在 python 中创建 32 位浮点 wav 文件?
Posted
技术标签:
【中文标题】在 python 中创建 32 位浮点 wav 文件?【英文标题】:Create 32bit float wav file in python? 【发布时间】:2013-03-22 17:49:27 【问题描述】:我想在 Python (2.x) 中创建 32 位浮点 WAV 文件。虽然“标准”WAV 文件通常使用 int,但许多专业音频应用程序将音频数据处理(并保存)为浮点数。 标准 wave 模块无法做到这一点:http://bugs.python.org/issue16525 有没有人在不使用补丁模块的情况下实现了这一点? tnx 寻求帮助。
【问题讨论】:
WAV 文件制作起来非常简单。我们可能可以创建一个 Python 函数,它接受一个数字数组并做正确的事情。这需要是单声道、立体声还是多声道音频? 它将是单声道或多声道(3 声道)音频。 3 个频道?左右 LFE? 【参考方案1】:这听起来很有趣(看我的把手),所以我敲定了一些东西。也许你可以使用它。如果您的 Python 脚本生成数值介于 [-1.0 .. 1.0] 之间的单声道波形,请通过 sample_array
发送该波形并指定 sample_rate
(例如,44100 或 48000)。这将返回一个数组,您可以将其作为 .wav 文件写入磁盘。
我在 Windows Media Player、Apple QuickTime Player 和 VLC(都在 Windows 7 上)测试了生成的 .wav 输出。他们都玩过。
def float32_wav_file(sample_array, sample_rate):
byte_count = (len(sample_array)) * 4 # 32-bit floats
wav_file = ""
# write the header
wav_file += struct.pack('<ccccIccccccccIHHIIHH',
'R', 'I', 'F', 'F',
byte_count + 0x2c - 8, # header size
'W', 'A', 'V', 'E', 'f', 'm', 't', ' ',
0x10, # size of 'fmt ' header
3, # format 3 = floating-point PCM
1, # channels
sample_rate, # samples / second
sample_rate * 4, # bytes / second
4, # block alignment
32) # bits / sample
wav_file += struct.pack('<ccccI',
'd', 'a', 't', 'a', byte_count)
for sample in sample_array:
wav_file += struct.pack("<f", sample)
return wav_file
【讨论】:
迈克感谢您的代码。我将尽快测试代码。回答为什么三个通道的问题:原始数据来自三轴(南北,东西,上下)感应环磁力计,我想对数据进行一些光谱分析 - 主要是寻找地球的舒曼共振。 哇——我遇到过的任何多媒体库中都没有频道配置。 :-) 也许使用这个脚本保存 3 个单独的单声道波形而不是尝试交错更有意义? 感谢@MultimediaMike 的代码。您可以通过将循环替换为wav_file += struct.pack('<%df' % len(sample_array), *sample_array)
来优化它
为了进一步补充,如果您使用的是 python 3 和 numpy 数组,您可以使用 arr.tobytes()
而不是 struct.pack
。这也适用于多维数组,并以对多声道音频有意义的方式展平
嘿多媒体 Mike - 我给你留了一封电子邮件,你能看一下吗?【参考方案2】:
这是我的贡献...包括任意字长和任意数量的频道。 我冒昧地更改了 float32_wav_file 以包含一个保存用于测试的文件。请注意,文件结构的多通道数据部分是交错的。我敢肯定,那个循环可能会被极大地python化。
# see http://***.com/questions/15576798/create-32bit-float-wav-file-in-python
# see... http://blog.theroyweb.com/extracting-wav-file-header-information-using-a-python-script
import struct
def float32_wav_file(file_name, sample_array, sample_rate):
(M,N)=sample_array.shape
#print "len sample_array=(%d,%d)" % (M,N)
byte_count = M * N * 4 # (len(sample_array)) * 4 # 32-bit floats
wav_file = ""
# write the header
wav_file += struct.pack('<ccccIccccccccIHHIIHH',
'R', 'I', 'F', 'F',
byte_count + 0x2c - 8, # header size
'W', 'A', 'V', 'E', 'f', 'm', 't', ' ',
0x10, # size of 'fmt ' header
3, # format 3 = floating-point PCM
M, # channels
sample_rate, # samples / second
sample_rate * 4, # bytes / second
4, # block alignment
32) # bits / sample
wav_file += struct.pack('<ccccI',
'd', 'a', 't', 'a', byte_count)
print "packing..."
for j in range(0,N):
for k in range(0,M):
wav_file += struct.pack("<f", sample_array[k,j])
print "saving..."
fi=open(file_name,'wb')
for value in wav_file:
fi.write(value)
fi.close()
return wav_file
import numpy as np
def wav_file_read(filename):
fi=open(filename,'rb')
data=fi.read()
fi.close()
A, B, C, D =struct.unpack('4c', data[0:4]) # 'RIFF'
ChunkSize =struct.unpack('<l', data[4:8])[0] #4+(8+SubChunk1Size)+8+SubChunk2Size)
A, B, C, D =struct.unpack('4c', data[8:12]) # 'WAVE'
A, B, C, D =struct.unpack('4c', data[12:16]) # 'fmt '
Subchunk1Size =struct.unpack('<l', data[16:20])[0] # LITTLE ENDIAN, long, 16
AudioFormat =struct.unpack('<h', data[20:22])[0] # LITTLE ENDIAN, short, 1
NumChannels =struct.unpack('<h', data[22:24])[0] # LITTLE ENDIAN, short, Mono = 1, Stereo = 2
SampleRate =struct.unpack('<l', data[24:28])[0] # LITTLE ENDIAN, long, sample rate in samples per second
ByteRate =struct.unpack('<l', data[28:32])[0] # self.SampleRate * self.NumChannels * self.BitsPerSample/8)) # (ByteRate) LITTLE ENDIAN, long
BlockAlign =struct.unpack('<h', data[32:34])[0] # self.NumChannels * self.BitsPerSample/8)) # (BlockAlign) LITTLE ENDIAN, short
BitsPerSample =struct.unpack('<h', data[34:36])[0] # LITTLE ENDIAN, short
A, B, C, D =struct.unpack('4c', data[36:40]) # BIG ENDIAN, char*4
SubChunk2Size =struct.unpack('<l', data[40:44])[0] # LITTLE ENDIAN, long
waveData=data[44:]
(M,N)=(len(waveData),len(waveData[0]))
print("ChunkSize =%d\nSubchunk1Size =%d\nAudioFormat =%d\nNumChannels =%d\nSampleRate =%d\nByteRate =%d\nBlockAlign =%d\nBitsPerSample =%d\nA:%c, B:%c, C:%c, D:%c\nSubChunk2Size =%d" %
(ChunkSize ,
Subchunk1Size,
AudioFormat ,
NumChannels ,
SampleRate ,
ByteRate ,
BlockAlign ,
BitsPerSample ,
A, B, C, D ,
SubChunk2Size ))
if BitsPerSample==8:
print "Unpacking 8 bits on len(waveData)=%d" % len(waveData)
d=np.fromstring(waveData,np.uint8)
floatdata=d.astype(np.float64)/np.float(127)
elif BitsPerSample==16:
print "Unpacking 16 bits on len(waveData)=%d" % len(waveData)
d=np.zeros(SubChunk2Size/2, dtype=np.int16)
j=0
for k in range(0, SubChunk2Size, 2):
d[j]=struct.unpack('<h',waveData[k:k+2])[0]
j=j+1
floatdata=d.astype(np.float64)/np.float(32767)
elif BitsPerSample==24:
print "Unpacking 24 bits on len(waveData)=%d" % len(waveData)
d=np.zeros(SubChunk2Size/3, dtype=np.int32)
j=0
for k in range(0, SubChunk2Size, 3):
d[j]=struct.unpack('<l',struct.pack('c',waveData[k])+waveData[k:k+3])[0]
j=j+1
floatdata=d.astype(np.float64)/np.float(2147483647)
else: # anything else will be considered 32 bits
print "Unpacking 32 bits on len(waveData)=%d" % len(waveData)
d=np.fromstring(waveData,np.int32)
floatdata=d.astype(np.float64)/np.float(2147483647)
v=floatdata[0::NumChannels]
for i in range(1,NumChannels):
v=np.vstack((v,floatdata[i::NumChannels]))
#return (np.vstack((floatdata[0::2],floatdata[1::2])), SampleRate, NumChannels, BitsPerSample)
return (v, SampleRate, NumChannels, BitsPerSample)
if __name__ == "__main__":
(array,SampleRate,NumChannels,BitsPerSample)=wav_file_read("my_input_file.wav")
wavefile=float32_wav_file("test_file.wav",array,SampleRate)
【讨论】:
【参考方案3】:我试过测试 P Moran 的代码,但绝对不行,比如 'len(int(3))?'。对不起,伙计,我认为它没有经过测试。但不要害怕,现代蟒蛇有办法!
import numpy as np
songtime=np.arange(0,100,60/44100.,dtype=float)
coswav=np.cos(songtime)
sinewav=np.sin(songtime)
np.column_stack((coswav,sinwav))
songwav=np.column_stack((coswav,sinwav)).astype('float32')
import scipy.io.wavfile as scipy_io_wavfile
scipy_io_wavfile.write("5secof60Hz.wav",44100,songwav)
https://docs.python.org/3.7/library/wave.html https://docs.scipy.org/doc/scipy/reference/generated/scipy.io.wavfile.write.html
【讨论】:
SciPy 文档将 ±1 声明为 WAVE 文件中浮点值的限制,但没有强制执行,没有剪辑,没有警告。这对我有用(存储 acc 信号),但如果用作音频文件可能会伤害耳朵或扬声器。以上是关于在 python 中创建 32 位浮点 wav 文件?的主要内容,如果未能解决你的问题,请参考以下文章
使用 NAudio 如何将非隔行扫描 32 位浮点格式保存到文件
如何将您在 python 中创建的 .wav 文件保存到指定目录?
Qsettings 适用于 32 位 apk 但不在我的 64 位 apk 中创建文件