changeset 41:fbd7a497fd89

* Audition effects plugins during playback
author Chris Cannam
date Wed, 04 Oct 2006 11:01:39 +0000 (2006-10-04)
parents 75c5951cf9d7
children c0ae41c72421
files audioio/AudioCallbackPlaySource.cpp audioio/AudioCallbackPlaySource.h audioio/AudioJACKTarget.cpp audioio/AudioJACKTarget.h main/MainWindow.cpp transform/TransformFactory.cpp transform/TransformFactory.h
diffstat 7 files changed, 176 insertions(+), 25 deletions(-) [+]
line wrap: on
line diff
--- 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 <iostream>
@@ -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()) {
--- 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<Model *>                m_models;
-    RingBufferVector                *m_readBuffers;
-    RingBufferVector                *m_writeBuffers;
-    size_t                           m_readBufferFill;
-    size_t                           m_writeBufferFill;
-    Scavenger<RingBufferVector>      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<Model *>                 m_models;
+    RingBufferVector                 *m_readBuffers;
+    RingBufferVector                 *m_writeBuffers;
+    size_t                            m_readBufferFill;
+    size_t                            m_writeBufferFill;
+    Scavenger<RingBufferVector>       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<RealTimePluginInstance> m_pluginScavenger;
 
     RingBuffer<float> *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:
--- 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"
--- 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<jack_port_t *>  m_outputs;
--- 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,
--- 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 <iostream>
 #include <set>
 
@@ -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;
--- 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