1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2
3
4 /*
5 Rosegarden
6 A sequencer and musical notation editor.
7 Copyright 2000-2021 the Rosegarden development team.
8 See the AUTHORS file for more details.
9
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU General Public License as
12 published by the Free Software Foundation; either version 2 of the
13 License, or (at your option) any later version. See the file
14 COPYING included with this distribution for more information.
15 */
16
17 #ifndef RG_EVENT_H
18 #define RG_EVENT_H
19
20 #include "PropertyMap.h"
21 #include "Exception.h"
22 #include "TimeT.h"
23 #include "misc/Debug.h"
24
25 #include <rosegardenprivate_export.h>
26
27 #include <string>
28 #include <vector>
29 #include <iostream>
30
31
32 namespace Rosegarden
33 {
34
35
36 /// A generic Event.
37 /**
38 * The Event class represents an event of arbitrary type with some basic
39 * common attributes and an arbitrary number of properties of dynamically-
40 * determined name and type.
41 *
42 * An Event has a type; a duration, often zero for events other than
43 * notes; an absolute time, the time at which the event begins, which
44 * is used to order events within a Segment; and a "sub-ordering", used
45 * to determine an order for events that have the same absolute time
46 * (for example to ensure that the clef always appears before the key
47 * signature at the start of a piece). Besides these, an event can
48 * have any number of properties, which are typed values stored and
49 * retrieved by name. Properties may be persistent or non-persistent,
50 * depending on whether they are saved to file with the rest of the
51 * event data or are considered to be only cached values that can be
52 * recomputed at will if necessary.
53 *
54 * Segment is the primary container of Event objects.
55 *
56 * This class is both generic and polymorphic without using C++ language
57 * features (templates and inheritance) to implement those qualities.
58 * It would be interesting to explore whether inheritance/polymorphism
59 * would lead to an easier to understand and faster implementation of
60 * Event. The concrete types like Note would inherit directly from Event
61 * and would provide member objects without using properties and a
62 * PropertyMap. One key downside is that older versions of rg would then
63 * be unable to preserve properties that they do not understand.
64 * Not sure that's a very big deal given that the properties have been
65 * pretty stable for quite a while.
66 *
67 * There are concrete types such as Note (in NotationTypes.h) and
68 * ProgramChange (in MidiTypes.h) which can create Event objects as
69 * needed. Generally, the concrete types provide a "getAs*Event()" routine
70 * to create a corresponding Event object.
71 */
72 class ROSEGARDENPRIVATE_EXPORT Event
73 {
74 public:
75
76 // *** Exceptions
77
78 /// Attempt to access a property that is not present in the Event
79 class NoData : public Exception {
80 public:
NoData(const std::string & property)81 NoData(const std::string &property) :
82 Exception("No data found for property " + property)
83 { }
NoData(const std::string & property,const std::string & file,int line)84 NoData(const std::string &property, const std::string &file, int line) :
85 Exception("No data found for property " + property, file, line)
86 { }
87 };
88
89 /// Attempt to access a property with the wrong type
90 class BadType : public Exception {
91 public:
BadType(const std::string & property,const std::string & expected,const std::string & actual)92 BadType(const std::string &property, const std::string &expected, const std::string &actual) :
93 Exception("Bad type for " + property + " (expected " +
94 expected + ", found " + actual + ")")
95 { }
BadType(const std::string & property,const std::string & expected,const std::string & actual,const std::string & file,int line)96 BadType(const std::string &property, const std::string &expected, const std::string &actual,
97 const std::string &file, int line) :
98 Exception("Bad type for " + property + " (expected " +
99 expected + ", found " + actual + ")", file, line)
100 { }
101 };
102
103 // *** Constructors
104
105 Event(const std::string &type,
106 timeT absoluteTime, timeT duration = 0, short subOrdering = 0) :
m_data(new EventData (type,absoluteTime,duration,subOrdering))107 m_data(new EventData(type, absoluteTime, duration, subOrdering)),
108 m_nonPersistentProperties(nullptr)
109 { }
110
Event(const std::string & type,timeT absoluteTime,timeT duration,short subOrdering,timeT notationAbsoluteTime,timeT notationDuration)111 Event(const std::string &type,
112 timeT absoluteTime, timeT duration, short subOrdering,
113 timeT notationAbsoluteTime, timeT notationDuration) :
114 m_data(new EventData(type, absoluteTime, duration, subOrdering)),
115 m_nonPersistentProperties(nullptr)
116 {
117 setNotationAbsoluteTime(notationAbsoluteTime);
118 setNotationDuration(notationDuration);
119 }
120
121 // these ctors can't use default args: default has to be obtained from e
122
Event(const Event & e,timeT absoluteTime)123 Event(const Event &e, timeT absoluteTime) :
124 m_nonPersistentProperties(nullptr)
125 {
126 share(e);
127 unshare();
128 m_data->m_absoluteTime = absoluteTime;
129 setNotationAbsoluteTime(absoluteTime);
130 setNotationDuration(m_data->m_duration);
131 }
132
Event(const Event & e,timeT absoluteTime,timeT duration)133 Event(const Event &e, timeT absoluteTime, timeT duration) :
134 m_nonPersistentProperties(nullptr)
135 {
136 share(e);
137 unshare();
138 m_data->m_absoluteTime = absoluteTime;
139 m_data->m_duration = duration;
140 setNotationAbsoluteTime(absoluteTime);
141 setNotationDuration(duration);
142 }
143
Event(const Event & e,timeT absoluteTime,timeT duration,short subOrdering)144 Event(const Event &e, timeT absoluteTime,
145 timeT duration, short subOrdering):
146 m_nonPersistentProperties(nullptr)
147 {
148 share(e);
149 unshare();
150 m_data->m_absoluteTime = absoluteTime;
151 m_data->m_duration = duration;
152 m_data->m_subOrdering = subOrdering;
153 setNotationAbsoluteTime(absoluteTime);
154 setNotationDuration(duration);
155 }
156
Event(const Event & e,timeT absoluteTime,timeT duration,short subOrdering,timeT notationAbsoluteTime)157 Event(const Event &e, timeT absoluteTime, timeT duration, short subOrdering,
158 timeT notationAbsoluteTime) :
159 m_nonPersistentProperties(nullptr)
160 {
161 share(e);
162 unshare();
163 m_data->m_absoluteTime = absoluteTime;
164 m_data->m_duration = duration;
165 m_data->m_subOrdering = subOrdering;
166 setNotationAbsoluteTime(notationAbsoluteTime);
167 setNotationDuration(duration);
168 }
169
Event(const Event & e,timeT absoluteTime,timeT duration,short subOrdering,timeT notationAbsoluteTime,timeT notationDuration)170 Event(const Event &e, timeT absoluteTime, timeT duration, short subOrdering,
171 timeT notationAbsoluteTime, timeT notationDuration) :
172 m_nonPersistentProperties(nullptr)
173 {
174 share(e);
175 unshare();
176 m_data->m_absoluteTime = absoluteTime;
177 m_data->m_duration = duration;
178 m_data->m_subOrdering = subOrdering;
179 setNotationAbsoluteTime(notationAbsoluteTime);
180 setNotationDuration(notationDuration);
181 }
182
~Event()183 ~Event() { lose(); }
184
Event(const Event & e)185 Event(const Event &e) :
186 m_nonPersistentProperties(nullptr)
187 {
188 share(e);
189 }
190
191 Event &operator=(const Event &e)
192 {
193 // If they aren't the same...
194 if (&e != this) {
195 lose();
196 share(e);
197 }
198
199 return *this;
200 }
201
copyMoving(timeT offset)202 Event *copyMoving(timeT offset) const
203 {
204 return new Event(*this,
205 m_data->m_absoluteTime + offset,
206 m_data->m_duration,
207 m_data->m_subOrdering,
208 getNotationAbsoluteTime() + offset,
209 getNotationDuration());
210 }
211
212 // check if the events are copies
213 bool isCopyOf(const Event &e) const;
214
215 friend bool operator<(const Event&, const Event&);
216
217 /// Type of the Event (E.g. Note, Accidental, Key, etc...)
218 /**
219 * See NotationTypes.h and MidiTypes.h for more examples.
220 */
getType()221 std::string getType() const
222 {
223 if (!m_data) {
224 RG_DEBUG << "Event::getType(): FATAL: m_data == nullptr. Crash likely.";
225 return "";
226 }
227 return m_data->m_type;
228 }
229 /// Check Event type.
isa(const std::string & type)230 bool isa(const std::string &type) const { return (m_data->m_type == type); }
231
getAbsoluteTime()232 timeT getAbsoluteTime() const { return m_data->m_absoluteTime; }
getNotationAbsoluteTime()233 timeT getNotationAbsoluteTime() const { return m_data->getNotationTime(); }
234 /// Move Event in time without any ancillary coordination.
235 /**
236 * UNSAFE. Don't call this unless you know exactly what you're doing.
237 */
238 void unsafeChangeTime(timeT offset);
239
getDuration()240 timeT getDuration() const { return m_data->m_duration; }
getNotationDuration()241 timeT getNotationDuration() const { return m_data->getNotationDuration(); }
242 /**
243 * Returns the greater of getDuration() or getNotationDuration() for Note
244 * Events. Returns getDuration() for all other Event types.
245 *
246 * \author Tito Latini
247 */
248 timeT getGreaterDuration();
249
getSubOrdering()250 short getSubOrdering() const { return m_data->m_subOrdering; }
251
252 /**
253 * Return whether this Event's section of a triggered ornament
254 * is masked, for use when the Event is part of a multiple-tied-note
255 * ornament trigger. Uses the TRIGGER_EXPAND property.
256 */
257 bool maskedInTrigger() const;
258
259 // *** Properties (name/value pairs)
260
261 /**
262 * Tests if the Event has the property/data in parameter
263 */
264 bool has(const PropertyName &name) const;
265
266 /// Get the value for a property
267 /**
268 * \returns The value of the property.
269 * \throws NoData
270 * \throws BadType
271 */
272 template <PropertyType P>
273 typename PropertyDefn<P>::basic_type get(const PropertyName &name) const;
274
275 /// Get the value for a property
276 /**
277 * \returns true if the property value was successfully retrieved.
278 */
279 template <PropertyType P>
280 bool get(const PropertyName &name, typename PropertyDefn<P>::basic_type &val) const;
281
282 /// Get the value for a property as a std::string
283 /**
284 * \throws NoData
285 */
286 std::string getAsString(const PropertyName &name) const;
287
288 /**
289 * Tests if the specified property/data is persistent (is copied
290 * when duplicating the Event) or not
291 * \throws NoData
292 */
293 template <PropertyType P>
294 bool isPersistent(const PropertyName &name) const;
295
296 /**
297 * \throws NoData
298 */
299 PropertyType getPropertyType(const PropertyName &name) const;
300
301 /**
302 * \throws NoData
303 */
304 std::string getPropertyTypeAsString(const PropertyName &name) const;
305
306 /// Set the value for a property.
307 /**
308 * If the property/data already exists, this function just modifies the
309 * stored value, and if not, it creates the association.
310 *
311 * \throws BadType
312 */
313 template <PropertyType P>
314 void set(const PropertyName &name, typename PropertyDefn<P>::basic_type value,
315 bool persistent = true);
316
317 /// Set the value for a property if it doesn't exist as a persistent value.
318 /**
319 * \throws BadType
320 */
321 template <PropertyType P>
322 void setMaybe(const PropertyName &name, typename PropertyDefn<P>::basic_type value);
323
324 /// Destroy a property.
325 /**
326 * Does nothing if the property does not exist.
327 */
328 void unset(const PropertyName &name);
329
330 typedef std::vector<PropertyName> PropertyNames;
331 PropertyNames getPersistentPropertyNames() const;
332 PropertyNames getNonPersistentPropertyNames() const;
333
334 /**
335 * Destroy all the non persistent properties.
336 */
337 void clearNonPersistentProperties();
338
339 /// Compare Event objects using Event::operator<.
340 /**
341 * Used when creating sets and multisets of Event objects, like Segment.
342 */
343 struct EventCmp
344 {
operatorEventCmp345 bool operator()(const Event *e1, const Event *e2) const
346 {
347 return *e1 < *e2;
348 }
349 };
350
351 /// Compare Event objects based on their end times.
352 /**
353 * Used for example in classes that export to other formats, like
354 * Lilypond.
355 */
356 struct EventEndCmp
357 {
operatorEventEndCmp358 bool operator()(const Event *e1, const Event *e2) const
359 {
360 return e1->getAbsoluteTime() + e1->getDuration() <=
361 e2->getAbsoluteTime() + e2->getDuration();
362 }
363 };
364
365 /// Get the XML string representing the object.
366 /**
367 * If the absolute time of the event differs from the expected time,
368 * include the difference between the two as a timeOffset attribute.
369 * If expectedTime == 0, include an absoluteTime attribute instead.
370 */
371 std::string toXmlString(timeT expectedTime) const;
372
373 // *** DEBUG
374
375 /// Approximate. For debugging and inspection purposes.
376 size_t getStorageSize() const;
377
378 // UNUSED
379 static void dumpStats(std::ostream &);
380
381 protected:
382
383 // Interface for subclasses such as XmlStorableEvent.
384
Event()385 Event() :
386 m_data(new EventData("", 0, 0, 0)),
387 m_nonPersistentProperties(nullptr)
388 { }
389
setType(const std::string & t)390 void setType(const std::string &t) { unshare(); m_data->m_type = t; }
setAbsoluteTime(timeT t)391 void setAbsoluteTime(timeT t) { unshare(); m_data->m_absoluteTime = t; }
setDuration(timeT d)392 void setDuration(timeT d) { unshare(); m_data->m_duration = d; }
setSubOrdering(short o)393 void setSubOrdering(short o) { unshare(); m_data->m_subOrdering = o; }
setNotationAbsoluteTime(timeT t)394 void setNotationAbsoluteTime(timeT t) { unshare(); m_data->setNotationTime(t); }
setNotationDuration(timeT d)395 void setNotationDuration(timeT d) { unshare(); m_data->setNotationDuration(d); }
396
397 private:
398 friend QDebug &operator<<(QDebug &dbg, const Event &event);
399
400 /// Data that are shared between shallow-copied instances
401 struct EventData
402 {
403 EventData(const std::string &type,
404 timeT absoluteTime, timeT duration, short subOrdering);
405 EventData(const std::string &type,
406 timeT absoluteTime, timeT duration, short subOrdering,
407 const PropertyMap *properties);
408 /// Make a unique copy. Used for Copy On Write.
409 EventData *unshare();
410 ~EventData();
411 unsigned int m_refCount;
412
413 std::string m_type;
414 timeT m_absoluteTime;
415 timeT m_duration;
416 short m_subOrdering;
417
418 PropertyMap *m_properties;
419
420 // These are properties because we don't care so much about
421 // raw speed in get/set, but we do care about storage size for
422 // events that don't have them or that have zero values:
setNotationTimeEventData423 void setNotationTime(timeT t)
424 { setTime(NotationTime, t, m_absoluteTime); }
425 timeT getNotationTime() const;
setNotationDurationEventData426 void setNotationDuration(timeT d)
427 { setTime(NotationDuration, d, m_duration); }
428 timeT getNotationDuration() const;
429
430 private:
431 EventData(const EventData &);
432 EventData &operator=(const EventData &);
433
434 static PropertyName NotationTime;
435 static PropertyName NotationDuration;
436
437 /// Add the time property unless (t == deft).
438 void setTime(const PropertyName &name, timeT t, timeT deft);
439 };
440
441 // ??? This is a reference counted smart pointer supporting Copy
442 // On Write. We probably can't replace it with a QSharedPointer.
443 // It would be more interesting to make Copy On Write disable-able
444 // and see if it makes any sort of performance or memory difference.
445 // Might also be a good idea to see if C++11 move semantics would help.
446 EventData *m_data;
447 // ??? This doesn't seem to participate in Copy On Write, so a
448 // QSharedPointer should simplify managing this.
449 PropertyMap *m_nonPersistentProperties; // Unique to an instance
450
share(const Event & e)451 void share(const Event &e)
452 {
453 m_data = e.m_data;
454 m_data->m_refCount++;
455 }
456
457 /// Makes a copy. Used for Copy On Write.
458 /**
459 * Returns true if unshare was actually necessary.
460 */
unshare()461 bool unshare()
462 {
463 if (m_data->m_refCount > 1) {
464 m_data = m_data->unshare();
465 return true;
466 } else {
467 return false;
468 }
469 }
470
471 /// Dereference and delete.
lose()472 void lose()
473 {
474 if (--m_data->m_refCount == 0) {
475 delete m_data;
476 m_data = nullptr;
477 }
478 delete m_nonPersistentProperties;
479 m_nonPersistentProperties = nullptr;
480 }
481
482 /// Find a property in both the persistent and non-persistent properties.
483 /**
484 * @param[in] name The property to find.
485 * @param[out] i An iterator pointing to the property that was found.
486 * Invalid if the function return value is nullptr.
487 * \return The map in which the property was found. Returns nullptr
488 * otherwise.
489 */
490 PropertyMap *find(const PropertyName &name, PropertyMap::iterator &i);
491
492 /// Find a property in both the persistent and non-persistent properties.
493 /**
494 * @param[in] name The property to find.
495 * @param[out] i An iterator pointing to the property that was found.
496 * Invalid if the function return value is nullptr.
497 * \return The map in which the property was found. Returns nullptr
498 * otherwise.
499 */
find(const PropertyName & name,PropertyMap::const_iterator & i)500 const PropertyMap *find(const PropertyName &name,
501 PropertyMap::const_iterator &i) const
502 {
503 PropertyMap::iterator j;
504 PropertyMap *map = const_cast<Event *>(this)->find(name, j);
505 i = j;
506 return map;
507 }
508
insert(const PropertyPair & pair,bool persistent)509 PropertyMap::iterator insert(const PropertyPair &pair, bool persistent)
510 {
511 PropertyMap **map =
512 (persistent ? &m_data->m_properties : &m_nonPersistentProperties);
513
514 // If the map hasn't been created yet, create it.
515 if (!*map)
516 *map = new PropertyMap();
517
518 return (*map)->insert(pair).first;
519 }
520
521 #ifndef NDEBUG
522 static int m_getCount;
523 static int m_setCount;
524 static int m_setMaybeCount;
525 static int m_hasCount;
526 static int m_unsetCount;
527 static clock_t m_lastStats;
528 #endif
529 };
530
531 extern ROSEGARDENPRIVATE_EXPORT QDebug &operator<<(QDebug &dbg, const Event &event);
532
533 template <PropertyType P>
534 bool
get(const PropertyName & name,typename PropertyDefn<P>::basic_type & val)535 Event::get(const PropertyName &name,
536 typename PropertyDefn<P>::basic_type &val) const
537 {
538 #ifndef NDEBUG
539 ++m_getCount;
540 #endif
541
542 PropertyMap::const_iterator i;
543 const PropertyMap *map = find(name, i);
544
545 // Not found? Bail.
546 if (!map)
547 return false;
548
549 PropertyStoreBase *sb = i->second;
550 if (sb->getType() == P) {
551 val = (static_cast<PropertyStore<P> *>(sb))->getData();
552 return true;
553 } else {
554 #ifndef NDEBUG
555 RG_DEBUG << "get() Error: Attempt to get property \"" << name.getName() << "\" as" << PropertyDefn<P>::typeName() <<", actual type is" << sb->getTypeName();
556 #endif
557 return false;
558 }
559 }
560
561 template <PropertyType P>
562 typename PropertyDefn<P>::basic_type
get(const PropertyName & name)563 Event::get(const PropertyName &name) const
564 // throw (NoData, BadType)
565 {
566 #ifndef NDEBUG
567 ++m_getCount;
568 #endif
569
570 PropertyMap::const_iterator i;
571 const PropertyMap *map = find(name, i);
572
573 if (map) {
574
575 PropertyStoreBase *sb = i->second;
576 if (sb->getType() == P)
577 return (static_cast<PropertyStore<P> *>(sb))->getData();
578 else {
579 throw BadType(name.getName(),
580 PropertyDefn<P>::typeName(), sb->getTypeName(),
581 __FILE__, __LINE__);
582 }
583
584 } else {
585
586 #ifndef NDEBUG
587 RG_DEBUG << "Event::get(): Property" << name.getName().c_str() << "not found for Event:";
588 RG_DEBUG << *this;
589 #endif
590 throw NoData(name.getName(), __FILE__, __LINE__);
591 }
592 }
593
594
595 template <PropertyType P>
596 bool
isPersistent(const PropertyName & name)597 Event::isPersistent(const PropertyName &name) const
598 // throw (NoData)
599 {
600 PropertyMap::const_iterator i;
601 const PropertyMap *map = find(name, i);
602
603 if (!map)
604 throw NoData(name.getName(), __FILE__, __LINE__);
605
606 return (map == m_data->m_properties);
607 }
608
609
610 template <PropertyType P>
611 void
set(const PropertyName & name,typename PropertyDefn<P>::basic_type value,bool persistent)612 Event::set(const PropertyName &name, typename PropertyDefn<P>::basic_type value,
613 bool persistent)
614 // throw (BadType)
615 {
616 #ifndef NDEBUG
617 ++m_setCount;
618 #endif
619
620 // this is a little slow, could bear improvement
621
622 // Copy on Write
623 unshare();
624
625 PropertyMap::iterator i;
626 PropertyMap *map = find(name, i);
627
628 // If found, update.
629 if (map) {
630 bool persistentBefore = (map == m_data->m_properties);
631 if (persistentBefore != persistent) {
632 i = insert(*i, persistent);
633 map->erase(name);
634 }
635
636 PropertyStoreBase *sb = i->second;
637 if (sb->getType() == P) {
638 (static_cast<PropertyStore<P> *>(sb))->setData(value);
639 } else {
640 throw BadType(name.getName(),
641 PropertyDefn<P>::typeName(), sb->getTypeName(),
642 __FILE__, __LINE__);
643 }
644
645 } else { // Create
646 PropertyStoreBase *p = new PropertyStore<P>(value);
647 insert(PropertyPair(name, p), persistent);
648 }
649 }
650
651
652 template <PropertyType P>
653 void
setMaybe(const PropertyName & name,typename PropertyDefn<P>::basic_type value)654 Event::setMaybe(const PropertyName &name, typename PropertyDefn<P>::basic_type value)
655 // throw (BadType)
656 {
657 // setMaybe<> is actually called rather more frequently than set<>, so
658 // it makes sense for best performance to implement it separately
659 // rather than through calls to has, isPersistent and set<>
660
661 #ifndef NDEBUG
662 ++m_setMaybeCount;
663 #endif
664
665 // Copy On Write
666 unshare();
667
668 PropertyMap::iterator i;
669 PropertyMap *map = find(name, i);
670
671 // If found, update only if not persistent
672 if (map) {
673 // If persistent, bail.
674 if (map == m_data->m_properties)
675 return;
676
677 PropertyStoreBase *sb = i->second;
678
679 if (sb->getType() == P) {
680 (static_cast<PropertyStore<P> *>(sb))->setData(value);
681 } else {
682 throw BadType(name.getName(),
683 PropertyDefn<P>::typeName(), sb->getTypeName(),
684 __FILE__, __LINE__);
685 }
686 } else { // Create
687 PropertyStoreBase *p = new PropertyStore<P>(value);
688 insert(PropertyPair(name, p),
689 false); // persistent
690 }
691 }
692
693
694 }
695
696 #endif
697