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, ¤tSourceID)))
529 {
530 HeapBlock<OSType> types;
531 auto num = getAllDataSourcesForDevice (deviceID, types);
532
533 for (int i = 0; i < num; ++i)
534 {
535 if (types[num] == currentSourceID)
536 {
537 result = i;
538 break;
539 }
540 }
541 }
542 }
543
544 return result;
545 }
546
setCurrentSourceIndex(int index,bool input)547 void setCurrentSourceIndex (int index, bool input)
548 {
549 if (deviceID != 0)
550 {
551 HeapBlock<OSType> types;
552 auto num = getAllDataSourcesForDevice (deviceID, types);
553
554 if (isPositiveAndBelow (index, num))
555 {
556 AudioObjectPropertyAddress pa;
557 pa.mSelector = kAudioDevicePropertyDataSource;
558 pa.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
559 pa.mElement = kAudioObjectPropertyElementMaster;
560
561 OSType typeId = types[index];
562
563 OK (AudioObjectSetPropertyData (deviceID, &pa, 0, nullptr, sizeof (typeId), &typeId));
564 }
565 }
566 }
567
getNominalSampleRate() const568 double getNominalSampleRate() const
569 {
570 AudioObjectPropertyAddress pa;
571 pa.mSelector = kAudioDevicePropertyNominalSampleRate;
572 pa.mScope = kAudioObjectPropertyScopeGlobal;
573 pa.mElement = kAudioObjectPropertyElementMaster;
574 Float64 sr = 0;
575 UInt32 size = (UInt32) sizeof (sr);
576 return OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &sr)) ? (double) sr : 0.0;
577 }
578
setNominalSampleRate(double newSampleRate) const579 bool setNominalSampleRate (double newSampleRate) const
580 {
581 if (std::abs (getNominalSampleRate() - newSampleRate) < 1.0)
582 return true;
583
584 AudioObjectPropertyAddress pa;
585 pa.mSelector = kAudioDevicePropertyNominalSampleRate;
586 pa.mScope = kAudioObjectPropertyScopeGlobal;
587 pa.mElement = kAudioObjectPropertyElementMaster;
588 Float64 sr = newSampleRate;
589 return OK (AudioObjectSetPropertyData (deviceID, &pa, 0, nullptr, sizeof (sr), &sr));
590 }
591
592 //==============================================================================
reopen(const BigInteger & inputChannels,const BigInteger & outputChannels,double newSampleRate,int bufferSizeSamples)593 String reopen (const BigInteger& inputChannels,
594 const BigInteger& outputChannels,
595 double newSampleRate, int bufferSizeSamples)
596 {
597 String error;
598 callbacksAllowed = false;
599 stopTimer();
600
601 stop (false);
602
603 updateDetailsFromDevice();
604
605 activeInputChans = inputChannels;
606 activeInputChans.setRange (inChanNames.size(),
607 activeInputChans.getHighestBit() + 1 - inChanNames.size(),
608 false);
609
610 activeOutputChans = outputChannels;
611 activeOutputChans.setRange (outChanNames.size(),
612 activeOutputChans.getHighestBit() + 1 - outChanNames.size(),
613 false);
614
615 numInputChans = activeInputChans.countNumberOfSetBits();
616 numOutputChans = activeOutputChans.countNumberOfSetBits();
617
618 if (! setNominalSampleRate (newSampleRate))
619 {
620 updateDetailsFromDevice();
621 error = "Couldn't change sample rate";
622 }
623 else
624 {
625 // change buffer size
626 AudioObjectPropertyAddress pa;
627 pa.mSelector = kAudioDevicePropertyBufferFrameSize;
628 pa.mScope = kAudioObjectPropertyScopeGlobal;
629 pa.mElement = kAudioObjectPropertyElementMaster;
630 UInt32 framesPerBuf = (UInt32) bufferSizeSamples;
631
632 if (! OK (AudioObjectSetPropertyData (deviceID, &pa, 0, nullptr, sizeof (framesPerBuf), &framesPerBuf)))
633 {
634 updateDetailsFromDevice();
635 error = "Couldn't change buffer size";
636 }
637 else
638 {
639 // Annoyingly, after changing the rate and buffer size, some devices fail to
640 // correctly report their new settings until some random time in the future, so
641 // after calling updateDetailsFromDevice, we need to manually bodge these values
642 // to make sure we're using the correct numbers..
643 updateDetailsFromDevice();
644 sampleRate = newSampleRate;
645 bufferSize = bufferSizeSamples;
646
647 if (sampleRates.size() == 0)
648 error = "Device has no available sample-rates";
649 else if (bufferSizes.size() == 0)
650 error = "Device has no available buffer-sizes";
651 }
652 }
653
654 callbacksAllowed = true;
655 return error;
656 }
657
start()658 bool start()
659 {
660 if (! started)
661 {
662 callback = nullptr;
663
664 if (deviceID != 0)
665 {
666 if (OK (AudioDeviceCreateIOProcID (deviceID, audioIOProc, this, &audioProcID)))
667 {
668 if (OK (AudioDeviceStart (deviceID, audioIOProc)))
669 {
670 started = true;
671 }
672 else
673 {
674 OK (AudioDeviceDestroyIOProcID (deviceID, audioProcID));
675 audioProcID = {};
676 }
677 }
678 }
679 }
680
681 return started;
682 }
683
setCallback(AudioIODeviceCallback * cb)684 void setCallback (AudioIODeviceCallback* cb)
685 {
686 const ScopedLock sl (callbackLock);
687 callback = cb;
688 }
689
stop(bool leaveInterruptRunning)690 void stop (bool leaveInterruptRunning)
691 {
692 {
693 const ScopedLock sl (callbackLock);
694 callback = nullptr;
695 }
696
697 if (started && (deviceID != 0) && ! leaveInterruptRunning)
698 {
699 OK (AudioDeviceStop (deviceID, audioIOProc));
700 OK (AudioDeviceDestroyIOProcID (deviceID, audioProcID));
701 audioProcID = {};
702
703 started = false;
704
705 { const ScopedLock sl (callbackLock); }
706
707 // wait until it's definitely stopped calling back..
708 for (int i = 40; --i >= 0;)
709 {
710 Thread::sleep (50);
711
712 UInt32 running = 0;
713 UInt32 size = sizeof (running);
714
715 AudioObjectPropertyAddress pa;
716 pa.mSelector = kAudioDevicePropertyDeviceIsRunning;
717 pa.mScope = kAudioObjectPropertyScopeWildcard;
718 pa.mElement = kAudioObjectPropertyElementMaster;
719
720 OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &running));
721
722 if (running == 0)
723 break;
724 }
725
726 const ScopedLock sl (callbackLock);
727 }
728 }
729
getSampleRate() const730 double getSampleRate() const { return sampleRate; }
getBufferSize() const731 int getBufferSize() const { return bufferSize; }
732
audioCallback(const AudioBufferList * inInputData,AudioBufferList * outOutputData)733 void audioCallback (const AudioBufferList* inInputData,
734 AudioBufferList* outOutputData)
735 {
736 const ScopedLock sl (callbackLock);
737
738 if (callback != nullptr)
739 {
740 for (int i = numInputChans; --i >= 0;)
741 {
742 auto& info = inputChannelInfo.getReference(i);
743 auto dest = tempInputBuffers[i];
744 auto src = ((const float*) inInputData->mBuffers[info.streamNum].mData) + info.dataOffsetSamples;
745 auto stride = info.dataStrideSamples;
746
747 if (stride != 0) // if this is zero, info is invalid
748 {
749 for (int j = bufferSize; --j >= 0;)
750 {
751 *dest++ = *src;
752 src += stride;
753 }
754 }
755 }
756
757 callback->audioDeviceIOCallback (const_cast<const float**> (tempInputBuffers.get()),
758 numInputChans,
759 tempOutputBuffers,
760 numOutputChans,
761 bufferSize);
762
763 for (int i = numOutputChans; --i >= 0;)
764 {
765 auto& info = outputChannelInfo.getReference (i);
766 auto src = tempOutputBuffers[i];
767 auto dest = ((float*) outOutputData->mBuffers[info.streamNum].mData) + info.dataOffsetSamples;
768 auto stride = info.dataStrideSamples;
769
770 if (stride != 0) // if this is zero, info is invalid
771 {
772 for (int j = bufferSize; --j >= 0;)
773 {
774 *dest = *src++;
775 dest += stride;
776 }
777 }
778 }
779 }
780 else
781 {
782 for (UInt32 i = 0; i < outOutputData->mNumberBuffers; ++i)
783 zeromem (outOutputData->mBuffers[i].mData,
784 outOutputData->mBuffers[i].mDataByteSize);
785 }
786 }
787
788 // called by callbacks
deviceDetailsChanged()789 void deviceDetailsChanged()
790 {
791 if (callbacksAllowed.get() == 1)
792 startTimer (100);
793 }
794
timerCallback()795 void timerCallback() override
796 {
797 JUCE_COREAUDIOLOG ("Device changed");
798
799 stopTimer();
800 auto oldSampleRate = sampleRate;
801 auto oldBufferSize = bufferSize;
802
803 if (! updateDetailsFromDevice())
804 owner.stopInternal();
805 else if ((oldBufferSize != bufferSize || oldSampleRate != sampleRate) && owner.shouldRestartDevice())
806 owner.restart();
807 }
808
809 //==============================================================================
810 CoreAudioIODevice& owner;
811 int inputLatency = 0;
812 int outputLatency = 0;
813 int bitDepth = 32;
814 int xruns = 0;
815 BigInteger activeInputChans, activeOutputChans;
816 StringArray inChanNames, outChanNames;
817 Array<double> sampleRates;
818 Array<int> bufferSizes;
819 AudioIODeviceCallback* callback = nullptr;
820 AudioDeviceIOProcID audioProcID = {};
821
822 private:
823 CriticalSection callbackLock;
824 AudioDeviceID deviceID;
825 bool started = false;
826 double sampleRate = 0;
827 int bufferSize = 512;
828 HeapBlock<float> audioBuffer;
829 int numInputChans = 0;
830 int numOutputChans = 0;
831 Atomic<int> callbacksAllowed { 1 };
832 const bool isInputDevice, isOutputDevice;
833
834 Array<CallbackDetailsForChannel> inputChannelInfo, outputChannelInfo;
835 HeapBlock<float*> tempInputBuffers, tempOutputBuffers;
836
837 //==============================================================================
audioIOProc(AudioDeviceID,const AudioTimeStamp *,const AudioBufferList * inInputData,const AudioTimeStamp *,AudioBufferList * outOutputData,const AudioTimeStamp *,void * device)838 static OSStatus audioIOProc (AudioDeviceID /*inDevice*/,
839 const AudioTimeStamp* /*inNow*/,
840 const AudioBufferList* inInputData,
841 const AudioTimeStamp* /*inInputTime*/,
842 AudioBufferList* outOutputData,
843 const AudioTimeStamp* /*inOutputTime*/,
844 void* device)
845 {
846 static_cast<CoreAudioInternal*> (device)->audioCallback (inInputData, outOutputData);
847 return noErr;
848 }
849
deviceListenerProc(AudioDeviceID,UInt32,const AudioObjectPropertyAddress * pa,void * inClientData)850 static OSStatus deviceListenerProc (AudioDeviceID /*inDevice*/, UInt32 /*inLine*/,
851 const AudioObjectPropertyAddress* pa, void* inClientData)
852 {
853 auto intern = static_cast<CoreAudioInternal*> (inClientData);
854
855 switch (pa->mSelector)
856 {
857 case kAudioDeviceProcessorOverload:
858 intern->xruns++;
859 break;
860
861 case kAudioDevicePropertyBufferSize:
862 case kAudioDevicePropertyBufferFrameSize:
863 case kAudioDevicePropertyNominalSampleRate:
864 case kAudioDevicePropertyStreamFormat:
865 case kAudioDevicePropertyDeviceIsAlive:
866 case kAudioStreamPropertyPhysicalFormat:
867 intern->deviceDetailsChanged();
868 break;
869
870 case kAudioDevicePropertyDeviceHasChanged:
871 case kAudioObjectPropertyOwnedObjects:
872 intern->owner.restart();
873
874 if (intern->owner.deviceType != nullptr)
875 intern->owner.deviceType->triggerAsyncAudioDeviceListChange();
876 break;
877
878 case kAudioDevicePropertyBufferSizeRange:
879 case kAudioDevicePropertyVolumeScalar:
880 case kAudioDevicePropertyMute:
881 case kAudioDevicePropertyPlayThru:
882 case kAudioDevicePropertyDataSource:
883 case kAudioDevicePropertyDeviceIsRunning:
884 break;
885 }
886
887 return noErr;
888 }
889
890 //==============================================================================
getAllDataSourcesForDevice(AudioDeviceID deviceID,HeapBlock<OSType> & types)891 static int getAllDataSourcesForDevice (AudioDeviceID deviceID, HeapBlock<OSType>& types)
892 {
893 AudioObjectPropertyAddress pa;
894 pa.mSelector = kAudioDevicePropertyDataSources;
895 pa.mScope = kAudioObjectPropertyScopeWildcard;
896 pa.mElement = kAudioObjectPropertyElementMaster;
897 UInt32 size = 0;
898
899 if (deviceID != 0
900 && AudioObjectGetPropertyDataSize (deviceID, &pa, 0, nullptr, &size) == noErr)
901 {
902 types.calloc (size, 1);
903
904 if (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, types) == noErr)
905 return size / (int) sizeof (OSType);
906 }
907
908 return 0;
909 }
910
OK(const OSStatus errorCode) const911 bool OK (const OSStatus errorCode) const
912 {
913 if (errorCode == noErr)
914 return true;
915
916 const String errorMessage ("CoreAudio error: " + String::toHexString ((int) errorCode));
917 JUCE_COREAUDIOLOG (errorMessage);
918
919 if (callback != nullptr)
920 callback->audioDeviceError (errorMessage);
921
922 return false;
923 }
924
925 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioInternal)
926 };
927
928
929 //==============================================================================
930 class CoreAudioIODevice : public AudioIODevice,
931 private Timer
932 {
933 public:
CoreAudioIODevice(CoreAudioIODeviceType * dt,const String & deviceName,AudioDeviceID inputDeviceId,int inputIndex_,AudioDeviceID outputDeviceId,int outputIndex_)934 CoreAudioIODevice (CoreAudioIODeviceType* dt,
935 const String& deviceName,
936 AudioDeviceID inputDeviceId, int inputIndex_,
937 AudioDeviceID outputDeviceId, int outputIndex_)
938 : AudioIODevice (deviceName, "CoreAudio"),
939 deviceType (dt),
940 inputIndex (inputIndex_),
941 outputIndex (outputIndex_)
942 {
943 CoreAudioInternal* device = nullptr;
944
945 if (outputDeviceId == 0 || outputDeviceId == inputDeviceId)
946 {
947 jassert (inputDeviceId != 0);
948 device = new CoreAudioInternal (*this, inputDeviceId, true, outputDeviceId != 0);
949 }
950 else
951 {
952 device = new CoreAudioInternal (*this, outputDeviceId, false, true);
953 }
954
955 jassert (device != nullptr);
956 internal.reset (device);
957
958 AudioObjectPropertyAddress pa;
959 pa.mSelector = kAudioObjectPropertySelectorWildcard;
960 pa.mScope = kAudioObjectPropertyScopeWildcard;
961 pa.mElement = kAudioObjectPropertyElementWildcard;
962
963 AudioObjectAddPropertyListener (kAudioObjectSystemObject, &pa, hardwareListenerProc, internal.get());
964 }
965
~CoreAudioIODevice()966 ~CoreAudioIODevice() override
967 {
968 close();
969
970 AudioObjectPropertyAddress pa;
971 pa.mSelector = kAudioObjectPropertySelectorWildcard;
972 pa.mScope = kAudioObjectPropertyScopeWildcard;
973 pa.mElement = kAudioObjectPropertyElementWildcard;
974
975 AudioObjectRemovePropertyListener (kAudioObjectSystemObject, &pa, hardwareListenerProc, internal.get());
976 }
977
getOutputChannelNames()978 StringArray getOutputChannelNames() override { return internal->outChanNames; }
getInputChannelNames()979 StringArray getInputChannelNames() override { return internal->inChanNames; }
980
isOpen()981 bool isOpen() override { return isOpen_; }
982
getAvailableSampleRates()983 Array<double> getAvailableSampleRates() override { return internal->sampleRates; }
getAvailableBufferSizes()984 Array<int> getAvailableBufferSizes() override { return internal->bufferSizes; }
985
getCurrentSampleRate()986 double getCurrentSampleRate() override { return internal->getSampleRate(); }
getCurrentBitDepth()987 int getCurrentBitDepth() override { return internal->bitDepth; }
getCurrentBufferSizeSamples()988 int getCurrentBufferSizeSamples() override { return internal->getBufferSize(); }
getXRunCount() const989 int getXRunCount() const noexcept override { return internal->xruns; }
990
getDefaultBufferSize()991 int getDefaultBufferSize() override
992 {
993 int best = 0;
994
995 for (int i = 0; best < 512 && i < internal->bufferSizes.size(); ++i)
996 best = internal->bufferSizes.getUnchecked(i);
997
998 if (best == 0)
999 best = 512;
1000
1001 return best;
1002 }
1003
open(const BigInteger & inputChannels,const BigInteger & outputChannels,double sampleRate,int bufferSizeSamples)1004 String open (const BigInteger& inputChannels,
1005 const BigInteger& outputChannels,
1006 double sampleRate, int bufferSizeSamples) override
1007 {
1008 isOpen_ = true;
1009 internal->xruns = 0;
1010
1011 inputChannelsRequested = inputChannels;
1012 outputChannelsRequested = outputChannels;
1013
1014 if (bufferSizeSamples <= 0)
1015 bufferSizeSamples = getDefaultBufferSize();
1016
1017 if (sampleRate <= 0)
1018 sampleRate = internal->getNominalSampleRate();
1019
1020 lastError = internal->reopen (inputChannels, outputChannels, sampleRate, bufferSizeSamples);
1021 JUCE_COREAUDIOLOG ("Opened: " << getName());
1022
1023 isOpen_ = lastError.isEmpty();
1024
1025 return lastError;
1026 }
1027
close()1028 void close() override
1029 {
1030 isOpen_ = false;
1031 internal->stop (false);
1032 }
1033
getActiveOutputChannels() const1034 BigInteger getActiveOutputChannels() const override { return internal->activeOutputChans; }
getActiveInputChannels() const1035 BigInteger getActiveInputChannels() const override { return internal->activeInputChans; }
1036
getOutputLatencyInSamples()1037 int getOutputLatencyInSamples() override
1038 {
1039 // this seems like a good guess at getting the latency right - comparing
1040 // this with a round-trip measurement, it gets it to within a few millisecs
1041 // for the built-in mac soundcard
1042 return internal->outputLatency;
1043 }
1044
getInputLatencyInSamples()1045 int getInputLatencyInSamples() override
1046 {
1047 return internal->inputLatency;
1048 }
1049
start(AudioIODeviceCallback * callback)1050 void start (AudioIODeviceCallback* callback) override
1051 {
1052 if (! isStarted)
1053 {
1054 if (callback != nullptr)
1055 callback->audioDeviceAboutToStart (this);
1056
1057 isStarted = internal->start();
1058
1059 if (isStarted)
1060 {
1061 internal->setCallback (callback);
1062 previousCallback = callback;
1063 }
1064 }
1065 }
1066
stop()1067 void stop() override
1068 {
1069 restartDevice = false;
1070
1071 if (isStarted)
1072 {
1073 auto lastCallback = internal->callback;
1074
1075 isStarted = false;
1076 internal->stop (true);
1077
1078 if (lastCallback != nullptr)
1079 lastCallback->audioDeviceStopped();
1080 }
1081 }
1082
stopInternal()1083 void stopInternal()
1084 {
1085 stop();
1086 restartDevice = true;
1087 }
1088
isPlaying()1089 bool isPlaying() override
1090 {
1091 if (internal->callback == nullptr)
1092 isStarted = false;
1093
1094 return isStarted;
1095 }
1096
getLastError()1097 String getLastError() override
1098 {
1099 return lastError;
1100 }
1101
audioDeviceListChanged()1102 void audioDeviceListChanged()
1103 {
1104 if (deviceType != nullptr)
1105 deviceType->audioDeviceListChanged();
1106 }
1107
restart()1108 void restart()
1109 {
1110 if (deviceWrapperRestartCallback != nullptr)
1111 {
1112 deviceWrapperRestartCallback();
1113 }
1114 else
1115 {
1116 {
1117 const ScopedLock sl (closeLock);
1118
1119 if (isStarted)
1120 {
1121 if (internal->callback != nullptr)
1122 previousCallback = internal->callback;
1123
1124 stopInternal();
1125 }
1126 }
1127
1128 startTimer (100);
1129 }
1130 }
1131
setCurrentSampleRate(double newSampleRate)1132 bool setCurrentSampleRate (double newSampleRate)
1133 {
1134 return internal->setNominalSampleRate (newSampleRate);
1135 }
1136
setDeviceWrapperRestartCallback(const std::function<void ()> & cb)1137 void setDeviceWrapperRestartCallback (const std::function<void()>& cb)
1138 {
1139 deviceWrapperRestartCallback = cb;
1140 }
1141
shouldRestartDevice() const1142 bool shouldRestartDevice() const noexcept { return restartDevice; }
1143
1144 WeakReference<CoreAudioIODeviceType> deviceType;
1145 int inputIndex, outputIndex;
1146
1147 private:
1148 std::unique_ptr<CoreAudioInternal> internal;
1149 bool isOpen_ = false, isStarted = false, restartDevice = true;
1150 String lastError;
1151 AudioIODeviceCallback* previousCallback = nullptr;
1152 std::function<void()> deviceWrapperRestartCallback = nullptr;
1153 BigInteger inputChannelsRequested, outputChannelsRequested;
1154 CriticalSection closeLock;
1155
timerCallback()1156 void timerCallback() override
1157 {
1158 stopTimer();
1159
1160 stopInternal();
1161
1162 internal->updateDetailsFromDevice();
1163
1164 open (inputChannelsRequested, outputChannelsRequested,
1165 getCurrentSampleRate(), getCurrentBufferSizeSamples());
1166 start (previousCallback);
1167 }
1168
hardwareListenerProc(AudioDeviceID,UInt32,const AudioObjectPropertyAddress * pa,void * inClientData)1169 static OSStatus hardwareListenerProc (AudioDeviceID /*inDevice*/, UInt32 /*inLine*/, const AudioObjectPropertyAddress* pa, void* inClientData)
1170 {
1171 switch (pa->mSelector)
1172 {
1173 case kAudioHardwarePropertyDevices:
1174 static_cast<CoreAudioInternal*> (inClientData)->deviceDetailsChanged();
1175 break;
1176
1177 case kAudioHardwarePropertyDefaultOutputDevice:
1178 case kAudioHardwarePropertyDefaultInputDevice:
1179 case kAudioHardwarePropertyDefaultSystemOutputDevice:
1180 break;
1181 }
1182
1183 return noErr;
1184 }
1185
1186 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioIODevice)
1187 };
1188
1189 //==============================================================================
1190 class AudioIODeviceCombiner : public AudioIODevice,
1191 private Thread,
1192 private Timer
1193 {
1194 public:
AudioIODeviceCombiner(const String & deviceName,CoreAudioIODeviceType * deviceType)1195 AudioIODeviceCombiner (const String& deviceName, CoreAudioIODeviceType* deviceType)
1196 : AudioIODevice (deviceName, "CoreAudio"),
1197 Thread (deviceName),
1198 owner (deviceType)
1199 {
1200 }
1201
~AudioIODeviceCombiner()1202 ~AudioIODeviceCombiner() override
1203 {
1204 close();
1205 devices.clear();
1206 }
1207
addDevice(CoreAudioIODevice * device,bool useInputs,bool useOutputs)1208 void addDevice (CoreAudioIODevice* device, bool useInputs, bool useOutputs)
1209 {
1210 jassert (device != nullptr);
1211 jassert (! isOpen());
1212 jassert (! device->isOpen());
1213 devices.add (new DeviceWrapper (*this, device, useInputs, useOutputs));
1214
1215 if (currentSampleRate == 0)
1216 currentSampleRate = device->getCurrentSampleRate();
1217
1218 if (currentBufferSize == 0)
1219 currentBufferSize = device->getCurrentBufferSizeSamples();
1220 }
1221
getDevices() const1222 Array<AudioIODevice*> getDevices() const
1223 {
1224 Array<AudioIODevice*> devs;
1225
1226 for (auto* d : devices)
1227 devs.add (d->device.get());
1228
1229 return devs;
1230 }
1231
getOutputChannelNames()1232 StringArray getOutputChannelNames() override
1233 {
1234 StringArray names;
1235
1236 for (auto* d : devices)
1237 names.addArray (d->getOutputChannelNames());
1238
1239 names.appendNumbersToDuplicates (false, true);
1240 return names;
1241 }
1242
getInputChannelNames()1243 StringArray getInputChannelNames() override
1244 {
1245 StringArray names;
1246
1247 for (auto* d : devices)
1248 names.addArray (d->getInputChannelNames());
1249
1250 names.appendNumbersToDuplicates (false, true);
1251 return names;
1252 }
1253
getAvailableSampleRates()1254 Array<double> getAvailableSampleRates() override
1255 {
1256 Array<double> commonRates;
1257 bool first = true;
1258
1259 for (auto* d : devices)
1260 {
1261 auto rates = d->device->getAvailableSampleRates();
1262
1263 if (first)
1264 {
1265 first = false;
1266 commonRates = rates;
1267 }
1268 else
1269 {
1270 commonRates.removeValuesNotIn (rates);
1271 }
1272 }
1273
1274 return commonRates;
1275 }
1276
getAvailableBufferSizes()1277 Array<int> getAvailableBufferSizes() override
1278 {
1279 Array<int> commonSizes;
1280 bool first = true;
1281
1282 for (auto* d : devices)
1283 {
1284 auto sizes = d->device->getAvailableBufferSizes();
1285
1286 if (first)
1287 {
1288 first = false;
1289 commonSizes = sizes;
1290 }
1291 else
1292 {
1293 commonSizes.removeValuesNotIn (sizes);
1294 }
1295 }
1296
1297 return commonSizes;
1298 }
1299
isOpen()1300 bool isOpen() override { return active; }
isPlaying()1301 bool isPlaying() override { return callback != nullptr; }
getCurrentSampleRate()1302 double getCurrentSampleRate() override { return currentSampleRate; }
getCurrentBufferSizeSamples()1303 int getCurrentBufferSizeSamples() override { return currentBufferSize; }
1304
getCurrentBitDepth()1305 int getCurrentBitDepth() override
1306 {
1307 int depth = 32;
1308
1309 for (auto* d : devices)
1310 depth = jmin (depth, d->device->getCurrentBitDepth());
1311
1312 return depth;
1313 }
1314
getDefaultBufferSize()1315 int getDefaultBufferSize() override
1316 {
1317 int size = 0;
1318
1319 for (auto* d : devices)
1320 size = jmax (size, d->device->getDefaultBufferSize());
1321
1322 return size;
1323 }
1324
open(const BigInteger & inputChannels,const BigInteger & outputChannels,double sampleRate,int bufferSize)1325 String open (const BigInteger& inputChannels,
1326 const BigInteger& outputChannels,
1327 double sampleRate, int bufferSize) override
1328 {
1329 inputChannelsRequested = inputChannels;
1330 outputChannelsRequested = outputChannels;
1331 sampleRateRequested = sampleRate;
1332 bufferSizeRequested = bufferSize;
1333
1334 close();
1335 active = true;
1336
1337 if (bufferSize <= 0)
1338 bufferSize = getDefaultBufferSize();
1339
1340 if (sampleRate <= 0)
1341 {
1342 auto rates = getAvailableSampleRates();
1343
1344 for (int i = 0; i < rates.size() && sampleRate < 44100.0; ++i)
1345 sampleRate = rates.getUnchecked(i);
1346 }
1347
1348 currentSampleRate = sampleRate;
1349 currentBufferSize = bufferSize;
1350
1351 const int fifoSize = bufferSize * 3 + 1;
1352 int totalInputChanIndex = 0, totalOutputChanIndex = 0;
1353 int chanIndex = 0;
1354
1355 for (auto* d : devices)
1356 {
1357 BigInteger ins (inputChannels >> totalInputChanIndex);
1358 BigInteger outs (outputChannels >> totalOutputChanIndex);
1359
1360 int numIns = d->getInputChannelNames().size();
1361 int numOuts = d->getOutputChannelNames().size();
1362
1363 totalInputChanIndex += numIns;
1364 totalOutputChanIndex += numOuts;
1365
1366 String err = d->open (ins, outs, sampleRate, bufferSize,
1367 chanIndex, fifoSize);
1368
1369 if (err.isNotEmpty())
1370 {
1371 close();
1372 lastError = err;
1373 return err;
1374 }
1375
1376 chanIndex += d->numInputChans + d->numOutputChans;
1377 }
1378
1379 fifos.setSize (chanIndex, fifoSize);
1380 fifoReadPointers = fifos.getArrayOfReadPointers();
1381 fifoWritePointers = fifos.getArrayOfWritePointers();
1382 fifos.clear();
1383 startThread (9);
1384 threadInitialised.wait();
1385
1386 return {};
1387 }
1388
close()1389 void close() override
1390 {
1391 stop();
1392 stopThread (10000);
1393 fifos.clear();
1394 active = false;
1395
1396 for (auto* d : devices)
1397 d->close();
1398 }
1399
restart(AudioIODeviceCallback * cb)1400 void restart (AudioIODeviceCallback* cb)
1401 {
1402 const ScopedLock sl (closeLock);
1403
1404 close();
1405
1406 auto newSampleRate = sampleRateRequested;
1407 auto newBufferSize = bufferSizeRequested;
1408
1409 for (auto* d : devices)
1410 {
1411 auto deviceSampleRate = d->getCurrentSampleRate();
1412
1413 if (deviceSampleRate != sampleRateRequested)
1414 {
1415 if (! getAvailableSampleRates().contains (deviceSampleRate))
1416 return;
1417
1418 for (auto* d2 : devices)
1419 if (d2 != d)
1420 d2->setCurrentSampleRate (deviceSampleRate);
1421
1422 newSampleRate = deviceSampleRate;
1423 break;
1424 }
1425 }
1426
1427 for (auto* d : devices)
1428 {
1429 auto deviceBufferSize = d->getCurrentBufferSizeSamples();
1430
1431 if (deviceBufferSize != bufferSizeRequested)
1432 {
1433 if (! getAvailableBufferSizes().contains (deviceBufferSize))
1434 return;
1435
1436 newBufferSize = deviceBufferSize;
1437 break;
1438 }
1439 }
1440
1441 open (inputChannelsRequested, outputChannelsRequested,
1442 newSampleRate, newBufferSize);
1443
1444 start (cb);
1445 }
1446
restartAsync()1447 void restartAsync()
1448 {
1449 {
1450 const ScopedLock sl (closeLock);
1451
1452 if (active)
1453 {
1454 if (callback != nullptr)
1455 previousCallback = callback;
1456
1457 close();
1458 }
1459 }
1460
1461 startTimer (100);
1462 }
1463
getActiveOutputChannels() const1464 BigInteger getActiveOutputChannels() const override
1465 {
1466 BigInteger chans;
1467 int start = 0;
1468
1469 for (auto* d : devices)
1470 {
1471 auto numChans = d->getOutputChannelNames().size();
1472
1473 if (numChans > 0)
1474 {
1475 chans |= (d->device->getActiveOutputChannels() << start);
1476 start += numChans;
1477 }
1478 }
1479
1480 return chans;
1481 }
1482
getActiveInputChannels() const1483 BigInteger getActiveInputChannels() const override
1484 {
1485 BigInteger chans;
1486 int start = 0;
1487
1488 for (auto* d : devices)
1489 {
1490 auto numChans = d->getInputChannelNames().size();
1491
1492 if (numChans > 0)
1493 {
1494 chans |= (d->device->getActiveInputChannels() << start);
1495 start += numChans;
1496 }
1497 }
1498
1499 return chans;
1500 }
1501
getOutputLatencyInSamples()1502 int getOutputLatencyInSamples() override
1503 {
1504 int lat = 0;
1505
1506 for (auto* d : devices)
1507 lat = jmax (lat, d->device->getOutputLatencyInSamples());
1508
1509 return lat + currentBufferSize * 2;
1510 }
1511
getInputLatencyInSamples()1512 int getInputLatencyInSamples() override
1513 {
1514 int lat = 0;
1515
1516 for (auto* d : devices)
1517 lat = jmax (lat, d->device->getInputLatencyInSamples());
1518
1519 return lat + currentBufferSize * 2;
1520 }
1521
start(AudioIODeviceCallback * newCallback)1522 void start (AudioIODeviceCallback* newCallback) override
1523 {
1524 if (callback != newCallback)
1525 {
1526 stop();
1527 fifos.clear();
1528
1529 for (auto* d : devices)
1530 d->start();
1531
1532 if (newCallback != nullptr)
1533 newCallback->audioDeviceAboutToStart (this);
1534
1535 const ScopedLock sl (callbackLock);
1536 callback = newCallback;
1537 previousCallback = callback;
1538 }
1539 }
1540
stop()1541 void stop() override { shutdown ({}); }
1542
getLastError()1543 String getLastError() override
1544 {
1545 return lastError;
1546 }
1547
1548 private:
1549 WeakReference<CoreAudioIODeviceType> owner;
1550 CriticalSection callbackLock;
1551 AudioIODeviceCallback* callback = nullptr;
1552 AudioIODeviceCallback* previousCallback = nullptr;
1553 double currentSampleRate = 0;
1554 int currentBufferSize = 0;
1555 bool active = false;
1556 String lastError;
1557 AudioBuffer<float> fifos;
1558 const float** fifoReadPointers = nullptr;
1559 float** fifoWritePointers = nullptr;
1560 WaitableEvent threadInitialised;
1561 CriticalSection closeLock;
1562
1563 BigInteger inputChannelsRequested, outputChannelsRequested;
1564 double sampleRateRequested = 44100;
1565 int bufferSizeRequested = 512;
1566
run()1567 void run() override
1568 {
1569 auto numSamples = currentBufferSize;
1570
1571 AudioBuffer<float> buffer (fifos.getNumChannels(), numSamples);
1572 buffer.clear();
1573
1574 Array<const float*> inputChans;
1575 Array<float*> outputChans;
1576
1577 for (auto* d : devices)
1578 {
1579 for (int j = 0; j < d->numInputChans; ++j) inputChans.add (buffer.getReadPointer (d->inputIndex + j));
1580 for (int j = 0; j < d->numOutputChans; ++j) outputChans.add (buffer.getWritePointer (d->outputIndex + j));
1581 }
1582
1583 auto numInputChans = inputChans.size();
1584 auto numOutputChans = outputChans.size();
1585
1586 inputChans.add (nullptr);
1587 outputChans.add (nullptr);
1588
1589 auto blockSizeMs = jmax (1, (int) (1000 * numSamples / currentSampleRate));
1590
1591 jassert (numInputChans + numOutputChans == buffer.getNumChannels());
1592
1593 threadInitialised.signal();
1594
1595 while (! threadShouldExit())
1596 {
1597 readInput (buffer, numSamples, blockSizeMs);
1598
1599 bool didCallback = true;
1600
1601 {
1602 const ScopedLock sl (callbackLock);
1603
1604 if (callback != nullptr)
1605 callback->audioDeviceIOCallback ((const float**) inputChans.getRawDataPointer(), numInputChans,
1606 outputChans.getRawDataPointer(), numOutputChans, numSamples);
1607 else
1608 didCallback = false;
1609 }
1610
1611 if (didCallback)
1612 {
1613 pushOutputData (buffer, numSamples, blockSizeMs);
1614 }
1615 else
1616 {
1617 for (int i = 0; i < numOutputChans; ++i)
1618 FloatVectorOperations::clear (outputChans[i], numSamples);
1619
1620 reset();
1621 }
1622 }
1623 }
1624
timerCallback()1625 void timerCallback() override
1626 {
1627 stopTimer();
1628
1629 restart (previousCallback);
1630 }
1631
shutdown(const String & error)1632 void shutdown (const String& error)
1633 {
1634 AudioIODeviceCallback* lastCallback = nullptr;
1635
1636 {
1637 const ScopedLock sl (callbackLock);
1638 std::swap (callback, lastCallback);
1639 }
1640
1641 for (auto* d : devices)
1642 d->device->stopInternal();
1643
1644 if (lastCallback != nullptr)
1645 {
1646 if (error.isNotEmpty())
1647 lastCallback->audioDeviceError (error);
1648 else
1649 lastCallback->audioDeviceStopped();
1650 }
1651 }
1652
reset()1653 void reset()
1654 {
1655 for (auto* d : devices)
1656 d->reset();
1657 }
1658
underrun()1659 void underrun()
1660 {
1661 }
1662
readInput(AudioBuffer<float> & buffer,const int numSamples,const int blockSizeMs)1663 void readInput (AudioBuffer<float>& buffer, const int numSamples, const int blockSizeMs)
1664 {
1665 for (auto* d : devices)
1666 d->done = (d->numInputChans == 0 || d->isWaitingForInput);
1667
1668 float totalWaitTimeMs = blockSizeMs * 5.0f;
1669 constexpr int numReadAttempts = 6;
1670 auto sumPower2s = [] (int maxPower) { return (1 << (maxPower + 1)) - 1; };
1671 float waitTime = totalWaitTimeMs / (float) sumPower2s (numReadAttempts - 2);
1672
1673 for (int numReadAttemptsRemaining = numReadAttempts;;)
1674 {
1675 bool anySamplesRemaining = false;
1676
1677 for (auto* d : devices)
1678 {
1679 if (! d->done)
1680 {
1681 if (d->isInputReady (numSamples))
1682 {
1683 d->readInput (buffer, numSamples);
1684 d->done = true;
1685 }
1686 else
1687 {
1688 anySamplesRemaining = true;
1689 }
1690 }
1691 }
1692
1693 if (! anySamplesRemaining)
1694 return;
1695
1696 if (--numReadAttemptsRemaining == 0)
1697 break;
1698
1699 wait (jmax (1, roundToInt (waitTime)));
1700 waitTime *= 2.0f;
1701 }
1702
1703 for (auto* d : devices)
1704 if (! d->done)
1705 for (int i = 0; i < d->numInputChans; ++i)
1706 buffer.clear (d->inputIndex + i, 0, numSamples);
1707 }
1708
pushOutputData(AudioBuffer<float> & buffer,const int numSamples,const int blockSizeMs)1709 void pushOutputData (AudioBuffer<float>& buffer, const int numSamples, const int blockSizeMs)
1710 {
1711 for (auto* d : devices)
1712 d->done = (d->numOutputChans == 0);
1713
1714 for (int tries = 5;;)
1715 {
1716 bool anyRemaining = false;
1717
1718 for (auto* d : devices)
1719 {
1720 if (! d->done)
1721 {
1722 if (d->isOutputReady (numSamples))
1723 {
1724 d->pushOutputData (buffer, numSamples);
1725 d->done = true;
1726 }
1727 else
1728 {
1729 anyRemaining = true;
1730 }
1731 }
1732 }
1733
1734 if ((! anyRemaining) || --tries == 0)
1735 return;
1736
1737 wait (blockSizeMs);
1738 }
1739 }
1740
handleAudioDeviceAboutToStart(AudioIODevice * device)1741 void handleAudioDeviceAboutToStart (AudioIODevice* device)
1742 {
1743 const ScopedLock sl (callbackLock);
1744
1745 auto newSampleRate = device->getCurrentSampleRate();
1746 auto commonRates = getAvailableSampleRates();
1747
1748 if (! commonRates.contains (newSampleRate))
1749 {
1750 commonRates.sort();
1751
1752 if (newSampleRate < commonRates.getFirst() || newSampleRate > commonRates.getLast())
1753 {
1754 newSampleRate = jlimit (commonRates.getFirst(), commonRates.getLast(), newSampleRate);
1755 }
1756 else
1757 {
1758 for (auto it = commonRates.begin(); it < commonRates.end() - 1; ++it)
1759 {
1760 if (it[0] < newSampleRate && it[1] > newSampleRate)
1761 {
1762 newSampleRate = newSampleRate - it[0] < it[1] - newSampleRate ? it[0] : it[1];
1763 break;
1764 }
1765 }
1766 }
1767 }
1768
1769 currentSampleRate = newSampleRate;
1770 bool anySampleRateChanges = false;
1771
1772 for (auto* d : devices)
1773 {
1774 if (d->getCurrentSampleRate() != currentSampleRate)
1775 {
1776 d->setCurrentSampleRate (currentSampleRate);
1777 anySampleRateChanges = true;
1778 }
1779 }
1780
1781 if (anySampleRateChanges && owner != nullptr)
1782 owner->audioDeviceListChanged();
1783
1784 if (callback != nullptr)
1785 callback->audioDeviceAboutToStart (device);
1786 }
1787
handleAudioDeviceStopped()1788 void handleAudioDeviceStopped() { shutdown ({}); }
handleAudioDeviceError(const String & errorMessage)1789 void handleAudioDeviceError (const String& errorMessage) { shutdown (errorMessage.isNotEmpty() ? errorMessage : String ("unknown")); }
1790
1791 //==============================================================================
1792 struct DeviceWrapper : private AudioIODeviceCallback
1793 {
DeviceWrapperjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1794 DeviceWrapper (AudioIODeviceCombiner& cd, CoreAudioIODevice* d, bool useIns, bool useOuts)
1795 : owner (cd), device (d),
1796 useInputs (useIns), useOutputs (useOuts)
1797 {
1798 d->setDeviceWrapperRestartCallback ([this] { owner.restartAsync(); });
1799 }
1800
~DeviceWrapperjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1801 ~DeviceWrapper() override
1802 {
1803 close();
1804 }
1805
openjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1806 String open (const BigInteger& inputChannels, const BigInteger& outputChannels,
1807 double sampleRate, int bufferSize, int channelIndex, int fifoSize)
1808 {
1809 inputFifo.setTotalSize (fifoSize);
1810 outputFifo.setTotalSize (fifoSize);
1811 inputFifo.reset();
1812 outputFifo.reset();
1813
1814 auto err = device->open (useInputs ? inputChannels : BigInteger(),
1815 useOutputs ? outputChannels : BigInteger(),
1816 sampleRate, bufferSize);
1817
1818 numInputChans = useInputs ? device->getActiveInputChannels().countNumberOfSetBits() : 0;
1819 numOutputChans = useOutputs ? device->getActiveOutputChannels().countNumberOfSetBits() : 0;
1820
1821 isWaitingForInput = numInputChans > 0;
1822
1823 inputIndex = channelIndex;
1824 outputIndex = channelIndex + numInputChans;
1825
1826 return err;
1827 }
1828
closejuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1829 void close()
1830 {
1831 device->close();
1832 }
1833
startjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1834 void start()
1835 {
1836 reset();
1837 device->start (this);
1838 }
1839
resetjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1840 void reset()
1841 {
1842 inputFifo.reset();
1843 outputFifo.reset();
1844 }
1845
getOutputChannelNamesjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1846 StringArray getOutputChannelNames() const { return useOutputs ? device->getOutputChannelNames() : StringArray(); }
getInputChannelNamesjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1847 StringArray getInputChannelNames() const { return useInputs ? device->getInputChannelNames() : StringArray(); }
1848
isInputReadyjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1849 bool isInputReady (int numSamples) const noexcept
1850 {
1851 return numInputChans == 0 || inputFifo.getNumReady() >= numSamples;
1852 }
1853
readInputjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1854 void readInput (AudioBuffer<float>& destBuffer, int numSamples)
1855 {
1856 if (numInputChans == 0)
1857 return;
1858
1859 int start1, size1, start2, size2;
1860 inputFifo.prepareToRead (numSamples, start1, size1, start2, size2);
1861
1862 for (int i = 0; i < numInputChans; ++i)
1863 {
1864 auto index = inputIndex + i;
1865 auto dest = destBuffer.getWritePointer (index);
1866 auto src = owner.fifoReadPointers[index];
1867
1868 if (size1 > 0) FloatVectorOperations::copy (dest, src + start1, size1);
1869 if (size2 > 0) FloatVectorOperations::copy (dest + size1, src + start2, size2);
1870 }
1871
1872 inputFifo.finishedRead (size1 + size2);
1873 }
1874
isOutputReadyjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1875 bool isOutputReady (int numSamples) const noexcept
1876 {
1877 return numOutputChans == 0 || outputFifo.getFreeSpace() >= numSamples;
1878 }
1879
pushOutputDatajuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1880 void pushOutputData (AudioBuffer<float>& srcBuffer, int numSamples)
1881 {
1882 if (numOutputChans == 0)
1883 return;
1884
1885 int start1, size1, start2, size2;
1886 outputFifo.prepareToWrite (numSamples, start1, size1, start2, size2);
1887
1888 for (int i = 0; i < numOutputChans; ++i)
1889 {
1890 auto index = outputIndex + i;
1891 auto dest = owner.fifoWritePointers[index];
1892 auto src = srcBuffer.getReadPointer (index);
1893
1894 if (size1 > 0) FloatVectorOperations::copy (dest + start1, src, size1);
1895 if (size2 > 0) FloatVectorOperations::copy (dest + start2, src + size1, size2);
1896 }
1897
1898 outputFifo.finishedWrite (size1 + size2);
1899 }
1900
audioDeviceIOCallbackjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1901 void audioDeviceIOCallback (const float** inputChannelData, int numInputChannels,
1902 float** outputChannelData, int numOutputChannels,
1903 int numSamples) override
1904 {
1905 if (numInputChannels > 0)
1906 {
1907 isWaitingForInput = false;
1908
1909 int start1, size1, start2, size2;
1910 inputFifo.prepareToWrite (numSamples, start1, size1, start2, size2);
1911
1912 if (size1 + size2 < numSamples)
1913 {
1914 inputFifo.reset();
1915 inputFifo.prepareToWrite (numSamples, start1, size1, start2, size2);
1916 }
1917
1918 for (int i = 0; i < numInputChannels; ++i)
1919 {
1920 auto dest = owner.fifoWritePointers[inputIndex + i];
1921 auto src = inputChannelData[i];
1922
1923 if (size1 > 0) FloatVectorOperations::copy (dest + start1, src, size1);
1924 if (size2 > 0) FloatVectorOperations::copy (dest + start2, src + size1, size2);
1925 }
1926
1927 auto totalSize = size1 + size2;
1928 inputFifo.finishedWrite (totalSize);
1929
1930 if (numSamples > totalSize)
1931 {
1932 auto samplesRemaining = numSamples - totalSize;
1933
1934 for (int i = 0; i < numInputChans; ++i)
1935 FloatVectorOperations::clear (owner.fifoWritePointers[inputIndex + i] + totalSize, samplesRemaining);
1936
1937 owner.underrun();
1938 }
1939 }
1940
1941 if (numOutputChannels > 0)
1942 {
1943 int start1, size1, start2, size2;
1944 outputFifo.prepareToRead (numSamples, start1, size1, start2, size2);
1945
1946 if (size1 + size2 < numSamples)
1947 {
1948 Thread::sleep (1);
1949 outputFifo.prepareToRead (numSamples, start1, size1, start2, size2);
1950 }
1951
1952 for (int i = 0; i < numOutputChannels; ++i)
1953 {
1954 auto dest = outputChannelData[i];
1955 auto src = owner.fifoReadPointers[outputIndex + i];
1956
1957 if (size1 > 0) FloatVectorOperations::copy (dest, src + start1, size1);
1958 if (size2 > 0) FloatVectorOperations::copy (dest + size1, src + start2, size2);
1959 }
1960
1961 auto totalSize = size1 + size2;
1962 outputFifo.finishedRead (totalSize);
1963
1964 if (numSamples > totalSize)
1965 {
1966 auto samplesRemaining = numSamples - totalSize;
1967
1968 for (int i = 0; i < numOutputChannels; ++i)
1969 FloatVectorOperations::clear (outputChannelData[i] + totalSize, samplesRemaining);
1970
1971 owner.underrun();
1972 }
1973 }
1974
1975 owner.notify();
1976 }
1977
getCurrentSampleRatejuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1978 double getCurrentSampleRate() { return device->getCurrentSampleRate(); }
setCurrentSampleRatejuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1979 bool setCurrentSampleRate (double newSampleRate) { return device->setCurrentSampleRate (newSampleRate); }
getCurrentBufferSizeSamplesjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1980 int getCurrentBufferSizeSamples() { return device->getCurrentBufferSizeSamples(); }
1981
audioDeviceAboutToStartjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1982 void audioDeviceAboutToStart (AudioIODevice* d) override { owner.handleAudioDeviceAboutToStart (d); }
audioDeviceStoppedjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1983 void audioDeviceStopped() override { owner.handleAudioDeviceStopped(); }
audioDeviceErrorjuce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper1984 void audioDeviceError (const String& errorMessage) override { owner.handleAudioDeviceError (errorMessage); }
1985
1986 AudioIODeviceCombiner& owner;
1987 std::unique_ptr<CoreAudioIODevice> device;
1988 int inputIndex = 0, numInputChans = 0, outputIndex = 0, numOutputChans = 0;
1989 bool useInputs = false, useOutputs = false;
1990 std::atomic<bool> isWaitingForInput { false };
1991 AbstractFifo inputFifo { 32 }, outputFifo { 32 };
1992 bool done = false;
1993
1994 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DeviceWrapper)
1995 };
1996
1997 OwnedArray<DeviceWrapper> devices;
1998
1999 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioIODeviceCombiner)
2000 };
2001
2002
2003 //==============================================================================
2004 class CoreAudioIODeviceType : public AudioIODeviceType,
2005 private AsyncUpdater
2006 {
2007 public:
CoreAudioIODeviceType()2008 CoreAudioIODeviceType() : AudioIODeviceType ("CoreAudio")
2009 {
2010 AudioObjectPropertyAddress pa;
2011 pa.mSelector = kAudioHardwarePropertyDevices;
2012 pa.mScope = kAudioObjectPropertyScopeWildcard;
2013 pa.mElement = kAudioObjectPropertyElementWildcard;
2014
2015 AudioObjectAddPropertyListener (kAudioObjectSystemObject, &pa, hardwareListenerProc, this);
2016 }
2017
~CoreAudioIODeviceType()2018 ~CoreAudioIODeviceType() override
2019 {
2020 AudioObjectPropertyAddress pa;
2021 pa.mSelector = kAudioHardwarePropertyDevices;
2022 pa.mScope = kAudioObjectPropertyScopeWildcard;
2023 pa.mElement = kAudioObjectPropertyElementWildcard;
2024
2025 AudioObjectRemovePropertyListener (kAudioObjectSystemObject, &pa, hardwareListenerProc, this);
2026 }
2027
2028 //==============================================================================
scanForDevices()2029 void scanForDevices() override
2030 {
2031 hasScanned = true;
2032
2033 inputDeviceNames.clear();
2034 outputDeviceNames.clear();
2035 inputIds.clear();
2036 outputIds.clear();
2037
2038 UInt32 size;
2039
2040 AudioObjectPropertyAddress pa;
2041 pa.mSelector = kAudioHardwarePropertyDevices;
2042 pa.mScope = kAudioObjectPropertyScopeWildcard;
2043 pa.mElement = kAudioObjectPropertyElementMaster;
2044
2045 if (AudioObjectGetPropertyDataSize (kAudioObjectSystemObject, &pa, 0, nullptr, &size) == noErr)
2046 {
2047 HeapBlock<AudioDeviceID> devs;
2048 devs.calloc (size, 1);
2049
2050 if (AudioObjectGetPropertyData (kAudioObjectSystemObject, &pa, 0, nullptr, &size, devs) == noErr)
2051 {
2052 auto num = (int) size / (int) sizeof (AudioDeviceID);
2053
2054 for (int i = 0; i < num; ++i)
2055 {
2056 char name[1024];
2057 size = sizeof (name);
2058 pa.mSelector = kAudioDevicePropertyDeviceName;
2059
2060 if (AudioObjectGetPropertyData (devs[i], &pa, 0, nullptr, &size, name) == noErr)
2061 {
2062 auto nameString = String::fromUTF8 (name, (int) strlen (name));
2063 auto numIns = getNumChannels (devs[i], true);
2064 auto numOuts = getNumChannels (devs[i], false);
2065
2066 if (numIns > 0)
2067 {
2068 inputDeviceNames.add (nameString);
2069 inputIds.add (devs[i]);
2070 }
2071
2072 if (numOuts > 0)
2073 {
2074 outputDeviceNames.add (nameString);
2075 outputIds.add (devs[i]);
2076 }
2077 }
2078 }
2079 }
2080 }
2081
2082 inputDeviceNames.appendNumbersToDuplicates (false, true);
2083 outputDeviceNames.appendNumbersToDuplicates (false, true);
2084 }
2085
getDeviceNames(bool wantInputNames) const2086 StringArray getDeviceNames (bool wantInputNames) const override
2087 {
2088 jassert (hasScanned); // need to call scanForDevices() before doing this
2089
2090 return wantInputNames ? inputDeviceNames
2091 : outputDeviceNames;
2092 }
2093
getDefaultDeviceIndex(bool forInput) const2094 int getDefaultDeviceIndex (bool forInput) const override
2095 {
2096 jassert (hasScanned); // need to call scanForDevices() before doing this
2097
2098 AudioDeviceID deviceID;
2099 UInt32 size = sizeof (deviceID);
2100
2101 // if they're asking for any input channels at all, use the default input, so we
2102 // get the built-in mic rather than the built-in output with no inputs..
2103
2104 AudioObjectPropertyAddress pa;
2105 pa.mSelector = forInput ? kAudioHardwarePropertyDefaultInputDevice
2106 : kAudioHardwarePropertyDefaultOutputDevice;
2107 pa.mScope = kAudioObjectPropertyScopeWildcard;
2108 pa.mElement = kAudioObjectPropertyElementMaster;
2109
2110 if (AudioObjectGetPropertyData (kAudioObjectSystemObject, &pa, 0, nullptr, &size, &deviceID) == noErr)
2111 {
2112 if (forInput)
2113 {
2114 for (int i = inputIds.size(); --i >= 0;)
2115 if (inputIds[i] == deviceID)
2116 return i;
2117 }
2118 else
2119 {
2120 for (int i = outputIds.size(); --i >= 0;)
2121 if (outputIds[i] == deviceID)
2122 return i;
2123 }
2124 }
2125
2126 return 0;
2127 }
2128
getIndexOfDevice(AudioIODevice * device,bool asInput) const2129 int getIndexOfDevice (AudioIODevice* device, bool asInput) const override
2130 {
2131 jassert (hasScanned); // need to call scanForDevices() before doing this
2132
2133 if (auto* d = dynamic_cast<CoreAudioIODevice*> (device))
2134 return asInput ? d->inputIndex
2135 : d->outputIndex;
2136
2137 if (auto* d = dynamic_cast<AudioIODeviceCombiner*> (device))
2138 {
2139 for (auto* dev : d->getDevices())
2140 {
2141 auto index = getIndexOfDevice (dev, asInput);
2142
2143 if (index >= 0)
2144 return index;
2145 }
2146 }
2147
2148 return -1;
2149 }
2150
hasSeparateInputsAndOutputs() const2151 bool hasSeparateInputsAndOutputs() const override { return true; }
2152
createDevice(const String & outputDeviceName,const String & inputDeviceName)2153 AudioIODevice* createDevice (const String& outputDeviceName,
2154 const String& inputDeviceName) override
2155 {
2156 jassert (hasScanned); // need to call scanForDevices() before doing this
2157
2158 auto inputIndex = inputDeviceNames.indexOf (inputDeviceName);
2159 auto outputIndex = outputDeviceNames.indexOf (outputDeviceName);
2160
2161 auto inputDeviceID = inputIds[inputIndex];
2162 auto outputDeviceID = outputIds[outputIndex];
2163
2164 if (inputDeviceID == 0 && outputDeviceID == 0)
2165 return nullptr;
2166
2167 auto combinedName = outputDeviceName.isEmpty() ? inputDeviceName
2168 : outputDeviceName;
2169
2170 if (inputDeviceID == outputDeviceID)
2171 return new CoreAudioIODevice (this, combinedName, inputDeviceID, inputIndex, outputDeviceID, outputIndex);
2172
2173 std::unique_ptr<CoreAudioIODevice> in, out;
2174
2175 if (inputDeviceID != 0)
2176 in.reset (new CoreAudioIODevice (this, inputDeviceName, inputDeviceID, inputIndex, 0, -1));
2177
2178 if (outputDeviceID != 0)
2179 out.reset (new CoreAudioIODevice (this, outputDeviceName, 0, -1, outputDeviceID, outputIndex));
2180
2181 if (in == nullptr) return out.release();
2182 if (out == nullptr) return in.release();
2183
2184 std::unique_ptr<AudioIODeviceCombiner> combo (new AudioIODeviceCombiner (combinedName, this));
2185 combo->addDevice (in.release(), true, false);
2186 combo->addDevice (out.release(), false, true);
2187 return combo.release();
2188 }
2189
audioDeviceListChanged()2190 void audioDeviceListChanged()
2191 {
2192 scanForDevices();
2193 callDeviceChangeListeners();
2194 }
2195
triggerAsyncAudioDeviceListChange()2196 void triggerAsyncAudioDeviceListChange()
2197 {
2198 triggerAsyncUpdate();
2199 }
2200
2201 //==============================================================================
2202 private:
2203 StringArray inputDeviceNames, outputDeviceNames;
2204 Array<AudioDeviceID> inputIds, outputIds;
2205
2206 bool hasScanned = false;
2207
getNumChannels(AudioDeviceID deviceID,bool input)2208 static int getNumChannels (AudioDeviceID deviceID, bool input)
2209 {
2210 int total = 0;
2211 UInt32 size;
2212
2213 AudioObjectPropertyAddress pa;
2214 pa.mSelector = kAudioDevicePropertyStreamConfiguration;
2215 pa.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
2216 pa.mElement = kAudioObjectPropertyElementMaster;
2217
2218 if (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, nullptr, &size) == noErr)
2219 {
2220 HeapBlock<AudioBufferList> bufList;
2221 bufList.calloc (size, 1);
2222
2223 if (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, bufList) == noErr)
2224 {
2225 auto numStreams = (int) bufList->mNumberBuffers;
2226
2227 for (int i = 0; i < numStreams; ++i)
2228 total += bufList->mBuffers[i].mNumberChannels;
2229 }
2230 }
2231
2232 return total;
2233 }
2234
hardwareListenerProc(AudioDeviceID,UInt32,const AudioObjectPropertyAddress *,void * clientData)2235 static OSStatus hardwareListenerProc (AudioDeviceID, UInt32, const AudioObjectPropertyAddress*, void* clientData)
2236 {
2237 static_cast<CoreAudioIODeviceType*> (clientData)->triggerAsyncAudioDeviceListChange();
2238 return noErr;
2239 }
2240
handleAsyncUpdate()2241 void handleAsyncUpdate() override
2242 {
2243 audioDeviceListChanged();
2244 }
2245
2246 JUCE_DECLARE_WEAK_REFERENCEABLE (CoreAudioIODeviceType)
2247 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioIODeviceType)
2248 };
2249
2250 };
2251
2252 #undef JUCE_COREAUDIOLOG
2253
2254 } // namespace juce
2255