Python工具箱系列(六十二)
对录音文件进行操作
在参加会议、论坛时经常会有录音的需求。但限于条件,经常会出现录音的声音小需要增强,无关内容需要剪切等需求。此时,就需要对音频文件进行变换。以下代码演示了部分变换功能。
from pydub import AudioSegment
from pydub.silence import detect_silence, detect_nonsilent
from pathlib import Path
import glob
import matplotlib.pyplot as plt
import librosa
import librosa.display
from librosa.display import waveshow
def add_silence(audiofilename, silence_length, output_file):
"""
给音频文件头增加一段静音
Args:
audiofilename (string): 音频文件名
silence_length (int): 静音的秒数
output_file (string): 输出的音频文件名
"""
one_sec_segment = AudioSegment.silent(duration=silence_length*1000)
sound = AudioSegment.from_file(audiofilename)
final_song = one_sec_segment + sound
final_song.export(output_file, format="wav")
def cut(audiofilename, start, duration, outputfilename):
"""
从音频文件中切割一段并且输出为文件
Args:
audiofilename (string): 音频文件名
start (int): 开始时间(秒)
duration (int): 持续时间(秒)
output_file (string): 输出的音频文件名
"""
sound = AudioSegment.from_file(audiofilename)
print(sound.duration_seconds, sound.dBFS, sound.frame_rate)
cutstart = start*1000
cutend = (start+duration)*1000
clip = sound[cutstart:cutend]
clip.export(outputfilename)
def cutbynonesilent(audiofilename):
"""
从音频文件中切割所有非静音段,并且输出成系列文件
Args:
audiofilename (string): 音频文件名
"""
sound = AudioSegment.from_file(audiofilename)
# 非静音检测,按最少2秒,-50DB为分隔标准
start_end_list = detect_nonsilent(sound, 2000, -50, 1)
# 将非静音时段的音频单独输出
pth = Path(audiofilename)
for index, seg in enumerate(start_end_list):
# 组合输出文件名
outputfilename = Path.joinpath(pth.parent, f'{pth.stem}-{index}.wav')
clip = sound[seg[0]:seg[1]]
clip.export(outputfilename)
def continuous_mix(filelist, outputfilename):
"""
串烧
Args:
filelist (list): 要串烧的文件列表
outputfilename (string): 输出文件
"""
one_sec_segment = AudioSegment.silent(duration=2*1000)
mixer = AudioSegment.from_file(filelist[0])
for file in filelist[1:]:
# 在头上合成时加入一段静音进行分隔。
clip = AudioSegment.from_file(file)
mixer = mixer + one_sec_segment + clip
# 渐入与渐出
mixer.fade_in(1000).fade_out(1000)
mixer.export(outputfilename)
def volum(audiofilename,factor):
"""
放大或者缩小音量
Args:
audiofilename (string): 音频文件名
factor (int): 正值为放大的分贝数,负值为缩小的分贝数
"""
pth = Path(audiofilename)
outputfilename = Path.joinpath(pth.parent, f'{pth.stem}-gain{pth.suffix}')
sound = AudioSegment.from_file(audiofilename)
sound += factor
sound.export(outputfilename)
def test_mixer():
# 串烧指定目录下的所有MP3文件
globfiles = glob.iglob(r'd:\test\mp3\*.mp3')
continuous_mix(list(globfiles), r'd:\test\done\mixer.mp3')
def test_cut():
targetfilename = r'D:\test\m4a\1.m4a'
cut(targetfilename, 10, 20, r'd:\test\cut1.wav')
cutbynonesilent(targetfilename)
def test_volum():
originaudio = r'D:\test\m4a\1-0.wav'
changedaudio = r'D:\test\m4a\1-0-gain.wav'
volum(originaudio,20)
# plt.figure(figsize=(15,17))
fig, ax = plt.subplots(nrows=2, sharex=True)
origin ,sr = librosa.load(originaudio)
librosa.display.waveshow(origin,sr=sr,ax=ax[0])
ax[0].set(title='origin audio wave')
ax[0].label_outer()
changed ,sr = librosa.load(changedaudio)
librosa.display.waveshow(changed,sr=sr,ax=ax[1])
ax[1].set(title='changed audio wave')
ax[1].label_outer()
plt.show()
test_mixer()
test_cut()
test_volum()
上述代码中:
-
综合使用了librosa/pydub两种声音处理库,各取所长。librosa能够在更底层处理音频文件,进行各类变换。
-
add_silence函数对指定的音频文件进行加工,在头部加入一段静音
-
cut函数对于指定的音频文件进行切割,能够将其中一段音频切出来后存为另一个音频文件
-
cutbynonesilent函数对于指定的音频文件进行切割,能够将非静音部分切割为不同的文件。这个功能对于录音来说非常必要。在现实的录音中,有可能存在大段的静音。切割后,可以针对每部分进行语音的加工(放大音量、语音转文字等)
-
continuous_mix函数是一个串烧的函数,能够将输入的文件列表中的文件依次合成在一起。形成一个串烧的效果,并且加入了静音,以及渐入渐出效果
-
volum函数对音频文件进行音量的增强与衰减。下图展示了音量变化的效果
最后再整理一下安装过程:
pip install pyaudio
pip install pydub
pip install librosa
pip install matlibplot
代码中列出的MP3/wav文件可以随便替换,不必纠结。