diff --git a/src/wx/CMakeLists.txt b/src/wx/CMakeLists.txt index 4b961c36..6cf1b787 100644 --- a/src/wx/CMakeLists.txt +++ b/src/wx/CMakeLists.txt @@ -6,6 +6,10 @@ endif() include(VbamFunctions) set(VBAM_WX_COMMON + audio/audio.cpp + audio/audio.h + audio/internal/openal.cpp + audio/internal/openal.h background-input.cpp background-input.h cmdevents.cpp @@ -47,8 +51,6 @@ set(VBAM_WX_COMMON gfxviewers.cpp guiinit.cpp ioregs.h - openal.cpp - openal.h opts.cpp opts.h panel.cpp @@ -249,7 +251,7 @@ endif() if(WIN32) target_sources(visualboyadvance-m PRIVATE - dsound.cpp + audio/internal/dsound.cpp wxvbam.rc ) target_link_libraries(visualboyadvance-m @@ -313,7 +315,7 @@ target_link_libraries(visualboyadvance-m ${OPENAL_LIBRARY}) # XAudio2. if(ENABLE_XAUDIO2) - target_sources(visualboyadvance-m PRIVATE xaudio2.cpp) + target_sources(visualboyadvance-m PRIVATE audio/internal/xaudio2.cpp) target_compile_definitions(visualboyadvance-m PRIVATE VBAM_ENABLE_XAUDIO2) endif() @@ -324,7 +326,7 @@ endif() # FAudio. if(ENABLE_FAUDIO) - target_sources(visualboyadvance-m PRIVATE faudio.cpp) + target_sources(visualboyadvance-m PRIVATE audio/internal/faudio.cpp) target_link_libraries(visualboyadvance-m FAudio::FAudio) target_compile_definitions(visualboyadvance-m PRIVATE VBAM_ENABLE_FAUDIO) endif() @@ -607,6 +609,9 @@ add_custom_command( set(VBAM_LOCALIZABLE_FILES ${VBAM_WX_COMMON}) list(APPEND VBAM_LOCALIZABLE_FILES + audio/internal/dsound.cpp + audio/internal/faudio.cpp + audio/internal/xaudio2.cpp autoupdater/autoupdater.h autoupdater/macos/autoupdater.cpp autoupdater/macos/sparkle-wrapper.h @@ -614,11 +619,8 @@ list(APPEND VBAM_LOCALIZABLE_FILES autoupdater/wxmsw/winsparkle-rc.h autoupdater/wxmsw/winsparkle-wrapper.cpp autoupdater/wxmsw/winsparkle-wrapper.h - dsound.cpp - faudio.cpp widgets/dpi-support.cpp widgets/dpi-support-mac.mm - xaudio2.cpp ${CMAKE_SOURCE_DIR}/src/core/gba/gbaLink.cpp ) diff --git a/src/wx/audio/audio.cpp b/src/wx/audio/audio.cpp new file mode 100644 index 00000000..208dad20 --- /dev/null +++ b/src/wx/audio/audio.cpp @@ -0,0 +1,76 @@ +#include "wx/audio/audio.h" + +#include "wx/audio/internal/openal.h" + +#if defined(__WXMSW__) +#include "wx/audio/internal/dsound.h" +#endif + + +#if defined(VBAM_ENABLE_FAUDIO) +#include "wx/audio/internal/faudio.h" +#endif + +#if defined(VBAM_ENABLE_XAUDIO2) +#include "wx/audio/internal/xaudio2.h" +#endif + +namespace audio { + +std::vector EnumerateAudioDevices(const config::AudioApi& audio_api) { + switch (audio_api) { + case config::AudioApi::kOpenAL: + return audio::internal::GetOpenALDevices(); + +#if defined(__WXMSW__) + case config::AudioApi::kDirectSound: + return audio::internal::GetDirectSoundDevices(); +#endif + +#if defined(VBAM_ENABLE_XAUDIO2) + case config::AudioApi::kXAudio2: + return audio::internal::GetXAudio2Devices(); +#endif + +#if defined(VBAM_ENABLE_FAUDIO) + case config::AudioApi::kFAudio: + return audio::internal::GetFAudioDevices(); +#endif + + case config::AudioApi::kLast: + default: + // This should never happen. + assert(false); + return {}; + } +} + +std::unique_ptr CreateSoundDriver(const config::AudioApi& api) { + switch (api) { + case config::AudioApi::kOpenAL: + return audio::internal::CreateOpenALDriver(); + +#if defined(__WXMSW__) + case config::AudioApi::kDirectSound: + return audio::internal::CreateDirectSoundDriver(); +#endif + +#if defined(VBAM_ENABLE_XAUDIO2) + case config::AudioApi::kXAudio2: + return audio::internal::CreateXAudio2Driver(); +#endif + +#if defined(VBAM_ENABLE_FAUDIO) + case config::AudioApi::kFAudio: + return audio::internal::CreateFAudioDriver(); +#endif + + case config::AudioApi::kLast: + default: + // This should never happen. + assert(false); + return nullptr; + } +} + +} // namespace audio diff --git a/src/wx/audio/audio.h b/src/wx/audio/audio.h new file mode 100644 index 00000000..c4937c49 --- /dev/null +++ b/src/wx/audio/audio.h @@ -0,0 +1,30 @@ +#ifndef WX_AUDIO_AUDIO_H_ +#define WX_AUDIO_AUDIO_H_ + +#include +#include + +#include + +#include "core/base/sound_driver.h" +#include "wx/config/option.h" + +namespace audio { + +// Represents an audio device. +struct AudioDevice { + // The device user-friendly name. + wxString name; + // The underlying device ID. + wxString id; +}; + +// Returns the set of audio devices for the given API. +std::vector EnumerateAudioDevices(const config::AudioApi& api); + +// Creates a sound driver for the given API. +std::unique_ptr CreateSoundDriver(const config::AudioApi& api); + +} // namespace audio + +#endif // WX_AUDIO_AUDIO_H_ diff --git a/src/wx/dsound.cpp b/src/wx/audio/internal/dsound.cpp similarity index 77% rename from src/wx/dsound.cpp rename to src/wx/audio/internal/dsound.cpp index 3b54f416..8cccf72c 100644 --- a/src/wx/dsound.cpp +++ b/src/wx/audio/internal/dsound.cpp @@ -2,6 +2,8 @@ #error "This file should only be compiled on Windows" #endif +#include "wx/audio/internal/dsound.h" + // DirectSound8 #define DIRECTSOUND_VERSION 0x0800 #include @@ -24,14 +26,19 @@ extern bool soundBufferLow; +namespace audio { +namespace internal { + +namespace { + class DirectSound : public SoundDriver { private: - LPDIRECTSOUND8 pDirectSound; // DirectSound interface - LPDIRECTSOUNDBUFFER dsbPrimary; // Primary DirectSound buffer - LPDIRECTSOUNDBUFFER dsbSecondary; // Secondary DirectSound buffer + LPDIRECTSOUND8 pDirectSound; // DirectSound interface + LPDIRECTSOUNDBUFFER dsbPrimary; // Primary DirectSound buffer + LPDIRECTSOUNDBUFFER dsbSecondary; // Secondary DirectSound buffer LPDIRECTSOUNDNOTIFY dsbNotify; HANDLE dsbEvent; - WAVEFORMATEX wfx; // Primary buffer wave format + WAVEFORMATEX wfx; // Primary buffer wave format int soundBufferLen; int soundBufferTotalLen; unsigned int soundNextPosition; @@ -45,12 +52,11 @@ public: void pause() override; void reset() override; void resume() override; - void write(uint16_t *finalWave, int length) override; + void write(uint16_t* finalWave, int length) override; void setThrottle(unsigned short throttle_) override; }; -DirectSound::DirectSound() -{ +DirectSound::DirectSound() { pDirectSound = NULL; dsbPrimary = NULL; dsbSecondary = NULL; @@ -60,8 +66,7 @@ DirectSound::DirectSound() soundNextPosition = 0; } -DirectSound::~DirectSound() -{ +DirectSound::~DirectSound() { if (dsbNotify) { dsbNotify->Release(); dsbNotify = NULL; @@ -88,13 +93,13 @@ DirectSound::~DirectSound() } } -bool DirectSound::init(long sampleRate) -{ +bool DirectSound::init(long sampleRate) { HRESULT hr; DWORD freq; DSBUFFERDESC dsbdesc; int i; - hr = CoCreateInstance(CLSID_DirectSound8, NULL, CLSCTX_INPROC_SERVER, IID_IDirectSound8, (LPVOID*)&pDirectSound); + hr = CoCreateInstance(CLSID_DirectSound8, NULL, CLSCTX_INPROC_SERVER, IID_IDirectSound8, + (LPVOID*)&pDirectSound); if (hr != S_OK) { wxLogError(_("Cannot create Direct Sound %08x"), hr); @@ -116,7 +121,8 @@ bool DirectSound::init(long sampleRate) return false; } - if (FAILED(hr = pDirectSound->SetCooperativeLevel((HWND)wxGetApp().frame->GetHandle(), DSSCL_PRIORITY))) { + if (FAILED(hr = pDirectSound->SetCooperativeLevel((HWND)wxGetApp().frame->GetHandle(), + DSSCL_PRIORITY))) { wxLogError(_("Cannot SetCooperativeLevel %08x"), hr); return false; } @@ -158,7 +164,8 @@ bool DirectSound::init(long sampleRate) // Create secondary sound buffer ZeroMemory(&dsbdesc, sizeof(DSBUFFERDESC)); dsbdesc.dwSize = sizeof(DSBUFFERDESC); - dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLFREQUENCY; + dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY | + DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLFREQUENCY; if (!hw_accel) { dsbdesc.dwFlags |= DSBCAPS_LOCSOFTWARE; @@ -177,7 +184,8 @@ bool DirectSound::init(long sampleRate) return false; } - if (SUCCEEDED(hr = dsbSecondary->QueryInterface(IID_IDirectSoundNotify8, (LPVOID*)&dsbNotify))) { + if (SUCCEEDED(hr = + dsbSecondary->QueryInterface(IID_IDirectSoundNotify8, (LPVOID*)&dsbNotify))) { dsbEvent = CreateEvent(NULL, FALSE, FALSE, NULL); DSBPOSITIONNOTIFY notify[10]; @@ -207,7 +215,7 @@ void DirectSound::setThrottle(unsigned short throttle_) { HRESULT hr; if (throttle_ == 0) - throttle_ = 450; // Close to upper bound on frequency. + throttle_ = 450; // Close to upper bound on frequency. long freq = soundGetSampleRate(); @@ -216,8 +224,7 @@ void DirectSound::setThrottle(unsigned short throttle_) { } } -void DirectSound::pause() -{ +void DirectSound::pause() { LPDIRECTSOUNDBUFFER bufs[] = {dsbPrimary, dsbSecondary}; for (auto buf : bufs) { if (buf == NULL) @@ -231,8 +238,7 @@ void DirectSound::pause() } } -void DirectSound::reset() -{ +void DirectSound::reset() { if (dsbSecondary == NULL) return; @@ -241,8 +247,7 @@ void DirectSound::reset() soundNextPosition = 0; } -void DirectSound::resume() -{ +void DirectSound::resume() { LPDIRECTSOUNDBUFFER bufs[] = {dsbPrimary, dsbSecondary}; for (auto buf : bufs) { if (buf == NULL) @@ -252,8 +257,7 @@ void DirectSound::resume() } } -void DirectSound::write(uint16_t* finalWave, int) -{ +void DirectSound::write(uint16_t* finalWave, int) { if (!pDirectSound) return; @@ -272,7 +276,9 @@ void DirectSound::write(uint16_t* finalWave, int) if (!soundPaused) { while (true) { dsbSecondary->GetCurrentPosition(&play, NULL); - int BufferLeft = ((soundNextPosition <= play) ? play - soundNextPosition : soundBufferTotalLen - soundNextPosition + play); + int BufferLeft = ((soundNextPosition <= play) + ? play - soundNextPosition + : soundBufferTotalLen - soundNextPosition + play); if (BufferLeft > soundBufferLen) { if (BufferLeft > soundBufferTotalLen - (soundBufferLen * 3)) @@ -297,24 +303,12 @@ void DirectSound::write(uint16_t* finalWave, int) // Obtain memory address of write block. // This will be in two parts if the block wraps around. - if (DSERR_BUFFERLOST == (hr = dsbSecondary->Lock( - soundNextPosition, - soundBufferLen, - &lpvPtr1, - &dwBytes1, - &lpvPtr2, - &dwBytes2, - 0))) { + if (DSERR_BUFFERLOST == (hr = dsbSecondary->Lock(soundNextPosition, soundBufferLen, &lpvPtr1, + &dwBytes1, &lpvPtr2, &dwBytes2, 0))) { // If DSERR_BUFFERLOST is returned, restore and retry lock. dsbSecondary->Restore(); - hr = dsbSecondary->Lock( - soundNextPosition, - soundBufferLen, - &lpvPtr1, - &dwBytes1, - &lpvPtr2, - &dwBytes2, - 0); + hr = dsbSecondary->Lock(soundNextPosition, soundBufferLen, &lpvPtr1, &dwBytes1, &lpvPtr2, + &dwBytes2, 0); } soundNextPosition += soundBufferLen; @@ -336,27 +330,33 @@ void DirectSound::write(uint16_t* finalWave, int) } } -std::unique_ptr newDirectSound() -{ - return std::make_unique(); -} +static BOOL CALLBACK DSEnumCB(LPGUID guid, LPCTSTR desc, LPCTSTR /*module*/, LPVOID user) { + std::vector* devices = static_cast*>(user); -struct devnames { - wxArrayString *names, *ids; -}; + if (guid == nullptr) { + devices->push_back({desc, {}}); + return TRUE; + } -static BOOL CALLBACK DSEnumCB(LPGUID guid, LPCTSTR desc, LPCTSTR, LPVOID user) -{ - devnames* dn = (devnames*)user; - dn->names->push_back(desc); - WCHAR buf[32 + 4 + 2 + 1]; // hex digits + "-" + "{}" + \0 - StringFromGUID2(*guid, buf, sizeof(buf)); - dn->ids->push_back(buf); + static constexpr size_t kGuidLength = 32 + 4 + 2 + 1; // hex digits + "-" + "{}" + \0 + std::array device_id; + StringFromGUID2(*guid, device_id.data(), device_id.size()); + + devices->push_back({desc, device_id.data()}); return TRUE; } -bool GetDSDevices(wxArrayString& names, wxArrayString& ids) -{ - devnames dn = { &names, &ids }; - return DirectSoundEnumerate(DSEnumCB, (LPVOID)&dn) == DS_OK; +} // namespace + +std::vector GetDirectSoundDevices() { + std::vector devices; + DirectSoundEnumerateW(DSEnumCB, &devices); + return devices; } + +std::unique_ptr CreateDirectSoundDriver() { + return std::make_unique(); +} + +} // namespace internal +} // namespace audio diff --git a/src/wx/audio/internal/dsound.h b/src/wx/audio/internal/dsound.h new file mode 100644 index 00000000..a9e83ca4 --- /dev/null +++ b/src/wx/audio/internal/dsound.h @@ -0,0 +1,22 @@ +#ifndef WX_AUDIO_INTERNAL_DSOUND_H_ +#define WX_AUDIO_INTERNAL_DSOUND_H_ + +#if !defined(__WXMSW__) +#error "This file should only be included on Windows" +#endif + +#include "wx/audio/audio.h" + +namespace audio { +namespace internal { + +// Returns the set of DirectSound devices. +std::vector GetDirectSoundDevices(); + +// Creates a DirectSound sound driver. +std::unique_ptr CreateDirectSoundDriver(); + +} // namespace internal +} // namespace audio + +#endif // WX_AUDIO_INTERNAL_DSOUND_H_ diff --git a/src/wx/faudio.cpp b/src/wx/audio/internal/faudio.cpp similarity index 69% rename from src/wx/faudio.cpp rename to src/wx/audio/internal/faudio.cpp index a46b5178..e0fe31c0 100644 --- a/src/wx/faudio.cpp +++ b/src/wx/audio/internal/faudio.cpp @@ -1,10 +1,12 @@ +#include #if !defined(VBAM_ENABLE_FAUDIO) #error "This file should only be compiled if FAudio is enabled" #endif -#include +#include "wx/audio/internal/faudio.h" + +#include -#include #include // FAudio @@ -16,63 +18,50 @@ #include #include -#include "core/base/sound_driver.h" #include "core/base/system.h" #include "core/gba/gbaGlobals.h" #include "wx/config/option-proxy.h" +namespace audio { +namespace internal { + namespace { -int GetFADevices(FAudio* fa, wxArrayString* names, wxArrayString* ids, - const wxString* match) -{ +int FAGetDev(FAudio* fa) { + const wxString& audio_device = OPTION(kSoundAudioDevice); + if (audio_device.empty()) { + // Just use the default device. + return 0; + } + uint32_t hr; uint32_t dev_count = 0; hr = FAudio_GetDeviceCount(fa, &dev_count); - if (hr != 0) { wxLogError(_("FAudio: Enumerating devices failed!")); - return -1; - } else { - FAudioDeviceDetails dd; + return 0; + } - for (uint32_t i = 0; i < dev_count; i++) { - hr = FAudio_GetDeviceDetails(fa, i, &dd); - - if (hr != 0) { - continue; - } else { - if (ids) { - ids->push_back((wchar_t*) dd.DeviceID); - names->push_back((wchar_t*) dd.DisplayName); - } else if (*match == wxString((wchar_t*) dd.DeviceID)) - return i; - } + FAudioDeviceDetails dd; + for (uint32_t i = 0; i < dev_count; i++) { + hr = FAudio_GetDeviceDetails(fa, i, &dd); + if (hr != 0) { + continue; + } + const wxString device_id(reinterpret_cast(dd.DeviceID)); + if (audio_device == device_id) { + return i; } } - return -1; -} - -int FAGetDev(FAudio* fa) -{ - const wxString& audio_device = OPTION(kSoundAudioDevice); - if (audio_device.empty()) - return 0; - else { - int ret = GetFADevices(fa, nullptr, nullptr, &audio_device); - return ret < 0 ? 0 : ret; - } + return 0; } class FAudio_BufferNotify : public FAudioVoiceCallback { public: - bool WaitForSignal() { - return WaitForSingleObject(buffer_end_event_, 10000) != WAIT_TIMEOUT; - } + bool WaitForSignal() { return WaitForSingleObject(buffer_end_event_, 10000) != WAIT_TIMEOUT; } - FAudio_BufferNotify() - { + FAudio_BufferNotify() { buffer_end_event_ = nullptr; buffer_end_event_ = CreateEvent(nullptr, FALSE, FALSE, nullptr); assert(buffer_end_event_ != nullptr); @@ -85,8 +74,7 @@ public: OnLoopEnd = &FAudio_BufferNotify::StaticOnLoopEnd; OnVoiceError = &FAudio_BufferNotify::StaticOnVoiceError; } - ~FAudio_BufferNotify() - { + ~FAudio_BufferNotify() { CloseHandle(buffer_end_event_); buffer_end_event_ = nullptr; } @@ -108,8 +96,7 @@ private: static void StaticOnVoiceError(FAudioVoiceCallback*, void*, uint32_t) {} }; -class FAudio_Output - : public SoundDriver { +class FAudio_Output : public SoundDriver { public: FAudio_Output(); ~FAudio_Output(); @@ -139,11 +126,11 @@ private: volatile bool device_changed; FAudio* faud; - FAudioMasteringVoice* mVoice; // listener - FAudioSourceVoice* sVoice; // sound source + FAudioMasteringVoice* mVoice; // listener + FAudioSourceVoice* sVoice; // sound source FAudioBuffer buf; FAudioVoiceState vState; - FAudio_BufferNotify notify; // buffer end notification + FAudio_BufferNotify notify; // buffer end notification }; class FAudio_Device_Notifier : public IMMNotificationClient { @@ -162,7 +149,9 @@ public: ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID** ppvInterface); - HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId); + HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, + ERole role, + LPCWSTR pwstrDeviceId); HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId); HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId); HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState); @@ -190,14 +179,12 @@ FAudio_Output::FAudio_Output() : buffer_count_(OPTION(kSoundBuffers)) { f_notifier.do_register(this); } -FAudio_Output::~FAudio_Output() -{ +FAudio_Output::~FAudio_Output() { f_notifier.do_unregister(this); close(); } -void FAudio_Output::close() -{ +void FAudio_Output::close() { initialized = false; if (sVoice) { @@ -220,22 +207,20 @@ void FAudio_Output::close() } } -void FAudio_Output::device_change() -{ +void FAudio_Output::device_change() { device_changed = true; } -bool FAudio_Output::init(long sampleRate) -{ +bool FAudio_Output::init(long sampleRate) { if (failed || initialized) return false; uint32_t hr; // Initialize FAudio uint32_t flags = 0; - //#ifdef _DEBUG + // #ifdef _DEBUG // flags = FAUDIO_DEBUG_ENGINE; - //#endif + // #endif hr = FAudioCreate(&faud, flags, FAUDIO_DEFAULT_PROCESSOR); if (hr != 0) { @@ -255,8 +240,8 @@ bool FAudio_Output::init(long sampleRate) static const uint16_t kNumChannels = 2; static const uint16_t kBitsPerSample = 16; static const uint16_t kBlockAlign = kNumChannels * (kBitsPerSample / 8); - FAudioWaveFormatEx wfx { - /*.wFormatTag=*/ FAUDIO_FORMAT_PCM, + FAudioWaveFormatEx wfx{ + /*.wFormatTag=*/FAUDIO_FORMAT_PCM, /*.nChannels=*/kNumChannels, /*.nSamplesPerSec=*/freq_, /*.nAvgBytesPerSec=*/freq_ * kBlockAlign, @@ -286,96 +271,96 @@ bool FAudio_Output::init(long sampleRate) if (OPTION(kSoundUpmix)) { // set up stereo upmixing - FAudioDeviceDetails dd {}; + FAudioDeviceDetails dd{}; assert(FAudio_GetDeviceDetails(faud, 0, &dd) == 0); std::vector matrix(sizeof(float) * 2 * dd.OutputFormat.Format.nChannels); bool matrixAvailable = true; switch (dd.OutputFormat.Format.nChannels) { - case 4: // 4.0 - //Speaker \ Left Source Right Source - /*Front L*/ matrix[0] = 1.0000f; - matrix[1] = 0.0000f; - /*Front R*/ matrix[2] = 0.0000f; - matrix[3] = 1.0000f; - /*Back L*/ matrix[4] = 1.0000f; - matrix[5] = 0.0000f; - /*Back R*/ matrix[6] = 0.0000f; - matrix[7] = 1.0000f; - break; + case 4: // 4.0 + // Speaker \ Left Source Right Source + /*Front L*/ matrix[0] = 1.0000f; + matrix[1] = 0.0000f; + /*Front R*/ matrix[2] = 0.0000f; + matrix[3] = 1.0000f; + /*Back L*/ matrix[4] = 1.0000f; + matrix[5] = 0.0000f; + /*Back R*/ matrix[6] = 0.0000f; + matrix[7] = 1.0000f; + break; - case 5: // 5.0 - //Speaker \ Left Source Right Source - /*Front L*/ matrix[0] = 1.0000f; - matrix[1] = 0.0000f; - /*Front R*/ matrix[2] = 0.0000f; - matrix[3] = 1.0000f; - /*Front C*/ matrix[4] = 0.7071f; - matrix[5] = 0.7071f; - /*Side L*/ matrix[6] = 1.0000f; - matrix[7] = 0.0000f; - /*Side R*/ matrix[8] = 0.0000f; - matrix[9] = 1.0000f; - break; + case 5: // 5.0 + // Speaker \ Left Source Right Source + /*Front L*/ matrix[0] = 1.0000f; + matrix[1] = 0.0000f; + /*Front R*/ matrix[2] = 0.0000f; + matrix[3] = 1.0000f; + /*Front C*/ matrix[4] = 0.7071f; + matrix[5] = 0.7071f; + /*Side L*/ matrix[6] = 1.0000f; + matrix[7] = 0.0000f; + /*Side R*/ matrix[8] = 0.0000f; + matrix[9] = 1.0000f; + break; - case 6: // 5.1 - //Speaker \ Left Source Right Source - /*Front L*/ matrix[0] = 1.0000f; - matrix[1] = 0.0000f; - /*Front R*/ matrix[2] = 0.0000f; - matrix[3] = 1.0000f; - /*Front C*/ matrix[4] = 0.7071f; - matrix[5] = 0.7071f; - /*LFE */ matrix[6] = 0.0000f; - matrix[7] = 0.0000f; - /*Side L*/ matrix[8] = 1.0000f; - matrix[9] = 0.0000f; - /*Side R*/ matrix[10] = 0.0000f; - matrix[11] = 1.0000f; - break; + case 6: // 5.1 + // Speaker \ Left Source Right Source + /*Front L*/ matrix[0] = 1.0000f; + matrix[1] = 0.0000f; + /*Front R*/ matrix[2] = 0.0000f; + matrix[3] = 1.0000f; + /*Front C*/ matrix[4] = 0.7071f; + matrix[5] = 0.7071f; + /*LFE */ matrix[6] = 0.0000f; + matrix[7] = 0.0000f; + /*Side L*/ matrix[8] = 1.0000f; + matrix[9] = 0.0000f; + /*Side R*/ matrix[10] = 0.0000f; + matrix[11] = 1.0000f; + break; - case 7: // 6.1 - //Speaker \ Left Source Right Source - /*Front L*/ matrix[0] = 1.0000f; - matrix[1] = 0.0000f; - /*Front R*/ matrix[2] = 0.0000f; - matrix[3] = 1.0000f; - /*Front C*/ matrix[4] = 0.7071f; - matrix[5] = 0.7071f; - /*LFE */ matrix[6] = 0.0000f; - matrix[7] = 0.0000f; - /*Side L*/ matrix[8] = 1.0000f; - matrix[9] = 0.0000f; - /*Side R*/ matrix[10] = 0.0000f; - matrix[11] = 1.0000f; - /*Back C*/ matrix[12] = 0.7071f; - matrix[13] = 0.7071f; - break; + case 7: // 6.1 + // Speaker \ Left Source Right Source + /*Front L*/ matrix[0] = 1.0000f; + matrix[1] = 0.0000f; + /*Front R*/ matrix[2] = 0.0000f; + matrix[3] = 1.0000f; + /*Front C*/ matrix[4] = 0.7071f; + matrix[5] = 0.7071f; + /*LFE */ matrix[6] = 0.0000f; + matrix[7] = 0.0000f; + /*Side L*/ matrix[8] = 1.0000f; + matrix[9] = 0.0000f; + /*Side R*/ matrix[10] = 0.0000f; + matrix[11] = 1.0000f; + /*Back C*/ matrix[12] = 0.7071f; + matrix[13] = 0.7071f; + break; - case 8: // 7.1 - //Speaker \ Left Source Right Source - /*Front L*/ matrix[0] = 1.0000f; - matrix[1] = 0.0000f; - /*Front R*/ matrix[2] = 0.0000f; - matrix[3] = 1.0000f; - /*Front C*/ matrix[4] = 0.7071f; - matrix[5] = 0.7071f; - /*LFE */ matrix[6] = 0.0000f; - matrix[7] = 0.0000f; - /*Back L*/ matrix[8] = 1.0000f; - matrix[9] = 0.0000f; - /*Back R*/ matrix[10] = 0.0000f; - matrix[11] = 1.0000f; - /*Side L*/ matrix[12] = 1.0000f; - matrix[13] = 0.0000f; - /*Side R*/ matrix[14] = 0.0000f; - matrix[15] = 1.0000f; - break; + case 8: // 7.1 + // Speaker \ Left Source Right Source + /*Front L*/ matrix[0] = 1.0000f; + matrix[1] = 0.0000f; + /*Front R*/ matrix[2] = 0.0000f; + matrix[3] = 1.0000f; + /*Front C*/ matrix[4] = 0.7071f; + matrix[5] = 0.7071f; + /*LFE */ matrix[6] = 0.0000f; + matrix[7] = 0.0000f; + /*Back L*/ matrix[8] = 1.0000f; + matrix[9] = 0.0000f; + /*Back R*/ matrix[10] = 0.0000f; + matrix[11] = 1.0000f; + /*Side L*/ matrix[12] = 1.0000f; + matrix[13] = 0.0000f; + /*Side R*/ matrix[14] = 0.0000f; + matrix[15] = 1.0000f; + break; - default: - matrixAvailable = false; - break; + default: + matrixAvailable = false; + break; } if (matrixAvailable) { @@ -445,8 +430,7 @@ void FAudio_Output::write(uint16_t* finalWave, int) { assert(hr == 0); } -void FAudio_Output::pause() -{ +void FAudio_Output::pause() { if (!initialized || failed) return; @@ -457,8 +441,7 @@ void FAudio_Output::pause() } } -void FAudio_Output::resume() -{ +void FAudio_Output::resume() { if (!initialized || failed) return; @@ -469,8 +452,7 @@ void FAudio_Output::resume() } } -void FAudio_Output::reset() -{ +void FAudio_Output::reset() { if (!initialized || failed) return; @@ -484,8 +466,7 @@ void FAudio_Output::reset() playing = true; } -void FAudio_Output::setThrottle(unsigned short throttle_) -{ +void FAudio_Output::setThrottle(unsigned short throttle_) { if (!initialized || failed) return; @@ -497,28 +478,22 @@ void FAudio_Output::setThrottle(unsigned short throttle_) assert(hr == 0); } -FAudio_Device_Notifier::FAudio_Device_Notifier() - : registered(0) -{ +FAudio_Device_Notifier::FAudio_Device_Notifier() : registered(0) { InitializeCriticalSection(&lock); } -FAudio_Device_Notifier::~FAudio_Device_Notifier() -{ +FAudio_Device_Notifier::~FAudio_Device_Notifier() { DeleteCriticalSection(&lock); } -ULONG STDMETHODCALLTYPE FAudio_Device_Notifier::AddRef() -{ +ULONG STDMETHODCALLTYPE FAudio_Device_Notifier::AddRef() { return 1; } -ULONG STDMETHODCALLTYPE FAudio_Device_Notifier::Release() -{ +ULONG STDMETHODCALLTYPE FAudio_Device_Notifier::Release() { return 1; } -HRESULT STDMETHODCALLTYPE FAudio_Device_Notifier::QueryInterface(REFIID riid, VOID** ppvInterface) -{ +HRESULT STDMETHODCALLTYPE FAudio_Device_Notifier::QueryInterface(REFIID riid, VOID** ppvInterface) { if (IID_IUnknown == riid) { *ppvInterface = (IUnknown*)this; } else if (__uuidof(IMMNotificationClient) == riid) { @@ -562,8 +537,7 @@ HRESULT STDMETHODCALLTYPE FAudio_Device_Notifier::OnPropertyValueChanged(LPCWSTR return S_OK; } -void FAudio_Device_Notifier::do_register(FAudio_Output* p_instance) -{ +void FAudio_Device_Notifier::do_register(FAudio_Output* p_instance) { if (InterlockedIncrement(®istered) == 1) { pEnumerator = nullptr; HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, @@ -579,8 +553,7 @@ void FAudio_Device_Notifier::do_register(FAudio_Output* p_instance) LeaveCriticalSection(&lock); } -void FAudio_Device_Notifier::do_unregister(FAudio_Output* p_instance) -{ +void FAudio_Device_Notifier::do_unregister(FAudio_Output* p_instance) { if (InterlockedDecrement(®istered) == 0) { if (pEnumerator) { pEnumerator->UnregisterEndpointNotificationCallback(this); @@ -601,13 +574,11 @@ void FAudio_Device_Notifier::do_unregister(FAudio_Output* p_instance) LeaveCriticalSection(&lock); } - } // namespace -bool GetFADevices(wxArrayString& names, wxArrayString& ids) -{ - uint32_t hr; +std::vector GetFAudioDevices() { FAudio* fa = nullptr; + uint32_t hr; uint32_t flags = 0; #ifdef _DEBUG flags = FAUDIO_DEBUG_ENGINE; @@ -616,15 +587,40 @@ bool GetFADevices(wxArrayString& names, wxArrayString& ids) if (hr != 0) { wxLogError(_("The FAudio interface failed to initialize!")); - return false; + return {}; + } + + uint32_t dev_count = 0; + hr = FAudio_GetDeviceCount(fa, &dev_count); + if (hr != 0) { + wxLogError(_("FAudio: Enumerating devices failed!")); + return {}; + } + + std::vector devices; + devices.reserve(dev_count + 1); + devices.push_back({_("Default device"), wxEmptyString}); + + for (uint32_t i = 0; i < dev_count; i++) { + FAudioDeviceDetails dd; + hr = FAudio_GetDeviceDetails(fa, i, &dd); + if (hr != 0) { + continue; + } + + const wxString display_name(reinterpret_cast(dd.DisplayName)); + const wxString device_id(reinterpret_cast(dd.DeviceID)); + + devices.push_back({display_name, device_id}); } - GetFADevices(fa, &names, &ids, nullptr); FAudio_Release(fa); - return true; + return devices; } -// Class Declaration -std::unique_ptr newFAudio_Output() { +std::unique_ptr CreateFAudioDriver() { return std::make_unique(); } + +} // namespace internal +} // namespace audio \ No newline at end of file diff --git a/src/wx/audio/internal/faudio.h b/src/wx/audio/internal/faudio.h new file mode 100644 index 00000000..744963dd --- /dev/null +++ b/src/wx/audio/internal/faudio.h @@ -0,0 +1,22 @@ +#ifndef WX_AUDIO_INTERNAL_FAUDIO_H_ +#define WX_AUDIO_INTERNAL_FAUDIO_H_ + +#if !defined(VBAM_ENABLE_FAUDIO) +#error "This file should only be included if FAudio is enabled" +#endif + +#include "wx/audio/audio.h" + +namespace audio { +namespace internal { + +// Returns the set of FAudio devices. +std::vector GetFAudioDevices(); + +// Creates an FAudio sound driver. +std::unique_ptr CreateFAudioDriver(); + +} // namespace internal +} // namespace audio + +#endif // WX_AUDIO_INTERNAL_FAUDIO_H_ diff --git a/src/wx/openal.cpp b/src/wx/audio/internal/openal.cpp similarity index 64% rename from src/wx/openal.cpp rename to src/wx/audio/internal/openal.cpp index a52cc01c..deb84dc8 100644 --- a/src/wx/openal.cpp +++ b/src/wx/audio/internal/openal.cpp @@ -1,18 +1,51 @@ -// === LOGALL writes very detailed informations to vba-trace.log === -//#define LOGALL +#include "wx/audio/internal/openal.h" -#include "wx/openal.h" +// === LOGALL writes very detailed informations to vba-trace.log === +// #define LOGALL + +// on win32 and mac, pointer typedefs only happen with AL_NO_PROTOTYPES +// on mac, ALC_NO_PROTOTYPES as well + +// #define AL_NO_PROTOTYPES 1 + +// on mac, alc pointer typedefs ony happen for ALC if ALC_NO_PROTOTYPES +// unfortunately, there is a bug in the system headers (use of ALCvoid when +// void should be used; shame on Apple for introducing this error, and shame +// on Creative for making a typedef to void in the first place) +// #define ALC_NO_PROTOTYPES 1 + +#include +#include + +// since the ALC typedefs are broken on Mac: + +#ifdef __WXMAC__ +typedef ALCcontext*(ALC_APIENTRY* LPALCCREATECONTEXT)(ALCdevice* device, const ALCint* attrlist); +typedef ALCboolean(ALC_APIENTRY* LPALCMAKECONTEXTCURRENT)(ALCcontext* context); +typedef void(ALC_APIENTRY* LPALCDESTROYCONTEXT)(ALCcontext* context); +typedef ALCdevice*(ALC_APIENTRY* LPALCOPENDEVICE)(const ALCchar* devicename); +typedef ALCboolean(ALC_APIENTRY* LPALCCLOSEDEVICE)(ALCdevice* device); +typedef ALCboolean(ALC_APIENTRY* LPALCISEXTENSIONPRESENT)(ALCdevice* device, + const ALCchar* extname); +typedef const ALCchar*(ALC_APIENTRY* LPALCGETSTRING)(ALCdevice* device, ALCenum param); +#endif #include #include +#include +#include #include -#include "core/base/sound_driver.h" #include "core/gba/gbaGlobals.h" #include "core/gba/gbaSound.h" #include "wx/config/option-proxy.h" +namespace audio { +namespace internal { + +namespace { + // Debug #define ASSERT_SUCCESS assert(AL_NO_ERROR == alGetError()) @@ -22,8 +55,10 @@ #undef winlog #endif // https://stackoverflow.com/a/1306690/262458 -#define winlog(x,...) do {} while(0) -#define debugState() // +#define winlog(x, ...) \ + do { \ + } while (0) +#define debugState() // #endif struct OPENALFNTABLE; @@ -33,13 +68,13 @@ public: OpenAL(); ~OpenAL() override; - static bool GetDevices(wxArrayString& names, wxArrayString& ids); - bool init(long sampleRate); // initialize the sound buffer queue - void setThrottle(unsigned short throttle_); // set game speed - void pause(); // pause the secondary sound buffer - void reset(); // stop and reset the secondary sound buffer - void resume(); // play/resume the secondary sound buffer - void write(uint16_t* finalWave, int length); // write the emulated sound to a sound buffer + bool init(long sampleRate) override; // initialize the sound buffer queue + void setThrottle(unsigned short throttle_) override; // set game speed + void pause() override; // pause the secondary sound buffer + void reset() override; // stop and reset the secondary sound buffer + void resume() override; // play/resume the secondary sound buffer + void write(uint16_t* finalWave, + int length) override; // write the emulated sound to a sound buffer private: bool initialized; @@ -57,20 +92,18 @@ private: #endif }; -OpenAL::OpenAL() -{ +OpenAL::OpenAL() { initialized = false; buffersLoaded = false; - device = NULL; - context = NULL; + device = nullptr; + context = nullptr; buffer = (ALuint*)malloc(OPTION(kSoundBuffers) * sizeof(ALuint)); memset(buffer, 0, OPTION(kSoundBuffers) * sizeof(ALuint)); tempBuffer = 0; source = 0; } -OpenAL::~OpenAL() -{ +OpenAL::~OpenAL() { if (!initialized) return; @@ -83,22 +116,21 @@ OpenAL::~OpenAL() alDeleteBuffers(OPTION(kSoundBuffers), buffer); ASSERT_SUCCESS; free(buffer); - alcMakeContextCurrent(NULL); + alcMakeContextCurrent(nullptr); // Wine incorrectly returns ALC_INVALID_VALUE // and then fails the rest of these functions as well // so there will be a leak under Wine, but that's a bug in Wine, not // this code - //ASSERT_SUCCESS; + // ASSERT_SUCCESS; alcDestroyContext(context); - //ASSERT_SUCCESS; + // ASSERT_SUCCESS; alcCloseDevice(device); - //ASSERT_SUCCESS; - alGetError(); // reset error state + // ASSERT_SUCCESS; + alGetError(); // reset error state } #ifdef LOGALL -void OpenAL::debugState() -{ +void OpenAL::debugState() { ALint value = 0; alGetSourcei(source, AL_SOURCE_STATE, &value); ASSERT_SUCCESS; @@ -107,28 +139,27 @@ void OpenAL::debugState() winlog(" State: "); switch (value) { - case AL_INITIAL: - winlog("AL_INITIAL\n"); - break; + case AL_INITIAL: + winlog("AL_INITIAL\n"); + break; - case AL_PLAYING: - winlog("AL_PLAYING\n"); - break; + case AL_PLAYING: + winlog("AL_PLAYING\n"); + break; - case AL_PAUSED: - winlog("AL_PAUSED\n"); - break; + case AL_PAUSED: + winlog("AL_PAUSED\n"); + break; - case AL_STOPPED: - winlog("AL_STOPPED\n"); - break; + case AL_STOPPED: + winlog("AL_STOPPED\n"); + break; - default: - winlog("!unknown!\n"); - break; + default: + winlog("!unknown!\n"); + break; } - alGetSourcei(source, AL_BUFFERS_QUEUED, &value); ASSERT_SUCCESS; winlog(" Buffers in queue: %i\n", value); @@ -138,21 +169,29 @@ void OpenAL::debugState() } #endif -bool OpenAL::init(long sampleRate) -{ +bool OpenAL::init(long sampleRate) { winlog("OpenAL::init\n"); assert(initialized == false); const wxString& audio_device = OPTION(kSoundAudioDevice); if (!audio_device.empty()) { device = alcOpenDevice(audio_device.utf8_str()); + if (device == nullptr) { + // Might be the default device. Try again. + OPTION(kSoundAudioDevice) = wxEmptyString; + device = alcOpenDevice(nullptr); + } } else { - device = alcOpenDevice(NULL); + device = alcOpenDevice(nullptr); } - assert(device != NULL); - context = alcCreateContext(device, NULL); - assert(context != NULL); + if (!device) { + wxLogError(_("OpenAL: Failed to open audio device")); + return false; + } + + context = alcCreateContext(device, nullptr); + assert(context != nullptr); ALCboolean retVal = alcMakeContextCurrent(context); assert(ALC_TRUE == retVal); alGenBuffers(OPTION(kSoundBuffers), buffer); @@ -178,8 +217,7 @@ void OpenAL::setThrottle(unsigned short throttle_) { ASSERT_SUCCESS; } -void OpenAL::resume() -{ +void OpenAL::resume() { if (!initialized) return; @@ -201,8 +239,7 @@ void OpenAL::resume() debugState(); } -void OpenAL::pause() -{ +void OpenAL::pause() { if (!initialized) return; @@ -224,8 +261,7 @@ void OpenAL::pause() debugState(); } -void OpenAL::reset() -{ +void OpenAL::reset() { if (!initialized) return; @@ -247,9 +283,8 @@ void OpenAL::reset() debugState(); } -void OpenAL::write(uint16_t* finalWave, int length) -{ - (void)length; // unused param +void OpenAL::write(uint16_t* finalWave, int length) { + (void)length; // unused param if (!initialized) return; @@ -327,35 +362,39 @@ void OpenAL::write(uint16_t* finalWave, int length) } } -std::unique_ptr newOpenAL() -{ +} // namespace + +std::vector GetOpenALDevices() { + std::vector devices; + +#ifdef ALC_DEVICE_SPECIFIER + + if (alcIsExtensionPresent(nullptr, "ALC_ENUMERATION_EXT") == AL_FALSE) { + // this extension isn't critical to OpenAL operating + return devices; + } + + const char* devs = alcGetString(nullptr, ALC_DEVICE_SPECIFIER); + + while (*devs) { + const wxString device_name(devs, wxConvLibc); + devices.push_back({device_name, device_name}); + devs += strlen(devs) + 1; + } + +#else + + devices.push_back({_("Default device"), wxEmptyString}); + +#endif + + return devices; +} + +std::unique_ptr CreateOpenALDriver() { winlog("newOpenAL\n"); return std::make_unique(); } -bool GetOALDevices(wxArrayString& names, wxArrayString& ids) -{ - return OpenAL::GetDevices(names, ids); -} - -bool OpenAL::GetDevices(wxArrayString& names, wxArrayString& ids) -{ -#ifdef ALC_DEVICE_SPECIFIER - - if (alcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT") == AL_FALSE) - // this extension isn't critical to OpenAL operating - return true; - - const char* devs = alcGetString(NULL, ALC_DEVICE_SPECIFIER); - - while (*devs) { - names.push_back(wxString(devs, wxConvLibc)); - ids.push_back(names[names.size() - 1]); - devs += strlen(devs) + 1; - } - -#endif - - // should work anyway, but must always use default driver - return true; -} +} // namespace internal +} // namespace audio \ No newline at end of file diff --git a/src/wx/audio/internal/openal.h b/src/wx/audio/internal/openal.h new file mode 100644 index 00000000..8c0bf1df --- /dev/null +++ b/src/wx/audio/internal/openal.h @@ -0,0 +1,18 @@ +#ifndef WX_AUDIO_INTERNAL_OPENAL_H_ +#define WX_AUDIO_INTERNAL_OPENAL_H_ + +#include "wx/audio/audio.h" + +namespace audio { +namespace internal { + +// Returns the set of OpenAL devices. +std::vector GetOpenALDevices(); + +// Creates an OpenAL sound driver. +std::unique_ptr CreateOpenALDriver(); + +} // namespace internal +} // namespace audio + +#endif // WX_AUDIO_INTERNAL_OPENAL_H_ diff --git a/src/wx/xaudio2.cpp b/src/wx/audio/internal/xaudio2.cpp similarity index 61% rename from src/wx/xaudio2.cpp rename to src/wx/audio/internal/xaudio2.cpp index 50402f8f..338fa6ec 100644 --- a/src/wx/xaudio2.cpp +++ b/src/wx/audio/internal/xaudio2.cpp @@ -2,6 +2,8 @@ #error "This file should only be compiled if XAudio2 is enabled" #endif +#include "wx/audio/internal/xaudio2.h" + #include #include @@ -14,78 +16,55 @@ #include #else #include -#endif +#endif #include #include #include #include "core/base/sound_driver.h" -#include "core/base/system.h" // for systemMessage() +#include "core/base/system.h" // for systemMessage() #include "core/gba/gbaGlobals.h" #include "wx/config/option-proxy.h" -int GetXA2Devices(IXAudio2* xa, wxArrayString* names, wxArrayString* ids, - const wxString* match) -{ - HRESULT hr; - UINT32 dev_count = 0; - hr = xa->GetDeviceCount(&dev_count); +namespace audio { +namespace internal { - if (hr != S_OK) { - wxLogError(_("XAudio2: Enumerating devices failed!")); - return true; - } else { - XAUDIO2_DEVICE_DETAILS dd; +namespace { - for (UINT32 i = 0; i < dev_count; i++) { - hr = xa->GetDeviceDetails(i, &dd); - - if (hr != S_OK) { - continue; - } else { - if (ids) { - ids->push_back(dd.DeviceID); - names->push_back(dd.DisplayName); - } else if (*match == dd.DeviceID) - return i; - } - } +int XA2GetDev(IXAudio2* xa) { + const wxString& audio_device = OPTION(kSoundAudioDevice); + if (audio_device.empty()) { + // Just use the default device. + return 0; } - return -1; -} - -bool GetXA2Devices(wxArrayString& names, wxArrayString& ids) -{ - HRESULT hr; - IXAudio2* xa = NULL; - hr = XAudio2Create(&xa, 0); - + uint32_t hr; + uint32_t dev_count = 0; + hr = xa->GetDeviceCount(&dev_count); if (hr != S_OK) { - wxLogError(_("The XAudio2 interface failed to initialize!")); + wxLogError(_("XAudio2: Enumerating devices failed!")); return false; } - GetXA2Devices(xa, &names, &ids, NULL); - xa->Release(); - return true; -} - -static int XA2GetDev(IXAudio2* xa) -{ - const wxString& audio_device = OPTION(kSoundAudioDevice); - if (audio_device.empty()) - return 0; - else { - int ret = GetXA2Devices(xa, NULL, NULL, &audio_device); - return ret < 0 ? 0 : ret; + for (UINT32 i = 0; i < dev_count; i++) { + XAUDIO2_DEVICE_DETAILS dd; + hr = xa->GetDeviceDetails(i, &dd); + if (hr != S_OK) { + continue; + } + const wxString device_id(reinterpret_cast(dd.DeviceID)); + if (audio_device == device_id) { + return i; + } } + + return 0; } class XAudio2_Output; -static void xaudio2_device_changed(XAudio2_Output*); +void xaudio2_device_changed(XAudio2_Output*); class XAudio2_Device_Notifier : public IMMNotificationClient { volatile LONG registered; @@ -97,28 +76,14 @@ class XAudio2_Device_Notifier : public IMMNotificationClient { std::vector instances; public: - XAudio2_Device_Notifier() - : registered(0) - { - InitializeCriticalSection(&lock); - } - ~XAudio2_Device_Notifier() - { - DeleteCriticalSection(&lock); - } + XAudio2_Device_Notifier() : registered(0) { InitializeCriticalSection(&lock); } + ~XAudio2_Device_Notifier() { DeleteCriticalSection(&lock); } - ULONG STDMETHODCALLTYPE AddRef() - { - return 1; - } + ULONG STDMETHODCALLTYPE AddRef() { return 1; } - ULONG STDMETHODCALLTYPE Release() - { - return 1; - } + ULONG STDMETHODCALLTYPE Release() { return 1; } - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID** ppvInterface) - { + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID** ppvInterface) { if (IID_IUnknown == riid) { *ppvInterface = (IUnknown*)this; } else if (__uuidof(IMMNotificationClient) == riid) { @@ -131,8 +96,7 @@ public: return S_OK; } - HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole, LPCWSTR pwstrDeviceId) - { + HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole, LPCWSTR pwstrDeviceId) { if (flow == eRender && last_device.compare(pwstrDeviceId) != 0) { last_device = pwstrDeviceId; EnterCriticalSection(&lock); @@ -152,11 +116,11 @@ public: HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR, DWORD) { return S_OK; } HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(LPCWSTR, const PROPERTYKEY) { return S_OK; } - void do_register(XAudio2_Output* p_instance) - { + void do_register(XAudio2_Output* p_instance) { if (InterlockedIncrement(®istered) == 1) { pEnumerator = NULL; - HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, __uuidof(IMMDeviceEnumerator), (void**)&pEnumerator); + HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, + __uuidof(IMMDeviceEnumerator), (void**)&pEnumerator); if (SUCCEEDED(hr)) { pEnumerator->RegisterEndpointNotificationCallback(this); @@ -168,8 +132,7 @@ public: LeaveCriticalSection(&lock); } - void do_unregister(XAudio2_Output* p_instance) - { + void do_unregister(XAudio2_Output* p_instance) { if (InterlockedDecrement(®istered) == 0) { if (pEnumerator) { pEnumerator->UnregisterEndpointNotificationCallback(this); @@ -196,22 +159,19 @@ class XAudio2_BufferNotify : public IXAudio2VoiceCallback { public: HANDLE hBufferEndEvent; - XAudio2_BufferNotify() - { + XAudio2_BufferNotify() { hBufferEndEvent = NULL; hBufferEndEvent = CreateEvent(NULL, FALSE, FALSE, NULL); assert(hBufferEndEvent != NULL); } - ~XAudio2_BufferNotify() - { + ~XAudio2_BufferNotify() { CloseHandle(hBufferEndEvent); hBufferEndEvent = NULL; } STDMETHOD_(void, OnBufferEnd) - (void*) - { + (void*) { assert(hBufferEndEvent != NULL); SetEvent(hBufferEndEvent); } @@ -232,13 +192,13 @@ public: }; // Class Declaration -class XAudio2_Output - : public SoundDriver { +class XAudio2_Output : public SoundDriver { public: XAudio2_Output(); ~XAudio2_Output() override; void device_change(); + private: void close(); @@ -247,7 +207,7 @@ private: void pause() override; void reset() override; void resume() override; - void write(uint16_t *finalWave, int length) override; + void write(uint16_t* finalWave, int length) override; void setThrottle(unsigned short throttle_) override; bool failed; @@ -262,16 +222,15 @@ private: volatile bool device_changed; IXAudio2* xaud; - IXAudio2MasteringVoice* mVoice; // listener - IXAudio2SourceVoice* sVoice; // sound source + IXAudio2MasteringVoice* mVoice; // listener + IXAudio2SourceVoice* sVoice; // sound source XAUDIO2_BUFFER buf; XAUDIO2_VOICE_STATE vState; - XAudio2_BufferNotify notify; // buffer end notification + XAudio2_BufferNotify notify; // buffer end notification }; // Class Implementation -XAudio2_Output::XAudio2_Output() -{ +XAudio2_Output::XAudio2_Output() { failed = false; initialized = false; playing = false; @@ -288,14 +247,12 @@ XAudio2_Output::XAudio2_Output() g_notifier.do_register(this); } -XAudio2_Output::~XAudio2_Output() -{ +XAudio2_Output::~XAudio2_Output() { g_notifier.do_unregister(this); close(); } -void XAudio2_Output::close() -{ +void XAudio2_Output::close() { initialized = false; if (sVoice) { @@ -324,13 +281,11 @@ void XAudio2_Output::close() } } -void XAudio2_Output::device_change() -{ +void XAudio2_Output::device_change() { device_changed = true; } -bool XAudio2_Output::init(long sampleRate) -{ +bool XAudio2_Output::init(long sampleRate) { if (failed || initialized) return false; @@ -361,13 +316,8 @@ bool XAudio2_Output::init(long sampleRate) wfx.nBlockAlign = wfx.nChannels * (wfx.wBitsPerSample / 8); wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; // create sound receiver - hr = xaud->CreateMasteringVoice( - &mVoice, - XAUDIO2_DEFAULT_CHANNELS, - XAUDIO2_DEFAULT_SAMPLERATE, - 0, - XA2GetDev(xaud), - NULL); + hr = xaud->CreateMasteringVoice(&mVoice, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE, + 0, XA2GetDev(xaud), NULL); if (hr != S_OK) { wxLogError(_("XAudio2: Creating mastering voice failed!")); @@ -399,89 +349,89 @@ bool XAudio2_Output::init(long sampleRate) bool matrixAvailable = true; switch (dd.OutputFormat.Format.nChannels) { - case 4: // 4.0 - //Speaker \ Left Source Right Source - /*Front L*/ matrix[0] = 1.0000f; - matrix[1] = 0.0000f; - /*Front R*/ matrix[2] = 0.0000f; - matrix[3] = 1.0000f; - /*Back L*/ matrix[4] = 1.0000f; - matrix[5] = 0.0000f; - /*Back R*/ matrix[6] = 0.0000f; - matrix[7] = 1.0000f; - break; + case 4: // 4.0 + // Speaker \ Left Source Right Source + /*Front L*/ matrix[0] = 1.0000f; + matrix[1] = 0.0000f; + /*Front R*/ matrix[2] = 0.0000f; + matrix[3] = 1.0000f; + /*Back L*/ matrix[4] = 1.0000f; + matrix[5] = 0.0000f; + /*Back R*/ matrix[6] = 0.0000f; + matrix[7] = 1.0000f; + break; - case 5: // 5.0 - //Speaker \ Left Source Right Source - /*Front L*/ matrix[0] = 1.0000f; - matrix[1] = 0.0000f; - /*Front R*/ matrix[2] = 0.0000f; - matrix[3] = 1.0000f; - /*Front C*/ matrix[4] = 0.7071f; - matrix[5] = 0.7071f; - /*Side L*/ matrix[6] = 1.0000f; - matrix[7] = 0.0000f; - /*Side R*/ matrix[8] = 0.0000f; - matrix[9] = 1.0000f; - break; + case 5: // 5.0 + // Speaker \ Left Source Right Source + /*Front L*/ matrix[0] = 1.0000f; + matrix[1] = 0.0000f; + /*Front R*/ matrix[2] = 0.0000f; + matrix[3] = 1.0000f; + /*Front C*/ matrix[4] = 0.7071f; + matrix[5] = 0.7071f; + /*Side L*/ matrix[6] = 1.0000f; + matrix[7] = 0.0000f; + /*Side R*/ matrix[8] = 0.0000f; + matrix[9] = 1.0000f; + break; - case 6: // 5.1 - //Speaker \ Left Source Right Source - /*Front L*/ matrix[0] = 1.0000f; - matrix[1] = 0.0000f; - /*Front R*/ matrix[2] = 0.0000f; - matrix[3] = 1.0000f; - /*Front C*/ matrix[4] = 0.7071f; - matrix[5] = 0.7071f; - /*LFE */ matrix[6] = 0.0000f; - matrix[7] = 0.0000f; - /*Side L*/ matrix[8] = 1.0000f; - matrix[9] = 0.0000f; - /*Side R*/ matrix[10] = 0.0000f; - matrix[11] = 1.0000f; - break; + case 6: // 5.1 + // Speaker \ Left Source Right Source + /*Front L*/ matrix[0] = 1.0000f; + matrix[1] = 0.0000f; + /*Front R*/ matrix[2] = 0.0000f; + matrix[3] = 1.0000f; + /*Front C*/ matrix[4] = 0.7071f; + matrix[5] = 0.7071f; + /*LFE */ matrix[6] = 0.0000f; + matrix[7] = 0.0000f; + /*Side L*/ matrix[8] = 1.0000f; + matrix[9] = 0.0000f; + /*Side R*/ matrix[10] = 0.0000f; + matrix[11] = 1.0000f; + break; - case 7: // 6.1 - //Speaker \ Left Source Right Source - /*Front L*/ matrix[0] = 1.0000f; - matrix[1] = 0.0000f; - /*Front R*/ matrix[2] = 0.0000f; - matrix[3] = 1.0000f; - /*Front C*/ matrix[4] = 0.7071f; - matrix[5] = 0.7071f; - /*LFE */ matrix[6] = 0.0000f; - matrix[7] = 0.0000f; - /*Side L*/ matrix[8] = 1.0000f; - matrix[9] = 0.0000f; - /*Side R*/ matrix[10] = 0.0000f; - matrix[11] = 1.0000f; - /*Back C*/ matrix[12] = 0.7071f; - matrix[13] = 0.7071f; - break; + case 7: // 6.1 + // Speaker \ Left Source Right Source + /*Front L*/ matrix[0] = 1.0000f; + matrix[1] = 0.0000f; + /*Front R*/ matrix[2] = 0.0000f; + matrix[3] = 1.0000f; + /*Front C*/ matrix[4] = 0.7071f; + matrix[5] = 0.7071f; + /*LFE */ matrix[6] = 0.0000f; + matrix[7] = 0.0000f; + /*Side L*/ matrix[8] = 1.0000f; + matrix[9] = 0.0000f; + /*Side R*/ matrix[10] = 0.0000f; + matrix[11] = 1.0000f; + /*Back C*/ matrix[12] = 0.7071f; + matrix[13] = 0.7071f; + break; - case 8: // 7.1 - //Speaker \ Left Source Right Source - /*Front L*/ matrix[0] = 1.0000f; - matrix[1] = 0.0000f; - /*Front R*/ matrix[2] = 0.0000f; - matrix[3] = 1.0000f; - /*Front C*/ matrix[4] = 0.7071f; - matrix[5] = 0.7071f; - /*LFE */ matrix[6] = 0.0000f; - matrix[7] = 0.0000f; - /*Back L*/ matrix[8] = 1.0000f; - matrix[9] = 0.0000f; - /*Back R*/ matrix[10] = 0.0000f; - matrix[11] = 1.0000f; - /*Side L*/ matrix[12] = 1.0000f; - matrix[13] = 0.0000f; - /*Side R*/ matrix[14] = 0.0000f; - matrix[15] = 1.0000f; - break; + case 8: // 7.1 + // Speaker \ Left Source Right Source + /*Front L*/ matrix[0] = 1.0000f; + matrix[1] = 0.0000f; + /*Front R*/ matrix[2] = 0.0000f; + matrix[3] = 1.0000f; + /*Front C*/ matrix[4] = 0.7071f; + matrix[5] = 0.7071f; + /*LFE */ matrix[6] = 0.0000f; + matrix[7] = 0.0000f; + /*Back L*/ matrix[8] = 1.0000f; + matrix[9] = 0.0000f; + /*Back R*/ matrix[10] = 0.0000f; + matrix[11] = 1.0000f; + /*Side L*/ matrix[12] = 1.0000f; + matrix[13] = 0.0000f; + /*Side R*/ matrix[14] = 0.0000f; + matrix[15] = 1.0000f; + break; - default: - matrixAvailable = false; - break; + default: + matrixAvailable = false; + break; } if (matrixAvailable) { @@ -502,8 +452,7 @@ bool XAudio2_Output::init(long sampleRate) return true; } -void XAudio2_Output::write(uint16_t* finalWave, int) -{ +void XAudio2_Output::write(uint16_t* finalWave, int) { if (!initialized || failed) return; @@ -548,13 +497,12 @@ void XAudio2_Output::write(uint16_t* finalWave, int) buf.AudioBytes = soundBufferLen; buf.pAudioData = &buffers[currentBuffer * soundBufferLen]; currentBuffer++; - currentBuffer %= (bufferCount + 1); // + 1 because we need one temporary buffer - HRESULT hr = sVoice->SubmitSourceBuffer(&buf); // send buffer to queue + currentBuffer %= (bufferCount + 1); // + 1 because we need one temporary buffer + HRESULT hr = sVoice->SubmitSourceBuffer(&buf); // send buffer to queue assert(hr == S_OK); } -void XAudio2_Output::pause() -{ +void XAudio2_Output::pause() { if (!initialized || failed) return; @@ -565,8 +513,7 @@ void XAudio2_Output::pause() } } -void XAudio2_Output::resume() -{ +void XAudio2_Output::resume() { if (!initialized || failed) return; @@ -577,8 +524,7 @@ void XAudio2_Output::resume() } } -void XAudio2_Output::reset() -{ +void XAudio2_Output::reset() { if (!initialized || failed) return; @@ -592,8 +538,7 @@ void XAudio2_Output::reset() playing = true; } -void XAudio2_Output::setThrottle(unsigned short throttle_) -{ +void XAudio2_Output::setThrottle(unsigned short throttle_) { if (!initialized || failed) return; @@ -604,12 +549,50 @@ void XAudio2_Output::setThrottle(unsigned short throttle_) assert(hr == S_OK); } -void xaudio2_device_changed(XAudio2_Output* instance) -{ +void xaudio2_device_changed(XAudio2_Output* instance) { instance->device_change(); } -std::unique_ptr newXAudio2_Output() -{ +} // namespace + +std::vector GetXAudio2Devices() { + HRESULT hr; + IXAudio2* xa = nullptr; + hr = XAudio2Create(&xa, 0); + + if (hr != S_OK) { + wxLogError(_("The XAudio2 interface failed to initialize!")); + return {}; + } + + UINT32 dev_count = 0; + hr = xa->GetDeviceCount(&dev_count); + if (hr != S_OK) { + wxLogError(_("XAudio2: Enumerating devices failed!")); + return {}; + } + + std::vector devices; + devices.reserve(dev_count + 1); + devices.push_back({_("Default device"), wxEmptyString}); + + for (UINT32 i = 0; i < dev_count; i++) { + XAUDIO2_DEVICE_DETAILS dd; + hr = xa->GetDeviceDetails(i, &dd); + + if (hr != S_OK) { + continue; + } + devices.push_back({dd.DisplayName, dd.DeviceID}); + } + + xa->Release(); + return devices; +} + +std::unique_ptr CreateXAudio2Driver() { return std::make_unique(); } + +} // namespace internal +} // namespace audio diff --git a/src/wx/audio/internal/xaudio2.h b/src/wx/audio/internal/xaudio2.h new file mode 100644 index 00000000..e05c30d2 --- /dev/null +++ b/src/wx/audio/internal/xaudio2.h @@ -0,0 +1,22 @@ +#ifndef WX_AUDIO_INTERNAL_XAUDIO2_H_ +#define WX_AUDIO_INTERNAL_XAUDIO2_H_ + +#if !defined(VBAM_ENABLE_FAUDIO) +#error "This file should only be included if FAudio is enabled" +#endif + +#include "wx/audio/audio.h" + +namespace audio { +namespace internal { + +// Returns the set of XAudio2 devices. +std::vector GetXAudio2Devices(); + +// Creates an XAudio2 sound driver. +std::unique_ptr CreateXAudio2Driver(); + +} // namespace internal +} // namespace audio + +#endif // WX_AUDIO_INTERNAL_XAUDIO2_H_ diff --git a/src/wx/dialogs/sound-config.cpp b/src/wx/dialogs/sound-config.cpp index ebe9130f..64c07d54 100644 --- a/src/wx/dialogs/sound-config.cpp +++ b/src/wx/dialogs/sound-config.cpp @@ -8,15 +8,16 @@ #include #include +#include #include #include +#include "wx/audio/audio.h" #include "wx/config/option-id.h" #include "wx/config/option-proxy.h" #include "wx/config/option.h" #include "wx/dialogs/validated-child.h" #include "wx/widgets/option-validator.h" -#include "wx/wxvbam.h" namespace dialogs { @@ -103,13 +104,18 @@ private: bool WriteToWindow() override { wxChoice* choice = wxDynamicCast(GetWindow(), wxChoice); assert(choice); - const wxString& device = option()->GetString(); - const int selection = choice->FindString(device); - if (selection == wxNOT_FOUND) { - return true; + + const wxString& device_id = option()->GetString(); + for (size_t i = 0; i < choice->GetCount(); i++) { + const wxString& choide_id = + dynamic_cast(choice->GetClientObject(i))->GetData(); + if (device_id == choide_id) { + choice->SetSelection(i); + return true; + } } - choice->SetSelection(selection); + choice->SetSelection(0); return true; } @@ -121,7 +127,8 @@ private: return option()->SetString(wxEmptyString); } - return option()->SetString(choice->GetString(selection)); + return option()->SetString( + dynamic_cast(choice->GetClientObject(selection))->GetData()); } }; @@ -255,56 +262,21 @@ void SoundConfig::OnBuffersChanged(wxCommandEvent& event) { void SoundConfig::OnAudioApiChanged(wxCommandEvent& event, config::AudioApi audio_api) { audio_device_selector_->Clear(); - audio_device_selector_->Append(_("Default device"), new wxStringClientData(wxEmptyString)); - // Gather device names and IDs. - wxArrayString device_names; - wxArrayString device_ids; - switch (audio_api) { - case config::AudioApi::kOpenAL: - if (!GetOALDevices(device_names, device_ids)) { - return; - } - break; + bool audio_device_found = false; + for (const auto& device : audio::EnumerateAudioDevices(audio_api)) { + const int i = + audio_device_selector_->Append(device.name, new wxStringClientData(device.id)); -#if defined(__WXMSW__) - case config::AudioApi::kDirectSound: - if (!(GetDSDevices(device_names, device_ids))) { - return; - } - break; -#endif - -#if defined(VBAM_ENABLE_XAUDIO2) - case config::AudioApi::kXAudio2: - if (!GetXA2Devices(device_names, device_ids)) { - return; - } - break; -#endif - -#if defined(VBAM_ENABLE_FAUDIO) - case config::AudioApi::kFAudio: - if (!GetFADevices(device_names, device_ids)) { - return; - } - break; -#endif - - case config::AudioApi::kLast: - // This should never happen. - assert(false); - return; + if (!audio_device_found && audio_api == OPTION(kSoundAudioAPI) && + OPTION(kSoundAudioDevice) == device.id) { + audio_device_selector_->SetSelection(i); + audio_device_found = true; + } } - audio_device_selector_->SetSelection(0); - - for (size_t i = 0; i < device_names.size(); i++) { - audio_device_selector_->Append(device_names[i], new wxStringClientData(device_ids[i])); - - if (audio_api == OPTION(kSoundAudioAPI) && OPTION(kSoundAudioDevice) == device_ids[i]) { - audio_device_selector_->SetSelection(i + 1); - } + if (!audio_device_found) { + audio_device_selector_->SetSelection(0); } #if defined(VBAM_ENABLE_XAUDIO2) && defined(VBAM_ENABLE_FAUDIO) diff --git a/src/wx/openal.h b/src/wx/openal.h deleted file mode 100644 index 9a737989..00000000 --- a/src/wx/openal.h +++ /dev/null @@ -1,26 +0,0 @@ -// on win32 and mac, pointer typedefs only happen with AL_NO_PROTOTYPES -// on mac, ALC_NO_PROTOTYPES as well - -//#define AL_NO_PROTOTYPES 1 - -// on mac, alc pointer typedefs ony happen for ALC if ALC_NO_PROTOTYPES -// unfortunately, there is a bug in the system headers (use of ALCvoid when -// void should be used; shame on Apple for introducing this error, and shame -// on Creative for making a typedef to void in the first place) -//#define ALC_NO_PROTOTYPES 1 - -#include -#include - -// since the ALC typedefs are broken on Mac: - -#ifdef __WXMAC__ -typedef ALCcontext*(ALC_APIENTRY* LPALCCREATECONTEXT)(ALCdevice* device, const ALCint* attrlist); -typedef ALCboolean(ALC_APIENTRY* LPALCMAKECONTEXTCURRENT)(ALCcontext* context); -typedef void(ALC_APIENTRY* LPALCDESTROYCONTEXT)(ALCcontext* context); -typedef ALCdevice*(ALC_APIENTRY* LPALCOPENDEVICE)(const ALCchar* devicename); -typedef ALCboolean(ALC_APIENTRY* LPALCCLOSEDEVICE)(ALCdevice* device); -typedef ALCboolean(ALC_APIENTRY* LPALCISEXTENSIONPRESENT)(ALCdevice* device, - const ALCchar* extname); -typedef const ALCchar*(ALC_APIENTRY* LPALCGETSTRING)(ALCdevice* device, ALCenum param); -#endif diff --git a/src/wx/sys.cpp b/src/wx/sys.cpp index 8dd02285..590d181f 100644 --- a/src/wx/sys.cpp +++ b/src/wx/sys.cpp @@ -1,5 +1,4 @@ #include -#include #include #include @@ -11,9 +10,9 @@ #include "core/gb/gbGlobals.h" #include "core/gba/gbaGlobals.h" #include "core/gba/gbaSound.h" +#include "wx/audio/audio.h" #include "wx/config/game-control.h" #include "wx/config/option-proxy.h" -#include "wx/config/option.h" #include "wx/wxvbam.h" // These should probably be in vbamcore @@ -1230,34 +1229,7 @@ class SoundDriver; std::unique_ptr systemSoundInit() { soundShutdown(); - - switch (OPTION(kSoundAudioAPI)) { - case config::AudioApi::kOpenAL: - return newOpenAL(); - -#if defined(__WXMSW__) - case config::AudioApi::kDirectSound: - return newDirectSound(); -#endif - -#if defined(VBAM_ENABLE_XAUDIO2) - case config::AudioApi::kXAudio2: - return newXAudio2_Output(); -#endif - -#if defined(VBAM_ENABLE_FAUDIO) - case config::AudioApi::kFAudio: - return newFAudio_Output(); -#endif - - case config::AudioApi::kLast: - // This should never happen. - assert(false); - return nullptr; - } - - assert(false); - return nullptr; + return audio::CreateSoundDriver(OPTION(kSoundAudioAPI)); } void systemOnWriteDataToSoundBuffer(const uint16_t* finalWave, int length) diff --git a/src/wx/wxvbam.h b/src/wx/wxvbam.h index fc488cb9..2752a3e8 100644 --- a/src/wx/wxvbam.h +++ b/src/wx/wxvbam.h @@ -711,27 +711,6 @@ private: #include "wx/opts.h" -// I should add this to SoundDriver, but wxArrayString is wx-specific -// I suppose I could make subclass wxSoundDriver. maybe later. -class SoundDriver; -extern std::unique_ptr newOpenAL(); -extern bool GetOALDevices(wxArrayString& names, wxArrayString& ids); - -#if defined(__WXMSW__) -extern std::unique_ptr newDirectSound(); -extern bool GetDSDevices(wxArrayString& names, wxArrayString& ids); -#endif // defined(__WXMSW__) - -#if defined(VBAM_ENABLE_XAUDIO2) -extern std::unique_ptr newXAudio2_Output(); -extern bool GetXA2Devices(wxArrayString& names, wxArrayString& ids); -#endif // defined(VBAM_ENABLE_XAUDIO2) - -#if defined(VBAM_ENABLE_FAUDIO) -extern std::unique_ptr newFAudio_Output(); -extern bool GetFADevices(wxArrayString& names, wxArrayString& ids); -#endif // defined(VBAM_ENABLE_FAUDIO) - #if defined(VBAM_ENABLE_DEBUGGER) extern bool debugger; extern void (*dbgMain)();