annotate data/model/Labeller.h @ 383:94fc0591ea43 1.2-stable

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