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