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 on AudioStream.ContentType.
Speed
Playback-rate multiplier. Providers clamp to their supported range.
Stream
When true, request chunked audio. When false, the entire audio arrives as a single AudioChunk.
Raw
Byte passthrough escape hatch. The library overlays Model and Input; 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 until EOF; 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 from AudioFormat when 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 populate Words and Confidence.
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 TranscriptSegment
func (s *TranscriptStream) Err() error
func (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=true segments within a single utterance (Deepgram's is_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 alongside is_final. Voice-agent turn-taking should dispatch off SpeechFinal, not Final.

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