[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)
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
|
|
|
@ -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"
|
||||
#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
|
|
@ -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)
|
||||
#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(®istered) == 1) {
|
||||
pEnumerator = nullptr;
|
||||
HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER,
|
||||
|
@ -579,8 +553,7 @@ void FAudio_Device_Notifier::do_register(FAudio_Output* p_instance)
|
|||
LeaveCriticalSection(&lock);
|
||||
}
|
||||
|
||||
void FAudio_Device_Notifier::do_unregister(FAudio_Output* p_instance)
|
||||
{
|
||||
void FAudio_Device_Notifier::do_unregister(FAudio_Output* p_instance) {
|
||||
if (InterlockedDecrement(®istered) == 0) {
|
||||
if (pEnumerator) {
|
||||
pEnumerator->UnregisterEndpointNotificationCallback(this);
|
||||
|
@ -601,13 +574,11 @@ void FAudio_Device_Notifier::do_unregister(FAudio_Output* p_instance)
|
|||
LeaveCriticalSection(&lock);
|
||||
}
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
bool GetFADevices(wxArrayString& names, wxArrayString& ids)
|
||||
{
|
||||
uint32_t hr;
|
||||
std::vector<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
|
|
@ -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 ===
|
||||
//#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
|
|
@ -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"
|
||||
#endif
|
||||
|
||||
#include "wx/audio/internal/xaudio2.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include <string>
|
||||
|
@ -21,71 +23,48 @@
|
|||
#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(®istered) == 1) {
|
||||
pEnumerator = NULL;
|
||||
HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, __uuidof(IMMDeviceEnumerator), (void**)&pEnumerator);
|
||||
HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER,
|
||||
__uuidof(IMMDeviceEnumerator), (void**)&pEnumerator);
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
pEnumerator->RegisterEndpointNotificationCallback(this);
|
||||
|
@ -168,8 +132,7 @@ public:
|
|||
LeaveCriticalSection(&lock);
|
||||
}
|
||||
|
||||
void do_unregister(XAudio2_Output* p_instance)
|
||||
{
|
||||
void do_unregister(XAudio2_Output* p_instance) {
|
||||
if (InterlockedDecrement(®istered) == 0) {
|
||||
if (pEnumerator) {
|
||||
pEnumerator->UnregisterEndpointNotificationCallback(this);
|
||||
|
@ -196,22 +159,19 @@ class XAudio2_BufferNotify : public IXAudio2VoiceCallback {
|
|||
public:
|
||||
HANDLE hBufferEndEvent;
|
||||
|
||||
XAudio2_BufferNotify()
|
||||
{
|
||||
XAudio2_BufferNotify() {
|
||||
hBufferEndEvent = NULL;
|
||||
hBufferEndEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
assert(hBufferEndEvent != NULL);
|
||||
}
|
||||
|
||||
~XAudio2_BufferNotify()
|
||||
{
|
||||
~XAudio2_BufferNotify() {
|
||||
CloseHandle(hBufferEndEvent);
|
||||
hBufferEndEvent = NULL;
|
||||
}
|
||||
|
||||
STDMETHOD_(void, OnBufferEnd)
|
||||
(void*)
|
||||
{
|
||||
(void*) {
|
||||
assert(hBufferEndEvent != NULL);
|
||||
SetEvent(hBufferEndEvent);
|
||||
}
|
||||
|
@ -232,13 +192,13 @@ public:
|
|||
};
|
||||
|
||||
// Class Declaration
|
||||
class XAudio2_Output
|
||||
: public SoundDriver {
|
||||
class XAudio2_Output : public SoundDriver {
|
||||
public:
|
||||
XAudio2_Output();
|
||||
~XAudio2_Output() override;
|
||||
|
||||
void device_change();
|
||||
|
||||
private:
|
||||
void close();
|
||||
|
||||
|
@ -247,7 +207,7 @@ private:
|
|||
void pause() override;
|
||||
void reset() override;
|
||||
void resume() override;
|
||||
void write(uint16_t *finalWave, int length) override;
|
||||
void write(uint16_t* finalWave, int length) override;
|
||||
void setThrottle(unsigned short throttle_) override;
|
||||
|
||||
bool failed;
|
||||
|
@ -262,16 +222,15 @@ private:
|
|||
volatile bool device_changed;
|
||||
|
||||
IXAudio2* xaud;
|
||||
IXAudio2MasteringVoice* mVoice; // listener
|
||||
IXAudio2SourceVoice* sVoice; // sound source
|
||||
IXAudio2MasteringVoice* mVoice; // listener
|
||||
IXAudio2SourceVoice* sVoice; // sound source
|
||||
XAUDIO2_BUFFER buf;
|
||||
XAUDIO2_VOICE_STATE vState;
|
||||
XAudio2_BufferNotify notify; // buffer end notification
|
||||
XAudio2_BufferNotify notify; // buffer end notification
|
||||
};
|
||||
|
||||
// Class Implementation
|
||||
XAudio2_Output::XAudio2_Output()
|
||||
{
|
||||
XAudio2_Output::XAudio2_Output() {
|
||||
failed = false;
|
||||
initialized = false;
|
||||
playing = false;
|
||||
|
@ -288,14 +247,12 @@ XAudio2_Output::XAudio2_Output()
|
|||
g_notifier.do_register(this);
|
||||
}
|
||||
|
||||
XAudio2_Output::~XAudio2_Output()
|
||||
{
|
||||
XAudio2_Output::~XAudio2_Output() {
|
||||
g_notifier.do_unregister(this);
|
||||
close();
|
||||
}
|
||||
|
||||
void XAudio2_Output::close()
|
||||
{
|
||||
void XAudio2_Output::close() {
|
||||
initialized = false;
|
||||
|
||||
if (sVoice) {
|
||||
|
@ -324,13 +281,11 @@ void XAudio2_Output::close()
|
|||
}
|
||||
}
|
||||
|
||||
void XAudio2_Output::device_change()
|
||||
{
|
||||
void XAudio2_Output::device_change() {
|
||||
device_changed = true;
|
||||
}
|
||||
|
||||
bool XAudio2_Output::init(long sampleRate)
|
||||
{
|
||||
bool XAudio2_Output::init(long sampleRate) {
|
||||
if (failed || initialized)
|
||||
return false;
|
||||
|
||||
|
@ -361,13 +316,8 @@ bool XAudio2_Output::init(long sampleRate)
|
|||
wfx.nBlockAlign = wfx.nChannels * (wfx.wBitsPerSample / 8);
|
||||
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
|
||||
// create sound receiver
|
||||
hr = xaud->CreateMasteringVoice(
|
||||
&mVoice,
|
||||
XAUDIO2_DEFAULT_CHANNELS,
|
||||
XAUDIO2_DEFAULT_SAMPLERATE,
|
||||
0,
|
||||
XA2GetDev(xaud),
|
||||
NULL);
|
||||
hr = xaud->CreateMasteringVoice(&mVoice, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE,
|
||||
0, XA2GetDev(xaud), NULL);
|
||||
|
||||
if (hr != S_OK) {
|
||||
wxLogError(_("XAudio2: Creating mastering voice failed!"));
|
||||
|
@ -399,89 +349,89 @@ bool XAudio2_Output::init(long sampleRate)
|
|||
bool matrixAvailable = true;
|
||||
|
||||
switch (dd.OutputFormat.Format.nChannels) {
|
||||
case 4: // 4.0
|
||||
//Speaker \ Left Source Right Source
|
||||
/*Front L*/ matrix[0] = 1.0000f;
|
||||
matrix[1] = 0.0000f;
|
||||
/*Front R*/ matrix[2] = 0.0000f;
|
||||
matrix[3] = 1.0000f;
|
||||
/*Back L*/ matrix[4] = 1.0000f;
|
||||
matrix[5] = 0.0000f;
|
||||
/*Back R*/ matrix[6] = 0.0000f;
|
||||
matrix[7] = 1.0000f;
|
||||
break;
|
||||
case 4: // 4.0
|
||||
// Speaker \ Left Source Right Source
|
||||
/*Front L*/ matrix[0] = 1.0000f;
|
||||
matrix[1] = 0.0000f;
|
||||
/*Front R*/ matrix[2] = 0.0000f;
|
||||
matrix[3] = 1.0000f;
|
||||
/*Back L*/ matrix[4] = 1.0000f;
|
||||
matrix[5] = 0.0000f;
|
||||
/*Back R*/ matrix[6] = 0.0000f;
|
||||
matrix[7] = 1.0000f;
|
||||
break;
|
||||
|
||||
case 5: // 5.0
|
||||
//Speaker \ Left Source Right Source
|
||||
/*Front L*/ matrix[0] = 1.0000f;
|
||||
matrix[1] = 0.0000f;
|
||||
/*Front R*/ matrix[2] = 0.0000f;
|
||||
matrix[3] = 1.0000f;
|
||||
/*Front C*/ matrix[4] = 0.7071f;
|
||||
matrix[5] = 0.7071f;
|
||||
/*Side L*/ matrix[6] = 1.0000f;
|
||||
matrix[7] = 0.0000f;
|
||||
/*Side R*/ matrix[8] = 0.0000f;
|
||||
matrix[9] = 1.0000f;
|
||||
break;
|
||||
case 5: // 5.0
|
||||
// Speaker \ Left Source Right Source
|
||||
/*Front L*/ matrix[0] = 1.0000f;
|
||||
matrix[1] = 0.0000f;
|
||||
/*Front R*/ matrix[2] = 0.0000f;
|
||||
matrix[3] = 1.0000f;
|
||||
/*Front C*/ matrix[4] = 0.7071f;
|
||||
matrix[5] = 0.7071f;
|
||||
/*Side L*/ matrix[6] = 1.0000f;
|
||||
matrix[7] = 0.0000f;
|
||||
/*Side R*/ matrix[8] = 0.0000f;
|
||||
matrix[9] = 1.0000f;
|
||||
break;
|
||||
|
||||
case 6: // 5.1
|
||||
//Speaker \ Left Source Right Source
|
||||
/*Front L*/ matrix[0] = 1.0000f;
|
||||
matrix[1] = 0.0000f;
|
||||
/*Front R*/ matrix[2] = 0.0000f;
|
||||
matrix[3] = 1.0000f;
|
||||
/*Front C*/ matrix[4] = 0.7071f;
|
||||
matrix[5] = 0.7071f;
|
||||
/*LFE */ matrix[6] = 0.0000f;
|
||||
matrix[7] = 0.0000f;
|
||||
/*Side L*/ matrix[8] = 1.0000f;
|
||||
matrix[9] = 0.0000f;
|
||||
/*Side R*/ matrix[10] = 0.0000f;
|
||||
matrix[11] = 1.0000f;
|
||||
break;
|
||||
case 6: // 5.1
|
||||
// Speaker \ Left Source Right Source
|
||||
/*Front L*/ matrix[0] = 1.0000f;
|
||||
matrix[1] = 0.0000f;
|
||||
/*Front R*/ matrix[2] = 0.0000f;
|
||||
matrix[3] = 1.0000f;
|
||||
/*Front C*/ matrix[4] = 0.7071f;
|
||||
matrix[5] = 0.7071f;
|
||||
/*LFE */ matrix[6] = 0.0000f;
|
||||
matrix[7] = 0.0000f;
|
||||
/*Side L*/ matrix[8] = 1.0000f;
|
||||
matrix[9] = 0.0000f;
|
||||
/*Side R*/ matrix[10] = 0.0000f;
|
||||
matrix[11] = 1.0000f;
|
||||
break;
|
||||
|
||||
case 7: // 6.1
|
||||
//Speaker \ Left Source Right Source
|
||||
/*Front L*/ matrix[0] = 1.0000f;
|
||||
matrix[1] = 0.0000f;
|
||||
/*Front R*/ matrix[2] = 0.0000f;
|
||||
matrix[3] = 1.0000f;
|
||||
/*Front C*/ matrix[4] = 0.7071f;
|
||||
matrix[5] = 0.7071f;
|
||||
/*LFE */ matrix[6] = 0.0000f;
|
||||
matrix[7] = 0.0000f;
|
||||
/*Side L*/ matrix[8] = 1.0000f;
|
||||
matrix[9] = 0.0000f;
|
||||
/*Side R*/ matrix[10] = 0.0000f;
|
||||
matrix[11] = 1.0000f;
|
||||
/*Back C*/ matrix[12] = 0.7071f;
|
||||
matrix[13] = 0.7071f;
|
||||
break;
|
||||
case 7: // 6.1
|
||||
// Speaker \ Left Source Right Source
|
||||
/*Front L*/ matrix[0] = 1.0000f;
|
||||
matrix[1] = 0.0000f;
|
||||
/*Front R*/ matrix[2] = 0.0000f;
|
||||
matrix[3] = 1.0000f;
|
||||
/*Front C*/ matrix[4] = 0.7071f;
|
||||
matrix[5] = 0.7071f;
|
||||
/*LFE */ matrix[6] = 0.0000f;
|
||||
matrix[7] = 0.0000f;
|
||||
/*Side L*/ matrix[8] = 1.0000f;
|
||||
matrix[9] = 0.0000f;
|
||||
/*Side R*/ matrix[10] = 0.0000f;
|
||||
matrix[11] = 1.0000f;
|
||||
/*Back C*/ matrix[12] = 0.7071f;
|
||||
matrix[13] = 0.7071f;
|
||||
break;
|
||||
|
||||
case 8: // 7.1
|
||||
//Speaker \ Left Source Right Source
|
||||
/*Front L*/ matrix[0] = 1.0000f;
|
||||
matrix[1] = 0.0000f;
|
||||
/*Front R*/ matrix[2] = 0.0000f;
|
||||
matrix[3] = 1.0000f;
|
||||
/*Front C*/ matrix[4] = 0.7071f;
|
||||
matrix[5] = 0.7071f;
|
||||
/*LFE */ matrix[6] = 0.0000f;
|
||||
matrix[7] = 0.0000f;
|
||||
/*Back L*/ matrix[8] = 1.0000f;
|
||||
matrix[9] = 0.0000f;
|
||||
/*Back R*/ matrix[10] = 0.0000f;
|
||||
matrix[11] = 1.0000f;
|
||||
/*Side L*/ matrix[12] = 1.0000f;
|
||||
matrix[13] = 0.0000f;
|
||||
/*Side R*/ matrix[14] = 0.0000f;
|
||||
matrix[15] = 1.0000f;
|
||||
break;
|
||||
case 8: // 7.1
|
||||
// Speaker \ Left Source Right Source
|
||||
/*Front L*/ matrix[0] = 1.0000f;
|
||||
matrix[1] = 0.0000f;
|
||||
/*Front R*/ matrix[2] = 0.0000f;
|
||||
matrix[3] = 1.0000f;
|
||||
/*Front C*/ matrix[4] = 0.7071f;
|
||||
matrix[5] = 0.7071f;
|
||||
/*LFE */ matrix[6] = 0.0000f;
|
||||
matrix[7] = 0.0000f;
|
||||
/*Back L*/ matrix[8] = 1.0000f;
|
||||
matrix[9] = 0.0000f;
|
||||
/*Back R*/ matrix[10] = 0.0000f;
|
||||
matrix[11] = 1.0000f;
|
||||
/*Side L*/ matrix[12] = 1.0000f;
|
||||
matrix[13] = 0.0000f;
|
||||
/*Side R*/ matrix[14] = 0.0000f;
|
||||
matrix[15] = 1.0000f;
|
||||
break;
|
||||
|
||||
default:
|
||||
matrixAvailable = false;
|
||||
break;
|
||||
default:
|
||||
matrixAvailable = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (matrixAvailable) {
|
||||
|
@ -502,8 +452,7 @@ bool XAudio2_Output::init(long sampleRate)
|
|||
return true;
|
||||
}
|
||||
|
||||
void XAudio2_Output::write(uint16_t* finalWave, int)
|
||||
{
|
||||
void XAudio2_Output::write(uint16_t* finalWave, int) {
|
||||
if (!initialized || failed)
|
||||
return;
|
||||
|
||||
|
@ -548,13 +497,12 @@ void XAudio2_Output::write(uint16_t* finalWave, int)
|
|||
buf.AudioBytes = soundBufferLen;
|
||||
buf.pAudioData = &buffers[currentBuffer * soundBufferLen];
|
||||
currentBuffer++;
|
||||
currentBuffer %= (bufferCount + 1); // + 1 because we need one temporary buffer
|
||||
HRESULT hr = sVoice->SubmitSourceBuffer(&buf); // send buffer to queue
|
||||
currentBuffer %= (bufferCount + 1); // + 1 because we need one temporary buffer
|
||||
HRESULT hr = sVoice->SubmitSourceBuffer(&buf); // send buffer to queue
|
||||
assert(hr == S_OK);
|
||||
}
|
||||
|
||||
void XAudio2_Output::pause()
|
||||
{
|
||||
void XAudio2_Output::pause() {
|
||||
if (!initialized || failed)
|
||||
return;
|
||||
|
||||
|
@ -565,8 +513,7 @@ void XAudio2_Output::pause()
|
|||
}
|
||||
}
|
||||
|
||||
void XAudio2_Output::resume()
|
||||
{
|
||||
void XAudio2_Output::resume() {
|
||||
if (!initialized || failed)
|
||||
return;
|
||||
|
||||
|
@ -577,8 +524,7 @@ void XAudio2_Output::resume()
|
|||
}
|
||||
}
|
||||
|
||||
void XAudio2_Output::reset()
|
||||
{
|
||||
void XAudio2_Output::reset() {
|
||||
if (!initialized || failed)
|
||||
return;
|
||||
|
||||
|
@ -592,8 +538,7 @@ void XAudio2_Output::reset()
|
|||
playing = true;
|
||||
}
|
||||
|
||||
void XAudio2_Output::setThrottle(unsigned short throttle_)
|
||||
{
|
||||
void XAudio2_Output::setThrottle(unsigned short throttle_) {
|
||||
if (!initialized || failed)
|
||||
return;
|
||||
|
||||
|
@ -604,12 +549,50 @@ void XAudio2_Output::setThrottle(unsigned short throttle_)
|
|||
assert(hr == S_OK);
|
||||
}
|
||||
|
||||
void xaudio2_device_changed(XAudio2_Output* instance)
|
||||
{
|
||||
void xaudio2_device_changed(XAudio2_Output* instance) {
|
||||
instance->device_change();
|
||||
}
|
||||
|
||||
std::unique_ptr<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
|
|
@ -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/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)
|
||||
|
|
|
@ -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 <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)
|
||||
|
|
|
@ -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)();
|
||||
|
|
Loading…
Reference in New Issue