| @@ -0,0 +1,303 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE 7 technical preview. | |||
| Copyright (c) 2022 - Raw Material Software Limited | |||
| You may use this code under the terms of the GPL v3 | |||
| (see www.gnu.org/licenses). | |||
| For the technical preview this file cannot be licensed commercially. | |||
| JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
| EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
| DISCLAIMED. | |||
| ============================================================================== | |||
| */ | |||
| namespace juce | |||
| { | |||
| ARAAudioSourceReader::ARAAudioSourceReader (ARAAudioSource* audioSource) | |||
| : AudioFormatReader (nullptr, "ARAAudioSourceReader"), | |||
| audioSourceBeingRead (audioSource) | |||
| { | |||
| jassert (audioSourceBeingRead != nullptr); | |||
| bitsPerSample = 32; | |||
| usesFloatingPointData = true; | |||
| sampleRate = audioSourceBeingRead->getSampleRate(); | |||
| numChannels = (unsigned int) audioSourceBeingRead->getChannelCount(); | |||
| lengthInSamples = audioSourceBeingRead->getSampleCount(); | |||
| tmpPtrs.resize (numChannels); | |||
| audioSourceBeingRead->addListener (this); | |||
| if (audioSourceBeingRead->isSampleAccessEnabled()) | |||
| hostReader.reset (new ARA::PlugIn::HostAudioReader (audioSourceBeingRead)); | |||
| } | |||
| ARAAudioSourceReader::~ARAAudioSourceReader() | |||
| { | |||
| invalidate(); | |||
| } | |||
| void ARAAudioSourceReader::invalidate() | |||
| { | |||
| ScopedWriteLock scopedLock (lock); | |||
| if (! isValid()) | |||
| return; | |||
| hostReader.reset(); | |||
| audioSourceBeingRead->removeListener (this); | |||
| audioSourceBeingRead = nullptr; | |||
| } | |||
| void ARAAudioSourceReader::willUpdateAudioSourceProperties (ARAAudioSource* audioSource, | |||
| ARAAudioSource::PropertiesPtr newProperties) | |||
| { | |||
| if (audioSource->getSampleCount() != newProperties->sampleCount | |||
| || audioSource->getSampleRate() != newProperties->sampleRate | |||
| || audioSource->getChannelCount() != newProperties->channelCount) | |||
| { | |||
| invalidate(); | |||
| } | |||
| } | |||
| void ARAAudioSourceReader::doUpdateAudioSourceContent (ARAAudioSource* audioSource, | |||
| ARAContentUpdateScopes scopeFlags) | |||
| { | |||
| jassertquiet (audioSourceBeingRead == audioSource); | |||
| // Don't invalidate if the audio signal is unchanged | |||
| if (scopeFlags.affectSamples()) | |||
| invalidate(); | |||
| } | |||
| void ARAAudioSourceReader::willEnableAudioSourceSamplesAccess (ARAAudioSource* audioSource, bool enable) | |||
| { | |||
| jassertquiet (audioSourceBeingRead == audioSource); | |||
| // Invalidate our reader if sample access is disabled | |||
| if (! enable) | |||
| { | |||
| ScopedWriteLock scopedLock (lock); | |||
| hostReader.reset(); | |||
| } | |||
| } | |||
| void ARAAudioSourceReader::didEnableAudioSourceSamplesAccess (ARAAudioSource* audioSource, bool enable) | |||
| { | |||
| jassertquiet (audioSourceBeingRead == audioSource); | |||
| // Recreate our reader if sample access is enabled | |||
| if (enable && isValid()) | |||
| { | |||
| ScopedWriteLock scopedLock (lock); | |||
| hostReader.reset (new ARA::PlugIn::HostAudioReader (audioSourceBeingRead)); | |||
| } | |||
| } | |||
| void ARAAudioSourceReader::willDestroyAudioSource (ARAAudioSource* audioSource) | |||
| { | |||
| jassertquiet (audioSourceBeingRead == audioSource); | |||
| invalidate(); | |||
| } | |||
| bool ARAAudioSourceReader::readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, | |||
| int64 startSampleInFile, int numSamples) | |||
| { | |||
| const auto destSize = (bitsPerSample / 8) * (size_t) numSamples; | |||
| const auto bufferOffset = (int) (bitsPerSample / 8) * startOffsetInDestBuffer; | |||
| if (isValid() && hostReader != nullptr) | |||
| { | |||
| const ScopedTryReadLock readLock (lock); | |||
| if (readLock.isLocked()) | |||
| { | |||
| for (size_t i = 0; i < tmpPtrs.size(); ++i) | |||
| { | |||
| if ((i < (size_t) numDestChannels) && (destSamples[i] != nullptr)) | |||
| { | |||
| tmpPtrs[i] = ((uint8_t*) destSamples[i]) + bufferOffset; | |||
| } | |||
| else | |||
| { | |||
| // We need to provide destination pointers for all channels in the ARA read call, even if | |||
| // readSamples is not reading all of them. Hence we use this dummy buffer to pad the read | |||
| // destination area. | |||
| static thread_local std::vector<uint8_t> dummyBuffer; | |||
| if (destSize > dummyBuffer.size()) | |||
| dummyBuffer.resize (destSize); | |||
| tmpPtrs[i] = dummyBuffer.data(); | |||
| } | |||
| } | |||
| return hostReader->readAudioSamples (startSampleInFile, numSamples, tmpPtrs.data()); | |||
| } | |||
| } | |||
| for (int i = 0; i < numDestChannels; ++i) | |||
| if (destSamples[i] != nullptr) | |||
| zeromem (((uint8_t*) destSamples[i]) + bufferOffset, destSize); | |||
| return false; | |||
| } | |||
| //============================================================================== | |||
| ARAPlaybackRegionReader::ARAPlaybackRegionReader (ARAPlaybackRegion* playbackRegion) | |||
| : ARAPlaybackRegionReader (playbackRegion->getAudioModification()->getAudioSource()->getSampleRate(), | |||
| playbackRegion->getAudioModification()->getAudioSource()->getChannelCount(), | |||
| { playbackRegion }) | |||
| {} | |||
| ARAPlaybackRegionReader::ARAPlaybackRegionReader (double rate, int numChans, | |||
| const std::vector<ARAPlaybackRegion*>& playbackRegions) | |||
| : AudioFormatReader (nullptr, "ARAPlaybackRegionReader") | |||
| { | |||
| // We're only providing the minimal set of meaningful values, since the ARA renderer should only | |||
| // look at the time position and the playing state, and read any related tempo or bar signature | |||
| // information from the ARA model directly (MusicalContext). | |||
| positionInfo.resetToDefault(); | |||
| positionInfo.isPlaying = true; | |||
| sampleRate = rate; | |||
| numChannels = (unsigned int) numChans; | |||
| bitsPerSample = 32; | |||
| usesFloatingPointData = true; | |||
| auto* documentController = (! playbackRegions.empty()) | |||
| ? playbackRegions.front()->getDocumentController<ARADocumentController>() | |||
| : nullptr; | |||
| playbackRenderer.reset (documentController ? static_cast<ARAPlaybackRenderer*> (documentController->doCreatePlaybackRenderer()) | |||
| : nullptr); | |||
| if (playbackRenderer != nullptr) | |||
| { | |||
| double regionsStartTime = std::numeric_limits<double>::max(); | |||
| double regionsEndTime = std::numeric_limits<double>::lowest(); | |||
| for (const auto& playbackRegion : playbackRegions) | |||
| { | |||
| jassert (playbackRegion->getDocumentController() == documentController); | |||
| auto playbackRegionTimeRange = playbackRegion->getTimeRange (ARAPlaybackRegion::IncludeHeadAndTail::yes); | |||
| regionsStartTime = jmin (regionsStartTime, playbackRegionTimeRange.getStart()); | |||
| regionsEndTime = jmax (regionsEndTime, playbackRegionTimeRange.getEnd()); | |||
| playbackRenderer->addPlaybackRegion (ARA::PlugIn::toRef (playbackRegion)); | |||
| playbackRegion->addListener (this); | |||
| } | |||
| startInSamples = (int64) (regionsStartTime * sampleRate + 0.5); | |||
| lengthInSamples = (int64) ((regionsEndTime - regionsStartTime) * sampleRate + 0.5); | |||
| playbackRenderer->prepareToPlay (rate, | |||
| maximumBlockSize, | |||
| numChans, | |||
| AudioProcessor::ProcessingPrecision::singlePrecision, | |||
| ARARenderer::AlwaysNonRealtime::yes); | |||
| } | |||
| else | |||
| { | |||
| startInSamples = 0; | |||
| lengthInSamples = 0; | |||
| } | |||
| } | |||
| ARAPlaybackRegionReader::~ARAPlaybackRegionReader() | |||
| { | |||
| invalidate(); | |||
| } | |||
| void ARAPlaybackRegionReader::invalidate() | |||
| { | |||
| ScopedWriteLock scopedWrite (lock); | |||
| if (! isValid()) | |||
| return; | |||
| for (auto& playbackRegion : playbackRenderer->getPlaybackRegions()) | |||
| playbackRegion->removeListener (this); | |||
| playbackRenderer->releaseResources(); | |||
| playbackRenderer.reset(); | |||
| } | |||
| bool ARAPlaybackRegionReader::readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, | |||
| int64 startSampleInFile, int numSamples) | |||
| { | |||
| bool success = false; | |||
| bool needClearSamples = true; | |||
| const ScopedTryReadLock readLock (lock); | |||
| if (readLock.isLocked()) | |||
| { | |||
| if (isValid()) | |||
| { | |||
| success = true; | |||
| needClearSamples = false; | |||
| positionInfo.timeInSamples = startSampleInFile + startInSamples; | |||
| while (numSamples > 0) | |||
| { | |||
| const int numSliceSamples = jmin (numSamples, maximumBlockSize); | |||
| AudioBuffer<float> buffer ((float **) destSamples, numDestChannels, startOffsetInDestBuffer, numSliceSamples); | |||
| positionInfo.timeInSeconds = static_cast<double> (positionInfo.timeInSamples) / sampleRate; | |||
| success &= playbackRenderer->processBlock (buffer, AudioProcessor::Realtime::no, positionInfo); | |||
| numSamples -= numSliceSamples; | |||
| startOffsetInDestBuffer += numSliceSamples; | |||
| positionInfo.timeInSamples += numSliceSamples; | |||
| } | |||
| } | |||
| } | |||
| if (needClearSamples) | |||
| for (int chan_i = 0; chan_i < numDestChannels; ++chan_i) | |||
| FloatVectorOperations::clear ((float *) destSamples[chan_i], numSamples); | |||
| return success; | |||
| } | |||
| void ARAPlaybackRegionReader::willUpdatePlaybackRegionProperties (ARAPlaybackRegion* playbackRegion, ARAPlaybackRegion::PropertiesPtr newProperties) | |||
| { | |||
| jassert (ARA::contains (playbackRenderer->getPlaybackRegions(), playbackRegion)); | |||
| if ((playbackRegion->getStartInAudioModificationTime() != newProperties->startInModificationTime) | |||
| || (playbackRegion->getDurationInAudioModificationTime() != newProperties->durationInModificationTime) | |||
| || (playbackRegion->getStartInPlaybackTime() != newProperties->startInPlaybackTime) | |||
| || (playbackRegion->getDurationInPlaybackTime() != newProperties->durationInPlaybackTime) | |||
| || (playbackRegion->isTimestretchEnabled() != ((newProperties->transformationFlags & ARA::kARAPlaybackTransformationTimestretch) != 0)) | |||
| || (playbackRegion->isTimeStretchReflectingTempo() != ((newProperties->transformationFlags & ARA::kARAPlaybackTransformationTimestretchReflectingTempo) != 0)) | |||
| || (playbackRegion->hasContentBasedFadeAtHead() != ((newProperties->transformationFlags & ARA::kARAPlaybackTransformationContentBasedFadeAtHead) != 0)) | |||
| || (playbackRegion->hasContentBasedFadeAtTail() != ((newProperties->transformationFlags & ARA::kARAPlaybackTransformationContentBasedFadeAtTail) != 0))) | |||
| { | |||
| invalidate(); | |||
| } | |||
| } | |||
| void ARAPlaybackRegionReader::didUpdatePlaybackRegionContent (ARAPlaybackRegion* playbackRegion, | |||
| ARAContentUpdateScopes scopeFlags) | |||
| { | |||
| jassertquiet (ARA::contains (playbackRenderer->getPlaybackRegions(), playbackRegion)); | |||
| // Invalidate if the audio signal is changed | |||
| if (scopeFlags.affectSamples()) | |||
| invalidate(); | |||
| } | |||
| void ARAPlaybackRegionReader::willDestroyPlaybackRegion (ARAPlaybackRegion* playbackRegion) | |||
| { | |||
| jassertquiet (ARA::contains (playbackRenderer->getPlaybackRegions(), playbackRegion)); | |||
| invalidate(); | |||
| } | |||
| } // namespace juce | |||
| @@ -0,0 +1,187 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE 7 technical preview. | |||
| Copyright (c) 2022 - Raw Material Software Limited | |||
| You may use this code under the terms of the GPL v3 | |||
| (see www.gnu.org/licenses). | |||
| For the technical preview this file cannot be licensed commercially. | |||
| JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
| EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
| DISCLAIMED. | |||
| ============================================================================== | |||
| */ | |||
| #pragma once | |||
| namespace juce | |||
| { | |||
| class AudioProcessor; | |||
| /* All these readers follow a common pattern of "invalidation": | |||
| Whenever the samples they are reading are altered, the readers become invalid and will stop | |||
| accessing the model graph. These alterations are model edits such as property changes, content | |||
| changes (if affecting sample scope), or the deletion of some model object involved in the read | |||
| process. Since these edits are performed on the document controller thread, reader validity can | |||
| immediately be checked after the edit has been concluded, and any reader that has become invalid | |||
| can be recreated. | |||
| Note that encountering a failure in any individual read call does not invalidate the reader, so | |||
| that the entity using the reader can decide whether to retry or to back out. This includes trying | |||
| to read an audio source for which the host has currently disabled access: the failure will be | |||
| immediately visible, but the reader will remain valid. This ensures that for example a realtime | |||
| renderer can just keep reading and will be seeing proper samples again once sample access is | |||
| re-enabled. | |||
| If desired, the code calling readSamples() can also implement proper signaling of any read error | |||
| to the document controller thread to trigger rebuilding the reader as needed. This will typically | |||
| be done when implementing audio source analysis: if there is an error upon reading the samples | |||
| that cannot be resolved within a reasonable timeout, then the analysis would be aborted. The | |||
| document controller code that monitors the analysis tasks can evaluate this and re-launch a new | |||
| analysis when appropriate (e.g. when access is re-enabled). | |||
| When reading playback regions (directly or through a region sequence reader), the reader will | |||
| represent the regions as a single source object that covers the union of all affected regions. | |||
| The first sample produced by the reader thus will be the first sample of the earliest region. | |||
| This means that the location of this region has to be taken into account by the calling code if | |||
| it wants to relate the samples to the model or any other reader output. | |||
| */ | |||
| //============================================================================== | |||
| /** | |||
| Subclass of AudioFormatReader that reads samples from a single ARA audio source. | |||
| Plug-Ins typically use this from their rendering code, wrapped in a BufferingAudioReader | |||
| to bridge between realtime rendering and non-realtime audio reading. | |||
| The reader becomes invalidated if | |||
| - the audio source content is updated in a way that affects its samples, | |||
| - the audio source sample access is disabled, or | |||
| - the audio source being read is destroyed. | |||
| @tags{ARA} | |||
| */ | |||
| class JUCE_API ARAAudioSourceReader : public AudioFormatReader, | |||
| private ARAAudioSource::Listener | |||
| { | |||
| public: | |||
| /** Use an ARAAudioSource to construct an audio source reader for the given \p audioSource. */ | |||
| explicit ARAAudioSourceReader (ARAAudioSource* audioSource); | |||
| ~ARAAudioSourceReader() override; | |||
| bool readSamples (int** destSamples, | |||
| int numDestChannels, | |||
| int startOffsetInDestBuffer, | |||
| int64 startSampleInFile, | |||
| int numSamples) override; | |||
| /** Returns true as long as the reader's underlying ARAAudioSource remains accessible and its | |||
| sample content is not changed. | |||
| */ | |||
| bool isValid() const { return audioSourceBeingRead != nullptr; } | |||
| /** Invalidate the reader - the reader will call this internally if needed, but can also be | |||
| invalidated from the outside (from message thread only!). | |||
| */ | |||
| void invalidate(); | |||
| void willUpdateAudioSourceProperties (ARAAudioSource* audioSource, | |||
| ARAAudioSource::PropertiesPtr newProperties) override; | |||
| void doUpdateAudioSourceContent (ARAAudioSource* audioSource, | |||
| ARAContentUpdateScopes scopeFlags) override; | |||
| void willEnableAudioSourceSamplesAccess (ARAAudioSource* audioSource, bool enable) override; | |||
| void didEnableAudioSourceSamplesAccess (ARAAudioSource* audioSource, bool enable) override; | |||
| void willDestroyAudioSource (ARAAudioSource* audioSource) override; | |||
| private: | |||
| ARAAudioSource* audioSourceBeingRead; | |||
| std::unique_ptr<ARA::PlugIn::HostAudioReader> hostReader; | |||
| ReadWriteLock lock; | |||
| std::vector<void*> tmpPtrs; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ARAAudioSourceReader) | |||
| }; | |||
| //============================================================================== | |||
| /** | |||
| Subclass of AudioFormatReader that reads samples from a group of playback regions. | |||
| Plug-Ins typically use this to draw the output of a playback region in their UI. | |||
| In order to read from playback regions, the reader requires an audio processor that acts as ARA | |||
| playback renderer. Configuring the audio processor for real-time operation results in the reader | |||
| being real-time capable too, unlike most other AudioFormatReaders. The reader instance will take | |||
| care of adding all regions being read to the renderer and invoke its processBlock function in | |||
| order to read the region samples. | |||
| The reader becomes invalid if | |||
| - any region properties are updated in a way that would affect its samples, | |||
| - any region content is updated in a way that would affect its samples, or | |||
| - any of its regions are destroyed. | |||
| @tags{ARA} | |||
| */ | |||
| class JUCE_API ARAPlaybackRegionReader : public AudioFormatReader, | |||
| private ARAPlaybackRegion::Listener | |||
| { | |||
| public: | |||
| /** Create an ARAPlaybackRegionReader instance to read the given \p playbackRegion, using the | |||
| sample rate and channel count of the underlying ARAAudioSource. | |||
| @param playbackRegion The playback region that should be read - must not be nullptr! | |||
| */ | |||
| explicit ARAPlaybackRegionReader (ARAPlaybackRegion* playbackRegion); | |||
| /** Create an ARAPlaybackRegionReader instance to read the given \p playbackRegions | |||
| @param sampleRate The sample rate that should be used for reading. | |||
| @param numChannels The channel count that should be used for reading. | |||
| @param playbackRegions The vector of playback regions that should be read - must not be empty! | |||
| All regions must be part of the same ARADocument. | |||
| */ | |||
| ARAPlaybackRegionReader (double sampleRate, int numChannels, | |||
| const std::vector<ARAPlaybackRegion*>& playbackRegions); | |||
| ~ARAPlaybackRegionReader() override; | |||
| /** Returns true as long as any of the reader's underlying playback region's haven't changed. */ | |||
| bool isValid() const { return (playbackRenderer != nullptr); } | |||
| /** Invalidate the reader - this should be called if the sample content of any of the reader's | |||
| ARAPlaybackRegions changes. | |||
| */ | |||
| void invalidate(); | |||
| bool readSamples (int** destSamples, | |||
| int numDestChannels, | |||
| int startOffsetInDestBuffer, | |||
| int64 startSampleInFile, | |||
| int numSamples) override; | |||
| void willUpdatePlaybackRegionProperties (ARAPlaybackRegion* playbackRegion, | |||
| ARAPlaybackRegion::PropertiesPtr newProperties) override; | |||
| void didUpdatePlaybackRegionContent (ARAPlaybackRegion* playbackRegion, | |||
| ARAContentUpdateScopes scopeFlags) override; | |||
| void willDestroyPlaybackRegion (ARAPlaybackRegion* playbackRegion) override; | |||
| /** The starting point of the reader in playback samples */ | |||
| int64 startInSamples = 0; | |||
| private: | |||
| std::unique_ptr<ARAPlaybackRenderer> playbackRenderer; | |||
| AudioPlayHead::CurrentPositionInfo positionInfo; | |||
| ReadWriteLock lock; | |||
| static constexpr int maximumBlockSize = 4 * 1024; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ARAPlaybackRegionReader) | |||
| }; | |||
| } // namespace juce | |||
| @@ -61,6 +61,11 @@ | |||
| #include "codecs/juce_WavAudioFormat.cpp" | |||
| #include "codecs/juce_LAMEEncoderAudioFormat.cpp" | |||
| #if JucePlugin_Enable_ARA | |||
| #include "juce_audio_processors/utilities/ARA/juce_ARADocumentControllerCommon.cpp" | |||
| #include "format/juce_ARAAudioReaders.cpp" | |||
| #endif | |||
| #if JUCE_WINDOWS && JUCE_USE_WINDOWS_MEDIA_FORMAT | |||
| #include "codecs/juce_WindowsMediaAudioFormat.cpp" | |||
| #endif | |||
| @@ -121,3 +121,9 @@ | |||
| #include "codecs/juce_WavAudioFormat.h" | |||
| #include "codecs/juce_WindowsMediaAudioFormat.h" | |||
| #include "sampler/juce_Sampler.h" | |||
| #if JucePlugin_Enable_ARA | |||
| #include <juce_audio_processors/juce_audio_processors.h> | |||
| #include "format/juce_ARAAudioReaders.h" | |||
| #endif | |||
| @@ -219,6 +219,7 @@ private: | |||
| #include "utilities/juce_AudioProcessorValueTreeState.cpp" | |||
| #include "utilities/juce_PluginHostType.cpp" | |||
| #include "utilities/juce_NativeScaleFactorNotifier.cpp" | |||
| #include "utilities/ARA/juce_ARA_utils.cpp" | |||
| #include "format_types/juce_LV2PluginFormat.cpp" | |||
| @@ -160,6 +160,7 @@ | |||
| #include "utilities/juce_ParameterAttachments.h" | |||
| #include "utilities/juce_AudioProcessorValueTreeState.h" | |||
| #include "utilities/juce_PluginHostType.h" | |||
| #include "utilities/ARA/juce_ARA_utils.h" | |||
| // This is here to avoid missing-prototype warnings in user code. | |||
| // If you're implementing a plugin, you should supply a body for | |||
| @@ -24,7 +24,7 @@ | |||
| To prevent such problems it's easiest to have it in its own translation unit. | |||
| */ | |||
| #if (JUCE_PLUGINHOST_ARA && (JUCE_PLUGINHOST_VST3 || JUCE_PLUGINHOST_AU) && (JUCE_MAC || JUCE_WINDOWS)) | |||
| #if (JucePlugin_Enable_ARA || (JUCE_PLUGINHOST_ARA && (JUCE_PLUGINHOST_VST3 || JUCE_PLUGINHOST_AU))) && (JUCE_MAC || JUCE_WINDOWS) | |||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wgnu-zero-variadic-macro-arguments", "-Wmissing-prototypes") | |||
| #include <ARA_Library/Debug/ARADebug.c> | |||
| @@ -0,0 +1,964 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE 7 technical preview. | |||
| Copyright (c) 2022 - Raw Material Software Limited | |||
| You may use this code under the terms of the GPL v3 | |||
| (see www.gnu.org/licenses). | |||
| For the technical preview this file cannot be licensed commercially. | |||
| JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
| EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
| DISCLAIMED. | |||
| ============================================================================== | |||
| */ | |||
| namespace juce | |||
| { | |||
| class ARADocumentControllerSpecialisation::ARADocumentControllerImpl : public ARADocumentController, | |||
| private juce::Timer | |||
| { | |||
| public: | |||
| ARADocumentControllerImpl (const ARA::PlugIn::PlugInEntry* entry, | |||
| const ARA::ARADocumentControllerHostInstance* instance, | |||
| ARADocumentControllerSpecialisation* spec) | |||
| : ARADocumentController (entry, instance), specialisation (spec) | |||
| { | |||
| } | |||
| template <typename PlaybackRenderer_t = ARAPlaybackRenderer> | |||
| std::vector<PlaybackRenderer_t*> const& getPlaybackRenderers() const noexcept | |||
| { | |||
| return ARA::PlugIn::DocumentController::getPlaybackRenderers<PlaybackRenderer_t>(); | |||
| } | |||
| template <typename EditorRenderer_t = ARAEditorRenderer> | |||
| std::vector<EditorRenderer_t*> const& getEditorRenderers() const noexcept | |||
| { | |||
| return ARA::PlugIn::DocumentController::getEditorRenderers<EditorRenderer_t>(); | |||
| } | |||
| template <typename EditorView_t = ARAEditorView> | |||
| std::vector<EditorView_t*> const& getEditorViews() const noexcept | |||
| { | |||
| return ARA::PlugIn::DocumentController::getEditorViews<EditorView_t>(); | |||
| } | |||
| auto getSpecialisation() { return specialisation; } | |||
| protected: | |||
| //============================================================================== | |||
| bool doRestoreObjectsFromStream (ARAInputStream& input, const ARARestoreObjectsFilter* filter) noexcept | |||
| { | |||
| return specialisation->doRestoreObjectsFromStream (input, filter); | |||
| } | |||
| bool doStoreObjectsToStream (ARAOutputStream& output, const ARAStoreObjectsFilter* filter) noexcept | |||
| { | |||
| return specialisation->doStoreObjectsToStream (output, filter); | |||
| } | |||
| //============================================================================== | |||
| // Model object creation | |||
| ARA::PlugIn::Document* doCreateDocument () noexcept override; | |||
| ARA::PlugIn::MusicalContext* doCreateMusicalContext (ARA::PlugIn::Document* document, ARA::ARAMusicalContextHostRef hostRef) noexcept override; | |||
| ARA::PlugIn::RegionSequence* doCreateRegionSequence (ARA::PlugIn::Document* document, ARA::ARARegionSequenceHostRef hostRef) noexcept override; | |||
| ARA::PlugIn::AudioSource* doCreateAudioSource (ARA::PlugIn::Document* document, ARA::ARAAudioSourceHostRef hostRef) noexcept override; | |||
| ARA::PlugIn::AudioModification* doCreateAudioModification (ARA::PlugIn::AudioSource* audioSource, ARA::ARAAudioModificationHostRef hostRef, const ARA::PlugIn::AudioModification* optionalModificationToClone) noexcept override; | |||
| ARA::PlugIn::PlaybackRegion* doCreatePlaybackRegion (ARA::PlugIn::AudioModification* modification, ARA::ARAPlaybackRegionHostRef hostRef) noexcept override; | |||
| //============================================================================== | |||
| // Plugin role implementation | |||
| friend class ARAPlaybackRegionReader; | |||
| ARA::PlugIn::PlaybackRenderer* doCreatePlaybackRenderer() noexcept override; | |||
| ARA::PlugIn::EditorRenderer* doCreateEditorRenderer() noexcept override; | |||
| ARA::PlugIn::EditorView* doCreateEditorView() noexcept override; | |||
| //============================================================================== | |||
| // ARAAudioSource content access | |||
| bool doIsAudioSourceContentAvailable (const ARA::PlugIn::AudioSource* audioSource, | |||
| ARA::ARAContentType type) noexcept override; | |||
| ARA::ARAContentGrade doGetAudioSourceContentGrade (const ARA::PlugIn::AudioSource* audioSource, | |||
| ARA::ARAContentType type) noexcept override; | |||
| ARA::PlugIn::ContentReader* doCreateAudioSourceContentReader (ARA::PlugIn::AudioSource* audioSource, | |||
| ARA::ARAContentType type, | |||
| const ARA::ARAContentTimeRange* range) noexcept override; | |||
| //============================================================================== | |||
| // ARAAudioModification content access | |||
| bool doIsAudioModificationContentAvailable (const ARA::PlugIn::AudioModification* audioModification, | |||
| ARA::ARAContentType type) noexcept override; | |||
| ARA::ARAContentGrade doGetAudioModificationContentGrade (const ARA::PlugIn::AudioModification* audioModification, | |||
| ARA::ARAContentType type) noexcept override; | |||
| ARA::PlugIn::ContentReader* doCreateAudioModificationContentReader (ARA::PlugIn::AudioModification* audioModification, | |||
| ARA::ARAContentType type, | |||
| const ARA::ARAContentTimeRange* range) noexcept override; | |||
| //============================================================================== | |||
| // ARAPlaybackRegion content access | |||
| bool doIsPlaybackRegionContentAvailable (const ARA::PlugIn::PlaybackRegion* playbackRegion, | |||
| ARA::ARAContentType type) noexcept override; | |||
| ARA::ARAContentGrade doGetPlaybackRegionContentGrade (const ARA::PlugIn::PlaybackRegion* playbackRegion, | |||
| ARA::ARAContentType type) noexcept override; | |||
| ARA::PlugIn::ContentReader* doCreatePlaybackRegionContentReader (ARA::PlugIn::PlaybackRegion* playbackRegion, | |||
| ARA::ARAContentType type, | |||
| const ARA::ARAContentTimeRange* range) noexcept override; | |||
| //============================================================================== | |||
| // ARAAudioSource analysis | |||
| bool doIsAudioSourceContentAnalysisIncomplete (const ARA::PlugIn::AudioSource* audioSource, | |||
| ARA::ARAContentType type) noexcept override; | |||
| void doRequestAudioSourceContentAnalysis (ARA::PlugIn::AudioSource* audioSource, | |||
| std::vector<ARA::ARAContentType> const& contentTypes) noexcept override; | |||
| //============================================================================== | |||
| // Analysis Algorithm selection | |||
| ARA::ARAInt32 doGetProcessingAlgorithmsCount() noexcept override; | |||
| const ARA::ARAProcessingAlgorithmProperties* doGetProcessingAlgorithmProperties (ARA::ARAInt32 algorithmIndex) noexcept override; | |||
| ARA::ARAInt32 doGetProcessingAlgorithmForAudioSource (const ARA::PlugIn::AudioSource* audioSource) noexcept override; | |||
| void doRequestProcessingAlgorithmForAudioSource (ARA::PlugIn::AudioSource* audioSource, | |||
| ARA::ARAInt32 algorithmIndex) noexcept override; | |||
| #ifndef DOXYGEN | |||
| //============================================================================== | |||
| bool doRestoreObjectsFromArchive (ARA::PlugIn::HostArchiveReader* archiveReader, const ARA::PlugIn::RestoreObjectsFilter* filter) noexcept override; | |||
| bool doStoreObjectsToArchive (ARA::PlugIn::HostArchiveWriter* archiveWriter, const ARA::PlugIn::StoreObjectsFilter* filter) noexcept override; | |||
| //============================================================================== | |||
| // Document notifications | |||
| void willBeginEditing() noexcept override; | |||
| void didEndEditing() noexcept override; | |||
| void willNotifyModelUpdates() noexcept override; | |||
| void didNotifyModelUpdates() noexcept override; | |||
| void willUpdateDocumentProperties (ARA::PlugIn::Document* document, ARADocument::PropertiesPtr newProperties) noexcept override; | |||
| void didUpdateDocumentProperties (ARA::PlugIn::Document* document) noexcept override; | |||
| void didAddMusicalContextToDocument (ARA::PlugIn::Document* document, ARA::PlugIn::MusicalContext* musicalContext) noexcept override; | |||
| void willRemoveMusicalContextFromDocument (ARA::PlugIn::Document* document, ARA::PlugIn::MusicalContext* musicalContext) noexcept override; | |||
| void didReorderMusicalContextsInDocument (ARA::PlugIn::Document* document) noexcept override; | |||
| void didAddRegionSequenceToDocument (ARA::PlugIn::Document* document, ARA::PlugIn::RegionSequence* regionSequence) noexcept override; | |||
| void willRemoveRegionSequenceFromDocument (ARA::PlugIn::Document* document, ARA::PlugIn::RegionSequence* regionSequence) noexcept override; | |||
| void didReorderRegionSequencesInDocument (ARA::PlugIn::Document* document) noexcept override; | |||
| void didAddAudioSourceToDocument (ARA::PlugIn::Document* document, ARA::PlugIn::AudioSource* audioSource) noexcept override; | |||
| void willRemoveAudioSourceFromDocument (ARA::PlugIn::Document* document, ARA::PlugIn::AudioSource* audioSource) noexcept override; | |||
| void willDestroyDocument (ARA::PlugIn::Document* document) noexcept override; | |||
| //============================================================================== | |||
| // MusicalContext notifications | |||
| void willUpdateMusicalContextProperties (ARA::PlugIn::MusicalContext* musicalContext, ARAMusicalContext::PropertiesPtr newProperties) noexcept override; | |||
| void didUpdateMusicalContextProperties (ARA::PlugIn::MusicalContext* musicalContext) noexcept override; | |||
| void doUpdateMusicalContextContent (ARA::PlugIn::MusicalContext* musicalContext, const ARA::ARAContentTimeRange* range, ARA::ContentUpdateScopes flags) noexcept override; | |||
| void didAddRegionSequenceToMusicalContext (ARA::PlugIn::MusicalContext* musicalContext, ARA::PlugIn::RegionSequence* regionSequence) noexcept override; | |||
| void willRemoveRegionSequenceFromMusicalContext (ARA::PlugIn::MusicalContext* musicalContext, ARA::PlugIn::RegionSequence* regionSequence) noexcept override; | |||
| void didReorderRegionSequencesInMusicalContext (ARA::PlugIn::MusicalContext* musicalContext) noexcept override; | |||
| void willDestroyMusicalContext (ARA::PlugIn::MusicalContext* musicalContext) noexcept override; | |||
| //============================================================================== | |||
| // RegionSequence notifications, typically not overridden further | |||
| void willUpdateRegionSequenceProperties (ARA::PlugIn::RegionSequence* regionSequence, ARARegionSequence::PropertiesPtr newProperties) noexcept override; | |||
| void didUpdateRegionSequenceProperties (ARA::PlugIn::RegionSequence* regionSequence) noexcept override; | |||
| void didAddPlaybackRegionToRegionSequence (ARA::PlugIn::RegionSequence* regionSequence, ARA::PlugIn::PlaybackRegion* playbackRegion) noexcept override; | |||
| void willRemovePlaybackRegionFromRegionSequence (ARA::PlugIn::RegionSequence* regionSequence, ARA::PlugIn::PlaybackRegion* playbackRegion) noexcept override; | |||
| void willDestroyRegionSequence (ARA::PlugIn::RegionSequence* regionSequence) noexcept override; | |||
| //============================================================================== | |||
| // AudioSource notifications | |||
| void willUpdateAudioSourceProperties (ARA::PlugIn::AudioSource* audioSource, ARAAudioSource::PropertiesPtr newProperties) noexcept override; | |||
| void didUpdateAudioSourceProperties (ARA::PlugIn::AudioSource* audioSource) noexcept override; | |||
| void doUpdateAudioSourceContent (ARA::PlugIn::AudioSource* audioSource, const ARA::ARAContentTimeRange* range, ARA::ContentUpdateScopes flags) noexcept override; | |||
| void willEnableAudioSourceSamplesAccess (ARA::PlugIn::AudioSource* audioSource, bool enable) noexcept override; | |||
| void didEnableAudioSourceSamplesAccess (ARA::PlugIn::AudioSource* audioSource, bool enable) noexcept override; | |||
| void didAddAudioModificationToAudioSource (ARA::PlugIn::AudioSource* audioSource, ARA::PlugIn::AudioModification* audioModification) noexcept override; | |||
| void willRemoveAudioModificationFromAudioSource (ARA::PlugIn::AudioSource* audioSource, ARA::PlugIn::AudioModification* audioModification) noexcept override; | |||
| void willDeactivateAudioSourceForUndoHistory (ARA::PlugIn::AudioSource* audioSource, bool deactivate) noexcept override; | |||
| void didDeactivateAudioSourceForUndoHistory (ARA::PlugIn::AudioSource* audioSource, bool deactivate) noexcept override; | |||
| void willDestroyAudioSource (ARA::PlugIn::AudioSource* audioSource) noexcept override; | |||
| //============================================================================== | |||
| // AudioModification notifications | |||
| void willUpdateAudioModificationProperties (ARA::PlugIn::AudioModification* audioModification, ARAAudioModification::PropertiesPtr newProperties) noexcept override; | |||
| void didUpdateAudioModificationProperties (ARA::PlugIn::AudioModification* audioModification) noexcept override; | |||
| void didAddPlaybackRegionToAudioModification (ARA::PlugIn::AudioModification* audioModification, ARA::PlugIn::PlaybackRegion* playbackRegion) noexcept override; | |||
| void willRemovePlaybackRegionFromAudioModification (ARA::PlugIn::AudioModification* audioModification, ARA::PlugIn::PlaybackRegion* playbackRegion) noexcept override; | |||
| void willDeactivateAudioModificationForUndoHistory (ARA::PlugIn::AudioModification* audioModification, bool deactivate) noexcept override; | |||
| void didDeactivateAudioModificationForUndoHistory (ARA::PlugIn::AudioModification* audioModification, bool deactivate) noexcept override; | |||
| void willDestroyAudioModification (ARA::PlugIn::AudioModification* audioModification) noexcept override; | |||
| //============================================================================== | |||
| // PlaybackRegion notifications | |||
| void willUpdatePlaybackRegionProperties (ARA::PlugIn::PlaybackRegion* playbackRegion, ARAPlaybackRegion::PropertiesPtr newProperties) noexcept override; | |||
| void didUpdatePlaybackRegionProperties (ARA::PlugIn::PlaybackRegion* playbackRegion) noexcept override; | |||
| void willDestroyPlaybackRegion (ARA::PlugIn::PlaybackRegion* playbackRegion) noexcept override; | |||
| //============================================================================== | |||
| // juce::Timer overrides | |||
| void timerCallback() override; | |||
| public: | |||
| //============================================================================== | |||
| /** @internal */ | |||
| void internalNotifyAudioSourceAnalysisProgressStarted (ARAAudioSource* audioSource) override; | |||
| /** @internal */ | |||
| void internalNotifyAudioSourceAnalysisProgressUpdated (ARAAudioSource* audioSource, float progress) override; | |||
| /** @internal */ | |||
| void internalNotifyAudioSourceAnalysisProgressCompleted (ARAAudioSource* audioSource) override; | |||
| /** @internal */ | |||
| void internalDidUpdateAudioSourceAnalysisProgress (ARAAudioSource* audioSource, | |||
| ARAAudioSource::ARAAnalysisProgressState state, | |||
| float progress) override; | |||
| //============================================================================== | |||
| /** @internal */ | |||
| void internalNotifyAudioSourceContentChanged (ARAAudioSource* audioSource, | |||
| ARAContentUpdateScopes scopeFlags, | |||
| bool notifyARAHost) override; | |||
| /** @internal */ | |||
| void internalNotifyAudioModificationContentChanged (ARAAudioModification* audioModification, | |||
| ARAContentUpdateScopes scopeFlags, | |||
| bool notifyARAHost) override; | |||
| /** @internal */ | |||
| void internalNotifyPlaybackRegionContentChanged (ARAPlaybackRegion* playbackRegion, | |||
| ARAContentUpdateScopes scopeFlags, | |||
| bool notifyARAHost) override; | |||
| #endif | |||
| private: | |||
| //============================================================================== | |||
| ARADocumentControllerSpecialisation* specialisation; | |||
| std::atomic<bool> internalAnalysisProgressIsSynced { true }; | |||
| ScopedJuceInitialiser_GUI libraryInitialiser; | |||
| int activeAudioSourcesCount = 0; | |||
| //============================================================================== | |||
| template <typename ModelObject, typename Function, typename... Ts> | |||
| void notifyListeners (Function ModelObject::Listener::* function, ModelObject* modelObject, Ts... ts) | |||
| { | |||
| (specialisation->*function) (modelObject, ts...); | |||
| modelObject->notifyListeners ([&] (auto& l) | |||
| { | |||
| try | |||
| { | |||
| (l.*function) (modelObject, ts...); | |||
| } | |||
| catch (...) | |||
| { | |||
| } | |||
| }); | |||
| } | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ARADocumentControllerImpl) | |||
| }; | |||
| ARA::PlugIn::DocumentController* ARADocumentControllerSpecialisation::getDocumentController() noexcept | |||
| { | |||
| return documentController.get(); | |||
| } | |||
| //============================================================================== | |||
| void ARADocumentControllerSpecialisation::ARADocumentControllerImpl::internalNotifyAudioSourceAnalysisProgressStarted (ARAAudioSource* audioSource) | |||
| { | |||
| if (audioSource->internalAnalysisProgressTracker.updateProgress (ARA::kARAAnalysisProgressStarted, 0.0f)) | |||
| internalAnalysisProgressIsSynced.store (false, std::memory_order_release); | |||
| DocumentController::notifyAudioSourceAnalysisProgressStarted (audioSource); | |||
| } | |||
| void ARADocumentControllerSpecialisation::ARADocumentControllerImpl::internalNotifyAudioSourceAnalysisProgressUpdated (ARAAudioSource* audioSource, | |||
| float progress) | |||
| { | |||
| if (audioSource->internalAnalysisProgressTracker.updateProgress (ARA::kARAAnalysisProgressUpdated, progress)) | |||
| internalAnalysisProgressIsSynced.store (false, std::memory_order_release); | |||
| DocumentController::notifyAudioSourceAnalysisProgressUpdated (audioSource, progress); | |||
| } | |||
| void ARADocumentControllerSpecialisation::ARADocumentControllerImpl::internalNotifyAudioSourceAnalysisProgressCompleted (ARAAudioSource* audioSource) | |||
| { | |||
| if (audioSource->internalAnalysisProgressTracker.updateProgress (ARA::kARAAnalysisProgressCompleted, 1.0f)) | |||
| internalAnalysisProgressIsSynced.store (false, std::memory_order_release); | |||
| DocumentController::notifyAudioSourceAnalysisProgressCompleted (audioSource); | |||
| } | |||
| void ARADocumentControllerSpecialisation::ARADocumentControllerImpl::internalDidUpdateAudioSourceAnalysisProgress (ARAAudioSource* audioSource, | |||
| ARAAudioSource::ARAAnalysisProgressState state, | |||
| float progress) | |||
| { | |||
| specialisation->didUpdateAudioSourceAnalysisProgress (audioSource, state, progress); | |||
| } | |||
| //============================================================================== | |||
| ARADocumentControllerSpecialisation* ARADocumentControllerSpecialisation::getSpecialisedDocumentControllerImpl (ARA::PlugIn::DocumentController* dc) | |||
| { | |||
| return static_cast<ARADocumentControllerImpl*> (dc)->getSpecialisation(); | |||
| } | |||
| ARADocument* ARADocumentControllerSpecialisation::getDocumentImpl() | |||
| { | |||
| return documentController->getDocument(); | |||
| } | |||
| //============================================================================== | |||
| // some helper macros to ease repeated declaration & implementation of notification functions below: | |||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wgnu-zero-variadic-macro-arguments") | |||
| // no notification arguments | |||
| #define OVERRIDE_TO_NOTIFY_1(function, ModelObjectType, modelObject) \ | |||
| void ARADocumentControllerSpecialisation::ARADocumentControllerImpl::function (ARA::PlugIn::ModelObjectType* modelObject) noexcept \ | |||
| { \ | |||
| notifyListeners (&ARA##ModelObjectType::Listener::function, static_cast<ARA##ModelObjectType*> (modelObject)); \ | |||
| } | |||
| // single notification argument, model object version | |||
| #define OVERRIDE_TO_NOTIFY_2(function, ModelObjectType, modelObject, ArgumentType, argument) \ | |||
| void ARADocumentControllerSpecialisation::ARADocumentControllerImpl::function (ARA::PlugIn::ModelObjectType* modelObject, ARA::PlugIn::ArgumentType argument) noexcept \ | |||
| { \ | |||
| notifyListeners (&ARA##ModelObjectType::Listener::function, static_cast<ARA##ModelObjectType*> (modelObject), static_cast<ARA##ArgumentType> (argument)); \ | |||
| } | |||
| // single notification argument, non-model object version | |||
| #define OVERRIDE_TO_NOTIFY_3(function, ModelObjectType, modelObject, ArgumentType, argument) \ | |||
| void ARADocumentControllerSpecialisation::ARADocumentControllerImpl::function (ARA::PlugIn::ModelObjectType* modelObject, ArgumentType argument) noexcept \ | |||
| { \ | |||
| notifyListeners (&ARA##ModelObjectType::Listener::function, static_cast<ARA##ModelObjectType*> (modelObject), argument); \ | |||
| } | |||
| //============================================================================== | |||
| ARA::PlugIn::Document* ARADocumentControllerSpecialisation::ARADocumentControllerImpl::doCreateDocument() noexcept | |||
| { | |||
| auto* document = specialisation->doCreateDocument(); | |||
| // Your Document subclass must inherit from juce::ARADocument | |||
| jassert (dynamic_cast<ARADocument*> (document)); | |||
| return document; | |||
| } | |||
| void ARADocumentControllerSpecialisation::ARADocumentControllerImpl::willBeginEditing() noexcept | |||
| { | |||
| notifyListeners (&ARADocument::Listener::willBeginEditing, static_cast<ARADocument*> (getDocument())); | |||
| } | |||
| void ARADocumentControllerSpecialisation::ARADocumentControllerImpl::didEndEditing() noexcept | |||
| { | |||
| notifyListeners (&ARADocument::Listener::didEndEditing, static_cast<ARADocument*> (getDocument())); | |||
| if (isTimerRunning() && (activeAudioSourcesCount == 0)) | |||
| stopTimer(); | |||
| else if (! isTimerRunning() && (activeAudioSourcesCount > 0)) | |||
| startTimerHz (20); | |||
| } | |||
| void ARADocumentControllerSpecialisation::ARADocumentControllerImpl::willNotifyModelUpdates() noexcept | |||
| { | |||
| notifyListeners (&ARADocument::Listener::willNotifyModelUpdates, static_cast<ARADocument*> (getDocument())); | |||
| } | |||
| void ARADocumentControllerSpecialisation::ARADocumentControllerImpl::didNotifyModelUpdates() noexcept | |||
| { | |||
| notifyListeners (&ARADocument::Listener::didNotifyModelUpdates, static_cast<ARADocument*> (getDocument())); | |||
| } | |||
| //============================================================================== | |||
| bool ARADocumentControllerSpecialisation::ARADocumentControllerImpl::doRestoreObjectsFromArchive (ARA::PlugIn::HostArchiveReader* archiveReader, | |||
| const ARA::PlugIn::RestoreObjectsFilter* filter) noexcept | |||
| { | |||
| ARAInputStream reader (archiveReader); | |||
| return doRestoreObjectsFromStream (reader, filter); | |||
| } | |||
| bool ARADocumentControllerSpecialisation::ARADocumentControllerImpl::doStoreObjectsToArchive (ARA::PlugIn::HostArchiveWriter* archiveWriter, | |||
| const ARA::PlugIn::StoreObjectsFilter* filter) noexcept | |||
| { | |||
| ARAOutputStream writer (archiveWriter); | |||
| return doStoreObjectsToStream (writer, filter); | |||
| } | |||
| //============================================================================== | |||
| OVERRIDE_TO_NOTIFY_3 (willUpdateDocumentProperties, Document, document, ARADocument::PropertiesPtr, newProperties) | |||
| OVERRIDE_TO_NOTIFY_1 (didUpdateDocumentProperties, Document, document) | |||
| OVERRIDE_TO_NOTIFY_2 (didAddMusicalContextToDocument, Document, document, MusicalContext*, musicalContext) | |||
| OVERRIDE_TO_NOTIFY_2 (willRemoveMusicalContextFromDocument, Document, document, MusicalContext*, musicalContext) | |||
| OVERRIDE_TO_NOTIFY_1 (didReorderMusicalContextsInDocument, Document, document) | |||
| OVERRIDE_TO_NOTIFY_2 (didAddRegionSequenceToDocument, Document, document, RegionSequence*, regionSequence) | |||
| OVERRIDE_TO_NOTIFY_2 (willRemoveRegionSequenceFromDocument, Document, document, RegionSequence*, regionSequence) | |||
| OVERRIDE_TO_NOTIFY_1 (didReorderRegionSequencesInDocument, Document, document) | |||
| OVERRIDE_TO_NOTIFY_2 (didAddAudioSourceToDocument, Document, document, AudioSource*, audioSource) | |||
| OVERRIDE_TO_NOTIFY_2 (willRemoveAudioSourceFromDocument, Document, document, AudioSource*, audioSource) | |||
| OVERRIDE_TO_NOTIFY_1 (willDestroyDocument, Document, document) | |||
| //============================================================================== | |||
| ARA::PlugIn::MusicalContext* ARADocumentControllerSpecialisation::ARADocumentControllerImpl::doCreateMusicalContext (ARA::PlugIn::Document* document, | |||
| ARA::ARAMusicalContextHostRef hostRef) noexcept | |||
| { | |||
| return specialisation->doCreateMusicalContext (static_cast<ARADocument*> (document), hostRef); | |||
| } | |||
| void ARADocumentControllerSpecialisation::ARADocumentControllerImpl::doUpdateMusicalContextContent (ARA::PlugIn::MusicalContext* musicalContext, | |||
| const ARA::ARAContentTimeRange*, | |||
| ARA::ContentUpdateScopes flags) noexcept | |||
| { | |||
| notifyListeners (&ARAMusicalContext::Listener::doUpdateMusicalContextContent, | |||
| static_cast<ARAMusicalContext*> (musicalContext), | |||
| flags); | |||
| } | |||
| OVERRIDE_TO_NOTIFY_3 (willUpdateMusicalContextProperties, MusicalContext, musicalContext, ARAMusicalContext::PropertiesPtr, newProperties) | |||
| OVERRIDE_TO_NOTIFY_1 (didUpdateMusicalContextProperties, MusicalContext, musicalContext) | |||
| OVERRIDE_TO_NOTIFY_2 (didAddRegionSequenceToMusicalContext, MusicalContext, musicalContext, RegionSequence*, regionSequence) | |||
| OVERRIDE_TO_NOTIFY_2 (willRemoveRegionSequenceFromMusicalContext, MusicalContext, musicalContext, RegionSequence*, regionSequence) | |||
| OVERRIDE_TO_NOTIFY_1 (didReorderRegionSequencesInMusicalContext, MusicalContext, musicalContext) | |||
| OVERRIDE_TO_NOTIFY_1 (willDestroyMusicalContext, MusicalContext, musicalContext) | |||
| //============================================================================== | |||
| ARA::PlugIn::RegionSequence* ARADocumentControllerSpecialisation::ARADocumentControllerImpl::doCreateRegionSequence (ARA::PlugIn::Document* document, ARA::ARARegionSequenceHostRef hostRef) noexcept | |||
| { | |||
| return specialisation->doCreateRegionSequence (static_cast<ARADocument*> (document), hostRef); | |||
| } | |||
| OVERRIDE_TO_NOTIFY_3 (willUpdateRegionSequenceProperties, RegionSequence, regionSequence, ARARegionSequence::PropertiesPtr, newProperties) | |||
| OVERRIDE_TO_NOTIFY_1 (didUpdateRegionSequenceProperties, RegionSequence, regionSequence) | |||
| OVERRIDE_TO_NOTIFY_2 (didAddPlaybackRegionToRegionSequence, RegionSequence, regionSequence, PlaybackRegion*, playbackRegion) | |||
| OVERRIDE_TO_NOTIFY_2 (willRemovePlaybackRegionFromRegionSequence, RegionSequence, regionSequence, PlaybackRegion*, playbackRegion) | |||
| OVERRIDE_TO_NOTIFY_1 (willDestroyRegionSequence, RegionSequence, regionSequence) | |||
| //============================================================================== | |||
| ARA::PlugIn::AudioSource* ARADocumentControllerSpecialisation::ARADocumentControllerImpl::doCreateAudioSource (ARA::PlugIn::Document* document, ARA::ARAAudioSourceHostRef hostRef) noexcept | |||
| { | |||
| ++activeAudioSourcesCount; | |||
| return specialisation->doCreateAudioSource (static_cast<ARADocument*> (document), hostRef); | |||
| } | |||
| void ARADocumentControllerSpecialisation::ARADocumentControllerImpl::doUpdateAudioSourceContent (ARA::PlugIn::AudioSource* audioSource, | |||
| const ARA::ARAContentTimeRange*, | |||
| ARA::ContentUpdateScopes flags) noexcept | |||
| { | |||
| notifyListeners (&ARAAudioSource::Listener::doUpdateAudioSourceContent, static_cast<ARAAudioSource*> (audioSource), flags); | |||
| } | |||
| OVERRIDE_TO_NOTIFY_3 (willUpdateAudioSourceProperties, AudioSource, audioSource, ARAAudioSource::PropertiesPtr, newProperties) | |||
| OVERRIDE_TO_NOTIFY_1 (didUpdateAudioSourceProperties, AudioSource, audioSource) | |||
| OVERRIDE_TO_NOTIFY_3 (willEnableAudioSourceSamplesAccess, AudioSource, audioSource, bool, enable) | |||
| OVERRIDE_TO_NOTIFY_3 (didEnableAudioSourceSamplesAccess, AudioSource, audioSource, bool, enable) | |||
| OVERRIDE_TO_NOTIFY_2 (didAddAudioModificationToAudioSource, AudioSource, audioSource, AudioModification*, audioModification) | |||
| OVERRIDE_TO_NOTIFY_2 (willRemoveAudioModificationFromAudioSource, AudioSource, audioSource, AudioModification*, audioModification) | |||
| OVERRIDE_TO_NOTIFY_3 (willDeactivateAudioSourceForUndoHistory, AudioSource, audioSource, bool, deactivate) | |||
| void ARADocumentControllerSpecialisation::ARADocumentControllerImpl::didDeactivateAudioSourceForUndoHistory (ARA::PlugIn::AudioSource* audioSource, | |||
| bool deactivate) noexcept | |||
| { | |||
| activeAudioSourcesCount += (deactivate ? -1 : 1); | |||
| notifyListeners (&ARAAudioSource::Listener::didDeactivateAudioSourceForUndoHistory, | |||
| static_cast<ARAAudioSource*> (audioSource), | |||
| deactivate); | |||
| } | |||
| void ARADocumentControllerSpecialisation::ARADocumentControllerImpl::willDestroyAudioSource (ARA::PlugIn::AudioSource* audioSource) noexcept | |||
| { | |||
| if (! audioSource->isDeactivatedForUndoHistory()) | |||
| --activeAudioSourcesCount; | |||
| notifyListeners (&ARAAudioSource::Listener::willDestroyAudioSource, static_cast<ARAAudioSource*> (audioSource)); | |||
| } | |||
| //============================================================================== | |||
| ARA::PlugIn::AudioModification* ARADocumentControllerSpecialisation::ARADocumentControllerImpl::doCreateAudioModification (ARA::PlugIn::AudioSource* audioSource, | |||
| ARA::ARAAudioModificationHostRef hostRef, | |||
| const ARA::PlugIn::AudioModification* optionalModificationToClone) noexcept | |||
| { | |||
| return specialisation->doCreateAudioModification (static_cast<ARAAudioSource*> (audioSource), | |||
| hostRef, | |||
| static_cast<const ARAAudioModification*> (optionalModificationToClone)); | |||
| } | |||
| OVERRIDE_TO_NOTIFY_3 (willUpdateAudioModificationProperties, AudioModification, audioModification, ARAAudioModification::PropertiesPtr, newProperties) | |||
| OVERRIDE_TO_NOTIFY_1 (didUpdateAudioModificationProperties, AudioModification, audioModification) | |||
| OVERRIDE_TO_NOTIFY_2 (didAddPlaybackRegionToAudioModification, AudioModification, audioModification, PlaybackRegion*, playbackRegion) | |||
| OVERRIDE_TO_NOTIFY_2 (willRemovePlaybackRegionFromAudioModification, AudioModification, audioModification, PlaybackRegion*, playbackRegion) | |||
| OVERRIDE_TO_NOTIFY_3 (willDeactivateAudioModificationForUndoHistory, AudioModification, audioModification, bool, deactivate) | |||
| OVERRIDE_TO_NOTIFY_3 (didDeactivateAudioModificationForUndoHistory, AudioModification, audioModification, bool, deactivate) | |||
| OVERRIDE_TO_NOTIFY_1 (willDestroyAudioModification, AudioModification, audioModification) | |||
| //============================================================================== | |||
| ARA::PlugIn::PlaybackRegion* ARADocumentControllerSpecialisation::ARADocumentControllerImpl::doCreatePlaybackRegion (ARA::PlugIn::AudioModification* modification, | |||
| ARA::ARAPlaybackRegionHostRef hostRef) noexcept | |||
| { | |||
| return specialisation->doCreatePlaybackRegion (static_cast<ARAAudioModification*> (modification), hostRef); | |||
| } | |||
| OVERRIDE_TO_NOTIFY_3 (willUpdatePlaybackRegionProperties, PlaybackRegion, playbackRegion, ARAPlaybackRegion::PropertiesPtr, newProperties) | |||
| OVERRIDE_TO_NOTIFY_1 (didUpdatePlaybackRegionProperties, PlaybackRegion, playbackRegion) | |||
| OVERRIDE_TO_NOTIFY_1 (willDestroyPlaybackRegion, PlaybackRegion, playbackRegion) | |||
| //============================================================================== | |||
| void ARADocumentControllerSpecialisation::ARADocumentControllerImpl::internalNotifyAudioSourceContentChanged (ARAAudioSource* audioSource, | |||
| ARAContentUpdateScopes scopeFlags, | |||
| bool notifyARAHost) | |||
| { | |||
| if (notifyARAHost) | |||
| DocumentController::notifyAudioSourceContentChanged (audioSource, scopeFlags); | |||
| notifyListeners (&ARAAudioSource::Listener::doUpdateAudioSourceContent, audioSource, scopeFlags); | |||
| } | |||
| void ARADocumentControllerSpecialisation::ARADocumentControllerImpl::internalNotifyAudioModificationContentChanged (ARAAudioModification* audioModification, | |||
| ARAContentUpdateScopes scopeFlags, | |||
| bool notifyARAHost) | |||
| { | |||
| if (notifyARAHost) | |||
| DocumentController::notifyAudioModificationContentChanged (audioModification, scopeFlags); | |||
| notifyListeners (&ARAAudioModification::Listener::didUpdateAudioModificationContent, audioModification, scopeFlags); | |||
| } | |||
| void ARADocumentControllerSpecialisation::ARADocumentControllerImpl::internalNotifyPlaybackRegionContentChanged (ARAPlaybackRegion* playbackRegion, | |||
| ARAContentUpdateScopes scopeFlags, | |||
| bool notifyARAHost) | |||
| { | |||
| if (notifyARAHost) | |||
| DocumentController::notifyPlaybackRegionContentChanged (playbackRegion, scopeFlags); | |||
| notifyListeners (&ARAPlaybackRegion::Listener::didUpdatePlaybackRegionContent, playbackRegion, scopeFlags); | |||
| } | |||
| //============================================================================== | |||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||
| #undef OVERRIDE_TO_NOTIFY_1 | |||
| #undef OVERRIDE_TO_NOTIFY_2 | |||
| #undef OVERRIDE_TO_NOTIFY_3 | |||
| //============================================================================== | |||
| ARADocument* ARADocumentControllerSpecialisation::doCreateDocument() | |||
| { | |||
| return new ARADocument (static_cast<ARADocumentControllerImpl*> (getDocumentController())); | |||
| } | |||
| ARAMusicalContext* ARADocumentControllerSpecialisation::doCreateMusicalContext (ARADocument* document, | |||
| ARA::ARAMusicalContextHostRef hostRef) | |||
| { | |||
| return new ARAMusicalContext (static_cast<ARADocument*> (document), hostRef); | |||
| } | |||
| ARARegionSequence* ARADocumentControllerSpecialisation::doCreateRegionSequence (ARADocument* document, | |||
| ARA::ARARegionSequenceHostRef hostRef) | |||
| { | |||
| return new ARARegionSequence (static_cast<ARADocument*> (document), hostRef); | |||
| } | |||
| ARAAudioSource* ARADocumentControllerSpecialisation::doCreateAudioSource (ARADocument* document, | |||
| ARA::ARAAudioSourceHostRef hostRef) | |||
| { | |||
| return new ARAAudioSource (static_cast<ARADocument*> (document), hostRef); | |||
| } | |||
| ARAAudioModification* ARADocumentControllerSpecialisation::doCreateAudioModification ( | |||
| ARAAudioSource* audioSource, | |||
| ARA::ARAAudioModificationHostRef hostRef, | |||
| const ARAAudioModification* optionalModificationToClone) | |||
| { | |||
| return new ARAAudioModification (static_cast<ARAAudioSource*> (audioSource), | |||
| hostRef, | |||
| static_cast<const ARAAudioModification*> (optionalModificationToClone)); | |||
| } | |||
| ARAPlaybackRegion* | |||
| ARADocumentControllerSpecialisation::doCreatePlaybackRegion (ARAAudioModification* modification, | |||
| ARA::ARAPlaybackRegionHostRef hostRef) | |||
| { | |||
| return new ARAPlaybackRegion (static_cast<ARAAudioModification*> (modification), hostRef); | |||
| } | |||
| //============================================================================== | |||
| ARA::PlugIn::PlaybackRenderer* ARADocumentControllerSpecialisation::ARADocumentControllerImpl::doCreatePlaybackRenderer() noexcept | |||
| { | |||
| return specialisation->doCreatePlaybackRenderer(); | |||
| } | |||
| ARA::PlugIn::EditorRenderer* ARADocumentControllerSpecialisation::ARADocumentControllerImpl::doCreateEditorRenderer() noexcept | |||
| { | |||
| return specialisation->doCreateEditorRenderer(); | |||
| } | |||
| ARA::PlugIn::EditorView* ARADocumentControllerSpecialisation::ARADocumentControllerImpl::doCreateEditorView() noexcept | |||
| { | |||
| return specialisation->doCreateEditorView(); | |||
| } | |||
| bool ARADocumentControllerSpecialisation::ARADocumentControllerImpl::doIsAudioSourceContentAvailable (const ARA::PlugIn::AudioSource* audioSource, | |||
| ARA::ARAContentType type) noexcept | |||
| { | |||
| return specialisation->doIsAudioSourceContentAvailable (audioSource, type); | |||
| } | |||
| ARA::ARAContentGrade ARADocumentControllerSpecialisation::ARADocumentControllerImpl::doGetAudioSourceContentGrade (const ARA::PlugIn::AudioSource* audioSource, | |||
| ARA::ARAContentType type) noexcept | |||
| { | |||
| return specialisation->doGetAudioSourceContentGrade (audioSource, type); | |||
| } | |||
| ARA::PlugIn::ContentReader* ARADocumentControllerSpecialisation::ARADocumentControllerImpl::doCreateAudioSourceContentReader (ARA::PlugIn::AudioSource* audioSource, | |||
| ARA::ARAContentType type, | |||
| const ARA::ARAContentTimeRange* range) noexcept | |||
| { | |||
| return specialisation->doCreateAudioSourceContentReader (audioSource, type, range); | |||
| } | |||
| bool ARADocumentControllerSpecialisation::ARADocumentControllerImpl::doIsAudioModificationContentAvailable (const ARA::PlugIn::AudioModification* audioModification, | |||
| ARA::ARAContentType type) noexcept | |||
| { | |||
| return specialisation->doIsAudioModificationContentAvailable (audioModification, type); | |||
| } | |||
| ARA::ARAContentGrade ARADocumentControllerSpecialisation::ARADocumentControllerImpl::doGetAudioModificationContentGrade (const ARA::PlugIn::AudioModification* audioModification, | |||
| ARA::ARAContentType type) noexcept | |||
| { | |||
| return specialisation->doGetAudioModificationContentGrade (audioModification, type); | |||
| } | |||
| ARA::PlugIn::ContentReader* ARADocumentControllerSpecialisation::ARADocumentControllerImpl::doCreateAudioModificationContentReader (ARA::PlugIn::AudioModification* audioModification, | |||
| ARA::ARAContentType type, | |||
| const ARA::ARAContentTimeRange* range) noexcept | |||
| { | |||
| return specialisation->doCreateAudioModificationContentReader (audioModification, type, range); | |||
| } | |||
| bool ARADocumentControllerSpecialisation::ARADocumentControllerImpl::doIsPlaybackRegionContentAvailable (const ARA::PlugIn::PlaybackRegion* playbackRegion, | |||
| ARA::ARAContentType type) noexcept | |||
| { | |||
| return specialisation->doIsPlaybackRegionContentAvailable (playbackRegion, type); | |||
| } | |||
| ARA::ARAContentGrade | |||
| ARADocumentControllerSpecialisation::ARADocumentControllerImpl::doGetPlaybackRegionContentGrade (const ARA::PlugIn::PlaybackRegion* playbackRegion, | |||
| ARA::ARAContentType type) noexcept | |||
| { | |||
| return specialisation->doGetPlaybackRegionContentGrade (playbackRegion, type); | |||
| } | |||
| ARA::PlugIn::ContentReader* ARADocumentControllerSpecialisation::ARADocumentControllerImpl::doCreatePlaybackRegionContentReader (ARA::PlugIn::PlaybackRegion* playbackRegion, | |||
| ARA::ARAContentType type, | |||
| const ARA::ARAContentTimeRange* range) noexcept | |||
| { | |||
| return specialisation->doCreatePlaybackRegionContentReader (playbackRegion, type, range); | |||
| } | |||
| bool ARADocumentControllerSpecialisation::ARADocumentControllerImpl::doIsAudioSourceContentAnalysisIncomplete (const ARA::PlugIn::AudioSource* audioSource, | |||
| ARA::ARAContentType type) noexcept | |||
| { | |||
| return specialisation->doIsAudioSourceContentAnalysisIncomplete (audioSource, type); | |||
| } | |||
| void ARADocumentControllerSpecialisation::ARADocumentControllerImpl::doRequestAudioSourceContentAnalysis (ARA::PlugIn::AudioSource* audioSource, | |||
| std::vector<ARA::ARAContentType> const& contentTypes) noexcept | |||
| { | |||
| specialisation->doRequestAudioSourceContentAnalysis (audioSource, contentTypes); | |||
| } | |||
| ARA::ARAInt32 ARADocumentControllerSpecialisation::ARADocumentControllerImpl::doGetProcessingAlgorithmsCount() noexcept | |||
| { | |||
| return specialisation->doGetProcessingAlgorithmsCount(); | |||
| } | |||
| const ARA::ARAProcessingAlgorithmProperties* ARADocumentControllerSpecialisation::ARADocumentControllerImpl::doGetProcessingAlgorithmProperties (ARA::ARAInt32 algorithmIndex) noexcept | |||
| { | |||
| return specialisation->doGetProcessingAlgorithmProperties (algorithmIndex); | |||
| } | |||
| ARA::ARAInt32 ARADocumentControllerSpecialisation::ARADocumentControllerImpl::doGetProcessingAlgorithmForAudioSource (const ARA::PlugIn::AudioSource* audioSource) noexcept | |||
| { | |||
| return specialisation->doGetProcessingAlgorithmForAudioSource (audioSource); | |||
| } | |||
| void ARADocumentControllerSpecialisation::ARADocumentControllerImpl::doRequestProcessingAlgorithmForAudioSource (ARA::PlugIn::AudioSource* audioSource, | |||
| ARA::ARAInt32 algorithmIndex) noexcept | |||
| { | |||
| return specialisation->doRequestProcessingAlgorithmForAudioSource (audioSource, algorithmIndex); | |||
| } | |||
| //============================================================================== | |||
| // Helper code for ARADocumentControllerSpecialisation::ARADocumentControllerImpl::timerCallback() to | |||
| // rewire the host-related ARA SDK's progress tracker to our internal update mechanism. | |||
| namespace ModelUpdateControllerProgressAdapter | |||
| { | |||
| using namespace ARA; | |||
| static void ARA_CALL notifyAudioSourceAnalysisProgress (ARAModelUpdateControllerHostRef /*controllerHostRef*/, | |||
| ARAAudioSourceHostRef audioSourceHostRef, ARAAnalysisProgressState state, float value) noexcept | |||
| { | |||
| auto audioSource = reinterpret_cast<ARAAudioSource*> (audioSourceHostRef); | |||
| audioSource->getDocumentController<ARADocumentController>()->internalDidUpdateAudioSourceAnalysisProgress (audioSource, state, value); | |||
| audioSource->notifyListeners ([&] (ARAAudioSource::Listener& l) { l.didUpdateAudioSourceAnalysisProgress (audioSource, state, value); }); | |||
| } | |||
| static void ARA_CALL notifyAudioSourceContentChanged (ARAModelUpdateControllerHostRef, ARAAudioSourceHostRef, | |||
| const ARAContentTimeRange*, ARAContentUpdateFlags) noexcept | |||
| { | |||
| jassertfalse; // not to be called - this adapter only forwards analysis progress | |||
| } | |||
| static void ARA_CALL notifyAudioModificationContentChanged (ARAModelUpdateControllerHostRef, ARAAudioModificationHostRef, | |||
| const ARAContentTimeRange*, ARAContentUpdateFlags) noexcept | |||
| { | |||
| jassertfalse; // not to be called - this adapter only forwards analysis progress | |||
| } | |||
| static void ARA_CALL notifyPlaybackRegionContentChanged (ARAModelUpdateControllerHostRef, ARAPlaybackRegionHostRef, | |||
| const ARAContentTimeRange*, ARAContentUpdateFlags) noexcept | |||
| { | |||
| jassertfalse; // not to be called - this adapter only forwards analysis progress | |||
| } | |||
| static ARA::PlugIn::HostModelUpdateController* get() | |||
| { | |||
| static const auto modelUpdateControllerInterface = makeARASizedStruct (&ARA::ARAModelUpdateControllerInterface::notifyPlaybackRegionContentChanged, | |||
| ModelUpdateControllerProgressAdapter::notifyAudioSourceAnalysisProgress, | |||
| ModelUpdateControllerProgressAdapter::notifyAudioSourceContentChanged, | |||
| ModelUpdateControllerProgressAdapter::notifyAudioModificationContentChanged, | |||
| ModelUpdateControllerProgressAdapter::notifyPlaybackRegionContentChanged); | |||
| static const auto instance = makeARASizedStruct (&ARA::ARADocumentControllerHostInstance::playbackControllerInterface, | |||
| nullptr, | |||
| nullptr, | |||
| nullptr, | |||
| nullptr, | |||
| nullptr, | |||
| nullptr, | |||
| nullptr, | |||
| &modelUpdateControllerInterface, | |||
| nullptr, | |||
| nullptr); | |||
| static auto progressAdapter = ARA::PlugIn::HostModelUpdateController { &instance }; | |||
| return &progressAdapter; | |||
| } | |||
| } | |||
| void ARADocumentControllerSpecialisation::ARADocumentControllerImpl::timerCallback() | |||
| { | |||
| if (! internalAnalysisProgressIsSynced.exchange (true, std::memory_order_release)) | |||
| for (auto& audioSource : getDocument()->getAudioSources()) | |||
| audioSource->internalAnalysisProgressTracker.notifyProgress (ModelUpdateControllerProgressAdapter::get(), | |||
| reinterpret_cast<ARA::ARAAudioSourceHostRef> (audioSource)); | |||
| } | |||
| //============================================================================== | |||
| ARAInputStream::ARAInputStream (ARA::PlugIn::HostArchiveReader* reader) | |||
| : archiveReader (reader), | |||
| size ((int64) reader->getArchiveSize()) | |||
| {} | |||
| int ARAInputStream::read (void* destBuffer, int maxBytesToRead) | |||
| { | |||
| const auto bytesToRead = std::min ((int64) maxBytesToRead, size - position); | |||
| if (bytesToRead > 0 && ! archiveReader->readBytesFromArchive ((ARA::ARASize) position, (ARA::ARASize) bytesToRead, | |||
| static_cast<ARA::ARAByte*> (destBuffer))) | |||
| { | |||
| failure = true; | |||
| return 0; | |||
| } | |||
| position += bytesToRead; | |||
| return (int) bytesToRead; | |||
| } | |||
| bool ARAInputStream::setPosition (int64 newPosition) | |||
| { | |||
| position = jlimit ((int64) 0, size, newPosition); | |||
| return true; | |||
| } | |||
| bool ARAInputStream::isExhausted() | |||
| { | |||
| return position >= size; | |||
| } | |||
| ARAOutputStream::ARAOutputStream (ARA::PlugIn::HostArchiveWriter* writer) | |||
| : archiveWriter (writer) | |||
| {} | |||
| bool ARAOutputStream::write (const void* dataToWrite, size_t numberOfBytes) | |||
| { | |||
| if (! archiveWriter->writeBytesToArchive ((ARA::ARASize) position, numberOfBytes, (const ARA::ARAByte*) dataToWrite)) | |||
| return false; | |||
| position += numberOfBytes; | |||
| return true; | |||
| } | |||
| bool ARAOutputStream::setPosition (int64 newPosition) | |||
| { | |||
| position = newPosition; | |||
| return true; | |||
| } | |||
| //============================================================================== | |||
| ARADocumentControllerSpecialisation::ARADocumentControllerSpecialisation ( | |||
| const ARA::PlugIn::PlugInEntry* entry, | |||
| const ARA::ARADocumentControllerHostInstance* instance) | |||
| : documentController (std::make_unique<ARADocumentControllerImpl> (entry, instance, this)) | |||
| { | |||
| } | |||
| ARADocumentControllerSpecialisation::~ARADocumentControllerSpecialisation() = default; | |||
| ARAPlaybackRenderer* ARADocumentControllerSpecialisation::doCreatePlaybackRenderer() | |||
| { | |||
| return new ARAPlaybackRenderer (getDocumentController()); | |||
| } | |||
| ARAEditorRenderer* ARADocumentControllerSpecialisation::doCreateEditorRenderer() | |||
| { | |||
| return new ARAEditorRenderer (getDocumentController()); | |||
| } | |||
| ARAEditorView* ARADocumentControllerSpecialisation::doCreateEditorView() | |||
| { | |||
| return new ARAEditorView (getDocumentController()); | |||
| } | |||
| bool ARADocumentControllerSpecialisation::doIsAudioSourceContentAvailable (const ARA::PlugIn::AudioSource* audioSource, | |||
| ARA::ARAContentType type) | |||
| { | |||
| juce::ignoreUnused (audioSource, type); | |||
| return false; | |||
| } | |||
| ARA::ARAContentGrade ARADocumentControllerSpecialisation::doGetAudioSourceContentGrade (const ARA::PlugIn::AudioSource* audioSource, | |||
| ARA::ARAContentType type) | |||
| { | |||
| // Overriding doIsAudioSourceContentAvailable() requires overriding | |||
| // doGetAudioSourceContentGrade() accordingly! | |||
| jassertfalse; | |||
| juce::ignoreUnused (audioSource, type); | |||
| return ARA::kARAContentGradeInitial; | |||
| } | |||
| ARA::PlugIn::ContentReader* ARADocumentControllerSpecialisation::doCreateAudioSourceContentReader (ARA::PlugIn::AudioSource* audioSource, | |||
| ARA::ARAContentType type, | |||
| const ARA::ARAContentTimeRange* range) | |||
| { | |||
| // Overriding doIsAudioSourceContentAvailable() requires overriding | |||
| // doCreateAudioSourceContentReader() accordingly! | |||
| jassertfalse; | |||
| juce::ignoreUnused (audioSource, type, range); | |||
| return nullptr; | |||
| } | |||
| bool ARADocumentControllerSpecialisation::doIsAudioModificationContentAvailable (const ARA::PlugIn::AudioModification* audioModification, | |||
| ARA::ARAContentType type) | |||
| { | |||
| juce::ignoreUnused (audioModification, type); | |||
| return false; | |||
| } | |||
| ARA::ARAContentGrade ARADocumentControllerSpecialisation::doGetAudioModificationContentGrade (const ARA::PlugIn::AudioModification* audioModification, | |||
| ARA::ARAContentType type) | |||
| { | |||
| // Overriding doIsAudioModificationContentAvailable() requires overriding | |||
| // doGetAudioModificationContentGrade() accordingly! | |||
| jassertfalse; | |||
| juce::ignoreUnused (audioModification, type); | |||
| return ARA::kARAContentGradeInitial; | |||
| } | |||
| ARA::PlugIn::ContentReader* ARADocumentControllerSpecialisation::doCreateAudioModificationContentReader (ARA::PlugIn::AudioModification* audioModification, | |||
| ARA::ARAContentType type, | |||
| const ARA::ARAContentTimeRange* range) | |||
| { | |||
| // Overriding doIsAudioModificationContentAvailable() requires overriding | |||
| // doCreateAudioModificationContentReader() accordingly! | |||
| jassertfalse; | |||
| juce::ignoreUnused (audioModification, type, range); | |||
| return nullptr; | |||
| } | |||
| bool ARADocumentControllerSpecialisation::doIsPlaybackRegionContentAvailable (const ARA::PlugIn::PlaybackRegion* playbackRegion, | |||
| ARA::ARAContentType type) | |||
| { | |||
| juce::ignoreUnused (playbackRegion, type); | |||
| return false; | |||
| } | |||
| ARA::ARAContentGrade ARADocumentControllerSpecialisation::doGetPlaybackRegionContentGrade (const ARA::PlugIn::PlaybackRegion* playbackRegion, | |||
| ARA::ARAContentType type) | |||
| { | |||
| // Overriding doIsPlaybackRegionContentAvailable() requires overriding | |||
| // doGetPlaybackRegionContentGrade() accordingly! | |||
| jassertfalse; | |||
| juce::ignoreUnused (playbackRegion, type); | |||
| return ARA::kARAContentGradeInitial; | |||
| } | |||
| ARA::PlugIn::ContentReader* ARADocumentControllerSpecialisation::doCreatePlaybackRegionContentReader (ARA::PlugIn::PlaybackRegion* playbackRegion, | |||
| ARA::ARAContentType type, | |||
| const ARA::ARAContentTimeRange* range) | |||
| { | |||
| // Overriding doIsPlaybackRegionContentAvailable() requires overriding | |||
| // doCreatePlaybackRegionContentReader() accordingly! | |||
| jassertfalse; | |||
| juce::ignoreUnused (playbackRegion, type, range); | |||
| return nullptr; | |||
| } | |||
| bool ARADocumentControllerSpecialisation::doIsAudioSourceContentAnalysisIncomplete (const ARA::PlugIn::AudioSource* audioSource, | |||
| ARA::ARAContentType type) | |||
| { | |||
| juce::ignoreUnused (audioSource, type); | |||
| return false; | |||
| } | |||
| void ARADocumentControllerSpecialisation::doRequestAudioSourceContentAnalysis (ARA::PlugIn::AudioSource* audioSource, | |||
| std::vector<ARA::ARAContentType> const& contentTypes) | |||
| { | |||
| juce::ignoreUnused (audioSource, contentTypes); | |||
| } | |||
| ARA::ARAInt32 ARADocumentControllerSpecialisation::doGetProcessingAlgorithmsCount() { return 0; } | |||
| const ARA::ARAProcessingAlgorithmProperties* | |||
| ARADocumentControllerSpecialisation::doGetProcessingAlgorithmProperties (ARA::ARAInt32 algorithmIndex) | |||
| { | |||
| juce::ignoreUnused (algorithmIndex); | |||
| return nullptr; | |||
| } | |||
| ARA::ARAInt32 ARADocumentControllerSpecialisation::doGetProcessingAlgorithmForAudioSource (const ARA::PlugIn::AudioSource* audioSource) | |||
| { | |||
| // doGetProcessingAlgorithmForAudioSource() must be implemented if the supported | |||
| // algorithm count is greater than zero. | |||
| if (getDocumentController()->getProcessingAlgorithmsCount() > 0) | |||
| jassertfalse; | |||
| juce::ignoreUnused (audioSource); | |||
| return 0; | |||
| } | |||
| void ARADocumentControllerSpecialisation::doRequestProcessingAlgorithmForAudioSource (ARA::PlugIn::AudioSource* audioSource, | |||
| ARA::ARAInt32 algorithmIndex) | |||
| { | |||
| // doRequestProcessingAlgorithmForAudioSource() must be implemented if the supported | |||
| // algorithm count is greater than zero. | |||
| if (getDocumentController()->getProcessingAlgorithmsCount() > 0) | |||
| jassertfalse; | |||
| juce::ignoreUnused (audioSource, algorithmIndex); | |||
| } | |||
| } // namespace juce | |||
| @@ -0,0 +1,513 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE 7 technical preview. | |||
| Copyright (c) 2022 - Raw Material Software Limited | |||
| You may use this code under the terms of the GPL v3 | |||
| (see www.gnu.org/licenses). | |||
| For the technical preview this file cannot be licensed commercially. | |||
| JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
| EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
| DISCLAIMED. | |||
| ============================================================================== | |||
| */ | |||
| #pragma once | |||
| namespace juce | |||
| { | |||
| class ARAPlaybackRenderer; | |||
| class ARAEditorRenderer; | |||
| class ARAEditorView; | |||
| class ARAInputStream; | |||
| class ARAOutputStream; | |||
| /** This class contains the customisation points for the JUCE provided ARA document controller | |||
| implementation. | |||
| Every ARA enabled plugin must provide its own document controller implementation. To do this, | |||
| inherit from this class, and override its protected methods as needed. Then you need to | |||
| implement a global function somewhere in your module called createARAFactory(). This function | |||
| must return an ARAFactory* that will instantiate document controller objects using your | |||
| specialisation. There are helper functions inside ARADocumentControllerSpecialisation, so the | |||
| implementation of createARAFactory() can always be a simple one-liner. For example | |||
| @code | |||
| class MyDocumentController : public ARADocumentControllerSpecialisation | |||
| { | |||
| //... | |||
| }; | |||
| const ARA::ARAFactory* JUCE_CALLTYPE createARAFactory() | |||
| { | |||
| return juce::ARADocumentControllerSpecialisation::createARAFactory<MyDocumentController>(); | |||
| } | |||
| @endcode | |||
| Most member functions have a default implementation so you can build up your required feature | |||
| set gradually. The protected functions of this class fall in three distinct groups: | |||
| - interactive editing and playback, | |||
| - analysis features provided by the plugin and utilised by the host, and | |||
| - maintaining the ARA model graph. | |||
| On top of the pure virtual functions, you will probably want to override | |||
| doCreatePlaybackRenderer() at the very least if you want your plugin to play any sound. This | |||
| function belongs to the first group. | |||
| If your plugin has analysis capabilities and wants to allow the host to access these, functions | |||
| in the second group should be overridden. | |||
| The default implementation of the ARA model object classes - i.e. ARADocument, ARAMusicalContext, | |||
| ARARegionSequence, ARAAudioSource, ARAAudioModification, ARAPlaybackRegion - should be sufficient | |||
| for maintaining a representation of the ARA model graph, hence overriding the model object | |||
| creation functions e.g. doCreateMusicalContext() is considered an advanced use case. Hence you | |||
| should be able to get a lot done without overriding functions in the third group. | |||
| In order to react to the various ARA state changes you can override any of the ARA model object | |||
| Listener functions that ARADocumentControllerSpecialisation inherits from. Such listener | |||
| functions can be attached to one particular model objects instance, but the listener functions | |||
| inside ARADocumentControllerSpecialisation will respond to the events of all instances of the | |||
| model objects. | |||
| @tags{ARA} | |||
| */ | |||
| class ARADocumentControllerSpecialisation : public ARADocument::Listener, | |||
| public ARAMusicalContext::Listener, | |||
| public ARARegionSequence::Listener, | |||
| public ARAAudioSource::Listener, | |||
| public ARAAudioModification::Listener, | |||
| public ARAPlaybackRegion::Listener | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Constructor. Used internally by the ARAFactory implementation. | |||
| */ | |||
| ARADocumentControllerSpecialisation (const ARA::PlugIn::PlugInEntry* entry, | |||
| const ARA::ARADocumentControllerHostInstance* instance); | |||
| /** Destructor. */ | |||
| virtual ~ARADocumentControllerSpecialisation(); | |||
| /** Returns the underlying DocumentController object that references this specialisation. | |||
| */ | |||
| ARA::PlugIn::DocumentController* getDocumentController() noexcept; | |||
| /** Helper function for implementing the global createARAFactory() function. | |||
| For example | |||
| @code | |||
| class MyDocumentController : public ARADocumentControllerSpecialisation | |||
| { | |||
| //... | |||
| }; | |||
| const ARA::ARAFactory* JUCE_CALLTYPE createARAFactory() | |||
| { | |||
| return juce::ARADocumentControllerSpecialisation::createARAFactory<MyDocumentController>(); | |||
| } | |||
| @endcode | |||
| */ | |||
| template <typename SpecialisationType> | |||
| static const ARA::ARAFactory* createARAFactory() | |||
| { | |||
| static_assert (std::is_base_of<ARADocumentControllerSpecialisation, SpecialisationType>::value, | |||
| "DocumentController specialization types must inherit from ARADocumentControllerSpecialisation"); | |||
| return ARA::PlugIn::PlugInEntry::getPlugInEntry<FactoryConfig<SpecialisationType>>()->getFactory(); | |||
| } | |||
| /** Returns a pointer to the ARADocumentControllerSpecialisation instance that is referenced | |||
| by the provided DocumentController. You can use this function to access your specialisation | |||
| from anywhere where you have access to ARA::PlugIn::DocumentController*. | |||
| */ | |||
| template <typename Specialisation = ARADocumentControllerSpecialisation> | |||
| static Specialisation* getSpecialisedDocumentController (ARA::PlugIn::DocumentController* dc) | |||
| { | |||
| return static_cast<Specialisation*> (getSpecialisedDocumentControllerImpl (dc)); | |||
| } | |||
| /** Returns a pointer to the ARA document root maintained by this document controller. */ | |||
| template <typename DocumentType = ARADocument> | |||
| DocumentType* getDocument() | |||
| { | |||
| return static_cast<DocumentType*> (getDocumentImpl()); | |||
| } | |||
| protected: | |||
| //============================================================================== | |||
| /** Read an ARADocument archive from a juce::InputStream. | |||
| @param input Data stream containing previously persisted data to be used when restoring the ARADocument | |||
| @param filter A filter to be applied to the stream | |||
| Return true if the operation is successful. | |||
| @see ARADocumentControllerInterface::restoreObjectsFromArchive | |||
| */ | |||
| virtual bool doRestoreObjectsFromStream (ARAInputStream& input, const ARARestoreObjectsFilter* filter) = 0; | |||
| /** Write an ARADocument archive to a juce::OutputStream. | |||
| @param output Data stream that should be used to write the persistent ARADocument data | |||
| @param filter A filter to be applied to the stream | |||
| Returns true if the operation is successful. | |||
| @see ARADocumentControllerInterface::storeObjectsToArchive | |||
| */ | |||
| virtual bool doStoreObjectsToStream (ARAOutputStream& output, const ARAStoreObjectsFilter* filter) = 0; | |||
| //============================================================================== | |||
| /** Override to return a custom subclass instance of ARAPlaybackRenderer. */ | |||
| virtual ARAPlaybackRenderer* doCreatePlaybackRenderer(); | |||
| /** Override to return a custom subclass instance of ARAEditorRenderer. */ | |||
| virtual ARAEditorRenderer* doCreateEditorRenderer(); | |||
| /** Override to return a custom subclass instance of ARAEditorView. */ | |||
| virtual ARAEditorView* doCreateEditorView(); | |||
| //============================================================================== | |||
| // ARAAudioSource content access | |||
| /** Override to implement isAudioSourceContentAvailable() for all your supported content types - | |||
| the default implementation always returns false, preventing any calls to doGetAudioSourceContentGrade() | |||
| and doCreateAudioSourceContentReader(). | |||
| This function's result is returned from | |||
| ARA::PlugIn::DocumentControllerDelegate::doIsAudioSourceContentAvailable. | |||
| */ | |||
| virtual bool doIsAudioSourceContentAvailable (const ARA::PlugIn::AudioSource* audioSource, | |||
| ARA::ARAContentType type); | |||
| /** Override to implement getAudioSourceContentGrade() for all your supported content types. | |||
| This function's result is returned from | |||
| ARA::PlugIn::DocumentControllerDelegate::doGetAudioSourceContentGrade. | |||
| */ | |||
| virtual ARA::ARAContentGrade doGetAudioSourceContentGrade (const ARA::PlugIn::AudioSource* audioSource, | |||
| ARA::ARAContentType type); | |||
| /** Override to implement createAudioSourceContentReader() for all your supported content types, | |||
| returning a custom subclass instance of ContentReader providing data of the requested type. | |||
| This function's result is returned from | |||
| ARA::PlugIn::DocumentControllerDelegate::doCreateAudioSourceContentReader. | |||
| */ | |||
| virtual ARA::PlugIn::ContentReader* doCreateAudioSourceContentReader (ARA::PlugIn::AudioSource* audioSource, | |||
| ARA::ARAContentType type, | |||
| const ARA::ARAContentTimeRange* range); | |||
| //============================================================================== | |||
| // ARAAudioModification content access | |||
| /** Override to implement isAudioModificationContentAvailable() for all your supported content types - | |||
| the default implementation always returns false. | |||
| For read-only data directly inherited from the underlying audio source you can just delegate the | |||
| call to the audio source, but user-editable modification data must be specifically handled here. | |||
| This function's result is returned from | |||
| ARA::PlugIn::DocumentControllerDelegate::doIsAudioModificationContentAvailable. | |||
| */ | |||
| virtual bool doIsAudioModificationContentAvailable (const ARA::PlugIn::AudioModification* audioModification, | |||
| ARA::ARAContentType type); | |||
| /** Override to implement getAudioModificationContentGrade() for all your supported content types. | |||
| For read-only data directly inherited from the underlying audio source you can just delegate the | |||
| call to the audio source, but user-editable modification data must be specifically handled here. | |||
| This function's result is returned from | |||
| ARA::PlugIn::DocumentControllerDelegate::doGetAudioModificationContentGrade. | |||
| */ | |||
| virtual ARA::ARAContentGrade doGetAudioModificationContentGrade (const ARA::PlugIn::AudioModification* audioModification, | |||
| ARA::ARAContentType type); | |||
| /** Override to implement createAudioModificationContentReader() for all your supported content types, | |||
| returning a custom subclass instance of ContentReader providing data of the requested \p type. | |||
| For read-only data directly inherited from the underlying audio source you can just delegate the | |||
| call to the audio source, but user-editable modification data must be specifically handled here. | |||
| This function's result is returned from | |||
| ARA::PlugIn::DocumentControllerDelegate::doCreateAudioModificationContentReader. | |||
| */ | |||
| virtual ARA::PlugIn::ContentReader* doCreateAudioModificationContentReader (ARA::PlugIn::AudioModification* audioModification, | |||
| ARA::ARAContentType type, | |||
| const ARA::ARAContentTimeRange* range); | |||
| //============================================================================== | |||
| // ARAPlaybackRegion content access | |||
| /** Override to implement isPlaybackRegionContentAvailable() for all your supported content types - | |||
| the default implementation always returns false. | |||
| Typically, this call can directly delegate to the underlying audio modification, since most | |||
| plug-ins will apply their modification data to the playback region with a transformation that | |||
| does not affect content availability. | |||
| This function's result is returned from | |||
| ARA::PlugIn::DocumentControllerDelegate::doIsPlaybackRegionContentAvailable. | |||
| */ | |||
| virtual bool doIsPlaybackRegionContentAvailable (const ARA::PlugIn::PlaybackRegion* playbackRegion, | |||
| ARA::ARAContentType type); | |||
| /** Override to implement getPlaybackRegionContentGrade() for all your supported content types. | |||
| Typically, this call can directly delegate to the underlying audio modification, since most | |||
| plug-ins will apply their modification data to the playback region with a transformation that | |||
| does not affect content grade. | |||
| This function's result is returned from | |||
| ARA::PlugIn::DocumentControllerDelegate::doGetPlaybackRegionContentGrade. | |||
| */ | |||
| virtual ARA::ARAContentGrade doGetPlaybackRegionContentGrade (const ARA::PlugIn::PlaybackRegion* playbackRegion, | |||
| ARA::ARAContentType type); | |||
| /** Override to implement createPlaybackRegionContentReader() for all your supported content types, | |||
| returning a custom subclass instance of ContentReader providing data of the requested type. | |||
| This function's result is returned from | |||
| ARA::PlugIn::DocumentControllerDelegate::doCreatePlaybackRegionContentReader. | |||
| */ | |||
| virtual ARA::PlugIn::ContentReader* doCreatePlaybackRegionContentReader (ARA::PlugIn::PlaybackRegion* playbackRegion, | |||
| ARA::ARAContentType type, | |||
| const ARA::ARAContentTimeRange* range); | |||
| //============================================================================== | |||
| // ARAAudioSource analysis | |||
| /** Override to implement isAudioSourceContentAnalysisIncomplete(). | |||
| This function's result is returned from | |||
| ARA::PlugIn::DocumentControllerDelegate::doIsAudioSourceContentAnalysisIncomplete. | |||
| */ | |||
| virtual bool doIsAudioSourceContentAnalysisIncomplete (const ARA::PlugIn::AudioSource* audioSource, | |||
| ARA::ARAContentType type); | |||
| /** Override to implement requestAudioSourceContentAnalysis(). | |||
| This function's called from | |||
| ARA::PlugIn::DocumentControllerDelegate::doRequestAudioSourceContentAnalysis. | |||
| */ | |||
| virtual void doRequestAudioSourceContentAnalysis (ARA::PlugIn::AudioSource* audioSource, | |||
| std::vector<ARA::ARAContentType> const& contentTypes); | |||
| //============================================================================== | |||
| // Analysis Algorithm selection | |||
| /** Override to implement getProcessingAlgorithmsCount(). | |||
| This function's result is returned from | |||
| ARA::PlugIn::DocumentControllerDelegate::doGetProcessingAlgorithmsCount. | |||
| */ | |||
| virtual ARA::ARAInt32 doGetProcessingAlgorithmsCount (); | |||
| /** Override to implement getProcessingAlgorithmProperties(). | |||
| This function's result is returned from | |||
| ARA::PlugIn::DocumentControllerDelegate::doGetProcessingAlgorithmProperties. | |||
| */ | |||
| virtual const ARA::ARAProcessingAlgorithmProperties* | |||
| doGetProcessingAlgorithmProperties (ARA::ARAInt32 algorithmIndex); | |||
| /** Override to implement getProcessingAlgorithmForAudioSource(). | |||
| This function's result is returned from | |||
| ARA::PlugIn::DocumentControllerDelegate::doGetProcessingAlgorithmForAudioSource. | |||
| */ | |||
| virtual ARA::ARAInt32 doGetProcessingAlgorithmForAudioSource (const ARA::PlugIn::AudioSource* audioSource); | |||
| /** Override to implement requestProcessingAlgorithmForAudioSource(). | |||
| This function's called from | |||
| ARA::PlugIn::DocumentControllerDelegate::doRequestProcessingAlgorithmForAudioSource. | |||
| */ | |||
| virtual void doRequestProcessingAlgorithmForAudioSource (ARA::PlugIn::AudioSource* audioSource, ARA::ARAInt32 algorithmIndex); | |||
| //============================================================================== | |||
| /** Override to return a custom subclass instance of ARADocument. */ | |||
| ARADocument* doCreateDocument(); | |||
| /** Override to return a custom subclass instance of ARAMusicalContext. */ | |||
| ARAMusicalContext* doCreateMusicalContext (ARADocument* document, | |||
| ARA::ARAMusicalContextHostRef hostRef); | |||
| /** Override to return a custom subclass instance of ARARegionSequence. */ | |||
| ARARegionSequence* doCreateRegionSequence (ARADocument* document, | |||
| ARA::ARARegionSequenceHostRef hostRef); | |||
| /** Override to return a custom subclass instance of ARAAudioSource. */ | |||
| ARAAudioSource* doCreateAudioSource (ARADocument* document, | |||
| ARA::ARAAudioSourceHostRef hostRef); | |||
| /** Override to return a custom subclass instance of ARAAudioModification. */ | |||
| ARAAudioModification* doCreateAudioModification (ARAAudioSource* audioSource, | |||
| ARA::ARAAudioModificationHostRef hostRef, | |||
| const ARAAudioModification* optionalModificationToClone); | |||
| /** Override to return a custom subclass instance of ARAPlaybackRegion. */ | |||
| ARAPlaybackRegion* doCreatePlaybackRegion (ARAAudioModification* modification, | |||
| ARA::ARAPlaybackRegionHostRef hostRef); | |||
| private: | |||
| //============================================================================== | |||
| template <typename SpecialisationType> | |||
| class FactoryConfig : public ARA::PlugIn::FactoryConfig | |||
| { | |||
| public: | |||
| FactoryConfig() noexcept | |||
| { | |||
| const juce::String compatibleDocumentArchiveIDString = JucePlugin_ARACompatibleArchiveIDs; | |||
| if (compatibleDocumentArchiveIDString.isNotEmpty()) | |||
| { | |||
| compatibleDocumentArchiveIDStrings = juce::StringArray::fromLines (compatibleDocumentArchiveIDString); | |||
| for (const auto& compatibleID : compatibleDocumentArchiveIDStrings) | |||
| compatibleDocumentArchiveIDs.push_back (compatibleID.toRawUTF8()); | |||
| } | |||
| // Update analyzeable content types | |||
| static constexpr std::array<ARA::ARAContentType, 6> contentTypes { | |||
| ARA::kARAContentTypeNotes, | |||
| ARA::kARAContentTypeTempoEntries, | |||
| ARA::kARAContentTypeBarSignatures, | |||
| ARA::kARAContentTypeStaticTuning, | |||
| ARA::kARAContentTypeKeySignatures, | |||
| ARA::kARAContentTypeSheetChords | |||
| }; | |||
| JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6313) | |||
| for (size_t i = 0; i < contentTypes.size(); ++i) | |||
| if (JucePlugin_ARAContentTypes & (1 << i)) | |||
| analyzeableContentTypes.push_back (contentTypes[i]); | |||
| JUCE_END_IGNORE_WARNINGS_MSVC | |||
| // Update playback transformation flags | |||
| static constexpr std::array<ARA::ARAPlaybackTransformationFlags, 4> playbackTransformationFlags { | |||
| ARA::kARAPlaybackTransformationTimestretch, | |||
| ARA::kARAPlaybackTransformationTimestretchReflectingTempo, | |||
| ARA::kARAPlaybackTransformationContentBasedFadeAtTail, | |||
| ARA::kARAPlaybackTransformationContentBasedFadeAtHead | |||
| }; | |||
| supportedPlaybackTransformationFlags = 0; | |||
| JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6313) | |||
| for (size_t i = 0; i < playbackTransformationFlags.size(); ++i) | |||
| if (JucePlugin_ARATransformationFlags & (1 << i)) | |||
| supportedPlaybackTransformationFlags |= playbackTransformationFlags[i]; | |||
| JUCE_END_IGNORE_WARNINGS_MSVC | |||
| } | |||
| const char* getFactoryID() const noexcept override { return JucePlugin_ARAFactoryID; } | |||
| const char* getPlugInName() const noexcept override { return JucePlugin_Name; } | |||
| const char* getManufacturerName() const noexcept override { return JucePlugin_Manufacturer; } | |||
| const char* getInformationURL() const noexcept override { return JucePlugin_ManufacturerWebsite; } | |||
| const char* getVersion() const noexcept override { return JucePlugin_VersionString; } | |||
| const char* getDocumentArchiveID() const noexcept override { return JucePlugin_ARADocumentArchiveID; } | |||
| ARA::ARASize getCompatibleDocumentArchiveIDsCount() const noexcept override | |||
| { | |||
| return compatibleDocumentArchiveIDs.size(); | |||
| } | |||
| const ARA::ARAPersistentID* getCompatibleDocumentArchiveIDs() const noexcept override | |||
| { | |||
| return compatibleDocumentArchiveIDs.empty() ? nullptr : compatibleDocumentArchiveIDs.data(); | |||
| } | |||
| ARA::ARASize getAnalyzeableContentTypesCount() const noexcept override | |||
| { | |||
| return analyzeableContentTypes.size(); | |||
| } | |||
| const ARA::ARAContentType* getAnalyzeableContentTypes() const noexcept override | |||
| { | |||
| return analyzeableContentTypes.empty() ? nullptr : analyzeableContentTypes.data(); | |||
| } | |||
| ARA::ARAPlaybackTransformationFlags getSupportedPlaybackTransformationFlags() const noexcept override | |||
| { | |||
| return supportedPlaybackTransformationFlags; | |||
| } | |||
| ARA::PlugIn::DocumentController* createDocumentController (const ARA::PlugIn::PlugInEntry* entry, | |||
| const ARA::ARADocumentControllerHostInstance* instance) const noexcept override | |||
| { | |||
| auto* spec = new SpecialisationType (entry, instance); | |||
| return spec->getDocumentController(); | |||
| } | |||
| void destroyDocumentController (ARA::PlugIn::DocumentController* controller) const noexcept override | |||
| { | |||
| delete getSpecialisedDocumentController (controller); | |||
| } | |||
| private: | |||
| juce::StringArray compatibleDocumentArchiveIDStrings; | |||
| std::vector<ARA::ARAPersistentID> compatibleDocumentArchiveIDs; | |||
| std::vector<ARA::ARAContentType> analyzeableContentTypes; | |||
| ARA::ARAPlaybackTransformationFlags supportedPlaybackTransformationFlags; | |||
| }; | |||
| //============================================================================== | |||
| static ARADocumentControllerSpecialisation* getSpecialisedDocumentControllerImpl (ARA::PlugIn::DocumentController*); | |||
| ARADocument* getDocumentImpl(); | |||
| //============================================================================== | |||
| class ARADocumentControllerImpl; | |||
| std::unique_ptr<ARADocumentControllerImpl> documentController; | |||
| }; | |||
| /** Used to read persisted ARA archives - see doRestoreObjectsFromStream() for details. | |||
| @tags{ARA} | |||
| */ | |||
| class ARAInputStream : public InputStream | |||
| { | |||
| public: | |||
| explicit ARAInputStream (ARA::PlugIn::HostArchiveReader*); | |||
| int64 getPosition() override { return position; } | |||
| int64 getTotalLength() override { return size; } | |||
| int read (void*, int) override; | |||
| bool setPosition (int64) override; | |||
| bool isExhausted() override; | |||
| bool failed() const { return failure; } | |||
| private: | |||
| ARA::PlugIn::HostArchiveReader* archiveReader; | |||
| int64 position = 0; | |||
| int64 size; | |||
| bool failure = false; | |||
| }; | |||
| /** Used to write persistent ARA archives - see doStoreObjectsToStream() for details. | |||
| @tags{ARA} | |||
| */ | |||
| class ARAOutputStream : public OutputStream | |||
| { | |||
| public: | |||
| explicit ARAOutputStream (ARA::PlugIn::HostArchiveWriter*); | |||
| int64 getPosition() override { return position; } | |||
| void flush() override {} | |||
| bool write (const void*, size_t) override; | |||
| bool setPosition (int64) override; | |||
| private: | |||
| ARA::PlugIn::HostArchiveWriter* archiveWriter; | |||
| int64 position = 0; | |||
| }; | |||
| } // namespace juce | |||
| @@ -0,0 +1,64 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE 7 technical preview. | |||
| Copyright (c) 2022 - Raw Material Software Limited | |||
| You may use this code under the terms of the GPL v3 | |||
| (see www.gnu.org/licenses). | |||
| For the technical preview this file cannot be licensed commercially. | |||
| JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
| EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
| DISCLAIMED. | |||
| ============================================================================== | |||
| */ | |||
| namespace juce | |||
| { | |||
| class ARADocumentController : public ARA::PlugIn::DocumentController | |||
| { | |||
| public: | |||
| using ARA::PlugIn::DocumentController::DocumentController; | |||
| template <typename Document_t = ARADocument> | |||
| Document_t* getDocument() const noexcept { return ARA::PlugIn::DocumentController::getDocument<Document_t>(); } | |||
| //============================================================================== | |||
| /** @internal */ | |||
| virtual void internalNotifyAudioSourceAnalysisProgressStarted (ARAAudioSource* audioSource) = 0; | |||
| /** @internal */ | |||
| virtual void internalNotifyAudioSourceAnalysisProgressUpdated (ARAAudioSource* audioSource, float progress) = 0; | |||
| /** @internal */ | |||
| virtual void internalNotifyAudioSourceAnalysisProgressCompleted (ARAAudioSource* audioSource) = 0; | |||
| /** @internal */ | |||
| virtual void internalDidUpdateAudioSourceAnalysisProgress (ARAAudioSource* audioSource, | |||
| ARAAudioSource::ARAAnalysisProgressState state, | |||
| float progress) = 0; | |||
| //============================================================================== | |||
| /** @internal */ | |||
| virtual void internalNotifyAudioSourceContentChanged (ARAAudioSource* audioSource, | |||
| ARAContentUpdateScopes scopeFlags, | |||
| bool notifyARAHost) = 0; | |||
| /** @internal */ | |||
| virtual void internalNotifyAudioModificationContentChanged (ARAAudioModification* audioModification, | |||
| ARAContentUpdateScopes scopeFlags, | |||
| bool notifyARAHost) = 0; | |||
| /** @internal */ | |||
| virtual void internalNotifyPlaybackRegionContentChanged (ARAPlaybackRegion* playbackRegion, | |||
| ARAContentUpdateScopes scopeFlags, | |||
| bool notifyARAHost) = 0; | |||
| friend class ARAPlaybackRegionReader; | |||
| }; | |||
| } // namespace juce | |||
| @@ -0,0 +1,192 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE 7 technical preview. | |||
| Copyright (c) 2022 - Raw Material Software Limited | |||
| You may use this code under the terms of the GPL v3 | |||
| (see www.gnu.org/licenses). | |||
| For the technical preview this file cannot be licensed commercially. | |||
| JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
| EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
| DISCLAIMED. | |||
| ============================================================================== | |||
| */ | |||
| namespace juce | |||
| { | |||
| //============================================================================== | |||
| size_t ARADocument::getNumChildren() const noexcept | |||
| { | |||
| return getMusicalContexts().size() + getRegionSequences().size() + getAudioSources().size(); | |||
| } | |||
| ARAObject* ARADocument::getChild (size_t index) | |||
| { | |||
| auto& musicalContexts = getMusicalContexts(); | |||
| if (index < musicalContexts.size()) | |||
| return musicalContexts[index]; | |||
| const auto numMusicalContexts = musicalContexts.size(); | |||
| auto& regionSequences = getRegionSequences(); | |||
| if (index < numMusicalContexts + regionSequences.size()) | |||
| return regionSequences[index - numMusicalContexts]; | |||
| const auto numMusicalContextsAndRegionSequences = numMusicalContexts + regionSequences.size(); | |||
| auto& audioSources = getAudioSources(); | |||
| if (index < numMusicalContextsAndRegionSequences + audioSources.size()) | |||
| return getAudioSources()[index - numMusicalContextsAndRegionSequences]; | |||
| return nullptr; | |||
| } | |||
| //============================================================================== | |||
| size_t ARARegionSequence::getNumChildren() const noexcept | |||
| { | |||
| return 0; | |||
| } | |||
| ARAObject* ARARegionSequence::getChild (size_t) | |||
| { | |||
| return nullptr; | |||
| } | |||
| Range<double> ARARegionSequence::getTimeRange (ARAPlaybackRegion::IncludeHeadAndTail includeHeadAndTail) const | |||
| { | |||
| if (getPlaybackRegions().empty()) | |||
| return {}; | |||
| auto startTime = std::numeric_limits<double>::max(); | |||
| auto endTime = std::numeric_limits<double>::lowest(); | |||
| for (const auto& playbackRegion : getPlaybackRegions()) | |||
| { | |||
| const auto regionTimeRange = playbackRegion->getTimeRange (includeHeadAndTail); | |||
| startTime = jmin (startTime, regionTimeRange.getStart()); | |||
| endTime = jmax (endTime, regionTimeRange.getEnd()); | |||
| } | |||
| return { startTime, endTime }; | |||
| } | |||
| double ARARegionSequence::getCommonSampleRate() const | |||
| { | |||
| const auto getSampleRate = [] (auto* playbackRegion) | |||
| { | |||
| return playbackRegion->getAudioModification()->getAudioSource()->getSampleRate(); | |||
| }; | |||
| const auto range = getPlaybackRegions(); | |||
| const auto sampleRate = range.size() > 0 ? getSampleRate (range.front()) : 0.0; | |||
| if (std::any_of (range.begin(), range.end(), [&] (auto& x) { return getSampleRate (x) != sampleRate; })) | |||
| return 0.0; | |||
| return sampleRate; | |||
| } | |||
| //============================================================================== | |||
| size_t ARAAudioSource::getNumChildren() const noexcept | |||
| { | |||
| return getAudioModifications().size(); | |||
| } | |||
| ARAObject* ARAAudioSource::getChild (size_t index) | |||
| { | |||
| return getAudioModifications()[index]; | |||
| } | |||
| void ARAAudioSource::notifyAnalysisProgressStarted() | |||
| { | |||
| getDocumentController<ARADocumentController>()->internalNotifyAudioSourceAnalysisProgressStarted (this); | |||
| } | |||
| void ARAAudioSource::notifyAnalysisProgressUpdated (float progress) | |||
| { | |||
| getDocumentController<ARADocumentController>()->internalNotifyAudioSourceAnalysisProgressUpdated (this, progress); | |||
| } | |||
| void ARAAudioSource::notifyAnalysisProgressCompleted() | |||
| { | |||
| getDocumentController<ARADocumentController>()->internalNotifyAudioSourceAnalysisProgressCompleted (this); | |||
| } | |||
| void ARAAudioSource::notifyContentChanged (ARAContentUpdateScopes scopeFlags, bool notifyARAHost) | |||
| { | |||
| getDocumentController<ARADocumentController>()->internalNotifyAudioSourceContentChanged (this, | |||
| scopeFlags, | |||
| notifyARAHost); | |||
| } | |||
| //============================================================================== | |||
| size_t ARAAudioModification::getNumChildren() const noexcept | |||
| { | |||
| return getPlaybackRegions().size(); | |||
| } | |||
| ARAObject* ARAAudioModification::getChild (size_t index) | |||
| { | |||
| return getPlaybackRegions()[index]; | |||
| } | |||
| void ARAAudioModification::notifyContentChanged (ARAContentUpdateScopes scopeFlags, bool notifyARAHost) | |||
| { | |||
| getDocumentController<ARADocumentController>()->internalNotifyAudioModificationContentChanged (this, | |||
| scopeFlags, | |||
| notifyARAHost); | |||
| } | |||
| //============================================================================== | |||
| ARAObject* ARAPlaybackRegion::getParent() { return getAudioModification(); } | |||
| Range<double> ARAPlaybackRegion::getTimeRange (IncludeHeadAndTail includeHeadAndTail) const | |||
| { | |||
| auto startTime = getStartInPlaybackTime(); | |||
| auto endTime = getEndInPlaybackTime(); | |||
| if (includeHeadAndTail == IncludeHeadAndTail::yes) | |||
| { | |||
| ARA::ARATimeDuration headTime {}, tailTime {}; | |||
| getDocumentController()->getPlaybackRegionHeadAndTailTime (toRef (this), &headTime, &tailTime); | |||
| startTime -= headTime; | |||
| endTime += tailTime; | |||
| } | |||
| return { startTime, endTime }; | |||
| } | |||
| Range<int64> ARAPlaybackRegion::getSampleRange (double sampleRate, IncludeHeadAndTail includeHeadAndTail) const | |||
| { | |||
| const auto timeRange = getTimeRange (includeHeadAndTail); | |||
| return { ARA::samplePositionAtTime (timeRange.getStart(), sampleRate), | |||
| ARA::samplePositionAtTime (timeRange.getEnd(), sampleRate) }; | |||
| } | |||
| double ARAPlaybackRegion::getHeadTime() const | |||
| { | |||
| ARA::ARATimeDuration headTime {}, tailTime {}; | |||
| getDocumentController()->getPlaybackRegionHeadAndTailTime (toRef (this), &headTime, &tailTime); | |||
| return headTime; | |||
| } | |||
| double ARAPlaybackRegion::getTailTime() const | |||
| { | |||
| ARA::ARATimeDuration headTime {}, tailTime {}; | |||
| getDocumentController()->getPlaybackRegionHeadAndTailTime (toRef (this), &headTime, &tailTime); | |||
| return tailTime; | |||
| } | |||
| void ARAPlaybackRegion::notifyContentChanged (ARAContentUpdateScopes scopeFlags, bool notifyARAHost) | |||
| { | |||
| getDocumentController<ARADocumentController>()->internalNotifyPlaybackRegionContentChanged (this, | |||
| scopeFlags, | |||
| notifyARAHost); | |||
| } | |||
| } // namespace juce | |||
| @@ -0,0 +1,85 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE 7 technical preview. | |||
| Copyright (c) 2022 - Raw Material Software Limited | |||
| You may use this code under the terms of the GPL v3 | |||
| (see www.gnu.org/licenses). | |||
| For the technical preview this file cannot be licensed commercially. | |||
| JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
| EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
| DISCLAIMED. | |||
| ============================================================================== | |||
| */ | |||
| #include "juce_ARAPlugInInstanceRoles.h" | |||
| namespace juce | |||
| { | |||
| bool ARARenderer::processBlock (AudioBuffer<double>& buffer, | |||
| AudioProcessor::Realtime realtime, | |||
| const AudioPlayHead::CurrentPositionInfo& positionInfo) noexcept | |||
| { | |||
| ignoreUnused (buffer, realtime, positionInfo); | |||
| // If you hit this assertion then either the caller called the double | |||
| // precision version of processBlock on a processor which does not support it | |||
| // (i.e. supportsDoublePrecisionProcessing() returns false), or the implementation | |||
| // of the ARARenderer forgot to override the double precision version of this method | |||
| jassertfalse; | |||
| return false; | |||
| } | |||
| //============================================================================== | |||
| #if ARA_VALIDATE_API_CALLS | |||
| void ARAPlaybackRenderer::addPlaybackRegion (ARA::ARAPlaybackRegionRef playbackRegionRef) noexcept | |||
| { | |||
| if (araExtension) | |||
| ARA_VALIDATE_API_STATE (! araExtension->isPrepared); | |||
| ARA::PlugIn::PlaybackRenderer::addPlaybackRegion (playbackRegionRef); | |||
| } | |||
| void ARAPlaybackRenderer::removePlaybackRegion (ARA::ARAPlaybackRegionRef playbackRegionRef) noexcept | |||
| { | |||
| if (araExtension) | |||
| ARA_VALIDATE_API_STATE (! araExtension->isPrepared); | |||
| ARA::PlugIn::PlaybackRenderer::removePlaybackRegion (playbackRegionRef); | |||
| } | |||
| #endif | |||
| //============================================================================== | |||
| void ARAEditorView::doNotifySelection (const ARA::PlugIn::ViewSelection* viewSelection) noexcept | |||
| { | |||
| listeners.call ([&] (Listener& l) | |||
| { | |||
| l.onNewSelection (*viewSelection); | |||
| }); | |||
| } | |||
| void ARAEditorView::doNotifyHideRegionSequences (std::vector<ARA::PlugIn::RegionSequence*> const& regionSequences) noexcept | |||
| { | |||
| listeners.call ([&] (Listener& l) | |||
| { | |||
| l.onHideRegionSequences (ARA::vector_cast<ARARegionSequence*> (regionSequences)); | |||
| }); | |||
| } | |||
| void ARAEditorView::addListener (Listener* l) | |||
| { | |||
| listeners.add (l); | |||
| } | |||
| void ARAEditorView::removeListener (Listener* l) | |||
| { | |||
| listeners.remove (l); | |||
| } | |||
| } // namespace juce | |||
| @@ -0,0 +1,246 @@ | |||
| #pragma once | |||
| namespace juce | |||
| { | |||
| //============================================================================== | |||
| /** Base class for a renderer fulfilling either the ARAPlaybackRenderer or the ARAEditorRenderer role. | |||
| Instances of either subclass are constructed by the DocumentController. | |||
| @tags{ARA} | |||
| */ | |||
| class JUCE_API ARARenderer | |||
| { | |||
| public: | |||
| enum class AlwaysNonRealtime { no, yes }; | |||
| virtual ~ARARenderer() = default; | |||
| /** Initialises the renderer for playback. | |||
| @param sampleRate The sample rate that will be used for the data that is sent | |||
| to the renderer | |||
| @param maximumSamplesPerBlock The maximum number of samples that will be in the blocks | |||
| sent to process() method | |||
| @param numChannels The number of channels that the process() method will be | |||
| expected to handle | |||
| @param precision This should be the same as the result of getProcessingPrecision() | |||
| for the enclosing AudioProcessor | |||
| @param alwaysNonRealtime yes if this renderer is never used in realtime (e.g. if | |||
| providing data for views only) | |||
| */ | |||
| virtual void prepareToPlay (double sampleRate, | |||
| int maximumSamplesPerBlock, | |||
| int numChannels, | |||
| AudioProcessor::ProcessingPrecision precision, | |||
| AlwaysNonRealtime alwaysNonRealtime = AlwaysNonRealtime::no) | |||
| { | |||
| ignoreUnused (sampleRate, maximumSamplesPerBlock, numChannels, precision, alwaysNonRealtime); | |||
| } | |||
| /** Frees render resources allocated in prepareToPlay(). */ | |||
| virtual void releaseResources() {} | |||
| /** Resets the internal state variables of the renderer. */ | |||
| virtual void reset() {} | |||
| /** Renders the output into the given buffer. Returns true if rendering executed without error, | |||
| false otherwise. | |||
| @param buffer The output buffer for the rendering. ARAPlaybackRenderers will | |||
| replace the sample data, while ARAEditorRenderer will add to it. | |||
| @param realtime Indicates whether the call is executed under real time constraints. | |||
| The value of this parameter may change from one call to the next, | |||
| and if the value is yes, the rendering may fail if the required | |||
| samples cannot be obtained in time. | |||
| @param positionInfo Current song position, playback state and playback loop location. | |||
| There should be no need to access the bpm, timeSig and ppqPosition | |||
| members in any ARA renderer since ARA provides that information with | |||
| random access in its model graph. | |||
| Returns false if non-ARA fallback rendering is required and true otherwise. | |||
| */ | |||
| virtual bool processBlock (AudioBuffer<float>& buffer, | |||
| AudioProcessor::Realtime realtime, | |||
| const AudioPlayHead::CurrentPositionInfo& positionInfo) noexcept = 0; | |||
| /** Renders the output into the given buffer. Returns true if rendering executed without error, | |||
| false otherwise. | |||
| @param buffer The output buffer for the rendering. ARAPlaybackRenderers will | |||
| replace the sample data, while ARAEditorRenderer will add to it. | |||
| @param realtime Indicates whether the call is executed under real time constraints. | |||
| The value of this parameter may change from one call to the next, | |||
| and if the value is yes, the rendering may fail if the required | |||
| samples cannot be obtained in time. | |||
| @param positionInfo Current song position, playback state and playback loop location. | |||
| There should be no need to access the bpm, timeSig and ppqPosition | |||
| members in any ARA renderer since ARA provides that information with | |||
| random access in its model graph. | |||
| Returns false if non-ARA fallback rendering is required and true otherwise. | |||
| */ | |||
| virtual bool processBlock (AudioBuffer<double>& buffer, | |||
| AudioProcessor::Realtime realtime, | |||
| const AudioPlayHead::CurrentPositionInfo& positionInfo) noexcept; | |||
| }; | |||
| //============================================================================== | |||
| /** Base class for a renderer fulfilling the ARAPlaybackRenderer role as described in the ARA SDK. | |||
| Instances of this class are constructed by the DocumentController. If you are subclassing | |||
| ARAPlaybackRenderer, make sure to call the base class implementation of any overridden function, | |||
| except for processBlock. | |||
| @tags{ARA} | |||
| */ | |||
| class JUCE_API ARAPlaybackRenderer : public ARA::PlugIn::PlaybackRenderer, | |||
| public ARARenderer | |||
| { | |||
| public: | |||
| using ARA::PlugIn::PlaybackRenderer::PlaybackRenderer; | |||
| bool processBlock (AudioBuffer<float>& buffer, | |||
| AudioProcessor::Realtime realtime, | |||
| const AudioPlayHead::CurrentPositionInfo& positionInfo) noexcept override | |||
| { | |||
| ignoreUnused (buffer, realtime, positionInfo); | |||
| return false; | |||
| } | |||
| // Shadowing templated getters to default to JUCE versions of the returned classes | |||
| /** Returns the PlaybackRegions | |||
| * | |||
| * @tparam PlaybackRegion_t | |||
| * @return | |||
| */ | |||
| template <typename PlaybackRegion_t = ARAPlaybackRegion> | |||
| std::vector<PlaybackRegion_t*> const& getPlaybackRegions() const noexcept | |||
| { | |||
| return ARA::PlugIn::PlaybackRenderer::getPlaybackRegions<PlaybackRegion_t>(); | |||
| } | |||
| #if ARA_VALIDATE_API_CALLS | |||
| void addPlaybackRegion (ARA::ARAPlaybackRegionRef playbackRegionRef) noexcept override; | |||
| void removePlaybackRegion (ARA::ARAPlaybackRegionRef playbackRegionRef) noexcept override; | |||
| AudioProcessorARAExtension* araExtension {}; | |||
| #endif | |||
| private: | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ARAPlaybackRenderer) | |||
| }; | |||
| //============================================================================== | |||
| /** Base class for a renderer fulfilling the ARAEditorRenderer role as described in the ARA SDK. | |||
| Instances of this class are constructed by the DocumentController. If you are subclassing | |||
| ARAEditorRenderer, make sure to call the base class implementation of any overridden function, | |||
| except for processBlock. | |||
| @tags{ARA} | |||
| */ | |||
| class JUCE_API ARAEditorRenderer : public ARA::PlugIn::EditorRenderer, | |||
| public ARARenderer | |||
| { | |||
| public: | |||
| using ARA::PlugIn::EditorRenderer::EditorRenderer; | |||
| // Shadowing templated getters to default to JUCE versions of the returned classes | |||
| template <typename PlaybackRegion_t = ARAPlaybackRegion> | |||
| std::vector<PlaybackRegion_t*> const& getPlaybackRegions() const noexcept | |||
| { | |||
| return ARA::PlugIn::EditorRenderer::getPlaybackRegions<PlaybackRegion_t>(); | |||
| } | |||
| template <typename RegionSequence_t = ARARegionSequence> | |||
| std::vector<RegionSequence_t*> const& getRegionSequences() const noexcept | |||
| { | |||
| return ARA::PlugIn::EditorRenderer::getRegionSequences<RegionSequence_t>(); | |||
| } | |||
| // By default, editor renderers will just let the signal pass through unaltered. | |||
| // If you're overriding this to implement actual audio preview, remember to check | |||
| // isNonRealtime of the process context - typically preview is limited to realtime. | |||
| bool processBlock (AudioBuffer<float>& buffer, | |||
| AudioProcessor::Realtime isNonRealtime, | |||
| const AudioPlayHead::CurrentPositionInfo& positionInfo) noexcept override | |||
| { | |||
| ignoreUnused (buffer, isNonRealtime, positionInfo); | |||
| return true; | |||
| } | |||
| private: | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ARAEditorRenderer) | |||
| }; | |||
| //============================================================================== | |||
| /** Base class for a renderer fulfilling the ARAEditorView role as described in the ARA SDK. | |||
| Instances of this class are constructed by the DocumentController. If you are subclassing | |||
| ARAEditorView, make sure to call the base class implementation of overridden functions. | |||
| @tags{ARA} | |||
| */ | |||
| class JUCE_API ARAEditorView : public ARA::PlugIn::EditorView | |||
| { | |||
| public: | |||
| using ARA::PlugIn::EditorView::EditorView; | |||
| // Shadowing templated getters to default to JUCE versions of the returned classes | |||
| template <typename RegionSequence_t = ARARegionSequence> | |||
| std::vector<RegionSequence_t*> const& getHiddenRegionSequences() const noexcept | |||
| { | |||
| return ARA::PlugIn::EditorView::getHiddenRegionSequences<RegionSequence_t>(); | |||
| } | |||
| // Base class implementation must be called if overridden | |||
| void doNotifySelection (const ARA::PlugIn::ViewSelection* currentSelection) noexcept override; | |||
| // Base class implementation must be called if overridden | |||
| void doNotifyHideRegionSequences (std::vector<ARA::PlugIn::RegionSequence*> const& regionSequences) noexcept override; | |||
| /** A base class for listeners that want to know about changes to an ARAEditorView object. | |||
| Use ARAEditorView::addListener() to register your listener with an ARAEditorView. | |||
| */ | |||
| class JUCE_API Listener | |||
| { | |||
| public: | |||
| /** Destructor. */ | |||
| virtual ~Listener() = default; | |||
| ARA_DISABLE_UNREFERENCED_PARAMETER_WARNING_BEGIN | |||
| /** Called when the editor view's selection changes. | |||
| @param viewSelection The current selection state | |||
| */ | |||
| virtual void onNewSelection (const ARA::PlugIn::ViewSelection& viewSelection) | |||
| { | |||
| ignoreUnused (viewSelection); | |||
| } | |||
| /** Called when region sequences are flagged as hidden in the host UI. | |||
| @param regionSequences A vector containing all hidden region sequences. | |||
| */ | |||
| virtual void onHideRegionSequences (std::vector<ARARegionSequence*> const& regionSequences) | |||
| { | |||
| ignoreUnused (regionSequences); | |||
| } | |||
| ARA_DISABLE_UNREFERENCED_PARAMETER_WARNING_END | |||
| }; | |||
| /** \copydoc ARAListenableModelClass::addListener */ | |||
| void addListener (Listener* l); | |||
| /** \copydoc ARAListenableModelClass::removeListener */ | |||
| void removeListener (Listener* l); | |||
| private: | |||
| ListenerList<Listener> listeners; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ARAEditorView) | |||
| }; | |||
| } | |||
| @@ -0,0 +1,25 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE 7 technical preview. | |||
| Copyright (c) 2022 - Raw Material Software Limited | |||
| You may use this code under the terms of the GPL v3 | |||
| (see www.gnu.org/licenses). | |||
| For the technical preview this file cannot be licensed commercially. | |||
| JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
| EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
| DISCLAIMED. | |||
| ============================================================================== | |||
| */ | |||
| #if JucePlugin_Enable_ARA | |||
| #include "juce_ARADocumentControllerCommon.cpp" | |||
| #include "juce_ARADocumentController.cpp" | |||
| #include "juce_ARAModelObjects.cpp" | |||
| #include "juce_ARAPlugInInstanceRoles.cpp" | |||
| #include "juce_AudioProcessor_ARAExtensions.cpp" | |||
| #endif | |||
| @@ -0,0 +1,78 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE 7 technical preview. | |||
| Copyright (c) 2022 - Raw Material Software Limited | |||
| You may use this code under the terms of the GPL v3 | |||
| (see www.gnu.org/licenses). | |||
| For the technical preview this file cannot be licensed commercially. | |||
| JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
| EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
| DISCLAIMED. | |||
| ============================================================================== | |||
| */ | |||
| #if JucePlugin_Enable_ARA | |||
| // Include ARA SDK headers | |||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wgnu-zero-variadic-macro-arguments", | |||
| "-Wunused-parameter") | |||
| JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6387) | |||
| #include <ARA_Library/PlugIn/ARAPlug.h> | |||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||
| JUCE_END_IGNORE_WARNINGS_MSVC | |||
| namespace juce | |||
| { | |||
| using ARAViewSelection = ARA::PlugIn::ViewSelection; | |||
| using ARAContentUpdateScopes = ARA::ContentUpdateScopes; | |||
| using ARARestoreObjectsFilter = ARA::PlugIn::RestoreObjectsFilter; | |||
| using ARAStoreObjectsFilter = ARA::PlugIn::StoreObjectsFilter; | |||
| /** Converts an ARA::ARAUtf8String to a JUCE String. */ | |||
| inline String convertARAString (ARA::ARAUtf8String str) | |||
| { | |||
| return String (CharPointer_UTF8 (str)); | |||
| } | |||
| /** Converts a potentially NULL ARA::ARAUtf8String to a JUCE String. | |||
| Returns the JUCE equivalent of the provided string if it's not nullptr, and the fallback string | |||
| otherwise. | |||
| */ | |||
| inline String convertOptionalARAString (ARA::ARAUtf8String str, const String& fallbackString = String()) | |||
| { | |||
| return (str != nullptr) ? convertARAString (str) : fallbackString; | |||
| } | |||
| /** Converts an ARA::ARAColor* to a JUCE Colour. */ | |||
| inline Colour convertARAColour (const ARA::ARAColor* colour) | |||
| { | |||
| return Colour::fromFloatRGBA (colour->r, colour->g, colour->b, 1.0f); | |||
| } | |||
| /** Converts a potentially NULL ARA::ARAColor* to a JUCE Colour. | |||
| Returns the JUCE equivalent of the provided colour if it's not nullptr, and the fallback colour | |||
| otherwise. | |||
| */ | |||
| inline Colour convertOptionalARAColour (const ARA::ARAColor* colour, const Colour& fallbackColour = Colour()) | |||
| { | |||
| return (colour != nullptr) ? convertARAColour (colour) : fallbackColour; | |||
| } | |||
| } // namespace juce | |||
| #include "juce_ARAModelObjects.h" | |||
| #include "juce_ARADocumentController.h" | |||
| #include "juce_AudioProcessor_ARAExtensions.h" | |||
| #include "juce_ARAPlugInInstanceRoles.h" | |||
| #endif | |||
| @@ -0,0 +1,149 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE 7 technical preview. | |||
| Copyright (c) 2022 - Raw Material Software Limited | |||
| You may use this code under the terms of the GPL v3 | |||
| (see www.gnu.org/licenses). | |||
| For the technical preview this file cannot be licensed commercially. | |||
| JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | |||
| EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | |||
| DISCLAIMED. | |||
| ============================================================================== | |||
| */ | |||
| #include "juce_AudioProcessor_ARAExtensions.h" | |||
| namespace juce | |||
| { | |||
| //============================================================================== | |||
| bool AudioProcessorARAExtension::getTailLengthSecondsForARA (double& tailLength) const | |||
| { | |||
| if (! isBoundToARA()) | |||
| return false; | |||
| tailLength = 0.0; | |||
| if (auto playbackRenderer = getPlaybackRenderer()) | |||
| for (const auto& playbackRegion : playbackRenderer->getPlaybackRegions()) | |||
| tailLength = jmax (tailLength, playbackRegion->getTailTime()); | |||
| return true; | |||
| } | |||
| bool AudioProcessorARAExtension::prepareToPlayForARA (double sampleRate, | |||
| int samplesPerBlock, | |||
| int numChannels, | |||
| AudioProcessor::ProcessingPrecision precision) | |||
| { | |||
| #if ARA_VALIDATE_API_CALLS | |||
| isPrepared = true; | |||
| #endif | |||
| if (! isBoundToARA()) | |||
| return false; | |||
| if (auto playbackRenderer = getPlaybackRenderer()) | |||
| playbackRenderer->prepareToPlay (sampleRate, samplesPerBlock, numChannels, precision); | |||
| if (auto editorRenderer = getEditorRenderer()) | |||
| editorRenderer->prepareToPlay (sampleRate, samplesPerBlock, numChannels, precision); | |||
| return true; | |||
| } | |||
| bool AudioProcessorARAExtension::releaseResourcesForARA() | |||
| { | |||
| #if ARA_VALIDATE_API_CALLS | |||
| isPrepared = false; | |||
| #endif | |||
| if (! isBoundToARA()) | |||
| return false; | |||
| if (auto playbackRenderer = getPlaybackRenderer()) | |||
| playbackRenderer->releaseResources(); | |||
| if (auto editorRenderer = getEditorRenderer()) | |||
| editorRenderer->releaseResources(); | |||
| return true; | |||
| } | |||
| bool AudioProcessorARAExtension::processBlockForARA (AudioBuffer<float>& buffer, | |||
| AudioProcessor::Realtime realtime, | |||
| const AudioPlayHead::CurrentPositionInfo& positionInfo) | |||
| { | |||
| // validate that the host has prepared us before processing | |||
| ARA_VALIDATE_API_STATE (isPrepared); | |||
| if (! isBoundToARA()) | |||
| return false; | |||
| // Render our ARA playback regions for this buffer. | |||
| if (auto playbackRenderer = getPlaybackRenderer()) | |||
| playbackRenderer->processBlock (buffer, realtime, positionInfo); | |||
| // Render our ARA editor regions and sequences for this buffer. | |||
| // This example does not support editor rendering and thus uses the default implementation, | |||
| // which is a no-op and could be omitted in actual plug-ins to optimize performance. | |||
| if (auto editorRenderer = getEditorRenderer()) | |||
| editorRenderer->processBlock (buffer, realtime, positionInfo); | |||
| return true; | |||
| } | |||
| bool AudioProcessorARAExtension::processBlockForARA (AudioBuffer<float>& buffer, | |||
| juce::AudioProcessor::Realtime realtime, | |||
| AudioPlayHead* playhead) | |||
| { | |||
| AudioPlayHead::CurrentPositionInfo positionInfo; | |||
| if (! isBoundToARA() || ! playhead || ! playhead->getCurrentPosition (positionInfo)) | |||
| positionInfo.resetToDefault(); | |||
| return processBlockForARA (buffer, realtime, positionInfo); | |||
| } | |||
| //============================================================================== | |||
| void AudioProcessorARAExtension::didBindToARA() noexcept | |||
| { | |||
| // validate that the ARA binding is not established by the host while prepared to play | |||
| #if ARA_VALIDATE_API_CALLS | |||
| ARA_VALIDATE_API_STATE (! isPrepared); | |||
| if (auto playbackRenderer = getPlaybackRenderer()) | |||
| playbackRenderer->araExtension = this; | |||
| #endif | |||
| #if (! JUCE_DISABLE_ASSERTIONS) | |||
| // validate proper subclassing of the instance role classes | |||
| if (auto playbackRenderer = getPlaybackRenderer()) | |||
| jassert (dynamic_cast<ARAPlaybackRenderer*> (playbackRenderer) != nullptr); | |||
| if (auto editorRenderer = getEditorRenderer()) | |||
| jassert (dynamic_cast<ARAEditorRenderer*> (editorRenderer) != nullptr); | |||
| if (auto editorView = getEditorView()) | |||
| jassert (dynamic_cast<ARAEditorView*> (editorView) != nullptr); | |||
| #endif | |||
| } | |||
| //============================================================================== | |||
| AudioProcessorEditorARAExtension::AudioProcessorEditorARAExtension (AudioProcessor* audioProcessor) | |||
| : araProcessorExtension (dynamic_cast<AudioProcessorARAExtension*> (audioProcessor)) | |||
| { | |||
| if (isARAEditorView()) | |||
| getARAEditorView()->setEditorOpen (true); | |||
| } | |||
| AudioProcessorEditorARAExtension::~AudioProcessorEditorARAExtension() | |||
| { | |||
| if (isARAEditorView()) | |||
| getARAEditorView()->setEditorOpen (false); | |||
| } | |||
| } // namespace juce | |||
| @@ -0,0 +1,179 @@ | |||
| #pragma once | |||
| namespace juce | |||
| { | |||
| class AudioProcessor; | |||
| class ARAPlaybackRenderer; | |||
| class ARAEditorRenderer; | |||
| class ARAEditorView; | |||
| //============================================================================== | |||
| /** Extension class meant to be subclassed by the plugin's implementation of @see AudioProcessor. | |||
| Subclassing AudioProcessorARAExtension allows access to the three possible plugin instance | |||
| roles as defined by the ARA SDK. Hosts can assign any subset of roles to each plugin instance. | |||
| @tags{ARA} | |||
| */ | |||
| class JUCE_API AudioProcessorARAExtension : public ARA::PlugIn::PlugInExtension | |||
| { | |||
| public: | |||
| AudioProcessorARAExtension() = default; | |||
| //============================================================================== | |||
| /** Returns the result of ARA::PlugIn::PlugInExtension::getPlaybackRenderer() with the pointer | |||
| cast to ARAPlaybackRenderer*. | |||
| If you have overridden ARADocumentControllerSpecialisation::doCreatePlaybackRenderer(), | |||
| then you can use the template parameter to cast the pointers to your subclass of | |||
| ARAPlaybackRenderer. | |||
| */ | |||
| template <typename PlaybackRenderer_t = ARAPlaybackRenderer> | |||
| PlaybackRenderer_t* getPlaybackRenderer() const noexcept | |||
| { | |||
| return ARA::PlugIn::PlugInExtension::getPlaybackRenderer<PlaybackRenderer_t>(); | |||
| } | |||
| /** Returns the result of ARA::PlugIn::PlugInExtension::getEditorRenderer() with the pointer | |||
| cast to ARAEditorRenderer*. | |||
| If you have overridden ARADocumentControllerSpecialisation::doCreateEditorRenderer(), | |||
| then you can use the template parameter to cast the pointers to your subclass of | |||
| ARAEditorRenderer. | |||
| */ | |||
| template <typename EditorRenderer_t = ARAEditorRenderer> | |||
| EditorRenderer_t* getEditorRenderer() const noexcept | |||
| { | |||
| return ARA::PlugIn::PlugInExtension::getEditorRenderer<EditorRenderer_t>(); | |||
| } | |||
| /** Returns the result of ARA::PlugIn::PlugInExtension::getEditorView() with the pointer | |||
| cast to ARAEditorView*. | |||
| If you have overridden ARADocumentControllerSpecialisation::doCreateEditorView(), | |||
| then you can use the template parameter to cast the pointers to your subclass of | |||
| ARAEditorView. | |||
| */ | |||
| template <typename EditorView_t = ARAEditorView> | |||
| EditorView_t* getEditorView() const noexcept | |||
| { | |||
| return ARA::PlugIn::PlugInExtension::getEditorView<EditorView_t>(); | |||
| } | |||
| //============================================================================== | |||
| /** Returns true if plugin instance fulfills the ARAPlaybackRenderer role. */ | |||
| bool isPlaybackRenderer() const noexcept | |||
| { | |||
| return ARA::PlugIn::PlugInExtension::getPlaybackRenderer() != nullptr; | |||
| } | |||
| /** Returns true if plugin instance fulfills the ARAEditorRenderer role. */ | |||
| bool isEditorRenderer() const noexcept | |||
| { | |||
| return ARA::PlugIn::PlugInExtension::getEditorRenderer() != nullptr; | |||
| } | |||
| /** Returns true if plugin instance fulfills the ARAEditorView role. */ | |||
| bool isEditorView() const noexcept | |||
| { | |||
| return ARA::PlugIn::PlugInExtension::getEditorView() != nullptr; | |||
| } | |||
| //============================================================================== | |||
| #if ARA_VALIDATE_API_CALLS | |||
| bool isPrepared { false }; | |||
| #endif | |||
| protected: | |||
| /** Implementation helper for AudioProcessor::getTailLengthSeconds(). | |||
| If bound to ARA, this traverses the instance roles to retrieve the respective tail time | |||
| and returns true. Otherwise returns false and leaves tailLength unmodified. | |||
| */ | |||
| bool getTailLengthSecondsForARA (double& tailLength) const; | |||
| /** Implementation helper for AudioProcessor::prepareToPlay(). | |||
| If bound to ARA, this traverses the instance roles to prepare them for play and returns | |||
| true. Otherwise returns false and does nothing. | |||
| */ | |||
| bool prepareToPlayForARA (double sampleRate, | |||
| int samplesPerBlock, | |||
| int numChannels, | |||
| AudioProcessor::ProcessingPrecision precision); | |||
| /** Implementation helper for AudioProcessor::releaseResources(). | |||
| If bound to ARA, this traverses the instance roles to let them release resources and returns | |||
| true. Otherwise returns false and does nothing. | |||
| */ | |||
| bool releaseResourcesForARA(); | |||
| /** Implementation helper for AudioProcessor::processBlock(). | |||
| If bound to ARA, this traverses the instance roles to let them process the block and returns | |||
| true. Otherwise returns false and does nothing. | |||
| Use this overload if your rendering code already has a current positionInfo available. | |||
| */ | |||
| bool processBlockForARA (AudioBuffer<float>& buffer, | |||
| AudioProcessor::Realtime realtime, | |||
| const AudioPlayHead::CurrentPositionInfo& positionInfo); | |||
| /** Implementation helper for AudioProcessor::processBlock(). | |||
| If bound to ARA, this traverses the instance roles to let them process the block and returns | |||
| true. Otherwise returns false and does nothing. | |||
| Use this overload if your rendering code does not have a current positionInfo available. | |||
| */ | |||
| bool processBlockForARA (AudioBuffer<float>& buffer, AudioProcessor::Realtime isNonRealtime, AudioPlayHead* playhead); | |||
| //============================================================================== | |||
| /** Optional hook for derived classes to perform any additional initialization that may be needed. | |||
| If overriding this, make sure you call the base class implementation from your override. | |||
| */ | |||
| void didBindToARA() noexcept override; | |||
| private: | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorARAExtension) | |||
| }; | |||
| //============================================================================== | |||
| /** Extension class meant to be subclassed by the plugin's implementation of @see AudioProcessorEditor. | |||
| Subclassing AudioProcessorARAExtension allows access to the ARAEditorView instance role as | |||
| described by the ARA SDK. | |||
| @tags{ARA} | |||
| */ | |||
| class JUCE_API AudioProcessorEditorARAExtension | |||
| { | |||
| public: | |||
| /** Constructor. */ | |||
| explicit AudioProcessorEditorARAExtension (AudioProcessor* audioProcessor); | |||
| /** \copydoc AudioProcessorARAExtension::getEditorView */ | |||
| template <typename EditorView_t = ARAEditorView> | |||
| EditorView_t* getARAEditorView() const noexcept | |||
| { | |||
| return (this->araProcessorExtension != nullptr) ? this->araProcessorExtension->getEditorView<EditorView_t>() | |||
| : nullptr; | |||
| } | |||
| /** \copydoc AudioProcessorARAExtension::isEditorView */ | |||
| bool isARAEditorView() const noexcept { return getARAEditorView() != nullptr; } | |||
| protected: | |||
| /** Destructor. */ | |||
| ~AudioProcessorEditorARAExtension(); | |||
| private: | |||
| AudioProcessorARAExtension* araProcessorExtension; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorEditorARAExtension) | |||
| }; | |||
| } // namespace juce | |||