annotate data/model/NoteModel.h @ 1008:d9e0e59a1581

When using an aggregate model to pass data to a transform, zero-pad the shorter input to the duration of the longer rather than truncating the longer. (This is better behaviour for e.g. MATCH, and in any case the code was previously truncating incorrectly and ending up with garbage data at the end.)
author Chris Cannam
date Fri, 14 Nov 2014 13:51:33 +0000
parents 59e7fe1b1003
children cc27f35aa75c
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
Chris@147 16 #ifndef _NOTE_MODEL_H_
Chris@147 17 #define _NOTE_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@150 22 #include "base/PlayParameterRepository.h"
Chris@852 23 #include "base/Pitch.h"
Chris@147 24
Chris@147 25 /**
Chris@441 26 * NoteModel -- a concrete IntervalModel for notes.
Chris@441 27 */
Chris@441 28
Chris@441 29 /**
Chris@441 30 * Note type for use in a sparse model. All we mean by a "note" is
Chris@441 31 * something that has an onset time, a single value, a duration, and a
Chris@441 32 * level. Like other points, it can also have a label. With this
Chris@441 33 * point type, the model can be thought of as representing a simple
Chris@441 34 * MIDI-type piano roll, except that the y coordinates (values) do not
Chris@441 35 * have to be discrete integers.
Chris@147 36 */
Chris@147 37
Chris@147 38 struct Note
Chris@147 39 {
Chris@147 40 public:
Chris@340 41 Note(long _frame) : frame(_frame), value(0.0f), duration(0), level(1.f) { }
Chris@929 42 Note(long _frame, float _value, int _duration, float _level, QString _label) :
Chris@340 43 frame(_frame), value(_value), duration(_duration), level(_level), label(_label) { }
Chris@147 44
Chris@147 45 int getDimensions() const { return 3; }
Chris@147 46
Chris@147 47 long frame;
Chris@147 48 float value;
Chris@929 49 int duration;
Chris@340 50 float level;
Chris@147 51 QString label;
Chris@147 52
Chris@338 53 QString getLabel() const { return label; }
Chris@338 54
Chris@314 55 void toXml(QTextStream &stream,
Chris@314 56 QString indent = "",
Chris@314 57 QString extraAttributes = "") const
Chris@147 58 {
Chris@314 59 stream <<
Chris@340 60 QString("%1<point frame=\"%2\" value=\"%3\" duration=\"%4\" level=\"%5\" label=\"%6\" %7/>\n")
Chris@627 61 .arg(indent).arg(frame).arg(value).arg(duration).arg(level)
Chris@627 62 .arg(XmlExportable::encodeEntities(label)).arg(extraAttributes);
Chris@147 63 }
Chris@147 64
Chris@929 65 QString toDelimitedDataString(QString delimiter, int sampleRate) const
Chris@147 66 {
Chris@147 67 QStringList list;
Chris@147 68 list << RealTime::frame2RealTime(frame, sampleRate).toString().c_str();
Chris@147 69 list << QString("%1").arg(value);
Chris@340 70 list << RealTime::frame2RealTime(duration, sampleRate).toString().c_str();
Chris@340 71 list << QString("%1").arg(level);
Chris@318 72 if (label != "") list << label;
Chris@147 73 return list.join(delimiter);
Chris@147 74 }
Chris@147 75
Chris@147 76 struct Comparator {
Chris@147 77 bool operator()(const Note &p1,
Chris@147 78 const Note &p2) const {
Chris@147 79 if (p1.frame != p2.frame) return p1.frame < p2.frame;
Chris@147 80 if (p1.value != p2.value) return p1.value < p2.value;
Chris@147 81 if (p1.duration != p2.duration) return p1.duration < p2.duration;
Chris@340 82 if (p1.level != p2.level) return p1.level < p2.level;
Chris@147 83 return p1.label < p2.label;
Chris@147 84 }
Chris@147 85 };
Chris@147 86
Chris@147 87 struct OrderComparator {
Chris@147 88 bool operator()(const Note &p1,
Chris@147 89 const Note &p2) const {
Chris@147 90 return p1.frame < p2.frame;
Chris@147 91 }
Chris@147 92 };
Chris@147 93 };
Chris@147 94
Chris@147 95
Chris@852 96 class NoteModel : public IntervalModel<Note>, public NoteExportable
Chris@147 97 {
Chris@423 98 Q_OBJECT
Chris@423 99
Chris@147 100 public:
Chris@929 101 NoteModel(int sampleRate, int resolution,
Chris@256 102 bool notifyOnAdd = true) :
Chris@437 103 IntervalModel<Note>(sampleRate, resolution, notifyOnAdd),
Chris@256 104 m_valueQuantization(0)
Chris@256 105 {
Chris@391 106 PlayParameterRepository::getInstance()->addPlayable(this);
Chris@256 107 }
Chris@256 108
Chris@929 109 NoteModel(int sampleRate, int resolution,
Chris@147 110 float valueMinimum, float valueMaximum,
Chris@147 111 bool notifyOnAdd = true) :
Chris@437 112 IntervalModel<Note>(sampleRate, resolution,
Chris@437 113 valueMinimum, valueMaximum,
Chris@437 114 notifyOnAdd),
Chris@147 115 m_valueQuantization(0)
Chris@147 116 {
Chris@391 117 PlayParameterRepository::getInstance()->addPlayable(this);
Chris@391 118 }
Chris@391 119
Chris@391 120 virtual ~NoteModel()
Chris@391 121 {
Chris@391 122 PlayParameterRepository::getInstance()->removePlayable(this);
Chris@147 123 }
Chris@147 124
Chris@147 125 float getValueQuantization() const { return m_valueQuantization; }
Chris@147 126 void setValueQuantization(float q) { m_valueQuantization = q; }
Chris@147 127
Chris@345 128 QString getTypeName() const { return tr("Note"); }
Chris@345 129
Chris@391 130 virtual bool canPlay() const { return true; }
Chris@391 131
Chris@866 132 virtual QString getDefaultPlayClipId() const
Chris@391 133 {
Chris@909 134 return "elecpiano";
Chris@391 135 }
Chris@391 136
Chris@288 137 virtual void toXml(QTextStream &out,
Chris@288 138 QString indent = "",
Chris@288 139 QString extraAttributes = "") const
Chris@147 140 {
Chris@318 141 std::cerr << "NoteModel::toXml: extraAttributes = \""
Chris@318 142 << extraAttributes.toStdString() << std::endl;
Chris@318 143
Chris@437 144 IntervalModel<Note>::toXml
Chris@288 145 (out,
Chris@288 146 indent,
Chris@452 147 QString("%1 subtype=\"note\" valueQuantization=\"%2\"")
Chris@147 148 .arg(extraAttributes).arg(m_valueQuantization));
Chris@147 149 }
Chris@147 150
Chris@424 151 /**
Chris@424 152 * TabularModel methods.
Chris@424 153 */
Chris@424 154
Chris@424 155 virtual int getColumnCount() const
Chris@424 156 {
Chris@424 157 return 6;
Chris@424 158 }
Chris@424 159
Chris@424 160 virtual QString getHeading(int column) const
Chris@424 161 {
Chris@424 162 switch (column) {
Chris@424 163 case 0: return tr("Time");
Chris@424 164 case 1: return tr("Frame");
Chris@424 165 case 2: return tr("Pitch");
Chris@424 166 case 3: return tr("Duration");
Chris@424 167 case 4: return tr("Level");
Chris@424 168 case 5: return tr("Label");
Chris@424 169 default: return tr("Unknown");
Chris@424 170 }
Chris@424 171 }
Chris@424 172
Chris@424 173 virtual QVariant getData(int row, int column, int role) const
Chris@424 174 {
Chris@437 175 if (column < 4) {
Chris@437 176 return IntervalModel<Note>::getData(row, column, role);
Chris@425 177 }
Chris@425 178
Chris@606 179 PointListConstIterator i = getPointListIteratorForRow(row);
Chris@424 180 if (i == m_points.end()) return QVariant();
Chris@424 181
Chris@424 182 switch (column) {
Chris@424 183 case 4: return i->level;
Chris@424 184 case 5: return i->label;
Chris@424 185 default: return QVariant();
Chris@424 186 }
Chris@424 187 }
Chris@424 188
Chris@424 189 virtual Command *getSetDataCommand(int row, int column, const QVariant &value, int role)
Chris@424 190 {
Chris@437 191 if (column < 4) {
Chris@437 192 return IntervalModel<Note>::getSetDataCommand
Chris@425 193 (row, column, value, role);
Chris@425 194 }
Chris@425 195
Chris@740 196 if (role != Qt::EditRole) return 0;
Chris@606 197 PointListConstIterator i = getPointListIteratorForRow(row);
Chris@740 198 if (i == m_points.end()) return 0;
Chris@424 199 EditCommand *command = new EditCommand(this, tr("Edit Data"));
Chris@424 200
Chris@424 201 Point point(*i);
Chris@424 202 command->deletePoint(point);
Chris@424 203
Chris@424 204 switch (column) {
Chris@424 205 case 4: point.level = value.toDouble(); break;
Chris@424 206 case 5: point.label = value.toString(); break;
Chris@424 207 }
Chris@424 208
Chris@424 209 command->addPoint(point);
Chris@424 210 return command->finish();
Chris@424 211 }
Chris@424 212
Chris@424 213 virtual SortType getSortType(int column) const
Chris@424 214 {
Chris@424 215 if (column == 5) return SortAlphabetical;
Chris@424 216 return SortNumeric;
Chris@424 217 }
Chris@424 218
Chris@852 219 /**
Chris@852 220 * NoteExportable methods.
Chris@852 221 */
Chris@852 222
Chris@852 223 NoteList getNotes() const {
Chris@929 224 return getNotesWithin(getStartFrame(), getEndFrame());
Chris@852 225 }
Chris@852 226
Chris@929 227 NoteList getNotesWithin(int startFrame, int endFrame) const {
Chris@852 228
Chris@852 229 PointList points = getPoints(startFrame, endFrame);
Chris@852 230 NoteList notes;
Chris@852 231
Chris@852 232 for (PointList::iterator pli =
Chris@852 233 points.begin(); pli != points.end(); ++pli) {
Chris@852 234
Chris@929 235 int duration = pli->duration;
Chris@852 236 if (duration == 0 || duration == 1) {
Chris@852 237 duration = getSampleRate() / 20;
Chris@852 238 }
Chris@852 239
Chris@852 240 int pitch = lrintf(pli->value);
Chris@852 241
Chris@852 242 int velocity = 100;
Chris@852 243 if (pli->level > 0.f && pli->level <= 1.f) {
Chris@852 244 velocity = lrintf(pli->level * 127);
Chris@852 245 }
Chris@852 246
Chris@852 247 NoteData note(pli->frame, duration, pitch, velocity);
Chris@852 248
Chris@852 249 if (getScaleUnits() == "Hz") {
Chris@852 250 note.frequency = pli->value;
Chris@852 251 note.midiPitch = Pitch::getPitchForFrequency(note.frequency);
Chris@852 252 note.isMidiPitchQuantized = false;
Chris@852 253 }
Chris@852 254
Chris@852 255 notes.push_back(note);
Chris@852 256 }
Chris@852 257
Chris@852 258 return notes;
Chris@852 259 }
Chris@852 260
Chris@147 261 protected:
Chris@147 262 float m_valueQuantization;
Chris@147 263 };
Chris@147 264
Chris@147 265 #endif