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.
215 lines
6.5 KiB
215 lines
6.5 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.
|
|
*/
|
|
|
|
|
|
#define OSC_FLAGS (GSL_INCLUDER_CASE | OSC_INCLUDER_FLAGS)
|
|
#define ISYNC1_OSYNC0 ((OSC_FLAGS & OSC_FLAG_ISYNC) && !(OSC_FLAGS & OSC_FLAG_OSYNC))
|
|
#define ISYNC1_OSYNC1 ((OSC_FLAGS & OSC_FLAG_ISYNC) && (OSC_FLAGS & OSC_FLAG_OSYNC))
|
|
#define ISYNC0_OSYNC1 ((OSC_FLAGS & OSC_FLAG_OSYNC) && !(OSC_FLAGS & OSC_FLAG_ISYNC))
|
|
#define WITH_OSYNC (OSC_FLAGS & OSC_FLAG_OSYNC)
|
|
#define WITH_FREQ (OSC_FLAGS & OSC_FLAG_FREQ)
|
|
#define WITH_SMOD (OSC_FLAGS & OSC_FLAG_SELF_MOD)
|
|
#define WITH_LMOD (OSC_FLAGS & OSC_FLAG_LINEAR_MOD)
|
|
#define WITH_EMOD (OSC_FLAGS & OSC_FLAG_EXP_MOD)
|
|
#define WITH_PWM_MOD (OSC_FLAGS & OSC_FLAG_PWM_MOD)
|
|
#define PULSE_OSC (OSC_FLAGS & OSC_FLAG_PULSE_OSC)
|
|
|
|
|
|
static void
|
|
GSL_INCLUDER_FUNC (GslOscData *osc,
|
|
guint n_values,
|
|
const gfloat *ifreq,
|
|
const gfloat *mod_in,
|
|
const gfloat *sync_in,
|
|
const gfloat *pwm_in,
|
|
gfloat *mono_out,
|
|
gfloat *sync_out)
|
|
{
|
|
gfloat last_sync_level = osc->last_sync_level;
|
|
gfloat last_pwm_level = osc->last_pwm_level;
|
|
gdouble last_freq_level = osc->last_freq_level;
|
|
guint32 cur_pos = osc->cur_pos;
|
|
guint32 last_pos = osc->last_pos;
|
|
guint32 sync_pos, pos_inc;
|
|
gfloat posm_strength, self_posm_strength;
|
|
gfloat *boundary = mono_out + n_values;
|
|
GslOscWave *wave = &osc->wave;
|
|
|
|
/* FIXME: should we do gsl_fpu_setround() here? */
|
|
|
|
pos_inc = gsl_dtoi (osc->last_freq_level * gsl_cent_factor (osc->config.fine_tune) * wave->freq_to_step);
|
|
sync_pos = osc->config.phase * wave->phase_to_pos;
|
|
posm_strength = pos_inc * osc->config.fm_strength;
|
|
self_posm_strength = pos_inc * osc->config.self_fm_strength;
|
|
|
|
/* do the mixing */
|
|
do
|
|
{
|
|
gfloat v;
|
|
|
|
/* handle syncs
|
|
*/
|
|
#if (ISYNC1_OSYNC0) /* input sync only */
|
|
{
|
|
gfloat sync_level = *sync_in++;
|
|
if_reject (GSL_SIGNAL_RAISING_EDGE (last_sync_level, sync_level))
|
|
cur_pos = sync_pos;
|
|
last_sync_level = sync_level;
|
|
}
|
|
#elif (ISYNC1_OSYNC1) /* input and output sync */
|
|
{
|
|
gfloat sync_level = *sync_in++;
|
|
if_reject (GSL_SIGNAL_RAISING_EDGE (last_sync_level, sync_level))
|
|
{
|
|
cur_pos = sync_pos;
|
|
*sync_out++ = 1.0;
|
|
}
|
|
else /* figure output sync position */
|
|
{
|
|
guint is_sync = (sync_pos <= cur_pos) + (last_pos < sync_pos) + (cur_pos < last_pos);
|
|
*sync_out++ = is_sync >= 2 ? 1.0 : 0.0;
|
|
}
|
|
last_sync_level = sync_level;
|
|
}
|
|
#elif (ISYNC0_OSYNC1) /* output sync only */
|
|
{
|
|
/* figure output sync position */
|
|
guint is_sync = (sync_pos <= cur_pos) + (last_pos < sync_pos) + (cur_pos < last_pos);
|
|
*sync_out++ = is_sync >= 2 ? 1.0 : 0.0;
|
|
}
|
|
#endif
|
|
|
|
/* track frequency changes
|
|
*/
|
|
#if (WITH_FREQ)
|
|
{
|
|
gdouble freq_level = *ifreq++;
|
|
freq_level = GSL_SIGNAL_TO_FREQ (freq_level);
|
|
if (GSL_SIGNAL_FREQ_CHANGED (last_freq_level, freq_level))
|
|
{
|
|
if_reject (freq_level <= wave->min_freq || freq_level > wave->max_freq)
|
|
{
|
|
gdouble fcpos, flpos;
|
|
const gfloat *orig_values = wave->values;
|
|
|
|
fcpos = cur_pos * wave->ifrac_to_float;
|
|
flpos = last_pos * wave->ifrac_to_float;
|
|
gsl_osc_table_lookup (osc->config.table, freq_level, wave);
|
|
if (orig_values != wave->values) /* catch non-changes */
|
|
{
|
|
last_pos = flpos / wave->ifrac_to_float;
|
|
cur_pos = fcpos / wave->ifrac_to_float;
|
|
sync_pos = osc->config.phase * wave->phase_to_pos;
|
|
pos_inc = gsl_dtoi (freq_level * gsl_cent_factor (osc->config.fine_tune) * wave->freq_to_step);
|
|
#if (PULSE_OSC)
|
|
osc->last_pwm_level = 0;
|
|
osc_update_pwm_offset (osc, osc->last_pwm_level);
|
|
last_pwm_level = osc->last_pwm_level;
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
pos_inc = gsl_dtoi (freq_level * gsl_cent_factor (osc->config.fine_tune) * wave->freq_to_step);
|
|
posm_strength = pos_inc * osc->config.fm_strength;
|
|
self_posm_strength = pos_inc * osc->config.self_fm_strength;
|
|
last_freq_level = freq_level;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* track pulse witdh modulation
|
|
*/
|
|
#if (WITH_PWM_MOD)
|
|
{
|
|
gfloat pwm_level = *pwm_in++;
|
|
if (fabs (last_pwm_level - pwm_level) > 1.0 / 65536.0)
|
|
{
|
|
last_pwm_level = pwm_level;
|
|
osc_update_pwm_offset (osc, pwm_level);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* output signal calculation
|
|
*/
|
|
#if (PULSE_OSC) /* pulse width modulation oscillator */
|
|
{
|
|
guint32 tpos, ipos;
|
|
tpos = cur_pos >> wave->n_frac_bits;
|
|
ipos = (cur_pos - osc->pwm_offset) >> wave->n_frac_bits;
|
|
v = wave->values[tpos] - wave->values[ipos];
|
|
v = (v + osc->pwm_center) * osc->pwm_max;
|
|
}
|
|
#else /* table read out and linear ipol */
|
|
{
|
|
guint32 tpos, ifrac;
|
|
gfloat ffrac, w;
|
|
tpos = cur_pos >> wave->n_frac_bits;
|
|
ifrac = cur_pos & wave->frac_bittqmask;
|
|
ffrac = ifrac * wave->ifrac_to_float;
|
|
v = wave->values[tpos];
|
|
w = wave->values[tpos + 1];
|
|
v *= 1.0 - ffrac;
|
|
w *= ffrac;
|
|
v += w;
|
|
}
|
|
#endif /* v = value_out done */
|
|
*mono_out++ = v;
|
|
|
|
/* position increment
|
|
*/
|
|
#if (WITH_OSYNC)
|
|
last_pos = cur_pos;
|
|
#endif
|
|
#if (WITH_SMOD) /* self modulation */
|
|
cur_pos += self_posm_strength * v;
|
|
#endif
|
|
#if (WITH_LMOD) /* linear fm */
|
|
{
|
|
gfloat mod_level = *mod_in++;
|
|
cur_pos += pos_inc + posm_strength * mod_level;
|
|
}
|
|
#elif (WITH_EMOD) /* exponential fm */
|
|
{
|
|
gfloat mod_level = *mod_in++;
|
|
cur_pos += pos_inc * gsl_signal_exp2 (osc->config.fm_strength * mod_level);
|
|
}
|
|
#else /* no modulation */
|
|
cur_pos += pos_inc;
|
|
#endif
|
|
}
|
|
while (mono_out < boundary);
|
|
|
|
osc->last_pos = WITH_OSYNC ? last_pos : cur_pos;
|
|
osc->cur_pos = cur_pos;
|
|
osc->last_sync_level = last_sync_level;
|
|
osc->last_freq_level = last_freq_level;
|
|
osc->last_pwm_level = last_pwm_level;
|
|
}
|
|
|
|
#undef ISYNC1_OSYNC0
|
|
#undef ISYNC1_OSYNC1
|
|
#undef ISYNC0_OSYNC1
|
|
#undef WITH_OSYNC
|
|
#undef WITH_FREQ
|
|
#undef WITH_SMOD
|
|
#undef WITH_LMOD
|
|
#undef WITH_EMOD
|
|
#undef OSC_FLAGS
|