欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【XAuido2】播放wav和ogg格式音频文件

程序员文章站 2022-07-05 14:53:48
...

通过MSDN的文档可以知道,XAuido2出了最新的再发行版本,可以兼容win7。而DirectX SDK的XAudio2.7版本已经是2010年的产物了。需要下载库的我有上传资源在CSDN~

封装之后,不仅可播放wav文件,也可以播放ogg文件。不过解析ogg格式需要配置libogg、libvorbis、libvorbisfile三个库,在ogg的官网下载配置即可。

https://xiph.org/downloads/

并且我分为音乐和音效两个混音,可以分别控制音量大小,符合一般游戏的需求。

并且我还是用X3DAudio实现了简单的3D音效封装,代码如下:

标头DNDSound:

//
//name:		DNDSound
//author:	Lveyou
//date:		17-11-11

//other:
//17-11-11: 简单封装zplaylib库 - Lveyou
//18-08-09: 改为 XAudio2 播放 wav文件 - Lveyou
//

#ifndef _DND_SOUND_H_
#define _DND_SOUND_H_


#include "DNDDLL.h"
#include "DNDTypedef.h"
#include "DNDString.h"
#include "DNDGame.h"
#include "DNDDebug.h"



namespace DND
{
	

	class DLL_API Voice
	{
		friend class Voice_imp;
	public:
		virtual void Play() = 0;
		//传入256无限循环
		virtual void PlayLoop(UINT32 num = 256) = 0;
		virtual void Pause() = 0;
		virtual void SetVolume(float volume) = 0;
		virtual void SetFrequencyRatio(float ratio) = 0;
		//是否没有播放
		virtual bool IsEnd() = 0;
		//设置是否播放完成后再删除,默认为true
		virtual void SetDeleteWaitEnd(bool delete_wait_end) = 0;
		
	private:
		virtual ~Voice() {}
	};

	class DLL_API Sound
	{
	public:
		virtual void SetOpen(bool open = true) = 0;
		virtual bool IsOpen() = 0;
		virtual void Load(const String& name, const String& path) = 0;
		//0为音效、1为音乐(group id)
		virtual Voice* Create(const String& name,UINT32 group_id = 0, bool dsp = false, float scaler = 64.0f) = 0;

		//删除音效
		virtual void Delete(Voice* voice) = 0;
		//传入-1代表总音量,0为音效、1为音乐(group id)
		virtual void SetVolume(float v, UINT32 group_id = -1) = 0;
		virtual float GetVolume(UINT32 group_id = -1) = 0;
		//此方法返
		//virtual Voice* GetVoice(const String& name) = 0;

		//请判断是播放状态再调用
		virtual void UpdateDSP(Voice* voice,
			Vector3 voice_pos, Vector3 listener_pos,
			Vector3 voice_v = Vector3(), Vector3 listener_v = Vector3()) = 0;
	};

	inline void snd_play(const String& name, UINT32 group_id = 0, float volume = 1.0f)
	{
		if (name.IsEmpty())
			return;//

		auto* voice = Game::Get()->sound->Create(name, group_id);
		if (voice)
		{
			if (volume != 1.0f)
				voice->SetVolume(volume);
			voice->Play();
		}
			
		else
		{
			debug_err(String(L"DND: 音效获取失败") + name);
			return;
		}

		Game::Get()->sound->Delete(voice);
	}
}

#endif

 实现标头:

//
//name:		DNDSound imp
//author:	Lveyou
//date:		18-08-09

//other:
//18-08-09: 实现 - Lveyou
//20-12-28: 使用XAuido2.9再发行版本,兼容win7 - Lveyou
//

#ifndef _DND_SOUND_IMP_H_
#define _DND_SOUND_IMP_H_

//#ifdef USE_DX_SDK
//#include <..\dx11_sdk\include\XAudio2.h>
//#include <..\dx11_sdk\include\X3DAudio.h>
//#else
//#include <XAudio2.h>
//#include <X3DAudio.h>
//#endif
#include <..\XAudio2.9Redist\include\XAudio2.h>
#include <..\XAudio2.9Redist\include\X3DAudio.h>

#include <map>
#include <list>
using namespace std;


#include "DNDSound.h"
#include "DNDThread.h"


namespace DND
{
	class VoiceData
	{
	public:	
		WAVEFORMATEXTENSIBLE _wfx;
		XAUDIO2_BUFFER _buffer;
		VoiceData()
		{
			ZeroMemory(&_wfx, sizeof(_wfx));
			ZeroMemory(&_buffer, sizeof(_buffer));
		}
	};

	class VoiceCallback;
	class Voice_imp : public Voice//, public Thread
	{
	public:
		virtual void Play() override;
		virtual void PlayLoop(UINT32 num) override;
		virtual void Pause() override;
		virtual void SetVolume(float volume) override;
		virtual void SetFrequencyRatio(float ratio) override;

		virtual bool IsEnd() override;
		virtual void SetDeleteWaitEnd(bool delete_wait_end) override;
		//virtual void _run() override;

		bool _is_valid()
		{
			return _sourceVoice;
		}

		IXAudio2SourceVoice* _sourceVoice;
		VoiceCallback* _voiceCallBack;
		bool _end;//为true代表需要提交
		bool _deleteWaitEnd;//等待结束再删除
		UINT32 _groupID;

		//
		XAUDIO2_BUFFER* _bufferRef;
		//
		X3DAUDIO_EMITTER* _emitter;
		

		Voice_imp();

		~Voice_imp();
	};

	//回调
	class VoiceCallback : public IXAudio2VoiceCallback
	{
	public:
		Voice_imp* _voice;
		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 STDMETHODCALLTYPE OnStreamEnd()
		{
			SetEvent(hBufferEndEvent);
		}

		//Unused methods are stubs
		void STDMETHODCALLTYPE OnVoiceProcessingPassEnd() { }
		void STDMETHODCALLTYPE OnVoiceProcessingPassStart(UINT32 SamplesRequired) {    }
		void STDMETHODCALLTYPE OnBufferEnd(void * pBufferContext) 
		{ 
			//debug_err(L"DND: VoiceCallback: OnBufferEnd "); 
			/*_voice->_sourceVoice->Stop(0);
			if (FAILED(_voice->_sourceVoice->SubmitSourceBuffer(_voice->_bufferRef)))
			{
				debug_err(L"DND: VoiceCallback: 提交SourceBuffer失败: ");
			}
			else*/
			//直接标记为播放结束
			_voice->_end = true;
		}
		void STDMETHODCALLTYPE OnBufferStart(void * pBufferContext) {    }
		void STDMETHODCALLTYPE OnLoopEnd(void * pBufferContext) 
		{    
			停止循环
			//_voice->_sourceVoice->Stop(XAUDIO2_PLAY_TAILS);
			指明它已经播放结束了
			//_voice->_ready = true;
			//debug_err(L"DND: VoiceCallback: OnLoopEnd ");
		}
		void STDMETHODCALLTYPE OnVoiceError(void * pBufferContext, HRESULT Error) { }
	};

	


	class Sound_imp : public Sound
	{
	public:
		virtual void SetOpen(bool open = true) override;
		virtual bool IsOpen() override;
		virtual void Load(const String& name, const String& path) override;
		virtual Voice* Create(const String& name, UINT32 group_id = 0, bool dsp = false, float scaler = 64.0f) override;
		virtual void Delete(Voice* voice) override;
		virtual void SetVolume(float v, UINT32 group_id = -1) override;
		virtual float GetVolume(UINT32 group_id = -1) override;
		//virtual Voice* GetVoice(const String& name) override;

		virtual void UpdateDSP(Voice* voice,
			Vector3 voice_pos, Vector3 listener_pos, 
			Vector3 voice_v = Vector3(), Vector3 listener_v = Vector3()) override;

		void _init();

		//失败返回一个空Voice,防止指针操作崩溃
		Voice* _get_voice_invalid();
		bool _is_voice_valid(Voice* v);

		Sound_imp()
		{
			_xaudio2 = NULL;
			_masterVoice = NULL;
			_open = false;
			_inited = false;
		}
	public:
		IXAudio2* _xaudio2;
		IXAudio2MasteringVoice* _masterVoice;

		//子混音
		IXAudio2SubmixVoice * _submixVoice[2];

		//0为音效、1为音乐(group id)
		XAUDIO2_SEND_DESCRIPTOR _sendArray0[1];
		XAUDIO2_SEND_DESCRIPTOR _sendArray1[1];
		XAUDIO2_VOICE_SENDS _voiceSends[2];

		//设备信息
		//UINT32 _cannelMask;//主混音
		X3DAUDIO_HANDLE _x3dInstance;

		XAUDIO2_VOICE_DETAILS _voiceDetails;

		//name -> data
		map<String, VoiceData*> _mapVoiceData;

		//所有音频
		//map<String, list<Voice_imp*>> _mapAllVoice;
		
		//删除列表
		list<Voice_imp*> _listDelete;

		//听者信息
		X3DAUDIO_LISTENER _listener;
		X3DAUDIO_DSP_SETTINGS _dsp;

		bool _open;
		bool _inited;
	};
}

#endif

 源:

#include "DNDSound_imp.h"
#include "DNDDebug.h"
#include "DNDStreamOutput.h"
#include "DNDSystem.h"

#include "vorbis\codec.h"
#include "vorbis\vorbisfile.h"

//#ifdef _WIN32
//#include <io.h>
//#include <fcntl.h>
//#endif

//#include <fstream>
#include <io.h>

namespace DND
{
#ifdef _XBOX //Big-Endian
#define fourccRIFF 'RIFF'
#define fourccDATA 'data'
#define fourccFMT 'fmt '
#define fourccWAVE 'WAVE'
#define fourccXWMA 'XWMA'
#define fourccDPDS 'dpds'
#endif

#ifndef _XBOX //Little-Endian
#define fourccRIFF 'FFIR'
#define fourccDATA 'atad'
#define fourccFMT ' tmf'
#define fourccWAVE 'EVAW'
#define fourccXWMA 'AMWX'
#define fourccDPDS 'sdpd'
#endif
	HRESULT FindChunk(FILE* fp, DWORD fourcc, DWORD & dwChunkSize, DWORD & dwChunkDataPosition)
	{
		HRESULT hr = S_OK;
		/*if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, 0, NULL, FILE_BEGIN))
			return HRESULT_FROM_WIN32(GetLastError());*/
		if (0 != fseek(fp, 0, SEEK_SET))
			return S_FALSE;

		DWORD dwChunkType;
		DWORD dwChunkDataSize;
		DWORD dwRIFFDataSize = 0;
		DWORD dwFileType;
		DWORD bytesRead = 0;
		DWORD dwOffset = 0;

		while (hr == S_OK)
		{
			DWORD dwRead;
			/*if (0 == ReadFile(hFile, &dwChunkType, sizeof(DWORD), &dwRead, NULL))
				hr = HRESULT_FROM_WIN32(GetLastError());

			if (0 == ReadFile(hFile, &dwChunkDataSize, sizeof(DWORD), &dwRead, NULL))
				hr = HRESULT_FROM_WIN32(GetLastError());*/
			dwRead = fread(&dwChunkType, sizeof(DWORD), 1, fp);

			dwRead = fread(&dwChunkDataSize, sizeof(DWORD), 1, fp);

			switch (dwChunkType)
			{
			case fourccRIFF:
				dwRIFFDataSize = dwChunkDataSize;
				dwChunkDataSize = 4;
				/*if (0 == ReadFile(hFile, &dwFileType, sizeof(DWORD), &dwRead, NULL))
					hr = HRESULT_FROM_WIN32(GetLastError());*/
				dwRead = fread(&dwFileType, sizeof(DWORD), 1, fp);
				break;

			default:
				/*if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, dwChunkDataSize, NULL, FILE_CURRENT))
					return HRESULT_FROM_WIN32(GetLastError());*/
				if (0 != fseek(fp, dwChunkDataSize, SEEK_CUR))
					return S_FALSE;
				break;
			}

			dwOffset += sizeof(DWORD) * 2;

			if (dwChunkType == fourcc)
			{
				dwChunkSize = dwChunkDataSize;
				dwChunkDataPosition = dwOffset;
				return S_OK;
			}

			dwOffset += dwChunkDataSize;

			if (bytesRead >= dwRIFFDataSize) return S_FALSE;

		}


		return S_OK;

	}

	HRESULT ReadChunkData(FILE* fp, void * buffer, DWORD buffersize, DWORD bufferoffset)
	{
		HRESULT hr = S_OK;
	/*	if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, bufferoffset, NULL, FILE_BEGIN))
			return HRESULT_FROM_WIN32(GetLastError());*/
		if (0 != fseek(fp, bufferoffset, SEEK_SET))
			return S_FALSE;
		//DWORD dwRead;
		/*if (0 == ReadFile(hFile, buffer, buffersize, &dwRead, NULL))
			hr = HRESULT_FROM_WIN32(GetLastError());*/
		fread(buffer, buffersize, 1, fp);
		return hr;
	}

	void Sound_imp::SetOpen(bool open /*= true*/)
	{
		_open = open;
		
		if(_open)
			_init();
		
	}

	bool Sound_imp::IsOpen()
	{
		return _open;
	}

	


	//
	//以上代码copy自dx sdk文档


	void Sound_imp::Load(const String& name, const String& path)
	{
		if (!_open)
			return;

		String name_format = path;
		int index = name_format.FindEnd(L'.');
		if(index != -1)
			name_format.CutHead(index);

		int file_type = 0;
		if (name_format == L".wav")
			file_type = 1;
		else if (name_format == L".ogg")
			file_type = 2;

		if (file_type == 1)
		{
			FILE* file = NULL;
			if (Game::Get()->sys->GetFile(path, file) == -1)
			{
				debug_err(String(L"DND: wav文件加载失败: ") + path);
				return;
			}
			//HANDLE hFile = (HANDLE)_get_osfhandle((int)file);
				/*HANDLE hFile = CreateFile(
					file,
					GENERIC_READ,
					FILE_SHARE_READ,
					NULL,
					OPEN_EXISTING,
					0,
					NULL);*/

					/*if (INVALID_HANDLE_VALUE == hFile)
					{
						debug_err(String(L"DND: wav文件获取文件句柄失败: ") + path);
						return;
					}*/

			if (0 != fseek(file, 0, SEEK_SET))
				//INVALID_SET_FILE_POINTER == SetFilePointer(hFile, 0, NULL, FILE_BEGIN))
			{
				debug_err(String(L"DND: wav文件加载失败2: ") + path);
				fclose(file);
				return;
			}

			DWORD dwChunkSize;
			DWORD dwChunkPosition;
			//check the file type, should be fourccWAVE or 'XWMA'
			FindChunk(file, fourccRIFF, dwChunkSize, dwChunkPosition);
			DWORD filetype;
			ReadChunkData(file, &filetype, sizeof(DWORD), dwChunkPosition);
			if (filetype != fourccWAVE)
			{
				debug_err(String(L"DND: 音效文件加载失败,不是wav文件: ") + path);
				fclose(file);
				return;
			}

			VoiceData* ret = new VoiceData;


			if (S_OK != FindChunk(file, fourccFMT, dwChunkSize, dwChunkPosition))
			{
				debug_err(String(L"DND: 加载wav文件fourccFMT失败: ") + path);
				fclose(file);
				return;
			}
				
			if (S_OK != ReadChunkData(file, &(ret->_wfx), dwChunkSize, dwChunkPosition))
			{
				debug_err(String(L"DND: 加载wav文件_wfx失败: ") + path);
				fclose(file);
				return;
			};


			//fill out the audio data buffer with the contents of the fourccDATA chunk
			if (S_OK != FindChunk(file, fourccDATA, dwChunkSize, dwChunkPosition))
			{
				debug_err(String(L"DND: 加载wav文件fourccDATA失败: ") + path);
				fclose(file);
				return;
			};
			
			BYTE * pDataBuffer = new BYTE[dwChunkSize];

			if (S_OK != ReadChunkData(file, pDataBuffer, dwChunkSize, dwChunkPosition))
			{
				debug_err(String(L"DND: 加载wav文件pDataBuffer失败: ") + path);
				fclose(file);
				return;
			};
			

			(ret->_buffer).AudioBytes = dwChunkSize;  //buffer containing audio data
			(ret->_buffer).pAudioData = pDataBuffer;  //size of the audio buffer in bytes
			(ret->_buffer).Flags = 0;// XAUDIO2_END_OF_STREAM; // tell the source voice not to expect any data after this buffer
			(ret->_buffer).LoopCount = 0;// XAUDIO2_LOOP_INFINITE


			_mapVoiceData[name] = ret;

			//至少一个准备播放
			//_mapAllVoice[name].push_back((Voice_imp*)Create(name));


			//CloseHandle(hFile);
			fclose(file);
		}
		else if(file_type == 2)
		{
			FILE* file = NULL;

			if(Game::Get()->sys->GetFile(path, file) == -1)
			{
				debug_err(String(L"DND: ogg文件加载失败: ") + path);
				return;
			}
			/*char temp[1024] = {0};
			name.GetMultiByteStr(temp, 1024);


			if (fopen_s(&file, temp, "rb") == NULL)
			{
				debug_err(String(L"DND: ogg文件加载失败: ") + path);
				return;
			}*/

			OggVorbis_File vf;
			int eof = 0;
			int current_section;

//#ifdef _WIN32
//			_setmode(_fileno(stdin), _O_BINARY);
//			_setmode(_fileno(stdout), _O_BINARY);
//#endif
			int ov_ret = ov_open_callbacks(file, &vf, NULL, 0, OV_CALLBACKS_NOCLOSE);
			if (ov_ret < 0)
			{
				debug_err(String(L"DND: 音效文件加载失败,不是ogg文件: ") + path);
				fclose(file);
				return;
			}


			VoiceData* ret = new VoiceData;

			//写入wfx
			{
				vorbis_info *vi = ov_info(&vf, -1);
	
				//只需要填充Foramt这一个结构,其他被截断了
				ret->_wfx.Format.cbSize = 0;
				ret->_wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
				ret->_wfx.Format.nChannels = vi->channels;//声道数
				ret->_wfx.Format.nSamplesPerSec = vi->rate;//采样率
				ret->_wfx.Format.wBitsPerSample = 16; //msdn: 如果wFormatTag是WAVE_FORMAT_PCM,则wBitsPerSample应该等于8或16
				
				ret->_wfx.Format.nBlockAlign = ret->_wfx.Format.nChannels * ret->_wfx.Format.wBitsPerSample / 8;//msdn: 如果wFormatTag是WAVE_FORMAT_PCM或WAVE_FORMAT_EXTENSIBLE,则nBlockAlign必须等于nChannels和wBitsPerSample的乘积除以8(每字节位数)

				ret->_wfx.Format.nAvgBytesPerSec = ret->_wfx.Format.nSamplesPerSec * ret->_wfx.Format.nBlockAlign;//msdn: 如果wFormatTag是WAVE_FORMAT_PCM,则nAvgBytesPerSec应该等于nSamplesPerSec和nBlockAlign的乘积
	
			}


			//写入buffer
			char pcmout[4096];
			StreamOutput o;
			o.SetDelete(false);

			while (!eof) {
				long ret = ov_read(&vf, pcmout, sizeof(pcmout), 0, 2, 1, &current_section);
				if (ret == 0) {
					/* EOF */
					eof = 1;
				}
				else if (ret < 0) {
					/* error in the stream.  Not a problem, just reporting it in
					case we (the app) cares.  In this case, we don't. */
				}
				else {
					/* we don't bother dealing with sample rate changes, etc, but
					you'll have to*/
					o.Write(pcmout, ret);
			
					//fwrite(pcmout, 1, ret, stdout);
				}
			}


			(ret->_buffer).AudioBytes = o.GetLength();  //buffer containing audio data
			(ret->_buffer).pAudioData = o.GetBuffer();  //size of the audio buffer in bytes
			(ret->_buffer).Flags = 0;// XAUDIO2_END_OF_STREAM; // tell the source voice not to expect any data after this buffer
			(ret->_buffer).LoopCount = 0;


			_mapVoiceData[name] = ret;

			//至少一个准备播放
			//_mapAllVoice[name].push_back((Voice_imp*)Create(name));

			ov_clear(&vf);

			fclose(file);
		}
		else
		{
			debug_err(String(L"DND: 音效文件加载失败,不支持的格式: ") + name_format);
			return;
		}
		
	}

	DND::Voice* Sound_imp::Create(const String& name, UINT32 group_id, bool dsp, float scaler)
	{
		if (!_open)
			return _get_voice_invalid();

		Voice_imp* ret = new Voice_imp;
		ret->_voiceCallBack = new VoiceCallback; 
		ret->_voiceCallBack->_voice = ret;
		ret->_groupID = group_id;

		auto& iter = _mapVoiceData.find(name);

		if (iter == _mapVoiceData.end())
		{
			debug_err(String(L"DND: Sound::Create: 音频没有加载: ") + name);
			delete ret;
			return _get_voice_invalid();
		}

		
		if (FAILED(_xaudio2->CreateSourceVoice(&(ret->_sourceVoice), (WAVEFORMATEX*)&(iter->second->_wfx),
			0, XAUDIO2_DEFAULT_FREQ_RATIO, ret->_voiceCallBack, &_voiceSends[group_id], NULL)))
		{
			debug_err(String(L"DND: Sound::Create: 创建SourceVoice失败: ") + name);
			delete ret;
			return _get_voice_invalid();
		}

		ret->_bufferRef = &(iter->second->_buffer);
		/*if (FAILED(ret->_sourceVoice->SubmitSourceBuffer(ret->_bufferRef)))
		{
			debug_err(String(L"DND: Sound::Create: 提交SourceBuffer失败: ") + name);
			delete ret;
			return NULL;
		}*/

		//dsp
		if (dsp)
		{
			XAUDIO2_VOICE_DETAILS voice_detail;
			ret->_sourceVoice->GetVoiceDetails(&voice_detail);
			if (voice_detail.InputChannels != 2)
			{
				debug_err(String(L"DND: Sound::Create: 音频文件通道数不为2,不能创建DSP效果: ") + name);
				return ret;
			}

			ret->_emitter = new X3DAUDIO_EMITTER;
			ZeroMemory(ret->_emitter, sizeof(X3DAUDIO_EMITTER));
			ret->_emitter->ChannelCount = 2;
			ret->_emitter->CurveDistanceScaler = scaler;

			FLOAT32* p_cas = new FLOAT32[2];
			for (UINT32 i = 0; i != 2; ++i)
				p_cas[i] = 0;

			ret->_emitter->pChannelAzimuths = p_cas;

			ret->_emitter->OrientFront = {0.0f, 0.0f, 1.0f};
			ret->_emitter->OrientTop = { 0.0f, -1.0f, 0.0f };
			
		}

		return ret;
	}

	void Sound_imp::SetVolume(float v, UINT32 group_id)
	{
		if (!_open)
			return;
		//_masterVoice->SetVolume(v);
		if (group_id == -1)
			_masterVoice->SetVolume(v);
		else
			_submixVoice[group_id]->SetVolume(v);
	}

	float Sound_imp::GetVolume(UINT32 group_id)
	{
		if (!_open)
			return 0;

		float ret;

		if(group_id == -1)
			_masterVoice->GetVolume(&ret);
		else
			_submixVoice[group_id]->GetVolume(&ret);
		//_masterVoice->GetVolume(&ret);
		return ret;
	}

	//DND::Voice* Sound_imp::GetVoice(const String& name)
	//{
	//	auto& iter = _mapAllVoice.find(name);
	//	if (iter == _mapAllVoice.end())
	//		return NULL;

	//	for (auto& iter2 : iter->second)
	//	{
	//		if (iter2->_ready)
	//			return iter2;
	//	}

	//	Voice* ret = Create(name);
	//	iter->second.push_back((Voice_imp*)ret);

	//	return ret;

	//}

	void Sound_imp::UpdateDSP(Voice* voice, Vector3 voice_pos, Vector3 listener_pos, Vector3 voice_v /*= Vector3()*/, Vector3 listener_v /*= Vector3()*/)
	{
		if (!_is_voice_valid(voice))
			return;

		Voice_imp* p = (Voice_imp*)voice;
		if (p && p->_emitter)
		{
			//设置速度与位置
			p->_emitter->Position.x = voice_pos.a;
			p->_emitter->Position.y = voice_pos.b;
			p->_emitter->Position.z = voice_pos.c;

			p->_emitter->Velocity.x = voice_v.a;
			p->_emitter->Velocity.y = voice_v.b;
			p->_emitter->Velocity.z = voice_v.c;

			_listener.Position.x = listener_pos.a;
			_listener.Position.y = listener_pos.b;
			_listener.Position.z = listener_pos.c;

			_listener.Velocity.x = listener_v.a;
			_listener.Velocity.y = listener_v.b;
			_listener.Velocity.z = listener_v.c;

			//计算
			X3DAudioCalculate(_x3dInstance, &_listener, p->_emitter,
				X3DAUDIO_CALCULATE_MATRIX
				//| X3DAUDIO_CALCULATE_DOPPLER
				/*| X3DAUDIO_CALCULATE_LPF_DIRECT
				| X3DAUDIO_CALCULATE_REVERB*/,
				&_dsp);

			//应用效果
			p->_sourceVoice->SetOutputMatrix(_submixVoice[p->_groupID], 2, _voiceDetails.InputChannels, _dsp.pMatrixCoefficients);
			//p->_sourceVoice->SetFrequencyRatio(_dsp.DopplerFactor);

			/*p->_sourceVoice->SetOutputMatrix(_submixVoice[p->_groupID], 2, _voiceDetails.InputChannels, &_dsp.ReverbLevel);*/
			
			/*XAUDIO2_FILTER_PARAMETERS FilterParameters = { LowPassFilter, 2.0f * sinf(X3DAUDIO_PI / 6.0f * _dsp.LPFDirectCoefficient), 1.0f };
			p->_sourceVoice->SetFilterParameters(&FilterParameters);*/
		}
	}

	void Sound_imp::_init()
	{
		if (_inited)
			return;
		_inited = true;

#ifndef _XBOX
		CoInitializeEx(NULL, COINIT_MULTITHREADED);
#endif
		HRESULT hr;
		if (FAILED(hr = XAudio2Create(&_xaudio2, 0, XAUDIO2_DEFAULT_PROCESSOR)))
		{
			dnd_assert(L"DND: XAudio2 初始化失败!");
			return;
		}

//#define USE_DX_SDK_SND

#ifdef USE_DX_SDK_SND
		//获取设备详情
		XAUDIO2_DEVICE_DETAILS device_details;
		_xaudio2->GetDeviceDetails(0, &device_details);
		debug_msg(String::Format(L"DND: 音频设备: %ws-%ws", device_details.DisplayName, device_details.DeviceID));
#endif

		//直接使用0,全局音频设备
		if (FAILED(hr = _xaudio2->CreateMasteringVoice(&_masterVoice, XAUDIO2_DEFAULT_CHANNELS,
			XAUDIO2_DEFAULT_SAMPLERATE, 0, 0, NULL)))
		{
			dnd_assert(L"DND: XAudio2 创建 MasterVoice 失败!");
			return;
		}


		//获取主控详情
		_masterVoice->GetVoiceDetails(&_voiceDetails);

		//子混音(应和主混音通道数一致)
		if (FAILED(hr = _xaudio2->CreateSubmixVoice(&(_submixVoice[0]),
			_voiceDetails.InputChannels,
			_voiceDetails.InputSampleRate, 0, 0, 0)))
		{
			dnd_assert(L"DND: XAudio2 创建 SubmixVoice0 失败!");
			return;
		}
		if (FAILED(hr = _xaudio2->CreateSubmixVoice(&(_submixVoice[1]), 
			_voiceDetails.InputChannels,
			_voiceDetails.InputSampleRate, 0, 0, 0)))
		{
			dnd_assert(L"DND: XAudio2 创建 SubmixVoice1 失败!");
			return;
		}
		_sendArray0[0] = { 0, _submixVoice[0] };
		_sendArray1[0] = { 0, _submixVoice[1] };
		_voiceSends[0] = { 1, _sendArray0 };
		_voiceSends[1] = { 1, _sendArray1 };

		

		//初始化X3DAudio
		DWORD dwChannelMask;
#ifdef USE_DX_SDK_SND
		dwChannelMask = device_details.OutputFormat.dwChannelMask;
#else
		_masterVoice->GetChannelMask(&dwChannelMask);
#endif
		X3DAudioInitialize(dwChannelMask, X3DAUDIO_SPEED_OF_SOUND, _x3dInstance);
		

		//听者只有一个
		ZeroMemory(&_listener, sizeof(X3DAUDIO_LISTENER));
		_listener.OrientFront = { 0.0f,0.0f,-1.0f };
		_listener.OrientTop = { 0.0f,-1.0f,0.0f };

		//dsp
		ZeroMemory(&_dsp, sizeof(X3DAUDIO_DSP_SETTINGS));
		FLOAT32 * matrix = new FLOAT32[2 * _voiceDetails.InputChannels];
		FLOAT32* dtimes = new FLOAT32[_voiceDetails.InputChannels];
		_dsp.SrcChannelCount = 2;
		_dsp.DstChannelCount = _voiceDetails.InputChannels;
		_dsp.pMatrixCoefficients = matrix;
		_dsp.pDelayTimes = dtimes;
		/*_dsp.EmitterToListenerDistance = 1.0f / 64.0f;
		_dsp.EmitterVelocityComponent = 1.0f / 64.0f;
		_dsp.ListenerVelocityComponent = 1.0f / 64.0f;*/

	}

	void Sound_imp::Delete(Voice* voice)
	{
		if (!voice)
			return;

		Voice_imp* p = (Voice_imp*)voice;
		if (p->_sourceVoice == NULL)
		{
			delete p;
			return;
		}

		if (!p->_deleteWaitEnd || p->_end)
		{
			p->_sourceVoice->Stop();
			delete p;
		}
		else
		{
			_listDelete.push_back(p);
		}
	}

	DND::Voice* Sound_imp::_get_voice_invalid()
	{
		Voice_imp* ret = new Voice_imp();
		return ret;
	}

	bool Sound_imp::_is_voice_valid(Voice* v)
	{
		return v 
		&& ((Voice_imp*)v)->_sourceVoice;
	}


	void Voice_imp::Play()
	{
		if (!_is_valid())
			return;

		if (_end)
		{
			_bufferRef->LoopCount = 0;
			if (FAILED(_sourceVoice->SubmitSourceBuffer(_bufferRef)))
			{
				debug_err(String(L"DND: Voice::Play: 提交SourceBuffer失败: "));
				return;
			}
			_sourceVoice->Start(0, XAUDIO2_COMMIT_NOW);
			_end = false;
		}
		

		/*WaitForSingleObject(_voiceCallBack->hBufferEndEvent, INFINITE);
		Thread::Start();*/
		//_sourceVoice->SetChannelVolumes(,)
	}


	void Voice_imp::PlayLoop(UINT32 num)
	{
		if (!_is_valid())
			return;

		if (_end)
		{//结束了则重新提交
			_bufferRef->LoopCount = num - 1;
			if (FAILED(_sourceVoice->SubmitSourceBuffer(_bufferRef)))
			{
				debug_err(String(L"DND: Voice::PlayLoop: 提交SourceBuffer失败: "));
				return;
			}
			_sourceVoice->Start(0, XAUDIO2_COMMIT_NOW);
			_end = false;
		}
	}

	void Voice_imp::Pause()
	{
		if (!_is_valid())
			return;
		_sourceVoice->Stop(0, XAUDIO2_COMMIT_NOW);

	}

	void Voice_imp::SetVolume(float volume)
	{
		if (!_is_valid())
			return;
		_sourceVoice->SetVolume(volume);
	}


	bool Voice_imp::IsEnd()
	{
		return _end;
	}

	void Voice_imp::SetDeleteWaitEnd(bool delete_wait_end)
	{
		_deleteWaitEnd = delete_wait_end;
	}

	Voice_imp::Voice_imp()
	{

		_sourceVoice = NULL;
		_voiceCallBack = NULL;
		_end = true;
		_deleteWaitEnd = true;
		_groupID = 0;

		_emitter = NULL;
		_bufferRef = NULL;

	}

	Voice_imp::~Voice_imp()
	{
		if (_sourceVoice)
			_sourceVoice->DestroyVoice();

		//先于_sourceVoice释放会导致深不可测的bug
		delete _voiceCallBack;

		if (_emitter)
			delete[] _emitter->pChannelAzimuths;
		delete _emitter;
	}

	void Voice_imp::SetFrequencyRatio(float ratio)
	{
		if (!_is_valid())
			return;
		_sourceVoice->SetFrequencyRatio(ratio);
		//_sourceVoice->
	}

	/*void Voice_imp::_run()
	{

	}*/

}