summaryrefslogtreecommitdiffstats
path: root/flow/gsl/gsloscillator.c
diff options
context:
space:
mode:
Diffstat (limited to 'flow/gsl/gsloscillator.c')
-rw-r--r--flow/gsl/gsloscillator.c219
1 files changed, 219 insertions, 0 deletions
diff --git a/flow/gsl/gsloscillator.c b/flow/gsl/gsloscillator.c
new file mode 100644
index 0000000..0747cff
--- /dev/null
+++ b/flow/gsl/gsloscillator.c
@@ -0,0 +1,219 @@
+/* GSL - Generic Sound Layer
+ * Copyright (C) 1999, 2000-2002 Tim Janik
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#include "gsloscillator.h"
+
+#include "gslsignal.h"
+
+#define SIGNAL_LEVEL_INVAL (-2.0) /* trigger level-changed checks */
+
+
+/* --- functions --- */
+static inline void
+osc_update_pwm_offset (GslOscData *osc,
+ gfloat pulse_mod) /* -1..+1 */
+{
+ guint32 maxp_offs, minp_offs, mpos, tpos;
+ gfloat min, max, foffset;
+
+ /* figure actual pulse width (0..1) */
+ foffset = osc->config.pulse_width; /* 0..1 */
+ foffset += pulse_mod * osc->config.pulse_mod_strength;
+ foffset = CLAMP (foffset, 0.0, 1.0);
+
+ /* calculate pulse scaling range for this offset */
+ osc->pwm_offset = foffset * osc->wave.n_values;
+ osc->pwm_offset <<= osc->wave.n_frac_bits;
+
+ maxp_offs = (osc->wave.min_pos + osc->wave.n_values + osc->wave.max_pos) << (osc->wave.n_frac_bits - 1);
+ minp_offs = (osc->wave.max_pos + osc->wave.min_pos) << (osc->wave.n_frac_bits - 1);
+
+ mpos = maxp_offs + (osc->pwm_offset >> 1);
+ tpos = mpos >> osc->wave.n_frac_bits;
+ max = osc->wave.values[tpos];
+ mpos -= osc->pwm_offset;
+ tpos = mpos >> osc->wave.n_frac_bits;
+ max -= osc->wave.values[tpos];
+ mpos = minp_offs + (osc->pwm_offset >> 1);
+ tpos = mpos >> osc->wave.n_frac_bits;
+ min = osc->wave.values[tpos];
+ mpos -= osc->pwm_offset;
+ tpos = mpos >> osc->wave.n_frac_bits;
+ min -= osc->wave.values[tpos];
+ osc->pwm_center = (min + max) / -2.0;
+ min = fabs (min + osc->pwm_center);
+ max = fabs (max + osc->pwm_center);
+ max = MAX (max, min);
+ if_reject (max < GSL_FLOAT_MIN_NORMAL)
+ {
+ osc->pwm_center = foffset < 0.5 ? -1.0 : +1.0;
+ osc->pwm_max = 1.0;
+ }
+ else
+ osc->pwm_max = 1.0 / max;
+}
+
+
+/* --- generate processing function variants --- */
+#define OSC_FLAG_INVAL (0xffffffff)
+#define OSC_FLAG_ISYNC (1)
+#define OSC_FLAG_OSYNC (2)
+#define OSC_FLAG_FREQ (4)
+#define OSC_FLAG_SELF_MOD (8)
+#define OSC_FLAG_LINEAR_MOD (16)
+#define OSC_FLAG_EXP_MOD (32)
+#define OSC_FLAG_PWM_MOD (64)
+#define OSC_FLAG_PULSE_OSC (128)
+
+/* normal oscillator variants */
+#define OSC_INCLUDER_FLAGS 0
+#define GSL_INCLUDER_FIRST_CASE 0
+#define GSL_INCLUDER_LAST_CASE 63
+#define GSL_INCLUDER_NAME oscillator_process_normal
+#define GSL_INCLUDER_TABLE static void (*osc_process_table[]) (GslOscData*,guint, \
+ const gfloat*,const gfloat*,const gfloat*, \
+ const gfloat*,gfloat*,gfloat*)
+#define GSL_INCLUDER_FILE "gsloscillator-aux.c"
+#include "gslincluder.c"
+#undef OSC_INCLUDER_FLAGS
+
+/* pulse width modulation oscillator variants */
+#define OSC_INCLUDER_FLAGS OSC_FLAG_PULSE_OSC
+#define GSL_INCLUDER_FIRST_CASE 0
+#define GSL_INCLUDER_LAST_CASE 127
+#define GSL_INCLUDER_NAME oscillator_process_pulse
+#define GSL_INCLUDER_TABLE static void (*osc_process_pulse_table[]) (GslOscData*,guint, \
+ const gfloat*,const gfloat*,const gfloat*, \
+ const gfloat*,gfloat*,gfloat*)
+#define GSL_INCLUDER_FILE "gsloscillator-aux.c"
+#include "gslincluder.c"
+#undef OSC_INCLUDER_FLAGS
+
+
+/* --- functions --- */
+static inline void
+osc_process (GslOscData *osc,
+ guint n_values,
+ guint mode,
+ const gfloat *ifreq,
+ const gfloat *imod,
+ const gfloat *isync,
+ const gfloat *ipwm,
+ gfloat *mono_out,
+ gfloat *sync_out)
+{
+ mode |= isync ? OSC_FLAG_ISYNC : 0;
+ mode |= sync_out ? OSC_FLAG_OSYNC : 0;
+ mode |= ifreq ? OSC_FLAG_FREQ : 0;
+ if (osc->config.pulse_mod_strength > GSL_FLOAT_MIN_NORMAL)
+ mode |= ipwm ? OSC_FLAG_PWM_MOD : 0;
+ if (osc->config.self_fm_strength > GSL_FLOAT_MIN_NORMAL)
+ mode |= OSC_FLAG_SELF_MOD;
+ if (imod && osc->config.exponential_fm)
+ mode |= OSC_FLAG_EXP_MOD;
+ else if (imod)
+ mode |= OSC_FLAG_LINEAR_MOD;
+
+ if_reject (mode != osc->last_mode)
+ {
+ guint change_mask = osc->last_mode >= OSC_FLAG_INVAL ? OSC_FLAG_INVAL : osc->last_mode ^ mode;
+
+ if (change_mask & OSC_FLAG_FREQ)
+ {
+ gdouble fcpos, flpos;
+
+ fcpos = osc->cur_pos * osc->wave.ifrac_to_float;
+ flpos = osc->last_pos * osc->wave.ifrac_to_float;
+ osc->last_freq_level = osc->config.cfreq;
+ gsl_osc_table_lookup (osc->config.table, osc->last_freq_level, &osc->wave);
+ osc->last_pos = flpos / osc->wave.ifrac_to_float;
+ osc->cur_pos = fcpos / osc->wave.ifrac_to_float;
+ }
+ if (!(mode & OSC_FLAG_ISYNC))
+ osc->last_sync_level = 0;
+ if (mode & OSC_FLAG_PULSE_OSC)
+ {
+ osc->last_pwm_level = 0;
+ osc_update_pwm_offset (osc, osc->last_pwm_level);
+ }
+ osc->last_mode = mode;
+ }
+
+ /* invoke generated function variant */
+ if (mode & OSC_FLAG_PULSE_OSC)
+ osc_process_pulse_table[mode & ~OSC_FLAG_PULSE_OSC] (osc, n_values,
+ ifreq, imod, isync, ipwm,
+ mono_out, sync_out);
+ else
+ osc_process_table[mode] (osc, n_values,
+ ifreq, imod, isync, NULL,
+ mono_out, sync_out);
+}
+
+void
+gsl_osc_process (GslOscData *osc,
+ guint n_values,
+ const gfloat *ifreq,
+ const gfloat *imod,
+ const gfloat *isync,
+ gfloat *mono_out,
+ gfloat *sync_out)
+{
+ g_return_if_fail (osc != NULL);
+ g_return_if_fail (n_values > 0);
+ g_return_if_fail (mono_out != NULL);
+
+ if (osc->last_mode & OSC_FLAG_PULSE_OSC)
+ osc->last_mode = OSC_FLAG_INVAL;
+ osc_process (osc, n_values, 0,
+ ifreq, imod, isync, NULL,
+ mono_out, sync_out);
+}
+
+void
+gsl_osc_process_pulse (GslOscData *osc,
+ guint n_values,
+ const gfloat *ifreq,
+ const gfloat *imod,
+ const gfloat *isync,
+ const gfloat *ipwm,
+ gfloat *mono_out,
+ gfloat *sync_out)
+{
+ g_return_if_fail (osc != NULL);
+ g_return_if_fail (n_values > 0);
+ g_return_if_fail (mono_out != NULL);
+
+ if (!(osc->last_mode & OSC_FLAG_PULSE_OSC))
+ osc->last_mode = OSC_FLAG_INVAL;
+ osc_process (osc, n_values, OSC_FLAG_PULSE_OSC,
+ ifreq, imod, isync, ipwm,
+ mono_out, sync_out);
+}
+
+void
+gsl_osc_config (GslOscData *osc,
+ GslOscConfig *config)
+{
+ g_return_if_fail (osc != NULL);
+ g_return_if_fail (config != NULL);
+ g_return_if_fail (config->table != NULL);
+
+ osc->config = *config;
+ osc->last_mode = OSC_FLAG_INVAL;
+}