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);
1667 
1668         for (int tries = 5;;)
1669         {
1670             bool anyRemaining = false;
1671 
1672             for (auto* d : devices)
1673             {
1674                 if (! d->done)
1675                 {
1676                     if (d->isInputReady (numSamples))
1677                     {
1678                         d->readInput (buffer, numSamples);
1679                         d->done = true;
1680                     }
1681                     else
1682                         anyRemaining = true;
1683                 }
1684             }
1685 
1686             if (! anyRemaining)
1687                 return;
1688 
1689             if (--tries == 0)
1690                 break;
1691 
1692             wait (blockSizeMs);
1693         }
1694 
1695         for (auto* d : devices)
1696             if (! d->done)
1697                 for (int i = 0; i < d->numInputChans; ++i)
1698                     buffer.clear (d->inputIndex + i, 0, numSamples);
1699     }
1700 
pushOutputData(AudioBuffer<float> & buffer,const int numSamples,const int blockSizeMs)1701     void pushOutputData (AudioBuffer<float>& buffer, const int numSamples, const int blockSizeMs)
1702     {
1703         for (auto* d : devices)
1704             d->done = (d->numOutputChans == 0);
1705 
1706         for (int tries = 5;;)
1707         {
1708             bool anyRemaining = false;
1709 
1710             for (auto* d : devices)
1711             {
1712                 if (! d->done)
1713                 {
1714                     if (d->isOutputReady (numSamples))
1715                     {
1716                         d->pushOutputData (buffer, numSamples);
1717                         d->done = true;
1718                     }
1719                     else
1720                         anyRemaining = true;
1721                 }
1722             }
1723 
1724             if ((! anyRemaining) || --tries == 0)
1725                 return;
1726 
1727             wait (blockSizeMs);
1728         }
1729     }
1730 
handleAudioDeviceAboutToStart(AudioIODevice * device)1731     void handleAudioDeviceAboutToStart (AudioIODevice* device)
1732     {
1733         const ScopedLock sl (callbackLock);
1734 
1735         auto newSampleRate = device->getCurrentSampleRate();
1736         auto commonRates = getAvailableSampleRates();
1737 
1738         if (! commonRates.contains (newSampleRate))
1739         {
1740             commonRates.sort();
1741 
1742             if (newSampleRate < commonRates.getFirst() || newSampleRate > commonRates.getLast())
1743             {
1744                 newSampleRate = jlimit (commonRates.getFirst(), commonRates.getLast(), newSampleRate);
1745             }
1746             else
1747             {
1748                 for (auto it = commonRates.begin(); it < commonRates.end() - 1; ++it)
1749                 {
1750                     if (it[0] < newSampleRate && it[1] > newSampleRate)
1751                     {
1752                         newSampleRate = newSampleRate - it[0] < it[1] - newSampleRate ? it[0] : it[1];
1753                         break;
1754                     }
1755                 }
1756             }
1757         }
1758 
1759         currentSampleRate = newSampleRate;
1760         bool anySampleRateChanges = false;
1761 
1762         for (auto* d : devices)
1763         {
1764             if (d->getCurrentSampleRate() != currentSampleRate)
1765             {
1766                 d->setCurrentSampleRate (currentSampleRate);
1767                 anySampleRateChanges = true;
1768             }
1769         }
1770 
1771         if (anySampleRateChanges && owner != nullptr)
1772             owner->audioDeviceListChanged();
1773 
1774         if (callback != nullptr)
1775             callback->audioDeviceAboutToStart (device);
1776     }
1777 
handleAudioDeviceStopped()1778     void handleAudioDeviceStopped()                            { shutdown ({}); }
handleAudioDeviceError(const String & errorMessage)1779     void handleAudioDeviceError (const String& errorMessage)   { shutdown (errorMessage.isNotEmpty() ? errorMessage : String ("unknown")); }
1780 
1781     //==============================================================================
1782     struct DeviceWrapper  : private AudioIODeviceCallback
1783     {
DeviceWrapperjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1784         DeviceWrapper (AudioIODeviceCombiner& cd, CoreAudioIODevice* d, bool useIns, bool useOuts)
1785             : owner (cd), device (d),
1786               useInputs (useIns), useOutputs (useOuts)
1787         {
1788             d->setDeviceWrapperRestartCallback ([this] { owner.restartAsync(); });
1789         }
1790 
~DeviceWrapperjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1791         ~DeviceWrapper() override
1792         {
1793             close();
1794         }
1795 
openjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1796         String open (const BigInteger& inputChannels, const BigInteger& outputChannels,
1797                      double sampleRate, int bufferSize, int channelIndex, int fifoSize)
1798         {
1799             inputFifo.setTotalSize (fifoSize);
1800             outputFifo.setTotalSize (fifoSize);
1801             inputFifo.reset();
1802             outputFifo.reset();
1803 
1804             auto err = device->open (useInputs  ? inputChannels  : BigInteger(),
1805                                      useOutputs ? outputChannels : BigInteger(),
1806                                      sampleRate, bufferSize);
1807 
1808             numInputChans  = useInputs  ? device->getActiveInputChannels().countNumberOfSetBits()  : 0;
1809             numOutputChans = useOutputs ? device->getActiveOutputChannels().countNumberOfSetBits() : 0;
1810 
1811             inputIndex = channelIndex;
1812             outputIndex = channelIndex + numInputChans;
1813 
1814             return err;
1815         }
1816 
closejuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1817         void close()
1818         {
1819             device->close();
1820         }
1821 
startjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1822         void start()
1823         {
1824             reset();
1825             device->start (this);
1826         }
1827 
resetjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1828         void reset()
1829         {
1830             inputFifo.reset();
1831             outputFifo.reset();
1832         }
1833 
getOutputChannelNamesjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1834         StringArray getOutputChannelNames() const  { return useOutputs ? device->getOutputChannelNames() : StringArray(); }
getInputChannelNamesjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1835         StringArray getInputChannelNames()  const  { return useInputs  ? device->getInputChannelNames()  : StringArray(); }
1836 
isInputReadyjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1837         bool isInputReady (int numSamples) const noexcept
1838         {
1839             return numInputChans == 0 || inputFifo.getNumReady() >= numSamples;
1840         }
1841 
readInputjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1842         void readInput (AudioBuffer<float>& destBuffer, int numSamples)
1843         {
1844             if (numInputChans == 0)
1845                 return;
1846 
1847             int start1, size1, start2, size2;
1848             inputFifo.prepareToRead (numSamples, start1, size1, start2, size2);
1849 
1850             for (int i = 0; i < numInputChans; ++i)
1851             {
1852                 auto index = inputIndex + i;
1853                 auto dest = destBuffer.getWritePointer (index);
1854                 auto src = owner.fifoReadPointers[index];
1855 
1856                 if (size1 > 0)  FloatVectorOperations::copy (dest,         src + start1, size1);
1857                 if (size2 > 0)  FloatVectorOperations::copy (dest + size1, src + start2, size2);
1858             }
1859 
1860             inputFifo.finishedRead (size1 + size2);
1861         }
1862 
isOutputReadyjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1863         bool isOutputReady (int numSamples) const noexcept
1864         {
1865             return numOutputChans == 0 || outputFifo.getFreeSpace() >= numSamples;
1866         }
1867 
pushOutputDatajuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1868         void pushOutputData (AudioBuffer<float>& srcBuffer, int numSamples)
1869         {
1870             if (numOutputChans == 0)
1871                 return;
1872 
1873             int start1, size1, start2, size2;
1874             outputFifo.prepareToWrite (numSamples, start1, size1, start2, size2);
1875 
1876             for (int i = 0; i < numOutputChans; ++i)
1877             {
1878                 auto index = outputIndex + i;
1879                 auto dest = owner.fifoWritePointers[index];
1880                 auto src = srcBuffer.getReadPointer (index);
1881 
1882                 if (size1 > 0)  FloatVectorOperations::copy (dest + start1, src,         size1);
1883                 if (size2 > 0)  FloatVectorOperations::copy (dest + start2, src + size1, size2);
1884             }
1885 
1886             outputFifo.finishedWrite (size1 + size2);
1887         }
1888 
audioDeviceIOCallbackjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1889         void audioDeviceIOCallback (const float** inputChannelData, int numInputChannels,
1890                                     float** outputChannelData, int numOutputChannels,
1891                                     int numSamples) override
1892         {
1893             if (numInputChannels > 0)
1894             {
1895                 int start1, size1, start2, size2;
1896                 inputFifo.prepareToWrite (numSamples, start1, size1, start2, size2);
1897 
1898                 if (size1 + size2 < numSamples)
1899                 {
1900                     inputFifo.reset();
1901                     inputFifo.prepareToWrite (numSamples, start1, size1, start2, size2);
1902                 }
1903 
1904                 for (int i = 0; i < numInputChannels; ++i)
1905                 {
1906                     auto dest = owner.fifoWritePointers[inputIndex + i];
1907                     auto src = inputChannelData[i];
1908 
1909                     if (size1 > 0)  FloatVectorOperations::copy (dest + start1, src,         size1);
1910                     if (size2 > 0)  FloatVectorOperations::copy (dest + start2, src + size1, size2);
1911                 }
1912 
1913                 auto totalSize = size1 + size2;
1914                 inputFifo.finishedWrite (totalSize);
1915 
1916                 if (numSamples > totalSize)
1917                 {
1918                     auto samplesRemaining = numSamples - totalSize;
1919 
1920                     for (int i = 0; i < numInputChans; ++i)
1921                         FloatVectorOperations::clear (owner.fifoWritePointers[inputIndex + i] + totalSize, samplesRemaining);
1922 
1923                     owner.underrun();
1924                 }
1925             }
1926 
1927             if (numOutputChannels > 0)
1928             {
1929                 int start1, size1, start2, size2;
1930                 outputFifo.prepareToRead (numSamples, start1, size1, start2, size2);
1931 
1932                 if (size1 + size2 < numSamples)
1933                 {
1934                     Thread::sleep (1);
1935                     outputFifo.prepareToRead (numSamples, start1, size1, start2, size2);
1936                 }
1937 
1938                 for (int i = 0; i < numOutputChannels; ++i)
1939                 {
1940                     auto dest = outputChannelData[i];
1941                     auto src = owner.fifoReadPointers[outputIndex + i];
1942 
1943                     if (size1 > 0)  FloatVectorOperations::copy (dest,         src + start1, size1);
1944                     if (size2 > 0)  FloatVectorOperations::copy (dest + size1, src + start2, size2);
1945                 }
1946 
1947                 auto totalSize = size1 + size2;
1948                 outputFifo.finishedRead (totalSize);
1949 
1950                 if (numSamples > totalSize)
1951                 {
1952                     auto samplesRemaining = numSamples - totalSize;
1953 
1954                     for (int i = 0; i < numOutputChannels; ++i)
1955                         FloatVectorOperations::clear (outputChannelData[i] + totalSize, samplesRemaining);
1956 
1957                     owner.underrun();
1958                 }
1959             }
1960 
1961             owner.notify();
1962         }
1963 
getCurrentSampleRatejuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1964         double getCurrentSampleRate()                        { return device->getCurrentSampleRate(); }
setCurrentSampleRatejuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1965         bool   setCurrentSampleRate (double newSampleRate)   { return device->setCurrentSampleRate (newSampleRate); }
getCurrentBufferSizeSamplesjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1966         int  getCurrentBufferSizeSamples()                   { return device->getCurrentBufferSizeSamples(); }
1967 
audioDeviceAboutToStartjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1968         void audioDeviceAboutToStart (AudioIODevice* d) override      { owner.handleAudioDeviceAboutToStart (d); }
audioDeviceStoppedjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1969         void audioDeviceStopped() override                            { owner.handleAudioDeviceStopped(); }
audioDeviceErrorjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1970         void audioDeviceError (const String& errorMessage) override   { owner.handleAudioDeviceError (errorMessage); }
1971 
1972         AudioIODeviceCombiner& owner;
1973         std::unique_ptr<CoreAudioIODevice> device;
1974         int inputIndex = 0, numInputChans = 0, outputIndex = 0, numOutputChans = 0;
1975         bool useInputs = false, useOutputs = false;
1976         AbstractFifo inputFifo { 32 }, outputFifo { 32 };
1977         bool done = false;
1978 
1979         JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DeviceWrapper)
1980     };
1981 
1982     OwnedArray<DeviceWrapper> devices;
1983 
1984     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioIODeviceCombiner)
1985 };
1986 
1987 
1988 //==============================================================================
1989 class CoreAudioIODeviceType  : public AudioIODeviceType,
1990                                private AsyncUpdater
1991 {
1992 public:
CoreAudioIODeviceType()1993     CoreAudioIODeviceType()  : AudioIODeviceType ("CoreAudio")
1994     {
1995         AudioObjectPropertyAddress pa;
1996         pa.mSelector = kAudioHardwarePropertyDevices;
1997         pa.mScope = kAudioObjectPropertyScopeWildcard;
1998         pa.mElement = kAudioObjectPropertyElementWildcard;
1999 
2000         AudioObjectAddPropertyListener (kAudioObjectSystemObject, &pa, hardwareListenerProc, this);
2001     }
2002 
~CoreAudioIODeviceType()2003     ~CoreAudioIODeviceType() override
2004     {
2005         AudioObjectPropertyAddress pa;
2006         pa.mSelector = kAudioHardwarePropertyDevices;
2007         pa.mScope = kAudioObjectPropertyScopeWildcard;
2008         pa.mElement = kAudioObjectPropertyElementWildcard;
2009 
2010         AudioObjectRemovePropertyListener (kAudioObjectSystemObject, &pa, hardwareListenerProc, this);
2011     }
2012 
2013     //==============================================================================
scanForDevices()2014     void scanForDevices() override
2015     {
2016         hasScanned = true;
2017 
2018         inputDeviceNames.clear();
2019         outputDeviceNames.clear();
2020         inputIds.clear();
2021         outputIds.clear();
2022 
2023         UInt32 size;
2024 
2025         AudioObjectPropertyAddress pa;
2026         pa.mSelector = kAudioHardwarePropertyDevices;
2027         pa.mScope = kAudioObjectPropertyScopeWildcard;
2028         pa.mElement = kAudioObjectPropertyElementMaster;
2029 
2030         if (AudioObjectGetPropertyDataSize (kAudioObjectSystemObject, &pa, 0, nullptr, &size) == noErr)
2031         {
2032             HeapBlock<AudioDeviceID> devs;
2033             devs.calloc (size, 1);
2034 
2035             if (AudioObjectGetPropertyData (kAudioObjectSystemObject, &pa, 0, nullptr, &size, devs) == noErr)
2036             {
2037                 auto num = (int) size / (int) sizeof (AudioDeviceID);
2038 
2039                 for (int i = 0; i < num; ++i)
2040                 {
2041                     char name[1024];
2042                     size = sizeof (name);
2043                     pa.mSelector = kAudioDevicePropertyDeviceName;
2044 
2045                     if (AudioObjectGetPropertyData (devs[i], &pa, 0, nullptr, &size, name) == noErr)
2046                     {
2047                         auto nameString = String::fromUTF8 (name, (int) strlen (name));
2048                         auto numIns  = getNumChannels (devs[i], true);
2049                         auto numOuts = getNumChannels (devs[i], false);
2050 
2051                         if (numIns > 0)
2052                         {
2053                             inputDeviceNames.add (nameString);
2054                             inputIds.add (devs[i]);
2055                         }
2056 
2057                         if (numOuts > 0)
2058                         {
2059                             outputDeviceNames.add (nameString);
2060                             outputIds.add (devs[i]);
2061                         }
2062                     }
2063                 }
2064             }
2065         }
2066 
2067         inputDeviceNames.appendNumbersToDuplicates (false, true);
2068         outputDeviceNames.appendNumbersToDuplicates (false, true);
2069     }
2070 
getDeviceNames(bool wantInputNames) const2071     StringArray getDeviceNames (bool wantInputNames) const override
2072     {
2073         jassert (hasScanned); // need to call scanForDevices() before doing this
2074 
2075         return wantInputNames ? inputDeviceNames
2076                               : outputDeviceNames;
2077     }
2078 
getDefaultDeviceIndex(bool forInput) const2079     int getDefaultDeviceIndex (bool forInput) const override
2080     {
2081         jassert (hasScanned); // need to call scanForDevices() before doing this
2082 
2083         AudioDeviceID deviceID;
2084         UInt32 size = sizeof (deviceID);
2085 
2086         // if they're asking for any input channels at all, use the default input, so we
2087         // get the built-in mic rather than the built-in output with no inputs..
2088 
2089         AudioObjectPropertyAddress pa;
2090         pa.mSelector = forInput ? kAudioHardwarePropertyDefaultInputDevice
2091                                 : kAudioHardwarePropertyDefaultOutputDevice;
2092         pa.mScope    = kAudioObjectPropertyScopeWildcard;
2093         pa.mElement  = kAudioObjectPropertyElementMaster;
2094 
2095         if (AudioObjectGetPropertyData (kAudioObjectSystemObject, &pa, 0, nullptr, &size, &deviceID) == noErr)
2096         {
2097             if (forInput)
2098             {
2099                 for (int i = inputIds.size(); --i >= 0;)
2100                     if (inputIds[i] == deviceID)
2101                         return i;
2102             }
2103             else
2104             {
2105                 for (int i = outputIds.size(); --i >= 0;)
2106                     if (outputIds[i] == deviceID)
2107                         return i;
2108             }
2109         }
2110 
2111         return 0;
2112     }
2113 
getIndexOfDevice(AudioIODevice * device,bool asInput) const2114     int getIndexOfDevice (AudioIODevice* device, bool asInput) const override
2115     {
2116         jassert (hasScanned); // need to call scanForDevices() before doing this
2117 
2118         if (auto* d = dynamic_cast<CoreAudioIODevice*> (device))
2119             return asInput ? d->inputIndex
2120                            : d->outputIndex;
2121 
2122         if (auto* d = dynamic_cast<AudioIODeviceCombiner*> (device))
2123         {
2124             for (auto* dev : d->getDevices())
2125             {
2126                 auto index = getIndexOfDevice (dev, asInput);
2127 
2128                 if (index >= 0)
2129                     return index;
2130             }
2131         }
2132 
2133         return -1;
2134     }
2135 
hasSeparateInputsAndOutputs() const2136     bool hasSeparateInputsAndOutputs() const override    { return true; }
2137 
createDevice(const String & outputDeviceName,const String & inputDeviceName)2138     AudioIODevice* createDevice (const String& outputDeviceName,
2139                                  const String& inputDeviceName) override
2140     {
2141         jassert (hasScanned); // need to call scanForDevices() before doing this
2142 
2143         auto inputIndex  = inputDeviceNames.indexOf (inputDeviceName);
2144         auto outputIndex = outputDeviceNames.indexOf (outputDeviceName);
2145 
2146         auto inputDeviceID  = inputIds[inputIndex];
2147         auto outputDeviceID = outputIds[outputIndex];
2148 
2149         if (inputDeviceID == 0 && outputDeviceID == 0)
2150             return nullptr;
2151 
2152         auto combinedName = outputDeviceName.isEmpty() ? inputDeviceName
2153                                                        : outputDeviceName;
2154 
2155         if (inputDeviceID == outputDeviceID)
2156             return new CoreAudioIODevice (this, combinedName, inputDeviceID, inputIndex, outputDeviceID, outputIndex);
2157 
2158         std::unique_ptr<CoreAudioIODevice> in, out;
2159 
2160         if (inputDeviceID != 0)
2161             in.reset (new CoreAudioIODevice (this, inputDeviceName, inputDeviceID, inputIndex, 0, -1));
2162 
2163         if (outputDeviceID != 0)
2164             out.reset (new CoreAudioIODevice (this, outputDeviceName, 0, -1, outputDeviceID, outputIndex));
2165 
2166         if (in == nullptr)   return out.release();
2167         if (out == nullptr)  return in.release();
2168 
2169         std::unique_ptr<AudioIODeviceCombiner> combo (new AudioIODeviceCombiner (combinedName, this));
2170         combo->addDevice (in.release(),  true, false);
2171         combo->addDevice (out.release(), false, true);
2172         return combo.release();
2173     }
2174 
audioDeviceListChanged()2175     void audioDeviceListChanged()
2176     {
2177         scanForDevices();
2178         callDeviceChangeListeners();
2179     }
2180 
triggerAsyncAudioDeviceListChange()2181     void triggerAsyncAudioDeviceListChange()
2182     {
2183         triggerAsyncUpdate();
2184     }
2185 
2186     //==============================================================================
2187 private:
2188     StringArray inputDeviceNames, outputDeviceNames;
2189     Array<AudioDeviceID> inputIds, outputIds;
2190 
2191     bool hasScanned = false;
2192 
getNumChannels(AudioDeviceID deviceID,bool input)2193     static int getNumChannels (AudioDeviceID deviceID, bool input)
2194     {
2195         int total = 0;
2196         UInt32 size;
2197 
2198         AudioObjectPropertyAddress pa;
2199         pa.mSelector = kAudioDevicePropertyStreamConfiguration;
2200         pa.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
2201         pa.mElement = kAudioObjectPropertyElementMaster;
2202 
2203         if (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, nullptr, &size) == noErr)
2204         {
2205             HeapBlock<AudioBufferList> bufList;
2206             bufList.calloc (size, 1);
2207 
2208             if (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, bufList) == noErr)
2209             {
2210                 auto numStreams = (int) bufList->mNumberBuffers;
2211 
2212                 for (int i = 0; i < numStreams; ++i)
2213                     total += bufList->mBuffers[i].mNumberChannels;
2214             }
2215         }
2216 
2217         return total;
2218     }
2219 
hardwareListenerProc(AudioDeviceID,UInt32,const AudioObjectPropertyAddress *,void * clientData)2220     static OSStatus hardwareListenerProc (AudioDeviceID, UInt32, const AudioObjectPropertyAddress*, void* clientData)
2221     {
2222         static_cast<CoreAudioIODeviceType*> (clientData)->triggerAsyncAudioDeviceListChange();
2223         return noErr;
2224     }
2225 
handleAsyncUpdate()2226     void handleAsyncUpdate() override
2227     {
2228         audioDeviceListChanged();
2229     }
2230 
2231     JUCE_DECLARE_WEAK_REFERENCEABLE (CoreAudioIODeviceType)
2232     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioIODeviceType)
2233 };
2234 
2235 };
2236 
2237 #undef JUCE_COREAUDIOLOG
2238 
2239 } // namespace juce
2240