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>
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
3839
3940typedef 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+
5599static 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,
313372static 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
357409static void coreaudio_set_nonblock_state (void * data , bool state )
@@ -397,20 +449,14 @@ static bool coreaudio_use_float(void *data) { return true; }
397449
398450static 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
410456static 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