रिअल-टाइम ट्रान्सक्रिप्ट दोन वेळा येतो: आधी सतत बदलणारा ड्राफ्ट म्हणून, मग न बदलणारी अंतिम ओळ म्हणून. ड्राफ्ट बदलता येणारी शेवटची ओळ म्हणून दाखवा आणि अंतिम ओळ इतिहास म्हणून ठेवा; लाइव्ह कॅप्शन व्ह्यूमधील बाकी सर्व जोडणी आहे.
रिअल-टाइम सेशन हे VocalStack API करत असलेले सर्वात लहान काम आहे. तुमचा सर्व्हर एक HTTP कॉल करतो आणि सही केलेला streamUrl परत मिळवतो. तुमचा क्लायंट त्या URL ला WebSocket जोडतो, ऑडिओ कॅप्चर होताच कच्चा ऑडिओ पाठवतो, आणि स्पीकरच्या काही क्षण मागे राहणारा ट्रान्सक्रिप्ट JSON टेक्स्ट फ्रेम्समध्ये वाचतो. ऑडिओ संपला की तुम्ही eof पाठवता, सर्व्हर उरलेले फ्लश करतो, सेशनमध्ये मोजलेली माहिती सांगतो आणि कनेक्शन बंद करतो.
काहीही साठवले जात नाही. ट्रान्सक्रिप्ट फक्त वायरवर असतो, आणि कॅप्शन व्ह्यू, मीटिंग ओव्हरले किंवा तुमच्या स्वतःच्या अॅपमधील व्हॉइस फीचरला तेच हवे असते. शब्द नंतर जपून ठेवायचे, शेअर करायचे आणि एक्सपोर्ट करायचे असतील तर तुम्हाला त्याऐवजी लाइव्ह ट्रान्सक्रिप्शन आणि ब्रॉडकास्ट हवे आहेत; शेवटचा विभाग तिकडे निर्देश करतो.
हा आकार बहुतेक स्ट्रीमिंग स्पीच API मध्ये सारखाच असतो: HTTP वरून क्रेडेन्शियल देवाणघेवाण, एक दीर्घ-काळ चालणारा सॉकेट जिथे ऑडिओ आत जातो आणि JSON बाहेर येतो, आणि तात्पुरता मजकूर आणि अंतिम मजकूर वेगळे ओळखण्याचा एक मार्ग. प्रत्येक API मध्ये नावे वेगळी असतात; मुख्य भाग क्वचितच वेगळे असतात. येथे, क्रमानुसार:
POST /v1/realtime/sessions. उत्तर म्हणून { sessionId, streamUrl, expiresAt } मिळते.streamUrl वर WebSocket उघडा.session.loading हार्टबीट्स येत असताना session.ready येईपर्यंत थांबा.transcript.draft वाचा (शेवटचा भाग बदला) आणि transcript.committed वाचा (अंतिम ओळ जोडा).{"type":"eof"} पाठवा; session.done वाचा.फक्त पहिल्या चरणाला तुमची की लागते, म्हणून ते तुमच्या सर्व्हरवर होते.
Developers, API keys अंतर्गत Write परवानगीसह एक की तयार करा. की सर्व्हर्स आणि स्क्रिप्ट्ससाठी आहे: वेब पेजवरून किंवा URL मध्ये पाठवलेली की API नाकारते, म्हणून सेशन तुमच्या स्वतःच्या राउटमागे तयार करा, आणि पेज फक्त streamUrl पाहतो.
URL मध्ये स्वतःचा टोकन असतो, आणि त्यामागे एक ब्राउझर मर्यादा आहे जी सर्वसाधारणपणे जाणून घेणे उपयोगी आहे. ब्राउझरचा WebSocket कन्स्ट्रक्टर रिक्वेस्ट हेडर सेट करू शकत नाही, त्यामुळे की ठेवायला Authorization हेडर नसतो; प्रत्येक ब्राउझर-समोरचा स्पीच API आपल्या सॉकेटला URL मध्येच अल्प-मुदतीच्या टोकनने प्रमाणित करतो (किंवा सबप्रोटोकॉलच्या नावात लपवून). अल्प-मुदत हाच मुद्दा आहे: URL लॉग्स आणि इतिहासात जाऊन पोहोचतो, त्यामुळे त्यात असलेले टोकन कालबाह्य व्हावे लागते. expiresAt च्या आधी कनेक्ट करा.
SDKs Developers area मधून डाउनलोड केले जातात, पॅकेज रजिस्ट्रीमधून नाही; quickstart मधील Install विभाग या वातावरणासाठी स्वतःच्या कमांड्स दाखवतो. TypeScript SDK सह सर्व्हर राउट असा दिसतो:
// On your server, behind a route of yours (POST /api/transcribe, say):
import { VocalStack } from '@vocalstack/sdk';
const vs = new VocalStack({ baseUrl: '{{api}}', apiKey: process.env.VOCALSTACK_API_KEY });
export async function createStream(): Promise<{ sessionId: string; streamUrl: string }> {
const { sessionId, streamUrl } = await vs.realtime.createSession({ spokenLanguage: 'en' });
return { sessionId, streamUrl };
}
SDK शिवाय हे एकच fetch आहे. हे उदाहरण स्पॅनिश भाषांतरही मागते आणि वाक्य जोडणी बंद करते, खाली दिलेल्या वायर नमुन्याप्रमाणे:
const res = await fetch('{{api}}/v1/realtime/sessions', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.VOCALSTACK_API_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ spokenLanguage: 'en', translate: ['es'], sentences: { enabled: false } }),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`); // the error envelope: { code, message, … }
const { streamUrl } = (await res.json()) as { sessionId: string; streamUrl: string; expiresAt: string };
प्रत्येक पर्यायाला डीफॉल्ट मूल्य असते. सुरुवातीला माहीत असायला हवेत असे पर्याय म्हणजे spokenLanguage (हे वगळले तर भाषा ओळखली जाते), translate (लक्ष्य भाषांची यादी), speakerLabels आणि latencyMode; बाकीचे खाली गरजेनुसार येतात.
streamUrl ऑडिओ कॅप्चर करणाऱ्या कोणत्याही गोष्टीला द्या; यापुढे कोणतीही की लागत नाही.
streamUrl ला जसे आहे तसे कनेक्ट करा, कोणताही हेडर न लावता. सेशन सुरू होत असताना सर्व्हर session.loading हार्टबीट्स पाठवतो. मग session.ready येतो. त्याआधी कोणताही ऑडिओ पाठवू नका. लवकर पाठवलेला ऑडिओ नंतरसाठी ठेवला जात नाही; सर्व्हर त्याला त्रुटी मानतो, error फ्रेम पाठवतो आणि सॉकेट बंद करतो.
सॉकेट उघडल्यावर प्रत्येक अपयश त्या फ्रेमच्या स्वरूपात येते: {"type":"error","code":"…","message":"…","retryable":true|false}. code ही बंद यादी आहे (invalid_audio, invalid_options, invalid_token, capacity_exceeded, session_expired, slow_consumer, engine_unavailable, internal), message तुमच्या लॉग्ससाठी आहे, आणि retryable सांगते की नवीन सेशन प्रयत्न करण्यासारखे आहे का. पहिल्या फ्रेमपासूनच ती हाताळा:
const ws = new WebSocket(streamUrl); // the token is in the URL: no header
ws.binaryType = 'arraybuffer';
ws.addEventListener('message', (m: MessageEvent) => {
if (typeof m.data !== 'string') return;
const f = JSON.parse(m.data);
switch (f.type) {
case 'session.loading':
console.error('starting…');
break;
case 'session.ready':
startMicrophone(); // send audio only after session.ready
break;
case 'error':
console.error(`${f.code}: ${f.message}`);
if (f.retryable) scheduleReconnect(); // a new session, with backoff; see "Stop"
break;
}
});
कच्च्या क्लायंटने आणखी दोन गोष्टी स्वतः हाताळायच्या असतात; SDKs ते आधीच करतात. पहिली, नाकारलेल्या अपग्रेडमध्ये API चा नेहमीचा HTTP एरर एन्व्हेलप येतो: 401 invalid_token, 410 session_expired, किंवा Retry-After हेडरसह 503 unavailable, जो पाळायचा आहे.
दुसरी, संपृक्तता (saturation) त्रुटीसारखी दिसत नाही, तर शांततेसारखी दिसते. 30 सेकंद एकही फ्रेम न आल्यास, त्याला capacity_exceeded समजा आणि पुन्हा कनेक्ट करा; नवीन सेशन सहसा जाते. लोडिंग फ्रेम येत राहिल्या पण session.ready कधीच आला नाही, तर त्याला engine_unavailable समजा, जे देखील retryable आहे. SDKs मध्ये RealtimeSession.connect session.ready आल्यावर पूर्ण होते आणि readyDeadlineMs नंतर योग्य कोडसह अयशस्वी होते.
session.ready आल्यावर सर्व्हर बोललेल्या वेगाने ऑडिओची अपेक्षा करतो.
ऑडिओ कच्च्या PCM चे बायनरी फ्रेम्स म्हणून जातो: 32-bit float, little-endian, 16 kHz, mono (pcm_f32le, सेशनचा डीफॉल्ट audio फॉरमॅट). तो कॅप्चर होताच, सुमारे 100 ते 250 ms च्या तुकड्यांमध्ये पाठवा, आणि प्रत्येक तुकड्यामध्ये ट्रान्सक्रिप्टची वाट न पाहता.
गणित एकदा करून पाहण्यासारखे आहे. 16,000 नमुने प्रति सेकंद, प्रत्येकी 4 बाइट्स, म्हणजे प्रति सेकंद 64,000 बाइट्स, म्हणून 100 ms म्हणजे 6,400 बाइट्स आणि 250 ms म्हणजे 16,000. स्पीच मॉडेल्स सामान्यतः 16 kHz mono वर प्रशिक्षित केली जातात, म्हणूनच अनेक रिअल-टाइम APIs तेच मागतात; तर मायक्रोफोन आणि ब्राउझर 44.1 किंवा 48 kHz वर आणि अनेकदा स्टीरिओमध्ये कॅप्चर करतात. बाइट्स जाण्यापूर्वी काहीतरी ऑडिओचा रीसॅम्पल करून चॅनेल्स एकत्र करणे आवश्यक आहे: येथे ते ffmpeg करतो, आणि ब्राउझरमध्ये ते SDK चा मायक्रोफोन हेल्पर करतो. 48 kHz ऑडिओ पाठवून तो 16 kHz म्हणून घोषित केला तर ते अपयशी होत नाही; पण ते स्लो-मोशनमधील बोलणे ट्रान्सक्रिप्ट करते, आणि ते चुकीचे.
Node मध्ये, ffmpeg हे सर्वात सोपे कॅप्चर डिव्हाइस आहे:
import { spawn } from 'node:child_process';
function startMicrophone(): void {
// Raw PCM: f32le, 16 kHz, mono, as binary frames. macOS: -f avfoundation -i :0, Linux: -f pulse -i default.
const mic = spawn('ffmpeg', ['-loglevel', 'error', '-f', 'avfoundation', '-i', ':0', '-f', 'f32le', '-ac', '1', '-ar', '16000', '-']);
mic.stdout.on('data', (chunk: Buffer) => {
// Backpressure: bytes the socket has accepted but not yet put on the wire.
// Two seconds of audio (128,000 bytes) queued means the link, not the server, is behind.
if (ws.bufferedAmount > 128_000) console.error(`link behind by ${ws.bufferedAmount} bytes`);
ws.send(new Uint8Array(chunk));
});
mic.on('close', () => ws.send(JSON.stringify({ type: 'eof' })));
process.once('SIGINT', () => mic.kill());
}
ws.send कधीच ब्लॉक होत नाही; ते रांगेत टाकते. bufferedAmount ही त्या रांगेचा आकार आहे, आणि निरोगी लिंकवर ते जवळपास शून्य असते. लाइव्ह ऑडिओ थांबवता येत नाही, म्हणून वाढत जाणाऱ्या रांगेला उपयोगी प्रतिसाद म्हणजे ते लक्षात घेणे, फ्रेम्स वगळणे नव्हे. सर्व्हरही बॅकप्रेशर लावतो. तो रिअल टाइमच्या काही सेकंद पुढचा ऑडिओ स्वीकारतो (limits.audioBufferS), आणि मग तो पकडेपर्यंत पुढचे वाचणे थांबवतो. परतीच्या मार्गावर कमिट्स नुकसानाशिवाय रांगेत ठेवल्या जातात, तर ड्राफ्ट्स एकत्र केले जातात, त्यामुळे हळू वाचणाऱ्याला कमी, पण ताजे ड्राफ्ट्स दिसतात. कमिट्सनेही मागे पडणारा क्लायंट slow_consumer आणि retryable: false असलेली error फ्रेम मिळवतो आणि डिस्कनेक्ट होतो; ते दिसले तर तुमचा मेसेज हँडलर खूप जास्त काम करत आहे.
ब्राउझरमध्ये, @vocalstack/sdk/mic मधील streamMicrophone मायक्रोफोनची परवानगी मागतो, 16 kHz वर रीसॅम्पल करतो, आणि चंक्स clock फ्रेम्ससह पाठवतो, ज्यामुळे ब्राउझर टॅब थ्रॉटल केल्यावर सर्व्हर गॅप्स चिन्हांकित करू शकतो. पेज सेशन तुमच्या राउटकडून घेतो आणि की कधीच पाहत नाही:
import { RealtimeSession } from '@vocalstack/sdk';
import { streamMicrophone } from '@vocalstack/sdk/mic';
export async function transcribeMicrophone(
stream: { sessionId: string; streamUrl: string },
render: (committed: string, tail: string) => void,
): Promise<void> {
const session = await RealtimeSession.connect(stream);
const mic = await streamMicrophone(session); // asks for the microphone, sends 16 kHz PCM
document.querySelector('#stop')?.addEventListener('click', () => {
void mic.stop();
session.eof();
});
let committed = '';
for await (const ev of session) {
if (ev.type === 'transcript.draft') render(committed, ev.segments.map((s) => s.text).join(' '));
if (ev.type === 'transcript.committed') {
committed += `${ev.segment.text} `;
render(committed, '');
}
}
}
हे render(committed, tail) सिग्नेचर हाच संपूर्ण UI पॅटर्न आहे; पुढचा विभाग का ते सांगतो.
दोन फ्रेम प्रकार ट्रान्सक्रिप्ट बनवतात, आणि त्यांची गल्लत करणे ही सर्वात सामान्य चूक आहे.
प्रथम, दोन का आहेत. स्पीच मॉडेल एखादा शब्द काय होता हे अंशतः पुढे येणाऱ्या शब्दांवरून ठरवते: "I scream" आणि "ice cream" वाक्य पुढे जाईपर्यंत सारखेच वाजतात. त्यामुळे रिअल-टाइम रेकग्नायझर लवकर एक अंदाज देतो, मग आणखी ऑडिओ आल्यावर तो सुधारतो, आणि पुरेसे ऐकल्यावरच स्थिर होतो. प्रत्येक स्ट्रीमिंग स्पीच API हे कसे तरी उघड करतो. काही त्या अंदाजांना "interim" किंवा "partial" निकाल म्हणतात आणि स्थिर झालेल्यांना is_final ने चिन्हांकित करतात; शब्दावली बदलते, शिस्त बदलत नाही. येथे अंदाजाला ड्राफ्ट आणि स्थिर मजकुराला कमिट म्हटले आहे.
transcript.draft हा लाइव्ह टेल आहे: शेवटच्या कमिटनंतर ऐकलेले शब्द, जे अजूनही बदलत आहेत. प्रत्येक ड्राफ्ट मागील ड्राफ्टची पूर्ण जागा घेतो. त्याच्या segments सह टेल पुन्हा काढा; त्याला कधीच जोडू नका. ड्राफ्ट्सना हेतुपुरस्सर seq नसतो, त्यामुळे ते ज्या कमिट्समध्ये बदलतील त्यांच्याशी जुळवण्याचा प्रयत्न करू नका.
transcript.committed हे अंतिम आहे: seq (0, 1, 2, अंतर न पडता), start, end, text, speaker आणि प्रत्येक शब्दाच्या वेळेसह words असलेला एक segment. ते जोडा आणि ते कव्हर करणारा ड्राफ्ट टाका; पुढचा ड्राफ्ट या कमिटच्या शेवटापासून सुरू होतो.
committed: [seq 0] [seq 1]
draft: "how are" ← frame 1
draft: "how are you do" ← frame 2 REPLACES frame 1
committed: [seq 0] [seq 1] [seq 2 "How are you doing?"]
← the commit consumes the tail; drop the draft
ड्राफ्ट जोडणारा UI डुप्लिकेट, चमकणारा मजकूर दाखवतो. तुमच्या कॅप्शन व्ह्यूमध्ये असा थरथर येतो जो स्पीकरकडे कधीच नव्हता. सर्व कमिट झालेले सेगमेंट्स दाखवा, मग त्यांच्या नंतर सर्वात नवीन ड्राफ्ट दाखवा, आणि कमिट आल्यावर ड्राफ्ट साफ करा. कमिटमध्ये असे शब्द असू शकतात जे कोणत्याही ड्राफ्टमध्ये कधीच दिसले नव्हते.
वरील fetch उदाहरणातील (translate: ["es"], वाक्य जोडणी बंद) काही सेकंदांचे बोलणे वायरवर असे दिसते:
{"type":"session.loading"}
{"type":"session.ready"}
{"type":"transcript.draft","segments":[{"start":0.0,"text":"Good","speaker":0,"tentative":true}]}
{"type":"transcript.draft","segments":[{"start":0.0,"text":"Good morning every","speaker":0,"tentative":true}]}
{"type":"transcript.committed","segment":{"seq":0,"start":0.0,"end":1.4,"text":"Good morning, everyone.","speaker":0,"words":[{"start":0.0,"end":0.3,"text":"Good"},{"start":0.3,"end":0.8,"text":"morning,"},{"start":0.8,"end":1.4,"text":"everyone."}]}}
{"type":"transcript.translated","seq":0,"translations":{"es":"Buenos días a todos."}}
{"type":"transcript.draft","segments":[{"start":1.6,"text":"Thanks for","speaker":0,"tentative":true}]}
या फ्रेम्सच्या आजूबाजूला आणखी दोन फ्रेम्स येतात. transcript.amended एका कमिट झालेल्या सेगमेंटला जागेवरच दुरुस्त करते (त्याच seq सह नवीन text किंवा speaker), जेव्हा त्यातील एखादा शब्द दुरुस्त झालेला असतो; ते तुम्ही आधीच काढलेल्या ओळीवर लागू करा. transcript.words कमिट होण्याआधी टेलसाठी शब्दांच्या वेळा स्ट्रीम करते. त्यासाठी events.words लागतो, जो डीफॉल्टनुसार चालू असतो; इव्हेंट पर्याय काहीही सांगोत, कमिटमध्ये नेहमी त्याचे words असतात. शेवटी, तुम्हाला माहीत नसलेले फ्रेम प्रकार दुर्लक्षित करा: नवीन प्रकार सूचना न देता जोडले जातात.
हा Python SDK चा वाचन लूप आहे, टर्मिनल हाच कॅप्शन व्ह्यू. येथेही वाक्य जोडणी बंद आहे, त्यामुळे भाषांतरे transcript.translated म्हणून येतात, आणि error फ्रेम लूपमधून VocalStackError म्हणून बाहेर पडते:
import asyncio
import os
from vocalstack import RealtimeSession, VocalStack, VocalStackError
MIC = ["ffmpeg", "-loglevel", "error", "-f", "avfoundation", "-i", ":0",
"-f", "f32le", "-ac", "1", "-ar", "16000", "-"]
async def main() -> None:
async with VocalStack("{{api}}", api_key=os.environ["VOCALSTACK_API_KEY"]) as vs:
created = await vs.realtime.create_session(
spoken_language="en", translate=["es"], sentences={"enabled": False}
)
session = await RealtimeSession.connect(created.session_id, created.stream_url)
mic = await asyncio.create_subprocess_exec(*MIC, stdout=asyncio.subprocess.PIPE)
async def feed() -> None:
assert mic.stdout is not None
while chunk := await mic.stdout.read(8000): # 125 ms of audio
await session.send_audio(chunk)
await session.eof() # the server flushes, then sends session.done
feeder = asyncio.create_task(feed())
try:
async with session:
async for event in session:
if event.type == "transcript.draft":
# The live tail: replace it, never append it.
tail = " ".join(s.text for s in event.segments)
print(f"\r\x1b[K\x1b[2m{tail}\x1b[0m", end="", flush=True)
elif event.type == "transcript.committed":
# Final: append it (seq 0, 1, 2...), and the tail it covers is gone.
print(f"\r\x1b[K{event.segment.text}", flush=True)
elif event.type == "transcript.translated":
print(f" es: {event.translations.get('es')}")
await feeder
print(f"done ({session.done_reason})", session.stats)
except VocalStackError as e:
# An `error` frame mid-stream, or a refused connect.
print(f"\nerror {e.code}: {e.message} (retryable: {e.retryable})")
finally:
if mic.returncode is None:
mic.kill()
asyncio.run(main())
वरीलप्रमाणे प्रति-सेगमेंट भाषांतर हा सोपा प्रकार आहे. जो प्रकार वाचकांना अधिक आवडतो त्याला स्वतःचा विभाग आहे.
translate: ["es", "fr"] सेशन पुढे जाताना भाषांतरे मागते. ती कशी येतात हे वाक्य जोडणीवर (sentences: { enabled: true }) अवलंबून आहे, जी डीफॉल्टनुसार चालू असते.
वाक्य जोडणी बंद असताना, प्रत्येक कमिटनंतर थोड्या वेळाने transcript.translated फ्रेम येते, जी त्याचा seq नावाने देते आणि भाषेनुसार मांडलेले translations देते; ते त्या सेगमेंटला जोडा. वरील उदाहरणे हेच वापरतात. हे सोपे आहे आणि कमिट लॉगसारखे वाचले जाते: एक सेगमेंट, एक भाषांतर, आणि वाक्याची सीमा जिथे स्पीकरने श्वास घेतला तिथे.
वाक्य जोडणी चालू असताना, सर्व्हर कमिट्सना पूर्ण वाक्यांमध्ये जोडतो आणि त्यांचे भाषांतर करतो, जे खूप चांगले वाचते. प्रत्येक transcript.sentence फ्रेम एका वाक्याची पूर्ण जागा घेते, sentenceId द्वारे ओळखली जाते. तिची status open वरून closed (किंवा dropped, म्हणजे टाकून द्या) अशी बदलते, आणि तिची translations उघड असताना तात्पुरती असतात आणि बंद झाल्यावर अंतिम. sentenceId नुसार अपसर्ट करा (असल्यास बदला, नसल्यास जोडा), कधीही फक्त जोडू नका. TypeScript SDK यासाठीच applySentence पुरवतो:
import { applySentence, type SentenceSegment } from '@vocalstack/sdk';
let sentences: SentenceSegment[] = [];
for await (const ev of session) {
if (ev.type !== 'transcript.sentence') continue;
sentences = applySentence(sentences, ev.sentence); // upsert by sentenceId
if (ev.sentence.status === 'closed') console.log(`${ev.sentence.text}\n es: ${ev.sentence.translations?.es}`);
}
भाषांतरे ट्रान्सक्रिप्टच्या एक-दोन सेकंद मागे येतात. त्यांनी आवाजातही बोलले जावे असे वाटत असेल तर voice: ["es"] जोडा (त्यासाठी वाक्ये हवीत). प्रत्येक बंद वाक्याचे स्पॅनिश मग transcript.voice फ्रेम म्हणून येते, ज्यात क्लिप 24 kHz mono मधील base64 Ogg/Opus म्हणून असते. लांब वाक्य तुकड्यांमध्ये (parts पैकी part) येते, जे एकापुढे एक वाजवायचे. तात्पुरता मजकूर कधीच बोलला जात नाही. लाइव्ह भाषांतर मार्गदर्शक मध्ये व्हॉइसेसचे तपशील आहेत, आणि सेकंद कुठे जातात हे विलंबाची साखळी समजावते.
लक्ष्य भाषा सेशन तयार करताना निश्चित होत नाहीत.
session.update टेक्स्ट फ्रेम spokenLanguage, translate, speakerLabels, latencyMode, voice किंवा voiceByLang त्या क्षणापासून बदलते; जे फील्ड्स तुम्ही वगळता ते तसेच राहतात.
{"type":"session.update","translate":["es","fr"],"latencyMode":"fast"}
SDKs ते session.update(…) म्हणून देतात. translate संपूर्ण यादी बदलते, आणि [] भाषांतर बंद करते. आधी दिलेले सेगमेंट्स पुन्हा ट्रान्सक्रिप्ट किंवा भाषांतरित केले जात नाहीत, आणि स्पीकर लेबल्स चालू केले तर तिथूनच स्पीकरना लेबल दिले जातात. जे बोलले गेले ते बोलले गेले; सेटिंग्ज फक्त पुढे येणाऱ्या गोष्टींवर लागू होतात.
ऑडिओ संपला की {"type":"eof"} पाठवा (SDKs मध्ये session.eof()). सर्व्हर अजून धरून ठेवलेले ट्रान्सक्रिप्ट करतो, शेवटच्या कमिट्स पाठवतो, मग session.done पाठवून बंद होतो. त्याऐवजी cancel पाठवले तर ते लगेच थांबते आणि उरलेले टाकून देते.
{"type":"session.done","reason":"eof","stats":{"audioS":30.0,"processingS":30.6,"realtimeFactor":0.98}}
reason eof, max_duration (सेशन प्लॅनच्या कमाल कालावधीपर्यंत पोहोचले आणि तोच फ्लश चालला) किंवा cancelled असते. stats सर्व्हर मोजतो: audioS म्हणजे तुम्ही पाठवलेला ऑडिओ, processingS म्हणजे सेशन चालले तो भिंतीवरचा वेळ, आणि realtimeFactor त्यांचे गुणोत्तर. लाइव्ह सेशन 1 च्या अगदी थोडे खाली येते, कारण भिंतीवरच्या वेळेत तुमच्या eof नंतरचा अंतिम फ्लश समाविष्ट असतो. त्यापेक्षा खूप कमी आकडा म्हणजे सेशनला ऑडिओच्या कालावधीपेक्षा लक्षणीय जास्त वेळ लागला; पहिली गोष्ट तपासायची म्हणजे ऑडिओ किती वेगाने आला. फाइलसाठी, त्याच आकड्यावरून कळते की प्लेबॅकपेक्षा किती पट वेगाने ट्रान्सक्रिप्ट परत आले.
सेशन जास्तीत जास्त limits.maxDurationS इतके चालते (GET /v1/realtime/capabilities वरून), आणि त्याचा ऑडिओ तुमच्या प्लॅनच्या लाइव्ह दराने प्रति सेकंद तुमचे क्रेडिट खर्च करतो, कोणत्याही ट्रान्सक्रिप्शनप्रमाणे 5-मिनिटांच्या किमान मर्यादेसह: ऑडिओ मिळालेले प्रत्येक सेशन किमान 5 मिनिटांसाठी बिल होते (किंमत). त्याच सॉकेटवर एक संपूर्ण फाइलही घेता येते: सेशन input: { kind: "file" } सह तयार करा, फाइलचे स्वतःचे बाइट्स (mp3, wav, flac, ogg, m4a) आणि eof पाठवा, आणि ट्रान्सक्रिप्ट ऑडिओ प्लेबॅकपेक्षा कितीतरी पट वेगाने परत येते, वाटेत transcript.progress फ्रेम्ससह.
येथे पुन्हा सुरू करणे (resume) नाही. जोडणी तुटली तर सेशन संपते; पुन्हा कनेक्ट करणे म्हणजे नवीन सेशन तयार करणे, ज्याचे स्वतःचे किमान बिल होते, आणि तुमच्या लाइव्ह स्रोतातून seq 0 पासून पुन्हा स्ट्रीम करणे. पुन्हा कनेक्ट करण्याचा नमुना कोणत्याही सॉकेट API साठी मानक आहे, आणि एकदा नीट करण्यासारखा आहे:
error फ्रेमवर retryable: true, Retry-After सह 503, किंवा 30 सेकंद एकही फ्रेम न येणे, जे capacity_exceeded दर्शवते. invalid_token आणि session_expired म्हणजे तुमच्या सर्व्हरकडून नवीन streamUrl, जुन्या URL ने पुन्हा प्रयत्न नव्हे.सेशन तयार करणारा राउट वारंवार कॉल करणे स्वस्त ठेवा आणि बंद होणे हे नवीन सुरुवात म्हणून हाताळा. पुन्हा सुरू न होणे हीच साठवण न करण्याची किंमत आहे; ट्रान्सक्रिप्ट सॉकेटच्या पलीकडे टिकायला हवे असेल तर ते स्वतः ठेवा.
त्याच स्ट्रीमने रेकॉर्डिंग म्हणून साठवावे, नंतर दुरुस्त करावे, एक्सपोर्ट करावे, आणि प्रेक्षकांनी त्यांच्या स्वतःच्या भाषांत वाचावे असे वाटत असेल तर, त्याऐवजी ते लाइव्ह ट्रान्सक्रिप्शनला प्रकाशित करा. थेट जाण्याचे मार्गदर्शक तोच PCM सॉकेट एक ट्रान्सक्रिप्शन आणि ब्रॉडकास्ट लिंक यामधून दाखवतो, आणि ऐकण्याचे मार्गदर्शक श्रोत्याची बाजू दाखवतो, जिथे प्रत्येक श्रोता एक भाषा निवडतो आणि उशिरा सामील झालेला lastSeq कर्सरवरून पकडतो. एका खोलीसाठी ते कसे दिसते, हा एक लिंक, प्रत्येक भाषा चा विषय आहे.
जपून ठेवायची फाइल हवी असल्यास, साठवणारा मार्ग API वापरून फाइलचे ट्रान्सक्रिप्शन आहे. पूर्ण फ्रेम संदर्भ, प्रत्येक फील्डसह, API संदर्भातील मायक्रोफोन स्ट्रीम करा मार्गदर्शक आहे.