summaryrefslogtreecommitdiffstats
path: root/flow/gsl/gsldatautils.c
diff options
context:
space:
mode:
Diffstat (limited to 'flow/gsl/gsldatautils.c')
-rw-r--r--flow/gsl/gsldatautils.c462
1 files changed, 462 insertions, 0 deletions
diff --git a/flow/gsl/gsldatautils.c b/flow/gsl/gsldatautils.c
new file mode 100644
index 0000000..d8c52c0
--- /dev/null
+++ b/flow/gsl/gsldatautils.c
@@ -0,0 +1,462 @@
+/* GSL - Generic Sound Layer
+ * Copyright (C) 2001-2002 Stefan Westerfeld and 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 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.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#include "gsldatautils.h"
+#include "gsldatacache.h"
+#include <errno.h>
+#include <unistd.h>
+
+
+#define BSIZE GSL_DATA_HANDLE_PEEK_BUFFER /* FIXME: global buffer size setting */
+
+
+/* --- functions --- */
+gfloat
+gsl_data_peek_value_f (GslDataHandle *dhandle,
+ GslLong pos,
+ GslDataPeekBuffer *peekbuf)
+{
+ if (pos < peekbuf->start || pos >= peekbuf->end)
+ {
+ GslLong dhandle_length = dhandle->setup.n_values;
+ GslLong inc, k, bsize = MIN (GSL_DATA_HANDLE_PEEK_BUFFER, dhandle_length);
+
+ g_return_val_if_fail (pos >= 0 && pos < dhandle_length, 0);
+
+ peekbuf->start = peekbuf->dir > 0 ? pos : peekbuf->dir < 0 ? pos - bsize + 1: pos - bsize / 2;
+ peekbuf->end = MIN (peekbuf->start + bsize, dhandle_length);
+ peekbuf->start = MAX (peekbuf->start, 0);
+ for (k = peekbuf->start; k < peekbuf->end; k += inc)
+ {
+ guint n_retries = 5; /* FIXME: need global retry strategy */
+
+ do
+ inc = gsl_data_handle_read (dhandle, k, peekbuf->end - k, peekbuf->data + k - peekbuf->start);
+ while (inc < 1 && n_retries-- && GSL_DATA_HANDLE_OPENED (dhandle));
+ if (inc < 1)
+ { /* pathologic */
+ peekbuf->data[k - peekbuf->start] = 0;
+ inc = 1;
+ gsl_message_send (GSL_MSG_DATA_HANDLE, "PeekBuffer",
+ GSL_ERROR_READ_FAILED, "unable to read from data handle (%p)", dhandle);
+ }
+ }
+ }
+ return peekbuf->data[pos - peekbuf->start];
+}
+
+gint /* errno */
+gsl_data_handle_dump (GslDataHandle *dhandle,
+ gint fd,
+ GslWaveFormatType format,
+ guint byte_order)
+{
+ GslLong l, offs = 0;
+
+ g_return_val_if_fail (dhandle != NULL, EINVAL);
+ g_return_val_if_fail (GSL_DATA_HANDLE_OPENED (dhandle), EINVAL);
+ g_return_val_if_fail (fd >= 0, EINVAL);
+ g_return_val_if_fail (format >= GSL_WAVE_FORMAT_UNSIGNED_8 && format <= GSL_WAVE_FORMAT_FLOAT, EINVAL);
+ g_return_val_if_fail (byte_order == G_LITTLE_ENDIAN || byte_order == G_BIG_ENDIAN, EINVAL);
+
+ l = dhandle->setup.n_values;
+ while (l)
+ {
+ GslLong retry, j, n = MIN (l, GSL_DATA_HANDLE_PEEK_BUFFER);
+ gfloat src[GSL_DATA_HANDLE_PEEK_BUFFER];
+
+ retry = GSL_N_IO_RETRIES;
+ do
+ n = gsl_data_handle_read (dhandle, offs, n, src);
+ while (n < 1 && retry--);
+ if (retry < 0)
+ return EIO;
+
+ l -= n;
+ offs += n;
+
+ n = gsl_conv_from_float_clip (format, byte_order, src, src, n);
+
+ do
+ j = write (fd, src, n);
+ while (j < 0 && errno == EINTR);
+ if (j < 0)
+ return errno ? errno : EIO;
+ }
+ return 0;
+}
+
+static void
+write_bytes (gint fd,
+ guint n_bytes,
+ const void *bytes)
+{
+ gint errold = errno;
+ guint j;
+
+ do
+ j = write (fd, bytes, n_bytes);
+ while (j < 0 && errno == EINTR);
+
+ if (!errno)
+ errno = errold;
+}
+
+static void
+write_uint32_le (gint fd,
+ guint32 val)
+{
+ val = GUINT32_TO_LE (val);
+ write_bytes (fd, 4, &val);
+}
+
+static void
+write_uint16_le (gint fd,
+ guint16 val)
+{
+ val = GUINT16_TO_LE (val);
+ write_bytes (fd, 2, &val);
+}
+
+gint /* errno */
+gsl_data_handle_dump_wav (GslDataHandle *dhandle,
+ gint fd,
+ guint n_bits,
+ guint n_channels,
+ guint sample_freq)
+{
+ guint data_length, file_length, byte_per_sample, byte_per_second;
+
+ g_return_val_if_fail (dhandle != NULL, EINVAL);
+ g_return_val_if_fail (GSL_DATA_HANDLE_OPENED (dhandle), EINVAL);
+ g_return_val_if_fail (fd >= 0, EINVAL);
+ g_return_val_if_fail (n_bits == 16 || n_bits == 8, EINVAL);
+ g_return_val_if_fail (n_channels >= 1, EINVAL);
+
+ data_length = dhandle->setup.n_values * (n_bits == 16 ? 2 : 1);
+ file_length = data_length;
+ file_length += 4 + 4; /* 'RIFF' header */
+ file_length += 4 + 4 + 2 + 2 + 4 + 4 + 2 + 2; /* 'fmt ' header */
+ file_length += 4 + 4; /* 'data' header */
+ byte_per_sample = (n_bits == 16 ? 2 : 1) * n_channels;
+ byte_per_second = byte_per_sample * sample_freq;
+
+ errno = 0;
+ write_bytes (fd, 4, "RIFF"); /* main_chunk */
+ write_uint32_le (fd, file_length);
+ write_bytes (fd, 4, "WAVE"); /* chunk_type */
+ write_bytes (fd, 4, "fmt "); /* sub_chunk */
+ write_uint32_le (fd, 16); /* sub chunk length */
+ write_uint16_le (fd, 1); /* format (1=PCM) */
+ write_uint16_le (fd, n_channels);
+ write_uint32_le (fd, sample_freq);
+ write_uint32_le (fd, byte_per_second);
+ write_uint16_le (fd, byte_per_sample);
+ write_uint16_le (fd, n_bits);
+ write_bytes (fd, 4, "data"); /* data chunk */
+ write_uint32_le (fd, data_length);
+
+ if (errno)
+ return errno;
+
+ return gsl_data_handle_dump (dhandle, fd,
+ n_bits == 16 ? GSL_WAVE_FORMAT_SIGNED_16 : GSL_WAVE_FORMAT_UNSIGNED_8,
+ G_LITTLE_ENDIAN);
+}
+
+gboolean
+gsl_data_detect_signal (GslDataHandle *handle,
+ GslLong *sigstart_p,
+ GslLong *sigend_p)
+{
+ gfloat level_0, level_1, level_2, level_3, level_4;
+ gfloat signal_threshold = 16. * 16. * 16.; /* noise level threshold */
+ GslLong k, xcheck = -1, minsamp = -1, maxsamp = -2;
+ GslDataPeekBuffer peek_buffer = { +1 /* incremental direction */, 0, };
+
+ g_return_val_if_fail (handle != NULL, FALSE);
+ g_return_val_if_fail (GSL_DATA_HANDLE_OPENED (handle), FALSE);
+ g_return_val_if_fail (sigstart_p || sigend_p, FALSE);
+
+ /* keep open */
+ gsl_data_handle_open (handle);
+
+ /* find fadein/fadeout point */
+ k = 0;
+ level_4 = gsl_data_handle_peek_value (handle, k, &peek_buffer);
+ level_4 *= 32768;
+ level_0 = level_1 = level_2 = level_3 = level_4;
+ for (; k < handle->setup.n_values; k++)
+ {
+ gfloat mean, needx, current;
+
+ current = gsl_data_handle_peek_value (handle, k, &peek_buffer) * 32768.;
+ if (xcheck < 0 && ABS (current) >= 16)
+ xcheck = k;
+ mean = (level_0 + level_1 + level_2 + level_3 + level_4) / 5;
+ needx = (ABS (level_4 + current - (level_0 + level_1 + level_2 + level_3) / 2) *
+ ABS (level_4 - mean) * ABS (current - mean));
+ /* shift */
+ level_0 = level_1; level_1 = level_2; level_2 = level_3; level_3 = level_4; level_4 = current;
+ /* aprox. noise compare */
+ if (ABS (needx) > signal_threshold)
+ {
+ if (minsamp < 0)
+ minsamp = k;
+ if (maxsamp < k)
+ maxsamp = k;
+ }
+ /* if (minsamp >= 0 && xcheck >= 0)
+ * break;
+ */
+ }
+ if (xcheck - minsamp > 0)
+ g_printerr("###################");
+ g_printerr ("active area %ld .. %ld, signal>16 at: %ld\t diff: %ld\n",minsamp,maxsamp,xcheck, xcheck-minsamp);
+
+ /* release open reference */
+ gsl_data_handle_close (handle);
+
+ if (sigstart_p)
+ *sigstart_p = minsamp;
+ if (sigend_p)
+ *sigend_p = MAX (-1, maxsamp);
+
+ return maxsamp >= minsamp;
+}
+
+GslLong
+gsl_data_find_sample (GslDataHandle *dhandle,
+ gfloat min_value,
+ gfloat max_value,
+ GslLong start_offset,
+ gint direction)
+{
+ GslDataPeekBuffer peekbuf = { 0, 0, 0, };
+ GslLong i;
+
+ g_return_val_if_fail (dhandle != NULL, -1);
+ g_return_val_if_fail (direction == -1 || direction == +1, -1);
+
+ if (gsl_data_handle_open (dhandle) != GSL_ERROR_NONE ||
+ start_offset >= dhandle->setup.n_values)
+ return -1;
+
+ if (start_offset < 0)
+ start_offset = dhandle->setup.n_values - 1;
+
+ peekbuf.dir = direction;
+ if (min_value <= max_value)
+ for (i = start_offset; i < dhandle->setup.n_values && i >= 0; i += direction)
+ {
+ gfloat val = gsl_data_handle_peek_value (dhandle, i, &peekbuf);
+
+ /* g_print ("(%lu): %f <= %f <= %f\n", i, min_value, val, max_value); */
+ if (val >= min_value && val <= max_value)
+ break;
+ }
+ else
+ for (i = start_offset; i < dhandle->setup.n_values && i >= 0; i += direction)
+ {
+ gfloat val = gsl_data_handle_peek_value (dhandle, i, &peekbuf);
+
+ /* g_print ("(%lu): %f > %f || %f < %f\n", i, val, max_value, val, min_value); */
+ if (val > min_value || val < max_value)
+ break;
+ }
+
+ gsl_data_handle_close (dhandle);
+
+ return i >= dhandle->setup.n_values ? -1: i;
+}
+
+static inline gdouble
+tailmatch_score_loop (GslDataHandle *shandle,
+ GslDataHandle *dhandle,
+ GslLong start,
+ gdouble worst_score)
+{
+ GslLong l, length = MIN (shandle->setup.n_values, dhandle->setup.n_values);
+ gfloat v1[GSL_DATA_HANDLE_PEEK_BUFFER], v2[GSL_DATA_HANDLE_PEEK_BUFFER];
+ gdouble score = 0;
+
+ g_assert (start < length);
+
+ for (l = start; l < length; )
+ {
+ GslLong b = MIN (GSL_DATA_HANDLE_PEEK_BUFFER, length - l);
+
+ b = gsl_data_handle_read (shandle, l, b, v1);
+ b = gsl_data_handle_read (dhandle, l, b, v2);
+ g_assert (b >= 1);
+ l += b;
+
+ while (b--)
+ score += (v1[b] - v2[b]) * (v1[b] - v2[b]);
+
+ /* for performance, prematurely abort */
+ if (score > worst_score)
+ break;
+ }
+ return score;
+}
+
+gboolean
+gsl_data_find_tailmatch (GslDataHandle *dhandle,
+ const GslLoopSpec *lspec,
+ GslLong *loop_start_p,
+ GslLong *loop_end_p)
+{
+ GslDataHandle *shandle;
+ GslDataCache *dcache;
+ GslLong length, offset, l, lsize, pcount, start = 0, end = 0;
+ gdouble pbound, pval, best_score = GSL_MAXLONG;
+
+ g_return_val_if_fail (dhandle != NULL, FALSE);
+ g_return_val_if_fail (lspec != NULL, FALSE);
+ g_return_val_if_fail (loop_start_p != NULL, FALSE);
+ g_return_val_if_fail (loop_end_p != NULL, FALSE);
+ g_return_val_if_fail (lspec->head_skip >= 0, FALSE);
+ g_return_val_if_fail (lspec->tail_cut >= 0, FALSE);
+ g_return_val_if_fail (lspec->min_loop >= 1, FALSE);
+ g_return_val_if_fail (lspec->max_loop >= lspec->min_loop, FALSE);
+ g_return_val_if_fail (lspec->tail_cut >= lspec->max_loop, FALSE);
+
+ if (gsl_data_handle_open (dhandle) != GSL_ERROR_NONE)
+ return FALSE;
+ length = dhandle->setup.n_values;
+ if (lspec->head_skip < length)
+ {
+ gsl_data_handle_close (dhandle);
+ return FALSE;
+ }
+ offset = lspec->head_skip;
+ length -= offset;
+ if (lspec->tail_cut < length)
+ {
+ gsl_data_handle_close (dhandle);
+ return FALSE;
+ }
+ length -= lspec->tail_cut;
+ if (lspec->max_loop <= length)
+ {
+ gsl_data_handle_close (dhandle);
+ return FALSE;
+ }
+
+ dcache = gsl_data_cache_new (dhandle, 1);
+ shandle = gsl_data_handle_new_dcached (dcache);
+ gsl_data_cache_unref (dcache);
+ gsl_data_handle_open (shandle);
+ gsl_data_handle_close (dhandle);
+ gsl_data_handle_unref (shandle);
+ /* at this point, we just hold one open() count on shandle */
+
+ pbound = (lspec->max_loop - lspec->min_loop + 1.);
+ pbound *= length / 100.;
+ pval = 0;
+ pcount = 100;
+
+ for (lsize = lspec->min_loop; lsize <= lspec->max_loop; lsize++)
+ {
+ for (l = length - lsize; l >= 0; l--)
+ {
+ GslDataHandle *lhandle = gsl_data_handle_new_looped (shandle, offset + l, offset + l + lsize - 1);
+ gdouble score;
+
+ gsl_data_handle_open (lhandle);
+ score = tailmatch_score_loop (shandle, lhandle, offset + l, best_score);
+ gsl_data_handle_close (lhandle);
+ gsl_data_handle_unref (lhandle);
+
+ if (score < best_score)
+ {
+ start = offset + l;
+ end = offset + l + lsize - 1;
+ g_print ("\nimproved: %f < %f: [0x%lx..0x%lx] (%lu)\n", score, best_score, start, end, lsize);
+ best_score = score;
+ }
+ else
+ break;
+ }
+ if (!pcount--)
+ {
+ pcount = 100;
+ pval = lsize - lspec->min_loop;
+ pbound = (lspec->max_loop - lspec->min_loop + 1.);
+ g_print ("\rprocessed: %f%% \r", pval / pbound);
+ }
+ }
+ gsl_data_handle_close (shandle);
+
+ g_print ("\nhalted: %f: [0x%lx..0x%lx] (%lu)\n", best_score, start, end, end - start + 1);
+
+ *loop_start_p = start;
+ *loop_end_p = end;
+
+ return TRUE;
+}
+
+/**
+ * gsl_data_find_block
+ * @handle: an open GslDataHandle
+ * @n_values: amount of values to look for
+ * @values: values to find
+ * @epsilon: maximum difference upon comparisons
+ * @RETURNS: position of values in data handle or -1
+ *
+ * Find the position of a block of values within a
+ * data handle, where all values compare to the reference
+ * values with a delta smaller than epsilon.
+ */
+GslLong
+gsl_data_find_block (GslDataHandle *handle,
+ guint n_values,
+ const gfloat *values,
+ gfloat epsilon)
+{
+ GslDataPeekBuffer pbuf = { +1 /* random access: 0 */ };
+ guint i;
+
+ g_return_val_if_fail (handle != NULL, -1);
+ g_return_val_if_fail (GSL_DATA_HANDLE_OPENED (handle), -1);
+
+ if (n_values < 1)
+ return -1;
+ else
+ g_return_val_if_fail (values != NULL, -1);
+
+ for (i = 0; i < handle->setup.n_values; i++)
+ {
+ guint j;
+
+ if (n_values > handle->setup.n_values - i)
+ return -1;
+
+ for (j = 0; j < n_values; j++)
+ {
+ if (fabs (values[j] - gsl_data_handle_peek_value (handle, i + j, &pbuf)) >= epsilon)
+ break;
+ }
+ if (j >= n_values)
+ return i;
+ }
+ return -1;
+}
+
+/* vim:set ts=8 sts=2 sw=2: */