# HG changeset patch # User Chris Cannam # Date 1552488610 0 # Node ID 7a23dfe65d6629a681099ed889ff007d57a2e0ea # Parent d591836e47efd3f283a5ae219c0a6ad4fd9f44a9 Update NoteModel to use EventSeries. This is incomplete and won't provide enough to update svgui for it yet; must also refactor to avoid duplication of nasty APIs when updating the rest of the models diff -r d591836e47ef -r 7a23dfe65d66 base/NoteData.h --- a/base/NoteData.h Wed Mar 13 14:46:54 2019 +0000 +++ b/base/NoteData.h Wed Mar 13 14:50:10 2019 +0000 @@ -47,11 +47,4 @@ typedef std::vector NoteList; -class NoteExportable -{ -public: - virtual NoteList getNotes() const = 0; - virtual NoteList getNotesWithin(sv_frame_t startFrame, sv_frame_t endFrame) const = 0; -}; - #endif diff -r d591836e47ef -r 7a23dfe65d66 base/NoteExportable.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/base/NoteExportable.h Wed Mar 13 14:50:10 2019 +0000 @@ -0,0 +1,42 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef SV_NOTE_EXPORTABLE_H +#define SV_NOTE_EXPORTABLE_H + +#include "NoteData.h" + +class NoteExportable +{ +public: + /** + * Get all notes in the exportable object. + */ + virtual NoteList getNotes() const = 0; + + /** + * Get notes that are active at the given frame, i.e. that start + * before or at this frame and have not ended by it. + */ + virtual NoteList getNotesActiveAt(sv_frame_t frame) const = 0; + + /** + * Get notes that start within the range in frames defined by the + * given start frame and duration. + */ + virtual NoteList getNotesStartingWithin(sv_frame_t startFrame, + sv_frame_t duration) const = 0; +}; + +#endif diff -r d591836e47ef -r 7a23dfe65d66 data/fileio/CSVFileReader.cpp --- a/data/fileio/CSVFileReader.cpp Wed Mar 13 14:46:54 2019 +0000 +++ b/data/fileio/CSVFileReader.cpp Wed Mar 13 14:50:10 2019 +0000 @@ -453,8 +453,8 @@ } else if (modelType == CSVFormat::TwoDimensionalModelWithDurationAndPitch) { float level = ((value >= 0.f && value <= 1.f) ? value : 1.f); - NoteModel::Point point(frameNo, pitch, duration, level, label); - model2b->addPoint(point); + Event note(frameNo, pitch, duration, level, label); + model2b->addPoint(note); } else if (modelType == CSVFormat::ThreeDimensionalModel) { diff -r d591836e47ef -r 7a23dfe65d66 data/fileio/MIDIFileReader.cpp --- a/data/fileio/MIDIFileReader.cpp Wed Mar 13 14:46:54 2019 +0000 +++ b/data/fileio/MIDIFileReader.cpp Wed Mar 13 14:50:10 2019 +0000 @@ -1027,8 +1027,8 @@ float level = float((*i)->getVelocity()) / 128.f; - Note note(startFrame, (*i)->getPitch(), - endFrame - startFrame, level, noteLabel); + Event note(startFrame, (*i)->getPitch(), + endFrame - startFrame, level, noteLabel); // SVDEBUG << "Adding note " << startFrame << "," << (endFrame-startFrame) << " : " << int((*i)->getPitch()) << endl; diff -r d591836e47ef -r 7a23dfe65d66 data/fileio/MIDIFileWriter.cpp --- a/data/fileio/MIDIFileWriter.cpp Wed Mar 13 14:46:54 2019 +0000 +++ b/data/fileio/MIDIFileWriter.cpp Wed Mar 13 14:50:10 2019 +0000 @@ -25,6 +25,7 @@ #include "data/midi/MIDIEvent.h" #include "base/NoteData.h" +#include "base/NoteExportable.h" #include "base/Pitch.h" #include diff -r d591836e47ef -r 7a23dfe65d66 data/model/FlexiNoteModel.h --- a/data/model/FlexiNoteModel.h Wed Mar 13 14:46:54 2019 +0000 +++ b/data/model/FlexiNoteModel.h Wed Mar 13 14:50:10 2019 +0000 @@ -18,6 +18,7 @@ #include "IntervalModel.h" #include "base/NoteData.h" +#include "base/NoteExportable.h" #include "base/RealTime.h" #include "base/Pitch.h" #include "base/PlayParameterRepository.h" @@ -230,10 +231,15 @@ NoteList getNotes() const override { - return getNotesWithin(getStartFrame(), getEndFrame()); + return getNotesStartingWithin(getStartFrame(), getEndFrame()); } - NoteList getNotesWithin(sv_frame_t startFrame, sv_frame_t endFrame) const + //!!!: + NoteList getNotesActiveAt(sv_frame_t) const override { + return {}; + } + + NoteList getNotesStartingWithin(sv_frame_t startFrame, sv_frame_t endFrame) const override { PointList points = getPoints(startFrame, endFrame); NoteList notes; diff -r d591836e47ef -r 7a23dfe65d66 data/model/NoteModel.h --- a/data/model/NoteModel.h Wed Mar 13 14:46:54 2019 +0000 +++ b/data/model/NoteModel.h Wed Mar 13 14:50:10 2019 +0000 @@ -16,156 +16,192 @@ #ifndef SV_NOTE_MODEL_H #define SV_NOTE_MODEL_H -#include "IntervalModel.h" +#include "Model.h" +#include "TabularModel.h" +#include "base/UnitDatabase.h" +#include "base/EventSeries.h" #include "base/NoteData.h" +#include "base/NoteExportable.h" #include "base/RealTime.h" #include "base/PlayParameterRepository.h" #include "base/Pitch.h" +#include "system/System.h" -/** - * NoteModel -- a concrete IntervalModel for notes. - */ +#include +#include -/** - * Note type for use in a sparse model. All we mean by a "note" is - * something that has an onset time, a single value, a duration, and a - * level. Like other points, it can also have a label. With this - * point type, the model can be thought of as representing a simple - * MIDI-type piano roll, except that the y coordinates (values) do not - * have to be discrete integers. - */ - -struct Note -{ -public: - Note(sv_frame_t _frame) : frame(_frame), value(0.0f), duration(0), level(1.f) { } - Note(sv_frame_t _frame, float _value, sv_frame_t _duration, float _level, QString _label) : - frame(_frame), value(_value), duration(_duration), level(_level), label(_label) { } - - int getDimensions() const { return 3; } - - sv_frame_t frame; - float value; - sv_frame_t duration; - float level; - 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(level) - .arg(XmlExportable::encodeEntities(label)).arg(extraAttributes); - } - - QString toDelimitedDataString(QString delimiter, DataExportOptions opts, 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 (!(opts & DataExportOmitLevels)) { - list << QString("%1").arg(level); - } - if (label != "") list << label; - return list.join(delimiter); - } - - struct Comparator { - bool operator()(const Note &p1, - const Note &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; - if (p1.level != p2.level) return p1.level < p2.level; - return p1.label < p2.label; - } - }; - - struct OrderComparator { - bool operator()(const Note &p1, - const Note &p2) const { - return p1.frame < p2.frame; - } - }; - - bool operator==(const Note &other) const { - // ew - Comparator c; - return !(c(*this, other) || c(other, *this)); - } -}; - - -class NoteModel : public IntervalModel, public NoteExportable +class NoteModel : public Model, + public TabularModel, + public NoteExportable { Q_OBJECT public: NoteModel(sv_samplerate_t sampleRate, int resolution, bool notifyOnAdd = true) : - IntervalModel(sampleRate, resolution, notifyOnAdd), - m_valueQuantization(0) - { + m_sampleRate(sampleRate), + m_resolution(resolution), + m_valueMinimum(0.f), + m_valueMaximum(0.f), + m_haveExtents(false), + m_valueQuantization(0), + m_units(""), + m_extendTo(0), + m_notifyOnAdd(notifyOnAdd), + m_sinceLastNotifyMin(-1), + m_sinceLastNotifyMax(-1), + m_completion(0) { PlayParameterRepository::getInstance()->addPlayable(this); } NoteModel(sv_samplerate_t sampleRate, int resolution, float valueMinimum, float valueMaximum, bool notifyOnAdd = true) : - IntervalModel(sampleRate, resolution, - valueMinimum, valueMaximum, - notifyOnAdd), - m_valueQuantization(0) - { + m_sampleRate(sampleRate), + m_resolution(resolution), + m_valueMinimum(valueMinimum), + m_valueMaximum(valueMaximum), + m_haveExtents(true), + m_valueQuantization(0), + m_units(""), + m_extendTo(0), + m_notifyOnAdd(notifyOnAdd), + m_sinceLastNotifyMin(-1), + m_sinceLastNotifyMax(-1), + m_completion(0) { PlayParameterRepository::getInstance()->addPlayable(this); } - virtual ~NoteModel() - { + virtual ~NoteModel() { PlayParameterRepository::getInstance()->removePlayable(this); } + + QString getTypeName() const override { return tr("Note"); } + + 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; } + + bool canPlay() const override { return true; } + QString getDefaultPlayClipId() const override { + return "elecpiano"; + } + + QString getScaleUnits() const { return m_units; } + void setScaleUnits(QString units) { + m_units = units; + UnitDatabase::getInstance()->registerUnit(units); + } float getValueQuantization() const { return m_valueQuantization; } void setValueQuantization(float q) { m_valueQuantization = q; } - QString getTypeName() const override { return tr("Note"); } + float getValueMinimum() const { return m_valueMinimum; } + float getValueMaximum() const { return m_valueMaximum; } + + int getCompletion() const { return m_completion; } - bool canPlay() const override { return true; } + void setCompletion(int completion, bool update = true) { - QString getDefaultPlayClipId() const override - { - return "elecpiano"; + 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; + } } + + void toXml(QTextStream &out, + QString indent = "", + QString extraAttributes = "") const override { - void toXml(QTextStream &out, - QString indent = "", - QString extraAttributes = "") const override - { - std::cerr << "NoteModel::toXml: extraAttributes = \"" - << extraAttributes.toStdString() << "\"" << std::endl; - - IntervalModel::toXml + //!!! what is valueQuantization used for? + + Model::toXml (out, indent, - QString("%1 subtype=\"note\" valueQuantization=\"%2\"") - .arg(extraAttributes).arg(m_valueQuantization)); + QString("type=\"sparse\" dimensions=\"3\" resolution=\"%1\" " + "notifyOnAdd=\"%2\" dataset=\"%3\" subtype=\"note\" " + "valueQuantization=\"%4\" minimum=\"%5\" maximum=\"%6\" " + "units=\"%7\" %8") + .arg(m_resolution) + .arg(m_notifyOnAdd ? "true" : "false") + .arg(getObjectExportId(&m_events)) + .arg(m_valueQuantization) + .arg(m_valueMinimum) + .arg(m_valueMaximum) + .arg(m_units) + .arg(extraAttributes)); + + m_events.toXml(out, indent, QString("dimensions=\"3\"")); } /** * TabularModel methods. */ + + int getRowCount() const override { + return m_events.count(); + } - int getColumnCount() const override - { + int getColumnCount() const override { return 6; } - 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"); @@ -177,44 +213,158 @@ } } - 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->level; - case 5: 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.getLevel(); + case 5: return e.getLabel(); default: return QVariant(); } } + class EditCommand : public Command + { + public: + //!!! borrowed ptr + EditCommand(NoteModel *model, QString name) : + m_model(model), m_name(name) { } + + QString getName() const override { + return m_name; + } + + void addPoint(Event e) { + m_add.insert(e); + } + void deletePoint(Event e) { + m_remove.insert(e); + } + + void execute() override { + for (const Event &e: m_add) m_model->addPoint(e); + for (const Event &e: m_remove) m_model->deletePoint(e); + } + + void unexecute() override { + for (const Event &e: m_remove) m_model->addPoint(e); + for (const Event &e: m_add) m_model->deletePoint(e); + } + + private: + NoteModel *m_model; + std::set m_add; + std::set m_remove; + QString m_name; + }; + + //!!! rename Point to Note throughout? Just because we can now? + void addPoint(Event e) { + + 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; + } + + 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 deletePoint(Event e) { + { + QMutexLocker locker(&m_mutex); + m_events.remove(e); + } + emit modelChangedWithin(e.getFrame(), + e.getFrame() + e.getDuration() + m_resolution); + } + + EventVector getPoints() const /*!!! override? - and/or rename? */ { + EventVector ee; + for (int i = 0; i < m_events.count(); ++i) { + ee.push_back(m_events.getEventByIndex(i)); + } + return ee; + } + + //!!! bleah + EventVector getPoints(sv_frame_t start, sv_frame_t end) const { + return m_events.getEventsSpanning(start, end - start); + } + + int getPointCount() const { + return m_events.count(); + } + + bool isEmpty() const { + return m_events.isEmpty(); + } + + bool containsPoint(const Event &e) const { + return m_events.contains(e); + } + Command *getSetDataCommand(int row, int column, const QVariant &value, int role) override { - if (column < 4) { - return IntervalModel::getSetDataCommand - (row, column, value, role); + 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.withLevel(float(value.toDouble())); break; + case 5: 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 4: point.level = float(value.toDouble()); break; - case 5: point.label = value.toString(); break; - } - - command->addPoint(point); - return command->finish(); + command->deletePoint(e0); + command->addPoint(e1); + return command; } SortType getSortType(int column) const override @@ -228,45 +378,57 @@ */ NoteList getNotes() const override { - return getNotesWithin(getStartFrame(), getEndFrame()); + return getNotesStartingWithin(getStartFrame(), + getEndFrame() - getStartFrame()); } - NoteList getNotesWithin(sv_frame_t startFrame, sv_frame_t endFrame) const override { - - PointList points = getPoints(startFrame, endFrame); + NoteList getNotesActiveAt(sv_frame_t frame) const override { + NoteList notes; + EventVector ee = m_events.getEventsCovering(frame); + for (const auto &e: ee) { + notes.push_back(e.toNoteData(getSampleRate(), + getScaleUnits() != "Hz")); + } + return notes; + } + + NoteList getNotesStartingWithin(sv_frame_t startFrame, + sv_frame_t duration) const override { - for (PointList::iterator pli = - points.begin(); pli != points.end(); ++pli) { - - sv_frame_t duration = pli->duration; - if (duration == 0 || duration == 1) { - duration = sv_frame_t(getSampleRate() / 20); - } - - int pitch = int(lrintf(pli->value)); - - int velocity = 100; - if (pli->level > 0.f && pli->level <= 1.f) { - velocity = int(lrintf(pli->level * 127)); - } - - NoteData note(pli->frame, duration, pitch, velocity); - - if (getScaleUnits() == "Hz") { - note.frequency = pli->value; - note.midiPitch = Pitch::getPitchForFrequency(note.frequency); - note.isMidiPitchQuantized = false; - } - - notes.push_back(note); + NoteList notes; + EventVector ee = m_events.getEventsStartingWithin(startFrame, duration); + for (const auto &e: ee) { + notes.push_back(e.toNoteData(getSampleRate(), + getScaleUnits() != "Hz")); } - return notes; } protected: + sv_samplerate_t m_sampleRate; + int m_resolution; + + float m_valueMinimum; + float m_valueMaximum; + bool m_haveExtents; float m_valueQuantization; + QString m_units; + + sv_frame_t m_extendTo; + + bool m_notifyOnAdd; + sv_frame_t m_sinceLastNotifyMin; + sv_frame_t m_sinceLastNotifyMax; + + EventSeries m_events; + + int m_completion; + + mutable QMutex m_mutex; + + //!!! do we have general docs for ownership and synchronisation of models? + // this might be a good opportunity to stop using bare pointers to them }; #endif diff -r d591836e47ef -r 7a23dfe65d66 data/model/SparseOneDimensionalModel.h --- a/data/model/SparseOneDimensionalModel.h Wed Mar 13 14:46:54 2019 +0000 +++ b/data/model/SparseOneDimensionalModel.h Wed Mar 13 14:50:10 2019 +0000 @@ -18,6 +18,7 @@ #include "SparseModel.h" #include "base/NoteData.h" +#include "base/NoteExportable.h" #include "base/PlayParameterRepository.h" #include "base/RealTime.h" @@ -188,12 +189,18 @@ */ NoteList getNotes() const override { - return getNotesWithin(getStartFrame(), getEndFrame()); + return getNotesStartingWithin(getStartFrame(), + getEndFrame() - getStartFrame()); } - NoteList getNotesWithin(sv_frame_t startFrame, sv_frame_t endFrame) const override { + NoteList getNotesActiveAt(sv_frame_t frame) const override { + return getNotesStartingWithin(frame, 1); + } + + NoteList getNotesStartingWithin(sv_frame_t startFrame, + sv_frame_t duration) const override { - PointList points = getPoints(startFrame, endFrame); + PointList points = getPoints(startFrame, startFrame + duration); NoteList notes; for (PointList::iterator pli = diff -r d591836e47ef -r 7a23dfe65d66 data/model/TabularModel.h --- a/data/model/TabularModel.h Wed Mar 13 14:46:54 2019 +0000 +++ b/data/model/TabularModel.h Wed Mar 13 14:50:10 2019 +0000 @@ -19,6 +19,8 @@ #include #include +#include "base/RealTime.h" + class Command; /** @@ -55,6 +57,22 @@ virtual Command *getSetDataCommand(int /* row */, int /* column */, const QVariant &, int /* role */) { return 0; } virtual Command *getInsertRowCommand(int /* beforeRow */) { return 0; } virtual Command *getRemoveRowCommand(int /* row */) { return 0; } + + QVariant adaptFrameForRole(sv_frame_t frame, + sv_samplerate_t rate, + int role) const { + if (role == SortRole) return int(frame); + RealTime rt = RealTime::frame2RealTime(frame, rate); + if (role == Qt::EditRole) return rt.toString().c_str(); + else return rt.toText().c_str(); + } + + QVariant adaptValueForRole(float value, + QString unit, + int role) const { + if (role == SortRole || role == Qt::EditRole) return value; + else return QString("%1 %2").arg(value).arg(unit); + } }; #endif diff -r d591836e47ef -r 7a23dfe65d66 data/model/test/TestSparseModels.h --- a/data/model/test/TestSparseModels.h Wed Mar 13 14:46:54 2019 +0000 +++ b/data/model/test/TestSparseModels.h Wed Mar 13 14:50:10 2019 +0000 @@ -93,18 +93,19 @@ QCOMPARE(m.getPoints().size(), 3); QCOMPARE(*m.getPoints().begin(), p1); QCOMPARE(*m.getPoints().rbegin(), p3); - +/*!!! auto pp = m.getPoints(20, 30); QCOMPARE(pp.size(), 2); QCOMPARE(*pp.begin(), p1); QCOMPARE(*pp.rbegin(), p2); - + pp = m.getPoints(40, 50); QCOMPARE(pp.size(), 0); pp = m.getPoints(50, 50); QCOMPARE(pp.size(), 1); QCOMPARE(*pp.begin(), p3); +*/ } void s1d_xml() { @@ -136,11 +137,11 @@ void note_extents() { NoteModel m(100, 10, false); - NoteModel::Point p1(20, 123.4f, 40, 0.8f, "note 1"); + Event p1(20, 123.4f, 40, 0.8f, "note 1"); m.addPoint(p1); QCOMPARE(m.isEmpty(), false); QCOMPARE(m.getPointCount(), 1); - NoteModel::Point p2(50, 124.3f, 30, 0.9f, "note 2"); + Event p2(50, 124.3f, 30, 0.9f, "note 2"); m.addPoint(p2); QCOMPARE(m.isEmpty(), false); QCOMPARE(m.getPointCount(), 2); @@ -150,8 +151,8 @@ QCOMPARE(m.getStartFrame(), 20); QCOMPARE(m.getEndFrame(), 80); QCOMPARE(m.containsPoint(p1), true); - QCOMPARE(m.getValueMinimum(), 123.4); - QCOMPARE(m.getValueMaximum(), 124.3); + QCOMPARE(m.getValueMinimum(), 123.4f); + QCOMPARE(m.getValueMaximum(), 124.3f); m.deletePoint(p1); QCOMPARE(m.getPointCount(), 1); QCOMPARE(m.getPoints().size(), 1); @@ -163,9 +164,9 @@ void note_sample() { NoteModel m(100, 10, false); - NoteModel::Point p1(20, 123.4f, 20, 0.8f, "note 1"); - NoteModel::Point p2(20, 124.3f, 10, 0.9f, "note 2"); - NoteModel::Point p3(50, 126.3f, 30, 0.9f, "note 3"); + Event p1(20, 123.4f, 10, 0.8f, "note 1"); + Event p2(20, 124.3f, 20, 0.9f, "note 2"); + Event p3(50, 126.3f, 30, 0.9f, "note 3"); m.addPoint(p1); m.addPoint(p2); m.addPoint(p3); @@ -181,21 +182,22 @@ pp = m.getPoints(30, 50); QCOMPARE(pp.size(), 1); - QCOMPARE(*pp.begin(), p1); + QCOMPARE(*pp.begin(), p2); pp = m.getPoints(40, 50); QCOMPARE(pp.size(), 0); + //!!! this is a poor api pp = m.getPoints(50, 50); - QCOMPARE(pp.size(), 1); - QCOMPARE(*pp.begin(), p3); + QCOMPARE(pp.size(), 0); +// QCOMPARE(*pp.begin(), p3); } void note_xml() { NoteModel m(100, 10, false); - NoteModel::Point p1(20, 123.4f, 20, 0.8f, "note 1"); - NoteModel::Point p2(20, 124.3f, 10, 0.9f, "note 2"); - NoteModel::Point p3(50, 126.3f, 30, 0.9f, "note 3"); + Event p1(20, 123.4f, 20, 0.8f, "note 1"); + Event p2(20, 124.3f, 10, 0.9f, "note 2"); + Event p3(50, 126.3f, 30, 0.9f, "note 3"); m.setScaleUnits("Hz"); m.addPoint(p1); m.addPoint(p2); @@ -205,10 +207,10 @@ m.toXml(str); str.flush(); QString expected = - "\n" + "\n" "\n" + " \n" " \n" - " \n" " \n" "\n"; expected.replace("\'", "\""); @@ -218,7 +220,6 @@ } QCOMPARE(xml, expected); } - }; diff -r d591836e47ef -r 7a23dfe65d66 files.pri --- a/files.pri Wed Mar 13 14:46:54 2019 +0000 +++ b/files.pri Wed Mar 13 14:50:10 2019 +0000 @@ -15,6 +15,7 @@ base/LogRange.h \ base/MagnitudeRange.h \ base/NoteData.h \ + base/NoteExportable.h \ base/Pitch.h \ base/Playable.h \ base/PlayParameterRepository.h \ diff -r d591836e47ef -r 7a23dfe65d66 rdf/RDFExporter.cpp --- a/rdf/RDFExporter.cpp Wed Mar 13 14:46:54 2019 +0000 +++ b/rdf/RDFExporter.cpp Wed Mar 13 14:50:10 2019 +0000 @@ -103,15 +103,14 @@ if (m) { f.hasTimestamp = true; f.hasDuration = true; - const NoteModel::PointList &pl(m->getPoints()); - for (NoteModel::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->getPoints()); + 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.values.push_back(i->level); - f.label = i->label.toStdString(); + f.values.push_back(e.getValue()); + f.values.push_back(e.getLevel()); + f.label = e.getLabel().toStdString(); m_fw->write(trackId, transform, output, features, summaryType); } return; diff -r d591836e47ef -r 7a23dfe65d66 rdf/RDFImporter.cpp --- a/rdf/RDFImporter.cpp Wed Mar 13 14:46:54 2019 +0000 +++ b/rdf/RDFImporter.cpp Wed Mar 13 14:50:10 2019 +0000 @@ -745,7 +745,7 @@ level = values[1]; } } - NoteModel::Point point(ftime, value, fduration, level, label); + Event point(ftime, value, fduration, level, label); nm->addPoint(point); } else { float value = 0.f, duration = 1.f, level = 1.f; @@ -758,8 +758,8 @@ } } } - NoteModel::Point point(ftime, value, sv_frame_t(lrintf(duration)), - level, label); + Event point(ftime, value, sv_frame_t(lrintf(duration)), + level, label); nm->addPoint(point); } return; diff -r d591836e47ef -r 7a23dfe65d66 transform/FeatureExtractionModelTransformer.cpp --- a/transform/FeatureExtractionModelTransformer.cpp Wed Mar 13 14:46:54 2019 +0000 +++ b/transform/FeatureExtractionModelTransformer.cpp Wed Mar 13 14:50:10 2019 +0000 @@ -1073,10 +1073,10 @@ NoteModel *model = getConformingOutput(n); if (!model) return; - model->addPoint(NoteModel::Point(frame, value, // value is pitch - duration, - velocity / 127.f, - feature.label.c_str())); + model->addPoint(Event(frame, value, // value is pitch + duration, + velocity / 127.f, + feature.label.c_str())); } else { RegionModel *model = getConformingOutput(n);