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 (kAudioHardwarePropertyDevices, 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::current_buffer_size_id(AudioDeviceID id) {
485 UInt32 size = sizeof(UInt32);
487 err = GetPropertyWrapper (id, 0, 0, kAudioDevicePropertyBufferFrameSize, &size, &buffer_size);
489 return _samples_per_period;
496 CoreAudioPCM::current_sample_rate_id(AudioDeviceID id, bool input) {
500 size = sizeof (rate);
502 err = GetPropertyWrapper(id, 0, input, kAudioDevicePropertyNominalSampleRate, &size, &rate);
510 CoreAudioPCM::current_sample_rate(uint32_t device_id, bool input) {
511 if (device_id >= _n_devices) {
514 return current_sample_rate_id(_device_ids[device_id], input);
518 CoreAudioPCM::sample_rate() {
519 if (_active_device_id == 0) {
522 return current_sample_rate_id(_active_device_id, _playback_channels > 0 ? false : true);
526 CoreAudioPCM::set_device_sample_rate_id (AudioDeviceID id, float rate, bool input)
528 std::vector<int>::iterator intIter;
532 if (current_sample_rate_id(id, input) == rate) {
536 Float64 newNominalRate = rate;
537 size = sizeof (Float64);
539 err = SetPropertyWrapper(id, NULL, 0, input, kAudioDevicePropertyNominalSampleRate, size, &newNominalRate);
541 fprintf(stderr, "CoreAudioPCM: failed to set samplerate\n");
545 int timeout = 3000; // 3 sec
546 while (--timeout > 0) {
547 if (current_sample_rate_id(id, input) == rate) {
552 fprintf(stderr, "CoreAudioPCM: CoreAudio: Setting SampleRate took %d ms.\n", (3000 - timeout));
555 fprintf(stderr, "CoreAudioPCM: CoreAudio: Setting SampleRate timed out.\n");
563 CoreAudioPCM::set_device_sample_rate (uint32_t device_id, float rate, bool input)
565 return set_device_sample_rate_id(_device_ids[device_id], rate, input);
569 CoreAudioPCM::discover()
574 if (pthread_mutex_trylock (&_discovery_lock)) {
579 delete _device_ids; _device_ids = 0;
580 free(_device_ins); _device_ins = 0;
581 free(_device_outs); _device_outs = 0;
585 err = GetHardwarePropertyInfoWrapper (kAudioHardwarePropertyDevices, &size);
587 _n_devices = size / sizeof (AudioDeviceID);
588 size = _n_devices * sizeof (AudioDeviceID);
590 _device_ids = new AudioDeviceID[_n_devices];
591 _device_ins = (uint32_t*) calloc(_n_devices, sizeof(uint32_t));
592 _device_outs = (uint32_t*) calloc(_n_devices, sizeof(uint32_t));
594 assert(_device_ins && _device_outs && _device_ids);
595 if (!_device_ins || !_device_ins || !_device_ids) {
596 fprintf(stderr, "OUT OF MEMORY\n");
600 pthread_mutex_unlock (&_discovery_lock);
604 err = GetHardwarePropertyWrapper (kAudioHardwarePropertyDevices, &size, _device_ids);
606 for (size_t idx = 0; idx < _n_devices; ++idx) {
609 err = GetPropertyWrapper (_device_ids[idx], 0, 0, kAudioDevicePropertyDeviceName, &size, deviceName);
611 if (kAudioHardwareNoError != err) {
612 fprintf(stderr, "CoreAudioPCM: device name query failed\n");
616 UInt32 inputChannelCount = available_channels(idx, true);
617 UInt32 outputChannelCount = available_channels(idx, false);
620 std::string dn = deviceName;
621 _device_ins[idx] = inputChannelCount;
622 _device_outs[idx] = outputChannelCount;
624 printf("CoreAudio Device: #%ld (id:%lu) '%s' in:%u out:%u\n", idx,
625 (long unsigned int)_device_ids[idx],
627 (unsigned int)inputChannelCount, (unsigned int)outputChannelCount);
629 if (outputChannelCount > 0 || inputChannelCount > 0) {
630 _devices.insert (std::pair<size_t, std::string> (idx, dn));
634 pthread_mutex_unlock (&_discovery_lock);
638 CoreAudioPCM::xrun_callback ()
641 printf("Coreaudio XRUN\n");
643 if (_xrun_callback) {
644 _xrun_callback(_xrun_arg);
649 CoreAudioPCM::buffer_size_callback ()
651 _samples_per_period = current_buffer_size_id(_active_device_id);
653 if (_buffer_size_callback) {
654 _buffer_size_callback(_buffer_size_arg);
659 CoreAudioPCM::sample_rate_callback ()
662 printf("Sample Rate Changed!\n");
664 if (_sample_rate_callback) {
665 _sample_rate_callback(_sample_rate_arg);
670 CoreAudioPCM::pcm_stop ()
674 AudioOutputUnitStop(_auhal);
677 AudioObjectPropertyAddress prop;
678 prop.mScope = kAudioObjectPropertyScopeGlobal;
680 if (_active_device_id > 0) {
681 prop.mSelector = kAudioDeviceProcessorOverload;
682 AudioObjectRemovePropertyListener(_active_device_id, &prop, &property_callback_ptr, this);
683 prop.mSelector = kAudioDevicePropertyBufferFrameSize;
684 AudioObjectRemovePropertyListener(_active_device_id, &prop, &property_callback_ptr, this);
685 prop.mSelector = kAudioDevicePropertyNominalSampleRate;
686 AudioObjectRemovePropertyListener(_active_device_id, &prop, &property_callback_ptr, this);
689 if (_active_device_id > 0) {
690 AudioDeviceRemovePropertyListener(_active_device_id, 0, true, kAudioDeviceProcessorOverload, property_callback_ptr);
691 AudioDeviceRemovePropertyListener(_active_device_id, 0, true, kAudioDevicePropertyBufferFrameSize, property_callback_ptr);
692 AudioDeviceRemovePropertyListener(_active_device_id, 0, true, kAudioDevicePropertyNominalSampleRate, property_callback_ptr);
696 if (_aggregate_plugin_id) {
697 destroy_aggregate_device();
700 AudioUnitUninitialize(_auhal);
702 AudioComponentInstanceDispose(_auhal);
704 CloseComponent(_auhal);
708 _capture_channels = 0;
709 _playback_channels = 0;
710 _aggregate_plugin_id = 0;
711 _aggregate_device_id = 0;
712 _active_device_id = 0;
714 free(_input_audio_buffer_list);
715 _input_audio_buffer_list = 0;
717 _input_names.clear();
718 _output_names.clear();
721 _process_callback = 0;
726 static void PrintStreamDesc (AudioStreamBasicDescription *inDesc)
728 printf ("- - - - - - - - - - - - - - - - - - - -\n");
729 printf (" Sample Rate:%.2f", inDesc->mSampleRate);
730 printf (" Format ID:%.*s\n", (int)sizeof(inDesc->mFormatID), (char*)&inDesc->mFormatID);
731 printf (" Format Flags:%X\n", (unsigned int)inDesc->mFormatFlags);
732 printf (" Bytes per Packet:%d\n", (int)inDesc->mBytesPerPacket);
733 printf (" Frames per Packet:%d\n", (int)inDesc->mFramesPerPacket);
734 printf (" Bytes per Frame:%d\n", (int)inDesc->mBytesPerFrame);
735 printf (" Channels per Frame:%d\n", (int)inDesc->mChannelsPerFrame);
736 printf (" Bits per Channel:%d\n", (int)inDesc->mBitsPerChannel);
737 printf ("- - - - - - - - - - - - - - - - - - - -\n");
742 CoreAudioPCM::set_device_buffer_size_id (AudioDeviceID id, uint32_t samples_per_period)
747 uint32val = samples_per_period;
748 err = SetPropertyWrapper(id, NULL, 0, true, kAudioDevicePropertyBufferFrameSize, sizeof(UInt32), &uint32val);
749 if (err != noErr) { return -1; }
750 err = SetPropertyWrapper(id, NULL, 0, false, kAudioDevicePropertyBufferFrameSize, sizeof(UInt32), &uint32val);
751 if (err != noErr) { return -1; }
756 CoreAudioPCM::set_samples_per_period (uint32_t n_samples)
759 if (_state != 0 || _active_device_id == 0) {
762 set_device_buffer_size_id (_active_device_id, n_samples);
767 CoreAudioPCM::pcm_start (
768 uint32_t device_id_in, uint32_t device_id_out,
769 uint32_t sample_rate, uint32_t samples_per_period,
770 int (process_callback (void*, const uint32_t, const uint64_t)), void *process_arg)
774 std::string errorMsg;
777 // TODO add "none' device to force half-duplex
779 if (device_id_out >= _n_devices || device_id_in >= _n_devices) {
783 pthread_mutex_lock (&_discovery_lock);
785 _process_callback = process_callback;
786 _process_arg = process_arg;
787 _samples_per_period = samples_per_period;
788 _cur_samples_per_period = 0;
789 _active_device_id = 0;
790 _capture_channels = 0;
791 _playback_channels = 0;
793 const uint32_t chn_in = _device_ins[device_id_in] + ((device_id_out != device_id_in) ? _device_ins[device_id_out] : 0);
794 const uint32_t chn_out = _device_outs[device_id_out] + ((device_id_out != device_id_in) ? _device_outs[device_id_in] : 0);
796 assert (chn_in > 0 || chn_out > 0);
801 AudioDeviceID device_id;
802 AudioStreamBasicDescription srcFormat, dstFormat;
804 AudioComponentDescription cd = {kAudioUnitType_Output, kAudioUnitSubType_HALOutput, kAudioUnitManufacturer_Apple, 0, 0};
805 AudioComponent HALOutput = AudioComponentFindNext(NULL, &cd);
806 if (!HALOutput) { errorMsg="AudioComponentFindNext"; goto error; }
808 err = AudioComponentInstanceNew(HALOutput, &_auhal);
809 if (err != noErr) { errorMsg="AudioComponentInstanceNew"; goto error; }
811 err = AudioUnitInitialize(_auhal);
812 if (err != noErr) { errorMsg="AudioUnitInitialize"; goto error; }
814 // explicitly change samplerate of the device
815 if (set_device_sample_rate(device_id_in, sample_rate, true)) {
816 errorMsg="Failed to set SampleRate, Capture Device"; goto error;
818 if (set_device_sample_rate(device_id_out, sample_rate, false)) {
819 errorMsg="Failed to set SampleRate, Playback Device"; goto error;
822 // explicitly request device buffer size
823 if (set_device_buffer_size_id(_device_ids[device_id_in], samples_per_period)) {
824 errorMsg="kAudioDevicePropertyBufferFrameSize, Input"; goto error;
826 if (set_device_buffer_size_id(_device_ids[device_id_out], samples_per_period)) {
827 errorMsg="kAudioDevicePropertyBufferFrameSize, Output"; goto error;
830 // create aggregate device..
831 if (_device_ids[device_id_in] != _device_ids[device_id_out]) {
832 if (0 == create_aggregate_device(_device_ids[device_id_in], _device_ids[device_id_out], sample_rate, &_aggregate_device_id)) {
833 device_id = _aggregate_device_id;
835 _aggregate_device_id = 0;
836 _aggregate_plugin_id = 0;
837 errorMsg="Cannot create Aggregate Device"; goto error;
840 device_id = _device_ids[device_id_out];
843 if (device_id_out != device_id_in) {
844 assert(_aggregate_device_id > 0);
847 // enableIO to progress further
848 uint32val = (chn_in > 0) ? 1 : 0;
849 err = AudioUnitSetProperty(_auhal, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, AUHAL_INPUT_ELEMENT, &uint32val, sizeof(UInt32));
850 if (err != noErr) { errorMsg="kAudioOutputUnitProperty_EnableIO, Input"; goto error; }
852 uint32val = (chn_out > 0) ? 1 : 0;
853 err = AudioUnitSetProperty(_auhal, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, AUHAL_OUTPUT_ELEMENT, &uint32val, sizeof(UInt32));
854 if (err != noErr) { errorMsg="kAudioOutputUnitProperty_EnableIO, Output"; goto error; }
856 err = AudioUnitSetProperty(_auhal, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &device_id, sizeof(AudioDeviceID));
857 if (err != noErr) { errorMsg="kAudioOutputUnitProperty_CurrentDevice, Input"; goto error; }
861 srcFormat.mSampleRate = sample_rate;
862 srcFormat.mFormatID = kAudioFormatLinearPCM;
863 srcFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved;
864 srcFormat.mBytesPerPacket = sizeof(float);
865 srcFormat.mFramesPerPacket = 1;
866 srcFormat.mBytesPerFrame = sizeof(float);
867 srcFormat.mChannelsPerFrame = chn_in;
868 srcFormat.mBitsPerChannel = 32;
870 err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, AUHAL_INPUT_ELEMENT, &srcFormat, sizeof(AudioStreamBasicDescription));
871 if (err != noErr) { errorMsg="kAudioUnitProperty_StreamFormat, Output"; goto error; }
873 err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, AUHAL_INPUT_ELEMENT, (UInt32*)&_samples_per_period, sizeof(UInt32));
874 if (err != noErr) { errorMsg="kAudioUnitProperty_MaximumFramesPerSlice, Input"; goto error; }
878 dstFormat.mSampleRate = sample_rate;
879 dstFormat.mFormatID = kAudioFormatLinearPCM;
880 dstFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved;
881 dstFormat.mBytesPerPacket = sizeof(float);
882 dstFormat.mFramesPerPacket = 1;
883 dstFormat.mBytesPerFrame = sizeof(float);
884 dstFormat.mChannelsPerFrame = chn_out;
885 dstFormat.mBitsPerChannel = 32;
887 err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, AUHAL_OUTPUT_ELEMENT, &dstFormat, sizeof(AudioStreamBasicDescription));
888 if (err != noErr) { errorMsg="kAudioUnitProperty_StreamFormat Input"; goto error; }
890 err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, AUHAL_OUTPUT_ELEMENT, (UInt32*)&_samples_per_period, sizeof(UInt32));
891 if (err != noErr) { errorMsg="kAudioUnitProperty_MaximumFramesPerSlice, Output"; goto error; }
894 /* read back stream descriptions */
896 size = sizeof(AudioStreamBasicDescription);
897 err = AudioUnitGetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, AUHAL_INPUT_ELEMENT, &srcFormat, &size);
898 if (err != noErr) { errorMsg="Get kAudioUnitProperty_StreamFormat, Output"; goto error; }
899 _capture_channels = srcFormat.mChannelsPerFrame;
901 PrintStreamDesc(&srcFormat);
906 size = sizeof(AudioStreamBasicDescription);
907 err = AudioUnitGetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, AUHAL_OUTPUT_ELEMENT, &dstFormat, &size);
908 if (err != noErr) { errorMsg="Get kAudioUnitProperty_StreamFormat, Input"; goto error; }
909 _playback_channels = dstFormat.mChannelsPerFrame;
912 PrintStreamDesc(&dstFormat);
916 /* prepare buffers for input */
917 if (_capture_channels > 0) {
918 _input_audio_buffer_list = (AudioBufferList*)malloc(sizeof(AudioBufferList) + (_capture_channels - 1) * sizeof(AudioBuffer));
919 assert(_input_audio_buffer_list);
920 if (!_input_audio_buffer_list) { errorMsg="Out of Memory."; goto error; }
923 _active_device_id = device_id;
926 err = add_listener (_active_device_id, kAudioDeviceProcessorOverload, this);
927 if (err != noErr) { errorMsg="kAudioDeviceProcessorOverload, Listen"; goto error; }
929 err = add_listener (_active_device_id, kAudioDevicePropertyBufferFrameSize, this);
930 if (err != noErr) { errorMsg="kAudioDevicePropertyBufferFrameSize, Listen"; goto error; }
932 err = add_listener (_active_device_id, kAudioDevicePropertyNominalSampleRate, this);
933 if (err != noErr) { errorMsg="kAudioDevicePropertyBufferFrameSize, Listen"; goto error; }
935 _samples_per_period = current_buffer_size_id(_active_device_id);
938 AURenderCallbackStruct renderCallback;
939 memset (&renderCallback, 0, sizeof (renderCallback));
940 renderCallback.inputProc = render_callback_ptr;
941 renderCallback.inputProcRefCon = this;
942 if (_playback_channels == 0) {
943 err = AudioUnitSetProperty(_auhal,
944 kAudioOutputUnitProperty_SetInputCallback,
945 kAudioUnitScope_Output, 1,
946 &renderCallback, sizeof (renderCallback));
948 err = AudioUnitSetProperty(_auhal,
949 kAudioUnitProperty_SetRenderCallback,
950 kAudioUnitScope_Output, 0,
951 &renderCallback, sizeof (renderCallback));
954 if (err != noErr) { errorMsg="kAudioUnitProperty_SetRenderCallback"; goto error; }
956 /* setup complete, now get going.. */
957 if (AudioOutputUnitStart(_auhal) == noErr) {
958 _input_names.clear();
959 _output_names.clear();
960 cache_port_names (device_id, true);
961 cache_port_names (device_id, false);
963 pthread_mutex_unlock (&_discovery_lock);
966 if (set_device_buffer_size_id(_active_device_id, samples_per_period)) {
967 errorMsg="kAudioDevicePropertyBufferFrameSize"; goto error;
974 char *rv = (char*)&err;
975 fprintf(stderr, "CoreaudioPCM Error: %c%c%c%c %s\n", rv[0], rv[1], rv[2], rv[3], errorMsg.c_str());
978 _active_device_id = 0;
979 pthread_mutex_unlock (&_discovery_lock);
984 CoreAudioPCM::cache_port_names(AudioDeviceID id, bool input)
989 n_chn = _capture_channels;
991 n_chn = _playback_channels;;
994 AudioObjectPropertyAddress property_address;
995 property_address.mSelector = kAudioObjectPropertyElementName;
996 property_address.mScope = input ? kAudioDevicePropertyScopeInput: kAudioDevicePropertyScopeOutput;
999 for (uint32_t c = 0; c < n_chn; ++c) {
1000 CFStringRef name = NULL;
1001 std::stringstream ss;
1005 #ifdef COREAUDIO_108
1006 property_address.mElement = c + 1;
1007 err = AudioObjectGetPropertyDataSize(id, &property_address, 0, NULL, &size);
1009 err = AudioDeviceGetPropertyInfo (id, c + 1, input,
1010 kAudioDevicePropertyChannelNameCFString,
1015 if (err == kAudioHardwareNoError) {
1016 #ifdef COREAUDIO_108
1017 err = AudioObjectGetPropertyData(id, &property_address, c + 1, NULL, &size, &name);
1019 err = AudioDeviceGetProperty (id, c + 1, input,
1020 kAudioDevicePropertyChannelNameCFString,
1026 bool decoded = false;
1027 char* cstr_name = 0;
1028 if (err == kAudioHardwareNoError) {
1029 CFIndex length = CFStringGetLength(name);
1030 CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8);
1031 cstr_name = new char[maxSize];
1032 decoded = CFStringGetCString(name, cstr_name, maxSize, kCFStringEncodingUTF8);
1037 if (cstr_name && decoded && (0 != std::strlen(cstr_name) ) ) {
1038 ss << " - " << cstr_name;
1041 printf("%s %d Name: %s\n", input ? "Input" : "Output", c+1, ss.str().c_str());
1045 _input_names.push_back (ss.str());
1047 _output_names.push_back (ss.str());
1053 delete [] cstr_name;
1058 CoreAudioPCM::cached_port_name(uint32_t port, bool input) const
1060 if (_state != 0) { return ""; }
1063 if (port >= _input_names.size()) {
1066 return _input_names[port];
1068 if (port >= _output_names.size()) {
1071 return _output_names[port];
1077 CoreAudioPCM::render_callback (
1078 AudioUnitRenderActionFlags* ioActionFlags,
1079 const AudioTimeStamp* inTimeStamp,
1081 UInt32 inNumberFrames,
1082 AudioBufferList* ioData)
1084 OSStatus retVal = kAudioHardwareNoError;
1086 if (_samples_per_period < inNumberFrames) {
1088 printf("samples per period exceeds configured value, cycle skipped (%u < %u)\n",
1089 (unsigned int)_samples_per_period, (unsigned int)inNumberFrames);
1091 for (uint32_t i = 0; _playback_channels > 0 && i < ioData->mNumberBuffers; ++i) {
1092 float* ob = (float*) ioData->mBuffers[i].mData;
1093 memset(ob, 0, sizeof(float) * inNumberFrames);
1098 assert(_playback_channels == 0 || ioData->mNumberBuffers == _playback_channels);
1100 UInt64 cur_cycle_start = AudioGetCurrentHostTime ();
1101 _cur_samples_per_period = inNumberFrames;
1103 if (_capture_channels > 0) {
1104 _input_audio_buffer_list->mNumberBuffers = _capture_channels;
1105 for (uint32_t i = 0; i < _capture_channels; ++i) {
1106 _input_audio_buffer_list->mBuffers[i].mNumberChannels = 1;
1107 _input_audio_buffer_list->mBuffers[i].mDataByteSize = inNumberFrames * sizeof(float);
1108 _input_audio_buffer_list->mBuffers[i].mData = NULL;
1111 retVal = AudioUnitRender(_auhal, ioActionFlags, inTimeStamp, AUHAL_INPUT_ELEMENT, inNumberFrames, _input_audio_buffer_list);
1114 if (retVal != kAudioHardwareNoError) {
1116 char *rv = (char*)&retVal;
1117 printf("ERR %c%c%c%c\n", rv[0], rv[1], rv[2], rv[3]);
1119 if (_error_callback) {
1120 _error_callback(_error_arg);
1125 _output_audio_buffer_list = ioData;
1131 if (_process_callback) {
1132 rv = _process_callback(_process_arg, inNumberFrames, cur_cycle_start);
1135 _in_process = false;
1137 if (rv != 0 && _playback_channels > 0) {
1139 for (uint32_t i = 0; i < ioData->mNumberBuffers; ++i) {
1140 float* ob = (float*) ioData->mBuffers[i].mData;
1141 memset(ob, 0, sizeof(float) * inNumberFrames);
1148 CoreAudioPCM::get_capture_channel (uint32_t chn, float *input, uint32_t n_samples)
1150 if (!_in_process || chn > _capture_channels || n_samples > _cur_samples_per_period) {
1153 assert(_input_audio_buffer_list->mNumberBuffers > chn);
1154 memcpy((void*)input, (void*)_input_audio_buffer_list->mBuffers[chn].mData, sizeof(float) * n_samples);
1159 CoreAudioPCM::set_playback_channel (uint32_t chn, const float *output, uint32_t n_samples)
1161 if (!_in_process || chn > _playback_channels || n_samples > _cur_samples_per_period) {
1165 assert(_output_audio_buffer_list && _output_audio_buffer_list->mNumberBuffers > chn);
1166 memcpy((void*)_output_audio_buffer_list->mBuffers[chn].mData, (void*)output, sizeof(float) * n_samples);
1172 CoreAudioPCM::launch_control_app (uint32_t device_id)
1174 if (device_id >= _n_devices) {
1178 CFStringRef config_app = NULL;
1179 UInt32 size = sizeof (config_app);
1182 err = GetPropertyWrapper(_device_ids[device_id], 0, false, kAudioDevicePropertyConfigurationApplication, &size, &config_app);
1183 if (kAudioHardwareNoError != err) {
1188 if (noErr == LSFindApplicationForInfo(kLSUnknownCreator, config_app, NULL, &appFSRef, NULL)) {
1189 LSOpenFSRef(&appFSRef, NULL);
1191 // open default AudioMIDISetup if device app is not found
1192 CFStringRef audioMidiSetup = CFStringCreateWithCString(kCFAllocatorDefault, "com.apple.audio.AudioMIDISetup", kCFStringEncodingMacRoman);
1193 if (noErr == LSFindApplicationForInfo(kLSUnknownCreator, audioMidiSetup, NULL, &appFSRef, NULL)) {
1194 LSOpenFSRef(&appFSRef, NULL);
1198 CFRelease (config_app);