Mercurial > SDL_sound_CoreAudio
annotate decoders/flac.c @ 474:c66080364dff
Most decoders now report total sample play time, now. Technically, this
breaks binary compatibility with the 1.0 branch, since it extends the
Sound_Sample struct, but most (all?) programs are just passing pointers
allocated by SDL_sound around, and might be okay.
Source-level compatibility is not broken...yet! :)
--ryan.
-------- Original Message --------
Subject: SDL_sound patch: Finding total length of time of sound file.
Date: Sun, 26 Jan 2003 09:31:17 -0800 (PST)
Hi Ryan,
I am working with Eric Wing and helping him modify
SDL_sound. AS part of our efforts in improving and
enhancing SDL_sound, we like to submit this patch. We
modified the codecs to find the total time of a sound
file. Below is the explanation of the patch. The
patch is appended as an attachment to this email.
* MOTIVATION:
We needed the ability to get the total play time of a
sample (And we noticed that we're not the only ones).
Since SDL_sound blocks direct access to the specific
decoders, there is no way for a user to know this
information short of decoding the whole thing.
Because of this, we believe this will be a useful
addition, even though the accuracy may not be perfect
(subject to each decoder) or the information may not
always be available.
* CONTRIBUTORS:
Wesley Leong (modified the majority of the codecs and
verified the results)
Eric Wing (showed everyone how to do modify codec,
modified mikmod)
Wang Lam (modified a handful of codecs, researched
into specs and int overflow)
Ahilan Anantha (modified a few codecs and helped with
integer math)
* GENERAL ISSUES:
We chose the value to be milliseconds as an Sint32.
Milliseconds because that's what Sound_Seek takes as a
parameter and -1 to allow for instances/codecs where
the value could not be determined. We are
not sure if this is the final convention you want, so
we are willing to work with you on this.
We also expect the total_time field to be set on open
and never again modified by SDL_sound. Users may
access it directly much like the sample buffer and
buffer_size. We thought about recomputing the time
on DecodeAll, but since users may seek or decode small
chunks first, not all the data may be there. So this
is better done by the user. This may be good
information to document.
Currently, all the main codecs are implemented except
for QuickTime.
author | Ryan C. Gordon <icculus@icculus.org> |
---|---|
date | Sat, 08 May 2004 08:19:50 +0000 |
parents | 30f96c853462 |
children | 3e705c9180e5 |
rev | line source |
---|---|
155 | 1 /* |
2 * SDL_sound -- An abstract sound format decoding API. | |
3 * Copyright (C) 2001 Ryan C. Gordon. | |
4 * | |
5 * This library is free software; you can redistribute it and/or | |
6 * modify it under the terms of the GNU Lesser General Public | |
7 * License as published by the Free Software Foundation; either | |
8 * version 2.1 of the License, or (at your option) any later version. | |
9 * | |
10 * This library 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 GNU | |
13 * Lesser General Public License for more details. | |
14 * | |
15 * You should have received a copy of the GNU Lesser General Public | |
16 * License along with this library; if not, write to the Free Software | |
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 */ | |
19 | |
20 /* | |
21 * FLAC decoder for SDL_sound. | |
22 * | |
23 * This driver handles FLAC audio, that is to say the Free Lossless Audio | |
24 * Codec. It depends on libFLAC for decoding, which can be grabbed from: | |
25 * http://flac.sourceforge.net | |
26 * | |
184
47cc2de2ae36
Changed reference to "LICENSE" file to "COPYING".
Ryan C. Gordon <icculus@icculus.org>
parents:
182
diff
changeset
|
27 * Please see the file COPYING in the source's root directory. |
155 | 28 * |
29 * This file written by Torbjörn Andersson. (d91tan@Update.UU.SE) | |
30 */ | |
31 | |
32 #if HAVE_CONFIG_H | |
33 # include <config.h> | |
34 #endif | |
35 | |
36 #ifdef SOUND_SUPPORTS_FLAC | |
37 | |
38 #include <stdio.h> | |
39 #include <stdlib.h> | |
40 #include <string.h> | |
41 | |
42 #include "SDL_sound.h" | |
43 | |
44 #define __SDL_SOUND_INTERNAL__ | |
45 #include "SDL_sound_internal.h" | |
46 | |
443 | 47 #include <FLAC/seekable_stream_decoder.h> |
312 | 48 |
49 #define D_END_OF_STREAM FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM | |
50 | |
51 #define d_new() FLAC__seekable_stream_decoder_new() | |
52 #define d_init(x) FLAC__seekable_stream_decoder_init(x) | |
422
5b06e23d934e
Removed backwards compatible API nastiness from FLAC decoder.
Ryan C. Gordon <icculus@icculus.org>
parents:
400
diff
changeset
|
53 #define d_process_metadata(x) FLAC__seekable_stream_decoder_process_until_end_of_metadata(x) |
5b06e23d934e
Removed backwards compatible API nastiness from FLAC decoder.
Ryan C. Gordon <icculus@icculus.org>
parents:
400
diff
changeset
|
54 #define d_process_one_frame(x) FLAC__seekable_stream_decoder_process_single(x) |
312 | 55 #define d_get_state(x) FLAC__seekable_stream_decoder_get_state(x) |
56 #define d_finish(x) FLAC__seekable_stream_decoder_finish(x) | |
57 #define d_delete(x) FLAC__seekable_stream_decoder_delete(x) | |
58 #define d_set_read_callback(x, y) FLAC__seekable_stream_decoder_set_read_callback(x, y) | |
59 #define d_set_write_callback(x, y) FLAC__seekable_stream_decoder_set_write_callback(x, y) | |
60 #define d_set_metadata_callback(x, y) FLAC__seekable_stream_decoder_set_metadata_callback(x, y) | |
61 #define d_set_error_callback(x, y) FLAC__seekable_stream_decoder_set_error_callback(x, y) | |
62 #define d_set_client_data(x, y) FLAC__seekable_stream_decoder_set_client_data(x, y) | |
63 | |
64 typedef FLAC__SeekableStreamDecoder decoder_t; | |
65 typedef FLAC__SeekableStreamDecoderReadStatus d_read_status_t; | |
66 | |
67 #define D_SEEK_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK | |
68 #define D_SEEK_STATUS_ERROR FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR | |
69 #define D_TELL_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK | |
70 #define D_TELL_STATUS_ERROR FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR | |
71 #define D_LENGTH_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK | |
72 #define D_LENGTH_STATUS_ERROR FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_ERROR | |
73 | |
74 #define d_set_seek_callback(x, y) FLAC__seekable_stream_decoder_set_seek_callback(x, y) | |
75 #define d_set_tell_callback(x, y) FLAC__seekable_stream_decoder_set_tell_callback(x, y) | |
76 #define d_set_length_callback(x, y) FLAC__seekable_stream_decoder_set_length_callback(x, y) | |
77 #define d_set_eof_callback(x, y) FLAC__seekable_stream_decoder_set_eof_callback(x, y) | |
78 #define d_seek_absolute(x, y) FLAC__seekable_stream_decoder_seek_absolute(x, y) | |
79 | |
80 typedef FLAC__SeekableStreamDecoderSeekStatus d_seek_status_t; | |
81 typedef FLAC__SeekableStreamDecoderTellStatus d_tell_status_t; | |
82 typedef FLAC__SeekableStreamDecoderLengthStatus d_length_status_t; | |
83 | |
393
b12c4483815e
Handles all versions of libFLAC up to version 1.0.3, now.
Ryan C. Gordon <icculus@icculus.org>
parents:
387
diff
changeset
|
84 #define D_WRITE_CONTINUE FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE |
b12c4483815e
Handles all versions of libFLAC up to version 1.0.3, now.
Ryan C. Gordon <icculus@icculus.org>
parents:
387
diff
changeset
|
85 #define D_READ_END_OF_STREAM FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM |
b12c4483815e
Handles all versions of libFLAC up to version 1.0.3, now.
Ryan C. Gordon <icculus@icculus.org>
parents:
387
diff
changeset
|
86 #define D_READ_ABORT FLAC__STREAM_DECODER_READ_STATUS_ABORT |
b12c4483815e
Handles all versions of libFLAC up to version 1.0.3, now.
Ryan C. Gordon <icculus@icculus.org>
parents:
387
diff
changeset
|
87 #define D_READ_CONTINUE FLAC__STREAM_DECODER_READ_STATUS_CONTINUE |
312 | 88 |
89 #define d_error_status_string FLAC__StreamDecoderErrorStatusString | |
90 | |
393
b12c4483815e
Handles all versions of libFLAC up to version 1.0.3, now.
Ryan C. Gordon <icculus@icculus.org>
parents:
387
diff
changeset
|
91 typedef FLAC__StreamDecoderErrorStatus d_error_status_t; |
b12c4483815e
Handles all versions of libFLAC up to version 1.0.3, now.
Ryan C. Gordon <icculus@icculus.org>
parents:
387
diff
changeset
|
92 typedef FLAC__StreamMetadata d_metadata_t; |
312 | 93 typedef FLAC__StreamDecoderWriteStatus d_write_status_t; |
94 | |
155 | 95 |
96 static int FLAC_init(void); | |
97 static void FLAC_quit(void); | |
98 static int FLAC_open(Sound_Sample *sample, const char *ext); | |
99 static void FLAC_close(Sound_Sample *sample); | |
100 static Uint32 FLAC_read(Sound_Sample *sample); | |
221
c9772a9f5271
Initial implementation or stubs for rewind method. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
184
diff
changeset
|
101 static int FLAC_rewind(Sound_Sample *sample); |
306
c97be6e1bd27
Added framework for Sound_Seek() support.
Ryan C. Gordon <icculus@icculus.org>
parents:
231
diff
changeset
|
102 static int FLAC_seek(Sound_Sample *sample, Uint32 ms); |
155 | 103 |
104 static const char *extensions_flac[] = { "FLAC", "FLA", NULL }; | |
105 | |
106 const Sound_DecoderFunctions __Sound_DecoderFunctions_FLAC = | |
107 { | |
108 { | |
109 extensions_flac, | |
110 "Free Lossless Audio Codec", | |
111 "Torbjörn Andersson <d91tan@Update.UU.SE>", | |
112 "http://flac.sourceforge.net/" | |
113 }, | |
114 | |
221
c9772a9f5271
Initial implementation or stubs for rewind method. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
184
diff
changeset
|
115 FLAC_init, /* init() method */ |
c9772a9f5271
Initial implementation or stubs for rewind method. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
184
diff
changeset
|
116 FLAC_quit, /* quit() method */ |
c9772a9f5271
Initial implementation or stubs for rewind method. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
184
diff
changeset
|
117 FLAC_open, /* open() method */ |
c9772a9f5271
Initial implementation or stubs for rewind method. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
184
diff
changeset
|
118 FLAC_close, /* close() method */ |
c9772a9f5271
Initial implementation or stubs for rewind method. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
184
diff
changeset
|
119 FLAC_read, /* read() method */ |
306
c97be6e1bd27
Added framework for Sound_Seek() support.
Ryan C. Gordon <icculus@icculus.org>
parents:
231
diff
changeset
|
120 FLAC_rewind, /* rewind() method */ |
c97be6e1bd27
Added framework for Sound_Seek() support.
Ryan C. Gordon <icculus@icculus.org>
parents:
231
diff
changeset
|
121 FLAC_seek /* seek() method */ |
155 | 122 }; |
123 | |
124 /* This is what we store in our internal->decoder_private field. */ | |
125 typedef struct | |
126 { | |
312 | 127 decoder_t *decoder; |
155 | 128 SDL_RWops *rw; |
129 Sound_Sample *sample; | |
130 Uint32 frame_size; | |
166
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
131 Uint8 is_flac; |
312 | 132 Uint32 stream_length; |
155 | 133 } flac_t; |
134 | |
135 | |
166
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
136 static void free_flac(flac_t *f) |
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
137 { |
312 | 138 d_finish(f->decoder); |
139 d_delete(f->decoder); | |
166
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
140 free(f); |
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
141 } /* free_flac */ |
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
142 |
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
143 |
312 | 144 static d_read_status_t read_callback( |
145 const decoder_t *decoder, FLAC__byte buffer[], | |
155 | 146 unsigned int *bytes, void *client_data) |
147 { | |
148 flac_t *f = (flac_t *) client_data; | |
149 Uint32 retval; | |
150 | |
151 retval = SDL_RWread(f->rw, (Uint8 *) buffer, 1, *bytes); | |
152 | |
153 if (retval == 0) | |
154 { | |
155 *bytes = 0; | |
156 f->sample->flags |= SOUND_SAMPLEFLAG_EOF; | |
312 | 157 return(D_READ_END_OF_STREAM); |
155 | 158 } /* if */ |
159 | |
160 if (retval == -1) | |
161 { | |
162 *bytes = 0; | |
163 f->sample->flags |= SOUND_SAMPLEFLAG_ERROR; | |
312 | 164 return(D_READ_ABORT); |
155 | 165 } /* if */ |
166 | |
167 if (retval < *bytes) | |
168 { | |
169 *bytes = retval; | |
170 f->sample->flags |= SOUND_SAMPLEFLAG_EAGAIN; | |
171 } /* if */ | |
172 | |
312 | 173 return(D_READ_CONTINUE); |
174 } /* read_callback */ | |
155 | 175 |
176 | |
312 | 177 static d_write_status_t write_callback( |
178 const decoder_t *decoder, const FLAC__Frame *frame, | |
393
b12c4483815e
Handles all versions of libFLAC up to version 1.0.3, now.
Ryan C. Gordon <icculus@icculus.org>
parents:
387
diff
changeset
|
179 const FLAC__int32 * const buffer[], |
b12c4483815e
Handles all versions of libFLAC up to version 1.0.3, now.
Ryan C. Gordon <icculus@icculus.org>
parents:
387
diff
changeset
|
180 void *client_data) |
155 | 181 { |
182 flac_t *f = (flac_t *) client_data; | |
183 Uint32 i, j; | |
166
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
184 Uint32 sample; |
155 | 185 Uint8 *dst; |
186 | |
187 f->frame_size = frame->header.channels * frame->header.blocksize | |
188 * frame->header.bits_per_sample / 8; | |
189 | |
190 if (f->frame_size > f->sample->buffer_size) | |
191 Sound_SetBufferSize(f->sample, f->frame_size); | |
192 | |
193 dst = f->sample->buffer; | |
194 | |
166
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
195 /* If the sample is neither exactly 8-bit nor 16-bit, it will have to |
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
196 * be converted. Unfortunately the buffer is read-only, so we either |
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
197 * have to check for each sample, or make a copy of the buffer. I'm |
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
198 * not sure which way is best, so I've arbitrarily picked the former. |
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
199 */ |
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
200 if (f->sample->actual.format == AUDIO_S8) |
155 | 201 { |
202 for (i = 0; i < frame->header.blocksize; i++) | |
203 for (j = 0; j < frame->header.channels; j++) | |
166
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
204 { |
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
205 sample = buffer[j][i]; |
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
206 if (frame->header.bits_per_sample < 8) |
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
207 sample <<= (8 - frame->header.bits_per_sample); |
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
208 *dst++ = sample & 0x00ff; |
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
209 } /* for */ |
155 | 210 } /* if */ |
211 else | |
212 { | |
213 for (i = 0; i < frame->header.blocksize; i++) | |
214 for (j = 0; j < frame->header.channels; j++) | |
215 { | |
166
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
216 sample = buffer[j][i]; |
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
217 if (frame->header.bits_per_sample < 16) |
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
218 sample <<= (16 - frame->header.bits_per_sample); |
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
219 else if (frame->header.bits_per_sample > 16) |
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
220 sample >>= (frame->header.bits_per_sample - 16); |
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
221 *dst++ = (sample & 0xff00) >> 8; |
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
222 *dst++ = sample & 0x00ff; |
155 | 223 } /* for */ |
224 } /* else */ | |
225 | |
312 | 226 return(D_WRITE_CONTINUE); |
227 } /* write_callback */ | |
155 | 228 |
229 | |
312 | 230 static void metadata_callback( |
231 const decoder_t *decoder, | |
232 const d_metadata_t *metadata, | |
155 | 233 void *client_data) |
234 { | |
235 flac_t *f = (flac_t *) client_data; | |
312 | 236 |
155 | 237 SNDDBG(("FLAC: Metadata callback.\n")); |
238 | |
166
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
239 /* There are several kinds of metadata, but STREAMINFO is the only |
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
240 * one that always has to be there. |
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
241 */ |
155 | 242 if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) |
243 { | |
244 SNDDBG(("FLAC: Metadata is streaminfo.\n")); | |
166
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
245 |
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
246 f->is_flac = 1; |
155 | 247 f->sample->actual.channels = metadata->data.stream_info.channels; |
248 f->sample->actual.rate = metadata->data.stream_info.sample_rate; | |
249 | |
474
c66080364dff
Most decoders now report total sample play time, now. Technically, this
Ryan C. Gordon <icculus@icculus.org>
parents:
443
diff
changeset
|
250 if (metadata->data.stream_info.sample_rate == 0 || |
c66080364dff
Most decoders now report total sample play time, now. Technically, this
Ryan C. Gordon <icculus@icculus.org>
parents:
443
diff
changeset
|
251 metadata->data.stream_info.total_samples == 0) |
c66080364dff
Most decoders now report total sample play time, now. Technically, this
Ryan C. Gordon <icculus@icculus.org>
parents:
443
diff
changeset
|
252 f->sample->total_time = -1; |
c66080364dff
Most decoders now report total sample play time, now. Technically, this
Ryan C. Gordon <icculus@icculus.org>
parents:
443
diff
changeset
|
253 else { |
c66080364dff
Most decoders now report total sample play time, now. Technically, this
Ryan C. Gordon <icculus@icculus.org>
parents:
443
diff
changeset
|
254 f->sample->total_time = (metadata->data.stream_info.total_samples) |
c66080364dff
Most decoders now report total sample play time, now. Technically, this
Ryan C. Gordon <icculus@icculus.org>
parents:
443
diff
changeset
|
255 / metadata->data.stream_info.sample_rate * 1000; |
c66080364dff
Most decoders now report total sample play time, now. Technically, this
Ryan C. Gordon <icculus@icculus.org>
parents:
443
diff
changeset
|
256 f->sample->total_time += (metadata->data.stream_info.total_samples |
c66080364dff
Most decoders now report total sample play time, now. Technically, this
Ryan C. Gordon <icculus@icculus.org>
parents:
443
diff
changeset
|
257 % metadata->data.stream_info.sample_rate) * 1000 |
c66080364dff
Most decoders now report total sample play time, now. Technically, this
Ryan C. Gordon <icculus@icculus.org>
parents:
443
diff
changeset
|
258 / metadata->data.stream_info.sample_rate; |
c66080364dff
Most decoders now report total sample play time, now. Technically, this
Ryan C. Gordon <icculus@icculus.org>
parents:
443
diff
changeset
|
259 } |
c66080364dff
Most decoders now report total sample play time, now. Technically, this
Ryan C. Gordon <icculus@icculus.org>
parents:
443
diff
changeset
|
260 |
166
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
261 if (metadata->data.stream_info.bits_per_sample > 8) |
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
262 f->sample->actual.format = AUDIO_S16MSB; |
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
263 else |
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
264 f->sample->actual.format = AUDIO_S8; |
155 | 265 } /* if */ |
312 | 266 } /* metadata_callback */ |
155 | 267 |
268 | |
312 | 269 static void error_callback( |
270 const decoder_t *decoder, | |
271 d_error_status_t status, | |
155 | 272 void *client_data) |
273 { | |
274 flac_t *f = (flac_t *) client_data; | |
164
77482005beb6
Now includes FLAC/stream_decoder.h instead of FLAC/all.h. More robust
Ryan C. Gordon <icculus@icculus.org>
parents:
155
diff
changeset
|
275 |
387
fb519e6028e3
Changed all the Sound_SetError() calls to __Sound_SetError (or BAIL*_MACRO)
Ryan C. Gordon <icculus@icculus.org>
parents:
377
diff
changeset
|
276 __Sound_SetError(d_error_status_string[status]); |
155 | 277 f->sample->flags |= SOUND_SAMPLEFLAG_ERROR; |
312 | 278 } /* error_callback */ |
279 | |
280 | |
281 static d_seek_status_t seek_callback( | |
282 const decoder_t *decoder, | |
283 FLAC__uint64 absolute_byte_offset, | |
284 void *client_data) | |
285 { | |
286 flac_t *f = (flac_t *) client_data; | |
287 | |
288 if (SDL_RWseek(f->rw, absolute_byte_offset, SEEK_SET) >= 0) | |
289 { | |
290 return(D_SEEK_STATUS_OK); | |
291 } /* if */ | |
292 | |
293 return(D_SEEK_STATUS_ERROR); | |
294 } /* seek_callback*/ | |
295 | |
296 | |
297 static d_tell_status_t tell_callback( | |
298 const decoder_t *decoder, | |
299 FLAC__uint64 *absolute_byte_offset, | |
300 void *client_data) | |
301 { | |
302 flac_t *f = (flac_t *) client_data; | |
400 | 303 int pos; |
312 | 304 |
305 pos = SDL_RWtell(f->rw); | |
306 | |
307 if (pos < 0) | |
308 { | |
309 return(D_TELL_STATUS_ERROR); | |
310 } /* if */ | |
155 | 311 |
312 | 312 *absolute_byte_offset = pos; |
313 return(D_TELL_STATUS_OK); | |
314 } /* tell_callback */ | |
315 | |
316 | |
317 static d_length_status_t length_callback( | |
318 const decoder_t *decoder, | |
319 FLAC__uint64 *stream_length, | |
320 void *client_data) | |
321 { | |
322 flac_t *f = (flac_t *) client_data; | |
323 | |
324 if (f->sample->flags & SOUND_SAMPLEFLAG_CANSEEK) | |
325 { | |
326 *stream_length = f->stream_length; | |
327 return(D_LENGTH_STATUS_OK); | |
328 } /* if */ | |
329 | |
330 return(D_LENGTH_STATUS_ERROR); | |
331 } /* length_callback */ | |
332 | |
333 | |
334 static FLAC__bool eof_callback( | |
335 const decoder_t *decoder, | |
336 void *client_data) | |
337 { | |
338 flac_t *f = (flac_t *) client_data; | |
400 | 339 int pos; |
312 | 340 |
341 /* Maybe we could check for SOUND_SAMPLEFLAG_EOF here instead? */ | |
342 pos = SDL_RWtell(f->rw); | |
343 | |
344 if (pos >= 0 && pos >= f->stream_length) | |
345 { | |
346 return(true); | |
347 } /* if */ | |
348 | |
349 return(false); | |
350 } /* eof_callback */ | |
351 | |
155 | 352 |
353 static int FLAC_init(void) | |
354 { | |
355 return(1); /* always succeeds. */ | |
356 } /* FLAC_init */ | |
357 | |
358 | |
359 static void FLAC_quit(void) | |
360 { | |
361 /* it's a no-op. */ | |
362 } /* FLAC_quit */ | |
363 | |
364 | |
182
3849438b735e
Checks magic number again, unless the file extension is recognized.
Ryan C. Gordon <icculus@icculus.org>
parents:
166
diff
changeset
|
365 #define FLAC_MAGIC 0x43614C66 /* "fLaC" in ASCII. */ |
3849438b735e
Checks magic number again, unless the file extension is recognized.
Ryan C. Gordon <icculus@icculus.org>
parents:
166
diff
changeset
|
366 |
155 | 367 static int FLAC_open(Sound_Sample *sample, const char *ext) |
368 { | |
369 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
370 SDL_RWops *rw = internal->rw; | |
312 | 371 decoder_t *decoder; |
155 | 372 flac_t *f; |
166
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
373 int i; |
182
3849438b735e
Checks magic number again, unless the file extension is recognized.
Ryan C. Gordon <icculus@icculus.org>
parents:
166
diff
changeset
|
374 int has_extension = 0; |
312 | 375 Uint32 pos; |
182
3849438b735e
Checks magic number again, unless the file extension is recognized.
Ryan C. Gordon <icculus@icculus.org>
parents:
166
diff
changeset
|
376 |
3849438b735e
Checks magic number again, unless the file extension is recognized.
Ryan C. Gordon <icculus@icculus.org>
parents:
166
diff
changeset
|
377 /* |
3849438b735e
Checks magic number again, unless the file extension is recognized.
Ryan C. Gordon <icculus@icculus.org>
parents:
166
diff
changeset
|
378 * If the extension is "flac", we'll believe that this is really meant |
3849438b735e
Checks magic number again, unless the file extension is recognized.
Ryan C. Gordon <icculus@icculus.org>
parents:
166
diff
changeset
|
379 * to be a FLAC stream, and will try to grok it from existing metadata. |
3849438b735e
Checks magic number again, unless the file extension is recognized.
Ryan C. Gordon <icculus@icculus.org>
parents:
166
diff
changeset
|
380 * metadata searching can be a very expensive operation, however, so |
3849438b735e
Checks magic number again, unless the file extension is recognized.
Ryan C. Gordon <icculus@icculus.org>
parents:
166
diff
changeset
|
381 * unless the user swears that it is a FLAC stream through the extension, |
3849438b735e
Checks magic number again, unless the file extension is recognized.
Ryan C. Gordon <icculus@icculus.org>
parents:
166
diff
changeset
|
382 * we decide what to do based on the existance of a 32-bit magic number. |
3849438b735e
Checks magic number again, unless the file extension is recognized.
Ryan C. Gordon <icculus@icculus.org>
parents:
166
diff
changeset
|
383 */ |
3849438b735e
Checks magic number again, unless the file extension is recognized.
Ryan C. Gordon <icculus@icculus.org>
parents:
166
diff
changeset
|
384 for (i = 0; extensions_flac[i] != NULL; i++) |
3849438b735e
Checks magic number again, unless the file extension is recognized.
Ryan C. Gordon <icculus@icculus.org>
parents:
166
diff
changeset
|
385 { |
3849438b735e
Checks magic number again, unless the file extension is recognized.
Ryan C. Gordon <icculus@icculus.org>
parents:
166
diff
changeset
|
386 if (__Sound_strcasecmp(ext, extensions_flac[i]) == 0) |
3849438b735e
Checks magic number again, unless the file extension is recognized.
Ryan C. Gordon <icculus@icculus.org>
parents:
166
diff
changeset
|
387 { |
3849438b735e
Checks magic number again, unless the file extension is recognized.
Ryan C. Gordon <icculus@icculus.org>
parents:
166
diff
changeset
|
388 has_extension = 1; |
3849438b735e
Checks magic number again, unless the file extension is recognized.
Ryan C. Gordon <icculus@icculus.org>
parents:
166
diff
changeset
|
389 break; |
3849438b735e
Checks magic number again, unless the file extension is recognized.
Ryan C. Gordon <icculus@icculus.org>
parents:
166
diff
changeset
|
390 } /* if */ |
3849438b735e
Checks magic number again, unless the file extension is recognized.
Ryan C. Gordon <icculus@icculus.org>
parents:
166
diff
changeset
|
391 } /* for */ |
3849438b735e
Checks magic number again, unless the file extension is recognized.
Ryan C. Gordon <icculus@icculus.org>
parents:
166
diff
changeset
|
392 |
3849438b735e
Checks magic number again, unless the file extension is recognized.
Ryan C. Gordon <icculus@icculus.org>
parents:
166
diff
changeset
|
393 if (!has_extension) |
3849438b735e
Checks magic number again, unless the file extension is recognized.
Ryan C. Gordon <icculus@icculus.org>
parents:
166
diff
changeset
|
394 { |
3849438b735e
Checks magic number again, unless the file extension is recognized.
Ryan C. Gordon <icculus@icculus.org>
parents:
166
diff
changeset
|
395 int rc; |
3849438b735e
Checks magic number again, unless the file extension is recognized.
Ryan C. Gordon <icculus@icculus.org>
parents:
166
diff
changeset
|
396 Uint32 flac_magic = SDL_ReadLE32(rw); |
3849438b735e
Checks magic number again, unless the file extension is recognized.
Ryan C. Gordon <icculus@icculus.org>
parents:
166
diff
changeset
|
397 BAIL_IF_MACRO(flac_magic != FLAC_MAGIC, "FLAC: Not a FLAC stream.", 0); |
3849438b735e
Checks magic number again, unless the file extension is recognized.
Ryan C. Gordon <icculus@icculus.org>
parents:
166
diff
changeset
|
398 |
3849438b735e
Checks magic number again, unless the file extension is recognized.
Ryan C. Gordon <icculus@icculus.org>
parents:
166
diff
changeset
|
399 /* move back over magic number for metadata scan... */ |
3849438b735e
Checks magic number again, unless the file extension is recognized.
Ryan C. Gordon <icculus@icculus.org>
parents:
166
diff
changeset
|
400 rc = SDL_RWseek(internal->rw, -sizeof (flac_magic), SEEK_CUR); |
3849438b735e
Checks magic number again, unless the file extension is recognized.
Ryan C. Gordon <icculus@icculus.org>
parents:
166
diff
changeset
|
401 BAIL_IF_MACRO(rc < 0, ERR_IO_ERROR, 0); |
3849438b735e
Checks magic number again, unless the file extension is recognized.
Ryan C. Gordon <icculus@icculus.org>
parents:
166
diff
changeset
|
402 } /* if */ |
155 | 403 |
404 f = (flac_t *) malloc(sizeof (flac_t)); | |
405 BAIL_IF_MACRO(f == NULL, ERR_OUT_OF_MEMORY, 0); | |
406 | |
312 | 407 decoder = d_new(); |
155 | 408 if (decoder == NULL) |
409 { | |
410 free(f); | |
387
fb519e6028e3
Changed all the Sound_SetError() calls to __Sound_SetError (or BAIL*_MACRO)
Ryan C. Gordon <icculus@icculus.org>
parents:
377
diff
changeset
|
411 BAIL_MACRO(ERR_OUT_OF_MEMORY, 0); |
155 | 412 } /* if */ |
413 | |
312 | 414 d_set_read_callback(decoder, read_callback); |
415 d_set_write_callback(decoder, write_callback); | |
416 d_set_metadata_callback(decoder, metadata_callback); | |
417 d_set_error_callback(decoder, error_callback); | |
418 d_set_seek_callback(decoder, seek_callback); | |
419 d_set_tell_callback(decoder, tell_callback); | |
420 d_set_length_callback(decoder, length_callback); | |
421 d_set_eof_callback(decoder, eof_callback); | |
422
5b06e23d934e
Removed backwards compatible API nastiness from FLAC decoder.
Ryan C. Gordon <icculus@icculus.org>
parents:
400
diff
changeset
|
422 |
312 | 423 d_set_client_data(decoder, f); |
155 | 424 |
425 f->rw = internal->rw; | |
426 f->sample = sample; | |
427 f->decoder = decoder; | |
164
77482005beb6
Now includes FLAC/stream_decoder.h instead of FLAC/all.h. More robust
Ryan C. Gordon <icculus@icculus.org>
parents:
155
diff
changeset
|
428 f->sample->actual.format = 0; |
182
3849438b735e
Checks magic number again, unless the file extension is recognized.
Ryan C. Gordon <icculus@icculus.org>
parents:
166
diff
changeset
|
429 f->is_flac = 0 /* !!! FIXME: should be "has_extension", not "0". */; |
166
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
430 |
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
431 internal->decoder_private = f; |
312 | 432 d_init(decoder); |
166
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
433 |
400 | 434 sample->flags = SOUND_SAMPLEFLAG_NONE; |
435 | |
436 pos = SDL_RWtell(f->rw); | |
437 if (SDL_RWseek(f->rw, 0, SEEK_END) > 0) | |
438 { | |
439 f->stream_length = SDL_RWtell(f->rw); | |
440 if (SDL_RWseek(f->rw, pos, SEEK_SET) == -1) | |
441 { | |
442 free_flac(f); | |
443 BAIL_MACRO(ERR_IO_ERROR, 0); | |
444 } /* if */ | |
445 sample->flags = SOUND_SAMPLEFLAG_CANSEEK; | |
446 } /* if */ | |
447 | |
231
d3dc34315ac7
Rewind method implemented by Torbj�rn.
Ryan C. Gordon <icculus@icculus.org>
parents:
221
diff
changeset
|
448 /* |
182
3849438b735e
Checks magic number again, unless the file extension is recognized.
Ryan C. Gordon <icculus@icculus.org>
parents:
166
diff
changeset
|
449 * If we are not sure this is a FLAC stream, check for the STREAMINFO |
166
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
450 * metadata block. If not, we'd have to peek at the first audio frame |
182
3849438b735e
Checks magic number again, unless the file extension is recognized.
Ryan C. Gordon <icculus@icculus.org>
parents:
166
diff
changeset
|
451 * and get the sound format from there, but that is not yet |
3849438b735e
Checks magic number again, unless the file extension is recognized.
Ryan C. Gordon <icculus@icculus.org>
parents:
166
diff
changeset
|
452 * implemented. |
166
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
453 */ |
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
454 if (!f->is_flac) |
155 | 455 { |
312 | 456 d_process_metadata(decoder); |
166
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
457 |
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
458 /* Still not FLAC? Give up. */ |
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
459 if (!f->is_flac) |
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
460 { |
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
461 free_flac(f); |
387
fb519e6028e3
Changed all the Sound_SetError() calls to __Sound_SetError (or BAIL*_MACRO)
Ryan C. Gordon <icculus@icculus.org>
parents:
377
diff
changeset
|
462 BAIL_MACRO("FLAC: No metadata found. Not a FLAC stream?", 0); |
166
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
463 } /* if */ |
155 | 464 } /* if */ |
465 | |
164
77482005beb6
Now includes FLAC/stream_decoder.h instead of FLAC/all.h. More robust
Ryan C. Gordon <icculus@icculus.org>
parents:
155
diff
changeset
|
466 SNDDBG(("FLAC: Accepting data stream.\n")); |
155 | 467 return(1); |
468 } /* FLAC_open */ | |
469 | |
470 | |
471 static void FLAC_close(Sound_Sample *sample) | |
472 { | |
473 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
474 flac_t *f = (flac_t *) internal->decoder_private; | |
475 | |
166
d8904267d23c
Cleanups, fixes and enhancements by Torbj�rn Andersson.
Ryan C. Gordon <icculus@icculus.org>
parents:
164
diff
changeset
|
476 free_flac(f); |
155 | 477 } /* FLAC_close */ |
478 | |
479 | |
480 static Uint32 FLAC_read(Sound_Sample *sample) | |
481 { | |
482 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
483 flac_t *f = (flac_t *) internal->decoder_private; | |
484 Uint32 len; | |
485 | |
312 | 486 if (!d_process_one_frame(f->decoder)) |
155 | 487 { |
488 sample->flags |= SOUND_SAMPLEFLAG_ERROR; | |
387
fb519e6028e3
Changed all the Sound_SetError() calls to __Sound_SetError (or BAIL*_MACRO)
Ryan C. Gordon <icculus@icculus.org>
parents:
377
diff
changeset
|
489 BAIL_MACRO("FLAC: Couldn't decode frame.", 0); |
155 | 490 } /* if */ |
491 | |
312 | 492 if (d_get_state(f->decoder) == D_END_OF_STREAM) |
231
d3dc34315ac7
Rewind method implemented by Torbj�rn.
Ryan C. Gordon <icculus@icculus.org>
parents:
221
diff
changeset
|
493 { |
d3dc34315ac7
Rewind method implemented by Torbj�rn.
Ryan C. Gordon <icculus@icculus.org>
parents:
221
diff
changeset
|
494 sample->flags |= SOUND_SAMPLEFLAG_EOF; |
d3dc34315ac7
Rewind method implemented by Torbj�rn.
Ryan C. Gordon <icculus@icculus.org>
parents:
221
diff
changeset
|
495 return(0); |
d3dc34315ac7
Rewind method implemented by Torbj�rn.
Ryan C. Gordon <icculus@icculus.org>
parents:
221
diff
changeset
|
496 } /* if */ |
d3dc34315ac7
Rewind method implemented by Torbj�rn.
Ryan C. Gordon <icculus@icculus.org>
parents:
221
diff
changeset
|
497 |
164
77482005beb6
Now includes FLAC/stream_decoder.h instead of FLAC/all.h. More robust
Ryan C. Gordon <icculus@icculus.org>
parents:
155
diff
changeset
|
498 /* An error may have been signalled through the error callback. */ |
77482005beb6
Now includes FLAC/stream_decoder.h instead of FLAC/all.h. More robust
Ryan C. Gordon <icculus@icculus.org>
parents:
155
diff
changeset
|
499 if (sample->flags & SOUND_SAMPLEFLAG_ERROR) |
77482005beb6
Now includes FLAC/stream_decoder.h instead of FLAC/all.h. More robust
Ryan C. Gordon <icculus@icculus.org>
parents:
155
diff
changeset
|
500 return(0); |
231
d3dc34315ac7
Rewind method implemented by Torbj�rn.
Ryan C. Gordon <icculus@icculus.org>
parents:
221
diff
changeset
|
501 |
155 | 502 return(f->frame_size); |
503 } /* FLAC_read */ | |
504 | |
221
c9772a9f5271
Initial implementation or stubs for rewind method. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
184
diff
changeset
|
505 |
c9772a9f5271
Initial implementation or stubs for rewind method. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
184
diff
changeset
|
506 static int FLAC_rewind(Sound_Sample *sample) |
c9772a9f5271
Initial implementation or stubs for rewind method. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
184
diff
changeset
|
507 { |
312 | 508 return FLAC_seek(sample, 0); |
221
c9772a9f5271
Initial implementation or stubs for rewind method. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
184
diff
changeset
|
509 } /* FLAC_rewind */ |
c9772a9f5271
Initial implementation or stubs for rewind method. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
184
diff
changeset
|
510 |
306
c97be6e1bd27
Added framework for Sound_Seek() support.
Ryan C. Gordon <icculus@icculus.org>
parents:
231
diff
changeset
|
511 |
c97be6e1bd27
Added framework for Sound_Seek() support.
Ryan C. Gordon <icculus@icculus.org>
parents:
231
diff
changeset
|
512 static int FLAC_seek(Sound_Sample *sample, Uint32 ms) |
c97be6e1bd27
Added framework for Sound_Seek() support.
Ryan C. Gordon <icculus@icculus.org>
parents:
231
diff
changeset
|
513 { |
312 | 514 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; |
515 flac_t *f = (flac_t *) internal->decoder_private; | |
516 | |
517 d_seek_absolute(f->decoder, (ms * sample->actual.rate) / 1000); | |
518 return(1); | |
306
c97be6e1bd27
Added framework for Sound_Seek() support.
Ryan C. Gordon <icculus@icculus.org>
parents:
231
diff
changeset
|
519 } /* FLAC_seek */ |
c97be6e1bd27
Added framework for Sound_Seek() support.
Ryan C. Gordon <icculus@icculus.org>
parents:
231
diff
changeset
|
520 |
155 | 521 #endif /* SOUND_SUPPORTS_FLAC */ |
522 | |
523 /* end of flac.c ... */ |