/* * 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 (size_t size) : _rb (size) { } void CircularSampleBuffer::write (Sample const* buf, size_t n_samples) { size_t 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); } void CircularSampleBuffer::silence (size_t n_samples) { size_t ws = _rb.write_space (); if (ws < n_samples) { /* overwrite old data (consider a spinlock wrt ::read) */ _rb.increment_read_idx (n_samples - ws); } PBD::RingBuffer::rw_vector vec; _rb.get_write_vector (&vec); if (vec.len[0] >= (size_t) n_samples) { memset (vec.buf[0], 0, sizeof (Sample) * n_samples); } else { assert (vec.len[0] > 0 && vec.len[0] + vec.len[1] >= n_samples); memset (vec.buf[0], 0, sizeof (Sample) * vec.len[0]); memset (vec.buf[1], 0, sizeof (Sample) * (n_samples - vec.len[0])); } _rb.increment_write_idx (n_samples); } bool CircularSampleBuffer::read (Sample& s_min, Sample& s_max, size_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); size_t to_proc = std::min (spp, vec.len[0]); ARDOUR::find_peaks (vec.buf[0], to_proc, &s_min, &s_max); to_proc = std::min (spp - to_proc, 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 (size_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 () { _idx.store (0); _ack.store (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 = _idx.load (); memcpy (&_buf[write_idx], &e, sizeof (Event)); write_idx = (write_idx + 1) & _size_mask; _idx.store (write_idx); _ack.store (1); } bool CircularEventBuffer::read (EventList& l) { guint to_read = _size_mask; size_t canderef (1); if (!_ack.compare_exchange_strong (canderef, 0)) { return false; } l.clear (); guint priv_idx = _idx.load (); 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; }