From c57f1201efce928c25c7899cc0baab09845c9ba7 Mon Sep 17 00:00:00 2001 Message-Id: From: Mark Wooding Date: Sat, 29 Sep 2007 23:53:02 +0100 Subject: [PATCH] FLAC support in tracklength plugin Organization: Straylight/Edgeware From: rjk@greenend.org.uk <> --- configure.ac | 3 ++ plugins/Makefile.am | 2 +- plugins/tracklength.c | 79 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 82 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 0384893..af15c83 100644 --- a/configure.ac +++ b/configure.ac @@ -144,6 +144,9 @@ if test $want_server = yes; then AC_CHECK_LIB([ao], [ao_initialize], [AC_SUBST(LIBAO,[-lao])], [missing_libraries="$missing_libraries libao"]) + AC_CHECK_LIB([FLAC], [FLAC__stream_decoder_new], + [AC_SUBST(LIBFLAC,[-lFLAC])], + [missing_libraries="$missing_libraries libFLAC"]) fi if test $want_alsa = yes; then AC_CHECK_LIB([asound], [snd_pcm_open], diff --git a/plugins/Makefile.am b/plugins/Makefile.am index cbd5b74..1b7386c 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -27,7 +27,7 @@ notify_la_LDFLAGS=-module tracklength_la_SOURCES=tracklength.c mad.c madshim.h ../lib/wav.h ../lib/wav.c tracklength_la_LDFLAGS=-module -tracklength_la_LIBADD=$(LIBVORBISFILE) $(LIBMAD) +tracklength_la_LIBADD=$(LIBVORBISFILE) $(LIBMAD) $(LIBFLAC) fs_la_SOURCES=fs.c fs_la_LDFLAGS=-module diff --git a/plugins/tracklength.c b/plugins/tracklength.c index c851f18..c1b9cb9 100644 --- a/plugins/tracklength.c +++ b/plugins/tracklength.c @@ -1,6 +1,6 @@ /* * This file is part of DisOrder. - * Portions copyright (C) 2004, 2005 Richard Kettlewell (see also below) + * Copyright (C) 2004, 2005, 2007 Richard Kettlewell * * 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 @@ -32,6 +32,7 @@ #include #include +#include #include @@ -114,13 +115,89 @@ static long tl_wav(const char *path) { return duration; } +/* libFLAC's "simplified" interface is rather heavyweight... */ + +struct flac_state { + long duration; + const char *path; +}; + +static void flac_metadata(const FLAC__FileDecoder attribute((unused)) *decoder, + const FLAC__StreamMetadata *metadata, + void *client_data) { + struct flac_state *const state = client_data; + const FLAC__StreamMetadata_StreamInfo *const stream_info + = &metadata->data.stream_info; + + if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO) + /* FLAC uses 0 to mean unknown and conveniently so do we */ + state->duration = (stream_info->total_samples + + stream_info->sample_rate - 1) + / stream_info->sample_rate; +} + +static void flac_error(const FLAC__FileDecoder attribute((unused)) *decoder, + FLAC__StreamDecoderErrorStatus status, + void *client_data) { + const struct flac_state *const state = client_data; + + disorder_error(0, "error decoding %s: %s", state->path, + FLAC__StreamDecoderErrorStatusString[status]); +} + +static FLAC__StreamDecoderWriteStatus flac_write + (const FLAC__FileDecoder attribute((unused)) *decoder, + const FLAC__Frame attribute((unused)) *frame, + const FLAC__int32 attribute((unused)) *const buffer_[], + void attribute((unused)) *client_data) { + const struct flac_state *const state = client_data; + + if(state->duration >= 0) + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + else + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; +} + +static long tl_flac(const char *path) { + FLAC__FileDecoder *fd = 0; + FLAC__FileDecoderState fs; + struct flac_state state[1]; + + state->duration = -1; /* error */ + state->path = path; + if(!(fd = FLAC__file_decoder_new())) { + disorder_error(0, "FLAC__file_decoder_new failed"); + goto fail; + } + if(!(FLAC__file_decoder_set_filename(fd, path))) { + disorder_error(0, "FLAC__file_set_filename failed"); + goto fail; + } + FLAC__file_decoder_set_metadata_callback(fd, flac_metadata); + FLAC__file_decoder_set_error_callback(fd, flac_error); + FLAC__file_decoder_set_write_callback(fd, flac_write); + FLAC__file_decoder_set_client_data(fd, state); + if((fs = FLAC__file_decoder_init(fd))) { + disorder_error(0, "FLAC__file_decoder_init: %s", + FLAC__FileDecoderStateString[fs]); + goto fail; + } + FLAC__file_decoder_process_until_end_of_metadata(fd); +fail: + if(fd) + FLAC__file_decoder_delete(fd); + return state->duration; +} + static const struct { const char *ext; long (*fn)(const char *path); } file_formats[] = { + { ".FLAC", tl_flac }, { ".MP3", tl_mp3 }, { ".OGG", tl_ogg }, { ".WAV", tl_wav }, + { ".flac", tl_flac }, { ".mp3", tl_mp3 }, { ".ogg", tl_ogg }, { ".wav", tl_wav } -- [mdw]