備忘錄_20160105(定位)
修改
回首頁
程式 2023-01-24 04:27:25 1674505645 100
語音辨識 speech / voice recognition。pocketsphinx。cmusphinx。python。vosk。
語音辨識 speech / voice recognition。pocketsphinx。cmusphinx。python。vosk。
- 英文語音關鍵詞識別。長時間聆聽。pocketsphinx。LiveSpeech。kws_threshold範圍從1~1e-50。
- 中文語音詞句識別。限定秒數。SpeechRecognition+vosk。(https://alphacephei.com/vosk/ (vosk引擎,回傳簡體中文,辨識率不錯))
- 簡體中文換成正體中文。使用 OpenCC 進行簡繁互換。
- 使用 urllib 進行關鍵字搜尋並擷取網頁內文
- 使用 pyttsx3 進行文字轉語音(可接受中文)
- 使用 pygetwindow+pyautogui 來進行視窗操作(如關閉頁籤,按空白鍵等)
- 使用 webbrowser 開啟指定 url 的網頁
- playsound 若和 threading 搭配,可以不鎖住主執行緒。
import os
import webbrowser
from pocketsphinx import LiveSpeech # pip install pocketsphinx
from playsound import playsound # pip install playsound==1.2.2
import threading
import pyttsx3 # 記得也要安裝 sudo apt install/remove espeak-ng
import urllib.request
import urllib.parse
import time
import pyautogui
import pygetwindow
# pip install pyaudio
from opencc import OpenCC # pip install opencc-python-reimplemented
# 轉換模式
#
# hk2s: 繁體中文 (香港) -> 簡體中文
# s2hk: 簡體中文 -> 繁體中文 (香港)
# s2t: 簡體中文 -> 繁體中文
# s2tw: 簡體中文 -> 繁體中文 (台灣)
# s2twp: 簡體中文 -> 繁體中文 (台灣, 包含慣用詞轉換)
# t2hk: 繁體中文 -> 繁體中文 (香港)
# t2s: 繁體中文 -> 簡體中文
# t2tw: 繁體中文 -> 繁體中文 (台灣)
# tw2s: 繁體中文 (台灣) -> 簡體中文
# tw2sp: 繁體中文 (台灣) -> 簡體中文 (包含慣用詞轉換 )
import speech_recognition # pip install SpeechRecognition; pip install vosk; 下載 vosk 的 中文 model(資料夾名稱須為 model)
import sounddevice # pip install sounddevice
from scipy.io.wavfile import write # pip install scipy
# pip install pydub
#
# ●ffmpeg for windows
# https://www.ffmpeg.org/download.html 下載 windows 版本的 ffmpeg 後,解壓縮(某資料夾,不能刪除),
# 環境變數 path ,增加一個路徑到其 bin 的位置,之後再安裝 pip install ffmpeg
#
# ●ffmpeg for debian
# sudo apt install ffmpeg
#
from pydub import AudioSegment
# 若錄音有遇到問題,如 SystemError: ffi_prep_closure(): bad user_data (it seems that the version of the libffi library seen at runtime is different from the 'ffi.h' file seen at compile-time)
# 則試試下面兩行(改為舊版本)
# pip uninstall cffi
# pip install cffi==1.15.0
def printMenu():
print('')
print('')
print('')
print('')
print('●語音助理 ver 0.22●')
print('')
print('說出 ' + strKeyPhrase + ' 即可進入指令模式,指令如下')
print('------')
print('自動音樂 [影片關鍵字] ← 搜尋youtube,關閉含YouTube字眼頁籤,開啟網頁,按下空白鍵')
print('尋找音樂 [影片關鍵字] ← 搜尋youtube,開啟網頁')
print('再見 ← 離開語音助理')
print('論語、冰雪奇緣、夜空中最亮的星、我可能不會愛你、小幸運 ← 任一名稱,播放資料夾中對應的mp3檔案')
print('立刻給我關機 ← 如字面所說,若工作無存檔,那就慘了!')
print('我要錄音 ← 錄音30秒')
print('搜尋網路 [搜尋關鍵字] ← 搜尋google,開啟網頁')
print('------')
print('其他註記')
print('1.(一些歌名供參考 ^^) 傳奇、紅豆、約定、風箏、聽海、光年之外、那些年、明天會更好、隱形的翅膀、陳淑樺-問')
print('2.youtube 的 autoplay 要自己手動關掉。')
print('')
print('')
print('')
print('')
def searchYoutube(strKW, booTryAutoPlay):
print("搜尋關鍵字:" + strKW)
oFP = urllib.request.urlopen("https://www.youtube.com/results?search_query=" + urllib.parse.quote_plus(strKW))
strBytes = oFP.read()
strUtf8 = strBytes.decode("utf8")
oFP.close()
#f=open('debug.log', mode='w', encoding='utf-8')
#f.write(soup.prettify())
#f.close()
straSearchResult=[]
iIdxStart=0
strKWTitle='"title":{"runs":[{"text":"'
strKWId='"videoId":"'
strKWEnd='"'
while True:
iIdxTitleL=-1
iIdxTitleR=-1
iIdxIdL=-1
iIdxIdR=-1
iIdxTitleL = strUtf8.find(strKWTitle, iIdxStart)
if iIdxTitleL==-1: break
iIdxTitleR = strUtf8.find(strKWEnd, iIdxTitleL+len(strKWTitle))
if iIdxTitleR==-1: break
iIdxIdL = strUtf8.find(strKWId, iIdxTitleR+len(strKWEnd))
if iIdxIdL==-1: break
iIdxIdR = strUtf8.find(strKWEnd, iIdxIdL+len(strKWId))
if iIdxIdR==-1: break
strSRTitle = strUtf8[iIdxTitleL+len(strKWTitle) : iIdxTitleR]
strSRId = strUtf8[iIdxIdL+len(strKWId) : iIdxIdR]
straSearchResult.append(strSRTitle + "\t" + strSRId)
iIdxStart = iIdxIdR+len(strKWEnd)
if iIdxStart>=len(strUtf8): break
if len(straSearchResult)<1:
print("找不到符合關鍵字的音樂")
oTTSEngine.say("找不到符合關鍵字的音樂")
oTTSEngine.runAndWait()
else:
if booTryAutoPlay==True:
print('盡可能關閉有 YouTube 字眼的頁籤,使用 Ctrl+W。')
try:
oOldWin = pygetwindow.getWindowsWithTitle("YouTube")[0]
oOldWin.maximize()
oOldWin.activate()
pyautogui.hotkey('ctrl','w')
print('已嘗試關閉')
except:
print('沒有找到可能的視窗')
strWantedTitle, strWantedId = straSearchResult[0].split("\t")
webbrowser.open("https://www.youtube.com/watch?autoplay=0&v=" + strWantedId)
print("開啟:(" + strWantedId + ")-" + strWantedTitle)
oTTSEngine.say("開啟" + strWantedTitle)
oTTSEngine.runAndWait()
if booTryAutoPlay==True:
time.sleep(5) # 等待瀏覽器開啟,暫定5秒
print('準備按下空白鍵')
try:
oNewWin = pygetwindow.getWindowsWithTitle("YouTube")[0]
oNewWin.maximize()
oNewWin.activate()
pyautogui.press('space')
print('已嘗試按下空白鍵')
except:
print('自動化失敗,請自行點選播放。')
oTTSEngine.say("自動化失敗,請自行點選播放。")
oTTSEngine.runAndWait()
strKeyPhrase = 'listen to me'
# 初始化文字轉語音
oTTSEngine = pyttsx3.init()
oTTSVoice = oTTSEngine.getProperty('voices')
fTTSRate = oTTSEngine.getProperty('rate')
fTTSVolume = oTTSEngine.getProperty('volume')
oTTSEngine.setProperty('rate', fTTSRate-20)
oTTSEngine.setProperty('volume', fTTSVolume-0.1)
printMenu()
for oEnglishPhrase in LiveSpeech(keyphrase=strKeyPhrase, kws_threshold=1e-20):
# print(str(oEnglishPhrase))
# 從麥克風讀取一段聲音(中文)
oRecognizer = speech_recognition.Recognizer()
strZhCn = ""
with speech_recognition.Microphone() as oMic:
playsound('try6_5secs.mp3')
oRecognizer.adjust_for_ambient_noise(oMic, duration=0.5) # 抓前面0.5秒作為背景雜音的分析
print("請在五秒內說出指令,指令長度最多七秒。")
try:
oAudio = oRecognizer.listen(oMic, 5, 7) # timeout=5, commandtimeout=7
print("辨認中,請稍候。")
playsound('try6_recognize.mp3')
strZhCn = oRecognizer.recognize_vosk(oAudio)
except:
strZhCn = "error!"
# 把 { "text":"......" } 變成 ......
strZhCn = strZhCn.replace("{","").replace("}","").replace('"text" : "','').replace('"','').replace("\r","").replace("\n","")
# 簡體中文轉換為正體中文
oConverter = OpenCC('s2twp')
strZhTw = oConverter.convert(strZhCn)
strZhTw = strZhTw.replace(' ','')
strZhCn = strZhCn.strip()
try:
print("指令(簡體中文):" + strZhCn)
print("指令(正體中文):" + strZhTw)
if strZhTw.find('自動音樂')!=-1:
searchYoutube(strZhTw.replace("自動音樂",""), True)
elif strZhTw.find('尋找音樂')!=-1:
searchYoutube(strZhTw.replace("尋找音樂",""), False)
elif strZhTw.find('再見')!=-1:
print('See you next time.')
playsound('try6_seeyou.mp3')
break
# 直接呼叫 playsound 時,要等播放完畢,才能接受下一道語音指令。
# 若是透過 threading.Thread 來呼叫 playsound,則能馬上接受下一道語音指令。
elif strZhTw.find('論語')!=-1:
playsound('try6_20180809_luen2ju3_7th_a.mp3')
elif strZhTw.find('冰雪奇緣')!=-1:
threading.Thread(target=playsound, args=('try6_letitgo.mp3',), daemon=True).start()
elif strZhTw.find('夜空中最亮的星')!=-1:
threading.Thread(target=playsound, args=('try6_star.mp3',), daemon=True).start()
elif strZhTw.find('我可能不會愛你')!=-1 or strZhTw.find('我可能不會愛妳')!=-1:
threading.Thread(target=playsound, args=('try6_imaynotloveyou.mp3',), daemon=True).start()
elif strZhTw.find('小幸運')!=-1:
threading.Thread(target=playsound, args=('try6_littlelucky.mp3',), daemon=True).start()
elif strZhTw.strip().find('立刻給我關機')==0:
if (str(os.name)).startswith('nt'):
os.system('shutdown -s -f -t 0')
elif (str(os.name)).startswith('posix'):
os.system('systemctl poweroff')
else:
print('unknown os')
elif strZhTw.strip().find('我要錄音')==0:
strFNRecWav='try6_rec.wav'
strFNRecMp3='try6_rec.mp3'
iSampleRate = 44100
iSeconds = 30
print('開始錄音'+str(iSeconds)+'秒')
oTTSEngine.say('開始錄音'+str(iSeconds)+'秒')
oTTSEngine.runAndWait()
oMyRecording = sounddevice.rec(int(iSeconds * iSampleRate), samplerate=iSampleRate, channels=2)
sounddevice.wait()
write(strFNRecWav, iSampleRate, oMyRecording)
print(strFNRecWav+'錄音完畢!')
oTTSEngine.say('錄音完畢')
oTTSEngine.runAndWait()
print('準備轉為mp3')
oTTSEngine.say('準備轉為mp3')
oTTSEngine.runAndWait()
oSound=AudioSegment.from_wav(strFNRecWav)
oSound.export(strFNRecMp3, format='mp3')
print('轉為mp3完成('+strFNRecMp3+')')
oTTSEngine.say('轉為mp3完成')
oTTSEngine.runAndWait()
print('開始播放剛剛錄製的mp3('+strFNRecMp3+')')
playsound('try6_rec.mp3')
elif strZhTw.find('搜尋網路')!=-1:
webbrowser.open("https://www.google.com/search?q=" + urllib.parse.quote_plus(strZhTw.replace("搜尋網路","")))
elif len(strZhTw)>0:
print("無法辨識的指令:" + strZhTw)
oTTSEngine.say(strZhTw)
oTTSEngine.runAndWait()
except speech_recognition.UnknownValueError:
print("未知的錯誤!speech_recognition.UnknownValueError!")
except speech_recognition.RequestError as e:
print("無法取得 vosk 服務; {0}".format(e))
printMenu()