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