@@ -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_WavAudioFormat.cpp" | ||||
#include "codecs/juce_LAMEEncoderAudioFormat.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 | #if JUCE_WINDOWS && JUCE_USE_WINDOWS_MEDIA_FORMAT | ||||
#include "codecs/juce_WindowsMediaAudioFormat.cpp" | #include "codecs/juce_WindowsMediaAudioFormat.cpp" | ||||
#endif | #endif |
@@ -121,3 +121,9 @@ | |||||
#include "codecs/juce_WavAudioFormat.h" | #include "codecs/juce_WavAudioFormat.h" | ||||
#include "codecs/juce_WindowsMediaAudioFormat.h" | #include "codecs/juce_WindowsMediaAudioFormat.h" | ||||
#include "sampler/juce_Sampler.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_AudioProcessorValueTreeState.cpp" | ||||
#include "utilities/juce_PluginHostType.cpp" | #include "utilities/juce_PluginHostType.cpp" | ||||
#include "utilities/juce_NativeScaleFactorNotifier.cpp" | #include "utilities/juce_NativeScaleFactorNotifier.cpp" | ||||
#include "utilities/ARA/juce_ARA_utils.cpp" | |||||
#include "format_types/juce_LV2PluginFormat.cpp" | #include "format_types/juce_LV2PluginFormat.cpp" | ||||
@@ -160,6 +160,7 @@ | |||||
#include "utilities/juce_ParameterAttachments.h" | #include "utilities/juce_ParameterAttachments.h" | ||||
#include "utilities/juce_AudioProcessorValueTreeState.h" | #include "utilities/juce_AudioProcessorValueTreeState.h" | ||||
#include "utilities/juce_PluginHostType.h" | #include "utilities/juce_PluginHostType.h" | ||||
#include "utilities/ARA/juce_ARA_utils.h" | |||||
// This is here to avoid missing-prototype warnings in user code. | // This is here to avoid missing-prototype warnings in user code. | ||||
// If you're implementing a plugin, you should supply a body for | // 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. | 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") | JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wgnu-zero-variadic-macro-arguments", "-Wmissing-prototypes") | ||||
#include <ARA_Library/Debug/ARADebug.c> | #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 |