use a note tracker to resolve notes cut off during render by the end of the region
[ardour.git] / libs / ardour / delayline.cc
index d0a691c5ffdbcf3ed3fbefc05efde1c9ccdb8fdb..2ec56c3ae242e04cb527b300ba243ded1d9a5cc0 100644 (file)
@@ -1,21 +1,20 @@
 /*
-    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>
@@ -29,6 +28,8 @@
 #include "ardour/midi_buffer.h"
 #include "ardour/runtime_functions.h"
 
+#define MAX_BUFFER_SIZE 8192
+
 using namespace std;
 using namespace PBD;
 using namespace ARDOUR;
@@ -63,12 +64,20 @@ DelayLine::run (BufferSet& bufs, samplepos_t /* start_sample */, samplepos_t /*
        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) {
 
@@ -91,7 +100,7 @@ DelayLine::run (BufferSet& bufs, samplepos_t /* start_sample */, samplepos_t /*
                                                Sample* rb = (*bi).get ();
                                                write_to_rb (rb, i->data (), add);
                                        }
-                                       _woff = (_woff + add) % _bsiz;
+                                       _woff = (_woff + add) & _bsiz_mask;
                                        delay_diff += add;
                                }
                        } else {
@@ -102,18 +111,18 @@ DelayLine::run (BufferSet& bufs, samplepos_t /* start_sample */, samplepos_t /*
                        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) {
@@ -136,7 +145,7 @@ DelayLine::run (BufferSet& bufs, samplepos_t /* start_sample */, samplepos_t /*
                                // 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];
@@ -144,9 +153,9 @@ DelayLine::run (BufferSet& bufs, samplepos_t /* start_sample */, samplepos_t /*
                        }
 
 #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
@@ -155,8 +164,28 @@ DelayLine::run (BufferSet& bufs, samplepos_t /* start_sample */, samplepos_t /*
                /* 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 */
@@ -167,8 +196,8 @@ DelayLine::run (BufferSet& bufs, samplepos_t /* start_sample */, samplepos_t /*
                                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;
@@ -179,12 +208,16 @@ DelayLine::run (BufferSet& bufs, samplepos_t /* start_sample */, samplepos_t /*
                                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 ()) {
@@ -275,7 +308,7 @@ DelayLine::set_delay (samplecnt_t signal_delay)
                        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);
        }
 
@@ -294,14 +327,29 @@ void
 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;
        }
@@ -316,24 +364,32 @@ DelayLine::allocate_pending_buffers (samplecnt_t signal_delay, ChanCount const&
        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);
 }
 
@@ -361,6 +417,9 @@ DelayLine::configure_io (ChanCount in, ChanCount out)
        if (in.n_midi () > 0 && !_midi_buf) {
                _midi_buf.reset (new MidiBuffer (16384));
        }
+#ifndef NDEBUG
+       lm.release ();
+#endif
 
        return Processor::configure_io (in, out);
 }