13namespace tracktion {
inline namespace engine
16#if TRACKTION_LOG_DEVICES
17 #define TRACKTION_LOG_DEVICE(text) TRACKTION_LOG(text)
19 #define TRACKTION_LOG_DEVICE(text)
32 if ((! (bracketed1.isEmpty() || bracketed2.isEmpty()))
38 <<
" (" << bracketed1 <<
" + " << bracketed2 <<
")";
44 for (
int i = s1.
length(); --i >= 0;)
50 for (
int i = s2.
length(); --i >= 0;)
61 << endNum1 <<
" + " << endNum2;
65 nm << s1 <<
" + " << s2;
72static bool isMicrosoftGSSynth (MidiOutputDevice& mo)
75 return mo.getName().containsIgnoreCase (
"Microsoft GS ");
77 (void) mo;
return false;
82DeviceManager::TracktionEngineAudioDeviceManager::TracktionEngineAudioDeviceManager (
Engine& e) : engine (e) {}
86 if (engine.getEngineBehaviour().addSystemAudioIODeviceTypes())
95 if (
auto device = dm.deviceManager.getCurrentAudioDevice())
97 deviceName = device->getName();
99 if (dm.engine.getEngineBehaviour().isDescriptionOfWaveDevicesSupported())
101 dm.engine.getEngineBehaviour().describeWaveDevices (inputs, *device,
true);
102 dm.engine.getEngineBehaviour().describeWaveDevices (outputs, *device,
false);
106 describeStandardDevices (inputs, *device,
true);
107 describeStandardDevices (outputs, *device,
false);
114 auto channelNames = isInput ? device.getInputChannelNames()
115 : device.getOutputChannelNames();
117 if (channelNames.size() == 2)
120 channelNames.set (0, name.replace (
"123",
"1"));
121 channelNames.set (1, name.replace (
"123",
"2"));
124 auto isDeviceEnabled = [
this, isInput] (
int index)
126 return isInput ? dm.isDeviceInEnabled (index) : dm.isDeviceOutEnabled (index);
129 for (
int i = 0; i < channelNames.size(); ++i)
131 const bool canBeStereo = i < channelNames.size() - 1;
133 if (canBeStereo && (isInput ? dm.isDeviceInChannelStereo (i)
134 : dm.isDeviceOutChannelStereo (i)))
137 i, i + 1, isDeviceEnabled (i) || isDeviceEnabled (i + 1)));
145 if (i < channelNames.size())
151 bool operator== (
const AvailableWaveDeviceList& other)
const noexcept {
return deviceName == other.deviceName && inputs == other.inputs && outputs == other.outputs; }
165 void handleAsyncUpdate()
override
167 deviceManager.prepareToStart();
175void DeviceManager::initialise (
int defaultNumInputs,
int defaultNumOutputs)
177 defaultNumInputChannelsToOpen = defaultNumInputs;
178 defaultNumOutputChannelsToOpen = defaultNumOutputs;
181 finishedInitialising =
true;
182 rescanMidiDeviceList();
183 rescanWaveDeviceList();
186 deviceManager.addAudioCallback (
this);
188 midiRescanIntervalSeconds = engine.getPropertyStorage().getProperty (SettingID::midiScanIntervalSeconds, 4);
189 restartMidiCheckTimer();
192void DeviceManager::resetToDefaults (
bool deviceSettings,
bool resetInputDevices,
193 bool resetOutputDevices,
bool latencySettings,
bool mixSettings)
195 TRACKTION_LOG (
"Returning audio settings to defaults");
197 auto& storage = engine.getPropertyStorage();
201 storage.removeProperty (SettingID::audio_device_setup);
202 storage.removePropertyItem (SettingID::audiosettings, deviceManager.getCurrentAudioDeviceType());
207 storage.setProperty (SettingID::maxLatency, 5.0f);
208 storage.setProperty (SettingID::lowLatencyBuffer, 5.8f);
216 storage.setProperty (SettingID::use64Bit,
false);
219 if (resetInputDevices)
220 for (
auto wid : waveInputs)
221 wid->resetToDefault();
223 if (resetOutputDevices)
224 for (
auto wod : waveOutputs)
225 wod->resetToDefault();
228 TransportControl::restartAllTransports (engine,
false);
229 SelectionManager::refreshAllPropertyPanels();
232void DeviceManager::setMidiDeviceScanIntervalSeconds (
int intervalSeconds)
234 midiRescanIntervalSeconds = intervalSeconds;
235 engine.getPropertyStorage().setProperty (SettingID::midiScanIntervalSeconds, intervalSeconds);
236 restartMidiCheckTimer();
239void DeviceManager::restartMidiCheckTimer()
241 if (usesHardwareMidiDevices() && midiRescanIntervalSeconds != 0)
242 startTimer (midiRescanIntervalSeconds * 1000);
247void DeviceManager::rescanMidiDeviceList()
249 onlyRescanMidiOnHardwareChange =
false;
253void DeviceManager::timerCallback()
255 applyNewMidiDeviceList();
258static constexpr const char* allMidiInsName =
"All MIDI Ins";
259static constexpr const char* allMidiInsID =
"all_midi_in";
264 virtualDeviceIDs.
addTokens (engine.getPropertyStorage().getProperty (SettingID::virtualmididevices).toString(),
";", {});
268 virtualDeviceIDs.
insert (0, allMidiInsID);
269 return virtualDeviceIDs;
274 engine.getPropertyStorage().setProperty (SettingID::virtualmididevices, list.
joinIntoString (
";"));
286 std::vector<bool> physicalMidiOutsEnabled, physicalMidiInsEnabled, virtualMidiInsEnabled;
292 bool useHardwareDevices)
294 if (sourceHostedAudioDeviceInterface)
300 if (useHardwareDevices)
302 scanHardwareDevices (sourceEngine);
304 for (
auto& info : midiOuts)
308 physicalMidiOutsEnabled.
push_back (d->isEnabled());
311 for (
auto& info : midiIns)
315 physicalMidiInsEnabled.
push_back (d->isEnabled());
323 physicalMidiOutsEnabled.
push_back (d->isEnabled());
327 for (
auto& v : getVirtualDeviceIDs (sourceEngine))
329 bool isAllMidiIn = (v == allMidiInsID);
330 auto deviceName = isAllMidiIn ?
juce::String (allMidiInsName) : v;
334 virtualMidiInsEnabled.
push_back (d->isEnabled());
338 void scanHardwareDevices (
Engine& e)
341 static constexpr double maxAcceptableTimeForAutoScanning = 0.2;
346 auto scanTime = timer.getSeconds();
348 if (scanTime > maxAcceptableTimeForAutoScanning)
350 TRACKTION_LOG (
"MIDI input scan took " +
juce::String (scanTime) +
" seconds. Disabling auto-scanning");
358 auto scanTime = timer.getSeconds();
360 if (scanTime > maxAcceptableTimeForAutoScanning)
362 TRACKTION_LOG (
"MIDI output scan took " +
juce::String (scanTime) +
" seconds. Disabling auto-scanning");
375 static bool hasReported =
false;
377 if (! hasReported || total.getSeconds() > 0.1)
380 TRACKTION_LOG_DEVICE (
"MIDI Input devices scanned in: " + total.getDescription());
386 return midiIns != other.midiIns
387 || midiOuts != other.midiOuts;
392 return physicalMidiOutsEnabled == other.physicalMidiOutsEnabled
393 && physicalMidiInsEnabled == other.physicalMidiInsEnabled
394 && virtualMidiInsEnabled == other.virtualMidiInsEnabled;
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);
411 return ! operator== (other);
416 if (hostedMidiIn && current.hostedMidiIn)
417 hostedMidiIn = current.hostedMidiIn;
419 if (hostedMidiOut && current.hostedMidiOut)
420 hostedMidiOut = current.hostedMidiOut;
422 for (
auto& d : physicalMidiIns)
423 for (
auto& old : current.physicalMidiIns)
424 if (old->getDeviceID() == d->getDeviceID())
427 for (
auto& d : virtualMidiIns)
428 for (
auto& old : current.virtualMidiIns)
429 if (old->getDeviceID() == d->getDeviceID())
432 for (
auto& d : physicalMidiOuts)
433 for (
auto& old : current.physicalMidiOuts)
434 if (old->getDeviceID() == d->getDeviceID())
445 for (
auto& d : physicalMidiIns)
448 for (
auto& d : virtualMidiIns)
461 for (
auto& d : physicalMidiOuts)
468void DeviceManager::applyNewMidiDeviceList()
471 TRACKTION_ASSERT_MESSAGE_THREAD
473 restartMidiCheckTimer();
475 if (lastMIDIDeviceList ==
nullptr)
479 hostedAudioDeviceInterface.get(),
480 usesHardwareMidiDevices());
482 if (onlyRescanMidiOnHardwareChange
483 && ! newList->hasHardwareChanged (*lastMIDIDeviceList))
486 onlyRescanMidiOnHardwareChange =
true;
488 auto& storage = engine.getPropertyStorage();
490 auto newDefaultOut = storage.getProperty (SettingID::defaultMidiOutDevice).toString();
491 auto newDefaultIn = storage.getProperty (SettingID::defaultMidiInDevice).toString();
493 bool defaultsChanged = (defaultMidiOutID != newDefaultOut
494 || defaultMidiInID != newDefaultIn);
496 if (! defaultsChanged && *newList == *lastMIDIDeviceList)
499 defaultMidiOutID = newDefaultOut;
500 defaultMidiInID = newDefaultIn;
502 bool enablementChanged = ! newList->hasSameEnablement (*lastMIDIDeviceList);
504 newList->harvestExistingDevices (*lastMIDIDeviceList);
506 auto newMidiIns = newList->getAllIns();
507 auto newMidiOuts = newList->getAllOuts();
509 lastMIDIDeviceList = std::move (newList);
511 if (! (defaultsChanged || enablementChanged))
515 if (newMidiIns == midiInputs && newMidiOuts == midiOutputs)
519 TRACKTION_LOG (
"Updating MIDI I/O devices");
521 for (
auto mi : newMidiIns)
522 TRACKTION_LOG_DEVICE (
"Found MIDI in: " + mi->getDeviceID() +
" (\"" + mi->
getName() +
"\")" + (mi->isEnabled() ?
" (enabled)" :
""));
524 for (
auto mo : newMidiOuts)
525 TRACKTION_LOG_DEVICE (
"Found MIDI out: " + mo->getDeviceID() +
" (\"" + mo->
getName() +
"\")" + (mo->isEnabled() ?
" (enabled)" :
""));
527 for (
auto& d : newMidiOuts)
531 auto error = d->openDevice();
533 if (! error.isEmpty())
535 d->setEnabled (
false);
536 engine.getUIBehaviour().showWarningMessage (error);
541 for (
auto& d : newMidiIns)
545 auto error = d->openDevice();
547 if (! error.isEmpty())
549 d->setEnabled (
false);
550 engine.getUIBehaviour().showWarningMessage (error);
555 clearAllContextDevices();
564 reloadAllContextDevices();
566 int enabledMidiOuts = 0;
568 for (
auto mo : midiOutputs)
579 for (
auto mi : midiInputs)
580 if (! mi->isEnabled())
584 const bool hasEnabledMidiDefaultDevs = storage.getProperty (SettingID::hasEnabledMidiDefaultDevs,
false);
585 storage.setProperty (SettingID::hasEnabledMidiDefaultDevs,
true);
586 storage.flushSettingsToDisk();
588 checkDefaultDevicesAreValid();
590 if (enabledMidiOuts == 0 && ! hasEnabledMidiDefaultDevs)
592 for (
auto& d : midiOutputs)
594 if (! isMicrosoftGSSynth (*d))
596 d->setEnabled (
true);
604 engine.getExternalControllerManager().midiInOutDevicesChanged();
609 if (
auto input = getDefaultMidiInDevice())
610 input->handleIncomingMidiMessage (
nullptr, m);
613void DeviceManager::broadcastMessageToAllVirtualDevices (MidiInputDevice& source,
const juce::MidiMessage& m)
617 for (
auto d : midiInputs)
618 if (auto vmd = dynamic_cast<VirtualMidiInputDevice*> (d.get()))
619 vmd->handleMessageFromPhysicalDevice (source, m);
622bool DeviceManager::usesHardwareMidiDevices()
624 return hostedAudioDeviceInterface ==
nullptr
625 || hostedAudioDeviceInterface->parameters.useMidiDevices;
630 if (hostedAudioDeviceInterface ==
nullptr)
633 return *hostedAudioDeviceInterface;
636bool DeviceManager::isHostedAudioDeviceInterfaceInUse()
const
638 return hostedAudioDeviceInterface !=
nullptr
639 && deviceManager.getCurrentAudioDeviceType() ==
"Hosted Device";
642void DeviceManager::removeHostedAudioDeviceInterface()
644 for (
auto device : deviceManager.getAvailableDeviceTypes())
646 if (device->getTypeName() ==
"Hosted Device")
648 deviceManager.removeAudioDeviceType (device);
653 hostedAudioDeviceInterface.reset();
656juce::String DeviceManager::getDefaultAudioOutDeviceName (
bool translated)
658 return translated ? (
"(" +
TRANS(
"Default audio output") +
")")
659 :
"(default audio output)";
662juce::String DeviceManager::getDefaultMidiOutDeviceName (
bool translated)
664 return translated ? (
"(" +
TRANS(
"Default MIDI output") +
")")
665 :
"(default MIDI output)";
668juce::String DeviceManager::getDefaultAudioInDeviceName (
bool translated)
670 return translated ? (
"(" +
TRANS(
"Default audio input") +
")")
671 :
"(default audio input)";
674juce::String DeviceManager::getDefaultMidiInDeviceName (
bool translated)
676 return translated ? (
"(" +
TRANS(
"Default MIDI input") +
")")
677 :
"(default MIDI input)";
681void DeviceManager::changeListenerCallback (ChangeBroadcaster*)
685 rescanWaveDeviceList();
688bool DeviceManager::isMSWavetableSynthPresent()
const
690 for (
auto mo : midiOutputs)
691 if (mo->isEnabled() && isMicrosoftGSSynth (*mo))
700 TRACKTION_ASSERT_MESSAGE_THREAD
702 auto virtualDeviceIDs = getVirtualDeviceIDs (engine);
704 if (virtualDeviceIDs.contains (name))
707 virtualDeviceIDs.add (name);
708 setVirtualDeviceIDs (engine, virtualDeviceIDs);
710 rescanMidiDeviceList();
715void DeviceManager::deleteVirtualMidiDevice (VirtualMidiInputDevice& vmi)
718 TRACKTION_ASSERT_MESSAGE_THREAD
720 auto deviceID = vmi.getDeviceID();
722 engine.getPropertyStorage().removePropertyItem (SettingID::virtualmidiin, deviceID);
724 auto virtualDeviceIDs = getVirtualDeviceIDs (engine);
725 virtualDeviceIDs.removeString (deviceID);
726 setVirtualDeviceIDs (engine, virtualDeviceIDs);
728 rescanMidiDeviceList();
731void DeviceManager::sanityCheckEnabledChannels()
733 for (
int i = outEnabled.getHighestBit() + 2; --i >= 0;)
735 if (isDeviceOutChannelStereo (i))
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);
744 for (
int i = inEnabled.getHighestBit() + 2; --i >= 0;)
746 if (isDeviceInChannelStereo (i))
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);
755 if (currentSampleRate < 22050 || currentSampleRate > 200000)
756 currentSampleRate = 44100.0;
759void DeviceManager::rescanWaveDeviceList()
763 if (lastAvailableWaveDeviceList ==
nullptr || *newList != *lastAvailableWaveDeviceList)
765 lastAvailableWaveDeviceList = std::move (newList);
766 triggerAsyncUpdate();
770void DeviceManager::handleAsyncUpdate()
773 TRACKTION_ASSERT_MESSAGE_THREAD
775 static bool reentrant =
false;
783 TRACKTION_LOG (
"Rebuilding Wave Device List...");
785 prepareToStartCaller->handleUpdateNowIfNeeded();
787 if (lastAvailableWaveDeviceList ==
nullptr)
794 for (
auto& d : lastAvailableWaveDeviceList->inputs)
796 auto wi =
new WaveInputDevice (engine,
TRANS(
"Wave Audio Input"), d, InputDevice::waveDevice);
797 newWaveInputs.
add (wi);
799 TRACKTION_LOG_DEVICE (
"Wave In: " + wi->getName() + (wi->isEnabled() ?
" (enabled): " :
": ")
803 for (
auto& d : lastAvailableWaveDeviceList->outputs)
805 auto wo =
new WaveOutputDevice (engine, d);
806 newWaveOutputs.
add (wo);
809 for (
const auto& ci : wo->getChannels())
810 newActiveOutChannels.setBit (ci.indexInDevice);
812 TRACKTION_LOG_DEVICE (
"Wave Out: " + wo->getName() + (wo->isEnabled() ?
" (enabled): " :
": ")
816 clearAllContextDevices();
820 newWaveInputs.
swapWith (waveInputs);
821 newWaveOutputs.
swapWith (waveOutputs);
822 newActiveOutChannels.
swapWith (activeOutChannels);
825 sanityCheckEnabledChannels();
826 reloadAllContextDevices();
828 checkDefaultDevicesAreValid();
831 #if TRACKTION_LOG_ENABLED
832 auto wo = getDefaultWaveOutDevice();
833 TRACKTION_LOG (
"Default Wave Out: " + (wo !=
nullptr ? wo->getName() :
juce::String()));
835 auto mo = getDefaultMidiOutDevice();
836 TRACKTION_LOG (
"Default MIDI Out: " + (mo !=
nullptr ? mo->getName() :
juce::String()));
838 auto wi = getDefaultWaveInDevice();
839 TRACKTION_LOG (
"Default Wave In: " + (wi !=
nullptr ? wi->getName() :
juce::String()));
841 auto mi = getDefaultMidiInDevice();
842 TRACKTION_LOG (
"Default MIDI In: " + (mi !=
nullptr ? mi->getName() :
juce::String()));
846void DeviceManager::loadSettings()
849 auto& storage = engine.getPropertyStorage();
853 if (isHostedAudioDeviceInterfaceInUse())
855 error = deviceManager.initialise (defaultNumInputChannelsToOpen,
856 defaultNumOutputChannelsToOpen,
857 nullptr,
false,
"Hosted Device",
nullptr);
861 auto audioXml = storage.getXmlProperty (SettingID::audio_device_setup);
863 if (audioXml !=
nullptr)
864 error = deviceManager.initialise (defaultNumInputChannelsToOpen,
865 defaultNumOutputChannelsToOpen,
866 audioXml.get(),
true);
868 error = deviceManager.initialiseWithDefaultDevices (defaultNumInputChannelsToOpen,
869 defaultNumOutputChannelsToOpen);
873 TRACKTION_LOG_ERROR (
"AudioDeviceManager init: " + error);
876 outMonoChans.clear();
877 inStereoChans.clear();
879 outEnabled.setBit (0);
880 outEnabled.setBit (1);
882 inEnabled.setBit (0);
883 inEnabled.setBit (1);
885 if (! engine.getEngineBehaviour().isDescriptionOfWaveDevicesSupported())
887 if (
auto n = storage.getXmlPropertyItem (SettingID::audiosettings, deviceManager.getCurrentAudioDeviceType()))
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);
896 auto currentDeviceType = deviceManager.getCurrentAudioDeviceType();
897 defaultWaveOutID = storage.getPropertyItem (SettingID::defaultWaveOutDevice, currentDeviceType);
898 defaultWaveInID = storage.getPropertyItem (SettingID::defaultWaveInDevice, currentDeviceType);
900 TRACKTION_LOG (
"Audio block size: " +
juce::String (getBlockSize())
904void DeviceManager::saveSettings()
906 auto& storage = engine.getPropertyStorage();
908 if (
auto audioXml = deviceManager.createStateXml())
909 storage.setXmlProperty (SettingID::audio_device_setup, *audioXml);
911 if (! engine.getEngineBehaviour().isDescriptionOfWaveDevicesSupported())
913 if (deviceManager.getCurrentAudioDevice() !=
nullptr)
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));
922 storage.setXmlPropertyItem (SettingID::audiosettings, deviceManager.getCurrentAudioDeviceType(), n);
927void DeviceManager::checkDefaultDevicesAreValid()
929 if (! finishedInitialising)
932 if (getDefaultWaveOutDevice() ==
nullptr || ! getDefaultWaveOutDevice()->isEnabled())
934 for (
auto d : waveOutputs)
938 setDefaultWaveOutDevice (d->getDeviceID());
944 if (getDefaultWaveInDevice() ==
nullptr || ! getDefaultWaveInDevice()->isEnabled())
946 for (
auto d : waveInputs)
950 setDefaultWaveInDevice (d->getDeviceID());
956 if (getDefaultMidiOutDevice() ==
nullptr || ! getDefaultMidiOutDevice()->isEnabled())
958 for (
auto d : midiOutputs)
962 setDefaultMidiOutDevice (d->getDeviceID());
968 if (getDefaultMidiInDevice() ==
nullptr || ! getDefaultMidiInDevice()->isEnabled())
970 if (
auto allMidi = findInputDeviceForID (allMidiInsID);
971 allMidi !=
nullptr && allMidi->isEnabled())
973 setDefaultMidiInDevice (allMidi->getDeviceID());
979 for (
auto d : midiInputs)
983 setDefaultMidiInDevice (d->getDeviceID());
991double DeviceManager::getSampleRate()
const
993 if (
auto device = deviceManager.getCurrentAudioDevice())
994 return device->getCurrentSampleRate();
999int DeviceManager::getBitDepth()
const
1001 if (
auto device = deviceManager.getCurrentAudioDevice())
1002 return device->getCurrentBitDepth();
1007int DeviceManager::getBlockSize()
const
1009 if (
auto device = deviceManager.getCurrentAudioDevice())
1010 return device->getCurrentBufferSizeSamples();
1015double DeviceManager::getBlockSizeMs()
const
1017 return getBlockSize() * 1000.0 / getSampleRate();
1020TimeDuration DeviceManager::getBlockLength()
const
1022 return TimeDuration::fromSamples (getBlockSize(), getSampleRate());
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())); }
1030void DeviceManager::setDefaultWaveOutDevice (
juce::String deviceID)
1032 if (defaultWaveOutID != deviceID)
1034 if (
auto d = findOutputDeviceForID (deviceID))
1038 defaultWaveOutID = deviceID;
1039 engine.getPropertyStorage().setPropertyItem (SettingID::defaultWaveOutDevice,
1040 deviceManager.getCurrentAudioDeviceType(),
1042 rescanWaveDeviceList();
1048void DeviceManager::setDefaultWaveInDevice (
juce::String deviceID)
1050 if (defaultWaveInID != deviceID)
1052 if (
auto d = findInputDeviceForID (deviceID))
1056 defaultWaveInID = deviceID;
1057 engine.getPropertyStorage().setPropertyItem (SettingID::defaultWaveInDevice,
1058 deviceManager.getCurrentAudioDeviceType(),
1060 rescanWaveDeviceList();
1066void DeviceManager::setDefaultMidiOutDevice (
juce::String deviceID)
1068 if (defaultMidiOutID != deviceID)
1070 if (
auto d = findOutputDeviceForID (deviceID))
1074 engine.getPropertyStorage().setProperty (SettingID::defaultMidiOutDevice, deviceID);
1075 rescanMidiDeviceList();
1081void DeviceManager::setDefaultMidiInDevice (
juce::String deviceID)
1083 if (defaultMidiInID != deviceID)
1085 if (
auto d = findInputDeviceForID (deviceID))
1089 engine.getPropertyStorage().setProperty (SettingID::defaultMidiInDevice, deviceID);
1090 rescanMidiDeviceList();
1096void DeviceManager::setDeviceOutChannelStereo (
int chan,
bool isStereoPair)
1100 if (isDeviceOutChannelStereo (chan) != isStereoPair)
1102 outMonoChans.setBit (chan / 2, ! isStereoPair);
1106 const bool en = outEnabled[chan] || outEnabled[chan + 1];
1107 outEnabled.setBit (chan, en);
1108 outEnabled.setBit (chan + 1, en);
1111 rescanWaveDeviceList();
1115void DeviceManager::setDeviceInChannelStereo (
int chan,
bool isStereoPair)
1119 if (isDeviceInChannelStereo (chan) != isStereoPair)
1121 inStereoChans.setBit (chan / 2, isStereoPair);
1125 const bool en = inEnabled[chan] || inEnabled[chan + 1];
1126 inEnabled.setBit (chan, en);
1127 inEnabled.setBit (chan + 1, en);
1130 rescanWaveDeviceList();
1136 for (
auto& ci : channels)
1138 if (outEnabled[ci.indexInDevice] != b)
1140 outEnabled.setBit (ci.indexInDevice, b);
1141 rescanWaveDeviceList();
1148 for (
auto& ci : channels)
1150 if (inEnabled[ci.indexInDevice] != b)
1152 inEnabled.setBit (ci.indexInDevice, b);
1153 rescanWaveDeviceList();
1158int DeviceManager::getNumMidiInDevices()
const
1161 return (
int) midiInputs.size();
1168 if (index >= 0 && index < (
int) midiInputs.size())
1169 return midiInputs[(
size_t) index];
1184 for (
auto& m : midiInputs)
1185 if (m->getDeviceID() == deviceID)
1191void DeviceManager::broadcastStreamTimeToMidiDevices (
double timeToBroadcast)
1195 for (
auto mi : midiInputs)
1196 if (mi->isEnabled())
1197 mi->masterTimeUpdate (timeToBroadcast);
1200int DeviceManager::getNumInputDevices()
const
1202 return getNumWaveInDevices() + getNumMidiInDevices();
1205InputDevice* DeviceManager::getInputDevice (
int index)
const
1207 if (index >= getNumWaveInDevices())
1208 return getMidiInDevice (index - getNumWaveInDevices()).get();
1210 return getWaveInDevice (index);
1213int DeviceManager::getNumOutputDevices()
const
1215 return getNumWaveOutDevices() + getNumMidiOutDevices();
1218OutputDevice* DeviceManager::getOutputDeviceAt (
int index)
const
1220 if (index >= getNumWaveOutDevices())
1221 return getMidiOutDevice (index - getNumWaveOutDevices());
1223 return getWaveOutDevice (index);
1226InputDevice* DeviceManager::findInputDeviceForID (
const juce::String&
id)
const
1228 for (
auto d : waveInputs)
1229 if (d->getDeviceID() == id)
1234 for (
auto d : midiInputs)
1235 if (d->getDeviceID() == id)
1241InputDevice* DeviceManager::findInputDeviceWithName (
const juce::String& name)
const
1243 for (
auto d : waveInputs)
1249 for (
auto d : midiInputs)
1256OutputDevice* DeviceManager::findOutputDeviceForID (
const juce::String&
id)
const
1258 for (
auto d : waveOutputs)
1259 if (d->getDeviceID() == id)
1262 for (
auto d : midiOutputs)
1263 if (d->getDeviceID() == id)
1269OutputDevice* DeviceManager::findOutputDeviceWithName (
const juce::String& name)
const
1271 if (name == getDefaultAudioOutDeviceName (
false))
return getDefaultWaveOutDevice();
1272 if (name == getDefaultMidiOutDeviceName (
false))
return getDefaultMidiOutDevice();
1274 for (
auto d : waveOutputs)
1278 for (
auto d : midiOutputs)
1285int DeviceManager::getRecordAdjustmentSamples()
1287 if (
auto d = deviceManager.getCurrentAudioDevice())
1288 return d->getOutputLatencyInSamples() + d->getInputLatencyInSamples();
1293double DeviceManager::getRecordAdjustmentMs()
1295 if (
auto d = deviceManager.getCurrentAudioDevice())
1296 return getRecordAdjustmentSamples() * 1000.0 / d->getCurrentSampleRate();
1301double DeviceManager::getOutputLatencySeconds()
const
1303 return outputLatencyTime;
1306PerformanceMeasurement::Statistics DeviceManager::getCPUStatistics()
const
1308 return performanceStats.load();
1311void DeviceManager::restCPUStatistics()
1316void DeviceManager::audioDeviceIOCallbackWithContext (
const float*
const* inputChannelData,
int numInputChannels,
1317 float*
const* outputChannelData,
int totalNumOutputChannels,
1325 const ScopedSteadyLoad load (steadyLoadContext, numSamples);
1330 for (
int i = 0; i < totalNumOutputChannels; ++i)
1331 if (
auto dest = outputChannelData[i])
1332 juce::FloatVectorOperations::clear (dest, numSamples);
1339 if (numSamples <= maxBlockSize)
1341 audioDeviceIOCallbackInternal (inputChannelData, numInputChannels,
1342 outputChannelData, totalNumOutputChannels,
1347 for (
int sampleStartIndex = 0;;)
1349 const auto numThisTime =
std::min (numSamples, maxBlockSize);
1351 for (
int i = 0; i < numInputChannels; ++i)
1352 inputChannelsScratch[i] = inputChannelData[i] + sampleStartIndex;
1354 for (
int i = 0; i < totalNumOutputChannels; ++i)
1355 outputChannelsScratch[i] = outputChannelData[i] + sampleStartIndex;
1357 audioDeviceIOCallbackInternal (inputChannelsScratch, numInputChannels,
1358 outputChannelsScratch, totalNumOutputChannels,
1361 numSamples -= numThisTime;
1362 sampleStartIndex += numThisTime;
1364 if (numSamples == 0)
1369void DeviceManager::audioDeviceIOCallbackInternal (
const float*
const* inputChannelData,
int numInputChannels,
1370 float*
const* outputChannelData,
int totalNumOutputChannels,
1374 engine.getAudioFileManager().cache.nextBlockStarted();
1376 if (clearStatsFlag.exchange (
false))
1377 performanceMeasurement.getStatisticsAndReset();
1379 const ScopedPerformanceMeasurement spm (performanceMeasurement);
1381 if (currentCpuUsage > cpuLimitBeforeMuting)
1383 for (
int i = 0; i < totalNumOutputChannels; ++i)
1384 if (
auto dest = outputChannelData[i])
1385 juce::FloatVectorOperations::clear (dest, numSamples);
1387 currentCpuUsage =
std::min (0.9, currentCpuUsage * 0.99);
1391 broadcastStreamTimeToMidiDevices (streamTime + outputLatencyTime);
1395 SCOPED_REALTIME_CHECK
1398 for (
auto wi : waveInputs)
1399 wi->consumeNextAudioBlock (inputChannelData, numInputChannels, numSamples, streamTime);
1401 for (
int i = totalNumOutputChannels; --i >= 0;)
1402 if (
auto dest = outputChannelData[i])
1403 juce::FloatVectorOperations::clear (dest, numSamples);
1405 double blockLength = numSamples / currentSampleRate;
1407 blockStreamTime = { streamTime, streamTime + blockLength };
1409 for (
auto c : activeContexts)
1410 c->fillNextNodeBlock (outputChannelData, totalNumOutputChannels, numSamples);
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)
1420 streamTime = blockStreamTime.
getEnd();
1421 currentCpuUsage = deviceManager.getCpuUsage();
1424 if (globalOutputAudioProcessor !=
nullptr)
1428 globalOutputAudioProcessor->processBlock (ab, mb);
1434 bool hasClipped =
false;
1436 for (
int i = totalNumOutputChannels; --i >= 0;)
1438 if (
auto dest = outputChannelData[i])
1440 if (activeOutChannels[i])
1442 for (
int j = 0; j < numSamples; ++j)
1444 auto samp = dest[j];
1451 else if (samp > 1.0f)
1472 performanceStats.store (performanceMeasurement.getStatistics());
1478 currentCpuUsage = 0.0f;
1480 if (globalOutputAudioProcessor !=
nullptr)
1481 globalOutputAudioProcessor->prepareToPlay (device->getCurrentSampleRate(), device->getCurrentBufferSizeSamples());
1484 steadyLoadContext.setSampleRate (device->getCurrentSampleRate());
1490 prepareToStartCaller->triggerAsyncUpdate();
1493void DeviceManager::prepareToStart()
1495 if (
auto device = deviceManager.getCurrentAudioDevice())
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());
1505 inputChannelsScratch.realloc (device->getInputChannelNames().size());
1506 outputChannelsScratch.realloc (device->getOutputChannelNames().size());
1511 for (
auto c : activeContexts)
1513 const EditPlaybackContext::ScopedDeviceListReleaser rebuilder (*c,
true);
1514 c->resyncToGlobalStreamTime ({ streamTime, streamTime + device->getCurrentBufferSizeSamples() / currentSampleRate }, currentSampleRate);
1515 c->edit.restartPlayback();
1519 isSuspended =
false;
1523void DeviceManager::audioDeviceStopped()
1526 currentCpuUsage = 0.0f;
1528 if (globalOutputAudioProcessor !=
nullptr)
1529 globalOutputAudioProcessor->releaseResources();
1532void DeviceManager::updateNumCPUs()
1537 for (
auto c : activeContexts)
1542void DeviceManager::enableOutputClipping (
bool clipOutput)
1547bool DeviceManager::hasOutputClipped (
bool reset)
1551 if (hasClipped && reset)
1559 TRACKTION_ASSERT_MESSAGE_THREAD
1561 double lastStreamTime;
1565 lastStreamTime = streamTime;
1566 c->resyncToGlobalStreamTime ({ lastStreamTime, lastStreamTime + getBlockSize() / currentSampleRate }, currentSampleRate);
1567 activeContexts.addIfNotAlreadyThere (c);
1570 for (
int i = 200; --i >= 0;)
1572 juce::Thread::sleep (1);
1573 if (lastStreamTime != streamTime)
1578void DeviceManager::removeContext (EditPlaybackContext* c)
1581 activeContexts.removeAllInstancesOf (c);
1584void DeviceManager::clearAllContextDevices()
1588 for (
auto c : activeContexts)
1589 const EditPlaybackContext::ScopedDeviceListReleaser rebuilder (*c, false);
1592void DeviceManager::reloadAllContextDevices()
1596 for (
auto c : activeContexts)
1598 const EditPlaybackContext::ScopedDeviceListReleaser rebuilder (*c,
true);
1599 c->edit.restartPlayback();
1605 if (newProcessor !=
nullptr)
1606 newProcessor->prepareToPlay (getSampleRate(), getBlockSize());
1610 std::swap (globalOutputAudioProcessor, newProcessor);
1613 newProcessor.
reset();
1616DeviceManager::DeviceManager (
Engine& e) : engine (e)
1624 gDeviceManager = &deviceManager;
1627DeviceManager::~DeviceManager()
1629 gDeviceManager =
nullptr;
1635void DeviceManager::closeDevices()
1638 TRACKTION_ASSERT_MESSAGE_THREAD
1640 lastMIDIDeviceList.reset();
1641 lastAvailableWaveDeviceList.reset();
1643 jassert (activeContexts.isEmpty());
1644 clearAllContextDevices();
1648 midiOutputs.clear();
1656 waveOutputs.clear();
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()
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
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 ...
#define TRANS(stringLiteral)
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.
Describes a WaveDevice from which the WaveOutputDevice and WaveInputDevice lists will be built.
#define CRASH_TRACER
This macro adds the current location to a stack which gets logged if a crash happens.