# HG changeset patch # User Chris Cannam # Date 1552659830 0 # Node ID 1cc9a0d4b1b693e8b22b168885bfbf3d65bfcf05 # Parent 86bbccb79c9bcbb59bfdaf557053560322c7d514 Update RegionModel following NoteModel, er, model. They have quite a bit in common that we should now pull out some of diff -r 86bbccb79c9b -r 1cc9a0d4b1b6 data/fileio/CSVFileReader.cpp --- a/data/fileio/CSVFileReader.cpp Fri Mar 15 10:57:35 2019 +0000 +++ b/data/fileio/CSVFileReader.cpp Fri Mar 15 14:23:50 2019 +0000 @@ -447,8 +447,8 @@ } else if (modelType == CSVFormat::TwoDimensionalModelWithDuration) { - RegionModel::Point point(frameNo, value, duration, label); - model2a->addPoint(point); + Event region(frameNo, value, duration, label); + model2a->add(region); } else if (modelType == CSVFormat::TwoDimensionalModelWithDurationAndPitch) { @@ -581,31 +581,30 @@ } } - map pointMap; - for (RegionModel::PointList::const_iterator i = - model2a->getPoints().begin(); - i != model2a->getPoints().end(); ++i) { - RegionModel::Point p(*i); - int count = labelCountMap[p.label]; - v = countLabelValueMap[count][p.label]; - // SVCERR << "mapping from label \"" << p.label << "\" (count " << count << ") to value " << v << endl; - RegionModel::Point pp(p.frame, v, p.duration, p.label); - pointMap[p] = pp; + map eventMap; + + EventVector allEvents = model2a->getAllEvents(); + for (const Event &e: allEvents) { + int count = labelCountMap[e.getLabel()]; + v = countLabelValueMap[count][e.getLabel()]; + // SVCERR << "mapping from label \"" << p.label + // << "\" (count " << count + // << ") to value " << v << endl; + eventMap[e] = Event(e.getFrame(), v, + e.getDuration(), e.getLabel()); } - for (map::iterator i = - pointMap.begin(); i != pointMap.end(); ++i) { + for (const auto &i: eventMap) { // There could be duplicate regions; if so replace // them all -- but we need to check we're not // replacing a region by itself (or else this will // never terminate) - if (i->first.value == i->second.value) { + if (i.first.getValue() == i.second.getValue()) { continue; } - while (model2a->containsPoint(i->first)) { - model2a->deletePoint(i->first); - model2a->addPoint(i->second); + while (model2a->containsEvent(i.first)) { + model2a->remove(i.first); + model2a->add(i.second); } } } diff -r 86bbccb79c9b -r 1cc9a0d4b1b6 data/model/NoteModel.h --- a/data/model/NoteModel.h Fri Mar 15 10:57:35 2019 +0000 +++ b/data/model/NoteModel.h Fri Mar 15 14:23:50 2019 +0000 @@ -307,8 +307,8 @@ } } - Command *getSetDataCommand(int row, int column, const QVariant &value, int role) override - { + 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; diff -r 86bbccb79c9b -r 1cc9a0d4b1b6 data/model/RegionModel.h --- a/data/model/RegionModel.h Fri Mar 15 10:57:35 2019 +0000 +++ b/data/model/RegionModel.h Fri Mar 15 14:23:50 2019 +0000 @@ -16,105 +16,76 @@ #ifndef SV_REGION_MODEL_H #define SV_REGION_MODEL_H -#include "IntervalModel.h" +#include "EventCommands.h" +#include "TabularModel.h" +#include "Model.h" + #include "base/RealTime.h" +#include "base/EventSeries.h" +#include "base/UnitDatabase.h" + +#include "system/System.h" + +#include /** - * RegionModel -- a concrete IntervalModel for intervals associated - * with a value, which we call regions for no very compelling reason. + * RegionModel -- a model for intervals associated with a value, which + * we call regions for no very compelling reason. */ - -/** - * Region "point" type. A region is something that has an onset time, - * a single value, and a duration. Like other points, it can also - * have a label. - * - * This is called RegionRec instead of Region to avoid name collisions - * with the X11 Region struct. Bah. - */ - -struct RegionRec -{ -public: - RegionRec() : frame(0), value(0.f), duration(0) { } - RegionRec(sv_frame_t _frame) : frame(_frame), value(0.0f), duration(0) { } - RegionRec(sv_frame_t _frame, float _value, sv_frame_t _duration, QString _label) : - frame(_frame), value(_value), duration(_duration), label(_label) { } - - int getDimensions() const { return 3; } - - sv_frame_t frame; - float value; - sv_frame_t duration; - 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(value).arg(duration) - .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(); - list << QString("%1").arg(value); - list << RealTime::frame2RealTime(duration, sampleRate).toString().c_str(); - if (label != "") list << label; - return list.join(delimiter); - } - - struct Comparator { - bool operator()(const RegionRec &p1, - const RegionRec &p2) const { - if (p1.frame != p2.frame) return p1.frame < p2.frame; - if (p1.value != p2.value) return p1.value < p2.value; - if (p1.duration != p2.duration) return p1.duration < p2.duration; - return p1.label < p2.label; - } - }; - - struct OrderComparator { - bool operator()(const RegionRec &p1, - const RegionRec &p2) const { - return p1.frame < p2.frame; - } - }; -}; - - -class RegionModel : public IntervalModel +class RegionModel : public Model, + public TabularModel, + public EventEditable { Q_OBJECT public: - RegionModel(sv_samplerate_t sampleRate, int resolution, + RegionModel(sv_samplerate_t sampleRate, + int resolution, bool notifyOnAdd = true) : - IntervalModel(sampleRate, resolution, notifyOnAdd), + m_sampleRate(sampleRate), + m_resolution(resolution), + m_valueMinimum(0.f), + m_valueMaximum(0.f), + m_haveExtents(false), m_valueQuantization(0), - m_haveDistinctValues(false) - { + m_haveDistinctValues(false), + m_notifyOnAdd(notifyOnAdd), + m_sinceLastNotifyMin(-1), + m_sinceLastNotifyMax(-1), + m_completion(0) { } RegionModel(sv_samplerate_t sampleRate, int resolution, float valueMinimum, float valueMaximum, bool notifyOnAdd = true) : - IntervalModel(sampleRate, resolution, - valueMinimum, valueMaximum, - notifyOnAdd), + m_sampleRate(sampleRate), + m_resolution(resolution), + m_valueMinimum(valueMinimum), + m_valueMaximum(valueMaximum), + m_haveExtents(false), m_valueQuantization(0), - m_haveDistinctValues(false) - { + m_haveDistinctValues(false), + m_notifyOnAdd(notifyOnAdd), + m_sinceLastNotifyMin(-1), + m_sinceLastNotifyMax(-1), + m_completion(0) { } - virtual ~RegionModel() - { + virtual ~RegionModel() { + } + + QString getTypeName() const override { return tr("Region"); } + + 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; } + + QString getScaleUnits() const { return m_units; } + void setScaleUnits(QString units) { + m_units = units; + UnitDatabase::getInstance()->registerUnit(units); } float getValueQuantization() const { return m_valueQuantization; } @@ -122,33 +93,172 @@ bool haveDistinctValues() const { return m_haveDistinctValues; } - QString getTypeName() const override { return tr("Region"); } + float getValueMinimum() const { return m_valueMinimum; } + float getValueMaximum() const { return m_valueMaximum; } + + int getCompletion() const { return m_completion; } - void toXml(QTextStream &out, - QString indent = "", - QString extraAttributes = "") const override - { - std::cerr << "RegionModel::toXml: extraAttributes = \"" - << extraAttributes.toStdString() << std::endl; + void setCompletion(int completion, bool update = true) { - IntervalModel::toXml - (out, - indent, - QString("%1 subtype=\"region\" valueQuantization=\"%2\"") - .arg(extraAttributes).arg(m_valueQuantization)); + bool emitCompletionChanged = true; + bool emitGeneralModelChanged = false; + bool emitRegionChanged = false; + + { + QMutexLocker locker(&m_mutex); + + if (m_completion != completion) { + m_completion = completion; + + if (completion == 100) { + + if (m_notifyOnAdd) { + emitCompletionChanged = false; + } + + m_notifyOnAdd = true; // henceforth + emitGeneralModelChanged = true; + + } else if (!m_notifyOnAdd) { + + if (update && + m_sinceLastNotifyMin >= 0 && + m_sinceLastNotifyMax >= 0) { + emitRegionChanged = true; + } + } + } + } + + if (emitCompletionChanged) { + emit completionChanged(); + } + if (emitGeneralModelChanged) { + emit modelChanged(); + } + if (emitRegionChanged) { + emit modelChangedWithin(m_sinceLastNotifyMin, m_sinceLastNotifyMax); + m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1; + } } /** + * 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 getEventsWithin(sv_frame_t f, sv_frame_t duration) const { + return m_events.getEventsWithin(f, duration); + } + EventVector getEventsStartingWithin(sv_frame_t f, sv_frame_t duration) const { + return m_events.getEventsStartingWithin(f, duration); + } + EventVector getEventsCovering(sv_frame_t f) const { + return m_events.getEventsCovering(f); + } + + /** + * Editing methods. + */ + void add(Event e) override { + + bool allChange = false; + + { + QMutexLocker locker(&m_mutex); + m_events.add(e); +//!!!??? if (point.getLabel() != "") m_hasTextLabels = true; + + float v = e.getValue(); + if (!ISNAN(v) && !ISINF(v)) { + if (!m_haveExtents || v < m_valueMinimum) { + m_valueMinimum = v; allChange = true; + } + if (!m_haveExtents || v > m_valueMaximum) { + m_valueMaximum = v; allChange = true; + } + m_haveExtents = true; + } + + if (e.hasValue() && e.getValue() != 0.f) { + m_haveDistinctValues = true; + } + + sv_frame_t f = e.getFrame(); + + if (!m_notifyOnAdd) { + if (m_sinceLastNotifyMin == -1 || f < m_sinceLastNotifyMin) { + m_sinceLastNotifyMin = f; + } + if (m_sinceLastNotifyMax == -1 || f > m_sinceLastNotifyMax) { + m_sinceLastNotifyMax = f; + } + } + } + + if (m_notifyOnAdd) { + emit modelChangedWithin(e.getFrame(), + e.getFrame() + e.getDuration() + m_resolution); + } + if (allChange) { + emit modelChanged(); + } + } + + void remove(Event e) override { + { + QMutexLocker locker(&m_mutex); + m_events.remove(e); + } + emit modelChangedWithin(e.getFrame(), + e.getFrame() + e.getDuration() + m_resolution); + } + + /** * TabularModel methods. */ - - int getColumnCount() const override - { + + int getRowCount() const override { + return m_events.count(); + } + + int getColumnCount() const override { return 5; } - 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"); @@ -159,41 +269,45 @@ } } - QVariant getData(int row, int column, int role) const override - { - if (column < 4) { - return IntervalModel::getData(row, column, role); + 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 4: return i->label; + case 0: return adaptFrameForRole(e.getFrame(), getSampleRate(), role); + case 1: return int(e.getFrame()); + case 2: return adaptValueForRole(e.getValue(), getScaleUnits(), role); + case 3: return int(e.getDuration()); + case 4: return e.getLabel(); default: return QVariant(); } } - Command *getSetDataCommand(int row, int column, const QVariant &value, int role) override - { - if (column < 4) { - return IntervalModel::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.withValue(float(value.toDouble())); break; + case 3: e1 = e0.withDuration(value.toInt()); break; + case 4: e1 = e0.withLabel(value.toString()); break; } - if (role != Qt::EditRole) return 0; - PointListIterator 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 4: 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(); } @@ -203,15 +317,53 @@ return SortNumeric; } - void addPoint(const Point &point) override - { - if (point.value != 0.f) m_haveDistinctValues = true; - IntervalModel::addPoint(point); + + /** + * XmlExportable methods. + */ + void toXml(QTextStream &out, + QString indent = "", + QString extraAttributes = "") const override { + + Model::toXml + (out, + indent, + QString("type=\"sparse\" dimensions=\"3\" resolution=\"%1\" " + "notifyOnAdd=\"%2\" dataset=\"%3\" subtype=\"%4\" " + "valueQuantization=\"%5\" minimum=\"%6\" maximum=\"%7\" " + "%8") + .arg(m_resolution) + .arg(m_notifyOnAdd ? "true" : "false") + .arg(getObjectExportId(&m_events)) + .arg("region") + .arg(m_valueQuantization) + .arg(m_valueMinimum) + .arg(m_valueMaximum) + .arg(extraAttributes)); + + m_events.toXml(out, indent, QString("dimensions=\"3\"")); } protected: + sv_samplerate_t m_sampleRate; + int m_resolution; + + float m_valueMinimum; + float m_valueMaximum; + bool m_haveExtents; float m_valueQuantization; bool m_haveDistinctValues; + QString m_units; + + bool m_notifyOnAdd; + sv_frame_t m_sinceLastNotifyMin; + sv_frame_t m_sinceLastNotifyMax; + + EventSeries m_events; + + int m_completion; + + mutable QMutex m_mutex; }; #endif diff -r 86bbccb79c9b -r 1cc9a0d4b1b6 rdf/RDFExporter.cpp --- a/rdf/RDFExporter.cpp Fri Mar 15 10:57:35 2019 +0000 +++ b/rdf/RDFExporter.cpp Fri Mar 15 14:23:50 2019 +0000 @@ -85,14 +85,13 @@ if (m) { f.hasTimestamp = true; f.hasDuration = true; - const RegionModel::PointList &pl(m->getPoints()); - for (RegionModel::PointList::const_iterator i = pl.begin(); - i != pl.end(); ++i) { - f.timestamp = RealTime::frame2RealTime(i->frame, sr).toVampRealTime(); - f.duration = RealTime::frame2RealTime(i->duration, sr).toVampRealTime(); + EventVector ee(m->getAllEvents()); + for (auto e: ee) { + f.timestamp = RealTime::frame2RealTime(e.getFrame(), sr).toVampRealTime(); + f.duration = RealTime::frame2RealTime(e.getDuration(), sr).toVampRealTime(); f.values.clear(); - f.values.push_back(i->value); - f.label = i->label.toStdString(); + f.values.push_back(e.getValue()); + f.label = e.getLabel().toStdString(); m_fw->write(trackId, transform, output, features, summaryType); } return; diff -r 86bbccb79c9b -r 1cc9a0d4b1b6 rdf/RDFImporter.cpp --- a/rdf/RDFImporter.cpp Fri Mar 15 10:57:35 2019 +0000 +++ b/rdf/RDFImporter.cpp Fri Mar 15 14:23:50 2019 +0000 @@ -779,8 +779,8 @@ value = values[0]; } if (haveDuration) { - RegionModel::Point point(ftime, value, fduration, label); - rm->addPoint(point); + Event e(ftime, value, fduration, label); + rm->add(e); } else { // This won't actually happen -- we only create region models // if we do have duration -- but just for completeness @@ -791,9 +791,8 @@ duration = values[1]; } } - RegionModel::Point point(ftime, value, - sv_frame_t(lrintf(duration)), label); - rm->addPoint(point); + Event e(ftime, value, sv_frame_t(lrintf(duration)), label); + rm->add(e); } return; } diff -r 86bbccb79c9b -r 1cc9a0d4b1b6 transform/FeatureExtractionModelTransformer.cpp --- a/transform/FeatureExtractionModelTransformer.cpp Fri Mar 15 10:57:35 2019 +0000 +++ b/transform/FeatureExtractionModelTransformer.cpp Fri Mar 15 14:23:50 2019 +0000 @@ -1057,17 +1057,17 @@ label = QString("[%1] %2").arg(i+1).arg(label); } - model->addPoint(RegionModel::Point(frame, - value, - duration, - label)); + model->add(Event(frame, + value, + duration, + label)); } } else { - model->addPoint(RegionModel::Point(frame, - value, - duration, - feature.label.c_str())); + model->add(Event(frame, + value, + duration, + feature.label.c_str())); } }