/* This file is part of Ingen. * Copyright 2007-2011 David Robillard * * 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 #include #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 "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 Server { namespace Internals { static const float MAX_DELAY_SECONDS = 8.0f; InternalPlugin* DelayNode::internal_plugin(Shared::LV2URIMap& uris) { return new InternalPlugin(uris, NS_INTERNALS "Delay", "delay"); } DelayNode::DelayNode( InternalPlugin* plugin, BufferFactory& bufs, const std::string& path, bool polyphonic, PatchImpl* parent, SampleRate srate) : NodeImpl(plugin, path, polyphonic, parent, srate) , _buffer(0) , _buffer_length(0) , _buffer_mask(0) , _write_phase(0) { const Ingen::Shared::LV2URIMap& uris = bufs.uris(); _ports = new Raul::Array(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) { NodeImpl::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(); NodeImpl::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; NodeImpl::post_process(context); } } // namespace Internals } // namespace Server } // namespace Ingen