X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fardour%2Fmidi_ring_buffer.h;h=14934456e132f8afc3836fdc8ec56b8f41d4a24c;hb=cd17e05e3a22614387050736c89a4727c4da0d61;hp=7f0e9f3b375578972c90ef4715a4438728d9af76;hpb=85c223da0dac610566e6461585892be1d2708660;p=ardour.git diff --git a/libs/ardour/ardour/midi_ring_buffer.h b/libs/ardour/ardour/midi_ring_buffer.h index 7f0e9f3b37..14934456e1 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,375 +21,115 @@ #include #include -#include -#include -namespace ARDOUR { +#include "evoral/EventRingBuffer.hpp" +#include "ardour/types.h" +#include "ardour/buffer.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 MidiRingBuffer : public Evoral::EventRingBuffer { public: - /** @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; - } + MidiRingBuffer(size_t size) + : Evoral::EventRingBuffer(size) + , _channel_mask(0x0000FFFF) + {} + + inline bool read_prefix(T* time, Evoral::EventType* type, uint32_t* size); + inline bool read_contents(uint32_t size, uint8_t* buf); - /** Reset(empty) the ringbuffer. - * NOT thread safe. + size_t read(MidiBuffer& dst, framepos_t start, framepos_t end, framecnt_t offset=0, bool stop_on_overflow_in_destination=false); + void dump(std::ostream& dst); + + /** Set the channel filtering mode. + * @param mask If mode is FilterChannels, each bit represents a midi channel: + * bit 0 = channel 0, bit 1 = channel 1 etc. the read and write methods will only + * process events whose channel bit is 1. + * If mode is ForceChannel, mask is simply a channel number which all events will + * be forced to while reading. */ - void reset() { - g_atomic_int_set(&_write_ptr, 0); - g_atomic_int_set(&_read_ptr, 0); + void set_channel_mode(ChannelMode mode, uint16_t mask) { + g_atomic_int_set(&_channel_mask, (uint32_t(mode) << 16) | uint32_t(mask)); } - 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; - } + ChannelMode get_channel_mode() const { + return static_cast((g_atomic_int_get(&_channel_mask) & 0xFFFF0000) >> 16); } - size_t capacity() const { return _size; } - - size_t peek(size_t size, T* dst); - bool full_peek(size_t size, T* dst); - - size_t read(size_t size, T* dst); - bool full_read(size_t size, T* dst); - - void write(size_t size, const T* src); - -protected: - mutable gint _write_ptr; - mutable gint _read_ptr; - - size_t _size; ///< Size (capacity) in bytes - T* _buf; ///< size, event, size, event... -}; - - -/** 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. - */ -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) -{ - if (read_space() < size) - 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) - 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); + uint16_t get_channel_mask() const { + return g_atomic_int_get(&_channel_mask) & 0x0000FFFF; } -} - - -/* ******************************************************************** */ - - -/** 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), _channel_mask(0xFFFF) - {} - - 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); - - void set_channel_mask(uint16_t channel_mask) { _channel_mask = channel_mask; } - uint16_t get_channel_mask() { return _channel_mask; } - protected: - inline bool is_channel_event(Byte event_type_byte) { + inline bool is_channel_event(uint8_t event_type_byte) { // mask out channel information event_type_byte &= 0xF0; // midi channel events range from 0x80 to 0xE0 return (0x80 <= event_type_byte) && (event_type_byte <= 0xE0); } - -private: - uint16_t _channel_mask; -}; + inline bool is_note_on(uint8_t event_type_byte) { + // mask out channel information + return (event_type_byte & 0xF0) == MIDI_CMD_NOTE_ON; + } -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); + inline bool is_note_off(uint8_t event_type_byte) { + // mask out channel information + return (event_type_byte & 0xF0) == MIDI_CMD_NOTE_OFF; + } - return success; -} +private: + volatile uint32_t _channel_mask; // 16 bits mode, 16 bits mask + MidiStateTracker _tracker; +}; /** 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); - - return success; -} - - -/** 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). + * by a call to read_contents (or the read pointer will be garbage). */ +template inline bool -MidiRingBuffer::read_contents(size_t size, Byte* buf) +MidiRingBuffer::read_prefix(T* time, Evoral::EventType* type, uint32_t* size) { - return MidiRingBufferBase::full_read(size, buf); -} - - -inline size_t -MidiRingBuffer::write(double time, size_t size, const Byte* buf) -{ - printf("MRB - write %#X %d %d with time %lf\n", - buf[0], buf[1], buf[2], time); + if (PBD::RingBufferNPT::read((uint8_t*)time, sizeof(T)) != sizeof (T)) { + return false; + } - assert(size > 0); - - // filter events for channels - if(is_channel_event(buf[0])) { - Byte channel_nr = buf[0] & 0x0F; - if( !(_channel_mask & (1L << channel_nr)) ) { - return 0; - } + if (PBD::RingBufferNPT::read((uint8_t*)type, sizeof(Evoral::EventType)) != sizeof (Evoral::EventType)) { + return false; } - 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; + if (PBD::RingBufferNPT::read((uint8_t*)size, sizeof(uint32_t)) != sizeof (uint32_t)) { + return false; } + return true; } -/** 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. +/** 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). */ -inline size_t -MidiRingBuffer::read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t offset) +template +inline bool +MidiRingBuffer::read_contents(uint32_t size, uint8_t* buf) { - if (read_space() == 0) - return 0; - - MIDI::Event 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; - } - - Byte first_event_byte; - if(success) - success = full_peek(sizeof(Byte), &first_event_byte); - - // could this ever happen??? - if (!success) { - std::cerr << "MRB: PEEK ERROR (first event byte)" << std::endl; - continue; - } - - // filter events for channels - if(is_channel_event(first_event_byte)) { - Byte channel_nr = first_event_byte & 0x0F; - if( !(_channel_mask & (1L << channel_nr)) ) { - return 0; - } - } - - 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; + return PBD::RingBufferNPT::read(buf, size) == size; }