Skip to content

Load the diarization waveform with soundfile instead of torchaudio#326

Open
happyarts wants to merge 1 commit into
kaixxx:mainfrom
happyarts:fix/pyannote-soundfile-load
Open

Load the diarization waveform with soundfile instead of torchaudio#326
happyarts wants to merge 1 commit into
kaixxx:mainfrom
happyarts:fix/pyannote-soundfile-load

Conversation

@happyarts

@happyarts happyarts commented Jul 22, 2026

Copy link
Copy Markdown

What

The pyannote worker loads the audio for diarization with torchaudio.load and passes it to the pipeline as an in-memory waveform dict. This PR replaces that one call with soundfile — the file at this point is always noScribe's own converted 16 kHz mono PCM WAV, so no decoding backend is needed at all.

Since the worker now imports soundfile directly, it is added to the two Windows requirements files; Linux and macOS already list it explicitly.

Why

  • torchaudio.load is deprecated: starting with torchaudio 2.9 it delegates to torchcodec, and torchcodec is hard-paired to both the installed torch version and the system FFmpeg major (e.g. torchcodec 0.7 supports FFmpeg 4–7 only, so it fails to load against current Homebrew FFmpeg 8 and prints a scary-looking warning at startup).
  • After this change, noScribe's own code no longer uses torchaudio anywhere — the torch/torchaudio/torchcodec version pairings become pyannote's internal concern only, which makes future dependency upgrades much less entangled.

Scope note

The torchaudio/torchcodec requirement pins are deliberately left untouched here: pyannote.audio declares both as hard dependencies, so they stay installed either way. This PR only removes noScribe's direct use, which is what makes a later pin bump (or dropping the explicit pins) a self-contained follow-up.

Proof

  • New test tests/test_pyannote_waveform_load.py: the soundfile-loaded tensor is bit-for-bit identical (torch.equal, not just allclose) to what torchaudio.load returns for the WAV format noScribe produces.
  • Verified with a full diarization run on real audio: identical segments and speakers, same speed. The loader is zero-copy for mono (no extra waveform-sized allocation in the worker).

No behavior change intended; the waveform reaching pyannote is byte-identical to before.

🤖 Generated with Claude Code

@happyarts
happyarts force-pushed the fix/pyannote-soundfile-load branch from 6f17d03 to 24d06f1 Compare July 22, 2026 11:05
happyarts pushed a commit to happyarts/noScribe that referenced this pull request Jul 22, 2026
/simplify on PR kaixxx#326 found that data.T.copy() doubled the worker's peak
memory (~2x the waveform, ~460 MB extra on 2 h of audio): for mono the
(frames, 1) transpose is already contiguous, so .contiguous() is a
zero-copy no-op and only copies in the hypothetical multichannel case.
Also simplifies the test fixture and marks the torchaudio bit-identity
test as a migration-time proof.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@happyarts
happyarts force-pushed the fix/pyannote-soundfile-load branch 3 times, most recently from 23a15b5 to 8a11850 Compare July 22, 2026 11:19
happyarts pushed a commit to happyarts/noScribe that referenced this pull request Jul 22, 2026
/code-review on PR kaixxx#326: the diarization worker now imports soundfile
directly, but only the Linux/macOS requirements listed it explicitly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The pyannote worker only ever loads noScribe's own converted audio
(16 kHz mono pcm_s16le WAV) and already passes it to the pipeline as an
in-memory waveform dict. Reading that file needs no decoding backend, so
plain soundfile replaces torchaudio.load here.

Why: torchaudio.load is deprecated in favour of torchcodec in the
upcoming torchaudio releases, and torchcodec is hard-paired to both the
torch version and the installed FFmpeg major. Dropping the call removes
the last direct torchaudio use from noScribe's own code, so those
version pairings become pyannote's internal concern only.

soundfile is added to the two Windows requirements files -- Linux and
macOS already list it explicitly.

The new test proves the swap is a drop-in: the soundfile-loaded tensor
is bit-for-bit identical to what torchaudio.load returned, with no
extra waveform-sized copy (zero-copy for mono). Verified with a full
diarization run (242 segments, 4 speakers) on real audio.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@happyarts
happyarts force-pushed the fix/pyannote-soundfile-load branch from 8a11850 to 36833e3 Compare July 22, 2026 11:28
happyarts pushed a commit to happyarts/noScribe that referenced this pull request Jul 22, 2026
/code-review findings 2+3 on PR kaixxx#326: a non-converted input now fails
with a message pointing at the conversion contract (audio/convert.py)
instead of a bare LibsndfileError, and the loader test skips cleanly on
a Python built without tkinter instead of erroring at collection.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@happyarts

Copy link
Copy Markdown
Author

Kurzer Kontext zu diesem PR: torchaudio.load ist ab torchaudio 2.9 deprecated (wandert zu torchcodec). Der Diarisierungs-Worker lädt hier nur noScribes eigene konvertierte WAV (16 kHz mono PCM16), dafür reicht soundfile — das ist bereits eine Abhängigkeit unter Linux/macOS und wurde jetzt auch in die Windows-Requirements aufgenommen.

Der neue Loader ist bit-identisch zu torchaudio.load (per torch.equal getestet, Test liegt bei) und zero-copy für Mono. Läuft unverändert auf dem aktuellen wie auf neueren torch-Ständen und ist unabhängig von #327 — beliebige Merge-Reihenfolge.

@kaixxx

kaixxx commented Jul 22, 2026

Copy link
Copy Markdown
Owner

@happyarts Thank you, that looks interesting. However, I will be away on holidays for a few weeks. I will take a look after my return by the end of August, ok?
Best
Kai

@happyarts

Copy link
Copy Markdown
Author

Klar, selbstverständlich. Genieße deinen Urlaub. Ich denke, da kommen noch ein paar Sachen von mir. Der große Brocken, die Integration von Voxtral kommt erst noch - den Teste ich noch ausführlich. Das wird spannend, weil auf Mac 10x so schnell und bessere Qualität als Whisper.

@kaixxx

kaixxx commented Jul 22, 2026

Copy link
Copy Markdown
Owner

Der große Brocken, die Integration von Voxtral kommt erst noch

Nun, da bin ich gespannt. Allerdings wäre es gut, vor solchen großen Änderungen die Architektur gemeinsam zu besprechen. Die eigentliche Transkription ist ja jetzt schon im "transcription_worker" gekapselt. Ich könnte mir vorstellen, hier einen alternativen voxtral worker zu erstellen, mit identischer Kommunikationsschnittstelle. So ließe sich das Transkriptionsbackend leicht wechseln.
Ungünstig fände ich jedoch, wenn die Logik der Kombination von den PyAnnote-Segmenten und den transkribierten Textsegmenten im Hautprogramm stark angepasst werden müsste. Darin stecken die eigentliche Intelligenz von noScribe (wenn es die gibt) und viele Stunden Test und Tweaks, etwa um die Erkennung von Pausen und überlappender Sprache zu optimieren.

@happyarts

Copy link
Copy Markdown
Author

Hi Kai, selbstverständlich, ein neues Modul sollte in die Architektur passen. Die Voxtral-Geschichte war eigentlich eine schnelle Idee, ist dann aber doch größer und komplexer geworden als gedacht. Ich habe ein paar handoptimierte Modellgewichts-Varianten gebaut und auf HF hochgeladen (alle getestet), die Erkennung von Wortwiederholungen und das saubere Lösen derselben ist echt tricky, ich (genauer: die KI für mich) habe einen Bug im torchaudio gefunden und ich tune das Modul gerade noch in einigen Kleinigkeiten.

Damit das sauber ist halte ich den Voxtral-Teil ja zurück, bis die kleinen PRs durch sind. Du kannst schon schauen: https://github.com/happyarts/noScribe/tree/feature/voxtral-engine und gern Wünsche / Vorschläge / Ideen äußern.

Zur Beruhigung vorab: Die Integration folgt exakt dem Muster, das du beschreibst. Es gibt einen eigenen voxtral_mp_worker als Subprozess, der das Message-Protokoll des whisper_mp_worker 1:1 spiegelt (log/progress/segment/result) — im Hauptprogramm wählt die Engine-Auswahl nur den Entrypoint, beide laufen durch denselben on_segment-Pfad. Die Kombinationslogik mit den PyAnnote-Segmenten (Pausen, überlappende Sprache etc.) bleibt unangetastet; die Engine liefert lediglich Segmente mit Wort-Timestamps in demselben Format.

Markus

happyarts pushed a commit to happyarts/noScribe that referenced this pull request Jul 22, 2026
/simplify on PR kaixxx#326 found that data.T.copy() doubled the worker's peak
memory (~2x the waveform, ~460 MB extra on 2 h of audio): for mono the
(frames, 1) transpose is already contiguous, so .contiguous() is a
zero-copy no-op and only copies in the hypothetical multichannel case.
Also simplifies the test fixture and marks the torchaudio bit-identity
test as a migration-time proof.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
happyarts pushed a commit to happyarts/noScribe that referenced this pull request Jul 22, 2026
/code-review on PR kaixxx#326: the diarization worker now imports soundfile
directly, but only the Linux/macOS requirements listed it explicitly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
happyarts pushed a commit to happyarts/noScribe that referenced this pull request Jul 22, 2026
/code-review findings 2+3 on PR kaixxx#326: a non-converted input now fails
with a message pointing at the conversion contract (audio/convert.py)
instead of a bare LibsndfileError, and the loader test skips cleanly on
a Python built without tkinter instead of erroring at collection.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@happyarts
happyarts force-pushed the fix/pyannote-soundfile-load branch from 36833e3 to 72fc97f Compare July 22, 2026 23:56
happyarts added a commit to happyarts/noScribe that referenced this pull request Jul 22, 2026
/simplify on PR kaixxx#326 found that data.T.copy() doubled the worker's peak
memory (~2x the waveform, ~460 MB extra on 2 h of audio): for mono the
(frames, 1) transpose is already contiguous, so .contiguous() is a
zero-copy no-op and only copies in the hypothetical multichannel case.
Also simplifies the test fixture and marks the torchaudio bit-identity
test as a migration-time proof.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
happyarts added a commit to happyarts/noScribe that referenced this pull request Jul 22, 2026
/code-review on PR kaixxx#326: the diarization worker now imports soundfile
directly, but only the Linux/macOS requirements listed it explicitly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
happyarts added a commit to happyarts/noScribe that referenced this pull request Jul 22, 2026
/code-review findings 2+3 on PR kaixxx#326: a non-converted input now fails
with a message pointing at the conversion contract (audio/convert.py)
instead of a bare LibsndfileError, and the loader test skips cleanly on
a Python built without tkinter instead of erroring at collection.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kaixxx

kaixxx commented Jul 23, 2026

Copy link
Copy Markdown
Owner

Alles klar, das hört sich interessant an. Dann schauen wir mal.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants