Apply MIDI looping patch from torbenh, with minor changes.
[ardour.git] / libs / ardour / midi_buffer.cc
index 5b7c789ba07fc1217f52df1de5db8594cd75d6b0..ae370cdef79c7e1b66b6a6a69bb3ae6b8a283e97 100644 (file)
@@ -27,31 +27,61 @@ static const int CPU_CACHE_ALIGN = 16; /* arguably 32 on most arches, but it mat
 #endif
 
 using namespace std;
-
-namespace ARDOUR {
+using namespace ARDOUR;
 
 
 // FIXME: mirroring for MIDI buffers?
 MidiBuffer::MidiBuffer(size_t capacity)
        : Buffer(DataType::MIDI, capacity)
-//     , _owns_data(true)
-       , _events(NULL)
-       , _data(NULL)
+       , _events(0)
+       , _data(0)
+//     , _owns_data(false)
+{
+       if (capacity) {
+               resize (_capacity);
+               silence(_capacity);
+       }
+}
+       
+MidiBuffer::~MidiBuffer()
+{
+       if (_events) {
+               free(_events);
+       }
+       if (_data) {
+               free(_data);
+       }
+}
+
+void
+MidiBuffer::resize (size_t size)
 {
-       assert(capacity > 0);
+       assert(size > 0);
+
+       if (size < _capacity) {
+               return;
+       }
+
+       if (_data) {
+               free (_data);
+       }
+
+       if (_events) {
+               free (_events);
+       }
 
        _size = 0;
+       _capacity = size;
 
 #ifdef NO_POSIX_MEMALIGN
-       _events = (MidiEvent *) malloc(sizeof(MidiEvent) * capacity);
-       _data = (Byte *) malloc(sizeof(Byte) * capacity * MAX_EVENT_SIZE);
+       _events = (Evoral::Event *) malloc(sizeof(Evoral::Event) * _capacity);
+       _data = (uint8_t *) malloc(sizeof(uint8_t) * _capacity * MAX_EVENT_SIZE);
 #else
-       posix_memalign((void**)&_events, CPU_CACHE_ALIGN, sizeof(MidiEvent) * capacity);
-       posix_memalign((void**)&_data, CPU_CACHE_ALIGN, sizeof(Byte) * capacity * MAX_EVENT_SIZE);
+       posix_memalign((void**)&_events, CPU_CACHE_ALIGN, sizeof(Evoral::Event) * _capacity);
+       posix_memalign((void**)&_data, CPU_CACHE_ALIGN, sizeof(uint8_t) * _capacity * MAX_EVENT_SIZE);
 #endif 
        assert(_data);
        assert(_events);
-       silence(_capacity);
 }
 
 void
@@ -60,16 +90,10 @@ MidiBuffer::copy(const MidiBuffer& copy)
        assert(_capacity >= copy._capacity);
        _size = 0;
 
-       for (size_t i=0; i < copy.size(); ++i)
+       for (size_t i = 0; i < copy.size(); ++i)
                push_back(copy[i]);
 }
 
-MidiBuffer::~MidiBuffer()
-{
-       free(_events);
-       free(_data);
-}
-
 
 /** Read events from @a src starting at time @a offset into the START of this buffer, for
  * time direction @a nframes.  Relative time, where 0 = start of buffer.
@@ -80,21 +104,27 @@ void
 MidiBuffer::read_from(const Buffer& src, nframes_t nframes, nframes_t offset)
 {
        assert(src.type() == DataType::MIDI);
-       const MidiBuffer& msrc = (MidiBuffer&)src;
-
-       assert(_capacity >= src.size());
+       assert(&src != this);
 
-       clear();
-       assert(_size == 0);
+       const MidiBuffer& msrc = (MidiBuffer&)src;
+       
+       assert(_capacity >= msrc.size());
 
+       if (offset == 0) {
+               clear();
+               assert(_size == 0);
+       }
+       
        // FIXME: slow
-       for (size_t i=0; i < src.size(); ++i) {
-               const MidiEvent& ev = msrc[i];
-               if (ev.time >= offset && ev.time < offset+nframes) {
-                       //cerr << "MidiBuffer::read_from got event, " << ev.time << endl;
+       for (size_t i=0; i < msrc.size(); ++i) {
+               const Evoral::MIDIEvent& ev = msrc[i];
+               if (ev.time() < offset)
+                   continue;
+               if (ev.time() < (nframes + offset)) {
+                       //cout << "MidiBuffer::read_from got event, " << int(ev.type()) << " time: " << ev.time() << " buffer size: " << _size << endl;
                        push_back(ev);
                } else {
-                       //cerr << "MidiBuffer event out of range, " << ev.time << endl;
+                       cerr << "MidiBuffer event out of range, " << ev.time() << endl;
                }
        }
 
@@ -110,16 +140,16 @@ MidiBuffer::read_from(const Buffer& src, nframes_t nframes, nframes_t offset)
  * @return false if operation failed (not enough room)
  */
 bool
-MidiBuffer::push_back(const MidiEvent& ev)
+MidiBuffer::push_back(const Evoral::MIDIEvent& ev)
 {
        if (_size == _capacity)
                return false;
 
-       Byte* const write_loc = _data + (_size * MAX_EVENT_SIZE);
+       uint8_t* const write_loc = _data + (_size * MAX_EVENT_SIZE);
 
-       memcpy(write_loc, ev.buffer, ev.size);
+       memcpy(write_loc, ev.buffer(), ev.size());
        _events[_size] = ev;
-       _events[_size].buffer = write_loc;
+       _events[_size].set_buffer(ev.size(), write_loc, false);
        ++_size;
 
        //cerr << "MidiBuffer: pushed, size = " << _size << endl;
@@ -143,12 +173,11 @@ MidiBuffer::push_back(const jack_midi_event_t& ev)
        if (_size == _capacity)
                return false;
 
-       Byte* const write_loc = _data + (_size * MAX_EVENT_SIZE);
+       uint8_t* const write_loc = _data + (_size * MAX_EVENT_SIZE);
 
        memcpy(write_loc, ev.buffer, ev.size);
-       _events[_size].time = (double)ev.time;
-       _events[_size].size = ev.size;
-       _events[_size].buffer = write_loc;
+       _events[_size].time() = (double)ev.time;
+       _events[_size].set_buffer(ev.size, write_loc, false);
        ++_size;
 
        //cerr << "MidiBuffer: pushed, size = " << _size << endl;
@@ -162,23 +191,25 @@ MidiBuffer::push_back(const jack_midi_event_t& ev)
 /** Reserve space for a new event in the buffer.
  *
  * This call is for copying MIDI directly into the buffer, the data location
- * (of sufficient size to write \a size bytes) is returned, or NULL on failure.
+ * (of sufficient size to write \a size bytes) is returned, or 0 on failure.
  * This call MUST be immediately followed by a write to the returned data
  * location, or the buffer will be corrupted and very nasty things will happen.
  */
-Byte*
+uint8_t*
 MidiBuffer::reserve(double time, size_t size)
 {
-       assert(size <= MAX_EVENT_SIZE);
+       if (size > MAX_EVENT_SIZE) {
+               cerr << "WARNING: Failed to reserve " << size << " bytes for event";
+               return 0;
+       }
 
        if (_size == _capacity)
-               return NULL;
+               return 0;
 
-       Byte* const write_loc = _data + (_size * MAX_EVENT_SIZE);
+       uint8_t* const write_loc = _data + (_size * MAX_EVENT_SIZE);
 
-       _events[_size].time = time;
-       _events[_size].size = size;
-       _events[_size].buffer = write_loc;
+       _events[_size].time() = time;
+       _events[_size].set_buffer(size, write_loc, false);
        ++_size;
 
        //cerr << "MidiBuffer: reserved, size = " << _size << endl;
@@ -193,15 +224,47 @@ void
 MidiBuffer::silence(nframes_t dur, nframes_t offset)
 {
        // FIXME use parameters
-       assert(offset == 0);
-       //assert(dur == _capacity);
+       if (offset != 0)
+               cerr << "WARNING: MidiBuffer::silence w/ offset != 0 (not implemented)" << endl;
 
-       memset(_events, 0, sizeof(MidiEvent) * _capacity);
-       memset(_data, 0, sizeof(Byte) * _capacity * MAX_EVENT_SIZE);
+       memset(_events, 0, sizeof(Evoral::Event) * _capacity);
+       memset(_data, 0, sizeof(uint8_t) * _capacity * MAX_EVENT_SIZE);
        _size = 0;
        _silent = true;
 }
 
+bool
+MidiBuffer::merge_in_place( const MidiBuffer &other )
+{
+       if( other.size() == 0 )
+               return true;
+
+       if( this->size() == 0 ) {
+               copy( other );
+               return true;
+       }
+
+       {
+               MidiBuffer merge_buffer( 0 );
+               Evoral::MIDIEvent onstack_events[_capacity];
+               uint8_t   onstack_data[_capacity * MAX_EVENT_SIZE];
+               merge_buffer._events = onstack_events;
+               merge_buffer._data = onstack_data;
+               merge_buffer._size = 0;
+
+               bool retval = merge_buffer.merge( *this, other );
+
+               copy( merge_buffer );
+
+               // set pointers to zero again, so destructor
+               // does not end in calling free() for memory
+               // on the stack;
+               merge_buffer._events = 0;
+               merge_buffer._data = 0;
+
+               return retval;
+       }
+}
 
 /** Clear, and merge \a a and \a b into this buffer.
  *
@@ -214,14 +277,18 @@ MidiBuffer::merge(const MidiBuffer& a, const MidiBuffer& b)
 {
        _size = 0;
 
-       // Die if a merge isn't necessary as it's expensive
-       assert(a.size() > 0 && b.size() > 0);
+       // This is mostly the case :(
+       if( this == &a )
+           merge_in_place( b );
+
+       if( this == &b )
+           merge_in_place( a );
 
        size_t a_index = 0;
        size_t b_index = 0;
        size_t count = a.size() + b.size();
 
-       while (count > 0 && a_index < a.size() && b_index < b.size()) {
+       while (count > 0) {
                
                if (size() == capacity()) {
                        cerr << "WARNING: MIDI buffer overrun, events lost!" << endl;
@@ -229,16 +296,16 @@ MidiBuffer::merge(const MidiBuffer& a, const MidiBuffer& b)
                }
                
                if (a_index == a.size()) {
-                       push_back(a[a_index]);
-                       ++a_index;
-               } else if (b_index == b.size()) {
                        push_back(b[b_index]);
                        ++b_index;
+               } else if (b_index == b.size()) {
+                       push_back(a[a_index]);
+                       ++a_index;
                } else {
-                       const MidiEvent& a_ev = a[a_index];
-                       const MidiEvent& b_ev = b[b_index];
+                       const Evoral::MIDIEvent& a_ev = a[a_index];
+                       const Evoral::MIDIEvent& b_ev = b[b_index];
 
-                       if (a_ev.time <= b_ev.time) {
+                       if (a_ev.time() <= b_ev.time()) {
                                push_back(a_ev);
                                ++a_index;
                        } else {
@@ -253,6 +320,3 @@ MidiBuffer::merge(const MidiBuffer& a, const MidiBuffer& b)
        return true;
 }
 
-
-} // namespace ARDOUR
-