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 using namespace ARDOUR;
24 /* abstraction for deprecated CoreAudio */
26 static OSStatus GetPropertyWrapper (
27 AudioDeviceID id, UInt32 elem, bool input, AudioDevicePropertyID prop, UInt32* size, void * data)
30 AudioObjectPropertyAddress property_address;
31 property_address.mSelector = prop;
33 case kAudioDevicePropertyBufferFrameSize:
34 case kAudioDevicePropertyBufferFrameSizeRange:
35 property_address.mScope = kAudioObjectPropertyScopeGlobal;
38 property_address.mScope = input ? kAudioDevicePropertyScopeInput: kAudioDevicePropertyScopeOutput;
41 property_address.mElement = kAudioObjectPropertyElementMaster;
42 return AudioObjectGetPropertyData(id, &property_address, elem, NULL, size, data);
44 return AudioDeviceGetProperty(id, elem, input, prop, size, data);
48 static OSStatus SetPropertyWrapper (
49 AudioDeviceID id, const AudioTimeStamp* when, UInt32 chn, bool input, AudioDevicePropertyID prop, UInt32 size, void * data)
52 AudioObjectPropertyAddress property_address;
53 property_address.mSelector = prop;
54 property_address.mScope = input ? kAudioDevicePropertyScopeInput: kAudioDevicePropertyScopeOutput;
55 property_address.mElement = kAudioObjectPropertyElementMaster;
56 return AudioObjectSetPropertyData (id, &property_address, 0, NULL, size, data);
58 return AudioDeviceSetProperty (id, when, chn, input, prop, size, data);
62 static OSStatus GetHardwarePropertyInfoWrapper (AudioDevicePropertyID prop, UInt32* size)
65 AudioObjectPropertyAddress property_address;
66 property_address.mSelector = prop;
67 property_address.mScope = kAudioObjectPropertyScopeGlobal;
68 property_address.mElement = kAudioObjectPropertyElementMaster;
69 return AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &property_address, 0, NULL, size);
72 return AudioHardwareGetPropertyInfo(prop, size, &outWritable);
76 static OSStatus GetHardwarePropertyWrapper (AudioDevicePropertyID prop, UInt32* size, void *d)
79 AudioObjectPropertyAddress property_address;
80 property_address.mSelector = prop;
81 property_address.mScope = kAudioObjectPropertyScopeGlobal;
82 property_address.mElement = kAudioObjectPropertyElementMaster;
83 return AudioObjectGetPropertyData(kAudioObjectSystemObject, &property_address, 0, NULL, size, d);
85 return AudioHardwareGetProperty (prop, size, d);
89 static OSStatus GetPropertyInfoWrapper (AudioDeviceID id, UInt32 elem, bool input, AudioDevicePropertyID prop, UInt32* size)
92 AudioObjectPropertyAddress property_address;
93 property_address.mSelector = prop;
94 property_address.mScope = input ? kAudioDevicePropertyScopeInput: kAudioDevicePropertyScopeOutput;
95 property_address.mElement = elem;
96 return AudioObjectGetPropertyDataSize(id, &property_address, 0, NULL, size);
99 return AudioDeviceGetPropertyInfo(id, elem, input, prop, size, &outWritable);
103 static OSStatus GetDeviceNameFromID(AudioDeviceID id, char* name)
106 return GetPropertyWrapper (id, 0, 0, kAudioDevicePropertyDeviceName, &size, name);
109 static CFStringRef GetDeviceName(AudioDeviceID id)
111 UInt32 size = sizeof(CFStringRef);
113 OSStatus err = GetPropertyWrapper (id, 0, 0, kAudioDevicePropertyDeviceUID, &size, &UIname);
114 return (err == noErr) ? UIname : NULL;
117 ///////////////////////////////////////////////////////////////////////////////
119 #include "coreaudio_pcmio_aggregate.cc"
125 static OSStatus property_callback_ptr (AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void* arg) {
126 CoreAudioPCM * self = static_cast<CoreAudioPCM*>(arg);
127 for (UInt32 i = 0; i < inNumberAddresses; ++i) {
128 switch (inAddresses[i].mSelector) {
129 case kAudioHardwarePropertyDevices:
130 self->hw_changed_callback();
132 case kAudioDeviceProcessorOverload:
133 self->xrun_callback();
135 case kAudioDevicePropertyBufferFrameSize:
136 self->buffer_size_callback();
138 case kAudioDevicePropertyNominalSampleRate:
139 self->sample_rate_callback();
150 static OSStatus hw_changed_callback_ptr (AudioHardwarePropertyID inPropertyID, void* arg) {
151 if (inPropertyID == kAudioHardwarePropertyDevices) {
152 CoreAudioPCM * self = static_cast<CoreAudioPCM*>(arg);
153 self->hw_changed_callback();
158 static OSStatus property_callback_ptr (
159 AudioDeviceID inDevice,
162 AudioDevicePropertyID inPropertyID,
165 CoreAudioPCM * d = static_cast<CoreAudioPCM*> (inClientData);
166 switch (inPropertyID) {
167 case kAudioDeviceProcessorOverload:
170 case kAudioDevicePropertyBufferFrameSize:
171 d->buffer_size_callback();
173 case kAudioDevicePropertyNominalSampleRate:
174 d->sample_rate_callback();
182 static OSStatus render_callback_ptr (
184 AudioUnitRenderActionFlags* ioActionFlags,
185 const AudioTimeStamp* inTimeStamp,
187 UInt32 inNumberFrames,
188 AudioBufferList* ioData)
190 CoreAudioPCM * d = static_cast<CoreAudioPCM*> (inRefCon);
191 return d->render_callback(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
195 static OSStatus add_listener (AudioDeviceID id, AudioDevicePropertyID selector, void *arg) {
197 AudioObjectPropertyAddress property_address;
198 property_address.mSelector = selector;
199 property_address.mScope = kAudioObjectPropertyScopeGlobal;
200 property_address.mElement = 0;
201 return AudioObjectAddPropertyListener(id, &property_address, property_callback_ptr, arg);
203 return AudioDeviceAddPropertyListener(id, 0, true, selector, property_callback_ptr, arg);
208 ///////////////////////////////////////////////////////////////////////////////
210 CoreAudioPCM::CoreAudioPCM ()
213 , _input_audio_buffer_list (0)
214 , _active_device_id (0)
215 , _aggregate_device_id (0)
216 , _aggregate_plugin_id (0)
218 , _capture_channels (0)
219 , _playback_channels (0)
220 , _in_process (false)
222 , _process_callback (0)
223 , _error_callback (0)
224 , _hw_changed_callback (0)
226 , _buffer_size_callback (0)
227 , _sample_rate_callback (0)
231 pthread_mutex_init (&_discovery_lock, 0);
234 CFRunLoopRef theRunLoop = NULL;
235 AudioObjectPropertyAddress property = { kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal, kAudioHardwarePropertyDevices };
236 AudioObjectSetPropertyData (kAudioObjectSystemObject, &property, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);
238 property.mSelector = kAudioHardwarePropertyDevices;
239 property.mScope = kAudioObjectPropertyScopeGlobal;
240 property.mElement = 0;
241 AudioObjectAddPropertyListener(kAudioObjectSystemObject, &property, property_callback_ptr, this);
243 AudioHardwareAddPropertyListener (kAudioHardwarePropertyDevices, hw_changed_callback_ptr, this);
247 CoreAudioPCM::~CoreAudioPCM ()
256 AudioObjectPropertyAddress prop;
257 prop.mSelector = kAudioHardwarePropertyDevices;
258 prop.mScope = kAudioObjectPropertyScopeGlobal;
260 AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &prop, &property_callback_ptr, this);
262 AudioHardwareRemovePropertyListener(kAudioHardwarePropertyDevices, hw_changed_callback_ptr);
264 free(_input_audio_buffer_list);
265 pthread_mutex_destroy (&_discovery_lock);
270 CoreAudioPCM::hw_changed_callback() {
272 printf("CoreAudio HW change..\n");
275 if (_hw_changed_callback) {
276 _hw_changed_callback(_hw_changed_arg);
282 CoreAudioPCM::available_sample_rates(uint32_t device_id, std::vector<float>& sampleRates)
288 if (device_id >= _n_devices) {
292 err = GetPropertyInfoWrapper (_device_ids[device_id], 0, false, kAudioDevicePropertyAvailableNominalSampleRates, &size);
294 if (err != kAudioHardwareNoError) {
298 uint32_t numRates = size / sizeof(AudioValueRange);
299 AudioValueRange* supportedRates = new AudioValueRange[numRates];
301 err = GetPropertyWrapper (_device_ids[device_id], 0, false, kAudioDevicePropertyAvailableNominalSampleRates, &size, supportedRates);
303 if (err != kAudioHardwareNoError) {
304 delete [] supportedRates;
308 static const float ardourRates[] = { 8000.0, 22050.0, 24000.0, 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0};
310 for(uint32_t i = 0; i < sizeof(ardourRates)/sizeof(float); ++i) {
311 for(uint32_t j = 0; j < numRates; ++j) {
312 if ((supportedRates[j].mMinimum <= ardourRates[i]) &&
313 (supportedRates[j].mMaximum >= ardourRates[i])) {
314 sampleRates.push_back (ardourRates[i]);
320 delete [] supportedRates;
325 CoreAudioPCM::available_buffer_sizes(uint32_t device_id, std::vector<uint32_t>& bufferSizes)
331 if (device_id >= _n_devices) {
335 AudioValueRange supportedRange;
336 size = sizeof (AudioValueRange);
338 err = GetPropertyWrapper (_device_ids[device_id], 0, 0, kAudioDevicePropertyBufferFrameSizeRange, &size, &supportedRange);
343 static const uint32_t ardourSizes[] = { 16, 32, 64, 128, 256, 512, 1024, 2048, 4096 };
345 for(uint32_t i = 0; i < sizeof(ardourSizes)/sizeof(uint32_t); ++i) {
346 if ((supportedRange.mMinimum <= ardourSizes[i]) &&
347 (supportedRange.mMaximum >= ardourSizes[i])) {
348 bufferSizes.push_back (ardourSizes[i]);
352 if (bufferSizes.empty()) {
353 bufferSizes.push_back ((uint32_t)supportedRange.mMinimum);
354 bufferSizes.push_back ((uint32_t)supportedRange.mMaximum);
360 CoreAudioPCM::available_channels(uint32_t device_id, bool input)
364 AudioBufferList *bufferList = NULL;
365 uint32_t channel_count = 0;
367 if (device_id >= _n_devices) {
371 /* query number of inputs */
372 err = GetPropertyInfoWrapper (_device_ids[device_id], 0, input, kAudioDevicePropertyStreamConfiguration, &size);
373 if (kAudioHardwareNoError != err) {
374 fprintf(stderr, "CoreaAudioPCM: kAudioDevicePropertyStreamConfiguration failed\n");
378 bufferList = (AudioBufferList *)(malloc(size));
380 if (!bufferList) { fprintf(stderr, "OUT OF MEMORY\n"); return 0; }
381 bufferList->mNumberBuffers = 0;
382 err = GetPropertyWrapper (_device_ids[device_id], 0, input, kAudioDevicePropertyStreamConfiguration, &size, bufferList);
384 if(kAudioHardwareNoError != err) {
385 fprintf(stderr, "CoreaAudioPCM: kAudioDevicePropertyStreamConfiguration failed\n");
390 for(UInt32 j = 0; j < bufferList->mNumberBuffers; ++j) {
391 channel_count += bufferList->mBuffers[j].mNumberChannels;
394 return channel_count;
398 CoreAudioPCM::get_stream_latencies(uint32_t device_id, bool input, std::vector<uint32_t>& latencies)
403 if (device_id >= _n_devices) {
407 err = GetPropertyInfoWrapper (_device_ids[device_id], 0, input, kAudioDevicePropertyStreams, &size);
408 if (err != noErr) { return; }
410 uint32_t stream_count = size / sizeof(UInt32);
411 AudioStreamID streamIDs[stream_count];
413 err = GetPropertyWrapper (_device_ids[device_id], 0, input, kAudioDevicePropertyStreams, &size, &streamIDs);
415 fprintf(stderr, "GetStreamLatencies kAudioDevicePropertyStreams\n");
419 for (uint32_t i = 0; i < stream_count; i++) {
420 UInt32 stream_latency;
421 size = sizeof(UInt32);
423 AudioObjectPropertyAddress property_address;
424 property_address.mSelector = kAudioDevicePropertyStreams;
425 property_address.mScope = input ? kAudioDevicePropertyScopeInput: kAudioDevicePropertyScopeOutput;
426 property_address.mElement = i; // ??
427 err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, &stream_latency);
429 err = AudioStreamGetProperty(streamIDs[i], input, kAudioStreamPropertyLatency, &size, &stream_latency);
432 fprintf(stderr, "GetStreamLatencies kAudioStreamPropertyLatency\n");
436 printf(" ^ Stream %u latency: %u\n", (unsigned int)i, (unsigned int)stream_latency);
438 latencies.push_back(stream_latency);
443 CoreAudioPCM::get_latency(uint32_t device_id, bool input)
446 uint32_t latency = 0;
447 UInt32 size = sizeof(UInt32);
451 if (device_id >= _n_devices) {
455 err = GetPropertyWrapper (_device_ids[device_id], 0, input, kAudioDevicePropertyLatency, &size, &lat0);
456 if (err != kAudioHardwareNoError) {
457 fprintf(stderr, "GetLatency kAudioDevicePropertyLatency\n");
460 err = GetPropertyWrapper (_device_ids[device_id], 0, input, kAudioDevicePropertySafetyOffset, &size, &latS);
461 if (err != kAudioHardwareNoError) {
462 fprintf(stderr, "GetLatency kAudioDevicePropertySafetyOffset\n");
466 printf("%s Latency systemic+safetyoffset = %u + %u\n",
467 input ? "Input" : "Output", (unsigned int)lat0, (unsigned int)latS);
469 latency = lat0 + latS;
471 uint32_t max_stream_latency = 0;
472 std::vector<uint32_t> stream_latencies;
473 get_stream_latencies(device_id, input, stream_latencies);
474 for (size_t i = 0; i < stream_latencies.size(); ++i) {
475 max_stream_latency = std::max(max_stream_latency, stream_latencies[i]);
477 latency += max_stream_latency;
483 CoreAudioPCM::get_latency(bool input)
485 if (_active_device_id == 0) {
488 return get_latency (_active_device_id, input);
492 CoreAudioPCM::current_buffer_size_id(AudioDeviceID id) {
494 UInt32 size = sizeof(UInt32);
496 err = GetPropertyWrapper (id, 0, 0, kAudioDevicePropertyBufferFrameSize, &size, &buffer_size);
498 return _samples_per_period;
505 CoreAudioPCM::current_sample_rate_id(AudioDeviceID id, bool input) {
509 size = sizeof (rate);
511 err = GetPropertyWrapper(id, 0, input, kAudioDevicePropertyNominalSampleRate, &size, &rate);
519 CoreAudioPCM::current_sample_rate(uint32_t device_id, bool input) {
520 if (device_id >= _n_devices) {
523 return current_sample_rate_id(_device_ids[device_id], input);
527 CoreAudioPCM::sample_rate() {
528 if (_active_device_id == 0) {
531 return current_sample_rate_id(_active_device_id, _playback_channels > 0 ? false : true);
535 CoreAudioPCM::set_device_sample_rate_id (AudioDeviceID id, float rate, bool input)
537 std::vector<int>::iterator intIter;
541 if (current_sample_rate_id(id, input) == rate) {
545 Float64 newNominalRate = rate;
546 size = sizeof (Float64);
548 err = SetPropertyWrapper(id, NULL, 0, input, kAudioDevicePropertyNominalSampleRate, size, &newNominalRate);
550 fprintf(stderr, "CoreAudioPCM: failed to set samplerate\n");
554 int timeout = 3000; // 3 sec
555 while (--timeout > 0) {
556 if (current_sample_rate_id(id, input) == rate) {
561 fprintf(stderr, "CoreAudioPCM: CoreAudio: Setting SampleRate took %d ms.\n", (3000 - timeout));
564 fprintf(stderr, "CoreAudioPCM: CoreAudio: Setting SampleRate timed out.\n");
572 CoreAudioPCM::set_device_sample_rate (uint32_t device_id, float rate, bool input)
574 if (device_id >= _n_devices) {
577 return set_device_sample_rate_id(_device_ids[device_id], rate, input);
581 CoreAudioPCM::discover()
586 if (pthread_mutex_trylock (&_discovery_lock)) {
591 delete _device_ids; _device_ids = 0;
592 free(_device_ins); _device_ins = 0;
593 free(_device_outs); _device_outs = 0;
597 err = GetHardwarePropertyInfoWrapper (kAudioHardwarePropertyDevices, &size);
599 _n_devices = size / sizeof (AudioDeviceID);
600 size = _n_devices * sizeof (AudioDeviceID);
602 _device_ids = new AudioDeviceID[_n_devices];
603 _device_ins = (uint32_t*) calloc(_n_devices, sizeof(uint32_t));
604 _device_outs = (uint32_t*) calloc(_n_devices, sizeof(uint32_t));
606 assert(_device_ins && _device_outs && _device_ids);
607 if (!_device_ins || !_device_ins || !_device_ids) {
608 fprintf(stderr, "OUT OF MEMORY\n");
612 pthread_mutex_unlock (&_discovery_lock);
616 err = GetHardwarePropertyWrapper (kAudioHardwarePropertyDevices, &size, _device_ids);
618 for (size_t idx = 0; idx < _n_devices; ++idx) {
621 err = GetPropertyWrapper (_device_ids[idx], 0, 0, kAudioDevicePropertyDeviceName, &size, deviceName);
623 if (kAudioHardwareNoError != err) {
624 fprintf(stderr, "CoreAudioPCM: device name query failed\n");
628 UInt32 inputChannelCount = available_channels(idx, true);
629 UInt32 outputChannelCount = available_channels(idx, false);
632 std::string dn = deviceName;
633 _device_ins[idx] = inputChannelCount;
634 _device_outs[idx] = outputChannelCount;
636 printf("CoreAudio Device: #%ld (id:%lu) '%s' in:%u out:%u\n", idx,
637 (long unsigned int)_device_ids[idx],
639 (unsigned int)inputChannelCount, (unsigned int)outputChannelCount);
641 if (outputChannelCount > 0 || inputChannelCount > 0) {
642 _devices.insert (std::pair<size_t, std::string> (idx, dn));
644 if (inputChannelCount > 0) {
645 _input_devices.insert (std::pair<size_t, std::string> (idx, dn));
647 if (outputChannelCount > 0) {
648 _output_devices.insert (std::pair<size_t, std::string> (idx, dn));
650 if (outputChannelCount > 0 && inputChannelCount > 0) {
651 _duplex_devices.insert (std::pair<size_t, std::string> (idx, dn));
655 pthread_mutex_unlock (&_discovery_lock);
659 CoreAudioPCM::xrun_callback ()
662 printf("Coreaudio XRUN\n");
664 if (_xrun_callback) {
665 _xrun_callback(_xrun_arg);
670 CoreAudioPCM::buffer_size_callback ()
672 _samples_per_period = current_buffer_size_id(_active_device_id);
674 if (_buffer_size_callback) {
675 _buffer_size_callback(_buffer_size_arg);
680 CoreAudioPCM::sample_rate_callback ()
683 printf("Sample Rate Changed!\n");
685 if (_sample_rate_callback) {
686 _sample_rate_callback(_sample_rate_arg);
691 CoreAudioPCM::pcm_stop ()
695 AudioOutputUnitStop(_auhal);
698 AudioObjectPropertyAddress prop;
699 prop.mScope = kAudioObjectPropertyScopeGlobal;
701 if (_active_device_id > 0) {
702 prop.mSelector = kAudioDeviceProcessorOverload;
703 AudioObjectRemovePropertyListener(_active_device_id, &prop, &property_callback_ptr, this);
704 prop.mSelector = kAudioDevicePropertyBufferFrameSize;
705 AudioObjectRemovePropertyListener(_active_device_id, &prop, &property_callback_ptr, this);
706 prop.mSelector = kAudioDevicePropertyNominalSampleRate;
707 AudioObjectRemovePropertyListener(_active_device_id, &prop, &property_callback_ptr, this);
710 if (_active_device_id > 0) {
711 AudioDeviceRemovePropertyListener(_active_device_id, 0, true, kAudioDeviceProcessorOverload, property_callback_ptr);
712 AudioDeviceRemovePropertyListener(_active_device_id, 0, true, kAudioDevicePropertyBufferFrameSize, property_callback_ptr);
713 AudioDeviceRemovePropertyListener(_active_device_id, 0, true, kAudioDevicePropertyNominalSampleRate, property_callback_ptr);
717 if (_aggregate_plugin_id) {
718 destroy_aggregate_device();
722 AudioUnitUninitialize(_auhal);
724 AudioComponentInstanceDispose(_auhal);
726 CloseComponent(_auhal);
730 _capture_channels = 0;
731 _playback_channels = 0;
732 _aggregate_plugin_id = 0;
733 _aggregate_device_id = 0;
734 _active_device_id = 0;
736 free(_input_audio_buffer_list);
737 _input_audio_buffer_list = 0;
739 _input_names.clear();
740 _output_names.clear();
743 _process_callback = 0;
748 static void PrintStreamDesc (AudioStreamBasicDescription *inDesc)
750 printf ("- - - - - - - - - - - - - - - - - - - -\n");
751 printf (" Sample Rate:%.2f", inDesc->mSampleRate);
752 printf (" Format ID:%.*s\n", (int)sizeof(inDesc->mFormatID), (char*)&inDesc->mFormatID);
753 printf (" Format Flags:%X\n", (unsigned int)inDesc->mFormatFlags);
754 printf (" Bytes per Packet:%d\n", (int)inDesc->mBytesPerPacket);
755 printf (" Frames per Packet:%d\n", (int)inDesc->mFramesPerPacket);
756 printf (" Bytes per Frame:%d\n", (int)inDesc->mBytesPerFrame);
757 printf (" Channels per Frame:%d\n", (int)inDesc->mChannelsPerFrame);
758 printf (" Bits per Channel:%d\n", (int)inDesc->mBitsPerChannel);
759 printf ("- - - - - - - - - - - - - - - - - - - -\n");
764 CoreAudioPCM::set_device_buffer_size_id (AudioDeviceID id, uint32_t samples_per_period)
769 uint32val = samples_per_period;
770 err = SetPropertyWrapper(id, NULL, 0, true, kAudioDevicePropertyBufferFrameSize, sizeof(UInt32), &uint32val);
771 if (err != noErr) { return -1; }
772 err = SetPropertyWrapper(id, NULL, 0, false, kAudioDevicePropertyBufferFrameSize, sizeof(UInt32), &uint32val);
773 if (err != noErr) { return -1; }
778 CoreAudioPCM::set_samples_per_period (uint32_t n_samples)
781 if (_state != 0 || _active_device_id == 0) {
784 set_device_buffer_size_id (_active_device_id, n_samples);
789 CoreAudioPCM::pcm_start (
790 uint32_t device_id_in, uint32_t device_id_out,
791 uint32_t sample_rate, uint32_t samples_per_period,
792 int (process_callback (void*, const uint32_t, const uint64_t)), void *process_arg)
796 std::string errorMsg;
799 // "None" = UINT32_MAX
800 if (device_id_out >= _n_devices && device_id_in >= _n_devices) {
804 pthread_mutex_lock (&_discovery_lock);
806 _process_callback = process_callback;
807 _process_arg = process_arg;
808 _samples_per_period = samples_per_period;
809 _cur_samples_per_period = 0;
810 _active_device_id = 0;
811 _capture_channels = 0;
812 _playback_channels = 0;
814 const uint32_t chn_in = (device_id_in < _n_devices ? _device_ins[device_id_in] : 0) + ((device_id_out != device_id_in && device_id_out < _n_devices) ? _device_ins[device_id_out] : 0);
815 const uint32_t chn_out =(device_id_out < _n_devices ? _device_outs[device_id_out] : 0) + ((device_id_out != device_id_in && device_id_in < _n_devices) ? _device_outs[device_id_in] : 0);
817 assert (chn_in > 0 || chn_out > 0);
822 AudioDeviceID device_id;
823 AudioStreamBasicDescription srcFormat, dstFormat;
825 #ifndef COREAUDIO_108
826 ComponentDescription cd = {kAudioUnitType_Output, kAudioUnitSubType_HALOutput, kAudioUnitManufacturer_Apple, 0, 0};
827 Component HALOutput = FindNextComponent(NULL, &cd);
828 if (!HALOutput) { errorMsg="FindNextComponent"; _state = -2; goto error; }
830 err = OpenAComponent(HALOutput, &_auhal);
831 if (err != noErr) { errorMsg="OpenAComponent"; _state = -2; goto error; }
833 AudioComponentDescription cd = {kAudioUnitType_Output, kAudioUnitSubType_HALOutput, kAudioUnitManufacturer_Apple, 0, 0};
834 AudioComponent HALOutput = AudioComponentFindNext(NULL, &cd);
835 if (!HALOutput) { errorMsg="AudioComponentFindNext"; _state = -2; goto error; }
837 err = AudioComponentInstanceNew(HALOutput, &_auhal);
838 if (err != noErr) { errorMsg="AudioComponentInstanceNew"; _state = -2; goto error; }
841 err = AudioUnitInitialize(_auhal);
842 if (err != noErr) { errorMsg="AudioUnitInitialize"; _state = -3; goto error; }
844 // explicitly change samplerate of the devices, TODO allow separate rates with aggregates
845 if (set_device_sample_rate(device_id_in, sample_rate, true)) {
846 errorMsg="Failed to set SampleRate, Capture Device"; _state = -4; goto error;
848 if (set_device_sample_rate(device_id_out, sample_rate, false)) {
849 errorMsg="Failed to set SampleRate, Playback Device"; _state = -4; goto error;
852 // explicitly request device buffer size
853 if (device_id_in < _n_devices && set_device_buffer_size_id(_device_ids[device_id_in], samples_per_period)) {
854 errorMsg="kAudioDevicePropertyBufferFrameSize, Input"; _state = -5; goto error;
856 if (device_id_out < _n_devices && set_device_buffer_size_id(_device_ids[device_id_out], samples_per_period)) {
857 errorMsg="kAudioDevicePropertyBufferFrameSize, Output"; _state = -5; goto error;
860 // create aggregate device..
861 if (device_id_in < _n_devices && device_id_out < _n_devices && _device_ids[device_id_in] != _device_ids[device_id_out]) {
862 if (0 == create_aggregate_device(_device_ids[device_id_in], _device_ids[device_id_out], sample_rate, &_aggregate_device_id)) {
863 device_id = _aggregate_device_id;
865 _aggregate_device_id = 0;
866 _aggregate_plugin_id = 0;
867 errorMsg="Cannot create Aggregate Device"; _state = -12; goto error;
869 } else if (device_id_out < _n_devices) {
870 device_id = _device_ids[device_id_out];
872 assert (device_id_in < _n_devices);
873 device_id = _device_ids[device_id_in];
876 if (device_id_out != device_id_in) {
877 assert(_aggregate_device_id > 0 || device_id_in >= _n_devices || device_id_out >= _n_devices);
880 // enableIO to progress further
881 uint32val = (chn_in > 0) ? 1 : 0;
882 err = AudioUnitSetProperty(_auhal, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, AUHAL_INPUT_ELEMENT, &uint32val, sizeof(UInt32));
883 if (err != noErr) { errorMsg="kAudioOutputUnitProperty_EnableIO, Input"; _state = -7; goto error; }
885 uint32val = (chn_out > 0) ? 1 : 0;
886 err = AudioUnitSetProperty(_auhal, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, AUHAL_OUTPUT_ELEMENT, &uint32val, sizeof(UInt32));
887 if (err != noErr) { errorMsg="kAudioOutputUnitProperty_EnableIO, Output"; _state = -7; goto error; }
889 err = AudioUnitSetProperty(_auhal, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &device_id, sizeof(AudioDeviceID));
890 if (err != noErr) { errorMsg="kAudioOutputUnitProperty_CurrentDevice, Input"; _state = -7; goto error; }
894 srcFormat.mSampleRate = sample_rate;
895 srcFormat.mFormatID = kAudioFormatLinearPCM;
896 srcFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved;
897 srcFormat.mBytesPerPacket = sizeof(float);
898 srcFormat.mFramesPerPacket = 1;
899 srcFormat.mBytesPerFrame = sizeof(float);
900 srcFormat.mChannelsPerFrame = chn_in;
901 srcFormat.mBitsPerChannel = 32;
903 err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, AUHAL_INPUT_ELEMENT, &srcFormat, sizeof(AudioStreamBasicDescription));
904 if (err != noErr) { errorMsg="kAudioUnitProperty_StreamFormat, Output"; _state = -6; goto error; }
906 err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, AUHAL_INPUT_ELEMENT, (UInt32*)&_samples_per_period, sizeof(UInt32));
907 if (err != noErr) { errorMsg="kAudioUnitProperty_MaximumFramesPerSlice, Input"; _state = -6; goto error; }
911 dstFormat.mSampleRate = sample_rate;
912 dstFormat.mFormatID = kAudioFormatLinearPCM;
913 dstFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved;
914 dstFormat.mBytesPerPacket = sizeof(float);
915 dstFormat.mFramesPerPacket = 1;
916 dstFormat.mBytesPerFrame = sizeof(float);
917 dstFormat.mChannelsPerFrame = chn_out;
918 dstFormat.mBitsPerChannel = 32;
920 err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, AUHAL_OUTPUT_ELEMENT, &dstFormat, sizeof(AudioStreamBasicDescription));
921 if (err != noErr) { errorMsg="kAudioUnitProperty_StreamFormat Input"; _state = -5; goto error; }
923 err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, AUHAL_OUTPUT_ELEMENT, (UInt32*)&_samples_per_period, sizeof(UInt32));
924 if (err != noErr) { errorMsg="kAudioUnitProperty_MaximumFramesPerSlice, Output"; _state = -5; goto error; }
927 /* read back stream descriptions */
929 size = sizeof(AudioStreamBasicDescription);
930 err = AudioUnitGetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, AUHAL_INPUT_ELEMENT, &srcFormat, &size);
931 if (err != noErr) { errorMsg="Get kAudioUnitProperty_StreamFormat, Output"; _state = -5; goto error; }
932 _capture_channels = srcFormat.mChannelsPerFrame;
934 PrintStreamDesc(&srcFormat);
939 size = sizeof(AudioStreamBasicDescription);
940 err = AudioUnitGetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, AUHAL_OUTPUT_ELEMENT, &dstFormat, &size);
941 if (err != noErr) { errorMsg="Get kAudioUnitProperty_StreamFormat, Input"; _state = -5; goto error; }
942 _playback_channels = dstFormat.mChannelsPerFrame;
945 PrintStreamDesc(&dstFormat);
949 /* prepare buffers for input */
950 if (_capture_channels > 0) {
951 _input_audio_buffer_list = (AudioBufferList*)malloc(sizeof(AudioBufferList) + (_capture_channels - 1) * sizeof(AudioBuffer));
952 assert(_input_audio_buffer_list);
953 if (!_input_audio_buffer_list) { errorMsg="Out of Memory."; _state = -8; goto error; }
956 _active_device_id = device_id;
959 err = add_listener (_active_device_id, kAudioDeviceProcessorOverload, this);
960 if (err != noErr) { errorMsg="kAudioDeviceProcessorOverload, Listen"; _state = -9; goto error; }
962 err = add_listener (_active_device_id, kAudioDevicePropertyBufferFrameSize, this);
963 if (err != noErr) { errorMsg="kAudioDevicePropertyBufferFrameSize, Listen"; _state = -9; goto error; }
965 err = add_listener (_active_device_id, kAudioDevicePropertyNominalSampleRate, this);
966 if (err != noErr) { errorMsg="kAudioDevicePropertyNominalSampleRate, Listen"; _state = -9; goto error; }
968 _samples_per_period = current_buffer_size_id(_active_device_id);
971 AURenderCallbackStruct renderCallback;
972 memset (&renderCallback, 0, sizeof (renderCallback));
973 renderCallback.inputProc = render_callback_ptr;
974 renderCallback.inputProcRefCon = this;
975 if (_playback_channels == 0) {
976 err = AudioUnitSetProperty(_auhal,
977 kAudioOutputUnitProperty_SetInputCallback,
978 kAudioUnitScope_Output, 1,
979 &renderCallback, sizeof (renderCallback));
981 err = AudioUnitSetProperty(_auhal,
982 kAudioUnitProperty_SetRenderCallback,
983 kAudioUnitScope_Output, 0,
984 &renderCallback, sizeof (renderCallback));
987 if (err != noErr) { errorMsg="kAudioUnitProperty_SetRenderCallback"; _state = -10; goto error; }
989 /* setup complete, now get going.. */
990 if (AudioOutputUnitStart(_auhal) == noErr) {
991 _input_names.clear();
992 _output_names.clear();
993 cache_port_names (device_id, true);
994 cache_port_names (device_id, false);
996 pthread_mutex_unlock (&_discovery_lock);
999 if (set_device_buffer_size_id(_active_device_id, samples_per_period)) {
1000 errorMsg="kAudioDevicePropertyBufferFrameSize"; _state = -11; goto error;
1007 assert (_state != 0);
1008 char *rv = (char*)&err;
1009 fprintf(stderr, "CoreaudioPCM Error: %c%c%c%c %s\n", rv[0], rv[1], rv[2], rv[3], errorMsg.c_str());
1011 _active_device_id = 0;
1012 pthread_mutex_unlock (&_discovery_lock);
1017 CoreAudioPCM::cache_port_names(AudioDeviceID id, bool input)
1022 n_chn = _capture_channels;
1024 n_chn = _playback_channels;;
1026 #ifdef COREAUDIO_108
1027 AudioObjectPropertyAddress property_address;
1028 property_address.mSelector = kAudioObjectPropertyElementName;
1029 property_address.mScope = input ? kAudioDevicePropertyScopeInput: kAudioDevicePropertyScopeOutput;
1032 for (uint32_t c = 0; c < n_chn; ++c) {
1033 CFStringRef name = NULL;
1034 std::stringstream ss;
1038 #ifdef COREAUDIO_108
1039 property_address.mElement = c + 1;
1040 err = AudioObjectGetPropertyDataSize(id, &property_address, 0, NULL, &size);
1042 err = AudioDeviceGetPropertyInfo (id, c + 1, input,
1043 kAudioDevicePropertyChannelNameCFString,
1048 if (err == kAudioHardwareNoError) {
1049 #ifdef COREAUDIO_108
1050 err = AudioObjectGetPropertyData(id, &property_address, c + 1, NULL, &size, &name);
1052 err = AudioDeviceGetProperty (id, c + 1, input,
1053 kAudioDevicePropertyChannelNameCFString,
1059 bool decoded = false;
1060 char* cstr_name = 0;
1061 if (err == kAudioHardwareNoError) {
1062 CFIndex length = CFStringGetLength(name);
1063 CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8);
1064 cstr_name = new char[maxSize];
1065 decoded = CFStringGetCString(name, cstr_name, maxSize, kCFStringEncodingUTF8);
1070 if (cstr_name && decoded && (0 != std::strlen(cstr_name) ) ) {
1071 ss << " - " << cstr_name;
1074 printf("%s %d Name: %s\n", input ? "Input" : "Output", c+1, ss.str().c_str());
1078 _input_names.push_back (ss.str());
1080 _output_names.push_back (ss.str());
1086 delete [] cstr_name;
1091 CoreAudioPCM::cached_port_name(uint32_t port, bool input) const
1093 if (_state != 0) { return ""; }
1096 if (port >= _input_names.size()) {
1099 return _input_names[port];
1101 if (port >= _output_names.size()) {
1104 return _output_names[port];
1110 CoreAudioPCM::render_callback (
1111 AudioUnitRenderActionFlags* ioActionFlags,
1112 const AudioTimeStamp* inTimeStamp,
1114 UInt32 inNumberFrames,
1115 AudioBufferList* ioData)
1117 OSStatus retVal = kAudioHardwareNoError;
1119 if (_samples_per_period < inNumberFrames) {
1121 printf("samples per period exceeds configured value, cycle skipped (%u < %u)\n",
1122 (unsigned int)_samples_per_period, (unsigned int)inNumberFrames);
1124 for (uint32_t i = 0; _playback_channels > 0 && i < ioData->mNumberBuffers; ++i) {
1125 float* ob = (float*) ioData->mBuffers[i].mData;
1126 memset(ob, 0, sizeof(float) * inNumberFrames);
1131 assert(_playback_channels == 0 || ioData->mNumberBuffers == _playback_channels);
1133 UInt64 cur_cycle_start = AudioGetCurrentHostTime ();
1134 _cur_samples_per_period = inNumberFrames;
1136 if (_capture_channels > 0) {
1137 _input_audio_buffer_list->mNumberBuffers = _capture_channels;
1138 for (uint32_t i = 0; i < _capture_channels; ++i) {
1139 _input_audio_buffer_list->mBuffers[i].mNumberChannels = 1;
1140 _input_audio_buffer_list->mBuffers[i].mDataByteSize = inNumberFrames * sizeof(float);
1141 _input_audio_buffer_list->mBuffers[i].mData = NULL;
1144 retVal = AudioUnitRender(_auhal, ioActionFlags, inTimeStamp, AUHAL_INPUT_ELEMENT, inNumberFrames, _input_audio_buffer_list);
1147 if (retVal != kAudioHardwareNoError) {
1149 char *rv = (char*)&retVal;
1150 printf("ERR %c%c%c%c\n", rv[0], rv[1], rv[2], rv[3]);
1152 if (_error_callback) {
1153 _error_callback(_error_arg);
1158 _output_audio_buffer_list = ioData;
1164 if (_process_callback) {
1165 rv = _process_callback(_process_arg, inNumberFrames, cur_cycle_start);
1168 _in_process = false;
1170 if (rv != 0 && _playback_channels > 0) {
1172 for (uint32_t i = 0; i < ioData->mNumberBuffers; ++i) {
1173 float* ob = (float*) ioData->mBuffers[i].mData;
1174 memset(ob, 0, sizeof(float) * inNumberFrames);
1181 CoreAudioPCM::get_capture_channel (uint32_t chn, float *input, uint32_t n_samples)
1183 if (!_in_process || chn > _capture_channels || n_samples > _cur_samples_per_period) {
1186 assert(_input_audio_buffer_list->mNumberBuffers > chn);
1187 memcpy((void*)input, (void*)_input_audio_buffer_list->mBuffers[chn].mData, sizeof(float) * n_samples);
1192 CoreAudioPCM::set_playback_channel (uint32_t chn, const float *output, uint32_t n_samples)
1194 if (!_in_process || chn > _playback_channels || n_samples > _cur_samples_per_period) {
1198 assert(_output_audio_buffer_list && _output_audio_buffer_list->mNumberBuffers > chn);
1199 memcpy((void*)_output_audio_buffer_list->mBuffers[chn].mData, (void*)output, sizeof(float) * n_samples);
1205 CoreAudioPCM::launch_control_app (uint32_t device_id)
1207 if (device_id >= _n_devices) {
1211 CFStringRef config_app = NULL;
1212 UInt32 size = sizeof (config_app);
1215 err = GetPropertyWrapper(_device_ids[device_id], 0, false, kAudioDevicePropertyConfigurationApplication, &size, &config_app);
1216 if (kAudioHardwareNoError != err) {
1221 if (noErr == LSFindApplicationForInfo(kLSUnknownCreator, config_app, NULL, &appFSRef, NULL)) {
1222 LSOpenFSRef(&appFSRef, NULL);
1224 // open default AudioMIDISetup if device app is not found
1225 CFStringRef audioMidiSetup = CFStringCreateWithCString(kCFAllocatorDefault, "com.apple.audio.AudioMIDISetup", kCFStringEncodingMacRoman);
1226 if (noErr == LSFindApplicationForInfo(kLSUnknownCreator, audioMidiSetup, NULL, &appFSRef, NULL)) {
1227 LSOpenFSRef(&appFSRef, NULL);
1231 CFRelease (config_app);