2018-12-20 23:35:49 +01:00
|
|
|
|
#ifdef _WIN32
|
2016-07-21 15:41:40 +02:00
|
|
|
|
|
|
|
|
|
|
#include "Utilities/Log.h"
|
|
|
|
|
|
#include "Utilities/StrFmt.h"
|
|
|
|
|
|
#include "Emu/System.h"
|
|
|
|
|
|
|
|
|
|
|
|
#include "XAudio2Thread.h"
|
|
|
|
|
|
#include "3rdparty/minidx12/Include/xaudio2.h"
|
|
|
|
|
|
|
2016-07-30 16:10:01 +02:00
|
|
|
|
static thread_local HMODULE s_tls_xaudio2_lib{};
|
2016-07-21 15:41:40 +02:00
|
|
|
|
static thread_local IXAudio2* s_tls_xaudio2_instance{};
|
|
|
|
|
|
static thread_local IXAudio2MasteringVoice* s_tls_master_voice{};
|
|
|
|
|
|
static thread_local IXAudio2SourceVoice* s_tls_source_voice{};
|
|
|
|
|
|
|
2016-07-30 16:10:01 +02:00
|
|
|
|
void XAudio2Thread::xa28_init(void* lib)
|
2016-07-21 15:41:40 +02:00
|
|
|
|
{
|
2016-07-30 16:10:01 +02:00
|
|
|
|
s_tls_xaudio2_lib = (HMODULE)lib;
|
|
|
|
|
|
|
|
|
|
|
|
const auto create = (XAudio2Create)GetProcAddress(s_tls_xaudio2_lib, "XAudio2Create");
|
2016-07-21 15:41:40 +02:00
|
|
|
|
|
|
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
|
|
|
2016-07-25 18:30:21 +02:00
|
|
|
|
hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
|
|
|
|
|
if (FAILED(hr))
|
|
|
|
|
|
{
|
|
|
|
|
|
LOG_ERROR(GENERAL, "XAudio2Thread : CoInitializeEx() failed(0x%08x)", (u32)hr);
|
|
|
|
|
|
Emu.Pause();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-07-21 15:41:40 +02:00
|
|
|
|
hr = create(&s_tls_xaudio2_instance, 0, XAUDIO2_DEFAULT_PROCESSOR);
|
|
|
|
|
|
if (FAILED(hr))
|
|
|
|
|
|
{
|
|
|
|
|
|
LOG_ERROR(GENERAL, "XAudio2Thread : XAudio2Create() failed(0x%08x)", (u32)hr);
|
|
|
|
|
|
Emu.Pause();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-05-20 13:45:02 +02:00
|
|
|
|
hr = s_tls_xaudio2_instance->CreateMasteringVoice(&s_tls_master_voice, g_cfg.audio.downmix_to_2ch ? 2 : 8, 48000);
|
2016-07-21 15:41:40 +02:00
|
|
|
|
if (FAILED(hr))
|
|
|
|
|
|
{
|
|
|
|
|
|
LOG_ERROR(GENERAL, "XAudio2Thread : CreateMasteringVoice() failed(0x%08x)", (u32)hr);
|
|
|
|
|
|
s_tls_xaudio2_instance->Release();
|
|
|
|
|
|
Emu.Pause();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void XAudio2Thread::xa28_destroy()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (s_tls_source_voice != nullptr)
|
|
|
|
|
|
{
|
|
|
|
|
|
s_tls_source_voice->Stop();
|
|
|
|
|
|
s_tls_source_voice->DestroyVoice();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (s_tls_master_voice != nullptr)
|
|
|
|
|
|
{
|
|
|
|
|
|
s_tls_master_voice->DestroyVoice();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (s_tls_xaudio2_instance != nullptr)
|
|
|
|
|
|
{
|
|
|
|
|
|
s_tls_xaudio2_instance->StopEngine();
|
|
|
|
|
|
s_tls_xaudio2_instance->Release();
|
|
|
|
|
|
}
|
2016-07-25 18:30:21 +02:00
|
|
|
|
|
|
|
|
|
|
CoUninitialize();
|
2016-07-30 16:10:01 +02:00
|
|
|
|
|
|
|
|
|
|
FreeLibrary(s_tls_xaudio2_lib);
|
2016-07-21 15:41:40 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void XAudio2Thread::xa28_play()
|
|
|
|
|
|
{
|
2018-12-20 23:35:49 +01:00
|
|
|
|
AUDIT(s_tls_source_voice != nullptr);
|
|
|
|
|
|
|
2016-07-21 15:41:40 +02:00
|
|
|
|
HRESULT hr = s_tls_source_voice->Start();
|
|
|
|
|
|
if (FAILED(hr))
|
|
|
|
|
|
{
|
|
|
|
|
|
LOG_ERROR(GENERAL, "XAudio2Thread : Start() failed(0x%08x)", (u32)hr);
|
|
|
|
|
|
Emu.Pause();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void XAudio2Thread::xa28_flush()
|
|
|
|
|
|
{
|
2018-12-20 23:35:49 +01:00
|
|
|
|
AUDIT(s_tls_source_voice != nullptr);
|
|
|
|
|
|
|
2016-07-21 15:41:40 +02:00
|
|
|
|
HRESULT hr = s_tls_source_voice->FlushSourceBuffers();
|
|
|
|
|
|
if (FAILED(hr))
|
|
|
|
|
|
{
|
|
|
|
|
|
LOG_ERROR(GENERAL, "XAudio2Thread : FlushSourceBuffers() failed(0x%08x)", (u32)hr);
|
|
|
|
|
|
Emu.Pause();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void XAudio2Thread::xa28_stop()
|
|
|
|
|
|
{
|
2018-12-20 23:35:49 +01:00
|
|
|
|
AUDIT(s_tls_source_voice != nullptr);
|
|
|
|
|
|
|
2016-07-21 15:41:40 +02:00
|
|
|
|
HRESULT hr = s_tls_source_voice->Stop();
|
|
|
|
|
|
if (FAILED(hr))
|
|
|
|
|
|
{
|
|
|
|
|
|
LOG_ERROR(GENERAL, "XAudio2Thread : Stop() failed(0x%08x)", (u32)hr);
|
|
|
|
|
|
Emu.Pause();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-12-20 23:35:49 +01:00
|
|
|
|
bool XAudio2Thread::xa28_is_playing()
|
|
|
|
|
|
{
|
|
|
|
|
|
AUDIT(s_tls_source_voice != nullptr);
|
|
|
|
|
|
|
|
|
|
|
|
XAUDIO2_VOICE_STATE state;
|
|
|
|
|
|
s_tls_source_voice->GetState(&state, XAUDIO2_VOICE_NOSAMPLESPLAYED);
|
|
|
|
|
|
|
|
|
|
|
|
return state.BuffersQueued > 0 || state.pCurrentBufferContext != nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-07-21 15:41:40 +02:00
|
|
|
|
void XAudio2Thread::xa28_open()
|
|
|
|
|
|
{
|
|
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
2018-12-20 23:35:49 +01:00
|
|
|
|
const u32 sample_size = get_sample_size();
|
|
|
|
|
|
const u32 channels = get_channels();
|
|
|
|
|
|
const u32 sampling_rate = get_sampling_rate();
|
2016-07-21 15:41:40 +02:00
|
|
|
|
|
|
|
|
|
|
WAVEFORMATEX waveformatex;
|
2017-05-20 13:45:02 +02:00
|
|
|
|
waveformatex.wFormatTag = g_cfg.audio.convert_to_u16 ? WAVE_FORMAT_PCM : WAVE_FORMAT_IEEE_FLOAT;
|
2016-07-21 15:41:40 +02:00
|
|
|
|
waveformatex.nChannels = channels;
|
2018-12-20 23:35:49 +01:00
|
|
|
|
waveformatex.nSamplesPerSec = sampling_rate;
|
|
|
|
|
|
waveformatex.nAvgBytesPerSec = static_cast<DWORD>(sampling_rate * channels * sample_size);
|
2016-07-21 15:41:40 +02:00
|
|
|
|
waveformatex.nBlockAlign = channels * sample_size;
|
|
|
|
|
|
waveformatex.wBitsPerSample = sample_size * 8;
|
|
|
|
|
|
waveformatex.cbSize = 0;
|
|
|
|
|
|
|
|
|
|
|
|
hr = s_tls_xaudio2_instance->CreateSourceVoice(&s_tls_source_voice, &waveformatex, 0, XAUDIO2_DEFAULT_FREQ_RATIO);
|
|
|
|
|
|
if (FAILED(hr))
|
|
|
|
|
|
{
|
|
|
|
|
|
LOG_ERROR(GENERAL, "XAudio2Thread : CreateSourceVoice() failed(0x%08x)", (u32)hr);
|
|
|
|
|
|
Emu.Pause();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-12-20 23:35:49 +01:00
|
|
|
|
AUDIT(s_tls_source_voice != nullptr);
|
|
|
|
|
|
s_tls_source_voice->SetVolume(channels == 2 ? 1.0 : 4.0);
|
2016-07-21 15:41:40 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-12-20 23:35:49 +01:00
|
|
|
|
bool XAudio2Thread::xa28_add(const void* src, int size)
|
2016-07-21 15:41:40 +02:00
|
|
|
|
{
|
2018-12-20 23:35:49 +01:00
|
|
|
|
AUDIT(s_tls_source_voice != nullptr);
|
|
|
|
|
|
|
2016-08-12 21:33:13 +02:00
|
|
|
|
XAUDIO2_VOICE_STATE state;
|
2018-12-20 23:35:49 +01:00
|
|
|
|
s_tls_source_voice->GetState(&state, XAUDIO2_VOICE_NOSAMPLESPLAYED);
|
2016-08-12 21:33:13 +02:00
|
|
|
|
|
2018-12-20 23:35:49 +01:00
|
|
|
|
if (state.BuffersQueued >= MAX_AUDIO_BUFFERS)
|
2016-08-12 21:33:13 +02:00
|
|
|
|
{
|
|
|
|
|
|
LOG_WARNING(GENERAL, "XAudio2Thread : too many buffers enqueued (%d, pos=%u)", state.BuffersQueued, state.SamplesPlayed);
|
2018-12-20 23:35:49 +01:00
|
|
|
|
return false;
|
2016-08-12 21:33:13 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-07-21 15:41:40 +02:00
|
|
|
|
XAUDIO2_BUFFER buffer;
|
|
|
|
|
|
|
2018-12-20 23:35:49 +01:00
|
|
|
|
buffer.AudioBytes = size * get_sample_size();
|
2016-07-21 15:41:40 +02:00
|
|
|
|
buffer.Flags = 0;
|
|
|
|
|
|
buffer.LoopBegin = XAUDIO2_NO_LOOP_REGION;
|
|
|
|
|
|
buffer.LoopCount = 0;
|
|
|
|
|
|
buffer.LoopLength = 0;
|
|
|
|
|
|
buffer.pAudioData = (const BYTE*)src;
|
|
|
|
|
|
buffer.pContext = 0;
|
|
|
|
|
|
buffer.PlayBegin = 0;
|
2018-12-20 23:35:49 +01:00
|
|
|
|
buffer.PlayLength = AUDIO_BUFFER_SAMPLES;
|
2016-07-21 15:41:40 +02:00
|
|
|
|
|
|
|
|
|
|
HRESULT hr = s_tls_source_voice->SubmitSourceBuffer(&buffer);
|
|
|
|
|
|
if (FAILED(hr))
|
|
|
|
|
|
{
|
|
|
|
|
|
LOG_ERROR(GENERAL, "XAudio2Thread : AddData() failed(0x%08x)", (u32)hr);
|
|
|
|
|
|
Emu.Pause();
|
|
|
|
|
|
}
|
2018-12-20 23:35:49 +01:00
|
|
|
|
|
|
|
|
|
|
return true;
|
2016-07-21 15:41:40 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|