TOOLS: compile C sources as C++ sources
authorStefan Westerfeld <stefan@space.twc.de>
Tue, 10 May 2011 10:19:05 +0000 (12:19 +0200)
committerTim Janik <timj@gtk.org>
Wed, 11 May 2011 00:55:38 +0000 (02:55 +0200)
16 files changed:
tools/Makefile.am
tools/bseloopfuncs.c [deleted file]
tools/bseloopfuncs.cc [new file with mode: 0644]
tools/bseloopfuncs.h [deleted file]
tools/bseloopfuncs.hh [new file with mode: 0644]
tools/bsewavetool.hh
tools/cutvorbis.c [deleted file]
tools/cutvorbis.cc [new file with mode: 0644]
tools/magictest.c [deleted file]
tools/magictest.cc [new file with mode: 0644]
tools/mathtool.c [deleted file]
tools/mathtool.cc [new file with mode: 0644]
tools/sfiutils.c [deleted file]
tools/sfiutils.cc [new file with mode: 0644]
tools/sfiutils.h [deleted file]
tools/sfiutils.hh [new file with mode: 0644]

index e0a51c2..180adfe 100644 (file)
@@ -17,12 +17,12 @@ progs_ldadd     = $(top_builddir)/bse/libbse.la $(BSE_LIBS)
 #
 
 bin_PROGRAMS        = bsewavetool
-bsewavetool_SOURCES = bsewavetool.cc bwtwave.cc bseloopfuncs.c
+bsewavetool_SOURCES = bsewavetool.cc bwtwave.cc bseloopfuncs.cc
 bsewavetool_LDADD   = $(progs_ldadd)
-EXTRA_DIST         += bsewavetool.hh bwtwave.hh bseloopfuncs.h sfiutils.h
+EXTRA_DIST         += bsewavetool.hh bwtwave.hh bseloopfuncs.hh sfiutils.hh
 
 noinst_PROGRAMS  += cutvorbis
-cutvorbis_SOURCES = cutvorbis.c cxxdummy.cc
+cutvorbis_SOURCES = cutvorbis.cc cxxdummy.cc
 cutvorbis_LDADD   = $(progs_ldadd)
 
 noinst_PROGRAMS    += bsefextract
@@ -34,11 +34,11 @@ bsefcompare_SOURCES = bsefcompare.cc
 bsefcompare_LDADD   = $(progs_ldadd)
 
 noinst_PROGRAMS  += magictest
-magictest_SOURCES = magictest.c cxxdummy.cc
+magictest_SOURCES = magictest.cc cxxdummy.cc
 magictest_LDADD   = $(progs_ldadd)
 
 noinst_PROGRAMS += mathtool
-mathtool_SOURCES = mathtool.c cxxdummy.cc
+mathtool_SOURCES = mathtool.cc cxxdummy.cc
 mathtool_LDADD   = $(progs_ldadd)
 
 # === Bsewavetool tests ===
diff --git a/tools/bseloopfuncs.c b/tools/bseloopfuncs.c
deleted file mode 100644 (file)
index 85c376c..0000000
+++ /dev/null
@@ -1,934 +0,0 @@
-/* BSE - Better Sound Engine
- * Copyright (C) 2001, 2003 Tim Janik and Stefan Westerfeld
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 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
- * Lesser General Public License for more details.
- *
- * A copy of the GNU Lesser General Public License should ship along
- * with this library; if not, see http://www.gnu.org/copyleft/.
- */
-#include "bseloopfuncs.h"
-#include <bse/gsldatacache.h>
-#include <string.h>
-#include <signal.h>    /* G_BREAKPOINT() */
-#include <stdio.h>
-#include <math.h>
-
-typedef struct {
-  gdouble score;
-  GslLong loop_start;
-  GslLong loop_length;
-} LoopEntry;
-typedef struct {
-  guint      max_entries;
-  guint      n_entries;
-  gdouble    worst_score;
-  LoopEntry  entries[1];        /* flexible array */
-} LoopStack;
-
-static gdouble
-score_headloop (GslDataHandle *dhandle,
-                const gfloat  *ls, /* loop start */
-                GslLong        ll, /* loop length */
-                GslLong        cl, /* compare area length */
-                gdouble        max_score)
-{
-  GslLong nl = cl / ll;         /* number of full loop comparisons */
-  GslLong rl = cl - nl * ll;    /* fraction of last comparison */
-  const gfloat *c = ls + ll;    /* compare area start */
-  const gfloat *p, *le = ls + ll;
-  gdouble score = 0;
-
-  /* compute score for loop repeated over comparison area
-   * .......|##########|-------------------------|......
-   *        ls         c                        c+cl
-   */
-
-  while (nl--)
-    {
-      p = ls;
-      while (p < le)
-        {
-          gdouble tmp = *p++ - *c++;
-          GSL_GCC_PREFETCH (p);
-          GSL_GCC_PREFETCH (c);
-          score += tmp * tmp;
-        }
-      if (score > max_score)
-        return score;
-    }
-  le = ls + rl;
-  p = ls;
-  while (p < le)
-    {
-      gdouble tmp = *p++ - *c++;
-      GSL_GCC_PREFETCH (p);
-      GSL_GCC_PREFETCH (c);
-      score += tmp * tmp;
-    }
-  return score;
-}
-
-static gdouble
-score_tailloop (GslDataHandle *dhandle,
-                const gfloat  *cs, /* compare area start */
-                GslLong        cl, /* compare area length */
-                GslLong        ll, /* loop length */
-                gdouble        max_score)
-{
-  GslLong nl = cl / ll;         /* number of full loop comparisons */
-  GslLong rl = cl - nl * ll;    /* fraction of last comparison */
-  const gfloat *ls = cs + cl;   /* loop start */
-  const gfloat *le = ls + ll;
-  const gfloat *p, *c = cs;
-  gdouble score = 0;
-
-  /* compute score for loop repeated over comparison area
-   * .......|-------------------------|##########|......
-   *        c                         ls         le
-   */
-
-  p = ls + ll - rl;
-  while (p < le)
-    {
-      gdouble tmp = *p++ - *c++;
-      GSL_GCC_PREFETCH (p);
-      GSL_GCC_PREFETCH (c);
-      score += tmp * tmp;
-    }
-  while (nl--)
-    {
-      if (score > max_score)
-        return score;
-      p = ls;
-      while (p < le)
-        {
-          gdouble tmp = *p++ - *c++;
-          GSL_GCC_PREFETCH (p);
-          GSL_GCC_PREFETCH (c);
-          score += tmp * tmp;
-        }
-    }
-  return score;
-}
-
-gboolean
-gsl_data_find_loop5 (GslDataHandle     *dhandle,
-                     GslDataLoopConfig *config,
-                     gpointer           pdata,
-                     GslProgressFunc    pfunc)
-{
-  GslLong bstart, blength, min_llength, max_llength, i, dhandle_n_values, clength, pcount, score_pcount = 0;
-  GslLong frame = 441; // FIXME: need assertion for frame vs. block length
-  GslProgressState pstate = gsl_progress_state (pdata, pfunc, 1);
-  GslDataPeekBuffer pbuf = { +1, };
-  gdouble pdist, fcenter, bfrac;
-  guint apoints;
-  gfloat *block;
-  gboolean found_loop = FALSE;
-
-  g_return_val_if_fail (dhandle != NULL, FALSE);
-  g_return_val_if_fail (config != NULL, FALSE);
-  g_return_val_if_fail (frame <= config->block_start, FALSE);
-  config->n_details = 0;
-
-  /* check out data handle */
-  if (gsl_data_handle_open (dhandle) != BSE_ERROR_NONE)
-    return FALSE;
-  dhandle_n_values = gsl_data_handle_n_values (dhandle);
-
-  /* confine parameters */
-  bstart = CLAMP (config->block_start, 0, dhandle_n_values - 1);
-  if (config->block_length < 0)
-    blength = dhandle_n_values - bstart - frame;
-  else
-    blength = MIN (dhandle_n_values - bstart - frame, config->block_length);
-  if (blength < 4)
-    return FALSE;
-
-  /* determine boundaries */
-  max_llength = blength / CLAMP (config->repetitions, 2, blength);
-  min_llength = MAX (frame + 1, config->min_loop);
-  if (min_llength > max_llength)
-    return FALSE;
-  /* determine frame distances */
-  apoints = CLAMP (config->analysis_points, 1, blength / 2 - 1);
-  bfrac = blength / (apoints + 1.0);
-  /* length of comparison area */
-  clength = blength / 2;
-
-  /* provide fully cached area for comparisons */
-  block = g_new (gfloat, dhandle_n_values);
-  for (i = 0; i < dhandle_n_values; i++)
-    block[i] = gsl_data_handle_peek_value (dhandle, i, &pbuf);
-
-  /* upper boundary for amount of comparisons */
-  pdist = apoints * (max_llength + 1 - min_llength);
-  pcount = 0;
-  config->score = G_MAXDOUBLE;
-
-  /* loop over the centers of all loops */
-  for (fcenter = bstart + bfrac; fcenter + 1.0 < bstart + blength; fcenter += bfrac)
-    {
-      GslLong ipp; // llength;
-      /* loop over all loop lengths */
-      // for (llength = min_llength; llength <= max_llength; llength++)
-      for (ipp = min_llength; ipp <= max_llength - min_llength; ipp++)
-        {
-          /* for better load balancing, we do simple size alterations and don't loop
-           * from min to max loop length but loop from both ends in turn (ipp -> llength)
-           */
-          GslLong llength = ipp & 1 ? max_llength - ipp / 2 : min_llength + ipp / 2;
-          GslLong hstart, hlength, tstart, tlength;
-          /* determine loop center as 0-relative position */
-          GslLong lstart = fcenter - 0.5;
-          /* offset loop around center */
-          lstart -= (llength - 1) >> 1;
-          /* update progress (inner loop) counter */
-          pcount++;
-          /* confine to block boundaries */
-          if (lstart < bstart || lstart + llength > bstart + blength)
-            continue;
-          /* center head/tail comparison areas around loop */
-          hstart = lstart - clength / 2;
-          hlength = clength / 2;
-          tstart = lstart + llength;
-          tlength = clength / 2;
-          /* shift head/tail if either exceeds boundaries */
-          if (hstart < bstart)
-            {
-              GslLong diff = bstart - hstart;
-              hstart += diff;
-              hlength -= diff;
-              tlength += diff;
-            }
-          else if (tstart + tlength > bstart + blength)
-            {
-              GslLong diff = tstart + tlength - bstart - blength;
-              tlength -= diff;
-              hstart -= diff;
-              hlength += diff;
-            }
-          /* accumulate score */
-         double score = 0, score1 = 0, score2 = 0;
-          double human_size = 1e6;
-          double score1_weight = human_size / (2.0 * frame);
-          double score2_weight = human_size / (1.0 * tlength + hlength);
-
-         /* compute proximity score */
-         score1  = score_tailloop (dhandle, block + lstart - frame, frame, llength,
-                                   (config->score - score) / score1_weight);
-         score   = score1 * score1_weight + score2 * score2_weight;
-          score1 += score_headloop (dhandle, block + lstart, llength, frame,
-                                   (config->score - score) / score1_weight);
-         score   = score1 * score1_weight + score2 * score2_weight;
-         /* loop comparision score */
-          score2  = score_headloop (dhandle, block + lstart, llength, tlength,
-                                   (config->score - score) / score2_weight);
-         score   = score1 * score1_weight + score2 * score2_weight;
-          score2 += score_tailloop (dhandle, block + hstart, hlength, llength,
-                                   (config->score - score) / score2_weight);
-         score   = score1 * score1_weight + score2 * score2_weight;
-
-          /* apply score */
-          if (score < config->score)
-            {
-              config->loop_start = lstart;
-              config->loop_length = llength;
-              config->score = score;
-             config->n_details = 2;
-             config->detail_names[0] = "score1 (proximity score)";
-             config->detail_names[1] = "score2 (loop comparision)";
-             config->detail_scores[0] = score1 * score1_weight;
-             config->detail_scores[1] = score2 * score2_weight;
-              score_pcount = pcount;
-              found_loop = TRUE;
-            }
-          gsl_progress_notify (&pstate, pcount * 100.0 / pdist, "score:%+g len:%ld (pos:%6lu len=%ld)",
-                               config->score, config->loop_length, lstart, llength);
-        }
-    }
-  gsl_progress_wipe (&pstate);
-  g_printerr ("  LOOP: %6llu - %6llu [%6llu] (block: %6llu - %6llu [%6llu]) (score:%+g at:%5.1f%%)\n",
-              config->loop_start, config->loop_start + config->loop_length, config->loop_length,
-              bstart, bstart + blength, blength,
-              config->score, score_pcount * 100.0 / pdist);
-
-  /* cleanups */
-  g_free (block);
-  gsl_data_handle_close (dhandle);
-
-  return found_loop;
-}
-
-gboolean
-gsl_data_find_loop4 (GslDataHandle     *dhandle,
-                     GslDataLoopConfig *config,
-                     gpointer           pdata,
-                     GslProgressFunc    pfunc)
-{
-  GslLong bstart, blength, min_llength, max_llength, i, dhandle_n_values, clength, llength, pcount, score_pcount = 0;
-  GslProgressState pstate = gsl_progress_state (pdata, pfunc, 1);
-  GslDataPeekBuffer pbuf = { +1, };
-  gdouble pdist, fcenter, bfrac;
-  gfloat *bstart_block;
-  const gfloat *block;
-  gboolean found_loop = FALSE;
-
-  g_return_val_if_fail (dhandle != NULL, FALSE);
-  g_return_val_if_fail (config != NULL, FALSE);
-  config->n_details = 0;
-
-  /* check out data handle */
-  if (gsl_data_handle_open (dhandle) != BSE_ERROR_NONE)
-    return FALSE;
-  dhandle_n_values = gsl_data_handle_n_values (dhandle);
-
-  /* confine parameters */
-  bstart = CLAMP (config->block_start, 0, dhandle_n_values - 1);
-  if (config->block_length < 0)
-    blength = dhandle_n_values - bstart;
-  else
-    blength = MIN (dhandle_n_values - bstart, config->block_length);
-  if (blength < 4)
-    return FALSE;
-
-  /* determine boundaries */
-  max_llength = blength / CLAMP (config->repetitions, 2, blength);
-  min_llength = MAX (1, config->min_loop);
-  if (min_llength > max_llength)
-    return FALSE;
-  /* determine frame distances */
-  bfrac = blength / (CLAMP (config->analysis_points, 1, blength / 2 - 1) + 1.0);
-  /* length of comparison area */
-  clength = blength / 2;
-
-  /* provide fully cached area for comparisons */
-  bstart_block = g_new (gfloat, blength);
-  for (i = 0; i < blength; i++)
-    bstart_block[i] = gsl_data_handle_peek_value (dhandle, bstart + i, &pbuf);
-  block = bstart_block - bstart;
-
-  /* upper boundary for amount of comparisons */
-  pdist = (blength / bfrac + 0.5) * (max_llength + 1 - min_llength);
-  pcount = 0;
-  config->score = G_MAXDOUBLE;
-
-  /* loop over the centers of all loops */
-  for (fcenter = bstart + bfrac; fcenter + 1.0 < bstart + blength; fcenter += bfrac)
-    /* loop over all loop lengths */
-    for (llength = min_llength; llength <= max_llength; llength += 1)
-      {
-        GslLong hstart, hlength, tstart, tlength;
-        gdouble score = 0;
-        /* determine loop center as 0-relative position */
-        GslLong lstart = fcenter - 0.5;
-        /* offset loop around center */
-        lstart -= (llength - 1) >> 1;
-        /* update progress (inner loop) counter */
-        pcount++;
-        /* confine to block boundaries */
-        if (lstart < bstart || lstart + llength > bstart + blength)
-          break;
-        /* center head/tail comparison areas around loop */
-        hstart = lstart - clength / 2;
-        hlength = clength / 2;
-        tstart = lstart + llength;
-        tlength = clength / 2;
-        /* shift head/tail if either exceeds boundaries */
-        if (hstart < bstart)
-          {
-            GslLong diff = bstart - hstart;
-            hstart += diff;
-            hlength -= diff;
-            tlength += diff;
-          }
-        else if (tstart + tlength > bstart + blength)
-          {
-            GslLong diff = tstart + tlength - bstart - blength;
-            tlength -= diff;
-            hstart -= diff;
-            hlength += diff;
-          }
-        /* accumulate score */
-        score += score_headloop (dhandle, block + lstart, llength, tlength, config->score);
-        score += score_tailloop (dhandle, block + hstart, hlength, llength, config->score - score);
-        /* apply score */
-        if (score < config->score)
-          {
-            config->loop_start = lstart;
-            config->loop_length = llength;
-            config->score = score;
-            score_pcount = pcount;
-            found_loop = TRUE;
-          }
-        gsl_progress_notify (&pstate, pcount * 100.0 / pdist, "score:%+g len:%ld (pos:%6lu len=%ld)",
-                             config->score, config->loop_length, lstart, llength);
-      }
-  gsl_progress_wipe (&pstate);
-  g_printerr ("  LOOP: %6llu - %6llu [%6llu] (block: %6llu - %6llu [%6llu]) (score:%+g at:%5.1f%%)\n",
-              config->loop_start, config->loop_start + config->loop_length, config->loop_length,
-              bstart, bstart + blength, blength,
-              config->score, score_pcount * 100.0 / pdist);
-
-  /* cleanups */
-  g_free (bstart_block);
-  gsl_data_handle_close (dhandle);
-
-  return found_loop;
-}
-
-gboolean
-gsl_data_find_loop3 (GslDataHandle     *dhandle,
-                     GslDataLoopConfig *config,
-                     gpointer           pdata,
-                     GslProgressFunc    pfunc)
-{
-  GslProgressState pstate = gsl_progress_state (pdata, pfunc, 4);
-  GslDataPeekBuffer pbuf = { +1, };
-  GslLong minll, maxll, i, dhandle_n_values, pcount;
-  gfloat *sp, *ep, *cstart, *block;
-  gdouble pdist;
-  gboolean found_loop = FALSE;
-
-  g_return_val_if_fail (dhandle != NULL, FALSE);
-  g_return_val_if_fail (config != NULL, FALSE);
-  config->n_details = 0;
-
-  if (gsl_data_handle_open (dhandle) != BSE_ERROR_NONE)
-    return FALSE;
-  dhandle_n_values = gsl_data_handle_n_values (dhandle);
-  config->block_start = CLAMP (config->block_start, 0, dhandle_n_values - 1);
-  if (config->block_length < 0)
-    config->block_length = dhandle_n_values - config->block_start;
-  else
-    config->block_length = MIN (config->block_length, dhandle_n_values - config->block_start);
-  if (config->block_length < 2)
-    return FALSE;
-  if (config->repetitions != CLAMP (config->repetitions, 2, config->block_length))
-    return FALSE;
-  /* current implementation supports just repetitions == 2 */
-  g_return_val_if_fail (config->repetitions == 2, FALSE);
-
-  /* provide fully cached area for comparisons */
-  block = g_new (gfloat, config->block_length);
-  for (i = 0; i < config->block_length; i++)
-    block[i] = gsl_data_handle_peek_value (dhandle, config->block_start + i, &pbuf);
-
-  /* test every possible loop size at every possible position */
-  maxll = config->block_length / 2;
-  minll = maxll * 0.91;
-  g_return_val_if_fail (maxll > minll, FALSE); // FIXME
-  cstart = block + config->block_length / 2;
-  config->score = G_MAXDOUBLE;
-  pcount = 0, pdist = (maxll * 1.0 - minll * 1.0 + 2.0) * (maxll * 1.0 - minll * 1.0 + 1.0) / 2.;
-  g_printerr ("pdist: %f\n", pdist);
-  for (sp = block; sp < cstart - minll; sp++)
-    for (ep = sp + minll; ep < cstart; ep++)
-      {
-        gdouble score = score_headloop (dhandle, sp, ep - sp, config->block_length / 2, config->score);
-        if (score <= config->score)
-          {
-            config->loop_start = sp - block;
-            config->loop_length = ep - sp;
-            config->score = score;
-            found_loop = TRUE;
-          }
-        gsl_progress_notify (&pstate, ++pcount * 100.0 / pdist, "score:%+g (pos:%6lu)", config->score, sp - block);
-        // g_printerr ("processed: %7.3f (score:%+g)            \r", ++pcount * 100.0 / pdist, config->score);
-      }
-
-  g_free (block);
-  gsl_data_handle_close (dhandle);
-  return found_loop;
-}
-
-gboolean
-gsl_data_find_loop2 (GslDataHandle     *dhandle,
-                     GslDataLoopConfig *config,
-                     gpointer           pdata,
-                     GslProgressFunc    pfunc)
-{
-  GslProgressState pstate = gsl_progress_state (pdata, pfunc, 4);
-  GslDataPeekBuffer pbuf = { +1, };
-  GslLong minll, maxll, i, dhandle_n_values, pcount, ll;
-  gfloat *sp, *ep, *cstart, *block;
-  gdouble pdist;
-  gboolean found_loop = FALSE;
-
-  g_return_val_if_fail (dhandle != NULL, FALSE);
-  g_return_val_if_fail (config != NULL, FALSE);
-  config->n_details = 0;
-
-  if (gsl_data_handle_open (dhandle) != BSE_ERROR_NONE)
-    return FALSE;
-  dhandle_n_values = gsl_data_handle_n_values (dhandle);
-  config->block_start = CLAMP (config->block_start, 0, dhandle_n_values - 1);
-  if (config->block_length < 0)
-    config->block_length = dhandle_n_values - config->block_start;
-  else
-    config->block_length = MIN (config->block_length, dhandle_n_values - config->block_start);
-  if (config->block_length < 2)
-    return FALSE;
-  if (config->repetitions != CLAMP (config->repetitions, 2, config->block_length))
-    return FALSE;
-  /* current implementation supports just repetitions == 2 */
-  g_return_val_if_fail (config->repetitions == 2, FALSE);
-
-  /* provide fully cached area for comparisons */
-  block = g_new (gfloat, config->block_length);
-  for (i = 0; i < config->block_length; i++)
-    block[i] = gsl_data_handle_peek_value (dhandle, config->block_start + i, &pbuf);
-
-  goto print_sizes;
-
-  /* find best loop size at one position */
-  maxll = config->block_length / 2;
-  minll = 1;
-  g_return_val_if_fail (maxll > minll, FALSE); // FIXME
-  cstart = block + config->block_length / 2;
-  config->score = G_MAXDOUBLE;
-  pcount = 0, pdist = (maxll * 1.0 - minll * 1.0 + 2.0) * (maxll * 1.0 - minll * 1.0 + 1.0) / 2.;
-  sp = block;
-  for (ep = sp + minll; ep < cstart; ep++)
-    {
-      gdouble score = score_headloop (dhandle, sp, ep - sp, config->block_length / 2, config->score);
-      if (score <= config->score)
-        {
-          config->loop_start = sp - block;
-          config->loop_length = ep - sp;
-          config->score = score;
-          found_loop = TRUE;
-        }
-      gsl_progress_notify (&pstate, ++pcount * 100.0 / pdist, "score:%+g (pos:%6lu)", config->score, sp - block);
-    }
-  gsl_progress_wipe (&pstate);
-  ll = config->loop_length;
-  g_printerr ("loop size: %llu\n", ll);
-
-  /* test every possible position */
-  minll = ll;
-  maxll = ll + 1;
-  g_return_val_if_fail (maxll > minll, FALSE); // FIXME
-  cstart = block + config->block_length / 2;
-  config->score = G_MAXDOUBLE;
-  pcount = 0, pdist = (maxll * 1.0 - minll * 1.0 + 2.0) * (maxll * 1.0 - minll * 1.0 + 1.0) / 2.;
-  g_printerr ("pdist: %f\n", pdist);
-  for (sp = block; sp < cstart - minll; sp++)
-    {
-      ep = sp + minll;
-      {
-        gdouble score = score_headloop (dhandle, sp, ep - sp, config->block_length / 2, G_MAXDOUBLE);
-        g_print ("%u %.17g\n", sp - block, score);
-        continue;
-        if (score <= config->score)
-          {
-            config->loop_start = sp - block;
-            config->loop_length = ep - sp;
-            config->score = score;
-            found_loop = TRUE;
-          }
-        gsl_progress_notify (&pstate, ++pcount * 100.0 / pdist, "score:%+g (pos:%6lu)", config->score, sp - block);
-      }
-    }
-
-  G_BREAKPOINT ();
- print_sizes:
-
-  /* test every possible loop size */
-  maxll = config->block_length / 2;
-  minll = 1;
-  g_return_val_if_fail (maxll > minll, FALSE); // FIXME
-  cstart = block + config->block_length / 2;
-  config->score = G_MAXDOUBLE;
-  pcount = 0, pdist = (maxll * 1.0 - minll * 1.0 + 2.0) * (maxll * 1.0 - minll * 1.0 + 1.0) / 2.;
-  g_printerr ("pdist: %f\n", pdist);
-  for (sp = block + 99999; sp < cstart - minll; sp++)
-    {
-      for (ep = sp + minll; ep < cstart; ep++)
-        {
-          gdouble score = score_headloop (dhandle, sp, ep - sp, config->block_length / 2, config->score);
-          g_print ("%u %.17g\n", ep - sp, score);
-          continue;
-          if (score <= config->score)
-            {
-              config->loop_start = sp - block;
-              config->loop_length = ep - sp;
-              config->score = score;
-              found_loop = TRUE;
-            }
-          gsl_progress_notify (&pstate, ++pcount * 100.0 / pdist, "score:%+g (pos:%6lu)", config->score, sp - block);
-        }
-      G_BREAKPOINT ();
-    }
-
-  g_free (block);
-  gsl_data_handle_close (dhandle);
-  return found_loop;
-}
-
-static inline gdouble
-dcache_headloop_score (GslDataCache     *dcache,
-                       GslLong           lstart,        /* loop start (reaches till bstart) */
-                       GslLong           cstart,        /* compare area start sample */
-                       GslLong           clength,       /* compare area length */
-                       gdouble           max_score,     /* no need to score beyond this */
-                       GslDataCacheNode **lnp,
-                       GslDataCacheNode **cnp,
-                       gboolean           weighted)
-{
-  gsize node_size = GSL_DATA_CACHE_NODE_SIZE (dcache);
-  GslDataCacheNode *lnode = *lnp, *cnode = *cnp;
-  gdouble wmax = clength, score = 0.0;
-  GslLong i = 0, loop_length = cstart - lstart;
-
-  /* compute score for loop repeated over comparison area
-   * .......|##########|-------------------------|......
-   *    loopstart   cstart                (cstart+clength)
-   */
-
-  while (i < clength)
-    {
-      GslLong cdiff, ldiff, clen, llen, k, l = i % loop_length;
-      gfloat *cb, *lb; /* base pointer */
-      if (lnode->offset > lstart + l || lstart + l >= lnode->offset + node_size)
-       {
-         gsl_data_cache_unref_node (dcache, lnode);
-         lnode = *lnp = gsl_data_cache_ref_node (dcache, lstart + l, TRUE);
-       }
-      if (cnode->offset > cstart + i || cstart + i >= cnode->offset + node_size)
-       {
-         gsl_data_cache_unref_node (dcache, cnode);
-         cnode = *cnp = gsl_data_cache_ref_node (dcache, cstart + i, TRUE);
-       }
-      cdiff = cstart + i - cnode->offset;
-      ldiff = lstart + l - lnode->offset;
-      clen = MIN (node_size - cdiff, clength - i);
-      llen = MIN (node_size - ldiff, loop_length - l);
-      cb = cnode->data + cdiff - i;
-      lb = lnode->data + ldiff - i;
-      if (weighted)
-        for (k = i + MIN (llen, clen); i < k; i++)
-          {
-            gdouble ed = lb[i] - cb[i];
-            score += ed * ed * (1.0 - i / wmax);
-          }
-      else
-        for (k = i + MIN (llen, clen); i < k; i++)
-          {
-            gdouble ed = lb[i] - cb[i];
-            score += ed * ed;
-          }
-      if (score > max_score)
-        break;
-    }
-
-  return score;
-}
-
-gboolean
-gsl_data_find_loop1 (GslDataHandle    *dhandle,
-                    GslDataLoopConfig *config,
-                    gpointer           pdata,
-                    GslProgressFunc    pfunc)
-{
-  GslProgressState pstate = gsl_progress_state (pdata, pfunc, 1);
-  guint frame = 4410;   // FIXME: adjustable value?
-  GslLong cstart, clength, j, dhandle_n_values, ls, ll, minll, pcount, maxll, min_loop = frame;
-  GslDataCache *dcache;
-  GslDataCacheNode *dnode1, *dnode2;
-  gdouble pdist;
-  gboolean found_loop = FALSE;
-
-  g_return_val_if_fail (dhandle != NULL, FALSE);
-  g_return_val_if_fail (config != NULL, FALSE);
-  config->n_details = 0;
-
-  if (gsl_data_handle_open (dhandle) != BSE_ERROR_NONE)
-    return FALSE;
-  dhandle_n_values = gsl_data_handle_n_values (dhandle);
-  config->block_start = CLAMP (config->block_start, 0, dhandle_n_values - 1);
-  if (config->block_length < 0)
-    config->block_length = dhandle_n_values - config->block_start;
-  else
-    config->block_length = MIN (config->block_length, dhandle_n_values - config->block_start);
-  if (config->block_length < 2)
-    return FALSE;
-  if (config->repetitions != CLAMP (config->repetitions, 2, config->block_length))
-    return FALSE;
-
-  dcache = gsl_data_cache_new (dhandle, 1);
-  gsl_data_cache_open (dcache);
-  gsl_data_handle_close (dhandle);
-  gsl_data_cache_unref (dcache);
-
-  dnode1 = gsl_data_cache_ref_node (dcache, config->block_start, TRUE);
-  dnode2 = gsl_data_cache_ref_node (dcache, config->block_start, TRUE);
-
-  /* widen loop, keeping it end-aligned to cstart
-   *      |------------##########|--------------------|......
-   * block_start   loopstart   cstart          (cstart+clength)
-   */
-
-  /* find a good loop length */
-  config->score = G_MAXDOUBLE;
-  cstart = config->block_start + config->block_length / config->repetitions;
-  clength = config->block_length - (cstart - config->block_start);
-  pcount = 0, pdist = (cstart - min_loop - config->block_start);
-  for (j = config->block_start; j < cstart - min_loop; j++)
-    {
-      gdouble score = dcache_headloop_score (dcache, j, cstart, clength, config->score, &dnode1, &dnode2, FALSE);
-      if (score <= config->score)
-        {
-          config->loop_start = j;
-          config->loop_length = cstart - j;
-          config->score = score;
-          found_loop = TRUE;
-        }
-      if (pcount++ % 16 == 0)
-        gsl_progress_notify (&pstate, pcount * 100.0 / pdist, "score:%+g", config->score);
-    }
-  gsl_progress_wipe (&pstate);
-  if (!found_loop)
-    goto seek_loop_done;
-  g_printerr ("  lLOOP: %6llu - %6llu [%6llu] (block: %6llu - %6llu [%6llu]) (score:%+g)\n",
-              config->loop_start, config->loop_start + config->loop_length, config->loop_length,
-              config->block_start, config->block_start + config->block_length, config->block_length,
-              config->score);
-  
-  /* find best loop position */
-  ll = config->loop_length;
-  minll = ll;
-  maxll = ll + 1;
-  config->score = G_MAXDOUBLE;
-  pcount = 0, pdist = (config->block_length - maxll - frame) * (maxll - minll);
-  for (ls = config->block_start; ls + maxll + frame <= config->block_start + config->block_length; ls++)
-    {
-      /* find best loop length */
-      for (ll = minll; ll < maxll; ll++)
-        {
-          gdouble score = dcache_headloop_score (dcache, ls, ls + ll, frame, config->score, &dnode1, &dnode2, TRUE);
-          if (score <= config->score)
-            {
-              config->loop_start = ls;
-              config->loop_length = ll;
-              config->score = score;
-              found_loop = TRUE;
-            }
-          gsl_progress_notify (&pstate, ++pcount * 100.0 / pdist, "weighted-frame-score:%+g", config->score);
-        }
-    }
-  gsl_progress_wipe (&pstate);
-  g_printerr ("  pLOOP: %6llu - %6llu [%6llu] (block: %6llu - %6llu [%6llu]) (weighted-frame-score:%+g)\n",
-              config->loop_start, config->loop_start + config->loop_length, config->loop_length,
-              config->block_start, config->block_start + config->block_length, config->block_length,
-              config->score);
-
-  /* find best loop length */
-  frame = MIN (config->loop_length / 5, 4410 * 7);
-  while (frame >= 32) // FIXME
-    {
-      /* jitter loop length */
-      minll = config->loop_length - frame;
-      minll = MAX (minll, 1);
-      maxll = config->loop_length + frame;
-      maxll = MIN (config->block_start + config->block_length - frame,
-                   config->loop_start + maxll) -
-              config->loop_start;
-
-      /* find best loop length */
-      config->score = G_MAXDOUBLE;
-      ls = config->loop_start;
-      pcount = 0, pdist = maxll - minll;
-      for (ll = minll; ll < maxll; ll++)
-        {
-          gdouble score = dcache_headloop_score (dcache, ls, ls + ll, frame, config->score, &dnode1, &dnode2, FALSE);
-          if (score <= config->score)
-            {
-              config->loop_start = ls;
-              config->loop_length = ll;
-              config->score = score;
-              found_loop = TRUE;
-            }
-          gsl_progress_notify (&pstate, ++pcount * 100.0 / pdist, "frame-score:%+g", config->score);
-        }
-      gsl_progress_wipe (&pstate);
-      g_printerr ("  sLOOP: %6llu - %6llu [%6llu] (block: %6llu - %6llu [%6llu]) (frame[%d]-score:%+g)\n",
-                  config->loop_start, config->loop_start + config->loop_length, config->loop_length,
-                  config->block_start, config->block_start + config->block_length, config->block_length,
-                  frame, config->score);
-      frame /= 2;
-    }
-
- seek_loop_done:
-
-  gsl_data_cache_unref_node (dcache, dnode1);
-  gsl_data_cache_unref_node (dcache, dnode2);
-
-  gsl_data_cache_close (dcache);
-  return found_loop;
-}
-
-static inline float
-tailloop_score (GslDataCache          *dcache,
-               const GslDataTailLoop *cfg,
-               GslLong                loopstart,
-               GslLong                loopsize,
-               gfloat                 worstscore)
-{
-  gsize node_size = GSL_DATA_CACHE_NODE_SIZE (dcache);
-  GslLong looppos, i, compare = cfg->pre_loop_compare;
-  gfloat score = 0.0;
-  GslDataCacheNode *snode, *lnode;
-
-  /* compute score for loopsize with compare samples before loop */
-  /* -----|-------------------------|-----------------
-   *  loopstart-compare          loopstart
-   */
-
-  looppos = loopstart - compare;
-  while (looppos < loopstart)
-    looppos += loopsize;
-
-  snode = gsl_data_cache_ref_node (dcache, loopstart - compare, TRUE);
-  lnode = gsl_data_cache_ref_node (dcache, looppos, TRUE);
-  for (i = loopstart - compare; i < loopstart;)
-    {
-      GslLong sdiff, ldiff, slen, llen, loop_len, compare_len, j;
-      gfloat *sb, *lb;
-
-      if (snode->offset > i || i >= snode->offset + node_size)
-       {
-         gsl_data_cache_unref_node (dcache, snode);
-         snode = gsl_data_cache_ref_node (dcache, i, TRUE);
-       }
-      if (lnode->offset > looppos || looppos >= lnode->offset + node_size)
-       {
-         gsl_data_cache_unref_node (dcache, lnode);
-         lnode = gsl_data_cache_ref_node (dcache, looppos, TRUE);
-       }
-      sdiff = i - snode->offset;
-      ldiff = looppos - lnode->offset;
-      slen = node_size - sdiff;
-      llen = node_size - ldiff;
-      sb = snode->data + sdiff;
-      lb = lnode->data + ldiff;
-      compare_len = loopstart - i;
-      loop_len = loopsize - (looppos - loopstart);
-      
-      slen = MIN (slen, compare_len);
-      llen = MIN (llen, loop_len);
-      slen = MIN (slen, llen);
-      
-      j = slen;
-      if (cfg->cmp_strategy == GSL_DATA_TAIL_LOOP_CMP_CORRELATION)
-       {
-         while (j--)
-           score += sb[j] * lb[j];
-       }
-      else
-       {
-         while (j--)
-           {
-             gfloat tmp = lb[j] - sb[j];
-             score += tmp * tmp;
-           }
-         if (score > worstscore)
-           break;
-       }
-      
-      i += slen;
-      looppos += slen;
-      if (looppos >= loopstart + loopsize)
-       looppos -= loopsize;
-    }
-  gsl_data_cache_unref_node (dcache, snode);
-  gsl_data_cache_unref_node (dcache, lnode);
-
-  return score;
-}
-
-gdouble
-gsl_data_find_loop0 (GslDataHandle         *dhandle,
-                     const GslDataTailLoop *cfg,
-                     GslLong               *loop_start_p,
-                     GslLong               *loop_end_p)
-{
-  GslDataCache *dcache;
-  GslLong perc_bound, cfg_max_loop, dhandle_n_values;
-  GslLong perc_count = 0, perc_val = 0;
-  GslLong loopsize, bestloopsize = 0;
-  gdouble bestscore;
-
-  g_return_val_if_fail (dhandle != NULL, 0);
-  g_return_val_if_fail (cfg != NULL, 0);
-  g_return_val_if_fail (loop_start_p != NULL, 0);
-  g_return_val_if_fail (loop_end_p != NULL, 0);
-  g_return_val_if_fail (cfg->min_loop >= 1, 0);
-
-  if (gsl_data_handle_open (dhandle) != BSE_ERROR_NONE)
-    return 0;
-  dhandle_n_values = gsl_data_handle_n_values (dhandle);
-
-  g_return_val_if_fail (cfg->pre_loop_compare < dhandle_n_values - 1, 0);
-  cfg_max_loop = cfg->max_loop < 0 ? dhandle_n_values - 1 - cfg->pre_loop_compare : cfg->max_loop;
-  g_return_val_if_fail (cfg_max_loop >= cfg->min_loop, 0);
-  g_return_val_if_fail (cfg->pre_loop_compare + cfg_max_loop < dhandle_n_values, 0);
-  g_return_val_if_fail (cfg->cmp_strategy == GSL_DATA_TAIL_LOOP_CMP_LEAST_SQUARE ||
-                       cfg->cmp_strategy == GSL_DATA_TAIL_LOOP_CMP_CORRELATION, 0);
-
-  dcache = gsl_data_cache_new (dhandle, 1);
-  gsl_data_cache_open (dcache);
-  gsl_data_handle_close (dhandle);
-  gsl_data_cache_unref (dcache);
-
-  perc_bound = (cfg_max_loop - cfg->min_loop) / 100.0;
-
-  /* we try to maximize correlation, but to minimize the error */
-  if (cfg->cmp_strategy == GSL_DATA_TAIL_LOOP_CMP_CORRELATION)
-    bestscore = 0.0;
-  else
-    bestscore = 1e+14;
-
-  for (loopsize = cfg->min_loop; loopsize < cfg_max_loop; loopsize++)
-    {
-      gdouble score = tailloop_score (dcache, cfg,
-                                     dhandle_n_values - loopsize, loopsize,
-                                     bestscore);
-
-      /* we try to maximize correlation, but to minimize the error */
-      if ((cfg->cmp_strategy == GSL_DATA_TAIL_LOOP_CMP_CORRELATION && score > bestscore) ||
-         (cfg->cmp_strategy == GSL_DATA_TAIL_LOOP_CMP_LEAST_SQUARE && score < bestscore))
-       {
-         bestloopsize = loopsize;
-         bestscore = score;
-       }
-      if (++perc_count >= perc_bound)
-       {
-         perc_count = 0;
-         perc_val++;
-         g_printerr ("processed %llu%%       \r", perc_val);
-       }
-    }
-  g_printerr ("\nbest match (%s): len in samples=%lld, len=%lld, score=%f\n",
-             (cfg->cmp_strategy == GSL_DATA_TAIL_LOOP_CMP_CORRELATION) ? "correlation" : "least squares",
-             bestloopsize, bestloopsize, bestscore);
-
-  *loop_start_p = dhandle_n_values - bestloopsize;
-  *loop_end_p = dhandle_n_values - 1;
-
-  gsl_data_cache_close (dcache);
-
-  /* FIXME: statistics: scan for other extreme points in
-   *                    score[cfg->min_loop..cfg_max_loop]
-   */
-
-  return bestscore;
-}
diff --git a/tools/bseloopfuncs.cc b/tools/bseloopfuncs.cc
new file mode 100644 (file)
index 0000000..743297d
--- /dev/null
@@ -0,0 +1,934 @@
+/* BSE - Better Sound Engine
+ * Copyright (C) 2001, 2003 Tim Janik and Stefan Westerfeld
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * A copy of the GNU Lesser General Public License should ship along
+ * with this library; if not, see http://www.gnu.org/copyleft/.
+ */
+#include "bseloopfuncs.hh"
+#include <bse/gsldatacache.h>
+#include <string.h>
+#include <signal.h>    /* G_BREAKPOINT() */
+#include <stdio.h>
+#include <math.h>
+
+typedef struct {
+  gdouble score;
+  GslLong loop_start;
+  GslLong loop_length;
+} LoopEntry;
+typedef struct {
+  guint      max_entries;
+  guint      n_entries;
+  gdouble    worst_score;
+  LoopEntry  entries[1];        /* flexible array */
+} LoopStack;
+
+static gdouble
+score_headloop (GslDataHandle *dhandle,
+                const gfloat  *ls, /* loop start */
+                GslLong        ll, /* loop length */
+                GslLong        cl, /* compare area length */
+                gdouble        max_score)
+{
+  GslLong nl = cl / ll;         /* number of full loop comparisons */
+  GslLong rl = cl - nl * ll;    /* fraction of last comparison */
+  const gfloat *c = ls + ll;    /* compare area start */
+  const gfloat *p, *le = ls + ll;
+  gdouble score = 0;
+
+  /* compute score for loop repeated over comparison area
+   * .......|##########|-------------------------|......
+   *        ls         c                        c+cl
+   */
+
+  while (nl--)
+    {
+      p = ls;
+      while (p < le)
+        {
+          gdouble tmp = *p++ - *c++;
+          GSL_GCC_PREFETCH (p);
+          GSL_GCC_PREFETCH (c);
+          score += tmp * tmp;
+        }
+      if (score > max_score)
+        return score;
+    }
+  le = ls + rl;
+  p = ls;
+  while (p < le)
+    {
+      gdouble tmp = *p++ - *c++;
+      GSL_GCC_PREFETCH (p);
+      GSL_GCC_PREFETCH (c);
+      score += tmp * tmp;
+    }
+  return score;
+}
+
+static gdouble
+score_tailloop (GslDataHandle *dhandle,
+                const gfloat  *cs, /* compare area start */
+                GslLong        cl, /* compare area length */
+                GslLong        ll, /* loop length */
+                gdouble        max_score)
+{
+  GslLong nl = cl / ll;         /* number of full loop comparisons */
+  GslLong rl = cl - nl * ll;    /* fraction of last comparison */
+  const gfloat *ls = cs + cl;   /* loop start */
+  const gfloat *le = ls + ll;
+  const gfloat *p, *c = cs;
+  gdouble score = 0;
+
+  /* compute score for loop repeated over comparison area
+   * .......|-------------------------|##########|......
+   *        c                         ls         le
+   */
+
+  p = ls + ll - rl;
+  while (p < le)
+    {
+      gdouble tmp = *p++ - *c++;
+      GSL_GCC_PREFETCH (p);
+      GSL_GCC_PREFETCH (c);
+      score += tmp * tmp;
+    }
+  while (nl--)
+    {
+      if (score > max_score)
+        return score;
+      p = ls;
+      while (p < le)
+        {
+          gdouble tmp = *p++ - *c++;
+          GSL_GCC_PREFETCH (p);
+          GSL_GCC_PREFETCH (c);
+          score += tmp * tmp;
+        }
+    }
+  return score;
+}
+
+gboolean
+gsl_data_find_loop5 (GslDataHandle     *dhandle,
+                     GslDataLoopConfig *config,
+                     gpointer           pdata,
+                     GslProgressFunc    pfunc)
+{
+  GslLong bstart, blength, min_llength, max_llength, i, dhandle_n_values, clength, pcount, score_pcount = 0;
+  GslLong frame = 441; // FIXME: need assertion for frame vs. block length
+  GslProgressState pstate = gsl_progress_state (pdata, pfunc, 1);
+  GslDataPeekBuffer pbuf = { +1, };
+  gdouble pdist, fcenter, bfrac;
+  guint apoints;
+  gfloat *block;
+  gboolean found_loop = FALSE;
+
+  g_return_val_if_fail (dhandle != NULL, FALSE);
+  g_return_val_if_fail (config != NULL, FALSE);
+  g_return_val_if_fail (frame <= config->block_start, FALSE);
+  config->n_details = 0;
+
+  /* check out data handle */
+  if (gsl_data_handle_open (dhandle) != BSE_ERROR_NONE)
+    return FALSE;
+  dhandle_n_values = gsl_data_handle_n_values (dhandle);
+
+  /* confine parameters */
+  bstart = CLAMP (config->block_start, 0, dhandle_n_values - 1);
+  if (config->block_length < 0)
+    blength = dhandle_n_values - bstart - frame;
+  else
+    blength = MIN (dhandle_n_values - bstart - frame, config->block_length);
+  if (blength < 4)
+    return FALSE;
+
+  /* determine boundaries */
+  max_llength = blength / CLAMP (config->repetitions, 2, blength);
+  min_llength = MAX (frame + 1, config->min_loop);
+  if (min_llength > max_llength)
+    return FALSE;
+  /* determine frame distances */
+  apoints = CLAMP (config->analysis_points, 1, blength / 2 - 1);
+  bfrac = blength / (apoints + 1.0);
+  /* length of comparison area */
+  clength = blength / 2;
+
+  /* provide fully cached area for comparisons */
+  block = g_new (gfloat, dhandle_n_values);
+  for (i = 0; i < dhandle_n_values; i++)
+    block[i] = gsl_data_handle_peek_value (dhandle, i, &pbuf);
+
+  /* upper boundary for amount of comparisons */
+  pdist = apoints * (max_llength + 1 - min_llength);
+  pcount = 0;
+  config->score = G_MAXDOUBLE;
+
+  /* loop over the centers of all loops */
+  for (fcenter = bstart + bfrac; fcenter + 1.0 < bstart + blength; fcenter += bfrac)
+    {
+      GslLong ipp; // llength;
+      /* loop over all loop lengths */
+      // for (llength = min_llength; llength <= max_llength; llength++)
+      for (ipp = min_llength; ipp <= max_llength - min_llength; ipp++)
+        {
+          /* for better load balancing, we do simple size alterations and don't loop
+           * from min to max loop length but loop from both ends in turn (ipp -> llength)
+           */
+          GslLong llength = ipp & 1 ? max_llength - ipp / 2 : min_llength + ipp / 2;
+          GslLong hstart, hlength, tstart, tlength;
+          /* determine loop center as 0-relative position */
+          GslLong lstart = fcenter - 0.5;
+          /* offset loop around center */
+          lstart -= (llength - 1) >> 1;
+          /* update progress (inner loop) counter */
+          pcount++;
+          /* confine to block boundaries */
+          if (lstart < bstart || lstart + llength > bstart + blength)
+            continue;
+          /* center head/tail comparison areas around loop */
+          hstart = lstart - clength / 2;
+          hlength = clength / 2;
+          tstart = lstart + llength;
+          tlength = clength / 2;
+          /* shift head/tail if either exceeds boundaries */
+          if (hstart < bstart)
+            {
+              GslLong diff = bstart - hstart;
+              hstart += diff;
+              hlength -= diff;
+              tlength += diff;
+            }
+          else if (tstart + tlength > bstart + blength)
+            {
+              GslLong diff = tstart + tlength - bstart - blength;
+              tlength -= diff;
+              hstart -= diff;
+              hlength += diff;
+            }
+          /* accumulate score */
+         double score = 0, score1 = 0, score2 = 0;
+          double human_size = 1e6;
+          double score1_weight = human_size / (2.0 * frame);
+          double score2_weight = human_size / (1.0 * tlength + hlength);
+
+         /* compute proximity score */
+         score1  = score_tailloop (dhandle, block + lstart - frame, frame, llength,
+                                   (config->score - score) / score1_weight);
+         score   = score1 * score1_weight + score2 * score2_weight;
+          score1 += score_headloop (dhandle, block + lstart, llength, frame,
+                                   (config->score - score) / score1_weight);
+         score   = score1 * score1_weight + score2 * score2_weight;
+         /* loop comparision score */
+          score2  = score_headloop (dhandle, block + lstart, llength, tlength,
+                                   (config->score - score) / score2_weight);
+         score   = score1 * score1_weight + score2 * score2_weight;
+          score2 += score_tailloop (dhandle, block + hstart, hlength, llength,
+                                   (config->score - score) / score2_weight);
+         score   = score1 * score1_weight + score2 * score2_weight;
+
+          /* apply score */
+          if (score < config->score)
+            {
+              config->loop_start = lstart;
+              config->loop_length = llength;
+              config->score = score;
+             config->n_details = 2;
+             config->detail_names[0] = "score1 (proximity score)";
+             config->detail_names[1] = "score2 (loop comparision)";
+             config->detail_scores[0] = score1 * score1_weight;
+             config->detail_scores[1] = score2 * score2_weight;
+              score_pcount = pcount;
+              found_loop = TRUE;
+            }
+          gsl_progress_notify (&pstate, pcount * 100.0 / pdist, "score:%+g len:%ld (pos:%6lu len=%ld)",
+                               config->score, config->loop_length, lstart, llength);
+        }
+    }
+  gsl_progress_wipe (&pstate);
+  g_printerr ("  LOOP: %6llu - %6llu [%6llu] (block: %6llu - %6llu [%6llu]) (score:%+g at:%5.1f%%)\n",
+              config->loop_start, config->loop_start + config->loop_length, config->loop_length,
+              bstart, bstart + blength, blength,
+              config->score, score_pcount * 100.0 / pdist);
+
+  /* cleanups */
+  g_free (block);
+  gsl_data_handle_close (dhandle);
+
+  return found_loop;
+}
+
+gboolean
+gsl_data_find_loop4 (GslDataHandle     *dhandle,
+                     GslDataLoopConfig *config,
+                     gpointer           pdata,
+                     GslProgressFunc    pfunc)
+{
+  GslLong bstart, blength, min_llength, max_llength, i, dhandle_n_values, clength, llength, pcount, score_pcount = 0;
+  GslProgressState pstate = gsl_progress_state (pdata, pfunc, 1);
+  GslDataPeekBuffer pbuf = { +1, };
+  gdouble pdist, fcenter, bfrac;
+  gfloat *bstart_block;
+  const gfloat *block;
+  gboolean found_loop = FALSE;
+
+  g_return_val_if_fail (dhandle != NULL, FALSE);
+  g_return_val_if_fail (config != NULL, FALSE);
+  config->n_details = 0;
+
+  /* check out data handle */
+  if (gsl_data_handle_open (dhandle) != BSE_ERROR_NONE)
+    return FALSE;
+  dhandle_n_values = gsl_data_handle_n_values (dhandle);
+
+  /* confine parameters */
+  bstart = CLAMP (config->block_start, 0, dhandle_n_values - 1);
+  if (config->block_length < 0)
+    blength = dhandle_n_values - bstart;
+  else
+    blength = MIN (dhandle_n_values - bstart, config->block_length);
+  if (blength < 4)
+    return FALSE;
+
+  /* determine boundaries */
+  max_llength = blength / CLAMP (config->repetitions, 2, blength);
+  min_llength = MAX (1, config->min_loop);
+  if (min_llength > max_llength)
+    return FALSE;
+  /* determine frame distances */
+  bfrac = blength / (CLAMP (config->analysis_points, 1, blength / 2 - 1) + 1.0);
+  /* length of comparison area */
+  clength = blength / 2;
+
+  /* provide fully cached area for comparisons */
+  bstart_block = g_new (gfloat, blength);
+  for (i = 0; i < blength; i++)
+    bstart_block[i] = gsl_data_handle_peek_value (dhandle, bstart + i, &pbuf);
+  block = bstart_block - bstart;
+
+  /* upper boundary for amount of comparisons */
+  pdist = (blength / bfrac + 0.5) * (max_llength + 1 - min_llength);
+  pcount = 0;
+  config->score = G_MAXDOUBLE;
+
+  /* loop over the centers of all loops */
+  for (fcenter = bstart + bfrac; fcenter + 1.0 < bstart + blength; fcenter += bfrac)
+    /* loop over all loop lengths */
+    for (llength = min_llength; llength <= max_llength; llength += 1)
+      {
+        GslLong hstart, hlength, tstart, tlength;
+        gdouble score = 0;
+        /* determine loop center as 0-relative position */
+        GslLong lstart = fcenter - 0.5;
+        /* offset loop around center */
+        lstart -= (llength - 1) >> 1;
+        /* update progress (inner loop) counter */
+        pcount++;
+        /* confine to block boundaries */
+        if (lstart < bstart || lstart + llength > bstart + blength)
+          break;
+        /* center head/tail comparison areas around loop */
+        hstart = lstart - clength / 2;
+        hlength = clength / 2;
+        tstart = lstart + llength;
+        tlength = clength / 2;
+        /* shift head/tail if either exceeds boundaries */
+        if (hstart < bstart)
+          {
+            GslLong diff = bstart - hstart;
+            hstart += diff;
+            hlength -= diff;
+            tlength += diff;
+          }
+        else if (tstart + tlength > bstart + blength)
+          {
+            GslLong diff = tstart + tlength - bstart - blength;
+            tlength -= diff;
+            hstart -= diff;
+            hlength += diff;
+          }
+        /* accumulate score */
+        score += score_headloop (dhandle, block + lstart, llength, tlength, config->score);
+        score += score_tailloop (dhandle, block + hstart, hlength, llength, config->score - score);
+        /* apply score */
+        if (score < config->score)
+          {
+            config->loop_start = lstart;
+            config->loop_length = llength;
+            config->score = score;
+            score_pcount = pcount;
+            found_loop = TRUE;
+          }
+        gsl_progress_notify (&pstate, pcount * 100.0 / pdist, "score:%+g len:%ld (pos:%6lu len=%ld)",
+                             config->score, config->loop_length, lstart, llength);
+      }
+  gsl_progress_wipe (&pstate);
+  g_printerr ("  LOOP: %6llu - %6llu [%6llu] (block: %6llu - %6llu [%6llu]) (score:%+g at:%5.1f%%)\n",
+              config->loop_start, config->loop_start + config->loop_length, config->loop_length,
+              bstart, bstart + blength, blength,
+              config->score, score_pcount * 100.0 / pdist);
+
+  /* cleanups */
+  g_free (bstart_block);
+  gsl_data_handle_close (dhandle);
+
+  return found_loop;
+}
+
+gboolean
+gsl_data_find_loop3 (GslDataHandle     *dhandle,
+                     GslDataLoopConfig *config,
+                     gpointer           pdata,
+                     GslProgressFunc    pfunc)
+{
+  GslProgressState pstate = gsl_progress_state (pdata, pfunc, 4);
+  GslDataPeekBuffer pbuf = { +1, };
+  GslLong minll, maxll, i, dhandle_n_values, pcount;
+  gfloat *sp, *ep, *cstart, *block;
+  gdouble pdist;
+  gboolean found_loop = FALSE;
+
+  g_return_val_if_fail (dhandle != NULL, FALSE);
+  g_return_val_if_fail (config != NULL, FALSE);
+  config->n_details = 0;
+
+  if (gsl_data_handle_open (dhandle) != BSE_ERROR_NONE)
+    return FALSE;
+  dhandle_n_values = gsl_data_handle_n_values (dhandle);
+  config->block_start = CLAMP (config->block_start, 0, dhandle_n_values - 1);
+  if (config->block_length < 0)
+    config->block_length = dhandle_n_values - config->block_start;
+  else
+    config->block_length = MIN (config->block_length, dhandle_n_values - config->block_start);
+  if (config->block_length < 2)
+    return FALSE;
+  if (config->repetitions != CLAMP (config->repetitions, 2, config->block_length))
+    return FALSE;
+  /* current implementation supports just repetitions == 2 */
+  g_return_val_if_fail (config->repetitions == 2, FALSE);
+
+  /* provide fully cached area for comparisons */
+  block = g_new (gfloat, config->block_length);
+  for (i = 0; i < config->block_length; i++)
+    block[i] = gsl_data_handle_peek_value (dhandle, config->block_start + i, &pbuf);
+
+  /* test every possible loop size at every possible position */
+  maxll = config->block_length / 2;
+  minll = maxll * 0.91;
+  g_return_val_if_fail (maxll > minll, FALSE); // FIXME
+  cstart = block + config->block_length / 2;
+  config->score = G_MAXDOUBLE;
+  pcount = 0, pdist = (maxll * 1.0 - minll * 1.0 + 2.0) * (maxll * 1.0 - minll * 1.0 + 1.0) / 2.;
+  g_printerr ("pdist: %f\n", pdist);
+  for (sp = block; sp < cstart - minll; sp++)
+    for (ep = sp + minll; ep < cstart; ep++)
+      {
+        gdouble score = score_headloop (dhandle, sp, ep - sp, config->block_length / 2, config->score);
+        if (score <= config->score)
+          {
+            config->loop_start = sp - block;
+            config->loop_length = ep - sp;
+            config->score = score;
+            found_loop = TRUE;
+          }
+        gsl_progress_notify (&pstate, ++pcount * 100.0 / pdist, "score:%+g (pos:%6lu)", config->score, sp - block);
+        // g_printerr ("processed: %7.3f (score:%+g)            \r", ++pcount * 100.0 / pdist, config->score);
+      }
+
+  g_free (block);
+  gsl_data_handle_close (dhandle);
+  return found_loop;
+}
+
+gboolean
+gsl_data_find_loop2 (GslDataHandle     *dhandle,
+                     GslDataLoopConfig *config,
+                     gpointer           pdata,
+                     GslProgressFunc    pfunc)
+{
+  GslProgressState pstate = gsl_progress_state (pdata, pfunc, 4);
+  GslDataPeekBuffer pbuf = { +1, };
+  GslLong minll, maxll, i, dhandle_n_values, pcount, ll;
+  gfloat *sp, *ep, *cstart, *block;
+  gdouble pdist;
+  gboolean found_loop = FALSE;
+
+  g_return_val_if_fail (dhandle != NULL, FALSE);
+  g_return_val_if_fail (config != NULL, FALSE);
+  config->n_details = 0;
+
+  if (gsl_data_handle_open (dhandle) != BSE_ERROR_NONE)
+    return FALSE;
+  dhandle_n_values = gsl_data_handle_n_values (dhandle);
+  config->block_start = CLAMP (config->block_start, 0, dhandle_n_values - 1);
+  if (config->block_length < 0)
+    config->block_length = dhandle_n_values - config->block_start;
+  else
+    config->block_length = MIN (config->block_length, dhandle_n_values - config->block_start);
+  if (config->block_length < 2)
+    return FALSE;
+  if (config->repetitions != CLAMP (config->repetitions, 2, config->block_length))
+    return FALSE;
+  /* current implementation supports just repetitions == 2 */
+  g_return_val_if_fail (config->repetitions == 2, FALSE);
+
+  /* provide fully cached area for comparisons */
+  block = g_new (gfloat, config->block_length);
+  for (i = 0; i < config->block_length; i++)
+    block[i] = gsl_data_handle_peek_value (dhandle, config->block_start + i, &pbuf);
+
+  goto print_sizes;
+
+  /* find best loop size at one position */
+  maxll = config->block_length / 2;
+  minll = 1;
+  g_return_val_if_fail (maxll > minll, FALSE); // FIXME
+  cstart = block + config->block_length / 2;
+  config->score = G_MAXDOUBLE;
+  pcount = 0, pdist = (maxll * 1.0 - minll * 1.0 + 2.0) * (maxll * 1.0 - minll * 1.0 + 1.0) / 2.;
+  sp = block;
+  for (ep = sp + minll; ep < cstart; ep++)
+    {
+      gdouble score = score_headloop (dhandle, sp, ep - sp, config->block_length / 2, config->score);
+      if (score <= config->score)
+        {
+          config->loop_start = sp - block;
+          config->loop_length = ep - sp;
+          config->score = score;
+          found_loop = TRUE;
+        }
+      gsl_progress_notify (&pstate, ++pcount * 100.0 / pdist, "score:%+g (pos:%6lu)", config->score, sp - block);
+    }
+  gsl_progress_wipe (&pstate);
+  ll = config->loop_length;
+  g_printerr ("loop size: %llu\n", ll);
+
+  /* test every possible position */
+  minll = ll;
+  maxll = ll + 1;
+  g_return_val_if_fail (maxll > minll, FALSE); // FIXME
+  cstart = block + config->block_length / 2;
+  config->score = G_MAXDOUBLE;
+  pcount = 0, pdist = (maxll * 1.0 - minll * 1.0 + 2.0) * (maxll * 1.0 - minll * 1.0 + 1.0) / 2.;
+  g_printerr ("pdist: %f\n", pdist);
+  for (sp = block; sp < cstart - minll; sp++)
+    {
+      ep = sp + minll;
+      {
+        gdouble score = score_headloop (dhandle, sp, ep - sp, config->block_length / 2, G_MAXDOUBLE);
+        g_print ("%u %.17g\n", sp - block, score);
+        continue;
+        if (score <= config->score)
+          {
+            config->loop_start = sp - block;
+            config->loop_length = ep - sp;
+            config->score = score;
+            found_loop = TRUE;
+          }
+        gsl_progress_notify (&pstate, ++pcount * 100.0 / pdist, "score:%+g (pos:%6lu)", config->score, sp - block);
+      }
+    }
+
+  G_BREAKPOINT ();
+ print_sizes:
+
+  /* test every possible loop size */
+  maxll = config->block_length / 2;
+  minll = 1;
+  g_return_val_if_fail (maxll > minll, FALSE); // FIXME
+  cstart = block + config->block_length / 2;
+  config->score = G_MAXDOUBLE;
+  pcount = 0, pdist = (maxll * 1.0 - minll * 1.0 + 2.0) * (maxll * 1.0 - minll * 1.0 + 1.0) / 2.;
+  g_printerr ("pdist: %f\n", pdist);
+  for (sp = block + 99999; sp < cstart - minll; sp++)
+    {
+      for (ep = sp + minll; ep < cstart; ep++)
+        {
+          gdouble score = score_headloop (dhandle, sp, ep - sp, config->block_length / 2, config->score);
+          g_print ("%u %.17g\n", ep - sp, score);
+          continue;
+          if (score <= config->score)
+            {
+              config->loop_start = sp - block;
+              config->loop_length = ep - sp;
+              config->score = score;
+              found_loop = TRUE;
+            }
+          gsl_progress_notify (&pstate, ++pcount * 100.0 / pdist, "score:%+g (pos:%6lu)", config->score, sp - block);
+        }
+      G_BREAKPOINT ();
+    }
+
+  g_free (block);
+  gsl_data_handle_close (dhandle);
+  return found_loop;
+}
+
+static inline gdouble
+dcache_headloop_score (GslDataCache     *dcache,
+                       GslLong           lstart,        /* loop start (reaches till bstart) */
+                       GslLong           cstart,        /* compare area start sample */
+                       GslLong           clength,       /* compare area length */
+                       gdouble           max_score,     /* no need to score beyond this */
+                       GslDataCacheNode **lnp,
+                       GslDataCacheNode **cnp,
+                       gboolean           weighted)
+{
+  gsize node_size = GSL_DATA_CACHE_NODE_SIZE (dcache);
+  GslDataCacheNode *lnode = *lnp, *cnode = *cnp;
+  gdouble wmax = clength, score = 0.0;
+  GslLong i = 0, loop_length = cstart - lstart;
+
+  /* compute score for loop repeated over comparison area
+   * .......|##########|-------------------------|......
+   *    loopstart   cstart                (cstart+clength)
+   */
+
+  while (i < clength)
+    {
+      GslLong cdiff, ldiff, clen, llen, k, l = i % loop_length;
+      gfloat *cb, *lb; /* base pointer */
+      if (lnode->offset > lstart + l || lstart + l >= lnode->offset + node_size)
+       {
+         gsl_data_cache_unref_node (dcache, lnode);
+         lnode = *lnp = gsl_data_cache_ref_node (dcache, lstart + l, GSL_DATA_CACHE_DEMAND_LOAD);
+       }
+      if (cnode->offset > cstart + i || cstart + i >= cnode->offset + node_size)
+       {
+         gsl_data_cache_unref_node (dcache, cnode);
+         cnode = *cnp = gsl_data_cache_ref_node (dcache, cstart + i, GSL_DATA_CACHE_DEMAND_LOAD);
+       }
+      cdiff = cstart + i - cnode->offset;
+      ldiff = lstart + l - lnode->offset;
+      clen = MIN (node_size - cdiff, clength - i);
+      llen = MIN (node_size - ldiff, loop_length - l);
+      cb = cnode->data + cdiff - i;
+      lb = lnode->data + ldiff - i;
+      if (weighted)
+        for (k = i + MIN (llen, clen); i < k; i++)
+          {
+            gdouble ed = lb[i] - cb[i];
+            score += ed * ed * (1.0 - i / wmax);
+          }
+      else
+        for (k = i + MIN (llen, clen); i < k; i++)
+          {
+            gdouble ed = lb[i] - cb[i];
+            score += ed * ed;
+          }
+      if (score > max_score)
+        break;
+    }
+
+  return score;
+}
+
+gboolean
+gsl_data_find_loop1 (GslDataHandle    *dhandle,
+                    GslDataLoopConfig *config,
+                    gpointer           pdata,
+                    GslProgressFunc    pfunc)
+{
+  GslProgressState pstate = gsl_progress_state (pdata, pfunc, 1);
+  guint frame = 4410;   // FIXME: adjustable value?
+  GslLong cstart, clength, j, dhandle_n_values, ls, ll, minll, pcount, maxll, min_loop = frame;
+  GslDataCache *dcache;
+  GslDataCacheNode *dnode1, *dnode2;
+  gdouble pdist;
+  gboolean found_loop = FALSE;
+
+  g_return_val_if_fail (dhandle != NULL, FALSE);
+  g_return_val_if_fail (config != NULL, FALSE);
+  config->n_details = 0;
+
+  if (gsl_data_handle_open (dhandle) != BSE_ERROR_NONE)
+    return FALSE;
+  dhandle_n_values = gsl_data_handle_n_values (dhandle);
+  config->block_start = CLAMP (config->block_start, 0, dhandle_n_values - 1);
+  if (config->block_length < 0)
+    config->block_length = dhandle_n_values - config->block_start;
+  else
+    config->block_length = MIN (config->block_length, dhandle_n_values - config->block_start);
+  if (config->block_length < 2)
+    return FALSE;
+  if (config->repetitions != CLAMP (config->repetitions, 2, config->block_length))
+    return FALSE;
+
+  dcache = gsl_data_cache_new (dhandle, 1);
+  gsl_data_cache_open (dcache);
+  gsl_data_handle_close (dhandle);
+  gsl_data_cache_unref (dcache);
+
+  dnode1 = gsl_data_cache_ref_node (dcache, config->block_start, GSL_DATA_CACHE_DEMAND_LOAD);
+  dnode2 = gsl_data_cache_ref_node (dcache, config->block_start, GSL_DATA_CACHE_DEMAND_LOAD);
+
+  /* widen loop, keeping it end-aligned to cstart
+   *      |------------##########|--------------------|......
+   * block_start   loopstart   cstart          (cstart+clength)
+   */
+
+  /* find a good loop length */
+  config->score = G_MAXDOUBLE;
+  cstart = config->block_start + config->block_length / config->repetitions;
+  clength = config->block_length - (cstart - config->block_start);
+  pcount = 0, pdist = (cstart - min_loop - config->block_start);
+  for (j = config->block_start; j < cstart - min_loop; j++)
+    {
+      gdouble score = dcache_headloop_score (dcache, j, cstart, clength, config->score, &dnode1, &dnode2, FALSE);
+      if (score <= config->score)
+        {
+          config->loop_start = j;
+          config->loop_length = cstart - j;
+          config->score = score;
+          found_loop = TRUE;
+        }
+      if (pcount++ % 16 == 0)
+        gsl_progress_notify (&pstate, pcount * 100.0 / pdist, "score:%+g", config->score);
+    }
+  gsl_progress_wipe (&pstate);
+  if (!found_loop)
+    goto seek_loop_done;
+  g_printerr ("  lLOOP: %6llu - %6llu [%6llu] (block: %6llu - %6llu [%6llu]) (score:%+g)\n",
+              config->loop_start, config->loop_start + config->loop_length, config->loop_length,
+              config->block_start, config->block_start + config->block_length, config->block_length,
+              config->score);
+  
+  /* find best loop position */
+  ll = config->loop_length;
+  minll = ll;
+  maxll = ll + 1;
+  config->score = G_MAXDOUBLE;
+  pcount = 0, pdist = (config->block_length - maxll - frame) * (maxll - minll);
+  for (ls = config->block_start; ls + maxll + frame <= config->block_start + config->block_length; ls++)
+    {
+      /* find best loop length */
+      for (ll = minll; ll < maxll; ll++)
+        {
+          gdouble score = dcache_headloop_score (dcache, ls, ls + ll, frame, config->score, &dnode1, &dnode2, TRUE);
+          if (score <= config->score)
+            {
+              config->loop_start = ls;
+              config->loop_length = ll;
+              config->score = score;
+              found_loop = TRUE;
+            }
+          gsl_progress_notify (&pstate, ++pcount * 100.0 / pdist, "weighted-frame-score:%+g", config->score);
+        }
+    }
+  gsl_progress_wipe (&pstate);
+  g_printerr ("  pLOOP: %6llu - %6llu [%6llu] (block: %6llu - %6llu [%6llu]) (weighted-frame-score:%+g)\n",
+              config->loop_start, config->loop_start + config->loop_length, config->loop_length,
+              config->block_start, config->block_start + config->block_length, config->block_length,
+              config->score);
+
+  /* find best loop length */
+  frame = MIN (config->loop_length / 5, 4410 * 7);
+  while (frame >= 32) // FIXME
+    {
+      /* jitter loop length */
+      minll = config->loop_length - frame;
+      minll = MAX (minll, 1);
+      maxll = config->loop_length + frame;
+      maxll = MIN (config->block_start + config->block_length - frame,
+                   config->loop_start + maxll) -
+              config->loop_start;
+
+      /* find best loop length */
+      config->score = G_MAXDOUBLE;
+      ls = config->loop_start;
+      pcount = 0, pdist = maxll - minll;
+      for (ll = minll; ll < maxll; ll++)
+        {
+          gdouble score = dcache_headloop_score (dcache, ls, ls + ll, frame, config->score, &dnode1, &dnode2, FALSE);
+          if (score <= config->score)
+            {
+              config->loop_start = ls;
+              config->loop_length = ll;
+              config->score = score;
+              found_loop = TRUE;
+            }
+          gsl_progress_notify (&pstate, ++pcount * 100.0 / pdist, "frame-score:%+g", config->score);
+        }
+      gsl_progress_wipe (&pstate);
+      g_printerr ("  sLOOP: %6llu - %6llu [%6llu] (block: %6llu - %6llu [%6llu]) (frame[%d]-score:%+g)\n",
+                  config->loop_start, config->loop_start + config->loop_length, config->loop_length,
+                  config->block_start, config->block_start + config->block_length, config->block_length,
+                  frame, config->score);
+      frame /= 2;
+    }
+
+ seek_loop_done:
+
+  gsl_data_cache_unref_node (dcache, dnode1);
+  gsl_data_cache_unref_node (dcache, dnode2);
+
+  gsl_data_cache_close (dcache);
+  return found_loop;
+}
+
+static inline float
+tailloop_score (GslDataCache          *dcache,
+               const GslDataTailLoop *cfg,
+               GslLong                loopstart,
+               GslLong                loopsize,
+               gfloat                 worstscore)
+{
+  gsize node_size = GSL_DATA_CACHE_NODE_SIZE (dcache);
+  GslLong looppos, i, compare = cfg->pre_loop_compare;
+  gfloat score = 0.0;
+  GslDataCacheNode *snode, *lnode;
+
+  /* compute score for loopsize with compare samples before loop */
+  /* -----|-------------------------|-----------------
+   *  loopstart-compare          loopstart
+   */
+
+  looppos = loopstart - compare;
+  while (looppos < loopstart)
+    looppos += loopsize;
+
+  snode = gsl_data_cache_ref_node (dcache, loopstart - compare, GSL_DATA_CACHE_DEMAND_LOAD);
+  lnode = gsl_data_cache_ref_node (dcache, looppos, GSL_DATA_CACHE_DEMAND_LOAD);
+  for (i = loopstart - compare; i < loopstart;)
+    {
+      GslLong sdiff, ldiff, slen, llen, loop_len, compare_len, j;
+      gfloat *sb, *lb;
+
+      if (snode->offset > i || i >= snode->offset + node_size)
+       {
+         gsl_data_cache_unref_node (dcache, snode);
+         snode = gsl_data_cache_ref_node (dcache, i, GSL_DATA_CACHE_DEMAND_LOAD);
+       }
+      if (lnode->offset > looppos || looppos >= lnode->offset + node_size)
+       {
+         gsl_data_cache_unref_node (dcache, lnode);
+         lnode = gsl_data_cache_ref_node (dcache, looppos, GSL_DATA_CACHE_DEMAND_LOAD);
+       }
+      sdiff = i - snode->offset;
+      ldiff = looppos - lnode->offset;
+      slen = node_size - sdiff;
+      llen = node_size - ldiff;
+      sb = snode->data + sdiff;
+      lb = lnode->data + ldiff;
+      compare_len = loopstart - i;
+      loop_len = loopsize - (looppos - loopstart);
+      
+      slen = MIN (slen, compare_len);
+      llen = MIN (llen, loop_len);
+      slen = MIN (slen, llen);
+      
+      j = slen;
+      if (cfg->cmp_strategy == GSL_DATA_TAIL_LOOP_CMP_CORRELATION)
+       {
+         while (j--)
+           score += sb[j] * lb[j];
+       }
+      else
+       {
+         while (j--)
+           {
+             gfloat tmp = lb[j] - sb[j];
+             score += tmp * tmp;
+           }
+         if (score > worstscore)
+           break;
+       }
+      
+      i += slen;
+      looppos += slen;
+      if (looppos >= loopstart + loopsize)
+       looppos -= loopsize;
+    }
+  gsl_data_cache_unref_node (dcache, snode);
+  gsl_data_cache_unref_node (dcache, lnode);
+
+  return score;
+}
+
+gdouble
+gsl_data_find_loop0 (GslDataHandle         *dhandle,
+                     const GslDataTailLoop *cfg,
+                     GslLong               *loop_start_p,
+                     GslLong               *loop_end_p)
+{
+  GslDataCache *dcache;
+  GslLong perc_bound, cfg_max_loop, dhandle_n_values;
+  GslLong perc_count = 0, perc_val = 0;
+  GslLong loopsize, bestloopsize = 0;
+  gdouble bestscore;
+
+  g_return_val_if_fail (dhandle != NULL, 0);
+  g_return_val_if_fail (cfg != NULL, 0);
+  g_return_val_if_fail (loop_start_p != NULL, 0);
+  g_return_val_if_fail (loop_end_p != NULL, 0);
+  g_return_val_if_fail (cfg->min_loop >= 1, 0);
+
+  if (gsl_data_handle_open (dhandle) != BSE_ERROR_NONE)
+    return 0;
+  dhandle_n_values = gsl_data_handle_n_values (dhandle);
+
+  g_return_val_if_fail (cfg->pre_loop_compare < dhandle_n_values - 1, 0);
+  cfg_max_loop = cfg->max_loop < 0 ? dhandle_n_values - 1 - cfg->pre_loop_compare : cfg->max_loop;
+  g_return_val_if_fail (cfg_max_loop >= cfg->min_loop, 0);
+  g_return_val_if_fail (cfg->pre_loop_compare + cfg_max_loop < dhandle_n_values, 0);
+  g_return_val_if_fail (cfg->cmp_strategy == GSL_DATA_TAIL_LOOP_CMP_LEAST_SQUARE ||
+                       cfg->cmp_strategy == GSL_DATA_TAIL_LOOP_CMP_CORRELATION, 0);
+
+  dcache = gsl_data_cache_new (dhandle, 1);
+  gsl_data_cache_open (dcache);
+  gsl_data_handle_close (dhandle);
+  gsl_data_cache_unref (dcache);
+
+  perc_bound = (cfg_max_loop - cfg->min_loop) / 100.0;
+
+  /* we try to maximize correlation, but to minimize the error */
+  if (cfg->cmp_strategy == GSL_DATA_TAIL_LOOP_CMP_CORRELATION)
+    bestscore = 0.0;
+  else
+    bestscore = 1e+14;
+
+  for (loopsize = cfg->min_loop; loopsize < cfg_max_loop; loopsize++)
+    {
+      gdouble score = tailloop_score (dcache, cfg,
+                                     dhandle_n_values - loopsize, loopsize,
+                                     bestscore);
+
+      /* we try to maximize correlation, but to minimize the error */
+      if ((cfg->cmp_strategy == GSL_DATA_TAIL_LOOP_CMP_CORRELATION && score > bestscore) ||
+         (cfg->cmp_strategy == GSL_DATA_TAIL_LOOP_CMP_LEAST_SQUARE && score < bestscore))
+       {
+         bestloopsize = loopsize;
+         bestscore = score;
+       }
+      if (++perc_count >= perc_bound)
+       {
+         perc_count = 0;
+         perc_val++;
+         g_printerr ("processed %llu%%       \r", perc_val);
+       }
+    }
+  g_printerr ("\nbest match (%s): len in samples=%lld, len=%lld, score=%f\n",
+             (cfg->cmp_strategy == GSL_DATA_TAIL_LOOP_CMP_CORRELATION) ? "correlation" : "least squares",
+             bestloopsize, bestloopsize, bestscore);
+
+  *loop_start_p = dhandle_n_values - bestloopsize;
+  *loop_end_p = dhandle_n_values - 1;
+
+  gsl_data_cache_close (dcache);
+
+  /* FIXME: statistics: scan for other extreme points in
+   *                    score[cfg->min_loop..cfg_max_loop]
+   */
+
+  return bestscore;
+}
diff --git a/tools/bseloopfuncs.h b/tools/bseloopfuncs.h
deleted file mode 100644 (file)
index bf85b84..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-/* BSE - Better Sound Engine
- * Copyright (C) 2001, 2003 Tim Janik and Stefan Westerfeld
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 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
- * Lesser General Public License for more details.
- *
- * A copy of the GNU Lesser General Public License should ship along
- * with this library; if not, see http://www.gnu.org/copyleft/.
- */
-#ifndef __BSE_LOOPFUNCS_H__
-#define __BSE_LOOPFUNCS_H__
-
-#include <bse/gsldatautils.h>
-#include <bse/gslcommon.h>
-
-G_BEGIN_DECLS
-
-
-typedef struct {
-  /* block containing loop */
-  GslLong       block_start;
-  GslLong       block_length;
-  /* performance/quality parameters */
-  GslLong       analysis_points;
-  /* assumed repetitions within block */
-  GslLong       repetitions;
-  GslLong       min_loop;
-  /* resulting loop */
-  gdouble       score;
-  GslLong       loop_start;
-  GslLong       loop_length;
-  /* score details */
-  guint         n_details;
-  const char   *detail_names[64];
-  double        detail_scores[64];
-} GslDataLoopConfig;
-
-/* mem-cached loop position and size finder. tests through all possible
- * loop sizes around center points determined by block/(analysis_points+1).
- * uses full-block comparisons (centering comparison area around the
- * loop) and tight neighbourhood comparisons. the full-block and
- * neighbourhood compraisons are normalized by the sample count to produce
- * a single error score. the progress counter is slightly off and will
- * often count to values lesser than 100%.
- */
-gboolean        gsl_data_find_loop5             (GslDataHandle          *dhandle,
-                                                 GslDataLoopConfig      *config,
-                                                 gpointer                pdata,
-                                                 GslProgressFunc         pfunc);
-/* mem-cached loop position and size finder. tests through all possible
- * loop sizes around center points determined by block/(analysis_points+1).
- * uses full-block comparisons (centering comparison area around the
- * loop) and can produce non-acceptable results. looping 6 seconds in
- * 61 samples runs roughly 6 hours on 2500MHz.
- */
-gboolean        gsl_data_find_loop4             (GslDataHandle          *dhandle,
-                                                 GslDataLoopConfig      *config,
-                                                 gpointer                pdata,
-                                                 GslProgressFunc         pfunc);
-/* fully mem-cached loop position/size finder.
- * attempts to determine optimum loop position and size by trying all
- * possible combinations (n^2) and doing a full block comparison on
- * each (*n). performance is n^3 and not suitable for practical
- * application. (and even then, full block comparisons are not a good
- * enough criterion to always reveal acceptable loop transitions).
- * 44100*3=132300, 132300*132300=17503290000
- */
-gboolean        gsl_data_find_loop3             (GslDataHandle     *dhandle,
-                                                 GslDataLoopConfig *config,
-                                                 gpointer           pdata,
-                                                 GslProgressFunc    pfunc);
-/* quick hack to dump gnuplot file with diffenrent loop
- * positions or loop sizes
- */
-gboolean        gsl_data_find_loop2             (GslDataHandle     *dhandle,
-                                                 GslDataLoopConfig *config,
-                                                 gpointer           pdata,
-                                                 GslProgressFunc    pfunc);
-/* dcache based head-compare loop finder,
- * 1) finds optimum loop length
- * 2) find optimum loop position
- * 3) reruns loop length finder around positions
- *    with shrinking neighbourhood comparisons
- */
-gboolean        gsl_data_find_loop1              (GslDataHandle     *dhandle,
-                                                  GslDataLoopConfig *config,
-                                                  gpointer           pdata,
-                                                  GslProgressFunc    pfunc);
-
-
-typedef enum
-{
-  GSL_DATA_TAIL_LOOP_CMP_LEAST_SQUARE,
-  GSL_DATA_TAIL_LOOP_CMP_CORRELATION,
-} GslDataTailLoopCmpType;
-typedef struct {
-  GslLong               min_loop;              /* minimum size */
-  GslLong               max_loop;              /* maximum size (-1 for n_values) */
-  GslLong               pre_loop_compare;      /* area size */
-  GslDataTailLoopCmpType cmp_strategy;
-  /* |-----|----------------|-------------------------------|
-   *        pre_loop_compare loop area (min_loop..max_loop)
-   *                        ^                               ^
-   *                     loop_start                      loop_end
-   */
-} GslDataTailLoop;
-/* dcache based tail loop finder derived from code from stefan */
-gdouble                gsl_data_find_loop0             (GslDataHandle          *dhandle,
-                                                const GslDataTailLoop  *cfg,
-                                                GslLong                *loop_start_p,
-                                                GslLong                *loop_end_p);
-/* gsl_data_find_loop0(): good loops, bad size:
- * # Tail Loop Finding
- * #  --loop <cfg>  loop finder configuration, consisting of ':'-seperated values:
- * #                n_samples indicating minimum loop size [4410]
- * #                n_samples indicating maximum loop size [-1]
- * #                n_samples compare area size (ahead of loop start) [8820]
- * TL_CFG="--loop=5000:-1:5000"
- */
-
-G_END_DECLS
-
-#endif  /* __BSE_LOOPFUNCS_H__ */
diff --git a/tools/bseloopfuncs.hh b/tools/bseloopfuncs.hh
new file mode 100644 (file)
index 0000000..bf85b84
--- /dev/null
@@ -0,0 +1,130 @@
+/* BSE - Better Sound Engine
+ * Copyright (C) 2001, 2003 Tim Janik and Stefan Westerfeld
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * A copy of the GNU Lesser General Public License should ship along
+ * with this library; if not, see http://www.gnu.org/copyleft/.
+ */
+#ifndef __BSE_LOOPFUNCS_H__
+#define __BSE_LOOPFUNCS_H__
+
+#include <bse/gsldatautils.h>
+#include <bse/gslcommon.h>
+
+G_BEGIN_DECLS
+
+
+typedef struct {
+  /* block containing loop */
+  GslLong       block_start;
+  GslLong       block_length;
+  /* performance/quality parameters */
+  GslLong       analysis_points;
+  /* assumed repetitions within block */
+  GslLong       repetitions;
+  GslLong       min_loop;
+  /* resulting loop */
+  gdouble       score;
+  GslLong       loop_start;
+  GslLong       loop_length;
+  /* score details */
+  guint         n_details;
+  const char   *detail_names[64];
+  double        detail_scores[64];
+} GslDataLoopConfig;
+
+/* mem-cached loop position and size finder. tests through all possible
+ * loop sizes around center points determined by block/(analysis_points+1).
+ * uses full-block comparisons (centering comparison area around the
+ * loop) and tight neighbourhood comparisons. the full-block and
+ * neighbourhood compraisons are normalized by the sample count to produce
+ * a single error score. the progress counter is slightly off and will
+ * often count to values lesser than 100%.
+ */
+gboolean        gsl_data_find_loop5             (GslDataHandle          *dhandle,
+                                                 GslDataLoopConfig      *config,
+                                                 gpointer                pdata,
+                                                 GslProgressFunc         pfunc);
+/* mem-cached loop position and size finder. tests through all possible
+ * loop sizes around center points determined by block/(analysis_points+1).
+ * uses full-block comparisons (centering comparison area around the
+ * loop) and can produce non-acceptable results. looping 6 seconds in
+ * 61 samples runs roughly 6 hours on 2500MHz.
+ */
+gboolean        gsl_data_find_loop4             (GslDataHandle          *dhandle,
+                                                 GslDataLoopConfig      *config,
+                                                 gpointer                pdata,
+                                                 GslProgressFunc         pfunc);
+/* fully mem-cached loop position/size finder.
+ * attempts to determine optimum loop position and size by trying all
+ * possible combinations (n^2) and doing a full block comparison on
+ * each (*n). performance is n^3 and not suitable for practical
+ * application. (and even then, full block comparisons are not a good
+ * enough criterion to always reveal acceptable loop transitions).
+ * 44100*3=132300, 132300*132300=17503290000
+ */
+gboolean        gsl_data_find_loop3             (GslDataHandle     *dhandle,
+                                                 GslDataLoopConfig *config,
+                                                 gpointer           pdata,
+                                                 GslProgressFunc    pfunc);
+/* quick hack to dump gnuplot file with diffenrent loop
+ * positions or loop sizes
+ */
+gboolean        gsl_data_find_loop2             (GslDataHandle     *dhandle,
+                                                 GslDataLoopConfig *config,
+                                                 gpointer           pdata,
+                                                 GslProgressFunc    pfunc);
+/* dcache based head-compare loop finder,
+ * 1) finds optimum loop length
+ * 2) find optimum loop position
+ * 3) reruns loop length finder around positions
+ *    with shrinking neighbourhood comparisons
+ */
+gboolean        gsl_data_find_loop1              (GslDataHandle     *dhandle,
+                                                  GslDataLoopConfig *config,
+                                                  gpointer           pdata,
+                                                  GslProgressFunc    pfunc);
+
+
+typedef enum
+{
+  GSL_DATA_TAIL_LOOP_CMP_LEAST_SQUARE,
+  GSL_DATA_TAIL_LOOP_CMP_CORRELATION,
+} GslDataTailLoopCmpType;
+typedef struct {
+  GslLong               min_loop;              /* minimum size */
+  GslLong               max_loop;              /* maximum size (-1 for n_values) */
+  GslLong               pre_loop_compare;      /* area size */
+  GslDataTailLoopCmpType cmp_strategy;
+  /* |-----|----------------|-------------------------------|
+   *        pre_loop_compare loop area (min_loop..max_loop)
+   *                        ^                               ^
+   *                     loop_start                      loop_end
+   */
+} GslDataTailLoop;
+/* dcache based tail loop finder derived from code from stefan */
+gdouble                gsl_data_find_loop0             (GslDataHandle          *dhandle,
+                                                const GslDataTailLoop  *cfg,
+                                                GslLong                *loop_start_p,
+                                                GslLong                *loop_end_p);
+/* gsl_data_find_loop0(): good loops, bad size:
+ * # Tail Loop Finding
+ * #  --loop <cfg>  loop finder configuration, consisting of ':'-seperated values:
+ * #                n_samples indicating minimum loop size [4410]
+ * #                n_samples indicating maximum loop size [-1]
+ * #                n_samples compare area size (ahead of loop start) [8820]
+ * TL_CFG="--loop=5000:-1:5000"
+ */
+
+G_END_DECLS
+
+#endif  /* __BSE_LOOPFUNCS_H__ */
index 80eb366..8664e97 100644 (file)
@@ -16,7 +16,7 @@
  */
 #include <bse/gsldatahandle.h>
 #include <bse/gslwavechunk.h>
-#include "bseloopfuncs.h"
+#include "bseloopfuncs.hh"
 #include "bwtwave.hh"
 #include <typeinfo>
 #include <string>
diff --git a/tools/cutvorbis.c b/tools/cutvorbis.c
deleted file mode 100644 (file)
index a122cfa..0000000
+++ /dev/null
@@ -1,167 +0,0 @@
-/* GSL - Generic Sound Layer
- * Copyright (C) 2003 Tim Janik
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 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
- * Lesser General Public License for more details.
- *
- * A copy of the GNU Lesser General Public License should ship along
- * with this library; if not, see http://www.gnu.org/copyleft/.
- */
-#include <bse/gslvorbis-cutter.h>
-#include <bse/bsemain.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-static GslVorbisCutterMode cutmode = GSL_VORBIS_CUTTER_NONE;
-static SfiNum              cutpoint = 0;
-static guint               filtered_serialno = 0;
-static gboolean            filter_serialno = FALSE;
-
-static void
-parse_args (int    *argc_p,
-            char ***argv_p)
-{
-  guint argc = *argc_p;
-  gchar **argv = *argv_p;
-  guint i;
-  for (i = 1; i < argc; i++)
-    {
-      if (strcmp ("-s", argv[i]) == 0 && i + 1 < argc)
-        {
-          cutmode = GSL_VORBIS_CUTTER_SAMPLE_BOUNDARY;
-          argv[i++] = NULL;
-          cutpoint = g_ascii_strtoull (argv[i], NULL, 10);
-          argv[i] = NULL;
-        }
-      else if (strcmp ("-k", argv[i]) == 0 && i + 1 < argc)
-        {
-          cutmode = GSL_VORBIS_CUTTER_PACKET_BOUNDARY;
-          argv[i++] = NULL;
-          cutpoint = g_ascii_strtoull (argv[i], NULL, 10);
-          argv[i] = NULL;
-        }
-      else if (strcmp ("-p", argv[i]) == 0 && i + 1 < argc)
-        {
-          cutmode = GSL_VORBIS_CUTTER_PAGE_BOUNDARY;
-          argv[i++] = NULL;
-          cutpoint = g_ascii_strtoull (argv[i], NULL, 10);
-          argv[i] = NULL;
-        }
-      else if (strcmp ("-S", argv[i]) == 0 && i + 1 < argc)
-        {
-          filter_serialno = TRUE;
-          argv[i++] = NULL;
-          filtered_serialno = g_ascii_strtoull (argv[i], NULL, 10);
-          argv[i] = NULL;
-        }
-    }
-  guint e = 1;
-  for (i = 1; i < argc; i++)
-    if (argv[i])
-      {
-        argv[e++] = argv[i];
-        if (i >= e)
-          argv[i] = NULL;
-      }
-  *argc_p = e;
-}
-
-int
-main (int   argc,
-      char *argv[])
-{
-  const gchar *ifile, *ofile;
-  GslVorbisCutter *cutter;
-  gint ifd, ofd;
-
-  /* initialization */
-  SfiInitValue values[] = {
-    { "stand-alone",            "true" }, /* no rcfiles etc. */
-    { "wave-chunk-padding",     NULL, 1, },
-    { "dcache-block-size",      NULL, 8192, },
-    { "dcache-cache-memory",    NULL, 5 * 1024 * 1024, },
-    { NULL }
-  };
-  bse_init_inprocess (&argc, &argv, "BseCutVorbis", values);
-
-  /* arguments */
-  parse_args (&argc, &argv);
-  if (argc != 3)
-    {
-      g_printerr ("usage: cutvorbis infile.ogg [{-s|-k|-p} <cutpoint>] [-S <serialno>] outfile.ogg\n");
-      g_printerr ("  -S <serialno>  only process data from the Ogg/Vorbis stream with <serialno>\n");
-      g_printerr ("  -s <cutpoint>  cut the Ogg/Vorbis stream at sample <cutpoint>\n");
-      g_printerr ("  -k <cutpoint>  same as -s, but cut at vorbis packet boundary\n");
-      g_printerr ("  -p <cutpoint>  same as -s, but cut at ogg page boundary\n");
-      exit (1);
-    }
-  ifile = argv[1];
-  ofile = argv[2];
-
-  cutter = gsl_vorbis_cutter_new ();
-  gsl_vorbis_cutter_set_cutpoint (cutter, cutmode, cutpoint);
-  if (filter_serialno)
-    gsl_vorbis_cutter_filter_serialno (cutter, filtered_serialno);
-
-  ifd = open (ifile, O_RDONLY);
-  if (ifd < 0)
-    {
-      g_printerr ("Error: failed to open \"%s\": %s\n", ifile, g_strerror (errno));
-      exit (1);
-    }
-  ofd = open (ofile, O_CREAT | O_TRUNC | O_WRONLY, 0666);
-  if (ofd < 0)
-    {
-      g_printerr ("Error: failed to open \"%s\": %s\n", ofile, g_strerror (errno));
-      exit (1);
-    }
-
-  while (!gsl_vorbis_cutter_ogg_eos (cutter))
-    {
-      guint blength = 8192, n, j;
-      guint8 buffer[blength];
-      do
-        j = read (ifd, buffer, blength);
-      while (j < 0 && errno == EINTR);
-      if (j <= 0)
-        {
-          const char *errstr = g_strerror (errno);
-          if (!errno && j == 0)
-            errstr = "End of File";
-          g_printerr ("Error: failed to read from \"%s\": %s\n", ifile, errstr);
-          exit (1);
-        }
-      gsl_vorbis_cutter_write_ogg (cutter, j, buffer);
-      n = gsl_vorbis_cutter_read_ogg (cutter, blength, buffer);
-      do
-        j = write (ofd, buffer, n);
-      while (j < 0 && errno == EINTR);
-      if (j < 0)
-        {
-          g_printerr ("Error: failed to write to \"%s\": %s\n", ofile, g_strerror (errno));
-          exit (1);
-        }
-    }
-
-  close (ifd);
-  if (close (ofd) < 0)
-    {
-      g_printerr ("Error: failed to flush \"%s\": %s\n", ofile, g_strerror (errno));
-      exit (1);
-    }
-  g_print ("done\n");
-
-  return 0;
-}
diff --git a/tools/cutvorbis.cc b/tools/cutvorbis.cc
new file mode 100644 (file)
index 0000000..a122cfa
--- /dev/null
@@ -0,0 +1,167 @@
+/* GSL - Generic Sound Layer
+ * Copyright (C) 2003 Tim Janik
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * A copy of the GNU Lesser General Public License should ship along
+ * with this library; if not, see http://www.gnu.org/copyleft/.
+ */
+#include <bse/gslvorbis-cutter.h>
+#include <bse/bsemain.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+static GslVorbisCutterMode cutmode = GSL_VORBIS_CUTTER_NONE;
+static SfiNum              cutpoint = 0;
+static guint               filtered_serialno = 0;
+static gboolean            filter_serialno = FALSE;
+
+static void
+parse_args (int    *argc_p,
+            char ***argv_p)
+{
+  guint argc = *argc_p;
+  gchar **argv = *argv_p;
+  guint i;
+  for (i = 1; i < argc; i++)
+    {
+      if (strcmp ("-s", argv[i]) == 0 && i + 1 < argc)
+        {
+          cutmode = GSL_VORBIS_CUTTER_SAMPLE_BOUNDARY;
+          argv[i++] = NULL;
+          cutpoint = g_ascii_strtoull (argv[i], NULL, 10);
+          argv[i] = NULL;
+        }
+      else if (strcmp ("-k", argv[i]) == 0 && i + 1 < argc)
+        {
+          cutmode = GSL_VORBIS_CUTTER_PACKET_BOUNDARY;
+          argv[i++] = NULL;
+          cutpoint = g_ascii_strtoull (argv[i], NULL, 10);
+          argv[i] = NULL;
+        }
+      else if (strcmp ("-p", argv[i]) == 0 && i + 1 < argc)
+        {
+          cutmode = GSL_VORBIS_CUTTER_PAGE_BOUNDARY;
+          argv[i++] = NULL;
+          cutpoint = g_ascii_strtoull (argv[i], NULL, 10);
+          argv[i] = NULL;
+        }
+      else if (strcmp ("-S", argv[i]) == 0 && i + 1 < argc)
+        {
+          filter_serialno = TRUE;
+          argv[i++] = NULL;
+          filtered_serialno = g_ascii_strtoull (argv[i], NULL, 10);
+          argv[i] = NULL;
+        }
+    }
+  guint e = 1;
+  for (i = 1; i < argc; i++)
+    if (argv[i])
+      {
+        argv[e++] = argv[i];
+        if (i >= e)
+          argv[i] = NULL;
+      }
+  *argc_p = e;
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+  const gchar *ifile, *ofile;
+  GslVorbisCutter *cutter;
+  gint ifd, ofd;
+
+  /* initialization */
+  SfiInitValue values[] = {
+    { "stand-alone",            "true" }, /* no rcfiles etc. */
+    { "wave-chunk-padding",     NULL, 1, },
+    { "dcache-block-size",      NULL, 8192, },
+    { "dcache-cache-memory",    NULL, 5 * 1024 * 1024, },
+    { NULL }
+  };
+  bse_init_inprocess (&argc, &argv, "BseCutVorbis", values);
+
+  /* arguments */
+  parse_args (&argc, &argv);
+  if (argc != 3)
+    {
+      g_printerr ("usage: cutvorbis infile.ogg [{-s|-k|-p} <cutpoint>] [-S <serialno>] outfile.ogg\n");
+      g_printerr ("  -S <serialno>  only process data from the Ogg/Vorbis stream with <serialno>\n");
+      g_printerr ("  -s <cutpoint>  cut the Ogg/Vorbis stream at sample <cutpoint>\n");
+      g_printerr ("  -k <cutpoint>  same as -s, but cut at vorbis packet boundary\n");
+      g_printerr ("  -p <cutpoint>  same as -s, but cut at ogg page boundary\n");
+      exit (1);
+    }
+  ifile = argv[1];
+  ofile = argv[2];
+
+  cutter = gsl_vorbis_cutter_new ();
+  gsl_vorbis_cutter_set_cutpoint (cutter, cutmode, cutpoint);
+  if (filter_serialno)
+    gsl_vorbis_cutter_filter_serialno (cutter, filtered_serialno);
+
+  ifd = open (ifile, O_RDONLY);
+  if (ifd < 0)
+    {
+      g_printerr ("Error: failed to open \"%s\": %s\n", ifile, g_strerror (errno));
+      exit (1);
+    }
+  ofd = open (ofile, O_CREAT | O_TRUNC | O_WRONLY, 0666);
+  if (ofd < 0)
+    {
+      g_printerr ("Error: failed to open \"%s\": %s\n", ofile, g_strerror (errno));
+      exit (1);
+    }
+
+  while (!gsl_vorbis_cutter_ogg_eos (cutter))
+    {
+      guint blength = 8192, n, j;
+      guint8 buffer[blength];
+      do
+        j = read (ifd, buffer, blength);
+      while (j < 0 && errno == EINTR);
+      if (j <= 0)
+        {
+          const char *errstr = g_strerror (errno);
+          if (!errno && j == 0)
+            errstr = "End of File";
+          g_printerr ("Error: failed to read from \"%s\": %s\n", ifile, errstr);
+          exit (1);
+        }
+      gsl_vorbis_cutter_write_ogg (cutter, j, buffer);
+      n = gsl_vorbis_cutter_read_ogg (cutter, blength, buffer);
+      do
+        j = write (ofd, buffer, n);
+      while (j < 0 && errno == EINTR);
+      if (j < 0)
+        {
+          g_printerr ("Error: failed to write to \"%s\": %s\n", ofile, g_strerror (errno));
+          exit (1);
+        }
+    }
+
+  close (ifd);
+  if (close (ofd) < 0)
+    {
+      g_printerr ("Error: failed to flush \"%s\": %s\n", ofile, g_strerror (errno));
+      exit (1);
+    }
+  g_print ("done\n");
+
+  return 0;
+}
diff --git a/tools/magictest.c b/tools/magictest.c
deleted file mode 100644 (file)
index 8f4fe36..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-/* BSE - Better Sound Engine
- * Copyright (C) 2000-2003 Tim Janik
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 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
- * Lesser General Public License for more details.
- *
- * A copy of the GNU Lesser General Public License should ship along
- * with this library; if not, see http://www.gnu.org/copyleft/.
- */
-#include <bse/bsemain.h>
-#include "topconfig.h"
-#include <bse/gslmagic.h>
-#include <bse/gslcommon.h>
-#include <bse/bseloader.h>
-#include <bse/gsldatahandle.h>
-#include <stdio.h>
-#include <string.h>
-
-static gint
-help (gchar *arg)
-{
-  fprintf (stderr, "usage: magictest [-{h|p|}] [files...]\n");
-  fprintf (stderr, "       -p         include plugins\n");
-  fprintf (stderr, "       -t         test loading file info\n");
-  fprintf (stderr, "       -h         guess what ;)\n");
-  
-  return arg != NULL;
-}
-
-int
-main (gint   argc,
-      gchar *argv[])
-{
-  static gchar *magic_presets[][2] = {
-    /* some test entries, order is important for some cases */
-    { "Berkeley DB 2.X Hash/Little Endian",    "12 lelong 0x061561", },
-    { "MS-DOS executable (EXE)",               "0 string MZ", },
-    { "ELF object file",                       "0 string \177ELF", },
-    { "Bourne shell script text",              "0,string,#!/bin/sh", },
-    { "Bourne shell script text",              "0,string,#!\\ /bin/sh", },
-    { "Bourne shell script text",              "0,string,#!\\t/bin/sh", },
-    { "GIF image data",                                "0,string,GIF8", },
-    { "X window image dump (v7)",              ("# .xwd files\n"
-                                                "0x04,ubelong,0x0000007"), },
-    { "RIFF/WAVE audio",                       ("0 string RIFF\n"
-                                                "8 string WAVE"), },
-    { "RIFF data",                             "0 string RIFF", },
-    { "GslWave file",                          "0 string #GslWave\n", },
-  };
-  static const guint n_magic_presets = sizeof (magic_presets) / sizeof (magic_presets[0]);
-  guint i;
-  SfiRing *magic_list = NULL;
-  gboolean test_open = FALSE;
-
-  /* initialization */
-  SfiInitValue values[] = {
-    { "stand-alone",            "true" }, /* no rcfiles etc. */
-    { NULL }
-  };
-  bse_init_inprocess (&argc, &argv, "BseMagicTest", values);
-  
-  for (i = 0; i < n_magic_presets; i++)
-    magic_list = sfi_ring_append (magic_list,
-                                 gsl_magic_create (magic_presets[i][0],
-                                                   0,
-                                                   0,
-                                                   magic_presets[i][1]));
-  
-  for (i = 1; i < argc; i++)
-    {
-      if (strcmp ("-p", argv[i]) == 0)
-       ; // FIXME: bsw_register_plugins (BSE_PATH_PLUGINS, FALSE, NULL, NULL, NULL);
-      else if (strcmp ("-t", argv[i]) == 0)
-       test_open = TRUE;
-      else if (strcmp ("-h", argv[i]) == 0)
-       {
-         return help (NULL);
-       }
-      else
-       {
-         BseLoader *loader = bse_loader_match (argv[i]);
-         GslMagic *magic = gsl_magic_list_match_file (magic_list, argv[i]);
-         guint l = strlen (argv[i]);
-         gchar *pad;
-         
-         g_print ("%s:", argv[i]);
-         pad = g_strnfill (MAX (40, l) - l, ' ');
-         g_print (pad);
-         g_free (pad);
-         if (!magic && !loader)
-           g_print (" no magic/loader found");
-         else
-           {
-             if (magic)
-               g_print (" MAGIC: %s", (char*) magic->data);
-             if (loader)
-                {
-                  if (test_open)
-                    {
-                      BseWaveFileInfo *wfi;
-                      BseErrorType error = 0;
-                      g_print ("\n  LOADER: %s\n", loader->name);
-                      wfi = bse_wave_file_info_load (argv[i], &error);
-                      if (wfi)
-                        bse_wave_file_info_unref (wfi);
-                      g_print ("  ERROR: %s", bse_error_blurb (error));
-                    }
-                  else
-                    g_print (" LOADER: %s", loader->name);
-                }
-           }
-         g_print ("\n");
-       }
-    }
-  
-  return 0;
-}
diff --git a/tools/magictest.cc b/tools/magictest.cc
new file mode 100644 (file)
index 0000000..b970172
--- /dev/null
@@ -0,0 +1,124 @@
+/* BSE - Better Sound Engine
+ * Copyright (C) 2000-2003 Tim Janik
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * A copy of the GNU Lesser General Public License should ship along
+ * with this library; if not, see http://www.gnu.org/copyleft/.
+ */
+#include <bse/bsemain.h>
+#include "topconfig.h"
+#include <bse/gslmagic.h>
+#include <bse/gslcommon.h>
+#include <bse/bseloader.h>
+#include <bse/gsldatahandle.h>
+#include <stdio.h>
+#include <string.h>
+
+static gint
+help (gchar *arg)
+{
+  fprintf (stderr, "usage: magictest [-{h|p|}] [files...]\n");
+  fprintf (stderr, "       -p         include plugins\n");
+  fprintf (stderr, "       -t         test loading file info\n");
+  fprintf (stderr, "       -h         guess what ;)\n");
+  
+  return arg != NULL;
+}
+
+int
+main (gint   argc,
+      gchar *argv[])
+{
+  static gchar *magic_presets[][2] = {
+    /* some test entries, order is important for some cases */
+    { "Berkeley DB 2.X Hash/Little Endian",    "12 lelong 0x061561", },
+    { "MS-DOS executable (EXE)",               "0 string MZ", },
+    { "ELF object file",                       "0 string \177ELF", },
+    { "Bourne shell script text",              "0,string,#!/bin/sh", },
+    { "Bourne shell script text",              "0,string,#!\\ /bin/sh", },
+    { "Bourne shell script text",              "0,string,#!\\t/bin/sh", },
+    { "GIF image data",                                "0,string,GIF8", },
+    { "X window image dump (v7)",              ("# .xwd files\n"
+                                                "0x04,ubelong,0x0000007"), },
+    { "RIFF/WAVE audio",                       ("0 string RIFF\n"
+                                                "8 string WAVE"), },
+    { "RIFF data",                             "0 string RIFF", },
+    { "GslWave file",                          "0 string #GslWave\n", },
+  };
+  static const guint n_magic_presets = sizeof (magic_presets) / sizeof (magic_presets[0]);
+  guint i;
+  SfiRing *magic_list = NULL;
+  gboolean test_open = FALSE;
+
+  /* initialization */
+  SfiInitValue values[] = {
+    { "stand-alone",            "true" }, /* no rcfiles etc. */
+    { NULL }
+  };
+  bse_init_inprocess (&argc, &argv, "BseMagicTest", values);
+  
+  for (i = 0; i < n_magic_presets; i++)
+    magic_list = sfi_ring_append (magic_list,
+                                 gsl_magic_create (magic_presets[i][0],
+                                                   0,
+                                                   0,
+                                                   magic_presets[i][1]));
+  
+  for (i = 1; i < argc; i++)
+    {
+      if (strcmp ("-p", argv[i]) == 0)
+       ; // FIXME: bsw_register_plugins (BSE_PATH_PLUGINS, FALSE, NULL, NULL, NULL);
+      else if (strcmp ("-t", argv[i]) == 0)
+       test_open = TRUE;
+      else if (strcmp ("-h", argv[i]) == 0)
+       {
+         return help (NULL);
+       }
+      else
+       {
+         BseLoader *loader = bse_loader_match (argv[i]);
+         GslMagic *magic = gsl_magic_list_match_file (magic_list, argv[i]);
+         guint l = strlen (argv[i]);
+         gchar *pad;
+         
+         g_print ("%s:", argv[i]);
+         pad = g_strnfill (MAX (40, l) - l, ' ');
+         g_print (pad);
+         g_free (pad);
+         if (!magic && !loader)
+           g_print (" no magic/loader found");
+         else
+           {
+             if (magic)
+               g_print (" MAGIC: %s", (char*) magic->data);
+             if (loader)
+                {
+                  if (test_open)
+                    {
+                      BseWaveFileInfo *wfi;
+                      BseErrorType error = BSE_ERROR_NONE;
+                      g_print ("\n  LOADER: %s\n", loader->name);
+                      wfi = bse_wave_file_info_load (argv[i], &error);
+                      if (wfi)
+                        bse_wave_file_info_unref (wfi);
+                      g_print ("  ERROR: %s", bse_error_blurb (error));
+                    }
+                  else
+                    g_print (" LOADER: %s", loader->name);
+                }
+           }
+         g_print ("\n");
+       }
+    }
+  
+  return 0;
+}
diff --git a/tools/mathtool.c b/tools/mathtool.c
deleted file mode 100644 (file)
index 40f56e7..0000000
+++ /dev/null
@@ -1,739 +0,0 @@
-/* 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 Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 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
- * Lesser General Public License for more details.
- *
- * A copy of the GNU Lesser General Public License should ship along
- * with this library; if not, see http://www.gnu.org/copyleft/.
- */
-#define GSL_EXTENSIONS
-#include <bse/bsemain.h>
-#include <bse/gslcommon.h>
-#include <bse/gslfilter.h>
-#include <bse/bseloader.h>
-#include <bse/bsemathsignal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#define PREC    "15"
-
-
-static void    usage (void)    G_GNUC_NORETURN;
-
-static void  ring_test (void);
-
-static guint         shift_argc = 0;
-static const gchar **shift_argv = NULL;
-
-static const gchar*
-shift (void)
-{
-  const gchar *arg;
-  
-  if (shift_argc > 1)
-    {
-      shift_argc--;
-      arg = shift_argv++[1];
-      if (!arg)
-       arg = "";
-    }
-  else
-    arg = NULL;
-  return arg;
-}
-static const gchar*
-pshift (void)
-{
-  const gchar *arg = shift ();
-  
-  return arg ? arg : "";
-}
-
-int
-main (int   argc,
-      char *argv[])
-{
-  const gchar *arg;
-  /* iir filter parameters */
-  enum { FILTER_GNUPLOT, FILTER_SCAN } filter_mode = FILTER_GNUPLOT;
-  const gchar *filter_label = 0;
-  gdouble *a, *b;
-  guint i, order = 0;
-  
-  shift_argc = argc;
-  shift_argv = (const gchar**) argv;
-
-  /* init */
-  SfiInitValue values[] = {
-    { "stand-alone",            "true" }, /* no rcfiles etc. */
-    { "wave-chunk-padding",     NULL, 1, },
-    { "dcache-block-size",      NULL, 8192, },
-    { "dcache-cache-memory",    NULL, 5 * 1024 * 1024, },
-    { NULL }
-  };
-  bse_init_inprocess (&argc, &argv, NULL, values);
-
-  arg = shift ();
-  if (!arg)
-    usage ();
-  
- restart:
-  a = b = 0;
-  
-  if (strcmp (arg, "approx5-exp2-run") == 0)
-    {
-      gfloat f;
-      gdouble r = 0;
-      for (i = 0; i < 10; i++)
-       for (f = -1; f <= 1.0; f += 1.0 / 10000000.0)
-         r += bse_approx5_exp2 (f);
-      return (r > 0) & 8;
-    }
-  else if (strcmp (arg, "libc-exp-run") == 0)
-    {
-      gfloat f;
-      gdouble r = 0;
-      for (i = 0; i < 10; i++)
-       for (f = -1; f <= 1.0; f += 1.0 / 10000000.0)
-         r += exp (f);
-      return (r > 0) & 8;
-    }
-  else if (strcmp (arg, "wave-scan") == 0)
-    {
-      const gchar *file = pshift ();
-      
-      while (file)
-       {
-         BseWaveFileInfo *fi;
-         BseErrorType error;
-         
-         fi = bse_wave_file_info_load (file, &error);
-         if (fi)
-           {
-             guint i;
-             
-             g_print ("Loader \"%s\" found %u waves in \"%s\":\n", fi->loader->name, fi->n_waves, file);
-             for (i = 0; i < fi->n_waves; i++)
-               g_print ("%u) %s\n", i + 1, fi->waves[i].name);
-             bse_wave_file_info_unref (fi);
-           }
-         else
-           g_print ("Failed to scan \"%s\": %s\n", file, bse_error_blurb (error));
-         file = pshift ();
-         if (!file[0])
-           break;
-       }
-    }
-  else if (strcmp (arg, "file-test") == 0)
-    {
-      const gchar *file = pshift ();
-      
-      g_print ("file test for \"%s\":\n", file);
-      g_print ("  is readable   : %s\n", bse_error_blurb (gsl_file_check (file, "r")));
-      g_print ("  is writable   : %s\n", bse_error_blurb (gsl_file_check (file, "w")));
-      g_print ("  is executable : %s\n", bse_error_blurb (gsl_file_check (file, "x")));
-      g_print ("  is file       : %s\n", bse_error_blurb (gsl_file_check (file, "f")));
-      g_print ("  is directory  : %s\n", bse_error_blurb (gsl_file_check (file, "d")));
-      g_print ("  is link       : %s\n", bse_error_blurb (gsl_file_check (file, "l")));
-    }
-  else if (strcmp (arg, "ring-test") == 0)
-    {
-      ring_test ();
-    }
-#if 0
-  else if (strcmp (arg, "rf") == 0)
-    {
-      double x, y, z;
-      x = atof (pshift ());
-      y = atof (pshift ());
-      z = atof (pshift ());
-      
-      g_print ("rf(%f, %f, %f) = %."PREC"f\n", x, y, z, gsl_ellip_rf (x, y, z));
-    }
-  else if (strcmp (arg, "F") == 0)
-    {
-      double phi, ak;
-      phi = atof (pshift ());
-      ak = atof (pshift ());
-      
-      g_print ("F(%f, %f) = %."PREC"f\n", phi, ak, gsl_ellip_F (phi, ak));
-    }
-  else if (strcmp (arg, "sn") == 0)
-    {
-      double u, emmc;
-      u = atof (pshift ());
-      emmc = atof (pshift ());
-      
-      g_print ("sn(%f, %f) = %."PREC"f\n", u, emmc, gsl_ellip_sn (u, emmc));
-    }
-  else if (strcmp (arg, "snc") == 0)
-    {
-      BseComplex u, emmc;
-      u.re = atof (pshift ());
-      u.im = atof (pshift ());
-      emmc.re = atof (pshift ());
-      emmc.im = atof (pshift ());
-      
-      g_print ("snc(%s, %s) = %s\n",
-              bse_complex_str (u),
-              bse_complex_str (emmc),
-              bse_complex_str (bse_complex_ellip_sn (u, emmc)));
-    }
-  else if (strcmp (arg, "sci_snc") == 0)
-    {
-      BseComplex u, k2;
-      u.re = atof (pshift ());
-      u.im = atof (pshift ());
-      k2.re = atof (pshift ());
-      k2.im = atof (pshift ());
-      
-      g_print ("sci_snc(%s, %s) = %s\n",
-              bse_complex_str (u),
-              bse_complex_str (k2),
-              bse_complex_str (bse_complex_ellip_sn (u, bse_complex_sub (bse_complex (1.0, 0), k2))));
-    }
-  else if (strcmp (arg, "asn") == 0)
-    {
-      double y, emmc;
-      y = atof (pshift ());
-      emmc = atof (pshift ());
-      
-      g_print ("asn(%f, %f) = %."PREC"f\n", y, emmc, gsl_ellip_asn (y, emmc));
-    }
-  else if (strcmp (arg, "asnc") == 0)
-    {
-      BseComplex y, emmc;
-      y.re = atof (pshift ());
-      y.im = atof (pshift ());
-      emmc.re = atof (pshift ());
-      emmc.im = atof (pshift ());
-      
-      g_print ("asnc(%s, %s) = %s\n",
-              bse_complex_str (y), bse_complex_str (emmc),
-              bse_complex_str (bse_complex_ellip_asn (y, emmc)));
-      g_print ("asn(%f, %f = %."PREC"f\n",
-              y.re, emmc.re, gsl_ellip_asn (y.re, emmc.re));
-    }
-  else if (strcmp (arg, "sci_sn") == 0)
-    {
-      double u, k2;
-      u = atof (pshift ());
-      k2 = atof (pshift ());
-      g_print ("sci_sn(%f, %f) = %."PREC"f\n", u, k2, gsl_ellip_sn (u, 1.0 - k2));
-    }
-  else if (strcmp (arg, "sci_asn") == 0)
-    {
-      double y, k2;
-      y = atof (pshift ());
-      k2 = atof (pshift ());
-      g_print ("sci_asn(%f, %f) = %."PREC"f\n", y, k2, gsl_ellip_asn (y, 1.0 - k2));
-    }
-  else if (strcmp (arg, "sci_asnc") == 0)
-    {
-      BseComplex y, k2;
-      y.re = atof (pshift ());
-      y.im = atof (pshift ());
-      k2.re = atof (pshift ());
-      k2.im = atof (pshift ());
-      g_print ("sci_asnc(%s, %s) = %s\n",
-              bse_complex_str (y), bse_complex_str (k2),
-              bse_complex_str (bse_complex_ellip_asn (y, bse_complex_sub (bse_complex (1.0, 0), k2))));
-      g_print ("asn(%f, %f = %."PREC"f\n",
-              y.re, k2.re, gsl_ellip_asn (y.re, 1.0 - k2.re));
-    }
-  else if (strncmp (arg, "poly", 4) == 0)
-    {
-      guint order;
-      arg = arg + 4;
-      order = 2;
-      {
-       double a[100] = { 1, 2, 1 }, b[100] = { 1, -3./2., 0.5 };
-       g_print ("# Test order=%u  norm=%f:\n",
-                order,
-                bse_poly_eval (order, a, 1) / bse_poly_eval (order, b, 1));
-       g_print ("H%u(z)=%s/%s\n", order,
-                bse_poly_str (order, a, "z"),
-                bse_poly_str (order, b, "z"));
-       if (*arg)
-         {
-           BseComplex root, roots[100];
-           guint i;
-           
-           if (*arg == 'r')
-             {
-               g_print ("#roots:\n");
-               bse_poly_complex_roots (order, a, roots);
-               for (i = 0; i < order; i++)
-                 {
-                   root = bse_complex_div (bse_complex (1, 0), roots[i]);
-                   g_print ("%+.14f %+.14f # %.14f\n", root.re, root.im, bse_complex_abs (root));
-                 }
-             }
-           if (*arg == 'p')
-             {
-               g_print ("#poles:\n");
-               bse_poly_complex_roots (order, b, roots);
-               for (i = 0; i < order; i++)
-                 {
-                   root = bse_complex_div (bse_complex (1, 0), roots[i]);
-                   g_print ("%+.14f %+.14f # %.14f\n", root.re, root.im, bse_complex_abs (root));
-                 }
-             }
-         }
-      }
-    }
-#endif
-  else if (strcmp (arg, "sin") == 0)
-    {
-      BseComplex phi;
-      phi.re = atof (pshift ());
-      phi.im = atof (pshift ());
-      g_print ("sin(%s) = %s\n",
-              bse_complex_str (phi),
-              bse_complex_str (bse_complex_sin (phi)));
-    }
-  else if (strcmp (arg, "cos") == 0)
-    {
-      BseComplex phi;
-      phi.re = atof (pshift ());
-      phi.im = atof (pshift ());
-      g_print ("cos(%s) = %s\n",
-              bse_complex_str (phi),
-              bse_complex_str (bse_complex_cos (phi)));
-    }
-  else if (strcmp (arg, "tan") == 0)
-    {
-      BseComplex phi;
-      phi.re = atof (pshift ());
-      phi.im = atof (pshift ());
-      g_print ("tan(%s) = %s\n",
-              bse_complex_str (phi),
-              bse_complex_str (bse_complex_tan (phi)));
-    }
-  else if (strcmp (arg, "sinh") == 0)
-    {
-      BseComplex phi;
-      phi.re = atof (pshift ());
-      phi.im = atof (pshift ());
-      g_print ("sinh(%s) = %s\n",
-              bse_complex_str (phi),
-              bse_complex_str (bse_complex_sinh (phi)));
-    }
-  else if (strcmp (arg, "cosh") == 0)
-    {
-      BseComplex phi;
-      phi.re = atof (pshift ());
-      phi.im = atof (pshift ());
-      g_print ("cosh(%s) = %s\n",
-              bse_complex_str (phi),
-              bse_complex_str (bse_complex_cosh (phi)));
-    }
-  else if (strcmp (arg, "tanh") == 0)
-    {
-      BseComplex phi;
-      phi.re = atof (pshift ());
-      phi.im = atof (pshift ());
-      g_print ("tanh(%s) = %s\n",
-              bse_complex_str (phi),
-              bse_complex_str (bse_complex_tanh (phi)));
-    }
-  else if (strcmp (arg, "midi2freq") == 0)
-    {
-      gint note;
-      note = atol (pshift ());
-      note = CLAMP (note, 0, 128);
-      g_print ("midi2freq(%u) = %f\n",
-              note,
-              bse_temp_freq (BSE_CONFIG (kammer_freq), note - BSE_CONFIG (midi_kammer_note)));
-    }
-  else if (strcmp (arg, "blp") == 0)
-    {
-      double f, e;
-      order = atoi (pshift ()); order = MAX (order, 1);
-      f = atof (pshift ());
-      e = atof (pshift ());
-      f *= PI / 2.;
-      a = g_new (gdouble, order + 1);
-      b = g_new (gdouble, order + 1);
-      gsl_filter_butter_lp (order, f, e, a, b);
-      g_print ("# Lowpass Butterworth filter order=%u freq=%f epsilon(s^2)=%f norm0=%f:\n",
-              order, f, e,
-              bse_poly_eval (order, a, 1) / bse_poly_eval (order, b, 1));
-      filter_label = "BL";
-    }
-  else if (strcmp (arg, "bhp") == 0)
-    {
-      double f, e;
-      order = atoi (pshift ()); order = MAX (order, 1);
-      f = atof (pshift ());
-      e = atof (pshift ());
-      f *= PI / 2.;
-      a = g_new (gdouble, order + 1);
-      b = g_new (gdouble, order + 1);
-      
-      gsl_filter_butter_hp (order, f, e, a, b);
-      g_print ("# Highpass Butterworth filter order=%u freq=%f epsilon(s^2)=%f norm0=%f:\n",
-              order, f, e,
-              bse_poly_eval (order, a, 1) / bse_poly_eval (order, b, 1));
-      filter_label = "BH";
-    }
-  else if (strcmp (arg, "bbp") == 0)
-    {
-      double f1, f2, e;
-      order = atoi (pshift ()); order = MAX (order, 1);
-      f1 = atof (pshift ());
-      f2 = atof (pshift ());
-      e = atof (pshift ());
-      f1 *= PI / 2.;
-      f2 *= PI / 2.;
-      a = g_new (gdouble, order + 1);
-      b = g_new (gdouble, order + 1);
-      
-      gsl_filter_butter_bp (order, f1, f2, e, a, b);
-      g_print ("# Bandpass Butterworth filter order=%u freq1=%f freq2=%f epsilon(s^2)=%f norm0=%f:\n",
-              order, f1, f2, e,
-              bse_poly_eval (order, a, 1) / bse_poly_eval (order, b, 1));
-      filter_label = "BP";
-    }
-  else if (strcmp (arg, "bbs") == 0)
-    {
-      double f1, f2, e;
-      order = atoi (pshift ()); order = MAX (order, 1);
-      f1 = atof (pshift ());
-      f2 = atof (pshift ());
-      e = atof (pshift ());
-      f1 *= PI / 2.;
-      f2 *= PI / 2.;
-      a = g_new (gdouble, order + 1);
-      b = g_new (gdouble, order + 1);
-      
-      gsl_filter_butter_bs (order, f1, f2, e, a, b);
-      g_print ("# Bandstop Butterworth filter order=%u freq1=%f freq2=%f epsilon(s^2)=%f norm0=%f:\n",
-              order, f1, f2, e,
-              bse_poly_eval (order, a, 1) / bse_poly_eval (order, b, 1));
-      filter_label = "BS";
-    }
-  else if (strcmp (arg, "t1l") == 0)
-    {
-      double f, e;
-      order = atoi (pshift ()); order = MAX (order, 1);
-      f = atof (pshift ());
-      e = atof (pshift ());
-      f *= PI / 2.;
-      a = g_new (gdouble, order + 1);
-      b = g_new (gdouble, order + 1);
-      
-      gsl_filter_tscheb1_lp (order, f, e, a, b);
-      g_print ("# Lowpass Tschebyscheff Type1 order=%u freq=%f epsilon(s^2)=%f norm0=%f:\n",
-              order, f, e,
-              bse_poly_eval (order, a, 1) / bse_poly_eval (order, b, 1));
-      filter_label = "T1L";
-    }
-  else if (strcmp (arg, "t1h") == 0)
-    {
-      double f, e;
-      order = atoi (pshift ()); order = MAX (order, 1);
-      f = atof (pshift ());
-      e = atof (pshift ());
-      f *= PI / 2.;
-      
-      a = g_new (gdouble, order + 1);
-      b = g_new (gdouble, order + 1);
-      
-      gsl_filter_tscheb1_hp (order, f, e, a, b);
-      g_print ("# Highpass Tschebyscheff Type1 order=%u freq=%f epsilon(s^2)=%f norm0=%f:\n",
-              order, f, e,
-              bse_poly_eval (order, a, 1) / bse_poly_eval (order, b, 1));
-      filter_label = "T1H";
-    }
-  else if (strcmp (arg, "t1s") == 0)
-    {
-      double fc, fr, e;
-      order = atoi (pshift ()); order = MAX (order, 1);
-      fc = atof (pshift ());
-      fr = atof (pshift ());
-      e = atof (pshift ());
-      fc *= PI / 2.;
-      fr *= PI / 2.;
-      
-      a = g_new (gdouble, order + 1);
-      b = g_new (gdouble, order + 1);
-      
-      gsl_filter_tscheb1_bs (order, fc, fr, e, a, b);
-      g_print ("# Bandstop Tschebyscheff Type1 order=%u freq_c=%f freq_r=%f epsilon(s^2)=%f norm=%f:\n",
-              order, fc, fr, e,
-              bse_poly_eval (order, a, 1) / bse_poly_eval (order, b, 1));
-      filter_label = "T1S";
-    }
-  else if (strcmp (arg, "t1p") == 0)
-    {
-      double fc, fr, e;
-      order = atoi (pshift ()); order = MAX (order, 1);
-      fc = atof (pshift ());
-      fr = atof (pshift ());
-      e = atof (pshift ());
-      fc *= PI / 2.;
-      fr *= PI / 2.;
-      
-      a = g_new (gdouble, order + 1);
-      b = g_new (gdouble, order + 1);
-      
-      gsl_filter_tscheb1_bp (order, fc, fr, e, a, b);
-      g_print ("# Bandpass Tschebyscheff Type1 order=%u freq_c=%f freq_r=%f epsilon(s^2)=%f norm=%f:\n",
-              order, fc, fr, e,
-              bse_poly_eval (order, a, 1) / bse_poly_eval (order, b, 1));
-      filter_label = "T1P";
-    }
-  else if (strcmp (arg, "t2l") == 0)
-    {
-      double f, st, e;
-      order = atoi (pshift ()); order = MAX (order, 1);
-      f = atof (pshift ());
-      st = atof (pshift ());
-      e = atof (pshift ());
-      f *= PI / 2.;
-      
-      a = g_new (gdouble, order + 1);
-      b = g_new (gdouble, order + 1);
-      
-      gsl_filter_tscheb2_lp (order, f, st, e, a, b);
-      g_print ("# Lowpass Tschebyscheff Type2 order=%u freq=%f steepness=%f (%f) epsilon(s^2)=%f norm=%f:\n",
-              order, f, st, f * (1.+st), e,
-              bse_poly_eval (order, a, 1) / bse_poly_eval (order, b, 1));
-      filter_label = "T2L";
-    }
-  else if (strcmp (arg, "t2h") == 0)
-    {
-      double f, st, e;
-      order = atoi (pshift ()); order = MAX (order, 1);
-      f = atof (pshift ());
-      st = atof (pshift ());
-      e = atof (pshift ());
-      f *= PI / 2.;
-      
-      a = g_new (gdouble, order + 1);
-      b = g_new (gdouble, order + 1);
-      
-      gsl_filter_tscheb2_hp (order, f, st, e, a, b);
-      g_print ("# Highpass Tschebyscheff Type2 order=%u freq=%f steepness=%f (%f, %f) epsilon(s^2)=%f norm=%f:\n",
-              order, f, st, PI - f, (PI - f) * (1.+st), e,
-              bse_poly_eval (order, a, 1) / bse_poly_eval (order, b, 1));
-      filter_label = "T2H";
-    }
-  else if (strcmp (arg, "t2p") == 0)
-    {
-      double f1, f2, st, e;
-      order = atoi (pshift ()); order = MAX (order, 1);
-      f1 = atof (pshift ());
-      f2 = atof (pshift ());
-      st = atof (pshift ());
-      e = atof (pshift ());
-      f1 *= PI / 2.;
-      f2 *= PI / 2.;
-      
-      a = g_new (gdouble, order + 1);
-      b = g_new (gdouble, order + 1);
-      
-      gsl_filter_tscheb2_bp (order, f1, f2, st, e, a, b);
-      g_print ("# Bandpass Tschebyscheff Type2 order=%u freq1=%f freq2=%f steepness=%f epsilon(s^2)=%f norm=%f:\n",
-              order, f1, f2, st, e,
-              bse_poly_eval (order, a, 1) / bse_poly_eval (order, b, 1));
-      filter_label = "T2P";
-    }
-  else if (strcmp (arg, "t2s") == 0)
-    {
-      double f1, f2, st, e;
-      order = atoi (pshift ()); order = MAX (order, 1);
-      f1 = atof (pshift ());
-      f2 = atof (pshift ());
-      st = atof (pshift ());
-      e = atof (pshift ());
-      f1 *= PI / 2.;
-      f2 *= PI / 2.;
-      
-      a = g_new (gdouble, order + 1);
-      b = g_new (gdouble, order + 1);
-      
-      gsl_filter_tscheb2_bs (order, f1, f2, st, e, a, b);
-      g_print ("# Bandstop Tschebyscheff Type2 order=%u freq1=%f freq2=%f steepness=%f epsilon(s^2)=%f norm=%f:\n",
-              order, f1, f2, st, e,
-              bse_poly_eval (order, a, 1) / bse_poly_eval (order, b, 1));
-      filter_label = "T2S";
-    }
-  else if (strcmp (arg, "scan") == 0)
-    {
-      filter_mode = FILTER_SCAN;
-    }
-  else if (strcmp (arg, "fir") == 0)
-    {
-      unsigned int iorder = atoi (pshift ());
-      unsigned int n_points = 0;
-      
-      double *freq = g_newa (double, argc / 2 + 1);
-      double *value = g_newa (double, argc / 2 + 1);
-      double *a = g_newa (double, iorder);
-      const char *f, *v;
-      
-      do
-       {
-         f = pshift ();
-         v = pshift ();
-         
-         if (f[0] && v[0])
-           {
-             freq[n_points] = atof (f) * PI;
-             value[n_points] = atof (v);
-             n_points++;
-           }
-       }
-      while (f[0] && v[0]);
-      
-      gsl_filter_fir_approx (iorder, a, n_points, freq, value, FALSE);
-      g_print ("FIR%u(z)=%s\n", iorder, bse_poly_str (iorder, a, "z"));
-    }
-  else
-    usage ();
-  
-  if (a && b)
-    {
-      gdouble freq;
-      
-      if (filter_mode == FILTER_SCAN)
-       {
-         freq = 0.001;
-         while (freq < 3.14)
-           {
-             g_print ("%f %.20f\n", freq, gsl_filter_sine_scan (order, a, b, freq, 2 * M_PI));
-             freq = MIN (freq * 1.1, freq + 0.01);
-           }
-       }
-      else if (filter_mode == FILTER_GNUPLOT)
-       {
-         g_print ("%s%u(z)=%s/%s\n", filter_label, order,
-                  bse_poly_str (order, a, "z"),
-                  bse_poly_str (order, b, "z"));
-       }
-      else
-       g_error ("unknown filter_mode");
-      g_free (a);
-      g_free (b);
-    }
-  
-  arg = shift ();
-  if (arg)
-    goto restart;
-  
-  return 0;
-}
-
-static void
-usage (void)
-{
-  g_print ("usage: mathtests {test} [args...]\n");
-  g_print ("tests:\n");
-  g_print ("  wave-scan <file>          scan a wave file for waves\n");
-  g_print ("  file-test <file>          test file properties\n");
-  g_print ("  ring-test                 test ring implementation\n");
-  g_print ("  rf <x> <y> <z>            Carlson's elliptic integral of the first kind\n");
-  g_print ("  F <phi> <ak>              Legendre elliptic integral of the 1st kind\n");
-  g_print ("  sn <u> <emmc>             Jacobian elliptic function sn()\n");
-  g_print ("  asn <y> <emmc>            elliptic integral, inverse sn()\n");
-  g_print ("  sin <phi.re> <phi.im>     complex sine\n");
-  g_print ("  cos <phi.re> <phi.im>     complex cosine\n");
-  g_print ("  tan <phi.re> <phi.im>     complex tangent\n");
-  g_print ("  sinh <phi.re> <phi.im>    complex hyperbolic sine\n");
-  g_print ("  cosh <phi.re> <phi.im>    complex hyperbolic cosine\n");
-  g_print ("  tanh <phi.re> <phi.im>    complex hyperbolic tangent\n");
-  g_print ("  midi2freq <midinote>      convert midinote into oscilaltor frequency\n");
-  g_print ("  snc <u.re> <u.im> <emmc.re> <emmc.im>     sn() for complex numbers\n");
-  g_print ("  asnc <y.re> <y.im> <emmc.re> <emmc.im>    asn() for complex numbers\n");
-  g_print ("  sci_sn <u> <k2>                           scilab version of sn()\n");
-  g_print ("  sci_asn <y> <k2>                          scilab version of asn()\n");
-  g_print ("  sci_snc <u.re> <u.im> <k2.re> <k2.im>     scilab version of snc()\n");
-  g_print ("  sci_asnc <y.re> <y.im> <k2.re> <k2.im>    scilab version of asnc()\n");
-  g_print ("  blp <order> <freq> <epsilon>              butterworth lowpass filter\n");
-  g_print ("  bhp <order> <freq> <epsilon>              butterworth higpass filter\n");
-  g_print ("  bbp <order> <freqc> <freqr> <epsilon>     butterworth bandpass filter\n");
-  g_print ("  t1l <order> <freq> <epsilon>              type1 tschebyscheff lowpass filter\n");
-  g_print ("  t1h <order> <freq> <epsilon>              type1 tschebyscheff highpass filter\n");
-  g_print ("  t1s <order> <freqc> <freqr> <epsilon>     type1 tschebyscheff bandstop filter\n");
-  g_print ("  t1p <order> <freqc> <freqr> <epsilon>     type1 tschebyscheff bandpass filter\n");
-  g_print ("  t2l <order> <freqc> <steepn> <epsilon>    type2 tschebyscheff lowpass filter\n");
-  g_print ("  t2h <order> <freqc> <steepn> <epsilon>    type2 tschebyscheff highpass filter\n");
-  g_print ("  fir <order> <freq1> <value1> ...          fir approximation\n");
-  g_print ("  scan blp <order> <freq> <epsilon>         scan butterworth lowpass filter\n");
-  g_print ("  poly | polyr | polyp                      polynom test (+roots or +poles)\n");
-  exit (1);
-}
-
-static void
-print_int_ring (SfiRing *ring)
-{
-  SfiRing *node;
-  g_print ("{");
-  for (node = ring; node; node = sfi_ring_walk (node, ring))
-    g_print ("%c", (gint) node->data);
-  g_print ("}");
-}
-
-static gint
-ints_cmp (gconstpointer d1,
-         gconstpointer d2,
-          gpointer      data)
-{
-  gint i1 = (gint) d1;
-  gint i2 = (gint) d2;
-  return i1 - i2;
-}
-
-static void
-ring_test (void)
-{
-  gint data_array[][64] = {
-    { 0, },
-    { 1, 'a', },
-    { 2, 'a', 'a', },
-    { 2, 'a', 'b', },
-    { 2, 'z', 'a', },
-    { 3, 'a', 'c', 'z' },
-    { 3, 'a', 'z', 'c' },
-    { 3, 'c', 'a', 'z' },
-    { 3, 'z', 'c', 'a' },
-    { 3, 'a', 'a', 'a' },
-    { 3, 'a', 'a', 'z' },
-    { 3, 'a', 'z', 'a' },
-    { 3, 'z', 'a', 'a' },
-    { 10, 'g', 's', 't', 'y', 'x', 'q', 'i', 'n', 'j', 'a' },
-    { 15, 'w', 'k', 't', 'o', 'c', 's', 'j', 'd', 'd', 'q', 'p', 'v', 'q', 'r', 'a' },
-    { 26, 'z', 'y', 'x', 'w', 'v', 'u', 't', 's', 'r', 'q', 'p', 'o', 'n', 'm'
-      ,   'l', 'k', 'j', 'i', 'h', 'g', 'f', 'e', 'd', 'c', 'b', 'a', },
-  };
-  gint n;
-
-  for (n = 0; n < G_N_ELEMENTS (data_array); n++)
-    {
-      gint i, l = data_array[n][0];
-      SfiRing *ring = NULL;
-      for (i = 1; i <= l; i++)
-       ring = sfi_ring_append (ring, (gpointer) (data_array[n][i]));
-      g_print ("source: ");
-      print_int_ring (ring);
-      ring = sfi_ring_sort (ring, ints_cmp, NULL);
-      g_print (" sorted: ");
-      print_int_ring (ring);
-      g_print ("\n");
-      sfi_ring_free (ring);
-    }
-}
-
-
-/* vim:set ts=8 sts=2 sw=2: */
diff --git a/tools/mathtool.cc b/tools/mathtool.cc
new file mode 100644 (file)
index 0000000..48d6e45
--- /dev/null
@@ -0,0 +1,739 @@
+/* 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 Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * A copy of the GNU Lesser General Public License should ship along
+ * with this library; if not, see http://www.gnu.org/copyleft/.
+ */
+#define GSL_EXTENSIONS
+#include <bse/bsemain.h>
+#include <bse/gslcommon.h>
+#include <bse/gslfilter.h>
+#include <bse/bseloader.h>
+#include <bse/bsemathsignal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define PREC    "15"
+
+
+static void    usage (void)    G_GNUC_NORETURN;
+
+static void  ring_test (void);
+
+static guint         shift_argc = 0;
+static const gchar **shift_argv = NULL;
+
+static const gchar*
+shift (void)
+{
+  const gchar *arg;
+  
+  if (shift_argc > 1)
+    {
+      shift_argc--;
+      arg = shift_argv++[1];
+      if (!arg)
+       arg = "";
+    }
+  else
+    arg = NULL;
+  return arg;
+}
+static const gchar*
+pshift (void)
+{
+  const gchar *arg = shift ();
+  
+  return arg ? arg : "";
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+  const gchar *arg;
+  /* iir filter parameters */
+  enum { FILTER_GNUPLOT, FILTER_SCAN } filter_mode = FILTER_GNUPLOT;
+  const gchar *filter_label = 0;
+  gdouble *a, *b;
+  guint i, order = 0;
+  
+  shift_argc = argc;
+  shift_argv = (const gchar**) argv;
+
+  /* init */
+  SfiInitValue values[] = {
+    { "stand-alone",            "true" }, /* no rcfiles etc. */
+    { "wave-chunk-padding",     NULL, 1, },
+    { "dcache-block-size",      NULL, 8192, },
+    { "dcache-cache-memory",    NULL, 5 * 1024 * 1024, },
+    { NULL }
+  };
+  bse_init_inprocess (&argc, &argv, NULL, values);
+
+  arg = shift ();
+  if (!arg)
+    usage ();
+  
+ restart:
+  a = b = 0;
+  
+  if (strcmp (arg, "approx5-exp2-run") == 0)
+    {
+      gfloat f;
+      gdouble r = 0;
+      for (i = 0; i < 10; i++)
+       for (f = -1; f <= 1.0; f += 1.0 / 10000000.0)
+         r += bse_approx5_exp2 (f);
+      return (r > 0) & 8;
+    }
+  else if (strcmp (arg, "libc-exp-run") == 0)
+    {
+      gfloat f;
+      gdouble r = 0;
+      for (i = 0; i < 10; i++)
+       for (f = -1; f <= 1.0; f += 1.0 / 10000000.0)
+         r += exp (f);
+      return (r > 0) & 8;
+    }
+  else if (strcmp (arg, "wave-scan") == 0)
+    {
+      const gchar *file = pshift ();
+      
+      while (file)
+       {
+         BseWaveFileInfo *fi;
+         BseErrorType error;
+         
+         fi = bse_wave_file_info_load (file, &error);
+         if (fi)
+           {
+             guint i;
+             
+             g_print ("Loader \"%s\" found %u waves in \"%s\":\n", fi->loader->name, fi->n_waves, file);
+             for (i = 0; i < fi->n_waves; i++)
+               g_print ("%u) %s\n", i + 1, fi->waves[i].name);
+             bse_wave_file_info_unref (fi);
+           }
+         else
+           g_print ("Failed to scan \"%s\": %s\n", file, bse_error_blurb (error));
+         file = pshift ();
+         if (!file[0])
+           break;
+       }
+    }
+  else if (strcmp (arg, "file-test") == 0)
+    {
+      const gchar *file = pshift ();
+      
+      g_print ("file test for \"%s\":\n", file);
+      g_print ("  is readable   : %s\n", bse_error_blurb (gsl_file_check (file, "r")));
+      g_print ("  is writable   : %s\n", bse_error_blurb (gsl_file_check (file, "w")));
+      g_print ("  is executable : %s\n", bse_error_blurb (gsl_file_check (file, "x")));
+      g_print ("  is file       : %s\n", bse_error_blurb (gsl_file_check (file, "f")));
+      g_print ("  is directory  : %s\n", bse_error_blurb (gsl_file_check (file, "d")));
+      g_print ("  is link       : %s\n", bse_error_blurb (gsl_file_check (file, "l")));
+    }
+  else if (strcmp (arg, "ring-test") == 0)
+    {
+      ring_test ();
+    }
+#if 0
+  else if (strcmp (arg, "rf") == 0)
+    {
+      double x, y, z;
+      x = atof (pshift ());
+      y = atof (pshift ());
+      z = atof (pshift ());
+      
+      g_print ("rf(%f, %f, %f) = %."PREC"f\n", x, y, z, gsl_ellip_rf (x, y, z));
+    }
+  else if (strcmp (arg, "F") == 0)
+    {
+      double phi, ak;
+      phi = atof (pshift ());
+      ak = atof (pshift ());
+      
+      g_print ("F(%f, %f) = %."PREC"f\n", phi, ak, gsl_ellip_F (phi, ak));
+    }
+  else if (strcmp (arg, "sn") == 0)
+    {
+      double u, emmc;
+      u = atof (pshift ());
+      emmc = atof (pshift ());
+      
+      g_print ("sn(%f, %f) = %."PREC"f\n", u, emmc, gsl_ellip_sn (u, emmc));
+    }
+  else if (strcmp (arg, "snc") == 0)
+    {
+      BseComplex u, emmc;
+      u.re = atof (pshift ());
+      u.im = atof (pshift ());
+      emmc.re = atof (pshift ());
+      emmc.im = atof (pshift ());
+      
+      g_print ("snc(%s, %s) = %s\n",
+              bse_complex_str (u),
+              bse_complex_str (emmc),
+              bse_complex_str (bse_complex_ellip_sn (u, emmc)));
+    }
+  else if (strcmp (arg, "sci_snc") == 0)
+    {
+      BseComplex u, k2;
+      u.re = atof (pshift ());
+      u.im = atof (pshift ());
+      k2.re = atof (pshift ());
+      k2.im = atof (pshift ());
+      
+      g_print ("sci_snc(%s, %s) = %s\n",
+              bse_complex_str (u),
+              bse_complex_str (k2),
+              bse_complex_str (bse_complex_ellip_sn (u, bse_complex_sub (bse_complex (1.0, 0), k2))));
+    }
+  else if (strcmp (arg, "asn") == 0)
+    {
+      double y, emmc;
+      y = atof (pshift ());
+      emmc = atof (pshift ());
+      
+      g_print ("asn(%f, %f) = %."PREC"f\n", y, emmc, gsl_ellip_asn (y, emmc));
+    }
+  else if (strcmp (arg, "asnc") == 0)
+    {
+      BseComplex y, emmc;
+      y.re = atof (pshift ());
+      y.im = atof (pshift ());
+      emmc.re = atof (pshift ());
+      emmc.im = atof (pshift ());
+      
+      g_print ("asnc(%s, %s) = %s\n",
+              bse_complex_str (y), bse_complex_str (emmc),
+              bse_complex_str (bse_complex_ellip_asn (y, emmc)));
+      g_print ("asn(%f, %f = %."PREC"f\n",
+              y.re, emmc.re, gsl_ellip_asn (y.re, emmc.re));
+    }
+  else if (strcmp (arg, "sci_sn") == 0)
+    {
+      double u, k2;
+      u = atof (pshift ());
+      k2 = atof (pshift ());
+      g_print ("sci_sn(%f, %f) = %."PREC"f\n", u, k2, gsl_ellip_sn (u, 1.0 - k2));
+    }
+  else if (strcmp (arg, "sci_asn") == 0)
+    {
+      double y, k2;
+      y = atof (pshift ());
+      k2 = atof (pshift ());
+      g_print ("sci_asn(%f, %f) = %."PREC"f\n", y, k2, gsl_ellip_asn (y, 1.0 - k2));
+    }
+  else if (strcmp (arg, "sci_asnc") == 0)
+    {
+      BseComplex y, k2;
+      y.re = atof (pshift ());
+      y.im = atof (pshift ());
+      k2.re = atof (pshift ());
+      k2.im = atof (pshift ());
+      g_print ("sci_asnc(%s, %s) = %s\n",
+              bse_complex_str (y), bse_complex_str (k2),
+              bse_complex_str (bse_complex_ellip_asn (y, bse_complex_sub (bse_complex (1.0, 0), k2))));
+      g_print ("asn(%f, %f = %."PREC"f\n",
+              y.re, k2.re, gsl_ellip_asn (y.re, 1.0 - k2.re));
+    }
+  else if (strncmp (arg, "poly", 4) == 0)
+    {
+      guint order;
+      arg = arg + 4;
+      order = 2;
+      {
+       double a[100] = { 1, 2, 1 }, b[100] = { 1, -3./2., 0.5 };
+       g_print ("# Test order=%u  norm=%f:\n",
+                order,
+                bse_poly_eval (order, a, 1) / bse_poly_eval (order, b, 1));
+       g_print ("H%u(z)=%s/%s\n", order,
+                bse_poly_str (order, a, "z"),
+                bse_poly_str (order, b, "z"));
+       if (*arg)
+         {
+           BseComplex root, roots[100];
+           guint i;
+           
+           if (*arg == 'r')
+             {
+               g_print ("#roots:\n");
+               bse_poly_complex_roots (order, a, roots);
+               for (i = 0; i < order; i++)
+                 {
+                   root = bse_complex_div (bse_complex (1, 0), roots[i]);
+                   g_print ("%+.14f %+.14f # %.14f\n", root.re, root.im, bse_complex_abs (root));
+                 }
+             }
+           if (*arg == 'p')
+             {
+               g_print ("#poles:\n");
+               bse_poly_complex_roots (order, b, roots);
+               for (i = 0; i < order; i++)
+                 {
+                   root = bse_complex_div (bse_complex (1, 0), roots[i]);
+                   g_print ("%+.14f %+.14f # %.14f\n", root.re, root.im, bse_complex_abs (root));
+                 }
+             }
+         }
+      }
+    }
+#endif
+  else if (strcmp (arg, "sin") == 0)
+    {
+      BseComplex phi;
+      phi.re = atof (pshift ());
+      phi.im = atof (pshift ());
+      g_print ("sin(%s) = %s\n",
+              bse_complex_str (phi),
+              bse_complex_str (bse_complex_sin (phi)));
+    }
+  else if (strcmp (arg, "cos") == 0)
+    {
+      BseComplex phi;
+      phi.re = atof (pshift ());
+      phi.im = atof (pshift ());
+      g_print ("cos(%s) = %s\n",
+              bse_complex_str (phi),
+              bse_complex_str (bse_complex_cos (phi)));
+    }
+  else if (strcmp (arg, "tan") == 0)
+    {
+      BseComplex phi;
+      phi.re = atof (pshift ());
+      phi.im = atof (pshift ());
+      g_print ("tan(%s) = %s\n",
+              bse_complex_str (phi),
+              bse_complex_str (bse_complex_tan (phi)));
+    }
+  else if (strcmp (arg, "sinh") == 0)
+    {
+      BseComplex phi;
+      phi.re = atof (pshift ());
+      phi.im = atof (pshift ());
+      g_print ("sinh(%s) = %s\n",
+              bse_complex_str (phi),
+              bse_complex_str (bse_complex_sinh (phi)));
+    }
+  else if (strcmp (arg, "cosh") == 0)
+    {
+      BseComplex phi;
+      phi.re = atof (pshift ());
+      phi.im = atof (pshift ());
+      g_print ("cosh(%s) = %s\n",
+              bse_complex_str (phi),
+              bse_complex_str (bse_complex_cosh (phi)));
+    }
+  else if (strcmp (arg, "tanh") == 0)
+    {
+      BseComplex phi;
+      phi.re = atof (pshift ());
+      phi.im = atof (pshift ());
+      g_print ("tanh(%s) = %s\n",
+              bse_complex_str (phi),
+              bse_complex_str (bse_complex_tanh (phi)));
+    }
+  else if (strcmp (arg, "midi2freq") == 0)
+    {
+      gint note;
+      note = atol (pshift ());
+      note = CLAMP (note, 0, 128);
+      g_print ("midi2freq(%u) = %f\n",
+              note,
+              bse_temp_freq (BSE_CONFIG (kammer_freq), note - BSE_CONFIG (midi_kammer_note)));
+    }
+  else if (strcmp (arg, "blp") == 0)
+    {
+      double f, e;
+      order = atoi (pshift ()); order = MAX (order, 1);
+      f = atof (pshift ());
+      e = atof (pshift ());
+      f *= PI / 2.;
+      a = g_new (gdouble, order + 1);
+      b = g_new (gdouble, order + 1);
+      gsl_filter_butter_lp (order, f, e, a, b);
+      g_print ("# Lowpass Butterworth filter order=%u freq=%f epsilon(s^2)=%f norm0=%f:\n",
+              order, f, e,
+              bse_poly_eval (order, a, 1) / bse_poly_eval (order, b, 1));
+      filter_label = "BL";
+    }
+  else if (strcmp (arg, "bhp") == 0)
+    {
+      double f, e;
+      order = atoi (pshift ()); order = MAX (order, 1);
+      f = atof (pshift ());
+      e = atof (pshift ());
+      f *= PI / 2.;
+      a = g_new (gdouble, order + 1);
+      b = g_new (gdouble, order + 1);
+      
+      gsl_filter_butter_hp (order, f, e, a, b);
+      g_print ("# Highpass Butterworth filter order=%u freq=%f epsilon(s^2)=%f norm0=%f:\n",
+              order, f, e,
+              bse_poly_eval (order, a, 1) / bse_poly_eval (order, b, 1));
+      filter_label = "BH";
+    }
+  else if (strcmp (arg, "bbp") == 0)
+    {
+      double f1, f2, e;
+      order = atoi (pshift ()); order = MAX (order, 1);
+      f1 = atof (pshift ());
+      f2 = atof (pshift ());
+      e = atof (pshift ());
+      f1 *= PI / 2.;
+      f2 *= PI / 2.;
+      a = g_new (gdouble, order + 1);
+      b = g_new (gdouble, order + 1);
+      
+      gsl_filter_butter_bp (order, f1, f2, e, a, b);
+      g_print ("# Bandpass Butterworth filter order=%u freq1=%f freq2=%f epsilon(s^2)=%f norm0=%f:\n",
+              order, f1, f2, e,
+              bse_poly_eval (order, a, 1) / bse_poly_eval (order, b, 1));
+      filter_label = "BP";
+    }
+  else if (strcmp (arg, "bbs") == 0)
+    {
+      double f1, f2, e;
+      order = atoi (pshift ()); order = MAX (order, 1);
+      f1 = atof (pshift ());
+      f2 = atof (pshift ());
+      e = atof (pshift ());
+      f1 *= PI / 2.;
+      f2 *= PI / 2.;
+      a = g_new (gdouble, order + 1);
+      b = g_new (gdouble, order + 1);
+      
+      gsl_filter_butter_bs (order, f1, f2, e, a, b);
+      g_print ("# Bandstop Butterworth filter order=%u freq1=%f freq2=%f epsilon(s^2)=%f norm0=%f:\n",
+              order, f1, f2, e,
+              bse_poly_eval (order, a, 1) / bse_poly_eval (order, b, 1));
+      filter_label = "BS";
+    }
+  else if (strcmp (arg, "t1l") == 0)
+    {
+      double f, e;
+      order = atoi (pshift ()); order = MAX (order, 1);
+      f = atof (pshift ());
+      e = atof (pshift ());
+      f *= PI / 2.;
+      a = g_new (gdouble, order + 1);
+      b = g_new (gdouble, order + 1);
+      
+      gsl_filter_tscheb1_lp (order, f, e, a, b);
+      g_print ("# Lowpass Tschebyscheff Type1 order=%u freq=%f epsilon(s^2)=%f norm0=%f:\n",
+              order, f, e,
+              bse_poly_eval (order, a, 1) / bse_poly_eval (order, b, 1));
+      filter_label = "T1L";
+    }
+  else if (strcmp (arg, "t1h") == 0)
+    {
+      double f, e;
+      order = atoi (pshift ()); order = MAX (order, 1);
+      f = atof (pshift ());
+      e = atof (pshift ());
+      f *= PI / 2.;
+      
+      a = g_new (gdouble, order + 1);
+      b = g_new (gdouble, order + 1);
+      
+      gsl_filter_tscheb1_hp (order, f, e, a, b);
+      g_print ("# Highpass Tschebyscheff Type1 order=%u freq=%f epsilon(s^2)=%f norm0=%f:\n",
+              order, f, e,
+              bse_poly_eval (order, a, 1) / bse_poly_eval (order, b, 1));
+      filter_label = "T1H";
+    }
+  else if (strcmp (arg, "t1s") == 0)
+    {
+      double fc, fr, e;
+      order = atoi (pshift ()); order = MAX (order, 1);
+      fc = atof (pshift ());
+      fr = atof (pshift ());
+      e = atof (pshift ());
+      fc *= PI / 2.;
+      fr *= PI / 2.;
+      
+      a = g_new (gdouble, order + 1);
+      b = g_new (gdouble, order + 1);
+      
+      gsl_filter_tscheb1_bs (order, fc, fr, e, a, b);
+      g_print ("# Bandstop Tschebyscheff Type1 order=%u freq_c=%f freq_r=%f epsilon(s^2)=%f norm=%f:\n",
+              order, fc, fr, e,
+              bse_poly_eval (order, a, 1) / bse_poly_eval (order, b, 1));
+      filter_label = "T1S";
+    }
+  else if (strcmp (arg, "t1p") == 0)
+    {
+      double fc, fr, e;
+      order = atoi (pshift ()); order = MAX (order, 1);
+      fc = atof (pshift ());
+      fr = atof (pshift ());
+      e = atof (pshift ());
+      fc *= PI / 2.;
+      fr *= PI / 2.;
+      
+      a = g_new (gdouble, order + 1);
+      b = g_new (gdouble, order + 1);
+      
+      gsl_filter_tscheb1_bp (order, fc, fr, e, a, b);
+      g_print ("# Bandpass Tschebyscheff Type1 order=%u freq_c=%f freq_r=%f epsilon(s^2)=%f norm=%f:\n",
+              order, fc, fr, e,
+              bse_poly_eval (order, a, 1) / bse_poly_eval (order, b, 1));
+      filter_label = "T1P";
+    }
+  else if (strcmp (arg, "t2l") == 0)
+    {
+      double f, st, e;
+      order = atoi (pshift ()); order = MAX (order, 1);
+      f = atof (pshift ());
+      st = atof (pshift ());
+      e = atof (pshift ());
+      f *= PI / 2.;
+      
+      a = g_new (gdouble, order + 1);
+      b = g_new (gdouble, order + 1);
+      
+      gsl_filter_tscheb2_lp (order, f, st, e, a, b);
+      g_print ("# Lowpass Tschebyscheff Type2 order=%u freq=%f steepness=%f (%f) epsilon(s^2)=%f norm=%f:\n",
+              order, f, st, f * (1.+st), e,
+              bse_poly_eval (order, a, 1) / bse_poly_eval (order, b, 1));
+      filter_label = "T2L";
+    }
+  else if (strcmp (arg, "t2h") == 0)
+    {
+      double f, st, e;
+      order = atoi (pshift ()); order = MAX (order, 1);
+      f = atof (pshift ());
+      st = atof (pshift ());
+      e = atof (pshift ());
+      f *= PI / 2.;
+      
+      a = g_new (gdouble, order + 1);
+      b = g_new (gdouble, order + 1);
+      
+      gsl_filter_tscheb2_hp (order, f, st, e, a, b);
+      g_print ("# Highpass Tschebyscheff Type2 order=%u freq=%f steepness=%f (%f, %f) epsilon(s^2)=%f norm=%f:\n",
+              order, f, st, PI - f, (PI - f) * (1.+st), e,
+              bse_poly_eval (order, a, 1) / bse_poly_eval (order, b, 1));
+      filter_label = "T2H";
+    }
+  else if (strcmp (arg, "t2p") == 0)
+    {
+      double f1, f2, st, e;
+      order = atoi (pshift ()); order = MAX (order, 1);
+      f1 = atof (pshift ());
+      f2 = atof (pshift ());
+      st = atof (pshift ());
+      e = atof (pshift ());
+      f1 *= PI / 2.;
+      f2 *= PI / 2.;
+      
+      a = g_new (gdouble, order + 1);
+      b = g_new (gdouble, order + 1);
+      
+      gsl_filter_tscheb2_bp (order, f1, f2, st, e, a, b);
+      g_print ("# Bandpass Tschebyscheff Type2 order=%u freq1=%f freq2=%f steepness=%f epsilon(s^2)=%f norm=%f:\n",
+              order, f1, f2, st, e,
+              bse_poly_eval (order, a, 1) / bse_poly_eval (order, b, 1));
+      filter_label = "T2P";
+    }
+  else if (strcmp (arg, "t2s") == 0)
+    {
+      double f1, f2, st, e;
+      order = atoi (pshift ()); order = MAX (order, 1);
+      f1 = atof (pshift ());
+      f2 = atof (pshift ());
+      st = atof (pshift ());
+      e = atof (pshift ());
+      f1 *= PI / 2.;
+      f2 *= PI / 2.;
+      
+      a = g_new (gdouble, order + 1);
+      b = g_new (gdouble, order + 1);
+      
+      gsl_filter_tscheb2_bs (order, f1, f2, st, e, a, b);
+      g_print ("# Bandstop Tschebyscheff Type2 order=%u freq1=%f freq2=%f steepness=%f epsilon(s^2)=%f norm=%f:\n",
+              order, f1, f2, st, e,
+              bse_poly_eval (order, a, 1) / bse_poly_eval (order, b, 1));
+      filter_label = "T2S";
+    }
+  else if (strcmp (arg, "scan") == 0)
+    {
+      filter_mode = FILTER_SCAN;
+    }
+  else if (strcmp (arg, "fir") == 0)
+    {
+      unsigned int iorder = atoi (pshift ());
+      unsigned int n_points = 0;
+      
+      double *freq = g_newa (double, argc / 2 + 1);
+      double *value = g_newa (double, argc / 2 + 1);
+      double *a = g_newa (double, iorder);
+      const char *f, *v;
+      
+      do
+       {
+         f = pshift ();
+         v = pshift ();
+         
+         if (f[0] && v[0])
+           {
+             freq[n_points] = atof (f) * PI;
+             value[n_points] = atof (v);
+             n_points++;
+           }
+       }
+      while (f[0] && v[0]);
+      
+      gsl_filter_fir_approx (iorder, a, n_points, freq, value, FALSE);
+      g_print ("FIR%u(z)=%s\n", iorder, bse_poly_str (iorder, a, "z"));
+    }
+  else
+    usage ();
+  
+  if (a && b)
+    {
+      gdouble freq;
+      
+      if (filter_mode == FILTER_SCAN)
+       {
+         freq = 0.001;
+         while (freq < 3.14)
+           {
+             g_print ("%f %.20f\n", freq, gsl_filter_sine_scan (order, a, b, freq, 2 * M_PI));
+             freq = MIN (freq * 1.1, freq + 0.01);
+           }
+       }
+      else if (filter_mode == FILTER_GNUPLOT)
+       {
+         g_print ("%s%u(z)=%s/%s\n", filter_label, order,
+                  bse_poly_str (order, a, "z"),
+                  bse_poly_str (order, b, "z"));
+       }
+      else
+       g_error ("unknown filter_mode");
+      g_free (a);
+      g_free (b);
+    }
+  
+  arg = shift ();
+  if (arg)
+    goto restart;
+  
+  return 0;
+}
+
+static void
+usage (void)
+{
+  g_print ("usage: mathtests {test} [args...]\n");
+  g_print ("tests:\n");
+  g_print ("  wave-scan <file>          scan a wave file for waves\n");
+  g_print ("  file-test <file>          test file properties\n");
+  g_print ("  ring-test                 test ring implementation\n");
+  g_print ("  rf <x> <y> <z>            Carlson's elliptic integral of the first kind\n");
+  g_print ("  F <phi> <ak>              Legendre elliptic integral of the 1st kind\n");
+  g_print ("  sn <u> <emmc>             Jacobian elliptic function sn()\n");
+  g_print ("  asn <y> <emmc>            elliptic integral, inverse sn()\n");
+  g_print ("  sin <phi.re> <phi.im>     complex sine\n");
+  g_print ("  cos <phi.re> <phi.im>     complex cosine\n");
+  g_print ("  tan <phi.re> <phi.im>     complex tangent\n");
+  g_print ("  sinh <phi.re> <phi.im>    complex hyperbolic sine\n");
+  g_print ("  cosh <phi.re> <phi.im>    complex hyperbolic cosine\n");
+  g_print ("  tanh <phi.re> <phi.im>    complex hyperbolic tangent\n");
+  g_print ("  midi2freq <midinote>      convert midinote into oscilaltor frequency\n");
+  g_print ("  snc <u.re> <u.im> <emmc.re> <emmc.im>     sn() for complex numbers\n");
+  g_print ("  asnc <y.re> <y.im> <emmc.re> <emmc.im>    asn() for complex numbers\n");
+  g_print ("  sci_sn <u> <k2>                           scilab version of sn()\n");
+  g_print ("  sci_asn <y> <k2>                          scilab version of asn()\n");
+  g_print ("  sci_snc <u.re> <u.im> <k2.re> <k2.im>     scilab version of snc()\n");
+  g_print ("  sci_asnc <y.re> <y.im> <k2.re> <k2.im>    scilab version of asnc()\n");
+  g_print ("  blp <order> <freq> <epsilon>              butterworth lowpass filter\n");
+  g_print ("  bhp <order> <freq> <epsilon>              butterworth higpass filter\n");
+  g_print ("  bbp <order> <freqc> <freqr> <epsilon>     butterworth bandpass filter\n");
+  g_print ("  t1l <order> <freq> <epsilon>              type1 tschebyscheff lowpass filter\n");
+  g_print ("  t1h <order> <freq> <epsilon>              type1 tschebyscheff highpass filter\n");
+  g_print ("  t1s <order> <freqc> <freqr> <epsilon>     type1 tschebyscheff bandstop filter\n");
+  g_print ("  t1p <order> <freqc> <freqr> <epsilon>     type1 tschebyscheff bandpass filter\n");
+  g_print ("  t2l <order> <freqc> <steepn> <epsilon>    type2 tschebyscheff lowpass filter\n");
+  g_print ("  t2h <order> <freqc> <steepn> <epsilon>    type2 tschebyscheff highpass filter\n");
+  g_print ("  fir <order> <freq1> <value1> ...          fir approximation\n");
+  g_print ("  scan blp <order> <freq> <epsilon>         scan butterworth lowpass filter\n");
+  g_print ("  poly | polyr | polyp                      polynom test (+roots or +poles)\n");
+  exit (1);
+}
+
+static void
+print_int_ring (SfiRing *ring)
+{
+  SfiRing *node;
+  g_print ("{");
+  for (node = ring; node; node = sfi_ring_walk (node, ring))
+    g_print ("%c", (gint) (long) node->data);
+  g_print ("}");
+}
+
+static gint
+ints_cmp (gconstpointer d1,
+         gconstpointer d2,
+          gpointer      data)
+{
+  gint i1 = (gint) (long) d1;
+  gint i2 = (gint) (long) d2;
+  return i1 - i2;
+}
+
+static void
+ring_test (void)
+{
+  gint data_array[][64] = {
+    { 0, },
+    { 1, 'a', },
+    { 2, 'a', 'a', },
+    { 2, 'a', 'b', },
+    { 2, 'z', 'a', },
+    { 3, 'a', 'c', 'z' },
+    { 3, 'a', 'z', 'c' },
+    { 3, 'c', 'a', 'z' },
+    { 3, 'z', 'c', 'a' },
+    { 3, 'a', 'a', 'a' },
+    { 3, 'a', 'a', 'z' },
+    { 3, 'a', 'z', 'a' },
+    { 3, 'z', 'a', 'a' },
+    { 10, 'g', 's', 't', 'y', 'x', 'q', 'i', 'n', 'j', 'a' },
+    { 15, 'w', 'k', 't', 'o', 'c', 's', 'j', 'd', 'd', 'q', 'p', 'v', 'q', 'r', 'a' },
+    { 26, 'z', 'y', 'x', 'w', 'v', 'u', 't', 's', 'r', 'q', 'p', 'o', 'n', 'm'
+      ,   'l', 'k', 'j', 'i', 'h', 'g', 'f', 'e', 'd', 'c', 'b', 'a', },
+  };
+  gint n;
+
+  for (n = 0; n < G_N_ELEMENTS (data_array); n++)
+    {
+      gint i, l = data_array[n][0];
+      SfiRing *ring = NULL;
+      for (i = 1; i <= l; i++)
+       ring = sfi_ring_append (ring, (gpointer) (data_array[n][i]));
+      g_print ("source: ");
+      print_int_ring (ring);
+      ring = sfi_ring_sort (ring, ints_cmp, NULL);
+      g_print (" sorted: ");
+      print_int_ring (ring);
+      g_print ("\n");
+      sfi_ring_free (ring);
+    }
+}
+
+
+/* vim:set ts=8 sts=2 sw=2: */
diff --git a/tools/sfiutils.c b/tools/sfiutils.c
deleted file mode 100644 (file)
index 53d6beb..0000000
+++ /dev/null
@@ -1,623 +0,0 @@
-/* SFI - Synthesis Fusion Kit Interface
- * Copyright (C) 2000-2004 Tim Janik
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 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
- * Lesser General Public License for more details.
- *
- * A copy of the GNU Lesser General Public License should ship along
- * with this library; if not, see http://www.gnu.org/copyleft/.
- */
-#include "sfiutils.h"
-#include <bse/gslcommon.h>
-#include <bse/bsemath.h>
-#include <string.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-/* --- option parser (YES!) --- */
-static SfiRing*
-parse_arguments (gint              *argc_p,
-                 gchar           ***argv_p,
-                 guint              n_arguments,
-                 const SfiArgument *arguments,
-                 gboolean           enlist)
-{
-  static const gchar *success_const = "1";
-  guint argc = *argc_p;
-  gchar **argv = *argv_p,  *unknown_short = NULL, *extraneous_arg = NULL;
-  guint *lengths = alloca (sizeof (guint) * n_arguments);
-  guint i, k, l, missing = n_arguments, extraneous = n_arguments;
-  SfiRing *ring = NULL;
-  
-  for (i = 0; i < n_arguments; i++)
-    {
-      g_return_val_if_fail (arguments[i].value_p != NULL, NULL);
-      
-      lengths[i] = arguments[i].long_opt ? strlen (arguments[i].long_opt) : 0;
-    }
-  
-  for (i = 1; i < argc; i++)
-    {
-      gchar *opt = argv[i];
-      
-      l = strlen (opt);
-      if (l > 2 && opt[0] == '-' && opt[1] == '-')
-        {
-          opt += 2;
-          l -= 2;
-          for (k = 0; k < n_arguments; k++)       /* find option */
-            if ((l == lengths[k] || (l > lengths[k] && opt[lengths[k]] == '=')) &&
-                strncmp (arguments[k].long_opt, opt, lengths[k]) == 0)
-              break;
-          if (k < n_arguments)                    /* found one */
-            {
-              if (l > lengths[k])               /* --foo=XXX option */
-                {
-                  if (arguments[k].takes_arg)
-                    *(arguments[k].value_p) = opt + lengths[k] + 1;
-                  else
-                    {
-                      extraneous = k;
-                      break;
-                    }
-                }
-              else if (arguments[k].takes_arg)
-                {
-                  if (i + 1 >= argc)            /* --foo needs an arg */
-                    {
-                      missing = k;
-                      break;
-                    }
-                  *(arguments[k].value_p) = argv[i + 1];
-                  argv[i] = NULL;               /* eat --foo XXX argument */
-                  i++;
-                }
-              else
-                *(arguments[k].value_p) = success_const;
-              argv[i] = NULL;
-              continue;
-            }
-          extraneous_arg = opt - 2;
-          break;
-        }
-      if (opt[0] == '-')
-        {
-          gboolean found_short = FALSE;
-          
-          if (opt[1] == '-' && !opt[2])         /* abort on "--" */
-            break;
-          opt += 1;
-          l -= 1;
-        next_short_opt:
-          for (k = 0; k < n_arguments; k++)       /* find option */
-            if (arguments[k].short_opt == *opt)
-              break;
-          if (k < n_arguments)                    /* found one */
-            {
-              found_short = TRUE;
-              if (arguments[k].takes_arg)
-                {
-                  if (opt[1])
-                    *(arguments[k].value_p) = opt + 1;
-                  else if (i + 1 >= argc)
-                    {
-                      missing = k;
-                      break;
-                    }
-                  else
-                    {
-                      *(arguments[k].value_p) = argv[i + 1];
-                      argv[i] = NULL;
-                      i++;
-                    }
-                }
-              else
-                {
-                  *(arguments[k].value_p) = success_const;
-                  opt += 1;
-                  l -= 1;
-                  if (*opt)
-                    goto next_short_opt;
-                }
-              argv[i] = NULL;
-              continue;
-            }
-          if (found_short)
-            {
-              unknown_short = opt;
-              break;
-            }
-          /* ok, treat as normal argument now */
-          opt -= 1;
-          l += 1;
-        }
-      /* ok, store away args */
-      for (k = 0; k < n_arguments; k++)       /* find option */
-        if (!arguments[k].short_opt && !arguments[k].long_opt && !*(arguments[k].value_p))
-          break;
-      if (k < n_arguments)
-        *(arguments[k].value_p) = opt;    /* found it */
-      else
-        {
-          if (enlist)
-            ring = sfi_ring_append (ring, opt);
-          else
-            {
-              extraneous_arg = opt;     /* FIXME: want this warning or not? */
-              break;
-            }
-        }
-      argv[i] = NULL;
-    }
-  
-  /* handle errors */
-  if (unknown_short || missing < n_arguments || extraneous < n_arguments || extraneous_arg)
-    {
-      if (unknown_short)
-        g_printerr ("unknown short options: \"%s\"\n", unknown_short);
-      if (missing < n_arguments)
-        if (arguments[missing].long_opt)
-          g_printerr ("missing option argument for \"--%s\"\n", arguments[missing].long_opt);
-        else
-          g_printerr ("missing option argument for \"-%c\"\n", arguments[missing].short_opt);
-      else
-        ;
-      if (extraneous < n_arguments)
-        g_printerr ("extraneous argument to option \"%s\"\n", arguments[extraneous].long_opt);
-      if (extraneous_arg)
-        g_printerr ("extraneous argument: \"%s\"\n", extraneous_arg);
-      exit (127);
-    }
-  
-  sfi_arguments_collapse (argc_p, argv_p);
-  
-  return ring;
-}
-
-void
-sfi_arguments_parse (gint              *argc_p,
-                     gchar           ***argv_p,
-                     guint              n_arguments,
-                     const SfiArgument *arguments)
-{
-  parse_arguments (argc_p, argv_p, n_arguments, arguments, FALSE);
-}
-
-SfiRing*
-sfi_arguments_parse_list (gint              *argc_p,
-                          gchar           ***argv_p,
-                          guint              n_arguments,
-                          const SfiArgument *arguments)
-{
-  return parse_arguments (argc_p, argv_p, n_arguments, arguments, TRUE);
-}
-
-void
-sfi_arguments_collapse (gint    *argc_p,
-                        gchar ***argv_p)
-{
-  gchar **argv = *argv_p;
-  guint argc = *argc_p, e, i;
-  
-  e = 1;
-  for (i = 1; i < argc; i++)
-    if (argv[i])
-      {
-        argv[e++] = argv[i];
-        if (i >= e)
-          argv[i] = NULL;
-      }
-  *argc_p = e;
-}
-
-
-gchar*
-sfi_util_file_name_subst_ext (const gchar *file_name,
-                              const gchar *new_extension)
-{
-  gchar *p, *name;
-  
-  g_return_val_if_fail (file_name != NULL, NULL);
-  g_return_val_if_fail (new_extension != NULL, NULL);
-  
-  name = g_strdup (file_name);
-  p = strrchr (name, '.');
-  if (p && p > name)
-    *p = 0;
-  p = g_strconcat (name, new_extension, NULL);
-  g_free (name);
-  name = p;
-  
-  return name;
-}
-
-
-/* --- word splitter --- */
-static guint
-upper_power2 (guint number)
-{
-  return number ? 1 << g_bit_storage (number - 1) : 0;
-}
-
-static void
-free_entry (SfiUtilFileList *flist)
-{
-  guint i, j;
-  
-  for (i = 0; i < flist->n_entries; i++)
-    {
-      for (j = 0; j < flist->entries[i].n_fields; j++)
-        g_free (flist->entries[i].fields[j]);
-      g_free (flist->entries[i].fields);
-    }
-  g_free (flist->entries);
-}
-
-void
-sfi_util_file_list_free (SfiUtilFileList *flist)
-{
-  free_entry (flist);
-  g_free (flist->formats);
-  g_free (flist->free1);
-  g_free (flist->free2);
-  g_free (flist);
-}
-
-SfiUtilFileList*
-sfi_util_file_list_read (gint fd)
-{
-  GScanner *scanner;
-  SfiUtilFileList flist = { 0, NULL };
-  SfiUtilFileEntry *entry = NULL;
-  gboolean line_start = TRUE;
-  gchar *cset_identifier;
-  guint i;
-  
-  scanner = g_scanner_new64 (NULL);
-  scanner->config->cset_skip_characters = " \t\r";      /* parse "\n" */
-  scanner->config->scan_identifier_1char = TRUE;
-  scanner->config->scan_symbols = FALSE;
-  scanner->config->scan_octal = FALSE;
-  scanner->config->scan_float = FALSE;
-  scanner->config->scan_hex = FALSE;
-  scanner->config->identifier_2_string = TRUE;
-  cset_identifier = g_new (gchar, 224);
-  for (i = 33; i < 255; i++)
-    cset_identifier[i - 33] = i;
-  cset_identifier[i - 33] = 0;
-  scanner->config->cset_identifier_first = cset_identifier;
-  scanner->config->cset_identifier_nth = cset_identifier;
-  g_scanner_input_file (scanner, fd);
-  while (g_scanner_get_next_token (scanner) != G_TOKEN_EOF &&
-         scanner->token != G_TOKEN_ERROR)
-    {
-      while (scanner->token == '\n')
-        {
-          g_scanner_get_next_token (scanner);
-          line_start = TRUE;
-        }
-      if (scanner->token == G_TOKEN_STRING)
-        {
-          if (line_start)
-            {
-              guint old_size = upper_power2 (flist.n_entries);
-              guint new_size = upper_power2 (++flist.n_entries);
-              
-              if (new_size >= old_size)
-                flist.entries = g_renew (SfiUtilFileEntry, flist.entries, new_size);
-              entry = flist.entries + flist.n_entries - 1;
-              entry->line_number = scanner->line;
-              entry->n_fields = 0;
-              entry->fields = NULL;
-            }
-          do
-            {
-              guint old_size = upper_power2 (entry->n_fields);
-              guint new_size = upper_power2 (++entry->n_fields);
-              
-              if (new_size >= old_size)
-                entry->fields = g_renew (gchar*, entry->fields, entry->n_fields);
-              entry->fields[entry->n_fields - 1] = g_strdup (scanner->value.v_string);
-              g_scanner_get_next_token (scanner);
-            }
-          while (scanner->token == G_TOKEN_STRING);
-        }
-      if (scanner->token != '\n')
-        {
-          g_scanner_unexp_token (scanner, '\n', NULL, NULL, NULL, NULL, FALSE);
-          g_scanner_get_next_token (scanner);
-        }
-    }
-  g_scanner_destroy (scanner);
-  g_free (cset_identifier);
-  
-  return g_memdup (&flist, sizeof (flist));
-}
-
-SfiUtilFileList*
-sfi_util_file_list_read_simple (const gchar *file_name,
-                                guint        n_formats,
-                                const gchar *formats)
-{
-  SfiUtilFileList *flist;
-  gchar *s;
-  guint i;
-  gint fd;
-  
-  g_return_val_if_fail (file_name != NULL, NULL);
-  g_return_val_if_fail (n_formats < 1000, NULL);
-  
-  fd = open (file_name, O_RDONLY);
-  if (fd < 0)
-    return NULL;
-  flist = sfi_util_file_list_read (fd);
-  close (fd);
-  if (!flist)
-    return NULL;
-  
-  flist->n_formats = n_formats;
-  flist->formats = g_new (gchar*, flist->n_formats);
-  s = g_new (gchar, flist->n_formats * 4);
-  flist->free1 = s;
-  for (i = 0; i < flist->n_formats; i++)
-    {
-      /* default initialize formats to { "000", "001", "002", ... } */
-      flist->formats[i] = s;
-      *s++ = i / 100 + '0';
-      *s++ = (i % 100) / 10 + '0';
-      *s++ = (i % 10) + '0';
-      *s++ = 0;
-    }
-  s = g_strdup (formats);
-  flist->free2 = s;
-  for (i = 0; s && *s && i < flist->n_formats; i++)
-    {
-      gchar *next = strchr (s, ':');
-      
-      if (next)
-        *next++ = 0;
-      flist->formats[i] = s;
-      s = next;
-    }
-  return flist;
-}
-
-
-/* --- formatted field extraction --- */
-static gdouble
-str2num (const gchar *str,
-         guint        nth)
-{
-  gchar *num_any = ".0123456789", *num_first = num_any + 1;
-  
-  while (nth--)
-    {
-      /* skip number */
-      if (*str && strchr (num_first, *str))
-        do
-          str++;
-        while (*str && strchr (num_any, *str));
-      /* and trailing non-number stuff */
-      while (*str && !strchr (num_first, *str))
-        str++;
-      if (!*str)
-        return BSE_DOUBLE_NAN;
-    }
-  if (strchr (num_first, *str))
-    return atof (str);
-  return BSE_DOUBLE_NAN;
-}
-
-const gchar*
-sfi_util_file_entry_get_field (SfiUtilFileEntry *entry,
-                               const gchar     **format_p)
-{
-  const gchar *format = *format_p;
-  gchar *field, *ep = NULL;
-  glong fnum;
-  
-  *format_p = NULL;
-  if (*format == '#')
-    return format + 1;
-  fnum = strtol (format, &ep, 10);
-  if (fnum < 1)
-    sfi_error ("Invalid chunk format given (index:%ld < 1): \"%s\"", fnum, format);
-  field = fnum <= entry->n_fields ? entry->fields[fnum - 1] : NULL;
-  if (field && ep && *ep)
-    *format_p = ep;
-  return field;
-}
-
-gchar*
-sfi_util_file_entry_get_string (SfiUtilFileEntry *entry,
-                                const gchar      *format,
-                                const gchar      *dflt)
-{
-  const gchar *field;
-  
-  field = sfi_util_file_entry_get_field (entry, &format);
-  if (!field)
-    return g_strdup (dflt);
-  if (format)
-    sfi_error ("Invalid chunk format given: ...%s", format);
-  return g_strdup (field);
-}
-
-gdouble
-sfi_util_file_entry_get_num (SfiUtilFileEntry *entry,
-                             const gchar      *format,
-                             gdouble           dflt)
-{
-  const gchar *field = sfi_util_file_entry_get_field (entry, &format);
-  gchar *base, *ep = NULL;
-  gdouble d = dflt;
-  
-  if (!field)
-    return d;
-  if (format)
-    {
-      switch (*format)
-        {
-          glong l;
-        case 'n':
-          l = strtol (++format, &ep, 10);
-          d = str2num (field, l);
-          break;
-        case 'b':
-          l = strtol (++format, &ep, 10);
-          base = strrchr (field, '/');
-          d = str2num  (base ? base : field, l);
-          break;
-        default:
-          sfi_error ("Invalid chunk format given: modifier `%c'", *format);
-        }
-      if (ep && *ep)
-        {
-          if (*ep == 'm')
-            d = bse_temp_freq (gsl_get_config ()->kammer_freq,
-                               d - gsl_get_config ()->midi_kammer_note);
-          else
-            sfi_error ("Invalid chunk format given: postmodifier `%c'", *ep);
-        }
-    }
-  else
-    d = str2num (field, 0);
-  return d;
-}
-
-gdouble
-sfi_arguments_extract_num (const gchar *string,
-                           const gchar *format,
-                           gdouble     *counter,
-                           gdouble      dflt)
-{
-  gchar *base, *ep = NULL;
-  gdouble d = dflt;
-  
-  if (!string)
-    return d;
-  if (format)
-    {
-      switch (*format)
-        {
-          glong l;
-        case '#':
-          d = str2num (++format, 0);
-          break;
-        case 'n':
-          l = strtol (++format, &ep, 10);
-          d = str2num (string, l);
-          break;
-        case 'b':
-          l = strtol (++format, &ep, 10);
-          base = strrchr (string, '/');
-          d = str2num  (base ? base : string, l);
-          break;
-        case 'c':
-          format++;
-          d = *counter;
-          if (*format == '*')
-            {
-              l = strtol (++format, &ep, 10);
-              d *= l;
-            }
-          else
-            ep = (char*) format;
-          break;
-        default:
-          sfi_error ("Invalid chunk format given: modifier `%c'", *format);
-        }
-      if (ep && *ep)
-        {
-          if (*ep == 'm')       /* interpret d as midi note and return freq */
-            d = bse_temp_freq (gsl_get_config ()->kammer_freq,
-                               d - gsl_get_config ()->midi_kammer_note);
-          else
-            sfi_error ("Invalid chunk format given: postmodifier `%c'", *ep);
-        }
-    }
-  else
-    d = str2num (string, 0);
-  *counter += 1;
-  return d;
-}
-
-gboolean
-sfi_arguments_read_num (const gchar **option,
-                        gdouble      *num)
-{
-  const gchar *opt, *spaces = " \t\n";
-  gchar *p = NULL;
-  gdouble d;
-  
-  if (!option)
-    return FALSE;
-  if (!*option)
-    {
-      *option = NULL;
-      return FALSE;
-    }
-  opt = *option;
-  while (*opt && strchr (spaces, *opt))
-    opt++;
-  if (!*opt)
-    {
-      *option = NULL;
-      return TRUE;      /* last, default */
-    }
-  if (*opt == ':')
-    {
-      *option = opt + 1;
-      return TRUE;      /* any, default */
-    }
-  d = g_strtod (*option, &p);
-  if (p)
-    while (*p && strchr (spaces, *p))
-      p++;
-  if (!p || !*p)
-    {
-      *option = NULL;
-      *num = d;
-      return TRUE;      /* last, valid */
-    }
-  if (*p != ':')
-    {
-      *option = NULL;
-      return FALSE;
-    }
-  *option = p + 1;
-  *num = d;
-  return TRUE;          /* any, valid */
-}
-
-guint
-sfi_arguments_read_all_nums (const gchar *option,
-                             gdouble     *first,
-                             ...)
-{
-  va_list args;
-  gdouble *d;
-  guint n = 0;
-  
-  va_start (args, first);
-  d = first;
-  while (d)
-    {
-      if (!sfi_arguments_read_num (&option, d))
-        break;
-      n++;
-      d = va_arg (args, gdouble*);
-    }
-  va_end (args);
-  return n;
-}
diff --git a/tools/sfiutils.cc b/tools/sfiutils.cc
new file mode 100644 (file)
index 0000000..53d6beb
--- /dev/null
@@ -0,0 +1,623 @@
+/* SFI - Synthesis Fusion Kit Interface
+ * Copyright (C) 2000-2004 Tim Janik
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * A copy of the GNU Lesser General Public License should ship along
+ * with this library; if not, see http://www.gnu.org/copyleft/.
+ */
+#include "sfiutils.h"
+#include <bse/gslcommon.h>
+#include <bse/bsemath.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+/* --- option parser (YES!) --- */
+static SfiRing*
+parse_arguments (gint              *argc_p,
+                 gchar           ***argv_p,
+                 guint              n_arguments,
+                 const SfiArgument *arguments,
+                 gboolean           enlist)
+{
+  static const gchar *success_const = "1";
+  guint argc = *argc_p;
+  gchar **argv = *argv_p,  *unknown_short = NULL, *extraneous_arg = NULL;
+  guint *lengths = alloca (sizeof (guint) * n_arguments);
+  guint i, k, l, missing = n_arguments, extraneous = n_arguments;
+  SfiRing *ring = NULL;
+  
+  for (i = 0; i < n_arguments; i++)
+    {
+      g_return_val_if_fail (arguments[i].value_p != NULL, NULL);
+      
+      lengths[i] = arguments[i].long_opt ? strlen (arguments[i].long_opt) : 0;
+    }
+  
+  for (i = 1; i < argc; i++)
+    {
+      gchar *opt = argv[i];
+      
+      l = strlen (opt);
+      if (l > 2 && opt[0] == '-' && opt[1] == '-')
+        {
+          opt += 2;
+          l -= 2;
+          for (k = 0; k < n_arguments; k++)       /* find option */
+            if ((l == lengths[k] || (l > lengths[k] && opt[lengths[k]] == '=')) &&
+                strncmp (arguments[k].long_opt, opt, lengths[k]) == 0)
+              break;
+          if (k < n_arguments)                    /* found one */
+            {
+              if (l > lengths[k])               /* --foo=XXX option */
+                {
+                  if (arguments[k].takes_arg)
+                    *(arguments[k].value_p) = opt + lengths[k] + 1;
+                  else
+                    {
+                      extraneous = k;
+                      break;
+                    }
+                }
+              else if (arguments[k].takes_arg)
+                {
+                  if (i + 1 >= argc)            /* --foo needs an arg */
+                    {
+                      missing = k;
+                      break;
+                    }
+                  *(arguments[k].value_p) = argv[i + 1];
+                  argv[i] = NULL;               /* eat --foo XXX argument */
+                  i++;
+                }
+              else
+                *(arguments[k].value_p) = success_const;
+              argv[i] = NULL;
+              continue;
+            }
+          extraneous_arg = opt - 2;
+          break;
+        }
+      if (opt[0] == '-')
+        {
+          gboolean found_short = FALSE;
+          
+          if (opt[1] == '-' && !opt[2])         /* abort on "--" */
+            break;
+          opt += 1;
+          l -= 1;
+        next_short_opt:
+          for (k = 0; k < n_arguments; k++)       /* find option */
+            if (arguments[k].short_opt == *opt)
+              break;
+          if (k < n_arguments)                    /* found one */
+            {
+              found_short = TRUE;
+              if (arguments[k].takes_arg)
+                {
+                  if (opt[1])
+                    *(arguments[k].value_p) = opt + 1;
+                  else if (i + 1 >= argc)
+                    {
+                      missing = k;
+                      break;
+                    }
+                  else
+                    {
+                      *(arguments[k].value_p) = argv[i + 1];
+                      argv[i] = NULL;
+                      i++;
+                    }
+                }
+              else
+                {
+                  *(arguments[k].value_p) = success_const;
+                  opt += 1;
+                  l -= 1;
+                  if (*opt)
+                    goto next_short_opt;
+                }
+              argv[i] = NULL;
+              continue;
+            }
+          if (found_short)
+            {
+              unknown_short = opt;
+              break;
+            }
+          /* ok, treat as normal argument now */
+          opt -= 1;
+          l += 1;
+        }
+      /* ok, store away args */
+      for (k = 0; k < n_arguments; k++)       /* find option */
+        if (!arguments[k].short_opt && !arguments[k].long_opt && !*(arguments[k].value_p))
+          break;
+      if (k < n_arguments)
+        *(arguments[k].value_p) = opt;    /* found it */
+      else
+        {
+          if (enlist)
+            ring = sfi_ring_append (ring, opt);
+          else
+            {
+              extraneous_arg = opt;     /* FIXME: want this warning or not? */
+              break;
+            }
+        }
+      argv[i] = NULL;
+    }
+  
+  /* handle errors */
+  if (unknown_short || missing < n_arguments || extraneous < n_arguments || extraneous_arg)
+    {
+      if (unknown_short)
+        g_printerr ("unknown short options: \"%s\"\n", unknown_short);
+      if (missing < n_arguments)
+        if (arguments[missing].long_opt)
+          g_printerr ("missing option argument for \"--%s\"\n", arguments[missing].long_opt);
+        else
+          g_printerr ("missing option argument for \"-%c\"\n", arguments[missing].short_opt);
+      else
+        ;
+      if (extraneous < n_arguments)
+        g_printerr ("extraneous argument to option \"%s\"\n", arguments[extraneous].long_opt);
+      if (extraneous_arg)
+        g_printerr ("extraneous argument: \"%s\"\n", extraneous_arg);
+      exit (127);
+    }
+  
+  sfi_arguments_collapse (argc_p, argv_p);
+  
+  return ring;
+}
+
+void
+sfi_arguments_parse (gint              *argc_p,
+                     gchar           ***argv_p,
+                     guint              n_arguments,
+                     const SfiArgument *arguments)
+{
+  parse_arguments (argc_p, argv_p, n_arguments, arguments, FALSE);
+}
+
+SfiRing*
+sfi_arguments_parse_list (gint              *argc_p,
+                          gchar           ***argv_p,
+                          guint              n_arguments,
+                          const SfiArgument *arguments)
+{
+  return parse_arguments (argc_p, argv_p, n_arguments, arguments, TRUE);
+}
+
+void
+sfi_arguments_collapse (gint    *argc_p,
+                        gchar ***argv_p)
+{
+  gchar **argv = *argv_p;
+  guint argc = *argc_p, e, i;
+  
+  e = 1;
+  for (i = 1; i < argc; i++)
+    if (argv[i])
+      {
+        argv[e++] = argv[i];
+        if (i >= e)
+          argv[i] = NULL;
+      }
+  *argc_p = e;
+}
+
+
+gchar*
+sfi_util_file_name_subst_ext (const gchar *file_name,
+                              const gchar *new_extension)
+{
+  gchar *p, *name;
+  
+  g_return_val_if_fail (file_name != NULL, NULL);
+  g_return_val_if_fail (new_extension != NULL, NULL);
+  
+  name = g_strdup (file_name);
+  p = strrchr (name, '.');
+  if (p && p > name)
+    *p = 0;
+  p = g_strconcat (name, new_extension, NULL);
+  g_free (name);
+  name = p;
+  
+  return name;
+}
+
+
+/* --- word splitter --- */
+static guint
+upper_power2 (guint number)
+{
+  return number ? 1 << g_bit_storage (number - 1) : 0;
+}
+
+static void
+free_entry (SfiUtilFileList *flist)
+{
+  guint i, j;
+  
+  for (i = 0; i < flist->n_entries; i++)
+    {
+      for (j = 0; j < flist->entries[i].n_fields; j++)
+        g_free (flist->entries[i].fields[j]);
+      g_free (flist->entries[i].fields);
+    }
+  g_free (flist->entries);
+}
+
+void
+sfi_util_file_list_free (SfiUtilFileList *flist)
+{
+  free_entry (flist);
+  g_free (flist->formats);
+  g_free (flist->free1);
+  g_free (flist->free2);
+  g_free (flist);
+}
+
+SfiUtilFileList*
+sfi_util_file_list_read (gint fd)
+{
+  GScanner *scanner;
+  SfiUtilFileList flist = { 0, NULL };
+  SfiUtilFileEntry *entry = NULL;
+  gboolean line_start = TRUE;
+  gchar *cset_identifier;
+  guint i;
+  
+  scanner = g_scanner_new64 (NULL);
+  scanner->config->cset_skip_characters = " \t\r";      /* parse "\n" */
+  scanner->config->scan_identifier_1char = TRUE;
+  scanner->config->scan_symbols = FALSE;
+  scanner->config->scan_octal = FALSE;
+  scanner->config->scan_float = FALSE;
+  scanner->config->scan_hex = FALSE;
+  scanner->config->identifier_2_string = TRUE;
+  cset_identifier = g_new (gchar, 224);
+  for (i = 33; i < 255; i++)
+    cset_identifier[i - 33] = i;
+  cset_identifier[i - 33] = 0;
+  scanner->config->cset_identifier_first = cset_identifier;
+  scanner->config->cset_identifier_nth = cset_identifier;
+  g_scanner_input_file (scanner, fd);
+  while (g_scanner_get_next_token (scanner) != G_TOKEN_EOF &&
+         scanner->token != G_TOKEN_ERROR)
+    {
+      while (scanner->token == '\n')
+        {
+          g_scanner_get_next_token (scanner);
+          line_start = TRUE;
+        }
+      if (scanner->token == G_TOKEN_STRING)
+        {
+          if (line_start)
+            {
+              guint old_size = upper_power2 (flist.n_entries);
+              guint new_size = upper_power2 (++flist.n_entries);
+              
+              if (new_size >= old_size)
+                flist.entries = g_renew (SfiUtilFileEntry, flist.entries, new_size);
+              entry = flist.entries + flist.n_entries - 1;
+              entry->line_number = scanner->line;
+              entry->n_fields = 0;
+              entry->fields = NULL;
+            }
+          do
+            {
+              guint old_size = upper_power2 (entry->n_fields);
+              guint new_size = upper_power2 (++entry->n_fields);
+              
+              if (new_size >= old_size)
+                entry->fields = g_renew (gchar*, entry->fields, entry->n_fields);
+              entry->fields[entry->n_fields - 1] = g_strdup (scanner->value.v_string);
+              g_scanner_get_next_token (scanner);
+            }
+          while (scanner->token == G_TOKEN_STRING);
+        }
+      if (scanner->token != '\n')
+        {
+          g_scanner_unexp_token (scanner, '\n', NULL, NULL, NULL, NULL, FALSE);
+          g_scanner_get_next_token (scanner);
+        }
+    }
+  g_scanner_destroy (scanner);
+  g_free (cset_identifier);
+  
+  return g_memdup (&flist, sizeof (flist));
+}
+
+SfiUtilFileList*
+sfi_util_file_list_read_simple (const gchar *file_name,
+                                guint        n_formats,
+                                const gchar *formats)
+{
+  SfiUtilFileList *flist;
+  gchar *s;
+  guint i;
+  gint fd;
+  
+  g_return_val_if_fail (file_name != NULL, NULL);
+  g_return_val_if_fail (n_formats < 1000, NULL);
+  
+  fd = open (file_name, O_RDONLY);
+  if (fd < 0)
+    return NULL;
+  flist = sfi_util_file_list_read (fd);
+  close (fd);
+  if (!flist)
+    return NULL;
+  
+  flist->n_formats = n_formats;
+  flist->formats = g_new (gchar*, flist->n_formats);
+  s = g_new (gchar, flist->n_formats * 4);
+  flist->free1 = s;
+  for (i = 0; i < flist->n_formats; i++)
+    {
+      /* default initialize formats to { "000", "001", "002", ... } */
+      flist->formats[i] = s;
+      *s++ = i / 100 + '0';
+      *s++ = (i % 100) / 10 + '0';
+      *s++ = (i % 10) + '0';
+      *s++ = 0;
+    }
+  s = g_strdup (formats);
+  flist->free2 = s;
+  for (i = 0; s && *s && i < flist->n_formats; i++)
+    {
+      gchar *next = strchr (s, ':');
+      
+      if (next)
+        *next++ = 0;
+      flist->formats[i] = s;
+      s = next;
+    }
+  return flist;
+}
+
+
+/* --- formatted field extraction --- */
+static gdouble
+str2num (const gchar *str,
+         guint        nth)
+{
+  gchar *num_any = ".0123456789", *num_first = num_any + 1;
+  
+  while (nth--)
+    {
+      /* skip number */
+      if (*str && strchr (num_first, *str))
+        do
+          str++;
+        while (*str && strchr (num_any, *str));
+      /* and trailing non-number stuff */
+      while (*str && !strchr (num_first, *str))
+        str++;
+      if (!*str)
+        return BSE_DOUBLE_NAN;
+    }
+  if (strchr (num_first, *str))
+    return atof (str);
+  return BSE_DOUBLE_NAN;
+}
+
+const gchar*
+sfi_util_file_entry_get_field (SfiUtilFileEntry *entry,
+                               const gchar     **format_p)
+{
+  const gchar *format = *format_p;
+  gchar *field, *ep = NULL;
+  glong fnum;
+  
+  *format_p = NULL;
+  if (*format == '#')
+    return format + 1;
+  fnum = strtol (format, &ep, 10);
+  if (fnum < 1)
+    sfi_error ("Invalid chunk format given (index:%ld < 1): \"%s\"", fnum, format);
+  field = fnum <= entry->n_fields ? entry->fields[fnum - 1] : NULL;
+  if (field && ep && *ep)
+    *format_p = ep;
+  return field;
+}
+
+gchar*
+sfi_util_file_entry_get_string (SfiUtilFileEntry *entry,
+                                const gchar      *format,
+                                const gchar      *dflt)
+{
+  const gchar *field;
+  
+  field = sfi_util_file_entry_get_field (entry, &format);
+  if (!field)
+    return g_strdup (dflt);
+  if (format)
+    sfi_error ("Invalid chunk format given: ...%s", format);
+  return g_strdup (field);
+}
+
+gdouble
+sfi_util_file_entry_get_num (SfiUtilFileEntry *entry,
+                             const gchar      *format,
+                             gdouble           dflt)
+{
+  const gchar *field = sfi_util_file_entry_get_field (entry, &format);
+  gchar *base, *ep = NULL;
+  gdouble d = dflt;
+  
+  if (!field)
+    return d;
+  if (format)
+    {
+      switch (*format)
+        {
+          glong l;
+        case 'n':
+          l = strtol (++format, &ep, 10);
+          d = str2num (field, l);
+          break;
+        case 'b':
+          l = strtol (++format, &ep, 10);
+          base = strrchr (field, '/');
+          d = str2num  (base ? base : field, l);
+          break;
+        default:
+          sfi_error ("Invalid chunk format given: modifier `%c'", *format);
+        }
+      if (ep && *ep)
+        {
+          if (*ep == 'm')
+            d = bse_temp_freq (gsl_get_config ()->kammer_freq,
+                               d - gsl_get_config ()->midi_kammer_note);
+          else
+            sfi_error ("Invalid chunk format given: postmodifier `%c'", *ep);
+        }
+    }
+  else
+    d = str2num (field, 0);
+  return d;
+}
+
+gdouble
+sfi_arguments_extract_num (const gchar *string,
+                           const gchar *format,
+                           gdouble     *counter,
+                           gdouble      dflt)
+{
+  gchar *base, *ep = NULL;
+  gdouble d = dflt;
+  
+  if (!string)
+    return d;
+  if (format)
+    {
+      switch (*format)
+        {
+          glong l;
+        case '#':
+          d = str2num (++format, 0);
+          break;
+        case 'n':
+          l = strtol (++format, &ep, 10);
+          d = str2num (string, l);
+          break;
+        case 'b':
+          l = strtol (++format, &ep, 10);
+          base = strrchr (string, '/');
+          d = str2num  (base ? base : string, l);
+          break;
+        case 'c':
+          format++;
+          d = *counter;
+          if (*format == '*')
+            {
+              l = strtol (++format, &ep, 10);
+              d *= l;
+            }
+          else
+            ep = (char*) format;
+          break;
+        default:
+          sfi_error ("Invalid chunk format given: modifier `%c'", *format);
+        }
+      if (ep && *ep)
+        {
+          if (*ep == 'm')       /* interpret d as midi note and return freq */
+            d = bse_temp_freq (gsl_get_config ()->kammer_freq,
+                               d - gsl_get_config ()->midi_kammer_note);
+          else
+            sfi_error ("Invalid chunk format given: postmodifier `%c'", *ep);
+        }
+    }
+  else
+    d = str2num (string, 0);
+  *counter += 1;
+  return d;
+}
+
+gboolean
+sfi_arguments_read_num (const gchar **option,
+                        gdouble      *num)
+{
+  const gchar *opt, *spaces = " \t\n";
+  gchar *p = NULL;
+  gdouble d;
+  
+  if (!option)
+    return FALSE;
+  if (!*option)
+    {
+      *option = NULL;
+      return FALSE;
+    }
+  opt = *option;
+  while (*opt && strchr (spaces, *opt))
+    opt++;
+  if (!*opt)
+    {
+      *option = NULL;
+      return TRUE;      /* last, default */
+    }
+  if (*opt == ':')
+    {
+      *option = opt + 1;
+      return TRUE;      /* any, default */
+    }
+  d = g_strtod (*option, &p);
+  if (p)
+    while (*p && strchr (spaces, *p))
+      p++;
+  if (!p || !*p)
+    {
+      *option = NULL;
+      *num = d;
+      return TRUE;      /* last, valid */
+    }
+  if (*p != ':')
+    {
+      *option = NULL;
+      return FALSE;
+    }
+  *option = p + 1;
+  *num = d;
+  return TRUE;          /* any, valid */
+}
+
+guint
+sfi_arguments_read_all_nums (const gchar *option,
+                             gdouble     *first,
+                             ...)
+{
+  va_list args;
+  gdouble *d;
+  guint n = 0;
+  
+  va_start (args, first);
+  d = first;
+  while (d)
+    {
+      if (!sfi_arguments_read_num (&option, d))
+        break;
+      n++;
+      d = va_arg (args, gdouble*);
+    }
+  va_end (args);
+  return n;
+}
diff --git a/tools/sfiutils.h b/tools/sfiutils.h
deleted file mode 100644 (file)
index 4d7d1f4..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-/* SFI - Synthesis Fusion Kit Interface
- * Copyright (C) 2000-2004 Tim Janik
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 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
- * Lesser General Public License for more details.
- *
- * A copy of the GNU Lesser General Public License should ship along
- * with this library; if not, see http://www.gnu.org/copyleft/.
- */
-#ifndef        __SFI_UTILS_H__
-#define        __SFI_UTILS_H__
-
-#include <math.h>
-#include <bse/gsldefs.h>
-#include <sfi/sfi.h>
-
-G_BEGIN_DECLS
-
-typedef struct
-{
-  guint   line_number;
-  guint   n_fields;
-  gchar **fields;
-} SfiUtilFileEntry;
-
-typedef struct
-{
-  guint             n_entries;
-  SfiUtilFileEntry *entries;
-  guint                    n_formats;
-  gchar           **formats;
-  gpointer          free1, free2;
-} SfiUtilFileList;
-
-/* value extraction from formats:
- * # <something>       -> value is <something>
- * <num>               -> value is <num> word of line
- * <num> n <nth>       -> <nth> number found in word <num>
- * <num> b <nth>       -> <nth> number found in basename(word <num>)
- */
-SfiUtilFileList* sfi_util_file_list_read       (gint                    fd);
-SfiUtilFileList* sfi_util_file_list_read_simple        (const gchar            *file_name,
-                                                guint                   n_formats,
-                                                const gchar            *formats);
-void            sfi_util_file_list_free        (SfiUtilFileList        *text);
-const gchar*    sfi_util_file_entry_get_field  (SfiUtilFileEntry       *entry,
-                                                const gchar           **format_p);
-gchar*          sfi_util_file_entry_get_string (SfiUtilFileEntry       *entry,
-                                                const gchar            *format,
-                                                const gchar            *dflt);
-gdouble                 sfi_util_file_entry_get_num    (SfiUtilFileEntry       *line,
-                                                const gchar            *format,
-                                                gdouble                 dflt);
-
-
-gchar*          sfi_util_file_name_subst_ext   (const gchar            *file_name,
-                                                const gchar            *new_extension);
-
-typedef struct {
-  gchar                short_opt;
-  gchar        *long_opt;
-  const gchar **value_p;
-  guint         takes_arg : 1;
-} SfiArgument;
-void       sfi_arguments_parse         (gint            *argc_p,
-                                        gchar         ***argv_p,
-                                        guint            n_options,
-                                        const SfiArgument *options);
-SfiRing*    sfi_arguments_parse_list   (gint            *argc_p,
-                                        gchar         ***argv_p,
-                                        guint            n_options,
-                                        const SfiArgument *options);
-void       sfi_arguments_collapse      (gint            *argc_p,
-                                        gchar         ***argv_p);
-
-/* format for value extraction:
- * # <something>       -> string is <something>
- * n <nth>             -> <nth> number found in string
- * b <nth>             -> <nth> number found in basename(string)
- * c [*<num>]          -> counter (with optional multiplication)
- * if <nth>==0, number may not be preceeded by non-digit chars
- */
-gdouble            sfi_arguments_extract_num   (const gchar    *string,
-                                         const gchar   *format,
-                                         gdouble       *counter,
-                                         gdouble        dflt);
-gboolean    sfi_arguments_read_num     (const gchar   **option,
-                                        gdouble        *num);
-guint      sfi_arguments_read_all_nums (const gchar    *option,
-                                        gdouble        *first,
-                                        ...) G_GNUC_NULL_TERMINATED;
-
-G_END_DECLS
-
-#endif /* __SFI_UTILS_H__ */
diff --git a/tools/sfiutils.hh b/tools/sfiutils.hh
new file mode 100644 (file)
index 0000000..4d7d1f4
--- /dev/null
@@ -0,0 +1,102 @@
+/* SFI - Synthesis Fusion Kit Interface
+ * Copyright (C) 2000-2004 Tim Janik
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * A copy of the GNU Lesser General Public License should ship along
+ * with this library; if not, see http://www.gnu.org/copyleft/.
+ */
+#ifndef        __SFI_UTILS_H__
+#define        __SFI_UTILS_H__
+
+#include <math.h>
+#include <bse/gsldefs.h>
+#include <sfi/sfi.h>
+
+G_BEGIN_DECLS
+
+typedef struct
+{
+  guint   line_number;
+  guint   n_fields;
+  gchar **fields;
+} SfiUtilFileEntry;
+
+typedef struct
+{
+  guint             n_entries;
+  SfiUtilFileEntry *entries;
+  guint                    n_formats;
+  gchar           **formats;
+  gpointer          free1, free2;
+} SfiUtilFileList;
+
+/* value extraction from formats:
+ * # <something>       -> value is <something>
+ * <num>               -> value is <num> word of line
+ * <num> n <nth>       -> <nth> number found in word <num>
+ * <num> b <nth>       -> <nth> number found in basename(word <num>)
+ */
+SfiUtilFileList* sfi_util_file_list_read       (gint                    fd);
+SfiUtilFileList* sfi_util_file_list_read_simple        (const gchar            *file_name,
+                                                guint                   n_formats,
+                                                const gchar            *formats);
+void            sfi_util_file_list_free        (SfiUtilFileList        *text);
+const gchar*    sfi_util_file_entry_get_field  (SfiUtilFileEntry       *entry,
+                                                const gchar           **format_p);
+gchar*          sfi_util_file_entry_get_string (SfiUtilFileEntry       *entry,
+                                                const gchar            *format,
+                                                const gchar            *dflt);
+gdouble                 sfi_util_file_entry_get_num    (SfiUtilFileEntry       *line,
+                                                const gchar            *format,
+                                                gdouble                 dflt);
+
+
+gchar*          sfi_util_file_name_subst_ext   (const gchar            *file_name,
+                                                const gchar            *new_extension);
+
+typedef struct {
+  gchar                short_opt;
+  gchar        *long_opt;
+  const gchar **value_p;
+  guint         takes_arg : 1;
+} SfiArgument;
+void       sfi_arguments_parse         (gint            *argc_p,
+                                        gchar         ***argv_p,
+                                        guint            n_options,
+                                        const SfiArgument *options);
+SfiRing*    sfi_arguments_parse_list   (gint            *argc_p,
+                                        gchar         ***argv_p,
+                                        guint            n_options,
+                                        const SfiArgument *options);
+void       sfi_arguments_collapse      (gint            *argc_p,
+                                        gchar         ***argv_p);
+
+/* format for value extraction:
+ * # <something>       -> string is <something>
+ * n <nth>             -> <nth> number found in string
+ * b <nth>             -> <nth> number found in basename(string)
+ * c [*<num>]          -> counter (with optional multiplication)
+ * if <nth>==0, number may not be preceeded by non-digit chars
+ */
+gdouble            sfi_arguments_extract_num   (const gchar    *string,
+                                         const gchar   *format,
+                                         gdouble       *counter,
+                                         gdouble        dflt);
+gboolean    sfi_arguments_read_num     (const gchar   **option,
+                                        gdouble        *num);
+guint      sfi_arguments_read_all_nums (const gchar    *option,
+                                        gdouble        *first,
+                                        ...) G_GNUC_NULL_TERMINATED;
+
+G_END_DECLS
+
+#endif /* __SFI_UTILS_H__ */