# HG changeset patch # User Chris Cannam # Date 1561039100 -3600 # Node ID 649ac57c5a2dee598aa00eaa29a2d58a7e404575 # Parent 8efce64dd85ec5669cf094df639996eddb0bc78f# Parent a47a23c824ac5f5983e7f1e02b63b5f5a5eb75e5 Merge diff -r a47a23c824ac -r 649ac57c5a2d base/Clipboard.h --- a/base/Clipboard.h Wed May 22 15:37:44 2019 +0100 +++ b/base/Clipboard.h Thu Jun 20 14:58:20 2019 +0100 @@ -36,7 +36,7 @@ bool referenceFramesDiffer() const; protected: - EventVector m_points; + EventVector m_points; }; #endif diff -r a47a23c824ac -r 649ac57c5a2d data/midi/MIDIInput.cpp --- a/data/midi/MIDIInput.cpp Wed May 22 15:37:44 2019 +0100 +++ b/data/midi/MIDIInput.cpp Thu Jun 20 14:58:20 2019 +0100 @@ -119,7 +119,7 @@ SVCERR << "ERROR: MIDIInput::postEvent: MIDI event queue is full and not clearing -- abandoning incoming event" << endl; return; } - SVCERR << "WARNING: MIDIInput::postEvent: MIDI event queue (capacity " << m_buffer.getSize() << " is full!" << endl; + SVCERR << "WARNING: MIDIInput::postEvent: MIDI event queue (capacity " << m_buffer.getSize() << ") is full!" << endl; SVDEBUG << "Waiting for something to be processed" << endl; #ifdef _WIN32 Sleep(1); diff -r a47a23c824ac -r 649ac57c5a2d data/model/AggregateWaveModel.h --- a/data/model/AggregateWaveModel.h Wed May 22 15:37:44 2019 +0100 +++ b/data/model/AggregateWaveModel.h Thu Jun 20 14:58:20 2019 +0100 @@ -62,7 +62,7 @@ float getValueMaximum() const override { return 1.0f; } sv_frame_t getStartFrame() const override { return 0; } - sv_frame_t getEndFrame() const override { return getFrameCount(); } + sv_frame_t getTrueEndFrame() const override { return getFrameCount(); } floatvec_t getData(int channel, sv_frame_t start, sv_frame_t count) const override; diff -r a47a23c824ac -r 649ac57c5a2d data/model/AlignmentModel.cpp --- a/data/model/AlignmentModel.cpp Wed May 22 15:37:44 2019 +0100 +++ b/data/model/AlignmentModel.cpp Thu Jun 20 14:58:20 2019 +0100 @@ -90,7 +90,7 @@ } sv_frame_t -AlignmentModel::getEndFrame() const +AlignmentModel::getTrueEndFrame() const { sv_frame_t a = m_reference->getEndFrame(); sv_frame_t b = m_aligned->getEndFrame(); diff -r a47a23c824ac -r 649ac57c5a2d data/model/AlignmentModel.h --- a/data/model/AlignmentModel.h Wed May 22 15:37:44 2019 +0100 +++ b/data/model/AlignmentModel.h Thu Jun 20 14:58:20 2019 +0100 @@ -41,7 +41,7 @@ QString getError() const { return m_error; } sv_frame_t getStartFrame() const override; - sv_frame_t getEndFrame() const override; + sv_frame_t getTrueEndFrame() const override; sv_samplerate_t getSampleRate() const override; bool isReady(int *completion = 0) const override; int getCompletion() const override { diff -r a47a23c824ac -r 649ac57c5a2d data/model/Dense3DModelPeakCache.h --- a/data/model/Dense3DModelPeakCache.h Wed May 22 15:37:44 2019 +0100 +++ b/data/model/Dense3DModelPeakCache.h Thu Jun 20 14:58:20 2019 +0100 @@ -40,8 +40,8 @@ return m_source->getStartFrame(); } - sv_frame_t getEndFrame() const override { - return m_source->getEndFrame(); + sv_frame_t getTrueEndFrame() const override { + return m_source->getTrueEndFrame(); } int getResolution() const override { diff -r a47a23c824ac -r 649ac57c5a2d data/model/EditableDenseThreeDimensionalModel.cpp --- a/data/model/EditableDenseThreeDimensionalModel.cpp Wed May 22 15:37:44 2019 +0100 +++ b/data/model/EditableDenseThreeDimensionalModel.cpp Thu Jun 20 14:58:20 2019 +0100 @@ -83,7 +83,7 @@ } sv_frame_t -EditableDenseThreeDimensionalModel::getEndFrame() const +EditableDenseThreeDimensionalModel::getTrueEndFrame() const { return m_resolution * m_data.size() + (m_resolution - 1); } diff -r a47a23c824ac -r 649ac57c5a2d data/model/EditableDenseThreeDimensionalModel.h --- a/data/model/EditableDenseThreeDimensionalModel.h Wed May 22 15:37:44 2019 +0100 +++ b/data/model/EditableDenseThreeDimensionalModel.h Thu Jun 20 14:58:20 2019 +0100 @@ -55,7 +55,7 @@ sv_samplerate_t getSampleRate() const override; sv_frame_t getStartFrame() const override; - sv_frame_t getEndFrame() const override; + sv_frame_t getTrueEndFrame() const override; /** * Set the frame offset of the first column. diff -r a47a23c824ac -r 649ac57c5a2d data/model/FFTModel.h --- a/data/model/FFTModel.h Wed May 22 15:37:44 2019 +0100 +++ b/data/model/FFTModel.h Thu Jun 20 14:58:20 2019 +0100 @@ -65,7 +65,7 @@ float getValueAt(int x, int y) const override { return getMagnitudeAt(x, y); } bool isOK() const override { return m_model && m_model->isOK(); } sv_frame_t getStartFrame() const override { return 0; } - sv_frame_t getEndFrame() const override { + sv_frame_t getTrueEndFrame() const override { return sv_frame_t(getWidth()) * getResolution() + getResolution(); } sv_samplerate_t getSampleRate() const override { diff -r a47a23c824ac -r 649ac57c5a2d data/model/ImageModel.h --- a/data/model/ImageModel.h Wed May 22 15:37:44 2019 +0100 +++ b/data/model/ImageModel.h Thu Jun 20 14:58:20 2019 +0100 @@ -61,7 +61,7 @@ sv_frame_t getStartFrame() const override { return m_events.getStartFrame(); } - sv_frame_t getEndFrame() const override { + sv_frame_t getTrueEndFrame() const override { if (m_events.isEmpty()) return 0; sv_frame_t e = m_events.getEndFrame() + 1; if (e % m_resolution == 0) return e; diff -r a47a23c824ac -r 649ac57c5a2d data/model/Model.h --- a/data/model/Model.h Wed May 22 15:37:44 2019 +0100 +++ b/data/model/Model.h Thu Jun 20 14:58:20 2019 +0100 @@ -62,8 +62,36 @@ * frames (as a multiple of the resolution) spanned by the * model. This is broadly consistent with the definition of the * end frame of a Selection object. + * + * If the end has been extended by extendEndFrame() beyond the + * true end frame, return the extended end instead. This is + * usually the behaviour you want. */ - virtual sv_frame_t getEndFrame() const = 0; + sv_frame_t getEndFrame() const { + sv_frame_t trueEnd = getTrueEndFrame(); + if (m_extendTo > trueEnd) { + return m_extendTo; + } else { + return trueEnd; + } + } + + /** + * Return the audio frame at the end of the model. This is + * identical to getEndFrame(), except that it ignores any extended + * duration set with extendEndFrame(). + */ + virtual sv_frame_t getTrueEndFrame() const = 0; + + /** + * Extend the end of the model. If this is set to something beyond + * the true end of the data within the model, then getEndFrame() + * will return this value instead of the true end. (This is used + * by the Tony application.) + */ + void extendEndFrame(sv_frame_t to) { + m_extendTo = to; + } /** * Return the frame rate in frames per second. @@ -315,7 +343,8 @@ m_sourceModel(0), m_alignment(0), m_abandoning(false), - m_aboutToDelete(false) { } + m_aboutToDelete(false), + m_extendTo(0) { } // Not provided. Model(const Model &); @@ -327,7 +356,8 @@ QString m_typeUri; bool m_abandoning; bool m_aboutToDelete; - + sv_frame_t m_extendTo; + int getNextId(); }; diff -r a47a23c824ac -r 649ac57c5a2d data/model/NoteModel.h --- a/data/model/NoteModel.h Wed May 22 15:37:44 2019 +0100 +++ b/data/model/NoteModel.h Thu Jun 20 14:58:20 2019 +0100 @@ -102,7 +102,7 @@ sv_frame_t getStartFrame() const override { return m_events.getStartFrame(); } - sv_frame_t getEndFrame() const override { + sv_frame_t getTrueEndFrame() const override { if (m_events.isEmpty()) return 0; sv_frame_t e = m_events.getEndFrame(); if (e % m_resolution == 0) return e; diff -r a47a23c824ac -r 649ac57c5a2d data/model/PathModel.h --- a/data/model/PathModel.h Wed May 22 15:37:44 2019 +0100 +++ b/data/model/PathModel.h Thu Jun 20 14:58:20 2019 +0100 @@ -83,7 +83,7 @@ sv_frame_t getStartFrame() const override { return m_start; } - sv_frame_t getEndFrame() const override { + sv_frame_t getTrueEndFrame() const override { return m_end; } diff -r a47a23c824ac -r 649ac57c5a2d data/model/ReadOnlyWaveFileModel.h --- a/data/model/ReadOnlyWaveFileModel.h Wed May 22 15:37:44 2019 +0100 +++ b/data/model/ReadOnlyWaveFileModel.h Thu Jun 20 14:58:20 2019 +0100 @@ -77,7 +77,7 @@ float getValueMaximum() const override { return 1.0f; } sv_frame_t getStartFrame() const override { return m_startFrame; } - sv_frame_t getEndFrame() const override { return m_startFrame + getFrameCount(); } + sv_frame_t getTrueEndFrame() const override { return m_startFrame + getFrameCount(); } void setStartFrame(sv_frame_t startFrame) override { m_startFrame = startFrame; } diff -r a47a23c824ac -r 649ac57c5a2d data/model/RegionModel.h --- a/data/model/RegionModel.h Wed May 22 15:37:44 2019 +0100 +++ b/data/model/RegionModel.h Thu Jun 20 14:58:20 2019 +0100 @@ -83,7 +83,7 @@ sv_frame_t getStartFrame() const override { return m_events.getStartFrame(); } - sv_frame_t getEndFrame() const override { + sv_frame_t getTrueEndFrame() const override { if (m_events.isEmpty()) return 0; sv_frame_t e = m_events.getEndFrame(); if (e % m_resolution == 0) return e; diff -r a47a23c824ac -r 649ac57c5a2d data/model/SparseOneDimensionalModel.h --- a/data/model/SparseOneDimensionalModel.h Wed May 22 15:37:44 2019 +0100 +++ b/data/model/SparseOneDimensionalModel.h Thu Jun 20 14:58:20 2019 +0100 @@ -67,7 +67,7 @@ sv_frame_t getStartFrame() const override { return m_events.getStartFrame(); } - sv_frame_t getEndFrame() const override { + sv_frame_t getTrueEndFrame() const override { if (m_events.isEmpty()) return 0; sv_frame_t e = m_events.getEndFrame() + 1; if (e % m_resolution == 0) return e; diff -r a47a23c824ac -r 649ac57c5a2d data/model/SparseTimeValueModel.h --- a/data/model/SparseTimeValueModel.h Wed May 22 15:37:44 2019 +0100 +++ b/data/model/SparseTimeValueModel.h Thu Jun 20 14:58:20 2019 +0100 @@ -87,7 +87,7 @@ sv_frame_t getStartFrame() const override { return m_events.getStartFrame(); } - sv_frame_t getEndFrame() const override { + sv_frame_t getTrueEndFrame() const override { if (m_events.isEmpty()) return 0; sv_frame_t e = m_events.getEndFrame() + 1; if (e % m_resolution == 0) return e; diff -r a47a23c824ac -r 649ac57c5a2d data/model/TextModel.h --- a/data/model/TextModel.h Wed May 22 15:37:44 2019 +0100 +++ b/data/model/TextModel.h Thu Jun 20 14:58:20 2019 +0100 @@ -59,7 +59,7 @@ sv_frame_t getStartFrame() const override { return m_events.getStartFrame(); } - sv_frame_t getEndFrame() const override { + sv_frame_t getTrueEndFrame() const override { if (m_events.isEmpty()) return 0; sv_frame_t e = m_events.getEndFrame() + 1; if (e % m_resolution == 0) return e; diff -r a47a23c824ac -r 649ac57c5a2d data/model/WaveFileModel.h --- a/data/model/WaveFileModel.h Wed May 22 15:37:44 2019 +0100 +++ b/data/model/WaveFileModel.h Thu Jun 20 14:58:20 2019 +0100 @@ -37,7 +37,7 @@ QString getLocation() const override = 0; sv_frame_t getStartFrame() const override = 0; - sv_frame_t getEndFrame() const override = 0; + sv_frame_t getTrueEndFrame() const override = 0; virtual void setStartFrame(sv_frame_t startFrame) = 0; diff -r a47a23c824ac -r 649ac57c5a2d data/model/WritableWaveFileModel.cpp --- a/data/model/WritableWaveFileModel.cpp Wed May 22 15:37:44 2019 +0100 +++ b/data/model/WritableWaveFileModel.cpp Thu Jun 20 14:58:20 2019 +0100 @@ -248,6 +248,7 @@ m_reader->updateDone(); m_proportion = 100; emit modelChanged(); + emit writeCompleted(); } void diff -r a47a23c824ac -r 649ac57c5a2d data/model/WritableWaveFileModel.h --- a/data/model/WritableWaveFileModel.h Wed May 22 15:37:44 2019 +0100 +++ b/data/model/WritableWaveFileModel.h Thu Jun 20 14:58:20 2019 +0100 @@ -175,7 +175,7 @@ float getValueMaximum() const override { return 1.0f; } sv_frame_t getStartFrame() const override { return m_startFrame; } - sv_frame_t getEndFrame() const override { return m_startFrame + getFrameCount(); } + sv_frame_t getTrueEndFrame() const override { return m_startFrame + getFrameCount(); } void setStartFrame(sv_frame_t startFrame) override; @@ -196,6 +196,9 @@ QString indent = "", QString extraAttributes = "") const override; +signals: + void writeCompleted(); + protected: ReadOnlyWaveFileModel *m_model; diff -r a47a23c824ac -r 649ac57c5a2d data/model/test/MockWaveModel.h --- a/data/model/test/MockWaveModel.h Wed May 22 15:37:44 2019 +0100 +++ b/data/model/test/MockWaveModel.h Thu Jun 20 14:58:20 2019 +0100 @@ -48,7 +48,7 @@ QString getDefaultPlayClipId() const override { return ""; } sv_frame_t getStartFrame() const override { return 0; } - sv_frame_t getEndFrame() const override { return m_data[0].size(); } + sv_frame_t getTrueEndFrame() const override { return m_data[0].size(); } sv_samplerate_t getSampleRate() const override { return 44100; } bool isOK() const override { return true; } int getCompletion() const override { return 100; } diff -r a47a23c824ac -r 649ac57c5a2d transform/ModelTransformerFactory.cpp --- a/transform/ModelTransformerFactory.cpp Wed May 22 15:37:44 2019 +0100 +++ b/transform/ModelTransformerFactory.cpp Thu Jun 20 14:58:20 2019 +0100 @@ -32,11 +32,14 @@ #include #include +#include #include #include using std::vector; +using std::set; +using std::map; ModelTransformerFactory * ModelTransformerFactory::m_instance = new ModelTransformerFactory; @@ -53,7 +56,7 @@ ModelTransformer::Input ModelTransformerFactory::getConfigurationForTransform(Transform &transform, - const std::vector &candidateInputModels, + const vector &candidateInputModels, Model *defaultInputModel, AudioPlaySource *source, sv_frame_t startFrame, @@ -260,8 +263,6 @@ void ModelTransformerFactory::transformerFinished() { - QMutexLocker locker(&m_mutex); - QObject *s = sender(); ModelTransformer *transformer = dynamic_cast(s); @@ -272,6 +273,8 @@ return; } + m_mutex.lock(); + if (m_runningTransformers.find(transformer) == m_runningTransformers.end()) { cerr << "WARNING: ModelTransformerFactory::transformerFinished(" << transformer @@ -281,16 +284,34 @@ m_runningTransformers.erase(transformer); + map> toNotifyOfMore; + vector toNotifyOfNoMore; + if (m_handlers.find(transformer) != m_handlers.end()) { if (transformer->willHaveAdditionalOutputModels()) { vector mm = transformer->detachAdditionalOutputModels(); - m_handlers[transformer]->moreModelsAvailable(mm); + toNotifyOfMore[m_handlers[transformer]] = mm; } else { - m_handlers[transformer]->noMoreModelsAvailable(); + toNotifyOfNoMore.push_back(m_handlers[transformer]); } m_handlers.erase(transformer); } + m_mutex.unlock(); + + // These calls have to be made without the mutex held, as they may + // ultimately call back on us (e.g. we have one baroque situation + // where this could trigger a command to create a layer, which + // triggers the command history to clip the stack, which deletes a + // spare old model, which calls back on our modelAboutToBeDeleted) + + for (const auto &i: toNotifyOfMore) { + i.first->moreModelsAvailable(i.second); + } + for (AdditionalModelHandler *handler: toNotifyOfNoMore) { + handler->noMoreModelsAvailable(); + } + if (transformer->isAbandoned()) { if (transformer->getMessage() != "") { emit transformFailed("", transformer->getMessage());