我从 wav 文件中获取峰值频率。但是对于录制的 2 个频道 wav,它不起作用
Posted
技术标签:
【中文标题】我从 wav 文件中获取峰值频率。但是对于录制的 2 个频道 wav,它不起作用【英文标题】:I am getting peak frequency from wav file. But for recorded 2 channels wav it is not working 【发布时间】:2016-06-14 13:11:12 【问题描述】:我正在从 wav 文件中获取峰值频率
我从 wav 文件中获取峰值频率的代码是:
import wave
import struct
import numpy as np
import wave
import contextlib
if __name__ == '__main__':
fname = "test.wav"
frate = 0
data_size = 0
with contextlib.closing(wave.open(fname,'r')) as f:
frate = f.getframerate()
data_size = f.getnframes()
wav_file = wave.open(fname, 'r')
data = wav_file.readframes(data_size)
data_size = data_size * wav_file.getnchannels()
print wav_file.getparams()
wav_file.close()
data = struct.unpack('nh'.format(n=data_size), data)
data = np.array(data)
w = np.fft.fft(data)
freqs = np.fft.fftfreq(len(w))
print(freqs.min(), freqs.max())
# Find the peak in the coefficients
idx = np.argmax(np.abs(w))
freq = freqs[idx]
freq_in_hertz = abs(freq * frate)
print(freq_in_hertz)
我录制了一个具有 48000 采样率、16 位宽、2 个通道的 wav 文件。 在那个文件中,我有一个 1000Hz 的正弦音。 但脚本只输出 500Hz。 我不知道我哪里出错了。 但是对于单通道和生成的具有 48000 采样率、16 位宽、2 通道的 wav 文件,它工作正常。
我使用以下脚本生成了 wav 文件
import math
import wave
import struct
if __name__ == '__main__':
# http://***.com/questions/3637350/how-to-write-stereo-wav-files-in-python
# http://www.sonicspot.com/guide/wavefiles.html
freq = 1000
data_size = 454656 * 2
fname = "test.wav"
frate = 48000.0
amp = 64000.0
nchannels = 2
sampwidth = 2
framerate = int(frate)
nframes = data_size
comptype = "NONE"
compname = "not compressed"
data = [math.sin(2 * math.pi * freq * (x / frate))
for x in range(data_size)]
wav_file = wave.open(fname, 'w')
wav_file.setparams(
(nchannels, sampwidth, framerate, nframes, comptype, compname))
for v in data:
wav_file.writeframes(struct.pack('h', int(v * amp / 2)))
wav_file.close()
我不知道我哪里做错了。 我在脚本生成的 wav script_gen.wav 上传了我的 wav 文件,采样率为 48000,2 个通道,16 位。 录制的 wav:2 channel wav 48000 采样率,2 通道,16 位 1 通道 wav(此处不允许发布链接,因此将在 cmets 中发布)具有 48000 采样率,1 通道,16 位。
我大胆地检查了所有这些峰值频率,它只显示 1000Khz。
但是当我尝试使用我的脚本时,我得到了 1 通道 wav 的正确输出,而 2 通道 wav 却失败了。
更新: 我将峰值频率的一半作为 2 个通道的输出。
我感觉我错过了什么。 任何人都可以帮助我吗?
【问题讨论】:
1 channel wav 【参考方案1】:为什么这么复杂?考虑以下
#!/usr/bin/env python3
import numpy as np
from numpy import fft
import scipy.io.wavfile as wf
import matplotlib.pyplot as plt
sr = 44100 # sample rate
len_sig = 2 # length of resulting signal in seconds
f = 1000 # frequency in Hz
# set you time axis
t = np.linspace(0, len_sig, sr*len_sig)
# set your signal
mono_data = np.sin(2*np.pi*t*f)
# write single channel .wav file
wf.write('mono.wav', sr, mono_data)
# write two-channel .wav file
stereo_data = np.vstack((mono_data, mono_data)).T
wf.write('stereo.wav', sr, stereo_data)
现在通过加载和分析数据来测试它
# Load data
mono_sr, mono_data = wf.read('mono.wav')
stereo_sr, stereo_data = wf.read('stereo.wav')
# analyze the data
X_mono = fft.fft(mono_data) / len(mono_data) # remember to normalize your amplitudes
# Remember that half of energy of the signal is distributed over the
# positive frequencies and the other half over the negative frequencies.
#
# Commonly you want see a magnitude spectrum. That means, we ignore the phases. Hence, we
# simply multiply the spectrum by 2 and consider ONLY the first half of it.
freq_nq = len(X_mono) // 2
X_mono = abs(X_mono[:freq_nq]) * 2
freqs_mono = fft.fftfreq(len(mono_data), 1/mono_sr)[:freq_nq]
# in order the analyze a stereo signal you first have to add both channels
sum_stereo = stereo_data.sum(axis=1) / 2
# and now the same way as above
freq_nq = len(sum_stereo) // 2
X_stereo= abs(fft.fft(sum_stereo))[:freq_nq] / len(stereo_data) * 2
freqs_stereo = fft.fftfreq(len(stereo_data), 1/stereo_sr)[:freq_nq]
选峰:
freqs_mono[np.argmax(X_mono)] # == 1000.0
freqs_stereo[np.argmax(X_stereo)] # == 1000.0
绘制结果:
fig, (ax1, ax2) = plt.subplots(2, figsize=(10,5), sharex=True, sharey=True)
ax1.set_title('mono signal')
ax1.set_xlim([0, 2000])
ax1.plot(freqs_mono, X_mono, 'b', lw=2)
ax2.set_title('stereo signal')
ax2.plot(freqs_stereo, X_stereo, 'g', lw=2)
ax2.set_xlim([0, 2000])
plt.tight_layout()
plt.show()
【讨论】:
这里的 wf 和 fft 是什么意思。您导入了哪些库。 @MaxPowers 我们不能对我的代码进行一些更改并使其正常工作。基本上我是python的新手。理解需要时间。这些代码 我的脚本产生一半的峰值频率作为 2 通道波的结果。我想我错过了什么。你能帮我吗 @vvn 我更新了代码以包含缺少的导入语句。 @SoCelectron 我添加了选择峰的最简单方法。【参考方案2】:我认为这将对您有所帮助。 只是添加了更多的东西来配合你的外观。 使用 MaxPowers 逻辑。 您需要将 24 位数据转换为 32 位,然后这也适用于 24 位。
import sys
import wave
import struct
import numpy as np
import wave
import argparse
def parse_arguments():
"""Parses command line arguments."""
parser = argparse.ArgumentParser(description='Tool to get peak frequency')
parser.add_argument('fname', metavar='test.wav', type=str,
help='Path to a wav file')
args = parser.parse_args()
return args
def main():
args = parse_arguments()
fname = args.fname
wav_file = wave.open(fname, 'r')
frate = wav_file.getframerate()
data_size = wav_file.getnframes()
data = wav_file.readframes(data_size)
nChannels = wav_file.getnchannels()
nSample = wav_file.getsampwidth()
data_size = data_size * nChannels * nSample
wav_file.close()
if nSample == 2:
fmt = "<i2"
else :
fmt = "<i4"
data = np.frombuffer(data,dtype=fmt)
if nChannels == 2 :
data = data.reshape(-1,nChannels)
data = data.sum(axis=1) / 2
# and now the same way as above as said by maxpowers
freq_nq = len(data) // 2
X= abs(np.fft.fft(data))[:freq_nq] / len(data) * 2
freqs = np.fft.fftfreq(len(data), 1./frate)[:freq_nq]
print freqs[np.argmax(X)]
if __name__ == '__main__':
try:
main()
except (Exception) as e:
print str(e)
sys.exit(255)
【讨论】:
以上是关于我从 wav 文件中获取峰值频率。但是对于录制的 2 个频道 wav,它不起作用的主要内容,如果未能解决你的问题,请参考以下文章