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