音频在现实中一些序列振动的波形,如果转化为电压信号,则是电压的振动信号。通过麦克风进行输入时,需要对电压信号进行采样,因此就有一个采样频率的概念。根据奈斯特定理,采样频率需要大于2倍的信号最高频率,其单位为Hz,意思是每秒样本数。而在windows系统中,还有个“sample”-即样本的概念,所谓样本,就是每一次采样时得到的电压信号,并且以二进制的形式存储。根据采样精度的不同,每个样本可以采用不同的位数进行存储,比如8位或者12位,当然是位数越高也能真实反映声音的变化细节了。在多数机器上有两个喇叭(左、右),这就是两个不同的声道,通过将不同数据放入不同声道,将会在左声道和右声道得到不同的播放声音。在系统中,操作系统提供了方便的双声道操作方法。并且左右声道的样本数据都是交错存储,即按照左、右、左、右、......的顺序存储。比如CD品质的音频采样频率是44100 Hz,而样本位数为16bits,这说明1MB的音频数据可需要播放6秒的时间,即1MB/2/44100=1024*1024/2/44100/2=5.944。第一个2是说一个样本有2字节,而第二个2说明是双声道,也就是每次采样都是采样两个声道。
播放声音其实就是把数字信号发送到声卡,然后进行DAC,变成模拟信号驱动喇叭发出声音,如果不是输出到喇叭而是直接使用,则可以作为信号发生器使用。如果要在声卡产生波形,有几种方法:
1)采用API函数PlaySound
BOOL WINAPI MessageBeep(UINT uType);//播放相应的系统提示声音 BOOL sndPlaySound(LPCSTR lpszSound,UINT fuSound);//播放文件名、注册键或WIN.INI中指定的音频。并且可以通过fuSound指明异步、同步或循环播放方式
BOOL PlaySound(LPCSTR pszSound, HMODULE hmod,DWORD fdwSound)
这是一个操作系统API函数,参数pszSound是指定了要播放声音的字符串,该参数可以是WAVE文件的名字,或是WAV资源的名字,或是内存中声音数据的指针,或是在系统注册表WIN.INI中定义的系统事件声音。如果该参数为NULL则停止正在播放的声音。参数hmod是应用程序的实例句柄,除非pszSound的指向一个资源标识符(即fdwSound被定义为SND_RESOURCE),否则必须设置为NULL。参数fdwSound是标志的组合,如下表所示。若成功则函数返回TRUE,否则返回FALSE。
SND_APPLICATION 用应用程序指定的关联来播放声音 SND_ALIAS pszSound参数指定了注册表或WIN.INI中的系统事件的别名 SND_ALIAS_ID pszSound参数指定了预定义的声音标识符 SND_ASYNC 用异步方式播放声音,PlaySound函数在开始播放后立即返回 SND_FILENAME pszSound参数指定了WAVE文件名 SND_LOOP 重复播放声音,必须与SND_ASYNC标志一块使用 SND_MEMORY 播放载入到内存中的声音,此时pszSound是指向声音数据的指针 SND_NODEFAULT 不播放缺省声音,若无此标志,则PlaySound在没找到声音时会播放缺省声音 SND_NOSTOP PlaySound不打断原来的声音播出并立即返回FALSE SND_NOWAIT 如果驱动程序正忙则函数就不播放声音并立即返回 SND_PURGE 停止所有与调用任务有关的声音。若参数pszSound为NULL,就停止所有的声音,否则,停止pszSound指定的声音 SND_RESOURCE pszSound参数是WAVE资源的标识符,这时要用到hmod参数 SND_SYNC 同步播放声音,在播放完后PlaySound函数才返回。
采用sndPlaySound和PlaySound函数进行播放时将会将整个波形音频文件加载到内存中,因此限制了可以播放的最大文件大小,通常最好小于100K。
2)大多数的其它的方法都通过低级的API实现
auxGetNumDevs 获得系统中的辅助输出设备数量,可以获取声卡数目 waveInGetNumDevs 获得系统中的波形音频输入设备数量 waveOutGetNumDevs 获得系统中的波形音频输出设备数量 auxGetDevCaps 获取指定辅助输出设备的功能,可以得到声卡音量控制能力 waveInGetDevCaps 获取波形音频输入设备的功能 waveOutGetDevCaps 获取波形音频输出设备的功能
(a)重要的数据结构
/* MMTIME data structure */ typedef struct mmtime_tag { UINT wType; /*指明联合体中的数据类型*/ union { DWORD ms; /* 毫秒 */ DWORD sample; /*采样率*/ DWORD cb; /* 字节数*/ DWORD ticks; /* MIDI流中的滴答数*/
/* SMPTE */ struct { BYTE hour; /* 小时*/ BYTE min; /* 分钟*/ BYTE sec; /* 秒*/ BYTE frame; /* 帧 */ BYTE fps; /* 帧/秒 */ BYTE dummy; /* 填充字节 */ #ifdef _WIN32 BYTE pad[2]; #endif } smpte;
/* MIDI */ struct { DWORD songptrpos; /* 歌曲指针位置 */ } midi; } u; } MMTIME, *PMMTIME, NEAR *NPMMTIME, FAR *LPMMTIME;
其中的wType可以取值如下:
#define TIME_MS 0x0001 /*以毫秒为单位的时间*/ #define TIME_SAMPLES 0x0002 /* 波形采样数 */ #define TIME_BYTES 0x0004 /* 当前字节偏移量*/ #define TIME_SMPTE 0x0008 /* SMPTE 时间 */ #define TIME_MIDI 0x0010 /* MIDI时间*/ #define TIME_TICKS 0x0020 /* MIDI流中的滴答*/
多媒体扩展的windows消息
#define MM_JOY1MOVE 0x3A0 /* 游戏杆 */ #define MM_JOY2MOVE 0x3A1 #define MM_JOY1ZMOVE 0x3A2 #define MM_JOY2ZMOVE 0x3A3 #define MM_JOY1BUTTONDOWN 0x3B5 #define MM_JOY2BUTTONDOWN 0x3B6 #define MM_JOY1BUTTONUP 0x3B7 #define MM_JOY2BUTTONUP 0x3B8
#define MM_MCINOTIFY 0x3B9 /* MCI */
#define MM_WOM_OPEN 0x3BB /* 波形输出 */ #define MM_WOM_CLOSE 0x3BC #define MM_WOM_DONE 0x3BD
#define MM_WIM_OPEN 0x3BE /*波形输入 */ #define MM_WIM_CLOSE 0x3BF #define MM_WIM_DATA 0x3C0
#define MM_MIM_OPEN 0x3C1 /* MIDI 输入 */ #define MM_MIM_CLOSE 0x3C2 #define MM_MIM_DATA 0x3C3 #define MM_MIM_LONGDATA 0x3C4 #define MM_MIM_ERROR 0x3C5 #define MM_MIM_LONGERROR 0x3C6
#define MM_MOM_OPEN 0x3C7 /* MIDI 输出 */ #define MM_MOM_CLOSE 0x3C8 #define MM_MOM_DONE 0x3C9
几个重要的结构
波形音频输出数据类型
类型 描述
HWAVEOUT 波形音频输出设备的句柄
WAVEFORMATEX 波形音频设备所支持的指定的结构体,
WAVEHDR 波形音频输入数据的结构体头
WAVEOUTCAPS 经常用作查询特殊波形音频输出设备能力
typedef struct tagWAVEOUTCAPS { WORD wMid; /* 生产商ID*/ WORD wPid; /*产品ID */ MMVERSION vDriverVersion; /* 驱动器版本*/ CHAR szPname[MAXPNAMELEN]; /* 产品名称,以NULL结尾的字符串*/ DWORD dwFormats; /* 支持的格式,详见表一*/ WORD wChannels; /*支持的通道数 */ WORD wReserved1; /* 保留 */ DWORD dwSupport; /* 驱动器支持的功能*/ } WAVEOUTCAPS
typedef struct { WORD wMid; /* 生产商ID*/ WORD wPid; /*产品ID */ MMVERSION vDriverVersion; /* 驱动器版本*/ TCHAR szPname[MAXPNAMELEN]; /* 产品名称,以NULL结尾的字符串*/ WORD wTechnology; WORD wReserved1; /* 保留 */ DWORD dwSupport; } AUXCAPS;
typedef struct { WORD wMid; /* 生产商ID*/ WORD wPid; /*产品ID */ MMVERSION vDriverVersion; /* 驱动器版本*/ TCHAR szPname[MAXPNAMELEN]; /* 产品名称,以NULL结尾的字符串*/ DWORD dwFormats; /* 支持的格式,详见表一*/ WORD wChannels; /*支持的通道数 */ WORD wReserved1; /* 保留 */ } WAVEINCAPS;
/* 波形数据块头*/ typedef struct wavehdr_tag { LPSTR lpData; /* 锁定数据缓冲的指针 */ DWORD dwBufferLength; /* 数据缓冲区的长度*/ DWORD dwBytesRecorded; /* 只用于输入y */ DWORD_PTR dwUser; /* 客户端使用 */ DWORD dwFlags; /* 排序标志,查看flags定义*/ DWORD dwLoops; /* 循环控制计数器*/ struct wavehdr_tag FAR *lpNext; /* 保留用于驱动程序 */ DWORD_PTR reserved; /* 保留用于驱动程序*/ } WAVEHDR, *PWAVEHDR, NEAR *NPWAVEHDR, FAR *LPWAVEHDR;
/* * 用于所有非PCM格式的扩展波形格式,此结构对于所有的非PCM格式数据都是通用的*/
typedef struct tWAVEFORMATEX { WORD wFormatTag; /* 格式类型 */ WORD nChannels; /* 通道数量,比如单声道,立体声*/ DWORD nSamplesPerSec; /* 采样率 */ DWORD nAvgBytesPerSec; /* 用于缓冲估计*/ WORD nBlockAlign; /* 数据块大小 */ WORD wBitsPerSample; /* 单一数据块每个采样的数据位数*/ WORD cbSize; /* cbSize之后的数据大小,单位为字节。注:cbSize后面跟着数据 */ } WAVEFORMATEX, *PWAVEFORMATEX, NEAR *NPWAVEFORMATEX, FAR *LPWAVEFORMATEX;
(b)打开声音设备函数
打开声音设备需要使用WaveOutOpen函数,其原型为
WINMMAPI MMRESULT WINAPI waveOutOpen(LPHWAVEOUT phwo, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen)
参数介绍:
phwo-一个指向音频设备句柄的指针,如果fdwOpen设置为WAVE_FORMAT_QUERY,则这个参数可以设置为NULL。
uDeviceID-音频设备输出设备ID,也可以设置为WAVE_MAPPER,这时程序会根据波形数据格式自行选择打开合适的设备。
pwfx-WAVEFORMATEX结构的指针,包含要申请的波形格式
dwCallback-可以是回调函数地址,事件句、窗口句柄或一个将在波形音频回放时以便处理与回放进度相关消息的线程ID,如果不需要这些功能,则可以将其设置为NULL。
dwInstance-回调函数的实例数据,如果dwCallback为窗口句柄,则没有此参数
fwOpen-打开设置的选项,可选项有:
CALLBACK_EVENT dwCallback 参数栏是事件柄 CALLBACK_FUNCTION dwCallback 参数栏是CALLBACK过程地址 CALLBACK_NULL 默认的设置,即无CALLBACK进程 CALLBACK_THREAD dwCallback 参数栏是线程ID CALLBACK_WINDOW dwCallback 参数栏是窗口柄 WAVE_ALLOWSYNC 如果该项被设置,一个同步的设备能被打开。如果在打开一个同步驱动时没有用该项,设备打开将会失败 WAVE_FORMAT_DIRECT 如果设定该项,则ACM驱动器将不会对音频数据进行格式转换 WAVE_FORMAT_QUERY 如果设定该项,waveOutOpen询问设备来决定是否支持给定的格式,但设备实际上并没有被打开。 WAVE_MAPPED 该项被设定后uDeviceID参数表示一个被音频设备映射的波形设备。
成功后返回MMSYSERR_NOERROR ,否则返回以下值: 值 描述 MMSYSERR_ALLOCATED 表示资源已存在 MMSYSERR_BADDEVICEID 装置ID超出范围 MMSYSERR_NODRIVER 没有驱动 MMSYSERR_NOMEM 不能分配内存 WAVERR_BADFORMAT 企图打开一个不被支持的格式 WAVERR_SYNC 装置是可同步的,但waveOutOpen 没用有WAVE_ALLOWSYNC呼叫
一些多媒体计算机有多个波形音频输出设备,除非你想在系统中打开指定的波形音频输出设备,否则必须在打开设备时使用WAVE_MAPPER标识符打开设备。WaveOutOpen函数将会选择一个最符合所要播放数据格式的最佳设备。
你确定系统中有多少个设备类型时,你可以使用下列其中之一的函数来查询每一个设备的能力:
函数 描述
auxGetDevCaps 返回一个输出设备的能力
waveinGetDevCaps 返回一个波形音频输入设备的能力
waveOutGetDevCaps 返回一个波形音频输出设备的能力
下列的每一个函数填充一个指定设备能力的数据结构。下列表格列出了数据结构相对应的函数
函数 结构
auxgetDevCaps AUXCAPS
waveInGetDevCaps WAVEINCAPS
waveOutGetDevCaps WAVEOUTCAPS
标准的格式列在WAVEOUTCAPS结构成员的dwFormats中。波形音频设备可以支持一个非标准的格式。为了确定一个特殊的格式是否被这个设备支持,你可以指定WAVE_FORMAT_QUERY标志调用waveOutOpen 函数。这个标志位不会打开设备。你指定的格式问题的WAVEFORMATEX结构所指向pwfx参数传递给waveOutOpen 。