我之前从未使用过音频信号,但我对信号处理知之甚少。不过,我需要使用 matplotlib 库中的pyplot.specgram
函数来表示和音频信号。我就是这样做的。
import matplotlib.pyplot as plt
import scipy.io.wavfile as wavfile
rate, frames = wavfile.read("song.wav")
plt.specgram(frames)
当我看到x轴和y轴,我认为频率和时间域时,我无法理解频率缩放的事实从 0到1.0 ,时间从 0到80k 。 背后的直觉是什么,更重要的是,如何以人性化的格式表示它,频率为0到100k,时间是以秒为单位?
答案 0 :(得分:4)
首先,频谱图表示信号的频谱内容随时间的变化 - 这是时域的频域表示波形(例如正弦波,您的文件“song.wav”或其他任意波形 - 即振幅随时间的变化)。
频率值(y轴,赫兹)完全取决于波形的采样频率(“song.wav”),范围从“0”到“采样频率/ 2”,上限是“奈奎斯特频率”或“折叠频率”(https://en.wikipedia.org/wiki/Aliasing#Folding)。如果没有另外指定,matplotlib specgram函数将自动确定输入波形的采样频率,定义为1 / dt,其中dt是波形的离散采样之间的时间间隔。您可以将选项Fs ='sampling rate'传递给specgram函数以手动定义它是什么。如果你弄清楚并将这些变量自己传递给specgram函数,你就会更容易理解正在发生的事情
时间值(x轴,秒)完全取决于“song.wav”的长度。如果使用较大的窗口长度来计算每个光谱切片,您可能会注意到一些空白或填充(想想 - 垂直排列并水平平铺以创建光谱图像的单个光谱)
要使图中的轴更直观,请使用x轴和y轴标签,还可以使用类似this的方法缩放轴值(即更改单位)
带回家的消息 - 尝试使用您的代码更加冗长:请参阅下面的示例。
import matplotlib.pyplot as plt
import numpy as np
# generate a 5Hz sine wave
fs = 50
t = np.arange(0, 5, 1.0/fs)
f0 = 5
phi = np.pi/2
A = 1
x = A * np.sin(2 * np.pi * f0 * t +phi)
nfft = 25
# plot x-t, time-domain, i.e. source waveform
plt.subplot(211)
plt.plot(t, x)
plt.xlabel('time')
plt.ylabel('amplitude')
# plot power(f)-t, frequency-domain, i.e. spectrogram
plt.subplot(212)
# call specgram function, setting Fs (sampling frequency)
# and nfft (number of waveform samples, defining a time window,
# for which to compute the spectra)
plt.specgram(x, Fs=fs, NFFT=nfft, noverlap=5, detrend='mean', mode='psd')
plt.xlabel('time')
plt.ylabel('frequency')
plt.show()
5Hz_spectrogram:
答案 1 :(得分:4)
正如其他人所指出的那样,您需要指定采样率,否则您将获得标准化频率(介于0和1之间)和样本索引(0到80k)。幸运的是,这很简单:
plt.specgram(frames, Fs=rate)
扩展Nukolas的答案并合并我的Changing plot scale by a factor in matplotlib 和 matplotlib intelligent axis labels for timedelta 我们不仅可以在频率轴上获得kHz,还可以在时间轴上获得分钟和秒数。
import matplotlib.pyplot as plt
import scipy.io.wavfile as wavfile
cmap = plt.get_cmap('viridis') # this may fail on older versions of matplotlib
vmin = -40 # hide anything below -40 dB
cmap.set_under(color='k', alpha=None)
rate, frames = wavfile.read("song.wav")
fig, ax = plt.subplots()
pxx, freq, t, cax = ax.specgram(frames[:, 0], # first channel
Fs=rate, # to get frequency axis in Hz
cmap=cmap, vmin=vmin)
cbar = fig.colorbar(cax)
cbar.set_label('Intensity dB')
ax.axis("tight")
# Prettify
import matplotlib
import datetime
ax.set_xlabel('time h:mm:ss')
ax.set_ylabel('frequency kHz')
scale = 1e3 # KHz
ticks = matplotlib.ticker.FuncFormatter(lambda x, pos: '{0:g}'.format(x/scale))
ax.yaxis.set_major_formatter(ticks)
def timeTicks(x, pos):
d = datetime.timedelta(seconds=x)
return str(d)
formatter = matplotlib.ticker.FuncFormatter(timeTicks)
ax.xaxis.set_major_formatter(formatter)
plt.show()
<强>结果:强>