上一页 下一页 返回

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的命令消息就不作详细介绍了。