大家好,我是痞子衡,是正經搞技術的痞子,今天痞子衡給大家介紹的是語音處理工具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 = https://www.cnblogs.com/henjay724/p/wf.readframes(CHUNK)
while data !='':
stream.write(data)
data = https://www.cnblogs.com/henjay724/p/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 = https://www.cnblogs.com/henjay724/p/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 = https://www.cnblogs.com/henjay724/p/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誕生之音頻錄播實作痞子衡便介紹完畢了,掌聲在哪里~~~
參考檔案
- Python決議Wav檔案并繪制波形的方法
歡迎訂閱
文章會同時發布到我的 博客園主頁、CSDN主頁、微信公眾號 平臺上,
微信搜索"痞子衡嵌入式"或者掃描下面二維碼,就可以在手機上第一時間看了哦,

轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/17240.html
標籤:嵌入式
