给大神鼓掌:400行Python代码实现文语处理助手!(NXP资深系统工程师精品)

嵌入式ARM 2020-05-08 00:00

(1) - 环境搭建


  智能语音交互市场近年来发展迅速,其典型的应用之一智能音箱产品如今已走入千家万户,深受大家喜爱。智能音箱产品的核心就是语音处理,包括音频采集、语音识别(ASR)、自然语言处理(NLP)、文语合成(TTS)、音频播放五大部分。目前除了音频采集和播放必须在嵌入式端实现外,其余三部分一般都在云端处理(嵌入式端通过有线(USB)或无线(Wifi/BLE)将音频数据发送到云端)。痞子衡对语音处理一直比较感兴趣,最近在玩Python也注意到Python里有很多语音处理库,因此打算从零开始写一个基于Python的语音处理工具,这个语音处理工具我们暂且叫她pzh-speech,初步计划为pzh-speech设计4大功能:wav音频录制,语音识别,文语合成,音频播放。

  在写pzh-py-speech时需要先搭好开发环境,下表列出了开发过程中会用到的所有软件/工具包:

一、涉及工具列表

工具 功能 下载地址
Python 2.7.14 Python官方包(解释器) https://www.python.org/
PyAudio 0.2.11 跨平台开源Audio I/O库 PortAudio 的Python封装 http://people.csail.mit.edu/hubert/pyaudio/
Matplotlib 2.2.3 一款非常强大的Python 2D绘图库 https://matplotlib.org/
https://github.com/matplotlib/matplotlib
NumPy 1.15.0 基础Python科学计算包 http://www.numpy.org/
https://www.scipy.org/
SpeechRecognition 3.8.1 一款支持多引擎的Python语音识别(ASR)库 https://github.com/Uberi/speech_recognition
PocketSphinx 0.1.15 卡内基-梅隆大学开源语音识别引擎 CMU Sphinx 的Python封装 https://github.com/bambocher/pocketsphinx-python
https://pypi.org/project/pocketsphinx/
pyttsx3 2.7 pyTTS, pyttsx项目的延续之作,一款轻量级的Python文语合成引擎 https://github.com/nateshmbhat/pyttsx3
https://pypi.org/project/pyttsx3/
eSpeak 1.48.04 一款开源的TTS,可将转换结果保存为wav http://espeak.sourceforge.net/
wxPython 4.0.3 跨平台开源GUI库 wxWidgets 的Python封装库 https://www.wxpython.org/
https://pypi.org/project/wxPython/
wxFormBuilder 3.8.0 wxPython GUI界面构建工具 https://github.com/wxFormBuilder/wxFormBuilder
PyCharm Community 2018.02 一款流行的Python集成开发环境 http://www.jetbrains.com/pycharm/

二、基础环境搭建(Python + PyAudio + Matplotlib + NumPy)

  pzh-py-speech工具是一个完全基于Python语言开发的应用软件,首先安装好Python 2.7.14,痞子衡的安装目录为C:\tools_mcu\Python27,安装完成后确保系统环境变量里包括该路径(C:\tools_mcu\Python27),因为该路径下包含python.exe,后续python命令需调用这个python.exe完成的。此外pip是Python的包管理工具,我们可以借助pip来安装PyAudio和Matplotlib包(NumPy含在Matplotlib里):

PS C:\tools_mcu\Python27\Scripts> .\pip.exe install pyaudio

Collecting pyaudio
...
Successfully installed pyaudio-0.2.11

PS C:\tools_mcu\Python27\Scripts> .\pip.exe install matplotlib

Collecting matplotlib
...
Successfully installed backports.functools-lru-cache-1.5 cycler-0.10.0 kiwisolver-1.0.1 matplotlib-2.2.3 numpy-1.15.0 pyparsing-2.2.0 python-dateutil-2.7.3 pytz-2018.5

  有了PyAudio便可以读写Audio,有了Matplotlib便可以将Audio以波形方式图形化显示出来。这两个工具安装完成,JaysPySPEECH工具开发的Python基础环境便搭好了。

Note: 关于GUI及调试等相关工具(wxPython、wxFormBuilder、PyCharm)的安装详见痞子衡另一个作品 pzh-py-com的环境搭建。

二、高级环境搭建(SpeechRecognition + PocketSphinx + pyttsx3 + eSpeak)

  上一步主要安装了pzh-py-speech的基础开发环境,用于Audio的录播与显示,但是pzh-py-speech设计之初便考虑支持语音识别、文语转换功能,因为我们还需要进一步安装相关Python库。
  首先安装语音识别库,SpeechRecognition是一款非常流行的支持多引擎的语音识别Python库,痞子衡为pzh-py-speech选用的就是SpeechRecognition,其中语音识别引擎选用的是可以离线工作的PocketSphinx,具体安装如下:

PS C:\tools_mcu\Python27\Scripts> .\pip.exe install SpeechRecognition

Collecting SpeechRecognition
...
Successfully installed SpeechRecognition-3.8.1

PS C:\tools_mcu\Python27\Scripts> python -m pip install --upgrade pip setuptools wheel

Requirement already up-to-date: pip in c:\tools_mcu\python27\lib\site-packages (18.0)
Collecting setuptools
...
Successfully installed setuptools-40.2.0 wheel-0.31.1

PS C:\tools_mcu\Python27\Scripts> .\pip.exe install --upgrade pocketsphinx

Collecting pocketsphinx
...
Successfully installed pocketsphinx-0.1.15

  最后安装文语合成库,pyttsx3是一款超轻量级的文语合成Python库,其是经典的pyTTS、pyttsx项目的延续,其内核为Microsoft Speech API (SAPI5),可离线工作,具体安装如下:

PS C:\tools_mcu\Python27\Scripts> .\pip.exe install pyttsx3

Collecting pyttsx3
...
Successfully installed pyttsx3-2.7

  pyttsx3仅能在线发声,无法保存到wav文件,因此我们还需要一个可以保存wav文件的TTS,痞子衡选择了eSpeak,其具体安装详见系列第六篇。到了这里,pzh-py-speech工具开发的Python环境便全部搭好了。


(2) - 界面构建


构建pzh-py-speech的界面过程与pzh-py-com构建步骤类似,也是分四步:界面设计简图、界面设计wxPython组件图、在wxFormBuilder里创作、使用生成的Python代码。为了突出重点,痞子衡只讲前两步,后面的过程不再赘述。

一、界面设计简图

  在真正进入代码设计pzh-py-speech界面前,首先应该在纸上画一个界面草图,确定pzh-py-speech界面应该有哪些元素构成,这些元素分别位于界面上什么位置。下面是痞子衡画的pzh-py-speech的界面简图,界面主要包括三大部分:接收区、配置区、发送区,接收区用于显示从串口接收到的数据;配置区用于配置串口参数;发送区用于编辑要从串口发送出去的数据。

二、界面设计wxPython组件图

  有了pzh-py-speech的界面设计简图指导,下一步需要将设计简图解析成如下的wxPython组件图,将简图里的元素转换成wxPython里的真实组件。这一步需要配合查阅wxPython相关手册,了解wxPython有哪些组件。


(3) - 音频显示


  音频显示是pzh-py-speech的主要功能,pzh-py-speech借助的是Matplotlib以及NumPy来实现的音频显示功能,今天痞子衡为大家介绍音频显示在pzh-py-speech中是如何实现的。

一、SciPy工具集

  SciPy是一套Python科学计算相关的工具集,其本身也是一个Python库,这个工具集主要包含以下6大Python库,pzh-py-speech所用到的Matplotlib以及NumPy均属于SciPy工具集。

1.1 NumPy

  NumPy是一套最基础的Python科学计算包,它主要用于数组与矩阵运算,它是一个开源项目,被收录进 NumFOCUS 组织维护的 Sponsored Project 里。pzh-py-speech使用的是NumPy 1.15.0。
  NumPy库的官方主页如下:

  • NumPy官方主页: http://www.numpy.org/

  • NumPy安装方法: https://pypi.org/project/numpy/

  NumPy的快速上手可参考这个网页 https://docs.scipy.org/doc/numpy/user/quickstart.html

1.2 Matplotlib

  Matplotlib是一套Python高质量2D绘图库,它的初始设计者为John Hunter,它也是一个开源项目,被同样收录进 NumFOCUS 组织维护的 Sponsored Project 里。pzh-py-speech使用的是Matplotlib 2.2.3。
  Matplotlib库的官方主页如下:

  • Matplotlib官方主页: https://matplotlib.org/

  • Matplotlib安装方法: https://pypi.org/project/matplotlib/

  Matplotlib绘图功能非常强大,但是作为一般使用,我们没有必要去通读其官方文档,其提供了非常多的example代码,这些example都在 https://matplotlib.org/gallery/index.html, 我们只要找到能满足我们需求的example,在其基础上简单修改即可。下面就是一个最简单的正弦波示例:

import matplotlib
import matplotlib.pyplot as plt
import numpy as np

# Data for plotting
t = np.arange(0.0, 2.0, 0.01)
s = 1 + np.sin(2 * np.pi * t)

fig, ax = plt.subplots()
ax.plot(t, s)

ax.set(xlabel='time (s)', ylabel='voltage (mV)',
title='About as simple as it gets, folks')
ax.grid()

fig.savefig("test.png")
plt.show()

二、pzh-py-speech音频显示实现

  pzh-py-speech关于音频显示功能实现主要有四点:选择.wav文件、读取.wav文件、绘制.wav波形、添加光标功能,最终pzh-py-speech效果如下图所示,痞子衡为逐一为大家介绍实现细节。

2.1 选择.wav文件功能

  选择wav文件主要借助的是wxPython里的genericDirCtrl控件提供的功能实现的,我们使用genericDirCtrl控件创建了一个名为m_genericDirCtrl_audioDir的对象,借助其SetFilter()方法实现了仅显示.wav文件格式的过滤,并且我们为m_genericDirCtrl_audioDir还创建了一个event,即viewAudio(),这个event的触发条件是选中m_genericDirCtrl_audioDir里列出的.wav文件,当viewAudio()被触发时,我们通过GetFilePath()方法即可获得选中的.wav文件路径。

class mainWin(win.speech_win):

def __init__(self, parent):
win.speech_win.__init__(self, parent)
# ...
self.m_genericDirCtrl_audioDir.SetFilter("Audio files (*.wav)|*.wav")

def viewAudio( self, event ):
self.wavPath = self.m_genericDirCtrl_audioDir.GetFilePath()

2.2 读取.wav文件功能

  读取.wav文件主要借助的是python自带的标准库wave,以及第三方的NumPy库。痞子衡创建了一个名为wavCanvasPanel的类,在这个类中定义了readWave(self, wavPath, wavInfo)方法,其中参数wavPath即是要读取的.wav文件路径,参数wavInfo是GUI状态栏对象,用于直观显示读取到的.wav文件信息。
  在wavCanvasPanel.readWave()方法中,痞子衡首先使用了wave库里的功能获取到.wav文件的所有信息以及所有PCM数据,然后借助NumPy库将PCM数据按channel重新组织,便于后续图形显示。关于数据重新组织,有一个地方需要特别说明,即int24类型(3-byte)是不被NumPy中的fromstring()原生支持,因此痞子衡自己实现了一个非标准类型数据的fromstring()。

import numpy
import wave

class wavCanvasPanel(wx.Panel):

def fromstring(self, wavData, alignedByte):
if alignedByte <= 8:
src = numpy.ndarray(len(wavData), numpy.dtype('>i1'), wavData)
dest = numpy.zeros(len(wavData) / alignedByte, numpy.dtype('>i8'))
for i in range(alignedByte):
dest.view(dtype='>i1')[alignedByte-1-i::8] = src.view(dtype='>i1')[i::alignedByte]
[hex(x) for x in dest]
return True, dest
else:
return False, wavData

def readWave(self, wavPath, wavInfo):
if os.path.isfile(wavPath):
# Open the wav file to get wave data and parameters
wavFile = wave.open(wavPath, "rb")
wavParams = wavFile.getparams()
wavChannels = wavParams[0]
wavSampwidth = wavParams[1]
wavFramerate = wavParams[2]
wavFrames = wavParams[3]
wavInfo.SetStatusText('Opened Audio Info = ' +
'Channels:' + str(wavChannels) +
', SampWidth:' + str(wavSampwidth) + 'Byte' +
', SampRate:' + str(wavFramerate) + 'kHz' +
', FormatTag:' + wavParams[4])
wavData = wavFile.readframes(wavFrames)
wavFile.close()
# Transpose the wav data if wave has multiple channels
if wavSampwidth == 1:
dtype = numpy.int8
elif wavSampwidth == 2:
dtype = numpy.int16
elif wavSampwidth == 3:
dtype = None
elif wavSampwidth == 4:
dtype = numpy.float32
else:
return 0, 0, 0
if dtype != None:
retData = numpy.fromstring(wavData, dtype = dtype)
else:
# Implement int24 manually
status, retData = self.fromstring(wavData, 3)
if not status:
return 0, 0, 0
if wavChannels != 1:
retData.shape = -1, wavChannels
retData = retData.T
# Calculate and arange wave time
retTime = numpy.arange(0, wavFrames) * (1.0 / wavFramerate)
retChannels = wavChannels
return retChannels, retData, retTime
else:
return 0, 0, 0

2.3 绘制.wav波形功能

  绘制.wav波形是最主要的功能。痞子衡在wavCanvasPanel类中实现了showWave(self, wavPath, wavInfo)方法,这个方法会在GUI控件m_genericDirCtrl_audioDir的事件函数viewAudio()中被调用。
  在wavCanvasPanel.showWave()方法中,痞子衡首先使用了readWave()获取.wav文件中经过重新组织的PCM数据,然后借助Matplotlib中的figure类中的add_axes()方法逐一将各channel的PCM数据绘制出来,并辅以各种信息(x、y轴精度、标签等)一同显示出来。由于GUI控件里专门用于显示波形的Panel对象尺寸为720*360 inch,痞子衡限制了最多显示.wav的前8通道。

import matplotlib
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.figure import Figure

MAX_AUDIO_CHANNEL = 8
#unit: inch
PLOT_PANEL_WIDTH = 720
PLOT_PANEL_HEIGHT = 360
#unit: percent
PLOT_AXES_WIDTH_TITLE = 0.05
PLOT_AXES_HEIGHT_LABEL = 0.075

class wavCanvasPanel(wx.Panel):

def __init__(self, parent):
wx.Panel.__init__(self, parent)
dpi = 60
width = PLOT_PANEL_WIDTH / dpi
height = PLOT_PANEL_HEIGHT / dpi
self.wavFigure = Figure(figsize=[width,height], dpi=dpi, facecolor='#404040')
self.wavCanvas = FigureCanvas(self, -1, self.wavFigure)
self.wavSizer = wx.BoxSizer(wx.VERTICAL)
self.wavSizer.Add(self.wavCanvas, 1, wx.EXPAND|wx.ALL)
self.SetSizerAndFit(self.wavSizer)
self.wavAxes = [None] * MAX_AUDIO_CHANNEL

def readWave(self, wavPath, wavInfo):
# ...

def showWave(self, wavPath, wavInfo):
self.wavFigure.clear()
waveChannels, waveData, waveTime = self.readWave(wavPath, wavInfo)
if waveChannels != 0:
# Note: only show max supported channel if actual channel > max supported channel
if waveChannels > MAX_AUDIO_CHANNEL:
waveChannels = MAX_AUDIO_CHANNEL
# Polt the waveform of each channel in sequence
for i in range(waveChannels):
left = PLOT_AXES_HEIGHT_LABEL
bottom = (1.0 / waveChannels) * (waveChannels - 1 - i) + PLOT_AXES_HEIGHT_LABEL
height = 1.0 / waveChannels - (PLOT_AXES_WIDTH_TITLE + PLOT_AXES_HEIGHT_LABEL)
width = 1 - left - 0.05
self.wavAxes[i] = self.wavFigure.add_axes([left, bottom, width, height], facecolor='k')
self.wavAxes[i].set_prop_cycle(color='#00F279', lw=[1])
self.wavAxes[i].set_xlabel('time (s)', color='w')
self.wavAxes[i].set_ylabel('value', color='w')
if waveChannels == 1:
data = waveData
else:
data = waveData[i]
self.wavAxes[i].plot(waveTime, data)
self.wavAxes[i].grid()
self.wavAxes[i].tick_params(labelcolor='w')
self.wavAxes[i].set_title('Audio Channel ' + str(i), color='w')
# Note!!!: draw() must be called if figure has been cleared once
self.wavCanvas.draw()

class mainWin(win.speech_win):

def __init__(self, parent):
win.speech_win.__init__(self, parent)
self.wavPanel = wavCanvasPanel(self.m_panel_plot)
# ...

def viewAudio( self, event ):
self.wavPath = self.m_genericDirCtrl_audioDir.GetFilePath()
self.wavPanel.showWave(self.wavPath, self.statusBar)

2.4 添加光标功能

  光标定位功能不是必要功能,但其可以让软件看起来高大上,痞子衡创建了一个名为wavCursor类来实现它,主要在这个类中实现了moveMouse方法,这个方法将会被FigureCanvasWxAgg类中的mpl_connect()方法添加到各通道axes中。

MAX_AUDIO_CHANNEL = 8

class wavCursor(object):
def __init__(self, ax, x, y):
self.ax = ax
self.vline = ax.axvline(color='r', alpha=1)
self.hline = ax.axhline(color='r', alpha=1)
self.marker, = ax.plot([0],[0], marker="o", color="crimson", zorder=3)
self.x = x
self.y = y
self.xlim = self.x[len(self.x)-1]
self.text = ax.text(0.7, 0.9, '', bbox=dict(facecolor='red', alpha=0.5))

def moveMouse(self, event):
if not event.inaxes:
return
x, y = event.xdata, event.ydata
if x > self.xlim:
x = self.xlim
index = numpy.searchsorted(self.x, [x])[0]
x = self.x[index]
y = self.y[index]
self.vline.set_xdata(x)
self.hline.set_ydata(y)
self.marker.set_data([x],[y])
self.text.set_text('x=%1.2f, y=%1.2f' % (x, y))
self.text.set_position((x,y))
self.ax.figure.canvas.draw_idle()

class wavCanvasPanel(wx.Panel):
def __init__(self, parent):
# ...
self.wavAxes = [None] * MAX_AUDIO_CHANNEL
# 定义光标对象
self.wavCursor = [None] * MAX_AUDIO_CHANNEL

def showWave(self, wavPath, wavInfo):
# ...
if waveChannels != 0:
# ...
for i in range(waveChannels):
# ...
self.wavAxes[i].set_title('Audio Channel ' + str(i), color='w')
# 实例化光标对象,并使用mpl_connect()将moveMouse()动作加入光标对象
self.wavCursor[i] = wavCursor(self.wavAxes[i], waveTime, data)
self.wavCanvas.mpl_connect('motion_notify_event', self.wavCursor[i].moveMouse)
# ...


(4) - 音频录播


  音频录播是pzh-py-speech的主要功能,pzh-py-speech借助的是Python自带wave库以及第三方PyAudio库来实现的音频播放和录制功能,今天痞子衡为大家介绍音频录播在pzh-py-speech中是如何实现的。

一、wave简介

  wave是python标准库,其可以实现wav音频文件的读写,并且能解析wav音频的参数。pzh-py-speech借助wave库来读写wav文件,播放音频时借助wave库来读取wav文件并获取音频参数(通道,采样宽度,采样率),录制音频时借助wave库来设置音频参数并保存成wav文件。下面列举了pzh-py-speech所用到的全部API:

  • wave用法: https://docs.python.org/2/library/wave.html

wave.open()

# wav音频读API
Wave_read.getnchannels() # 获取音频通道数
Wave_read.getsampwidth() # 获取音频采样宽度
Wave_read.getframerate() # 获取音频采样率
Wave_read.getnframes() # 获取音频总帧数
Wave_read.readframes(n) # 读取音频帧数据
Wave_read.tell() # 获取已读取的音频帧数
Wave_read.close()

# wav音频写API
Wave_write.setnchannels(n) # 设置音频通道数
Wave_write.setsampwidth(n) # 设置音频采样宽度
Wave_write.setframerate(n) # 设置音频采样率
Wave_write.writeframes(data) # 写入音频帧数据
Wave_write.close()

二、PyAudio简介

  PyAudio是开源跨平台音频库PortAudio的python封装,PyAudio库的维护者是Hubert Pham,该库从2006年开始推出,一直持续更新至今,pzh-py-speech使用的是PyAudio 0.2.11。
  pzh-py-speech借助PyAudio库来实现音频数据流控制(包括从PC麦克风获取音频流,将音频流输出给PC扬声器),如果说wave库实现的是对wav文件的单纯操作,那么PyAudio库则实现的是音频相关硬件设备的交互。
  PyAudio项目的官方主页如下:

  • PortAudio官方主页: http://www.portaudio.com/

  • PyAudio官方主页: http://people.csail.mit.edu/hubert/pyaudio/

  • PyAudio安装方法: https://pypi.org/project/PyAudio/

  PyAudio对音频流的控制有两种,一种是阻塞式的,另一种是非阻塞式的(callback),前者一般用于确定的音频控制(比如单纯播放一个本地音频文件,并且中途不会有暂停/继续等操作),后者一般用于灵活的音频控制(比如录制一段音频,但是要等待一个事件响应才会结束)。pzh-py-speech用的是后者。下面是两种方式的音频播放使用示例:

import pyaudio
import wave

CHUNK = 1024

wf = wave.open(“test.wav”, 'rb')
p = pyaudio.PyAudio()

##########################################################
# 此为阻塞式,循环读取1024个byte音频数据去播放,直到test.wav文件数据被全部读出
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
channels=wf.getnchannels(),
rate=wf.getframerate(),
output=True)
data = wf.readframes(CHUNK)
while data != '':
stream.write(data)
data = wf.readframes(CHUNK)
##########################################################
# 此为非阻塞式的(callback),系统会自动读取test.wav文件里的音频帧,直到播放完毕
def callback(in_data, frame_count, time_info, status):
data = wf.readframes(frame_count)
return (data, pyaudio.paContinue)
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
channels=wf.getnchannels(),
rate=wf.getframerate(),
output=True,
stream_callback=callback)
stream.start_stream()
while stream.is_active():
time.sleep(0.1)
##########################################################

stream.stop_stream()
stream.close()
p.terminate()

三、pzh-py-speech音频录播实现

3.1 播放实现

  播放功能本身实现不算复杂,但pzh-py-speech里实现的是播放按钮的五种状态Start -> Play -> Pause -> Resume -> End控制,即播放中途实现了暂停和恢复,因此代码要稍微复杂一些。此处的重点是playAudioCallback()函数里的else分支,如果在暂停状态下,必须还是要给PyAudio返回一段空数据:

import wave
import pyaudio

AUDIO_PLAY_STATE_START = 0
AUDIO_PLAY_STATE_PLAY = 1
AUDIO_PLAY_STATE_PAUSE = 2
AUDIO_PLAY_STATE_RESUME = 3
AUDIO_PLAY_STATE_END = 4

class mainWin(win.speech_win):

def __init__(self, parent):
# ...
# Start -> Play -> Pause -> Resume -> End
self.playState = AUDIO_PLAY_STATE_START

def viewAudio( self, event ):
self.wavPath = self.m_genericDirCtrl_audioDir.GetFilePath()
if self.playState != AUDIO_PLAY_STATE_START:
self.playState = AUDIO_PLAY_STATE_END
self.m_button_play.SetLabel('Play Start')

def playAudioCallback(self, in_data, frame_count, time_info, status):
if self.playState == AUDIO_PLAY_STATE_PLAY or self.playState == AUDIO_PLAY_STATE_RESUME:
data = self.wavFile.readframes(frame_count)
if self.wavFile.getnframes() == self.wavFile.tell():
status = pyaudio.paComplete
self.playState = AUDIO_PLAY_STATE_END
self.m_button_play.SetLabel('Play Start')
else:
status = pyaudio.paContinue
return (data, status)
else:
# Note!!!:
data = numpy.zeros(frame_count*self.wavFile.getnchannels()).tostring()
return (data, pyaudio.paContinue)

def playAudio( self, event ):
if os.path.isfile(self.wavPath):
if self.playState == AUDIO_PLAY_STATE_END:
self.playState = AUDIO_PLAY_STATE_START
self.wavStream.stop_stream()
self.wavStream.close()
self.wavPyaudio.terminate()
self.wavFile.close()
if self.playState == AUDIO_PLAY_STATE_START:
self.playState = AUDIO_PLAY_STATE_PLAY
self.m_button_play.SetLabel('Play Pause')
self.wavFile = wave.open(self.wavPath, "rb")
self.wavPyaudio = pyaudio.PyAudio()
self.wavStream = self.wavPyaudio.open(format=self.wavPyaudio.get_format_from_width(self.wavFile.getsampwidth()),
channels=self.wavFile.getnchannels(),
rate=self.wavFile.getframerate(),
output=True,
stream_callback=self.playAudioCallback)
self.wavStream.start_stream()
elif self.playState == AUDIO_PLAY_STATE_PLAY or self.playState == AUDIO_PLAY_STATE_RESUME:
self.playState = AUDIO_PLAY_STATE_PAUSE
self.m_button_play.SetLabel('Play Resume')
elif self.playState == AUDIO_PLAY_STATE_PAUSE:
self.playState = AUDIO_PLAY_STATE_RESUME
self.m_button_play.SetLabel('Play Pause')
else:
pass

3.2 录制实现

  相比播放功能,录制功能就简单了些,因为录制按钮状态就两种Start -> End,暂不支持中断后继续录制。这里的重点主要是音频三大参数(采样宽度,采样率,通道数)设置的支持:

import wave
import pyaudio

class mainWin(win.speech_win):

def recordAudioCallback(self, in_data, frame_count, time_info, status):
if not self.isRecording:
status = pyaudio.paComplete
else:
self.wavFrames.append(in_data)
status = pyaudio.paContinue
return (in_data, status)

def recordAudio( self, event ):
if not self.isRecording:
self.isRecording = True
self.m_button_record.SetLabel('Record Stop')
# Get the wave parameter from user settings
fileName = self.m_textCtrl_recFileName.GetLineText(0)
if fileName == '':
fileName = 'rec_untitled1.wav'
self.wavPath = os.path.join(os.path.dirname(os.path.abspath(os.path.dirname(__file__))), 'conv', 'rec', fileName)
self.wavSampRate = int(self.m_choice_sampRate.GetString(self.m_choice_sampRate.GetSelection()))
channels = self.m_choice_channels.GetString(self.m_choice_channels.GetSelection())
if channels == 'Mono':
self.wavChannels = 1
else: #if channels == 'Stereo':
self.wavChannels = 2
bitDepth = int(self.m_choice_bitDepth.GetString(self.m_choice_bitDepth.GetSelection()))
if bitDepth == 8:
self.wavBitFormat = pyaudio.paInt8
elif bitDepth == 24:
self.wavBitFormat = pyaudio.paInt24
elif bitDepth == 32:
self.wavBitFormat = pyaudio.paFloat32
else:
self.wavBitFormat = pyaudio.paInt16
# Record audio according to wave parameters
self.wavFrames = []
self.wavPyaudio = pyaudio.PyAudio()
self.wavStream = self.wavPyaudio.open(format=self.wavBitFormat,
channels=self.wavChannels,
rate=self.wavSampRate,
input=True,
frames_per_buffer=AUDIO_CHUNK_SIZE,
stream_callback=self.recordAudioCallback)
self.wavStream.start_stream()
else:
self.isRecording = False
self.m_button_record.SetLabel('Record Start')
self.wavStream.stop_stream()
self.wavStream.close()
self.wavPyaudio.terminate()
# Save the wave data into file
wavFile = wave.open(self.wavPath, 'wb')
wavFile.setnchannels(self.wavChannels)
wavFile.setsampwidth(self.wavPyaudio.get_sample_size(self.wavBitFormat))
wavFile.setframerate(self.wavSampRate)
wavFile.writeframes(b''.join(self.wavFrames))
wavFile.close()


(5)-语音识别


  语音识别是pzh-py-speech的核心功能,pzh-py-speech利用的是SpeechRecognition系统以及CMU Sphinx引擎来实现的语音识别功能,今天痞子衡为大家介绍了语音识别在pzh-py-speech中是如何实现的。

一,语音识别系统简介

  SpeechRecognition是一套基于python实现的语音识别的系统,该系统的设计者为Anthony Zhang(Uberi),该库从2014年开始推出,一直持续更新至今,pzh-py-speech使用的是SpeechRecognition 3.8.1。
  SpeechRecognition系统的官方主页如下:

  • SpeechRecognition官方主页:https :  //github.com/Uberi/speech_recognition

  • SpeechRecognition安装方法:https :  //pypi.org/project/SpeechRecognition/

  SpeechRecognition系统本身并没有语音识别功能,其主要是调用第三方语音识别引擎来实现语音识别,SpeechRecognition支持的语音识别引擎非常多,有如下8种:

  • CMU Sphinx(离线工作)

  • Google语音识别

  • Google Cloud Speech API

  • 威特

  • Microsoft Bing语音识别

  • Houndify API

  • IBM语音转文字

  • Snowboy Hotword检测(可离线使用)

  不管是替代哪一种语音识别引擎,在SpeechRecognition里调用接口都是一致的,我们以实现音频文件转文字的示例代码audio_transcribe.py来了解SpeechRecognition的用法,截取audio_transcribe.py部分内容如下:

import speech_recognition as sr

# 指定要转换的音频源文件(english.wav)
from os import path
AUDIO_FILE = path.join(path.dirname(path.realpath(__file__)), "english.wav")

# 定义SpeechRecognition对象并获取音频源文件(english.wav)中的数据
r = sr.Recognizer()
with sr.AudioFile(AUDIO_FILE) as source:
audio = r.record(source) # read the entire audio file

# 使用CMU Sphinx引擎去识别音频
try:
print("Sphinx thinks you said " + r.recognize_sphinx(audio))
except sr.UnknownValueError:
print("Sphinx could not understand audio")
except sr.RequestError as e:
print("Sphinx error; {0}".format(e))

# 使用Microsoft Bing Voice Recognition引擎去识别音频
BING_KEY = "INSERT BING API KEY HERE" # Microsoft Bing Voice Recognition API keys 32-character lowercase hexadecimal strings
try:
print("Microsoft Bing Voice Recognition thinks you said " + r.recognize_bing(audio, key=BING_KEY))
except sr.UnknownValueError:
print("Microsoft Bing Voice Recognition could not understand audio")
except sr.RequestError as e:
print("Could not request results from Microsoft Bing Voice Recognition service; {0}".format(e))

# 使用其他引擎去识别音频
# ... ...

  有木有觉得SpeechRecognition使用起来特别简单?是的,这正是SpeechRecognition系统强大之处,更多示例可见https://github.com/Uberi/speech_recognition/tree/master/examples。

1.1精选CMU Sphinx引擎

  前面痞子衡讲了SpeechRecognition系统本身并没有语音识别功能,因此我们需要为SpeechRecognition安装一个语音识别引擎,痞子衡为JaysPySPEECH的是可离线工作的CMU
  Sphinx。CMU Sphinx是卡内基梅隆大学开发的一种开源语音识别引擎,该引擎可以离线工作,并支持多语种(英语,中文,法语等)。CMUSphinx引擎的官方主页如下:

  • CMU Sphinx官方主页:https :  //cmusphinx.github.io/

  • CMU Sphinx官方下载:https :  //sourceforge.net/projects/cmusphinx/

  由于JaysPySPEECH是基于Python环境开发的,因此我们不能直接使用CMU Sphinx,那该怎么办?别着急,Dmitry Prazdnichnov大牛为CMU Sphinx写了Python封装接口,即PocketSphinx,其官方主页如下:

  • PocketSphinx官方主页:https :  //github.com/bambocher/pocketsphinx-python

  • PocketSphinx安装方法:https ://pypi.org/project/pocketsphinx/ 

  我们在JaysPySPEECH生成系列文章第一篇环境构建里已经安装了语音识别和PocketSphinx,痞子衡的安装路径为C:\ tools_mcu \ Python27 \ Lib \ site-packages下的\ speech_recognition与\ pocketsphinx,安装好这两个包,引擎便选好了。

1.2为PocketSphinx引擎增加中文语言包

  默认情况下,PocketSphinx仅支持美国英语语言的识别,在C:\ tools_mcu \ Python27 \ Lib \ site-packages \ speech_recognition \ pocketsphinx-data目录下仅能看到美国文件夹,先来看一下这个文件夹里有什么:

\pocketsphinx-data\en-US
\acoustic-model --声学模型
\feat.params --HMM模型的特征参数
\mdef --模型定义文件
\means --混合高斯模型的均值
\mixture_weights --混合权重
\noisedict --噪声也就是非语音字典
\sendump --从声学模型中获取混合权重
\transition_matrices --HMM模型的状态转移矩阵
\variances --混合高斯模型的方差
\language-model.lm.bin --语言模型
\pronounciation-dictionary.dict --拼音字典

  其实看到了这一堆文件是不是觉得有点难懂?这其实跟CMU Sphinx引擎的语音识别原理有关,此处我们暂且不深入了解,对我们调用API的应用来说只需要关于如何为CMU Sphinx增加其他语言包(某种中文包)。
  要想增加其他语言,首先得要有语言包数据,CMU Sphinx主页提供了12种主流语言包的下载https://sourceforge.net/projects/cmusphinx/files/Acoustic_and_Language_Models/ ,因为JaysPySPEECH需要支持中文识别,因此我们需要下载\ Mandarin下面的三个文件:

\Mandarin
\zh_broadcastnews_16k_ptm256_8000.tar.bz2 --声学模型
\zh_broadcastnews_64000_utf8.DMP --语言模型
\zh_broadcastnews_utf8.dic --拼音字典

  有了中文语言包数据,然后我们需要根据关于使用PocketSphinx的说明里指示的步骤操作,痞子衡整理如下:

  1. \ speech_recognition \ pocketsphinx-data目录下创建zh-CN文件夹

  2. 将zh_broadcastnews_16k_ptm256_8000.tar.bz2解压缩并里面所有文件放入\ zh-CN \ acoustic-model文件夹下

  3. 将zh_broadcastnews_utf8.dic重命名为发音-dictionary.dict并加入\ zh-CN文件夹下

  4. 通过SphinxBase工具将zh_broadcastnews_64000_utf8.DMP转换成language-model.lm.bin并加入\ zh-CN文件夹下

  关于第4步里提到的SphinxBase工具,我们需要从https://github.com/cmusphinx/sphinxbase里下载源码,然后使用Visual Studio 2010(或更高版本)打开\ sphinxbase \ sphinxbase.sln工程全部重建后会在\ sphinxbase \ bin \ Release \ x64下看到生成了如下6个工具:

\\sphinxbase\bin\Release\x64
\sphinx_cepview.exe
\sphinx_fe.exe
\sphinx_jsgf2fsg.exe
\sphinx_lm_convert.exe
\sphinx_pitch.exe
\sphinx_seg.exe

  我们主要使用sphinx_lm_convert.exe工具完成转换工作生成language-model.lm.bin,具体命令如下:

PS C:\ tools_mcu \ sphinxbase \ bin \ Release \ x64>  。\ sphinx_lm_convert.exe -i。\ zh_broadcastnews_64000_utf8.DMP -o language-model.lm-ofmt arpa

Current configuration:
[NAME] [DEFLT] [VALUE]
-case
-help no no
-i .\zh_broadcastnews_64000_utf8.DMP
-ifmt
-logbase 1.0001 1.000100e+00
-mmap no no
-o language-model.lm
-ofmt arpa

INFO: ngram_model_trie.c(354): Trying to read LM in trie binary format
INFO: ngram_model_trie.c(365): Header doesn't match
INFO: ngram_model_trie.c(177): Trying to read LM in arpa format
INFO: ngram_model_trie.c(70): No \data\ mark in LM file
INFO: ngram_model_trie.c(445): Trying to read LM in dmp format
INFO: ngram_model_trie.c(527): ngrams 1=63944, 2=16600781, 3=20708460
INFO: lm_trie.c(474): Training quantizer
INFO: lm_trie.c(482): Building LM trie

PS C:\ tools_mcu \ sphinxbase \ bin \ Release \ x64>  。\ sphinx_lm_convert.exe -i。\ language-model.lm -o语言模型.lm.bin

Current configuration:
[NAME] [DEFLT] [VALUE]
-case
-help no no
-i .\language-model.lm
-ifmt
-logbase 1.0001 1.000100e+00
-mmap no no
-o language-model.lm.bin
-ofmt

INFO: ngram_model_trie.c(354): Trying to read LM in trie binary format
INFO: ngram_model_trie.c(365): Header doesn't match
INFO: ngram_model_trie.c(177): Trying to read LM in arpa format
INFO: ngram_model_trie.c(193): LM of order 3
INFO: ngram_model_trie.c(195): #1-grams: 63944
INFO: ngram_model_trie.c(195): #2-grams: 16600781
INFO: ngram_model_trie.c(195): #3-grams: 20708460
INFO: lm_trie.c(474): Training quantizer
INFO: lm_trie.c(482): Building LM trie

二,pzh-py-speech语音识别实现

  语音识别代码实现实际上很简单,直接调用speech_recognition里的API即可,目前仅实现了CMU Sphinx引擎,并且仅支持中英双语识别。具体到pzh-py-speech上主要是实现GUI界面上的“ ASR”按钮的相应函数,即audioSpeechRecognition(),如果用户更改了配置参数(语言类型,ASR引擎类型),并单击“ ASR”按钮,此时便会触发audioSpeechRecognition()的执行。代码如下:

import speech_recognition

class mainWin(win.speech_win):

def getLanguageSelection(self):
languageType = self.m_choice_lang.GetString(self.m_choice_lang.GetSelection())
if languageType == 'Mandarin Chinese':
languageType = 'zh-CN'
languageName = 'Chinese'
else: # languageType == 'US English':
languageType = 'en-US'
languageName = 'English'
return languageType, languageName

def audioSpeechRecognition( self, event ):
if os.path.isfile(self.wavPath):
# 创建speech_recognition语音识别对象asrObj
asrObj = speech_recognition.Recognizer()
# 获取wav文件里的语音内容
with speech_recognition.AudioFile(self.wavPath) as source:
speechAudio = asrObj.record(source)
self.m_textCtrl_asrttsText.Clear()
# 获取语音语言类型(English/Chinese)
languageType, languageName = self.getLanguageSelection()
engineType = self.m_choice_asrEngine.GetString(self.m_choice_asrEngine.GetSelection())
if engineType == 'CMU Sphinx':
try:
# 调用recognize_sphinx完成语音识别
speechText = asrObj.recognize_sphinx(speechAudio, language=languageType)
# 语音识别结果显示在asrttsText文本框内
self.m_textCtrl_asrttsText.write(speechText)
self.statusBar.SetStatusText("ASR Conversation Info: Successfully")
# 语音识别结果写入指定文件
fileName = self.m_textCtrl_asrFileName.GetLineText(0)
if fileName == '':
fileName = 'asr_untitled1.txt'
asrFilePath = os.path.join(os.path.dirname(os.path.abspath(os.path.dirname(__file__))), 'conv', 'asr', fileName)
asrFileObj = open(asrFilePath, 'wb')
asrFileObj.write(speechText)
asrFileObj.close()
except speech_recognition.UnknownValueError:
self.statusBar.SetStatusText("ASR Conversation Info: Sphinx could not understand audio")
except speech_recognition.RequestError as e:
self.statusBar.SetStatusText("ASR Conversation Info: Sphinx error; {0}".format(e))
else:
self.statusBar.SetStatusText("ASR Conversation Info: Unavailable ASR Engine")


(6) - 文语合成


  大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是语音处理工具pzh-py-speech诞生之文语合成实现

  文语合成是pzh-py-speech的核心功能,pzh-py-speech借助的是pyttsx3以及eSpeak引擎来实现的文语合成功能,今天痞子衡为大家介绍文语合成在pzh-py-speech中是如何实现的。

一、pyttsx3简介

  pyttsx3是一套基于实现SAPI5文语合成引擎的Python封装库,该库的设计者为Natesh M Bhat,该库其实是 pyTTS 和 pyttsx 项目的延续,pyttsx3主要是为Python3版本设计的,但同时也兼容Python2。JaysPySPEECH使用的是pyttsx3 2.7。
  pyttsx3系统的官方主页如下:

  • pyttsx3官方主页: https://github.com/nateshmbhat/pyttsx3

  • pyttsx3安装方法: https://pypi.org/project/pyttsx3/

  pyttsx3的使用足够简单,其官方文档 https://pyttsx3.readthedocs.io/en/latest/engine.html 半小时即可读完,下面是最简单的一个示例代码:

import pyttsx3;

engine = pyttsx3.init();
engine.say("I will speak this text");
engine.runAndWait() ;

1.1 Microsoft Speech API (SAPI5)引擎

  前面痞子衡讲了pyttsx3基于的文语合成内核是SAPI5引擎,这是微软公司开发的TTS引擎,其官方主页如下:

  • SAPI5官方文档: https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ms723627(v%3dvs.85)

  由于pyttsx3已经将SAPI5封装好,所有我们没有必要关注SAPI5本身的TTS实现原理。

1.2 确认PC支持的语音包

  在使用pyttsx3进行文语合成时,依赖的是当前PC的语音环境,打开控制面板(Control Panel)->语言识别(Speech Recognition),可见到如下页面:

  痞子衡使用的PC是Win10英文版,故默认仅有英文语音包(David是男声,Zira是女声),这点也可以使用如下pyttsx3调用代码来确认:

import pyttsx3;

ttsObj = pyttsx3.init()
voices = ttsObj.getProperty('voices')
for voice in voices:
print ('id = {} \nname = {} \n'.format(voice.id, voice.name))

  代码运行结果如下:

id = HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens\TTS_MS_EN-US_DAVID_11.0
name = Microsoft David Desktop - English (United States)

id = HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens\TTS_MS_EN-US_ZIRA_11.0
name = Microsoft Zira Desktop - English (United States)

1.3 为PC增加语音包支持

  要想在使用pzh-py-speech时可以实现中英双语合成,要确保PC上既有英文语音包也有中文语音包,痞子衡PC上当前仅有英文语音包,故需要安装中文语音包(安装其他语言语音包的方法类似)。
  Windows系统下中文语音包有很多,可以使用第三方公司提供的语音包(比如 NeoSpeech公司 ),也可以使用微软提供的语音包,痞子衡选用的是经典的慧慧语音包(zh-CN_HuiHui)。
  进入 Microsoft Speech Platform - Runtime (Version 11) 和 Microsoft Speech Platform - Runtime Languages (Version 11) 下载页面将选中文件下载(亲测仅能用Google Chrome浏览器才能正常访问,IE竟然也无法打开):

  先安装SpeechPlatformRuntime.msi(双击安装即可),安装完成之后重启电脑,再安装MSSpeech_TTS_zh-CN_HuiHui.msi,安装结束之后需要修改注册表,打开Run(Win键+R键)输入"regedit"即可看到如下registry编辑界面,HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices路径下可以看到默认语音包(DAVID, ZIRA),HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech Server\v11.0\Voices路径下可看到新安装的语音包(HuiHui):

  右键HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech Server\v11.0\Voices,将其导出成.reg文件,使用文本编辑器打开这个.reg文件将其中"\Speech Server\v11.0"全部替换成"\Speech"并保存,然后将这个修改后的.reg文件再导入注册表。

  导入成功后,便可在注册表和语音识别选项里看到Huihui身影:

Note: 上述修改仅针对32bit操作系统,如果是64bit系统,需要同时将HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Speech Server\v11.0\Voices路径的注册表按同样方法也操作一遍。

二、eSpeak简介

  由于pyttsx3仅能在线发声,无法将合成后的语音保存为wav文件,因此痞子衡需要为JaysPySPEECH再寻一款可以保存为wav的TTS引擎。痞子衡选中的是eSpeak,eSpeak是一个简洁的开源语音合成软件,用C语言写成,支持英语和其他很多语言,同时也支持SAPI5接口,合成的语音可以导出为wav文件。
  eSpeak的官方主页如下:

  • eSpeak官方主页: http://espeak.sourceforge.net/

  • eSpeak下载安装: http://espeak.sourceforge.net/download.html

  • eSpeak补充语言包: http://espeak.sourceforge.net/data/index.html

  eSpeak从标准输入或者输入文件中读取文本,虽然语音输出与真人声音相去甚远,但是在项目需要的时候,eSpeak仍不失为一个简便快捷的工具。
  痞子衡将eSpeak 1.48.04安装在了C:\tools_mcu\eSpeak路径下,进入这个路径可以找到\eSpeak\command_line\espeak.exe,这便是我们需要调用的工具,为了方便调用,你需要将"C:\tools_mcu\eSpeak\command_line"路径加入系统环境变量Path中。
  关于中文支持,在\eSpeak\espeak-data\zh_dict文件里已经包含了基本的中文字符,但是如要想要完整的中文支持,还需要下载zh_listx.zip中文语音包,解压后将里面的zh_listx文件放到\eSpeak\dictsource目录下,并且在\eSpeak\dictsource路径下执行命令"espeak --compile=zh",执行成功后可以看到\eSpeak\espeak-data\zh_dict文件明显变大了。
  eSpeak对于python来说是个外部程序,我们需要借助subprocess来调用espeak.exe,下面是示例代码:

import subprocess
import sys
reload(sys)
sys.setdefaultencoding('utf-8')

enText = "Hello world"
zhText = u"你好世界"
txtFile = "C:/test.txt" #文件内为中文
wavFile = "C:/test.wav"

# 在线发音(-v是设置voice,en是英文,m3男声,zh是中文,f3是女声)
subprocess.call(["espeak", "-ven+m3", enText])
subprocess.call(["espeak", "-vzh+f3", zhText])
# 保存为wav文件(第一种方法仅能保存英文wav,如果想保存其他语言wav需要使用第二种方法)
subprocess.call(["espeak","-w"+wavFile, enText])
subprocess.call(["espeak","-vzh+f3", "-f"+txtFile, "-w"+wavFile])

  如果想直接体验eSpeak的发音质量,可以直接打开\eSpeak\TTSApp.exe应用程序,软件使用非常简单:

三、pzh-py-speech文语合成实现

  文语合成实现主要分为两部分:TTS, TTW。实现TTS需要import pyttsx3,实现TTW需要借助subprocess调用eSpeak,下面 痞子衡分别介绍这两部分的实现:

3.1 Text-to-Speech实现

  TTS代码实现其实很简单,目前仅实现了pyttsx3引擎,并且仅支持中英双语识别。具体到pzh-py-speech上主要是实现GUI界面上"TTS"按钮的回调函数,即textToSpeech(),如果用户选定了配置参数(语言类型、发音人类型、TTS引擎类型),并点击了"TTS"按钮,此时便会触发textToSpeech()的执行。代码如下:

reload(sys)
sys.setdefaultencoding('utf-8')
import pyttsx3

class mainWin(win.speech_win):

def __init__(self, parent):
# ...
self.ttsObj = None

def refreshVoice( self, event ):
languageType, languageName = self.getLanguageSelection()
engineType = self.m_choice_ttsEngine.GetString(self.m_choice_ttsEngine.GetSelection())
if engineType == 'pyttsx3 - SAPI5':
if self.ttsObj == None:
self.ttsObj = pyttsx3.init()
voices = self.ttsObj.getProperty('voices')
voiceItems = [None] * len(voices)
itemIndex = 0
for voice in voices:
voiceId = voice.id.lower()
voiceName = voice.name.lower()
if (voiceId.find(languageType.lower()) != -1) or (voiceName.find(languageName.lower()) != -1):
voiceItems[itemIndex] = voice.name
itemIndex += 1
voiceItems = voiceItems[0:itemIndex]
self.m_choice_voice.Clear()
self.m_choice_voice.SetItems(voiceItems)
else:
voiceItem = ['N/A']
self.m_choice_voice.Clear()
self.m_choice_voice.SetItems(voiceItem)

def textToSpeech( self, event ):
# 获取语音语言类型(English/Chinese)
languageType, languageName = self.getLanguageSelection()
# 从asrttsText文本框获取要转换的文本
lines = self.m_textCtrl_asrttsText.GetNumberOfLines()
if lines != 0:
data = ''
for i in range(0, lines):
data += self.m_textCtrl_asrttsText.GetLineText(i)
else:
return
ttsEngineType = self.m_choice_ttsEngine.GetString(self.m_choice_ttsEngine.GetSelection())
if ttsEngineType == 'pyttsx3 - SAPI5':
# 尝试创建pyttsx3文语合成对象ttsObj
if self.ttsObj == None:
self.ttsObj = pyttsx3.init()
# 搜索当前PC是否存在指定语言类型的发声人
hasVoice = False
voices = self.ttsObj.getProperty('voices')
voiceSel = self.m_choice_voice.GetString(self.m_choice_voice.GetSelection())
for voice in voices:
#print ('id = {} \nname = {} \nlanguages = {} \n'.format(voice.id, voice.name, voice.languages))
voiceId = voice.id.lower()
voiceName = voice.name.lower()
if (voiceId.find(languageType.lower()) != -1) or (voiceName.find(languageName.lower()) != -1):
if (voiceSel == '') or (voiceSel == voice.name):
hasVoice = True
break
if hasVoice:
# 调用pyttsx3里的say()和runAndWait()完成文语合成,直接在线发音
self.ttsObj.setProperty('voice', voice.id)
self.ttsObj.say(data)
self.statusBar.SetStatusText("TTS Conversation Info: Run and Wait")
self.ttsObj.runAndWait()
self.statusBar.SetStatusText("TTS Conversation Info: Successfully")
else:
self.statusBar.SetStatusText("TTS Conversation Info: Language is not supported by current PC")
self.textToWav(data, languageType)
else:
self.statusBar.SetStatusText("TTS Conversation Info: Unavailable TTS Engine")

3.2 Text-to-Wav实现

  TTW代码实现也很简单,目前仅实现了eSpeak引擎,并且仅支持中英双语识别。具体到pzh-py-speech上主要是实现GUI界面上"TTW"按钮的回调函数,即textToWav(),如果用户选定了配置参数(发音人性别类型、TTW引擎类型),并点击了"TTW"按钮,此时便会触发textToWav()的执行。代码如下:

import subprocess

class mainWin(win.speech_win):

def textToWav(self, text, language):
fileName = self.m_textCtrl_ttsFileName.GetLineText(0)
if fileName == '':
fileName = 'tts_untitled1.wav'
ttsFilePath = os.path.join(os.path.dirname(os.path.abspath(os.path.dirname(__file__))), 'conv', 'tts', fileName)
ttwEngineType = self.m_choice_ttwEngine.GetString(self.m_choice_ttwEngine.GetSelection())
if ttwEngineType == 'eSpeak TTS':
ttsTextFile = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'ttsTextTemp.txt')
ttsTextFileObj = open(ttsTextFile, 'wb')
ttsTextFileObj.write(text)
ttsTextFileObj.close()
try:
#espeak_path = "C:/tools_mcu/eSpeak/command_line/espeak.exe"
#subprocess.call([espeak_path, "-v"+languageType[0:2], text])
gender = self.m_choice_gender.GetString(self.m_choice_gender.GetSelection())
gender = gender.lower()[0] + '3'
# 调用espeak.exe完成文字到wav文件的转换
subprocess.call(["espeak", "-v"+language[0:2]+'+'+gender, "-f"+ttsTextFile, "-w"+ttsFilePath])
except:
self.statusBar.SetStatusText("TTW Conversation Info: eSpeak is not installed or its path is not added into system environment")
os.remove(ttsTextFile)
else:
self.statusBar.SetStatusText("TTW Conversation Info: Unavailable TTW Engine")

  至此,语音处理工具pzh-py-speech诞生之文语合成实现痞子衡便介绍完毕了,掌声在哪里~~~


本文授权转载自公众号“痞子衡嵌入式”,作者痞子衡

嵌入式ARM 关注这个时代最火的嵌入式ARM,你想知道的都在这里。
评论
  • RGB灯光无法同步?细致的动态光效设定反而成为产品客诉来源!随着科技的进步和消费者需求变化,电脑接口设备单一功能性已无法满足市场需求,因此在产品上增加「动态光效」的形式便应运而生,藉此吸引消费者目光。这种RGB灯光效果,不仅能增强电脑周边产品的视觉吸引力,还能为用户提供个性化的体验,展现独特自我风格。如今,笔记本电脑、键盘、鼠标、鼠标垫、耳机、显示器等多种电脑接口设备多数已配备动态光效。这些设备的灯光效果会随着音乐节奏、游戏情节或使用者的设置而变化。想象一个画面,当一名游戏玩家,按下电源开关,整
    百佳泰测试实验室 2025-02-27 14:15 138浏览
  • 在物联网领域中,无线射频技术作为设备间通信的核心手段,已深度渗透工业自动化、智慧城市及智能家居等多元场景。然而,随着物联网设备接入规模的不断扩大,如何降低运维成本,提升通信数据的传输速度和响应时间,实现更广泛、更稳定的覆盖已成为当前亟待解决的系统性难题。SoC无线收发模块-RFM25A12在此背景下,华普微创新推出了一款高性能、远距离与高性价比的Sub-GHz无线SoC收发模块RFM25A12,旨在提升射频性能以满足行业中日益增长与复杂的设备互联需求。值得一提的是,RFM25A12还支持Wi-S
    华普微HOPERF 2025-02-28 09:06 145浏览
  • 更多生命体征指标风靡的背后都只有一个原因:更多人将健康排在人生第一顺位!“AGEs,也就是晚期糖基化终末产物,英文名Advanced Glycation End-products,是存在于我们体内的一种代谢产物” 艾迈斯欧司朗亚太区健康监测高级市场经理王亚琴说道,“相信业内的朋友都会有关注,最近该指标的热度很高,它可以用来评估人的生活方式是否健康。”据悉,AGEs是可穿戴健康监测领域的一个“萌新”指标,近来备受关注。如果站在学术角度来理解它,那么AGEs是在非酶促条件下,蛋白质、氨基酸
    艾迈斯欧司朗 2025-02-27 14:50 400浏览
  • 一、VSM的基本原理震动样品磁强计(Vibrating Sample Magnetometer,简称VSM)是一种灵敏且高效的磁性测量仪器。其基本工作原理是利用震动样品在探测线圈中引起的变化磁场来产生感应电压,这个感应电压与样品的磁矩成正比。因此,通过测量这个感应电压,我们就能够精确地确定样品的磁矩。在VSM中,被测量的样品通常被固定在一个震动头上,并以一定的频率和振幅震动。这种震动在探测线圈中引起了变化的磁通量,从而产生了一个交流电信号。这个信号的幅度和样品的磁矩有着直接的关系。因此,通过仔细
    锦正茂科技 2025-02-28 13:30 100浏览
  • 在2024年的科技征程中,具身智能的发展已成为全球关注的焦点。从实验室到现实应用,这一领域正以前所未有的速度推进,改写着人类与机器的互动边界。这一年,我们见证了具身智能技术的突破与变革,它不仅落地各行各业,带来新的机遇,更在深刻影响着我们的生活方式和思维方式。随着相关技术的飞速发展,具身智能不再仅仅是一个技术概念,更像是一把神奇的钥匙。身后的众多行业,无论愿意与否,都像是被卷入一场伟大变革浪潮中的船只,注定要被这股汹涌的力量重塑航向。01为什么是具身智能?为什么在中国?最近,中国具身智能行业的进
    艾迈斯欧司朗 2025-02-28 15:45 222浏览
  • 1,微软下载免费Visual Studio Code2,安装C/C++插件,如果无法直接点击下载, 可以选择手动install from VSIX:ms-vscode.cpptools-1.23.6@win32-x64.vsix3,安装C/C++编译器MniGW (MinGW在 Windows 环境下提供类似于 Unix/Linux 环境下的开发工具,使开发者能够轻松地在 Windows 上编写和编译 C、C++ 等程序.)4,C/C++插件扩展设置中添加Include Path 5,
    黎查 2025-02-28 14:39 141浏览
  • 应用趋势与客户需求,AI PC的未来展望随着人工智能(AI)技术的日益成熟,AI PC(人工智能个人电脑)逐渐成为消费者和企业工作中的重要工具。这类产品集成了最新的AI处理器,如NPU、CPU和GPU,并具备许多智能化功能,为用户带来更高效且直观的操作体验。AI PC的目标是提升工作和日常生活的效率,通过深度学习与自然语言处理等技术,实现更流畅的多任务处理、实时翻译、语音助手、图像生成等功能,满足现代用户对生产力和娱乐的双重需求。随着各行各业对数字转型需求的增长,AI PC也开始在各个领域中显示
    百佳泰测试实验室 2025-02-27 14:08 255浏览
  • 振动样品磁强计是一种用于测量材料磁性的精密仪器,广泛应用于科研、工业检测等领域。然而,其测量准确度会受到多种因素的影响,下面我们将逐一分析这些因素。一、温度因素温度是影响振动样品磁强计测量准确度的重要因素之一。随着温度的变化,材料的磁性也会发生变化,从而影响测量结果的准确性。因此,在进行磁性测量时,应确保恒温环境,以减少温度波动对测量结果的影响。二、样品制备样品的制备过程同样会影响振动样品磁强计的测量准确度。样品的形状、尺寸和表面处理等因素都会对测量结果产生影响。为了确保测量准确度,应严格按照规
    锦正茂科技 2025-02-28 14:05 134浏览
  •           近日受某专业机构邀请,参加了官方举办的《广东省科技创新条例》宣讲会。在与会之前,作为一名技术工作者一直认为技术的法例都是保密和侵权方面的,而潜意识中感觉法律有束缚创新工作的进行可能。通过一个上午学习新法,对广东省的科技创新有了新的认识。广东是改革的前沿阵地,是科技创新的沃土,企业是创新的主要个体。《广东省科技创新条例》是广东省为促进科技创新、推动高质量发展而制定的地方性法规,主要内容包括: 总则:明确立法目
    广州铁金刚 2025-02-28 10:14 103浏览
  • Matter 协议,原名 CHIP(Connected Home over IP),是由苹果、谷歌、亚马逊和三星等科技巨头联合ZigBee联盟(现连接标准联盟CSA)共同推出的一套基于IP协议的智能家居连接标准,旨在打破智能家居设备之间的 “语言障碍”,实现真正的互联互通。然而,目标与现实之间总有落差,前期阶段的Matter 协议由于设备支持类型有限、设备生态协同滞后以及设备通信协议割裂等原因,并未能彻底消除智能家居中的“设备孤岛”现象,但随着2025年的到来,这些现象都将得到完美的解决。近期,
    华普微HOPERF 2025-02-27 10:32 216浏览
  •         近日,广电计量在聚焦离子束(FIB)领域编写的专业著作《聚焦离子束:失效分析》正式出版,填补了国内聚焦离子束领域实践性专业书籍的空白,为该领域的技术发展与知识传播提供了重要助力。         随着芯片技术不断发展,芯片的集成度越来越高,结构也日益复杂。这使得传统的失效分析方法面临巨大挑战。FIB技术的出现,为芯片失效分析带来了新的解决方案。它能够在纳米尺度上对芯片进行精确加工和分析。当芯
    广电计量 2025-02-28 09:15 116浏览
  • 美国加州CEC能效跟DOE能效有什么区别?CEC/DOE是什么关系?美国加州CEC能效跟DOE能效有什么区别?CEC/DOE是什么关系?‌美国加州CEC能效认证与美国DOE能效认证在多个方面存在显著差异‌。认证范围和适用地区‌CEC能效认证‌:仅适用于在加利福尼亚州销售的电器产品。CEC认证的范围包括制冷设备、房间空调、中央空调、便携式空调、加热器、热水器、游泳池加热器、卫浴配件、光源、应急灯具、交通信号模块、灯具、洗碗机、洗衣机、干衣机、烹饪器具、电机和压缩机、变压器、外置电源、消费类电子设备
    张工nx808593 2025-02-27 18:04 120浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦