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.