1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2017 - ROLI Ltd.
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
15
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18 DISCLAIMED.
19
20 ==============================================================================
21 */
22
23 namespace juce
24 {
25
26 #if JUCE_COREAUDIO_LOGGING_ENABLED
27 #define JUCE_COREAUDIOLOG(a) { String camsg ("CoreAudio: "); camsg << a; Logger::writeToLog (camsg); }
28 #else
29 #define JUCE_COREAUDIOLOG(a)
30 #endif
31
32 #ifdef __clang__
33 #pragma clang diagnostic push
34 #pragma clang diagnostic ignored "-Wnonnull" // avoid some spurious 10.11 SDK warnings
35 #endif
36
37 //==============================================================================
38 struct SystemVol
39 {
SystemVoljuce::SystemVol40 SystemVol (AudioObjectPropertySelector selector) noexcept
41 : outputDeviceID (kAudioObjectUnknown)
42 {
43 addr.mScope = kAudioObjectPropertyScopeGlobal;
44 addr.mElement = kAudioObjectPropertyElementMaster;
45 addr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
46
47 if (AudioObjectHasProperty (kAudioObjectSystemObject, &addr))
48 {
49 UInt32 deviceIDSize = sizeof (outputDeviceID);
50 OSStatus status = AudioObjectGetPropertyData (kAudioObjectSystemObject, &addr, 0, nullptr, &deviceIDSize, &outputDeviceID);
51
52 if (status == noErr)
53 {
54 addr.mElement = kAudioObjectPropertyElementMaster;
55 addr.mSelector = selector;
56 addr.mScope = kAudioDevicePropertyScopeOutput;
57
58 if (! AudioObjectHasProperty (outputDeviceID, &addr))
59 outputDeviceID = kAudioObjectUnknown;
60 }
61 }
62 }
63
getGainjuce::SystemVol64 float getGain() const noexcept
65 {
66 Float32 gain = 0;
67
68 if (outputDeviceID != kAudioObjectUnknown)
69 {
70 UInt32 size = sizeof (gain);
71 AudioObjectGetPropertyData (outputDeviceID, &addr, 0, nullptr, &size, &gain);
72 }
73
74 return (float) gain;
75 }
76
setGainjuce::SystemVol77 bool setGain (float gain) const noexcept
78 {
79 if (outputDeviceID != kAudioObjectUnknown && canSetVolume())
80 {
81 Float32 newVolume = gain;
82 UInt32 size = sizeof (newVolume);
83
84 return AudioObjectSetPropertyData (outputDeviceID, &addr, 0, nullptr, size, &newVolume) == noErr;
85 }
86
87 return false;
88 }
89
isMutedjuce::SystemVol90 bool isMuted() const noexcept
91 {
92 UInt32 muted = 0;
93
94 if (outputDeviceID != kAudioObjectUnknown)
95 {
96 UInt32 size = sizeof (muted);
97 AudioObjectGetPropertyData (outputDeviceID, &addr, 0, nullptr, &size, &muted);
98 }
99
100 return muted != 0;
101 }
102
setMutedjuce::SystemVol103 bool setMuted (bool mute) const noexcept
104 {
105 if (outputDeviceID != kAudioObjectUnknown && canSetVolume())
106 {
107 UInt32 newMute = mute ? 1 : 0;
108 UInt32 size = sizeof (newMute);
109
110 return AudioObjectSetPropertyData (outputDeviceID, &addr, 0, nullptr, size, &newMute) == noErr;
111 }
112
113 return false;
114 }
115
116 private:
117 AudioDeviceID outputDeviceID;
118 AudioObjectPropertyAddress addr;
119
canSetVolumejuce::SystemVol120 bool canSetVolume() const noexcept
121 {
122 Boolean isSettable = NO;
123 return AudioObjectIsPropertySettable (outputDeviceID, &addr, &isSettable) == noErr && isSettable;
124 }
125 };
126
127 #ifdef __clang__
128 #pragma clang diagnostic pop
129 #endif
130
131 #define JUCE_SYSTEMAUDIOVOL_IMPLEMENTED 1
getGain()132 float JUCE_CALLTYPE SystemAudioVolume::getGain() { return SystemVol (kAudioHardwareServiceDeviceProperty_VirtualMasterVolume).getGain(); }
setGain(float gain)133 bool JUCE_CALLTYPE SystemAudioVolume::setGain (float gain) { return SystemVol (kAudioHardwareServiceDeviceProperty_VirtualMasterVolume).setGain (gain); }
isMuted()134 bool JUCE_CALLTYPE SystemAudioVolume::isMuted() { return SystemVol (kAudioDevicePropertyMute).isMuted(); }
setMuted(bool mute)135 bool JUCE_CALLTYPE SystemAudioVolume::setMuted (bool mute) { return SystemVol (kAudioDevicePropertyMute).setMuted (mute); }
136
137 //==============================================================================
138 struct CoreAudioClasses
139 {
140
141 class CoreAudioIODeviceType;
142 class CoreAudioIODevice;
143
144 //==============================================================================
145 class CoreAudioInternal : private Timer
146 {
147 public:
CoreAudioInternal(CoreAudioIODevice & d,AudioDeviceID id,bool input,bool output)148 CoreAudioInternal (CoreAudioIODevice& d, AudioDeviceID id, bool input, bool output)
149 : owner (d),
150 deviceID (id),
151 isInputDevice (input),
152 isOutputDevice (output)
153 {
154 jassert (deviceID != 0);
155
156 updateDetailsFromDevice();
157 JUCE_COREAUDIOLOG ("Creating CoreAudioInternal\n"
158 << (isInputDevice ? (" inputDeviceId " + String (deviceID) + "\n") : "")
159 << (isOutputDevice ? (" outputDeviceId " + String (deviceID) + "\n") : "")
160 << getDeviceDetails().joinIntoString ("\n "));
161
162 AudioObjectPropertyAddress pa;
163 pa.mSelector = kAudioObjectPropertySelectorWildcard;
164 pa.mScope = kAudioObjectPropertyScopeWildcard;
165 pa.mElement = kAudioObjectPropertyElementWildcard;
166
167 AudioObjectAddPropertyListener (deviceID, &pa, deviceListenerProc, this);
168 }
169
~CoreAudioInternal()170 ~CoreAudioInternal() override
171 {
172 AudioObjectPropertyAddress pa;
173 pa.mSelector = kAudioObjectPropertySelectorWildcard;
174 pa.mScope = kAudioObjectPropertyScopeWildcard;
175 pa.mElement = kAudioObjectPropertyElementWildcard;
176
177 AudioObjectRemovePropertyListener (deviceID, &pa, deviceListenerProc, this);
178
179 stop (false);
180 }
181
allocateTempBuffers()182 void allocateTempBuffers()
183 {
184 auto tempBufSize = bufferSize + 4;
185 audioBuffer.calloc ((numInputChans + numOutputChans) * tempBufSize);
186
187 tempInputBuffers.calloc (numInputChans + 2);
188 tempOutputBuffers.calloc (numOutputChans + 2);
189
190 int count = 0;
191 for (int i = 0; i < numInputChans; ++i) tempInputBuffers[i] = audioBuffer + count++ * tempBufSize;
192 for (int i = 0; i < numOutputChans; ++i) tempOutputBuffers[i] = audioBuffer + count++ * tempBufSize;
193 }
194
195 struct CallbackDetailsForChannel
196 {
197 int streamNum;
198 int dataOffsetSamples;
199 int dataStrideSamples;
200 };
201
202 // returns the number of actual available channels
getChannelInfo(bool input,Array<CallbackDetailsForChannel> & newChannelInfo) const203 StringArray getChannelInfo (bool input, Array<CallbackDetailsForChannel>& newChannelInfo) const
204 {
205 StringArray newNames;
206 int chanNum = 0;
207 UInt32 size;
208
209 AudioObjectPropertyAddress pa;
210 pa.mSelector = kAudioDevicePropertyStreamConfiguration;
211 pa.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
212 pa.mElement = kAudioObjectPropertyElementMaster;
213
214 if (OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, nullptr, &size)))
215 {
216 HeapBlock<AudioBufferList> bufList;
217 bufList.calloc (size, 1);
218
219 if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, bufList)))
220 {
221 const int numStreams = (int) bufList->mNumberBuffers;
222
223 for (int i = 0; i < numStreams; ++i)
224 {
225 auto& b = bufList->mBuffers[i];
226
227 for (unsigned int j = 0; j < b.mNumberChannels; ++j)
228 {
229 String name;
230 NSString* nameNSString = nil;
231 size = sizeof (nameNSString);
232
233 pa.mSelector = kAudioObjectPropertyElementName;
234 pa.mElement = (AudioObjectPropertyElement) chanNum + 1;
235
236 if (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &nameNSString) == noErr)
237 {
238 name = nsStringToJuce (nameNSString);
239 [nameNSString release];
240 }
241
242 if ((input ? activeInputChans : activeOutputChans) [chanNum])
243 {
244 CallbackDetailsForChannel info = { i, (int) j, (int) b.mNumberChannels };
245 newChannelInfo.add (info);
246 }
247
248 if (name.isEmpty())
249 name << (input ? "Input " : "Output ") << (chanNum + 1);
250
251 newNames.add (name);
252 ++chanNum;
253 }
254 }
255 }
256 }
257
258 return newNames;
259 }
260
getSampleRatesFromDevice() const261 Array<double> getSampleRatesFromDevice() const
262 {
263 Array<double> newSampleRates;
264
265 AudioObjectPropertyAddress pa;
266 pa.mScope = kAudioObjectPropertyScopeWildcard;
267 pa.mElement = kAudioObjectPropertyElementMaster;
268 pa.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
269 UInt32 size = 0;
270
271 if (OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, nullptr, &size)))
272 {
273 HeapBlock<AudioValueRange> ranges;
274 ranges.calloc (size, 1);
275
276 if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, ranges)))
277 {
278 for (auto r : { 8000, 11025, 16000, 22050, 32000,
279 44100, 48000, 88200, 96000, 176400,
280 192000, 352800, 384000, 705600, 768000 })
281 {
282 auto rate = (double) r;
283
284 for (int j = size / (int) sizeof (AudioValueRange); --j >= 0;)
285 {
286 if (rate >= ranges[j].mMinimum - 2 && rate <= ranges[j].mMaximum + 2)
287 {
288 newSampleRates.add (rate);
289 break;
290 }
291 }
292 }
293 }
294 }
295
296 if (newSampleRates.isEmpty() && sampleRate > 0)
297 newSampleRates.add (sampleRate);
298
299 return newSampleRates;
300 }
301
getBufferSizesFromDevice() const302 Array<int> getBufferSizesFromDevice() const
303 {
304 Array<int> newBufferSizes;
305
306 AudioObjectPropertyAddress pa;
307 pa.mScope = kAudioObjectPropertyScopeWildcard;
308 pa.mElement = kAudioObjectPropertyElementMaster;
309 pa.mSelector = kAudioDevicePropertyBufferFrameSizeRange;
310 UInt32 size = 0;
311
312 if (OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, nullptr, &size)))
313 {
314 HeapBlock<AudioValueRange> ranges;
315 ranges.calloc (size, 1);
316
317 if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, ranges)))
318 {
319 newBufferSizes.add ((int) (ranges[0].mMinimum + 15) & ~15);
320
321 for (int i = 32; i <= 2048; i += 32)
322 {
323 for (int j = size / (int) sizeof (AudioValueRange); --j >= 0;)
324 {
325 if (i >= ranges[j].mMinimum && i <= ranges[j].mMaximum)
326 {
327 newBufferSizes.addIfNotAlreadyThere (i);
328 break;
329 }
330 }
331 }
332
333 if (bufferSize > 0)
334 newBufferSizes.addIfNotAlreadyThere (bufferSize);
335 }
336 }
337
338 if (newBufferSizes.isEmpty() && bufferSize > 0)
339 newBufferSizes.add (bufferSize);
340
341 return newBufferSizes;
342 }
343
getLatencyFromDevice(AudioObjectPropertyScope scope) const344 int getLatencyFromDevice (AudioObjectPropertyScope scope) const
345 {
346 UInt32 latency = 0;
347 UInt32 size = sizeof (latency);
348 AudioObjectPropertyAddress pa;
349 pa.mElement = kAudioObjectPropertyElementMaster;
350 pa.mSelector = kAudioDevicePropertyLatency;
351 pa.mScope = scope;
352 AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &latency);
353
354 UInt32 safetyOffset = 0;
355 size = sizeof (safetyOffset);
356 pa.mSelector = kAudioDevicePropertySafetyOffset;
357 AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &safetyOffset);
358
359 return (int) (latency + safetyOffset);
360 }
361
getBitDepthFromDevice(AudioObjectPropertyScope scope) const362 int getBitDepthFromDevice (AudioObjectPropertyScope scope) const
363 {
364 AudioObjectPropertyAddress pa;
365 pa.mElement = kAudioObjectPropertyElementMaster;
366 pa.mSelector = kAudioStreamPropertyPhysicalFormat;
367 pa.mScope = scope;
368
369 AudioStreamBasicDescription asbd;
370 UInt32 size = sizeof (asbd);
371
372 if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &asbd)))
373 return (int) asbd.mBitsPerChannel;
374
375 return 0;
376 }
377
getFrameSizeFromDevice() const378 int getFrameSizeFromDevice() const
379 {
380 AudioObjectPropertyAddress pa;
381 pa.mScope = kAudioObjectPropertyScopeWildcard;
382 pa.mElement = kAudioObjectPropertyElementMaster;
383 pa.mSelector = kAudioDevicePropertyBufferFrameSize;
384
385 UInt32 framesPerBuf = (UInt32) bufferSize;
386 UInt32 size = sizeof (framesPerBuf);
387 AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &framesPerBuf);
388 return (int) framesPerBuf;
389 }
390
isDeviceAlive() const391 bool isDeviceAlive() const
392 {
393 AudioObjectPropertyAddress pa;
394 pa.mScope = kAudioObjectPropertyScopeWildcard;
395 pa.mElement = kAudioObjectPropertyElementMaster;
396 pa.mSelector = kAudioDevicePropertyDeviceIsAlive;
397
398 UInt32 isAlive = 0;
399 UInt32 size = sizeof (isAlive);
400 return deviceID != 0
401 && OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &isAlive))
402 && isAlive != 0;
403 }
404
updateDetailsFromDevice()405 bool updateDetailsFromDevice()
406 {
407 stopTimer();
408
409 if (! isDeviceAlive())
410 return false;
411
412 // this collects all the new details from the device without any locking, then
413 // locks + swaps them afterwards.
414
415 auto newSampleRate = getNominalSampleRate();
416 auto newBufferSize = getFrameSizeFromDevice();
417
418 auto newBufferSizes = getBufferSizesFromDevice();
419 auto newSampleRates = getSampleRatesFromDevice();
420
421 auto newInputLatency = getLatencyFromDevice (kAudioDevicePropertyScopeInput);
422 auto newOutputLatency = getLatencyFromDevice (kAudioDevicePropertyScopeOutput);
423
424 Array<CallbackDetailsForChannel> newInChans, newOutChans;
425 auto newInNames = isInputDevice ? getChannelInfo (true, newInChans) : StringArray();
426 auto newOutNames = isOutputDevice ? getChannelInfo (false, newOutChans) : StringArray();
427
428 auto inputBitDepth = isInputDevice ? getBitDepthFromDevice (kAudioDevicePropertyScopeInput) : 0;
429 auto outputBitDepth = isOutputDevice ? getBitDepthFromDevice (kAudioDevicePropertyScopeOutput) : 0;
430 auto newBitDepth = jmax (inputBitDepth, outputBitDepth);
431
432 {
433 const ScopedLock sl (callbackLock);
434
435 bitDepth = newBitDepth > 0 ? newBitDepth : 32;
436
437 if (newSampleRate > 0)
438 sampleRate = newSampleRate;
439
440 inputLatency = newInputLatency;
441 outputLatency = newOutputLatency;
442 bufferSize = newBufferSize;
443
444 sampleRates.swapWith (newSampleRates);
445 bufferSizes.swapWith (newBufferSizes);
446
447 inChanNames.swapWith (newInNames);
448 outChanNames.swapWith (newOutNames);
449
450 inputChannelInfo.swapWith (newInChans);
451 outputChannelInfo.swapWith (newOutChans);
452
453 numInputChans = inputChannelInfo.size();
454 numOutputChans = outputChannelInfo.size();
455
456 allocateTempBuffers();
457 }
458
459 return true;
460 }
461
getDeviceDetails()462 StringArray getDeviceDetails()
463 {
464 StringArray result;
465
466 String availableSampleRates ("Available sample rates:");
467
468 for (auto& s : sampleRates)
469 availableSampleRates << " " << s;
470
471 result.add (availableSampleRates);
472 result.add ("Sample rate: " + String (sampleRate));
473 String availableBufferSizes ("Available buffer sizes:");
474
475 for (auto& b : bufferSizes)
476 availableBufferSizes << " " << b;
477
478 result.add (availableBufferSizes);
479 result.add ("Buffer size: " + String (bufferSize));
480 result.add ("Bit depth: " + String (bitDepth));
481 result.add ("Input latency: " + String (inputLatency));
482 result.add ("Output latency: " + String (outputLatency));
483 result.add ("Input channel names: " + inChanNames.joinIntoString (" "));
484 result.add ("Output channel names: " + outChanNames.joinIntoString (" "));
485
486 return result;
487 }
488
489 //==============================================================================
getSources(bool input)490 StringArray getSources (bool input)
491 {
492 StringArray s;
493 HeapBlock<OSType> types;
494 auto num = getAllDataSourcesForDevice (deviceID, types);
495
496 for (int i = 0; i < num; ++i)
497 {
498 AudioValueTranslation avt;
499 char buffer[256];
500
501 avt.mInputData = &(types[i]);
502 avt.mInputDataSize = sizeof (UInt32);
503 avt.mOutputData = buffer;
504 avt.mOutputDataSize = 256;
505
506 UInt32 transSize = sizeof (avt);
507
508 AudioObjectPropertyAddress pa;
509 pa.mSelector = kAudioDevicePropertyDataSourceNameForID;
510 pa.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
511 pa.mElement = kAudioObjectPropertyElementMaster;
512
513 if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &transSize, &avt)))
514 s.add (buffer);
515 }
516
517 return s;
518 }
519
getCurrentSourceIndex(bool input) const520 int getCurrentSourceIndex (bool input) const
521 {
522 OSType currentSourceID = 0;
523 UInt32 size = sizeof (currentSourceID);
524 int result = -1;
525
526 AudioObjectPropertyAddress pa;
527 pa.mSelector = kAudioDevicePropertyDataSource;
528 pa.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
529 pa.mElement = kAudioObjectPropertyElementMaster;
530
531 if (deviceID != 0)
532 {
533 if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, ¤tSourceID)))
534 {
535 HeapBlock<OSType> types;
536 auto num = getAllDataSourcesForDevice (deviceID, types);
537
538 for (int i = 0; i < num; ++i)
539 {
540 if (types[num] == currentSourceID)
541 {
542 result = i;
543 break;
544 }
545 }
546 }
547 }
548
549 return result;
550 }
551
setCurrentSourceIndex(int index,bool input)552 void setCurrentSourceIndex (int index, bool input)
553 {
554 if (deviceID != 0)
555 {
556 HeapBlock<OSType> types;
557 auto num = getAllDataSourcesForDevice (deviceID, types);
558
559 if (isPositiveAndBelow (index, num))
560 {
561 AudioObjectPropertyAddress pa;
562 pa.mSelector = kAudioDevicePropertyDataSource;
563 pa.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
564 pa.mElement = kAudioObjectPropertyElementMaster;
565
566 OSType typeId = types[index];
567
568 OK (AudioObjectSetPropertyData (deviceID, &pa, 0, nullptr, sizeof (typeId), &typeId));
569 }
570 }
571 }
572
getNominalSampleRate() const573 double getNominalSampleRate() const
574 {
575 AudioObjectPropertyAddress pa;
576 pa.mSelector = kAudioDevicePropertyNominalSampleRate;
577 pa.mScope = kAudioObjectPropertyScopeGlobal;
578 pa.mElement = kAudioObjectPropertyElementMaster;
579 Float64 sr = 0;
580 UInt32 size = (UInt32) sizeof (sr);
581 return OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &sr)) ? (double) sr : 0.0;
582 }
583
setNominalSampleRate(double newSampleRate) const584 bool setNominalSampleRate (double newSampleRate) const
585 {
586 if (std::abs (getNominalSampleRate() - newSampleRate) < 1.0)
587 return true;
588
589 AudioObjectPropertyAddress pa;
590 pa.mSelector = kAudioDevicePropertyNominalSampleRate;
591 pa.mScope = kAudioObjectPropertyScopeGlobal;
592 pa.mElement = kAudioObjectPropertyElementMaster;
593 Float64 sr = newSampleRate;
594 return OK (AudioObjectSetPropertyData (deviceID, &pa, 0, nullptr, sizeof (sr), &sr));
595 }
596
597 //==============================================================================
reopen(const BigInteger & inputChannels,const BigInteger & outputChannels,double newSampleRate,int bufferSizeSamples)598 String reopen (const BigInteger& inputChannels,
599 const BigInteger& outputChannels,
600 double newSampleRate, int bufferSizeSamples)
601 {
602 String error;
603 callbacksAllowed = false;
604 stopTimer();
605
606 stop (false);
607
608 updateDetailsFromDevice();
609
610 activeInputChans = inputChannels;
611 activeInputChans.setRange (inChanNames.size(),
612 activeInputChans.getHighestBit() + 1 - inChanNames.size(),
613 false);
614
615 activeOutputChans = outputChannels;
616 activeOutputChans.setRange (outChanNames.size(),
617 activeOutputChans.getHighestBit() + 1 - outChanNames.size(),
618 false);
619
620 numInputChans = activeInputChans.countNumberOfSetBits();
621 numOutputChans = activeOutputChans.countNumberOfSetBits();
622
623 if (! setNominalSampleRate (newSampleRate))
624 {
625 updateDetailsFromDevice();
626 error = "Couldn't change sample rate";
627 }
628 else
629 {
630 // change buffer size
631 AudioObjectPropertyAddress pa;
632 pa.mSelector = kAudioDevicePropertyBufferFrameSize;
633 pa.mScope = kAudioObjectPropertyScopeGlobal;
634 pa.mElement = kAudioObjectPropertyElementMaster;
635 UInt32 framesPerBuf = (UInt32) bufferSizeSamples;
636
637 if (! OK (AudioObjectSetPropertyData (deviceID, &pa, 0, nullptr, sizeof (framesPerBuf), &framesPerBuf)))
638 {
639 updateDetailsFromDevice();
640 error = "Couldn't change buffer size";
641 }
642 else
643 {
644 // Annoyingly, after changing the rate and buffer size, some devices fail to
645 // correctly report their new settings until some random time in the future, so
646 // after calling updateDetailsFromDevice, we need to manually bodge these values
647 // to make sure we're using the correct numbers..
648 updateDetailsFromDevice();
649 sampleRate = newSampleRate;
650 bufferSize = bufferSizeSamples;
651
652 if (sampleRates.size() == 0)
653 error = "Device has no available sample-rates";
654 else if (bufferSizes.size() == 0)
655 error = "Device has no available buffer-sizes";
656 }
657 }
658
659 callbacksAllowed = true;
660 return error;
661 }
662
start()663 bool start()
664 {
665 if (! started)
666 {
667 callback = nullptr;
668
669 if (deviceID != 0)
670 {
671 if (OK (AudioDeviceCreateIOProcID (deviceID, audioIOProc, this, &audioProcID)))
672 {
673 if (OK (AudioDeviceStart (deviceID, audioIOProc)))
674 {
675 started = true;
676 }
677 else
678 {
679 OK (AudioDeviceDestroyIOProcID (deviceID, audioProcID));
680 audioProcID = {};
681 }
682 }
683 }
684 }
685
686 return started;
687 }
688
setCallback(AudioIODeviceCallback * cb)689 void setCallback (AudioIODeviceCallback* cb)
690 {
691 const ScopedLock sl (callbackLock);
692 callback = cb;
693 }
694
stop(bool leaveInterruptRunning)695 void stop (bool leaveInterruptRunning)
696 {
697 {
698 const ScopedLock sl (callbackLock);
699 callback = nullptr;
700 }
701
702 if (started && (deviceID != 0) && ! leaveInterruptRunning)
703 {
704 OK (AudioDeviceStop (deviceID, audioIOProc));
705 OK (AudioDeviceDestroyIOProcID (deviceID, audioProcID));
706 audioProcID = {};
707
708 started = false;
709
710 { const ScopedLock sl (callbackLock); }
711
712 // wait until it's definitely stopped calling back..
713 for (int i = 40; --i >= 0;)
714 {
715 Thread::sleep (50);
716
717 UInt32 running = 0;
718 UInt32 size = sizeof (running);
719
720 AudioObjectPropertyAddress pa;
721 pa.mSelector = kAudioDevicePropertyDeviceIsRunning;
722 pa.mScope = kAudioObjectPropertyScopeWildcard;
723 pa.mElement = kAudioObjectPropertyElementMaster;
724
725 OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &running));
726
727 if (running == 0)
728 break;
729 }
730
731 const ScopedLock sl (callbackLock);
732 }
733 }
734
getSampleRate() const735 double getSampleRate() const { return sampleRate; }
getBufferSize() const736 int getBufferSize() const { return bufferSize; }
737
audioCallback(const AudioBufferList * inInputData,AudioBufferList * outOutputData)738 void audioCallback (const AudioBufferList* inInputData,
739 AudioBufferList* outOutputData)
740 {
741 const ScopedLock sl (callbackLock);
742
743 if (callback != nullptr)
744 {
745 for (int i = numInputChans; --i >= 0;)
746 {
747 auto& info = inputChannelInfo.getReference(i);
748 auto dest = tempInputBuffers[i];
749 auto src = ((const float*) inInputData->mBuffers[info.streamNum].mData) + info.dataOffsetSamples;
750 auto stride = info.dataStrideSamples;
751
752 if (stride != 0) // if this is zero, info is invalid
753 {
754 for (int j = bufferSize; --j >= 0;)
755 {
756 *dest++ = *src;
757 src += stride;
758 }
759 }
760 }
761
762 callback->audioDeviceIOCallback (const_cast<const float**> (tempInputBuffers.get()),
763 numInputChans,
764 tempOutputBuffers,
765 numOutputChans,
766 bufferSize);
767
768 for (int i = numOutputChans; --i >= 0;)
769 {
770 auto& info = outputChannelInfo.getReference (i);
771 auto src = tempOutputBuffers[i];
772 auto dest = ((float*) outOutputData->mBuffers[info.streamNum].mData) + info.dataOffsetSamples;
773 auto stride = info.dataStrideSamples;
774
775 if (stride != 0) // if this is zero, info is invalid
776 {
777 for (int j = bufferSize; --j >= 0;)
778 {
779 *dest = *src++;
780 dest += stride;
781 }
782 }
783 }
784 }
785 else
786 {
787 for (UInt32 i = 0; i < outOutputData->mNumberBuffers; ++i)
788 zeromem (outOutputData->mBuffers[i].mData,
789 outOutputData->mBuffers[i].mDataByteSize);
790 }
791 }
792
793 // called by callbacks
deviceDetailsChanged()794 void deviceDetailsChanged()
795 {
796 if (callbacksAllowed.get() == 1)
797 startTimer (100);
798 }
799
timerCallback()800 void timerCallback() override
801 {
802 JUCE_COREAUDIOLOG ("Device changed");
803
804 stopTimer();
805 auto oldSampleRate = sampleRate;
806 auto oldBufferSize = bufferSize;
807
808 if (! updateDetailsFromDevice())
809 owner.stopInternal();
810 else if ((oldBufferSize != bufferSize || oldSampleRate != sampleRate) && owner.shouldRestartDevice())
811 owner.restart();
812 }
813
814 //==============================================================================
815 CoreAudioIODevice& owner;
816 int inputLatency = 0;
817 int outputLatency = 0;
818 int bitDepth = 32;
819 int xruns = 0;
820 BigInteger activeInputChans, activeOutputChans;
821 StringArray inChanNames, outChanNames;
822 Array<double> sampleRates;
823 Array<int> bufferSizes;
824 AudioIODeviceCallback* callback = nullptr;
825 AudioDeviceIOProcID audioProcID = {};
826
827 private:
828 CriticalSection callbackLock;
829 AudioDeviceID deviceID;
830 bool started = false;
831 double sampleRate = 0;
832 int bufferSize = 512;
833 HeapBlock<float> audioBuffer;
834 int numInputChans = 0;
835 int numOutputChans = 0;
836 Atomic<int> callbacksAllowed { 1 };
837 const bool isInputDevice, isOutputDevice;
838
839 Array<CallbackDetailsForChannel> inputChannelInfo, outputChannelInfo;
840 HeapBlock<float*> tempInputBuffers, tempOutputBuffers;
841
842 //==============================================================================
audioIOProc(AudioDeviceID,const AudioTimeStamp *,const AudioBufferList * inInputData,const AudioTimeStamp *,AudioBufferList * outOutputData,const AudioTimeStamp *,void * device)843 static OSStatus audioIOProc (AudioDeviceID /*inDevice*/,
844 const AudioTimeStamp* /*inNow*/,
845 const AudioBufferList* inInputData,
846 const AudioTimeStamp* /*inInputTime*/,
847 AudioBufferList* outOutputData,
848 const AudioTimeStamp* /*inOutputTime*/,
849 void* device)
850 {
851 static_cast<CoreAudioInternal*> (device)->audioCallback (inInputData, outOutputData);
852 return noErr;
853 }
854
deviceListenerProc(AudioDeviceID,UInt32,const AudioObjectPropertyAddress * pa,void * inClientData)855 static OSStatus deviceListenerProc (AudioDeviceID /*inDevice*/, UInt32 /*inLine*/,
856 const AudioObjectPropertyAddress* pa, void* inClientData)
857 {
858 auto intern = static_cast<CoreAudioInternal*> (inClientData);
859
860 switch (pa->mSelector)
861 {
862 case kAudioDeviceProcessorOverload:
863 intern->xruns++;
864 break;
865
866 case kAudioDevicePropertyBufferSize:
867 case kAudioDevicePropertyBufferFrameSize:
868 case kAudioDevicePropertyNominalSampleRate:
869 case kAudioDevicePropertyStreamFormat:
870 case kAudioDevicePropertyDeviceIsAlive:
871 case kAudioStreamPropertyPhysicalFormat:
872 intern->deviceDetailsChanged();
873 break;
874
875 case kAudioDevicePropertyDeviceHasChanged:
876 case kAudioObjectPropertyOwnedObjects:
877 intern->owner.restart();
878
879 if (intern->owner.deviceType != nullptr)
880 intern->owner.deviceType->triggerAsyncAudioDeviceListChange();
881 break;
882
883 case kAudioDevicePropertyBufferSizeRange:
884 case kAudioDevicePropertyVolumeScalar:
885 case kAudioDevicePropertyMute:
886 case kAudioDevicePropertyPlayThru:
887 case kAudioDevicePropertyDataSource:
888 case kAudioDevicePropertyDeviceIsRunning:
889 break;
890 }
891
892 return noErr;
893 }
894
895 //==============================================================================
getAllDataSourcesForDevice(AudioDeviceID deviceID,HeapBlock<OSType> & types)896 static int getAllDataSourcesForDevice (AudioDeviceID deviceID, HeapBlock<OSType>& types)
897 {
898 AudioObjectPropertyAddress pa;
899 pa.mSelector = kAudioDevicePropertyDataSources;
900 pa.mScope = kAudioObjectPropertyScopeWildcard;
901 pa.mElement = kAudioObjectPropertyElementMaster;
902 UInt32 size = 0;
903
904 if (deviceID != 0
905 && AudioObjectGetPropertyDataSize (deviceID, &pa, 0, nullptr, &size) == noErr)
906 {
907 types.calloc (size, 1);
908
909 if (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, types) == noErr)
910 return size / (int) sizeof (OSType);
911 }
912
913 return 0;
914 }
915
OK(const OSStatus errorCode) const916 bool OK (const OSStatus errorCode) const
917 {
918 if (errorCode == noErr)
919 return true;
920
921 const String errorMessage ("CoreAudio error: " + String::toHexString ((int) errorCode));
922 JUCE_COREAUDIOLOG (errorMessage);
923
924 if (callback != nullptr)
925 callback->audioDeviceError (errorMessage);
926
927 return false;
928 }
929
930 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioInternal)
931 };
932
933
934 //==============================================================================
935 class CoreAudioIODevice : public AudioIODevice,
936 private Timer
937 {
938 public:
CoreAudioIODevice(CoreAudioIODeviceType * dt,const String & deviceName,AudioDeviceID inputDeviceId,int inputIndex_,AudioDeviceID outputDeviceId,int outputIndex_)939 CoreAudioIODevice (CoreAudioIODeviceType* dt,
940 const String& deviceName,
941 AudioDeviceID inputDeviceId, int inputIndex_,
942 AudioDeviceID outputDeviceId, int outputIndex_)
943 : AudioIODevice (deviceName, "CoreAudio"),
944 deviceType (dt),
945 inputIndex (inputIndex_),
946 outputIndex (outputIndex_)
947 {
948 CoreAudioInternal* device = nullptr;
949
950 if (outputDeviceId == 0 || outputDeviceId == inputDeviceId)
951 {
952 jassert (inputDeviceId != 0);
953 device = new CoreAudioInternal (*this, inputDeviceId, true, outputDeviceId != 0);
954 }
955 else
956 {
957 device = new CoreAudioInternal (*this, outputDeviceId, false, true);
958 }
959
960 jassert (device != nullptr);
961 internal.reset (device);
962
963 AudioObjectPropertyAddress pa;
964 pa.mSelector = kAudioObjectPropertySelectorWildcard;
965 pa.mScope = kAudioObjectPropertyScopeWildcard;
966 pa.mElement = kAudioObjectPropertyElementWildcard;
967
968 AudioObjectAddPropertyListener (kAudioObjectSystemObject, &pa, hardwareListenerProc, internal.get());
969 }
970
~CoreAudioIODevice()971 ~CoreAudioIODevice() override
972 {
973 close();
974
975 AudioObjectPropertyAddress pa;
976 pa.mSelector = kAudioObjectPropertySelectorWildcard;
977 pa.mScope = kAudioObjectPropertyScopeWildcard;
978 pa.mElement = kAudioObjectPropertyElementWildcard;
979
980 AudioObjectRemovePropertyListener (kAudioObjectSystemObject, &pa, hardwareListenerProc, internal.get());
981 }
982
getOutputChannelNames()983 StringArray getOutputChannelNames() override { return internal->outChanNames; }
getInputChannelNames()984 StringArray getInputChannelNames() override { return internal->inChanNames; }
985
isOpen()986 bool isOpen() override { return isOpen_; }
987
getAvailableSampleRates()988 Array<double> getAvailableSampleRates() override { return internal->sampleRates; }
getAvailableBufferSizes()989 Array<int> getAvailableBufferSizes() override { return internal->bufferSizes; }
990
getCurrentSampleRate()991 double getCurrentSampleRate() override { return internal->getSampleRate(); }
getCurrentBitDepth()992 int getCurrentBitDepth() override { return internal->bitDepth; }
getCurrentBufferSizeSamples()993 int getCurrentBufferSizeSamples() override { return internal->getBufferSize(); }
getXRunCount() const994 int getXRunCount() const noexcept override { return internal->xruns; }
995
getDefaultBufferSize()996 int getDefaultBufferSize() override
997 {
998 int best = 0;
999
1000 for (int i = 0; best < 512 && i < internal->bufferSizes.size(); ++i)
1001 best = internal->bufferSizes.getUnchecked(i);
1002
1003 if (best == 0)
1004 best = 512;
1005
1006 return best;
1007 }
1008
open(const BigInteger & inputChannels,const BigInteger & outputChannels,double sampleRate,int bufferSizeSamples)1009 String open (const BigInteger& inputChannels,
1010 const BigInteger& outputChannels,
1011 double sampleRate, int bufferSizeSamples) override
1012 {
1013 isOpen_ = true;
1014 internal->xruns = 0;
1015
1016 inputChannelsRequested = inputChannels;
1017 outputChannelsRequested = outputChannels;
1018
1019 if (bufferSizeSamples <= 0)
1020 bufferSizeSamples = getDefaultBufferSize();
1021
1022 if (sampleRate <= 0)
1023 sampleRate = internal->getNominalSampleRate();
1024
1025 lastError = internal->reopen (inputChannels, outputChannels, sampleRate, bufferSizeSamples);
1026 JUCE_COREAUDIOLOG ("Opened: " << getName());
1027
1028 isOpen_ = lastError.isEmpty();
1029
1030 return lastError;
1031 }
1032
close()1033 void close() override
1034 {
1035 isOpen_ = false;
1036 internal->stop (false);
1037 }
1038
getActiveOutputChannels() const1039 BigInteger getActiveOutputChannels() const override { return internal->activeOutputChans; }
getActiveInputChannels() const1040 BigInteger getActiveInputChannels() const override { return internal->activeInputChans; }
1041
getOutputLatencyInSamples()1042 int getOutputLatencyInSamples() override
1043 {
1044 // this seems like a good guess at getting the latency right - comparing
1045 // this with a round-trip measurement, it gets it to within a few millisecs
1046 // for the built-in mac soundcard
1047 return internal->outputLatency;
1048 }
1049
getInputLatencyInSamples()1050 int getInputLatencyInSamples() override
1051 {
1052 return internal->inputLatency;
1053 }
1054
start(AudioIODeviceCallback * callback)1055 void start (AudioIODeviceCallback* callback) override
1056 {
1057 if (! isStarted)
1058 {
1059 if (callback != nullptr)
1060 callback->audioDeviceAboutToStart (this);
1061
1062 isStarted = internal->start();
1063
1064 if (isStarted)
1065 {
1066 internal->setCallback (callback);
1067 previousCallback = callback;
1068 }
1069 }
1070 }
1071
stop()1072 void stop() override
1073 {
1074 restartDevice = false;
1075
1076 if (isStarted)
1077 {
1078 auto lastCallback = internal->callback;
1079
1080 isStarted = false;
1081 internal->stop (true);
1082
1083 if (lastCallback != nullptr)
1084 lastCallback->audioDeviceStopped();
1085 }
1086 }
1087
stopInternal()1088 void stopInternal()
1089 {
1090 stop();
1091 restartDevice = true;
1092 }
1093
isPlaying()1094 bool isPlaying() override
1095 {
1096 if (internal->callback == nullptr)
1097 isStarted = false;
1098
1099 return isStarted;
1100 }
1101
getLastError()1102 String getLastError() override
1103 {
1104 return lastError;
1105 }
1106
audioDeviceListChanged()1107 void audioDeviceListChanged()
1108 {
1109 if (deviceType != nullptr)
1110 deviceType->audioDeviceListChanged();
1111 }
1112
restart()1113 void restart()
1114 {
1115 if (deviceWrapperRestartCallback != nullptr)
1116 {
1117 deviceWrapperRestartCallback();
1118 }
1119 else
1120 {
1121 {
1122 const ScopedLock sl (closeLock);
1123
1124 if (isStarted)
1125 {
1126 if (internal->callback != nullptr)
1127 previousCallback = internal->callback;
1128
1129 stopInternal();
1130 }
1131 }
1132
1133 startTimer (100);
1134 }
1135 }
1136
setCurrentSampleRate(double newSampleRate)1137 bool setCurrentSampleRate (double newSampleRate)
1138 {
1139 return internal->setNominalSampleRate (newSampleRate);
1140 }
1141
setDeviceWrapperRestartCallback(const std::function<void ()> & cb)1142 void setDeviceWrapperRestartCallback (const std::function<void()>& cb)
1143 {
1144 deviceWrapperRestartCallback = cb;
1145 }
1146
shouldRestartDevice() const1147 bool shouldRestartDevice() const noexcept { return restartDevice; }
1148
1149 WeakReference<CoreAudioIODeviceType> deviceType;
1150 int inputIndex, outputIndex;
1151
1152 private:
1153 std::unique_ptr<CoreAudioInternal> internal;
1154 bool isOpen_ = false, isStarted = false, restartDevice = true;
1155 String lastError;
1156 AudioIODeviceCallback* previousCallback = nullptr;
1157 std::function<void()> deviceWrapperRestartCallback = nullptr;
1158 BigInteger inputChannelsRequested, outputChannelsRequested;
1159 CriticalSection closeLock;
1160
timerCallback()1161 void timerCallback() override
1162 {
1163 stopTimer();
1164
1165 stopInternal();
1166
1167 internal->updateDetailsFromDevice();
1168
1169 open (inputChannelsRequested, outputChannelsRequested,
1170 getCurrentSampleRate(), getCurrentBufferSizeSamples());
1171 start (previousCallback);
1172 }
1173
hardwareListenerProc(AudioDeviceID,UInt32,const AudioObjectPropertyAddress * pa,void * inClientData)1174 static OSStatus hardwareListenerProc (AudioDeviceID /*inDevice*/, UInt32 /*inLine*/, const AudioObjectPropertyAddress* pa, void* inClientData)
1175 {
1176 switch (pa->mSelector)
1177 {
1178 case kAudioHardwarePropertyDevices:
1179 static_cast<CoreAudioInternal*> (inClientData)->deviceDetailsChanged();
1180 break;
1181
1182 case kAudioHardwarePropertyDefaultOutputDevice:
1183 case kAudioHardwarePropertyDefaultInputDevice:
1184 case kAudioHardwarePropertyDefaultSystemOutputDevice:
1185 break;
1186 }
1187
1188 return noErr;
1189 }
1190
1191 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioIODevice)
1192 };
1193
1194 //==============================================================================
1195 class AudioIODeviceCombiner : public AudioIODevice,
1196 private Thread,
1197 private Timer
1198 {
1199 public:
AudioIODeviceCombiner(const String & deviceName,CoreAudioIODeviceType * deviceType)1200 AudioIODeviceCombiner (const String& deviceName, CoreAudioIODeviceType* deviceType)
1201 : AudioIODevice (deviceName, "CoreAudio"),
1202 Thread (deviceName),
1203 owner (deviceType)
1204 {
1205 }
1206
~AudioIODeviceCombiner()1207 ~AudioIODeviceCombiner() override
1208 {
1209 close();
1210 devices.clear();
1211 }
1212
addDevice(CoreAudioIODevice * device,bool useInputs,bool useOutputs)1213 void addDevice (CoreAudioIODevice* device, bool useInputs, bool useOutputs)
1214 {
1215 jassert (device != nullptr);
1216 jassert (! isOpen());
1217 jassert (! device->isOpen());
1218 devices.add (new DeviceWrapper (*this, device, useInputs, useOutputs));
1219
1220 if (currentSampleRate == 0)
1221 currentSampleRate = device->getCurrentSampleRate();
1222
1223 if (currentBufferSize == 0)
1224 currentBufferSize = device->getCurrentBufferSizeSamples();
1225 }
1226
getDevices() const1227 Array<AudioIODevice*> getDevices() const
1228 {
1229 Array<AudioIODevice*> devs;
1230
1231 for (auto* d : devices)
1232 devs.add (d->device.get());
1233
1234 return devs;
1235 }
1236
getOutputChannelNames()1237 StringArray getOutputChannelNames() override
1238 {
1239 StringArray names;
1240
1241 for (auto* d : devices)
1242 names.addArray (d->getOutputChannelNames());
1243
1244 names.appendNumbersToDuplicates (false, true);
1245 return names;
1246 }
1247
getInputChannelNames()1248 StringArray getInputChannelNames() override
1249 {
1250 StringArray names;
1251
1252 for (auto* d : devices)
1253 names.addArray (d->getInputChannelNames());
1254
1255 names.appendNumbersToDuplicates (false, true);
1256 return names;
1257 }
1258
getAvailableSampleRates()1259 Array<double> getAvailableSampleRates() override
1260 {
1261 Array<double> commonRates;
1262 bool first = true;
1263
1264 for (auto* d : devices)
1265 {
1266 auto rates = d->device->getAvailableSampleRates();
1267
1268 if (first)
1269 {
1270 first = false;
1271 commonRates = rates;
1272 }
1273 else
1274 {
1275 commonRates.removeValuesNotIn (rates);
1276 }
1277 }
1278
1279 return commonRates;
1280 }
1281
getAvailableBufferSizes()1282 Array<int> getAvailableBufferSizes() override
1283 {
1284 Array<int> commonSizes;
1285 bool first = true;
1286
1287 for (auto* d : devices)
1288 {
1289 auto sizes = d->device->getAvailableBufferSizes();
1290
1291 if (first)
1292 {
1293 first = false;
1294 commonSizes = sizes;
1295 }
1296 else
1297 {
1298 commonSizes.removeValuesNotIn (sizes);
1299 }
1300 }
1301
1302 return commonSizes;
1303 }
1304
isOpen()1305 bool isOpen() override { return active; }
isPlaying()1306 bool isPlaying() override { return callback != nullptr; }
getCurrentSampleRate()1307 double getCurrentSampleRate() override { return currentSampleRate; }
getCurrentBufferSizeSamples()1308 int getCurrentBufferSizeSamples() override { return currentBufferSize; }
1309
getCurrentBitDepth()1310 int getCurrentBitDepth() override
1311 {
1312 int depth = 32;
1313
1314 for (auto* d : devices)
1315 depth = jmin (depth, d->device->getCurrentBitDepth());
1316
1317 return depth;
1318 }
1319
getDefaultBufferSize()1320 int getDefaultBufferSize() override
1321 {
1322 int size = 0;
1323
1324 for (auto* d : devices)
1325 size = jmax (size, d->device->getDefaultBufferSize());
1326
1327 return size;
1328 }
1329
open(const BigInteger & inputChannels,const BigInteger & outputChannels,double sampleRate,int bufferSize)1330 String open (const BigInteger& inputChannels,
1331 const BigInteger& outputChannels,
1332 double sampleRate, int bufferSize) override
1333 {
1334 inputChannelsRequested = inputChannels;
1335 outputChannelsRequested = outputChannels;
1336 sampleRateRequested = sampleRate;
1337 bufferSizeRequested = bufferSize;
1338
1339 close();
1340 active = true;
1341
1342 if (bufferSize <= 0)
1343 bufferSize = getDefaultBufferSize();
1344
1345 if (sampleRate <= 0)
1346 {
1347 auto rates = getAvailableSampleRates();
1348
1349 for (int i = 0; i < rates.size() && sampleRate < 44100.0; ++i)
1350 sampleRate = rates.getUnchecked(i);
1351 }
1352
1353 currentSampleRate = sampleRate;
1354 currentBufferSize = bufferSize;
1355
1356 const int fifoSize = bufferSize * 3 + 1;
1357 int totalInputChanIndex = 0, totalOutputChanIndex = 0;
1358 int chanIndex = 0;
1359
1360 for (auto* d : devices)
1361 {
1362 BigInteger ins (inputChannels >> totalInputChanIndex);
1363 BigInteger outs (outputChannels >> totalOutputChanIndex);
1364
1365 int numIns = d->getInputChannelNames().size();
1366 int numOuts = d->getOutputChannelNames().size();
1367
1368 totalInputChanIndex += numIns;
1369 totalOutputChanIndex += numOuts;
1370
1371 String err = d->open (ins, outs, sampleRate, bufferSize,
1372 chanIndex, fifoSize);
1373
1374 if (err.isNotEmpty())
1375 {
1376 close();
1377 lastError = err;
1378 return err;
1379 }
1380
1381 chanIndex += d->numInputChans + d->numOutputChans;
1382 }
1383
1384 fifos.setSize (chanIndex, fifoSize);
1385 fifoReadPointers = fifos.getArrayOfReadPointers();
1386 fifoWritePointers = fifos.getArrayOfWritePointers();
1387 fifos.clear();
1388 startThread (9);
1389 threadInitialised.wait();
1390
1391 return {};
1392 }
1393
close()1394 void close() override
1395 {
1396 stop();
1397 stopThread (10000);
1398 fifos.clear();
1399 active = false;
1400
1401 for (auto* d : devices)
1402 d->close();
1403 }
1404
restart(AudioIODeviceCallback * cb)1405 void restart (AudioIODeviceCallback* cb)
1406 {
1407 const ScopedLock sl (closeLock);
1408
1409 close();
1410
1411 auto newSampleRate = sampleRateRequested;
1412 auto newBufferSize = bufferSizeRequested;
1413
1414 for (auto* d : devices)
1415 {
1416 auto deviceSampleRate = d->getCurrentSampleRate();
1417
1418 if (deviceSampleRate != sampleRateRequested)
1419 {
1420 if (! getAvailableSampleRates().contains (deviceSampleRate))
1421 return;
1422
1423 for (auto* d2 : devices)
1424 if (d2 != d)
1425 d2->setCurrentSampleRate (deviceSampleRate);
1426
1427 newSampleRate = deviceSampleRate;
1428 break;
1429 }
1430 }
1431
1432 for (auto* d : devices)
1433 {
1434 auto deviceBufferSize = d->getCurrentBufferSizeSamples();
1435
1436 if (deviceBufferSize != bufferSizeRequested)
1437 {
1438 if (! getAvailableBufferSizes().contains (deviceBufferSize))
1439 return;
1440
1441 newBufferSize = deviceBufferSize;
1442 break;
1443 }
1444 }
1445
1446 open (inputChannelsRequested, outputChannelsRequested,
1447 newSampleRate, newBufferSize);
1448
1449 start (cb);
1450 }
1451
restartAsync()1452 void restartAsync()
1453 {
1454 {
1455 const ScopedLock sl (closeLock);
1456
1457 if (active)
1458 {
1459 if (callback != nullptr)
1460 previousCallback = callback;
1461
1462 close();
1463 }
1464 }
1465
1466 startTimer (100);
1467 }
1468
getActiveOutputChannels() const1469 BigInteger getActiveOutputChannels() const override
1470 {
1471 BigInteger chans;
1472 int start = 0;
1473
1474 for (auto* d : devices)
1475 {
1476 auto numChans = d->getOutputChannelNames().size();
1477
1478 if (numChans > 0)
1479 {
1480 chans |= (d->device->getActiveOutputChannels() << start);
1481 start += numChans;
1482 }
1483 }
1484
1485 return chans;
1486 }
1487
getActiveInputChannels() const1488 BigInteger getActiveInputChannels() const override
1489 {
1490 BigInteger chans;
1491 int start = 0;
1492
1493 for (auto* d : devices)
1494 {
1495 auto numChans = d->getInputChannelNames().size();
1496
1497 if (numChans > 0)
1498 {
1499 chans |= (d->device->getActiveInputChannels() << start);
1500 start += numChans;
1501 }
1502 }
1503
1504 return chans;
1505 }
1506
getOutputLatencyInSamples()1507 int getOutputLatencyInSamples() override
1508 {
1509 int lat = 0;
1510
1511 for (auto* d : devices)
1512 lat = jmax (lat, d->device->getOutputLatencyInSamples());
1513
1514 return lat + currentBufferSize * 2;
1515 }
1516
getInputLatencyInSamples()1517 int getInputLatencyInSamples() override
1518 {
1519 int lat = 0;
1520
1521 for (auto* d : devices)
1522 lat = jmax (lat, d->device->getInputLatencyInSamples());
1523
1524 return lat + currentBufferSize * 2;
1525 }
1526
start(AudioIODeviceCallback * newCallback)1527 void start (AudioIODeviceCallback* newCallback) override
1528 {
1529 if (callback != newCallback)
1530 {
1531 stop();
1532 fifos.clear();
1533
1534 for (auto* d : devices)
1535 d->start();
1536
1537 if (newCallback != nullptr)
1538 newCallback->audioDeviceAboutToStart (this);
1539
1540 const ScopedLock sl (callbackLock);
1541 callback = newCallback;
1542 previousCallback = callback;
1543 }
1544 }
1545
stop()1546 void stop() override { shutdown ({}); }
1547
getLastError()1548 String getLastError() override
1549 {
1550 return lastError;
1551 }
1552
1553 private:
1554 WeakReference<CoreAudioIODeviceType> owner;
1555 CriticalSection callbackLock;
1556 AudioIODeviceCallback* callback = nullptr;
1557 AudioIODeviceCallback* previousCallback = nullptr;
1558 double currentSampleRate = 0;
1559 int currentBufferSize = 0;
1560 bool active = false;
1561 String lastError;
1562 AudioBuffer<float> fifos;
1563 const float** fifoReadPointers = nullptr;
1564 float** fifoWritePointers = nullptr;
1565 WaitableEvent threadInitialised;
1566 CriticalSection closeLock;
1567
1568 BigInteger inputChannelsRequested, outputChannelsRequested;
1569 double sampleRateRequested = 44100;
1570 int bufferSizeRequested = 512;
1571
run()1572 void run() override
1573 {
1574 auto numSamples = currentBufferSize;
1575
1576 AudioBuffer<float> buffer (fifos.getNumChannels(), numSamples);
1577 buffer.clear();
1578
1579 Array<const float*> inputChans;
1580 Array<float*> outputChans;
1581
1582 for (auto* d : devices)
1583 {
1584 for (int j = 0; j < d->numInputChans; ++j) inputChans.add (buffer.getReadPointer (d->inputIndex + j));
1585 for (int j = 0; j < d->numOutputChans; ++j) outputChans.add (buffer.getWritePointer (d->outputIndex + j));
1586 }
1587
1588 auto numInputChans = inputChans.size();
1589 auto numOutputChans = outputChans.size();
1590
1591 inputChans.add (nullptr);
1592 outputChans.add (nullptr);
1593
1594 auto blockSizeMs = jmax (1, (int) (1000 * numSamples / currentSampleRate));
1595
1596 jassert (numInputChans + numOutputChans == buffer.getNumChannels());
1597
1598 threadInitialised.signal();
1599
1600 while (! threadShouldExit())
1601 {
1602 readInput (buffer, numSamples, blockSizeMs);
1603
1604 bool didCallback = true;
1605
1606 {
1607 const ScopedLock sl (callbackLock);
1608
1609 if (callback != nullptr)
1610 callback->audioDeviceIOCallback ((const float**) inputChans.getRawDataPointer(), numInputChans,
1611 outputChans.getRawDataPointer(), numOutputChans, numSamples);
1612 else
1613 didCallback = false;
1614 }
1615
1616 if (didCallback)
1617 {
1618 pushOutputData (buffer, numSamples, blockSizeMs);
1619 }
1620 else
1621 {
1622 for (int i = 0; i < numOutputChans; ++i)
1623 FloatVectorOperations::clear (outputChans[i], numSamples);
1624
1625 reset();
1626 }
1627 }
1628 }
1629
timerCallback()1630 void timerCallback() override
1631 {
1632 stopTimer();
1633
1634 restart (previousCallback);
1635 }
1636
shutdown(const String & error)1637 void shutdown (const String& error)
1638 {
1639 AudioIODeviceCallback* lastCallback = nullptr;
1640
1641 {
1642 const ScopedLock sl (callbackLock);
1643 std::swap (callback, lastCallback);
1644 }
1645
1646 for (auto* d : devices)
1647 d->device->stopInternal();
1648
1649 if (lastCallback != nullptr)
1650 {
1651 if (error.isNotEmpty())
1652 lastCallback->audioDeviceError (error);
1653 else
1654 lastCallback->audioDeviceStopped();
1655 }
1656 }
1657
reset()1658 void reset()
1659 {
1660 for (auto* d : devices)
1661 d->reset();
1662 }
1663
underrun()1664 void underrun()
1665 {
1666 }
1667
readInput(AudioBuffer<float> & buffer,const int numSamples,const int blockSizeMs)1668 void readInput (AudioBuffer<float>& buffer, const int numSamples, const int blockSizeMs)
1669 {
1670 for (auto* d : devices)
1671 d->done = (d->numInputChans == 0);
1672
1673 for (int tries = 5;;)
1674 {
1675 bool anyRemaining = false;
1676
1677 for (auto* d : devices)
1678 {
1679 if (! d->done)
1680 {
1681 if (d->isInputReady (numSamples))
1682 {
1683 d->readInput (buffer, numSamples);
1684 d->done = true;
1685 }
1686 else
1687 anyRemaining = true;
1688 }
1689 }
1690
1691 if (! anyRemaining)
1692 return;
1693
1694 if (--tries == 0)
1695 break;
1696
1697 wait (blockSizeMs);
1698 }
1699
1700 for (auto* d : devices)
1701 if (! d->done)
1702 for (int i = 0; i < d->numInputChans; ++i)
1703 buffer.clear (d->inputIndex + i, 0, numSamples);
1704 }
1705
pushOutputData(AudioBuffer<float> & buffer,const int numSamples,const int blockSizeMs)1706 void pushOutputData (AudioBuffer<float>& buffer, const int numSamples, const int blockSizeMs)
1707 {
1708 for (auto* d : devices)
1709 d->done = (d->numOutputChans == 0);
1710
1711 for (int tries = 5;;)
1712 {
1713 bool anyRemaining = false;
1714
1715 for (auto* d : devices)
1716 {
1717 if (! d->done)
1718 {
1719 if (d->isOutputReady (numSamples))
1720 {
1721 d->pushOutputData (buffer, numSamples);
1722 d->done = true;
1723 }
1724 else
1725 anyRemaining = true;
1726 }
1727 }
1728
1729 if ((! anyRemaining) || --tries == 0)
1730 return;
1731
1732 wait (blockSizeMs);
1733 }
1734 }
1735
handleAudioDeviceAboutToStart(AudioIODevice * device)1736 void handleAudioDeviceAboutToStart (AudioIODevice* device)
1737 {
1738 const ScopedLock sl (callbackLock);
1739
1740 auto newSampleRate = device->getCurrentSampleRate();
1741 auto commonRates = getAvailableSampleRates();
1742
1743 if (! commonRates.contains (newSampleRate))
1744 {
1745 commonRates.sort();
1746
1747 if (newSampleRate < commonRates.getFirst() || newSampleRate > commonRates.getLast())
1748 {
1749 newSampleRate = jlimit (commonRates.getFirst(), commonRates.getLast(), newSampleRate);
1750 }
1751 else
1752 {
1753 for (auto it = commonRates.begin(); it < commonRates.end() - 1; ++it)
1754 {
1755 if (it[0] < newSampleRate && it[1] > newSampleRate)
1756 {
1757 newSampleRate = newSampleRate - it[0] < it[1] - newSampleRate ? it[0] : it[1];
1758 break;
1759 }
1760 }
1761 }
1762 }
1763
1764 currentSampleRate = newSampleRate;
1765 bool anySampleRateChanges = false;
1766
1767 for (auto* d : devices)
1768 {
1769 if (d->getCurrentSampleRate() != currentSampleRate)
1770 {
1771 d->setCurrentSampleRate (currentSampleRate);
1772 anySampleRateChanges = true;
1773 }
1774 }
1775
1776 if (anySampleRateChanges && owner != nullptr)
1777 owner->audioDeviceListChanged();
1778
1779 if (callback != nullptr)
1780 callback->audioDeviceAboutToStart (device);
1781 }
1782
handleAudioDeviceStopped()1783 void handleAudioDeviceStopped() { shutdown ({}); }
handleAudioDeviceError(const String & errorMessage)1784 void handleAudioDeviceError (const String& errorMessage) { shutdown (errorMessage.isNotEmpty() ? errorMessage : String ("unknown")); }
1785
1786 //==============================================================================
1787 struct DeviceWrapper : private AudioIODeviceCallback
1788 {
DeviceWrapperjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1789 DeviceWrapper (AudioIODeviceCombiner& cd, CoreAudioIODevice* d, bool useIns, bool useOuts)
1790 : owner (cd), device (d),
1791 useInputs (useIns), useOutputs (useOuts)
1792 {
1793 d->setDeviceWrapperRestartCallback ([this] { owner.restartAsync(); });
1794 }
1795
~DeviceWrapperjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1796 ~DeviceWrapper() override
1797 {
1798 close();
1799 }
1800
openjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1801 String open (const BigInteger& inputChannels, const BigInteger& outputChannels,
1802 double sampleRate, int bufferSize, int channelIndex, int fifoSize)
1803 {
1804 inputFifo.setTotalSize (fifoSize);
1805 outputFifo.setTotalSize (fifoSize);
1806 inputFifo.reset();
1807 outputFifo.reset();
1808
1809 auto err = device->open (useInputs ? inputChannels : BigInteger(),
1810 useOutputs ? outputChannels : BigInteger(),
1811 sampleRate, bufferSize);
1812
1813 numInputChans = useInputs ? device->getActiveInputChannels().countNumberOfSetBits() : 0;
1814 numOutputChans = useOutputs ? device->getActiveOutputChannels().countNumberOfSetBits() : 0;
1815
1816 inputIndex = channelIndex;
1817 outputIndex = channelIndex + numInputChans;
1818
1819 return err;
1820 }
1821
closejuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1822 void close()
1823 {
1824 device->close();
1825 }
1826
startjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1827 void start()
1828 {
1829 reset();
1830 device->start (this);
1831 }
1832
resetjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1833 void reset()
1834 {
1835 inputFifo.reset();
1836 outputFifo.reset();
1837 }
1838
getOutputChannelNamesjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1839 StringArray getOutputChannelNames() const { return useOutputs ? device->getOutputChannelNames() : StringArray(); }
getInputChannelNamesjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1840 StringArray getInputChannelNames() const { return useInputs ? device->getInputChannelNames() : StringArray(); }
1841
isInputReadyjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1842 bool isInputReady (int numSamples) const noexcept
1843 {
1844 return numInputChans == 0 || inputFifo.getNumReady() >= numSamples;
1845 }
1846
readInputjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1847 void readInput (AudioBuffer<float>& destBuffer, int numSamples)
1848 {
1849 if (numInputChans == 0)
1850 return;
1851
1852 int start1, size1, start2, size2;
1853 inputFifo.prepareToRead (numSamples, start1, size1, start2, size2);
1854
1855 for (int i = 0; i < numInputChans; ++i)
1856 {
1857 auto index = inputIndex + i;
1858 auto dest = destBuffer.getWritePointer (index);
1859 auto src = owner.fifoReadPointers[index];
1860
1861 if (size1 > 0) FloatVectorOperations::copy (dest, src + start1, size1);
1862 if (size2 > 0) FloatVectorOperations::copy (dest + size1, src + start2, size2);
1863 }
1864
1865 inputFifo.finishedRead (size1 + size2);
1866 }
1867
isOutputReadyjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1868 bool isOutputReady (int numSamples) const noexcept
1869 {
1870 return numOutputChans == 0 || outputFifo.getFreeSpace() >= numSamples;
1871 }
1872
pushOutputDatajuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1873 void pushOutputData (AudioBuffer<float>& srcBuffer, int numSamples)
1874 {
1875 if (numOutputChans == 0)
1876 return;
1877
1878 int start1, size1, start2, size2;
1879 outputFifo.prepareToWrite (numSamples, start1, size1, start2, size2);
1880
1881 for (int i = 0; i < numOutputChans; ++i)
1882 {
1883 auto index = outputIndex + i;
1884 auto dest = owner.fifoWritePointers[index];
1885 auto src = srcBuffer.getReadPointer (index);
1886
1887 if (size1 > 0) FloatVectorOperations::copy (dest + start1, src, size1);
1888 if (size2 > 0) FloatVectorOperations::copy (dest + start2, src + size1, size2);
1889 }
1890
1891 outputFifo.finishedWrite (size1 + size2);
1892 }
1893
audioDeviceIOCallbackjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1894 void audioDeviceIOCallback (const float** inputChannelData, int numInputChannels,
1895 float** outputChannelData, int numOutputChannels,
1896 int numSamples) override
1897 {
1898 if (numInputChannels > 0)
1899 {
1900 int start1, size1, start2, size2;
1901 inputFifo.prepareToWrite (numSamples, start1, size1, start2, size2);
1902
1903 if (size1 + size2 < numSamples)
1904 {
1905 inputFifo.reset();
1906 inputFifo.prepareToWrite (numSamples, start1, size1, start2, size2);
1907 }
1908
1909 for (int i = 0; i < numInputChannels; ++i)
1910 {
1911 auto dest = owner.fifoWritePointers[inputIndex + i];
1912 auto src = inputChannelData[i];
1913
1914 if (size1 > 0) FloatVectorOperations::copy (dest + start1, src, size1);
1915 if (size2 > 0) FloatVectorOperations::copy (dest + start2, src + size1, size2);
1916 }
1917
1918 auto totalSize = size1 + size2;
1919 inputFifo.finishedWrite (totalSize);
1920
1921 if (numSamples > totalSize)
1922 {
1923 auto samplesRemaining = numSamples - totalSize;
1924
1925 for (int i = 0; i < numInputChans; ++i)
1926 FloatVectorOperations::clear (owner.fifoWritePointers[inputIndex + i] + totalSize, samplesRemaining);
1927
1928 owner.underrun();
1929 }
1930 }
1931
1932 if (numOutputChannels > 0)
1933 {
1934 int start1, size1, start2, size2;
1935 outputFifo.prepareToRead (numSamples, start1, size1, start2, size2);
1936
1937 if (size1 + size2 < numSamples)
1938 {
1939 Thread::sleep (1);
1940 outputFifo.prepareToRead (numSamples, start1, size1, start2, size2);
1941 }
1942
1943 for (int i = 0; i < numOutputChannels; ++i)
1944 {
1945 auto dest = outputChannelData[i];
1946 auto src = owner.fifoReadPointers[outputIndex + i];
1947
1948 if (size1 > 0) FloatVectorOperations::copy (dest, src + start1, size1);
1949 if (size2 > 0) FloatVectorOperations::copy (dest + size1, src + start2, size2);
1950 }
1951
1952 auto totalSize = size1 + size2;
1953 outputFifo.finishedRead (totalSize);
1954
1955 if (numSamples > totalSize)
1956 {
1957 auto samplesRemaining = numSamples - totalSize;
1958
1959 for (int i = 0; i < numOutputChannels; ++i)
1960 FloatVectorOperations::clear (outputChannelData[i] + totalSize, samplesRemaining);
1961
1962 owner.underrun();
1963 }
1964 }
1965
1966 owner.notify();
1967 }
1968
getCurrentSampleRatejuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1969 double getCurrentSampleRate() { return device->getCurrentSampleRate(); }
setCurrentSampleRatejuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1970 bool setCurrentSampleRate (double newSampleRate) { return device->setCurrentSampleRate (newSampleRate); }
getCurrentBufferSizeSamplesjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1971 int getCurrentBufferSizeSamples() { return device->getCurrentBufferSizeSamples(); }
1972
audioDeviceAboutToStartjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1973 void audioDeviceAboutToStart (AudioIODevice* d) override { owner.handleAudioDeviceAboutToStart (d); }
audioDeviceStoppedjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1974 void audioDeviceStopped() override { owner.handleAudioDeviceStopped(); }
audioDeviceErrorjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1975 void audioDeviceError (const String& errorMessage) override { owner.handleAudioDeviceError (errorMessage); }
1976
1977 AudioIODeviceCombiner& owner;
1978 std::unique_ptr<CoreAudioIODevice> device;
1979 int inputIndex = 0, numInputChans = 0, outputIndex = 0, numOutputChans = 0;
1980 bool useInputs = false, useOutputs = false;
1981 AbstractFifo inputFifo { 32 }, outputFifo { 32 };
1982 bool done = false;
1983
1984 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DeviceWrapper)
1985 };
1986
1987 OwnedArray<DeviceWrapper> devices;
1988
1989 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioIODeviceCombiner)
1990 };
1991
1992
1993 //==============================================================================
1994 class CoreAudioIODeviceType : public AudioIODeviceType,
1995 private AsyncUpdater
1996 {
1997 public:
CoreAudioIODeviceType()1998 CoreAudioIODeviceType() : AudioIODeviceType ("CoreAudio")
1999 {
2000 AudioObjectPropertyAddress pa;
2001 pa.mSelector = kAudioHardwarePropertyDevices;
2002 pa.mScope = kAudioObjectPropertyScopeWildcard;
2003 pa.mElement = kAudioObjectPropertyElementWildcard;
2004
2005 AudioObjectAddPropertyListener (kAudioObjectSystemObject, &pa, hardwareListenerProc, this);
2006 }
2007
~CoreAudioIODeviceType()2008 ~CoreAudioIODeviceType() override
2009 {
2010 AudioObjectPropertyAddress pa;
2011 pa.mSelector = kAudioHardwarePropertyDevices;
2012 pa.mScope = kAudioObjectPropertyScopeWildcard;
2013 pa.mElement = kAudioObjectPropertyElementWildcard;
2014
2015 AudioObjectRemovePropertyListener (kAudioObjectSystemObject, &pa, hardwareListenerProc, this);
2016 }
2017
2018 //==============================================================================
scanForDevices()2019 void scanForDevices() override
2020 {
2021 hasScanned = true;
2022
2023 inputDeviceNames.clear();
2024 outputDeviceNames.clear();
2025 inputIds.clear();
2026 outputIds.clear();
2027
2028 UInt32 size;
2029
2030 AudioObjectPropertyAddress pa;
2031 pa.mSelector = kAudioHardwarePropertyDevices;
2032 pa.mScope = kAudioObjectPropertyScopeWildcard;
2033 pa.mElement = kAudioObjectPropertyElementMaster;
2034
2035 if (AudioObjectGetPropertyDataSize (kAudioObjectSystemObject, &pa, 0, nullptr, &size) == noErr)
2036 {
2037 HeapBlock<AudioDeviceID> devs;
2038 devs.calloc (size, 1);
2039
2040 if (AudioObjectGetPropertyData (kAudioObjectSystemObject, &pa, 0, nullptr, &size, devs) == noErr)
2041 {
2042 auto num = (int) size / (int) sizeof (AudioDeviceID);
2043
2044 for (int i = 0; i < num; ++i)
2045 {
2046 char name[1024];
2047 size = sizeof (name);
2048 pa.mSelector = kAudioDevicePropertyDeviceName;
2049
2050 if (AudioObjectGetPropertyData (devs[i], &pa, 0, nullptr, &size, name) == noErr)
2051 {
2052 auto nameString = String::fromUTF8 (name, (int) strlen (name));
2053 auto numIns = getNumChannels (devs[i], true);
2054 auto numOuts = getNumChannels (devs[i], false);
2055
2056 if (numIns > 0)
2057 {
2058 inputDeviceNames.add (nameString);
2059 inputIds.add (devs[i]);
2060 }
2061
2062 if (numOuts > 0)
2063 {
2064 outputDeviceNames.add (nameString);
2065 outputIds.add (devs[i]);
2066 }
2067 }
2068 }
2069 }
2070 }
2071
2072 inputDeviceNames.appendNumbersToDuplicates (false, true);
2073 outputDeviceNames.appendNumbersToDuplicates (false, true);
2074 }
2075
getDeviceNames(bool wantInputNames) const2076 StringArray getDeviceNames (bool wantInputNames) const override
2077 {
2078 jassert (hasScanned); // need to call scanForDevices() before doing this
2079
2080 return wantInputNames ? inputDeviceNames
2081 : outputDeviceNames;
2082 }
2083
getDefaultDeviceIndex(bool forInput) const2084 int getDefaultDeviceIndex (bool forInput) const override
2085 {
2086 jassert (hasScanned); // need to call scanForDevices() before doing this
2087
2088 AudioDeviceID deviceID;
2089 UInt32 size = sizeof (deviceID);
2090
2091 // if they're asking for any input channels at all, use the default input, so we
2092 // get the built-in mic rather than the built-in output with no inputs..
2093
2094 AudioObjectPropertyAddress pa;
2095 pa.mSelector = forInput ? kAudioHardwarePropertyDefaultInputDevice
2096 : kAudioHardwarePropertyDefaultOutputDevice;
2097 pa.mScope = kAudioObjectPropertyScopeWildcard;
2098 pa.mElement = kAudioObjectPropertyElementMaster;
2099
2100 if (AudioObjectGetPropertyData (kAudioObjectSystemObject, &pa, 0, nullptr, &size, &deviceID) == noErr)
2101 {
2102 if (forInput)
2103 {
2104 for (int i = inputIds.size(); --i >= 0;)
2105 if (inputIds[i] == deviceID)
2106 return i;
2107 }
2108 else
2109 {
2110 for (int i = outputIds.size(); --i >= 0;)
2111 if (outputIds[i] == deviceID)
2112 return i;
2113 }
2114 }
2115
2116 return 0;
2117 }
2118
getIndexOfDevice(AudioIODevice * device,bool asInput) const2119 int getIndexOfDevice (AudioIODevice* device, bool asInput) const override
2120 {
2121 jassert (hasScanned); // need to call scanForDevices() before doing this
2122
2123 if (auto* d = dynamic_cast<CoreAudioIODevice*> (device))
2124 return asInput ? d->inputIndex
2125 : d->outputIndex;
2126
2127 if (auto* d = dynamic_cast<AudioIODeviceCombiner*> (device))
2128 {
2129 for (auto* dev : d->getDevices())
2130 {
2131 auto index = getIndexOfDevice (dev, asInput);
2132
2133 if (index >= 0)
2134 return index;
2135 }
2136 }
2137
2138 return -1;
2139 }
2140
hasSeparateInputsAndOutputs() const2141 bool hasSeparateInputsAndOutputs() const override { return true; }
2142
createDevice(const String & outputDeviceName,const String & inputDeviceName)2143 AudioIODevice* createDevice (const String& outputDeviceName,
2144 const String& inputDeviceName) override
2145 {
2146 jassert (hasScanned); // need to call scanForDevices() before doing this
2147
2148 auto inputIndex = inputDeviceNames.indexOf (inputDeviceName);
2149 auto outputIndex = outputDeviceNames.indexOf (outputDeviceName);
2150
2151 auto inputDeviceID = inputIds[inputIndex];
2152 auto outputDeviceID = outputIds[outputIndex];
2153
2154 if (inputDeviceID == 0 && outputDeviceID == 0)
2155 return nullptr;
2156
2157 auto combinedName = outputDeviceName.isEmpty() ? inputDeviceName
2158 : outputDeviceName;
2159
2160 if (inputDeviceID == outputDeviceID)
2161 return new CoreAudioIODevice (this, combinedName, inputDeviceID, inputIndex, outputDeviceID, outputIndex);
2162
2163 std::unique_ptr<CoreAudioIODevice> in, out;
2164
2165 if (inputDeviceID != 0)
2166 in.reset (new CoreAudioIODevice (this, inputDeviceName, inputDeviceID, inputIndex, 0, -1));
2167
2168 if (outputDeviceID != 0)
2169 out.reset (new CoreAudioIODevice (this, outputDeviceName, 0, -1, outputDeviceID, outputIndex));
2170
2171 if (in == nullptr) return out.release();
2172 if (out == nullptr) return in.release();
2173
2174 std::unique_ptr<AudioIODeviceCombiner> combo (new AudioIODeviceCombiner (combinedName, this));
2175 combo->addDevice (in.release(), true, false);
2176 combo->addDevice (out.release(), false, true);
2177 return combo.release();
2178 }
2179
audioDeviceListChanged()2180 void audioDeviceListChanged()
2181 {
2182 scanForDevices();
2183 callDeviceChangeListeners();
2184 }
2185
triggerAsyncAudioDeviceListChange()2186 void triggerAsyncAudioDeviceListChange()
2187 {
2188 triggerAsyncUpdate();
2189 }
2190
2191 //==============================================================================
2192 private:
2193 StringArray inputDeviceNames, outputDeviceNames;
2194 Array<AudioDeviceID> inputIds, outputIds;
2195
2196 bool hasScanned = false;
2197
getNumChannels(AudioDeviceID deviceID,bool input)2198 static int getNumChannels (AudioDeviceID deviceID, bool input)
2199 {
2200 int total = 0;
2201 UInt32 size;
2202
2203 AudioObjectPropertyAddress pa;
2204 pa.mSelector = kAudioDevicePropertyStreamConfiguration;
2205 pa.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
2206 pa.mElement = kAudioObjectPropertyElementMaster;
2207
2208 if (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, nullptr, &size) == noErr)
2209 {
2210 HeapBlock<AudioBufferList> bufList;
2211 bufList.calloc (size, 1);
2212
2213 if (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, bufList) == noErr)
2214 {
2215 auto numStreams = (int) bufList->mNumberBuffers;
2216
2217 for (int i = 0; i < numStreams; ++i)
2218 total += bufList->mBuffers[i].mNumberChannels;
2219 }
2220 }
2221
2222 return total;
2223 }
2224
hardwareListenerProc(AudioDeviceID,UInt32,const AudioObjectPropertyAddress *,void * clientData)2225 static OSStatus hardwareListenerProc (AudioDeviceID, UInt32, const AudioObjectPropertyAddress*, void* clientData)
2226 {
2227 static_cast<CoreAudioIODeviceType*> (clientData)->triggerAsyncAudioDeviceListChange();
2228 return noErr;
2229 }
2230
handleAsyncUpdate()2231 void handleAsyncUpdate() override
2232 {
2233 audioDeviceListChanged();
2234 }
2235
2236 JUCE_DECLARE_WEAK_REFERENCEABLE (CoreAudioIODeviceType)
2237 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioIODeviceType)
2238 };
2239
2240 };
2241
2242 //==============================================================================
createAudioIODeviceType_CoreAudio()2243 AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_CoreAudio()
2244 {
2245 return new CoreAudioClasses::CoreAudioIODeviceType();
2246 }
2247
2248 #undef JUCE_COREAUDIOLOG
2249
2250 } // namespace juce
2251