Mercurial > audlegacy-plugins
changeset 930:2f742d127b3e trunk
[svn] - initial import of flacng from audacious-flacng-0.012
author | nenolod |
---|---|
date | Mon, 09 Apr 2007 10:55:23 -0700 |
parents | 9631824411bf |
children | b6c95e2a14f4 |
files | ChangeLog configure.ac mk/rules.mk.in src/flacng/AUTHORS src/flacng/Makefile src/flacng/README src/flacng/debug.h src/flacng/flac_compat112.h src/flacng/flac_compat113.h src/flacng/flac_compat114.h src/flacng/flacng.h src/flacng/plugin.c src/flacng/plugin.h src/flacng/seekable_stream_callbacks.c src/flacng/seekable_stream_callbacks.h src/flacng/tools.c src/flacng/tools.h src/flacng/version.h |
diffstat | 18 files changed, 1977 insertions(+), 2 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog Mon Apr 09 08:51:31 2007 -0700 +++ b/ChangeLog Mon Apr 09 10:55:23 2007 -0700 @@ -1,3 +1,11 @@ +2007-04-09 15:51:31 +0000 William Pitcock <nenolod@sacredspiral.co.uk> + revision [1986] + - use C-style vectors (part 2) + + trunk/src/madplug/SFMT-params19937.h | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + + 2007-04-09 15:50:46 +0000 William Pitcock <nenolod@sacredspiral.co.uk> revision [1984] - use C-style vectors instead of apple's stuff
--- a/configure.ac Mon Apr 09 08:51:31 2007 -0700 +++ b/configure.ac Mon Apr 09 10:55:23 2007 -0700 @@ -497,6 +497,29 @@ INPUT_PLUGINS="$INPUT_PLUGINS vorbis" fi +dnl *** FLACng (in progress replacement to XMMS-flac port) + +AC_ARG_ENABLE(flacng, + [ --disable-flacng disable flac input plugin (default=enabled)], + [enable_flacng=$enableval], + [enable_flacng="yes"] +) + +if test "$enable_flacng" = "yes"; then + PKG_CHECK_MODULES(FLAC, [flac >= 1.1.2], + [have_flacng=yes], + [AC_MSG_WARN([*** Cannot find libFLAC, FLACng will not be built ***]) + have_flacng=no] + ) +else + AC_MSG_RESULT([*** FLACng plugin disabled per user request ***]) + have_flacng=no +fi + +if test "$have_flacng" = "yes"; then + INPUT_PLUGINS="$INPUT_PLUGINS flacng" +fi + dnl *** WavPack 4.31 support AC_ARG_ENABLE(wavpack, @@ -1266,6 +1289,7 @@ echo " Tone Generator: yes" echo " Ogg Vorbis (vorbis): $have_oggvorbis" echo " Free Lossless Audio Codec (flac): $have_flac" +echo " Free Lossless Audio Codec (flacng): $have_flacng" echo " Commodore 64 audio (sid): $have_sidplay" echo " Game music (spc, nsf & gbs): yes" echo " PlayStation audio (sexypsf): yes"
--- a/mk/rules.mk.in Mon Apr 09 08:51:31 2007 -0700 +++ b/mk/rules.mk.in Mon Apr 09 10:55:23 2007 -0700 @@ -202,8 +202,8 @@ LIBBEEP_MAJOR_VERSION ?= @LIBBEEP_MAJOR_VERSION@ LIBBEEP_MICRO_VERSION ?= @LIBBEEP_MICRO_VERSION@ LIBBEEP_MINOR_VERSION ?= @LIBBEEP_MINOR_VERSION@ -LIBFLAC_CFLAGS ?= @LIBFLAC_CFLAGS@ -LIBFLAC_LIBS ?= @LIBFLAC_LIBS@ +FLAC_CFLAGS ?= @FLAC_CFLAGS@ +FLAC_LIBS ?= @FLAC_LIBS@ LIBGLADE_CFLAGS ?= @LIBGLADE_CFLAGS@ LIBGLADE_LIBS ?= @LIBGLADE_LIBS@ LIBNMS_CFLAGS ?= @LIBNMS_CFLAGS@ @@ -358,3 +358,4 @@ IMLIB2_CFLAGS ?= @IMLIB2_CFLAGS@ IMLIB2_LIBS ?= @IMLIB2_LIBS@ SIMD_CFLAGS ?= @SIMD_CFLAGS@ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flacng/AUTHORS Mon Apr 09 10:55:23 2007 -0700 @@ -0,0 +1,28 @@ +/* + * A FLAC decoder plugin for the Audacious Media Player + * Copyright (C) 2005 Ralf Ertzinger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +Ralf Ertzinger <ralf@skytale.net> + +Thanks to Hendrik Scholz <http://www.wormulon.net> for testing the FreeBSD +side + +William Pitcock <nenolod@sacredspiral.co.uk> + - tuple builder + - full transition to InputPlayback API + - full vorbiscomment support
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flacng/Makefile Mon Apr 09 10:55:23 2007 -0700 @@ -0,0 +1,16 @@ +include ../../mk/rules.mk +include ../../mk/init.mk + +OBJECTIVE_LIBS = libflacng$(SHARED_SUFFIX) + +LIBDIR = $(plugindir)/$(INPUT_PLUGIN_DIR) + +LIBADD = $(GTK_LIBS) $(GLIB_LIBS) $(PANGO_LIBS) $(FLAC_LIBS) + +SOURCES = plugin.c tools.c seekable_stream_callbacks.c + +OBJECTS = ${SOURCES:.c=.o} + +CFLAGS += $(PICFLAGS) $(GTK_CFLAGS) $(GLIB_CFLAGS) $(PANGO_CFLAGS) -I../../intl -I../.. -I./libflac $(FLAC_CFLAGS) + +include ../../mk/objective.mk
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flacng/README Mon Apr 09 10:55:23 2007 -0700 @@ -0,0 +1,109 @@ +/* + * A FLAC decoder plugin for the Audacious Media Player + * Copyright (C) 2005 Ralf Ertzinger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +INTRODUCTION +============ + +This is an input plugin for the Audacious Media Player enabling the playback +of FLAC compressed audio files. + +The Audacious Media Player (http://www.audacious-media-player.org) is a port of +the XMMS audio player to GTK2. + +FLAC (http://flac.sourceforge.net/) is an Open Source lossless audio +codec developed by Josh Coalson. + + + +STATUS +====== + +This plugin is to be considered of alpha quality. Use at your own risk. + + +OK, let's be brief with this and first list the things that do _not_ work +at the current time. + +- No display of bitrate +- Visualizer plugins do not work with FLAC streams (*) +- No OGGFlac streams (*) +- Maximum of 2 channels (Stereo) (*) +- No configuration options at all (*) + +Still interested? Good. +The items marked with a (*) above can be fixed, this just takes some code +that has not been written yet (har har). +The FLAC decoder does not emit the current bitrate, so you'll have to +settle for "VBR", which is true enough. +As for configuration options, so far I have not found anything worth +configuring, maybe you can think of something. + +Nonetheless. + +Given all the restrictions above, when presented with a 8/16/24 bit, stereo +not-packed-in-ogg FLAC stream, this plugin will do it's best to produce +some sound out of it. This may even work. It may, on the other hand, crash +your machine and eat your data. + +Did I mention that the code is alpha quality? + + + +HOW TO BUILD +============ + +You will need the following to compile the code: + +1) A working compiler environment (if you do not know what this means + you probably do not want to use this code now, anyway) + +2) The Audacious media player, including development header files + +3) The FLAC libraries, including development header files + +If you have all this the rest is easy. +Unpack the tar ball, change into the directory created and +issue "configure" (without the quotes). This will try and find the +necessary stuff on your system. When it finishes successfully, type +"make". This will build the plugin library. +To enable debugging messages (and you probably want those at this +point) use "configure --debug" + +To install the plugin in your home directory (no special permissions +needed, ideal for testing) use "make localinstall" (you can use +"make localremove" to remove the plugin again). To install the plugin +for all users use "make install" instead. + + + +TESTING +======= + +After successful installation start Audacious (preferrably from the command +line so you can see error or debug messages) and look in the configuration +panel under plugins. The FLAC plugin ought to appear there, looking +like "FLAC audio plugin". Now add a FLAC file to +the playlist and try it. + + + +BUGS +==== + +Sure. Tons of 'em. Report to <ralf@skytale.net> for the time being.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flacng/debug.h Mon Apr 09 10:55:23 2007 -0700 @@ -0,0 +1,35 @@ +/* + * A FLAC decoder plugin for the Audacious Media Player + * Copyright (C) 2005 Ralf Ertzinger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef DEBUG_H +#define DEBUG_H + +#define _ENTER _DEBUG("enter") +#define _LEAVE _DEBUG("leave"); return +#define _MESSAGE(tag, string, ...) do { fprintf(stderr, "%s: libflacng.so: %s:%d (%s): " string "\n", \ + tag, __FILE__, __LINE__, __func__, ##__VA_ARGS__); } while(0) + +#define _ERROR(...) _MESSAGE("ERROR", __VA_ARGS__) + +#ifdef DEBUG +#define _DEBUG(...) _MESSAGE("DEBUG", __VA_ARGS__) +#else +#define _DEBUG(...) {} +#endif + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flacng/flac_compat112.h Mon Apr 09 10:55:23 2007 -0700 @@ -0,0 +1,35 @@ +#ifndef FLAC_COMPAT112_H +#define FLAC_COMPAT112_H + +#if !defined(FLAC_API_VERSION_CURRENT) + +#define FLAC__StreamDecoder FLAC__SeekableStreamDecoder + +#define FLAC__StreamDecoderReadStatus FLAC__SeekableStreamDecoderReadStatus +#define FLAC__StreamDecoderSeekStatus FLAC__SeekableStreamDecoderSeekStatus +#define FLAC__StreamDecoderTellStatus FLAC__SeekableStreamDecoderTellStatus +#define FLAC__StreamDecoderLengthStatus FLAC__SeekableStreamDecoderLengthStatus +#define FLAC__stream_decoder_new FLAC__seekable_stream_decoder_new + +#define FLAC__STREAM_DECODER_SEEK_STATUS_ERROR FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR +#define FLAC__STREAM_DECODER_SEEK_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK +#define FLAC__STREAM_DECODER_TELL_STATUS_ERROR FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR +#define FLAC__STREAM_DECODER_TELL_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK +#define FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_ERROR +#define FLAC__STREAM_DECODER_LENGTH_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK + +FLAC__stream_decoder_init_stream( + FLAC__SeekableStreamDecoder* decoder, + FLAC__SeekableStreamDecoderReadCallback read_callback, + FLAC__SeekableStreamDecoderSeekCallback seek_callback, + FLAC__SeekableStreamDecoderTellCallback tell_callback, + FLAC__SeekableStreamDecoderLengthCallback length_callback, + FLAC__SeekableStreamDecoderEofCallback eof_callback, + FLAC__SeekableStreamDecoderWriteCallback write_callback, + FLAC__SeekableStreamDecoderMetadataCallback metadata_callback, + FLAC__SeekableStreamDecoderErrorCallback error_callback, + void * client_data); + +#endif + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flacng/flac_compat113.h Mon Apr 09 10:55:23 2007 -0700 @@ -0,0 +1,7 @@ +#ifndef FLAC_COMPAT113_H +#define FLAC_COMPAT113_H + +#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT == 7 +#endif + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flacng/flac_compat114.h Mon Apr 09 10:55:23 2007 -0700 @@ -0,0 +1,7 @@ +#ifndef FLAC_COMPAT114_H +#define FLAC_COMPAT114_H + +#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT == 8 +#endif + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flacng/flacng.h Mon Apr 09 10:55:23 2007 -0700 @@ -0,0 +1,94 @@ +/* + * A FLAC decoder plugin for the Audacious Media Player + * Copyright (C) 2005 Ralf Ertzinger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef FLACNG_H +#define FLACNG_H + +#include <glib.h> +#include <audacious/plugin.h> +#include <audacious/vfs.h> + +#define OUTPUT_BLOCK_SIZE (8192u) +#define MAX_SUPPORTED_CHANNELS (2u) +#define BUFFER_SIZE_SAMP (FLAC__MAX_BLOCK_SIZE * FLAC__MAX_CHANNELS) +#define BUFFER_SIZE_BYTE (BUFFER_SIZE_SAMP * (FLAC__MAX_BITS_PER_SAMPLE/8)) + +/* + * Audio information about the stream as a whole, gathered from + * the metadata header + */ +struct stream_info { + guint bits_per_sample; + guint samplerate; + guint channels; + gulong samples; + gboolean has_seektable; +}; + +/* + * Information about the last decoded audio frame. + * Also describes the format of the audio currently contained + * in the stream buffer. + */ +struct frame_info { + guint bits_per_sample; + guint samplerate; + guint channels; +}; + +/* + * Information about the stream content, from the metadata + */ +struct stream_comment { + gchar* artist; + gchar* album; + gchar* title; + gchar* tracknumber; + gchar* genre; + gchar* date; +}; + +/* + * Replaygain information, from the metadata + */ +struct stream_replaygain { + gboolean has_rg; + gchar* ref_loud; + gchar* track_gain; + gchar* track_peak; + gchar* album_gain; + gchar* album_peak; +}; + + +typedef struct callback_info { + gint32* output_buffer; + gint32* write_pointer; + guint buffer_free; + guint buffer_used; + VFSFile* input_stream; + struct stream_info stream; + struct stream_comment comment; + struct stream_replaygain replaygain; + gboolean metadata_changed; + struct frame_info frame; + glong read_max; + gchar* name; +} callback_info; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flacng/plugin.c Mon Apr 09 10:55:23 2007 -0700 @@ -0,0 +1,706 @@ +/* + * A FLAC decoder plugin for the Audacious Media Player + * Copyright (C) 2005 Ralf Ertzinger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <audacious/util.h> +#include <audacious/output.h> +#include <glib/gi18n.h> +#include "flacng.h" +#include "tools.h" +#include "plugin.h" +#include "seekable_stream_callbacks.h" +#include "version.h" +#include "debug.h" + +static gchar *flac_fmts[] = { "flac", NULL }; + +InputPlugin flac_ip = { + NULL, + NULL, + "FLACng Audio Plugin", + flac_init, + flac_aboutbox, + NULL, + flac_is_our_file, + NULL, + flac_play_file, + flac_stop, + flac_pause, + flac_seek, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + flac_get_song_info, + NULL, + NULL, + flac_get_song_tuple, // get a tuple + NULL, + NULL, // write a tuple back to a file as a tag +/* flac_is_our_fd */ NULL, // version of is_our_file which is handed an FD + flac_fmts // vector of fileextensions allowed by the plugin +}; + +FLAC__StreamDecoder* test_decoder; +FLAC__StreamDecoder* main_decoder; +callback_info* test_info; +callback_info* main_info; +gboolean plugin_initialized = FALSE; +gint seek_to = -1; +static GThread* thread; +GMutex* flac_pl_mutex; + +/* === */ + +InputPlugin* get_iplugin_info(void) { + + _ENTER; + + _DEBUG("%s (%s)", flac_ip.description, _VERSION); + + _LEAVE &flac_ip; +} + +/* --- */ + +void flac_init(void) { + + FLAC__StreamDecoderInitStatus ret; + + _ENTER; + + /* + * Callback structure and decoder for file test + * purposes + */ + if (NULL == (test_info = init_callback_info("test"))) { + _ERROR("Could not initialize the test callback structure!"); + _LEAVE; + } + _DEBUG("Test callback structure at %p", test_info); + + if (NULL == (test_decoder = FLAC__stream_decoder_new())) { + _ERROR("Could not create the test FLAC decoder instance!"); + _LEAVE; + } + + + /* + * We want the VORBISCOMMENT metadata, for the file tags + * and SEEKTABLE + */ + FLAC__stream_decoder_set_metadata_respond(test_decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT); + FLAC__stream_decoder_set_metadata_respond(test_decoder, FLAC__METADATA_TYPE_SEEKTABLE); + + /* + * Callback structure and decoder for main decoding + * loop + */ + if (NULL == (main_info = init_callback_info("main"))) { + _ERROR("Could not initialize the main callback structure!"); + _LEAVE; + } + _DEBUG("Main callback structure at %p", main_info); + + if (NULL == (main_decoder = FLAC__stream_decoder_new())) { + _ERROR("Could not create the main FLAC decoder instance!"); + _LEAVE; + } + + + /* + * We want the VORBISCOMMENT metadata, for the file tags + * and SEEKTABLE + */ + FLAC__stream_decoder_set_metadata_respond(main_decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT); + FLAC__stream_decoder_set_metadata_respond(main_decoder, FLAC__METADATA_TYPE_SEEKTABLE); + + /* + * Initialize decoders + */ + if (FLAC__STREAM_DECODER_INIT_STATUS_OK != (ret = FLAC__stream_decoder_init_stream( + test_decoder, + read_callback, + seek_callback, + tell_callback, + length_callback, + eof_callback, + write_callback, + metadata_callback, + error_callback, + test_info))) { + _ERROR("Could not initialize test FLAC decoder: %s(%d)", StreamDecoderInitState(ret), ret); + _LEAVE; + } + + if (FLAC__STREAM_DECODER_INIT_STATUS_OK != (ret = FLAC__stream_decoder_init_stream( + main_decoder, + read_callback, + seek_callback, + tell_callback, + length_callback, + eof_callback, + write_callback, + metadata_callback, + error_callback, + main_info))) { + _ERROR("Could not initialize main FLAC decoder: %s(%d)", StreamDecoderInitState(ret), ret); + _LEAVE; + } + + /* + * Initialize the play loop mutex + */ + flac_pl_mutex = g_mutex_new(); + + _DEBUG("plugin initialized OK!"); + plugin_initialized = TRUE; + _LEAVE; +} + +/* --- */ + +gboolean flac_is_our_file(gchar* filename) { + + _ENTER; + + if (!plugin_initialized) { + _ERROR("Plugin not initialized!"); + _LEAVE FALSE; + } + + _DEBUG("Testing file: %s", filename); + + if (FALSE == read_metadata(filename, test_decoder, test_info)) { + _DEBUG("File not handled by this plugin!"); + _LEAVE FALSE; + } + + /* + * See if the metadata has changed + */ + if (FALSE == test_info->metadata_changed) { + _DEBUG("No metadata found in stream"); + _LEAVE FALSE; + } + + /* + * If we get here, the file is supported by FLAC. + * The stream characteristics have been filled in by + * the metadata callback. + * We can close the stream now. + */ + + vfs_fclose(test_info->input_stream); + test_info->input_stream = NULL; + + + _DEBUG("Stream encoded at %d Hz, %d bps, %d channels", + test_info->stream.samplerate, + test_info->stream.bits_per_sample, + test_info->stream.channels); + + if (MAX_SUPPORTED_CHANNELS < test_info->stream.channels) { + _ERROR("This number of channels (%d) is currently not supported, stream not handled by this plugin", + test_info->stream.channels); + _LEAVE FALSE; + } + + if ((16 != test_info->stream.bits_per_sample) && + (24 != test_info->stream.bits_per_sample) && + (8 != test_info->stream.bits_per_sample)) { + _ERROR("This number of bits (%d) is currently not supported, stream not handled by this plugin", + test_info->stream.bits_per_sample); + _LEAVE FALSE; + } + + /* + * Looks good. + */ + + _DEBUG("Accepting file %s", filename); + + reset_info(test_info); + + _LEAVE TRUE; +} + +/* --- */ + +void squeeze_audio(gint32* src, void* dst, guint count, guint src_res, guint dst_res) { + + /* + * Changes the sample width of count samples in src from + * src_res to dst_res and places the result in dst + */ + + gint i; + gint32* rp = src; + gint8* wp = dst; + gint16* wp2 = dst; + gint32* wp4 = dst; + + _ENTER; + + _DEBUG("Converting %d samples %d->%d", count, src_res, dst_res); + + if ((src_res % 8 != 0) || (dst_res % 8 != 0)) { + _ERROR("Can not convert from %d bps to %d bps: not a multiple of 8", + src_res, dst_res); + _LEAVE; + } + + if (16 == dst_res) { + if (8 == src_res) { + for (i=0; i<count; i++, wp2++, rp++) { + *wp2 = (*rp << 8) & 0xffff; + } + } else if (16 == src_res) { + for (i=0; i<count; i++, wp2++, rp++) { + *wp2 = *rp & 0xffff; + } + } else if (24 == src_res) { + for (i=0; i<count; i++, wp2++, rp++) { + *wp2 = (*rp >> 8) & 0xffff; + } + } else if (32 == src_res) { + for (i=0; i<count; i++, wp2++, rp++) { + *wp2 = (*rp >> 16) & 0xffff; + } + } + } else if (8 == dst_res) { + if (8 == src_res) { + for (i=0; i<count; i++, wp++, rp++) { + *wp = *rp & 0xff; + } + } else if (16 == src_res) { + for (i=0; i<count; i++, wp++, rp++) { + *wp = (*rp >> 8) & 0xff; + } + } else if (24 == src_res) { + for (i=0; i<count; i++, wp++, rp++) { + *wp = (*rp >> 16) & 0xff; + } + } else if (32 == src_res) { + for (i=0; i<count; i++, wp++, rp++) { + *wp = (*rp >> 24) & 0xff; + } + } + } else if (32 == dst_res) { + if (8 == src_res) { + for (i=0; i<count; i++, wp4++, rp++) { + *wp4 = (*rp << 24) & 0xffffffff; + } + } else if (16 == src_res) { + for (i=0; i<count; i++, wp4++, rp++) { + *wp4 = (*rp << 16) & 0xffffffff; + } + } else if (24 == src_res) { + for (i=0; i<count; i++, wp4++, rp++) { + *wp4 = (*rp << 8) & 0xffffffff; + } + } else if (32 == src_res) { + for (i=0; i<count; i++, wp4++, rp++) { + *wp4 = *rp; + } + } + } + + _LEAVE; +} + +/* --- */ + +static gpointer flac_play_loop(gpointer arg) { + + /* + * The main play loop. + * Decode a frame, push the decoded data to the output plugin + * chunkwise. Repeat until finished. + * + * Must be entered with flac_pl_mutex held! + */ + + gint ofree; + gint32* read_pointer; + gint elements_left; + gint seek_sample; + FLAC__StreamDecoderState state; + struct stream_info stream_info; + guint sample_count; + void* play_buffer; + InputPlayback* playback = (InputPlayback *) arg; + + _ENTER; + + if (NULL == (play_buffer = malloc(BUFFER_SIZE_BYTE))) { + _ERROR("Could not allocate conversion buffer"); + playback->playing = FALSE; + g_mutex_unlock(flac_pl_mutex); + _LEAVE NULL; + } + + stream_info.samplerate = main_info->stream.samplerate; + stream_info.channels = main_info->stream.channels; + main_info->metadata_changed = FALSE; + + if (!playback->output->open_audio(FMT_S16_NE, + main_info->stream.samplerate, + main_info->stream.channels)) { + playback->playing = FALSE; + _ERROR("Could not open output plugin!"); + g_mutex_unlock(flac_pl_mutex); + _LEAVE NULL; + } + + while (TRUE == playback->playing) { + + /* + * Try to decode a single frame of audio + */ + if (FALSE == FLAC__stream_decoder_process_single(main_decoder)) { + /* + * Something went wrong + */ + _ERROR("Error while decoding!"); + break; + } + + /* + * Maybe the metadata changed midstream. Check. + */ + + if (main_info->metadata_changed) { + /* + * Samplerate and channels are important + */ + if (stream_info.samplerate != main_info->stream.samplerate) { + _ERROR("Samplerate changed midstream (now: %d, was: %d). This is not supported yet.", + main_info->stream.samplerate, stream_info.samplerate); + break; + } + + if (stream_info.channels != main_info->stream.channels) { + _ERROR("Number of channels changed midstream (now: %d, was: %d). This is not supported yet.", + main_info->stream.channels, stream_info.channels); + break; + } + + main_info->metadata_changed = FALSE; + } + + /* + * Compare the frame metadata to the current stream metadata + */ + if (main_info->stream.samplerate != main_info->frame.samplerate) { + _ERROR("Frame samplerate mismatch (stream: %d, frame: %d)!", + main_info->stream.samplerate, main_info->frame.samplerate); + break; + } + + if (main_info->stream.channels != main_info->frame.channels) { + _ERROR("Frame channel mismatch (stream: %d, frame: %d)!", + main_info->stream.channels, main_info->frame.channels); + break; + } + + /* + * If the frame decoded was an audio frame we now + * have data in info->output_buffer + * + * The data in this buffer is in 32 bit wide samples, even if the + * real sample width is smaller. It has to be converted before + * feeding it to he output plugin. + * + * Feed the data in chunks of OUTPUT_BLOCK_SIZE elements to the output + * plugin + */ + read_pointer = main_info->output_buffer; + elements_left = main_info->buffer_used; + + while ((TRUE == playback->playing) && (0 != elements_left)) { + + sample_count = MIN(OUTPUT_BLOCK_SIZE, elements_left); + + /* + * Convert the audio. + * Currently this is hardcoded to 16 bit. + * 8 and 24 bit are sampled up/down linear. + */ + _DEBUG("Converting %d samples to FMT_S16_NE", sample_count); + squeeze_audio(read_pointer, play_buffer, sample_count, main_info->frame.bits_per_sample, 16); + + _DEBUG("Copying %d samples to output plugin", sample_count); + + produce_audio(flac_ip.output->written_time(), FMT_S16_NE, main_info->frame.channels, sample_count * sizeof(gint16), play_buffer, NULL); + + read_pointer += sample_count; + elements_left -= sample_count; + + _DEBUG("%d elements left to be output", elements_left); + } + + /* + * Clear the buffer. + */ + main_info->write_pointer = main_info->output_buffer; + main_info->buffer_free = BUFFER_SIZE_SAMP; + main_info->buffer_used = 0; + + /* + * Do we have to seek to somewhere? + */ + if (-1 != seek_to) { + _DEBUG("Seek requested to %d seconds", seek_to); + + if (FALSE == main_info->stream.has_seektable) { + _ERROR("Stream does not have a seektable, can not seek!"); + } else { + seek_sample = seek_to * main_info->stream.samplerate; + _DEBUG("Seek requested to sample %d", seek_sample); + if (FALSE == FLAC__stream_decoder_seek_absolute(main_decoder, seek_sample)) { + _ERROR("Could not seek to sample %d!", seek_sample); + } else { + /* + * Flush the buffers + */ + flac_ip.output->flush(seek_to * 1000); + } + } + seek_to = -1; + } + + /* + * Have we reached the end of the stream? + */ + state = FLAC__stream_decoder_get_state(main_decoder); + if (0 == elements_left && (FLAC__STREAM_DECODER_END_OF_STREAM == state)) { + /* + * Yes. Drain the output buffer and stop playing. + */ + + _DEBUG("End of stream reached, draining output buffer"); + + while((-1 == seek_to) && flac_ip.output->buffer_playing() && playback->playing == TRUE) { + g_usleep(40000); + } + + if (-1 == seek_to) { + _DEBUG("Output buffer empty."); + playback->playing = FALSE; + } + } + } + + /* + * Clean up a bit + */ + playback->playing = FALSE; + _DEBUG("Closing audio device"); + flac_ip.output->close_audio(); + _DEBUG("Audio device closed"); + + free(play_buffer); + + if (FALSE == FLAC__stream_decoder_flush(main_decoder)) { + _ERROR("Could not flush decoder state!"); + } + + /* + * Release the play loop mutex + */ + g_mutex_unlock(flac_pl_mutex); + + g_thread_exit(NULL); + + _LEAVE NULL; +} + +/* --- */ + +void flac_play_file (InputPlayback* input) { + + gint l; + + _ENTER; + + if (!plugin_initialized) { + _ERROR("plugin not initialized!"); + _LEAVE; + } + + /* + * This will end a currently running decoder thread + */ + input->playing = FALSE; + xmms_usleep(20000); + + if (FALSE == read_metadata(input->filename, main_decoder, main_info)) { + _ERROR("Could not prepare file for playing!"); + _LEAVE; + } + + /* + * Calculate the length + */ + if (0 == main_info->stream.samplerate) { + _ERROR("Invalid sample rate for stream!"); + l = -1; + } else { + l = (main_info->stream.samples / main_info->stream.samplerate) * 1000; + } + + /* + * Grab the play loop mutex + */ + _DEBUG("Waiting for play loop mutex..."); + g_mutex_lock(flac_pl_mutex); + _DEBUG("Got play loop mutex"); + + input->playing = TRUE; + + flac_ip.set_info(get_title(input->filename, main_info), l, -1, main_info->stream.samplerate, main_info->stream.channels); + + thread = g_thread_create(flac_play_loop, input, TRUE, NULL); + + _LEAVE; +} + +/* --- */ + +void flac_stop(InputPlayback* input) { + + _ENTER; + + input->playing = FALSE; + + _LEAVE; +} + +/* --- */ + +void flac_pause(InputPlayback* input, gshort p) { + + _ENTER; + + input->output->pause(p); + + _LEAVE; +} + +/* --- */ + +void flac_seek(InputPlayback* input, gint time) { + + _ENTER; + + if (!input->playing) { + _DEBUG("Can not seek while not playing"); + _LEAVE; + } + + _DEBUG("Requesting seek to %d", time); + seek_to = time; + + while (-1 != seek_to) { + xmms_usleep(10000); + } + + _LEAVE; +} + +/* --- */ + +void flac_get_song_info(gchar* filename, gchar** title, gint* length) { + + gint l; + + _ENTER; + + if (FALSE == read_metadata(filename, test_decoder, test_info)) { + _ERROR("Could not read file info!"); + *length = -1; + *title = g_strdup(""); + _LEAVE; + } + + /* + * Calculate the stream length (milliseconds) + */ + if (0 == test_info->stream.samplerate) { + _ERROR("Invalid sample rate for stream!"); + l = -1; + } else { + l = (test_info->stream.samples / test_info->stream.samplerate) * 1000; + _DEBUG("Stream length: %d seconds", l/1000); + } + + *length = l; + *title = get_title(filename, test_info); + + reset_info(test_info); + + _LEAVE; +} + +/* --- */ + +TitleInput *flac_get_song_tuple(gchar* filename) { + + gint l; + TitleInput *tuple; + + _ENTER; + + if (FALSE == read_metadata(filename, test_decoder, test_info)) { + _ERROR("Could not read metadata tuple for file <%s>", filename); + _LEAVE NULL; + } + + tuple = get_tuple(filename, test_info); + + reset_info(test_info); + + _LEAVE tuple; +} + +/* --- */ + +void flac_aboutbox(void) { + + static GtkWidget* about_window; + + if (about_window) { + gdk_window_raise(about_window->window); + } + + about_window = xmms_show_message(_("About FLAC Audio Plugin"), + ("FLAC Audio Plugin (" _VERSION ")\n\n" + "Original code by\n" + "Ralf Ertzinger <ralf@skytale.net>\n" + "\n" + "http://www.skytale.net/projects/bmp-flac2/"), + _("OK"), FALSE, NULL, NULL); + g_signal_connect(G_OBJECT(about_window), "destroy", + G_CALLBACK(gtk_widget_destroyed), &about_window); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flacng/plugin.h Mon Apr 09 10:55:23 2007 -0700 @@ -0,0 +1,14 @@ +#ifndef _PLUGIN_H +#define _PLUGIN_H + +void flac_init(void); +void flac_aboutbox(void); +gboolean flac_is_our_file(gchar* filename); +void flac_play_file (InputPlayback* input); +void flac_stop(InputPlayback* input); +void flac_pause(InputPlayback* input, gshort p); +void flac_seek(InputPlayback* input, gint time); +void flac_get_song_info(gchar* filename, gchar** title, gint* length); +TitleInput *flac_get_song_tuple(gchar* filename); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flacng/seekable_stream_callbacks.c Mon Apr 09 10:55:23 2007 -0700 @@ -0,0 +1,338 @@ +/* + * A FLAC decoder plugin for the Audacious Media Player + * Copyright (C) 2005 Ralf Ertzinger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <FLAC/all.h> +#include <glib.h> +#include <glib/gi18n.h> +#include <audacious/vfs.h> +#include "flacng.h" +#include "tools.h" +#include "seekable_stream_callbacks.h" +#include "debug.h" + +/* === */ + +FLAC__StreamDecoderReadStatus read_callback(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data) { + + callback_info* info; + gint to_read; + size_t read; + + _ENTER; + + info = (callback_info*) client_data; + _DEBUG("Using callback_info %s", info->name); + + if (NULL == info->input_stream) { + _ERROR("Trying to read data from an uninitialized file!"); + _LEAVE FLAC__STREAM_DECODER_READ_STATUS_ABORT; + } + + if (0 <= info->read_max) { + to_read = MIN(*bytes, info->read_max); + _DEBUG("Reading restricted to %d bytes", info->read_max); + } else { + to_read = *bytes; + } + + if (0 == to_read) { + _LEAVE FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + } + + read = vfs_fread(buffer, 1, to_read, info->input_stream); + + if ((0 < read) && (0 < info->read_max)) { + info->read_max -= read; + } + + _DEBUG("Wanted %d bytes, got %d bytes", *bytes, read); + *bytes = read; + + switch(read) { + case -1: + _ERROR("Error while reading from stream!"); + _LEAVE FLAC__STREAM_DECODER_READ_STATUS_ABORT; + break; + + case 0: + _DEBUG("Stream reached EOF"); + _LEAVE FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + break; + + default: + _LEAVE FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + } +} + +/* --- */ + +FLAC__StreamDecoderSeekStatus seek_callback(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data) { + + callback_info* info; + + _ENTER; + + info = (callback_info*) client_data; + _DEBUG("Using callback_info %s", info->name); + + _DEBUG("Seeking to %lld", absolute_byte_offset); + + if (0 != vfs_fseek(info->input_stream, absolute_byte_offset, SEEK_SET)) { + _ERROR("Could not seek to %lld!", absolute_byte_offset); + _LEAVE FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; + } + + _LEAVE FLAC__STREAM_DECODER_SEEK_STATUS_OK; +} + +/* --- */ + +FLAC__StreamDecoderTellStatus tell_callback(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) { + + callback_info* info; + glong position; + + _ENTER; + + info = (callback_info*) client_data; + _DEBUG("Using callback_info %s", info->name); + + if (-1 == (position = vfs_ftell(info->input_stream))) { + fprintf(stderr, "Could not tell current position!"); + return FLAC__STREAM_DECODER_TELL_STATUS_ERROR; + } + + _DEBUG("Current position: %d", position); + + *absolute_byte_offset = position; + + _LEAVE FLAC__STREAM_DECODER_TELL_STATUS_OK; +} + +/* --- */ + +FLAC__bool eof_callback(const FLAC__StreamDecoder *decoder, void *client_data) { + + callback_info* info; + gboolean eof; + + _ENTER; + + info = (callback_info*) client_data; + _DEBUG("Using callback_info %s", info->name); + + eof = vfs_feof(info->input_stream); + + _LEAVE eof; +} + +/* --- */ + +FLAC__StreamDecoderLengthStatus length_callback(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data) { + + callback_info* info; + size_t size; + + _ENTER; + + info = (callback_info*) client_data; + _DEBUG("Using callback_info %s", info->name); + + *stream_length = 0; + + _LEAVE FLAC__STREAM_DECODER_LENGTH_STATUS_OK; +} + +/* --- */ + +FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) { + + glong i; + gshort j; + gint32 sample; + callback_info* info; + + _ENTER; + + info = (callback_info*) client_data; + _DEBUG("Using callback_info %s", info->name); + + _DEBUG("Frame decoded: %d samples per channel, %d channels, %d bps", + frame->header.blocksize, frame->header.channels, frame->header.bits_per_sample); + + /* + * Check if there is more data decoded than we have space + * for. This _should_ not happen given how our buffer is sized, + * but you never know. + */ + if (info->buffer_free < (frame->header.blocksize * frame->header.channels)) { + _ERROR("BUG! Too much data decoded from stream!"); + _LEAVE FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + + if ((frame->header.bits_per_sample != 8) && + (frame->header.bits_per_sample != 16) && + (frame->header.bits_per_sample != 24)) { + _ERROR("Unsupported bitrate found in stream: %d!", frame->header.bits_per_sample); + _LEAVE FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + + /* + * Copy the frame metadata, will be compared to stream + * metadata later + * This also describes the format of the current buffer content. + */ + info->frame.channels = frame->header.channels; + info->frame.samplerate = frame->header.sample_rate; + info->frame.bits_per_sample = frame->header.bits_per_sample; + + for (i=0; i < frame->header.blocksize; i++) { + for (j=0; j < frame->header.channels; j++) { + *(info->write_pointer++) = buffer[j][i]; + info->buffer_free -= 1; + info->buffer_used += 1; + } + } + + _DEBUG("free space in buffer after copying: %d samples", info->buffer_free); + + _LEAVE FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; +} + +/* --- */ + +void error_callback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) { + + _ENTER; + + _ERROR("FLAC decoder error callback was called: %d", status); + + _LEAVE; + +} + +/* --- */ + +void metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) { + + callback_info* info; + gint i; + FLAC__StreamMetadata_VorbisComment_Entry* entry; + FLAC__StreamMetadata* metadata_copy; + gchar* key; + gchar* value; + int artist_offset; + + _ENTER; + + info = (callback_info*) client_data; + _DEBUG("Using callback_info %s", info->name); + + /* + * We have found a metadata block. Enable unrestricted reading + */ + info->read_max = -1; + + if (FLAC__METADATA_TYPE_STREAMINFO == metadata->type) { + /* + * Basic stream information. Sample rate, channels and stuff + */ + _DEBUG("FLAC__METADATA_TYPE_STREAMINFO found"); + + info->stream.samples = metadata->data.stream_info.total_samples; + _DEBUG("total_samples=%lld", metadata->data.stream_info.total_samples); + info->stream.bits_per_sample = metadata->data.stream_info.bits_per_sample; + _DEBUG("bits_per_sample=%d", metadata->data.stream_info.bits_per_sample); + info->stream.channels = metadata->data.stream_info.channels; + _DEBUG("channels=%d", metadata->data.stream_info.channels); + info->stream.samplerate = metadata->data.stream_info.sample_rate; + _DEBUG("sample_rate=%d", metadata->data.stream_info.sample_rate); + + info->metadata_changed = TRUE; + } + + if (FLAC__METADATA_TYPE_VORBIS_COMMENT == metadata->type) { + /* + * We will possibly need to modify some of the entries + * in the metadata field, so we make a copy of it + * first. + * The original structure must not be modified. + */ + metadata_copy = FLAC__metadata_object_clone(metadata); + + /* + * A vorbis type comment field. + */ + _DEBUG("FLAC__METADATA_TYPE_VORBIS_COMMENT found"); + _DEBUG("Vorbis comment contains %d fields", metadata_copy->data.vorbis_comment.num_comments); + _DEBUG("Vendor string: %s", metadata_copy->data.vorbis_comment.vendor_string.entry); + + /* + * Find an ARTIST field + */ + if (0 <= (artist_offset = FLAC__metadata_object_vorbiscomment_find_entry_from(metadata_copy, 0, "ARTIST"))) { + _DEBUG("ARTIST field found @ %d: %s", artist_offset, + metadata_copy->data.vorbis_comment.comments[artist_offset].entry); + } + + + /* + * Enumerate the comment entries + */ + entry = metadata_copy->data.vorbis_comment.comments; + for (i=0; i < metadata_copy->data.vorbis_comment.num_comments; i++,entry++) { + _DEBUG("Comment[%d]: %s", i, entry->entry); + + /* + * Try and parse the comment. + * If FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair() succeeds, + * it allocates memory for the key and value which we have to take + * care of. + */ + if (false == FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(*entry, &key, &value)) { + _DEBUG("Could not parse comment"); + } else { + _DEBUG("Key: <%s>, Value <%s>", key, value); + add_comment(info, key, value); + free(key); + free(value); + } + } + + /* + * Free our metadata copy + */ + FLAC__metadata_object_delete(metadata_copy); + + info->metadata_changed = TRUE; + } + + if (FLAC__METADATA_TYPE_SEEKTABLE == metadata->type) { + /* + * We have found a seektable, which means that we can seek + * without telling FLAC the length of the file (which we can not + * do, since Audacious lacks the functions for that) + */ + _DEBUG("FLAC__METADATA_TYPE_SEEKTABLE found"); + info->stream.has_seektable = TRUE; + } + + _LEAVE; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flacng/seekable_stream_callbacks.h Mon Apr 09 10:55:23 2007 -0700 @@ -0,0 +1,35 @@ +/* + * A FLAC decoder plugin for the Audacious Media Player + * Copyright (C) 2005 Ralf Ertzinger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef CALLBACKS_H +#define CALLBACKS_H + +#include "flac_compat112.h" +#include "flac_compat113.h" +#include "flac_compat114.h" + +FLAC__StreamDecoderReadStatus read_callback(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data); +FLAC__StreamDecoderSeekStatus seek_callback(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data); +FLAC__StreamDecoderTellStatus tell_callback(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data); +FLAC__bool eof_callback(const FLAC__StreamDecoder *decoder, void *client_data); +FLAC__StreamDecoderLengthStatus length_callback(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data); +FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data); +void error_callback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); +void metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flacng/tools.c Mon Apr 09 10:55:23 2007 -0700 @@ -0,0 +1,478 @@ +/* + * A FLAC decoder plugin for the Audacious Media Player + * Copyright (C) 2005 Ralf Ertzinger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <strings.h> +#include "tools.h" +#include "debug.h" + +/* === */ + +callback_info* init_callback_info(gchar* name) { + + callback_info* info; + + _ENTER; + + if (NULL == name) { + _ERROR("Can not allocate callback structure without a name"); + _LEAVE NULL; + } + + if (NULL == (info = malloc(sizeof(callback_info)))) { + _ERROR("Could not allocate memory for callback structure!"); + _LEAVE NULL; + } + + if (NULL == (info->output_buffer = malloc(BUFFER_SIZE_BYTE))) { + _ERROR("Could not allocate memory for output buffer!"); + _LEAVE NULL; + } + + /* + * We need to set this manually to NULL because + * reset_info will try to close the file pointed to + * by input_stream if it is non-NULL. + * Same for info->comment.x + */ + info->name = name; + info->input_stream = NULL; + info->comment.artist = NULL; + info->comment.album = NULL; + info->comment.title = NULL; + info->comment.tracknumber = NULL; + info->comment.genre = NULL; + info->comment.date = NULL; + info->replaygain.ref_loud = NULL; + info->replaygain.track_gain = NULL; + info->replaygain.track_peak = NULL; + info->replaygain.album_gain = NULL; + info->replaygain.album_peak = NULL; + reset_info(info); + + _DEBUG("Playback buffer allocated for %d samples, %d bytes", BUFFER_SIZE_SAMP, BUFFER_SIZE_BYTE); + + _LEAVE info; +} + +/* --- */ + +void reset_info(callback_info* info) { + + _ENTER; + + _DEBUG("Using callback_info %s", info->name); + + if (NULL != info->input_stream) { + vfs_fclose(info->input_stream); + info->input_stream = NULL; + } + + // memset(info->output_buffer, 0, BUFFER_SIZE * sizeof(int16_t)); + info->stream.samplerate = 0; + info->stream.bits_per_sample = 0; + info->stream.channels = 0; + info->stream.samples = 0; + info->stream.has_seektable = FALSE; + info->buffer_free = BUFFER_SIZE_SAMP; + info->buffer_used = 0; + info->write_pointer = info->output_buffer; + info->read_max = -1; + + /* + * Clear the stream and frame information + */ + info->stream.bits_per_sample = 0; + info->stream.samplerate = 0; + info->stream.channels = 0; + info->stream.samples = 0; + + if (NULL != info->comment.artist) { + free(info->comment.artist); + info->comment.artist = NULL; + } + + if (NULL != info->comment.album) { + free(info->comment.album); + info->comment.album = NULL; + } + + if (NULL != info->comment.title) { + free(info->comment.title); + info->comment.title = NULL; + } + + if (NULL != info->comment.tracknumber) { + free(info->comment.tracknumber); + info->comment.tracknumber = NULL; + } + + if (NULL != info->comment.genre) { + free(info->comment.genre); + info->comment.genre = NULL; + } + + if (NULL != info->replaygain.ref_loud) { + free(info->replaygain.ref_loud); + info->replaygain.ref_loud = NULL; + } + + if (NULL != info->replaygain.track_gain) { + free(info->replaygain.track_gain); + info->replaygain.track_gain = NULL; + } + + if (NULL != info->replaygain.track_peak) { + free(info->replaygain.track_peak); + info->replaygain.track_peak = NULL; + } + + if (NULL != info->replaygain.album_gain) { + free(info->replaygain.album_gain); + info->replaygain.album_gain = NULL; + } + + if (NULL != info->replaygain.album_peak) { + free(info->replaygain.album_peak); + info->replaygain.album_peak = NULL; + } + + info->replaygain.has_rg = FALSE; + + if (NULL != info->comment.date) { + free(info->comment.date); + info->comment.date = NULL; + } + + info->frame.bits_per_sample = 0; + info->frame.samplerate = 0; + info->frame.channels = 0; + + info->metadata_changed = FALSE; + + _LEAVE; +} + +/* --- */ + +gchar* StreamDecoderInitState(FLAC__StreamDecoderInitStatus state) { + + _ENTER; + + switch(state) { + case FLAC__STREAM_DECODER_INIT_STATUS_OK: + _LEAVE "FLAC__STREAM_DECODER_INIT_STATUS_OK"; + break; + + case FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER: + _LEAVE "FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER"; + break; + + case FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS: + _LEAVE "FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS"; + break; + + case FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR: + _LEAVE "FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR"; + break; + + case FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE: + _LEAVE "FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE"; + break; + + case FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED: + _LEAVE "FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED"; + break; + } + + _LEAVE "Unknown state"; +} + +/* --- */ + +gchar* StreamDecoderState(FLAC__StreamDecoderState state) { + + _ENTER; + + switch(state) { + + case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA: + _LEAVE "FLAC__STREAM_DECODER_SEARCH_FOR_METADATA"; + break; + + case FLAC__STREAM_DECODER_READ_METADATA: + _LEAVE "FLAC__STREAM_DECODER_READ_METADATA"; + break; + + case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC: + _LEAVE "FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC"; + break; + + case FLAC__STREAM_DECODER_READ_FRAME: + _LEAVE "FLAC__STREAM_DECODER_READ_FRAME"; + break; + + case FLAC__STREAM_DECODER_END_OF_STREAM: + _LEAVE "FLAC__STREAM_DECODER_END_OF_STREAM"; + break; + + case FLAC__STREAM_DECODER_ABORTED: + _LEAVE "FLAC__STREAM_DECODER_ABORTED"; + break; + + case FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR: + _LEAVE "FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR"; + break; + + case FLAC__STREAM_DECODER_UNINITIALIZED: + _LEAVE "FLAC__STREAM_DECODER_UNINITIALIZED"; + break; + + } + + _LEAVE ""; +} + +/* --- */ + +gboolean read_metadata(gchar* filename, FLAC__StreamDecoder* decoder, callback_info* info) { + + FLAC__StreamDecoderState ret; + + _ENTER; + + _DEBUG("Using callback_info %s", info->name); + + _DEBUG("Opening file %s", filename); + + /* + * Reset the decoder + */ + if (false == FLAC__stream_decoder_reset(decoder)) { + _ERROR("Could not reset the decoder!"); + _LEAVE FALSE; + } + + reset_info(info); + + /* + * Just scan the first 8k for the start of metadata + */ + + info->read_max = 8192; + + /* + * Open the file + */ + if (NULL == (info->input_stream = vfs_fopen(filename, "rb"))) { + _ERROR("Could not open file for reading!"); + _LEAVE FALSE; + } + + /* + * Try to decode the metadata + */ + if (false == FLAC__stream_decoder_process_until_end_of_metadata(decoder)) { + ret = FLAC__stream_decoder_get_state(decoder); + _DEBUG("Could not read the metadata: %s(%d)!", StreamDecoderState(ret), ret); + reset_info(info); + _LEAVE FALSE; + } + + /* + * Seems to be a FLAC stream. Allow further reading + */ + + info->read_max = -1; + + _LEAVE TRUE; +} + +/* --- */ + +static const gchar* get_extension(const gchar* filename) { + + _ENTER; + + const gchar *ext = strrchr(filename, '.'); + _LEAVE ext ? ext + 1 : NULL; +} + +/* --- */ + +TitleInput* get_tuple(const gchar* filename, callback_info* info) { + + TitleInput *out; + + _ENTER; + + _DEBUG("Using callback_info %s", info->name); + + out = bmp_title_input_new(); + + out->file_name = g_path_get_basename(filename); + out->file_ext = get_extension(filename); + out->file_path = g_path_get_dirname(filename); + + out->performer = g_strdup(info->comment.artist); + out->track_name = g_strdup(info->comment.title); + out->album_name = g_strdup(info->comment.album); + out->genre = g_strdup(info->comment.genre); + if (info->comment.tracknumber != NULL) + out->track_number = atoi(info->comment.tracknumber); + if (info->comment.date != NULL) + out->year = atoi(info->comment.date); + + /* + * Calculate the stream length (milliseconds) + */ + if (0 == info->stream.samplerate) { + _ERROR("Invalid sample rate for stream!"); + out->length = -1; + } else { + out->length = (info->stream.samples / info->stream.samplerate) * 1000; + _DEBUG("Stream length: %d seconds", out->length / 1000); + } + + _DEBUG("Tuple created: [%p]", out); + + _LEAVE out; +} + +/* --- */ + +gchar* get_title(const gchar* filename, callback_info* info) { + + TitleInput *input; + gchar *title; + + _ENTER; + + _DEBUG("Using callback_info %s", info->name); + + input = get_tuple(filename, info); + + if (!(title = xmms_get_titlestring(xmms_get_gentitle_format(), input))) + title = g_strdup(input->file_name); + + bmp_title_input_free(input); + + _DEBUG("Title created: <%s>", title); + + _LEAVE title; +} + +/* --- */ + +void add_comment(callback_info* info, gchar* key, gchar* value) { + + /* + * We are currently interested in comments of type + * - ARTIST + * - ALBUM + * - TITLE + * - TRACKNUMBER + * + * We have to create copies of the value string. + */ + + gchar** destination = NULL; + gboolean rg = FALSE; + + _ENTER; + + _DEBUG("Using callback_info %s", info->name); + + if (0 == strcasecmp(key, "ARTIST")) { + _DEBUG("Found key ARTIST"); + destination = &(info->comment.artist); + } + + if (0 == strcasecmp(key, "ALBUM")) { + _DEBUG("Found key ALBUM"); + destination = &(info->comment.album); + } + + if (0 == strcasecmp(key, "TITLE")) { + _DEBUG("Found key TITLE"); + destination = &(info->comment.title); + } + + if (0 == strcasecmp(key, "TRACKNUMBER")) { + _DEBUG("Found key TRACKNUMBER"); + destination = &(info->comment.tracknumber); + } + + if (0 == strcasecmp(key, "REPLAYGAIN_REFERENCE_LOUDNESS")) { + _DEBUG("Found key REPLAYGAIN_REFERENCE_LOUDNESS"); + destination = &(info->replaygain.ref_loud); + rg = TRUE; + } + + if (0 == strcasecmp(key, "REPLAYGAIN_TRACK_GAIN")) { + _DEBUG("Found key REPLAYGAIN_TRACK_GAIN"); + destination = &(info->replaygain.track_gain); + rg = TRUE; + } + + if (0 == strcasecmp(key, "REPLAYGAIN_TRACK_PEAK")) { + _DEBUG("Found key REPLAYGAIN_TRACK_PEAK"); + destination = &(info->replaygain.track_peak); + rg = TRUE; + } + + if (0 == strcasecmp(key, "REPLAYGAIN_ALBUM_GAIN")) { + _DEBUG("Found key REPLAYGAIN_ALBUM_GAIN"); + destination = &(info->replaygain.album_gain); + rg = TRUE; + } + + if (0 == strcasecmp(key, "REPLAYGAIN_ALBUM_PEAK")) { + _DEBUG("Found key REPLAYGAIN_ALBUM_PEAK"); + destination = &(info->replaygain.album_peak); + rg = TRUE; + } + + if (0 == strcasecmp(key, "DATE")) { + _DEBUG("Found key DATE"); + destination = &(info->comment.date); + } + + if (0 == strcasecmp(key, "GENRE")) { + _DEBUG("Found key GENRE"); + destination = &(info->comment.genre); + } + + if (NULL != destination) { + if (NULL != *destination) { + g_free(*destination); + } + + if (NULL == (*destination = g_strdup(value))) { + _ERROR("Could not allocate memory for comment!"); + _LEAVE; + } + } + + if (rg) { + info->replaygain.has_rg = TRUE; + } + + _LEAVE; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/flacng/tools.h Mon Apr 09 10:55:23 2007 -0700 @@ -0,0 +1,34 @@ +/* + * A FLAC decoder plugin for the Audacious Media Player + * Copyright (C) 2005 Ralf Ertzinger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef _TOOLS_H +#define _TOOLS_H + +#include <glib.h> +#include <FLAC/all.h> +#include "flacng.h" + +callback_info* init_callback_info(gchar* name); +void reset_info(callback_info* info); +gchar* get_title(const gchar* filename, callback_info* info); +TitleInput *get_tuple(const gchar *filename, callback_info* info); +void add_comment(callback_info* info, gchar* key, gchar* value); +gchar* StreamDecoderInitState(FLAC__StreamDecoderInitStatus); +gboolean read_metadata(gchar* filename, FLAC__StreamDecoder* decoder, callback_info* info); + +#endif