Files
talk/internal/audio/audio.go
T
m3ta-chiron ae9f3b680e fix(audio): NullSink-Verwurf-Zeile im CLI-Pfad sichtbar machen
NewSink bekommt einen optionalen Log-Writer (die CLI übergibt stderr);
zuvor verwarf der null-Sink bei erfolgreicher Synthese stumm — die
sichtbare Zeile kam nur aus dem ErrNoByteOutput-Zweig. Sink-Interface
unverändert; Aufrufstellen ab Task 03 müssen den Writer übergeben.
TestSayArgumentMitNullSink beweist die Zeile jetzt extern auf dem
CLI-Pfad, TestNewSink deckt das Factory-Wiring ab; Live-Beweis mit
espeak-ng dokumentiert ("audio: N Bytes verworfen (null sink)", Exit 0).

Dazu (Minor): "was" aus englishWords entfernt — widersprach dem
Mehrdeutigkeits-Kommentar und dem Report; neuer TestFall
"Was macht er?" → de.
2026-10-18 15:46:20 +02:00

58 lines
1.7 KiB
Go

package audio
import (
"context"
"fmt"
"io"
)
// Sink-Namen (Teil des öffentlichen Vertrags; Spec: audio.sink = "oto"|"null").
const (
NameOto = "oto"
NameNull = "null"
)
// Sink gibt Audio-Bytes wieder. Die Bytes stammen aus Provider.Synthesize;
// oto versteht RIFF/WAVE (PCM) — das Lieferformat von osbuiltin.
type Sink interface {
// Play spielt die Daten blockierend ab (bis zum Ende oder bis ctx
// abgebrochen wird).
Play(ctx context.Context, data []byte) error
// Name liefert den Sink-Namen (audio.sink-Konfiguration).
Name() string
}
// NewSink liefert den Sink zum konfigurierten Namen ("oto" oder "null").
// log ist ein optionaler Log-Output — die CLI übergibt ihr stderr, über den
// der null-Sink seine Verwurf-Zeile schreibt (nil = still verwerfen); der
// oto-Sink braucht kein Log. Unbekannte Namen sind ein klarer Fehler.
func NewSink(name string, log io.Writer) (Sink, error) {
switch name {
case NameOto:
return OtoSink{}, nil
case NameNull:
return NullSink{W: log}, nil
default:
return nil, fmt.Errorf("unbekannter audio sink %q (erlaubt: %q, %q)", name, NameOto, NameNull)
}
}
// NullSink verwirft jede Ansage ohne Ton — für CI, Server und Tests. Wurde
// ein W gesetzt (NewSink reicht dafür den Log-Output der CLI durch), loggt
// Play eine Zeile darüber, damit „läuft ohne Ton durch" sichtbar bleibt.
type NullSink struct {
W io.Writer // optionaler Log-Output; nil = still verwerfen
}
// Name implementiert Sink.
func (NullSink) Name() string { return NameNull }
// Play implementiert Sink: verwirft die Daten.
func (n NullSink) Play(ctx context.Context, data []byte) error {
if n.W != nil {
fmt.Fprintf(n.W, "audio: %d Bytes verworfen (null sink)\n", len(data))
}
return nil
}