痞子衡嵌入式:语音处理工具pzhspeech诞生记(4)

编程


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

  音频录播是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()

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

参考文档

  1. Python解析Wav文件并绘制波形的方法

以上是 痞子衡嵌入式:语音处理工具pzhspeech诞生记(4) 的全部内容, 来源链接: utcz.com/z/513081.html

回到顶部