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 
50 #include "lib.h"
51 #include "log.h"
52 #include "sound.h"
53 
54 /* Requested audio device name */
55 CFStringRef requested_device_name_ref = NULL;
56 char * requested_device_name = NULL;
57 
58 /* resolved device id */
59 static AudioDeviceID device = kAudioDeviceUnknown;
60 
61 /* type for atomic increments */
62 typedef volatile int atomic_int_t;
63 
64 /* the cyclic buffer containing m fragments */
65 static int16_t *soundbuffer;
66 
67 /* silence fragment */
68 static int16_t *silence;
69 
70 /* current read position: no. of fragment in soundbuffer */
71 static unsigned int read_position;
72 
73 /* the next position to write: no. of fragment in soundbuffer */
74 static unsigned int write_position;
75 
76 /* frames in fragment  */
77 static unsigned int frames_in_fragment;
78 
79 /* Size of fragment (bytes).  */
80 static unsigned int bytes_in_fragment;
81 
82 /* Size of fragment (SWORDs) */
83 static unsigned int swords_in_fragment;
84 
85 /* total number of fragments */
86 static unsigned int fragment_count;
87 
88 /* current number of fragments in buffer */
89 static atomic_int_t fragments_in_queue;
90 
91 /* number of interleaved channels (mono SID=1, stereo SID=2) */
92 static int in_channels;
93 
94 /* samples left in current fragment */
95 static unsigned int frames_left_in_fragment;
96 
97 /* bytes per input frame */
98 static unsigned int in_frame_byte_size;
99 
100 /* ----- Atomic Increment/Decrement for Thread-Safe Audio Buffers -------- */
101 
102 #if defined(__x86_64__) || defined(__i386__)
103 /* Intel Mac Implementation */
104 
atomic_increment(atomic_int_t * addr)105 static inline void atomic_increment(atomic_int_t * addr)
106 {
107     __asm__ __volatile__ ("lock ; incl %0"
108                           :"=m" (*addr)
109                           :"m" (*addr));
110 }
111 
atomic_decrement(atomic_int_t * addr)112 static inline void atomic_decrement(atomic_int_t * addr)
113 {
114     __asm__ __volatile__ ("lock ; decl %0"
115                           :"=m" (*addr)
116                           :"m" (*addr));
117 }
118 
119 #else
120 /* PowerPC Mac Implementation */
121 
atomic_add(atomic_int_t * addr,int val)122 static inline void atomic_add(atomic_int_t * addr, int val)
123 {
124     register int tmp;
125     asm volatile("    lwarx  %0,0,%2  \n\t"  /* load value & reserve */
126                  "    addc   %0,%0,%3 \n\t"  /* add <val> */
127                  "    stwcx. %0,0,%2  \n\n"  /* store new value */
128                  "    bne-   $-12"           /* check if store was successful */
129                  : "=&r"(tmp), "=m"(addr)
130                  : "r"(addr), "r"(val), "m"(addr)
131                  : "cr0"
132                 );
133 }
134 
atomic_increment(atomic_int_t * addr)135 static inline void atomic_increment(atomic_int_t * addr)
136 {
137     atomic_add(addr, 1);
138 }
139 
atomic_decrement(atomic_int_t * addr)140 static inline void atomic_decrement(atomic_int_t * addr)
141 {
142     atomic_add(addr, -1);
143 }
144 
145 #endif
146 
147 /* ----- Audio Converter ------------------------------------------------ */
148 
149 static AudioConverterRef converter = 0;
150 
converter_input(AudioConverterRef inAudioConverter,UInt32 * ioNumberDataPackets,AudioBufferList * ioData,AudioStreamPacketDescription ** outDataPacketDescription,void * inUserData)151 static OSStatus converter_input(AudioConverterRef inAudioConverter,
152                                 UInt32 * ioNumberDataPackets,
153                                 AudioBufferList * ioData,
154                                 AudioStreamPacketDescription** outDataPacketDescription,
155                                 void * inUserData)
156 {
157     UInt32 num_frames = *ioNumberDataPackets;
158 
159     int16_t *buffer;
160     if (fragments_in_queue) {
161         /* too many -> crop to available in current fragment */
162         if (num_frames > frames_left_in_fragment) {
163             num_frames = frames_left_in_fragment;
164         }
165 
166         /* calc position in sound buffer */
167         int sample_offset_in_fragment = frames_in_fragment - frames_left_in_fragment;
168         buffer = soundbuffer + swords_in_fragment * read_position + sample_offset_in_fragment * in_channels;
169 
170         /* update the samples left in the current fragment */
171         frames_left_in_fragment -= num_frames;
172 
173         /* fetch next fragment */
174         if (frames_left_in_fragment == 0) {
175             read_position = (read_position + 1) % fragment_count;
176             atomic_decrement(&fragments_in_queue);
177             frames_left_in_fragment = frames_in_fragment;
178         }
179     } else {
180         if (num_frames > frames_in_fragment) {
181             num_frames = frames_in_fragment;
182         }
183 
184         /* output silence */
185         buffer = silence;
186     }
187 
188     /* prepare return buffer */
189     ioData->mBuffers[0].mDataByteSize = num_frames * in_frame_byte_size;
190     ioData->mBuffers[0].mData = buffer;
191     *ioNumberDataPackets = num_frames;
192 
193     return kAudioHardwareNoError;
194 }
195 
converter_open(AudioStreamBasicDescription * in,AudioStreamBasicDescription * out)196 static int converter_open(AudioStreamBasicDescription *in,
197                           AudioStreamBasicDescription *out)
198 {
199     OSStatus err;
200 
201     /* need to to sample rate conversion? */
202     if (out->mSampleRate != in->mSampleRate) {
203         log_warning(LOG_DEFAULT, "sound (coreaudio_init): sampling rate conversion %dHz->%dHz",
204                     (int)in->mSampleRate, (int)out->mSampleRate);
205     }
206 
207     /* create a new audio converter */
208     err = AudioConverterNew(in, out, &converter);
209     if (err != noErr) {
210         log_error(LOG_DEFAULT,
211                   "sound (coreaudio_init): could not create AudioConverter: err=%d", (int)err);
212         return -1;
213     }
214 
215     /* duplicate mono stream to all output channels */
216     if (in->mChannelsPerFrame == 1 && out->mChannelsPerFrame > 1) {
217         Boolean writable;
218         UInt32 size;
219         err = AudioConverterGetPropertyInfo(converter, kAudioConverterChannelMap, &size, &writable);
220         if (err == noErr && writable) {
221             SInt32 * channel_map = lib_malloc(size);
222             if (channel_map) {
223                 memset(channel_map, 0, size);
224                 AudioConverterSetProperty(converter, kAudioConverterChannelMap, size, channel_map);
225                 lib_free(channel_map);
226             }
227         }
228     }
229 
230     return 0;
231 }
232 
converter_close(void)233 static void converter_close(void)
234 {
235     if (converter) {
236         AudioConverterDispose(converter);
237         converter = NULL;
238     }
239 }
240 
string_buf_size_for_utf8_char_length(int utf8Chars)241 static int string_buf_size_for_utf8_char_length(int utf8Chars)
242 {
243     return utf8Chars * 4 + 1;
244 }
245 
246 #if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MIN_REQUIRED>=MAC_OS_X_VERSION_10_6)
determine_output_device_id()247 static int determine_output_device_id()
248 {
249     OSStatus err;
250     UInt32 size;
251     AudioDeviceID default_device;
252 
253     /* get default audio device id */
254     size = sizeof(default_device);
255     err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
256                                    &size, (void*)&default_device);
257     if (err != kAudioHardwareNoError) {
258         log_error(LOG_DEFAULT, "sound (coreaudio_init): Failed to get default output device");
259         return -1;
260     }
261 
262     /* use the default audio device unless overridden */
263     device = default_device;
264 
265     if (requested_device_name) {
266         log_message(LOG_DEFAULT, "sound (coreaudio_init): Searching for audio output device: %s", requested_device_name);
267     }
268 
269     /* list audio devices */
270     AudioObjectPropertyAddress property_address = {
271         kAudioHardwarePropertyDevices,
272         kAudioObjectPropertyScopeGlobal,
273         kAudioObjectPropertyElementMaster
274     };
275     size = 0;
276     err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &property_address, 0, NULL, &size);
277     if (err != kAudioHardwareNoError) {
278         log_error(LOG_DEFAULT, "sound (coreaudio_init): AudioObjectGetPropertyDataSize (kAudioHardwarePropertyDevices) failed: %d", (int)err);
279         return -1;
280     }
281 
282     UInt32 device_count = size / sizeof(AudioDeviceID);
283 
284     AudioDeviceID *audio_devices = (AudioDeviceID *)(lib_calloc(device_count, sizeof(AudioDeviceID)));
285     if (audio_devices == NULL) {
286         log_error(LOG_DEFAULT, "sound (coreaudio_init): Unable to allocate memory");
287         return -1;
288     }
289 
290     err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property_address, 0, NULL, &size, audio_devices);
291     if (err != kAudioHardwareNoError) {
292         log_error(LOG_DEFAULT, "sound (coreaudio_init): AudioObjectGetPropertyData (kAudioHardwarePropertyDevices) failed: %d", (int)err);
293         lib_free(audio_devices);
294         audio_devices = NULL;
295         return -1;
296     }
297 
298     CFStringRef device_name_ref = NULL;
299     char * device_name = NULL;
300     CFIndex buffer_size = 0;
301 
302     /* search list of output devices for matching name */
303     for(UInt32 i = 0; i < device_count; ++i) {
304         property_address.mSelector = kAudioDevicePropertyStreams;
305         property_address.mScope = kAudioDevicePropertyScopeOutput;
306 
307         size = 0;
308         err = AudioObjectGetPropertyDataSize(audio_devices[i], &property_address, 0, NULL, &size);
309         if (err != kAudioHardwareNoError) {
310             log_error(LOG_DEFAULT, "sound (coreaudio_init): AudioObjectGetPropertyDataSize (kAudioDevicePropertyStreamConfiguration) failed: %d", (int)err);
311             lib_free(audio_devices);
312             audio_devices = NULL;
313             return -1;
314         }
315 
316         bool outputs_found = size > 0;
317 
318         /* get device name */
319         size = sizeof(device_name_ref);
320         property_address.mSelector = kAudioDevicePropertyDeviceNameCFString;
321         err = AudioObjectGetPropertyData(audio_devices[i], &property_address, 0, NULL, &size, &device_name_ref);
322         if (err != kAudioHardwareNoError) {
323             log_error(LOG_DEFAULT, "sound (coreaudio_init): AudioObjectGetPropertyData (kAudioDevicePropertyDeviceNameCFString) failed: %d", (int)err);
324             lib_free(audio_devices);
325             audio_devices = NULL;
326             return -1;
327         }
328 
329         buffer_size = string_buf_size_for_utf8_char_length(CFStringGetLength(device_name_ref));
330         device_name = lib_calloc(1, buffer_size);
331 
332         if(!CFStringGetCString(device_name_ref, device_name, buffer_size, kCFStringEncodingUTF8)) {
333             strcpy(device_name, "");
334         }
335 
336         if (!outputs_found) {
337             log_message(LOG_DEFAULT, "sound (coreaudio_init): Found audio device with no outputs: %s", device_name);
338             lib_free(device_name);
339             continue;
340         }
341 
342         if (audio_devices[i] == default_device) {
343             log_message(LOG_DEFAULT, "sound (coreaudio_init): Found output audio device: %s (Default)", device_name);
344         } else {
345             log_message(LOG_DEFAULT, "sound (coreaudio_init): Found output audio device: %s", device_name);
346         }
347 
348         if (requested_device_name == NULL) {
349             lib_free(device_name);
350             continue;
351         }
352 
353         if (kCFCompareEqualTo == CFStringCompare(requested_device_name_ref, device_name_ref, 0)) {
354             /* matches the requested audio device */
355             device = audio_devices[i];
356         }
357 
358         lib_free(device_name);
359     }
360 
361     lib_free(audio_devices);
362     audio_devices = NULL;
363 
364     /* get final device name */
365     device_name_ref = NULL;
366     size = sizeof(device_name_ref);
367     property_address.mSelector = kAudioDevicePropertyDeviceNameCFString;
368     err = AudioObjectGetPropertyData(device, &property_address, 0, NULL, &size, &device_name_ref);
369     if (err != kAudioHardwareNoError) {
370         log_error(LOG_DEFAULT, "sound (coreaudio_init): AudioObjectGetPropertyData (kAudioDevicePropertyDeviceNameCFString) failed: %d", (int)err);
371         return -1;
372     }
373 
374     buffer_size = string_buf_size_for_utf8_char_length(CFStringGetLength(device_name_ref));
375     device_name = lib_calloc(1, buffer_size);
376 
377     if(!CFStringGetCString(device_name_ref, device_name, buffer_size, kCFStringEncodingUTF8)) {
378         strcpy(device_name, "");
379     }
380 
381     log_message(LOG_DEFAULT, "sound (coreaudio_init): Using output audio device: %s", device_name);
382 
383     lib_free(device_name);
384 
385     return 0;
386 }
387 #endif
388 
389 /* ----- Audio API before AudioUnits ------------------------------------- */
390 
391 #ifndef HAVE_AUDIO_UNIT
392 
393 /* bytes per output frame */
394 static unsigned int out_frame_byte_size;
395 
396 /* proc id */
397 #if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED>=MAC_OS_X_VERSION_10_5)
398 static AudioDeviceIOProcID procID;
399 #endif
400 
audio_render(AudioDeviceID device,const AudioTimeStamp * now,const AudioBufferList * input_data,const AudioTimeStamp * input_time,AudioBufferList * output_data,const AudioTimeStamp * output_time,void * client_data)401 static OSStatus audio_render(AudioDeviceID device,
402                              const AudioTimeStamp  * now,
403                              const AudioBufferList * input_data,
404                              const AudioTimeStamp  * input_time,
405                              AudioBufferList       * output_data,
406                              const AudioTimeStamp  * output_time,
407                              void                  * client_data)
408 {
409     /* get the number of frames(=packets) in the output buffer */
410     UInt32 bufferPacketSize = output_data->mBuffers[0].mDataByteSize / out_frame_byte_size;
411 
412     OSStatus result = AudioConverterFillComplexBuffer(converter,
413                                                       converter_input,
414                                                       NULL,
415                                                       &bufferPacketSize,
416                                                       output_data,
417                                                       NULL);
418 
419     return result;
420 }
421 
audio_open(AudioStreamBasicDescription * in)422 static int audio_open(AudioStreamBasicDescription *in)
423 {
424     OSStatus err;
425     UInt32 size;
426 
427     if (0 != determine_output_device_id()) {
428         return -1;
429     }
430 
431     AudioStreamBasicDescription out;
432     /* get default output format */
433     size = sizeof(out);
434     err = AudioDeviceGetProperty(device, 0, false,
435                                  kAudioDevicePropertyStreamFormat,
436                                  &size, (void*)&out);
437     if (err != kAudioHardwareNoError) {
438         log_error(LOG_DEFAULT, "sound (coreaudio_init): stream format not support");
439         return -1;
440     }
441     /* store size of output frame */
442     out_frame_byte_size = out.mBytesPerPacket;
443 
444     /* setup audio renderer callback */
445 #if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED>=MAC_OS_X_VERSION_10_5)
446     err = AudioDeviceCreateIOProcID( device, audio_render, NULL, &procID );
447 #else
448     err = AudioDeviceAddIOProc( device, audio_render, NULL );
449 #endif
450     if (err != kAudioHardwareNoError) {
451         log_error(LOG_DEFAULT,
452                   "sound (coreaudio_init): could not add IO proc: err=%d", (int)err);
453         return -1;
454     }
455 
456     /* open audio converter */
457     return converter_open(in, &out);
458 }
459 
audio_close(void)460 static void audio_close(void)
461 {
462 #if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED>=MAC_OS_X_VERSION_10_5)
463     AudioDeviceDestroyIOProcID(device, procID);
464 #else
465     AudioDeviceRemoveIOProc(device, audio_render);
466 #endif
467 
468     converter_close();
469 }
470 
audio_start(void)471 static int audio_start(void)
472 {
473     OSStatus err = AudioDeviceStart(device, audio_render);
474     if (err != kAudioHardwareNoError) {
475         return -1;
476     } else {
477         return 0;
478     }
479 }
480 
audio_stop(void)481 static int audio_stop(void)
482 {
483     OSStatus err = AudioDeviceStop(device, audio_render);
484     if (err != kAudioHardwareNoError) {
485         return -1;
486     } else {
487         return 0;
488     }
489 }
490 
491 #else /* HAVE_AUDIO_UNIT */
492 /* ------ Audio Unit API ------------------------------------------------- */
493 
494 #ifdef DEBUG
495 /* FIXME: this hack fixes the processing of <CoreServices/CoreServices.h>
496    during a debug build. */
497 #undef DEBUG
498 #define DEBUG 1
499 #endif
500 
501 #include <AudioUnit/AudioUnit.h>
502 #include <CoreServices/CoreServices.h>
503 
504 static AudioUnit outputUnit;
505 
audio_render(void * inRefCon,AudioUnitRenderActionFlags * ioActionFlags,const AudioTimeStamp * inTimeStamp,UInt32 inBusNumber,UInt32 inNumberFrames,AudioBufferList * ioData)506 static OSStatus audio_render(void *inRefCon,
507                              AudioUnitRenderActionFlags  *ioActionFlags,
508                              const AudioTimeStamp        *inTimeStamp,
509                              UInt32 inBusNumber,
510                              UInt32 inNumberFrames,
511                              AudioBufferList             *ioData)
512 {
513     UInt32 numFrames = inNumberFrames;
514     return AudioConverterFillComplexBuffer(converter,
515                                            converter_input,
516                                            NULL,
517                                            &numFrames,
518                                            ioData,
519                                            NULL);
520 }
521 
522 #if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MIN_REQUIRED>=MAC_OS_X_VERSION_10_6)
audio_open(AudioStreamBasicDescription * in)523 static int audio_open(AudioStreamBasicDescription *in)
524 {
525     OSStatus err;
526     AudioComponentDescription desc;
527     AudioStreamBasicDescription out;
528     UInt32 size;
529     AudioComponent output_component;
530 
531     /* find the default audio component */
532     desc.componentType = kAudioUnitType_Output;
533     desc.componentSubType = kAudioUnitSubType_HALOutput;
534     desc.componentManufacturer = kAudioUnitManufacturer_Apple;
535     desc.componentFlags = 0;
536     desc.componentFlagsMask = 0;
537 
538     output_component = AudioComponentFindNext(NULL, &desc);
539     if (output_component == NULL) {
540         log_error(LOG_DEFAULT, "sound (coreaudio_init): can't find HAL output component");
541         return -1;
542     }
543 
544     /* open audio component and enable output IO */
545     err = AudioComponentInstanceNew(output_component, &outputUnit);
546     if (err) {
547         log_error(LOG_DEFAULT, "sound (coreaudio_init): error opening output unit");
548         return -1;
549     }
550 
551     /* select output device */
552     if(0 != determine_output_device_id()) {
553         return -1;
554     }
555 
556     err = AudioUnitSetProperty(outputUnit,
557                                kAudioOutputUnitProperty_CurrentDevice,
558                                kAudioUnitScope_Global,
559                                0,
560                                &device,
561                                sizeof(device));
562 
563     if (err) {
564         log_error(LOG_DEFAULT,
565                   "sound (coreaudio_init): error setting device id");
566         return -1;
567     }
568 
569     /* Set up a callback function to generate output to the output unit */
570     AURenderCallbackStruct input;
571     input.inputProc = audio_render;
572     input.inputProcRefCon = NULL;
573     err = AudioUnitSetProperty(outputUnit,
574                                kAudioUnitProperty_SetRenderCallback,
575                                kAudioUnitScope_Input,
576                                0,
577                                &input,
578                                sizeof(input));
579     if (err) {
580         log_error(LOG_DEFAULT,
581                   "sound (coreaudio_init): error setting render callback");
582         return -1;
583     }
584 
585     /* Get output properties */
586     size = sizeof(AudioStreamBasicDescription);
587     err = AudioUnitGetProperty(outputUnit,
588                                kAudioUnitProperty_StreamFormat,
589                                kAudioUnitScope_Input,
590                                0,
591                                &out,
592                                &size);
593     if (err) {
594         log_error(LOG_DEFAULT,
595                   "sound (coreaudio_init): error setting desired input format");
596         return -1;
597     }
598 
599     /* Init unit */
600     err = AudioUnitInitialize(outputUnit);
601     if (err) {
602         log_error(LOG_DEFAULT,
603                   "sound (coreaudio_init): error initializing audio unit");
604         return -1;
605     }
606 
607     /* open converter */
608     return converter_open(in, &out);
609 }
610 
audio_close(void)611 static void audio_close(void)
612 {
613     OSStatus err;
614 
615     converter_close();
616 
617     /* Uninit unit */
618     err = AudioUnitUninitialize(outputUnit);
619     if (err) {
620         log_error(LOG_DEFAULT, "sound (coreaudio_close): error uninitializing audio unit");
621     }
622 
623     /* Close component */
624     AudioComponentInstanceDispose(outputUnit);
625 }
626 #else
audio_open(AudioStreamBasicDescription * in)627 static int audio_open(AudioStreamBasicDescription *in)
628 {
629     OSStatus err;
630     UInt32 size;
631     AudioStreamBasicDescription out;
632 
633     /* get default audio device */
634     size = sizeof(device);
635     err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
636                                    &size, (void*)&device);
637     if (err != kAudioHardwareNoError) {
638         log_error(LOG_DEFAULT, "sound (coreaudio_init): Failed to get default output device");
639         return -1;
640     }
641 
642     /* get default output format */
643     size = sizeof(out);
644     err = AudioDeviceGetProperty(device, 0, false,
645                                  kAudioDevicePropertyStreamFormat,
646                                  &size, (void*)&out);
647     if (err != kAudioHardwareNoError) {
648         log_error(LOG_DEFAULT, "sound (coreaudio_init): stream format not support");
649         return -1;
650     }
651     /* store size of output frame */
652     out_frame_byte_size = out.mBytesPerPacket;
653 
654     /* setup audio renderer callback */
655 #if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED>=MAC_OS_X_VERSION_10_5)
656     err = AudioDeviceCreateIOProcID( device, audio_render, NULL, &procID );
657 #else
658     err = AudioDeviceAddIOProc( device, audio_render, NULL );
659 #endif
660     if (err != kAudioHardwareNoError) {
661         log_error(LOG_DEFAULT,
662                   "sound (coreaudio_init): could not add IO proc: err=%d", (int)err);
663         return -1;
664     }
665 
666     /* open audio converter */
667     return converter_open(in, &out);
668 }
669 
audio_close(void)670 static void audio_close(void)
671 {
672 #if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED>=MAC_OS_X_VERSION_10_5)
673     AudioDeviceDestroyIOProcID(device, procID);
674 #else
675     AudioDeviceRemoveIOProc(device, audio_render);
676 #endif
677 
678     converter_close();
679 }
680 #endif
681 
audio_start(void)682 static int audio_start(void)
683 {
684     OSStatus err = AudioOutputUnitStart(outputUnit);
685     if (err) {
686         return -1;
687     } else {
688         return 0;
689     }
690 }
691 
audio_stop(void)692 static int audio_stop(void)
693 {
694     OSStatus err = AudioOutputUnitStop(outputUnit);
695     if (err) {
696         return -1;
697     } else {
698         return 0;
699     }
700 }
701 
702 #endif /* HAVE_AUDIO_UNIT */
703 
704 /* ---------- coreaudio VICE API ------------------------------------------ */
705 
706 static int coreaudio_resume(void);
707 
coreaudio_init(const char * param,int * speed,int * fragsize,int * fragnr,int * channels)708 static int coreaudio_init(const char *param, int *speed,
709                           int *fragsize, int *fragnr, int *channels)
710 {
711     AudioStreamBasicDescription in;
712     int result;
713 
714     /* store fragment parameters */
715     if (param) {
716         requested_device_name_ref = CFStringCreateWithCString(NULL, param, kCFStringEncodingUTF8);
717 
718         CFIndex buffer_size = string_buf_size_for_utf8_char_length(CFStringGetLength(requested_device_name_ref));
719         requested_device_name = lib_calloc(1, buffer_size);
720 
721         if(!CFStringGetCString(requested_device_name_ref, requested_device_name, buffer_size, kCFStringEncodingUTF8)) {
722             strcpy(requested_device_name, "");
723         }
724     }
725     fragment_count = *fragnr;
726     frames_in_fragment = *fragsize;
727     in_channels = *channels;
728 
729     /* the size of a fragment in bytes and SWORDs */
730     swords_in_fragment = frames_in_fragment * in_channels;
731     bytes_in_fragment = swords_in_fragment * sizeof(int16_t);
732 
733     /* the size of a sample */
734     in_frame_byte_size = sizeof(int16_t) * in_channels;
735 
736     /* allocate sound buffers */
737     soundbuffer = lib_calloc(fragment_count, bytes_in_fragment);
738     silence = lib_calloc(1, bytes_in_fragment);
739 
740     /* define desired input format */
741     in.mChannelsPerFrame = *channels;
742     in.mSampleRate = (float)*speed;
743     in.mFormatID = kAudioFormatLinearPCM;
744 #if defined(__x86_64__) || defined(__i386__)
745     in.mFormatFlags = kAudioFormatFlagIsSignedInteger;
746 #else
747     in.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsBigEndian;
748 #endif
749     in.mBytesPerFrame = sizeof(int16_t) * *channels;
750     in.mBytesPerPacket = in.mBytesPerFrame;
751     in.mFramesPerPacket = 1;
752     in.mBitsPerChannel = 8 * sizeof(int16_t);
753     in.mReserved = 0;
754 
755     /* setup audio device */
756     result = audio_open(&in);
757     if (result < 0) {
758         return result;
759     }
760 
761     coreaudio_resume();
762 
763     return 0;
764 }
765 
coreaudio_write(int16_t * pbuf,size_t nr)766 static int coreaudio_write(int16_t *pbuf, size_t nr)
767 {
768     int i, count;
769 
770     /* number of fragments */
771     count = nr / swords_in_fragment;
772 
773     for (i = 0; i < count; i++)
774     {
775         if (fragments_in_queue == fragment_count) {
776             log_warning(LOG_DEFAULT, "sound (coreaudio): buffer overrun");
777             return -1;
778         }
779 
780         memcpy(soundbuffer + swords_in_fragment * write_position,
781                pbuf + i * swords_in_fragment,
782                bytes_in_fragment);
783 
784         write_position = (write_position + 1) % fragment_count;
785 
786         atomic_increment(&fragments_in_queue);
787     }
788 
789     return 0;
790 }
791 
coreaudio_bufferspace(void)792 static int coreaudio_bufferspace(void)
793 {
794     return (fragment_count - fragments_in_queue) * frames_in_fragment;
795 }
796 
coreaudio_close(void)797 static void coreaudio_close(void)
798 {
799     audio_close();
800 
801     lib_free(soundbuffer);
802     lib_free(silence);
803 
804     if (requested_device_name_ref) {
805         CFRelease(requested_device_name_ref);
806         requested_device_name_ref = NULL;
807     }
808 
809     if(requested_device_name) {
810         lib_free(requested_device_name);
811         requested_device_name = NULL;
812     }
813 }
814 
coreaudio_suspend(void)815 static int coreaudio_suspend(void)
816 {
817     int result = audio_stop();
818     if (result < 0) {
819         log_error(LOG_DEFAULT, "sound (coreaudio_init): could not stop audio");
820     }
821     return result;
822 }
823 
coreaudio_resume(void)824 static int coreaudio_resume(void)
825 {
826     int result;
827 
828     /* reset buffers before resume */
829     read_position = 0;
830     write_position = 0;
831     fragments_in_queue = 0;
832     frames_left_in_fragment = frames_in_fragment;
833 
834     result = audio_start();
835     if (result < 0) {
836         log_error(LOG_DEFAULT, "sound (coreaudio_init): could not start audio");
837     }
838     return result;
839 }
840 
841 static sound_device_t coreaudio_device =
842 {
843     "coreaudio",
844     coreaudio_init,
845     coreaudio_write,
846     NULL,
847     NULL,
848     coreaudio_bufferspace,
849     coreaudio_close,
850     coreaudio_suspend,
851     coreaudio_resume,
852     1,
853     2
854 };
855 
sound_init_coreaudio_device(void)856 int sound_init_coreaudio_device(void)
857 {
858     return sound_register_device(&coreaudio_device);
859 }
860 #endif
861