[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)¶ms[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