Stop throwing away the audio Whisper has to read
The single encode at save time was %mp3(bitrate=16), and the comment said why: it matched what Liquidsoap pushes to Icecast. That was the wrong thing to match. Icecast is the LISTENING path and 16 kbps is a bandwidth budget for a live stream; this file is the ACCURACY path -- it is what Whisper transcribes, and CLAUDE.md is explicit that everything downstream is hostage to it. P25 has already been through a vocoder, so 16 kbps MP3 stacked a second lossy stage on the one copy that had to stay faithful. FLAC instead. Lossless, so the bytes Whisper receives are the bytes PulseAudio captured. ~1.3 MB/min against 120 KB/min, which keeps a 600 s call (the time cap) around 13 MB -- inside Whisper's 25 MB request cap and well inside upload_max_bytes. Icecast's own 16 kbps stream is untouched; nothing about live listening changes. Capture, buffering, silence detection and the byte-offset trim are all unchanged: they operate on raw PCM and never saw the encode. The sample rate stays pinned to pcm.SAMPLE_RATE so the encode remains a straight pass -- the trim arithmetic depends on that, and Whisper resamples to 16 kHz itself. encode_mp3 is now encode_recording, the upload sends audio/flac, and the test that pinned the old contract now pins losslessness instead, including an assertion that no bitrate constant comes back. This is the before/after boundary for STT quality. Last night's window is the 16 kbps baseline. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
fb13bb8ae3
commit
0c08275482
@@ -59,7 +59,7 @@ def encodes(monkeypatch):
|
||||
path.write_bytes(audio)
|
||||
return True
|
||||
|
||||
monkeypatch.setattr(recorder_mod, "encode_mp3", _encode)
|
||||
monkeypatch.setattr(recorder_mod, "encode_recording", _encode)
|
||||
return calls
|
||||
|
||||
|
||||
@@ -412,21 +412,31 @@ async def test_a_failed_encode_leaves_no_file_and_no_recording(recorder, monkeyp
|
||||
async def _fail(audio, path):
|
||||
return False
|
||||
|
||||
monkeypatch.setattr(recorder_mod, "encode_mp3", _fail)
|
||||
monkeypatch.setattr(recorder_mod, "encode_recording", _fail)
|
||||
ingest(recorder, T0, T0 + 5.0)
|
||||
await recorder.start_recording("call-1", start_epoch=T0 + 1.0)
|
||||
|
||||
assert await recorder.stop_recording(end_epoch=T0 + 3.0) is None
|
||||
assert list(recorder._recordings_dir.glob("*.mp3")) == []
|
||||
assert list(recorder._recordings_dir.glob("*.flac")) == []
|
||||
|
||||
|
||||
def test_encoder_command_contract_matches_what_c2_expects():
|
||||
"""
|
||||
/upload has always received mono MP3 at 22050 Hz / 16 kbps, and Whisper
|
||||
consumes it downstream. The single encode must not quietly change that.
|
||||
The saved file is what Whisper transcribes, so the encode must stay
|
||||
LOSSLESS and must not resample. It was 16 kbps MP3 — a bitrate copied from
|
||||
Icecast's live stream, i.e. the listening path's budget applied to the
|
||||
accuracy path — which put a second lossy stage on top of the P25 vocoder.
|
||||
|
||||
The sample rate must equal pcm.SAMPLE_RATE or the encode stops being a
|
||||
straight pass and the byte-offset trim arithmetic no longer lines up.
|
||||
"""
|
||||
assert recorder_mod.MP3_SAMPLE_RATE == str(pcm.SAMPLE_RATE) == "22050"
|
||||
assert recorder_mod.MP3_BITRATE == "16k"
|
||||
assert recorder_mod.AUDIO_SAMPLE_RATE == str(pcm.SAMPLE_RATE) == "22050"
|
||||
assert recorder_mod.AUDIO_FORMAT == "flac"
|
||||
assert recorder_mod.AUDIO_SUFFIX == ".flac"
|
||||
assert recorder_mod.AUDIO_MIME == "audio/flac"
|
||||
# No bitrate constant should exist: a bitrate on a lossless codec would mean
|
||||
# someone reintroduced lossy encoding.
|
||||
assert not hasattr(recorder_mod, "MP3_BITRATE")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -523,7 +533,7 @@ async def test_discard_drops_the_audio_without_writing_anything(recorder, encode
|
||||
|
||||
assert not recorder.is_recording
|
||||
assert encodes == []
|
||||
assert list(recorder._recordings_dir.glob("*.mp3")) == []
|
||||
assert list(recorder._recordings_dir.glob("*.flac")) == []
|
||||
# ...and the recorder is immediately reusable.
|
||||
assert await recorder.start_recording("call-next", start_epoch=T0 + 2.0) is True
|
||||
|
||||
|
||||
Reference in New Issue
Block a user