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.
arts/flow/gsl/gsldatahandle-mad.c

712 lines
19 KiB

/* 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 */
/* vim:set ts=8 sts=2 sw=2: */