changeset 60:3086ff194ea0

* More structural work on feature extraction plugin C <-> C++ adapter * Allow use of LADSPA/DSSI plugins with control outputs as feature extraction plugins (DSSI with MIDI output still to come) * Reorder labels on spectrogram status box * Minor tweaks in doc etc.
author Chris Cannam
date Mon, 27 Mar 2006 15:03:02 +0000
parents 9705a1978ecc
children 749b5521e082
files base/ViewManager.cpp plugin/DSSIPluginFactory.cpp plugin/DSSIPluginInstance.cpp plugin/DSSIPluginInstance.h plugin/FeatureExtractionPluginAdapter.cpp plugin/FeatureExtractionPluginAdapter.h plugin/FeatureExtractionPluginHostAdapter.cpp plugin/FeatureExtractionPluginHostAdapter.h plugin/LADSPAPluginFactory.cpp plugin/LADSPAPluginFactory.h plugin/LADSPAPluginInstance.cpp plugin/LADSPAPluginInstance.h plugin/RealTimePluginFactory.h plugin/RealTimePluginInstance.h plugin/api/svp.h transform/RealTimePluginTransform.cpp transform/RealTimePluginTransform.h transform/TransformFactory.cpp transform/TransformFactory.h
diffstat 19 files changed, 1134 insertions(+), 436 deletions(-) [+]
line wrap: on
line diff
--- a/base/ViewManager.cpp	Fri Mar 24 18:15:50 2006 +0000
+++ b/base/ViewManager.cpp	Mon Mar 27 15:03:02 2006 +0000
@@ -76,10 +76,10 @@
 {
     if (m_playbackFrame != f) {
 	m_playbackFrame = f;
+	emit playbackFrameChanged(f);
 	if (m_playSource && m_playSource->isPlaying()) {
 	    m_playSource->play(f);
 	}
-	emit playbackFrameChanged(f);
     }
 }
 
--- a/plugin/DSSIPluginFactory.cpp	Fri Mar 24 18:15:50 2006 +0000
+++ b/plugin/DSSIPluginFactory.cpp	Mon Mar 27 15:03:02 2006 +0000
@@ -285,6 +285,18 @@
 	    continue;
 	}
 
+        RealTimePluginDescriptor *rtd = new RealTimePluginDescriptor;
+        rtd->name = ladspaDescriptor->Name;
+        rtd->label = ladspaDescriptor->Label;
+        rtd->maker = ladspaDescriptor->Maker;
+        rtd->copyright = ladspaDescriptor->Copyright;
+        rtd->category = "";
+        rtd->isSynth = (descriptor->run_synth ||
+                        descriptor->run_multiple_synths);
+        rtd->parameterCount = 0;
+        rtd->audioInputPortCount = 0;
+        rtd->controlOutputPortCount = 0;
+
 #ifdef HAVE_LRDF
 	char *def_uri = 0;
 	lrdf_defaults *defs = 0;
@@ -303,6 +315,8 @@
 		m_taxonomy[ladspaDescriptor->UniqueID] = category;
 	    }
 	}
+
+        rtd->category = category.toStdString();
 	
 //	std::cerr << "Plugin id is " << ladspaDescriptor->UniqueID
 //		  << ", category is \"" << (category ? category : QString("(none)"))
@@ -337,10 +351,31 @@
 	}
 #endif // HAVE_LRDF
 
+	for (unsigned long i = 0; i < ladspaDescriptor->PortCount; i++) {
+	    if (LADSPA_IS_PORT_CONTROL(ladspaDescriptor->PortDescriptors[i])) {
+                if (LADSPA_IS_PORT_INPUT(ladspaDescriptor->PortDescriptors[i])) {
+                    ++rtd->parameterCount;
+                } else {
+                    if (strcmp(ladspaDescriptor->PortNames[i], "latency") &&
+                        strcmp(ladspaDescriptor->PortNames[i], "_latency")) {
+                        ++rtd->controlOutputPortCount;
+                        rtd->controlOutputPortNames.push_back
+                            (ladspaDescriptor->PortNames[i]);
+                    }
+                }
+            } else {
+                if (LADSPA_IS_PORT_INPUT(ladspaDescriptor->PortDescriptors[i])) {
+                    ++rtd->audioInputPortCount;
+                }
+            }
+        }
+
 	QString identifier = PluginIdentifier::createIdentifier
 	    ("dssi", soname, ladspaDescriptor->Label);
 	m_identifiers.push_back(identifier);
 
+        m_rtDescriptors[identifier] = rtd;
+
 	++index;
     }
 
--- a/plugin/DSSIPluginInstance.cpp	Fri Mar 24 18:15:50 2006 +0000
+++ b/plugin/DSSIPluginInstance.cpp	Mon Mar 27 15:03:02 2006 +0000
@@ -773,6 +773,13 @@
 }
 
 float
+DSSIPluginInstance::getControlOutputValue(unsigned int output) const
+{
+    if (output > m_controlPortsOut.size()) return 0.0;
+    return (*m_controlPortsOut[output].second);
+}
+
+float
 DSSIPluginInstance::getParameterValue(unsigned int parameter) const
 {
 #ifdef DEBUG_DSSI
--- a/plugin/DSSIPluginInstance.h	Fri Mar 24 18:15:50 2006 +0000
+++ b/plugin/DSSIPluginInstance.h	Mon Mar 27 15:03:02 2006 +0000
@@ -75,6 +75,9 @@
     virtual sample_t **getAudioInputBuffers() { return m_inputBuffers; }
     virtual sample_t **getAudioOutputBuffers() { return m_outputBuffers; }
 
+    virtual size_t getControlOutputCount() const { return m_controlPortsOut.size(); }
+    virtual float getControlOutputValue(size_t n) const;
+
     virtual ProgramList getPrograms() const;
     virtual std::string getCurrentProgram() const;
     virtual std::string getProgram(int bank, int program) const;
--- a/plugin/FeatureExtractionPluginAdapter.cpp	Fri Mar 24 18:15:50 2006 +0000
+++ b/plugin/FeatureExtractionPluginAdapter.cpp	Mon Mar 27 15:03:02 2006 +0000
@@ -1,34 +1,439 @@
 /* -*- 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 file copyright 2006 Chris Cannam.
+  Sonic Visualiser
+  An audio file viewer and annotation editor.
+  Centre for Digital Music, Queen Mary, University of London.
+  This file copyright 2006 Chris Cannam.
     
-    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.
+  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 "FeatureExtractionPluginAdapter.h"
 
-#include "plugins/ChromagramPlugin.h"
-
-extern int blah()
+FeatureExtractionPluginAdapterBase::FeatureExtractionPluginAdapterBase() :
+    m_populated(false)
 {
-    FeatureExtractionPluginAdapter<ChromagramPlugin> adapter;
-    
-    const SVPPluginDescriptor *desc = adapter.getDescriptor();
-
-    SVPPluginHandle handle = desc->instantiate(desc, 48000);
-    
-    unsigned int preferredBlockSize = desc->getPreferredBlockSize(handle);
-
-    SVPOutputDescriptor *od = desc->getOutputDescriptor(handle, 2);
-
-    SVPFeatureList **feature = desc->process(handle, 0, 0, 0);
 }
 
+const SVPPluginDescriptor *
+FeatureExtractionPluginAdapterBase::getDescriptor()
+{
+    if (m_populated) return &m_descriptor;
+
+    FeatureExtractionPlugin *plugin = createPlugin(48000);
+
+    m_parameters = plugin->getParameterDescriptors();
+    m_programs = plugin->getPrograms();
+    
+    m_descriptor.name = strdup(plugin->getName().c_str());
+    m_descriptor.description = strdup(plugin->getDescription().c_str());
+    m_descriptor.maker = strdup(plugin->getMaker().c_str());
+    m_descriptor.pluginVersion = plugin->getPluginVersion();
+    m_descriptor.copyright = strdup(plugin->getCopyright().c_str());
+    
+    m_descriptor.parameterCount = m_parameters.size();
+    m_descriptor.parameters = (const SVPParameterDescriptor **)
+        malloc(m_parameters.size() * sizeof(SVPParameterDescriptor));
+    
+    for (unsigned int i = 0; i < m_parameters.size(); ++i) {
+        SVPParameterDescriptor *desc = (SVPParameterDescriptor *)
+            malloc(sizeof(SVPParameterDescriptor));
+        desc->name = strdup(m_parameters[i].name.c_str());
+        desc->description = strdup(m_parameters[i].description.c_str());
+        desc->unit = strdup(m_parameters[i].unit.c_str());
+        desc->minValue = m_parameters[i].minValue;
+        desc->maxValue = m_parameters[i].maxValue;
+        desc->defaultValue = m_parameters[i].defaultValue;
+        desc->isQuantized = m_parameters[i].isQuantized;
+        desc->quantizeStep = m_parameters[i].quantizeStep;
+        m_descriptor.parameters[i] = desc;
+    }
+    
+    m_descriptor.programCount = m_programs.size();
+    m_descriptor.programs = (const char **)
+        malloc(m_programs.size() * sizeof(const char *));
+    
+    for (unsigned int i = 0; i < m_programs.size(); ++i) {
+        m_descriptor.programs[i] = strdup(m_programs[i].c_str());
+    }
+    
+    m_descriptor.instantiate = svpInstantiate;
+    m_descriptor.cleanup = svpCleanup;
+    m_descriptor.initialise = svpInitialise;
+    m_descriptor.reset = svpReset;
+    m_descriptor.getParameter = svpGetParameter;
+    m_descriptor.setParameter = svpSetParameter;
+    m_descriptor.getCurrentProgram = svpGetCurrentProgram;
+    m_descriptor.selectProgram = svpSelectProgram;
+    m_descriptor.getPreferredStepSize = svpGetPreferredStepSize;
+    m_descriptor.getPreferredBlockSize = svpGetPreferredBlockSize;
+    m_descriptor.getMinChannelCount = svpGetMinChannelCount;
+    m_descriptor.getMaxChannelCount = svpGetMaxChannelCount;
+    m_descriptor.getOutputCount = svpGetOutputCount;
+    m_descriptor.getOutputDescriptor = svpGetOutputDescriptor;
+    m_descriptor.releaseOutputDescriptor = svpReleaseOutputDescriptor;
+    m_descriptor.process = svpProcess;
+    m_descriptor.getRemainingFeatures = svpGetRemainingFeatures;
+    m_descriptor.releaseFeatureSet = svpReleaseFeatureSet;
+    
+    m_adapterMap[&m_descriptor] = this;
+
+    delete plugin;
+
+    m_populated = true;
+    return &m_descriptor;
+}
+
+FeatureExtractionPluginAdapterBase::~FeatureExtractionPluginAdapterBase()
+{
+    if (!m_populated) return;
+
+    free((void *)m_descriptor.name);
+    free((void *)m_descriptor.description);
+    free((void *)m_descriptor.maker);
+    free((void *)m_descriptor.copyright);
+        
+    for (unsigned int i = 0; i < m_descriptor.parameterCount; ++i) {
+        const SVPParameterDescriptor *desc = m_descriptor.parameters[i];
+        free((void *)desc->name);
+        free((void *)desc->description);
+        free((void *)desc->unit);
+    }
+    free((void *)m_descriptor.parameters);
+
+    for (unsigned int i = 0; i < m_descriptor.programCount; ++i) {
+        free((void *)m_descriptor.programs[i]);
+    }
+    free((void *)m_descriptor.programs);
+
+    m_adapterMap.erase(&m_descriptor);
+}
+
+FeatureExtractionPluginAdapterBase *
+FeatureExtractionPluginAdapterBase::lookupAdapter(SVPPluginHandle handle)
+{
+    AdapterMap::const_iterator i = m_adapterMap.find(handle);
+    if (i == m_adapterMap.end()) return 0;
+    return i->second;
+}
+
+SVPPluginHandle
+FeatureExtractionPluginAdapterBase::svpInstantiate(const SVPPluginDescriptor *desc,
+                                                   float inputSampleRate)
+{
+    if (m_adapterMap.find(desc) == m_adapterMap.end()) return 0;
+    FeatureExtractionPluginAdapterBase *adapter = m_adapterMap[desc];
+    if (desc != &adapter->m_descriptor) return 0;
+
+    FeatureExtractionPlugin *plugin = adapter->createPlugin(inputSampleRate);
+    if (plugin) {
+        m_adapterMap[plugin] = adapter;
+    }
+
+    return plugin;
+}
+
+void
+FeatureExtractionPluginAdapterBase::svpCleanup(SVPPluginHandle handle)
+{
+    FeatureExtractionPluginAdapterBase *adapter = lookupAdapter(handle);
+    if (!adapter) {
+        delete ((FeatureExtractionPlugin *)handle);
+        return;
+    }
+    adapter->cleanup(((FeatureExtractionPlugin *)handle));
+}
+
+int
+FeatureExtractionPluginAdapterBase::svpInitialise(SVPPluginHandle handle,
+                                                  unsigned int channels,
+                                                  unsigned int stepSize,
+                                                  unsigned int blockSize)
+{
+    bool result = ((FeatureExtractionPlugin *)handle)->initialise
+        (channels, stepSize, blockSize);
+    return result ? 1 : 0;
+}
+
+void
+FeatureExtractionPluginAdapterBase::svpReset(SVPPluginHandle handle) 
+{
+    ((FeatureExtractionPlugin *)handle)->reset();
+}
+
+float
+FeatureExtractionPluginAdapterBase::svpGetParameter(SVPPluginHandle handle,
+                                                    int param) 
+{
+    FeatureExtractionPluginAdapterBase *adapter = lookupAdapter(handle);
+    if (!adapter) return 0.0;
+    FeatureExtractionPlugin::ParameterList &list = adapter->m_parameters;
+    return ((FeatureExtractionPlugin *)handle)->getParameter(list[param].name);
+}
+
+void
+FeatureExtractionPluginAdapterBase::svpSetParameter(SVPPluginHandle handle,
+                                                    int param, float value)
+{
+    FeatureExtractionPluginAdapterBase *adapter = lookupAdapter(handle);
+    if (!adapter) return;
+    FeatureExtractionPlugin::ParameterList &list = adapter->m_parameters;
+    ((FeatureExtractionPlugin *)handle)->setParameter(list[param].name, value);
+}
+
+unsigned int
+FeatureExtractionPluginAdapterBase::svpGetCurrentProgram(SVPPluginHandle handle)
+{
+    FeatureExtractionPluginAdapterBase *adapter = lookupAdapter(handle);
+    if (!adapter) return 0;
+    FeatureExtractionPlugin::ProgramList &list = adapter->m_programs;
+    std::string program = ((FeatureExtractionPlugin *)handle)->getCurrentProgram();
+    for (unsigned int i = 0; i < list.size(); ++i) {
+        if (list[i] == program) return i;
+    }
+    return 0;
+}
+
+void
+FeatureExtractionPluginAdapterBase::svpSelectProgram(SVPPluginHandle handle,
+                                                     unsigned int program)
+{
+    FeatureExtractionPluginAdapterBase *adapter = lookupAdapter(handle);
+    if (!adapter) return;
+    FeatureExtractionPlugin::ProgramList &list = adapter->m_programs;
+    ((FeatureExtractionPlugin *)handle)->selectProgram(list[program]);
+}
+
+unsigned int
+FeatureExtractionPluginAdapterBase::svpGetPreferredStepSize(SVPPluginHandle handle)
+{
+    return ((FeatureExtractionPlugin *)handle)->getPreferredStepSize();
+}
+
+unsigned int
+FeatureExtractionPluginAdapterBase::svpGetPreferredBlockSize(SVPPluginHandle handle) 
+{
+    return ((FeatureExtractionPlugin *)handle)->getPreferredBlockSize();
+}
+
+unsigned int
+FeatureExtractionPluginAdapterBase::svpGetMinChannelCount(SVPPluginHandle handle)
+{
+    return ((FeatureExtractionPlugin *)handle)->getMinChannelCount();
+}
+
+unsigned int
+FeatureExtractionPluginAdapterBase::svpGetMaxChannelCount(SVPPluginHandle handle)
+{
+    return ((FeatureExtractionPlugin *)handle)->getMaxChannelCount();
+}
+
+unsigned int
+FeatureExtractionPluginAdapterBase::svpGetOutputCount(SVPPluginHandle handle)
+{
+    FeatureExtractionPluginAdapterBase *adapter = lookupAdapter(handle);
+    if (!adapter) return 0;
+    return adapter->getOutputCount((FeatureExtractionPlugin *)handle);
+}
+
+SVPOutputDescriptor *
+FeatureExtractionPluginAdapterBase::svpGetOutputDescriptor(SVPPluginHandle handle,
+                                                           unsigned int i)
+{
+    FeatureExtractionPluginAdapterBase *adapter = lookupAdapter(handle);
+    if (!adapter) return 0;
+    return adapter->getOutputDescriptor((FeatureExtractionPlugin *)handle, i);
+}
+
+void
+FeatureExtractionPluginAdapterBase::svpReleaseOutputDescriptor(SVPOutputDescriptor *desc)
+{
+    if (desc->name) free((void *)desc->name);
+    if (desc->description) free((void *)desc->description);
+    if (desc->unit) free((void *)desc->unit);
+    for (unsigned int i = 0; i < desc->valueCount; ++i) {
+        free((void *)desc->valueNames[i]);
+    }
+    if (desc->valueNames) free((void *)desc->valueNames);
+    free((void *)desc);
+}
+
+SVPFeatureList **
+FeatureExtractionPluginAdapterBase::svpProcess(SVPPluginHandle handle,
+                                               float **inputBuffers,
+                                               int sec,
+                                               int nsec)
+{
+    FeatureExtractionPluginAdapterBase *adapter = lookupAdapter(handle);
+    if (!adapter) return 0;
+    return adapter->process((FeatureExtractionPlugin *)handle,
+                            inputBuffers, sec, nsec);
+}
+
+SVPFeatureList **
+FeatureExtractionPluginAdapterBase::svpGetRemainingFeatures(SVPPluginHandle handle)
+{
+    FeatureExtractionPluginAdapterBase *adapter = lookupAdapter(handle);
+    if (!adapter) return 0;
+    return adapter->getRemainingFeatures((FeatureExtractionPlugin *)handle);
+}
+
+void
+FeatureExtractionPluginAdapterBase::svpReleaseFeatureSet(SVPFeatureList **fs)
+{
+    if (!fs) return;
+    for (unsigned int i = 0; fs[i]; ++i) {
+        for (unsigned int j = 0; j < fs[i]->featureCount; ++j) {
+            SVPFeature *feature = &fs[i]->features[j];
+            if (feature->values) free((void *)feature->values);
+            if (feature->label) free((void *)feature->label);
+            free((void *)feature);
+        }
+        if (fs[i]->features) free((void *)fs[i]->features);
+        free((void *)fs[i]);
+    }
+    free((void *)fs);
+}
+
+void 
+FeatureExtractionPluginAdapterBase::cleanup(FeatureExtractionPlugin *plugin)
+{
+    if (m_pluginOutputs.find(plugin) != m_pluginOutputs.end()) {
+        delete m_pluginOutputs[plugin];
+        m_pluginOutputs.erase(plugin);
+    }
+    m_adapterMap.erase(plugin);
+    delete ((FeatureExtractionPlugin *)plugin);
+}
+
+void 
+FeatureExtractionPluginAdapterBase::checkOutputMap(FeatureExtractionPlugin *plugin)
+{
+    if (!m_pluginOutputs[plugin]) {
+        m_pluginOutputs[plugin] = new FeatureExtractionPlugin::OutputList
+            (plugin->getOutputDescriptors());
+    }
+}
+
+unsigned int 
+FeatureExtractionPluginAdapterBase::getOutputCount(FeatureExtractionPlugin *plugin)
+{
+    checkOutputMap(plugin);
+    return m_pluginOutputs[plugin]->size();
+}
+
+SVPOutputDescriptor *
+FeatureExtractionPluginAdapterBase::getOutputDescriptor(FeatureExtractionPlugin *plugin,
+                                                        unsigned int i)
+{
+    checkOutputMap(plugin);
+    FeatureExtractionPlugin::OutputDescriptor &od =
+        (*m_pluginOutputs[plugin])[i];
+
+    SVPOutputDescriptor *desc = (SVPOutputDescriptor *)
+        malloc(sizeof(SVPOutputDescriptor));
+
+    desc->name = strdup(od.name.c_str());
+    desc->description = strdup(od.description.c_str());
+    desc->unit = strdup(od.unit.c_str());
+    desc->hasFixedValueCount = od.hasFixedValueCount;
+    desc->valueCount = od.valueCount;
+
+    desc->valueNames = (const char **)
+        malloc(od.valueCount * sizeof(const char *));
+        
+    for (unsigned int i = 0; i < od.valueCount; ++i) {
+        desc->valueNames[i] = strdup(od.valueNames[i].c_str());
+    }
+
+    desc->hasKnownExtents = od.hasKnownExtents;
+    desc->minValue = od.minValue;
+    desc->maxValue = od.maxValue;
+    desc->isQuantized = od.isQuantized;
+    desc->quantizeStep = od.quantizeStep;
+
+    switch (od.sampleType) {
+    case FeatureExtractionPlugin::OutputDescriptor::OneSamplePerStep:
+        desc->sampleType = svpOneSamplePerStep; break;
+    case FeatureExtractionPlugin::OutputDescriptor::FixedSampleRate:
+        desc->sampleType = svpFixedSampleRate; break;
+    case FeatureExtractionPlugin::OutputDescriptor::VariableSampleRate:
+        desc->sampleType = svpVariableSampleRate; break;
+    }
+
+    desc->sampleRate = od.sampleRate;
+
+    return desc;
+}
+    
+SVPFeatureList **
+FeatureExtractionPluginAdapterBase::process(FeatureExtractionPlugin *plugin,
+                                            float **inputBuffers,
+                                            int sec, int nsec)
+{
+    RealTime rt(sec, nsec);
+    return convertFeatures(plugin->process(inputBuffers, rt));
+}
+    
+SVPFeatureList **
+FeatureExtractionPluginAdapterBase::getRemainingFeatures(FeatureExtractionPlugin *plugin)
+{
+    return convertFeatures(plugin->getRemainingFeatures());
+}
+
+SVPFeatureList **
+FeatureExtractionPluginAdapterBase::convertFeatures(const FeatureExtractionPlugin::FeatureSet &features)
+{
+    unsigned int n = 0;
+    if (features.begin() != features.end()) {
+        FeatureExtractionPlugin::FeatureSet::const_iterator i = features.end();
+        --i;
+        n = i->first + 1;
+    }
+
+    if (!n) return 0;
+
+    SVPFeatureList **fs = (SVPFeatureList **)
+        malloc((n + 1) * sizeof(SVPFeatureList *));
+
+    for (unsigned int i = 0; i < n; ++i) {
+        fs[i] = (SVPFeatureList *)malloc(sizeof(SVPFeatureList));
+        if (features.find(i) == features.end()) {
+            fs[i]->featureCount = 0;
+            fs[i]->features = 0;
+        } else {
+            FeatureExtractionPlugin::FeatureSet::const_iterator fi =
+                features.find(i);
+            const FeatureExtractionPlugin::FeatureList &fl = fi->second;
+            fs[i]->featureCount = fl.size();
+            fs[i]->features = (SVPFeature *)malloc(fl.size() *
+                                                   sizeof(SVPFeature));
+            for (unsigned int j = 0; j < fl.size(); ++j) {
+                fs[i]->features[j].hasTimestamp = fl[j].hasTimestamp;
+                fs[i]->features[j].sec = fl[j].timestamp.sec;
+                fs[i]->features[j].nsec = fl[j].timestamp.nsec;
+                fs[i]->features[j].valueCount = fl[j].values.size();
+                fs[i]->features[j].values = (float *)malloc
+                    (fs[i]->features[j].valueCount * sizeof(float));
+                for (unsigned int k = 0; k < fs[i]->features[j].valueCount; ++k) {
+                    fs[i]->features[j].values[k] = fl[j].values[k];
+                }
+                fs[i]->features[j].label = strdup(fl[j].label.c_str());
+            }
+        }
+    }
+
+    fs[n] = 0;
+
+    return fs;
+}
+
+FeatureExtractionPluginAdapterBase::AdapterMap 
+FeatureExtractionPluginAdapterBase::m_adapterMap;
+
+
--- a/plugin/FeatureExtractionPluginAdapter.h	Fri Mar 24 18:15:50 2006 +0000
+++ b/plugin/FeatureExtractionPluginAdapter.h	Mon Mar 27 15:03:02 2006 +0000
@@ -21,365 +21,106 @@
 
 #include <map>
 
-template <typename Plugin>
-class FeatureExtractionPluginAdapter
+class FeatureExtractionPluginAdapterBase
 {
 public:
-    FeatureExtractionPluginAdapter() {
-
-        Plugin plugin(48000);
-
-        m_parameters = plugin.getParameterDescriptors();
-        m_programs = plugin.getPrograms();
-
-        m_descriptor.name = strdup(plugin.getName().c_str());
-        m_descriptor.description = strdup(plugin.getDescription().c_str());
-        m_descriptor.maker = strdup(plugin.getMaker().c_str());
-        m_descriptor.pluginVersion = plugin.getPluginVersion();
-        m_descriptor.copyright = strdup(plugin.getCopyright().c_str());
-
-        m_descriptor.parameterCount = m_parameters.size();
-        m_descriptor.parameters = (const SVPParameterDescriptor **)
-            malloc(m_parameters.size() * sizeof(SVPParameterDescriptor));
-
-        for (unsigned int i = 0; i < m_parameters.size(); ++i) {
-            SVPParameterDescriptor *desc = (SVPParameterDescriptor *)
-                malloc(sizeof(SVPParameterDescriptor));
-            desc->name = strdup(m_parameters[i].name.c_str());
-            desc->description = strdup(m_parameters[i].description.c_str());
-            desc->unit = strdup(m_parameters[i].unit.c_str());
-            desc->minValue = m_parameters[i].minValue;
-            desc->maxValue = m_parameters[i].maxValue;
-            desc->defaultValue = m_parameters[i].defaultValue;
-            desc->isQuantized = m_parameters[i].isQuantized;
-            desc->quantizeStep = m_parameters[i].quantizeStep;
-            m_descriptor.parameters[i] = desc;
-        }
-
-        m_descriptor.programCount = m_programs.size();
-        m_descriptor.programs = (const char **)
-            malloc(m_programs.size() * sizeof(const char *));
-        
-        for (unsigned int i = 0; i < m_programs.size(); ++i) {
-            m_descriptor.programs[i] = strdup(m_programs[i].c_str());
-        }
-
-        m_descriptor.instantiate = svpInstantiate;
-        m_descriptor.cleanup = svpCleanup;
-        m_descriptor.initialise = svpInitialise;
-        m_descriptor.reset = svpReset;
-        m_descriptor.getParameter = svpGetParameter;
-        m_descriptor.setParameter = svpSetParameter;
-        m_descriptor.getCurrentProgram = svpGetCurrentProgram;
-        m_descriptor.selectProgram = svpSelectProgram;
-        m_descriptor.getPreferredStepSize = svpGetPreferredStepSize;
-        m_descriptor.getPreferredBlockSize = svpGetPreferredBlockSize;
-        m_descriptor.getMinChannelCount = svpGetMinChannelCount;
-        m_descriptor.getMaxChannelCount = svpGetMaxChannelCount;
-        m_descriptor.getOutputCount = svpGetOutputCount;
-        m_descriptor.getOutputDescriptor = svpGetOutputDescriptor;
-        m_descriptor.releaseOutputDescriptor = svpReleaseOutputDescriptor;
-        m_descriptor.process = svpProcess;
-        m_descriptor.getRemainingFeatures = svpGetRemainingFeatures;
-        m_descriptor.releaseFeatureSet = svpReleaseFeatureSet;
-
-        m_adapterMap[&m_descriptor] = this;
-    }
-
-    virtual ~FeatureExtractionPluginAdapter() {
-
-        free((void *)m_descriptor.name);
-        free((void *)m_descriptor.description);
-        free((void *)m_descriptor.maker);
-        free((void *)m_descriptor.copyright);
-        
-        for (unsigned int i = 0; i < m_descriptor.parameterCount; ++i) {
-            const SVPParameterDescriptor *desc = m_descriptor.parameters[i];
-            free((void *)desc->name);
-            free((void *)desc->description);
-            free((void *)desc->unit);
-        }
-        free((void *)m_descriptor.parameters);
-
-        for (unsigned int i = 0; i < m_descriptor.programCount; ++i) {
-            free((void *)m_descriptor.programs[i]);
-        }
-        free((void *)m_descriptor.programs);
-
-        m_adapterMap.erase(&m_descriptor);
-    }        
-    
-    const SVPPluginDescriptor *getDescriptor() const {
-        return &m_descriptor;
-    }
+    virtual ~FeatureExtractionPluginAdapterBase();
+    const SVPPluginDescriptor *getDescriptor();
 
 protected:
+    FeatureExtractionPluginAdapterBase();
+
+    virtual FeatureExtractionPlugin *createPlugin(float inputSampleRate) = 0;
+
     static SVPPluginHandle svpInstantiate(const SVPPluginDescriptor *desc,
-                                          float inputSampleRate) {
-        if (m_adapterMap.find(desc) == m_adapterMap.end()) return 0;
-        FeatureExtractionPluginAdapter *adapter = m_adapterMap[desc];
-        if (desc != &adapter->m_descriptor) return 0;
-        Plugin *plugin = new Plugin(inputSampleRate);
-        m_adapterMap[plugin] = adapter;
-        return plugin;
-    }
+                                          float inputSampleRate);
 
-    static void svpCleanup(SVPPluginHandle handle) {
-        if (m_adapterMap.find(handle) == m_adapterMap.end()) {
-            delete ((Plugin *)handle);
-            return;
-        }
-        m_adapterMap[handle]->cleanup(((Plugin *)handle));
-    }
+    static void svpCleanup(SVPPluginHandle handle);
 
     static int svpInitialise(SVPPluginHandle handle, unsigned int channels,
-                             unsigned int stepSize, unsigned int blockSize) {
-        bool result = ((Plugin *)handle)->initialise(channels,
-                                                     stepSize, blockSize);
-        return result ? 1 : 0;
-    }
+                             unsigned int stepSize, unsigned int blockSize);
 
-    static void svpReset(SVPPluginHandle handle) {
-        ((Plugin *)handle)->reset();
-    }
+    static void svpReset(SVPPluginHandle handle);
 
-    static float svpGetParameter(SVPPluginHandle handle, int param) {
-        if (m_adapterMap.find(handle) == m_adapterMap.end()) return 0.0;
-        FeatureExtractionPlugin::ParameterList &list =
-            m_adapterMap[handle]->m_parameters;
-        return ((Plugin *)handle)->getParameter(list[param].name);
-    }
+    static float svpGetParameter(SVPPluginHandle handle, int param);
+    static void svpSetParameter(SVPPluginHandle handle, int param, float value);
 
-    static void svpSetParameter(SVPPluginHandle handle, int param, float value) {
-        if (m_adapterMap.find(handle) == m_adapterMap.end()) return;
-        FeatureExtractionPlugin::ParameterList &list =
-            m_adapterMap[handle]->m_parameters;
-        ((Plugin *)handle)->setParameter(list[param].name, value);
-    }
+    static unsigned int svpGetCurrentProgram(SVPPluginHandle handle);
+    static void svpSelectProgram(SVPPluginHandle handle, unsigned int program);
 
-    static unsigned int svpGetCurrentProgram(SVPPluginHandle handle) {
-        if (m_adapterMap.find(handle) == m_adapterMap.end()) return 0;
-        FeatureExtractionPlugin::ProgramList &list =
-            m_adapterMap[handle]->m_programs;
-        std::string program = ((Plugin *)handle)->getCurrentProgram();
-        for (unsigned int i = 0; i < list.size(); ++i) {
-            if (list[i] == program) return i;
-        }
-        return 0;
-    }
+    static unsigned int svpGetPreferredStepSize(SVPPluginHandle handle);
+    static unsigned int svpGetPreferredBlockSize(SVPPluginHandle handle);
+    static unsigned int svpGetMinChannelCount(SVPPluginHandle handle);
+    static unsigned int svpGetMaxChannelCount(SVPPluginHandle handle);
 
-    static void svpSelectProgram(SVPPluginHandle handle, unsigned int program) {
-        if (m_adapterMap.find(handle) == m_adapterMap.end()) return;
-        FeatureExtractionPlugin::ProgramList &list =
-            m_adapterMap[handle]->m_programs;
-        ((Plugin *)handle)->selectProgram(list[program]);
-    }
-
-    static unsigned int svpGetPreferredStepSize(SVPPluginHandle handle) {
-        return ((Plugin *)handle)->getPreferredStepSize();
-    }
-
-    static unsigned int svpGetPreferredBlockSize(SVPPluginHandle handle) {
-        return ((Plugin *)handle)->getPreferredBlockSize();
-    }
-
-    static unsigned int svpGetMinChannelCount(SVPPluginHandle handle) {
-        return ((Plugin *)handle)->getMinChannelCount();
-    }
-
-    static unsigned int svpGetMaxChannelCount(SVPPluginHandle handle) {
-        return ((Plugin *)handle)->getMaxChannelCount();
-    }
-
-    static unsigned int svpGetOutputCount(SVPPluginHandle handle) {
-        if (m_adapterMap.find(handle) == m_adapterMap.end()) return 0;
-        return m_adapterMap[handle]->getOutputCount((Plugin *)handle);
-    }
+    static unsigned int svpGetOutputCount(SVPPluginHandle handle);
 
     static SVPOutputDescriptor *svpGetOutputDescriptor(SVPPluginHandle handle,
-                                                       unsigned int i) {
-        if (m_adapterMap.find(handle) == m_adapterMap.end()) return 0;
-        return m_adapterMap[handle]->getOutputDescriptor((Plugin *)handle, i);
-    }
+                                                       unsigned int i);
 
-    static void svpReleaseOutputDescriptor(SVPOutputDescriptor *desc) {
-        if (desc->name) free((void *)desc->name);
-        if (desc->description) free((void *)desc->description);
-        if (desc->unit) free((void *)desc->unit);
-        for (unsigned int i = 0; i < desc->valueCount; ++i) {
-            free((void *)desc->valueNames[i]);
-        }
-        if (desc->valueNames) free((void *)desc->valueNames);
-        free((void *)desc);
-    }
+    static void svpReleaseOutputDescriptor(SVPOutputDescriptor *desc);
 
     static SVPFeatureList **svpProcess(SVPPluginHandle handle,
                                        float **inputBuffers,
                                        int sec,
-                                       int nsec) {
-        if (m_adapterMap.find(handle) == m_adapterMap.end()) return 0;
-        return m_adapterMap[handle]->process((Plugin *)handle,
-                                             inputBuffers, sec, nsec);
-    }
+                                       int nsec);
 
-    static SVPFeatureList **svpGetRemainingFeatures(SVPPluginHandle handle) {
-        if (m_adapterMap.find(handle) == m_adapterMap.end()) return 0;
-        return m_adapterMap[handle]->getRemainingFeatures((Plugin *)handle);
-    }
+    static SVPFeatureList **svpGetRemainingFeatures(SVPPluginHandle handle);
 
-    static void svpReleaseFeatureSet(SVPFeatureList **fs) {
-        if (!fs) return;
-        for (unsigned int i = 0; fs[i]; ++i) {
-            for (unsigned int j = 0; j < fs[i]->featureCount; ++j) {
-                SVPFeature *feature = &fs[i]->features[j];
-                if (feature->values) free((void *)feature->values);
-                if (feature->label) free((void *)feature->label);
-                free((void *)feature);
-            }
-            if (fs[i]->features) free((void *)fs[i]->features);
-            free((void *)fs[i]);
-        }
-        free((void *)fs);
-    }
+    static void svpReleaseFeatureSet(SVPFeatureList **fs);
 
-    void cleanup(Plugin *plugin) {
-        if (m_pluginOutputs.find(plugin) != m_pluginOutputs.end()) {
-            delete m_pluginOutputs[plugin];
-            m_pluginOutputs.erase(plugin);
-        }
-        m_adapterMap.erase(plugin);
-        delete ((Plugin *)plugin);
-    }
+    void cleanup(FeatureExtractionPlugin *plugin);
+    void checkOutputMap(FeatureExtractionPlugin *plugin);
+    unsigned int getOutputCount(FeatureExtractionPlugin *plugin);
+    SVPOutputDescriptor *getOutputDescriptor(FeatureExtractionPlugin *plugin,
+                                             unsigned int i);
+    SVPFeatureList **process(FeatureExtractionPlugin *plugin,
+                             float **inputBuffers,
+                             int sec, int nsec);
+    SVPFeatureList **getRemainingFeatures(FeatureExtractionPlugin *plugin);
+    SVPFeatureList **convertFeatures(const FeatureExtractionPlugin::FeatureSet &features);
+    
+    typedef std::map<const void *, FeatureExtractionPluginAdapterBase *> AdapterMap;
+    static AdapterMap m_adapterMap;
+    static FeatureExtractionPluginAdapterBase *lookupAdapter(SVPPluginHandle);
 
-    void checkOutputMap(Plugin *plugin) {
-        if (!m_pluginOutputs[plugin]) {
-            m_pluginOutputs[plugin] = new FeatureExtractionPlugin::OutputList
-                (plugin->getOutputDescriptors());
-        }
-    }
-
-    unsigned int getOutputCount(Plugin *plugin) {
-        checkOutputMap(plugin);
-        return m_pluginOutputs[plugin]->size();
-    }
-
-    SVPOutputDescriptor *getOutputDescriptor(Plugin *plugin,
-                                             unsigned int i) {
-
-        checkOutputMap(plugin);
-        FeatureExtractionPlugin::OutputDescriptor &od =
-            (*m_pluginOutputs[plugin])[i];
-
-        SVPOutputDescriptor *desc = (SVPOutputDescriptor *)
-            malloc(sizeof(SVPOutputDescriptor));
-
-        desc->name = strdup(od.name.c_str());
-        desc->description = strdup(od.description.c_str());
-        desc->unit = strdup(od.unit.c_str());
-        desc->hasFixedValueCount = od.hasFixedValueCount;
-        desc->valueCount = od.valueCount;
-
-        desc->valueNames = (const char **)
-            malloc(od.valueCount * sizeof(const char *));
-        
-        for (unsigned int i = 0; i < od.valueCount; ++i) {
-            desc->valueNames[i] = strdup(od.valueNames[i].c_str());
-        }
-
-        desc->hasKnownExtents = od.hasKnownExtents;
-        desc->minValue = od.minValue;
-        desc->maxValue = od.maxValue;
-        desc->isQuantized = od.isQuantized;
-        desc->quantizeStep = od.quantizeStep;
-
-        switch (od.sampleType) {
-        case FeatureExtractionPlugin::OutputDescriptor::OneSamplePerStep:
-            desc->sampleType = svpOneSamplePerStep; break;
-        case FeatureExtractionPlugin::OutputDescriptor::FixedSampleRate:
-            desc->sampleType = svpFixedSampleRate; break;
-        case FeatureExtractionPlugin::OutputDescriptor::VariableSampleRate:
-            desc->sampleType = svpVariableSampleRate; break;
-        }
-
-        desc->sampleRate = od.sampleRate;
-
-        return desc;
-    }
-    
-    SVPFeatureList **process(Plugin *plugin,
-                             float **inputBuffers,
-                             int sec, int nsec) {
-        RealTime rt(sec, nsec);
-        return convertFeatures(plugin->process(inputBuffers, rt));
-    }
-    
-    SVPFeatureList **getRemainingFeatures(Plugin *plugin) {
-        return convertFeatures(plugin->getRemainingFeatures());
-    }
-
-    SVPFeatureList **convertFeatures(const FeatureExtractionPlugin::FeatureSet &features) {
-
-        unsigned int n = 0;
-        if (features.begin() != features.end()) {
-            FeatureExtractionPlugin::FeatureSet::const_iterator i = features.end();
-            --i;
-            n = i->first + 1;
-        }
-
-        if (!n) return 0;
-
-        SVPFeatureList **fs = (SVPFeatureList **)
-            malloc((n + 1) * sizeof(SVPFeatureList *));
-
-        for (unsigned int i = 0; i < n; ++i) {
-            fs[i] = (SVPFeatureList *)malloc(sizeof(SVPFeatureList));
-            if (features.find(i) == features.end()) {
-                fs[i]->featureCount = 0;
-                fs[i]->features = 0;
-            } else {
-                FeatureExtractionPlugin::FeatureSet::const_iterator fi =
-                    features.find(i);
-                const FeatureExtractionPlugin::FeatureList &fl = fi->second;
-                fs[i]->featureCount = fl.size();
-                fs[i]->features = (SVPFeature *)malloc(fl.size() *
-                                                       sizeof(SVPFeature));
-                for (unsigned int j = 0; j < fl.size(); ++j) {
-                    fs[i]->features[j].hasTimestamp = fl[j].hasTimestamp;
-                    fs[i]->features[j].sec = fl[j].timestamp.sec;
-                    fs[i]->features[j].nsec = fl[j].timestamp.nsec;
-                    fs[i]->features[j].valueCount = fl[j].values.size();
-                    fs[i]->features[j].values = (float *)malloc
-                        (fs[i]->features[j].valueCount * sizeof(float));
-                    for (unsigned int k = 0; k < fs[i]->features[j].valueCount; ++k) {
-                        fs[i]->features[j].values[k] = fl[j].values[k];
-                    }
-                    fs[i]->features[j].label = strdup(fl[j].label.c_str());
-                }
-            }
-        }
-
-        fs[n] = 0;
-
-        return fs;
-    }
-
-    typedef std::map<const void *, FeatureExtractionPluginAdapter *> AdapterMap;
-    static AdapterMap m_adapterMap;
-
+    bool m_populated;
     SVPPluginDescriptor m_descriptor;
     FeatureExtractionPlugin::ParameterList m_parameters;
     FeatureExtractionPlugin::ProgramList m_programs;
-
-    typedef std::map<Plugin *, FeatureExtractionPlugin::OutputList *> OutputMap;
+    
+    typedef std::map<FeatureExtractionPlugin *,
+                     FeatureExtractionPlugin::OutputList *> OutputMap;
     OutputMap m_pluginOutputs;
 
-    typedef std::map<Plugin *, SVPFeature ***> FeatureBufferMap;
+    typedef std::map<FeatureExtractionPlugin *,
+                     SVPFeature ***> FeatureBufferMap;
     FeatureBufferMap m_pluginFeatures;
 };
 
 template <typename Plugin>
-typename FeatureExtractionPluginAdapter<Plugin>::AdapterMap 
-FeatureExtractionPluginAdapter<Plugin>::m_adapterMap;
+class FeatureExtractionPluginAdapter : public FeatureExtractionPluginAdapterBase
+{
+public:
+    FeatureExtractionPluginAdapter() : FeatureExtractionPluginAdapterBase() { }
+    ~FeatureExtractionPluginAdapter() { }
+
+protected:
+    FeatureExtractionPlugin *createPlugin(float inputSampleRate) {
+        Plugin *plugin = new Plugin(inputSampleRate);
+        FeatureExtractionPlugin *fep =
+            dynamic_cast<FeatureExtractionPlugin *>(plugin);
+        if (!fep) {
+            std::cerr << "ERROR: FeatureExtractionPlugin::createPlugin: "
+                      << "Plugin is not a feature extraction plugin"
+                      << std::endl;
+            delete plugin;
+            return 0;
+        }
+        return fep;
+    }
+};
+    
 
 #endif
 
--- a/plugin/FeatureExtractionPluginHostAdapter.cpp	Fri Mar 24 18:15:50 2006 +0000
+++ b/plugin/FeatureExtractionPluginHostAdapter.cpp	Mon Mar 27 15:03:02 2006 +0000
@@ -78,14 +78,34 @@
 FeatureExtractionPluginHostAdapter::ParameterList
 FeatureExtractionPluginHostAdapter::getParameterDescriptors() const
 {
-    //!!!
-    return ParameterList();
+    ParameterList list;
+    for (unsigned int i = 0; i < m_descriptor->parameterCount; ++i) {
+        const SVPParameterDescriptor *spd = m_descriptor->parameters[i];
+        ParameterDescriptor pd;
+        pd.name = spd->name;
+        pd.description = spd->description;
+        pd.unit = spd->unit;
+        pd.minValue = spd->minValue;
+        pd.maxValue = spd->maxValue;
+        pd.defaultValue = spd->defaultValue;
+        pd.isQuantized = spd->isQuantized;
+        pd.quantizeStep = spd->quantizeStep;
+        list.push_back(pd);
+    }
+    return list;
 }
 
 float
 FeatureExtractionPluginHostAdapter::getParameter(std::string param) const
 {
-    //!!!
+    if (!m_handle) return 0.0;
+
+    for (unsigned int i = 0; i < m_descriptor->parameterCount; ++i) {
+        if (param == m_descriptor->parameters[i]->name) {
+            return m_descriptor->getParameter(m_handle, i);
+        }
+    }
+
     return 0.0;
 }
 
@@ -93,27 +113,48 @@
 FeatureExtractionPluginHostAdapter::setParameter(std::string param, 
                                                  float value)
 {
-    //!!!
+    if (!m_handle) return;
+
+    for (unsigned int i = 0; i < m_descriptor->parameterCount; ++i) {
+        if (param == m_descriptor->parameters[i]->name) {
+            m_descriptor->setParameter(m_handle, i, value);
+            return;
+        }
+    }
 }
 
 FeatureExtractionPluginHostAdapter::ProgramList
 FeatureExtractionPluginHostAdapter::getPrograms() const
 {
-    //!!!
-    return ProgramList();
+    ProgramList list;
+    
+    for (unsigned int i = 0; i < m_descriptor->programCount; ++i) {
+        list.push_back(m_descriptor->programs[i]);
+    }
+    
+    return list;
 }
 
 std::string
 FeatureExtractionPluginHostAdapter::getCurrentProgram() const
 {
-    //!!!
-    return "";
+    if (!m_handle) return "";
+
+    int pn = m_descriptor->getCurrentProgram(m_handle);
+    return m_descriptor->programs[pn];
 }
 
 void
 FeatureExtractionPluginHostAdapter::selectProgram(std::string program)
 {
-    //!!!
+    if (!m_handle) return;
+
+    for (unsigned int i = 0; i < m_descriptor->programCount; ++i) {
+        if (program == m_descriptor->programs[i]) {
+            m_descriptor->selectProgram(m_handle, i);
+            return;
+        }
+    }
 }
 
 size_t
@@ -133,22 +174,105 @@
 FeatureExtractionPluginHostAdapter::OutputList
 FeatureExtractionPluginHostAdapter::getOutputDescriptors() const
 {
-    //!!!
-    return OutputList();
+    OutputList list;
+    if (!m_handle) return list;
+
+    unsigned int count = m_descriptor->getOutputCount(m_handle);
+
+    for (unsigned int i = 0; i < count; ++i) {
+        SVPOutputDescriptor *sd = m_descriptor->getOutputDescriptor(m_handle, i);
+        OutputDescriptor d;
+        d.name = sd->name;
+        d.description = sd->description;
+        d.unit = sd->unit;
+        d.hasFixedValueCount = sd->hasFixedValueCount;
+        d.valueCount = sd->valueCount;
+        for (unsigned int j = 0; j < sd->valueCount; ++j) {
+            d.valueNames.push_back(sd->valueNames[i]);
+        }
+        d.hasKnownExtents = sd->hasKnownExtents;
+        d.minValue = sd->minValue;
+        d.maxValue = sd->maxValue;
+        d.isQuantized = sd->isQuantized;
+        d.quantizeStep = sd->quantizeStep;
+
+        switch (sd->sampleType) {
+        case svpOneSamplePerStep:
+            d.sampleType = OutputDescriptor::OneSamplePerStep; break;
+        case svpFixedSampleRate:
+            d.sampleType = OutputDescriptor::FixedSampleRate; break;
+        case svpVariableSampleRate:
+            d.sampleType = OutputDescriptor::VariableSampleRate; break;
+        }
+
+        d.sampleRate = sd->sampleRate;
+
+        list.push_back(d);
+
+        m_descriptor->releaseOutputDescriptor(sd);
+    }
+
+    return list;
 }
 
 FeatureExtractionPluginHostAdapter::FeatureSet
 FeatureExtractionPluginHostAdapter::process(float **inputBuffers,
                                             RealTime timestamp)
 {
-    //!!!
-    return FeatureSet();
+    FeatureSet fs;
+    if (!m_handle) return fs;
+
+    int sec = timestamp.sec;
+    int nsec = timestamp.nsec;
+    
+    SVPFeatureList **features = m_descriptor->process(m_handle,
+                                                      inputBuffers,
+                                                      sec, nsec);
+    
+    convertFeatures(features, fs);
+    m_descriptor->releaseFeatureSet(features);
+    return fs;
 }
 
 FeatureExtractionPluginHostAdapter::FeatureSet
 FeatureExtractionPluginHostAdapter::getRemainingFeatures()
 {
-    //!!!
-    return FeatureSet();
+    FeatureSet fs;
+    if (!m_handle) return fs;
+    
+    SVPFeatureList **features = m_descriptor->getRemainingFeatures(m_handle); 
+
+    convertFeatures(features, fs);
+    m_descriptor->releaseFeatureSet(features);
+    return fs;
 }
 
+void
+FeatureExtractionPluginHostAdapter::convertFeatures(SVPFeatureList **features,
+                                                    FeatureSet &fs)
+{
+    for (unsigned int i = 0; features[i]; ++i) {
+        
+        SVPFeatureList &list = *features[i];
+
+        if (list.featureCount > 0) {
+
+            for (unsigned int j = 0; j < list.featureCount; ++j) {
+                
+                Feature feature;
+                feature.hasTimestamp = list.features[j].hasTimestamp;
+                feature.timestamp = RealTime(list.features[j].sec,
+                                             list.features[j].nsec);
+
+                for (unsigned int k = 0; k < list.features[j].valueCount; ++k) {
+                    feature.values.push_back(list.features[j].values[k]);
+                }
+                
+                feature.label = list.features[j].label;
+
+                fs[i].push_back(feature);
+            }
+        }
+    }
+}
+
--- a/plugin/FeatureExtractionPluginHostAdapter.h	Fri Mar 24 18:15:50 2006 +0000
+++ b/plugin/FeatureExtractionPluginHostAdapter.h	Mon Mar 27 15:03:02 2006 +0000
@@ -54,6 +54,8 @@
     FeatureSet getRemainingFeatures();
 
 protected:
+    void convertFeatures(SVPFeatureList **, FeatureSet &);
+
     const SVPPluginDescriptor *m_descriptor;
     SVPPluginHandle m_handle;
 };
--- a/plugin/LADSPAPluginFactory.cpp	Fri Mar 24 18:15:50 2006 +0000
+++ b/plugin/LADSPAPluginFactory.cpp	Mon Mar 27 15:03:02 2006 +0000
@@ -125,6 +125,19 @@
     unloadUnusedLibraries();
 }
 	
+const RealTimePluginDescriptor *
+LADSPAPluginFactory::getPluginDescriptor(QString identifier) const
+{
+    std::map<QString, RealTimePluginDescriptor *>::const_iterator i =
+        m_rtDescriptors.find(identifier);
+
+    if (i != m_rtDescriptors.end()) {
+        return i->second;
+    } 
+
+    return 0;
+}
+
 float
 LADSPAPluginFactory::getPortMinimum(const LADSPA_Descriptor *descriptor, int port)
 {
@@ -573,6 +586,17 @@
     int index = 0;
     while ((descriptor = fn(index))) {
 
+        RealTimePluginDescriptor *rtd = new RealTimePluginDescriptor;
+        rtd->name = descriptor->Name;
+        rtd->label = descriptor->Label;
+        rtd->maker = descriptor->Maker;
+        rtd->copyright = descriptor->Copyright;
+        rtd->category = "";
+        rtd->isSynth = false;
+        rtd->parameterCount = 0;
+        rtd->audioInputPortCount = 0;
+        rtd->controlOutputPortCount = 0;
+
 #ifdef HAVE_LRDF
 	char *def_uri = 0;
 	lrdf_defaults *defs = 0;
@@ -588,6 +612,8 @@
 	    }
 	}
 	
+        rtd->category = category.toStdString();
+
 //	std::cerr << "Plugin id is " << descriptor->UniqueID
 //		  << ", category is \"" << (category ? category : QString("(none)"))
 //		  << "\", name is " << descriptor->Name
@@ -621,10 +647,31 @@
 	}
 #endif // HAVE_LRDF
 
+	for (unsigned long i = 0; i < descriptor->PortCount; i++) {
+	    if (LADSPA_IS_PORT_CONTROL(descriptor->PortDescriptors[i])) {
+                if (LADSPA_IS_PORT_INPUT(descriptor->PortDescriptors[i])) {
+                    ++rtd->parameterCount;
+                } else {
+                    if (strcmp(descriptor->PortNames[i], "latency") &&
+                        strcmp(descriptor->PortNames[i], "_latency")) {
+                        ++rtd->controlOutputPortCount;
+                        rtd->controlOutputPortNames.push_back
+                            (descriptor->PortNames[i]);
+                    }
+                }
+            } else {
+                if (LADSPA_IS_PORT_INPUT(descriptor->PortDescriptors[i])) {
+                    ++rtd->audioInputPortCount;
+                }
+            }
+        }
+
 	QString identifier = PluginIdentifier::createIdentifier
 	    ("ladspa", soname, descriptor->Label);
 	m_identifiers.push_back(identifier);
 
+        m_rtDescriptors[identifier] = rtd;
+
 	++index;
     }
 
--- a/plugin/LADSPAPluginFactory.h	Fri Mar 24 18:15:50 2006 +0000
+++ b/plugin/LADSPAPluginFactory.h	Mon Mar 27 15:03:02 2006 +0000
@@ -42,6 +42,8 @@
 
     virtual void enumeratePlugins(std::vector<QString> &list);
 
+    virtual const RealTimePluginDescriptor *getPluginDescriptor(QString identifier) const;
+
     virtual RealTimePluginInstance *instantiatePlugin(QString identifier,
 						      int clientId,
 						      int position,
@@ -78,6 +80,7 @@
     void unloadUnusedLibraries();
 
     std::vector<QString> m_identifiers;
+    std::map<QString, RealTimePluginDescriptor *> m_rtDescriptors;
 
     std::map<unsigned long, QString> m_taxonomy;
     std::map<QString, QString> m_fallbackCategories;
--- a/plugin/LADSPAPluginInstance.cpp	Fri Mar 24 18:15:50 2006 +0000
+++ b/plugin/LADSPAPluginInstance.cpp	Mon Mar 27 15:03:02 2006 +0000
@@ -414,6 +414,13 @@
 }
 
 float
+LADSPAPluginInstance::getControlOutputValue(unsigned int output) const
+{
+    if (output > m_controlPortsOut.size()) return 0.0;
+    return (*m_controlPortsOut[output].second);
+}
+
+float
 LADSPAPluginInstance::getParameterValue(unsigned int parameter) const
 {
     if (parameter >= m_controlPortsIn.size()) return 0.0;
--- a/plugin/LADSPAPluginInstance.h	Fri Mar 24 18:15:50 2006 +0000
+++ b/plugin/LADSPAPluginInstance.h	Mon Mar 27 15:03:02 2006 +0000
@@ -66,6 +66,9 @@
     virtual sample_t **getAudioInputBuffers() { return m_inputBuffers; }
     virtual sample_t **getAudioOutputBuffers() { return m_outputBuffers; }
 
+    virtual size_t getControlOutputCount() const { return m_controlPortsOut.size(); }
+    virtual float getControlOutputValue(size_t n) const;
+
     virtual bool isBypassed() const { return m_bypassed; }
     virtual void setBypassed(bool bypassed) { m_bypassed = bypassed; }
 
--- a/plugin/RealTimePluginFactory.h	Fri Mar 24 18:15:50 2006 +0000
+++ b/plugin/RealTimePluginFactory.h	Mon Mar 27 15:03:02 2006 +0000
@@ -27,6 +27,21 @@
 
 class RealTimePluginInstance;
 
+class RealTimePluginDescriptor
+{
+public:
+    std::string name;
+    std::string label;
+    std::string maker;
+    std::string copyright;
+    std::string category;
+    bool isSynth;
+    unsigned int parameterCount;
+    unsigned int audioInputPortCount;
+    unsigned int controlOutputPortCount;
+    std::vector<std::string> controlOutputPortNames;
+};
+
 class RealTimePluginFactory
 {
 public:
@@ -57,6 +72,11 @@
     virtual void enumeratePlugins(std::vector<QString> &list) = 0;
 
     /**
+     * Get some basic information about a plugin (rapidly).
+     */
+    virtual const RealTimePluginDescriptor *getPluginDescriptor(QString identifier) const = 0;
+
+    /**
      * Instantiate a plugin.
      */
     virtual RealTimePluginInstance *instantiatePlugin(QString identifier,
--- a/plugin/RealTimePluginInstance.h	Fri Mar 24 18:15:50 2006 +0000
+++ b/plugin/RealTimePluginInstance.h	Mon Mar 27 15:03:02 2006 +0000
@@ -33,9 +33,9 @@
 class RealTimePluginFactory;
 	
 /**
- * RealTimePluginInstance is a very trivial interface that an audio
- * process can use to refer to an instance of a plugin without needing
- * to know what type of plugin it is.
+ * RealTimePluginInstance is an interface that an audio process can
+ * use to refer to an instance of a plugin without needing to know
+ * what type of plugin it is.
  *
  * The audio code calls run() on an instance that has been passed to
  * it, and assumes that the passing code has already initialised the
@@ -47,6 +47,13 @@
  * which the subclass of RealTimePluginInstance must implement.
  */
 
+/*
+ * N.B. RealTimePluginInstance, RealTimePluginFactory and their
+ * subclasses are terrible code.  They've been reused, cut and pasted
+ * and mangled too many times to fit too many different uses, and
+ * could do with a good tidy.
+ */
+
 // These names are taken from LADSPA, but the values are not
 // guaranteed to match
 
@@ -91,6 +98,10 @@
     virtual sample_t **getAudioInputBuffers() = 0;
     virtual sample_t **getAudioOutputBuffers() = 0;
 
+    // Control inputs are known as parameters here
+    virtual size_t getControlOutputCount() const = 0;
+    virtual float getControlOutputValue(size_t n) const = 0;
+
 //     virtual QStringList getPrograms() const { return QStringList(); }
 //     virtual QString getCurrentProgram() const { return QString(); }
     virtual std::string getProgram(int /* bank */, int /* program */) const { return std::string(); }
--- a/plugin/api/svp.h	Fri Mar 24 18:15:50 2006 +0000
+++ b/plugin/api/svp.h	Mon Mar 27 15:03:02 2006 +0000
@@ -113,8 +113,8 @@
     unsigned int (*getPreferredBlockSize)(SVPPluginHandle);
     unsigned int (*getMinChannelCount)(SVPPluginHandle);
     unsigned int (*getMaxChannelCount)(SVPPluginHandle);
+
     unsigned int (*getOutputCount)(SVPPluginHandle);
-
     SVPOutputDescriptor *(*getOutputDescriptor)(SVPPluginHandle,
                                                 unsigned int);
     void (*releaseOutputDescriptor)(SVPOutputDescriptor *);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/transform/RealTimePluginTransform.cpp	Mon Mar 27 15:03:02 2006 +0000
@@ -0,0 +1,160 @@
+
+/* -*- 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 file copyright 2006 Chris Cannam.
+    
+    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 "RealTimePluginTransform.h"
+
+#include "plugin/RealTimePluginFactory.h"
+#include "plugin/RealTimePluginInstance.h"
+
+#include "base/Model.h"
+#include "model/SparseTimeValueModel.h"
+#include "model/DenseTimeValueModel.h"
+
+#include <iostream>
+
+RealTimePluginTransform::RealTimePluginTransform(Model *inputModel,
+                                                 QString pluginId,
+                                                 QString configurationXml,
+                                                 int output) :
+    Transform(inputModel),
+    m_plugin(0),
+    m_outputNo(output)
+{
+    std::cerr << "RealTimePluginTransform::RealTimePluginTransform: plugin " << pluginId.toStdString() << ", output " << output << std::endl;
+
+    RealTimePluginFactory *factory =
+	RealTimePluginFactory::instanceFor(pluginId);
+
+    if (!factory) {
+	std::cerr << "RealTimePluginTransform: No factory available for plugin id \""
+		  << pluginId.toStdString() << "\"" << std::endl;
+	return;
+    }
+
+    DenseTimeValueModel *input = getInput();
+    if (!input) return;
+
+    m_plugin = factory->instantiatePlugin(pluginId, 0, 0, m_input->getSampleRate(),
+                                          1024, //!!! wants to be configurable
+                                          input->getChannelCount());
+
+    if (!m_plugin) {
+	std::cerr << "RealTimePluginTransform: Failed to instantiate plugin \""
+		  << pluginId.toStdString() << "\"" << std::endl;
+	return;
+    }
+
+    if (configurationXml != "") {
+        m_plugin->setParametersFromXml(configurationXml);
+    }
+
+    if (m_outputNo >= m_plugin->getControlOutputCount()) {
+        std::cerr << "RealTimePluginTransform: Plugin has fewer than desired " << m_outputNo << " control outputs" << std::endl;
+        return;
+    }
+	
+    m_output = new SparseTimeValueModel(input->getSampleRate(),
+                                        1024, //!!!
+                                        0.0, 0.0, false);
+}
+
+RealTimePluginTransform::~RealTimePluginTransform()
+{
+    delete m_plugin;
+}
+
+DenseTimeValueModel *
+RealTimePluginTransform::getInput()
+{
+    DenseTimeValueModel *dtvm =
+	dynamic_cast<DenseTimeValueModel *>(getInputModel());
+    if (!dtvm) {
+	std::cerr << "RealTimePluginTransform::getInput: WARNING: Input model is not conformable to DenseTimeValueModel" << std::endl;
+    }
+    return dtvm;
+}
+
+void
+RealTimePluginTransform::run()
+{
+    DenseTimeValueModel *input = getInput();
+    if (!input) return;
+
+    SparseTimeValueModel *model = dynamic_cast<SparseTimeValueModel *>(m_output);
+    if (!model) return;
+
+    if (m_outputNo >= m_plugin->getControlOutputCount()) return;
+
+    size_t sampleRate = input->getSampleRate();
+    int channelCount = input->getChannelCount();
+    size_t blockSize = m_plugin->getBufferSize();
+
+    float **buffers = m_plugin->getAudioInputBuffers();
+
+    size_t startFrame = m_input->getStartFrame();
+    size_t   endFrame = m_input->getEndFrame();
+    size_t blockFrame = startFrame;
+
+    size_t prevCompletion = 0;
+
+    int i = 0;
+
+    while (blockFrame < endFrame) {
+
+	std::cout << "RealTimePluginTransform::run: blockFrame "
+		  << blockFrame;
+
+	size_t completion =
+	    (((blockFrame - startFrame) / blockSize) * 99) /
+	    (   (endFrame - startFrame) / blockSize);
+
+	size_t got = 0;
+
+	if (channelCount == 1) {
+	    got = input->getValues
+		(-1, blockFrame, blockFrame + blockSize, buffers[0]);
+	    while (got < blockSize) {
+		buffers[0][got++] = 0.0;
+	    }
+	} else {
+	    for (size_t ch = 0; ch < channelCount; ++ch) {
+		got = input->getValues
+		    (ch, blockFrame, blockFrame + blockSize, buffers[ch]);
+		while (got < blockSize) {
+		    buffers[ch][got++] = 0.0;
+		}
+	    }
+	}
+
+        m_plugin->run(RealTime::frame2RealTime(blockFrame, sampleRate));
+
+        float value = m_plugin->getControlOutputValue(m_outputNo);
+
+        std::cout << " value " << value << std::endl;
+
+	model->addPoint(SparseTimeValueModel::Point(blockFrame, value, ""));
+
+	if (blockFrame == startFrame || completion > prevCompletion) {
+	    model->setCompletion(completion);
+	    prevCompletion = completion;
+	}
+        
+	blockFrame += blockSize;
+    }
+    
+    model->setCompletion(100);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/transform/RealTimePluginTransform.h	Mon Mar 27 15:03:02 2006 +0000
@@ -0,0 +1,44 @@
+/* -*- 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 file copyright 2006 Chris Cannam.
+    
+    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 _REAL_TIME_PLUGIN_TRANSFORM_H_
+#define _REAL_TIME_PLUGIN_TRANSFORM_H_
+
+#include "Transform.h"
+#include "RealTimePluginInstance.h"
+
+class DenseTimeValueModel;
+
+class RealTimePluginTransform : public Transform
+{
+public:
+    RealTimePluginTransform(Model *inputModel,
+			    QString plugin,
+			    QString configurationXml = "",
+			    int output = 0);
+    virtual ~RealTimePluginTransform();
+
+protected:
+    virtual void run();
+
+    RealTimePluginInstance *m_plugin;
+    int m_outputNo;
+
+    // just casts
+    DenseTimeValueModel *getInput();
+};
+
+#endif
+
--- a/transform/TransformFactory.cpp	Fri Mar 24 18:15:50 2006 +0000
+++ b/transform/TransformFactory.cpp	Mon Mar 27 15:03:02 2006 +0000
@@ -16,8 +16,10 @@
 #include "TransformFactory.h"
 
 #include "FeatureExtractionPluginTransform.h"
+#include "RealTimePluginTransform.h"
 
 #include "plugin/FeatureExtractionPluginFactory.h"
+#include "plugin/RealTimePluginFactory.h"
 
 #include "widgets/PluginParameterDialog.h"
 
@@ -53,14 +55,57 @@
 void
 TransformFactory::populateTransforms()
 {
-    std::vector<QString> fexplugs =
+    TransformDescriptionMap transforms;
+
+    populateFeatureExtractionPlugins(transforms);
+    populateRealTimePlugins(transforms);
+
+    // disambiguate plugins with similar descriptions
+
+    std::map<QString, int> descriptions;
+
+    for (TransformDescriptionMap::iterator i = transforms.begin();
+         i != transforms.end(); ++i) {
+
+        TransformDesc desc = i->second;
+
+	++descriptions[desc.description];
+	++descriptions[QString("%1 [%2]").arg(desc.description).arg(desc.maker)];
+    }
+
+    std::map<QString, int> counts;
+    m_transforms.clear();
+
+    for (TransformDescriptionMap::iterator i = transforms.begin();
+         i != transforms.end(); ++i) {
+
+        TransformDesc desc = i->second;
+	QString name = desc.name;
+        QString description = desc.description;
+        QString maker = desc.maker;
+
+	if (descriptions[description] > 1) {
+	    description = QString("%1 [%2]").arg(description).arg(maker);
+	    if (descriptions[description] > 1) {
+		description = QString("%1 <%2>")
+		    .arg(description).arg(++counts[description]);
+	    }
+	}
+
+        desc.description = description;
+	m_transforms[name] = desc;
+    }	    
+}
+
+void
+TransformFactory::populateFeatureExtractionPlugins(TransformDescriptionMap &transforms)
+{
+    std::vector<QString> plugs =
 	FeatureExtractionPluginFactory::getAllPluginIdentifiers();
 
-    std::map<QString, QString> makers;
+    for (size_t i = 0; i < plugs.size(); ++i) {
 
-    for (size_t i = 0; i < fexplugs.size(); ++i) {
-
-	QString pluginId = fexplugs[i];
+	QString pluginId = plugs[i];
 
 	FeatureExtractionPluginFactory *factory =
 	    FeatureExtractionPluginFactory::instanceFor(pluginId);
@@ -88,62 +133,92 @@
 		    .arg(pluginId).arg(outputs[j].name.c_str());
 
 	    QString userDescription;
+            QString friendlyName;
 
 	    if (outputs.size() == 1) {
 		userDescription = pluginDescription;
+                friendlyName = pluginDescription;
 	    } else {
 		userDescription = QString("%1: %2")
 		    .arg(pluginDescription)
 		    .arg(outputs[j].description.c_str());
+                friendlyName = outputs[j].description.c_str();
 	    }
 
             bool configurable = (!plugin->getPrograms().empty() ||
                                  !plugin->getParameterDescriptors().empty());
 
-	    m_transforms[transformName] = 
+	    transforms[transformName] = 
                 TransformDesc(transformName,
                               userDescription,
+                              friendlyName,
+                              plugin->getMaker().c_str(),
                               configurable);
-	    
-	    makers[transformName] = plugin->getMaker().c_str();
 	}
     }
+}
 
-    // disambiguate plugins with similar descriptions
+void
+TransformFactory::populateRealTimePlugins(TransformDescriptionMap &transforms)
+{
+    std::vector<QString> plugs =
+	RealTimePluginFactory::getAllPluginIdentifiers();
 
-    std::map<QString, int> descriptions;
+    for (size_t i = 0; i < plugs.size(); ++i) {
+        
+	QString pluginId = plugs[i];
 
-    for (TransformDescriptionMap::iterator i = m_transforms.begin();
-         i != m_transforms.end(); ++i) {
+        RealTimePluginFactory *factory =
+            RealTimePluginFactory::instanceFor(pluginId);
 
-        TransformDesc desc = i->second;
-
-	++descriptions[desc.description];
-	++descriptions[QString("%1 [%2]").arg(desc.description).arg(makers[desc.name])];
-    }
-
-    std::map<QString, int> counts;
-    TransformDescriptionMap newMap;
-
-    for (TransformDescriptionMap::iterator i = m_transforms.begin();
-         i != m_transforms.end(); ++i) {
-
-        TransformDesc desc = i->second;
-	QString name = desc.name, description = desc.description;
-
-	if (descriptions[description] > 1) {
-	    description = QString("%1 [%2]").arg(description).arg(makers[name]);
-	    if (descriptions[description] > 1) {
-		description = QString("%1 <%2>")
-		    .arg(description).arg(++counts[description]);
-	    }
+	if (!factory) {
+	    std::cerr << "WARNING: TransformFactory::populateTransforms: No real time plugin factory for instance " << pluginId.toLocal8Bit().data() << std::endl;
+	    continue;
 	}
 
-        desc.description = description;
-	newMap[name] = desc;
-    }	    
-	    
-    m_transforms = newMap;
+        const RealTimePluginDescriptor *descriptor =
+            factory->getPluginDescriptor(pluginId);
+
+        if (!descriptor) {
+	    std::cerr << "WARNING: TransformFactory::populateTransforms: Failed to query plugin " << pluginId.toLocal8Bit().data() << std::endl;
+	    continue;
+	}
+	
+        if (descriptor->controlOutputPortCount == 0 ||
+            descriptor->audioInputPortCount == 0) continue;
+
+        std::cout << "TransformFactory::populateRealTimePlugins: plugin " << pluginId.toStdString() << " has " << descriptor->controlOutputPortCount << " output ports" << std::endl;
+	
+	QString pluginDescription = descriptor->name.c_str();
+
+	for (size_t j = 0; j < descriptor->controlOutputPortCount; ++j) {
+
+	    QString transformName = QString("%1:%2").arg(pluginId).arg(j);
+	    QString userDescription;
+
+	    if (j < descriptor->controlOutputPortNames.size() &&
+                descriptor->controlOutputPortNames[j] != "") {
+		userDescription = tr("%1: %2")
+                    .arg(pluginDescription)
+                    .arg(descriptor->controlOutputPortNames[j].c_str());
+	    } else if (descriptor->controlOutputPortCount > 1) {
+		userDescription = tr("%1: Output %2")
+		    .arg(pluginDescription)
+		    .arg(j + 1);
+	    } else {
+                userDescription = pluginDescription;
+            }
+
+            bool configurable = (descriptor->parameterCount > 0);
+
+	    transforms[transformName] = 
+                TransformDesc(transformName,
+                              userDescription,
+                              userDescription,
+                              descriptor->maker.c_str(),
+                              configurable);
+	}
+    }
 }
 
 QString
@@ -157,14 +232,9 @@
 QString
 TransformFactory::getTransformFriendlyName(TransformName name)
 {
-    QString description = getTransformDescription(name);
-
-    int i = description.indexOf(':');
-    if (i >= 0) {
-	return description.remove(0, i + 2);
-    } else {
-	return description;
-    }
+    if (m_transforms.find(name) != m_transforms.end()) {
+	return m_transforms[name].friendlyName;
+    } else return "";
 }
 
 bool
@@ -188,22 +258,30 @@
 
     std::cerr << "last configuration: " << configurationXml.toStdString() << std::endl;
 
+    PluginInstance *plugin = 0;
+
     if (FeatureExtractionPluginFactory::instanceFor(id)) {
-        FeatureExtractionPlugin *plugin =
-            FeatureExtractionPluginFactory::instanceFor(id)->instantiatePlugin
+
+        plugin = FeatureExtractionPluginFactory::instanceFor(id)->instantiatePlugin
             (id, inputModel->getSampleRate());
-        if (plugin) {
-            if (configurationXml != "") {
-                plugin->setParametersFromXml(configurationXml);
-            }
-            PluginParameterDialog *dialog = new PluginParameterDialog(plugin);
-            if (dialog->exec() == QDialog::Accepted) {
-                ok = true;
-            }
-            configurationXml = plugin->toXmlString();
-            delete dialog;
-            delete plugin;
+
+    } else if (RealTimePluginFactory::instanceFor(id)) {
+
+        plugin = RealTimePluginFactory::instanceFor(id)->instantiatePlugin
+            (id, 0, 0, inputModel->getSampleRate(), 1024, 1);
+    }
+
+    if (plugin) {
+        if (configurationXml != "") {
+            plugin->setParametersFromXml(configurationXml);
         }
+        PluginParameterDialog *dialog = new PluginParameterDialog(plugin);
+        if (dialog->exec() == QDialog::Accepted) {
+            ok = true;
+        }
+        configurationXml = plugin->toXmlString();
+        delete dialog;
+        delete plugin;
     }
 
     if (ok) m_lastConfigurations[name] = configurationXml;
@@ -217,11 +295,6 @@
 {
     Transform *transform = 0;
 
-    // The only transform type we support at the moment is the
-    // FeatureExtractionPluginTransform.  In future we may wish to
-    // support e.g. RealTimePluginTransform for audio->audio or
-    // audio->midi transforms using standard effects plugins.
-
     QString id = name.section(':', 0, 2);
     QString output = name.section(':', 3);
 
@@ -230,6 +303,11 @@
                                                          id,
                                                          configurationXml,
                                                          output);
+    } else if (RealTimePluginFactory::instanceFor(id)) {
+        transform = new RealTimePluginTransform(inputModel,
+                                                id,
+                                                configurationXml,
+                                                output.toInt());
     } else {
         std::cerr << "TransformFactory::createTransform: Unknown transform "
                   << name.toStdString() << std::endl;
--- a/transform/TransformFactory.h	Fri Mar 24 18:15:50 2006 +0000
+++ b/transform/TransformFactory.h	Mon Mar 27 15:03:02 2006 +0000
@@ -33,14 +33,20 @@
     // within the application.  The description is intended to be
     // human readable.  In principle it doesn't have to be unique, but
     // the factory will add suffixes to ensure that it is, all the
-    // same (just to avoid user confusion).
+    // same (just to avoid user confusion).  The friendly name is a
+    // shorter version of the description.
 
     struct TransformDesc {
         TransformDesc() { }
-	TransformDesc(TransformName _name, QString _description, bool _configurable) :
-	    name(_name), description(_description), configurable(_configurable) { }
+	TransformDesc(TransformName _name, QString _description,
+                      QString _friendlyName, QString _maker,
+                      bool _configurable) :
+	    name(_name), description(_description), friendlyName(_friendlyName),
+            maker(_maker), configurable(_configurable) { }
 	TransformName name;
 	QString description;
+        QString friendlyName;
+        QString maker;
         bool configurable;
     };
     typedef std::vector<TransformDesc> TransformList;
@@ -116,6 +122,8 @@
     TransformDescriptionMap m_transforms;
 
     void populateTransforms();
+    void populateFeatureExtractionPlugins(TransformDescriptionMap &);
+    void populateRealTimePlugins(TransformDescriptionMap &);
 
     static TransformFactory *m_instance;
 };