Skip to content

Commit 184fd68

Browse files
committed
coreaudio: minor performance improvements
this steals some of the improvements from the coreaudio3 driver to use atomics and dispatch_semaphore instead of fifo_queue.
1 parent 6943c93 commit 184fd68

1 file changed

Lines changed: 113 additions & 67 deletions

File tree

audio/drivers/coreaudio.c

Lines changed: 113 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
* If not, see <http://www.gnu.org/licenses/>.
1515
*/
1616
#include <stdlib.h>
17+
#include <stdatomic.h>
18+
19+
#include <dispatch/dispatch.h>
1720

1821
#if TARGET_OS_IPHONE
1922
#include <AudioToolbox/AudioToolbox.h>
@@ -26,8 +29,6 @@
2629
#include <AudioUnit/AUComponent.h>
2730

2831
#include <boolean.h>
29-
#include <queues/fifo_queue.h>
30-
#include <rthreads/rthreads.h>
3132
#include <retro_endianness.h>
3233
#include <string/stdstring.h>
3334

@@ -38,20 +39,63 @@
3839

3940
typedef struct coreaudio
4041
{
41-
slock_t *lock;
42-
scond_t *cond;
42+
dispatch_semaphore_t sema;
43+
44+
/* Lock-free ring buffer */
45+
float *buffer;
46+
size_t capacity; /* Power of 2 for fast masking */
47+
size_t write_ptr; /* Only touched by main thread */
48+
size_t read_ptr; /* Only touched by audio callback */
49+
atomic_size_t filled; /* Samples currently in buffer */
50+
4351
#if !HAS_MACOSX_10_12
4452
ComponentInstance dev;
4553
#else
4654
AudioComponentInstance dev;
4755
#endif
48-
fifo_buffer_t *buffer;
49-
size_t buffer_size;
5056
bool dev_alive;
5157
bool is_paused;
5258
bool nonblock;
5359
} coreaudio_t;
5460

61+
/* Lock-free ring buffer operations */
62+
63+
static inline size_t rb_write_avail(coreaudio_t *dev)
64+
{
65+
return dev->capacity - atomic_load_explicit(&dev->filled, memory_order_acquire);
66+
}
67+
68+
static inline size_t rb_read_avail(coreaudio_t *dev)
69+
{
70+
return atomic_load_explicit(&dev->filled, memory_order_acquire);
71+
}
72+
73+
static void rb_write(coreaudio_t *dev, const float *data, size_t count)
74+
{
75+
size_t first = dev->capacity - dev->write_ptr;
76+
if (first > count)
77+
first = count;
78+
79+
memcpy(dev->buffer + dev->write_ptr, data, first * sizeof(float));
80+
memcpy(dev->buffer, data + first, (count - first) * sizeof(float));
81+
82+
dev->write_ptr = (dev->write_ptr + count) & (dev->capacity - 1);
83+
atomic_fetch_add_explicit(&dev->filled, count, memory_order_release);
84+
}
85+
86+
static void rb_read(coreaudio_t *dev, float *data, size_t count)
87+
{
88+
size_t first = dev->capacity - dev->read_ptr;
89+
if (first > count)
90+
first = count;
91+
92+
memcpy(data, dev->buffer + dev->read_ptr, first * sizeof(float));
93+
memcpy(data + first, dev->buffer, (count - first) * sizeof(float));
94+
95+
dev->read_ptr = (dev->read_ptr + count) & (dev->capacity - 1);
96+
atomic_fetch_sub_explicit(&dev->filled, count, memory_order_release);
97+
}
98+
5599
static void coreaudio_free(void *data)
56100
{
57101
coreaudio_t *dev = (coreaudio_t*)data;
@@ -70,10 +114,10 @@ static void coreaudio_free(void *data)
70114
}
71115

72116
if (dev->buffer)
73-
fifo_free(dev->buffer);
117+
free(dev->buffer);
74118

75-
slock_free(dev->lock);
76-
scond_free(dev->cond);
119+
if (dev->sema)
120+
dispatch_release(dev->sema);
77121

78122
free(dev);
79123
}
@@ -83,9 +127,10 @@ static OSStatus coreaudio_audio_write_cb(void *userdata,
83127
const AudioTimeStamp *time_stamp, UInt32 bus_number,
84128
UInt32 number_frames, AudioBufferList *io_data)
85129
{
86-
unsigned write_avail;
87-
void *outbuf = NULL;
88130
coreaudio_t *dev = (coreaudio_t*)userdata;
131+
float *outbuf;
132+
size_t frames_needed;
133+
size_t avail;
89134

90135
(void)time_stamp;
91136
(void)bus_number;
@@ -94,21 +139,24 @@ static OSStatus coreaudio_audio_write_cb(void *userdata,
94139
if (!io_data || io_data->mNumberBuffers != 1)
95140
return noErr;
96141

97-
write_avail = io_data->mBuffers[0].mDataByteSize;
98-
outbuf = io_data->mBuffers[0].mData;
99-
100-
slock_lock(dev->lock);
142+
outbuf = (float *)io_data->mBuffers[0].mData;
143+
frames_needed = io_data->mBuffers[0].mDataByteSize / sizeof(float);
144+
avail = rb_read_avail(dev);
101145

102-
if (FIFO_READ_AVAIL(dev->buffer) < write_avail)
146+
if (avail < frames_needed)
103147
{
148+
/* Underrun: read what we have, fill rest with silence */
104149
*action_flags = kAudioUnitRenderAction_OutputIsSilence;
105-
/* Seems to be needed. */
106-
memset(outbuf, 0, write_avail);
150+
if (avail > 0)
151+
rb_read(dev, outbuf, avail);
152+
memset(outbuf + avail, 0, (frames_needed - avail) * sizeof(float));
107153
}
108154
else
109-
fifo_read(dev->buffer, outbuf, write_avail);
110-
slock_unlock(dev->lock);
111-
scond_signal(dev->cond);
155+
rb_read(dev, outbuf, frames_needed);
156+
157+
/* Wake writer if it might be waiting */
158+
dispatch_semaphore_signal(dev->sema);
159+
112160
return noErr;
113161
}
114162

@@ -171,7 +219,7 @@ static void *coreaudio_init(const char *device,
171219
unsigned block_frames,
172220
unsigned *new_rate)
173221
{
174-
size_t fifo_size;
222+
size_t buffer_samples;
175223
UInt32 i_size;
176224
AudioStreamBasicDescription real_desc;
177225
#if !HAS_MACOSX_10_12
@@ -194,8 +242,7 @@ static void *coreaudio_init(const char *device,
194242
if (!dev)
195243
return NULL;
196244

197-
dev->lock = slock_new();
198-
dev->cond = scond_new();
245+
dev->sema = dispatch_semaphore_create(0);
199246

200247
/* Create AudioComponent */
201248
desc.componentType = kAudioUnitType_Output;
@@ -287,17 +334,29 @@ static void *coreaudio_init(const char *device,
287334

288335
/* Enforce minimum latency to prevent buffer issues */
289336
if (latency < 8)
290-
latency = 8;
337+
latency = 8;
291338

292-
fifo_size = (latency * (*new_rate)) / 1000;
293-
fifo_size *= 2 * sizeof(float);
294-
dev->buffer_size = fifo_size;
339+
/* Calculate buffer size in samples (stereo) */
340+
buffer_samples = (latency * (*new_rate)) / 1000;
341+
buffer_samples *= 2; /* stereo */
295342

296-
if (!(dev->buffer = fifo_new(fifo_size)))
343+
/* Round up to next power of 2 for fast modulo via masking */
344+
dev->capacity = 1;
345+
while (dev->capacity < buffer_samples)
346+
dev->capacity <<= 1;
347+
348+
dev->buffer = (float *)calloc(dev->capacity, sizeof(float));
349+
if (!dev->buffer)
297350
goto error;
298351

299-
RARCH_LOG("[CoreAudio] Using buffer size of %u bytes: (latency = %u ms).\n",
300-
(unsigned)fifo_size, latency);
352+
atomic_init(&dev->filled, 0);
353+
dev->write_ptr = 0;
354+
dev->read_ptr = 0;
355+
356+
RARCH_LOG("[CoreAudio] Buffer: %u samples (%u bytes, %.1f ms).\n",
357+
(unsigned)dev->capacity,
358+
(unsigned)(dev->capacity * sizeof(float)),
359+
(float)dev->capacity * 1000.0f / (*new_rate) / 2.0f);
301360

302361
if (AudioOutputUnitStart(dev->dev) != noErr)
303362
goto error;
@@ -313,45 +372,38 @@ static void *coreaudio_init(const char *device,
313372
static ssize_t coreaudio_write(void *data, const void *buf_, size_t len)
314373
{
315374
coreaudio_t *dev = (coreaudio_t*)data;
316-
const uint8_t *buf = (const uint8_t*)buf_;
317-
size_t _len = 0;
375+
const float *buf = (const float *)buf_;
376+
size_t samples = len / sizeof(float);
377+
size_t written = 0;
318378

319-
while (!dev->is_paused && len > 0)
379+
while (!dev->is_paused && samples > 0)
320380
{
321-
size_t write_avail;
322-
323-
slock_lock(dev->lock);
381+
size_t avail = rb_write_avail(dev);
382+
size_t to_write = (avail < samples) ? avail : samples;
324383

325-
write_avail = FIFO_WRITE_AVAIL(dev->buffer);
326-
if (write_avail > len)
327-
write_avail = len;
328-
329-
fifo_write(dev->buffer, buf, write_avail);
330-
buf += write_avail;
331-
_len += write_avail;
332-
len -= write_avail;
384+
if (to_write > 0)
385+
{
386+
rb_write(dev, buf, to_write);
387+
buf += to_write;
388+
written += to_write;
389+
samples -= to_write;
390+
}
333391

334392
if (dev->nonblock)
335-
{
336-
slock_unlock(dev->lock);
337393
break;
338-
}
339394

340-
#if TARGET_OS_IOS
341-
if (write_avail == 0 && !scond_wait_timeout(
342-
dev->cond, dev->lock, 300000))
395+
if (samples > 0)
343396
{
344-
slock_unlock(dev->lock);
345-
break;
397+
/* Buffer full, wait for audio callback to drain some.
398+
* Use a timeout as a safety net in case audio stalls. */
399+
dispatch_time_t timeout = dispatch_time(
400+
DISPATCH_TIME_NOW, 500 * NSEC_PER_MSEC);
401+
if (dispatch_semaphore_wait(dev->sema, timeout) != 0)
402+
break; /* Timeout - audio might be stalled */
346403
}
347-
#else
348-
if (write_avail == 0)
349-
scond_wait(dev->cond, dev->lock);
350-
#endif
351-
slock_unlock(dev->lock);
352404
}
353405

354-
return _len;
406+
return written * sizeof(float);
355407
}
356408

357409
static void coreaudio_set_nonblock_state(void *data, bool state)
@@ -397,20 +449,14 @@ static bool coreaudio_use_float(void *data) { return true; }
397449

398450
static size_t coreaudio_write_avail(void *data)
399451
{
400-
size_t avail;
401452
coreaudio_t *dev = (coreaudio_t*)data;
402-
403-
slock_lock(dev->lock);
404-
avail = FIFO_WRITE_AVAIL(dev->buffer);
405-
slock_unlock(dev->lock);
406-
407-
return avail;
453+
return rb_write_avail(dev) * sizeof(float);
408454
}
409455

410456
static size_t coreaudio_buffer_size(void *data)
411457
{
412458
coreaudio_t *dev = (coreaudio_t*)data;
413-
return dev->buffer_size;
459+
return dev->capacity * sizeof(float);
414460
}
415461

416462
/* TODO/FIXME - implement */

0 commit comments

Comments
 (0)