BogaudioModules

BogaudioModules for VCV Rack
Log | Files | Refs | README | LICENSE

commit e281c970770c2c2ddb8880059caf323c4d195012
parent 3f35fd302b67a22d804b1218736ddb1e7bc4f3c7
Author: Matt Demanett <matt@demanett.net>
Date:   Thu, 14 May 2020 21:28:47 -0400

AD: fix retrigger; in loop mode, make retrigger a sync. #117

Diffstat:
MREADME.md | 8++++++--
Msrc/AD.cpp | 13++++++++++++-
Msrc/dsp/envelope.cpp | 15+++++++++++++++
Msrc/dsp/envelope.hpp | 1+
4 files changed, 34 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md @@ -283,9 +283,13 @@ _Polyphony:_ <a href="#polyphony">Polyphonic</a>, with channels defined by the T #### <a name="ad"></a> AD -An AD (Attack, Decay) envelope generator in 3HP. The attack and decay are exponentially scaled with durations up to 10 seconds. The attack and decay times have CV inputs expecting 0-10V inputs; if a CV is present, the corresponding knob attenuates it. +An AD (Attack, Decay) envelope generator in 3HP. The attack and decay stages have durations up to 10 seconds, and CV inputs expecting 0-10V inputs; if a CV is present, the corresponding knob attenuates it. -When a trigger voltage is received at the TRIG input, the envelope cycle begins; whether the module will retrigger or not if a new trigger is received before the envelope cycle repeats is controlled by the RT toggle. When the cycle completes, a trigger is emitted at EOC, and the cycle retriggers if the TRIG voltage is high. If loop mode is enabled by the LP toggle, the envelope cycles continuously regardless of the TRIG input. +When a trigger or gate is received at the TRIG input, the envelope cycle begins and runs to its end. At the end of the cycle, a pulse is emitted at EOC. + +If the RT (retrigger) toggle is enabled, if TRIG receives a new trigger while the envelope is decaying, it reenters the attack stage at whatever level the envelope is currently at. If the cycle ends and the TRIG voltage is high, the cycle restarts. + +If the LP (loop) toggle is enabled, the envelope cycles continuously (it doesn't need a trigger to start it). If RT is also enabled, triggers at TRIG will restart the cycle (this is similar to syncing an LFO). By default, the attack and decay envelope segments have an exponential curve -- in linear mode (the LIN toggle), the segments are linear. diff --git a/src/AD.cpp b/src/AD.cpp @@ -91,8 +91,19 @@ void AD::processChannel(const ProcessArgs& args, int c) { Engine& e = *_engines[c]; bool start = e.trigger.process(inputs[TRIGGER_INPUT].getVoltage(c)); - if (!e.on && ((start || (_retriggerMode && e.trigger.isHigh())) || (_loopMode && e.envelope.isStage(ADSR::STOPPED_STAGE)))) { + if (!e.on && ( + start || + (_retriggerMode && e.trigger.isHigh()) || + (_loopMode && e.envelope.isStage(ADSR::STOPPED_STAGE)) + )) { e.on = true; + } else if (e.on && start && _retriggerMode) { + if (_loopMode) { + e.envelope.reset(); + } + else { + e.envelope.retrigger(); + } } e.envelope.setGate(e.on); outputs[ENV_OUTPUT].setChannels(_channels); diff --git a/src/dsp/envelope.cpp b/src/dsp/envelope.cpp @@ -64,6 +64,21 @@ void ADSR::setShapes(float attackShape, float decayShape, float releaseShape) { _releaseShape = releaseShape; } +void ADSR::retrigger() { + switch (_stage) { + case STOPPED_STAGE: { + _stage = ATTACK_STAGE; + _stageProgress = 0.0f; + break; + } + default: { + _stage = ATTACK_STAGE; + float e = powf(_envelope, 1.0f / _attackShape); + _stageProgress = e * _attack; + } + } +} + float ADSR::_next() { if (_gated) { switch (_stage) { diff --git a/src/dsp/envelope.hpp b/src/dsp/envelope.hpp @@ -56,6 +56,7 @@ struct ADSR : EnvelopeGenerator { void setLinearShape(bool linear); void setShapes(float attackShape, float decayShape, float releaseShape); bool isStage(Stage stage) { return _stage == stage; } + void retrigger(); float _next() override; };