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/gslwaveosc-aux.c

250 lines
8.7 KiB

/* GSL - Generic Sound Layer
* Copyright (C) 2001-2002 Tim Janik and Stefan Westerfeld
*
* 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 CHECK_SYNC (WOSC_MIX_VARIANT & WOSC_MIX_WITH_SYNC)
#define CHECK_FREQ (WOSC_MIX_VARIANT & WOSC_MIX_WITH_FREQ)
#define CHECK_MOD (WOSC_MIX_VARIANT & WOSC_MIX_WITH_MOD)
#define EXPONENTIAL_FM (WOSC_MIX_VARIANT & WOSC_MIX_WITH_EXP_FM)
#define DIRSTRIDE(b) (b->dirstride) /* (1) change for n_channel stepping */
static void
WOSC_MIX_VARIANT_NAME (GslWaveOscData *wosc,
guint n_values,
const gfloat *freq_in,
const gfloat *mod_in,
const gfloat *sync_in,
gfloat *wave_out)
{
gfloat *wave_boundary;
gfloat last_sync_level = wosc->last_sync_level;
gfloat last_freq_level = wosc->last_freq_level;
gfloat last_mod_level = wosc->last_mod_level;
GslWaveChunkBlock *block = &wosc->block;
gdouble *a = wosc->a, *b = wosc->b, *y = wosc->y;
gfloat *boundary = block->end;
guint wosc_j = wosc->j;
/* do the mixing */
wave_boundary = wave_out + n_values;
do
{
gfloat ffrac;
if (CHECK_SYNC)
{
gfloat sync_level = *sync_in++;
if_reject (GSL_SIGNAL_RAISING_EDGE (last_sync_level, sync_level))
{
wosc->j = wosc_j;
gsl_wave_osc_retrigger (wosc, CHECK_FREQ ? GSL_SIGNAL_TO_FREQ (*freq_in) : wosc->config.cfreq);
/* retrigger alters last_freq and last_mod */
last_freq_level = wosc->last_freq_level;
last_mod_level = wosc->last_mod_level;
wosc_j = wosc->j;
boundary = block->end;
/* FIXME: g_assert (ABS (block->dirstride) == 1); */
last_sync_level = sync_level;
}
}
if (CHECK_MOD && CHECK_FREQ)
{
gfloat mod_level = *mod_in++, freq_level = *freq_in++;
if_reject (GSL_SIGNAL_FREQ_CHANGED (last_freq_level, freq_level))
{
last_freq_level = freq_level;
if (GSL_SIGNAL_MOD_CHANGED (last_mod_level, mod_level))
last_mod_level = mod_level;
goto UPDATE_FREQ;
}
else if_reject (GSL_SIGNAL_MOD_CHANGED (last_mod_level, mod_level))
{
gfloat new_freq;
last_mod_level = mod_level;
UPDATE_FREQ:
new_freq = GSL_SIGNAL_TO_FREQ (freq_level);
if (EXPONENTIAL_FM)
new_freq *= gsl_signal_exp2 (wosc->config.fm_strength * mod_level);
else /* LINEAR_FM */
new_freq *= 1.0 + wosc->config.fm_strength * mod_level;
wave_osc_transform_filter (wosc, new_freq);
}
}
else if (CHECK_MOD)
{
gfloat mod_level = *mod_in++;
if (GSL_SIGNAL_MOD_CHANGED (last_mod_level, mod_level))
{
gfloat new_freq = wosc->config.cfreq;
if (EXPONENTIAL_FM)
new_freq *= gsl_signal_exp2 (wosc->config.fm_strength * mod_level);
else /* LINEAR_FM */
new_freq *= 1.0 + wosc->config.fm_strength * mod_level;
last_mod_level = mod_level;
wave_osc_transform_filter (wosc, new_freq);
}
}
else if (CHECK_FREQ)
{
gfloat freq_level = *freq_in++;
if (GSL_SIGNAL_FREQ_CHANGED (last_freq_level, freq_level))
{
last_freq_level = freq_level;
wave_osc_transform_filter (wosc, GSL_SIGNAL_TO_FREQ (freq_level));
}
}
/* process filter while necesary */
while (wosc->cur_pos >= (FRAC_MASK + 1) << 1)
{
gfloat c, c0, c1, c2, c3, c4, c5, c6, c7, c8;
gfloat d, d0, d1, d2, d3, d4, d5, d6, d7;
gfloat *x;
if_reject (wosc->x >= boundary) /* wchunk block boundary */
{
GslLong next_offset = block->next_offset;
gsl_wave_chunk_unuse_block (wosc->wchunk, block);
block->play_dir = wosc->config.play_dir;
block->offset = next_offset;
gsl_wave_chunk_use_block (wosc->wchunk, block);
wosc->x = block->start + wosc->config.channel;
boundary = block->end;
/* FIXME: g_assert (ABS (block->dirstride) == 1); */
}
if_expect (block->dirstride > 0)
{
x = wosc->x;
d0 = b[0] * y[wosc_j]; wosc_j++; wosc_j &= 0x7;
d1 = b[1] * y[wosc_j]; wosc_j++; wosc_j &= 0x7;
d2 = b[2] * y[wosc_j]; wosc_j++; wosc_j &= 0x7;
d3 = b[3] * y[wosc_j]; wosc_j++; wosc_j &= 0x7;
d4 = b[4] * y[wosc_j]; wosc_j++; wosc_j &= 0x7;
d5 = b[5] * y[wosc_j]; wosc_j++; wosc_j &= 0x7;
d6 = b[6] * y[wosc_j]; wosc_j++; wosc_j &= 0x7;
d7 = b[7] * y[wosc_j]; wosc_j++; wosc_j &= 0x7;
c8 = a[8] * x[-4 * DIRSTRIDE (block)];
c6 = a[6] * x[-3 * DIRSTRIDE (block)];
c4 = a[4] * x[-2 * DIRSTRIDE (block)];
c2 = a[2] * x[-1 * DIRSTRIDE (block)];
c0 = a[0] * x[0 * DIRSTRIDE (block)];
d = d0 + d1 + d2 + d3 + d4 + d5 + d6 + d7;
c = c0 + c2 + c4 + c6 + c8;
y[wosc_j] = c - d; wosc_j++; wosc_j &= 0x7;
d0 = b[0] * y[wosc_j]; wosc_j++; wosc_j &= 0x7;
d1 = b[1] * y[wosc_j]; wosc_j++; wosc_j &= 0x7;
d2 = b[2] * y[wosc_j]; wosc_j++; wosc_j &= 0x7;
d3 = b[3] * y[wosc_j]; wosc_j++; wosc_j &= 0x7;
d4 = b[4] * y[wosc_j]; wosc_j++; wosc_j &= 0x7;
d5 = b[5] * y[wosc_j]; wosc_j++; wosc_j &= 0x7;
d6 = b[6] * y[wosc_j]; wosc_j++; wosc_j &= 0x7;
d7 = b[7] * y[wosc_j]; wosc_j++; wosc_j &= 0x7;
c7 = a[7] * x[-3 * DIRSTRIDE (block)];
c5 = a[5] * x[-2 * DIRSTRIDE (block)];
c3 = a[3] * x[-1 * DIRSTRIDE (block)];
c1 = a[1] * x[0 * DIRSTRIDE (block)];
d = d0 + d1 + d2 + d3 + d4 + d5 + d6 + d7;
c = c1 + c3 + c5 + c7;
y[wosc_j] = c - d; wosc_j++; wosc_j &= 0x7;
wosc->x += DIRSTRIDE (block);
}
else /* dirstride < 0 */
{
x = wosc->x;
d0 = b[0] * y[wosc_j]; wosc_j++; wosc_j &= 0x7;
d1 = b[1] * y[wosc_j]; wosc_j++; wosc_j &= 0x7;
d2 = b[2] * y[wosc_j]; wosc_j++; wosc_j &= 0x7;
d3 = b[3] * y[wosc_j]; wosc_j++; wosc_j &= 0x7;
d4 = b[4] * y[wosc_j]; wosc_j++; wosc_j &= 0x7;
d5 = b[5] * y[wosc_j]; wosc_j++; wosc_j &= 0x7;
d6 = b[6] * y[wosc_j]; wosc_j++; wosc_j &= 0x7;
d7 = b[7] * y[wosc_j]; wosc_j++; wosc_j &= 0x7;
c8 = a[8] * x[-4 * -DIRSTRIDE (block)];
c6 = a[6] * x[-3 * -DIRSTRIDE (block)];
c4 = a[4] * x[-2 * -DIRSTRIDE (block)];
c2 = a[2] * x[-1 * -DIRSTRIDE (block)];
c0 = a[0] * x[0 * -DIRSTRIDE (block)];
d = d0 + d1 + d2 + d3 + d4 + d5 + d6 + d7;
c = c0 + c2 + c4 + c6 + c8;
y[wosc_j] = c - d; wosc_j++; wosc_j &= 0x7;
d0 = b[0] * y[wosc_j]; wosc_j++; wosc_j &= 0x7;
d1 = b[1] * y[wosc_j]; wosc_j++; wosc_j &= 0x7;
d2 = b[2] * y[wosc_j]; wosc_j++; wosc_j &= 0x7;
d3 = b[3] * y[wosc_j]; wosc_j++; wosc_j &= 0x7;
d4 = b[4] * y[wosc_j]; wosc_j++; wosc_j &= 0x7;
d5 = b[5] * y[wosc_j]; wosc_j++; wosc_j &= 0x7;
d6 = b[6] * y[wosc_j]; wosc_j++; wosc_j &= 0x7;
d7 = b[7] * y[wosc_j]; wosc_j++; wosc_j &= 0x7;
c7 = a[7] * x[-3 * -DIRSTRIDE (block)];
c5 = a[5] * x[-2 * -DIRSTRIDE (block)];
c3 = a[3] * x[-1 * -DIRSTRIDE (block)];
c1 = a[1] * x[0 * -DIRSTRIDE (block)];
d = d0 + d1 + d2 + d3 + d4 + d5 + d6 + d7;
c = c1 + c3 + c5 + c7;
y[wosc_j] = c - d; wosc_j++; wosc_j &= 0x7;
wosc->x += -DIRSTRIDE (block);
}
wosc->cur_pos -= (FRAC_MASK + 1) << 1;
}
/* interpolate filter output from current pos
* wosc->cur_pos >> FRAC_SHIFT is 1 or 0;
*/
if (wosc->cur_pos >> FRAC_SHIFT)
{
guint k = wosc_j - 2;
ffrac = wosc->cur_pos & FRAC_MASK; /* int -> float */
ffrac *= 1. / (FRAC_MASK + 1.);
*wave_out++ = y[k & 0x7] * (1.0 - ffrac) + y[(k + 1) & 0x7] * ffrac;
}
else
{
guint k = wosc_j - 3;
ffrac = wosc->cur_pos; /* int -> float */
ffrac *= 1. / (FRAC_MASK + 1.);
*wave_out++ = y[k & 0x7] * (1.0 - ffrac) + y[(k + 1) & 0x7] * ffrac;
}
/* increment */
wosc->cur_pos += wosc->istep;
}
while (wave_out < wave_boundary);
wosc->j = wosc_j;
wosc->last_sync_level = last_sync_level;
wosc->last_freq_level = last_freq_level;
wosc->last_mod_level = last_mod_level;
}
#undef CHECK_SYNC
#undef CHECK_FREQ
#undef CHECK_MOD
#undef EXPONENTIAL_FM
#undef DIRSTRIDE
#undef WOSC_MIX_VARIANT
#undef WOSC_MIX_VARIANT_NAME
/* vim:set ts=8 sw=2 sts=2: */