[SCM] calf/master: + Monosynth: initial implementation of mod matrix (standalone only, no persistence, few sources/destinations, inefficient)
js at users.alioth.debian.org
js at users.alioth.debian.org
Tue May 7 15:39:32 UTC 2013
The following commit has been merged in the master branch:
commit f6939cd112c17d50d098bfd4990ab5cc11c399fa
Author: Krzysztof Foltman <wdev at foltman.com>
Date: Sat Apr 25 23:41:19 2009 +0100
+ Monosynth: initial implementation of mod matrix (standalone only, no persistence, few sources/destinations, inefficient)
diff --git a/gui/gui-monosynth.xml b/gui/gui-monosynth.xml
index c484732..7226e64 100644
--- a/gui/gui-monosynth.xml
+++ b/gui/gui-monosynth.xml
@@ -214,5 +214,8 @@
</frame>
</hbox>
</vbox>
+ <vbox border="10" page="Modulation Matrix">
+ <listview param="scale_detune" />
+ </vbox>
</notebook>
</vbox>
diff --git a/src/calf/giface.h b/src/calf/giface.h
index eef4a89..320b04b 100644
--- a/src/calf/giface.h
+++ b/src/calf/giface.h
@@ -232,10 +232,10 @@ struct table_edit_iface
virtual uint32_t get_table_rows(int param) = 0;
/// retrieve data item from the plugin
- virtual std::string get_cell(int param, int column) { return calf_utils::i2s(param)+":"+calf_utils::i2s(column); }
+ virtual std::string get_cell(int param, int row, int column) { return calf_utils::i2s(row)+":"+calf_utils::i2s(column); }
/// set data item to the plugin
- virtual void set_cell(int param, int column, const std::string &src, std::string &error) { error.clear(); }
+ virtual void set_cell(int param, int row, int column, const std::string &src, std::string &error) { error.clear(); }
/// return a line graph interface for a specific parameter/column (unused for now)
virtual line_graph_iface *get_graph_iface(int param, int column) { return NULL; }
@@ -402,6 +402,9 @@ public:
/// Handle MIDI Pitch Bend
/// @param value pitch bend value (-8192 to 8191, defined as in MIDI ie. 8191 = 200 ct by default)
inline void pitch_bend(int value) {}
+ /// Handle MIDI Channel Pressure
+ /// @param value channel pressure (0 to 127)
+ inline void channel_pressure(int value) {}
/// Called when params are changed (before processing)
inline void params_changed() {}
/// LADSPA-esque activate function, except it is called after ports are connected, not before
diff --git a/src/calf/gui.h b/src/calf/gui.h
index 06d2860..de74547 100644
--- a/src/calf/gui.h
+++ b/src/calf/gui.h
@@ -313,7 +313,7 @@ struct listview_param_control: public param_control, public send_configure_iface
virtual void get() {}
virtual void set() {}
virtual void send_configure(const char *key, const char *value);
- void update_store(const std::string &data);
+ void update_store();
static void on_edited(GtkCellRenderer *renderer, gchar *path, gchar *new_text, listview_param_control *pThis);
static void on_editing_canceled(GtkCellRenderer *renderer, listview_param_control *pThis);
};
diff --git a/src/calf/jackhost.h b/src/calf/jackhost.h
index 3223eb8..38b7b02 100644
--- a/src/calf/jackhost.h
+++ b/src/calf/jackhost.h
@@ -248,6 +248,9 @@ public:
case 12:
Module::program_change(buffer[1]);
break;
+ case 13:
+ Module::channel_pressure(buffer[1]);
+ break;
case 14:
value = buffer[1] + 128 * buffer[2] - 8192;
Module::pitch_bend(value);
diff --git a/src/calf/metadata.h b/src/calf/metadata.h
index a3ccb32..5b2b22b 100644
--- a/src/calf/metadata.h
+++ b/src/calf/metadata.h
@@ -114,13 +114,11 @@ struct monosynth_metadata: public plugin_metadata<monosynth_metadata>
};
enum {
moddest_none,
- moddest_amplitude,
+ moddest_attenuation,
moddest_cutoff,
moddest_resonance,
moddest_o1detune,
moddest_o2detune,
- moddest_o1pitch,
- moddest_o2pitch,
moddest_o1pw,
moddest_o2pw,
moddest_count,
diff --git a/src/calf/modules_synths.h b/src/calf/modules_synths.h
index f1ec9ee..bb054e4 100644
--- a/src/calf/modules_synths.h
+++ b/src/calf/modules_synths.h
@@ -35,11 +35,19 @@ namespace calf_plugins {
#define MONOSYNTH_WAVE_BITS 12
+struct modulation_entry
+{
+ int src1, src2;
+ float amount;
+ int dest;
+};
+
/// 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 audio_module<monosynth_metadata>, public line_graph_iface, public table_edit_iface
{
public:
+ enum { mod_matrix_slots = 10 };
float *ins[in_count];
float *outs[out_count];
float *params[param_count];
@@ -76,8 +84,18 @@ public:
dsp::adsr envelope;
dsp::keystack stack;
dsp::gain_smoothing master;
+ /// Smoothed cutoff value
dsp::inertia<dsp::exponential_ramp> inertia_cutoff;
+ /// Smoothed pitch bend value
dsp::inertia<dsp::exponential_ramp> inertia_pitchbend;
+ /// Smoothed channel pressure value
+ dsp::inertia<dsp::linear_ramp> inertia_pressure;
+ /// Rows of the modulation matrix
+ modulation_entry mod_matrix[mod_matrix_slots];
+ /// Currently used velocity
+ float velocity;
+ /// Current calculated mod matrix outputs
+ float moddest[moddest_count];
monosynth_audio_module();
static void precalculate_waves(progress_report_iface *reporter);
@@ -88,6 +106,8 @@ public:
void note_on(int note, int vel);
/// Handle MIDI Note Off message
void note_off(int note, int vel);
+ /// Handle MIDI Channel Pressure
+ void channel_pressure(int value);
/// Handle pitch bend message.
inline void pitch_bend(int value)
{
@@ -99,8 +119,13 @@ public:
float detune_scaled = (detune - 1); // * log(freq / 440);
if (*params[par_scaledetune] > 0)
detune_scaled *= pow(20.0 / freq, *params[par_scaledetune]);
- osc1.set_freq(freq * (1 - detune_scaled) * inertia_pitchbend.get_last() * lfo_bend, srate);
- osc2.set_freq(freq * (1 + detune_scaled) * inertia_pitchbend.get_last() * lfo_bend * xpose, srate);
+ float p1 = 1, p2 = 1;
+ if (moddest[moddest_o1detune] != 0)
+ p1 = pow(2.0, moddest[moddest_o1detune] * (1.0 / 1200.0));
+ if (moddest[moddest_o2detune] != 0)
+ p2 = pow(2.0, moddest[moddest_o2detune] * (1.0 / 1200.0));
+ osc1.set_freq(freq * (1 - detune_scaled) * p1 * inertia_pitchbend.get_last() * lfo_bend, srate);
+ osc2.set_freq(freq * (1 + detune_scaled) * p2 * inertia_pitchbend.get_last() * lfo_bend * xpose, srate);
}
/// Handle control change messages.
void control_change(int controller, int value);
@@ -152,48 +177,15 @@ public:
bool is_noisy(int param_no) { return param_no != par_cutoff; }
/// Calculate control signals and produce step_size samples of output.
void calculate_step();
+ /// Process modulation matrix
+ void calculate_modmatrix(float *modsrc);
/// Main processing function
- uint32_t process(uint32_t offset, uint32_t nsamples, uint32_t inputs_mask, uint32_t outputs_mask) {
- if (!running && queue_note_on == -1) {
- for (uint32_t i = 0; i < nsamples / step_size; i++)
- envelope.advance();
- return 0;
- }
- uint32_t op = offset;
- uint32_t op_end = offset + nsamples;
- while(op < op_end) {
- if (output_pos == 0) {
- if (running || queue_note_on != -1)
- calculate_step();
- else {
- envelope.advance();
- 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);
- if (is_stereo_filter())
- for(uint32_t i = 0 ; i < len; i++) {
- float vol = master.get();
- outs[0][op + i] = buffer[ip + i] * vol,
- outs[1][op + i] = buffer2[ip + i] * vol;
- }
- else
- for(uint32_t i = 0 ; i < len; i++)
- outs[0][op + i] = outs[1][op + i] = buffer[ip + i] * master.get();
- op += len;
- output_pos += len;
- if (output_pos == step_size)
- output_pos = 0;
- }
- }
-
- return 3;
- }
+ uint32_t process(uint32_t offset, uint32_t nsamples, uint32_t inputs_mask, uint32_t outputs_mask);
virtual const table_column_info *get_table_columns(int param);
virtual uint32_t get_table_rows(int param);
+ virtual std::string get_cell(int param, int row, int column);
+ virtual void set_cell(int param, int row, int column, const std::string &src, std::string &error);
};
struct organ_audio_module: public audio_module<organ_metadata>, public dsp::drawbar_organ, public line_graph_iface
diff --git a/src/gui.cpp b/src/gui.cpp
index f713e38..81a7869 100644
--- a/src/gui.cpp
+++ b/src/gui.cpp
@@ -710,7 +710,7 @@ GtkWidget *listview_param_control::create(plugin_gui *_gui, int _param_no)
for (int i = 0; i < cols; i++)
p[i] = G_TYPE_STRING;
lstore = gtk_list_store_newv(cols, p);
- update_store("");
+ update_store();
widget = gtk_tree_view_new_with_model(GTK_TREE_MODEL(lstore));
delete []p;
tree = GTK_TREE_VIEW(widget);
@@ -743,7 +743,7 @@ GtkWidget *listview_param_control::create(plugin_gui *_gui, int _param_no)
return widget;
}
-void listview_param_control::update_store(const std::string &data)
+void listview_param_control::update_store()
{
gtk_list_store_clear(lstore);
uint32_t rows = teif->get_table_rows(param_no);
@@ -753,7 +753,7 @@ void listview_param_control::update_store(const std::string &data)
gtk_list_store_insert(lstore, &iter, i);
for (int j = 0; j < cols; j++)
{
- gtk_list_store_set(lstore, &iter, j, teif->get_cell(i, j).c_str(), -1);
+ gtk_list_store_set(lstore, &iter, j, teif->get_cell(param_no, i, j).c_str(), -1);
}
positions.push_back(iter);
}
@@ -763,15 +763,28 @@ void listview_param_control::send_configure(const char *key, const char *value)
{
if (attribs["key"] == key)
{
- update_store(value);
+ update_store();
}
}
void listview_param_control::on_edited(GtkCellRenderer *renderer, gchar *path, gchar *new_text, listview_param_control *pThis)
{
const table_column_info *tci = pThis->teif->get_table_columns(pThis->param_no);
- gtk_list_store_set(pThis->lstore, &pThis->positions[atoi(path)], ((table_column_info *)g_object_get_data(G_OBJECT(renderer), "column")) - tci, new_text, -1);
- gtk_widget_grab_focus(pThis->widget);
+ int column = ((table_column_info *)g_object_get_data(G_OBJECT(renderer), "column")) - tci;
+ string error;
+ pThis->teif->set_cell(pThis->param_no, atoi(path), column, new_text, error);
+ if (error.empty()) {
+ pThis->update_store();
+ gtk_widget_grab_focus(pThis->widget);
+ }
+ else
+ {
+ GtkWidget *dialog = gtk_message_dialog_new(pThis->gui->window->toplevel, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+ "%s", error.c_str());
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+ gtk_widget_grab_focus(pThis->widget);
+ }
}
void listview_param_control::on_editing_canceled(GtkCellRenderer *renderer, listview_param_control *pThis)
diff --git a/src/monosynth.cpp b/src/monosynth.cpp
index 9e66851..e4c858e 100644
--- a/src/monosynth.cpp
+++ b/src/monosynth.cpp
@@ -35,9 +35,17 @@ using namespace std;
float silence[4097];
monosynth_audio_module::monosynth_audio_module()
-: inertia_cutoff(exponential_ramp(1))
-, inertia_pitchbend(exponential_ramp(1))
+: inertia_cutoff(1)
+, inertia_pitchbend(1)
+, inertia_pressure(64)
{
+ for (int i = 0; i < mod_matrix_slots; i++)
+ {
+ mod_matrix[i].src1 = modsrc_none;
+ mod_matrix[i].src2 = modsrc_none;
+ mod_matrix[i].amount = 0.f;
+ mod_matrix[i].dest = moddest_none;
+ }
}
void monosynth_audio_module::activate() {
@@ -50,6 +58,7 @@ void monosynth_audio_module::activate() {
modwheel_value = 0.f;
modwheel_value_int = 0;
inertia_cutoff.set_now(*params[par_cutoff]);
+ inertia_pressure.set_now(0);
filter.reset();
filter2.reset();
stack.clear();
@@ -233,8 +242,8 @@ void monosynth_audio_module::calculate_buffer_oscs(float lfo)
int flag2 = (wave2 == wave_sqr);
int32_t shift1 = last_pwshift1;
int32_t shift2 = last_pwshift2;
- int32_t shift_target1 = (int32_t)(0x78000000 * dsp::clip11(*params[par_pw1] + lfo * *params[par_lfopw]));
- int32_t shift_target2 = (int32_t)(0x78000000 * dsp::clip11(*params[par_pw2] + lfo * *params[par_lfopw]));
+ int32_t shift_target1 = (int32_t)(0x78000000 * dsp::clip11(*params[par_pw1] + lfo * *params[par_lfopw] + moddest[moddest_o1pw]));
+ int32_t shift_target2 = (int32_t)(0x78000000 * dsp::clip11(*params[par_pw2] + lfo * *params[par_lfopw] + moddest[moddest_o2pw]));
int32_t shift_delta1 = ((shift_target1 >> 1) - (last_pwshift1 >> 1)) >> (step_shift - 1);
int32_t shift_delta2 = ((shift_target2 >> 1) - (last_pwshift2 >> 1)) >> (step_shift - 1);
last_lfov = lfo;
@@ -313,6 +322,7 @@ 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);
+ velocity = queue_vel;
ampctl = 1.0 + (queue_vel - 1.0) * *params[par_vel2amp];
fltctl = 1.0 + (queue_vel - 1.0) * *params[par_vel2filter];
set_frequency();
@@ -363,6 +373,8 @@ void monosynth_audio_module::delayed_note_on()
}
envelope.advance();
queue_note_on = -1;
+ float modsrc[modsrc_count] = { 1, velocity, inertia_pressure.get_last(), modwheel_value, 0, last_lfov};
+ calculate_modmatrix(modsrc);
}
void monosynth_audio_module::set_sample_rate(uint32_t sr) {
@@ -376,6 +388,18 @@ void monosynth_audio_module::set_sample_rate(uint32_t sr) {
inertia_pitchbend.ramp.set_length(crate / 30); // 1/30s
}
+void monosynth_audio_module::calculate_modmatrix(float *modsrc)
+{
+ for (int i = 0; i < moddest_count; i++)
+ moddest[i] = 0;
+ for (int i = 0; i < mod_matrix_slots; i++)
+ {
+ modulation_entry &slot = mod_matrix[i];
+ if (slot.dest)
+ moddest[slot.dest] += modsrc[slot.src1] * modsrc[slot.src2] * slot.amount;
+ }
+}
+
void monosynth_audio_module::calculate_step()
{
if (queue_note_on != -1)
@@ -413,15 +437,21 @@ void monosynth_audio_module::calculate_step()
set_frequency();
envelope.advance();
float env = envelope.value;
+
+ // mod matrix
+ // this should be optimized heavily; I think I'll do it when MIDI in Ardour 3 gets stable :>
+ float modsrc[modsrc_count] = { 1, velocity, inertia_pressure.get(), modwheel_value, env, lfov};
+ calculate_modmatrix(modsrc);
+
inertia_cutoff.set_inertia(*params[par_cutoff]);
- cutoff = inertia_cutoff.get() * pow(2.0f, (lfov * *params[par_lfofilter] + env * fltctl * *params[par_envmod]) * (1.f / 1200.f));
+ cutoff = inertia_cutoff.get() * pow(2.0f, (lfov * *params[par_lfofilter] + env * fltctl * *params[par_envmod] + moddest[moddest_cutoff]) * (1.f / 1200.f));
if (*params[par_keyfollow] > 0.01f)
cutoff *= pow(freq / 264.f, *params[par_keyfollow]);
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;
+ resonance = resonance * (1 - e2r) + (0.7 + (resonance - 0.7) * env * env) * e2r + moddest[moddest_resonance];
float cutoff2 = dsp::clip(cutoff * separation, 10.f, 18000.f);
float newfgain = 0.f;
if (filter_type != last_filter_type)
@@ -476,6 +506,8 @@ void monosynth_audio_module::calculate_step()
float aenv = env;
if (*params[par_envtoamp] > 0.f)
newfgain *= 1.0 - (1.0 - aenv) * e2a;
+ if (moddest[moddest_attenuation] != 0.f)
+ newfgain *= dsp::clip<float>(1 - moddest[moddest_attenuation] * moddest[moddest_attenuation], 0.f, 1.f);
fgain_delta = (newfgain - fgain) * (1.0 / step_size);
calculate_buffer_oscs(lfov);
switch(filter_type)
@@ -541,6 +573,10 @@ void monosynth_audio_module::note_off(int note, int vel)
}
}
+void monosynth_audio_module::channel_pressure(int value)
+{
+ inertia_pressure.set_inertia(value * (1.0 / 127.0));
+}
void monosynth_audio_module::control_change(int controller, int value)
{
@@ -575,6 +611,45 @@ void monosynth_audio_module::deactivate()
stack.clear();
}
+uint32_t monosynth_audio_module::process(uint32_t offset, uint32_t nsamples, uint32_t inputs_mask, uint32_t outputs_mask) {
+ if (!running && queue_note_on == -1) {
+ for (uint32_t i = 0; i < nsamples / step_size; i++)
+ envelope.advance();
+ return 0;
+ }
+ uint32_t op = offset;
+ uint32_t op_end = offset + nsamples;
+ while(op < op_end) {
+ if (output_pos == 0) {
+ if (running || queue_note_on != -1)
+ calculate_step();
+ else {
+ envelope.advance();
+ 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);
+ if (is_stereo_filter())
+ for(uint32_t i = 0 ; i < len; i++) {
+ float vol = master.get();
+ outs[0][op + i] = buffer[ip + i] * vol,
+ outs[1][op + i] = buffer2[ip + i] * vol;
+ }
+ else
+ for(uint32_t i = 0 ; i < len; i++)
+ outs[0][op + i] = outs[1][op + i] = buffer[ip + i] * master.get();
+ op += len;
+ output_pos += len;
+ if (output_pos == step_size)
+ output_pos = 0;
+ }
+ }
+
+ return 3;
+}
+
static const char *monosynth_mod_src_names[] = {
"None",
"Velocity",
@@ -587,16 +662,11 @@ static const char *monosynth_mod_src_names[] = {
static const char *monosynth_mod_dest_names[] = {
"None",
- "Amplitude",
+ "Attenuation",
"Cutoff",
"Resonance",
- "OX: Detune",
"O1: Detune",
"O2: Detune",
- "OX: Pitch",
- "O1: Pitch",
- "O2: Pitch",
- "OX: PW",
"O1: PW",
"O2: PW",
NULL
@@ -617,5 +687,73 @@ const table_column_info *monosynth_audio_module::get_table_columns(int param)
uint32_t monosynth_audio_module::get_table_rows(int param)
{
- return 2;
+ return mod_matrix_slots;
}
+
+std::string monosynth_audio_module::get_cell(int param, int row, int column)
+{
+ assert(row >= 0 && row < mod_matrix_slots);
+ modulation_entry &slot = mod_matrix[row];
+ switch(column) {
+ case 0: // source 1
+ return monosynth_mod_src_names[slot.src1];
+ case 1: // source 2
+ return monosynth_mod_src_names[slot.src2];
+ case 2: // amount
+ return calf_utils::f2s(slot.amount);
+ case 3: // destination
+ return monosynth_mod_dest_names[slot.dest];
+ default:
+ assert(0);
+ return "";
+ }
+}
+
+void monosynth_audio_module::set_cell(int param, int row, int column, const std::string &src, std::string &error)
+{
+ assert(row >= 0 && row < mod_matrix_slots);
+ modulation_entry &slot = mod_matrix[row];
+ switch(column) {
+ case 0:
+ case 1:
+ {
+ for (int i = 0; monosynth_mod_src_names[i]; i++)
+ {
+ if (src == monosynth_mod_src_names[i])
+ {
+ if (column == 0)
+ slot.src1 = i;
+ else
+ slot.src2 = i;
+ error.clear();
+ return;
+ }
+ }
+ error = "Invalid source name";
+ return;
+ }
+ case 2:
+ {
+ stringstream ss(src);
+ ss >> slot.amount;
+ error.clear();
+ return;
+ }
+ case 3:
+ {
+ for (int i = 0; monosynth_mod_dest_names[i]; i++)
+ {
+ if (src == monosynth_mod_dest_names[i])
+ {
+ slot.dest = i;
+ error.clear();
+ return;
+ }
+ }
+ error = "Invalid destination name";
+ return;
+ }
+
+ }
+}
+
--
calf audio plugins packaging
More information about the pkg-multimedia-commits
mailing list