diff options
Diffstat (limited to 'src/engine/internals/Delay.cpp')
-rw-r--r-- | src/engine/internals/Delay.cpp | 208 |
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(¬e_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 + |