2024-12-01百度TTS語音

百度TTS語音

個(gè)人嘗試代碼倉庫:https://gitee.com/enzoism/chrome_tampermonkey

鳴謝:感謝每一位無私奉獻(xiàn)的傳道者,在此不一一具名!


1-學(xué)習(xí)目標(biāo)

  • 1)了解百度TTS語音
  • 2)使用百度TTS語音

2-參考網(wǎng)址


3-執(zhí)行過程記錄

1-TTS市場現(xiàn)狀

2024-12-01 目前應(yīng)該是ChatTTS的呼聲最高,但是為了不引入新的學(xué)習(xí)成本,暫時(shí)不考慮使用ChatTTS,直接使用在線API可以滿足我的需求?。ò俣鹊腡TS有太多的AI味,在這個(gè)時(shí)間節(jié)點(diǎn)上,百度可能還是沒有把重心放在這些細(xì)節(jié)上)


2-Tampermonkey

已經(jīng)完成整體的功能,可以直接復(fù)制當(dāng)前腳本到Tampermonkey,選中文字即可進(jìn)行語音播報(bào)

// ==UserScript==
// @name         Text to Speech using Baidu API
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  Convert selected text to speech using Baidu Text-to-Speech API
// @author       Your name
// @match        *://*/*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// ==/UserScript==

(function() {
    'use strict';

    // 配置信息 - 需要替換成你的百度API密鑰信息
    const API_KEY = '44kNzmCeZNnLHoUcNiGYnyI7';
    const SECRET_KEY = '7sNV1osooTqJliQB1aHB5lJKEKlLrg5f';
    let access_token = '';

    // 獲取access_token的函數(shù)
    async function getAccessToken() {
        const url = `https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=${API_KEY}&client_secret=${SECRET_KEY}`;
        
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'POST',
                url: url,
                headers: {
                    'Content-Type': 'application/json',
                    'Accept': 'application/json'
                },
                onload: function(response) {
                    if (response.status === 200) {
                        try {
                            const data = JSON.parse(response.responseText);
                            if (data.access_token) {
                                access_token = data.access_token;
                                GM_setValue('access_token', access_token);
                                resolve(access_token);
                            } else {
                                console.error('獲取access_token失敗: 返回?cái)?shù)據(jù)格式錯(cuò)誤', data);
                                reject(new Error('獲取access_token失敗: 返回?cái)?shù)據(jù)格式錯(cuò)誤'));
                            }
                        } catch (error) {
                            console.error('解析access_token響應(yīng)失敗:', error);
                            reject(error);
                        }
                    } else {
                        console.error('獲取access_token失敗:', response.status, response.responseText);
                        reject(new Error(`獲取access_token失敗: ${response.status}`));
                    }
                },
                onerror: function(error) {
                    console.error('請求access_token失敗:', error);
                    reject(error);
                }
            });
        });
    }

    // 文字轉(zhuǎn)語音的函數(shù)
    async function textToSpeech(text, params = {}) {
        if (!access_token) {
            access_token = GM_getValue('access_token') || await getAccessToken();
        }

        if (!access_token) {
            alert('無法獲取access_token,請檢查API配置');
            return;
        }

        const url = `https://tsn.baidu.com/text2audio`;
        const requestParams = new URLSearchParams({
            tex: encodeURIComponent(text),
            tok: access_token,
            cuid: 'tampermonkey_tts',
            ctp: 1,
            lan: 'zh',
            spd: params.spd || 5,
            pit: params.pit || 5,
            vol: params.vol || 5,
            per: params.per || 0,
            aue: 3
        });

        try {
            GM_xmlhttpRequest({
                method: 'GET',
                url: `${url}?${requestParams.toString()}`,
                responseType: 'blob',
                onload: function(response) {
                    if (response.status === 200) {
                        const blob = response.response;
                        const audio = new Audio(URL.createObjectURL(blob));
                        audio.play().catch(error => {
                            console.error('播放音頻失敗:', error);
                            alert('播放音頻失敗,請重試');
                        });
                    } else {
                        console.error('語音合成請求失敗:', response.status);
                        alert('語音合成失敗,請重試');
                    }
                },
                onerror: function(error) {
                    console.error('請求失敗:', error);
                    alert('請求失敗,請重試');
                }
            });
        } catch (error) {
            console.error('語音合成出錯(cuò):', error);
            alert('語音合成出錯(cuò),請重試');
        }
    }

    // 創(chuàng)建控制面板
    function createControlPanel() {
        const panel = document.createElement('div');
        panel.style.position = 'fixed';
        panel.style.bottom = '80px';
        panel.style.right = '20px';
        panel.style.zIndex = '9999';
        panel.style.backgroundColor = 'white';
        panel.style.padding = '10px';
        panel.style.border = '1px solid #ccc';
        panel.style.borderRadius = '5px';
        panel.style.display = 'none';

        // 語速控制
        const speedControl = document.createElement('div');
        speedControl.innerHTML = `
            <label>語速(0-15): 
                <input type="range" min="0" max="15" value="5" id="tts-speed">
                <span id="speed-value">5</span>
            </label>
        `;
        
        // 音量控制
        const volumeControl = document.createElement('div');
        volumeControl.innerHTML = `
            <label>音量(0-15): 
                <input type="range" min="0" max="15" value="5" id="tts-volume">
                <span id="volume-value">5</span>
            </label>
        `;

        // 音調(diào)控制
        const pitchControl = document.createElement('div');
        pitchControl.innerHTML = `
            <label>音調(diào)(0-15): 
                <input type="range" min="0" max="15" value="5" id="tts-pitch">
                <span id="pitch-value">5</span>
            </label>
        `;

        // 發(fā)音人選擇
        const personControl = document.createElement('div');
        personControl.innerHTML = `
            <label>發(fā)音人: 
                <select id="tts-person">
                    <option value="0">女聲</option>
                    <option value="1">男聲</option>
                    <option value="3">度逍遙</option>
                    <option value="4">度丫丫</option>
                </select>
            </label>
        `;

        panel.appendChild(speedControl);
        panel.appendChild(volumeControl);
        panel.appendChild(pitchControl);
        panel.appendChild(personControl);

        // 添加事件監(jiān)聽
        ['speed', 'volume', 'pitch'].forEach(param => {
            const input = panel.querySelector(`#tts-${param}`);
            const value = panel.querySelector(`#${param}-value`);
            input.addEventListener('input', () => {
                value.textContent = input.value;
                GM_setValue(`tts-${param}`, input.value);
            });
        });

        panel.querySelector('#tts-person').addEventListener('change', (e) => {
            GM_setValue('tts-person', e.target.value);
        });

        document.body.appendChild(panel);
        return panel;
    }

    // 創(chuàng)建懸浮按鈕
    function createFloatingButton() {
        const button = document.createElement('button');
        button.innerHTML = '朗讀選中文本';
        button.style.position = 'fixed';
        button.style.bottom = '20px';
        button.style.right = '20px';
        button.style.zIndex = '9999';
        button.style.padding = '10px';
        button.style.backgroundColor = '#4CAF50';
        button.style.color = 'white';
        button.style.border = 'none';
        button.style.borderRadius = '5px';
        button.style.cursor = 'pointer';

        const settingsButton = document.createElement('button');
        settingsButton.innerHTML = '??';
        settingsButton.style.position = 'fixed';
        settingsButton.style.bottom = '20px';
        settingsButton.style.right = '140px';
        settingsButton.style.zIndex = '9999';
        settingsButton.style.padding = '10px';
        settingsButton.style.backgroundColor = '#2196F3';
        settingsButton.style.color = 'white';
        settingsButton.style.border = 'none';
        settingsButton.style.borderRadius = '5px';
        settingsButton.style.cursor = 'pointer';

        const panel = createControlPanel();
        
        settingsButton.addEventListener('click', function() {
            panel.style.display = panel.style.display === 'none' ? 'block' : 'none';
        });

        button.addEventListener('click', function() {
            const selectedText = window.getSelection().toString().trim();
            if (selectedText) {
                const params = {
                    spd: parseInt(GM_getValue('tts-speed', 9)),
                    vol: parseInt(GM_getValue('tts-volume', 5)),
                    pit: parseInt(GM_getValue('tts-pitch', 5)),
                    per: parseInt(GM_getValue('tts-person', 0))
                };
                textToSpeech(selectedText, params);
            } else {
                alert('請先選擇要朗讀的文本');
            }
        });

        document.body.appendChild(button);
        document.body.appendChild(settingsButton);
    }

    // 初始化
    createFloatingButton();
})();

3-Python版本

Python3.8.5版本可運(yùn)行

import requests
import json
import base64
from urllib.parse import quote
import os
from playsound import playsound

class BaiduTTS:
    def __init__(self):
        # API配置信息
        self.API_KEY = '44kNzmCeZNnLHoUcNiGYnyI7'
        self.SECRET_KEY = '7sNV1osooTqJliQB1aHB5lJKEKlLrg5f'
        self.access_token = None
        
    def get_access_token(self):
        """獲取access_token"""
        url = f"https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id={self.API_KEY}&client_secret={self.SECRET_KEY}"
        
        response = requests.post(url)
        if response.status_code == 200:
            result = response.json()
            self.access_token = result['access_token']
            return self.access_token
        else:
            print(f"獲取access_token失敗: {response.status_code}")
            return None

    def text_to_speech(self, text, params=None):
        """文字轉(zhuǎn)語音"""
        if not self.access_token:
            self.access_token = self.get_access_token()
            
        if not self.access_token:
            print("無法獲取access_token")
            return False

        # 默認(rèn)參數(shù)
        default_params = {
            'spd': 9,  # 語速,取值0-15
            'pit': 5,  # 音調(diào),取值0-15
            'vol': 5,  # 音量,取值0-15
            'per': 0,  # 發(fā)音人,0為女聲,1為男聲,3為度逍遙,4為度丫丫
        }

        # 更新參數(shù)
        if params:
            default_params.update(params)

        url = "https://tsn.baidu.com/text2audio"
        
        params = {
            'tex': quote(text),
            'tok': self.access_token,
            'cuid': 'python_tts',
            'ctp': 1,
            'lan': 'zh',
            'aue': 3,  # mp3格式
            **default_params
        }
        
        try:
            response = requests.get(url, params=params)
            
            # 檢查是否返回音頻數(shù)據(jù)
            if response.headers['Content-Type'].startswith('audio/'):
                # 保存音頻文件
                audio_file = "output.mp3"
                with open(audio_file, 'wb') as f:
                    f.write(response.content)
                print(f"已保存音頻文件: {audio_file}")
                
                # 播放音頻
                playsound(audio_file)
                return True
            else:
                error_msg = response.json()
                print(f"轉(zhuǎn)換失敗: {error_msg}")
                return False
                
        except Exception as e:
            print(f"請求失敗: {str(e)}")
            return False

def main():
    # 使用示例
    tts = BaiduTTS()
    
    # 要轉(zhuǎn)換的文本
    text = input("請輸入要轉(zhuǎn)換的文字: ")
    
    # 可選:自定義參數(shù)
    params = {
        'spd': 9,  # 語速
        'pit': 5,  # 音調(diào)
        'vol': 5,  # 音量
        'per': 0   # 發(fā)音人
    }
    
    # 執(zhí)行轉(zhuǎn)換
    tts.text_to_speech(text, params)

if __name__ == "__main__":
    main()

4-其他知識補(bǔ)充

1-TTS發(fā)展歷史

TTS(Text-to-Speech,文本轉(zhuǎn)語音)技術(shù)的發(fā)展歷史可以追溯到20世紀(jì)50年代,經(jīng)歷了多個(gè)階段的技術(shù)進(jìn)步和創(chuàng)新。以下是TTS技術(shù)發(fā)展的主要里程碑:

1. 早期研究(20世紀(jì)50年代至70年代)

  • 早期嘗試:最早的TTS系統(tǒng)開始于20世紀(jì)50年代。研究人員嘗試通過機(jī)械設(shè)備和簡單的聲音合成器來生成語音。
  • 基礎(chǔ)研究:在20世紀(jì)60年代和70年代,主要集中在語音合成的基本原理和方法上,如共振峰合成(Formant Synthesis),通過模擬人類聲道的共振特性來生成語音。

2. 數(shù)字信號處理時(shí)代(20世紀(jì)80年代至90年代)

  • 數(shù)字信號處理技術(shù):隨著數(shù)字信號處理(DSP)技術(shù)的發(fā)展,TTS系統(tǒng)開始使用計(jì)算機(jī)來處理和生成語音。這種方法提高了語音合成的質(zhì)量和效率。
  • 規(guī)則合成器:出現(xiàn)了基于規(guī)則的合成器,這些系統(tǒng)通過一系列規(guī)則將文本轉(zhuǎn)換為語音。例如,線性預(yù)測編碼(LPC)技術(shù)被廣泛應(yīng)用于語音合成。
  • 有限狀態(tài)機(jī):有限狀態(tài)機(jī)(FSM)被用于文本分析和語音合成,這種方法簡化了復(fù)雜文本的處理。

3. 統(tǒng)計(jì)方法和數(shù)據(jù)驅(qū)動技術(shù)(21世紀(jì)初)

  • 統(tǒng)計(jì)語音合成:2000年后,統(tǒng)計(jì)方法開始主導(dǎo)TTS技術(shù)的研究。這些方法依賴于大量的語音數(shù)據(jù)和文本數(shù)據(jù),通過機(jī)器學(xué)習(xí)算法來生成語音。梅爾頻率倒譜系數(shù)(MFCC)和隱馬爾可夫模型(HMM)等技術(shù)被廣泛應(yīng)用。
  • 語音庫和TTS系統(tǒng):出現(xiàn)了基于語音庫的TTS系統(tǒng),這些系統(tǒng)使用預(yù)先錄制的語音片段來生成自然語音。例如,單元選擇合成(Unit Selection Synthesis)通過選擇和拼接預(yù)先錄制的語音單元來生成語音。

4. 深度學(xué)習(xí)和神經(jīng)網(wǎng)絡(luò)時(shí)代(2010年至今)

  • 神經(jīng)網(wǎng)絡(luò)TTS:隨著深度學(xué)習(xí)技術(shù)的發(fā)展,神經(jīng)網(wǎng)絡(luò)在TTS中的應(yīng)用迅速增長。遞歸神經(jīng)網(wǎng)絡(luò)(RNN)、長短期記憶網(wǎng)絡(luò)(LSTM)和變分自編碼器(VAE)等技術(shù)被用于生成更加自然和流暢的語音。
  • 端到端模型:出現(xiàn)了端到端TTS模型,這些模型直接從文本生成語音,簡化了處理流程。例如,WaveNet、Tacotron和Transformer等模型顯著提高了語音合成的質(zhì)量。
  • 多語言和個(gè)性化TTS:現(xiàn)代TTS系統(tǒng)支持多語言和個(gè)性化語音合成。用戶可以選擇不同的語音風(fēng)格和角色,甚至可以定制自己的語音模型。

5. 當(dāng)前趨勢和未來展望

  • 低資源語言支持:為了讓更多人受益于TTS技術(shù),研究人員正在開發(fā)低資源語言的TTS系統(tǒng),這些系統(tǒng)可以在數(shù)據(jù)有限的情況下仍然生成高質(zhì)量的語音。
  • 實(shí)時(shí)TTS:隨著硬件和算法的進(jìn)步,實(shí)時(shí)TTS系統(tǒng)的性能不斷提升,使得語音合成的延遲大大降低。
  • 多模態(tài)TTS:結(jié)合其他感知模態(tài)(如視覺和觸覺)的多模態(tài)TTS系統(tǒng)正在研究中,以提供更加豐富的用戶體驗(yàn)。

總的來說,TTS技術(shù)從早期的簡單合成發(fā)展到了現(xiàn)在的深度學(xué)習(xí)驅(qū)動的自然語音生成,不斷推動著人機(jī)交互和無障礙通信的發(fā)展。

2-TTS目前的發(fā)展

1-深度學(xué)習(xí)和神經(jīng)網(wǎng)絡(luò)時(shí)代(2010年至今)

  • 神經(jīng)網(wǎng)絡(luò)TTS
    • 技術(shù)應(yīng)用:RNN, LSTM, VAE
    • 目標(biāo):自然流暢的語音
  • 端到端模型
    • 模型:WaveNet, Tacotron, Transformer
    • 優(yōu)勢:簡化流程,提高質(zhì)量
  • 多語言和個(gè)性化TTS
    • 支持:多語言,個(gè)性化語音
    • 功能:選擇語音風(fēng)格和角色,定制語音模型

2-當(dāng)前趨勢和未來展望

  • 低資源語言支持
    • 目標(biāo):數(shù)據(jù)有限下生成高質(zhì)量語音
  • 實(shí)時(shí)TTS
    • 進(jìn)展:硬件和算法進(jìn)步
    • 結(jié)果:降低延遲
  • 多模態(tài)TTS
    • 方向:結(jié)合視覺和觸覺
    • 目標(biāo):豐富用戶體驗(yàn)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容