You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用ElevenLabs WebSocket向Twilio流音频仅播放白噪音问题排查

问题分析与修正方案

以下是你代码中存在的关键问题及对应修正:

1. 核心问题:重复发送累计音频数据

你当前每次向Twilio发送的是从开始到当前的所有音频chunk的base64拼接内容,而非仅当前新生成的chunk。Twilio Media Stream会将每次收到的media.payload追加到播放队列,导致前面的音频被重复播放、叠加,最终产生类似白噪音的混乱音效。

修正代码(send_to_twilio_eleven函数)

async def send_to_twilio_eleven(websocket, message, stream_sid, interruption_event):
    print("Starting TTS streaming from ElevenLabs")
    try:
        start_time = time.time()
        print("message is: ", message)
        
        async for audio_chunk in text_to_speech_stream(message):
            print("Chunk yielded in : ",time.time()-start_time)
            
            # 仅发送当前新生成的chunk,而非累计内容
            audio_delta = {
                "event": "media",
                "streamSid": stream_sid,
                "media": {
                    "payload": audio_chunk
                }
            }

            if interruption_event.is_set():
                print("Interruption detected")
                return

            await websocket.send_json(audio_delta)
            print("received audio sent to twilio")
            await asyncio.sleep(0)

    except Exception as e:
        print(f"Error in send_to_twilio_eleven: {e}")

2. 次要问题:冗余配置与逻辑混乱

(1)WebSocket payload冗余字段

ElevenLabs WebSocket的认证密钥xi-api-key只需通过请求头传递,无需放入payload;model_id和voice_id已在URL中指定,也无需重复写入payload。

(2)重复JSON解析逻辑

原代码中对WebSocket消息做了两次JSON解析,逻辑冗余且可能导致错误信息无法被正确捕获。

修正代码(text_to_speech_stream函数)

async def text_to_speech_stream(text: str):
    voice_id = "pNInz6obpgDQGcFmaJgB"
    model_id = "eleven_multilingual_v2"

    url = f"wss://api.elevenlabs.io/v1/text-to-speech/{voice_id}/stream-input?model_id={model_id}"

    headers = {
        'xi-api-key': "你的实际API密钥"  # 填入有效密钥
    }

    payload = {
        "text": " ",
        "voice_settings": {
            "stability": 0.0,
            "similarity_boost": 1.0,
            "style": 0.0,
            "use_speaker_boost": True
        },
        "output_format": "ulaw_8000",
        "flush": True
    }

    async with websockets.connect(url, extra_headers=headers) as ws:
        print("websocket connected: 11labs")
        await ws.send(json.dumps(payload))
        await ws.send(json.dumps({"text": text}))
        await ws.send(json.dumps({"text": ""}))

        start_time = time.time()
        print("waiting for receiving")

        while True:
            try:
                message = await ws.recv()
                if not isinstance(message, str):
                    print("Unknown message type received (not JSON)")
                    continue
                
                data = json.loads(message)
                if data.get("audio"):
                    print("yielded in: ", time.time()-start_time, flush=True)
                    yield data["audio"]
                elif data.get('isFinal'):
                    print("Received final signal, closing stream")
                    break
                elif data.get("warning"):
                    print(f"Warning: {data['warning']}")
                elif data.get("error"):
                    print(f"Error: {data['error']}")
                    break
                else:
                    print(f"Message from ElevenLabs: {data}")
            except websockets.exceptions.ConnectionClosedOK:
                print("Connection closed gracefully")
                break
            except json.JSONDecodeError:
                print(f"Failed to parse JSON message: {message}")
            except Exception as e:
                print(f"Error in ElevenLabs WebSocket: {e}")
                break

3. 额外验证建议

如果修正后仍有问题,可将ElevenLabs返回的audio字段base64解码为字节,手动添加ULAW格式的WAV头后保存为文件,验证音频本身是否正常。若保存的文件播放正常,则问题已完全解决;若仍为白噪音,需确认ElevenLabs的output_format参数是否生效(可尝试更换为pcm_8000后自行转ULAW编码)。

内容的提问来源于stack exchange,提问作者Dawny33

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 10:07:03