11.6 Win 32的多媒体服务
Windows 95/NT提供了丰富的多媒体服务功能,包括大量从低级到高级的多媒体API函数。利用这些功能强大的API,用户可以在不同层次上编写多媒体应用程序。有关多媒体服务的内容完全可以写一本书,本节只是向读者简要地介绍一些最常用的多媒体服务。
在用Visual C++开发多媒体应用时,用户必须在所有要用到多媒体函数的源程序中包含MMSYSTEM.H头文件,并且该文件位置应在WINDOWS.H头文件的后面。另外,在连接程序时要用到WINMM.LIB引入库,所以用户应该在Project Settings对话框的Link页的Object/library modules栏中加入WINMM.LIB,或者在源程序中加入下面一行:
#pragma comment(lib, "winmm.lib")
11.6.1 高级音频函数
Windows提供了三个特殊的播放声音的高级音频函数:MessageBeep、PlaySound和sndPlaySound。这三个函数可以满足播放波形声音的一般需要,但它们播放的WAVE文件(波形声音文件)的大小不能超过100KB,如果要播放较大的WAVE文件,则应该使用MCI服务。
MessageBeep读者已经用过了,该函数主要用来播放系统报警声音。系统报警声音是由用户在控制面板中的声音(Sounds)程序中定义的,或者在WIN.INI的[sounds]段中指定。该函数的声明为:
BOOL MessageBeep(UINT uType);
参数uType说明了告警级,如表11.4所示。若成功则函数返回TRUE。
表11.4 系统告警级
级别
描述
-1
从机器的扬声器中发出蜂鸣声。
MB_ICONASTERISK
播放由SystemAsterisk定义的声音。
MB_ICONEXCLAMATION
播放由SystemExclamation定义的声音。
MB_ICONHAND
播放由SystemHand定义的声音。
MB_ICONQUESTION
播放由SystemQuestion定义的声音。
MB_OK
播放由SystemDefault定义的声音
在开始播放后,MessageBeep函数立即返回。如果该函数不能播放指定的报警声音,它就播放SystemDefault定义的系统缺省声音,如果连系统缺省声音也播放不了,那么它就会在计算机的扬声器上发出嘟嘟声。在缺省时上表的MB_系列声音均未定义。
MessageBeep只能用来播放少数定义的声音,如果程序需要播放数字音频文件(*.WAV文件)或音频资源,就需要使用PlaySound或sndPlaySound函数。
PlaySound函数的声明为:
BOOL PlaySound(LPCSTR pszSound, HMODULE hmod,DWORD fdwSound);
参数pszSound是指定了要播放声音的字符串,该参数可以是WAVE文件的名字,或是WAV资源的名字,或是内存中声音数据的指针,或是在系统注册表WIN.INI中定义的系统事件声音。如果该参数为NULL则停止正在播放的声音。参数hmod是应用程序的实例句柄,当播放WAV资源时要用到该参数,否则它必须为NULL。参数fdwSound是标志的组合,如表11.5所示。若成功则函数返回TRUE,否则返回FALSE。
表11.5 播放标志
标志
含义
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函数才返回。
在C:\WINDOWS\MEDIA目录下有一个名为The Microsoft Sound.wav的声音文件,在Windows 95启动时会播放这个声音。下面我们用三种方法来调用PlaySound函数播出Windows 95的启动声音。
第一种方法是直接播出声音文件,相应的代码为:
PlaySound("c:\\win95\\media\\The Microsoft Sound.wav", NULL, SND_FILENAME | SND_ASYNC);
注意参数中的路径使用两个连续的反斜杠转义代表一个反斜杠。
第二种方法是把声音文件加入到资源中,然后从资源中播放声音。Visual C++支持WAVE型资源,用户在资源视图中单击鼠标右键并选择Import命令,然后在文件选择对话框中选择The Microsoft Sound.wav文件,则该文件就会被加入到WAVE资源中。假定声音资源的ID为IDR_STARTWIN,则下面的调用同样会输出启动声音:
PlaySound((LPCTSTR)IDR_STARTWIN, AfxGetInstanceHandle(), SND_RESOURCE | SND_ASYNC);
第三种方法是用PlaySound播放系统声音,Windows启动的声音是由SystemStart定义的系统声音,因此可以用下面的方法播放启动声音:
PlaySound("SystemStart",NULL,SND_ALIAS|SND_ASYNC);
函数sndPlaySound的功能与PlaySound类似,但少了一个参数。函数的声明为:
BOOL sndPlaySound(LPCSTR lpszSound, UINT fuSound);
除了不能指定资源名字外,参数lpszSound与PlaySound的是一样的。参数fuSound是如何播放声音的标志,可以是SND_ASYNC、SND_LOOP、SND_MEMORY、SND_NODEFAULT、SND_NOSTOP和SND_SYNC的组合,这些标志的含义与PlaySound的一样。
可以看出,sndPlaySound不能直接播放声音资源。要用该函数播放WAVE文件,可按下面的方式调用:
sndPlaySound(“MYSOUND.WAV”,SND_ASYNC);
11.6.2 MCI
MCI(Media Control Interface,媒体控制接口)向Windows程序提供了在高层次上控制媒体设备接口的能力。程序不必关心具体设备,就可以对激光唱机(CD)、视盘机、波形音频设备、视频播放设备和MIDI设备等媒体设备进行控制。对于程序员来说,可以把MCI理解为设备面板上的一排按键,通过选择不同的按键(发送不同的MCI命令)可以让设备完成各种功能,而不必关心设备内部实现。比如,对于play,视盘机和CD机有不同的反应(一个是播放视频,一个播放音频),而对用户来说却只需要按同一按钮。
应用程序通过向MCI发送命令来控制媒体设备。MCI命令接口分命令字符串和命令消息两种,两者具有相同的功能。命令字符串具有使用简单的特点,但是它的执行效率不如命令消息。
所有的MCI命令字符串都是通过多媒体API函数mciSendString传递给MCI的,该函数的声明为:
MCIERROR mciSendString(
LPCTSTR lpszCommand, //MCI命令字符串
LPTSTR lpszReturnString, //存放反馈信息的缓冲区
UINT cchReturn, //缓冲区的长度
HANDLE hwndCallback //回调窗口的句柄,一般为NULL
); //若成功则返回0,否则返回错误码。
该函数返回的错误码可以用mciGetErrorString函数进行分析,该函数的声明为:
BOOL mciGetErrorString(
DWORD fdwError, //函数mciSendString或mciSendCommand返回的错误码
LPTSTR lpszErrorText, //接收描述错误的字符串的缓冲区
UINT cchErrorText //缓冲区的长度
);
下面是使用mciSendString函数的一个简单例子:
char buf[50];
MCIERROR mciError;
mciError=mciSendString(“open cdaudio”,buf,strlen(buf),NULL);
if(mciError)
{
mciGetErrorString(mciError,buf,strlen(buf));
AfxMessageBox(buf);
return;
}
open cdaudio命令打开CD播放器,如果出错(如驱动器内没有CD)则返回错误码,此时可以用mciGetErrorString函数取得错误信息字符串。open是MCI打开设备的命令,cdaudio是MCI设备名。MCI的设备类型在表11.6列出。
表11.6 MCI设备类型
设备类型
描述
animation
动画设备
cdaudio
CD播放器
dat
数字音频磁带机
digitalvideo
某一窗口中的数字视频(不基于GDI)
other
未定义的MCI设备
overlay
重叠设备(窗口中的模拟视频)
scanner
图象扫描仪
sequencer
MIDI序列器
videodisc
视盘机
waveaudio
播放数字波形文件的音频设备
请读者注意,设备类型和设备名是不同的概念。设备类型是指响应一组共用命令的一类MCI设备,而设备名则是某一个MCI设备的名字。系统需要用不同的设备名来区分属于同一设备类型的不同设备。
设备名是在注册表或SYSTEM.INI的[mci]部分定义的,典型的[mci]段如下所示:
[mci]
cdaudio=mcicda.drv
sequencer=mciseq.drv
waveaudio=mciwave.drv
avivideo=mciavi.drv
videodisc=mcipionr.drv
等号的左边是设备名,右边是对应的MCI驱动程序。当安装了新的MCI驱动程序时,系统要用不同的设备名来区分。设备名通常与驱动程序中的设备类型名相同,如cdaudio和waveaudio等,但也有例外,如avivideo设备是一个digitalvideo类型的设备。
使用MCI设备一般包括打开、使用和关闭三个过程。MCI的大部分命令可以控制不同的媒体设备。例如,可以用play命令来播放WAVE文件、视频文件或CD。表11.7列出常用的MCI命令字符串,表中大部分命令都具有通用性。在MCI命令的后面一般要跟一个设备名以指定操作的对象。
表11.7 常用的MCI命令
命令
描述
capacility
查询设备能力
close
关闭设备
info
查询设备的信息
open
打开设备
pause
暂停设备的播放或记录
play
开始设备播放
record
开始
resume
恢复暂停播放或记录的设备
seek
改变媒体的当前位置
set
改变设置
status
查询设备状态信息
stop
停止设备的播放或记录
例如,上面的例子打开了一个CD播放机后,可以发送常用的命令来控制CD机:
play cdaudio from <位置> to <位置>。若省略from则从当前磁道开始播放,若省略to则播放到结束。
pause cdaudio。暂停播放。
stop cdaudio。停止播放。
resume cdaudio。继续被暂停的播放。
status cdaudio number of tracks。查询CD的磁道数。status cdaudio current track可以查询当前磁道。
seek cdaudio to <位置>。移动到指定磁道。
set cdaudio door open/closed。弹出或缩进CD盘。
close cdaudio。关闭设备。
MCI设备可以按简单设备和复合设备进行分类。象cdaudio这样的设备不使用文件,我们称之为简单设备,而复合设备在播放时要用到数据文件,如数字视频(digitalvideo)和波形音频(waveaudio)设备,我们把这些数据文件叫做设备元素。
在打开一个复合设备时要指定设备名和设备元素。例如,下面命令打开一个波形音频设备:
open mysound.wav type waveaudio
可以只为复合设备指定设备元素,例如:
open mysound.wav
如下面所示,系统通过查找注册表或WIN.INI的[mci extensions]可以确定打开哪一个设备。
[mci extensions]
mid=Sequencer
rmi=Sequencer
wav=waveaudio
avi=AVIVideo
有时,程序需要多次打开同一设备来播放不同的数据文件。例如,谁也不能否认在屏幕上同时播放两个AVI文件的可能性,在这种情况下,需要为每次打开的设备起一个不同的别名,这样MCI才能区分两个播放设备。例如,下面这段代码打开并播放了两个AVI文件:
char buf[50];
mciSendString("open dillo.avi type avivideo alias dillo",buf,strlen(buf),NULL);
mciSendString("play dillo repeat",buf,strlen(buf),NULL); //重复播放
mciSendString("open search.avi type avivideo alias search",buf,strlen(buf),NULL);
mciSendString("play search",buf,strlen(buf),NULL);
在用open命令打开设备时,如果指定了别名,则以后对该设备的操作都要使用别名。
到目前为止,我们使用的都是MCI命令字符串。读者可能己经有了这样的体会,命令字符串具有简单易学的优点,但这种接口与C/C++的风格相去甚远,如果程序要查询和设置大量数据,那么用字符串的形式将很不方便。
MCI的命令消息接口提供了C语言接口,它速度更快,并且更能符合C/C++程序员的需要。所有MCI命令消息都是通过mciSendCommand函数发送的,该函数的声明为:
MCIERROR mciSendCommand(
MCIDEVICEID IDDevice, //设备的ID,在打开设备时不用该参数
UINT uMsg, //命令消息
DWORD fdwCommand, //命令消息的标志
DWORD dwParam //指向包含命令消息参数的结构
); //若成功则返回0,否则返回错误码
清单11.8的代码演示了用MCI命令消息来打开和重复播放一个AVI文件:
清单11.8
MCI_DGV_OPEN_PARMS mciOpen;
UINT wDeviceID;
MCIERROR mciError;
mciOpen.lpstrDeviceType = "avivideo"; //设备名
mciOpen.lpstrElementName = "dillo.avi"; //设备元素
mciError=mciSendCommand(0, MCI_OPEN,
MCI_OPEN_TYPE|MCI_OPEN_ELEMENT, //使用了设备元素
(DWORD)&mciOpen);
if(mciError)
{
char s[80];
mciGetErrorString(mciError,s,80);
AfxMessageBox(s);
return ;
}
wDeviceID=mciOpen.wDeviceID; //保存设备ID
MCI_DGV_PLAY_PARMS mciPlay;
mciError=mciSendCommand(wDeviceID, MCI_PLAY, MCI_DGV_PLAY_REPEAT,
(DWORD)&mciPlay);
. . .
可以看出,用命令消息比用命令字符串要复杂的多。命令消息与命令字符串是对应的,例如,open与MCI_OPEN完成的是一样的功能。变量wDeviceID用来保存设备的ID,系统用ID来标识不同的设备,以保证命令发给正确的对象。
限于篇幅,对MCI的命令消息就不作详细介绍了。