annotate data/model/NoteModel.h @ 1520:954d0cf29ca7 import-audio-data

Switch the normalisation option in WritableWaveFileModel from normalising on read to normalising on write, so that the saved file is already normalised and therefore can be read again without having to remember to normalise it
author Chris Cannam
date Wed, 12 Sep 2018 13:56:56 +0100
parents 6965c83c8fa7
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
Chris@1495 16 #ifndef SV_NOTE_MODEL_H
Chris@1495 17 #define SV_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@1038 41 Note(sv_frame_t _frame) : frame(_frame), value(0.0f), duration(0), level(1.f) { }
Chris@1038 42 Note(sv_frame_t _frame, float _value, sv_frame_t _duration, float _level, QString _label) :
Chris@1429 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@1038 47 sv_frame_t frame;
Chris@147 48 float value;
Chris@1038 49 sv_frame_t 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@1429 59 stream <<
Chris@340 60 QString("%1<point frame=\"%2\" value=\"%3\" duration=\"%4\" level=\"%5\" label=\"%6\" %7/>\n")
Chris@1429 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@1060 65 QString toDelimitedDataString(QString delimiter, DataExportOptions opts, sv_samplerate_t sampleRate) const {
Chris@147 66 QStringList list;
Chris@147 67 list << RealTime::frame2RealTime(frame, sampleRate).toString().c_str();
Chris@147 68 list << QString("%1").arg(value);
Chris@340 69 list << RealTime::frame2RealTime(duration, sampleRate).toString().c_str();
Chris@1060 70 if (!(opts & DataExportOmitLevels)) {
Chris@1060 71 list << QString("%1").arg(level);
Chris@1060 72 }
Chris@318 73 if (label != "") list << label;
Chris@147 74 return list.join(delimiter);
Chris@147 75 }
Chris@147 76
Chris@147 77 struct Comparator {
Chris@1429 78 bool operator()(const Note &p1,
Chris@1429 79 const Note &p2) const {
Chris@1429 80 if (p1.frame != p2.frame) return p1.frame < p2.frame;
Chris@1429 81 if (p1.value != p2.value) return p1.value < p2.value;
Chris@1429 82 if (p1.duration != p2.duration) return p1.duration < p2.duration;
Chris@340 83 if (p1.level != p2.level) return p1.level < p2.level;
Chris@1429 84 return p1.label < p2.label;
Chris@1429 85 }
Chris@147 86 };
Chris@147 87
Chris@147 88 struct OrderComparator {
Chris@1429 89 bool operator()(const Note &p1,
Chris@1429 90 const Note &p2) const {
Chris@1429 91 return p1.frame < p2.frame;
Chris@1429 92 }
Chris@147 93 };
Chris@147 94 };
Chris@147 95
Chris@147 96
Chris@852 97 class NoteModel : public IntervalModel<Note>, public NoteExportable
Chris@147 98 {
Chris@423 99 Q_OBJECT
Chris@423 100
Chris@147 101 public:
Chris@1040 102 NoteModel(sv_samplerate_t sampleRate, int resolution,
Chris@1429 103 bool notifyOnAdd = true) :
Chris@1429 104 IntervalModel<Note>(sampleRate, resolution, notifyOnAdd),
Chris@1429 105 m_valueQuantization(0)
Chris@256 106 {
Chris@1429 107 PlayParameterRepository::getInstance()->addPlayable(this);
Chris@256 108 }
Chris@256 109
Chris@1040 110 NoteModel(sv_samplerate_t sampleRate, int resolution,
Chris@1429 111 float valueMinimum, float valueMaximum,
Chris@1429 112 bool notifyOnAdd = true) :
Chris@1429 113 IntervalModel<Note>(sampleRate, resolution,
Chris@437 114 valueMinimum, valueMaximum,
Chris@437 115 notifyOnAdd),
Chris@1429 116 m_valueQuantization(0)
Chris@147 117 {
Chris@1429 118 PlayParameterRepository::getInstance()->addPlayable(this);
Chris@391 119 }
Chris@391 120
Chris@391 121 virtual ~NoteModel()
Chris@391 122 {
Chris@391 123 PlayParameterRepository::getInstance()->removePlayable(this);
Chris@147 124 }
Chris@147 125
Chris@147 126 float getValueQuantization() const { return m_valueQuantization; }
Chris@147 127 void setValueQuantization(float q) { m_valueQuantization = q; }
Chris@147 128
Chris@345 129 QString getTypeName() const { return tr("Note"); }
Chris@345 130
Chris@391 131 virtual bool canPlay() const { return true; }
Chris@391 132
Chris@866 133 virtual QString getDefaultPlayClipId() const
Chris@391 134 {
Chris@909 135 return "elecpiano";
Chris@391 136 }
Chris@391 137
Chris@288 138 virtual void toXml(QTextStream &out,
Chris@288 139 QString indent = "",
Chris@288 140 QString extraAttributes = "") const
Chris@147 141 {
Chris@318 142 std::cerr << "NoteModel::toXml: extraAttributes = \""
Chris@318 143 << extraAttributes.toStdString() << std::endl;
Chris@318 144
Chris@437 145 IntervalModel<Note>::toXml
Chris@1429 146 (out,
Chris@288 147 indent,
Chris@1429 148 QString("%1 subtype=\"note\" valueQuantization=\"%2\"")
Chris@1429 149 .arg(extraAttributes).arg(m_valueQuantization));
Chris@147 150 }
Chris@147 151
Chris@424 152 /**
Chris@424 153 * TabularModel methods.
Chris@424 154 */
Chris@424 155
Chris@424 156 virtual int getColumnCount() const
Chris@424 157 {
Chris@424 158 return 6;
Chris@424 159 }
Chris@424 160
Chris@424 161 virtual QString getHeading(int column) const
Chris@424 162 {
Chris@424 163 switch (column) {
Chris@424 164 case 0: return tr("Time");
Chris@424 165 case 1: return tr("Frame");
Chris@424 166 case 2: return tr("Pitch");
Chris@424 167 case 3: return tr("Duration");
Chris@424 168 case 4: return tr("Level");
Chris@424 169 case 5: return tr("Label");
Chris@424 170 default: return tr("Unknown");
Chris@424 171 }
Chris@424 172 }
Chris@424 173
Chris@424 174 virtual QVariant getData(int row, int column, int role) const
Chris@424 175 {
Chris@437 176 if (column < 4) {
Chris@437 177 return IntervalModel<Note>::getData(row, column, role);
Chris@425 178 }
Chris@425 179
Chris@606 180 PointListConstIterator i = getPointListIteratorForRow(row);
Chris@424 181 if (i == m_points.end()) return QVariant();
Chris@424 182
Chris@424 183 switch (column) {
Chris@424 184 case 4: return i->level;
Chris@424 185 case 5: return i->label;
Chris@424 186 default: return QVariant();
Chris@424 187 }
Chris@424 188 }
Chris@424 189
Chris@424 190 virtual Command *getSetDataCommand(int row, int column, const QVariant &value, int role)
Chris@424 191 {
Chris@437 192 if (column < 4) {
Chris@437 193 return IntervalModel<Note>::getSetDataCommand
Chris@425 194 (row, column, value, role);
Chris@425 195 }
Chris@425 196
Chris@740 197 if (role != Qt::EditRole) return 0;
Chris@606 198 PointListConstIterator i = getPointListIteratorForRow(row);
Chris@740 199 if (i == m_points.end()) return 0;
Chris@424 200 EditCommand *command = new EditCommand(this, tr("Edit Data"));
Chris@424 201
Chris@424 202 Point point(*i);
Chris@424 203 command->deletePoint(point);
Chris@424 204
Chris@424 205 switch (column) {
Chris@1038 206 case 4: point.level = float(value.toDouble()); break;
Chris@424 207 case 5: point.label = value.toString(); break;
Chris@424 208 }
Chris@424 209
Chris@424 210 command->addPoint(point);
Chris@424 211 return command->finish();
Chris@424 212 }
Chris@424 213
Chris@424 214 virtual SortType getSortType(int column) const
Chris@424 215 {
Chris@424 216 if (column == 5) return SortAlphabetical;
Chris@424 217 return SortNumeric;
Chris@424 218 }
Chris@424 219
Chris@852 220 /**
Chris@852 221 * NoteExportable methods.
Chris@852 222 */
Chris@852 223
Chris@852 224 NoteList getNotes() const {
Chris@929 225 return getNotesWithin(getStartFrame(), getEndFrame());
Chris@852 226 }
Chris@852 227
Chris@1038 228 NoteList getNotesWithin(sv_frame_t startFrame, sv_frame_t endFrame) const {
Chris@852 229
Chris@1429 230 PointList points = getPoints(startFrame, endFrame);
Chris@852 231 NoteList notes;
Chris@852 232
Chris@852 233 for (PointList::iterator pli =
Chris@1429 234 points.begin(); pli != points.end(); ++pli) {
Chris@852 235
Chris@1429 236 sv_frame_t duration = pli->duration;
Chris@852 237 if (duration == 0 || duration == 1) {
Chris@1040 238 duration = sv_frame_t(getSampleRate() / 20);
Chris@852 239 }
Chris@852 240
Chris@1038 241 int pitch = int(lrintf(pli->value));
Chris@852 242
Chris@852 243 int velocity = 100;
Chris@852 244 if (pli->level > 0.f && pli->level <= 1.f) {
Chris@1038 245 velocity = int(lrintf(pli->level * 127));
Chris@852 246 }
Chris@852 247
Chris@852 248 NoteData note(pli->frame, duration, pitch, velocity);
Chris@852 249
Chris@852 250 if (getScaleUnits() == "Hz") {
Chris@852 251 note.frequency = pli->value;
Chris@852 252 note.midiPitch = Pitch::getPitchForFrequency(note.frequency);
Chris@852 253 note.isMidiPitchQuantized = false;
Chris@852 254 }
Chris@852 255
Chris@852 256 notes.push_back(note);
Chris@852 257 }
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