|
|
|
/* 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 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 <gsl/gsldatahandle-mad.h>
|
|
|
|
|
|
|
|
#include "gslfilehash.h"
|
|
|
|
#include <gsl/gsldatautils.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/mman.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
#if GSL_HAVE_LIBMAD
|
|
|
|
#include <mad.h>
|
|
|
|
|
|
|
|
|
|
|
|
/* --- debugging and errors --- */
|
|
|
|
#define MAD_DEBUG GSL_DEBUG_FUNCTION (GSL_MSG_DATA_HANDLE, "MAD")
|
|
|
|
#define MAD_MSG GSL_MESSAGE_FUNCTION (GSL_MSG_DATA_HANDLE, "MAD")
|
|
|
|
|
|
|
|
|
|
|
|
/* --- defines --- */
|
|
|
|
#define FILE_BUFFER_SIZE (1024 * 44) /* approximately 1 second at 320 kbit */
|
|
|
|
#define SEEK_BY_READ_AHEAD(h) (((h)->sample_rate / ((h)->frame_size * 2))) /* FIXME */
|
|
|
|
#define MAX_CHANNELS (5)
|
|
|
|
|
|
|
|
|
|
|
|
/* --- typedefs & structures --- */
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
GslDataHandle dhandle;
|
|
|
|
|
|
|
|
/* setup data */
|
|
|
|
guint sample_rate;
|
|
|
|
guint frame_size;
|
|
|
|
guint stream_options;
|
|
|
|
guint accumulate_state_frames;
|
|
|
|
guint skip_seek_table : 1;
|
|
|
|
|
|
|
|
/* file IO */
|
|
|
|
guint eof : 1;
|
|
|
|
GslHFile *hfile;
|
|
|
|
guint file_pos;
|
|
|
|
const gchar *error;
|
|
|
|
|
|
|
|
/* seek table */
|
|
|
|
GTime seek_mtime;
|
|
|
|
guint n_seeks;
|
|
|
|
guint *seeks;
|
|
|
|
|
|
|
|
/* file read buffer */
|
|
|
|
guint bfill;
|
|
|
|
guint8 buffer[FILE_BUFFER_SIZE + MAD_BUFFER_GUARD];
|
|
|
|
|
|
|
|
/* pcm housekeeping */
|
|
|
|
GslLong pcm_pos, pcm_length, next_pcm_pos;
|
|
|
|
|
|
|
|
/* libmad structures */
|
|
|
|
struct mad_stream stream;
|
|
|
|
struct mad_frame frame;
|
|
|
|
struct mad_synth synth;
|
|
|
|
} MadHandle;
|
|
|
|
|
|
|
|
|
|
|
|
/* --- prototypes --- */
|
|
|
|
static GslLong dh_mad_coarse_seek (GslDataHandle *data_handle,
|
|
|
|
GslLong voffset);
|
|
|
|
|
|
|
|
|
|
|
|
/* --- functions --- */
|
|
|
|
static gboolean /* FALSE: handle->eof || errno != 0 */
|
|
|
|
stream_read (MadHandle *handle)
|
|
|
|
{
|
|
|
|
struct mad_stream *stream = &handle->stream;
|
|
|
|
guint l;
|
|
|
|
|
|
|
|
/* no further data to read (flag must be reset upon seeks) */
|
|
|
|
if (handle->eof)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
/* keep remaining data in buffer */
|
|
|
|
if (stream->next_frame && handle->bfill)
|
|
|
|
{
|
|
|
|
handle->bfill = handle->buffer + handle->bfill - stream->next_frame;
|
|
|
|
memmove (handle->buffer, stream->next_frame, handle->bfill);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* fill buffer */
|
|
|
|
l = gsl_hfile_pread (handle->hfile, handle->file_pos, FILE_BUFFER_SIZE - handle->bfill, handle->buffer + handle->bfill);
|
|
|
|
if (l > 0)
|
|
|
|
{
|
|
|
|
handle->bfill += l;
|
|
|
|
handle->file_pos += l;
|
|
|
|
}
|
|
|
|
else if (l == 0)
|
|
|
|
{
|
|
|
|
handle->eof = TRUE;
|
|
|
|
memset (handle->buffer + handle->bfill, 0, MAD_BUFFER_GUARD);
|
|
|
|
handle->bfill += MAD_BUFFER_GUARD;
|
|
|
|
handle->file_pos += MAD_BUFFER_GUARD; /* bogus, but doesn't matter at eof */
|
|
|
|
}
|
|
|
|
|
|
|
|
mad_stream_buffer (stream, handle->buffer, handle->bfill);
|
|
|
|
|
|
|
|
return l < 0 ? FALSE : TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
check_frame_validity (MadHandle *handle,
|
|
|
|
struct mad_header *header)
|
|
|
|
{
|
|
|
|
guint frame_size = MAD_NSBSAMPLES (header) * 32;
|
|
|
|
gchar *reason = NULL;
|
|
|
|
|
|
|
|
if (frame_size <= 0)
|
|
|
|
reason = "frame_size < 1";
|
|
|
|
|
|
|
|
if (handle->frame_size && handle->dhandle.setup.n_channels)
|
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
if (frame_size != handle->frame_size)
|
|
|
|
reason = "frame with non-standard size";
|
|
|
|
#endif
|
|
|
|
if (MAD_NCHANNELS (header) != handle->dhandle.setup.n_channels)
|
|
|
|
reason = "frame with non-standard channel count";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (reason)
|
|
|
|
{
|
|
|
|
MAD_DEBUG ("skipping frame: %s", reason);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
read_next_frame_header (MadHandle *handle)
|
|
|
|
{
|
|
|
|
gboolean succeeded = TRUE;
|
|
|
|
|
|
|
|
/* fetch next frame header */
|
|
|
|
if (mad_header_decode (&handle->frame.header, &handle->stream) < 0)
|
|
|
|
{
|
|
|
|
if (!MAD_RECOVERABLE (handle->stream.error) ||
|
|
|
|
handle->stream.error == MAD_ERROR_LOSTSYNC)
|
|
|
|
{
|
|
|
|
/* read on */
|
|
|
|
if (!stream_read (handle))
|
|
|
|
{
|
|
|
|
handle->error = handle->eof ? NULL : g_strerror (errno);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
return read_next_frame_header (handle); /* retry */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!check_frame_validity (handle, &handle->frame.header))
|
|
|
|
return read_next_frame_header (handle); /* retry */
|
|
|
|
|
|
|
|
succeeded = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
handle->error = handle->stream.error ? mad_stream_errorstr (&handle->stream) : NULL;
|
|
|
|
|
|
|
|
return succeeded;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean /* FALSE: handle->eof || handle->error != NULL */
|
|
|
|
pcm_frame_read (MadHandle *handle,
|
|
|
|
gboolean synth)
|
|
|
|
{
|
|
|
|
gboolean succeeded = TRUE;
|
|
|
|
|
|
|
|
if (mad_frame_decode (&handle->frame, &handle->stream) < 0)
|
|
|
|
{
|
|
|
|
if (!MAD_RECOVERABLE (handle->stream.error) ||
|
|
|
|
handle->stream.error == MAD_ERROR_LOSTSYNC)
|
|
|
|
{
|
|
|
|
/* MAD_RECOVERABLE()==TRUE: frame was read, decoding failed (about to skip frame)
|
|
|
|
* MAD_RECOVERABLE()==FALSE: frame was not read, need data
|
|
|
|
* note: MAD_RECOVERABLE (MAD_ERROR_LOSTSYNC) == TRUE
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* read on */
|
|
|
|
if (!stream_read (handle))
|
|
|
|
{
|
|
|
|
handle->error = handle->eof ? NULL : g_strerror (errno);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
return pcm_frame_read (handle, synth); /* retry */
|
|
|
|
}
|
|
|
|
|
|
|
|
succeeded = FALSE;
|
|
|
|
if (synth)
|
|
|
|
mad_frame_mute (&handle->frame);
|
|
|
|
}
|
|
|
|
|
|
|
|
handle->pcm_pos = handle->next_pcm_pos;
|
|
|
|
handle->pcm_length = handle->frame_size;
|
|
|
|
handle->next_pcm_pos += handle->pcm_length;
|
|
|
|
|
|
|
|
if (synth)
|
|
|
|
mad_synth_frame (&handle->synth, &handle->frame);
|
|
|
|
|
|
|
|
handle->error = handle->stream.error && !succeeded ? mad_stream_errorstr (&handle->stream) : NULL;
|
|
|
|
|
|
|
|
return succeeded;
|
|
|
|
}
|
|
|
|
|
|
|
|
static guint*
|
|
|
|
create_seek_table (MadHandle *handle,
|
|
|
|
guint *n_seeks_p)
|
|
|
|
{
|
|
|
|
guint *seeks = NULL;
|
|
|
|
guint offs, n_seeks = 0;
|
|
|
|
|
|
|
|
*n_seeks_p = 0;
|
|
|
|
mad_synth_finish (&handle->synth);
|
|
|
|
mad_frame_finish (&handle->frame);
|
|
|
|
mad_stream_finish (&handle->stream);
|
|
|
|
mad_stream_init (&handle->stream);
|
|
|
|
mad_frame_init (&handle->frame);
|
|
|
|
mad_synth_init (&handle->synth);
|
|
|
|
mad_stream_options (&handle->stream, handle->stream_options);
|
|
|
|
|
|
|
|
offs = 0;
|
|
|
|
/* lseek (handle->hfile, offs, SEEK_SET) */
|
|
|
|
handle->eof = FALSE;
|
|
|
|
handle->bfill = 0;
|
|
|
|
handle->file_pos = 0;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
while (read_next_frame_header (handle))
|
|
|
|
{
|
|
|
|
guint this_pos = handle->file_pos - handle->bfill + handle->stream.this_frame - handle->buffer;
|
|
|
|
guint i = n_seeks++;
|
|
|
|
|
|
|
|
if (n_seeks > 256 * 1024) /* FIXME: max_frames */
|
|
|
|
{
|
|
|
|
g_free (seeks);
|
|
|
|
return NULL; /* FIXME: ETOOBIG */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gsl_alloc_upper_power2 (n_seeks) > gsl_alloc_upper_power2 (i))
|
|
|
|
seeks = g_renew (guint, seeks, gsl_alloc_upper_power2 (n_seeks));
|
|
|
|
seeks[i] = this_pos;
|
|
|
|
|
|
|
|
if (0)
|
|
|
|
{
|
|
|
|
if (mad_frame_decode (&handle->frame, &handle->stream) < 0)
|
|
|
|
MAD_DEBUG ("seektable frame read failed: %s", mad_stream_errorstr (&handle->stream));
|
|
|
|
mad_synth_frame (&handle->synth, &handle->frame);
|
|
|
|
MAD_DEBUG ("frame(%u) PCM:%u => FILE:%u FDIFF:%d (%x %x %x) br:%lu time:%ld/%lu mode:%u ext:%u flags:0x%x phase:%u",
|
|
|
|
i, i * handle->frame_size, this_pos, this_pos - seeks[MAX (i, 1) - 1],
|
|
|
|
handle->stream.this_frame[0], handle->stream.this_frame[1],
|
|
|
|
(handle->stream.this_frame[1] >> 1) & 3,
|
|
|
|
handle->frame.header.bitrate,
|
|
|
|
handle->frame.header.duration.seconds,
|
|
|
|
handle->frame.header.duration.fraction,
|
|
|
|
handle->frame.header.mode,
|
|
|
|
handle->frame.header.mode_extension,
|
|
|
|
handle->frame.header.flags,
|
|
|
|
handle->synth.phase);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!handle->eof)
|
|
|
|
{
|
|
|
|
MAD_DEBUG ("reading seektable frame failed: %s", handle->error ? handle->error : "Unknown");
|
|
|
|
|
|
|
|
/* frame read failed for a reason other than eof */
|
|
|
|
g_free (seeks);
|
|
|
|
return NULL; /* FIXME: EIO/errno */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (!handle->eof);
|
|
|
|
|
|
|
|
/* reset file offset */
|
|
|
|
offs = 0;
|
|
|
|
/* lseek (handle->hfile, offs, SEEK_SET) */
|
|
|
|
handle->eof = FALSE;
|
|
|
|
handle->file_pos = 0;
|
|
|
|
handle->bfill = 0;
|
|
|
|
|
|
|
|
/* shrink table */
|
|
|
|
seeks = g_renew (guint, seeks, n_seeks);
|
|
|
|
*n_seeks_p = n_seeks;
|
|
|
|
|
|
|
|
return seeks;
|
|
|
|
}
|
|
|
|
|
|
|
|
static GslErrorType
|
|
|
|
dh_mad_open (GslDataHandle *dhandle,
|
|
|
|
GslDataHandleSetup *setup)
|
|
|
|
{
|
|
|
|
MadHandle *handle = (MadHandle*) dhandle;
|
|
|
|
GslHFile *hfile;
|
|
|
|
GslLong n;
|
|
|
|
gboolean seek_invalidated = FALSE;
|
|
|
|
|
|
|
|
hfile = gsl_hfile_open (handle->dhandle.name);
|
|
|
|
if (!hfile)
|
|
|
|
return gsl_error_from_errno (errno, GSL_ERROR_OPEN_FAILED);
|
|
|
|
handle->hfile = hfile;
|
|
|
|
|
|
|
|
seek_invalidated |= handle->seek_mtime != hfile->mtime;
|
|
|
|
handle->bfill = 0;
|
|
|
|
handle->eof = FALSE;
|
|
|
|
handle->pcm_pos = 0;
|
|
|
|
handle->pcm_length = 0;
|
|
|
|
handle->next_pcm_pos = 0;
|
|
|
|
handle->file_pos = 0;
|
|
|
|
mad_stream_init (&handle->stream);
|
|
|
|
mad_frame_init (&handle->frame);
|
|
|
|
mad_synth_init (&handle->synth);
|
|
|
|
mad_stream_options (&handle->stream, handle->stream_options);
|
|
|
|
|
|
|
|
/* fetch first frame */
|
|
|
|
if (!read_next_frame_header (handle))
|
|
|
|
goto OPEN_FAILED;
|
|
|
|
|
|
|
|
/* get n_channels, frame size and sample rate */
|
|
|
|
setup->bit_depth = 24;
|
|
|
|
setup->n_channels = MAD_NCHANNELS (&handle->frame.header);
|
|
|
|
n = MAD_NSBSAMPLES (&handle->frame.header) * 32;
|
|
|
|
seek_invalidated |= n != handle->frame_size;
|
|
|
|
handle->frame_size = n;
|
|
|
|
handle->sample_rate = handle->frame.header.samplerate;
|
|
|
|
if (setup->n_channels < 1 ||
|
|
|
|
setup->n_channels > MAX_CHANNELS ||
|
|
|
|
handle->frame_size < 1 ||
|
|
|
|
handle->sample_rate < 1)
|
|
|
|
goto OPEN_FAILED;
|
|
|
|
|
|
|
|
/* seek through the stream to collect frame positions */
|
|
|
|
if (seek_invalidated || !handle->n_seeks)
|
|
|
|
{
|
|
|
|
handle->seek_mtime = hfile->mtime;
|
|
|
|
handle->n_seeks = 0;
|
|
|
|
g_free (handle->seeks);
|
|
|
|
handle->seeks = NULL;
|
|
|
|
if (handle->skip_seek_table)
|
|
|
|
{
|
|
|
|
/* fake seek table */
|
|
|
|
handle->n_seeks = 1;
|
|
|
|
handle->seeks = g_new (guint, handle->n_seeks);
|
|
|
|
handle->seeks[0] = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
handle->seeks = create_seek_table (handle, &handle->n_seeks);
|
|
|
|
if (!handle->seeks)
|
|
|
|
goto OPEN_FAILED;
|
|
|
|
MAD_DEBUG ("frames in seektable: %u", handle->n_seeks);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* validate/setup handle length */
|
|
|
|
n = handle->n_seeks * handle->frame_size * setup->n_channels;
|
|
|
|
if (n > 0)
|
|
|
|
setup->n_values = n;
|
|
|
|
else
|
|
|
|
goto OPEN_FAILED;
|
|
|
|
|
|
|
|
if (dh_mad_coarse_seek (&handle->dhandle, 0) != 0)
|
|
|
|
goto OPEN_FAILED;
|
|
|
|
|
|
|
|
return GSL_ERROR_NONE;
|
|
|
|
|
|
|
|
OPEN_FAILED:
|
|
|
|
g_free (handle->seeks);
|
|
|
|
handle->seeks = NULL;
|
|
|
|
handle->n_seeks = 0;
|
|
|
|
handle->seek_mtime = -1;
|
|
|
|
handle->bfill = 0;
|
|
|
|
handle->eof = FALSE;
|
|
|
|
handle->pcm_pos = 0;
|
|
|
|
handle->pcm_length = 0;
|
|
|
|
handle->next_pcm_pos = 0;
|
|
|
|
handle->file_pos = 0;
|
|
|
|
mad_synth_finish (&handle->synth);
|
|
|
|
mad_frame_finish (&handle->frame);
|
|
|
|
mad_stream_finish (&handle->stream);
|
|
|
|
gsl_hfile_close (handle->hfile);
|
|
|
|
handle->hfile = NULL;
|
|
|
|
|
|
|
|
return GSL_ERROR_OPEN_FAILED;
|
|
|
|
}
|
|
|
|
|
|
|
|
static GslLong
|
|
|
|
dh_mad_read (GslDataHandle *dhandle,
|
|
|
|
GslLong voffset, /* in values */
|
|
|
|
GslLong n_values,
|
|
|
|
gfloat *values)
|
|
|
|
{
|
|
|
|
MadHandle *handle = (MadHandle*) dhandle;
|
|
|
|
GslLong pos = voffset / dhandle->setup.n_channels;
|
|
|
|
gboolean frame_read_ok = TRUE;
|
|
|
|
|
|
|
|
if (pos < handle->pcm_pos ||
|
|
|
|
pos >= handle->pcm_pos + handle->pcm_length + SEEK_BY_READ_AHEAD (handle) * handle->frame_size)
|
|
|
|
{
|
|
|
|
GslLong tmp;
|
|
|
|
|
|
|
|
/* suckage, need to do lengthy seek in file */
|
|
|
|
tmp = dh_mad_coarse_seek (dhandle, voffset);
|
|
|
|
g_assert (tmp <= voffset);
|
|
|
|
}
|
|
|
|
|
|
|
|
while (pos >= handle->pcm_pos + handle->pcm_length)
|
|
|
|
frame_read_ok = pcm_frame_read (handle, TRUE);
|
|
|
|
|
|
|
|
/* check if the last call to pcm_frame_read() failed */
|
|
|
|
if (!frame_read_ok)
|
|
|
|
{
|
|
|
|
if (handle->stream.error == MAD_ERROR_BADDATAPTR)
|
|
|
|
{
|
|
|
|
/* if we encounter that the inter-frame accumulated layer-III state
|
|
|
|
* is not complete now, we'll try to increase the amount of frames
|
|
|
|
* we accumulate
|
|
|
|
*/
|
|
|
|
if (handle->accumulate_state_frames < 10)
|
|
|
|
{
|
|
|
|
handle->accumulate_state_frames++;
|
|
|
|
MAD_DEBUG ("retrying seek with accumulate_state_frames=%d",
|
|
|
|
handle->accumulate_state_frames);
|
|
|
|
|
|
|
|
/* force dh_mad_read to retry the seek */
|
|
|
|
dh_mad_coarse_seek (dhandle, 0);
|
|
|
|
return dh_mad_read (dhandle, voffset, n_values, values);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
MAD_DEBUG ("synthesizing frame failed, accumulate_state_frames is already %u: %s",
|
|
|
|
handle->accumulate_state_frames, handle->error);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
MAD_DEBUG ("failed to synthesize frame: %s", handle->error);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
n_values = MIN (n_values, handle->pcm_length * dhandle->setup.n_channels);
|
|
|
|
|
|
|
|
/* interleave into output buffer */
|
|
|
|
if (pos >= handle->pcm_pos && pos < handle->pcm_pos + handle->pcm_length)
|
|
|
|
{
|
|
|
|
guint offset = voffset - handle->pcm_pos * dhandle->setup.n_channels;
|
|
|
|
guint align = offset % dhandle->setup.n_channels;
|
|
|
|
guint n_samples = MIN (n_values, handle->pcm_length * dhandle->setup.n_channels - offset);
|
|
|
|
mad_fixed_t *pcm[MAX_CHANNELS];
|
|
|
|
gfloat *bound = values + n_samples;
|
|
|
|
guint i;
|
|
|
|
|
|
|
|
offset /= dhandle->setup.n_channels;
|
|
|
|
for (i = 0; i < dhandle->setup.n_channels; i++)
|
|
|
|
pcm[i] = handle->synth.pcm.samples[i] + offset + (i < align);
|
|
|
|
|
|
|
|
for (i = align; values < bound; values++)
|
|
|
|
{
|
|
|
|
mad_fixed_t mf = *(pcm[i]++);
|
|
|
|
|
|
|
|
*values = CLAMP (mf, -MAD_F_ONE, MAD_F_ONE) * (1. / (double) MAD_F_ONE);
|
|
|
|
if (++i >= dhandle->setup.n_channels)
|
|
|
|
i = 0;
|
|
|
|
}
|
|
|
|
return n_samples;
|
|
|
|
}
|
|
|
|
else /* something went wrong here, _badly_ */
|
|
|
|
{
|
|
|
|
MAD_MSG (GSL_ERROR_READ_FAILED,
|
|
|
|
"pcm position screwed (pos: %lu, handle-pos: %lu), aborting read",
|
|
|
|
pos, handle->pcm_pos);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static GslLong
|
|
|
|
dh_mad_coarse_seek (GslDataHandle *dhandle,
|
|
|
|
GslLong voffset)
|
|
|
|
{
|
|
|
|
MadHandle *handle = (MadHandle*) dhandle;
|
|
|
|
GslLong opos = handle->pcm_pos, pos = voffset / dhandle->setup.n_channels;
|
|
|
|
|
|
|
|
if (voffset < 0) /* pcm_tell() */
|
|
|
|
return handle->pcm_pos * dhandle->setup.n_channels;
|
|
|
|
|
|
|
|
if (pos < handle->pcm_pos ||
|
|
|
|
pos >= handle->pcm_pos + handle->pcm_length + SEEK_BY_READ_AHEAD (handle))
|
|
|
|
{
|
|
|
|
GslLong offs = pos;
|
|
|
|
guint i, file_pos;
|
|
|
|
|
|
|
|
/* reset decoder state */
|
|
|
|
mad_synth_finish (&handle->synth);
|
|
|
|
mad_frame_finish (&handle->frame);
|
|
|
|
mad_stream_finish (&handle->stream);
|
|
|
|
mad_stream_init (&handle->stream);
|
|
|
|
mad_frame_init (&handle->frame);
|
|
|
|
mad_synth_init (&handle->synth);
|
|
|
|
mad_stream_options (&handle->stream, handle->stream_options);
|
|
|
|
|
|
|
|
/* seek to some frames read ahead to accumulate layer III IDCMT state */
|
|
|
|
offs -= (gint) (handle->frame_size * handle->accumulate_state_frames);
|
|
|
|
offs = CLAMP (offs, 0, (gint) (handle->n_seeks * handle->frame_size));
|
|
|
|
|
|
|
|
/* get file position from seek table */
|
|
|
|
i = offs / handle->frame_size;
|
|
|
|
file_pos = handle->seeks[i];
|
|
|
|
|
|
|
|
/* perform file seek and adjust positions */
|
|
|
|
/* lseek (handle->hfile, file_pos, SEEK_SET) */
|
|
|
|
handle->eof = FALSE;
|
|
|
|
handle->bfill = 0;
|
|
|
|
handle->file_pos = file_pos;
|
|
|
|
handle->pcm_pos = i * handle->frame_size;
|
|
|
|
handle->pcm_length = 0;
|
|
|
|
handle->next_pcm_pos = handle->pcm_pos;
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
/* adapt synth phase */
|
|
|
|
handle->synth.phase = ((i + 1) * (handle->frame_size / 32)) % 16;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* accumulate state */
|
|
|
|
if (pos >= handle->accumulate_state_frames * handle->frame_size)
|
|
|
|
{
|
|
|
|
guint i;
|
|
|
|
for (i = 0; i < handle->accumulate_state_frames; i++)
|
|
|
|
{
|
|
|
|
gboolean synth = i + 1 == handle->accumulate_state_frames;
|
|
|
|
|
|
|
|
if (!pcm_frame_read (handle, synth) && handle->stream.error != MAD_ERROR_BADDATAPTR)
|
|
|
|
MAD_DEBUG ("COARSE-SEEK: frame read ahead (%u): failed: %s", i, handle->error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MAD_DEBUG ("seek-done: at %lu (f:%lu) want %lu (f:%lu) got %lu (f:%lu) diff %ld (diff-requested %ld)",
|
|
|
|
opos, opos / handle->frame_size,
|
|
|
|
pos, pos / handle->frame_size,
|
|
|
|
handle->pcm_pos, handle->pcm_pos / handle->frame_size,
|
|
|
|
handle->pcm_pos - opos, pos - opos);
|
|
|
|
}
|
|
|
|
|
|
|
|
return handle->pcm_pos * dhandle->setup.n_channels;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dh_mad_close (GslDataHandle *data_handle)
|
|
|
|
{
|
|
|
|
MadHandle *handle = (MadHandle*) data_handle;
|
|
|
|
|
|
|
|
handle->bfill = 0;
|
|
|
|
handle->eof = FALSE;
|
|
|
|
handle->pcm_pos = 0;
|
|
|
|
handle->pcm_length = 0;
|
|
|
|
handle->next_pcm_pos = 0;
|
|
|
|
handle->file_pos = 0;
|
|
|
|
mad_synth_finish (&handle->synth);
|
|
|
|
mad_frame_finish (&handle->frame);
|
|
|
|
mad_stream_finish (&handle->stream);
|
|
|
|
gsl_hfile_close (handle->hfile);
|
|
|
|
handle->hfile = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dh_mad_destroy (GslDataHandle *data_handle)
|
|
|
|
{
|
|
|
|
MadHandle *handle = (MadHandle*) data_handle;
|
|
|
|
|
|
|
|
g_free (handle->seeks);
|
|
|
|
handle->seeks = NULL;
|
|
|
|
handle->n_seeks = 0;
|
|
|
|
gsl_data_handle_common_free (data_handle);
|
|
|
|
gsl_delete_struct (MadHandle, handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
static GslDataHandleFuncs dh_mad_vtable = {
|
|
|
|
dh_mad_open,
|
|
|
|
dh_mad_read,
|
|
|
|
dh_mad_close,
|
|
|
|
dh_mad_destroy,
|
|
|
|
dh_mad_coarse_seek,
|
|
|
|
};
|
|
|
|
|
|
|
|
static GslDataHandle*
|
|
|
|
dh_mad_new (const gchar *file_name,
|
|
|
|
gboolean skip_seek_keep_open)
|
|
|
|
{
|
|
|
|
MadHandle *handle;
|
|
|
|
gboolean success;
|
|
|
|
|
|
|
|
handle = gsl_new_struct0 (MadHandle, 1);
|
|
|
|
success = gsl_data_handle_common_init (&handle->dhandle, file_name);
|
|
|
|
if (success)
|
|
|
|
{
|
|
|
|
GslErrorType error;
|
|
|
|
|
|
|
|
handle->dhandle.vtable = &dh_mad_vtable;
|
|
|
|
handle->sample_rate = 0;
|
|
|
|
handle->frame_size = 0;
|
|
|
|
handle->stream_options = MAD_OPTION_IGNORECRC;
|
|
|
|
handle->accumulate_state_frames = 0;
|
|
|
|
handle->eof = FALSE;
|
|
|
|
handle->hfile = NULL;
|
|
|
|
handle->file_pos = 0;
|
|
|
|
handle->error = NULL;
|
|
|
|
handle->n_seeks = 0;
|
|
|
|
handle->seeks = NULL;
|
|
|
|
handle->seek_mtime = -1;
|
|
|
|
handle->bfill = 0;
|
|
|
|
handle->pcm_pos = handle->pcm_length = handle->next_pcm_pos = 0;
|
|
|
|
|
|
|
|
/* we can only check matters upon opening
|
|
|
|
*/
|
|
|
|
handle->skip_seek_table = skip_seek_keep_open != FALSE;
|
|
|
|
error = gsl_data_handle_open (&handle->dhandle);
|
|
|
|
if (!error)
|
|
|
|
{
|
|
|
|
if (!skip_seek_keep_open)
|
|
|
|
gsl_data_handle_close (&handle->dhandle);
|
|
|
|
return &handle->dhandle;
|
|
|
|
}
|
|
|
|
gsl_data_handle_unref (&handle->dhandle);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
g_free (handle->seeks);
|
|
|
|
gsl_delete_struct (MadHandle, handle);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
GslDataHandle*
|
|
|
|
gsl_data_handle_new_mad (const gchar *file_name)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (file_name != NULL, NULL);
|
|
|
|
|
|
|
|
return dh_mad_new (file_name, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
GslErrorType
|
|
|
|
gsl_data_handle_mad_testopen (const gchar *file_name,
|
|
|
|
guint *n_channels,
|
|
|
|
gfloat *mix_freq)
|
|
|
|
{
|
|
|
|
GslDataHandle *dhandle;
|
|
|
|
MadHandle *handle;
|
|
|
|
|
|
|
|
g_return_val_if_fail (file_name != NULL, GSL_ERROR_INTERNAL);
|
|
|
|
|
|
|
|
dhandle = dh_mad_new (file_name, TRUE);
|
|
|
|
if (!dhandle)
|
|
|
|
return GSL_ERROR_OPEN_FAILED;
|
|
|
|
|
|
|
|
handle = (MadHandle*) dhandle;
|
|
|
|
if (n_channels)
|
|
|
|
*n_channels = handle->dhandle.setup.n_channels;
|
|
|
|
if (mix_freq)
|
|
|
|
*mix_freq = handle->sample_rate;
|
|
|
|
gsl_data_handle_close (dhandle);
|
|
|
|
gsl_data_handle_unref (dhandle);
|
|
|
|
|
|
|
|
return GSL_ERROR_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
#else /* !GSL_HAVE_LIBMAD */
|
|
|
|
|
|
|
|
GslDataHandle*
|
|
|
|
gsl_data_handle_new_mad (const gchar *file_name)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
GslErrorType
|
|
|
|
gsl_data_handle_mad_testopen (const gchar *file_name,
|
|
|
|
guint *n_channels,
|
|
|
|
gfloat *mix_freq)
|
|
|
|
{
|
|
|
|
return GSL_ERROR_FORMAT_UNKNOWN;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* !GSL_HAVE_LIBMAD */
|