EventSeries.cpp
Go to the documentation of this file.
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 
8  This program is free software; you can redistribute it and/or
9  modify it under the terms of the GNU General Public License as
10  published by the Free Software Foundation; either version 2 of the
11  License, or (at your option) any later version. See the file
12  COPYING included with this distribution for more information.
13 */
14 
15 #include "EventSeries.h"
16 
17 #include <QMutexLocker>
18 
19 using std::vector;
20 using std::string;
21 
23  EventSeries(other, QMutexLocker(&other.m_mutex))
24 {
25 }
26 
27 EventSeries::EventSeries(const EventSeries &other, const QMutexLocker &) :
28  m_events(other.m_events),
29  m_seams(other.m_seams),
31 {
32 }
33 
36 {
37  QMutexLocker locker(&m_mutex), otherLocker(&other.m_mutex);
38  m_events = other.m_events;
39  m_seams = other.m_seams;
41  return *this;
42 }
43 
46 {
47  QMutexLocker locker(&m_mutex), otherLocker(&other.m_mutex);
48  m_events = std::move(other.m_events);
49  m_seams = std::move(other.m_seams);
50  m_finalDurationlessEventFrame = std::move(other.m_finalDurationlessEventFrame);
51  return *this;
52 }
53 
54 bool
56 {
57  QMutexLocker locker(&m_mutex);
58  return m_events == other.m_events;
59 }
60 
63 {
64  EventSeries s;
65  for (const auto &e: v) {
66  s.add(e);
67  }
68  return s;
69 }
70 
71 bool
73 {
74  QMutexLocker locker(&m_mutex);
75  return m_events.empty();
76 }
77 
78 int
80 {
81  QMutexLocker locker(&m_mutex);
82  if (m_events.size() > INT_MAX) {
83  throw std::logic_error("too many events");
84  }
85  return int(m_events.size());
86 }
87 
88 void
90 {
91  QMutexLocker locker(&m_mutex);
92 
93  bool isUnique = true;
94 
95  auto pitr = lower_bound(m_events.begin(), m_events.end(), p);
96  if (pitr != m_events.end() && *pitr == p) {
97  isUnique = false;
98  }
99  m_events.insert(pitr, p);
100 
103  }
104 
105  if (p.hasDuration() && isUnique) {
106 
107  const sv_frame_t frame = p.getFrame();
108  const sv_frame_t endFrame = p.getFrame() + p.getDuration();
109 
110  createSeam(frame);
111  createSeam(endFrame);
112 
113  // These calls must both succeed after calling createSeam above
114  const auto i0 = m_seams.find(frame);
115  const auto i1 = m_seams.find(endFrame);
116 
117  for (auto i = i0; i != i1; ++i) {
118  if (i == m_seams.end()) {
119  SVCERR << "ERROR: EventSeries::add: "
120  << "reached end of seam map"
121  << endl;
122  break;
123  }
124  i->second.push_back(p);
125  }
126  }
127 
128 #ifdef DEBUG_EVENT_SERIES
129  std::cerr << "after add:" << std::endl;
130  dumpEvents();
131  dumpSeams();
132 #endif
133 }
134 
135 void
137 {
138  QMutexLocker locker(&m_mutex);
139 
140  // If we are removing the last (unique) example of an event,
141  // then we also need to remove it from the seam map. If this
142  // is only one of multiple identical events, then we don't.
143  bool isUnique = true;
144 
145  auto pitr = lower_bound(m_events.begin(), m_events.end(), p);
146  if (pitr == m_events.end() || *pitr != p) {
147  // we don't know this event
148  return;
149  } else {
150  auto nitr = pitr;
151  ++nitr;
152  if (nitr != m_events.end() && *nitr == p) {
153  isUnique = false;
154  }
155  }
156 
157  m_events.erase(pitr);
158 
159  if (!p.hasDuration() && isUnique &&
162  for (auto ritr = m_events.rbegin(); ritr != m_events.rend(); ++ritr) {
163  if (!ritr->hasDuration()) {
164  m_finalDurationlessEventFrame = ritr->getFrame();
165  break;
166  }
167  }
168  }
169 
170  if (p.hasDuration() && isUnique) {
171 
172  const sv_frame_t frame = p.getFrame();
173  const sv_frame_t endFrame = p.getFrame() + p.getDuration();
174 
175  const auto i0 = m_seams.find(frame);
176  const auto i1 = m_seams.find(endFrame);
177 
178 #ifdef DEBUG_EVENT_SERIES
179  // This should be impossible if we found p in m_events above
180  if (i0 == m_seams.end() || i1 == m_seams.end()) {
181  SVCERR << "ERROR: EventSeries::remove: either frame " << frame
182  << " or endFrame " << endFrame
183  << " for event not found in seam map: event is "
184  << p.toXmlString() << endl;
185  }
186 #endif
187 
188  // Remove any and all instances of p from the seam map; we
189  // are only supposed to get here if we are removing the
190  // last instance of p from the series anyway
191 
192  for (auto i = i0; i != i1; ++i) {
193  if (i == m_seams.end()) {
194  // This can happen only if we have a negative
195  // duration, which Event forbids
196  throw std::logic_error("unexpectedly reached end of map");
197  }
198  for (size_t j = 0; j < i->second.size(); ) {
199  if (i->second[j] == p) {
200  i->second.erase(i->second.begin() + j);
201  } else {
202  ++j;
203  }
204  }
205  }
206 
207  // Tidy up by removing any entries that are now identical
208  // to their predecessors
209 
210  std::vector<sv_frame_t> redundant;
211 
212  auto pitr = m_seams.end();
213  if (i0 != m_seams.begin()) {
214  pitr = i0;
215  --pitr;
216  }
217 
218  for (auto i = i0; i != m_seams.end(); ++i) {
219  if (pitr != m_seams.end() &&
220  seamsEqual(i->second, pitr->second)) {
221  redundant.push_back(i->first);
222  }
223  pitr = i;
224  if (i == i1) {
225  break;
226  }
227  }
228 
229  for (sv_frame_t f: redundant) {
230  m_seams.erase(f);
231  }
232 
233  // And remove any empty seams from the start of the map
234 
235  while (m_seams.begin() != m_seams.end() &&
236  m_seams.begin()->second.empty()) {
237  m_seams.erase(m_seams.begin());
238  }
239  }
240 
241 #ifdef DEBUG_EVENT_SERIES
242  std::cerr << "after remove:" << std::endl;
243  dumpEvents();
244  dumpSeams();
245 #endif
246 }
247 
248 bool
250 {
251  QMutexLocker locker(&m_mutex);
252  return binary_search(m_events.begin(), m_events.end(), p);
253 }
254 
255 void
257 {
258  QMutexLocker locker(&m_mutex);
259  m_events.clear();
260  m_seams.clear();
262 }
263 
266 {
267  QMutexLocker locker(&m_mutex);
268  if (m_events.empty()) return 0;
269  return m_events.begin()->getFrame();
270 }
271 
274 {
275  QMutexLocker locker(&m_mutex);
276 
277  sv_frame_t latest = 0;
278 
279  if (m_events.empty()) return latest;
280 
282 
283  if (m_seams.empty()) return latest;
284 
285  sv_frame_t lastSeam = m_seams.rbegin()->first;
286  if (lastSeam > latest) {
287  latest = lastSeam;
288  }
289 
290  return latest;
291 }
292 
295  sv_frame_t duration) const
296 {
297  QMutexLocker locker(&m_mutex);
298 
299  EventVector span;
300 
301  const sv_frame_t start = frame;
302  const sv_frame_t end = frame + duration;
303 
304  // first find any zero-duration events
305 
306  auto pitr = lower_bound(m_events.begin(), m_events.end(),
307  Event(start));
308  while (pitr != m_events.end() && pitr->getFrame() < end) {
309  if (!pitr->hasDuration()) {
310  span.push_back(*pitr);
311  }
312  ++pitr;
313  }
314 
315  // now any non-zero-duration ones from the seam map
316 
317  std::set<Event> found;
318  auto sitr = m_seams.lower_bound(start);
319  if (sitr == m_seams.end() || sitr->first > start) {
320  if (sitr != m_seams.begin()) {
321  --sitr;
322  }
323  }
324  while (sitr != m_seams.end() && sitr->first < end) {
325  for (const auto &p: sitr->second) {
326  found.insert(p);
327  }
328  ++sitr;
329  }
330  for (const auto &p: found) {
331  auto pitr = lower_bound(m_events.begin(), m_events.end(), p);
332  while (pitr != m_events.end() && *pitr == p) {
333  span.push_back(p);
334  ++pitr;
335  }
336  }
337 
338  return span;
339 }
340 
343  sv_frame_t duration,
344  int overspill) const
345 {
346  QMutexLocker locker(&m_mutex);
347 
348  EventVector span;
349 
350  const sv_frame_t start = frame;
351  const sv_frame_t end = frame + duration;
352 
353  // because we don't need to "look back" at events that end within
354  // but started without, we can do this entirely from m_events.
355  // The core operation is very simple, it's just overspill that
356  // complicates it.
357 
358  Events::const_iterator reference =
359  lower_bound(m_events.begin(), m_events.end(), Event(start));
360 
361  Events::const_iterator first = reference;
362  for (int i = 0; i < overspill; ++i) {
363  if (first == m_events.begin()) break;
364  --first;
365  }
366  for (int i = 0; i < overspill; ++i) {
367  if (first == reference) break;
368  span.push_back(*first);
369  ++first;
370  }
371 
372  Events::const_iterator pitr = reference;
373  Events::const_iterator last = reference;
374 
375  while (pitr != m_events.end() && pitr->getFrame() < end) {
376  if (!pitr->hasDuration() ||
377  (pitr->getFrame() + pitr->getDuration() <= end)) {
378  span.push_back(*pitr);
379  last = pitr;
380  ++last;
381  }
382  ++pitr;
383  }
384 
385  for (int i = 0; i < overspill; ++i) {
386  if (last == m_events.end()) break;
387  span.push_back(*last);
388  ++last;
389  }
390 
391  return span;
392 }
393 
396  sv_frame_t duration) const
397 {
398  QMutexLocker locker(&m_mutex);
399 
400  EventVector span;
401 
402  const sv_frame_t start = frame;
403  const sv_frame_t end = frame + duration;
404 
405  // because we don't need to "look back" at events that started
406  // earlier than the start of the given range, we can do this
407  // entirely from m_events
408 
409  auto pitr = lower_bound(m_events.begin(), m_events.end(),
410  Event(start));
411  while (pitr != m_events.end() && pitr->getFrame() < end) {
412  span.push_back(*pitr);
413  ++pitr;
414  }
415 
416  return span;
417 }
418 
421 {
422  QMutexLocker locker(&m_mutex);
423 
424  EventVector cover;
425 
426  // first find any zero-duration events
427 
428  auto pitr = lower_bound(m_events.begin(), m_events.end(),
429  Event(frame));
430  while (pitr != m_events.end() && pitr->getFrame() == frame) {
431  if (!pitr->hasDuration()) {
432  cover.push_back(*pitr);
433  }
434  ++pitr;
435  }
436 
437  // now any non-zero-duration ones from the seam map
438 
439  std::set<Event> found;
440  auto sitr = m_seams.lower_bound(frame);
441  if (sitr == m_seams.end() || sitr->first > frame) {
442  if (sitr != m_seams.begin()) {
443  --sitr;
444  }
445  }
446  if (sitr != m_seams.end() && sitr->first <= frame) {
447  for (const auto &p: sitr->second) {
448  found.insert(p);
449  }
450  ++sitr;
451  }
452  for (const auto &p: found) {
453  auto pitr = lower_bound(m_events.begin(), m_events.end(), p);
454  while (pitr != m_events.end() && *pitr == p) {
455  cover.push_back(p);
456  ++pitr;
457  }
458  }
459 
460  return cover;
461 }
462 
465 {
466  QMutexLocker locker(&m_mutex);
467 
468  return m_events;
469 }
470 
471 bool
472 EventSeries::getEventPreceding(const Event &e, Event &preceding) const
473 {
474  QMutexLocker locker(&m_mutex);
475 
476  auto pitr = lower_bound(m_events.begin(), m_events.end(), e);
477  if (pitr == m_events.end() || *pitr != e) {
478  return false;
479  }
480  if (pitr == m_events.begin()) {
481  return false;
482  }
483  --pitr;
484  preceding = *pitr;
485  return true;
486 }
487 
488 bool
489 EventSeries::getEventFollowing(const Event &e, Event &following) const
490 {
491  QMutexLocker locker(&m_mutex);
492 
493  auto pitr = lower_bound(m_events.begin(), m_events.end(), e);
494  if (pitr == m_events.end() || *pitr != e) {
495  return false;
496  }
497  while (*pitr == e) {
498  ++pitr;
499  if (pitr == m_events.end()) {
500  return false;
501  }
502  }
503  following = *pitr;
504  return true;
505 }
506 
507 bool
509  std::function<bool(const Event &)> predicate,
510  Direction direction,
511  Event &found) const
512 {
513  QMutexLocker locker(&m_mutex);
514 
515  auto pitr = lower_bound(m_events.begin(), m_events.end(),
516  Event(startSearchAt));
517 
518  while (true) {
519 
520  if (direction == Backward) {
521  if (pitr == m_events.begin()) {
522  break;
523  } else {
524  --pitr;
525  }
526  } else {
527  if (pitr == m_events.end()) {
528  break;
529  }
530  }
531 
532  const Event &e = *pitr;
533  if (predicate(e)) {
534  found = e;
535  return true;
536  }
537 
538  if (direction == Forward) {
539  ++pitr;
540  }
541  }
542 
543  return false;
544 }
545 
546 Event
548 {
549  QMutexLocker locker(&m_mutex);
550  if (!in_range_for(m_events, index)) {
551  throw std::logic_error("index out of range");
552  }
553  return m_events[index];
554 }
555 
556 int
558 {
559  QMutexLocker locker(&m_mutex);
560  auto pitr = lower_bound(m_events.begin(), m_events.end(), e);
561  auto d = distance(m_events.begin(), pitr);
562  if (d < 0 || d > INT_MAX) return 0;
563  return int(d);
564 }
565 
566 void
567 EventSeries::toXml(QTextStream &out,
568  QString indent,
569  QString extraAttributes) const
570 {
571  QMutexLocker locker(&m_mutex);
572 
573  out << indent << QString("<dataset id=\"%1\" %2>\n")
574  .arg(getExportId())
575  .arg(extraAttributes);
576 
577  for (const auto &p: m_events) {
578  p.toXml(out, indent + " ", "", {});
579  }
580 
581  out << indent << "</dataset>\n";
582 }
583 
584 void
585 EventSeries::toXml(QTextStream &out,
586  QString indent,
587  QString extraAttributes,
588  Event::ExportNameOptions options) const
589 {
590  QMutexLocker locker(&m_mutex);
591 
592  out << indent << QString("<dataset id=\"%1\" %2>\n")
593  .arg(getExportId())
594  .arg(extraAttributes);
595 
596  for (const auto &p: m_events) {
597  p.toXml(out, indent + " ", "", options);
598  }
599 
600  out << indent << "</dataset>\n";
601 }
602 
603 QVector<QString>
605  Event::ExportNameOptions nopts) const
606 {
607  if (m_events.empty()) {
608  return {};
609  } else {
610  return m_events.begin()->getStringExportHeaders(opts, nopts);
611  }
612 }
613 
614 QVector<QVector<QString>>
616  sv_frame_t startFrame,
617  sv_frame_t duration,
618  sv_samplerate_t sampleRate,
619  sv_frame_t resolution,
620  Event fillEvent) const
621 {
622  QMutexLocker locker(&m_mutex);
623 
624  QVector<QVector<QString>> rows;
625 
626  const sv_frame_t end = startFrame + duration;
627 
628  auto pitr = lower_bound(m_events.begin(), m_events.end(),
629  Event(startFrame));
630 
631  if (!(options & DataExportFillGaps)) {
632 
633  while (pitr != m_events.end() && pitr->getFrame() < end) {
634  rows.push_back(pitr->toStringExportRow(options, sampleRate));
635  ++pitr;
636  }
637 
638  } else {
639 
640  // find frame time of first point in range (if any)
641  sv_frame_t first = startFrame;
642  if (pitr != m_events.end()) {
643  first = pitr->getFrame();
644  }
645 
646  // project back to first frame time in range according to
647  // resolution. e.g. if f0 = 2, first = 9, resolution = 4 then
648  // we start at 5 (because 1 is too early and we need to arrive
649  // at 9 to match the first actual point). This method is
650  // stupid but easy to understand:
651  sv_frame_t f = first;
652  while (f >= startFrame + resolution) f -= resolution;
653 
654  // now progress, either writing the next point (if within
655  // distance) or a default fill point
656  while (f < end) {
657  if (pitr != m_events.end() && pitr->getFrame() <= f) {
658  rows.push_back(pitr->toStringExportRow
659  (options & ~DataExportFillGaps,
660  sampleRate));
661  ++pitr;
662  } else {
663  rows.push_back(fillEvent.withFrame(f).toStringExportRow
664  (options & ~DataExportFillGaps,
665  sampleRate));
666  }
667  f += resolution;
668  }
669  }
670 
671  return rows;
672 }
673 
double sv_samplerate_t
Sample rate.
Definition: BaseTypes.h:51
bool contains(const Event &e) const
QString toXmlString(QString indent="", QString extraAttributes="") const
Definition: Event.h:329
bool seamsEqual(const std::vector< Event > &s1, const std::vector< Event > &s2) const
Return true if the two seam map entries contain the same set of events.
Definition: EventSeries.h:327
int count() const
Definition: EventSeries.cpp:79
bool getEventFollowing(const Event &e, Event &following) const
If e is in the series and is not the final event in it, set following to the event immediate followin...
int64_t sv_frame_t
Frame index, the unit of our time axis.
Definition: BaseTypes.h:31
void add(const Event &e)
Definition: EventSeries.cpp:89
Event getEventByIndex(int index) const
Return the event at the given numerical index in the series, where 0 = the first event and count()-1 ...
EventVector getAllEvents() const
Retrieve all events, in their natural order.
QMutex m_mutex
Definition: EventSeries.h:246
static EventSeries fromEvents(const EventVector &ee)
Definition: EventSeries.cpp:62
sv_frame_t getStartFrame() const
Return the frame of the first event in the series.
QVector< QVector< QString > > toStringExportRows(DataExportOptions options, sv_frame_t startFrame, sv_frame_t duration, sv_samplerate_t sampleRate, sv_frame_t resolution, Event fillEvent) const
Emit events starting within the given range as string rows ready for conversion to an e...
Export sparse event-based models as if they were dense models, writing an event at every interval of ...
QVector< QString > getStringExportHeaders(DataExportOptions options, Event::ExportNameOptions) const
Return a label for each column that would be written by toStringExportRows.
int getIndexForEvent(const Event &e) const
Return the index of the first event in the series that does not compare inferior to the given event...
EventSeries & operator=(const EventSeries &)
Definition: EventSeries.cpp:35
void toXml(QTextStream &out, QString indent, QString extraAttributes) const override
Emit to XML as a dataset element.
QVector< QString > toStringExportRow(DataExportOptions opts, sv_samplerate_t sampleRate) const
Definition: Event.h:421
sv_frame_t getDuration() const
Definition: Event.h:138
bool isEmpty() const
Definition: EventSeries.cpp:72
void createSeam(sv_frame_t frame)
Create a seam at the given frame, copying from the prior seam if there is one.
Definition: EventSeries.h:302
EventVector getEventsCovering(sv_frame_t frame) const
Retrieve all events that cover the given frame.
bool getEventPreceding(const Event &e, Event &preceding) const
If e is in the series and is not the first event in it, set preceding to the event immediate precedin...
EventVector getEventsSpanning(sv_frame_t frame, sv_frame_t duration) const
Retrieve all events any part of which falls within the range in frames defined by the given frame f a...
sv_frame_t m_finalDurationlessEventFrame
The frame of the last durationless event we have in the series.
Definition: EventSeries.h:293
sv_frame_t getEndFrame() const
Return the frame plus duration of the event in the series that ends last.
Event withFrame(sv_frame_t frame) const
Definition: Event.h:115
void remove(const Event &e)
bool operator==(const EventSeries &other) const
Definition: EventSeries.cpp:55
ExportId getExportId() const
Return the numerical export identifier for this object.
Events m_events
Definition: EventSeries.h:264
bool getNearestEventMatching(sv_frame_t startSearchAt, std::function< bool(const Event &)> predicate, Direction direction, Event &found) const
Return the first event for which the given predicate returns true, searching events with start frames...
bool in_range_for(const C &container, T i)
Check whether an integer index is in range for a container, avoiding overflows and signed/unsigned co...
Definition: BaseTypes.h:37
EventVector getEventsWithin(sv_frame_t frame, sv_frame_t duration, int overspill=0) const
Retrieve all events falling wholly within the range in frames defined by the given frame f and durati...
An immutable(-ish) type used for point and event representation in sparse models, as well as for inte...
Definition: Event.h:55
FrameEventMap m_seams
Definition: EventSeries.h:282
sv_frame_t getFrame() const
Definition: Event.h:113
#define SVCERR
Definition: Debug.h:109
Container storing a series of events, with or without durations, and supporting the ability to query ...
Definition: EventSeries.h:49
bool hasDuration() const
Definition: Event.h:137
std::vector< Event > EventVector
Definition: Event.h:494
EventVector getEventsStartingWithin(sv_frame_t frame, sv_frame_t duration) const
Retrieve all events starting within the range in frames defined by the given frame f and duration d...
int DataExportOptions