changeset 1647:29a20719796e single-point

Rework NoteModel commands (not entirely successfully); remove FlexiNoteModel as it has always been almost entirely identical to NoteModel (unlike its layer counterpart)
author Chris Cannam
date Thu, 14 Mar 2019 15:31:59 +0000
parents b429750e64a8
children 86bbccb79c9b
files base/Event.h data/model/FlexiNoteModel.h data/model/NoteModel.h files.pri transform/FeatureExtractionModelTransformer.cpp
diffstat 5 files changed, 54 insertions(+), 353 deletions(-) [+]
line wrap: on
line diff
--- a/base/Event.h	Thu Mar 14 13:11:47 2019 +0000
+++ b/base/Event.h	Thu Mar 14 15:31:59 2019 +0000
@@ -27,12 +27,12 @@
 #include <QString>
 
 /**
- * An immutable type used for point and event representation in sparse
- * models, as well as for interchange within the clipboard. An event
- * always has a frame and (possibly empty) label, and optionally has
- * numerical value, level, duration in frames, and a mapped reference
- * frame. Event has an operator< defining a total ordering, by frame
- * first and then by the other properties.
+ * An immutable(-ish) type used for point and event representation in
+ * sparse models, as well as for interchange within the clipboard. An
+ * event always has a frame and (possibly empty) label, and optionally
+ * has numerical value, level, duration in frames, and a mapped
+ * reference frame. Event has an operator< defining a total ordering,
+ * by frame first and then by the other properties.
  * 
  * Event is based on the Clipboard::Point type up to SV v3.2.1 and is
  * intended also to replace the custom point types previously found in
@@ -83,6 +83,10 @@
     }
 
     Event(const Event &event) =default;
+
+    // We would ideally like Event to be immutable - but we have to
+    // have these because otherwise we can't put Events in vectors
+    // etc. Let's call it conceptually immutable
     Event &operator=(const Event &event) =default;
     Event &operator=(Event &&event) =default;
     
--- a/data/model/FlexiNoteModel.h	Thu Mar 14 13:11:47 2019 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,274 +0,0 @@
-/* -*- 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 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
-    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_FLEXINOTE_MODEL_H
-#define SV_FLEXINOTE_MODEL_H
-
-#include "IntervalModel.h"
-#include "base/NoteData.h"
-#include "base/NoteExportable.h"
-#include "base/RealTime.h"
-#include "base/Pitch.h"
-#include "base/PlayParameterRepository.h"
-
-/**
- * FlexiNoteModel -- a concrete IntervalModel for notes.
- */
-
-/**
- * Extension of the NoteModel for more flexible note interaction. 
- * The original NoteModel rationale is given below, will need to be
- * updated for FlexiNoteModel:
- *
- * 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 FlexiNote
-{
-public:
-    FlexiNote(sv_frame_t _frame) : frame(_frame), value(0.0f), duration(0), level(1.f) { }
-    FlexiNote(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<point frame=\"%2\" value=\"%3\" duration=\"%4\" level=\"%5\" label=\"%6\" %7/>\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 FlexiNote &p1,
-                        const FlexiNote &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 FlexiNote &p1,
-                        const FlexiNote &p2) const {
-            return p1.frame < p2.frame;
-        }
-    };
-};
-
-
-class FlexiNoteModel : public IntervalModel<FlexiNote>, public NoteExportable
-{
-    Q_OBJECT
-    
-public:
-    FlexiNoteModel(sv_samplerate_t sampleRate, int resolution,
-                   bool notifyOnAdd = true) :
-        IntervalModel<FlexiNote>(sampleRate, resolution, notifyOnAdd),
-        m_valueQuantization(0)
-    {
-        PlayParameterRepository::getInstance()->addPlayable(this);
-    }
-
-    FlexiNoteModel(sv_samplerate_t sampleRate, int resolution,
-              float valueMinimum, float valueMaximum,
-              bool notifyOnAdd = true) :
-        IntervalModel<FlexiNote>(sampleRate, resolution,
-                            valueMinimum, valueMaximum,
-                            notifyOnAdd),
-        m_valueQuantization(0)
-    {
-        PlayParameterRepository::getInstance()->addPlayable(this);
-    }
-
-    virtual ~FlexiNoteModel()
-    {
-        PlayParameterRepository::getInstance()->removePlayable(this);
-    }
-
-    float getValueQuantization() const { return m_valueQuantization; }
-    void setValueQuantization(float q) { m_valueQuantization = q; }
-    float getValueMinimum() const override { return 33; }
-    float getValueMaximum() const override { return 88; }
-
-    QString getTypeName() const override { return tr("FlexiNote"); }
-
-    bool canPlay() const override { return true; }
-
-    QString getDefaultPlayClipId() const override
-    {
-        return "elecpiano";
-    }
-
-    void toXml(QTextStream &out,
-                       QString indent = "",
-                       QString extraAttributes = "") const override
-    {
-        std::cerr << "FlexiNoteModel::toXml: extraAttributes = \"" 
-                  << extraAttributes.toStdString() << std::endl;
-
-        IntervalModel<FlexiNote>::toXml
-            (out,
-             indent,
-             QString("%1 subtype=\"flexinote\" valueQuantization=\"%2\"")
-             .arg(extraAttributes).arg(m_valueQuantization));
-    }
-
-    /**
-     * TabularModel methods.  
-     */
-    
-    int getColumnCount() const override
-    {
-        return 6;
-    }
-
-    QString getHeading(int column) const override
-    {
-        switch (column) {
-        case 0: return tr("Time");
-        case 1: return tr("Frame");
-        case 2: return tr("Pitch");
-        case 3: return tr("Duration");
-        case 4: return tr("Level");
-        case 5: return tr("Label");
-        default: return tr("Unknown");
-        }
-    }
-
-    QVariant getData(int row, int column, int role) const override
-    {
-        if (column < 4) {
-            return IntervalModel<FlexiNote>::getData(row, column, role);
-        }
-
-        PointListConstIterator i = getPointListIteratorForRow(row);
-        if (i == m_points.end()) return QVariant();
-
-        switch (column) {
-        case 4: return i->level;
-        case 5: return i->label;
-        default: return QVariant();
-        }
-    }
-
-    Command *getSetDataCommand(int row, int column, const QVariant &value, int role) override
-    {
-        if (column < 4) {
-            return IntervalModel<FlexiNote>::getSetDataCommand
-                (row, column, value, role);
-        }
-
-        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();
-    }
-
-    SortType getSortType(int column) const override
-    {
-        if (column == 5) return SortAlphabetical;
-        return SortNumeric;
-    }
-
-    /**
-     * NoteExportable methods.
-     */
-
-    NoteList getNotes() const 
-    override {
-        return getNotesStartingWithin(getStartFrame(), getEndFrame());
-    }
-
-    //!!!:
-    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;
-        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);
-        }
-        return notes;
-    }
-
-protected:
-    float m_valueQuantization;
-};
-
-#endif
--- a/data/model/NoteModel.h	Thu Mar 14 13:11:47 2019 +0000
+++ b/data/model/NoteModel.h	Thu Mar 14 15:31:59 2019 +0000
@@ -37,9 +37,16 @@
     Q_OBJECT
     
 public:
+    enum Subtype {
+        NORMAL_NOTE,
+        FLEXI_NOTE
+    };
+    
     NoteModel(sv_samplerate_t sampleRate,
               int resolution,
-              bool notifyOnAdd = true) :
+              bool notifyOnAdd = true,
+              Subtype subtype = NORMAL_NOTE) :
+        m_subtype(subtype),
         m_sampleRate(sampleRate),
         m_resolution(resolution),
         m_valueMinimum(0.f),
@@ -52,12 +59,18 @@
         m_sinceLastNotifyMin(-1),
         m_sinceLastNotifyMax(-1),
         m_completion(0) {
+        if (subtype == FLEXI_NOTE) {
+            m_valueMinimum = 33.f;
+            m_valueMaximum = 88.f;
+        }
         PlayParameterRepository::getInstance()->addPlayable(this);
     }
 
     NoteModel(sv_samplerate_t sampleRate, int resolution,
               float valueMinimum, float valueMaximum,
-              bool notifyOnAdd = true) :
+              bool notifyOnAdd = true,
+              Subtype subtype = NORMAL_NOTE) :
+        m_subtype(subtype),
         m_sampleRate(sampleRate),
         m_resolution(resolution),
         m_valueMinimum(valueMinimum),
@@ -76,8 +89,9 @@
     virtual ~NoteModel() {
         PlayParameterRepository::getInstance()->removePlayable(this);
     }
-    
+
     QString getTypeName() const override { return tr("Note"); }
+    Subtype getSubtype() const { return m_subtype; }
 
     bool isOK() const override { return true; }
     sv_frame_t getStartFrame() const override { return m_events.getStartFrame(); }
@@ -186,7 +200,7 @@
     public:
         //!!! borrowed ptr
         EditCommand(NoteModel *model, QString name) :
-            m_model(model), m_name(name) { }
+            m_model(model), m_executed(false), m_name(name) { }
 
         QString getName() const override {
             return m_name;
@@ -197,45 +211,44 @@
         }
 
         void add(Event e) {
-            m_add.insert(e);
+            m_adding.insert(e);
+            m_model->add(e);
+            m_executed = true;
         }
 
         void remove(Event e) {
-            m_remove.insert(e);
+            m_removing.insert(e);
+            m_model->remove(e);
+            m_executed = true;
         }
         
         void execute() override {
-            for (const Event &e: m_add) {
-                m_model->add(e);
-            }
-            for (const Event &e: m_remove) {
-                m_model->remove(e);
-            }
+            if (m_executed) return;
+            for (const Event &e: m_adding) m_model->add(e);
+            for (const Event &e: m_removing) m_model->remove(e);
+            m_executed = true;
         }
 
         void unexecute() override {
-            for (const Event &e: m_remove) {
-                m_model->add(e);
-            }
-            for (const Event &e: m_add) {
-                m_model->remove(e);
-            }
+            for (const Event &e: m_removing) m_model->add(e);
+            for (const Event &e: m_adding) m_model->remove(e);
+            m_executed = false;
         }
 
         EditCommand *finish() {
-            if (m_add.empty() && m_remove.empty()) {
+            if (m_adding.empty() && m_removing.empty()) {
                 delete this;
                 return nullptr;
             } else {
-                execute();
                 return this;
             }
         }
 
     private:
         NoteModel *m_model;
-        std::set<Event> m_add;
-        std::set<Event> m_remove;
+        bool m_executed;
+        std::set<Event> m_adding;
+        std::set<Event> m_removing;
         QString m_name;
     };
 
@@ -426,22 +439,24 @@
             (out,
              indent,
              QString("type=\"sparse\" dimensions=\"3\" resolution=\"%1\" "
-                     "notifyOnAdd=\"%2\" dataset=\"%3\" subtype=\"note\" "
-                     "valueQuantization=\"%4\" minimum=\"%5\" maximum=\"%6\" "
-                     "units=\"%7\" %8")
+                     "notifyOnAdd=\"%2\" dataset=\"%3\" subtype=\"%4\" "
+                     "valueQuantization=\"%5\" minimum=\"%6\" maximum=\"%7\" "
+                     "units=\"%8\" %9")
              .arg(m_resolution)
              .arg(m_notifyOnAdd ? "true" : "false")
              .arg(getObjectExportId(&m_events))
+             .arg(m_subtype == FLEXI_NOTE ? "flexinote" : "note")
              .arg(m_valueQuantization)
              .arg(m_valueMinimum)
              .arg(m_valueMaximum)
              .arg(m_units)
              .arg(extraAttributes));
-
+        
         m_events.toXml(out, indent, QString("dimensions=\"3\""));
     }
 
 protected:
+    Subtype m_subtype;
     sv_samplerate_t m_sampleRate;
     int m_resolution;
 
--- a/files.pri	Thu Mar 14 13:11:47 2019 +0000
+++ b/files.pri	Thu Mar 14 15:31:59 2019 +0000
@@ -87,7 +87,6 @@
            data/model/Model.h \
            data/model/ModelDataTableModel.h \
            data/model/NoteModel.h \
-           data/model/FlexiNoteModel.h \
            data/model/PathModel.h \
            data/model/PowerOfSqrtTwoZoomConstraint.h \
            data/model/PowerOfTwoZoomConstraint.h \
--- a/transform/FeatureExtractionModelTransformer.cpp	Thu Mar 14 13:11:47 2019 +0000
+++ b/transform/FeatureExtractionModelTransformer.cpp	Thu Mar 14 15:31:59 2019 +0000
@@ -28,7 +28,6 @@
 #include "data/model/EditableDenseThreeDimensionalModel.h"
 #include "data/model/DenseTimeValueModel.h"
 #include "data/model/NoteModel.h"
-#include "data/model/FlexiNoteModel.h"
 #include "data/model/RegionModel.h"
 #include "data/model/FFTModel.h"
 #include "data/model/WaveFileModel.h"
@@ -410,13 +409,8 @@
         // count > 1).  But we don't.
 
         QSettings settings;
-        settings.beginGroup("Transformer");
-        bool flexi = settings.value("use-flexi-note-model", false).toBool();
-        settings.endGroup();
 
-        cerr << "flexi = " << flexi << endl;
-
-        if (isNoteModel && !flexi) {
+        if (isNoteModel) {
 
             NoteModel *model;
             if (haveExtents) {
@@ -429,19 +423,6 @@
             model->setScaleUnits(m_descriptors[n]->unit.c_str());
             out = model;
 
-        } else if (isNoteModel && flexi) {
-
-            FlexiNoteModel *model;
-            if (haveExtents) {
-                model = new FlexiNoteModel
-                    (modelRate, modelResolution, minValue, maxValue, false);
-            } else {
-                model = new FlexiNoteModel
-                    (modelRate, modelResolution, false);
-            }
-            model->setScaleUnits(m_descriptors[n]->unit.c_str());
-            out = model;
-
         } else {
 
             RegionModel *model;
@@ -1027,7 +1008,7 @@
                 (SparseTimeValueModel::Point(frame, value, label));
         }
 
-    } else if (isOutput<FlexiNoteModel>(n) || isOutput<NoteModel>(n) || isOutput<RegionModel>(n)) { //GF: Added Note Model
+    } else if (isOutput<NoteModel>(n) || isOutput<RegionModel>(n)) {
 
         int index = 0;
 
@@ -1045,24 +1026,7 @@
             }
         }
 
-        if (isOutput<FlexiNoteModel>(n)) { // GF: added for flexi note model
-
-            float velocity = 100;
-            if ((int)feature.values.size() > index) {
-                velocity = feature.values[index++];
-            }
-            if (velocity < 0) velocity = 127;
-            if (velocity > 127) velocity = 127;
-
-            FlexiNoteModel *model = getConformingOutput<FlexiNoteModel>(n);
-            if (!model) return;
-            model->addPoint(FlexiNoteModel::Point(frame,
-                                                  value, // value is pitch
-                                                  duration,
-                                                  velocity / 127.f,
-                                                  feature.label.c_str()));
-                        // GF: end -- added for flexi note model
-        } else  if (isOutput<NoteModel>(n)) {
+        if (isOutput<NoteModel>(n)) {
 
             float velocity = 100;
             if ((int)feature.values.size() > index) {
@@ -1160,13 +1124,6 @@
         if (model->isAbandoning()) abandon();
         model->setCompletion(completion, true);
         
-    } else if (isOutput<FlexiNoteModel>(n)) {
-
-        FlexiNoteModel *model = getConformingOutput<FlexiNoteModel>(n);
-        if (!model) return;
-        if (model->isAbandoning()) abandon();
-        model->setCompletion(completion, true);
-
     } else if (isOutput<RegionModel>(n)) {
 
         RegionModel *model = getConformingOutput<RegionModel>(n);