Mercurial > hg > sonic-visualiser
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