diff options
| author | Paul Buetow <paul@buetow.org> | 2026-04-09 14:39:40 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-04-09 14:39:40 +0300 |
| commit | 4b4b3665230faafbd994f09894cb07c8f9d4b359 (patch) | |
| tree | 4d2737404feb4494cf9ae66ae7539e43a2240f12 /internal/audio | |
| parent | 2e72f367b17c27c93672ec62c9806affbe3c4a55 (diff) | |
Release v0.28.1: convert Gemini TTS mono audio to stereov0.28.1
Duplicate each 16-bit PCM sample into both L/R channels so all
audio output uses both speakers, and retroactively converted the
88 existing card MP3s to stereo via ffmpeg.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal/audio')
| -rw-r--r-- | internal/audio/gemini_provider.go | 21 | ||||
| -rw-r--r-- | internal/audio/gemini_provider_test.go | 3 |
2 files changed, 22 insertions, 2 deletions
diff --git a/internal/audio/gemini_provider.go b/internal/audio/gemini_provider.go index 74478bd..3b2872a 100644 --- a/internal/audio/gemini_provider.go +++ b/internal/audio/gemini_provider.go @@ -20,7 +20,8 @@ import ( const ( defaultGeminiTTSModel = "gemini-2.5-flash-preview-tts" geminiTTSLanguageCode = "bg" - geminiTTSChannels = 1 + // Gemini returns mono PCM; we upsample to stereo so both channels carry audio. + geminiTTSChannels = 2 geminiTTSSampleRate = 24000 geminiTTSBitsPerSample = 16 ) @@ -230,7 +231,25 @@ func ensureOutputDirectory(outputFile string) error { return nil } +// monoToStereo duplicates each 16-bit mono sample into both left and right +// channels, producing interleaved stereo PCM. The output is twice as large. +func monoToStereo(mono []byte) []byte { + bytesPerSample := geminiTTSBitsPerSample / 8 + stereo := make([]byte, len(mono)*2) + for i := 0; i+bytesPerSample <= len(mono); i += bytesPerSample { + sample := mono[i : i+bytesPerSample] + // Write sample for left channel, then right channel. + dst := i * 2 + copy(stereo[dst:], sample) + copy(stereo[dst+bytesPerSample:], sample) + } + return stereo +} + func encodePCMAsWAV(pcmData []byte) ([]byte, error) { + // Gemini returns mono audio; convert to stereo so both speakers carry audio. + pcmData = monoToStereo(pcmData) + var buffer bytes.Buffer if _, err := buffer.WriteString("RIFF"); err != nil { diff --git a/internal/audio/gemini_provider_test.go b/internal/audio/gemini_provider_test.go index 64b43b1..21b8a37 100644 --- a/internal/audio/gemini_provider_test.go +++ b/internal/audio/gemini_provider_test.go @@ -212,7 +212,8 @@ func TestWriteGeminiAudioFileWritesWAV(t *testing.T) { t.Fatalf("output file does not look like WAV data: %q", fileData[:4]) } - if got, want := len(fileData), 44+len(pcmData); got != want { + // Mono PCM is duplicated to stereo (both channels), so the data section doubles. + if got, want := len(fileData), 44+len(pcmData)*2; got != want { t.Fatalf("len(output) = %d, want %d", got, want) } } |
