comparison data/model/Labeller.h @ 1652:08bed13d3a26 single-point

Update Labeller to new event API + fewer magical side-effects
author Chris Cannam
date Tue, 19 Mar 2019 13:05:56 +0000
parents ad5f892c0c4d
children 9d82b164f264
comparison
equal deleted inserted replaced
1651:7a56bb85030f 1652:08bed13d3a26
14 */ 14 */
15 15
16 #ifndef SV_LABELLER_H 16 #ifndef SV_LABELLER_H
17 #define SV_LABELLER_H 17 #define SV_LABELLER_H
18 18
19 #include "SparseModel.h"
20 #include "SparseValueModel.h"
21
22 #include "base/Selection.h" 19 #include "base/Selection.h"
20 #include "base/Event.h"
21
22 #include "EventCommands.h"
23 23
24 #include <QObject> 24 #include <QObject>
25 25
26 #include <map> 26 #include <map>
27 #include <iostream> 27 #include <iostream>
60 // the point just added (as tempo is based on time to the next 60 // the point just added (as tempo is based on time to the next
61 // point, not the previous one) 61 // point, not the previous one)
62 // 62 //
63 // 4. re-label a set of points that have already been added to a 63 // 4. re-label a set of points that have already been added to a
64 // model 64 // model
65 //
66 // 5. generate new labelled points in the gaps between other
67 // points (subdivide), or remove them (winnow)
65 68
66 Labeller(ValueType type = ValueNone) : 69 Labeller(ValueType type = ValueNone) :
67 m_type(type), 70 m_type(type),
68 m_counter(1), 71 m_counter(1),
69 m_counter2(1), 72 m_counter2(1),
149 m_counter2++; 152 m_counter2++;
150 } 153 }
151 } 154 }
152 } 155 }
153 156
154 template <typename PointType> 157 enum Application {
155 void label(PointType &newPoint, PointType *prevPoint = 0) { 158 AppliesToThisEvent,
159 AppliesToPreviousEvent
160 };
161 typedef std::pair<Application, Event> Relabelling;
162 typedef std::pair<Application, Event> Revaluing;
163
164 /**
165 * Return a labelled event based on the given event, previous
166 * event if supplied, and internal labeller state. The returned
167 * event replaces either the event provided or the previous event,
168 * depending on the Application value in the returned pair.
169 */
170 Relabelling
171 label(Event e, const Event *prev = nullptr) {
172
173 QString label = e.getLabel();
174
156 if (m_type == ValueNone) { 175 if (m_type == ValueNone) {
157 newPoint.label = ""; 176 label = "";
158 } else if (m_type == ValueFromTwoLevelCounter) { 177 } else if (m_type == ValueFromTwoLevelCounter) {
159 newPoint.label = tr("%1.%2").arg(m_counter2).arg(m_counter); 178 label = tr("%1.%2").arg(m_counter2).arg(m_counter);
160 incrementCounter(); 179 incrementCounter();
161 } else if (m_type == ValueFromFrameNumber) { 180 } else if (m_type == ValueFromFrameNumber) {
162 // avoid going through floating-point value 181 // avoid going through floating-point value
163 newPoint.label = tr("%1").arg(newPoint.frame); 182 label = tr("%1").arg(e.getFrame());
164 } else { 183 } else {
165 float value = getValueFor<PointType>(newPoint, prevPoint); 184 float value = getValueFor(e, prev);
166 if (actingOnPrevPoint() && prevPoint) { 185 label = QString("%1").arg(value);
167 prevPoint->label = QString("%1").arg(value); 186 }
187
188 if (actingOnPrevEvent() && prev) {
189 return { AppliesToPreviousEvent, prev->withLabel(label) };
190 } else {
191 return { AppliesToThisEvent, e.withLabel(label) };
192 }
193 }
194
195 /**
196 * Return an event with a value following the labelling scheme,
197 * based on the given event, previous event if supplied, and
198 * internal labeller state. The returned event replaces either the
199 * event provided or the previous event, depending on the
200 * Application value in the returned pair.
201 */
202 Revaluing
203 revalue(Event e, const Event *prev = nullptr) {
204
205 float value = e.getValue();
206
207 if (m_type == ValueFromExistingNeighbour) {
208 if (!prev) {
209 std::cerr << "ERROR: Labeller::setValue: Previous point required but not provided" << std::endl;
168 } else { 210 } else {
169 newPoint.label = QString("%1").arg(value); 211 return { AppliesToThisEvent, e.withValue(prev->getValue()) };
170 } 212 }
171 } 213 } else {
172 } 214 value = getValueFor(e, prev);
173 215 }
216
217 if (actingOnPrevEvent() && prev) {
218 return { AppliesToPreviousEvent, prev->withValue(value) };
219 } else {
220 return { AppliesToThisEvent, e.withValue(value) };
221 }
222 }
223
174 /** 224 /**
175 * Relabel all points in the given model that lie within the given 225 * Relabel all events in the given event vector that lie within
176 * multi-selection, according to the labelling properties of this 226 * the given multi-selection, according to the labelling
177 * labeller. Return a command that has been executed but not yet 227 * properties of this labeller. Return a command that has been
228 * executed but not yet added to the history.
229 */
230 Command *labelAll(EventEditable *editable,
231 MultiSelection *ms,
232 const EventVector &allEvents) {
233
234 ChangeEventsCommand *command = new ChangeEventsCommand
235 (editable, tr("Label Points"));
236
237 Event prev;
238 bool havePrev = false;
239
240 for (auto p: allEvents) {
241
242 if (ms) {
243 Selection s(ms->getContainingSelection(p.getFrame(), false));
244 if (!s.contains(p.getFrame())) {
245 prev = p;
246 havePrev = true;
247 continue;
248 }
249 }
250
251 auto labelling = label(p, havePrev ? &prev : nullptr);
252
253 if (labelling.first == AppliesToThisEvent) {
254 command->remove(p);
255 } else {
256 command->remove(prev);
257 }
258
259 command->add(labelling.second);
260
261 prev = p;
262 havePrev = true;
263 }
264
265 return command->finish();
266 }
267
268 /**
269 * For each event in the given event vector (except the last), if
270 * that event lies within the given multi-selection, add n-1 new
271 * events at equally spaced intervals between it and the following
272 * event. Return a command that has been executed but not yet
178 * added to the history. 273 * added to the history.
179 */ 274 */
180 template <typename PointType> 275 Command *subdivide(EventEditable *editable,
181 Command *labelAll(SparseModel<PointType> &model, MultiSelection *ms) { 276 MultiSelection *ms,
182 277 const EventVector &allEvents,
183 auto points(model.getPoints()); 278 int n) {
184 auto command = new typename SparseModel<PointType>::EditCommand 279
185 (&model, tr("Label Points")); 280 ChangeEventsCommand *command = new ChangeEventsCommand
186 281 (editable, tr("Subdivide Points"));
187 PointType prevPoint(0); 282
188 bool havePrevPoint(false); 283 for (auto i = allEvents.begin(); i != allEvents.end(); ++i) {
189 284
190 for (auto p: points) { 285 auto j = i;
286 // require a "next point" even if it's not in selection
287 if (++j == allEvents.end()) {
288 break;
289 }
191 290
192 if (ms) { 291 if (ms) {
193 Selection s(ms->getContainingSelection(p.frame, false)); 292 Selection s(ms->getContainingSelection(i->getFrame(), false));
194 if (!s.contains(p.frame)) { 293 if (!s.contains(i->getFrame())) {
195 prevPoint = p;
196 havePrevPoint = true;
197 continue; 294 continue;
198 } 295 }
199 } 296 }
200 297
201 if (actingOnPrevPoint()) { 298 Event p(*i);
202 if (havePrevPoint) { 299 Event nextP(*j);
203 command->deletePoint(prevPoint); 300
204 label<PointType>(p, &prevPoint); 301 // n is the number of subdivisions, so we add n-1 new
205 command->addPoint(prevPoint); 302 // points equally spaced between p and nextP
206 } 303
207 } else { 304 for (int m = 1; m < n; ++m) {
208 command->deletePoint(p); 305 sv_frame_t f = p.getFrame() +
209 label<PointType>(p, &prevPoint); 306 (m * (nextP.getFrame() - p.getFrame())) / n;
210 command->addPoint(p); 307 Event newPoint = p
211 } 308 .withFrame(f)
212 309 .withLabel(tr("%1.%2").arg(p.getLabel()).arg(m+1));
213 prevPoint = p; 310 command->add(newPoint);
214 havePrevPoint = true; 311 }
215 } 312 }
216 313
217 return command->finish(); 314 return command->finish();
218 } 315 }
219 316
220 /** 317 /**
221 * For each point in the given model (except the last), if that 318 * The opposite of subdivide. Given an event vector, a
222 * point lies within the given multi-selection, add n-1 new points 319 * multi-selection, and a number n, remove all but every nth event
223 * at equally spaced intervals between it and the following point. 320 * from the vector within the extents of the multi-selection.
224 * Return a command that has been executed but not yet added to 321 * Return a command that has been executed but not yet added to
225 * the history. 322 * the history.
226 */ 323 */
227 template <typename PointType> 324 Command *winnow(EventEditable *editable,
228 Command *subdivide(SparseModel<PointType> &model, MultiSelection *ms, int n) { 325 MultiSelection *ms,
229 326 const EventVector &allEvents,
230 auto points(model.getPoints()); 327 int n) {
231 auto command = new typename SparseModel<PointType>::EditCommand 328
232 (&model, tr("Subdivide Points")); 329 ChangeEventsCommand *command = new ChangeEventsCommand
233 330 (editable, tr("Winnow Points"));
234 for (auto i = points.begin(); i != points.end(); ++i) {
235
236 auto j = i;
237 // require a "next point" even if it's not in selection
238 if (++j == points.end()) {
239 break;
240 }
241
242 if (ms) {
243 Selection s(ms->getContainingSelection(i->frame, false));
244 if (!s.contains(i->frame)) {
245 continue;
246 }
247 }
248
249 PointType p(*i);
250 PointType nextP(*j);
251
252 // n is the number of subdivisions, so we add n-1 new
253 // points equally spaced between p and nextP
254
255 for (int m = 1; m < n; ++m) {
256 sv_frame_t f = p.frame + (m * (nextP.frame - p.frame)) / n;
257 PointType newPoint(p);
258 newPoint.frame = f;
259 newPoint.label = tr("%1.%2").arg(p.label).arg(m+1);
260 command->addPoint(newPoint);
261 }
262 }
263
264 return command->finish();
265 }
266
267 /**
268 * Return a command that has been executed but not yet added to
269 * the history.
270 */
271 template <typename PointType>
272 Command *winnow(SparseModel<PointType> &model, MultiSelection *ms, int n) {
273
274 auto points(model.getPoints());
275 auto command = new typename SparseModel<PointType>::EditCommand
276 (&model, tr("Winnow Points"));
277 331
278 int counter = 0; 332 int counter = 0;
279 333
280 for (auto p: points) { 334 for (auto p: allEvents) {
281 335
282 if (ms) { 336 if (ms) {
283 Selection s(ms->getContainingSelection(p.frame, false)); 337 Selection s(ms->getContainingSelection(p.getFrame(), false));
284 if (!s.contains(p.frame)) { 338 if (!s.contains(p.getFrame())) {
285 counter = 0; 339 counter = 0;
286 continue; 340 continue;
287 } 341 }
288 } 342 }
289 343
293 if (counter == 1) { 347 if (counter == 1) {
294 // this is an Nth instant, don't remove it 348 // this is an Nth instant, don't remove it
295 continue; 349 continue;
296 } 350 }
297 351
298 command->deletePoint(p); 352 command->remove(p);
299 } 353 }
300 354
301 return command->finish(); 355 return command->finish();
302 }
303
304 template <typename PointType>
305 void setValue(PointType &newPoint, PointType *prevPoint = 0) {
306 if (m_type == ValueFromExistingNeighbour) {
307 if (!prevPoint) {
308 std::cerr << "ERROR: Labeller::setValue: Previous point required but not provided" << std::endl;
309 } else {
310 newPoint.value = prevPoint->value;
311 }
312 } else {
313 float value = getValueFor<PointType>(newPoint, prevPoint);
314 if (actingOnPrevPoint() && prevPoint) {
315 prevPoint->value = value;
316 } else {
317 newPoint.value = value;
318 }
319 }
320 } 356 }
321 357
322 bool requiresPrevPoint() const { 358 bool requiresPrevPoint() const {
323 return (m_type == ValueFromDurationFromPrevious || 359 return (m_type == ValueFromDurationFromPrevious ||
324 m_type == ValueFromDurationToNext || 360 m_type == ValueFromDurationToNext ||
325 m_type == ValueFromTempoFromPrevious || 361 m_type == ValueFromTempoFromPrevious ||
326 m_type == ValueFromDurationToNext); 362 m_type == ValueFromDurationToNext);
327 } 363 }
328 364
329 bool actingOnPrevPoint() const { 365 bool actingOnPrevEvent() const {
330 return (m_type == ValueFromDurationToNext || 366 return (m_type == ValueFromDurationToNext ||
331 m_type == ValueFromTempoToNext); 367 m_type == ValueFromTempoToNext);
332 } 368 }
333 369
334 protected: 370 protected:
335 template <typename PointType> 371 float getValueFor(Event p, const Event *prev) {
336 float getValueFor(PointType &newPoint, PointType *prevPoint) 372
337 {
338 float value = 0.f; 373 float value = 0.f;
339 374
340 switch (m_type) { 375 switch (m_type) {
341 376
342 case ValueNone: 377 case ValueNone:
353 value = float(m_counter2 + double(m_counter) / double(m_dp)); 388 value = float(m_counter2 + double(m_counter) / double(m_dp));
354 incrementCounter(); 389 incrementCounter();
355 break; 390 break;
356 391
357 case ValueFromFrameNumber: 392 case ValueFromFrameNumber:
358 value = float(newPoint.frame); 393 value = float(p.getFrame());
359 break; 394 break;
360 395
361 case ValueFromRealTime: 396 case ValueFromRealTime:
362 if (m_rate == 0.0) { 397 if (m_rate == 0.0) {
363 std::cerr << "ERROR: Labeller::getValueFor: Real-time conversion required, but no sample rate set" << std::endl; 398 std::cerr << "ERROR: Labeller::getValueFor: Real-time conversion required, but no sample rate set" << std::endl;
364 } else { 399 } else {
365 value = float(double(newPoint.frame) / m_rate); 400 value = float(double(p.getFrame()) / m_rate);
366 } 401 }
367 break; 402 break;
368 403
369 case ValueFromDurationToNext: 404 case ValueFromDurationToNext:
370 case ValueFromTempoToNext: 405 case ValueFromTempoToNext:
371 case ValueFromDurationFromPrevious: 406 case ValueFromDurationFromPrevious:
372 case ValueFromTempoFromPrevious: 407 case ValueFromTempoFromPrevious:
373 if (m_rate == 0.0) { 408 if (m_rate == 0.0) {
374 std::cerr << "ERROR: Labeller::getValueFor: Real-time conversion required, but no sample rate set" << std::endl; 409 std::cerr << "ERROR: Labeller::getValueFor: Real-time conversion required, but no sample rate set" << std::endl;
375 } else if (!prevPoint) { 410 } else if (!prev) {
376 std::cerr << "ERROR: Labeller::getValueFor: Time difference required, but only one point provided" << std::endl; 411 std::cerr << "ERROR: Labeller::getValueFor: Time difference required, but only one point provided" << std::endl;
377 } else { 412 } else {
378 sv_frame_t f0 = prevPoint->frame, f1 = newPoint.frame; 413 sv_frame_t f0 = prev->getFrame(), f1 = p.getFrame();
379 if (m_type == ValueFromDurationToNext || 414 if (m_type == ValueFromDurationToNext ||
380 m_type == ValueFromDurationFromPrevious) { 415 m_type == ValueFromDurationFromPrevious) {
381 value = float(double(f1 - f0) / m_rate); 416 value = float(double(f1 - f0) / m_rate);
382 } else { 417 } else {
383 if (f1 > f0) { 418 if (f1 > f0) {
392 // function must handle points that don't have values to 427 // function must handle points that don't have values to
393 // read from 428 // read from
394 break; 429 break;
395 430
396 case ValueFromLabel: 431 case ValueFromLabel:
397 if (newPoint.label != "") { 432 if (p.getLabel() != "") {
398 // more forgiving than QString::toFloat() 433 // more forgiving than QString::toFloat()
399 value = float(atof(newPoint.label.toLocal8Bit())); 434 value = float(atof(p.getLabel().toLocal8Bit()));
400 } else { 435 } else {
401 value = 0.f; 436 value = 0.f;
402 } 437 }
403 break; 438 break;
404 } 439 }