cont’d work on the coreaudio backend & cleanup
[ardour.git] / libs / backends / coreaudio / coreaudio_pcmio.cc
1 /*
2  * Copyright (C) 2015 Robin Gareus <robin@gareus.org>
3  *
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.
8  *
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.
13  *
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.
17  */
18
19 #include <glibmm.h>
20 #include "coreaudio_pcmio.h"
21
22 #ifdef COREAUDIO_108
23 static OSStatus hardwarePropertyChangeCallback(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void* arg) {
24         CoreAudioPCM * self = static_cast<CoreAudioPCM*>(arg);
25         self->hwPropertyChange();
26         return noErr;
27 }
28 #else
29 static OSStatus hardwarePropertyChangeCallback (AudioHardwarePropertyID inPropertyID, void* arg) {
30         if (inPropertyID == kAudioHardwarePropertyDevices) {
31                 CoreAudioPCM * self = static_cast<CoreAudioPCM*>(arg);
32                 self->hwPropertyChange();
33         }
34         return noErr;
35 }
36 #endif
37
38 CoreAudioPCM::CoreAudioPCM ()
39         : _auhal (0)
40         , _device_ids (0)
41         , _input_audio_buffer_list (0)
42         , _state (-1)
43         , _capture_channels (0)
44         , _playback_channels (0)
45         , _in_process (false)
46         , _n_devices (0)
47         , _process_callback (0)
48         , _error_callback (0)
49         , _hw_changed_callback (0)
50         , _device_ins (0)
51         , _device_outs (0)
52 {
53         pthread_mutex_init (&_discovery_lock, 0);
54
55 #ifdef COREAUDIO_108
56         CFRunLoopRef theRunLoop = NULL;
57         AudioObjectPropertyAddress property = { kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal, kAudioHardwarePropertyDevices };
58         AudioObjectSetPropertyData (kAudioObjectSystemObject, &property, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);
59
60         AudioObjectPropertyAddress prop;
61         prop.mSelector = kAudioHardwarePropertyDevices;
62         prop.mScope = kAudioObjectPropertyScopeGlobal;
63         prop.mElement = 0;
64         AudioObjectAddPropertyListener(kAudioObjectSystemObject, &prop, hardwarePropertyChangeCallback, this);
65 #else
66         AudioHardwareAddPropertyListener (kAudioHardwarePropertyDevices, hardwarePropertyChangeCallback, this);
67 #endif
68 }
69
70 CoreAudioPCM::~CoreAudioPCM ()
71 {
72         if (_state == 0) {
73                 pcm_stop();
74         }
75         delete _device_ids;
76         free(_device_ins);
77         free(_device_outs);
78 #ifdef COREAUDIO_108
79         AudioObjectPropertyAddress prop;
80         prop.mSelector = kAudioHardwarePropertyDevices;
81         prop.mScope = kAudioObjectPropertyScopeGlobal;
82         prop.mElement = 0;
83         AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &prop, &hardwarePropertyChangeCallback, this);
84 #else
85         AudioHardwareRemovePropertyListener(kAudioHardwarePropertyDevices, hardwarePropertyChangeCallback);
86 #endif
87         free(_input_audio_buffer_list);
88         pthread_mutex_destroy (&_discovery_lock);
89 }
90
91
92 void
93 CoreAudioPCM::hwPropertyChange() {
94         discover();
95         // TODO Filter events..
96         if (_hw_changed_callback) {
97                 _hw_changed_callback(_hw_changed_arg);
98         }
99 }
100
101
102 int
103 CoreAudioPCM::available_sample_rates(uint32_t device_id, std::vector<float>& sampleRates)
104 {
105         OSStatus err;
106         UInt32 size = 0;
107         sampleRates.clear();
108
109         if (device_id >= _n_devices) {
110                 return -1;
111         }
112
113 #ifdef COREAUDIO_108
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);
119 #else
120         err = AudioDeviceGetPropertyInfo(_device_ids[device_id], 0, 0, kAudioDevicePropertyAvailableNominalSampleRates, &size, NULL);
121 #endif
122
123         if (err != kAudioHardwareNoError) {
124                 return -1;
125         }
126
127         int numRates = size / sizeof(AudioValueRange);
128         AudioValueRange* supportedRates = new AudioValueRange[numRates];
129
130 #ifdef COREAUDIO_108
131         err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, supportedRates);
132 #else
133         err = AudioDeviceGetProperty(_device_ids[device_id], 0, 0, kAudioDevicePropertyAvailableNominalSampleRates, &size, supportedRates);
134 #endif
135
136         if (err != kAudioHardwareNoError) {
137                 delete [] supportedRates;
138                 return -1;
139         }
140
141         static const float ardourRates[] = { 8000.0, 22050.0, 24000.0, 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0};
142
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]);
148                                 break;
149                         }
150                 }
151         }
152
153         delete [] supportedRates;
154         return 0;
155 }
156
157 int
158 CoreAudioPCM::available_buffer_sizes(uint32_t device_id, std::vector<uint32_t>& bufferSizes)
159 {
160         OSStatus err;
161         UInt32 size = 0;
162         bufferSizes.clear();
163
164         if (device_id >= _n_devices) {
165                 return -1;
166         }
167
168         AudioValueRange supportedRange;
169         size = sizeof (AudioValueRange);
170
171 #ifdef COREAUDIO_108
172         AudioObjectPropertyAddress property_address;
173         property_address.mSelector = kAudioDevicePropertyBufferFrameSizeRange;
174         err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, &supportedRange);
175 #else
176         err = AudioDeviceGetProperty(_device_ids[device_id], 0, 0, kAudioDevicePropertyBufferFrameSizeRange, &size, &supportedRange);
177 #endif
178
179         if (err != kAudioHardwareNoError) {
180                 return -1;
181         }
182
183         static const uint32_t ardourSizes[] = { 16, 32, 64, 128, 256, 512, 1024, 2048, 4096 };
184
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]);
189                 }
190         }
191
192         if (bufferSizes.empty()) {
193                 bufferSizes.push_back ((uint32_t)supportedRange.mMinimum);
194                 bufferSizes.push_back ((uint32_t)supportedRange.mMaximum);
195         }
196         return 0;
197 }
198
199 uint32_t
200 CoreAudioPCM::available_channels(uint32_t device_id, bool input)
201 {
202         OSStatus err;
203         UInt32 size = 0;
204         AudioBufferList *bufferList = NULL;
205         uint32_t channel_count = 0;
206
207         if (device_id >= _n_devices) {
208                 return 0;
209         }
210
211         /* query number of inputs */
212 #ifdef COREAUDIO_108
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);
219                 return 0;
220         }
221
222         bufferList = (AudioBufferList *)(malloc(size));
223         assert(bufferList);
224         if (!bufferList) { fprintf(stderr, "OUT OF MEMORY\n"); return 0; }
225
226         err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, bufferList);
227
228 #else
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);
232                 return 0;
233         }
234
235         bufferList = (AudioBufferList *)(malloc(size));
236         assert(bufferList);
237         if (!bufferList) { fprintf(stderr, "OUT OF MEMORY\n"); return 0; }
238
239         bufferList->mNumberBuffers = 0;
240         err = AudioDeviceGetProperty(_device_ids[device_id], 0, input ? AUHAL_INPUT_ELEMENT : AUHAL_OUTPUT_ELEMENT, kAudioDevicePropertyStreamConfiguration, &size, bufferList);
241 #endif
242
243         if(kAudioHardwareNoError != err) {
244                 fprintf(stderr, "CoreaAudioPCM: kAudioDevicePropertyStreamConfiguration failed: %i\n", err);
245                 free(bufferList);
246                 return 0;
247         }
248
249         for(UInt32 j = 0; j < bufferList->mNumberBuffers; ++j) {
250                 channel_count += bufferList->mBuffers[j].mNumberChannels;
251         }
252         free(bufferList);
253         return channel_count;
254 }
255
256 void
257 CoreAudioPCM::get_stream_latencies(uint32 device_id, bool input, std::vector<uint32>& latencies)
258 {
259         OSStatus err;
260         UInt32 size = 0;
261
262         if (device_id >= _n_devices) {
263                 return;
264         }
265
266 #ifdef COREAUDIO_108
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);
272 #else
273         Boolean outWritable;
274         const int elem = input ? AUHAL_INPUT_ELEMENT : AUHAL_OUTPUT_ELEMENT;
275         err = AudioDeviceGetPropertyInfo(_device_ids[device_id], 0, elem, kAudioDevicePropertyStreams, &size, &outWritable);
276 #endif
277         if (err != noErr) {
278                 return;
279         }
280
281         uint32 stream_count = size / sizeof(UInt32);
282         AudioStreamID streamIDs[stream_count];
283
284 #ifdef COREAUDIO_108
285         err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, &streamIDs);
286 #else
287         err = AudioDeviceGetProperty(_device_ids[device_id], 0, elem, kAudioDevicePropertyStreams, &size, streamIDs);
288 #endif
289         if (err != noErr) {
290                 fprintf(stderr, "GetStreamLatencies kAudioDevicePropertyStreams\n");
291                 return;
292         }
293
294         for (uint32 i = 0; i < stream_count; i++) {
295                 UInt32 stream_latency;
296                 size = sizeof(UInt32);
297 #ifdef COREAUDIO_108
298                 property_address.mSelector = kAudioDevicePropertyStreams;
299                 err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, &stream_latency);
300 #else
301                 err = AudioStreamGetProperty(streamIDs[i], elem, kAudioStreamPropertyLatency, &size, &stream_latency);
302 #endif
303                 if (err != noErr) {
304                         fprintf(stderr, "GetStreamLatencies kAudioStreamPropertyLatency\n");
305                         return;
306                 }
307 #ifndef NDEBUG
308                 printf("Stream %d latency: %d\n", i, stream_latency);
309 #endif
310                 latencies.push_back(stream_latency);
311         }
312 }
313
314 uint32_t
315 CoreAudioPCM::get_latency(uint32 device_id, bool input)
316 {
317         OSStatus err;
318         uint32 latency = 0;
319         UInt32 size = sizeof(UInt32);
320         UInt32 lat0 = 0;
321         UInt32 latS = 0;
322
323         if (device_id >= _n_devices) {
324                 return 0;
325         }
326
327 #ifdef COREAUDIO_108
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);
333 #else
334         const int elem = input ? AUHAL_INPUT_ELEMENT : AUHAL_OUTPUT_ELEMENT;
335         err = AudioDeviceGetProperty(_device_ids[device_id], 0, elem, kAudioDevicePropertyLatency, &size, &lat0);
336 #endif
337         if (err != kAudioHardwareNoError) {
338                 fprintf(stderr, "GetLatency kAudioDevicePropertyLatency\n");
339         }
340
341 #ifdef COREAUDIO_108
342         property_address.mSelector = kAudioDevicePropertySafetyOffset;
343         err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, &latS);
344 #else
345         err = AudioDeviceGetProperty(_device_ids[device_id], 0, elem, kAudioDevicePropertySafetyOffset, &size, &latS);
346 #endif
347         if (err != kAudioHardwareNoError) {
348                 fprintf(stderr, "GetLatency kAudioDevicePropertySafetyOffset\n");
349         }
350
351 #ifndef NDEBUG
352         printf("Base Latency systemic+safetyoffset = %d+%d\n", lat0, latS);
353 #endif
354         latency = lat0 + latS;
355
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]);
361         }
362         latency += max_stream_latency;
363
364         return latency;
365 }
366
367
368
369
370 float
371 CoreAudioPCM::current_sample_rate(uint32 device_id, bool input) {
372         OSStatus err;
373         UInt32 size = 0;
374
375         if (device_id >= _n_devices) {
376                 return -1;
377         }
378
379         float sample_rate = 0;
380
381         Float64 rate;
382         size = sizeof (rate);
383
384 #ifdef COREAUDIO_108
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);
389 #else
390         err = AudioDeviceGetPropertyInfo(_device_ids[device_id], 0, input ? AUHAL_INPUT_ELEMENT : AUHAL_OUTPUT_ELEMENT, kAudioDevicePropertyNominalSampleRate, &size, &rate);
391 #endif
392
393         if (err == kAudioHardwareNoError) {
394                 sample_rate = rate;
395         }
396
397         // prefer input, if vailable
398
399 #ifdef COREAUDIO_108
400         property_address.mSelector = kAudioDevicePropertyNominalSampleRate;
401         property_address.mScope = kAudioDevicePropertyScopeInput;
402         err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, 0, NULL, &size, &rate);
403 #else
404         err = AudioDeviceGetPropertyInfo(_device_ids[device_id], 0, AUHAL_INPUT_ELEMENT, kAudioDevicePropertyNominalSampleRate, &size, &rate);
405 #endif
406
407         if (err == kAudioHardwareNoError) {
408                 sample_rate = rate;
409         }
410
411         return sample_rate;
412 }
413
414 int
415 CoreAudioPCM::set_device_sample_rate (uint32 device_id, float rate, bool input)
416 {
417         std::vector<int>::iterator intIter;
418         OSStatus err;
419         UInt32 size = 0;
420
421         if (current_sample_rate(device_id, input) == rate) {
422                 return 0;
423         }
424
425         Float64 newNominalRate = rate;
426         size = sizeof (Float64);
427
428 #ifdef COREAUDIO_108
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);
434 #else
435         err = AudioDeviceSetProperty(_device_ids[device_id], NULL, 0, input ? AUHAL_INPUT_ELEMENT : AUHAL_OUTPUT_ELEMENT, kAudioDevicePropertyNominalSampleRate, size, &newNominalRate);
436 #endif
437         if (err != noErr) {
438                 fprintf(stderr, "CoreAudioPCM: failed to set samplerate\n");
439                 return 0;
440         }
441
442         int timeout = 3000; // 3 sec
443         while (--timeout > 0) {
444                 if (current_sample_rate(device_id) == rate) {
445                         break;
446                 }
447                 Glib::usleep (1000);
448         }
449         fprintf(stderr, "CoreAudioPCM: CoreAudio: Setting SampleRate took %d ms.\n", (3000 - timeout));
450
451         if (timeout == 0) {
452                 fprintf(stderr, "CoreAudioPCM: CoreAudio: Setting SampleRate timed out.\n");
453                 return -1;
454         }
455
456         return 0;
457 }
458
459 void
460 CoreAudioPCM::discover()
461 {
462         OSStatus err;
463         UInt32 size = 0;
464
465         if (pthread_mutex_trylock (&_discovery_lock)) {
466                 return;
467         }
468
469         if (_device_ids) {
470                 delete _device_ids; _device_ids = 0;
471                 free(_device_ins); _device_ins = 0;
472                 free(_device_outs); _device_outs = 0;
473         }
474         _devices.clear();
475
476 #ifdef COREAUDIO_108
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);
482 #else
483         err = AudioHardwareGetPropertyInfo (kAudioHardwarePropertyDevices, &size, NULL);
484 #endif
485
486         _n_devices = size / sizeof (AudioDeviceID);
487         size = _n_devices * sizeof (AudioDeviceID);
488
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));
492
493         assert(_device_ins && _device_outs && _device_ids);
494         if (!_device_ins || !_device_ins || !_device_ids) {
495                 fprintf(stderr, "OUT OF MEMORY\n");
496                 _device_ids = 0;
497                 _device_ins = 0;
498                 _device_outs = 0;
499                 pthread_mutex_unlock (&_discovery_lock);
500                 return;
501         }
502
503
504 #ifdef COREAUDIO_108
505         property_address.mSelector = kAudioHardwarePropertyDevices;
506         err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property_address, 0, NULL, &size, _device_ids);
507 #else
508         err = AudioHardwareGetProperty (kAudioHardwarePropertyDevices, &size, _device_ids);
509 #endif
510
511         for (size_t idx = 0; idx < _n_devices; ++idx) {
512                 size = 64;
513                 char deviceName[64];
514 #ifdef COREAUDIO_108
515                 property_address.mSelector = kAudioDevicePropertyDeviceName;
516                 property_address.mScope = kAudioDevicePropertyScopeOutput;
517                 err = AudioObjectGetPropertyData(_device_ids[idx], &property_address, 0, NULL, &size, deviceName);
518 #else
519                 err = AudioDeviceGetProperty(_device_ids[idx], 0, 0, kAudioDevicePropertyDeviceName, &size, deviceName);
520 #endif
521
522                 if (kAudioHardwareNoError != err) {
523                         fprintf(stderr, "CoreAudioPCM: device name query failed: %i\n", err);
524                         continue;
525                 }
526
527                 UInt32 inputChannelCount = available_channels(idx, true);
528                 UInt32 outputChannelCount = available_channels(idx, false);
529
530                 {
531                         std::string dn = deviceName;
532                         _device_ins[idx] = inputChannelCount;
533                         _device_outs[idx] = outputChannelCount;
534 #ifndef NDEBUG
535                         printf("CoreAudio Device: #%ld '%s' in:%d out:%d\n", idx, deviceName, inputChannelCount, outputChannelCount);
536 #endif
537                         if (outputChannelCount > 0 && inputChannelCount > 0) {
538                                 _devices.insert (std::pair<size_t, std::string> (idx, dn));
539                         }
540                 }
541         }
542         pthread_mutex_unlock (&_discovery_lock);
543 }
544
545 void
546 CoreAudioPCM::pcm_stop ()
547 {
548         if (!_auhal) return;
549
550         AudioOutputUnitStop(_auhal);
551         AudioUnitUninitialize(_auhal);
552 #ifdef COREAUDIO_108
553         AudioComponentInstanceDispose(_auhal);
554 #else
555         CloseComponent(_auhal);
556 #endif
557         _auhal = 0;
558         _state = -1;
559         _capture_channels = 0;
560         _playback_channels = 0;
561
562         free(_input_audio_buffer_list);
563         _input_audio_buffer_list = 0;
564
565         _input_names.clear();
566         _output_names.clear();
567
568         _error_callback = 0;
569         _process_callback = 0;
570 }
571
572 #ifndef NDEBUG
573 static void PrintStreamDesc (AudioStreamBasicDescription *inDesc)
574 {
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");
585 }
586 #endif
587
588 static OSStatus render_callback_ptr (
589                 void* inRefCon,
590                 AudioUnitRenderActionFlags* ioActionFlags,
591                 const AudioTimeStamp* inTimeStamp,
592                 UInt32 inBusNumber,
593                 UInt32 inNumberFrames,
594                 AudioBufferList* ioData)
595 {
596         CoreAudioPCM * d = static_cast<CoreAudioPCM*> (inRefCon);
597         return d->render_callback(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
598 }
599
600
601
602 int
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)
607 {
608
609         assert(_device_ids);
610         std::string errorMsg;
611         _state = -2;
612
613         if (device_id_out >= _n_devices || device_id_in >= _n_devices) {
614                 return -1;
615         }
616
617         _process_callback = process_callback;
618         _process_arg = process_arg;
619         _max_samples_per_period = samples_per_period;
620         _cur_samples_per_period = 0;
621
622         ComponentResult err;
623         UInt32 uint32val;
624         AudioStreamBasicDescription srcFormat, dstFormat;
625
626         AudioComponentDescription cd = {kAudioUnitType_Output, kAudioUnitSubType_HALOutput, kAudioUnitManufacturer_Apple, 0, 0};
627         AudioComponent HALOutput = AudioComponentFindNext(NULL, &cd);
628         if (!HALOutput) { errorMsg="AudioComponentFindNext"; goto error; }
629
630         err = AudioComponentInstanceNew(HALOutput, &_auhal);
631         if (err != noErr) { errorMsg="AudioComponentInstanceNew"; goto error; }
632
633         err = AudioUnitInitialize(_auhal);
634         if (err != noErr) { errorMsg="AudioUnitInitialize"; goto error; }
635
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;
639         }
640         if (set_device_sample_rate(device_id_out, sample_rate, false)) {
641                 errorMsg="Failed to set SampleRate, Playback Device"; goto error;
642         }
643
644         // explicitly request device buffer size
645         uint32val = samples_per_period;
646 #ifdef COREAUDIO_108
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; }
653
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; }
657 #else
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; }
662 #endif
663
664         uint32val = 1;
665         err = AudioUnitSetProperty(_auhal, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, AUHAL_INPUT_ELEMENT, &uint32val, sizeof(UInt32));
666         if (err != noErr) { errorMsg="kAudioOutputUnitProperty_EnableIO, Input"; goto error; }
667         uint32val = 1;
668         err = AudioUnitSetProperty(_auhal, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, AUHAL_OUTPUT_ELEMENT, &uint32val, sizeof(UInt32));
669         if (err != noErr) { errorMsg="kAudioOutputUnitProperty_EnableIO, Output"; goto error; }
670
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; }
673
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; }
676
677         // Set buffer size
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; }
682
683         // set sample format
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;
692
693 #if 0
694         property_address = { kAudioDevicePropertyStreamFormat, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
695         err = AudioObjectSetPropertyData (_device_ids[device_id_in], &property_address, 0, NULL, sizeof(AudioStreamBasicDescription), &srcFormat);
696 #else
697         err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, AUHAL_INPUT_ELEMENT, &srcFormat, sizeof(AudioStreamBasicDescription));
698 #endif
699         if (err != noErr) { errorMsg="kAudioUnitProperty_StreamFormat, Output"; goto error; }
700
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;
709
710 #if 0
711         property_address = { kAudioDevicePropertyStreamFormat, kAudioDevicePropertyScopeInput, 0 };
712         err = AudioObjectSetPropertyData (_device_ids[device_id_out], &property_address, 0, NULL, sizeof(AudioStreamBasicDescription), &dstFormat);
713 #else
714         err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, AUHAL_OUTPUT_ELEMENT, &dstFormat, sizeof(AudioStreamBasicDescription));
715 #endif
716         if (err != noErr) { errorMsg="kAudioUnitProperty_StreamFormat Input"; goto error; }
717
718         UInt32 size;
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;
723 #ifndef NDEBUG
724         PrintStreamDesc(&srcFormat);
725 #endif
726
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;
731
732 #ifndef NDEBUG
733         PrintStreamDesc(&dstFormat);
734 #endif
735
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; }
739
740         // Setup callbacks
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; }
750
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);
756                 _state = 0;
757                 return 0;
758         }
759
760 error:
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());
763         pcm_stop();
764         _state = -3;
765         return -1;
766 }
767
768 void
769 CoreAudioPCM::cache_port_names(uint32 device_id, bool input)
770 {
771         uint32_t n_chn;
772         assert (device_id < _n_devices);
773
774         if (input) {
775                 n_chn = _capture_channels;
776         } else {
777                 n_chn = _playback_channels;;
778         }
779 #ifdef COREAUDIO_108
780         AudioObjectPropertyAddress property_address;
781         property_address.mSelector = kAudioObjectPropertyElementName;
782         property_address.mScope = input ? kAudioDevicePropertyScopeInput: kAudioDevicePropertyScopeOutput;
783 #else
784         const int elem = input ? AUHAL_INPUT_ELEMENT : AUHAL_OUTPUT_ELEMENT;
785 #endif
786
787         for (uint32_t c = 0; c < n_chn; ++c) {
788                 CFStringRef name = NULL;
789                 std::stringstream ss;
790                 UInt32 size = 0;
791                 OSStatus err;
792
793 #ifdef COREAUDIO_108
794                 property_address.mElement = c + 1;
795                 err = AudioObjectGetPropertyDataSize(_device_ids[device_id], &property_address, 0, NULL, &size);
796 #else
797                 err = AudioDeviceGetPropertyInfo (_device_ids[device_id], c + 1, elem,
798                                 kAudioDevicePropertyChannelNameCFString,
799                                 &size,
800                                 NULL);
801 #endif
802
803                 if (err == kAudioHardwareNoError) {
804 #ifdef COREAUDIO_108
805                         err = AudioObjectGetPropertyData(_device_ids[device_id], &property_address, c + 1, NULL, &size, &name);
806 #else
807                         err = AudioDeviceGetProperty (_device_ids[device_id], c + 1, elem,
808                                         kAudioDevicePropertyChannelNameCFString,
809                                         &size,
810                                         &name);
811 #endif
812                 }
813  
814                 bool decoded = false;
815                 char* cstr_name = 0;
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);
821                 }
822
823                 ss << (c + 1) << " - ";
824
825                 if (cstr_name && decoded && (0 != std::strlen(cstr_name) ) ) {
826                         ss << cstr_name;
827                 } else {
828                         if (input) {
829                                 ss << "Input " << (c + 1);
830                         } else {
831                                 ss << "Output " << (c + 1);
832                         }
833                 }
834
835                 printf("%s %d Name: %s\n", input ? "Input" : "Output", c+1, ss.str().c_str());
836
837                 if (input) {
838                         _input_names.push_back (ss.str());
839                 } else {
840                         _output_names.push_back (ss.str());
841                 }
842
843                 if (name) {
844                         CFRelease (name);
845                 }
846                 delete [] cstr_name;
847         }
848 }
849
850 std::string
851 CoreAudioPCM::cached_port_name(uint32 port, bool input) const
852 {
853         if (_state != 0) { return ""; }
854
855         if (input) {
856                 if (port > _input_names.size()) {
857                         return "";
858                 }
859                 return _input_names[port];
860         } else {
861                 if (port > _output_names.size()) {
862                         return "";
863                 }
864                 return _output_names[port];
865         }
866 }
867
868
869 OSStatus
870 CoreAudioPCM::render_callback (
871                 AudioUnitRenderActionFlags* ioActionFlags,
872                 const AudioTimeStamp* inTimeStamp,
873                 UInt32 inBusNumber,
874                 UInt32 inNumberFrames,
875                 AudioBufferList* ioData)
876 {
877         OSStatus retVal = 0;
878
879         assert(_max_samples_per_period >= inNumberFrames);
880         assert(ioData->mNumberBuffers = _playback_channels);
881
882         _cur_samples_per_period = inNumberFrames;
883
884
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;
890         }
891
892         retVal = AudioUnitRender(_auhal, ioActionFlags, inTimeStamp, AUHAL_INPUT_ELEMENT, inNumberFrames, _input_audio_buffer_list);
893
894         if (retVal != kAudioHardwareNoError) {
895 #if 0
896                 char *rv = (char*)&retVal;
897                 printf("ERR %c%c%c%c\n", rv[0], rv[1], rv[2], rv[3]);
898 #endif
899                 if (_error_callback) {
900                         _error_callback(_error_arg);
901                 }
902                 return retVal;
903         }
904
905         _output_audio_buffer_list = ioData;
906
907         _in_process = true;
908
909         int rv = -1;
910
911         if (_process_callback) {
912                 rv = _process_callback(_process_arg);
913         }
914
915         _in_process = false;
916
917         if (rv != 0) {
918                 // clear output
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);
922                 }
923         }
924         return noErr;
925 }
926
927 int
928 CoreAudioPCM::get_capture_channel (uint32_t chn, float *input, uint32_t n_samples)
929 {
930         if (!_in_process || chn > _capture_channels || n_samples > _cur_samples_per_period) {
931                 return -1;
932         }
933         assert(_input_audio_buffer_list->mNumberBuffers > chn);
934         memcpy((void*)input, (void*)_input_audio_buffer_list->mBuffers[chn].mData, sizeof(float) * n_samples);
935         return 0;
936
937 }
938 int
939 CoreAudioPCM::set_playback_channel (uint32_t chn, const float *output, uint32_t n_samples)
940 {
941         if (!_in_process || chn > _playback_channels || n_samples > _cur_samples_per_period) {
942                 return -1;
943         }
944
945         assert(_output_audio_buffer_list->mNumberBuffers > chn);
946         memcpy((void*)_output_audio_buffer_list->mBuffers[chn].mData, (void*)output, sizeof(float) * n_samples);
947         return 0;
948 }
949
950
951 void
952 CoreAudioPCM::launch_control_app (uint32_t device_id)
953 {
954         if (device_id >= _n_devices) {
955                 return;
956         }
957
958         CFStringRef config_app = NULL;
959         UInt32 size = sizeof (config_app);
960         OSStatus err;
961
962 #ifdef COREAUDIO_108
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);
968 #else
969         err = AudioDeviceGetProperty(_device_ids[device_id], 0, 0, kAudioDevicePropertyConfigurationApplication, &size, &config_app);
970 #endif
971         if (kAudioHardwareNoError != err) {
972                 return;
973         }
974
975         FSRef appFSRef;
976         if (noErr == LSFindApplicationForInfo(kLSUnknownCreator, config_app, NULL, &appFSRef, NULL)) {
977                 LSOpenFSRef(&appFSRef, NULL);
978         } else {
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);
983                 }
984         }
985         if (config_app) {
986                 CFRelease (config_app);
987         }
988 }