使用Python的plot_surface创建实时麦克风输入3D瀑布频谱图时出现问题

问题描述 投票:0回答:1

所以,我终于找到了一些时间来温习Python及其可视化功能,特别是声音。下面是我尝试使用plot_surface 将瀑布谱图组合在一起,该图部分基于在网络上找到的代码块。

import numpy as np
import matplotlib.pyplot as plt 
from scipy import signal # spectrogram function
from matplotlib import cm # colour map

import pyaudio as pa
import time
import struct

CHUNK = 1024
FORMAT = pa.paInt16
CHANNELS = 1
RATE = 44100 # in Hz
LENGTH = 2 # in seconds

p = pa.PyAudio()

stream = p.open(
    format = FORMAT,
    channels = CHANNELS,
    rate = RATE,
    input=True,
    output=True,
    frames_per_buffer=CHUNK
)

# 3d plot
fig = plt.figure()
ax = plt.axes(projection='3d')
ax.set_xlim(RATE/2,0)

# ---- OPTION QUESTION COMING UP BELOW
# this works better for option 1.
ax.set_zlim(-40,40)
# this works better for option 2. Why?
#ax.set_zlim(0,100)
# ---- END OF OPTION QUESTION

totaldata = bytearray(RATE*LENGTH) # this zeros out the entire array
sf = None

while 1:
    data = stream.read(CHUNK)

# ---- 2 OPTIONS COMING UP BELOW (UNCOMMENT ONE OR THE OTHER, AND MATCH IT WITH THE ONE ABOVE)
    # OPTION 1 (CHUNK of data only--this works fine)
    # prepare just this chunk for display
    numpydata = np.frombuffer(data, dtype=np.int16)

##    # OPTION 2 (1 second of data--this has huge walls against the edges of each chunk?)
##    # pop first CHUNK-sized data
##    totaldata = totaldata[((CHUNK*2)-1):]
##    # append the new data
##    totaldata[((RATE*LENGTH)-(CHUNK*2)):] = data
##    # load the data (option 1 ,one CHUNK)
##    #numpydata = np.frombuffer(onechunkdata, dtype=np.int16)
##    # (option 2, 1 second)
##    numpydata = np.frombuffer(totaldata, dtype=np.int16)
# ---- END OF TWO OPTIONS

    # analyze data
    freq_bins, timestamps, spec = signal.spectrogram(numpydata, RATE)

    # redraw (have if statement since the first time sf is not instantiated until below)
    if (sf):
        sf.remove()
    sf = ax.plot_surface(freq_bins[:, None], timestamps[None, :], 10.0*np.log10(spec), cmap='inferno')
    plt.show(block=False)
    plt.pause(0.01)

请注意,代码中注释了 2 个选项,第一个选项默认启用。第一个选项仅获取麦克风捕获的一块音频并立即将其可视化。从技术上讲,这看起来像瀑布,尽管是微观的。这个工作正常(如下图):

但是,当我启用第二个选项时(请注意代码中需要注释掉第一个选项并取消注释第二个选项的两个带注释的位置,第一个选项与 set_zlim 一起使用,第二个选项在 while 循环中),它尝试加载数组并动态弹出块大小的数据,然后将新数据附加到末尾。在这次迭代中,我在每个窗口周围都有大量的尖峰。因此,如果您直接从底部或顶部观察表面,您会发现在山谷内部,数据仍然正确显示,但边缘非常高,以至于从其他角度无法看到相同的情况。从侧面看,您可以看到每个块是如何被这些相同的尖峰包围的。请参阅下面显示问题的图片:

等角视图:

侧视图:

从底部看,你仍然可以看到我的口哨声的捕捉,显示出在尖峰山脉之间埋藏着稳定的频率:

我尝试更改 nfft、window、nperseg,也许最重要的是,分配不同的窗口函数(例如汉明),虽然它确实影响了频谱图的整体外观,但都没有解决其尖峰外观。那么,这里有几个问题:

  1. 如何使频谱图边缘平滑?

  2. 这是否可能是因为我通过计算和重绘中断了麦克风的读取,这可能会导致跳过样本,从而导致这些尖刺墙?

  3. 为什么在观察单个音频块与 1 秒数据时,z 轴具有如此截然不同的范围(请参阅代码中第一个注释的选项问题)?

  4. 为什么麦克风块实际上是请求块的两倍?它有一个音频通道,请求的块是 1024 个样本。然而,当有人请求数据长度时,我得到的是 2048。

  5. 推而广之,为什么 y 轴(时间)的图形长度是请求的总数据大小(RATE,即 44100 乘以 LENGTH,即 1s)的一半?

在我看来,这是捕获流中的窗口和/或跳过样本(然后无缝附加到总数据字节数组内)问题,但我不知道如何解决这个问题。问题 1 是主要问题,其余问题相关,但也可能独立于第一个问题。

谢谢您的帮助。

python matplotlib microphone spectrogram windowing
1个回答
0
投票

所以,我终于找到了第一个问题的答案。违规行是:

totaldata = totaldata[((CHUNK*2)-1):]

早些时候,我认为我需要从总数据缓冲区开头的 CHUNK*2 弹出中减去 1,因为数组索引是 [0]。事实证明,删除这一行就解决了问题1-3,问题2的答案是“不是,不是因为那些潜在的原因”,答案3是错误的数据,从而导致了上述问题。不过,仍然留下问题 4 和 5。

© www.soinside.com 2019 - 2024. All rights reserved.