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