# HG changeset patch # User Chris Cannam # Date 1563369941 -3600 # Node ID 155008f1bf10b05443d0ee5ef3d312183028f646 # Parent a82b9d4103931650e3d3c7ffa54ef0530ba3d875# Parent bd6e2fc5337764f8732cc3ba921fb854b032535a Merge from branch by-id diff -r a82b9d410393 -r 155008f1bf10 audio/AudioCallbackPlaySource.cpp --- a/audio/AudioCallbackPlaySource.cpp Fri Jun 14 17:19:37 2019 +0100 +++ b/audio/AudioCallbackPlaySource.cpp Wed Jul 17 14:25:41 2019 +0100 @@ -100,8 +100,8 @@ m_viewManager, SLOT(playStatusChanged(bool))); connect(PlayParameterRepository::getInstance(), - SIGNAL(playParametersChanged(PlayParameters *)), - this, SLOT(playParametersChanged(PlayParameters *))); + SIGNAL(playParametersChanged(int)), + this, SLOT(playParametersChanged(int))); connect(Preferences::getInstance(), SIGNAL(propertyChanged(PropertyContainer::PropertyName)), @@ -151,15 +151,19 @@ } void -AudioCallbackPlaySource::addModel(Model *model) +AudioCallbackPlaySource::addModel(ModelId modelId) { - if (m_models.find(model) != m_models.end()) return; + if (m_models.find(modelId) != m_models.end()) return; - bool willPlay = m_audioGenerator->addModel(model); + bool willPlay = m_audioGenerator->addModel(modelId); + + auto model = ModelById::get(modelId); + if (!model) return; m_mutex.lock(); - m_models.insert(model); + m_models.insert(modelId); + if (model->getEndFrame() > m_lastModelEndFrame) { m_lastModelEndFrame = model->getEndFrame(); } @@ -167,7 +171,7 @@ bool buffersIncreased = false, srChanged = false; int modelChannels = 1; - ReadOnlyWaveFileModel *rowfm = qobject_cast(model); + auto rowfm = std::dynamic_pointer_cast(model); if (rowfm) modelChannels = rowfm->getChannelCount(); if (modelChannels > m_sourceChannelCount) { m_sourceChannelCount = modelChannels; @@ -194,20 +198,19 @@ bool conflicting = false; - for (std::set::const_iterator i = m_models.begin(); - i != m_models.end(); ++i) { + for (ModelId otherId: m_models) { // Only read-only wave file models should be // considered conflicting -- writable wave file models // are derived and we shouldn't take their rates into // account. Also, don't give any particular weight to // a file that's already playing at the wrong rate // anyway - ReadOnlyWaveFileModel *other = - qobject_cast(*i); - if (other && other != rowfm && + if (otherId == modelId) continue; + auto other = ModelById::getAs(otherId); + if (other && other->getSampleRate() != model->getSampleRate() && other->getSampleRate() == m_sourceSampleRate) { - SVDEBUG << "AudioCallbackPlaySource::addModel: Conflicting wave file model " << *i << " found" << endl; + SVDEBUG << "AudioCallbackPlaySource::addModel: Conflicting wave file model " << otherId << " found" << endl; conflicting = true; break; } @@ -291,8 +294,8 @@ SVDEBUG << "AudioCallbackPlaySource::addModel: now have " << m_models.size() << " model(s)" << endl; #endif - connect(model, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)), - this, SLOT(modelChangedWithin(sv_frame_t, sv_frame_t))); + connect(model.get(), SIGNAL(modelChangedWithin(ModelId, sv_frame_t, sv_frame_t)), + this, SLOT(modelChangedWithin(ModelId, sv_frame_t, sv_frame_t))); #ifdef DEBUG_AUDIO_PLAY_SOURCE cout << "AudioCallbackPlaySource::addModel: awakening thread" << endl; @@ -302,7 +305,7 @@ } void -AudioCallbackPlaySource::modelChangedWithin(sv_frame_t +AudioCallbackPlaySource::modelChangedWithin(ModelId, sv_frame_t #ifdef DEBUG_AUDIO_PLAY_SOURCE startFrame #endif @@ -318,37 +321,31 @@ } void -AudioCallbackPlaySource::removeModel(Model *model) +AudioCallbackPlaySource::removeModel(ModelId modelId) { + auto model = ModelById::get(modelId); + if (!model) return; + m_mutex.lock(); #ifdef DEBUG_AUDIO_PLAY_SOURCE - cout << "AudioCallbackPlaySource::removeModel(" << model << ")" << endl; + cout << "AudioCallbackPlaySource::removeModel(" << modelId << ")" << endl; #endif - disconnect(model, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)), - this, SLOT(modelChangedWithin(sv_frame_t, sv_frame_t))); + disconnect(model.get(), SIGNAL(modelChangedWithin(ModelId, sv_frame_t, sv_frame_t)), + this, SLOT(modelChangedWithin(ModelId, sv_frame_t, sv_frame_t))); - m_models.erase(model); - - // I don't think we have to do this any more: if a new model is - // loaded at a different rate, we'll hit the non-conflicting path - // in addModel and the rate will be updated without problems; but - // if a new model is loaded at the rate that we were using for the - // last one, then we save work by not having reset this here - // -// if (m_models.empty()) { -// m_sourceSampleRate = 0; -// } + m_models.erase(modelId); sv_frame_t lastEnd = 0; - for (std::set::const_iterator i = m_models.begin(); - i != m_models.end(); ++i) { + for (ModelId otherId: m_models) { #ifdef DEBUG_AUDIO_PLAY_SOURCE - cout << "AudioCallbackPlaySource::removeModel(" << model << "): checking end frame on model " << *i << endl; + cout << "AudioCallbackPlaySource::removeModel(" << modelId << "): checking end frame on model " << otherId << endl; #endif - if ((*i)->getEndFrame() > lastEnd) { - lastEnd = (*i)->getEndFrame(); + if (auto other = ModelById::get(otherId)) { + if (other->getEndFrame() > lastEnd) { + lastEnd = other->getEndFrame(); + } } #ifdef DEBUG_AUDIO_PLAY_SOURCE cout << "(done, lastEnd now " << lastEnd << ")" << endl; @@ -356,7 +353,7 @@ } m_lastModelEndFrame = lastEnd; - m_audioGenerator->removeModel(model); + m_audioGenerator->removeModel(modelId); if (m_models.empty()) { m_sourceSampleRate = 0; @@ -579,13 +576,13 @@ } void -AudioCallbackPlaySource::playParametersChanged(PlayParameters *) +AudioCallbackPlaySource::playParametersChanged(int) { clearRingBuffers(); } void -AudioCallbackPlaySource::preferenceChanged(PropertyContainer::PropertyName ) +AudioCallbackPlaySource::preferenceChanged(PropertyContainer::PropertyName) { } @@ -1014,7 +1011,7 @@ } void -AudioCallbackPlaySource::setSoloModelSet(std::set s) +AudioCallbackPlaySource::setSoloModelSet(std::set s) { m_audioGenerator->setSoloModelSet(s); clearRingBuffers(); @@ -1664,7 +1661,7 @@ } } - for (std::set::iterator mi = m_models.begin(); + for (std::set::iterator mi = m_models.begin(); mi != m_models.end(); ++mi) { (void) m_audioGenerator->mixModel(*mi, chunkStart, diff -r a82b9d410393 -r 155008f1bf10 audio/AudioCallbackPlaySource.h --- a/audio/AudioCallbackPlaySource.h Fri Jun 14 17:19:37 2019 +0100 +++ b/audio/AudioCallbackPlaySource.h Wed Jul 17 14:25:41 2019 +0100 @@ -29,6 +29,7 @@ #include "base/Thread.h" #include "base/RealTime.h" +#include "data/model/Model.h" #include @@ -73,12 +74,12 @@ * models. The models must match in sample rate, but they don't * have to have identical numbers of channels. */ - virtual void addModel(Model *model); + virtual void addModel(ModelId model); /** * Remove a model. */ - virtual void removeModel(Model *model); + virtual void removeModel(ModelId model); /** * Remove all models. (Silence will ensue.) @@ -292,7 +293,7 @@ /** * Specify that only the given set of models should be played. */ - void setSoloModelSet(std::sets); + void setSoloModelSet(std::sets); /** * Specify that all models should be played as normal (if not @@ -325,9 +326,9 @@ void selectionChanged(); void playLoopModeChanged(); void playSelectionModeChanged(); - void playParametersChanged(PlayParameters *); + void playParametersChanged(int); void preferenceChanged(PropertyContainer::PropertyName); - void modelChangedWithin(sv_frame_t startFrame, sv_frame_t endFrame); + void modelChangedWithin(ModelId, sv_frame_t startFrame, sv_frame_t endFrame); protected: ViewManagerBase *m_viewManager; @@ -344,7 +345,7 @@ } }; - std::set m_models; + std::set m_models; RingBufferVector *m_readBuffers; RingBufferVector *m_writeBuffers; sv_frame_t m_readBufferFill; diff -r a82b9d410393 -r 155008f1bf10 audio/AudioGenerator.cpp --- a/audio/AudioGenerator.cpp Fri Jun 14 17:19:37 2019 +0100 +++ b/audio/AudioGenerator.cpp Wed Jul 17 14:25:41 2019 +0100 @@ -56,9 +56,9 @@ initialiseSampleDir(); connect(PlayParameterRepository::getInstance(), - SIGNAL(playClipIdChanged(const Playable *, QString)), + SIGNAL(playClipIdChanged(int, QString)), this, - SLOT(playClipIdChanged(const Playable *, QString))); + SLOT(playClipIdChanged(int, QString))); } AudioGenerator::~AudioGenerator() @@ -111,16 +111,19 @@ } bool -AudioGenerator::addModel(Model *model) +AudioGenerator::addModel(ModelId modelId) { + auto model = ModelById::get(modelId); + if (!model) return false; + if (!model->canPlay()) return false; + if (m_sourceSampleRate == 0) { m_sourceSampleRate = model->getSampleRate(); } else { - DenseTimeValueModel *dtvm = - dynamic_cast(model); + auto dtvm = std::dynamic_pointer_cast(model); if (dtvm) { m_sourceSampleRate = model->getSampleRate(); @@ -128,28 +131,31 @@ } } - const Playable *playable = model; - if (!playable || !playable->canPlay()) return 0; + auto parameters = + PlayParameterRepository::getInstance()->getPlayParameters + (modelId.untyped); - PlayParameters *parameters = - PlayParameterRepository::getInstance()->getPlayParameters(playable); + if (!parameters) { + SVCERR << "WARNING: Model with canPlay true is not known to PlayParameterRepository" << endl; + return false; + } bool willPlay = !parameters->isPlayMuted(); - if (usesClipMixer(model)) { - ClipMixer *mixer = makeClipMixerFor(model); + if (usesClipMixer(modelId)) { + ClipMixer *mixer = makeClipMixerFor(modelId); if (mixer) { QMutexLocker locker(&m_mutex); - m_clipMixerMap[model->getId()] = mixer; + m_clipMixerMap[modelId] = mixer; return willPlay; } } - if (usesContinuousSynth(model)) { - ContinuousSynth *synth = makeSynthFor(model); + if (usesContinuousSynth(modelId)) { + ContinuousSynth *synth = makeSynthFor(modelId); if (synth) { QMutexLocker locker(&m_mutex); - m_continuousSynthMap[model->getId()] = synth; + m_continuousSynthMap[modelId] = synth; return willPlay; } } @@ -158,74 +164,68 @@ } void -AudioGenerator::playClipIdChanged(const Playable *playable, QString) +AudioGenerator::playClipIdChanged(int playableId, QString) { - const Model *model = dynamic_cast(playable); - if (!model) { - cerr << "WARNING: AudioGenerator::playClipIdChanged: playable " - << playable << " is not a supported model type" - << endl; + ModelId modelId; + modelId.untyped = playableId; + + if (m_clipMixerMap.find(modelId) == m_clipMixerMap.end()) { return; } - if (m_clipMixerMap.find(model->getId()) == m_clipMixerMap.end()) { - return; - } - - ClipMixer *mixer = makeClipMixerFor(model); + ClipMixer *mixer = makeClipMixerFor(modelId); if (mixer) { QMutexLocker locker(&m_mutex); - m_clipMixerMap[model->getId()] = mixer; + ClipMixer *oldMixer = m_clipMixerMap[modelId]; + m_clipMixerMap[modelId] = mixer; + delete oldMixer; } } bool -AudioGenerator::usesClipMixer(const Model *model) +AudioGenerator::usesClipMixer(ModelId modelId) { bool clip = - (qobject_cast(model) || - qobject_cast(model)); + (ModelById::isa(modelId) || + ModelById::isa(modelId)); return clip; } bool -AudioGenerator::wantsQuieterClips(const Model *model) +AudioGenerator::wantsQuieterClips(ModelId modelId) { // 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(model)); + bool does = (ModelById::isa(modelId)); return does; } bool -AudioGenerator::usesContinuousSynth(const Model *model) +AudioGenerator::usesContinuousSynth(ModelId modelId) { - bool cont = - (qobject_cast(model)); + bool cont = (ModelById::isa(modelId)); return cont; } ClipMixer * -AudioGenerator::makeClipMixerFor(const Model *model) +AudioGenerator::makeClipMixerFor(ModelId modelId) { QString clipId; - const Playable *playable = model; - if (!playable || !playable->canPlay()) return nullptr; - - PlayParameters *parameters = - PlayParameterRepository::getInstance()->getPlayParameters(playable); + auto parameters = + PlayParameterRepository::getInstance()->getPlayParameters + (modelId.untyped); if (parameters) { clipId = parameters->getPlayClipId(); } #ifdef DEBUG_AUDIO_GENERATOR - std::cerr << "AudioGenerator::makeClipMixerFor(" << model << "): sample id = " << clipId << std::endl; + std::cerr << "AudioGenerator::makeClipMixerFor(" << modelId << "): sample id = " << clipId << std::endl; #endif if (clipId == "") { - SVDEBUG << "AudioGenerator::makeClipMixerFor(" << model << "): no sample, skipping" << endl; + SVDEBUG << "AudioGenerator::makeClipMixerFor(" << modelId << "): no sample, skipping" << endl; return nullptr; } @@ -237,7 +237,7 @@ QString clipPath = QString("%1/%2.wav").arg(m_sampleDir).arg(clipId); - double level = wantsQuieterClips(model) ? 0.5 : 1.0; + double level = wantsQuieterClips(modelId) ? 0.5 : 1.0; if (!mixer->loadClipData(clipPath, clipF0, level)) { delete mixer; return nullptr; @@ -251,11 +251,8 @@ } ContinuousSynth * -AudioGenerator::makeSynthFor(const Model *model) +AudioGenerator::makeSynthFor(ModelId) { - const Playable *playable = model; - if (!playable || !playable->canPlay()) return nullptr; - ContinuousSynth *synth = new ContinuousSynth(m_targetChannelCount, m_sourceSampleRate, m_processingBlockSize, @@ -269,20 +266,16 @@ } void -AudioGenerator::removeModel(Model *model) +AudioGenerator::removeModel(ModelId modelId) { - SparseOneDimensionalModel *sodm = - dynamic_cast(model); - if (!sodm) return; // nothing to do - QMutexLocker locker(&m_mutex); - if (m_clipMixerMap.find(sodm->getId()) == m_clipMixerMap.end()) { + if (m_clipMixerMap.find(modelId) == m_clipMixerMap.end()) { return; } - ClipMixer *mixer = m_clipMixerMap[sodm->getId()]; - m_clipMixerMap.erase(sodm->getId()); + ClipMixer *mixer = m_clipMixerMap[modelId]; + m_clipMixerMap.erase(modelId); delete mixer; } @@ -339,7 +332,7 @@ } void -AudioGenerator::setSoloModelSet(std::set s) +AudioGenerator::setSoloModelSet(std::set s) { QMutexLocker locker(&m_mutex); @@ -357,7 +350,7 @@ } sv_frame_t -AudioGenerator::mixModel(Model *model, +AudioGenerator::mixModel(ModelId modelId, sv_frame_t startFrame, sv_frame_t frameCount, float **buffer, sv_frame_t fadeIn, sv_frame_t fadeOut) @@ -369,25 +362,26 @@ QMutexLocker locker(&m_mutex); - Playable *playable = model; - if (!playable || !playable->canPlay()) return frameCount; + auto model = ModelById::get(modelId); + if (!model || !model->canPlay()) return frameCount; - PlayParameters *parameters = - PlayParameterRepository::getInstance()->getPlayParameters(playable); + auto parameters = + PlayParameterRepository::getInstance()->getPlayParameters + (modelId.untyped); if (!parameters) return frameCount; bool playing = !parameters->isPlayMuted(); if (!playing) { #ifdef DEBUG_AUDIO_GENERATOR - cout << "AudioGenerator::mixModel(" << model << "): muted" << endl; + cout << "AudioGenerator::mixModel(" << modelId << "): muted" << endl; #endif return frameCount; } if (m_soloing) { - if (m_soloModelSet.find(model) == m_soloModelSet.end()) { + if (m_soloModelSet.find(modelId) == m_soloModelSet.end()) { #ifdef DEBUG_AUDIO_GENERATOR - cout << "AudioGenerator::mixModel(" << model << "): not one of the solo'd models" << endl; + cout << "AudioGenerator::mixModel(" << modelId << "): not one of the solo'd models" << endl; #endif return frameCount; } @@ -396,35 +390,37 @@ float gain = parameters->getPlayGain(); float pan = parameters->getPlayPan(); - DenseTimeValueModel *dtvm = dynamic_cast(model); - if (dtvm) { - return mixDenseTimeValueModel(dtvm, startFrame, frameCount, + if (std::dynamic_pointer_cast(model)) { + return mixDenseTimeValueModel(modelId, startFrame, frameCount, buffer, gain, pan, fadeIn, fadeOut); } - if (usesClipMixer(model)) { - return mixClipModel(model, startFrame, frameCount, + if (usesClipMixer(modelId)) { + return mixClipModel(modelId, startFrame, frameCount, buffer, gain, pan); } - if (usesContinuousSynth(model)) { - return mixContinuousSynthModel(model, startFrame, frameCount, + if (usesContinuousSynth(modelId)) { + return mixContinuousSynthModel(modelId, startFrame, frameCount, buffer, gain, pan); } - std::cerr << "AudioGenerator::mixModel: WARNING: Model " << model << " of type " << model->getTypeName() << " is marked as playable, but I have no mechanism to play it" << std::endl; + std::cerr << "AudioGenerator::mixModel: WARNING: Model " << modelId << " of type " << model->getTypeName() << " is marked as playable, but I have no mechanism to play it" << std::endl; return frameCount; } sv_frame_t -AudioGenerator::mixDenseTimeValueModel(DenseTimeValueModel *dtvm, +AudioGenerator::mixDenseTimeValueModel(ModelId modelId, sv_frame_t startFrame, sv_frame_t frames, float **buffer, float gain, float pan, sv_frame_t fadeIn, sv_frame_t fadeOut) { sv_frame_t maxFrames = frames + std::max(fadeIn, fadeOut); + auto dtvm = ModelById::getAs(modelId); + if (!dtvm) return 0; + int modelChannels = dtvm->getChannelCount(); if (m_channelBufSiz < maxFrames || m_channelBufCount < modelChannels) { @@ -519,13 +515,15 @@ } sv_frame_t -AudioGenerator::mixClipModel(Model *model, +AudioGenerator::mixClipModel(ModelId modelId, sv_frame_t startFrame, sv_frame_t frames, float **buffer, float gain, float pan) { - ClipMixer *clipMixer = m_clipMixerMap[model->getId()]; + ClipMixer *clipMixer = m_clipMixerMap[modelId]; if (!clipMixer) return 0; + auto exportable = ModelById::getAs(modelId); + int blocks = int(frames / m_processingBlockSize); //!!! todo: the below -- it matters @@ -551,7 +549,7 @@ ClipMixer::NoteStart on; ClipMixer::NoteEnd off; - NoteOffSet ¬eOffs = m_noteOffs[model->getId()]; + NoteOffSet ¬eOffs = m_noteOffs[modelId]; float **bufferIndexes = new float *[m_targetChannelCount]; @@ -562,7 +560,6 @@ sv_frame_t reqStart = startFrame + i * m_processingBlockSize; NoteList notes; - NoteExportable *exportable = dynamic_cast(model); if (exportable) { notes = exportable->getNotesStartingWithin(reqStart, m_processingBlockSize); @@ -677,18 +674,19 @@ } sv_frame_t -AudioGenerator::mixContinuousSynthModel(Model *model, +AudioGenerator::mixContinuousSynthModel(ModelId modelId, sv_frame_t startFrame, sv_frame_t frames, float **buffer, float gain, float pan) { - ContinuousSynth *synth = m_continuousSynthMap[model->getId()]; + ContinuousSynth *synth = m_continuousSynthMap[modelId]; if (!synth) return 0; // only type we support here at the moment - SparseTimeValueModel *stvm = qobject_cast(model); + auto stvm = ModelById::getAs(modelId); + if (!stvm) return 0; if (stvm->getScaleUnits() != "Hz") return 0; int blocks = int(frames / m_processingBlockSize); diff -r a82b9d410393 -r 155008f1bf10 audio/AudioGenerator.h --- a/audio/AudioGenerator.h Fri Jun 14 17:19:37 2019 +0100 +++ b/audio/AudioGenerator.h Wed Jul 17 14:25:41 2019 +0100 @@ -48,12 +48,12 @@ * played. The model will be added regardless of the return * value. */ - virtual bool addModel(Model *model); + virtual bool addModel(ModelId model); /** * Remove a model. */ - virtual void removeModel(Model *model); + virtual void removeModel(ModelId model); /** * Remove all models. @@ -81,7 +81,7 @@ /** * Mix a single model into an output buffer. */ - virtual sv_frame_t mixModel(Model *model, + virtual sv_frame_t mixModel(ModelId model, sv_frame_t startFrame, sv_frame_t frameCount, float **buffer, @@ -91,7 +91,7 @@ /** * Specify that only the given set of models should be played. */ - virtual void setSoloModelSet(std::sets); + virtual void setSoloModelSet(std::sets); /** * Specify that all models should be played as normal (if not @@ -100,7 +100,7 @@ virtual void clearSoloModelSet(); protected slots: - void playClipIdChanged(const Playable *, QString); + void playClipIdChanged(int playableId, QString); protected: sv_samplerate_t m_sourceSampleRate; @@ -108,7 +108,7 @@ int m_waveType; bool m_soloing; - std::set m_soloModelSet; + std::set m_soloModelSet; struct NoteOff { @@ -140,12 +140,12 @@ }; - typedef std::map ClipMixerMap; + typedef std::map ClipMixerMap; typedef std::multiset NoteOffSet; - typedef std::map NoteOffMap; + typedef std::map NoteOffMap; - typedef std::map ContinuousSynthMap; + typedef std::map ContinuousSynthMap; QMutex m_mutex; @@ -155,25 +155,25 @@ ContinuousSynthMap m_continuousSynthMap; - bool usesClipMixer(const Model *); - bool wantsQuieterClips(const Model *); - bool usesContinuousSynth(const Model *); + bool usesClipMixer(ModelId); + bool wantsQuieterClips(ModelId); + bool usesContinuousSynth(ModelId); - ClipMixer *makeClipMixerFor(const Model *model); - ContinuousSynth *makeSynthFor(const Model *model); + ClipMixer *makeClipMixerFor(ModelId model); + ContinuousSynth *makeSynthFor(ModelId model); static void initialiseSampleDir(); virtual sv_frame_t mixDenseTimeValueModel - (DenseTimeValueModel *model, sv_frame_t startFrame, sv_frame_t frameCount, + (ModelId model, sv_frame_t startFrame, sv_frame_t frameCount, float **buffer, float gain, float pan, sv_frame_t fadeIn, sv_frame_t fadeOut); virtual sv_frame_t mixClipModel - (Model *model, sv_frame_t startFrame, sv_frame_t frameCount, + (ModelId model, sv_frame_t startFrame, sv_frame_t frameCount, float **buffer, float gain, float pan); virtual sv_frame_t mixContinuousSynthModel - (Model *model, sv_frame_t startFrame, sv_frame_t frameCount, + (ModelId model, sv_frame_t startFrame, sv_frame_t frameCount, float **buffer, float gain, float pan); static const sv_frame_t m_processingBlockSize; diff -r a82b9d410393 -r 155008f1bf10 framework/Align.cpp --- a/framework/Align.cpp Fri Jun 14 17:19:37 2019 +0100 +++ b/framework/Align.cpp Wed Jul 17 14:25:41 2019 +0100 @@ -33,7 +33,7 @@ #include bool -Align::alignModel(Document *doc, Model *ref, Model *other, QString &error) +Align::alignModel(Document *doc, ModelId ref, ModelId other, QString &error) { QSettings settings; settings.beginGroup("Preferences"); @@ -89,76 +89,89 @@ } bool -Align::alignModelViaTransform(Document *doc, Model *ref, Model *other, +Align::alignModelViaTransform(Document *doc, + ModelId referenceId, + ModelId otherId, QString &error) { QMutexLocker locker (&m_mutex); - - RangeSummarisableTimeValueModel *reference = qobject_cast - (ref); - - RangeSummarisableTimeValueModel *rm = qobject_cast - (other); - if (!reference || !rm) return false; // but this should have been tested already + auto reference = + ModelById::getAs(referenceId); + auto other = + ModelById::getAs(otherId); + + if (!reference || !other) return false; - // This involves creating either three or four new models: + // This involves creating a number of new models: // // 1. an AggregateWaveModel to provide the mixdowns of the main // model and the new model in its two channels, as input to the - // MATCH plugin + // MATCH plugin. We just call this one aggregateModel // // 2a. a SparseTimeValueModel which will be automatically created // by FeatureExtractionModelTransformer when running the // TuningDifference plugin to receive the relative tuning of the // second model (if pitch-aware alignment is enabled in the - // preferences) + // preferences). We call this tuningDiffOutputModel. // // 2b. a SparseTimeValueModel which will be automatically created // by FeatureExtractionPluginTransformer when running the MATCH - // plugin to perform alignment (so containing the alignment path) + // plugin to perform alignment (so containing the alignment path). + // We call this one pathOutputModel. // - // 3. an AlignmentModel, which stores the path model and carries - // out alignment lookups on it. + // 2c. a SparseTimeValueModel used solely to provide faked + // completion information to the AlignmentModel while a + // TuningDifference calculation is going on. We call this + // preparatoryModel. // - // The AggregateWaveModel [1] is registered with the document, - // which deletes it when it is invalidated (when one of its - // components is deleted). The SparseTimeValueModel [2a] is reused - // by us when starting the alignment process proper, and is then - // deleted by us. The SparseTimeValueModel [2b] is passed to the - // AlignmentModel, which takes ownership of it. The AlignmentModel - // is attached to the new model we are aligning, which also takes - // ownership of it. The only one of these models that we need to - // delete here is the SparseTimeValueModel [2a]. + // 3. an AlignmentModel, which stores the path and carries out + // alignment lookups on it. We just call this one alignmentModel. // - // (We also create a sneaky additional SparseTimeValueModel - // temporarily so we can attach completion information to it - - // this is quite unnecessary from the perspective of simply - // producing the results.) + // Models 1 and 3 are registered with the document, which will + // eventually release them. We don't release them here except in + // the case where an activity fails before the point where we + // would otherwise have registered them with the document. + // + // Models 2a (tuningDiffOutputModel), 2b (pathOutputModel) and 2c + // (preparatoryModel) are not registered with the document. Model + // 2b (pathOutputModel) is not registered because we do not have a + // stable reference to the document at the point where it is + // created. Model 2c (preparatoryModel) is not registered because + // it is a bodge that we are embarrassed about, so we try to + // manage it ourselves without anyone else noticing. Model 2a is + // not registered for symmetry with the other two. These have to + // be released by us when finished with, but their lifespans do + // not extend beyond the end of the alignment procedure, so this + // should be ok. AggregateWaveModel::ChannelSpecList components; - components.push_back(AggregateWaveModel::ModelChannelSpec - (reference, -1)); + components.push_back + (AggregateWaveModel::ModelChannelSpec(referenceId, -1)); - components.push_back(AggregateWaveModel::ModelChannelSpec - (rm, -1)); + components.push_back + (AggregateWaveModel::ModelChannelSpec(otherId, -1)); - AggregateWaveModel *aggregateModel = new AggregateWaveModel(components); - doc->addAggregateModel(aggregateModel); + auto aggregateModel = std::make_shared(components); + auto aggregateModelId = ModelById::add(aggregateModel); + doc->addNonDerivedModel(aggregateModelId); - AlignmentModel *alignmentModel = - new AlignmentModel(reference, other, nullptr); + auto alignmentModel = std::make_shared + (referenceId, otherId, ModelId()); + auto alignmentModelId = ModelById::add(alignmentModel); TransformId tdId = getTuningDifferenceTransformName(); if (tdId == "") { - if (beginTransformDrivenAlignment(aggregateModel, alignmentModel)) { - rm->setAlignment(alignmentModel); + if (beginTransformDrivenAlignment(aggregateModelId, + alignmentModelId)) { + other->setAlignment(alignmentModelId); + doc->addNonDerivedModel(alignmentModelId); } else { error = alignmentModel->getError(); - delete alignmentModel; + ModelById::release(alignmentModel); return false; } @@ -181,86 +194,79 @@ ModelTransformerFactory *mtf = ModelTransformerFactory::getInstance(); QString message; - Model *transformOutput = mtf->transform(transform, aggregateModel, message); + ModelId tuningDiffOutputModelId = mtf->transform(transform, + aggregateModelId, + message); - SparseTimeValueModel *tdout = dynamic_cast - (transformOutput); - - if (!tdout) { + auto tuningDiffOutputModel = + ModelById::getAs(tuningDiffOutputModelId); + if (!tuningDiffOutputModel) { SVCERR << "Align::alignModel: ERROR: Failed to create tuning-difference output model (no Tuning Difference plugin?)" << endl; - delete tdout; error = message; + ModelById::release(alignmentModel); return false; } - rm->setAlignment(alignmentModel); + other->setAlignment(alignmentModelId); + doc->addNonDerivedModel(alignmentModelId); - connect(tdout, SIGNAL(completionChanged()), - this, SLOT(tuningDifferenceCompletionChanged())); + connect(tuningDiffOutputModel.get(), + SIGNAL(completionChanged(ModelId)), + this, SLOT(tuningDifferenceCompletionChanged(ModelId))); TuningDiffRec rec; - rec.input = aggregateModel; - rec.alignment = alignmentModel; - - connect(aggregateModel, SIGNAL(aboutToBeDeleted()), - this, SLOT(aggregateModelAboutToBeDeleted())); + rec.input = aggregateModelId; + rec.alignment = alignmentModelId; // This model exists only so that the AlignmentModel can get a // completion value from somewhere while the tuning difference // calculation is going on - rec.preparatory = new SparseTimeValueModel - (aggregateModel->getSampleRate(), 1);; - rec.preparatory->setCompletion(0); + auto preparatoryModel = std::make_shared + (aggregateModel->getSampleRate(), 1); + auto preparatoryModelId = ModelById::add(preparatoryModel); + preparatoryModel->setCompletion(0); + rec.preparatory = preparatoryModelId; alignmentModel->setPathFrom(rec.preparatory); - m_pendingTuningDiffs[tdout] = rec; + m_pendingTuningDiffs[tuningDiffOutputModelId] = rec; } return true; } void -Align::aggregateModelAboutToBeDeleted() +Align::tuningDifferenceCompletionChanged(ModelId tuningDiffOutputModelId) { - SVCERR << "Align::aggregateModelAboutToBeDeleted" << endl; - - QObject *s = sender(); - AggregateWaveModel *awm = qobject_cast(s); - if (!awm) return; QMutexLocker locker(&m_mutex); - SVCERR << "Align::aggregateModelAboutToBeDeleted: awm = " << awm - << endl; - - for (const auto &p : m_pendingTuningDiffs) { - if (p.second.input == awm) { - SVCERR << "we have a record of this, getting rid of it" << endl; - m_pendingTuningDiffs.erase(p.first); - return; - } - } -} - -void -Align::tuningDifferenceCompletionChanged() -{ - QMutexLocker locker (&m_mutex); - - SparseTimeValueModel *td = qobject_cast(sender()); - if (!td) return; - - if (m_pendingTuningDiffs.find(td) == m_pendingTuningDiffs.end()) { + if (m_pendingTuningDiffs.find(tuningDiffOutputModelId) == + m_pendingTuningDiffs.end()) { SVCERR << "ERROR: Align::tuningDifferenceCompletionChanged: Model " - << td << " not found in pending tuning diff map!" << endl; + << tuningDiffOutputModelId + << " not found in pending tuning diff map!" << endl; return; } - TuningDiffRec rec = m_pendingTuningDiffs[td]; + auto tuningDiffOutputModel = + ModelById::getAs(tuningDiffOutputModelId); + if (!tuningDiffOutputModel) { + SVCERR << "WARNING: Align::tuningDifferenceCompletionChanged: Model " + << tuningDiffOutputModelId + << " not known as SparseTimeValueModel" << endl; + return; + } + TuningDiffRec rec = m_pendingTuningDiffs[tuningDiffOutputModelId]; + + auto alignmentModel = ModelById::getAs(rec.alignment); + if (!alignmentModel) { + SVCERR << "WARNING: Align::tuningDifferenceCompletionChanged:" + << "alignment model has disappeared" << endl; + return; + } + int completion = 0; - bool done = td->isReady(&completion); - -// SVCERR << "Align::tuningDifferenceCompletionChanged: done = " << done << ", completion = " << completion << endl; + bool done = tuningDiffOutputModel->isReady(&completion); if (!done) { // This will be the completion the alignment model reports, @@ -268,39 +274,54 @@ // 99 (not 100!) and then back to 0 again when we start // calculating the actual path in the following phase int clamped = (completion == 100 ? 99 : completion); -// SVCERR << "Align::tuningDifferenceCompletionChanged: setting rec.preparatory completion to " << clamped << endl; - rec.preparatory->setCompletion(clamped); + auto preparatoryModel = + ModelById::getAs(rec.preparatory); + if (preparatoryModel) { + preparatoryModel->setCompletion(clamped); + } return; } float tuningFrequency = 440.f; - if (!td->isEmpty()) { - tuningFrequency = td->getAllEvents()[0].getValue(); + if (!tuningDiffOutputModel->isEmpty()) { + tuningFrequency = tuningDiffOutputModel->getAllEvents()[0].getValue(); SVCERR << "Align::tuningDifferenceCompletionChanged: Reported tuning frequency = " << tuningFrequency << endl; } else { SVCERR << "Align::tuningDifferenceCompletionChanged: No tuning frequency reported" << endl; } - m_pendingTuningDiffs.erase(td); - td->aboutToDelete(); - delete td; - - rec.alignment->setPathFrom(nullptr); + ModelById::release(tuningDiffOutputModel); + + alignmentModel->setPathFrom({}); // replace preparatoryModel + ModelById::release(rec.preparatory); + rec.preparatory = {}; + + m_pendingTuningDiffs.erase(tuningDiffOutputModelId); beginTransformDrivenAlignment (rec.input, rec.alignment, tuningFrequency); } bool -Align::beginTransformDrivenAlignment(AggregateWaveModel *aggregateModel, - AlignmentModel *alignmentModel, +Align::beginTransformDrivenAlignment(ModelId aggregateModelId, + ModelId alignmentModelId, float tuningFrequency) { TransformId id = getAlignmentTransformName(); TransformFactory *tf = TransformFactory::getInstance(); + auto aggregateModel = + ModelById::getAs(aggregateModelId); + auto alignmentModel = + ModelById::getAs(alignmentModelId); + + if (!aggregateModel || !alignmentModel) { + SVCERR << "Align::alignModel: ERROR: One or other of the aggregate & alignment models has disappeared" << endl; + return false; + } + Transform transform = tf->getDefaultTransformFor (id, aggregateModel->getSampleRate()); @@ -317,90 +338,97 @@ ModelTransformerFactory *mtf = ModelTransformerFactory::getInstance(); QString message; - Model *transformOutput = mtf->transform - (transform, aggregateModel, message); + ModelId pathOutputModelId = mtf->transform + (transform, aggregateModelId, message); - if (!transformOutput) { + if (pathOutputModelId.isNone()) { transform.setStepSize(0); - transformOutput = mtf->transform - (transform, aggregateModel, message); + pathOutputModelId = mtf->transform + (transform, aggregateModelId, message); } - SparseTimeValueModel *path = dynamic_cast - (transformOutput); + auto pathOutputModel = + ModelById::getAs(pathOutputModelId); //!!! callers will need to be updated to get error from //!!! alignment model after initial call - if (!path) { + if (!pathOutputModel) { SVCERR << "Align::alignModel: ERROR: Failed to create alignment path (no MATCH plugin?)" << endl; - delete transformOutput; alignmentModel->setError(message); return false; } - path->setCompletion(0); - alignmentModel->setPathFrom(path); + pathOutputModel->setCompletion(0); + alignmentModel->setPathFrom(pathOutputModelId); - connect(alignmentModel, SIGNAL(completionChanged()), - this, SLOT(alignmentCompletionChanged())); + m_pendingAlignments[alignmentModelId] = pathOutputModelId; + + connect(alignmentModel.get(), SIGNAL(completionChanged(ModelId)), + this, SLOT(alignmentCompletionChanged(ModelId))); return true; } void -Align::alignmentCompletionChanged() +Align::alignmentCompletionChanged(ModelId alignmentModelId) { QMutexLocker locker (&m_mutex); - - AlignmentModel *am = qobject_cast(sender()); - if (!am) return; - if (am->isReady()) { - disconnect(am, SIGNAL(completionChanged()), - this, SLOT(alignmentCompletionChanged())); - emit alignmentComplete(am); + + auto alignmentModel = ModelById::getAs(alignmentModelId); + + if (alignmentModel && alignmentModel->isReady()) { + + if (m_pendingAlignments.find(alignmentModelId) != + m_pendingAlignments.end()) { + ModelId pathOutputModelId = m_pendingAlignments[alignmentModelId]; + ModelById::release(pathOutputModelId); + m_pendingAlignments.erase(alignmentModelId); + } + + disconnect(alignmentModel.get(), + SIGNAL(completionChanged(ModelId)), + this, SLOT(alignmentCompletionChanged(ModelId))); + emit alignmentComplete(alignmentModelId); } } bool -Align::alignModelViaProgram(Document *, Model *ref, Model *other, - QString program, QString &error) +Align::alignModelViaProgram(Document *doc, + ModelId referenceId, + ModelId otherId, + QString program, + QString &error) { QMutexLocker locker (&m_mutex); - WaveFileModel *reference = qobject_cast(ref); - WaveFileModel *rm = qobject_cast(other); - - if (!reference || !rm) { - return false; // but this should have been tested already - } - - while (!reference->isReady(nullptr) || !rm->isReady(nullptr)) { - qApp->processEvents(); - } - // Run an external program, passing to it paths to the main // model's audio file and the new model's audio file. It returns // the path in CSV form through stdout. - ReadOnlyWaveFileModel *roref = qobject_cast(reference); - ReadOnlyWaveFileModel *rorm = qobject_cast(rm); - if (!roref || !rorm) { + auto reference = ModelById::getAs(referenceId); + auto other = ModelById::getAs(otherId); + if (!reference || !other) { SVCERR << "ERROR: Align::alignModelViaProgram: Can't align non-read-only models via program (no local filename available)" << endl; return false; } + + while (!reference->isReady(nullptr) || !other->isReady(nullptr)) { + qApp->processEvents(); + } - QString refPath = roref->getLocalFilename(); - QString otherPath = rorm->getLocalFilename(); + QString refPath = reference->getLocalFilename(); + QString otherPath = other->getLocalFilename(); if (refPath == "" || otherPath == "") { error = "Failed to find local filepath for wave-file model"; return false; } - AlignmentModel *alignmentModel = - new AlignmentModel(reference, other, nullptr); - rm->setAlignment(alignmentModel); + auto alignmentModel = + std::make_shared(referenceId, otherId, ModelId()); + auto alignmentModelId = ModelById::add(alignmentModel); + other->setAlignment(alignmentModelId); QProcess *process = new QProcess; QStringList args; @@ -409,7 +437,7 @@ connect(process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(alignmentProgramFinished(int, QProcess::ExitStatus))); - m_pendingProcesses[process] = alignmentModel; + m_pendingProcesses[process] = alignmentModelId; process->start(program, args); bool success = process->waitForStarted(); @@ -419,10 +447,12 @@ << endl; error = "Alignment program could not be started"; m_pendingProcesses.erase(process); - rm->setAlignment(nullptr); // deletes alignmentModel as well + other->setAlignment({}); + ModelById::release(alignmentModelId); delete process; } + doc->addNonDerivedModel(alignmentModelId); return success; } @@ -441,7 +471,9 @@ return; } - AlignmentModel *alignmentModel = m_pendingProcesses[process]; + ModelId alignmentModelId = m_pendingProcesses[process]; + auto alignmentModel = ModelById::getAs(alignmentModelId); + if (!alignmentModel) return; if (exitCode == 0 && status == 0) { @@ -471,31 +503,40 @@ goto done; } + //!!! to use ById? + Model *csvOutput = reader.load(); - SparseTimeValueModel *path = qobject_cast(csvOutput); + SparseTimeValueModel *path = + qobject_cast(csvOutput); if (!path) { SVCERR << "ERROR: Align::alignmentProgramFinished: Output did not convert to sparse time-value model" << endl; alignmentModel->setError ("Output of program did not produce sparse time-value model"); + delete csvOutput; goto done; } - + if (path->isEmpty()) { SVCERR << "ERROR: Align::alignmentProgramFinished: Output contained no mappings" << endl; alignmentModel->setError ("Output of alignment program contained no mappings"); + delete path; goto done; } SVCERR << "Align::alignmentProgramFinished: Setting alignment path (" << path->getEventCount() << " point(s))" << endl; - alignmentModel->setPathFrom(path); + auto pathId = + ModelById::add(std::shared_ptr(path)); + alignmentModel->setPathFrom(pathId); - emit alignmentComplete(alignmentModel); + emit alignmentComplete(alignmentModelId); + + ModelById::release(pathId); } else { SVCERR << "ERROR: Align::alignmentProgramFinished: Aligner program " diff -r a82b9d410393 -r 155008f1bf10 framework/Align.h --- a/framework/Align.h Fri Jun 14 17:19:37 2019 +0100 +++ b/framework/Align.h Wed Jul 17 14:25:41 2019 +0100 @@ -4,7 +4,6 @@ Sonic Visualiser An audio file viewer and annotation editor. Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam and QMUL. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -22,7 +21,8 @@ #include #include -class Model; +#include "data/model/Model.h" + class AlignmentModel; class SparseTimeValueModel; class AggregateWaveModel; @@ -63,18 +63,18 @@ * lifespan. */ bool alignModel(Document *doc, - Model *reference, - Model *toAlign, + ModelId reference, + ModelId toAlign, QString &error); bool alignModelViaTransform(Document *doc, - Model *reference, - Model *toAlign, + ModelId reference, + ModelId toAlign, QString &error); bool alignModelViaProgram(Document *doc, - Model *reference, - Model *toAlign, + ModelId reference, + ModelId toAlign, QString program, QString &error); @@ -90,35 +90,39 @@ * reference and other models can be queried from the alignment * model. */ - void alignmentComplete(AlignmentModel *alignment); + void alignmentComplete(ModelId alignmentModel); // an AlignmentModel private slots: - void alignmentCompletionChanged(); - void tuningDifferenceCompletionChanged(); + void alignmentCompletionChanged(ModelId); + void tuningDifferenceCompletionChanged(ModelId); void alignmentProgramFinished(int, QProcess::ExitStatus); - void aggregateModelAboutToBeDeleted(); private: static QString getAlignmentTransformName(); static QString getTuningDifferenceTransformName(); - bool beginTransformDrivenAlignment(AggregateWaveModel *, - AlignmentModel *, + bool beginTransformDrivenAlignment(ModelId, // an AggregateWaveModel + ModelId, // an AlignmentModel float tuningFrequency = 0.f); QMutex m_mutex; struct TuningDiffRec { - AggregateWaveModel *input; - AlignmentModel *alignment; - SparseTimeValueModel *preparatory; + ModelId input; // an AggregateWaveModel + ModelId alignment; // an AlignmentModel + ModelId preparatory; // a SparseTimeValueModel }; - // tuning-difference output model -> data needed for subsequent alignment - std::map m_pendingTuningDiffs; + // tuning-difference output model (a SparseTimeValueModel) -> data + // needed for subsequent alignment + std::map m_pendingTuningDiffs; - // external alignment subprocess -> model into which to stuff the results - std::map m_pendingProcesses; + // alignment model id -> path output model id + std::map m_pendingAlignments; + + // external alignment subprocess -> model into which to stuff the + // results (an AlignmentModel) + std::map m_pendingProcesses; }; #endif diff -r a82b9d410393 -r 155008f1bf10 framework/Document.cpp --- a/framework/Document.cpp Fri Jun 14 17:19:37 2019 +0100 +++ b/framework/Document.cpp Wed Jul 17 14:25:41 2019 +0100 @@ -48,23 +48,17 @@ //!!! still need to handle command history, documentRestored/documentModified Document::Document() : - m_mainModel(nullptr), m_autoAlignment(false), m_align(new Align()), m_isIncomplete(false) { - connect(this, - SIGNAL(modelAboutToBeDeleted(Model *)), - ModelTransformerFactory::getInstance(), - SLOT(modelAboutToBeDeleted(Model *))); - connect(ModelTransformerFactory::getInstance(), SIGNAL(transformFailed(QString, QString)), this, SIGNAL(modelGenerationFailed(QString, QString))); - connect(m_align, SIGNAL(alignmentComplete(AlignmentModel *)), - this, SIGNAL(alignmentComplete(AlignmentModel *))); + connect(m_align, SIGNAL(alignmentComplete(ModelId)), + this, SIGNAL(alignmentComplete(ModelId))); } Document::~Document() @@ -79,43 +73,42 @@ CommandHistory::getInstance()->clear(); #ifdef DEBUG_DOCUMENT - SVDEBUG << "Document::~Document: about to delete layers" << endl; + SVCERR << "Document::~Document: about to delete layers" << endl; #endif while (!m_layers.empty()) { deleteLayer(*m_layers.begin(), true); } - if (!m_models.empty()) { - SVDEBUG << "Document::~Document: WARNING: " - << m_models.size() << " model(s) still remain -- " - << "should have been garbage collected when deleting layers" - << endl; - for (ModelRecord &rec: m_models) { - Model *model = rec.model; - if (model == m_mainModel) { - // just in case! - SVDEBUG << "Document::~Document: WARNING: Main model is also" - << " in models list!" << endl; - } else if (model) { - model->aboutToDelete(); - emit modelAboutToBeDeleted(model); - delete model; - } - } - m_models.clear(); +#ifdef DEBUG_DOCUMENT + SVCERR << "Document::~Document: about to release normal models" << endl; +#endif + for (auto mr: m_models) { + ModelById::release(mr.first); } #ifdef DEBUG_DOCUMENT - SVDEBUG << "Document::~Document: About to get rid of main model" - << endl; + SVCERR << "Document::~Document: about to release aggregate models" << endl; #endif - if (m_mainModel) { - m_mainModel->aboutToDelete(); - emit modelAboutToBeDeleted(m_mainModel); + for (auto m: m_aggregateModels) { + ModelById::release(m); } - emit mainModelChanged(nullptr); - delete m_mainModel; +#ifdef DEBUG_DOCUMENT + SVCERR << "Document::~Document: about to release alignment models" << endl; +#endif + for (auto m: m_alignmentModels) { + ModelById::release(m); + } + +#ifdef DEBUG_DOCUMENT + SVCERR << "Document::~Document: about to release main model" << endl; +#endif + if (!m_mainModel.isNone()) { + ModelById::release(m_mainModel); + } + + m_mainModel = {}; + emit mainModelChanged({}); } Layer * @@ -148,10 +141,10 @@ } Layer * -Document::createImportedLayer(Model *model) +Document::createImportedLayer(ModelId modelId) { LayerFactory::LayerTypeSet types = - LayerFactory::getInstance()->getValidLayerTypes(model); + LayerFactory::getInstance()->getValidLayerTypes(modelId); if (types.empty()) { SVCERR << "WARNING: Document::importLayer: no valid display layer for model" << endl; @@ -166,8 +159,8 @@ newLayer->setObjectName(getUniqueLayerName(newLayer->objectName())); - addImportedModel(model); - setModel(newLayer, model); + addNonDerivedModel(modelId); + setModel(newLayer, modelId); //!!! and all channels setChannel(newLayer, -1); @@ -186,20 +179,20 @@ Layer * Document::createEmptyLayer(LayerFactory::LayerType type) { - if (!m_mainModel) return nullptr; + if (m_mainModel.isNone()) return nullptr; - Model *newModel = + auto newModel = LayerFactory::getInstance()->createEmptyModel(type, m_mainModel); if (!newModel) return nullptr; - + Layer *newLayer = createLayer(type); if (!newLayer) { - delete newModel; return nullptr; } - addImportedModel(newModel); - setModel(newLayer, newModel); + auto newModelId = ModelById::add(newModel); + addNonDerivedModel(newModelId); + setModel(newLayer, newModelId); return newLayer; } @@ -234,7 +227,8 @@ const ModelTransformer::Input &input) { QString message; - vector newModels = addDerivedModels(transforms, input, message, nullptr); + vector newModels = + addDerivedModels(transforms, input, message, nullptr); if (newModels.empty()) { //!!! This identifier may be wrong! @@ -246,7 +240,7 @@ } QStringList names; - for (int i = 0; i < (int)newModels.size(); ++i) { + for (int i = 0; in_range_for(newModels, i); ++i) { names.push_back(getUniqueLayerName (TransformFactory::getInstance()-> getTransformFriendlyName @@ -275,13 +269,13 @@ } void - moreModelsAvailable(vector models) override { + moreModelsAvailable(vector models) override { SVDEBUG << "AdditionalModelConverter::moreModelsAvailable: " << models.size() << " model(s)" << endl; // We can't automatically regenerate the additional models on - // reload -- we should delete them instead + // reload - so they go in m_additionalModels instead of m_models QStringList names; - foreach (Model *model, models) { - m_doc->addAdditionalModel(model); + foreach (ModelId modelId, models) { + m_doc->addAdditionalModel(modelId); names.push_back(QString()); } vector layers = m_doc->createLayersForDerivedModels @@ -297,15 +291,6 @@ delete this; } - void cancel() { - foreach (Layer *layer, m_primary) { - Model *model = layer->getModel(); - if (model) { - model->abandon(); - } - } - } - private: Document *m_doc; vector m_primary; @@ -321,11 +306,11 @@ AdditionalModelConverter *amc = new AdditionalModelConverter(this, handler); - vector newModels = addDerivedModels + vector newModels = addDerivedModels (transforms, input, message, amc); QStringList names; - for (int i = 0; i < (int)newModels.size(); ++i) { + for (int i = 0; in_range_for(newModels, i); ++i) { names.push_back(getUniqueLayerName (TransformFactory::getInstance()-> getTransformFriendlyName @@ -348,37 +333,29 @@ return amc; } -void -Document::cancelAsyncLayerCreation(Document::LayerCreationAsyncHandle h) -{ - AdditionalModelConverter *conv = static_cast(h); - conv->cancel(); -} - vector -Document::createLayersForDerivedModels(vector newModels, +Document::createLayersForDerivedModels(vector newModels, QStringList names) { vector layers; - for (int i = 0; i < (int)newModels.size(); ++i) { + for (int i = 0; in_range_for(newModels, i); ++i) { - Model *newModel = newModels[i]; + ModelId newModelId = newModels[i]; LayerFactory::LayerTypeSet types = - LayerFactory::getInstance()->getValidLayerTypes(newModel); + LayerFactory::getInstance()->getValidLayerTypes(newModelId); if (types.empty()) { SVCERR << "WARNING: Document::createLayerForTransformer: no valid display layer for output of transform " << names[i] << endl; - //!!! inadequate cleanup: - deleteModelFromList(newModel); + releaseModel(newModelId); return vector(); } //!!! for now, just use the first suitable layer type Layer *newLayer = createLayer(*types.begin()); - setModel(newLayer, newModel); + setModel(newLayer, newModelId); //!!! We need to clone the model when adding the layer, so that it //can be edited without affecting other layers that are based on @@ -407,13 +384,14 @@ } void -Document::setMainModel(WaveFileModel *model) +Document::setMainModel(ModelId modelId) { - Model *oldMainModel = m_mainModel; - m_mainModel = model; + ModelId oldMainModel = m_mainModel; + m_mainModel = modelId; emit modelAdded(m_mainModel); - if (model) { + + if (auto model = ModelById::get(modelId)) { emit activity(tr("Set main model to %1").arg(model->objectName())); } else { emit activity(tr("Clear main model")); @@ -433,7 +411,7 @@ << m_layers.size() << " layers" << endl; SVDEBUG << "Models now: "; for (const auto &r: m_models) { - SVDEBUG << r.model << " "; + SVDEBUG << r.first << " "; } SVDEBUG << endl; SVDEBUG << "Old main model: " << oldMainModel << endl; @@ -441,15 +419,14 @@ for (Layer *layer: m_layers) { - Model *model = layer->getModel(); + ModelId modelId = layer->getModel(); #ifdef DEBUG_DOCUMENT SVDEBUG << "Document::setMainModel: inspecting model " - << (model ? model->objectName(): "(null)") << " in layer " - << layer->objectName() << endl; + << modelId << " in layer " << layer->objectName() << endl; #endif - if (model == oldMainModel) { + if (modelId == oldMainModel) { #ifdef DEBUG_DOCUMENT SVDEBUG << "... it uses the old main model, replacing" << endl; #endif @@ -457,7 +434,7 @@ continue; } - if (!model) { + if (modelId.isNone()) { SVCERR << "WARNING: Document::setMainModel: Null model in layer " << layer << endl; // get rid of this hideous degenerate @@ -465,19 +442,17 @@ continue; } - auto mitr = findModelInList(model); - - if (mitr == m_models.end()) { + if (m_models.find(modelId) == m_models.end()) { SVCERR << "WARNING: Document::setMainModel: Unknown model " - << model << " in layer " << layer << endl; + << modelId << " in layer " << layer << endl; // and this one obsoleteLayers.push_back(layer); continue; } - ModelRecord record = *mitr; + ModelRecord record = m_models[modelId]; - if (record.source && (record.source == oldMainModel)) { + if (!record.source.isNone() && (record.source == oldMainModel)) { #ifdef DEBUG_DOCUMENT SVDEBUG << "... it uses a model derived from the old main model, regenerating" << endl; @@ -493,13 +468,13 @@ //the main model has changed. QString message; - Model *replacementModel = + ModelId replacementModel = addDerivedModel(transform, ModelTransformer::Input (m_mainModel, record.channel), message); - if (!replacementModel) { + if (replacementModel.isNone()) { SVCERR << "WARNING: Document::setMainModel: Failed to regenerate model for transform \"" << transformId << "\"" << " in layer " << layer << endl; if (failedTransformers.find(transformId) @@ -517,15 +492,12 @@ message); } #ifdef DEBUG_DOCUMENT - SVDEBUG << "Replacing model " << model << " (type " - << typeid(*model).name() << ") with model " - << replacementModel << " (type " - << typeid(*replacementModel).name() << ") in layer " + SVDEBUG << "Replacing model " << modelId << ") with model " + << replacementModel << ") in layer " << layer << " (name " << layer->objectName() << ")" << endl; - RangeSummarisableTimeValueModel *rm = - dynamic_cast(replacementModel); + auto rm = ModelById::getAs(replacementModel); if (rm) { SVDEBUG << "new model has " << rm->getChannelCount() << " channels " << endl; } else { @@ -541,41 +513,36 @@ deleteLayer(obsoleteLayers[k], true); } - std::set additionalModels; + std::set additionalModels; for (const auto &rec : m_models) { - if (rec.additional) { - additionalModels.insert(rec.model); + if (rec.second.additional) { + additionalModels.insert(rec.first); } } - for (Model *a: additionalModels) { - deleteModelFromList(a); + for (ModelId a: additionalModels) { + m_models.erase(a); } for (const auto &rec : m_models) { - Model *m = rec.model; + auto m = ModelById::get(rec.first); + if (!m) continue; #ifdef DEBUG_DOCUMENT - SVDEBUG << "considering alignment for model " << m << " (name \"" - << m->objectName() << "\")" << endl; + SVDEBUG << "considering alignment for model " << rec.first << endl; #endif if (m_autoAlignment) { - alignModel(m); + alignModel(rec.first); - } else if (oldMainModel && + } else if (!oldMainModel.isNone() && (m->getAlignmentReference() == oldMainModel)) { - alignModel(m); + alignModel(rec.first); } } - if (oldMainModel) { - oldMainModel->aboutToDelete(); - emit modelAboutToBeDeleted(oldMainModel); - } - if (m_autoAlignment) { SVDEBUG << "Document::setMainModel: auto-alignment is on, aligning model if possible" << endl; alignModel(m_mainModel); @@ -585,45 +552,50 @@ emit mainModelChanged(m_mainModel); - delete oldMainModel; + if (!oldMainModel.isNone()) { + + // Remove the playable explicitly - the main model's dtor will + // do this, but just in case something is still hanging onto a + // shared_ptr to the old main model so it doesn't get deleted + PlayParameterRepository::getInstance()->removePlayable + (oldMainModel.untyped); + + ModelById::release(oldMainModel); + } } void Document::addAlreadyDerivedModel(const Transform &transform, const ModelTransformer::Input &input, - Model *outputModelToAdd) + ModelId outputModelToAdd) { - if (findModelInList(outputModelToAdd) != m_models.end()) { + if (m_models.find(outputModelToAdd) != m_models.end()) { SVCERR << "WARNING: Document::addAlreadyDerivedModel: Model already added" - << endl; + << endl; return; } - + #ifdef DEBUG_DOCUMENT - if (input.getModel()) { - SVDEBUG << "Document::addAlreadyDerivedModel: source is " << input.getModel() << " \"" << input.getModel()->objectName() << "\"" << endl; - } else { - SVDEBUG << "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; rec.additional = false; - rec.refcount = 0; - outputModelToAdd->setSourceModel(input.getModel()); + if (auto m = ModelById::get(outputModelToAdd)) { + m->setSourceModel(input.getModel()); + } - m_models.push_back(rec); + m_models[outputModelToAdd] = rec; #ifdef DEBUG_DOCUMENT SVDEBUG << "Document::addAlreadyDerivedModel: Added model " << outputModelToAdd << endl; SVDEBUG << "Models now: "; for (const auto &rec : m_models) { - SVDEBUG << rec.model << " "; + SVDEBUG << rec.first << " "; } SVDEBUG << endl; #endif @@ -631,131 +603,130 @@ emit modelAdded(outputModelToAdd); } - void -Document::addImportedModel(Model *model) +Document::addNonDerivedModel(ModelId modelId) { - if (findModelInList(model) != m_models.end()) { - SVCERR << "WARNING: Document::addImportedModel: Model already added" - << endl; + if (ModelById::isa(modelId)) { +#ifdef DEBUG_DOCUMENT + SVCERR << "Document::addNonDerivedModel: Model " << modelId << " is an aggregate model, adding it to aggregates" << endl; +#endif + m_aggregateModels.insert(modelId); + return; + } + if (ModelById::isa(modelId)) { +#ifdef DEBUG_DOCUMENT + SVCERR << "Document::addNonDerivedModel: Model " << modelId << " is an alignment model, adding it to alignments" << endl; +#endif + m_alignmentModels.insert(modelId); + return; + } + + if (m_models.find(modelId) != m_models.end()) { + SVCERR << "WARNING: Document::addNonDerivedModel: Model already added" + << endl; return; } ModelRecord rec; - rec.model = model; - rec.source = nullptr; + rec.source = {}; rec.channel = 0; - rec.refcount = 0; rec.additional = false; - m_models.push_back(rec); + m_models[modelId] = rec; #ifdef DEBUG_DOCUMENT - SVDEBUG << "Document::addImportedModel: Added model " << model << endl; + SVCERR << "Document::addNonDerivedModel: Added model " << modelId << endl; + SVCERR << "Models now: "; + for (const auto &rec : m_models) { + SVCERR << rec.first << " "; + } + SVCERR << endl; +#endif + + if (m_autoAlignment) { + SVDEBUG << "Document::addNonDerivedModel: auto-alignment is on, aligning model if possible" << endl; + alignModel(modelId); + } else { + SVDEBUG << "Document(" << this << "): addNonDerivedModel: auto-alignment is off" << endl; + } + + emit modelAdded(modelId); +} + +void +Document::addAdditionalModel(ModelId modelId) +{ + if (m_models.find(modelId) != m_models.end()) { + SVCERR << "WARNING: Document::addAdditionalModel: Model already added" + << endl; + return; + } + + ModelRecord rec; + rec.source = {}; + rec.channel = 0; + rec.additional = true; + + m_models[modelId] = rec; + +#ifdef DEBUG_DOCUMENT + SVDEBUG << "Document::addAdditionalModel: Added model " << modelId << endl; SVDEBUG << "Models now: "; for (const auto &rec : m_models) { - SVDEBUG << rec.model << " "; + SVDEBUG << rec.first << " "; } SVDEBUG << endl; #endif - if (m_autoAlignment) { - SVDEBUG << "Document::addImportedModel: auto-alignment is on, aligning model if possible" << endl; - alignModel(model); - } else { - SVDEBUG << "Document(" << this << "): addImportedModel: auto-alignment is off" << endl; + if (m_autoAlignment && + ModelById::isa(modelId)) { + SVDEBUG << "Document::addAdditionalModel: auto-alignment is on and model is an alignable type, aligning it if possible" << endl; + alignModel(modelId); } - emit modelAdded(model); + emit modelAdded(modelId); } -void -Document::addAdditionalModel(Model *model) -{ - 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.push_back(rec); - -#ifdef DEBUG_DOCUMENT - SVDEBUG << "Document::addAdditionalModel: Added model " << model << endl; - SVDEBUG << "Models now: "; - for (const auto &rec : m_models) { - SVDEBUG << rec.model << " "; - } - SVDEBUG << endl; -#endif - - if (m_autoAlignment) { - SVDEBUG << "Document::addAdditionalModel: auto-alignment is on, aligning model if possible" << endl; - alignModel(model); - } - - emit modelAdded(model); -} - -void -Document::addAggregateModel(AggregateWaveModel *model) -{ - connect(model, SIGNAL(modelInvalidated()), - this, SLOT(aggregateModelInvalidated())); - m_aggregateModels.insert(model); - SVDEBUG << "Document::addAggregateModel(" << model << ")" << endl; -} - -void -Document::aggregateModelInvalidated() -{ - QObject *s = sender(); - AggregateWaveModel *aggregate = qobject_cast(s); - SVDEBUG << "Document::aggregateModelInvalidated(" << aggregate << ")" << endl; - if (aggregate) releaseModel(aggregate); -} - -Model * +ModelId Document::addDerivedModel(const Transform &transform, const ModelTransformer::Input &input, QString &message) { for (auto &rec : m_models) { - if (rec.transform == transform && - rec.source == input.getModel() && - rec.channel == input.getChannel()) { + if (rec.second.transform == transform && + rec.second.source == input.getModel() && + rec.second.channel == input.getChannel()) { SVDEBUG << "derived model taken from map " << endl; - return rec.model; + return rec.first; } } Transforms tt; tt.push_back(transform); - vector mm = addDerivedModels(tt, input, message, nullptr); - if (mm.empty()) return nullptr; + vector mm = addDerivedModels(tt, input, message, nullptr); + if (mm.empty()) return {}; else return mm[0]; } -vector +vector Document::addDerivedModels(const Transforms &transforms, const ModelTransformer::Input &input, QString &message, AdditionalModelConverter *amc) { - vector mm = + vector mm = ModelTransformerFactory::getInstance()->transformMultiple (transforms, input, message, amc); - for (int j = 0; j < (int)mm.size(); ++j) { + for (int j = 0; in_range_for(mm, j); ++j) { - Model *model = mm[j]; + ModelId modelId = mm[j]; + Transform applied = transforms[j]; + + if (modelId.isNone()) { + SVCERR << "WARNING: Document::addDerivedModel: no output model for transform " << applied.getIdentifier() << endl; + continue; + } // The transform we actually used was presumably identical to // the one asked for, except that the version of the plugin @@ -768,111 +739,96 @@ //!!! would be nice to short-circuit this -- the version is //!!! static data, shouldn't have to construct a plugin for it //!!! (which may be expensive in Piper-world) - - Transform applied = transforms[j]; applied.setPluginVersion (TransformFactory::getInstance()-> getDefaultTransformFor(applied.getIdentifier(), applied.getSampleRate()) .getPluginVersion()); - if (!model) { - SVCERR << "WARNING: Document::addDerivedModel: no output model for transform " << applied.getIdentifier() << endl; - } else { - addAlreadyDerivedModel(applied, input, model); - } + addAlreadyDerivedModel(applied, input, modelId); } return mm; } void -Document::releaseModel(Model *model) // Will _not_ release main model! +Document::releaseModel(ModelId modelId) { - if (model == nullptr) { + // This is called when a layer has been deleted or has replaced + // its model, in order to reclaim storage for the old model. It + // could be a no-op without making any functional difference, as + // all the models stored in the ById pool are released when the + // document is deleted. But models can sometimes be large, so if + // we know no other layer is using one, we should release it. If + // we happen to release one that is being used, the ModelById + // borrowed-pointer mechanism will at least prevent memory errors, + // although the other code will have to stop whatever it's doing. + + if (auto model = ModelById::get(modelId)) { + SVCERR << "Document::releaseModel(" << modelId << "), name " + << model->objectName() << ", type " + << typeid(*model.get()).name() << endl; + } else { + SVCERR << "Document::releaseModel(" << modelId << ")" << endl; + } + + if (modelId.isNone()) { + return; + } + +#ifdef DEBUG_DOCUMENT + SVCERR << "Document::releaseModel(" << modelId << ")" << endl; +#endif + + if (modelId == m_mainModel) { +#ifdef DEBUG_DOCUMENT + SVCERR << "Document::releaseModel: It's the main model, ignoring" + << endl; +#endif return; } + if (m_models.find(modelId) == m_models.end()) { + // No point in releasing aggregate and alignment models, + // they're not large #ifdef DEBUG_DOCUMENT - SVDEBUG << "Document::releaseModel(" << model << ", type " - << model->getTypeName() << ", name \"" - << model->objectName() << "\")" << endl; + SVCERR << "Document::releaseModel: It's not a regular layer model, ignoring" << endl; #endif - - if (model == m_mainModel) { return; } - bool toDelete = false; - bool isInModelList = false; // should become true for any "normal" model - - 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 { + for (auto layer: m_layers) { + if (layer->getModel() == modelId) { #ifdef DEBUG_DOCUMENT - SVDEBUG << "Lowering refcount from " << mitr->refcount << endl; + SVCERR << "Document::releaseModel: It's still in use in at least one layer (e.g. " << layer << ", \"" << layer->getLayerPresentationName() << "\"), ignoring" << endl; #endif - if (--mitr->refcount == 0) { - toDelete = true; - } + return; } - isInModelList = true; - - } else if (m_aggregateModels.find(model) != m_aggregateModels.end()) { -#ifdef DEBUG_DOCUMENT - SVDEBUG << "Document::releaseModel: is an aggregate model" << endl; -#endif - toDelete = true; - } else { - SVCERR << "WARNING: Document::releaseModel: Unfound model " - << model << endl; - toDelete = true; } - if (toDelete) { +#ifdef DEBUG_DOCUMENT + SVCERR << "Document::releaseModel: Seems to be OK to release this one" + << endl; +#endif - int sourceCount = 0; + int sourceCount = 0; - for (auto &rec: m_models) { - if (rec.source == model) { - ++sourceCount; - rec.source = nullptr; - } - } - - if (sourceCount > 0) { - SVDEBUG << "Document::releaseModel: Deleting model " - << model << " even though it is source for " - << sourceCount << " other derived model(s) -- resetting " - << "their source fields appropriately" << endl; - } - - if (isInModelList) { - deleteModelFromList(model); - -#ifdef DEBUG_DOCUMENT - SVDEBUG << "Document::releaseModel: Deleted model " << model << endl; - SVDEBUG << "Models now: "; - for (const auto &r: m_models) { - SVDEBUG << r.model << " "; - } - SVDEBUG << endl; -#endif - } else { - model->aboutToDelete(); - emit modelAboutToBeDeleted(model); - delete model; - -#ifdef DEBUG_DOCUMENT - SVDEBUG << "Document::releaseModel: Deleted awkward model " << model << endl; -#endif + for (auto &m: m_models) { + if (m.second.source == modelId) { + ++sourceCount; + m.second.source = {}; } } + + if (sourceCount > 0) { + SVCERR << "Document::releaseModel: Request to release model " + << modelId << " even though it was source for " + << sourceCount << " other derived model(s) -- have cleared " + << "their source fields" << endl; + } + + m_models.erase(modelId); + ModelById::release(modelId); } void @@ -936,47 +892,37 @@ } void -Document::setModel(Layer *layer, Model *model) +Document::setModel(Layer *layer, ModelId modelId) { - if (model && - model != m_mainModel && - findModelInList(model) == m_models.end()) { + if (!modelId.isNone() && + modelId != m_mainModel && + m_models.find(modelId) == 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!" - << endl; + << " (\"" << layer->objectName() + << "\") wants to use unregistered model " << modelId + << ": register the layer's model before setting it!" + << endl; return; } - Model *previousModel = layer->getModel(); + ModelId previousModel = layer->getModel(); - if (previousModel == model) { + if (previousModel == modelId) { SVDEBUG << "NOTE: Document::setModel: Layer " << layer << " (\"" - << layer->objectName() << "\") is already set to model " - << model << " (\"" - << (model ? model->objectName(): "(null)") - << "\")" << endl; + << layer->objectName() + << "\") is already set to model " + << modelId << endl; return; } - if (model && model != m_mainModel) { - ModelList::iterator mitr = findModelInList(model); - if (mitr != m_models.end()) { - mitr->refcount ++; - } + if (!modelId.isNone() && !previousModel.isNone()) { + PlayParameterRepository::getInstance()->copyParameters + (previousModel.untyped, modelId.untyped); } - if (model && previousModel) { - PlayParameterRepository::getInstance()->copyParameters - (previousModel, model); - } + LayerFactory::getInstance()->setModel(layer, modelId); - LayerFactory::getInstance()->setModel(layer, model); - - if (previousModel) { - releaseModel(previousModel); - } + releaseModel(previousModel); } void @@ -988,8 +934,8 @@ void Document::addLayerToView(View *view, Layer *layer) { - Model *model = layer->getModel(); - if (!model) { + ModelId modelId = layer->getModel(); + if (modelId.isNone()) { #ifdef DEBUG_DOCUMENT SVDEBUG << "Document::addLayerToView: Layer (\"" << layer->objectName() @@ -997,10 +943,10 @@ << "normally you want to set the model first" << endl; #endif } else { - if (model != m_mainModel && - findModelInList(model) == m_models.end()) { + if (modelId != m_mainModel && + m_models.find(modelId) == m_models.end()) { SVCERR << "ERROR: Document::addLayerToView: Layer " << layer - << " has unregistered model " << model + << " has unregistered model " << modelId << " -- register the layer's model before adding the layer!" << endl; return; } @@ -1075,25 +1021,25 @@ } } -std::vector +std::vector Document::getTransformInputModels() { - std::vector models; + std::vector models; - if (!m_mainModel) return models; + if (m_mainModel.isNone()) return models; models.push_back(m_mainModel); //!!! This will pick up all models, including those that aren't visible... - for (ModelRecord &rec: m_models) { + for (auto rec: m_models) { - Model *model = rec.model; - if (!model || model == m_mainModel) continue; - DenseTimeValueModel *dtvm = dynamic_cast(model); - + ModelId modelId = rec.first; + if (modelId == m_mainModel) continue; + + auto dtvm = ModelById::getAs(modelId); if (dtvm) { - models.push_back(dtvm); + models.push_back(modelId); } } @@ -1101,11 +1047,11 @@ } bool -Document::isKnownModel(const Model *model) const +Document::isKnownModel(const ModelId modelId) const { - if (model == m_mainModel) return true; - for (const ModelRecord &rec: m_models) { - if (rec.model == model) return true; + if (modelId == m_mainModel) return true; + for (auto rec: m_models) { + if (rec.first == modelId) return true; } return false; } @@ -1117,30 +1063,29 @@ } void -Document::alignModel(Model *model, bool forceRecalculate) +Document::alignModel(ModelId modelId, bool forceRecalculate) { - SVDEBUG << "Document::alignModel(" << model << ", " << forceRecalculate + SVDEBUG << "Document::alignModel(" << modelId << ", " << forceRecalculate << ") (main model is " << m_mainModel << ")" << endl; - - RangeSummarisableTimeValueModel *rm = - dynamic_cast(model); + + auto rm = ModelById::getAs(modelId); if (!rm) { - SVDEBUG << "(model " << rm << " is not an alignable sort)" << endl; + SVDEBUG << "(model " << modelId << " is not an alignable sort)" << endl; return; } - if (!m_mainModel) { + if (m_mainModel.isNone()) { SVDEBUG << "(no main model to align to)" << endl; - if (forceRecalculate && rm->getAlignment()) { + if (forceRecalculate && !rm->getAlignment().isNone()) { SVDEBUG << "(but model is aligned, and forceRecalculate is true, " << "so resetting alignment to nil)" << endl; - rm->setAlignment(nullptr); + rm->setAlignment({}); } return; } if (rm->getAlignmentReference() == m_mainModel) { - SVDEBUG << "(model " << rm << " is already aligned to main model " + SVDEBUG << "(model " << modelId << " is already aligned to main model " << m_mainModel << ")" << endl; if (!forceRecalculate) { return; @@ -1150,60 +1095,58 @@ } } - if (model == m_mainModel) { + if (modelId == m_mainModel) { // The reference has an empty alignment to itself. This makes // it possible to distinguish between the reference and any // unaligned model just by looking at the model itself, // without also knowing what the main model is - SVDEBUG << "Document::alignModel(" << model + SVDEBUG << "Document::alignModel(" << modelId << "): is main model, setting alignment to itself" << endl; - rm->setAlignment(new AlignmentModel(model, model, nullptr)); + auto alignment = std::make_shared(modelId, modelId, + ModelId()); + + ModelId alignmentModelId = ModelById::add(alignment); + rm->setAlignment(alignmentModelId); + m_alignmentModels.insert(alignmentModelId); return; } - WritableWaveFileModel *w = - dynamic_cast(model); + auto w = ModelById::getAs(modelId); if (w && w->getWriteProportion() < 100) { - SVDEBUG << "Document::alignModel(" << model + SVDEBUG << "Document::alignModel(" << modelId << "): model write is not complete, deferring" << endl; - connect(w, SIGNAL(writeCompleted()), - this, SLOT(performDeferredAlignment())); + connect(w.get(), SIGNAL(writeCompleted(ModelId)), + this, SLOT(performDeferredAlignment(ModelId))); return; } SVDEBUG << "Document::alignModel: aligning..." << endl; - if (rm->getAlignmentReference() != nullptr) { + if (!rm->getAlignmentReference().isNone()) { SVDEBUG << "(Note: model " << rm << " is currently aligned to model " << rm->getAlignmentReference() << "; this will replace that)" << endl; } QString err; - if (!m_align->alignModel(this, m_mainModel, rm, err)) { + if (!m_align->alignModel(this, m_mainModel, modelId, err)) { SVCERR << "Alignment failed: " << err << endl; emit alignmentFailed(err); } } void -Document::performDeferredAlignment() +Document::performDeferredAlignment(ModelId modelId) { - QObject *s = sender(); - Model *m = dynamic_cast(s); - if (!m) { - SVDEBUG << "Document::performDeferredAlignment: sender is not a Model" << endl; - } else { - SVDEBUG << "Document::performDeferredAlignment: aligning..." << endl; - alignModel(m); - } + SVDEBUG << "Document::performDeferredAlignment: aligning..." << endl; + alignModel(modelId); } void Document::alignModels() { - for (const ModelRecord &rec: m_models) { - alignModel(rec.model); + for (auto rec: m_models) { + alignModel(rec.first); } alignModel(m_mainModel); } @@ -1211,8 +1154,8 @@ void Document::realignModels() { - for (const ModelRecord &rec: m_models) { - alignModel(rec.model, true); + for (auto rec: m_models) { + alignModel(rec.first, true); } alignModel(m_mainModel); } @@ -1362,7 +1305,8 @@ out << indent + QString("\n") .arg(extraAttributes == "" ? "" : " ").arg(extraAttributes); - if (m_mainModel) { + auto mainModel = ModelById::getAs(m_mainModel); + if (mainModel) { #ifdef DEBUG_DOCUMENT SVDEBUG << "Document::toXml: writing main model" << endl; @@ -1371,16 +1315,17 @@ if (asTemplate) { writePlaceholderMainModel(out, indent + " "); } else { - m_mainModel->toXml(out, indent + " ", "mainModel=\"true\""); + mainModel->toXml(out, indent + " ", "mainModel=\"true\""); } - PlayParameters *playParameters = - PlayParameterRepository::getInstance()->getPlayParameters(m_mainModel); + auto playParameters = + PlayParameterRepository::getInstance()->getPlayParameters + (m_mainModel.untyped); if (playParameters) { playParameters->toXml (out, indent + " ", QString("model=\"%1\"") - .arg(m_mainModel->getExportId())); + .arg(mainModel->getExportId())); } } else { #ifdef DEBUG_DOCUMENT @@ -1391,17 +1336,17 @@ // Models that are not used in a layer that is in a view should // not be written. Get our list of required models first. - std::set used; + std::set used; for (LayerViewMap::const_iterator i = m_layerViewMap.begin(); i != m_layerViewMap.end(); ++i) { if (i->first && !i->second.empty()) { // Layer exists, is in views - Model *m = i->first->getModel(); - if (m) { - used.insert(m); - if (m->getSourceModel()) { - used.insert(m->getSourceModel()); + ModelId modelId = i->first->getModel(); + if (auto model = ModelById::get(modelId)) { + used.insert(modelId); + if (!model->getSourceModel().isNone()) { + used.insert(model->getSourceModel()); } } } @@ -1420,16 +1365,16 @@ // that, so existing sessions will always have the aggregate // models first and we might as well stick with that. - for (std::set::iterator i = m_aggregateModels.begin(); - i != m_aggregateModels.end(); ++i) { + for (auto modelId: m_aggregateModels) { #ifdef DEBUG_DOCUMENT - SVDEBUG << "Document::toXml: checking aggregate model " << *i << endl; + SVDEBUG << "Document::toXml: checking aggregate model " + << modelId << endl; #endif - - AggregateWaveModel *aggregate = qobject_cast(*i); + + auto aggregate = ModelById::getAs(modelId); if (!aggregate) continue; - if (used.find(aggregate) == used.end()) { + if (used.find(modelId) == used.end()) { #ifdef DEBUG_DOCUMENT SVDEBUG << "(unused, skipping)" << endl; #endif @@ -1443,7 +1388,7 @@ aggregate->toXml(out, indent + " "); } - std::set written; + std::set written; // Now write the other models in two passes: first the models that // aren't derived from anything (in case they are source @@ -1455,17 +1400,18 @@ const int nonDerivedPass = 0, derivedPass = 1; for (int pass = nonDerivedPass; pass <= derivedPass; ++pass) { - for (const ModelRecord &rec: m_models) { + for (auto rec: m_models) { - Model *model = rec.model; + ModelId modelId = rec.first; - if (used.find(model) == used.end()) continue; + if (used.find(modelId) == used.end()) continue; + auto model = ModelById::get(modelId); + if (!model) continue; + #ifdef DEBUG_DOCUMENT - SVDEBUG << "Document::toXml: looking at model " << model - << " (" << model->getTypeName() << ", \"" - << model->objectName() << "\") [pass = " - << pass << "]" << endl; + SVDEBUG << "Document::toXml: looking at model " << modelId + << " [pass = " << pass << "]" << endl; #endif // We need an intelligent way to determine which models @@ -1485,7 +1431,8 @@ bool writeModel = true; bool haveDerivation = false; - if (rec.source && rec.transform.getIdentifier() != "") { + if (!rec.second.source.isNone() && + rec.second.transform.getIdentifier() != "") { haveDerivation = true; } @@ -1502,26 +1449,25 @@ } if (haveDerivation) { - if (dynamic_cast(model)) { - writeModel = false; - } else if (dynamic_cast(model)) { + if (ModelById::isa(modelId) || + ModelById::isa(modelId)) { writeModel = false; } } if (writeModel) { model->toXml(out, indent + " "); - written.insert(model); + written.insert(modelId); } if (haveDerivation) { writeBackwardCompatibleDerivation(out, indent + " ", - model, rec); + modelId, rec.second); } - //!!! We should probably own the PlayParameterRepository - PlayParameters *playParameters = - PlayParameterRepository::getInstance()->getPlayParameters(model); + auto playParameters = + PlayParameterRepository::getInstance()->getPlayParameters + (modelId.untyped); if (playParameters) { playParameters->toXml (out, indent + " ", @@ -1537,11 +1483,12 @@ // this will only work when the alignment is complete, so we // should probably wait for it if it isn't already by this point. - for (std::set::const_iterator i = written.begin(); - i != written.end(); ++i) { + for (auto modelId: written) { - const Model *model = *i; - const AlignmentModel *alignment = model->getAlignment(); + auto model = ModelById::get(modelId); + if (!model) continue; + + auto alignment = ModelById::get(model->getAlignment()); if (!alignment) continue; alignment->toXml(out, indent + " "); @@ -1557,15 +1504,17 @@ void Document::writePlaceholderMainModel(QTextStream &out, QString indent) const { + auto mainModel = ModelById::get(m_mainModel); + if (!mainModel) return; out << indent; out << QString("\n") - .arg(m_mainModel->getExportId()) - .arg(m_mainModel->getSampleRate()); + .arg(mainModel->getExportId()) + .arg(mainModel->getSampleRate()); } void Document::writeBackwardCompatibleDerivation(QTextStream &out, QString indent, - Model *targetModel, + ModelId targetModelId, const ModelRecord &rec) const { // There is a lot of redundancy in the XML we output here, because @@ -1591,7 +1540,10 @@ // 'type="transform"' in the derivation element. const Transform &transform = rec.transform; - + + auto targetModel = ModelById::get(targetModelId); + if (!targetModel) return; + // Just for reference, this is what we would write if we didn't // have to be backward compatible: // @@ -1623,7 +1575,7 @@ "model=\"%2\" channel=\"%3\" domain=\"%4\" " "stepSize=\"%5\" blockSize=\"%6\" %7windowType=\"%8\" " "transform=\"%9\">\n") - .arg(rec.source->getExportId()) + .arg(ModelById::getExportId(rec.source)) .arg(targetModel->getExportId()) .arg(rec.channel) .arg(TransformFactory::getInstance()->getTransformInputDomain diff -r a82b9d410393 -r 155008f1bf10 framework/Document.h --- a/framework/Document.h Fri Jun 14 17:19:37 2019 +0100 +++ b/framework/Document.h Wed Jul 17 14:25:41 2019 +0100 @@ -95,7 +95,7 @@ * Create and return a new layer associated with the given model, * and register the model as an imported model. */ - Layer *createImportedLayer(Model *); + Layer *createImportedLayer(ModelId); /** * Create and return a new layer of the given type, with an @@ -158,21 +158,13 @@ * transformer process, and the layers are returned through a * subsequent call to the provided handler (which must be * non-null). The handle returned will be passed through to the - * handler callback, and may be also used for cancelling the task. + * handler callback. */ LayerCreationAsyncHandle createDerivedLayersAsync(const Transforms &, const ModelTransformer::Input &, LayerCreationHandler *handler); /** - * Indicate that the async layer creation task associated with the - * given handle should be cancelled. There is no guarantee about - * what this will mean, and the handler callback may still be - * called. - */ - void cancelAsyncLayerCreation(LayerCreationAsyncHandle handle); - - /** * Delete the given layer, and also its associated model if no * longer used by any other layer. In general, this should be the * only method used to delete layers -- doing so directly is a bit @@ -183,39 +175,42 @@ /** * Set the main model (the source for playback sample rate, etc) * to the given wave file model. This will regenerate any derived - * models that were based on the previous main model. + * models that were based on the previous main model. The model + * must have been added to ModelById already, and Document will + * take responsibility for releasing it later. */ - void setMainModel(WaveFileModel *); + void setMainModel(ModelId); // a WaveFileModel /** * Get the main model (the source for playback sample rate, etc). */ - WaveFileModel *getMainModel() { return m_mainModel; } + ModelId getMainModel() { return m_mainModel; } + + std::vector getTransformInputModels(); /** - * Get the main model (the source for playback sample rate, etc). + * Return true if the model id is known to be the main model or + * one of the other existing models that can be shown in a new + * layer. */ - const WaveFileModel *getMainModel() const { return m_mainModel; } - - std::vector getTransformInputModels(); - - bool isKnownModel(const Model *) const; + bool isKnownModel(ModelId) const; /** * Add a derived model associated with the given transform, * running the transform and returning the resulting model. + * The model is added to ModelById before returning. */ - Model *addDerivedModel(const Transform &transform, - const ModelTransformer::Input &input, - QString &returnedMessage); + ModelId addDerivedModel(const Transform &transform, + const ModelTransformer::Input &input, + QString &returnedMessage); /** * Add derived models associated with the given set of related * transforms, running the transforms and returning the resulting - * models. + * models. The models are added to ModelById before returning. */ friend class AdditionalModelConverter; - std::vector addDerivedModels(const Transforms &transforms, + std::vector addDerivedModels(const Transforms &transforms, const ModelTransformer::Input &input, QString &returnedMessage, AdditionalModelConverter *); @@ -223,36 +218,33 @@ /** * 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. + * by the document using createDerivedModel or + * createDerivedLayer. Document will take responsibility for + * releasing the model later. */ void addAlreadyDerivedModel(const Transform &transform, const ModelTransformer::Input &input, - Model *outputModelToAdd); + ModelId outputModelToAdd); /** - * Add an imported (non-derived, non-main) model. This is - * necessary to register any imported model that is associated - * with a layer. + * Add an imported model, i.e. any model (other than the main + * model) that has been created by any means other than as the + * output of a transform. This is necessary to register any + * imported model that is to be associated with a layer, and also + * to make sure that the model is released by the Document + * later. Aggregate models, alignment models, and miscellaneous + * temporary models should also be added in this way, unless the + * temporary models are large enough to need managing in a way + * that guarantees the shortest possible lifespan. */ - void addImportedModel(Model *); - - /** - * Add an aggregate model (one which represents a set of component - * wave models as one model per channel in a single wave - * model). Aggregate models are unusual in that they are created - * for a single transform each and have no refcount. (This - * probably isn't ideal!) They are recorded separately from other - * models, and will be deleted if any of their component models - * are removed. - */ - void addAggregateModel(AggregateWaveModel *); + void addNonDerivedModel(ModelId); /** * Associate the given model with the given layer. The model must * have already been registered using one of the addXXModel * methods above. */ - void setModel(Layer *, Model *); + void setModel(Layer *, ModelId); /** * Set the given layer to use the given channel of its model (-1 @@ -320,9 +312,8 @@ // last removed from a view void layerInAView(Layer *, bool); - void modelAdded(Model *); - void mainModelChanged(WaveFileModel *); // emitted after modelAdded - void modelAboutToBeDeleted(Model *); + void modelAdded(ModelId); + void mainModelChanged(ModelId); // a WaveFileModel; emitted after modelAdded void modelGenerationFailed(QString transformName, QString message); void modelGenerationWarning(QString transformName, QString message); @@ -331,17 +322,16 @@ void modelRegenerationWarning(QString layerName, QString transformName, QString message); - void alignmentComplete(AlignmentModel *); + void alignmentComplete(ModelId); // an AlignmentModel void alignmentFailed(QString message); void activity(QString); protected slots: - void aggregateModelInvalidated(); - void performDeferredAlignment(); + void performDeferredAlignment(ModelId); protected: - void releaseModel(Model *model); + void releaseModel(ModelId model); /** * If model is suitable for alignment, align it against the main @@ -349,7 +339,7 @@ * alignment already for the current main model, leave it * unchanged unless forceRecalculate is true. */ - void alignModel(Model *, bool forceRecalculate = false); + void alignModel(ModelId, bool forceRecalculate = false); /* * Every model that is in use by a layer in the document must be @@ -362,75 +352,40 @@ * model is not reference counted for layers, and is not freed * unless it is replaced or the document is deleted. */ - WaveFileModel *m_mainModel; + ModelId m_mainModel; // a WaveFileModel struct ModelRecord { // Information associated with a non-main model. If this - // model is derived from another, then source will be non-NULL - // and the transform name will be set appropriately. If the - // transform name is set but source is NULL, then there was a - // transform involved but the (target) model has been modified - // since being generated from it. + // model is derived from another, then source will be + // something other than None and the transform name will be + // set appropriately. If the transform is set but source is + // None, then there was a transform involved but the (target) + // model has been modified since being generated from it. // This does not use ModelTransformer::Input, because it would - // be confusing to have Input objects hanging around with NULL + // be confusing to have Input objects hanging around with None // models in them. - Model *model; - const Model *source; + ModelId source; // may be None int channel; Transform transform; bool additional; - - // Count of the number of layers using this model. - int refcount; }; - // This used to be a map, 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 ModelList; - ModelList m_models; + // These must be stored in increasing order of id (as in the + // ordered std::map), to ensure repeatability for automated tests + std::map 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(); - } + std::set m_aggregateModels; + std::set m_alignmentModels; - 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 * transform). */ - void addAdditionalModel(Model *); + void addAdditionalModel(ModelId); - std::set m_aggregateModels; - class AddLayerCommand : public Command { public: @@ -475,13 +430,13 @@ void removeFromLayerViewMap(Layer *, View *); QString getUniqueLayerName(QString candidate); - void writeBackwardCompatibleDerivation(QTextStream &, QString, Model *, + void writeBackwardCompatibleDerivation(QTextStream &, QString, ModelId, const ModelRecord &) const; void toXml(QTextStream &, QString, QString, bool asTemplate) const; void writePlaceholderMainModel(QTextStream &, QString) const; - std::vector createLayersForDerivedModels(std::vector, + std::vector createLayersForDerivedModels(std::vector, QStringList names); /** diff -r a82b9d410393 -r 155008f1bf10 framework/MainWindowBase.cpp --- a/framework/MainWindowBase.cpp Fri Jun 14 17:19:37 2019 +0100 +++ b/framework/MainWindowBase.cpp Wed Jul 17 14:25:41 2019 +0100 @@ -63,7 +63,6 @@ #include "base/RecentFiles.h" -#include "base/PlayParameterRepository.h" #include "base/XmlExportable.h" #include "base/Profiler.h" #include "base/Preferences.h" @@ -105,7 +104,7 @@ #include #include #include -#include +#include #include #include @@ -179,6 +178,7 @@ qRegisterMetaType("sv_frame_t"); qRegisterMetaType("sv_samplerate_t"); + qRegisterMetaType("ModelId"); #ifdef Q_WS_X11 XSetErrorHandler(handle_x11_error); @@ -507,8 +507,8 @@ void MainWindowBase::resizeConstrained(QSize size) { - QDesktopWidget *desktop = QApplication::desktop(); - QRect available = desktop->availableGeometry(); + QScreen *screen = QApplication::primaryScreen(); + QRect available = screen->availableGeometry(); QSize actual(std::min(size.width(), available.width()), std::min(size.height(), available.height())); resize(actual); @@ -575,7 +575,7 @@ FileFinder *ff = FileFinder::getInstance(); if (type == FileFinder::AnyFile) { - if (getMainModel() != nullptr && + if (!getMainModelId().isNone() && m_paneStack != nullptr && m_paneStack->getCurrentPane() != nullptr) { // can import a layer return ff->getOpenFileName(FileFinder::AnyFile, m_sessionFile); @@ -668,7 +668,7 @@ (haveCurrentPane && (currentLayer != nullptr)); bool haveMainModel = - (getMainModel() != nullptr); + (!getMainModelId().isNone()); bool havePlayTarget = (m_playTarget != nullptr || m_audioIO != nullptr); bool haveSelection = @@ -693,7 +693,7 @@ !m_viewManager->getClipboard().empty()); bool haveTabularLayer = (haveCurrentLayer && - dynamic_cast(currentLayer->getModel())); + ModelById::isa(currentLayer->getModel())); emit canAddPane(haveMainModel); emit canDeleteCurrentPane(haveCurrentPane); @@ -839,7 +839,7 @@ if (m_viewManager->getPlaySoloMode()) { currentPaneChanged(m_paneStack->getCurrentPane()); } else { - m_viewManager->setPlaybackModel(nullptr); + m_viewManager->setPlaybackModel({}); if (m_playSource) { m_playSource->clearSoloModelSet(); } @@ -857,11 +857,13 @@ if (!(m_viewManager && m_playSource && m_viewManager->getPlaySoloMode())) { - if (m_viewManager) m_viewManager->setPlaybackModel(nullptr); + if (m_viewManager) { + m_viewManager->setPlaybackModel(ModelId()); + } return; } - Model *prevPlaybackModel = m_viewManager->getPlaybackModel(); + ModelId prevPlaybackModel = m_viewManager->getPlaybackModel(); // What we want here is not the currently playing frame (unless we // are about to clear out the audio playback buffers -- which may @@ -878,21 +880,20 @@ View::ModelSet soloModels = p->getModels(); View::ModelSet sources; - for (View::ModelSet::iterator mi = soloModels.begin(); - mi != soloModels.end(); ++mi) { + for (ModelId modelId: sources) { // If a model in this pane is derived from something else, // then we want to play that model as well -- if the model // that's derived from it is not something that is itself // individually playable (e.g. a waveform) - if (*mi && - !dynamic_cast(*mi) && - (*mi)->getSourceModel()) { - sources.insert((*mi)->getSourceModel()); + if (auto model = ModelById::get(modelId)) { + if (!ModelById::isa(modelId) && + !model->getSourceModel().isNone()) { + sources.insert(model->getSourceModel()); + } } } - for (View::ModelSet::iterator mi = sources.begin(); - mi != sources.end(); ++mi) { - soloModels.insert(*mi); + for (ModelId modelId: sources) { + soloModels.insert(modelId); } //!!! Need an "atomic" way of telling the play source that the @@ -900,23 +901,22 @@ //the play source should be making the setPlaybackModel call to //ViewManager - for (View::ModelSet::iterator mi = soloModels.begin(); - mi != soloModels.end(); ++mi) { - if (dynamic_cast(*mi)) { - m_viewManager->setPlaybackModel(*mi); + ModelId newPlaybackModel; + + for (ModelId modelId: soloModels) { + if (ModelById::isa(modelId)) { + m_viewManager->setPlaybackModel(modelId); + newPlaybackModel = modelId; } } - - RangeSummarisableTimeValueModel *a = - dynamic_cast(prevPlaybackModel); - RangeSummarisableTimeValueModel *b = - dynamic_cast(m_viewManager-> - getPlaybackModel()); m_playSource->setSoloModelSet(soloModels); - if (a && b && (a != b)) { - if (m_playSource->isPlaying()) m_playSource->play(frame); + if (!prevPlaybackModel.isNone() && !newPlaybackModel.isNone() && + prevPlaybackModel != newPlaybackModel) { + if (m_playSource->isPlaying()) { + m_playSource->play(frame); + } } } @@ -958,7 +958,6 @@ void MainWindowBase::selectAll() { - if (!getMainModel()) return; m_viewManager->setSelection(Selection(getModelsStartFrame(), getModelsEndFrame())); } @@ -966,23 +965,21 @@ void MainWindowBase::selectToStart() { - if (!getMainModel()) return; - m_viewManager->setSelection(Selection(getMainModel()->getStartFrame(), + m_viewManager->setSelection(Selection(getModelsStartFrame(), m_viewManager->getGlobalCentreFrame())); } void MainWindowBase::selectToEnd() { - if (!getMainModel()) return; m_viewManager->setSelection(Selection(m_viewManager->getGlobalCentreFrame(), - getMainModel()->getEndFrame())); + getModelsEndFrame())); } void MainWindowBase::selectVisible() { - Model *model = getMainModel(); + auto model = getMainModel(); if (!model) return; Pane *currentPane = m_paneStack->getCurrentPane(); @@ -990,11 +987,17 @@ sv_frame_t startFrame, endFrame; - if (currentPane->getStartFrame() < 0) startFrame = 0; - else startFrame = currentPane->getStartFrame(); - - if (currentPane->getEndFrame() > model->getEndFrame()) endFrame = model->getEndFrame(); - else endFrame = currentPane->getEndFrame(); + if (currentPane->getStartFrame() < 0) { + startFrame = 0; + } else { + startFrame = currentPane->getStartFrame(); + } + + if (currentPane->getEndFrame() > model->getEndFrame()) { + endFrame = model->getEndFrame(); + } else { + endFrame = currentPane->getEndFrame(); + } m_viewManager->setSelection(Selection(startFrame, endFrame)); } @@ -1208,9 +1211,8 @@ if (layer) { - Model *model = layer->getModel(); - SparseOneDimensionalModel *sodm = - dynamic_cast(model); + ModelId model = layer->getModel(); + auto sodm = ModelById::getAs(model); if (sodm) { Event point(frame, ""); @@ -1218,7 +1220,7 @@ bool havePrevPoint = false; ChangeEventsCommand *command = - new ChangeEventsCommand(sodm, tr("Add Point")); + new ChangeEventsCommand(model.untyped, tr("Add Point")); if (m_labeller) { @@ -1255,7 +1257,9 @@ .toText(false).c_str())); Command *c = command->finish(); - if (c) CommandHistory::getInstance()->addCommand(c, false); + if (c) { + CommandHistory::getInstance()->addCommand(c, false); + } } } } @@ -1300,13 +1304,16 @@ Layer *layer = pane->getSelectedLayer(); if (!layer) return; - RegionModel *rm = dynamic_cast(layer->getModel()); + ModelId modelId = layer->getModel(); + + auto rm = ModelById::getAs(modelId); if (rm) { Event point(alignedStart, rm->getValueMaximum() + 1, alignedDuration, ""); - ChangeEventsCommand *command = new ChangeEventsCommand(rm, name); + ChangeEventsCommand *command = new ChangeEventsCommand + (modelId.untyped, name); command->add(point); c = command->finish(); } @@ -1316,14 +1323,15 @@ return; } - NoteModel *nm = dynamic_cast(layer->getModel()); + auto nm = ModelById::getAs(modelId); if (nm) { Event point(alignedStart, nm->getValueMinimum(), alignedDuration, 1.f, ""); - ChangeEventsCommand *command = new ChangeEventsCommand(nm, name); + ChangeEventsCommand *command = new ChangeEventsCommand + (modelId.untyped, name); command->add(point); c = command->finish(); } @@ -1344,20 +1352,19 @@ if (!layer) return; MultiSelection ms(m_viewManager->getSelection()); + + ModelId modelId = layer->getModel(); - Model *model = layer->getModel(); - SparseOneDimensionalModel *sodm = dynamic_cast - (model); + auto sodm = ModelById::getAs(modelId); if (!sodm) return; if (!m_labeller) return; Labeller labeller(*m_labeller); labeller.setSampleRate(sodm->getSampleRate()); -/*!!! to be updated after SODM API update - Command *c = labeller.labelAll(*sodm, &ms); + + Command *c = labeller.labelAll(modelId.untyped, &ms, sodm->getAllEvents()); if (c) CommandHistory::getInstance()->addCommand(c, false); -*/ } void @@ -1370,10 +1377,10 @@ if (!layer) return; MultiSelection ms(m_viewManager->getSelection()); + + ModelId modelId = layer->getModel(); - Model *model = layer->getModel(); - SparseOneDimensionalModel *sodm = - dynamic_cast(model); + auto sodm = ModelById::getAs(modelId); if (!sodm) return; if (!m_labeller) return; @@ -1381,12 +1388,8 @@ Labeller labeller(*m_labeller); labeller.setSampleRate(sodm->getSampleRate()); - (void)n; -/*!!! to be updated after SODM API update - Command *c = labeller.subdivide - (*sodm, &ms, n); + Command *c = labeller.subdivide(modelId.untyped, &ms, sodm->getAllEvents(), n); if (c) CommandHistory::getInstance()->addCommand(c, false); -*/ } void @@ -1399,10 +1402,10 @@ if (!layer) return; MultiSelection ms(m_viewManager->getSelection()); + + ModelId modelId = layer->getModel(); - Model *model = layer->getModel(); - SparseOneDimensionalModel *sodm = - dynamic_cast(model); + auto sodm = ModelById::getAs(modelId); if (!sodm) return; if (!m_labeller) return; @@ -1410,12 +1413,8 @@ Labeller labeller(*m_labeller); labeller.setSampleRate(sodm->getSampleRate()); - (void)n; -/*!!! to be updated after SODM API update - Command *c = labeller.winnow - (*sodm, &ms, n); + Command *c = labeller.winnow(modelId.untyped, &ms, sodm->getAllEvents(), n); if (c) CommandHistory::getInstance()->addCommand(c, false); -*/ } MainWindowBase::FileOpenStatus @@ -1565,10 +1564,8 @@ SVDEBUG << "Yes, preserving incoming file rate" << endl; } - ReadOnlyWaveFileModel *newModel = new ReadOnlyWaveFileModel(source, rate); - + auto newModel = std::make_shared(source, rate); if (!newModel->isOK()) { - delete newModel; m_openingAudioFile = false; if (source.wasCancelled()) { return FileOpenCancelled; @@ -1577,12 +1574,13 @@ } } - return addOpenedAudioModel(source, newModel, mode, templateName, true); + auto newModelId = ModelById::add(newModel); + return addOpenedAudioModel(source, newModelId, mode, templateName, true); } MainWindowBase::FileOpenStatus MainWindowBase::addOpenedAudioModel(FileSource source, - WaveFileModel *newModel, + ModelId newModel, AudioFileOpenMode mode, QString templateName, bool registerSource) @@ -1608,7 +1606,7 @@ items, lastMode, &ok); if (!ok || item.isEmpty()) { - delete newModel; + ModelById::release(newModel); m_openingAudioFile = false; return FileOpenCancelled; } @@ -1635,7 +1633,7 @@ if (pane) { if (getMainModel()) { View::ModelSet models(pane->getModels()); - if (models.find(getMainModel()) != models.end()) { + if (models.find(getMainModelId()) != models.end()) { // Current pane contains main model: replace that mode = ReplaceMainModel; } @@ -1652,7 +1650,7 @@ } } - if (mode == CreateAdditionalModel && !getMainModel()) { + if (mode == CreateAdditionalModel && getMainModelId().isNone()) { SVDEBUG << "Mode is CreateAdditionalModel but we have no main model, switching to ReplaceSession mode" << endl; mode = ReplaceSession; } @@ -1690,12 +1688,10 @@ if (mode == ReplaceMainModel) { - Model *prevMain = getMainModel(); - if (prevMain) { + ModelId prevMain = getMainModelId(); + if (!prevMain.isNone()) { m_playSource->removeModel(prevMain); - PlayParameterRepository::getInstance()->removePlayable(prevMain); } - PlayParameterRepository::getInstance()->addPlayable(newModel); SVDEBUG << "SV about to call setMainModel(" << newModel << "): prevMain is " << prevMain << endl; @@ -1728,7 +1724,7 @@ CommandHistory::getInstance()->startCompoundOperation (tr("Import \"%1\"").arg(source.getBasename()), true); - m_document->addImportedModel(newModel); + m_document->addNonDerivedModel(newModel); AddPaneCommand *command = new AddPaneCommand(this); CommandHistory::getInstance()->addCommand(command); @@ -1772,7 +1768,7 @@ CommandHistory::getInstance()->startCompoundOperation (tr("Import \"%1\"").arg(source.getBasename()), true); - m_document->addImportedModel(newModel); + m_document->addNonDerivedModel(newModel); if (replace) { m_document->removeLayerFromView(pane, replace); @@ -1932,30 +1928,33 @@ MIDIFileImportDialog midiDlg(this); - Model *model = DataFileReaderFactory::loadNonCSV + Model *newModelPtr = DataFileReaderFactory::loadNonCSV (path, &midiDlg, getMainModel()->getSampleRate()); - if (!model) { + if (!newModelPtr) { CSVFormatDialog *dialog = new CSVFormatDialog(this, path, getMainModel()->getSampleRate(), 5); if (dialog->exec() == QDialog::Accepted) { - model = DataFileReaderFactory::loadCSV + newModelPtr = DataFileReaderFactory::loadCSV (path, dialog->getFormat(), getMainModel()->getSampleRate()); } delete dialog; } - if (model) { + if (newModelPtr) { SVDEBUG << "MainWindowBase::openLayer: Have model" << endl; emit activity(tr("Import MIDI file \"%1\"").arg(source.getLocation())); - Layer *newLayer = m_document->createImportedLayer(model); + ModelId modelId = + ModelById::add(std::shared_ptr(newModelPtr)); + + Layer *newLayer = m_document->createImportedLayer(modelId); if (newLayer) { @@ -1996,7 +1995,7 @@ return FileOpenWrongMode; } - if (!m_document->getMainModel()) { + if (!getMainModel()) { return FileOpenWrongMode; } @@ -2369,11 +2368,11 @@ return FileOpenFailed; } - std::vector models = importer.getDataModels(&dialog); + std::vector modelIds = importer.getDataModels(&dialog); dialog.setMessage(tr("Importing from RDF...")); - if (models.empty()) { + if (modelIds.empty()) { QMessageBox::critical (this, tr("Failed to import RDF"), tr("Failed to import RDF

No suitable data models found for import from RDF document at \"%1\"

").arg(source.getLocation())); @@ -2381,15 +2380,12 @@ } emit activity(tr("Import RDF document \"%1\"").arg(source.getLocation())); - - std::set added; - - for (int i = 0; i < (int)models.size(); ++i) { - - Model *m = models[i]; - WaveFileModel *w = dynamic_cast(m); - - if (w) { + + std::set added; + + for (auto modelId: modelIds) { + + if (ModelById::isa(modelId)) { Pane *pane = addPaneToStack(); Layer *layer = nullptr; @@ -2399,24 +2395,29 @@ } if (!getMainModel()) { - m_document->setMainModel(w); + m_document->setMainModel(modelId); layer = m_document->createMainModelLayer(LayerFactory::Waveform); } else { - layer = m_document->createImportedLayer(w); + layer = m_document->createImportedLayer(modelId); } m_document->addLayerToView(pane, layer); - added.insert(w); - - for (int j = 0; j < (int)models.size(); ++j) { - - Model *dm = models[j]; - - if (dm == m) continue; - if (dm->getSourceModel() != m) continue; - - layer = m_document->createImportedLayer(dm); + added.insert(modelId); + + for (auto otherId: modelIds) { + + if (otherId == modelId) continue; + + bool isDependent = false; + if (auto dm = ModelById::get(otherId)) { + if (dm->getSourceModel() == modelId) { + isDependent = true; + } + } + if (!isDependent) continue; + + layer = m_document->createImportedLayer(otherId); if (layer->isLayerOpaque() || dynamic_cast(layer)) { @@ -2462,18 +2463,16 @@ m_document->addLayerToView(pane, layer); } - added.insert(dm); + added.insert(otherId); } } } - for (int i = 0; i < (int)models.size(); ++i) { - - Model *m = models[i]; - - if (added.find(m) == added.end()) { + for (auto modelId : modelIds) { + + if (added.find(modelId) == added.end()) { - Layer *layer = m_document->createImportedLayer(m); + Layer *layer = m_document->createImportedLayer(modelId); if (!layer) return FileOpenFailed; Pane *singleLayerPane = addPaneToStack(); @@ -2651,18 +2650,17 @@ } } -WaveFileModel * -MainWindowBase::getMainModel() +ModelId +MainWindowBase::getMainModelId() const { - if (!m_document) return nullptr; + if (!m_document) return {}; return m_document->getMainModel(); } -const WaveFileModel * +std::shared_ptr MainWindowBase::getMainModel() const { - if (!m_document) return nullptr; - return m_document->getMainModel(); + return ModelById::getAs(getMainModelId()); } void @@ -2679,19 +2677,17 @@ connect(m_document, SIGNAL(layerInAView(Layer *, bool)), this, SLOT(layerInAView(Layer *, bool))); - connect(m_document, SIGNAL(modelAdded(Model *)), - this, SLOT(modelAdded(Model *))); - connect(m_document, SIGNAL(mainModelChanged(WaveFileModel *)), - this, SLOT(mainModelChanged(WaveFileModel *))); - connect(m_document, SIGNAL(modelAboutToBeDeleted(Model *)), - this, SLOT(modelAboutToBeDeleted(Model *))); + connect(m_document, SIGNAL(modelAdded(ModelId )), + this, SLOT(modelAdded(ModelId ))); + connect(m_document, SIGNAL(mainModelChanged(ModelId)), + this, SLOT(mainModelChanged(ModelId))); connect(m_document, SIGNAL(modelGenerationFailed(QString, QString)), this, SLOT(modelGenerationFailed(QString, QString))); connect(m_document, SIGNAL(modelRegenerationWarning(QString, QString, QString)), this, SLOT(modelRegenerationWarning(QString, QString, QString))); - connect(m_document, SIGNAL(alignmentComplete(AlignmentModel *)), - this, SLOT(alignmentComplete(AlignmentModel *))); + connect(m_document, SIGNAL(alignmentComplete(ModelId)), + this, SLOT(alignmentComplete(ModelId))); connect(m_document, SIGNAL(alignmentFailed(QString)), this, SLOT(alignmentFailed(QString))); @@ -2791,7 +2787,11 @@ QString suffix = QFileInfo(path).suffix().toLower(); - Model *model = layer->getModel(); + auto model = ModelById::get(layer->getModel()); + if (!model) { + error = tr("Internal error: unknown model"); + return false; + } if (suffix == "xml" || suffix == "svl") { @@ -2819,12 +2819,12 @@ } else if (suffix == "mid" || suffix == "midi") { - NoteModel *nm = dynamic_cast(model); + auto nm = ModelById::getAs(layer->getModel()); if (!nm) { error = tr("Can't export non-note layers to MIDI"); } else { - MIDIFileWriter writer(path, nm, nm->getSampleRate()); + MIDIFileWriter writer(path, nm.get(), nm->getSampleRate()); writer.write(); if (!writer.isOK()) { error = writer.getError(); @@ -2833,10 +2833,10 @@ } else if (suffix == "ttl" || suffix == "n3") { - if (!RDFExporter::canExportModel(model)) { + if (!RDFExporter::canExportModel(model.get())) { 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); + RDFExporter exporter(path, model.get()); exporter.write(); if (!exporter.isOK()) { error = exporter.getError(); @@ -2845,7 +2845,7 @@ } else { - CSVFileWriter writer(path, model, + CSVFileWriter writer(path, model.get(), ((suffix == "csv") ? "," : "\t")); writer.write(); @@ -2923,7 +2923,7 @@ Pane *currentPane = m_paneStack->getCurrentPane(); if (!currentPane) return; - Model *model = getMainModel(); + auto model = getMainModel(); if (!model) return; sv_frame_t start = model->getStartFrame(); @@ -3243,8 +3243,8 @@ SVCERR << "MainWindowBase::record: about to resume" << endl; m_audioIO->resume(); - WritableWaveFileModel *model = m_recordTarget->startRecording(); - if (!model) { + WritableWaveFileModel *modelPtr = m_recordTarget->startRecording(); + if (!modelPtr) { SVCERR << "ERROR: MainWindowBase::record: Recording failed" << endl; QMessageBox::critical (this, tr("Recording failed"), @@ -3253,18 +3253,20 @@ return; } - if (!model->isOK()) { + if (!modelPtr->isOK()) { SVCERR << "MainWindowBase::record: Model not OK, stopping and suspending" << endl; m_recordTarget->stopRecording(); m_audioIO->suspend(); if (action) action->setChecked(false); - delete model; + delete modelPtr; return; } SVCERR << "MainWindowBase::record: Model is OK, continuing..." << endl; + + QString location = modelPtr->getLocation(); - PlayParameterRepository::getInstance()->addPlayable(model); + auto modelId = ModelById::add(std::shared_ptr(modelPtr)); if (m_audioRecordMode == RecordReplaceSession || !getMainModel()) { @@ -3279,7 +3281,7 @@ SVCERR << "MainWindowBase::record: Session template open cancelled, stopping and suspending" << endl; m_recordTarget->stopRecording(); m_audioIO->suspend(); - PlayParameterRepository::getInstance()->removePlayable(model); + ModelById::release(modelId); return; } if (tplStatus != FileOpenFailed) { @@ -3292,17 +3294,16 @@ createDocument(); } - Model *prevMain = getMainModel(); - if (prevMain) { + ModelId prevMain = getMainModelId(); + if (!prevMain.isNone()) { m_playSource->removeModel(prevMain); - PlayParameterRepository::getInstance()->removePlayable(prevMain); } - m_document->setMainModel(model); + m_document->setMainModel(modelId); setupMenus(); findTimeRulerLayer(); - m_originalLocation = model->getLocation(); + m_originalLocation = location; if (loadedTemplate || (m_sessionFile == "")) { CommandHistory::getInstance()->clear(); @@ -3317,7 +3318,7 @@ CommandHistory::getInstance()->startCompoundOperation (tr("Import Recorded Audio"), true); - m_document->addImportedModel(model); + m_document->addNonDerivedModel(modelId); AddPaneCommand *command = new AddPaneCommand(this); CommandHistory::getInstance()->addCommand(command); @@ -3328,7 +3329,7 @@ m_document->addLayerToView(pane, m_timeRulerLayer); } - Layer *newLayer = m_document->createImportedLayer(model); + Layer *newLayer = m_document->createImportedLayer(modelId); if (newLayer) { m_document->addLayerToView(pane, newLayer); @@ -3338,7 +3339,7 @@ } updateMenuStates(); - m_recentFiles.addFile(model->getLocation()); + m_recentFiles.addFile(location); currentPaneChanged(m_paneStack->getCurrentPane()); emit audioFileLoaded(); @@ -3724,10 +3725,7 @@ if (pane) layer = pane->getSelectedLayer(); if (!layer) return; - Model *model = layer->getModel(); - if (!model) return; - - TabularModel *tabular = dynamic_cast(model); + auto tabular = ModelById::getAs(layer->getModel()); if (!tabular) { //!!! how to prevent this function from being active if not //appropriate model type? or will we ultimately support @@ -3746,7 +3744,8 @@ QString title = layer->getLayerPresentationName(); - ModelDataTableDialog *dialog = new ModelDataTableDialog(tabular, title, this); + ModelDataTableDialog *dialog = new ModelDataTableDialog + (layer->getModel(), title, this); dialog->setAttribute(Qt::WA_DeleteOnClose); connectLayerEditDialog(dialog); @@ -4005,10 +4004,10 @@ if (!inAView) removeLayerEditDialog(layer); // Check whether we need to add or remove model from play source - Model *model = layer->getModel(); - if (model) { + ModelId modelId = layer->getModel(); + if (!modelId.isNone()) { if (inAView) { - m_playSource->addModel(model); + m_playSource->addModel(modelId); } else { bool found = false; for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { @@ -4018,7 +4017,7 @@ Layer *pl = pane->getLayer(j); if (pl && !dynamic_cast(pl) && - (pl->getModel() == model)) { + (pl->getModel() == modelId)) { found = true; break; } @@ -4026,7 +4025,7 @@ if (found) break; } if (!found) { - m_playSource->removeModel(model); + m_playSource->removeModel(modelId); } } } @@ -4052,18 +4051,19 @@ } void -MainWindowBase::modelAdded(Model *model) +MainWindowBase::modelAdded(ModelId model) { // SVDEBUG << "MainWindowBase::modelAdded(" << model << ")" << endl; - std::cerr << "\nAdding model " << model->getTypeName() << " to playsource " << std::endl; + std::cerr << "\nAdding model " << model << " to playsource " << std::endl; m_playSource->addModel(model); } void -MainWindowBase::mainModelChanged(WaveFileModel *model) +MainWindowBase::mainModelChanged(ModelId modelId) { // SVDEBUG << "MainWindowBase::mainModelChanged(" << model << ")" << endl; updateDescriptionLabel(); + auto model = ModelById::getAs(modelId); if (model) m_viewManager->setMainModelSampleRate(model->getSampleRate()); if (model && !(m_playTarget || m_audioIO) && (m_soundOptions & WithAudioOutput)) { @@ -4072,16 +4072,6 @@ } void -MainWindowBase::modelAboutToBeDeleted(Model *model) -{ -// SVDEBUG << "MainWindowBase::modelAboutToBeDeleted(" << model << ")" << endl; - if (model == m_viewManager->getPlaybackModel()) { - m_viewManager->setPlaybackModel(nullptr); - } - m_playSource->removeModel(model); -} - -void MainWindowBase::paneDeleteButtonClicked(Pane *pane) { bool found = false; @@ -4118,9 +4108,9 @@ } void -MainWindowBase::alignmentComplete(AlignmentModel *model) +MainWindowBase::alignmentComplete(ModelId alignmentModelId) { - cerr << "MainWindowBase::alignmentComplete(" << model << ")" << endl; + cerr << "MainWindowBase::alignmentComplete(" << alignmentModelId << ")" << endl; } void diff -r a82b9d410393 -r 155008f1bf10 framework/MainWindowBase.h --- a/framework/MainWindowBase.h Fri Jun 14 17:19:37 2019 +0100 +++ b/framework/MainWindowBase.h Wed Jul 17 14:25:41 2019 +0100 @@ -35,6 +35,8 @@ #include "data/fileio/FileSource.h" #include "data/osc/OSCQueue.h" #include "data/osc/OSCMessageCallback.h" +#include "data/model/Model.h" + #include class Document; @@ -302,9 +304,8 @@ virtual void layerAboutToBeDeleted(Layer *); virtual void layerInAView(Layer *, bool); - virtual void mainModelChanged(WaveFileModel *); - virtual void modelAdded(Model *); - virtual void modelAboutToBeDeleted(Model *); + virtual void mainModelChanged(ModelId); + virtual void modelAdded(ModelId); virtual void updateMenuStates(); virtual void updateDescriptionLabel() = 0; @@ -315,7 +316,7 @@ virtual void modelRegenerationFailed(QString, QString, QString) = 0; virtual void modelRegenerationWarning(QString, QString, QString) = 0; - virtual void alignmentComplete(AlignmentModel *); + virtual void alignmentComplete(ModelId); virtual void alignmentFailed(QString) = 0; virtual void rightButtonMenuRequested(Pane *, QPoint point) = 0; @@ -418,12 +419,12 @@ mutable QLabel *m_statusLabel; QLabel *getStatusLabel() const; - WaveFileModel *getMainModel(); - const WaveFileModel *getMainModel() const; + ModelId getMainModelId() const; + std::shared_ptr getMainModel() const; void createDocument(); FileOpenStatus addOpenedAudioModel(FileSource source, - WaveFileModel *model, + ModelId model, AudioFileOpenMode mode, QString templateName, bool registerSource); diff -r a82b9d410393 -r 155008f1bf10 framework/SVFileReader.cpp --- a/framework/SVFileReader.cpp Fri Jun 14 17:19:37 2019 +0100 +++ b/framework/SVFileReader.cpp Wed Jul 17 14:25:41 2019 +0100 @@ -58,12 +58,9 @@ m_paneCallback(callback), m_location(location), m_currentPane(nullptr), + m_currentDataset(XmlExportable::NO_ID), m_currentLayer(nullptr), - m_currentDataset(nullptr), - m_currentDerivedModel(nullptr), - m_currentDerivedModelId(-1), - m_currentPlayParameters(nullptr), - m_currentTransformSource(nullptr), + m_pendingDerivedModel(XmlExportable::NO_ID), m_currentTransformChannel(0), m_currentTransformIsNewStyle(true), m_datasetSeparator(" "), @@ -108,24 +105,31 @@ << endl; } - std::set unaddedModels; + std::set unaddedModels; - for (std::map::iterator i = m_models.begin(); - i != m_models.end(); ++i) { - if (m_addedModels.find(i->second) == m_addedModels.end()) { - unaddedModels.insert(i->second); + for (auto i: m_models) { + if (m_addedModels.find(i.second) == m_addedModels.end()) { + unaddedModels.insert(i.second); } } if (!unaddedModels.empty()) { SVCERR << "WARNING: SV-XML: File contained " - << unaddedModels.size() << " unused models" - << endl; - while (!unaddedModels.empty()) { - delete *unaddedModels.begin(); - unaddedModels.erase(unaddedModels.begin()); + << unaddedModels.size() << " unused models" + << endl; + for (auto m: unaddedModels) { + ModelById::release(m); } - } + } + + if (!m_paths.empty()) { + SVCERR << "WARNING: SV-XML: File contained " + << m_paths.size() << " unused paths" + << endl; + for (auto p: m_paths) { + delete p.second; + } + } } bool @@ -277,15 +281,13 @@ if (name == "dataset") { - if (m_currentDataset) { + if (m_currentDataset != XmlExportable::NO_ID) { bool foundInAwaiting = false; - for (std::map::iterator i = m_awaitingDatasets.begin(); - i != m_awaitingDatasets.end(); ++i) { - if (haveModel(i->second) && - m_models[i->second] == m_currentDataset) { - m_awaitingDatasets.erase(i); + for (auto i: m_awaitingDatasets) { + if (i.second == m_currentDataset) { + m_awaitingDatasets.erase(i.first); foundInAwaiting = true; break; } @@ -296,7 +298,7 @@ } } - m_currentDataset = nullptr; + m_currentDataset = XmlExportable::NO_ID; } else if (name == "data") { @@ -305,23 +307,23 @@ } else if (name == "derivation") { - if (!m_currentDerivedModel) { - if (m_currentDerivedModelId < 0) { - SVCERR << "WARNING: SV-XML: Bad derivation output model id " - << m_currentDerivedModelId << endl; - } else if (haveModel(m_currentDerivedModelId)) { + if (m_currentDerivedModel.isNone()) { + if (m_pendingDerivedModel == XmlExportable::NO_ID) { + SVCERR << "WARNING: SV-XML: No valid output model id " + << "for derivation" << endl; + } else if (haveModel(m_pendingDerivedModel)) { SVCERR << "WARNING: SV-XML: Derivation has existing model " - << m_currentDerivedModelId - << " as target, not regenerating" << endl; + << m_pendingDerivedModel + << " as target, not regenerating" << endl; } else { QString message; - m_currentDerivedModel = m_models[m_currentDerivedModelId] = + m_currentDerivedModel = m_models[m_pendingDerivedModel] = m_document->addDerivedModel (m_currentTransform, ModelTransformer::Input(m_currentTransformSource, m_currentTransformChannel), message); - if (!m_currentDerivedModel) { + if (m_currentDerivedModel.isNone()) { emit modelRegenerationFailed(tr("(derived model in SV-XML)"), m_currentTransform.getIdentifier(), message); @@ -340,9 +342,9 @@ } m_addedModels.insert(m_currentDerivedModel); - m_currentDerivedModel = nullptr; - m_currentDerivedModelId = -1; - m_currentTransformSource = nullptr; + m_currentDerivedModel = {}; + m_pendingDerivedModel = XmlExportable::NO_ID; + m_currentTransformSource = {}; m_currentTransform = Transform(); m_currentTransformChannel = -1; @@ -355,7 +357,7 @@ } else if (name == "selections") { m_inSelections = false; } else if (name == "playparameters") { - m_currentPlayParameters = nullptr; + m_currentPlayParameters = {}; } return true; @@ -406,7 +408,7 @@ void SVFileReader::makeAggregateModels() { - std::map stillPending; + std::map stillPending; for (auto p: m_pendingAggregates) { @@ -415,20 +417,27 @@ bool skip = false; AggregateWaveModel::ChannelSpecList specs; - for (int componentId: rec.components) { + for (ExportId componentId: rec.components) { bool found = false; if (m_models.find(componentId) != m_models.end()) { - RangeSummarisableTimeValueModel *rs = - dynamic_cast - (m_models[componentId]); + ModelId modelId = m_models[componentId]; + auto rs = ModelById::getAs + (modelId); if (rs) { specs.push_back(AggregateWaveModel::ModelChannelSpec - (rs, -1)); + (modelId, -1)); found = true; + } else { + SVDEBUG << "SVFileReader::makeAggregateModels: " + << "Component model id " << componentId + << "in aggregate model id " << id + << "does not appear to be convertible to " + << "RangeSummarisableTimeValueModel" + << endl; } } if (!found) { - SVDEBUG << "SVFileReader::makeAggregateModels:" + SVDEBUG << "SVFileReader::makeAggregateModels: " << "Unknown component model id " << componentId << " in aggregate model id " << id << ", hoping we won't be needing it just yet" @@ -440,13 +449,13 @@ if (skip) { stillPending[id] = rec; } else { - AggregateWaveModel *model = new AggregateWaveModel(specs); + auto model = std::make_shared(specs); model->setObjectName(rec.name); + m_models[id] = ModelById::add(model); - SVDEBUG << "SVFileReader::makeAggregateModels: created aggregate model id " - << id << " with " << specs.size() << " components" << endl; - - m_models[id] = model; + SVDEBUG << "SVFileReader::makeAggregateModels: created aggregate " + << "model id " << id << " with " << specs.size() + << " components" << endl; } } @@ -457,32 +466,21 @@ SVFileReader::addUnaddedModels() { makeAggregateModels(); - - for (std::map::iterator i = m_models.begin(); - i != m_models.end(); ++i) { - Model *model = i->second; + for (auto i: m_models) { - if (m_addedModels.find(model) != m_addedModels.end()) { + ModelId modelId = i.second; + + if (m_addedModels.find(modelId) != m_addedModels.end()) { // already added this one continue; } + + m_document->addNonDerivedModel(modelId); - // 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(model) && - !dynamic_cast(model)) { - - m_document->addImportedModel(model); - } - - // 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); + // make a note of all models that have been added to the + // document, so they don't get released by our own destructor + m_addedModels.insert(modelId); } } @@ -538,7 +536,8 @@ } else if (rate == 0 && !isMainModel && Preferences::getInstance()->getResampleOnLoad()) { - WaveFileModel *mm = m_document->getMainModel(); + auto mm = ModelById::getAs + (m_document->getMainModel()); if (mm) rate = mm->getSampleRate(); } @@ -555,10 +554,13 @@ } model->setObjectName(name); - m_models[id] = model; + + ModelId modelId = ModelById::add(std::shared_ptr(model)); + m_models[id] = modelId; + if (isMainModel) { - m_document->setMainModel(model); - m_addedModels.insert(model); + m_document->setMainModel(modelId); + m_addedModels.insert(modelId); } // Derived models will be added when their derivation // is found. @@ -570,13 +572,13 @@ QString components = attributes.value("components"); QStringList componentIdStrings = components.split(","); std::vector componentIds; - for (auto cid: componentIdStrings) { + for (auto cidStr: componentIdStrings) { bool ok = false; - int id = cid.toInt(&ok); + int cid = cidStr.toInt(&ok); if (!ok) { - SVCERR << "SVFileReader::readModel: Failed to convert component model id from part \"" << cid << "\" in \"" << components << "\"" << endl; + SVCERR << "SVFileReader::readModel: Failed to convert component model id from part \"" << cidStr << "\" in \"" << components << "\"" << endl; } else { - componentIds.push_back(id); + componentIds.push_back(cid); } } PendingAggregateRec rec { name, sampleRate, componentIds }; @@ -603,10 +605,12 @@ READ_MANDATORY(int, windowSize, toInt); READ_MANDATORY(int, yBinCount, toInt); - EditableDenseThreeDimensionalModel *model = - new EditableDenseThreeDimensionalModel + auto model = std::make_shared (sampleRate, windowSize, yBinCount, EditableDenseThreeDimensionalModel::NoCompression); + + model->setObjectName(name); + m_models[id] = ModelById::add(model); float minimum = attributes.value("minimum").trimmed().toFloat(&ok); if (ok) model->setMinimumLevel(minimum); @@ -620,8 +624,6 @@ int startFrame = attributes.value("startFrame").trimmed().toInt(&ok); if (ok) model->setStartFrame(startFrame); - model->setObjectName(name); - m_models[id] = model; return true; } else { @@ -636,21 +638,21 @@ if (dimensions == 1) { READ_MANDATORY(int, resolution, toInt); - + if (attributes.value("subtype") == "image") { bool notifyOnAdd = (attributes.value("notifyOnAdd") == "true"); - ImageModel *model = new ImageModel(sampleRate, resolution, - notifyOnAdd); + auto model = std::make_shared + (sampleRate, resolution, notifyOnAdd); model->setObjectName(name); - m_models[id] = model; + m_models[id] = ModelById::add(model); } else { - SparseOneDimensionalModel *model = new SparseOneDimensionalModel + auto model = std::make_shared (sampleRate, resolution); model->setObjectName(name); - m_models[id] = model; + m_models[id] = ModelById::add(model); } int dataset = attributes.value("dataset").trimmed().toInt(&ok); @@ -677,73 +679,74 @@ if (dimensions == 2) { if (attributes.value("subtype") == "text") { - TextModel *model = new TextModel + auto model = std::make_shared (sampleRate, resolution, notifyOnAdd); model->setObjectName(name); - m_models[id] = model; + m_models[id] = ModelById::add(model); } else if (attributes.value("subtype") == "path") { - PathModel *model = new PathModel - (sampleRate, resolution, notifyOnAdd); - model->setObjectName(name); - m_models[id] = model; + // Paths are no longer actually models + Path *path = new Path(sampleRate, resolution); + m_paths[id] = path; } else { - SparseTimeValueModel *model; + std::shared_ptr model; if (haveMinMax) { - model = new SparseTimeValueModel - (sampleRate, resolution, minimum, maximum, notifyOnAdd); + model = std::make_shared + (sampleRate, resolution, minimum, maximum, + notifyOnAdd); } else { - model = new SparseTimeValueModel + model = std::make_shared (sampleRate, resolution, notifyOnAdd); } model->setScaleUnits(units); model->setObjectName(name); - m_models[id] = model; + m_models[id] = ModelById::add(model); } } else { if (attributes.value("subtype") == "region") { - RegionModel *model; + std::shared_ptr model; if (haveMinMax) { - model = new RegionModel - (sampleRate, resolution, minimum, maximum, notifyOnAdd); + model = std::make_shared + (sampleRate, resolution, minimum, maximum, + notifyOnAdd); } else { - model = new RegionModel + model = std::make_shared (sampleRate, resolution, notifyOnAdd); } model->setValueQuantization(valueQuantization); model->setScaleUnits(units); model->setObjectName(name); - m_models[id] = model; + m_models[id] = ModelById::add(model); } else if (attributes.value("subtype") == "flexinote") { - NoteModel *model; + std::shared_ptr model; if (haveMinMax) { - model = new NoteModel + model = std::make_shared (sampleRate, resolution, minimum, maximum, notifyOnAdd, NoteModel::FLEXI_NOTE); } else { - model = new NoteModel + model = std::make_shared (sampleRate, resolution, notifyOnAdd, NoteModel::FLEXI_NOTE); } model->setValueQuantization(valueQuantization); model->setScaleUnits(units); model->setObjectName(name); - m_models[id] = model; + m_models[id] = ModelById::add(model); } else { // note models written out by SV 1.3 and earlier // have no subtype, so we can't test that - NoteModel *model; + std::shared_ptr model; if (haveMinMax) { - model = new NoteModel + model = std::make_shared (sampleRate, resolution, minimum, maximum, notifyOnAdd); } else { - model = new NoteModel + model = std::make_shared (sampleRate, resolution, notifyOnAdd); } model->setValueQuantization(valueQuantization); model->setScaleUnits(units); model->setObjectName(name); - m_models[id] = model; + m_models[id] = ModelById::add(model); } } @@ -764,7 +767,8 @@ READ_MANDATORY(int, aligned, toInt); READ_MANDATORY(int, path, toInt); - Model *refModel = nullptr, *alignedModel = nullptr, *pathModel = nullptr; + ModelId refModel, alignedModel; + Path *pathPtr = nullptr; if (m_models.find(reference) != m_models.end()) { refModel = m_models[reference]; @@ -782,31 +786,30 @@ << endl; } - if (m_models.find(path) != m_models.end()) { - pathModel = m_models[path]; + if (m_paths.find(path) != m_paths.end()) { + pathPtr = m_paths[path]; } else { - SVCERR << "WARNING: SV-XML: Unknown path model id " + SVCERR << "WARNING: SV-XML: Unknown path id " << path << " in alignment model id " << id << endl; } - if (refModel && alignedModel && pathModel) { - AlignmentModel *model = new AlignmentModel - (refModel, alignedModel, nullptr); - PathModel *pm = dynamic_cast(pathModel); - if (!pm) { - SVCERR << "WARNING: SV-XML: Model id " << path - << " referenced as path for alignment " << id - << " is not a path model" << endl; - } else { - model->setPath(pm); - pm->setCompletion(100); + if (!refModel.isNone() && !alignedModel.isNone() && pathPtr) { + auto model = std::make_shared + (refModel, alignedModel, ModelId()); + model->setPath(*pathPtr); + model->setObjectName(name); + m_models[id] = ModelById::add(model); + if (auto am = ModelById::get(alignedModel)) { + am->setAlignment(m_models[id]); } - model->setObjectName(name); - m_models[id] = model; - alignedModel->setAlignment(model); return true; } + + if (pathPtr) { + delete pathPtr; + m_paths.erase(path); + } } else { @@ -972,11 +975,10 @@ if (modelOk) { if (haveModel(modelId)) { - Model *model = m_models[modelId]; - m_document->setModel(layer, model); + m_document->setModel(layer, m_models[modelId]); } else { SVCERR << "WARNING: SV-XML: Unknown model id " << modelId - << " in layer definition" << endl; + << " in layer definition" << endl; if (!layer->canExistWithoutModel()) { // Don't add a layer with an unknown model id // unless it explicitly supports this state @@ -1028,14 +1030,18 @@ return false; } - int modelId = m_awaitingDatasets[id]; + int awaitingId = m_awaitingDatasets[id]; + + ModelId modelId; + Path *path = nullptr; - Model *model = nullptr; - if (haveModel(modelId)) { - model = m_models[modelId]; + if (haveModel(awaitingId)) { + modelId = m_models[awaitingId]; + } else if (m_paths.find(awaitingId) != m_paths.end()) { + path = m_paths[awaitingId]; } else { - SVCERR << "WARNING: SV-XML: Internal error: Unknown model " << modelId - << " expecting dataset " << id << endl; + SVCERR << "WARNING: SV-XML: Internal error: Unknown model or path " + << modelId << " awaiting dataset " << id << endl; return false; } @@ -1043,33 +1049,37 @@ switch (dimensions) { case 1: - if (dynamic_cast(model)) good = true; - else if (dynamic_cast(model)) good = true; + good = + (ModelById::isa(modelId) || + ModelById::isa(modelId)); break; case 2: - if (dynamic_cast(model)) good = true; - else if (dynamic_cast(model)) good = true; - else if (dynamic_cast(model)) good = true; + good = + (ModelById::isa(modelId) || + ModelById::isa(modelId) || + path); break; case 3: - if (dynamic_cast(model)) good = true; - else if (dynamic_cast(model)) good = true; - else if (dynamic_cast(model)) { + if (ModelById::isa(modelId)) { + good = true; m_datasetSeparator = attributes.value("separator"); - good = true; + } else { + good = + (ModelById::isa(modelId) || + ModelById::isa(modelId)); } break; } if (!good) { SVCERR << "WARNING: SV-XML: Model id " << modelId << " has wrong number of dimensions or inappropriate type for " << dimensions << "-D dataset " << id << endl; - m_currentDataset = nullptr; + m_currentDataset = XmlExportable::NO_ID; return false; } - m_currentDataset = model; + m_currentDataset = awaitingId; return true; } @@ -1080,23 +1090,28 @@ READ_MANDATORY(int, frame, toInt); -// SVDEBUG << "SVFileReader::addPointToDataset: frame = " << frame << endl; + if (m_paths.find(m_currentDataset) != m_paths.end()) { + Path *path = m_paths[m_currentDataset]; + int mapframe = attributes.value("mapframe").trimmed().toInt(&ok); + path->add(PathPoint(frame, mapframe)); + return ok; + } - SparseOneDimensionalModel *sodm = dynamic_cast - (m_currentDataset); + if (!haveModel(m_currentDataset)) { + SVCERR << "WARNING: SV-XML: Point element found in non-point dataset" + << endl; + return false; + } + + ModelId modelId = m_models[m_currentDataset]; - if (sodm) { -// SVCERR << "Current dataset is a sparse one dimensional model" << endl; + if (auto sodm = ModelById::getAs(modelId)) { QString label = attributes.value("label"); sodm->add(Event(frame, label)); return true; } - SparseTimeValueModel *stvm = dynamic_cast - (m_currentDataset); - - if (stvm) { -// SVCERR << "Current dataset is a sparse time-value model" << endl; + if (auto stvm = ModelById::getAs(modelId)) { float value = 0.0; value = attributes.value("value").trimmed().toFloat(&ok); QString label = attributes.value("label"); @@ -1104,10 +1119,7 @@ return ok; } - NoteModel *nm = dynamic_cast(m_currentDataset); - - if (nm) { -// SVCERR << "Current dataset is a note model" << endl; + if (auto nm = ModelById::getAs(modelId)) { float value = 0.0; value = attributes.value("value").trimmed().toFloat(&ok); int duration = 0; @@ -1122,10 +1134,7 @@ return ok; } - RegionModel *rm = dynamic_cast(m_currentDataset); - - if (rm) { -// SVCERR << "Current dataset is a region model" << endl; + if (auto rm = ModelById::getAs(modelId)) { float value = 0.0; value = attributes.value("value").trimmed().toFloat(&ok); int duration = 0; @@ -1135,40 +1144,23 @@ return ok; } - TextModel *tm = dynamic_cast(m_currentDataset); - - if (tm) { -// SVCERR << "Current dataset is a text model" << endl; + if (auto tm = ModelById::getAs(modelId)) { float height = 0.0; 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->add(Event(frame, height, label)); return ok; } - PathModel *pm = dynamic_cast(m_currentDataset); - - if (pm) { -// 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->add(PathPoint(frame, mapframe)); - return ok; - } - - ImageModel *im = dynamic_cast(m_currentDataset); - - if (im) { -// SVCERR << "Current dataset is an image model" << endl; + if (auto im = ModelById::getAs(modelId)) { QString image = attributes.value("image"); QString label = attributes.value("label"); -// SVDEBUG << "SVFileReader::addPointToDataset: ImageModel: frame = " << frame << ", image = " << image << ", label = " << label << ", ok = " << ok << endl; im->add(Event(frame).withURI(image).withLabel(label)); return ok; } - SVCERR << "WARNING: SV-XML: Point element found in non-point dataset" << endl; + SVCERR << "WARNING: SV-XML: Point element found in non-point dataset" + << endl; return false; } @@ -1176,11 +1168,16 @@ bool SVFileReader::addBinToDataset(const QXmlAttributes &attributes) { - EditableDenseThreeDimensionalModel *dtdm = - dynamic_cast - (m_currentDataset); + if (!haveModel(m_currentDataset)) { + SVCERR << "WARNING: SV-XML: Bin definition found in incompatible dataset" + << endl; + return false; + } + + ModelId modelId = m_models[m_currentDataset]; - if (dtdm) { + if (auto dtdm = ModelById::getAs + (modelId)) { bool ok = false; int n = attributes.value("number").trimmed().toInt(&ok); @@ -1191,12 +1188,12 @@ } QString name = attributes.value("name"); - dtdm->setBinName(n, name); return true; } - SVCERR << "WARNING: SV-XML: Bin definition found in incompatible dataset" << endl; + SVCERR << "WARNING: SV-XML: Bin definition found in incompatible dataset" + << endl; return false; } @@ -1225,13 +1222,17 @@ bool SVFileReader::readRowData(const QString &text) { - EditableDenseThreeDimensionalModel *dtdm = - dynamic_cast - (m_currentDataset); - + if (!haveModel(m_currentDataset)) { + SVCERR << "WARNING: SV-XML: Row data found in non-row dataset" << endl; + return false; + } + + ModelId modelId = m_models[m_currentDataset]; bool warned = false; - if (dtdm) { + if (auto dtdm = ModelById::getAs + (modelId)) { + QStringList data = text.split(m_datasetSeparator); DenseThreeDimensionalModel::Column values; @@ -1262,30 +1263,29 @@ } SVCERR << "WARNING: SV-XML: Row data found in non-row dataset" << endl; - return false; } bool SVFileReader::readDerivation(const QXmlAttributes &attributes) { - int modelId = 0; + int modelExportId = 0; bool modelOk = false; - modelId = attributes.value("model").trimmed().toInt(&modelOk); + modelExportId = attributes.value("model").trimmed().toInt(&modelOk); if (!modelOk) { SVCERR << "WARNING: SV-XML: No model id specified for derivation" << endl; return false; } - if (haveModel(modelId)) { - m_currentDerivedModel = m_models[modelId]; + if (haveModel(modelExportId)) { + m_currentDerivedModel = m_models[modelExportId]; } else { // we'll regenerate the model when the derivation element ends - m_currentDerivedModel = nullptr; + m_currentDerivedModel = {}; } - m_currentDerivedModelId = modelId; + m_pendingDerivedModel = modelExportId; int sourceId = 0; bool sourceOk = false; @@ -1330,7 +1330,10 @@ int windowType = attributes.value("windowType").trimmed().toInt(&ok); if (ok) m_currentTransform.setWindowType(WindowType(windowType)); - if (!m_currentTransformSource) return true; + auto currentTransformSourceModel = ModelById::get(m_currentTransformSource); + if (!currentTransformSourceModel) return true; + + sv_samplerate_t sampleRate = currentTransformSourceModel->getSampleRate(); QString startFrameStr = attributes.value("startFrame"); QString durationStr = attributes.value("duration"); @@ -1348,12 +1351,10 @@ } m_currentTransform.setStartTime - (RealTime::frame2RealTime - (startFrame, m_currentTransformSource->getSampleRate())); + (RealTime::frame2RealTime(startFrame, sampleRate)); m_currentTransform.setDuration - (RealTime::frame2RealTime - (duration, m_currentTransformSource->getSampleRate())); + (RealTime::frame2RealTime(duration, sampleRate)); return true; } @@ -1361,27 +1362,27 @@ bool SVFileReader::readPlayParameters(const QXmlAttributes &attributes) { - m_currentPlayParameters = nullptr; + m_currentPlayParameters = {}; - int modelId = 0; + int modelExportId = 0; bool modelOk = false; - modelId = attributes.value("model").trimmed().toInt(&modelOk); + modelExportId = attributes.value("model").trimmed().toInt(&modelOk); if (!modelOk) { SVCERR << "WARNING: SV-XML: No model id specified for play parameters" << endl; return false; } - if (haveModel(modelId)) { + if (haveModel(modelExportId)) { bool ok = false; - PlayParameters *parameters = PlayParameterRepository::getInstance()-> - getPlayParameters(m_models[modelId]); + auto parameters = PlayParameterRepository::getInstance()-> + getPlayParameters(m_models[modelExportId].untyped); if (!parameters) { SVCERR << "WARNING: SV-XML: Play parameters for model " - << modelId + << modelExportId << " not found - has model been added to document?" << endl; return false; @@ -1401,12 +1402,12 @@ m_currentPlayParameters = parameters; -// SVCERR << "Current play parameters for model: " << m_models[modelId] << ": " << m_currentPlayParameters << endl; +// SVCERR << "Current play parameters for model: " << m_models[modelExportId] << ": " << m_currentPlayParameters << endl; } else { - SVCERR << "WARNING: SV-XML: Unknown model " << modelId - << " for play parameters" << endl; + SVCERR << "WARNING: SV-XML: Unknown model " << modelExportId + << " for play parameters" << endl; return false; } @@ -1416,7 +1417,7 @@ bool SVFileReader::readPlugin(const QXmlAttributes &attributes) { - if (m_currentDerivedModelId >= 0) { + if (m_pendingDerivedModel != XmlExportable::NO_ID) { return readPluginForTransform(attributes); } else if (m_currentPlayParameters) { return readPluginForPlayback(attributes); @@ -1467,7 +1468,7 @@ bool SVFileReader::readTransform(const QXmlAttributes &attributes) { - if (m_currentDerivedModelId < 0) { + if (m_pendingDerivedModel == XmlExportable::NO_ID) { SVCERR << "WARNING: SV-XML: Transform found outside derivation" << endl; return false; } @@ -1480,7 +1481,7 @@ bool SVFileReader::readParameter(const QXmlAttributes &attributes) { - if (m_currentDerivedModelId < 0) { + if (m_pendingDerivedModel == XmlExportable::NO_ID) { SVCERR << "WARNING: SV-XML: Parameter found outside derivation" << endl; return false; } diff -r a82b9d410393 -r 155008f1bf10 framework/SVFileReader.h --- a/framework/SVFileReader.h Fri Jun 14 17:19:37 2019 +0100 +++ b/framework/SVFileReader.h Wed Jul 17 14:25:41 2019 +0100 @@ -25,6 +25,7 @@ class Pane; class Model; +class Path; class Document; class PlayParameters; @@ -182,15 +183,15 @@ void setCurrentPane(Pane *pane) { m_currentPane = pane; } bool startElement(const QString &namespaceURI, - const QString &localName, - const QString &qName, - const QXmlAttributes& atts) override; - + const QString &localName, + const QString &qName, + const QXmlAttributes& atts) override; + bool characters(const QString &) override; bool endElement(const QString &namespaceURI, - const QString &localName, - const QString &qName) override; + const QString &localName, + const QString &qName) override; bool error(const QXmlParseException &exception) override; bool fatalError(const QXmlParseException &exception) override; @@ -233,32 +234,57 @@ void makeAggregateModels(); void addUnaddedModels(); - bool haveModel(int id) { - return (m_models.find(id) != m_models.end()) && m_models[id]; + // We use the term "pending" of things that have been referred to + // but not yet constructed because their definitions are + // incomplete. They are just referred to with an ExportId. Models + // that have been constructed are always added straight away to + // ById and are referred to with a ModelId (everywhere where + // previously we would have used a Model *). m_models maps from + // ExportId (as read from the file) to complete Models, or to a + // ModelId of None for any model that could not be constructed for + // some reason. + + typedef XmlExportable::ExportId ExportId; + + bool haveModel(ExportId id) { + return (m_models.find(id) != m_models.end()) && !m_models[id].isNone(); } - + struct PendingAggregateRec { QString name; sv_samplerate_t sampleRate; - std::vector components; + std::vector components; }; Document *m_document; SVFileReaderPaneCallback &m_paneCallback; QString m_location; Pane *m_currentPane; - std::map m_layers; - std::map m_models; - std::set m_addedModels; - std::map m_pendingAggregates; - std::map m_awaitingDatasets; // map dataset id -> model id + std::map m_layers; + std::map m_models; + std::map m_paths; + std::set m_addedModels; // i.e. added to Document, not just ById + std::map m_pendingAggregates; + + // A model element often contains a dataset id, and the dataset + // then follows it. When the model is read, an entry in this map + // is added, mapping from the dataset's export id (the actual + // dataset has not been read yet) back to the export id of the + // object that needs it. We map to export id rather than model id, + // because the object might be a path rather than a model. + std::map m_awaitingDatasets; + + // And then this is the model or path that a dataset element is + // currently being read into, i.e. the value looked up from + // m_awaitingDatasets at the point where the dataset is found. + ExportId m_currentDataset; + Layer *m_currentLayer; - Model *m_currentDataset; - Model *m_currentDerivedModel; - int m_currentDerivedModelId; - PlayParameters *m_currentPlayParameters; + ModelId m_currentDerivedModel; + ExportId m_pendingDerivedModel; + std::shared_ptr m_currentPlayParameters; Transform m_currentTransform; - Model *m_currentTransformSource; + ModelId m_currentTransformSource; int m_currentTransformChannel; bool m_currentTransformIsNewStyle; QString m_datasetSeparator; diff -r a82b9d410393 -r 155008f1bf10 framework/TransformUserConfigurator.cpp --- a/framework/TransformUserConfigurator.cpp Fri Jun 14 17:19:37 2019 +0100 +++ b/framework/TransformUserConfigurator.cpp Wed Jul 17 14:25:41 2019 +0100 @@ -60,11 +60,11 @@ TransformUserConfigurator::configure(ModelTransformer::Input &input, Transform &transform, Vamp::PluginBase *plugin, - Model *&inputModel, + ModelId &inputModel, AudioPlaySource *source, sv_frame_t startFrame, sv_frame_t duration, - const QMap &modelMap, + const QMap &modelMap, QStringList candidateModelNames, QString defaultModelName) { @@ -121,8 +121,6 @@ std::vector od = vp->getOutputDescriptors(); -// cerr << "configure: looking for output: " << output << endl; - if (od.size() > 1) { for (size_t i = 0; i < od.size(); ++i) { if (od[i].identifier == output.toStdString()) { @@ -135,9 +133,9 @@ } int sourceChannels = 1; - if (dynamic_cast(inputModel)) { - sourceChannels = dynamic_cast(inputModel) - ->getChannelCount(); + + if (auto dtvm = ModelById::getAs(inputModel)) { + sourceChannels = dtvm->getChannelCount(); } int minChannels = 1, maxChannels = sourceChannels; @@ -203,12 +201,17 @@ //around all this misc stuff, but that's for tomorrow //(whenever that may be) + sv_samplerate_t sampleRate = 0; + if (auto m = ModelById::get(inputModel)) { + sampleRate = m->getSampleRate(); + } + if (startFrame != 0 || duration != 0) { - if (dialog->getSelectionOnly()) { - transform.setStartTime(RealTime::frame2RealTime - (startFrame, inputModel->getSampleRate())); - transform.setDuration(RealTime::frame2RealTime - (duration, inputModel->getSampleRate())); + if (dialog->getSelectionOnly() && sampleRate != 0) { + transform.setStartTime + (RealTime::frame2RealTime(startFrame, sampleRate)); + transform.setDuration + (RealTime::frame2RealTime(duration, sampleRate)); } } diff -r a82b9d410393 -r 155008f1bf10 framework/TransformUserConfigurator.h --- a/framework/TransformUserConfigurator.h Fri Jun 14 17:19:37 2019 +0100 +++ b/framework/TransformUserConfigurator.h Wed Jul 17 14:25:41 2019 +0100 @@ -23,15 +23,15 @@ // This is of course absolutely gross bool configure(ModelTransformer::Input &input, - Transform &transform, - Vamp::PluginBase *plugin, - Model *&inputModel, - AudioPlaySource *source, - sv_frame_t startFrame, - sv_frame_t duration, - const QMap &modelMap, - QStringList candidateModelNames, - QString defaultModelName) override; + Transform &transform, + Vamp::PluginBase *plugin, + ModelId &inputModel, + AudioPlaySource *source, + sv_frame_t startFrame, + sv_frame_t duration, + const QMap &modelMap, + QStringList candidateModelNames, + QString defaultModelName) override; static void setParentWidget(QWidget *);