OpenShot Library | libopenshot-audio 0.2.0
juce_AudioDeviceManager.cpp
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2017 - ROLI Ltd.
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
15
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18 DISCLAIMED.
19
20 ==============================================================================
21*/
22
23namespace juce
24{
25
26bool AudioDeviceManager::AudioDeviceSetup::operator== (const AudioDeviceManager::AudioDeviceSetup& other) const
27{
28 return outputDeviceName == other.outputDeviceName
29 && inputDeviceName == other.inputDeviceName
30 && sampleRate == other.sampleRate
31 && bufferSize == other.bufferSize
32 && inputChannels == other.inputChannels
33 && useDefaultInputChannels == other.useDefaultInputChannels
34 && outputChannels == other.outputChannels
35 && useDefaultOutputChannels == other.useDefaultOutputChannels;
36}
37
38bool AudioDeviceManager::AudioDeviceSetup::operator!= (const AudioDeviceManager::AudioDeviceSetup& other) const
39{
40 return ! operator== (other);
41}
42
43//==============================================================================
45 public MidiInputCallback,
47{
48public:
49 CallbackHandler (AudioDeviceManager& adm) noexcept : owner (adm) {}
50
51private:
52 void audioDeviceIOCallback (const float** ins, int numIns, float** outs, int numOuts, int numSamples) override
53 {
54 owner.audioDeviceIOCallbackInt (ins, numIns, outs, numOuts, numSamples);
55 }
56
57 void audioDeviceAboutToStart (AudioIODevice* device) override
58 {
59 owner.audioDeviceAboutToStartInt (device);
60 }
61
62 void audioDeviceStopped() override
63 {
64 owner.audioDeviceStoppedInt();
65 }
66
67 void audioDeviceError (const String& message) override
68 {
69 owner.audioDeviceErrorInt (message);
70 }
71
72 void handleIncomingMidiMessage (MidiInput* source, const MidiMessage& message) override
73 {
74 owner.handleIncomingMidiMessageInt (source, message);
75 }
76
77 void audioDeviceListChanged() override
78 {
79 owner.audioDeviceListChanged();
80 }
81
82 AudioDeviceManager& owner;
83
84 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CallbackHandler)
85};
86
87//==============================================================================
89{
90 callbackHandler.reset (new CallbackHandler (*this));
91}
92
94{
95 currentAudioDevice.reset();
96 defaultMidiOutput.reset();
97}
98
99//==============================================================================
100void AudioDeviceManager::createDeviceTypesIfNeeded()
101{
102 if (availableDeviceTypes.size() == 0)
103 {
106
107 for (auto* t : types)
109
110 types.clear (false);
111
112 if (auto* first = availableDeviceTypes.getFirst())
113 currentDeviceType = first->getTypeName();
114 }
115}
116
118{
119 scanDevicesIfNeeded();
120 return availableDeviceTypes;
121}
122
123void AudioDeviceManager::audioDeviceListChanged()
124{
125 if (currentAudioDevice != nullptr)
126 {
127 auto isCurrentDeviceStillAvailable = [&]
128 {
129 for (auto* dt : availableDeviceTypes)
130 if (currentAudioDevice->getTypeName() == dt->getTypeName())
131 for (auto& dn : dt->getDeviceNames())
132 if (currentAudioDevice->getName() == dn)
133 return true;
134
135 return false;
136 };
137
138 if (! isCurrentDeviceStillAvailable())
139 {
141
142 std::unique_ptr<XmlElement> e (createStateXml());
143
144 if (e == nullptr)
145 initialiseDefault (preferredDeviceName, &currentSetup);
146 else
147 initialiseFromXML (*e, true, preferredDeviceName, &currentSetup);
148 }
149
150 if (currentAudioDevice != nullptr)
151 {
152 currentSetup.sampleRate = currentAudioDevice->getCurrentSampleRate();
153 currentSetup.bufferSize = currentAudioDevice->getCurrentBufferSizeSamples();
154 currentSetup.inputChannels = currentAudioDevice->getActiveInputChannels();
155 currentSetup.outputChannels = currentAudioDevice->getActiveOutputChannels();
156 }
157 }
158
160}
161
162//==============================================================================
163static void addIfNotNull (OwnedArray<AudioIODeviceType>& list, AudioIODeviceType* const device)
164{
165 if (device != nullptr)
166 list.add (device);
167}
168
170{
171 addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI (false));
172 addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI (true));
183}
184
186{
187 if (newDeviceType != nullptr)
188 {
189 jassert (lastDeviceTypeConfigs.size() == availableDeviceTypes.size());
190 availableDeviceTypes.add (newDeviceType);
191 lastDeviceTypeConfigs.add (new AudioDeviceSetup());
192
193 newDeviceType->addListener (callbackHandler.get());
194 }
195}
196
197static bool deviceListContains (AudioIODeviceType* type, bool isInput, const String& name)
198{
199 for (auto& deviceName : type->getDeviceNames (isInput))
200 if (deviceName.trim().equalsIgnoreCase (name.trim()))
201 return true;
202
203 return false;
204}
205
206//==============================================================================
207String AudioDeviceManager::initialise (const int numInputChannelsNeeded,
208 const int numOutputChannelsNeeded,
209 const XmlElement* const xml,
210 const bool selectDefaultDeviceOnFailure,
211 const String& preferredDefaultDeviceName,
212 const AudioDeviceSetup* preferredSetupOptions)
213{
214 scanDevicesIfNeeded();
215
216 numInputChansNeeded = numInputChannelsNeeded;
217 numOutputChansNeeded = numOutputChannelsNeeded;
218 preferredDeviceName = preferredDefaultDeviceName;
219
220 if (xml != nullptr && xml->hasTagName ("DEVICESETUP"))
221 return initialiseFromXML (*xml, selectDefaultDeviceOnFailure,
222 preferredDeviceName, preferredSetupOptions);
223
224 return initialiseDefault (preferredDeviceName, preferredSetupOptions);
225}
226
227String AudioDeviceManager::initialiseDefault (const String& preferredDefaultDeviceName,
228 const AudioDeviceSetup* preferredSetupOptions)
229{
230 AudioDeviceSetup setup;
231
232 if (preferredSetupOptions != nullptr)
233 {
234 setup = *preferredSetupOptions;
235 }
236 else if (preferredDefaultDeviceName.isNotEmpty())
237 {
238 for (auto* type : availableDeviceTypes)
239 {
240 for (auto& out : type->getDeviceNames (false))
241 {
242 if (out.matchesWildcard (preferredDefaultDeviceName, true))
243 {
244 setup.outputDeviceName = out;
245 break;
246 }
247 }
248
249 for (auto& in : type->getDeviceNames (true))
250 {
251 if (in.matchesWildcard (preferredDefaultDeviceName, true))
252 {
253 setup.inputDeviceName = in;
254 break;
255 }
256 }
257 }
258 }
259
260 insertDefaultDeviceNames (setup);
261 return setAudioDeviceSetup (setup, false);
262}
263
264String AudioDeviceManager::initialiseFromXML (const XmlElement& xml,
265 bool selectDefaultDeviceOnFailure,
266 const String& preferredDefaultDeviceName,
267 const AudioDeviceSetup* preferredSetupOptions)
268{
269 lastExplicitSettings.reset (new XmlElement (xml));
270
271 String error;
272 AudioDeviceSetup setup;
273
274 if (preferredSetupOptions != nullptr)
275 setup = *preferredSetupOptions;
276
277 if (xml.getStringAttribute ("audioDeviceName").isNotEmpty())
278 {
279 setup.inputDeviceName = setup.outputDeviceName
280 = xml.getStringAttribute ("audioDeviceName");
281 }
282 else
283 {
284 setup.inputDeviceName = xml.getStringAttribute ("audioInputDeviceName");
285 setup.outputDeviceName = xml.getStringAttribute ("audioOutputDeviceName");
286 }
287
288 currentDeviceType = xml.getStringAttribute ("deviceType");
289
290 if (findType (currentDeviceType) == nullptr)
291 {
292 if (auto* type = findType (setup.inputDeviceName, setup.outputDeviceName))
293 currentDeviceType = type->getTypeName();
294 else if (auto* firstType = availableDeviceTypes.getFirst())
295 currentDeviceType = firstType->getTypeName();
296 }
297
298 setup.bufferSize = xml.getIntAttribute ("audioDeviceBufferSize", setup.bufferSize);
299 setup.sampleRate = xml.getDoubleAttribute ("audioDeviceRate", setup.sampleRate);
300
301 setup.inputChannels .parseString (xml.getStringAttribute ("audioDeviceInChans", "11"), 2);
302 setup.outputChannels.parseString (xml.getStringAttribute ("audioDeviceOutChans", "11"), 2);
303
304 setup.useDefaultInputChannels = ! xml.hasAttribute ("audioDeviceInChans");
305 setup.useDefaultOutputChannels = ! xml.hasAttribute ("audioDeviceOutChans");
306
307 error = setAudioDeviceSetup (setup, true);
308
309 midiInsFromXml.clear();
310
311 forEachXmlChildElementWithTagName (xml, c, "MIDIINPUT")
312 midiInsFromXml.add (c->getStringAttribute ("name"));
313
314 for (auto& m : MidiInput::getDevices())
315 setMidiInputEnabled (m, midiInsFromXml.contains (m));
316
317 if (error.isNotEmpty() && selectDefaultDeviceOnFailure)
318 error = initialise (numInputChansNeeded, numOutputChansNeeded,
319 nullptr, false, preferredDefaultDeviceName);
320
321 setDefaultMidiOutput (xml.getStringAttribute ("defaultMidiOutput"));
322
323 return error;
324}
325
327 int numOutputChannelsNeeded)
328{
329 lastExplicitSettings.reset();
330
331 return initialise (numInputChannelsNeeded, numOutputChannelsNeeded,
332 nullptr, false, {}, nullptr);
333}
334
335void AudioDeviceManager::insertDefaultDeviceNames (AudioDeviceSetup& setup) const
336{
337 if (auto* type = getCurrentDeviceTypeObject())
338 {
339 if (numOutputChansNeeded > 0 && setup.outputDeviceName.isEmpty())
340 setup.outputDeviceName = type->getDeviceNames (false) [type->getDefaultDeviceIndex (false)];
341
342 if (numInputChansNeeded > 0 && setup.inputDeviceName.isEmpty())
343 setup.inputDeviceName = type->getDeviceNames (true) [type->getDefaultDeviceIndex (true)];
344 }
345}
346
348{
349 return createCopyIfNotNull (lastExplicitSettings.get());
350}
351
352//==============================================================================
353void AudioDeviceManager::scanDevicesIfNeeded()
354{
355 if (listNeedsScanning)
356 {
357 listNeedsScanning = false;
358
359 createDeviceTypesIfNeeded();
360
361 for (auto* type : availableDeviceTypes)
362 type->scanForDevices();
363 }
364}
365
366AudioIODeviceType* AudioDeviceManager::findType (const String& typeName)
367{
368 scanDevicesIfNeeded();
369
370 for (auto* type : availableDeviceTypes)
371 if (type->getTypeName() == typeName)
372 return type;
373
374 return {};
375}
376
377AudioIODeviceType* AudioDeviceManager::findType (const String& inputName, const String& outputName)
378{
379 scanDevicesIfNeeded();
380
381 for (auto* type : availableDeviceTypes)
382 if ((inputName.isNotEmpty() && deviceListContains (type, true, inputName))
383 || (outputName.isNotEmpty() && deviceListContains (type, false, outputName)))
384 return type;
385
386 return {};
387}
388
390{
391 return currentSetup;
392}
393
395{
396 setup = currentSetup;
397}
398
399void AudioDeviceManager::deleteCurrentDevice()
400{
401 currentAudioDevice.reset();
402 currentSetup.inputDeviceName.clear();
403 currentSetup.outputDeviceName.clear();
404}
405
406void AudioDeviceManager::setCurrentAudioDeviceType (const String& type, bool treatAsChosenDevice)
407{
408 for (int i = 0; i < availableDeviceTypes.size(); ++i)
409 {
410 if (availableDeviceTypes.getUnchecked(i)->getTypeName() == type
411 && currentDeviceType != type)
412 {
413 if (currentAudioDevice != nullptr)
414 {
416 Thread::sleep (1500); // allow a moment for OS devices to sort themselves out, to help
417 // avoid things like DirectSound/ASIO clashes
418 }
419
420 currentDeviceType = type;
421
422 AudioDeviceSetup s (*lastDeviceTypeConfigs.getUnchecked(i));
423 insertDefaultDeviceNames (s);
424
425 setAudioDeviceSetup (s, treatAsChosenDevice);
426
428 break;
429 }
430 }
431}
432
434{
435 for (auto* type : availableDeviceTypes)
436 if (type->getTypeName() == currentDeviceType)
437 return type;
438
439 return availableDeviceTypes.getFirst();
440}
441
443 bool treatAsChosenDevice)
444{
445 jassert (&newSetup != &currentSetup); // this will have no effect
446
447 if (newSetup == currentSetup && currentAudioDevice != nullptr)
448 return {};
449
450 if (! (newSetup == currentSetup))
452
453 stopDevice();
454
455 if (! newSetup.useDefaultInputChannels)
456 numInputChansNeeded = newSetup.inputChannels.countNumberOfSetBits();
457
458 if (! newSetup.useDefaultOutputChannels)
459 numOutputChansNeeded = newSetup.outputChannels.countNumberOfSetBits();
460
461 auto* type = getCurrentDeviceTypeObject();
462
463 if (type == nullptr)
464 {
465 deleteCurrentDevice();
466
467 if (treatAsChosenDevice)
468 updateXml();
469
470 return {};
471 }
472
473 String error;
474
475 if (currentSetup.inputDeviceName != newSetup.inputDeviceName
476 || currentSetup.outputDeviceName != newSetup.outputDeviceName
477 || currentAudioDevice == nullptr)
478 {
479 deleteCurrentDevice();
480 scanDevicesIfNeeded();
481
482 if (newSetup.outputDeviceName.isNotEmpty() && ! deviceListContains (type, false, newSetup.outputDeviceName))
483 return "No such device: " + newSetup.outputDeviceName;
484
485 if (newSetup.inputDeviceName.isNotEmpty() && ! deviceListContains (type, true, newSetup.inputDeviceName))
486 return "No such device: " + newSetup.inputDeviceName;
487
488 currentAudioDevice.reset (type->createDevice (newSetup.outputDeviceName, newSetup.inputDeviceName));
489
490 if (currentAudioDevice == nullptr)
491 error = "Can't open the audio device!\n\n"
492 "This may be because another application is currently using the same device - "
493 "if so, you should close any other applications and try again!";
494 else
495 error = currentAudioDevice->getLastError();
496
497 if (error.isNotEmpty())
498 {
499 deleteCurrentDevice();
500 return error;
501 }
502
503 if (newSetup.useDefaultInputChannels)
504 {
505 inputChannels.clear();
506 inputChannels.setRange (0, numInputChansNeeded, true);
507 }
508
509 if (newSetup.useDefaultOutputChannels)
510 {
511 outputChannels.clear();
512 outputChannels.setRange (0, numOutputChansNeeded, true);
513 }
514
515 if (newSetup.inputDeviceName.isEmpty()) inputChannels.clear();
516 if (newSetup.outputDeviceName.isEmpty()) outputChannels.clear();
517 }
518
519 if (! newSetup.useDefaultInputChannels)
520 inputChannels = newSetup.inputChannels;
521
522 if (! newSetup.useDefaultOutputChannels)
523 outputChannels = newSetup.outputChannels;
524
525 currentSetup = newSetup;
526
527 if (inputChannels.isZero() && outputChannels.isZero())
528 {
529 if (treatAsChosenDevice)
530 updateXml();
531
532 return {};
533 }
534
535 currentSetup.sampleRate = chooseBestSampleRate (newSetup.sampleRate);
536 currentSetup.bufferSize = chooseBestBufferSize (newSetup.bufferSize);
537
538 error = currentAudioDevice->open (inputChannels,
539 outputChannels,
540 currentSetup.sampleRate,
541 currentSetup.bufferSize);
542
543 if (error.isEmpty())
544 {
545 currentDeviceType = currentAudioDevice->getTypeName();
546
547 currentAudioDevice->start (callbackHandler.get());
548
549 currentSetup.sampleRate = currentAudioDevice->getCurrentSampleRate();
550 currentSetup.bufferSize = currentAudioDevice->getCurrentBufferSizeSamples();
551 currentSetup.inputChannels = currentAudioDevice->getActiveInputChannels();
552 currentSetup.outputChannels = currentAudioDevice->getActiveOutputChannels();
553
554 for (int i = 0; i < availableDeviceTypes.size(); ++i)
555 if (availableDeviceTypes.getUnchecked (i)->getTypeName() == currentDeviceType)
556 *(lastDeviceTypeConfigs.getUnchecked (i)) = currentSetup;
557
558 if (treatAsChosenDevice)
559 updateXml();
560 }
561 else
562 {
563 deleteCurrentDevice();
564 }
565
566 return error;
567}
568
569double AudioDeviceManager::chooseBestSampleRate (double rate) const
570{
571 jassert (currentAudioDevice != nullptr);
572
573 auto rates = currentAudioDevice->getAvailableSampleRates();
574
575 if (rate > 0 && rates.contains (rate))
576 return rate;
577
578 rate = currentAudioDevice->getCurrentSampleRate();
579
580 if (rate > 0 && rates.contains (rate))
581 return rate;
582
583 double lowestAbove44 = 0.0;
584
585 for (int i = rates.size(); --i >= 0;)
586 {
587 auto sr = rates[i];
588
589 if (sr >= 44100.0 && (lowestAbove44 < 1.0 || sr < lowestAbove44))
590 lowestAbove44 = sr;
591 }
592
593 if (lowestAbove44 > 0.0)
594 return lowestAbove44;
595
596 return rates[0];
597}
598
599int AudioDeviceManager::chooseBestBufferSize (int bufferSize) const
600{
601 jassert (currentAudioDevice != nullptr);
602
603 if (bufferSize > 0 && currentAudioDevice->getAvailableBufferSizes().contains (bufferSize))
604 return bufferSize;
605
606 return currentAudioDevice->getDefaultBufferSize();
607}
608
609void AudioDeviceManager::stopDevice()
610{
611 if (currentAudioDevice != nullptr)
612 currentAudioDevice->stop();
613
614 testSound.reset();
615}
616
618{
619 stopDevice();
620 currentAudioDevice.reset();
621 loadMeasurer.reset();
622}
623
625{
626 if (currentAudioDevice == nullptr)
627 {
628 if (currentSetup.inputDeviceName.isEmpty()
629 && currentSetup.outputDeviceName.isEmpty())
630 {
631 // This method will only reload the last device that was running
632 // before closeAudioDevice() was called - you need to actually open
633 // one first, with setAudioDevice().
634 jassertfalse;
635 return;
636 }
637
638 AudioDeviceSetup s (currentSetup);
639 setAudioDeviceSetup (s, false);
640 }
641}
642
643void AudioDeviceManager::updateXml()
644{
645 lastExplicitSettings.reset (new XmlElement ("DEVICESETUP"));
646
647 lastExplicitSettings->setAttribute ("deviceType", currentDeviceType);
648 lastExplicitSettings->setAttribute ("audioOutputDeviceName", currentSetup.outputDeviceName);
649 lastExplicitSettings->setAttribute ("audioInputDeviceName", currentSetup.inputDeviceName);
650
651 if (currentAudioDevice != nullptr)
652 {
653 lastExplicitSettings->setAttribute ("audioDeviceRate", currentAudioDevice->getCurrentSampleRate());
654
655 if (currentAudioDevice->getDefaultBufferSize() != currentAudioDevice->getCurrentBufferSizeSamples())
656 lastExplicitSettings->setAttribute ("audioDeviceBufferSize", currentAudioDevice->getCurrentBufferSizeSamples());
657
658 if (! currentSetup.useDefaultInputChannels)
659 lastExplicitSettings->setAttribute ("audioDeviceInChans", currentSetup.inputChannels.toString (2));
660
661 if (! currentSetup.useDefaultOutputChannels)
662 lastExplicitSettings->setAttribute ("audioDeviceOutChans", currentSetup.outputChannels.toString (2));
663 }
664
665 for (int i = 0; i < enabledMidiInputs.size(); ++i)
666 lastExplicitSettings->createNewChildElement ("MIDIINPUT")
667 ->setAttribute ("name", enabledMidiInputs[i]->getName());
668
669 if (midiInsFromXml.size() > 0)
670 {
671 // Add any midi devices that have been enabled before, but which aren't currently
672 // open because the device has been disconnected.
673 const StringArray availableMidiDevices (MidiInput::getDevices());
674
675 for (int i = 0; i < midiInsFromXml.size(); ++i)
676 if (! availableMidiDevices.contains (midiInsFromXml[i], true))
677 lastExplicitSettings->createNewChildElement ("MIDIINPUT")
678 ->setAttribute ("name", midiInsFromXml[i]);
679 }
680
681 if (defaultMidiOutputName.isNotEmpty())
682 lastExplicitSettings->setAttribute ("defaultMidiOutput", defaultMidiOutputName);
683}
684
685//==============================================================================
687{
688 {
689 const ScopedLock sl (audioCallbackLock);
690
691 if (callbacks.contains (newCallback))
692 return;
693 }
694
695 if (currentAudioDevice != nullptr && newCallback != nullptr)
696 newCallback->audioDeviceAboutToStart (currentAudioDevice.get());
697
698 const ScopedLock sl (audioCallbackLock);
699 callbacks.add (newCallback);
700}
701
703{
704 if (callbackToRemove != nullptr)
705 {
706 bool needsDeinitialising = currentAudioDevice != nullptr;
707
708 {
709 const ScopedLock sl (audioCallbackLock);
710
711 needsDeinitialising = needsDeinitialising && callbacks.contains (callbackToRemove);
712 callbacks.removeFirstMatchingValue (callbackToRemove);
713 }
714
715 if (needsDeinitialising)
716 callbackToRemove->audioDeviceStopped();
717 }
718}
719
720void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelData,
721 int numInputChannels,
722 float** outputChannelData,
723 int numOutputChannels,
724 int numSamples)
725{
726 const ScopedLock sl (audioCallbackLock);
727
728 inputLevelGetter->updateLevel (inputChannelData, numInputChannels, numSamples);
729 outputLevelGetter->updateLevel (const_cast<const float**> (outputChannelData), numOutputChannels, numSamples);
730
731 if (callbacks.size() > 0)
732 {
733 AudioProcessLoadMeasurer::ScopedTimer timer (loadMeasurer);
734
735 tempBuffer.setSize (jmax (1, numOutputChannels), jmax (1, numSamples), false, false, true);
736
737 callbacks.getUnchecked(0)->audioDeviceIOCallback (inputChannelData, numInputChannels,
738 outputChannelData, numOutputChannels, numSamples);
739
740 auto** tempChans = tempBuffer.getArrayOfWritePointers();
741
742 for (int i = callbacks.size(); --i > 0;)
743 {
744 callbacks.getUnchecked(i)->audioDeviceIOCallback (inputChannelData, numInputChannels,
745 tempChans, numOutputChannels, numSamples);
746
747 for (int chan = 0; chan < numOutputChannels; ++chan)
748 {
749 if (auto* src = tempChans [chan])
750 if (auto* dst = outputChannelData [chan])
751 for (int j = 0; j < numSamples; ++j)
752 dst[j] += src[j];
753 }
754 }
755 }
756 else
757 {
758 for (int i = 0; i < numOutputChannels; ++i)
759 zeromem (outputChannelData[i], sizeof (float) * (size_t) numSamples);
760 }
761
762 if (testSound != nullptr)
763 {
764 auto numSamps = jmin (numSamples, testSound->getNumSamples() - testSoundPosition);
765 auto* src = testSound->getReadPointer (0, testSoundPosition);
766
767 for (int i = 0; i < numOutputChannels; ++i)
768 for (int j = 0; j < numSamps; ++j)
769 outputChannelData [i][j] += src[j];
770
771 testSoundPosition += numSamps;
772
773 if (testSoundPosition >= testSound->getNumSamples())
774 testSound.reset();
775 }
776}
777
778void AudioDeviceManager::audioDeviceAboutToStartInt (AudioIODevice* const device)
779{
780 loadMeasurer.reset (device->getCurrentSampleRate(),
781 device->getCurrentBufferSizeSamples());
782
783 {
784 const ScopedLock sl (audioCallbackLock);
785
786 for (int i = callbacks.size(); --i >= 0;)
787 callbacks.getUnchecked(i)->audioDeviceAboutToStart (device);
788 }
789
791}
792
793void AudioDeviceManager::audioDeviceStoppedInt()
794{
796
797 const ScopedLock sl (audioCallbackLock);
798
799 loadMeasurer.reset();
800
801 for (int i = callbacks.size(); --i >= 0;)
802 callbacks.getUnchecked(i)->audioDeviceStopped();
803}
804
805void AudioDeviceManager::audioDeviceErrorInt (const String& message)
806{
807 const ScopedLock sl (audioCallbackLock);
808
809 for (int i = callbacks.size(); --i >= 0;)
810 callbacks.getUnchecked(i)->audioDeviceError (message);
811}
812
814{
815 return loadMeasurer.getLoadAsProportion();
816}
817
818//==============================================================================
819void AudioDeviceManager::setMidiInputEnabled (const String& name, const bool enabled)
820{
821 if (enabled != isMidiInputEnabled (name))
822 {
823 if (enabled)
824 {
825 auto index = MidiInput::getDevices().indexOf (name);
826
827 if (index >= 0)
828 {
829 if (auto* midiIn = MidiInput::openDevice (index, callbackHandler.get()))
830 {
831 enabledMidiInputs.add (midiIn);
832 midiIn->start();
833 }
834 }
835 }
836 else
837 {
838 for (int i = enabledMidiInputs.size(); --i >= 0;)
839 if (enabledMidiInputs[i]->getName() == name)
840 enabledMidiInputs.remove (i);
841 }
842
843 updateXml();
845 }
846}
847
849{
850 for (auto* mi : enabledMidiInputs)
851 if (mi->getName() == name)
852 return true;
853
854 return false;
855}
856
858{
859 removeMidiInputCallback (name, callbackToAdd);
860
861 if (name.isEmpty() || isMidiInputEnabled (name))
862 {
863 const ScopedLock sl (midiCallbackLock);
864
865 MidiCallbackInfo mc;
866 mc.deviceName = name;
867 mc.callback = callbackToAdd;
868 midiCallbacks.add (mc);
869 }
870}
871
873{
874 for (int i = midiCallbacks.size(); --i >= 0;)
875 {
876 auto& mc = midiCallbacks.getReference(i);
877
878 if (mc.callback == callbackToRemove && mc.deviceName == name)
879 {
880 const ScopedLock sl (midiCallbackLock);
881 midiCallbacks.remove (i);
882 }
883 }
884}
885
886void AudioDeviceManager::handleIncomingMidiMessageInt (MidiInput* source, const MidiMessage& message)
887{
888 if (! message.isActiveSense())
889 {
890 const ScopedLock sl (midiCallbackLock);
891
892 for (auto& mc : midiCallbacks)
893 if (mc.deviceName.isEmpty() || mc.deviceName == source->getName())
894 mc.callback->handleIncomingMidiMessage (source, message);
895 }
896}
897
898//==============================================================================
900{
901 if (defaultMidiOutputName != deviceName)
902 {
904
905 {
906 const ScopedLock sl (audioCallbackLock);
907 oldCallbacks.swapWith (callbacks);
908 }
909
910 if (currentAudioDevice != nullptr)
911 for (int i = oldCallbacks.size(); --i >= 0;)
912 oldCallbacks.getUnchecked(i)->audioDeviceStopped();
913
914 defaultMidiOutput.reset();
915 defaultMidiOutputName = deviceName;
916
917 if (deviceName.isNotEmpty())
918 defaultMidiOutput.reset (MidiOutput::openDevice (MidiOutput::getDevices().indexOf (deviceName)));
919
920 if (currentAudioDevice != nullptr)
921 for (auto* c : oldCallbacks)
922 c->audioDeviceAboutToStart (currentAudioDevice.get());
923
924 {
925 const ScopedLock sl (audioCallbackLock);
926 oldCallbacks.swapWith (callbacks);
927 }
928
929 updateXml();
931 }
932}
933
934//==============================================================================
935AudioDeviceManager::LevelMeter::LevelMeter() noexcept : level() {}
936
937void AudioDeviceManager::LevelMeter::updateLevel (const float* const* channelData, int numChannels, int numSamples) noexcept
938{
939 if (getReferenceCount() <= 1)
940 return;
941
942 auto localLevel = level.get();
943
944 if (numChannels > 0)
945 {
946 for (int j = 0; j < numSamples; ++j)
947 {
948 float s = 0;
949
950 for (int i = 0; i < numChannels; ++i)
951 s += std::abs (channelData[i][j]);
952
953 s /= (float) numChannels;
954
955 const float decayFactor = 0.99992f;
956
957 if (s > localLevel)
958 localLevel = s;
959 else if (localLevel > 0.001f)
960 localLevel *= decayFactor;
961 else
962 localLevel = 0;
963 }
964 }
965 else
966 {
967 localLevel = 0;
968 }
969
970 level = localLevel;
971}
972
973double AudioDeviceManager::LevelMeter::getCurrentLevel() const noexcept
974{
975 jassert (getReferenceCount() > 1);
976 return level.get();
977}
978
980{
981 { // cunningly nested to swap, unlock and delete in that order.
982 std::unique_ptr<AudioBuffer<float>> oldSound;
983
984 {
985 const ScopedLock sl (audioCallbackLock);
986 std::swap (oldSound, testSound);
987 }
988 }
989
990 testSoundPosition = 0;
991
992 if (currentAudioDevice != nullptr)
993 {
994 auto sampleRate = currentAudioDevice->getCurrentSampleRate();
995 auto soundLength = (int) sampleRate;
996
997 double frequency = 440.0;
998 float amplitude = 0.5f;
999
1000 auto phasePerSample = MathConstants<double>::twoPi / (sampleRate / frequency);
1001
1002 std::unique_ptr<AudioBuffer<float>> newSound (new AudioBuffer<float> (1, soundLength));
1003
1004 for (int i = 0; i < soundLength; ++i)
1005 newSound->setSample (0, i, amplitude * (float) std::sin (i * phasePerSample));
1006
1007 newSound->applyGainRamp (0, 0, soundLength / 10, 0.0f, 1.0f);
1008 newSound->applyGainRamp (0, soundLength - soundLength / 4, soundLength / 4, 1.0f, 0.0f);
1009
1010 {
1011 const ScopedLock sl (audioCallbackLock);
1012 std::swap (testSound, newSound);
1013 }
1014 }
1015}
1016
1018{
1019 auto deviceXRuns = (currentAudioDevice != nullptr ? currentAudioDevice->getXRunCount() : -1);
1020 return jmax (0, deviceXRuns) + loadMeasurer.getXRunCount();
1021}
1022
1023} // namespace juce
Holds a resizable array of primitive or copy-by-value objects.
Definition juce_Array.h:60
void swapWith(OtherArrayType &otherArray) noexcept
This swaps the contents of this array with those of another array.
Definition juce_Array.h:578
ElementType getUnchecked(int index) const
Returns one of the elements in the array, without checking the index passed in.
Definition juce_Array.h:256
int size() const noexcept
Returns the current number of elements in the array.
Definition juce_Array.h:219
void remove(int indexToRemove)
Removes an element from the array.
Definition juce_Array.h:724
void add(const ElementType &newElement)
Appends a new element at the end of the array.
Definition juce_Array.h:375
ElementType & getReference(int index) const noexcept
Returns a direct reference to one of the elements in the array, without checking the index passed in.
Definition juce_Array.h:271
A multi-channel buffer containing floating point audio samples.
void setSize(int newNumChannels, int newNumSamples, bool keepExistingContent=false, bool clearExtraSpace=false, bool avoidReallocating=false)
Changes the buffer's size or number of channels.
Type ** getArrayOfWritePointers() noexcept
Returns an array of pointers to the channels in the buffer.
Manages the state of some audio and midi i/o devices.
AudioDeviceManager()
Creates a default AudioDeviceManager.
void addAudioDeviceType(AudioIODeviceType *newDeviceType)
Adds a new device type to the list of types.
bool isMidiInputEnabled(const String &midiInputDeviceName) const
Returns true if a given midi input device is being used.
AudioDeviceSetup getAudioDeviceSetup() const
Returns the current device properties that are in use.
double getCpuUsage() const
Returns the average proportion of available CPU being spent inside the audio callbacks.
AudioIODeviceType * getCurrentDeviceTypeObject() const
Returns the currently active audio device type object.
String setAudioDeviceSetup(const AudioDeviceSetup &newSetup, bool treatAsChosenDevice)
Changes the current device or its settings.
XmlElement * createStateXml() const
Returns some XML representing the current state of the manager.
void setMidiInputEnabled(const String &midiInputDeviceName, bool enabled)
Enables or disables a midi input device.
virtual void createAudioDeviceTypes(OwnedArray< AudioIODeviceType > &types)
Creates a list of available types.
void addMidiInputCallback(const String &midiInputDeviceName, MidiInputCallback *callback)
Registers a listener for callbacks when midi events arrive from a midi input.
int getXRunCount() const noexcept
Returns the number of under- or over runs reported.
void setCurrentAudioDeviceType(const String &type, bool treatAsChosenDevice)
Changes the class of audio device being used.
~AudioDeviceManager() override
Destructor.
const OwnedArray< AudioIODeviceType > & getAvailableDeviceTypes()
Returns a list of the types of device supported.
String initialise(int numInputChannelsNeeded, int numOutputChannelsNeeded, const XmlElement *savedState, bool selectDefaultDeviceOnFailure, const String &preferredDefaultDeviceName=String(), const AudioDeviceSetup *preferredSetupOptions=nullptr)
Opens a set of audio devices ready for use.
void addAudioCallback(AudioIODeviceCallback *newCallback)
Registers an audio callback to be used.
void removeMidiInputCallback(const String &midiInputDeviceName, MidiInputCallback *callback)
Removes a listener that was previously registered with addMidiInputCallback().
String initialiseWithDefaultDevices(int numInputChannelsNeeded, int numOutputChannelsNeeded)
Resets everything to a default device setup, clearing any stored settings.
void restartLastAudioDevice()
Tries to reload the last audio device that was running.
void setDefaultMidiOutput(const String &deviceName)
Sets a midi output device to use as the default.
void removeAudioCallback(AudioIODeviceCallback *callback)
Deregisters a previously added callback.
void playTestSound()
Plays a beep through the current audio device.
void closeAudioDevice()
Closes the currently-open device.
One of these is passed to an AudioIODevice object to stream the audio data in and out.
virtual void audioDeviceAboutToStart(AudioIODevice *device)=0
Called to indicate that the device is about to start calling back.
virtual void audioDeviceStopped()=0
Called to indicate that the device has stopped.
A class for receiving events when audio devices are inserted or removed.
Represents a type of audio driver, such as DirectSound, ASIO, CoreAudio, etc.
virtual int getDefaultDeviceIndex(bool forInput) const =0
Returns the name of the default device.
static AudioIODeviceType * createAudioIODeviceType_Android()
Creates an Android device type if it's available on this platform, or returns null.
static AudioIODeviceType * createAudioIODeviceType_ASIO()
Creates an ASIO device type if it's available on this platform, or returns null.
static AudioIODeviceType * createAudioIODeviceType_Oboe()
Creates an Oboe device type if it's available on this platform, or returns null.
static AudioIODeviceType * createAudioIODeviceType_JACK()
Creates a JACK device type if it's available on this platform, or returns null.
static AudioIODeviceType * createAudioIODeviceType_DirectSound()
Creates a DirectSound device type if it's available on this platform, or returns null.
static AudioIODeviceType * createAudioIODeviceType_OpenSLES()
Creates an Android OpenSLES device type if it's available on this platform, or returns null.
static AudioIODeviceType * createAudioIODeviceType_WASAPI(bool exclusiveMode)
Creates a WASAPI device type if it's available on this platform, or returns null.
virtual StringArray getDeviceNames(bool wantInputNames=false) const =0
Returns the list of available devices of this type.
static AudioIODeviceType * createAudioIODeviceType_iOSAudio()
Creates an iOS device type if it's available on this platform, or returns null.
const String & getTypeName() const noexcept
Returns the name of this type of driver that this object manages.
static AudioIODeviceType * createAudioIODeviceType_Bela()
Creates a Bela device type if it's available on this platform, or returns null.
static AudioIODeviceType * createAudioIODeviceType_CoreAudio()
Creates a CoreAudio device type if it's available on this platform, or returns null.
void addListener(Listener *listener)
Adds a listener that will be called when this type of device is added or removed from the system.
static AudioIODeviceType * createAudioIODeviceType_ALSA()
Creates an ALSA device type if it's available on this platform, or returns null.
virtual AudioIODevice * createDevice(const String &outputDeviceName, const String &inputDeviceName)=0
Creates one of the devices of this type.
Base class for an audio device with synchronised input and output channels.
int getXRunCount() const
Returns the number of over- (or under-) runs recorded since the state was reset.
double getLoadAsProportion() const
Returns the current load as a proportion 0 to 1.0.
void clear() noexcept
Resets the value to 0.
void setRange(int startBit, int numBits, bool shouldBeSet)
Sets a range of bits to be either on or off.
String toString(int base, int minimumNumCharacters=1) const
Converts the number to a string.
bool isZero() const noexcept
Returns true if no bits are set.
int countNumberOfSetBits() const noexcept
Returns the total number of set bits in the value.
void sendChangeMessage()
Causes an asynchronous change message to be sent to all the registered listeners.
Automatically locks and unlocks a mutex object.
Receives incoming messages from a physical MIDI input device.
Represents a midi input device.
static StringArray getDevices()
Returns a list of the available midi input devices.
static MidiInput * openDevice(int deviceIndex, MidiInputCallback *callback)
Tries to open one of the midi input devices.
Encapsulates a MIDI message.
bool isActiveSense() const noexcept
Returns true if this is an active-sense message.
static MidiOutput * openDevice(int deviceIndex)
Tries to open one of the midi output devices.
static StringArray getDevices()
Returns a list of the available midi output devices.
An array designed for holding objects.
void clear(bool deleteObjects=true)
Clears the array, optionally deleting the objects inside it first.
int indexOf(StringRef stringToLookFor, bool ignoreCase=false, int startIndex=0) const
Searches for a string in the array.
void clear()
Removes all elements from the array.
int size() const noexcept
Returns the number of strings in the array.
The JUCE String class!
Definition juce_String.h:43
bool isEmpty() const noexcept
Returns true if the string contains no characters.
void clear() noexcept
Resets this string to be empty.
bool isNotEmpty() const noexcept
Returns true if the string contains at least one character.
static void JUCE_CALLTYPE sleep(int milliseconds)
Suspends the execution of the current thread until the specified timeout period has elapsed (note tha...
Used to build a tree of elements representing an XML document.
bool hasTagName(StringRef possibleTagName) const noexcept
Tests whether this element has a particular tag name.
This structure holds a set of properties describing the current audio setup.
String outputDeviceName
The name of the audio device used for output.
bool useDefaultInputChannels
If this is true, it indicates that the inputChannels array should be ignored, and instead,...
String inputDeviceName
The name of the audio device used for input.
BigInteger outputChannels
The set of active output channels.
BigInteger inputChannels
The set of active input channels.
bool useDefaultOutputChannels
If this is true, it indicates that the outputChannels array should be ignored, and instead,...
This class measures the time between its construction and destruction and adds it to an AudioProcessL...
Commonly used mathematical constants.