如何将wav文件转换为浮点幅度
我在标题里问了所有的问题:
我有一个wav文件(是用PyAudio从输入音频录制的),我想把它转换成浮点数数据,这样就能表示声音的强度(振幅),然后进行一些傅里叶变换等等……
有没有人知道怎么把WAV数据转换成浮点数?
5 个回答
我花了好几个小时在找这个问题的答案。结果发现解决办法其实很简单:你需要用到 struct.unpack。最后的代码大概是这样的:
rawdata=stream.read() # The raw PCM data in need of conversion
from struct import unpack # Import unpack -- this is what does the conversion
npts=len(rawdata) # Number of data points to be converted
formatstr='%ih' % npts # The format to convert the data; use '%iB' for unsigned PCM
int_data=unpack(formatstr,rawdata) # Convert from raw PCM to integer tuple
大部分功劳要归功于 解读WAV数据。唯一需要注意的就是要把 unpack 的格式设置正确:这需要正确的字节数和格式(有符号或无符号)。
大多数的wav文件都是用PCM 16位整数格式保存的。
你需要做的事情有:
- 解析文件头,了解它的格式是什么(可以查看Xophmeister提供的链接)
- 读取数据,把整数值转换成浮点数
整数值的范围是从-32768到32767,而你需要把它们转换成浮点数,范围是从-1.0到1.0。
我没有Python的代码,不过在C++中,如果PCM数据是16位整数,这里有一段代码示例,可以把它转换成浮点数(32位):
short* pBuffer = (short*)pReadBuffer;
const float ONEOVERSHORTMAX = 3.0517578125e-5f; // 1/32768
unsigned int uFrameRead = dwRead / m_fmt.Format.nBlockAlign;
for ( unsigned int i = 0; i < uFrameCount * m_fmt.Format.nChannels; ++i )
{
short i16In = pBuffer[i];
out_pBuffer[i] = (float)i16In * ONEOVERSHORTMAX;
}
要小心立体声文件,因为wav文件中的立体声PCM数据是交错存储的,这意味着数据的排列是LRLRLRLRLRLRLRLR(而不是LLLLLLLLRRRRRRRR)。根据你对数据的处理方式,你可能需要或不需要进行去交错处理。
我找到两种不错的方法来实现这个功能。
方法一:使用wavefile模块
如果你不介意安装一些额外的库,可以选择这个方法。虽然在我的Mac上安装时有点麻烦,但在我的Ubuntu服务器上就简单多了。
https://github.com/vokimon/python-wavefile
import wavefile
# returns the contents of the wav file as a double precision float array
def wav_to_floats(filename = 'file1.wav'):
w = wavefile.load(filename)
return w[1][0]
signal = wav_to_floats(sys.argv[1])
print "read "+str(len(signal))+" frames"
print "in the range "+str(min(signal))+" to "+str(max(signal))
方法二:使用wave模块
如果你想要减少安装模块的麻烦,可以选择这个方法。
这个方法可以从文件系统中读取一个wav文件,并把它转换成范围在-1到1之间的浮点数。它适用于16位的文件,如果文件有多个声道,它会按照文件中的样本顺序进行交错处理。对于其他位深度的文件,可以根据这个页面底部的表格,调整struct.unpack中的'h'参数:
https://docs.python.org/2/library/struct.html
不过,它不适用于24位的文件,因为没有24位的数据类型,所以无法告诉struct.unpack该怎么处理。
import wave
import struct
import sys
def wav_to_floats(wave_file):
w = wave.open(wave_file)
astr = w.readframes(w.getnframes())
# convert binary chunks to short
a = struct.unpack("%ih" % (w.getnframes()* w.getnchannels()), astr)
a = [float(val) / pow(2, 15) for val in a]
return a
# read the wav file specified as first command line arg
signal = wav_to_floats(sys.argv[1])
print "read "+str(len(signal))+" frames"
print "in the range "+str(min(signal))+" to "+str(max(signal))