19  enum class Shape { FLEXIBLE, EXPONENTIAL, LINEAR };
 
   22  float attack_slope_ = 0;
 
   24  float decay_slope_ = 0;
 
   25  float sustain_level_ = 0;
 
   27  float release_slope_ = 0;
 
   29  float release_start_ = 0;  
 
   30  int   sustain_steps_ = 0;  
 
   31  bool  params_changed_ = 
true;
 
   34  enum class State { ATTACK, DECAY, SUSTAIN, RELEASE, DONE };
 
   36  State state_ = State::DONE;
 
   37  Shape shape_ = Shape::LINEAR;
 
   44  init_abc (
float time_s, 
float slope)
 
   46    bool positive = slope > 0;
 
   47    slope = std::abs (slope);
 
   49    const float t1y = 0.5f + 0.25f * slope;
 
   51    a_ = slope * ( 1.0135809670870777f + slope * (-1.2970447050283254f + slope *   7.2390617313972063f));
 
   52    b_ = slope * (-5.8998946320566281f + slope * ( 5.7282487210570903f + slope * -15.525953208626062f));
 
   53    c_ = 1 - (t1y * a_ + b_) * t1y;
 
   61    const float time_factor = 1 / (rate_ * time_s);
 
   78  compute_slope_params (
float seconds, 
float start_x, 
float end_x)
 
   85    if (shape_ == Shape::LINEAR)
 
   90        c_ = (end_x - start_x) / steps;
 
   92    else if (shape_ == Shape::EXPONENTIAL)
 
   97        const double RATIO = (state_ == State::ATTACK) ? 0.2 : 0.001;
 
   99        const double f = -
log ((RATIO + 1) / RATIO) / steps;
 
  100        double factor = 
exp (f);
 
  101        c_ = (end_x - RATIO * (start_x - end_x)) * (1 - factor);
 
  105    else if (shape_ == Shape::FLEXIBLE)
 
  107        auto pos_time = [] (
auto x) { 
return std::max (x, 0.0001f);  };
 
  108        if (state_ == State::ATTACK)
 
  110            init_abc (pos_time (attack_), attack_slope_);
 
  112        else if (state_ == State::DECAY)
 
  115            float stretch = 1 / 
std::max (1 - sustain_level_, 0.01f);
 
  116            init_abc (-pos_time (decay_ * stretch), decay_slope_);
 
  118        else if (state_ == State::RELEASE)
 
  120            init_abc (-pos_time (release_), release_slope_);
 
  123            float l = 
std::max (release_start_, 0.01f);
 
  128    params_changed_ = 
false;
 
  133  set_shape (Shape shape)
 
  136    params_changed_ = 
true;
 
  142    params_changed_ = 
true;
 
  145  set_attack_slope (
float f)
 
  148    params_changed_ = 
true;
 
  154    params_changed_ = 
true;
 
  157  set_decay_slope (
float f)
 
  160    params_changed_ = 
true;
 
  163  set_sustain (
float f)
 
  165    sustain_level_ = f * 0.01f;
 
  166    params_changed_ = 
true;
 
  169  set_release (
float f)
 
  172    params_changed_ = 
true;
 
  175  set_release_slope (
float f)
 
  178    params_changed_ = 
true;
 
  181  set_rate (
int sample_rate)
 
  184    params_changed_ = 
true;
 
  190    state_          = State::ATTACK;
 
  191    params_changed_ = 
true;
 
  196    state_          = State::RELEASE;
 
  197    release_start_  = level_;
 
  198    params_changed_ = 
true;
 
  201  template<State STATE, Shape SHAPE>
 
  203  process (
uint *iptr, 
float *samples, 
uint n_samples)
 
  210    const float sustain_level = sustain_level_;
 
  212    float level = level_;
 
  214    while (i < n_samples)
 
  216        samples[i++] = level;
 
  218        if (SHAPE == Shape::FLEXIBLE)
 
  219          level = (a * level + b) * level + c;
 
  221        if (SHAPE == Shape::EXPONENTIAL)
 
  222          level = b * level + c;
 
  224        if (SHAPE == Shape::LINEAR)
 
  227        if (STATE == State::ATTACK && level > 1)
 
  230            state_          = State::DECAY;
 
  231            params_changed_ = 
true;
 
  234        if (STATE == State::DECAY && level < sustain_level)
 
  236            state_          = State::SUSTAIN;
 
  237            level           = sustain_level;
 
  238            params_changed_ = 
true;
 
  241        if (STATE == State::RELEASE && level < 1e-5f)
 
  243            state_ = State::DONE;
 
  252  template<State STATE>
 
  254  process (
uint *iptr, 
float *samples, 
uint n_samples)
 
  256    if (shape_ == Shape::LINEAR)
 
  257      process<STATE, Shape::LINEAR> (iptr, samples, n_samples);
 
  259    if (shape_ == Shape::EXPONENTIAL)
 
  260      process<STATE, Shape::EXPONENTIAL> (iptr, samples, n_samples);
 
  262    if (shape_ == Shape::FLEXIBLE)
 
  263      process<STATE, Shape::FLEXIBLE> (iptr, samples, n_samples);
 
  267  process (
float *samples, 
uint n_samples)
 
  270    if (state_ == State::ATTACK)
 
  272        compute_slope_params (attack_, 0, 1);
 
  273        process<State::ATTACK> (&i, samples, n_samples);
 
  275    if (state_ == State::DECAY)
 
  277        compute_slope_params (decay_, 1, sustain_level_);
 
  278        process<State::DECAY> (&i, samples, n_samples);
 
  280    if (state_ == State::RELEASE)
 
  282        compute_slope_params (release_, release_start_, 0);
 
  283        process<State::RELEASE> (&i, samples, n_samples);
 
  285    if (state_ == State::SUSTAIN)
 
  289            if (std::abs (sustain_level_ - level_) > 1e-5)
 
  292                c_ = (sustain_level_ - level_) / sustain_steps_;
 
  298            params_changed_ = 
false;
 
  300        while (sustain_steps_ && i < n_samples) 
 
  302            samples[i++] = level_;
 
  305            if (sustain_steps_ == 0)
 
  306              level_ = sustain_level_;
 
  308        while (i < n_samples)
 
  309          samples[i++] = level_;
 
  311    if (state_ == State::DONE)
 
  313        while (i < n_samples)
 
  320    if (state_ == State::SUSTAIN)
 
  322        return !params_changed_ && sustain_steps_ == 0;
 
  324    return state_ == State::DONE;
 
  329    return state_ == State::DONE;
 
  338  bool   old_c_, old_d_, old_e_, old_f_, old_g_;
 
  341    OSC1_SHAPE = 1, OSC1_PULSE_WIDTH, OSC1_SUB, OSC1_SUB_WIDTH, OSC1_SYNC, OSC1_PITCH, OSC1_OCTAVE, OSC1_UNISON_VOICES, OSC1_UNISON_DETUNE, OSC1_UNISON_STEREO,
 
  342    OSC2_SHAPE,     OSC2_PULSE_WIDTH, OSC2_SUB, OSC2_SUB_WIDTH, OSC2_SYNC, OSC2_PITCH, OSC2_OCTAVE, OSC2_UNISON_VOICES, OSC2_UNISON_DETUNE, OSC2_UNISON_STEREO,
 
  343    VE_MODEL, ATTACK, DECAY, SUSTAIN, RELEASE, ATTACK_SLOPE, DECAY_SLOPE, RELEASE_SLOPE,
 
  344    CUTOFF, RESONANCE, DRIVE, KEY_TRACK, FILTER_TYPE, LADDER_MODE, SKFILTER_MODE,
 
  345    FIL_ATTACK, FIL_DECAY, FIL_SUSTAIN, FIL_RELEASE, FIL_CUT_MOD,
 
  346    MIX, VEL_TRACK, POST_GAIN,
 
  347    KEY_C, KEY_D, KEY_E, KEY_F, KEY_G,
 
  350  enum { FILTER_TYPE_BYPASS, FILTER_TYPE_LADDER, FILTER_TYPE_SKFILTER };
 
  351  int filter_type_ = 0;
 
  353  static constexpr int CUTOFF_MIN_MIDI = 15;
 
  354  static constexpr int CUTOFF_MAX_MIDI = 144;
 
  368    FlexADSR     fil_envelope_;
 
  374    bool         new_voice_   = 
false;
 
  378    double       last_key_track_;
 
  381    double       last_cut_mod_;
 
  392    static constexpr int FILTER_OVERSAMPLE = 4;
 
  394    LadderVCF ladder_filter_ { FILTER_OVERSAMPLE };
 
  395    SKFilter  skfilter_ { FILTER_OVERSAMPLE };
 
  403    using namespace MakeIcon;
 
  408    auto oscparams = [&] (
int oscnum) {
 
  409      const uint I = oscnum + 1;
 
  410      const uint O = oscnum * (OSC2_SHAPE - OSC1_SHAPE);
 
  413      const double shape_default  = oscnum ? -100 : 0;
 
  414      const double octave_default = oscnum;
 
  416      pmap.
group = 
_(
"Oscillator %u", I);
 
  417      pmap[O+OSC1_SHAPE]       = 
Param { o+
"shape",       
_(
"Osc %u Shape", I),             
_(
"Shp%u", I),  shape_default, 
"%", { -100, 100, }, };
 
  418      pmap[O+OSC1_PULSE_WIDTH] = 
Param { o+
"pulse_width", 
_(
"Osc %u Pulse Width", I),       
_(
"PW%u", I),  50, 
"%", { 0, 100, }, };
 
  419      pmap[O+OSC1_SUB]         = 
Param { o+
"subharmonic", 
_(
"Osc %u Subharmonic", I),       
_(
"Sub%u", I),  0, 
"%", { 0, 100, }, };
 
  420      pmap[O+OSC1_SUB_WIDTH]   = 
Param { o+
"subharmonic_width", 
_(
"Osc %u Subharmonic Width", I), 
_(
"SbW%u", I), 50, 
"%", { 0, 100, }, };
 
  421      pmap[O+OSC1_SYNC]        = 
Param { o+
"sync_slave",  
_(
"Osc %u Sync Slave", I),        
_(
"Syn%u", I),  0, 
"Semitones", { 0, 60, }, };
 
  423      pmap[O+OSC1_PITCH]  = 
Param { o+
"pitch",  
_(
"Osc %u Pitch", I),  
_(
"Pit%u", I), 0,              
"semitones", { -7, 7, }, };
 
  424      pmap[O+OSC1_OCTAVE] = 
Param { o+
"octave", 
_(
"Osc %u Octave", I), 
_(
"Oct%u", I), octave_default, 
"octaves",   { -2, 3, }, };
 
  427      pmap[O+OSC1_UNISON_VOICES] = 
Param { o+
"unison_voices", 
_(
"Osc %u Unison Voices", I), 
_(
"Voi%u", I), 1, 
"Voices", { 1, 16, }, };
 
  428      pmap[O+OSC1_UNISON_DETUNE] = 
Param { o+
"unison_detune", 
_(
"Osc %u Unison Detune", I), 
_(
"Dtu%u", I), 6, 
"%", { 0.5, 50, }, };
 
  429      pmap[O+OSC1_UNISON_STEREO] = 
Param { o+
"unison_stereo", 
_(
"Osc %u Unison Stereo", I), 
_(
"Ste%u", I), 0, 
"%", { 0, 100, }, };
 
  435    pmap[MIX]       = 
Param { 
"mix", 
_(
"Mix"), 
_(
"Mix"), 30, 
"%", { 0, 100 }, };
 
  436    pmap[VEL_TRACK] = 
Param { 
"vel_track", 
_(
"Velocity Tracking"), 
_(
"VelTr"), 50, 
"%", { 0, 100, }, };
 
  438    pmap[POST_GAIN] = 
Param { 
"post_gain", 
_(
"Post Gain"), 
_(
"Gain"), -12, 
"dB", { -24, 24, }, };
 
  442    pmap.
group = 
_(
"Volume Envelope");
 
  444    ve_model_cs += { 
"A", 
"Analog" };
 
  445    ve_model_cs += { 
"F", 
"Flexible" };
 
  446    pmap[VE_MODEL] = 
Param { 
"ve_model", 
_(
"Envelope Model"), 
_(
"Model"), 0, 
"", std::move (ve_model_cs), 
"", { 
String (
"blurb=") + 
_(
"ADSR Model to be used"), } };
 
  448    pmap[ATTACK]  = 
Param { 
"attack",  
_(
"Attack"),  
_(
"A"), 20.0, 
"%", { 0, 100, }, };
 
  449    pmap[DECAY]   = 
Param { 
"decay",   
_(
"Decay"),   
_(
"D"), 30.0, 
"%", { 0, 100, }, };
 
  450    pmap[SUSTAIN] = 
Param { 
"sustain", 
_(
"Sustain"), 
_(
"S"), 50.0, 
"%", { 0, 100, }, };
 
  451    pmap[RELEASE] = 
Param { 
"release", 
_(
"Release"), 
_(
"R"), 30.0, 
"%", { 0, 100, }, };
 
  453    pmap[ATTACK_SLOPE]  = 
Param { 
"attack_slope", 
_(
"Attack Slope"), 
_(
"AS"), 50, 
"%", { -100, 100, }, };
 
  454    pmap[DECAY_SLOPE]   = 
Param { 
"decay_slope", 
_(
"Decay Slope"), 
_(
"DS"), -100, 
"%", { -100, 100, }, };
 
  455    pmap[RELEASE_SLOPE] = 
Param { 
"release_slope", 
_(
"Release Slope"), 
_(
"RS"), -100, 
"%", { -100, 100, }, };
 
  459    pmap[CUTOFF]    = 
Param { 
"cutoff", 
_(
"Cutoff"), 
_(
"Cutoff"), 60, 
"", { CUTOFF_MIN_MIDI, CUTOFF_MAX_MIDI, }, }; 
 
  460    pmap[RESONANCE] = 
Param { 
"resonance", 
_(
"Resonance"), 
_(
"Reso"), 25.0, 
"%", { 0, 100, }, };
 
  461    pmap[DRIVE]     = 
Param { 
"drive", 
_(
"Drive"), 
_(
"Drive"), 0, 
"dB", { -24, 36, }, };
 
  462    pmap[KEY_TRACK] = 
Param { 
"key_tracking", 
_(
"Key Tracking"), 
_(
"KeyTr"), 50, 
"%", { 0, 100, }, };
 
  463    ChoiceS filter_type_choices;
 
  464    filter_type_choices += { 
"—"_uc, 
"Bypass Filter" };
 
  465    filter_type_choices += { 
"LD"_uc, 
"Ladder Filter" };
 
  466    filter_type_choices += { 
"SKF"_uc, 
"Sallen-Key Filter" };
 
  467    pmap[FILTER_TYPE] = 
Param { 
"filter_type", 
_(
"Filter Type"), 
_(
"Type"), FILTER_TYPE_LADDER, 
"", std::move (filter_type_choices), 
"", { 
String (
"blurb=") + 
_(
"Filter Type to be used"), } };
 
  469    ChoiceS ladder_mode_choices;
 
  470    ladder_mode_choices += { 
"LP1"_uc, 
"1 Pole Lowpass, 6dB/Octave" };
 
  471    ladder_mode_choices += { 
"LP2"_uc, 
"2 Pole Lowpass, 12dB/Octave" };
 
  472    ladder_mode_choices += { 
"LP3"_uc, 
"3 Pole Lowpass, 18dB/Octave" };
 
  473    ladder_mode_choices += { 
"LP4"_uc, 
"4 Pole Lowpass, 24dB/Octave" };
 
  474    pmap[LADDER_MODE] = 
Param { 
"ladder_mode", 
_(
"Filter Mode"), 
_(
"Mode"), 1, 
"", std::move (ladder_mode_choices), 
"", { 
String (
"blurb=") + 
_(
"Ladder Filter Mode to be used"), } };
 
  476    ChoiceS skfilter_mode_choices;
 
  477    skfilter_mode_choices += { 
"LP1"_uc, 
"1 Pole Lowpass, 6dB/Octave" };
 
  478    skfilter_mode_choices += { 
"LP2"_uc, 
"2 Pole Lowpass, 12dB/Octave" };
 
  479    skfilter_mode_choices += { 
"LP3"_uc, 
"3 Pole Lowpass, 18dB/Octave" };
 
  480    skfilter_mode_choices += { 
"LP4"_uc, 
"4 Pole Lowpass, 24dB/Octave" };
 
  481    skfilter_mode_choices += { 
"LP6"_uc, 
"6 Pole Lowpass, 36dB/Octave" };
 
  482    skfilter_mode_choices += { 
"LP8"_uc, 
"8 Pole Lowpass, 48dB/Octave" };
 
  483    skfilter_mode_choices += { 
"BP2"_uc, 
"2 Pole Bandpass, 6dB/Octave" };
 
  484    skfilter_mode_choices += { 
"BP4"_uc, 
"4 Pole Bandpass, 12dB/Octave" };
 
  485    skfilter_mode_choices += { 
"BP6"_uc, 
"6 Pole Bandpass, 18dB/Octave" };
 
  486    skfilter_mode_choices += { 
"BP8"_uc, 
"8 Pole Bandpass, 24dB/Octave" };
 
  487    skfilter_mode_choices += { 
"HP1"_uc, 
"1 Pole Highpass, 6dB/Octave" };
 
  488    skfilter_mode_choices += { 
"HP2"_uc, 
"2 Pole Highpass, 12dB/Octave" };
 
  489    skfilter_mode_choices += { 
"HP3"_uc, 
"3 Pole Highpass, 18dB/Octave" };
 
  490    skfilter_mode_choices += { 
"HP4"_uc, 
"4 Pole Highpass, 24dB/Octave" };
 
  491    skfilter_mode_choices += { 
"HP6"_uc, 
"6 Pole Highpass, 36dB/Octave" };
 
  492    skfilter_mode_choices += { 
"HP8"_uc, 
"8 Pole Highpass, 48dB/Octave" };
 
  493    pmap[SKFILTER_MODE] = 
Param { 
"skfilter_mode", 
_(
"SKFilter Mode"), 
_(
"Mode"), 2, 
"", std::move (skfilter_mode_choices), 
"", { 
String (
"blurb=") + 
_(
"Sallen-Key Filter Mode to be used"), } };
 
  495    pmap.
group = 
_(
"Filter Envelope");
 
  496    pmap[FIL_ATTACK]  = 
Param { 
"fil_attack", 
_(
"Attack"), 
_(
"A"), 40, 
"%", { 0, 100, }, };
 
  497    pmap[FIL_DECAY]   = 
Param { 
"fil_decay", 
_(
"Decay"), 
_(
"D"), 55, 
"%", { 0, 100, }, };
 
  498    pmap[FIL_SUSTAIN] = 
Param { 
"fil_sustain", 
_(
"Sustain"), 
_(
"S"), 30, 
"%", { 0, 100, }, };
 
  499    pmap[FIL_RELEASE] = 
Param { 
"fil_release", 
_(
"Release"), 
_(
"R"), 30, 
"%", { 0, 100, }, };
 
  500    pmap[FIL_CUT_MOD] = 
Param { 
"fil_cut_mod", 
_(
"Env Cutoff Modulation"), 
_(
"CutMod"), 36, 
"semitones", { -96, 96, }, }; 
 
  502    pmap.
group = 
_(
"Keyboard Input");
 
  503    pmap[KEY_C] = 
Param { 
"c", 
_(
"Main Input 1"), 
_(
"C"), 
false, 
"", {}, 
GUIONLY + 
":toggle" };
 
  504    pmap[KEY_D] = 
Param { 
"d", 
_(
"Main Input 2"), 
_(
"D"), 
false, 
"", {}, 
GUIONLY + 
":toggle" };
 
  505    pmap[KEY_E] = 
Param { 
"e", 
_(
"Main Input 3"), 
_(
"E"), 
false, 
"", {}, 
GUIONLY + 
":toggle" };
 
  506    pmap[KEY_F] = 
Param { 
"f", 
_(
"Main Input 4"), 
_(
"F"), 
false, 
"", {}, 
GUIONLY + 
":toggle" };
 
  507    pmap[KEY_G] = 
Param { 
"g", 
_(
"Main Input 5"), 
_(
"G"), 
false, 
"", {}, 
GUIONLY + 
":toggle" };
 
  508    old_c_ = old_d_ = old_e_ = old_f_ = old_g_ = 
false;
 
  513    stereout_ = 
add_output_bus (
"Stereo Out", SpeakerArrangement::STEREO);
 
  517  set_max_voices (
uint n_voices)
 
  520    voices_.resize (n_voices);
 
  522    active_voices_.clear();
 
  523    active_voices_.reserve (n_voices);
 
  525    idle_voices_.clear();
 
  526    for (
auto& v : voices_)
 
  527      idle_voices_.push_back (&v);
 
  532    if (idle_voices_.empty()) 
 
  535    Voice *voice = idle_voices_.back();
 
  539    idle_voices_.pop_back();
 
  540    active_voices_.push_back (voice);
 
  547    size_t new_voice_count = 0;
 
  549    for (
size_t i = 0; i < active_voices_.size(); i++)
 
  551        Voice *voice = active_voices_[i];
 
  553        if (voice->state_ == Voice::IDLE)    
 
  555            idle_voices_.push_back (voice);
 
  559            active_voices_[new_voice_count++] = voice;
 
  562    active_voices_.resize (new_voice_count);
 
  565  reset (
uint64 target_stamp)
 override 
  574    osc.frequency_base = freq;
 
  575    osc.set_rate (sample_rate());
 
  577    osc.freq_mod_octaves  = properties->freq_mod_octaves;
 
  581  adjust_param (uint32_t tag)
 override 
  587            int new_filter_type = 
irintf (get_param (FILTER_TYPE));
 
  588            if (new_filter_type != filter_type_)
 
  590                filter_type_ = new_filter_type;
 
  591                for (Voice *voice : active_voices_)
 
  593                    if (filter_type_ == FILTER_TYPE_LADDER)
 
  594                      voice->ladder_filter_.reset();
 
  595                    if (filter_type_ == FILTER_TYPE_SKFILTER)
 
  596                      voice->skfilter_.reset();
 
  599            set_parameter_used (LADDER_MODE, filter_type_ == FILTER_TYPE_LADDER);
 
  600            set_parameter_used (SKFILTER_MODE, filter_type_ == FILTER_TYPE_SKFILTER);
 
  611            for (Voice *voice : active_voices_)
 
  612              update_volume_envelope (voice);
 
  620            for (Voice *voice : active_voices_)
 
  621              update_filter_envelope (voice);
 
  626            bool ve_has_slope = 
irintf (get_param (VE_MODEL)) > 0; 
 
  628            set_parameter_used (ATTACK_SLOPE,  ve_has_slope);
 
  629            set_parameter_used (DECAY_SLOPE,   ve_has_slope);
 
  630            set_parameter_used (RELEASE_SLOPE, ve_has_slope);
 
  633        case KEY_C: check_note (KEY_C, old_c_, 60); 
break;
 
  634        case KEY_D: check_note (KEY_D, old_d_, 62); 
break;
 
  635        case KEY_E: check_note (KEY_E, old_e_, 64); 
break;
 
  636        case KEY_F: check_note (KEY_F, old_f_, 65); 
break;
 
  637        case KEY_G: check_note (KEY_G, old_g_, 67); 
break;
 
  643    const uint O = oscnum * (OSC2_SHAPE - OSC1_SHAPE);
 
  644    osc.shape_base          = 
get_param (O+OSC1_SHAPE) * 0.01;
 
  645    osc.pulse_width_base    = 
get_param (O+OSC1_PULSE_WIDTH) * 0.01;
 
  646    osc.sub_base            = 
get_param (O+OSC1_SUB) * 0.01;
 
  647    osc.sub_width_base      = 
get_param (O+OSC1_SUB_WIDTH) * 0.01;
 
  650    int octave = 
irintf (get_param (O+OSC1_OCTAVE));
 
  651    octave = 
CLAMP (octave, -2, 3);
 
  652    osc.frequency_factor = 
fast_exp2 (octave + get_param (O+OSC1_PITCH) / 12.);
 
  654    int unison_voices = 
irintf (get_param (O+OSC1_UNISON_VOICES));
 
  655    unison_voices = 
CLAMP (unison_voices, 1, 16);
 
  656    osc.set_unison (unison_voices, get_param (O+OSC1_UNISON_DETUNE), get_param (O+OSC1_UNISON_STEREO) * 0.01);
 
  658    set_parameter_used (O + OSC1_UNISON_DETUNE, unison_voices > 1);
 
  659    set_parameter_used (O + OSC1_UNISON_STEREO, unison_voices > 1);
 
  662  perc_to_s (
double perc)
 
  665    const double x = perc * 0.01;
 
  666    return x * x * x * 8;
 
  669  perc_to_str (
double perc)
 
  671    double ms = perc_to_s (perc) * 1000;
 
  681  hz_to_str (
double hz)
 
  692  velocity_to_gain (
float velocity, 
float vel_track)
 
  704    const float x = (1 - vel_track) + vel_track * velocity;
 
  709  note_on (
int channel, 
int midi_note, 
float vel)
 
  711    Voice *voice = alloc_voice();
 
  715        voice->state_ = Voice::ON;
 
  716        voice->channel_ = channel;
 
  717        voice->midi_note_ = midi_note;
 
  718        voice->vel_gain_ = velocity_to_gain (vel, get_param (VEL_TRACK) * 0.01);
 
  722        switch (
irintf (get_param (VE_MODEL)))
 
  724            case 0:   voice->envelope_.set_shape (FlexADSR::Shape::EXPONENTIAL);
 
  726            default:  voice->envelope_.set_shape (FlexADSR::Shape::FLEXIBLE);
 
  729        update_volume_envelope (voice);
 
  730        voice->envelope_.set_rate (sample_rate());
 
  731        voice->envelope_.start();
 
  734        voice->fil_envelope_.set_shape (FlexADSR::Shape::LINEAR);
 
  735        update_filter_envelope (voice);
 
  736        voice->fil_envelope_.set_rate (sample_rate());
 
  737        voice->fil_envelope_.start();
 
  739        init_osc (voice->osc1_, voice->freq_);
 
  740        init_osc (voice->osc2_, voice->freq_);
 
  742        voice->osc1_.reset();
 
  743        voice->osc2_.reset();
 
  745        const float cutoff_min_hz = convert_cutoff (CUTOFF_MIN_MIDI);
 
  746        const float cutoff_max_hz = convert_cutoff (CUTOFF_MAX_MIDI);
 
  748        voice->ladder_filter_.reset();
 
  749        voice->ladder_filter_.set_rate (sample_rate());
 
  750        voice->ladder_filter_.set_frequency_range (cutoff_min_hz, cutoff_max_hz);
 
  752        voice->skfilter_.reset();
 
  753        voice->skfilter_.set_rate (sample_rate());
 
  754        voice->skfilter_.set_frequency_range (cutoff_min_hz, cutoff_max_hz);
 
  755        voice->new_voice_ = 
true;
 
  757        voice->cutoff_smooth_.reset (sample_rate(), 0.020);
 
  758        voice->last_cutoff_ = -5000; 
 
  760        voice->cut_mod_smooth_.reset (sample_rate(), 0.020);
 
  761        voice->last_cut_mod_ = -5000; 
 
  762        voice->last_key_track_ = -5000;
 
  764        voice->reso_smooth_.reset (sample_rate(), 0.020);
 
  765        voice->last_reso_ = -5000; 
 
  767        voice->drive_smooth_.reset (sample_rate(), 0.020);
 
  768        voice->last_drive_ = -5000; 
 
  772  note_off (
int channel, 
int midi_note)
 
  774    for (
auto voice : active_voices_)
 
  776        if (voice->state_ == Voice::ON && voice->midi_note_ == midi_note && voice->channel_ == channel)
 
  778            voice->state_ = Voice::RELEASE;
 
  779            voice->envelope_.stop();
 
  780            voice->fil_envelope_.stop();
 
  785  check_note (ParamType pid, 
bool& old_value, 
int note)
 
  787    const bool value = 
get_param (pid) > 0.5;
 
  788    if (value != old_value)
 
  790        constexpr int channel = 0;
 
  792          note_on (channel, note, 100./127.);
 
  794          note_off (channel, note);
 
  799  render_voice (Voice *voice, 
uint n_frames, 
float *mix_left_out, 
float *mix_right_out)
 
  801    float osc1_left_out[n_frames];
 
  802    float osc1_right_out[n_frames];
 
  803    float osc2_left_out[n_frames];
 
  804    float osc2_right_out[n_frames];
 
  806    update_osc (voice->osc1_, 0);
 
  807    update_osc (voice->osc2_, 1);
 
  808    voice->osc1_.process_sample_stereo (osc1_left_out, osc1_right_out, n_frames);
 
  809    voice->osc2_.process_sample_stereo (osc2_left_out, osc2_right_out, n_frames);
 
  812    const float mix_norm = 
get_param (MIX) * 0.01;
 
  813    const float v1 = voice->vel_gain_ * (1 - mix_norm);
 
  814    const float v2 = voice->vel_gain_ * mix_norm;
 
  815    for (
uint i = 0; i < n_frames; i++)
 
  817        mix_left_out[i]  = osc1_left_out[i] * v1 + osc2_left_out[i] * v2;
 
  818        mix_right_out[i] = osc1_right_out[i] * v1 + osc2_right_out[i] * v2;
 
  821    double cutoff = convert_cutoff (get_param (CUTOFF));
 
  822    double key_track = 
get_param (KEY_TRACK) * 0.01;
 
  824    if (fabs (voice->last_cutoff_ - cutoff) > 1e-7 || 
fabs (voice->last_key_track_ - key_track) > 1e-7)
 
  826        const bool reset = voice->last_cutoff_ < -1000;
 
  831        voice->cutoff_smooth_.set (
fast_log2 (cutoff) + key_track * 
fast_log2 (voice->freq_ / c3_hertz), reset);
 
  832        voice->last_cutoff_ = cutoff;
 
  833        voice->last_key_track_ = key_track;
 
  835    double cut_mod = 
get_param (FIL_CUT_MOD) / 12.; 
 
  836    if (fabs (voice->last_cut_mod_ - cut_mod) > 1e-7)
 
  838        const bool reset = voice->last_cut_mod_ < -1000;
 
  840        voice->cut_mod_smooth_.set (cut_mod, reset);
 
  841        voice->last_cut_mod_ = cut_mod;
 
  843    double resonance = 
get_param (RESONANCE) * 0.01;
 
  844    if (fabs (voice->last_reso_ - resonance) > 1e-7)
 
  846        const bool reset = voice->last_reso_ < -1000;
 
  848        voice->reso_smooth_.set (resonance, reset);
 
  849        voice->last_reso_ = resonance;
 
  852    if (fabs (voice->last_drive_ - drive) > 1e-7)
 
  854        const bool reset = voice->last_drive_ < -1000;
 
  856        voice->drive_smooth_.set (drive, reset);
 
  857        voice->last_drive_ = drive;
 
  860    auto filter_process_block = [&] (
auto& filter)
 
  862        auto gen_filter_input = [&] (
float *freq_in, 
float *reso_in, 
float *drive_in, 
uint n_frames)
 
  864            voice->fil_envelope_.process (freq_in, n_frames);
 
  866            for (
uint i = 0; i < n_frames; i++)
 
  868                freq_in[i] = 
fast_exp2 (voice->cutoff_smooth_.get_next() + freq_in[i] * voice->cut_mod_smooth_.get_next());
 
  869                reso_in[i] = voice->reso_smooth_.get_next();
 
  870                drive_in[i] = voice->drive_smooth_.get_next();
 
  874        bool const_freq = voice->cutoff_smooth_.is_constant() && voice->fil_envelope_.is_constant() && voice->cut_mod_smooth_.is_constant();
 
  875        bool const_reso = voice->reso_smooth_.is_constant();
 
  876        bool const_drive = voice->drive_smooth_.is_constant();
 
  878        if (const_freq && const_reso && const_drive)
 
  881            float freq, reso, drive;
 
  882            gen_filter_input (&freq, &reso, &drive, 1);
 
  884            filter.set_freq (freq);
 
  885            filter.set_reso (reso);
 
  886            filter.set_drive (drive);
 
  887            filter.process_block (n_frames, mix_left_out, mix_right_out);
 
  892            float freq_in[n_frames], reso_in[n_frames], drive_in[n_frames];
 
  893            gen_filter_input (freq_in, reso_in, drive_in, n_frames);
 
  895            filter.process_block (n_frames, mix_left_out, mix_right_out, freq_in, reso_in, drive_in);
 
  899    if (filter_type_ == FILTER_TYPE_LADDER)
 
  901        voice->ladder_filter_.set_mode (LadderVCF::Mode (
irintf (get_param (LADDER_MODE))));
 
  902        filter_process_block (voice->ladder_filter_);
 
  904    else if (filter_type_ == FILTER_TYPE_SKFILTER)
 
  906        voice->skfilter_.set_mode (SKFilter::Mode (
irintf (get_param (SKFILTER_MODE))));
 
  907        filter_process_block (voice->skfilter_);
 
  911  set_parameter_used (uint32_t 
id, 
bool used)
 
  917  update_volume_envelope (Voice *voice)
 
  919    voice->envelope_.set_attack (perc_to_s (get_param (ATTACK)));
 
  920    voice->envelope_.set_decay (perc_to_s (get_param (DECAY)));
 
  921    voice->envelope_.set_sustain (get_param (SUSTAIN));         
 
  922    voice->envelope_.set_release (perc_to_s (get_param (RELEASE)));
 
  923    voice->envelope_.set_attack_slope (get_param (ATTACK_SLOPE) * 0.01);
 
  924    voice->envelope_.set_decay_slope (get_param (DECAY_SLOPE) * 0.01);
 
  925    voice->envelope_.set_release_slope (get_param (RELEASE_SLOPE) * 0.01);
 
  928  update_filter_envelope (Voice *voice)
 
  930    voice->fil_envelope_.set_attack (perc_to_s (get_param (FIL_ATTACK)));
 
  931    voice->fil_envelope_.set_decay (perc_to_s (get_param (FIL_DECAY)));
 
  932    voice->fil_envelope_.set_sustain (get_param (FIL_SUSTAIN));         
 
  933    voice->fil_envelope_.set_release (perc_to_s (get_param (FIL_RELEASE)));
 
  936  render_audio (
float *left_out, 
float *right_out, 
uint n_frames)
 
  941    bool   need_free = 
false;
 
  943    for (Voice *voice : active_voices_)
 
  945        if (voice->new_voice_)
 
  948            if (filter_type_ == FILTER_TYPE_LADDER)
 
  949              idelay = voice->ladder_filter_.delay();
 
  950            if (filter_type_ == FILTER_TYPE_SKFILTER)
 
  951              idelay = voice->skfilter_.delay();
 
  956                render_voice (voice, idelay, junk, junk);
 
  958            voice->new_voice_ = 
false;
 
  960        float mix_left_out[n_frames];
 
  961        float mix_right_out[n_frames];
 
  963        render_voice (voice, n_frames, mix_left_out, mix_right_out);
 
  966        float volume_env[n_frames];
 
  967        voice->envelope_.process (volume_env, n_frames);
 
  968        float post_gain_factor = 
db2voltage (get_param (POST_GAIN));
 
  969        for (
uint i = 0; i < n_frames; i++)
 
  971            float amp = post_gain_factor * volume_env[i];
 
  972            left_out[i] += mix_left_out[i] * amp;
 
  973            right_out[i] += mix_right_out[i] * amp;
 
  975        if (voice->envelope_.done())
 
  977            voice->state_ = Voice::IDLE;
 
  982      free_unused_voices();
 
  985  render (
uint n_frames)
 override 
  989    float *left_out = 
oblock (stereout_, 0);
 
  990    float *right_out = 
oblock (stereout_, 1);
 
  997    for (
const auto &ev : evinput)
 
 1000        render_audio (left_out + offset, right_out + offset, ev.frame - offset);
 
 1003        switch (ev.message())
 
 1005          case MidiMessage::NOTE_OFF:
 
 1006            note_off (ev.channel, ev.key);
 
 1008          case MidiMessage::NOTE_ON:
 
 1009            note_on (ev.channel, ev.key, ev.velocity);
 
 1011          case MidiMessage::ALL_NOTES_OFF:
 
 1012            for (
auto voice : active_voices_)
 
 1013              if (voice->state_ == Voice::ON && voice->channel_ == ev.channel)
 
 1014                note_off (voice->channel_, voice->midi_note_);
 
 1016          case MidiMessage::PARAM_VALUE:
 
 1018            adjust_param (ev.param);
 
 1024    render_audio (left_out + offset, right_out + offset, n_frames - offset);
 
 1028  convert_cutoff (
double midi_note)
 
 1030    return 440 * 
std::pow (2, (midi_note - 69) / 12.);
 
 1036    for (
int oscnum = 0; oscnum < 2; oscnum++)
 
 1038        const uint O = oscnum * (OSC2_SHAPE - OSC1_SHAPE);
 
 1039        if (paramid == O+OSC1_UNISON_VOICES)
 
 1041        if (paramid == O+OSC1_OCTAVE)
 
 1044    for (
auto p : { ATTACK, DECAY, RELEASE, FIL_ATTACK, FIL_DECAY, FIL_RELEASE })
 
 1046        return perc_to_str (value);
 
 1047    if (paramid == CUTOFF)
 
 1048      return hz_to_str (convert_cutoff (value));
 
 1053  BlepSynth (
const ProcessorSetup &psetup) :
 
 1060    info.
label        = 
"BlepSynth";
 
 1066static auto blepsynth = register_audio_processor<BlepSynth> (
"Ase::Devices::BlepSynth");
 
Audio signal AudioProcessor base class, implemented by all effects and instruments.
 
float note_to_freq(int note) const
Convert MIDI note to Hertz according to the current MusicalTuning.
 
double get_param(Id32 paramid)
Fetch value of parameter id.
 
void prepare_event_input()
 
virtual String param_value_to_text(uint32_t paramid, double value) const
 
MidiEventInput midi_event_input()
Access the current MidiEvent inputs during render(), needs prepare_event_input().
 
OBusId add_output_bus(CString uilabel, SpeakerArrangement speakerarrangement, const String &hints="", const String &blurb="")
Add an output bus with uilabel and channels configured via speakerarrangement.
 
virtual void initialize(SpeakerArrangement busses)=0
 
void apply_event(const MidiEvent &event)
Assign MidiEvent::PARAM_VALUE event values to parameters.
 
void install_params(const AudioParams::Map ¶ms)
Reset list of parameters, enqueues parameter value initializaiton events.
 
float * oblock(OBusId b, uint c)
 
#define assert_return(expr,...)
Return from the current function if expr is unmet and issue an assertion warning.
 
#define CLAMP(v, mi, ma)
Yield v clamped to [mi … ma].
 
#define _(...)
Retrieve the translation of a C or C++ string.
 
The Anklang C++ API namespace.
 
std::string string_format(const char *format, const Args &...args) __attribute__((__format__(__printf__
Format a string similar to sprintf(3) with support for std::string and std::ostringstream convertible...
 
CString website_url
Website of/about this AudioProcessor.
 
uint64_t uint64
A 64-bit unsigned integer, use PRI*64 in format strings.
 
void floatfill(float *dst, float f, size_t n)
Fill n values of dst with f.
 
OBusId
ID type for AudioProcessor output buses, buses are numbered with increasing index.
 
CString version
Version identifier.
 
constexpr const char GUIONLY[]
GUI READABLE WRITABLE.
 
CString creator_name
Name of the creator.
 
CString label
Preferred user interface name.
 
std::string String
Convenience alias for std::string.
 
uint32_t uint
Provide 'uint' as convenience type.
 
CString category
Category to allow grouping for processors of similar function.
 
Float db2voltage(Float x)
Convert Decibel to synthesizer value (Voltage).
 
Detailed information and common properties of AudioProcessor subclasses.
 
Structured initializer for Parameter.
 
Parameter list construction helper.
 
String group
Group to be applied to all newly inserted Parameter objects.