comparison data/model/Labeller.h @ 305:fc656505c573

* Add labelling option for instants inserted through tapping (closes FR#1674184) Needs some refinement still, but it's almost functionally complete
author Chris Cannam
date Mon, 08 Oct 2007 14:44:38 +0000
parents
children 4669894a89ad
comparison
equal deleted inserted replaced
304:4fc6f49436b3 305:fc656505c573
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2
3 /*
4 Sonic Visualiser
5 An audio file viewer and annotation editor.
6 Centre for Digital Music, Queen Mary, University of London.
7 This file copyright 2006-2007 Chris Cannam and QMUL.
8
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License as
11 published by the Free Software Foundation; either version 2 of the
12 License, or (at your option) any later version. See the file
13 COPYING included with this distribution for more information.
14 */
15
16 #ifndef _LABELLER_H_
17 #define _LABELLER_H_
18
19 #include "SparseModel.h"
20 #include "SparseValueModel.h"
21
22 #include "base/Selection.h"
23
24 #include <QObject>
25
26 #include <map>
27 #include <iostream>
28
29 class Labeller : public QObject
30 {
31 public:
32 enum ValueType {
33 ValueNone,
34 ValueFromSimpleCounter,
35 ValueFromCyclicalCounter,
36 ValueFromTwoLevelCounter,
37 ValueFromFrameNumber,
38 ValueFromRealTime,
39 ValueFromRealTimeDifference,
40 ValueFromTempo,
41 ValueFromExistingNeighbour,
42 ValueFromLabel
43 };
44
45 // uses:
46 //
47 // 1. when adding points to a time-value model, generate values
48 // for those points based on their times or labels or a counter
49 //
50 // 2. when adding a single point to a time-instant model, generate
51 // a label for it based on its time and that of the previous point
52 // or a counter
53 //
54 // 3. when adding a single point to a time-instant model, generate
55 // a label for the previous point based on its time and that of
56 // the point just added (as tempo is based on time to the next
57 // point, not the previous one)
58 //
59 // 4. re-label a set of points that have already been added to a
60 // model
61
62 Labeller(ValueType type) :
63 m_type(type),
64 m_counter(1),
65 m_counter2(1),
66 m_cycle(4),
67 m_dp(10),
68 m_rate(0) { }
69
70 Labeller(const Labeller &l) :
71 QObject(),
72 m_type(l.m_type),
73 m_counter(1),
74 m_counter2(1),
75 m_cycle(l.m_cycle),
76 m_dp(l.m_dp),
77 m_rate(l.m_rate) { }
78
79 virtual ~Labeller() { }
80
81 typedef std::map<ValueType, QString> TypeNameMap;
82 TypeNameMap getTypeNames() const {
83 TypeNameMap m;
84 m[ValueNone] = tr("No numbering");
85 m[ValueFromSimpleCounter] = tr("Simple counter starting from 1");
86 m[ValueFromCyclicalCounter] = tr("Cyclical counter starting from 1");
87 m[ValueFromTwoLevelCounter] = tr("Cyclical two-level counter (bar/beat)");
88 m[ValueFromFrameNumber] = tr("Audio sample frame number");
89 m[ValueFromRealTime] = tr("Time in seconds");
90 m[ValueFromRealTimeDifference] = tr("Duration to the following item");
91 m[ValueFromTempo] = tr("Tempo (bpm) based on duration to following item");
92 m[ValueFromExistingNeighbour] = tr("Same value as the nearest previous item");
93 m[ValueFromLabel] = tr("Value extracted from the item's label (where possible)");
94 return m;
95 }
96
97 ValueType getType() const { return m_type; }
98 void setType(ValueType type) { m_type = type; }
99
100 int getCounterValue() const { return m_counter; }
101 void setCounterValue(int v) { m_counter = v; }
102
103 int getSecondLevelCounterValue() const { return m_counter2; }
104 void setSecondLevelCounterValue(int v) { m_counter2 = v; }
105
106 int getCounterCycleSize() const { return m_cycle; }
107 void setCounterCycleSize(int s) {
108 m_cycle = s;
109 m_dp = 1;
110 while (s > 0) {
111 s /= 10;
112 m_dp *= 10;
113 }
114 }
115
116 void setSampleRate(float rate) { m_rate = rate; }
117
118 void incrementCounter() {
119 m_counter++;
120 if (m_type == ValueFromCyclicalCounter ||
121 m_type == ValueFromTwoLevelCounter) {
122 if (m_counter > m_cycle) {
123 m_counter = 1;
124 m_counter2++;
125 }
126 }
127 }
128
129 template <typename PointType>
130 void label(PointType &newPoint, PointType *prevPoint = 0) {
131 if (m_type == ValueNone) {
132 newPoint.label = "";
133 } else if (m_type == ValueFromTwoLevelCounter) {
134 newPoint.label = tr("%1.%2").arg(m_counter2).arg(m_counter);
135 incrementCounter();
136 } else {
137 float value = getValueFor<PointType>(newPoint, prevPoint);
138 if (actingOnPrevPoint() && prevPoint) {
139 prevPoint->label = QString("%1").arg(value);
140 } else {
141 newPoint.label = QString("%1").arg(value);
142 }
143 }
144 }
145
146 template <typename PointType>
147 void labelAll(SparseModel<PointType> &model, MultiSelection *ms) {
148
149 typename SparseModel<PointType>::PointList::iterator i;
150 typename SparseModel<PointType>::PointList pl(model.getPoints());
151
152 typename SparseModel<PointType>::EditCommand *command =
153 new typename SparseModel<PointType>::EditCommand
154 (&model, tr("Label Points"));
155
156 PointType prevPoint(0);
157
158 for (i = pl.begin(); i != pl.end(); ++i) {
159
160 bool inRange = true;
161 if (ms) {
162 Selection s(ms->getContainingSelection(i->frame, false));
163 if (s.isEmpty() || !s.contains(i->frame)) {
164 inRange = false;
165 }
166 }
167
168 PointType p(*i);
169
170 if (!inRange) {
171 prevPoint = p;
172 continue;
173 }
174
175 if (actingOnPrevPoint()) {
176 if (i != pl.begin()) {
177 command->deletePoint(prevPoint);
178 label<PointType>(p, &prevPoint);
179 command->addPoint(prevPoint);
180 }
181 } else {
182 command->deletePoint(p);
183 label<PointType>(p, &prevPoint);
184 command->addPoint(p);
185 }
186
187 prevPoint = p;
188 }
189
190 command->finish();
191 }
192
193 template <typename PointType>
194 void setValue(PointType &newPoint, PointType *prevPoint = 0) {
195 if (m_type == ValueFromExistingNeighbour) {
196 if (!prevPoint) {
197 std::cerr << "ERROR: Labeller::setValue: Previous point required but not provided" << std::endl;
198 } else {
199 newPoint.value = prevPoint->value;
200 }
201 } else {
202 float value = getValueFor<PointType>(newPoint, prevPoint);
203 if (actingOnPrevPoint() && prevPoint) {
204 prevPoint->value = value;
205 } else {
206 newPoint.value = value;
207 }
208 }
209 }
210
211 bool actingOnPrevPoint() const {
212 return (m_type == ValueFromRealTimeDifference ||
213 m_type == ValueFromTempo);
214 }
215
216 protected:
217 template <typename PointType>
218 float getValueFor(PointType &newPoint, PointType *prevPoint)
219 {
220 float value = 0.f;
221
222 switch (m_type) {
223
224 case ValueNone:
225 value = 0;
226 break;
227
228 case ValueFromSimpleCounter:
229 case ValueFromCyclicalCounter:
230 value = m_counter;
231 incrementCounter();
232 break;
233
234 case ValueFromTwoLevelCounter:
235 value = m_counter2 + double(m_counter) / double(m_dp);
236 incrementCounter();
237 break;
238
239 case ValueFromFrameNumber:
240 value = newPoint.frame;
241 break;
242
243 case ValueFromRealTime:
244 if (m_rate == 0.f) {
245 std::cerr << "ERROR: Labeller::getValueFor: Real-time conversion required, but no sample rate set" << std::endl;
246 } else {
247 value = float(newPoint.frame) / float(m_rate);
248 }
249 break;
250
251 case ValueFromRealTimeDifference:
252 case ValueFromTempo:
253 if (m_rate == 0.f) {
254 std::cerr << "ERROR: Labeller::getValueFor: Real-time conversion required, but no sample rate set" << std::endl;
255 } else if (!prevPoint) {
256 std::cerr << "ERROR: Labeller::getValueFor: Time difference required, but only one point provided" << std::endl;
257 } else {
258 size_t f0 = prevPoint->frame, f1 = newPoint.frame;
259 if (m_type == ValueFromRealTimeDifference) {
260 value = float(f1 - f0) / m_rate;
261 } else {
262 if (f1 > f0) {
263 value = (60.f * m_rate) / (f1 - f0);
264 }
265 }
266 }
267 break;
268
269 case ValueFromExistingNeighbour:
270 // need to deal with this in the calling function, as this
271 // function must handle points that don't have values to
272 // read from
273 break;
274
275 case ValueFromLabel:
276 if (newPoint.label != "") {
277 // more forgiving than QString::toFloat()
278 value = atof(newPoint.label.toLocal8Bit());
279 } else {
280 value = 0.f;
281 }
282 break;
283 }
284
285 return value;
286 }
287
288 ValueType m_type;
289 int m_counter;
290 int m_counter2;
291 int m_cycle;
292 int m_dp;
293 float m_rate;
294 };
295
296 #endif