声音是多媒体的一个重要组成部分,在应用程序中加入声音可以使界面更友好。在VC++中可以根据不同的应用要求,用不同的方法实现声音的播放。
在VC++中的多媒体动态连接库中提供了一组与音频设备有关的函数。利用这些函数可以方便地播放声音。最简单的播放声音方法就是直接调用VC++中提供的声音播放函数
BOOL sndPlaySound ( LPCSTR lpszSound, UINT fuSound ); 或
BOOL PlaySound ( LPCSTR lpszSound, HMODULE hmod, DWORD fuSound );
其中参数lpszSound是需要播放声音的.WAV文件的路径和文件名,hmod在这里为NULL,fuSound是播放声音的标志,详细说明请参考VC++中的帮助。
例如播放C:/sound/music.wav可以用
sndPlaySound( "c://sound//music.wav", SND_ASYNC ); 或
PlaySound("c://sound//music.wav ", NULL, SND_ASYNC | SND_NODEFAULT );
如果没有找到music.wav文件,第一种格式将播放系统默认的声音,第二种格式不会播放系统默认的声音。
在VC++的程序设计中,可以利用各种标准的资源,如位图,菜单,对话框等。同时VC++也允许用户自定义资源,因此我们可以将声音文件作为用户自定义资源加入程序资源文件中,经过编译连接生成EXE文件,实现无.WAV文件的声音播放。 要实现作为资源的声音文件的播放,首先要在资源管理器中加入待播放的声音文件(实现过程并不复杂,这里不在叙述)。
假设生成的声音文件资源标识符为IDR_WAVE1。在播放时只需要调用下面的语句:
PlaySound(MAKEINTRESOURCE(IDR_WAVE1),AfxGetResourceHandle(),SND_ASYNC|SND_RESOURCE|SND_NODEFAULT|SND_LOOP);
其中MAKEINTRESOURCE()宏将整数资源标识符转变为字符串,AfxGetResourceHandle()函数返回包含资源的模块句柄,SND_RESOURCE是必须的标志。
作为资源的声音文件的第二种播放方法是把资源读入内存后作为内存数据播放。
具体步骤入下:
1. 获得包含资源的模块句柄
HMODULE hmod = AfxGetResourceHandle();
2. 检索资源块信息
HRSRC hSndResource = FindResource(hmod, MAKEINTRESOURCE(IDR_WAVE1), _T("WAVE"));
3. 装载资源数据并加锁
HGLOBAL hGlobalMem = LoadResource(hmod,hSndResource);
LPCTSTR lpMemSound = (LPCSTR)LockResource(hGlobalMem);
4. 播放声音文件
sndPlaySound(lpMemSound,SND_MEMORY));
5. 释放资源句柄 FreeResource(hGlobalMem);
在VC++中提供了一组对音频设备及多媒体文件直接进行操作的函数。利用这些函数可以灵活地对声音文件进行各种处理。
首先介绍几个要用到的数据结构。WAVEFORMATEX结构定义了WAVE音频数据文件的格式。WAVEHDR结构定义了波形音频缓冲区。读出的数据首先要填充此缓冲区才能送音频设备播放。WAVEOUTCAPS结构描述了音频设备的性能。MMCKINFO结构包含了RIFF文件中一个块的信息。详细的说明请参考VC++中的帮助。
下面给出程序流程简图及程序源代码清单,在VC++环境下可直接使用。
源程序清单如下:
LPSTR szFileName;//声音文件名
MMCKINFO mmckinfoParent;
MMCKINFO mmckinfoSubChunk;
DWORD dwFmtSize;
HMMIO m_hmmio;//音频文件句柄
DWORD m_WaveLong;
HPSTR lpData;//音频数据
HANDLE m_hData;
HANDLE m_hFormat;
WAVEFORMATEX * lpFormat;
DWORD m_dwDataOffset;
DWORD m_dwDataSize;
WAVEHDR pWaveOutHdr;
WAVEOUTCAPS pwoc;
HWAVEOUT hWaveOut;
/*
打开波形文件
*/
if(!(m_hmmio=mmioOpen(szFileName,NULL,MMIO_READ|MMIO_ALLOCBUF)))
{ //File open Error Error( "Failed to open the file. ");
return false;
}
/*
检查打开文件是否是声音文件
*/
mmckinfoParent.fccType = mmioFOURCC( 'W ', 'A ', 'V ', 'E ' );
if(mmioDescend(m_hmmio, (LPMMCKINFO)&mmckinfoParent, NULL, MMIO_FINDRIFF))
{ //NOT WAVE FILE AND QUIT
return FALSE;
}
/*
寻找'fmt '块
*/
mmckinfoSubChunk.ckid = mmioFOURCC( 'f ', 'm ', 't ', '' );
if(mmioDescend(m_hmmio, &mmckinfoSubChunk, &mmckinfoParent, MMIO_FINDCHUNK))
{ //Can 't find 'fmt ' chunk
return FALSE;
}
/*
获得'fmt '块的大小,申请内存
*/
dwFmtSize = mmckinfoSubChunk.cksize;
m_hFormat = LocalAlloc(LMEM_MOVEABLE, LOWORD(dwFmtSize));
if(!m_hFormat)
{ //failed alloc memory
return FALSE;
}
lpFormat=(WAVEFORMATEX*)LocalLock(m_hFormat);
if(!lpFormat)
{
//failed to lock the memory
return FALSE;
}
if((unsigned long)mmioRead(m_hmmio, (HPSTR)lpFormat, dwFmtSize)!=dwFmtSize)
{ //failed to read format chunk
return FALSE;
}
/*
离开fmt块
*/
mmioAscend(m_hmmio, &mmckinfoSubChunk, 0);
/*
寻找'data '块
*/
mmckinfoSubChunk.ckid = mmioFOURCC( 'd ', 'a ', 't ', 'a' );
if(mmioDescend(m_hmmio, &mmckinfoSubChunk, &mmckinfoParent, MMIO_FINDCHUNK))
{ //Can 't find 'data ' chunk
return FALSE;
}
/*
获得'data '块的大小
*/
m_dwDataSize = mmckinfoSubChunk.cksize ; m_dwDataOffset = mmckinfoSubChunk.dwDataOffset ;
if(m_dwDataSize==0L)
{ //no data in the 'data ' chunk
return FALSE;
}
/*
为音频数据分配内存
*/
lpData = new char[m_dwDataSize];
if(!lpData)
{
return FALSE;
}
if(mmioSeek(m_hmmio, SoundOffset, SEEK_SET) <0)
{ //Failed to read the data chunk
return FALSE;
}
m_WaveLong=mmioRead(m_hmmio, lpData, SoundLong);
if(m_WaveLong <0)
{ //Failed to read the data chunk
return FALSE;
}
/*
检查音频设备, 返回音频输出设备的性能
*/
if(waveOutGetDeVCaps(WAVE_MAPPER, &pwoc, sizeof(WAVEOUTCAPS))!=0)
{ //Unable to allocate or lock memory
return FALSE;
}
/*
检查音频输出设备是否能播放指定的音频文件
*/
if(waveOutOpen(&hWaveOut, DevsNum, lpFormat, NULL, NULL, CALLBACK_NULL)!=0)
{ //Failed to OPEN the wave out devices
return FALSE;
}
/*
准备待播放的数据
*/
pWaveOutHdr.lpData = (HPSTR)lpData;
pWaveOutHdr.dwBufferLength = m_WaveLong;
pWaveOutHdr.dwFlags = 0;
if(waveOutPrepareHeader(hWaveOut, &pWaveOutHdr, sizeof(WAVEHDR))!=0)
{ //Failed to prepare the wave data buffer
return FALSE;
}
/*
播放音频数据文件
*/
if(waveOutWrite(hWaveOut, &pWaveOutHdr, sizeof(WAVEHDR))!=0 )
{ // Failed to write the wave data buffer
return FALSE;
}
/*
关闭音频输出设备,释放内存
*/
waveOutReset(hWaveOut);
waveOutClose(hWaveOut);
LocalUnlock(m_hFormat);
LocalFree(m_hFormat);
delete []lpData;
说明:
1. 以上使用的音频设备和声音文件操作函数的声明包含在mmsystem.h头文件中,因此在程序中必须用#include "mmsystem.h "语句加入头文件。同时在编译时要加入动态连接导入库winmm.lib,具体实现方法是从Developer Studio的Project菜单中选择Settings,然后在Link选项卡上的Object/Library Modules控制中加入winmm.lib。
2. 在pWaveOutHdr.lpData中指定不同的数据,可以播放音频数据文件中任意指定位置的声音。
3. 以上程序均在VC++6.0中调试通过,在文中省略了对错误及异常情况的处理,在实际应用中必须加入。
在VC++中可以根据应用需要采用不同的方法播放声音文件。简单应用可以使用第一种方法直接调用声音播放函数。第二种方法可以把声音作为资源加入可执行文件中。如果在播放之前要对声音数据进行处理,可用第三种方法。