笔者最近在挑选开源的语音识别模型,首要测试的是百度的paddlepaddle;
测试之前,肯定需要了解一下音频解析的一些基本技术点,于是有此篇先导文章。
笔者看到的音频解析主要有几个:
- soundfile
- ffmpy
- librosa
1 librosa
安装代码:
!pip install librosa -i https://mirror.baidu.com/pypi/simple
!pip install soundfile -i https://mirror.baidu.com/pypi/simple
参考文档:librosa
1.1 音频读入
文档位置:https://librosa.org/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 librosa
import os
noise_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 librosa
import os
import numpy as np
import soundfile as sf
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)
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格式
参考:https://librosa.org/doc/latest/generated/librosa.load.html#librosa.load
第一种:
# Load using an already open SoundFile object
import soundfile
sfo = soundfile.SoundFile(librosa.ex('brahms'))
y, sr = librosa.load(sfo)
第二种:
# Load using an already open audioread object
import audioread.ffdec # Use ffmpeg decoder
aro = 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 https://mirror.baidu.com/pypi/simple
2.1 读入音频
read files from zip compressed archives:
import zipfile as zf
import soundfile as sf
import io
with 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 sf
import io
from six.moves.urllib.request import urlopen
url = "https://raw.githubusercontent.com/librosa/librosa/master/tests/data/test1_44100.wav"
data, samplerate = sf.read(io.BytesIO(urlopen(url).read()))
2.2 导出音频
导出音频的:
import numpy as np
import soundfile as sf
rate = 44100
data = np.random.uniform(-1, 1, size=(rate * 10, 2))
# Write out audio as 24bit PCM WAV
sf.write('stereo_file.wav', data, samplerate, subtype='PCM_24')
# Write out audio as 24bit Flac
sf.write('stereo_file.flac', data, samplerate, format='flac', subtype='PCM_24')
# Write out audio as 16bit OGG
sf.write('stereo_file.ogg', data, samplerate, format='ogg', subtype='vorbis')
3 ffmpy
Python 批量转换视频音频采样率(附代码) | Python工具
下载:
pip install ffmpy -i https://pypi.douban.com/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 None
video_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_rate
print(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 https://mirror.baidu.com/pypi/simple
paddle官方封装的一个,音频基本操作应该是librosa的库
具体参考:
https://paddleaudio-doc.readthedocs.io/en/latest/index.html
import paddleaudio
audio_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 ASRExecutor
import csv
import moviepy.editor as mp
import auditok
import os
import paddle
from paddlespeech.cli import ASRExecutor, TextExecutor
import soundfile
import librosa
import warnings
warnings.filterwarnings('ignore')
# 引入auditok库
import auditok
# 输入类别为audio
def qiefen(path, ty='audio', mmin_dur=1, mmax_dur=100000, mmax_silence=1, menergy_threshold=55):
audio_file = path
audio, 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 seconds
max_dur=mmax_dur, # maximum duration of an event
# maximum duration of tolerated continuous silence within an event
max_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 fields
print(
"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 wave
file ='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 AudioSegment
file_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 sf
def 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 WAV
sf.write(save_name, data, save_samplerate, subtype='PCM_24')
#print('')
return save_name
读入、读出都是通过soundfile
9 如何读取mp3
参考:https://blog.csdn.net/qq_37851620/article/details/127149729
soundfile.read:
只能读 .wav ,不能读 .mp3;
默认 dtype = ‘float64’,输出为 (-1, 1) 之间的数据 (做了 32768 归一化);修改为 dtype = ‘int16’,输出为 (-215, 215-1) 之间;
保留原始采样频率。
librosa.load:
可以读 .wav 和 .mp3;
输出为 (-1, 1);
sr=None 保留原始采样频率,设置其他采样频率会进行重采样,有点耗时;文章来源:https://www.toymoban.com/news/detail-787066.html
pydub.AudioSegment.from_file:
可以读 .wav 和 .mp3;
输出为 (-215, 215-1),手动除以32768(=2**15),可得到同2一样的结果;
保留原始采样频率,重采样可借助 librosa.resample。文章来源地址https://www.toymoban.com/news/detail-787066.html
到了这里,关于语音识别系列︱用python进行音频解析(一)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!