chiark / gitweb /
disobedience/disobedience.c: Show track title (or excuse) in title.
[disorder] / plugins / tracklength-flac.c
1 /*
2  * This file is part of DisOrder.
3  * Copyright (C) 2004, 2005, 2007, 2011 Richard Kettlewell
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 /** @file plugins/tracklength-flac.c
19  * @brief Compute track lengths for FLAC files
20  */
21 #include "tracklength.h"
22 #include <FLAC/stream_decoder.h>
23
24 /* libFLAC's "simplified" interface is rather heavyweight... */
25
26 /** @brief State used when computing FLAC file length */
27 struct flac_state {
28   /** @brief Duration or -1 */
29   long duration;
30
31   /** @brief File being analyzed */
32   const char *path;
33 };
34
35 static void flac_metadata(const FLAC__StreamDecoder attribute((unused)) *decoder,
36                           const FLAC__StreamMetadata *metadata,
37                           void *client_data) {
38   struct flac_state *const state = client_data;
39   const FLAC__StreamMetadata_StreamInfo *const stream_info
40     = &metadata->data.stream_info;
41
42   if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO)
43     /* FLAC uses 0 to mean unknown and conveniently so do we */
44     state->duration = (stream_info->total_samples
45                        + stream_info->sample_rate - 1)
46            / stream_info->sample_rate;
47 }
48
49 static void flac_error(const FLAC__StreamDecoder attribute((unused)) *decoder,
50                        FLAC__StreamDecoderErrorStatus status,
51                        void *client_data) {
52   const struct flac_state *const state = client_data;
53
54   disorder_error(0, "error decoding %s: %s", state->path,
55                  FLAC__StreamDecoderErrorStatusString[status]);
56 }
57
58 static FLAC__StreamDecoderWriteStatus flac_write
59     (const FLAC__StreamDecoder attribute((unused)) *decoder,
60      const FLAC__Frame attribute((unused)) *frame,
61      const FLAC__int32 attribute((unused)) *const buffer_[],
62      void attribute((unused)) *client_data) {
63   const struct flac_state *const state = client_data;
64
65   if(state->duration >= 0)
66     return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
67   else
68     return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
69 }
70
71 long tl_flac(const char *path) {
72   FLAC__StreamDecoder *sd = 0;
73   FLAC__StreamDecoderInitStatus is;
74   struct flac_state state[1];
75
76   state->duration = -1;                 /* error */
77   state->path = path;
78   if(!(sd = FLAC__stream_decoder_new())) {
79     disorder_error(0, "FLAC__stream_decoder_new failed");
80     goto fail;
81   }
82   if((is = FLAC__stream_decoder_init_file(sd, path, flac_write, flac_metadata,
83                                           flac_error, state))) {
84     disorder_error(0, "FLAC__stream_decoder_init_file %s: %s",
85                    path, FLAC__StreamDecoderInitStatusString[is]);
86     goto fail;
87   }
88   FLAC__stream_decoder_process_until_end_of_metadata(sd);
89 fail:
90   if(sd)
91     FLAC__stream_decoder_delete(sd);
92   return state->duration;
93 }
94
95 /*
96 Local Variables:
97 c-basic-offset:2
98 comment-column:40
99 fill-column:79
100 End:
101 */