changeset 55:ca1e3f5657d5

* Simplify maker names in plugin menu * Make sure derived models have a name (based on the transform) * Don't start deriving a model from a derived model until the derived model is ready * Tidy up completion management in writable wave file model * Make writable models save/reload correctly from session file (i.e. regenerating from the original transform) * Same for dense 3d models -- don't save the data, just the transform details * Add a comment describing the SV file format
author Chris Cannam
date Fri, 13 Oct 2006 12:51:05 +0000
parents ec77936c268e
children 4253ad318db5
files document/Document.cpp document/Document.h document/SVFileReader.cpp document/SVFileReader.h main/MainWindow.cpp transform/FeatureExtractionPluginTransform.cpp transform/RealTimePluginTransform.cpp transform/TransformFactory.cpp
diffstat 8 files changed, 281 insertions(+), 70 deletions(-) [+]
line wrap: on
line diff
--- a/document/Document.cpp	Thu Oct 12 14:56:28 2006 +0000
+++ b/document/Document.cpp	Fri Oct 13 12:51:05 2006 +0000
@@ -16,6 +16,8 @@
 #include "Document.h"
 
 #include "data/model/WaveFileModel.h"
+#include "data/model/WritableWaveFileModel.h"
+#include "data/model/DenseThreeDimensionalModel.h"
 #include "layer/Layer.h"
 #include "base/CommandHistory.h"
 #include "base/Command.h"
@@ -164,10 +166,10 @@
                              const PluginTransform::ExecutionContext &context,
                              QString configurationXml)
 {
-    Model *newModel = createModelForTransform(transform, inputModel,
-                                              context, configurationXml);
+    Model *newModel = addDerivedModel(transform, inputModel,
+                                      context, configurationXml);
     if (!newModel) {
-        // error already printed to stderr by createModelForTransform
+        // error already printed to stderr by addDerivedModel
         emit modelGenerationFailed(transform);
         return 0;
     }
@@ -262,10 +264,10 @@
             PluginTransform::ExecutionContext context = m_models[model].context;
 	    
 	    Model *replacementModel =
-                createModelForTransform(transform,
-                                        m_mainModel,
-                                        context,
-                                        m_models[model].configurationXml);
+                addDerivedModel(transform,
+                                m_mainModel,
+                                context,
+                                m_models[model].configurationXml);
 	    
 	    if (!replacementModel) {
 		std::cerr << "WARNING: Document::setMainModel: Failed to regenerate model for transform \""
@@ -353,10 +355,10 @@
 }
 
 Model *
-Document::createModelForTransform(TransformName transform,
-                                  Model *inputModel,
-                                  const PluginTransform::ExecutionContext &context,
-                                  QString configurationXml)
+Document::addDerivedModel(TransformName transform,
+                          Model *inputModel,
+                          const PluginTransform::ExecutionContext &context,
+                          QString configurationXml)
 {
     Model *model = 0;
 
@@ -373,7 +375,7 @@
 	(transform, inputModel, context, configurationXml);
 
     if (!model) {
-	std::cerr << "WARNING: Document::createModelForTransform: no output model for transform " << transform.toStdString() << std::endl;
+	std::cerr << "WARNING: Document::addDerivedModel: no output model for transform " << transform.toStdString() << std::endl;
     } else {
 	addDerivedModel(transform, inputModel, context, model, configurationXml);
     }
@@ -494,7 +496,12 @@
     Model *previousModel = layer->getModel();
 
     if (previousModel == model) {
-        std::cerr << "WARNING: Document::setModel: Layer is already set to this model" << std::endl;
+        std::cerr << "WARNING: Document::setModel: Layer " << layer << " (\""
+                  << layer->objectName().toStdString()
+                  << "\") is already set to model "
+                  << model << " (\""
+                  << (model ? model->objectName().toStdString() : "(null)")
+                  << "\")" << std::endl;
         return;
     }
 
@@ -520,7 +527,10 @@
 {
     Model *model = layer->getModel();
     if (!model) {
-	std::cerr << "Document::addLayerToView: Layer with no model being added to view: normally you want to set the model first" << std::endl;
+	std::cerr << "Document::addLayerToView: Layer (\""
+                  << layer->objectName().toStdString()
+                  << "\") with no model being added to view: "
+                  << "normally you want to set the model first" << std::endl;
     } else {
 	if (model != m_mainModel &&
 	    m_models.find(model) == m_models.end()) {
@@ -714,11 +724,42 @@
     for (ModelMap::const_iterator i = m_models.begin();
 	 i != m_models.end(); ++i) {
 
-	i->first->toXml(out, indent + "  ");
-	
+        const Model *model = i->first;
 	const ModelRecord &rec = i->second;
 
-	if (rec.source && rec.transform != "") {
+        // We need an intelligent way to determine which models need
+        // to be streamed (i.e. have been edited, or are small) and
+        // which should not be (i.e. remain as generated by a
+        // transform, and are large).
+        //
+        // At the moment we can get away with deciding not to stream
+        // dense 3d models or writable wave file models, provided they
+        // were generated from a transform, because at the moment there
+        // is no way to edit those model types so it should be safe to
+        // regenerate them.  That won't always work in future though.
+        // It would be particularly nice to be able to ask the user,
+        // as well as making an intelligent guess.
+
+        bool writeModel = true;
+        bool haveDerivation = false;
+
+        if (rec.source && rec.transform != "") {
+            haveDerivation = true;
+        } 
+
+        if (haveDerivation) {
+            if (dynamic_cast<const WritableWaveFileModel *>(model)) {
+                writeModel = false;
+            } else if (dynamic_cast<const DenseThreeDimensionalModel *>(model)) {
+                writeModel = false;
+            }
+        }
+
+        if (writeModel) {
+            i->first->toXml(out, indent + "  ");
+        }
+
+	if (haveDerivation) {
 	    
             //!!! stream the rest of the execution context in both directions (i.e. not just channel)
 
--- a/document/Document.h	Thu Oct 12 14:56:28 2006 +0000
+++ b/document/Document.h	Fri Oct 13 12:51:05 2006 +0000
@@ -130,10 +130,18 @@
     WaveFileModel *getMainModel() { return m_mainModel; }
 
     /**
-     * Add a derived model associated with the given transform name.
-     * This is necessary to register any derived model that was not
-     * created by the document using
-     * e.g. createDerivedLayer(TransformName) above.
+     * Add a derived model associated with the given transform,
+     * running the transform and returning the resulting model.
+     */
+    Model *addDerivedModel(TransformName transform,
+                           Model *inputModel,
+                           const PluginTransform::ExecutionContext &context,
+                           QString configurationXml);
+
+    /**
+     * Add a derived model associated with the given transform.  This
+     * is necessary to register any derived model that was not created
+     * by the document using createDerivedModel or createDerivedLayer.
      */
     void addDerivedModel(TransformName,
                          Model *inputModel,
@@ -193,10 +201,6 @@
     void modelRegenerationFailed(QString layerName, QString transformName);
 
 protected:
-    Model *createModelForTransform(TransformName transform,
-                                   Model *inputModel,
-                                   const PluginTransform::ExecutionContext &context,
-                                   QString configurationXml);
     void releaseModel(Model *model);
 
     /**
--- a/document/SVFileReader.cpp	Thu Oct 12 14:56:28 2006 +0000
+++ b/document/SVFileReader.cpp	Fri Oct 13 12:51:05 2006 +0000
@@ -39,6 +39,113 @@
 
 #include <iostream>
 
+/*
+    Some notes about the SV XML format.  We're very lazy with our XML:
+    there's no schema or DTD, and we depend heavily on elements being
+    in a particular order.
+ 
+    <sv>
+
+    <data>
+
+      <!-- The data section contains definitions of both models and
+           visual layers.  Layers are considered data in the document;
+           the structure of views that displays the layers is not. -->
+
+      <!-- id numbers are unique within the data type (i.e. no two
+           models can have the same id, but a model can have the same
+           id as a layer, etc).  SV generates its id numbers just for
+           the purpose of cross-referencing within the current file;
+           they don't necessarily have any meaning once the file has
+           been loaded. -->
+
+      <model id="0" name="..." type="..." ... />
+      <model id="1" name="..." type="..." ... />
+
+      <!-- Models that have data associated with them store it
+           in a neighbouring dataset element.  The dataset must follow
+           the model and precede any derivation or layer elements that
+           refer to the model. -->
+
+      <model id="2" name="..." type="..." dataset="0" ... />
+
+      <dataset id="0" type="..."> 
+        <point frame="..." value="..." ... />
+      </dataset>
+
+      <!-- Where one model is derived from another via a transform,
+           it has an associated derivation element.  This must follow
+           both the source and target model elements.  The source and
+           model attributes give the source model id and target model
+           id respectively.  A model can have both dataset and
+           derivation elements; if it does, dataset must appear first. 
+           If the model's data are not stored, but instead the model
+           is to be regenerated completely from the transform when 
+           the session is reloaded, then the model should have _only_
+           a derivation element, and no model element should appear
+           for it at all. -->
+
+      <derivation source="0" model="2" transform="..." ...>
+        <plugin id="..." ... />
+      </derivation>
+
+      <!-- The playparameters element lists playback settings for
+           a model. -->
+
+      <playparameters mute="false" pan="0" gain="1" model="1" ... />
+
+      <!-- Layer elements.  The models must have already been defined.
+           The same model may appear in more than one layer (of more
+           than one type). -->
+
+      <layer id="1" type="..." name="..." model="0" ... />
+      <layer id="2" type="..." name="..." model="1" ... />
+
+    </data>
+
+
+    <display>
+
+      <!-- The display element contains visual structure for the
+           layers.  It's simpler than the data section. -->
+
+      <!-- Overall preferred window size for this session. -->
+
+      <window width="..." height="..."/>
+
+      <!-- List of view elements to stack up.  Each one contains
+           a list of layers in stacking order, back to front. -->
+
+      <view type="pane" ...>
+        <layer id="1"/>
+        <layer id="2"/>
+      </view>
+
+      <!-- The layer elements just refer to layers defined in the
+           data section, so they don't have to have any attributes
+           other than the id.  For sort-of-historical reasons SV
+           actually does repeat the other attributes here, but
+           it doesn't need to. -->
+
+      <view type="pane" ...>
+        <layer id="2"/>
+      <view>
+
+    </display>
+
+
+    <!-- List of selected regions by audio frame extents. -->
+
+    <selections>
+      <selection start="..." end="..."/>
+    </selections>
+
+
+    </sv>
+ 
+ */
+
+
 SVFileReader::SVFileReader(Document *document,
 			   SVFileReaderPaneCallback &callback) :
     m_document(document),
@@ -46,6 +153,7 @@
     m_currentPane(0),
     m_currentDataset(0),
     m_currentDerivedModel(0),
+    m_currentDerivedModelId(-1),
     m_currentPlayParameters(0),
     m_datasetSeparator(" "),
     m_inRow(false),
@@ -258,19 +366,36 @@
 	m_inData = false;
 
     } else if (name == "derivation") {
-        
-        if (m_currentDerivedModel) {
+
+        if (!m_currentDerivedModel) {
+            if (m_currentDerivedModel < 0) {
+                std::cerr << "WARNING: SV-XML: Bad derivation output model id "
+                          << m_currentDerivedModelId << std::endl;
+            } else if (m_models[m_currentDerivedModelId]) {
+                std::cerr << "WARNING: SV-XML: Derivation has existing model "
+                          << m_currentDerivedModelId
+                          << " as target, not regenerating" << std::endl;
+            } else {
+                m_currentDerivedModel = m_models[m_currentDerivedModelId] =
+                    m_document->addDerivedModel(m_currentTransform,
+                                                m_currentTransformSource,
+                                                m_currentTransformContext,
+                                                m_currentTransformConfiguration);
+            }
+        } else {
             m_document->addDerivedModel(m_currentTransform,
-                                        m_document->getMainModel(), //!!!
+                                        m_currentTransformSource,
                                         m_currentTransformContext,
                                         m_currentDerivedModel,
                                         m_currentTransformConfiguration);
-            m_addedModels.insert(m_currentDerivedModel);
-            m_currentDerivedModel = 0;
-            m_currentTransform = "";
-            m_currentTransformConfiguration = "";
         }
 
+        m_addedModels.insert(m_currentDerivedModel);
+        m_currentDerivedModel = 0;
+        m_currentDerivedModelId = -1;
+        m_currentTransform = "";
+        m_currentTransformConfiguration = "";
+
     } else if (name == "row") {
 	m_inRow = false;
     } else if (name == "view") {
@@ -366,7 +491,7 @@
 
     QString type = attributes.value("type").trimmed();
     bool mainModel = (attributes.value("mainModel").trimmed() == "true");
-    
+
     if (type == "wavefile") {
 	
 	QString file = attributes.value("file");
@@ -412,11 +537,10 @@
 	
 	READ_MANDATORY(int, dimensions, toInt);
 		    
-	// Currently the only dense model we support here
-	// is the dense 3d model.  Dense time-value models
-	// are always file-backed waveform data, at this
-	// point, and they come in as the wavefile model
-	// type above.
+	// Currently the only dense model we support here is the dense
+	// 3d model.  Dense time-value models are always file-backed
+	// waveform data, at this point, and they come in as wavefile
+	// models.
 	
 	if (dimensions == 3) {
 	    
@@ -886,39 +1010,48 @@
 	std::cerr << "WARNING: SV-XML: No model id specified for derivation" << std::endl;
 	return false;
     }
+
+    QString transform = attributes.value("transform");
+
+    if (m_models.find(modelId) != m_models.end()) {
+        m_currentDerivedModel = m_models[modelId];
+    } else {
+        // we'll regenerate the model when the derivation element ends
+        m_currentDerivedModel = 0;
+    }
     
-    QString transform = attributes.value("transform");
+    m_currentDerivedModelId = modelId;
     
-    if (m_models.find(modelId) != m_models.end()) {
+    int sourceId = 0;
+    bool sourceOk = false;
+    sourceId = attributes.value("source").trimmed().toInt(&sourceOk);
 
-        m_currentDerivedModel = m_models[modelId];
-        m_currentTransform = transform;
-        m_currentTransformConfiguration = "";
+    if (sourceOk && m_models[sourceId]) {
+        m_currentTransformSource = m_models[sourceId];
+    } else {
+        m_currentTransformSource = m_document->getMainModel();
+    }
 
-        m_currentTransformContext = PluginTransform::ExecutionContext();
+    m_currentTransform = transform;
+    m_currentTransformConfiguration = "";
 
-        bool ok = false;
-        int channel = attributes.value("channel").trimmed().toInt(&ok);
-        if (ok) m_currentTransformContext.channel = channel;
+    m_currentTransformContext = PluginTransform::ExecutionContext();
 
-        int domain = attributes.value("domain").trimmed().toInt(&ok);
-        if (ok) m_currentTransformContext.domain = Vamp::Plugin::InputDomain(domain);
+    bool ok = false;
+    int channel = attributes.value("channel").trimmed().toInt(&ok);
+    if (ok) m_currentTransformContext.channel = channel;
 
-        int stepSize = attributes.value("stepSize").trimmed().toInt(&ok);
-        if (ok) m_currentTransformContext.stepSize = stepSize;
+    int domain = attributes.value("domain").trimmed().toInt(&ok);
+    if (ok) m_currentTransformContext.domain = Vamp::Plugin::InputDomain(domain);
 
-        int blockSize = attributes.value("blockSize").trimmed().toInt(&ok);
-        if (ok) m_currentTransformContext.blockSize = blockSize;
+    int stepSize = attributes.value("stepSize").trimmed().toInt(&ok);
+    if (ok) m_currentTransformContext.stepSize = stepSize;
 
-        int windowType = attributes.value("windowType").trimmed().toInt(&ok);
-        if (ok) m_currentTransformContext.windowType = WindowType(windowType);
+    int blockSize = attributes.value("blockSize").trimmed().toInt(&ok);
+    if (ok) m_currentTransformContext.blockSize = blockSize;
 
-    } else {
-	std::cerr << "WARNING: SV-XML: Unknown derived model " << modelId
-		  << " for transform \"" << transform.toLocal8Bit().data() << "\""
-		  << std::endl;
-        return false;
-    }
+    int windowType = attributes.value("windowType").trimmed().toInt(&ok);
+    if (ok) m_currentTransformContext.windowType = WindowType(windowType);
 
     return true;
 }
@@ -981,7 +1114,7 @@
 bool
 SVFileReader::readPlugin(const QXmlAttributes &attributes)
 {
-    if (!m_currentDerivedModel && !m_currentPlayParameters) {
+    if (m_currentDerivedModelId < 0 && !m_currentPlayParameters) {
         std::cerr << "WARNING: SV-XML: Plugin found outside derivation or play parameters" << std::endl;
         return false;
     }
@@ -990,7 +1123,8 @@
     
     for (int i = 0; i < attributes.length(); ++i) {
         configurationXml += QString(" %1=\"%2\"")
-            .arg(attributes.qName(i)).arg(attributes.value(i));
+            .arg(attributes.qName(i))
+            .arg(XmlExportable::encodeEntities(attributes.value(i)));
     }
 
     configurationXml += "/>";
--- a/document/SVFileReader.h	Thu Oct 12 14:56:28 2006 +0000
+++ b/document/SVFileReader.h	Fri Oct 13 12:51:05 2006 +0000
@@ -92,8 +92,10 @@
     std::map<int, int> m_awaitingDatasets; // map dataset id -> model id
     Model *m_currentDataset;
     Model *m_currentDerivedModel;
+    int m_currentDerivedModelId;
     PlayParameters *m_currentPlayParameters;
     QString m_currentTransform;
+    Model *m_currentTransformSource;
     PluginTransform::ExecutionContext m_currentTransformContext;
     QString m_currentTransformConfiguration;
     QString m_datasetSeparator;
--- a/main/MainWindow.cpp	Thu Oct 12 14:56:28 2006 +0000
+++ b/main/MainWindow.cpp	Fri Oct 13 12:51:05 2006 +0000
@@ -80,6 +80,7 @@
 #include <QDateTime>
 #include <QProcess>
 #include <QCheckBox>
+#include <QRegExp>
 
 #include <iostream>
 #include <cstdio>
@@ -688,7 +689,8 @@
 
             QString maker = *j;
             if (maker == "") maker = tr("Unknown");
-            
+            maker.replace(QRegExp(tr(" [\\(<].*$")), "");
+
             makerMenus[*i][maker] = new SubdividingMenu(maker, 30, 40);
             byMakerMenu->addMenu(makerMenus[*i][maker]);
             pendingMenus.insert(makerMenus[*i][maker]);
@@ -707,6 +709,7 @@
 
         QString maker = transforms[i].maker;
         if (maker == "") maker = tr("Unknown");
+        maker.replace(QRegExp(tr(" [\\(<].*$")), "");
 
         QString pluginName = description.section(": ", 0, 0);
         QString output = description.section(": ", 1);
--- a/transform/FeatureExtractionPluginTransform.cpp	Thu Oct 12 14:56:28 2006 +0000
+++ b/transform/FeatureExtractionPluginTransform.cpp	Fri Oct 13 12:51:05 2006 +0000
@@ -27,6 +27,7 @@
 #include "data/model/DenseTimeValueModel.h"
 #include "data/model/NoteModel.h"
 #include "data/model/FFTModel.h"
+#include "data/model/WaveFileModel.h"
 
 #include <fftw3.h>
 
@@ -224,6 +225,12 @@
     DenseTimeValueModel *input = getInput();
     if (!input) return;
 
+    while (!input->isReady()) {
+        if (dynamic_cast<WaveFileModel *>(input)) break; // no need to wait
+        std::cerr << "FeatureExtractionPluginTransform::run: Waiting for input model to be ready..." << std::endl;
+        sleep(1);
+    }
+
     if (!m_output) return;
 
     size_t sampleRate = m_input->getSampleRate();
--- a/transform/RealTimePluginTransform.cpp	Thu Oct 12 14:56:28 2006 +0000
+++ b/transform/RealTimePluginTransform.cpp	Fri Oct 13 12:51:05 2006 +0000
@@ -24,6 +24,7 @@
 #include "data/model/SparseTimeValueModel.h"
 #include "data/model/DenseTimeValueModel.h"
 #include "data/model/WritableWaveFileModel.h"
+#include "data/model/WaveFileModel.h"
 
 #include <iostream>
 
@@ -118,6 +119,12 @@
     DenseTimeValueModel *input = getInput();
     if (!input) return;
 
+    while (!input->isReady()) {
+        if (dynamic_cast<WaveFileModel *>(input)) break; // no need to wait
+        std::cerr << "FeatureExtractionPluginTransform::run: Waiting for input model to be ready..." << std::endl;
+        sleep(1);
+    }
+
     SparseTimeValueModel *stvm = dynamic_cast<SparseTimeValueModel *>(m_output);
     WritableWaveFileModel *wwfm = dynamic_cast<WritableWaveFileModel *>(m_output);
     if (!stvm && !wwfm) return;
@@ -195,10 +202,6 @@
 
             if (buffers) {
 
-                //!!! This will fail if any buffers[c] is null or
-                //uninitialised.  The plugin instance should ensure
-                //that that can't happen -- but it doesn't
-
                 if (blockFrame >= latency) {
                     wwfm->addSamples(buffers, blockSize);
                 } else if (blockFrame + blockSize >= latency) {
@@ -216,6 +219,7 @@
 
 	if (blockFrame == startFrame || completion > prevCompletion) {
 	    if (stvm) stvm->setCompletion(completion);
+	    if (wwfm) wwfm->setCompletion(completion);
 	    prevCompletion = completion;
 	}
         
@@ -223,6 +227,6 @@
     }
     
     if (stvm) stvm->setCompletion(100);
-    if (wwfm) wwfm->sync();
+    if (wwfm) wwfm->setCompletion(100);
 }
 
--- a/transform/TransformFactory.cpp	Thu Oct 12 14:56:28 2006 +0000
+++ b/transform/TransformFactory.cpp	Fri Oct 13 12:51:05 2006 +0000
@@ -664,7 +664,23 @@
     connect(t, SIGNAL(finished()), this, SLOT(transformFinished()));
 
     t->start();
-    return t->detachOutputModel();
+    Model *model = t->detachOutputModel();
+
+    if (model) {
+        QString imn = inputModel->objectName();
+        QString trn = getTransformFriendlyName(name);
+        if (imn != "") {
+            if (trn != "") {
+                model->setObjectName(tr("%1: %2").arg(imn).arg(trn));
+            } else {
+                model->setObjectName(imn);
+            }
+        } else if (trn != "") {
+            model->setObjectName(trn);
+        }
+    }
+
+    return model;
 }
 
 void