【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的官网下载配置即可。
并且我分为音乐和音效两个混音,可以分别控制音量大小,符合一般游戏的需求。
并且我还是用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, ¤t_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()
{
}*/
}
下一篇: 如何正确拆解三脚架?三脚架拆解图赏