diff options
Diffstat (limited to 'flow/gsl/gslwaveosc-aux.c')
-rw-r--r-- | flow/gsl/gslwaveosc-aux.c | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/flow/gsl/gslwaveosc-aux.c b/flow/gsl/gslwaveosc-aux.c new file mode 100644 index 0000000..4485242 --- /dev/null +++ b/flow/gsl/gslwaveosc-aux.c @@ -0,0 +1,249 @@ +/* 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., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, 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: */ |