# HG changeset patch # User Chris Cannam # Date 1159959699 0 # Node ID fbd7a497fd89f5f19cbd5a1ecf3fd5e1a236a7ad # Parent 75c5951cf9d74b58bca416681dbbe2b0c4e169a2 * Audition effects plugins during playback diff -r 75c5951cf9d7 -r fbd7a497fd89 audioio/AudioCallbackPlaySource.cpp --- a/audioio/AudioCallbackPlaySource.cpp Tue Oct 03 15:01:50 2006 +0000 +++ b/audioio/AudioCallbackPlaySource.cpp Wed Oct 04 11:01:39 2006 +0000 @@ -23,6 +23,7 @@ #include "base/Preferences.h" #include "data/model/DenseTimeValueModel.h" #include "data/model/SparseOneDimensionalModel.h" +#include "plugin/RealTimePluginInstance.h" #include "PhaseVocoderTimeStretcher.h" #include @@ -52,6 +53,7 @@ m_lastModelEndFrame(0), m_outputLeft(0.0), m_outputRight(0.0), + m_auditioningPlugin(0), m_timeStretcher(0), m_fillThread(0), m_converter(0), @@ -97,6 +99,8 @@ delete m_audioGenerator; m_bufferScavenger.scavenge(true); + m_pluginScavenger.scavenge(true); + m_timeStretcherScavenger.scavenge(true); } void @@ -639,6 +643,14 @@ initialiseConverter(); } +void +AudioCallbackPlaySource::setAuditioningPlugin(RealTimePluginInstance *plugin) +{ + RealTimePluginInstance *formerPlugin = m_auditioningPlugin; + m_auditioningPlugin = plugin; + if (formerPlugin) m_pluginScavenger.claim(formerPlugin); +} + size_t AudioCallbackPlaySource::getTargetSampleRate() const { @@ -753,6 +765,8 @@ } } + applyAuditioningEffect(count, buffer); + m_condition.wakeAll(); return got; } @@ -846,11 +860,56 @@ } } + applyAuditioningEffect(count, buffer); + m_condition.wakeAll(); return count; } +void +AudioCallbackPlaySource::applyAuditioningEffect(size_t count, float **buffers) +{ + RealTimePluginInstance *plugin = m_auditioningPlugin; + if (!plugin) return; + + if (plugin->getAudioInputCount() != getTargetChannelCount()) { + std::cerr << "plugin input count " << plugin->getAudioInputCount() + << " != our channel count " << getTargetChannelCount() + << std::endl; + return; + } + if (plugin->getAudioOutputCount() != getTargetChannelCount()) { + std::cerr << "plugin output count " << plugin->getAudioOutputCount() + << " != our channel count " << getTargetChannelCount() + << std::endl; + return; + } + if (plugin->getBufferSize() != count) { + std::cerr << "plugin buffer size " << plugin->getBufferSize() + << " != our block size " << count + << std::endl; + return; + } + + float **ib = plugin->getAudioInputBuffers(); + float **ob = plugin->getAudioOutputBuffers(); + + for (size_t c = 0; c < getTargetChannelCount(); ++c) { + for (size_t i = 0; i < count; ++i) { + ib[c][i] = buffers[c][i]; + } + } + + plugin->run(Vamp::RealTime::zeroTime); + + for (size_t c = 0; c < getTargetChannelCount(); ++c) { + for (size_t i = 0; i < count; ++i) { + buffers[c][i] = ob[c][i]; + } + } +} + // Called from fill thread, m_playing true, mutex held bool AudioCallbackPlaySource::fillBuffers() @@ -1286,6 +1345,7 @@ s.unifyRingBuffers(); s.m_bufferScavenger.scavenge(); + s.m_pluginScavenger.scavenge(); s.m_timeStretcherScavenger.scavenge(); if (work && s.m_playing && s.getSourceSampleRate()) { diff -r 75c5951cf9d7 -r fbd7a497fd89 audioio/AudioCallbackPlaySource.h --- a/audioio/AudioCallbackPlaySource.h Tue Oct 03 15:01:50 2006 +0000 +++ b/audioio/AudioCallbackPlaySource.h Wed Oct 04 11:01:39 2006 +0000 @@ -37,6 +37,7 @@ class AudioGenerator; class PlayParameters; class PhaseVocoderTimeStretcher; +class RealTimePluginInstance; /** * AudioCallbackPlaySource manages audio data supply to callback-based @@ -192,6 +193,23 @@ */ void setResampleQuality(int q); + /** + * Set a single real-time plugin as a processing effect for + * auditioning during playback. + * + * The plugin must have been initialised with + * getTargetChannelCount() channels and a getTargetBlockSize() + * sample frame processing block size. + * + * This playback source takes ownership of the plugin, which will + * be deleted at some point after the following call to + * setAuditioningPlugin (depending on real-time constraints). + * + * Pass a null pointer to remove the current auditioning plugin, + * if any. + */ + void setAuditioningPlugin(RealTimePluginInstance *plugin); + signals: void modelReplaced(); @@ -220,23 +238,25 @@ } }; - std::set m_models; - RingBufferVector *m_readBuffers; - RingBufferVector *m_writeBuffers; - size_t m_readBufferFill; - size_t m_writeBufferFill; - Scavenger m_bufferScavenger; - size_t m_sourceChannelCount; - size_t m_blockSize; - size_t m_sourceSampleRate; - size_t m_targetSampleRate; - size_t m_playLatency; - bool m_playing; - bool m_exiting; - size_t m_lastModelEndFrame; - static const size_t m_ringBufferSize; - float m_outputLeft; - float m_outputRight; + std::set m_models; + RingBufferVector *m_readBuffers; + RingBufferVector *m_writeBuffers; + size_t m_readBufferFill; + size_t m_writeBufferFill; + Scavenger m_bufferScavenger; + size_t m_sourceChannelCount; + size_t m_blockSize; + size_t m_sourceSampleRate; + size_t m_targetSampleRate; + size_t m_playLatency; + bool m_playing; + bool m_exiting; + size_t m_lastModelEndFrame; + static const size_t m_ringBufferSize; + float m_outputLeft; + float m_outputRight; + RealTimePluginInstance *m_auditioningPlugin; + Scavenger m_pluginScavenger; RingBuffer *getWriteRingBuffer(size_t c) { if (m_writeBuffers && c < m_writeBuffers->size()) { @@ -271,6 +291,9 @@ // frame argument passed in, in the case of looping). size_t mixModels(size_t &frame, size_t count, float **buffers); + // Called from getSourceSamples. + void applyAuditioningEffect(size_t count, float **buffers); + class AudioCallbackPlaySourceFillThread : public Thread { public: diff -r 75c5951cf9d7 -r fbd7a497fd89 audioio/AudioJACKTarget.cpp --- a/audioio/AudioJACKTarget.cpp Tue Oct 03 15:01:50 2006 +0000 +++ b/audioio/AudioJACKTarget.cpp Wed Oct 04 11:01:39 2006 +0000 @@ -78,6 +78,19 @@ return f(client, process_callback, arg); } +static int dynamic_jack_set_xrun_callback(jack_client_t *client, + JackXRunCallback xrun_callback, + void *arg) +{ + typedef int (*func)(jack_client_t *client, + JackXRunCallback xrun_callback, + void *arg); + void *s = symbol("jack_set_xrun_callback"); + if (!s) return 1; + func f = (func)s; + return f(client, xrun_callback, arg); +} + static const char **dynamic_jack_get_ports(jack_client_t *client, const char *port_name_pattern, const char *type_name_pattern, @@ -165,6 +178,7 @@ #define jack_get_buffer_size dynamic_jack_get_buffer_size #define jack_get_sample_rate dynamic_jack_get_sample_rate #define jack_set_process_callback dynamic_jack_set_process_callback +#define jack_set_xrun_callback dynamic_jack_set_xrun_callback #define jack_activate dynamic_jack_activate #define jack_deactivate dynamic_jack_deactivate #define jack_client_close dynamic_jack_client_close @@ -204,6 +218,7 @@ m_bufferSize = jack_get_buffer_size(m_client); m_sampleRate = jack_get_sample_rate(m_client); + jack_set_xrun_callback(m_client, xrunStatic, this); jack_set_process_callback(m_client, processStatic, this); if (jack_activate(m_client)) { @@ -236,6 +251,12 @@ return ((AudioJACKTarget *)arg)->process(nframes); } +int +AudioJACKTarget::xrunStatic(void *arg) +{ + return ((AudioJACKTarget *)arg)->xrun(); +} + void AudioJACKTarget::sourceModelReplaced() { @@ -365,6 +386,11 @@ return 0; } +int +AudioJACKTarget::xrun() +{ + std::cerr << "AudioJACKTarget: xrun!" << std::endl; +} #ifdef INCLUDE_MOCFILES #include "AudioJACKTarget.moc.cpp" diff -r 75c5951cf9d7 -r fbd7a497fd89 audioio/AudioJACKTarget.h --- a/audioio/AudioJACKTarget.h Tue Oct 03 15:01:50 2006 +0000 +++ b/audioio/AudioJACKTarget.h Wed Oct 04 11:01:39 2006 +0000 @@ -42,8 +42,10 @@ protected: int process(jack_nframes_t nframes); + int xrun(); static int processStatic(jack_nframes_t, void *); + static int xrunStatic(void *); jack_client_t *m_client; std::vector m_outputs; diff -r 75c5951cf9d7 -r fbd7a497fd89 main/MainWindow.cpp --- a/main/MainWindow.cpp Tue Oct 03 15:01:50 2006 +0000 +++ b/main/MainWindow.cpp Wed Oct 04 11:01:39 2006 +0000 @@ -2964,9 +2964,11 @@ PluginTransform::ExecutionContext context(channel); - bool ok = - factory->getConfigurationForTransform - (transform, m_document->getMainModel(), context, configurationXml); + bool ok = factory->getConfigurationForTransform(transform, + m_document->getMainModel(), + context, + configurationXml, + m_playSource); if (!ok) return; Layer *newLayer = m_document->createDerivedLayer(transform, diff -r 75c5951cf9d7 -r fbd7a497fd89 transform/TransformFactory.cpp --- a/transform/TransformFactory.cpp Tue Oct 03 15:01:50 2006 +0000 +++ b/transform/TransformFactory.cpp Wed Oct 04 11:01:39 2006 +0000 @@ -28,6 +28,8 @@ #include "vamp-sdk/PluginHostAdapter.h" +#include "sv/audioio/AudioCallbackPlaySource.h" //!!! shouldn't include here + #include #include @@ -443,7 +445,8 @@ TransformFactory::getConfigurationForTransform(TransformName name, Model *inputModel, PluginTransform::ExecutionContext &context, - QString &configurationXml) + QString &configurationXml, + AudioCallbackPlaySource *source) { QString id = name.section(':', 0, 2); QString output = name.section(':', 3); @@ -456,6 +459,7 @@ Vamp::PluginBase *plugin = 0; bool frequency = false; + bool effect = false; if (FeatureExtractionPluginFactory::instanceFor(id)) { @@ -469,8 +473,32 @@ } else if (RealTimePluginFactory::instanceFor(id)) { - plugin = RealTimePluginFactory::instanceFor(id)->instantiatePlugin - (id, 0, 0, inputModel->getSampleRate(), 1024, 1); + RealTimePluginFactory *factory = RealTimePluginFactory::instanceFor(id); + const RealTimePluginDescriptor *desc = factory->getPluginDescriptor(id); + + if (desc->audioInputPortCount > 0 && + desc->audioOutputPortCount > 0 && + !desc->isSynth) { + effect = true; + } + + size_t sampleRate = inputModel->getSampleRate(); + size_t blockSize = 1024; + size_t channels = 1; + if (effect && source) { + sampleRate = source->getTargetSampleRate(); + blockSize = source->getTargetBlockSize(); + channels = source->getTargetChannelCount(); + } + + RealTimePluginInstance *rtp = factory->instantiatePlugin + (id, 0, 0, sampleRate, blockSize, channels); + + plugin = rtp; + + if (effect && source && rtp) { + source->setAuditioningPlugin(rtp); + } } if (plugin) { @@ -516,7 +544,12 @@ context.makeConsistentWithPlugin(plugin); delete dialog; - delete plugin; + + if (effect && source) { + source->setAuditioningPlugin(0); // will delete our plugin + } else { + delete plugin; + } } if (ok) m_lastConfigurations[name] = configurationXml; diff -r 75c5951cf9d7 -r fbd7a497fd89 transform/TransformFactory.h --- a/transform/TransformFactory.h Tue Oct 03 15:01:50 2006 +0000 +++ b/transform/TransformFactory.h Wed Oct 04 11:01:39 2006 +0000 @@ -23,6 +23,8 @@ namespace Vamp { class PluginBase; } +class AudioCallbackPlaySource; + class TransformFactory : public QObject { Q_OBJECT @@ -78,11 +80,14 @@ * Get a configuration XML string for the given transform (by * asking the user, most likely). Returns true if the transform * is acceptable, false if the operation should be cancelled. + * Audio callback play source may be used to audition effects + * plugins, if provided. */ bool getConfigurationForTransform(TransformName name, Model *inputModel, PluginTransform::ExecutionContext &context, - QString &configurationXml); + QString &configurationXml, + AudioCallbackPlaySource *source = 0); /** * Return the output model resulting from applying the named