wave文件格式详解 |
您所在的位置:网站首页 › 要查找第一个字母为a且扩展名为wav的文件 › wave文件格式详解 |
目录
第一节 wav格式scheme介绍 第二节 真实wav文件分析 第三节 python读取wav文件 第一节 wav格式scheme介绍wav格式,是微软开发的一种文件格式规范,整个文件分为两部分。第一部分是“总文件头”,就包括两个信息,chunkID,其值为“RIFF”,占四个字节;ChunkSize,其值是整个wav文件除去chunkID和ChunkSize,后面所有文件大小的字节数,占四个字节。 第二部分是Format,其值为“wave”,占四个字节。它包括两个子chunk,分别是“fmt ”和“data”。在fmt子chunk中定义了该文件格式的参数信息,对于音频而言,包括:采样率、通道数、位宽、编码等等;data部分是“数据块”,即一帧一帧的二进制数据,对于音频而言,就是原始的PCM数据。 图一 wav格式文件示意图 表一 wav格式字段说明 Offset Size Name Description 0 4 ChunkID ASCII码“0x52494646”对应字母“RIFF” 4 4 ChunkSize 块大小是指除去ChunkID与ChunkSize的剩余部分有多少字节数据。注意:小尾字节序数。 8 4 Format ASCII码“0x57415645”对应字母“WAVE”。该块由两个子块组成,一个“fmt”块用于详细说明数据格式,一个“data”块包含实际的样本数据。 12 4 Subchunk1ID ASCII码“0x666d7420”对应字母“fmt ”。 16 4 Subchunk1Size 如果文件采用PCM编码,则该子块剩余字节数为16。 20 2 AudioFormat 如果文件采用PCM编码(线性量化),则AudioFormat=1。AudioFormat代表不同的压缩方式,表二说明了相应的压缩方式。 22 2 NumChannels 声道数,单声道(Mono)为1,双声道(Stereo)为2。 24 4 SampleRate 取样率,例:44.1kHz,48kHz。 28 4 ByteRate 传输速率,单位:Byte/s。 32 2 BlockAlign 一个样点(包含所有声道)的字节数。 34 2 BitsPerSample 每个样点对应的位数。 2 ExtraParamSize 如果采用PCM编码,该值不存在。 X ExtraParams 用于存储其他参数。如果采用PCM编码,该值不存在。 36 4 Subchunk2ID ASCII码“0x64617461”对应字母 “data”。 40 4 Subchunk2Size 实际样本数据的大小(单位:字节)。 44 * Data 实际的音频数据 。
表二 wave支持格式 AudioFormat Description 0 (0x0000) Unknown 1 (0x0001) PCM/uncompressed 2 (0x0002) Microsoft ADPCM 6 (0x0006) ITU G.711 a-law 7 (0x0007) ITU G.711 µ-law 17 (0x0011) IMA ADPCM 20 (0x0016) ITU G.723 ADPCM (Yamaha) 49 (0x0031) ITU G.721 ADPCM 80 (0x0050) MPEG 65,536 (0xFFFF) Experimental 第二节 真实wav文件分析实例分析: 下面notepad++打开一个wav的文件,看看里面十六进制内容。开始四个字节从小到大分别是0x52H、0x49H、0x46H、0x46H,分别对应ASCII的R、I、F、F字符。 根据第一节的格式介绍,其后四个字节是整个文件除去ChunkID、ChunkSize的大小,这里是00003224H,十进制为12836。从偏移字节地址8开始的四个字节为Format字段,内容为0x57415645,其对应的就是ASCII字符W、A、V、E。 0c~0f字节内容表示subchunk1ID的值“fmt(空格)”。 10H~13H值为ox00000010,十进制为16,表明该字块剩余占空间大小,14H-15H值为0001H,表明当前编码PCM。 16H-17H,表明通道数,为1;18H-1bH表明采样率,0x00001f40,十进制为8000。紧接着四个字节表明字节率,每个采样点占两个字节,那么该值为0x00003e80,十进制为16000。20H-21H对应BlockAlign,表示每个样本点占字节数0x0002,两个字节;22H-23H表明每个样本点占bit为数,0x0010,16个,与前一个值两个字节对应上。该音频文件是PCM格式,那么fmt子chunk在10H-13H为16,表明到该头字段结束处的剩余字节数为16,从14H到23H(包括23H字节)正好是16个字节。 24H-27H的值为ASCII字符d、a、t、a。其后四个字节为小端方式存储的数据“0x00003200”,表明音频数据段的大小为0x00003200,十进制为12800。从2c的a8H到322b的00H结束,正好是3200H个字节。 再看一个双声道的实例。下图是一个双声道的音频文件。 16H-17H,表明通道数,为2;18H-19H表明采样率,0x0000ac44,十进制为44100,表明当前wav录音文件的采样率是44.1K。紧接着四个字节表明字节率,每个采样点占两个字节,且是双声道,那么该值为ox0002b110,十进制为176400(44100*4)。20H-21H对应BlockAlign,表示每个样本点占字节数0x0004,4个字节,它是每个采样点占2个字节*双声道;22H-23H表明每个采样点占bit位数,0x0010,16位。 注释:采样点占bit位:是对波形进行离散采样,表示深度分8位、16位、24位等,不考虑声道数目;样本点占字节数得考虑声道数,双声道是单声道的2倍。 第三节 python读取wav文件读取上面给的第一个示例wav文件 # coding: utf-8 import wave from typing import Any, Dict import numpy as np nchannels = 0 sampwidth = 0 framerate = 0 def _WriteWav(fp: str, data: Dict) -> None: # 打开WAV文档 f = wave.open(fp, "wb") # 配置声道数、量化位数和取样频率 f.setnchannels(nchannels) f.setsampwidth(sampwidth) f.setframerate(framerate) # 将wav_data转换为二进制数据写入文件 f.writeframes(data) f.close() def _ReadWave(wav_path: str) -> None: global nchannels, sampwidth, framerate f = wave.open(wav_path, 'rb') params = f.getparams() nchannels, sampwidth, framerate, nframes = params[:4] print("时常:", float(nframes / framerate)) print("channels:", nchannels) print("sampewidth:", sampwidth) print("framerate:", framerate) print("framenumber:", nframes) strData = f.readframes(nframes) # 读取音频,字符串格式 print("len of strdata: ", len(strData)) waveData = np.fromstring(strData, dtype=np.int16)#将字符串转化为int if __name__ == "__main__": filename = "data/20200423152348/0.wav" _ReadWave(filename)Import wave包,通过open函数读取wav文件。 输出结果: 时常: 0.8 channels: 1 sampewidth: 2 framerate: 8000 framenumber: 6400 len of strdata: 12800 WAVE_FORMAT_PCM = 0x0001 _array_fmts = None, 'b', 'h', None, 'i' import audioop import struct import sys from chunk import Chunk from collections import namedtuple class Wave_read: class Wave_write: def open(f, mode=None):wave包主要包含三部分,函数open、类Wave_read、类Wave_write。 其中open的代码如下: def open(f, mode=None): if mode is None: if hasattr(f, 'mode'): mode = f.mode else: mode = 'rb' if mode in ('r', 'rb'): return Wave_read(f) elif mode in ('w', 'wb'): return Wave_write(f) else: raise Error("mode must be 'r', 'rb', 'w', or 'wb'")最终返回write或者read的对象,根据mode参数来选择。所以在_ReadWave函数中f = wave.open(path, ‘rb’),表明f是一个Wave_read对象。对wave文件的读取主要在Wave_read中。下面看看该类的主要代码。 class Wave_read: def initfp(self, file): self._convert = None self._soundpos = 0 self._file = Chunk(file, bigendian = 0) if self._file.getname() != b'RIFF': raise Error('file does not start with RIFF id') if self._file.read(4) != b'WAVE': raise Error('not a WAVE file') self._fmt_chunk_read = 0 self._data_chunk = None while 1: self._data_seek_needed = 1 try: chunk = Chunk(self._file, bigendian = 0) except EOFError: break chunkname = chunk.getname() if chunkname == b'fmt ': self._read_fmt_chunk(chunk) self._fmt_chunk_read = 1 elif chunkname == b'data': if not self._fmt_chunk_read: raise Error('data chunk before fmt chunk') self._data_chunk = chunk self._nframes = chunk.chunksize // self._framesize self._data_seek_needed = 0 break chunk.skip() if not self._fmt_chunk_read or not self._data_chunk: raise Error('fmt chunk and/or data chunk missing') def readframes(self, nframes): if self._data_seek_needed: self._data_chunk.seek(0, 0) pos = self._soundpos * self._framesize if pos: self._data_chunk.seek(pos, 0) self._data_seek_needed = 0 if nframes == 0: return b'' data = self._data_chunk.read(nframes * self._framesize) if self._sampwidth != 1 and sys.byteorder == 'big': data = audioop.byteswap(data, self._sampwidth) if self._convert and data: data = self._convert(data) self._soundpos = self._soundpos + len(data) // (self._nchannels * self._sampwidth) return data def _read_fmt_chunk(self, chunk): wFormatTag, self._nchannels, self._framerate, dwAvgBytesPerSec, wBlockAlign = struct.unpack_from('' else: strflag = ' |
今日新闻 |
点击排行 |
|
推荐新闻 |
图片新闻 |
|
专题文章 |
CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭 |