diff --git a/src/audioCore/ara/ARAChangeListener.cpp b/src/audioCore/ara/ARAChangeListener.cpp new file mode 100644 index 0000000..dd8d881 --- /dev/null +++ b/src/audioCore/ara/ARAChangeListener.cpp @@ -0,0 +1,9 @@ +#include "ARAChangeListener.h" +#include "ARAVirtualDocument.h" + +ARAChangeListener::ARAChangeListener(ARAVirtualDocument* document) + : document(document) {} + +void ARAChangeListener::changeListenerCallback(juce::ChangeBroadcaster* source) { + this->document->update(); +} diff --git a/src/audioCore/ara/ARAChangeListener.h b/src/audioCore/ara/ARAChangeListener.h new file mode 100644 index 0000000..29a97c2 --- /dev/null +++ b/src/audioCore/ara/ARAChangeListener.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +class ARAVirtualDocument; + +class ARAChangeListener : public juce::ChangeListener { +public: + ARAChangeListener(ARAVirtualDocument* document); + + void changeListenerCallback(juce::ChangeBroadcaster* source) override; + +private: + ARAVirtualDocument* const document; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ARAChangeListener) +}; diff --git a/src/audioCore/misc/ARAController.cpp b/src/audioCore/ara/ARAController.cpp similarity index 100% rename from src/audioCore/misc/ARAController.cpp rename to src/audioCore/ara/ARAController.cpp diff --git a/src/audioCore/misc/ARAController.h b/src/audioCore/ara/ARAController.h similarity index 100% rename from src/audioCore/misc/ARAController.h rename to src/audioCore/ara/ARAController.h diff --git a/src/audioCore/misc/ARAObjects.cpp b/src/audioCore/ara/ARAObjects.cpp similarity index 100% rename from src/audioCore/misc/ARAObjects.cpp rename to src/audioCore/ara/ARAObjects.cpp diff --git a/src/audioCore/misc/ARAObjects.h b/src/audioCore/ara/ARAObjects.h similarity index 100% rename from src/audioCore/misc/ARAObjects.h rename to src/audioCore/ara/ARAObjects.h diff --git a/src/audioCore/misc/ARASharedObject.h b/src/audioCore/ara/ARASharedObject.h similarity index 100% rename from src/audioCore/misc/ARASharedObject.h rename to src/audioCore/ara/ARASharedObject.h diff --git a/src/audioCore/misc/ARAVirtualDocument.cpp b/src/audioCore/ara/ARAVirtualDocument.cpp similarity index 79% rename from src/audioCore/misc/ARAVirtualDocument.cpp rename to src/audioCore/ara/ARAVirtualDocument.cpp index 574e039..ef507e3 100644 --- a/src/audioCore/misc/ARAVirtualDocument.cpp +++ b/src/audioCore/ara/ARAVirtualDocument.cpp @@ -7,15 +7,20 @@ ARAVirtualDocument::ARAVirtualDocument( juce::ARAHostModel::PlaybackRendererInterface& araPlaybackRenderer, const PluginOnOffFunc& pluginOnOff) : seq(seq), controller(controller), pluginOnOff(pluginOnOff), - araEditorRenderer(araEditorRenderer), araPlaybackRenderer(araPlaybackRenderer) {} + araEditorRenderer(araEditorRenderer), araPlaybackRenderer(araPlaybackRenderer) { + this->listener = std::make_unique(this); +} ARAVirtualDocument::~ARAVirtualDocument() { this->clear(); } void ARAVirtualDocument::update() { + /** Invoke This On Message Thread */ + JUCE_ASSERT_MESSAGE_THREAD + /** Turn Off Plugin */ - this->pluginOnOff(false); + this->lockPlugin(true); /** Lock Document */ juce::ARAEditGuard locker(this->controller); @@ -47,12 +52,12 @@ void ARAVirtualDocument::update() { this->addRegionToRenderer(); /** Turn On Plugin */ - this->pluginOnOff(true); + this->lockPlugin(false); } void ARAVirtualDocument::clear() { /** Turn Off Plugin */ - this->pluginOnOff(false); + this->lockPlugin(true); /** Lock Document */ juce::ARAEditGuard locker(this->controller); @@ -61,7 +66,11 @@ void ARAVirtualDocument::clear() { this->clearUnsafe(); /** Turn On Plugin */ - this->pluginOnOff(true); + this->lockPlugin(false); +} + +juce::ChangeListener* ARAVirtualDocument::getListener() const { + return this->listener.get(); } void ARAVirtualDocument::clearUnsafe() { @@ -97,3 +106,20 @@ void ARAVirtualDocument::addRegionToRenderer() { } } } + +void ARAVirtualDocument::lockPlugin(bool locked) { + juce::GenericScopedLock locker(this->pluginLockMutex); + + if (locked) { + if (this->pluginLockCount == 0) { + this->pluginOnOff(false); + } + this->pluginLockCount++; + } + else { + this->pluginLockCount--; + if (this->pluginLockCount == 0) { + this->pluginOnOff(true); + } + } +} diff --git a/src/audioCore/misc/ARAVirtualDocument.h b/src/audioCore/ara/ARAVirtualDocument.h similarity index 85% rename from src/audioCore/misc/ARAVirtualDocument.h rename to src/audioCore/ara/ARAVirtualDocument.h index 7e72c84..847b8a8 100644 --- a/src/audioCore/misc/ARAVirtualDocument.h +++ b/src/audioCore/ara/ARAVirtualDocument.h @@ -2,6 +2,7 @@ #include #include "ARAObjects.h" +#include "ARAChangeListener.h" class SeqSourceProcessor; @@ -20,12 +21,16 @@ class ARAVirtualDocument { void update(); void clear(); + juce::ChangeListener* getListener() const; + private: SeqSourceProcessor* const seq = nullptr; ARA::Host::DocumentController& controller; juce::ARAHostModel::EditorRendererInterface& araEditorRenderer; juce::ARAHostModel::PlaybackRendererInterface& araPlaybackRenderer; const PluginOnOffFunc pluginOnOff; + int pluginLockCount = 0; + juce::SpinLock pluginLockMutex; std::unique_ptr audioSource = nullptr; std::unique_ptr audioModification = nullptr; @@ -33,9 +38,13 @@ class ARAVirtualDocument { std::unique_ptr regionSequence = nullptr; std::unique_ptr playbackRegion = nullptr; + std::unique_ptr listener = nullptr; + void clearUnsafe(); void removeRegionToRenderer(); void addRegionToRenderer(); + void lockPlugin(bool locked); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ARAVirtualDocument) }; diff --git a/src/audioCore/graph/PluginDecorator.cpp b/src/audioCore/graph/PluginDecorator.cpp index 1ceb279..4ab5b09 100644 --- a/src/audioCore/graph/PluginDecorator.cpp +++ b/src/audioCore/graph/PluginDecorator.cpp @@ -4,7 +4,7 @@ #include "../uiCallback/UICallback.h" #include "../misc/AudioLock.h" #include "../misc/VMath.h" -#include "../misc/ARAController.h" +#include "../ara/ARAController.h" #include "../AudioCore.h" #include "../Utils.h" #include @@ -25,6 +25,9 @@ PluginDecorator::PluginDecorator(SeqSourceProcessor* seq, for (int i = 0; i < this->paramCCList.size(); i++) { this->paramCCList[i] = -1; } + + /** ARA Change Broadcaster */ + this->araChangeBroadcaster = std::make_unique(); } PluginDecorator::PluginDecorator(std::unique_ptr plugin, @@ -95,8 +98,8 @@ void PluginDecorator::setPlugin( else { this->plugin->setPlayHead(this->getPlayHead()); this->plugin->setNonRealtime(this->isNonRealtime()); - this->plugin->prepareToPlay(this->getSampleRate(), this->getBlockSize()); - this->pluginPrepared = true; + this->pluginOnOffInternal(true, + this->getSampleRate(), this->getBlockSize()); //this->updatePluginBuses(); @@ -137,15 +140,8 @@ void PluginDecorator::setARA( this->araPlaybackRenderer = pluginInstance.getPlaybackRendererInterface(); /** Plugin On Off */ - auto pluginOnOffFunc = [plugin = this->plugin.get()](bool on) { - if (plugin) { - if (on) { - plugin->prepareToPlay(plugin->getSampleRate(), plugin->getBlockSize()); - } - else { - plugin->releaseResources(); - } - } + auto pluginOnOffFunc = [this](bool on) { + this->pluginOnOffInternal(on, this->getSampleRate(), this->getBlockSize()); }; /** Create ARA Virtual Document */ @@ -158,14 +154,18 @@ void PluginDecorator::setARA( /** Prepare Plugin */ this->plugin->setPlayHead(this->getPlayHead()); this->plugin->setNonRealtime(this->isNonRealtime()); - this->plugin->prepareToPlay(this->getSampleRate(), this->getBlockSize()); - this->pluginPrepared = true; + this->pluginOnOffInternal(true, + this->getSampleRate(), this->getBlockSize()); //this->updatePluginBuses(); /** Prepare ARA Document */ - if (this->araDocumentController) { + if (this->araVirtualDocument) { this->araVirtualDocument->update(); + + /** Add Change Listener */ + this->araChangeBroadcaster->addChangeListener( + this->araVirtualDocument->getListener()); } /** Callback */ @@ -183,8 +183,8 @@ void PluginDecorator::handleARALoadError(const juce::String& pluginIdentifier) { /** Prepare Plugin */ this->plugin->setPlayHead(this->getPlayHead()); this->plugin->setNonRealtime(this->isNonRealtime()); - this->plugin->prepareToPlay(this->getSampleRate(), this->getBlockSize()); - this->pluginPrepared = true; + this->pluginOnOffInternal(true, + this->getSampleRate(), this->getBlockSize()); //this->updatePluginBuses(); @@ -395,6 +395,41 @@ void PluginDecorator::clearMIDICCListener() { this->ccListener = MIDICCListener{}; } +void PluginDecorator::invokeARADocumentChange() { + /** + * TODO Make the Celemony Melodyne happy. + * Celemony Melodyne GUI make the host crashed when ARA host document changed. + * Solve this later. + */ + + this->pluginOnOffInternal(false, + this->getSampleRate(), this->getBlockSize()); + + this->araChangeBroadcaster->sendSynchronousChangeMessage(); + //if (this->araDocumentController) { + // /** Plugin On Off */ + // auto pluginOnOffFunc = [this](bool on) { + // this->pluginOnOffInternal(on, this->getSampleRate(), this->getBlockSize()); + // }; + + // /** Create ARA Virtual Document */ + // this->araVirtualDocument = std::make_unique( + // this->seq, this->araDocumentController->getDocumentController(), + // this->araEditorRenderer, this->araPlaybackRenderer, pluginOnOffFunc); + + // if (this->araVirtualDocument) { + // this->araVirtualDocument->update(); + + // /** Add Change Listener */ + // this->araChangeBroadcaster->addChangeListener( + // this->araVirtualDocument->getListener()); + // } + //} + + this->pluginOnOffInternal(true, + this->getSampleRate(), this->getBlockSize()); +} + const juce::String PluginDecorator::getName() const { if (!this->plugin) { return ""; } return this->plugin->getName() + ((bool)this->araDocumentController ? "(ARA)" : ""); @@ -418,14 +453,15 @@ void PluginDecorator::prepareToPlay( this->doubleBuffer = std::make_unique>(channels, this->getBlockSize()); } - this->plugin->prepareToPlay(sampleRate, maximumExpectedSamplesPerBlock); - this->pluginPrepared = true; + this->pluginOnOffInternal(true, + this->getSampleRate(), this->getBlockSize()); } void PluginDecorator::releaseResources() { if (!this->plugin) { return; } - this->pluginPrepared = false; - this->plugin->releaseResources(); + + this->pluginOnOffInternal(false, + this->getSampleRate(), this->getBlockSize()); } void PluginDecorator::memoryWarningReceived() { @@ -828,3 +864,36 @@ void PluginDecorator::updateBuffer() { } } } + +void PluginDecorator::pluginOnOffInternal(bool shouldOn, double sampleRate, int blockSize) { + /** Try to fix Celemony Melodyne crash but this not works */ + /*juce::GenericScopedLock locker(this->pluginOnOffMutex); + + if (shouldOn) { + if (this->pluginOnOffCount == 0 && plugin) { + plugin->prepareToPlay(sampleRate, blockSize); + this->pluginPrepared = true; + } + this->pluginOnOffCount++; + } + else { + this->pluginOnOffCount--; + if (this->pluginOnOffCount == 0 && plugin) { + this->pluginPrepared = false; + plugin->releaseResources(); + } + }*/ + + if (shouldOn) { + if (plugin) { + plugin->prepareToPlay(sampleRate, blockSize); + this->pluginPrepared = true; + } + } + else { + if (plugin) { + this->pluginPrepared = false; + plugin->releaseResources(); + } + } +} diff --git a/src/audioCore/graph/PluginDecorator.h b/src/audioCore/graph/PluginDecorator.h index 1fed318..9c26567 100644 --- a/src/audioCore/graph/PluginDecorator.h +++ b/src/audioCore/graph/PluginDecorator.h @@ -2,7 +2,7 @@ #include #include "../project/Serializable.h" -#include "../misc/ARAVirtualDocument.h" +#include "../ara/ARAVirtualDocument.h" class SeqSourceProcessor; @@ -57,6 +57,8 @@ class PluginDecorator final : public juce::AudioProcessor, void setMIDICCListener(const MIDICCListener& listener); void clearMIDICCListener(); + void invokeARADocumentChange(); + class SafePointer { private: juce::WeakReference weakRef; @@ -163,6 +165,10 @@ class PluginDecorator final : public juce::AudioProcessor, juce::ARAHostModel::EditorRendererInterface araEditorRenderer; juce::ARAHostModel::PlaybackRendererInterface araPlaybackRenderer; std::unique_ptr araVirtualDocument = nullptr; + std::unique_ptr araChangeBroadcaster = nullptr; + + int pluginOnOffCount = 0; + juce::SpinLock pluginOnOffMutex; void numChannelsChanged() override; void numBusesChanged() override; @@ -175,6 +181,8 @@ class PluginDecorator final : public juce::AudioProcessor, void updateBuffer(); + void pluginOnOffInternal(bool shouldOn, double sampleRate, int blockSize); + JUCE_DECLARE_WEAK_REFERENCEABLE(PluginDecorator) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginDecorator) }; diff --git a/src/audioCore/graph/SeqSourceProcessor.cpp b/src/audioCore/graph/SeqSourceProcessor.cpp index d76f9c5..5a8128e 100644 --- a/src/audioCore/graph/SeqSourceProcessor.cpp +++ b/src/audioCore/graph/SeqSourceProcessor.cpp @@ -99,6 +99,11 @@ int SeqSourceProcessor::resetSeqTime( void SeqSourceProcessor::setTrackName(const juce::String& name) { this->trackName = name; + /** ARA Change */ + if (auto plugin = this->getInstrProcessor()) { + plugin->invokeARADocumentChange(); + } + /** Callback */ UICallbackAPI::invoke(UICallbackType::SeqChanged, this->index); } @@ -110,6 +115,11 @@ const juce::String SeqSourceProcessor::getTrackName() const { void SeqSourceProcessor::setTrackColor(const juce::Colour& color) { this->trackColor = color; + /** ARA Change */ + if (auto plugin = this->getInstrProcessor()) { + plugin->invokeARADocumentChange(); + } + /** Callback */ UICallbackAPI::invoke(UICallbackType::SeqChanged, this->index); } @@ -291,11 +301,22 @@ void SeqSourceProcessor::applyAudio() { /** Callback */ auto callback = [ptr = SafePointer{ this }] { if (ptr) { + /** ARA Change */ + if (auto plugin = ptr->getInstrProcessor()) { + plugin->invokeARADocumentChange(); + } + + /** UI Change */ ptr->invokeDataCallbacks(); } }; SourceManager::getInstance()->setCallback(this->audioSourceRef, SourceManager::SourceType::Audio, callback); + + /** ARA Change */ + if (auto plugin = this->getInstrProcessor()) { + plugin->invokeARADocumentChange(); + } } void SeqSourceProcessor::applyMIDI() { @@ -308,11 +329,22 @@ void SeqSourceProcessor::applyMIDI() { /** Callback */ auto callback = [ptr = SafePointer{ this }] { if (ptr) { + /** ARA Change */ + if (auto plugin = ptr->getInstrProcessor()) { + plugin->invokeARADocumentChange(); + } + + /** UI Change */ ptr->invokeDataCallbacks(); } }; SourceManager::getInstance()->setCallback(this->midiSourceRef, SourceManager::SourceType::MIDI, callback); + + /** ARA Change */ + if (auto plugin = this->getInstrProcessor()) { + plugin->invokeARADocumentChange(); + } } void SeqSourceProcessor::releaseAudio() { @@ -321,6 +353,11 @@ void SeqSourceProcessor::releaseAudio() { SourceManager::getInstance()->releaseSource(this->audioSourceRef); this->audioSourceRef = 0; } + + /** ARA Change */ + if (auto plugin = this->getInstrProcessor()) { + plugin->invokeARADocumentChange(); + } } void SeqSourceProcessor::releaseMIDI() { @@ -330,6 +367,11 @@ void SeqSourceProcessor::releaseMIDI() { this->midiSourceRef = 0; } this->currentMIDITrack = 0; + + /** ARA Change */ + if (auto plugin = this->getInstrProcessor()) { + plugin->invokeARADocumentChange(); + } } void SeqSourceProcessor::applyAudioIfNeed() { @@ -350,11 +392,21 @@ void SeqSourceProcessor::initAudio(double sampleRate, double length) { this->applyAudio(); SourceManager::getInstance()->initAudio(this->audioSourceRef, this->audioChannels.size(), sampleRate, length); + + /** ARA Change */ + if (auto plugin = this->getInstrProcessor()) { + plugin->invokeARADocumentChange(); + } } void SeqSourceProcessor::initMIDI() { this->applyMIDI(); SourceManager::getInstance()->initMIDI(this->midiSourceRef); + + /** ARA Change */ + if (auto plugin = this->getInstrProcessor()) { + plugin->invokeARADocumentChange(); + } } const juce::String SeqSourceProcessor::getAudioFileName() const { @@ -408,6 +460,11 @@ const juce::String SeqSourceProcessor::getMIDIName() const { void SeqSourceProcessor::setCurrentMIDITrack(int trackIndex) { this->currentMIDITrack = trackIndex; + /** ARA Change */ + if (auto plugin = this->getInstrProcessor()) { + plugin->invokeARADocumentChange(); + } + /** Callback */ UICallbackAPI::invoke(UICallbackType::SeqDataRefChanged, this->index); } diff --git a/src/audioCore/source/SourceItem.cpp b/src/audioCore/source/SourceItem.cpp index aa60bf4..9c675ef 100644 --- a/src/audioCore/source/SourceItem.cpp +++ b/src/audioCore/source/SourceItem.cpp @@ -282,7 +282,7 @@ void SourceItem::setCallback(const ChangedCallback& callback) { void SourceItem::invokeCallback() const { if (this->callback) { - this->callback(); + juce::MessageManager::callAsync(this->callback); } }