mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-30 02:17:49 +01:00
don't use inheritance when trying to define both POD-ish and highly-managed Events.
The compiler can rearrange class layouts in memory, which defeats the entire purpose of using the zero-sized array hack, as well as causing problems with pool allocators/intrusive refcnts
This commit is contained in:
parent
c6f81f82c0
commit
4069c84eca
5 changed files with 393 additions and 257 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -83,6 +83,8 @@ NoteBase::NoteBase(MidiRegionView& region, bool with_events, const NotePtr note)
|
|||
|
||||
NoteBase::~NoteBase()
|
||||
{
|
||||
cerr << "::~Notebase()";
|
||||
|
||||
_region.note_deleted (this);
|
||||
|
||||
delete _text;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,11 @@
|
|||
#include <new>
|
||||
#include <stdint.h>
|
||||
|
||||
//#define DEBUG_EVENT_REFCNT 1
|
||||
#ifdef DEBUG_EVENT_REFCNT
|
||||
#include <boost/atomic.hpp>
|
||||
#endif
|
||||
|
||||
#include <boost/intrusive_ptr.hpp>
|
||||
#include <boost/intrusive/list.hpp>
|
||||
#include <boost/smart_ptr/intrusive_ref_counter.hpp>
|
||||
|
|
@ -43,6 +48,227 @@ LIBEVORAL_API void init_event_id_counter(event_id_t n);
|
|||
|
||||
template<typename T> 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<typename E>
|
||||
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<typename T> 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<typename Time>
|
||||
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<T> can only be
|
||||
|
|
@ -338,17 +359,13 @@ template<typename Time>
|
|||
/** 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<T> 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<typename Time>
|
||||
class LIBEVORAL_API ManagedEvent : public boost::intrusive_ref_counter<ManagedEvent<Time> >,
|
||||
public PoolAllocated,
|
||||
public boost::noncopyable,
|
||||
public Event<Time>
|
||||
class LIBEVORAL_API ManagedEvent /* INHERITANCE ILLEGAL HERE */
|
||||
{
|
||||
public:
|
||||
static uint32_t memory_size (uint32_t size) { return sizeof (ManagedEvent<Time>) + size; }
|
||||
|
|
@ -379,28 +396,128 @@ class LIBEVORAL_API ManagedEvent : public boost::intrusive_ref_counter<ManagedEv
|
|||
return ::new (default_event_pool.alloc (memory_size (ev.size()))) ManagedEvent<Time> (default_event_pool, ev);
|
||||
}
|
||||
|
||||
private:
|
||||
ManagedEvent (EventPool& p, EventType ty, Time tm, size_t sz, uint8_t* data, event_id_t id = -1)
|
||||
: PoolAllocated (&p)
|
||||
, Event<Time> (ty, tm, sz, data, id)
|
||||
{}
|
||||
~ManagedEvent ();
|
||||
|
||||
ManagedEvent (EventPool& p, Event<Time> const & ev)
|
||||
: PoolAllocated (&p)
|
||||
, Event<Time> (ev)
|
||||
{}
|
||||
int refcnt() const { return _refcnt.load(); }
|
||||
EventPool* pool() const { return _pool; }
|
||||
|
||||
void operator delete (void* ptr) {
|
||||
ManagedEvent* ev = reinterpret_cast<ManagedEvent*> (ptr);
|
||||
if (ev && ev->_pool) {
|
||||
ev->_pool->release (ptr);
|
||||
}
|
||||
}
|
||||
|
||||
ManagedEvent& operator= (ManagedEvent<Time> const & other) {
|
||||
/* Does NOT copy event ID */
|
||||
if (this != &other) {
|
||||
_type = other._type;
|
||||
_time = other._time;
|
||||
_size = other._size;
|
||||
memcpy (_buf, other._buf, _size);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator== (ManagedEvent const & other) const {
|
||||
/* Does NOT compare event IDs */
|
||||
return _type == other._type &&
|
||||
_time == other._time &&
|
||||
_size == other._size &&
|
||||
!memcmp (_buf, other._buf, _size);
|
||||
}
|
||||
|
||||
bool time_order_before (ManagedEvent const & other) const {
|
||||
return event_time_order (*this, other);
|
||||
}
|
||||
|
||||
COMMON_EVENT_METHODS;
|
||||
|
||||
private:
|
||||
mutable boost::atomic<int> _refcnt;
|
||||
|
||||
friend void intrusive_ptr_add_ref (const ManagedEvent<Time>* irc) {
|
||||
irc->_refcnt.fetch_add (1, boost::memory_order_relaxed);
|
||||
std::cerr << "refcnt for " << irc << " += " << irc->refcnt() << std::endl;
|
||||
}
|
||||
|
||||
friend void intrusive_ptr_release (const ManagedEvent<Time>* irc) {
|
||||
if (irc->_refcnt.fetch_sub (1, boost::memory_order_release) == 1) {
|
||||
boost::atomic_thread_fence (boost::memory_order_acquire);
|
||||
std::cerr << "refcnt => 0, delete " << irc << std::endl;
|
||||
delete irc;
|
||||
} else {
|
||||
std::cerr << "refcnt for " << irc << " -= " << irc->refcnt() << std::endl;
|
||||
}
|
||||
}
|
||||
private:
|
||||
EventPool* _pool;
|
||||
COMMON_EVENT_DATA;
|
||||
|
||||
ManagedEvent (EventPool& p, EventType ty, Time tm, size_t sz, uint8_t* data, event_id_t id = -1)
|
||||
: _pool (&p)
|
||||
, _type (ty)
|
||||
, _time (tm)
|
||||
, _size (sz)
|
||||
, _id (id)
|
||||
{
|
||||
if (data) {
|
||||
/* specs for memcpy(2) state that passing size == zero is completely legal */
|
||||
memcpy (_buf, data, sz);
|
||||
} else {
|
||||
/* hopefully data will be written in shortly */
|
||||
memset (_buf, 0, sz);
|
||||
}
|
||||
}
|
||||
|
||||
ManagedEvent (EventPool& p, Event<Time> const & other)
|
||||
: _pool (&p)
|
||||
, _type (other.event_type())
|
||||
, _time (other.time())
|
||||
, _size (other.size())
|
||||
, _id (next_event_id())
|
||||
{
|
||||
memcpy (_buf, other.buffer(), _size);
|
||||
}
|
||||
|
||||
ManagedEvent (EventType ty, Time tm, size_t sz, uint8_t* data, event_id_t id = -1)
|
||||
: PoolAllocated (0)
|
||||
, Event<Time> (ty, tm, sz, data, id)
|
||||
{}
|
||||
: _pool (0)
|
||||
, _type (ty)
|
||||
, _time (tm)
|
||||
, _size (sz)
|
||||
, _id (id)
|
||||
{
|
||||
if (data) {
|
||||
/* specs for memcpy(2) state that passing size == zero is completely legal */
|
||||
memcpy (_buf, data, sz);
|
||||
} else {
|
||||
/* hopefully data will be written in shortly */
|
||||
memset (_buf, 0, sz);
|
||||
}
|
||||
}
|
||||
|
||||
ManagedEvent (Event<Time> const & ev)
|
||||
: PoolAllocated (0)
|
||||
, Event<Time> (ev)
|
||||
{}
|
||||
ManagedEvent (Event<Time> const & other)
|
||||
: _pool (0)
|
||||
, _type (other.event_type())
|
||||
, _time (other.time())
|
||||
, _size (other.size())
|
||||
, _id (next_event_id())
|
||||
{
|
||||
memcpy (_buf, other.buffer(), _size);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Time>
|
||||
/*LIBEVORAL_API*/ std::ostream& operator<<(std::ostream& o, const Evoral::ManagedEvent<Time>& ev) {
|
||||
o << "Event #" << ev.id() << " type = " << ev.event_type() << " @ " << ev.time();
|
||||
o << std::hex;
|
||||
for (uint32_t n = 0; n < ev.size(); ++n) {
|
||||
o << ' ' << (int) ev.buffer()[n];
|
||||
}
|
||||
o << std::dec;
|
||||
return o;
|
||||
}
|
||||
|
||||
/* A ref-counted pointer to an Event that can be placed inside an intrusive
|
||||
* (doubly-linked) list. This is what most things that manipulate Events in
|
||||
* "complex" or "smart" data structures should use at all times as the handle
|
||||
|
|
@ -419,6 +536,7 @@ struct LIBEVORAL_API EventPointer : public boost::intrusive_ptr<ManagedEvent<Tim
|
|||
{
|
||||
EventPointer () {}
|
||||
EventPointer (ManagedEvent<Time> * ev) : boost::intrusive_ptr<ManagedEvent<Time> > (ev) { }
|
||||
~EventPointer ();
|
||||
|
||||
/* convenience static factory method to hide the need to call a
|
||||
ManagedEvent factory method with the same parameters.
|
||||
|
|
|
|||
|
|
@ -29,8 +29,14 @@
|
|||
|
||||
#include "evoral/visibility.h"
|
||||
|
||||
#include "pbd/stacktrace.h"
|
||||
|
||||
#define DEBUG_EVENT_POOL 1
|
||||
|
||||
#ifdef DEBUG_EVENT_POOL
|
||||
#include <glib.h>
|
||||
#endif
|
||||
|
||||
namespace Evoral {
|
||||
|
||||
class LIBEVORAL_API EventPool
|
||||
|
|
@ -88,6 +94,7 @@ class LIBEVORAL_API EventPool
|
|||
_freelists.reserve (_freelists.size() + sp.size());
|
||||
for (SizePairs::const_iterator s = sp.begin(); s != sp.end(); ++s) {
|
||||
_freelists.push_back (new FreeList (s->first, s->second));
|
||||
std::cerr << name() << " freelist for " << s->first << " count = " << s->second << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -117,10 +124,13 @@ class LIBEVORAL_API EventPool
|
|||
void* ret = (*x)->back();
|
||||
(*x)->pop_back ();
|
||||
#ifdef DEBUG_EVENT_POOL
|
||||
std::cerr << name() << ": alloc size[" << (*x)->item_size << "] free = "
|
||||
std::cerr << name() << ": alloc " << ret << " w/size[" << (*x)->item_size << "] for " << sz << " free = "
|
||||
<< (*x)->size()
|
||||
<< " alloc = " << ((char*)(*x)->end - (char*)(*x)->block) / sizeof ((*x)->item_size)
|
||||
<< std::endl;
|
||||
if (g_getenv ("ARDOUR_DEBUG_EVENT_POOLS")) {
|
||||
PBD::stacktrace (std::cerr, 20);
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
|
|
@ -135,10 +145,13 @@ class LIBEVORAL_API EventPool
|
|||
if ((*x)->owns (p)) {
|
||||
(*x)->push_back (p);
|
||||
#ifdef DEBUG_EVENT_POOL
|
||||
std::cerr << name() << ": release size[" << (*x)->item_size << "] free = "
|
||||
std::cerr << name() << ": release " << p << " w/size[" << (*x)->item_size << "] free = "
|
||||
<< (*x)->size()
|
||||
<< " alloc = " << ((char*)(*x)->end - (char*)(*x)->block) / sizeof ((*x)->item_size)
|
||||
<< std::endl;
|
||||
if (g_getenv ("ARDOUR_DEBUG_EVENT_POOLS")) {
|
||||
PBD::stacktrace (std::cerr, 20);
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
|
@ -150,22 +163,6 @@ class LIBEVORAL_API EventPool
|
|||
std::string _name;
|
||||
};
|
||||
|
||||
class LIBEVORAL_API PoolAllocated
|
||||
{
|
||||
public:
|
||||
PoolAllocated (EventPool* p) : pool (p) {}
|
||||
|
||||
void operator delete (void *ptr) {
|
||||
PoolAllocated* pa = reinterpret_cast<PoolAllocated*>(ptr);
|
||||
if (pa && pa->pool) {
|
||||
pa->pool->release (ptr);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
EventPool* pool;
|
||||
};
|
||||
|
||||
} // namespace Evoral
|
||||
|
||||
#endif /* EVORAL_EVENTPOOL_HPP */
|
||||
|
|
|
|||
|
|
@ -26,6 +26,18 @@ namespace Evoral {
|
|||
|
||||
static event_id_t _event_id_counter = 0;
|
||||
|
||||
template<typename Time>
|
||||
EventPointer<Time>::~EventPointer ()
|
||||
{
|
||||
std::cerr << "::~EventPointer\n";
|
||||
}
|
||||
|
||||
template<typename Time>
|
||||
ManagedEvent<Time>::~ManagedEvent ()
|
||||
{
|
||||
std::cerr << "::~ManagedEvent\n";
|
||||
}
|
||||
|
||||
event_id_t
|
||||
event_id_counter()
|
||||
{
|
||||
|
|
@ -66,6 +78,10 @@ template class Event<Evoral::Beats>;
|
|||
template class Event<double>;
|
||||
template class Event<int64_t>; /* framepos_t in Ardour */
|
||||
|
||||
template class ManagedEvent<Evoral::Beats>;
|
||||
template class ManagedEvent<double>;
|
||||
template class ManagedEvent<int64_t>; /* framepos_t in Ardour */
|
||||
|
||||
template class EventPointer<Evoral::Beats>;
|
||||
template class EventPointer<int64_t>; /* framepos_t in Ardour */
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue