refactor: switch the whole project to English
talk is going to be published. Code comments, CLI output, error messages,
daemon log lines, MCP descriptions and results, test names and assertions,
README, spec, skill, build scripts and flake are now English. Only the
spoken announcement texts stay bilingual: the wrap result and the test
sentence are German or English depending on the detected language.
Language fallback is now configurable: `lang = "en" | "de"` (env
TALK_LANG, default en) is used when detection cannot tell (numbers, single
words, ties) and for talk's own announcements. langdetect.Detect takes the
fallback as an argument instead of hard-coding German. `talk setup` asks for
it; `talk doctor` and `talk status` show it.
Vocabulary pinned across packages so the process-boundary tests observe
stable strings: chain log lines ("chain: tier X skipped (not available)",
"falling back to the next tier", "spoken via X"), queue lines ("discarded:
quiet hours ... active", "replaced by toast", "max_age"), CLI notes
("speaking directly", "daemon started at", "no daemon", "nothing to cancel"),
status block with aligned English labels, MCP messages, origin guard 403.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
4d30bd95ad
commit
85fa64cfe6
95 files changed
+4707
-4854
No files matched your search
+22
-24
@@ -8,23 +8,21 @@ import (
|
||||
mpg "github.com/hajimehoshi/go-mp3"
|
||||
)
|
||||
|
||||
// decodeMP3 dekodiert MP3-Daten — das Lieferformat der Cloud-Provider ab
|
||||
// Task 03 (ElevenLabs, OpenAI TTS, Edge-TTS) — in rohe PCM-Samples für
|
||||
// oto.
|
||||
// decodeMP3 decodes MP3 data, the delivery format of the cloud providers
|
||||
// (ElevenLabs, OpenAI TTS, Edge TTS), into raw PCM samples for oto.
|
||||
//
|
||||
// Entscheidung mp3-Dekodierung (Task 03): eingebauter, reiner Go-Dekoder
|
||||
// github.com/hajimehoshi/go-mp3 v0.3.4 statt WAV/PCM-Anforderung bei den
|
||||
// APIs. Begründung: (1) Alle drei Cloud-Provider liefern mp3 als ihr
|
||||
// natives Format; nur OpenAI kennt response_format "wav"/"pcm", ElevenLabs
|
||||
// und Edge-TTS aber nicht — eine einheitliche mp3-Pipeline hält die Kette
|
||||
// symmetrisch. (2) Der Dekoder ist nachweislich frei von CGO (kein
|
||||
// import "C" im Modul), damit bleibt die Cross-Compile-Verpflichtung
|
||||
// (CGO_ENABLED=0 für alle 6 Targets) unangetastet — verifiziert per
|
||||
// GOOS/GOARCH-Matrix-Build, siehe Task-03-Report.
|
||||
// Decision: a built-in pure Go decoder (github.com/hajimehoshi/go-mp3
|
||||
// v0.3.4) instead of requesting WAV/PCM from the APIs. Reasons: (1) all
|
||||
// three cloud providers deliver mp3 as their native format; only OpenAI
|
||||
// knows response_format "wav"/"pcm", ElevenLabs and Edge TTS do not, so a
|
||||
// single mp3 pipeline keeps the chain symmetric. (2) The decoder is free of
|
||||
// CGO (no import "C" in the module), which keeps the cross-compile
|
||||
// commitment (CGO_ENABLED=0 for all 6 targets) intact, verified by a
|
||||
// GOOS/GOARCH matrix build.
|
||||
//
|
||||
// Der Dekoder liefert immer 16-Bit-PCM little-endian mit 2 Kanälen — auch
|
||||
// für Mono-Quellen (etwa Edge-TTS: 24 kHz mono) wird auf Stereo
|
||||
// gespiegelt; die Sample-Rate stammt aus dem ersten MP3-Frame.
|
||||
// The decoder always yields 16-bit little-endian PCM with 2 channels; mono
|
||||
// sources (such as Edge TTS: 24 kHz mono) are mirrored to stereo. The
|
||||
// sample rate comes from the first MP3 frame.
|
||||
func decodeMP3(data []byte) (pcmFormat, []byte, error) {
|
||||
dec, err := mpg.NewDecoder(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
@@ -32,21 +30,21 @@ func decodeMP3(data []byte) (pcmFormat, []byte, error) {
|
||||
}
|
||||
pcm, err := io.ReadAll(dec)
|
||||
if err != nil {
|
||||
return pcmFormat{}, nil, fmt.Errorf("mp3: dekodieren: %w", err)
|
||||
return pcmFormat{}, nil, fmt.Errorf("mp3: decoding: %w", err)
|
||||
}
|
||||
if len(pcm) == 0 {
|
||||
return pcmFormat{}, nil, fmt.Errorf("mp3: Dekodierung lieferte keine Samples")
|
||||
return pcmFormat{}, nil, fmt.Errorf("mp3: decoding produced no samples")
|
||||
}
|
||||
return pcmFormat{
|
||||
SampleRate: dec.SampleRate(),
|
||||
Channels: 2, // go-mp3 dekodiert grundsätzlich 2 Kanäle (s. doc)
|
||||
Channels: 2, // go-mp3 always decodes to 2 channels (see its docs)
|
||||
BitsPerCoded: 16,
|
||||
}, pcm, nil
|
||||
}
|
||||
|
||||
// looksLikeMP3 meldet, ob data nach MP3 aussieht: ID3v2-Tag (führt mit
|
||||
// "ID3") oder ein roher Frame-Sync (erstes Byte 0xFF, oberste 3 Bits des
|
||||
// zweiten Bytes gesetzt).
|
||||
// looksLikeMP3 reports whether data looks like MP3: an ID3v2 tag (leading
|
||||
// "ID3") or a raw frame sync (first byte 0xFF, top 3 bits of the second
|
||||
// byte set).
|
||||
func looksLikeMP3(data []byte) bool {
|
||||
if len(data) >= 3 && string(data[0:3]) == "ID3" {
|
||||
return true
|
||||
@@ -54,9 +52,9 @@ func looksLikeMP3(data []byte) bool {
|
||||
return len(data) >= 2 && data[0] == 0xFF && data[1]&0xE0 == 0xE0
|
||||
}
|
||||
|
||||
// decodeAudio ist die Format-Weiche der Sinks: RIFF/WAVE (PCM) nach
|
||||
// decodeWAV, MP3 (ID3-Tag oder Frame-Sync) nach decodeMP3. Alles andere
|
||||
// fällt durch zu decodeWAV, das den präzisen „kein RIFF"-Fehler liefert.
|
||||
// decodeAudio is the format switch of the sinks: RIFF/WAVE (PCM) goes to
|
||||
// decodeWAV, MP3 (ID3 tag or frame sync) to decodeMP3. Everything else
|
||||
// falls through to decodeWAV, which yields the precise "no RIFF" error.
|
||||
func decodeAudio(data []byte) (pcmFormat, []byte, error) {
|
||||
if len(data) >= 4 && string(data[0:4]) == "RIFF" {
|
||||
return decodeWAV(data)
|
||||
|
||||
Reference in new issue
Block a user