1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

ARAPluginDemo: Fix thread race when PlaybackRegion is modified during playback

This commit is contained in:
attila 2023-01-11 17:12:55 +01:00
parent 043182faa6
commit 0f402bb81f

View file

@ -296,11 +296,18 @@ private:
std::unique_ptr<AudioFormatReader> reader; std::unique_ptr<AudioFormatReader> reader;
}; };
struct ProcessingLockInterface
{
virtual ~ProcessingLockInterface() = default;
virtual ScopedTryReadLock getProcessingLock() = 0;
};
//============================================================================== //==============================================================================
class PlaybackRenderer : public ARAPlaybackRenderer class PlaybackRenderer : public ARAPlaybackRenderer
{ {
public: public:
using ARAPlaybackRenderer::ARAPlaybackRenderer; PlaybackRenderer (ARA::PlugIn::DocumentController* dc, ProcessingLockInterface& lockInterfaceIn)
: ARAPlaybackRenderer (dc), lockInterface (lockInterfaceIn) {}
void prepareToPlay (double sampleRateIn, void prepareToPlay (double sampleRateIn,
int maximumSamplesPerBlockIn, int maximumSamplesPerBlockIn,
@ -351,6 +358,11 @@ public:
AudioProcessor::Realtime realtime, AudioProcessor::Realtime realtime,
const AudioPlayHead::PositionInfo& positionInfo) noexcept override const AudioPlayHead::PositionInfo& positionInfo) noexcept override
{ {
const auto lock = lockInterface.getProcessingLock();
if (! lock.isLocked())
return true;
const auto numSamples = buffer.getNumSamples(); const auto numSamples = buffer.getNumSamples();
jassert (numSamples <= maximumSamplesPerBlock); jassert (numSamples <= maximumSamplesPerBlock);
jassert (numChannels == buffer.getNumChannels()); jassert (numChannels == buffer.getNumChannels());
@ -458,8 +470,7 @@ public:
private: private:
//============================================================================== //==============================================================================
// We're subclassing here only to provide a proper default c'tor for our shared resource ProcessingLockInterface& lockInterface;
SharedResourcePointer<SharedTimeSliceThread> sharedTimesliceThread; SharedResourcePointer<SharedTimeSliceThread> sharedTimesliceThread;
std::map<ARAAudioSource*, PossiblyBufferedReader> audioSourceReaders; std::map<ARAAudioSource*, PossiblyBufferedReader> audioSourceReaders;
bool useBufferedAudioSourceReader = true; bool useBufferedAudioSourceReader = true;
@ -473,8 +484,12 @@ class EditorRenderer : public ARAEditorRenderer,
private ARARegionSequence::Listener private ARARegionSequence::Listener
{ {
public: public:
EditorRenderer (ARA::PlugIn::DocumentController* documentController, const PreviewState* previewStateIn) EditorRenderer (ARA::PlugIn::DocumentController* documentController,
: ARAEditorRenderer (documentController), previewState (previewStateIn), previewBuffer() const PreviewState* previewStateIn,
ProcessingLockInterface& lockInterfaceIn)
: ARAEditorRenderer (documentController),
lockInterface (lockInterfaceIn),
previewState (previewStateIn)
{ {
jassert (previewState != nullptr); jassert (previewState != nullptr);
} }
@ -549,6 +564,11 @@ public:
{ {
ignoreUnused (realtime); ignoreUnused (realtime);
const auto lock = lockInterface.getProcessingLock();
if (! lock.isLocked())
return true;
return asyncConfigCallback.withLock ([&] (bool locked) return asyncConfigCallback.withLock ([&] (bool locked)
{ {
if (! locked) if (! locked)
@ -661,6 +681,7 @@ private:
}); });
} }
ProcessingLockInterface& lockInterface;
const PreviewState* previewState = nullptr; const PreviewState* previewState = nullptr;
AsyncConfigurationCallback asyncConfigCallback { [this] { configure(); } }; AsyncConfigurationCallback asyncConfigCallback { [this] { configure(); } };
double lastPreviewTime = 0.0; double lastPreviewTime = 0.0;
@ -678,7 +699,8 @@ private:
}; };
//============================================================================== //==============================================================================
class ARADemoPluginDocumentControllerSpecialisation : public ARADocumentControllerSpecialisation class ARADemoPluginDocumentControllerSpecialisation : public ARADocumentControllerSpecialisation,
private ProcessingLockInterface
{ {
public: public:
using ARADocumentControllerSpecialisation::ARADocumentControllerSpecialisation; using ARADocumentControllerSpecialisation::ARADocumentControllerSpecialisation;
@ -686,6 +708,16 @@ public:
PreviewState previewState; PreviewState previewState;
protected: protected:
void willBeginEditing (ARADocument*) override
{
processBlockLock.enterWrite();
}
void didEndEditing (ARADocument*) override
{
processBlockLock.exitWrite();
}
ARAAudioModification* doCreateAudioModification (ARAAudioSource* audioSource, ARAAudioModification* doCreateAudioModification (ARAAudioSource* audioSource,
ARA::ARAAudioModificationHostRef hostRef, ARA::ARAAudioModificationHostRef hostRef,
const ARAAudioModification* optionalModificationToClone) noexcept override const ARAAudioModification* optionalModificationToClone) noexcept override
@ -697,12 +729,12 @@ protected:
ARAPlaybackRenderer* doCreatePlaybackRenderer() noexcept override ARAPlaybackRenderer* doCreatePlaybackRenderer() noexcept override
{ {
return new PlaybackRenderer (getDocumentController()); return new PlaybackRenderer (getDocumentController(), *this);
} }
EditorRenderer* doCreateEditorRenderer() noexcept override EditorRenderer* doCreateEditorRenderer() noexcept override
{ {
return new EditorRenderer (getDocumentController(), &previewState); return new EditorRenderer (getDocumentController(), &previewState, *this);
} }
bool doRestoreObjectsFromStream (ARAInputStream& input, bool doRestoreObjectsFromStream (ARAInputStream& input,
@ -779,6 +811,14 @@ protected:
return true; return true;
} }
private:
ScopedTryReadLock getProcessingLock() override
{
return ScopedTryReadLock { processBlockLock };
}
ReadWriteLock processBlockLock;
}; };
struct PlayHeadState struct PlayHeadState