1 /*
2  * soundcoreaudio.c - Implementation of the CoreAudio sound device.
3  *
4  * Written by
5  *  Michael Klein <michael.klein@puffin.lb.shuttle.de>
6  *  Christian Vogelgsang <C.Vogelgsang@web.de> (Ported to Intel Mac)
7  *
8  * This file is part of VICE, the Versatile Commodore Emulator.
9  * See README for copyright notice.
10  *
11  *  This program is free software; you can redistribute it and/or modify
12  *  it under the terms of the GNU General Public License as published by
13  *  the Free Software Foundation; either version 2 of the License, or
14  *  (at your option) any later version.
15  *
16  *  This program is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *  GNU General Public License for more details.
20  *
21  *  You should have received a copy of the GNU General Public License
22  *  along with this program; if not, write to the Free Software
23  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
24  *  02111-1307  USA.
25  *
26  */
27 
28 /*
29  * Notation Hints:
30  *
31  * Mac OS X Audio:
32  *  1 Packet = 1 Frame
33  *  1 Frame  = 1 or 2 Samples (SWORD)  1:mono SID, 2:stereo SID
34  *  1 Slice  = n Frames
35  *
36  * VICE Audio:
37  *  1 Fragment = n Frames     (n=fragsize)
38  *  Soundbuffer = m Fragments (m=fragnum)
39  *
40  * VICE Fragment = CoreAudio Slice
41  */
42 
43 #include "vice.h"
44 
45 #ifdef USE_COREAUDIO
46 
47 #include <AudioToolbox/AudioToolbox.h>
48 #include <CoreAudio/CoreAudio.h>
49 #include <stdatomic.h>
50 #include <libkern/OSAtomic.h>
51 
52 #include "lib.h"
53 #include "log.h"
54 #include "sound.h"
55 #include "tick.h"
56 #include "vsyncapi.h"
57 
58 /* Requested audio device name */
59 CFStringRef requested_device_name_ref = NULL;
60 char *requested_device_name = NULL;
61 
62 /* resolved device id */
63 static AudioDeviceID device = kAudioDeviceUnknown;
64 
65 
66 /* the cyclic buffer containing m fragments */
67 static volatile int16_t *ringbuffer;
68 
69 /* the buffer used to pass non cyclic data to the driver  */
70 static volatile int16_t *copybuffer;
71 static volatile int copybuffer_size_bytes;
72 
73 /* current read position: no. of fragment in soundbuffer */
74 static volatile int read_position_2;
75 
76 /* the next position to write: no. of fragment in soundbuffer */
77 static volatile int write_position_2;
78 
79 /* current number of fragments in buffer */
80 static volatile int fragments_in_queue_2;
81 
82 /* samples left in current fragment */
83 static volatile int frames_left_in_fragment_2;
84 
85 
86 
87 /* frames in fragment  */
88 static unsigned int frames_in_fragment;
89 
90 /* Size of fragment (bytes).  */
91 static unsigned int bytes_in_fragment;
92 
93 /* Size of fragment (SWORDs) */
94 static unsigned int swords_in_fragment;
95 
96 /* total number of fragments */
97 static unsigned int fragment_count;
98 
99 /* number of interleaved channels (mono SID=1, stereo SID=2) */
100 static int in_channels;
101 
102 /* bytes per input frame */
103 static unsigned int in_frame_byte_size;
104 
105 /* How many times the audio code didn't get enough audio */
106 static unsigned long underflow_count;
107 
108 #if 0
109 static void coreaudio_buffer_stats(bool force)
110 {
111     static unsigned long last_log_time_sec;
112 
113     unsigned long this_log_time_sec;
114     unsigned long now;
115 
116     now = tick_now();
117     this_log_time_sec = now / (tick_per_second());
118 
119     if (!force && this_log_time_sec == last_log_time_sec) {
120         return;
121     }
122 
123     last_log_time_sec = this_log_time_sec;
124 
125     log_message(LOG_DEFAULT, "%d of %d (underflows: %lu)", fragments_in_queue_2, fragment_count, underflow_count);
126 }
127 #endif
128 
129 /* ----- Audio Converter ------------------------------------------------ */
130 
131 static AudioConverterRef converter = 0;
132 
converter_input(AudioConverterRef inAudioConverter,UInt32 * ioNumberDataPackets,AudioBufferList * ioData,AudioStreamPacketDescription ** outDataPacketDescription,void * inUserData)133 static OSStatus converter_input(AudioConverterRef inAudioConverter,
134                                 UInt32 * ioNumberDataPackets,
135                                 AudioBufferList * ioData,
136                                 AudioStreamPacketDescription** outDataPacketDescription,
137                                 void * inUserData)
138 {
139     UInt32 needed_frames = *ioNumberDataPackets;
140     int needed_bytes = needed_frames * in_frame_byte_size;
141     int this_read_position  = read_position_2;
142     int consumed_fragments = 0;
143 
144     int16_t *source;
145     int16_t *dest;
146 
147     /* ensure our copy buffer is large enough */
148     if (needed_bytes > copybuffer_size_bytes) {
149         lib_free((void *)copybuffer);
150         copybuffer_size_bytes = needed_bytes;
151         copybuffer = lib_malloc(copybuffer_size_bytes);
152         log_message(LOG_DEFAULT, "Copybuffer increase to %d bytes", copybuffer_size_bytes);
153     }
154 
155     /* prepare return buffer */
156     ioData->mBuffers[0].mNumberChannels = in_channels;
157     ioData->mBuffers[0].mData = (void *)copybuffer;
158     ioData->mBuffers[0].mDataByteSize = needed_bytes;
159     ioData->mNumberBuffers = 1;
160 
161     dest = (int16_t *)copybuffer;
162 
163     while (fragments_in_queue_2 && needed_frames) {
164         /*
165          * Assemble a single block of samples for the converter
166          */
167 
168         /* calc position in ring buffer */
169         int sample_offset_in_fragment = frames_in_fragment - frames_left_in_fragment_2;
170         source = (int16_t *)ringbuffer + (swords_in_fragment * this_read_position) + (sample_offset_in_fragment * in_channels);
171 
172         if (needed_frames < frames_left_in_fragment_2) {
173             /* The current fragement has more than enough, so it will be read from again next time. */
174             memcpy(dest, source, needed_frames * in_frame_byte_size);
175 
176             dest += needed_frames * in_channels;
177             frames_left_in_fragment_2 -= needed_frames;
178 
179             needed_frames = 0;
180         } else {
181             /* We'll need all the audio in the current frament. */
182             memcpy(dest, source, frames_left_in_fragment_2 * in_frame_byte_size);
183 
184             dest += frames_left_in_fragment_2 * in_channels;
185             needed_frames -= frames_left_in_fragment_2;
186 
187             this_read_position = (this_read_position + 1) % fragment_count;
188 
189             consumed_fragments++;
190             frames_left_in_fragment_2 = frames_in_fragment;
191         }
192     }
193 
194     if (needed_frames) {
195         /* Underflow */
196         underflow_count++;
197         *ioNumberDataPackets = 0;
198 
199         //memset(dest, 0, needed_frames * in_frame_byte_size);
200     } else {
201         /*
202         * There was enough data, apply the read.
203         */
204 
205         while(consumed_fragments--) {
206             OSAtomicDecrement32(&fragments_in_queue_2);
207             read_position_2 = this_read_position;
208         }
209     }
210 
211     return kAudioHardwareNoError;
212 }
213 
converter_open(AudioStreamBasicDescription * in,AudioStreamBasicDescription * out)214 static int converter_open(AudioStreamBasicDescription *in,
215                           AudioStreamBasicDescription *out)
216 {
217     OSStatus err;
218 
219     /* need to to sample rate conversion? */
220     if (out->mSampleRate != in->mSampleRate) {
221         log_message(LOG_DEFAULT, "sound (coreaudio_init): sampling rate conversion %dHz->%dHz",
222                     (int)in->mSampleRate, (int)out->mSampleRate);
223     }
224 
225     /* create a new audio converter */
226     err = AudioConverterNew(in, out, &converter);
227     if (err != noErr) {
228         log_error(LOG_DEFAULT,
229                   "sound (coreaudio_init): could not create AudioConverter: err=%d", (int)err);
230         return -1;
231     }
232 
233     /* duplicate mono stream to all output channels */
234     if (in->mChannelsPerFrame == 1 && out->mChannelsPerFrame > 1) {
235         Boolean writable;
236         UInt32 size;
237         err = AudioConverterGetPropertyInfo(converter, kAudioConverterChannelMap, &size, &writable);
238         if (err == noErr && writable) {
239             SInt32 * channel_map = lib_malloc(size);
240             if (channel_map) {
241                 memset(channel_map, 0, size);
242                 AudioConverterSetProperty(converter, kAudioConverterChannelMap, size, channel_map);
243                 lib_free(channel_map);
244             }
245         }
246     }
247 
248     return 0;
249 }
250 
converter_close(void)251 static void converter_close(void)
252 {
253     if (converter) {
254         AudioConverterDispose(converter);
255         converter = NULL;
256     }
257 }
258 
string_buf_size_for_utf8_char_length(CFIndex utf8Chars)259 static CFIndex string_buf_size_for_utf8_char_length(CFIndex utf8Chars)
260 {
261     return utf8Chars * 4 + 1;
262 }
263 
determine_output_device_id()264 static int determine_output_device_id()
265 {
266     OSStatus err;
267     UInt32 size;
268     AudioDeviceID default_device;
269     AudioObjectPropertyAddress property_address = {
270         0,
271         kAudioObjectPropertyScopeGlobal,
272         kAudioObjectPropertyElementMaster
273     };
274     bool requested_device_found = false;
275 
276     /* get default audio device id */
277     property_address.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
278     size = sizeof(default_device);
279     err = AudioObjectGetPropertyData(kAudioObjectSystemObject,
280                                      &property_address,
281                                      0,
282                                      NULL,
283                                      &size,
284                                      &default_device);
285     if (err != kAudioHardwareNoError) {
286         log_error(LOG_DEFAULT, "sound (coreaudio_init): Failed to get default output device");
287         return -1;
288     }
289 
290     /* use the default audio device unless overridden */
291     device = default_device;
292 
293     if (requested_device_name) {
294         log_message(LOG_DEFAULT, "sound (coreaudio_init): Searching for audio output device: %s", requested_device_name);
295     }
296 
297     /* list audio devices */
298     property_address.mSelector = kAudioHardwarePropertyDevices;
299     size = 0;
300     err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &property_address, 0, NULL, &size);
301     if (err != kAudioHardwareNoError) {
302         log_error(LOG_DEFAULT, "sound (coreaudio_init): AudioObjectGetPropertyDataSize (kAudioHardwarePropertyDevices) failed: %d", (int)err);
303         return -1;
304     }
305 
306     UInt32 device_count = size / sizeof(AudioDeviceID);
307 
308     AudioDeviceID *audio_devices = (AudioDeviceID *)(lib_calloc(device_count, sizeof(AudioDeviceID)));
309     if (audio_devices == NULL) {
310         log_error(LOG_DEFAULT, "sound (coreaudio_init): Unable to allocate memory");
311         return -1;
312     }
313 
314     err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property_address, 0, NULL, &size, audio_devices);
315     if (err != kAudioHardwareNoError) {
316         log_error(LOG_DEFAULT, "sound (coreaudio_init): AudioObjectGetPropertyData (kAudioHardwarePropertyDevices) failed: %d", (int)err);
317         lib_free(audio_devices);
318         audio_devices = NULL;
319         return -1;
320     }
321 
322     CFStringRef device_name_ref = NULL;
323     char * device_name = NULL;
324     CFIndex buffer_size = 0;
325 
326     /* search list of output devices for matching name */
327     for(UInt32 i = 0; i < device_count; ++i) {
328         property_address.mSelector = kAudioDevicePropertyStreams;
329         property_address.mScope = kAudioDevicePropertyScopeOutput;
330 
331         size = 0;
332         err = AudioObjectGetPropertyDataSize(audio_devices[i], &property_address, 0, NULL, &size);
333         if (err != kAudioHardwareNoError) {
334             log_error(LOG_DEFAULT, "sound (coreaudio_init): AudioObjectGetPropertyDataSize (kAudioDevicePropertyStreamConfiguration) failed: %d", (int)err);
335             lib_free(audio_devices);
336             audio_devices = NULL;
337             return -1;
338         }
339 
340         bool outputs_found = size > 0;
341 
342         /* get device name */
343         size = sizeof(device_name_ref);
344         property_address.mSelector = kAudioDevicePropertyDeviceNameCFString;
345         err = AudioObjectGetPropertyData(audio_devices[i], &property_address, 0, NULL, &size, &device_name_ref);
346         if (err != kAudioHardwareNoError) {
347             log_error(LOG_DEFAULT, "sound (coreaudio_init): AudioObjectGetPropertyData (kAudioDevicePropertyDeviceNameCFString) failed: %d", (int)err);
348             lib_free(audio_devices);
349             audio_devices = NULL;
350             return -1;
351         }
352 
353         buffer_size = string_buf_size_for_utf8_char_length(CFStringGetLength(device_name_ref));
354         device_name = lib_calloc(1, buffer_size);
355 
356         if(!CFStringGetCString(device_name_ref, device_name, buffer_size, kCFStringEncodingUTF8)) {
357             strcpy(device_name, "");
358         }
359 
360         if (!outputs_found) {
361             log_message(LOG_DEFAULT, "sound (coreaudio_init): Found audio device with no outputs: %s", device_name);
362             lib_free(device_name);
363             continue;
364         }
365 
366         if (audio_devices[i] == default_device) {
367             log_message(LOG_DEFAULT, "sound (coreaudio_init): Found output audio device: %s (Default)", device_name);
368         } else {
369             log_message(LOG_DEFAULT, "sound (coreaudio_init): Found output audio device: %s", device_name);
370         }
371 
372         if (requested_device_name == NULL) {
373             lib_free(device_name);
374             continue;
375         }
376 
377         if (kCFCompareEqualTo == CFStringCompare(requested_device_name_ref, device_name_ref, 0)) {
378             /* matches the requested audio device */
379             device = audio_devices[i];
380             requested_device_found = true;
381         }
382 
383         lib_free(device_name);
384     }
385 
386     lib_free(audio_devices);
387     audio_devices = NULL;
388 
389     /* get final device name */
390     device_name_ref = NULL;
391     size = sizeof(device_name_ref);
392     property_address.mSelector = kAudioDevicePropertyDeviceNameCFString;
393     err = AudioObjectGetPropertyData(device, &property_address, 0, NULL, &size, &device_name_ref);
394     if (err != kAudioHardwareNoError) {
395         log_error(LOG_DEFAULT, "sound (coreaudio_init): AudioObjectGetPropertyData (kAudioDevicePropertyDeviceNameCFString) failed: %d", (int)err);
396         return -1;
397     }
398 
399     buffer_size = string_buf_size_for_utf8_char_length(CFStringGetLength(device_name_ref));
400     device_name = lib_calloc(1, buffer_size);
401 
402     if(!CFStringGetCString(device_name_ref, device_name, buffer_size, kCFStringEncodingUTF8)) {
403         strcpy(device_name, "");
404     }
405 
406     log_message(LOG_DEFAULT, "sound (coreaudio_init): Using output audio device: %s", device_name);
407 
408     lib_free(device_name);
409 
410     return 0;
411 }
412 
413 /* ------ Audio Unit API ------------------------------------------------- */
414 
415 #ifdef DEBUG
416 /* FIXME: this hack fixes the processing of <CoreServices/CoreServices.h>
417    during a debug build. */
418 #undef DEBUG
419 #define DEBUG 1
420 #endif
421 
422 #include <AudioUnit/AudioUnit.h>
423 #include <CoreServices/CoreServices.h>
424 
425 static AudioUnit outputUnit;
426 
audio_render(void * inRefCon,AudioUnitRenderActionFlags * ioActionFlags,const AudioTimeStamp * inTimeStamp,UInt32 inBusNumber,UInt32 inNumberFrames,AudioBufferList * ioData)427 static OSStatus audio_render(void *inRefCon,
428                              AudioUnitRenderActionFlags  *ioActionFlags,
429                              const AudioTimeStamp        *inTimeStamp,
430                              UInt32 inBusNumber,
431                              UInt32 inNumberFrames,
432                              AudioBufferList             *ioData)
433 {
434     UInt32 numFrames = inNumberFrames;
435     return AudioConverterFillComplexBuffer(converter,
436                                            converter_input,
437                                            NULL,
438                                            &numFrames,
439                                            ioData,
440                                            NULL);
441 }
442 
audio_open(AudioStreamBasicDescription * in)443 static int audio_open(AudioStreamBasicDescription *in)
444 {
445     OSStatus err;
446     AudioComponentDescription desc;
447     AudioStreamBasicDescription out;
448     UInt32 size;
449     AudioComponent output_component;
450 
451     /* find the default audio component */
452     desc.componentType = kAudioUnitType_Output;
453     desc.componentSubType = kAudioUnitSubType_HALOutput;
454     desc.componentManufacturer = kAudioUnitManufacturer_Apple;
455     desc.componentFlags = 0;
456     desc.componentFlagsMask = 0;
457 
458     output_component = AudioComponentFindNext(NULL, &desc);
459     if (output_component == NULL) {
460         log_error(LOG_DEFAULT, "sound (coreaudio_init): can't find HAL output component");
461         return -1;
462     }
463 
464     /* open audio component and enable output IO */
465     err = AudioComponentInstanceNew(output_component, &outputUnit);
466     if (err) {
467         log_error(LOG_DEFAULT, "sound (coreaudio_init): error opening output unit");
468         outputUnit = NULL;
469         return -1;
470     }
471 
472     /* select output device */
473     if(determine_output_device_id()) {
474         return -1;
475     }
476 
477     err = AudioUnitSetProperty(outputUnit,
478                                kAudioOutputUnitProperty_CurrentDevice,
479                                kAudioUnitScope_Global,
480                                0,
481                                &device,
482                                sizeof(device));
483     if (err) {
484         log_error(LOG_DEFAULT,
485                   "sound (coreaudio_init): error setting device id");
486         return -1;
487     }
488 
489     /* Set up a callback function to generate output to the output unit */
490     AURenderCallbackStruct input;
491     input.inputProc = audio_render;
492     input.inputProcRefCon = NULL;
493     err = AudioUnitSetProperty(outputUnit,
494                                kAudioUnitProperty_SetRenderCallback,
495                                kAudioUnitScope_Input,
496                                0,
497                                &input,
498                                sizeof(input));
499     if (err) {
500         log_error(LOG_DEFAULT,
501                   "sound (coreaudio_init): error setting render callback");
502         return -1;
503     }
504 
505     /*
506      * For accuracy, we want to convert directly from the emulator output
507      * to the format used by the audio hardware, in a single step.
508      */
509 
510     /* Get hardware output properties */
511     size = sizeof(AudioStreamBasicDescription);
512     err = AudioUnitGetProperty(outputUnit,
513                                kAudioUnitProperty_StreamFormat,
514                                kAudioUnitScope_Output,
515                                0,
516                                &out,
517                                &size);
518     if (err) {
519         log_error(LOG_DEFAULT, "sound (coreaudio_init): error getting default input format");
520         return -1;
521     }
522 
523     /* Tell the AudioUnit that we'll give it samples in the same format */
524     err = AudioUnitSetProperty(outputUnit,
525                                kAudioUnitProperty_StreamFormat,
526                                kAudioUnitScope_Input,
527                                0,
528                                &out,
529                                size);
530     if (err) {
531         log_error(LOG_DEFAULT, "sound (coreaudio_init): error setting desired sample rate");
532     }
533 
534     /* Get the final format that we'll need to use - the above may have failed */
535     err = AudioUnitGetProperty(outputUnit,
536                                kAudioUnitProperty_StreamFormat,
537                                kAudioUnitScope_Input,
538                                0,
539                                &out,
540                                &size);
541     if (err) {
542         log_error(LOG_DEFAULT, "sound (coreaudio_init): error getting final output format");
543         return -1;
544     }
545 
546     /* Determine the range of supported buffer sizes */
547     AudioObjectPropertyAddress frame_size_range_address = {
548         kAudioDevicePropertyBufferFrameSizeRange,
549         kAudioObjectPropertyScopeOutput,
550         kAudioObjectPropertyElementMaster
551     };
552     AudioValueRange range = {0, 0};
553     size = sizeof(AudioValueRange);
554 
555     err = AudioObjectGetPropertyData(device, &frame_size_range_address, 0, NULL, &size, &range);
556     if (err) {
557         log_error(LOG_DEFAULT, "sound (coreaudio_init): error getting kAudioDevicePropertyBufferFrameSizeRange");
558         return -1;
559     }
560 
561     size = range.mMinimum > frames_in_fragment ? range.mMinimum : frames_in_fragment;
562 
563     log_message(LOG_DEFAULT, "sound (coreaudio_init): audio frame buffer size in samples min: %f max: %f fragment size: %u, chosen: %d", range.mMinimum, range.mMaximum, frames_in_fragment, size);
564 
565     /* set the buffer size */
566     AudioObjectPropertyAddress buffer_size_address = {
567         kAudioDevicePropertyBufferFrameSize,
568         kAudioObjectPropertyScopeGlobal,
569         kAudioObjectPropertyElementMaster
570     };
571 
572     err = AudioObjectSetPropertyData(device, &buffer_size_address, 0, NULL, sizeof(UInt32), &size);
573     if (err) {
574         log_error(LOG_DEFAULT, "sound (coreaudio_init): error setting buffer size to %d", size);
575         return -1;
576     }
577     err = AudioUnitSetProperty(outputUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &size, sizeof(size));
578     if (err) {
579         log_error(LOG_DEFAULT, "sound (coreaudio_init): error setting max frames per slice to %d", size);
580         return -1;
581     }
582 
583     /* open converter */
584     err = converter_open(in, &out);
585     if (err) {
586         log_error(LOG_DEFAULT, "sound (coreaudio_init): error initializing audio converter");
587         return -1;
588     }
589 
590     return 0;
591 }
592 
audio_start(void)593 static int audio_start(void)
594 {
595     OSStatus err;
596 
597     /* Init unit */
598     err = AudioUnitInitialize(outputUnit);
599     if (err) {
600         log_error(LOG_DEFAULT,
601                   "sound (coreaudio_start): error initializing audio unit");
602         return -1;
603     }
604 
605     err = AudioOutputUnitStart(outputUnit);
606     if (err) {
607         log_error(LOG_DEFAULT, "sound (coreaudio_start): failed to start audio device");
608         return -1;
609     }
610 
611     log_message(LOG_DEFAULT, "sound (coreaudio_start): Started");
612     return 0;
613 }
614 
audio_stop(void)615 static int audio_stop(void)
616 {
617     OSStatus err;
618 
619     err = AudioOutputUnitStop(outputUnit);
620     if (err) {
621         log_error(LOG_DEFAULT, "sound (audio_stop): error uninitializing audio unit");
622         return -1;
623     }
624 
625     err = AudioUnitUninitialize(outputUnit);
626     if (err) {
627         log_error(LOG_DEFAULT, "sound (audio_stop): error uninitializing audio unit");
628         return -1;
629     }
630 
631     return 0;
632 }
633 
audio_close(void)634 static void audio_close(void)
635 {
636     OSStatus err;
637 
638     if (outputUnit) {
639         audio_stop();
640 
641         log_message(LOG_DEFAULT, "AudioComponentInstanceDispose");
642         err = AudioComponentInstanceDispose(outputUnit);
643         if (err) {
644             log_error(LOG_DEFAULT, "sound (audio_close): error disposing of audio unit");
645         }
646 
647         outputUnit = NULL;
648     }
649 
650     converter_close();
651 }
652 
653 /* ---------- coreaudio VICE API ------------------------------------------ */
654 
655 static int coreaudio_resume(void);
656 
coreaudio_init(const char * param,int * speed,int * fragsize,int * fragnr,int * channels)657 static int coreaudio_init(const char *param, int *speed,
658                           int *fragsize, int *fragnr, int *channels)
659 {
660     AudioStreamBasicDescription in;
661     int result;
662 
663     /* store fragment parameters */
664     if (param) {
665         requested_device_name_ref = CFStringCreateWithCString(NULL, param, kCFStringEncodingUTF8);
666 
667         CFIndex buffer_size = string_buf_size_for_utf8_char_length(CFStringGetLength(requested_device_name_ref));
668         requested_device_name = lib_calloc(1, buffer_size);
669 
670         if(!CFStringGetCString(requested_device_name_ref, requested_device_name, buffer_size, kCFStringEncodingUTF8)) {
671             strcpy(requested_device_name, "");
672         }
673     }
674     fragment_count = *fragnr;
675     frames_in_fragment = *fragsize;
676     in_channels = *channels;
677 
678     /* the size of a fragment in bytes and SWORDs */
679     swords_in_fragment = frames_in_fragment * in_channels;
680     bytes_in_fragment = swords_in_fragment * sizeof(int16_t);
681 
682     /* the size of a sample */
683     in_frame_byte_size = sizeof(int16_t) * in_channels;
684 
685     /* allocate sound buffers */
686     ringbuffer = lib_calloc(fragment_count, bytes_in_fragment);
687 
688     /* define desired input format */
689     in.mChannelsPerFrame = *channels;
690     in.mSampleRate = (float)*speed;
691     in.mFormatID = kAudioFormatLinearPCM;
692 #if defined(__x86_64__) || defined(__i386__)
693     in.mFormatFlags = kAudioFormatFlagIsSignedInteger;
694 #else
695     in.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsBigEndian;
696 #endif
697     in.mBytesPerFrame = sizeof(int16_t) * *channels;
698     in.mBytesPerPacket = in.mBytesPerFrame;
699     in.mFramesPerPacket = 1;
700     in.mBitsPerChannel = 8 * sizeof(int16_t);
701     in.mReserved = 0;
702 
703     /* setup audio device */
704     result = audio_open(&in);
705     if (result < 0) {
706         return result;
707     }
708 
709     coreaudio_resume();
710 
711     return 0;
712 }
713 
coreaudio_write(int16_t * pbuf,size_t nr)714 static int coreaudio_write(int16_t *pbuf, size_t nr)
715 {
716     int i;
717     size_t count;
718 
719     /* number of fragments */
720     count = nr / swords_in_fragment;
721 
722     for (i = 0; i < count; i++)
723     {
724         if (fragments_in_queue_2 == fragment_count) {
725             log_warning(LOG_DEFAULT, "sound (coreaudio): buffer overrun");
726             return 0;
727         }
728 
729         memcpy((int16_t *)ringbuffer + (swords_in_fragment * write_position_2),
730                pbuf + (i * swords_in_fragment),
731                bytes_in_fragment);
732 
733         write_position_2 = (write_position_2 + 1) % fragment_count;
734 
735         OSAtomicIncrement32(&fragments_in_queue_2);
736     }
737 
738     /* coreaudio_buffer_stats(false); */
739 
740     return 0;
741 }
742 
coreaudio_bufferspace(void)743 static int coreaudio_bufferspace(void)
744 {
745     return (fragment_count - fragments_in_queue_2) * frames_in_fragment;
746 }
747 
coreaudio_close(void)748 static void coreaudio_close(void)
749 {
750     audio_close();
751 
752     lib_free((void *)ringbuffer);
753     lib_free((void *)copybuffer);
754 
755     ringbuffer = NULL;
756     copybuffer = NULL;
757     copybuffer_size_bytes = 0;
758 
759     if (requested_device_name_ref) {
760         CFRelease(requested_device_name_ref);
761         requested_device_name_ref = NULL;
762     }
763 
764     if(requested_device_name) {
765         lib_free(requested_device_name);
766         requested_device_name = NULL;
767     }
768 }
769 
coreaudio_suspend(void)770 static int coreaudio_suspend(void)
771 {
772     int result = audio_stop();
773     if (result < 0) {
774         log_error(LOG_DEFAULT, "sound (coreaudio_init): could not stop audio");
775     }
776     return result;
777 }
778 
coreaudio_resume(void)779 static int coreaudio_resume(void)
780 {
781     int result;
782 
783     /* reset buffers before resume */
784     read_position_2 = 0;
785     write_position_2 = 0;
786     fragments_in_queue_2 = 0;
787     frames_left_in_fragment_2 = frames_in_fragment;
788 
789     result = audio_start();
790     if (result < 0) {
791         log_error(LOG_DEFAULT, "sound (coreaudio_init): could not start audio");
792     }
793     return result;
794 }
795 
796 static sound_device_t coreaudio_device =
797 {
798     "coreaudio",
799     coreaudio_init,
800     coreaudio_write,
801     NULL,
802     NULL,
803     coreaudio_bufferspace,
804     coreaudio_close,
805     coreaudio_suspend,
806     coreaudio_resume,
807     1,
808     2,
809     true
810 };
811 
sound_init_coreaudio_device(void)812 int sound_init_coreaudio_device(void)
813 {
814     return sound_register_device(&coreaudio_device);
815 }
816 
817 #endif /* USE_COREAUDIO_SUPPORT */
818