[SCM] calf/master: + Fixed jackhost license (it's GPL now, LGPL didn't make any sense for an application anyway) + Additions and fixes to audio_module framework (added quadratic mapping, fixed combo box for min != 0) + Added polysynth framework (currently used only by organ synthesizers, there will be more, I hope!) + Added organ synthesizer

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


The following commit has been merged in the master branch:
commit 2d79a4d46e4dd3741aca918b6c4d12d9e59bfb45
Author: kfoltman <kfoltman at 78b06b96-2940-0410-b7fc-879d825d01d8>
Date:   Mon Dec 10 21:33:41 2007 +0000

    + Fixed jackhost license (it's GPL now, LGPL didn't make any sense for an application anyway)
    + Additions and fixes to audio_module framework (added quadratic mapping, fixed combo box for min != 0)
    + Added polysynth framework (currently used only by organ synthesizers, there will be more, I hope!)
    + Added organ synthesizer
    
    
    
    git-svn-id: https://calf.svn.sourceforge.net/svnroot/calf/trunk@12 78b06b96-2940-0410-b7fc-879d825d01d8

diff --git a/src/Makefile.am b/src/Makefile.am
index 178bbc5..f127919 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -37,10 +37,10 @@ endif
 calfbenchmark_SOURCES = benchmark.cpp
 calfbenchmark_LDADD = 
 
-libcalf_la_SOURCES = modules.cpp giface.cpp
+libcalf_la_SOURCES = modules.cpp giface.cpp synth.cpp
 libcalf_la_LDFLAGS = -rpath $(ladspadir) -version-info 0:0:0
 
-libcalfstatic_la_SOURCES = modules.cpp giface.cpp
+libcalfstatic_la_SOURCES = modules.cpp giface.cpp synth.cpp
 libcalfstatic_la_LDFLAGS = -static
 
 clean-local:
diff --git a/src/calf/Makefile.am b/src/calf/Makefile.am
index e49e1f3..ceff08e 100644
--- a/src/calf/Makefile.am
+++ b/src/calf/Makefile.am
@@ -1,3 +1,3 @@
 calfdir = $(includedir)/calf
 
-calf_HEADERS = audio_fx.h benchmark.h biquad.h buffer.h delay.h fft.h fixed_point.h giface.h inertia.h jackhost.h modules.h modules_dev.h onepole.h osc.h primitives.h wave.h
+calf_HEADERS = audio_fx.h benchmark.h biquad.h buffer.h delay.h fft.h fixed_point.h giface.h inertia.h jackhost.h modules.h modules_dev.h onepole.h organ.h osc.h primitives.h synth.h wave.h
diff --git a/src/calf/audio_fx.h b/src/calf/audio_fx.h
index 8844693..dd9741f 100644
--- a/src/calf/audio_fx.h
+++ b/src/calf/audio_fx.h
@@ -93,7 +93,7 @@ public:
  * Single-tap chorus without feedback.
  * Perhaps MaxDelay should be a bit longer!
  */
-template<class T, int MaxDelay=256>
+template<class T, int MaxDelay=512>
 class simple_chorus: public chorus_base
 {
 protected:
@@ -104,6 +104,7 @@ public:
         dry = 0.5f;
         wet = 0.5f;
         min_delay = 0.005f;
+        mod_depth = 0.0025f;
         setup(44100);
     }
     void reset() {
@@ -116,10 +117,11 @@ public:
         phase = 0;
         set_rate(get_rate());
         set_min_delay(get_min_delay());
+        set_mod_depth(get_mod_depth());
     }
     template<class OutIter, class InIter>
     void process(OutIter buf_out, InIter buf_in, int nsamples) {
-        int mds = min_delay_samples + mod_depth_samples * 256 + 65536;
+        int mds = min_delay_samples + mod_depth_samples * 256 + 2*65536;
         int mdepth = mod_depth_samples;
         for (int i=0; i<nsamples; i++) {
             phase += dphase;
@@ -150,6 +152,8 @@ protected:
     simple_delay<MaxDelay,T> delay;
     float fb;
 public:
+    simple_flanger()
+    : fb(0) {}
     void reset() {
         delay.reset();
     }
diff --git a/src/calf/fixed_point.h b/src/calf/fixed_point.h
index b019a67..7df20dc 100644
--- a/src/calf/fixed_point.h
+++ b/src/calf/fixed_point.h
@@ -236,54 +236,6 @@ inline fixed_point<T, FractBits> operator*(int v, fixed_point<T, FractBits> v2)
 /// wave position (unsigned 64-bit int including 24-bit fractional part)
 typedef fixed_point<unsigned long long int, 24> wpos;
 
-/// really bad place for it, should move it to some other file
-template<class T>
-class wave_player {
-    T *data;
-    bool is_active;
-    unsigned int size;
-    wpos pos, rate;
-public:
-    void set_wave(T *_data, int _size) { 
-        data = _data;
-        size = _size;
-        is_active = false;
-    }
-    void get_wave(T *&_data, int &_size) {
-        _data = data;
-        _size = size;
-    }
-    void set_pos(wpos _pos) {
-        pos = _pos;
-        is_active = true;
-    }
-    wpos get_pos() {
-        return pos;
-    }
-    void set_rate(wpos _rate) {
-        rate = _rate;
-    }
-    template<class U>U get() {
-        U result;
-        if (!is_active) {
-            dsp::zero(result);
-            return result;
-        }
-        unsigned int ipos = pos.ipart();
-        if (ipos>=size) {
-            dsp::zero(result);
-            is_active = false;
-            return result;
-        }
-        result = pos.lerp_ptr_lookup_int<T, 12, stereo_sample<int> >(data);
-        pos += rate;
-        return result;
-    }
-    bool get_active() {
-        return is_active;
-    }
-};
-
 };
 
 #endif
diff --git a/src/calf/giface.h b/src/calf/giface.h
index dfda027..99706d8 100644
--- a/src/calf/giface.h
+++ b/src/calf/giface.h
@@ -47,11 +47,12 @@ enum parameter_flags
   PF_ENUM_MULTI = 0x0004, 
   
   PF_SCALEMASK = 0x0FF0,
-  PF_SCALE_DEFAULT = 0x0000,
-  PF_SCALE_LINEAR = 0x0010,
-  PF_SCALE_LOG = 0x0020,
-  PF_SCALE_GAIN = 0x0030,
-  PF_SCALE_PERC = 0x0040,
+  PF_SCALE_DEFAULT = 0x0000, ///< no scale given
+  PF_SCALE_LINEAR = 0x0010, ///< linear scale
+  PF_SCALE_LOG = 0x0020, ///< log scale
+  PF_SCALE_GAIN = 0x0030, ///< gain = -96dB..0 or -inf dB
+  PF_SCALE_PERC = 0x0040, ///< percent
+  PF_SCALE_QUAD = 0x0050, ///< quadratic scale (decent for some gain/amplitude values)
 
   PF_CTLMASK = 0x0F000,
   PF_CTL_DEFAULT = 0x00000,
diff --git a/src/calf/jackhost.h b/src/calf/jackhost.h
index 4f696ee..8edd3a3 100644
--- a/src/calf/jackhost.h
+++ b/src/calf/jackhost.h
@@ -1,6 +1,10 @@
-/* Calf DSP Library
+/* Calf DSP Library Utility Application - calfjackhost
  * API wrapper for JACK Audio Connection Kit
  *
+ * Note: while this header file is licensed under LGPL license,
+ * the application as a whole is GPLed because of partial dependency
+ * on phat graphics library.
+ *
  * Copyright (C) 2007 Krzysztof Foltman
  *
  * This program is free software; you can redistribute it and/or
diff --git a/src/calf/modules_dev.h b/src/calf/modules_dev.h
index 13a70fa..50e2a94 100644
--- a/src/calf/modules_dev.h
+++ b/src/calf/modules_dev.h
@@ -28,11 +28,12 @@
 #include "audio_fx.h"
 #include "inertia.h"
 #include "osc.h"
+#include "organ.h"
 
 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
@@ -47,7 +48,6 @@ public:
     float *outs[out_count];
     float *params[param_count];
     uint32_t srate, crate;
-    dsp::sine_table<float, 2048, 1> waveform_sine;
     waveform_family<11> waves[wave_count];
     waveform_oscillator<11> osc1, osc2;
     bool running, stopping, gate;
@@ -272,9 +272,63 @@ public:
         return 3;
     }
 };
+
+struct organ_audio_module: public drawbar_organ
+{
+public:
+    enum { par_drawbar1, par_drawbar2, par_drawbar3, par_drawbar4, par_drawbar5, par_drawbar6, par_drawbar7, par_drawbar8, par_drawbar9, par_foldover,
+        par_percmode, par_percharm, par_vibrato, par_master, param_count };
+    enum { in_count = 0, out_count = 2, support_midi = true, rt_capable = true };
+    static const char *param_names[];
+    float *ins[in_count]; 
+    float *outs[out_count];
+    float *params[param_count];
+    organ_parameters par_values;
+    uint32_t srate;
+
+    organ_audio_module()
+    : drawbar_organ(&par_values)
+    {
+    }
+    static parameter_properties param_props[];
+    void set_sample_rate(uint32_t sr) {
+        srate = sr;
+    }
+    void handle_event(uint8_t *data, int len) {
+        switch(*data >> 4)
+        {
+        case 9:
+            note_on(data[1], data[2]);
+            break;
+        case 8:
+            note_off(data[1], data[2]);
+            break;
+        case 11:
+            control_change(data[1], data[2]);
+            break;
+        }
+        default_handle_event(data, len, params, param_count);
+    }
+    void params_changed() {
+        for (int i = 0; i < param_count; i++)
+            ((float *)&par_values)[i] = *params[i];
+        set_vibrato(parameters->get_vibrato_mode());
+    }
+    void activate() {
+        setup(srate);
+    }
+    void deactivate() {
+    }
+    uint32_t process(uint32_t offset, uint32_t nsamples, uint32_t inputs_mask, uint32_t outputs_mask) {
+        float *o[2] = { outs[0] + offset, outs[1] + offset };
+        render_to(o, nsamples);
+        return 3;
+    }
     
 };
 
+};
+
 #endif
 
 #endif
diff --git a/src/calf/organ.h b/src/calf/organ.h
new file mode 100644
index 0000000..dd80e90
--- /dev/null
+++ b/src/calf/organ.h
@@ -0,0 +1,260 @@
+/* Calf DSP Library
+ * Drawbar organ emulator. 
+ *
+ * Copyright (C) 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.
+ */
+
+#ifndef __CALF_ORGAN_H
+#define __CALF_ORGAN_H
+
+#include "synth.h"
+
+namespace synth 
+{
+
+struct organ_parameters {
+    float drawbars[9];
+    float foldover;
+    float percussion_mode;
+    float harmonic;
+    float vibrato_mode;
+    float master;
+
+    inline bool get_foldover() { return foldover >= 0.5f; }
+    inline int get_percussion_mode() { return dsp::fastf2i_drm(percussion_mode); }
+    inline int get_harmonic() { return dsp::fastf2i_drm(harmonic); }
+    inline int get_vibrato_mode() { return dsp::fastf2i_drm(vibrato_mode); }
+};
+
+class organ_voice_base
+{
+protected:
+    dsp::sine_table<float, 4096, 1> sine_wave;
+    dsp::fixed_point<int, 20> phase, dphase;
+    int note;
+    dsp::decay<double> amp;
+
+    inline float sine(dsp::fixed_point<int, 20> ph) {
+        return ph.lerp_table_lookup_float(sine_wave.data);
+    }
+};
+
+class organ_voice: public synth::voice, public organ_voice_base {
+protected:    
+    bool released;
+    int h4, h6, h8, h12, h16;
+    dsp::fixed_point<int, 3> h10;
+    organ_parameters *parameters;
+
+public:
+    organ_voice(organ_parameters *_parameters)
+    : parameters(_parameters) {
+    }
+
+    void reset() {
+        phase = 0;
+    }
+
+    void note_on(int note, int /*vel*/) {
+        reset();
+        this->note = note;
+        dphase.set(synth::midi_note_to_phase(note, 0, sample_rate));
+        amp.set(1.0f);
+        released = false;
+        calc_foldover();
+    }
+
+    void note_off(int /* vel */) {
+        released = true;
+    }
+
+    void calc_foldover() {
+        h4 = 4, h6 = 6, h8 = 8, h10 = 10, h12 = 12, h16 = 16;
+        if (!parameters->get_foldover()) return;
+        const int foc = 108, foc2 = 108 + 12, foc3 = 108 + 24;
+        if (note + 24 >= foc) h4 = 2;
+        if (note + 24 >= foc2) h4 = 1;
+        if (note + 36 >= foc) h8 = 4;
+        if (note + 36 >= foc2) h8 = 2;
+        if (note + 40 >= foc) h10 = 5.0;
+        if (note + 40 >= foc2) h10 = 2.5;
+        if (note + 43 >= foc) h12 = 6;
+        if (note + 43 >= foc2) h12 = 3;
+        if (note + 48 >= foc) h16 = 8;
+        if (note + 48 >= foc2) h16 = 4;
+        if (note + 48 >= foc3) h16 = 2;
+//        printf("h= %d %d %d %f %d %d\n", h4, h6, h8, (float)h10, h12, h16);
+    }
+
+    void render_to(float *buf[1], int nsamples) {
+        if (note == -1)
+            return;
+
+        if (!amp.get_active())
+            dsp::zero(buf[0], nsamples);
+        else {
+            for (int i=0; i<nsamples; i++) {
+//                float osc = 0.2*sine(phase)+0.1*sine(4*phase)+0.08*sine(12*phase)+0.08*sine(16*phase);
+                float *drawbars = parameters->drawbars;
+                float osc = 
+                    drawbars[0]*sine(phase)+
+                    drawbars[1]*sine(2*phase)+
+                    drawbars[2]*sine(3*phase)+
+                    drawbars[3]*sine(h4*phase)+
+                    drawbars[4]*sine(h6*phase)+
+                    drawbars[5]*sine(h8*phase)+
+                    drawbars[6]*sine(h10*phase)+
+                    drawbars[7]*sine(h12*phase)+
+                    drawbars[8]*sine(h16*phase);
+                buf[0][i] += osc*amp.get();
+                if (released) amp.age_lin((1.0/44100.0)/0.03,0.0);
+                phase += dphase;
+            }
+        }
+    }
+    virtual int get_current_note() {
+        return note;
+    }
+    virtual bool get_active() {
+        return (note != -1) && amp.get_active();
+    }
+};
+
+/// Not a true voice, just something with similar-ish interface.
+class percussion_voice: public organ_voice_base {
+public:
+    organ_parameters *parameters;
+    int sample_rate;
+
+    percussion_voice(organ_parameters *_parameters)
+    {
+        parameters = _parameters;
+        note = -1;
+    }
+    void reset() {
+        phase = 0;
+        note = -1;
+    }
+
+    void note_on(int note, int /*vel*/) {
+        reset();
+        this->note = note;
+        dphase.set(synth::midi_note_to_phase(note, 0, sample_rate));
+        amp.set(1.0f);
+    }
+
+    // this doesn't really have a voice interface
+    void render_to(float *buf, int nsamples) {
+        if (note == -1)
+            return;
+
+        if (!amp.get_active())
+            return;
+        int mode = parameters->get_percussion_mode();
+        if (!mode)
+            return;
+        int harmonic = 2 * parameters->get_harmonic();
+        // XXXKF the decay needs work!
+        float age_const = mode == 1 ? 0.001f : 0.0003f;
+        for (int i = 0; i < nsamples; i++) {
+            float osc = 0.2 * sine(harmonic * phase);
+            buf[i] += osc * amp.get();
+            amp.age_exp(age_const, 0.0001f);
+            phase += dphase;
+        }
+    }
+    bool get_active() {
+        return (note != -1) && amp.get_active();
+    }
+    void setup(int sr) {
+        sample_rate = sr;
+    }
+};
+
+struct drawbar_organ: public synth::basic_synth {
+    organ_parameters *parameters;
+    percussion_voice percussion;
+    // chorus instead of rotary speaker is, well, cheesy
+    // let me think of something better some day
+    dsp::simple_chorus<float> chorus, chorus2;
+    
+    drawbar_organ(organ_parameters *_parameters)
+    : parameters(_parameters)
+    , percussion(_parameters) {
+    }
+    void render_to(float *output[], int nsamples)
+    {
+        float buf[4096], *bufptr[] = { buf };
+        dsp::zero(buf, nsamples);
+        basic_synth::render_to(bufptr, nsamples);
+        if (percussion.get_active())
+            percussion.render_to(buf, nsamples);
+        float chorus_buffer[4096];
+        float chorus_buffer2[4096];
+        chorus.process(chorus_buffer, buf, nsamples);
+        chorus2.process(chorus_buffer2, buf, nsamples);
+        float gain = parameters->master;
+        for (int i=0; i<nsamples; i++) {
+            output[0][i] = gain*(buf[i] + chorus_buffer[i] - chorus_buffer2[i]);
+            output[1][i] = gain*(buf[i] - chorus_buffer[i] + chorus_buffer2[i]);
+        }
+    }
+    synth::voice *alloc_voice() {
+        return new organ_voice(parameters);
+    }
+    virtual void first_note_on(int note, int vel) {
+        percussion.note_on(note, vel);
+    }
+    virtual void setup(int sr) {
+        basic_synth::setup(sr);
+        chorus.setup(sr);chorus2.setup(sr);
+        chorus.set_min_delay(0.001f);chorus2.set_min_delay(0.0015f);
+        chorus.set_mod_depth(0.002f);chorus2.set_mod_depth(0.001f);
+
+        chorus.set_rate(0.63);chorus2.set_rate(0.63);
+        chorus.set_wet(0.25f);chorus2.set_wet(0.25f);
+        chorus.set_dry(0.0f);chorus2.set_dry(0.0f);
+        percussion.setup(sr);
+    }
+    void set_vibrato(int mode)
+    {
+        switch(parameters->get_vibrato_mode())
+        {
+            case 0:
+                chorus.set_wet(0.f);
+                chorus2.set_wet(0.f);
+                break;
+            case 1:
+                chorus.set_wet(0.5f);
+                chorus2.set_wet(0.5f);
+                chorus.set_rate(0.6f);
+                chorus2.set_rate(0.66f);
+                break;
+            case 2:
+                chorus.set_wet(0.5f);
+                chorus2.set_wet(0.5f);
+                chorus.set_rate(6.33f);
+                chorus2.set_rate(6.63f);
+                break;
+        }
+    }
+};
+
+};
+
+#endif
diff --git a/src/calf/synth.h b/src/calf/synth.h
new file mode 100644
index 0000000..7efc54a
--- /dev/null
+++ b/src/calf/synth.h
@@ -0,0 +1,161 @@
+/* Calf DSP Library
+ * Framework for synthesizer-like plugins. This is based
+ * on my earlier work on Drawbar electric organ emulator.
+ *
+ * Copyright (C) 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.
+ */
+#ifndef __CALF_SYNTH_H
+#define __CALF_SYNTH_H
+
+#include <list>
+#include <stack>
+#include "primitives.h"
+#include "audio_fx.h"
+
+namespace synth {
+
+/**
+ * A kind of set with fast non-ordered iteration, used for storing lists of pressed keys.
+ */
+class keystack {
+private:
+    int dcount;
+    uint8_t active[128];
+    uint8_t states[128];
+public:
+    keystack() {
+        memset(states, 0xFF, sizeof(states));
+        dcount = 0;
+    }
+    void clear() {
+        for (int i=0; i<dcount; i++)
+            states[active[i]] = 0xFF;
+        dcount = 0;
+    }
+    bool push(int key) {
+        assert(key >= 0 && key <= 127);
+        if (states[key] != 0xFF) {
+            return true;
+        }
+        states[key] = dcount;
+        active[dcount++] = key;
+        return false;
+    }
+    bool pop(int key) {
+        if (states[key] == 0xFF)
+            return false;
+        int pos = states[key];
+        if (pos != dcount-1) {
+            // reuse the popped item's stack position for stack top
+            int last = active[dcount-1];
+            active[pos] = last;
+            // mark that position's new place on stack
+            states[last] = pos;
+        }
+        states[key] = 0xFF;
+        dcount--;
+        return true;
+    }    
+    inline bool has(int key) {
+        return states[key] != 0xFF;
+    }
+    inline int count() {
+        return dcount;
+    }
+    inline bool empty() {
+        return (dcount == 0);
+    }
+    inline int nth(int n) {
+        return active[n];
+    }
+};
+
+/**
+ * Convert MIDI note number to normalized UINT phase (where 1<<32 is full cycle).
+ * @param MIDI note number
+ * @param cents detune in cents (1/100 of a semitone)
+ * @param sr sample rate
+ */
+inline unsigned int midi_note_to_phase(int note, double cents, int sr) {
+    double incphase = 440*pow(2.0, (note-69)/12.0 + cents/1200.0)/sr;
+    if (incphase >= 1.0) incphase = fmod(incphase, 1.0);
+    incphase *= 65536.0*65536.0;
+    return (unsigned int)incphase;
+}
+
+// Base class for all voice objects
+class voice {
+public:
+    int sample_rate;
+
+    /// reset voice to default state (used when a voice is to be reused)
+    virtual void setup(int sr) { sample_rate = sr; }
+    /// reset voice to default state (used when a voice is to be reused)
+    virtual void reset()=0;
+    /// a note was pressed
+    virtual void note_on(int note, int vel)=0;
+    /// a note was released
+    virtual void note_off(int vel)=0;
+    /// check if voice can be removed from active voice list
+    virtual bool get_active()=0;
+    /// render voice data to buffer
+    virtual void render_to(float *buf[], int nsamples)=0;
+    /// return the note used by this voice
+    virtual int get_current_note()=0;
+    /// empty virtual destructor
+    virtual ~voice() {}
+};
+
+/// Base class for all kinds of polyphonic instruments, provides
+/// somewhat reasonable voice management, pedal support - and 
+/// little else. It's implemented as a base class with virtual
+/// functions, so there's some performance loss, but it shouldn't
+/// be horrible.
+/// @todo it would make sense to support all notes off controller too
+struct basic_synth {
+protected:
+    synth::keystack keystack, keystack_hold;
+    int sample_rate;
+    bool hold, sostenuto;
+    std::list<synth::voice *> active_voices;
+    std::stack<synth::voice *> unused_voices;
+
+    void kill_note(int note, int vel, bool just_one);
+public:
+    virtual void setup(int sr) {
+        sample_rate = sr;
+        hold = false;
+        sostenuto = false;
+    }
+    virtual synth::voice *give_voice();
+    virtual synth::voice *alloc_voice()=0;
+    virtual void render_to(float *output[], int nsamples);
+    virtual void note_on(int note, int vel);
+    virtual void first_note_on(int note, int vel) {}
+    virtual void control_change(int ctl, int val);
+    virtual void note_off(int note, int vel) {
+        if (keystack.pop(note)) {
+            kill_note(note, vel, keystack_hold.has(note));
+        }        
+    }
+    virtual ~basic_synth();
+};
+
+}
+
+#endif
diff --git a/src/calf/wave.h b/src/calf/wave.h
index 55a4806..5003b57 100644
--- a/src/calf/wave.h
+++ b/src/calf/wave.h
@@ -39,4 +39,51 @@ bool load_wave(dsp::dynamic_buffer<T> &dest, const char *file_name) {
     return false;
 }
 
+template<class T>
+class wave_player {
+    T *data;
+    bool is_active;
+    unsigned int size;
+    wpos pos, rate;
+public:
+    void set_wave(T *_data, int _size) { 
+        data = _data;
+        size = _size;
+        is_active = false;
+    }
+    void get_wave(T *&_data, int &_size) {
+        _data = data;
+        _size = size;
+    }
+    void set_pos(wpos _pos) {
+        pos = _pos;
+        is_active = true;
+    }
+    wpos get_pos() {
+        return pos;
+    }
+    void set_rate(wpos _rate) {
+        rate = _rate;
+    }
+    template<class U>U get() {
+        U result;
+        if (!is_active) {
+            dsp::zero(result);
+            return result;
+        }
+        unsigned int ipos = pos.ipart();
+        if (ipos>=size) {
+            dsp::zero(result);
+            is_active = false;
+            return result;
+        }
+        result = pos.lerp_ptr_lookup_int<T, 12, int >(data);
+        pos += rate;
+        return result;
+    }
+    bool get_active() {
+        return is_active;
+    }
+};
+
 }
diff --git a/src/giface.cpp b/src/giface.cpp
index 240d573..2634e46 100644
--- a/src/giface.cpp
+++ b/src/giface.cpp
@@ -38,6 +38,9 @@ float parameter_properties::from_01(float value01) const
     default:
         value = min + (max - min) * value01;
         break;
+    case PF_SCALE_QUAD:
+        value = min + (max - min) * value01 * value01;
+        break;
     case PF_SCALE_LOG:
         value = min * pow(max / min, value01);
         break;
@@ -71,6 +74,8 @@ float parameter_properties::to_01(float value) const
     case PF_SCALE_PERC:
     default:
         return (value - min) / (max - min);
+    case PF_SCALE_QUAD:
+        return sqrt((value - min) / (max - min));
     case PF_SCALE_LOG:
         value /= min;
         return log(value) / log(max / min);
diff --git a/src/jackhost.cpp b/src/jackhost.cpp
index c4f109f..ac39b2b 100644
--- a/src/jackhost.cpp
+++ b/src/jackhost.cpp
@@ -1,21 +1,23 @@
-/* Calf DSP Library
+/* Calf DSP Library Utility Application - calfjackhost
  * Standalone application module wrapper example.
  * Copyright (C) 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.
+ * Note: This module uses phat graphics library, so it's 
+ * licensed under GPL license, not LGPL.
  *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU 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.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
  */
 #include <getopt.h>
 #include <stdint.h>
@@ -145,7 +147,7 @@ void jack_host_gui::create(synth::jack_host_base *_host, const char *title)
         {
             widget  = gtk_combo_box_new_text ();
             for (int j = (int)props.min; j <= (int)props.max; j++)
-                gtk_combo_box_append_text (GTK_COMBO_BOX (widget), props.choices[j]);
+                gtk_combo_box_append_text (GTK_COMBO_BOX (widget), props.choices[j - (int)props.min]);
             gtk_combo_box_set_active (GTK_COMBO_BOX (widget), (int)host->get_params()[i] - (int)props.min);
             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);
@@ -275,9 +277,15 @@ int main(int argc, char *argv[])
 #ifdef ENABLE_EXPERIMENTAL
         else if (!strcmp(effect_name, "monosynth"))
             jh = new jack_host<monosynth_audio_module>();
+        else if (!strcmp(effect_name, "organ"))
+            jh = new jack_host<organ_audio_module>();
 #endif
         else {
+#ifdef ENABLE_EXPERIMENTAL
+            fprintf(stderr, "Unknown filter name; allowed are: reverb, flanger, filter, organ, monosynth\n");
+#else
             fprintf(stderr, "Unknown filter name; allowed are: reverb, flanger, filter\n");
+#endif
             return 1;
         }
         jh->open(client_name, input_name, output_name, midi_name);
diff --git a/src/modules.cpp b/src/modules.cpp
index 97d53e2..484420f 100644
--- a/src/modules.cpp
+++ b/src/modules.cpp
@@ -105,7 +105,40 @@ synth::ladspa_wrapper<filter_audio_module> filter(filter_info);
 
 ////////////////////////////////////////////////////////////////////////////
 #ifdef ENABLE_EXPERIMENTAL
-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 *organ_audio_module::param_names[] = {"Out L", "Out R", "32'", "16'", "10 2/3'", "8'", "4'", "3 1/5'", "2 2/3'", "2'", "1'", "Foldover", "Perc Mode", "Perc Harm", "Vibrato Speed", "Master Volume"};
+
+const char *organ_percussion_mode_names[] = { "Off", "Short", "Long" };
+const char *organ_percussion_harmonic_names[] = { "2nd", "3rd" };
+const char *organ_vibrato_speed_names[] = { "Off", "Slow", "Fast" };
+
+parameter_properties organ_audio_module::param_props[] = {
+    { 0.3,       0,  1, 1.01, PF_FLOAT | PF_SCALE_QUAD | PF_CTL_KNOB, NULL },
+    { 0.3,       0,  1, 1.01, PF_FLOAT | PF_SCALE_QUAD | PF_CTL_KNOB, NULL },
+    { 0,         0,  1, 1.01, PF_FLOAT | PF_SCALE_QUAD | PF_CTL_KNOB, NULL },
+    { 0.3,       0,  1, 1.01, PF_FLOAT | PF_SCALE_QUAD | PF_CTL_KNOB, NULL },
+    { 0,         0,  1, 1.01, PF_FLOAT | PF_SCALE_QUAD | PF_CTL_KNOB, NULL },
+    { 0,         0,  1, 1.01, PF_FLOAT | PF_SCALE_QUAD | PF_CTL_KNOB, NULL },
+    { 0,         0,  1, 1.01, PF_FLOAT | PF_SCALE_QUAD | PF_CTL_KNOB, NULL },
+    { 0,         0,  1, 1.01, PF_FLOAT | PF_SCALE_QUAD | PF_CTL_KNOB, NULL },
+    { 0,         0,  1, 1.01, PF_FLOAT | PF_SCALE_QUAD | PF_CTL_KNOB, NULL },
+
+    { 1,         0,  1, 1.01, PF_BOOL | PF_CTL_TOGGLE, NULL },
+    { 1,         0,  2, 1.01, PF_ENUM | PF_CTL_COMBO, organ_percussion_mode_names },
+    { 3,         2,  3, 1.01, PF_ENUM | PF_CTL_COMBO, organ_percussion_harmonic_names },
+    { 1,         0,  2, 1.01, PF_ENUM | PF_CTL_COMBO, organ_vibrato_speed_names },
+
+    { 0.2,         0,  1, 1.01, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB, NULL },
+};
+
+static synth::ladspa_info organ_info = { 0x8481, "Organ", "Calf Organ", "Krzysztof Foltman", copyright, "SynthesizerPlugin" };
+
+#if USE_LADSPA
+synth::ladspa_wrapper<organ_audio_module> organ(organ_info);
+#endif
+
+#endif
+////////////////////////////////////////////////////////////////////////////
+#ifdef ENABLE_EXPERIMENTAL
 
 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" };
@@ -126,6 +159,8 @@ parameter_properties monosynth_audio_module::param_props[] = {
     { 0,          0,    1, 1.01, PF_BOOL | PF_CTL_TOGGLE, NULL },
 };
 
+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"};
+
 static synth::ladspa_info monosynth_info = { 0x8480, "Monosynth", "Calf Monosynth", "Krzysztof Foltman", copyright, "SynthesizerPlugin" };
 
 #if USE_LADSPA
diff --git a/src/synth.cpp b/src/synth.cpp
new file mode 100644
index 0000000..943e5f5
--- /dev/null
+++ b/src/synth.cpp
@@ -0,0 +1,133 @@
+/* Calf DSP Library
+ * Generic polyphonic synthesizer framework.
+ *
+ * Copyright (C) 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/synth.h>
+
+using namespace dsp;
+using namespace synth;
+
+void basic_synth::kill_note(int note, int vel, bool just_one)
+{
+    for (list<synth::voice *>::iterator it = active_voices.begin(); it != active_voices.end(); it++) {
+        if ((*it)->get_current_note() == note) {
+            (*it)->note_off(vel);
+            if (just_one)
+                return;
+        }
+    }
+}
+
+synth::voice *basic_synth::give_voice()
+{
+    if (unused_voices.empty())
+        return alloc_voice();
+    else {
+        synth::voice *v = unused_voices.top();
+        unused_voices.pop();
+        v->reset();
+        return v;
+    }   
+}
+
+void basic_synth::note_on(int note, int vel)
+{
+    if (!vel) {
+        note_off(note, 0);
+        return;
+    }
+    bool perc = keystack_hold.empty() && keystack.empty();
+    synth::voice *v = alloc_voice();
+    v->setup(sample_rate);
+    if (hold)
+        keystack_hold.push(note);
+    else
+        keystack.push(note);
+    v->note_on(note, vel);
+    active_voices.push_back(v);
+    if (perc) {
+        first_note_on(note, vel);
+    }
+}
+
+
+void basic_synth::control_change(int ctl, int val)
+{
+    if (ctl == 64) { // HOLD controller
+        bool prev = hold;
+        hold = (val >= 64);
+        if (!hold && prev && !sostenuto) {
+            // HOLD was released - release all keys which were previously held
+            for (int i=0; i<keystack_hold.count(); i++) {
+                kill_note(keystack_hold.nth(i), 0, false);
+            }
+            keystack_hold.clear();
+        }
+    }
+    if (ctl == 66) { // SOSTENUTO controller
+        bool prev = sostenuto;
+        sostenuto = (val >= 64);
+        if (sostenuto && !prev) {
+            // SOSTENUTO was pressed - move all notes onto sustain stack
+            for (int i=0; i<keystack.count(); i++) {
+                keystack_hold.push(keystack.nth(i));
+            }
+            keystack.clear();
+        }
+        if (!sostenuto && prev) {
+            // SOSTENUTO was released - release all keys which were previously held
+            for (int i=0; i<keystack_hold.count(); i++) {
+                kill_note(keystack_hold.nth(i), 0, false);
+            }
+            keystack_hold.clear();
+        }
+    }
+}
+
+void basic_synth::render_to(float *output[], int nsamples)
+{
+    // render voices, eliminate ones that aren't sounding anymore
+    for (list<synth::voice *>::iterator i = active_voices.begin(); i != active_voices.end();) {
+        synth::voice *v = *i;
+        v->render_to(output, nsamples);
+        if (!v->get_active()) {
+            i = active_voices.erase(i);
+            unused_voices.push(v);
+            continue;
+        }
+        i++;
+    }
+} 
+
+basic_synth::~basic_synth()
+{
+    while(!unused_voices.empty()) {
+        delete unused_voices.top();
+        unused_voices.pop();
+    }
+    for (list<voice *>::iterator i = active_voices.begin(); i != active_voices.end(); i++)
+        delete *i;
+}
+

-- 
calf audio plugins packaging



More information about the pkg-multimedia-commits mailing list