訊飛語音合成(流式版)語音專業版高質量的分析

一、引言

在現代的 Web 應用開發中,語音合成技術為用戶提供了更加便捷和人性化的交互體驗。訊飛語音合成(流式版)以其高效、穩定的性能,成為了眾多開發者的首選。本文將詳細介紹在 Home.vue 文件中實現訊飛語音合成(流式版)的開發邏輯、涉及的代碼以及相關的環境配置,幫助大家更好地掌握這一技術。
在這里插入圖片描述

二、開發環境配置

在這里插入圖片描述

在 Home.vue 文件中,我們需要引入并配置訊飛語音合成的相關參數。在代碼中,我們可以看到如下配置:
2-1, sparkCOnfig.js

// 訊飛星火大模型WebSocket配置
const getSparkConfig = () => {const config = {APPID: '6acb09d5',APISecret: 'MmNhN2VkY2JkMjQyODYyNzBhZDVhYjgz',APIKey: '36fb21a7095db0bb6ff2ac928e14a8e7',host: 'spark-api.xf-yun.com',path: '/v3.1/chat',};// 驗證配置參數的有效性const validateConfig = () => {const requiredFields = ['APPID', 'APISecret', 'APIKey', 'host', 'path'];for (const field of requiredFields) {if (!config[field]) {throw new Error(`缺少必要的配置參數: ${field}`);}}};// 獲取完整的WebSocket URLconst getWebSocketUrl = () => {validateConfig();return `wss://${config.host}${config.path}`;};return {...config,getWebSocketUrl,};
};export default getSparkConfig;

2-2, Home.vue

import TTSRecorder from '../utils/voice/onlineTTS'
const ttsConfig = {app_id: '6acb09d5',api_secret: 'MmNhN2VkY2Jk*',api_key: '36fb21a7095*'
};
TTSRecorder.init(ttsConfig);

這里的 app_id 、 api_secret 和 api_key 是我們在訊飛開放平臺申請的密鑰,用于身份驗證和訪問控制。通過調用 TTSRecorder.init(ttsConfig) 方法,我們將這些配置信息傳遞給 TTSRecorder 實例,以便后續使用。

三、開發邏輯分析

3.1 初始化階段

在 Home.vue 的 onMounted 生命周期鉤子中,我們首先調用 fetchWelcomeMessage 方法獲取歡迎消息。在獲取到消息后,我們初始化 3D 場景,并連接 WebSocket 服務。同時,我們還調用 ttsplaybtn 方法對歡迎消息進行語音播報:

onMounted(() => {fetchWelcomeMessage().then(() => {initGlbScene(container.value, objpath.value, objpath_create_time.value);animate();window.addEventListener('resize', handleResize);connectWebSocket();nextTick(() => {setTimeout(() => {ttsplaybtn(response.data.data.welcome, 0);}, 3000);});});scrollToBottom();
});

### 3.2 語音播放功能實現
ttsplaybtn 方法是實現語音播放的核心函數。在這個方法中,我們根據當前的播放狀態進行不同的處理:```vue
const ttsplaybtn = async (content, index) => {try {console.log('準備播放文本,當前狀態:', { content, index, isPlaying: isPlaying.value, currentPlayingIndex: currentPlayingIndex.value });if (isPlaying.value && currentPlayingIndex.value === index) {ttsRecorder.value.stop();stopAnimations();isPlaying.value = false;currentPlayingIndex.value = null;return;}if (ttsRecorder.value && isPlaying.value) {ttsRecorder.value.stop();stopAnimations();isPlaying.value = false;currentPlayingIndex.value = null;}ttsRecorder.value = new TTSRecorder({voiceName: 'xiaoyan',tte: 'UTF8',text: content,onEnd: () => {console.log('音頻播放結束');isPlaying.value = false;currentPlayingIndex.value = null;stopAnimations();}});currentPlayingIndex.value = index;isPlaying.value = true;console.log('開始播放文本:', content);startAnimations();await ttsRecorder.value.start();} catch (error) {console.error('語音播放出錯:', error);isPlaying.value = false;currentPlayingIndex.value = null;stopAnimations();ElMessage.error('語音播放失敗: ' + error.message);}
};

當用戶點擊播放按鈕時,我們首先檢查當前是否有其他消息正在播放。如果有,則停止當前播放的消息。然后,我們創建一個新的 TTSRecorder 實例,并傳入要播放的文本和相關配置。最后,我們調用 start 方法開始語音播放。sparkChat.js

// 訊飛星火大模型WebSocket通信模塊
import axios from ‘axios’
import getSparkConfig from ‘…/sparkConfig’

class SparkChatService {
constructor(callbacks) {
this.websocket = null
this.isReconnecting = false
this.reconnectAttempts = 0
this.MAX_RECONNECT_ATTEMPTS = 3
this.RECONNECT_INTERVAL = 2000

    // 獲取配置const sparkConfig = getSparkConfig()this.APPID = sparkConfig.APPIDthis.APISecret = sparkConfig.APISecretthis.APIKey = sparkConfig.APIKeythis.host = sparkConfig.hostthis.path = sparkConfig.paththis.sparkBaseUrl = sparkConfig.getWebSocketUrl()// 回調函數this.callbacks = callbacks || {}
}// 生成鑒權URL所需的日期
getAuthorizationDate() {return new Date().toUTCString()
}// 生成鑒權URL
async getAuthUrl() {const date = this.getAuthorizationDate()const tmp = `host: ${this.host}\ndate: ${date}\nGET ${this.path} HTTP/1.1`const encoder = new TextEncoder()const key = await window.crypto.subtle.importKey('raw',encoder.encode(this.APISecret),{ name: 'HMAC', hash: 'SHA-256' },false,['sign'])const signature = await window.crypto.subtle.sign('HMAC',key,encoder.encode(tmp))const signatureBase64 = btoa(String.fromCharCode(...new Uint8Array(signature)))const authorization_origin = `api_key="${this.APIKey}", algorithm="hmac-sha256", headers="host date request-line", signature="${signatureBase64}"`const authorization = btoa(authorization_origin)return `${this.sparkBaseUrl}?authorization=${encodeURIComponent(authorization)}&date=${encodeURIComponent(date)}&host=${encodeURIComponent(this.host)}`
}// 檢查WebSocket連接狀態
checkWebSocketConnection() {return this.websocket && this.websocket.readyState === WebSocket.OPEN
}// 重連WebSocket
async reconnectWebSocket() {if (this.isReconnecting || this.reconnectAttempts >= this.MAX_RECONNECT_ATTEMPTS) returnthis.isReconnecting = truethis.reconnectAttempts++console.log(`嘗試重新連接WebSocket (第${this.reconnectAttempts}次)...`)try {await this.connect()this.isReconnecting = falsethis.reconnectAttempts = 0console.log('WebSocket重連成功')} catch (error) {console.error('WebSocket重連失敗:', error)this.isReconnecting = falseif (this.reconnectAttempts < this.MAX_RECONNECT_ATTEMPTS) {setTimeout(() => this.reconnectWebSocket(), this.RECONNECT_INTERVAL)} else {console.error('WebSocket重連次數達到上限')this.callbacks.onError?.('網絡連接異常,請刷新頁面重試')}}
}// 建立WebSocket連接
async connect() {try {const url = await this.getAuthUrl()this.websocket = new WebSocket(url)this.websocket.onopen = () => {console.log('WebSocket連接已建立')this.isReconnecting = falsethis.reconnectAttempts = 0this.callbacks.onOpen?.()}this.websocket.onmessage = (event) => {const response = JSON.parse(event.data)if (response.header.code === 0) {if (response.payload.choices.text[0].content) {const content = response.payload.choices.text[0].content.replace(/\r?\n/g, '')this.callbacks.onMessage?.(content)}if (response.header.status === 2) {this.callbacks.onComplete?.()}} else {this.callbacks.onError?.(`抱歉,發生錯誤:${response.header.message}`)}}this.websocket.onerror = (error) => {console.error('WebSocket錯誤:', error)if (!this.isReconnecting) {this.reconnectWebSocket()}this.callbacks.onError?.(error)}this.websocket.onclose = () => {console.log('WebSocket連接已關閉')if (!this.isReconnecting) {this.reconnectWebSocket()}this.callbacks.onClose?.()}} catch (error) {console.error('連接WebSocket失敗:', error)throw error}
}// 發送消息
async sendMessage(message) {if (!this.checkWebSocketConnection()) {try {await this.reconnectWebSocket()} catch (error) {console.error('重連失敗,無法發送消息')throw new Error('網絡連接異常,請稍后重試')}}const requestData = {header: {app_id: this.APPID,uid: 'user1'},parameter: {chat: {domain: 'generalv3',temperature: 0.5,max_tokens: 4096}},payload: {message: {text: [{ role: 'user', content: message }]}}}try {this.websocket.send(JSON.stringify(requestData))} catch (error) {console.error('發送消息失敗:', error)throw new Error('發送消息失敗,請重試')}
}// 關閉連接
close() {if (this.websocket) {this.websocket.close()}
}

}

export default SparkChatService
···

3.3 播放狀態管理

為了實現播放和暫停功能的切換,我們使用 isPlaying 和 currentPlayingIndex 兩個響應式變量來管理播放狀態。在 ttsplaybtn 方法中,我們根據這兩個變量的值來判斷當前的播放狀態,并進行相應的處理。同時,在模板中,我們根據 isPlaying 的值來顯示不同的圖標:

<svg class="input-icon tts-btn" viewBox="0 0 24 24" aria-label="播報語音圖標" @click="ttsplaybtn(message.content, index)"><path v-if="currentPlayingIndex === index && isPlaying" d="M6 4h4v16H6zM14 4h4v16h-4z" fill="currentColor"/><path v-else d="M12 3c-4.97 0-9 4.03-9 9v7c0 1.66 1.34 3 3 3h3v-8H5v-2c0-3.87 3.13-7 7-7s7 3.13 7 7v2h-4v8h3c1.66 0 3-1.34 3-3v-7c0-4.97-4.03-9-9-9z" fill="currentColor"/>
</svg>

## 四、文件結構和代碼分析
### 4.1 文件結構
在項目中,與訊飛語音合成相關的文件主要包括:- Home.vue :主頁面文件,包含語音播放按鈕和相關邏輯。
- TTSRecorder.js :封裝了訊飛語音合成的核心功能,如初始化、開始播放、停止播放等。
- sparkConfig.js :配置文件,包含訊飛語音合成的 API 地址和密鑰信息。
### 4.2 代碼分析
- Home.vue :在這個文件中,我們主要處理用戶的交互事件,如點擊播放按鈕、停止播放等。同時,我們還管理播放狀態,并根據狀態更新界面。
- TTSRecorder.js :這個文件封裝了與訊飛語音合成服務的交互邏輯。它接收配置信息,并提供了 start 和 stop 方法來控制語音播放。
- sparkConfig.js :該文件存儲了訊飛語音合成的 API 地址和密鑰信息,確保我們能夠正確地與服務進行通信。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/74655.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/74655.shtml
英文地址,請注明出處:http://en.pswp.cn/web/74655.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

走進未來的交互世界:下一代HMI設計趨勢解析

在科技日新月異的今天&#xff0c;人機交互界面&#xff08;HMI&#xff09;設計正以前所未有的速度發展&#xff0c;不斷引領著未來的交互世界。從簡單的按鈕和圖標&#xff0c;到如今的智能助手和虛擬現實&#xff0c;HMI設計不僅改變了我們的生活方式&#xff0c;還深刻影響…

洛谷題單3-P1217 [USACO1.5] 回文質數 Prime Palindromes-python-流程圖重構

題目描述 因為 151 151 151 既是一個質數又是一個回文數&#xff08;從左到右和從右到左是看一樣的&#xff09;&#xff0c;所以 151 151 151 是回文質數。 寫一個程序來找出范圍 [ a , b ] ( 5 ≤ a < b ≤ 100 , 000 , 000 ) [a,b] (5 \le a < b \le 100,000,000…

學習筆記,DbContext context 對象是保存了所有用戶對象嗎

DbContext 并不會將所有用戶對象保存在內存中&#xff1a; DbContext 是 Entity Framework Core (EF Core) 的數據庫上下文&#xff0c;它是一個數據庫訪問的抽象層它實際上是與數據庫的一個連接會話&#xff0c;而不是數據的內存緩存當您通過 _context.Users 查詢數據時&…

本地命令行啟動服務并連接MySQL8

啟動服務命令 net start mysql8 關閉服務命令 net stop mysql8 本地連接MySQL數據庫mysql -u [用戶名] -p[密碼] 這里&#xff0c;我遇到了個問題 —— 啟動、關閉服務時&#xff0c;顯示 “發生系統錯誤 5。拒絕訪問。 ” 解法1&#xff1a;在 Windows 上以管理員身份打開…

數據蒸餾:Dataset Distillation by Matching Training Trajectories 論文翻譯和理解

一、TL&#xff1b;DR 數據集蒸餾的任務是合成一個較小的數據集&#xff0c;使得在該合成數據集上訓練的模型能夠達到在完整數據集上訓練的模型相同的測試準確率&#xff0c;號稱優于coreset的選擇方法本文中&#xff0c;對于給定的網絡&#xff0c;我們在蒸餾數據上對其進行幾…

【spring cloud Netflix】Ribbon組件

1.基本概念 SpringCloud Ribbon是基于Netflix Ribbon 實現的一套客戶端負載均衡的工具。簡單的說&#xff0c;Ribbon 是 Netflix 發布的開源項目&#xff0c;主要功能是提供客戶端的軟件負載均衡算法&#xff0c;將 Netflix 的中間層服務連接在一 起。Ribbon 的客戶端組件提供…

P1036 [NOIP 2002 普及組] 選數(DFS)

題目描述 已知 n 個整數 x1?,x2?,?,xn?&#xff0c;以及 1 個整數 k&#xff08;k<n&#xff09;。從 n 個整數中任選 k 個整數相加&#xff0c;可分別得到一系列的和。例如當 n4&#xff0c;k3&#xff0c;4 個整數分別為 3,7,12,19 時&#xff0c;可得全部的組合與它…

在響應式網頁的開發中使用固定布局、流式布局、彈性布局哪種更好

一、首先看下固定布局與流體布局的區別 &#xff08;一&#xff09;固定布局 固定布局的網頁有一個固定寬度的容器&#xff0c;內部組件寬度可以是固定像素值或百分比。其容器元素不會移動&#xff0c;無論訪客屏幕分辨率如何&#xff0c;看到的網頁寬度都相同。現代網頁設計…

二分查找與二叉樹中序遍歷——面試算法

目錄 二分查找與分治 循環方式 遞歸方式 元素中有重復的二分查找 基于二分查找的拓展問題 山脈數組的頂峰索引——局部有序 旋轉數字中的最小數字 找缺失數字 優化平方根 中序與搜索樹 二叉搜索樹中搜索特定值 驗證二叉搜索樹 有序數組轉化為二叉搜索樹 尋找兩個…

字符串——面試考察高頻算法題

目錄 轉換成小寫字母 字符串轉化為整數 反轉相關的問題 反轉字符串 k個一組反轉 僅僅反轉字母 反轉字符串里的單詞 驗證回文串 判斷是否互為字符重排 最長公共前綴 字符串壓縮問題 轉換成小寫字母 給你一個字符串 s &#xff0c;將該字符串中的大寫字母轉換成相同的…

現代復古電影海報品牌徽標設計襯線英文字體安裝包 Thick – Retro Vintage Cinematic Font

Thick 是一種大膽的復古字體&#xff0c;專為有影響力的標題和懷舊的視覺效果而設計。其厚實的字體、復古魅力和電影風格使其成為電影海報、產品標簽、活動品牌和編輯設計的理想選擇。無論您是在引導電影的黃金時代&#xff0c;還是在現代布局中注入復古活力&#xff0c;Thick …

[C++面試] new、delete相關面試點

一、入門 1、說說new與malloc的基本用途 int* p1 (int*)malloc(sizeof(int)); // C風格 int* p2 new int(10); // C風格&#xff0c;初始化為10 new 是 C 中的運算符&#xff0c;用于在堆上動態分配內存并調用對象的構造函數&#xff0c;會自動計算所需內存…

Unity URP管線與HDRP管線對比

1. 渲染架構與底層技術 URP 渲染路徑&#xff1a; 前向渲染&#xff08;Forward&#xff09;&#xff1a;默認單Pass前向&#xff0c;支持少量實時光源&#xff08;通常4-8個逐物體&#xff09;。 延遲渲染&#xff08;Deferred&#xff09;&#xff1a;可選但功能簡化&#…

JDK8卸載與安裝教程(超詳細)

JDK8卸載與安裝教程&#xff08;超詳細&#xff09; 最近學習一個項目&#xff0c;需要使用更高級的JDK&#xff0c;這里記錄一下卸載舊版本與安裝新版本JDK的過程。 JDK8卸載 以windows10操作系統為例&#xff0c;使用快捷鍵winR輸入cmd&#xff0c;打開控制臺窗口&#xf…

python爬蟲:DrissionPage實戰教程

如果本文章看不懂可以看看上一篇文章&#xff0c;加強自己的基礎&#xff1a;爬蟲自動化工具&#xff1a;DrissionPage-CSDN博客 案例解析&#xff1a; 前提&#xff1a;我們以ChromiumPage為主&#xff0c;寫代碼工具使用Pycharm&#xff08;python環境3.9-3.10&#xff09; …

07-01-自考數據結構(20331)- 排序-內部排序知識點

內部排序算法是數據結構核心內容,主要包括插入類(直接插入、希爾)、交換類(冒泡、快速)、選擇類(簡單選擇、堆)、歸并和基數五大類排序方法。 知識拓撲 知識點介紹 直接插入排序 定義:將每個待排序元素插入到已排序序列的適當位置 算法步驟: 從第二個元素開始遍歷…

Go語言-初學者日記(八):構建、部署與 Docker 化

&#x1f9f1; 一、go build&#xff1a;最基礎的構建方式 Go 的構建工具鏈是出了名的輕量、簡潔&#xff0c;直接用 go build 就能把項目編譯成二進制文件。 ? 構建當前項目 go build -o myapp-o myapp 指定輸出文件名默認會構建當前目錄下的 main.go 或 package main &a…

教程:如何使用 JSON 合并腳本

目錄 1. 介紹 2. 使用方法 3. 注意事項 4. 示例 5.完整代碼 1. 介紹 該腳本用于將多個 COCO 格式的 JSON 標注文件合并為一個 JSON 文件。COCO 格式常用于目標檢測和圖像分割任務&#xff0c;包含以下三個主要部分&#xff1a; "images"&#xff1a;圖像信息&a…

Java學習總結-緩沖流性能分析

測試用例&#xff1a; 分別使用原始的字節流&#xff0c;以及字節緩沖流復制一個很大的視頻。 測試步驟&#xff1a; 在這個分析性能需要一個記錄時間的工具&#xff1a;這個是記錄1970-1-1 00&#xff1a;00&#xff1a;00到現在的總毫秒值。 long start System.currentT…

流影---開源網絡流量分析平臺(五)(成果展示)

目錄 前沿 攻擊過程 前沿 前四章我們已經成功安裝了流影的各個功能&#xff0c;那么接下來我們就看看這個開源工具的實力&#xff0c;本實驗將進行多個攻擊手段&#xff08;ip掃描&#xff0c;端口掃描&#xff0c;sql注入&#xff09;攻擊靶機&#xff0c;來看看流影的態感效…