| 
									
										
										
										
											2015-05-24 06:55:12 +02:00
										 |  |  | // Copyright 2010 Dolphin Emulator Project
 | 
					
						
							| 
									
										
										
										
											2015-05-18 01:08:10 +02:00
										 |  |  | // Licensed under GPLv2+
 | 
					
						
							| 
									
										
										
										
											2013-04-17 23:09:55 -04:00
										 |  |  | // Refer to the license.txt file included.
 | 
					
						
							| 
									
										
										
										
											2010-11-14 23:56:26 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | #include "AudioCommon/XAudio2Stream.h"
 | 
					
						
							| 
									
										
										
										
											2013-10-19 02:27:57 -07:00
										 |  |  | #include <xaudio2.h>
 | 
					
						
							| 
									
										
										
										
											2014-02-17 05:18:15 -05:00
										 |  |  | #include "AudioCommon/AudioCommon.h"
 | 
					
						
							| 
									
										
										
										
											2014-04-14 01:15:23 +02:00
										 |  |  | #include "Common/Event.h"
 | 
					
						
							| 
									
										
										
										
											2015-09-26 17:13:07 -04:00
										 |  |  | #include "Common/Logging/Log.h"
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | #include "Common/MsgHandler.h"
 | 
					
						
							| 
									
										
										
										
											2010-11-14 23:56:26 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-19 02:27:57 -07:00
										 |  |  | #ifndef XAUDIO2_DLL
 | 
					
						
							|  |  |  | #error You are building this module against the wrong version of DirectX. You probably need to remove DXSDK_DIR from your include path.
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct StreamingVoiceContext : public IXAudio2VoiceCallback | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | private: | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   CMixer* const m_mixer; | 
					
						
							|  |  |  |   Common::Event& m_sound_sync_event; | 
					
						
							|  |  |  |   IXAudio2SourceVoice* m_source_voice; | 
					
						
							|  |  |  |   std::unique_ptr<BYTE[]> xaudio_buffer; | 
					
						
							| 
									
										
										
										
											2013-10-19 02:27:57 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   void SubmitBuffer(PBYTE buf_data); | 
					
						
							| 
									
										
										
										
											2013-10-19 02:27:57 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | public: | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   StreamingVoiceContext(IXAudio2* pXAudio2, CMixer* pMixer, Common::Event& pSyncEvent); | 
					
						
							| 
									
										
										
										
											2013-10-19 02:27:57 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   ~StreamingVoiceContext(); | 
					
						
							| 
									
										
										
										
											2013-10-19 02:27:57 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   void StreamingVoiceContext::Stop(); | 
					
						
							|  |  |  |   void StreamingVoiceContext::Play(); | 
					
						
							| 
									
										
										
										
											2013-10-19 02:27:57 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   STDMETHOD_(void, OnVoiceError)(THIS_ void* pBufferContext, HRESULT Error) {} | 
					
						
							|  |  |  |   STDMETHOD_(void, OnVoiceProcessingPassStart)(UINT32) {} | 
					
						
							|  |  |  |   STDMETHOD_(void, OnVoiceProcessingPassEnd)() {} | 
					
						
							|  |  |  |   STDMETHOD_(void, OnBufferStart)(void*) {} | 
					
						
							|  |  |  |   STDMETHOD_(void, OnLoopEnd)(void*) {} | 
					
						
							|  |  |  |   STDMETHOD_(void, OnStreamEnd)() {} | 
					
						
							|  |  |  |   STDMETHOD_(void, OnBufferEnd)(void* context); | 
					
						
							| 
									
										
										
										
											2013-10-19 02:27:57 -07:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-15 23:09:12 +00:00
										 |  |  | const int NUM_BUFFERS = 3; | 
					
						
							|  |  |  | const int SAMPLES_PER_BUFFER = 96; | 
					
						
							| 
									
										
										
										
											2010-11-14 23:56:26 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-15 23:09:12 +00:00
										 |  |  | const int NUM_CHANNELS = 2; | 
					
						
							|  |  |  | const int BUFFER_SIZE = SAMPLES_PER_BUFFER * NUM_CHANNELS; | 
					
						
							|  |  |  | const int BUFFER_SIZE_BYTES = BUFFER_SIZE * sizeof(s16); | 
					
						
							| 
									
										
										
										
											2010-11-14 23:56:26 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-15 23:09:12 +00:00
										 |  |  | void StreamingVoiceContext::SubmitBuffer(PBYTE buf_data) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   XAUDIO2_BUFFER buf = {}; | 
					
						
							|  |  |  |   buf.AudioBytes = BUFFER_SIZE_BYTES; | 
					
						
							|  |  |  |   buf.pContext = buf_data; | 
					
						
							|  |  |  |   buf.pAudioData = buf_data; | 
					
						
							| 
									
										
										
										
											2010-11-14 23:56:26 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   m_source_voice->SubmitSourceBuffer(&buf); | 
					
						
							| 
									
										
										
										
											2011-03-15 23:09:12 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2010-11-14 23:56:26 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | StreamingVoiceContext::StreamingVoiceContext(IXAudio2* pXAudio2, CMixer* pMixer, | 
					
						
							|  |  |  |                                              Common::Event& pSyncEvent) | 
					
						
							|  |  |  |     : m_mixer(pMixer), m_sound_sync_event(pSyncEvent), | 
					
						
							|  |  |  |       xaudio_buffer(new BYTE[NUM_BUFFERS * BUFFER_SIZE_BYTES]()) | 
					
						
							| 
									
										
										
										
											2011-03-15 23:09:12 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   WAVEFORMATEXTENSIBLE wfx = {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; | 
					
						
							|  |  |  |   wfx.Format.nSamplesPerSec = m_mixer->GetSampleRate(); | 
					
						
							|  |  |  |   wfx.Format.nChannels = 2; | 
					
						
							|  |  |  |   wfx.Format.wBitsPerSample = 16; | 
					
						
							|  |  |  |   wfx.Format.nBlockAlign = wfx.Format.nChannels * wfx.Format.wBitsPerSample / 8; | 
					
						
							|  |  |  |   wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign; | 
					
						
							|  |  |  |   wfx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); | 
					
						
							|  |  |  |   wfx.Samples.wValidBitsPerSample = 16; | 
					
						
							|  |  |  |   wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; | 
					
						
							|  |  |  |   wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // create source voice
 | 
					
						
							|  |  |  |   HRESULT hr; | 
					
						
							|  |  |  |   if (FAILED(hr = pXAudio2->CreateSourceVoice(&m_source_voice, &wfx.Format, XAUDIO2_VOICE_NOSRC, | 
					
						
							|  |  |  |                                               1.0f, this))) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     PanicAlert("XAudio2 CreateSourceVoice failed: %#X", hr); | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   m_source_voice->Start(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // start buffers with silence
 | 
					
						
							|  |  |  |   for (int i = 0; i != NUM_BUFFERS; ++i) | 
					
						
							|  |  |  |     SubmitBuffer(xaudio_buffer.get() + (i * BUFFER_SIZE_BYTES)); | 
					
						
							| 
									
										
										
										
											2011-03-15 23:09:12 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2010-11-14 23:56:26 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-15 23:09:12 +00:00
										 |  |  | StreamingVoiceContext::~StreamingVoiceContext() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (m_source_voice) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     m_source_voice->Stop(); | 
					
						
							|  |  |  |     m_source_voice->DestroyVoice(); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2011-03-15 23:09:12 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2010-11-14 23:56:26 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-15 23:09:12 +00:00
										 |  |  | void StreamingVoiceContext::Stop() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (m_source_voice) | 
					
						
							|  |  |  |     m_source_voice->Stop(); | 
					
						
							| 
									
										
										
										
											2011-03-15 23:09:12 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2010-11-14 23:56:26 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-15 23:09:12 +00:00
										 |  |  | void StreamingVoiceContext::Play() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (m_source_voice) | 
					
						
							|  |  |  |     m_source_voice->Start(); | 
					
						
							| 
									
										
										
										
											2011-03-15 23:09:12 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2010-11-14 23:56:26 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-15 23:09:12 +00:00
										 |  |  | void StreamingVoiceContext::OnBufferEnd(void* context) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   //  buffer end callback; gets SAMPLES_PER_BUFFER samples for a new buffer
 | 
					
						
							| 
									
										
										
										
											2011-03-15 23:09:12 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (!m_source_voice || !context) | 
					
						
							|  |  |  |     return; | 
					
						
							| 
									
										
										
										
											2013-03-19 21:51:12 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   // m_sound_sync_event->Wait(); // sync
 | 
					
						
							|  |  |  |   // m_sound_sync_event->Spin(); // or tight sync
 | 
					
						
							| 
									
										
										
										
											2013-03-19 21:51:12 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   m_mixer->Mix(static_cast<short*>(context), SAMPLES_PER_BUFFER); | 
					
						
							|  |  |  |   SubmitBuffer(static_cast<BYTE*>(context)); | 
					
						
							| 
									
										
										
										
											2011-03-15 23:09:12 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2010-11-14 23:56:26 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-19 02:27:57 -07:00
										 |  |  | HMODULE XAudio2::m_xaudio2_dll = nullptr; | 
					
						
							|  |  |  | typedef decltype(&XAudio2Create) XAudio2Create_t; | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | void* XAudio2::PXAudio2Create = nullptr; | 
					
						
							| 
									
										
										
										
											2013-10-19 02:27:57 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | bool XAudio2::InitLibrary() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (m_xaudio2_dll) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   m_xaudio2_dll = ::LoadLibrary(XAUDIO2_DLL); | 
					
						
							|  |  |  |   if (!m_xaudio2_dll) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!PXAudio2Create) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     PXAudio2Create = (XAudio2Create_t)::GetProcAddress(m_xaudio2_dll, "XAudio2Create"); | 
					
						
							|  |  |  |     if (!PXAudio2Create) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       ::FreeLibrary(m_xaudio2_dll); | 
					
						
							|  |  |  |       m_xaudio2_dll = nullptr; | 
					
						
							|  |  |  |       return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return true; | 
					
						
							| 
									
										
										
										
											2013-10-19 02:27:57 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-24 04:13:02 -04:00
										 |  |  | XAudio2::XAudio2() | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |     : m_mastering_voice(nullptr), m_volume(1.0f), | 
					
						
							|  |  |  |       m_cleanup_com(SUCCEEDED(CoInitializeEx(nullptr, COINIT_MULTITHREADED))) | 
					
						
							| 
									
										
										
										
											2013-10-19 02:27:57 -07:00
										 |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | XAudio2::~XAudio2() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   Stop(); | 
					
						
							|  |  |  |   if (m_cleanup_com) | 
					
						
							|  |  |  |     CoUninitialize(); | 
					
						
							| 
									
										
										
										
											2013-10-19 02:27:57 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-11-14 23:56:26 +00:00
										 |  |  | bool XAudio2::Start() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   HRESULT hr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // callback doesn't seem to run on a specific CPU anyways
 | 
					
						
							|  |  |  |   IXAudio2* xaudptr; | 
					
						
							|  |  |  |   if (FAILED(hr = ((XAudio2Create_t)PXAudio2Create)(&xaudptr, 0, XAUDIO2_DEFAULT_PROCESSOR))) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     PanicAlert("XAudio2 init failed: %#X", hr); | 
					
						
							|  |  |  |     Stop(); | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   m_xaudio2 = std::unique_ptr<IXAudio2, Releaser>(xaudptr); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // XAudio2 master voice
 | 
					
						
							|  |  |  |   // XAUDIO2_DEFAULT_CHANNELS instead of 2 for expansion?
 | 
					
						
							|  |  |  |   if (FAILED(hr = m_xaudio2->CreateMasteringVoice(&m_mastering_voice, 2, m_mixer->GetSampleRate()))) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     PanicAlert("XAudio2 master voice creation failed: %#X", hr); | 
					
						
							|  |  |  |     Stop(); | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Volume
 | 
					
						
							|  |  |  |   m_mastering_voice->SetVolume(m_volume); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   m_voice_context = std::unique_ptr<StreamingVoiceContext>( | 
					
						
							|  |  |  |       new StreamingVoiceContext(m_xaudio2.get(), m_mixer.get(), m_sound_sync_event)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return true; | 
					
						
							| 
									
										
										
										
											2010-11-14 23:56:26 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void XAudio2::SetVolume(int volume) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   // linear 1- .01
 | 
					
						
							|  |  |  |   m_volume = (float)volume / 100.f; | 
					
						
							| 
									
										
										
										
											2010-11-14 23:56:26 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (m_mastering_voice) | 
					
						
							|  |  |  |     m_mastering_voice->SetVolume(m_volume); | 
					
						
							| 
									
										
										
										
											2010-11-14 23:56:26 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void XAudio2::Clear(bool mute) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   m_muted = mute; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (m_voice_context) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     if (m_muted) | 
					
						
							|  |  |  |       m_voice_context->Stop(); | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |       m_voice_context->Play(); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2010-11-14 23:56:26 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void XAudio2::Stop() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   // m_sound_sync_event.Set();
 | 
					
						
							| 
									
										
										
										
											2010-11-14 23:56:26 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   m_voice_context.reset(); | 
					
						
							| 
									
										
										
										
											2010-11-14 23:56:26 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (m_mastering_voice) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     m_mastering_voice->DestroyVoice(); | 
					
						
							|  |  |  |     m_mastering_voice = nullptr; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2013-03-19 21:51:12 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   m_xaudio2.reset();  // release interface
 | 
					
						
							| 
									
										
										
										
											2013-10-19 02:27:57 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (m_xaudio2_dll) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     ::FreeLibrary(m_xaudio2_dll); | 
					
						
							|  |  |  |     m_xaudio2_dll = nullptr; | 
					
						
							|  |  |  |     PXAudio2Create = nullptr; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2010-11-14 23:56:26 +00:00
										 |  |  | } |