[Audio] Rework audio devices enumeration

This moves all of the audio code in the wx frontend to the
`src/wx/audio` folder and simplifies many call sites by having one
generic API to enumerate audio devices and create the audio driver.

In addition, this fixes many corner cases in device enumerations and
moves handling of the default device to the respective audio backends,
rather than pushing it to the UI.

Finally, this changes the `Sound/AudioDevice` setting to use the
underlying device ID, rather than the user-facing name.
This commit is contained in:
Fabrice de Gans 2024-04-06 17:35:42 -07:00 committed by Rafael Kitover
parent 4104a3d179
commit f4835674ed
15 changed files with 758 additions and 651 deletions

View File

@ -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
)

76
src/wx/audio/audio.cpp Normal file
View File

@ -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<AudioDevice> 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<SoundDriver> 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

30
src/wx/audio/audio.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef WX_AUDIO_AUDIO_H_
#define WX_AUDIO_AUDIO_H_
#include <memory>
#include <vector>
#include <wx/string.h>
#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<AudioDevice> EnumerateAudioDevices(const config::AudioApi& api);
// Creates a sound driver for the given API.
std::unique_ptr<SoundDriver> CreateSoundDriver(const config::AudioApi& api);
} // namespace audio
#endif // WX_AUDIO_AUDIO_H_

View File

@ -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 <Windows.h>
@ -24,6 +26,11 @@
extern bool soundBufferLow;
namespace audio {
namespace internal {
namespace {
class DirectSound : public SoundDriver {
private:
LPDIRECTSOUND8 pDirectSound; // DirectSound interface
@ -49,8 +56,7 @@ public:
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];
@ -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<SoundDriver> newDirectSound()
{
return std::make_unique<DirectSound>();
}
static BOOL CALLBACK DSEnumCB(LPGUID guid, LPCTSTR desc, LPCTSTR /*module*/, LPVOID user) {
std::vector<AudioDevice>* devices = static_cast<std::vector<AudioDevice>*>(user);
struct devnames {
wxArrayString *names, *ids;
};
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);
if (guid == nullptr) {
devices->push_back({desc, {}});
return TRUE;
}
bool GetDSDevices(wxArrayString& names, wxArrayString& ids)
{
devnames dn = { &names, &ids };
return DirectSoundEnumerate(DSEnumCB, (LPVOID)&dn) == DS_OK;
static constexpr size_t kGuidLength = 32 + 4 + 2 + 1; // hex digits + "-" + "{}" + \0
std::array<WCHAR, kGuidLength> device_id;
StringFromGUID2(*guid, device_id.data(), device_id.size());
devices->push_back({desc, device_id.data()});
return TRUE;
}
} // namespace
std::vector<AudioDevice> GetDirectSoundDevices() {
std::vector<AudioDevice> devices;
DirectSoundEnumerateW(DSEnumCB, &devices);
return devices;
}
std::unique_ptr<SoundDriver> CreateDirectSoundDriver() {
return std::make_unique<DirectSound>();
}
} // namespace internal
} // namespace audio

View File

@ -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<AudioDevice> GetDirectSoundDevices();
// Creates a DirectSound sound driver.
std::unique_ptr<SoundDriver> CreateDirectSoundDriver();
} // namespace internal
} // namespace audio
#endif // WX_AUDIO_INTERNAL_DSOUND_H_

View File

@ -1,10 +1,12 @@
#include <wx/string.h>
#if !defined(VBAM_ENABLE_FAUDIO)
#error "This file should only be compiled if FAudio is enabled"
#endif
#include <cstdio>
#include "wx/audio/internal/faudio.h"
#include <cassert>
#include <string>
#include <vector>
// FAudio
@ -16,63 +18,50 @@
#include <wx/log.h>
#include <wx/translation.h>
#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;
}
FAudioDeviceDetails dd;
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))
}
const wxString device_id(reinterpret_cast<wchar_t*>(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;
}
}
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();
@ -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,13 +207,11 @@ 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;
@ -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(&registered) == 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(&registered) == 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<AudioDevice> 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<AudioDevice> 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<wchar_t*>(dd.DisplayName));
const wxString device_id(reinterpret_cast<wchar_t*>(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<SoundDriver> newFAudio_Output() {
std::unique_ptr<SoundDriver> CreateFAudioDriver() {
return std::make_unique<FAudio_Output>();
}
} // namespace internal
} // namespace audio

View File

@ -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<AudioDevice> GetFAudioDevices();
// Creates an FAudio sound driver.
std::unique_ptr<SoundDriver> CreateFAudioDriver();
} // namespace internal
} // namespace audio
#endif // WX_AUDIO_INTERNAL_FAUDIO_H_

View File

@ -1,18 +1,51 @@
#include "wx/audio/internal/openal.h"
// === LOGALL writes very detailed informations to vba-trace.log ===
// #define LOGALL
#include "wx/openal.h"
// 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 <al.h>
#include <alc.h>
// 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 <cassert>
#include <wx/arrstr.h>
#include <wx/log.h>
#include <wx/translation.h>
#include <wx/utils.h>
#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,7 +55,9 @@
#undef winlog
#endif
// https://stackoverflow.com/a/1306690/262458
#define winlog(x,...) do {} while(0)
#define winlog(x, ...) \
do { \
} while (0)
#define debugState() //
#endif
@ -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,7 +116,7 @@ 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
@ -97,8 +130,7 @@ OpenAL::~OpenAL()
}
#ifdef LOGALL
void OpenAL::debugState()
{
void OpenAL::debugState() {
ALint value = 0;
alGetSourcei(source, AL_SOURCE_STATE, &value);
ASSERT_SUCCESS;
@ -128,7 +160,6 @@ void OpenAL::debugState()
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,8 +283,7 @@ void OpenAL::reset()
debugState();
}
void OpenAL::write(uint16_t* finalWave, int length)
{
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<SoundDriver> newOpenAL()
{
} // namespace
std::vector<AudioDevice> GetOpenALDevices() {
std::vector<AudioDevice> 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<SoundDriver> CreateOpenALDriver() {
winlog("newOpenAL\n");
return std::make_unique<OpenAL>();
}
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

View File

@ -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<AudioDevice> GetOpenALDevices();
// Creates an OpenAL sound driver.
std::unique_ptr<SoundDriver> CreateOpenALDriver();
} // namespace internal
} // namespace audio
#endif // WX_AUDIO_INTERNAL_OPENAL_H_

View File

@ -2,6 +2,8 @@
#error "This file should only be compiled if XAudio2 is enabled"
#endif
#include "wx/audio/internal/xaudio2.h"
#include <cstdio>
#include <string>
@ -25,67 +27,44 @@
#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 {
namespace {
int XA2GetDev(IXAudio2* xa) {
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 = xa->GetDeviceCount(&dev_count);
if (hr != S_OK) {
wxLogError(_("XAudio2: Enumerating devices failed!"));
return true;
} else {
XAUDIO2_DEVICE_DETAILS dd;
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;
}
}
}
return -1;
}
bool GetXA2Devices(wxArrayString& names, wxArrayString& ids)
{
HRESULT hr;
IXAudio2* xa = NULL;
hr = XAudio2Create(&xa, 0);
if (hr != S_OK) {
wxLogError(_("The XAudio2 interface failed to initialize!"));
return false;
}
GetXA2Devices(xa, &names, &ids, NULL);
xa->Release();
return true;
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<wchar_t*>(dd.DeviceID));
if (audio_device == device_id) {
return i;
}
}
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;
}
}
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<XAudio2_Output*> 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(&registered) == 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(&registered) == 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();
@ -270,8 +230,7 @@ private:
};
// 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!"));
@ -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;
@ -553,8 +502,7 @@ void XAudio2_Output::write(uint16_t* finalWave, int)
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<SoundDriver> newXAudio2_Output()
{
} // namespace
std::vector<AudioDevice> 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<AudioDevice> 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<SoundDriver> CreateXAudio2Driver() {
return std::make_unique<XAudio2_Output>();
}
} // namespace internal
} // namespace audio

View File

@ -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<AudioDevice> GetXAudio2Devices();
// Creates an XAudio2 sound driver.
std::unique_ptr<SoundDriver> CreateXAudio2Driver();
} // namespace internal
} // namespace audio
#endif // WX_AUDIO_INTERNAL_XAUDIO2_H_

View File

@ -8,15 +8,16 @@
#include <wx/radiobut.h>
#include <wx/slider.h>
#include <wx/string.h>
#include <wx/xrc/xmlres.h>
#include <functional>
#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) {
const wxString& device_id = option()->GetString();
for (size_t i = 0; i < choice->GetCount(); i++) {
const wxString& choide_id =
dynamic_cast<wxStringClientData*>(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<wxStringClientData*>(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;
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 (!audio_device_found && audio_api == OPTION(kSoundAudioAPI) &&
OPTION(kSoundAudioDevice) == device.id) {
audio_device_selector_->SetSelection(i);
audio_device_found = true;
}
break;
#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_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 defined(VBAM_ENABLE_XAUDIO2) && defined(VBAM_ENABLE_FAUDIO)

View File

@ -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 <al.h>
#include <alc.h>
// 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

View File

@ -1,5 +1,4 @@
#include <algorithm>
#include <cassert>
#include <wx/ffile.h>
#include <wx/generic/prntdlgg.h>
@ -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<SoundDriver> 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)

View File

@ -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<SoundDriver> newOpenAL();
extern bool GetOALDevices(wxArrayString& names, wxArrayString& ids);
#if defined(__WXMSW__)
extern std::unique_ptr<SoundDriver> newDirectSound();
extern bool GetDSDevices(wxArrayString& names, wxArrayString& ids);
#endif // defined(__WXMSW__)
#if defined(VBAM_ENABLE_XAUDIO2)
extern std::unique_ptr<SoundDriver> newXAudio2_Output();
extern bool GetXA2Devices(wxArrayString& names, wxArrayString& ids);
#endif // defined(VBAM_ENABLE_XAUDIO2)
#if defined(VBAM_ENABLE_FAUDIO)
extern std::unique_ptr<SoundDriver> 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)();