comparison src/flacng/plugin.c @ 930:2f742d127b3e trunk

[svn] - initial import of flacng from audacious-flacng-0.012
author nenolod
date Mon, 09 Apr 2007 10:55:23 -0700
parents
children b6c95e2a14f4
comparison
equal deleted inserted replaced
929:9631824411bf 930:2f742d127b3e
1 /*
2 * A FLAC decoder plugin for the Audacious Media Player
3 * Copyright (C) 2005 Ralf Ertzinger
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 2 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, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20 #include <audacious/util.h>
21 #include <audacious/output.h>
22 #include <glib/gi18n.h>
23 #include "flacng.h"
24 #include "tools.h"
25 #include "plugin.h"
26 #include "seekable_stream_callbacks.h"
27 #include "version.h"
28 #include "debug.h"
29
30 static gchar *flac_fmts[] = { "flac", NULL };
31
32 InputPlugin flac_ip = {
33 NULL,
34 NULL,
35 "FLACng Audio Plugin",
36 flac_init,
37 flac_aboutbox,
38 NULL,
39 flac_is_our_file,
40 NULL,
41 flac_play_file,
42 flac_stop,
43 flac_pause,
44 flac_seek,
45 NULL,
46 NULL,
47 NULL,
48 NULL,
49 NULL,
50 NULL,
51 NULL,
52 NULL,
53 NULL,
54 flac_get_song_info,
55 NULL,
56 NULL,
57 flac_get_song_tuple, // get a tuple
58 NULL,
59 NULL, // write a tuple back to a file as a tag
60 /* flac_is_our_fd */ NULL, // version of is_our_file which is handed an FD
61 flac_fmts // vector of fileextensions allowed by the plugin
62 };
63
64 FLAC__StreamDecoder* test_decoder;
65 FLAC__StreamDecoder* main_decoder;
66 callback_info* test_info;
67 callback_info* main_info;
68 gboolean plugin_initialized = FALSE;
69 gint seek_to = -1;
70 static GThread* thread;
71 GMutex* flac_pl_mutex;
72
73 /* === */
74
75 InputPlugin* get_iplugin_info(void) {
76
77 _ENTER;
78
79 _DEBUG("%s (%s)", flac_ip.description, _VERSION);
80
81 _LEAVE &flac_ip;
82 }
83
84 /* --- */
85
86 void flac_init(void) {
87
88 FLAC__StreamDecoderInitStatus ret;
89
90 _ENTER;
91
92 /*
93 * Callback structure and decoder for file test
94 * purposes
95 */
96 if (NULL == (test_info = init_callback_info("test"))) {
97 _ERROR("Could not initialize the test callback structure!");
98 _LEAVE;
99 }
100 _DEBUG("Test callback structure at %p", test_info);
101
102 if (NULL == (test_decoder = FLAC__stream_decoder_new())) {
103 _ERROR("Could not create the test FLAC decoder instance!");
104 _LEAVE;
105 }
106
107
108 /*
109 * We want the VORBISCOMMENT metadata, for the file tags
110 * and SEEKTABLE
111 */
112 FLAC__stream_decoder_set_metadata_respond(test_decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT);
113 FLAC__stream_decoder_set_metadata_respond(test_decoder, FLAC__METADATA_TYPE_SEEKTABLE);
114
115 /*
116 * Callback structure and decoder for main decoding
117 * loop
118 */
119 if (NULL == (main_info = init_callback_info("main"))) {
120 _ERROR("Could not initialize the main callback structure!");
121 _LEAVE;
122 }
123 _DEBUG("Main callback structure at %p", main_info);
124
125 if (NULL == (main_decoder = FLAC__stream_decoder_new())) {
126 _ERROR("Could not create the main FLAC decoder instance!");
127 _LEAVE;
128 }
129
130
131 /*
132 * We want the VORBISCOMMENT metadata, for the file tags
133 * and SEEKTABLE
134 */
135 FLAC__stream_decoder_set_metadata_respond(main_decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT);
136 FLAC__stream_decoder_set_metadata_respond(main_decoder, FLAC__METADATA_TYPE_SEEKTABLE);
137
138 /*
139 * Initialize decoders
140 */
141 if (FLAC__STREAM_DECODER_INIT_STATUS_OK != (ret = FLAC__stream_decoder_init_stream(
142 test_decoder,
143 read_callback,
144 seek_callback,
145 tell_callback,
146 length_callback,
147 eof_callback,
148 write_callback,
149 metadata_callback,
150 error_callback,
151 test_info))) {
152 _ERROR("Could not initialize test FLAC decoder: %s(%d)", StreamDecoderInitState(ret), ret);
153 _LEAVE;
154 }
155
156 if (FLAC__STREAM_DECODER_INIT_STATUS_OK != (ret = FLAC__stream_decoder_init_stream(
157 main_decoder,
158 read_callback,
159 seek_callback,
160 tell_callback,
161 length_callback,
162 eof_callback,
163 write_callback,
164 metadata_callback,
165 error_callback,
166 main_info))) {
167 _ERROR("Could not initialize main FLAC decoder: %s(%d)", StreamDecoderInitState(ret), ret);
168 _LEAVE;
169 }
170
171 /*
172 * Initialize the play loop mutex
173 */
174 flac_pl_mutex = g_mutex_new();
175
176 _DEBUG("plugin initialized OK!");
177 plugin_initialized = TRUE;
178 _LEAVE;
179 }
180
181 /* --- */
182
183 gboolean flac_is_our_file(gchar* filename) {
184
185 _ENTER;
186
187 if (!plugin_initialized) {
188 _ERROR("Plugin not initialized!");
189 _LEAVE FALSE;
190 }
191
192 _DEBUG("Testing file: %s", filename);
193
194 if (FALSE == read_metadata(filename, test_decoder, test_info)) {
195 _DEBUG("File not handled by this plugin!");
196 _LEAVE FALSE;
197 }
198
199 /*
200 * See if the metadata has changed
201 */
202 if (FALSE == test_info->metadata_changed) {
203 _DEBUG("No metadata found in stream");
204 _LEAVE FALSE;
205 }
206
207 /*
208 * If we get here, the file is supported by FLAC.
209 * The stream characteristics have been filled in by
210 * the metadata callback.
211 * We can close the stream now.
212 */
213
214 vfs_fclose(test_info->input_stream);
215 test_info->input_stream = NULL;
216
217
218 _DEBUG("Stream encoded at %d Hz, %d bps, %d channels",
219 test_info->stream.samplerate,
220 test_info->stream.bits_per_sample,
221 test_info->stream.channels);
222
223 if (MAX_SUPPORTED_CHANNELS < test_info->stream.channels) {
224 _ERROR("This number of channels (%d) is currently not supported, stream not handled by this plugin",
225 test_info->stream.channels);
226 _LEAVE FALSE;
227 }
228
229 if ((16 != test_info->stream.bits_per_sample) &&
230 (24 != test_info->stream.bits_per_sample) &&
231 (8 != test_info->stream.bits_per_sample)) {
232 _ERROR("This number of bits (%d) is currently not supported, stream not handled by this plugin",
233 test_info->stream.bits_per_sample);
234 _LEAVE FALSE;
235 }
236
237 /*
238 * Looks good.
239 */
240
241 _DEBUG("Accepting file %s", filename);
242
243 reset_info(test_info);
244
245 _LEAVE TRUE;
246 }
247
248 /* --- */
249
250 void squeeze_audio(gint32* src, void* dst, guint count, guint src_res, guint dst_res) {
251
252 /*
253 * Changes the sample width of count samples in src from
254 * src_res to dst_res and places the result in dst
255 */
256
257 gint i;
258 gint32* rp = src;
259 gint8* wp = dst;
260 gint16* wp2 = dst;
261 gint32* wp4 = dst;
262
263 _ENTER;
264
265 _DEBUG("Converting %d samples %d->%d", count, src_res, dst_res);
266
267 if ((src_res % 8 != 0) || (dst_res % 8 != 0)) {
268 _ERROR("Can not convert from %d bps to %d bps: not a multiple of 8",
269 src_res, dst_res);
270 _LEAVE;
271 }
272
273 if (16 == dst_res) {
274 if (8 == src_res) {
275 for (i=0; i<count; i++, wp2++, rp++) {
276 *wp2 = (*rp << 8) & 0xffff;
277 }
278 } else if (16 == src_res) {
279 for (i=0; i<count; i++, wp2++, rp++) {
280 *wp2 = *rp & 0xffff;
281 }
282 } else if (24 == src_res) {
283 for (i=0; i<count; i++, wp2++, rp++) {
284 *wp2 = (*rp >> 8) & 0xffff;
285 }
286 } else if (32 == src_res) {
287 for (i=0; i<count; i++, wp2++, rp++) {
288 *wp2 = (*rp >> 16) & 0xffff;
289 }
290 }
291 } else if (8 == dst_res) {
292 if (8 == src_res) {
293 for (i=0; i<count; i++, wp++, rp++) {
294 *wp = *rp & 0xff;
295 }
296 } else if (16 == src_res) {
297 for (i=0; i<count; i++, wp++, rp++) {
298 *wp = (*rp >> 8) & 0xff;
299 }
300 } else if (24 == src_res) {
301 for (i=0; i<count; i++, wp++, rp++) {
302 *wp = (*rp >> 16) & 0xff;
303 }
304 } else if (32 == src_res) {
305 for (i=0; i<count; i++, wp++, rp++) {
306 *wp = (*rp >> 24) & 0xff;
307 }
308 }
309 } else if (32 == dst_res) {
310 if (8 == src_res) {
311 for (i=0; i<count; i++, wp4++, rp++) {
312 *wp4 = (*rp << 24) & 0xffffffff;
313 }
314 } else if (16 == src_res) {
315 for (i=0; i<count; i++, wp4++, rp++) {
316 *wp4 = (*rp << 16) & 0xffffffff;
317 }
318 } else if (24 == src_res) {
319 for (i=0; i<count; i++, wp4++, rp++) {
320 *wp4 = (*rp << 8) & 0xffffffff;
321 }
322 } else if (32 == src_res) {
323 for (i=0; i<count; i++, wp4++, rp++) {
324 *wp4 = *rp;
325 }
326 }
327 }
328
329 _LEAVE;
330 }
331
332 /* --- */
333
334 static gpointer flac_play_loop(gpointer arg) {
335
336 /*
337 * The main play loop.
338 * Decode a frame, push the decoded data to the output plugin
339 * chunkwise. Repeat until finished.
340 *
341 * Must be entered with flac_pl_mutex held!
342 */
343
344 gint ofree;
345 gint32* read_pointer;
346 gint elements_left;
347 gint seek_sample;
348 FLAC__StreamDecoderState state;
349 struct stream_info stream_info;
350 guint sample_count;
351 void* play_buffer;
352 InputPlayback* playback = (InputPlayback *) arg;
353
354 _ENTER;
355
356 if (NULL == (play_buffer = malloc(BUFFER_SIZE_BYTE))) {
357 _ERROR("Could not allocate conversion buffer");
358 playback->playing = FALSE;
359 g_mutex_unlock(flac_pl_mutex);
360 _LEAVE NULL;
361 }
362
363 stream_info.samplerate = main_info->stream.samplerate;
364 stream_info.channels = main_info->stream.channels;
365 main_info->metadata_changed = FALSE;
366
367 if (!playback->output->open_audio(FMT_S16_NE,
368 main_info->stream.samplerate,
369 main_info->stream.channels)) {
370 playback->playing = FALSE;
371 _ERROR("Could not open output plugin!");
372 g_mutex_unlock(flac_pl_mutex);
373 _LEAVE NULL;
374 }
375
376 while (TRUE == playback->playing) {
377
378 /*
379 * Try to decode a single frame of audio
380 */
381 if (FALSE == FLAC__stream_decoder_process_single(main_decoder)) {
382 /*
383 * Something went wrong
384 */
385 _ERROR("Error while decoding!");
386 break;
387 }
388
389 /*
390 * Maybe the metadata changed midstream. Check.
391 */
392
393 if (main_info->metadata_changed) {
394 /*
395 * Samplerate and channels are important
396 */
397 if (stream_info.samplerate != main_info->stream.samplerate) {
398 _ERROR("Samplerate changed midstream (now: %d, was: %d). This is not supported yet.",
399 main_info->stream.samplerate, stream_info.samplerate);
400 break;
401 }
402
403 if (stream_info.channels != main_info->stream.channels) {
404 _ERROR("Number of channels changed midstream (now: %d, was: %d). This is not supported yet.",
405 main_info->stream.channels, stream_info.channels);
406 break;
407 }
408
409 main_info->metadata_changed = FALSE;
410 }
411
412 /*
413 * Compare the frame metadata to the current stream metadata
414 */
415 if (main_info->stream.samplerate != main_info->frame.samplerate) {
416 _ERROR("Frame samplerate mismatch (stream: %d, frame: %d)!",
417 main_info->stream.samplerate, main_info->frame.samplerate);
418 break;
419 }
420
421 if (main_info->stream.channels != main_info->frame.channels) {
422 _ERROR("Frame channel mismatch (stream: %d, frame: %d)!",
423 main_info->stream.channels, main_info->frame.channels);
424 break;
425 }
426
427 /*
428 * If the frame decoded was an audio frame we now
429 * have data in info->output_buffer
430 *
431 * The data in this buffer is in 32 bit wide samples, even if the
432 * real sample width is smaller. It has to be converted before
433 * feeding it to he output plugin.
434 *
435 * Feed the data in chunks of OUTPUT_BLOCK_SIZE elements to the output
436 * plugin
437 */
438 read_pointer = main_info->output_buffer;
439 elements_left = main_info->buffer_used;
440
441 while ((TRUE == playback->playing) && (0 != elements_left)) {
442
443 sample_count = MIN(OUTPUT_BLOCK_SIZE, elements_left);
444
445 /*
446 * Convert the audio.
447 * Currently this is hardcoded to 16 bit.
448 * 8 and 24 bit are sampled up/down linear.
449 */
450 _DEBUG("Converting %d samples to FMT_S16_NE", sample_count);
451 squeeze_audio(read_pointer, play_buffer, sample_count, main_info->frame.bits_per_sample, 16);
452
453 _DEBUG("Copying %d samples to output plugin", sample_count);
454
455 produce_audio(flac_ip.output->written_time(), FMT_S16_NE, main_info->frame.channels, sample_count * sizeof(gint16), play_buffer, NULL);
456
457 read_pointer += sample_count;
458 elements_left -= sample_count;
459
460 _DEBUG("%d elements left to be output", elements_left);
461 }
462
463 /*
464 * Clear the buffer.
465 */
466 main_info->write_pointer = main_info->output_buffer;
467 main_info->buffer_free = BUFFER_SIZE_SAMP;
468 main_info->buffer_used = 0;
469
470 /*
471 * Do we have to seek to somewhere?
472 */
473 if (-1 != seek_to) {
474 _DEBUG("Seek requested to %d seconds", seek_to);
475
476 if (FALSE == main_info->stream.has_seektable) {
477 _ERROR("Stream does not have a seektable, can not seek!");
478 } else {
479 seek_sample = seek_to * main_info->stream.samplerate;
480 _DEBUG("Seek requested to sample %d", seek_sample);
481 if (FALSE == FLAC__stream_decoder_seek_absolute(main_decoder, seek_sample)) {
482 _ERROR("Could not seek to sample %d!", seek_sample);
483 } else {
484 /*
485 * Flush the buffers
486 */
487 flac_ip.output->flush(seek_to * 1000);
488 }
489 }
490 seek_to = -1;
491 }
492
493 /*
494 * Have we reached the end of the stream?
495 */
496 state = FLAC__stream_decoder_get_state(main_decoder);
497 if (0 == elements_left && (FLAC__STREAM_DECODER_END_OF_STREAM == state)) {
498 /*
499 * Yes. Drain the output buffer and stop playing.
500 */
501
502 _DEBUG("End of stream reached, draining output buffer");
503
504 while((-1 == seek_to) && flac_ip.output->buffer_playing() && playback->playing == TRUE) {
505 g_usleep(40000);
506 }
507
508 if (-1 == seek_to) {
509 _DEBUG("Output buffer empty.");
510 playback->playing = FALSE;
511 }
512 }
513 }
514
515 /*
516 * Clean up a bit
517 */
518 playback->playing = FALSE;
519 _DEBUG("Closing audio device");
520 flac_ip.output->close_audio();
521 _DEBUG("Audio device closed");
522
523 free(play_buffer);
524
525 if (FALSE == FLAC__stream_decoder_flush(main_decoder)) {
526 _ERROR("Could not flush decoder state!");
527 }
528
529 /*
530 * Release the play loop mutex
531 */
532 g_mutex_unlock(flac_pl_mutex);
533
534 g_thread_exit(NULL);
535
536 _LEAVE NULL;
537 }
538
539 /* --- */
540
541 void flac_play_file (InputPlayback* input) {
542
543 gint l;
544
545 _ENTER;
546
547 if (!plugin_initialized) {
548 _ERROR("plugin not initialized!");
549 _LEAVE;
550 }
551
552 /*
553 * This will end a currently running decoder thread
554 */
555 input->playing = FALSE;
556 xmms_usleep(20000);
557
558 if (FALSE == read_metadata(input->filename, main_decoder, main_info)) {
559 _ERROR("Could not prepare file for playing!");
560 _LEAVE;
561 }
562
563 /*
564 * Calculate the length
565 */
566 if (0 == main_info->stream.samplerate) {
567 _ERROR("Invalid sample rate for stream!");
568 l = -1;
569 } else {
570 l = (main_info->stream.samples / main_info->stream.samplerate) * 1000;
571 }
572
573 /*
574 * Grab the play loop mutex
575 */
576 _DEBUG("Waiting for play loop mutex...");
577 g_mutex_lock(flac_pl_mutex);
578 _DEBUG("Got play loop mutex");
579
580 input->playing = TRUE;
581
582 flac_ip.set_info(get_title(input->filename, main_info), l, -1, main_info->stream.samplerate, main_info->stream.channels);
583
584 thread = g_thread_create(flac_play_loop, input, TRUE, NULL);
585
586 _LEAVE;
587 }
588
589 /* --- */
590
591 void flac_stop(InputPlayback* input) {
592
593 _ENTER;
594
595 input->playing = FALSE;
596
597 _LEAVE;
598 }
599
600 /* --- */
601
602 void flac_pause(InputPlayback* input, gshort p) {
603
604 _ENTER;
605
606 input->output->pause(p);
607
608 _LEAVE;
609 }
610
611 /* --- */
612
613 void flac_seek(InputPlayback* input, gint time) {
614
615 _ENTER;
616
617 if (!input->playing) {
618 _DEBUG("Can not seek while not playing");
619 _LEAVE;
620 }
621
622 _DEBUG("Requesting seek to %d", time);
623 seek_to = time;
624
625 while (-1 != seek_to) {
626 xmms_usleep(10000);
627 }
628
629 _LEAVE;
630 }
631
632 /* --- */
633
634 void flac_get_song_info(gchar* filename, gchar** title, gint* length) {
635
636 gint l;
637
638 _ENTER;
639
640 if (FALSE == read_metadata(filename, test_decoder, test_info)) {
641 _ERROR("Could not read file info!");
642 *length = -1;
643 *title = g_strdup("");
644 _LEAVE;
645 }
646
647 /*
648 * Calculate the stream length (milliseconds)
649 */
650 if (0 == test_info->stream.samplerate) {
651 _ERROR("Invalid sample rate for stream!");
652 l = -1;
653 } else {
654 l = (test_info->stream.samples / test_info->stream.samplerate) * 1000;
655 _DEBUG("Stream length: %d seconds", l/1000);
656 }
657
658 *length = l;
659 *title = get_title(filename, test_info);
660
661 reset_info(test_info);
662
663 _LEAVE;
664 }
665
666 /* --- */
667
668 TitleInput *flac_get_song_tuple(gchar* filename) {
669
670 gint l;
671 TitleInput *tuple;
672
673 _ENTER;
674
675 if (FALSE == read_metadata(filename, test_decoder, test_info)) {
676 _ERROR("Could not read metadata tuple for file <%s>", filename);
677 _LEAVE NULL;
678 }
679
680 tuple = get_tuple(filename, test_info);
681
682 reset_info(test_info);
683
684 _LEAVE tuple;
685 }
686
687 /* --- */
688
689 void flac_aboutbox(void) {
690
691 static GtkWidget* about_window;
692
693 if (about_window) {
694 gdk_window_raise(about_window->window);
695 }
696
697 about_window = xmms_show_message(_("About FLAC Audio Plugin"),
698 ("FLAC Audio Plugin (" _VERSION ")\n\n"
699 "Original code by\n"
700 "Ralf Ertzinger <ralf@skytale.net>\n"
701 "\n"
702 "http://www.skytale.net/projects/bmp-flac2/"),
703 _("OK"), FALSE, NULL, NULL);
704 g_signal_connect(G_OBJECT(about_window), "destroy",
705 G_CALLBACK(gtk_widget_destroyed), &about_window);
706 }