[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:
parent
4104a3d179
commit
f4835674ed
|
@ -6,6 +6,10 @@ endif()
|
||||||
include(VbamFunctions)
|
include(VbamFunctions)
|
||||||
|
|
||||||
set(VBAM_WX_COMMON
|
set(VBAM_WX_COMMON
|
||||||
|
audio/audio.cpp
|
||||||
|
audio/audio.h
|
||||||
|
audio/internal/openal.cpp
|
||||||
|
audio/internal/openal.h
|
||||||
background-input.cpp
|
background-input.cpp
|
||||||
background-input.h
|
background-input.h
|
||||||
cmdevents.cpp
|
cmdevents.cpp
|
||||||
|
@ -47,8 +51,6 @@ set(VBAM_WX_COMMON
|
||||||
gfxviewers.cpp
|
gfxviewers.cpp
|
||||||
guiinit.cpp
|
guiinit.cpp
|
||||||
ioregs.h
|
ioregs.h
|
||||||
openal.cpp
|
|
||||||
openal.h
|
|
||||||
opts.cpp
|
opts.cpp
|
||||||
opts.h
|
opts.h
|
||||||
panel.cpp
|
panel.cpp
|
||||||
|
@ -249,7 +251,7 @@ endif()
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
target_sources(visualboyadvance-m
|
target_sources(visualboyadvance-m
|
||||||
PRIVATE
|
PRIVATE
|
||||||
dsound.cpp
|
audio/internal/dsound.cpp
|
||||||
wxvbam.rc
|
wxvbam.rc
|
||||||
)
|
)
|
||||||
target_link_libraries(visualboyadvance-m
|
target_link_libraries(visualboyadvance-m
|
||||||
|
@ -313,7 +315,7 @@ target_link_libraries(visualboyadvance-m ${OPENAL_LIBRARY})
|
||||||
|
|
||||||
# XAudio2.
|
# XAudio2.
|
||||||
if(ENABLE_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)
|
target_compile_definitions(visualboyadvance-m PRIVATE VBAM_ENABLE_XAUDIO2)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -324,7 +326,7 @@ endif()
|
||||||
|
|
||||||
# FAudio.
|
# FAudio.
|
||||||
if(ENABLE_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_link_libraries(visualboyadvance-m FAudio::FAudio)
|
||||||
target_compile_definitions(visualboyadvance-m PRIVATE VBAM_ENABLE_FAUDIO)
|
target_compile_definitions(visualboyadvance-m PRIVATE VBAM_ENABLE_FAUDIO)
|
||||||
endif()
|
endif()
|
||||||
|
@ -607,6 +609,9 @@ add_custom_command(
|
||||||
|
|
||||||
set(VBAM_LOCALIZABLE_FILES ${VBAM_WX_COMMON})
|
set(VBAM_LOCALIZABLE_FILES ${VBAM_WX_COMMON})
|
||||||
list(APPEND VBAM_LOCALIZABLE_FILES
|
list(APPEND VBAM_LOCALIZABLE_FILES
|
||||||
|
audio/internal/dsound.cpp
|
||||||
|
audio/internal/faudio.cpp
|
||||||
|
audio/internal/xaudio2.cpp
|
||||||
autoupdater/autoupdater.h
|
autoupdater/autoupdater.h
|
||||||
autoupdater/macos/autoupdater.cpp
|
autoupdater/macos/autoupdater.cpp
|
||||||
autoupdater/macos/sparkle-wrapper.h
|
autoupdater/macos/sparkle-wrapper.h
|
||||||
|
@ -614,11 +619,8 @@ list(APPEND VBAM_LOCALIZABLE_FILES
|
||||||
autoupdater/wxmsw/winsparkle-rc.h
|
autoupdater/wxmsw/winsparkle-rc.h
|
||||||
autoupdater/wxmsw/winsparkle-wrapper.cpp
|
autoupdater/wxmsw/winsparkle-wrapper.cpp
|
||||||
autoupdater/wxmsw/winsparkle-wrapper.h
|
autoupdater/wxmsw/winsparkle-wrapper.h
|
||||||
dsound.cpp
|
|
||||||
faudio.cpp
|
|
||||||
widgets/dpi-support.cpp
|
widgets/dpi-support.cpp
|
||||||
widgets/dpi-support-mac.mm
|
widgets/dpi-support-mac.mm
|
||||||
xaudio2.cpp
|
|
||||||
${CMAKE_SOURCE_DIR}/src/core/gba/gbaLink.cpp
|
${CMAKE_SOURCE_DIR}/src/core/gba/gbaLink.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
#include "wx/audio/audio.h"
|
||||||
|
|
||||||
|
#include "wx/audio/internal/openal.h"
|
||||||
|
|
||||||
|
#if defined(__WXMSW__)
|
||||||
|
#include "wx/audio/internal/dsound.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(VBAM_ENABLE_FAUDIO)
|
||||||
|
#include "wx/audio/internal/faudio.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(VBAM_ENABLE_XAUDIO2)
|
||||||
|
#include "wx/audio/internal/xaudio2.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace audio {
|
||||||
|
|
||||||
|
std::vector<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
|
|
@ -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_
|
|
@ -2,6 +2,8 @@
|
||||||
#error "This file should only be compiled on Windows"
|
#error "This file should only be compiled on Windows"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "wx/audio/internal/dsound.h"
|
||||||
|
|
||||||
// DirectSound8
|
// DirectSound8
|
||||||
#define DIRECTSOUND_VERSION 0x0800
|
#define DIRECTSOUND_VERSION 0x0800
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
@ -24,14 +26,19 @@
|
||||||
|
|
||||||
extern bool soundBufferLow;
|
extern bool soundBufferLow;
|
||||||
|
|
||||||
|
namespace audio {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
class DirectSound : public SoundDriver {
|
class DirectSound : public SoundDriver {
|
||||||
private:
|
private:
|
||||||
LPDIRECTSOUND8 pDirectSound; // DirectSound interface
|
LPDIRECTSOUND8 pDirectSound; // DirectSound interface
|
||||||
LPDIRECTSOUNDBUFFER dsbPrimary; // Primary DirectSound buffer
|
LPDIRECTSOUNDBUFFER dsbPrimary; // Primary DirectSound buffer
|
||||||
LPDIRECTSOUNDBUFFER dsbSecondary; // Secondary DirectSound buffer
|
LPDIRECTSOUNDBUFFER dsbSecondary; // Secondary DirectSound buffer
|
||||||
LPDIRECTSOUNDNOTIFY dsbNotify;
|
LPDIRECTSOUNDNOTIFY dsbNotify;
|
||||||
HANDLE dsbEvent;
|
HANDLE dsbEvent;
|
||||||
WAVEFORMATEX wfx; // Primary buffer wave format
|
WAVEFORMATEX wfx; // Primary buffer wave format
|
||||||
int soundBufferLen;
|
int soundBufferLen;
|
||||||
int soundBufferTotalLen;
|
int soundBufferTotalLen;
|
||||||
unsigned int soundNextPosition;
|
unsigned int soundNextPosition;
|
||||||
|
@ -45,12 +52,11 @@ public:
|
||||||
void pause() override;
|
void pause() override;
|
||||||
void reset() override;
|
void reset() override;
|
||||||
void resume() 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;
|
void setThrottle(unsigned short throttle_) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
DirectSound::DirectSound()
|
DirectSound::DirectSound() {
|
||||||
{
|
|
||||||
pDirectSound = NULL;
|
pDirectSound = NULL;
|
||||||
dsbPrimary = NULL;
|
dsbPrimary = NULL;
|
||||||
dsbSecondary = NULL;
|
dsbSecondary = NULL;
|
||||||
|
@ -60,8 +66,7 @@ DirectSound::DirectSound()
|
||||||
soundNextPosition = 0;
|
soundNextPosition = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
DirectSound::~DirectSound()
|
DirectSound::~DirectSound() {
|
||||||
{
|
|
||||||
if (dsbNotify) {
|
if (dsbNotify) {
|
||||||
dsbNotify->Release();
|
dsbNotify->Release();
|
||||||
dsbNotify = NULL;
|
dsbNotify = NULL;
|
||||||
|
@ -88,13 +93,13 @@ DirectSound::~DirectSound()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DirectSound::init(long sampleRate)
|
bool DirectSound::init(long sampleRate) {
|
||||||
{
|
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
DWORD freq;
|
DWORD freq;
|
||||||
DSBUFFERDESC dsbdesc;
|
DSBUFFERDESC dsbdesc;
|
||||||
int i;
|
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) {
|
if (hr != S_OK) {
|
||||||
wxLogError(_("Cannot create Direct Sound %08x"), hr);
|
wxLogError(_("Cannot create Direct Sound %08x"), hr);
|
||||||
|
@ -116,7 +121,8 @@ bool DirectSound::init(long sampleRate)
|
||||||
return false;
|
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);
|
wxLogError(_("Cannot SetCooperativeLevel %08x"), hr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -158,7 +164,8 @@ bool DirectSound::init(long sampleRate)
|
||||||
// Create secondary sound buffer
|
// Create secondary sound buffer
|
||||||
ZeroMemory(&dsbdesc, sizeof(DSBUFFERDESC));
|
ZeroMemory(&dsbdesc, sizeof(DSBUFFERDESC));
|
||||||
dsbdesc.dwSize = 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) {
|
if (!hw_accel) {
|
||||||
dsbdesc.dwFlags |= DSBCAPS_LOCSOFTWARE;
|
dsbdesc.dwFlags |= DSBCAPS_LOCSOFTWARE;
|
||||||
|
@ -177,7 +184,8 @@ bool DirectSound::init(long sampleRate)
|
||||||
return false;
|
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);
|
dsbEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||||
DSBPOSITIONNOTIFY notify[10];
|
DSBPOSITIONNOTIFY notify[10];
|
||||||
|
|
||||||
|
@ -207,7 +215,7 @@ void DirectSound::setThrottle(unsigned short throttle_) {
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
|
||||||
if (throttle_ == 0)
|
if (throttle_ == 0)
|
||||||
throttle_ = 450; // Close to upper bound on frequency.
|
throttle_ = 450; // Close to upper bound on frequency.
|
||||||
|
|
||||||
long freq = soundGetSampleRate();
|
long freq = soundGetSampleRate();
|
||||||
|
|
||||||
|
@ -216,8 +224,7 @@ void DirectSound::setThrottle(unsigned short throttle_) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DirectSound::pause()
|
void DirectSound::pause() {
|
||||||
{
|
|
||||||
LPDIRECTSOUNDBUFFER bufs[] = {dsbPrimary, dsbSecondary};
|
LPDIRECTSOUNDBUFFER bufs[] = {dsbPrimary, dsbSecondary};
|
||||||
for (auto buf : bufs) {
|
for (auto buf : bufs) {
|
||||||
if (buf == NULL)
|
if (buf == NULL)
|
||||||
|
@ -231,8 +238,7 @@ void DirectSound::pause()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DirectSound::reset()
|
void DirectSound::reset() {
|
||||||
{
|
|
||||||
if (dsbSecondary == NULL)
|
if (dsbSecondary == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -241,8 +247,7 @@ void DirectSound::reset()
|
||||||
soundNextPosition = 0;
|
soundNextPosition = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DirectSound::resume()
|
void DirectSound::resume() {
|
||||||
{
|
|
||||||
LPDIRECTSOUNDBUFFER bufs[] = {dsbPrimary, dsbSecondary};
|
LPDIRECTSOUNDBUFFER bufs[] = {dsbPrimary, dsbSecondary};
|
||||||
for (auto buf : bufs) {
|
for (auto buf : bufs) {
|
||||||
if (buf == NULL)
|
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)
|
if (!pDirectSound)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -272,7 +276,9 @@ void DirectSound::write(uint16_t* finalWave, int)
|
||||||
if (!soundPaused) {
|
if (!soundPaused) {
|
||||||
while (true) {
|
while (true) {
|
||||||
dsbSecondary->GetCurrentPosition(&play, NULL);
|
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 > soundBufferLen) {
|
||||||
if (BufferLeft > soundBufferTotalLen - (soundBufferLen * 3))
|
if (BufferLeft > soundBufferTotalLen - (soundBufferLen * 3))
|
||||||
|
@ -297,24 +303,12 @@ void DirectSound::write(uint16_t* finalWave, int)
|
||||||
|
|
||||||
// Obtain memory address of write block.
|
// Obtain memory address of write block.
|
||||||
// This will be in two parts if the block wraps around.
|
// This will be in two parts if the block wraps around.
|
||||||
if (DSERR_BUFFERLOST == (hr = dsbSecondary->Lock(
|
if (DSERR_BUFFERLOST == (hr = dsbSecondary->Lock(soundNextPosition, soundBufferLen, &lpvPtr1,
|
||||||
soundNextPosition,
|
&dwBytes1, &lpvPtr2, &dwBytes2, 0))) {
|
||||||
soundBufferLen,
|
|
||||||
&lpvPtr1,
|
|
||||||
&dwBytes1,
|
|
||||||
&lpvPtr2,
|
|
||||||
&dwBytes2,
|
|
||||||
0))) {
|
|
||||||
// If DSERR_BUFFERLOST is returned, restore and retry lock.
|
// If DSERR_BUFFERLOST is returned, restore and retry lock.
|
||||||
dsbSecondary->Restore();
|
dsbSecondary->Restore();
|
||||||
hr = dsbSecondary->Lock(
|
hr = dsbSecondary->Lock(soundNextPosition, soundBufferLen, &lpvPtr1, &dwBytes1, &lpvPtr2,
|
||||||
soundNextPosition,
|
&dwBytes2, 0);
|
||||||
soundBufferLen,
|
|
||||||
&lpvPtr1,
|
|
||||||
&dwBytes1,
|
|
||||||
&lpvPtr2,
|
|
||||||
&dwBytes2,
|
|
||||||
0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
soundNextPosition += soundBufferLen;
|
soundNextPosition += soundBufferLen;
|
||||||
|
@ -336,27 +330,33 @@ void DirectSound::write(uint16_t* finalWave, int)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<SoundDriver> newDirectSound()
|
static BOOL CALLBACK DSEnumCB(LPGUID guid, LPCTSTR desc, LPCTSTR /*module*/, LPVOID user) {
|
||||||
{
|
std::vector<AudioDevice>* devices = static_cast<std::vector<AudioDevice>*>(user);
|
||||||
return std::make_unique<DirectSound>();
|
|
||||||
}
|
|
||||||
|
|
||||||
struct devnames {
|
if (guid == nullptr) {
|
||||||
wxArrayString *names, *ids;
|
devices->push_back({desc, {}});
|
||||||
};
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
static BOOL CALLBACK DSEnumCB(LPGUID guid, LPCTSTR desc, LPCTSTR, LPVOID user)
|
static constexpr size_t kGuidLength = 32 + 4 + 2 + 1; // hex digits + "-" + "{}" + \0
|
||||||
{
|
std::array<WCHAR, kGuidLength> device_id;
|
||||||
devnames* dn = (devnames*)user;
|
StringFromGUID2(*guid, device_id.data(), device_id.size());
|
||||||
dn->names->push_back(desc);
|
|
||||||
WCHAR buf[32 + 4 + 2 + 1]; // hex digits + "-" + "{}" + \0
|
devices->push_back({desc, device_id.data()});
|
||||||
StringFromGUID2(*guid, buf, sizeof(buf));
|
|
||||||
dn->ids->push_back(buf);
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetDSDevices(wxArrayString& names, wxArrayString& ids)
|
} // namespace
|
||||||
{
|
|
||||||
devnames dn = { &names, &ids };
|
std::vector<AudioDevice> GetDirectSoundDevices() {
|
||||||
return DirectSoundEnumerate(DSEnumCB, (LPVOID)&dn) == DS_OK;
|
std::vector<AudioDevice> devices;
|
||||||
|
DirectSoundEnumerateW(DSEnumCB, &devices);
|
||||||
|
return devices;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<SoundDriver> CreateDirectSoundDriver() {
|
||||||
|
return std::make_unique<DirectSound>();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace audio
|
|
@ -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_
|
|
@ -1,10 +1,12 @@
|
||||||
|
#include <wx/string.h>
|
||||||
#if !defined(VBAM_ENABLE_FAUDIO)
|
#if !defined(VBAM_ENABLE_FAUDIO)
|
||||||
#error "This file should only be compiled if FAudio is enabled"
|
#error "This file should only be compiled if FAudio is enabled"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <cstdio>
|
#include "wx/audio/internal/faudio.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
// FAudio
|
// FAudio
|
||||||
|
@ -16,63 +18,50 @@
|
||||||
#include <wx/log.h>
|
#include <wx/log.h>
|
||||||
#include <wx/translation.h>
|
#include <wx/translation.h>
|
||||||
|
|
||||||
#include "core/base/sound_driver.h"
|
|
||||||
#include "core/base/system.h"
|
#include "core/base/system.h"
|
||||||
#include "core/gba/gbaGlobals.h"
|
#include "core/gba/gbaGlobals.h"
|
||||||
#include "wx/config/option-proxy.h"
|
#include "wx/config/option-proxy.h"
|
||||||
|
|
||||||
|
namespace audio {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
int GetFADevices(FAudio* fa, wxArrayString* names, wxArrayString* ids,
|
int FAGetDev(FAudio* fa) {
|
||||||
const wxString* match)
|
const wxString& audio_device = OPTION(kSoundAudioDevice);
|
||||||
{
|
if (audio_device.empty()) {
|
||||||
|
// Just use the default device.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t hr;
|
uint32_t hr;
|
||||||
uint32_t dev_count = 0;
|
uint32_t dev_count = 0;
|
||||||
hr = FAudio_GetDeviceCount(fa, &dev_count);
|
hr = FAudio_GetDeviceCount(fa, &dev_count);
|
||||||
|
|
||||||
if (hr != 0) {
|
if (hr != 0) {
|
||||||
wxLogError(_("FAudio: Enumerating devices failed!"));
|
wxLogError(_("FAudio: Enumerating devices failed!"));
|
||||||
return -1;
|
return 0;
|
||||||
} else {
|
}
|
||||||
FAudioDeviceDetails dd;
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < dev_count; i++) {
|
FAudioDeviceDetails dd;
|
||||||
hr = FAudio_GetDeviceDetails(fa, i, &dd);
|
for (uint32_t i = 0; i < dev_count; i++) {
|
||||||
|
hr = FAudio_GetDeviceDetails(fa, i, &dd);
|
||||||
if (hr != 0) {
|
if (hr != 0) {
|
||||||
continue;
|
continue;
|
||||||
} else {
|
}
|
||||||
if (ids) {
|
const wxString device_id(reinterpret_cast<wchar_t*>(dd.DeviceID));
|
||||||
ids->push_back((wchar_t*) dd.DeviceID);
|
if (audio_device == device_id) {
|
||||||
names->push_back((wchar_t*) dd.DisplayName);
|
return i;
|
||||||
} else if (*match == wxString((wchar_t*) dd.DeviceID))
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
class FAudio_BufferNotify : public FAudioVoiceCallback {
|
||||||
public:
|
public:
|
||||||
bool WaitForSignal() {
|
bool WaitForSignal() { return WaitForSingleObject(buffer_end_event_, 10000) != WAIT_TIMEOUT; }
|
||||||
return WaitForSingleObject(buffer_end_event_, 10000) != WAIT_TIMEOUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
FAudio_BufferNotify()
|
FAudio_BufferNotify() {
|
||||||
{
|
|
||||||
buffer_end_event_ = nullptr;
|
buffer_end_event_ = nullptr;
|
||||||
buffer_end_event_ = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
buffer_end_event_ = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||||
assert(buffer_end_event_ != nullptr);
|
assert(buffer_end_event_ != nullptr);
|
||||||
|
@ -85,8 +74,7 @@ public:
|
||||||
OnLoopEnd = &FAudio_BufferNotify::StaticOnLoopEnd;
|
OnLoopEnd = &FAudio_BufferNotify::StaticOnLoopEnd;
|
||||||
OnVoiceError = &FAudio_BufferNotify::StaticOnVoiceError;
|
OnVoiceError = &FAudio_BufferNotify::StaticOnVoiceError;
|
||||||
}
|
}
|
||||||
~FAudio_BufferNotify()
|
~FAudio_BufferNotify() {
|
||||||
{
|
|
||||||
CloseHandle(buffer_end_event_);
|
CloseHandle(buffer_end_event_);
|
||||||
buffer_end_event_ = nullptr;
|
buffer_end_event_ = nullptr;
|
||||||
}
|
}
|
||||||
|
@ -108,8 +96,7 @@ private:
|
||||||
static void StaticOnVoiceError(FAudioVoiceCallback*, void*, uint32_t) {}
|
static void StaticOnVoiceError(FAudioVoiceCallback*, void*, uint32_t) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class FAudio_Output
|
class FAudio_Output : public SoundDriver {
|
||||||
: public SoundDriver {
|
|
||||||
public:
|
public:
|
||||||
FAudio_Output();
|
FAudio_Output();
|
||||||
~FAudio_Output();
|
~FAudio_Output();
|
||||||
|
@ -139,11 +126,11 @@ private:
|
||||||
volatile bool device_changed;
|
volatile bool device_changed;
|
||||||
|
|
||||||
FAudio* faud;
|
FAudio* faud;
|
||||||
FAudioMasteringVoice* mVoice; // listener
|
FAudioMasteringVoice* mVoice; // listener
|
||||||
FAudioSourceVoice* sVoice; // sound source
|
FAudioSourceVoice* sVoice; // sound source
|
||||||
FAudioBuffer buf;
|
FAudioBuffer buf;
|
||||||
FAudioVoiceState vState;
|
FAudioVoiceState vState;
|
||||||
FAudio_BufferNotify notify; // buffer end notification
|
FAudio_BufferNotify notify; // buffer end notification
|
||||||
};
|
};
|
||||||
|
|
||||||
class FAudio_Device_Notifier : public IMMNotificationClient {
|
class FAudio_Device_Notifier : public IMMNotificationClient {
|
||||||
|
@ -162,7 +149,9 @@ public:
|
||||||
ULONG STDMETHODCALLTYPE AddRef();
|
ULONG STDMETHODCALLTYPE AddRef();
|
||||||
ULONG STDMETHODCALLTYPE Release();
|
ULONG STDMETHODCALLTYPE Release();
|
||||||
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID** ppvInterface);
|
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 OnDeviceAdded(LPCWSTR pwstrDeviceId);
|
||||||
HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId);
|
HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId);
|
||||||
HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState);
|
HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState);
|
||||||
|
@ -190,14 +179,12 @@ FAudio_Output::FAudio_Output() : buffer_count_(OPTION(kSoundBuffers)) {
|
||||||
f_notifier.do_register(this);
|
f_notifier.do_register(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
FAudio_Output::~FAudio_Output()
|
FAudio_Output::~FAudio_Output() {
|
||||||
{
|
|
||||||
f_notifier.do_unregister(this);
|
f_notifier.do_unregister(this);
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FAudio_Output::close()
|
void FAudio_Output::close() {
|
||||||
{
|
|
||||||
initialized = false;
|
initialized = false;
|
||||||
|
|
||||||
if (sVoice) {
|
if (sVoice) {
|
||||||
|
@ -220,22 +207,20 @@ void FAudio_Output::close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FAudio_Output::device_change()
|
void FAudio_Output::device_change() {
|
||||||
{
|
|
||||||
device_changed = true;
|
device_changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FAudio_Output::init(long sampleRate)
|
bool FAudio_Output::init(long sampleRate) {
|
||||||
{
|
|
||||||
if (failed || initialized)
|
if (failed || initialized)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
uint32_t hr;
|
uint32_t hr;
|
||||||
// Initialize FAudio
|
// Initialize FAudio
|
||||||
uint32_t flags = 0;
|
uint32_t flags = 0;
|
||||||
//#ifdef _DEBUG
|
// #ifdef _DEBUG
|
||||||
// flags = FAUDIO_DEBUG_ENGINE;
|
// flags = FAUDIO_DEBUG_ENGINE;
|
||||||
//#endif
|
// #endif
|
||||||
hr = FAudioCreate(&faud, flags, FAUDIO_DEFAULT_PROCESSOR);
|
hr = FAudioCreate(&faud, flags, FAUDIO_DEFAULT_PROCESSOR);
|
||||||
|
|
||||||
if (hr != 0) {
|
if (hr != 0) {
|
||||||
|
@ -255,8 +240,8 @@ bool FAudio_Output::init(long sampleRate)
|
||||||
static const uint16_t kNumChannels = 2;
|
static const uint16_t kNumChannels = 2;
|
||||||
static const uint16_t kBitsPerSample = 16;
|
static const uint16_t kBitsPerSample = 16;
|
||||||
static const uint16_t kBlockAlign = kNumChannels * (kBitsPerSample / 8);
|
static const uint16_t kBlockAlign = kNumChannels * (kBitsPerSample / 8);
|
||||||
FAudioWaveFormatEx wfx {
|
FAudioWaveFormatEx wfx{
|
||||||
/*.wFormatTag=*/ FAUDIO_FORMAT_PCM,
|
/*.wFormatTag=*/FAUDIO_FORMAT_PCM,
|
||||||
/*.nChannels=*/kNumChannels,
|
/*.nChannels=*/kNumChannels,
|
||||||
/*.nSamplesPerSec=*/freq_,
|
/*.nSamplesPerSec=*/freq_,
|
||||||
/*.nAvgBytesPerSec=*/freq_ * kBlockAlign,
|
/*.nAvgBytesPerSec=*/freq_ * kBlockAlign,
|
||||||
|
@ -286,96 +271,96 @@ bool FAudio_Output::init(long sampleRate)
|
||||||
|
|
||||||
if (OPTION(kSoundUpmix)) {
|
if (OPTION(kSoundUpmix)) {
|
||||||
// set up stereo upmixing
|
// set up stereo upmixing
|
||||||
FAudioDeviceDetails dd {};
|
FAudioDeviceDetails dd{};
|
||||||
assert(FAudio_GetDeviceDetails(faud, 0, &dd) == 0);
|
assert(FAudio_GetDeviceDetails(faud, 0, &dd) == 0);
|
||||||
std::vector<float> matrix(sizeof(float) * 2 * dd.OutputFormat.Format.nChannels);
|
std::vector<float> matrix(sizeof(float) * 2 * dd.OutputFormat.Format.nChannels);
|
||||||
|
|
||||||
bool matrixAvailable = true;
|
bool matrixAvailable = true;
|
||||||
|
|
||||||
switch (dd.OutputFormat.Format.nChannels) {
|
switch (dd.OutputFormat.Format.nChannels) {
|
||||||
case 4: // 4.0
|
case 4: // 4.0
|
||||||
//Speaker \ Left Source Right Source
|
// Speaker \ Left Source Right Source
|
||||||
/*Front L*/ matrix[0] = 1.0000f;
|
/*Front L*/ matrix[0] = 1.0000f;
|
||||||
matrix[1] = 0.0000f;
|
matrix[1] = 0.0000f;
|
||||||
/*Front R*/ matrix[2] = 0.0000f;
|
/*Front R*/ matrix[2] = 0.0000f;
|
||||||
matrix[3] = 1.0000f;
|
matrix[3] = 1.0000f;
|
||||||
/*Back L*/ matrix[4] = 1.0000f;
|
/*Back L*/ matrix[4] = 1.0000f;
|
||||||
matrix[5] = 0.0000f;
|
matrix[5] = 0.0000f;
|
||||||
/*Back R*/ matrix[6] = 0.0000f;
|
/*Back R*/ matrix[6] = 0.0000f;
|
||||||
matrix[7] = 1.0000f;
|
matrix[7] = 1.0000f;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 5: // 5.0
|
case 5: // 5.0
|
||||||
//Speaker \ Left Source Right Source
|
// Speaker \ Left Source Right Source
|
||||||
/*Front L*/ matrix[0] = 1.0000f;
|
/*Front L*/ matrix[0] = 1.0000f;
|
||||||
matrix[1] = 0.0000f;
|
matrix[1] = 0.0000f;
|
||||||
/*Front R*/ matrix[2] = 0.0000f;
|
/*Front R*/ matrix[2] = 0.0000f;
|
||||||
matrix[3] = 1.0000f;
|
matrix[3] = 1.0000f;
|
||||||
/*Front C*/ matrix[4] = 0.7071f;
|
/*Front C*/ matrix[4] = 0.7071f;
|
||||||
matrix[5] = 0.7071f;
|
matrix[5] = 0.7071f;
|
||||||
/*Side L*/ matrix[6] = 1.0000f;
|
/*Side L*/ matrix[6] = 1.0000f;
|
||||||
matrix[7] = 0.0000f;
|
matrix[7] = 0.0000f;
|
||||||
/*Side R*/ matrix[8] = 0.0000f;
|
/*Side R*/ matrix[8] = 0.0000f;
|
||||||
matrix[9] = 1.0000f;
|
matrix[9] = 1.0000f;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 6: // 5.1
|
case 6: // 5.1
|
||||||
//Speaker \ Left Source Right Source
|
// Speaker \ Left Source Right Source
|
||||||
/*Front L*/ matrix[0] = 1.0000f;
|
/*Front L*/ matrix[0] = 1.0000f;
|
||||||
matrix[1] = 0.0000f;
|
matrix[1] = 0.0000f;
|
||||||
/*Front R*/ matrix[2] = 0.0000f;
|
/*Front R*/ matrix[2] = 0.0000f;
|
||||||
matrix[3] = 1.0000f;
|
matrix[3] = 1.0000f;
|
||||||
/*Front C*/ matrix[4] = 0.7071f;
|
/*Front C*/ matrix[4] = 0.7071f;
|
||||||
matrix[5] = 0.7071f;
|
matrix[5] = 0.7071f;
|
||||||
/*LFE */ matrix[6] = 0.0000f;
|
/*LFE */ matrix[6] = 0.0000f;
|
||||||
matrix[7] = 0.0000f;
|
matrix[7] = 0.0000f;
|
||||||
/*Side L*/ matrix[8] = 1.0000f;
|
/*Side L*/ matrix[8] = 1.0000f;
|
||||||
matrix[9] = 0.0000f;
|
matrix[9] = 0.0000f;
|
||||||
/*Side R*/ matrix[10] = 0.0000f;
|
/*Side R*/ matrix[10] = 0.0000f;
|
||||||
matrix[11] = 1.0000f;
|
matrix[11] = 1.0000f;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 7: // 6.1
|
case 7: // 6.1
|
||||||
//Speaker \ Left Source Right Source
|
// Speaker \ Left Source Right Source
|
||||||
/*Front L*/ matrix[0] = 1.0000f;
|
/*Front L*/ matrix[0] = 1.0000f;
|
||||||
matrix[1] = 0.0000f;
|
matrix[1] = 0.0000f;
|
||||||
/*Front R*/ matrix[2] = 0.0000f;
|
/*Front R*/ matrix[2] = 0.0000f;
|
||||||
matrix[3] = 1.0000f;
|
matrix[3] = 1.0000f;
|
||||||
/*Front C*/ matrix[4] = 0.7071f;
|
/*Front C*/ matrix[4] = 0.7071f;
|
||||||
matrix[5] = 0.7071f;
|
matrix[5] = 0.7071f;
|
||||||
/*LFE */ matrix[6] = 0.0000f;
|
/*LFE */ matrix[6] = 0.0000f;
|
||||||
matrix[7] = 0.0000f;
|
matrix[7] = 0.0000f;
|
||||||
/*Side L*/ matrix[8] = 1.0000f;
|
/*Side L*/ matrix[8] = 1.0000f;
|
||||||
matrix[9] = 0.0000f;
|
matrix[9] = 0.0000f;
|
||||||
/*Side R*/ matrix[10] = 0.0000f;
|
/*Side R*/ matrix[10] = 0.0000f;
|
||||||
matrix[11] = 1.0000f;
|
matrix[11] = 1.0000f;
|
||||||
/*Back C*/ matrix[12] = 0.7071f;
|
/*Back C*/ matrix[12] = 0.7071f;
|
||||||
matrix[13] = 0.7071f;
|
matrix[13] = 0.7071f;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 8: // 7.1
|
case 8: // 7.1
|
||||||
//Speaker \ Left Source Right Source
|
// Speaker \ Left Source Right Source
|
||||||
/*Front L*/ matrix[0] = 1.0000f;
|
/*Front L*/ matrix[0] = 1.0000f;
|
||||||
matrix[1] = 0.0000f;
|
matrix[1] = 0.0000f;
|
||||||
/*Front R*/ matrix[2] = 0.0000f;
|
/*Front R*/ matrix[2] = 0.0000f;
|
||||||
matrix[3] = 1.0000f;
|
matrix[3] = 1.0000f;
|
||||||
/*Front C*/ matrix[4] = 0.7071f;
|
/*Front C*/ matrix[4] = 0.7071f;
|
||||||
matrix[5] = 0.7071f;
|
matrix[5] = 0.7071f;
|
||||||
/*LFE */ matrix[6] = 0.0000f;
|
/*LFE */ matrix[6] = 0.0000f;
|
||||||
matrix[7] = 0.0000f;
|
matrix[7] = 0.0000f;
|
||||||
/*Back L*/ matrix[8] = 1.0000f;
|
/*Back L*/ matrix[8] = 1.0000f;
|
||||||
matrix[9] = 0.0000f;
|
matrix[9] = 0.0000f;
|
||||||
/*Back R*/ matrix[10] = 0.0000f;
|
/*Back R*/ matrix[10] = 0.0000f;
|
||||||
matrix[11] = 1.0000f;
|
matrix[11] = 1.0000f;
|
||||||
/*Side L*/ matrix[12] = 1.0000f;
|
/*Side L*/ matrix[12] = 1.0000f;
|
||||||
matrix[13] = 0.0000f;
|
matrix[13] = 0.0000f;
|
||||||
/*Side R*/ matrix[14] = 0.0000f;
|
/*Side R*/ matrix[14] = 0.0000f;
|
||||||
matrix[15] = 1.0000f;
|
matrix[15] = 1.0000f;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
matrixAvailable = false;
|
matrixAvailable = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matrixAvailable) {
|
if (matrixAvailable) {
|
||||||
|
@ -445,8 +430,7 @@ void FAudio_Output::write(uint16_t* finalWave, int) {
|
||||||
assert(hr == 0);
|
assert(hr == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FAudio_Output::pause()
|
void FAudio_Output::pause() {
|
||||||
{
|
|
||||||
if (!initialized || failed)
|
if (!initialized || failed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -457,8 +441,7 @@ void FAudio_Output::pause()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FAudio_Output::resume()
|
void FAudio_Output::resume() {
|
||||||
{
|
|
||||||
if (!initialized || failed)
|
if (!initialized || failed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -469,8 +452,7 @@ void FAudio_Output::resume()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FAudio_Output::reset()
|
void FAudio_Output::reset() {
|
||||||
{
|
|
||||||
if (!initialized || failed)
|
if (!initialized || failed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -484,8 +466,7 @@ void FAudio_Output::reset()
|
||||||
playing = true;
|
playing = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FAudio_Output::setThrottle(unsigned short throttle_)
|
void FAudio_Output::setThrottle(unsigned short throttle_) {
|
||||||
{
|
|
||||||
if (!initialized || failed)
|
if (!initialized || failed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -497,28 +478,22 @@ void FAudio_Output::setThrottle(unsigned short throttle_)
|
||||||
assert(hr == 0);
|
assert(hr == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
FAudio_Device_Notifier::FAudio_Device_Notifier()
|
FAudio_Device_Notifier::FAudio_Device_Notifier() : registered(0) {
|
||||||
: registered(0)
|
|
||||||
{
|
|
||||||
InitializeCriticalSection(&lock);
|
InitializeCriticalSection(&lock);
|
||||||
}
|
}
|
||||||
FAudio_Device_Notifier::~FAudio_Device_Notifier()
|
FAudio_Device_Notifier::~FAudio_Device_Notifier() {
|
||||||
{
|
|
||||||
DeleteCriticalSection(&lock);
|
DeleteCriticalSection(&lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
ULONG STDMETHODCALLTYPE FAudio_Device_Notifier::AddRef()
|
ULONG STDMETHODCALLTYPE FAudio_Device_Notifier::AddRef() {
|
||||||
{
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ULONG STDMETHODCALLTYPE FAudio_Device_Notifier::Release()
|
ULONG STDMETHODCALLTYPE FAudio_Device_Notifier::Release() {
|
||||||
{
|
|
||||||
return 1;
|
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) {
|
if (IID_IUnknown == riid) {
|
||||||
*ppvInterface = (IUnknown*)this;
|
*ppvInterface = (IUnknown*)this;
|
||||||
} else if (__uuidof(IMMNotificationClient) == riid) {
|
} else if (__uuidof(IMMNotificationClient) == riid) {
|
||||||
|
@ -562,8 +537,7 @@ HRESULT STDMETHODCALLTYPE FAudio_Device_Notifier::OnPropertyValueChanged(LPCWSTR
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FAudio_Device_Notifier::do_register(FAudio_Output* p_instance)
|
void FAudio_Device_Notifier::do_register(FAudio_Output* p_instance) {
|
||||||
{
|
|
||||||
if (InterlockedIncrement(®istered) == 1) {
|
if (InterlockedIncrement(®istered) == 1) {
|
||||||
pEnumerator = nullptr;
|
pEnumerator = nullptr;
|
||||||
HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER,
|
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);
|
LeaveCriticalSection(&lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FAudio_Device_Notifier::do_unregister(FAudio_Output* p_instance)
|
void FAudio_Device_Notifier::do_unregister(FAudio_Output* p_instance) {
|
||||||
{
|
|
||||||
if (InterlockedDecrement(®istered) == 0) {
|
if (InterlockedDecrement(®istered) == 0) {
|
||||||
if (pEnumerator) {
|
if (pEnumerator) {
|
||||||
pEnumerator->UnregisterEndpointNotificationCallback(this);
|
pEnumerator->UnregisterEndpointNotificationCallback(this);
|
||||||
|
@ -601,13 +574,11 @@ void FAudio_Device_Notifier::do_unregister(FAudio_Output* p_instance)
|
||||||
LeaveCriticalSection(&lock);
|
LeaveCriticalSection(&lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
bool GetFADevices(wxArrayString& names, wxArrayString& ids)
|
std::vector<AudioDevice> GetFAudioDevices() {
|
||||||
{
|
|
||||||
uint32_t hr;
|
|
||||||
FAudio* fa = nullptr;
|
FAudio* fa = nullptr;
|
||||||
|
uint32_t hr;
|
||||||
uint32_t flags = 0;
|
uint32_t flags = 0;
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
flags = FAUDIO_DEBUG_ENGINE;
|
flags = FAUDIO_DEBUG_ENGINE;
|
||||||
|
@ -616,15 +587,40 @@ bool GetFADevices(wxArrayString& names, wxArrayString& ids)
|
||||||
|
|
||||||
if (hr != 0) {
|
if (hr != 0) {
|
||||||
wxLogError(_("The FAudio interface failed to initialize!"));
|
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);
|
FAudio_Release(fa);
|
||||||
return true;
|
return devices;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Class Declaration
|
std::unique_ptr<SoundDriver> CreateFAudioDriver() {
|
||||||
std::unique_ptr<SoundDriver> newFAudio_Output() {
|
|
||||||
return std::make_unique<FAudio_Output>();
|
return std::make_unique<FAudio_Output>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace audio
|
|
@ -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_
|
|
@ -1,18 +1,51 @@
|
||||||
// === LOGALL writes very detailed informations to vba-trace.log ===
|
#include "wx/audio/internal/openal.h"
|
||||||
//#define LOGALL
|
|
||||||
|
|
||||||
#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 <cassert>
|
||||||
|
|
||||||
#include <wx/arrstr.h>
|
#include <wx/arrstr.h>
|
||||||
|
#include <wx/log.h>
|
||||||
|
#include <wx/translation.h>
|
||||||
#include <wx/utils.h>
|
#include <wx/utils.h>
|
||||||
|
|
||||||
#include "core/base/sound_driver.h"
|
|
||||||
#include "core/gba/gbaGlobals.h"
|
#include "core/gba/gbaGlobals.h"
|
||||||
#include "core/gba/gbaSound.h"
|
#include "core/gba/gbaSound.h"
|
||||||
#include "wx/config/option-proxy.h"
|
#include "wx/config/option-proxy.h"
|
||||||
|
|
||||||
|
namespace audio {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
// Debug
|
// Debug
|
||||||
#define ASSERT_SUCCESS assert(AL_NO_ERROR == alGetError())
|
#define ASSERT_SUCCESS assert(AL_NO_ERROR == alGetError())
|
||||||
|
|
||||||
|
@ -22,8 +55,10 @@
|
||||||
#undef winlog
|
#undef winlog
|
||||||
#endif
|
#endif
|
||||||
// https://stackoverflow.com/a/1306690/262458
|
// https://stackoverflow.com/a/1306690/262458
|
||||||
#define winlog(x,...) do {} while(0)
|
#define winlog(x, ...) \
|
||||||
#define debugState() //
|
do { \
|
||||||
|
} while (0)
|
||||||
|
#define debugState() //
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct OPENALFNTABLE;
|
struct OPENALFNTABLE;
|
||||||
|
@ -33,13 +68,13 @@ public:
|
||||||
OpenAL();
|
OpenAL();
|
||||||
~OpenAL() override;
|
~OpenAL() override;
|
||||||
|
|
||||||
static bool GetDevices(wxArrayString& names, wxArrayString& ids);
|
bool init(long sampleRate) override; // initialize the sound buffer queue
|
||||||
bool init(long sampleRate); // initialize the sound buffer queue
|
void setThrottle(unsigned short throttle_) override; // set game speed
|
||||||
void setThrottle(unsigned short throttle_); // set game speed
|
void pause() override; // pause the secondary sound buffer
|
||||||
void pause(); // pause the secondary sound buffer
|
void reset() override; // stop and reset the secondary sound buffer
|
||||||
void reset(); // stop and reset the secondary sound buffer
|
void resume() override; // play/resume the secondary sound buffer
|
||||||
void resume(); // play/resume the secondary sound buffer
|
void write(uint16_t* finalWave,
|
||||||
void write(uint16_t* finalWave, int length); // write the emulated sound to a sound buffer
|
int length) override; // write the emulated sound to a sound buffer
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool initialized;
|
bool initialized;
|
||||||
|
@ -57,20 +92,18 @@ private:
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
OpenAL::OpenAL()
|
OpenAL::OpenAL() {
|
||||||
{
|
|
||||||
initialized = false;
|
initialized = false;
|
||||||
buffersLoaded = false;
|
buffersLoaded = false;
|
||||||
device = NULL;
|
device = nullptr;
|
||||||
context = NULL;
|
context = nullptr;
|
||||||
buffer = (ALuint*)malloc(OPTION(kSoundBuffers) * sizeof(ALuint));
|
buffer = (ALuint*)malloc(OPTION(kSoundBuffers) * sizeof(ALuint));
|
||||||
memset(buffer, 0, OPTION(kSoundBuffers) * sizeof(ALuint));
|
memset(buffer, 0, OPTION(kSoundBuffers) * sizeof(ALuint));
|
||||||
tempBuffer = 0;
|
tempBuffer = 0;
|
||||||
source = 0;
|
source = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
OpenAL::~OpenAL()
|
OpenAL::~OpenAL() {
|
||||||
{
|
|
||||||
if (!initialized)
|
if (!initialized)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -83,22 +116,21 @@ OpenAL::~OpenAL()
|
||||||
alDeleteBuffers(OPTION(kSoundBuffers), buffer);
|
alDeleteBuffers(OPTION(kSoundBuffers), buffer);
|
||||||
ASSERT_SUCCESS;
|
ASSERT_SUCCESS;
|
||||||
free(buffer);
|
free(buffer);
|
||||||
alcMakeContextCurrent(NULL);
|
alcMakeContextCurrent(nullptr);
|
||||||
// Wine incorrectly returns ALC_INVALID_VALUE
|
// Wine incorrectly returns ALC_INVALID_VALUE
|
||||||
// and then fails the rest of these functions as well
|
// 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
|
// so there will be a leak under Wine, but that's a bug in Wine, not
|
||||||
// this code
|
// this code
|
||||||
//ASSERT_SUCCESS;
|
// ASSERT_SUCCESS;
|
||||||
alcDestroyContext(context);
|
alcDestroyContext(context);
|
||||||
//ASSERT_SUCCESS;
|
// ASSERT_SUCCESS;
|
||||||
alcCloseDevice(device);
|
alcCloseDevice(device);
|
||||||
//ASSERT_SUCCESS;
|
// ASSERT_SUCCESS;
|
||||||
alGetError(); // reset error state
|
alGetError(); // reset error state
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef LOGALL
|
#ifdef LOGALL
|
||||||
void OpenAL::debugState()
|
void OpenAL::debugState() {
|
||||||
{
|
|
||||||
ALint value = 0;
|
ALint value = 0;
|
||||||
alGetSourcei(source, AL_SOURCE_STATE, &value);
|
alGetSourcei(source, AL_SOURCE_STATE, &value);
|
||||||
ASSERT_SUCCESS;
|
ASSERT_SUCCESS;
|
||||||
|
@ -107,28 +139,27 @@ void OpenAL::debugState()
|
||||||
winlog(" State: ");
|
winlog(" State: ");
|
||||||
|
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case AL_INITIAL:
|
case AL_INITIAL:
|
||||||
winlog("AL_INITIAL\n");
|
winlog("AL_INITIAL\n");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AL_PLAYING:
|
case AL_PLAYING:
|
||||||
winlog("AL_PLAYING\n");
|
winlog("AL_PLAYING\n");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AL_PAUSED:
|
case AL_PAUSED:
|
||||||
winlog("AL_PAUSED\n");
|
winlog("AL_PAUSED\n");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AL_STOPPED:
|
case AL_STOPPED:
|
||||||
winlog("AL_STOPPED\n");
|
winlog("AL_STOPPED\n");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
winlog("!unknown!\n");
|
winlog("!unknown!\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
alGetSourcei(source, AL_BUFFERS_QUEUED, &value);
|
alGetSourcei(source, AL_BUFFERS_QUEUED, &value);
|
||||||
ASSERT_SUCCESS;
|
ASSERT_SUCCESS;
|
||||||
winlog(" Buffers in queue: %i\n", value);
|
winlog(" Buffers in queue: %i\n", value);
|
||||||
|
@ -138,21 +169,29 @@ void OpenAL::debugState()
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool OpenAL::init(long sampleRate)
|
bool OpenAL::init(long sampleRate) {
|
||||||
{
|
|
||||||
winlog("OpenAL::init\n");
|
winlog("OpenAL::init\n");
|
||||||
assert(initialized == false);
|
assert(initialized == false);
|
||||||
|
|
||||||
const wxString& audio_device = OPTION(kSoundAudioDevice);
|
const wxString& audio_device = OPTION(kSoundAudioDevice);
|
||||||
if (!audio_device.empty()) {
|
if (!audio_device.empty()) {
|
||||||
device = alcOpenDevice(audio_device.utf8_str());
|
device = alcOpenDevice(audio_device.utf8_str());
|
||||||
|
if (device == nullptr) {
|
||||||
|
// Might be the default device. Try again.
|
||||||
|
OPTION(kSoundAudioDevice) = wxEmptyString;
|
||||||
|
device = alcOpenDevice(nullptr);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
device = alcOpenDevice(NULL);
|
device = alcOpenDevice(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(device != NULL);
|
if (!device) {
|
||||||
context = alcCreateContext(device, NULL);
|
wxLogError(_("OpenAL: Failed to open audio device"));
|
||||||
assert(context != NULL);
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
context = alcCreateContext(device, nullptr);
|
||||||
|
assert(context != nullptr);
|
||||||
ALCboolean retVal = alcMakeContextCurrent(context);
|
ALCboolean retVal = alcMakeContextCurrent(context);
|
||||||
assert(ALC_TRUE == retVal);
|
assert(ALC_TRUE == retVal);
|
||||||
alGenBuffers(OPTION(kSoundBuffers), buffer);
|
alGenBuffers(OPTION(kSoundBuffers), buffer);
|
||||||
|
@ -178,8 +217,7 @@ void OpenAL::setThrottle(unsigned short throttle_) {
|
||||||
ASSERT_SUCCESS;
|
ASSERT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenAL::resume()
|
void OpenAL::resume() {
|
||||||
{
|
|
||||||
if (!initialized)
|
if (!initialized)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -201,8 +239,7 @@ void OpenAL::resume()
|
||||||
debugState();
|
debugState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenAL::pause()
|
void OpenAL::pause() {
|
||||||
{
|
|
||||||
if (!initialized)
|
if (!initialized)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -224,8 +261,7 @@ void OpenAL::pause()
|
||||||
debugState();
|
debugState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenAL::reset()
|
void OpenAL::reset() {
|
||||||
{
|
|
||||||
if (!initialized)
|
if (!initialized)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -247,9 +283,8 @@ void OpenAL::reset()
|
||||||
debugState();
|
debugState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenAL::write(uint16_t* finalWave, int length)
|
void OpenAL::write(uint16_t* finalWave, int length) {
|
||||||
{
|
(void)length; // unused param
|
||||||
(void)length; // unused param
|
|
||||||
if (!initialized)
|
if (!initialized)
|
||||||
return;
|
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");
|
winlog("newOpenAL\n");
|
||||||
return std::make_unique<OpenAL>();
|
return std::make_unique<OpenAL>();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetOALDevices(wxArrayString& names, wxArrayString& ids)
|
} // namespace internal
|
||||||
{
|
} // namespace audio
|
||||||
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;
|
|
||||||
}
|
|
|
@ -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_
|
|
@ -2,6 +2,8 @@
|
||||||
#error "This file should only be compiled if XAudio2 is enabled"
|
#error "This file should only be compiled if XAudio2 is enabled"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "wx/audio/internal/xaudio2.h"
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -21,71 +23,48 @@
|
||||||
#include <wx/translation.h>
|
#include <wx/translation.h>
|
||||||
|
|
||||||
#include "core/base/sound_driver.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 "core/gba/gbaGlobals.h"
|
||||||
#include "wx/config/option-proxy.h"
|
#include "wx/config/option-proxy.h"
|
||||||
|
|
||||||
int GetXA2Devices(IXAudio2* xa, wxArrayString* names, wxArrayString* ids,
|
namespace audio {
|
||||||
const wxString* match)
|
namespace internal {
|
||||||
{
|
|
||||||
HRESULT hr;
|
|
||||||
UINT32 dev_count = 0;
|
|
||||||
hr = xa->GetDeviceCount(&dev_count);
|
|
||||||
|
|
||||||
if (hr != S_OK) {
|
namespace {
|
||||||
wxLogError(_("XAudio2: Enumerating devices failed!"));
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
XAUDIO2_DEVICE_DETAILS dd;
|
|
||||||
|
|
||||||
for (UINT32 i = 0; i < dev_count; i++) {
|
int XA2GetDev(IXAudio2* xa) {
|
||||||
hr = xa->GetDeviceDetails(i, &dd);
|
const wxString& audio_device = OPTION(kSoundAudioDevice);
|
||||||
|
if (audio_device.empty()) {
|
||||||
if (hr != S_OK) {
|
// Just use the default device.
|
||||||
continue;
|
return 0;
|
||||||
} else {
|
|
||||||
if (ids) {
|
|
||||||
ids->push_back(dd.DeviceID);
|
|
||||||
names->push_back(dd.DisplayName);
|
|
||||||
} else if (*match == dd.DeviceID)
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
uint32_t hr;
|
||||||
}
|
uint32_t dev_count = 0;
|
||||||
|
hr = xa->GetDeviceCount(&dev_count);
|
||||||
bool GetXA2Devices(wxArrayString& names, wxArrayString& ids)
|
|
||||||
{
|
|
||||||
HRESULT hr;
|
|
||||||
IXAudio2* xa = NULL;
|
|
||||||
hr = XAudio2Create(&xa, 0);
|
|
||||||
|
|
||||||
if (hr != S_OK) {
|
if (hr != S_OK) {
|
||||||
wxLogError(_("The XAudio2 interface failed to initialize!"));
|
wxLogError(_("XAudio2: Enumerating devices failed!"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
GetXA2Devices(xa, &names, &ids, NULL);
|
for (UINT32 i = 0; i < dev_count; i++) {
|
||||||
xa->Release();
|
XAUDIO2_DEVICE_DETAILS dd;
|
||||||
return true;
|
hr = xa->GetDeviceDetails(i, &dd);
|
||||||
}
|
if (hr != S_OK) {
|
||||||
|
continue;
|
||||||
static int XA2GetDev(IXAudio2* xa)
|
}
|
||||||
{
|
const wxString device_id(reinterpret_cast<wchar_t*>(dd.DeviceID));
|
||||||
const wxString& audio_device = OPTION(kSoundAudioDevice);
|
if (audio_device == device_id) {
|
||||||
if (audio_device.empty())
|
return i;
|
||||||
return 0;
|
}
|
||||||
else {
|
|
||||||
int ret = GetXA2Devices(xa, NULL, NULL, &audio_device);
|
|
||||||
return ret < 0 ? 0 : ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
class XAudio2_Output;
|
class XAudio2_Output;
|
||||||
|
|
||||||
static void xaudio2_device_changed(XAudio2_Output*);
|
void xaudio2_device_changed(XAudio2_Output*);
|
||||||
|
|
||||||
class XAudio2_Device_Notifier : public IMMNotificationClient {
|
class XAudio2_Device_Notifier : public IMMNotificationClient {
|
||||||
volatile LONG registered;
|
volatile LONG registered;
|
||||||
|
@ -97,28 +76,14 @@ class XAudio2_Device_Notifier : public IMMNotificationClient {
|
||||||
std::vector<XAudio2_Output*> instances;
|
std::vector<XAudio2_Output*> instances;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
XAudio2_Device_Notifier()
|
XAudio2_Device_Notifier() : registered(0) { InitializeCriticalSection(&lock); }
|
||||||
: registered(0)
|
~XAudio2_Device_Notifier() { DeleteCriticalSection(&lock); }
|
||||||
{
|
|
||||||
InitializeCriticalSection(&lock);
|
|
||||||
}
|
|
||||||
~XAudio2_Device_Notifier()
|
|
||||||
{
|
|
||||||
DeleteCriticalSection(&lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
ULONG STDMETHODCALLTYPE AddRef()
|
ULONG STDMETHODCALLTYPE AddRef() { return 1; }
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ULONG STDMETHODCALLTYPE Release()
|
ULONG STDMETHODCALLTYPE Release() { return 1; }
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID** ppvInterface)
|
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID** ppvInterface) {
|
||||||
{
|
|
||||||
if (IID_IUnknown == riid) {
|
if (IID_IUnknown == riid) {
|
||||||
*ppvInterface = (IUnknown*)this;
|
*ppvInterface = (IUnknown*)this;
|
||||||
} else if (__uuidof(IMMNotificationClient) == riid) {
|
} else if (__uuidof(IMMNotificationClient) == riid) {
|
||||||
|
@ -131,8 +96,7 @@ public:
|
||||||
return S_OK;
|
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) {
|
if (flow == eRender && last_device.compare(pwstrDeviceId) != 0) {
|
||||||
last_device = pwstrDeviceId;
|
last_device = pwstrDeviceId;
|
||||||
EnterCriticalSection(&lock);
|
EnterCriticalSection(&lock);
|
||||||
|
@ -152,11 +116,11 @@ public:
|
||||||
HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR, DWORD) { return S_OK; }
|
HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR, DWORD) { return S_OK; }
|
||||||
HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(LPCWSTR, const PROPERTYKEY) { return S_OK; }
|
HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(LPCWSTR, const PROPERTYKEY) { return S_OK; }
|
||||||
|
|
||||||
void do_register(XAudio2_Output* p_instance)
|
void do_register(XAudio2_Output* p_instance) {
|
||||||
{
|
|
||||||
if (InterlockedIncrement(®istered) == 1) {
|
if (InterlockedIncrement(®istered) == 1) {
|
||||||
pEnumerator = NULL;
|
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)) {
|
if (SUCCEEDED(hr)) {
|
||||||
pEnumerator->RegisterEndpointNotificationCallback(this);
|
pEnumerator->RegisterEndpointNotificationCallback(this);
|
||||||
|
@ -168,8 +132,7 @@ public:
|
||||||
LeaveCriticalSection(&lock);
|
LeaveCriticalSection(&lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void do_unregister(XAudio2_Output* p_instance)
|
void do_unregister(XAudio2_Output* p_instance) {
|
||||||
{
|
|
||||||
if (InterlockedDecrement(®istered) == 0) {
|
if (InterlockedDecrement(®istered) == 0) {
|
||||||
if (pEnumerator) {
|
if (pEnumerator) {
|
||||||
pEnumerator->UnregisterEndpointNotificationCallback(this);
|
pEnumerator->UnregisterEndpointNotificationCallback(this);
|
||||||
|
@ -196,22 +159,19 @@ class XAudio2_BufferNotify : public IXAudio2VoiceCallback {
|
||||||
public:
|
public:
|
||||||
HANDLE hBufferEndEvent;
|
HANDLE hBufferEndEvent;
|
||||||
|
|
||||||
XAudio2_BufferNotify()
|
XAudio2_BufferNotify() {
|
||||||
{
|
|
||||||
hBufferEndEvent = NULL;
|
hBufferEndEvent = NULL;
|
||||||
hBufferEndEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
hBufferEndEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||||
assert(hBufferEndEvent != NULL);
|
assert(hBufferEndEvent != NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
~XAudio2_BufferNotify()
|
~XAudio2_BufferNotify() {
|
||||||
{
|
|
||||||
CloseHandle(hBufferEndEvent);
|
CloseHandle(hBufferEndEvent);
|
||||||
hBufferEndEvent = NULL;
|
hBufferEndEvent = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
STDMETHOD_(void, OnBufferEnd)
|
STDMETHOD_(void, OnBufferEnd)
|
||||||
(void*)
|
(void*) {
|
||||||
{
|
|
||||||
assert(hBufferEndEvent != NULL);
|
assert(hBufferEndEvent != NULL);
|
||||||
SetEvent(hBufferEndEvent);
|
SetEvent(hBufferEndEvent);
|
||||||
}
|
}
|
||||||
|
@ -232,13 +192,13 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
// Class Declaration
|
// Class Declaration
|
||||||
class XAudio2_Output
|
class XAudio2_Output : public SoundDriver {
|
||||||
: public SoundDriver {
|
|
||||||
public:
|
public:
|
||||||
XAudio2_Output();
|
XAudio2_Output();
|
||||||
~XAudio2_Output() override;
|
~XAudio2_Output() override;
|
||||||
|
|
||||||
void device_change();
|
void device_change();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void close();
|
void close();
|
||||||
|
|
||||||
|
@ -247,7 +207,7 @@ private:
|
||||||
void pause() override;
|
void pause() override;
|
||||||
void reset() override;
|
void reset() override;
|
||||||
void resume() 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;
|
void setThrottle(unsigned short throttle_) override;
|
||||||
|
|
||||||
bool failed;
|
bool failed;
|
||||||
|
@ -262,16 +222,15 @@ private:
|
||||||
volatile bool device_changed;
|
volatile bool device_changed;
|
||||||
|
|
||||||
IXAudio2* xaud;
|
IXAudio2* xaud;
|
||||||
IXAudio2MasteringVoice* mVoice; // listener
|
IXAudio2MasteringVoice* mVoice; // listener
|
||||||
IXAudio2SourceVoice* sVoice; // sound source
|
IXAudio2SourceVoice* sVoice; // sound source
|
||||||
XAUDIO2_BUFFER buf;
|
XAUDIO2_BUFFER buf;
|
||||||
XAUDIO2_VOICE_STATE vState;
|
XAUDIO2_VOICE_STATE vState;
|
||||||
XAudio2_BufferNotify notify; // buffer end notification
|
XAudio2_BufferNotify notify; // buffer end notification
|
||||||
};
|
};
|
||||||
|
|
||||||
// Class Implementation
|
// Class Implementation
|
||||||
XAudio2_Output::XAudio2_Output()
|
XAudio2_Output::XAudio2_Output() {
|
||||||
{
|
|
||||||
failed = false;
|
failed = false;
|
||||||
initialized = false;
|
initialized = false;
|
||||||
playing = false;
|
playing = false;
|
||||||
|
@ -288,14 +247,12 @@ XAudio2_Output::XAudio2_Output()
|
||||||
g_notifier.do_register(this);
|
g_notifier.do_register(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
XAudio2_Output::~XAudio2_Output()
|
XAudio2_Output::~XAudio2_Output() {
|
||||||
{
|
|
||||||
g_notifier.do_unregister(this);
|
g_notifier.do_unregister(this);
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void XAudio2_Output::close()
|
void XAudio2_Output::close() {
|
||||||
{
|
|
||||||
initialized = false;
|
initialized = false;
|
||||||
|
|
||||||
if (sVoice) {
|
if (sVoice) {
|
||||||
|
@ -324,13 +281,11 @@ void XAudio2_Output::close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void XAudio2_Output::device_change()
|
void XAudio2_Output::device_change() {
|
||||||
{
|
|
||||||
device_changed = true;
|
device_changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool XAudio2_Output::init(long sampleRate)
|
bool XAudio2_Output::init(long sampleRate) {
|
||||||
{
|
|
||||||
if (failed || initialized)
|
if (failed || initialized)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -361,13 +316,8 @@ bool XAudio2_Output::init(long sampleRate)
|
||||||
wfx.nBlockAlign = wfx.nChannels * (wfx.wBitsPerSample / 8);
|
wfx.nBlockAlign = wfx.nChannels * (wfx.wBitsPerSample / 8);
|
||||||
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
|
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
|
||||||
// create sound receiver
|
// create sound receiver
|
||||||
hr = xaud->CreateMasteringVoice(
|
hr = xaud->CreateMasteringVoice(&mVoice, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE,
|
||||||
&mVoice,
|
0, XA2GetDev(xaud), NULL);
|
||||||
XAUDIO2_DEFAULT_CHANNELS,
|
|
||||||
XAUDIO2_DEFAULT_SAMPLERATE,
|
|
||||||
0,
|
|
||||||
XA2GetDev(xaud),
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
if (hr != S_OK) {
|
if (hr != S_OK) {
|
||||||
wxLogError(_("XAudio2: Creating mastering voice failed!"));
|
wxLogError(_("XAudio2: Creating mastering voice failed!"));
|
||||||
|
@ -399,89 +349,89 @@ bool XAudio2_Output::init(long sampleRate)
|
||||||
bool matrixAvailable = true;
|
bool matrixAvailable = true;
|
||||||
|
|
||||||
switch (dd.OutputFormat.Format.nChannels) {
|
switch (dd.OutputFormat.Format.nChannels) {
|
||||||
case 4: // 4.0
|
case 4: // 4.0
|
||||||
//Speaker \ Left Source Right Source
|
// Speaker \ Left Source Right Source
|
||||||
/*Front L*/ matrix[0] = 1.0000f;
|
/*Front L*/ matrix[0] = 1.0000f;
|
||||||
matrix[1] = 0.0000f;
|
matrix[1] = 0.0000f;
|
||||||
/*Front R*/ matrix[2] = 0.0000f;
|
/*Front R*/ matrix[2] = 0.0000f;
|
||||||
matrix[3] = 1.0000f;
|
matrix[3] = 1.0000f;
|
||||||
/*Back L*/ matrix[4] = 1.0000f;
|
/*Back L*/ matrix[4] = 1.0000f;
|
||||||
matrix[5] = 0.0000f;
|
matrix[5] = 0.0000f;
|
||||||
/*Back R*/ matrix[6] = 0.0000f;
|
/*Back R*/ matrix[6] = 0.0000f;
|
||||||
matrix[7] = 1.0000f;
|
matrix[7] = 1.0000f;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 5: // 5.0
|
case 5: // 5.0
|
||||||
//Speaker \ Left Source Right Source
|
// Speaker \ Left Source Right Source
|
||||||
/*Front L*/ matrix[0] = 1.0000f;
|
/*Front L*/ matrix[0] = 1.0000f;
|
||||||
matrix[1] = 0.0000f;
|
matrix[1] = 0.0000f;
|
||||||
/*Front R*/ matrix[2] = 0.0000f;
|
/*Front R*/ matrix[2] = 0.0000f;
|
||||||
matrix[3] = 1.0000f;
|
matrix[3] = 1.0000f;
|
||||||
/*Front C*/ matrix[4] = 0.7071f;
|
/*Front C*/ matrix[4] = 0.7071f;
|
||||||
matrix[5] = 0.7071f;
|
matrix[5] = 0.7071f;
|
||||||
/*Side L*/ matrix[6] = 1.0000f;
|
/*Side L*/ matrix[6] = 1.0000f;
|
||||||
matrix[7] = 0.0000f;
|
matrix[7] = 0.0000f;
|
||||||
/*Side R*/ matrix[8] = 0.0000f;
|
/*Side R*/ matrix[8] = 0.0000f;
|
||||||
matrix[9] = 1.0000f;
|
matrix[9] = 1.0000f;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 6: // 5.1
|
case 6: // 5.1
|
||||||
//Speaker \ Left Source Right Source
|
// Speaker \ Left Source Right Source
|
||||||
/*Front L*/ matrix[0] = 1.0000f;
|
/*Front L*/ matrix[0] = 1.0000f;
|
||||||
matrix[1] = 0.0000f;
|
matrix[1] = 0.0000f;
|
||||||
/*Front R*/ matrix[2] = 0.0000f;
|
/*Front R*/ matrix[2] = 0.0000f;
|
||||||
matrix[3] = 1.0000f;
|
matrix[3] = 1.0000f;
|
||||||
/*Front C*/ matrix[4] = 0.7071f;
|
/*Front C*/ matrix[4] = 0.7071f;
|
||||||
matrix[5] = 0.7071f;
|
matrix[5] = 0.7071f;
|
||||||
/*LFE */ matrix[6] = 0.0000f;
|
/*LFE */ matrix[6] = 0.0000f;
|
||||||
matrix[7] = 0.0000f;
|
matrix[7] = 0.0000f;
|
||||||
/*Side L*/ matrix[8] = 1.0000f;
|
/*Side L*/ matrix[8] = 1.0000f;
|
||||||
matrix[9] = 0.0000f;
|
matrix[9] = 0.0000f;
|
||||||
/*Side R*/ matrix[10] = 0.0000f;
|
/*Side R*/ matrix[10] = 0.0000f;
|
||||||
matrix[11] = 1.0000f;
|
matrix[11] = 1.0000f;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 7: // 6.1
|
case 7: // 6.1
|
||||||
//Speaker \ Left Source Right Source
|
// Speaker \ Left Source Right Source
|
||||||
/*Front L*/ matrix[0] = 1.0000f;
|
/*Front L*/ matrix[0] = 1.0000f;
|
||||||
matrix[1] = 0.0000f;
|
matrix[1] = 0.0000f;
|
||||||
/*Front R*/ matrix[2] = 0.0000f;
|
/*Front R*/ matrix[2] = 0.0000f;
|
||||||
matrix[3] = 1.0000f;
|
matrix[3] = 1.0000f;
|
||||||
/*Front C*/ matrix[4] = 0.7071f;
|
/*Front C*/ matrix[4] = 0.7071f;
|
||||||
matrix[5] = 0.7071f;
|
matrix[5] = 0.7071f;
|
||||||
/*LFE */ matrix[6] = 0.0000f;
|
/*LFE */ matrix[6] = 0.0000f;
|
||||||
matrix[7] = 0.0000f;
|
matrix[7] = 0.0000f;
|
||||||
/*Side L*/ matrix[8] = 1.0000f;
|
/*Side L*/ matrix[8] = 1.0000f;
|
||||||
matrix[9] = 0.0000f;
|
matrix[9] = 0.0000f;
|
||||||
/*Side R*/ matrix[10] = 0.0000f;
|
/*Side R*/ matrix[10] = 0.0000f;
|
||||||
matrix[11] = 1.0000f;
|
matrix[11] = 1.0000f;
|
||||||
/*Back C*/ matrix[12] = 0.7071f;
|
/*Back C*/ matrix[12] = 0.7071f;
|
||||||
matrix[13] = 0.7071f;
|
matrix[13] = 0.7071f;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 8: // 7.1
|
case 8: // 7.1
|
||||||
//Speaker \ Left Source Right Source
|
// Speaker \ Left Source Right Source
|
||||||
/*Front L*/ matrix[0] = 1.0000f;
|
/*Front L*/ matrix[0] = 1.0000f;
|
||||||
matrix[1] = 0.0000f;
|
matrix[1] = 0.0000f;
|
||||||
/*Front R*/ matrix[2] = 0.0000f;
|
/*Front R*/ matrix[2] = 0.0000f;
|
||||||
matrix[3] = 1.0000f;
|
matrix[3] = 1.0000f;
|
||||||
/*Front C*/ matrix[4] = 0.7071f;
|
/*Front C*/ matrix[4] = 0.7071f;
|
||||||
matrix[5] = 0.7071f;
|
matrix[5] = 0.7071f;
|
||||||
/*LFE */ matrix[6] = 0.0000f;
|
/*LFE */ matrix[6] = 0.0000f;
|
||||||
matrix[7] = 0.0000f;
|
matrix[7] = 0.0000f;
|
||||||
/*Back L*/ matrix[8] = 1.0000f;
|
/*Back L*/ matrix[8] = 1.0000f;
|
||||||
matrix[9] = 0.0000f;
|
matrix[9] = 0.0000f;
|
||||||
/*Back R*/ matrix[10] = 0.0000f;
|
/*Back R*/ matrix[10] = 0.0000f;
|
||||||
matrix[11] = 1.0000f;
|
matrix[11] = 1.0000f;
|
||||||
/*Side L*/ matrix[12] = 1.0000f;
|
/*Side L*/ matrix[12] = 1.0000f;
|
||||||
matrix[13] = 0.0000f;
|
matrix[13] = 0.0000f;
|
||||||
/*Side R*/ matrix[14] = 0.0000f;
|
/*Side R*/ matrix[14] = 0.0000f;
|
||||||
matrix[15] = 1.0000f;
|
matrix[15] = 1.0000f;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
matrixAvailable = false;
|
matrixAvailable = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matrixAvailable) {
|
if (matrixAvailable) {
|
||||||
|
@ -502,8 +452,7 @@ bool XAudio2_Output::init(long sampleRate)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void XAudio2_Output::write(uint16_t* finalWave, int)
|
void XAudio2_Output::write(uint16_t* finalWave, int) {
|
||||||
{
|
|
||||||
if (!initialized || failed)
|
if (!initialized || failed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -548,13 +497,12 @@ void XAudio2_Output::write(uint16_t* finalWave, int)
|
||||||
buf.AudioBytes = soundBufferLen;
|
buf.AudioBytes = soundBufferLen;
|
||||||
buf.pAudioData = &buffers[currentBuffer * soundBufferLen];
|
buf.pAudioData = &buffers[currentBuffer * soundBufferLen];
|
||||||
currentBuffer++;
|
currentBuffer++;
|
||||||
currentBuffer %= (bufferCount + 1); // + 1 because we need one temporary buffer
|
currentBuffer %= (bufferCount + 1); // + 1 because we need one temporary buffer
|
||||||
HRESULT hr = sVoice->SubmitSourceBuffer(&buf); // send buffer to queue
|
HRESULT hr = sVoice->SubmitSourceBuffer(&buf); // send buffer to queue
|
||||||
assert(hr == S_OK);
|
assert(hr == S_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
void XAudio2_Output::pause()
|
void XAudio2_Output::pause() {
|
||||||
{
|
|
||||||
if (!initialized || failed)
|
if (!initialized || failed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -565,8 +513,7 @@ void XAudio2_Output::pause()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void XAudio2_Output::resume()
|
void XAudio2_Output::resume() {
|
||||||
{
|
|
||||||
if (!initialized || failed)
|
if (!initialized || failed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -577,8 +524,7 @@ void XAudio2_Output::resume()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void XAudio2_Output::reset()
|
void XAudio2_Output::reset() {
|
||||||
{
|
|
||||||
if (!initialized || failed)
|
if (!initialized || failed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -592,8 +538,7 @@ void XAudio2_Output::reset()
|
||||||
playing = true;
|
playing = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void XAudio2_Output::setThrottle(unsigned short throttle_)
|
void XAudio2_Output::setThrottle(unsigned short throttle_) {
|
||||||
{
|
|
||||||
if (!initialized || failed)
|
if (!initialized || failed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -604,12 +549,50 @@ void XAudio2_Output::setThrottle(unsigned short throttle_)
|
||||||
assert(hr == S_OK);
|
assert(hr == S_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
void xaudio2_device_changed(XAudio2_Output* instance)
|
void xaudio2_device_changed(XAudio2_Output* instance) {
|
||||||
{
|
|
||||||
instance->device_change();
|
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>();
|
return std::make_unique<XAudio2_Output>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace audio
|
|
@ -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_
|
|
@ -8,15 +8,16 @@
|
||||||
#include <wx/radiobut.h>
|
#include <wx/radiobut.h>
|
||||||
#include <wx/slider.h>
|
#include <wx/slider.h>
|
||||||
|
|
||||||
|
#include <wx/string.h>
|
||||||
#include <wx/xrc/xmlres.h>
|
#include <wx/xrc/xmlres.h>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
|
#include "wx/audio/audio.h"
|
||||||
#include "wx/config/option-id.h"
|
#include "wx/config/option-id.h"
|
||||||
#include "wx/config/option-proxy.h"
|
#include "wx/config/option-proxy.h"
|
||||||
#include "wx/config/option.h"
|
#include "wx/config/option.h"
|
||||||
#include "wx/dialogs/validated-child.h"
|
#include "wx/dialogs/validated-child.h"
|
||||||
#include "wx/widgets/option-validator.h"
|
#include "wx/widgets/option-validator.h"
|
||||||
#include "wx/wxvbam.h"
|
|
||||||
|
|
||||||
namespace dialogs {
|
namespace dialogs {
|
||||||
|
|
||||||
|
@ -103,13 +104,18 @@ private:
|
||||||
bool WriteToWindow() override {
|
bool WriteToWindow() override {
|
||||||
wxChoice* choice = wxDynamicCast(GetWindow(), wxChoice);
|
wxChoice* choice = wxDynamicCast(GetWindow(), wxChoice);
|
||||||
assert(choice);
|
assert(choice);
|
||||||
const wxString& device = option()->GetString();
|
|
||||||
const int selection = choice->FindString(device);
|
const wxString& device_id = option()->GetString();
|
||||||
if (selection == wxNOT_FOUND) {
|
for (size_t i = 0; i < choice->GetCount(); i++) {
|
||||||
return true;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +127,8 @@ private:
|
||||||
return option()->SetString(wxEmptyString);
|
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) {
|
void SoundConfig::OnAudioApiChanged(wxCommandEvent& event, config::AudioApi audio_api) {
|
||||||
audio_device_selector_->Clear();
|
audio_device_selector_->Clear();
|
||||||
audio_device_selector_->Append(_("Default device"), new wxStringClientData(wxEmptyString));
|
|
||||||
|
|
||||||
// Gather device names and IDs.
|
bool audio_device_found = false;
|
||||||
wxArrayString device_names;
|
for (const auto& device : audio::EnumerateAudioDevices(audio_api)) {
|
||||||
wxArrayString device_ids;
|
const int i =
|
||||||
switch (audio_api) {
|
audio_device_selector_->Append(device.name, new wxStringClientData(device.id));
|
||||||
case config::AudioApi::kOpenAL:
|
|
||||||
if (!GetOALDevices(device_names, device_ids)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
#if defined(__WXMSW__)
|
if (!audio_device_found && audio_api == OPTION(kSoundAudioAPI) &&
|
||||||
case config::AudioApi::kDirectSound:
|
OPTION(kSoundAudioDevice) == device.id) {
|
||||||
if (!(GetDSDevices(device_names, device_ids))) {
|
audio_device_selector_->SetSelection(i);
|
||||||
return;
|
audio_device_found = true;
|
||||||
}
|
}
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
audio_device_selector_->SetSelection(0);
|
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)
|
#if defined(VBAM_ENABLE_XAUDIO2) && defined(VBAM_ENABLE_FAUDIO)
|
||||||
|
|
|
@ -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
|
|
|
@ -1,5 +1,4 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
#include <wx/ffile.h>
|
#include <wx/ffile.h>
|
||||||
#include <wx/generic/prntdlgg.h>
|
#include <wx/generic/prntdlgg.h>
|
||||||
|
@ -11,9 +10,9 @@
|
||||||
#include "core/gb/gbGlobals.h"
|
#include "core/gb/gbGlobals.h"
|
||||||
#include "core/gba/gbaGlobals.h"
|
#include "core/gba/gbaGlobals.h"
|
||||||
#include "core/gba/gbaSound.h"
|
#include "core/gba/gbaSound.h"
|
||||||
|
#include "wx/audio/audio.h"
|
||||||
#include "wx/config/game-control.h"
|
#include "wx/config/game-control.h"
|
||||||
#include "wx/config/option-proxy.h"
|
#include "wx/config/option-proxy.h"
|
||||||
#include "wx/config/option.h"
|
|
||||||
#include "wx/wxvbam.h"
|
#include "wx/wxvbam.h"
|
||||||
|
|
||||||
// These should probably be in vbamcore
|
// These should probably be in vbamcore
|
||||||
|
@ -1230,34 +1229,7 @@ class SoundDriver;
|
||||||
std::unique_ptr<SoundDriver> systemSoundInit()
|
std::unique_ptr<SoundDriver> systemSoundInit()
|
||||||
{
|
{
|
||||||
soundShutdown();
|
soundShutdown();
|
||||||
|
return audio::CreateSoundDriver(OPTION(kSoundAudioAPI));
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void systemOnWriteDataToSoundBuffer(const uint16_t* finalWave, int length)
|
void systemOnWriteDataToSoundBuffer(const uint16_t* finalWave, int length)
|
||||||
|
|
|
@ -711,27 +711,6 @@ private:
|
||||||
|
|
||||||
#include "wx/opts.h"
|
#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)
|
#if defined(VBAM_ENABLE_DEBUGGER)
|
||||||
extern bool debugger;
|
extern bool debugger;
|
||||||
extern void (*dbgMain)();
|
extern void (*dbgMain)();
|
||||||
|
|
Loading…
Reference in New Issue