XAudio2学习之循环播放音频数据

有时候一个音频特效需要多次播放,比如一个连击动作的声效。

1.当然你可以多次打开文件,读取内容然后播放;

2.也可以打开文件,读取一次到内存,创建多个XAUDIO2_BUFFER,提交多次进行播放;

3.最好的方法是使用XAUDIO2_BUFFER的Loop功能来进行播放,只需要读取一次文件,创建一个XAUDIO2_BUFFER,提交一次,就可以实现播放多次的需求。

对于多次打开文件这种方法,一般是不会采用的,读写操作,解析文件,申请内存等都耗时间,可能会导致声效不及时,出现错觉。

对于第二种方法,可以采用,但是要多次创建XAUDIO2_BUFFER,多次提交,多次等待,也有不少缺点,但是也算可行。需要注意的是,回调实现的方法是void OnBufferEnd(void * pBufferContext)    { SetEvent(hBufferEndEvent); }

下面是实现方法:

#pragma once
#include "WaveFile.h"
#include "XAudio2.h"

class VoiceCallback : public IXAudio2VoiceCallback
{
public:
	HANDLE hBufferEndEvent;
	VoiceCallback() : hBufferEndEvent(CreateEvent(NULL, FALSE, FALSE, NULL)){}
	~VoiceCallback(){ CloseHandle(hBufferEndEvent); }

	//Called when the voice has just finished playing a contiguous audio stream.
	void OnStreamEnd() { /*SetEvent(hBufferEndEvent);*/ }

	//Unused methods are stubs
	void OnVoiceProcessingPassEnd() { }
	void OnVoiceProcessingPassStart(UINT32 SamplesRequired) {    }
	void OnBufferEnd(void * pBufferContext)    { SetEvent(hBufferEndEvent); }
	void OnBufferStart(void * pBufferContext) {    }
	void OnLoopEnd(void * pBufferContext) { /*SetEvent(hBufferEndEvent);*/ }
	void OnVoiceError(void * pBufferContext, HRESULT Error) { }
};

 int main(int argc, char *argv[])
 {
	 HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);;//com初始化
 	if (FAILED(hr))
 		return 0;
 
 	IXAudio2 *pEngine = NULL;
 	hr = XAudio2Create(&pEngine);//创建引擎
 	if (FAILED(hr))
 		return 0;
 
 	IXAudio2MasteringVoice *pMasterVoice = NULL;
 	hr = pEngine->CreateMasteringVoice(&pMasterVoice);//创建主声音,默认是输出当前扬声器
 	if (FAILED(hr))
 		return 0;
 
 	CWaveFile waveFile;
 	hr = waveFile.Open(L"F:\\桌面\\TestSong.wav", NULL, WAVEFILE_READ);//加载文件
 	if (FAILED(hr))
 		return 0;
	
 	WAVEFORMATEX *waveFormat = waveFile.GetFormat();//获取文件格式

	VoiceCallback pCallBack;
 	IXAudio2SourceVoice *pSourceVoice = NULL;
	hr = pEngine->CreateSourceVoice(&pSourceVoice, waveFormat, 0, 1.0f, &pCallBack);//创建源声音,用来提交数据
 	if (FAILED(hr))
 		return 0;
	
 	DWORD size = waveFile.GetSize();//获取文件的大小
 	BYTE *pData = new BYTE[size];//申请内存空间,用于保存数据
 	hr = waveFile.Read(pData, size, &size);//读取文件内容
 	if (FAILED(hr))
 		return 0;
 
	XAUDIO2_BUFFER buffer = {0};//将读取的文件数据,赋值XAUDIO2_BUFFER
 	buffer.AudioBytes = size;
 	buffer.pAudioData = pData; 
 	hr = pSourceVoice->SubmitSourceBuffer(&buffer);//提交内存数据
 	if (FAILED(hr))
 		return 0;
 
	XAUDIO2_BUFFER buffer1 = { 0 };//将读取的文件数据,赋值XAUDIO2_BUFFER
	buffer1.AudioBytes = size;
	buffer1.pAudioData = pData;
	hr = pSourceVoice->SubmitSourceBuffer(&buffer1);//提交内存数据
	if (FAILED(hr))
		return 0;

	XAUDIO2_BUFFER buffer2 = { 0 };//将读取的文件数据,赋值XAUDIO2_BUFFER
	buffer2.AudioBytes = size;
	buffer2.pAudioData = pData;
	hr = pSourceVoice->SubmitSourceBuffer(&buffer2);//提交内存数据
	if (FAILED(hr))
		return 0;


 	hr = pSourceVoice->Start(0);//启动源声音
 	if (FAILED(hr))
 		return 0;
 
	XAUDIO2_VOICE_STATE state;
	pSourceVoice->GetState(&state);//获取状态
	while (state.BuffersQueued > /*XAUDIO2_MAX_QUEUED_BUFFERS - 1*/0)
	{
		WaitForSingleObject(pCallBack.hBufferEndEvent, INFINITE);
		pSourceVoice->GetState(&state);
	}
 
 	pMasterVoice->DestroyVoice();//释放资源
 	pSourceVoice->DestroyVoice();//释放资源
 	pEngine->Release();//释放资源
 	CoUninitialize();//释放资源

	delete []pData;//释放资源
	pData = NULL;

 	return 0;
 }
源码下载:http://download.csdn.net/detail/u011417605/9481804

对于第三种方法,这是最好的方式了,使用XAUDIO2_BUFFER的Loop功能来进行播放,只需要读取一次文件,创建一个XAUDIO2_BUFFER,提交一次,就可以实现播放多次的需求。

// Used in IXAudio2SourceVoice::SubmitSourceBuffer
typedef struct XAUDIO2_BUFFER
{
    UINT32 Flags;                       // Either 0 or XAUDIO2_END_OF_STREAM.
    UINT32 AudioBytes;                  // Size of the audio data buffer in bytes.
    const BYTE* pAudioData;             // Pointer to the audio data buffer.
    UINT32 PlayBegin;                   // First sample in this buffer to be played.
    UINT32 PlayLength;                  // Length of the region to be played in samples,
                                        //  or 0 to play the whole buffer.
    UINT32 LoopBegin;                   // First sample of the region to be looped.
    UINT32 LoopLength;                  // Length of the desired loop region in samples,
                                        //  or 0 to loop the entire buffer.
    UINT32 LoopCount;                   // Number of times to repeat the loop region,
                                        //  or XAUDIO2_LOOP_INFINITE to loop forever.
    void* pContext;                     // Context value to be passed back in callbacks.
} XAUDIO2_BUFFER;
需要注意的是,回调实现的是:

void OnStreamEnd() { SetEvent(hBufferEndEvent); }

然后,XAUDIO2_BUFFER的标志位需要设置buffer.Flags = XAUDIO2_END_OF_STREAM;

下面是实现:

#pragma once
#include "WaveFile.h"
#include "XAudio2.h"

class VoiceCallback : public IXAudio2VoiceCallback
{
public:
	HANDLE hBufferEndEvent;
	VoiceCallback() : hBufferEndEvent(CreateEvent(NULL, FALSE, FALSE, NULL)){}
	~VoiceCallback(){ CloseHandle(hBufferEndEvent); }

	//Called when the voice has just finished playing a contiguous audio stream.
	void OnStreamEnd() { SetEvent(hBufferEndEvent); }

	//Unused methods are stubs
	void OnVoiceProcessingPassEnd() { }
	void OnVoiceProcessingPassStart(UINT32 SamplesRequired) {    }
	void OnBufferEnd(void * pBufferContext)    { /*SetEvent(hBufferEndEvent);*/ }
	void OnBufferStart(void * pBufferContext) {    }
	void OnLoopEnd(void * pBufferContext) { /*SetEvent(hBufferEndEvent);*/ }
	void OnVoiceError(void * pBufferContext, HRESULT Error) { }
};

 int main(int argc, char *argv[])
 {
	 HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);;//com初始化
 	if (FAILED(hr))
 		return 0;
 
 	IXAudio2 *pEngine = NULL;
 	hr = XAudio2Create(&pEngine);//创建引擎
 	if (FAILED(hr))
 		return 0;
 
 	IXAudio2MasteringVoice *pMasterVoice = NULL;
 	hr = pEngine->CreateMasteringVoice(&pMasterVoice);//创建主声音,默认是输出当前扬声器
 	if (FAILED(hr))
 		return 0;
 
 	CWaveFile waveFile;
 	hr = waveFile.Open(L"F:\\桌面\\TestSong.wav", NULL, WAVEFILE_READ);//加载文件
 	if (FAILED(hr))
 		return 0;
	
 	WAVEFORMATEX *waveFormat = waveFile.GetFormat();//获取文件格式

	VoiceCallback pCallBack;
 	IXAudio2SourceVoice *pSourceVoice = NULL;
	hr = pEngine->CreateSourceVoice(&pSourceVoice, waveFormat, 0, 1.0f, &pCallBack);//创建源声音,用来提交数据
 	if (FAILED(hr))
 		return 0;
	
 	DWORD size = waveFile.GetSize();//获取文件的大小
 	BYTE *pData = new BYTE[size];//申请内存空间,用于保存数据
 	hr = waveFile.Read(pData, size, &size);//读取文件内容
 	if (FAILED(hr))
 		return 0;
 
	XAUDIO2_BUFFER buffer = {0};//将读取的文件数据,赋值XAUDIO2_BUFFER
 	buffer.AudioBytes = size;
 	buffer.pAudioData = pData;
	buffer.LoopBegin = 0;
	buffer.LoopCount = 3;
	buffer.LoopLength = (double)size / waveFormat->nBlockAlign;//块对齐,数据的最小原子单位
	buffer.PlayBegin = 0;
	buffer.PlayLength = (double)size / waveFormat->nBlockAlign;
 	buffer.pContext = &pCallBack;
 	buffer.Flags = XAUDIO2_END_OF_STREAM;
 
 	hr = pSourceVoice->SubmitSourceBuffer(&buffer);//提交内存数据
 	if (FAILED(hr))
 		return 0;
 
 	hr = pSourceVoice->Start(0);//启动源声音
 	if (FAILED(hr))
 		return 0;
 
	XAUDIO2_VOICE_STATE state;
	pSourceVoice->GetState(&state);//获取状态
	while (state.BuffersQueued > /*XAUDIO2_MAX_QUEUED_BUFFERS - 1*/0)
	{
		WaitForSingleObject(pCallBack.hBufferEndEvent, INFINITE);
		pSourceVoice->GetState(&state);
	}
 
 	pMasterVoice->DestroyVoice();//释放资源
 	pSourceVoice->DestroyVoice();//释放资源
 	pEngine->Release();//释放资源
 	CoUninitialize();//释放资源

	delete []pData;//释放资源
	pData = NULL;

 	return 0;
 }

交流QQ:1245178753

源码下载:http://download.csdn.net/detail/u011417605/9481822

本文地址:http://blog.csdn.net/u011417605/article/details/51066693

你可能感兴趣的:(XAudio2)