2 * Copyright (C) 2015 Robin Gareus <robin@gareus.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #include "coreaudio_pcmio.h"
22 /* abstraction for deprecated CoreAudio */
24 static OSStatus GetPropertyWrapper (
25 AudioDeviceID id, UInt32 elem, bool input, AudioDevicePropertyID prop, UInt32* size, void * data)
28 AudioObjectPropertyAddress property_address;
29 property_address.mSelector = prop;
31 case kAudioDevicePropertyBufferFrameSize:
32 case kAudioDevicePropertyBufferFrameSizeRange:
33 property_address.mScope = kAudioObjectPropertyScopeGlobal;
36 property_address.mScope = input ? kAudioDevicePropertyScopeInput: kAudioDevicePropertyScopeOutput;
39 property_address.mElement = kAudioObjectPropertyElementMaster;
40 return AudioObjectGetPropertyData(id, &property_address, elem, NULL, size, data);
42 return AudioDeviceGetProperty(id, elem, input, prop, size, data);
46 static OSStatus SetPropertyWrapper (
47 AudioDeviceID id, const AudioTimeStamp* when, UInt32 chn, bool input, AudioDevicePropertyID prop, UInt32 size, void * data)
50 AudioObjectPropertyAddress property_address;
51 property_address.mSelector = prop;
52 property_address.mScope = input ? kAudioDevicePropertyScopeInput: kAudioDevicePropertyScopeOutput;
53 property_address.mElement = kAudioObjectPropertyElementMaster;
54 return AudioObjectSetPropertyData (id, &property_address, 0, NULL, size, data);
56 return AudioDeviceSetProperty (id, when, chn, input, prop, size, data);
60 static OSStatus GetHardwarePropertyInfoWrapper (AudioDevicePropertyID prop, UInt32* size)
63 AudioObjectPropertyAddress property_address;
64 property_address.mSelector = prop;
65 property_address.mScope = kAudioObjectPropertyScopeGlobal;
66 property_address.mElement = kAudioObjectPropertyElementMaster;
67 return AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &property_address, 0, NULL, size);
70 return AudioHardwareGetPropertyInfo(prop, size, &outWritable);
74 static OSStatus GetHardwarePropertyWrapper (AudioDevicePropertyID prop, UInt32* size, void *d)
77 AudioObjectPropertyAddress property_address;
78 property_address.mSelector = prop;
79 property_address.mScope = kAudioObjectPropertyScopeGlobal;
80 property_address.mElement = kAudioObjectPropertyElementMaster;
81 return AudioObjectGetPropertyData(kAudioObjectSystemObject, &property_address, 0, NULL, size, d);
83 return AudioHardwareGetProperty (kAudioHardwarePropertyDevices, size, d);
87 static OSStatus GetPropertyInfoWrapper (AudioDeviceID id, UInt32 elem, bool input, AudioDevicePropertyID prop, UInt32* size)
90 AudioObjectPropertyAddress property_address;
91 property_address.mSelector = prop;
92 property_address.mScope = input ? kAudioDevicePropertyScopeInput: kAudioDevicePropertyScopeOutput;
93 property_address.mElement = elem;
94 return AudioObjectGetPropertyDataSize(id, &property_address, 0, NULL, size);
97 return AudioDeviceGetPropertyInfo(id, elem, input, prop, size, &outWritable);
101 static OSStatus GetDeviceNameFromID(AudioDeviceID id, char* name)
104 return GetPropertyWrapper (id, 0, 0, kAudioDevicePropertyDeviceName, &size, name);
107 static CFStringRef GetDeviceName(AudioDeviceID id)
109 UInt32 size = sizeof(CFStringRef);
111 OSStatus err = GetPropertyWrapper (id, 0, 0, kAudioDevicePropertyDeviceUID, &size, &UIname);
112 return (err == noErr) ? UIname : NULL;
115 ///////////////////////////////////////////////////////////////////////////////
117 #include "coreaudio_pcmio_aggregate.cc"
123 static OSStatus property_callback_ptr (AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void* arg) {
124 CoreAudioPCM * self = static_cast<CoreAudioPCM*>(arg);
125 for (UInt32 i = 0; i < inNumberAddresses; ++i) {
126 switch (inAddresses[i].mSelector) {
127 case kAudioHardwarePropertyDevices:
128 self->hw_changed_callback();
130 case kAudioDeviceProcessorOverload:
131 self->xrun_callback();
133 case kAudioDevicePropertyBufferFrameSize:
134 self->buffer_size_callback();
136 case kAudioDevicePropertyNominalSampleRate:
137 self->sample_rate_callback();
148 static OSStatus hw_changed_callback_ptr (AudioHardwarePropertyID inPropertyID, void* arg) {
149 if (inPropertyID == kAudioHardwarePropertyDevices) {
150 CoreAudioPCM * self = static_cast<CoreAudioPCM*>(arg);
151 self->hw_changed_callback();
156 static OSStatus property_callback_ptr (
157 AudioDeviceID inDevice,
160 AudioDevicePropertyID inPropertyID,
163 CoreAudioPCM * d = static_cast<CoreAudioPCM*> (inClientData);
164 switch (inPropertyID) {
165 case kAudioDeviceProcessorOverload:
168 case kAudioDevicePropertyBufferFrameSize:
169 d->buffer_size_callback();
171 case kAudioDevicePropertyNominalSampleRate:
172 d->sample_rate_callback();
180 static OSStatus render_callback_ptr (
182 AudioUnitRenderActionFlags* ioActionFlags,
183 const AudioTimeStamp* inTimeStamp,
185 UInt32 inNumberFrames,
186 AudioBufferList* ioData)
188 CoreAudioPCM * d = static_cast<CoreAudioPCM*> (inRefCon);
189 return d->render_callback(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
193 static OSStatus add_listener (AudioDeviceID id, AudioDevicePropertyID selector, void *arg) {
195 AudioObjectPropertyAddress property_address;
196 property_address.mSelector = selector;
197 property_address.mScope = kAudioObjectPropertyScopeGlobal;
198 property_address.mElement = 0;
199 return AudioObjectAddPropertyListener(id, &property_address, property_callback_ptr, arg);
201 return AudioDeviceAddPropertyListener(id, 0, true, selector, property_callback_ptr, arg);
206 ///////////////////////////////////////////////////////////////////////////////
208 CoreAudioPCM::CoreAudioPCM ()
211 , _input_audio_buffer_list (0)
212 , _active_device_id (0)
213 , _aggregate_device_id (0)
214 , _aggregate_plugin_id (0)
216 , _capture_channels (0)
217 , _playback_channels (0)
218 , _in_process (false)
220 , _process_callback (0)
221 , _error_callback (0)
222 , _hw_changed_callback (0)
224 , _buffer_size_callback (0)
225 , _sample_rate_callback (0)
229 pthread_mutex_init (&_discovery_lock, 0);
232 CFRunLoopRef theRunLoop = NULL;
233 AudioObjectPropertyAddress property = { kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal, kAudioHardwarePropertyDevices };
234 AudioObjectSetPropertyData (kAudioObjectSystemObject, &property, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);
236 property.mSelector = kAudioHardwarePropertyDevices;
237 property.mScope = kAudioObjectPropertyScopeGlobal;
238 property.mElement = 0;
239 AudioObjectAddPropertyListener(kAudioObjectSystemObject, &property, property_callback_ptr, this);
241 AudioHardwareAddPropertyListener (kAudioHardwarePropertyDevices, hw_changed_callback_ptr, this);
245 CoreAudioPCM::~CoreAudioPCM ()
254 AudioObjectPropertyAddress prop;
255 prop.mSelector = kAudioHardwarePropertyDevices;
256 prop.mScope = kAudioObjectPropertyScopeGlobal;
258 AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &prop, &property_callback_ptr, this);
260 AudioHardwareRemovePropertyListener(kAudioHardwarePropertyDevices, hw_changed_callback_ptr);
262 free(_input_audio_buffer_list);
263 pthread_mutex_destroy (&_discovery_lock);
268 CoreAudioPCM::hw_changed_callback() {
270 printf("CoreAudio HW change..\n");
273 if (_hw_changed_callback) {
274 _hw_changed_callback(_hw_changed_arg);
280 CoreAudioPCM::available_sample_rates(uint32_t device_id, std::vector<float>& sampleRates)
286 if (device_id >= _n_devices) {
290 err = GetPropertyInfoWrapper (_device_ids[device_id], 0, false, kAudioDevicePropertyAvailableNominalSampleRates, &size);
292 if (err != kAudioHardwareNoError) {
296 uint32_t numRates = size / sizeof(AudioValueRange);
297 AudioValueRange* supportedRates = new AudioValueRange[numRates];
299 err = GetPropertyWrapper (_device_ids[device_id], 0, false, kAudioDevicePropertyAvailableNominalSampleRates, &size, supportedRates);
301 if (err != kAudioHardwareNoError) {
302 delete [] supportedRates;
306 static const float ardourRates[] = { 8000.0, 22050.0, 24000.0, 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0};
308 for(uint32_t i = 0; i < sizeof(ardourRates)/sizeof(float); ++i) {
309 for(uint32_t j = 0; j < numRates; ++j) {
310 if ((supportedRates[j].mMinimum <= ardourRates[i]) &&
311 (supportedRates[j].mMaximum >= ardourRates[i])) {
312 sampleRates.push_back (ardourRates[i]);
318 delete [] supportedRates;
323 CoreAudioPCM::available_buffer_sizes(uint32_t device_id, std::vector<uint32_t>& bufferSizes)
329 if (device_id >= _n_devices) {
333 AudioValueRange supportedRange;
334 size = sizeof (AudioValueRange);
336 err = GetPropertyWrapper (_device_ids[device_id], 0, 0, kAudioDevicePropertyBufferFrameSizeRange, &size, &supportedRange);
341 static const uint32_t ardourSizes[] = { 16, 32, 64, 128, 256, 512, 1024, 2048, 4096 };
343 for(uint32_t i = 0; i < sizeof(ardourSizes)/sizeof(uint32_t); ++i) {
344 if ((supportedRange.mMinimum <= ardourSizes[i]) &&
345 (supportedRange.mMaximum >= ardourSizes[i])) {
346 bufferSizes.push_back (ardourSizes[i]);
350 if (bufferSizes.empty()) {
351 bufferSizes.push_back ((uint32_t)supportedRange.mMinimum);
352 bufferSizes.push_back ((uint32_t)supportedRange.mMaximum);
358 CoreAudioPCM::available_channels(uint32_t device_id, bool input)
362 AudioBufferList *bufferList = NULL;
363 uint32_t channel_count = 0;
365 if (device_id >= _n_devices) {
369 /* query number of inputs */
370 err = GetPropertyInfoWrapper (_device_ids[device_id], 0, input, kAudioDevicePropertyStreamConfiguration, &size);
371 if (kAudioHardwareNoError != err) {
372 fprintf(stderr, "CoreaAudioPCM: kAudioDevicePropertyStreamConfiguration failed\n");
376 bufferList = (AudioBufferList *)(malloc(size));
378 if (!bufferList) { fprintf(stderr, "OUT OF MEMORY\n"); return 0; }
379 bufferList->mNumberBuffers = 0;
380 err = GetPropertyWrapper (_device_ids[device_id], 0, input, kAudioDevicePropertyStreamConfiguration, &size, bufferList);
382 if(kAudioHardwareNoError != err) {
383 fprintf(stderr, "CoreaAudioPCM: kAudioDevicePropertyStreamConfiguration failed\n");
388 for(UInt32 j = 0; j < bufferList->mNumberBuffers; ++j) {
389 channel_count += bufferList->mBuffers[j].mNumberChannels;
392 return channel_count;
396 CoreAudioPCM::get_stream_latencies(uint32_t device_id, bool input, std::vector<uint32_t>& latencies)
401 if (device_id >= _n_devices) {
405 err = GetPropertyInfoWrapper (_device_ids[device_id], 0, input, kAudioDevicePropertyStreams, &size);
406 if (err != noErr) { return; }
408 uint32_t stream_count = size / sizeof(UInt32);
409 AudioStreamID streamIDs[stream_count];
411 err = GetPropertyWrapper (_device_ids[device_id], 0, input, kAudioDevicePropertyStreams, &size, &streamIDs);
413 fprintf(stderr, "GetStreamLatencies kAudioDevicePropertyStreams\n");
417 for (uint32_t i = 0; i < stream_count; i++) {
418 UInt32 stream_latency;
419 size = sizeof(UInt32);
421 AudioObjectPropertyAddress property_address;
422 property_address.mSelector = kAudioDevicePropertyStreams;
423 property_address.mScope = input ? kAudioDevicePropertyScopeInput: kAudioDevicePropertyScopeOutput;
424 property_address.mElement = i; // ??
425 err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, &stream_latency);
427 err = AudioStreamGetProperty(streamIDs[i], input, kAudioStreamPropertyLatency, &size, &stream_latency);
430 fprintf(stderr, "GetStreamLatencies kAudioStreamPropertyLatency\n");
434 printf(" ^ Stream %u latency: %u\n", (unsigned int)i, (unsigned int)stream_latency);
436 latencies.push_back(stream_latency);
441 CoreAudioPCM::get_latency(uint32_t device_id, bool input)
444 uint32_t latency = 0;
445 UInt32 size = sizeof(UInt32);
449 if (device_id >= _n_devices) {
453 err = GetPropertyWrapper (_device_ids[device_id], 0, input, kAudioDevicePropertyLatency, &size, &lat0);
454 if (err != kAudioHardwareNoError) {
455 fprintf(stderr, "GetLatency kAudioDevicePropertyLatency\n");
458 err = GetPropertyWrapper (_device_ids[device_id], 0, input, kAudioDevicePropertySafetyOffset, &size, &latS);
459 if (err != kAudioHardwareNoError) {
460 fprintf(stderr, "GetLatency kAudioDevicePropertySafetyOffset\n");
464 printf("%s Latency systemic+safetyoffset = %u + %u\n",
465 input ? "Input" : "Output", (unsigned int)lat0, (unsigned int)latS);
467 latency = lat0 + latS;
469 uint32_t max_stream_latency = 0;
470 std::vector<uint32_t> stream_latencies;
471 get_stream_latencies(device_id, input, stream_latencies);
472 for (size_t i = 0; i < stream_latencies.size(); ++i) {
473 max_stream_latency = std::max(max_stream_latency, stream_latencies[i]);
475 latency += max_stream_latency;
481 CoreAudioPCM::current_buffer_size_id(AudioDeviceID id) {
483 UInt32 size = sizeof(UInt32);
485 err = GetPropertyWrapper (id, 0, 0, kAudioDevicePropertyBufferFrameSize, &size, &buffer_size);
487 return _samples_per_period;
494 CoreAudioPCM::current_sample_rate_id(AudioDeviceID id, bool input) {
498 size = sizeof (rate);
500 err = GetPropertyWrapper(id, 0, input, kAudioDevicePropertyNominalSampleRate, &size, &rate);
508 CoreAudioPCM::current_sample_rate(uint32_t device_id, bool input) {
509 if (device_id >= _n_devices) {
512 return current_sample_rate_id(_device_ids[device_id], input);
516 CoreAudioPCM::sample_rate() {
517 if (_active_device_id == 0) {
520 return current_sample_rate_id(_active_device_id, _playback_channels > 0 ? false : true);
524 CoreAudioPCM::set_device_sample_rate_id (AudioDeviceID id, float rate, bool input)
526 std::vector<int>::iterator intIter;
530 if (current_sample_rate_id(id, input) == rate) {
534 Float64 newNominalRate = rate;
535 size = sizeof (Float64);
537 err = SetPropertyWrapper(id, NULL, 0, input, kAudioDevicePropertyNominalSampleRate, size, &newNominalRate);
539 fprintf(stderr, "CoreAudioPCM: failed to set samplerate\n");
543 int timeout = 3000; // 3 sec
544 while (--timeout > 0) {
545 if (current_sample_rate_id(id, input) == rate) {
550 fprintf(stderr, "CoreAudioPCM: CoreAudio: Setting SampleRate took %d ms.\n", (3000 - timeout));
553 fprintf(stderr, "CoreAudioPCM: CoreAudio: Setting SampleRate timed out.\n");
561 CoreAudioPCM::set_device_sample_rate (uint32_t device_id, float rate, bool input)
563 return set_device_sample_rate_id(_device_ids[device_id], rate, input);
567 CoreAudioPCM::discover()
572 if (pthread_mutex_trylock (&_discovery_lock)) {
577 delete _device_ids; _device_ids = 0;
578 free(_device_ins); _device_ins = 0;
579 free(_device_outs); _device_outs = 0;
583 err = GetHardwarePropertyInfoWrapper (kAudioHardwarePropertyDevices, &size);
585 _n_devices = size / sizeof (AudioDeviceID);
586 size = _n_devices * sizeof (AudioDeviceID);
588 _device_ids = new AudioDeviceID[_n_devices];
589 _device_ins = (uint32_t*) calloc(_n_devices, sizeof(uint32_t));
590 _device_outs = (uint32_t*) calloc(_n_devices, sizeof(uint32_t));
592 assert(_device_ins && _device_outs && _device_ids);
593 if (!_device_ins || !_device_ins || !_device_ids) {
594 fprintf(stderr, "OUT OF MEMORY\n");
598 pthread_mutex_unlock (&_discovery_lock);
602 err = GetHardwarePropertyWrapper (kAudioHardwarePropertyDevices, &size, _device_ids);
604 for (size_t idx = 0; idx < _n_devices; ++idx) {
607 err = GetPropertyWrapper (_device_ids[idx], 0, 0, kAudioDevicePropertyDeviceName, &size, deviceName);
609 if (kAudioHardwareNoError != err) {
610 fprintf(stderr, "CoreAudioPCM: device name query failed\n");
614 UInt32 inputChannelCount = available_channels(idx, true);
615 UInt32 outputChannelCount = available_channels(idx, false);
618 std::string dn = deviceName;
619 _device_ins[idx] = inputChannelCount;
620 _device_outs[idx] = outputChannelCount;
622 printf("CoreAudio Device: #%ld (id:%lu) '%s' in:%u out:%u\n", idx,
623 (long unsigned int)_device_ids[idx],
625 (unsigned int)inputChannelCount, (unsigned int)outputChannelCount);
627 if (outputChannelCount > 0 || inputChannelCount > 0) {
628 _devices.insert (std::pair<size_t, std::string> (idx, dn));
632 pthread_mutex_unlock (&_discovery_lock);
636 CoreAudioPCM::xrun_callback ()
639 printf("Coreaudio XRUN\n");
641 if (_xrun_callback) {
642 _xrun_callback(_xrun_arg);
647 CoreAudioPCM::buffer_size_callback ()
649 _samples_per_period = current_buffer_size_id(_active_device_id);
651 if (_buffer_size_callback) {
652 _buffer_size_callback(_buffer_size_arg);
657 CoreAudioPCM::sample_rate_callback ()
660 printf("Sample Rate Changed!\n");
662 if (_sample_rate_callback) {
663 _sample_rate_callback(_sample_rate_arg);
668 CoreAudioPCM::pcm_stop ()
672 AudioOutputUnitStop(_auhal);
675 AudioObjectPropertyAddress prop;
676 prop.mScope = kAudioObjectPropertyScopeGlobal;
678 if (_active_device_id > 0) {
679 prop.mSelector = kAudioDeviceProcessorOverload;
680 AudioObjectRemovePropertyListener(_active_device_id, &prop, &property_callback_ptr, this);
681 prop.mSelector = kAudioDevicePropertyBufferFrameSize;
682 AudioObjectRemovePropertyListener(_active_device_id, &prop, &property_callback_ptr, this);
683 prop.mSelector = kAudioDevicePropertyNominalSampleRate;
684 AudioObjectRemovePropertyListener(_active_device_id, &prop, &property_callback_ptr, this);
687 if (_active_device_id > 0) {
688 AudioDeviceRemovePropertyListener(_active_device_id, 0, true, kAudioDeviceProcessorOverload, property_callback_ptr);
689 AudioDeviceRemovePropertyListener(_active_device_id, 0, true, kAudioDevicePropertyBufferFrameSize, property_callback_ptr);
690 AudioDeviceRemovePropertyListener(_active_device_id, 0, true, kAudioDevicePropertyNominalSampleRate, property_callback_ptr);
694 if (_aggregate_plugin_id) {
695 destroy_aggregate_device();
698 AudioUnitUninitialize(_auhal);
700 AudioComponentInstanceDispose(_auhal);
702 CloseComponent(_auhal);
706 _capture_channels = 0;
707 _playback_channels = 0;
708 _aggregate_plugin_id = 0;
709 _aggregate_device_id = 0;
710 _active_device_id = 0;
712 free(_input_audio_buffer_list);
713 _input_audio_buffer_list = 0;
715 _input_names.clear();
716 _output_names.clear();
719 _process_callback = 0;
724 static void PrintStreamDesc (AudioStreamBasicDescription *inDesc)
726 printf ("- - - - - - - - - - - - - - - - - - - -\n");
727 printf (" Sample Rate:%.2f", inDesc->mSampleRate);
728 printf (" Format ID:%.*s\n", (int)sizeof(inDesc->mFormatID), (char*)&inDesc->mFormatID);
729 printf (" Format Flags:%X\n", (unsigned int)inDesc->mFormatFlags);
730 printf (" Bytes per Packet:%d\n", (int)inDesc->mBytesPerPacket);
731 printf (" Frames per Packet:%d\n", (int)inDesc->mFramesPerPacket);
732 printf (" Bytes per Frame:%d\n", (int)inDesc->mBytesPerFrame);
733 printf (" Channels per Frame:%d\n", (int)inDesc->mChannelsPerFrame);
734 printf (" Bits per Channel:%d\n", (int)inDesc->mBitsPerChannel);
735 printf ("- - - - - - - - - - - - - - - - - - - -\n");
740 CoreAudioPCM::set_device_buffer_size_id (AudioDeviceID id, uint32_t samples_per_period)
745 uint32val = samples_per_period;
746 err = SetPropertyWrapper(id, NULL, 0, true, kAudioDevicePropertyBufferFrameSize, sizeof(UInt32), &uint32val);
747 if (err != noErr) { return -1; }
748 err = SetPropertyWrapper(id, NULL, 0, false, kAudioDevicePropertyBufferFrameSize, sizeof(UInt32), &uint32val);
749 if (err != noErr) { return -1; }
754 CoreAudioPCM::set_samples_per_period (uint32_t n_samples)
757 if (_state != 0 || _active_device_id == 0) {
760 set_device_buffer_size_id (_active_device_id, n_samples);
765 CoreAudioPCM::pcm_start (
766 uint32_t device_id_in, uint32_t device_id_out,
767 uint32_t sample_rate, uint32_t samples_per_period,
768 int (process_callback (void*, const uint32_t, const uint64_t)), void *process_arg)
772 std::string errorMsg;
775 // TODO add "none' device to force half-duplex
777 if (device_id_out >= _n_devices || device_id_in >= _n_devices) {
781 pthread_mutex_lock (&_discovery_lock);
783 _process_callback = process_callback;
784 _process_arg = process_arg;
785 _samples_per_period = samples_per_period;
786 _cur_samples_per_period = 0;
787 _active_device_id = 0;
788 _capture_channels = 0;
789 _playback_channels = 0;
791 const uint32_t chn_in = _device_ins[device_id_in] + ((device_id_out != device_id_in) ? _device_ins[device_id_out] : 0);
792 const uint32_t chn_out = _device_outs[device_id_out] + ((device_id_out != device_id_in) ? _device_outs[device_id_in] : 0);
794 assert (chn_in > 0 || chn_out > 0);
799 AudioDeviceID device_id;
800 AudioStreamBasicDescription srcFormat, dstFormat;
802 AudioComponentDescription cd = {kAudioUnitType_Output, kAudioUnitSubType_HALOutput, kAudioUnitManufacturer_Apple, 0, 0};
803 AudioComponent HALOutput = AudioComponentFindNext(NULL, &cd);
804 if (!HALOutput) { errorMsg="AudioComponentFindNext"; goto error; }
806 err = AudioComponentInstanceNew(HALOutput, &_auhal);
807 if (err != noErr) { errorMsg="AudioComponentInstanceNew"; goto error; }
809 err = AudioUnitInitialize(_auhal);
810 if (err != noErr) { errorMsg="AudioUnitInitialize"; goto error; }
812 // explicitly change samplerate of the device
813 if (set_device_sample_rate(device_id_in, sample_rate, true)) {
814 errorMsg="Failed to set SampleRate, Capture Device"; goto error;
816 if (set_device_sample_rate(device_id_out, sample_rate, false)) {
817 errorMsg="Failed to set SampleRate, Playback Device"; goto error;
820 // explicitly request device buffer size
821 if (set_device_buffer_size_id(_device_ids[device_id_in], samples_per_period)) {
822 errorMsg="kAudioDevicePropertyBufferFrameSize, Input"; goto error;
824 if (set_device_buffer_size_id(_device_ids[device_id_out], samples_per_period)) {
825 errorMsg="kAudioDevicePropertyBufferFrameSize, Output"; goto error;
828 // create aggregate device..
829 if (_device_ids[device_id_in] != _device_ids[device_id_out]) {
830 if (0 == create_aggregate_device(_device_ids[device_id_in], _device_ids[device_id_out], sample_rate, &_aggregate_device_id)) {
831 device_id = _aggregate_device_id;
833 _aggregate_device_id = 0;
834 _aggregate_plugin_id = 0;
835 errorMsg="Cannot create Aggregate Device"; goto error;
838 device_id = _device_ids[device_id_out];
841 if (device_id_out != device_id_in) {
842 assert(_aggregate_device_id > 0);
845 // enableIO to progress further
846 uint32val = (chn_in > 0) ? 1 : 0;
847 err = AudioUnitSetProperty(_auhal, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, AUHAL_INPUT_ELEMENT, &uint32val, sizeof(UInt32));
848 if (err != noErr) { errorMsg="kAudioOutputUnitProperty_EnableIO, Input"; goto error; }
850 uint32val = (chn_out > 0) ? 1 : 0;
851 err = AudioUnitSetProperty(_auhal, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, AUHAL_OUTPUT_ELEMENT, &uint32val, sizeof(UInt32));
852 if (err != noErr) { errorMsg="kAudioOutputUnitProperty_EnableIO, Output"; goto error; }
854 err = AudioUnitSetProperty(_auhal, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &device_id, sizeof(AudioDeviceID));
855 if (err != noErr) { errorMsg="kAudioOutputUnitProperty_CurrentDevice, Input"; goto error; }
859 srcFormat.mSampleRate = sample_rate;
860 srcFormat.mFormatID = kAudioFormatLinearPCM;
861 srcFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved;
862 srcFormat.mBytesPerPacket = sizeof(float);
863 srcFormat.mFramesPerPacket = 1;
864 srcFormat.mBytesPerFrame = sizeof(float);
865 srcFormat.mChannelsPerFrame = chn_in;
866 srcFormat.mBitsPerChannel = 32;
868 err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, AUHAL_INPUT_ELEMENT, &srcFormat, sizeof(AudioStreamBasicDescription));
869 if (err != noErr) { errorMsg="kAudioUnitProperty_StreamFormat, Output"; goto error; }
871 err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, AUHAL_INPUT_ELEMENT, (UInt32*)&_samples_per_period, sizeof(UInt32));
872 if (err != noErr) { errorMsg="kAudioUnitProperty_MaximumFramesPerSlice, Input"; goto error; }
876 dstFormat.mSampleRate = sample_rate;
877 dstFormat.mFormatID = kAudioFormatLinearPCM;
878 dstFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved;
879 dstFormat.mBytesPerPacket = sizeof(float);
880 dstFormat.mFramesPerPacket = 1;
881 dstFormat.mBytesPerFrame = sizeof(float);
882 dstFormat.mChannelsPerFrame = chn_out;
883 dstFormat.mBitsPerChannel = 32;
885 err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, AUHAL_OUTPUT_ELEMENT, &dstFormat, sizeof(AudioStreamBasicDescription));
886 if (err != noErr) { errorMsg="kAudioUnitProperty_StreamFormat Input"; goto error; }
888 err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, AUHAL_OUTPUT_ELEMENT, (UInt32*)&_samples_per_period, sizeof(UInt32));
889 if (err != noErr) { errorMsg="kAudioUnitProperty_MaximumFramesPerSlice, Output"; goto error; }
892 /* read back stream descriptions */
894 size = sizeof(AudioStreamBasicDescription);
895 err = AudioUnitGetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, AUHAL_INPUT_ELEMENT, &srcFormat, &size);
896 if (err != noErr) { errorMsg="Get kAudioUnitProperty_StreamFormat, Output"; goto error; }
897 _capture_channels = srcFormat.mChannelsPerFrame;
899 PrintStreamDesc(&srcFormat);
904 size = sizeof(AudioStreamBasicDescription);
905 err = AudioUnitGetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, AUHAL_OUTPUT_ELEMENT, &dstFormat, &size);
906 if (err != noErr) { errorMsg="Get kAudioUnitProperty_StreamFormat, Input"; goto error; }
907 _playback_channels = dstFormat.mChannelsPerFrame;
910 PrintStreamDesc(&dstFormat);
914 /* prepare buffers for input */
915 if (_capture_channels > 0) {
916 _input_audio_buffer_list = (AudioBufferList*)malloc(sizeof(AudioBufferList) + (_capture_channels - 1) * sizeof(AudioBuffer));
917 assert(_input_audio_buffer_list);
918 if (!_input_audio_buffer_list) { errorMsg="Out of Memory."; goto error; }
921 _active_device_id = device_id;
924 err = add_listener (_active_device_id, kAudioDeviceProcessorOverload, this);
925 if (err != noErr) { errorMsg="kAudioDeviceProcessorOverload, Listen"; goto error; }
927 err = add_listener (_active_device_id, kAudioDevicePropertyBufferFrameSize, this);
928 if (err != noErr) { errorMsg="kAudioDevicePropertyBufferFrameSize, Listen"; goto error; }
930 err = add_listener (_active_device_id, kAudioDevicePropertyNominalSampleRate, this);
931 if (err != noErr) { errorMsg="kAudioDevicePropertyBufferFrameSize, Listen"; goto error; }
933 _samples_per_period = current_buffer_size_id(_active_device_id);
936 AURenderCallbackStruct renderCallback;
937 memset (&renderCallback, 0, sizeof (renderCallback));
938 renderCallback.inputProc = render_callback_ptr;
939 renderCallback.inputProcRefCon = this;
940 if (_playback_channels == 0) {
941 err = AudioUnitSetProperty(_auhal,
942 kAudioOutputUnitProperty_SetInputCallback,
943 kAudioUnitScope_Output, 1,
944 &renderCallback, sizeof (renderCallback));
946 err = AudioUnitSetProperty(_auhal,
947 kAudioUnitProperty_SetRenderCallback,
948 kAudioUnitScope_Output, 0,
949 &renderCallback, sizeof (renderCallback));
952 if (err != noErr) { errorMsg="kAudioUnitProperty_SetRenderCallback"; goto error; }
954 /* setup complete, now get going.. */
955 if (AudioOutputUnitStart(_auhal) == noErr) {
956 _input_names.clear();
957 _output_names.clear();
958 cache_port_names (device_id, true);
959 cache_port_names (device_id, false);
961 pthread_mutex_unlock (&_discovery_lock);
964 if (set_device_buffer_size_id(_active_device_id, samples_per_period)) {
965 errorMsg="kAudioDevicePropertyBufferFrameSize"; goto error;
972 char *rv = (char*)&err;
973 fprintf(stderr, "CoreaudioPCM Error: %c%c%c%c %s\n", rv[0], rv[1], rv[2], rv[3], errorMsg.c_str());
976 _active_device_id = 0;
977 pthread_mutex_unlock (&_discovery_lock);
982 CoreAudioPCM::cache_port_names(AudioDeviceID id, bool input)
987 n_chn = _capture_channels;
989 n_chn = _playback_channels;;
992 AudioObjectPropertyAddress property_address;
993 property_address.mSelector = kAudioObjectPropertyElementName;
994 property_address.mScope = input ? kAudioDevicePropertyScopeInput: kAudioDevicePropertyScopeOutput;
997 for (uint32_t c = 0; c < n_chn; ++c) {
998 CFStringRef name = NULL;
999 std::stringstream ss;
1003 #ifdef COREAUDIO_108
1004 property_address.mElement = c + 1;
1005 err = AudioObjectGetPropertyDataSize(id, &property_address, 0, NULL, &size);
1007 err = AudioDeviceGetPropertyInfo (id, c + 1, input,
1008 kAudioDevicePropertyChannelNameCFString,
1013 if (err == kAudioHardwareNoError) {
1014 #ifdef COREAUDIO_108
1015 err = AudioObjectGetPropertyData(id, &property_address, c + 1, NULL, &size, &name);
1017 err = AudioDeviceGetProperty (id, c + 1, input,
1018 kAudioDevicePropertyChannelNameCFString,
1024 bool decoded = false;
1025 char* cstr_name = 0;
1026 if (err == kAudioHardwareNoError) {
1027 CFIndex length = CFStringGetLength(name);
1028 CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8);
1029 cstr_name = new char[maxSize];
1030 decoded = CFStringGetCString(name, cstr_name, maxSize, kCFStringEncodingUTF8);
1035 if (cstr_name && decoded && (0 != std::strlen(cstr_name) ) ) {
1036 ss << " - " << cstr_name;
1039 printf("%s %d Name: %s\n", input ? "Input" : "Output", c+1, ss.str().c_str());
1043 _input_names.push_back (ss.str());
1045 _output_names.push_back (ss.str());
1051 delete [] cstr_name;
1056 CoreAudioPCM::cached_port_name(uint32_t port, bool input) const
1058 if (_state != 0) { return ""; }
1061 if (port >= _input_names.size()) {
1064 return _input_names[port];
1066 if (port >= _output_names.size()) {
1069 return _output_names[port];
1075 CoreAudioPCM::render_callback (
1076 AudioUnitRenderActionFlags* ioActionFlags,
1077 const AudioTimeStamp* inTimeStamp,
1079 UInt32 inNumberFrames,
1080 AudioBufferList* ioData)
1082 OSStatus retVal = kAudioHardwareNoError;
1084 if (_samples_per_period < inNumberFrames) {
1086 printf("samples per period exceeds configured value, cycle skipped (%u < %u)\n",
1087 (unsigned int)_samples_per_period, (unsigned int)inNumberFrames);
1089 for (uint32_t i = 0; _playback_channels > 0 && i < ioData->mNumberBuffers; ++i) {
1090 float* ob = (float*) ioData->mBuffers[i].mData;
1091 memset(ob, 0, sizeof(float) * inNumberFrames);
1096 assert(_playback_channels == 0 || ioData->mNumberBuffers == _playback_channels);
1098 UInt64 cur_cycle_start = AudioGetCurrentHostTime ();
1099 _cur_samples_per_period = inNumberFrames;
1101 if (_capture_channels > 0) {
1102 _input_audio_buffer_list->mNumberBuffers = _capture_channels;
1103 for (uint32_t i = 0; i < _capture_channels; ++i) {
1104 _input_audio_buffer_list->mBuffers[i].mNumberChannels = 1;
1105 _input_audio_buffer_list->mBuffers[i].mDataByteSize = inNumberFrames * sizeof(float);
1106 _input_audio_buffer_list->mBuffers[i].mData = NULL;
1109 retVal = AudioUnitRender(_auhal, ioActionFlags, inTimeStamp, AUHAL_INPUT_ELEMENT, inNumberFrames, _input_audio_buffer_list);
1112 if (retVal != kAudioHardwareNoError) {
1114 char *rv = (char*)&retVal;
1115 printf("ERR %c%c%c%c\n", rv[0], rv[1], rv[2], rv[3]);
1117 if (_error_callback) {
1118 _error_callback(_error_arg);
1123 _output_audio_buffer_list = ioData;
1129 if (_process_callback) {
1130 rv = _process_callback(_process_arg, inNumberFrames, cur_cycle_start);
1133 _in_process = false;
1135 if (rv != 0 && _playback_channels > 0) {
1137 for (uint32_t i = 0; i < ioData->mNumberBuffers; ++i) {
1138 float* ob = (float*) ioData->mBuffers[i].mData;
1139 memset(ob, 0, sizeof(float) * inNumberFrames);
1146 CoreAudioPCM::get_capture_channel (uint32_t chn, float *input, uint32_t n_samples)
1148 if (!_in_process || chn > _capture_channels || n_samples > _cur_samples_per_period) {
1151 assert(_input_audio_buffer_list->mNumberBuffers > chn);
1152 memcpy((void*)input, (void*)_input_audio_buffer_list->mBuffers[chn].mData, sizeof(float) * n_samples);
1157 CoreAudioPCM::set_playback_channel (uint32_t chn, const float *output, uint32_t n_samples)
1159 if (!_in_process || chn > _playback_channels || n_samples > _cur_samples_per_period) {
1163 assert(_output_audio_buffer_list && _output_audio_buffer_list->mNumberBuffers > chn);
1164 memcpy((void*)_output_audio_buffer_list->mBuffers[chn].mData, (void*)output, sizeof(float) * n_samples);
1170 CoreAudioPCM::launch_control_app (uint32_t device_id)
1172 if (device_id >= _n_devices) {
1176 CFStringRef config_app = NULL;
1177 UInt32 size = sizeof (config_app);
1180 err = GetPropertyWrapper(_device_ids[device_id], 0, false, kAudioDevicePropertyConfigurationApplication, &size, &config_app);
1181 if (kAudioHardwareNoError != err) {
1186 if (noErr == LSFindApplicationForInfo(kLSUnknownCreator, config_app, NULL, &appFSRef, NULL)) {
1187 LSOpenFSRef(&appFSRef, NULL);
1189 // open default AudioMIDISetup if device app is not found
1190 CFStringRef audioMidiSetup = CFStringCreateWithCString(kCFAllocatorDefault, "com.apple.audio.AudioMIDISetup", kCFStringEncodingMacRoman);
1191 if (noErr == LSFindApplicationForInfo(kLSUnknownCreator, audioMidiSetup, NULL, &appFSRef, NULL)) {
1192 LSOpenFSRef(&appFSRef, NULL);
1196 CFRelease (config_app);