/* This file is part of Ingen. * Copyright (C) 2007 Dave Robillard * * Ingen 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. * * Ingen 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 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., * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define __STDC_LIMIT_MACROS 1 #include #include #include "EventBuffer.hpp" #include "lv2ext/lv2_event.h" #include "lv2ext/lv2_event_helpers.h" using namespace std; namespace Ingen { /** Allocate a new event buffer. * \a capacity is in bytes (not number of events). */ EventBuffer::EventBuffer(size_t capacity) : Buffer(DataType(DataType::EVENT), capacity) , _latest_frames(0) , _latest_subframes(0) { if (capacity > UINT32_MAX) { cerr << "Event buffer size " << capacity << " too large, aborting." << endl; throw std::bad_alloc(); } int ret = posix_memalign((void**)&_local_buf, 16, sizeof(LV2_Event_Buffer) + capacity); if (ret) { cerr << "Failed to allocate event buffer. Aborting." << endl; exit(EXIT_FAILURE); } _local_buf->event_count = 0; _local_buf->capacity = (uint32_t)capacity; _local_buf->size = 0; _local_buf->data = reinterpret_cast(_local_buf + 1); _buf = _local_buf; reset(0); //cerr << "Creating MIDI Buffer " << _buf << ", capacity = " << _buf->capacity << endl; } EventBuffer::~EventBuffer() { free(_local_buf); } /** Use another buffer's data instead of the local one. * * This buffer will essentially be identical to @a buf after this call. */ bool EventBuffer::join(Buffer* buf) { EventBuffer* mbuf = dynamic_cast(buf); if (mbuf) { _buf = mbuf->local_data(); _joined_buf = mbuf; _iter = mbuf->_iter; _iter.buf = _buf; return false; } else { return false; } //assert(mbuf->size() == _size); _joined_buf = mbuf; return true; } void EventBuffer::unjoin() { _joined_buf = NULL; _buf = _local_buf; reset(_this_nframes); } void EventBuffer::prepare_read(FrameTime start, SampleCount nframes) { //cerr << "\t" << this << " prepare_read: " << event_count() << endl; rewind(); _this_nframes = nframes; } void EventBuffer::prepare_write(FrameTime start, SampleCount nframes) { //cerr << "\t" << this << " prepare_write: " << event_count() << endl; reset(nframes); } /** FIXME: parameters ignored */ void EventBuffer::copy(const Buffer* src_buf, size_t start_sample, size_t end_sample) { const EventBuffer* src = dynamic_cast(src_buf); assert(src); assert(_buf->capacity >= src->_buf->capacity); clear(); src->rewind(); memcpy(_buf, src->_buf, src->_buf->size); } /** Increment the read position by one event. * * \return true if increment was successful, or false if end of buffer reached. */ bool EventBuffer::increment() const { if (lv2_event_is_valid(&_iter)) { lv2_event_increment(&_iter); return true; } else { return false; } } /** * \return true iff the cursor is valid (ie get_event is safe) */ bool EventBuffer::is_valid() const { return lv2_event_is_valid(&_iter); } /** Append an event to the buffer. * * \a timestamp must be >= the latest event in the buffer, * and < this_nframes() * * \return true on success */ bool EventBuffer::append(uint32_t frames, uint32_t subframes, uint16_t type, uint16_t size, const uint8_t* data) { #ifndef NDEBUG if (lv2_event_is_valid(&_iter)) { LV2_Event* last_event = lv2_event_get(&_iter, NULL); assert(last_event->frames < frames || (last_event->frames == frames && last_event->subframes <= subframes)); } #endif /*cout << "Appending event type " << type << ", size " << size << " @ " << frames << "." << subframes << endl;*/ const bool ret = lv2_event_write(&_iter, frames, subframes, type, size, data); if (!ret) cerr << "ERROR: Failed to write event." << endl; _latest_frames = frames; _latest_subframes = subframes; return ret; } /** Append a buffer of events to the buffer. * * \a timestamp must be >= the latest event in the buffer, * and < this_nframes() * * \return true on success */ bool EventBuffer::append(const LV2_Event_Buffer* buf) { uint8_t** data = NULL; bool ret = true; LV2_Event_Iterator iter; for (lv2_event_begin(&iter, _buf); lv2_event_is_valid(&iter); lv2_event_increment(&iter)) { LV2_Event* ev = lv2_event_get(&iter, data); #ifndef NDEBUG assert((ev->frames > _latest_frames) || (ev->frames == _latest_frames && ev->subframes >= _latest_subframes)); #endif if (!(ret = append(ev->frames, ev->subframes, ev->type, ev->size, *data))) { cerr << "ERROR: Failed to write event." << endl; break; } _latest_frames = ev->frames; _latest_subframes = ev->subframes; } return ret; } /** Read an event from the current position in the buffer * * \return true if read was successful, or false if end of buffer reached */ bool EventBuffer::get_event(uint32_t* frames, uint32_t* subframes, uint16_t* type, uint16_t* size, uint8_t** data) const { if (lv2_event_is_valid(&_iter)) { LV2_Event* ev = lv2_event_get(&_iter, data); *frames = ev->frames; *subframes = ev->subframes; *type = ev->type; *size = ev->size; return true; } else { return false; } } /** Clear, and merge \a a and \a b into this buffer. * * FIXME: This is slow. * * \return true if complete merge was successful */ bool EventBuffer::merge(const EventBuffer& a, const EventBuffer& b) { // Die if a merge isn't necessary as it's expensive assert(a.size() > 0 && b.size() > 0); reset(_this_nframes); a.rewind(); b.rewind(); #if 0 uint32_t a_frames; uint32_t a_subframes; uint16_t a_type; uint16_t a_size; uint8_t* a_data; uint32_t b_frames; uint32_t b_subframes; uint16_t b_type; uint16_t b_size; uint8_t* b_data; #endif cout << "FIXME: merge" << endl; #if 0 a.get_event(&a_frames, &a_subframes, &a_type, &a_size, &a_data); b.get_event(&b_frames, &b_subframes, &b_type, &b_size, &b_data); while (true) { if (a_data && (!b_data || (a_time < b_time))) { append(a_time, a_size, a_data); if (a.increment()) a.get_event(&a_time, &a_size, &a_data); else a_data = NULL; } else if (b_data) { append(b_time, b_size, b_data); if (b.increment()) b.get_event(&b_time, &b_size, &b_data); else b_data = NULL; } else { break; } } _latest_stamp = max(a_time, b_time); #endif return true; } } // namespace Ingen