[SCM] calf/master: + Curve: add maximum number of points, pass CalfCurve object to callbacks, allow customized clipping + Framework: some API docs, more of incomplete handling of DSSI-like configure() (JACK host only, no support in presets etc., be patient) + Organ: more work on FM-based percussion generator, including user-settable keytrack curve (editing currently limited to white keys) + SVN: mark more files as ignored (install-sh, depcomp, missing)
js at users.alioth.debian.org
js at users.alioth.debian.org
Tue May 7 15:37:27 UTC 2013
The following commit has been merged in the master branch:
commit e044c37a4700b3e31bf56fd953b547ed3f4a8d13
Author: kfoltman <kfoltman at 78b06b96-2940-0410-b7fc-879d825d01d8>
Date: Fri Aug 1 23:05:42 2008 +0000
+ Curve: add maximum number of points, pass CalfCurve object to callbacks, allow customized clipping
+ Framework: some API docs, more of incomplete handling of DSSI-like configure() (JACK host only, no support in presets etc., be patient)
+ Organ: more work on FM-based percussion generator, including user-settable keytrack curve (editing currently limited to white keys)
+ SVN: mark more files as ignored (install-sh, depcomp, missing)
git-svn-id: https://calf.svn.sourceforge.net/svnroot/calf/trunk@248 78b06b96-2940-0410-b7fc-879d825d01d8
diff --git a/src/calf/ctl_curve.h b/src/calf/ctl_curve.h
index 978d013..61c0fb6 100644
--- a/src/calf/ctl_curve.h
+++ b/src/calf/ctl_curve.h
@@ -47,19 +47,22 @@ struct CalfCurve
struct EventSink
{
/// Called when a point has been edited, added or removed
- virtual void curve_changed(const point_vector &data) = 0;
+ virtual void curve_changed(CalfCurve *src, const point_vector &data) = 0;
+ /// Called to clip/snap/otherwise adjust candidate point coordinates
+ virtual void clip(CalfCurve *src, int pt, float &x, float &y, bool &hide) = 0;
};
/// Null implementation of EventSink
struct EventAdapter: public EventSink
{
- virtual void curve_changed(const point_vector &data) {}
+ virtual void curve_changed(CalfCurve *src, const point_vector &data) {}
+ virtual void clip(CalfCurve *src, int pt, float &x, float &y, bool &hide) {}
};
/// Debug implementation of EventSink
struct EventTester: public EventAdapter
{
- virtual void curve_changed(const point_vector &data) {
+ virtual void curve_changed(CalfCurve *src, const point_vector &data) {
for(size_t i = 0; i < data.size(); i++)
g_message("Point %d: (%f, %f)", (int)i, data[i].first, data[i].second);
}
@@ -81,6 +84,10 @@ struct CalfCurve
GdkCursor *hand_cursor;
/// Cached pencil (add point) cursor
GdkCursor *pencil_cursor;
+ /// Cached arrow (do not add point) cursor
+ GdkCursor *arrow_cursor;
+ /// Maximum number of points
+ unsigned int point_limit;
/// Convert logical (mapping) to physical (screen) coordinates
void log2phys(float &x, float &y);
@@ -100,7 +107,7 @@ struct CalfCurveClass
};
/// Create a CalfCurve
-extern GtkWidget *calf_curve_new();
+extern GtkWidget *calf_curve_new(unsigned int point_limit = -1);
/// Return a GObject type for class CalfCurve
extern GType calf_curve_get_type();
diff --git a/src/calf/giface.h b/src/calf/giface.h
index f06a369..a6c22ae 100644
--- a/src/calf/giface.h
+++ b/src/calf/giface.h
@@ -108,45 +108,96 @@ struct plugin_command_info
struct parameter_properties
{
float def_value, min, max, step;
+ /// OR of parameter_flags
uint32_t flags;
+ /// for PF_ENUM: array of text values (from min to max step 1)
const char **choices;
- const char *short_name, *name;
+ /// parameter label (for use in LV2 label field etc.)
+ const char *short_name;
+ /// parameter human-readable name
+ const char *name;
+ /// convert from [0, 1] range to [min, max] (applying scaling)
float from_01(double value01) const;
+ /// convert from [min, max] to [0, 1] range (applying reverse scaling)
double to_01(float value) const;
+ /// stringify (in sensible way)
std::string to_string(float value) const;
+ /// get required width (for reserving GUI space)
int get_char_count() const;
+ /// get increment step based on step value (if specified) and other factors
float get_increment() const;
};
+/// 'provides live line graph values' interface
struct line_graph_iface
{
+ /// Obtain subindex'th graph of parameter index
+ /// @param index parameter/graph number (usually tied to particular plugin control port)
+ /// @param subindex graph number (there may be multiple overlaid graphs for one parameter, eg. for monosynth 2x12dB filters)
+ /// @param data buffer for normalized output values
+ /// @param points number of points to fill
+ /// @param context cairo context to adjust (for multicolour graphs etc.)
+ /// @retval true graph data was returned; subindex+1 graph may or may not be available
+ /// @retval false graph data was not returned; subindex+1 graph does not exist either
virtual bool get_graph(int index, int subindex, float *data, int points, cairo_t *context) = 0;
+ /// Standard destructor to make compiler happy
virtual ~line_graph_iface() {}
};
+/// 'may receive configure variables' interface
+struct send_configure_iface
+{
+ /// Called to set configure variable
+ /// @param key variable name
+ /// @param value variable content
+ virtual void send_configure(const char *key, const char *value) = 0;
+};
+
struct plugin_command_info;
+/// Interface for host-GUI-plugin interaction (should be really split in two, but ... meh)
struct plugin_ctl_iface
{
+ /// @return description structure for given parameter
virtual parameter_properties *get_param_props(int param_no) = 0;
+ /// @return total number of parameters
+ virtual int get_param_count() = 0;
+ /// @return value of given parameter
virtual float get_param_value(int param_no) = 0;
+ /// Set value of given parameter
virtual void set_param_value(int param_no, float value) = 0;
- virtual int get_param_count() = 0;
+ /// Return custom XML
virtual const char *get_gui_xml() = 0;
+ /// @return line_graph_iface if any
virtual line_graph_iface *get_line_graph_iface() = 0;
+ /// @return port offset of first control (parameter) port (= number of audio inputs + number of audio outputs in all existing plugins as for 1 Aug 2008)
virtual int get_param_port_offset() = 0;
+ /// Load preset with given number
virtual bool activate_preset(int bank, int program) = 0;
+ /// @return plugin long name
virtual const char *get_name() = 0;
+ /// @return plugin LV2 label
virtual const char *get_id() = 0;
+ /// @return plugin human-readable label
virtual const char *get_label() = 0;
+ /// @return number of audio inputs
virtual int get_input_count()=0;
+ /// @return number of audio outputs
virtual int get_output_count()=0;
+ /// @return true if plugin has MIDI input
virtual bool get_midi()=0;
+ /// @return volume level for port'th port (if supported by the implementation, currently only jack_host<Module> implements that by measuring signal level on plugin ports)
virtual float get_level(unsigned int port)=0;
- virtual ~plugin_ctl_iface() {}
+ /// @return NULL-terminated list of menu commands
virtual plugin_command_info *get_commands() { return NULL; }
- virtual char *configure(const char *key, const char *value) { return NULL; }
+ /// Execute menu command with given number
virtual void execute(int cmd_no)=0;
+ /// Set a configure variable on a plugin
+ virtual char *configure(const char *key, const char *value) { return NULL; }
+ /// Send all configure variables set within a plugin to given destination (which may be limited to only those that plugin understands)
+ virtual void send_configures(send_configure_iface *) {}
+ /// Do-nothing destructor to silence compiler warning
+ virtual ~plugin_ctl_iface() {}
};
struct midi_event {
diff --git a/src/calf/gui.h b/src/calf/gui.h
index a08eb94..faedde2 100644
--- a/src/calf/gui.h
+++ b/src/calf/gui.h
@@ -71,6 +71,8 @@ struct param_control: public control_base
virtual void get()=0;
/// called to transfer the value from parameter(s) to control
virtual void set()=0;
+ /// called on DSSI configure()
+ virtual void configure(const char *key, const char *value) {}
virtual void hook_params();
virtual void on_idle() {}
virtual ~param_control();
@@ -236,7 +238,7 @@ struct curve_param_control: public param_control
class plugin_gui_window;
-class plugin_gui
+class plugin_gui: public send_configure_iface
{
protected:
int param_count;
@@ -265,6 +267,7 @@ public:
void refresh(int param_no, param_control *originator = NULL);
void xml_element_start(const char *element, const char *attributes[]);
void set_param_value(int param_no, float value, param_control *originator = NULL);
+ void send_configure(const char *key, const char *value);
void on_idle();
~plugin_gui();
static void xml_element_start(void *data, const char *element, const char *attributes[]);
diff --git a/src/calf/jackhost.h b/src/calf/jackhost.h
index 4859212..922171e 100644
--- a/src/calf/jackhost.h
+++ b/src/calf/jackhost.h
@@ -414,6 +414,9 @@ public:
virtual void execute(int cmd_no) {
module.execute(cmd_no);
}
+ virtual char *configure(const char *key, const char *value) {
+ return module.configure(key, value);
+ }
};
extern jack_host_base *create_jack_host(const char *name);
diff --git a/src/calf/modules_synths.h b/src/calf/modules_synths.h
index f6e996c..7c04af8 100644
--- a/src/calf/modules_synths.h
+++ b/src/calf/modules_synths.h
@@ -273,6 +273,7 @@ public:
static const char *get_id() { return "organ"; }
static const char *get_label() { return "Organ"; }
static plugin_command_info *get_commands();
+ virtual char *configure(const char *key, const char *value);
};
};
diff --git a/src/calf/organ.h b/src/calf/organ.h
index f339113..e50d1c1 100644
--- a/src/calf/organ.h
+++ b/src/calf/organ.h
@@ -24,6 +24,8 @@
#include "synth.h"
+#define ORGAN_KEYTRACK_POINTS 4
+
namespace synth
{
@@ -54,14 +56,15 @@ struct organ_parameters {
float percussion_level;
float percussion_wave;
float percussion_harmonic;
+ float percussion_fm_time;
+ float percussion_fm_depth;
float percussion_fm_wave;
float percussion_fm_harmonic;
- float percussion_fm_depth;
float percussion_trigger;
float percussion_vel2amp;
float filter_chain;
float master;
-
+
organ_filter_parameters filters[organ_parameters::FilterCount];
organ_env_parameters envs[organ_parameters::EnvCount];
float lfo_rate;
@@ -70,12 +73,15 @@ struct organ_parameters {
float lfo_phase;
float lfo_mode;
- double perc_decay_const;
+ double perc_decay_const, perc_fm_decay_const;
float multiplier[9];
int phaseshift[9];
float cutoff;
unsigned int foldvalue;
float pitch_bend;
+
+ float percussion_keytrack[ORGAN_KEYTRACK_POINTS][2];
+
organ_parameters() : pitch_bend(1.0f) {}
@@ -244,7 +250,8 @@ class percussion_voice: public organ_voice_base {
public:
int sample_rate;
dsp::fixed_point<int64_t, 20> phase, modphase, dphase, moddphase;
- dsp::biquad<float> filter;
+ dsp::decay fm_amp;
+ float fm_keytrack;
percussion_voice(organ_parameters *_parameters)
: organ_voice_base(_parameters)
@@ -254,27 +261,18 @@ public:
void reset() {
phase = 0;
modphase = 0;
+ dphase = 0;
+ moddphase = 0;
note = -1;
}
- void note_on(int note, int vel) {
- // do not reset phase if voice is still running (to prevent clicks, even at cost of slight loss of "percussiveness")
- if (!amp.get_active())
- {
- phase = 0;
- modphase = 0;
- filter.reset_d1();
- }
- this->note = note;
- amp.set(1.0f + (vel - 127) * parameters->percussion_vel2amp / 127.0);
- update_pitch();
- }
+ void note_on(int note, int vel);
void update_pitch()
{
float phase = synth::midi_note_to_phase(note, 0, sample_rate);
- dphase.set(phase * parameters->percussion_harmonic * parameters->pitch_bend);
- moddphase.set(phase * parameters->percussion_fm_harmonic * parameters->pitch_bend);
+ dphase.set(phase * parameters->percussion_harmonic * 0.5 * parameters->pitch_bend);
+ moddphase.set(phase * parameters->percussion_fm_harmonic * 0.5 * parameters->pitch_bend);
}
// this doesn't really have a voice interface
@@ -304,7 +302,9 @@ struct drawbar_organ: public synth::basic_synth {
par_pan1, par_pan2, par_pan3, par_pan4, par_pan5, par_pan6, par_pan7, par_pan8, par_pan9,
par_routing1, par_routing2, par_routing3, par_routing4, par_routing5, par_routing6, par_routing7, par_routing8, par_routing9,
par_foldover,
- par_percdecay, par_perclevel, par_percwave, par_percharm, par_percfmwave, par_percfmharm, par_percfmdepth, par_perctrigger, par_percvel2amp,
+ par_percdecay, par_perclevel, par_percwave, par_percharm,
+ par_percfmdecay, par_percfmdepth, par_percfmwave, par_percfmharm,
+ par_perctrigger, par_percvel2amp,
par_filterchain,
par_master,
par_f1cutoff, par_f1res, par_f1env1, par_f1env2, par_f1env3, par_f1keyf,
diff --git a/src/ctl_curve.cpp b/src/ctl_curve.cpp
index 5c554b3..6a4ebdc 100644
--- a/src/ctl_curve.cpp
+++ b/src/ctl_curve.cpp
@@ -27,9 +27,13 @@
static gpointer parent_class = NULL;
GtkWidget *
-calf_curve_new()
+calf_curve_new(unsigned int point_limit)
{
GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_CURVE, NULL ));
+ g_assert(CALF_IS_CURVE(widget));
+
+ CalfCurve *self = CALF_CURVE(widget);
+ self->point_limit = point_limit;
return widget;
}
@@ -152,6 +156,9 @@ calf_curve_button_press (GtkWidget *widget, GdkEventButton *event)
found_pt = find_nearest(self, event->x, event->y, insert_pt);
if (found_pt == -1 && insert_pt != -1)
{
+ // if at point limit, do not start anything
+ if (self->points->size() >= self->point_limit)
+ return TRUE;
float x = event->x, y = event->y;
bool hide = false;
self->phys2log(x, y);
@@ -170,7 +177,7 @@ calf_curve_button_press (GtkWidget *widget, GdkEventButton *event)
self->cur_pt = found_pt;
gtk_widget_queue_draw(widget);
if (self->sink)
- self->sink->curve_changed(*self->points);
+ self->sink->curve_changed(self, *self->points);
gdk_window_set_cursor(widget->window, self->hand_cursor);
return TRUE;
}
@@ -185,7 +192,7 @@ calf_curve_button_release (GtkWidget *widget, GdkEventButton *event)
self->cur_pt = -1;
self->hide_current = false;
if (self->sink)
- self->sink->curve_changed(*self->points);
+ self->sink->curve_changed(self, *self->points);
gtk_widget_queue_draw(widget);
gdk_window_set_cursor(widget->window, self->pencil_cursor);
return FALSE;
@@ -205,14 +212,14 @@ calf_curve_pointer_motion (GtkWidget *widget, GdkEventMotion *event)
self->clip(self->cur_pt, x, y, self->hide_current);
(*self->points)[self->cur_pt] = CalfCurve::point(x, y);
if (self->sink)
- self->sink->curve_changed(*self->points);
+ self->sink->curve_changed(self, *self->points);
gtk_widget_queue_draw(widget);
}
else
{
int insert_pt = -1;
if (find_nearest(self, event->x, event->y, insert_pt) == -1)
- gdk_window_set_cursor(widget->window, self->pencil_cursor);
+ gdk_window_set_cursor(widget->window, self->points->size() >= self->point_limit ? self->arrow_cursor : self->pencil_cursor);
else
gdk_window_set_cursor(widget->window, self->hand_cursor);
}
@@ -267,6 +274,7 @@ calf_curve_init (CalfCurve *self)
self->hide_current = false;
self->pencil_cursor = gdk_cursor_new(GDK_PENCIL);
self->hand_cursor = gdk_cursor_new(GDK_FLEUR);
+ self->arrow_cursor = gdk_cursor_new(GDK_ARROW);
}
void CalfCurve::log2phys(float &x, float &y) {
@@ -281,9 +289,11 @@ void CalfCurve::phys2log(float &x, float &y) {
void CalfCurve::clip(int pt, float &x, float &y, bool &hide)
{
+ hide = false;
+ sink->clip(this, pt, x, y, hide);
+
float ymin = std::min(y0, y1), ymax = std::max(y0, y1);
float yamp = ymax - ymin;
- hide = false;
if (pt != 0 && pt != (int)(points->size() - 1))
{
if (y < ymin - yamp || y > ymax + yamp)
diff --git a/src/gui.cpp b/src/gui.cpp
index ada4173..5f5dfe1 100644
--- a/src/gui.cpp
+++ b/src/gui.cpp
@@ -28,6 +28,8 @@
#include <calf/preset_gui.h>
#include <calf/main_win.h>
+#include <iostream>
+
using namespace synth;
using namespace std;
@@ -352,14 +354,37 @@ GtkWidget *keyboard_param_control::create(plugin_gui *_gui, int _param_no)
// curve
+struct curve_param_control_callback: public CalfCurve::EventAdapter
+{
+ curve_param_control *ctl;
+
+ curve_param_control_callback(curve_param_control *_ctl)
+ : ctl(_ctl) {}
+
+ virtual void curve_changed(CalfCurve *src, const CalfCurve::point_vector &data) {
+ stringstream ss;
+ ss << data.size() << endl;
+ for (size_t i = 0; i < data.size(); i++)
+ ss << data[i].first << " " << data[i].second << endl;
+ ctl->gui->send_configure(ctl->attribs["key"].c_str(), ss.str().c_str());
+ }
+ virtual void clip(CalfCurve *src, int pt, float &x, float &y, bool &hide)
+ {
+ // int gridpt = floor(x * 71 * 2);
+ // clip to the middle of the nearest white key
+ x = (floor(x * 71) + 0.5)/ 71.0;
+ }
+};
+
GtkWidget *curve_param_control::create(plugin_gui *_gui, int _param_no)
{
gui = _gui;
param_no = _param_no;
+ require_attribute("key");
- widget = calf_curve_new();
+ widget = calf_curve_new(get_int("maxpoints", -1));
curve = CALF_CURVE(widget);
- curve->sink = new CalfCurve::EventTester;
+ curve->sink = new curve_param_control_callback(this);
// gtk_curve_set_curve_type(curve, GTK_CURVE_TYPE_LINEAR);
return widget;
}
@@ -811,6 +836,11 @@ GtkWidget *plugin_gui::create_from_xml(plugin_ctl_iface *_plugin, const char *xm
return GTK_WIDGET(top_container->container);
}
+void plugin_gui::send_configure(const char *key, const char *value)
+{
+ plugin->configure(key, value);
+}
+
void plugin_gui::on_idle()
{
for (unsigned int i = 0; i < params.size(); i++)
diff --git a/src/organ.cpp b/src/organ.cpp
index 6aadacc..9435288 100644
--- a/src/organ.cpp
+++ b/src/organ.cpp
@@ -29,6 +29,7 @@
#include <calf/giface.h>
#include <calf/modules.h>
#include <calf/modules_dev.h>
+#include <iostream>
using namespace synth;
using namespace std;
@@ -274,52 +275,68 @@ const char *organ_audio_module::get_gui_xml()
"</frame>"
"</hbox>"
"<vbox page=\"Advanced\">"
- "<align scale-x=\"0.8\" scale-y=\"0.3\">"
+ "<align scale-x=\"1.0\" scale-y=\"0.3\">"
"<frame label=\"Percussive section\">"
- "<hbox>"
- "<vbox>"
- "<label param=\"perc_decay\"/>"
- "<knob param=\"perc_decay\" expand=\"0\" fill=\"0\"/>"
- "<value param=\"perc_decay\"/>"
- "</vbox>"
- "<vbox>"
- "<label param=\"perc_level\"/>"
- "<knob param=\"perc_level\" expand=\"0\" fill=\"0\"/>"
- "<value param=\"perc_level\"/>"
- "</vbox>"
- "<vbox>"
- "<label param=\"perc_vel2amp\"/>"
- "<knob param=\"perc_vel2amp\" expand=\"0\" fill=\"0\"/>"
- "<value param=\"perc_vel2amp\"/>"
- "</vbox>"
- "<vbox>"
- "<label param=\"perc_waveform\"/>"
- "<combo param=\"perc_waveform\"/>"
- "</vbox>"
- "<vbox>"
- "<label param=\"perc_harmonic\"/>"
- "<knob param=\"perc_harmonic\" expand=\"0\" fill=\"0\"/>"
- "<value param=\"perc_harmonic\"/>"
- "</vbox>"
- "<vbox>"
- "<label param=\"perc_fm_waveform\"/>"
- "<combo param=\"perc_fm_waveform\"/>"
- "</vbox>"
- "<vbox>"
- "<label param=\"perc_fm_harmonic\"/>"
- "<knob param=\"perc_fm_harmonic\" expand=\"0\" fill=\"0\"/>"
- "<value param=\"perc_fm_harmonic\"/>"
- "</vbox>"
- "<vbox>"
- "<label param=\"perc_fm_depth\"/>"
- "<knob param=\"perc_fm_depth\" expand=\"0\" fill=\"0\"/>"
- "<value param=\"perc_fm_depth\"/>"
- "</vbox>"
- "<vbox>"
- "<label param=\"perc_trigger\"/>"
- "<combo param=\"perc_trigger\"/>"
- "</vbox>"
- "</hbox>"
+ "<vbox>"
+ "<align scale-x=\"0.0\" scale-y=\"1.0\"><vbox><keyboard octaves=\"10\"/><curve key=\"map_curve\" maxpoints=\"4\"/></vbox></align>"
+ "<hbox>"
+ "<table rows=\"2\" cols=\"4\">"
+ "<vbox attach-x=\"0\" attach-y=\"0\">"
+ "<label param=\"perc_waveform\"/>"
+ "<combo param=\"perc_waveform\"/>"
+ "</vbox>"
+ "<vbox attach-x=\"1\" attach-y=\"0\">"
+ "<label param=\"perc_harmonic\"/>"
+ "<knob param=\"perc_harmonic\" expand=\"0\" fill=\"0\"/>"
+ "<value param=\"perc_harmonic\"/>"
+ "</vbox>"
+ "<vbox attach-x=\"2\" attach-y=\"0\">"
+ "<label param=\"perc_level\"/>"
+ "<knob param=\"perc_level\" expand=\"0\" fill=\"0\"/>"
+ "<value param=\"perc_level\"/>"
+ "</vbox>"
+ "<vbox attach-x=\"3\" attach-y=\"0\">"
+ "<label param=\"perc_decay\"/>"
+ "<knob param=\"perc_decay\" expand=\"0\" fill=\"0\"/>"
+ "<value param=\"perc_decay\"/>"
+ "</vbox>"
+ "<vbox attach-x=\"0\" attach-y=\"1\">"
+ "<label param=\"perc_fm_waveform\"/>"
+ "<combo param=\"perc_fm_waveform\"/>"
+ "</vbox>"
+ "<vbox attach-x=\"1\" attach-y=\"1\">"
+ "<label param=\"perc_fm_harmonic\"/>"
+ "<knob param=\"perc_fm_harmonic\" expand=\"0\" fill=\"0\"/>"
+ "<value param=\"perc_fm_harmonic\"/>"
+ "</vbox>"
+ "<vbox attach-x=\"2\" attach-y=\"1\">"
+ "<label param=\"perc_fm_depth\"/>"
+ "<knob param=\"perc_fm_depth\" expand=\"0\" fill=\"0\"/>"
+ "<value param=\"perc_fm_depth\"/>"
+ "</vbox>"
+ "<vbox attach-x=\"3\" attach-y=\"1\">"
+ "<label param=\"perc_fm_decay\"/>"
+ "<knob param=\"perc_fm_decay\" expand=\"0\" fill=\"0\"/>"
+ "<value param=\"perc_fm_decay\"/>"
+ "</vbox>"
+ "</table>"
+ "<vbox>"
+ "<hbox>"
+ "<vbox>"
+ "<label param=\"perc_trigger\"/>"
+ "<combo param=\"perc_trigger\"/>"
+ "</vbox>"
+ "</hbox>"
+ "<hbox>"
+ "<vbox>"
+ "<label param=\"perc_vel2amp\"/>"
+ "<knob param=\"perc_vel2amp\" expand=\"0\" fill=\"0\"/>"
+ "<value param=\"perc_vel2amp\"/>"
+ "</vbox>"
+ "</hbox>"
+ "</vbox>"
+ "</hbox>"
+ "</vbox>"
"</frame>"
"</align>"
"<align scale-x=\"0.8\" scale-y=\"0.3\">"
@@ -472,13 +489,17 @@ parameter_properties organ_audio_module::param_props[] = {
{ 0, 0, 2, 0, PF_ENUM | PF_SCALE_LINEAR | PF_CTL_COMBO, organ_routing_names, "routing9", "Routing 9" },
{ 96, 0, 127, 128, PF_INT | PF_CTL_KNOB | PF_UNIT_NOTE, NULL, "foldnote", "Foldover" },
- { 200, 10, 3000, 100, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "perc_decay", "P: Decay time" },
+
+ { 200, 10, 3000, 100, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "perc_decay", "P: Carrier Decay" },
{ 0.25, 0, 1, 100, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB, NULL, "perc_level", "P: Level" },
- { 2, 0, organ_voice_base::wave_count_small - 1, 1, PF_ENUM | PF_CTL_COMBO, organ_wave_names, "perc_waveform", "P: Waveform" },
- { 16, 1, 32, 32, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB, NULL, "perc_harmonic", "P: Harmonic" },
- { 2, 0, organ_voice_base::wave_count_small - 1, 1, PF_ENUM | PF_CTL_COMBO, organ_wave_names, "perc_fm_waveform", "P: FM Waveform" },
- { 16, 1, 32, 32, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB, NULL, "perc_fm_harmonic", "P: FM Freq" },
- { 0, 0, 1, 0, PF_FLOAT | PF_SCALE_PERC, NULL, "perc_fm_depth", "P: FM Depth" },
+ { 2, 0, organ_voice_base::wave_count_small - 1, 1, PF_ENUM | PF_CTL_COMBO, organ_wave_names, "perc_waveform", "P: Carrier Wave" },
+ { 2, 1, 32, 32, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB, NULL, "perc_harmonic", "P: Carrier Frq" },
+
+ { 200, 10, 3000, 100, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "perc_fm_decay", "P: Modulator Decay" },
+ { 0, 0, 4, 0, PF_FLOAT | PF_SCALE_PERC, NULL, "perc_fm_depth", "P: FM Depth" },
+ { 2, 0, organ_voice_base::wave_count_small - 1, 1, PF_ENUM | PF_CTL_COMBO, organ_wave_names, "perc_fm_waveform", "P: Modulator Wave" },
+ { 2, 1, 32, 32, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB, NULL, "perc_fm_harmonic", "P: Modulator Frq" },
+
{ 0, 0, organ_voice_base::perctrig_count - 1, 0, PF_ENUM | PF_CTL_COMBO, organ_percussion_trigger_names, "perc_trigger", "P: Trigger" },
{ 0, 0, 1, 0, PF_FLOAT | PF_SCALE_PERC, NULL, "perc_vel2amp", "P: Vel->Amp" },
@@ -1088,6 +1109,7 @@ void organ_voice::render_block() {
void drawbar_organ::update_params()
{
parameters->perc_decay_const = dsp::decay::calc_exp_constant(1.0 / 1024.0, 0.001 * parameters->percussion_time * sample_rate);
+ parameters->perc_fm_decay_const = dsp::decay::calc_exp_constant(1.0 / 1024.0, 0.001 * parameters->percussion_fm_time * sample_rate);
for (int i = 0; i < 9; i++)
{
parameters->multiplier[i] = parameters->harmonics[i] * pow(2.0, parameters->detune[i] * (1.0 / 1200.0));
@@ -1118,7 +1140,35 @@ void organ_audio_module::execute(int cmd_no)
}
}
-void percussion_voice::render_to(float (*buf)[2], int nsamples) {
+void percussion_voice::note_on(int note, int vel)
+{
+ // do not reset phase if voice is still running (to prevent clicks, even at cost of slight loss of "percussiveness")
+ if (!amp.get_active())
+ {
+ phase = 0;
+ modphase = 0;
+ }
+ this->note = note;
+ amp.set(1.0f + (vel - 127) * parameters->percussion_vel2amp / 127.0);
+ update_pitch();
+ float (*kt)[2] = parameters->percussion_keytrack;
+ // assume last point (will be put there by padding)
+ fm_keytrack = kt[ORGAN_KEYTRACK_POINTS - 1][1];
+ // yes binary search would be nice if we had more than those crappy 4 points
+ for (int i = 0; i < ORGAN_KEYTRACK_POINTS - 1; i++)
+ {
+ float &lower = kt[i][0], upper = kt[i + 1][0];
+ if (note >= lower && note < upper)
+ {
+ fm_keytrack = kt[i][1] + (note - lower) * (kt[i + 1][1] - kt[i][1]) / (upper - lower);
+ break;
+ }
+ }
+ fm_amp.set(fm_keytrack * (1.0f + (vel - 127) * parameters->percussion_vel2amp / 127.0));
+}
+
+void percussion_voice::render_to(float (*buf)[2], int nsamples)
+{
if (note == -1)
return;
@@ -1130,6 +1180,7 @@ void percussion_voice::render_to(float (*buf)[2], int nsamples) {
static float zeros[ORGAN_WAVE_SIZE];
// XXXKF the decay needs work!
double age_const = parameters->perc_decay_const;
+ double fm_age_const = parameters->perc_fm_decay_const;
int timbre = parameters->get_percussion_wave();
if (timbre < 0 || timbre >= wave_count_small)
return;
@@ -1146,18 +1197,49 @@ void percussion_voice::render_to(float (*buf)[2], int nsamples) {
}
for (int i = 0; i < nsamples; i++) {
float fm = wave(fmdata, modphase);
- fm *= ORGAN_WAVE_SIZE * parameters->percussion_fm_depth * amp.get();
+ fm *= ORGAN_WAVE_SIZE * parameters->percussion_fm_depth * fm_amp.get();
modphase += moddphase;
+ fm_amp.age_exp(fm_age_const, 1.0 / 32768.0);
- float osc = level * wave(data, phase);
+ float osc = level * wave(data, phase + dsp::fixed_point<int64_t, 20>(fm));
osc *= level * amp.get();
buf[i][0] += osc;
buf[i][1] += osc;
amp.age_exp(age_const, 1.0 / 32768.0);
phase += dphase;
- phase += dsp::fixed_point<int64_t, 20>(fm);
}
}
+
+char *organ_audio_module::configure(const char *key, const char *value)
+{
+ if (!strcmp(key, "map_curve"))
+ {
+ stringstream ss(value);
+ int points;
+ ss >> points;
+ int i;
+ float x = 0, y = 0;
+ for (i = 0; i < points; i++)
+ {
+ static const int whites[] = { 0, 2, 4, 5, 7, 9, 11 };
+ ss >> x >> y;
+ int wkey = (int)(x * 71);
+ x = whites[wkey % 7] + 12 * (wkey / 7);
+ parameters->percussion_keytrack[i][0] = x;
+ parameters->percussion_keytrack[i][1] = y;
+ // cout << "(" << x << ", " << y << ")" << endl;
+ }
+ // pad with constant Y
+ for (; i < ORGAN_KEYTRACK_POINTS; i++) {
+ parameters->percussion_keytrack[i][0] = x;
+ parameters->percussion_keytrack[i][1] = y;
+ }
+ return NULL;
+ }
+ cout << "Set configure value " << key << " to " << value;
+ return NULL;
+}
+
plugin_command_info *organ_audio_module::get_commands()
{
static plugin_command_info cmds[] = {
--
calf audio plugins packaging
More information about the pkg-multimedia-commits
mailing list