1
0
mirror of https://github.com/libretro/RetroArch synced 2024-07-03 00:38:44 +00:00

(OSX) Add CoreAudio V3 driver from stuartcarnie

This commit is contained in:
twinaphex 2019-01-31 23:14:20 +01:00
parent c62a7e4b5e
commit 6aca340927
28 changed files with 493 additions and 1 deletions

View File

@ -510,9 +510,19 @@ endif
# Audio
ifeq ($(HAVE_COREAUDIO), 1)
OBJ += audio/drivers/coreaudio.o
LIBS += -framework CoreServices -framework CoreAudio -framework AudioUnit
HAVE_COREAUDIO_LIBS = 1
endif
ifeq ($(HAVE_COREAUDIO3), 1)
OBJ += audio/drivers/coreaudio3.o
HAVE_COREAUDIO_LIBS = 1
endif
ifeq ($(HAVE_COREAUDIO_LIBS), 1)
LIBS += -framework CoreServices -framework CoreAudio -framework AudioUnit
endif
ifeq ($(HAVE_CORETEXT), 1)

View File

@ -89,6 +89,9 @@ static const audio_driver_t *audio_drivers[] = {
#ifdef HAVE_COREAUDIO
&audio_coreaudio,
#endif
#ifdef HAVE_COREAUDIO3
&audio_coreaudio3,
#endif
#ifdef HAVE_AL
&audio_openal,
#endif

View File

@ -304,6 +304,7 @@ extern audio_driver_t audio_pulse;
extern audio_driver_t audio_dsound;
extern audio_driver_t audio_wasapi;
extern audio_driver_t audio_coreaudio;
extern audio_driver_t audio_coreaudio3;
extern audio_driver_t audio_xenon360;
extern audio_driver_t audio_ps3;
extern audio_driver_t audio_gx;

382
audio/drivers/coreaudio3.m Normal file
View File

@ -0,0 +1,382 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2019 - Stuart Carnie
*
* RetroArch 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.
*
* RetroArch 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 RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
#import <AVFoundation/AVFoundation.h>
#include <stdio.h>
#include <stdatomic.h>
#include <stdlib.h>
#include <memory.h>
#include "../audio_driver.h"
#pragma mark - ringbuffer
typedef struct ringbuffer
{
float *buffer;
size_t cap;
atomic_int len;
size_t writePtr;
size_t readPtr;
} ringbuffer_t;
typedef ringbuffer_t * ringbuffer_h;
static inline size_t rb_len(ringbuffer_h r)
{
return atomic_load_explicit(&r->len, memory_order_relaxed);
}
static inline size_t rb_cap(ringbuffer_h r)
{
return (r->readPtr + r->cap - r->writePtr) % r->cap;
}
static inline size_t rb_avail(ringbuffer_h r)
{
return r->cap - rb_len(r);
}
static inline void rb_advance_write(ringbuffer_h r)
{
r->writePtr = (r->writePtr + 1) % r->cap;
}
static inline void rb_advance_write_n(ringbuffer_h r, size_t n)
{
r->writePtr = (r->writePtr + n) % r->cap;
}
static inline void rb_advance_read(ringbuffer_h r)
{
r->readPtr = (r->readPtr + 1) % r->cap;
}
static inline void rb_len_add(ringbuffer_h r, int n)
{
atomic_fetch_add(&r->len, n);
}
static inline void rb_len_sub(ringbuffer_h r, int n)
{
atomic_fetch_sub(&r->len, n);
}
static void rb_init(ringbuffer_h r, size_t cap)
{
r->buffer = malloc(cap * sizeof(float));
r->cap = cap;
atomic_init(&r->len, 0);
r->writePtr = 0;
r->readPtr = 0;
}
static void rb_free(ringbuffer_h r)
{
free(r->buffer);
bzero(r, sizeof(*r));
}
#define UNLIKELY(x) __builtin_expect((x), 0)
#define LIKELY(x) __builtin_expect((x), 1)
static void rb_write_data(ringbuffer_h r, const float *data, size_t len)
{
size_t avail = rb_avail(r);
size_t n = MIN(len, avail);
size_t first_write = n;
size_t rest_write = 0;
if (r->writePtr + n > r->cap)
{
first_write = r->cap - r->writePtr;
rest_write = n - first_write;
}
memcpy(r->buffer + r->writePtr, data, first_write*sizeof(float));
memcpy(r->buffer, data + first_write, rest_write*sizeof(float));
rb_advance_write_n(r, n);
rb_len_add(r, (int)n);
}
static void rb_read_data(ringbuffer_h r, float *d0, float *d1, size_t len)
{
size_t need = len*2;
do {
size_t have = rb_len(r);
size_t n = MIN(have, need);
int i = 0;
for (; i < n/2; i++)
{
d0[i] = r->buffer[r->readPtr];
rb_advance_read(r);
d1[i] = r->buffer[r->readPtr];
rb_advance_read(r);
}
need -= n;
rb_len_sub(r, (int)n);
if (UNLIKELY(need > 0))
{
/* we got more data */
if (rb_len(r) > 0)
continue;
// underflow
const float quiet = 0.0f;
size_t fill = (need/2)*sizeof(float);
memset_pattern4(&d0[i], &quiet, fill);
memset_pattern4(&d1[i], &quiet, fill);
}
} while (0);
}
#pragma mark - CoreAudio3
static bool g_interrupted;
@interface CoreAudio3 : NSObject {
ringbuffer_t _rb;
dispatch_semaphore_t _sema;
AUAudioUnit *_au;
size_t _bufferSize;
BOOL _nonBlock;
}
@property (nonatomic, readwrite) BOOL nonBlock;
@property (nonatomic, readonly) BOOL paused;
@property (nonatomic, readonly) size_t writeAvailableInBytes;
@property (nonatomic, readonly) size_t bufferSizeInBytes;
- (instancetype)initWithRate:(NSUInteger)rate
latency:(NSUInteger)latency;
- (ssize_t)writeFloat:(const float *)data samples:(size_t)samples;
- (void)start;
- (void)stop;
@end
@implementation CoreAudio3
- (instancetype)initWithRate:(NSUInteger)rate
latency:(NSUInteger)latency {
if (self = [super init])
{
_sema = dispatch_semaphore_create(0);
_bufferSize = (latency * rate) / 1000;
_bufferSize *= 2; // stereo
rb_init(&_rb, _bufferSize);
AudioComponentDescription desc = {
.componentType = kAudioUnitType_Output,
.componentSubType = kAudioUnitSubType_DefaultOutput,
.componentManufacturer = kAudioUnitManufacturer_Apple,
};
NSError *err;
AUAudioUnit *au = [[AUAudioUnit alloc] initWithComponentDescription:desc error:&err];
if (err != nil)
return nil;
AVAudioFormat *format = au.outputBusses[0].format;
if (format.channelCount != 2)
return nil;
AVAudioFormat *renderFormat = [[AVAudioFormat alloc] initStandardFormatWithSampleRate:rate channels:2];
[au.inputBusses[0] setFormat:renderFormat error:&err];
if (err != nil)
return nil;
ringbuffer_h rb = &_rb;
__block dispatch_semaphore_t sema = _sema;
au.outputProvider = ^AUAudioUnitStatus(AudioUnitRenderActionFlags * actionFlags, const AudioTimeStamp * timestamp, AUAudioFrameCount frameCount, NSInteger inputBusNumber, AudioBufferList * inputData) {
rb_read_data(rb, inputData->mBuffers[0].mData, inputData->mBuffers[1].mData, frameCount);
dispatch_semaphore_signal(sema);
return 0;
};
[au allocateRenderResourcesAndReturnError:&err];
if (err != nil)
return nil;
_au = au;
RARCH_LOG("[CoreAudio3]: Using buffer size of %u bytes: (latency = %u ms)\n", (unsigned)self.bufferSizeInBytes, latency);
[self start];
}
return self;
}
- (void)dealloc {
rb_free(&_rb);
}
- (BOOL)paused {
return !_au.running;
}
- (size_t)bufferSizeInBytes {
return _bufferSize * sizeof(float);
}
- (size_t)writeAvailableInBytes {
return rb_avail(&_rb) * sizeof(float);
}
- (void)start {
NSError *err;
[_au startHardwareAndReturnError:&err];
}
- (void)stop {
[_au stopHardware];
}
- (ssize_t)writeFloat:(const float *)data samples:(size_t)samples {
size_t written = 0;
while (!g_interrupted && samples > 0)
{
size_t write_avail = rb_avail(&_rb);
if (write_avail > samples)
write_avail = samples;
rb_write_data(&_rb, data, write_avail);
data += write_avail;
written += write_avail;
samples -= write_avail;
if (_nonBlock)
break;
if (write_avail == 0)
dispatch_semaphore_wait(_sema, DISPATCH_TIME_FOREVER);
}
return written;
}
@end
static void coreaudio3_free(void *data)
{
CoreAudio3 *dev = (__bridge_transfer CoreAudio3 *)data;
if (dev == nil)
return;
[dev stop];
dev = nil;
}
static void *coreaudio3_init(const char *device,
unsigned rate, unsigned latency,
unsigned block_frames,
unsigned *new_rate)
{
CoreAudio3 *dev = [[CoreAudio3 alloc] initWithRate:rate
latency:latency];
*new_rate = rate;
return (__bridge_retained void *)dev;
}
static ssize_t coreaudio3_write(void *data, const void *buf_, size_t size)
{
CoreAudio3 *dev = (__bridge CoreAudio3 *)data;
return [dev writeFloat:(const float *)buf_ samples:size/sizeof(float)] * sizeof(float);
}
static void coreaudio3_set_nonblock_state(void *data, bool state)
{
CoreAudio3 *dev = (__bridge CoreAudio3 *)data;
if (dev == nil)
return;
dev.nonBlock = state;
}
static bool coreaudio3_alive(void *data)
{
CoreAudio3 *dev = (__bridge CoreAudio3 *)data;
if (dev == nil)
return NO;
return !dev.paused;
}
static bool coreaudio3_stop(void *data)
{
CoreAudio3 *dev = (__bridge CoreAudio3 *)data;
if (dev == nil)
return NO;
[dev stop];
return dev.paused;
}
static bool coreaudio3_start(void *data, bool is_shutdown)
{
CoreAudio3 *dev = (__bridge CoreAudio3 *)data;
if (dev == nil)
return NO;
[dev start];
return !dev.paused;
}
static bool coreaudio3_use_float(void *data)
{
return YES;
}
static size_t coreaudio3_write_avail(void *data)
{
CoreAudio3 *dev = (__bridge CoreAudio3 *)data;
if (dev == nil)
return 0;
return dev.writeAvailableInBytes;
}
static size_t coreaudio3_buffer_size(void *data)
{
CoreAudio3 *dev = (__bridge CoreAudio3 *)data;
if (dev == nil)
return 0;
return dev.bufferSizeInBytes;
}
audio_driver_t audio_coreaudio3 = {
coreaudio3_init,
coreaudio3_write,
coreaudio3_stop,
coreaudio3_start,
coreaudio3_alive,
coreaudio3_set_nonblock_state,
coreaudio3_free,
coreaudio3_use_float,
"coreaudio3",
coreaudio3_write_avail,
coreaudio3_buffer_size,
};

View File

@ -152,6 +152,12 @@ static const bool _coreaudio_supp = true;
static const bool _coreaudio_supp = false;
#endif
#ifdef HAVE_COREAUDIO3
static const bool _coreaudio3_supp = true;
#else
static const bool _coreaudio3_supp = false;
#endif
#if defined(HAVE_OSS) || defined(HAVE_OSS_BSD)
static const bool _oss_supp = true;
#else

View File

@ -191,6 +191,7 @@ enum audio_driver_enum
AUDIO_DSOUND,
AUDIO_WASAPI,
AUDIO_COREAUDIO,
AUDIO_COREAUDIO3,
AUDIO_PS3,
AUDIO_XENON360,
AUDIO_WII,
@ -394,6 +395,8 @@ static enum audio_driver_enum AUDIO_DEFAULT_DRIVER = AUDIO_TINYALSA;
static enum audio_driver_enum AUDIO_DEFAULT_DRIVER = AUDIO_OSS;
#elif defined(HAVE_JACK)
static enum audio_driver_enum AUDIO_DEFAULT_DRIVER = AUDIO_JACK;
#elif defined(HAVE_COREAUDIO3)
static enum audio_driver_enum AUDIO_DEFAULT_DRIVER = AUDIO_COREAUDIO3;
#elif defined(HAVE_COREAUDIO)
static enum audio_driver_enum AUDIO_DEFAULT_DRIVER = AUDIO_COREAUDIO;
#elif defined(HAVE_XAUDIO)

View File

@ -64,6 +64,10 @@
#include "../input/drivers_joypad/mfi_joypad.m"
#endif
#ifdef HAVE_COREAUDIO3
#include "../audio/drivers/coreaudio3.m"
#endif
#if defined(HAVE_DISCORD)
#include "../deps/discord-rpc/src/discord_register_osx.m"
#endif

View File

@ -3756,3 +3756,7 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO_SUPPORT,
"CoreAudio support"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO3_SUPPORT,
"CoreAudio V3 support"
)

View File

@ -4768,3 +4768,7 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO_SUPPORT,
"CoreAudio support"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO3_SUPPORT,
"CoreAudio V3 support"
)

View File

@ -3526,3 +3526,7 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO_SUPPORT,
"CoreAudio support"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO3_SUPPORT,
"CoreAudio V3 support"
)

View File

@ -3655,3 +3655,7 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO_SUPPORT,
"CoreAudio support"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO3_SUPPORT,
"CoreAudio V3 support"
)

View File

@ -7752,3 +7752,7 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO_SUPPORT,
"CoreAudio support"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO3_SUPPORT,
"CoreAudio V3 support"
)

View File

@ -3414,3 +3414,7 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO_SUPPORT,
"CoreAudio support"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO3_SUPPORT,
"CoreAudio V3 support"
)

View File

@ -7888,3 +7888,7 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO_SUPPORT,
"CoreAudio support"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO3_SUPPORT,
"CoreAudio V3 support"
)

View File

@ -3570,3 +3570,7 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO_SUPPORT,
"CoreAudio support"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO3_SUPPORT,
"CoreAudio V3 support"
)

View File

@ -3611,3 +3611,7 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO_SUPPORT,
"CoreAudio support"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO3_SUPPORT,
"CoreAudio V3 support"
)

View File

@ -4060,3 +4060,7 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO_SUPPORT,
"CoreAudio support"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO3_SUPPORT,
"CoreAudio V3 support"
)

View File

@ -3521,3 +3521,7 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO_SUPPORT,
"CoreAudio support"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO3_SUPPORT,
"CoreAudio V3 support"
)

View File

@ -3400,3 +3400,7 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO_SUPPORT,
"CoreAudio ondersteuning"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO3_SUPPORT,
"CoreAudio V3 support"
)

View File

@ -3814,3 +3814,7 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO_SUPPORT,
"CoreAudio support"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO3_SUPPORT,
"CoreAudio V3 support"
)

View File

@ -8040,3 +8040,7 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO_SUPPORT,
"CoreAudio support"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO3_SUPPORT,
"CoreAudio V3 support"
)

View File

@ -3480,3 +3480,7 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO_SUPPORT,
"CoreAudio support"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO3_SUPPORT,
"CoreAudio V3 support"
)

View File

@ -3683,3 +3683,7 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO_SUPPORT,
"CoreAudio support"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO3_SUPPORT,
"CoreAudio V3 support"
)

View File

@ -8120,3 +8120,7 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO_SUPPORT,
"CoreAudio support"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO3_SUPPORT,
"CoreAudio V3 support"
)

View File

@ -3570,3 +3570,7 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO_SUPPORT,
"CoreAudio support"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO3_SUPPORT,
"CoreAudio V3 support"
)

View File

@ -1098,6 +1098,15 @@ static int menu_displaylist_parse_system_info(menu_displaylist_info_t *info)
menu_entries_append_enum(info->list, feat_str, "",
MENU_ENUM_LABEL_SYSTEM_INFO_ENTRY, MENU_SETTINGS_CORE_INFO_NONE, 0, 0);
snprintf(feat_str, sizeof(feat_str),
"%s: %s",
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO3_SUPPORT),
_coreaudio3_supp ?
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_YES) :
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO));
menu_entries_append_enum(info->list, feat_str, "",
MENU_ENUM_LABEL_SYSTEM_INFO_ENTRY, MENU_SETTINGS_CORE_INFO_NONE, 0, 0);
snprintf(feat_str, sizeof(feat_str),
"%s: %s",
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DSOUND_SUPPORT),

View File

@ -1836,6 +1836,7 @@ enum msg_hash_enums
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_OSS_SUPPORT,
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_OPENAL_SUPPORT,
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO_SUPPORT,
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_COREAUDIO3_SUPPORT,
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_OPENSL_SUPPORT,
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_RSOUND_SUPPORT,
MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_ROARAUDIO_SUPPORT,

View File

@ -390,6 +390,7 @@ static void retroarch_print_features(void)
_PSUPP(vg, "OpenVG", "Video context driver");
_PSUPP(coreaudio, "CoreAudio", "Audio driver");
_PSUPP(coreaudio3, "CoreAudioV3", "Audio driver");
_PSUPP(alsa, "ALSA", "Audio driver");
_PSUPP(oss, "OSS", "Audio driver");
_PSUPP(jack, "Jack", "Audio driver");