简介
我们的目标是对音频应用一个砖墙式 10 kHz 高通滤波器,然后将其保存。音频连续录制并在 5 秒内保存到单独的 .wav 文件中。
到目前为止我们所拥有的
目前当前脚本:
- 声明一个函数来应用一个巴特沃斯高通滤波器 (
butter_high-pass_filter),其输出是一个浮点值数组
data_chunk=array('h',data)
data_chunk = butter_high-pass_filter(data_chunk,10000,RATE)
-
data_chunk 从未使用过,因此从不将高通过音频帧保存到文件中(错误 2)
- 数据被读取 5 秒值得音频
frames=[]
for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
data = stream.read(CHUNK)
frames.append(data)
- stream.read 被阻塞,所以这将等到正确数量的音频被读取。
- 然后将收集到的数据以相同格式写入
wav 文件
words = ["RECORDING-", time.strftime("%Y%m%d-%H%M%S"), ".wav"]
FILE_NAME= "".join(words)
wavfile=wave.open(FILE_NAME,'wb')
wavfile.setnchannels(CHANNELS)
wavfile.setsampwidth(audio.get_sample_size(FORMAT))
wavfile.setframerate(RATE)
wavfile.writeframes(b''.join(frames))
wavfile.close()
解决方案
这里的问题是解决方案是多方面的,并且需要当前脚本中当前缺少的多个部分。
此外,为了避免进一步的复杂化,需要采用与最初预期的方法略有不同的方法。与实时应用过滤器不同,过滤器可以在保存之前简单地应用于wav 样本数据。
这个
- 无需处理过滤器状态连续性
- 限制在数据类型之间来回转换的需要
此外,外部永远while 循环已被删除。时间和记忆开始成为一个问题。该程序可以简单地一遍又一遍地重新运行,直到用例覆盖更加清晰
- 为什么必须对音频进行高通滤波?
- 为什么在记录所有数据(即应用于
wav 文件后)不能进行过滤?
- 为什么必须保存数据?
- 数据格式有限制吗?位深度?采样率?
在回答这些问题之前,有太多可能的路线,每条路线都有限制,实际上可以在一个答案中涵盖。
故障
流程的完整细分将是
- 声明一个函数,将浮点样本数据作为输入,将浮点数据作为输出的高通滤波器
- 将来自 pyaudio 的 5 秒字节字符串数据连接到单个变量中
- 将数据解压缩为 16 位(有符号短)格式的样本数组
- 将样本缩放为 1.0 到 -1.0 之间的浮点格式
- 将数据发送到高通滤波器
- 在 16 位(有符号短)格式范围内缩放过滤器样本
- 将 16 位过滤样本打包成一个字节串
- 将过滤后的字节串数据写入 wav 文件。
脚本
import pyaudio, wave, time, sys, os, struct
from array import array
from scipy import signal
# 1. Declare a function that takes floating point sample data as input and high pass
# filters with floating point data as output
# 2. concatenate 5 seconds of byte-string data from PyAudio into a single variable
# 3. unpack data as a 16-bit (signed short) format array of samples
# 4. scale samples to floating point format between 1.0 and -1.0
# 5. send data to high pass filter
# 6. scale filter samples in the range of 16-bit (signed short) format
# 7. pack 16-bit filtered samples into a byte string
# 8. write filtered byte-string data to a wav file.
# ---------------------------------------------------------------------
# 1. Declare High Pass Filter
def butter_highpass(cutoff: float, fs: float, order: int = 5) -> tuple:
"""
Generate FIR and IIR coefficients for a butterworth highpass filter
:param cutoff: cutoff frequency (hz)
:param fs: sampling rate
:param order: filter order
:return: tuple of filter coefficients
"""
nyq = 0.5 * fs
normal_cutoff = cutoff / nyq
b, a = signal.butter(order, normal_cutoff, btype='high', analog=False)
return b, a
def butter_highpass_filter(data: [float], cutoff: float, fs: float, order: int = 5) -> [float]:
"""
apply a butterworth high pass filter to sample data in floating point format
:param data: float sample data array
:param cutoff: filter cutoff (hz)
:param fs: sample data sampling rate
:param order: filter order
:return: floating point array of filtered sample data
"""
b, a = butter_highpass(cutoff, fs, order=order)
y = signal.filtfilt(b, a, data)
return y
# ---------------------------------------------------------------------
# Init Global Variables
FORMAT = pyaudio.paInt16
CHANNELS = 1
CHUNK = 1024
RATE = 44100
RECORD_SECONDS = 5
audio = pyaudio.PyAudio()
stream = audio.open(format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
frames_per_buffer=CHUNK)
# ---------------------------------------------------------------------
# Main Program
if __name__ == '__main__':
# ---------------------------------------------------------------------
# 2. concat 5 seconds of data into a single string
frames = b''
for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
frames += stream.read(CHUNK)
# ---------------------------------------------------------------------
# 3. Unpack data as a 16 - bit
sample_data = array('h', frames)
# ---------------------------------------------------------------------
# 4. scale samples to floating point format between 1.0 and -1.0
samples = [sample_datum / (2**15) for sample_datum in sample_data]
print("Max Amplitude:", max(samples))
# ---------------------------------------------------------------------
# 5. send data to high pass filter
filtered_sample_data = butter_highpass_filter(samples, 10000.0, RATE)
# filtered_sample_data = samples
# ---------------------------------------------------------------------
# 6. scale filter samples in the range of 16-bit (signed short) format
# (2 ** 14) for headroom (very lazy)
sample_data_16_bit = [int(sample * (2 ** 14)) for sample in filtered_sample_data]
# # ---------------------------------------------------------------------
# # 7. pack 16-bit filtered samples into a byte string
raw_data = [struct.pack('h', sample) for sample in sample_data_16_bit]
# # ---------------------------------------------------------------------
# # 8. Write Wav
file_name = "".join(["RECORDING-", time.strftime("%Y%m%d-%H%M%S"), ".wav"])
wavfile = wave.open(file_name, 'wb')
wavfile.setnchannels(CHANNELS)
wavfile.setsampwidth(audio.get_sample_size(FORMAT))
wavfile.setframerate(RATE)
wavfile.writeframes(b''.join(raw_data))
wavfile.close()
评论
潜在的添加可能包括使用matplotlib 绘制频谱、标准化过滤后的音频、将过程封装到自定义函数中,但这些都留给 OP 作为练习。
以下是标准化后的音频频谱屏幕截图。正如@marco 在 cmets 中所引用的那样,过滤器确实有一个斜率,这是预期的。这可以通过增加过滤器的阶数来改善。