changeset 350:d7c41483af8f

* Merge from transforms branch -- switch over to using Transform object properly
author Chris Cannam
date Fri, 07 Dec 2007 16:47:31 +0000
parents edda24bb85fc
children 7263e37d8913
files base/RealTime.cpp base/RealTime.h base/Window.h plugin/plugin.pro plugin/transform/FeatureExtractionModelTransformer.cpp plugin/transform/FeatureExtractionModelTransformer.h plugin/transform/ModelTransformer.cpp plugin/transform/ModelTransformer.h plugin/transform/ModelTransformerFactory.cpp plugin/transform/ModelTransformerFactory.h plugin/transform/PluginTransformer.cpp plugin/transform/PluginTransformer.h plugin/transform/RealTimeEffectModelTransformer.cpp plugin/transform/RealTimeEffectModelTransformer.h plugin/transform/Transform.cpp plugin/transform/Transform.h plugin/transform/TransformDescription.h plugin/transform/TransformFactory.cpp plugin/transform/TransformFactory.h
diffstat 19 files changed, 983 insertions(+), 498 deletions(-) [+]
line wrap: on
line diff
--- a/base/RealTime.cpp	Fri Nov 30 17:31:09 2007 +0000
+++ b/base/RealTime.cpp	Fri Dec 07 16:47:31 2007 +0000
@@ -122,6 +122,51 @@
     return s.substr(0, s.length() - 1);
 }
 
+RealTime
+RealTime::fromString(std::string s)
+{
+    bool negative = false;
+    bool faulty = false;
+    bool section = 0;
+    std::string ssec, snsec;
+
+    for (size_t i = 0; i < s.length(); ++i) {
+
+        char c = s[i];
+        if (isspace(c)) continue;
+
+        if (section == 0) {
+
+            if (c == '-') negative = true;
+            else if (isdigit(c)) { section = 1; ssec += c; }
+            else if (c == '.') section = 2;
+            else break;
+
+        } else if (section == 1) {
+
+            if (c == '.') section = 2;
+            else if (isdigit(c)) ssec += c;
+            else break;
+
+        } else if (section == 2) {
+
+            if (isdigit(c)) snsec += c;
+            else break;
+        }
+    }
+
+    while (snsec.length() < 8) snsec += '0';
+
+    int sec = atoi(ssec.c_str());
+    int nsec = atoi(snsec.c_str());
+    if (negative) sec = -sec;
+
+    std::cerr << "RealTime::fromString: string " << s << " -> "
+              << sec << " sec, " << nsec << " nsec" << std::endl;
+
+    return RealTime(sec, nsec);
+}
+
 std::string
 RealTime::toText(bool fixedDp) const
 {
--- a/base/RealTime.h	Fri Nov 30 17:31:09 2007 +0000
+++ b/base/RealTime.h	Fri Dec 07 16:47:31 2007 +0000
@@ -95,30 +95,45 @@
     RealTime operator*(int m) const;
     RealTime operator/(int d) const;
 
-    // Find the fractional difference between times
-    //
+    /**
+     * Return the ratio of two times.
+     */
     double operator/(const RealTime &r) const;
 
-    // Return a human-readable debug-type string to full precision
-    // (probably not a format to show to a user directly).  If align
-    // is true, prepend " " to the start of positive values so that
-    // they line up with negative ones (which start with "-").
-    // 
+    /**
+     * Return a human-readable debug-type string to full precision
+     * (probably not a format to show to a user directly).  If align
+     * is true, prepend " " to the start of positive values so that
+     * they line up with negative ones (which start with "-").
+     */ 
     std::string toString(bool align = false) const;
 
-    // Return a user-readable string to the nearest millisecond
-    // in a form like HH:MM:SS.mmm
-    //
+    /**
+     * Convert a string as obtained from toString back to a RealTime
+     * object.
+     */
+    static RealTime fromString(std::string);
+
+    /**
+     * Return a user-readable string to the nearest millisecond, in a
+     * form like HH:MM:SS.mmm
+     */
     std::string toText(bool fixedDp = false) const;
 
-    // Return a user-readable string to the nearest second in a form
-    // like "6s" (for less than a minute) or "2:21" (for more).
-    //
+    /**
+     * Return a user-readable string to the nearest second, in a form
+     * like "6s" (for less than a minute) or "2:21" (for more).
+     */
     std::string toSecText() const;
 
-    // Convenience functions for handling sample frames
-    //
+    /**
+     * Convert a RealTime into a sample frame at the given sample rate.
+     */
     static long realTime2Frame(const RealTime &r, unsigned int sampleRate);
+
+    /**
+     * Convert a sample frame at the given sample rate into a RealTime.
+     */
     static RealTime frame2RealTime(long frame, unsigned int sampleRate);
 
     static const RealTime zeroTime;
--- a/base/Window.h	Fri Nov 30 17:31:09 2007 +0000
+++ b/base/Window.h	Fri Dec 07 16:47:31 2007 +0000
@@ -18,6 +18,7 @@
 
 #include <cmath>
 #include <iostream>
+#include <string>
 #include <map>
 
 enum WindowType {
@@ -61,6 +62,12 @@
     WindowType getType() const { return m_type; }
     size_t getSize() const { return m_size; }
 
+    // The names used by these functions are un-translated, for use in
+    // e.g. XML I/O.  Use Preferences::getPropertyValueLabel if you
+    // want translated names for use in the user interface.
+    static std::string getNameForType(WindowType type);
+    static WindowType getTypeForName(std::string name);
+
 protected:
     WindowType m_type;
     size_t m_size;
@@ -159,4 +166,46 @@
     }
 }
 
+template <typename T>
+std::string
+Window<T>::getNameForType(WindowType type)
+{
+    switch (type) {
+    case RectangularWindow:    return "rectangular";
+    case BartlettWindow:       return "bartlett";
+    case HammingWindow:        return "hamming";
+    case HanningWindow:        return "hanning";
+    case BlackmanWindow:       return "blackman";
+    case GaussianWindow:       return "gaussian";
+    case ParzenWindow:         return "parzen";
+    case NuttallWindow:        return "nuttall";
+    case BlackmanHarrisWindow: return "blackman-harris";
+    }
+
+    std::cerr << "WARNING: Window::getNameForType: unknown type "
+              << type << std::endl;
+
+    return "unknown";
+}
+
+template <typename T>
+WindowType
+Window<T>::getTypeForName(std::string name)
+{
+    if (name == "rectangular")     return RectangularWindow;
+    if (name == "bartlett")        return BartlettWindow;
+    if (name == "hamming")         return HammingWindow;
+    if (name == "hanning")         return HanningWindow;
+    if (name == "blackman")        return BlackmanWindow;
+    if (name == "gaussian")        return GaussianWindow;
+    if (name == "parzen")          return ParzenWindow;
+    if (name == "nuttall")         return NuttallWindow;
+    if (name == "blackman-harris") return BlackmanHarrisWindow;
+
+    std::cerr << "WARNING: Window::getTypeForName: unknown name \""
+              << name << "\", defaulting to \"hanning\"" << std::endl;
+
+    return HanningWindow;
+}
+
 #endif
--- a/plugin/plugin.pro	Fri Nov 30 17:31:09 2007 +0000
+++ b/plugin/plugin.pro	Fri Dec 07 16:47:31 2007 +0000
@@ -36,7 +36,6 @@
            api/alsa/seq_midi_event.h \
            api/alsa/sound/asequencer.h \
            transform/FeatureExtractionModelTransformer.h \
-           transform/PluginTransformer.h \
            transform/RealTimeEffectModelTransformer.h \
            transform/Transform.h \
            transform/TransformDescription.h \
@@ -55,7 +54,6 @@
            api/dssi_alsa_compat.c \
            plugins/SamplePlayer.cpp \
            transform/FeatureExtractionModelTransformer.cpp \
-           transform/PluginTransformer.cpp \
            transform/RealTimeEffectModelTransformer.cpp \
            transform/Transform.cpp \
            transform/TransformFactory.cpp \
--- a/plugin/transform/FeatureExtractionModelTransformer.cpp	Fri Nov 30 17:31:09 2007 +0000
+++ b/plugin/transform/FeatureExtractionModelTransformer.cpp	Fri Dec 07 16:47:31 2007 +0000
@@ -29,21 +29,22 @@
 #include "data/model/FFTModel.h"
 #include "data/model/WaveFileModel.h"
 
+#include "TransformFactory.h"
+
 #include <QMessageBox>
 
 #include <iostream>
 
-FeatureExtractionModelTransformer::FeatureExtractionModelTransformer(Model *inputModel,
-								   QString pluginId,
-                                                                   const ExecutionContext &context,
-                                                                   QString configurationXml,
-								   QString outputName) :
-    PluginTransformer(inputModel, context),
+FeatureExtractionModelTransformer::FeatureExtractionModelTransformer(Input in,
+                                                                     const Transform &transform) :
+    ModelTransformer(in, transform),
     m_plugin(0),
     m_descriptor(0),
     m_outputFeatureNo(0)
 {
-//    std::cerr << "FeatureExtractionModelTransformer::FeatureExtractionModelTransformer: plugin " << pluginId.toStdString() << ", outputName " << outputName.toStdString() << std::endl;
+//    std::cerr << "FeatureExtractionModelTransformer::FeatureExtractionModelTransformer: plugin " << pluginId.toStdString() << ", outputName " << m_transform.getOutput().toStdString() << std::endl;
+
+    QString pluginId = transform.getPluginIdentifier();
 
     FeatureExtractionPluginFactory *factory =
 	FeatureExtractionPluginFactory::instanceFor(pluginId);
@@ -54,22 +55,24 @@
 	return;
     }
 
-    m_plugin = factory->instantiatePlugin(pluginId, m_input->getSampleRate());
+    DenseTimeValueModel *input = getConformingInput();
+    if (!input) {
+        std::cerr << "FeatureExtractionModelTransformer: Input model not conformable" << std::endl;
+        return;
+    }
 
+    m_plugin = factory->instantiatePlugin(pluginId, input->getSampleRate());
     if (!m_plugin) {
 	std::cerr << "FeatureExtractionModelTransformer: Failed to instantiate plugin \""
 		  << pluginId.toStdString() << "\"" << std::endl;
 	return;
     }
 
-    m_context.makeConsistentWithPlugin(m_plugin);
+    TransformFactory::getInstance()->makeContextConsistentWithPlugin
+        (m_transform, m_plugin);
 
-    if (configurationXml != "") {
-        PluginXml(m_plugin).setParametersFromXml(configurationXml);
-    }
-
-    DenseTimeValueModel *input = getInput();
-    if (!input) return;
+    TransformFactory::getInstance()->setPluginParameters
+        (m_transform, m_plugin);
 
     size_t channelCount = input->getChannelCount();
     if (m_plugin->getMaxChannelCount() < channelCount) {
@@ -85,14 +88,14 @@
     }
 
     std::cerr << "Initialising feature extraction plugin with channels = "
-              << channelCount << ", step = " << m_context.stepSize
-              << ", block = " << m_context.blockSize << std::endl;
+              << channelCount << ", step = " << m_transform.getStepSize()
+              << ", block = " << m_transform.getBlockSize() << std::endl;
 
     if (!m_plugin->initialise(channelCount,
-                              m_context.stepSize,
-                              m_context.blockSize)) {
+                              m_transform.getStepSize(),
+                              m_transform.getBlockSize())) {
         std::cerr << "FeatureExtractionModelTransformer: Plugin "
-                  << m_plugin->getIdentifier() << " failed to initialise!" << std::endl;
+                  << pluginId.toStdString() << " failed to initialise!" << std::endl;
         return;
     }
 
@@ -105,7 +108,8 @@
     }
     
     for (size_t i = 0; i < outputs.size(); ++i) {
-	if (outputName == "" || outputs[i].identifier == outputName.toStdString()) {
+	if (m_transform.getOutput() == "" ||
+            outputs[i].identifier == m_transform.getOutput().toStdString()) {
 	    m_outputFeatureNo = i;
 	    m_descriptor = new Vamp::Plugin::OutputDescriptor
 		(outputs[i]);
@@ -116,7 +120,7 @@
     if (!m_descriptor) {
 	std::cerr << "FeatureExtractionModelTransformer: Plugin \""
 		  << pluginId.toStdString() << "\" has no output named \""
-		  << outputName.toStdString() << "\"" << std::endl;
+		  << m_transform.getOutput().toStdString() << "\"" << std::endl;
 	return;
     }
 
@@ -140,7 +144,7 @@
         haveExtents = true;
     }
 
-    size_t modelRate = m_input->getSampleRate();
+    size_t modelRate = input->getSampleRate();
     size_t modelResolution = 1;
     
     switch (m_descriptor->sampleType) {
@@ -152,7 +156,7 @@
 	break;
 
     case Vamp::Plugin::OutputDescriptor::OneSamplePerStep:
-	modelResolution = m_context.stepSize;
+	modelResolution = m_transform.getStepSize();
 	break;
 
     case Vamp::Plugin::OutputDescriptor::FixedSampleRate:
@@ -219,7 +223,7 @@
         m_output = model;
     }
 
-    if (m_output) m_output->setSourceModel(m_input);
+    if (m_output) m_output->setSourceModel(input);
 }
 
 FeatureExtractionModelTransformer::~FeatureExtractionModelTransformer()
@@ -230,12 +234,12 @@
 }
 
 DenseTimeValueModel *
-FeatureExtractionModelTransformer::getInput()
+FeatureExtractionModelTransformer::getConformingInput()
 {
     DenseTimeValueModel *dtvm =
 	dynamic_cast<DenseTimeValueModel *>(getInputModel());
     if (!dtvm) {
-	std::cerr << "FeatureExtractionModelTransformer::getInput: WARNING: Input model is not conformable to DenseTimeValueModel" << std::endl;
+	std::cerr << "FeatureExtractionModelTransformer::getConformingInput: WARNING: Input model is not conformable to DenseTimeValueModel" << std::endl;
     }
     return dtvm;
 }
@@ -243,7 +247,7 @@
 void
 FeatureExtractionModelTransformer::run()
 {
-    DenseTimeValueModel *input = getInput();
+    DenseTimeValueModel *input = getConformingInput();
     if (!input) return;
 
     if (!m_output) return;
@@ -260,7 +264,7 @@
         sleep(1);
     }
 
-    size_t sampleRate = m_input->getSampleRate();
+    size_t sampleRate = input->getSampleRate();
 
     size_t channelCount = input->getChannelCount();
     if (m_plugin->getMaxChannelCount() < channelCount) {
@@ -269,9 +273,12 @@
 
     float **buffers = new float*[channelCount];
     for (size_t ch = 0; ch < channelCount; ++ch) {
-	buffers[ch] = new float[m_context.blockSize + 2];
+	buffers[ch] = new float[m_transform.getBlockSize() + 2];
     }
 
+    size_t stepSize = m_transform.getStepSize();
+    size_t blockSize = m_transform.getBlockSize();
+
     bool frequencyDomain = (m_plugin->getInputDomain() ==
                             Vamp::Plugin::FrequencyDomain);
     std::vector<FFTModel *> fftModels;
@@ -279,12 +286,12 @@
     if (frequencyDomain) {
         for (size_t ch = 0; ch < channelCount; ++ch) {
             FFTModel *model = new FFTModel
-                                  (getInput(),
-                                   channelCount == 1 ? m_context.channel : ch,
-                                   m_context.windowType,
-                                   m_context.blockSize,
-                                   m_context.stepSize,
-                                   m_context.blockSize,
+                                  (getConformingInput(),
+                                   channelCount == 1 ? m_input.getChannel() : ch,
+                                   m_transform.getWindowType(),
+                                   blockSize,
+                                   stepSize,
+                                   blockSize,
                                    false,
                                    StorageAdviser::PrecisionCritical);
             if (!model->isOK()) {
@@ -301,11 +308,17 @@
         }
     }
 
-    long startFrame = m_input->getStartFrame();
-    long   endFrame = m_input->getEndFrame();
+    long startFrame = m_input.getModel()->getStartFrame();
+    long   endFrame = m_input.getModel()->getEndFrame();
 
-    long contextStart = m_context.startFrame;
-    long contextDuration = m_context.duration;
+    RealTime contextStartRT = m_transform.getStartTime();
+    RealTime contextDurationRT = m_transform.getDuration();
+
+    long contextStart =
+        RealTime::realTime2Frame(contextStartRT, sampleRate);
+
+    long contextDuration =
+        RealTime::realTime2Frame(contextDurationRT, sampleRate);
 
     if (contextStart == 0 || contextStart < startFrame) {
         contextStart = startFrame;
@@ -327,7 +340,7 @@
     while (!m_abandoned) {
 
         if (frequencyDomain) {
-            if (blockFrame - int(m_context.blockSize)/2 >
+            if (blockFrame - int(blockSize)/2 >
                 contextStart + contextDuration) break;
         } else {
             if (blockFrame >= 
@@ -336,24 +349,24 @@
 
 //	std::cerr << "FeatureExtractionModelTransformer::run: blockFrame "
 //		  << blockFrame << ", endFrame " << endFrame << ", blockSize "
-//                  << m_context.blockSize << std::endl;
+//                  << blockSize << std::endl;
 
 	long completion =
-	    (((blockFrame - contextStart) / m_context.stepSize) * 99) /
-	    (contextDuration / m_context.stepSize);
+	    (((blockFrame - contextStart) / stepSize) * 99) /
+	    (contextDuration / stepSize);
 
-	// channelCount is either m_input->channelCount or 1
+	// channelCount is either m_input.getModel()->channelCount or 1
 
         for (size_t ch = 0; ch < channelCount; ++ch) {
             if (frequencyDomain) {
-                int column = (blockFrame - startFrame) / m_context.stepSize;
-                for (size_t i = 0; i <= m_context.blockSize/2; ++i) {
+                int column = (blockFrame - startFrame) / stepSize;
+                for (size_t i = 0; i <= blockSize/2; ++i) {
                     fftModels[ch]->getValuesAt
                         (column, i, buffers[ch][i*2], buffers[ch][i*2+1]);
                 }
             } else {
                 getFrames(ch, channelCount, 
-                          blockFrame, m_context.blockSize, buffers[ch]);
+                          blockFrame, blockSize, buffers[ch]);
             }                
         }
 
@@ -371,7 +384,7 @@
 	    prevCompletion = completion;
 	}
 
-	blockFrame += m_context.stepSize;
+	blockFrame += stepSize;
     }
 
     if (m_abandoned) return;
@@ -410,8 +423,11 @@
         startFrame = 0;
     }
 
-    long got = getInput()->getData
-        ((channelCount == 1 ? m_context.channel : channel),
+    DenseTimeValueModel *input = getConformingInput();
+    if (!input) return;
+
+    long got = input->getData
+        ((channelCount == 1 ? m_input.getChannel() : channel),
          startFrame, size, buffer + offset);
 
     while (got < size) {
@@ -419,10 +435,10 @@
         ++got;
     }
 
-    if (m_context.channel == -1 && channelCount == 1 &&
-        getInput()->getChannelCount() > 1) {
+    if (m_input.getChannel() == -1 && channelCount == 1 &&
+        input->getChannelCount() > 1) {
         // use mean instead of sum, as plugin input
-        int cc = getInput()->getChannelCount();
+        int cc = input->getChannelCount();
         for (long i = 0; i < size; ++i) {
             buffer[i] /= cc;
         }
@@ -433,7 +449,7 @@
 FeatureExtractionModelTransformer::addFeature(size_t blockFrame,
 					     const Vamp::Plugin::Feature &feature)
 {
-    size_t inputRate = m_input->getSampleRate();
+    size_t inputRate = m_input.getModel()->getSampleRate();
 
 //    std::cerr << "FeatureExtractionModelTransformer::addFeature("
 //	      << blockFrame << ")" << std::endl;
@@ -472,8 +488,10 @@
 	
     if (binCount == 0) {
 
-	SparseOneDimensionalModel *model = getOutput<SparseOneDimensionalModel>();
+	SparseOneDimensionalModel *model =
+            getConformingOutput<SparseOneDimensionalModel>();
 	if (!model) return;
+
 	model->addPoint(SparseOneDimensionalModel::Point(frame, feature.label.c_str()));
 	
     } else if (binCount == 1) {
@@ -481,8 +499,10 @@
 	float value = 0.0;
 	if (feature.values.size() > 0) value = feature.values[0];
 
-	SparseTimeValueModel *model = getOutput<SparseTimeValueModel>();
+	SparseTimeValueModel *model =
+            getConformingOutput<SparseTimeValueModel>();
 	if (!model) return;
+
 	model->addPoint(SparseTimeValueModel::Point(frame, value, feature.label.c_str()));
 //        std::cerr << "SparseTimeValueModel::addPoint(" << frame << ", " << value << "), " << feature.label.c_str() << std::endl;
 
@@ -500,7 +520,7 @@
         if (velocity < 0) velocity = 127;
         if (velocity > 127) velocity = 127;
 
-        NoteModel *model = getOutput<NoteModel>();
+        NoteModel *model = getConformingOutput<NoteModel>();
         if (!model) return;
 
         model->addPoint(NoteModel::Point(frame, pitch,
@@ -513,7 +533,7 @@
 	DenseThreeDimensionalModel::Column values = feature.values;
 	
 	EditableDenseThreeDimensionalModel *model =
-            getOutput<EditableDenseThreeDimensionalModel>();
+            getConformingOutput<EditableDenseThreeDimensionalModel>();
 	if (!model) return;
 
 	model->setColumn(frame / model->getResolution(), values);
@@ -533,29 +553,32 @@
 
     if (binCount == 0) {
 
-	SparseOneDimensionalModel *model = getOutput<SparseOneDimensionalModel>();
+	SparseOneDimensionalModel *model =
+            getConformingOutput<SparseOneDimensionalModel>();
 	if (!model) return;
-	model->setCompletion(completion, m_context.updates);
+	model->setCompletion(completion, true); //!!!m_context.updates);
 
     } else if (binCount == 1) {
 
-	SparseTimeValueModel *model = getOutput<SparseTimeValueModel>();
+	SparseTimeValueModel *model =
+            getConformingOutput<SparseTimeValueModel>();
 	if (!model) return;
-	model->setCompletion(completion, m_context.updates);
+	model->setCompletion(completion, true); //!!!m_context.updates);
 
     } else if (m_descriptor->sampleType ==
 	       Vamp::Plugin::OutputDescriptor::VariableSampleRate) {
 
-	NoteModel *model = getOutput<NoteModel>();
+	NoteModel *model =
+            getConformingOutput<NoteModel>();
 	if (!model) return;
-	model->setCompletion(completion, m_context.updates);
+	model->setCompletion(completion, true); //!!!m_context.updates);
 
     } else {
 
 	EditableDenseThreeDimensionalModel *model =
-            getOutput<EditableDenseThreeDimensionalModel>();
+            getConformingOutput<EditableDenseThreeDimensionalModel>();
 	if (!model) return;
-	model->setCompletion(completion, m_context.updates);
+	model->setCompletion(completion, true); //!!!m_context.updates);
     }
 }
 
--- a/plugin/transform/FeatureExtractionModelTransformer.h	Fri Nov 30 17:31:09 2007 +0000
+++ b/plugin/transform/FeatureExtractionModelTransformer.h	Fri Dec 07 16:47:31 2007 +0000
@@ -16,20 +16,21 @@
 #ifndef _FEATURE_EXTRACTION_PLUGIN_TRANSFORMER_H_
 #define _FEATURE_EXTRACTION_PLUGIN_TRANSFORMER_H_
 
-#include "PluginTransformer.h"
+#include "ModelTransformer.h"
+
+#include <vamp-sdk/Plugin.h>
+
+#include <iostream>
 
 class DenseTimeValueModel;
 
-class FeatureExtractionModelTransformer : public PluginTransformer
+class FeatureExtractionModelTransformer : public ModelTransformer
 {
     Q_OBJECT
 
 public:
-    FeatureExtractionModelTransformer(Model *inputModel,
-                                      QString plugin,
-                                      const ExecutionContext &context,
-                                      QString configurationXml = "",
-                                      QString outputName = "");
+    FeatureExtractionModelTransformer(Input input,
+                                      const Transform &transform);
     virtual ~FeatureExtractionModelTransformer();
 
 protected:
@@ -48,8 +49,8 @@
                    long startFrame, long size, float *buffer);
 
     // just casts
-    DenseTimeValueModel *getInput();
-    template <typename ModelClass> ModelClass *getOutput() {
+    DenseTimeValueModel *getConformingInput();
+    template <typename ModelClass> ModelClass *getConformingOutput() {
 	ModelClass *mc = dynamic_cast<ModelClass *>(m_output);
 	if (!mc) {
 	    std::cerr << "FeatureExtractionModelTransformer::getOutput: Output model not conformable" << std::endl;
--- a/plugin/transform/ModelTransformer.cpp	Fri Nov 30 17:31:09 2007 +0000
+++ b/plugin/transform/ModelTransformer.cpp	Fri Dec 07 16:47:31 2007 +0000
@@ -15,8 +15,9 @@
 
 #include "ModelTransformer.h"
 
-ModelTransformer::ModelTransformer(Model *m) :
-    m_input(m),
+ModelTransformer::ModelTransformer(Input input, const Transform &transform) :
+    m_transform(transform),
+    m_input(input),
     m_output(0),
     m_detached(false),
     m_abandoned(false)
--- a/plugin/transform/ModelTransformer.h	Fri Nov 30 17:31:09 2007 +0000
+++ b/plugin/transform/ModelTransformer.h	Fri Dec 07 16:47:31 2007 +0000
@@ -20,6 +20,8 @@
 
 #include "data/model/Model.h"
 
+#include "Transform.h"
+
 /**
  * A ModelTransformer turns one data model into another.
  *
@@ -38,19 +40,38 @@
 public:
     virtual ~ModelTransformer();
 
+    class Input {
+    public:
+        Input(Model *m) : m_model(m), m_channel(-1) { }
+        Input(Model *m, int c) : m_model(m), m_channel(c) { }
+
+        Model *getModel() const { return m_model; }
+        void setModel(Model *m) { m_model = m; }
+
+        int getChannel() const { return m_channel; }
+        void setChannel(int c) { m_channel = c; }
+
+    protected:
+        Model *m_model;
+        int m_channel;
+    };
+
     // Just a hint to the processing thread that it should give up.
     // Caller should still wait() and/or delete the transform before
     // assuming its input and output models are no longer required.
     void abandon() { m_abandoned = true; }
 
-    Model *getInputModel()  { return m_input; }
+    Model *getInputModel()  { return m_input.getModel(); }
+    int getInputChannel() { return m_input.getChannel(); }
+
     Model *getOutputModel() { return m_output; }
     Model *detachOutputModel() { m_detached = true; return m_output; }
 
 protected:
-    ModelTransformer(Model *m);
+    ModelTransformer(Input input, const Transform &transform);
 
-    Model *m_input; // I don't own this
+    Transform m_transform;
+    Input m_input; // I don't own the model in this
     Model *m_output; // I own this, unless...
     bool m_detached; // ... this is true.
     bool m_abandoned;
--- a/plugin/transform/ModelTransformerFactory.cpp	Fri Nov 30 17:31:09 2007 +0000
+++ b/plugin/transform/ModelTransformerFactory.cpp	Fri Dec 07 16:47:31 2007 +0000
@@ -51,7 +51,8 @@
 }
 
 bool
-ModelTransformerFactory::getChannelRange(TransformId identifier, Vamp::PluginBase *plugin,
+ModelTransformerFactory::getChannelRange(TransformId identifier,
+                                         Vamp::PluginBase *plugin,
                                          int &minChannels, int &maxChannels)
 {
     Vamp::Plugin *vp = 0;
@@ -66,17 +67,17 @@
     }
 }
 
-Model *
-ModelTransformerFactory::getConfigurationForTransformer(TransformId identifier,
-                                                        const std::vector<Model *> &candidateInputModels,
-                                                        Model *defaultInputModel,
-                                                        PluginTransformer::ExecutionContext &context,
-                                                        QString &configurationXml,
-                                                        AudioCallbackPlaySource *source,
-                                                        size_t startFrame,
-                                                        size_t duration)
+ModelTransformer::Input
+ModelTransformerFactory::getConfigurationForTransform(Transform &transform,
+                                                      const std::vector<Model *> &candidateInputModels,
+                                                      Model *defaultInputModel,
+                                                      AudioCallbackPlaySource *source,
+                                                      size_t startFrame,
+                                                      size_t duration)
 {
-    if (candidateInputModels.empty()) return 0;
+    ModelTransformer::Input input(0);
+
+    if (candidateInputModels.empty()) return input;
 
     //!!! This will need revision -- we'll have to have a callback
     //from the dialog for when the candidate input model is changed,
@@ -99,15 +100,15 @@
         }
     }
 
-    QString id = identifier.section(':', 0, 2);
-    QString output = identifier.section(':', 3);
+    QString id = transform.getPluginIdentifier();
+    QString output = transform.getOutput();
     QString outputLabel = "";
     QString outputDescription = "";
     
     bool ok = false;
-    configurationXml = m_lastConfigurations[identifier];
+    QString configurationXml = m_lastConfigurations[transform.getIdentifier()];
 
-//    std::cerr << "last configuration: " << configurationXml.toStdString() << std::endl;
+    std::cerr << "last configuration: " << configurationXml.toStdString() << std::endl;
 
     Vamp::PluginBase *plugin = 0;
 
@@ -117,7 +118,7 @@
 
     if (FeatureExtractionPluginFactory::instanceFor(id)) {
 
-        std::cerr << "getConfigurationForTransformer: instantiating Vamp plugin" << std::endl;
+        std::cerr << "getConfigurationForTransform: instantiating Vamp plugin" << std::endl;
 
         Vamp::Plugin *vp =
             FeatureExtractionPluginFactory::instanceFor(id)->instantiatePlugin
@@ -184,11 +185,18 @@
 
     if (plugin) {
 
-        context = PluginTransformer::ExecutionContext(context.channel, plugin);
+        // Ensure block size etc are valid
+        TransformFactory::getInstance()->
+            makeContextConsistentWithPlugin(transform, plugin);
 
-        if (configurationXml != "") {
-            PluginXml(plugin).setParametersFromXml(configurationXml);
-        }
+        // Prepare the plugin with any existing parameters already
+        // found in the transform
+        TransformFactory::getInstance()->
+            setPluginParameters(transform, plugin);
+        
+        // For this interactive usage, we want to override those with
+        // whatever the user chose last time around
+        PluginXml(plugin).setParametersFromXml(configurationXml);
 
         int sourceChannels = 1;
         if (dynamic_cast<DenseTimeValueModel *>(inputModel)) {
@@ -197,7 +205,8 @@
         }
 
         int minChannels = 1, maxChannels = sourceChannels;
-        getChannelRange(identifier, plugin, minChannels, maxChannels);
+        getChannelRange(transform.getIdentifier(), plugin,
+                        minChannels, maxChannels);
 
         int targetChannels = sourceChannels;
         if (!effect) {
@@ -205,7 +214,7 @@
             if (sourceChannels > maxChannels) targetChannels = maxChannels;
         }
 
-        int defaultChannel = context.channel;
+        int defaultChannel = -1; //!!! no longer saved! [was context.channel]
 
         PluginParameterDialog *dialog = new PluginParameterDialog(plugin);
 
@@ -242,22 +251,42 @@
         } else {
             std::cerr << "Selected input empty: \"" << selectedInput.toStdString() << "\"" << std::endl;
         }
+        
+        // Write parameters back to transform object
+        TransformFactory::getInstance()->
+            setParametersFromPlugin(transform, plugin);
 
-        configurationXml = PluginXml(plugin).toXmlString();
-        context.channel = dialog->getChannel();
+        input.setChannel(dialog->getChannel());
         
+        //!!! The dialog ought to be taking & returning transform
+        //objects and input objects and stuff rather than passing
+        //around all this misc stuff, but that's for tomorrow
+        //(whenever that may be)
+
         if (startFrame != 0 || duration != 0) {
             if (dialog->getSelectionOnly()) {
-                context.startFrame = startFrame;
-                context.duration = duration;
+                transform.setStartTime(RealTime::frame2RealTime
+                                       (startFrame, inputModel->getSampleRate()));
+                transform.setDuration(RealTime::frame2RealTime
+                                      (duration, inputModel->getSampleRate()));
             }
         }
 
-        dialog->getProcessingParameters(context.stepSize,
-                                        context.blockSize,
-                                        context.windowType);
+        size_t stepSize = 0, blockSize = 0;
+        WindowType windowType = HanningWindow;
 
-        context.makeConsistentWithPlugin(plugin);
+        dialog->getProcessingParameters(stepSize,
+                                        blockSize,
+                                        windowType);
+
+        transform.setStepSize(stepSize);
+        transform.setBlockSize(blockSize);
+        transform.setWindowType(windowType);
+
+        TransformFactory::getInstance()->
+            makeContextConsistentWithPlugin(transform, plugin);
+
+        configurationXml = PluginXml(plugin).toXmlString();
 
         delete dialog;
 
@@ -268,11 +297,14 @@
         }
     }
 
-    if (ok) m_lastConfigurations[identifier] = configurationXml;
+    if (ok) {
+        m_lastConfigurations[transform.getIdentifier()] = configurationXml;
+        input.setModel(inputModel);
+    }
 
-    return ok ? inputModel : 0;
+    return input;
 }
-
+/*!!!
 PluginTransformer::ExecutionContext
 ModelTransformerFactory::getDefaultContextForTransformer(TransformId identifier,
                                                 Model *inputModel)
@@ -295,43 +327,40 @@
 
     return context;
 }
-
+*/
 ModelTransformer *
-ModelTransformerFactory::createTransformer(TransformId identifier, Model *inputModel,
-                                  const PluginTransformer::ExecutionContext &context,
-                                  QString configurationXml)
+ModelTransformerFactory::createTransformer(const Transform &transform,
+                                           const ModelTransformer::Input &input)
 {
     ModelTransformer *transformer = 0;
 
-    QString id = identifier.section(':', 0, 2);
-    QString output = identifier.section(':', 3);
+    QString id = transform.getPluginIdentifier();
 
     if (FeatureExtractionPluginFactory::instanceFor(id)) {
-        transformer = new FeatureExtractionModelTransformer
-            (inputModel, id, context, configurationXml, output);
+
+        transformer =
+            new FeatureExtractionModelTransformer(input, transform);
+
     } else if (RealTimePluginFactory::instanceFor(id)) {
-        transformer = new RealTimeEffectModelTransformer
-            (inputModel, id, context, configurationXml,
-             TransformFactory::getInstance()->getTransformUnits(identifier),
-             output == "A" ? -1 : output.toInt());
+
+        transformer =
+            new RealTimeEffectModelTransformer(input, transform);
+
     } else {
         std::cerr << "ModelTransformerFactory::createTransformer: Unknown transform \""
-                  << identifier.toStdString() << "\"" << std::endl;
+                  << transform.getIdentifier().toStdString() << "\"" << std::endl;
         return transformer;
     }
 
-    if (transformer) transformer->setObjectName(identifier);
+    if (transformer) transformer->setObjectName(transform.getIdentifier());
     return transformer;
 }
 
 Model *
-ModelTransformerFactory::transform(TransformId identifier, Model *inputModel,
-                            const PluginTransformer::ExecutionContext &context,
-                            QString configurationXml)
+ModelTransformerFactory::transform(const Transform &transform,
+                                   const ModelTransformer::Input &input)
 {
-    ModelTransformer *t = createTransformer(identifier, inputModel, context,
-                                            configurationXml);
-
+    ModelTransformer *t = createTransformer(transform, input);
     if (!t) return 0;
 
     connect(t, SIGNAL(finished()), this, SLOT(transformerFinished()));
@@ -342,10 +371,10 @@
     Model *model = t->detachOutputModel();
 
     if (model) {
-        QString imn = inputModel->objectName();
+        QString imn = input.getModel()->objectName();
         QString trn =
             TransformFactory::getInstance()->getTransformFriendlyName
-            (identifier);
+            (transform.getIdentifier());
         if (imn != "") {
             if (trn != "") {
                 model->setObjectName(tr("%1: %2").arg(imn).arg(trn));
--- a/plugin/transform/ModelTransformerFactory.h	Fri Nov 30 17:31:09 2007 +0000
+++ b/plugin/transform/ModelTransformerFactory.h	Fri Dec 07 16:47:31 2007 +0000
@@ -21,8 +21,6 @@
 
 #include "ModelTransformer.h"
 
-#include "PluginTransformer.h"
-
 #include <map>
 #include <set>
 
@@ -40,28 +38,29 @@
     static ModelTransformerFactory *getInstance();
 
     /**
-     * Get a configuration XML string for the given transform (by
-     * asking the user, most likely).  Returns the selected input
-     * model if the transform is acceptable, 0 if the operation should
-     * be cancelled.  Audio callback play source may be used to
-     * audition effects plugins, if provided.
+     * Fill out the configuration for the given transform (by asking
+     * the user, most likely).  Returns the selected input model and
+     * channel if the transform is acceptable, or an input with a null
+     * model if the operation should be cancelled.  Audio callback
+     * play source may be used to audition effects plugins, if
+     * provided.
      */
-    Model *getConfigurationForTransformer(TransformId identifier,
-                                          const std::vector<Model *> &candidateInputModels,
-                                          Model *defaultInputModel,
-                                          PluginTransformer::ExecutionContext &context,
-                                          QString &configurationXml,
-                                          AudioCallbackPlaySource *source = 0,
-                                          size_t startFrame = 0,
-                                          size_t duration = 0);
-
+    ModelTransformer::Input
+    getConfigurationForTransform(Transform &transform,
+                                 const std::vector<Model *> &candidateInputModels,
+                                 Model *defaultInputModel,
+                                 AudioCallbackPlaySource *source = 0,
+                                 size_t startFrame = 0,
+                                 size_t duration = 0);
+    
     /**
      * Get the default execution context for the given transform
      * and input model (if known).
      */
+/*!!!
     PluginTransformer::ExecutionContext getDefaultContextForTransformer(TransformId identifier,
                                                                         Model *inputModel = 0);
-
+*/
     /**
      * Return the output model resulting from applying the named
      * transform to the given input model.  The transform may still be
@@ -75,9 +74,8 @@
      * The returned model is owned by the caller and must be deleted
      * when no longer needed.
      */
-    Model *transform(TransformId identifier, Model *inputModel,
-                     const PluginTransformer::ExecutionContext &context,
-                     QString configurationXml = "");
+    Model *transform(const Transform &transform,
+                     const ModelTransformer::Input &input);
 
 protected slots:
     void transformerFinished();
@@ -85,9 +83,8 @@
     void modelAboutToBeDeleted(Model *);
 
 protected:
-    ModelTransformer *createTransformer(TransformId identifier, Model *inputModel,
-                                        const PluginTransformer::ExecutionContext &context,
-                                        QString configurationXml);
+    ModelTransformer *createTransformer(const Transform &transform,
+                                        const ModelTransformer::Input &input);
 
     typedef std::map<TransformId, QString> TransformerConfigurationMap;
     TransformerConfigurationMap m_lastConfigurations;
--- a/plugin/transform/PluginTransformer.cpp	Fri Nov 30 17:31:09 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,127 +0,0 @@
-/* -*- 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 QMUL.
-   
-    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 "PluginTransformer.h"
-
-#include "vamp-sdk/PluginHostAdapter.h"
-#include "vamp-sdk/hostext/PluginWrapper.h"
-
-PluginTransformer::PluginTransformer(Model *inputModel,
-				 const ExecutionContext &context) :
-    ModelTransformer(inputModel),
-    m_context(context)
-{
-}
-
-PluginTransformer::ExecutionContext::ExecutionContext(int _c, size_t _bs) :
-    channel(_c),
-    domain(Vamp::Plugin::TimeDomain),
-    stepSize(_bs ? _bs : 1024),
-    blockSize(_bs ? _bs : 1024),
-    windowType(HanningWindow),
-    startFrame(0),
-    duration(0),
-    sampleRate(0),
-    updates(true)
-{
-}
-
-PluginTransformer::ExecutionContext::ExecutionContext(int _c, size_t _ss,
-                                                    size_t _bs, WindowType _wt) :
-    channel(_c),
-    domain(Vamp::Plugin::FrequencyDomain),
-    stepSize(_ss ? _ss : (_bs ? _bs / 2 : 512)),
-    blockSize(_bs ? _bs : 1024),
-    windowType(_wt),
-    startFrame(0),
-    duration(0),
-    sampleRate(0),
-    updates(true)
-{
-}
-
-PluginTransformer::ExecutionContext::ExecutionContext(int _c,
-                                                    const Vamp::PluginBase *_plugin) :
-    channel(_c),
-    domain(Vamp::Plugin::TimeDomain),
-    stepSize(0),
-    blockSize(0),
-    windowType(HanningWindow),
-    startFrame(0),
-    duration(0),
-    sampleRate(0)
-{
-    makeConsistentWithPlugin(_plugin);
-}
-
-bool
-PluginTransformer::ExecutionContext::operator==(const ExecutionContext &c)
-{
-    return (c.channel == channel &&
-            c.domain == domain &&
-            c.stepSize == stepSize &&
-            c.blockSize == blockSize &&
-            c.windowType == windowType &&
-            c.startFrame == startFrame &&
-            c.duration == duration &&
-            c.sampleRate == sampleRate);
-}
-
-void
-PluginTransformer::ExecutionContext::makeConsistentWithPlugin(const Vamp::PluginBase *_plugin)
-{
-    const Vamp::Plugin *vp = dynamic_cast<const Vamp::Plugin *>(_plugin);
-    if (!vp) {
-//        std::cerr << "makeConsistentWithPlugin: not a Vamp::Plugin" << std::endl;
-        vp = dynamic_cast<const Vamp::PluginHostAdapter *>(_plugin); //!!! why?
-}
-    if (!vp) {
-//        std::cerr << "makeConsistentWithPlugin: not a Vamp::PluginHostAdapter" << std::endl;
-        vp = dynamic_cast<const Vamp::HostExt::PluginWrapper *>(_plugin); //!!! no, I mean really why?
-    }
-    if (!vp) {
-//        std::cerr << "makeConsistentWithPlugin: not a Vamp::HostExt::PluginWrapper" << std::endl;
-    }
-
-//    std::cerr << "makeConsistentWithPlugin: stepSize = " << stepSize << ", blockSize = " << blockSize << std::endl;
-
-    if (!vp) {
-        domain = Vamp::Plugin::TimeDomain;
-//        std::cerr << "time domain RT plugin" << std::endl;
-        if (!stepSize) {
-            if (!blockSize) blockSize = 1024;
-            stepSize = blockSize;
-        } else {
-            if (!blockSize) blockSize = stepSize;
-        }
-    } else {
-        domain = vp->getInputDomain();
-//        std::cerr << "feature extraction plugin" << std::endl;
-        if (!stepSize) stepSize = vp->getPreferredStepSize();
-        if (!blockSize) blockSize = vp->getPreferredBlockSize();
-        if (!blockSize) blockSize = 1024;
-        if (!stepSize) {
-            if (domain == Vamp::Plugin::FrequencyDomain) {
-//                std::cerr << "frequency domain, step = " << blockSize/2 << std::endl;
-                stepSize = blockSize/2;
-            } else {
-//                std::cerr << "time domain, step = " << blockSize/2 << std::endl;
-                stepSize = blockSize;
-            }
-        }
-    }
-}
-    
-
--- a/plugin/transform/PluginTransformer.h	Fri Nov 30 17:31:09 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/* -*- 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 QMUL.
-   
-    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 _PLUGIN_TRANSFORMER_H_
-#define _PLUGIN_TRANSFORMER_H_
-
-#include "ModelTransformer.h"
-
-#include "base/Window.h"
-
-#include "vamp-sdk/Plugin.h"
-
-//!!! should this just move back up to Transformer? It is after all used
-//directly in all sorts of generic places, like Document
-
-class PluginTransformer : public ModelTransformer
-{
-public:
-    class ExecutionContext {
-    public:
-        // Time domain:
-        ExecutionContext(int _c = -1, size_t _bs = 0);
-        
-        // Frequency domain:
-        ExecutionContext(int _c, size_t _ss, size_t _bs, WindowType _wt);
-
-        // From plugin defaults:
-        ExecutionContext(int _c, const Vamp::PluginBase *_plugin);
-
-        bool operator==(const ExecutionContext &);
-
-        void makeConsistentWithPlugin(const Vamp::PluginBase *_plugin);
-
-        int channel;
-        Vamp::Plugin::InputDomain domain;
-        size_t stepSize;
-        size_t blockSize;
-        WindowType windowType;
-        size_t startFrame;
-        size_t duration;    // 0 -> whole thing
-        float sampleRate;   // 0 -> model's rate
-        bool updates;
-    };
-
-protected:
-    PluginTransformer(Model *inputModel,
-                      const ExecutionContext &context);
-
-    ExecutionContext m_context;
-};
-
-#endif
--- a/plugin/transform/RealTimeEffectModelTransformer.cpp	Fri Nov 30 17:31:09 2007 +0000
+++ b/plugin/transform/RealTimeEffectModelTransformer.cpp	Fri Dec 07 16:47:31 2007 +0000
@@ -25,22 +25,23 @@
 #include "data/model/WritableWaveFileModel.h"
 #include "data/model/WaveFileModel.h"
 
+#include "TransformFactory.h"
+
 #include <iostream>
 
-RealTimeEffectModelTransformer::RealTimeEffectModelTransformer(Model *inputModel,
-                                                               QString pluginId,
-                                                               const ExecutionContext &context,
-                                                               QString configurationXml,
-                                                               QString units,
-                                                               int output) :
-    PluginTransformer(inputModel, context),
-    m_pluginId(pluginId),
-    m_configurationXml(configurationXml),
-    m_units(units),
-    m_plugin(0),
-    m_outputNo(output)
+RealTimeEffectModelTransformer::RealTimeEffectModelTransformer(Input in,
+                                                               const Transform &transform) :
+    ModelTransformer(in, transform),
+    m_plugin(0)
 {
-    if (!m_context.blockSize) m_context.blockSize = 1024;
+    m_units = TransformFactory::getInstance()->getTransformUnits
+        (transform.getIdentifier());
+    m_outputNo =
+        (transform.getOutput() == "A") ? -1 : transform.getOutput().toInt();
+
+    QString pluginId = transform.getPluginIdentifier();
+
+    if (!m_transform.getBlockSize()) m_transform.setBlockSize(1024);
 
 //    std::cerr << "RealTimeEffectModelTransformer::RealTimeEffectModelTransformer: plugin " << pluginId.toStdString() << ", output " << output << std::endl;
 
@@ -53,12 +54,12 @@
 	return;
     }
 
-    DenseTimeValueModel *input = getInput();
+    DenseTimeValueModel *input = getConformingInput();
     if (!input) return;
 
     m_plugin = factory->instantiatePlugin(pluginId, 0, 0,
-                                          m_input->getSampleRate(),
-                                          m_context.blockSize,
+                                          input->getSampleRate(),
+                                          m_transform.getBlockSize(),
                                           input->getChannelCount());
 
     if (!m_plugin) {
@@ -67,9 +68,7 @@
 	return;
     }
 
-    if (configurationXml != "") {
-        PluginXml(m_plugin).setParametersFromXml(configurationXml);
-    }
+    TransformFactory::getInstance()->setPluginParameters(m_transform, m_plugin);
 
     if (m_outputNo >= 0 &&
         m_outputNo >= int(m_plugin->getControlOutputCount())) {
@@ -92,9 +91,9 @@
     } else {
 	
         SparseTimeValueModel *model = new SparseTimeValueModel
-            (input->getSampleRate(), m_context.blockSize, 0.0, 0.0, false);
+            (input->getSampleRate(), m_transform.getBlockSize(), 0.0, 0.0, false);
 
-        if (units != "") model->setScaleUnits(units);
+        if (m_units != "") model->setScaleUnits(m_units);
 
         m_output = model;
     }
@@ -106,12 +105,12 @@
 }
 
 DenseTimeValueModel *
-RealTimeEffectModelTransformer::getInput()
+RealTimeEffectModelTransformer::getConformingInput()
 {
     DenseTimeValueModel *dtvm =
 	dynamic_cast<DenseTimeValueModel *>(getInputModel());
     if (!dtvm) {
-	std::cerr << "RealTimeEffectModelTransformer::getInput: WARNING: Input model is not conformable to DenseTimeValueModel" << std::endl;
+	std::cerr << "RealTimeEffectModelTransformer::getConformingInput: WARNING: Input model is not conformable to DenseTimeValueModel" << std::endl;
     }
     return dtvm;
 }
@@ -119,7 +118,7 @@
 void
 RealTimeEffectModelTransformer::run()
 {
-    DenseTimeValueModel *input = getInput();
+    DenseTimeValueModel *input = getConformingInput();
     if (!input) return;
 
     while (!input->isReady()) {
@@ -136,17 +135,23 @@
 
     size_t sampleRate = input->getSampleRate();
     size_t channelCount = input->getChannelCount();
-    if (!wwfm && m_context.channel != -1) channelCount = 1;
+    if (!wwfm && m_input.getChannel() != -1) channelCount = 1;
 
     long blockSize = m_plugin->getBufferSize();
 
     float **inbufs = m_plugin->getAudioInputBuffers();
 
-    long startFrame = m_input->getStartFrame();
-    long   endFrame = m_input->getEndFrame();
+    long startFrame = m_input.getModel()->getStartFrame();
+    long   endFrame = m_input.getModel()->getEndFrame();
     
-    long contextStart = m_context.startFrame;
-    long contextDuration = m_context.duration;
+    RealTime contextStartRT = m_transform.getStartTime();
+    RealTime contextDurationRT = m_transform.getDuration();
+
+    long contextStart =
+        RealTime::realTime2Frame(contextStartRT, sampleRate);
+
+    long contextDuration =
+        RealTime::realTime2Frame(contextDurationRT, sampleRate);
 
     if (contextStart == 0 || contextStart < startFrame) {
         contextStart = startFrame;
@@ -179,7 +184,7 @@
 	if (channelCount == 1) {
             if (inbufs && inbufs[0]) {
                 got = input->getData
-                    (m_context.channel, blockFrame, blockSize, inbufs[0]);
+                    (m_input.getChannel(), blockFrame, blockSize, inbufs[0]);
                 while (got < blockSize) {
                     inbufs[0][got++] = 0.0;
                 }          
--- a/plugin/transform/RealTimeEffectModelTransformer.h	Fri Nov 30 17:31:09 2007 +0000
+++ b/plugin/transform/RealTimeEffectModelTransformer.h	Fri Dec 07 16:47:31 2007 +0000
@@ -16,34 +16,27 @@
 #ifndef _REAL_TIME_PLUGIN_TRANSFORMER_H_
 #define _REAL_TIME_PLUGIN_TRANSFORMER_H_
 
-#include "PluginTransformer.h"
+#include "ModelTransformer.h"
 #include "plugin/RealTimePluginInstance.h"
 
 class DenseTimeValueModel;
 
-class RealTimeEffectModelTransformer : public PluginTransformer
+class RealTimeEffectModelTransformer : public ModelTransformer
 {
 public:
-    RealTimeEffectModelTransformer(Model *inputModel,
-			    QString plugin,
-                            const ExecutionContext &context,
-			    QString configurationXml = "",
-                            QString units = "",
-			    int output = -1); // -1 -> audio, 0+ -> data
+    RealTimeEffectModelTransformer(Input input,
+                                   const Transform &transform);
     virtual ~RealTimeEffectModelTransformer();
 
 protected:
     virtual void run();
 
-    QString m_pluginId;
-    QString m_configurationXml;
     QString m_units;
-
     RealTimePluginInstance *m_plugin;
     int m_outputNo;
 
     // just casts
-    DenseTimeValueModel *getInput();
+    DenseTimeValueModel *getConformingInput();
 };
 
 #endif
--- a/plugin/transform/Transform.cpp	Fri Nov 30 17:31:09 2007 +0000
+++ b/plugin/transform/Transform.cpp	Fri Dec 07 16:47:31 2007 +0000
@@ -19,6 +19,17 @@
 
 #include "plugin/FeatureExtractionPluginFactory.h"
 
+#include <QXmlAttributes>
+
+#include <QDomDocument>
+#include <QDomElement>
+#include <QDomNamedNodeMap>
+#include <QDomAttr>
+
+#include <QTextStream>
+
+#include <iostream>
+
 Transform::Transform() :
     m_stepSize(0),
     m_blockSize(0),
@@ -27,10 +38,102 @@
 {
 }
 
+Transform::Transform(QString xml) :
+    m_stepSize(0),
+    m_blockSize(0),
+    m_windowType(HanningWindow),
+    m_sampleRate(0)
+{
+    QDomDocument doc;
+    
+    QString error;
+    int errorLine;
+    int errorColumn;
+
+    if (!doc.setContent(xml, false, &error, &errorLine, &errorColumn)) {
+        std::cerr << "Transform::Transform: Error in parsing XML: "
+                  << error.toStdString() << " at line " << errorLine
+                  << ", column " << errorColumn << std::endl;
+        std::cerr << "Input follows:" << std::endl;
+        std::cerr << xml.toStdString() << std::endl;
+        std::cerr << "Input ends." << std::endl;
+        return;
+    }
+    
+    QDomElement transformElt = doc.firstChildElement("transform");
+    QDomNamedNodeMap attrNodes = transformElt.attributes();
+    QXmlAttributes attrs;
+
+    for (unsigned int i = 0; i < attrNodes.length(); ++i) {
+        QDomAttr attr = attrNodes.item(i).toAttr();
+        if (!attr.isNull()) attrs.append(attr.name(), "", "", attr.value());
+    }
+
+    setFromXmlAttributes(attrs);
+
+    for (QDomElement paramElt = transformElt.firstChildElement("parameter");
+         !paramElt.isNull();
+         paramElt = paramElt.nextSiblingElement("parameter")) {
+
+        QDomNamedNodeMap paramAttrs = paramElt.attributes();
+
+        QDomAttr nameAttr = paramAttrs.namedItem("name").toAttr();
+        if (nameAttr.isNull() || nameAttr.value() == "") continue;
+        
+        QDomAttr valueAttr = paramAttrs.namedItem("value").toAttr();
+        if (valueAttr.isNull() || valueAttr.value() == "") continue;
+
+        setParameter(nameAttr.value(), valueAttr.value().toFloat());
+    }
+
+    for (QDomElement configElt = transformElt.firstChildElement("configuration");
+         !configElt.isNull();
+         configElt = configElt.nextSiblingElement("configuration")) {
+
+        QDomNamedNodeMap configAttrs = configElt.attributes();
+
+        QDomAttr nameAttr = configAttrs.namedItem("name").toAttr();
+        if (nameAttr.isNull() || nameAttr.value() == "") continue;
+        
+        QDomAttr valueAttr = configAttrs.namedItem("value").toAttr();
+        if (valueAttr.isNull() || valueAttr.value() == "") continue;
+
+        setConfigurationValue(nameAttr.value(), valueAttr.value());
+    }
+}
+
 Transform::~Transform()
 {
 }
 
+bool
+Transform::operator==(const Transform &t)
+{
+    return 
+        m_id == t.m_id &&
+        m_parameters == t.m_parameters &&
+        m_configuration == t.m_configuration &&
+        m_program == t.m_program &&
+        m_stepSize == t.m_stepSize &&
+        m_blockSize == t.m_blockSize &&
+        m_windowType == t.m_windowType &&
+        m_startTime == t.m_startTime &&
+        m_duration == t.m_duration &&
+        m_sampleRate == t.m_sampleRate;
+}
+
+void
+Transform::setIdentifier(TransformId id)
+{
+    m_id = id;
+}
+
+TransformId
+Transform::getIdentifier() const
+{
+    return m_id;
+}
+
 QString
 Transform::createIdentifier(QString type, QString soName, QString label,
                             QString output)
@@ -73,8 +176,212 @@
     return m_id.section(':', 3);
 }
 
+const Transform::ParameterMap &
+Transform::getParameters() const
+{
+    return m_parameters;
+}
+
 void
-Transform::toXml(QTextStream &stream, QString indent, QString extraAttributes) const
+Transform::setParameters(const ParameterMap &pm)
 {
+    m_parameters = pm;
+}
+
+void
+Transform::setParameter(QString name, float value)
+{
+    std::cerr << "Transform::setParameter(" << name.toStdString()
+              << ") -> " << value << std::endl;
+    m_parameters[name] = value;
+}
+
+const Transform::ConfigurationMap &
+Transform::getConfiguration() const
+{
+    return m_configuration;
+}
+
+void
+Transform::setConfiguration(const ConfigurationMap &cm)
+{
+    m_configuration = cm;
+}
+
+void
+Transform::setConfigurationValue(QString name, QString value)
+{
+    std::cerr << "Transform::setConfigurationValue(" << name.toStdString()
+              << ") -> " << value.toStdString() << std::endl;
+    m_configuration[name] = value;
+}
+
+QString
+Transform::getProgram() const
+{
+    return m_program;
+}
+
+void
+Transform::setProgram(QString program)
+{
+    m_program = program;
+}
+
     
+size_t
+Transform::getStepSize() const
+{
+    return m_stepSize;
 }
+
+void
+Transform::setStepSize(size_t s)
+{
+    m_stepSize = s;
+}
+    
+size_t
+Transform::getBlockSize() const
+{
+    return m_blockSize;
+}
+
+void
+Transform::setBlockSize(size_t s)
+{
+    m_blockSize = s;
+}
+
+WindowType
+Transform::getWindowType() const
+{
+    return m_windowType;
+}
+
+void
+Transform::setWindowType(WindowType type)
+{
+    m_windowType = type;
+}
+
+RealTime
+Transform::getStartTime() const
+{
+    return m_startTime;
+}
+
+void
+Transform::setStartTime(RealTime t)
+{
+    m_startTime = t;
+}
+
+RealTime
+Transform::getDuration() const
+{
+    return m_duration;
+}
+
+void
+Transform::setDuration(RealTime d)
+{
+    m_duration = d;
+}
+    
+float
+Transform::getSampleRate() const
+{
+    return m_sampleRate;
+}
+
+void
+Transform::setSampleRate(float rate)
+{
+    m_sampleRate = rate;
+}
+
+void
+Transform::toXml(QTextStream &out, QString indent, QString extraAttributes) const
+{
+    out << indent;
+
+    bool haveContent = true;
+    if (m_parameters.empty() && m_configuration.empty()) haveContent = false;
+
+    out << QString("<transform id=\"%1\" program=\"%2\" stepSize=\"%3\" blockSize=\"%4\" windowType=\"%5\" startTime=\"%6\" duration=\"%7\" sampleRate=\"%8\" %9")
+        .arg(encodeEntities(m_id))
+        .arg(encodeEntities(m_program))
+        .arg(m_stepSize)
+        .arg(m_blockSize)
+        .arg(encodeEntities(Window<float>::getNameForType(m_windowType).c_str()))
+        .arg(encodeEntities(m_startTime.toString().c_str()))
+        .arg(encodeEntities(m_duration.toString().c_str()))
+        .arg(m_sampleRate)
+        .arg(extraAttributes);
+
+    if (haveContent) {
+
+        out << ">\n";
+
+        for (ParameterMap::const_iterator i = m_parameters.begin();
+             i != m_parameters.end(); ++i) {
+            out << indent << "  "
+                << QString("<parameter name=\"%1\" value=\"%2\"/>\n")
+                .arg(encodeEntities(i->first))
+                .arg(i->second);
+        }
+        
+        for (ConfigurationMap::const_iterator i = m_configuration.begin();
+             i != m_configuration.end(); ++i) {
+            out << indent << "  "
+                << QString("<configuration name=\"%1\" value=\"%2\"/>\n")
+                .arg(encodeEntities(i->first))
+                .arg(encodeEntities(i->second));
+        }
+
+        out << indent << "</transform>\n";
+
+    } else {
+
+        out << "/>\n";
+    }
+}
+
+void
+Transform::setFromXmlAttributes(const QXmlAttributes &attrs)
+{
+    if (attrs.value("id") != "") {
+        setIdentifier(attrs.value("id"));
+    }
+
+    if (attrs.value("program") != "") {
+        setProgram(attrs.value("program"));
+    }
+
+    if (attrs.value("stepSize") != "") {
+        setStepSize(attrs.value("stepSize").toInt());
+    }
+
+    if (attrs.value("blockSize") != "") {
+        setBlockSize(attrs.value("blockSize").toInt());
+    }
+
+    if (attrs.value("windowType") != "") {
+        setWindowType(Window<float>::getTypeForName
+                      (attrs.value("windowType").toStdString()));
+    }
+
+    if (attrs.value("startTime") != "") {
+        setStartTime(RealTime::fromString(attrs.value("startTime").toStdString()));
+    }
+
+    if (attrs.value("duration") != "") {
+        setStartTime(RealTime::fromString(attrs.value("duration").toStdString()));
+    }
+    
+    if (attrs.value("sampleRate") != "") {
+        setSampleRate(attrs.value("sampleRate").toFloat());
+    }
+}
+
--- a/plugin/transform/Transform.h	Fri Nov 30 17:31:09 2007 +0000
+++ b/plugin/transform/Transform.h	Fri Dec 07 16:47:31 2007 +0000
@@ -18,13 +18,14 @@
 
 #include "base/XmlExportable.h"
 #include "base/Window.h"
-
-#include <vamp-sdk/RealTime.h>
+#include "base/RealTime.h"
 
 #include <QString>
 
 typedef QString TransformId;
 
+class QXmlAttributes;
+
 namespace Vamp {
     class PluginBase;
 }
@@ -32,11 +33,32 @@
 class Transform : public XmlExportable
 {
 public:
+    /**
+     * Construct a new Transform with default data and no identifier.
+     * The Transform object will be meaningless until some data and an
+     * identifier have been set on it.
+     *
+     * To construct a Transform for use with a particular transform
+     * identifier, use TransformFactory::getDefaultTransformFor.
+     */
     Transform();
+
+    /**
+     * Construct a Transform by parsing the given XML data string.
+     * This is the inverse of toXml.
+     */
+    Transform(QString xml);
+
     virtual ~Transform();
 
-    void setIdentifier(TransformId id) { m_id = id; }
-    TransformId getIdentifier() const { return m_id; }
+    /**
+     * Compare two Transforms.  They only compare equal if every data
+     * element matches.
+     */
+    bool operator==(const Transform &);
+
+    void setIdentifier(TransformId id);
+    TransformId getIdentifier() const;
     
     void setPlugin(QString pluginIdentifier);
     void setOutput(QString output);
@@ -49,39 +71,47 @@
 
     typedef std::map<QString, float> ParameterMap;
     
-    ParameterMap getParameters() const { return m_parameters; }
-    void setParameters(const ParameterMap &pm) { m_parameters = pm; }
+    const ParameterMap &getParameters() const;
+    void setParameters(const ParameterMap &pm);
+    void setParameter(QString name, float value);
 
     typedef std::map<QString, QString> ConfigurationMap;
 
-    ConfigurationMap getConfiguration() const { return m_configuration; }
-    void setConfiguration(const ConfigurationMap &cm) { m_configuration = cm; }
+    const ConfigurationMap &getConfiguration() const;
+    void setConfiguration(const ConfigurationMap &cm);
+    void setConfigurationValue(QString name, QString value);
 
-    QString getProgram() const { return m_program; }
-    void setProgram(QString program) { m_program = program; }
+    QString getProgram() const;
+    void setProgram(QString program);
     
-    size_t getStepSize() const { return m_stepSize; }
-    void setStepSize(size_t s) { m_stepSize = s; }
+    size_t getStepSize() const;
+    void setStepSize(size_t s);
     
-    size_t getBlockSize() const { return m_blockSize; }
-    void setBlockSize(size_t s) { m_blockSize = s; }
-
-    WindowType getWindowType() const { return m_windowType; }
-    void setWindowType(WindowType type) { m_windowType = type; }
-
-    Vamp::RealTime getStartTime() const { return m_startTime; }
-    void setStartTime(Vamp::RealTime t) { m_startTime = t; }
-
-    Vamp::RealTime getDuration() const { return m_duration; } // 0 -> all
-    void setDuration(Vamp::RealTime d) { m_duration = d; }
+    size_t getBlockSize() const;
+    void setBlockSize(size_t s);
     
-    float getSampleRate() const { return m_sampleRate; } // 0 -> as input
-    void setSampleRate(float rate) { m_sampleRate = rate; }
+    WindowType getWindowType() const;
+    void setWindowType(WindowType type);
+    
+    RealTime getStartTime() const;
+    void setStartTime(RealTime t);
+    
+    RealTime getDuration() const; // 0 -> all
+    void setDuration(RealTime d);
+    
+    float getSampleRate() const; // 0 -> as input
+    void setSampleRate(float rate);
 
     void toXml(QTextStream &stream, QString indent = "",
                QString extraAttributes = "") const;
 
-    static Transform fromXmlString(QString xml);
+    /**
+     * Set the main transform data from the given XML attributes.
+     * This does not set the parameters or configuration, which are
+     * exported to separate XML elements rather than attributes of the
+     * transform element.
+     */
+    void setFromXmlAttributes(const QXmlAttributes &);
 
 protected:
     TransformId m_id; // pluginid:output, that is type:soname:label:output
@@ -99,8 +129,8 @@
     size_t m_stepSize;
     size_t m_blockSize;
     WindowType m_windowType;
-    Vamp::RealTime m_startTime;
-    Vamp::RealTime m_duration;
+    RealTime m_startTime;
+    RealTime m_duration;
     float m_sampleRate;
 };
 
--- a/plugin/transform/TransformDescription.h	Fri Nov 30 17:31:09 2007 +0000
+++ b/plugin/transform/TransformDescription.h	Fri Dec 07 16:47:31 2007 +0000
@@ -37,6 +37,10 @@
  * The friendly name is a shorter version of the name.
  *
  * The type is also intended to be user-readable, for use in menus.
+ *
+ * To obtain these objects, use
+ * TransformFactory::getAllTransformDescriptions and
+ * TransformFactory::getTransformDescription.
  */
 
 struct TransformDescription
--- a/plugin/transform/TransformFactory.cpp	Fri Nov 30 17:31:09 2007 +0000
+++ b/plugin/transform/TransformFactory.cpp	Fri Dec 07 16:47:31 2007 +0000
@@ -28,6 +28,7 @@
 #include <set>
 
 #include <QRegExp>
+#include <QTextStream>
 
 TransformFactory *
 TransformFactory::m_instance = new TransformFactory;
@@ -43,7 +44,7 @@
 }
 
 TransformList
-TransformFactory::getAllTransforms()
+TransformFactory::getAllTransformDescriptions()
 {
     if (m_transforms.empty()) populateTransforms();
 
@@ -62,6 +63,18 @@
     return list;
 }
 
+TransformDescription
+TransformFactory::getTransformDescription(TransformId id)
+{
+    if (m_transforms.empty()) populateTransforms();
+
+    if (m_transforms.find(id) == m_transforms.end()) {
+        return TransformDescription();
+    }
+
+    return m_transforms[id];
+}
+
 std::vector<QString>
 TransformFactory::getAllTransformTypes()
 {
@@ -217,7 +230,7 @@
 	}
 
 	Vamp::Plugin *plugin = 
-	    factory->instantiatePlugin(pluginId, 48000);
+	    factory->instantiatePlugin(pluginId, 44100);
 
 	if (!plugin) {
 	    std::cerr << "WARNING: TransformFactory::populateTransforms: Failed to instantiate plugin " << pluginId.toLocal8Bit().data() << std::endl;
@@ -423,6 +436,71 @@
     }
 }
 
+
+Transform
+TransformFactory::getDefaultTransformFor(TransformId id, size_t rate)
+{
+    Transform t;
+    t.setIdentifier(id);
+    if (rate != 0) t.setSampleRate(rate);
+
+    Vamp::PluginBase *plugin = instantiatePluginFor(id, rate);
+
+    if (plugin) {
+        setParametersFromPlugin(t, plugin);
+        makeContextConsistentWithPlugin(t, plugin);
+        delete plugin;
+    }
+
+    return t;
+}
+
+Vamp::PluginBase *
+TransformFactory::instantiatePluginFor(TransformId identifier, size_t rate)
+{
+    Transform t;
+    t.setIdentifier(identifier);
+    if (rate == 0) rate = 44100;
+    QString pluginId = t.getPluginIdentifier();
+
+    Vamp::PluginBase *plugin = 0;
+
+    if (t.getType() == Transform::FeatureExtraction) {
+
+        FeatureExtractionPluginFactory *factory = 
+            FeatureExtractionPluginFactory::instanceFor(pluginId);
+
+        plugin = factory->instantiatePlugin(pluginId, rate);
+
+    } else {
+
+        RealTimePluginFactory *factory = 
+            RealTimePluginFactory::instanceFor(pluginId);
+            
+        plugin = factory->instantiatePlugin(pluginId, 0, 0, rate, 1024, 1);
+    }
+
+    return plugin;
+}
+
+Vamp::Plugin *
+TransformFactory::downcastVampPlugin(Vamp::PluginBase *plugin)
+{
+    Vamp::Plugin *vp = dynamic_cast<Vamp::Plugin *>(plugin);
+    if (!vp) {
+//        std::cerr << "makeConsistentWithPlugin: not a Vamp::Plugin" << std::endl;
+        vp = dynamic_cast<Vamp::PluginHostAdapter *>(plugin); //!!! why?
+}
+    if (!vp) {
+//        std::cerr << "makeConsistentWithPlugin: not a Vamp::PluginHostAdapter" << std::endl;
+        vp = dynamic_cast<Vamp::HostExt::PluginWrapper *>(plugin); //!!! no, I mean really why?
+    }
+    if (!vp) {
+//        std::cerr << "makeConsistentWithPlugin: not a Vamp::HostExt::PluginWrapper" << std::endl;
+    }
+    return vp;
+}
+
 bool
 TransformFactory::haveTransform(TransformId identifier)
 {
@@ -454,6 +532,28 @@
     } else return "";
 }
 
+Vamp::Plugin::InputDomain
+TransformFactory::getTransformInputDomain(TransformId identifier)
+{
+    Transform transform;
+    transform.setIdentifier(identifier);
+
+    if (transform.getType() != Transform::FeatureExtraction) {
+        return Vamp::Plugin::TimeDomain;
+    }
+
+    Vamp::Plugin *plugin =
+        downcastVampPlugin(instantiatePluginFor(identifier, 0));
+
+    if (plugin) {
+        Vamp::Plugin::InputDomain d = plugin->getInputDomain();
+        delete plugin;
+        return d;
+    }
+
+    return Vamp::Plugin::TimeDomain;
+}
+
 bool
 TransformFactory::isTransformConfigurable(TransformId identifier)
 {
@@ -472,7 +572,7 @@
 
         Vamp::Plugin *plugin = 
             FeatureExtractionPluginFactory::instanceFor(id)->
-            instantiatePlugin(id, 48000);
+            instantiatePlugin(id, 44100);
         if (!plugin) return false;
 
         min = plugin->getMinChannelCount();
@@ -483,6 +583,8 @@
 
     } else if (RealTimePluginFactory::instanceFor(id)) {
 
+        // don't need to instantiate
+
         const RealTimePluginDescriptor *descriptor = 
             RealTimePluginFactory::instanceFor(id)->
             getPluginDescriptor(id);
@@ -503,6 +605,10 @@
 {
     Transform::ParameterMap pmap;
 
+    //!!! record plugin & API version
+
+    //!!! check that this is the right plugin!
+
     Vamp::PluginBase::ParameterList parameters =
         plugin->getParameterDescriptors();
 
@@ -539,21 +645,48 @@
 }
 
 void
+TransformFactory::setPluginParameters(const Transform &transform,
+                                      Vamp::PluginBase *plugin)
+{
+    //!!! check plugin & API version (see e.g. PluginXml::setParameters)
+
+    //!!! check that this is the right plugin!
+
+    RealTimePluginInstance *rtpi =
+        dynamic_cast<RealTimePluginInstance *>(plugin);
+
+    if (rtpi) {
+        const Transform::ConfigurationMap &cmap = transform.getConfiguration();
+        for (Transform::ConfigurationMap::const_iterator i = cmap.begin();
+             i != cmap.end(); ++i) {
+            rtpi->configure(i->first.toStdString(), i->second.toStdString());
+        }
+    }
+
+    if (transform.getProgram() != "") {
+        plugin->selectProgram(transform.getProgram().toStdString());
+    }
+
+    const Transform::ParameterMap &pmap = transform.getParameters();
+
+    Vamp::PluginBase::ParameterList parameters =
+        plugin->getParameterDescriptors();
+
+    for (Vamp::PluginBase::ParameterList::const_iterator i = parameters.begin();
+         i != parameters.end(); ++i) {
+        QString key = i->identifier.c_str();
+        Transform::ParameterMap::const_iterator pmi = pmap.find(key);
+        if (pmi != pmap.end()) {
+            plugin->setParameter(i->identifier, pmi->second);
+        }
+    }
+}
+
+void
 TransformFactory::makeContextConsistentWithPlugin(Transform &transform,
                                                   Vamp::PluginBase *plugin)
 {
-    const Vamp::Plugin *vp = dynamic_cast<const Vamp::Plugin *>(plugin);
-    if (!vp) {
-//        std::cerr << "makeConsistentWithPlugin: not a Vamp::Plugin" << std::endl;
-        vp = dynamic_cast<const Vamp::PluginHostAdapter *>(plugin); //!!! why?
-}
-    if (!vp) {
-//        std::cerr << "makeConsistentWithPlugin: not a Vamp::PluginHostAdapter" << std::endl;
-        vp = dynamic_cast<const Vamp::HostExt::PluginWrapper *>(plugin); //!!! no, I mean really why?
-    }
-    if (!vp) {
-//        std::cerr << "makeConsistentWithPlugin: not a Vamp::HostExt::PluginWrapper" << std::endl;
-    }
+    const Vamp::Plugin *vp = downcastVampPlugin(plugin);
 
     if (!vp) {
         // time domain input for real-time effects plugin
@@ -586,49 +719,40 @@
     }
 }
 
-Transform
-TransformFactory::getDefaultTransformFor(TransformId id, size_t rate)
+QString
+TransformFactory::getPluginConfigurationXml(const Transform &t)
 {
-    Transform t;
-    t.setIdentifier(id);
-    
-    if (rate == 0) {
-        rate = 44100;
-    } else {
-        t.setSampleRate(rate);
+    QString xml;
+
+    Vamp::PluginBase *plugin = instantiatePluginFor(t.getIdentifier(), 0);
+    if (!plugin) {
+        std::cerr << "TransformFactory::getPluginConfigurationXml: "
+                  << "Unable to instantiate plugin for transform \""
+                  << t.getIdentifier().toStdString() << "\"" << std::endl;
+        return xml;
     }
 
-    QString pluginId = t.getPluginIdentifier();
+    QTextStream out(&xml);
+    PluginXml(plugin).toXml(out);
+    delete plugin;
 
-    if (t.getType() == Transform::FeatureExtraction) {
+    return xml;
+}
 
-        FeatureExtractionPluginFactory *factory = 
-            FeatureExtractionPluginFactory::instanceFor(pluginId);
-
-        Vamp::Plugin *plugin = factory->instantiatePlugin
-            (pluginId, rate);
-
-        if (plugin) {
-            setParametersFromPlugin(t, plugin);
-            makeContextConsistentWithPlugin(t, plugin);
-            delete plugin;
-        }
-
-    } else {
-
-        RealTimePluginFactory *factory = 
-            RealTimePluginFactory::instanceFor(pluginId);
-            
-        RealTimePluginInstance *plugin = factory->instantiatePlugin
-            (pluginId, 0, 0, rate, 1024, 1);
-        
-        if (plugin) {
-            setParametersFromPlugin(t, plugin);
-            makeContextConsistentWithPlugin(t, plugin);
-            delete plugin;
-        }
+void
+TransformFactory::setParametersFromPluginConfigurationXml(Transform &t,
+                                                          QString xml)
+{
+    Vamp::PluginBase *plugin = instantiatePluginFor(t.getIdentifier(), 0);
+    if (!plugin) {
+        std::cerr << "TransformFactory::setParametersFromPluginConfigurationXml: "
+                  << "Unable to instantiate plugin for transform \""
+                  << t.getIdentifier().toStdString() << "\"" << std::endl;
+        return;
     }
 
-    return t;
+    PluginXml(plugin).setParametersFromXml(xml);
+    setParametersFromPlugin(t, plugin);
+    delete plugin;
 }
 
--- a/plugin/transform/TransformFactory.h	Fri Nov 30 17:31:09 2007 +0000
+++ b/plugin/transform/TransformFactory.h	Fri Dec 07 16:47:31 2007 +0000
@@ -18,11 +18,11 @@
 
 #include "TransformDescription.h"
 
+#include <vamp-sdk/Plugin.h>
+
 #include <map>
 #include <set>
 
-namespace Vamp { class PluginBase; }
-
 class TransformFactory : public QObject
 {
     Q_OBJECT
@@ -32,10 +32,10 @@
 
     static TransformFactory *getInstance();
 
-    TransformList getAllTransforms();
+    TransformList getAllTransformDescriptions();
+    TransformDescription getTransformDescription(TransformId id);
 
     std::vector<QString> getAllTransformTypes();
-
     std::vector<QString> getTransformCategories(QString transformType);
     std::vector<QString> getTransformMakers(QString transformType);
 
@@ -45,6 +45,13 @@
     bool haveTransform(TransformId identifier);
 
     /**
+     * A single transform ID can lead to many possible Transforms,
+     * with different parameters and execution context settings.
+     * Return the default one for the given transform.
+     */
+    Transform getDefaultTransformFor(TransformId identifier, size_t rate = 0);
+
+    /**
      * Full name of a transform, suitable for putting on a menu.
      */
     QString getTransformName(TransformId identifier);
@@ -57,6 +64,8 @@
 
     QString getTransformUnits(TransformId identifier);
 
+    Vamp::Plugin::InputDomain getTransformInputDomain(TransformId identifier);
+
     /**
      * Return true if the transform has any configurable parameters,
      * i.e. if getConfigurationForTransform can ever return a non-trivial
@@ -82,6 +91,12 @@
     void setParametersFromPlugin(Transform &transform, Vamp::PluginBase *plugin);
 
     /**
+     * Set the parameters, program and configuration strings on the
+     * given plugin from the given Transform object.
+     */
+    void setPluginParameters(const Transform &transform, Vamp::PluginBase *plugin);
+    
+    /**
      * If the given Transform object has no processing step and block
      * sizes set, set them to appropriate defaults for the given
      * plugin.
@@ -89,11 +104,27 @@
     void makeContextConsistentWithPlugin(Transform &transform, Vamp::PluginBase *plugin); 
 
     /**
-     * A single transform ID can lead to many possible Transforms,
-     * with different parameters and execution context settings.
-     * Return the default one for the given transform.
+     * Retrieve a <plugin ... /> XML fragment that describes the
+     * plugin parameters, program and configuration data for the given
+     * transform.
+     *
+     * This function is provided for backward compatibility only.  Use
+     * Transform::toXml where compatibility with PluginXml
+     * descriptions of transforms is not required.
      */
-    Transform getDefaultTransformFor(TransformId identifier, size_t rate = 0);
+    QString getPluginConfigurationXml(const Transform &transform);
+
+    /**
+     * Set the plugin parameters, program and configuration strings on
+     * the given Transform object from the given <plugin ... /> XML
+     * fragment.
+     *
+     * This function is provided for backward compatibility only.  Use
+     * Transform(QString) where compatibility with PluginXml
+     * descriptions of transforms is not required.
+     */
+    void setParametersFromPluginConfigurationXml(Transform &transform,
+                                                 QString xml);
 
 protected:
     typedef std::map<TransformId, TransformDescription> TransformDescriptionMap;
@@ -103,6 +134,9 @@
     void populateFeatureExtractionPlugins(TransformDescriptionMap &);
     void populateRealTimePlugins(TransformDescriptionMap &);
 
+    Vamp::PluginBase *instantiatePluginFor(TransformId id, size_t rate);
+    Vamp::Plugin *downcastVampPlugin(Vamp::PluginBase *);
+
     static TransformFactory *m_instance;
 };