/*
- 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
#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/event_ring_buffer.h"
+#include "ardour/libardour_visibility.h"
+#include "ardour/types.h"
+#include "ardour/midi_state_tracker.h"
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.
*
- * ALL publically accessible sizes refer to event COUNTS. What actually goes
- * on in here is none of the callers business :)
+ * This is simply a wrapper around a raw ringbuffer which writes/reads events
+ * as flat placked blobs.
+ * The buffer looks like this:
+ *
+ * [timestamp][type][size][size bytes of raw MIDI][timestamp][type][size](etc...)
*/
-class MidiRingBuffer {
+template<typename T>
+class /*LIBARDOUR_API*/ MidiRingBuffer : public 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);
- }
-
- virtual ~MidiRingBuffer() {
- delete[] _ev_buf;
- delete[] _raw_buf;
- }
-
- void reset () {
- /* !!! NOT THREAD SAFE !!! */
- g_atomic_int_set (&_write_ptr, 0);
- g_atomic_int_set (&_read_ptr, 0);
- }
-
- 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;
- }
- }
-
- 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;
- }
- }
-
- size_t capacity() const { return _size; }
-
- /** Read one event and appends it to @a out. */
- //size_t read(MidiBuffer& out);
+ /** @param size Size in bytes. */
+ MidiRingBuffer(size_t size) : EventRingBuffer<T>(size) {}
- /** Write one event (@a in) */
- size_t write(const MidiEvent& in); // deep copies in
+ inline bool read_prefix(T* time, Evoral::EventType* type, uint32_t* size);
+ inline bool read_contents(uint32_t size, uint8_t* buf);
- /** 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, nframes_t start, nframes_t end);
+ size_t read(MidiBuffer& dst, samplepos_t start, samplepos_t end, samplecnt_t offset=0, bool stop_on_overflow_in_destination=false);
+ size_t skip_to(samplepos_t start);
- /** Write all events from @a in, applying @a offset to all time stamps */
- size_t write(const MidiBuffer& in, nframes_t offset = 0);
+ void dump(std::ostream& dst);
+ void flush (samplepos_t start, samplepos_t end);
- inline void clear_event(size_t index);
+ void reset_tracker ();
+ void resolve_tracker (MidiBuffer& dst, samplepos_t);
+ void resolve_tracker (Evoral::EventSink<samplepos_t>& dst, samplepos_t);
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
-
+ MidiStateTracker _tracker;
};
-/** 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 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;
-
- 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());
+ if (PBD::RingBufferNPT<uint8_t>::read((uint8_t*)time, sizeof(T)) != sizeof (T)) {
+ return false;
+ }
- return 1;
+ if (PBD::RingBufferNPT<uint8_t>::read((uint8_t*)type, sizeof(Evoral::EventType)) != sizeof (Evoral::EventType)) {
+ return false;
}
-}
-inline size_t
-MidiRingBuffer::read(MidiBuffer& dst, nframes_t start, nframes_t end)
-{
- if (read_space() == 0)
- return 0;
-
- size_t priv_read_ptr = g_atomic_int_get(&_read_ptr);
- 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;
+ if (PBD::RingBufferNPT<uint8_t>::read((uint8_t*)size, sizeof(uint32_t)) != sizeof (uint32_t)) {
+ return false;
}
-
- g_atomic_int_set(&_read_ptr, priv_read_ptr);
-
- //printf("(R) read space: %zu\n", read_space());
- return count;
+ return true;
}
-inline size_t
-MidiRingBuffer::write(const MidiBuffer& in, nframes_t offset)
-{
- 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;
+/** 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)
+{
+ return PBD::RingBufferNPT<uint8_t>::read(buf, size) == size;
}
} // namespace ARDOUR