From d3e7c0047b52b2edc8213cb30dd23148cf9de98b Mon Sep 17 00:00:00 2001 From: Peter Sobot Date: Thu, 7 Mar 2024 16:13:13 -0500 Subject: [PATCH] Report current time in samples to plugins. --- pedalboard/ExternalPlugin.h | 42 ++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/pedalboard/ExternalPlugin.h b/pedalboard/ExternalPlugin.h index b3a5942f..e9ab9595 100644 --- a/pedalboard/ExternalPlugin.h +++ b/pedalboard/ExternalPlugin.h @@ -471,7 +471,7 @@ class AbstractExternalPlugin : public Plugin { }; template -class ExternalPlugin : public AbstractExternalPlugin { +class ExternalPlugin : public AbstractExternalPlugin, juce::AudioPlayHead { public: ExternalPlugin( std::string &_pathToPluginFile, @@ -599,6 +599,10 @@ class ExternalPlugin : public AbstractExternalPlugin { ~ExternalPlugin() { { std::lock_guard lock(EXTERNAL_PLUGIN_MUTEX); + if (pluginInstance) { + pluginInstance->setPlayHead(nullptr); + } + pluginInstance.reset(); NUM_ACTIVE_EXTERNAL_PLUGINS--; @@ -664,6 +668,9 @@ class ExternalPlugin : public AbstractExternalPlugin { { std::lock_guard lock(EXTERNAL_PLUGIN_MUTEX); // Delete the plugin instance itself: + if (pluginInstance) { + pluginInstance->setPlayHead(nullptr); + } pluginInstance.reset(); NUM_ACTIVE_EXTERNAL_PLUGINS--; } @@ -683,6 +690,7 @@ class ExternalPlugin : public AbstractExternalPlugin { loadError.toStdString()); } + pluginInstance->setPlayHead(this); pluginInstance->enableAllBuses(); auto mainInputBus = pluginInstance->getBus(true, 0); @@ -692,6 +700,9 @@ class ExternalPlugin : public AbstractExternalPlugin { auto exception = std::invalid_argument( "Plugin '" + pluginInstance->getName().toStdString() + "' does not produce audio output."); + if (pluginInstance) { + pluginInstance->setPlayHead(nullptr); + } pluginInstance.reset(); throw exception; } @@ -710,6 +721,7 @@ class ExternalPlugin : public AbstractExternalPlugin { pathToPluginFile.toStdString() + ": " + loadError.toStdString()); } + pluginInstance->setPlayHead(this); } } @@ -881,7 +893,10 @@ class ExternalPlugin : public AbstractExternalPlugin { juce::AudioBuffer audioBuffer(numOutputChannels, bufferSize); audioBuffer.clear(); + currentPositionInfo.isPlaying = true; pluginInstance->processBlock(audioBuffer, emptyNoteBuffer); + currentPositionInfo.isPlaying = false; + currentPositionInfo.timeInSamples += bufferSize; auto noiseFloor = audioBuffer.getMagnitude(0, bufferSize); audioBuffer.clear(); @@ -891,7 +906,10 @@ class ExternalPlugin : public AbstractExternalPlugin { // the messages in a MidiBuffer get erased every time we call processBlock! { juce::MidiBuffer noteOnBuffer(noteOn); + currentPositionInfo.isPlaying = true; pluginInstance->processBlock(audioBuffer, noteOnBuffer); + currentPositionInfo.isPlaying = false; + currentPositionInfo.timeInSamples += bufferSize; } // Then keep pumping the message thread until we get some louder output: @@ -914,8 +932,11 @@ class ExternalPlugin : public AbstractExternalPlugin { audioBuffer.clear(); { + currentPositionInfo.isPlaying = true; juce::MidiBuffer noteOnBuffer(noteOn); pluginInstance->processBlock(audioBuffer, noteOnBuffer); + currentPositionInfo.isPlaying = false; + currentPositionInfo.timeInSamples += bufferSize; } if (juce::Time::currentTimeMillis() >= endTime) @@ -927,8 +948,12 @@ class ExternalPlugin : public AbstractExternalPlugin { audioBuffer.clear(); { juce::MidiBuffer allNotesOffBuffer(allNotesOff); + currentPositionInfo.isPlaying = true; pluginInstance->processBlock(audioBuffer, allNotesOffBuffer); + currentPositionInfo.isPlaying = false; + currentPositionInfo.timeInSamples += bufferSize; } + currentPositionInfo.timeInSamples = 0; pluginInstance->reset(); pluginInstance->releaseResources(); @@ -1046,6 +1071,7 @@ class ExternalPlugin : public AbstractExternalPlugin { // Force prepare() to be called again later by invalidating lastSpec: lastSpec.maximumBlockSize = 0; samplesProvided = 0; + currentPositionInfo.timeInSamples = 0; } } @@ -1071,6 +1097,8 @@ class ExternalPlugin : public AbstractExternalPlugin { pluginInstance->setNonRealtime(true); pluginInstance->prepareToPlay(spec.sampleRate, spec.maximumBlockSize); + currentPositionInfo.timeInSamples = 0; + currentPositionInfo.isPlaying = false; lastSpec = spec; } @@ -1142,8 +1170,11 @@ class ExternalPlugin : public AbstractExternalPlugin { channelPointers.size(), outputBlock.getNumSamples()); + currentPositionInfo.isPlaying = true; pluginInstance->processBlock(audioBuffer, emptyMidiBuffer); + currentPositionInfo.isPlaying = false; samplesProvided += outputBlock.getNumSamples(); + currentPositionInfo.timeInSamples += outputBlock.getNumSamples(); // To compensate for any latency added by the plugin, // only tell Pedalboard to use the last _n_ samples. @@ -1238,7 +1269,10 @@ class ExternalPlugin : public AbstractExternalPlugin { juce::MidiBuffer midiChunk; midiChunk.addEvents(midiInputBuffer, i, chunkSampleCount, -i); + currentPositionInfo.isPlaying = true; pluginInstance->processBlock(audioChunk, midiChunk); + currentPositionInfo.isPlaying = false; + currentPositionInfo.timeInSamples += chunkSampleCount; } } @@ -1302,6 +1336,11 @@ class ExternalPlugin : public AbstractExternalPlugin { ExternalPluginReloadType reloadType = ExternalPluginReloadType::Unknown; + bool getCurrentPosition(CurrentPositionInfo &result) override { + result = currentPositionInfo; + return true; + } + private: constexpr static int ExternalLoadSampleRate = 44100, ExternalLoadMaximumBlockSize = 8192; @@ -1309,6 +1348,7 @@ class ExternalPlugin : public AbstractExternalPlugin { juce::PluginDescription foundPluginDescription; juce::AudioPluginFormatManager pluginFormatManager; std::unique_ptr pluginInstance; + juce::AudioPlayHead::CurrentPositionInfo currentPositionInfo; long samplesProvided = 0; float initializationTimeout = DEFAULT_INITIALIZATION_TIMEOUT_SECONDS;