windows声音应用程序开发指南第2章WAV文件格式_第1页
windows声音应用程序开发指南第2章WAV文件格式_第2页
windows声音应用程序开发指南第2章WAV文件格式_第3页
windows声音应用程序开发指南第2章WAV文件格式_第4页
windows声音应用程序开发指南第2章WAV文件格式_第5页
已阅读5页,还剩69页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第2章 WAV文件格式,2.1 WAV文件格式1,2,3,4 2.2 保存为WAV文件格式1,2,3,4,5 2.3 压缩WAVE音频5 2.4 本 章 小 结,2.1 WAV文件格式1,2,3,4,2.1.1 RIFF文件和WAV文件格式 1RIFF文件 RIFF可以看做是一种树状结构,其基本构成单位为“块”(Chunk),它犹如树状结构中的节点。每个Chunk由“辨别码”(ID)、“数据大小”(Size)和“数据”(Data)所组成,如表2-1所示。,表2-1 RIFF文件格式,一般而言,Chunk本身并不允许内部再包含Chunk,但有两种情况例外,即分别以“RIFF”及“LIST”为辨别

2、码的Chunk可以包含子Chunk。 以“RIFF”为辨别码的Chunk包含子Chunk的格式如图2-1所示。图中前4个字符,图2-1 RIFF文件结构,2WAV文件 WAV为WAVEFORM(波形)的缩写。WAV文件的结构如图2-2所示,RIFF Chunk中子Chunk的“格式辨别码”为“WAVE”。整个文件由两个Chunk所组成:辨别码“fmt”(注意,最后一个是空白字符)及“data”。,图2-2 WAV文件结构,在“fmt”的Chunk下包含了一个PCMWAVEFORMAT数据结构,该结构与前一章中的WAVEFORMATEX结构类似,其定义如下: typedef struct wav

3、eformat - tag WORDwFormatTag ; WORD nChannels; DWORDnSamplesPerSec; DWORD nAvgBytesperSec; WORDnBlockAlign; WAVEFORMAT;,typedef struct pcmwaveformat - tag WAVEFORMAT wf ; WORD wBitsPerSample; PCMWAVEFORMAT;,其意义分别如下: wFormatTag:记录着此声音的格式代号,例如WAVE_FORMAT_PCM,WAVE_FORMAT_ADPCM等等。 nChannels:记录声音的声道数。 nS

4、amp1esPerSec:记录每秒采样数(采样率)。 nAvgBytesPerSec:记录每秒的平均数据量。 nBlockA1ign:记录块的对齐单位。 wBitsPerSample:记录每个采样样本所需的位元数。,“data”Chunk包含真正的声音数据。Windows目前仅提供WAVE_FORMAT_PCM一种数据格式,其所代表的意义是脉冲编码调制(PCM,Pu1se Code Modulation)。 用编辑工具UltraEdit查看WAV文件的文件头,如图2-3所示。 表2-2以数据在文件中的存放位置说明了WAV文件格式,其中偏移地址是相对于文件头的地址。,图2-3 WAV文件的文件头

5、,表2-2 WAV文件格式说明表,Windows定义了在“data”Chunk中数据的存放情形,表2-3列出了四种不同声道数及取样所需的位元数以及位元位置的安排。其中: 对于8位单声道,每个样本数据由8位(bit)表示; 对于8位立体声,每个声道的数据由一个8位(bit)数据表示,且第一个8位(bit)数据表示0声道(左)数据,紧随其后的8位(bit)数据表示1声道(右)数据; 对于16位单声道,每个样本数据由16位(bit)表示;,对于16位立体声,每个声道的数据由一个16位(bit)数据表示,且第一个16位(bit)数据表示0声道(左)数据,紧随其后的16位(bit)数据表示1声道(右)数

6、据。,表2-3 PCM数据的存放方式,WAV文件的每个样本值包含在一个整数i中,i的长度为容纳指定样本长度所需的最小字节数。首先存储低有效字节,表示样本幅度的位放在i的高有效位上,剩下的位置为0,这样8位和16位的PCM波形样本的数据格式如表2-4所示。,表2-4 PCM数据的存放方式,2.1.2 WAV文件信息的具体应用 WAV文件包括了对原始声音的高速率采样数据,并且以WAVE_PCM_FORMAT格式的形式保存。在读出WAV文件头信息之后,接着的数据就是原始声音的高速率采样信息。我们可以在Visual C+程序中对这些信息作多方面的处理,其中包括:, 波形显示 我们可以以时间振幅的方式显

7、示出原始声音的波形,这是最简单同时也是最直接的信息处理方式。在时间范围内,我们可以观察该信号波形是否连续,中间是否有跳变等。 频谱显示 我们可以以频率振幅的方式显示出原始声音的频谱,在对原始信号经过FFT变换之后,可以得到该信号的频谱,进而得到该信号的能量集中带、分布特征、谱对称系数等等。, 用于语音信号识别 讲话者的个体识别是语音信号处理的一个重要内容,但它的一个前提条件是必须提供语音信号的数字波形。通常的方法是将原始的语音信号进行放大、抗混叠滤波、A/D采样、数值编码,最终得到语音信号的数字波形。,2.2 保存为WAV文件格式1,2,3,4,5,2.2.1 创建一个空文件 用系统函数Cre

8、ateFile创建一个空文件,其程序如下: #include FILE* m_fp = NULL;/文件句柄 DWORDdw=0; /文件长度,DWORD dwTotalAudioLength=0;/声音数据长度 HANDLEOpen(LPCTSTR lp) SECURITY_ATTRIBUTESsa; sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = FALSE; return CreateFile(lp, GENERIC_WRITE, 0, ,2.2.2

9、 写WAV文件头 首先,要得到声音采样数据的相关信息,通常这些信息存储在一个WAVEFORMATEX结构中,用系统函数WriteFile将文件头信息写入新创建的文件。其程序如下: BOOL WriteWave(char *Des, WAVEFORMATEX wfx) longcbFmtChunk, cbDataChunk;,/m_fp在前面已定义过 m_fp = fopen ( Des , w+b ); if (!m_fp ) return FALSE; /打开文件出错 cbFmtChunk = sizeof(WAVEFORMATEX) +wfx.cbSize;/WAVEFORMATEX结构长

10、度 dw= 46;/46为文件头的长度 dwTotalAudioLength=0; cbDataChunk = dwTotalAudioLength;,fwrite ( RIFF,1, sizeof (DWORD), m_fp ); /RIFF标识符(占用4 Byte) fwrite ( /WAVEFORMAT结构的长度(4Byte) ,fwrite ( ,2.2.3 写声音数据 将给定缓冲区中声音数据写入WAV文件,其程序如下: BOOL WriteWave(LPBYTE lpBufferData, DWORD dwDataSize) if (dwDataSize = 0),return F

11、ALSE; else fwrite(lpBufferData, 1, dwDataSize, m_fp); dw+= dwDataSize;/文件长度随着增加 dwTotalAudioLength=dwTotalAudioLength+ dwDataSize;/声音数据长度随着增加 return TRUE; ,fseek( m_fp, 42, SEEK_SET );/设置文件指针 fwrite( ,2.3 压缩WAVE音频5,2.3.1 CODECs介绍 Microsoft的Windows 95/98/NT/2000操作系统都具有能通过安装的CODECs处理编码的WAVE格式的音频和视频数据流

12、的能力。 一个CODEC是一小段用于编码(Code)及解码(DECode)数据流的代码(因此得名CODEC)。许多CODECs既能编码又能解码。而一些CODECs仅能用于解码,这样私有数据可以在系统上播放,但数据格式不能在系统上创建。,2.3.2 系统中有什么CODECs Microsoft的Windows 95/98/NT/2000本身附带有几种标准的CODECs,也可由系统中所安装的应用程序安装其它的 CODECs。例如,DSP Group公司的TrueSpeech CODEC随Windows 95发送,因此,用户写的任何基于Windows 95的应用程序都可使用此CODEC(只要用户没有

13、在控制面板中删除它或禁止它)。,所有安装的CODECs都由音频编码解码器(ACM)管理(见图2-4)。我们可以用一段小程序从ACM中查到安装了哪些CODECs,它们都支持什么格式。也可双击控制面板中的多媒体选项,选择高级标签,就能看到系统中所安装的CODECs。 下面用一段程序介绍如何应用ACM。首先从调用ACM编程接口所需的包含的头文件开始:,#include #include #include / 多媒体注册 #include / 音频编码解码器 #include ,图2-4 音频编码解码器,mmsystem.h头文件包含了Windows支持的大部分的多媒体功能,但不包含ACM接口及任何厂

14、商定义。 mmreg.h包含了对不同厂商设计的各种WAVE数据类型的格式标签的定义。它也包含了用于处理不同的WAVE数据类型的结构(基于WAVEFORAMTEX)的定义。 msacm.h包含了ACM所需的API、标志等等。 我们要做的第一件事就是执行一些常见的ACM查询来判断版本号,获取它当前管理了多少个驱动程序的信息。下面是查询ACM的部分代码:,DWORD dwACMVer = acmGetVersion(); printf(ACM version %u.%.02u build %u, HIWORD(dwACMVer) 8, HIWORD(dwACMVer) ,acmGetVersion函

15、数返回一个32位的十六进制数值,表示ACM的版本号,其形如0 xAABBCCCC。其中,AA代表ACM的主版本号;BB代表ACM的副版本号;CCCC代表ACM的debug版本的build号,对于一个非debug版本(零售版)的ACM,CCCC总为0。例如,在应用程序中返回版本号为0 x05000856,则ACM版本号为5.00,且系统中安装的是debug版本,build号为2134(十进制)。,用acmMetrics函数可以知道系统中安装了多少个CODECs驱动程序: DWORD dwCodecs = 0; MMRESULTmmr=acmMetrics(NULL, ACM_METRIC_COU

16、NT_CODECS, ,对ACM有了简单了解后,现在可以要求它枚举出系统中当前所有的驱动程序。我们在程序中所调用的枚举函数使用回调函数来显示每个设备的数据,这在Windows编程中是一种很普遍的方法。下面的调用就是枚举当前ACM所管理的所有设备: / 枚举所有允许的驱动程序 printf(Enabled drivers:n); mmr = acmDriverEnum(DriverEnumProc, 0, 0); if (mmr) /show_error(mmr);,acmDriverEnum函数列举出可用的ACM驱动程序,第一个参数指出回调函数的地址。现在,让我们看看枚举回调函数DriverE

17、numProc,它由系统中的每一个驱动程序调用: BOOL CALLBACK DriverEnumProc(HACMDRIVERID hadid, DWORD dwInstance, DWORD fdwSupport) printf( id: %8.8lxH, hadid); printf( supports:n);,if(fdwSupport,/ 获得一些具体信息 ACMDRIVERDETAILS dd; dd.cbStruct = sizeof(dd); MMRESULT mmr = acmDriverDetails(hadid, else ,printf( Short name: %sn

18、, dd.szShortName); printf( Long name: %sn, dd.szLongName); printf( Copyright: %sn, dd.szCopyright); printf( Licensing: %sn, dd.szLicensing); printf( Features: %sn, dd.szFeatures); printf( Supports %u formatsn, dd.cFormatTags); printf( Supports %u filter formatsn, dd.cFilterTags); ,/ 打开驱动程序 HACMDRIVE

19、R had = NULL; mmr = acmDriverOpen(,mmr = acmMetrics(had, ACM_METRIC_MAX_SIZE_FORMAT, ,if (mmr) printf( ); /show_error(mmr); free(pwf); acmDriverClose(had, 0); return TRUE; / 继续枚举 ,为结构分配了空间后,现在可以用acmFormatEnum来枚举所支持的格式。这次又用到一个回调函数来取得枚举出的所支持格式的相关数据: BOOL CALLBACK FormatEnumProc(HACMDRIVERID hadid, LPA

20、CMFORMATDETAILS pafd, DWORD dwInstance, DWORD fdwSupport) printf( %4.4lXH, %sn, pafd-dwFormatTag, pafd-szFormat); return TRUE; / 继续枚举 ,2.3.3 使用特定的CODEC 1两步实现编码 在理想的情况下,编码一些数据可能只要向系统发出命令“这有一些数据,请编码成这种格式”即可。但实际上,Windows编程与理想相去甚远,在当前的现实中,我们得自已做许多琐碎的工作。,2编码的实现过程 在本例中,实现编码分以下四个阶段: (1) 创建一些WAV格式数据的样本; (2)

21、 找到一个合适的CODEC; (3) 将数据转换为该CODEC可处理的中间格式; (4) 将数据转换成所需的格式。,为了简单起见,源数据用程序创建,而不是录入或是从WAV文件中读取: / 首先我们创建一个可能是刚刚才录制的WAV,其格式为11.025 kHz, 8位单声道PCM,这是一个 / 在所有机器上都可用的录入格式。例如1秒长的1 kHz的正弦波WAVE,刚好1000个周期 WAVEFORMATEX wfSrc; memset(,wfSrc.wFormatTag = WAVE_FORMAT_PCM; / PCM wfSrc.nChannels = 1; / 单声道 wfSrc.nSamp

22、lesPerSec = 11025; / 11.025 kHz wfSrc.wBitsPerSample = 8; / 8 bit wfSrc.nBlockAlign = wfSrc.nChannels * wfSrc.wBitsPerSample / 8; wfSrc.nAvgBytesPerSec = wfSrc.nSamplesPerSec * wfSrc.nBlockAlign; DWORD dwSrcSamples = wfSrc.nSamplesPerSec;,BYTE* pSrcData = new BYTE dwSrcSamples; / 分配1秒种的长度内存空间 BYTE*

23、pData = pSrcData; double f = 1000.0; double pi = 4.0 * atan(1.0); double w = 2.0 * pi * f; for (DWORD dw = 0; dw dwSrcSamples ;dw+) *(pData+dw)= w; ,上面的代码创建了一个WAVEFORMATEX结构用来描述源数据格式,并用简单的数学方法生成了1秒钟长的11.025 kHz,8位单声道的PCM的WAVE数据。 下一步就是选择要将数据转换成什么格式及选定一个合适的CODEC。 WORD wFormatTag = WAVE_FORMAT_DSPGROUP

24、_TRUESPEECH; / 现在我们选定一个支持目标格式标签的CODEC HACMDRIVERID hadid = find_driver(wFormatTag);,if (hadid = NULL) printf(No driver foundn); exit(1); printf(Driver found (hadid: %4.4lXH)n, hadid);,find_driver函数枚举所有的驱动程序,直到找到一个支持给定标签值的驱动程序(本例为WAVE_FORMAT_DSPGROUP_TRUESPEECH)。之所以没有在此给出代码,是因为它与前面的枚举代码非常相像。随后你可以查看它是

25、怎样工作的。 选定了驱动程序后,现在要为最终驱动程序将要产生的压缩数据格式创建一个WAVEFORMATEX结构,并为驱动程序用于输入的中间PCM格式产生一个WAVEFORMATEX结构。,/ 获得格式的详情 / 注意:这只是一个给定格式标签的第一种或是最可能的格式 WAVEFORMATEX* pwfDrv = get_driver_format(hadid, wFormatTag); if (pwfDrv = NULL) printf(Error getting format infon); exit(1); ,printf(Driver format: %u bits, %lu sample

26、s per secondn, pwfDrv-wBitsPerSample, pwfDrv-nSamplesPerSec); / 获取驱动程序所支持的PCM格式标签 / 注意:我们只是选取CODEC所支持的枚举出的第一种PCM格式,但不一定是最好的选择 WAVEFORMATEX* pwfPCM = get_driver_format(hadid, WAVE_FORMAT_PCM); if (pwfPCM = NULL) printf(Error getting PCM format infon); exit(1); ,printf(PCM format: %u bits, %lu samples

27、 per secondn, pwfPCM-wBitsPerSample, pwfPCM-nSamplesPerSec); 还要进一步强调的是,get_driver_format函数仅仅枚举出第一种匹配的格式,也许不能获得最好的质量。,让我们看看第一步的转换,它完成的是将源数据转换为中间格式: / 将源WAVE转换为CODEC所支持的PCM格式 / 我们使用任一种能实现PCM格式间相互转换的驱动程序 HACMSTREAM hstr = NULL; mmr = acmStreamOpen( exit(1); / 为转换结果开辟一个缓冲区 DWORD dwSrcBytes = dwSrcSample

28、s * wfSrc.wBitsPerSample / 8; DWORD dwDst1Samples =dwSrcSamples * pwfPCM-nSamplesPerSec / wfSrc.nSamplesPerSec; DWORD dwDst1Bytes = dwDst1Samples * pwfPCM-wBitsPerSample / 8;,BYTE* pDst1Data = new BYTE dwDst1Bytes; / 填写转换信息 ACMSTREAMHEADER strhdr; memset( / 转换数据,printf(Converting to intermediate PCM

29、 format.n); mmr = acmStreamConvert(hstr, ,最后一步是将中间格式转换为最终的压缩格式: / 将中间格式转换为最终的压缩格式 / 打开驱动程序 HACMDRIVER had = NULL; mmr = acmDriverOpen( ,/ 打开转换流 / 注意使用了ACM_STREAMOPENF_NONREALTIME标志 / 若没有此标志,一些软件压缩程序会报告512号错误,即不可能 mmr = acmStreamOpen( exit(1); / 为转换结果分配一个缓冲区 / 根据以字节计的平均速率计算输出缓冲区的尺寸 / 并加上一机动位(bit),/ 若没有此额外的空间,IMA_ADPCM驱动程序将不能转换 DWORD dwDst2Bytes=pwfDrv-nAvgBytesPerSec *dwDst1Samples/ pwfPCM-nSamplesPerSec; dwDst2Bytes = dwDst2Bytes * 3 / 2;

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论