summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--raul/MIDIRingBuffer.h162
-rw-r--r--raul/Makefile.am5
-rw-r--r--raul/RingBuffer.h163
-rw-r--r--raul/TimeSlice.h8
-rw-r--r--raul/types.h31
-rw-r--r--tests/Makefile.am16
-rw-r--r--tests/midi_ringbuffer_test.cpp40
-rw-r--r--tests/ringbuffer_test.cpp46
8 files changed, 462 insertions, 9 deletions
diff --git a/raul/MIDIRingBuffer.h b/raul/MIDIRingBuffer.h
new file mode 100644
index 0000000..d054bbc
--- /dev/null
+++ b/raul/MIDIRingBuffer.h
@@ -0,0 +1,162 @@
+/* This file is part of Raul.
+ * Copyright (C) 2007 Dave Robillard <http://drobilla.net>
+ *
+ * Raul is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Raul 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef RAUL_MIDI_RING_BUFFER_H
+#define RAUL_MIDI_RING_BUFFER_H
+
+#include <cassert>
+#include <algorithm>
+#include <glib.h>
+#include <raul/types.h>
+#include <raul/RingBuffer.h>
+
+#include <iostream>
+
+namespace Raul {
+
+
+/** A MIDI RingBuffer
+ */
+class MIDIRingBuffer : private Raul::RingBuffer<char> {
+public:
+
+ struct MidiEvent {
+ TickTime time;
+ size_t size;
+ unsigned char* buf;
+ };
+
+
+ /** @param size Size in bytes.
+ */
+ MIDIRingBuffer(size_t size)
+ : RingBuffer<char>(size)
+ {
+ }
+
+ size_t capacity() const { return _size; }
+
+ /** Read one event and appends it to @a out. */
+ //size_t read(MidiBuffer& out);
+
+ /** 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, TickTime start, TickTime end);
+
+ /** Write one event */
+ //size_t write(const MidiEvent& in); // deep copies in
+
+ size_t write(TickTime time, size_t size, const char* buf);
+ bool read(TickTime* time, size_t* size, char* buf);
+
+};
+
+
+bool
+MIDIRingBuffer::read(TickTime* time, size_t* size, char* buf)
+{
+ bool success = RingBuffer<char>::full_read(sizeof(TickTime), (char*)time);
+ if (success)
+ success = RingBuffer<char>::full_read(sizeof(size_t), (char*)size);
+ if (success)
+ success = RingBuffer<char>::full_read(*size, buf);
+
+ return success;
+}
+
+
+inline size_t
+MIDIRingBuffer::write(TickTime time, size_t size, const char* buf)
+{
+ assert(size > 0);
+
+ if (write_space() < (sizeof(TickTime) + sizeof(size_t) + size)) {
+ return 0;
+ } else {
+ RingBuffer<char>::write(sizeof(TickTime), (char*)&time);
+ RingBuffer<char>::write(sizeof(size_t), (char*)&size);
+ RingBuffer<char>::write(size, buf);
+ return size;
+ }
+}
+
+#if 0
+inline size_t
+MIDIRingBuffer::read(MidiBuffer& dst, TickTime start, TickTime end)
+{
+ if (read_space() == 0)
+ return 0;
+
+ size_t priv_read_ptr = g_atomic_int_get(&_read_ptr);
+ TickTime 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, TickTime time)
+{
+ const size_t num_events = in.size();
+ const 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;
+}
+#endif
+
+
+} // namespace Raul
+
+#endif // RAUL_MIDI_RING_BUFFER_H
+
diff --git a/raul/Makefile.am b/raul/Makefile.am
index 94ec10b..9c9e71c 100644
--- a/raul/Makefile.am
+++ b/raul/Makefile.am
@@ -1,6 +1,7 @@
raulincludedir = $(includedir)/raul
raulinclude_HEADERS = \
+ types.h \
midi_events.h \
Array.h \
Atom.h \
@@ -22,6 +23,7 @@ raulinclude_HEADERS = \
RDFWriter.h \
SRMWQueue.h \
SRSWQueue.h \
+ RingBuffer.h \
Semaphore.h \
SharedPtr.h \
Slave.h \
@@ -30,9 +32,10 @@ raulinclude_HEADERS = \
WeakPtr.h \
TimeSlice.h \
Quantizer.h \
+ MIDIRingBuffer.h \
MIDISink.h \
SMFReader.h \
- SMFWriter.h
+ SMFWriter.h
if WITH_LIBLO
raulinclude_HEADERS += AtomLiblo.h
diff --git a/raul/RingBuffer.h b/raul/RingBuffer.h
new file mode 100644
index 0000000..beaf957
--- /dev/null
+++ b/raul/RingBuffer.h
@@ -0,0 +1,163 @@
+/* This file is part of Raul.
+ * Copyright (C) 2007 Dave Robillard <http://drobilla.net>
+ *
+ * Raul is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Raul 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef RAUL__RING_BUFFER_H
+#define RAUL__RING_BUFFER_H
+
+#include <cassert>
+#include <glib.h>
+
+namespace Raul {
+
+
+/** A lock-free RingBuffer.
+ * Read/Write realtime safe.
+ * Single-reader Single-writer thread safe.
+ */
+template <typename T>
+class RingBuffer {
+public:
+
+ /** @param size Size in bytes.
+ */
+ RingBuffer(size_t size)
+ : _size(size)
+ , _buf(new T[size])
+ {
+ reset();
+ assert(read_space() == 0);
+ assert(write_space() == size - 1);
+ }
+
+ virtual ~RingBuffer() {
+ 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;
+ }
+ }
+
+ size_t capacity() const { return _size; }
+
+ size_t read(size_t size, T* dst);
+ void write(size_t size, const T* src);
+
+ bool full_read(size_t size, T* dst);
+
+protected:
+ mutable gint _write_ptr;
+ mutable gint _read_ptr;
+
+ size_t _size; ///< Size (capacity) in bytes
+ T* _buf; ///< size, event, size, event...
+};
+
+
+/** 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<typename T>
+size_t
+RingBuffer<T>::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<typename T>
+inline void
+RingBuffer<T>::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);
+ }
+}
+
+
+template<typename T>
+bool
+RingBuffer<T>::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;
+}
+
+
+} // namespace Raul
+
+#endif // RAUL_RING_BUFFER_H
+
diff --git a/raul/TimeSlice.h b/raul/TimeSlice.h
index 7ffd8a4..dec91e5 100644
--- a/raul/TimeSlice.h
+++ b/raul/TimeSlice.h
@@ -20,17 +20,11 @@
#include <cassert>
#include <boost/utility.hpp>
+#include <raul/types.h>
namespace Raul {
-typedef uint32_t TickTime; ///< absolute time in ticks
-typedef uint32_t TickCount; ///< offset in ticks
-typedef double BeatTime;
-typedef double BeatCount;
-typedef double Seconds;
-
-
/** A duration of time, with conversion between tick time and beat time.
*
* This is a slice along a single timeline (ie t=0 in ticks and t=0 in beats
diff --git a/raul/types.h b/raul/types.h
new file mode 100644
index 0000000..2d96e09
--- /dev/null
+++ b/raul/types.h
@@ -0,0 +1,31 @@
+/* This file is part of Raul.
+ * Copyright (C) 2007 Dave Robillard <http://drobilla.net>
+ *
+ * Raul is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Raul 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef RAUL_TYPES_H
+#define RAUL_TYPES_H
+
+namespace Raul {
+
+typedef uint32_t TickTime; ///< absolute time in ticks
+typedef uint32_t TickCount; ///< offset in ticks
+typedef double BeatTime;
+typedef double BeatCount;
+typedef double Seconds;
+
+}
+
+#endif // RAUL_TYPES_H
diff --git a/tests/Makefile.am b/tests/Makefile.am
index cbc3582..af2b7f4 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -3,11 +3,23 @@ if BUILD_TESTS
AM_CXXFLAGS = -I.. -lpthread @RASQAL_CFLAGS@ @GLIBMM_CFLAGS@
ALL_LIBS = @RASQAL_LIBS@ @GLIBMM_LIBS@ ../src/libraul.la
-bin_PROGRAMS = path_test thread_test queue_test atomic_test list_test time_test quantize_test smf_test
+bin_PROGRAMS = \
+ path_test \
+ thread_test \
+ queue_test \
+ ringbuffer_test \
+ midi_ringbuffer_test \
+ atomic_test \
+ list_test \
+ time_test \
+ quantize_test \
+ smf_test
thread_test_LDADD = $(ALL_LIBS)
path_test_LDADD = $(ALL_LIBS)
queue_test_LDADD = $(ALL_LIBS)
+ringbuffer_test_LDADD = $(ALL_LIBS)
+midi_ringbuffer_test_LDADD = $(ALL_LIBS)
atomic_test_LDADD = $(ALL_LIBS)
list_test_LDADD = $(ALL_LIBS)
time_test_LDADD = $(ALL_LIBS)
@@ -17,6 +29,8 @@ smf_test_LDADD = $(ALL_LIBS)
path_test_SOURCES = path_test.cpp
thread_test_SOURCES = thread_test.cpp
queue_test_SOURCES = queue_test.cpp
+midi_ringbuffer_test_SOURCES = midi_ringbuffer_test.cpp
+ringbuffer_test_SOURCES = ringbuffer_test.cpp
atomic_test_SOURCES = atomic_test.cpp
list_test_SOURCES = list_test.cpp
time_test_SOURCES = time_test.cpp
diff --git a/tests/midi_ringbuffer_test.cpp b/tests/midi_ringbuffer_test.cpp
new file mode 100644
index 0000000..c8fc508
--- /dev/null
+++ b/tests/midi_ringbuffer_test.cpp
@@ -0,0 +1,40 @@
+#include <iostream>
+#include "raul/MIDIRingBuffer.h"
+
+using namespace std;
+using namespace Raul;
+
+void
+read_write_test(MIDIRingBuffer& rb, unsigned offset)
+{
+ TickTime t;
+ size_t size;
+ char buf[5];
+
+ snprintf(buf, 5, "%d", offset);
+ size = strlen(buf);
+
+ size_t written = rb.write(offset, size, buf);
+ assert(written == size);
+
+ for (size_t i=0; i < 4; ++i)
+ buf[i] = 0;
+
+ rb.read(&t, &size, buf);
+
+ cout << "t=" << t << ", s=" << size << ", b='" << buf << "'" << endl;
+
+}
+
+
+int
+main()
+{
+ MIDIRingBuffer rb(32);
+
+ for (size_t i=0; i < 9999; ++i)
+ read_write_test(rb, i);
+
+ return 0;
+}
+
diff --git a/tests/ringbuffer_test.cpp b/tests/ringbuffer_test.cpp
new file mode 100644
index 0000000..01f136f
--- /dev/null
+++ b/tests/ringbuffer_test.cpp
@@ -0,0 +1,46 @@
+#include <iostream>
+#include "raul/RingBuffer.h"
+
+using namespace std;
+using namespace Raul;
+
+void
+print_buf(size_t size, char* buf)
+{
+ cout << "{ ";
+ for (size_t i=0; i < size; ++i) {
+ cout << buf[i];
+ if (i < size-1)
+ cout << ", ";
+ }
+
+ cout << " }" << endl;
+}
+
+
+int
+main()
+{
+ RingBuffer<char> rb(5);
+
+ char ev[] = { 'a', 'b', 'c' };
+
+ rb.write(3, ev);
+
+ char buf[3];
+ rb.read(3, buf);
+ print_buf(3, buf);
+
+ char ev2[] = { 'd', 'e', 'f' };
+ rb.write(3, ev2);
+
+
+ size_t read = rb.read(3, buf);
+ if (read < 3)
+ rb.read(3 - read, buf + read);
+
+ print_buf(3, buf);
+
+ return 0;
+}
+