[SCM] calf/master: + Lots of new functionality and bugfixes (clicks!) in monosynth + Support for semitone unit + Support for toggle parameters in calfjackhost

js at users.alioth.debian.org js at users.alioth.debian.org
Tue May 7 15:36:42 UTC 2013


The following commit has been merged in the master branch:
commit b4e5ab8eb062c3cfbe31bcbd51c2178614477753
Author: kfoltman <kfoltman at 78b06b96-2940-0410-b7fc-879d825d01d8>
Date:   Sat Dec 8 22:53:14 2007 +0000

    + Lots of new functionality and bugfixes (clicks!) in monosynth
    + Support for semitone unit
    + Support for toggle parameters in calfjackhost
    
    
    git-svn-id: https://calf.svn.sourceforge.net/svnroot/calf/trunk@9 78b06b96-2940-0410-b7fc-879d825d01d8

diff --git a/src/calf/giface.h b/src/calf/giface.h
index 5597f2a..dfda027 100644
--- a/src/calf/giface.h
+++ b/src/calf/giface.h
@@ -72,6 +72,7 @@ enum parameter_flags
   PF_UNIT_SEC = 0x04000000,
   PF_UNIT_MSEC = 0x05000000,
   PF_UNIT_CENTS = 0x06000000,
+  PF_UNIT_SEMITONES = 0x07000000,
 };
 
 struct parameter_properties
diff --git a/src/calf/modules_dev.h b/src/calf/modules_dev.h
index 337ad19..e439180 100644
--- a/src/calf/modules_dev.h
+++ b/src/calf/modules_dev.h
@@ -34,10 +34,12 @@ namespace synth {
 using namespace dsp;
         
 /// Monosynth-in-making. Parameters may change at any point, so don't make songs with it!
+/// It lacks inertia for parameters, even for those that really need it.
 class monosynth_audio_module
 {
 public:
-    enum { par_cutoff, par_resonance, par_envmod, par_decay, param_count };
+    enum { wave_saw, wave_sqr, wave_pulse, wave_sine, wave_triangle, wave_count };
+    enum { par_wave1, par_wave2, par_detune, par_osc2xpose, par_oscmode, par_oscmix, par_cutoff, par_resonance, par_envmod, par_envtores, par_decay, par_keyfollow, par_legato, param_count };
     enum { in_count = 0, out_count = 2, support_midi = true, rt_capable = true };
     enum { step_size = 64 };
     static const char *param_names[];
@@ -46,18 +48,22 @@ public:
     float *params[param_count];
     uint32_t srate, crate;
     dsp::sine_table<float, 2048, 1> waveform_sine;
-    waveform_family<11> waves_saw;
+    waveform_family<11> waves[wave_count];
     waveform_oscillator<11> osc1, osc2;
-    bool running;
+    bool running, stopping, gate;
     int last_key;
     
     float buffer[step_size];
     uint32_t output_pos;
     biquad<float> filter;
     biquad<float> filter2;
-    float cutoff, decay_factor;
+    int wave1, wave2;
+    float freq, cutoff, decay_factor;
+    float detune, xpose, xfade;
     int voice_age;
     float odcr;
+    int queue_note_on;
+    bool legato;
     
     static parameter_properties param_props[];
     void set_sample_rate(uint32_t sr) {
@@ -65,80 +71,181 @@ public:
         crate = sr / step_size;
         odcr = (float)(1.0 / crate);
     }
+    void note_on()
+    {
+        freq = 440 * pow(2.0, (queue_note_on - 69) / 12.0);
+        osc1.set_freq(freq * (2 - detune), srate);
+        osc2.set_freq(freq * (detune) * xpose, srate);
+        osc1.waveform = waves[wave1].get_level(osc1.phasedelta);
+        osc2.waveform = waves[wave2].get_level(osc2.phasedelta);
+        
+        if (!running)
+        {
+            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;
+            }
+            voice_age = 0;
+            running = true;
+        }
+        gate = true;
+        stopping = false;
+        if (!legato)
+            voice_age = 0;
+        queue_note_on = -1;
+    }
     void handle_event(uint8_t *data, int len) {
         switch(*data >> 4)
         {
         case 9:
             if (data[2]) {
+                queue_note_on = data[1];
                 last_key = data[1];
-                float freq = 440 * pow(2.0, (last_key - 69) / 12.0);
-                osc1.set_freq(freq*0.995, srate);
-                osc2.set_freq(freq*1.005, srate);
-                osc2.waveform = osc1.waveform = waves_saw.get_level(osc1.phasedelta);
-                if (!running)
-                {
-                    osc1.reset();
-                    osc2.reset();
-                    filter.reset();
-                    filter2.reset();
-                    cutoff = 16000;
-                    voice_age = 0;
-                    running = true;
-                }
             }
             // printf("note on %d %d\n", data[1], data[2]);
             break;
         case 8:
             // printf("note off %d %d\n", data[1], data[2]);
             if (data[1] == last_key)
-                running = false;
+                gate = false;
             break;
         }
         default_handle_event(data, len, params, param_count);
     }
     void params_changed() {
         decay_factor = odcr * 1000.0 / *params[par_decay];
+        wave1 = dsp::clip(dsp::fastf2i_drm(*params[par_wave1]), 0, (int)wave_count - 1);
+        wave2 = dsp::clip(dsp::fastf2i_drm(*params[par_wave2]), 0, (int)wave_count - 1);
+        detune = pow(2.0, *params[par_detune] / 1200.0);
+        xpose = pow(2.0, *params[par_osc2xpose] / 12.0);
+        xfade = *params[par_oscmix];
+        legato = *params[par_legato] >= 0.5f;
     }
     void activate() {
         running = false;
         output_pos = 0;
+        queue_note_on = -1;
         filter.reset();
         filter2.reset();
         float data[2048];
-        for (int i = 0 ; i < 2048; i++)
-            data[i] = (float)(-1.f + i / 1024.f);
         bandlimiter<11> bl;
-        waves_saw.make(bl, data);
+
+        // 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);
     }
     void deactivate() {
     }
     void calculate_step() {
+        if (queue_note_on != -1)
+            note_on();
+        else if (stopping)
+        {
+            running = false;
+            dsp::zero(buffer, step_size);
+            return;
+        }
         float env = max(0.f, 1.f - voice_age * decay_factor);
         cutoff = *params[par_cutoff] * pow(2.0f, env * *params[par_envmod] * (1.f / 1200.f));
-        if (cutoff < 33.f)
-            cutoff = 33.f;
+        if (*params[par_keyfollow] >= 0.5f)
+            cutoff *= freq / 264.0f;
+        if (cutoff < 10.f)
+            cutoff = 10.f;
         if (cutoff > 16000.f)
             cutoff = 16000.f;
-        filter.set_lp_rbj(cutoff, *params[par_resonance], srate);
+        float resonance = *params[par_resonance];
+        float e2r = *params[par_envtores];
+        resonance = resonance * (1 - e2r) + (0.7 + (resonance - 0.7) * env) * e2r;
+        filter.set_lp_rbj(cutoff, resonance, srate);
+        float fgain = min(0.5, 0.5 / resonance);
         filter2.copy_coeffs(filter);
         for (uint32_t i = 0; i < step_size; i++) 
         {
-            float wave = 0.2f * (osc1.get() + osc2.get());
+            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);
+            // primitive waveshaping (hard-knee)
+            float abswave = fabs(wave);
+            if (abswave > 0.75)
+            {
+                abswave = abswave - 0.5 * (abswave - 0.75);
+                if (abswave > 1.0)
+                    abswave = 1.0;
+                wave = (wave > 0.0) ? abswave : - abswave;
+                
+            }
             buffer[i] = dsp::clip(wave, -1.f, +1.f);
         }
+        if (!gate)
+        {
+            for (int i = 0; i < step_size; i++)
+                buffer[i] *= (step_size - i) * (1.0f / step_size);
+            stopping = true;
+        }
 
         voice_age++;
     }
     uint32_t process(uint32_t offset, uint32_t nsamples, uint32_t inputs_mask, uint32_t outputs_mask) {
-        if (!running)
+        if (!running && queue_note_on == -1)
             return 0;
         uint32_t op = offset;
         uint32_t op_end = offset + nsamples;
         while(op < op_end) {
-            if (output_pos == 0)
-                calculate_step();
+            if (output_pos == 0) {
+                if (running || queue_note_on != -1)
+                    calculate_step();
+                else 
+                    dsp::zero(buffer, step_size);
+            }
             if(op < op_end) {
                 uint32_t ip = output_pos;
                 uint32_t len = std::min(step_size - output_pos, op_end - op);
diff --git a/src/calf/primitives.h b/src/calf/primitives.h
index 5af0800..1aaad76 100644
--- a/src/calf/primitives.h
+++ b/src/calf/primitives.h
@@ -397,6 +397,16 @@ bool sine_table<T,N,Multiplier>::initialized = false;
 template<class T, int N, int Multiplier>
 T sine_table<T,N,Multiplier>::data[N+1];
 
+/// fast float to int conversion using default rounding mode
+inline int fastf2i_drm(float f)
+{
+	volatile int v;
+	__asm ( "flds %1; fistpl %0" : "=m"(v) : "m"(f));
+	return v;
+}
+
+
+
 };
 
 #endif
diff --git a/src/giface.cpp b/src/giface.cpp
index 7603b45..240d573 100644
--- a/src/giface.cpp
+++ b/src/giface.cpp
@@ -29,21 +29,37 @@ using namespace std;
 
 float parameter_properties::from_01(float value01) const
 {
+    float value = dsp::clip(value01, 0.f, 1.f);
     switch(flags & PF_SCALEMASK)
     {
     case PF_SCALE_DEFAULT:
     case PF_SCALE_LINEAR:
     case PF_SCALE_PERC:
     default:
-        return min + (max - min) * value01;
+        value = min + (max - min) * value01;
+        break;
     case PF_SCALE_LOG:
-        return min * pow(max / min, value01);
+        value = min * pow(max / min, value01);
+        break;
     case PF_SCALE_GAIN:
         if (value01 < 0.0001)
-            return min;
-        float rmin = 1.0f / 4096.0f;
-        return rmin * pow(max / rmin, value01);
+            value = min;
+        else {
+            float rmin = 1.0f / 4096.0f;
+            value = rmin * pow(max / rmin, value01);
+        }
+        break;
+    }
+    switch(flags & PF_TYPEMASK)
+    {
+    case PF_INT:
+    case PF_BOOL:
+    case PF_ENUM:
+    case PF_ENUM_MULTI:
+        value = (int)value;
+        break;
     }
+    return value;
 }
 
 float parameter_properties::to_01(float value) const
@@ -82,6 +98,7 @@ std::string parameter_properties::to_string(float value) const
     case PF_UNIT_SEC: return string(buf) + " s";
     case PF_UNIT_MSEC: return string(buf) + " ms";
     case PF_UNIT_CENTS: return string(buf) + " ct";
+    case PF_UNIT_SEMITONES: return string(buf) + "#";
     }
     return string(buf);
 }
diff --git a/src/jackhost.cpp b/src/jackhost.cpp
index e0271a6..c4f109f 100644
--- a/src/jackhost.cpp
+++ b/src/jackhost.cpp
@@ -56,6 +56,7 @@ public:
     void create(synth::jack_host_base *_host, const char *title);
     static void hscale_value_changed(GtkHScale *widget, gpointer value);
     static void combo_value_changed(GtkComboBox *widget, gpointer value);
+    static void toggle_value_changed(GtkCheckButton *widget, gpointer value);
 
 #if USE_PHAT
     static void knob_value_changed(PhatKnob *widget, gpointer value);
@@ -89,6 +90,15 @@ void jack_host_gui::combo_value_changed(GtkComboBox *widget, gpointer value)
     jh.set_params();
 }
 
+void jack_host_gui::toggle_value_changed(GtkCheckButton *widget, gpointer value)
+{
+    jack_host_param *jhp = (jack_host_param *)value;
+    jack_host_base &jh = *jhp->gui->host;
+    int nparam = jhp->param_no;
+    jh.get_params()[nparam] = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget)) + jh.get_param_props()[jhp->param_no].min;
+    jh.set_params();
+}
+
 #if USE_PHAT
 void jack_host_gui::knob_value_changed(PhatKnob *widget, gpointer value)
 {
@@ -140,6 +150,14 @@ void jack_host_gui::create(synth::jack_host_base *_host, const char *title)
             gtk_signal_connect (GTK_OBJECT (widget), "changed", G_CALLBACK (combo_value_changed), (gpointer)&params[i]);
             gtk_table_attach (GTK_TABLE (table), widget, 1, 3, i + 1, i + 2, GTK_EXPAND, GTK_SHRINK, 0, 0);
         }
+        else if ((props.flags & PF_TYPEMASK) == PF_BOOL && 
+                 (props.flags & PF_CTLMASK) == PF_CTL_TOGGLE)
+        {
+            widget  = gtk_check_button_new ();
+            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), (int)host->get_params()[i] - (int)props.min);
+            gtk_signal_connect (GTK_OBJECT (widget), "toggled", G_CALLBACK (toggle_value_changed), (gpointer)&params[i]);
+            gtk_table_attach (GTK_TABLE (table), widget, 1, 3, i + 1, i + 2, GTK_EXPAND, GTK_SHRINK, 0, 0);
+        }
         else
         {
 #if USE_PHAT
diff --git a/src/modules.cpp b/src/modules.cpp
index 4a0fc24..97d53e2 100644
--- a/src/modules.cpp
+++ b/src/modules.cpp
@@ -105,13 +105,25 @@ synth::ladspa_wrapper<filter_audio_module> filter(filter_info);
 
 ////////////////////////////////////////////////////////////////////////////
 #ifdef ENABLE_EXPERIMENTAL
-const char *monosynth_audio_module::param_names[] = {"Out L", "Out R", "Cutoff", "Resonance", "Filter Env", "Decay"};
+const char *monosynth_audio_module::param_names[] = {"Out L", "Out R", "Osc1 Wave", "Osc2 Wave", "Osc 1/2 Detune", "Osc 2 Transpose", "Phase Mode", "Osc Mix", "Cutoff", "Resonance", "Env->Cutoff", "Env->Res", "Decay", "Key Follow", "Legato"};
+
+const char *monosynth_waveform_names[] = { "Sawtooth", "Square", "Pulse", "Sine", "Triangle" };
+const char *monosynth_mode_names[] = { "0 : 0", "0 : 180", "0 : 90", "90 : 90", "90 : 270", "Random" };
 
 parameter_properties monosynth_audio_module::param_props[] = {
+    { wave_saw,         0,wave_count - 1, 1, PF_ENUM | PF_CTL_COMBO, monosynth_waveform_names },
+    { wave_pulse,         0,wave_count - 1, 1, PF_ENUM | PF_CTL_COMBO, monosynth_waveform_names },
+    { 10,         0,  100, 1.01, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL },
+    { 12,       -24,   24, 1.01, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_SEMITONES, NULL },
+    { 0,          0,    5, 1.01, PF_ENUM | PF_CTL_COMBO, monosynth_mode_names },
+    { 0.5,        0,    1, 1.01, PF_FLOAT | PF_SCALE_PERC, NULL },
     { 33,        10,16000, 1.01, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ, NULL },
-    { 2,        0.7,    4, 1.01, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB, NULL },
-    { 8000,       0,10800, 1.01, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL },
-    { 350,      10,20000, 1.01, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL },
+    { 2,        0.7,    8, 1.01, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB, NULL },
+    { 8000,  -10800,10800, 1.01, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL },
+    { 0.5,        0,    1, 1.01, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB, NULL },
+    { 350,       10,20000, 1.01, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL },
+    { 0,          0,    1, 1.01, PF_BOOL | PF_CTL_TOGGLE, NULL },
+    { 0,          0,    1, 1.01, PF_BOOL | PF_CTL_TOGGLE, NULL },
 };
 
 static synth::ladspa_info monosynth_info = { 0x8480, "Monosynth", "Calf Monosynth", "Krzysztof Foltman", copyright, "SynthesizerPlugin" };

-- 
calf audio plugins packaging



More information about the pkg-multimedia-commits mailing list