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