通过火山云API来实现:流式大模型语音对话

这里我们需要在火山云语音控制台开通大模型的流式语音对话、获取豆包模型的apiKey,开通语音合成项目。


这里使用的豆包模型是Doubao-lite,延迟会更低一些
配置说明
这里一共有四个文件,分别是主要的fastAPI、LLM、STT、文件
TTS中需要配置

appid = "123"   #填写控制台的APPID
token = "XXXX"  #填写控制台上的Access Token
cluster = "XXXXX"  #填写语音生成的组id
voice_type = "BV034_streaming"   #这里是生成声音的类型选择

host = "openspeech.bytedance.com"  #无需更改
api_url = f"wss://{host}/api/v1/tts/ws_binary" #无需更改

LLM中配置

 # 初始化客户端,传入 API 密钥
   self.client = Ark(api_key="XXXX")

在STT的146行中配置

header = {
            "X-Api-Resource-Id": "volc.bigasr.sauc.duration",
            "X-Api-Access-Key": "XXXXX",  #和TTS配置内容相同
            "X-Api-App-Key": "123",  #和TTS配置内容相同
            "X-Api-Request-Id": reqid
        }

 还有前端HTML的配置中记得根据自己服务的所在ip更改配置
 

前端测试html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>WebSocket 音频传输测试</title>
    <style>
        body {
            font-family: Arial, sans-serif;
        }
        #status {
            margin-bottom: 10px;
        }
        #messages {
            border: 1px solid #ccc;
            height: 200px;
            overflow-y: scroll;
            padding: 10px;
        }
        #controls {
            margin-top: 10px;
        }
        #controls button {
            margin-right: 5px;
        }
        #latency {
            margin-top: 10px;
            font-weight: bold;
        }
    </style>
</head>
<body>

<h1>WebSocket 音频传输测试</h1>

<!-- 显示当前连接状态 -->
<div id="status">状态:未连接</div>

<!-- 显示日志消息 -->
<div id="messages"></div>

<!-- 控制按钮 -->
<div id="controls">
    <button id="startButton">开始录音并发送</button>
    <button id="stopButton" disabled>停止录音</button>
</div>

<!-- 延迟显示区域 -->
<div id="latency"></div>

<script>
    // WebSocket 服务器地址,请根据实际情况替换
    const wsUrl = 'ws://127.0.0.1:8000/ws';

    // 全局变量
    let socket = null; // WebSocket 实例
    const messagesDiv = document.getElementById('messages'); // 日志消息显示区域
    const statusDiv = document.getElementById('status'); // 连接状态显示区域
    const startButton = document.getElementById('startButton'); // 开始录音按钮
    const stopButton = document.getElementById('stopButton'); // 停止录音按钮
    let recordingAudioContext; // 音频录制上下文
    let audioInput; // 音频输入节点
    let processor; // 音频处理节点

    // 播放相关变量
    let playbackAudioContext;
    let playbackQueue = [];
    let playbackTime = 0;
    let isPlaying = false;

    // 延迟测量变量
    let overSentTime = null; // 记录发送 'over' 的时间
    let latencyMeasured = false; // 标记是否已经测量延迟

    /**
     * 向日志区域添加消息
     * @param {string} message - 要记录的消息
     */
    function logMessage(message) {
        const p = document.createElement('p');
        p.textContent = message;
        messagesDiv.appendChild(p);
        messagesDiv.scrollTop = messagesDiv.scrollHeight; // 自动滚动到最新消息
    }

    /**
     * 初始化Playback AudioContext
     */
    function initializePlayback() {
        playbackAudioContext = new (window.AudioContext || window.webkitAudioContext)();
        logMessage('Playback AudioContext 已创建');
    }

    /**
     * 解码并添加到播放队列
     * @param {ArrayBuffer} data - 接收到的音频数据
     */
    function appendToPlaybackQueue(data) {
        playbackAudioContext.decodeAudioData(data, (audioBuffer) => {
            playbackQueue.push(audioBuffer);
            schedulePlayback();
        }, (error) => {
            logMessage('解码音频数据时出错:' + error);
        });
    }

    /**
     * 调度播放队列中的音频缓冲区
     */
    function schedulePlayback() {
        if (isPlaying) return;
        if (playbackQueue.length === 0) return;

        // 获取下一个缓冲区
        const buffer = playbackQueue.shift();

        // 创建一个缓冲源
        const source = playbackAudioContext.createBufferSource();
        source.buffer = buffer;
        source.connect(playbackAudioContext.destination);

        // 如果 playbackTime 小于当前时间,则更新为当前时间
        if (playbackTime < playbackAudioContext.currentTime) {
            playbackTime = playbackAudioContext.currentTime;
        }

        // 计划在 playbackTime 播放
        source.start(playbackTime);
        logMessage(`Scheduled buffer to play at ${playbackTime.toFixed(2)}s`);

        // 更新 playbackTime
        playbackTime += buffer.duration;

        // 标记为正在播放
        isPlaying = true;

        // 当缓冲源播放结束时
        source.onended = () => {
            isPlaying = false;
            // 继续播放队列中的下一个缓冲区
            schedulePlayback();
        };
    }

    /**
     * 创建并连接 WebSocket
     */
    function createWebSocket() {
        if (socket !== null && (socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING)) {
            logMessage('WebSocket 已经连接或正在连接中');
            return;
        }

        socket = new WebSocket(wsUrl);
        socket.binaryType = 'arraybuffer';

        socket.onopen = function () {
            statusDiv.textContent = '状态:已连接';
            logMessage('WebSocket 连接已打开');
            startButton.disabled = false; // 启用开始录音按钮
        };

        socket.onmessage = function (event) {
            // 如果接收到的是字符串且内容为 'over'
            if (typeof event.data === 'string' && event.data === 'over') {
                logMessage('收到结束信号: over');
                // 标记 MediaSource 结束
                return;
            }

            // 如果接收到的是二进制数据(ArrayBuffer)
            if (event.data instanceof ArrayBuffer) {
                logMessage('接收到音频数据');

                // 检查是否已经发送 'over' 并且尚未测量延迟
                if (overSentTime !== null && !latencyMeasured) {
                    let receiveTime = performance.now();
                    let latency = receiveTime - overSentTime;
                    logMessage(`延迟时间:${latency.toFixed(2)} 毫秒`);
                    document.getElementById('latency').textContent = `延迟时间:${latency.toFixed(2)} 毫秒`;
                    latencyMeasured = true; // 标记为已测量
                }

                appendToPlayback
### 火山引擎流式语音合成使用方法 #### 请求地址与认证方式 对于希望利用火山引擎提供的流式语音合成功能的开发者而言,请求应发送至特定主机。HOST设定为`https://ark.cn-beijing.volces.com`[^1]。为了验证身份并获得访问权限,在HTTP头部需加入`Authorization: Bearer YOUR_API_KEY`字段,其中YOUR_API_KEY应当替换为实际取得的有效API密钥[^2]。 #### HTTP请求结构体实例 下面展示了一个通过命令行工具curl发起POST请求的例子,用于触发一次完整的对话完成操作: ```bash curl -i -k -X POST 'https://ark.cn-beijing.volces.com/v1/chat/completions' \ --header 'Authorization: Bearer YOUR_API_KEY' \ --header 'Content-Type: application/json' \ --data '{"model":"Doubao-pro-4k","messages":[{"role":"user","content":"你好"}]}' ``` 请注意上述代码中的URL已经调整为适用于火山引擎服务的端点,并且在数据载荷里指定了具体使用的模型名称(此处选用的是"Doubao-pro-4k")。此段落描述了如何构建一个基本的HTTP请求来调用该平台上的AI聊天接口。 #### 多语言支持特性 考虑到全球化应用的需求,当前先进的TTS(Text To Speech)技术可以很好地兼容多种自然语言输入,不仅限于普通话和英语,还包括但不限于法语、德语以及其他欧洲主要语言连同它们的地方变种形式。这使得基于此类技术支持的应用程序能够更好地服务于国际化的用户群体[^3]。 #### 性能优势概述 相较于市场上现有的解决方案,本方案特别强调其卓越的文字解析能力和逻辑推演技巧;特别是在处理较长篇幅的文章摘要以及编程脚本创作方面展现出了明显的技术领先水平,某些场景下的表现甚至优于知名竞品GPT-4系列版本[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weighless1129

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值