在下面,我有将从麦克风接收输入的代码,如果音频块的平均值超过某个阈值,它将产生音频块的频谱图(长度为30毫秒)。这是在正常对话过程中生成的频谱图的样子:
从我所看到的情况来看,这看起来并不像我期望的频谱图在给定音频及其环境的情况下那样。我期待更多类似以下内容(转置为节省空间):
我录制时使用的麦克风是Macbook上的默认麦克风,有什么问题的建议吗?
record.py:
import pyaudio
import struct
import math
import numpy as np
from scipy import signal
import matplotlib.pyplot as plt
THRESHOLD = 40 # dB
RATE = 44100
INPUT_BLOCK_TIME = 0.03 # 30 ms
INPUT_FRAMES_PER_BLOCK = int(RATE * INPUT_BLOCK_TIME)
def get_rms(block):
return np.sqrt(np.mean(np.square(block)))
class AudioHandler(object):
def __init__(self):
self.pa = pyaudio.PyAudio()
self.stream = self.open_mic_stream()
self.threshold = THRESHOLD
self.plot_counter = 0
def stop(self):
self.stream.close()
def find_input_device(self):
device_index = None
for i in range( self.pa.get_device_count() ):
devinfo = self.pa.get_device_info_by_index(i)
print('Device %{}: %{}'.format(i, devinfo['name']))
for keyword in ['mic','input']:
if keyword in devinfo['name'].lower():
print('Found an input: device {} - {}'.format(i, devinfo['name']))
device_index = i
return device_index
if device_index == None:
print('No preferred input found; using default input device.')
return device_index
def open_mic_stream( self ):
device_index = self.find_input_device()
stream = self.pa.open( format = pyaudio.paInt16,
channels = 1,
rate = RATE,
input = True,
input_device_index = device_index,
frames_per_buffer = INPUT_FRAMES_PER_BLOCK)
return stream
def processBlock(self, snd_block):
f, t, Sxx = signal.spectrogram(snd_block, RATE)
plt.pcolormesh(t, f, Sxx)
plt.ylabel('Frequency [Hz]')
plt.xlabel('Time [sec]')
plt.savefig('data/spec{}.png'.format(self.plot_counter), bbox_inches='tight')
self.plot_counter += 1
def listen(self):
try:
raw_block = self.stream.read(INPUT_FRAMES_PER_BLOCK, exception_on_overflow = False)
count = len(raw_block) / 2
format = '%dh' % (count)
snd_block = np.array(struct.unpack(format, raw_block))
except Exception as e:
print('Error recording: {}'.format(e))
return
amplitude = get_rms(snd_block)
if amplitude > self.threshold:
self.processBlock(snd_block)
else:
pass
if __name__ == '__main__':
audio = AudioHandler()
for i in range(0,100):
audio.listen()
根据评论进行编辑:
如果我们将速率限制为16000 Hz,并使用对数刻度作为色图,则这是在麦克风附近敲击的输出:
在我看来,这仍然有些奇怪,但也似乎是朝正确方向迈出的一步。
使用Sox并与我的程序生成的频谱图进行比较:
最佳答案
首先,观察您的代码在彼此之上最多绘制100个频谱图(如果processBlock
被多次调用),而您只能看到最后一个频谱图。您可能要修复该问题。此外,我假设您知道为什么要使用30ms录音。就个人而言,我想不出一个实际的应用,笔记本电脑麦克风记录的30ms可以提供有趣的见解。它取决于您正在录制的内容以及如何触发录制,但是此问题与实际问题有关。
否则,代码将完美运行。只需对processBlock
函数进行一些小改动,并应用一些背景知识,即可获得信息量大的和美观的声谱图。
因此,让我们谈谈实际的频谱图。我将SoX输出作为引用。彩条注释说它是dBFS
1,它是对数度量(dB是Decibel的缩写)。因此,让我们首先将频谱图转换为dB:
f, t, Sxx = signal.spectrogram(snd_block, RATE)
dBS = 10 * np.log10(Sxx) # convert to dB
plt.pcolormesh(t, f, dBS)
这改善了色阶。现在,我们看到了以前隐藏的较高频段中的噪声。接下来,让我们解决时间问题。频谱图将信号分成多个段(默认长度为256),并为每个段计算频谱。这意味着我们具有出色的频率分辨率,但时间分辨率却很差,因为只有少数这样的片段适合信号窗口(大约1300个样本长)。在时间分辨率和频率分辨率之间始终需要权衡取舍。这与uncertainty principle有关。因此,通过将信号分成较短的段,让我们将一些频率分辨率换成时间分辨率:
f, t, Sxx = signal.spectrogram(snd_block, RATE, nperseg=64)
大!现在我们在两个轴上都获得了相对平衡的分辨率-但请稍等!为什么结果如此像素化?实际上,这就是短30ms时间窗口中的所有信息。仅有1300种样本可以二维分布。但是,我们可以作弊,并使用较高的FFT分辨率和重叠的片段。尽管它不提供其他信息,但这会使结果更平滑:
f, t, Sxx = signal.spectrogram(snd_block, RATE, nperseg=64, nfft=256, noverlap=60)
看得出漂亮的频谱干涉图样。 (这些模式取决于所使用的窗口函数,在这里我们不做详细介绍。请参见光谱图函数的
window
参数来使用它们。)结果看起来不错,但实际上不包含任何比以前更多的信息。图片。为了使结果更接近SoX-lixe,请注意,SoX频谱图在时间轴上较为模糊。您可以通过使用原始的低时间分辨率(长片段)来获得此效果,但是为了平滑而重叠它们:
f, t, Sxx = signal.spectrogram(snd_block, RATE, noverlap=250)
我个人更喜欢第三个解决方案,但是您将需要找到自己喜欢的时间/频率权衡。
最后,让我们使用更类似于SoX的颜色图:
plt.pcolormesh(t, f, dBS, cmap='inferno')
对以下行的简短评论:
THRESHOLD = 40 # dB
将阈值与输入信号的RMS进行比较,该值不是以dB为单位,而是原始幅度单位。
1显然,FS是满量程的缩写。 dBFS表示dB量度是相对于最大范围的。 0 dB是当前表示形式中最大的信号,因此实际值必须
关于python - 从麦克风产生频谱图,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/43353172/