最近在写游戏菜单,想做一个可以手动设置游戏场景音乐,音效,及音量的菜单,看许多书上用的都是DXMusic,DirectSound,可自从DirectX 9.0c之后,DXMusic和DirectSound就不再存在了,反而被XAudio给取代了。在网上查找资料,书上查找相关信息,也是甚至更少,迫不得已只能去官网和官方给的样例慢慢琢磨。
XAudio2官方介绍:
https://msdn.microsoft.com/zh-cn/library/windows/desktop/ee415737
在XAudio2系统中共有三种声音,分别是:source voice(源声音),master voice(主声音),
sbumix(混合音)。
Source voice(源声音):也就是从磁盘文件中所采集到的音频数据——最初始的状态(在这个系统中)
Submix voice(混合声音):也就是将多个源声音进行混合——类似于游戏场景中背景音乐和音效的混合
Master voice(主声音):这是重中之重,将音频数据最后写到咱们的音频输出设备上——播放声音
想要播放声音大概分为以下几步:
1、初始化XAudio2。
2、进行音频文件的查找。
3、将波形文件的数据读入到内存中
4、创建源声音
5、将缓冲区内的音频提交给源声音
6、最终播放声音
1、先使用XAudio2Create函数创建一个XAudio2的对象
函数原型:
HRESULT XAudio2Create(
IXAudio2 **ppXAudio2, //需要实例化的对象
//指该实例化对象的行为,一般设置为0或者XAUDIO2_DEBUG_ENGINE宏
UINT32 Flags = 0,
//让CPU分配一个线程给该音频系统,不做特别要求可设置为XAUDIO2_DEFAULT_PROCESSOR
XAUDIO2_PROCESSOR XAudio2Processor = XAUDIO2_DEFAULT_PROCESSOR
)
IXAudio2 *pXAudio2 = NULL;
HRESULT hr;
//创建示列
if (FAILED(hr=XAudio2Create(&pXAudio2,0,XAUDIO2_DEFAULT_PROCESSOR)))
{
::MessageBox(NULL, L"XAudio2 Create Failed", L"Error", NULL);
return hr;
}
2、创建主声音,很关键,能不能播放声音就看它是否存在。
利用CreateMasteringVoice函数来创建主声音
函数原型:
HRESULT CreateMasteringVoice(
IXAudio2MasteringVoice **ppMasteringVoice, //实例化对象
UINT32 InputChannels = XAUDIO2_DEFAULT_CHANNELS,// //输入音频中声音期望的声道
UINT32 InputSampleRate = XAUDIO2_DEFAULT_SAMPLERATE,//音频的样本率
UINT32 Flags = 0, //指定声音行为,默认为0
UINT32 DeviceIndex = 0, //音频设备索引,默认音频设备=0
const XAUDIO2_EFFECT_CHAIN *pEffectChain = NULL //音频的效果链
)
IXAudio2MasteringVoice *pMasterVoice = NULL;
//后面的参数由于声明的时候已经初始化,不做特别要求,可以不用填,默认
if (FAILED(hr=pXAudio2->CreateMasteringVoice(&pMasterVoice)))
{
::MessageBox(NULL, L"The Mastering voice is created failed", L"Error", NULL);
return false;
}
看官方例子:
定义一个辅助查找文件函数Find MediaFile(WCHAR* strDestPath, int cchDest,LPCWSTR strFilename))
各个参数分别表示文件路径,最大的路径存放容量,音频文件名
这时就需要官方给的DX Wave型类了。
类的头文件定义如下:
class WaveFile
{
public:
WAVEFORMATEX* m_pwfx; //pointer to WAVEFORMATEX structure 波形文件格式的结构指针
HMMIO m_hmmio; //a I/O handle for the WAVE 波形文件操作的句柄
MMCKINFO m_ck; //Multimedia RIFF chunk 多媒体资源文件交换格式块
MMCKINFO m_ckRiff; //use in opening a wave file 用于使用打开一个波形文件
DWORD m_dwSize; //size of the wave file 波形文件的大小,用于存放在内存中
MMIOINFO m_mmioinfoOut; //
DWORD m_dwFlags;
BOOL m_bIsReadingFromMemory;
BYTE* m_pdData;
BYTE* m_pdDataCur;
ULONG m_ulDataSize;
CHAR* m_pResourceBuffer;
protected:
HRESULT ReadMMIO();
HRESULT WriteMMIO(WAVEFORMATEX* pwfxDest);
public:
WaveFile();
~WaveFile();
HRESULT Open(LPWSTR strFileName, WAVEFORMATEX *pwfx, DWORD dwFlags);
HRESULT OpenFromMemory(BYTE* pbData, ULONG ulDataSize, WAVEFORMATEX *pwfx, DWORD dwFlags);
HRESULT Close();
HRESULT Read(BYTE *pBuffer, DWORD dwSizeToRead, DWORD *pdwSizeRead);
HRESULT Write(UINT nSizeToWrite, BYTE *pbData, UINT* pnSizeWrote);
DWORD GetSize();
HRESULT ResetFile();
WAVEFORMATEX *GetFormat()
{
return m_pwfx;
}
};
如何进行读操作?
先将文件进行打开,分析信息,上一步是查找文件是否存在
WaveFile wav;
if (FAILED(hr = wav.Open(L"XXX.wav", NULL, WAVEFILE_READ)))
{
return hr;
}
再获取波形文件的相关信息:
//获取格式
WAVEFORMATEX *pwfx = wav.GetFormat();
//计算这个文件共占多少字节
DWORD cbWaveSize = wav.GetSize();
//用于存放文件的数据
BYTE *pbWaveData = new BYTE[cbWaveSize];
最后再进行读文件操作:
if (FAILED(hr = wav.Read(pbWaveData, cbWaveSize, &cbWaveSize)))
{
SAFE_DELETE_ARRAY(pbWaveData);
return hr;
}
通过利用CreateSourceVoice()函数来进行创建
函数原型:
HRESULT CreateSourceVoice(
IXAudio2SourceVoice **ppSourceVoice, //实例对象
const WAVEFORMATEX *pSourceFormat, //音频文件格式8,16,20,32位
UINT32 Flags = 0, //源声音的行为
//音频的最大频率允许
float MaxFrequencyRatio = XAUDIO2_DEFAULT_FREQ_RATIO,
//向客户提供的回调指针
IXAudio2VoiceCallback *pCallback = NULL,
//描述目的地声音为源声音的指针
const XAUDIO2_VOICE_SENDS *pSendList = NULL,
const XAUDIO2_EFFECT_CHAIN *pEffectChain = NULL //效果链
)
一般这样创建:
IXAudio2SourceVoice *pSourceVoice;
if (FAILED(hr = pXAudio2->CreateSourceVoice(&pSourceVoice, pwfx)))
{
::MessageBox(NULL, L"The Source Voice is Created failed", L"Error", MB_OK);
return FALSE;
}
//首先定义音频缓冲区
XAUDIO2_BUFFER buffer = { 0 };
//设置音频缓冲区的内容,至少要设置这个三个,不然就播放不了声音,有一次就是忽略到这点导致一直播放不了
buffer.pAudioData = pbWaveData;
buffer.Flags = XAUDIO2_END_OF_STREAM; //不要这个缓冲区之后的任何数据
buffer.AudioBytes = cbWaveSize;
完整的XAUDIO2_BUFFER:
typedef struct XAUDIO2_BUFFER
{
UINT32 Flags; // 行为方式,要还说不要缓冲区后面的数据
UINT32 AudioBytes; // 音频数据的大小
const BYTE* pAudioData; // 音频数据指针
UINT32 PlayBegin; // 缓冲区应该播放第一个对象
UINT32 PlayLength; // 播放区域的长度,为0就默认为整个缓冲区
UINT32 LoopBegin; // 循环播放该音频
UINT32 LoopLength; // 循环区域的长度,为0就默认整个缓冲区
UINT32 LoopCount; // 循环播放的次数,如果为0,那么上面连个参数LoopBegin和LoopLength也要为0,这个设定才成功
void* pContext; //传回客户端的值
} XAUDIO2_BUFFER;
接下来利用SubmitSourceBuffer()函数来提交缓冲区的内容
函数原型:
HRESULT SubmitSourceBuffer(
const XAUDIO2_BUFFER *pBuffer, //该指针指向一个XAUDIO2_BUFFER的队列
const XAUDIO2_BUFFER_WMA *pBufferWMA = NULL //提交额外缓冲的指针
)
用法:
if (FAILED(hr = pSourceVoice->SubmitSourceBuffer(&buffer)))
{
::MessageBox(NULL, L"Submit an XAudio2_Buffer Failed", L"Error", MB_OK);
return FALSE;
}
利用源声音的Start()函数,将声音传Master voice(主声音),主声音将音频数据传给我们的音频设备,方能播放声音了。
函数原型:
HRESULT Start(
UINT32 Flags, //控制声音是否开始,0代表开始
UINT32 OperationSet = XAUDIO2_COMMIT_NOW //官方称为part of a deferred batch(延期批的部分)
)
if (FAILED(hr = pSourceVoice->Start(0)))
{
return FALSE;
}
最终就可以听见声音了。
源码下载:
http://download.csdn.net/download/qq_33248019/10030436