RetroArch/audio/drivers/coreaudio.c

469 lines
12 KiB
C
Raw Normal View History

2012-04-21 21:13:50 +00:00
/* RetroArch - A frontend for libretro.
2014-01-01 00:50:59 +00:00
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
* Copyright (C) 2011-2014 - Chris Moeller
2011-08-08 15:27:52 +00:00
*
2012-04-21 21:13:50 +00:00
* RetroArch is free software: you can redistribute it and/or modify it under the terms
2011-08-08 15:27:52 +00:00
* 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.
*
2012-04-21 21:13:50 +00:00
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
2011-08-08 15:27:52 +00:00
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
2012-04-21 21:31:57 +00:00
* You should have received a copy of the GNU General Public License along with RetroArch.
2011-08-08 15:27:52 +00:00
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
2015-09-14 01:36:59 +00:00
2011-08-08 15:27:52 +00:00
2015-06-02 09:09:54 +00:00
#if TARGET_OS_IPHONE
#include <AudioToolbox/AudioToolbox.h>
2015-06-02 09:09:54 +00:00
#else
#include <CoreAudio/CoreAudio.h>
#endif
#include <CoreAudio/CoreAudioTypes.h>
2011-08-08 15:27:52 +00:00
#include <AudioUnit/AudioUnit.h>
#include <AudioUnit/AUComponent.h>
2015-09-14 01:36:59 +00:00
#include <boolean.h>
#include <queues/fifo_queue.h>
#include <rthreads/rthreads.h>
2015-09-14 01:36:59 +00:00
#include <retro_endianness.h>
2016-01-21 01:48:00 +00:00
#include <string/stdstring.h>
2015-09-14 01:36:59 +00:00
2015-11-23 18:40:09 +00:00
#include "../audio_driver.h"
2015-11-23 19:25:30 +00:00
#include "../../configuration.h"
2015-11-23 11:07:00 +00:00
#include "../../verbosity.h"
2015-09-14 01:36:59 +00:00
2015-04-19 23:32:51 +00:00
#if defined(__powerpc__) || defined(__ppc__) || defined(__POWERPC__)
#ifndef OSX_PPC
#define OSX_PPC
#endif
#endif
2011-08-08 15:27:52 +00:00
typedef struct coreaudio
{
slock_t *lock;
scond_t *cond;
2011-08-08 15:27:52 +00:00
2014-05-22 20:11:14 +00:00
#ifdef OSX_PPC
ComponentInstance dev;
#else
AudioComponentInstance dev;
2014-05-22 20:11:14 +00:00
#endif
2011-08-08 15:27:52 +00:00
bool dev_alive;
bool is_paused;
2011-08-08 15:27:52 +00:00
fifo_buffer_t *buffer;
bool nonblock;
2012-02-26 00:22:07 +00:00
size_t buffer_size;
2011-08-08 15:27:52 +00:00
} coreaudio_t;
static bool g_interrupted;
2011-08-08 15:27:52 +00:00
static void coreaudio_free(void *data)
{
2011-12-24 12:46:12 +00:00
coreaudio_t *dev = (coreaudio_t*)data;
2015-01-12 04:05:56 +00:00
2011-08-08 15:27:52 +00:00
if (!dev)
return;
if (dev->dev_alive)
{
AudioOutputUnitStop(dev->dev);
#ifdef OSX_PPC
CloseComponent(dev->dev);
#else
AudioComponentInstanceDispose(dev->dev);
#endif
2011-08-08 15:27:52 +00:00
}
if (dev->buffer)
fifo_free(dev->buffer);
slock_free(dev->lock);
scond_free(dev->cond);
2011-08-08 15:27:52 +00:00
free(dev);
}
2014-09-09 20:24:29 +00:00
static OSStatus audio_write_cb(void *userdata,
AudioUnitRenderActionFlags *action_flags,
2011-08-08 15:27:52 +00:00
const AudioTimeStamp *time_stamp, UInt32 bus_number,
UInt32 number_frames, AudioBufferList *io_data)
{
2015-04-17 08:54:03 +00:00
void *outbuf;
2015-01-12 04:05:56 +00:00
unsigned write_avail;
2011-12-24 12:46:12 +00:00
coreaudio_t *dev = (coreaudio_t*)userdata;
2015-01-12 04:05:56 +00:00
2011-08-08 15:27:52 +00:00
(void)time_stamp;
(void)bus_number;
(void)number_frames;
if (!io_data)
return noErr;
if (io_data->mNumberBuffers != 1)
return noErr;
2015-01-12 04:05:56 +00:00
write_avail = io_data->mBuffers[0].mDataByteSize;
2015-04-17 08:54:03 +00:00
outbuf = io_data->mBuffers[0].mData;
2011-08-08 15:27:52 +00:00
slock_lock(dev->lock);
2015-01-12 04:05:56 +00:00
2011-08-08 15:27:52 +00:00
if (fifo_read_avail(dev->buffer) < write_avail)
{
*action_flags = kAudioUnitRenderAction_OutputIsSilence;
2014-09-09 20:24:29 +00:00
/* Seems to be needed. */
memset(outbuf, 0, write_avail);
slock_unlock(dev->lock);
2015-04-17 08:54:03 +00:00
/* Technically possible to deadlock without. */
scond_signal(dev->cond);
2015-04-17 08:54:03 +00:00
return noErr;
2011-08-08 15:27:52 +00:00
}
2011-08-08 15:31:03 +00:00
fifo_read(dev->buffer, outbuf, write_avail);
slock_unlock(dev->lock);
scond_signal(dev->cond);
2011-08-08 15:27:52 +00:00
return noErr;
}
2015-06-02 09:11:35 +00:00
#if TARGET_OS_IPHONE
static void coreaudio_interrupt_listener(void *data, UInt32 interrupt_state)
{
(void)data;
g_interrupted = (interrupt_state == kAudioSessionBeginInterruption);
}
#else
static void choose_output_device(coreaudio_t *dev, const char* device)
{
2015-01-12 04:05:56 +00:00
unsigned i;
2015-04-17 08:54:03 +00:00
AudioDeviceID *devices;
AudioObjectPropertyAddress propaddr =
{
kAudioHardwarePropertyDevices,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
2015-04-17 08:54:03 +00:00
UInt32 size = 0, deviceCount;
2014-09-09 20:24:29 +00:00
if (AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
&propaddr, 0, 0, &size) != noErr)
return;
2015-04-17 08:54:03 +00:00
deviceCount = size / sizeof(AudioDeviceID);
devices = (AudioDeviceID*)malloc(size);
2015-03-15 15:37:43 +00:00
2015-04-17 08:54:03 +00:00
if (!devices || AudioObjectGetPropertyData(kAudioObjectSystemObject,
2014-09-09 20:24:29 +00:00
&propaddr, 0, 0, &size, devices) != noErr)
goto done;
2015-04-17 08:54:03 +00:00
propaddr.mScope = kAudioDevicePropertyScopeOutput;
propaddr.mSelector = kAudioDevicePropertyDeviceName;
2015-04-17 08:54:03 +00:00
size = 1024;
2015-01-12 04:05:56 +00:00
for (i = 0; i < deviceCount; i ++)
{
char device_name[1024];
device_name[0] = 0;
2014-09-09 20:24:29 +00:00
if (AudioObjectGetPropertyData(devices[i],
&propaddr, 0, 0, &size, device_name) == noErr
2016-01-21 01:48:00 +00:00
&& string_is_equal(device_name, device))
{
2014-09-09 20:24:29 +00:00
AudioUnitSetProperty(dev->dev, kAudioOutputUnitProperty_CurrentDevice,
kAudioUnitScope_Global, 0, &devices[i], sizeof(AudioDeviceID));
goto done;
}
}
done:
free(devices);
}
#endif
2014-09-09 20:24:29 +00:00
static void *coreaudio_init(const char *device,
unsigned rate, unsigned latency)
2011-08-08 15:27:52 +00:00
{
2015-03-15 04:52:26 +00:00
size_t fifo_size;
2015-01-12 04:05:56 +00:00
UInt32 i_size;
2015-03-15 04:52:26 +00:00
AudioStreamBasicDescription real_desc;
2015-03-15 04:55:03 +00:00
#ifdef OSX_PPC
Component comp;
#else
AudioComponent comp;
#endif
2015-06-02 09:09:54 +00:00
#ifndef TARGET_OS_IPHONE
2015-12-04 09:38:51 +00:00
AudioChannelLayout layout = {0};
2015-03-15 04:55:03 +00:00
#endif
2015-12-04 09:38:51 +00:00
AURenderCallbackStruct cb = {0};
2015-03-15 04:52:26 +00:00
AudioStreamBasicDescription stream_desc = {0};
2015-12-04 09:38:51 +00:00
bool component_unavailable = false;
static bool session_initialized = false;
coreaudio_t *dev = NULL;
2015-03-15 04:52:26 +00:00
#ifdef OSX_PPC
2015-12-04 09:38:51 +00:00
ComponentDescription desc = {0};
2015-03-15 04:52:26 +00:00
#else
2015-12-04 09:38:51 +00:00
AudioComponentDescription desc = {0};
2015-03-15 04:52:26 +00:00
#endif
2015-12-04 09:38:51 +00:00
settings_t *settings = config_get_ptr();
2015-01-12 04:05:56 +00:00
(void)session_initialized;
2011-08-08 15:27:52 +00:00
(void)device;
2015-03-15 04:52:26 +00:00
dev = (coreaudio_t*)calloc(1, sizeof(*dev));
2011-08-08 15:27:52 +00:00
if (!dev)
return NULL;
dev->lock = slock_new();
dev->cond = scond_new();
2011-08-08 15:27:52 +00:00
2015-06-02 09:09:54 +00:00
#if TARGET_OS_IPHONE
if (!session_initialized)
{
session_initialized = true;
AudioSessionInitialize(0, 0, coreaudio_interrupt_listener, 0);
AudioSessionSetActive(true);
}
#endif
2015-03-15 04:52:26 +00:00
/* Create AudioComponent */
2011-12-24 12:46:12 +00:00
desc.componentType = kAudioUnitType_Output;
2015-06-02 09:09:54 +00:00
#if TARGET_OS_IPHONE
desc.componentSubType = kAudioUnitSubType_RemoteIO;
#else
2011-12-24 12:46:12 +00:00
desc.componentSubType = kAudioUnitSubType_HALOutput;
#endif
2011-12-24 12:46:12 +00:00
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
2014-05-22 20:11:14 +00:00
#ifdef OSX_PPC
2015-03-15 04:52:26 +00:00
comp = FindNextComponent(NULL, &desc);
2014-05-22 20:11:14 +00:00
#else
2015-03-15 04:52:26 +00:00
comp = AudioComponentFindNext(NULL, &desc);
2014-05-22 20:11:14 +00:00
#endif
2011-08-08 15:27:52 +00:00
if (comp == NULL)
goto error;
#ifdef OSX_PPC
2015-12-04 09:38:51 +00:00
component_unavailable = (OpenAComponent(comp, &dev->dev) != noErr);
#else
2015-12-04 09:38:51 +00:00
component_unavailable = (AudioComponentInstanceNew(comp, &dev->dev) != noErr);
#endif
2015-12-04 09:38:51 +00:00
if (component_unavailable)
2011-08-08 15:27:52 +00:00
goto error;
2015-06-02 15:34:20 +00:00
#if !TARGET_OS_IPHONE
if (device)
choose_output_device(dev, device);
#endif
2011-08-08 15:27:52 +00:00
dev->dev_alive = true;
2015-03-15 04:52:26 +00:00
/* Set audio format */
stream_desc.mSampleRate = rate;
stream_desc.mBitsPerChannel = sizeof(float) * CHAR_BIT;
2011-12-24 12:46:12 +00:00
stream_desc.mChannelsPerFrame = 2;
2015-03-15 04:52:26 +00:00
stream_desc.mBytesPerPacket = 2 * sizeof(float);
stream_desc.mBytesPerFrame = 2 * sizeof(float);
stream_desc.mFramesPerPacket = 1;
stream_desc.mFormatID = kAudioFormatLinearPCM;
stream_desc.mFormatFlags = kAudioFormatFlagIsFloat |
2014-09-09 20:24:29 +00:00
kAudioFormatFlagIsPacked | (is_little_endian() ?
0 : kAudioFormatFlagIsBigEndian);
if (AudioUnitSetProperty(dev->dev, kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input, 0, &stream_desc, sizeof(stream_desc)) != noErr)
2011-08-08 15:27:52 +00:00
goto error;
2014-09-09 20:24:29 +00:00
/* Check returned audio format. */
2015-04-17 08:54:03 +00:00
i_size = sizeof(real_desc);
2014-09-09 20:24:29 +00:00
if (AudioUnitGetProperty(dev->dev, kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input, 0, &real_desc, &i_size) != noErr)
2011-08-08 15:27:52 +00:00
goto error;
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;
2014-09-09 20:24:29 +00:00
RARCH_LOG("[CoreAudio]: Using output sample rate of %.1f Hz\n",
(float)real_desc.mSampleRate);
2015-03-20 20:31:16 +00:00
settings->audio.out_rate = real_desc.mSampleRate;
2014-09-09 20:24:29 +00:00
/* Set channel layout (fails on iOS). */
2015-06-02 09:09:54 +00:00
#ifndef TARGET_OS_IPHONE
layout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
if (AudioUnitSetProperty(dev->dev, kAudioUnitProperty_AudioChannelLayout,
kAudioUnitScope_Input, 0, &layout, sizeof(layout)) != noErr)
2011-08-08 15:27:52 +00:00
goto error;
#endif
2011-08-08 15:27:52 +00:00
2014-09-09 20:24:29 +00:00
/* Set callbacks and finish up. */
cb.inputProc = audio_write_cb;
2011-12-24 12:46:12 +00:00
cb.inputProcRefCon = dev;
2011-08-08 15:27:52 +00:00
if (AudioUnitSetProperty(dev->dev, kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input, 0, &cb, sizeof(cb)) != noErr)
2011-08-08 15:27:52 +00:00
goto error;
if (AudioUnitInitialize(dev->dev) != noErr)
2011-08-08 15:27:52 +00:00
goto error;
2015-03-20 20:31:16 +00:00
fifo_size = (latency * settings->audio.out_rate) / 1000;
2011-08-08 15:45:49 +00:00
fifo_size *= 2 * sizeof(float);
2012-02-26 00:22:07 +00:00
dev->buffer_size = fifo_size;
2011-08-08 15:27:52 +00:00
dev->buffer = fifo_new(fifo_size);
if (!dev->buffer)
goto error;
2014-09-09 20:24:29 +00:00
RARCH_LOG("[CoreAudio]: Using buffer size of %u bytes: (latency = %u ms)\n",
(unsigned)fifo_size, latency);
2011-08-08 15:27:52 +00:00
if (AudioOutputUnitStart(dev->dev) != noErr)
2011-08-08 15:27:52 +00:00
goto error;
return dev;
error:
2012-04-21 21:25:32 +00:00
RARCH_ERR("[CoreAudio]: Failed to initialize driver ...\n");
2011-08-08 15:27:52 +00:00
coreaudio_free(dev);
return NULL;
}
2011-11-02 18:31:36 +00:00
static ssize_t coreaudio_write(void *data, const void *buf_, size_t size)
2011-08-08 15:27:52 +00:00
{
2011-12-24 12:46:12 +00:00
coreaudio_t *dev = (coreaudio_t*)data;
const uint8_t *buf = (const uint8_t*)buf_;
2011-08-08 15:27:52 +00:00
size_t written = 0;
while (!g_interrupted && size > 0)
2011-08-08 15:27:52 +00:00
{
2015-01-12 04:05:56 +00:00
size_t write_avail;
slock_lock(dev->lock);
2011-08-08 15:27:52 +00:00
2015-01-12 04:05:56 +00:00
write_avail = fifo_write_avail(dev->buffer);
2011-08-08 15:27:52 +00:00
if (write_avail > size)
write_avail = size;
fifo_write(dev->buffer, buf, write_avail);
2015-04-17 08:54:03 +00:00
buf += write_avail;
2011-08-08 15:27:52 +00:00
written += write_avail;
2015-04-17 08:54:03 +00:00
size -= write_avail;
2011-08-08 15:27:52 +00:00
if (dev->nonblock)
{
slock_unlock(dev->lock);
2011-08-08 15:27:52 +00:00
break;
}
2015-06-02 09:09:54 +00:00
#if TARGET_OS_IPHONE
if (write_avail == 0 && !scond_wait_timeout(
dev->cond, dev->lock, 3000000))
g_interrupted = true;
#else
2011-08-08 15:27:52 +00:00
if (write_avail == 0)
scond_wait(dev->cond, dev->lock);
#endif
slock_unlock(dev->lock);
2011-08-08 15:27:52 +00:00
}
return written;
}
static void coreaudio_set_nonblock_state(void *data, bool state)
2011-08-08 15:27:52 +00:00
{
2011-12-24 12:46:12 +00:00
coreaudio_t *dev = (coreaudio_t*)data;
if (dev)
dev->nonblock = state;
2011-08-08 15:27:52 +00:00
}
static bool coreaudio_alive(void *data)
2011-08-08 15:27:52 +00:00
{
2011-12-24 12:46:12 +00:00
coreaudio_t *dev = (coreaudio_t*)data;
2015-01-12 04:05:56 +00:00
if (!dev)
return false;
return !dev->is_paused;
}
static bool coreaudio_stop(void *data)
{
coreaudio_t *dev = (coreaudio_t*)data;
2015-01-12 04:05:56 +00:00
if (!dev)
return false;
dev->is_paused = (AudioOutputUnitStop(dev->dev) == noErr) ? true : false;
return dev->is_paused ? true : false;
2011-08-08 15:27:52 +00:00
}
static bool coreaudio_start(void *data)
{
2011-12-24 12:46:12 +00:00
coreaudio_t *dev = (coreaudio_t*)data;
2015-01-12 04:05:56 +00:00
if (!dev)
return false;
dev->is_paused = (AudioOutputUnitStart(dev->dev) == noErr) ? false : true;
return dev->is_paused ? false : true;
2011-08-08 15:27:52 +00:00
}
2011-08-08 15:45:49 +00:00
static bool coreaudio_use_float(void *data)
{
(void)data;
return true;
}
2012-02-26 00:22:07 +00:00
static size_t coreaudio_write_avail(void *data)
{
2015-01-12 04:05:56 +00:00
size_t avail;
2012-02-26 00:22:07 +00:00
coreaudio_t *dev = (coreaudio_t*)data;
2015-01-12 04:05:56 +00:00
slock_lock(dev->lock);
2015-01-12 04:05:56 +00:00
avail = fifo_write_avail(dev->buffer);
slock_unlock(dev->lock);
2015-01-12 04:05:56 +00:00
2012-02-26 00:22:07 +00:00
return avail;
}
static size_t coreaudio_buffer_size(void *data)
{
coreaudio_t *dev = (coreaudio_t*)data;
return dev->buffer_size;
}
static void *coreaudio_device_list_new(void *data)
{
/* TODO/FIXME */
return NULL;
}
static void coreaudio_device_list_free(void *data, void *array_list_data)
{
/* TODO/FIXME */
}
2014-09-11 05:06:20 +00:00
audio_driver_t audio_coreaudio = {
2011-12-24 12:46:12 +00:00
coreaudio_init,
coreaudio_write,
coreaudio_stop,
coreaudio_start,
coreaudio_alive,
2011-12-24 12:46:12 +00:00
coreaudio_set_nonblock_state,
coreaudio_free,
coreaudio_use_float,
2012-02-26 00:22:07 +00:00
"coreaudio",
coreaudio_device_list_new,
coreaudio_device_list_free,
2012-02-26 00:22:07 +00:00
coreaudio_write_avail,
coreaudio_buffer_size,
2011-08-08 15:27:52 +00:00
};