[SCM] calf/master: + Monosynth: moved to separate .cpp file (might be slightly slower, but nothing worth keeping huge mess in .h file) + Monosynth: another attempt at fixing ADSR envelope, hope it's 100% right this time + Monosynth: started adding more waveforms, changed "Skewed" waveforms too so that they are less chirpy + Added missing presets.xml file to repository
js at users.alioth.debian.org
js at users.alioth.debian.org
Tue May 7 15:36:48 UTC 2013
The following commit has been merged in the master branch:
commit e7d7a806861e16a4d32dc5c62a5613ad10ee628d
Author: kfoltman <kfoltman at 78b06b96-2940-0410-b7fc-879d825d01d8>
Date: Sun Dec 23 23:57:04 2007 +0000
+ Monosynth: moved to separate .cpp file (might be slightly slower, but nothing worth keeping huge mess in .h file)
+ Monosynth: another attempt at fixing ADSR envelope, hope it's 100% right this time
+ Monosynth: started adding more waveforms, changed "Skewed" waveforms too so that they are less chirpy
+ Added missing presets.xml file to repository
git-svn-id: https://calf.svn.sourceforge.net/svnroot/calf/trunk@41 78b06b96-2940-0410-b7fc-879d825d01d8
diff --git a/presets.xml b/presets.xml
new file mode 100644
index 0000000..ceedc6d
--- /dev/null
+++ b/presets.xml
@@ -0,0 +1,165 @@
+<presets>
+<preset bank="0" program="0" plugin="monosynth" name="Synth Brass">
+ <param name="o1_wave" value="0" />
+ <param name="o2_wave" value="0" />
+ <param name="o12_detune" value="5.53015" />
+ <param name="o2_xpose" value="0" />
+ <param name="phase_mode" value="5" />
+ <param name="o12_mix" value="0.5" />
+ <param name="Filter" value="2" />
+ <param name="cutoff" value="661.957" />
+ <param name="res" value="0.7" />
+ <param name="filter_sep" value="0" />
+ <param name="env2cutoff" value="6443.01" />
+ <param name="env2res" value="1" />
+ <param name="adsr_a" value="84.5126" />
+ <param name="adsr_d" value="350" />
+ <param name="adsr_s" value="0.682958" />
+ <param name="adsr_r" value="59.6428" />
+ <param name="key_follow" value="1" />
+ <param name="legato" value="2" />
+ <param name="portamento" value="64.4744" />
+ <param name="vel2amp" value="0.530111" />
+ <param name="vel2filter" value="0.225724" />
+</preset>
+<preset bank="0" program="0" plugin="monosynth" name="Octaved Square">
+ <param name="o1_wave" value="3" />
+ <param name="o2_wave" value="1" />
+ <param name="o12_detune" value="0.60614" />
+ <param name="o2_xpose" value="24" />
+ <param name="phase_mode" value="1" />
+ <param name="o12_mix" value="0.5" />
+ <param name="Filter" value="4" />
+ <param name="cutoff" value="1170.34" />
+ <param name="res" value="0.7" />
+ <param name="filter_sep" value="-777.368" />
+ <param name="env2cutoff" value="9325.2" />
+ <param name="env2res" value="1" />
+ <param name="adsr_a" value="1" />
+ <param name="adsr_d" value="108.852" />
+ <param name="adsr_s" value="0.0176431" />
+ <param name="adsr_r" value="91.6363" />
+ <param name="key_follow" value="1" />
+ <param name="legato" value="0" />
+ <param name="portamento" value="1" />
+ <param name="vel2amp" value="1" />
+ <param name="vel2filter" value="0.225724" />
+</preset>
+<preset bank="0" program="0" plugin="monosynth" name="Move it!">
+ <param name="o1_wave" value="2" />
+ <param name="o2_wave" value="2" />
+ <param name="o12_detune" value="13.1621" />
+ <param name="o2_xpose" value="0" />
+ <param name="phase_mode" value="1" />
+ <param name="o12_mix" value="0.5" />
+ <param name="Filter" value="1" />
+ <param name="cutoff" value="90.3129" />
+ <param name="res" value="3.0757" />
+ <param name="filter_sep" value="1272.27" />
+ <param name="env2cutoff" value="7551.9" />
+ <param name="env2res" value="1" />
+ <param name="adsr_a" value="53.3753" />
+ <param name="adsr_d" value="50.9018" />
+ <param name="adsr_s" value="1" />
+ <param name="adsr_r" value="91.6363" />
+ <param name="key_follow" value="1" />
+ <param name="legato" value="0" />
+ <param name="portamento" value="1" />
+ <param name="vel2amp" value="1" />
+ <param name="vel2filter" value="1" />
+</preset>
+<preset bank="0" program="0" plugin="monosynth" name="Fat Bass">
+ <param name="o1_wave" value="0" />
+ <param name="o2_wave" value="0" />
+ <param name="o12_detune" value="7.49069" />
+ <param name="o2_xpose" value="0" />
+ <param name="phase_mode" value="1" />
+ <param name="o12_mix" value="0.5" />
+ <param name="Filter" value="1" />
+ <param name="cutoff" value="116.961" />
+ <param name="res" value="2" />
+ <param name="filter_sep" value="0" />
+ <param name="env2cutoff" value="10800" />
+ <param name="env2res" value="1" />
+ <param name="adsr_a" value="1" />
+ <param name="adsr_d" value="125.992" />
+ <param name="adsr_s" value="0.530111" />
+ <param name="adsr_r" value="50" />
+ <param name="key_follow" value="1" />
+ <param name="legato" value="0" />
+ <param name="portamento" value="1" />
+ <param name="vel2amp" value="0" />
+ <param name="vel2filter" value="0.5" />
+</preset>
+<preset bank="0" program="0" plugin="monosynth" name="Cut Through Bass">
+ <param name="o1_wave" value="1" />
+ <param name="o2_wave" value="1" />
+ <param name="o12_detune" value="18.0793" />
+ <param name="o2_xpose" value="0" />
+ <param name="phase_mode" value="1" />
+ <param name="o12_mix" value="0.5" />
+ <param name="filter" value="2" />
+ <param name="cutoff" value="88.9763" />
+ <param name="res" value="2" />
+ <param name="filter_sep" value="640.487" />
+ <param name="env2cutoff" value="8000" />
+ <param name="env2res" value="1" />
+ <param name="adsr_a" value="1" />
+ <param name="adsr_d" value="350" />
+ <param name="adsr_s" value="0.166667" />
+ <param name="adsr_r" value="63.9728" />
+ <param name="key_follow" value="1" />
+ <param name="legato" value="0" />
+ <param name="portamento" value="1" />
+ <param name="vel2amp" value="1" />
+ <param name="vel2filter" value="1" />
+</preset>
+<preset bank="0" program="0" plugin="monosynth" name="Fat Cats">
+ <param name="o1_wave" value="0" />
+ <param name="o2_wave" value="2" />
+ <param name="o12_detune" value="9.26552" />
+ <param name="o2_xpose" value="0" />
+ <param name="phase_mode" value="5" />
+ <param name="o12_mix" value="0.5" />
+ <param name="filter" value="1" />
+ <param name="cutoff" value="159.315" />
+ <param name="res" value="4.42802" />
+ <param name="filter_sep" value="1212.42" />
+ <param name="env2cutoff" value="8000" />
+ <param name="env2res" value="1" />
+ <param name="env2amp" value="1" />
+ <param name="adsr_a" value="736.806" />
+ <param name="adsr_d" value="10" />
+ <param name="adsr_s" value="1" />
+ <param name="adsr_r" value="162.197" />
+ <param name="key_follow" value="1" />
+ <param name="legato" value="3" />
+ <param name="portamento" value="167.215" />
+ <param name="vel2filter" value="1" />
+ <param name="vel2amp" value="0" />
+</preset>
+<preset bank="0" program="0" plugin="monosynth" name="Twinkle">
+ <param name="o1_wave" value="4" />
+ <param name="o2_wave" value="3" />
+ <param name="o12_detune" value="0.60614" />
+ <param name="o2_xpose" value="24" />
+ <param name="phase_mode" value="1" />
+ <param name="o12_mix" value="0.5" />
+ <param name="filter" value="2" />
+ <param name="cutoff" value="1170.34" />
+ <param name="res" value="0.7" />
+ <param name="filter_sep" value="-1202.13" />
+ <param name="env2cutoff" value="3841.02" />
+ <param name="env2res" value="1" />
+ <param name="env2amp" value="1" />
+ <param name="adsr_a" value="1" />
+ <param name="adsr_d" value="108.852" />
+ <param name="adsr_s" value="0.0176431" />
+ <param name="adsr_r" value="91.6363" />
+ <param name="key_follow" value="1" />
+ <param name="legato" value="2" />
+ <param name="portamento" value="12.5992" />
+ <param name="vel2filter" value="0" />
+ <param name="vel2amp" value="1" />
+</preset>
+</presets>
\ No newline at end of file
diff --git a/src/Makefile.am b/src/Makefile.am
index 4d535af..cc12308 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -44,10 +44,10 @@ endif
calfbenchmark_SOURCES = benchmark.cpp
calfbenchmark_LDADD =
-calf_la_SOURCES = modules.cpp giface.cpp synth.cpp preset.cpp
+calf_la_SOURCES = modules.cpp giface.cpp monosynth.cpp synth.cpp preset.cpp
calf_la_LDFLAGS = -rpath $(ladspadir) -avoid-version -module -lexpat
-libcalfstatic_la_SOURCES = modules.cpp giface.cpp synth.cpp preset.cpp
+libcalfstatic_la_SOURCES = modules.cpp giface.cpp monosynth.cpp synth.cpp preset.cpp
libcalfstatic_la_LDFLAGS = -static -lexpat
libcalfgui_la_SOURCES = gui.cpp preset_gui.cpp
diff --git a/src/calf/modules_synths.h b/src/calf/modules_synths.h
index b20eda3..329fa35 100644
--- a/src/calf/modules_synths.h
+++ b/src/calf/modules_synths.h
@@ -35,7 +35,7 @@ namespace synth {
class monosynth_audio_module: public null_audio_module
{
public:
- enum { wave_saw, wave_sqr, wave_pulse, wave_sine, wave_triangle, wave_varistep, wave_skewsaw, wave_skewsqr, wave_count };
+ enum { wave_saw, wave_sqr, wave_pulse, wave_sine, wave_triangle, wave_varistep, wave_skewsaw, wave_skewsqr, wave_test1, wave_test2, wave_test3, wave_test4, wave_test5, wave_test6, wave_test7, wave_test8, wave_count };
enum { flt_lp12, flt_lp24, flt_2lp12, flt_hp12, flt_lpbr, flt_hpbr, flt_bp6, flt_2bp6 };
enum { par_wave1, par_wave2, par_detune, par_osc2xpose, par_oscmode, par_oscmix, par_filtertype, par_cutoff, par_resonance, par_cutoffsep, par_envmod, par_envtores, par_envtoamp, par_attack, par_decay, par_sustain, par_release, par_keyfollow, par_legato, par_portamento, par_vel2filter, par_vel2amp, param_count };
enum { in_count = 0, out_count = 2, support_midi = true, rt_capable = true };
@@ -64,69 +64,8 @@ public:
synth::adsr envelope;
static parameter_properties param_props[];
- void set_sample_rate(uint32_t sr) {
- srate = sr;
- crate = sr / step_size;
- odcr = (float)(1.0 / crate);
- phaseshifter.set_ap(1000.f, sr);
- fgain = 0.f;
- fgain_delta = 0.f;
- }
- void delayed_note_on()
- {
- porta_time = 0.f;
- start_freq = freq;
- target_freq = freq = 440 * pow(2.0, (queue_note_on - 69) / 12.0);
- ampctl = 1.0 + (queue_vel - 1.0) * *params[par_vel2amp];
- fltctl = 1.0 + (queue_vel - 1.0) * *params[par_vel2filter];
- set_frequency();
- osc1.waveform = waves[wave1].get_level(osc1.phasedelta);
- osc2.waveform = waves[wave2].get_level(osc2.phasedelta);
-
- if (!running)
- {
- if (legato >= 2)
- porta_time = -1.f;
- osc1.reset();
- osc2.reset();
- filter.reset();
- filter2.reset();
- switch((int)*params[par_oscmode])
- {
- case 1:
- osc2.phase = 0x80000000;
- break;
- case 2:
- osc2.phase = 0x40000000;
- break;
- case 3:
- osc1.phase = osc2.phase = 0x40000000;
- break;
- case 4:
- osc1.phase = 0x40000000;
- osc2.phase = 0xC0000000;
- break;
- case 5:
- // rand() is crap, but I don't have any better RNG in Calf yet
- osc1.phase = rand() << 16;
- osc2.phase = rand() << 16;
- break;
- default:
- break;
- }
- envelope.note_on();
- running = true;
- }
- if (legato >= 2 && !gate)
- porta_time = -1.f;
- gate = true;
- stopping = false;
- if (!(legato & 1) || envelope.released()) {
- envelope.note_on();
- }
- envelope.advance();
- queue_note_on = -1;
- }
+ void set_sample_rate(uint32_t sr);
+ void delayed_note_on();
void note_on(int note, int vel)
{
queue_note_on = note;
@@ -140,11 +79,11 @@ public:
envelope.note_off();
}
}
- void pitch_bend(int value)
+ inline void pitch_bend(int value)
{
pitchbend = pow(2.0, value / 8192.0);
}
- void set_frequency()
+ inline void set_frequency()
{
osc1.set_freq(freq * (2 - detune) * pitchbend, srate);
osc2.set_freq(freq * (detune) * pitchbend * xpose, srate);
@@ -163,62 +102,8 @@ public:
legato = dsp::fastf2i_drm(*params[par_legato]);
set_frequency();
}
- void activate() {
- running = false;
- output_pos = 0;
- queue_note_on = -1;
- pitchbend = 1.f;
- filter.reset();
- filter2.reset();
- float data[2048];
- bandlimiter<11> bl;
-
- // yes these waves don't have really perfect 1/x spectrum because of aliasing
- // (so what?)
- for (int i = 0 ; i < 1024; i++)
- data[i] = (float)(i / 1024.f),
- data[i + 1024] = (float)(i / 1024.f - 1.0f);
- waves[wave_saw].make(bl, data);
-
- for (int i = 0 ; i < 2048; i++)
- data[i] = (float)(i < 1024 ? -1.f : 1.f);
- waves[wave_sqr].make(bl, data);
-
- for (int i = 0 ; i < 2048; i++)
- data[i] = (float)(i < 64 ? -1.f : 1.f);
- waves[wave_pulse].make(bl, data);
-
- // XXXKF sure this is a waste of space, this will be fixed some day by better bandlimiter
- for (int i = 0 ; i < 2048; i++)
- data[i] = (float)sin(i * PI / 1024);
- waves[wave_sine].make(bl, data);
-
- for (int i = 0 ; i < 512; i++) {
- data[i] = i / 512.0,
- data[i + 512] = 1 - i / 512.0,
- data[i + 1024] = - i / 512.0,
- data[i + 1536] = -1 + i / 512.0;
- }
- waves[wave_triangle].make(bl, data);
-
- for (int i = 0, j = 1; i < 2048; i++) {
- data[i] = -1 + j / 1024.0;
- if (i == j)
- j *= 2;
- }
- waves[wave_varistep].make(bl, data);
-
- for (int i = 0; i < 2048; i++) {
- data[i] = (min(1.f, (float)(i / 64.f))) * (-1 + fmod (i * i / 8192.0, 2.0));
- }
- waves[wave_skewsaw].make(bl, data);
- for (int i = 0; i < 2048; i++) {
- data[i] = (min(1.f, (float)(i / 64.f))) * (fmod (i * i / 2048.0, 2.0) < 1.0 ? -1.0 : +1.0);
- }
- waves[wave_skewsqr].make(bl, data);
- }
- void deactivate() {
- }
+ void activate();
+ void deactivate() {}
inline float softclip(float wave) const
{
float abswave = fabs(wave);
@@ -231,154 +116,14 @@ public:
}
return wave;
}
- void calculate_buffer_ser()
- {
- for (uint32_t i = 0; i < step_size; i++)
- {
- float osc1val = osc1.get();
- float osc2val = osc2.get();
- float wave = fgain * (osc1val + (osc2val - osc1val) * xfade);
- wave = filter.process_d1(wave);
- wave = filter2.process_d1(wave);
- buffer[i] = softclip(wave);
- fgain += fgain_delta;
- }
- }
- void calculate_buffer_single()
- {
- for (uint32_t i = 0; i < step_size; i++)
- {
- float osc1val = osc1.get();
- float osc2val = osc2.get();
- float wave = fgain * (osc1val + (osc2val - osc1val) * xfade);
- wave = filter.process_d1(wave);
- buffer[i] = softclip(wave);
- fgain += fgain_delta;
- }
- }
- void calculate_buffer_stereo()
- {
- for (uint32_t i = 0; i < step_size; i++)
- {
- float osc1val = osc1.get();
- float osc2val = osc2.get();
- float wave1 = osc1val + (osc2val - osc1val) * xfade;
- float wave2 = phaseshifter.process_ap(wave1);
- buffer[i] = softclip(fgain * filter.process_d1(wave1));
- buffer2[i] = softclip(fgain * filter2.process_d1(wave2));
- fgain += fgain_delta;
- }
- }
- bool is_stereo_filter() const
+ void calculate_buffer_ser();
+ void calculate_buffer_single();
+ void calculate_buffer_stereo();
+ inline bool is_stereo_filter() const
{
return filter_type == flt_2lp12 || filter_type == flt_2bp6;
}
- void calculate_step() {
- if (queue_note_on != -1)
- delayed_note_on();
- else if (stopping)
- {
- running = false;
- dsp::zero(buffer, step_size);
- if (is_stereo_filter())
- dsp::zero(buffer2, step_size);
- return;
- }
- float porta_total_time = *params[par_portamento] * 0.001f;
-
- if (porta_total_time >= 0.00101f && porta_time >= 0) {
- // XXXKF this is criminal, optimize!
- float point = porta_time / porta_total_time;
- if (point >= 1.0f) {
- freq = target_freq;
- porta_time = -1;
- } else {
- freq = start_freq * pow(target_freq / start_freq, point);
- porta_time += odcr;
- }
- }
- set_frequency();
- envelope.advance();
- float env = envelope.value;
- cutoff = *params[par_cutoff] * pow(2.0f, env * fltctl * *params[par_envmod] * (1.f / 1200.f));
- if (*params[par_keyfollow] >= 0.5f)
- cutoff *= freq / 264.0f;
- cutoff = dsp::clip(cutoff , 10.f, 18000.f);
- float resonance = *params[par_resonance];
- float e2r = *params[par_envtores];
- float e2a = *params[par_envtoamp];
- resonance = resonance * (1 - e2r) + (0.7 + (resonance - 0.7) * env * env) * e2r;
- float cutoff2 = dsp::clip(cutoff * separation, 10.f, 18000.f);
- float newfgain = 0.f;
- switch(filter_type)
- {
- case flt_lp12:
- filter.set_lp_rbj(cutoff, resonance, srate);
- newfgain = min(0.7f, 0.7f / resonance) * ampctl;
- break;
- case flt_hp12:
- filter.set_hp_rbj(cutoff, resonance, srate);
- newfgain = min(0.7f, 0.7f / resonance) * ampctl;
- break;
- case flt_lp24:
- filter.set_lp_rbj(cutoff, resonance, srate);
- filter2.set_lp_rbj(cutoff2, resonance, srate);
- newfgain = min(0.5f, 0.5f / resonance) * ampctl;
- break;
- case flt_lpbr:
- filter.set_lp_rbj(cutoff, resonance, srate);
- filter2.set_br_rbj(cutoff2, resonance, srate);
- newfgain = min(0.5f, 0.5f / resonance) * ampctl;
- break;
- case flt_hpbr:
- filter.set_hp_rbj(cutoff, resonance, srate);
- filter2.set_br_rbj(cutoff2, resonance, srate);
- newfgain = min(0.5f, 0.5f / resonance) * ampctl;
- break;
- case flt_2lp12:
- filter.set_lp_rbj(cutoff, resonance, srate);
- filter2.set_lp_rbj(cutoff2, resonance, srate);
- newfgain = min(0.7f, 0.7f / resonance) * ampctl;
- break;
- case flt_bp6:
- filter.set_bp_rbj(cutoff, resonance, srate);
- newfgain = ampctl;
- break;
- case flt_2bp6:
- filter.set_bp_rbj(cutoff, resonance, srate);
- filter2.set_bp_rbj(cutoff2, resonance, srate);
- newfgain = ampctl;
- break;
- }
- newfgain *= 1.0 - (1.0 - env) * e2a;
- fgain_delta = (newfgain - fgain) * (1.0 / step_size);
- switch(filter_type)
- {
- case flt_lp24:
- case flt_lpbr:
- case flt_hpbr: // Oomek's wish
- calculate_buffer_ser();
- break;
- case flt_lp12:
- case flt_hp12:
- case flt_bp6:
- calculate_buffer_single();
- break;
- case flt_2lp12:
- case flt_2bp6:
- calculate_buffer_stereo();
- break;
- }
- if (envelope.state == adsr::STOP)
- {
- for (int i = 0; i < step_size; i++)
- buffer[i] *= (step_size - i) * (1.0f / step_size);
- if (is_stereo_filter())
- for (int i = 0; i < step_size; i++)
- buffer2[i] *= (step_size - i) * (1.0f / step_size);
- stopping = true;
- }
- }
+ void calculate_step();
uint32_t process(uint32_t offset, uint32_t nsamples, uint32_t inputs_mask, uint32_t outputs_mask) {
if (!running && queue_note_on == -1)
return 0;
diff --git a/src/calf/synth.h b/src/calf/synth.h
index 7df5b97..eb0bbb4 100644
--- a/src/calf/synth.h
+++ b/src/calf/synth.h
@@ -166,17 +166,18 @@ public:
env_state state;
// note: these are *rates*, not times
- double attack, decay, sustain, release;
- double value, thisrelease, releasemul;
+ double attack, decay, sustain, release, release_time;
+ double value, thisrelease, thiss;
adsr()
{
- attack = decay = sustain = release = thisrelease = releasemul = 0.f;
+ attack = decay = sustain = release = thisrelease = thiss = 0.f;
reset();
}
inline void reset()
{
value = 0.f;
+ thiss = 0.f;
state = STOP;
}
inline void set(float a, float d, float s, float r, float er)
@@ -184,8 +185,14 @@ public:
attack = 1.0 / (a * er);
decay = (1 - s) / (d * er);
sustain = s;
- release = s / (r * er);
- thisrelease = releasemul * release;
+ release_time = r * er;
+ release = s / release_time;
+ // in release:
+ // lock thiss setting (start of release for current note) and unlock thisrelease setting (current note's release rate)
+ if (state != RELEASE)
+ thiss = s;
+ else
+ thisrelease = thiss / release_time;
}
inline bool released()
{
@@ -194,19 +201,22 @@ public:
inline void note_on()
{
state = ATTACK;
+ thiss = sustain;
}
inline void note_off()
{
if (state == STOP)
return;
- if (sustain > 0)
- releasemul = value / sustain;
- else
- releasemul = 1.f;
- thisrelease = releasemul * release;
- if (value > sustain && decay > thisrelease)
+ thiss = std::max(sustain, value);
+ thisrelease = thiss / release_time;
+ // we're in attack or decay, and if decay is faster than release
+ if (value > sustain && decay > thisrelease) {
+ // use standard release time later (because we'll be switching at sustain point)
+ thisrelease = release;
state = LOCKDECAY;
- else {
+ } else {
+ // in attack/decay, but use fixed release time
+ // in case value fell below sustain, assume it didn't (for the purpose of calculating release rate only)
state = RELEASE;
}
}
@@ -237,7 +247,6 @@ public:
value = 0.f;
state = RELEASE;
thisrelease = release;
- releasemul = 1.f;
}
break;
case SUSTAIN:
diff --git a/src/modules.cpp b/src/modules.cpp
index 3dfb323..80756fa 100644
--- a/src/modules.cpp
+++ b/src/modules.cpp
@@ -171,53 +171,6 @@ static synth::ladspa_wrapper<organ_audio_module> organ(organ_info);
#endif
////////////////////////////////////////////////////////////////////////////
-const char *monosynth_audio_module::port_names[] = {
- "Out L", "Out R",
-};
-
-const char *monosynth_waveform_names[] = { "Sawtooth", "Square", "Pulse", "Sine", "Triangle", "Varistep", "Skewed Saw", "Skewed Square" };
-const char *monosynth_mode_names[] = { "0 : 0", "0 : 180", "0 : 90", "90 : 90", "90 : 270", "Random" };
-const char *monosynth_legato_names[] = { "Retrig", "Legato", "Fng Retrig", "Fng Legato" };
-
-const char *monosynth_filter_choices[] = {
- "12dB/oct Lowpass",
- "24dB/oct Lowpass",
- "2x12dB/oct Lowpass",
- "12dB/oct Highpass",
- "Lowpass+Notch",
- "Highpass+Notch",
- "6dB/oct Bandpass",
- "2x6dB/oct Bandpass",
-};
-
-parameter_properties monosynth_audio_module::param_props[] = {
- { wave_saw, 0, wave_count - 1, 1, PF_ENUM | PF_CTL_COMBO, monosynth_waveform_names, "o1_wave", "Osc1 Wave" },
- { wave_sqr, 0, wave_count - 1, 1, PF_ENUM | PF_CTL_COMBO, monosynth_waveform_names, "o2_wave", "Osc2 Wave" },
- { 10, 0, 100, 1.01, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "o12_detune", "O1<>2 Detune" },
- { 12, -24, 24, 1.01, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_SEMITONES, NULL, "o2_xpose", "Osc 2 transpose" },
- { 0, 0, 5, 1.01, PF_ENUM | PF_CTL_COMBO, monosynth_mode_names, "phase_mode", "Phase mode" },
- { 0.5, 0, 1, 1.01, PF_FLOAT | PF_SCALE_PERC, NULL, "o12_mix", "O1<>2 Mix" },
- { 1, 0, 7, 1.01, PF_ENUM | PF_CTL_COMBO, monosynth_filter_choices, "filter", "Filter" },
- { 33, 10,16000, 1.01, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ, NULL, "cutoff", "Cutoff" },
- { 2, 0.7, 8, 1.01, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB, NULL, "res", "Resonance" },
- { 0, -2400, 2400, 1.01, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "filter_sep", "Separation" },
- { 8000, -10800,10800, 1.01, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "env2cutoff", "Env->Cutoff" },
- { 1, 0, 1, 1.01, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "env2res", "Env->Res" },
- { 1, 0, 1, 1.01, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "env2amp", "Env->Amp" },
-
- { 1, 1,20000, 1.01, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "adsr_a", "Attack" },
- { 350, 10,20000, 1.01, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "adsr_d", "Decay" },
- { 0.5, 0, 1, 1.01, PF_FLOAT | PF_SCALE_PERC, NULL, "adsr_s", "Sustain" },
- { 50, 10,20000, 1.01, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "adsr_r", "Release" },
-
- { 0, 0, 1, 1.01, PF_BOOL | PF_CTL_TOGGLE, NULL, "key_follow", "Key Follow" },
- { 0, 0, 3, 1.01, PF_ENUM | PF_CTL_COMBO, monosynth_legato_names, "legato", "Legato Mode" },
- { 1, 1, 2000, 1.01, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "portamento", "Portamento" },
-
- { 0.5, 0, 1, 0.1, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "vel2filter", "Vel->Filter" },
- { 0, 0, 1, 0.1, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "vel2amp", "Vel->Amp" },
-};
-
static synth::ladspa_info monosynth_info = { 0x8480, "Monosynth", "Calf Monosynth", "Krzysztof Foltman", copyright, "SynthesizerPlugin" };
#if USE_LADSPA
diff --git a/src/monosynth.cpp b/src/monosynth.cpp
new file mode 100644
index 0000000..d6024a3
--- /dev/null
+++ b/src/monosynth.cpp
@@ -0,0 +1,376 @@
+/* Calf DSP Library
+ * Example audio modules - monosynth
+ *
+ * Copyright (C) 2001-2007 Krzysztof Foltman
+ *
+ * This program 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 of the License, or (at your option) any later version.
+ *
+ * This program 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.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#include <assert.h>
+#include <memory.h>
+#if USE_JACK
+#include <jack/jack.h>
+#endif
+#include <calf/giface.h>
+#include <calf/modules.h>
+#include <calf/modules_synths.h>
+
+using namespace synth;
+
+const char *monosynth_audio_module::port_names[] = {
+ "Out L", "Out R",
+};
+
+const char *monosynth_waveform_names[] = { "Sawtooth", "Square", "Pulse", "Sine", "Triangle", "Varistep", "Skewed Saw", "Skewed Square",
+ "Test 1", "Test 2", "Test 3", "Test 4", "Test 5", "Test 6", "Test 7", "Test 8" };
+const char *monosynth_mode_names[] = { "0 : 0", "0 : 180", "0 : 90", "90 : 90", "90 : 270", "Random" };
+const char *monosynth_legato_names[] = { "Retrig", "Legato", "Fng Retrig", "Fng Legato" };
+
+const char *monosynth_filter_choices[] = {
+ "12dB/oct Lowpass",
+ "24dB/oct Lowpass",
+ "2x12dB/oct Lowpass",
+ "12dB/oct Highpass",
+ "Lowpass+Notch",
+ "Highpass+Notch",
+ "6dB/oct Bandpass",
+ "2x6dB/oct Bandpass",
+};
+
+parameter_properties monosynth_audio_module::param_props[] = {
+ { wave_saw, 0, wave_count - 1, 1, PF_ENUM | PF_CTL_COMBO, monosynth_waveform_names, "o1_wave", "Osc1 Wave" },
+ { wave_sqr, 0, wave_count - 1, 1, PF_ENUM | PF_CTL_COMBO, monosynth_waveform_names, "o2_wave", "Osc2 Wave" },
+ { 10, 0, 100, 1.01, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "o12_detune", "O1<>2 Detune" },
+ { 12, -24, 24, 1.01, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_SEMITONES, NULL, "o2_xpose", "Osc 2 transpose" },
+ { 0, 0, 5, 1.01, PF_ENUM | PF_CTL_COMBO, monosynth_mode_names, "phase_mode", "Phase mode" },
+ { 0.5, 0, 1, 1.01, PF_FLOAT | PF_SCALE_PERC, NULL, "o12_mix", "O1<>2 Mix" },
+ { 1, 0, 7, 1.01, PF_ENUM | PF_CTL_COMBO, monosynth_filter_choices, "filter", "Filter" },
+ { 33, 10,16000, 1.01, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ, NULL, "cutoff", "Cutoff" },
+ { 2, 0.7, 8, 1.01, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB, NULL, "res", "Resonance" },
+ { 0, -2400, 2400, 1.01, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "filter_sep", "Separation" },
+ { 8000, -10800,10800, 1.01, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "env2cutoff", "Env->Cutoff" },
+ { 1, 0, 1, 1.01, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "env2res", "Env->Res" },
+ { 1, 0, 1, 1.01, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "env2amp", "Env->Amp" },
+
+ { 1, 1,20000, 1.01, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "adsr_a", "Attack" },
+ { 350, 10,20000, 1.01, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "adsr_d", "Decay" },
+ { 0.5, 0, 1, 1.01, PF_FLOAT | PF_SCALE_PERC, NULL, "adsr_s", "Sustain" },
+ { 50, 10,20000, 1.01, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "adsr_r", "Release" },
+
+ { 0, 0, 1, 1.01, PF_BOOL | PF_CTL_TOGGLE, NULL, "key_follow", "Key Follow" },
+ { 0, 0, 3, 1.01, PF_ENUM | PF_CTL_COMBO, monosynth_legato_names, "legato", "Legato Mode" },
+ { 1, 1, 2000, 1.01, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "portamento", "Portamento" },
+
+ { 0.5, 0, 1, 0.1, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "vel2filter", "Vel->Filter" },
+ { 0, 0, 1, 0.1, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "vel2amp", "Vel->Amp" },
+};
+
+void monosynth_audio_module::activate() {
+ running = false;
+ output_pos = 0;
+ queue_note_on = -1;
+ pitchbend = 1.f;
+ filter.reset();
+ filter2.reset();
+ float data[2048];
+ bandlimiter<11> bl;
+
+ // yes these waves don't have really perfect 1/x spectrum because of aliasing
+ // (so what?)
+ for (int i = 0 ; i < 1024; i++)
+ data[i] = (float)(i / 1024.f),
+ data[i + 1024] = (float)(i / 1024.f - 1.0f);
+ waves[wave_saw].make(bl, data);
+
+ for (int i = 0 ; i < 2048; i++)
+ data[i] = (float)(i < 1024 ? -1.f : 1.f);
+ waves[wave_sqr].make(bl, data);
+
+ for (int i = 0 ; i < 2048; i++)
+ data[i] = (float)(i < 64 ? -1.f : 1.f);
+ waves[wave_pulse].make(bl, data);
+
+ // XXXKF sure this is a waste of space, this will be fixed some day by better bandlimiter
+ for (int i = 0 ; i < 2048; i++)
+ data[i] = (float)sin(i * PI / 1024);
+ waves[wave_sine].make(bl, data);
+
+ for (int i = 0 ; i < 512; i++) {
+ data[i] = i / 512.0,
+ data[i + 512] = 1 - i / 512.0,
+ data[i + 1024] = - i / 512.0,
+ data[i + 1536] = -1 + i / 512.0;
+ }
+ waves[wave_triangle].make(bl, data);
+
+ for (int i = 0, j = 1; i < 2048; i++) {
+ data[i] = -1 + j / 1024.0;
+ if (i == j)
+ j *= 2;
+ }
+ waves[wave_varistep].make(bl, data);
+
+ for (int i = 0; i < 2048; i++) {
+ int ii = (i < 1024) ? i : 2048 - i;
+
+ data[i] = (min(1.f, (float)(i / 64.f))) * (1.0 - i / 2048.0) * (-1 + fmod (i * ii / 65536.0, 2.0));
+ }
+ waves[wave_skewsaw].make(bl, data);
+ for (int i = 0; i < 2048; i++) {
+ int ii = (i < 1024) ? i : 2048 - i;
+ data[i] = (min(1.f, (float)(i / 64.f))) * (1.0 - i / 2048.0) * (fmod (i * ii / 32768.0, 2.0) < 1.0 ? -1.0 : +1.0);
+ }
+ waves[wave_skewsqr].make(bl, data);
+
+ for (int i = 0; i < 1024; i++) {
+ data[i] = exp(-i / 1024.0);
+ data[i + 1024] = -exp(-i / 1024.0);
+ }
+ waves[wave_test1].make(bl, data);
+ for (int i = 0; i < 2048; i++) {
+ data[i] = exp(-i / 1024.0) * sin(i * PI / 1024) * cos(2 * PI * i / 1024);
+ }
+ waves[wave_test2].make(bl, data);
+ for (int i = 0; i < 2048; i++) {
+ int ii = (i < 1024) ? i : 2048 - i;
+ data[i] = (ii / 1024.0) * sin(i * 15 * PI / 1024 + 2 * PI * sin(i * 18 * PI / 1024)) * sin(i * 12 * PI / 1024 + 2 * PI * sin(i * 11 * PI / 1024));
+ }
+ waves[wave_test3].make(bl, data);
+ for (int i = 0; i < 2048; i++) {
+ data[i] = sin(i * 2 * PI / 1024) * sin(i * 2 * PI / 1024 + 0.5 * PI * sin(i * 18 * PI / 1024)) * sin(i * 1 * PI / 1024 + 0.5 * PI * sin(i * 11 * PI / 1024));
+ }
+ waves[wave_test4].make(bl, data);
+ for (int i = 0; i < 2048; i++) {
+ data[i] = sin(i * 2 * PI / 1024 + 0.2 * PI * sin(i * 13 * PI / 1024)) * sin(i * PI / 1024 + 0.2 * PI * sin(i * 15 * PI / 1024));
+ }
+ waves[wave_test5].make(bl, data);
+ waves[wave_test6].make(bl, data);
+ waves[wave_test7].make(bl, data);
+ waves[wave_test8].make(bl, data);
+}
+
+void monosynth_audio_module::calculate_buffer_ser()
+{
+ for (uint32_t i = 0; i < step_size; i++)
+ {
+ float osc1val = osc1.get();
+ float osc2val = osc2.get();
+ float wave = fgain * (osc1val + (osc2val - osc1val) * xfade);
+ wave = filter.process_d1(wave);
+ wave = filter2.process_d1(wave);
+ buffer[i] = softclip(wave);
+ fgain += fgain_delta;
+ }
+}
+
+void monosynth_audio_module::calculate_buffer_single()
+{
+ for (uint32_t i = 0; i < step_size; i++)
+ {
+ float osc1val = osc1.get();
+ float osc2val = osc2.get();
+ float wave = fgain * (osc1val + (osc2val - osc1val) * xfade);
+ wave = filter.process_d1(wave);
+ buffer[i] = softclip(wave);
+ fgain += fgain_delta;
+ }
+}
+
+void monosynth_audio_module::calculate_buffer_stereo()
+{
+ for (uint32_t i = 0; i < step_size; i++)
+ {
+ float osc1val = osc1.get();
+ float osc2val = osc2.get();
+ float wave1 = osc1val + (osc2val - osc1val) * xfade;
+ float wave2 = phaseshifter.process_ap(wave1);
+ buffer[i] = softclip(fgain * filter.process_d1(wave1));
+ buffer2[i] = softclip(fgain * filter2.process_d1(wave2));
+ fgain += fgain_delta;
+ }
+}
+
+void monosynth_audio_module::delayed_note_on()
+{
+ porta_time = 0.f;
+ start_freq = freq;
+ target_freq = freq = 440 * pow(2.0, (queue_note_on - 69) / 12.0);
+ ampctl = 1.0 + (queue_vel - 1.0) * *params[par_vel2amp];
+ fltctl = 1.0 + (queue_vel - 1.0) * *params[par_vel2filter];
+ set_frequency();
+ osc1.waveform = waves[wave1].get_level(osc1.phasedelta);
+ osc2.waveform = waves[wave2].get_level(osc2.phasedelta);
+
+ if (!running)
+ {
+ if (legato >= 2)
+ porta_time = -1.f;
+ osc1.reset();
+ osc2.reset();
+ filter.reset();
+ filter2.reset();
+ switch((int)*params[par_oscmode])
+ {
+ case 1:
+ osc2.phase = 0x80000000;
+ break;
+ case 2:
+ osc2.phase = 0x40000000;
+ break;
+ case 3:
+ osc1.phase = osc2.phase = 0x40000000;
+ break;
+ case 4:
+ osc1.phase = 0x40000000;
+ osc2.phase = 0xC0000000;
+ break;
+ case 5:
+ // rand() is crap, but I don't have any better RNG in Calf yet
+ osc1.phase = rand() << 16;
+ osc2.phase = rand() << 16;
+ break;
+ default:
+ break;
+ }
+ envelope.note_on();
+ running = true;
+ }
+ if (legato >= 2 && !gate)
+ porta_time = -1.f;
+ gate = true;
+ stopping = false;
+ if (!(legato & 1) || envelope.released()) {
+ envelope.note_on();
+ }
+ envelope.advance();
+ queue_note_on = -1;
+}
+
+void monosynth_audio_module::set_sample_rate(uint32_t sr) {
+ srate = sr;
+ crate = sr / step_size;
+ odcr = (float)(1.0 / crate);
+ phaseshifter.set_ap(1000.f, sr);
+ fgain = 0.f;
+ fgain_delta = 0.f;
+}
+
+void monosynth_audio_module::calculate_step()
+{
+ if (queue_note_on != -1)
+ delayed_note_on();
+ else if (stopping)
+ {
+ running = false;
+ dsp::zero(buffer, step_size);
+ if (is_stereo_filter())
+ dsp::zero(buffer2, step_size);
+ return;
+ }
+ float porta_total_time = *params[par_portamento] * 0.001f;
+
+ if (porta_total_time >= 0.00101f && porta_time >= 0) {
+ // XXXKF this is criminal, optimize!
+ float point = porta_time / porta_total_time;
+ if (point >= 1.0f) {
+ freq = target_freq;
+ porta_time = -1;
+ } else {
+ freq = start_freq * pow(target_freq / start_freq, point);
+ porta_time += odcr;
+ }
+ }
+ set_frequency();
+ envelope.advance();
+ float env = envelope.value;
+ cutoff = *params[par_cutoff] * pow(2.0f, env * fltctl * *params[par_envmod] * (1.f / 1200.f));
+ if (*params[par_keyfollow] >= 0.5f)
+ cutoff *= freq / 264.0f;
+ cutoff = dsp::clip(cutoff , 10.f, 18000.f);
+ float resonance = *params[par_resonance];
+ float e2r = *params[par_envtores];
+ float e2a = *params[par_envtoamp];
+ resonance = resonance * (1 - e2r) + (0.7 + (resonance - 0.7) * env * env) * e2r;
+ float cutoff2 = dsp::clip(cutoff * separation, 10.f, 18000.f);
+ float newfgain = 0.f;
+ switch(filter_type)
+ {
+ case flt_lp12:
+ filter.set_lp_rbj(cutoff, resonance, srate);
+ newfgain = min(0.7f, 0.7f / resonance) * ampctl;
+ break;
+ case flt_hp12:
+ filter.set_hp_rbj(cutoff, resonance, srate);
+ newfgain = min(0.7f, 0.7f / resonance) * ampctl;
+ break;
+ case flt_lp24:
+ filter.set_lp_rbj(cutoff, resonance, srate);
+ filter2.set_lp_rbj(cutoff2, resonance, srate);
+ newfgain = min(0.5f, 0.5f / resonance) * ampctl;
+ break;
+ case flt_lpbr:
+ filter.set_lp_rbj(cutoff, resonance, srate);
+ filter2.set_br_rbj(cutoff2, resonance, srate);
+ newfgain = min(0.5f, 0.5f / resonance) * ampctl;
+ break;
+ case flt_hpbr:
+ filter.set_hp_rbj(cutoff, resonance, srate);
+ filter2.set_br_rbj(cutoff2, resonance, srate);
+ newfgain = min(0.5f, 0.5f / resonance) * ampctl;
+ break;
+ case flt_2lp12:
+ filter.set_lp_rbj(cutoff, resonance, srate);
+ filter2.set_lp_rbj(cutoff2, resonance, srate);
+ newfgain = min(0.7f, 0.7f / resonance) * ampctl;
+ break;
+ case flt_bp6:
+ filter.set_bp_rbj(cutoff, resonance, srate);
+ newfgain = ampctl;
+ break;
+ case flt_2bp6:
+ filter.set_bp_rbj(cutoff, resonance, srate);
+ filter2.set_bp_rbj(cutoff2, resonance, srate);
+ newfgain = ampctl;
+ break;
+ }
+ newfgain *= 1.0 - (1.0 - env) * e2a;
+ fgain_delta = (newfgain - fgain) * (1.0 / step_size);
+ switch(filter_type)
+ {
+ case flt_lp24:
+ case flt_lpbr:
+ case flt_hpbr: // Oomek's wish
+ calculate_buffer_ser();
+ break;
+ case flt_lp12:
+ case flt_hp12:
+ case flt_bp6:
+ calculate_buffer_single();
+ break;
+ case flt_2lp12:
+ case flt_2bp6:
+ calculate_buffer_stereo();
+ break;
+ }
+ if (envelope.state == adsr::STOP)
+ {
+ for (int i = 0; i < step_size; i++)
+ buffer[i] *= (step_size - i) * (1.0f / step_size);
+ if (is_stereo_filter())
+ for (int i = 0; i < step_size; i++)
+ buffer2[i] *= (step_size - i) * (1.0f / step_size);
+ stopping = true;
+ }
+}
--
calf audio plugins packaging
More information about the pkg-multimedia-commits
mailing list