/*
- Copyright (C) 2006, 2013 Paul Davis
- Copyright (C) 2013, 2014 Robin Gareus <robin@gareus.org>
-
- 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.,
- 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
+ * Copyright (C) 2014-2017 Robin Gareus <robin@gareus.org>
+ *
+ * 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 <assert.h>
#include <cmath>
#include "ardour/midi_buffer.h"
#include "ardour/runtime_functions.h"
+#define MAX_BUFFER_SIZE 8192
+
using namespace std;
using namespace PBD;
using namespace ARDOUR;
Glib::Threads::Mutex::Lock lm (_set_delay_mutex, Glib::Threads::TRY_LOCK);
assert (lm.locked ());
#endif
+ assert (n_samples <= MAX_BUFFER_SIZE);
const sampleoffset_t pending_delay = _pending_delay;
sampleoffset_t delay_diff = _delay - pending_delay;
const bool pending_flush = _pending_flush;
+
+ if (delay_diff == 0 && _delay == 0) {
+ return;
+ }
+
_pending_flush = false;
+ // TODO handle pending_flush.
+
/* Audio buffers */
if (_buf.size () == bufs.count ().n_audio () && _buf.size () > 0) {
Sample* rb = (*bi).get ();
write_to_rb (rb, i->data (), add);
}
- _woff = (_woff + add) % _bsiz;
+ _woff = (_woff + add) & _bsiz_mask;
delay_diff += add;
}
} else {
for (AudioDlyBuf::iterator i = _buf.begin(); i != _buf.end (); ++i) {
Sample* rb = (*i).get ();
for (uint32_t s = 0; s < fade_out_len; ++s) {
- sampleoffset_t off = (_woff + _bsiz - s) % _bsiz;
+ sampleoffset_t off = (_woff + _bsiz - s) & _bsiz_mask;
rb[off] *= s / (float) fade_out_len;
}
/* clear data in rb */
// TODO optimize this using memset
for (uint32_t s = 0; s < -delay_diff; ++s) {
- sampleoffset_t off = (_woff + _bsiz + s) % _bsiz;
+ sampleoffset_t off = (_woff + _bsiz + s) & _bsiz_mask;
rb[off] = 0.f;
}
}
- _woff = (_woff - delay_diff) % _bsiz;
+ _woff = (_woff - delay_diff) & _bsiz_mask;
/* fade-in, directly apply to input buffer */
for (BufferSet::audio_iterator i = bufs.audio_begin (); i != bufs.audio_end (); ++i) {
// TODO consider handling fade_out & fade_in separately
// if fade_out_len < fade_in_len.
for (uint32_t s = 0; s < xfade_len; ++s) {
- sampleoffset_t off = (_roff + s) % _bsiz;
+ sampleoffset_t off = (_roff + s) & _bsiz_mask;
const gain_t g = s / (float) xfade_len;
src[s] *= g;
src[s] += (1.f - g) * rb[off];
}
#ifndef NDEBUG
- sampleoffset_t check = (_roff + delay_diff) % _bsiz;
+ sampleoffset_t check = (_roff + delay_diff) & _bsiz_mask;
#endif
- _roff = (_woff + _bsiz - pending_delay) % _bsiz;
+ _roff = (_woff + _bsiz - pending_delay) & _bsiz_mask;
#ifndef NDEBUG
assert (_roff == check);
#endif
/* set new delay */
_delay = pending_delay;
+ if (pending_flush) {
+ /* fade out data after read-pointer, clear buffer until write-pointer */
+ const samplecnt_t fade_out_len = std::min (_delay, (samplecnt_t)FADE_LEN);
+
+ for (AudioDlyBuf::iterator i = _buf.begin(); i != _buf.end (); ++i) {
+ Sample* rb = (*i).get ();
+ uint32_t s = 0;
+ for (; s < fade_out_len; ++s) {
+ sampleoffset_t off = (_roff + s) & _bsiz_mask;
+ rb[off] *= 1. - (s / (float) fade_out_len);
+ }
+ for (; s < _delay; ++s) {
+ sampleoffset_t off = (_roff + s) & _bsiz_mask;
+ rb[off] = 0;
+ }
+ assert (_woff == ((_roff + s) & _bsiz_mask));
+ }
+ // TODO consider adding a fade-in to bufs
+ }
+
/* delay audio buffers */
- assert (_delay == ((_woff - _roff + _bsiz) % _bsiz));
+ assert (_delay == ((_woff - _roff + _bsiz) & _bsiz_mask));
AudioDlyBuf::iterator bi = _buf.begin ();
if (_delay == 0) {
/* do nothing */
write_to_rb (rb, i->data (), n_samples);
read_from_rb (rb, i->data (), n_samples);
}
- _roff = (_roff + n_samples) % _bsiz;
- _woff = (_woff + n_samples) % _bsiz;
+ _roff = (_roff + n_samples) & _bsiz_mask;
+ _woff = (_woff + n_samples) & _bsiz_mask;
} else {
/* only write _delay samples to ringbuffer, memmove buffer */
samplecnt_t tail = n_samples - _delay;
memmove (&src[_delay], src, tail * sizeof(Sample));
read_from_rb (rb, src, _delay);
}
- _roff = (_roff + _delay) % _bsiz;
- _woff = (_woff + _delay) % _bsiz;
+ _roff = (_roff + _delay) & _bsiz_mask;
+ _woff = (_woff + _delay) & _bsiz_mask;
}
} else {
/* set new delay for MIDI only */
_delay = pending_delay;
+
+ /* prepare for the case that an audio-port is added */
+ _woff = _delay;
+ _roff = 0;
}
if (_midi_buf.get ()) {
string_compose ("%1 set_delay to %2 samples for %3 channels\n",
name (), signal_delay, _configured_output.n_audio ()));
- if (signal_delay + 8192 + 1 > _bsiz) {
+ if (signal_delay + MAX_BUFFER_SIZE + 1 > _bsiz) {
allocate_pending_buffers (signal_delay, _configured_output);
}
DelayLine::allocate_pending_buffers (samplecnt_t signal_delay, ChanCount const& cc)
{
assert (signal_delay >= 0);
- samplecnt_t rbs = signal_delay + 8192 + 1;
+#if 1
+ /* If no buffers are required, don't allocate any.
+ * This may backfire later, allocating buffers on demand
+ * may take time and cause x-runs.
+ *
+ * The default buffersize is 4 * 16kB and - once allocated -
+ * usually sufficies for the lifetime of the delayline instance.
+ */
+ if (signal_delay == _pending_delay && signal_delay == 0) {
+ return;
+ }
+#endif
+ samplecnt_t rbs = signal_delay + MAX_BUFFER_SIZE + 1;
rbs = std::max (_bsiz, rbs);
+ uint64_t power_of_two;
+ for (power_of_two = 1; 1 << power_of_two < rbs; ++power_of_two) {}
+ rbs = 1 << power_of_two;
+
if (cc.n_audio () == _buf.size () && _bsiz == rbs) {
return;
}
- _buf.clear ();
if (cc.n_audio () == 0) {
return;
}
AudioDlyBuf::iterator bo = _buf.begin ();
AudioDlyBuf::iterator bn = pending_buf.begin ();
+ sampleoffset_t offset = (_roff <= _woff) ? 0 : rbs - _bsiz;
+
for (; bo != _buf.end () && bn != pending_buf.end(); ++bo, ++bn) {
Sample* rbo = (*bo).get ();
Sample* rbn = (*bn).get ();
- if (_roff < _woff) {
+ if (_roff == _woff) {
+ continue;
+ } else if (_roff < _woff) {
/* copy data between _roff .. _woff to new buffer */
copy_vector (&rbn[_roff], &rbo[_roff], _woff - _roff);
} else {
/* copy data between _roff .. old_size to end of new buffer, increment _roff
* copy data from 0.._woff to beginning of new buffer
*/
- sampleoffset_t offset = rbs - _bsiz;
copy_vector (&rbn[_roff + offset], &rbo[_roff], _bsiz - _roff);
copy_vector (rbn, rbo, _woff);
- _roff += offset;
- assert (_roff < rbs);
}
}
+
+ assert (signal_delay >= _pending_delay);
+ assert ((_roff <= (_woff + signal_delay - _pending_delay) & (rbs -1)) || offset > 0);
+ _roff += offset;
+ assert (_roff < rbs);
+
_bsiz = rbs;
+ _bsiz_mask = _bsiz - 1;
_buf.swap (pending_buf);
}
if (in.n_midi () > 0 && !_midi_buf) {
_midi_buf.reset (new MidiBuffer (16384));
}
+#ifndef NDEBUG
+ lm.release ();
+#endif
return Processor::configure_io (in, out);
}