annotate data/model/Labeller.h @ 458:f60360209e5c

* Fix race condition in FFTFileCache when reading from the same FFT model from multiple threads (e.g. when applying more than one plugin at once)
author Chris Cannam
date Wed, 15 Oct 2008 12:08:02 +0000
parents 6a96bff0bd59
children 2f85f0fbf7ab
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@305 137 void incrementCounter() {
Chris@305 138 m_counter++;
Chris@305 139 if (m_type == ValueFromCyclicalCounter ||
Chris@305 140 m_type == ValueFromTwoLevelCounter) {
Chris@305 141 if (m_counter > m_cycle) {
Chris@305 142 m_counter = 1;
Chris@305 143 m_counter2++;
Chris@305 144 }
Chris@305 145 }
Chris@305 146 }
Chris@305 147
Chris@305 148 template <typename PointType>
Chris@305 149 void label(PointType &newPoint, PointType *prevPoint = 0) {
Chris@305 150 if (m_type == ValueNone) {
Chris@305 151 newPoint.label = "";
Chris@305 152 } else if (m_type == ValueFromTwoLevelCounter) {
Chris@305 153 newPoint.label = tr("%1.%2").arg(m_counter2).arg(m_counter);
Chris@305 154 incrementCounter();
Chris@306 155 } else if (m_type == ValueFromFrameNumber) {
Chris@306 156 // avoid going through floating-point value
Chris@306 157 newPoint.label = tr("%1").arg(newPoint.frame);
Chris@305 158 } else {
Chris@305 159 float value = getValueFor<PointType>(newPoint, prevPoint);
Chris@305 160 if (actingOnPrevPoint() && prevPoint) {
Chris@305 161 prevPoint->label = QString("%1").arg(value);
Chris@305 162 } else {
Chris@305 163 newPoint.label = QString("%1").arg(value);
Chris@305 164 }
Chris@305 165 }
Chris@305 166 }
Chris@305 167
Chris@305 168 template <typename PointType>
Chris@305 169 void labelAll(SparseModel<PointType> &model, MultiSelection *ms) {
Chris@305 170
Chris@305 171 typename SparseModel<PointType>::PointList::iterator i;
Chris@305 172 typename SparseModel<PointType>::PointList pl(model.getPoints());
Chris@305 173
Chris@305 174 typename SparseModel<PointType>::EditCommand *command =
Chris@305 175 new typename SparseModel<PointType>::EditCommand
Chris@305 176 (&model, tr("Label Points"));
Chris@305 177
Chris@305 178 PointType prevPoint(0);
Chris@305 179
Chris@305 180 for (i = pl.begin(); i != pl.end(); ++i) {
Chris@305 181
Chris@305 182 bool inRange = true;
Chris@305 183 if (ms) {
Chris@305 184 Selection s(ms->getContainingSelection(i->frame, false));
Chris@305 185 if (s.isEmpty() || !s.contains(i->frame)) {
Chris@305 186 inRange = false;
Chris@305 187 }
Chris@305 188 }
Chris@305 189
Chris@305 190 PointType p(*i);
Chris@305 191
Chris@305 192 if (!inRange) {
Chris@305 193 prevPoint = p;
Chris@305 194 continue;
Chris@305 195 }
Chris@305 196
Chris@305 197 if (actingOnPrevPoint()) {
Chris@305 198 if (i != pl.begin()) {
Chris@305 199 command->deletePoint(prevPoint);
Chris@305 200 label<PointType>(p, &prevPoint);
Chris@305 201 command->addPoint(prevPoint);
Chris@305 202 }
Chris@305 203 } else {
Chris@305 204 command->deletePoint(p);
Chris@305 205 label<PointType>(p, &prevPoint);
Chris@305 206 command->addPoint(p);
Chris@305 207 }
Chris@305 208
Chris@305 209 prevPoint = p;
Chris@305 210 }
Chris@305 211
Chris@305 212 command->finish();
Chris@305 213 }
Chris@305 214
Chris@305 215 template <typename PointType>
Chris@305 216 void setValue(PointType &newPoint, PointType *prevPoint = 0) {
Chris@305 217 if (m_type == ValueFromExistingNeighbour) {
Chris@305 218 if (!prevPoint) {
Chris@305 219 std::cerr << "ERROR: Labeller::setValue: Previous point required but not provided" << std::endl;
Chris@305 220 } else {
Chris@305 221 newPoint.value = prevPoint->value;
Chris@305 222 }
Chris@305 223 } else {
Chris@305 224 float value = getValueFor<PointType>(newPoint, prevPoint);
Chris@305 225 if (actingOnPrevPoint() && prevPoint) {
Chris@305 226 prevPoint->value = value;
Chris@305 227 } else {
Chris@305 228 newPoint.value = value;
Chris@305 229 }
Chris@305 230 }
Chris@305 231 }
Chris@305 232
Chris@355 233 bool requiresPrevPoint() const {
Chris@355 234 return (m_type == ValueFromDurationFromPrevious ||
Chris@355 235 m_type == ValueFromDurationToNext ||
Chris@355 236 m_type == ValueFromTempoFromPrevious ||
Chris@355 237 m_type == ValueFromDurationToNext);
Chris@355 238 }
Chris@355 239
Chris@305 240 bool actingOnPrevPoint() const {
Chris@355 241 return (m_type == ValueFromDurationToNext ||
Chris@355 242 m_type == ValueFromTempoToNext);
Chris@305 243 }
Chris@305 244
Chris@305 245 protected:
Chris@305 246 template <typename PointType>
Chris@305 247 float getValueFor(PointType &newPoint, PointType *prevPoint)
Chris@305 248 {
Chris@305 249 float value = 0.f;
Chris@305 250
Chris@305 251 switch (m_type) {
Chris@305 252
Chris@305 253 case ValueNone:
Chris@305 254 value = 0;
Chris@305 255 break;
Chris@305 256
Chris@305 257 case ValueFromSimpleCounter:
Chris@305 258 case ValueFromCyclicalCounter:
Chris@305 259 value = m_counter;
Chris@305 260 incrementCounter();
Chris@305 261 break;
Chris@305 262
Chris@305 263 case ValueFromTwoLevelCounter:
Chris@305 264 value = m_counter2 + double(m_counter) / double(m_dp);
Chris@305 265 incrementCounter();
Chris@305 266 break;
Chris@305 267
Chris@305 268 case ValueFromFrameNumber:
Chris@305 269 value = newPoint.frame;
Chris@305 270 break;
Chris@305 271
Chris@305 272 case ValueFromRealTime:
Chris@305 273 if (m_rate == 0.f) {
Chris@305 274 std::cerr << "ERROR: Labeller::getValueFor: Real-time conversion required, but no sample rate set" << std::endl;
Chris@305 275 } else {
Chris@305 276 value = float(newPoint.frame) / float(m_rate);
Chris@305 277 }
Chris@305 278 break;
Chris@305 279
Chris@355 280 case ValueFromDurationToNext:
Chris@355 281 case ValueFromTempoToNext:
Chris@355 282 case ValueFromDurationFromPrevious:
Chris@355 283 case ValueFromTempoFromPrevious:
Chris@305 284 if (m_rate == 0.f) {
Chris@305 285 std::cerr << "ERROR: Labeller::getValueFor: Real-time conversion required, but no sample rate set" << std::endl;
Chris@305 286 } else if (!prevPoint) {
Chris@305 287 std::cerr << "ERROR: Labeller::getValueFor: Time difference required, but only one point provided" << std::endl;
Chris@305 288 } else {
Chris@305 289 size_t f0 = prevPoint->frame, f1 = newPoint.frame;
Chris@355 290 if (m_type == ValueFromDurationToNext ||
Chris@355 291 m_type == ValueFromDurationFromPrevious) {
Chris@305 292 value = float(f1 - f0) / m_rate;
Chris@305 293 } else {
Chris@305 294 if (f1 > f0) {
Chris@305 295 value = (60.f * m_rate) / (f1 - f0);
Chris@305 296 }
Chris@305 297 }
Chris@305 298 }
Chris@305 299 break;
Chris@305 300
Chris@305 301 case ValueFromExistingNeighbour:
Chris@305 302 // need to deal with this in the calling function, as this
Chris@305 303 // function must handle points that don't have values to
Chris@305 304 // read from
Chris@305 305 break;
Chris@305 306
Chris@305 307 case ValueFromLabel:
Chris@305 308 if (newPoint.label != "") {
Chris@305 309 // more forgiving than QString::toFloat()
Chris@305 310 value = atof(newPoint.label.toLocal8Bit());
Chris@305 311 } else {
Chris@305 312 value = 0.f;
Chris@305 313 }
Chris@305 314 break;
Chris@305 315 }
Chris@305 316
Chris@305 317 return value;
Chris@305 318 }
Chris@305 319
Chris@305 320 ValueType m_type;
Chris@305 321 int m_counter;
Chris@305 322 int m_counter2;
Chris@305 323 int m_cycle;
Chris@305 324 int m_dp;
Chris@305 325 float m_rate;
Chris@305 326 };
Chris@305 327
Chris@305 328 #endif