summaryrefslogtreecommitdiffstats
path: root/src/engine/internals/Delay.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/internals/Delay.cpp')
-rw-r--r--src/engine/internals/Delay.cpp208
1 files changed, 208 insertions, 0 deletions
diff --git a/src/engine/internals/Delay.cpp b/src/engine/internals/Delay.cpp
new file mode 100644
index 00000000..202ba616
--- /dev/null
+++ b/src/engine/internals/Delay.cpp
@@ -0,0 +1,208 @@
+/* This file is part of Ingen.
+ * Copyright (C) 2007-2009 Dave Robillard <http://drobilla.net>
+ *
+ * Ingen 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.
+ *
+ * Ingen 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
+ */
+
+#include <cmath>
+#include <limits.h>
+#include "raul/log.hpp"
+#include "raul/Array.hpp"
+#include "raul/Maid.hpp"
+#include "raul/midi_events.h"
+#include "shared/LV2URIMap.hpp"
+#include "internals/Delay.hpp"
+#include "AudioBuffer.hpp"
+#include "Driver.hpp"
+#include "EventBuffer.hpp"
+#include "InputPort.hpp"
+#include "InternalPlugin.hpp"
+#include "OutputPort.hpp"
+#include "PatchImpl.hpp"
+#include "ProcessContext.hpp"
+#include "ingen-config.h"
+#include "util.hpp"
+
+#define LOG(s) s << "[DelayNode] "
+
+#define CALC_DELAY(delaytime) \
+ (f_clamp (delaytime * (float)sample_rate, 1.0f, (float)(buffer_mask + 1)))
+
+using namespace std;
+using namespace Raul;
+
+namespace Ingen {
+namespace Internals {
+
+using namespace Shared;
+
+static const float MAX_DELAY_SECONDS = 8.0f;
+
+static InternalPlugin note_plugin(NS_INTERNALS "Delay", "delay");
+
+InternalPlugin& DelayNode::internal_plugin() { return note_plugin; }
+
+DelayNode::DelayNode(BufferFactory& bufs, const string& path, bool polyphonic, PatchImpl* parent, SampleRate srate)
+ : NodeBase(&note_plugin, path, polyphonic, parent, srate)
+ , _buffer(0)
+ , _buffer_length(0)
+ , _buffer_mask(0)
+ , _write_phase(0)
+{
+ const LV2URIMap& uris = Shared::LV2URIMap::instance();
+ _ports = new Raul::Array<PortImpl*>(3);
+
+ const float default_delay = 1.0f;
+ _last_delay_time = default_delay;
+ _delay_samples = default_delay;
+
+ _delay_port = new InputPort(bufs, this, "delay", 1, _polyphony, PortType::CONTROL, default_delay);
+ _delay_port->set_property(uris.lv2_name, "Delay");
+ _delay_port->set_property(uris.lv2_default, default_delay);
+ _delay_port->set_property(uris.lv2_minimum, (float)(1.0/(double)srate));
+ _delay_port->set_property(uris.lv2_maximum, MAX_DELAY_SECONDS);
+ _ports->at(0) = _delay_port;
+
+ _in_port = new InputPort(bufs, this, "in", 0, 1, PortType::AUDIO, 0.0f);
+ _in_port->set_property(uris.lv2_name, "Input");
+ _ports->at(1) = _in_port;
+
+ _out_port = new OutputPort(bufs, this, "out", 0, 1, PortType::AUDIO, 0.0f);
+ _out_port->set_property(uris.lv2_name, "Output");
+ _ports->at(2) = _out_port;
+
+ //_buffer = bufs.get(PortType::AUDIO, bufs.audio_buffer_size(buffer_length_frames), true);
+
+}
+
+
+DelayNode::~DelayNode()
+{
+ //_buffer.reset();
+ free(_buffer);
+}
+
+
+void
+DelayNode::activate(BufferFactory& bufs)
+{
+ NodeBase::activate(bufs);
+ const SampleCount min_size = MAX_DELAY_SECONDS * _srate;
+
+ // Smallest power of two larger than min_size
+ SampleCount size = 1;
+ while (size < min_size)
+ size <<= 1;
+
+ _buffer = (float*)calloc(size, sizeof(float));
+ _buffer_mask = size - 1;
+ _buffer_length = size;
+ //_buffer->clear();
+ _write_phase = 0;
+}
+
+
+static inline float f_clamp(float x, float a, float b)
+{
+ const float x1 = fabs(x - a);
+ const float x2 = fabs(x - b);
+
+ x = x1 + a + b;
+ x -= x2;
+ x *= 0.5;
+
+ return x;
+}
+
+
+static inline float cube_interp(const float fr, const float inm1, const float
+ in, const float inp1, const float inp2)
+{
+ return in + 0.5f * fr * (inp1 - inm1 +
+ fr * (4.0f * inp1 + 2.0f * inm1 - 5.0f * in - inp2 +
+ fr * (3.0f * (in - inp1) - inm1 + inp2)));
+}
+
+
+void
+DelayNode::process(ProcessContext& context)
+{
+ AudioBuffer* const delay_buf = (AudioBuffer*)_delay_port->buffer(0).get();
+ AudioBuffer* const in_buf = (AudioBuffer*)_in_port->buffer(0).get();
+ AudioBuffer* const out_buf = (AudioBuffer*)_out_port->buffer(0).get();
+
+ NodeBase::pre_process(context);
+
+ DelayNode* plugin_data = this;
+
+ const float* const in = in_buf->data();
+ float* const out = out_buf->data();
+ const float delay_time = delay_buf->data()[0];
+ const uint32_t buffer_mask = plugin_data->_buffer_mask;
+ const unsigned int sample_rate = plugin_data->_srate;
+ float delay_samples = plugin_data->_delay_samples;
+ long write_phase = plugin_data->_write_phase;
+ const uint32_t sample_count = context.nframes();
+
+ if (write_phase == 0) {
+ _last_delay_time = delay_time;
+ _delay_samples = delay_samples = CALC_DELAY(delay_time);
+ }
+
+ if (delay_time == _last_delay_time) {
+ const long idelay_samples = (long)delay_samples;
+ const float frac = delay_samples - idelay_samples;
+
+ for (uint32_t i = 0; i < sample_count; i++) {
+ long read_phase = write_phase - (long)delay_samples;
+ const float read = cube_interp(frac,
+ buffer_at(read_phase - 1),
+ buffer_at(read_phase),
+ buffer_at(read_phase + 1),
+ buffer_at(read_phase + 2));
+ buffer_at(write_phase++) = in[i];
+ out[i] = read;
+ }
+ } else {
+ const float next_delay_samples = CALC_DELAY(delay_time);
+ const float delay_samples_slope = (next_delay_samples - delay_samples) / sample_count;
+
+ for (uint32_t i = 0; i < sample_count; i++) {
+ delay_samples += delay_samples_slope;
+ write_phase++;
+ const long read_phase = write_phase - (long)delay_samples;
+ const long idelay_samples = (long)delay_samples;
+ const float frac = delay_samples - idelay_samples;
+ const float read = cube_interp(frac,
+ buffer_at(read_phase - 1),
+ buffer_at(read_phase),
+ buffer_at(read_phase + 1),
+ buffer_at(read_phase + 2));
+ buffer_at(write_phase) = in[i];
+ out[i] = read;
+ }
+
+ _last_delay_time = delay_time;
+ _delay_samples = delay_samples;
+ }
+
+ _write_phase = write_phase;
+
+ NodeBase::post_process(context);
+}
+
+
+} // namespace Internals
+} // namespace Ingen
+