Audio and Speech
Transcribe spoken audio, generate timestamped subtitles, and synthesize natural text-to-speech with Whisper and OpenAI.
TL;DR
- Transcribe spoken audio recordings into text using Whisper
transcriptions.create()calls. - Request word-level timestamps using
verbose_jsonresponse formatting options. - Synthesize natural speech audio streams utilizing OpenAI
audio.speech.create()APIs.
Whisper Transcription API
Basic Audio File TranscriptionTranscribe local audio recording to plain text string.
import OpenAI from 'openai';
import fs from 'node:fs';
const client = new OpenAI();
const tr = await client.audio.transcriptions.create({
file: fs.createReadStream('meeting.mp3'),
model: 'whisper-1',
});
console.log(tr.text);Domain Glossary PromptingGuide Whisper spelling for technical jargon and proper nouns.
const tr = await client.audio.transcriptions.create({
file: fs.createReadStream('podcast.m4a'),
model: 'whisper-1',
prompt: 'Cheatsheet, TypeScript, PostgreSQL, Zod.',
});Automatic Audio TranslationTranscribe foreign language speech directly into English.
const tr = await
client.audio.translations.create({
file: fs.createReadStream('spanish_call.mp3'),
model: 'whisper-1',
});
console.log(tr.text);Timestamps & Subtitle Generation
Word-Level Timestamp RequestRequest granular millisecond boundaries for video subtitles.
const out = await client.audio.transcriptions.create({
file: fs.createReadStream('clip.mp3'),
model: 'whisper-1',
response_format: 'verbose_json',
timestamp_granularities: ['word', 'segment'],
});
const words = (out as any).words;SRT Subtitle File GeneratorFormat segment timestamps into standard SubRip format.
function toSRTTime(seconds: number): string {
const ms = Math.floor((seconds % 1) * 1000);
const s = Math.floor(seconds % 60);
const m = Math.floor((seconds / 60) % 60);
const h = Math.floor(seconds / 3600);
const p = (n: number, d = 2) => {
return String(n).padStart(d, '0');
};
return `${p(h)}:${p(m)}:${p(s)},${p(ms, 3)}`;
}Segment IteratorIterate across subtitle blocks and construct SRT cues.
function buildSRT(segments: any[]) {
return segments.map((seg, i) => {
const start = toSRTTime(seg.start);
const end = toSRTTime(seg.end);
const time = `${start} --> ${end}`;
const body = `${time}\n${seg.text.trim()}`;
return `${i + 1}\n${body}\n`;
}).join('\n');
}Text-to-Speech (TTS) Synthesis
Lifelike Voice Audio GenerationSynthesize spoken narration from text script.
const mp3 = await client.audio.speech.create({
model: 'tts-1',
voice: 'alloy', // alloy, echo, fable, onyx, nova
input: 'Welcome to Useful Cheatsheets.',
});
const buffer = Buffer.from(await mp3.arrayBuffer());
await fs.promises.writeFile('welcome.mp3', buffer);HD Model Voice SelectionUse tts-1-hd for master production audio tracks.
const hd = await client.audio.speech.create({
model: 'tts-1-hd',
voice: 'nova',
input: 'High-definition voice mastering.',
response_format: 'mp3',
speed: 1.05,
});Low-Latency Opus StreamingStream spoken audio chunks with Opus codec for real-time apps.
const stream = await client.audio.speech.create({
model: 'tts-1',
voice: 'echo',
input: 'Real-time assistant response.',
response_format: 'opus',
});
// Pipe directly to client web socketAudio Preprocessing & File Limits
File Size Gate CheckVerify audio file adheres to 25MB HTTP upload ceiling.
function checkAudioSize(filePath: string) {
const stat = fs.statSync(filePath);
const mb = stat.size / (1024 * 1024);
if (mb > 24) {
const s = mb.toFixed(1);
throw new Error(`File ${s}MB exceeds 25MB`);
}
}Segment Audio Splitter ConceptSlice long recordings into 10-minute segments before upload.
// ffmpeg -i in.mp3 -f segment -segment_time 600
// -c copy part%03d.mp3
// Process parts through Whisper and merge transcriptsTranscript Merging SanitizerJoin sliced audio transcript segments into unified text.
function mergeTranscripts(parts: string[]) {
return parts
.map(p => p.trim())
.filter(Boolean)
.join(' ');
}Tips
- Specify custom glossaries and acronyms inside the
promptparameter of Whisper to dramatically improve transcription accuracy. - Chunk audio files larger than 25 megabytes into smaller segments with
ffmpegbefore transmitting to provider endpoints.
Warnings
- Never send raw uncompressed WAV audio across network boundaries when lightweight
mp3orm4afiles preserve identical acoustic fidelity. - Avoid blocking main event loops during long speech generation requests by utilizing streaming
toReadableStream()pipe mechanics.
In Practice
Checks file size, dispatches audio file to Whisper, and writes transcript to output file.
- Import OpenAI SDK and Node file system modules.
- Verify audio file does not exceed 25MB provider ceiling.
- Create readable file stream and dispatch to whisper-1.
- Write returned transcript string to local markdown file.
import OpenAI from 'openai';
import fs from 'node:fs';
const client = new OpenAI();
async function transcribeAudio(file: string) {
const s = fs.statSync(file);
if (s.size > 24 * 1024 * 1024) {
throw new Error('File exceeds 25MB limit');
}
const r = await client.audio.transcriptions.create({
file: fs.createReadStream(file),
model: 'whisper-1',
});
return r.text;
}
console.log(await transcribeAudio('note.mp3'));FAQ
OpenAI Whisper accepts files up to 25MB. For longer recordings, use ffmpeg or fluent-ffmpeg to split audio into 10-minute segments before transcribing.
Setting timestamp_granularities: ['word'] returns start and end millisecond timestamps for every individual spoken word, perfect for video captioning.
OpenAI TTS supports mp3, opus (for low-latency streaming), aac, flac, and uncompressed pcm audio formats.