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