package provider import ( "context" "errors" "fmt" "sort" "strings" ) // DefaultName ist der Provider, den die Kette nutzt, wenn kein --provider // gepinnt ist. Task 02: osbuiltin; Task 03 ersetzt den Default durch die // Fallback-Kette (ElevenLabs → OpenAI → Edge → osbuiltin). const DefaultName = "osbuiltin" // ErrNoByteOutput meldet, dass die Plattform keine über Synthesize // beziehbaren Audio-Bytes liefern kann — konkret Linux mit speech-dispatcher // (spd-say) ohne espeak: spd-say kann Audio nur selbst über den Daemon // abspielen, nicht in einen Prozess schreiben. Aufrufer sollen auf // DirectSpeaker ausweichen (siehe dort). var ErrNoByteOutput = errors.New( "diese Plattform kann keine Audio-Bytes liefern (Linux ohne espeak; spd-say spielt nur direkt)") // Provider ist der Vertrag aller TTS-Backends von talk. // // Synthesize liefert dekodierbare Audio-Bytes; das Format ist von der // Implementierung dokumentiert (osbuiltin: RIFF/WAVE mit PCM — von allen // Sinks der Aufgabe abspielbar; Cloud-Provider ab Task 03: mp3). type Provider interface { // Name liefert den eindeutigen Providernamen (CLI --provider, Config). Name() string // Available meldet, ob der Provider auf diesem System überhaupt nutzbar // ist (z. B. Binary oder Key vorhanden). Muss billig und ohne Neben- // wirkungen sein; false heißt: Kette soll übersprungen werden. Available(ctx context.Context) bool // Synthesize erzeugt Audio-Bytes für text in der Sprache lang. Synthesize(ctx context.Context, text, lang string) ([]byte, error) } // DirectSpeaker ist eine dokumentierte optionale Erweiterung über das // Provider-Interface hinaus (kein Bestandteil des Basisvertrags): Provider, // die Text ohne Umweg über Synthesize-Bytes selbst hörbar ausgeben können. // Notwendig auf Linux-Systemen, deren OS-TTS (speech-dispatcher) keine // Audio-Bytes an Prozesse liefern kann — dort spielt osbuiltin.SpeakDirect // über spd-say selbst ab (blockierend bis zum Ende der Ansage). // // Task 03+ muss diese Erweiterung beachten: In der Kette ist SpeakDirect // die letzte Stufe, wenn kein Provider Bytes liefern kann. type DirectSpeaker interface { // SpeakDirect gibt text in der Sprache lang direkt über das OS-TTS aus // und blockiert bis zum Ende der Wiedergabe (oder bis ctx abgebrochen // wird). SpeakDirect(ctx context.Context, text, lang string) error } // registry bildet Providernamen auf Konstruktoren; Task 03 ergänzt hier // elevenlabs, openai und edge. var registry = map[string]func() Provider{ "osbuiltin": func() Provider { return OSBuiltin{} }, } // Names liefert die registrierten Providernamen (sortiert). func Names() []string { names := make([]string, 0, len(registry)) for name := range registry { names = append(names, name) } sort.Strings(names) return names } // New liefert den Provider mit dem angegebenen Namen; "" wählt den Default. // Unbekannte (oder in diesem Stand noch nicht implementierte) Namen sind // ein klarer Fehler. func New(name string) (Provider, error) { if name == "" { name = DefaultName } ctor, ok := registry[name] if !ok { return nil, fmt.Errorf("unbekannter oder noch nicht implementierter Provider %q (in diesem Stand verfügbar: %s)", name, strings.Join(Names(), ", ")) } return ctor(), nil }