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"
23 static OSStatus hardwarePropertyChangeCallback(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void* arg) {
24 CoreAudioPCM * self = static_cast<CoreAudioPCM*>(arg);
25 self->hwPropertyChange();
29 static OSStatus hardwarePropertyChangeCallback (AudioHardwarePropertyID inPropertyID, void* arg) {
30 if (inPropertyID == kAudioHardwarePropertyDevices) {
31 CoreAudioPCM * self = static_cast<CoreAudioPCM*>(arg);
32 self->hwPropertyChange();
38 CoreAudioPCM::CoreAudioPCM ()
41 , _input_audio_buffer_list (0)
43 , _capture_channels (0)
44 , _playback_channels (0)
47 , _process_callback (0)
49 , _hw_changed_callback (0)
53 pthread_mutex_init (&_discovery_lock, 0);
56 CFRunLoopRef theRunLoop = NULL;
57 AudioObjectPropertyAddress property = { kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal, kAudioHardwarePropertyDevices };
58 AudioObjectSetPropertyData (kAudioObjectSystemObject, &property, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);
60 AudioObjectPropertyAddress prop;
61 prop.mSelector = kAudioHardwarePropertyDevices;
62 prop.mScope = kAudioObjectPropertyScopeGlobal;
64 AudioObjectAddPropertyListener(kAudioObjectSystemObject, &prop, hardwarePropertyChangeCallback, this);
66 AudioHardwareAddPropertyListener (kAudioHardwarePropertyDevices, hardwarePropertyChangeCallback, this);
70 CoreAudioPCM::~CoreAudioPCM ()
79 AudioObjectPropertyAddress prop;
80 prop.mSelector = kAudioHardwarePropertyDevices;
81 prop.mScope = kAudioObjectPropertyScopeGlobal;
83 AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &prop, &hardwarePropertyChangeCallback, this);
85 AudioHardwareRemovePropertyListener(kAudioHardwarePropertyDevices, hardwarePropertyChangeCallback);
87 free(_input_audio_buffer_list);
88 pthread_mutex_destroy (&_discovery_lock);
93 CoreAudioPCM::hwPropertyChange() {
95 // TODO Filter events..
96 if (_hw_changed_callback) {
97 _hw_changed_callback(_hw_changed_arg);
103 CoreAudioPCM::available_sample_rates(uint32_t device_id, std::vector<float>& sampleRates)
109 if (device_id >= _n_devices) {
114 AudioObjectPropertyAddress property_address;
115 property_address.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
116 property_address.mScope = kAudioDevicePropertyScopeOutput;
117 property_address.mElement = kAudioObjectPropertyElementMaster;
118 err = AudioObjectGetPropertyDataSize(_device_ids[device_id], &property_address, 0, NULL, &size);
120 err = AudioDeviceGetPropertyInfo(_device_ids[device_id], 0, 0, kAudioDevicePropertyAvailableNominalSampleRates, &size, NULL);
123 if (err != kAudioHardwareNoError) {
127 int numRates = size / sizeof(AudioValueRange);
128 AudioValueRange* supportedRates = new AudioValueRange[numRates];
131 err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, supportedRates);
133 err = AudioDeviceGetProperty(_device_ids[device_id], 0, 0, kAudioDevicePropertyAvailableNominalSampleRates, &size, supportedRates);
136 if (err != kAudioHardwareNoError) {
137 delete [] supportedRates;
141 static const float ardourRates[] = { 8000.0, 22050.0, 24000.0, 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0};
143 for(uint32_t i = 0; i < sizeof(ardourRates)/sizeof(float); ++i) {
144 for(uint32_t j = 0; j < numRates; ++j) {
145 if ((supportedRates[j].mMinimum <= ardourRates[i]) &&
146 (supportedRates[j].mMaximum >= ardourRates[i])) {
147 sampleRates.push_back (ardourRates[i]);
153 delete [] supportedRates;
158 CoreAudioPCM::available_buffer_sizes(uint32_t device_id, std::vector<uint32_t>& bufferSizes)
164 if (device_id >= _n_devices) {
168 AudioValueRange supportedRange;
169 size = sizeof (AudioValueRange);
172 AudioObjectPropertyAddress property_address;
173 property_address.mSelector = kAudioDevicePropertyBufferFrameSizeRange;
174 err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, &supportedRange);
176 err = AudioDeviceGetProperty(_device_ids[device_id], 0, 0, kAudioDevicePropertyBufferFrameSizeRange, &size, &supportedRange);
179 if (err != kAudioHardwareNoError) {
183 static const uint32_t ardourSizes[] = { 16, 32, 64, 128, 256, 512, 1024, 2048, 4096 };
185 for(uint32_t i = 0; i < sizeof(ardourSizes)/sizeof(uint32_t); ++i) {
186 if ((supportedRange.mMinimum <= ardourSizes[i]) &&
187 (supportedRange.mMaximum >= ardourSizes[i])) {
188 bufferSizes.push_back (ardourSizes[i]);
192 if (bufferSizes.empty()) {
193 bufferSizes.push_back ((uint32_t)supportedRange.mMinimum);
194 bufferSizes.push_back ((uint32_t)supportedRange.mMaximum);
200 CoreAudioPCM::available_channels(uint32_t device_id, bool input)
204 AudioBufferList *bufferList = NULL;
205 uint32_t channel_count = 0;
207 if (device_id >= _n_devices) {
211 /* query number of inputs */
213 AudioObjectPropertyAddress property_address;
214 property_address.mSelector = kAudioDevicePropertyStreamConfiguration;
215 property_address.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
216 err = AudioObjectGetPropertyDataSize(_device_ids[device_id], &property_address, 0, NULL, &size);
217 if (kAudioHardwareNoError != err) {
218 fprintf(stderr, "CoreaAudioPCM: kAudioDevicePropertyStreamConfiguration failed: %i\n", err);
222 bufferList = (AudioBufferList *)(malloc(size));
224 if (!bufferList) { fprintf(stderr, "OUT OF MEMORY\n"); return 0; }
226 err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, bufferList);
229 err = AudioDeviceGetPropertyInfo (_device_ids[device_id], 0, input ? AUHAL_INPUT_ELEMENT : AUHAL_OUTPUT_ELEMENT, kAudioDevicePropertyStreamConfiguration, &size, NULL);
230 if (kAudioHardwareNoError != err) {
231 fprintf(stderr, "CoreaAudioPCM: kAudioDevicePropertyStreamConfiguration failed: %i\n", err);
235 bufferList = (AudioBufferList *)(malloc(size));
237 if (!bufferList) { fprintf(stderr, "OUT OF MEMORY\n"); return 0; }
239 bufferList->mNumberBuffers = 0;
240 err = AudioDeviceGetProperty(_device_ids[device_id], 0, input ? AUHAL_INPUT_ELEMENT : AUHAL_OUTPUT_ELEMENT, kAudioDevicePropertyStreamConfiguration, &size, bufferList);
243 if(kAudioHardwareNoError != err) {
244 fprintf(stderr, "CoreaAudioPCM: kAudioDevicePropertyStreamConfiguration failed: %i\n", err);
249 for(UInt32 j = 0; j < bufferList->mNumberBuffers; ++j) {
250 channel_count += bufferList->mBuffers[j].mNumberChannels;
253 return channel_count;
257 CoreAudioPCM::get_stream_latencies(uint32 device_id, bool input, std::vector<uint32>& latencies)
262 if (device_id >= _n_devices) {
267 AudioObjectPropertyAddress property_address;
268 property_address.mSelector = kAudioDevicePropertyStreams;
269 property_address.mScope = input ? kAudioDevicePropertyScopeInput: kAudioDevicePropertyScopeOutput;
270 property_address.mElement = kAudioObjectPropertyElementMaster;
271 err = AudioObjectGetPropertyDataSize(_device_ids[device_id], &property_address, 0, NULL, &size);
274 const int elem = input ? AUHAL_INPUT_ELEMENT : AUHAL_OUTPUT_ELEMENT;
275 err = AudioDeviceGetPropertyInfo(_device_ids[device_id], 0, elem, kAudioDevicePropertyStreams, &size, &outWritable);
281 uint32 stream_count = size / sizeof(UInt32);
282 AudioStreamID streamIDs[stream_count];
285 err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, &streamIDs);
287 err = AudioDeviceGetProperty(_device_ids[device_id], 0, elem, kAudioDevicePropertyStreams, &size, streamIDs);
290 fprintf(stderr, "GetStreamLatencies kAudioDevicePropertyStreams\n");
294 for (uint32 i = 0; i < stream_count; i++) {
295 UInt32 stream_latency;
296 size = sizeof(UInt32);
298 property_address.mSelector = kAudioDevicePropertyStreams;
299 err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, &stream_latency);
301 err = AudioStreamGetProperty(streamIDs[i], elem, kAudioStreamPropertyLatency, &size, &stream_latency);
304 fprintf(stderr, "GetStreamLatencies kAudioStreamPropertyLatency\n");
308 printf("Stream %d latency: %d\n", i, stream_latency);
310 latencies.push_back(stream_latency);
315 CoreAudioPCM::get_latency(uint32 device_id, bool input)
319 UInt32 size = sizeof(UInt32);
323 if (device_id >= _n_devices) {
328 AudioObjectPropertyAddress property_address;
329 property_address.mSelector = kAudioDevicePropertyLatency;
330 property_address.mScope = input? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
331 property_address.mElement = 0;
332 err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, &lat0);
334 const int elem = input ? AUHAL_INPUT_ELEMENT : AUHAL_OUTPUT_ELEMENT;
335 err = AudioDeviceGetProperty(_device_ids[device_id], 0, elem, kAudioDevicePropertyLatency, &size, &lat0);
337 if (err != kAudioHardwareNoError) {
338 fprintf(stderr, "GetLatency kAudioDevicePropertyLatency\n");
342 property_address.mSelector = kAudioDevicePropertySafetyOffset;
343 err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, &latS);
345 err = AudioDeviceGetProperty(_device_ids[device_id], 0, elem, kAudioDevicePropertySafetyOffset, &size, &latS);
347 if (err != kAudioHardwareNoError) {
348 fprintf(stderr, "GetLatency kAudioDevicePropertySafetyOffset\n");
352 printf("Base Latency systemic+safetyoffset = %d+%d\n", lat0, latS);
354 latency = lat0 + latS;
356 uint32_t max_stream_latency = 0;
357 std::vector<uint32> stream_latencies;
358 get_stream_latencies(device_id, input, stream_latencies);
359 for (size_t i = 0; i < stream_latencies.size(); ++i) {
360 max_stream_latency = std::max(max_stream_latency, stream_latencies[i]);
362 latency += max_stream_latency;
371 CoreAudioPCM::current_sample_rate(uint32 device_id, bool input) {
375 if (device_id >= _n_devices) {
379 float sample_rate = 0;
382 size = sizeof (rate);
385 AudioObjectPropertyAddress property_address;
386 property_address.mSelector = kAudioDevicePropertyNominalSampleRate;
387 property_address.mScope = input ? kAudioDevicePropertyScopeInput: kAudioDevicePropertyScopeOutput;
388 err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, &rate);
390 err = AudioDeviceGetPropertyInfo(_device_ids[device_id], 0, input ? AUHAL_INPUT_ELEMENT : AUHAL_OUTPUT_ELEMENT, kAudioDevicePropertyNominalSampleRate, &size, &rate);
393 if (err == kAudioHardwareNoError) {
397 // prefer input, if vailable
400 property_address.mSelector = kAudioDevicePropertyNominalSampleRate;
401 property_address.mScope = kAudioDevicePropertyScopeInput;
402 err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, &rate);
404 err = AudioDeviceGetPropertyInfo(_device_ids[device_id], 0, AUHAL_INPUT_ELEMENT, kAudioDevicePropertyNominalSampleRate, &size, &rate);
407 if (err == kAudioHardwareNoError) {
415 CoreAudioPCM::set_device_sample_rate (uint32 device_id, float rate, bool input)
417 std::vector<int>::iterator intIter;
421 if (current_sample_rate(device_id, input) == rate) {
425 Float64 newNominalRate = rate;
426 size = sizeof (Float64);
429 AudioObjectPropertyAddress property_address;
430 property_address.mSelector = kAudioDevicePropertyNominalSampleRate;
431 property_address.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
432 property_address.mElement = kAudioObjectPropertyElementMaster;
433 err = AudioObjectSetPropertyData (_device_ids[device_id], &property_address, 0, NULL, size, &newNominalRate);
435 err = AudioDeviceSetProperty(_device_ids[device_id], NULL, 0, input ? AUHAL_INPUT_ELEMENT : AUHAL_OUTPUT_ELEMENT, kAudioDevicePropertyNominalSampleRate, size, &newNominalRate);
438 fprintf(stderr, "CoreAudioPCM: failed to set samplerate\n");
442 int timeout = 3000; // 3 sec
443 while (--timeout > 0) {
444 if (current_sample_rate(device_id) == rate) {
449 fprintf(stderr, "CoreAudioPCM: CoreAudio: Setting SampleRate took %d ms.\n", (3000 - timeout));
452 fprintf(stderr, "CoreAudioPCM: CoreAudio: Setting SampleRate timed out.\n");
460 CoreAudioPCM::discover()
465 if (pthread_mutex_trylock (&_discovery_lock)) {
470 delete _device_ids; _device_ids = 0;
471 free(_device_ins); _device_ins = 0;
472 free(_device_outs); _device_outs = 0;
477 AudioObjectPropertyAddress property_address;
478 property_address.mSelector = kAudioHardwarePropertyDevices;
479 property_address.mScope = kAudioObjectPropertyScopeGlobal;
480 property_address.mElement = kAudioObjectPropertyElementMaster;
481 err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &property_address, 0, NULL, &size);
483 err = AudioHardwareGetPropertyInfo (kAudioHardwarePropertyDevices, &size, NULL);
486 _n_devices = size / sizeof (AudioDeviceID);
487 size = _n_devices * sizeof (AudioDeviceID);
489 _device_ids = new AudioDeviceID[_n_devices];
490 _device_ins = (uint32_t*) calloc(_n_devices, sizeof(uint32_t));
491 _device_outs = (uint32_t*) calloc(_n_devices, sizeof(uint32_t));
493 assert(_device_ins && _device_outs && _device_ids);
494 if (!_device_ins || !_device_ins || !_device_ids) {
495 fprintf(stderr, "OUT OF MEMORY\n");
499 pthread_mutex_unlock (&_discovery_lock);
505 property_address.mSelector = kAudioHardwarePropertyDevices;
506 err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property_address, 0, NULL, &size, _device_ids);
508 err = AudioHardwareGetProperty (kAudioHardwarePropertyDevices, &size, _device_ids);
511 for (size_t idx = 0; idx < _n_devices; ++idx) {
515 property_address.mSelector = kAudioDevicePropertyDeviceName;
516 property_address.mScope = kAudioDevicePropertyScopeOutput;
517 err = AudioObjectGetPropertyData(_device_ids[idx], &property_address, 0, NULL, &size, deviceName);
519 err = AudioDeviceGetProperty(_device_ids[idx], 0, 0, kAudioDevicePropertyDeviceName, &size, deviceName);
522 if (kAudioHardwareNoError != err) {
523 fprintf(stderr, "CoreAudioPCM: device name query failed: %i\n", err);
527 UInt32 inputChannelCount = available_channels(idx, true);
528 UInt32 outputChannelCount = available_channels(idx, false);
531 std::string dn = deviceName;
532 _device_ins[idx] = inputChannelCount;
533 _device_outs[idx] = outputChannelCount;
535 printf("CoreAudio Device: #%ld '%s' in:%d out:%d\n", idx, deviceName, inputChannelCount, outputChannelCount);
537 if (outputChannelCount > 0 && inputChannelCount > 0) {
538 _devices.insert (std::pair<size_t, std::string> (idx, dn));
542 pthread_mutex_unlock (&_discovery_lock);
546 CoreAudioPCM::pcm_stop ()
550 AudioOutputUnitStop(_auhal);
551 AudioUnitUninitialize(_auhal);
553 AudioComponentInstanceDispose(_auhal);
555 CloseComponent(_auhal);
559 _capture_channels = 0;
560 _playback_channels = 0;
562 free(_input_audio_buffer_list);
563 _input_audio_buffer_list = 0;
565 _input_names.clear();
566 _output_names.clear();
569 _process_callback = 0;
573 static void PrintStreamDesc (AudioStreamBasicDescription *inDesc)
575 printf ("- - - - - - - - - - - - - - - - - - - -\n");
576 printf (" Sample Rate:%f", inDesc->mSampleRate);
577 printf (" Format ID:%.*s\n", (int)sizeof(inDesc->mFormatID), (char*)&inDesc->mFormatID);
578 printf (" Format Flags:%X\n", inDesc->mFormatFlags);
579 printf (" Bytes per Packet:%d\n", inDesc->mBytesPerPacket);
580 printf (" Frames per Packet:%d\n", inDesc->mFramesPerPacket);
581 printf (" Bytes per Frame:%d\n", inDesc->mBytesPerFrame);
582 printf (" Channels per Frame:%d\n", inDesc->mChannelsPerFrame);
583 printf (" Bits per Channel:%d\n", inDesc->mBitsPerChannel);
584 printf ("- - - - - - - - - - - - - - - - - - - -\n");
588 static OSStatus render_callback_ptr (
590 AudioUnitRenderActionFlags* ioActionFlags,
591 const AudioTimeStamp* inTimeStamp,
593 UInt32 inNumberFrames,
594 AudioBufferList* ioData)
596 CoreAudioPCM * d = static_cast<CoreAudioPCM*> (inRefCon);
597 return d->render_callback(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
603 CoreAudioPCM::pcm_start (
604 uint32_t device_id_in, uint32_t device_id_out,
605 uint32_t sample_rate, uint32_t samples_per_period,
606 int (process_callback (void*)), void *process_arg)
610 std::string errorMsg;
613 if (device_id_out >= _n_devices || device_id_in >= _n_devices) {
617 _process_callback = process_callback;
618 _process_arg = process_arg;
619 _max_samples_per_period = samples_per_period;
620 _cur_samples_per_period = 0;
624 AudioStreamBasicDescription srcFormat, dstFormat;
626 AudioComponentDescription cd = {kAudioUnitType_Output, kAudioUnitSubType_HALOutput, kAudioUnitManufacturer_Apple, 0, 0};
627 AudioComponent HALOutput = AudioComponentFindNext(NULL, &cd);
628 if (!HALOutput) { errorMsg="AudioComponentFindNext"; goto error; }
630 err = AudioComponentInstanceNew(HALOutput, &_auhal);
631 if (err != noErr) { errorMsg="AudioComponentInstanceNew"; goto error; }
633 err = AudioUnitInitialize(_auhal);
634 if (err != noErr) { errorMsg="AudioUnitInitialize"; goto error; }
636 // explicitly change samplerate of the device
637 if (set_device_sample_rate(device_id_in, sample_rate, true)) {
638 errorMsg="Failed to set SampleRate, Capture Device"; goto error;
640 if (set_device_sample_rate(device_id_out, sample_rate, false)) {
641 errorMsg="Failed to set SampleRate, Playback Device"; goto error;
644 // explicitly request device buffer size
645 uint32val = samples_per_period;
647 AudioObjectPropertyAddress property_address;
648 property_address.mSelector = kAudioDevicePropertyBufferFrameSize;
649 property_address.mScope = kAudioDevicePropertyScopeInput;
650 property_address.mElement = kAudioObjectPropertyElementMaster;
651 err = AudioObjectSetPropertyData (_device_ids[device_id_in], &property_address, 0, NULL, sizeof(UInt32), &uint32val);
652 if (err != noErr) { errorMsg="kAudioDevicePropertyBufferFrameSize, Input"; goto error; }
654 property_address.mScope = kAudioDevicePropertyScopeOutput;
655 err = AudioObjectSetPropertyData (_device_ids[device_id_out], &property_address, 0, NULL, sizeof(UInt32), &uint32val);
656 if (err != noErr) { errorMsg="kAudioDevicePropertyBufferFrameSize, Output"; goto error; }
658 err = AudioDeviceSetProperty(_device_ids[device_id_in], NULL, 0, AUHAL_INPUT_ELEMENT, kAudioDevicePropertyBufferFrameSize, sizeof(UInt32), &uint32val);
659 if (err != noErr) { errorMsg="kAudioDevicePropertyBufferFrameSize, Input"; goto error; }
660 err = AudioDeviceSetProperty(_device_ids[device_id_out], NULL, 0, AUHAL_OUTPUT_ELEMENT, kAudioDevicePropertyBufferFrameSize, sizeof(UInt32), &uint32val);
661 if (err != noErr) { errorMsg="kAudioDevicePropertyBufferFrameSize, Output"; goto error; }
665 err = AudioUnitSetProperty(_auhal, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, AUHAL_INPUT_ELEMENT, &uint32val, sizeof(UInt32));
666 if (err != noErr) { errorMsg="kAudioOutputUnitProperty_EnableIO, Input"; goto error; }
668 err = AudioUnitSetProperty(_auhal, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, AUHAL_OUTPUT_ELEMENT, &uint32val, sizeof(UInt32));
669 if (err != noErr) { errorMsg="kAudioOutputUnitProperty_EnableIO, Output"; goto error; }
671 err = AudioUnitSetProperty(_auhal, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, AUHAL_OUTPUT_ELEMENT, &_device_ids[device_id_out], sizeof(AudioDeviceID));
672 if (err != noErr) { errorMsg="kAudioOutputUnitProperty_CurrentDevice, Output"; goto error; }
674 err = AudioUnitSetProperty(_auhal, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, AUHAL_INPUT_ELEMENT, &_device_ids[device_id_in], sizeof(AudioDeviceID));
675 if (err != noErr) { errorMsg="kAudioOutputUnitProperty_CurrentDevice, Input"; goto error; }
678 err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, AUHAL_INPUT_ELEMENT, (UInt32*)&_max_samples_per_period, sizeof(UInt32));
679 if (err != noErr) { errorMsg="kAudioUnitProperty_MaximumFramesPerSlice, Input"; goto error; }
680 err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, AUHAL_OUTPUT_ELEMENT, (UInt32*)&_max_samples_per_period, sizeof(UInt32));
681 if (err != noErr) { errorMsg="kAudioUnitProperty_MaximumFramesPerSlice, Output"; goto error; }
684 srcFormat.mSampleRate = sample_rate;
685 srcFormat.mFormatID = kAudioFormatLinearPCM;
686 srcFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved;
687 srcFormat.mBytesPerPacket = sizeof(float);
688 srcFormat.mFramesPerPacket = 1;
689 srcFormat.mBytesPerFrame = sizeof(float);
690 srcFormat.mChannelsPerFrame = _device_ins[device_id_in];
691 srcFormat.mBitsPerChannel = 32;
694 property_address = { kAudioDevicePropertyStreamFormat, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
695 err = AudioObjectSetPropertyData (_device_ids[device_id_in], &property_address, 0, NULL, sizeof(AudioStreamBasicDescription), &srcFormat);
697 err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, AUHAL_INPUT_ELEMENT, &srcFormat, sizeof(AudioStreamBasicDescription));
699 if (err != noErr) { errorMsg="kAudioUnitProperty_StreamFormat, Output"; goto error; }
701 dstFormat.mSampleRate = sample_rate;
702 dstFormat.mFormatID = kAudioFormatLinearPCM;
703 dstFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved;
704 dstFormat.mBytesPerPacket = sizeof(float);
705 dstFormat.mFramesPerPacket = 1;
706 dstFormat.mBytesPerFrame = sizeof(float);
707 dstFormat.mChannelsPerFrame = _device_outs[device_id_out];
708 dstFormat.mBitsPerChannel = 32;
711 property_address = { kAudioDevicePropertyStreamFormat, kAudioDevicePropertyScopeInput, 0 };
712 err = AudioObjectSetPropertyData (_device_ids[device_id_out], &property_address, 0, NULL, sizeof(AudioStreamBasicDescription), &dstFormat);
714 err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, AUHAL_OUTPUT_ELEMENT, &dstFormat, sizeof(AudioStreamBasicDescription));
716 if (err != noErr) { errorMsg="kAudioUnitProperty_StreamFormat Input"; goto error; }
719 size = sizeof(AudioStreamBasicDescription);
720 err = AudioUnitGetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, AUHAL_INPUT_ELEMENT, &srcFormat, &size);
721 if (err != noErr) { errorMsg="Get kAudioUnitProperty_StreamFormat, Output"; goto error; }
722 _capture_channels = srcFormat.mChannelsPerFrame;
724 PrintStreamDesc(&srcFormat);
727 size = sizeof(AudioStreamBasicDescription);
728 err = AudioUnitGetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, AUHAL_OUTPUT_ELEMENT, &dstFormat, &size);
729 if (err != noErr) { errorMsg="Get kAudioUnitProperty_StreamFormat, Input"; goto error; }
730 _playback_channels = dstFormat.mChannelsPerFrame;
733 PrintStreamDesc(&dstFormat);
736 _input_audio_buffer_list = (AudioBufferList*)malloc(sizeof(UInt32) + _capture_channels * sizeof(AudioBuffer));
737 assert(_input_audio_buffer_list);
738 if (!_input_audio_buffer_list) { errorMsg="Out of Memory."; goto error; }
741 AURenderCallbackStruct renderCallback;
742 memset (&renderCallback, 0, sizeof (renderCallback));
743 renderCallback.inputProc = render_callback_ptr;
744 renderCallback.inputProcRefCon = this;
745 err = AudioUnitSetProperty(_auhal,
746 kAudioUnitProperty_SetRenderCallback,
747 kAudioUnitScope_Output, AUHAL_OUTPUT_ELEMENT,
748 &renderCallback, sizeof (renderCallback));
749 if (err != noErr) { errorMsg="kAudioUnitProperty_SetRenderCallback"; goto error; }
751 if (AudioOutputUnitStart(_auhal) == noErr) {
752 _input_names.clear();
753 _output_names.clear();
754 cache_port_names( device_id_in, true);
755 cache_port_names( device_id_out, false);
761 char *rv = (char*)&err;
762 fprintf(stderr, "CoreaudioPCM Error: %c%c%c%c %s\n", rv[0], rv[1], rv[2], rv[3], errorMsg.c_str());
769 CoreAudioPCM::cache_port_names(uint32 device_id, bool input)
772 assert (device_id < _n_devices);
775 n_chn = _capture_channels;
777 n_chn = _playback_channels;;
780 AudioObjectPropertyAddress property_address;
781 property_address.mSelector = kAudioObjectPropertyElementName;
782 property_address.mScope = input ? kAudioDevicePropertyScopeInput: kAudioDevicePropertyScopeOutput;
784 const int elem = input ? AUHAL_INPUT_ELEMENT : AUHAL_OUTPUT_ELEMENT;
787 for (uint32_t c = 0; c < n_chn; ++c) {
788 CFStringRef name = NULL;
789 std::stringstream ss;
794 property_address.mElement = c + 1;
795 err = AudioObjectGetPropertyDataSize(_device_ids[device_id], &property_address, 0, NULL, &size);
797 err = AudioDeviceGetPropertyInfo (_device_ids[device_id], c + 1, elem,
798 kAudioDevicePropertyChannelNameCFString,
803 if (err == kAudioHardwareNoError) {
805 err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, c + 1, NULL, &size, &name);
807 err = AudioDeviceGetProperty (_device_ids[device_id], c + 1, elem,
808 kAudioDevicePropertyChannelNameCFString,
814 bool decoded = false;
816 if (err == kAudioHardwareNoError) {
817 CFIndex length = CFStringGetLength(name);
818 CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8);
819 cstr_name = new char[maxSize];
820 decoded = CFStringGetCString(name, cstr_name, maxSize, kCFStringEncodingUTF8);
823 ss << (c + 1) << " - ";
825 if (cstr_name && decoded && (0 != std::strlen(cstr_name) ) ) {
829 ss << "Input " << (c + 1);
831 ss << "Output " << (c + 1);
835 printf("%s %d Name: %s\n", input ? "Input" : "Output", c+1, ss.str().c_str());
838 _input_names.push_back (ss.str());
840 _output_names.push_back (ss.str());
851 CoreAudioPCM::cached_port_name(uint32 port, bool input) const
853 if (_state != 0) { return ""; }
856 if (port > _input_names.size()) {
859 return _input_names[port];
861 if (port > _output_names.size()) {
864 return _output_names[port];
870 CoreAudioPCM::render_callback (
871 AudioUnitRenderActionFlags* ioActionFlags,
872 const AudioTimeStamp* inTimeStamp,
874 UInt32 inNumberFrames,
875 AudioBufferList* ioData)
879 assert(_max_samples_per_period >= inNumberFrames);
880 assert(ioData->mNumberBuffers = _playback_channels);
882 _cur_samples_per_period = inNumberFrames;
885 _input_audio_buffer_list->mNumberBuffers = _capture_channels;
886 for (uint32_t i = 0; i < _capture_channels; ++i) {
887 _input_audio_buffer_list->mBuffers[i].mNumberChannels = 1;
888 _input_audio_buffer_list->mBuffers[i].mDataByteSize = inNumberFrames * sizeof(float);
889 _input_audio_buffer_list->mBuffers[i].mData = NULL;
892 retVal = AudioUnitRender(_auhal, ioActionFlags, inTimeStamp, AUHAL_INPUT_ELEMENT, inNumberFrames, _input_audio_buffer_list);
894 if (retVal != kAudioHardwareNoError) {
896 char *rv = (char*)&retVal;
897 printf("ERR %c%c%c%c\n", rv[0], rv[1], rv[2], rv[3]);
899 if (_error_callback) {
900 _error_callback(_error_arg);
905 _output_audio_buffer_list = ioData;
911 if (_process_callback) {
912 rv = _process_callback(_process_arg);
919 for (uint32_t i = 0; i < ioData->mNumberBuffers; ++i) {
920 float* ob = (float*) ioData->mBuffers[i].mData;
921 memset(ob, 0, sizeof(float) * inNumberFrames);
928 CoreAudioPCM::get_capture_channel (uint32_t chn, float *input, uint32_t n_samples)
930 if (!_in_process || chn > _capture_channels || n_samples > _cur_samples_per_period) {
933 assert(_input_audio_buffer_list->mNumberBuffers > chn);
934 memcpy((void*)input, (void*)_input_audio_buffer_list->mBuffers[chn].mData, sizeof(float) * n_samples);
939 CoreAudioPCM::set_playback_channel (uint32_t chn, const float *output, uint32_t n_samples)
941 if (!_in_process || chn > _playback_channels || n_samples > _cur_samples_per_period) {
945 assert(_output_audio_buffer_list->mNumberBuffers > chn);
946 memcpy((void*)_output_audio_buffer_list->mBuffers[chn].mData, (void*)output, sizeof(float) * n_samples);
952 CoreAudioPCM::launch_control_app (uint32_t device_id)
954 if (device_id >= _n_devices) {
958 CFStringRef config_app = NULL;
959 UInt32 size = sizeof (config_app);
963 AudioObjectPropertyAddress property_address;
964 property_address.mSelector = kAudioDevicePropertyConfigurationApplication;
965 property_address.mScope = kAudioDevicePropertyScopeOutput;
966 property_address.mElement = kAudioObjectPropertyElementMaster;
967 err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, &config_app);
969 err = AudioDeviceGetProperty(_device_ids[device_id], 0, 0, kAudioDevicePropertyConfigurationApplication, &size, &config_app);
971 if (kAudioHardwareNoError != err) {
976 if (noErr == LSFindApplicationForInfo(kLSUnknownCreator, config_app, NULL, &appFSRef, NULL)) {
977 LSOpenFSRef(&appFSRef, NULL);
979 // open default AudioMIDISetup if device app is not found
980 CFStringRef audioMidiSetup = CFStringCreateWithCString(kCFAllocatorDefault, "com.apple.audio.AudioMIDISetup", kCFStringEncodingMacRoman);
981 if (noErr == LSFindApplicationForInfo(kLSUnknownCreator, audioMidiSetup, NULL, &appFSRef, NULL)) {
982 LSOpenFSRef(&appFSRef, NULL);
986 CFRelease (config_app);