annotate data/model/FlexiNoteModel.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 06579b8ffb7b
children b14064bd1f97
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:
matthiasm@774 45 FlexiNote(long _frame) : frame(_frame), value(0.0f), duration(0), level(1.f) { }
Chris@929 46 FlexiNote(long _frame, float _value, int _duration, float _level, QString _label) :
Chris@340 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@147 51 long frame;
Chris@147 52 float value;
Chris@929 53 int 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@314 63 stream <<
Chris@340 64 QString("%1<point frame=\"%2\" value=\"%3\" duration=\"%4\" level=\"%5\" label=\"%6\" %7/>\n")
Chris@627 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@929 69 QString toDelimitedDataString(QString delimiter, int 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@340 75 list << QString("%1").arg(level);
Chris@318 76 if (label != "") list << label;
Chris@147 77 return list.join(delimiter);
Chris@147 78 }
Chris@147 79
Chris@147 80 struct Comparator {
matthiasm@774 81 bool operator()(const FlexiNote &p1,
matthiasm@774 82 const FlexiNote &p2) const {
Chris@147 83 if (p1.frame != p2.frame) return p1.frame < p2.frame;
Chris@147 84 if (p1.value != p2.value) return p1.value < p2.value;
Chris@147 85 if (p1.duration != p2.duration) return p1.duration < p2.duration;
Chris@340 86 if (p1.level != p2.level) return p1.level < p2.level;
Chris@147 87 return p1.label < p2.label;
Chris@147 88 }
Chris@147 89 };
Chris@147 90
Chris@147 91 struct OrderComparator {
matthiasm@774 92 bool operator()(const FlexiNote &p1,
matthiasm@774 93 const FlexiNote &p2) const {
Chris@147 94 return p1.frame < p2.frame;
Chris@147 95 }
Chris@147 96 };
Chris@147 97 };
Chris@147 98
Chris@147 99
Chris@852 100 class FlexiNoteModel : public IntervalModel<FlexiNote>, public NoteExportable
Chris@147 101 {
Chris@423 102 Q_OBJECT
Chris@423 103
Chris@147 104 public:
Chris@929 105 FlexiNoteModel(int sampleRate, int resolution,
Chris@256 106 bool notifyOnAdd = true) :
matthiasm@774 107 IntervalModel<FlexiNote>(sampleRate, resolution, notifyOnAdd),
Chris@256 108 m_valueQuantization(0)
Chris@256 109 {
Chris@391 110 PlayParameterRepository::getInstance()->addPlayable(this);
Chris@256 111 }
Chris@256 112
Chris@929 113 FlexiNoteModel(int sampleRate, int resolution,
Chris@147 114 float valueMinimum, float valueMaximum,
Chris@147 115 bool notifyOnAdd = true) :
matthiasm@774 116 IntervalModel<FlexiNote>(sampleRate, resolution,
Chris@437 117 valueMinimum, valueMaximum,
Chris@437 118 notifyOnAdd),
Chris@147 119 m_valueQuantization(0)
Chris@147 120 {
Chris@391 121 PlayParameterRepository::getInstance()->addPlayable(this);
Chris@391 122 }
Chris@391 123
matthiasm@774 124 virtual ~FlexiNoteModel()
Chris@391 125 {
Chris@391 126 PlayParameterRepository::getInstance()->removePlayable(this);
Chris@147 127 }
Chris@147 128
Chris@147 129 float getValueQuantization() const { return m_valueQuantization; }
Chris@147 130 void setValueQuantization(float q) { m_valueQuantization = q; }
matthiasm@791 131 float getValueMinimum() const { return 33; }
matthiasm@791 132 float getValueMaximum() const { return 88; }
Chris@147 133
matthiasm@774 134 QString getTypeName() const { return tr("FlexiNote"); }
Chris@345 135
Chris@391 136 virtual bool canPlay() const { return true; }
Chris@391 137
Chris@866 138 virtual QString getDefaultPlayClipId() const
Chris@391 139 {
matthiasm@873 140 return "elecpiano";
Chris@391 141 }
Chris@391 142
Chris@288 143 virtual void toXml(QTextStream &out,
Chris@288 144 QString indent = "",
Chris@288 145 QString extraAttributes = "") const
Chris@147 146 {
matthiasm@774 147 std::cerr << "FlexiNoteModel::toXml: extraAttributes = \""
Chris@318 148 << extraAttributes.toStdString() << std::endl;
Chris@318 149
matthiasm@774 150 IntervalModel<FlexiNote>::toXml
Chris@288 151 (out,
Chris@288 152 indent,
Chris@896 153 QString("%1 subtype=\"flexinote\" valueQuantization=\"%2\"")
Chris@147 154 .arg(extraAttributes).arg(m_valueQuantization));
Chris@147 155 }
Chris@147 156
Chris@424 157 /**
Chris@424 158 * TabularModel methods.
Chris@424 159 */
Chris@424 160
Chris@424 161 virtual int getColumnCount() const
Chris@424 162 {
Chris@424 163 return 6;
Chris@424 164 }
Chris@424 165
Chris@424 166 virtual QString getHeading(int column) const
Chris@424 167 {
Chris@424 168 switch (column) {
Chris@424 169 case 0: return tr("Time");
Chris@424 170 case 1: return tr("Frame");
Chris@424 171 case 2: return tr("Pitch");
Chris@424 172 case 3: return tr("Duration");
Chris@424 173 case 4: return tr("Level");
Chris@424 174 case 5: return tr("Label");
Chris@424 175 default: return tr("Unknown");
Chris@424 176 }
Chris@424 177 }
Chris@424 178
Chris@424 179 virtual QVariant getData(int row, int column, int role) const
Chris@424 180 {
Chris@437 181 if (column < 4) {
matthiasm@774 182 return IntervalModel<FlexiNote>::getData(row, column, role);
Chris@425 183 }
Chris@425 184
Chris@606 185 PointListConstIterator i = getPointListIteratorForRow(row);
Chris@424 186 if (i == m_points.end()) return QVariant();
Chris@424 187
Chris@424 188 switch (column) {
Chris@424 189 case 4: return i->level;
Chris@424 190 case 5: return i->label;
Chris@424 191 default: return QVariant();
Chris@424 192 }
Chris@424 193 }
Chris@424 194
Chris@424 195 virtual Command *getSetDataCommand(int row, int column, const QVariant &value, int role)
Chris@424 196 {
Chris@437 197 if (column < 4) {
matthiasm@774 198 return IntervalModel<FlexiNote>::getSetDataCommand
Chris@425 199 (row, column, value, role);
Chris@425 200 }
Chris@425 201
Chris@740 202 if (role != Qt::EditRole) return 0;
Chris@606 203 PointListConstIterator i = getPointListIteratorForRow(row);
Chris@740 204 if (i == m_points.end()) return 0;
Chris@424 205 EditCommand *command = new EditCommand(this, tr("Edit Data"));
Chris@424 206
Chris@424 207 Point point(*i);
Chris@424 208 command->deletePoint(point);
Chris@424 209
Chris@424 210 switch (column) {
Chris@424 211 case 4: point.level = value.toDouble(); break;
Chris@424 212 case 5: point.label = value.toString(); break;
Chris@424 213 }
Chris@424 214
Chris@424 215 command->addPoint(point);
Chris@424 216 return command->finish();
Chris@424 217 }
Chris@424 218
Chris@424 219 virtual SortType getSortType(int column) const
Chris@424 220 {
Chris@424 221 if (column == 5) return SortAlphabetical;
Chris@424 222 return SortNumeric;
Chris@424 223 }
Chris@424 224
Chris@852 225 /**
Chris@852 226 * NoteExportable methods.
Chris@852 227 */
Chris@852 228
matthiasm@914 229 NoteList getNotes() const
matthiasm@914 230 {
Chris@930 231 return getNotesWithin(getStartFrame(), getEndFrame());
Chris@852 232 }
Chris@852 233
Chris@930 234 NoteList getNotesWithin(int startFrame, int endFrame) const
matthiasm@914 235 {
matthiasm@914 236 PointList points = getPoints(startFrame, endFrame);
Chris@852 237 NoteList notes;
Chris@917 238 for (PointList::iterator pli = points.begin(); pli != points.end(); ++pli) {
Chris@929 239 int duration = pli->duration;
Chris@852 240 if (duration == 0 || duration == 1) {
Chris@852 241 duration = getSampleRate() / 20;
Chris@852 242 }
matthiasm@914 243 int pitch = lrintf(pli->value);
Chris@852 244
Chris@852 245 int velocity = 100;
Chris@917 246 if (pli->level > 0.f && pli->level <= 1.f) {
Chris@852 247 velocity = lrintf(pli->level * 127);
Chris@852 248 }
Chris@852 249
Chris@852 250 NoteData note(pli->frame, duration, pitch, velocity);
Chris@852 251
Chris@852 252 if (getScaleUnits() == "Hz") {
Chris@852 253 note.frequency = pli->value;
Chris@852 254 note.midiPitch = Pitch::getPitchForFrequency(note.frequency);
Chris@852 255 note.isMidiPitchQuantized = false;
Chris@852 256 }
Chris@852 257 notes.push_back(note);
Chris@852 258 }
Chris@852 259 return notes;
Chris@852 260 }
Chris@852 261
Chris@147 262 protected:
Chris@147 263 float m_valueQuantization;
Chris@147 264 };
Chris@147 265
Chris@147 266 #endif