From 9a7379dfffaf696ff3dd3e565ad82a9da0aeaf4d Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Mon, 7 Dec 2020 02:47:26 +0100 Subject: [PATCH] Add a CircularSampleBuffer for input port scopes --- libs/ardour/ardour/circular_buffer.h | 77 ++++++++++++++ libs/ardour/circular_buffer.cc | 151 +++++++++++++++++++++++++++ libs/ardour/wscript | 1 + 3 files changed, 229 insertions(+) create mode 100644 libs/ardour/ardour/circular_buffer.h create mode 100644 libs/ardour/circular_buffer.cc diff --git a/libs/ardour/ardour/circular_buffer.h b/libs/ardour/ardour/circular_buffer.h new file mode 100644 index 0000000000..148d2b1695 --- /dev/null +++ b/libs/ardour/ardour/circular_buffer.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2020 Robin Gareus + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 more 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _ardour_circular_buffer_h_ +#define _ardour_circular_buffer_h_ + +#include "pbd/ringbuffer.h" + +#include "ardour/libardour_visibility.h" +#include "ardour/types.h" + +namespace ARDOUR { + +/** Endless ringbuffer + * + * Writing never fails, and may flush out old data. + * This is intended for an oscilloscope waveform view. + */ +class LIBARDOUR_API CircularSampleBuffer +{ +public: + CircularSampleBuffer (samplecnt_t size); + + void write (Sample const*, samplecnt_t); + bool read (Sample& s_min, Sample& s_max, samplecnt_t n_samples); + +private: + PBD::RingBuffer _rb; + CircularSampleBuffer (CircularSampleBuffer const&); +}; + +class LIBARDOUR_API CircularEventBuffer +{ +public: + struct Event { + /* up to 3 byte MIDI events, 32bit aligned */ + Event (uint8_t const* buf = 0, size_t sz = 0); + uint8_t data[3]; + uint8_t pad; + }; + + typedef std::vector EventList; + + CircularEventBuffer (samplecnt_t size); + ~CircularEventBuffer (); + + void reset (); + void write (uint8_t const*, size_t); + bool read (EventList&); + +private: + CircularEventBuffer (CircularEventBuffer const&); + + Event* _buf; + guint _size; + guint _size_mask; + gint _idx; + gint _ack; +}; + +} +#endif diff --git a/libs/ardour/circular_buffer.cc b/libs/ardour/circular_buffer.cc new file mode 100644 index 0000000000..d5e0885553 --- /dev/null +++ b/libs/ardour/circular_buffer.cc @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2020 Robin Gareus + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 more 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "ardour/circular_buffer.h" +#include "ardour/runtime_functions.h" + +using namespace ARDOUR; + +CircularSampleBuffer::CircularSampleBuffer (samplecnt_t size) + : _rb (size) +{ +} + +void +CircularSampleBuffer::write (Sample const* buf, samplecnt_t n_samples) +{ + guint ws = _rb.write_space (); + if (ws < n_samples) { + /* overwrite old data (consider a spinlock wrt ::read) */ + _rb.increment_read_idx (n_samples - ws); + } + _rb.write (buf, n_samples); +} + +bool +CircularSampleBuffer::read (Sample& s_min, Sample& s_max, samplecnt_t spp) +{ + s_min = s_max = 0; + + PBD::RingBuffer::rw_vector vec; + _rb.get_read_vector (&vec); + + if (vec.len[0] + vec.len[1] < spp) { + return false; + } + + /* immediately mark as read, allow writer to overwrite data if needed */ + _rb.increment_read_idx (spp); + + samplecnt_t to_proc = std::min (spp, (samplecnt_t)vec.len[0]); + ARDOUR::find_peaks (vec.buf[0], to_proc, &s_min, &s_max); + + to_proc = std::min (spp - to_proc, (samplecnt_t)vec.len[1]); + if (to_proc > 0) { // XXX is this check needed? + ARDOUR::find_peaks (vec.buf[1], to_proc, &s_min, &s_max); + } + + return true; +} + +CircularEventBuffer::Event::Event (uint8_t const* buf, size_t size) +{ + switch (size) { + case 0: + data[0] = 0; + data[1] = 0; + data[2] = 0; + break; + case 1: + data[0] = buf[0]; + data[1] = 0; + data[2] = 0; + break; + case 2: + data[0] = buf[0]; + data[1] = buf[1]; + data[2] = 0; + break; + default: + case 3: + data[0] = buf[0]; + data[1] = buf[1]; + data[2] = buf[2]; + break; + } + pad = 0; +} + +CircularEventBuffer::CircularEventBuffer (samplecnt_t size) +{ + guint power_of_two; + for (power_of_two = 1; 1U << power_of_two < size; ++power_of_two) {} + _size = 1 << power_of_two; + _size_mask = _size; + _size_mask -= 1; + _buf = new Event[size]; + reset (); +} + +CircularEventBuffer::~CircularEventBuffer () +{ + delete [] _buf; +} + +void +CircularEventBuffer::reset () { + g_atomic_int_set (&_idx, 0); + g_atomic_int_set (&_ack, 0); + memset ((void*)_buf, 0, _size * sizeof (Event)); +} + +void +CircularEventBuffer::write (uint8_t const* buf, size_t size) +{ + Event e (buf, size); + + guint write_idx = g_atomic_int_get (&_idx); + memcpy (&_buf[write_idx], &e, sizeof (Event)); + write_idx = (write_idx + 1) & _size_mask; + g_atomic_int_set (&_idx, write_idx); + g_atomic_int_set (&_ack, 1); +} + +bool +CircularEventBuffer::read (EventList& l) +{ + guint to_read = _size_mask; + if (!g_atomic_int_compare_and_exchange (&_ack, 1, 0)) { + return false; + } + + l.clear (); + guint priv_idx = g_atomic_int_get (&_idx); + while (priv_idx > 0) { + --priv_idx; + --to_read; + l.push_back (_buf[priv_idx]); + } + priv_idx += _size_mask; + while (to_read > 0) { + l.push_back (_buf[priv_idx]); + --priv_idx; + --to_read; + } + return true; +} diff --git a/libs/ardour/wscript b/libs/ardour/wscript index 931388df00..1a11a7ac88 100644 --- a/libs/ardour/wscript +++ b/libs/ardour/wscript @@ -56,6 +56,7 @@ libardour_sources = [ 'capturing_processor.cc', 'chan_count.cc', 'chan_mapping.cc', + 'circular_buffer.cc', 'config_text.cc', 'control_group.cc', 'control_protocol_manager.cc',