# HG changeset patch # User Chris Cannam # Date 1553098933 0 # Node ID 5b7b01da430ab523e33595233ef2437876db582b # Parent 31b46a5647db249a8df7ea346bca375cee998ec1 Start updating SparseOneDimensionalModel diff -r 31b46a5647db -r 5b7b01da430a data/fileio/CSVFileReader.cpp --- a/data/fileio/CSVFileReader.cpp Wed Mar 20 15:45:52 2019 +0000 +++ b/data/fileio/CSVFileReader.cpp Wed Mar 20 16:22:13 2019 +0000 @@ -437,8 +437,8 @@ if (modelType == CSVFormat::OneDimensionalModel) { - SparseOneDimensionalModel::Point point(frameNo, label); - model1->addPoint(point); + Event point(frameNo, label); + model1->add(point); } else if (modelType == CSVFormat::TwoDimensionalModel) { diff -r 31b46a5647db -r 5b7b01da430a data/model/SparseOneDimensionalModel.h --- a/data/model/SparseOneDimensionalModel.h Wed Mar 20 15:45:52 2019 +0000 +++ b/data/model/SparseOneDimensionalModel.h Wed Mar 20 16:22:13 2019 +0000 @@ -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. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -16,115 +15,173 @@ #ifndef SV_SPARSE_ONE_DIMENSIONAL_MODEL_H #define SV_SPARSE_ONE_DIMENSIONAL_MODEL_H -#include "SparseModel.h" +#include "EventCommands.h" +#include "TabularModel.h" +#include "Model.h" +#include "DeferredNotifier.h" + #include "base/NoteData.h" +#include "base/EventSeries.h" #include "base/NoteExportable.h" #include "base/PlayParameterRepository.h" #include "base/RealTime.h" +#include "system/System.h" + #include -struct OneDimensionalPoint -{ -public: - OneDimensionalPoint(sv_frame_t _frame) : frame(_frame) { } - OneDimensionalPoint(sv_frame_t _frame, QString _label) : frame(_frame), label(_label) { } - - int getDimensions() const { return 1; } - - sv_frame_t frame; - QString label; - - QString getLabel() const { return label; } - - void toXml(QTextStream &stream, - QString indent = "", - QString extraAttributes = "") const - { - stream << QString("%1\n") - .arg(indent).arg(frame).arg(XmlExportable::encodeEntities(label)) - .arg(extraAttributes); - } - - QString toDelimitedDataString(QString delimiter, DataExportOptions, sv_samplerate_t sampleRate) const - { - QStringList list; - list << RealTime::frame2RealTime(frame, sampleRate).toString().c_str(); - if (label != "") list << label; - return list.join(delimiter); - } - - struct Comparator { - bool operator()(const OneDimensionalPoint &p1, - const OneDimensionalPoint &p2) const { - if (p1.frame != p2.frame) return p1.frame < p2.frame; - return p1.label < p2.label; - } - }; - - struct OrderComparator { - bool operator()(const OneDimensionalPoint &p1, - const OneDimensionalPoint &p2) const { - return p1.frame < p2.frame; - } - }; - - bool operator==(const OneDimensionalPoint &p) const { - return (frame == p.frame && label == p.label); - } -}; - - -class SparseOneDimensionalModel : public SparseModel, +/** + * A model representing a series of time instants with optional labels + * but without values. + */ +class SparseOneDimensionalModel : public Model, + public TabularModel, + public EventEditable, public NoteExportable { Q_OBJECT public: - SparseOneDimensionalModel(sv_samplerate_t sampleRate, int resolution, + SparseOneDimensionalModel(sv_samplerate_t sampleRate, + int resolution, bool notifyOnAdd = true) : - SparseModel(sampleRate, resolution, notifyOnAdd) - { + m_sampleRate(sampleRate), + m_resolution(resolution), + m_notifier(this, + notifyOnAdd ? + DeferredNotifier::NOTIFY_ALWAYS : + DeferredNotifier::NOTIFY_DEFERRED), + m_completion(100) { PlayParameterRepository::getInstance()->addPlayable(this); } - virtual ~SparseOneDimensionalModel() - { + virtual ~SparseOneDimensionalModel() { PlayParameterRepository::getInstance()->removePlayable(this); } - bool canPlay() const override { return true; } - - QString getDefaultPlayClipId() const override - { - return "tap"; - } - - int getIndexOf(const Point &point) - { - // slow - int i = 0; - Point::Comparator comparator; - for (PointList::const_iterator j = m_points.begin(); - j != m_points.end(); ++j, ++i) { - if (!comparator(*j, point) && !comparator(point, *j)) return i; - } - return -1; - } - QString getTypeName() const override { return tr("Sparse 1-D"); } + bool isOK() const override { return true; } + sv_frame_t getStartFrame() const override { return m_events.getStartFrame(); } + sv_frame_t getEndFrame() const override { return m_events.getEndFrame(); } + sv_samplerate_t getSampleRate() const override { return m_sampleRate; } + int getResolution() const { return m_resolution; } + + bool canPlay() const override { return true; } + QString getDefaultPlayClipId() const override { return "tap"; } + + int getCompletion() const { return m_completion; } + + void setCompletion(int completion, bool update = true) { + + { QMutexLocker locker(&m_mutex); + if (m_completion == completion) return; + m_completion = completion; + } + + if (update) { + m_notifier.makeDeferredNotifications(); + } + + emit completionChanged(); + + if (completion == 100) { + // henceforth: + m_notifier.switchMode(DeferredNotifier::NOTIFY_ALWAYS); + emit modelChanged(); + } + } + + /** + * Query methods. + */ + + int getEventCount() const { + return m_events.count(); + } + bool isEmpty() const { + return m_events.isEmpty(); + } + bool containsEvent(const Event &e) const { + return m_events.contains(e); + } + EventVector getAllEvents() const { + return m_events.getAllEvents(); + } + EventVector getEventsSpanning(sv_frame_t f, sv_frame_t duration) const { + return m_events.getEventsSpanning(f, duration); + } + EventVector getEventsCovering(sv_frame_t f) const { + return m_events.getEventsCovering(f); + } + EventVector getEventsWithin(sv_frame_t f, sv_frame_t duration, + int overspill = 0) const { + return m_events.getEventsWithin(f, duration, overspill); + } + EventVector getEventsStartingWithin(sv_frame_t f, sv_frame_t duration) const { + return m_events.getEventsStartingWithin(f, duration); + } + EventVector getEventsStartingAt(sv_frame_t f) const { + return m_events.getEventsStartingAt(f); + } + bool getNearestEventMatching(sv_frame_t startSearchAt, + std::function predicate, + EventSeries::Direction direction, + Event &found) const { + return m_events.getNearestEventMatching + (startSearchAt, predicate, direction, found); + } + + /** + * Editing methods. + */ + void add(Event e) override { + + { QMutexLocker locker(&m_mutex); + m_events.add(e.withoutValue().withoutDuration()); + } + + m_notifier.update(e.getFrame(), m_resolution); + } + + void remove(Event e) override { + { QMutexLocker locker(&m_mutex); + m_events.remove(e); + } + emit modelChangedWithin(e.getFrame(), e.getFrame() + m_resolution); + } + /** * TabularModel methods. */ - int getColumnCount() const override - { + int getRowCount() const override { + return m_events.count(); + } + + int getColumnCount() const override { return 3; } - QString getHeading(int column) const override - { + bool isColumnTimeValue(int column) const override { + // NB duration is not a "time value" -- that's for columns + // whose sort ordering is exactly that of the frame time + return (column < 2); + } + + sv_frame_t getFrameForRow(int row) const override { + if (row < 0 || row >= m_events.count()) { + return 0; + } + Event e = m_events.getEventByIndex(row); + return e.getFrame(); + } + + int getRowForFrame(sv_frame_t frame) const override { + return m_events.getIndexForEvent(Event(frame)); + } + + QString getHeading(int column) const override { switch (column) { case 0: return tr("Time"); case 1: return tr("Frame"); @@ -133,57 +190,48 @@ } } - QVariant getData(int row, int column, int role) const override - { - if (column < 2) { - return SparseModel::getData - (row, column, role); + SortType getSortType(int column) const override { + if (column == 2) return SortAlphabetical; + return SortNumeric; + } + + QVariant getData(int row, int column, int role) const override { + + if (row < 0 || row >= m_events.count()) { + return QVariant(); } - PointListConstIterator i = getPointListIteratorForRow(row); - if (i == m_points.end()) return QVariant(); + Event e = m_events.getEventByIndex(row); switch (column) { - case 2: return i->label; + case 0: return adaptFrameForRole(e.getFrame(), getSampleRate(), role); + case 1: return int(e.getFrame()); + case 2: return e.getLabel(); default: return QVariant(); } } - Command *getSetDataCommand(int row, int column, const QVariant &value, int role) override - { - if (column < 2) { - return SparseModel::getSetDataCommand - (row, column, value, role); + Command *getSetDataCommand(int row, int column, const QVariant &value, int role) override { + if (row < 0 || row >= m_events.count()) return nullptr; + if (role != Qt::EditRole) return nullptr; + + Event e0 = m_events.getEventByIndex(row); + Event e1; + + switch (column) { + case 0: e1 = e0.withFrame(sv_frame_t(round(value.toDouble() * + getSampleRate()))); break; + case 1: e1 = e0.withFrame(value.toInt()); break; + case 2: e1 = e0.withLabel(value.toString()); break; } - if (role != Qt::EditRole) return 0; - PointListConstIterator i = getPointListIteratorForRow(row); - if (i == m_points.end()) return 0; - EditCommand *command = new EditCommand(this, tr("Edit Data")); - - Point point(*i); - command->deletePoint(point); - - switch (column) { - case 2: point.label = value.toString(); break; - } - - command->addPoint(point); + ChangeEventsCommand *command = + new ChangeEventsCommand(this, tr("Edit Data")); + command->remove(e0); + command->add(e1); return command->finish(); } - - bool isColumnTimeValue(int column) const override - { - return (column < 2); - } - - SortType getSortType(int column) const override - { - if (column == 2) return SortAlphabetical; - return SortNumeric; - } - /** * NoteExportable methods. */ @@ -200,21 +248,45 @@ NoteList getNotesStartingWithin(sv_frame_t startFrame, sv_frame_t duration) const override { - PointList points = getPoints(startFrame, startFrame + duration); NoteList notes; - - for (PointList::iterator pli = - points.begin(); pli != points.end(); ++pli) { - - notes.push_back - (NoteData(pli->frame, - sv_frame_t(getSampleRate() / 6), // arbitrary short duration - 64, // default pitch - 100)); // default velocity + EventVector ee = m_events.getEventsStartingWithin(startFrame, duration); + for (const auto &e: ee) { + notes.push_back(e.toNoteData(getSampleRate(), true)); } - return notes; } + + /** + * XmlExportable methods. + */ + void toXml(QTextStream &out, + QString indent = "", + QString extraAttributes = "") const override { + + Model::toXml + (out, + indent, + QString("type=\"sparse\" dimensions=\"1\" resolution=\"%1\" " + "notifyOnAdd=\"%2\" dataset=\"%3\" %4") + .arg(m_resolution) + .arg("true") // always true after model reaches 100% - + // subsequent events are always notified + .arg(getObjectExportId(&m_events)) + .arg(extraAttributes)); + + m_events.toXml(out, indent, QString("dimensions=\"1\"")); + } + +protected: + sv_samplerate_t m_sampleRate; + int m_resolution; + + DeferredNotifier m_notifier; + int m_completion; + + EventSeries m_events; + + mutable QMutex m_mutex; }; #endif diff -r 31b46a5647db -r 5b7b01da430a data/model/SparseTimeValueModel.h --- a/data/model/SparseTimeValueModel.h Wed Mar 20 15:45:52 2019 +0000 +++ b/data/model/SparseTimeValueModel.h Wed Mar 20 16:22:13 2019 +0000 @@ -171,8 +171,7 @@ bool allChange = false; - { - QMutexLocker locker(&m_mutex); + { QMutexLocker locker(&m_mutex); m_events.add(e.withoutDuration()); // can't have duration here if (e.getLabel() != "") { diff -r 31b46a5647db -r 5b7b01da430a data/model/test/TestSparseModels.h --- a/data/model/test/TestSparseModels.h Wed Mar 20 15:45:52 2019 +0000 +++ b/data/model/test/TestSparseModels.h Wed Mar 20 16:22:13 2019 +0000 @@ -33,76 +33,78 @@ void s1d_empty() { SparseOneDimensionalModel m(100, 10, false); QCOMPARE(m.isEmpty(), true); - QCOMPARE(m.getPointCount(), 0); - QCOMPARE(m.getPoints().begin(), m.getPoints().end()); + QCOMPARE(m.getEventCount(), 0); + QCOMPARE(m.getAllEvents().size(), 0); QCOMPARE(m.getStartFrame(), 0); QCOMPARE(m.getEndFrame(), 0); QCOMPARE(m.getSampleRate(), 100); QCOMPARE(m.getResolution(), 10); QCOMPARE(m.isSparse(), true); - SparseOneDimensionalModel::Point p(10); - m.addPoint(p); + Event p(10); + m.add(p); +/*!!! m.clear(); QCOMPARE(m.isEmpty(), true); - QCOMPARE(m.getPointCount(), 0); - QCOMPARE(m.getPoints().begin(), m.getPoints().end()); + QCOMPARE(m.getEventCount(), 0); + QCOMPARE(m.getAllEvents().size(), 0); QCOMPARE(m.getStartFrame(), 0); QCOMPARE(m.getEndFrame(), 0); - m.addPoint(p); - m.deletePoint(p); + m.add(p); +*/ + m.remove(p); QCOMPARE(m.isEmpty(), true); - QCOMPARE(m.getPointCount(), 0); - QCOMPARE(m.getPoints().begin(), m.getPoints().end()); + QCOMPARE(m.getEventCount(), 0); + QCOMPARE(m.getAllEvents().size(), 0); QCOMPARE(m.getStartFrame(), 0); QCOMPARE(m.getEndFrame(), 0); } void s1d_extents() { SparseOneDimensionalModel m(100, 10, false); - SparseOneDimensionalModel::Point p1(20); - m.addPoint(p1); + Event p1(20); + m.add(p1); QCOMPARE(m.isEmpty(), false); - QCOMPARE(m.getPointCount(), 1); - SparseOneDimensionalModel::Point p2(50); - m.addPoint(p2); + QCOMPARE(m.getEventCount(), 1); + Event p2(50); + m.add(p2); QCOMPARE(m.isEmpty(), false); - QCOMPARE(m.getPointCount(), 2); - QCOMPARE(m.getPoints().size(), 2); - QCOMPARE(*m.getPoints().begin(), p1); - QCOMPARE(*m.getPoints().rbegin(), p2); + QCOMPARE(m.getEventCount(), 2); + QCOMPARE(m.getAllEvents().size(), 2); + QCOMPARE(*m.getAllEvents().begin(), p1); + QCOMPARE(*m.getAllEvents().rbegin(), p2); QCOMPARE(m.getStartFrame(), 20); QCOMPARE(m.getEndFrame(), 60); - QCOMPARE(m.containsPoint(p1), true); - m.deletePoint(p1); - QCOMPARE(m.getPointCount(), 1); - QCOMPARE(m.getPoints().size(), 1); - QCOMPARE(*m.getPoints().begin(), p2); + QCOMPARE(m.containsEvent(p1), true); + m.remove(p1); + QCOMPARE(m.getEventCount(), 1); + QCOMPARE(m.getAllEvents().size(), 1); + QCOMPARE(*m.getAllEvents().begin(), p2); QCOMPARE(m.getStartFrame(), 50); QCOMPARE(m.getEndFrame(), 60); - QCOMPARE(m.containsPoint(p1), false); + QCOMPARE(m.containsEvent(p1), false); } void s1d_sample() { SparseOneDimensionalModel m(100, 10, false); - SparseOneDimensionalModel::Point p1(20), p2(20), p3(50); - m.addPoint(p1); - m.addPoint(p2); - m.addPoint(p3); - QCOMPARE(m.getPoints().size(), 3); - QCOMPARE(*m.getPoints().begin(), p1); - QCOMPARE(*m.getPoints().rbegin(), p3); + Event p1(20), p2(20), p3(50); + m.add(p1); + m.add(p2); + m.add(p3); + QCOMPARE(m.getAllEvents().size(), 3); + QCOMPARE(*m.getAllEvents().begin(), p1); + QCOMPARE(*m.getAllEvents().rbegin(), p3); /*!!! - auto pp = m.getPoints(20, 30); + auto pp = m.getAllEvents(20, 30); QCOMPARE(pp.size(), 2); QCOMPARE(*pp.begin(), p1); QCOMPARE(*pp.rbegin(), p2); - pp = m.getPoints(40, 50); + pp = m.getAllEvents(40, 50); QCOMPARE(pp.size(), 0); - pp = m.getPoints(50, 50); + pp = m.getAllEvents(50, 50); QCOMPARE(pp.size(), 1); QCOMPARE(*pp.begin(), p3); */ @@ -111,17 +113,17 @@ void s1d_xml() { SparseOneDimensionalModel m(100, 10, false); m.setObjectName("This \"&\" that"); - SparseOneDimensionalModel::Point p1(20), p2(20), p3(50); - p2.label = "Label &'\">"; - m.addPoint(p1); - m.addPoint(p2); - m.addPoint(p3); + Event p1(20), p2(20), p3(50); + p2 = p2.withLabel("Label &'\">"); + m.add(p1); + m.add(p2); + m.add(p3); QString xml; QTextStream str(&xml, QIODevice::WriteOnly); m.toXml(str); str.flush(); QString expected = - "\n" + "\n" "\n" " \n" " \n" diff -r 31b46a5647db -r 5b7b01da430a rdf/RDFExporter.cpp --- a/rdf/RDFExporter.cpp Wed Mar 20 15:45:52 2019 +0000 +++ b/rdf/RDFExporter.cpp Wed Mar 20 16:22:13 2019 +0000 @@ -120,12 +120,11 @@ if (m) { f.hasTimestamp = true; f.hasDuration = false; - const SparseOneDimensionalModel::PointList &pl(m->getPoints()); - for (SparseOneDimensionalModel::PointList::const_iterator i = pl.begin(); - i != pl.end(); ++i) { - f.timestamp = RealTime::frame2RealTime(i->frame, sr).toVampRealTime(); + EventVector ee(m->getAllEvents()); + for (auto e: ee) { + f.timestamp = RealTime::frame2RealTime(e.getFrame(), sr).toVampRealTime(); f.values.clear(); - f.label = i->label.toStdString(); + f.label = e.getLabel().toStdString(); m_fw->write(trackId, transform, output, features, summaryType); } return; diff -r 31b46a5647db -r 5b7b01da430a rdf/RDFImporter.cpp --- a/rdf/RDFImporter.cpp Wed Mar 20 15:45:52 2019 +0000 +++ b/rdf/RDFImporter.cpp Wed Mar 20 16:22:13 2019 +0000 @@ -709,8 +709,8 @@ SparseOneDimensionalModel *sodm = dynamic_cast(model); if (sodm) { - SparseOneDimensionalModel::Point point(ftime, label); - sodm->addPoint(point); + Event point(ftime, label); + sodm->add(point); return; } diff -r 31b46a5647db -r 5b7b01da430a transform/FeatureExtractionModelTransformer.cpp --- a/transform/FeatureExtractionModelTransformer.cpp Wed Mar 20 15:45:52 2019 +0000 +++ b/transform/FeatureExtractionModelTransformer.cpp Wed Mar 20 16:22:13 2019 +0000 @@ -977,8 +977,7 @@ getConformingOutput(n); if (!model) return; - model->addPoint(SparseOneDimensionalModel::Point - (frame, feature.label.c_str())); + model->add(Event(frame, feature.label.c_str())); } else if (isOutput(n)) {