diff --git a/Makefile b/Makefile
index 0cadf95c73..26cc1530f7 100644
--- a/Makefile
+++ b/Makefile
@@ -83,6 +83,11 @@ ifeq ($(HAVE_PULSE), 1)
DEFINES += $(PULSE_CFLAGS)
endif
+ifeq ($(HAVE_COREAUDIO), 1)
+ OBJ += audio/coreaudio.o
+ LIBS += -framework CoreServices -framework CoreAudio -framework AudioUnit
+endif
+
ifeq ($(HAVE_SDL), 1)
OBJ += gfx/sdl.o gfx/gl.o input/sdl.o audio/sdl.o fifo_buffer.o
DEFINES += $(SDL_CFLAGS) $(BSD_LOCAL_INC)
diff --git a/audio/coreaudio.c b/audio/coreaudio.c
new file mode 100644
index 0000000000..27b514cb96
--- /dev/null
+++ b/audio/coreaudio.c
@@ -0,0 +1,278 @@
+/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
+ * Copyright (C) 2010-2011 - Hans-Kristian Arntzen
+ *
+ * Some code herein may be based on code found in BSNES.
+ *
+ * SSNES is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with SSNES.
+ * If not, see .
+ */
+
+
+#include "driver.h"
+#include "general.h"
+#include "fifo_buffer.h"
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+typedef struct coreaudio
+{
+ pthread_mutex_t lock;
+ pthread_cond_t cond;
+
+ ComponentInstance dev;
+ bool dev_alive;
+
+ fifo_buffer_t *buffer;
+ bool nonblock;
+} coreaudio_t;
+
+static void coreaudio_free(void *data)
+{
+ coreaudio_t *dev = data;
+ if (!dev)
+ return;
+
+ if (dev->dev_alive)
+ {
+ AudioOutputUnitStop(dev->dev);
+ CloseComponent(dev->dev);
+ }
+
+ if (dev->buffer)
+ fifo_free(dev->buffer);
+
+ pthread_mutex_destroy(&dev->lock);
+ pthread_cond_destroy(&dev->cond);
+
+ free(dev);
+}
+
+static OSStatus audio_cb(void *userdata, AudioUnitRenderActionFlags *action_flags,
+ const AudioTimeStamp *time_stamp, UInt32 bus_number,
+ UInt32 number_frames, AudioBufferList *io_data)
+{
+ coreaudio_t *dev = userdata;
+ (void)time_stamp;
+ (void)bus_number;
+ (void)number_frames;
+
+ if (!io_data)
+ return noErr;
+ if (io_data->mNumberBuffers != 1)
+ return noErr;
+
+ unsigned write_avail = io_data->mBuffers[0].mDataByteSize;
+
+ pthread_mutex_lock(&dev->lock);
+ if (fifo_read_avail(dev->buffer) < write_avail)
+ {
+ *action_flags = kAudioUnitRenderAction_OutputIsSilence;
+ pthread_mutex_unlock(&dev->lock);
+ pthread_cond_signal(&dev->cond); // Technically possible to deadlock without.
+ return noErr;
+ }
+
+ void *outbuf = io_data->mBuffers[0].mData;
+ fifo_read(dev->buffer, output, write_avail);
+ pthread_mutex_unlock(&dev->lock);
+ pthread_cond_signal(&dev->cond);
+ return noErr;
+}
+
+static void* coreaudio_init(const char* device, unsigned rate, unsigned latency)
+{
+ (void)device;
+
+ coreaudio_t *dev = calloc(1, sizeof(*dev));
+ if (!dev)
+ return NULL;
+
+ CFRunLoopRef run_loop = NULL;
+ AudioObjectPropertyAddress addr = {
+ kAudioHardwarePropertyRunLoop,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyyElementMaster
+ };
+
+ pthread_mutex_init(&dev->lock, NULL);
+ pthread_cond_init(&dev->cond, NULL);
+ AudioObjectSetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL,
+ sizeof(CFRunLoopRef), &run_loop);
+
+ ComponentDescription desc = {
+ .componentType = kAudioUnitType_Output,
+ .componentSubType = kAudioUnitSubType_HALOutput,
+ .componentManufacturer = kAudioUnitManufacturer_Apple,
+ };
+
+ Component comp = FindNextComponent(NULL, &desc);
+ if (comp == NULL)
+ goto error;
+
+ OSStatus res = OpenAComponent(comp, &dev->dev);
+ if (res != noErr)
+ goto error;
+
+ dev->dev_alive = true;
+
+ AudioStreamBasicDescription stream_desc = {
+ .mSampleRate = rate,
+ .mBitsPerChannel = sizeof(float) * CHAR_BIT,
+ .mChannelsPerFrame = 2,
+ .mBytesPerPacket = 2 * sizeof(float),
+ .mFramesPerPacket = 1,
+ .mFormatID = kAudioFormatLinearPCM,
+ .mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked | (is_little_endian() ? 0 : kAudioFormatFlagisBigEndian),
+ };
+
+ res = AudioUnitSetProperty(dev->dev, kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Input, 0, &stream_desc, sizeof(stream_desc));
+ if (res != noErr)
+ goto error;
+
+ AudioStreamBasicDescription real_desc;
+ UInt32 i_size = sizeof(real_desc);
+ res = AudioUnitGetProperty(dev->dev, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &real_desc, &i_size);
+ if (res != noErr)
+ goto error;
+
+ SSNES_LOG("[CoreAudio]: Using output sample rate of %.1f\n", (float)real_desc.mSampleRate);
+ g_settings.audio.out_rate = real_desc.mSampleRate;
+
+ if (real_desc.mChannelsPerFrame != stream_desc.mChannelsPerFrame)
+ goto error;
+ if (real_desc.mBitsPerChannel != stream_desc.mBitsPerChannel)
+ goto error;
+ if (real_desc.mFormatFlags != stream_desc.mFormatFlags)
+ goto error;
+ if (real_desc.mFormatID != stream_desc.mFormatID)
+ goto error;
+
+ AudioChannelLayout layout = {
+ .mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelBitmap,
+ .mChannelBitmap = (1 << 2) - 1,
+ };
+
+ res = AudioUnitSetProperty(dev->dev, kAudioUnitProperty_AudioChannelLayout,
+ kAudioUnitScope_Input, 0, &layout, sizeof(layout));
+ if (res != noErr)
+ goto error;
+
+ AURenderCallbackStruct cb = {
+ .inputProc = audio_cb,
+ .inputProcRefCon = dev,
+ };
+
+ res = AudioUnitSetProperty(dev->dev, kAudioUnitProperty_SetRenderCallback,
+ kAudioUnitScope_Input, 0, &cb, sizeof(cb));
+ if (res != noErr)
+ goto error;
+
+ res = AudioUnitInitialize(dev->dev);
+ if (res != noErr)
+ goto error;
+
+ size_t fifo_size = (latency * g_settings.audio.out_rate) / 1000;
+ fifo_size *= 2 * sizeof(float);
+
+ dev->buffer = fifo_new(fifo_size);
+ if (!dev->buffer)
+ goto error;
+
+ SSNES_LOG("[CoreAudio]: Using buffer size of %u bytes\n", (unsigned)fifo_size);
+
+ res = AudioOutputUnitStart(dev->dev);
+ if (res != noErr)
+ goto error;
+
+ return dev;
+
+error:
+ SSNES_ERR("[CoreAudio]: Failed to initialize driver ...\n");
+ coreaudio_free(dev);
+ return NULL;
+}
+
+static ssize_t coreaudio_write(void* data, const void* buf_, size_t size)
+{
+ coreaudio_t *dev = data;
+
+ const uint8_t *buf = buf_;
+ size_t written = 0;
+
+ while (size > 0)
+ {
+ pthread_mutex_lock(&dev->lock);
+
+ size_t write_avail = fifo_write_avail(dev->buffer);
+ if (write_avail > size)
+ write_avail = size;
+
+ fifo_write(dev->buffer, buf, write_avail);
+ buf += write_avail;
+ written += write_avail;
+ size -= write_avail;
+
+ if (dev->nonblock)
+ {
+ pthread_mutex_unlock(&dev->lock);
+ break;
+ }
+
+ if (write_avail == 0)
+ pthread_cond_wait(&dev->cond, &dev->lock);
+ pthread_mutex_unlock(&dev->lock);
+ }
+
+ return written;
+}
+
+static bool coreaudio_stop(void *data)
+{
+ coreaudio_t *dev = data;
+ return AudioOutputUnitStop(dev->dev) == noErr;
+}
+
+static void coreaudio_set_nonblock_state(void *data, bool state)
+{
+ coreaudio_t *dev = data;
+ dev->nonblock = state;
+}
+
+static bool coreaudio_start(void *data)
+{
+ coreaudio_t *dev = data;
+ return AudioOutputUnitStart(dev->dev) == noErr;
+}
+
+static bool coreaudio_use_float(void *data)
+{
+ (void)data;
+ return true;
+}
+
+const audio_driver_t audio_rsound = {
+ .init = coreaudio_init,
+ .write = coreaudio_write,
+ .stop = coreaudio_stop,
+ .start = coreaudio_start,
+ .set_nonblock_state = coreaudio_set_nonblock_state,
+ .free = coreaudio_free,
+ .use_float = coreaudio_use_float,
+ .ident = "coreaudio"
+};
+
diff --git a/config.def.h b/config.def.h
index 9f3bd6e56e..22a872c014 100644
--- a/config.def.h
+++ b/config.def.h
@@ -58,6 +58,7 @@
#define AUDIO_PULSE 10
#define AUDIO_EXT 15
#define AUDIO_DSOUND 16
+#define AUDIO_COREAUDIO 17
////////////////////////
#define INPUT_SDL 7
#define INPUT_X 12
@@ -83,6 +84,8 @@
#define AUDIO_DEFAULT_DRIVER AUDIO_OSS
#elif defined(HAVE_JACK)
#define AUDIO_DEFAULT_DRIVER AUDIO_JACK
+#elif defined(HAVE_COREAUDIO)
+#define AUDIO_DEFAULT_DRIVER AUDIO_COREAUDIO
#elif defined(HAVE_AL)
#define AUDIO_DEFAULT_DRIVER AUDIO_AL
#elif defined(HAVE_DSOUND)
diff --git a/driver.c b/driver.c
index 0d9b5ccef1..353339d477 100644
--- a/driver.c
+++ b/driver.c
@@ -38,6 +38,9 @@ static const audio_driver_t *audio_drivers[] = {
#ifdef HAVE_RSOUND
&audio_rsound,
#endif
+#ifdef HAVE_COREAUDIO
+ &audio_coreaudio,
+#endif
#ifdef HAVE_AL
&audio_openal,
#endif
diff --git a/driver.h b/driver.h
index 30df26393c..743a58347a 100644
--- a/driver.h
+++ b/driver.h
@@ -159,6 +159,7 @@ extern const audio_driver_t audio_xa;
extern const audio_driver_t audio_pulse;
extern const audio_driver_t audio_ext;
extern const audio_driver_t audio_dsound;
+extern const audio_driver_t audio_coreaudio;
extern const video_driver_t video_gl;
extern const video_driver_t video_xvideo;
extern const video_driver_t video_sdl;
diff --git a/qb/config.libs.sh b/qb/config.libs.sh
index c12689fc29..f3ba583371 100644
--- a/qb/config.libs.sh
+++ b/qb/config.libs.sh
@@ -46,6 +46,8 @@ check_pkgconf ROAR libroar
check_pkgconf JACK jack 0.120.1
check_pkgconf PULSE libpulse
+check_lib COREAUDIO "-framework AudioUnit" AudioUnitInitialize
+
check_pkgconf SDL sdl 1.2.10
check_critical SDL "Cannot find SDL 1.2 library."
check_pkgconf SDL_NEW sdl 1.3
@@ -81,7 +83,7 @@ check_pkgconf PYTHON python3
add_define_make OS $OS
# Creates config.mk and config.h.
-VARS="ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK PULSE SDL DYLIB CG XML SDL_IMAGE DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL SWSCALE SRC CONFIGFILE FREETYPE XVIDEO NETPLAY FBO STRL PYTHON"
+VARS="ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK COREAUDIO PULSE SDL DYLIB CG XML SDL_IMAGE DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL SWSCALE SRC CONFIGFILE FREETYPE XVIDEO NETPLAY FBO STRL PYTHON"
create_config_make config.mk $VARS
create_config_header config.h $VARS
diff --git a/qb/config.params.sh b/qb/config.params.sh
index 6f526649d4..40b8f16a4c 100644
--- a/qb/config.params.sh
+++ b/qb/config.params.sh
@@ -23,6 +23,7 @@ add_command_line_enable RSOUND "Enable RSound support" auto
add_command_line_enable ROAR "Enable RoarAudio support" auto
add_command_line_enable AL "Enable OpenAL support" auto
add_command_line_enable JACK "Enable JACK support" auto
+add_command_line_enable COREAUDIO "Enable CoreAudio support" auto
add_command_line_enable PULSE "Enable PulseAudio support" auto
add_command_line_enable FREETYPE "Enable FreeType support" auto
add_command_line_enable XVIDEO "Enable XVideo support" auto
diff --git a/settings.c b/settings.c
index e0e9b1a32f..d56e3f031a 100644
--- a/settings.c
+++ b/settings.c
@@ -71,6 +71,9 @@ static void set_defaults(void)
case AUDIO_ROAR:
def_audio = "roar";
break;
+ case AUDIO_COREAUDIO:
+ def_audio = "coreaudio";
+ break;
case AUDIO_AL:
def_audio = "openal";
break;