X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fmidi_buffer.cc;h=cbf7603c96020f54a80e84cfaa121a4473608de3;hb=d37a00ae137a4292d4bf389357b9c3d093b8239e;hp=f1f6b9b633450e8b8e8b84fc37aaf8326923b266;hpb=a2d2f738cb63dbf0fb89e0a00c424ce883fb7888;p=ardour.git diff --git a/libs/ardour/midi_buffer.cc b/libs/ardour/midi_buffer.cc index f1f6b9b633..cbf7603c96 100644 --- a/libs/ardour/midi_buffer.cc +++ b/libs/ardour/midi_buffer.cc @@ -1,60 +1,47 @@ /* - Copyright (C) 2006-2007 Paul Davis + Copyright (C) 2006-2007 Paul Davis Author: Dave Robillard - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include -#include - -#ifdef __x86_64__ -static const int CPU_CACHE_ALIGN = 64; -#else -static const int CPU_CACHE_ALIGN = 16; /* arguably 32 on most arches, but it matters less */ -#endif +#include "pbd/malign.h" +#include "ardour/midi_buffer.h" using namespace std; using namespace ARDOUR; - // FIXME: mirroring for MIDI buffers? MidiBuffer::MidiBuffer(size_t capacity) : Buffer(DataType::MIDI, capacity) - , _events(0) , _data(0) -// , _owns_data(false) { if (capacity) { - resize (_capacity); + resize(_capacity); silence(_capacity); } } - + MidiBuffer::~MidiBuffer() { - if (_events) { - free(_events); - } - if (_data) { - free(_data); - } + free(_data); } void -MidiBuffer::resize (size_t size) +MidiBuffer::resize(size_t size) { assert(size > 0); @@ -62,71 +49,66 @@ MidiBuffer::resize (size_t size) return; } - if (_data) { - free (_data); - } - - if (_events) { - free (_events); - } + free(_data); _size = 0; _capacity = size; + cache_aligned_malloc ((void**) &_data, _capacity); -#ifdef NO_POSIX_MEMALIGN - _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(Evoral::Event) * _capacity); - posix_memalign((void**)&_data, CPU_CACHE_ALIGN, sizeof(uint8_t) * _capacity * MAX_EVENT_SIZE); -#endif assert(_data); - assert(_events); } void MidiBuffer::copy(const MidiBuffer& copy) { - assert(_capacity >= copy._capacity); - _size = 0; - - for (size_t i = 0; i < copy.size(); ++i) - push_back(copy[i]); + assert(_capacity >= copy._size); + _size = copy._size; + memcpy(_data, copy._data, copy._size); } /** 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. + * time duration @a nframes. Relative time, where 0 = start of buffer. * * Note that offset and nframes refer to sample time, NOT buffer offsets or event counts. */ void -MidiBuffer::read_from(const Buffer& src, nframes_t nframes, nframes_t offset) +MidiBuffer::read_from (const Buffer& src, nframes_t nframes, nframes_t dst_offset, nframes_t src_offset) { - assert(src.type() == DataType::MIDI); - assert(&src != this); - - const MidiBuffer& msrc = (MidiBuffer&)src; - - assert(_capacity >= msrc.size()); - - clear(); - assert(_size == 0); - - // FIXME: slow - for (size_t i=0; i < msrc.size(); ++i) { - const Evoral::MIDIEvent& ev = msrc[i]; - if (ev.time() >= offset && ev.time() < offset+nframes) { - //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; + assert (src.type() == DataType::MIDI); + assert (&src != this); + + const MidiBuffer& msrc = (MidiBuffer&) src; + + assert (_capacity >= msrc.size()); + + if (dst_offset == 0) { + clear (); + assert (_size == 0); + } + + /* XXX use dst_offset somehow */ + + for (MidiBuffer::const_iterator i = msrc.begin(); i != msrc.end(); ++i) { + const Evoral::MIDIEvent ev(*i, false); + if (ev.time() >= src_offset && ev.time() < (nframes+src_offset)) { + push_back (ev); } } _silent = src.silent(); } +void +MidiBuffer::merge_from (const Buffer& src, nframes_t /*nframes*/, nframes_t /*dst_offset*/, nframes_t /*src_offset*/) +{ + const MidiBuffer* mbuf = dynamic_cast(&src); + assert (mbuf); + assert (mbuf != this); + + /* XXX use nframes, and possible offsets */ + merge_in_place (*mbuf); +} /** Push an event into the buffer. * @@ -136,20 +118,53 @@ 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 Evoral::MIDIEvent& ev) +MidiBuffer::push_back(const Evoral::MIDIEvent& ev) { - if (_size == _capacity) + const size_t stamp_size = sizeof(TimeType); + /*cerr << "MidiBuffer: pushing event @ " << ev.time() + << " size = " << ev.size() << endl;*/ + + if (_size + stamp_size + ev.size() >= _capacity) { + cerr << "MidiBuffer::push_back failed (buffer is full)" << endl; + return false; + } + + if (!Evoral::midi_event_is_valid(ev.buffer(), ev.size())) { + cerr << "WARNING: MidiBuffer ignoring illegal MIDI event" << endl; return false; + } - uint8_t* const write_loc = _data + (_size * MAX_EVENT_SIZE); + push_back(ev.time(), ev.size(), ev.buffer()); - memcpy(write_loc, ev.buffer(), ev.size()); - _events[_size] = ev; - _events[_size].set_buffer(ev.size(), write_loc, false); - ++_size; + return true; +} - //cerr << "MidiBuffer: pushed, size = " << _size << endl; +/** Push an event into the buffer. + * @return false if operation failed (not enough room) + */ +bool +MidiBuffer::push_back(TimeType time, size_t size, const uint8_t* data) +{ + const size_t stamp_size = sizeof(TimeType); + /*cerr << "MidiBuffer: pushing event @ " << ev.time() + << " size = " << ev.size() << endl;*/ + + if (_size + stamp_size + size >= _capacity) { + cerr << "MidiBuffer::push_back failed (buffer is full)" << endl; + return false; + } + + if (!Evoral::midi_event_is_valid(data, size)) { + cerr << "WARNING: MidiBuffer ignoring illegal MIDI event" << endl; + return false; + } + + uint8_t* const write_loc = _data + _size; + *((TimeType*)write_loc) = time; + memcpy(write_loc + stamp_size, data, size); + + _size += stamp_size + size; _silent = false; return true; @@ -166,18 +181,22 @@ MidiBuffer::push_back(const Evoral::MIDIEvent& ev) bool MidiBuffer::push_back(const jack_midi_event_t& ev) { - if (_size == _capacity) + const size_t stamp_size = sizeof(TimeType); + if (_size + stamp_size + ev.size >= _capacity) { + cerr << "MidiBuffer::push_back failed (buffer is full)" << endl; return false; + } - 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].set_buffer(ev.size, write_loc, false); - ++_size; + if (!Evoral::midi_event_is_valid(ev.buffer, ev.size)) { + cerr << "WARNING: MidiBuffer ignoring illegal MIDI event" << endl; + return false; + } - //cerr << "MidiBuffer: pushed, size = " << _size << endl; + uint8_t* const write_loc = _data + _size; + *((TimeType*)write_loc) = ev.time; + memcpy(write_loc + stamp_size, ev.buffer, ev.size); + _size += stamp_size + ev.size; _silent = false; return true; @@ -192,24 +211,21 @@ MidiBuffer::push_back(const jack_midi_event_t& ev) * location, or the buffer will be corrupted and very nasty things will happen. */ uint8_t* -MidiBuffer::reserve(double time, size_t size) +MidiBuffer::reserve(TimeType time, size_t size) { - if (size > MAX_EVENT_SIZE) { - cerr << "WARNING: Failed to reserve " << size << " bytes for event"; + const size_t stamp_size = sizeof(TimeType); + if (_size + stamp_size + size >= _capacity) { return 0; } - if (_size == _capacity) - return 0; - - uint8_t* const write_loc = _data + (_size * MAX_EVENT_SIZE); - - _events[_size].time() = time; - _events[_size].set_buffer(size, write_loc, false); - ++_size; + // write timestamp + uint8_t* write_loc = _data + _size; + *((TimeType*)write_loc) = time; - //cerr << "MidiBuffer: reserved, size = " << _size << endl; + // move write_loc to begin of MIDI buffer data to write to + write_loc += stamp_size; + _size += stamp_size + size; _silent = false; return write_loc; @@ -217,22 +233,128 @@ MidiBuffer::reserve(double time, size_t size) void -MidiBuffer::silence(nframes_t dur, nframes_t offset) +MidiBuffer::silence (nframes_t /*nframes*/, nframes_t /*offset*/) { - // FIXME use parameters - if (offset != 0) - cerr << "WARNING: MidiBuffer::silence w/ offset != 0 (not implemented)" << endl; + /* XXX iterate over existing events, find all in range given by offset & nframes, + and delete them. + */ - memset(_events, 0, sizeof(Evoral::Event) * _capacity); - memset(_data, 0, sizeof(uint8_t) * _capacity * MAX_EVENT_SIZE); _size = 0; _silent = true; } +/** Merge \a other into this buffer. Realtime safe. */ +bool +MidiBuffer::merge_in_place(const MidiBuffer &other) +{ + if (other.size() == 0) { + return true; + } + + if (_size == 0) { + copy(other); + return true; + } + + if (_size + other.size() > _capacity) { + cerr << "MidiBuffer::merge failed (no space)" << endl; + return false; + } + +#ifndef NDEBUG + size_t test_orig_us_size = _size; + size_t test_orig_them_size = other._size; + TimeType test_time = 0; + size_t test_us_count = 0; + size_t test_them_count = 0; + for (iterator i = begin(); i != end(); ++i) { + assert(Evoral::midi_event_is_valid((*i).buffer(), (*i).size())); + assert((*i).time() >= test_time); + test_time = (*i).time(); + ++test_us_count; + } + test_time = 0; + for (const_iterator i = other.begin(); i != other.end(); ++i) { + assert(Evoral::midi_event_is_valid((*i).buffer(), (*i).size())); + assert((*i).time() >= test_time); + test_time = (*i).time(); + ++test_them_count; + } +#endif + + const_iterator them = other.begin(); + iterator us = begin(); + + while (them != other.end()) { + + size_t sz = 0; + ssize_t src = -1; + + /* gather up total size of events that are earlier than + the event referenced by "us" + */ + + while (them != other.end() && (*them).time() <= (*us).time()) { + if (src == -1) { + src = them.offset; + } + sz += sizeof (TimeType) + (*them).size(); + ++them; + } + + if (us != end()) + cerr << "us @ " << (*us).time() << endl; + if (them != other.end()) + cerr << "them @ " << (*them).time() << endl; + + if (sz) { + assert(src >= 0); + /* move existing */ + memmove (_data + us.offset + sz, _data + us.offset, _size - us.offset); + /* increase _size */ + _size += sz; + assert(_size <= _capacity); + /* insert new stuff */ + memcpy (_data + us.offset, other._data + src, sz); + /* update iterator to our own events. this is a miserable hack */ + us.offset += sz; + } else { + + /* advance past our own events to get to the correct insertion + point for the next event(s) from "other" + */ + + while (us != end() && (*us).time() < (*them).time()) { + ++us; + } + } + + if (!(us != end())) { + /* just append the rest of other */ + memcpy (_data + us.offset, other._data + them.offset, other._size - them.offset); + _size += other._size - them.offset; + break; + } + } + +#ifndef NDEBUG + assert(_size == test_orig_us_size + test_orig_them_size); + size_t test_final_count = 0; + test_time = 0; + for (iterator i = begin(); i != end(); ++i) { + cerr << "CHECK " << test_final_count << " / " << test_us_count + test_them_count << endl; + assert(Evoral::midi_event_is_valid((*i).buffer(), (*i).size())); + assert((*i).time() >= test_time); + test_time = (*i).time(); + ++test_final_count; + } + assert(test_final_count = test_us_count + test_them_count); +#endif + + return true; +} /** Clear, and merge \a a and \a b into this buffer. - * - * FIXME: This is slow. * * \return true if complete merge was successful */ @@ -241,40 +363,38 @@ 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); + if (this == &a) { + return merge_in_place(b); + } else if (this == &b) { + return merge_in_place(a); + } - size_t a_index = 0; - size_t b_index = 0; - size_t count = a.size() + b.size(); + const_iterator ai = a.begin(); + const_iterator bi = b.begin(); - while (count > 0 && a_index < a.size() && b_index < b.size()) { - - if (size() == capacity()) { - cerr << "WARNING: MIDI buffer overrun, events lost!" << endl; - return false; - } - - 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; + resize(a.size() + b.size()); + while (ai != a.end() && bi != b.end()) { + if ((*ai).time() < (*bi).time()) { + memcpy(_data + _size, (*ai).buffer(), (*ai).size()); + _size += (*ai).size(); + ++ai; } else { - const Evoral::MIDIEvent& a_ev = a[a_index]; - const Evoral::MIDIEvent& b_ev = b[b_index]; - - if (a_ev.time() <= b_ev.time()) { - push_back(a_ev); - ++a_index; - } else { - push_back(b_ev); - ++b_index; - } + memcpy(_data + _size, (*bi).buffer(), (*bi).size()); + _size += (*bi).size(); + ++bi; } + } + + while (ai != a.end()) { + memcpy(_data + _size, (*ai).buffer(), (*ai).size()); + _size += (*ai).size(); + ++ai; + } - --count; + while (bi != b.end()) { + memcpy(_data + _size, (*bi).buffer(), (*bi).size()); + _size += (*bi).size(); + ++bi; } return true;