VC下音频文件的播放
杨晨
引子
首先说明一点,我学得不好,感觉没能较好的完成党交给的重任,呵呵。
我这几天一直没有看关于播放声音的东东。因为一方面感觉似乎单从我们要完成的五子,炸弹程序来看,我们现在已有的知识应该已经够用了。另一方面,在VC下播放音频是一项很大很杂的东西,我自己不想在这上面花费过多的时间。再有就是我这个文章(应该叫随笔,我想到哪写到哪,没有条理见谅)早写一天晚写一天对大家没有影响,毕竟这个东东相对于我们的程序是独立的。
恩,由于我看的东西也挺杂的,手边也没有比较权威的资料,看了一些msdn上的东东,限于英语水平有限,只是自己理解了一些,还不知对不对,所以很多东西我不能乱言,因为这样可能会对你有所误导。下面说的大多参照网上或有些书上一些强人的观点,当然也有很多我自己的语言,g你要看着些地方不爽可以不屑于顾。
OK,开始了。
Visual C++对多媒体的支持
对于一般的应用程序来说,Visual C++ 可以说是包罗万象,然而令人遗憾的是,几乎没有听说过Visual C++ 对多媒体提供过什么支持,甚至有人说Visual C++不适合多媒体编程。若是我们完全使用Visual C++的类库而不想点花招的话,恐怕连最一个简单的RPG游戏都编不出来。对于一个需要大量动画、声音的多媒体应用程序来说,Visual C++ 最多提供了一个外壳,而编制一个优秀的声音、动画引擎的任务,就落到了程序员的身上。
上面的话不是我说的,我接着说一句:那么,如何开发这个引擎,这个重任有可能以后就落的你的头上了。需要你针对你的应用程序去组织类.
大的不太好,小的应用程序应该完全凑合。
Windows的多媒体服务功能
Windows 提供了丰富的多媒体服务功能,包括大量从低级到高级的多媒体API函数。利用这些功能强大的API,用户可以在不同层次上编写多媒体应用程序.这里简要地介绍一些最常用的多媒体服务。
1,高级音频函数.
MessageBeep、PlaySound和sndPlaySound。这三个函数你应该熟悉,我不想在这说太多。因为我感觉很简单的,你查一下msdn就会了。要注意一下他们需要什么样的头文件,参数都是做什么用,还有限于什么样的文件格式。比如它们都不能播放大于100k的波形声音文件.
写个例子你应该清晰一些:
PlaySound("关机.wav", NULL, SND_ASYNC|SND_LOOP);
//第一个参数是声音文件名,最后的那个SND_LOOP是循环播放的意思。
我感觉要实现同时播放两个音乐好似只用这几个函数是不可能实现的。不管你用不用线程控制,用了一次那么它就把那个设备的接口给占住了,用第二次要么把那个接口给抢走了第一次的不能用了,要么抢不走,苦苦等你不想用了再拾过来。
2.MCI
什么是MCI?
MCI(Media Control Interface)媒体控制接口是MircroSoft提供的一组多媒体设备和文件的标准接口,它的好处是可以方便地控制绝大多数多媒体设备包括音频、视频、影碟、录像等多媒体设备,而不需要知道它们的内部工作状况。但是古人云:成也萧何,败也萧何。MCI虽然看上去高大全,但对于一些高级应用来说,它是远远不够的。
上面的话也不是我说的,但低级中级应用是完全凑合的。
MCI向Windows程序提供了在高层次上控制媒体设备接口的能力。程序不必关心具体设备,就可以对激光唱机(CD)、视盘机、波形音频设备、视频播放设备和MIDI设备等媒体设备进行控制。对于程序员来说,可以把MCI理解为设备面板上的一排按键,通过选择不同的按键(发送不同的MCI命令)可以让设备完成各种功能,而不必关心设备内部实现。比如,对于play,视盘机和CD机有不同的反应(一个是播放视频,一个播放音频),而对用户来说却只需要按同一按钮。
MCI的使用方法
应用程序通过向MCI发送命令来控制媒体设备。MCI命令接口分命令字符串和命令消息两种,两者具有相同的功能。命令字符串具有使用简单的特点,但是它的执行效率不如命令消息。(mciSendString,字符串消息。 mciSendCommand,命令消息)
1.先说mciSendString.
表面上就是发送字符串的意思,就是要告诉某些设备,请你播放!,注意下面参数的第一个都是用双引号弄起来的,只是引号里写的有固定的格式罢了。
先写个简单的例子,使你不至于一头雾水。
mciSendString("open 老鼠爱大米.avi type 设备1 ",…);
mciSendString("play 设备1 repeat",…);
上面这个例子是我胡乱写的,你应该一下子有些感觉到头上是蓝天了吧,大约看得懂什么意思吧,那repeat什么意思啊知道吧。就是说你特别喜欢那首歌,听完一次还要听。
还是说一下吧吧。第一句话先发送个命令,把那个老鼠爱大米放进设备里,然后第二句再发送个命令设备开始给我运转!
所有的MCI命令字符串都是通过多媒体API函数mciSendString传递给MCI的,该函数的声明为:
MCIERROR mciSendString( //MCIERROR 只是一个返回类型,你别害怕啊。
//命令字符(刚才……前面的:"open 老鼠爱大米.avi type 设备1 ")
LPCTSTR lpszCommand,
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;
}
mciSendString("open 老鼠爱大米.avi type 设备1 ",…);
mciSendString("play 设备1 repeat",…);
这样你就可以听老鼠爱大米了,但是如果我还想听 [大米爱老鼠]这首歌,我就想两个歌一起播放, 那我就在前面加一句mciSendString("open大米爱老鼠.avi type 设备1 ",…);可以不?
这是不可以的。
在这种情况下,需要为每次打开的设备起一个不同的别名,这样MCI才能区分两个播放设备。
例如,下面这段代码打开并播放了两个AVI文件:(Alias是别名的意思。)
char buf[50];
mciSendString("open老鼠爱大米.avi type avivideo alias adu",buf,strlen
(buf),NULL);
mciSendString("play adu repeat",buf,strlen(buf),NULL); //重复播放
mciSendString("open大米爱老鼠.avi type avivideo alias guanghao",buf,strlen
(buf),NULL);
mciSendString("play guanghao ",buf,strlen(buf),NULL);
把上面这些写成红色因为这样就实现了背景音乐和普通音乐的同时播放。
2. mciSendCommand
关于这个我就不用多说什么了。写个例子:
下面基本上都是固定的格式,比如要打开并播放一个.wav文件,只要把下面蓝色的两部分换为
waveaudio 和 Windows XP 关机.wav 可以了.
MCI_DGV_OPEN_PARMS mciOpen;
UINT wDeviceID;
MCIERROR mciError;
mciOpen.lpstrDeviceType = "avivideo"; //设备名
mciOpen.lpstrElementName = "老鼠.avi"; //设备元素
mciError=mciSendCommand(0, MCI_OPEN,
MCI_OPEN_TYPE|MCI_OPEN_ELEMENT, //使用了设备元素
(DWORD)&mciOpen);
wDeviceID=mciOpen.wDeviceID; //保存设备ID
MCI_DGV_PLAY_PARMS mciPlay;
mciError=mciSendCommand(wDeviceID, MCI_PLAY, MCI_DGV_PLAY_REPEAT,
(DWORD)&mciPlay);
写这些程序时要包含许多的头文件。我也没有归纳过什么。比如
#include <Mmsystem.h>;
#include <Digitalv.h>
#include <vfw.h>
#pragma comment(lib,"vfw32.lib")
#pragma comment(lib,"Winmm.lib")
我用的时候就是让程序不报错,最先全注释掉,然后露一行,如果报错了,看看用到了哪个函数,然后查MSDN再把需要它的头文件放进去。
还有,就是感觉很多网上强人播放声音文件都用了下面的方法,MCIWndCreate和MCIWndPlay.当然也都是用MCI类的。这个可真是方便又简单啊。呵呵。
如:
HWND m_hwndMCI;
if(m_hwndMCI!=NULL) //建立一个MCIWND窗口
{
MCIWndDestroy(m_hwndMCI);
}
int type=2; //隐藏默认工具栏 0表显示
CString filename="星空无限.mp3"; //要播放的文件文件名
m_hwndMCI=MCIWndCreate(m_hWnd,AfxGetInstanceHandle(),type,filename);
MCIWndPlay(m_hwndMCI);
在stdafx.h中加入:
#include<vfw.h>
#pragma comment(lib,"vfw32.lib")
更多控制函数请自己打开vfw.h文件查看
如:
MCIWndGetPosition(m_hwndMCI) 得到当前播放位置,用于控制播放进度滑块
MCIWndGetLength(m_hwndMCI) 文件播放长度
MCIWndSetVolume(m_hwndMCI,iVol) 设置音量,大小为iVol,最大为1000
MCIWndGetVolume(m_hwndMCI) 得到当前音量值
MCIWndPlayFromTo(m_hwndMCI, lStart, lEnd) 播放片段
就先说这些了。一起努力,一起进步。
-------------------------------------
sangmin
7/27/2005于赣榆