annotate data/model/Labeller.h @ 308:14e0f60435b8

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