JavaScript SDK
Scribe:在 JavaScript 中进行实时语音转文本
安装
npm install @elevenlabs/client# oryarn add @elevenlabs/client# orpnpm install @elevenlabs/client
使用 ElevenLabs 语音转文本技能,通过 AI 编程助手转写音频:
npx skills add elevenlabs/skills --skill speech-to-text
此库可用于任何基于 JavaScript 的项目。如果使用 React,建议使用
useScribe hook,它提供
内置状态管理和生命周期处理。
使用方法
以下是连接 Scribe 并记录转写结果的最小可运行示例:
import { Scribe, RealtimeEvents } from "@elevenlabs/client";const token = await fetchTokenFromServer();const connection = Scribe.connect({token,modelId: "scribe_v2_realtime",microphone: {echoCancellation: true,noiseSuppression: true,},});connection.on(RealtimeEvents.PARTIAL_TRANSCRIPT, (data) => {console.log("Partial:", data.text);});connection.on(RealtimeEvents.COMMITTED_TRANSCRIPT, (data) => {console.log("Committed:", data.text);});// Later, close the connectionconnection.close();
获取令牌
Scribe 需要一次性令牌进行身份验证。在服务器上创建一个 API 端点:
// Node.js serverapp.get("/scribe-token", yourAuthMiddleware, async (req, res) => {const response = await fetch("https://api.elevenlabs.io/v1/single-use-token/realtime_scribe", {method: "POST",headers: {"xi-api-key": process.env.ELEVENLABS_API_KEY,},});const data = await response.json();res.json({ token: data.token });});
ElevenLabs API 密钥十分敏感。切勿将其暴露给客户端。始终在 服务器上生成令牌。
// Clientconst fetchToken = async () => {const response = await fetch("/scribe-token");const { token } = await response.json();return token;};
连接选项
Scribe.connect() 接受麦克风选项或手动音频选项。两者共用一组基础选项。
基础选项
| 属性 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| token | string | 用于 WebSocket 身份验证的一次性令牌。 | |
| modelId | string | 模型 ID(例如 "scribe_v2_realtime")。 | |
| baseUri | string | "wss://api.elevenlabs.io" | 自定义 WebSocket 基础 URI。 |
| commitStrategy | CommitStrategy | "manual" | "manual" 或 "vad"。 |
| vadSilenceThresholdSecs | number | 1.5 | VAD 提交前的静音秒数(0.3-3.0)。 |
| vadThreshold | number | 0.4 | VAD 灵敏度(0.1-0.9,数值越低越灵敏)。 |
| minSpeechDurationMs | number | 100 | 最短语音时长,单位为 ms(50-2000)。 |
| minSilenceDurationMs | number | 100 | 最短静音时长,单位为 ms(50-2000)。 |
| languageCode | string | ISO-639-1 或 ISO-639-3 语言代码。留空则自动检测。 | |
| includeTimestamps | boolean | false | 通过 COMMITTED_TRANSCRIPT_WITH_TIMESTAMPS 事件接收词级时间戳。 |
麦克风选项
传入 microphone 对象,可直接从用户麦克风流式传输音频。连接会自动处理 getUserMedia 和音频编码。
const connection = Scribe.connect({token,modelId: "scribe_v2_realtime",microphone: {deviceId: "optional-device-id",echoCancellation: true,noiseSuppression: true,autoGainControl: true,},});
| 属性 | 类型 | 描述 |
|---|---|---|
| deviceId | string | 指定麦克风设备 ID。 |
| echoCancellation | boolean | 启用回声消除。 |
| noiseSuppression | boolean | 启用降噪。 |
| autoGainControl | boolean | 启用自动增益控制。 |
手动音频选项
传入 audioFormat 和 sampleRate,通过 connection.send() 手动发送音频数据。
import { AudioFormat } from "@elevenlabs/client";const connection = Scribe.connect({token,modelId: "scribe_v2_realtime",audioFormat: AudioFormat.PCM_16000,sampleRate: 16000,});
| 属性 | 类型 | 描述 |
|---|---|---|
| audioFormat | AudioFormat | 音频编码格式(例如 AudioFormat.PCM_16000)。 |
| sampleRate | number | 采样率,单位为 Hz。必须与 audioFormat 匹配。 |
AudioFormat 枚举
enum AudioFormat {PCM_8000 = "pcm_8000",PCM_16000 = "pcm_16000",PCM_22050 = "pcm_22050",PCM_24000 = "pcm_24000",PCM_44100 = "pcm_44100",PCM_48000 = "pcm_48000",ULAW_8000 = "ulaw_8000",}
麦克风模式
直接从用户麦克风流式传输音频:
import { Scribe, RealtimeEvents } from "@elevenlabs/client";async function transcribeFromMicrophone() {const token = await fetchToken();const connection = Scribe.connect({token,modelId: "scribe_v2_realtime",microphone: {echoCancellation: true,noiseSuppression: true,autoGainControl: true,},});connection.on(RealtimeEvents.PARTIAL_TRANSCRIPT, (data) => {document.getElementById("live").textContent = data.text;});connection.on(RealtimeEvents.COMMITTED_TRANSCRIPT, (data) => {const el = document.createElement("p");el.textContent = data.text;document.getElementById("transcripts").appendChild(el);document.getElementById("live").textContent = "";});document.getElementById("stop").addEventListener("click", () => {connection.close();});}
手动音频模式(文件转写)
通过手动发送音频数据来转写预录音频文件:
import { Scribe, RealtimeEvents, AudioFormat } from "@elevenlabs/client";async function transcribeFile(file) {const token = await fetchToken();const connection = Scribe.connect({token,modelId: "scribe_v2_realtime",audioFormat: AudioFormat.PCM_16000,sampleRate: 16000,});connection.on(RealtimeEvents.COMMITTED_TRANSCRIPT, (data) => {console.log("Transcript:", data.text);});// Decode audio fileconst arrayBuffer = await file.arrayBuffer();const audioContext = new AudioContext({ sampleRate: 16000 });const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);// Convert to PCM16const channelData = audioBuffer.getChannelData(0);const pcmData = new Int16Array(channelData.length);for (let i = 0; i < channelData.length; i++) {const sample = Math.max(-1, Math.min(1, channelData[i]));pcmData[i] = sample < 0 ? sample * 32768 : sample * 32767;}// Send in chunksconst chunkSize = 4096;for (let offset = 0; offset < pcmData.length; offset += chunkSize) {const chunk = pcmData.slice(offset, offset + chunkSize);const bytes = new Uint8Array(chunk.buffer);const base64 = btoa(String.fromCharCode(...bytes));connection.send({ audioBase64: base64 });await new Promise((resolve) => setTimeout(resolve, 50));}// Commit and closeconnection.commit();}
RealtimeConnection
Scribe.connect() 返回一个 RealtimeConnection 实例,包含以下方法。
on(event, listener)
注册事件监听器。可用事件类型请参阅事件。
connection.on(RealtimeEvents.COMMITTED_TRANSCRIPT, (data) => {console.log("Committed:", data.text);});
off(event, listener)
移除之前注册的事件监听器。
const handler = (data) => console.log(data.text);connection.on(RealtimeEvents.COMMITTED_TRANSCRIPT, handler);// Laterconnection.off(RealtimeEvents.COMMITTED_TRANSCRIPT, handler);
send(data)
向 Scribe 发送音频数据(仅限手动音频模式)。
connection.send({audioBase64: base64AudioChunk,commit: false, // Optional: commit immediatelysampleRate: 16000, // Optional: override sample ratepreviousText: "Previous transcription text", // Optional: context from a previous transcription});
previousText 字段只能在会话的第一个音频块中发送。在后续
音频块中发送会导致错误。
commit()
手动提交当前转写结果。仅在使用 CommitStrategy.MANUAL 时需要。
connection.commit();
close()
关闭 WebSocket 连接并清理资源(麦克风流、音频上下文)。
connection.close();
事件
使用 connection.on(event, listener) 注册事件监听器。所有事件均可作为 RealtimeEvents 枚举中的常量使用。
转写事件
| 事件 | 数据 | 描述 |
|---|---|---|
| SESSION_STARTED | { session_id: string } | Scribe 会话已开始。 |
| PARTIAL_TRANSCRIPT | { text: string } | 临时转写结果。 |
| COMMITTED_TRANSCRIPT | { text: string } | 已完成的转写结果。 |
| COMMITTED_TRANSCRIPT_WITH_TIMESTAMPS | { text: string; language_code?: string; words?: WordsItem[] } | 包含词级时间信息的已完成结果。 |
WordsItem 类型包含词级时间信息:
interface WordsItem {text?: string; // Word textstart?: number; // Start time in secondsend?: number; // End time in secondstype?: "word" | "spacing"; // Token typespeaker_id?: string; // Speaker identifier}
连接事件
| 事件 | 数据 | 描述 |
|---|---|---|
| OPEN | Event | WebSocket 连接已打开。 |
| CLOSE | Event | WebSocket 连接已关闭。 |
| ERROR | Error | Event | 通用错误。 |
错误事件
所有错误事件均会接收 { error: string }。
| 事件 | 描述 |
|---|---|
| AUTH_ERROR | 身份验证错误。 |
| QUOTA_EXCEEDED | 已超出使用配额。 |
| COMMIT_THROTTLED | 提交请求受到限流。 |
| TRANSCRIBER_ERROR | 转写引擎错误。 |
| UNACCEPTED_TERMS | 尚未接受服务条款。 |
| RATE_LIMITED | 已被限流。 |
| INPUT_ERROR | 输入格式无效。 |
| QUEUE_OVERFLOW | 处理队列已满。 |
| RESOURCE_EXHAUSTED | 服务器资源已满。 |
| SESSION_TIME_LIMIT_EXCEEDED | 已达到最长会话时长。 |
| CHUNK_SIZE_EXCEEDED | 音频块过大。 |
| INSUFFICIENT_AUDIO_ACTIVITY | 音频活动不足,无法维持连接。 |
提交策略
控制何时提交转写结果:
import { Scribe, CommitStrategy } from '@elevenlabs/client';// Manual (default): you control when to commitconst connection = Scribe.connect({token,modelId: 'scribe_v2_realtime',audioFormat: AudioFormat.PCM_16000,sampleRate: 16000,commitStrategy: CommitStrategy.MANUAL,});// Send audio, then commit when readyconnection.send({ audioBase64: chunk });connection.commit();// Voice Activity Detection: Scribe detects silences and commits automaticallyconst connection = Scribe.connect({token,modelId: 'scribe_v2_realtime',microphone: { echoCancellation: true },commitStrategy: CommitStrategy.VAD,});
更多详情请参阅转写文本和提交策略。
完整示例
以下完整示例使用基于 VAD 的提交策略转写麦克风音频:
import { Scribe, RealtimeEvents, CommitStrategy } from "@elevenlabs/client";async function startTranscription() {const token = await fetchToken();const connection = Scribe.connect({token,modelId: "scribe_v2_realtime",commitStrategy: CommitStrategy.VAD,microphone: {echoCancellation: true,noiseSuppression: true,},});connection.on(RealtimeEvents.SESSION_STARTED, (data) => {console.log("Session started:", data.session_id);});connection.on(RealtimeEvents.PARTIAL_TRANSCRIPT, (data) => {document.getElementById("live").textContent = data.text;});connection.on(RealtimeEvents.COMMITTED_TRANSCRIPT, (data) => {const el = document.createElement("p");el.textContent = data.text;document.getElementById("transcripts").appendChild(el);document.getElementById("live").textContent = "";});connection.on(RealtimeEvents.ERROR, (error) => {console.error("Scribe error:", error);});// Stop buttondocument.getElementById("stop").addEventListener("click", () => {connection.close();});}document.getElementById("start").addEventListener("click", startTranscription);