annotate data/model/Labeller.h @ 392:183ee2a55fc7

* More work to abstract out interactive components used in the data library, so that it does not need to depend on QtGui.
author Chris Cannam
date Fri, 14 Mar 2008 17:14:21 +0000
parents bc4712c7d269
children 6a96bff0bd59
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@355 39 ValueFromDurationFromPrevious,
Chris@355 40 ValueFromDurationToNext,
Chris@355 41 ValueFromTempoFromPrevious,
Chris@355 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@355 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@380 75 m_counter(l.m_counter),
Chris@380 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@355 86 m[ValueNone]
Chris@355 87 = tr("No numbering");
Chris@355 88 m[ValueFromSimpleCounter]
Chris@355 89 = tr("Simple counter");
Chris@355 90 m[ValueFromCyclicalCounter]
Chris@355 91 = tr("Cyclical counter");
Chris@355 92 m[ValueFromTwoLevelCounter]
Chris@355 93 = tr("Cyclical two-level counter (bar/beat)");
Chris@355 94 m[ValueFromFrameNumber]
Chris@355 95 = tr("Audio sample frame number");
Chris@355 96 m[ValueFromRealTime]
Chris@355 97 = tr("Time in seconds");
Chris@355 98 m[ValueFromDurationToNext]
Chris@355 99 = tr("Duration to the following item");
Chris@355 100 m[ValueFromTempoToNext]
Chris@355 101 = tr("Tempo (bpm) based on duration to following item");
Chris@355 102 m[ValueFromDurationFromPrevious]
Chris@355 103 = tr("Duration since the previous item");
Chris@355 104 m[ValueFromTempoFromPrevious]
Chris@355 105 = tr("Tempo (bpm) based on duration since previous item");
Chris@355 106 m[ValueFromExistingNeighbour]
Chris@355 107 = tr("Same as the nearest previous item");
Chris@355 108 m[ValueFromLabel]
Chris@355 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@355 231 bool requiresPrevPoint() const {
Chris@355 232 return (m_type == ValueFromDurationFromPrevious ||
Chris@355 233 m_type == ValueFromDurationToNext ||
Chris@355 234 m_type == ValueFromTempoFromPrevious ||
Chris@355 235 m_type == ValueFromDurationToNext);
Chris@355 236 }
Chris@355 237
Chris@305 238 bool actingOnPrevPoint() const {
Chris@355 239 return (m_type == ValueFromDurationToNext ||
Chris@355 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@355 278 case ValueFromDurationToNext:
Chris@355 279 case ValueFromTempoToNext:
Chris@355 280 case ValueFromDurationFromPrevious:
Chris@355 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@355 288 if (m_type == ValueFromDurationToNext ||
Chris@355 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