Skip to content

Commit

Permalink
envelope reform.
Browse files Browse the repository at this point in the history
rewrite the mess of the first draft and allow operators to save a ton of CPU if they are at no output and the envelope is not going to move otherwise.
  • Loading branch information
Paul Forgey committed May 9, 2020
1 parent 8735862 commit 2f03ebe
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 129 deletions.
194 changes: 82 additions & 112 deletions purefm/DSP/env.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,16 @@ envelope::envelope(globals const *g) {
_globals = g;
_level = eg_min;
_out = eg_min;
_rate_adj = 0;
_at = 0;
_key_up = -1;
_end = 0;
_patch = nullptr;
_egs = nullptr;
_trigger = false;
_running = false;
_stage = -1;
_key_up = -1;
_run = false;
_idle = true;
_type = eg_exp;
}

envelope::~envelope() {
Expand All @@ -29,11 +33,11 @@ envelope::~envelope() {
int
envelope::pitch_value(int value, int lfo) {
if (_patch == nullptr) {
return 0;
return value;
}
return (value >> (8 + _patch->scale)) +
(_globals->pitch_bend >> _patch->bend) +
(lfo >> (8 + _patch->lfo));
(_globals->pitch_bend >> _patch->bend) +
(lfo >> (8 + _patch->lfo));
}

int
Expand All @@ -50,165 +54,131 @@ envelope::op_value(int value, int lfo) {
void
envelope::update(env_patch const *patch) {
_patch = patch;
_key_up = 0;
_end = 0;
if (patch != nullptr) {
_egs = patch->egs.get();
if (_egs != nullptr) {
int const key_up = _patch->key_up;
_end = (int)_egs->size();
if (key_up >= 0 && key_up < _end) {
_key_up = key_up;
} else {
_key_up = -1;
}
}
} else {
_egs = nullptr;
}
}

void
envelope::start(env_patch const *patch, int rate_adj, bool trigger) {
_patch = patch;
if (patch == nullptr) {
update(patch);
if (_egs == nullptr || _egs->empty()) {
return;
}

_trigger = trigger;
_egs = _patch->egs.get();
_key_up = _patch->key_up;

if (_key_up >= _egs->size()) {
_key_up = -1;
}
_rate_adj = rate_adj;

if (trigger) {
start(rate_adj);
}
else if (!_globals->sustain_pedal) {
run();
} else if (!_globals->sustain_pedal) {
stop();
}
}

void
envelope::start_with(env_patch const *patch, int level, bool trigger) {
if (trigger) {
_level = level;
_out = level;
}
start(patch, 0, trigger);
}

void
envelope::start(int rate_adj) {
_rate_adj = rate_adj;
_running = true;
envelope::run() {
_run = true;
set(0);

}

void
envelope::stop() {
_running = false;

if (_patch->loop) {
_run = false;
if (_key_up >= 0) {
set(_key_up);
} else {
if (_key_up >= 0) {
set(_key_up);
}
}
}

void
envelope::set(int stage) {
if (_egs->empty()) {
_stage = -1;
return;
}

if (stage >= 0 && stage >= _egs->size()) {
if (_running && _patch->loop) {
stage = 0;
envelope::set(int at) {
_idle = false;
if (_run && (at == _key_up || at == _end)) {
if (_patch->loop && _at > 0) {
at = 0;
} else {
stage = -1;
_idle = true;
return;
}
}
_stage = stage;
if (stage < 0) {
if (at < 0 || at >= _end) {
_idle = true;
return;
}
_at = at;
auto const &eg = (*_egs)[at];
int goal = eg->goal;

auto const &e = (*_egs)[stage];

eg_type to_t = e->type;
_goal = e->goal;
_step = std::max(e->rate + _rate_adj, 1);
switch (eg->type) {
case eg_linear:
// if transitioning to a linear output, translate its starting state
_level = eg_min + (_globals->t.exp((eg_max+1 - _out) >> 6) << 8);
break;

if (to_t == eg_delay) {
case eg_delay:
// delay immediately outputs its goal for the count of eg_min->eg_max
_level = eg_min;
_goal = eg_max;
_out = e->goal;
}
else if (to_t == eg_linear) {
_level = eg_min + (_globals->t.exp((eg_max+1 - _out) >> 6) << 8);
} else {
goal = eg_max;
_out = eg->goal;
break;

default:
// otherwise whatever form of prior output is starting point
_level = _out;
break;
}

if ((_goal < _level && _step > 0) || (_goal > _level && _step < 0)) {
_step = -_step;
}
_type = eg->type;
_stage.set(_level, goal, eg->rate + _rate_adj);
}

int
envelope::step() {
// no patch? no output
if (_patch == nullptr) {
return eg_min;
}
// no stages? return output
if (_stage < 0 || _egs->empty()) {
return _out;
envelope::step(int count) {
if (!_trigger && _run && !_globals->sustain_pedal) {
stop();
}

if (_running) {
if (!_trigger && !_globals->sustain_pedal) {
stop();
}
else if (_stage == _key_up) {
if (!_patch->loop) {
return _out;
}
set(0);
}
if (_stage < 0) {
if (_stage.done()) {
if (!_idle) {
set(_at+1);
} else {
return _out;
}
}

bool done = false;

_level += _step;
if ((_level >= _goal && _step > 0) || (_level <= _goal && _step < 0)) {
_level = _goal;
done = true;
}

_level = _stage.step(count);
int i;
auto const &e = (*_egs)[_stage];

switch (e->type) {
case eg_exp:
case eg_pitch:
_out = _level;
break;

case eg_linear:
i = (_level - eg_min) >> 10;
if (i > 0) {
_out = eg_max - (_globals->t.log(i) << 6);
} else {
_out = eg_min;
}
break;

case eg_delay:
// leave alone
break;
}
switch(_type) {
case eg_linear:
i = (_level - eg_min) >> 10;
if (i > 0) {
_out = eg_max - (_globals->t.log(i) << 6);
} else {
_out = eg_min;
}
break;

case eg_delay:
break;

if (done) {
set(_stage+1);
default:
_out = _level;
break;
}

return _out;
Expand Down
62 changes: 55 additions & 7 deletions purefm/DSP/env.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,47 @@

#include "globals.hpp"
#include <vector>
#include <algorithm>

class eg_stage {
public:
eg_stage() {
_level = eg_min;
_goal = eg_min;
_rate = 0;
}
virtual ~eg_stage() {}

void set(int level, int goal, int rate) {
_level = level;
_goal = goal;

rate = std::max(rate, 1);
if (goal < level) {
_rate = -rate;
} else {
_rate = rate;
}
}

bool done() const { return _level == _goal; }
int level() const { return _level; }

int step(int count) {
if (!done()) {
_level += _rate * count;
if (_rate < 0) {
_level = std::max(_goal, _level);
} else {
_level = std::min(_goal, _level);
}
}
return _level;
}

private:
int _level, _goal, _rate;
};

class envelope {
public:
Expand All @@ -19,26 +60,33 @@ class envelope {

void update(env_patch const *);
void start(env_patch const *, int rate_adj, bool trigger);
int step();
int step(int count);
int out() const { return _out; }
void init_at(int out) { _out = out; _level = out; }

// for pitch envelope
void start_with(env_patch const *, int level, bool trigger);
int pitch_value(int value, int lfo);
int op_value(int value, int lfo);

bool idle() const { return _idle && _level == eg_min; }

private:
void set(int stage);
void start(int rate_adj);
void run();
void stop();
void set(int at);

private:
int _level, _out;
int _step, _goal;
int _at;
eg_type _type;
eg_stage _stage;
bool _trigger, _run, _idle;
eg_vec const *_egs;
int _key_up, _end;
int _rate_adj;
int _stage, _key_up;
bool _trigger, _running;

env_patch const *_patch;
eg_vec const *_egs;
globals const *_globals;
};

Expand Down
2 changes: 1 addition & 1 deletion purefm/DSP/lfosc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ lfo::step() {

unsigned long pitch = _globals->t.pitch(_frequency);
osc = _osc.step(*f, _globals->t, pitch, 0, &neg);
env = _env.step() + _patch->level;
env = _env.step(1) + _patch->level;

osc = _globals->t.output(osc, env);
return neg ? -osc : osc;
Expand Down
4 changes: 2 additions & 2 deletions purefm/DSP/op.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ op::step(int lfo, int pitch) {
return;
}

if (!_patch->enabled) {
if (!_patch->enabled || _env.idle()) {
*_out = *_sum;
return;
}
Expand All @@ -111,7 +111,7 @@ op::step(int lfo, int pitch) {
bool neg;
int mod = *_mod << 2;
int out = _osc.step(_globals->t.pitch(frequency), mod, &neg);
int env = _env.op_value(_env.step(), lfo);
int env = _env.op_value(_env.step(1), lfo);
env += bias + _level;

out = _globals->t.output(out, env);
Expand Down
Loading

0 comments on commit 2f03ebe

Please sign in to comment.