diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index bda3df2811..2aba9ec988 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -1368,6 +1368,8 @@ MidiRegionView::display_sysexes() MidiRegionView::~MidiRegionView () { + cerr << "::~MidiRegionView()\n"; + in_destructor = true; hide_verbose_cursor (); @@ -1386,6 +1388,7 @@ MidiRegionView::~MidiRegionView () delete _note_group; delete _note_diff_command; delete _step_edit_cursor; + delete _ghost_note; } void diff --git a/gtk2_ardour/note_base.cc b/gtk2_ardour/note_base.cc index 8f7a510405..19641b26d9 100644 --- a/gtk2_ardour/note_base.cc +++ b/gtk2_ardour/note_base.cc @@ -83,6 +83,8 @@ NoteBase::NoteBase(MidiRegionView& region, bool with_events, const NotePtr note) NoteBase::~NoteBase() { + cerr << "::~Notebase()"; + _region.note_deleted (this); delete _text; diff --git a/libs/evoral/evoral/Event.hpp b/libs/evoral/evoral/Event.hpp index e32dc997aa..5f034972a7 100644 --- a/libs/evoral/evoral/Event.hpp +++ b/libs/evoral/evoral/Event.hpp @@ -25,6 +25,11 @@ #include #include +//#define DEBUG_EVENT_REFCNT 1 +#ifdef DEBUG_EVENT_REFCNT +#include +#endif + #include #include #include @@ -43,6 +48,227 @@ LIBEVORAL_API void init_event_id_counter(event_id_t n); template class Sequence; +#define COMMON_EVENT_DATA \ + EventType _type; /*< Type of event (application relative, NOT MIDI 'type') */ \ + Time _time; /*< Time stamp of event */ \ + uint32_t _size; /*< Size of buffer in bytes */ \ + event_id_t _id; /*< Unique event ID */ \ + uint8_t _buf[0]; /*< Event data. Must be at end, to use C-style variable-sized structure hack */ + +#define COMMON_EVENT_METHODS \ + inline EventType event_type() const { return _type; } \ + inline Time time() const { return _time; } \ + inline uint32_t size() const { return _size; } \ + inline void set_size (uint32_t s) { _size = s; /* CAREFUL !!! */ } \ + inline uint32_t object_size() const { return size() + sizeof (*this); } \ + inline const uint8_t* buffer() const { return _buf; } \ + inline uint8_t* buffer() { return _buf; } \ + \ + inline void set_event_type(EventType t) { _type = t; } \ + \ + inline void set_time(Time t) { _time = t; } \ + \ + inline event_id_t id() const { return _id; } \ + inline void set_id(event_id_t n) { _id = n; } \ + \ + /* The following methods are type specific and only make sense for the \ + correct event type. It is the caller's responsibility to only call \ + methods which make sense for the given event type. Currently this + means \ + they all only make sense for MIDI, but built-in support may be added + for \ + other protocols in the future, or the internal representation may + change \ + to be protocol agnostic. */ \ + \ + uint8_t type() const { return midi_type (_buf); } \ + uint8_t channel() const { return midi_channel (_buf); } \ + bool is_channel_msg() const { return midi_is_channel_msg (_buf); } \ + bool is_note_on() const { return midi_is_note_on (_buf); } \ + bool is_note_off() const { return midi_is_note_off (_buf); } \ + bool is_note() const { return midi_is_note (_buf); } \ + bool is_poly_pressure() const { return midi_is_poly_pressure (_buf); } \ + bool is_channel_pressure() const { return midi_is_channel_pressure (_buf); } \ + bool is_cc() const { return midi_is_cc (_buf); } \ + bool is_pgm_change() const { return midi_is_pgm_change (_buf); } \ + bool is_pitch_bender() const { return midi_is_pitch_bender (_buf); } \ + bool is_channel_event() const { return midi_is_channel_event (_buf); } \ + bool is_smf_meta_event() const { return midi_is_smf_meta_event (_buf); } \ + bool is_sysex() const { return midi_is_sysex (_buf); } \ + bool is_spp() const { return midi_is_spp (_buf, _size); } \ + bool is_mtc_quarter() const { return midi_is_mtc_quarter (_buf, _size); } \ + bool is_mtc_full() const { return midi_is_mtc_full (_buf, _size); } \ + \ + uint8_t note() const { return midi_note (_buf); } \ + uint8_t velocity() const { return midi_velocity (_buf); } \ + uint8_t poly_note() const { return midi_poly_note (_buf); } \ + uint8_t poly_pressure() const { return midi_poly_pressure (_buf); } \ + uint8_t channel_pressure() const { return midi_channel_pressure (_buf); } \ + uint8_t cc_number() const { return midi_cc_number (_buf); } \ + uint8_t cc_value() const { return midi_cc_value (_buf); } \ + uint8_t pgm_number() const { return midi_pgm_number (_buf); } \ + uint8_t pitch_bender_lsb() const { return midi_pitch_bender_lsb (_buf); } \ + uint8_t pitch_bender_msb() const { return midi_pitch_bender_msb (_buf); } \ + uint16_t pitch_bender_value() const { return midi_pitch_bender_value (_buf); } \ + \ + void set_channel(uint8_t channel) { _buf[0] = (0xF0 & _buf[0]) | (0x0F & channel); } \ + void set_type(uint8_t type) { _buf[0] = (0x0F & _buf[0]) | (0xF0 & type); } \ + void set_note(uint8_t num) { _buf[1] = num; } \ + void set_velocity(uint8_t val) { _buf[2] = val; } \ + void set_cc_number(uint8_t num) { _buf[1] = num; } \ + void set_cc_value(uint8_t val) { _buf[2] = val; } \ + void set_pgm_number(uint8_t num) { _buf[1] = num; } \ + \ + uint16_t value() const { \ + switch (type()) { \ + case MIDI_CMD_CONTROL: \ + return cc_value(); \ + case MIDI_CMD_BENDER: \ + return pitch_bender_value(); \ + case MIDI_CMD_NOTE_PRESSURE: \ + return poly_pressure(); \ + case MIDI_CMD_CHANNEL_PRESSURE: \ + return channel_pressure(); \ + default: \ + return 0; \ + } \ + } + +template +bool event_time_order (const E & a, const E & other) +{ + if (a.time() < other.time()) { + return true; + } else if (a.time() > other.time()) { + return false; + } + + if (a.type() != MIDI_EVENT) { + /* times are equal, sort order is arbitrary */ + return false; + } + + /* times are equal. Use MIDI semantics */ + + bool other_first = false; + + /* two events at identical times. we need to determine + the order in which they should occur. + + the rule is: + + Controller messages + Program Change + Note Off + Note On + Note Pressure + Channel Pressure + Pitch Bend + */ + + if (!a.is_channel_msg() || !other.is_channel_msg() || (a.channel() != other.channel())) { + + /* if either message is not a channel message, or if + * the channels are + * different, we don't care about the type. + */ + + other_first = true; + + } else { + + switch (other.type()) { + case MIDI_CMD_CONTROL: + other_first = true; + break; + + case MIDI_CMD_PGM_CHANGE: + switch (a.type()) { + case MIDI_CMD_CONTROL: + break; + case MIDI_CMD_PGM_CHANGE: + case MIDI_CMD_NOTE_OFF: + case MIDI_CMD_NOTE_ON: + case MIDI_CMD_NOTE_PRESSURE: + case MIDI_CMD_CHANNEL_PRESSURE: + case MIDI_CMD_BENDER: + other_first = true; + } + break; + + case MIDI_CMD_NOTE_OFF: + switch (a.type()) { + case MIDI_CMD_CONTROL: + case MIDI_CMD_PGM_CHANGE: + break; + case MIDI_CMD_NOTE_OFF: + case MIDI_CMD_NOTE_ON: + case MIDI_CMD_NOTE_PRESSURE: + case MIDI_CMD_CHANNEL_PRESSURE: + case MIDI_CMD_BENDER: + other_first = true; + } \ + break; + + case MIDI_CMD_NOTE_ON: + switch (a.type()) { + case MIDI_CMD_CONTROL: + case MIDI_CMD_PGM_CHANGE: + case MIDI_CMD_NOTE_OFF: + break; + case MIDI_CMD_NOTE_ON: + case MIDI_CMD_NOTE_PRESSURE: + case MIDI_CMD_CHANNEL_PRESSURE: + case MIDI_CMD_BENDER: + other_first = true; + } + break; + case MIDI_CMD_NOTE_PRESSURE: + switch (a.type()) { + case MIDI_CMD_CONTROL: + case MIDI_CMD_PGM_CHANGE: + case MIDI_CMD_NOTE_OFF: + case MIDI_CMD_NOTE_ON: + break; + case MIDI_CMD_NOTE_PRESSURE: + case MIDI_CMD_CHANNEL_PRESSURE: + case MIDI_CMD_BENDER: + other_first = true; + } + break; + + case MIDI_CMD_CHANNEL_PRESSURE: + switch (a.type()) { + case MIDI_CMD_CONTROL: + case MIDI_CMD_PGM_CHANGE: + case MIDI_CMD_NOTE_OFF: + case MIDI_CMD_NOTE_ON: + case MIDI_CMD_NOTE_PRESSURE: + break; + case MIDI_CMD_CHANNEL_PRESSURE: + case MIDI_CMD_BENDER: + other_first = true; + } + break; + case MIDI_CMD_BENDER: + switch (a.type()) { + case MIDI_CMD_CONTROL: + case MIDI_CMD_PGM_CHANGE: + case MIDI_CMD_NOTE_OFF: + case MIDI_CMD_NOTE_ON: + case MIDI_CMD_NOTE_PRESSURE: + case MIDI_CMD_CHANNEL_PRESSURE: + break; + case MIDI_CMD_BENDER: + other_first = true; + } + break; + } + } + + return other_first; +} + /** An event. * * Template parameter Time is the type of the time stamp used for this event. @@ -50,11 +276,10 @@ template class Sequence; * This is not POD: the structure uses the C-style zero-sized array hack to * allow us to place a contiguous data block immediately after the Event * structure. - * - * This object is otherwise intended to be POD: DO NOT ADD VIRTUAL METHODS. */ template -class LIBEVORAL_API Event { +class LIBEVORAL_API Event /* INHERITANCE ILLEGAL HERE */ +{ public: Event (EventType ty, Time tm, size_t sz, uint8_t const * data, event_id_t id = -1) : _type (ty) @@ -80,6 +305,8 @@ public: memcpy (_buf, other.buffer(), _size); } + ~Event() {} + Event& operator= (Event const & other) { /* Does NOT copy event ID */ if (this != &other) { @@ -99,219 +326,13 @@ public: !memcmp (_buf, other._buf, _size); } - inline EventType event_type() const { return _type; } - inline Time time() const { return _time; } - inline uint32_t size() const { return _size; } - inline void set_size (uint32_t s) { _size = s; /* CAREFUL !!! */ } - inline uint32_t object_size() const { return size() + sizeof (*this); } - inline const uint8_t* buffer() const { return _buf; } - inline uint8_t* buffer() { return _buf; } - - inline void set_event_type(EventType t) { _type = t; } - - inline void set_time(Time t) { _time = t; } - - inline event_id_t id() const { return _id; } - inline void set_id(event_id_t n) { _id = n; } - - /* The following methods are type specific and only make sense for the - correct event type. It is the caller's responsibility to only call - methods which make sense for the given event type. Currently this means - they all only make sense for MIDI, but built-in support may be added for - other protocols in the future, or the internal representation may change - to be protocol agnostic. */ - - uint8_t type() const { return midi_type (_buf); } - uint8_t channel() const { return midi_channel (_buf); } - bool is_channel_msg() const { return midi_is_channel_msg (_buf); } - bool is_note_on() const { return midi_is_note_on (_buf); } - bool is_note_off() const { return midi_is_note_off (_buf); } - bool is_note() const { return midi_is_note (_buf); } - bool is_poly_pressure() const { return midi_is_poly_pressure (_buf); } - bool is_channel_pressure() const { return midi_is_channel_pressure (_buf); } - bool is_cc() const { return midi_is_cc (_buf); } - bool is_pgm_change() const { return midi_is_pgm_change (_buf); } - bool is_pitch_bender() const { return midi_is_pitch_bender (_buf); } - bool is_channel_event() const { return midi_is_channel_event (_buf); } - bool is_smf_meta_event() const { return midi_is_smf_meta_event (_buf); } - bool is_sysex() const { return midi_is_sysex (_buf); } - bool is_spp() const { return midi_is_spp (_buf, _size); } - bool is_mtc_quarter() const { return midi_is_mtc_quarter (_buf, _size); } - bool is_mtc_full() const { return midi_is_mtc_full (_buf, _size); } - - uint8_t note() const { return midi_note (_buf); } - uint8_t velocity() const { return midi_velocity (_buf); } - uint8_t poly_note() const { return midi_poly_note (_buf); } - uint8_t poly_pressure() const { return midi_poly_pressure (_buf); } - uint8_t channel_pressure() const { return midi_channel_pressure (_buf); } - uint8_t cc_number() const { return midi_cc_number (_buf); } - uint8_t cc_value() const { return midi_cc_value (_buf); } - uint8_t pgm_number() const { return midi_pgm_number (_buf); } - uint8_t pitch_bender_lsb() const { return midi_pitch_bender_lsb (_buf); } - uint8_t pitch_bender_msb() const { return midi_pitch_bender_msb (_buf); } - uint16_t pitch_bender_value() const { return midi_pitch_bender_value (_buf); } - - void set_channel(uint8_t channel) { _buf[0] = (0xF0 & _buf[0]) | (0x0F & channel); } - void set_type(uint8_t type) { _buf[0] = (0x0F & _buf[0]) | (0xF0 & type); } - void set_note(uint8_t num) { _buf[1] = num; } - void set_velocity(uint8_t val) { _buf[2] = val; } - void set_cc_number(uint8_t num) { _buf[1] = num; } - void set_cc_value(uint8_t val) { _buf[2] = val; } - void set_pgm_number(uint8_t num) { _buf[1] = num; } - - uint16_t value() const { - switch (type()) { - case MIDI_CMD_CONTROL: - return cc_value(); - case MIDI_CMD_BENDER: - return pitch_bender_value(); - case MIDI_CMD_NOTE_PRESSURE: - return poly_pressure(); - case MIDI_CMD_CHANNEL_PRESSURE: - return channel_pressure(); - default: - return 0; - } + bool time_order_before (Event const & other) const { + return event_time_order (*this, other); } - inline bool time_order_before (Event const & other) const { - if (_time < other.time()) { - return true; - } else if (_time > other.time()) { - return false; - } - - if (_type != MIDI_EVENT) { - /* times are equal, sort order is arbitrary */ - return false; - } - - /* times are equal. Use MIDI semantics */ - - bool other_first = false; - - /* two events at identical times. we need to determine - the order in which they should occur. - - the rule is: - - Controller messages - Program Change - Note Off - Note On - Note Pressure - Channel Pressure - Pitch Bend - */ - - if (!is_channel_msg() || !other.is_channel_msg() || (channel() != other.channel())) { - - /* if either message is not a channel message, or if the channels are - * different, we don't care about the type. - */ - - other_first = true; - - } else { - - switch (other.type()) { - case MIDI_CMD_CONTROL: - other_first = true; - break; - - case MIDI_CMD_PGM_CHANGE: - switch (type()) { - case MIDI_CMD_CONTROL: - break; - case MIDI_CMD_PGM_CHANGE: - case MIDI_CMD_NOTE_OFF: - case MIDI_CMD_NOTE_ON: - case MIDI_CMD_NOTE_PRESSURE: - case MIDI_CMD_CHANNEL_PRESSURE: - case MIDI_CMD_BENDER: - other_first = true; - } - break; - - case MIDI_CMD_NOTE_OFF: - switch (type()) { - case MIDI_CMD_CONTROL: - case MIDI_CMD_PGM_CHANGE: - break; - case MIDI_CMD_NOTE_OFF: - case MIDI_CMD_NOTE_ON: - case MIDI_CMD_NOTE_PRESSURE: - case MIDI_CMD_CHANNEL_PRESSURE: - case MIDI_CMD_BENDER: - other_first = true; - } - break; - - case MIDI_CMD_NOTE_ON: - switch (type()) { - case MIDI_CMD_CONTROL: - case MIDI_CMD_PGM_CHANGE: - case MIDI_CMD_NOTE_OFF: - break; - case MIDI_CMD_NOTE_ON: - case MIDI_CMD_NOTE_PRESSURE: - case MIDI_CMD_CHANNEL_PRESSURE: - case MIDI_CMD_BENDER: - other_first = true; - } - break; - case MIDI_CMD_NOTE_PRESSURE: - switch (type()) { - case MIDI_CMD_CONTROL: - case MIDI_CMD_PGM_CHANGE: - case MIDI_CMD_NOTE_OFF: - case MIDI_CMD_NOTE_ON: - break; - case MIDI_CMD_NOTE_PRESSURE: - case MIDI_CMD_CHANNEL_PRESSURE: - case MIDI_CMD_BENDER: - other_first = true; - } - break; - - case MIDI_CMD_CHANNEL_PRESSURE: - switch (type()) { - case MIDI_CMD_CONTROL: - case MIDI_CMD_PGM_CHANGE: - case MIDI_CMD_NOTE_OFF: - case MIDI_CMD_NOTE_ON: - case MIDI_CMD_NOTE_PRESSURE: - break; - case MIDI_CMD_CHANNEL_PRESSURE: - case MIDI_CMD_BENDER: - other_first = true; - } - break; - case MIDI_CMD_BENDER: - switch (type()) { - case MIDI_CMD_CONTROL: - case MIDI_CMD_PGM_CHANGE: - case MIDI_CMD_NOTE_OFF: - case MIDI_CMD_NOTE_ON: - case MIDI_CMD_NOTE_PRESSURE: - case MIDI_CMD_CHANNEL_PRESSURE: - break; - case MIDI_CMD_BENDER: - other_first = true; - } - break; - } - } - - return other_first; - } - - protected: - EventType _type; ///< Type of event (application relative, NOT MIDI 'type') - Time _time; ///< Time stamp of event - uint32_t _size; ///< Size of buffer in bytes - event_id_t _id; ///< Unique event ID - uint8_t _buf[0]; ///< Event data. Must be at end, to use C-style variable-sized structure hack + COMMON_EVENT_METHODS; + public: + COMMON_EVENT_DATA private: /* hide these methods since they are illegal. Event can only be @@ -338,17 +359,13 @@ template /** An reference-counted version of an Event, to be used in contexts where * the event is shared between various contexts (e.g. editing of a Sequence) * - * Inheritance order is based on the assumption that the ref-counter will - * likely be accessed more frequently than any actual data elements in - * Event. Could be wrong and probably needs measuring. But also based on the - * fact that the Event needs to be at the end in order for the zero-size - * array hack to work. + * This cannot inherit from any classes, because we cannot control the memory + * layout, and it is mandatory that the _buf member occurs at the end of the + * object. Surprisingly, perhaps, memory layout is at the whim of the compiler + * and is not required to follow the inheritance order in any way. */ template -class LIBEVORAL_API ManagedEvent : public boost::intrusive_ref_counter >, - public PoolAllocated, - public boost::noncopyable, - public Event