new envelope module



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



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]