# HG changeset patch # User Chris Cannam # Date 1585912445 -3600 # Node ID 771ec060c1d2af545519a8322d7078778b65fd4f # Parent 7b1d30af4b38537c800debd57e8d0d7a2090c9b4# Parent 1c6c10cc5f73ff32b54197ed1060ad0558ed8197 Merge from branch audio-source-refactor. Pull out auditioning effect wrapper and time stretch wrapper from play source; corresponding changes to plugin memory management etc. diff -r 7b1d30af4b38 -r 771ec060c1d2 audio/AudioCallbackPlaySource.cpp --- a/audio/AudioCallbackPlaySource.cpp Wed Mar 25 12:09:13 2020 +0000 +++ b/audio/AudioCallbackPlaySource.cpp Fri Apr 03 12:14:05 2020 +0100 @@ -16,6 +16,8 @@ #include "AudioCallbackPlaySource.h" #include "AudioGenerator.h" +#include "TimeStretchWrapper.h" +#include "EffectWrapper.h" #include "data/model/Model.h" #include "base/ViewManagerBase.h" @@ -32,9 +34,6 @@ #include "bqvec/VectorOps.h" -#include -using namespace RubberBand; - using breakfastquay::v_zero_channels; #include @@ -73,20 +72,12 @@ m_outputLeft(0.0), m_outputRight(0.0), m_levelsSet(false), - m_auditioningPlugin(nullptr), - m_auditioningPluginBypassed(false), - m_auditioningPluginFailed(false), m_playStartFrame(0), m_playStartFramePassed(false), - m_timeStretcher(nullptr), - m_monoStretcher(nullptr), - m_stretchRatio(1.0), - m_stretchMono(false), - m_stretcherInputCount(0), - m_stretcherInputs(nullptr), - m_stretcherInputSizes(nullptr), m_fillThread(nullptr), - m_resamplerWrapper(nullptr) + m_resamplerWrapper(nullptr), + m_timeStretchWrapper(nullptr), + m_auditioningEffectWrapper(nullptr) { m_viewManager->setAudioPlaySource(this); @@ -135,14 +126,9 @@ delete m_audioGenerator; - for (int i = 0; i < m_stretcherInputCount; ++i) { - delete[] m_stretcherInputs[i]; - } - delete[] m_stretcherInputSizes; - delete[] m_stretcherInputs; - - delete m_timeStretcher; - delete m_monoStretcher; + delete m_timeStretchWrapper; + delete m_auditioningEffectWrapper; + delete m_resamplerWrapper; m_bufferScavenger.scavenge(true); m_pluginScavenger.scavenge(true); @@ -151,6 +137,35 @@ #endif } +breakfastquay::ApplicationPlaybackSource * +AudioCallbackPlaySource::getApplicationPlaybackSource() +{ + QMutexLocker locker(&m_mutex); + + if (m_timeStretchWrapper) { + return m_timeStretchWrapper; + } + + checkWrappers(); + return m_timeStretchWrapper; +} + +void +AudioCallbackPlaySource::checkWrappers() +{ + // to be called only with m_mutex held + + if (!m_resamplerWrapper) { + m_resamplerWrapper = new breakfastquay::ResamplerWrapper(this); + } + if (!m_auditioningEffectWrapper) { + m_auditioningEffectWrapper = new EffectWrapper(m_resamplerWrapper); + } + if (!m_timeStretchWrapper) { + m_timeStretchWrapper = new TimeStretchWrapper(m_auditioningEffectWrapper); + } +} + void AudioCallbackPlaySource::addModel(ModelId modelId) { @@ -250,24 +265,14 @@ if (srChanged) { - SVCERR << "AudioCallbackPlaySource: Source rate changed" << endl; + checkWrappers(); - if (m_resamplerWrapper) { - SVCERR << "AudioCallbackPlaySource: Source sample rate changed to " - << m_sourceSampleRate << ", updating resampler wrapper" << endl; - m_resamplerWrapper->changeApplicationSampleRate - (int(round(m_sourceSampleRate))); - m_resamplerWrapper->reset(); - } - - delete m_timeStretcher; - delete m_monoStretcher; - m_timeStretcher = nullptr; - m_monoStretcher = nullptr; - - if (m_stretchRatio != 1.f) { - setTimeStretch(m_stretchRatio); - } + SVCERR << "AudioCallbackPlaySource: Source sample rate changed to " + << m_sourceSampleRate << ", updating resampler wrapper" + << endl; + m_resamplerWrapper->changeApplicationSampleRate + (int(round(m_sourceSampleRate))); + m_resamplerWrapper->reset(); } rebuildRangeLists(); @@ -483,11 +488,8 @@ m_mutex.lock(); - if (m_timeStretcher) { - m_timeStretcher->reset(); - } - if (m_monoStretcher) { - m_monoStretcher->reset(); + if (m_timeStretchWrapper) { + m_timeStretchWrapper->reset(); } m_readBufferFill = m_writeBufferFill = startFrame; @@ -599,21 +601,12 @@ if (!m_playing) return; - RealTimePluginInstance *ap = m_auditioningPlugin; - if (ap && !m_auditioningPluginBypassed) { - m_auditioningPluginBypassed = true; + if (m_auditioningEffectWrapper && + !m_auditioningEffectWrapper->isBypassed()) { + m_auditioningEffectWrapper->setBypassed(true); emit audioOverloadPluginDisabled(); return; } - - if (m_timeStretcher && - m_timeStretcher->getTimeRatio() < 1.0 && - m_stretcherInputCount > 1 && - m_monoStretcher && !m_stretchMono) { - m_stretchMono = true; - emit audioTimeStretchMultiChannelDisabled(); - return; - } } void @@ -628,28 +621,18 @@ } void -AudioCallbackPlaySource::setResamplerWrapper(breakfastquay::ResamplerWrapper *w) -{ - m_resamplerWrapper = w; - if (m_resamplerWrapper && m_sourceSampleRate != 0) { - m_resamplerWrapper->changeApplicationSampleRate - (int(round(m_sourceSampleRate))); - } -} - -void AudioCallbackPlaySource::setSystemPlaybackBlockSize(int size) { - cout << "AudioCallbackPlaySource::setTarget: Block size -> " << size << endl; + SVDEBUG << "AudioCallbackPlaySource::setTarget: Block size -> " << size << endl; if (size != 0) { m_blockSize = size; } if (size * 4 > m_ringBufferSize) { #ifdef DEBUG_AUDIO_PLAY_SOURCE - cout << "AudioCallbackPlaySource::setTarget: Buffer size " - << size << " > a quarter of ring buffer size " - << m_ringBufferSize << ", calling for more ring buffer" - << endl; + SVCERR << "AudioCallbackPlaySource::setTarget: Buffer size " + << size << " > a quarter of ring buffer size " + << m_ringBufferSize << ", calling for more ring buffer" + << endl; #endif m_ringBufferSize = size * 4; if (m_writeBuffers && !m_writeBuffers->empty()) { @@ -704,9 +687,8 @@ AudioCallbackPlaySource::getCurrentFrame(RealTime latency_t) { // The ring buffers contain data at the source sample rate and all - // processing (including time stretching) happens at this - // rate. Resampling only happens after the audio data leaves this - // class. + // processing here happens at this rate. Resampling only happens + // after the audio data leaves this class. // (But because historically more than one sample rate could have // been involved here, we do latency calculations using RealTime @@ -736,16 +718,6 @@ RealTime inbuffer_t = RealTime::frame2RealTime(inbuffer, rate); - sv_frame_t stretchlat = 0; - double timeRatio = 1.0; - - if (m_timeStretcher) { - stretchlat = m_timeStretcher->getLatency(); - timeRatio = m_timeStretcher->getTimeRatio(); - } - - RealTime stretchlat_t = RealTime::frame2RealTime(stretchlat, rate); - // When the target has just requested a block from us, the last // sample it obtained was our buffer fill frame count minus the // amount of read space (converted back to source sample rate) @@ -784,12 +756,6 @@ RealTime bufferedto_t = RealTime::frame2RealTime(readBufferFill, rate); - if (timeRatio != 1.0) { - lastretrieved_t = lastretrieved_t / timeRatio; - sincerequest_t = sincerequest_t / timeRatio; - latency_t = latency_t / timeRatio; - } - #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING cout << "\nbuffered to: " << bufferedto_t << ", in buffer: " << inbuffer_t << ", device latency: " << latency_t << "\n since request: " << sincerequest_t << ", last retrieved quantity: " << lastretrieved_t << endl; #endif @@ -805,7 +771,7 @@ if (m_rangeStarts.empty()) { // this code is only used in case of error in rebuildRangeLists RealTime playing_t = bufferedto_t - - latency_t - stretchlat_t - lastretrieved_t - inbuffer_t + - latency_t - lastretrieved_t - inbuffer_t + sincerequest_t; if (playing_t < RealTime::zeroTime) playing_t = RealTime::zeroTime; sv_frame_t frame = RealTime::realTime2Frame(playing_t, rate); @@ -831,7 +797,7 @@ RealTime playing_t = bufferedto_t; playing_t = playing_t - - latency_t - stretchlat_t - lastretrieved_t - inbuffer_t + - latency_t - lastretrieved_t - inbuffer_t + sincerequest_t; // This rather gross little hack is used to ensure that latency @@ -849,8 +815,7 @@ if (playing_t < playstart_t) { // cout << "playing_t " << playing_t << " < playstart_t " // << playstart_t << endl; - if (/*!!! sincerequest_t > RealTime::zeroTime && */ - m_playStartedAt + latency_t + stretchlat_t < + if (m_playStartedAt + latency_t < RealTime::fromSeconds(currentTime)) { // cout << "but we've been playing for long enough that I think we should disregard it (it probably results from loop wrapping)" << endl; m_playStartFramePassed = true; @@ -1003,22 +968,23 @@ } void -AudioCallbackPlaySource::setAuditioningEffect(Auditionable *a) +AudioCallbackPlaySource::setAuditioningEffect(std::shared_ptr a) { - RealTimePluginInstance *plugin = dynamic_cast(a); + SVDEBUG << "AudioCallbackPlaySource::setAuditioningEffect(" << a << ")" + << endl; + + auto plugin = std::dynamic_pointer_cast(a); if (a && !plugin) { SVCERR << "WARNING: AudioCallbackPlaySource::setAuditioningEffect: auditionable object " << a << " is not a real-time plugin instance" << endl; } m_mutex.lock(); - m_auditioningPlugin = plugin; - m_auditioningPluginBypassed = false; - m_auditioningPluginFailed = false; + m_auditioningEffectWrapper->setEffect(plugin); + m_auditioningEffectWrapper->setBypassed(false); m_mutex.unlock(); SVDEBUG << "AudioCallbackPlaySource::setAuditioningEffect: set plugin to " - << plugin << " and bypassed to " << m_auditioningPluginBypassed - << endl; + << plugin << endl; } void @@ -1069,35 +1035,10 @@ void AudioCallbackPlaySource::setTimeStretch(double factor) { - m_stretchRatio = factor; + checkWrappers(); - int rate = int(getSourceSampleRate()); - if (!rate) return; // have to make our stretcher later - - if (m_timeStretcher || (factor == 1.0)) { - // stretch ratio will be set in next process call if appropriate - } else { - m_stretcherInputCount = getTargetChannelCount(); - RubberBandStretcher *stretcher = new RubberBandStretcher - (rate, - m_stretcherInputCount, - RubberBandStretcher::OptionProcessRealTime, - factor); - RubberBandStretcher *monoStretcher = new RubberBandStretcher - (rate, - 1, - RubberBandStretcher::OptionProcessRealTime, - factor); - m_stretcherInputs = new float *[m_stretcherInputCount]; - m_stretcherInputSizes = new sv_frame_t[m_stretcherInputCount]; - for (int c = 0; c < m_stretcherInputCount; ++c) { - m_stretcherInputSizes[c] = 16384; - m_stretcherInputs[c] = new float[m_stretcherInputSizes[c]]; - } - m_monoStretcher = monoStretcher; - m_timeStretcher = stretcher; - } - + m_timeStretchWrapper->setTimeStretchRatio(factor); + emit activity(tr("Change time-stretch factor to %1").arg(factor)); } @@ -1202,230 +1143,51 @@ if (count == 0) return 0; - RubberBandStretcher *ts = m_timeStretcher; - RubberBandStretcher *ms = m_monoStretcher; - - double ratio = ts ? ts->getTimeRatio() : 1.0; - - if (ratio != m_stretchRatio) { - if (!ts) { - SVCERR << "WARNING: AudioCallbackPlaySource::getSourceSamples: Time ratio change to " << m_stretchRatio << " is pending, but no stretcher is set" << endl; - m_stretchRatio = 1.0; - } else { - ts->setTimeRatio(m_stretchRatio); - if (ms) ms->setTimeRatio(m_stretchRatio); - if (m_stretchRatio >= 1.0) m_stretchMono = false; - } - } - - int stretchChannels = m_stretcherInputCount; - if (m_stretchMono) { - if (ms) { - ts = ms; - stretchChannels = 1; - } else { - m_stretchMono = false; - } - } - if (m_target) { m_lastRetrievedBlockSize = count; m_lastRetrievalTimestamp = m_target->getCurrentTime(); } - if (!ts || ratio == 1.f) { - - int got = 0; + int got = 0; #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING - cout << "channels == " << channels << endl; + cout << "channels == " << channels << endl; #endif - for (int ch = 0; ch < channels; ++ch) { + for (int ch = 0; ch < channels; ++ch) { - RingBuffer *rb = getReadRingBuffer(ch); + RingBuffer *rb = getReadRingBuffer(ch); - if (rb) { + if (rb) { - // this is marginally more likely to leave our channels in - // sync after a processing failure than just passing "count": - sv_frame_t request = count; - if (ch > 0) request = got; + // this is marginally more likely to leave our channels in + // sync after a processing failure than just passing "count": + sv_frame_t request = count; + if (ch > 0) request = got; - got = rb->read(buffer[ch], int(request)); + got = rb->read(buffer[ch], int(request)); #ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING - cout << "AudioCallbackPlaySource::getSamples: got " << got << " (of " << count << ") samples on channel " << ch << ", signalling for more (possibly)" << endl; + cout << "AudioCallbackPlaySource::getSamples: got " << got << " (of " << count << ") samples on channel " << ch << ", signalling for more (possibly)" << endl; #endif - } + } - for (int ch = 0; ch < channels; ++ch) { - for (int i = got; i < count; ++i) { - buffer[ch][i] = 0.0; - } + for (int ch = 0; ch < channels; ++ch) { + for (int i = got; i < count; ++i) { + buffer[ch][i] = 0.0; } } - - applyAuditioningEffect(count, buffer); + } #ifdef DEBUG_AUDIO_PLAY_SOURCE cout << "AudioCallbackPlaySource::getSamples: awakening thread" << endl; #endif - m_condition.wakeAll(); - - return got; - } - - sv_frame_t available; - sv_frame_t fedToStretcher = 0; - int warned = 0; - - // The input block for a given output is approx output / ratio, - // but we can't predict it exactly, for an adaptive timestretcher. - - while ((available = ts->available()) < count) { - - sv_frame_t reqd = lrint(double(count - available) / ratio); - reqd = std::max(reqd, sv_frame_t(ts->getSamplesRequired())); - if (reqd == 0) reqd = 1; - - sv_frame_t got = reqd; - -#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING - cout << "reqd = " <= m_stretcherInputCount) continue; - RingBuffer *rb = getReadRingBuffer(c); - if (rb) { - sv_frame_t gotHere; - if (stretchChannels == 1 && c > 0) { - gotHere = rb->readAdding(m_stretcherInputs[0], int(got)); - } else { - gotHere = rb->read(m_stretcherInputs[c], int(got)); - } - if (gotHere < got) got = gotHere; - -#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING - if (c == 0) { - cout << "feeding stretcher: got " << gotHere - << ", " << rb->getReadSpace() << " remain" << endl; - } -#endif - - } else { - SVCERR << "WARNING: No ring buffer available for channel " << c << " in stretcher input block" << endl; - } - } - - if (got < reqd) { - SVCERR << "WARNING: Read underrun in playback (" - << got << " < " << reqd << ")" << endl; - } - - ts->process(m_stretcherInputs, size_t(got), false); - - fedToStretcher += got; - - if (got == 0) break; - - if (ts->available() == available) { - SVCERR << "WARNING: AudioCallbackPlaySource::getSamples: Added " << got << " samples to time stretcher, created no new available output samples (warned = " << warned << ")" << endl; - if (++warned == 5) break; - } - } - - ts->retrieve(buffer, size_t(count)); - - v_zero_channels(buffer + stretchChannels, channels - stretchChannels, count); - - applyAuditioningEffect(count, buffer); - -#ifdef DEBUG_AUDIO_PLAY_SOURCE - cout << "AudioCallbackPlaySource::getSamples [stretched]: awakening thread" << endl; -#endif - m_condition.wakeAll(); - return count; + return got; } -void -AudioCallbackPlaySource::applyAuditioningEffect(sv_frame_t count, float *const *buffers) -{ - if (m_auditioningPluginBypassed) return; - RealTimePluginInstance *plugin = m_auditioningPlugin; - if (!plugin) return; - - if ((int)plugin->getAudioInputCount() != getTargetChannelCount()) { - if (!m_auditioningPluginFailed) { - SVCERR << "AudioCallbackPlaySource::applyAuditioningEffect: " - << "Can't run plugin: plugin input count " - << plugin->getAudioInputCount() - << " != our channel count " << getTargetChannelCount() - << " (future errors for this plugin will be suppressed)" - << endl; - m_auditioningPluginFailed = true; - } - return; - } - if ((int)plugin->getAudioOutputCount() != getTargetChannelCount()) { - if (!m_auditioningPluginFailed) { - SVCERR << "AudioCallbackPlaySource::applyAuditioningEffect: " - << "Can't run plugin: plugin output count " - << plugin->getAudioOutputCount() - << " != our channel count " << getTargetChannelCount() - << " (future errors for this plugin will be suppressed)" - << endl; - m_auditioningPluginFailed = true; - } - return; - } - if ((int)plugin->getBufferSize() < count) { - if (!m_auditioningPluginFailed) { - SVCERR << "AudioCallbackPlaySource::applyAuditioningEffect: " - << "Can't run plugin: plugin buffer size " - << plugin->getBufferSize() - << " < our block size " << count - << " (future errors for this plugin will be suppressed)" - << endl; - m_auditioningPluginFailed = true; - } - return; - } - - float **ib = plugin->getAudioInputBuffers(); - float **ob = plugin->getAudioOutputBuffers(); - - for (int c = 0; c < getTargetChannelCount(); ++c) { - for (int i = 0; i < count; ++i) { - ib[c][i] = buffers[c][i]; - } - } - - plugin->run(Vamp::RealTime::zeroTime, int(count)); - - for (int c = 0; c < getTargetChannelCount(); ++c) { - for (int i = 0; i < count; ++i) { - buffers[c][i] = ob[c][i]; - } - } -} - // Called from fill thread, m_playing true, mutex held bool AudioCallbackPlaySource::fillBuffers() diff -r 7b1d30af4b38 -r 771ec060c1d2 audio/AudioCallbackPlaySource.h --- a/audio/AudioCallbackPlaySource.h Wed Mar 25 12:09:13 2020 +0000 +++ b/audio/AudioCallbackPlaySource.h Fri Apr 03 12:14:05 2020 +0100 @@ -36,10 +36,6 @@ #include #include -namespace RubberBand { - class RubberBandStretcher; -} - namespace breakfastquay { class ResamplerWrapper; } @@ -50,6 +46,8 @@ class PlayParameters; class RealTimePluginInstance; class AudioCallbackPlayTarget; +class TimeStretchWrapper; +class EffectWrapper; /** * AudioCallbackPlaySource manages audio data supply to callback-based @@ -67,6 +65,18 @@ public: AudioCallbackPlaySource(ViewManagerBase *, QString clientName); virtual ~AudioCallbackPlaySource(); + + /** + * Return an ApplicationPlaybackSource interface to this + * class. Although this class implements ApplicationPlaybackSource + * itself, the object returned here may be a wrapper which + * provides facilities not implemented in this class, such as + * time-stretching, resampling, and an auditioning effect. The + * returned pointer points to an object which is owned by this + * object. Caller must ensure the lifetime of this object exceeds + * the scope which the returned pointer is retained. + */ + breakfastquay::ApplicationPlaybackSource *getApplicationPlaybackSource(); /** * Add a data model to be played from. The source can mix @@ -124,11 +134,6 @@ * Set the playback target. */ virtual void setSystemPlaybackTarget(breakfastquay::SystemPlaybackTarget *); - - /** - * Set the resampler wrapper, if one is in use. - */ - virtual void setResamplerWrapper(breakfastquay::ResamplerWrapper *); /** * Set the block size of the target audio device. This should be @@ -288,7 +293,8 @@ * Pass a null pointer to remove the current auditioning plugin, * if any. */ - virtual void setAuditioningEffect(Auditionable *plugin) override; + virtual void setAuditioningEffect(std::shared_ptr plugin) + override; /** * Specify that only the given set of models should be played. @@ -315,7 +321,6 @@ void channelCountIncreased(int count); // target channel count (see getTargetChannelCount()) void audioOverloadPluginDisabled(); - void audioTimeStretchMultiChannelDisabled(); void activity(QString); @@ -369,9 +374,6 @@ float m_outputLeft; float m_outputRight; bool m_levelsSet; - RealTimePluginInstance *m_auditioningPlugin; - bool m_auditioningPluginBypassed; - bool m_auditioningPluginFailed; Scavenger m_pluginScavenger; sv_frame_t m_playStartFrame; bool m_playStartFramePassed; @@ -397,15 +399,6 @@ void clearRingBuffers(bool haveLock = false, int count = 0); void unifyRingBuffers(); - RubberBand::RubberBandStretcher *m_timeStretcher; - RubberBand::RubberBandStretcher *m_monoStretcher; - double m_stretchRatio; - bool m_stretchMono; - - int m_stretcherInputCount; - float **m_stretcherInputs; - sv_frame_t *m_stretcherInputSizes; - // Called from fill thread, m_playing true, mutex held // Return true if work done bool fillBuffers(); @@ -416,9 +409,6 @@ // frame argument passed in, in the case of looping). sv_frame_t mixModels(sv_frame_t &frame, sv_frame_t count, float **buffers); - // Called from getSourceSamples. - void applyAuditioningEffect(sv_frame_t count, float *const *buffers); - // Ranges of current selections, if play selection is active std::vector m_rangeStarts; std::vector m_rangeDurations; @@ -442,9 +432,11 @@ QMutex m_mutex; QWaitCondition m_condition; FillThread *m_fillThread; - breakfastquay::ResamplerWrapper *m_resamplerWrapper; // I don't own this + breakfastquay::ResamplerWrapper *m_resamplerWrapper; + TimeStretchWrapper *m_timeStretchWrapper; + EffectWrapper *m_auditioningEffectWrapper; + void checkWrappers(); }; #endif - diff -r 7b1d30af4b38 -r 771ec060c1d2 audio/EffectWrapper.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audio/EffectWrapper.cpp Fri Apr 03 12:14:05 2020 +0100 @@ -0,0 +1,259 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "EffectWrapper.h" + +#include + +#include "base/Debug.h" + +//#define DEBUG_EFFECT_WRAPPER 1 + +using namespace std; + +static const int DEFAULT_RING_BUFFER_SIZE = 131071; + +EffectWrapper::EffectWrapper(ApplicationPlaybackSource *source) : + m_source(source), + m_bypassed(false), + m_failed(false), + m_channelCount(0) +{ +} + +EffectWrapper::~EffectWrapper() +{ +} + +void +EffectWrapper::setEffect(weak_ptr effect) +{ + lock_guard guard(m_mutex); + +#ifdef DEBUG_EFFECT_WRAPPER + SVCERR << "EffectWrapper[" << this + << "]::setEffect(" << effect.lock() << ")" << endl; +#endif + + m_effect = effect; + m_failed = false; +} + +void +EffectWrapper::setBypassed(bool bypassed) +{ + lock_guard guard(m_mutex); + +#ifdef DEBUG_EFFECT_WRAPPER + SVCERR << "EffectWrapper[" << this + << "]::setBypassed(" << bypassed << ")" << endl; +#endif + + m_bypassed = bypassed; +} + +bool +EffectWrapper::isBypassed() const +{ + lock_guard guard(m_mutex); + + return m_bypassed; +} + +void +EffectWrapper::reset() +{ + lock_guard guard(m_mutex); + +#ifdef DEBUG_EFFECT_WRAPPER + SVCERR << "EffectWrapper[" << this << "]::reset" << endl; +#endif + + for (auto &rb: m_effectOutputBuffers) { + rb.reset(); + } + + m_failed = false; +} + +int +EffectWrapper::getSourceSamples(float *const *samples, + int nchannels, int nframes) +{ + lock_guard guard(m_mutex); + +#ifdef DEBUG_EFFECT_WRAPPER + SVCERR << "EffectWrapper[" << this << "]::getSourceSamples: " << nframes + << " frames across " << nchannels << " channels" << endl; +#endif + + auto effect(m_effect.lock()); + + if (!effect) { +#ifdef DEBUG_EFFECT_WRAPPER + SVCERR << "EffectWrapper::getSourceSamples: " + << "no effect is set" << endl; +#endif + return m_source->getSourceSamples(samples, nchannels, nframes); + } + + if (m_bypassed || m_failed) { +#ifdef DEBUG_EFFECT_WRAPPER + SVCERR << "EffectWrapper::getSourceSamples: " + << "effect is bypassed or has failed" << endl; +#endif + return m_source->getSourceSamples(samples, nchannels, nframes); + } + + static int warnings = 0; + if (nchannels != m_channelCount) { + if (warnings >= 0) { + SVCERR << "WARNING: EffectWrapper::getSourceSamples called for a number of channels different from that set with setSystemPlaybackChannelCount (" + << nchannels << " vs " << m_channelCount << ")" << endl; + if (++warnings == 6) { + SVCERR << "(further warnings will be suppressed)" << endl; + warnings = -1; + } + } + return 0; + } + + if ((int)effect->getAudioInputCount() != m_channelCount) { + if (!m_failed) { + SVCERR << "EffectWrapper::getSourceSamples: " + << "Can't run plugin: plugin input count " + << effect->getAudioInputCount() + << " != our channel count " << m_channelCount + << " (future errors for this plugin will be suppressed)" + << endl; + m_failed = true; + } + } + if ((int)effect->getAudioOutputCount() != m_channelCount) { + if (!m_failed) { + SVCERR << "EffectWrapper::getSourceSamples: " + << "Can't run plugin: plugin output count " + << effect->getAudioOutputCount() + << " != our channel count " << m_channelCount + << " (future errors for this plugin will be suppressed)" + << endl; + m_failed = true; + } + } + + if (m_failed) { + return m_source->getSourceSamples(samples, nchannels, nframes); + } + + float **ib = effect->getAudioInputBuffers(); + float **ob = effect->getAudioOutputBuffers(); + int blockSize = effect->getBufferSize(); + + int got = 0; + + while (got < nframes) { + + int read = 0; + for (int c = 0; c < nchannels; ++c) { + read = m_effectOutputBuffers[c].read(samples[c] + got, + nframes - got); + } + + got += read; + + if (got < nframes) { + + int toRun = m_source->getSourceSamples(ib, nchannels, blockSize); + if (toRun <= 0) break; + +#ifdef DEBUG_EFFECT_WRAPPER + SVCERR << "EffectWrapper::getSourceSamples: Running effect " + << "for " << toRun << " frames" << endl; +#endif + effect->run(Vamp::RealTime::zeroTime, toRun); + + for (int c = 0; c < nchannels; ++c) { + m_effectOutputBuffers[c].write(ob[c], toRun); + } + } + } + + return got; +} + +void +EffectWrapper::setSystemPlaybackChannelCount(int count) +{ + { + lock_guard guard(m_mutex); +#ifdef DEBUG_EFFECT_WRAPPER + SVCERR << "EffectWrapper[" << this + << "]::setSystemPlaybackChannelCount(" << count << ")" << endl; +#endif + m_effectOutputBuffers.resize + (count, RingBuffer(DEFAULT_RING_BUFFER_SIZE)); + m_channelCount = count; + } + m_source->setSystemPlaybackChannelCount(count); +} + +void +EffectWrapper::setSystemPlaybackSampleRate(int rate) +{ + m_source->setSystemPlaybackSampleRate(rate); +} + +std::string +EffectWrapper::getClientName() const +{ + return m_source->getClientName(); +} + +int +EffectWrapper::getApplicationSampleRate() const +{ + return m_source->getApplicationSampleRate(); +} + +int +EffectWrapper::getApplicationChannelCount() const +{ + return m_source->getApplicationChannelCount(); +} + +void +EffectWrapper::setSystemPlaybackBlockSize(int sz) +{ + SVDEBUG << "NOTE: EffectWrapper::setSystemPlaybackBlockSize called " + << "with size = " << sz << "; not passing to wrapped source, as " + << "actual block size will vary" << endl; +} + +void +EffectWrapper::setSystemPlaybackLatency(int latency) +{ + m_source->setSystemPlaybackLatency(latency); +} + +void +EffectWrapper::setOutputLevels(float left, float right) +{ + m_source->setOutputLevels(left, right); +} + +void +EffectWrapper::audioProcessingOverload() +{ + m_source->audioProcessingOverload(); +} diff -r 7b1d30af4b38 -r 771ec060c1d2 audio/EffectWrapper.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audio/EffectWrapper.h Fri Apr 03 12:14:05 2020 +0100 @@ -0,0 +1,115 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef SV_EFFECT_WRAPPER_H +#define SV_EFFECT_WRAPPER_H + +#include "bqaudioio/ApplicationPlaybackSource.h" + +#include "base/BaseTypes.h" +#include "base/RingBuffer.h" + +#include "plugin/RealTimePluginInstance.h" + +#include +#include +#include + +/** + * A breakfastquay::ApplicationPlaybackSource wrapper that applies a + * real-time effect plugin. + */ +class EffectWrapper : public breakfastquay::ApplicationPlaybackSource +{ +public: + /** + * Create a wrapper around the given ApplicationPlaybackSource, + * implementing another ApplicationPlaybackSource interface that + * draws from the same source data but with an effect optionally + * applied. + * + * The wrapper does not take ownership of the wrapped + * ApplicationPlaybackSource, whose lifespan must exceed that of + * this object. + */ + EffectWrapper(ApplicationPlaybackSource *source); + ~EffectWrapper(); + + /** + * Set the effect to apply. The effect instance is shared with the + * caller: the expectation is that the caller may continue to + * modify its parameters etc during auditioning. Replaces any + * instance previously set. + */ + void setEffect(std::weak_ptr); + + /** + * Remove any applied effect without setting another one. + */ + void clearEffect(); + + /** + * Bypass or un-bypass the effect. + */ + void setBypassed(bool bypassed); + + /** + * Return true if the effect is bypassed. + */ + bool isBypassed() const; + + /** + * Clear any buffered data. + */ + void reset(); + + // These functions are passed through to the wrapped + // ApplicationPlaybackSource + + std::string getClientName() const override; + int getApplicationSampleRate() const override; + int getApplicationChannelCount() const override; + + void setSystemPlaybackBlockSize(int) override; + void setSystemPlaybackSampleRate(int) override; + void setSystemPlaybackChannelCount(int) override; + void setSystemPlaybackLatency(int) override; + + void setOutputLevels(float peakLeft, float peakRight) override; + void audioProcessingOverload() override; + + /** + * Request some samples from the wrapped + * ApplicationPlaybackSource, apply effect if set, and return them + * to the target + */ + int getSourceSamples(float *const *samples, int nchannels, int nframes) + override; + +private: + ApplicationPlaybackSource *m_source; + std::weak_ptr m_effect; + bool m_bypassed; + bool m_failed; + int m_channelCount; + std::vector> m_effectOutputBuffers; + mutable std::mutex m_mutex; + + EffectWrapper(const EffectWrapper &)=delete; + EffectWrapper &operator=(const EffectWrapper &)=delete; +}; + +#endif + + diff -r 7b1d30af4b38 -r 771ec060c1d2 audio/TimeStretchWrapper.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audio/TimeStretchWrapper.cpp Fri Apr 03 12:14:05 2020 +0100 @@ -0,0 +1,228 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "TimeStretchWrapper.h" + +#include + +#include "base/Debug.h" + +using namespace RubberBand; +using namespace std; + +TimeStretchWrapper::TimeStretchWrapper(ApplicationPlaybackSource *source) : + m_source(source), + m_stretcher(nullptr), + m_timeRatio(1.0), + m_stretcherInputSize(16384), + m_channelCount(0), + m_sampleRate(0) +{ +} + +TimeStretchWrapper::~TimeStretchWrapper() +{ + delete m_stretcher; +} + +void +TimeStretchWrapper::setTimeStretchRatio(double ratio) +{ + lock_guard guard(m_mutex); + + SVDEBUG << "TimeStretchWrapper::setTimeStretchRatio: setting ratio to " + << ratio << " (was " << m_timeRatio << ")" << endl; + + m_timeRatio = ratio; + + // Stretcher will be updated by checkStretcher() from next call to + // getSourceSamples() +} + +void +TimeStretchWrapper::reset() +{ + lock_guard guard(m_mutex); + + if (m_stretcher) { + m_stretcher->reset(); + } +} + +int +TimeStretchWrapper::getSourceSamples(float *const *samples, + int nchannels, int nframes) +{ + checkStretcher(); + + lock_guard guard(m_mutex); + + static int warnings = 0; + if (nchannels != m_channelCount) { + if (warnings >= 0) { + SVCERR << "WARNING: getSourceSamples called for a number of channels different from that set with setSystemPlaybackChannelCount (" + << nchannels << " vs " << m_channelCount << ")" << endl; + if (++warnings == 6) { + SVCERR << "(further warnings will be suppressed)" << endl; + warnings = -1; + } + } + return 0; + } + + if (!m_stretcher) { + return m_source->getSourceSamples(samples, nchannels, nframes); + } + + vector inputPtrs(m_channelCount, nullptr); + for (int i = 0; i < m_channelCount; ++i) { + inputPtrs[i] = m_inputs[i].data(); + } + + // The input block for a given output is approx output / ratio, + // but we can't predict it exactly, for an adaptive timestretcher. + + sv_frame_t available; + + while ((available = m_stretcher->available()) < nframes) { + + int reqd = int(ceil(double(nframes - available) / m_timeRatio)); + reqd = std::max(reqd, int(m_stretcher->getSamplesRequired())); + reqd = std::min(reqd, m_stretcherInputSize); + if (reqd == 0) reqd = 1; + + int got = m_source->getSourceSamples + (inputPtrs.data(), nchannels, reqd); + + if (got <= 0) { + SVCERR << "WARNING: Failed to obtain any source samples at all" + << endl; + return 0; + } + + m_stretcher->process + (inputPtrs.data(), size_t(got), false); + } + + return int(m_stretcher->retrieve(samples, nframes)); +} + +void +TimeStretchWrapper::checkStretcher() +{ + lock_guard guard(m_mutex); + + if (m_timeRatio == 1.0 || !m_channelCount || !m_sampleRate) { + if (m_stretcher) { + SVDEBUG << "TimeStretchWrapper::checkStretcher: m_timeRatio = " + << m_timeRatio << ", m_channelCount = " << m_channelCount + << ", m_sampleRate = " << m_sampleRate + << ", deleting existing stretcher" << endl; + delete m_stretcher; + m_stretcher = nullptr; + } + return; + } + + if (m_stretcher) { + SVDEBUG << "TimeStretchWrapper::checkStretcher: setting stretcher ratio to " << m_timeRatio << endl; + m_stretcher->setTimeRatio(m_timeRatio); + return; + } + + SVDEBUG << "TimeStretchWrapper::checkStretcher: creating stretcher with ratio " << m_timeRatio << endl; + + m_stretcher = new RubberBandStretcher + (size_t(round(m_sampleRate)), + m_channelCount, + RubberBandStretcher::OptionProcessRealTime, + m_timeRatio); + + m_inputs.resize(m_channelCount); + for (auto &v: m_inputs) { + v.resize(m_stretcherInputSize); + } +} + +void +TimeStretchWrapper::setSystemPlaybackChannelCount(int count) +{ + { + lock_guard guard(m_mutex); + if (m_channelCount != count) { + delete m_stretcher; + m_stretcher = nullptr; + } + m_channelCount = count; + } + m_source->setSystemPlaybackChannelCount(count); +} + +void +TimeStretchWrapper::setSystemPlaybackSampleRate(int rate) +{ + { + lock_guard guard(m_mutex); + if (m_sampleRate != rate) { + delete m_stretcher; + m_stretcher = nullptr; + } + m_sampleRate = rate; + } + m_source->setSystemPlaybackSampleRate(rate); +} + +std::string +TimeStretchWrapper::getClientName() const +{ + return m_source->getClientName(); +} + +int +TimeStretchWrapper::getApplicationSampleRate() const +{ + return m_source->getApplicationSampleRate(); +} + +int +TimeStretchWrapper::getApplicationChannelCount() const +{ + return m_source->getApplicationChannelCount(); +} + +void +TimeStretchWrapper::setSystemPlaybackBlockSize(int sz) +{ + SVDEBUG << "NOTE: TimeStretchWrapper::setSystemPlaybackBlockSize called " + << "with size = " << sz << "; not passing to wrapped source, as " + << "actual block size will vary" << endl; +} + +void +TimeStretchWrapper::setSystemPlaybackLatency(int latency) +{ + m_source->setSystemPlaybackLatency(latency); +} + +void +TimeStretchWrapper::setOutputLevels(float left, float right) +{ + m_source->setOutputLevels(left, right); +} + +void +TimeStretchWrapper::audioProcessingOverload() +{ + m_source->audioProcessingOverload(); +} diff -r 7b1d30af4b38 -r 771ec060c1d2 audio/TimeStretchWrapper.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audio/TimeStretchWrapper.h Fri Apr 03 12:14:05 2020 +0100 @@ -0,0 +1,108 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef SV_TIME_STRETCH_WRAPPER_H +#define SV_TIME_STRETCH_WRAPPER_H + +#include "bqaudioio/ApplicationPlaybackSource.h" + +#include "base/BaseTypes.h" + +#include +#include + +namespace RubberBand { + class RubberBandStretcher; +} + +/** + * A breakfastquay::ApplicationPlaybackSource wrapper that implements + * time-stretching using Rubber Band. Note that the stretcher is + * bypassed entirely when a ratio of 1.0 is set; this means it's + * (almost) free to use one of these wrappers normally, but it also + * means you can't switch from 1.0 to another ratio (or back again) + * without some audible artifacts. + * + * This is real-time safe while the ratio is fixed, and may perform + * reallocations when the ratio changes. + */ +class TimeStretchWrapper : public breakfastquay::ApplicationPlaybackSource +{ +public: + /** + * Create a wrapper around the given ApplicationPlaybackSource, + * implementing another ApplicationPlaybackSource interface that + * draws from the same source data but with a time-stretcher + * optionally applied. + * + * The wrapper does not take ownership of the wrapped + * ApplicationPlaybackSource, whose lifespan must exceed that of + * this object. + */ + TimeStretchWrapper(ApplicationPlaybackSource *source); + ~TimeStretchWrapper(); + + /** + * Set a time stretch factor, i.e. playback speed, where 1.0 is + * normal speed + */ + void setTimeStretchRatio(double ratio); + + /** + * Clear stretcher buffers. + */ + void reset(); + + // These functions are passed through to the wrapped + // ApplicationPlaybackSource + + std::string getClientName() const override; + int getApplicationSampleRate() const override; + int getApplicationChannelCount() const override; + + void setSystemPlaybackBlockSize(int) override; + void setSystemPlaybackSampleRate(int) override; + void setSystemPlaybackChannelCount(int) override; + void setSystemPlaybackLatency(int) override; + + void setOutputLevels(float peakLeft, float peakRight) override; + void audioProcessingOverload() override; + + /** + * Request some samples from the wrapped + * ApplicationPlaybackSource, time-stretch if appropriate, and + * return them to the target + */ + int getSourceSamples(float *const *samples, int nchannels, int nframes) + override; + +private: + ApplicationPlaybackSource *m_source; + RubberBand::RubberBandStretcher *m_stretcher; + double m_timeRatio; + std::vector> m_inputs; + std::mutex m_mutex; + int m_stretcherInputSize; + int m_channelCount; + sv_samplerate_t m_sampleRate; + + void checkStretcher(); // call without m_mutex held + + TimeStretchWrapper(const TimeStretchWrapper &)=delete; + TimeStretchWrapper &operator=(const TimeStretchWrapper &)=delete; +}; + +#endif + + diff -r 7b1d30af4b38 -r 771ec060c1d2 files.pri --- a/files.pri Wed Mar 25 12:09:13 2020 +0000 +++ b/files.pri Fri Apr 03 12:14:05 2020 +0100 @@ -5,7 +5,9 @@ audio/AudioGenerator.h \ audio/ClipMixer.h \ audio/ContinuousSynth.h \ + audio/EffectWrapper.h \ audio/PlaySpeedRangeMapper.h \ + audio/TimeStretchWrapper.h \ framework/Align.h \ framework/Document.h \ framework/MainWindowBase.h \ @@ -20,7 +22,9 @@ audio/AudioGenerator.cpp \ audio/ClipMixer.cpp \ audio/ContinuousSynth.cpp \ + audio/EffectWrapper.cpp \ audio/PlaySpeedRangeMapper.cpp \ + audio/TimeStretchWrapper.cpp \ framework/Align.cpp \ framework/Document.cpp \ framework/MainWindowBase.cpp \ diff -r 7b1d30af4b38 -r 771ec060c1d2 framework/MainWindowBase.cpp --- a/framework/MainWindowBase.cpp Wed Mar 25 12:09:13 2020 +0000 +++ b/framework/MainWindowBase.cpp Fri Apr 03 12:14:05 2020 +0100 @@ -82,7 +82,6 @@ #include #include #include -#include #include #include @@ -151,7 +150,6 @@ m_midiMode(midiMode), m_playSource(nullptr), m_recordTarget(nullptr), - m_resamplerWrapper(nullptr), m_playTarget(nullptr), m_audioIO(nullptr), m_oscQueue(nullptr), @@ -2572,17 +2570,14 @@ SVCERR << "createAudioIO: Preferred record device = \"" << preference.recordDevice << "\"" << endl; - if (!m_resamplerWrapper) { - m_resamplerWrapper = new breakfastquay::ResamplerWrapper(m_playSource); - m_playSource->setResamplerWrapper(m_resamplerWrapper); - } + breakfastquay::ApplicationPlaybackSource *source = + m_playSource->getApplicationPlaybackSource(); std::string errorString; if (m_audioMode == AUDIO_PLAYBACK_AND_RECORD) { m_audioIO = breakfastquay::AudioFactory:: - createCallbackIO(m_recordTarget, m_resamplerWrapper, - preference, errorString); + createCallbackIO(m_recordTarget, source, preference, errorString); if (m_audioIO) { SVCERR << "MainWindowBase::createAudioIO: Suspending on creation" << endl; m_audioIO->suspend(); // start in suspended state @@ -2597,8 +2592,7 @@ if (!m_audioIO) { m_playTarget = breakfastquay::AudioFactory:: - createCallbackPlayTarget(m_resamplerWrapper, - preference, errorString); + createCallbackPlayTarget(source, preference, errorString); if (m_playTarget) { SVCERR << "MainWindowBase::createAudioIO: Suspending on creation" << endl; m_playTarget->suspend(); // start in suspended state @@ -2652,7 +2646,6 @@ // First prevent this trying to call target. if (m_playSource) { m_playSource->setSystemPlaybackTarget(nullptr); - m_playSource->setResamplerWrapper(nullptr); } // Then delete the breakfastquay::System object. @@ -2660,14 +2653,8 @@ delete m_audioIO; delete m_playTarget; - // And the breakfastquay resampler wrapper. We need to - // delete/recreate this if the channel count changes, which is one - // of the use cases for recreateAudioIO() calling this - delete m_resamplerWrapper; - m_audioIO = nullptr; m_playTarget = nullptr; - m_resamplerWrapper = nullptr; } void diff -r 7b1d30af4b38 -r 771ec060c1d2 framework/MainWindowBase.h --- a/framework/MainWindowBase.h Wed Mar 25 12:09:13 2020 +0000 +++ b/framework/MainWindowBase.h Fri Apr 03 12:14:05 2020 +0100 @@ -50,6 +50,7 @@ class WaveformLayer; class WaveFileModel; class AudioCallbackPlaySource; +class TimeStretchWrapper; class AudioCallbackRecordTarget; class CommandHistory; class QMenu; @@ -75,7 +76,6 @@ namespace breakfastquay { class SystemPlaybackTarget; class SystemAudioIO; - class ResamplerWrapper; } /** @@ -415,7 +415,6 @@ AudioCallbackPlaySource *m_playSource; AudioCallbackRecordTarget *m_recordTarget; - breakfastquay::ResamplerWrapper *m_resamplerWrapper; breakfastquay::SystemPlaybackTarget *m_playTarget; // only one of this... breakfastquay::SystemAudioIO *m_audioIO; // ... and this exists diff -r 7b1d30af4b38 -r 771ec060c1d2 framework/TransformUserConfigurator.cpp --- a/framework/TransformUserConfigurator.cpp Wed Mar 25 12:09:13 2020 +0000 +++ b/framework/TransformUserConfigurator.cpp Fri Apr 03 12:14:05 2020 +0100 @@ -40,15 +40,20 @@ bool TransformUserConfigurator::getChannelRange(TransformId identifier, - Vamp::PluginBase *plugin, + std::shared_ptr plugin, int &minChannels, int &maxChannels) { if (plugin && plugin->getType() == "Feature Extraction Plugin") { - Vamp::Plugin *vp = static_cast(plugin); - SVDEBUG << "TransformUserConfigurator::getChannelRange: is a Vamp plugin" << endl; - minChannels = int(vp->getMinChannelCount()); - maxChannels = int(vp->getMaxChannelCount()); - return true; + auto vp = std::dynamic_pointer_cast(plugin); + if (vp) { + SVDEBUG << "TransformUserConfigurator::getChannelRange: is a Vamp plugin" << endl; + minChannels = int(vp->getMinChannelCount()); + maxChannels = int(vp->getMaxChannelCount()); + return true; + } else { + SVCERR << "TransformUserConfigurator::getChannelRange: inconsistent plugin identity!" << endl; + return false; + } } else { SVDEBUG << "TransformUserConfigurator::getChannelRange: is not a Vamp plugin" << endl; return TransformFactory::getInstance()-> @@ -59,7 +64,7 @@ bool TransformUserConfigurator::configure(ModelTransformer::Input &input, Transform &transform, - Vamp::PluginBase *plugin, + std::shared_ptr plugin, ModelId &inputModel, AudioPlaySource *source, sv_frame_t startFrame, @@ -85,36 +90,35 @@ if (RealTimePluginFactory::instanceFor(id)) { RealTimePluginFactory *factory = RealTimePluginFactory::instanceFor(id); - const RealTimePluginDescriptor *desc = factory->getPluginDescriptor(id); + RealTimePluginDescriptor desc = factory->getPluginDescriptor(id); - if (desc->audioInputPortCount > 0 && - desc->audioOutputPortCount > 0 && - !desc->isSynth) { + if (desc.audioInputPortCount > 0 && + desc.audioOutputPortCount > 0 && + !desc.isSynth) { effect = true; } - if (desc->audioInputPortCount == 0) { + if (desc.audioInputPortCount == 0) { generator = true; } if (output != "A") { int outputNo = output.toInt(); - if (outputNo >= 0 && outputNo < int(desc->controlOutputPortCount)) { - outputLabel = desc->controlOutputPortNames[outputNo].c_str(); + if (outputNo >= 0 && outputNo < int(desc.controlOutputPortCount)) { + outputLabel = desc.controlOutputPortNames[outputNo].c_str(); } } - RealTimePluginInstance *rtp = - static_cast(plugin); - - if (effect && source) { + auto auditionable = std::dynamic_pointer_cast(plugin); + + if (effect && source && auditionable) { SVDEBUG << "Setting auditioning effect" << endl; - source->setAuditioningEffect(rtp); + source->setAuditioningEffect(auditionable); } } else { - Vamp::Plugin *vp = static_cast(plugin); + auto vp = std::dynamic_pointer_cast(plugin); frequency = (vp->getInputDomain() == Vamp::Plugin::FrequencyDomain); @@ -229,7 +233,7 @@ delete dialog; if (effect && source) { - source->setAuditioningEffect(nullptr); + source->setAuditioningEffect({}); } return ok; diff -r 7b1d30af4b38 -r 771ec060c1d2 framework/TransformUserConfigurator.h --- a/framework/TransformUserConfigurator.h Wed Mar 25 12:09:13 2020 +0000 +++ b/framework/TransformUserConfigurator.h Fri Apr 03 12:14:05 2020 +0100 @@ -17,6 +17,8 @@ #include "transform/ModelTransformerFactory.h" +#include + class TransformUserConfigurator : public ModelTransformerFactory::UserConfigurator { public: @@ -24,7 +26,7 @@ bool configure(ModelTransformer::Input &input, Transform &transform, - Vamp::PluginBase *plugin, + std::shared_ptr plugin, ModelId &inputModel, AudioPlaySource *source, sv_frame_t startFrame, @@ -37,7 +39,8 @@ private: bool getChannelRange(TransformId identifier, - Vamp::PluginBase *plugin, int &min, int &max); + std::shared_ptr plugin, + int &min, int &max); };