chiark / gitweb /
Disobedience: basic support for required/prohibited tags.
[disorder] / server / decode-flac.c
1 /*
2  * This file is part of DisOrder
3  * Copyright (C) 2007-2010 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 server/decode.c
19  * @brief General-purpose decoder for use by speaker process
20  */
21 #include "decode.h"
22 #include <FLAC/stream_decoder.h>
23
24 /** @brief Metadata callback for FLAC decoder
25  *
26  * This is a no-op here.
27  */
28 static void flac_metadata(const FLAC__StreamDecoder attribute((unused)) *decoder,
29                           const FLAC__StreamMetadata attribute((unused)) *metadata,
30                           void attribute((unused)) *client_data) {
31 }
32
33 /** @brief Error callback for FLAC decoder */
34 static void flac_error(const FLAC__StreamDecoder attribute((unused)) *decoder,
35                        FLAC__StreamDecoderErrorStatus status,
36                        void attribute((unused)) *client_data) {
37   disorder_fatal(0, "error decoding %s: %s", path,
38                  FLAC__StreamDecoderErrorStatusString[status]);
39 }
40
41 /** @brief Write callback for FLAC decoder */
42 static FLAC__StreamDecoderWriteStatus flac_write
43     (const FLAC__StreamDecoder attribute((unused)) *decoder,
44      const FLAC__Frame *frame,
45      const FLAC__int32 *const buffer[],
46      void attribute((unused)) *client_data) {
47   size_t n, c;
48
49   output_header(frame->header.sample_rate,
50                 frame->header.channels,
51                 frame->header.bits_per_sample,
52                 (frame->header.channels * frame->header.blocksize
53                  * frame->header.bits_per_sample) / 8,
54                 ENDIAN_BIG);
55   for(n = 0; n < frame->header.blocksize; ++n) {
56     for(c = 0; c < frame->header.channels; ++c) {
57       switch(frame->header.bits_per_sample) {
58       case 8: output_8(buffer[c][n]); break;
59       case 16: output_16(buffer[c][n]); break;
60       case 24: output_24(buffer[c][n]); break;
61       case 32: output_32(buffer[c][n]); break;
62       }
63     }
64   }
65   return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
66 }
67
68 static FLAC__StreamDecoderReadStatus flac_read(const FLAC__StreamDecoder attribute((unused)) *decoder,
69                                                FLAC__byte buffer[],
70                                                size_t *bytes,
71                                                void *client_data) {
72   struct hreader *flacinput = client_data;
73   int n = hreader_read(flacinput, buffer, *bytes);
74   if(n == 0) {
75     *bytes = 0;
76     return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
77   }
78   if(n < 0) {
79     *bytes = 0;
80     return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
81   }
82   *bytes = n;
83   return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
84 }
85
86 static FLAC__StreamDecoderSeekStatus flac_seek(const FLAC__StreamDecoder attribute((unused)) *decoder,
87                                                FLAC__uint64 absolute_byte_offset, 
88                                                void *client_data) {
89   struct hreader *flacinput = client_data;
90   if(hreader_seek(flacinput, absolute_byte_offset, SEEK_SET) < 0)
91     return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
92   else
93     return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
94 }
95
96 static FLAC__StreamDecoderTellStatus flac_tell(const FLAC__StreamDecoder attribute((unused)) *decoder, 
97                                                FLAC__uint64 *absolute_byte_offset,
98                                                void *client_data) {
99   struct hreader *flacinput = client_data;
100   off_t offset = hreader_seek(flacinput, 0, SEEK_CUR);
101   if(offset < 0)
102     return FLAC__STREAM_DECODER_TELL_STATUS_ERROR;
103   *absolute_byte_offset = offset;
104   return FLAC__STREAM_DECODER_TELL_STATUS_OK;
105 }
106
107 static FLAC__StreamDecoderLengthStatus flac_length(const FLAC__StreamDecoder attribute((unused)) *decoder, 
108                                                    FLAC__uint64 *stream_length, 
109                                                    void *client_data) {
110   struct hreader *flacinput = client_data;
111   *stream_length = hreader_size(flacinput);
112   return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
113 }
114
115 static FLAC__bool flac_eof(const FLAC__StreamDecoder attribute((unused)) *decoder, 
116                            void *client_data) {
117   struct hreader *flacinput = client_data;
118   return hreader_eof(flacinput);
119 }
120
121 /** @brief FLAC file decoder */
122 void decode_flac(void) {
123   FLAC__StreamDecoder *sd = FLAC__stream_decoder_new();
124   FLAC__StreamDecoderInitStatus is;
125   struct hreader flacinput[1];
126
127   if (!sd)
128     disorder_fatal(0, "FLAC__stream_decoder_new failed");
129   if(hreader_init(path, flacinput))
130     disorder_fatal(errno, "error opening %s", path);
131
132   if((is = FLAC__stream_decoder_init_stream(sd,
133                                             flac_read,
134                                             flac_seek,
135                                             flac_tell,
136                                             flac_length,
137                                             flac_eof,
138                                             flac_write, flac_metadata,
139                                             flac_error, 
140                                             flacinput)))
141     disorder_fatal(0, "FLAC__stream_decoder_init_stream %s: %s",
142                    path, FLAC__StreamDecoderInitStatusString[is]);
143
144   FLAC__stream_decoder_process_until_end_of_stream(sd);
145   FLAC__stream_decoder_finish(sd);
146   FLAC__stream_decoder_delete(sd);
147 }
148
149 /*
150 Local Variables:
151 c-basic-offset:2
152 comment-column:40
153 fill-column:79
154 indent-tabs-mode:nil
155 End:
156 */