Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion .github/workflows/gigaam.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ on:

env:
FORCE_COLOR: 1
# Repository secrets are unavailable to fork pull requests.
RUN_HF_TOKEN_TESTS: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}

jobs:
test:
Expand Down Expand Up @@ -72,8 +74,13 @@ jobs:
- name: Run batching tests
run: |
pytest -v tests/test_batching.py --tb=short

- name: Run VAD utility tests
run: |
pytest -v tests/test_vad_utils.py --tb=short

- name: Run longform tests
if: env.RUN_HF_TOKEN_TESTS == 'true'
env:
HF_TOKEN: ${{ secrets.HF_TOKEN }}
run: |
Expand All @@ -85,9 +92,19 @@ jobs:

- name: Run timestamps tests
run: |
pytest -v tests/test_timestamps.py --tb=short
pytest -v tests/test_timestamps.py -k "not transcribe_longform" --tb=short

- name: Run longform timestamps tests
if: env.RUN_HF_TOKEN_TESTS == 'true'
env:
HF_TOKEN: ${{ secrets.HF_TOKEN }}
run: |
pytest -v tests/test_timestamps.py -k "transcribe_longform" --tb=short

- name: Run training tests
if: env.RUN_HF_TOKEN_TESTS == 'true'
env:
HF_TOKEN: ${{ secrets.HF_TOKEN }}
run: |
pytest -v tests/test_training.py --tb=short

Expand Down
18 changes: 16 additions & 2 deletions gigaam/vad_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,19 @@ def get_pipeline(
return _PIPELINE.to(device)


def _apply_pipeline(pipeline: Pipeline, audio_file):
"""
Applies PyAnnote pipeline to a preloaded waveform dict.

GigaAM already decodes the audio for ASR chunk slicing. Reusing the same
waveform for VAD avoids depending on pyannote/torchcodec path decoding and
keeps VAD boundaries aligned with the samples later sliced for ASR.
"""
if hasattr(pipeline, "prepare_one") and hasattr(pipeline, "apply"):
return pipeline.apply(pipeline.prepare_one(audio_file, preload=False))
return pipeline(audio_file)


def segment_audio_file(
wav_file: str,
sr: int,
Expand All @@ -92,9 +105,10 @@ def segment_audio_file(
The segmentation is performed using a PyAnnote voice activity detection pipeline.
"""

audio = load_audio(wav_file)
audio = load_audio(wav_file, sample_rate=sr)
pipeline = get_pipeline(device)
sad_segments = cast(Annotation, pipeline(wav_file))
sad_input = {"waveform": audio.unsqueeze(0), "sample_rate": sr}
sad_segments = cast(Annotation, _apply_pipeline(pipeline, sad_input))

segments: List[torch.Tensor] = []
curr_duration = 0.0
Expand Down
62 changes: 62 additions & 0 deletions tests/test_vad_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import torch


class _FakeSegment:
def __init__(self, start, end):
self.start = start
self.end = end


class _FakeTimeline:
def support(self):
return [_FakeSegment(1.0, 3.0), _FakeSegment(4.0, 6.0)]


class _FakeSadSegments:
def get_timeline(self):
return _FakeTimeline()


class _FakePyannote4Pipeline:
def __init__(self):
self.used_apply = False

def prepare_one(self, audio_file, preload=False):
assert set(audio_file) == {"waveform", "sample_rate"}
assert audio_file["waveform"].shape == (1, 80)
assert audio_file["sample_rate"] == 10
assert preload is False
return audio_file

def apply(self, prepared):
assert prepared["sample_rate"] == 10
self.used_apply = True
return _FakeSadSegments()

def __call__(self, audio_file):
raise AssertionError("segment_audio_file should use preloaded waveform input")


def test_segmentation_uses_preloaded_waveform_for_pyannote_pipeline(monkeypatch):
"""VAD should use the same decoded waveform later sliced for ASR chunks."""
from gigaam import vad_utils

pipeline = _FakePyannote4Pipeline()
monkeypatch.setattr(vad_utils, "get_pipeline", lambda device: pipeline)
monkeypatch.setattr(
vad_utils,
"load_audio",
lambda wav_file, sample_rate: torch.arange(80, dtype=torch.float32),
)

segments, boundaries = vad_utils.segment_audio_file(
"fake.wav",
sr=10,
max_duration=10.0,
min_duration=8.0,
strict_limit_duration=30.0,
)

assert pipeline.used_apply
assert boundaries == [(1.0, 6.0)]
assert torch.equal(segments[0], torch.arange(10, 60, dtype=torch.float32))
Loading