Browse Source

ARA Client: Add ARA plugin model classes for writing plugins

pull/22/head
attila 3 years ago
parent
commit
9ae96e98ca
18 changed files with 4137 additions and 1 deletions
  1. +303
    -0
      modules/juce_audio_formats/format/juce_ARAAudioReaders.cpp
  2. +187
    -0
      modules/juce_audio_formats/format/juce_ARAAudioReaders.h
  3. +5
    -0
      modules/juce_audio_formats/juce_audio_formats.cpp
  4. +6
    -0
      modules/juce_audio_formats/juce_audio_formats.h
  5. +1
    -0
      modules/juce_audio_processors/juce_audio_processors.cpp
  6. +1
    -0
      modules/juce_audio_processors/juce_audio_processors.h
  7. +1
    -1
      modules/juce_audio_processors/juce_audio_processors_ara.cpp
  8. +964
    -0
      modules/juce_audio_processors/utilities/ARA/juce_ARADocumentController.cpp
  9. +513
    -0
      modules/juce_audio_processors/utilities/ARA/juce_ARADocumentController.h
  10. +64
    -0
      modules/juce_audio_processors/utilities/ARA/juce_ARADocumentControllerCommon.cpp
  11. +192
    -0
      modules/juce_audio_processors/utilities/ARA/juce_ARAModelObjects.cpp
  12. +1138
    -0
      modules/juce_audio_processors/utilities/ARA/juce_ARAModelObjects.h
  13. +85
    -0
      modules/juce_audio_processors/utilities/ARA/juce_ARAPlugInInstanceRoles.cpp
  14. +246
    -0
      modules/juce_audio_processors/utilities/ARA/juce_ARAPlugInInstanceRoles.h
  15. +25
    -0
      modules/juce_audio_processors/utilities/ARA/juce_ARA_utils.cpp
  16. +78
    -0
      modules/juce_audio_processors/utilities/ARA/juce_ARA_utils.h
  17. +149
    -0
      modules/juce_audio_processors/utilities/ARA/juce_AudioProcessor_ARAExtensions.cpp
  18. +179
    -0
      modules/juce_audio_processors/utilities/ARA/juce_AudioProcessor_ARAExtensions.h

+ 303
- 0
modules/juce_audio_formats/format/juce_ARAAudioReaders.cpp View File

@@ -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

+ 187
- 0
modules/juce_audio_formats/format/juce_ARAAudioReaders.h View File

@@ -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

+ 5
- 0
modules/juce_audio_formats/juce_audio_formats.cpp View File

@@ -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

+ 6
- 0
modules/juce_audio_formats/juce_audio_formats.h View File

@@ -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

+ 1
- 0
modules/juce_audio_processors/juce_audio_processors.cpp View File

@@ -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"


+ 1
- 0
modules/juce_audio_processors/juce_audio_processors.h View File

@@ -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


+ 1
- 1
modules/juce_audio_processors/juce_audio_processors_ara.cpp View File

@@ -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>


+ 964
- 0
modules/juce_audio_processors/utilities/ARA/juce_ARADocumentController.cpp View File

@@ -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

+ 513
- 0
modules/juce_audio_processors/utilities/ARA/juce_ARADocumentController.h View File

@@ -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

+ 64
- 0
modules/juce_audio_processors/utilities/ARA/juce_ARADocumentControllerCommon.cpp View File

@@ -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

+ 192
- 0
modules/juce_audio_processors/utilities/ARA/juce_ARAModelObjects.cpp View File

@@ -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

+ 1138
- 0
modules/juce_audio_processors/utilities/ARA/juce_ARAModelObjects.h
File diff suppressed because it is too large
View File


+ 85
- 0
modules/juce_audio_processors/utilities/ARA/juce_ARAPlugInInstanceRoles.cpp View File

@@ -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

+ 246
- 0
modules/juce_audio_processors/utilities/ARA/juce_ARAPlugInInstanceRoles.h View File

@@ -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)
};
}

+ 25
- 0
modules/juce_audio_processors/utilities/ARA/juce_ARA_utils.cpp View File

@@ -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

+ 78
- 0
modules/juce_audio_processors/utilities/ARA/juce_ARA_utils.h View File

@@ -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

+ 149
- 0
modules/juce_audio_processors/utilities/ARA/juce_AudioProcessor_ARAExtensions.cpp View File

@@ -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

+ 179
- 0
modules/juce_audio_processors/utilities/ARA/juce_AudioProcessor_ARAExtensions.h View File

@@ -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

Loading…
Cancel
Save