diff options
author | David Robillard <d@drobilla.net> | 2007-03-31 01:40:11 +0000 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2007-03-31 01:40:11 +0000 |
commit | 423cbc959a04b6d54365b943b22936ec6b2a4b62 (patch) | |
tree | 9e349380384b0e8b5532a6507f32e21cfe20a5b9 | |
parent | 9e81a81484aa0142f0c02cf066c41bd2d2c84eea (diff) | |
download | raul-423cbc959a04b6d54365b943b22936ec6b2a4b62.tar.gz raul-423cbc959a04b6d54365b943b22936ec6b2a4b62.tar.bz2 raul-423cbc959a04b6d54365b943b22936ec6b2a4b62.zip |
Added RingBuffer and MIDIRingBuffer classes/tests.
git-svn-id: http://svn.drobilla.net/lad/raul@382 a436a847-0d15-0410-975c-d299462d15a1
-rw-r--r-- | raul/MIDIRingBuffer.h | 162 | ||||
-rw-r--r-- | raul/Makefile.am | 5 | ||||
-rw-r--r-- | raul/RingBuffer.h | 163 | ||||
-rw-r--r-- | raul/TimeSlice.h | 8 | ||||
-rw-r--r-- | raul/types.h | 31 | ||||
-rw-r--r-- | tests/Makefile.am | 16 | ||||
-rw-r--r-- | tests/midi_ringbuffer_test.cpp | 40 | ||||
-rw-r--r-- | tests/ringbuffer_test.cpp | 46 |
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; +} + |