[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,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<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;
};
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<WCHAR, kGuidLength> 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<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;
}
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<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;
}
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<float> 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(&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 @@
// === 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 <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,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<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>
@ -14,78 +16,55 @@
#include <xaudio2.legacy.h>
#else
#include <XAudio2.h>
#endif
#endif
#include <wx/arrstr.h>
#include <wx/log.h>
#include <wx/translation.h>
#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<wchar_t*>(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<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();
@ -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<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) {
return true;
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;
}
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)

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)();