You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
arts/flow/gsl/gsloscillator.c

220 lines
6.7 KiB

/* 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., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, 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;
}