mirror of
https://github.com/visualboyadvance-m/visualboyadvance-m
synced 2025-10-05 23:52:49 +02:00
Compare commits
1 Commits
e889e895f2
...
audio-fix-
Author | SHA1 | Date | |
---|---|---|---|
|
f382c4fbbc |
@@ -6,6 +6,10 @@ endif()
|
||||
include(VbamFunctions)
|
||||
|
||||
set(VBAM_WX_COMMON
|
||||
audio/audio.cpp
|
||||
audio/audio.h
|
||||
audio/internal/openal.cpp
|
||||
audio/internal/openal.h
|
||||
background-input.cpp
|
||||
background-input.h
|
||||
cmdevents.cpp
|
||||
@@ -47,8 +51,6 @@ set(VBAM_WX_COMMON
|
||||
gfxviewers.cpp
|
||||
guiinit.cpp
|
||||
ioregs.h
|
||||
openal.cpp
|
||||
openal.h
|
||||
opts.cpp
|
||||
opts.h
|
||||
panel.cpp
|
||||
@@ -249,7 +251,7 @@ endif()
|
||||
if(WIN32)
|
||||
target_sources(visualboyadvance-m
|
||||
PRIVATE
|
||||
dsound.cpp
|
||||
audio/internal/dsound.cpp
|
||||
wxvbam.rc
|
||||
)
|
||||
target_link_libraries(visualboyadvance-m
|
||||
@@ -313,7 +315,7 @@ target_link_libraries(visualboyadvance-m ${OPENAL_LIBRARY})
|
||||
|
||||
# XAudio2.
|
||||
if(ENABLE_XAUDIO2)
|
||||
target_sources(visualboyadvance-m PRIVATE xaudio2.cpp)
|
||||
target_sources(visualboyadvance-m PRIVATE audio/internal/xaudio2.cpp)
|
||||
target_compile_definitions(visualboyadvance-m PRIVATE VBAM_ENABLE_XAUDIO2)
|
||||
endif()
|
||||
|
||||
@@ -324,7 +326,7 @@ endif()
|
||||
|
||||
# FAudio.
|
||||
if(ENABLE_FAUDIO)
|
||||
target_sources(visualboyadvance-m PRIVATE faudio.cpp)
|
||||
target_sources(visualboyadvance-m PRIVATE audio/internal/faudio.cpp)
|
||||
target_link_libraries(visualboyadvance-m FAudio::FAudio)
|
||||
target_compile_definitions(visualboyadvance-m PRIVATE VBAM_ENABLE_FAUDIO)
|
||||
endif()
|
||||
@@ -607,6 +609,9 @@ add_custom_command(
|
||||
|
||||
set(VBAM_LOCALIZABLE_FILES ${VBAM_WX_COMMON})
|
||||
list(APPEND VBAM_LOCALIZABLE_FILES
|
||||
audio/internal/dsound.cpp
|
||||
audio/internal/faudio.cpp
|
||||
audio/internal/xaudio2.cpp
|
||||
autoupdater/autoupdater.h
|
||||
autoupdater/macos/autoupdater.cpp
|
||||
autoupdater/macos/sparkle-wrapper.h
|
||||
@@ -614,11 +619,8 @@ list(APPEND VBAM_LOCALIZABLE_FILES
|
||||
autoupdater/wxmsw/winsparkle-rc.h
|
||||
autoupdater/wxmsw/winsparkle-wrapper.cpp
|
||||
autoupdater/wxmsw/winsparkle-wrapper.h
|
||||
dsound.cpp
|
||||
faudio.cpp
|
||||
widgets/dpi-support.cpp
|
||||
widgets/dpi-support-mac.mm
|
||||
xaudio2.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/core/gba/gbaLink.cpp
|
||||
)
|
||||
|
||||
|
76
src/wx/audio/audio.cpp
Normal file
76
src/wx/audio/audio.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
#include "wx/audio/audio.h"
|
||||
|
||||
#include "wx/audio/internal/openal.h"
|
||||
|
||||
#if defined(__WXMSW__)
|
||||
#include "wx/audio/internal/dsound.h"
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(VBAM_ENABLE_FAUDIO)
|
||||
#include "wx/audio/internal/faudio.h"
|
||||
#endif
|
||||
|
||||
#if defined(VBAM_ENABLE_XAUDIO2)
|
||||
#include "wx/audio/internal/xaudio2.h"
|
||||
#endif
|
||||
|
||||
namespace audio {
|
||||
|
||||
std::vector<AudioDevice> EnumerateAudioDevices(const config::AudioApi& audio_api) {
|
||||
switch (audio_api) {
|
||||
case config::AudioApi::kOpenAL:
|
||||
return audio::internal::GetOpenALDevices();
|
||||
|
||||
#if defined(__WXMSW__)
|
||||
case config::AudioApi::kDirectSound:
|
||||
return audio::internal::GetDirectSoundDevices();
|
||||
#endif
|
||||
|
||||
#if defined(VBAM_ENABLE_XAUDIO2)
|
||||
case config::AudioApi::kXAudio2:
|
||||
return audio::internal::GetXAudio2Devices();
|
||||
#endif
|
||||
|
||||
#if defined(VBAM_ENABLE_FAUDIO)
|
||||
case config::AudioApi::kFAudio:
|
||||
return audio::internal::GetFAudioDevices();
|
||||
#endif
|
||||
|
||||
case config::AudioApi::kLast:
|
||||
default:
|
||||
// This should never happen.
|
||||
assert(false);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<SoundDriver> CreateSoundDriver(const config::AudioApi& api) {
|
||||
switch (api) {
|
||||
case config::AudioApi::kOpenAL:
|
||||
return audio::internal::CreateOpenALDriver();
|
||||
|
||||
#if defined(__WXMSW__)
|
||||
case config::AudioApi::kDirectSound:
|
||||
return audio::internal::CreateDirectSoundDriver();
|
||||
#endif
|
||||
|
||||
#if defined(VBAM_ENABLE_XAUDIO2)
|
||||
case config::AudioApi::kXAudio2:
|
||||
return audio::internal::CreateXAudio2Driver();
|
||||
#endif
|
||||
|
||||
#if defined(VBAM_ENABLE_FAUDIO)
|
||||
case config::AudioApi::kFAudio:
|
||||
return audio::internal::CreateFAudioDriver();
|
||||
#endif
|
||||
|
||||
case config::AudioApi::kLast:
|
||||
default:
|
||||
// This should never happen.
|
||||
assert(false);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace audio
|
30
src/wx/audio/audio.h
Normal file
30
src/wx/audio/audio.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef WX_AUDIO_AUDIO_H_
|
||||
#define WX_AUDIO_AUDIO_H_
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <wx/string.h>
|
||||
|
||||
#include "core/base/sound_driver.h"
|
||||
#include "wx/config/option.h"
|
||||
|
||||
namespace audio {
|
||||
|
||||
// Represents an audio device.
|
||||
struct AudioDevice {
|
||||
// The device user-friendly name.
|
||||
wxString name;
|
||||
// The underlying device ID.
|
||||
wxString id;
|
||||
};
|
||||
|
||||
// Returns the set of audio devices for the given API.
|
||||
std::vector<AudioDevice> EnumerateAudioDevices(const config::AudioApi& api);
|
||||
|
||||
// Creates a sound driver for the given API.
|
||||
std::unique_ptr<SoundDriver> CreateSoundDriver(const config::AudioApi& api);
|
||||
|
||||
} // namespace audio
|
||||
|
||||
#endif // WX_AUDIO_AUDIO_H_
|
@@ -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
|
22
src/wx/audio/internal/dsound.h
Normal file
22
src/wx/audio/internal/dsound.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef WX_AUDIO_INTERNAL_DSOUND_H_
|
||||
#define WX_AUDIO_INTERNAL_DSOUND_H_
|
||||
|
||||
#if !defined(__WXMSW__)
|
||||
#error "This file should only be included on Windows"
|
||||
#endif
|
||||
|
||||
#include "wx/audio/audio.h"
|
||||
|
||||
namespace audio {
|
||||
namespace internal {
|
||||
|
||||
// Returns the set of DirectSound devices.
|
||||
std::vector<AudioDevice> GetDirectSoundDevices();
|
||||
|
||||
// Creates a DirectSound sound driver.
|
||||
std::unique_ptr<SoundDriver> CreateDirectSoundDriver();
|
||||
|
||||
} // namespace internal
|
||||
} // namespace audio
|
||||
|
||||
#endif // WX_AUDIO_INTERNAL_DSOUND_H_
|
@@ -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
|
22
src/wx/audio/internal/faudio.h
Normal file
22
src/wx/audio/internal/faudio.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef WX_AUDIO_INTERNAL_FAUDIO_H_
|
||||
#define WX_AUDIO_INTERNAL_FAUDIO_H_
|
||||
|
||||
#if !defined(VBAM_ENABLE_FAUDIO)
|
||||
#error "This file should only be included if FAudio is enabled"
|
||||
#endif
|
||||
|
||||
#include "wx/audio/audio.h"
|
||||
|
||||
namespace audio {
|
||||
namespace internal {
|
||||
|
||||
// Returns the set of FAudio devices.
|
||||
std::vector<AudioDevice> GetFAudioDevices();
|
||||
|
||||
// Creates an FAudio sound driver.
|
||||
std::unique_ptr<SoundDriver> CreateFAudioDriver();
|
||||
|
||||
} // namespace internal
|
||||
} // namespace audio
|
||||
|
||||
#endif // WX_AUDIO_INTERNAL_FAUDIO_H_
|
@@ -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
|
18
src/wx/audio/internal/openal.h
Normal file
18
src/wx/audio/internal/openal.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef WX_AUDIO_INTERNAL_OPENAL_H_
|
||||
#define WX_AUDIO_INTERNAL_OPENAL_H_
|
||||
|
||||
#include "wx/audio/audio.h"
|
||||
|
||||
namespace audio {
|
||||
namespace internal {
|
||||
|
||||
// Returns the set of OpenAL devices.
|
||||
std::vector<AudioDevice> GetOpenALDevices();
|
||||
|
||||
// Creates an OpenAL sound driver.
|
||||
std::unique_ptr<SoundDriver> CreateOpenALDriver();
|
||||
|
||||
} // namespace internal
|
||||
} // namespace audio
|
||||
|
||||
#endif // WX_AUDIO_INTERNAL_OPENAL_H_
|
@@ -2,6 +2,8 @@
|
||||
#error "This file should only be compiled if XAudio2 is enabled"
|
||||
#endif
|
||||
|
||||
#include "wx/audio/internal/xaudio2.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include <string>
|
||||
@@ -14,78 +16,55 @@
|
||||
#include <xaudio2.legacy.h>
|
||||
#else
|
||||
#include <XAudio2.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <wx/arrstr.h>
|
||||
#include <wx/log.h>
|
||||
#include <wx/translation.h>
|
||||
|
||||
#include "core/base/sound_driver.h"
|
||||
#include "core/base/system.h" // for systemMessage()
|
||||
#include "core/base/system.h" // for systemMessage()
|
||||
#include "core/gba/gbaGlobals.h"
|
||||
#include "wx/config/option-proxy.h"
|
||||
|
||||
int GetXA2Devices(IXAudio2* xa, wxArrayString* names, wxArrayString* ids,
|
||||
const wxString* match)
|
||||
{
|
||||
HRESULT hr;
|
||||
UINT32 dev_count = 0;
|
||||
hr = xa->GetDeviceCount(&dev_count);
|
||||
namespace audio {
|
||||
namespace internal {
|
||||
|
||||
if (hr != S_OK) {
|
||||
wxLogError(_("XAudio2: Enumerating devices failed!"));
|
||||
return true;
|
||||
} else {
|
||||
XAUDIO2_DEVICE_DETAILS dd;
|
||||
namespace {
|
||||
|
||||
for (UINT32 i = 0; i < dev_count; i++) {
|
||||
hr = xa->GetDeviceDetails(i, &dd);
|
||||
|
||||
if (hr != S_OK) {
|
||||
continue;
|
||||
} else {
|
||||
if (ids) {
|
||||
ids->push_back(dd.DeviceID);
|
||||
names->push_back(dd.DisplayName);
|
||||
} else if (*match == dd.DeviceID)
|
||||
return i;
|
||||
}
|
||||
}
|
||||
int XA2GetDev(IXAudio2* xa) {
|
||||
const wxString& audio_device = OPTION(kSoundAudioDevice);
|
||||
if (audio_device.empty()) {
|
||||
// Just use the default device.
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool GetXA2Devices(wxArrayString& names, wxArrayString& ids)
|
||||
{
|
||||
HRESULT hr;
|
||||
IXAudio2* xa = NULL;
|
||||
hr = XAudio2Create(&xa, 0);
|
||||
|
||||
uint32_t hr;
|
||||
uint32_t dev_count = 0;
|
||||
hr = xa->GetDeviceCount(&dev_count);
|
||||
if (hr != S_OK) {
|
||||
wxLogError(_("The XAudio2 interface failed to initialize!"));
|
||||
wxLogError(_("XAudio2: Enumerating devices failed!"));
|
||||
return false;
|
||||
}
|
||||
|
||||
GetXA2Devices(xa, &names, &ids, NULL);
|
||||
xa->Release();
|
||||
return true;
|
||||
}
|
||||
|
||||
static int XA2GetDev(IXAudio2* xa)
|
||||
{
|
||||
const wxString& audio_device = OPTION(kSoundAudioDevice);
|
||||
if (audio_device.empty())
|
||||
return 0;
|
||||
else {
|
||||
int ret = GetXA2Devices(xa, NULL, NULL, &audio_device);
|
||||
return ret < 0 ? 0 : ret;
|
||||
for (UINT32 i = 0; i < dev_count; i++) {
|
||||
XAUDIO2_DEVICE_DETAILS dd;
|
||||
hr = xa->GetDeviceDetails(i, &dd);
|
||||
if (hr != S_OK) {
|
||||
continue;
|
||||
}
|
||||
const wxString device_id(reinterpret_cast<wchar_t*>(dd.DeviceID));
|
||||
if (audio_device == device_id) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
class XAudio2_Output;
|
||||
|
||||
static void xaudio2_device_changed(XAudio2_Output*);
|
||||
void xaudio2_device_changed(XAudio2_Output*);
|
||||
|
||||
class XAudio2_Device_Notifier : public IMMNotificationClient {
|
||||
volatile LONG registered;
|
||||
@@ -97,28 +76,14 @@ class XAudio2_Device_Notifier : public IMMNotificationClient {
|
||||
std::vector<XAudio2_Output*> instances;
|
||||
|
||||
public:
|
||||
XAudio2_Device_Notifier()
|
||||
: registered(0)
|
||||
{
|
||||
InitializeCriticalSection(&lock);
|
||||
}
|
||||
~XAudio2_Device_Notifier()
|
||||
{
|
||||
DeleteCriticalSection(&lock);
|
||||
}
|
||||
XAudio2_Device_Notifier() : registered(0) { InitializeCriticalSection(&lock); }
|
||||
~XAudio2_Device_Notifier() { DeleteCriticalSection(&lock); }
|
||||
|
||||
ULONG STDMETHODCALLTYPE AddRef()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
ULONG STDMETHODCALLTYPE AddRef() { return 1; }
|
||||
|
||||
ULONG STDMETHODCALLTYPE Release()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
ULONG STDMETHODCALLTYPE Release() { return 1; }
|
||||
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID** ppvInterface)
|
||||
{
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID** ppvInterface) {
|
||||
if (IID_IUnknown == riid) {
|
||||
*ppvInterface = (IUnknown*)this;
|
||||
} else if (__uuidof(IMMNotificationClient) == riid) {
|
||||
@@ -131,8 +96,7 @@ public:
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole, LPCWSTR pwstrDeviceId)
|
||||
{
|
||||
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole, LPCWSTR pwstrDeviceId) {
|
||||
if (flow == eRender && last_device.compare(pwstrDeviceId) != 0) {
|
||||
last_device = pwstrDeviceId;
|
||||
EnterCriticalSection(&lock);
|
||||
@@ -152,11 +116,11 @@ public:
|
||||
HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR, DWORD) { return S_OK; }
|
||||
HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(LPCWSTR, const PROPERTYKEY) { return S_OK; }
|
||||
|
||||
void do_register(XAudio2_Output* p_instance)
|
||||
{
|
||||
void do_register(XAudio2_Output* p_instance) {
|
||||
if (InterlockedIncrement(®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
|
22
src/wx/audio/internal/xaudio2.h
Normal file
22
src/wx/audio/internal/xaudio2.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef WX_AUDIO_INTERNAL_XAUDIO2_H_
|
||||
#define WX_AUDIO_INTERNAL_XAUDIO2_H_
|
||||
|
||||
#if !defined(VBAM_ENABLE_FAUDIO)
|
||||
#error "This file should only be included if FAudio is enabled"
|
||||
#endif
|
||||
|
||||
#include "wx/audio/audio.h"
|
||||
|
||||
namespace audio {
|
||||
namespace internal {
|
||||
|
||||
// Returns the set of XAudio2 devices.
|
||||
std::vector<AudioDevice> GetXAudio2Devices();
|
||||
|
||||
// Creates an XAudio2 sound driver.
|
||||
std::unique_ptr<SoundDriver> CreateXAudio2Driver();
|
||||
|
||||
} // namespace internal
|
||||
} // namespace audio
|
||||
|
||||
#endif // WX_AUDIO_INTERNAL_XAUDIO2_H_
|
@@ -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)();
|
||||
|
Reference in New Issue
Block a user