diff options
Diffstat (limited to 'flow/gsl/gsloscillator.c')
-rw-r--r-- | flow/gsl/gsloscillator.c | 219 |
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; +} |