diff document/SVFileReader.cpp @ 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 5f9fdca0c7d8
children bedc7517b6e8
line wrap: on
line diff
--- 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 += "/>";