我从本教程中获取了以下代码:
def get_spectrogram(waveform):
zero_padding = tf.zeros([4900] - tf.shape(waveform), dtype=tf.float32)
waveform = tf.cast(waveform, tf.float32)
equal_length = tf.concat([waveform, zero_padding], 0)
spectrogram = tf.signal.stft(equal_length, frame_length=256, frame_step=128)
spectrogram = tf.abs(spectrogram)
return spectrogram
spectrogram = get_spectrogram(waveform)
print('Spectrogram shape:', spectrogram.shape)
得到如下输出的谱图形状:
谱图形状:(37,129)
第一个和第二个值是什么意思?
如果我有4900个样本,frame_step
为128。第一个值不应该是38吗?
4900/128 = 38.28125 -> 38 rounded
碰巧在Kotlin库中,我得到的形状是(38,127)。
我需要理解,因为我在Android中使用TFLite实现模型,因此我正在预处理来自移动设备的数据。
我不太熟悉Python API,但假设它与我非常熟悉的WaveBeans类似,看起来你得到的是二维矩阵。
你所做的是一个短傅里叶变换,它基本上是FFT随时间的变化。虽然FFT幅度或相位是二维的,可以表示为一维矢量,但SFT是三维的,也有时间轴,这就是为什么它是二维矢量。
所以看起来38边是时间指标,127边是频率指标,值是特定时频bin上的FFT值,尽管那是复数。把它想象成一个极坐标,相位是角度,大小是长度。在你的代码中,你似乎通过调用.abs()
函数来获得大小,所以你已经摆脱了复数表示。
在WaveBeans中有一个API专门用于FFT,以提取出相位和幅度,以及频率值和时间值。
为了保证答案完整,我将提供一个代码片段:// let's take simple sine as an example
val waveformAsAStream = 440.sine().trim(1000)
val fftStream = waveformAsAStream
.window(256,128)
// zero padding is already done inside, but if window.size == fft.size it doesn't really do anything
.fft(256)
// evaluate it, for example as a kotlin sequence
val stft = fftStream.asSequence(44100.0f)
.toList()
// get the specific sample for the sake of the demonstration
val fftSample = stft.drop(10).first()
// get time in nano seconds
fftSample.time()
// outputs the time of the taken sample:
// 29024943
// get frequencies values
fftSample.frequency().toList()
// outputs a list of size 128, each element is a frequency in Hz :
// [0.0, 172.265625, 344.53125, 516.796875, 689.0625, ..., 21360.9375, 21533.203125, 21705.46875, 21877.734375]
// get magnitude values
fftSample.magnitude().toList()
// outputs a list of size 128, each element is magnitude value for specific bin in dB:
// [29.629418039768613, 31.125367384785786, 38.077554502661705, 38.480916556622745, ..., -11.57802246867041]
// the index of the closest bin (index) of the frequency
fftSample.bin(440.0)
// outputs:
// 3
// get the magnitude in the FFT spectrogram of the specific frequency
fftSample.magnitude().toList()[fftSample.bin(440.0)]
// outputs:
// 38.480916556622745
虽然我建议更好的FFT输出结果使用窗口函数,例如hamming是流行的一个,并使用较小大小的窗口(零填充将做对齐技巧在这种情况下,FFT需要特定的输入长度),即像这样的东西:
waveformAsAStream
.window(101, 85)
.hamming()
.fft(256)
如果你想玩周围的值,你可以使用Kotlin Jupyter笔记本与WaveBeans库,检查它在github