You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
463 lines
13 KiB
463 lines
13 KiB
15 years ago
|
/* 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: */
|