簡單地寫了一個按段落朗讀文本的demo:DEMO鏈接_gitee.com。
有時候會請求不到數據,不知道是網絡原因還是什么,已添加自動重新請求。
config.ini:
;關於語音合成的相關配置
[default]
api_key = Your api key
secret_key = Your secret key
;發音人選擇, 基礎音庫:0為度小美,1為度小宇,3為度逍遙,4為度丫丫,
;精品音庫:5為度小嬌,103為度米朵,106為度博文,110為度小童,111為度小萌,默認為度小美
per = 3
;語速,取值0-15,默認為5中語速
spd = 4
;音調,取值0-15,默認為5中語調
pit = 5
;音量,取值0-9,默認為5中音量
vol = 5
# 下載的文件格式, 3:mp3(default) 4: pcm-16k 5: pcm-8k 6. wav
aue = 3
;下載的文件格式, 可選項:mp3(default), pcm-16k, pcm-8k, wav
format = mp3
cuid = 123456PYTHON
tts_url = http://tsn.baidu.com/text2audio
token_url = http://openapi.baidu.com/oauth/2.0/token
;有此scope表示有tts能力,沒有請在網頁里勾選
scope = audio_tts_post
[追風箏的人.txt]
text_lct = 12
main.py
# coding=utf-8
import os
import json
from configparser import ConfigParser
from playsound import playsound
from urllib.request import urlopen
from urllib.request import Request
from urllib.error import URLError
from urllib.error import HTTPError
from urllib.parse import urlencode
from urllib.parse import quote_plus
TEXT = "歡迎使用百度語音合成。"
ini_file = "./config.ini"
cfg_name = "default"
book = "D:/總要刪的/追風箏的人.txt"
def load_config(ini, name):
cfg = ConfigParser()
# 讀取文件內容
cfg.read(ini, encoding="gbk")
# cfg.items()返回list,元素為tuple
return dict(cfg.items(name))
class DemoError(Exception):
pass
def fetch_token(dft_cfg):
# print("fetch token begin")
params = {'grant_type': 'client_credentials',
'client_id': dft_cfg['api_key'],
'client_secret': dft_cfg['secret_key']}
post_data = urlencode(params)
post_data = post_data.encode('utf-8')
req = Request(dft_cfg['token_url'], post_data)
try:
f = urlopen(req, timeout=5)
result_str = f.read()
except URLError as err:
print('token http response http code : ' + str(err.code))
result_str = err.read()
result_str = result_str.decode()
# print(result_str)
result = json.loads(result_str)
# print(result)
if 'access_token' in result.keys() and 'scope' in result.keys():
if not dft_cfg['scope'] in result['scope'].split(' '):
raise DemoError('scope is not correct')
# print('SUCCESS WITH TOKEN: %s ; EXPIRES IN SECONDS: %s' % (result['access_token'], result['expires_in']))
return result['access_token']
else:
raise DemoError('MAYBE API_KEY or SECRET_KEY not correct: access_token or scope not found in token response')
def update_text(file, book_title, ini):
# 讀取配置文件
cfg = ConfigParser()
# 讀取文件內容
cfg.read(ini, encoding="gbk")
if cfg.has_option(book_title, "text_lct"):
now_lct = int(cfg.get(book_title, "text_lct"))
else:
cfg.add_section(book_title)
now_lct = 0
if len(file) <= now_lct:
return "已經讀到最后一句啦!換本書吧~!"
else:
while not len(file[now_lct].strip()):
now_lct = now_lct + 1
# 更新配置文件
cfg.set(book_title, "text_lct", str(now_lct + 1))
cfg.write(open(ini, "r+"))
return file[now_lct]
def request_api(params):
data = urlencode(params)
req = Request(dft_cfg['tts_url'], data.encode('utf-8'))
try:
f = urlopen(req)
result_str = f.read()
headers = dict((name.lower(), value) for name, value in f.headers.items())
has_error = ('content-type' not in headers.keys() or headers['content-type'].find('audio/') < 0)
except Exception as e:
print('asr http response http code : ' + str(e))
result_str = str(e)
has_error = True
if has_error:
print("tts api error:" + str(result_str, 'utf-8'))
request_api(params)
else:
# Step 3.4: 保存請求的音頻結果並輸出成temp.mp3,朗讀完畢后刪除
save_file = "error.txt" if has_error else 'temp.' + dft_cfg['format']
with open(save_file, 'wb') as of:
of.write(result_str)
playsound(save_file)
os.remove(save_file)
if __name__ == '__main__':
# Step 1: 載入配置文件
dft_cfg = load_config(ini_file, cfg_name)
# Step 2: 獲取Token
token = fetch_token(dft_cfg)
# Step 3: 向API發起請求
# Step 3.1: 初始化請求參數params、書籍標題
params = {'tok': token, 'tex': '', 'per': dft_cfg['per'], 'spd': dft_cfg['spd'], 'pit': dft_cfg['pit'],
'vol': dft_cfg['vol'], 'aue': dft_cfg['aue'], 'cuid': dft_cfg['cuid'],
'lan': 'zh', 'ctp': 1} # lan ctp 固定參數
book_title = (book.split('/'))[-1]
# 打開指定書籍, 並按行讀取
with open(book, "r", encoding='utf-8') as f:
file = f.readlines()
# Step 3.2: 不斷獲取文本並朗讀請求得到的音頻
while 1:
# Step 3.2.1: 根據上次閱讀的位置,更新需要合成的文本內容
TEXT = update_text(file, book_title, ini_file)
print(TEXT)
params['tex'] = quote_plus(TEXT) # 此處TEXT需要兩次urlencode
# Step 3.2.2: 將參數打包,並向指定URL請求,並朗讀
request_api(params)
目前的結果: