annotate data/model/Labeller.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 2f49be7d4264
rev   line source
Chris@305 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@305 2
Chris@305 3 /*
Chris@305 4 Sonic Visualiser
Chris@305 5 An audio file viewer and annotation editor.
Chris@305 6 Centre for Digital Music, Queen Mary, University of London.
Chris@305 7 This file copyright 2006-2007 Chris Cannam and QMUL.
Chris@305 8
Chris@305 9 This program is free software; you can redistribute it and/or
Chris@305 10 modify it under the terms of the GNU General Public License as
Chris@305 11 published by the Free Software Foundation; either version 2 of the
Chris@305 12 License, or (at your option) any later version. See the file
Chris@305 13 COPYING included with this distribution for more information.
Chris@305 14 */
Chris@305 15
Chris@305 16 #ifndef _LABELLER_H_
Chris@305 17 #define _LABELLER_H_
Chris@305 18
Chris@305 19 #include "SparseModel.h"
Chris@305 20 #include "SparseValueModel.h"
Chris@305 21
Chris@305 22 #include "base/Selection.h"
Chris@305 23
Chris@305 24 #include <QObject>
Chris@305 25
Chris@305 26 #include <map>
Chris@305 27 #include <iostream>
Chris@305 28
Chris@305 29 class Labeller : public QObject
Chris@305 30 {
Chris@423 31 Q_OBJECT
Chris@423 32
Chris@305 33 public:
Chris@305 34 enum ValueType {
Chris@305 35 ValueNone,
Chris@305 36 ValueFromSimpleCounter,
Chris@305 37 ValueFromCyclicalCounter,
Chris@305 38 ValueFromTwoLevelCounter,
Chris@305 39 ValueFromFrameNumber,
Chris@305 40 ValueFromRealTime,
Chris@355 41 ValueFromDurationFromPrevious,
Chris@355 42 ValueFromDurationToNext,
Chris@355 43 ValueFromTempoFromPrevious,
Chris@355 44 ValueFromTempoToNext,
Chris@305 45 ValueFromExistingNeighbour,
Chris@305 46 ValueFromLabel
Chris@305 47 };
Chris@305 48
Chris@305 49 // uses:
Chris@305 50 //
Chris@305 51 // 1. when adding points to a time-value model, generate values
Chris@305 52 // for those points based on their times or labels or a counter
Chris@305 53 //
Chris@305 54 // 2. when adding a single point to a time-instant model, generate
Chris@305 55 // a label for it based on its time and that of the previous point
Chris@305 56 // or a counter
Chris@305 57 //
Chris@305 58 // 3. when adding a single point to a time-instant model, generate
Chris@305 59 // a label for the previous point based on its time and that of
Chris@305 60 // the point just added (as tempo is based on time to the next
Chris@305 61 // point, not the previous one)
Chris@305 62 //
Chris@305 63 // 4. re-label a set of points that have already been added to a
Chris@305 64 // model
Chris@305 65
Chris@355 66 Labeller(ValueType type = ValueNone) :
Chris@305 67 m_type(type),
Chris@305 68 m_counter(1),
Chris@305 69 m_counter2(1),
Chris@305 70 m_cycle(4),
Chris@305 71 m_dp(10),
Chris@305 72 m_rate(0) { }
Chris@305 73
Chris@305 74 Labeller(const Labeller &l) :
Chris@305 75 QObject(),
Chris@305 76 m_type(l.m_type),
Chris@380 77 m_counter(l.m_counter),
Chris@380 78 m_counter2(l.m_counter2),
Chris@305 79 m_cycle(l.m_cycle),
Chris@305 80 m_dp(l.m_dp),
Chris@305 81 m_rate(l.m_rate) { }
Chris@305 82
Chris@305 83 virtual ~Labeller() { }
Chris@305 84
Chris@305 85 typedef std::map<ValueType, QString> TypeNameMap;
Chris@305 86 TypeNameMap getTypeNames() const {
Chris@305 87 TypeNameMap m;
Chris@355 88 m[ValueNone]
Chris@355 89 = tr("No numbering");
Chris@355 90 m[ValueFromSimpleCounter]
Chris@355 91 = tr("Simple counter");
Chris@355 92 m[ValueFromCyclicalCounter]
Chris@355 93 = tr("Cyclical counter");
Chris@355 94 m[ValueFromTwoLevelCounter]
Chris@355 95 = tr("Cyclical two-level counter (bar/beat)");
Chris@355 96 m[ValueFromFrameNumber]
Chris@355 97 = tr("Audio sample frame number");
Chris@355 98 m[ValueFromRealTime]
Chris@355 99 = tr("Time in seconds");
Chris@355 100 m[ValueFromDurationToNext]
Chris@355 101 = tr("Duration to the following item");
Chris@355 102 m[ValueFromTempoToNext]
Chris@355 103 = tr("Tempo (bpm) based on duration to following item");
Chris@355 104 m[ValueFromDurationFromPrevious]
Chris@355 105 = tr("Duration since the previous item");
Chris@355 106 m[ValueFromTempoFromPrevious]
Chris@355 107 = tr("Tempo (bpm) based on duration since previous item");
Chris@355 108 m[ValueFromExistingNeighbour]
Chris@355 109 = tr("Same as the nearest previous item");
Chris@355 110 m[ValueFromLabel]
Chris@355 111 = tr("Value extracted from the item's label (where possible)");
Chris@305 112 return m;
Chris@305 113 }
Chris@305 114
Chris@305 115 ValueType getType() const { return m_type; }
Chris@305 116 void setType(ValueType type) { m_type = type; }
Chris@305 117
Chris@305 118 int getCounterValue() const { return m_counter; }
Chris@305 119 void setCounterValue(int v) { m_counter = v; }
Chris@305 120
Chris@305 121 int getSecondLevelCounterValue() const { return m_counter2; }
Chris@305 122 void setSecondLevelCounterValue(int v) { m_counter2 = v; }
Chris@305 123
Chris@305 124 int getCounterCycleSize() const { return m_cycle; }
Chris@305 125 void setCounterCycleSize(int s) {
Chris@305 126 m_cycle = s;
Chris@305 127 m_dp = 1;
Chris@305 128 while (s > 0) {
Chris@305 129 s /= 10;
Chris@305 130 m_dp *= 10;
Chris@305 131 }
Chris@307 132 if (m_counter > m_cycle) m_counter = 1;
Chris@305 133 }
Chris@305 134
Chris@305 135 void setSampleRate(float rate) { m_rate = rate; }
Chris@305 136
Chris@830 137 void resetCounters() {
Chris@830 138 m_counter = 1;
Chris@830 139 m_counter2 = 1;
Chris@830 140 m_cycle = 4;
Chris@830 141 }
Chris@830 142
Chris@305 143 void incrementCounter() {
Chris@305 144 m_counter++;
Chris@305 145 if (m_type == ValueFromCyclicalCounter ||
Chris@305 146 m_type == ValueFromTwoLevelCounter) {
Chris@305 147 if (m_counter > m_cycle) {
Chris@305 148 m_counter = 1;
Chris@305 149 m_counter2++;
Chris@305 150 }
Chris@305 151 }
Chris@305 152 }
Chris@305 153
Chris@305 154 template <typename PointType>
Chris@305 155 void label(PointType &newPoint, PointType *prevPoint = 0) {
Chris@305 156 if (m_type == ValueNone) {
Chris@305 157 newPoint.label = "";
Chris@305 158 } else if (m_type == ValueFromTwoLevelCounter) {
Chris@305 159 newPoint.label = tr("%1.%2").arg(m_counter2).arg(m_counter);
Chris@305 160 incrementCounter();
Chris@306 161 } else if (m_type == ValueFromFrameNumber) {
Chris@306 162 // avoid going through floating-point value
Chris@306 163 newPoint.label = tr("%1").arg(newPoint.frame);
Chris@305 164 } else {
Chris@305 165 float value = getValueFor<PointType>(newPoint, prevPoint);
Chris@305 166 if (actingOnPrevPoint() && prevPoint) {
Chris@305 167 prevPoint->label = QString("%1").arg(value);
Chris@305 168 } else {
Chris@305 169 newPoint.label = QString("%1").arg(value);
Chris@305 170 }
Chris@305 171 }
Chris@305 172 }
Chris@305 173
Chris@305 174 template <typename PointType>
Chris@305 175 void labelAll(SparseModel<PointType> &model, MultiSelection *ms) {
Chris@305 176
Chris@305 177 typename SparseModel<PointType>::PointList::iterator i;
Chris@305 178 typename SparseModel<PointType>::PointList pl(model.getPoints());
Chris@305 179
Chris@305 180 typename SparseModel<PointType>::EditCommand *command =
Chris@305 181 new typename SparseModel<PointType>::EditCommand
Chris@305 182 (&model, tr("Label Points"));
Chris@305 183
Chris@305 184 PointType prevPoint(0);
Chris@305 185
Chris@305 186 for (i = pl.begin(); i != pl.end(); ++i) {
Chris@305 187
Chris@305 188 bool inRange = true;
Chris@305 189 if (ms) {
Chris@305 190 Selection s(ms->getContainingSelection(i->frame, false));
Chris@305 191 if (s.isEmpty() || !s.contains(i->frame)) {
Chris@305 192 inRange = false;
Chris@305 193 }
Chris@305 194 }
Chris@305 195
Chris@305 196 PointType p(*i);
Chris@305 197
Chris@305 198 if (!inRange) {
Chris@305 199 prevPoint = p;
Chris@305 200 continue;
Chris@305 201 }
Chris@305 202
Chris@305 203 if (actingOnPrevPoint()) {
Chris@305 204 if (i != pl.begin()) {
Chris@305 205 command->deletePoint(prevPoint);
Chris@305 206 label<PointType>(p, &prevPoint);
Chris@305 207 command->addPoint(prevPoint);
Chris@305 208 }
Chris@305 209 } else {
Chris@305 210 command->deletePoint(p);
Chris@305 211 label<PointType>(p, &prevPoint);
Chris@305 212 command->addPoint(p);
Chris@305 213 }
Chris@305 214
Chris@305 215 prevPoint = p;
Chris@305 216 }
Chris@305 217
Chris@305 218 command->finish();
Chris@305 219 }
Chris@305 220
Chris@305 221 template <typename PointType>
Chris@305 222 void setValue(PointType &newPoint, PointType *prevPoint = 0) {
Chris@305 223 if (m_type == ValueFromExistingNeighbour) {
Chris@305 224 if (!prevPoint) {
Chris@305 225 std::cerr << "ERROR: Labeller::setValue: Previous point required but not provided" << std::endl;
Chris@305 226 } else {
Chris@305 227 newPoint.value = prevPoint->value;
Chris@305 228 }
Chris@305 229 } else {
Chris@305 230 float value = getValueFor<PointType>(newPoint, prevPoint);
Chris@305 231 if (actingOnPrevPoint() && prevPoint) {
Chris@305 232 prevPoint->value = value;
Chris@305 233 } else {
Chris@305 234 newPoint.value = value;
Chris@305 235 }
Chris@305 236 }
Chris@305 237 }
Chris@305 238
Chris@355 239 bool requiresPrevPoint() const {
Chris@355 240 return (m_type == ValueFromDurationFromPrevious ||
Chris@355 241 m_type == ValueFromDurationToNext ||
Chris@355 242 m_type == ValueFromTempoFromPrevious ||
Chris@355 243 m_type == ValueFromDurationToNext);
Chris@355 244 }
Chris@355 245
Chris@305 246 bool actingOnPrevPoint() const {
Chris@355 247 return (m_type == ValueFromDurationToNext ||
Chris@355 248 m_type == ValueFromTempoToNext);
Chris@305 249 }
Chris@305 250
Chris@305 251 protected:
Chris@305 252 template <typename PointType>
Chris@305 253 float getValueFor(PointType &newPoint, PointType *prevPoint)
Chris@305 254 {
Chris@305 255 float value = 0.f;
Chris@305 256
Chris@305 257 switch (m_type) {
Chris@305 258
Chris@305 259 case ValueNone:
Chris@305 260 value = 0;
Chris@305 261 break;
Chris@305 262
Chris@305 263 case ValueFromSimpleCounter:
Chris@305 264 case ValueFromCyclicalCounter:
Chris@305 265 value = m_counter;
Chris@305 266 incrementCounter();
Chris@305 267 break;
Chris@305 268
Chris@305 269 case ValueFromTwoLevelCounter:
Chris@305 270 value = m_counter2 + double(m_counter) / double(m_dp);
Chris@305 271 incrementCounter();
Chris@305 272 break;
Chris@305 273
Chris@305 274 case ValueFromFrameNumber:
Chris@305 275 value = newPoint.frame;
Chris@305 276 break;
Chris@305 277
Chris@305 278 case ValueFromRealTime:
Chris@305 279 if (m_rate == 0.f) {
Chris@305 280 std::cerr << "ERROR: Labeller::getValueFor: Real-time conversion required, but no sample rate set" << std::endl;
Chris@305 281 } else {
Chris@305 282 value = float(newPoint.frame) / float(m_rate);
Chris@305 283 }
Chris@305 284 break;
Chris@305 285
Chris@355 286 case ValueFromDurationToNext:
Chris@355 287 case ValueFromTempoToNext:
Chris@355 288 case ValueFromDurationFromPrevious:
Chris@355 289 case ValueFromTempoFromPrevious:
Chris@305 290 if (m_rate == 0.f) {
Chris@305 291 std::cerr << "ERROR: Labeller::getValueFor: Real-time conversion required, but no sample rate set" << std::endl;
Chris@305 292 } else if (!prevPoint) {
Chris@305 293 std::cerr << "ERROR: Labeller::getValueFor: Time difference required, but only one point provided" << std::endl;
Chris@305 294 } else {
Chris@929 295 int f0 = prevPoint->frame, f1 = newPoint.frame;
Chris@355 296 if (m_type == ValueFromDurationToNext ||
Chris@355 297 m_type == ValueFromDurationFromPrevious) {
Chris@305 298 value = float(f1 - f0) / m_rate;
Chris@305 299 } else {
Chris@305 300 if (f1 > f0) {
Chris@305 301 value = (60.f * m_rate) / (f1 - f0);
Chris@305 302 }
Chris@305 303 }
Chris@305 304 }
Chris@305 305 break;
Chris@305 306
Chris@305 307 case ValueFromExistingNeighbour:
Chris@305 308 // need to deal with this in the calling function, as this
Chris@305 309 // function must handle points that don't have values to
Chris@305 310 // read from
Chris@305 311 break;
Chris@305 312
Chris@305 313 case ValueFromLabel:
Chris@305 314 if (newPoint.label != "") {
Chris@305 315 // more forgiving than QString::toFloat()
Chris@305 316 value = atof(newPoint.label.toLocal8Bit());
Chris@305 317 } else {
Chris@305 318 value = 0.f;
Chris@305 319 }
Chris@305 320 break;
Chris@305 321 }
Chris@305 322
Chris@305 323 return value;
Chris@305 324 }
Chris@305 325
Chris@305 326 ValueType m_type;
Chris@305 327 int m_counter;
Chris@305 328 int m_counter2;
Chris@305 329 int m_cycle;
Chris@305 330 int m_dp;
Chris@305 331 float m_rate;
Chris@305 332 };
Chris@305 333
Chris@305 334 #endif