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 
23 namespace juce
24 {
25 
26 bool 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 
38 bool AudioDeviceManager::AudioDeviceSetup::operator!= (const AudioDeviceManager::AudioDeviceSetup& other) const
39 {
40  return ! operator== (other);
41 }
42 
43 //==============================================================================
45  public MidiInputCallback,
47 {
48 public:
49  CallbackHandler (AudioDeviceManager& adm) noexcept : owner (adm) {}
50 
51 private:
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 //==============================================================================
100 void AudioDeviceManager::createDeviceTypesIfNeeded()
101 {
102  if (availableDeviceTypes.size() == 0)
103  {
105  createAudioDeviceTypes (types);
106 
107  for (auto* t : types)
108  addAudioDeviceType (t);
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 
123 void 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 //==============================================================================
163 static 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 
197 static 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 //==============================================================================
207 String 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 
227 String 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 
264 String 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 
335 void 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 //==============================================================================
353 void 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 
366 AudioIODeviceType* 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 
377 AudioIODeviceType* 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 
399 void AudioDeviceManager::deleteCurrentDevice()
400 {
401  currentAudioDevice.reset();
402  currentSetup.inputDeviceName.clear();
403  currentSetup.outputDeviceName.clear();
404 }
405 
406 void 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  {
506  inputChannels.setRange (0, numInputChansNeeded, true);
507  }
508 
509  if (newSetup.useDefaultOutputChannels)
510  {
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 
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,
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 
569 double 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 
599 int 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 
609 void 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 
643 void 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 
720 void 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 
778 void 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 
793 void 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 
805 void 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 //==============================================================================
819 void 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 
886 void 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  {
903  Array<AudioIODeviceCallback*> oldCallbacks;
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 //==============================================================================
935 AudioDeviceManager::LevelMeter::LevelMeter() noexcept : level() {}
936 
937 void 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 
973 double 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
void addAudioCallback(AudioIODeviceCallback *newCallback)
Registers an audio callback to be used.
String initialiseWithDefaultDevices(int numInputChannelsNeeded, int numOutputChannelsNeeded)
Resets everything to a default device setup, clearing any stored settings.
One of these is passed to an AudioIODevice object to stream the audio data in and out...
BigInteger outputChannels
The set of active output channels.
bool hasTagName(StringRef possibleTagName) const noexcept
Tests whether this element has a particular tag name.
Represents a midi input device.
Encapsulates a MIDI message.
bool isNotEmpty() const noexcept
Returns true if the string contains at least one character.
Definition: juce_String.h:306
static MidiInput * openDevice(int deviceIndex, MidiInputCallback *callback)
Tries to open one of the midi input devices.
void swapWith(OtherArrayType &otherArray) noexcept
This swaps the contents of this array with those of another array.
Definition: juce_Array.h:578
bool isMidiInputEnabled(const String &midiInputDeviceName) const
Returns true if a given midi input device is being used.
virtual void audioDeviceAboutToStart(AudioIODevice *device)=0
Called to indicate that the device is about to start calling back.
String outputDeviceName
The name of the audio device used for output.
Used to build a tree of elements representing an XML document.
static AudioIODeviceType * createAudioIODeviceType_CoreAudio()
Creates a CoreAudio device type if it&#39;s available on this platform, or returns null.
static MidiOutput * openDevice(int deviceIndex)
Tries to open one of the midi output devices.
void parseString(StringRef text, int base)
Reads the numeric value from a string.
String setAudioDeviceSetup(const AudioDeviceSetup &newSetup, bool treatAsChosenDevice)
Changes the current device or its settings.
static AudioIODeviceType * createAudioIODeviceType_iOSAudio()
Creates an iOS device type if it&#39;s available on this platform, or returns null.
static AudioIODeviceType * createAudioIODeviceType_Oboe()
Creates an Oboe device type if it&#39;s available on this platform, or returns null.
A special array for holding a list of strings.
~AudioDeviceManager() override
Destructor.
The JUCE String class!
Definition: juce_String.h:42
int getIntAttribute(StringRef attributeName, int defaultReturnValue=0) const
Returns the value of a named attribute as an integer.
void closeAudioDevice()
Closes the currently-open device.
static AudioIODeviceType * createAudioIODeviceType_OpenSLES()
Creates an Android OpenSLES device type if it&#39;s available on this platform, or returns null...
BigInteger inputChannels
The set of active input channels.
AudioDeviceManager()
Creates a default AudioDeviceManager.
bool hasAttribute(StringRef attributeName) const noexcept
Checks whether the element contains an attribute with a certain name.
const String & getName() const noexcept
Returns the name of this device.
ElementType getUnchecked(int index) const
Returns one of the elements in the array, without checking the index passed in.
Definition: juce_Array.h:256
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 addAudioDeviceType(AudioIODeviceType *newDeviceType)
Adds a new device type to the list of types.
int indexOf(StringRef stringToLookFor, bool ignoreCase=false, int startIndex=0) const
Searches for a string in the array.
void clear() noexcept
Resets the value to 0.
AudioDeviceSetup getAudioDeviceSetup() const
Returns the current device properties that are in use.
static AudioIODeviceType * createAudioIODeviceType_ASIO()
Creates an ASIO device type if it&#39;s available on this platform, or returns null.
void removeMidiInputCallback(const String &midiInputDeviceName, MidiInputCallback *callback)
Removes a listener that was previously registered with addMidiInputCallback().
static AudioIODeviceType * createAudioIODeviceType_Bela()
Creates a Bela device type if it&#39;s available on this platform, or returns null.
Represents a type of audio driver, such as DirectSound, ASIO, CoreAudio, etc.
virtual StringArray getDeviceNames(bool wantInputNames=false) const =0
Returns the list of available devices of this type.
AudioIODeviceType * getCurrentDeviceTypeObject() const
Returns the currently active audio device type object.
bool useDefaultOutputChannels
If this is true, it indicates that the outputChannels array should be ignored, and instead...
static StringArray getDevices()
Returns a list of the available midi output devices.
This class measures the time between its construction and destruction and adds it to an AudioProcessL...
A class for receiving events when audio devices are inserted or removed.
void setDefaultMidiOutput(const String &deviceName)
Sets a midi output device to use as the default.
int getXRunCount() const noexcept
Returns the number of under- or over runs reported.
bool useDefaultInputChannels
If this is true, it indicates that the inputChannels array should be ignored, and instead...
double getDoubleAttribute(StringRef attributeName, double defaultReturnValue=0.0) const
Returns the value of a named attribute as floating-point.
void setCurrentAudioDeviceType(const String &type, bool treatAsChosenDevice)
Changes the class of audio device being used.
static AudioIODeviceType * createAudioIODeviceType_JACK()
Creates a JACK device type if it&#39;s available on this platform, or returns null.
virtual double getCurrentSampleRate()=0
Returns the sample rate that the device is currently using.
static AudioIODeviceType * createAudioIODeviceType_DirectSound()
Creates a DirectSound device type if it&#39;s available on this platform, or returns null.
String inputDeviceName
The name of the audio device used for input.
static AudioIODeviceType * createAudioIODeviceType_WASAPI(bool exclusiveMode)
Creates a WASAPI device type if it&#39;s available on this platform, or returns null. ...
virtual int getCurrentBufferSizeSamples()=0
Returns the buffer size that the device is currently using.
virtual int getDefaultDeviceIndex(bool forInput) const =0
Returns the name of the default device.
void setMidiInputEnabled(const String &midiInputDeviceName, bool enabled)
Enables or disables a midi input device.
Holds a resizable array of primitive or copy-by-value objects.
Definition: juce_Array.h:59
void addListener(Listener *listener)
Adds a listener that will be called when this type of device is added or removed from the system...
void removeAudioCallback(AudioIODeviceCallback *callback)
Deregisters a previously added callback.
virtual AudioIODevice * createDevice(const String &outputDeviceName, const String &inputDeviceName)=0
Creates one of the devices of this type.
int size() const noexcept
Returns the current number of elements in the array.
Definition: juce_Array.h:219
bool isZero() const noexcept
Returns true if no bits are set.
void sendChangeMessage()
Causes an asynchronous change message to be sent to all the registered listeners. ...
void playTestSound()
Plays a beep through the current audio device.
virtual void audioDeviceStopped()=0
Called to indicate that the device has stopped.
static AudioIODeviceType * createAudioIODeviceType_Android()
Creates an Android device type if it&#39;s available on this platform, or returns null.
bool isEmpty() const noexcept
Returns true if the string contains no characters.
Definition: juce_String.h:300
Base class for an audio device with synchronised input and output channels.
bool contains(StringRef stringToLookFor, bool ignoreCase=false) const
Searches for a string in the array.
This structure holds a set of properties describing the current audio setup.
void addMidiInputCallback(const String &midiInputDeviceName, MidiInputCallback *callback)
Registers a listener for callbacks when midi events arrive from a midi input.
Receives incoming messages from a physical MIDI input device.
const String & getStringAttribute(StringRef attributeName) const noexcept
Returns the value of a named attribute.
const OwnedArray< AudioIODeviceType > & getAvailableDeviceTypes()
Returns a list of the types of device supported.
void clear() noexcept
Resets this string to be empty.
bool isActiveSense() const noexcept
Returns true if this is an active-sense message.
ObjectClass * add(ObjectClass *newObject) noexcept
Appends a new object to the end of the array.
static void JUCE_CALLTYPE sleep(int milliseconds)
Suspends the execution of the current thread until the specified timeout period has elapsed (note tha...
An array designed for holding objects.
virtual void createAudioDeviceTypes(OwnedArray< AudioIODeviceType > &types)
Creates a list of available types.
static StringArray getDevices()
Returns a list of the available midi input devices.
Commonly used mathematical constants.
int countNumberOfSetBits() const noexcept
Returns the total number of set bits in the value.
const String & getTypeName() const noexcept
Returns the name of this type of driver that this object manages.
String trim() const
Returns a copy of this string with any whitespace characters removed from the start and end...
void restartLastAudioDevice()
Tries to reload the last audio device that was running.
Automatically locks and unlocks a mutex object.
XmlElement * createStateXml() const
Returns some XML representing the current state of the manager.
virtual void scanForDevices()=0
Refreshes the object&#39;s cached list of known devices.
double getCpuUsage() const
Returns the average proportion of available CPU being spent inside the audio callbacks.
Manages the state of some audio and midi i/o devices.
void setRange(int startBit, int numBits, bool shouldBeSet)
Sets a range of bits to be either on or off.
static AudioIODeviceType * createAudioIODeviceType_ALSA()
Creates an ALSA device type if it&#39;s available on this platform, or returns null.