/* 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., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "gsldatautils.h" #include "gsldatacache.h" #include #include #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; }