From e671a1e03df0d327691b9d13fb3bd753fb58a85c Mon Sep 17 00:00:00 2001 From: David Robillard Date: Thu, 2 Aug 2007 18:33:34 +0000 Subject: Fix MIDI patching. Remove ugly C LV2 MIDI functions and moved functionality into MidiBuffer. git-svn-id: http://svn.drobilla.net/lad/ingen@671 a436a847-0d15-0410-975c-d299462d15a1 --- src/common/lv2ext/Makefile.am | 2 +- src/common/lv2ext/lv2-midifunctions.h | 168 ---------------------------------- src/libs/engine/AudioBuffer.hpp | 2 +- src/libs/engine/Buffer.hpp | 4 +- src/libs/engine/InputPort.cpp | 3 + src/libs/engine/JackMidiDriver.cpp | 7 +- src/libs/engine/MidiBuffer.cpp | 138 +++++++++++++++++++++++----- src/libs/engine/MidiBuffer.hpp | 48 +++++----- src/libs/engine/OSCBuffer.cpp | 1 - src/libs/engine/OSCBuffer.hpp | 3 +- 10 files changed, 155 insertions(+), 221 deletions(-) delete mode 100644 src/common/lv2ext/lv2-midifunctions.h (limited to 'src') diff --git a/src/common/lv2ext/Makefile.am b/src/common/lv2ext/Makefile.am index ed382ecc..a4dceb45 100644 --- a/src/common/lv2ext/Makefile.am +++ b/src/common/lv2ext/Makefile.am @@ -1 +1 @@ -noinst_HEADERS = lv2-midiport.h lv2-midifunctions.h +noinst_HEADERS = lv2-midiport.h diff --git a/src/common/lv2ext/lv2-midifunctions.h b/src/common/lv2ext/lv2-midifunctions.h deleted file mode 100644 index 0f47ca5d..00000000 --- a/src/common/lv2ext/lv2-midifunctions.h +++ /dev/null @@ -1,168 +0,0 @@ -/**************************************************************************** - - lv2-midifunctions.h - support file for using MIDI in LV2 plugins - - Copyright (C) 2006-2007 Lars Luthman - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 01222-1307 USA - -****************************************************************************/ - -/** @file - This file contains static helper functions for the LV2 MIDI datatype - extension. -*/ - -#ifndef LV2_MIDIFUNCTIONS -#define LV2_MIDIFUNCTIONS - -#include -#include - -#include "lv2-midiport.h" - - -/** This structure contains information about a MIDI port buffer, the - current period size, and the position in the MIDI data buffer that - we are currently reading from or writing to. It needs to be recreated - or at least reinitialised every process() call. */ -typedef struct { - - /** The MIDI port structure that we want to read or write. */ - LV2_MIDI* midi; - - /** The number of frames in this process cycle. */ - uint32_t frame_count; - - /** The current position in the data buffer. Should be initialised to 0. */ - uint32_t position; - -} LV2_MIDIState; - - -inline static LV2_MIDI* lv2midi_new(uint32_t capacity) -{ - LV2_MIDI* midi = (LV2_MIDI*)malloc(sizeof(LV2_MIDI)); - - midi->event_count = 0; - midi->capacity = capacity; - midi->size = 0; - midi->data = (unsigned char*)malloc(sizeof(unsigned char) * capacity); - - return midi; -} - - -static void lv2midi_free(LV2_MIDI* midi) -{ - free(midi->data); - free(midi); -} - - -static void lv2midi_reset_buffer(LV2_MIDI* midi) -{ - midi->event_count = 0; - midi->size = 0; -} - - -static void lv2midi_reset_state(LV2_MIDIState* state, LV2_MIDI* midi, uint32_t frame_count) -{ - state->midi = midi; - state->frame_count = frame_count; - state->position = 0; -} - - -/** This function reads one event from the port associated with the @c state - parameter and writes its timestamp, size and a pointer to its data bytes - into the parameters @c timestamp, @c size and @c data, respectively. - It does not advance the read position in the MIDI data buffer, two - subsequent calls to lv2midi_get_event() will read the same event. - - The function returns the timestamp for the read event, or the @c frame_count - member of @c state if there are no more events in the buffer. */ -static double lv2midi_get_event(LV2_MIDIState* state, - double* timestamp, - uint32_t* size, - unsigned char** data) { - - if (state->position >= state->midi->size) { - state->position = state->midi->size; - *timestamp = state->frame_count; - *size = 0; - *data = NULL; - return *timestamp; - } - - *timestamp = *(double*)(state->midi->data + state->position); - *size = *(uint32_t*)(state->midi->data + state->position + sizeof(double)); - *data = state->midi->data + state->position + - sizeof(double) + sizeof(uint32_t); - return *timestamp; -} - - -/** This function advances the read/write position in @c state to the next - event and returns its timestamp, or the @c frame_count member of @c state - is there are no more events. */ -static double lv2midi_step(LV2_MIDIState* state) { - - if (state->position + sizeof(double) + sizeof(uint32_t) >= state->midi->size) { - state->position = state->midi->size; - return state->frame_count; - } - - state->position += sizeof(double); - uint32_t size = *(uint32_t*)(state->midi->data + state->position); - state->position += sizeof(uint32_t); - state->position += size; - - if (state->position >= state->midi->size) - return state->frame_count; - - return *(double*)(state->midi->data + state->position); -} - - -/** This function writes one MIDI event to the port buffer associated with - @c state. It returns 0 when the event was written successfully to the - buffer, and -1 when there was not enough room. The read/write position - is advanced automatically. */ -static int lv2midi_put_event(LV2_MIDIState* state, - double timestamp, - uint32_t size, - const unsigned char* data) { - - if (state->midi->capacity - state->midi->size < - sizeof(double) + sizeof(uint32_t) + size) - return -1; - - *(double*)(state->midi->data + state->midi->size) = timestamp; - state->midi->size += sizeof(double); - *(uint32_t*)(state->midi->data + state->midi->size) = size; - state->midi->size += sizeof(uint32_t); - memcpy(state->midi->data + state->midi->size, data, size); - state->midi->size += size; - - ++state->midi->event_count; - - return 0; -} - - -#endif - diff --git a/src/libs/engine/AudioBuffer.hpp b/src/libs/engine/AudioBuffer.hpp index d0644d27..d0379895 100644 --- a/src/libs/engine/AudioBuffer.hpp +++ b/src/libs/engine/AudioBuffer.hpp @@ -55,7 +55,7 @@ public: void prepare_read(SampleCount nframes); void prepare_write(SampleCount nframes) {} - void reset(SampleCount nframes) const {} + void rewind() const {} void resize(size_t size); void filled_size(size_t size) { _filled_size = size; } diff --git a/src/libs/engine/Buffer.hpp b/src/libs/engine/Buffer.hpp index b3f7fe3c..bdca2c07 100644 --- a/src/libs/engine/Buffer.hpp +++ b/src/libs/engine/Buffer.hpp @@ -40,8 +40,8 @@ public: /** Clear contents and reset state */ virtual void clear() = 0; - /** Reset state (ie reset read ptr), but leave contents */ - virtual void reset(SampleCount nframes) const = 0; + /** Rewing (ie reset read pointer), but leave contents unchanged */ + virtual void rewind() const = 0; virtual void prepare_read(SampleCount nframes) = 0; virtual void prepare_write(SampleCount nframes) = 0; diff --git a/src/libs/engine/InputPort.cpp b/src/libs/engine/InputPort.cpp index 1580dc81..6466dee3 100644 --- a/src/libs/engine/InputPort.cpp +++ b/src/libs/engine/InputPort.cpp @@ -182,6 +182,9 @@ InputPort::pre_process(SampleCount nframes, FrameTime start, FrameTime end) for (uint32_t i=0; i < _poly; ++i) _buffers.at(i)->prepare_read(nframes); + + if (_type == DataType::MIDI) + cerr << path() << " nevents: " << ((MidiBuffer*)_buffers.at(0))->event_count() << endl; if (!do_mixdown) { assert(_buffers.at(0)->is_joined_to((*_connections.begin())->buffer(0))); diff --git a/src/libs/engine/JackMidiDriver.cpp b/src/libs/engine/JackMidiDriver.cpp index 4d608beb..9abe83f6 100644 --- a/src/libs/engine/JackMidiDriver.cpp +++ b/src/libs/engine/JackMidiDriver.cpp @@ -85,10 +85,13 @@ JackMidiPort::prepare_block(const SampleCount block_start, const SampleCount blo jack_midi_event_t ev; jack_midi_event_get(&ev, jack_buffer, i); - patch_buf->put_event(ev.time, ev.size, ev.buffer); + bool success = patch_buf->append(ev.time, ev.size, ev.buffer); + if (!success) + cerr << "WARNING: Failed to write MIDI to port buffer, event(s) lost!" << endl; } - //cerr << "Jack MIDI got " << event_count << " events." << endl; + //if (event_count) + // cerr << "Jack MIDI got " << event_count << " events." << endl; } diff --git a/src/libs/engine/MidiBuffer.cpp b/src/libs/engine/MidiBuffer.cpp index 4d515f2e..4ec63524 100644 --- a/src/libs/engine/MidiBuffer.cpp +++ b/src/libs/engine/MidiBuffer.cpp @@ -15,6 +15,8 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#define __STDC_LIMIT_MACROS 1 +#include #include #include "MidiBuffer.hpp" @@ -22,21 +24,43 @@ using namespace std; namespace Ingen { + +/** Allocate a new MIDI buffer. + * \a capacity is in bytes (not number of events). + */ MidiBuffer::MidiBuffer(size_t capacity) : Buffer(DataType(DataType::MIDI), capacity) - , _buf(lv2midi_new((uint32_t)capacity)) , _joined_buf(NULL) { - _local_state.midi = _buf; - _state = &_local_state; - assert(_local_state.midi); + if (capacity > UINT32_MAX) { + cerr << "MIDI buffer size " << capacity << " too large, aborting." << endl; + throw std::bad_alloc(); + } + + int ret = posix_memalign((void**)&_local_buf, 16, sizeof(LV2_MIDI)); + if (ret) { + cerr << "Failed to allocate MIDI buffer. Aborting." << endl; + exit(EXIT_FAILURE); + } + + ret = posix_memalign((void**)&_local_buf->data, 16, capacity); + if (ret) { + cerr << "Failed to allocate MIDI buffer contents. Aborting." << endl; + exit(EXIT_FAILURE); + } + + _local_buf->capacity = (uint32_t)capacity; + _buf = _local_buf; reset(0); - clear(); - assert(_local_state.midi == _buf); //cerr << "Creating MIDI Buffer " << _buf << ", capacity = " << _buf->capacity << endl; } +MidiBuffer::~MidiBuffer() +{ + free(_local_buf->data); + free(_local_buf); +} /** Use another buffer's data instead of the local one. @@ -47,14 +71,18 @@ bool MidiBuffer::join(Buffer* buf) { MidiBuffer* mbuf = dynamic_cast(buf); - if (!mbuf) + if (mbuf) { + _position = mbuf->_position; + _buf = mbuf->local_data(); + _joined_buf = mbuf; + return false; + } else { return false; + } //assert(mbuf->size() == _size); _joined_buf = mbuf; - - _state = mbuf->_state; return true; } @@ -64,10 +92,7 @@ void MidiBuffer::unjoin() { _joined_buf = NULL; - _state = &_local_state; - _state->midi = _buf; - - clear(); + _buf = _local_buf; reset(_this_nframes); } @@ -86,19 +111,15 @@ MidiBuffer::is_joined_to(Buffer* buf) const void MidiBuffer::prepare_read(SampleCount nframes) { - assert(!_joined_buf || data() == _joined_buf->data()); - - reset(nframes); + rewind(); + _this_nframes = nframes; } void MidiBuffer::prepare_write(SampleCount nframes) { - clear(); reset(nframes); - - assert(!_joined_buf || data() == _joined_buf->data()); } /** FIXME: parameters ignored */ @@ -107,16 +128,91 @@ MidiBuffer::copy(const Buffer* src_buf, size_t start_sample, size_t end_sample) { MidiBuffer* src = (MidiBuffer*)src_buf; clear(); - src->reset(_this_nframes); + src->rewind(); const uint32_t frame_count = min(_this_nframes, src->this_nframes()); double time; uint32_t size; unsigned char* data; while (src->increment() < frame_count) { src->get_event(&time, &size, &data); - put_event(time, size, data); + append(time, size, data); + } +} + +/** Increment the read position by one event. + * + * Returns the timestamp of the now current event, or this_nframes if + * there are no events left. + */ +double +MidiBuffer::increment() const +{ + if (_position + sizeof(double) + sizeof(uint32_t) >= _buf->size) { + _position = _buf->size; + return _this_nframes; // hit end } + + _position += sizeof(double) + sizeof(uint32_t) + *(uint32_t*)(_buf->data + _position); + + if (_position >= _buf->size) + return _this_nframes; + else + return *(double*)(_buf->data + _position); +} + + +/** Append a MIDI event to the buffer. + * + * \a timestamp must be > the latest event in the buffer, + * and < this_nframes() + * + * \return true on success + */ +bool +MidiBuffer::append(double timestamp, + uint32_t size, + const unsigned char* data) +{ + if (_buf->capacity - _buf->size < sizeof(double) + sizeof(uint32_t) + size) + return false; + + *(double*)(_buf->data + _buf->size) = timestamp; + _buf->size += sizeof(double); + *(uint32_t*)(_buf->data + _buf->size) = size; + _buf->size += sizeof(uint32_t); + memcpy(_buf->data + _buf->size, data, size); + _buf->size += size; + + ++_buf->event_count; + + return true; +} + + +/** Read an event from the current position in the buffer + * + * \return the timestamp for the read event, or this_nframes() + * if there are no more events in the buffer. + */ +double +MidiBuffer::get_event(double* timestamp, + uint32_t* size, + unsigned char** data) const +{ + if (_position >= _buf->size) { + _position = _buf->size; + *timestamp = _this_nframes; + *size = 0; + *data = NULL; + return *timestamp; + } + + *timestamp = *(double*)(_buf->data + _position); + *size = *(uint32_t*)(_buf->data + _position + sizeof(double)); + *data = _buf->data + _position + sizeof(double) + sizeof(uint32_t); + return *timestamp; } } // namespace Ingen + diff --git a/src/libs/engine/MidiBuffer.hpp b/src/libs/engine/MidiBuffer.hpp index dbccd4e8..87504833 100644 --- a/src/libs/engine/MidiBuffer.hpp +++ b/src/libs/engine/MidiBuffer.hpp @@ -18,7 +18,7 @@ #ifndef MIDIBUFFER_H #define MIDIBUFFER_H -#include +#include #include "Buffer.hpp" #include "DataType.hpp" @@ -29,7 +29,7 @@ class MidiBuffer : public Buffer { public: MidiBuffer(size_t capacity); - ~MidiBuffer() { lv2midi_free(_local_state.midi); } + ~MidiBuffer(); void prepare_read(SampleCount nframes); void prepare_write(SampleCount nframes); @@ -39,38 +39,38 @@ public: void unjoin(); uint32_t this_nframes() const { return _this_nframes; } + uint32_t event_count() const { return _buf->event_count; } - void copy(const Buffer* src, size_t start_sample, size_t end_sample); - + inline LV2_MIDI* local_data() { return _local_buf; } + inline LV2_MIDI* data() - { return ((_joined_buf != NULL) ? _joined_buf->data() : _state->midi); } + { return ((_joined_buf != NULL) ? _joined_buf->data() : _buf); } inline const LV2_MIDI* data() const - { return ((_joined_buf != NULL) ? _joined_buf->data() : _state->midi); } - - inline void clear() - { assert(_state); assert(_state->midi); lv2midi_reset_buffer(_state->midi); _state->position = 0; } + { return ((_joined_buf != NULL) ? _joined_buf->data() : _buf); } + + void copy(const Buffer* src, size_t start_sample, size_t end_sample); - inline void reset(SampleCount nframes) const - { assert(_state); assert(_state->midi); lv2midi_reset_state(_state, _state->midi, nframes); _this_nframes = nframes; } + inline void rewind() const { _position = 0; } + inline void clear() { if (_joined_buf) reset(_this_nframes); } + inline void reset(SampleCount nframes) { + _position = 0; + _buf->event_count = 0; + _buf->size = 0; + } - inline double increment() const - { assert(_state); assert(_state->midi); return lv2midi_step(_state); } + double increment() const; - inline double get_event(double* timestamp, uint32_t* size, unsigned char** data) const - { assert(_state); assert(_state->midi); return lv2midi_get_event(_state, timestamp, size, data); } + double get_event(double* timestamp, uint32_t* size, unsigned char** data) const; - inline int put_event(double timestamp, uint32_t size, const unsigned char* data) - { assert(_state); assert(_state->midi); return lv2midi_put_event(_state, timestamp, size, data); } + bool append(double timestamp, uint32_t size, const unsigned char* data); private: - LV2_MIDIState _local_state; - LV2_MIDIState* _state; - LV2_MIDI* const _buf; - - MidiBuffer* _joined_buf; ///< Buffer to mirror, if joined - - mutable uint32_t _this_nframes; + uint32_t _this_nframes; ///< Current cycle nframes + mutable uint32_t _position; ///< Index into _buf + MidiBuffer* _joined_buf; ///< Buffer to mirror, if joined + LV2_MIDI* _buf; ///< Contents (maybe belong to _joined_buf) + LV2_MIDI* _local_buf; ///< Local contents }; diff --git a/src/libs/engine/OSCBuffer.cpp b/src/libs/engine/OSCBuffer.cpp index a5f8f3ff..44cf3235 100644 --- a/src/libs/engine/OSCBuffer.cpp +++ b/src/libs/engine/OSCBuffer.cpp @@ -69,7 +69,6 @@ OSCBuffer::unjoin() //_state = &_local_state; //_state->midi = _buf; - clear(); reset(_this_nframes); } diff --git a/src/libs/engine/OSCBuffer.hpp b/src/libs/engine/OSCBuffer.hpp index dc529d30..7e1bc451 100644 --- a/src/libs/engine/OSCBuffer.hpp +++ b/src/libs/engine/OSCBuffer.hpp @@ -32,7 +32,8 @@ public: ~OSCBuffer() { } void clear() { lv2_osc_buffer_clear(_buf); } - void reset(SampleCount nframs) const {} + void reset(SampleCount nframes) { clear(); _this_nframes = nframes; } + void rewind() const {} void prepare_read(SampleCount nframes); void prepare_write(SampleCount nframes); -- cgit v1.2.1