tracktion-engine 3.0-10-g034fdde4aa5
Tracktion Engine — High level data model for audio applications

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_DeviceManager.cpp
Go to the documentation of this file.
1 /*
2 ,--. ,--. ,--. ,--.
3 ,-' '-.,--.--.,--,--.,---.| |,-.,-' '-.`--' ,---. ,--,--, Copyright 2024
4 '-. .-'| .--' ,-. | .--'| /'-. .-',--.| .-. || \ Tracktion Software
5 | | | | \ '-' \ `--.| \ \ | | | |' '-' '| || | Corporation
6 `---' `--' `--`--'`---'`--'`--' `---' `--' `---' `--''--' www.tracktion.com
7
8 Tracktion Engine uses a GPL/commercial licence - see LICENCE.md for details.
9*/
10
11juce::AudioDeviceManager* gDeviceManager = nullptr; // TODO
12
13namespace tracktion { inline namespace engine
14{
15
16#if TRACKTION_LOG_DEVICES
17 #define TRACKTION_LOG_DEVICE(text) TRACKTION_LOG(text)
18#else
19 #define TRACKTION_LOG_DEVICE(text)
20#endif
21
22static juce::String mergeTwoNames (const juce::String& s1, const juce::String& s2)
23{
24 juce::String nm;
25
26 auto bracketed1 = s1.fromLastOccurrenceOf ("(", false, false)
27 .upToFirstOccurrenceOf (")", false, false).trim();
28
29 auto bracketed2 = s2.fromLastOccurrenceOf ("(", false, false)
30 .upToFirstOccurrenceOf (")", false, false).trim();
31
32 if ((! (bracketed1.isEmpty() || bracketed2.isEmpty()))
33 && s1.endsWithChar (')') && s2.endsWithChar (')')
34 && s1.upToLastOccurrenceOf ("(", false, false).trim()
35 == s2.upToLastOccurrenceOf ("(", false, false).trim())
36 {
37 nm << s1.upToLastOccurrenceOf ("(", false, false).trim()
38 << " (" << bracketed1 << " + " << bracketed2 << ")";
39 }
40 else
41 {
42 juce::String endNum1, endNum2;
43
44 for (int i = s1.length(); --i >= 0;)
46 endNum1 = juce::String::charToString (s1[i]) + endNum1;
47 else
48 break;
49
50 for (int i = s2.length(); --i >= 0;)
52 endNum2 = juce::String::charToString (s2[i]) + endNum2;
53 else
54 break;
55
56 if ((! (endNum1.isEmpty() || endNum2.isEmpty()))
57 && s1.substring (0, s1.length() - endNum1.length())
58 == s2.substring (0, s2.length() - endNum2.length()))
59 {
60 nm << s1.substring (0, s1.length() - endNum1.length())
61 << endNum1 << " + " << endNum2;
62 }
63 else
64 {
65 nm << s1 << " + " << s2;
66 }
67 }
68
69 return nm;
70}
71
72static bool isMicrosoftGSSynth (MidiOutputDevice& mo)
73{
74 #if JUCE_WINDOWS
75 return mo.getName().containsIgnoreCase ("Microsoft GS ");
76 #else
77 (void) mo; return false;
78 #endif
79}
80
81//==============================================================================
82DeviceManager::TracktionEngineAudioDeviceManager::TracktionEngineAudioDeviceManager (Engine& e) : engine (e) {}
83
84void DeviceManager::TracktionEngineAudioDeviceManager::createAudioDeviceTypes (juce::OwnedArray<juce::AudioIODeviceType>& types)
85{
86 if (engine.getEngineBehaviour().addSystemAudioIODeviceTypes())
88}
89
90//==============================================================================
92{
94 {
95 if (auto device = dm.deviceManager.getCurrentAudioDevice())
96 {
97 deviceName = device->getName();
98
99 if (dm.engine.getEngineBehaviour().isDescriptionOfWaveDevicesSupported())
100 {
101 dm.engine.getEngineBehaviour().describeWaveDevices (inputs, *device, true);
102 dm.engine.getEngineBehaviour().describeWaveDevices (outputs, *device, false);
103 }
104 else
105 {
106 describeStandardDevices (inputs, *device, true);
107 describeStandardDevices (outputs, *device, false);
108 }
109 }
110 }
111
112 void describeStandardDevices (std::vector<WaveDeviceDescription>& descriptions, juce::AudioIODevice& device, bool isInput)
113 {
114 auto channelNames = isInput ? device.getInputChannelNames()
115 : device.getOutputChannelNames();
116
117 if (channelNames.size() == 2)
118 {
119 juce::String name (isInput ? TRANS("Input 123") : TRANS("Output 123"));
120 channelNames.set (0, name.replace ("123", "1"));
121 channelNames.set (1, name.replace ("123", "2"));
122 }
123
124 auto isDeviceEnabled = [this, isInput] (int index)
125 {
126 return isInput ? dm.isDeviceInEnabled (index) : dm.isDeviceOutEnabled (index);
127 };
128
129 for (int i = 0; i < channelNames.size(); ++i)
130 {
131 const bool canBeStereo = i < channelNames.size() - 1;
132
133 if (canBeStereo && (isInput ? dm.isDeviceInChannelStereo (i)
134 : dm.isDeviceOutChannelStereo (i)))
135 {
136 descriptions.push_back (WaveDeviceDescription (mergeTwoNames (channelNames[i], channelNames[i + 1]),
137 i, i + 1, isDeviceEnabled (i) || isDeviceEnabled (i + 1)));
138 ++i;
139 }
140 else
141 {
142 descriptions.push_back (WaveDeviceDescription (channelNames[i], i, -1, isDeviceEnabled (i)));
143 ++i;
144
145 if (i < channelNames.size())
146 descriptions.push_back (WaveDeviceDescription (channelNames[i], i, -1, isDeviceEnabled (i)));
147 }
148 }
149 }
150
151 bool operator== (const AvailableWaveDeviceList& other) const noexcept { return deviceName == other.deviceName && inputs == other.inputs && outputs == other.outputs; }
152 bool operator!= (const AvailableWaveDeviceList& other) const noexcept { return ! operator== (other); }
153
154 DeviceManager& dm;
155 juce::String deviceName;
157};
158
159
160//==============================================================================
162{
163 PrepareToStartCaller (DeviceManager& owner) : deviceManager (owner) {}
164
165 void handleAsyncUpdate() override
166 {
167 deviceManager.prepareToStart();
168 }
169
170 DeviceManager& deviceManager;
171};
172
173//==============================================================================
174//==============================================================================
175void DeviceManager::initialise (int defaultNumInputs, int defaultNumOutputs)
176{
177 defaultNumInputChannelsToOpen = defaultNumInputs;
178 defaultNumOutputChannelsToOpen = defaultNumOutputs;
179
180 loadSettings();
181 finishedInitialising = true;
182 rescanMidiDeviceList();
183 rescanWaveDeviceList();
184 updateNumCPUs();
185
186 deviceManager.addAudioCallback (this);
187
188 midiRescanIntervalSeconds = engine.getPropertyStorage().getProperty (SettingID::midiScanIntervalSeconds, 4);
189 restartMidiCheckTimer();
190}
191
192void DeviceManager::resetToDefaults (bool deviceSettings, bool resetInputDevices,
193 bool resetOutputDevices, bool latencySettings, bool mixSettings)
194{
195 TRACKTION_LOG ("Returning audio settings to defaults");
196
197 auto& storage = engine.getPropertyStorage();
198
199 if (deviceSettings)
200 {
201 storage.removeProperty (SettingID::audio_device_setup);
202 storage.removePropertyItem (SettingID::audiosettings, deviceManager.getCurrentAudioDeviceType());
203 }
204
205 if (latencySettings)
206 {
207 storage.setProperty (SettingID::maxLatency, 5.0f);
208 storage.setProperty (SettingID::lowLatencyBuffer, 5.8f);
209 }
210
211 if (mixSettings)
212 {
213 storage.setProperty (SettingID::cpu, juce::SystemStats::getNumCpus());
214 updateNumCPUs();
215
216 storage.setProperty (SettingID::use64Bit, false);
217 }
218
219 if (resetInputDevices)
220 for (auto wid : waveInputs)
221 wid->resetToDefault();
222
223 if (resetOutputDevices)
224 for (auto wod : waveOutputs)
225 wod->resetToDefault();
226
227 loadSettings();
228 TransportControl::restartAllTransports (engine, false);
229 SelectionManager::refreshAllPropertyPanels();
230}
231
232void DeviceManager::setMidiDeviceScanIntervalSeconds (int intervalSeconds)
233{
234 midiRescanIntervalSeconds = intervalSeconds;
235 engine.getPropertyStorage().setProperty (SettingID::midiScanIntervalSeconds, intervalSeconds);
236 restartMidiCheckTimer();
237}
238
239void DeviceManager::restartMidiCheckTimer()
240{
241 if (usesHardwareMidiDevices() && midiRescanIntervalSeconds != 0)
242 startTimer (midiRescanIntervalSeconds * 1000);
243 else
244 stopTimer();
245}
246
247void DeviceManager::rescanMidiDeviceList()
248{
249 onlyRescanMidiOnHardwareChange = false;
250 startTimer (5);
251}
252
253void DeviceManager::timerCallback()
254{
255 applyNewMidiDeviceList();
256}
257
258static constexpr const char* allMidiInsName = "All MIDI Ins";
259static constexpr const char* allMidiInsID = "all_midi_in";
260
261static juce::StringArray getVirtualDeviceIDs (Engine& engine)
262{
263 juce::StringArray virtualDeviceIDs;
264 virtualDeviceIDs.addTokens (engine.getPropertyStorage().getProperty (SettingID::virtualmididevices).toString(), ";", {});
265 virtualDeviceIDs.removeEmptyStrings();
266 virtualDeviceIDs.removeString (allMidiInsName);
267 virtualDeviceIDs.removeString (allMidiInsID);
268 virtualDeviceIDs.insert (0, allMidiInsID);
269 return virtualDeviceIDs;
270}
271
272static void setVirtualDeviceIDs (Engine& engine, const juce::StringArray& list)
273{
274 engine.getPropertyStorage().setProperty (SettingID::virtualmididevices, list.joinIntoString (";"));
275}
276
278{
279 juce::Array<juce::MidiDeviceInfo> midiIns, midiOuts;
280
286 std::vector<bool> physicalMidiOutsEnabled, physicalMidiInsEnabled, virtualMidiInsEnabled;
287
288 MIDIDeviceList() = default;
289
290 MIDIDeviceList (Engine& sourceEngine,
291 HostedAudioDeviceInterface* sourceHostedAudioDeviceInterface,
292 bool useHardwareDevices)
293 {
294 if (sourceHostedAudioDeviceInterface)
295 {
296 hostedMidiOut = std::unique_ptr<MidiOutputDevice> (sourceHostedAudioDeviceInterface->createMidiOutput());
297 hostedMidiIn = std::unique_ptr<MidiInputDevice> (sourceHostedAudioDeviceInterface->createMidiInput());
298 }
299
300 if (useHardwareDevices)
301 {
302 scanHardwareDevices (sourceEngine);
303
304 for (auto& info : midiOuts)
305 {
306 auto d = std::make_shared<MidiOutputDevice> (sourceEngine, info);
307 physicalMidiOuts.push_back (d);
308 physicalMidiOutsEnabled.push_back (d->isEnabled());
309 }
310
311 for (auto& info : midiIns)
312 {
313 auto d = std::make_shared<PhysicalMidiInputDevice> (sourceEngine, info);
314 physicalMidiIns.push_back (d);
315 physicalMidiInsEnabled.push_back (d->isEnabled());
316 }
317
318 #if 0 // JUCE_MAC && JUCE_DEBUG
319 // This is causing problems on macOS as creating the device will cause the OS to send an async
320 // "devices changed" callback which will create the device again etc. in an infinite loop
321 auto d = std::make_unique<SoftwareMidiOutputDevice> (engine, "Tracktion MIDI Device");
322 physicalMidiOuts.push_back (d);
323 physicalMidiOutsEnabled.push_back (d->isEnabled());
324 #endif
325 }
326
327 for (auto& v : getVirtualDeviceIDs (sourceEngine))
328 {
329 bool isAllMidiIn = (v == allMidiInsID);
330 auto deviceName = isAllMidiIn ? juce::String (allMidiInsName) : v;
331 auto deviceID = isAllMidiIn ? juce::String (allMidiInsID) : ("vmidiin_" + juce::String::toHexString (v.hashCode()));
332 auto d = std::make_shared<VirtualMidiInputDevice> (sourceEngine, deviceName, InputDevice::virtualMidiDevice, deviceID, isAllMidiIn);
333 virtualMidiIns.push_back (d);
334 virtualMidiInsEnabled.push_back (d->isEnabled());
335 }
336 }
337
338 void scanHardwareDevices (Engine& e)
339 {
340 StopwatchTimer total;
341 static constexpr double maxAcceptableTimeForAutoScanning = 0.2;
342
343 {
344 StopwatchTimer timer;
346 auto scanTime = timer.getSeconds();
347
348 if (scanTime > maxAcceptableTimeForAutoScanning)
349 {
350 TRACKTION_LOG ("MIDI input scan took " + juce::String (scanTime) + " seconds. Disabling auto-scanning");
351 e.getDeviceManager().setMidiDeviceScanIntervalSeconds (0);
352 }
353 }
354
355 {
356 StopwatchTimer timer;
358 auto scanTime = timer.getSeconds();
359
360 if (scanTime > maxAcceptableTimeForAutoScanning)
361 {
362 TRACKTION_LOG ("MIDI output scan took " + juce::String (scanTime) + " seconds. Disabling auto-scanning");
363 e.getDeviceManager().setMidiDeviceScanIntervalSeconds (0);
364 }
365 }
366
367 auto comparator = [] (const juce::MidiDeviceInfo& a, const juce::MidiDeviceInfo& b)
368 {
369 return a.identifier < b.identifier;
370 };
371
372 std::sort (midiIns.begin(), midiIns.end(), comparator);
373 std::sort (midiOuts.begin(), midiOuts.end(), comparator);
374
375 static bool hasReported = false;
376
377 if (! hasReported || total.getSeconds() > 0.1)
378 {
379 hasReported = true;
380 TRACKTION_LOG_DEVICE ("MIDI Input devices scanned in: " + total.getDescription());
381 }
382 }
383
384 bool hasHardwareChanged (const MIDIDeviceList& other) const
385 {
386 return midiIns != other.midiIns
387 || midiOuts != other.midiOuts;
388 }
389
390 bool hasSameEnablement (const MIDIDeviceList& other) const
391 {
392 return physicalMidiOutsEnabled == other.physicalMidiOutsEnabled
393 && physicalMidiInsEnabled == other.physicalMidiInsEnabled
394 && virtualMidiInsEnabled == other.virtualMidiInsEnabled;
395 }
396
397 bool operator== (const MIDIDeviceList& other) const
398 {
399 return midiIns == other.midiIns
400 && midiOuts == other.midiOuts
401 && hostedMidiIn == other.hostedMidiIn
402 && hostedMidiOut == other.hostedMidiOut
403 && physicalMidiOuts == other.physicalMidiOuts
404 && physicalMidiIns == other.physicalMidiIns
405 && virtualMidiIns == other.virtualMidiIns
406 && hasSameEnablement (other);
407 }
408
409 bool operator!= (const MIDIDeviceList& other) const
410 {
411 return ! operator== (other);
412 }
413
414 void harvestExistingDevices (MIDIDeviceList& current)
415 {
416 if (hostedMidiIn && current.hostedMidiIn)
417 hostedMidiIn = current.hostedMidiIn;
418
419 if (hostedMidiOut && current.hostedMidiOut)
420 hostedMidiOut = current.hostedMidiOut;
421
422 for (auto& d : physicalMidiIns)
423 for (auto& old : current.physicalMidiIns)
424 if (old->getDeviceID() == d->getDeviceID())
425 d = old;
426
427 for (auto& d : virtualMidiIns)
428 for (auto& old : current.virtualMidiIns)
429 if (old->getDeviceID() == d->getDeviceID())
430 d = old;
431
432 for (auto& d : physicalMidiOuts)
433 for (auto& old : current.physicalMidiOuts)
434 if (old->getDeviceID() == d->getDeviceID())
435 d = old;
436 }
437
439 {
441
442 if (hostedMidiIn)
443 list.push_back (hostedMidiIn);
444
445 for (auto& d : physicalMidiIns)
446 list.push_back (d);
447
448 for (auto& d : virtualMidiIns)
449 list.push_back (d);
450
451 return list;
452 }
453
455 {
457
458 if (hostedMidiOut)
459 list.push_back (hostedMidiOut);
460
461 for (auto& d : physicalMidiOuts)
462 list.push_back (d);
463
464 return list;
465 }
466};
467
468void DeviceManager::applyNewMidiDeviceList()
469{
471 TRACKTION_ASSERT_MESSAGE_THREAD
472
473 restartMidiCheckTimer();
474
475 if (lastMIDIDeviceList == nullptr)
476 lastMIDIDeviceList = std::make_unique<MIDIDeviceList>();
477
478 auto newList = std::make_unique<MIDIDeviceList> (engine,
479 hostedAudioDeviceInterface.get(),
480 usesHardwareMidiDevices());
481
482 if (onlyRescanMidiOnHardwareChange
483 && ! newList->hasHardwareChanged (*lastMIDIDeviceList))
484 return;
485
486 onlyRescanMidiOnHardwareChange = true;
487
488 auto& storage = engine.getPropertyStorage();
489
490 auto newDefaultOut = storage.getProperty (SettingID::defaultMidiOutDevice).toString();
491 auto newDefaultIn = storage.getProperty (SettingID::defaultMidiInDevice).toString();
492
493 bool defaultsChanged = (defaultMidiOutID != newDefaultOut
494 || defaultMidiInID != newDefaultIn);
495
496 if (! defaultsChanged && *newList == *lastMIDIDeviceList)
497 return;
498
499 defaultMidiOutID = newDefaultOut;
500 defaultMidiInID = newDefaultIn;
501
502 bool enablementChanged = ! newList->hasSameEnablement (*lastMIDIDeviceList);
503
504 newList->harvestExistingDevices (*lastMIDIDeviceList);
505
506 auto newMidiIns = newList->getAllIns();
507 auto newMidiOuts = newList->getAllOuts();
508
509 lastMIDIDeviceList = std::move (newList);
510
511 if (! (defaultsChanged || enablementChanged))
512 {
513 const std::shared_lock sl (midiInputsMutex);
514
515 if (newMidiIns == midiInputs && newMidiOuts == midiOutputs)
516 return;
517 }
518
519 TRACKTION_LOG ("Updating MIDI I/O devices");
520
521 for (auto mi : newMidiIns)
522 TRACKTION_LOG_DEVICE ("Found MIDI in: " + mi->getDeviceID() + " (\"" + mi->getName() + "\")" + (mi->isEnabled() ? " (enabled)" : ""));
523
524 for (auto mo : newMidiOuts)
525 TRACKTION_LOG_DEVICE ("Found MIDI out: " + mo->getDeviceID() + " (\"" + mo->getName() + "\")" + (mo->isEnabled() ? " (enabled)" : ""));
526
527 for (auto& d : newMidiOuts)
528 {
529 if (d->isEnabled())
530 {
531 auto error = d->openDevice();
532
533 if (! error.isEmpty())
534 {
535 d->setEnabled (false);
536 engine.getUIBehaviour().showWarningMessage (error);
537 }
538 }
539 }
540
541 for (auto& d : newMidiIns)
542 {
543 if (d->isEnabled())
544 {
545 auto error = d->openDevice();
546
547 if (! error.isEmpty())
548 {
549 d->setEnabled (false);
550 engine.getUIBehaviour().showWarningMessage (error);
551 }
552 }
553 }
554
555 clearAllContextDevices();
556
557 {
558 const std::unique_lock sl (midiInputsMutex);
559
560 std::swap (newMidiIns, midiInputs);
561 std::swap (newMidiOuts, midiOutputs);
562 }
563
564 reloadAllContextDevices();
565
566 int enabledMidiOuts = 0;
567
568 for (auto mo : midiOutputs)
569 {
570 if (mo->isEnabled())
571 ++enabledMidiOuts;
572 else
573 mo->closeDevice();
574 }
575
576 {
577 const std::shared_lock sl (midiInputsMutex);
578
579 for (auto mi : midiInputs)
580 if (! mi->isEnabled())
581 mi->closeDevice();
582 }
583
584 const bool hasEnabledMidiDefaultDevs = storage.getProperty (SettingID::hasEnabledMidiDefaultDevs, false);
585 storage.setProperty (SettingID::hasEnabledMidiDefaultDevs, true);
586 storage.flushSettingsToDisk();
587
588 checkDefaultDevicesAreValid();
589
590 if (enabledMidiOuts == 0 && ! hasEnabledMidiDefaultDevs)
591 {
592 for (auto& d : midiOutputs)
593 {
594 if (! isMicrosoftGSSynth (*d))
595 {
596 d->setEnabled (true);
597 break;
598 }
599 }
600 }
601
602 sendChangeMessage();
603
604 engine.getExternalControllerManager().midiInOutDevicesChanged();
605}
606
607void DeviceManager::injectMIDIMessageToDefaultDevice (const juce::MidiMessage& m)
608{
609 if (auto input = getDefaultMidiInDevice())
610 input->handleIncomingMidiMessage (nullptr, m);
611}
612
613void DeviceManager::broadcastMessageToAllVirtualDevices (MidiInputDevice& source, const juce::MidiMessage& m)
614{
615 const std::shared_lock sl (contextLock);
616
617 for (auto d : midiInputs)
618 if (auto vmd = dynamic_cast<VirtualMidiInputDevice*> (d.get()))
619 vmd->handleMessageFromPhysicalDevice (source, m);
620}
621
622bool DeviceManager::usesHardwareMidiDevices()
623{
624 return hostedAudioDeviceInterface == nullptr
625 || hostedAudioDeviceInterface->parameters.useMidiDevices;
626}
627
628HostedAudioDeviceInterface& DeviceManager::getHostedAudioDeviceInterface()
629{
630 if (hostedAudioDeviceInterface == nullptr)
631 hostedAudioDeviceInterface = std::make_unique<HostedAudioDeviceInterface> (engine);
632
633 return *hostedAudioDeviceInterface;
634}
635
636bool DeviceManager::isHostedAudioDeviceInterfaceInUse() const
637{
638 return hostedAudioDeviceInterface != nullptr
639 && deviceManager.getCurrentAudioDeviceType() == "Hosted Device";
640}
641
642void DeviceManager::removeHostedAudioDeviceInterface()
643{
644 for (auto device : deviceManager.getAvailableDeviceTypes())
645 {
646 if (device->getTypeName() == "Hosted Device")
647 {
648 deviceManager.removeAudioDeviceType (device);
649 break;
650 }
651 }
652
653 hostedAudioDeviceInterface.reset();
654}
655
656juce::String DeviceManager::getDefaultAudioOutDeviceName (bool translated)
657{
658 return translated ? ("(" + TRANS("Default audio output") + ")")
659 : "(default audio output)";
660}
661
662juce::String DeviceManager::getDefaultMidiOutDeviceName (bool translated)
663{
664 return translated ? ("(" + TRANS("Default MIDI output") + ")")
665 : "(default MIDI output)";
666}
667
668juce::String DeviceManager::getDefaultAudioInDeviceName (bool translated)
669{
670 return translated ? ("(" + TRANS("Default audio input") + ")")
671 : "(default audio input)";
672}
673
674juce::String DeviceManager::getDefaultMidiInDeviceName (bool translated)
675{
676 return translated ? ("(" + TRANS("Default MIDI input") + ")")
677 : "(default MIDI input)";
678}
679
680// This is a change in the juce::AudioDeviceManager
681void DeviceManager::changeListenerCallback (ChangeBroadcaster*)
682{
684 saveSettings();
685 rescanWaveDeviceList();
686}
687
688bool DeviceManager::isMSWavetableSynthPresent() const
689{
690 for (auto mo : midiOutputs)
691 if (mo->isEnabled() && isMicrosoftGSSynth (*mo))
692 return true;
693
694 return false;
695}
696
697juce::Result DeviceManager::createVirtualMidiDevice (const juce::String& name)
698{
700 TRACKTION_ASSERT_MESSAGE_THREAD
701
702 auto virtualDeviceIDs = getVirtualDeviceIDs (engine);
703
704 if (virtualDeviceIDs.contains (name))
705 return juce::Result::fail (TRANS("Name is already in use!"));
706
707 virtualDeviceIDs.add (name);
708 setVirtualDeviceIDs (engine, virtualDeviceIDs);
709
710 rescanMidiDeviceList();
711
712 return juce::Result::ok();
713}
714
715void DeviceManager::deleteVirtualMidiDevice (VirtualMidiInputDevice& vmi)
716{
718 TRACKTION_ASSERT_MESSAGE_THREAD
719
720 auto deviceID = vmi.getDeviceID();
721
722 engine.getPropertyStorage().removePropertyItem (SettingID::virtualmidiin, deviceID);
723
724 auto virtualDeviceIDs = getVirtualDeviceIDs (engine);
725 virtualDeviceIDs.removeString (deviceID);
726 setVirtualDeviceIDs (engine, virtualDeviceIDs);
727
728 rescanMidiDeviceList();
729}
730
731void DeviceManager::sanityCheckEnabledChannels()
732{
733 for (int i = outEnabled.getHighestBit() + 2; --i >= 0;)
734 {
735 if (isDeviceOutChannelStereo (i))
736 {
737 const int chan = i & ~1;
738 const bool en = outEnabled[chan] || outEnabled[chan + 1];
739 outEnabled.setBit (chan, en);
740 outEnabled.setBit (chan + 1, en);
741 }
742 }
743
744 for (int i = inEnabled.getHighestBit() + 2; --i >= 0;)
745 {
746 if (isDeviceInChannelStereo (i))
747 {
748 const int chan = i & ~1;
749 const bool en = inEnabled[chan] || inEnabled[chan + 1];
750 inEnabled.setBit (chan, en);
751 inEnabled.setBit (chan + 1, en);
752 }
753 }
754
755 if (currentSampleRate < 22050 || currentSampleRate > 200000)
756 currentSampleRate = 44100.0;
757}
758
759void DeviceManager::rescanWaveDeviceList()
760{
761 auto newList = std::make_unique<AvailableWaveDeviceList> (*this);
762
763 if (lastAvailableWaveDeviceList == nullptr || *newList != *lastAvailableWaveDeviceList)
764 {
765 lastAvailableWaveDeviceList = std::move (newList);
766 triggerAsyncUpdate();
767 }
768}
769
770void DeviceManager::handleAsyncUpdate()
771{
773 TRACKTION_ASSERT_MESSAGE_THREAD
774
775 static bool reentrant = false;
776 jassert (! reentrant);
777
778 if (reentrant)
779 return;
780
781 const juce::ScopedValueSetter<bool> v (reentrant, true);
782
783 TRACKTION_LOG ("Rebuilding Wave Device List...");
784
785 prepareToStartCaller->handleUpdateNowIfNeeded();
786
787 if (lastAvailableWaveDeviceList == nullptr)
788 lastAvailableWaveDeviceList = std::make_unique<AvailableWaveDeviceList> (*this);
789
792 juce::BigInteger newActiveOutChannels;
793
794 for (auto& d : lastAvailableWaveDeviceList->inputs)
795 {
796 auto wi = new WaveInputDevice (engine, TRANS("Wave Audio Input"), d, InputDevice::waveDevice);
797 newWaveInputs.add (wi);
798
799 TRACKTION_LOG_DEVICE ("Wave In: " + wi->getName() + (wi->isEnabled() ? " (enabled): " : ": ")
800 + createDescriptionOfChannels (wi->deviceChannels));
801 }
802
803 for (auto& d : lastAvailableWaveDeviceList->outputs)
804 {
805 auto wo = new WaveOutputDevice (engine, d);
806 newWaveOutputs.add (wo);
807
808 if (wo->isEnabled())
809 for (const auto& ci : wo->getChannels())
810 newActiveOutChannels.setBit (ci.indexInDevice);
811
812 TRACKTION_LOG_DEVICE ("Wave Out: " + wo->getName() + (wo->isEnabled() ? " (enabled): " : ": ")
813 + createDescriptionOfChannels (wo->deviceChannels));
814 }
815
816 clearAllContextDevices();
817
818 {
819 const std::unique_lock sl (contextLock);
820 newWaveInputs.swapWith (waveInputs);
821 newWaveOutputs.swapWith (waveOutputs);
822 newActiveOutChannels.swapWith (activeOutChannels);
823 }
824
825 sanityCheckEnabledChannels();
826 reloadAllContextDevices();
827 saveSettings();
828 checkDefaultDevicesAreValid();
829 sendChangeMessage();
830
831 #if TRACKTION_LOG_ENABLED
832 auto wo = getDefaultWaveOutDevice();
833 TRACKTION_LOG ("Default Wave Out: " + (wo != nullptr ? wo->getName() : juce::String()));
834
835 auto mo = getDefaultMidiOutDevice();
836 TRACKTION_LOG ("Default MIDI Out: " + (mo != nullptr ? mo->getName() : juce::String()));
837
838 auto wi = getDefaultWaveInDevice();
839 TRACKTION_LOG ("Default Wave In: " + (wi != nullptr ? wi->getName() : juce::String()));
840
841 auto mi = getDefaultMidiInDevice();
842 TRACKTION_LOG ("Default MIDI In: " + (mi != nullptr ? mi->getName() : juce::String()));
843 #endif
844}
845
846void DeviceManager::loadSettings()
847{
848 juce::String error;
849 auto& storage = engine.getPropertyStorage();
850
851 {
853 if (isHostedAudioDeviceInterfaceInUse())
854 {
855 error = deviceManager.initialise (defaultNumInputChannelsToOpen,
856 defaultNumOutputChannelsToOpen,
857 nullptr, false, "Hosted Device", nullptr);
858 }
859 else
860 {
861 auto audioXml = storage.getXmlProperty (SettingID::audio_device_setup);
862
863 if (audioXml != nullptr)
864 error = deviceManager.initialise (defaultNumInputChannelsToOpen,
865 defaultNumOutputChannelsToOpen,
866 audioXml.get(), true);
867 else
868 error = deviceManager.initialiseWithDefaultDevices (defaultNumInputChannelsToOpen,
869 defaultNumOutputChannelsToOpen);
870 }
871
872 if (error.isNotEmpty())
873 TRACKTION_LOG_ERROR ("AudioDeviceManager init: " + error);
874 }
875
876 outMonoChans.clear();
877 inStereoChans.clear();
878 outEnabled.clear();
879 outEnabled.setBit (0);
880 outEnabled.setBit (1);
881 inEnabled.clear();
882 inEnabled.setBit (0);
883 inEnabled.setBit (1);
884
885 if (! engine.getEngineBehaviour().isDescriptionOfWaveDevicesSupported()) //else UI will take care about inputs/outputs names and their mapping to device channels
886 {
887 if (auto n = storage.getXmlPropertyItem (SettingID::audiosettings, deviceManager.getCurrentAudioDeviceType()))
888 {
889 outMonoChans.parseString (n->getStringAttribute ("monoChansOut", outMonoChans.toString (2)), 2);
890 inStereoChans.parseString (n->getStringAttribute ("stereoChansIn", inStereoChans.toString (2)), 2);
891 outEnabled.parseString (n->getStringAttribute ("outEnabled", outEnabled.toString (2)), 2);
892 inEnabled.parseString (n->getStringAttribute ("inEnabled", inEnabled.toString (2)), 2);
893 }
894 }
895
896 auto currentDeviceType = deviceManager.getCurrentAudioDeviceType();
897 defaultWaveOutID = storage.getPropertyItem (SettingID::defaultWaveOutDevice, currentDeviceType);
898 defaultWaveInID = storage.getPropertyItem (SettingID::defaultWaveInDevice, currentDeviceType);
899
900 TRACKTION_LOG ("Audio block size: " + juce::String (getBlockSize())
901 + " Rate: " + juce::String ((int) getSampleRate()));
902}
903
904void DeviceManager::saveSettings()
905{
906 auto& storage = engine.getPropertyStorage();
907
908 if (auto audioXml = deviceManager.createStateXml())
909 storage.setXmlProperty (SettingID::audio_device_setup, *audioXml);
910
911 if (! engine.getEngineBehaviour().isDescriptionOfWaveDevicesSupported())
912 {
913 if (deviceManager.getCurrentAudioDevice() != nullptr)
914 {
915 juce::XmlElement n ("AUDIODEVICE");
916
917 n.setAttribute ("outEnabled", outEnabled.toString (2));
918 n.setAttribute ("inEnabled", inEnabled.toString (2));
919 n.setAttribute ("monoChansOut", outMonoChans.toString (2));
920 n.setAttribute ("stereoChansIn", inStereoChans.toString (2));
921
922 storage.setXmlPropertyItem (SettingID::audiosettings, deviceManager.getCurrentAudioDeviceType(), n);
923 }
924 }
925}
926
927void DeviceManager::checkDefaultDevicesAreValid()
928{
929 if (! finishedInitialising)
930 return;
931
932 if (getDefaultWaveOutDevice() == nullptr || ! getDefaultWaveOutDevice()->isEnabled())
933 {
934 for (auto d : waveOutputs)
935 {
936 if (d->isEnabled())
937 {
938 setDefaultWaveOutDevice (d->getDeviceID());
939 break;
940 }
941 }
942 }
943
944 if (getDefaultWaveInDevice() == nullptr || ! getDefaultWaveInDevice()->isEnabled())
945 {
946 for (auto d : waveInputs)
947 {
948 if (d->isEnabled())
949 {
950 setDefaultWaveInDevice (d->getDeviceID());
951 break;
952 }
953 }
954 }
955
956 if (getDefaultMidiOutDevice() == nullptr || ! getDefaultMidiOutDevice()->isEnabled())
957 {
958 for (auto d : midiOutputs)
959 {
960 if (d->isEnabled())
961 {
962 setDefaultMidiOutDevice (d->getDeviceID());
963 break;
964 }
965 }
966 }
967
968 if (getDefaultMidiInDevice() == nullptr || ! getDefaultMidiInDevice()->isEnabled())
969 {
970 if (auto allMidi = findInputDeviceForID (allMidiInsID);
971 allMidi != nullptr && allMidi->isEnabled())
972 {
973 setDefaultMidiInDevice (allMidi->getDeviceID());
974 }
975 else
976 {
977 const std::shared_lock sl (midiInputsMutex);
978
979 for (auto d : midiInputs)
980 {
981 if (d->isEnabled())
982 {
983 setDefaultMidiInDevice (d->getDeviceID());
984 break;
985 }
986 }
987 }
988 }
989}
990
991double DeviceManager::getSampleRate() const
992{
993 if (auto device = deviceManager.getCurrentAudioDevice())
994 return device->getCurrentSampleRate();
995
996 return 44100;
997}
998
999int DeviceManager::getBitDepth() const
1000{
1001 if (auto device = deviceManager.getCurrentAudioDevice())
1002 return device->getCurrentBitDepth();
1003
1004 return 16;
1005}
1006
1007int DeviceManager::getBlockSize() const
1008{
1009 if (auto device = deviceManager.getCurrentAudioDevice())
1010 return device->getCurrentBufferSizeSamples();
1011
1012 return 256;
1013}
1014
1015double DeviceManager::getBlockSizeMs() const
1016{
1017 return getBlockSize() * 1000.0 / getSampleRate();
1018}
1019
1020TimeDuration DeviceManager::getBlockLength() const
1021{
1022 return TimeDuration::fromSamples (getBlockSize(), getSampleRate());
1023}
1024
1025WaveInputDevice* DeviceManager::getDefaultWaveInDevice() const { return dynamic_cast<WaveInputDevice*> (findInputDeviceForID (getDefaultWaveInDeviceID())); }
1026WaveOutputDevice* DeviceManager::getDefaultWaveOutDevice() const { return dynamic_cast<WaveOutputDevice*> (findOutputDeviceForID (getDefaultWaveOutDeviceID())); }
1027MidiInputDevice* DeviceManager::getDefaultMidiInDevice() const { return dynamic_cast<MidiInputDevice*> (findInputDeviceForID (getDefaultMidiInDeviceID())); }
1028MidiOutputDevice* DeviceManager::getDefaultMidiOutDevice() const { return dynamic_cast<MidiOutputDevice*> (findOutputDeviceForID (getDefaultMidiOutDeviceID())); }
1029
1030void DeviceManager::setDefaultWaveOutDevice (juce::String deviceID)
1031{
1032 if (defaultWaveOutID != deviceID)
1033 {
1034 if (auto d = findOutputDeviceForID (deviceID))
1035 {
1036 if (d->isEnabled())
1037 {
1038 defaultWaveOutID = deviceID;
1039 engine.getPropertyStorage().setPropertyItem (SettingID::defaultWaveOutDevice,
1040 deviceManager.getCurrentAudioDeviceType(),
1041 deviceID);
1042 rescanWaveDeviceList();
1043 }
1044 }
1045 }
1046}
1047
1048void DeviceManager::setDefaultWaveInDevice (juce::String deviceID)
1049{
1050 if (defaultWaveInID != deviceID)
1051 {
1052 if (auto d = findInputDeviceForID (deviceID))
1053 {
1054 if (d->isEnabled())
1055 {
1056 defaultWaveInID = deviceID;
1057 engine.getPropertyStorage().setPropertyItem (SettingID::defaultWaveInDevice,
1058 deviceManager.getCurrentAudioDeviceType(),
1059 deviceID);
1060 rescanWaveDeviceList();
1061 }
1062 }
1063 }
1064}
1065
1066void DeviceManager::setDefaultMidiOutDevice (juce::String deviceID)
1067{
1068 if (defaultMidiOutID != deviceID)
1069 {
1070 if (auto d = findOutputDeviceForID (deviceID))
1071 {
1072 if (d->isEnabled())
1073 {
1074 engine.getPropertyStorage().setProperty (SettingID::defaultMidiOutDevice, deviceID);
1075 rescanMidiDeviceList();
1076 }
1077 }
1078 }
1079}
1080
1081void DeviceManager::setDefaultMidiInDevice (juce::String deviceID)
1082{
1083 if (defaultMidiInID != deviceID)
1084 {
1085 if (auto d = findInputDeviceForID (deviceID))
1086 {
1087 if (d->isEnabled())
1088 {
1089 engine.getPropertyStorage().setProperty (SettingID::defaultMidiInDevice, deviceID);
1090 rescanMidiDeviceList();
1091 }
1092 }
1093 }
1094}
1095
1096void DeviceManager::setDeviceOutChannelStereo (int chan, bool isStereoPair)
1097{
1098 chan &= ~1;
1099
1100 if (isDeviceOutChannelStereo (chan) != isStereoPair)
1101 {
1102 outMonoChans.setBit (chan / 2, ! isStereoPair);
1103
1104 if (isStereoPair)
1105 {
1106 const bool en = outEnabled[chan] || outEnabled[chan + 1];
1107 outEnabled.setBit (chan, en);
1108 outEnabled.setBit (chan + 1, en);
1109 }
1110
1111 rescanWaveDeviceList();
1112 }
1113}
1114
1115void DeviceManager::setDeviceInChannelStereo (int chan, bool isStereoPair)
1116{
1117 chan &= ~1;
1118
1119 if (isDeviceInChannelStereo (chan) != isStereoPair)
1120 {
1121 inStereoChans.setBit (chan / 2, isStereoPair);
1122
1123 if (isStereoPair)
1124 {
1125 const bool en = inEnabled[chan] || inEnabled[chan + 1];
1126 inEnabled.setBit (chan, en);
1127 inEnabled.setBit (chan + 1, en);
1128 }
1129
1130 rescanWaveDeviceList();
1131 }
1132}
1133
1134void DeviceManager::setWaveOutChannelsEnabled (const std::vector<ChannelIndex>& channels, bool b)
1135{
1136 for (auto& ci : channels)
1137 {
1138 if (outEnabled[ci.indexInDevice] != b)
1139 {
1140 outEnabled.setBit (ci.indexInDevice, b);
1141 rescanWaveDeviceList();
1142 }
1143 }
1144}
1145
1146void DeviceManager::setWaveInChannelsEnabled (const std::vector<ChannelIndex>& channels, bool b)
1147{
1148 for (auto& ci : channels)
1149 {
1150 if (inEnabled[ci.indexInDevice] != b)
1151 {
1152 inEnabled.setBit (ci.indexInDevice, b);
1153 rescanWaveDeviceList();
1154 }
1155 }
1156}
1157
1158int DeviceManager::getNumMidiInDevices() const
1159{
1160 const std::shared_lock sl (midiInputsMutex);
1161 return (int) midiInputs.size();
1162}
1163
1164std::shared_ptr<MidiInputDevice> DeviceManager::getMidiInDevice (int index) const
1165{
1166 const std::shared_lock sl (midiInputsMutex);
1167
1168 if (index >= 0 && index < (int) midiInputs.size())
1169 return midiInputs[(size_t) index];
1170
1171 return {};
1172}
1173
1174std::vector<std::shared_ptr<MidiInputDevice>> DeviceManager::getMidiInDevices() const
1175{
1176 const std::shared_lock sl (midiInputsMutex);
1177 return midiInputs;
1178}
1179
1180std::shared_ptr<MidiInputDevice> DeviceManager::findMidiInputDeviceForID (const juce::String& deviceID) const
1181{
1182 const std::shared_lock sl (midiInputsMutex);
1183
1184 for (auto& m : midiInputs)
1185 if (m->getDeviceID() == deviceID)
1186 return m;
1187
1188 return {};
1189}
1190
1191void DeviceManager::broadcastStreamTimeToMidiDevices (double timeToBroadcast)
1192{
1193 const std::shared_lock sl (midiInputsMutex);
1194
1195 for (auto mi : midiInputs)
1196 if (mi->isEnabled())
1197 mi->masterTimeUpdate (timeToBroadcast);
1198}
1199
1200int DeviceManager::getNumInputDevices() const
1201{
1202 return getNumWaveInDevices() + getNumMidiInDevices();
1203}
1204
1205InputDevice* DeviceManager::getInputDevice (int index) const
1206{
1207 if (index >= getNumWaveInDevices())
1208 return getMidiInDevice (index - getNumWaveInDevices()).get();
1209
1210 return getWaveInDevice (index);
1211}
1212
1213int DeviceManager::getNumOutputDevices() const
1214{
1215 return getNumWaveOutDevices() + getNumMidiOutDevices();
1216}
1217
1218OutputDevice* DeviceManager::getOutputDeviceAt (int index) const
1219{
1220 if (index >= getNumWaveOutDevices())
1221 return getMidiOutDevice (index - getNumWaveOutDevices());
1222
1223 return getWaveOutDevice (index);
1224}
1225
1226InputDevice* DeviceManager::findInputDeviceForID (const juce::String& id) const
1227{
1228 for (auto d : waveInputs)
1229 if (d->getDeviceID() == id)
1230 return d;
1231
1232 const std::shared_lock sl (midiInputsMutex);
1233
1234 for (auto d : midiInputs)
1235 if (d->getDeviceID() == id)
1236 return d.get();
1237
1238 return {};
1239}
1240
1241InputDevice* DeviceManager::findInputDeviceWithName (const juce::String& name) const
1242{
1243 for (auto d : waveInputs)
1244 if (d->getName() == name)
1245 return d;
1246
1247 const std::shared_lock sl (midiInputsMutex);
1248
1249 for (auto d : midiInputs)
1250 if (d->getName() == name)
1251 return d.get();
1252
1253 return {};
1254}
1255
1256OutputDevice* DeviceManager::findOutputDeviceForID (const juce::String& id) const
1257{
1258 for (auto d : waveOutputs)
1259 if (d->getDeviceID() == id)
1260 return d;
1261
1262 for (auto d : midiOutputs)
1263 if (d->getDeviceID() == id)
1264 return d.get();
1265
1266 return {};
1267}
1268
1269OutputDevice* DeviceManager::findOutputDeviceWithName (const juce::String& name) const
1270{
1271 if (name == getDefaultAudioOutDeviceName (false)) return getDefaultWaveOutDevice();
1272 if (name == getDefaultMidiOutDeviceName (false)) return getDefaultMidiOutDevice();
1273
1274 for (auto d : waveOutputs)
1275 if (d->getName() == name)
1276 return d;
1277
1278 for (auto d : midiOutputs)
1279 if (d->getName() == name)
1280 return d.get();
1281
1282 return {};
1283}
1284
1285int DeviceManager::getRecordAdjustmentSamples()
1286{
1287 if (auto d = deviceManager.getCurrentAudioDevice())
1288 return d->getOutputLatencyInSamples() + d->getInputLatencyInSamples();
1289
1290 return 0;
1291}
1292
1293double DeviceManager::getRecordAdjustmentMs()
1294{
1295 if (auto d = deviceManager.getCurrentAudioDevice())
1296 return getRecordAdjustmentSamples() * 1000.0 / d->getCurrentSampleRate();
1297
1298 return 0.0;
1299}
1300
1301double DeviceManager::getOutputLatencySeconds() const
1302{
1303 return outputLatencyTime;
1304}
1305
1306PerformanceMeasurement::Statistics DeviceManager::getCPUStatistics() const
1307{
1308 return performanceStats.load();
1309}
1310
1311void DeviceManager::restCPUStatistics()
1312{
1313 clearStatsFlag.store (true, std::memory_order_relaxed);
1314}
1315
1316void DeviceManager::audioDeviceIOCallbackWithContext (const float* const* inputChannelData, int numInputChannels,
1317 float* const* outputChannelData, int totalNumOutputChannels,
1318 int numSamples,
1320{
1323
1324 #if JUCE_ANDROID
1325 const ScopedSteadyLoad load (steadyLoadContext, numSamples);
1326 #endif
1327
1328 if (isSuspended)
1329 {
1330 for (int i = 0; i < totalNumOutputChannels; ++i)
1331 if (auto dest = outputChannelData[i])
1332 juce::FloatVectorOperations::clear (dest, numSamples);
1333
1334 return;
1335 }
1336
1337 // Some interfaces ask for blocks larger than the current buffer size so in
1338 // these cases we need to render the buffer in chunks
1339 if (numSamples <= maxBlockSize)
1340 {
1341 audioDeviceIOCallbackInternal (inputChannelData, numInputChannels,
1342 outputChannelData, totalNumOutputChannels,
1343 numSamples);
1344 return;
1345 }
1346
1347 for (int sampleStartIndex = 0;;)
1348 {
1349 const auto numThisTime = std::min (numSamples, maxBlockSize);
1350
1351 for (int i = 0; i < numInputChannels; ++i)
1352 inputChannelsScratch[i] = inputChannelData[i] + sampleStartIndex;
1353
1354 for (int i = 0; i < totalNumOutputChannels; ++i)
1355 outputChannelsScratch[i] = outputChannelData[i] + sampleStartIndex;
1356
1357 audioDeviceIOCallbackInternal (inputChannelsScratch, numInputChannels,
1358 outputChannelsScratch, totalNumOutputChannels,
1359 numThisTime);
1360
1361 numSamples -= numThisTime;
1362 sampleStartIndex += numThisTime;
1363
1364 if (numSamples == 0)
1365 break;
1366 }
1367}
1368
1369void DeviceManager::audioDeviceIOCallbackInternal (const float* const* inputChannelData, int numInputChannels,
1370 float* const* outputChannelData, int totalNumOutputChannels,
1371 int numSamples)
1372{
1373 {
1374 engine.getAudioFileManager().cache.nextBlockStarted();
1375
1376 if (clearStatsFlag.exchange (false))
1377 performanceMeasurement.getStatisticsAndReset();
1378
1379 const ScopedPerformanceMeasurement spm (performanceMeasurement);
1380
1381 if (currentCpuUsage > cpuLimitBeforeMuting)
1382 {
1383 for (int i = 0; i < totalNumOutputChannels; ++i)
1384 if (auto dest = outputChannelData[i])
1385 juce::FloatVectorOperations::clear (dest, numSamples);
1386
1387 currentCpuUsage = std::min (0.9, currentCpuUsage * 0.99);
1388 }
1389 else
1390 {
1391 broadcastStreamTimeToMidiDevices (streamTime + outputLatencyTime);
1392 juce::Range<double> blockStreamTime;
1393
1394 {
1395 SCOPED_REALTIME_CHECK
1396 const std::shared_lock sl (contextLock);
1397
1398 for (auto wi : waveInputs)
1399 wi->consumeNextAudioBlock (inputChannelData, numInputChannels, numSamples, streamTime);
1400
1401 for (int i = totalNumOutputChannels; --i >= 0;)
1402 if (auto dest = outputChannelData[i])
1403 juce::FloatVectorOperations::clear (dest, numSamples);
1404
1405 double blockLength = numSamples / currentSampleRate;
1406
1407 blockStreamTime = { streamTime, streamTime + blockLength };
1408
1409 for (auto c : activeContexts)
1410 c->fillNextNodeBlock (outputChannelData, totalNumOutputChannels, numSamples);
1411 }
1412
1413 for (int i = totalNumOutputChannels; --i >= 0;)
1414 if (auto* dest = outputChannelData[i])
1415 if (activeOutChannels[i])
1416 for (int j = 0; j < numSamples; ++j)
1417 if (! std::isnormal (dest[j]))
1418 dest[j] = 0;
1419
1420 streamTime = blockStreamTime.getEnd();
1421 currentCpuUsage = deviceManager.getCpuUsage();
1422 }
1423
1424 if (globalOutputAudioProcessor != nullptr)
1425 {
1426 juce::AudioBuffer<float> ab (outputChannelData, totalNumOutputChannels, numSamples);
1428 globalOutputAudioProcessor->processBlock (ab, mb);
1429 }
1430
1431 // Output clipping
1432 if (outputClippingEnabled.load (std::memory_order_relaxed))
1433 {
1434 bool hasClipped = false;
1435
1436 for (int i = totalNumOutputChannels; --i >= 0;)
1437 {
1438 if (auto dest = outputChannelData[i])
1439 {
1440 if (activeOutChannels[i])
1441 {
1442 for (int j = 0; j < numSamples; ++j)
1443 {
1444 auto samp = dest[j];
1445
1446 if (samp < -1.0f)
1447 {
1448 samp = -1.0f;
1449 hasClipped = true;
1450 }
1451 else if (samp > 1.0f)
1452 {
1453 samp = 1.0f;
1454 hasClipped = true;
1455 }
1456 else
1457 {
1458 continue;
1459 }
1460
1461 dest[j] = samp;
1462 }
1463 }
1464 }
1465 }
1466
1467 if (hasClipped)
1468 outputHasClipped.store (true, std::memory_order_relaxed);
1469 }
1470 }
1471
1472 performanceStats.store (performanceMeasurement.getStatistics());
1473}
1474
1475void DeviceManager::audioDeviceAboutToStart (juce::AudioIODevice* device)
1476{
1477 streamTime = 0;
1478 currentCpuUsage = 0.0f;
1479
1480 if (globalOutputAudioProcessor != nullptr)
1481 globalOutputAudioProcessor->prepareToPlay (device->getCurrentSampleRate(), device->getCurrentBufferSizeSamples());
1482
1483 #if JUCE_ANDROID
1484 steadyLoadContext.setSampleRate (device->getCurrentSampleRate());
1485 #endif
1486
1487 // A lot of the prep has to happen on the message thread, so we'll suspend
1488 // the callbacks until prepareToStart() has been called
1489 isSuspended = true;
1490 prepareToStartCaller->triggerAsyncUpdate();
1491}
1492
1493void DeviceManager::prepareToStart()
1494{
1495 if (auto device = deviceManager.getCurrentAudioDevice())
1496 {
1497 maxBlockSize = device->getCurrentBufferSizeSamples();
1498 currentSampleRate = device->getCurrentSampleRate();
1499 jassert (currentSampleRate > 0.0);
1500 currentLatencyMs = maxBlockSize * 1000.0f / currentSampleRate;
1501 outputLatencyTime = device->getOutputLatencyInSamples() / currentSampleRate;
1502 defaultWaveOutID = engine.getPropertyStorage().getPropertyItem (SettingID::defaultWaveOutDevice, device->getTypeName());
1503 defaultWaveInID = engine.getPropertyStorage().getPropertyItem (SettingID::defaultWaveInDevice, device->getTypeName());
1504
1505 inputChannelsScratch.realloc (device->getInputChannelNames().size());
1506 outputChannelsScratch.realloc (device->getOutputChannelNames().size());
1507
1508 {
1509 const std::shared_lock sl (contextLock);
1510
1511 for (auto c : activeContexts)
1512 {
1513 const EditPlaybackContext::ScopedDeviceListReleaser rebuilder (*c, true);
1514 c->resyncToGlobalStreamTime ({ streamTime, streamTime + device->getCurrentBufferSizeSamples() / currentSampleRate }, currentSampleRate);
1515 c->edit.restartPlayback();
1516 }
1517 }
1518
1519 isSuspended = false;
1520 }
1521}
1522
1523void DeviceManager::audioDeviceStopped()
1524{
1525 isSuspended = true;
1526 currentCpuUsage = 0.0f;
1527
1528 if (globalOutputAudioProcessor != nullptr)
1529 globalOutputAudioProcessor->releaseResources();
1530}
1531
1532void DeviceManager::updateNumCPUs()
1533{
1534 const juce::ScopedLock sl (deviceManager.getAudioCallbackLock());
1535 const std::shared_lock cl (contextLock);
1536
1537 for (auto c : activeContexts)
1538 c->updateNumCPUs();
1539}
1540
1541//==============================================================================
1542void DeviceManager::enableOutputClipping (bool clipOutput)
1543{
1544 outputClippingEnabled.store (clipOutput, std::memory_order_relaxed);
1545}
1546
1547bool DeviceManager::hasOutputClipped (bool reset)
1548{
1549 const auto hasClipped = outputHasClipped.load (std::memory_order_relaxed);
1550
1551 if (hasClipped && reset)
1552 outputHasClipped.store (false, std::memory_order_relaxed);
1553
1554 return hasClipped;
1555}
1556
1557void DeviceManager::addContext (EditPlaybackContext* c)
1558{
1559 TRACKTION_ASSERT_MESSAGE_THREAD
1560
1561 double lastStreamTime;
1562
1563 {
1564 const std::unique_lock sl (contextLock);
1565 lastStreamTime = streamTime;
1566 c->resyncToGlobalStreamTime ({ lastStreamTime, lastStreamTime + getBlockSize() / currentSampleRate }, currentSampleRate);
1567 activeContexts.addIfNotAlreadyThere (c);
1568 }
1569
1570 for (int i = 200; --i >= 0;)
1571 {
1572 juce::Thread::sleep (1);
1573 if (lastStreamTime != streamTime)
1574 break;
1575 }
1576}
1577
1578void DeviceManager::removeContext (EditPlaybackContext* c)
1579{
1580 const std::unique_lock sl (contextLock);
1581 activeContexts.removeAllInstancesOf (c);
1582}
1583
1584void DeviceManager::clearAllContextDevices()
1585{
1586 const std::shared_lock sl (contextLock);
1587
1588 for (auto c : activeContexts)
1589 const EditPlaybackContext::ScopedDeviceListReleaser rebuilder (*c, false);
1590}
1591
1592void DeviceManager::reloadAllContextDevices()
1593{
1594 const std::shared_lock sl (contextLock);
1595
1596 for (auto c : activeContexts)
1597 {
1598 const EditPlaybackContext::ScopedDeviceListReleaser rebuilder (*c, true);
1599 c->edit.restartPlayback();
1600 }
1601}
1602
1603void DeviceManager::setGlobalOutputAudioProcessor (std::unique_ptr<juce::AudioProcessor> newProcessor)
1604{
1605 if (newProcessor != nullptr)
1606 newProcessor->prepareToPlay (getSampleRate(), getBlockSize());
1607
1608 {
1609 const juce::ScopedLock sl (deviceManager.getAudioCallbackLock());
1610 std::swap (globalOutputAudioProcessor, newProcessor);
1611 }
1612
1613 newProcessor.reset();
1614}
1615
1616DeviceManager::DeviceManager (Engine& e) : engine (e)
1617{
1619
1620 prepareToStartCaller = std::make_unique<PrepareToStartCaller> (*this);
1621
1622 deviceManager.addChangeListener (this);
1623
1624 gDeviceManager = &deviceManager;
1625}
1626
1627DeviceManager::~DeviceManager()
1628{
1629 gDeviceManager = nullptr;
1630
1632 deviceManager.removeChangeListener (this);
1633}
1634
1635void DeviceManager::closeDevices()
1636{
1638 TRACKTION_ASSERT_MESSAGE_THREAD
1639
1640 lastMIDIDeviceList.reset();
1641 lastAvailableWaveDeviceList.reset();
1642
1643 jassert (activeContexts.isEmpty());
1644 clearAllContextDevices();
1645
1646 deviceManager.removeAudioCallback (this);
1647
1648 midiOutputs.clear();
1649
1650 {
1651 const std::unique_lock sl (midiInputsMutex);
1652 midiInputs.clear();
1653 }
1654
1655 waveInputs.clear();
1656 waveOutputs.clear();
1657}
1658
1659}} // namespace tracktion { inline namespace engine
ElementType * begin() noexcept
ElementType * end() noexcept
virtual void createAudioDeviceTypes(OwnedArray< AudioIODeviceType > &types)
void removeAudioCallback(AudioIODeviceCallback *callback)
void swapWith(BigInteger &) noexcept
void addChangeListener(ChangeListener *listener)
void removeChangeListener(ChangeListener *listener)
static bool isDigit(char character) noexcept
static void JUCE_CALLTYPE disableDenormalisedNumberSupport(bool shouldDisable=true) noexcept
static Array< MidiDeviceInfo > getAvailableDevices()
static Array< MidiDeviceInfo > getAvailableDevices()
void swapWith(OtherArrayType &otherArray) noexcept
ObjectClass * add(ObjectClass *newObject)
constexpr ValueType getEnd() const noexcept
static Result fail(const String &errorMessage) noexcept
static Result ok() noexcept
String joinIntoString(StringRef separatorString, int startIndex=0, int numberOfElements=-1) const
void removeEmptyStrings(bool removeWhitespaceStrings=true)
void insert(int index, String stringToAdd)
void removeString(StringRef stringToRemove, bool ignoreCase=false)
int addTokens(StringRef stringToTokenise, bool preserveQuotedStrings)
String upToFirstOccurrenceOf(StringRef substringToEndWith, bool includeSubStringInResult, bool ignoreCase) const
int length() const noexcept
String trim() const
bool endsWithChar(juce_wchar character) const noexcept
bool isEmpty() const noexcept
static String toHexString(IntegerType number)
String upToLastOccurrenceOf(StringRef substringToFind, bool includeSubStringInResult, bool ignoreCase) const
static String charToString(juce_wchar character)
String substring(int startIndex, int endIndex) const
String fromLastOccurrenceOf(StringRef substringToFind, bool includeSubStringInResult, bool ignoreCase) const
bool isNotEmpty() const noexcept
static int getNumCpus() noexcept
The Engine is the central class for all tracktion sessions.
DeviceManager & getDeviceManager() const
Returns the DeviceManager instance for handling audio / MIDI devices.
The HostedAudioDeviceInterface allows an application or plugin to pass audio and midi buffers to the ...
T is_pointer_v
T isnormal(T... args)
#define TRANS(stringLiteral)
#define jassert(expression)
T min(T... args)
juce::String getName(LaunchQType t)
Retuns the name of a LaunchQType for display purposes.
juce::String createDescriptionOfChannels(const std::vector< ChannelIndex > &channels)
Creates a String description of the channels.
T push_back(T... args)
T reset(T... args)
T sort(T... args)
Describes a WaveDevice from which the WaveOutputDevice and WaveInputDevice lists will be built.
T swap(T... args)
typedef size_t
#define CRASH_TRACER
This macro adds the current location to a stack which gets logged if a crash happens.