Implement CoreAudio driver
This commit is contained in:
parent
ef85b71444
commit
79d3453544
|
@ -8,6 +8,7 @@ include(VbamFunctions)
|
|||
set(VBAM_WX_COMMON
|
||||
audio/audio.cpp
|
||||
audio/audio.h
|
||||
audio/internal/coreaudio.cpp
|
||||
audio/internal/sdl.cpp
|
||||
audio/internal/sdl.h
|
||||
audio/internal/openal.cpp
|
||||
|
@ -159,6 +160,9 @@ if(CMAKE_TOOLCHAIN_FILE MATCHES "vcpkg")
|
|||
endif()
|
||||
|
||||
if(APPLE)
|
||||
find_library(COREAUDIO CoreAudio)
|
||||
find_library(COREFOUNDATION CoreFoundation)
|
||||
find_library(AUDIOTOOLBOX AudioToolbox)
|
||||
find_library(METAL Metal)
|
||||
find_library(METALKIT MetalKit)
|
||||
endif()
|
||||
|
@ -333,6 +337,10 @@ function(configure_wx_target target)
|
|||
|
||||
# Metal
|
||||
if(APPLE)
|
||||
_add_link_libraries(${COREFOUNDATION})
|
||||
_add_link_libraries(${COREAUDIO})
|
||||
_add_link_libraries(${AUDIOTOOLBOX})
|
||||
|
||||
if(CMAKE_Metal_COMPILER)
|
||||
_add_link_libraries($<LINK_LIBRARY:WEAK_FRAMEWORK,${METAL}>)
|
||||
_add_link_libraries($<LINK_LIBRARY:WEAK_FRAMEWORK,${METALKIT}>)
|
||||
|
|
|
@ -12,6 +12,10 @@
|
|||
#include "wx/audio/internal/faudio.h"
|
||||
#endif
|
||||
|
||||
#if defined(__WXMAC__)
|
||||
#include "wx/audio/internal/coreaudio.h"
|
||||
#endif
|
||||
|
||||
#if defined(VBAM_ENABLE_XAUDIO2)
|
||||
#include "wx/audio/internal/xaudio2.h"
|
||||
#endif
|
||||
|
@ -41,6 +45,11 @@ std::vector<AudioDevice> EnumerateAudioDevices(const config::AudioApi& audio_api
|
|||
return audio::internal::GetFAudioDevices();
|
||||
#endif
|
||||
|
||||
#if defined(__WXMAC__)
|
||||
case config::AudioApi::kCoreAudio:
|
||||
return audio::internal::GetCoreAudioDevices();
|
||||
#endif
|
||||
|
||||
case config::AudioApi::kLast:
|
||||
default:
|
||||
VBAM_NOTREACHED();
|
||||
|
@ -71,6 +80,11 @@ std::unique_ptr<SoundDriver> CreateSoundDriver(const config::AudioApi& api) {
|
|||
return audio::internal::CreateFAudioDriver();
|
||||
#endif
|
||||
|
||||
#if defined(__WXMAC__)
|
||||
case config::AudioApi::kCoreAudio:
|
||||
return audio::internal::CreateCoreAudioDriver();
|
||||
#endif
|
||||
|
||||
case config::AudioApi::kLast:
|
||||
default:
|
||||
VBAM_NOTREACHED();
|
||||
|
|
|
@ -0,0 +1,589 @@
|
|||
#ifdef __APPLE__
|
||||
#include "wx/audio/internal/coreaudio.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 <CoreAudio/CoreAudio.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
#include <stdlib.h>
|
||||
#include <memory.h>
|
||||
|
||||
#include <wx/arrstr.h>
|
||||
#include <wx/log.h>
|
||||
#include <wx/translation.h>
|
||||
#include <wx/utils.h>
|
||||
|
||||
#include "core/base/sound_driver.h"
|
||||
#include "core/base/check.h"
|
||||
#include "core/gba/gbaGlobals.h"
|
||||
#include "core/gba/gbaSound.h"
|
||||
#include "core/base/ringbuffer.h"
|
||||
#include "wx/config/option-proxy.h"
|
||||
|
||||
#ifndef kAudioObjectPropertyElementMain
|
||||
#define kAudioObjectPropertyElementMain kAudioObjectPropertyElementMaster
|
||||
#endif
|
||||
|
||||
#ifndef LOGALL
|
||||
// replace logging functions with comments
|
||||
#ifdef winlog
|
||||
#undef winlog
|
||||
#endif
|
||||
// https://stackoverflow.com/a/1306690/262458
|
||||
#define winlog(x, ...) \
|
||||
do { \
|
||||
} while (0)
|
||||
#define debugState() //
|
||||
#endif
|
||||
|
||||
extern int emulating;
|
||||
|
||||
namespace audio {
|
||||
namespace internal {
|
||||
|
||||
namespace {
|
||||
|
||||
static const AudioObjectPropertyAddress devlist_address = {
|
||||
kAudioHardwarePropertyDevices,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMain
|
||||
};
|
||||
|
||||
static const AudioObjectPropertyAddress default_playback_device_address = {
|
||||
kAudioHardwarePropertyDefaultOutputDevice,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMain
|
||||
};
|
||||
|
||||
const AudioObjectPropertyAddress addr = {
|
||||
kAudioDevicePropertyStreamConfiguration,
|
||||
kAudioDevicePropertyScopeOutput,
|
||||
kAudioObjectPropertyElementMain
|
||||
};
|
||||
|
||||
const AudioObjectPropertyAddress nameaddr = {
|
||||
kAudioObjectPropertyName,
|
||||
kAudioDevicePropertyScopeOutput,
|
||||
kAudioObjectPropertyElementMain
|
||||
};
|
||||
|
||||
static const AudioObjectPropertyAddress alive_address = {
|
||||
kAudioDevicePropertyDeviceIsAlive,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMain
|
||||
};
|
||||
|
||||
class CoreAudioAudio : public SoundDriver {
|
||||
public:
|
||||
CoreAudioAudio();
|
||||
~CoreAudioAudio() override;
|
||||
|
||||
bool init(long sampleRate) override; // initialize the sound buffer queue
|
||||
void deinit();
|
||||
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
|
||||
|
||||
AudioStreamBasicDescription description;
|
||||
AudioQueueBufferRef buffer = NULL;
|
||||
AudioQueueBufferRef *buffers = NULL;
|
||||
AudioQueueRef audioQueue = NULL;
|
||||
AudioDeviceID device = 0;
|
||||
bool must_wait = false;
|
||||
uint16_t current_rate = 0;
|
||||
int current_buffer = 0;
|
||||
AudioTimeStamp timestamp;
|
||||
|
||||
private:
|
||||
int soundBufferLen = 0;
|
||||
AudioDeviceID GetCoreAudioDevice(wxString name);
|
||||
void setBuffer(uint16_t* finalWave, int length);
|
||||
|
||||
bool initialized = false;
|
||||
};
|
||||
|
||||
static void PlaybackBufferReadyCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer)
|
||||
{
|
||||
int bufIndex = 0;
|
||||
CoreAudioAudio *cadevice = (CoreAudioAudio *)inUserData;
|
||||
|
||||
for (int i = 0; i < OPTION(kSoundBuffers); i++)
|
||||
{
|
||||
if (inBuffer == cadevice->buffers[i])
|
||||
{
|
||||
bufIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cadevice->buffer = cadevice->buffers[bufIndex];
|
||||
|
||||
// buffer is unexpectedly here? We're probably dying, but try to requeue this buffer with silence.
|
||||
if (cadevice->buffer) {
|
||||
memset(cadevice->buffer->mAudioData, 0, cadevice->buffer->mAudioDataBytesCapacity);
|
||||
cadevice->buffer->mAudioDataByteSize = 0;
|
||||
}
|
||||
|
||||
if ((OPTION(kSoundBuffers) - 1) >= bufIndex) {
|
||||
cadevice->must_wait = false;
|
||||
cadevice->current_buffer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static OSStatus DeviceAliveNotification(AudioObjectID devid, UInt32 num_addr, const AudioObjectPropertyAddress *addrs, void *data)
|
||||
{
|
||||
CoreAudioAudio *cadevice = (CoreAudioAudio *)data;
|
||||
UInt32 alive = 1;
|
||||
UInt32 size = sizeof(alive);
|
||||
const OSStatus error = AudioObjectGetPropertyData(devid, addrs, 0, NULL, &size, &alive);
|
||||
(void)num_addr;
|
||||
|
||||
bool dead = false;
|
||||
if (error == kAudioHardwareBadDeviceError) {
|
||||
dead = true; // device was unplugged.
|
||||
} else if ((error == kAudioHardwareNoError) && (!alive)) {
|
||||
dead = true; // device died in some other way.
|
||||
}
|
||||
|
||||
if (dead) {
|
||||
cadevice->reset();
|
||||
}
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
AudioDeviceID CoreAudioAudio::GetCoreAudioDevice(wxString name)
|
||||
{
|
||||
uint32_t size = 0;
|
||||
AudioDeviceID dev = 0;
|
||||
AudioDeviceID *devs = NULL;
|
||||
AudioBufferList *buflist = NULL;
|
||||
OSStatus result = 0;
|
||||
CFStringRef cfstr = NULL;
|
||||
|
||||
if (name == _("Default device")) {
|
||||
if (AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &default_playback_device_address, 0, NULL, &size) != kAudioHardwareNoError) {
|
||||
return 0;
|
||||
} else if ((devs = (AudioDeviceID *)malloc(size)) == NULL) {
|
||||
return 0;
|
||||
} else if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &default_playback_device_address, 0, NULL, &size, devs) != kAudioHardwareNoError) {
|
||||
free(devs);
|
||||
return 0;
|
||||
}
|
||||
dev = devs[0];
|
||||
|
||||
free(devs);
|
||||
|
||||
return dev;
|
||||
} else {
|
||||
if (AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &devlist_address, 0, NULL, &size) != kAudioHardwareNoError) {
|
||||
return 0;
|
||||
} else if ((devs = (AudioDeviceID *)malloc(size)) == NULL) {
|
||||
return 0;
|
||||
} else if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &devlist_address, 0, NULL, &size, devs) != kAudioHardwareNoError) {
|
||||
free(devs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const UInt32 total_devices = (UInt32) (size / sizeof(AudioDeviceID));
|
||||
for (UInt32 i = 0; i < total_devices; i++)
|
||||
{
|
||||
if (AudioObjectGetPropertyDataSize(devs[i], &addr, 0, NULL, &size) != noErr) {
|
||||
continue;
|
||||
} else if ((buflist = (AudioBufferList *)malloc(size)) == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
result = AudioObjectGetPropertyData(devs[i], &addr, 0, NULL, &size, buflist);
|
||||
|
||||
if (result != noErr) {
|
||||
free(buflist);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (buflist->mNumberBuffers == 0) {
|
||||
free(buflist);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
size = sizeof(CFStringRef);
|
||||
|
||||
if (AudioObjectGetPropertyData(devs[i], &nameaddr, 0, NULL, &size, &cfstr) != kAudioHardwareNoError) {
|
||||
free(buflist);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
CFIndex len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr), kCFStringEncodingUTF8);
|
||||
const char *name = (const char *)malloc(len + 1);
|
||||
CFStringGetCString(cfstr, (char *)name, len + 1, kCFStringEncodingUTF8);
|
||||
|
||||
if (name != NULL)
|
||||
{
|
||||
const wxString device_name(name, wxConvLibc);
|
||||
if (device_name == name) {
|
||||
dev = devs[i];
|
||||
free(devs);
|
||||
free(buflist);
|
||||
free((void *)name);
|
||||
|
||||
return dev;
|
||||
}
|
||||
}
|
||||
|
||||
free(buflist);
|
||||
free((void *)name);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
CoreAudioAudio::CoreAudioAudio():
|
||||
current_rate(static_cast<unsigned short>(coreOptions.throttle)),
|
||||
initialized(false)
|
||||
{}
|
||||
|
||||
void CoreAudioAudio::deinit() {
|
||||
if (!initialized)
|
||||
return;
|
||||
|
||||
AudioObjectRemovePropertyListener(device, &alive_address, DeviceAliveNotification, this);
|
||||
|
||||
for (int i = 0; i < OPTION(kSoundBuffers); i++) {
|
||||
AudioQueueFreeBuffer(audioQueue, buffers[i]);
|
||||
}
|
||||
|
||||
buffer = NULL;
|
||||
AudioQueueStop(audioQueue, TRUE);
|
||||
device = 0;
|
||||
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
CoreAudioAudio::~CoreAudioAudio() {
|
||||
deinit();
|
||||
}
|
||||
|
||||
static bool AssignDeviceToAudioQueue(CoreAudioAudio *cadevice)
|
||||
{
|
||||
const AudioObjectPropertyAddress prop = {
|
||||
kAudioDevicePropertyDeviceUID,
|
||||
kAudioDevicePropertyScopeOutput,
|
||||
kAudioObjectPropertyElementMain
|
||||
};
|
||||
|
||||
OSStatus result;
|
||||
CFStringRef devuid;
|
||||
UInt32 devuidsize = sizeof(devuid);
|
||||
result = AudioObjectGetPropertyData(cadevice->device, &prop, 0, NULL, &devuidsize, &devuid);
|
||||
|
||||
if (result != noErr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
result = AudioQueueSetProperty(cadevice->audioQueue, kAudioQueueProperty_CurrentDevice, &devuid, devuidsize);
|
||||
|
||||
if (result != noErr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CFRelease(devuid); // Release devuid; we're done with it and AudioQueueSetProperty should have retained if it wants to keep it.
|
||||
|
||||
return (bool)(result == noErr);
|
||||
}
|
||||
|
||||
static bool PrepareDevice(CoreAudioAudio *cadevice)
|
||||
{
|
||||
const AudioDeviceID devid = cadevice->device;
|
||||
OSStatus result = noErr;
|
||||
UInt32 size = 0;
|
||||
|
||||
AudioObjectPropertyAddress addr = {
|
||||
0,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMain
|
||||
};
|
||||
|
||||
UInt32 alive = 0;
|
||||
size = sizeof(alive);
|
||||
addr.mSelector = kAudioDevicePropertyDeviceIsAlive;
|
||||
addr.mScope = kAudioDevicePropertyScopeOutput;
|
||||
result = AudioObjectGetPropertyData(devid, &addr, 0, NULL, &size, &alive);
|
||||
|
||||
if (result != noErr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!alive) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// some devices don't support this property, so errors are fine here.
|
||||
pid_t pid = 0;
|
||||
size = sizeof(pid);
|
||||
addr.mSelector = kAudioDevicePropertyHogMode;
|
||||
result = AudioObjectGetPropertyData(devid, &addr, 0, NULL, &size, &pid);
|
||||
if ((result == noErr) && (pid != -1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CoreAudioAudio::init(long sampleRate) {
|
||||
OSStatus result = 0;
|
||||
|
||||
if (initialized) {
|
||||
deinit();
|
||||
}
|
||||
|
||||
AudioChannelLayout layout;
|
||||
memset(&layout, 0, sizeof(layout));
|
||||
|
||||
device = GetCoreAudioDevice(OPTION(kSoundAudioDevice));
|
||||
|
||||
if (device == 0) {
|
||||
fprintf(stderr, "Couldn't get Core Audio device\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
description.mFormatID = kAudioFormatLinearPCM;
|
||||
description.mFormatFlags = kLinearPCMFormatFlagIsPacked;
|
||||
description.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
|
||||
description.mChannelsPerFrame = 2;
|
||||
description.mBitsPerChannel = 16;
|
||||
description.mSampleRate = current_rate ? static_cast<UInt32>(sampleRate * (current_rate / 100.0)) : static_cast<UInt32>(sampleRate);
|
||||
description.mFramesPerPacket = 1;
|
||||
description.mBytesPerFrame = description.mChannelsPerFrame * (description.mBitsPerChannel / 8);
|
||||
description.mBytesPerPacket = description.mBytesPerFrame * description.mFramesPerPacket;
|
||||
|
||||
soundBufferLen = (sampleRate / 60) * description.mBytesPerPacket;
|
||||
|
||||
PrepareDevice(this);
|
||||
|
||||
AudioObjectAddPropertyListener(device, &alive_address, DeviceAliveNotification, this);
|
||||
result = AudioQueueNewOutput(&description, PlaybackBufferReadyCallback, this, NULL, NULL, 0, &audioQueue);
|
||||
|
||||
if (result != noErr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AssignDeviceToAudioQueue(this);
|
||||
|
||||
layout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
|
||||
result = AudioQueueSetProperty(audioQueue, kAudioQueueProperty_ChannelLayout, &layout, sizeof(layout));
|
||||
|
||||
if (result != noErr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
buffers = (AudioQueueBufferRef *)calloc(OPTION(kSoundBuffers), sizeof(AudioQueueBufferRef));
|
||||
|
||||
for (int i = 0; i < OPTION(kSoundBuffers); i++) {
|
||||
result = AudioQueueAllocateBuffer(audioQueue, soundBufferLen, &buffers[i]);
|
||||
|
||||
if (result != noErr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(buffers[i]->mAudioData, 0x00, buffers[i]->mAudioDataBytesCapacity);
|
||||
buffers[i]->mAudioDataByteSize = buffers[i]->mAudioDataBytesCapacity;
|
||||
|
||||
PlaybackBufferReadyCallback(this, audioQueue, buffers[i]);
|
||||
buffers[i]->mAudioDataByteSize = 0;
|
||||
}
|
||||
|
||||
result = AudioQueueStart(audioQueue, NULL);
|
||||
|
||||
return initialized = true;
|
||||
}
|
||||
|
||||
void CoreAudioAudio::setThrottle(unsigned short throttle_) {
|
||||
if (!initialized)
|
||||
return;
|
||||
|
||||
if (throttle_ == 0)
|
||||
throttle_ = 200;
|
||||
|
||||
current_rate = throttle_;
|
||||
reset();
|
||||
}
|
||||
|
||||
void CoreAudioAudio::resume() {
|
||||
if (!initialized)
|
||||
return;
|
||||
|
||||
AudioQueueDeviceGetNearestStartTime(audioQueue, ×tamp, 0);
|
||||
AudioQueueStart(audioQueue, NULL);
|
||||
|
||||
winlog("CoreAudioAudio::resume\n");
|
||||
}
|
||||
|
||||
void CoreAudioAudio::pause() {
|
||||
if (!initialized)
|
||||
return;
|
||||
|
||||
AudioQueuePause(audioQueue);
|
||||
|
||||
winlog("CoreAudioAudio::pause\n");
|
||||
}
|
||||
|
||||
void CoreAudioAudio::reset() {
|
||||
if (!initialized)
|
||||
return;
|
||||
|
||||
winlog("CoreAudioAudio::reset\n");
|
||||
|
||||
init(soundGetSampleRate());
|
||||
}
|
||||
|
||||
void CoreAudioAudio::setBuffer(uint16_t* finalWave, int length) {
|
||||
AudioQueueBufferRef this_buf = NULL;
|
||||
|
||||
this_buf = buffers[current_buffer];
|
||||
|
||||
if (this_buf == NULL) {
|
||||
fprintf(stderr, "Current buffer is NULL\n");
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy((uint8_t *)this_buf->mAudioData + this_buf->mAudioDataByteSize, finalWave, length);
|
||||
this_buf->mAudioDataByteSize += (UInt32)length;
|
||||
|
||||
if (this_buf->mAudioDataByteSize == this_buf->mAudioDataBytesCapacity) {
|
||||
AudioQueueEnqueueBufferWithParameters(audioQueue, this_buf, 0, NULL, 0, 0, 0, NULL, NULL, ×tamp);
|
||||
}
|
||||
|
||||
must_wait = true;
|
||||
}
|
||||
|
||||
void CoreAudioAudio::write(uint16_t* finalWave, int length) {
|
||||
std::size_t samples = length / (description.mBitsPerChannel / 8);
|
||||
std::size_t avail = 0;
|
||||
|
||||
if (!initialized)
|
||||
return;
|
||||
|
||||
while ((avail = ((buffers[current_buffer]->mAudioDataBytesCapacity - buffers[current_buffer]->mAudioDataByteSize) / (description.mBitsPerChannel / 8))) < samples)
|
||||
{
|
||||
setBuffer(finalWave, (avail * (description.mBitsPerChannel / 8)));
|
||||
|
||||
finalWave += (avail * (description.mBitsPerChannel / 8));
|
||||
samples -= avail;
|
||||
|
||||
if (buffers[current_buffer]->mAudioDataByteSize == buffers[current_buffer]->mAudioDataBytesCapacity) {
|
||||
current_buffer++;
|
||||
}
|
||||
|
||||
while ((current_buffer >= (OPTION(kSoundBuffers) - 1)) && must_wait) {
|
||||
wxMilliSleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
setBuffer(finalWave, samples * (description.mBitsPerChannel / 8));
|
||||
|
||||
if (buffers[current_buffer]->mAudioDataByteSize == buffers[current_buffer]->mAudioDataBytesCapacity) {
|
||||
current_buffer++;
|
||||
}
|
||||
|
||||
while ((current_buffer >= (OPTION(kSoundBuffers) - 1)) && must_wait) {
|
||||
wxMilliSleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::vector<AudioDevice> GetCoreAudioDevices() {
|
||||
std::vector<AudioDevice> devices;
|
||||
uint32_t size = 0;
|
||||
AudioDeviceID *devs = NULL;
|
||||
AudioBufferList *buflist = NULL;
|
||||
OSStatus result = 0;
|
||||
CFStringRef cfstr = NULL;
|
||||
|
||||
devices.push_back({_("Default device"), wxEmptyString});
|
||||
|
||||
if (AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &devlist_address, 0, NULL, &size) != kAudioHardwareNoError) {
|
||||
return devices;
|
||||
} else if ((devs = (AudioDeviceID *)malloc(size)) == NULL) {
|
||||
return devices;
|
||||
} else if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &devlist_address, 0, NULL, &size, devs) != kAudioHardwareNoError) {
|
||||
free(devs);
|
||||
return devices;
|
||||
}
|
||||
|
||||
const UInt32 total_devices = (UInt32) (size / sizeof(AudioDeviceID));
|
||||
for (UInt32 i = 0; i < total_devices; i++)
|
||||
{
|
||||
if (AudioObjectGetPropertyDataSize(devs[i], &addr, 0, NULL, &size) != noErr) {
|
||||
continue;
|
||||
} else if ((buflist = (AudioBufferList *)malloc(size)) == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
result = AudioObjectGetPropertyData(devs[i], &addr, 0, NULL, &size, buflist);
|
||||
|
||||
if (result != noErr) {
|
||||
free(buflist);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (buflist->mNumberBuffers == 0) {
|
||||
free(buflist);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
size = sizeof(CFStringRef);
|
||||
|
||||
if (AudioObjectGetPropertyData(devs[i], &nameaddr, 0, NULL, &size, &cfstr) != kAudioHardwareNoError) {
|
||||
free(buflist);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
CFIndex len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr), kCFStringEncodingUTF8);
|
||||
const char *name = (const char *)malloc(len + 1);
|
||||
CFStringGetCString(cfstr, (char *)name, len + 1, kCFStringEncodingUTF8);
|
||||
|
||||
if (name != NULL)
|
||||
{
|
||||
const wxString device_name(name, wxConvLibc);
|
||||
devices.push_back({device_name, device_name});
|
||||
}
|
||||
|
||||
free(buflist);
|
||||
free((void *)name);
|
||||
}
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
std::unique_ptr<SoundDriver> CreateCoreAudioDriver() {
|
||||
winlog("newCoreAudio\n");
|
||||
return std::make_unique<CoreAudioAudio>();
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace audio
|
||||
|
||||
#endif
|
|
@ -0,0 +1,18 @@
|
|||
#ifndef WX_AUDIO_INTERNAL_COREAUDIO_H_
|
||||
#define WX_AUDIO_INTERNAL_COREAUDIO_H_
|
||||
|
||||
#include "wx/audio/audio.h"
|
||||
|
||||
namespace audio {
|
||||
namespace internal {
|
||||
|
||||
// Returns the set of OpenAL devices.
|
||||
std::vector<AudioDevice> GetCoreAudioDevices();
|
||||
|
||||
// Creates an OpenAL sound driver.
|
||||
std::unique_ptr<SoundDriver> CreateCoreAudioDriver();
|
||||
|
||||
} // namespace internal
|
||||
} // namespace audio
|
||||
|
||||
#endif // WX_AUDIO_INTERNAL_COREAUDIO_H_
|
|
@ -98,6 +98,9 @@ static const std::array<wxString, kNbAudioApis> kAudioApiStrings = {
|
|||
#if defined(VBAM_ENABLE_FAUDIO)
|
||||
"faudio",
|
||||
#endif
|
||||
#if defined(__WXMAC__)
|
||||
"coreaudio",
|
||||
#endif
|
||||
};
|
||||
|
||||
// These MUST follow the same order as the definitions of the enum above.
|
||||
|
@ -222,8 +225,12 @@ std::array<Option, kNbOptions>& Option::All() {
|
|||
/// Sound
|
||||
#if defined(VBAM_ENABLE_XAUDIO2)
|
||||
AudioApi audio_api = AudioApi::kXAudio2;
|
||||
#else
|
||||
#if defined(__WXMAC__)
|
||||
AudioApi audio_api = AudioApi::kCoreAudio;
|
||||
#else
|
||||
AudioApi audio_api = AudioApi::kOpenAL;
|
||||
#endif
|
||||
#endif
|
||||
wxString audio_dev;
|
||||
// 10 fixes stuttering on mac with openal, as opposed to 5
|
||||
|
|
|
@ -94,6 +94,9 @@ enum class AudioApi {
|
|||
#if defined(VBAM_ENABLE_FAUDIO)
|
||||
kFAudio,
|
||||
#endif // VBAM_ENABLE_FAUDIO
|
||||
#if defined(__WXMAC__)
|
||||
kCoreAudio,
|
||||
#endif
|
||||
|
||||
// Do not add anything under here.
|
||||
kLast,
|
||||
|
|
|
@ -194,6 +194,16 @@ SoundConfig::SoundConfig(wxWindow* parent) : BaseDialog(parent, "SoundConfig") {
|
|||
audio_api_button->Hide();
|
||||
#endif
|
||||
|
||||
audio_api_button = GetValidatedChild("CoreAudio");
|
||||
#if defined(__WXMAC__)
|
||||
audio_api_button->SetValidator(AudioApiValidator(config::AudioApi::kCoreAudio));
|
||||
audio_api_button->Bind(wxEVT_RADIOBUTTON,
|
||||
std::bind(&SoundConfig::OnAudioApiChanged, this, std::placeholders::_1,
|
||||
config::AudioApi::kCoreAudio));
|
||||
#else
|
||||
audio_api_button->Hide();
|
||||
#endif
|
||||
|
||||
// Upmix configuration.
|
||||
upmix_checkbox_ = GetValidatedChild<wxCheckBox>("Upmix");
|
||||
#if defined(VBAM_ENABLE_XAUDIO2) || defined(VBAM_ENABLE_FAUDIO)
|
||||
|
|
|
@ -64,9 +64,9 @@ protected:
|
|||
void DrawingPanelInit() override;
|
||||
|
||||
private:
|
||||
SDL_Window *sdlwindow;
|
||||
SDL_Texture *texture;
|
||||
SDL_Renderer *renderer;
|
||||
SDL_Window *sdlwindow = NULL;
|
||||
SDL_Texture *texture = NULL;
|
||||
SDL_Renderer *renderer = NULL;
|
||||
};
|
||||
|
||||
#if defined(__WXMSW__) && !defined(NO_D3D)
|
||||
|
|
|
@ -135,6 +135,13 @@
|
|||
<flag>wxALL|wxEXPAND</flag>
|
||||
<border>5</border>
|
||||
</object>
|
||||
<object class="sizeritem">
|
||||
<object class="wxRadioButton" name="CoreAudio">
|
||||
<label translate="0">CoreAudio</label>
|
||||
</object>
|
||||
<flag>wxALL|wxEXPAND</flag>
|
||||
<border>5</border>
|
||||
</object>
|
||||
<orient>wxHORIZONTAL</orient>
|
||||
</object>
|
||||
<flag>wxEXPAND</flag>
|
||||
|
|
Loading…
Reference in New Issue