changeset 855:42bbc538d983 tonioni_multi_transform

Merge from tonioni branch
author Chris Cannam
date Wed, 04 Dec 2013 18:29:15 +0000
parents 418cd2064769 (current diff) 23ecd10c2eb6 (diff)
children 8593a3fe50ac
files
diffstat 11 files changed, 283 insertions(+), 133 deletions(-) [+]
line wrap: on
line diff
--- a/data/fileio/MIDIFileWriter.cpp	Mon Dec 02 11:17:24 2013 +0000
+++ b/data/fileio/MIDIFileWriter.cpp	Wed Dec 04 18:29:15 2013 +0000
@@ -23,8 +23,7 @@
 #include "MIDIFileWriter.h"
 
 #include "data/midi/MIDIEvent.h"
-
-#include "model/NoteModel.h"
+#include "model/NoteData.h"
 
 #include "base/Pitch.h"
 
@@ -37,14 +36,13 @@
 
 using namespace MIDIConstants;
 
-MIDIFileWriter::MIDIFileWriter(QString path, NoteModel *model, float tempo) :
+MIDIFileWriter::MIDIFileWriter(QString path, const NoteExportable *exportable,
+                               int sampleRate, float tempo) :
     m_path(path),
-    m_model(model),
-    m_modelUsesHz(false),
+    m_exportable(exportable),
+    m_sampleRate(sampleRate),
     m_tempo(tempo)
 {
-    if (model->getScaleUnits().toLower() == "hz") m_modelUsesHz = true;
-
     if (!convert()) {
         m_error = "Conversion from model to internal MIDI format failed";
     }
@@ -342,42 +340,28 @@
 
     // Omit time signature
 
-    const NoteModel::PointList &notes =
-        static_cast<SparseModel<Note> *>(m_model)->getPoints();
+    NoteList notes = m_exportable->getNotes();
 
-    for (NoteModel::PointList::const_iterator i = notes.begin();
-         i != notes.end(); ++i) {
+    for (NoteList::const_iterator i = notes.begin(); i != notes.end(); ++i) {
 
-        long frame = i->frame;
-        float value = i->value;
+        size_t frame = i->start;
         size_t duration = i->duration;
-
-        int pitch;
-
-        if (m_modelUsesHz) {
-            pitch = Pitch::getPitchForFrequency(value);
-        } else {
-            pitch = lrintf(value);
-        }
+        int pitch = i->midiPitch;
+        int velocity = i->velocity;
 
         if (pitch < 0) pitch = 0;
         if (pitch > 127) pitch = 127;
 
         // Convert frame to MIDI time
 
-        double seconds = double(frame) / double(m_model->getSampleRate());
+        double seconds = double(frame) / double(m_sampleRate);
         double quarters = (seconds * m_tempo) / 60.0;
-        unsigned long midiTime = lrint(quarters * m_timingDivision);
-
-        int velocity = 100;
-        if (i->level > 0.f && i->level <= 1.f) {
-            velocity = lrintf(i->level * 127.f);
-        }
+        unsigned long midiTime = int(quarters * m_timingDivision + 0.5);
 
         // Get the sounding time for the matching NOTE_OFF
-        seconds = double(frame + duration) / double(m_model->getSampleRate());
+        seconds = double(frame + duration) / double(m_sampleRate);
         quarters = (seconds * m_tempo) / 60.0;
-        unsigned long endTime = lrint(quarters * m_timingDivision);
+        unsigned long endTime = int(quarters * m_timingDivision + 0.5);
 
         // At this point all the notes we insert have absolute times
         // in the delta time fields.  We resolve these into delta
--- a/data/fileio/MIDIFileWriter.h	Mon Dec 02 11:17:24 2013 +0000
+++ b/data/fileio/MIDIFileWriter.h	Wed Dec 04 18:29:15 2013 +0000
@@ -32,7 +32,7 @@
 #include <fstream>
 
 class MIDIEvent;
-class NoteModel;
+class NoteExportable;
 
 /**
  * Write a MIDI file.  This includes file write code for generic
@@ -43,7 +43,10 @@
 class MIDIFileWriter 
 {
 public:
-    MIDIFileWriter(QString path, NoteModel *model, float tempo = 120.f);
+    MIDIFileWriter(QString path, 
+                   const NoteExportable *exportable, 
+                   int sampleRate, // used to convert exportable sample timings
+                   float tempo = 120.f);
     virtual ~MIDIFileWriter();
 
     virtual bool isOK() const;
@@ -74,18 +77,18 @@
     
     bool convert();
 
-    QString             m_path;
-    NoteModel          *m_model;
-    bool                m_modelUsesHz;
-    float               m_tempo;
-    int                 m_timingDivision;   // pulses per quarter note
-    MIDIFileFormatType  m_format;
-    unsigned int        m_numberOfTracks;
+    QString               m_path;
+    const NoteExportable *m_exportable;
+    int                   m_sampleRate;
+    float                 m_tempo;
+    int                   m_timingDivision;   // pulses per quarter note
+    MIDIFileFormatType    m_format;
+    unsigned int          m_numberOfTracks;
 
-    MIDIComposition     m_midiComposition;
+    MIDIComposition       m_midiComposition;
 
-    std::ofstream      *m_midiFile;
-    QString             m_error;
+    std::ofstream        *m_midiFile;
+    QString               m_error;
 };
 
 #endif
--- a/data/model/FlexiNoteModel.h	Mon Dec 02 11:17:24 2013 +0000
+++ b/data/model/FlexiNoteModel.h	Wed Dec 04 18:29:15 2013 +0000
@@ -16,10 +16,10 @@
 #ifndef _FLEXINOTE_MODEL_H_
 #define _FLEXINOTE_MODEL_H_
 
-// #include "NotelikeModel.h" // GF: reomved as this is an uncommitted experiment for now
-
 #include "IntervalModel.h"
+#include "NoteData.h"
 #include "base/RealTime.h"
+#include "base/Pitch.h"
 #include "base/PlayParameterRepository.h"
 
 /**
@@ -97,7 +97,7 @@
 };
 
 
-class FlexiNoteModel : public IntervalModel<FlexiNote>
+class FlexiNoteModel : public IntervalModel<FlexiNote>, public NoteExportable
 {
     Q_OBJECT
     
@@ -227,6 +227,48 @@
         return SortNumeric;
     }
 
+    /**
+     * NoteExportable methods.
+     */
+
+    NoteList getNotes() const {
+        return getNotes(getStartFrame(), getEndFrame());
+    }
+
+    NoteList getNotes(size_t startFrame, size_t endFrame) const {
+        
+	PointList points = getPoints(startFrame, endFrame);
+        NoteList notes;
+
+        for (PointList::iterator pli =
+		 points.begin(); pli != points.end(); ++pli) {
+
+	    size_t duration = pli->duration;
+            if (duration == 0 || duration == 1) {
+                duration = getSampleRate() / 20;
+            }
+
+            int pitch = lrintf(pli->value);
+            
+            int velocity = 100;
+            if (pli->level > 0.f && pli->level <= 1.f) {
+                velocity = lrintf(pli->level * 127);
+            }
+
+            NoteData note(pli->frame, duration, pitch, velocity);
+
+            if (getScaleUnits() == "Hz") {
+                note.frequency = pli->value;
+                note.midiPitch = Pitch::getPitchForFrequency(note.frequency);
+                note.isMidiPitchQuantized = false;
+            }
+        
+            notes.push_back(note);
+        }
+        
+        return notes;
+    }
+
 protected:
     float m_valueQuantization;
 };
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/data/model/NoteData.h	Wed Dec 04 18:29:15 2013 +0000
@@ -0,0 +1,43 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#ifndef NOTE_DATA_H
+#define NOTE_DATA_H
+
+#include <vector>
+
+struct NoteData
+{
+    NoteData(size_t _start, size_t _dur, int _mp, int _vel) :
+	start(_start), duration(_dur), midiPitch(_mp), frequency(0),
+	isMidiPitchQuantized(true), velocity(_vel) { };
+            
+    size_t start;     // audio sample frame
+    size_t duration;  // in audio sample frames
+    int midiPitch; // 0-127
+    int frequency; // Hz, to be used if isMidiPitchQuantized false
+    bool isMidiPitchQuantized;
+    int velocity;  // MIDI-style 0-127
+};
+
+typedef std::vector<NoteData> NoteList;
+
+class NoteExportable
+{
+public:
+    virtual NoteList getNotes() const = 0;
+    virtual NoteList getNotes(size_t startFrame, size_t endFrame) const = 0;
+};
+
+#endif
--- a/data/model/NoteModel.h	Mon Dec 02 11:17:24 2013 +0000
+++ b/data/model/NoteModel.h	Wed Dec 04 18:29:15 2013 +0000
@@ -17,8 +17,10 @@
 #define _NOTE_MODEL_H_
 
 #include "IntervalModel.h"
+#include "NoteData.h"
 #include "base/RealTime.h"
 #include "base/PlayParameterRepository.h"
+#include "base/Pitch.h"
 
 /**
  * NoteModel -- a concrete IntervalModel for notes.
@@ -91,7 +93,7 @@
 };
 
 
-class NoteModel : public IntervalModel<Note>
+class NoteModel : public IntervalModel<Note>, public NoteExportable
 {
     Q_OBJECT
     
@@ -219,6 +221,48 @@
         return SortNumeric;
     }
 
+    /**
+     * NoteExportable methods.
+     */
+
+    NoteList getNotes() const {
+        return getNotes(getStartFrame(), getEndFrame());
+    }
+
+    NoteList getNotes(size_t startFrame, size_t endFrame) const {
+        
+	PointList points = getPoints(startFrame, endFrame);
+        NoteList notes;
+
+        for (PointList::iterator pli =
+		 points.begin(); pli != points.end(); ++pli) {
+
+	    size_t duration = pli->duration;
+            if (duration == 0 || duration == 1) {
+                duration = getSampleRate() / 20;
+            }
+
+            int pitch = lrintf(pli->value);
+            
+            int velocity = 100;
+            if (pli->level > 0.f && pli->level <= 1.f) {
+                velocity = lrintf(pli->level * 127);
+            }
+
+            NoteData note(pli->frame, duration, pitch, velocity);
+
+            if (getScaleUnits() == "Hz") {
+                note.frequency = pli->value;
+                note.midiPitch = Pitch::getPitchForFrequency(note.frequency);
+                note.isMidiPitchQuantized = false;
+            }
+        
+            notes.push_back(note);
+        }
+        
+        return notes;
+    }
+
 protected:
     float m_valueQuantization;
 };
--- a/data/model/SparseOneDimensionalModel.h	Mon Dec 02 11:17:24 2013 +0000
+++ b/data/model/SparseOneDimensionalModel.h	Wed Dec 04 18:29:15 2013 +0000
@@ -17,6 +17,7 @@
 #define _SPARSE_ONE_DIMENSIONAL_MODEL_H_
 
 #include "SparseModel.h"
+#include "NoteData.h"
 #include "base/PlayParameterRepository.h"
 #include "base/RealTime.h"
 
@@ -69,7 +70,8 @@
 };
 
 
-class SparseOneDimensionalModel : public SparseModel<OneDimensionalPoint>
+class SparseOneDimensionalModel : public SparseModel<OneDimensionalPoint>,
+                                  public NoteExportable
 {
     Q_OBJECT
     
@@ -181,6 +183,32 @@
         if (column == 2) return SortAlphabetical;
         return SortNumeric;
     }
+
+    /**
+     * NoteExportable methods.
+     */
+
+    NoteList getNotes() const {
+        return getNotes(getStartFrame(), getEndFrame());
+    }
+
+    NoteList getNotes(size_t startFrame, size_t endFrame) const {
+        
+	PointList points = getPoints(startFrame, endFrame);
+        NoteList notes;
+
+	for (PointList::iterator pli =
+		 points.begin(); pli != points.end(); ++pli) {
+
+            notes.push_back
+                (NoteData(pli->frame,
+                          getSampleRate() / 6, // arbitrary short duration
+                          64,   // default pitch
+                          100)); // default velocity
+        }
+
+        return notes;
+    }
 };
 
 #endif
--- a/transform/FeatureExtractionModelTransformer.cpp	Mon Dec 02 11:17:24 2013 +0000
+++ b/transform/FeatureExtractionModelTransformer.cpp	Wed Dec 04 18:29:15 2013 +0000
@@ -138,6 +138,7 @@
         size_t pstep = primaryTransform.getStepSize();
         size_t pblock = primaryTransform.getBlockSize();
 
+///!!! hang on, this isn't right -- we're modifying a copy
         primaryTransform.setStepSize(0);
         primaryTransform.setBlockSize(0);
         TransformFactory::getInstance()->makeContextConsistentWithPlugin
@@ -449,7 +450,9 @@
 {
 //    SVDEBUG << "FeatureExtractionModelTransformer::~FeatureExtractionModelTransformer()" << endl;
     delete m_plugin;
-    delete m_descriptors[n];
+    for (int j = 0; j < m_descriptors.size(); ++j) {
+        delete m_descriptors[j];
+    }
 }
 
 DenseTimeValueModel *
@@ -473,6 +476,8 @@
 
     if (m_outputs.empty()) return;
 
+    Transform primaryTransform = m_transforms[0];
+
     while (!input->isReady() && !m_abandoned) {
         SVDEBUG << "FeatureExtractionModelTransformer::run: Waiting for input model to be ready..." << endl;
         usleep(500000);
@@ -488,11 +493,11 @@
 
     float **buffers = new float*[channelCount];
     for (size_t ch = 0; ch < channelCount; ++ch) {
-	buffers[ch] = new float[m_transforms[n].getBlockSize() + 2];
+	buffers[ch] = new float[primaryTransform.getBlockSize() + 2];
     }
 
-    size_t stepSize = m_transforms[n].getStepSize();
-    size_t blockSize = m_transforms[n].getBlockSize();
+    size_t stepSize = primaryTransform.getStepSize();
+    size_t blockSize = primaryTransform.getBlockSize();
 
     bool frequencyDomain = (m_plugin->getInputDomain() ==
                             Vamp::Plugin::FrequencyDomain);
@@ -503,7 +508,7 @@
             FFTModel *model = new FFTModel
                                   (getConformingInput(),
                                    channelCount == 1 ? m_input.getChannel() : ch,
-                                   m_transforms[n].getWindowType(),
+                                   primaryTransform.getWindowType(),
                                    blockSize,
                                    stepSize,
                                    blockSize,
@@ -511,7 +516,9 @@
                                    StorageAdviser::PrecisionCritical);
             if (!model->isOK()) {
                 delete model;
-                setCompletion(100);
+                for (int j = 0; j < (int)m_outputNos.size(); ++j) {
+                    setCompletion(j, 100);
+                }
                 //!!! need a better way to handle this -- previously we were using a QMessageBox but that isn't an appropriate thing to do here either
                 throw AllocationFailed("Failed to create the FFT model for this feature extraction model transformer");
             }
@@ -523,8 +530,8 @@
     long startFrame = m_input.getModel()->getStartFrame();
     long   endFrame = m_input.getModel()->getEndFrame();
 
-    RealTime contextStartRT = m_transforms[n].getStartTime();
-    RealTime contextDurationRT = m_transforms[n].getDuration();
+    RealTime contextStartRT = primaryTransform.getStartTime();
+    RealTime contextDurationRT = primaryTransform.getDuration();
 
     long contextStart =
         RealTime::realTime2Frame(contextStartRT, sampleRate);
@@ -547,7 +554,9 @@
 
     long prevCompletion = 0;
 
-    setCompletion(0);
+    for (int j = 0; j < (int)m_outputNos.size(); ++j) {
+        setCompletion(j, 0);
+    }
 
     float *reals = 0;
     float *imaginaries = 0;
@@ -604,13 +613,17 @@
 
         if (m_abandoned) break;
 
-	for (size_t fi = 0; fi < features[m_outputNos[n]].size(); ++fi) {
-	    Vamp::Plugin::Feature feature = features[m_outputNos[n]][fi];
-	    addFeature(blockFrame, feature);
-	}
+        for (int j = 0; j < (int)m_outputNos.size(); ++j) {
+            for (size_t fi = 0; fi < features[m_outputNos[j]].size(); ++fi) {
+                Vamp::Plugin::Feature feature = features[m_outputNos[j]][fi];
+                addFeature(j, blockFrame, feature);
+            }
+        }
 
 	if (blockFrame == contextStart || completion > prevCompletion) {
-	    setCompletion(completion);
+            for (int j = 0; j < (int)m_outputNos.size(); ++j) {
+                setCompletion(j, completion);
+            }
 	    prevCompletion = completion;
 	}
 
@@ -620,13 +633,17 @@
     if (!m_abandoned) {
         Vamp::Plugin::FeatureSet features = m_plugin->getRemainingFeatures();
 
-        for (size_t fi = 0; fi < features[m_outputNos[n]].size(); ++fi) {
-            Vamp::Plugin::Feature feature = features[m_outputNos[n]][fi];
-            addFeature(blockFrame, feature);
+        for (int j = 0; j < (int)m_outputNos.size(); ++j) {
+            for (size_t fi = 0; fi < features[m_outputNos[j]].size(); ++fi) {
+                Vamp::Plugin::Feature feature = features[m_outputNos[j]][fi];
+                addFeature(j, blockFrame, feature);
+            }
         }
     }
 
-    setCompletion(100);
+    for (int j = 0; j < (int)m_outputNos.size(); ++j) {
+        setCompletion(j, 100);
+    }
 
     if (frequencyDomain) {
         for (size_t ch = 0; ch < channelCount; ++ch) {
@@ -698,8 +715,9 @@
 }
 
 void
-FeatureExtractionModelTransformer::addFeature(size_t blockFrame,
-					     const Vamp::Plugin::Feature &feature)
+FeatureExtractionModelTransformer::addFeature(int n,
+                                              size_t blockFrame,
+                                              const Vamp::Plugin::Feature &feature)
 {
     size_t inputRate = m_input.getModel()->getSampleRate();
 
@@ -749,8 +767,6 @@
     // to, we instead test what sort of model the constructor decided
     // to create.
 
-    //!!! currently hardcoding model 0
-
     if (isOutput<SparseOneDimensionalModel>(n)) {
 
         SparseOneDimensionalModel *model =
@@ -872,7 +888,7 @@
 }
 
 void
-FeatureExtractionModelTransformer::setCompletion(int completion)
+FeatureExtractionModelTransformer::setCompletion(int n, int completion)
 {
     int binCount = 1;
     if (m_descriptors[n]->hasFixedBinCount) {
--- a/transform/FeatureExtractionModelTransformer.h	Mon Dec 02 11:17:24 2013 +0000
+++ b/transform/FeatureExtractionModelTransformer.h	Wed Dec 04 18:29:15 2013 +0000
@@ -63,10 +63,11 @@
 
     void createOutputModel(int n);
 
-    void addFeature(size_t blockFrame,
+    void addFeature(int n,
+                    size_t blockFrame,
 		    const Vamp::Plugin::Feature &feature);
 
-    void setCompletion(int);
+    void setCompletion(int, int);
 
     void getFrames(int channelCount, long startFrame, long size,
                    float **buffer);
--- a/transform/ModelTransformerFactory.cpp	Mon Dec 02 11:17:24 2013 +0000
+++ b/transform/ModelTransformerFactory.cpp	Wed Dec 04 18:29:15 2013 +0000
@@ -35,6 +35,8 @@
 
 #include <QRegExp>
 
+using std::vector;
+
 ModelTransformerFactory *
 ModelTransformerFactory::m_instance = new ModelTransformerFactory;
 
@@ -163,30 +165,30 @@
 }
 
 ModelTransformer *
-ModelTransformerFactory::createTransformer(const Transform &transform,
+ModelTransformerFactory::createTransformer(const Transforms &transforms,
                                            const ModelTransformer::Input &input)
 {
     ModelTransformer *transformer = 0;
 
-    QString id = transform.getPluginIdentifier();
+    QString id = transforms[0].getPluginIdentifier();
 
     if (FeatureExtractionPluginFactory::instanceFor(id)) {
 
         transformer =
-            new FeatureExtractionModelTransformer(input, transform);
+            new FeatureExtractionModelTransformer(input, transforms, FeatureExtractionModelTransformer::FlexiNoteOutputModel); //!!! gross
 
     } else if (RealTimePluginFactory::instanceFor(id)) {
 
         transformer =
-            new RealTimeEffectModelTransformer(input, transform);
+            new RealTimeEffectModelTransformer(input, transforms[0]);
 
     } else {
         SVDEBUG << "ModelTransformerFactory::createTransformer: Unknown transform \""
-                  << transform.getIdentifier() << "\"" << endl;
+                  << transforms[0].getIdentifier() << "\"" << endl;
         return transformer;
     }
 
-    if (transformer) transformer->setObjectName(transform.getIdentifier());
+    if (transformer) transformer->setObjectName(transforms[0].getIdentifier());
     return transformer;
 }
 
@@ -197,37 +199,11 @@
 {
     SVDEBUG << "ModelTransformerFactory::transform: Constructing transformer with input model " << input.getModel() << endl;
 
-    ModelTransformer *t = createTransformer(transform, input);
-    if (!t) return 0;
-
-    connect(t, SIGNAL(finished()), this, SLOT(transformerFinished()));
-
-    m_runningTransformers.insert(t);
-
-    t->start();
-    Model *model = t->detachOutputModel();
-
-    if (model) {
-        QString imn = input.getModel()->objectName();
-        QString trn =
-            TransformFactory::getInstance()->getTransformFriendlyName
-            (transform.getIdentifier());
-        if (imn != "") {
-            if (trn != "") {
-                model->setObjectName(tr("%1: %2").arg(imn).arg(trn));
-            } else {
-                model->setObjectName(imn);
-            }
-        } else if (trn != "") {
-            model->setObjectName(trn);
-        }
-    } else {
-        t->wait();
-    }
-
-    message = t->getMessage();
-
-    return model;
+    Transforms transforms;
+    transforms.push_back(transform);
+    vector<Model *> mm = transformMultiple(transforms, input, message);
+    if (mm.empty()) return 0;
+    else return mm[0];
 }
 
 vector<Model *>
@@ -238,28 +214,30 @@
     SVDEBUG << "ModelTransformerFactory::transformMultiple: Constructing transformer with input model " << input.getModel() << endl;
     
     ModelTransformer *t = createTransformer(transforms, input);
-    if (!t) return 0;
+    if (!t) return vector<Model *>();
 
     connect(t, SIGNAL(finished()), this, SLOT(transformerFinished()));
 
     m_runningTransformers.insert(t);
 
     t->start();
-    Model *model = t->detachOutputModel();
+    vector<Model *> models = t->detachOutputModels();
 
-    if (model) {
+    if (!models.empty()) {
         QString imn = input.getModel()->objectName();
         QString trn =
             TransformFactory::getInstance()->getTransformFriendlyName
-            (transform.getIdentifier());
-        if (imn != "") {
-            if (trn != "") {
-                model->setObjectName(tr("%1: %2").arg(imn).arg(trn));
-            } else {
-                model->setObjectName(imn);
+            (transforms[0].getIdentifier());
+        for (int i = 0; i < models.size(); ++i) {
+            if (imn != "") {
+                if (trn != "") {
+                    models[i]->setObjectName(tr("%1: %2").arg(imn).arg(trn));
+                } else {
+                    models[i]->setObjectName(imn);
+                }
+            } else if (trn != "") {
+                models[i]->setObjectName(trn);
             }
-        } else if (trn != "") {
-            model->setObjectName(trn);
         }
     } else {
         t->wait();
@@ -267,7 +245,7 @@
 
     message = t->getMessage();
 
-    return model;
+    return models;
 }
 
 void
@@ -306,8 +284,13 @@
 
         ModelTransformer *t = *i;
 
-        if (t->getInputModel() == m || t->getOutputModel() == m) {
+        if (t->getInputModel() == m) {
             affected.insert(t);
+        } else {
+            vector<Model *> mm = t->getOutputModels();
+            for (int i = 0; i < (int)mm.size(); ++i) {
+                if (mm[i] == m) affected.insert(t);
+            }
         }
     }
 
--- a/transform/ModelTransformerFactory.h	Mon Dec 02 11:17:24 2013 +0000
+++ b/transform/ModelTransformerFactory.h	Wed Dec 04 18:29:15 2013 +0000
@@ -119,7 +119,7 @@
     void modelAboutToBeDeleted(Model *);
 
 protected:
-    ModelTransformer *createTransformer(const Transform &transform,
+    ModelTransformer *createTransformer(const Transforms &transforms,
                                         const ModelTransformer::Input &input);
 
     typedef std::map<TransformId, QString> TransformerConfigurationMap;
--- a/transform/RealTimeEffectModelTransformer.cpp	Mon Dec 02 11:17:24 2013 +0000
+++ b/transform/RealTimeEffectModelTransformer.cpp	Wed Dec 04 18:29:15 2013 +0000
@@ -30,10 +30,16 @@
 #include <iostream>
 
 RealTimeEffectModelTransformer::RealTimeEffectModelTransformer(Input in,
-                                                               const Transform &transform) :
-    ModelTransformer(in, transform),
+                                                               const Transform &t) :
+    ModelTransformer(in, t),
     m_plugin(0)
 {
+    Transform transform(t);
+    if (!transform.getBlockSize()) {
+        transform.setBlockSize(1024);
+        m_transforms[0] = transform;
+    }
+
     m_units = TransformFactory::getInstance()->getTransformUnits
         (transform.getIdentifier());
     m_outputNo =
@@ -41,8 +47,6 @@
 
     QString pluginId = transform.getPluginIdentifier();
 
-    if (!m_transform.getBlockSize()) m_transform.setBlockSize(1024);
-
 //    SVDEBUG << "RealTimeEffectModelTransformer::RealTimeEffectModelTransformer: plugin " << pluginId << ", output " << output << endl;
 
     RealTimePluginFactory *factory =
@@ -59,16 +63,16 @@
 
     m_plugin = factory->instantiatePlugin(pluginId, 0, 0,
                                           input->getSampleRate(),
-                                          m_transform.getBlockSize(),
+                                          transform.getBlockSize(),
                                           input->getChannelCount());
 
     if (!m_plugin) {
 	cerr << "RealTimeEffectModelTransformer: Failed to instantiate plugin \""
-		  << pluginId << "\"" << endl;
+             << pluginId << "\"" << endl;
 	return;
     }
 
-    TransformFactory::getInstance()->setPluginParameters(m_transform, m_plugin);
+    TransformFactory::getInstance()->setPluginParameters(transform, m_plugin);
 
     if (m_outputNo >= 0 &&
         m_outputNo >= int(m_plugin->getControlOutputCount())) {
@@ -91,7 +95,7 @@
     } else {
 	
         SparseTimeValueModel *model = new SparseTimeValueModel
-            (input->getSampleRate(), m_transform.getBlockSize(), 0.0, 0.0, false);
+            (input->getSampleRate(), transform.getBlockSize(), 0.0, 0.0, false);
 
         if (m_units != "") model->setScaleUnits(m_units);
 
@@ -143,9 +147,11 @@
 
     long startFrame = m_input.getModel()->getStartFrame();
     long   endFrame = m_input.getModel()->getEndFrame();
+
+    Transform transform = m_transforms[0];
     
-    RealTime contextStartRT = m_transform.getStartTime();
-    RealTime contextDurationRT = m_transform.getDuration();
+    RealTime contextStartRT = transform.getStartTime();
+    RealTime contextDurationRT = transform.getDuration();
 
     long contextStart =
         RealTime::realTime2Frame(contextStartRT, sampleRate);