diff options
Diffstat (limited to 'flow/gsl/gslosctable.c')
-rw-r--r-- | flow/gsl/gslosctable.c | 626 |
1 files changed, 626 insertions, 0 deletions
diff --git a/flow/gsl/gslosctable.c b/flow/gsl/gslosctable.c new file mode 100644 index 0000000..f3e096d --- /dev/null +++ b/flow/gsl/gslosctable.c @@ -0,0 +1,626 @@ +/* GSL - Generic Sound Layer + * Copyright (C) 2001-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 "gslosctable.h" + +#include <string.h> +#include "gbsearcharray.h" +#include "gslcommon.h" +#include "gslmath.h" +#include "gslfft.h" + + +#define OSC_DEBUG GSL_DEBUG_FUNCTION (GSL_MSG_OSC, G_STRLOC) + +#define OSC_FREQ_EPSILON (1e-3) /* range within which frequencies are "equal" */ + +/* compare mfreqs against each other, use an arbitrary sample rate + * for which OSC_FREQ_EPSILON makes sense + */ +#define CACHE_MATCH_FREQ(usr_mfreq, cache_mfreq) \ + (fabs ((cache_mfreq) * 44107 - (usr_mfreq) * 44107) < OSC_FREQ_EPSILON) + + +/* --- structures --- */ +typedef struct +{ + /* main key (osc and cache tables) */ + gfloat mfreq; /* [0..0.5], mix_freq relative */ + /* secondary key (cache tables) */ + GslOscWaveForm wave_form; + guint8 *filter_func; /* just here for key indexing */ + /* data */ + guint ref_count; + guint min_pos, max_pos; /* pulse extension */ + guint n_values; + const gfloat values[1]; /* flexible array */ +} OscTableEntry; + + +/* --- prototypes --- */ +static gint cache_table_entry_locs_cmp (gconstpointer bsearch_node1, /* key */ + gconstpointer bsearch_node2); +static gint osc_table_entry_locs_cmp (gconstpointer bsearch_node1, /* key */ + gconstpointer bsearch_node2); +static void osc_wave_extrema_pos (guint n_values, + const gfloat *values, + guint *minp_p, + guint *maxp_p); +void gsl_osc_cache_debug_dump (void); + + +/* --- variables --- */ +static GBSearchArray *cache_entries = NULL; +static const GBSearchConfig cache_taconfig = { + sizeof (OscTableEntry*), + cache_table_entry_locs_cmp, + 0 +}; +static const GBSearchConfig osc_taconfig = { + sizeof (OscTableEntry*), + osc_table_entry_locs_cmp, + 0 +}; + + +/* --- functions --- */ +static gint +cache_table_entry_locs_cmp (gconstpointer bsearch_node1, /* key */ + gconstpointer bsearch_node2) +{ + const OscTableEntry * const *ep1 = bsearch_node1; + const OscTableEntry * const *ep2 = bsearch_node2; + const OscTableEntry *e1 = *ep1; + const OscTableEntry *e2 = *ep2; + + if (e1->wave_form == e2->wave_form) + { + if (e1->filter_func == e2->filter_func) + return G_BSEARCH_ARRAY_CMP (e1->mfreq, e2->mfreq); + else + return e1->filter_func > e2->filter_func ? 1 : -1; + } + else + return e1->wave_form > e2->wave_form ? 1 : -1; +} + +static gint +osc_table_entry_locs_cmp (gconstpointer bsearch_node1, /* key */ + gconstpointer bsearch_node2) +{ + const OscTableEntry * const *ep1 = bsearch_node1; + const OscTableEntry * const *ep2 = bsearch_node2; + const OscTableEntry *e1 = *ep1; + const OscTableEntry *e2 = *ep2; + + return G_BSEARCH_ARRAY_CMP (e1->mfreq, e2->mfreq); +} + +static OscTableEntry* +cache_table_entry_lookup_best (GslOscWaveForm wave_form, + guint8* filter_func, + gfloat mfreq) +{ + OscTableEntry key, *k = &key, **ep1 = NULL, **ep2, **ep3 = NULL; + + key.mfreq = mfreq; + key.wave_form = wave_form; + key.filter_func = filter_func; + + /* get exact match or a match which is one off into either direction */ + ep2 = g_bsearch_array_lookup_sibling (cache_entries, &cache_taconfig, &k); + if (ep2) + { + guint i = g_bsearch_array_get_index (cache_entries, &cache_taconfig, ep2); + + /* get siblings */ + if (i > 0) + ep1 = g_bsearch_array_get_nth (cache_entries, &cache_taconfig, i - 1); + if (i + 1 < g_bsearch_array_get_n_nodes (cache_entries)) + ep3 = g_bsearch_array_get_nth (cache_entries, &cache_taconfig, i + 1); + + /* get rid of invalid matches, i.e. ones with: + * - a different wave + * - a different filter + * - a filter wider than required + */ + if (ep1 && ((*ep1)->wave_form != wave_form || + (*ep1)->filter_func != filter_func || + (*ep1)->mfreq < mfreq)) + ep1 = NULL; + if (ep3 && ((*ep3)->wave_form != wave_form || + (*ep3)->filter_func != filter_func || + (*ep3)->mfreq < mfreq)) + ep3 = NULL; + if ((*ep2)->wave_form != wave_form || + (*ep2)->filter_func != filter_func || + (*ep2)->mfreq < mfreq) + { + /* collapse siblings, so that, if we have valid matches, ep2 is amongst them */ + if (ep1) + { + ep2 = ep1; + ep1 = NULL; + } + else if (ep3) + { + ep2 = ep3; + ep3 = NULL; + } + else + ep2 = NULL; /* no valid match at all */ + } + } + + /* now figure best out of valid siblings */ + if (ep2) + { + if (ep1 && fabs ((*ep1)->mfreq - mfreq) < fabs ((*ep2)->mfreq - mfreq)) + ep2 = ep1; + if (ep3 && fabs ((*ep3)->mfreq - mfreq) < fabs ((*ep2)->mfreq - mfreq)) + ep2 = ep3; + } + return ep2 ? *ep2 : NULL; +} + +static OscTableEntry* +osc_table_entry_lookup_best (const GslOscTable *table, + gfloat mfreq, + gfloat *min_mfreq) +{ + OscTableEntry key, *k = &key, **ep; + guint i; + + /* get exact match or a match which is one off into either direction */ + key.mfreq = mfreq; + ep = g_bsearch_array_lookup_sibling (table->entry_array, &osc_taconfig, &k); + if_reject (!ep) + return NULL; /* ugh, bad */ + + if (mfreq > (*ep)->mfreq) /* need better filter */ + { + i = g_bsearch_array_get_index (table->entry_array, &osc_taconfig, ep); + if (i + 1 < g_bsearch_array_get_n_nodes (table->entry_array)) + ep = g_bsearch_array_get_nth (table->entry_array, &osc_taconfig, i + 1); + else /* bad, might cause aliasing */ + OSC_DEBUG ("lookup mismatch, aliasing possible: want_freq=%f got_freq=%f", + mfreq * table->mix_freq, (*ep)->mfreq * table->mix_freq); + } + + if (min_mfreq) + { + /* fetch mfreq from previous */ + i = g_bsearch_array_get_index (table->entry_array, &osc_taconfig, ep); + if (i > 0) + { + OscTableEntry **tp = g_bsearch_array_get_nth (table->entry_array, &osc_taconfig, i - 1); + + *min_mfreq = (*tp)->mfreq; + } + else + *min_mfreq = 0; + } + + return *ep; +} + +static guint +wave_table_size (GslOscWaveForm wave_form, + gfloat mfreq) +{ + /* have to return power of 2, and honour 8 <= size */ + + /* FIXME: decide on other table sizes + 10000: 256 + 5000: 512 + 2500: 1024 + 1250: 2048 + GSL_OSC_WAVE_SAW_FALL always huge buffers to guarantee pulse width stepping granularity + */ + + if (wave_form == GSL_OSC_WAVE_SAW_FALL) + return 8192; + + return 2048; +} + +static void +fft_filter (guint n_values, + gfloat *values, /* [0..n_values], n_values/2 complex values */ + gdouble scale_window, + double (*window) (double)) +{ + guint i; + + n_values >>= 1; + scale_window /= (gdouble) n_values; + for (i = 0; i <= n_values; i++) + { + gdouble w = window (i * scale_window); + values[i * 2] *= w; + values[i * 2 + 1] *= w; + } +} + +static OscTableEntry* +cache_table_ref_entry (GslOscWaveForm wave_form, + double (*filter_func) (double), + gfloat mfreq) +{ + OscTableEntry *e = cache_table_entry_lookup_best (wave_form, (guint8*) filter_func, mfreq); + + if (e && !CACHE_MATCH_FREQ (mfreq, e->mfreq)) + e = NULL; + if (!e) + { + guint size = wave_table_size (wave_form, mfreq); + gfloat *values, *fft, step, min, max; + + /* size: + * - OscTableEntry already contains the first float values + * - we need n_values+1 adressable floats to provide values[0] == values[n_values] + */ + e = g_malloc (sizeof (OscTableEntry) + sizeof (gfloat) * size); + values = (gfloat*) &e->values[0]; + e->wave_form = wave_form; + e->filter_func = (guint8*) filter_func; + e->mfreq = mfreq; + e->ref_count = 1; + e->n_values = size; + gsl_osc_wave_fill_buffer (e->wave_form, e->n_values, values); + + /* filter wave accordingly */ + gsl_osc_wave_extrema (e->n_values, values, &min, &max); + fft = g_new (gfloat, e->n_values + 2); /* [0..n_values] for n_values/2 complex freqs */ + gsl_power2_fftar_simple (e->n_values, values, fft); + step = e->mfreq * (gdouble) e->n_values; + fft_filter (e->n_values, fft, step, filter_func); + gsl_power2_fftsr_simple (e->n_values, fft, values); + g_free (fft); + gsl_osc_wave_normalize (e->n_values, values, (min + max) / 2, max); + + /* provide values[0]==values[n_values] */ + values[e->n_values] = values[0]; + + /* pulse min/max pos extension */ + osc_wave_extrema_pos (e->n_values, values, &e->min_pos, &e->max_pos); + + /* insert into cache */ + cache_entries = g_bsearch_array_insert (cache_entries, &cache_taconfig, &e); + } + else + e->ref_count++; + return e; +} + +static void +cache_table_unref_entry (OscTableEntry *e) +{ + g_return_if_fail (e->ref_count > 0); + + e->ref_count -= 1; + if (e->ref_count == 0) + { + OscTableEntry **ep; + guint i; + + ep = g_bsearch_array_lookup (cache_entries, &cache_taconfig, &e); + i = g_bsearch_array_get_index (cache_entries, &cache_taconfig, ep); + cache_entries = g_bsearch_array_remove (cache_entries, &cache_taconfig, i); + } +} + +GslOscTable* +gsl_osc_table_create (gfloat mix_freq, + GslOscWaveForm wave_form, + double (*filter_func) (double), + guint n_freqs, + const gfloat *freqs) +{ + GslOscTable *table; + gfloat nyquist; + guint i; + + g_return_val_if_fail (mix_freq > 0, NULL); + g_return_val_if_fail (n_freqs > 0, NULL); + g_return_val_if_fail (freqs != NULL, NULL); + + if (!cache_entries) + cache_entries = g_bsearch_array_create (&cache_taconfig); + + table = gsl_new_struct (GslOscTable, 1); + table->mix_freq = mix_freq; + table->wave_form = wave_form; + table->entry_array = g_bsearch_array_create (&osc_taconfig); + nyquist = table->mix_freq * 0.5; + if (wave_form == GSL_OSC_WAVE_PULSE_SAW) + wave_form = GSL_OSC_WAVE_SAW_FALL; + for (i = 0; i < n_freqs; i++) + { + OscTableEntry *e; + gdouble mfreq = MIN (nyquist, freqs[i]); + + mfreq /= table->mix_freq; + e = osc_table_entry_lookup_best (table, mfreq, NULL); + if (!e || fabs (e->mfreq * table->mix_freq - mfreq * table->mix_freq) > OSC_FREQ_EPSILON) + { + e = cache_table_ref_entry (wave_form, filter_func, mfreq); + table->entry_array = g_bsearch_array_insert (table->entry_array, &osc_taconfig, &e); + } + else if (e) + OSC_DEBUG ("not inserting existing entry (freq=%f) for freq %f (nyquist=%f)", + e->mfreq * table->mix_freq, mfreq * table->mix_freq, nyquist); + } + + return table; +} + +void +gsl_osc_table_lookup (const GslOscTable *table, + gfloat freq, + GslOscWave *wave) +{ + OscTableEntry *e; + gfloat mfreq, min_mfreq; + + g_return_if_fail (table != NULL); + g_return_if_fail (wave != NULL); + + mfreq = freq / table->mix_freq; + e = osc_table_entry_lookup_best (table, mfreq, &min_mfreq); + if (e) + { + guint32 int_one; + gfloat float_one; + + wave->min_freq = min_mfreq * table->mix_freq; + wave->max_freq = e->mfreq * table->mix_freq; + wave->n_values = e->n_values; + wave->values = e->values; + wave->n_frac_bits = g_bit_storage (wave->n_values - 1); + wave->n_frac_bits = 32 - wave->n_frac_bits; + int_one = 1 << wave->n_frac_bits; + wave->frac_bitmask = int_one - 1; + float_one = int_one; + wave->freq_to_step = float_one * wave->n_values / table->mix_freq; + wave->phase_to_pos = wave->n_values * float_one; + wave->ifrac_to_float = 1.0 / float_one; + /* pulse min/max pos extension */ + wave->min_pos = e->min_pos; + wave->max_pos = e->max_pos; + } + else + { + /* shouldn't happen */ + OSC_DEBUG ("table lookup revealed NULL, empty table?"); + memset (wave, 0, sizeof (*wave)); + } +} + +void +gsl_osc_table_free (GslOscTable *table) +{ + guint n; + + g_return_if_fail (table != NULL); + + n = g_bsearch_array_get_n_nodes (table->entry_array); + while (n--) + { + OscTableEntry **ep; + + ep = g_bsearch_array_get_nth (table->entry_array, &osc_taconfig, n); + cache_table_unref_entry (*ep); + table->entry_array = g_bsearch_array_remove (table->entry_array, &osc_taconfig, n); + } + g_bsearch_array_free (table->entry_array, &osc_taconfig); + gsl_delete_struct (GslOscTable, table); +} + +void +gsl_osc_cache_debug_dump (void) +{ + OSC_DEBUG ("left in cache: %u", g_bsearch_array_get_n_nodes (cache_entries)); +} + +void +gsl_osc_wave_fill_buffer (GslOscWaveForm type, + guint n_values, + gfloat *values) +{ + gdouble max = n_values, hmax = max * 0.5, qmax = n_values * 0.25; + gint i, half = n_values / 2, quarter = half / 2; + + switch (type) + { + gdouble frac, pos; + case GSL_OSC_WAVE_SINE: + for (i = 0; i < n_values; i++) + { + frac = ((gdouble) i) / max; /* [0..1[ */ + pos = frac * 2. * GSL_PI; + values[i] = sin (pos); + } + break; + case GSL_OSC_WAVE_SAW_RISE: + for (i = 0; i < n_values; i++) + { + frac = ((gdouble) i) / max; /* [0..1[ */ + values[i] = 2.0 * frac - 1.0; + } + break; + case GSL_OSC_WAVE_SAW_FALL: + for (i = 0; i < n_values; i++) + { + frac = ((gdouble) i) / max; /* [0..1[ */ + values[i] = 1.0 - 2.0 * frac; + } + break; + case GSL_OSC_WAVE_PEAK_RISE: /* spaced saw */ + for (i = 0; i < half; i++) + { + frac = ((gdouble) i) / hmax; + values[i] = 2.0 * frac - 1.0; + } + for (; i < n_values; i++) + values[i] = -1.0; + break; + case GSL_OSC_WAVE_PEAK_FALL: /* spaced saw */ + for (i = 0; i < half; i++) + { + frac = ((gdouble) i) / hmax; + values[i] = 1.0 - 2.0 * frac; + } + for (; i < n_values; i++) + values[i] = -1.0; + break; + case GSL_OSC_WAVE_TRIANGLE: + for (i = 0; i < quarter; i++) + { + frac = ((gdouble) i) / qmax; + values[i] = frac; + } + for (; i < half + quarter; i++) + { + frac = ((gdouble) i - quarter) / hmax; + values[i] = 1.0 - 2.0 * frac; + } + for (; i < n_values; i++) + { + frac = ((gdouble) i - half - quarter) / qmax; + values[i] = frac - 1.0; + } + break; + case GSL_OSC_WAVE_MOOG_SAW: + for (i = 0; i < half; i++) + { + frac = ((gdouble) i) / hmax; + values[i] = 2.0 * frac - 1.0; + } + for (; i < n_values; i++) + { + frac = ((gdouble) i) / max; + values[i] = 1.0 - 2.0 * frac; + } + break; + case GSL_OSC_WAVE_SQUARE: + for (i = 0; i < half; i++) + values[i] = 1.0; + for (; i < n_values; i++) + values[i] = -1.0; + break; + default: + g_critical ("%s: invalid wave form id (%u)", G_STRLOC, type); + case GSL_OSC_WAVE_NONE: + for (i = 0; i < n_values; i++) + values[i] = 0; + break; + } +} + +static void +osc_wave_extrema_pos (guint n_values, + const gfloat *values, + guint *minp_p, + guint *maxp_p) +{ + guint i, minp = 0, maxp = 0; + gfloat min = values[0], max = min; + + for (i = 1; i < n_values; i++) + { + if (values[i] > max) + { + max = values[i]; + maxp = i; + } + else if (values[i] < min) + { + min = values[i]; + minp = i; + } + } + *minp_p = minp; + *maxp_p = maxp; +} + +void +gsl_osc_wave_extrema (guint n_values, + const gfloat *values, + gfloat *min_p, + gfloat *max_p) +{ + guint minp, maxp; + + g_return_if_fail (n_values > 0 && values != NULL && min_p != NULL && max_p != NULL); + + osc_wave_extrema_pos (n_values, values, &minp, &maxp); + *min_p = values[minp]; + *max_p = values[maxp]; +} + +void +gsl_osc_wave_adjust_range (guint n_values, + gfloat *values, + gfloat min, + gfloat max, + gfloat new_center, + gfloat new_max) +{ + gfloat center; + guint i; + + g_return_if_fail (n_values > 0 && values != NULL); + + center = (min + max) / 2; + center = new_center - center; + min = fabs (min + center); + max = fabs (max + center); + if (min > max) + max = min; + if (max > GSL_FLOAT_MIN_NORMAL) + max = new_max / max; + else + max = 0; + for (i = 0; i < n_values; i++) + values[i] = (values[i] + center) * max; +} + +void +gsl_osc_wave_normalize (guint n_values, + gfloat *values, + gfloat new_center, + gfloat new_max) +{ + gfloat min, max; + guint i; + + g_return_if_fail (n_values > 0 && values != NULL); + + min = values[0]; + max = min; + for (i = 1; i < n_values; i++) + { + register gfloat v = values[i]; + + max = MAX (max, v); + min = MIN (min, v); + } + + gsl_osc_wave_adjust_range (n_values, values, min, max, new_center, new_max); +} |