annotate data/model/FlexiNoteModel.h @ 1455:ec9e65fcf749

The use of the begin/end pairs here just seems to cause too many rows to be deleted (from the visual representation, not the underlying model). Things apparently work better if we just modify the underlying model and let the change signals percolate back up again. To that end, update the change handlers so as to cover their proper ranges with dataChanged signals.
author Chris Cannam
date Mon, 23 Apr 2018 16:03:35 +0100
parents 48e9f538e6e9
children c01cbe41aeb5
rev   line source
Chris@147 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@147 2
Chris@147 3 /*
Chris@147 4 Sonic Visualiser
Chris@147 5 An audio file viewer and annotation editor.
Chris@147 6 Centre for Digital Music, Queen Mary, University of London.
Chris@147 7 This file copyright 2006 Chris Cannam.
Chris@147 8
Chris@147 9 This program is free software; you can redistribute it and/or
Chris@147 10 modify it under the terms of the GNU General Public License as
Chris@147 11 published by the Free Software Foundation; either version 2 of the
Chris@147 12 License, or (at your option) any later version. See the file
Chris@147 13 COPYING included with this distribution for more information.
Chris@147 14 */
Chris@147 15
matthiasm@774 16 #ifndef _FLEXINOTE_MODEL_H_
matthiasm@774 17 #define _FLEXINOTE_MODEL_H_
Chris@147 18
Chris@437 19 #include "IntervalModel.h"
Chris@852 20 #include "NoteData.h"
Chris@391 21 #include "base/RealTime.h"
Chris@852 22 #include "base/Pitch.h"
Chris@150 23 #include "base/PlayParameterRepository.h"
Chris@147 24
Chris@147 25 /**
matthiasm@774 26 * FlexiNoteModel -- a concrete IntervalModel for notes.
Chris@441 27 */
Chris@441 28
Chris@441 29 /**
matthiasm@774 30 * Extension of the NoteModel for more flexible note interaction.
matthiasm@774 31 * The original NoteModel rationale is given below, will need to be
matthiasm@774 32 * updated for FlexiNoteModel:
matthiasm@774 33 *
Chris@441 34 * Note type for use in a sparse model. All we mean by a "note" is
Chris@441 35 * something that has an onset time, a single value, a duration, and a
Chris@441 36 * level. Like other points, it can also have a label. With this
Chris@441 37 * point type, the model can be thought of as representing a simple
Chris@441 38 * MIDI-type piano roll, except that the y coordinates (values) do not
Chris@441 39 * have to be discrete integers.
Chris@147 40 */
Chris@147 41
matthiasm@774 42 struct FlexiNote
Chris@147 43 {
Chris@147 44 public:
Chris@1039 45 FlexiNote(sv_frame_t _frame) : frame(_frame), value(0.0f), duration(0), level(1.f) { }
Chris@1039 46 FlexiNote(sv_frame_t _frame, float _value, sv_frame_t _duration, float _level, QString _label) :
Chris@1429 47 frame(_frame), value(_value), duration(_duration), level(_level), label(_label) { }
Chris@147 48
Chris@147 49 int getDimensions() const { return 3; }
Chris@147 50
Chris@1039 51 sv_frame_t frame;
Chris@147 52 float value;
Chris@1039 53 sv_frame_t duration;
Chris@340 54 float level;
Chris@147 55 QString label;
Chris@147 56
Chris@338 57 QString getLabel() const { return label; }
Chris@338 58
Chris@314 59 void toXml(QTextStream &stream,
Chris@314 60 QString indent = "",
Chris@314 61 QString extraAttributes = "") const
Chris@147 62 {
Chris@1429 63 stream <<
Chris@340 64 QString("%1<point frame=\"%2\" value=\"%3\" duration=\"%4\" level=\"%5\" label=\"%6\" %7/>\n")
Chris@1429 65 .arg(indent).arg(frame).arg(value).arg(duration).arg(level)
Chris@627 66 .arg(XmlExportable::encodeEntities(label)).arg(extraAttributes);
Chris@147 67 }
Chris@147 68
Chris@1060 69 QString toDelimitedDataString(QString delimiter, DataExportOptions opts, sv_samplerate_t sampleRate) const
Chris@147 70 {
Chris@147 71 QStringList list;
Chris@147 72 list << RealTime::frame2RealTime(frame, sampleRate).toString().c_str();
Chris@147 73 list << QString("%1").arg(value);
Chris@340 74 list << RealTime::frame2RealTime(duration, sampleRate).toString().c_str();
Chris@1060 75 if (!(opts & DataExportOmitLevels)) {
Chris@1060 76 list << QString("%1").arg(level);
Chris@1060 77 }
Chris@318 78 if (label != "") list << label;
Chris@147 79 return list.join(delimiter);
Chris@147 80 }
Chris@147 81
Chris@147 82 struct Comparator {
Chris@1429 83 bool operator()(const FlexiNote &p1,
Chris@1429 84 const FlexiNote &p2) const {
Chris@1429 85 if (p1.frame != p2.frame) return p1.frame < p2.frame;
Chris@1429 86 if (p1.value != p2.value) return p1.value < p2.value;
Chris@1429 87 if (p1.duration != p2.duration) return p1.duration < p2.duration;
Chris@340 88 if (p1.level != p2.level) return p1.level < p2.level;
Chris@1429 89 return p1.label < p2.label;
Chris@1429 90 }
Chris@147 91 };
Chris@147 92
Chris@147 93 struct OrderComparator {
Chris@1429 94 bool operator()(const FlexiNote &p1,
Chris@1429 95 const FlexiNote &p2) const {
Chris@1429 96 return p1.frame < p2.frame;
Chris@1429 97 }
Chris@147 98 };
Chris@147 99 };
Chris@147 100
Chris@147 101
Chris@852 102 class FlexiNoteModel : public IntervalModel<FlexiNote>, public NoteExportable
Chris@147 103 {
Chris@423 104 Q_OBJECT
Chris@423 105
Chris@147 106 public:
Chris@1040 107 FlexiNoteModel(sv_samplerate_t sampleRate, int resolution,
Chris@1040 108 bool notifyOnAdd = true) :
Chris@1429 109 IntervalModel<FlexiNote>(sampleRate, resolution, notifyOnAdd),
Chris@1429 110 m_valueQuantization(0)
Chris@256 111 {
Chris@1429 112 PlayParameterRepository::getInstance()->addPlayable(this);
Chris@256 113 }
Chris@256 114
Chris@1040 115 FlexiNoteModel(sv_samplerate_t sampleRate, int resolution,
Chris@1429 116 float valueMinimum, float valueMaximum,
Chris@1429 117 bool notifyOnAdd = true) :
Chris@1429 118 IntervalModel<FlexiNote>(sampleRate, resolution,
Chris@437 119 valueMinimum, valueMaximum,
Chris@437 120 notifyOnAdd),
Chris@1429 121 m_valueQuantization(0)
Chris@147 122 {
Chris@1429 123 PlayParameterRepository::getInstance()->addPlayable(this);
Chris@391 124 }
Chris@391 125
matthiasm@774 126 virtual ~FlexiNoteModel()
Chris@391 127 {
Chris@391 128 PlayParameterRepository::getInstance()->removePlayable(this);
Chris@147 129 }
Chris@147 130
Chris@147 131 float getValueQuantization() const { return m_valueQuantization; }
Chris@147 132 void setValueQuantization(float q) { m_valueQuantization = q; }
matthiasm@791 133 float getValueMinimum() const { return 33; }
matthiasm@791 134 float getValueMaximum() const { return 88; }
Chris@147 135
matthiasm@774 136 QString getTypeName() const { return tr("FlexiNote"); }
Chris@345 137
Chris@391 138 virtual bool canPlay() const { return true; }
Chris@391 139
Chris@866 140 virtual QString getDefaultPlayClipId() const
Chris@391 141 {
matthiasm@873 142 return "elecpiano";
Chris@391 143 }
Chris@391 144
Chris@288 145 virtual void toXml(QTextStream &out,
Chris@288 146 QString indent = "",
Chris@288 147 QString extraAttributes = "") const
Chris@147 148 {
matthiasm@774 149 std::cerr << "FlexiNoteModel::toXml: extraAttributes = \""
Chris@318 150 << extraAttributes.toStdString() << std::endl;
Chris@318 151
matthiasm@774 152 IntervalModel<FlexiNote>::toXml
Chris@1429 153 (out,
Chris@288 154 indent,
Chris@1429 155 QString("%1 subtype=\"flexinote\" valueQuantization=\"%2\"")
Chris@1429 156 .arg(extraAttributes).arg(m_valueQuantization));
Chris@147 157 }
Chris@147 158
Chris@424 159 /**
Chris@424 160 * TabularModel methods.
Chris@424 161 */
Chris@424 162
Chris@424 163 virtual int getColumnCount() const
Chris@424 164 {
Chris@424 165 return 6;
Chris@424 166 }
Chris@424 167
Chris@424 168 virtual QString getHeading(int column) const
Chris@424 169 {
Chris@424 170 switch (column) {
Chris@424 171 case 0: return tr("Time");
Chris@424 172 case 1: return tr("Frame");
Chris@424 173 case 2: return tr("Pitch");
Chris@424 174 case 3: return tr("Duration");
Chris@424 175 case 4: return tr("Level");
Chris@424 176 case 5: return tr("Label");
Chris@424 177 default: return tr("Unknown");
Chris@424 178 }
Chris@424 179 }
Chris@424 180
Chris@424 181 virtual QVariant getData(int row, int column, int role) const
Chris@424 182 {
Chris@437 183 if (column < 4) {
matthiasm@774 184 return IntervalModel<FlexiNote>::getData(row, column, role);
Chris@425 185 }
Chris@425 186
Chris@606 187 PointListConstIterator i = getPointListIteratorForRow(row);
Chris@424 188 if (i == m_points.end()) return QVariant();
Chris@424 189
Chris@424 190 switch (column) {
Chris@424 191 case 4: return i->level;
Chris@424 192 case 5: return i->label;
Chris@424 193 default: return QVariant();
Chris@424 194 }
Chris@424 195 }
Chris@424 196
Chris@424 197 virtual Command *getSetDataCommand(int row, int column, const QVariant &value, int role)
Chris@424 198 {
Chris@437 199 if (column < 4) {
matthiasm@774 200 return IntervalModel<FlexiNote>::getSetDataCommand
Chris@425 201 (row, column, value, role);
Chris@425 202 }
Chris@425 203
Chris@740 204 if (role != Qt::EditRole) return 0;
Chris@606 205 PointListConstIterator i = getPointListIteratorForRow(row);
Chris@740 206 if (i == m_points.end()) return 0;
Chris@424 207 EditCommand *command = new EditCommand(this, tr("Edit Data"));
Chris@424 208
Chris@424 209 Point point(*i);
Chris@424 210 command->deletePoint(point);
Chris@424 211
Chris@424 212 switch (column) {
Chris@1039 213 case 4: point.level = float(value.toDouble()); break;
Chris@424 214 case 5: point.label = value.toString(); break;
Chris@424 215 }
Chris@424 216
Chris@424 217 command->addPoint(point);
Chris@424 218 return command->finish();
Chris@424 219 }
Chris@424 220
Chris@424 221 virtual SortType getSortType(int column) const
Chris@424 222 {
Chris@424 223 if (column == 5) return SortAlphabetical;
Chris@424 224 return SortNumeric;
Chris@424 225 }
Chris@424 226
Chris@852 227 /**
Chris@852 228 * NoteExportable methods.
Chris@852 229 */
Chris@852 230
matthiasm@914 231 NoteList getNotes() const
matthiasm@914 232 {
Chris@930 233 return getNotesWithin(getStartFrame(), getEndFrame());
Chris@852 234 }
Chris@852 235
Chris@1039 236 NoteList getNotesWithin(sv_frame_t startFrame, sv_frame_t endFrame) const
matthiasm@914 237 {
Chris@1429 238 PointList points = getPoints(startFrame, endFrame);
Chris@852 239 NoteList notes;
Chris@917 240 for (PointList::iterator pli = points.begin(); pli != points.end(); ++pli) {
Chris@1429 241 sv_frame_t duration = pli->duration;
Chris@852 242 if (duration == 0 || duration == 1) {
Chris@1040 243 duration = sv_frame_t(getSampleRate() / 20);
Chris@852 244 }
Chris@1039 245 int pitch = int(lrintf(pli->value));
Chris@852 246
Chris@852 247 int velocity = 100;
Chris@917 248 if (pli->level > 0.f && pli->level <= 1.f) {
Chris@1039 249 velocity = int(lrintf(pli->level * 127));
Chris@852 250 }
Chris@852 251
Chris@852 252 NoteData note(pli->frame, duration, pitch, velocity);
Chris@852 253
Chris@852 254 if (getScaleUnits() == "Hz") {
Chris@852 255 note.frequency = pli->value;
Chris@852 256 note.midiPitch = Pitch::getPitchForFrequency(note.frequency);
Chris@852 257 note.isMidiPitchQuantized = false;
Chris@852 258 }
Chris@852 259 notes.push_back(note);
Chris@852 260 }
Chris@852 261 return notes;
Chris@852 262 }
Chris@852 263
Chris@147 264 protected:
Chris@147 265 float m_valueQuantization;
Chris@147 266 };
Chris@147 267
Chris@147 268 #endif