Attached is the new ADSR module. Features: - pretty fast - pretty stable, judging from first tests - should be easy to debug and maintain, thanks to clean structure - Attack, Decay, Release times settable from 0.4 ms to 25 s (log-scale) - Sustain level settable from 0 to 100% (of course) - nothing fancy here- Fade to 0% / 100% instead of Sustain- like in some popular hardware virtual analogues (cough cough) - settable cleanup time (delay before Synth Done signal is emitted, to account for additional delays/choruses/flangers/filters in synth output etc) - configurable behaviour for notes released before attack+decay ends (to avoid situations like when a long release starts from 100% instead of desired 10% just because someone released a key too early, before decay phase was over)
Of course you need to add it to Makefile / Makefile.plugins in order to get it to work. Sorry about that.
Any comments? -- Krzysztof Foltman
Index: bsemodernadsr.cc =================================================================== --- bsemodernadsr.cc (revision 0) +++ bsemodernadsr.cc (revision 0) @@ -0,0 +1,262 @@ +/* BSE - Bedevilled Sound Engine + * Copyright (C) 2003 Tim Janik + * Modern ADSR envelope + * Copyright (C) 2007 Krzysztof Foltman + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * A copy of the GNU Lesser General Public License should ship along + * with this library; if not, see http://www.gnu.org/copyleft/. + */ +#include "bsemodernadsr.genidl.hh" +#include <bse/bseblockutils.hh> + +namespace Bse { + +#define LOTS 100000 + +class ModernADSR : public ModernADSRBase { + /* ModernADSR module implementation */ + class Module : public SynthesisModule { + /* coefficients: */ + enum State + { + NONE, + ATTACK, + DECAY, + DECAY_AUTOOFF, + SUSTAIN, + FADE0, + FADE100, + RELEASE, + CLEANUP, + }; + int attack, decay, release, decay_release, fade, cleanup; + float sustain; + ADSRAttackMode attack_mode, effective_attack_mode; + ADSRDecayMode decay_mode, effective_decay_mode; + ADSRFadeMode fade_mode; + /* state: */ + unsigned int count, total; + State state; + float start, step, prev; + public: + void + config (ModernADSRProperties *params) + { + double f = mix_freq() / 1000.0; + float time_dr = 1.0 / (1.0 / params->time_d + 1.0 / params->time_r); + attack = dtoi(params->time_a * f); + decay = dtoi(params->time_d * f); + sustain = params->level_s * 0.01; + fade = dtoi(params->time_f * f); + release = dtoi(params->time_r * f); + decay_release = dtoi(time_dr * f); + cleanup = dtoi(params->cleanup * f); + attack_mode = params->attack_mode; + decay_mode = params->decay_mode; + fade_mode = params->fade_mode; + if (decay_mode == ADSR_DECAY_AUTOMATIC) + effective_decay_mode = (decay < release) ? ADSR_DECAY_HOLD : ADSR_DECAY_PRIMITIVE; + else + effective_decay_mode = decay_mode; + if (attack_mode == ADSR_ATTACK_AUTOMATIC) + effective_attack_mode = (decay < release) ? ADSR_ATTACK_DTZ : ADSR_ATTACK_PRIMITIVE; + else + effective_attack_mode = attack_mode; + } + void + reset () + { + state = NONE; + count = total = 0; + start = step = 0.f; + prev = 0.f; + } + + inline void + start_state (State _state, int _length, float _end) + { + state = _state; + float cur = start + step * count; + if (!_length) + { + start = _end; + step = 0.f; + timeout(); + return; + } + count = 0; + total = _length; + start = cur; + step = (_end - start) / total; + } + + void + skip_state () + { + start += step * total; + } + + void + timeout() + { + switch (state) { + case NONE: + start_state (NONE, LOTS, 0.f); + break; + case ATTACK: + { + start_state (DECAY, decay, sustain); + break; + } + case DECAY: + { + if (fade_mode == ADSR_FADE_NONE) + start_state (SUSTAIN, LOTS, sustain); + else if (fade_mode == ADSR_FADE_TO0) + start_state (FADE0, fade, 0.f); + else if (fade_mode == ADSR_FADE_TO100) + start_state (FADE100, fade, 1.f); + break; + } + case DECAY_AUTOOFF: + start_state (RELEASE, release, 0.f); + break; + case SUSTAIN: + start_state (SUSTAIN, LOTS, sustain); + break; + case FADE100: + start_state (FADE100, LOTS, 1.f); + break; + case FADE0: + start_state (CLEANUP, cleanup, 0.f); + break; + case RELEASE: + start_state (CLEANUP, cleanup, 0.f); + break; + case CLEANUP: + start_state (NONE, LOTS, 0.f); + break; + } + } + + void + note_off() + { + switch (state) { + case NONE: + start_state (NONE, LOTS, 0.f); + break; + case ATTACK: + if (effective_attack_mode == ADSR_ATTACK_MIRROR) + start_state (RELEASE, count, 0.f); + else if (effective_attack_mode == ADSR_ATTACK_DTZ) + start_state (RELEASE, decay, 0.f); + else /* ADSR_ATTACK_PRIMITIVE */ + start_state (RELEASE, release, 0.f); + break; + case DECAY: + if (effective_decay_mode == ADSR_DECAY_HOLD) + state = DECAY_AUTOOFF; + else if (effective_decay_mode == ADSR_DECAY_DTZ) + start_state (RELEASE, total - count, 0.f); + else + start_state (RELEASE, release, 0.f); + break; + case SUSTAIN: + case FADE100: + case FADE0: + start_state (RELEASE, release, 0.f); + break; + case DECAY_AUTOOFF: + case RELEASE: + case CLEANUP: + break; + } + } + + unsigned int + process_wait (unsigned int remain, float *out, const float *in) + { + unsigned int i; + for (i = 0; i < remain; i++) { + if (G_UNLIKELY (in[i] > prev)) { + prev = in[i]; + start_state (ATTACK, attack, 1.f); + return i; + } + prev = in[i]; + out[i] = 0.f; + } + return i; + } + + unsigned int + process_state (unsigned int remain, float *out, const float *in) + { + unsigned int i, c = count; + for (i = 0; i < remain; i++) { + if (G_UNLIKELY (in[i] < prev)) { + prev = in[i]; + note_off(); + return i; + } + prev = in[i]; + out[i] = start + c++ * step; + } + return i; + } + + void + process (unsigned int n_values) + { + const IStream &istr_gate = istream (ICHANNEL_GATE_IN); + const OStream &ostr_ctrl = ostream (OCHANNEL_CTRL_OUT); + const float *in_gate = istr_gate.values; + float *out_ctrl = ostr_ctrl.values; + unsigned int i = 0; + bool input_active = istr_gate.connected && istr_gate.values != const_values (0); + bool generated = false; + if (input_active || state != NONE) + { + while (i < n_values) + { + unsigned int remain = n_values - i; + if (state == NONE) + { + remain = process_wait (remain, out_ctrl + i, in_gate + i); + } else { + if (remain > (total - count)) + remain = total - count; + generated = true; + remain = process_state (remain, out_ctrl + i, in_gate + i); + count += remain; + if (count >= total) + timeout(); + } + i += remain; + } + } + if (!generated) + ostream_set (OCHANNEL_CTRL_OUT, const_values (0)); + ostream_set (OCHANNEL_DONE_OUT, const_values (generated ? 0.f : 1.f)); + } + }; +public: + /* implement creation and config methods for synthesis Module */ + BSE_EFFECT_INTEGRATE_MODULE (ModernADSR, Module, ModernADSRProperties); +}; + +BSE_CXX_DEFINE_EXPORTS(); +BSE_CXX_REGISTER_ALL_TYPES_FROM_BSEMODERNADSR_IDL(); + +} // Bse Index: bsemodernadsr.idl =================================================================== --- bsemodernadsr.idl (revision 0) +++ bsemodernadsr.idl (revision 0) @@ -0,0 +1,67 @@ +/* BseModernADSR - Configurable ADSR Envelope + * Copyright (c) 2007 Krzysztof Foltman + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library 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 Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ +#include <bse/bse.idl> + +namespace Bse { + +choice ADSRAttackMode { + ADSR_ATTACK_AUTOMATIC = (_("Automatic"), _("Try to select most 'musically useful' behaviour")), + ADSR_ATTACK_MIRROR = (_("Mirror"), _("Use note length to date as release time (release = mirror of attack)")), + ADSR_ATTACK_DTZ = (_("Decay to zero"), _("Use decay time as release time")), + ADSR_ATTACK_PRIMITIVE = (_("Primitive"), _("Use normal release time starting from current level")), +}; + +choice ADSRDecayMode { + ADSR_DECAY_AUTOMATIC = (_("Automatic"), _("Try to select most 'musically useful' behaviour")), + ADSR_DECAY_HOLD = (_("Hold decay"), _("Do not interrupt the decay stage on key release")), + ADSR_DECAY_DTZ = (_("Decay to zero"), _("Use remaining decay time as release time")), + ADSR_DECAY_PRIMITIVE = (_("Primitive"), _("Use normal release time immediately from current level (like SF2)")), +}; + +choice ADSRFadeMode { + ADSR_FADE_NONE = (_("No fade"), _("Keep the constant sustain level")), + ADSR_FADE_TO0 = (_("Fade to 0%"), _("Fade to 0% for specified time")), + ADSR_FADE_TO100 = (_("Fade to 100%"), _("Fade to 100% for specified time")), +}; + +class ModernADSR : Effect { + Info icon = "icons/madsr.png"; + Info authors = "Krzysztof Foltman"; + Info license = _("GNU Lesser General Public License"); + Info category = _("Other Sources/Modern ADSR"); + Info blurb = _("ADSR envelope with added fade time parameter"); + IStream gate_in = (_("Gate In"), _("Gate input (activates/deactivates envelope)")); + OStream ctrl_out = (_("Ctrl Out"), _("Envelope output")); + OStream done_out = (_("Done Out"), _("This signal goes high after the release phase has completed")); + group _("Parameters") { + Real time_a = LogScale (_("Attack [ms]"), _("Attack time (zero to 100%)"), 0.4, 0, 25000.0, 10, 100.0, 250.0, 1, ":scale" STANDARD); + Real time_d = LogScale (_("Decay [ms]"), _("Decay time (100% to sustain)"), 100, 0, 25000.0, 10, 100.0, 250.0, 1, ":scale" STANDARD); + Real level_s = Perc (_("Sustain level (%)"), _("Sustain level"), 30, ":scale" STANDARD); + Real time_r = LogScale (_("Release [ms]"), _("Release time (sustain to 0%)"), 1000, 0, 25000.0, 10, 100.0, 250.0, 1, ":scale" STANDARD); + }; + group _("Advanced") { + ADSRAttackMode attack_mode = Choice (_("Attack Mode"), _("What to do when a key is released during attack phase"), ADSR_ATTACK_AUTOMATIC, STANDARD); + ADSRDecayMode decay_mode = Choice (_("Decay Mode"), _("What to do when a key is released during decay phase"), ADSR_DECAY_AUTOMATIC, STANDARD); + ADSRFadeMode fade_mode = Choice (_("Fade Mode"), _("What happens during sustain phase"), ADSR_FADE_NONE, STANDARD); + Real time_f = LogScale (_("Fade [ms]"), _("Fade time (sustain to 0% or sustain to 100%)"), 0, 0, 25000.0, 10, 100.0, 250.0, 1, ":scale" STANDARD); + Real cleanup = Real (_("Cleanup [ms]"), _("Delay before Done signal is activated"), 0, 0, 100, 100, ":scale" STANDARD); + }; +}; + +} // Bse Index: icons/madsr.png =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: icons/madsr.png ___________________________________________________________________ Name: svn:mime-type + application/octet-stream
Attachment:
madsr.png
Description: PNG image