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
|