Windows录音API学习笔记
结构体和函数信息
结构体
WAVEINCAPS
该结构描述了一个波形音频输入设备的能力。
typedef struct {
WORD wMid; 用于波形音频输入设备的设备驱动程序制造商标识符。
WORD wPid; 声音输入设备的产品识别码。
MMVERSION vDriverVersion; 用于波形音频输入设备的设备驱动程序的版本号。高位字节是主版本号,低字节是次版本号。
CHAR szPname[MAXPNAMELEN]; 设备名称
DWORD dwFormats; 所支持的标准格式。可以是以下组合:
WORD wChannels; 数值指定设备是否支持单(1)或立体声(2)的输入
WORD wReserved1; 填充
} WAVEINCAPS;
HWAVEIN 目前推测是打开声音设备后获取的句柄(现在看就是句柄)
WAVEHDR
此结构定义用于标识一个波形音频缓冲器中的报头。
typedef struct {
LPSTR lpData; 指向波形缓冲区。
DWORD dwBufferLength; 缓冲区长度
DWORD dwBytesRecorded; 被用于输入时缓冲区的数据长度
DWORD dwUser; 用户数据
DWORD dwFlags; 标志提供有关缓冲区的信息。详见MSDN
DWORD dwLoops; 循环播放的次数,仅用于输出缓冲器
struct wavehdr_tag * lpNext; 保留
DWORD reserved; 保留
} WAVEHDR;
WAVEFORMATEX
该结构定义的波形的音频数据的格式。只有共同所有波形的音频数据格式的格式信息被包括在这种结构。对于需要更多的信息格式,该结构被包括在另一种结构第一部件,伴随着的附加信息。
typedef struct {
WORD wFormatTag; 波形音频格式类型。格式标记注册的微软公司的很多压缩算法。格式标签的完整列表可以在MMREG.H头文件中找到。
WORD nChannels; 在波形的音频数据的通道数。单声道的数据使用一个通道,立体声数据使用两个通道。
DWORD nSamplesPerSec; 采样速率,以每秒(赫兹)的样品,每个通道应被播放或录制。如果wFormatTag是WAVE_FORMAT_PCM,然后nSamplesPerSec共同的价值观是8.0千赫,11.025千赫,22.05 kHz和44.1 kHz的。对于非PCM格式,这件必须根据制造商的格式标记的规范来计算。
DWORD nAvgBytesPerSec; 所需的平均数据传输速率,以每秒字节的格式标记。如果wFormatTag是WAVE_FORMAT_PCM,nAvgBytesPerSec应等于nSamplesPerSec和nBlockAlign的乘积。对于非PCM格式,这件必须根据制造商的格式标记的规范来计算。
播放和录制软件,可以通过使用nAvgBytesPerSec成员估计缓冲区大小。
WORD nBlockAlign;
块对齐,以字节为单位。块对齐用于wFormatTag格式类型数据的最小基本单位。如果wFormatTag是WAVE_FORMAT_PCM,nBlockAlign应等于nChannels和wBitsPerSample的乘积/8(每字节比特)。对于非PCM格式,这件必须根据制造商的格式标记的规范来计算。
播放和录制软件必须处理的数据nBlockAlign字节的倍数的时间。书面和从设备读取数据必须开始于一个块的开始。例如,它是非法的开始播放在样品的中间PCM数据(也就是,在一个非块对齐的边界)。
WORD wBitsPerSample;
采样位数用于wFormatTag格式类型。如果wFormatTag是WAVE_FORMAT_PCM,然后wBitsPerSample应等于8或16。对于非PCM格式,这件必须根据制造商的格式标记的规范来设置。请注意,某些压缩方案不能定义为wBitsPerSample一个值,因此该构件可以是零。
WORD cbSize;
大小以字节为单位的额外格式信息追加到WAVEFORMATEX结构的末端。此信息可用于通过非PCM格式来存储额外用于wFormatTag属性。如果没有额外的信息所必需的wFormatTag,这个部件必须被设置为零。请注意,对于WAVE_FORMAT_PCM格式(只有WAVE_FORMAT_PCM格式),这个成员被忽略。
} WAVEFORMATEX;
函数信息
//这里获取到声音设备句柄使用的是waveInOpen函数,在那里进行的初始化。
返回装置的数量。返回的零值表示没有设备存在或发生了错误。
UINT waveInGetNumDevs(VOID);
该函数检索一个给定的波形音频输入设备的能力。
MMRESULT waveInGetDevCaps(
UINTuDeviceID, 标识符波形音频输出的装置。它可以是一个设备标识符或一个开放波形音频输入设备的一个句柄。
LPWAVEINCAPSpwic, 指向一个WAVEINCAPS结构填充有关器件的信息的功能
UINTcbwic WAVEINCAPS结构体的字节数
);
MMRESULT waveInOpen(
LPHWAVEINphwi,指向打开的声音设备的标识句柄。在fdwOpen参数指定为WAVE_FORMAT_QUERY时这个参数可以为空
UINTuDeviceID, 要打开设备的标识符
LPWAVEFORMATEXpwfx, 指向一个WAVEFORMATEX结构,它标识用于记录波形音频数据所需的格式。您可以waveInOpen返回后立即释放此结构。
DWORDdwCallback, 指针指向一个固定的回调函数,事件句柄,句柄到窗口或线程波形音频录制过程中被调用来处理与记录的进度消息的标识符。如果没有回调功能是必需的,则该值可以是零。
DWORDdwCallbackInstance,传给回调机制的数据类型,此参数并不用于窗口回调
DWORDfdwOpenz指明dwCallBack传入的数据类型,比如时间句柄或者函数指针
);
该函数为音频输入设备提供缓冲区
MMRESULT waveInPrepareHeader(
HWAVEINhwi, 声音输入设备句柄
LPWAVEHDRpwh, 指向WAVEHDR结构,标识要准备的缓冲区。
UINTcbwhWAVEHDR结构的大小
);
该函数发送一个输入缓冲区给定的波形音频输入设备。当缓冲区被填满后,通知应用程序。
MMRESULT waveInAddBuffer(
HWAVEINhwi, 声音输入设备句柄
LPWAVEHDRpwh, 指向WAVEHDR结构,标识要准备的缓冲区。
UINTcbwhWAVEHDR结构的大小
);
该函数启动指定设备的输入
MMRESULT waveInStart(
HWAVEINhwi 设备句柄
);
基于Windows API的录音分析
昨天看了一天的Audio API和windows的Wav文件相关资料,渐渐的理清了一点思路,所以在此总结一下,待本文已完成之后,应该就能继续下一步了。
1 首先要了解的是计算机是如何表示声音文件的。
我们熟知的音频格式有:MP3,WMA,FLAC,以及WAV。这里我暂时只关注WAV。要知道的是,WAV其实就是WAVE,意思为波形。真实世界中的声音都是连续的,因为是模拟信号,但是在计算机中存储的信息都是数字信号。所以在将声音存储到计算机之前,就必须要进行声音的数字化,转换成计算机能够存储的形式。
学过信号与系统的应该都知道,模拟信号转换为数字信号,一种比较通用的方法就是进行等间隔采样。根据奈奎斯特定理,采样频率至少为信号频率的2倍,才能无失真的保存原有的音频信号。因此采样频率的高低决定了数字信号的保真度,自然是越高越好。打个比方,一个周期为1ms的正弦信号,采两个点和采100个点的信号在还原成模拟信号的时候,肯定是采100个点信号的还原效果更好。
在对模拟信号完成采样后,得到的是一系列的离散电压信号。因此在将数据存储至计算机前,还需要对这些模拟信号进行量化。这里所谓量化,就是用二进制数据来表示电平的大小。一般采用8位(256级)或者16位(65536级)的数据来表示,在硬件级的设计中,需要根据ADC的具体情况来决定。而在Windows中,可以使用waveInGetDevCaps函数获取声卡信息,以判断使用8位或者16位的量化采样位数。在其参数的dwFormats 成员中,包含了相应的信息,具体如下:
假设0-5V电平的正弦信号,在采用8位的量化下,就将0-5V分成256个梯度,每个梯度的电压差值为0.0195V。一个4V的采样值对应的数值就是205(204.8)。
在经过上面两步的处理之后,模拟声音信号就转化为了具有固定采样频率和相应量化标准的数字信号。所以数字声音信号最主要的两个参数就是采样位宽和采样频率。
另外一个需要注意的就是声道。我们平时听到的音乐都是立体声,也就是双声道的。这个在WAV文件格式中有专门的介绍,不过我没细看……记得是交替出现的。
现在回过头来看一下上面的表,声卡在提供信号的时候,也就上面这三个主要因素:采样频率,声道,量化位宽。
我根据WAV文件头的资料写了一份代码,打开了并查看了一个我计算机上的WAV格式的音乐文件,以前在CD上拷的。
运行结果如下:
不过这个就是看一看,暂时还用不到,有了上面的基础,接下来要做的事分析API的使用。
第一个要调用的函数:
UINT waveInGetNumDevs(VOID);
这个函数的仅仅是用来查看计算机上是否有声卡设备,如果返回值是0,那么表明没有声卡。不过一般这种情况不会发生,毕竟现在的计算机主板都是集成声卡的。
第二个要调用的函数:
MMRESULT waveInGetDevCaps(
UINTuDeviceID,
LPWAVEINCAPSpwic,
UINTcbwic);
这个函数的第一个参数是设备ID,不过我们现在并不知道设备ID,但是没关系,只要我们知道有设备存在就可以了。不过在MSDN上有这么一句让我比较费解的话:
“Use this function to determine the number of waveform-audio input devices present in the system.”
中文意思就是:使用这个函数来决定系统中声音输入设备的数量。我看这个函数名感觉更像是获取设备能力信息,而且第二个参数更是直接点出了其目的,“声音输入设备的能力”。
回来接着说第一个参数,MSDN在Remarks说明了这个参数可以是从0值到设备数量中的任何一个数,或者使用WAVE_MAPPER。我估计这个WAVE_MAPPER就是0值,因为传入的参数要求是个指针,所以,你懂的。因此只要填个0就可以了。不过至于多声卡设备具体怎么玩,我还真的不太清楚。
第二个参数是一个指向WAVEINCAPS结构的指针,在这把这个结构好好剖析一下:
typedef struct {
WORD wMid;
WORD wPid;
MMVERSION vDriverVersion;
CHAR szPname[MAXPNAMELEN];
DWORD dwFormats;
WORD wChannels;
WORD wReserved1;
} WAVEINCAPS;
前两个结构体成员分别是设备制造商信息和产品标示符,我点击去看了下,发现一堆的设备制造商信息的宏定义……不用太关心。
第三个成员是MMVERSION,其本质就是一个UINT,驱动设备版本号,也不用太关心。
第四个成员是设备名称,就是一个以’