Mercurial > hg > svapp
changeset 666:21673429dba5 single-point
Merge from default branch
author | Chris Cannam |
---|---|
date | Wed, 24 Apr 2019 11:45:31 +0100 |
parents | 06db8f3ceb95 (diff) e19c609a7bec (current diff) |
children | 31ea416fea3c |
files | framework/Align.cpp framework/Document.cpp framework/SVFileReader.cpp |
diffstat | 9 files changed, 601 insertions(+), 301 deletions(-) [+] |
line wrap: on
line diff
--- a/audio/AudioGenerator.cpp Thu Apr 04 16:17:11 2019 +0100 +++ b/audio/AudioGenerator.cpp Wed Apr 24 11:45:31 2019 +0100 @@ -22,11 +22,10 @@ #include "base/Exceptions.h" #include "data/model/NoteModel.h" -#include "data/model/FlexiNoteModel.h" #include "data/model/DenseTimeValueModel.h" #include "data/model/SparseTimeValueModel.h" #include "data/model/SparseOneDimensionalModel.h" -#include "data/model/NoteData.h" +#include "base/NoteData.h" #include "ClipMixer.h" #include "ContinuousSynth.h" @@ -185,8 +184,7 @@ { bool clip = (qobject_cast<const SparseOneDimensionalModel *>(model) || - qobject_cast<const NoteModel *>(model) || - qobject_cast<const FlexiNoteModel *>(model)); + qobject_cast<const NoteModel *>(model)); return clip; } @@ -196,9 +194,7 @@ // basically, anything that usually has sustain (like notes) or // often has multiple sounds at once (like notes) wants to use a // quieter level than simple click tracks - bool does = - (qobject_cast<const NoteModel *>(model) || - qobject_cast<const FlexiNoteModel *>(model)); + bool does = (qobject_cast<const NoteModel *>(model)); return does; } @@ -559,6 +555,8 @@ float **bufferIndexes = new float *[m_targetChannelCount]; + //!!! + for first block, prime with notes already active + for (int i = 0; i < blocks; ++i) { sv_frame_t reqStart = startFrame + i * m_processingBlockSize; @@ -566,8 +564,8 @@ NoteList notes; NoteExportable *exportable = dynamic_cast<NoteExportable *>(model); if (exportable) { - notes = exportable->getNotesWithin(reqStart, - reqStart + m_processingBlockSize); + notes = exportable->getNotesStartingWithin(reqStart, + m_processingBlockSize); } std::vector<ClipMixer::NoteStart> starts; @@ -714,32 +712,28 @@ bufferIndexes[c] = buffer[c] + i * m_processingBlockSize; } - SparseTimeValueModel::PointList points = - stvm->getPoints(reqStart, reqStart + m_processingBlockSize); + EventVector points = + stvm->getEventsStartingWithin(reqStart, m_processingBlockSize); // by default, repeat last frequency float f0 = 0.f; - // go straight to the last freq that is genuinely in this range - for (SparseTimeValueModel::PointList::const_iterator itr = points.end(); - itr != points.begin(); ) { - --itr; - if (itr->frame >= reqStart && - itr->frame < reqStart + m_processingBlockSize) { - f0 = itr->value; - break; - } + // go straight to the last freq in this range + if (!points.empty()) { + f0 = points.rbegin()->getValue(); } - // if we found no such frequency and the next point is further + // if there is no such frequency and the next point is further // away than twice the model resolution, go silent (same // criterion TimeValueLayer uses for ending a discrete curve // segment) if (f0 == 0.f) { - SparseTimeValueModel::PointList nextPoints = - stvm->getNextPoints(reqStart + m_processingBlockSize); - if (nextPoints.empty() || - nextPoints.begin()->frame > reqStart + 2 * stvm->getResolution()) { + Event nextP; + if (!stvm->getNearestEventMatching(reqStart + m_processingBlockSize, + [](Event) { return true; }, + EventSeries::Forward, + nextP) || + nextP.getFrame() > reqStart + 2 * stvm->getResolution()) { f0 = -1.f; } }
--- a/files.pri Thu Apr 04 16:17:11 2019 +0100 +++ b/files.pri Wed Apr 24 11:45:31 2019 +0100 @@ -9,6 +9,7 @@ framework/Align.h \ framework/Document.h \ framework/MainWindowBase.h \ + framework/OSCScript.h \ framework/SVFileReader.h \ framework/TransformUserConfigurator.h \ framework/VersionTester.h
--- a/framework/Align.cpp Thu Apr 04 16:17:11 2019 +0100 +++ b/framework/Align.cpp Wed Apr 24 11:45:31 2019 +0100 @@ -139,7 +139,7 @@ (transformOutput); if (!path) { - cerr << "Align::alignModel: ERROR: Failed to create alignment path (no MATCH plugin?)" << endl; + SVCERR << "Align::alignModel: ERROR: Failed to create alignment path (no MATCH plugin?)" << endl; delete transformOutput; delete aggregateModel; m_error = message; @@ -192,7 +192,7 @@ ReadOnlyWaveFileModel *roref = qobject_cast<ReadOnlyWaveFileModel *>(reference); ReadOnlyWaveFileModel *rorm = qobject_cast<ReadOnlyWaveFileModel *>(rm); if (!roref || !rorm) { - cerr << "ERROR: Align::alignModelViaProgram: Can't align non-read-only models via program (no local filename available)" << endl; + SVCERR << "ERROR: Align::alignModelViaProgram: Can't align non-read-only models via program (no local filename available)" << endl; return false; } @@ -223,8 +223,8 @@ bool success = process->waitForStarted(); if (!success) { - cerr << "ERROR: Align::alignModelViaProgram: Program did not start" - << endl; + SVCERR << "ERROR: Align::alignModelViaProgram: Program did not start" + << endl; m_error = "Alignment program could not be started"; m_processModels.erase(process); rm->setAlignment(nullptr); // deletes alignmentModel as well @@ -237,13 +237,13 @@ void Align::alignmentProgramFinished(int exitCode, QProcess::ExitStatus status) { - cerr << "Align::alignmentProgramFinished" << endl; + SVCERR << "Align::alignmentProgramFinished" << endl; QProcess *process = qobject_cast<QProcess *>(sender()); if (m_processModels.find(process) == m_processModels.end()) { - cerr << "ERROR: Align::alignmentProgramFinished: Process " << process - << " not found in process model map!" << endl; + SVCERR << "ERROR: Align::alignmentProgramFinished: Process " << process + << " not found in process model map!" << endl; return; } @@ -269,8 +269,8 @@ CSVFileReader reader(process, format, alignmentModel->getSampleRate()); if (!reader.isOK()) { - cerr << "ERROR: Align::alignmentProgramFinished: Failed to parse output" - << endl; + SVCERR << "ERROR: Align::alignmentProgramFinished: Failed to parse output" + << endl; m_error = QString("Failed to parse output of program: %1") .arg(reader.getError()); goto done; @@ -280,30 +280,30 @@ SparseTimeValueModel *path = qobject_cast<SparseTimeValueModel *>(csvOutput); if (!path) { - cerr << "ERROR: Align::alignmentProgramFinished: Output did not convert to sparse time-value model" - << endl; + SVCERR << "ERROR: Align::alignmentProgramFinished: Output did not convert to sparse time-value model" + << endl; m_error = QString("Output of program did not produce sparse time-value model"); goto done; } - if (path->getPoints().empty()) { - cerr << "ERROR: Align::alignmentProgramFinished: Output contained no mappings" - << endl; + if (path->isEmpty()) { + SVCERR << "ERROR: Align::alignmentProgramFinished: Output contained no mappings" + << endl; m_error = QString("Output of alignment program contained no mappings"); goto done; } - cerr << "Align::alignmentProgramFinished: Setting alignment path (" - << path->getPoints().size() << " point(s))" << endl; - + SVCERR << "Align::alignmentProgramFinished: Setting alignment path (" + << path->getEventCount() << " point(s))" << endl; + alignmentModel->setPathFrom(path); emit alignmentComplete(alignmentModel); } else { - cerr << "ERROR: Align::alignmentProgramFinished: Aligner program " - << "failed: exit code " << exitCode << ", status " << status - << endl; + SVCERR << "ERROR: Align::alignmentProgramFinished: Aligner program " + << "failed: exit code " << exitCode << ", status " << status + << endl; m_error = "Aligner process returned non-zero exit status"; }
--- a/framework/Document.cpp Thu Apr 04 16:17:11 2019 +0100 +++ b/framework/Document.cpp Wed Apr 24 11:45:31 2019 +0100 @@ -21,7 +21,6 @@ #include "data/model/WritableWaveFileModel.h" #include "data/model/DenseThreeDimensionalModel.h" #include "data/model/DenseTimeValueModel.h" -#include "data/model/FlexiNoteModel.h" #include "data/model/AggregateWaveModel.h" #include "layer/Layer.h" @@ -75,7 +74,7 @@ //the document, be nice to fix that #ifdef DEBUG_DOCUMENT - cerr << "\n\nDocument::~Document: about to clear command history" << endl; + SVDEBUG << "\n\nDocument::~Document: about to clear command history" << endl; #endif CommandHistory::getInstance()->clear(); @@ -91,8 +90,8 @@ << m_models.size() << " model(s) still remain -- " << "should have been garbage collected when deleting layers" << endl; - while (!m_models.empty()) { - Model *model = m_models.begin()->first; + for (ModelRecord &rec: m_models) { + Model *model = rec.model; if (model == m_mainModel) { // just in case! SVDEBUG << "Document::~Document: WARNING: Main model is also" @@ -102,8 +101,8 @@ emit modelAboutToBeDeleted(model); delete model; } - m_models.erase(m_models.begin()); } + m_models.clear(); } #ifdef DEBUG_DOCUMENT @@ -127,7 +126,7 @@ newLayer->setObjectName(getUniqueLayerName(newLayer->objectName())); - m_layers.insert(newLayer); + m_layers.push_back(newLayer); #ifdef DEBUG_DOCUMENT SVDEBUG << "Document::createLayer: Added layer of type " << type @@ -155,7 +154,7 @@ LayerFactory::getInstance()->getValidLayerTypes(model); if (types.empty()) { - cerr << "WARNING: Document::importLayer: no valid display layer for model" << endl; + SVCERR << "WARNING: Document::importLayer: no valid display layer for model" << endl; return nullptr; } @@ -173,7 +172,7 @@ //!!! and all channels setChannel(newLayer, -1); - m_layers.insert(newLayer); + m_layers.push_back(newLayer); #ifdef DEBUG_DOCUMENT SVDEBUG << "Document::createImportedLayer: Added layer of type " << type @@ -277,7 +276,7 @@ void moreModelsAvailable(vector<Model *> models) override { - std::cerr << "AdditionalModelConverter::moreModelsAvailable: " << models.size() << " model(s)" << std::endl; + SVDEBUG << "AdditionalModelConverter::moreModelsAvailable: " << models.size() << " model(s)" << endl; // We can't automatically regenerate the additional models on // reload -- we should delete them instead QStringList names; @@ -293,7 +292,7 @@ void noMoreModelsAvailable() override { - std::cerr << "AdditionalModelConverter::noMoreModelsAvailable" << std::endl; + SVDEBUG << "AdditionalModelConverter::noMoreModelsAvailable" << endl; m_handler->layersCreated(this, m_primary, vector<Layer *>()); delete this; } @@ -370,12 +369,9 @@ LayerFactory::getInstance()->getValidLayerTypes(newModel); if (types.empty()) { - cerr << "WARNING: Document::createLayerForTransformer: no valid display layer for output of transform " << names[i] << endl; + SVCERR << "WARNING: Document::createLayerForTransformer: no valid display layer for output of transform " << names[i] << endl; //!!! inadequate cleanup: - newModel->aboutToDelete(); - emit modelAboutToBeDeleted(newModel); - m_models.erase(newModel); - delete newModel; + deleteModelFromList(newModel); return vector<Layer *>(); } @@ -433,62 +429,64 @@ // delete any of the models. #ifdef DEBUG_DOCUMENT - cerr << "Document::setMainModel: Have " + SVDEBUG << "Document::setMainModel: Have " << m_layers.size() << " layers" << endl; - cerr << "Models now: "; - for (ModelMap::const_iterator i = m_models.begin(); i != m_models.end(); ++i) { - cerr << i->first << " "; - } - cerr << endl; - cerr << "Old main model: " << oldMainModel << endl; + SVDEBUG << "Models now: "; + for (const auto &r: m_models) { + SVDEBUG << r.model << " "; + } + SVDEBUG << endl; + SVDEBUG << "Old main model: " << oldMainModel << endl; #endif - for (LayerSet::iterator i = m_layers.begin(); i != m_layers.end(); ++i) { + for (Layer *layer: m_layers) { - Layer *layer = *i; Model *model = layer->getModel(); #ifdef DEBUG_DOCUMENT - cerr << "Document::setMainModel: inspecting model " - << (model ? model->objectName(): "(null)") << " in layer " - << layer->objectName() << endl; + SVDEBUG << "Document::setMainModel: inspecting model " + << (model ? model->objectName(): "(null)") << " in layer " + << layer->objectName() << endl; #endif if (model == oldMainModel) { #ifdef DEBUG_DOCUMENT - cerr << "... it uses the old main model, replacing" << endl; + SVDEBUG << "... it uses the old main model, replacing" << endl; #endif LayerFactory::getInstance()->setModel(layer, m_mainModel); continue; } if (!model) { - cerr << "WARNING: Document::setMainModel: Null model in layer " - << layer << endl; + SVCERR << "WARNING: Document::setMainModel: Null model in layer " + << layer << endl; // get rid of this hideous degenerate obsoleteLayers.push_back(layer); continue; } - if (m_models.find(model) == m_models.end()) { - cerr << "WARNING: Document::setMainModel: Unknown model " - << model << " in layer " << layer << endl; + auto mitr = findModelInList(model); + + if (mitr == m_models.end()) { + SVCERR << "WARNING: Document::setMainModel: Unknown model " + << model << " in layer " << layer << endl; // and this one obsoleteLayers.push_back(layer); continue; } - - if (m_models[model].source && - (m_models[model].source == oldMainModel)) { + + ModelRecord record = *mitr; + + if (record.source && (record.source == oldMainModel)) { #ifdef DEBUG_DOCUMENT - cerr << "... it uses a model derived from the old main model, regenerating" << endl; + SVDEBUG << "... it uses a model derived from the old main model, regenerating" << endl; #endif // This model was derived from the previous main // model: regenerate it. - const Transform &transform = m_models[model].transform; + const Transform &transform = record.transform; QString transformId = transform.getIdentifier(); //!!! We have a problem here if the number of channels in @@ -498,12 +496,12 @@ Model *replacementModel = addDerivedModel(transform, ModelTransformer::Input - (m_mainModel, m_models[model].channel), + (m_mainModel, record.channel), message); if (!replacementModel) { - cerr << "WARNING: Document::setMainModel: Failed to regenerate model for transform \"" - << transformId << "\"" << " in layer " << layer << endl; + SVCERR << "WARNING: Document::setMainModel: Failed to regenerate model for transform \"" + << transformId << "\"" << " in layer " << layer << endl; if (failedTransformers.find(transformId) == failedTransformers.end()) { emit modelRegenerationFailed(layer->objectName(), @@ -519,19 +517,19 @@ message); } #ifdef DEBUG_DOCUMENT - cerr << "Replacing model " << model << " (type " - << typeid(*model).name() << ") with model " - << replacementModel << " (type " - << typeid(*replacementModel).name() << ") in layer " - << layer << " (name " << layer->objectName() << ")" - << endl; + SVDEBUG << "Replacing model " << model << " (type " + << typeid(*model).name() << ") with model " + << replacementModel << " (type " + << typeid(*replacementModel).name() << ") in layer " + << layer << " (name " << layer->objectName() << ")" + << endl; RangeSummarisableTimeValueModel *rm = dynamic_cast<RangeSummarisableTimeValueModel *>(replacementModel); if (rm) { - cerr << "new model has " << rm->getChannelCount() << " channels " << endl; + SVDEBUG << "new model has " << rm->getChannelCount() << " channels " << endl; } else { - cerr << "new model " << replacementModel << " is not a RangeSummarisableTimeValueModel!" << endl; + SVDEBUG << "new model " << replacementModel << " is not a RangeSummarisableTimeValueModel!" << endl; } #endif setModel(layer, replacementModel); @@ -543,18 +541,19 @@ deleteLayer(obsoleteLayers[k], true); } - for (ModelMap::iterator i = m_models.begin(); i != m_models.end(); ++i) { - if (i->second.additional) { - Model *m = i->first; - m->aboutToDelete(); - emit modelAboutToBeDeleted(m); - delete m; + std::set<Model *> additionalModels; + for (const auto &rec : m_models) { + if (rec.additional) { + additionalModels.insert(rec.model); } } + for (Model *a: additionalModels) { + deleteModelFromList(a); + } - for (ModelMap::iterator i = m_models.begin(); i != m_models.end(); ++i) { + for (const auto &rec : m_models) { - Model *m = i->first; + Model *m = rec.model; #ifdef DEBUG_DOCUMENT SVDEBUG << "considering alignment for model " << m << " (name \"" @@ -592,21 +591,22 @@ const ModelTransformer::Input &input, Model *outputModelToAdd) { - if (m_models.find(outputModelToAdd) != m_models.end()) { - cerr << "WARNING: Document::addAlreadyDerivedModel: Model already added" + if (findModelInList(outputModelToAdd) != m_models.end()) { + SVCERR << "WARNING: Document::addAlreadyDerivedModel: Model already added" << endl; return; } #ifdef DEBUG_DOCUMENT if (input.getModel()) { - cerr << "Document::addAlreadyDerivedModel: source is " << input.getModel() << " \"" << input.getModel()->objectName() << "\"" << endl; + SVDEBUG << "Document::addAlreadyDerivedModel: source is " << input.getModel() << " \"" << input.getModel()->objectName() << "\"" << endl; } else { - cerr << "Document::addAlreadyDerivedModel: source is " << input.getModel() << endl; + SVDEBUG << "Document::addAlreadyDerivedModel: source is " << input.getModel() << endl; } #endif ModelRecord rec; + rec.model = outputModelToAdd; rec.source = input.getModel(); rec.channel = input.getChannel(); rec.transform = transform; @@ -615,15 +615,15 @@ outputModelToAdd->setSourceModel(input.getModel()); - m_models[outputModelToAdd] = rec; + m_models.push_back(rec); #ifdef DEBUG_DOCUMENT - cerr << "Document::addAlreadyDerivedModel: Added model " << outputModelToAdd << endl; - cerr << "Models now: "; - for (ModelMap::const_iterator i = m_models.begin(); i != m_models.end(); ++i) { - cerr << i->first << " "; + SVDEBUG << "Document::addAlreadyDerivedModel: Added model " << outputModelToAdd << endl; + SVDEBUG << "Models now: "; + for (const auto &rec : m_models) { + SVDEBUG << rec.model << " "; } - cerr << endl; + SVDEBUG << endl; #endif emit modelAdded(outputModelToAdd); @@ -633,27 +633,28 @@ void Document::addImportedModel(Model *model) { - if (m_models.find(model) != m_models.end()) { - cerr << "WARNING: Document::addImportedModel: Model already added" + if (findModelInList(model) != m_models.end()) { + SVCERR << "WARNING: Document::addImportedModel: Model already added" << endl; return; } ModelRecord rec; + rec.model = model; rec.source = nullptr; rec.channel = 0; rec.refcount = 0; rec.additional = false; - m_models[model] = rec; + m_models.push_back(rec); #ifdef DEBUG_DOCUMENT SVDEBUG << "Document::addImportedModel: Added model " << model << endl; - cerr << "Models now: "; - for (ModelMap::const_iterator i = m_models.begin(); i != m_models.end(); ++i) { - cerr << i->first << " "; + SVDEBUG << "Models now: "; + for (const auto &rec : m_models) { + SVDEBUG << rec.model << " "; } - cerr << endl; + SVDEBUG << endl; #endif if (m_autoAlignment) { @@ -669,27 +670,28 @@ void Document::addAdditionalModel(Model *model) { - if (m_models.find(model) != m_models.end()) { - cerr << "WARNING: Document::addAdditionalModel: Model already added" + if (findModelInList(model) != m_models.end()) { + SVCERR << "WARNING: Document::addAdditionalModel: Model already added" << endl; return; } ModelRecord rec; + rec.model = model; rec.source = nullptr; rec.channel = 0; rec.refcount = 0; rec.additional = true; - m_models[model] = rec; + m_models.push_back(rec); #ifdef DEBUG_DOCUMENT SVDEBUG << "Document::addAdditionalModel: Added model " << model << endl; - cerr << "Models now: "; - for (ModelMap::const_iterator i = m_models.begin(); i != m_models.end(); ++i) { - cerr << i->first << " "; + SVDEBUG << "Models now: "; + for (const auto &rec : m_models) { + SVDEBUG << rec.model << " "; } - cerr << endl; + SVDEBUG << endl; #endif if (m_autoAlignment) { @@ -706,6 +708,7 @@ connect(model, SIGNAL(modelInvalidated()), this, SLOT(aggregateModelInvalidated())); m_aggregateModels.insert(model); + SVDEBUG << "Document::addAggregateModel(" << model << ")" << endl; } void @@ -713,6 +716,7 @@ { QObject *s = sender(); AggregateWaveModel *aggregate = qobject_cast<AggregateWaveModel *>(s); + SVDEBUG << "Document::aggregateModelInvalidated(" << aggregate << ")" << endl; if (aggregate) releaseModel(aggregate); } @@ -721,12 +725,12 @@ const ModelTransformer::Input &input, QString &message) { - for (ModelMap::iterator i = m_models.begin(); i != m_models.end(); ++i) { - if (i->second.transform == transform && - i->second.source == input.getModel() && - i->second.channel == input.getChannel()) { - std::cerr << "derived model taken from map " << std::endl; - return i->first; + for (auto &rec : m_models) { + if (rec.transform == transform && + rec.source == input.getModel() && + rec.channel == input.getChannel()) { + SVDEBUG << "derived model taken from map " << endl; + return rec.model; } } @@ -771,7 +775,7 @@ .getPluginVersion()); if (!model) { - cerr << "WARNING: Document::addDerivedModel: no output model for transform " << applied.getIdentifier() << endl; + SVCERR << "WARNING: Document::addDerivedModel: no output model for transform " << applied.getIdentifier() << endl; } else { addAlreadyDerivedModel(applied, input, model); } @@ -799,12 +803,17 @@ bool toDelete = false; - if (m_models.find(model) != m_models.end()) { - if (m_models[model].refcount == 0) { + ModelList::iterator mitr = findModelInList(model); + + if (mitr != m_models.end()) { + if (mitr->refcount == 0) { SVCERR << "WARNING: Document::releaseModel: model " << model << " reference count is zero already!" << endl; } else { - if (--m_models[model].refcount == 0) { +#ifdef DEBUG_DOCUMENT + SVDEBUG << "Lowering refcount from " << mitr->refcount << endl; +#endif + if (--mitr->refcount == 0) { toDelete = true; } } @@ -823,10 +832,10 @@ int sourceCount = 0; - for (ModelMap::iterator i = m_models.begin(); i != m_models.end(); ++i) { - if (i->second.source == model) { + for (auto &rec: m_models) { + if (rec.source == model) { ++sourceCount; - i->second.source = nullptr; + rec.source = nullptr; } } @@ -837,20 +846,16 @@ << "their source fields appropriately" << endl; } - model->aboutToDelete(); - emit modelAboutToBeDeleted(model); - m_models.erase(model); + deleteModelFromList(model); #ifdef DEBUG_DOCUMENT SVDEBUG << "Document::releaseModel: Deleted model " << model << endl; - cerr << "Models now: "; - for (ModelMap::const_iterator i = m_models.begin(); i != m_models.end(); ++i) { - cerr << i->first << " "; + SVDEBUG << "Models now: "; + for (const auto &r: m_models) { + SVDEBUG << r.model << " "; } - cerr << endl; + SVDEBUG << endl; #endif - - delete model; } } @@ -860,7 +865,7 @@ if (m_layerViewMap.find(layer) != m_layerViewMap.end() && m_layerViewMap[layer].size() > 0) { - cerr << "WARNING: Document::deleteLayer: Layer " + SVCERR << "WARNING: Document::deleteLayer: Layer " << layer << " [" << layer->objectName() << "]" << " is still used in " << m_layerViewMap[layer].size() << " views!" << endl; @@ -868,7 +873,7 @@ if (force) { #ifdef DEBUG_DOCUMENT - cerr << "(force flag set -- deleting from all views)" << endl; + SVCERR << "(force flag set -- deleting from all views)" << endl; #endif for (std::set<View *>::iterator j = m_layerViewMap[layer].begin(); @@ -885,7 +890,15 @@ } } - if (m_layers.find(layer) == m_layers.end()) { + bool found = false; + for (auto itr = m_layers.begin(); itr != m_layers.end(); ++itr) { + if (*itr == layer) { + found = true; + m_layers.erase(itr); + break; + } + } + if (!found) { SVDEBUG << "Document::deleteLayer: Layer " << layer << " (typeid " << typeid(layer).name() << ") does not exist, or has already been deleted " @@ -893,8 +906,6 @@ return; } - m_layers.erase(layer); - #ifdef DEBUG_DOCUMENT SVDEBUG << "Document::deleteLayer: Removing (and about to release model), now have " << m_layers.size() << " layers" << endl; @@ -911,8 +922,8 @@ { if (model && model != m_mainModel && - m_models.find(model) == m_models.end()) { - cerr << "ERROR: Document::setModel: Layer " << layer + findModelInList(model) == m_models.end()) { + SVCERR << "ERROR: Document::setModel: Layer " << layer << " (\"" << layer->objectName() << "\") wants to use unregistered model " << model << ": register the layer's model before setting it!" @@ -932,7 +943,10 @@ } if (model && model != m_mainModel) { - m_models[model].refcount ++; + ModelList::iterator mitr = findModelInList(model); + if (mitr != m_models.end()) { + mitr->refcount ++; + } } if (model && previousModel) { @@ -941,7 +955,6 @@ } LayerFactory::getInstance()->setModel(layer, model); - // std::cerr << "layer type: " << LayerFactory::getInstance()->getLayerTypeName(LayerFactory::getInstance()->getLayerType(layer)) << std::endl; if (previousModel) { releaseModel(previousModel); @@ -961,13 +974,14 @@ if (!model) { #ifdef DEBUG_DOCUMENT SVDEBUG << "Document::addLayerToView: Layer (\"" - << layer->objectName() << "\") with no model being added to view: " + << layer->objectName() + << "\") with no model being added to view: " << "normally you want to set the model first" << endl; #endif } else { if (model != m_mainModel && - m_models.find(model) == m_models.end()) { - cerr << "ERROR: Document::addLayerToView: Layer " << layer + findModelInList(model) == m_models.end()) { + SVCERR << "ERROR: Document::addLayerToView: Layer " << layer << " has unregistered model " << model << " -- register the layer's model before adding the layer!" << endl; return; @@ -993,7 +1007,7 @@ if (m_layerViewMap[layer].find(view) != m_layerViewMap[layer].end()) { - cerr << "WARNING: Document::addToLayerViewMap:" + SVCERR << "WARNING: Document::addToLayerViewMap:" << " Layer " << layer << " -> view " << view << " already in" << " layer view map -- internal inconsistency" << endl; } @@ -1008,7 +1022,7 @@ { if (m_layerViewMap[layer].find(view) == m_layerViewMap[layer].end()) { - cerr << "WARNING: Document::removeFromLayerViewMap:" + SVCERR << "WARNING: Document::removeFromLayerViewMap:" << " Layer " << layer << " -> view " << view << " not in" << " layer view map -- internal inconsistency" << endl; } @@ -1032,7 +1046,7 @@ bool duplicate = false; - for (LayerSet::iterator i = m_layers.begin(); i != m_layers.end(); ++i) { + for (auto i = m_layers.begin(); i != m_layers.end(); ++i) { if ((*i)->objectName() == adjusted) { duplicate = true; break; @@ -1054,9 +1068,9 @@ //!!! This will pick up all models, including those that aren't visible... - for (ModelMap::iterator i = m_models.begin(); i != m_models.end(); ++i) { + for (ModelRecord &rec: m_models) { - Model *model = i->first; + Model *model = rec.model; if (!model || model == m_mainModel) continue; DenseTimeValueModel *dtvm = dynamic_cast<DenseTimeValueModel *>(model); @@ -1072,7 +1086,10 @@ Document::isKnownModel(const Model *model) const { if (model == m_mainModel) return true; - return (m_models.find(const_cast<Model *>(model)) != m_models.end()); + for (const ModelRecord &rec: m_models) { + if (rec.model == model) return true; + } + return false; } bool @@ -1122,8 +1139,8 @@ void Document::alignModels() { - for (ModelMap::iterator i = m_models.begin(); i != m_models.end(); ++i) { - alignModel(i->first); + for (const ModelRecord &rec: m_models) { + alignModel(rec.model); } alignModel(m_mainModel); } @@ -1275,6 +1292,10 @@ if (m_mainModel) { +#ifdef DEBUG_DOCUMENT + SVDEBUG << "Document::toXml: writing main model" << endl; +#endif + if (asTemplate) { writePlaceholderMainModel(out, indent + " "); } else { @@ -1287,8 +1308,12 @@ playParameters->toXml (out, indent + " ", QString("model=\"%1\"") - .arg(XmlExportable::getObjectExportId(m_mainModel))); + .arg(m_mainModel->getExportId())); } + } else { +#ifdef DEBUG_DOCUMENT + SVDEBUG << "Document::toXml: have no main model to write" << endl; +#endif } // Models that are not used in a layer that is in a view should @@ -1326,16 +1351,22 @@ for (std::set<Model *>::iterator i = m_aggregateModels.begin(); i != m_aggregateModels.end(); ++i) { - SVDEBUG << "checking aggregate model " << *i << endl; +#ifdef DEBUG_DOCUMENT + SVDEBUG << "Document::toXml: checking aggregate model " << *i << endl; +#endif AggregateWaveModel *aggregate = qobject_cast<AggregateWaveModel *>(*i); if (!aggregate) continue; if (used.find(aggregate) == used.end()) { +#ifdef DEBUG_DOCUMENT SVDEBUG << "(unused, skipping)" << endl; +#endif continue; } +#ifdef DEBUG_DOCUMENT SVDEBUG << "(used, writing)" << endl; +#endif aggregate->toXml(out, indent + " "); } @@ -1352,14 +1383,19 @@ const int nonDerivedPass = 0, derivedPass = 1; for (int pass = nonDerivedPass; pass <= derivedPass; ++pass) { - for (ModelMap::const_iterator i = m_models.begin(); - i != m_models.end(); ++i) { + for (const ModelRecord &rec: m_models) { - Model *model = i->first; - const ModelRecord &rec = i->second; + Model *model = rec.model; if (used.find(model) == used.end()) continue; +#ifdef DEBUG_DOCUMENT + SVDEBUG << "Document::toXml: looking at model " << model + << " (" << model->getTypeName() << ", \"" + << model->objectName() << "\") [pass = " + << pass << "]" << endl; +#endif + // 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 @@ -1418,7 +1454,7 @@ playParameters->toXml (out, indent + " ", QString("model=\"%1\"") - .arg(XmlExportable::getObjectExportId(model))); + .arg(model->getExportId())); } } } @@ -1439,9 +1475,7 @@ alignment->toXml(out, indent + " "); } - for (LayerSet::const_iterator i = m_layers.begin(); - i != m_layers.end(); ++i) { - + for (auto i = m_layers.begin(); i != m_layers.end(); ++i) { (*i)->toXml(out, indent + " "); } @@ -1453,7 +1487,7 @@ { out << indent; out << QString("<model id=\"%1\" name=\"placeholder\" sampleRate=\"%2\" type=\"wavefile\" file=\":samples/silent.wav\" mainModel=\"true\"/>\n") - .arg(getObjectExportId(m_mainModel)) + .arg(m_mainModel->getExportId()) .arg(m_mainModel->getSampleRate()); } @@ -1492,8 +1526,8 @@ // out << indent // << QString("<derivation type=\"transform\" source=\"%1\" " // "model=\"%2\" channel=\"%3\">\n") - // .arg(XmlExportable::getObjectExportId(rec.source)) - // .arg(XmlExportable::getObjectExportId(targetModel)) + // .arg(rec.source->getExportId()) + // .arg(targetModel->getExportId()) // .arg(rec.channel); // // transform.toXml(out, indent + " "); @@ -1517,8 +1551,8 @@ "model=\"%2\" channel=\"%3\" domain=\"%4\" " "stepSize=\"%5\" blockSize=\"%6\" %7windowType=\"%8\" " "transform=\"%9\">\n") - .arg(XmlExportable::getObjectExportId(rec.source)) - .arg(XmlExportable::getObjectExportId(targetModel)) + .arg(rec.source->getExportId()) + .arg(targetModel->getExportId()) .arg(rec.channel) .arg(TransformFactory::getInstance()->getTransformInputDomain (transform.getIdentifier()))
--- a/framework/Document.h Thu Apr 04 16:17:11 2019 +0100 +++ b/framework/Document.h Wed Apr 24 11:45:31 2019 +0100 @@ -370,6 +370,7 @@ // be confusing to have Input objects hanging around with NULL // models in them. + Model *model; const Model *source; int channel; Transform transform; @@ -379,8 +380,41 @@ int refcount; }; - typedef std::map<Model *, ModelRecord> ModelMap; - ModelMap m_models; + // This used to be a map<Model *, ModelRecord>, but a vector + // ensures that models are consistently recorded in order of + // creation rather than at the whim of heap allocation, and that's + // useful for automated testing. We don't expect ever to have so + // many models that there is any significant overhead there. + typedef std::vector<ModelRecord> ModelList; + ModelList m_models; + + ModelList::iterator findModelInList(Model *m) { + for (ModelList::iterator i = m_models.begin(); + i != m_models.end(); ++i) { + if (i->model == m) { + return i; + } + } + return m_models.end(); + } + + void deleteModelFromList(Model *m) { + ModelList keep; + bool found = false; + for (const ModelRecord &rec: m_models) { + if (rec.model == m) { + found = true; + m->aboutToDelete(); + emit modelAboutToBeDeleted(m); + } else { + keep.push_back(rec); + } + } + m_models = keep; + if (found) { + delete m; + } + } /** * Add an extra derived model (returned at the end of processing a @@ -447,8 +481,8 @@ * And these are the layers. We also control the lifespans of * these (usually through the commands used to add and remove them). */ - typedef std::set<Layer *> LayerSet; - LayerSet m_layers; + typedef std::vector<Layer *> LayerList; + LayerList m_layers; bool m_autoAlignment; Align *m_align;
--- a/framework/MainWindowBase.cpp Thu Apr 04 16:17:11 2019 +0100 +++ b/framework/MainWindowBase.cpp Wed Apr 24 11:45:31 2019 +0100 @@ -22,7 +22,6 @@ #include "data/model/WritableWaveFileModel.h" #include "data/model/SparseOneDimensionalModel.h" #include "data/model/NoteModel.h" -#include "data/model/FlexiNoteModel.h" #include "data/model/Labeller.h" #include "data/model/TabularModel.h" #include "view/ViewManager.h" @@ -55,10 +54,12 @@ #include "data/fileio/PlaylistFileReader.h" #include "data/fileio/WavFileWriter.h" #include "data/fileio/MIDIFileWriter.h" +#include "data/fileio/CSVFileWriter.h" #include "data/fileio/BZipFileDevice.h" #include "data/fileio/FileSource.h" #include "data/fileio/AudioFileReaderFactory.h" #include "rdf/RDFImporter.h" +#include "rdf/RDFExporter.h" #include "base/RecentFiles.h" @@ -72,6 +73,7 @@ #include "data/osc/OSCQueue.h" #include "data/midi/MIDIInput.h" +#include "OSCScript.h" #include "system/System.h" @@ -149,6 +151,7 @@ m_audioIO(nullptr), m_oscQueue(nullptr), m_oscQueueStarter(nullptr), + m_oscScript(nullptr), m_midiInput(nullptr), m_recentFiles("RecentFiles", 20), m_recentTransforms("RecentTransforms", 20), @@ -328,6 +331,17 @@ delete m_viewManager; delete m_midiInput; + if (m_oscScript) { + disconnect(m_oscScript, nullptr, nullptr, nullptr); + m_oscScript->abandon(); + m_oscScript->wait(1000); + if (m_oscScript->isRunning()) { + m_oscScript->terminate(); + m_oscScript->wait(1000); + } + delete m_oscScript; + } + if (m_oscQueueStarter) { disconnect(m_oscQueueStarter, nullptr, nullptr, nullptr); m_oscQueueStarter->wait(1000); @@ -501,9 +515,9 @@ } void -MainWindowBase::startOSCQueue() +MainWindowBase::startOSCQueue(bool withNetworkPort) { - m_oscQueueStarter = new OSCQueueStarter(this); + m_oscQueueStarter = new OSCQueueStarter(this, withNetworkPort); connect(m_oscQueueStarter, SIGNAL(finished()), this, SLOT(oscReady())); m_oscQueueStarter->start(); } @@ -516,10 +530,45 @@ QTimer *oscTimer = new QTimer(this); connect(oscTimer, SIGNAL(timeout()), this, SLOT(pollOSC())); oscTimer->start(1000); - SVCERR << "Finished setting up OSC interface" << endl; + + if (m_oscQueue->hasPort()) { + SVDEBUG << "Finished setting up OSC interface" << endl; + } else { + SVDEBUG << "Finished setting up internal-only OSC queue" << endl; + } + + if (m_oscScriptFile != QString()) { + startOSCScript(); + } } } +void +MainWindowBase::startOSCScript() +{ + m_oscScript = new OSCScript(m_oscScriptFile, m_oscQueue); + connect(m_oscScript, SIGNAL(finished()), + this, SLOT(oscScriptFinished())); + m_oscScriptFile = QString(); + m_oscScript->start(); +} + +void +MainWindowBase::cueOSCScript(QString fileName) +{ + m_oscScriptFile = fileName; + if (m_oscQueue && m_oscQueue->isOK()) { + startOSCScript(); + } +} + +void +MainWindowBase::oscScriptFinished() +{ + delete m_oscScript; + m_oscScript = 0; +} + QString MainWindowBase::getOpenFileName(FileFinder::FileType type) { @@ -1136,46 +1185,44 @@ if (layer) { Model *model = layer->getModel(); - SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *> - (model); + SparseOneDimensionalModel *sodm = + dynamic_cast<SparseOneDimensionalModel *>(model); if (sodm) { - SparseOneDimensionalModel::Point point(frame, ""); - - SparseOneDimensionalModel::Point prevPoint(0); + Event point(frame, ""); + Event prevPoint(0); bool havePrevPoint = false; - SparseOneDimensionalModel::EditCommand *command = - new SparseOneDimensionalModel::EditCommand(sodm, tr("Add Point")); + ChangeEventsCommand *command = + new ChangeEventsCommand(sodm, tr("Add Point")); if (m_labeller) { if (m_labeller->requiresPrevPoint()) { - - SparseOneDimensionalModel::PointList prevPoints = - sodm->getPreviousPoints(frame); - - if (!prevPoints.empty()) { - prevPoint = *prevPoints.begin(); + + if (sodm->getNearestEventMatching + (frame, + [](Event) { return true; }, + EventSeries::Backward, + prevPoint)) { havePrevPoint = true; } } m_labeller->setSampleRate(sodm->getSampleRate()); - if (m_labeller->actingOnPrevPoint() && havePrevPoint) { - command->deletePoint(prevPoint); - } - - m_labeller->label<SparseOneDimensionalModel::Point> + Labeller::Relabelling relabelling = m_labeller->label (point, havePrevPoint ? &prevPoint : nullptr); - if (m_labeller->actingOnPrevPoint() && havePrevPoint) { - command->addPoint(prevPoint); + if (relabelling.first == Labeller::AppliesToPreviousEvent) { + command->remove(prevPoint); + command->add(relabelling.second); + } else { + point = relabelling.second; } } - command->addPoint(point); + command->add(point); command->setName(tr("Add Point at %1 s") .arg(RealTime::frame2RealTime @@ -1231,14 +1278,12 @@ RegionModel *rm = dynamic_cast<RegionModel *>(layer->getModel()); if (rm) { - RegionModel::Point point(alignedStart, - rm->getValueMaximum() + 1, - alignedDuration, - ""); - RegionModel::EditCommand *command = - new RegionModel::EditCommand(rm, tr("Add Point")); - command->addPoint(point); - command->setName(name); + Event point(alignedStart, + rm->getValueMaximum() + 1, + alignedDuration, + ""); + ChangeEventsCommand *command = new ChangeEventsCommand(rm, name); + command->add(point); c = command->finish(); } @@ -1249,15 +1294,13 @@ NoteModel *nm = dynamic_cast<NoteModel *>(layer->getModel()); if (nm) { - NoteModel::Point point(alignedStart, - nm->getValueMinimum(), - alignedDuration, - 1.f, - ""); - NoteModel::EditCommand *command = - new NoteModel::EditCommand(nm, tr("Add Point")); - command->addPoint(point); - command->setName(name); + Event point(alignedStart, + nm->getValueMinimum(), + alignedDuration, + 1.f, + ""); + ChangeEventsCommand *command = new ChangeEventsCommand(nm, name); + command->add(point); c = command->finish(); } @@ -1265,25 +1308,6 @@ CommandHistory::getInstance()->addCommand(c, false); return; } - - FlexiNoteModel *fnm = dynamic_cast<FlexiNoteModel *>(layer->getModel()); - if (fnm) { - FlexiNoteModel::Point point(alignedStart, - fnm->getValueMinimum(), - alignedDuration, - 1.f, - ""); - FlexiNoteModel::EditCommand *command = - new FlexiNoteModel::EditCommand(fnm, tr("Add Point")); - command->addPoint(point); - command->setName(name); - c = command->finish(); - } - - if (c) { - CommandHistory::getInstance()->addCommand(c, false); - return; - } } void @@ -1306,9 +1330,10 @@ Labeller labeller(*m_labeller); labeller.setSampleRate(sodm->getSampleRate()); - +/*!!! to be updated after SODM API update Command *c = labeller.labelAll<SparseOneDimensionalModel::Point>(*sodm, &ms); if (c) CommandHistory::getInstance()->addCommand(c, false); +*/ } void @@ -1332,9 +1357,12 @@ Labeller labeller(*m_labeller); labeller.setSampleRate(sodm->getSampleRate()); + (void)n; +/*!!! to be updated after SODM API update Command *c = labeller.subdivide<SparseOneDimensionalModel::Point> (*sodm, &ms, n); if (c) CommandHistory::getInstance()->addCommand(c, false); +*/ } void @@ -1358,9 +1386,12 @@ Labeller labeller(*m_labeller); labeller.setSampleRate(sodm->getSampleRate()); + (void)n; +/*!!! to be updated after SODM API update Command *c = labeller.winnow<SparseOneDimensionalModel::Point> (*sodm, &ms, n); if (c) CommandHistory::getInstance()->addCommand(c, false); +*/ } MainWindowBase::FileOpenStatus @@ -2736,6 +2767,79 @@ } } +bool +MainWindowBase::exportLayerTo(Layer *layer, QString path, QString &error) +{ + if (QFileInfo(path).suffix() == "") path += ".svl"; + + QString suffix = QFileInfo(path).suffix().toLower(); + + Model *model = layer->getModel(); + + if (suffix == "xml" || suffix == "svl") { + + QFile file(path); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + error = tr("Failed to open file %1 for writing").arg(path); + } else { + QTextStream out(&file); + out.setCodec(QTextCodec::codecForName("UTF-8")); + out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + << "<!DOCTYPE sonic-visualiser>\n" + << "<sv>\n" + << " <data>\n"; + + model->toXml(out, " "); + + out << " </data>\n" + << " <display>\n"; + + layer->toXml(out, " "); + + out << " </display>\n" + << "</sv>\n"; + } + + } else if (suffix == "mid" || suffix == "midi") { + + NoteModel *nm = dynamic_cast<NoteModel *>(model); + + if (!nm) { + error = tr("Can't export non-note layers to MIDI"); + } else { + MIDIFileWriter writer(path, nm, nm->getSampleRate()); + writer.write(); + if (!writer.isOK()) { + error = writer.getError(); + } + } + + } else if (suffix == "ttl" || suffix == "n3") { + + if (!RDFExporter::canExportModel(model)) { + error = tr("Sorry, cannot export this layer type to RDF (supported types are: region, note, text, time instants, time values)"); + } else { + RDFExporter exporter(path, model); + exporter.write(); + if (!exporter.isOK()) { + error = exporter.getError(); + } + } + + } else { + + CSVFileWriter writer(path, model, + ((suffix == "csv") ? "," : "\t")); + writer.write(); + + if (!writer.isOK()) { + error = writer.getError(); + } + } + + return (error == ""); +} + void MainWindowBase::toXml(QTextStream &out, bool asTemplate) {
--- a/framework/MainWindowBase.h Thu Apr 04 16:17:11 2019 +0100 +++ b/framework/MainWindowBase.h Wed Apr 24 11:45:31 2019 +0100 @@ -34,6 +34,7 @@ #include "data/fileio/FileFinder.h" #include "data/fileio/FileSource.h" #include "data/osc/OSCQueue.h" +#include "data/osc/OSCMessageCallback.h" #include <map> class Document; @@ -58,6 +59,7 @@ class QTreeView; class QPushButton; class OSCMessage; +class OSCScript; class MIDIInput; class KeyReference; class Labeller; @@ -81,7 +83,9 @@ * to use different subclasses retaining the same general structure. */ -class MainWindowBase : public QMainWindow, public FrameTimer +class MainWindowBase : public QMainWindow, + public FrameTimer, + public OSCMessageCallback { Q_OBJECT @@ -135,6 +139,10 @@ virtual bool saveSessionFile(QString path); virtual bool saveSessionTemplate(QString path); + virtual bool exportLayerTo(Layer *layer, QString path, QString &error); + + void cueOSCScript(QString filename); + /// Implementation of FrameTimer interface method sv_frame_t getFrame() const override; @@ -320,7 +328,7 @@ virtual void oscReady(); virtual void pollOSC(); - virtual void handleOSCMessage(const OSCMessage &) = 0; + virtual void oscScriptFinished(); virtual void contextHelpChanged(const QString &); virtual void inProgressSelectionChanged(); @@ -356,18 +364,27 @@ class OSCQueueStarter : public QThread { public: - OSCQueueStarter(MainWindowBase *mwb) : QThread(mwb), m_mwb(mwb) { } + OSCQueueStarter(MainWindowBase *mwb, bool withNetworkPort) : + QThread(mwb), m_mwb(mwb), m_withPort(withNetworkPort) { } + void run() override { - OSCQueue *queue = new OSCQueue(); // can take a long time + // NB creating the queue object can take a long time + OSCQueue *queue = new OSCQueue(m_withPort); m_mwb->m_oscQueue = queue; } + private: MainWindowBase *m_mwb; + bool m_withPort; }; OSCQueue *m_oscQueue; OSCQueueStarter *m_oscQueueStarter; - void startOSCQueue(); + OSCScript *m_oscScript; + QString m_oscScriptFile; + + void startOSCQueue(bool withNetworkPort); + void startOSCScript(); MIDIInput *m_midiInput;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/framework/OSCScript.h Wed Apr 24 11:45:31 2019 +0100 @@ -0,0 +1,131 @@ +/* -*- 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 SV_OSC_SCRIPT_H +#define SV_OSC_SCRIPT_H + +#include <QThread> +#include <QFile> +#include <QTextStream> + +#include "base/Debug.h" +#include "base/StringBits.h" +#include "data/osc/OSCQueue.h" +#include "data/osc/OSCMessage.h" + +#include <stdexcept> + +class OSCScript : public QThread +{ + Q_OBJECT + +public: + OSCScript(QString filename, OSCQueue *queue) : + m_filename(filename), + m_queue(queue), + m_abandoning(false) { + } + + void run() override { + + if (!m_queue) { + SVCERR << "OSCScript: No OSC queue available" << endl; + throw std::runtime_error("OSC queue not running"); + } + + QFile f; + QString reportedFilename; + + if (m_filename == "-") { + f.open(stdin, QFile::ReadOnly | QFile::Text); + reportedFilename = "<stdin>"; + } else { + f.setFileName(m_filename); + if (!f.open(QFile::ReadOnly | QFile::Text)) { + SVCERR << "OSCScript: Failed to open script file \"" + << m_filename << "\" for reading" << endl; + throw std::runtime_error("OSC script file not found"); + } + reportedFilename = m_filename; + } + + QTextStream str(&f); + int lineno = 0; + + while (!str.atEnd() && !m_abandoning) { + + ++lineno; + + QString line = str.readLine().trimmed(); + if (line == QString()) continue; + + if (line[0] == '#') { + continue; + + } else if (line[0].isDigit()) { + bool ok = false; + float pause = line.toFloat(&ok); + if (ok) { + SVCERR << "OSCScript: " + << reportedFilename << ":" << lineno + << ": pausing for " << pause << " sec" << endl; + msleep(unsigned(round(pause * 1000.0f))); + continue; + } else { + SVCERR << "OSCScript: " + << reportedFilename << ":" << lineno + << ": warning: failed to parse sleep time, ignoring" + << endl; + continue; + } + + } else if (line[0] == '/' && line.size() > 1) { + QStringList parts = StringBits::splitQuoted(line, ' '); + if (parts.empty()) { + SVCERR << "OSCScript: " + << reportedFilename << ":" << lineno + << ": warning: empty command spec, ignoring" + << endl; + continue; + } + OSCMessage message; + message.setMethod(parts[0].mid(1)); + for (int i = 1; i < parts.size(); ++i) { + message.addArg(parts[i]); + } + SVCERR << "OSCScript: " << reportedFilename << ":" << lineno + << ": invoking: \"" << parts[0] << "\"" << endl; + m_queue->postMessage(message); + + } else { + SVCERR << "OSCScript: " << reportedFilename << ":" << lineno + << ": warning: message expected, ignoring" << endl; + } + } + + SVCERR << "OSCScript: " << reportedFilename << ": finished" << endl; + } + + void abandon() { + m_abandoning = true; + } + +private: + QString m_filename; + OSCQueue *m_queue; // I do not own this + bool m_abandoning; +}; + +#endif +
--- a/framework/SVFileReader.cpp Thu Apr 04 16:17:11 2019 +0100 +++ b/framework/SVFileReader.cpp Wed Apr 24 11:45:31 2019 +0100 @@ -31,7 +31,6 @@ #include "data/model/SparseOneDimensionalModel.h" #include "data/model/SparseTimeValueModel.h" #include "data/model/NoteModel.h" -#include "data/model/FlexiNoteModel.h" #include "data/model/RegionModel.h" #include "data/model/TextModel.h" #include "data/model/ImageModel.h" @@ -459,28 +458,30 @@ { makeAggregateModels(); - std::set<Model *> unaddedModels; - for (std::map<int, Model *>::iterator i = m_models.begin(); i != m_models.end(); ++i) { - if (m_addedModels.find(i->second) == m_addedModels.end()) { - unaddedModels.insert(i->second); + + Model *model = i->second; + + if (m_addedModels.find(model) != m_addedModels.end()) { + // already added this one + continue; } - } - - for (std::set<Model *>::iterator i = unaddedModels.begin(); - i != unaddedModels.end(); ++i) { - Model *model = *i; - // don't want to add these models, because their lifespans - // are entirely dictated by the models that "own" them even - // though they were read independently from the .sv file. - // (pity we don't have a nicer way) + + // don't want to add path and alignment models to the + // document, because their lifespans are entirely dictated by + // the models that "own" them even though they were read + // independently from the .sv file. (pity we don't have a + // nicer way to handle this) if (!dynamic_cast<PathModel *>(model) && !dynamic_cast<AlignmentModel *>(model)) { + m_document->addImportedModel(model); } - // but we add all models here, so they don't get deleted - // when the file loader is destroyed + + // but we add all models including path and alignment ones to + // the added set, so they don't get deleted from our own + // destructor m_addedModels.insert(model); } } @@ -713,13 +714,16 @@ model->setObjectName(name); m_models[id] = model; } else if (attributes.value("subtype") == "flexinote") { - FlexiNoteModel *model; + NoteModel *model; if (haveMinMax) { - model = new FlexiNoteModel - (sampleRate, resolution, minimum, maximum, notifyOnAdd); + model = new NoteModel + (sampleRate, resolution, minimum, maximum, + notifyOnAdd, + NoteModel::FLEXI_NOTE); } else { - model = new FlexiNoteModel - (sampleRate, resolution, notifyOnAdd); + model = new NoteModel + (sampleRate, resolution, notifyOnAdd, + NoteModel::FLEXI_NOTE); } model->setValueQuantization(valueQuantization); model->setScaleUnits(units); @@ -1051,7 +1055,6 @@ case 3: if (dynamic_cast<NoteModel *>(model)) good = true; - else if (dynamic_cast<FlexiNoteModel *>(model)) good = true; else if (dynamic_cast<RegionModel *>(model)) good = true; else if (dynamic_cast<EditableDenseThreeDimensionalModel *>(model)) { m_datasetSeparator = attributes.value("separator"); @@ -1085,7 +1088,7 @@ if (sodm) { // SVCERR << "Current dataset is a sparse one dimensional model" << endl; QString label = attributes.value("label"); - sodm->addPoint(SparseOneDimensionalModel::Point(frame, label)); + sodm->add(Event(frame, label)); return true; } @@ -1097,7 +1100,7 @@ float value = 0.0; value = attributes.value("value").trimmed().toFloat(&ok); QString label = attributes.value("label"); - stvm->addPoint(SparseTimeValueModel::Point(frame, value, label)); + stvm->add(Event(frame, value, label)); return ok; } @@ -1115,25 +1118,7 @@ level = 1.f; ok = true; } - nm->addPoint(NoteModel::Point(frame, value, duration, level, label)); - return ok; - } - - FlexiNoteModel *fnm = dynamic_cast<FlexiNoteModel *>(m_currentDataset); - - if (fnm) { -// SVCERR << "Current dataset is a flexinote model" << endl; - float value = 0.0; - value = attributes.value("value").trimmed().toFloat(&ok); - int duration = 0; - duration = attributes.value("duration").trimmed().toInt(&ok); - QString label = attributes.value("label"); - float level = attributes.value("level").trimmed().toFloat(&ok); - if (!ok) { // level is optional - level = 1.f; - ok = true; - } - fnm->addPoint(FlexiNoteModel::Point(frame, value, duration, level, label)); + nm->add(Event(frame, value, duration, level, label)); return ok; } @@ -1146,7 +1131,7 @@ int duration = 0; duration = attributes.value("duration").trimmed().toInt(&ok); QString label = attributes.value("label"); - rm->addPoint(RegionModel::Point(frame, value, duration, label)); + rm->add(Event(frame, value, duration, label)); return ok; } @@ -1158,7 +1143,7 @@ height = attributes.value("height").trimmed().toFloat(&ok); QString label = attributes.value("label"); // SVDEBUG << "SVFileReader::addPointToDataset: TextModel: frame = " << frame << ", height = " << height << ", label = " << label << ", ok = " << ok << endl; - tm->addPoint(TextModel::Point(frame, height, label)); + tm->add(Event(frame, height, label)); return ok; } @@ -1168,7 +1153,7 @@ // SVCERR << "Current dataset is a path model" << endl; int mapframe = attributes.value("mapframe").trimmed().toInt(&ok); // SVDEBUG << "SVFileReader::addPointToDataset: PathModel: frame = " << frame << ", mapframe = " << mapframe << ", ok = " << ok << endl; - pm->addPoint(PathModel::Point(frame, mapframe)); + pm->add(PathPoint(frame, mapframe)); return ok; } @@ -1179,7 +1164,7 @@ QString image = attributes.value("image"); QString label = attributes.value("label"); // SVDEBUG << "SVFileReader::addPointToDataset: ImageModel: frame = " << frame << ", image = " << image << ", label = " << label << ", ok = " << ok << endl; - im->addPoint(ImageModel::Point(frame, image, label)); + im->add(Event(frame).withURI(image).withLabel(label)); return ok; }