diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-05 00:01:18 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-05 00:01:18 +0000 |
commit | 42995d7bf396933ee60c5f89c354ea89cf13df0d (patch) | |
tree | cfdcea0ac57420e7baf570bfe435e107bb842541 /flow/gsl/gsldatahandle-vorbis.c | |
download | arts-42995d7bf396933ee60c5f89c354ea89cf13df0d.tar.gz arts-42995d7bf396933ee60c5f89c354ea89cf13df0d.zip |
Copy of aRts for Trinity modifications
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/dependencies/arts@1070145 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'flow/gsl/gsldatahandle-vorbis.c')
-rw-r--r-- | flow/gsl/gsldatahandle-vorbis.c | 376 |
1 files changed, 376 insertions, 0 deletions
diff --git a/flow/gsl/gsldatahandle-vorbis.c b/flow/gsl/gsldatahandle-vorbis.c new file mode 100644 index 0000000..9b84ae0 --- /dev/null +++ b/flow/gsl/gsldatahandle-vorbis.c @@ -0,0 +1,376 @@ +/* GSL - Generic Sound Layer + * Copyright (C) 2001-2002 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 "gsldatahandle-vorbis.h" + +#if GSL_HAVE_OGGVORBIS +#include "gslfilehash.h" +#include <ogg/ogg.h> +#include <vorbis/vorbisfile.h> +#include <errno.h> + + +/* --- defines --- */ +#define MAX_CHANNELS (16) /* hard limit, eases our life somewhat */ +/* number of values to decode and throw away instead of seeking. since + * seeking can be quite time consuming, this should cover a good range + * of seek-ahead space + */ +#define SEEK_BY_READ_AHEAD(vhandle) (vhandle->max_block_size * 8) + + +/* --- structure --- */ +typedef struct { + GslDataHandle dhandle; + + guint stream; + guint n_streams; + + /* live data */ + gint64 soffset; /* our PCM start offset */ + guint max_block_size; + + /* pcm read out cache */ + GslLong pcm_pos, pcm_length; + gfloat *pcm[MAX_CHANNELS]; + + OggVorbis_File ofile; +} VorbisHandle; + + +/* --- functions --- */ +static GslErrorType +ov_errno_to_error (gint ov_errno, + GslErrorType fallback) +{ + switch (ov_errno) + { + case OV_EOF: return GSL_ERROR_EOF; + case OV_EBADLINK: + case OV_EBADPACKET: + case OV_HOLE: return GSL_ERROR_DATA_CORRUPT; + case OV_EREAD: return GSL_ERROR_READ_FAILED; + case OV_ENOSEEK: return GSL_ERROR_SEEK_FAILED; + case OV_EFAULT: + case OV_EIMPL: return GSL_ERROR_CODEC_FAILURE; + case OV_EINVAL: return GSL_ERROR_INTERNAL; + case OV_ENOTAUDIO: + case OV_EVERSION: + case OV_EBADHEADER: + case OV_ENOTVORBIS: return GSL_ERROR_FORMAT_INVALID; + case OV_FALSE: + default: return fallback; + } +} + +static size_t +rfile_read (void *ptr, + size_t size, + size_t nmemb, + void *datasource) +{ + GslRFile *rfile = datasource; + return gsl_rfile_read (rfile, size * nmemb, ptr); +} + +static int +rfile_seek (void *datasource, + ogg_int64_t offset, + int whence) +{ + GslRFile *rfile = datasource; + GslLong l; + switch (whence) + { + default: + case SEEK_SET: + l = gsl_rfile_seek_set (rfile, offset); + break; + case SEEK_CUR: + l = gsl_rfile_position (rfile); + l = gsl_rfile_seek_set (rfile, l + offset); + break; + case SEEK_END: + l = gsl_rfile_length (rfile); + l = gsl_rfile_seek_set (rfile, l + offset); + break; + } + return l; +} + +static int +rfile_close (void *datasource) +{ + GslRFile *rfile = datasource; + gsl_rfile_close (rfile); + return 0; +} + +static long +rfile_tell (void *datasource) +{ + GslRFile *rfile = datasource; + return gsl_rfile_position (rfile); +} + +static ov_callbacks rfile_ov_callbacks = { + rfile_read, + rfile_seek, + rfile_close, + rfile_tell, +}; + +static GslErrorType +dh_vorbis_open (GslDataHandle *data_handle, + GslDataHandleSetup *setup) +{ + VorbisHandle *vhandle = (VorbisHandle*) data_handle; + GslRFile *rfile; + vorbis_info *vi; + GslLong n, i; + gint err; + +#if 0 + file = fopen (vhandle->dhandle.name, "r"); + if (!file) + return gsl_error_from_errno (errno, GSL_ERROR_OPEN_FAILED); + err = ov_open (file, &vhandle->ofile, NULL, 0); + if (err < 0) + { + fclose (file); + return ov_errno_to_error (err, GSL_ERROR_OPEN_FAILED); + } +#endif + + rfile = gsl_rfile_open (vhandle->dhandle.name); + if (!rfile) + return gsl_error_from_errno (errno, GSL_ERROR_OPEN_FAILED); + err = ov_open_callbacks (rfile, &vhandle->ofile, NULL, 0, rfile_ov_callbacks); + if (err < 0) + { + gsl_rfile_close (rfile); + return ov_errno_to_error (err, GSL_ERROR_OPEN_FAILED); + } + + n = ov_streams (&vhandle->ofile); + if (n > vhandle->stream) + vhandle->n_streams = n; + else + { + ov_clear (&vhandle->ofile); /* closes file */ + return GSL_ERROR_OPEN_FAILED; + } + + vhandle->soffset = 0; + for (i = 0; i < vhandle->stream; i++) + vhandle->soffset += ov_pcm_total (&vhandle->ofile, i); + + n = ov_pcm_total (&vhandle->ofile, vhandle->stream); + vi = ov_info (&vhandle->ofile, vhandle->stream); + if (n > 0 && vi && vi->channels && ov_pcm_seek (&vhandle->ofile, vhandle->soffset) >= 0) + { + setup->n_channels = vi->channels; + setup->n_values = n * setup->n_channels; + setup->bit_depth = 24; + } + else + { + ov_clear (&vhandle->ofile); /* closes file */ + return GSL_ERROR_OPEN_FAILED; + } + + vhandle->max_block_size = vorbis_info_blocksize (vi, 0); + n = vorbis_info_blocksize (vi, 1); + vhandle->max_block_size = MAX (vhandle->max_block_size, n); + vhandle->pcm_pos = 0; + vhandle->pcm_length = 0; + + return GSL_ERROR_NONE; +} + +static GslLong +dh_vorbis_coarse_seek (GslDataHandle *dhandle, + GslLong voffset) +{ + VorbisHandle *vhandle = (VorbisHandle*) dhandle; + GslLong opos = vhandle->pcm_pos, pos = voffset / dhandle->setup.n_channels; + + if (voffset < 0) + return vhandle->pcm_pos * dhandle->setup.n_channels; + + if (pos < vhandle->pcm_pos || + pos >= vhandle->pcm_pos + vhandle->pcm_length + SEEK_BY_READ_AHEAD (vhandle)) + { + gint err = ov_pcm_seek_page (&vhandle->ofile, vhandle->soffset + pos); + + if (err) /* eek */ + err = ov_pcm_seek_page (&vhandle->ofile, vhandle->soffset); + else + vhandle->pcm_pos = ov_pcm_tell (&vhandle->ofile) - vhandle->soffset; + if (err || vhandle->pcm_pos < 0) /* urg, we're completely screwed */ + vhandle->pcm_pos = 0; + vhandle->pcm_length = 0; + } + g_printerr ("OggS-SEEK: at %lu want %lu got %lu (diff-requested %ld)\n", + opos, pos, vhandle->pcm_pos, pos - opos); + + return vhandle->pcm_pos * dhandle->setup.n_channels; +} + +static void +read_packet (VorbisHandle *vhandle) +{ + gfloat **pcm = NULL; + gint stream_id, i; + + vhandle->pcm_pos = ov_pcm_tell (&vhandle->ofile) - vhandle->soffset; +#if GSL_HAVE_OGGVORBIS_RC3 + vhandle->pcm_length = ov_read_float (&vhandle->ofile, &pcm, &stream_id); +#else + vhandle->pcm_length = ov_read_float (&vhandle->ofile, &pcm, (~0U>>1), &stream_id); +#endif + if (vhandle->pcm_pos < 0 || vhandle->pcm_length < 0 || stream_id != vhandle->stream) + { + /* urg, this is bad! */ + dh_vorbis_coarse_seek (&vhandle->dhandle, 0); + } + else + for (i = 0; i < vhandle->dhandle.setup.n_channels; i++) + vhandle->pcm[i] = pcm[i]; +} + +static GslLong +dh_vorbis_read (GslDataHandle *dhandle, + GslLong voffset, /* in values */ + GslLong n_values, + gfloat *values) +{ + VorbisHandle *vhandle = (VorbisHandle*) dhandle; + GslLong pos = voffset / dhandle->setup.n_channels; + + if (pos < vhandle->pcm_pos || + pos >= vhandle->pcm_pos + vhandle->pcm_length + SEEK_BY_READ_AHEAD (vhandle)) + { + GslLong tmp; + + /* suckage, needs to seek in file, this takes ages */ + tmp = dh_vorbis_coarse_seek (dhandle, voffset); + g_assert (tmp <= voffset); + } + + while (pos >= vhandle->pcm_pos + vhandle->pcm_length) + read_packet (vhandle); + + n_values = MIN (n_values, vhandle->pcm_length * dhandle->setup.n_channels); + + /* interleave into output buffer */ + if (pos >= vhandle->pcm_pos && pos < vhandle->pcm_pos + vhandle->pcm_length) + { + guint offset = voffset - vhandle->pcm_pos * dhandle->setup.n_channels; + guint align = offset % dhandle->setup.n_channels; + guint n_samples = MIN (n_values, vhandle->pcm_length * dhandle->setup.n_channels - offset); + gfloat *pcm[MAX_CHANNELS], *bound = values + n_samples; + guint i; + + offset /= dhandle->setup.n_channels; + for (i = 0; i < dhandle->setup.n_channels; i++) + pcm[i] = vhandle->pcm[i] + offset + (i < align); + + for (i = align; values < bound; values++) + { + gfloat f = *(pcm[i]++); + + f = CLAMP (f, -1.0, 1.0); + *values = f; + if (++i >= dhandle->setup.n_channels) + i = 0; + } + return n_samples; + } + else /* something went wrong here, _badly_ */ + return 0; +} + +static void +dh_vorbis_close (GslDataHandle *dhandle) +{ + VorbisHandle *vhandle = (VorbisHandle*) dhandle; + + ov_clear (&vhandle->ofile); + vhandle->pcm_pos = 0; + vhandle->pcm_length = 0; +} + +static void +dh_vorbis_destroy (GslDataHandle *data_handle) +{ + VorbisHandle *vhandle = (VorbisHandle*) data_handle; + + gsl_data_handle_common_free (data_handle); + gsl_delete_struct (VorbisHandle, vhandle); +} + +static GslDataHandleFuncs dh_vorbis_vtable = { + dh_vorbis_open, + dh_vorbis_read, + dh_vorbis_close, + dh_vorbis_destroy, + dh_vorbis_coarse_seek, +}; + +GslDataHandle* +gsl_data_handle_new_ogg_vorbis (const gchar *file_name, + guint lbitstream) +{ + VorbisHandle *vhandle; + gboolean success; + + g_return_val_if_fail (file_name != NULL, NULL); + + vhandle = gsl_new_struct0 (VorbisHandle, 1); + success = gsl_data_handle_common_init (&vhandle->dhandle, file_name); + if (success) + { + GslErrorType error; + + vhandle->dhandle.vtable = &dh_vorbis_vtable; + vhandle->n_streams = 0; + vhandle->stream = lbitstream; + + /* we can only check matters upon opening + */ + error = gsl_data_handle_open (&vhandle->dhandle); + if (!error) + { + gsl_data_handle_close (&vhandle->dhandle); + return &vhandle->dhandle; + } + else + { + gsl_data_handle_unref (&vhandle->dhandle); + return NULL; + } + } + else + { + gsl_delete_struct (VorbisHandle, vhandle); + return NULL; + } +} +#endif /* GSL_HAVE_OGGVORBIS */ + |