X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fardour%2Fmidi_ring_buffer.h;h=652f1c49cfa22a89d6a53166cd64090071ad7df6;hb=23a2cc4b71845a61dcc01f5663dacd74f198f0c3;hp=a0078061ee20b240b374a28b20e0a0d4a3ccd55d;hpb=51c0f6c442e5507754d5bac68550b1659dcf3a04;p=ardour.git diff --git a/libs/ardour/ardour/midi_ring_buffer.h b/libs/ardour/ardour/midi_ring_buffer.h index a0078061ee..652f1c49cf 100644 --- a/libs/ardour/ardour/midi_ring_buffer.h +++ b/libs/ardour/ardour/midi_ring_buffer.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2006 Paul Davis + Copyright (C) 2006 Paul Davis 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 @@ -21,337 +21,81 @@ #include #include -#include -#include - -namespace ARDOUR { +#include "ardour/event_ring_buffer.h" +#include "ardour/libardour_visibility.h" +#include "ardour/types.h" +#include "ardour/midi_state_tracker.h" -/* FIXME: this is probably too much inlined code */ +namespace ARDOUR { +class MidiBuffer; -/** A RingBuffer. - * Read/Write realtime safe. - * Single-reader Single-writer thread safe. +/** A RingBuffer for (MIDI) events. * - * This is Raul::RingBuffer, lifted for MIDIRingBuffer to inherit from as it works - * a bit differently than PBD::Ringbuffer. This could/should be replaced with - * the PBD ringbuffer to decrease code size, but this code is tested and known to - * work, so here it sits for now... + * This is simply a wrapper around a raw ringbuffer which writes/reads events + * as flat placked blobs. + * The buffer looks like this: * - * Ignore this class, use MidiRingBuffer. + * [timestamp][type][size][size bytes of raw MIDI][timestamp][type][size](etc...) */ -template -class MidiRingBufferBase { +template +class /*LIBARDOUR_API*/ MidiRingBuffer : public EventRingBuffer { public: + /** @param size Size in bytes. */ + MidiRingBuffer(size_t size) : EventRingBuffer(size) {} - /** @param size Size in bytes. - */ - MidiRingBufferBase(size_t size) - : _size(size) - , _buf(new T[size]) - { - reset(); - assert(read_space() == 0); - assert(write_space() == size - 1); - } - - virtual ~MidiRingBufferBase() { - delete[] _buf; - } - - /** Reset(empty) the ringbuffer. - * NOT thread safe. - */ - void reset() { - g_atomic_int_set(&_write_ptr, 0); - g_atomic_int_set(&_read_ptr, 0); - } - - size_t write_space() const { - - const size_t w = g_atomic_int_get(&_write_ptr); - const size_t r = g_atomic_int_get(&_read_ptr); - - if (w > r) { - return ((r - w + _size) % _size) - 1; - } else if(w < r) { - return (r - w) - 1; - } else { - return _size - 1; - } - } - - size_t read_space() const { - - const size_t w = g_atomic_int_get(&_write_ptr); - const size_t r = g_atomic_int_get(&_read_ptr); - - if (w > r) { - return w - r; - } else { - return (w - r + _size) % _size; - } - } + inline bool read_prefix(T* time, Evoral::EventType* type, uint32_t* size); + inline bool read_contents(uint32_t size, uint8_t* buf); - size_t capacity() const { return _size; } + size_t read(MidiBuffer& dst, framepos_t start, framepos_t end, framecnt_t offset=0, bool stop_on_overflow_in_destination=false); + size_t skip_to(framepos_t start); - size_t peek(size_t size, T* dst); - bool full_peek(size_t size, T* dst); + void dump(std::ostream& dst); + void flush (framepos_t start, framepos_t end); - size_t read(size_t size, T* dst); - bool full_read(size_t size, T* dst); - - void write(size_t size, const T* src); + void reset_tracker (); + void resolve_tracker (MidiBuffer& dst, framepos_t); + void resolve_tracker (Evoral::EventSink& dst, framepos_t); -protected: - mutable gint _write_ptr; - mutable gint _read_ptr; - - size_t _size; ///< Size (capacity) in bytes - T* _buf; ///< size, event, size, event... +private: + MidiStateTracker _tracker; }; -/** Peek at the ringbuffer (read w/o advancing read pointer). - * - * Note that a full read may not be done if the data wraps around. - * Caller must check return value and call again if necessary, or use the - * full_peek method which does this automatically. +/** Read the time and size of an event. This call MUST be immediately proceeded + * by a call to read_contents (or the read pointer will be garbage). */ template -size_t -MidiRingBufferBase::peek(size_t size, T* dst) -{ - const size_t priv_read_ptr = g_atomic_int_get(&_read_ptr); - - const size_t read_size = (priv_read_ptr + size < _size) - ? size - : _size - priv_read_ptr; - - memcpy(dst, &_buf[priv_read_ptr], read_size); - - return read_size; -} - - -template -bool -MidiRingBufferBase::full_peek(size_t size, T* dst) +inline bool +MidiRingBuffer::read_prefix(T* time, Evoral::EventType* type, uint32_t* size) { - if (read_space() < size) + if (PBD::RingBufferNPT::read((uint8_t*)time, sizeof(T)) != sizeof (T)) { return false; + } - const size_t read_size = peek(size, dst); - - if (read_size < size) - peek(size - read_size, dst + read_size); - - return true; -} - - -/** Read from the ringbuffer. - * - * Note that a full read may not be done if the data wraps around. - * Caller must check return value and call again if necessary, or use the - * full_read method which does this automatically. - */ -template -size_t -MidiRingBufferBase::read(size_t size, T* dst) -{ - const size_t priv_read_ptr = g_atomic_int_get(&_read_ptr); - - const size_t read_size = (priv_read_ptr + size < _size) - ? size - : _size - priv_read_ptr; - - memcpy(dst, &_buf[priv_read_ptr], read_size); - - g_atomic_int_set(&_read_ptr, (priv_read_ptr + read_size) % _size); - - return read_size; -} - - -template -bool -MidiRingBufferBase::full_read(size_t size, T* dst) -{ - if (read_space() < size) + if (PBD::RingBufferNPT::read((uint8_t*)type, sizeof(Evoral::EventType)) != sizeof (Evoral::EventType)) { return false; - - const size_t read_size = read(size, dst); - - if (read_size < size) - read(size - read_size, dst + read_size); - - return true; -} - - -template -inline void -MidiRingBufferBase::write(size_t size, const T* src) -{ - const size_t priv_write_ptr = g_atomic_int_get(&_write_ptr); - - if (priv_write_ptr + size <= _size) { - memcpy(&_buf[priv_write_ptr], src, size); - g_atomic_int_set(&_write_ptr, (priv_write_ptr + size) % _size); - } else { - const size_t this_size = _size - priv_write_ptr; - assert(this_size < size); - assert(priv_write_ptr + this_size <= _size); - memcpy(&_buf[priv_write_ptr], src, this_size); - memcpy(&_buf[0], src+this_size, size - this_size); - g_atomic_int_set(&_write_ptr, size - this_size); } -} - - -/* ******************************************************************** */ - -/** A MIDI RingBuffer. - * - * This is timestamps and MIDI packed sequentially into a single buffer, similarly - * to LV2 MIDI. The buffer looks like this: - * - * [timestamp][size][size bytes of raw MIDI][timestamp][size][etc..] - */ -class MidiRingBuffer : public MidiRingBufferBase { -public: - - /** @param size Size in bytes. - */ - MidiRingBuffer(size_t size) - : MidiRingBufferBase(size) - {} - - size_t write(double time, size_t size, const Byte* buf); - bool read(double* time, size_t* size, Byte* buf); - - bool read_prefix(double* time, size_t* size); - bool read_contents(size_t size, Byte* buf); - - size_t read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t offset=0); -}; - - -inline bool -MidiRingBuffer::read(double* time, size_t* size, Byte* buf) -{ - bool success = MidiRingBufferBase::full_read(sizeof(double), (Byte*)time); - if (success) - success = MidiRingBufferBase::full_read(sizeof(size_t), (Byte*)size); - if (success) - success = MidiRingBufferBase::full_read(*size, buf); - - return success; -} - - -/** Read the time and size of an event. This call MUST be immediately proceeded - * by a call to read_contents (or the read pointer will be garabage). - */ -inline bool -MidiRingBuffer::read_prefix(double* time, size_t* size) -{ - bool success = MidiRingBufferBase::full_read(sizeof(double), (Byte*)time); - if (success) - success = MidiRingBufferBase::full_read(sizeof(size_t), (Byte*)size); + if (PBD::RingBufferNPT::read((uint8_t*)size, sizeof(uint32_t)) != sizeof (uint32_t)) { + return false; + } - return success; + return true; } -/** Read the contenst of an event. This call MUST be immediately preceeded - * by a call to read_prefix (or the returned even will be garabage). +/** Read the content of an event. This call MUST be immediately preceded + * by a call to read_prefix (or the returned even will be garbage). */ +template inline bool -MidiRingBuffer::read_contents(size_t size, Byte* buf) -{ - return MidiRingBufferBase::full_read(size, buf); -} - - -inline size_t -MidiRingBuffer::write(double time, size_t size, const Byte* buf) +MidiRingBuffer::read_contents(uint32_t size, uint8_t* buf) { - //printf("MRB - write %#X %d %d with time %lf\n", - // buf[0], buf[1], buf[2], time); - - assert(size > 0); - - if (write_space() < (sizeof(double) + sizeof(size_t) + size)) { - return 0; - } else { - MidiRingBufferBase::write(sizeof(double), (Byte*)&time); - MidiRingBufferBase::write(sizeof(size_t), (Byte*)&size); - MidiRingBufferBase::write(size, buf); - return size; - } + return PBD::RingBufferNPT::read(buf, size) == size; } - -/** Read a block of MIDI events from buffer. - * - * Timestamps of events returned are relative to start (ie event with stamp 0 - * occurred at start), with offset added. - */ -inline size_t -MidiRingBuffer::read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t offset) -{ - if (read_space() == 0) - return 0; - - MidiEvent ev; - - size_t count = 0; - - //printf("MRB - read %u .. %u + %u\n", start, end, offset); - - while (read_space() > sizeof(double) + sizeof(size_t)) { - - full_peek(sizeof(double), (Byte*)&ev.time()); - - if (ev.time() > end) - break; - - bool success = MidiRingBufferBase::full_read(sizeof(double), (Byte*)&ev.time()); - if (success) - success = MidiRingBufferBase::full_read(sizeof(size_t), (Byte*)&ev.size()); - - if (!success) { - std::cerr << "MRB: READ ERROR (time/size)" << std::endl; - continue; - } - - if (ev.time() >= start) { - ev.time() -= start; - Byte* write_loc = dst.reserve(ev.time(), ev.size()); - success = MidiRingBufferBase::full_read(ev.size(), write_loc); - - if (success) { - ++count; - //printf("MRB - read event at time %lf\n", ev.time); - } else { - std::cerr << "MRB: READ ERROR (data)" << std::endl; - } - - } else { - printf("MRB - SKIPPING EVENT AT TIME %f\n", ev.time()); - } - } - - //printf("(R) read space: %zu\n", read_space()); - - return count; -} - - } // namespace ARDOUR #endif // __ardour_midi_ring_buffer_h__