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