1 /*                                                     -*- linux-c -*-
2     Copyright (C) 2005 Tom Szilagyi
3 
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 
18     $Id: dec_flac.c 1245 2012-02-04 10:33:30Z assworth $
19 */
20 
21 #include <config.h>
22 
23 #include <stddef.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <FLAC/format.h>
29 #include <FLAC/metadata.h>
30 
31 #include "../metadata.h"
32 #include "../metadata_api.h"
33 #include "../metadata_flac.h"
34 #include "../rb.h"
35 #include "dec_flac.h"
36 
37 
38 extern size_t sample_size;
39 
40 
41 /* FLAC write callback */
42 FLAC__StreamDecoderWriteStatus
write_callback(const FLAC__StreamDecoder * decoder,const FLAC__Frame * frame,const FLAC__int32 * const buffer[],void * client_data)43 write_callback(const FLAC__StreamDecoder * decoder,
44                const FLAC__Frame * frame,
45                const FLAC__int32 * const buffer[],
46                void * client_data) {
47 
48 	decoder_t * dec = (decoder_t *) client_data;
49 	flac_pdata_t * pd = (flac_pdata_t *)dec->pdata;
50 	file_decoder_t * fdec = dec->fdec;
51 	int i, j;
52 	long int scale, blocksize;
53         FLAC__int32 buf[2];
54         float fbuf[2];
55 
56 
57         if (pd->probing)
58                 return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
59 
60         blocksize = frame->header.blocksize;
61         scale = 1 << (pd->bits_per_sample - 1);
62 
63         for (i = 0; i < blocksize; i++) {
64                 for (j = 0; j < pd->channels; j++) {
65                         buf[j] = *(buffer[j] + i);
66                         fbuf[j] = (float)buf[j] * fdec->voladj_lin / scale;
67                 }
68                 rb_write(pd->rb, (char *)fbuf,
69                                       pd->channels * sample_size);
70         }
71 
72         return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
73 }
74 
75 
76 /* FLAC metadata callback */
77 void
metadata_callback(const FLAC__StreamDecoder * decoder,const FLAC__StreamMetadata * metadata,void * client_data)78 metadata_callback(const FLAC__StreamDecoder * decoder,
79                   const FLAC__StreamMetadata * metadata,
80                   void * client_data) {
81 
82 	decoder_t * dec = (decoder_t *)client_data;
83 	flac_pdata_t * pd = (flac_pdata_t *)dec->pdata;
84 
85         if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
86                 pd->SR = metadata->data.stream_info.sample_rate;
87                 pd->bits_per_sample = metadata->data.stream_info.bits_per_sample;
88                 pd->channels = metadata->data.stream_info.channels;
89                 pd->total_samples = metadata->data.stream_info.total_samples;
90         } else {
91                 fprintf(stderr, "FLAC metadata callback: ignoring unexpected header\n");
92 	}
93 }
94 
95 
96 /* FLAC error callback */
97 void
error_callback(const FLAC__StreamDecoder * decoder,FLAC__StreamDecoderErrorStatus status,void * client_data)98 error_callback(const FLAC__StreamDecoder * decoder,
99                FLAC__StreamDecoderErrorStatus status,
100                void * client_data) {
101 
102 	decoder_t * dec = (decoder_t *) client_data;
103 	flac_pdata_t * pd = (flac_pdata_t *)dec->pdata;
104 
105         pd->error = 1;
106 }
107 
108 
109 decoder_t *
flac_decoder_init(file_decoder_t * fdec)110 flac_decoder_init(file_decoder_t * fdec) {
111 
112         decoder_t * dec = NULL;
113 
114         if ((dec = calloc(1, sizeof(decoder_t))) == NULL) {
115                 fprintf(stderr, "dec_flac.c: flac_decoder_new() failed: calloc error\n");
116                 return NULL;
117         }
118 
119 	dec->fdec = fdec;
120 
121         if ((dec->pdata = calloc(1, sizeof(flac_pdata_t))) == NULL) {
122                 fprintf(stderr, "dec_flac.c: flac_decoder_new() failed: calloc error\n");
123                 return NULL;
124         }
125 
126 	dec->init = flac_decoder_init;
127 	dec->destroy = flac_decoder_destroy;
128 	dec->open = flac_decoder_open;
129 	dec->send_metadata = flac_decoder_send_metadata;
130 	dec->close = flac_decoder_close;
131 	dec->read = flac_decoder_read;
132 	dec->seek = flac_decoder_seek;
133 
134 	return dec;
135 }
136 
137 
138 void
flac_decoder_destroy(decoder_t * dec)139 flac_decoder_destroy(decoder_t * dec) {
140 
141 	free(dec->pdata);
142 	free(dec);
143 }
144 
145 
146 int
flac_meta_vc_replace_or_append(file_decoder_t * fdec,FLAC__StreamMetadata * smeta)147 flac_meta_vc_replace_or_append(file_decoder_t * fdec, FLAC__StreamMetadata * smeta) {
148 
149 	FLAC__Metadata_SimpleIterator * iter;
150 	FLAC__bool ret;
151 	int found = 0;
152 
153 	iter = FLAC__metadata_simple_iterator_new();
154 	if (iter == NULL) {
155 		return META_ERROR_NOMEM;
156 	}
157 
158 	ret = FLAC__metadata_simple_iterator_init(iter, fdec->filename, false, false);
159 	if (!ret) {
160 		fprintf(stderr, "dec_flac.c/flac_meta_replace_or_append: error: %s\n",
161 			FLAC__Metadata_SimpleIteratorStatusString[
162 			  FLAC__metadata_simple_iterator_status(iter)]);
163 
164 		FLAC__metadata_simple_iterator_delete(iter);
165 		return META_ERROR_INTERNAL;
166 	}
167 
168 	if (FLAC__metadata_simple_iterator_is_writable(iter) == false) {
169 		fprintf(stderr, "error: FLAC meta not writable!\n");
170 		FLAC__metadata_simple_iterator_delete(iter);
171 		return META_ERROR_NOT_WRITABLE;
172 	}
173 
174 	do {
175 		switch (FLAC__metadata_simple_iterator_get_block_type(iter)) {
176 		case FLAC__METADATA_TYPE_VORBIS_COMMENT:
177 			ret = FLAC__metadata_simple_iterator_set_block(iter, smeta, true);
178 			if (ret == false) {
179 				fprintf(stderr, "error: FLAC metadata write failed!\n");
180 				FLAC__metadata_simple_iterator_delete(iter);
181 				return META_ERROR_INTERNAL;
182 			}
183 			found = 1;
184 			break;
185 		default:
186 			break;
187 		}
188 	} while (FLAC__metadata_simple_iterator_next(iter));
189 
190 	if (!found) {
191 		do { /* rewind to STREAMINFO and insert after that */
192 			if (FLAC__metadata_simple_iterator_get_block_type(iter) ==
193 			    FLAC__METADATA_TYPE_STREAMINFO) {
194 				break;
195 			}
196 		} while (FLAC__metadata_simple_iterator_prev(iter));
197 
198 		ret = FLAC__metadata_simple_iterator_insert_block_after(iter, smeta, true);
199 		if (ret == false) {
200 			fprintf(stderr, "error: FLAC metadata write failed!\n");
201 			FLAC__metadata_simple_iterator_delete(iter);
202 			return META_ERROR_INTERNAL;
203 		}
204 	}
205 
206 	FLAC__metadata_simple_iterator_delete(iter);
207 	return META_ERROR_NONE;
208 }
209 
210 
211 int
flac_meta_append_pics(file_decoder_t * fdec,metadata_t * meta)212 flac_meta_append_pics(file_decoder_t * fdec, metadata_t * meta) {
213 
214 	FLAC__Metadata_SimpleIterator * iter;
215 	FLAC__bool ret;
216 	meta_frame_t * frame;
217 	int found_vc = 0;
218 
219 	iter = FLAC__metadata_simple_iterator_new();
220 	if (iter == NULL) {
221 		return META_ERROR_NOMEM;
222 	}
223 
224 	ret = FLAC__metadata_simple_iterator_init(iter, fdec->filename, false, false);
225 	if (!ret) {
226 		fprintf(stderr, "dec_flac.c/flac_meta_append_pics: error: %s\n",
227 			FLAC__Metadata_SimpleIteratorStatusString[
228 			  FLAC__metadata_simple_iterator_status(iter)]);
229 
230 		FLAC__metadata_simple_iterator_delete(iter);
231 		return META_ERROR_INTERNAL;
232 	}
233 
234 	if (FLAC__metadata_simple_iterator_is_writable(iter) == false) {
235 		fprintf(stderr, "error: FLAC meta not writable!\n");
236 		FLAC__metadata_simple_iterator_delete(iter);
237 		return META_ERROR_NOT_WRITABLE;
238 	}
239 
240 	/* try to insert pictures after the Vorbis Comment, or if
241 	   there is none, after STREAMINFO. */
242 	do {
243 		if (FLAC__metadata_simple_iterator_get_block_type(iter) ==
244 		    FLAC__METADATA_TYPE_VORBIS_COMMENT) {
245 			found_vc = 1;
246 			break;
247 		}
248 	} while (FLAC__metadata_simple_iterator_next(iter));
249 
250 	if (!found_vc) {
251 		do { /* rewind to STREAMINFO and insert after that */
252 			if (FLAC__metadata_simple_iterator_get_block_type(iter) ==
253 			    FLAC__METADATA_TYPE_STREAMINFO) {
254 				break;
255 			}
256 		} while (FLAC__metadata_simple_iterator_prev(iter));
257 	}
258 
259 	frame = metadata_get_frame_by_tag(meta, META_TAG_FLAC_APIC, NULL);
260 	while (frame) {
261 		FLAC__StreamMetadata * smeta = metadata_apic_frame_to_smeta(frame);
262 		ret = FLAC__metadata_simple_iterator_insert_block_after(iter, smeta, true);
263 		if (ret == false) {
264 			fprintf(stderr, "error: FLAC metadata write failed!\n");
265 			FLAC__metadata_object_delete(smeta);
266 			FLAC__metadata_simple_iterator_delete(iter);
267 			return META_ERROR_INTERNAL;
268 		}
269 
270 		FLAC__metadata_object_delete(smeta);
271 		frame = metadata_get_frame_by_tag(meta, META_TAG_FLAC_APIC, frame);
272 	}
273 
274 	FLAC__metadata_simple_iterator_delete(iter);
275 	return META_ERROR_NONE;
276 }
277 
278 
279 int
flac_meta_delete(file_decoder_t * fdec,int del_vc)280 flac_meta_delete(file_decoder_t * fdec, int del_vc) {
281 
282 	FLAC__Metadata_SimpleIterator * iter;
283 	FLAC__bool ret;
284 
285 	iter = FLAC__metadata_simple_iterator_new();
286 	if (iter == NULL) {
287 		return META_ERROR_NOMEM;
288 	}
289 
290 	ret = FLAC__metadata_simple_iterator_init(iter, fdec->filename, false, false);
291 	if (!ret) {
292 		fprintf(stderr, "dec_flac.c/flac_meta_delete: error: %s\n",
293 			FLAC__Metadata_SimpleIteratorStatusString[
294 			  FLAC__metadata_simple_iterator_status(iter)]);
295 
296 		FLAC__metadata_simple_iterator_delete(iter);
297 		return META_ERROR_INTERNAL;
298 	}
299 
300 	if (FLAC__metadata_simple_iterator_is_writable(iter) == false) {
301 		fprintf(stderr, "error: FLAC meta not writable!\n");
302 		FLAC__metadata_simple_iterator_delete(iter);
303 		return META_ERROR_NOT_WRITABLE;
304 	}
305 
306 	do {
307 		switch (FLAC__metadata_simple_iterator_get_block_type(iter)) {
308 		case FLAC__METADATA_TYPE_VORBIS_COMMENT:
309 			if (!del_vc)
310 				break;
311 			ret = FLAC__metadata_simple_iterator_delete_block(iter, true);
312 			if (ret == false) {
313 				fprintf(stderr, "error: FLAC metadata delete failed!\n");
314 				FLAC__metadata_simple_iterator_delete(iter);
315 				return META_ERROR_INTERNAL;
316 			}
317 			break;
318 		case FLAC__METADATA_TYPE_PICTURE:
319 			ret = FLAC__metadata_simple_iterator_delete_block(iter, true);
320 			if (ret == false) {
321 				fprintf(stderr, "error: FLAC metadata delete failed!\n");
322 				FLAC__metadata_simple_iterator_delete(iter);
323 				return META_ERROR_INTERNAL;
324 			}
325 			break;
326 		default:
327 			break;
328 		}
329 	} while (FLAC__metadata_simple_iterator_next(iter));
330 	FLAC__metadata_simple_iterator_delete(iter);
331 	return META_ERROR_NONE;
332 }
333 
334 int
flac_write_metadata(file_decoder_t * fdec,metadata_t * meta)335 flac_write_metadata(file_decoder_t * fdec, metadata_t * meta) {
336 
337 	int ret;
338 	int del_vc = 0;
339 
340 	if (metadata_get_frame_by_tag(meta, META_TAG_OXC, NULL) == NULL) {
341 		/* no Ogg Xiph comment in this metablock -- remove it from file */
342 		del_vc = 1;
343 	}
344 	ret = flac_meta_delete(fdec, del_vc);
345 	if (ret != META_ERROR_NONE) {
346 		return ret;
347 	}
348 
349 	if (!del_vc) {
350 		FLAC__StreamMetadata * smeta = metadata_to_flac_streammeta(meta);
351 		ret = flac_meta_vc_replace_or_append(fdec, smeta);
352 		if (ret != META_ERROR_NONE) {
353 			return ret;
354 		}
355 		FLAC__metadata_object_delete(smeta);
356 	}
357 
358 	if (metadata_get_frame_by_tag(meta, META_TAG_FLAC_APIC, NULL) != NULL) {
359 		ret = flac_meta_append_pics(fdec, meta);
360 		if (ret != META_ERROR_NONE) {
361 			return ret;
362 		}
363 	}
364 
365 	return META_ERROR_NONE;
366 }
367 
368 void
flac_send_metadata(decoder_t * dec)369 flac_send_metadata(decoder_t * dec) {
370 
371 	file_decoder_t * fdec = dec->fdec;
372 	metadata_t * meta;
373 	int found = 0;
374 	int writable;
375 	FLAC__Metadata_SimpleIterator * iter;
376 	FLAC__bool ret;
377 
378 	iter = FLAC__metadata_simple_iterator_new();
379 	if (iter == NULL) {
380 		return;
381 	}
382 
383 	writable = (access(fdec->filename, R_OK | W_OK) == 0) ? 1 : 0;
384 	ret = FLAC__metadata_simple_iterator_init(iter, fdec->filename, writable ? false : true, false);
385 	if (!ret) {
386 		fprintf(stderr, "dec_flac.c/flac_send_metadata: error: %s\n",
387 			FLAC__Metadata_SimpleIteratorStatusString[
388 			  FLAC__metadata_simple_iterator_status(iter)]);
389 
390 		FLAC__metadata_simple_iterator_delete(iter);
391 		return;
392 	}
393 
394 	meta = metadata_new();
395 	meta->fdec = fdec;
396 	fdec->meta = meta;
397 	meta->valid_tags = META_TAG_OXC | META_TAG_FLAC_APIC;
398 	if (writable && FLAC__metadata_simple_iterator_is_writable(iter) == true) {
399 		meta->writable = 1;
400 		fdec->meta_write = flac_write_metadata;
401 	}
402 
403 	do {
404 		FLAC__StreamMetadata * smeta;
405 		switch (FLAC__metadata_simple_iterator_get_block_type(iter)) {
406 		case FLAC__METADATA_TYPE_VORBIS_COMMENT:
407 			smeta = FLAC__metadata_simple_iterator_get_block(iter);
408 			FLAC__StreamMetadata_VorbisComment vc =
409 				FLAC__metadata_simple_iterator_get_block(iter)->data.vorbis_comment;
410 			metadata_from_flac_streammeta_vc(meta, &vc);
411 			found = 1;
412 
413 			FLAC__metadata_object_delete(smeta);
414 			break;
415 		case FLAC__METADATA_TYPE_PICTURE:
416 			smeta = FLAC__metadata_simple_iterator_get_block(iter);
417 			FLAC__StreamMetadata_Picture pic =
418 				FLAC__metadata_simple_iterator_get_block(iter)->data.picture;
419 			metadata_from_flac_streammeta_pic(meta, &pic);
420 			found = 1;
421 
422 			FLAC__metadata_object_delete(smeta);
423 			break;
424 		default:
425 			break;
426 		}
427 	} while (FLAC__metadata_simple_iterator_next(iter));
428 	FLAC__metadata_simple_iterator_delete(iter);
429 
430         if (!found && !meta->writable) {
431                 fdec->meta = NULL;
432                 metadata_free(meta);
433         }
434 }
435 
436 
437 int
flac_decoder_open(decoder_t * dec,char * filename)438 flac_decoder_open(decoder_t * dec, char * filename) {
439 
440 	flac_pdata_t * pd = (flac_pdata_t *)dec->pdata;
441 	file_decoder_t * fdec = dec->fdec;
442 
443 	int tried_flac = 0;
444 
445  try_flac:
446 	pd->error = 0;
447 	pd->state = FLAC__STREAM_DECODER_UNINITIALIZED;
448 	pd->flac_decoder = FLAC__stream_decoder_new();
449 
450 	if (FLAC__stream_decoder_init_file(pd->flac_decoder, filename,
451 					   write_callback, metadata_callback,
452 					   error_callback, (void *)dec)
453 	    != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
454 
455 		FLAC__stream_decoder_delete(pd->flac_decoder);
456 		return DECODER_OPEN_FERROR;
457 	}
458 
459 	pd->state = FLAC__stream_decoder_get_state(pd->flac_decoder);
460 	FLAC__stream_decoder_process_until_end_of_metadata(pd->flac_decoder);
461 
462 	if ((!pd->error) && (pd->channels > 0)) {
463 		if ((pd->channels != 1) && (pd->channels != 2)) {
464 			fprintf(stderr,
465 				"flac_decoder_open: FLAC file with %d channels is "
466 				"unsupported\n", pd->channels);
467 			return DECODER_OPEN_FERROR;
468 		} else {
469 			if (!tried_flac) {
470 				/* we need a real read test (some MP3's get to this point) */
471 				pd->probing = 1;
472 				FLAC__stream_decoder_process_single(pd->flac_decoder);
473 				pd->state = FLAC__stream_decoder_get_state(pd->flac_decoder);
474 
475 				if ((pd->state != FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) &&
476 				    (pd->state != FLAC__STREAM_DECODER_SEARCH_FOR_METADATA) &&
477 				    (pd->state != FLAC__STREAM_DECODER_READ_METADATA) &&
478 				    (pd->state != FLAC__STREAM_DECODER_READ_FRAME)) {
479 					return DECODER_OPEN_BADLIB;
480 				}
481 
482 				pd->probing = 0;
483 				tried_flac = 1;
484 				FLAC__stream_decoder_finish(pd->flac_decoder);
485 				FLAC__stream_decoder_delete(pd->flac_decoder);
486 				goto try_flac;
487 			}
488 
489 			pd->rb = rb_create(pd->channels * sample_size * RB_FLAC_SIZE);
490 
491 			fdec->fileinfo.channels = pd->channels;
492 			fdec->fileinfo.sample_rate = pd->SR;
493 			fdec->fileinfo.total_samples = pd->total_samples;
494 			fdec->fileinfo.bps = pd->bits_per_sample * fdec->fileinfo.sample_rate
495 				* fdec->fileinfo.channels;
496 
497 			fdec->file_lib = FLAC_LIB;
498 			strcpy(dec->format_str, "FLAC");
499 
500 			flac_send_metadata(dec);
501 			return DECODER_OPEN_SUCCESS;
502 		}
503 	} else {
504 		FLAC__stream_decoder_finish(pd->flac_decoder);
505 		FLAC__stream_decoder_delete(pd->flac_decoder);
506 
507 		return DECODER_OPEN_BADLIB;
508 	}
509 }
510 
511 
512 void
flac_decoder_send_metadata(decoder_t * dec)513 flac_decoder_send_metadata(decoder_t * dec) {
514 
515         file_decoder_t * fdec = dec->fdec;
516 
517         if (fdec->meta != NULL && fdec->meta_cb != NULL) {
518                 fdec->meta_cb(fdec->meta, fdec->meta_cbdata);
519         }
520 }
521 
522 
523 void
flac_decoder_close(decoder_t * dec)524 flac_decoder_close(decoder_t * dec) {
525 
526 	flac_pdata_t * pd = (flac_pdata_t *)dec->pdata;
527 
528 	FLAC__stream_decoder_finish(pd->flac_decoder);
529 	FLAC__stream_decoder_delete(pd->flac_decoder);
530 	rb_free(pd->rb);
531 }
532 
533 
534 unsigned int
flac_decoder_read(decoder_t * dec,float * dest,int num)535 flac_decoder_read(decoder_t * dec, float * dest, int num) {
536 
537 	flac_pdata_t * pd = (flac_pdata_t *)dec->pdata;
538 
539 	unsigned int numread = 0;
540 	unsigned int n_avail = 0;
541 
542 	pd->state = FLAC__stream_decoder_get_state(pd->flac_decoder);
543 	while ((rb_read_space(pd->rb) < num * pd->channels * sample_size) &&
544 	       (pd->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC ||
545 		pd->state == FLAC__STREAM_DECODER_SEARCH_FOR_METADATA ||
546 		pd->state == FLAC__STREAM_DECODER_READ_METADATA ||
547 		pd->state == FLAC__STREAM_DECODER_READ_FRAME)) {
548 		FLAC__stream_decoder_process_single(pd->flac_decoder);
549 		pd->state = FLAC__stream_decoder_get_state(pd->flac_decoder);
550 	}
551 
552 	/* Have i chosen the right conditions? */
553 	if ((pd->state != FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) &&
554 	    (pd->state != FLAC__STREAM_DECODER_SEARCH_FOR_METADATA) &&
555 	    (pd->state != FLAC__STREAM_DECODER_READ_METADATA) &&
556 	    (pd->state != FLAC__STREAM_DECODER_READ_FRAME) &&
557 	    (pd->state != FLAC__STREAM_DECODER_END_OF_STREAM)) {
558 		fprintf(stderr, "file_decoder_read() / FLAC: decoder error: %s\n",
559 			FLAC__StreamDecoderStateString[pd->state]);
560 		return 0; /* this means that a new file will be opened */
561 	}
562 
563 	n_avail = rb_read_space(pd->rb) /
564 		(pd->channels * sample_size);
565 
566 	if (n_avail > num) {
567 		n_avail = num;
568 	}
569 
570 	rb_read(pd->rb, (char *)dest, n_avail * pd->channels * sample_size);
571 	numread = n_avail;
572 	return numread;
573 }
574 
575 
576 void
flac_decoder_seek(decoder_t * dec,unsigned long long seek_to_pos)577 flac_decoder_seek(decoder_t * dec, unsigned long long seek_to_pos) {
578 
579 	flac_pdata_t * pd = (flac_pdata_t *)dec->pdata;
580 	file_decoder_t * fdec = dec->fdec;
581 	char flush_dest;
582 
583 
584 	if (seek_to_pos == fdec->fileinfo.total_samples) {
585 		--seek_to_pos;
586 	}
587 
588 	if (FLAC__stream_decoder_seek_absolute(pd->flac_decoder, seek_to_pos)) {
589 		fdec->samples_left = fdec->fileinfo.total_samples - seek_to_pos;
590 
591 		/* empty flac decoder ringbuffer */
592 		while (rb_read_space(pd->rb))
593 			rb_read(pd->rb, &flush_dest, sizeof(char));
594 	} else {
595 		fprintf(stderr, "flac_decoder_seek: warning: "
596 			"FLAC__file_decoder_seek_absolute() failed\n");
597 	}
598 }
599 
600 
601 
602 // vim: shiftwidth=8:tabstop=8:softtabstop=8 :
603