Last updated 2026-05-17
Package llmrouter — Audio
Every public symbol declared in
audio.go.
Two interfaces — Speaker
(text-to-speech) and
Transcriber
(speech-to-text) — plus the request, response, and stream types
they speak.
Import path:
import "github.com/elloloop/llmrouter"Speaker
The TTS contract. Implementations are concurrency-safe. Source: audio.go#L12.
type Speaker interface { Speak(ctx context.Context, req SpeechRequest) (*AudioStream, error)} Speak issues a synthesis request and returns an
*AudioStream that the
caller must drain (or cancel via ctx). A non-nil error
is returned only for synchronous failures; mid-stream errors
surface from AudioStream.Err() after the chunk channel
closes.
Transcriber
The STT contract. Implementations are concurrency-safe. Source: audio.go#L18.
type Transcriber interface { Transcribe(ctx context.Context, req TranscribeRequest) (*TranscriptStream, error)}SpeechRequest
The TTS request shape. Source: audio.go#L23.
type SpeechRequest struct { Model string // e.g. "tts-1", "tts-1-hd", "eleven_turbo_v2_5" Input string // text to synthesise; required Voice string // "alloy", "echo", or a vendor voice id Format string // "mp3" (default) | "opus" | "aac" | "flac" | "wav" | "pcm" | "ulaw" Speed *float64 // 0.25-4.0; nil for default Stream bool // request chunked audio Raw json.RawMessage // optional byte passthrough; Model + Input overlaid}Model- Provider-specific TTS model id.
Input- Text to synthesise. Required; the provider returns an error on empty.
Voice- Voice identifier. Format is provider-specific (canonical names for OpenAI, UUIDs for Cartesia/ElevenLabs).
Format-
Library-canonical audio format. Empty defaults to
mp3. Providers map this to their native enum and surface the result onAudioStream.ContentType. Speed- Playback-rate multiplier. Providers clamp to their supported range.
Stream-
When
true, request chunked audio. Whenfalse, the entire audio arrives as a singleAudioChunk. Raw-
Byte passthrough escape hatch. The library overlays
ModelandInput; everything else (voice settings, language, stability, style) survives unchanged.
AudioStream
The TTS stream handle. Single-consumer: only one goroutine should
read Chunks(). Source:
audio.go#L53.
type AudioStream struct { ContentType string // "audio/mpeg", "audio/opus", "audio/wav", "audio/pcm", ... // unexported: chunks chan, cancel func, errMu chan, err error}ContentType- The MIME type of the audio. Populated by the producer before any chunks are sent — safe to read after the first receive.
AudioStream.Chunks
func (s *AudioStream) Chunks() <-chan AudioChunk
Returns the receive-only chunk channel. Buffer capacity 16;
closes exactly once after the producer calls
Finish. Source:
audio.go#L75.
AudioStream.Err
func (s *AudioStream) Err() error
Blocks until the producer finishes; returns the terminal error
(nil on success). Safe to call multiple times.
Source:
audio.go#L79.
AudioStream.Cancel
func (s *AudioStream) Cancel()
Idempotent. Cancels the derived context the producer is using and
causes the producer to finish with
context.Canceled. Source:
audio.go#L85.
AudioChunk
type AudioChunk struct { Data []byte // decoded audio bytes for this frame Raw []byte // original wire bytes (often == Data)}
Source:
audio.go#L65.
For most providers Data == Raw; the distinction
matters when the provider wraps audio in a JSON envelope
(ElevenLabs base64-encoded chunks, Cartesia SSE), in which case
Raw is the envelope and Data is the
decoded audio.
NewAudioStream
Provider-facing constructor. Source: audio.go#L96.
func NewAudioStream(parent context.Context) ( *AudioStream, context.Context, AudioProducerHooks,)
Mirrors NewStream for chat. Returns the stream, a
derived context the producer should respect, and the
AudioProducerHooks
callbacks the producer must use. If the producer needs to set
ContentType, it must do so on the returned
*AudioStream before calling
Send.
AudioProducerHooks
type AudioProducerHooks struct { Send func(AudioChunk) bool // returns false if the consumer is gone Finish func(error) // must be called exactly once} Send returns false when the derived
context is cancelled — the producer should treat this as a signal
to clean up and call Finish. Finish
closes the chunk channel and unblocks any caller of
Err().
TranscribeRequest
The STT request shape. Source: audio.go#L127.
type TranscribeRequest struct { Model string // "whisper-1", "nova-3", "scribe_v1" Audio io.Reader // binary audio source; required AudioFormat string // "audio/mpeg", "audio/wav", ... Filename string // metadata for multipart-upload providers Language string // ISO-639-1 hint Prompt string // context hint (Whisper, ElevenLabs) ResponseFormat string // "json" | "text" | "srt" | "vtt" | "verbose_json" Temperature *float64 // sampling temperature (Whisper) Stream bool // request live transcription Raw json.RawMessage // byte passthrough; Model is overlaid}Audio-
Any
io.Reader. The library reads untilEOF; for retries, the caller must rewind. AudioFormat- Source MIME type. Some providers sniff (Whisper); others require an explicit format (Deepgram).
Filename-
Multipart-upload metadata. Defaults to
"audio"+ an extension derived fromAudioFormatwhen empty. Language- ISO-639-1 hint; empty means auto-detect.
Prompt- Optional priming context. Whisper uses this as a glossary of proper nouns and domain terms.
ResponseFormat-
Provider response format. The library always emits
TranscriptSegments regardless; richer modes populateWordsandConfidence. Temperature- Sampling temperature; Whisper-specific.
Stream- Live transcription. Providers that don't support streaming send a single final segment regardless.
Raw-
Byte passthrough for provider-specific fields
(Deepgram
diarize,smart_format,redact, etc.).
TranscriptStream
The STT stream handle. Single-consumer. Source: audio.go#L171.
type TranscriptStream struct { // unexported: segments chan, cancel func, errMu chan, err error}
func (s *TranscriptStream) Segments() <-chan TranscriptSegmentfunc (s *TranscriptStream) Err() errorfunc (s *TranscriptStream) Cancel()
Same lifecycle as AudioStream: Segments()
is the receive-only channel; Err() blocks until the
producer finishes; Cancel() is idempotent.
TranscriptSegment
type TranscriptSegment struct { Type string // upstream event name; "" for transcript-content Text string // transcribed text Final bool // true on the terminal segment for an utterance SpeechFinal bool // true when the provider detected end-of-speech Start, End time.Duration // timestamps relative to audio start Words []TranscriptWord Confidence float32 // [0, 1]; zero when not provided Raw json.RawMessage // original wire JSON for this segment}Source: audio.go#L191.
Type(v0.6+)-
Discriminates transcript-content segments
(
Type == "", the common case) from non-transcript stream events. Streaming providers populate this verbatim from the upstream event name — e.g. Deepgram emits"Results","SpeechStarted","UtteranceEnd","Metadata". Consumers should switch on this to drive barge-in, turn-taking, and similar behaviours. Text- The transcribed text. Empty for non-transcript event types.
Final-
True on the terminal segment for the current transcript window.
Streaming providers may emit several
Final=truesegments within a single utterance (Deepgram'sis_final). SpeechFinal(v0.6+)-
Distinct from
Final: indicates the provider has detected end-of-speech for the current utterance. Deepgram surfaces this as a separate bool alongsideis_final. Voice-agent turn-taking should dispatch offSpeechFinal, notFinal.
Streaming providers emit interim segments
(Final=false) followed by one or more
Final=true segments. Non-streaming providers emit
exactly one Final=true segment with
Type == "".
TranscriptWord
type TranscriptWord struct { Word string Start, End time.Duration Confidence float32}
Source:
audio.go#L206.
Populated by providers that return per-word timing — Whisper
verbose_json, Deepgram, ElevenLabs Scribe. Empty
otherwise.
NewTranscriptStream
func NewTranscriptStream(parent context.Context) ( *TranscriptStream, context.Context, TranscriptProducerHooks,)
Provider-facing constructor. Same shape as
NewAudioStream.
Source:
audio.go#L230.
TranscriptProducerHooks
type TranscriptProducerHooks struct { Send func(TranscriptSegment) bool Finish func(error)}
Source:
audio.go#L255.
Same contract as AudioProducerHooks: Send
returns false on cancellation, Finish
must be called exactly once.
See also
- Audio concept — narrative overview of TTS + STT.
- Stream API — same patterns for chat.
- OpenAI provider, ElevenLabs, Cartesia, Deepgram.