Documentation/Audio (TTS/STT)
v2.0.0-beta.1Snapshot bebdcd1Markdown ↗

Audio (TTS/STT)

Audio (TTS/STT)

The library provides comprehensive Text-to-Speech (TTS) and Speech-to-Text (STT) capabilities. These APIs are request-based and work best for files or bounded generation. For live speech-to-speech, streaming transcription, or continuous translation, use the OpenAI Realtime API or xAI Voice Agent API.

Text-to-Speech

Basic Usage

typescript
import { Connector, TextToSpeech, Vendor } from 'oneringai';

// Setup connector
Connector.create({
  name: 'openai',
  vendor: Vendor.OpenAI,
  auth: { type: 'api_key', apiKey: process.env.OPENAI_API_KEY! },
});

// Create TTS instance
const tts = TextToSpeech.create({
  connector: 'openai',
  model: 'tts-1-hd',      // High-quality model
  voice: 'nova',          // Female voice
});

// Synthesize to Buffer
const response = await tts.synthesize('Hello, world!');
console.log(response.audio);   // Buffer
console.log(response.format);  // 'mp3'

// Synthesize to file
await tts.toFile('Hello, world!', './output.mp3');

Voice Options

typescript
// Available voices
const voices = await tts.listVoices();
// [
//   { id: 'alloy', name: 'Alloy', gender: 'neutral', isDefault: true },
//   { id: 'echo', name: 'Echo', gender: 'male' },
//   { id: 'fable', name: 'Fable', gender: 'neutral', accent: 'british' },
//   { id: 'onyx', name: 'Onyx', gender: 'male' },
//   { id: 'nova', name: 'Nova', gender: 'female' },
//   { id: 'shimmer', name: 'Shimmer', gender: 'female' },
//   ...
// ]

// Synthesize with specific voice
const audio = await tts.synthesize('Hello', { voice: 'echo' });

Custom Voices (OpenAI)

Create OpenAI custom voices through the connector-first OpenAIVoices API. Prompt-designed voices work only with GPT-Live. Voices intended for TTS or Realtime must be created from an audio sample and an existing consent-recording ID. Custom voice access and the required api.voices.read/api.voices.write permissions are organization-gated.

typescript
import { createReadStream } from 'node:fs';
import { OpenAIVoices, TextToSpeech } from 'oneringai';

const voices = new OpenAIVoices('openai');
const customVoice = await voices.create({
  type: 'audio_sample',
  name: 'Brand narrator',
  audio_sample: createReadStream('./brand-narrator.wav'),
  consent: 'cons_1234abcd',
});

const branded = TextToSpeech.create({
  connector: 'openai',
  model: 'gpt-4o-mini-tts',
  voice: customVoice.id,
});

await branded.toFile('Spoken in your bespoke voice.', './brand.mp3');

// A stored audio-sample voice can also be selected per call.
await branded.synthesize('Different copy.', { voice: 'voice_5678efgh' });

How it's detected: any string starting with voice_ is forwarded as a custom-voice reference; everything else is treated as a built-in voice name (alloy, ash, ballad, coral, echo, fable, onyx, nova, sage, shimmer, verse, marin, cedar). tts.listVoices() returns only the built-ins. Keep returned custom-voice IDs in trusted application storage; do not place voice samples, consent recordings, or credentials in prompts.

For Google custom voices, use the connector-first GoogleVoices lifecycle API rather than tts.listVoices(). Google TTS accepts voice_…, voicekey_…, and resource-prefixed voices/… identifiers returned by that API.

Audio Formats

typescript
// Supported formats: mp3, opus, aac, flac, wav, pcm
const mp3 = await tts.synthesize('Hello', { format: 'mp3' });
const wav = await tts.synthesize('Hello', { format: 'wav' });
const flac = await tts.synthesize('Hello', { format: 'flac' });

Speed Control

typescript
// Speed range: 0.25 (slow) to 4.0 (fast)
const slow = await tts.synthesize('Speaking slowly', { speed: 0.5 });
const normal = await tts.synthesize('Normal speed', { speed: 1.0 });
const fast = await tts.synthesize('Speaking fast', { speed: 2.0 });

Instruction Steering (gpt-4o-mini-tts)

The gpt-4o-mini-tts model supports instruction steering for emotional control:

typescript
const tts = TextToSpeech.create({
  connector: 'openai',
  model: 'gpt-4o-mini-tts',
  voice: 'nova',
});

const audio = await tts.synthesize('I\'m so happy to see you!', {
  vendorOptions: {
    instructions: 'Speak with enthusiasm and joy, like greeting an old friend.',
  },
});

Model Introspection

typescript
// Get model information
const info = tts.getModelInfo();
console.log(info.capabilities.features.instructionSteering); // true for gpt-4o-mini-tts

// Check feature support
const canSteer = tts.supportsFeature('instructionSteering');
const canStream = tts.supportsFeature('streaming');

// Get supported formats
const formats = tts.getSupportedFormats();  // ['mp3', 'opus', 'aac', ...]

// List available models
const models = tts.listAvailableModels();

Streaming TTS

For real-time voice applications, TTS audio can be streamed as chunks arrive from the API instead of waiting for the entire response to buffer. This is useful for voice assistants and interactive agents.

typescript
import { TextToSpeech } from 'oneringai';

const tts = TextToSpeech.create({
  connector: 'openai',
  model: 'tts-1-hd',
  voice: 'nova',
});

// Check if provider supports streaming
console.log(tts.supportsStreaming('pcm'));  // true for OpenAI

// Stream PCM audio chunks
for await (const chunk of tts.synthesizeStream('Hello, world!', { format: 'pcm' })) {
  if (chunk.audio.length > 0) {
    // Process raw PCM data (24kHz, 16-bit signed LE, mono)
    playPCMChunk(chunk.audio);
  }
  if (chunk.isFinal) break;
}

// Non-streaming providers gracefully fall back to buffered synthesis
// (yields a single chunk with isFinal: true)

VoiceStream wraps an agent's text stream and interleaves audio events:

typescript
import { VoiceStream } from 'oneringai';

const voice = VoiceStream.create({
  ttsConnector: 'openai',
  ttsModel: 'tts-1-hd',
  voice: 'nova',
  format: 'mp3',       // MP3 recommended for broad compatibility
  streaming: false,     // Set true + format 'pcm' for low-latency streaming
});

// Wraps agent stream — text events pass through, audio events interleaved
for await (const event of voice.wrap(agent.stream('Tell me a story'))) {
  if (event.type === 'response.output_text.delta') {
    process.stdout.write(event.delta);
  } else if (event.type === 'response.audio_chunk.ready') {
    playAudio(event.audio_base64, event.format);
  }
}

Streaming notes:

  • MP3 format (default) is recommended — best compatibility and quality
  • PCM format enables streaming mode but browser playback is experimental
  • Streaming providers (OpenAI and xAI) yield chunks as they arrive from the API
  • Non-streaming providers fall back to buffered synthesis automatically
  • VoiceStream accumulates small API chunks into ~125ms buffers before emitting events

xAI TTS and WebSocket streaming

Use a Grok connector with the endpoint model id xai-tts. xAI accepts expressive tags such as [laugh] and [pause], custom voice ids, BCP-47 language codes, telephony codecs, and bidirectional WebSocket streaming. For buffered synthesis, vendorOptions.output_format.codec may override the normalized format; TTSResponse.format follows xAI's response Content-Type or JSON content_type, so returned WAV/PCM/telephony bytes are never mislabeled with the caller's earlier default.

typescript
Connector.create({
  name: 'xai',
  vendor: Vendor.Grok,
  auth: { type: 'api_key', apiKey: process.env.XAI_API_KEY! },
});

const xaiTTS = TextToSpeech.create({
  connector: 'xai', model: 'xai-tts', voice: 'eve',
});

for await (const chunk of xaiTTS.synthesizeStream('[laugh] Hello!', {
  format: 'mulaw',
  vendorOptions: {
    language: 'en',
    sample_rate: 8000,
    optimize_streaming_latency: 2, // xAI accepts levels 0, 1, and 2
  },
})) {
  if (chunk.audio.length) sendToPhone(chunk.audio);
}

Speech-to-Text

Basic Usage

typescript
import { Connector, SpeechToText, Vendor } from 'oneringai';

// Setup connector
Connector.create({
  name: 'openai',
  vendor: Vendor.OpenAI,
  auth: { type: 'api_key', apiKey: process.env.OPENAI_API_KEY! },
});

// Create STT instance
const stt = SpeechToText.create({
  connector: 'openai',
  model: 'gpt-transcribe',
});

// Transcribe from file path
const result = await stt.transcribeFile('./audio.mp3');
console.log(result.text);

// Transcribe from Buffer
import * as fs from 'fs/promises';
const audioBuffer = await fs.readFile('./audio.mp3');
const result2 = await stt.transcribe(audioBuffer);

When a Buffer contains a normal audio container, OneRingAI detects WAV, AIFF, MP3, FLAC, Ogg, WebM, MP4/M4A, or AAC bytes and preserves that format. For a headerless raw buffer, provide the normalized wire metadata. AAC ADTS headers, including the F0/F8 CRC-protected variants and F1/F9 unprotected variants, are checked before MPEG frame sync so AAC is never mislabeled as MP3:

typescript
const telephoneAudio = await stt.transcribe(pcm16le8kBuffer, {
  encoding: 'pcm',       // 'pcm', 'mulaw', or 'alaw'
  sampleRate: 8000,
});

Raw input otherwise defaults to signed 16-bit little-endian PCM at 16 kHz. This is especially important for Twilio audio, which is 8 kHz; TextPipeline sets that metadata automatically after the adapter decodes PCMU to PCM. When OpenAI receives raw μ-law or A-law, OneRingAI wraps it in a non-PCM WAV with an 18-byte WAVEFORMATEX chunk and the required zero cbSize; PCM keeps the standard 16-byte fmt chunk.

Timestamps

typescript
// Whisper remains available when word/segment timestamp formats are required.
const timestampedStt = SpeechToText.create({
  connector: 'openai',
  model: 'whisper-1',
});

// Word-level timestamps
const withWords = await timestampedStt.transcribeWithTimestamps(audioBuffer, 'word');
console.log(withWords.words);
// [
//   { word: 'Hello', start: 0.0, end: 0.5 },
//   { word: 'world', start: 0.6, end: 1.1 },
// ]

// Segment-level timestamps
const withSegments = await timestampedStt.transcribeWithTimestamps(audioBuffer, 'segment');
console.log(withSegments.segments);
// [
//   { id: 0, text: 'Hello world.', start: 0.0, end: 1.5 },
// ]

Google's dedicated gemini-3.5-transcribe model uses the Interactions API and supports automatic language detection, word/segment timestamps, custom vocabulary, and speaker diarization. gemini-3.5-transcribe-live is the bidirectional WebSocket model; the high-level file provider uses the non-live model. Google output is plain text, so json, verbose_json, srt, and vtt are rejected instead of being silently ignored:

typescript
const googleResult = await googleStt.transcribe(audio, {
  language: 'en',
  includeTimestamps: true,
  timestampGranularity: 'segment',
  vendorOptions: {
    customVocabulary: ['OneRingAI'],
    diarizationMode: 'speaker',
  },
});

Translation

Translate audio to English:

typescript
const stt = SpeechToText.create({
  connector: 'openai',
  model: 'whisper-1',
  language: 'fr', // default source-language hint for transcribe and translate
});

// Translate French audio to English
const english = await stt.translate(frenchAudioBuffer);
const germanHint = await stt.translate(germanAudioBuffer, { language: 'de' });
console.log(english.text);  // English translation

translate() forwards the instance default unless a per-call language is provided. The language is a source-language hint; the selected model/provider still determines the translation target.

Output Formats

typescript
// JSON (default)
const json = await stt.transcribe(audio, { outputFormat: 'json' });

// Plain text
const text = await stt.transcribe(audio, { outputFormat: 'text' });

// Subtitles (SRT format)
const srt = await stt.transcribe(audio, { outputFormat: 'srt' });

// WebVTT format
const vtt = await stt.transcribe(audio, { outputFormat: 'vtt' });

// Verbose JSON (includes all metadata)
const verbose = await stt.transcribe(audio, { outputFormat: 'verbose_json' });

Language Hints

typescript
// Provide language hint for better accuracy
const result = await stt.transcribe(audio, { language: 'fr' });  // French
const result2 = await stt.transcribe(audio, { language: 'es' }); // Spanish

xAI live streaming transcription

SpeechToText.transcribeStream() is available when the selected provider implements IStreamingSpeechToTextProvider. It accepts real-time-paced raw audio chunks and yields created, transcript, and done events. xAI supports PCM16, G.711 µ-law/A-law, interim results, keyterms, diarization, multichannel audio, VAD endpointing, and Smart Turn.

typescript
const liveSTT = SpeechToText.create({ connector: 'xai', model: 'xai-stt' });

for await (const event of liveSTT.transcribeStream(microphoneChunks(), {
  sampleRate: 16000,
  encoding: 'pcm',
  interimResults: true,
  language: 'en',
  vendorOptions: {
    keyterms: ['OneRingAI'],
    smart_turn: 0.7,
    smart_turn_timeout: 3000,
  },
})) {
  if (event.type === 'transcript') {
    renderTranscript(event.text ?? '', { final: event.isFinal });
  }
}

The library waits for transcript.created before sending binary frames and sends audio.done after the input iterator completes. The iterator must pace audio in real time; buffered audio is not automatically delayed. Yield { type: 'finalize' } (or include channel) for push-to-talk utterance finalization without closing the session.

Model Introspection

typescript
// Get model information
const info = stt.getModelInfo();
console.log(info.capabilities.features.diarization);  // Speaker identification

// Check feature support
const supportsTranslation = stt.supportsFeature('translation');
const supportsDiarization = stt.supportsFeature('diarization');

// Get supported formats
const inputFormats = stt.getSupportedInputFormats();
const outputFormats = stt.getSupportedOutputFormats();

// Get timestamp granularities
const granularities = stt.getTimestampGranularities();  // model-specific

Available Models

These tables describe checked-in registry metadata. Prices are estimates rather than live quotes, and account/region access must be verified separately. Prefer registry introspection for application model pickers.

TTS Models

Model Provider Features Pricing
gpt-4o-mini-tts OpenAI Instruction steering, emotions $0.015 / 1K chars
tts-1 OpenAI Fast, low-latency $0.015 / 1K chars
tts-1-hd OpenAI High-quality audio $0.030 / 1K chars
gemini-3.8-flash-tts Google Preferred creative/studio TTS, voice design and replication $1/M input tokens; $20/M output tokens
gemini-3.8-flash-lite-tts Google Preferred low-latency/high-throughput TTS and voice replication $0.50/M input tokens; $6/M output tokens
gemini-3.1-flash-tts-preview Google Legacy controllable low-latency TTS token priced
gemini-2.5-flash-preview-tts Google Deprecated; migrate to Gemini 3.8 Flash-Lite TTS token priced
gemini-2.5-pro-preview-tts Google Deprecated; migrate to Gemini 3.8 Flash TTS token priced
xai-tts xAI REST/WebSocket, expressive tags, custom voices, telephony codecs $0.015 / 1K chars

STT Models

Model Provider Features Price/minute
gpt-transcribe OpenAI Current accurate file transcription $0.0045
gpt-4o-mini-transcribe OpenAI Economical file transcription $0.003
gpt-live-transcribe OpenAI Recommended realtime streaming transcription, latency control, vocabulary/language hints $0.017
gpt-realtime-whisper OpenAI Low-latency realtime streaming transcription $0.017
gpt-4o-transcribe OpenAI Superior accuracy $0.006
gpt-4o-transcribe-diarize OpenAI Speaker identification $0.012
whisper-1 OpenAI Legacy-compatible general-purpose transcription $0.006
whisper-large-v3 Groq High-accuracy multilingual Whisper, word/segment timestamps $0.00185
whisper-large-v3-turbo Groq Current cost-optimized multilingual Whisper $0.00067
gemini-3.5-transcribe Google File transcription, word timestamps, vocabulary, diarization ~$0.005
gemini-3.5-transcribe-live Google Bidirectional WebSocket transcription ~$0.009
grok-voice-transcribe-2.0 xAI Current REST + streaming transcription, diarization, multichannel $0.00167 REST / $0.00333 stream
xai-stt xAI Legacy alias; migrate to Grok Voice Transcribe 2.0 $0.00167 REST / $0.00333 stream

Voice Assistant Pipeline

Combine TTS and STT for a voice assistant:

typescript
import { Connector, Agent, TextToSpeech, SpeechToText, Vendor } from 'oneringai';

// Setup
Connector.create({
  name: 'openai',
  vendor: Vendor.OpenAI,
  auth: { type: 'api_key', apiKey: process.env.OPENAI_API_KEY! },
});

const stt = SpeechToText.create({ connector: 'openai', model: 'gpt-transcribe' });
const agent = Agent.create({ connector: 'openai', model: 'gpt-5.6-terra' });
const tts = TextToSpeech.create({ connector: 'openai', model: 'tts-1-hd', voice: 'nova' });

// Voice assistant pipeline
async function voiceAssistant(audioInput: Buffer): Promise<Buffer> {
  // 1. Speech → Text
  const transcription = await stt.transcribe(audioInput);
  console.log('User said:', transcription.text);

  // 2. Text → AI Response
  if (!transcription.text) throw new Error('No transcript returned');
  const response = await agent.run(transcription.text);
  console.log('Agent response:', response.output_text);

  // 3. Text → Speech
  const audioResponse = await tts.synthesize(response.output_text ?? '');
  return audioResponse.audio;
}

Cost Estimation

typescript
import { calculateTTSCost, calculateSTTCost } from 'oneringai';

// TTS cost (per 1,000 characters)
const ttsCost = calculateTTSCost('tts-1-hd', 5000);  // 5000 characters
console.log(`TTS cost: $${ttsCost}`);  // $0.15

// STT cost (per minute)
const sttCost = calculateSTTCost('gpt-transcribe', 300);  // 5 minutes
console.log(`STT cost: $${sttCost}`);  // $0.0225

// Groq is much cheaper for STT
const groqCost = calculateSTTCost('whisper-large-v3', 300);  // 5 minutes
console.log(`Groq STT cost: $${groqCost}`);  // $0.0025

const xaiStreaming = calculateSTTCost('xai-stt', 300, { streaming: true });

Documentation snapshot · 2026-11-02Suggest a correction ↗

Search documentation

Type to search the v2 documentation.