X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/blobdiff_plain/ce6c36be6c2df99afd01a7a602debb321322e113..64bb08384a2ba77f482873f0fcc5b94ee6f21a5e:/plugins/tracklength.c diff --git a/plugins/tracklength.c b/plugins/tracklength.c index c851f18..86a1fc8 100644 --- a/plugins/tracklength.c +++ b/plugins/tracklength.c @@ -1,21 +1,24 @@ /* * 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 + * 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 + * the Free Software Foundation, either version 3 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. - * + * 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 + * along with this program. If not, see . + */ +/** @file plugins/tracklength.c + * @brief Plugin to compute track lengths + * + * Currently implements MP3, OGG, FLAC and WAV. */ #include @@ -32,6 +35,15 @@ #include #include +/* libFLAC has had an API change and stupidly taken away the old API */ +#if HAVE_FLAC_FILE_DECODER_H +# include +#else +# include +#define FLAC__FileDecoder FLAC__StreamDecoder +#define FLAC__FileDecoderState FLAC__StreamDecoderState +#endif + #include @@ -114,13 +126,114 @@ 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) { + struct flac_state state[1]; + + state->duration = -1; /* error */ + state->path = path; +#if HAVE_FLAC_FILE_DECODER_H + { + FLAC__FileDecoder *fd = 0; + FLAC__FileDecoderState fs; + + 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); + } +#else + { + FLAC__StreamDecoder *sd = 0; + FLAC__StreamDecoderInitStatus is; + + if(!(sd = FLAC__stream_decoder_new())) { + disorder_error(0, "FLAC__stream_decoder_new failed"); + goto fail; + } + if((is = FLAC__stream_decoder_init_file(sd, path, flac_write, flac_metadata, + flac_error, state))) { + disorder_error(0, "FLAC__stream_decoder_init_file %s: %s", + path, FLAC__StreamDecoderInitStatusString[is]); + goto fail; + } + FLAC__stream_decoder_process_until_end_of_metadata(sd); +fail: + if(sd) + FLAC__stream_decoder_delete(sd); + } +#endif + 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 }