笔者最近在挑选开源的语音识别模型,首要测试的是百度的paddlepaddle;
测试之前,肯定需要了解一下音频解析的一些基本技术点,于是有此篇先导文章。
笔者看到的音频解析主要有几个:
soundfileffmpylibrosa
文章目录
1 librosa1.1 音频读入1.2 音频写出1.3 librosa 读入 + PySoundFile写出1.4 从其他库转为librosa格式2 PySoundFile2.1 读入音频2.2 导出音频3 ffmpy4 AudioSegment / pydub5 paddleaudio6 音频切分 - auditok7 一个比较难解决的报错8 从网址URL下载音频8.1 soundfile1 librosa
安装代码:
!pip install librosa -i /pypi/simple!pip install soundfile -i /pypi/simple
参考文档:librosa
1.1 音频读入
文档位置:/doc/latest/core.html#audio-loading
signal, sr = librosa.load(path, sr=None)
其中load的参数包括:
librosa.load(path, *, sr=22050, mono=True, offset=0.0, duration=None, dtype=<class 'numpy.float32'>, res_type='kaiser_best')
其中sr = None,‘None’ 保留原始采样频率,设置其他采样频率会进行重采样,有点耗时
可以读 .wav 和 .mp3;
1.2 音频写出
在网络上其他几篇:python音频采样率转换 和 python 音频文件采样率转换在导出音频文件时候,会出现错误,贴一下他们的代码
代码片段一:
def resample_rate(path,new_sample_rate = 16000):signal, sr = librosa.load(path, sr=None)wavfile = path.split('/')[-1]wavfile = wavfile.split('.')[0]file_name = wavfile + '_new.wav'new_signal = librosa.resample(signal, sr, new_sample_rate) # librosa.output.write_wav(file_name, new_signal , new_sample_rate)
代码片段二:
import librosaimport osnoise_name="/media/dfy/fc0b6513-c379-4548-b391-876575f1493f/home/dfy/PycharmProjects/noise_data/"noise_name_list=os.listdir(noise_name)for one_name in noise_name_list:data=librosa.load(noise_name+one_name,16000)librosa.output.write_wav(noise_name+one_name,data[0],16000,norm=False)if __name__ == '__main__':pass
上述都是使用librosa.output
进行导出,最新的librosa已经摒弃了这个函数。出现报错:
AttributeError: module librosa has no attribute output No module named numba.decorators错误解决
0.8.0版本的将output的api屏蔽掉了,所以要么就是librosa降低版本,比如到0.7.2,要么使用另外的方式。
于是来到官方文档:librosa
推荐使用write的方式,是使用这个库:PySoundFile
1.3 librosa 读入 + PySoundFile写出
如果出现报错:
Input audio file has sample rate [44100], but decoder expects [16000]
就是音频采样比不对,需要修改一下。
笔者将1+2的开源库结合,微调了python音频采样率转换 和 python 音频文件采样率转换,得到以下,切换音频采样频率的函数:
import librosaimport osimport numpy as npimport soundfile as sfdef resample_rate(path,new_sample_rate = 16000):signal, sr = librosa.load(path, sr=None)wavfile = path.split('/')[-1]wavfile = wavfile.split('.')[0]file_name = wavfile + '_new.wav'new_signal = librosa.resample(signal, sr, new_sample_rate) # #librosa.output.write_wav(file_name, new_signal , new_sample_rate) sf.write(file_name, new_signal, new_sample_rate, subtype='PCM_24')print(f'{file_name} has download.')# wav_file = 'video/xxx.wav'resample_rate(wav_file,new_sample_rate = 16000)
改变为sample_rate 为16000
的音频文件
1.4 从其他库转为librosa格式
参考:/doc/latest/generated/librosa.load.html#librosa.load
第一种:
# Load using an already open SoundFile objectimport soundfilesfo = soundfile.SoundFile(librosa.ex('brahms'))y, sr = librosa.load(sfo)
第二种:
# Load using an already open audioread objectimport audioread.ffdec # Use ffmpeg decoderaro = audioread.ffdec.FFmpegAudioFile(librosa.ex('brahms'))y, sr = librosa.load(aro)
2 PySoundFile
python-soundfile是一个基于libsndfile、CFFI和NumPy的音频库。
可以直接使用函数read()和write()来读写声音文件。要按块方式读取声音文件,请使用blocks()。另外,声音文件也可以作为SoundFile对象打开。
PySoundFile的官方文档:readthedocs
下载:
!pip install soundfile -i /pypi/simple
2.1 读入音频
read files from zip compressed archives:
import zipfile as zfimport soundfile as sfimport iowith zf.ZipFile('test.zip') as myzip:with myzip.open('stereo_file.wav') as myfile:tmp = io.BytesIO(myfile.read())data, samplerate = sf.read(tmp)
Download and read from URL:
import soundfile as sfimport iofrom six.moves.urllib.request import urlopenurl = "/librosa/librosa/master/tests/data/test1_44100.wav"data, samplerate = sf.read(io.BytesIO(urlopen(url).read()))
2.2 导出音频
导出音频的:
import numpy as npimport soundfile as sfrate = 44100data = np.random.uniform(-1, 1, size=(rate * 10, 2))# Write out audio as 24bit PCM WAVsf.write('stereo_file.wav', data, samplerate, subtype='PCM_24')# Write out audio as 24bit Flacsf.write('stereo_file.flac', data, samplerate, format='flac', subtype='PCM_24')# Write out audio as 16bit OGGsf.write('stereo_file.ogg', data, samplerate, format='ogg', subtype='vorbis')
3 ffmpy
Python 批量转换视频音频采样率(附代码) | Python工具
下载:
pip install ffmpy -i /simple
具体代码见原文,只截取其中一段:
def transfor(video_path: str, tmp_dir: str, result_dir: str):file_name = os.path.basename(video_path)base_name = file_name.split('.')[0]file_ext = file_name.split('.')[-1]ext = 'wav'audio_path = os.path.join(tmp_dir, '{}.{}'.format(base_name, ext))print('文件名:{},提取音频'.format(audio_path))ff = FFmpeg(inputs={video_path: None}, outputs={audio_path: '-f {} -vn -ac 1 -ar 16000 -y'.format('wav')})print(ff.cmd)ff.run()if os.path.exists(audio_path) is False:return Nonevideo_tmp_path = os.path.join(tmp_dir, '{}_1.{}'.format(base_name, file_ext))ff_video = FFmpeg(inputs={video_path: None},outputs={video_tmp_path: '-an'})print(ff_video.cmd)ff_video.run()result_video_path = os.path.join(result_dir, file_name)ff_fuse = FFmpeg(inputs={video_tmp_path: None, audio_path: None}, outputs={result_video_path: '-map 0:v -map 1:a -c:v copy -c:a aac -shortest'})print(ff_fuse.cmd)ff_fuse.run()return result_video_path
4 AudioSegment / pydub
参考文章:
Python | 语音处理 | 用 librosa / AudioSegment / soundfile 读取音频文件的对比
另外一篇对pydub的参数介绍:
pydub简单介绍
官网地址:pydub
from pydub import AudioSegment #需要导入pydub三方库,第一次使用需要安装audio_path = './data/example.mp3't = time.time()song = AudioSegment.from_file(audio_path, format='mp3')# print(len(song)) #时长,单位:毫秒# print(song.frame_rate) #采样频率,单位:赫兹# print(song.sample_width) #量化位数,单位:字节# print(song.channels) #声道数,常见的MP3多是双声道的,声道越多文件也会越大。wav = np.array(song.get_array_of_samples())sr = song.frame_rateprint(f"sr={sr}, len={len(wav)}, 耗时: {time.time()-t}")print(f"(min, max, mean) = ({wav.min()}, {wav.max()}, {wav.mean()})")wav
输出结果为:
sr=16000, len=64320, 耗时: 0.04667925834655762(min, max, mean) = (-872, 740, -0.6079446517412935)array([ 1, -1, -2, ..., -1, 1, -2], dtype=int16)
5 paddleaudio
安装:
! pip install paddleaudio -i /pypi/simple
paddle官方封装的一个,音频基本操作应该是librosa的库
具体参考:
https://paddleaudio-doc.readthedocs.io/en/latest/index.html
import paddleaudioaudio_file = 'XXX.wav'paddleaudio.load(audio_file, sr=None, mono=True, normal=False)
得出:
(array([-3.9100647e-04, -3.0159950e-05, 1.1110306e-04, ...,1.4603138e-04, 2.5625229e-03, -7.6780319e-03], dtype=float32),16000)
音频数值 + 采样率
6 音频切分 - auditok
参考的是:【超简单】之基于PaddleSpeech搭建个人语音听写服务
!pip install auditok
切分原因上面交代过,因为PaddleSpeech识别最长语音为50s,故需要切分,这里直接调用好了。
from paddlespeech.cli.asr.infer import ASRExecutorimport csvimport moviepy.editor as mpimport auditokimport osimport paddlefrom paddlespeech.cli import ASRExecutor, TextExecutorimport soundfileimport librosaimport warningswarnings.filterwarnings('ignore')# 引入auditok库import auditok# 输入类别为audiodef qiefen(path, ty='audio', mmin_dur=1, mmax_dur=100000, mmax_silence=1, menergy_threshold=55):audio_file = pathaudio, audio_sample_rate = soundfile.read(audio_file, dtype="int16", always_2d=True)audio_regions = auditok.split(audio_file,min_dur=mmin_dur, # minimum duration of a valid audio event in secondsmax_dur=mmax_dur, # maximum duration of an event# maximum duration of tolerated continuous silence within an eventmax_silence=mmax_silence,energy_threshold=menergy_threshold # threshold of detection)for i, r in enumerate(audio_regions):# Regions returned by `split` have 'start' and 'end' metadata fieldsprint("Region {i}: {r.meta.start:.3f}s -- {r.meta.end:.3f}s".format(i=i, r=r))epath = ''file_pre = str(epath.join(audio_file.split('.')[0].split('/')[-1]))mk = 'change'if (os.path.exists(mk) == False):os.mkdir(mk)if (os.path.exists(mk + '/' + ty) == False):os.mkdir(mk + '/' + ty)if (os.path.exists(mk + '/' + ty + '/' + file_pre) == False):os.mkdir(mk + '/' + ty + '/' + file_pre)num = i# 为了取前三位数字排序s = '000000' + str(num)file_save = mk + '/' + ty + '/' + file_pre + '/' + \s[-3:] + '-' + '{meta.start:.3f}-{meta.end:.3f}' + '.wav'filename = r.save(file_save)print("region saved as: {}".format(filename))return mk + '/' + ty + '/' + file_pre
其中核心的auditok.split
代码,参数详解在auditok.core.split ,其输入的是音频文件名,不能是音频的data格式。
7 一个比较难解决的报错
AudioParameterError: Sample width must be one of: 1, 2 or 4 (bytes)
笔者在跑语音模型的识别遇到了以上的报错,
但是网上找了一圈,没找到对的解决方案。
在快要放弃的时候,无意间看到AudioSegment
库的神奇功能。
Sample width
是什么?
取样量化位宽(sampwidth)
import wavefile ='asr_example.wav'with wave.open(file) as fp:channels = fp.getnchannels()srate = fp.getframerate()swidth = fp.getsampwidth()data = fp.readframes(-1)swidth,srate
通过wave
可以查询到一个音频的比较重要的几个参数。
分别为:
nchannels:声道数sampwidth:返回该实例每一帧的字节宽度。framerate:采样频率nframes:采样点数
那遇到上述报错就需要重新调整,这里AudioSegment
库直接有
from pydub import AudioSegmentfile_in ='asr_example.wav' # 输入的音频名称file_out = 'asr_example_3.wav' # 输出的音频名称sound = AudioSegment.from_file(file_in)sound = sound.set_frame_rate(48000) # 可以修改音频采样率sound = sound.set_sample_width(4) # 重新设置字节宽度sound.export(file_out, format="wav")
以上就可以完美解决。
8 从网址URL下载音频
几种读入方式:
8.1 soundfile
import soundfile as sfdef save_audio_func(video_url,save_samplerate = 16000):'''音频导出'''save_name = video_url.split('/')[-1]data, samplerate = sf.read(io.BytesIO(urlopen(video_url).read()))# Write out audio as 24bit PCM WAVsf.write(save_name, data, save_samplerate, subtype='PCM_24')#print('')return save_name
读入、读出都是通过soundfile