do not allow smf_source's reads to stomp on cached read_end position in parent class...
[ardour.git] / libs / ardour / ardour / midi_ring_buffer.h
index 662f5a835040cbf6131d31b3eb00994e12419375..f879aa75341e668fa4c4cd335e2adbfd8a4ca27d 100644 (file)
 #ifndef __ardour_midi_ring_buffer_h__
 #define __ardour_midi_ring_buffer_h__
 
+#include <iostream>
 #include <algorithm>
-#include <ardour/types.h>
-#include <pbd/ringbufferNPT.h>
-#include <ardour/buffer.h>
+#include "ardour/types.h"
+#include "ardour/buffer.h"
+#include "evoral/EventRingBuffer.hpp"
 
 namespace ARDOUR {
 
-/** A MIDI RingBuffer
- * (necessary because MIDI events are variable sized so a generic RB won't do).
+class MidiBuffer;
+
+/** A RingBuffer for (MIDI) events.
+ *
+ * This is simply a wrapper around a raw ringbuffer which writes/reads events
+ * as flat placked blobs.
+ * The buffer looks like this:
  *
- * ALL publically accessible sizes refer to event COUNTS.  What actually goes
- * on in here is none of the callers business :)
+ * [timestamp][type][size][size bytes of raw MIDI][timestamp][type][size](etc...)
  */
-class MidiRingBuffer {
+template<typename T>
+class MidiRingBuffer : public Evoral::EventRingBuffer<T> {
 public:
-       MidiRingBuffer (size_t size)
-               : _size(size)
-               , _max_event_size(MidiBuffer::max_event_size())
-               , _ev_buf(new MidiEvent[size])
-               , _raw_buf(new RawMidi[size * _max_event_size])
-       {
-               reset ();
-               assert(read_space() == 0);
-               assert(write_space() == size - 1);
-       }
+       /** @param size Size in bytes.
+        */
+       MidiRingBuffer(size_t size)
+               : Evoral::EventRingBuffer<T>(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);
+
+       size_t read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t offset=0);
+       void dump(std::ostream& dst);
        
-       virtual ~MidiRingBuffer() {
-               delete[] _ev_buf;
-               delete[] _raw_buf;
+       /** 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 set_channel_mode(ChannelMode mode, uint16_t mask) {
+               g_atomic_int_set(&_channel_mask, (uint32_t(mode) << 16) | uint32_t(mask));
        }
 
-       void reset () {
-               /* !!! NOT THREAD SAFE !!! */
-               g_atomic_int_set (&_write_ptr, 0);
-               g_atomic_int_set (&_read_ptr, 0);
+       ChannelMode get_channel_mode() const {
+               return static_cast<ChannelMode>((g_atomic_int_get(&_channel_mask) & 0xFFFF0000) >> 16);
        }
-
-       size_t write_space () {
-               size_t w, r;
-               
-               w = g_atomic_int_get (&_write_ptr);
-               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;
-               }
+       
+       uint16_t get_channel_mask() const {
+               return g_atomic_int_get(&_channel_mask) & 0x0000FFFF;
        }
        
-       size_t read_space () {
-               size_t w, r;
-               
-               w = g_atomic_int_get (&_write_ptr);
-               r = g_atomic_int_get (&_read_ptr);
-               
-               if (w > r) {
-                       return w - r;
-               } else {
-                       return (w - r + _size) % _size;
-               }
+protected:
+       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);
        }
-
-       size_t capacity() const { return _size; }
-
-       /** Read one event and appends it to @a out. */
-       //size_t read(MidiBuffer& out);
-
-       /** Write one event (@a in) */
-       size_t write(const MidiEvent& in); // deep copies in
-
-       /** Read events all events up to time @a end into @a out, leaving stamps intact.
-        * Any events before @a start will be dropped. */
-       size_t read(MidiBuffer& out, jack_nframes_t start, jack_nframes_t end);
-
-       /** Write all events from @a in, applying @a offset to all time stamps */
-       size_t write(const MidiBuffer& in, jack_nframes_t offset = 0);
-
-       inline void clear_event(size_t index);
-
-private:
-
-       // _event_ indices
-       mutable gint _write_ptr;
-       mutable gint _read_ptr;
        
-       size_t     _size;           // size (capacity) in events
-       size_t     _max_event_size; // ratio of raw_buf size to ev_buf size
-       MidiEvent* _ev_buf;         // these point into...
-       RawMidi*   _raw_buf;        // this
-
+private:
+       volatile uint32_t _channel_mask; // 16 bits mode, 16 bits mask
 };
 
-/** Just for sanity checking */
-inline void
-MidiRingBuffer::clear_event(size_t index)
-{
-       memset(&_ev_buf[index].buffer, 0, _max_event_size);
-       _ev_buf[index].time = 0;
-       _ev_buf[index].size = 0;
-       _ev_buf[index].buffer = 0;
-
-}
 
-inline size_t
-MidiRingBuffer::write (const MidiEvent& ev)
+/** 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<typename T>
+inline bool
+MidiRingBuffer<T>::read_prefix(T* time, Evoral::EventType* type, uint32_t* size)
 {
-       //static jack_nframes_t last_write_time = 0;
-       
-       assert(ev.size > 0);
-
-       size_t priv_write_ptr = g_atomic_int_get(&_write_ptr);
-
-       if (write_space () == 0) {
-               return 0;
-       } else {
-               //assert(ev.time >= last_write_time);
-
-               const size_t raw_index = priv_write_ptr * _max_event_size;
+       bool success = Evoral::EventRingBuffer<T>::full_read(sizeof(T), (uint8_t*)time);
+       if (success)
+               success = Evoral::EventRingBuffer<T>::full_read(sizeof(Evoral::EventType), (uint8_t*)type);
+       if (success)
+               success = Evoral::EventRingBuffer<T>::full_read(sizeof(uint32_t), (uint8_t*)size);
 
-               MidiEvent* const write_ev = &_ev_buf[priv_write_ptr];
-               *write_ev = ev;
-
-               memcpy(&_raw_buf[raw_index], ev.buffer, ev.size);
-               write_ev->buffer = &_raw_buf[raw_index];
-        g_atomic_int_set(&_write_ptr, (priv_write_ptr + 1) % _size);
-               
-               printf("MRB - wrote %xd %d %d with time %u at index %zu (raw index %zu)\n",
-                       write_ev->buffer[0], write_ev->buffer[1], write_ev->buffer[2], write_ev->time,
-                       priv_write_ptr, raw_index);
-               
-               assert(write_ev->size = ev.size);
-
-               //last_write_time = ev.time;
-               //printf("(W) read space: %zu\n", read_space());
-
-               return 1;
-       }
+       return success;
 }
 
-inline size_t
-MidiRingBuffer::read(MidiBuffer& dst, jack_nframes_t start, jack_nframes_t end)
-{
-       if (read_space() == 0)
-               return 0;
-
-       size_t         priv_read_ptr = g_atomic_int_get(&_read_ptr);
-       jack_nframes_t time          = _ev_buf[priv_read_ptr].time;
-       size_t         count         = 0;
-       size_t         limit         = read_space();
-
-       while (time <= end && limit > 0) {
-               MidiEvent* const read_ev = &_ev_buf[priv_read_ptr];
-               if (time >= start) {
-                       dst.push_back(*read_ev);
-                       printf("MRB - read %#X %d %d with time %u at index %zu\n",
-                               read_ev->buffer[0], read_ev->buffer[1], read_ev->buffer[2], read_ev->time,
-                               priv_read_ptr);
-               } else {
-                       printf("MRB - SKIPPING - %#X %d %d with time %u at index %zu\n",
-                               read_ev->buffer[0], read_ev->buffer[1], read_ev->buffer[2], read_ev->time,
-                               priv_read_ptr);
-                       break;
-               }
-
-               clear_event(priv_read_ptr);
-
-               ++count;
-               --limit;
-               
-               priv_read_ptr = (priv_read_ptr + 1) % _size;
-               
-               assert(read_ev->time <= end);
-               time = _ev_buf[priv_read_ptr].time;
-       }
-       
-       g_atomic_int_set(&_read_ptr, priv_read_ptr);
-       
-       //printf("(R) read space: %zu\n", read_space());
-
-       return count;
-}
 
-inline size_t
-MidiRingBuffer::write(const MidiBuffer& in, jack_nframes_t offset)
+/** 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<typename T>
+inline bool
+MidiRingBuffer<T>::read_contents(uint32_t size, uint8_t* buf)
 {
-       size_t num_events = in.size();
-       size_t to_write = std::min(write_space(), num_events);
-
-       // FIXME: double copy :/
-       for (size_t i=0; i < to_write; ++i) {
-               MidiEvent ev = in[i];
-               ev.time += offset;
-               write(ev);
-       }
-
-       return to_write;
+       return Evoral::EventRingBuffer<T>::full_read(size, buf);
 }
 
+
 } // namespace ARDOUR
 
 #endif // __ardour_midi_ring_buffer_h__