在下面,我有将从麦克风接收输入的代码,如果音频块的平均值超过某个阈值,它将产生音频块的频谱图(长度为30毫秒)。这是在正常对话过程中生成的频谱图的样子:

python - 从麦克风产生频谱图-LMLPHP

从我所看到的情况来看,这看起来并不像我期望的频谱图在给定音频及其环境的情况下那样。我期待更多类似以下内容(转置为节省空间):

python - 从麦克风产生频谱图-LMLPHP

我录制时使用的麦克风是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,并使用对数刻度作为色图,则这是在麦克风附近敲击的输出:

python - 从麦克风产生频谱图-LMLPHP

在我看来,这仍然有些奇怪,但也似乎是朝正确方向迈出的一步。

使用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)

python - 从麦克风产生频谱图-LMLPHP

这改善了色阶。现在,我们看到了以前隐藏的较高频段中的噪声。接下来,让我们解决时间问题。频谱图将信号分成多个段(默认长度为256),并为每个段计算频谱。这意味着我们具有出色的频率分辨率,但时间分辨率却很差,因为只有少数这样的片段适合信号窗口(大约1300个样本长)。在时间分辨率和频率分辨率之间始终需要权衡取舍。这与uncertainty principle有关。因此,通过将信号分成较短的段,让我们将一些频率分辨率换成时间分辨率:
f, t, Sxx = signal.spectrogram(snd_block, RATE, nperseg=64)

python - 从麦克风产生频谱图-LMLPHP

大!现在我们在两个轴上都获得了相对平衡的分辨率-但请稍等!为什么结果如此像素化?实际上,这就是短30ms时间窗口中的所有信息。仅有1300种样本可以二维分布。但是,我们可以作弊,并使用较高的FFT分辨率和重叠的片段。尽管它不提供其他信息,但这会使结果更平滑:
f, t, Sxx = signal.spectrogram(snd_block, RATE, nperseg=64, nfft=256, noverlap=60)

python - 从麦克风产生频谱图-LMLPHP

看得出漂亮的频谱干涉图样。 (这些模式取决于所使用的窗口函数,在这里我们不做详细介绍。请参见光谱图函数的window参数来使用它们。)结果看起来不错,但实际上不包含任何比以前更多的信息。图片。

为了使结果更接近SoX-lixe,请注意,SoX频谱图在时间轴上较为模糊。您可以通过使用原始的低时间分辨率(长片段)来获得此效果,但是为了平滑而重叠它们:
f, t, Sxx = signal.spectrogram(snd_block, RATE, noverlap=250)

python - 从麦克风产生频谱图-LMLPHP

我个人更喜欢第三个解决方案,但是您将需要找到自己喜欢的时间/频率权衡。

最后,让我们使用更类似于SoX的颜色图:
plt.pcolormesh(t, f, dBS, cmap='inferno')

python - 从麦克风产生频谱图-LMLPHP

对以下行的简短评论:
THRESHOLD = 40 # dB

将阈值与输入信号的RMS进行比较,该值不是以dB为单位,而是原始幅度单位。

1显然,FS是满量程的缩写。 dBFS表示dB量度是相对于最大范围的。 0 dB是当前表示形式中最大的信号,因此实际值必须

关于python - 从麦克风产生频谱图,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/43353172/

10-09 04:19