需求:
wav是一种音频文件的格式,音频文件为二进制文件,wav文件由头部信息和音频采样数据组成,前44个字节为头部信息,包括声道数,采样频率,PCM位宽等等,后面是音频采样数据。
使用python,分析一个wav文件头部信息,处理音频数据。
思路:
open函数想以二进制模式打开文件,指定Mode参数为'b'。
二进制数据可以用readinto,读入提前分配的buffer中,便于数据处理。
解析二进制数据可以使用标准库中的struct模块的unpack方法
代码:
import struct
import array
# 以rb的模式打开wav文件
f = open('demo.wav','rb')
# 读取前44个字节
info = f.read(44)
# 将文件的指针移到文件的末尾
f.seek(0,2)
# 报告文件的指针
f.tell()
# 计算数组的长度
n = (f.tell() - 44) / 2
# 生成数组并初始化数组
buf = array.array('h',( 0 for _ in range(n)))
# 将文件的数据读入到buf中
# 首先要注意将文件的指针指向到data的开关
f.seek(44)
f.readinto(buf)
# 对每一个采样数据进行除以8的操作,这样可以使wav文件的声音变小一些
for i in range(n):
buf[i] /= 8
# 将改变的数据写入新的文件中
f2 = open('demo2.wav','wb')
# 先写入文件信息头的部分
f2.write(info)
# 将文件的数据写入文件中
buf.tofile(f2)
# 关闭文件
f2.close()
=========================================================================
>>> f = open('demo.wav','rb')
>>> info = f.read(44)
>>> info
b'RIFFx06xe5xfax01WAVELISTx98x00x00x00INFOIARTx05x00x00x00xd9xa9xd9xa9x00x00INAMx03x00'
>>> type(info)
bytes
>>> info[22:24]
b'FO'
>>> import struct
>>> struct.unpack('h',info[22:24])
(20294,)
>>> struct.unpack('>h',info[22:24])
(17999,)
>>> struct.unpack('>h',info[22:28])
---------------------------------------------------------------------------
error Traceback (most recent call last)
<ipython-input-99-f44c391af760> in <module>
----> 1 struct.unpack('>h',info[22:28])
error: unpack requires a buffer of 2 bytes
>>> struct.unpack('i',info[24:28])
(1414676809,)
>>> info.find(b'data')
-1
>>> f.seek(0)
0
>>> f.read(100)
b'RIFFx06xe5xfax01WAVELISTx98x00x00x00INFOIARTx05x00x00x00xd9xa9xd9xa9x00x00INAMx03x00x00x00xbbxadx00x00IPRDx07x00x00x00xd4xdaxd4xb6xb7xbdx00x00IGNRx05x00x00x00xc3xf1xd2xa5x00x00ITOCJx00x00x00C+96+404D+A8'
>>> f.read(200)
b'2E+FD6E+15732+19F7F+1D6AB+22FBF+27F40+2C3B9+32D7B+37FEC+3D049x00ITRKx02x00x00x006x00fmt x12x00x00x00x01x00x02x00Dxacx00x00x10xb1x02x00x04x00x10x00x00x00data@xe4xfax01x04x00xf4xffxfaxffx08x00x05x00xfcxffxfcxffx04x00x02x00xfaxffxffxffx07x00x03x00xfbxffxfdxffx08x00x06x00xfaxffxfaxffx05x00x02x00xf8xffx03x00x0cx00xfdxffxf3xffx01x00x0fx00x01x00xf4xffxffxff
x00x04x00xf7xffxf7xffx0fx00
x00xf0xffxf9xff x00x06x00xfcxffxf8xffx05x00x00x00x00x00x00x00'
>>> %load 5_2.py
>>> # %load 5_2.py
... import struct
...
... def find_subchunk(f.name):
... f.seek(12)
... while True:
... chunk_name = f.read(4)
... chunk_size, = struct.unpack('i',f.read(4))
...
... if chunk_name == name:
... return f.tell(),chunk_size
...
... f.seek(chunk_size,1) # 1代表指针的当前点,0代表开关,2代表文件结尾,这里是表示指针往前跳chunk_size个距离
...
...
File "<ipython-input-106-4d5fb97742fd>", line 4
def find_subchunk(f.name):
^
SyntaxError: invalid syntax
>>>
>>> %load 5_2.py
>>> # %load 5_2.py
... import struct
...
... def find_subchunk(f,name):
... f.seek(12)
... while True:
... chunk_name = f.read(4)
... chunk_size, = struct.unpack('i',f.read(4))
...
... if chunk_name == name:
... return f.tell(),chunk_size # 此处返回的是指针的偏移量和要查找块的大小
...
... f.seek(chunk_size,1) # 1代表指针的当前点,0代表开关,2代表文件结尾,这里是表示指针往前跳chunk_size个距离
...
...
>>> f
<_io.BufferedReader name='demo.wav'>
>>> offset ,size = find_subchunk(f,b'data') # 这里的数据前的部分不一定是44字节,需要定义一个函数来进行查找
>>> offset
206
>>> size
33219648
>>> offset + size # 可以看到和ll查看的大小是一致的
33219854
>>> ll demo.wav
-rw-r--r-- 1 richardo 33219854 11月 3 15:42 demo.wav
>>> import numpy as np
---------------------------------------------------------------------------
ModuleNotFoundError Traceback (most recent call last)
<ipython-input-115-0aa0b027fcb6> in <module>
----> 1 import numpy as np
ModuleNotFoundError: No module named 'numpy'
>>> import numpy as np
>>> buffer = np.zeros(size//2,dtype=np.short) # 生成一个buffer在bufer进行数据的处理
>>> buf
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-118-470933492c55> in <module>
----> 1 buf
NameError: name 'buf' is not defined
>>> buffer
array([0, 0, 0, ..., 0, 0, 0], dtype=int16)
>>> f.readinto(buffer) # 将data的数据部分全部读入buffer中
33219648
>>> buffer
array([ 4, -12, -6, ..., -2, -6, 3], dtype=int16)
>>> buffer[10000:20000]
array([ 5, -3, -2, ..., -3, -6, 1], dtype=int16)
>>> buffer //= 8 # 进行除以8处理,可以使文件的音量降低
>>> f2 = open('out.wav','wb')
>>> f.seek(0)
0
>>> info = f.read(offset)
>>> f2.write(info) # 先写data的前面头部部分
206
>>> buffer.tofile(f2) # 再写data本身
>>> f2.close()
>>>