comparison src/vorbis/vorbis.c @ 12:3da1b8942b8b trunk

[svn] - remove src/Input src/Output src/Effect src/General src/Visualization src/Container
author nenolod
date Mon, 18 Sep 2006 03:14:20 -0700
parents src/Input/vorbis/vorbis.c@088092a52fea
children c812e846b84e
comparison
equal deleted inserted replaced
11:cff1d04026ae 12:3da1b8942b8b
1 /*
2 * Copyright (C) Tony Arcieri <bascule@inferno.tusculum.edu>
3 * Copyright (C) 2001-2002 Haavard Kvaalen <havardk@xmms.org>
4 *
5 * ReplayGain processing Copyright (C) 2002 Gian-Carlo Pascutto <gcp@sjeng.org>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 * 02110-1301, USA.
21 *
22 */
23
24 /*
25 * 2002-01-11 ReplayGain processing added by Gian-Carlo Pascutto <gcp@sjeng.org>
26 */
27
28 /*
29 * Note that this uses vorbisfile, which is not (currently)
30 * thread-safe.
31 */
32
33 #ifdef HAVE_CONFIG_H
34 # include "config.h"
35 #endif
36
37 #include <glib.h>
38 #include <glib/gi18n.h>
39 #include <gtk/gtk.h>
40
41 #include <stdlib.h>
42 #include <math.h>
43 #include <string.h>
44
45 #include <fcntl.h>
46
47 #include <ogg/ogg.h>
48 #include <vorbis/codec.h>
49 #include <vorbis/vorbisfile.h>
50
51 #include "audacious/plugin.h"
52 #include "audacious/output.h"
53 #include "audacious/util.h"
54 #include "audacious/configdb.h"
55 #include "audacious/titlestring.h"
56
57 #include "vorbis.h"
58 #include "http.h"
59
60 extern vorbis_config_t vorbis_cfg;
61
62 static TitleInput *get_song_tuple(gchar *filename);
63 static int vorbis_check_file(char *filename);
64 static void vorbis_play(char *filename);
65 static void vorbis_stop(void);
66 static void vorbis_pause(short p);
67 static void vorbis_seek(int time);
68 static int vorbis_time(void);
69 static void vorbis_get_song_info(char *filename, char **title, int *length);
70 static gchar *vorbis_generate_title(OggVorbis_File * vorbisfile, gchar * fn);
71 static void vorbis_aboutbox(void);
72 static void vorbis_init(void);
73 static void vorbis_cleanup(void);
74 static long vorbis_process_replaygain(float **pcm, int samples, int ch,
75 char *pcmout, float rg_scale);
76 static gboolean vorbis_update_replaygain(float *scale);
77
78 static size_t ovcb_read(void *ptr, size_t size, size_t nmemb,
79 void *datasource);
80 static int ovcb_seek(void *datasource, int64_t offset, int whence);
81 static int ovcb_close(void *datasource);
82 static long ovcb_tell(void *datasource);
83
84 ov_callbacks vorbis_callbacks = {
85 ovcb_read,
86 ovcb_seek,
87 ovcb_close,
88 ovcb_tell
89 };
90
91 InputPlugin vorbis_ip = {
92 NULL,
93 NULL,
94 NULL, /* description */
95 vorbis_init, /* init */
96 vorbis_aboutbox, /* aboutbox */
97 vorbis_configure, /* configure */
98 vorbis_check_file, /* is_our_file */
99 NULL,
100 vorbis_play,
101 vorbis_stop,
102 vorbis_pause,
103 vorbis_seek,
104 NULL, /* set eq */
105 vorbis_time,
106 NULL,
107 NULL,
108 vorbis_cleanup,
109 NULL,
110 NULL,
111 NULL,
112 NULL,
113 vorbis_get_song_info,
114 vorbis_file_info_box, /* file info box, tag editing */
115 NULL,
116 get_song_tuple
117 };
118
119 static OggVorbis_File vf;
120
121 static GThread *thread;
122 int vorbis_playing = 0;
123 static int vorbis_eos = 0;
124 static int vorbis_is_streaming = 0;
125 static int vorbis_bytes_streamed = 0;
126 static volatile int seekneeded = -1;
127 static int samplerate, channels;
128 GMutex *vf_mutex;
129 static gboolean output_error;
130
131 gchar **vorbis_tag_encoding_list = NULL;
132
133 InputPlugin *
134 get_iplugin_info(void)
135 {
136 vorbis_ip.description = g_strdup_printf(_("Ogg Vorbis Audio Plugin"));
137 return &vorbis_ip;
138 }
139
140 static int
141 vorbis_check_file(char *filename)
142 {
143 VFSFile *stream;
144 OggVorbis_File vfile; /* avoid thread interaction */
145 char *ext;
146 gint result;
147
148 /* is this our http resource? */
149 if (strncasecmp(filename, "http://", 7) == 0) {
150 ext = strrchr(filename, '.');
151 if (ext) {
152 if (!strncasecmp(ext, ".ogg", 4)) {
153 return TRUE;
154 }
155 }
156 return FALSE;
157 }
158
159 if (!(stream = vfs_fopen(filename, "r"))) {
160 return FALSE;
161 }
162 /*
163 * The open function performs full stream detection and machine
164 * initialization. If it returns zero, the stream *is* Vorbis and
165 * we're fully ready to decode.
166 */
167
168 /* libvorbisfile isn't thread safe... */
169 memset(&vfile, 0, sizeof(vfile));
170 g_mutex_lock(vf_mutex);
171
172 result = ov_test_callbacks(stream, &vfile, NULL, 0, vorbis_callbacks);
173
174 switch (result) {
175 case OV_EREAD:
176 #ifdef DEBUG
177 g_message("** vorbis.c: Media read error: %s", filename);
178 #endif
179 g_mutex_unlock(vf_mutex);
180 vfs_fclose(stream);
181 return FALSE;
182 break;
183 case OV_ENOTVORBIS:
184 #ifdef DEBUG
185 g_message("** vorbis.c: Not Vorbis data: %s", filename);
186 #endif
187 g_mutex_unlock(vf_mutex);
188 vfs_fclose(stream);
189 return FALSE;
190 break;
191 case OV_EVERSION:
192 #ifdef DEBUG
193 g_message("** vorbis.c: Version mismatch: %s", filename);
194 #endif
195 g_mutex_unlock(vf_mutex);
196 vfs_fclose(stream);
197 return FALSE;
198 break;
199 case OV_EBADHEADER:
200 #ifdef DEBUG
201 g_message("** vorbis.c: Invalid Vorbis bistream header: %s",
202 filename);
203 #endif
204 g_mutex_unlock(vf_mutex);
205 vfs_fclose(stream);
206 return FALSE;
207 break;
208 case OV_EFAULT:
209 #ifdef DEBUG
210 g_message("** vorbis.c: Internal logic fault while reading %s",
211 filename);
212 #endif
213 g_mutex_unlock(vf_mutex);
214 vfs_fclose(stream);
215 return FALSE;
216 break;
217 case 0:
218 break;
219 default:
220 break;
221 }
222
223
224 ov_clear(&vfile); /* once the ov_open succeeds, the stream belongs to
225 vorbisfile.a. ov_clear will fclose it */
226 g_mutex_unlock(vf_mutex);
227 return TRUE;
228 }
229
230 static void
231 vorbis_jump_to_time(long time)
232 {
233 g_mutex_lock(vf_mutex);
234
235 /*
236 * We need to guard against seeking to the end, or things
237 * don't work right. Instead, just seek to one second prior
238 * to this
239 */
240 if (time == ov_time_total(&vf, -1))
241 time--;
242
243 vorbis_ip.output->flush(time * 1000);
244 ov_time_seek(&vf, time);
245
246 g_mutex_unlock(vf_mutex);
247 }
248
249 static void
250 do_seek(void)
251 {
252 if (seekneeded != -1 && !vorbis_is_streaming) {
253 vorbis_jump_to_time(seekneeded);
254 seekneeded = -1;
255 vorbis_eos = FALSE;
256 }
257 }
258
259 static int
260 vorbis_process_data(int last_section, gboolean use_rg, float rg_scale)
261 {
262 char pcmout[4096];
263 int bytes;
264 float **pcm;
265
266 /*
267 * A vorbis physical bitstream may consist of many logical
268 * sections (information for each of which may be fetched from
269 * the vf structure). This value is filled in by ov_read to
270 * alert us what section we're currently decoding in case we
271 * need to change playback settings at a section boundary
272 */
273 int current_section;
274
275 g_mutex_lock(vf_mutex);
276 if (use_rg) {
277 bytes =
278 ov_read_float(&vf, &pcm, sizeof(pcmout) / 2 / channels,
279 &current_section);
280 if (bytes > 0)
281 bytes = vorbis_process_replaygain(pcm, bytes, channels,
282 pcmout, rg_scale);
283 }
284 else {
285 bytes =
286 ov_read(&vf, pcmout, sizeof(pcmout),
287 (int) (G_BYTE_ORDER == G_BIG_ENDIAN),
288 2, 1, &current_section);
289 }
290
291 switch (bytes) {
292 case 0:
293 /* EOF */
294 g_mutex_unlock(vf_mutex);
295 vorbis_ip.output->buffer_free();
296 vorbis_ip.output->buffer_free();
297 vorbis_eos = TRUE;
298 return last_section;
299
300 case OV_HOLE:
301 case OV_EBADLINK:
302 /*
303 * error in the stream. Not a problem, just
304 * reporting it in case we (the app) cares.
305 * In this case, we don't.
306 */
307 g_mutex_unlock(vf_mutex);
308 return last_section;
309 }
310
311 if (current_section != last_section) {
312 /*
313 * The info struct is different in each section. vf
314 * holds them all for the given bitstream. This
315 * requests the current one
316 */
317 vorbis_info *vi = ov_info(&vf, -1);
318
319 if (vi->channels > 2) {
320 vorbis_eos = TRUE;
321 g_mutex_unlock(vf_mutex);
322 return current_section;
323 }
324
325
326 if (vi->rate != samplerate || vi->channels != channels) {
327 samplerate = vi->rate;
328 channels = vi->channels;
329 vorbis_ip.output->buffer_free();
330 vorbis_ip.output->buffer_free();
331 vorbis_ip.output->close_audio();
332 if (!vorbis_ip.output->
333 open_audio(FMT_S16_NE, vi->rate, vi->channels)) {
334 output_error = TRUE;
335 vorbis_eos = TRUE;
336 g_mutex_unlock(vf_mutex);
337 return current_section;
338 }
339 vorbis_ip.output->flush(ov_time_tell(&vf) * 1000);
340 }
341 }
342
343 g_mutex_unlock(vf_mutex);
344
345 if (!vorbis_playing)
346 return current_section;
347
348 if (seekneeded != -1)
349 do_seek();
350
351 produce_audio(vorbis_ip.output->written_time(),
352 FMT_S16_NE, channels, bytes, pcmout, &vorbis_playing);
353
354 return current_section;
355 }
356
357 static gpointer
358 vorbis_play_loop(gpointer arg)
359 {
360 char *filename = (char *) arg;
361 gchar *title = NULL;
362 double time;
363 long timercount = 0;
364 vorbis_info *vi;
365 long br;
366
367 int last_section = -1;
368
369 VFSFile *stream = NULL;
370 void *datasource = NULL;
371
372 gboolean use_rg;
373 float rg_scale = 1.0;
374
375 memset(&vf, 0, sizeof(vf));
376
377 if (strncasecmp("http://", filename, 7) != 0) {
378 /* file is a real file */
379 if ((stream = vfs_fopen(filename, "r")) == NULL) {
380 vorbis_eos = TRUE;
381 goto play_cleanup;
382 }
383 datasource = (void *) stream;
384 }
385 else {
386 /* file is a stream */
387 vorbis_is_streaming = 1;
388 vorbis_http_open(filename);
389 datasource = "NULL";
390 }
391
392 /*
393 * The open function performs full stream detection and
394 * machine initialization. None of the rest of ov_xx() works
395 * without it
396 */
397
398 g_mutex_lock(vf_mutex);
399 if (ov_open_callbacks(datasource, &vf, NULL, 0, vorbis_callbacks) < 0) {
400 vorbis_callbacks.close_func(datasource);
401 g_mutex_unlock(vf_mutex);
402 vorbis_eos = TRUE;
403 goto play_cleanup;
404 }
405 vi = ov_info(&vf, -1);
406
407 if (vorbis_is_streaming)
408 time = -1;
409 else
410 time = ov_time_total(&vf, -1) * 1000;
411
412 if (vi->channels > 2) {
413 vorbis_eos = TRUE;
414 g_mutex_unlock(vf_mutex);
415 goto play_cleanup;
416 }
417
418 samplerate = vi->rate;
419 channels = vi->channels;
420
421 title = vorbis_generate_title(&vf, filename);
422 use_rg = vorbis_update_replaygain(&rg_scale);
423 br = ov_bitrate(&vf, -1);
424
425 g_mutex_unlock(vf_mutex);
426
427 vorbis_ip.set_info(title, time, br, samplerate, channels);
428 if (!vorbis_ip.output->open_audio(FMT_S16_NE, vi->rate, vi->channels)) {
429 output_error = TRUE;
430 goto play_cleanup;
431 }
432
433 seekneeded = -1;
434
435 /*
436 * Note that chaining changes things here; A vorbis file may
437 * be a mix of different channels, bitrates and sample rates.
438 * You can fetch the information for any section of the file
439 * using the ov_ interface.
440 */
441
442 while (vorbis_playing) {
443 int current_section;
444
445 if (seekneeded != -1)
446 do_seek();
447
448 if (vorbis_eos) {
449 xmms_usleep(20000);
450 continue;
451 }
452
453 current_section = vorbis_process_data(last_section, use_rg, rg_scale);
454
455 if (current_section != last_section) {
456 /*
457 * set total play time, bitrate, rate, and channels of
458 * current section
459 */
460 if (title)
461 g_free(title);
462 g_mutex_lock(vf_mutex);
463 title = vorbis_generate_title(&vf, filename);
464 use_rg = vorbis_update_replaygain(&rg_scale);
465
466 if (vorbis_is_streaming)
467 time = -1;
468 else
469 time = ov_time_total(&vf, -1) * 1000;
470
471 br = ov_bitrate(&vf, current_section);
472
473 g_mutex_unlock(vf_mutex);
474
475 vorbis_ip.set_info(title, time, br, samplerate, channels);
476 timercount = vorbis_ip.output->output_time();
477
478 last_section = current_section;
479 }
480
481 if (!(vi->bitrate_upper == vi->bitrate_lower && vi->bitrate_upper == vi->bitrate_nominal)
482 && (vorbis_ip.output->output_time() > timercount + 1000
483 || vorbis_ip.output->output_time() < timercount)) {
484 /*
485 * simple hack to avoid updating too
486 * often
487 */
488 g_mutex_lock(vf_mutex);
489 br = ov_bitrate_instant(&vf);
490 g_mutex_unlock(vf_mutex);
491 if (br > 0)
492 vorbis_ip.set_info(title, time, br, samplerate, channels);
493 timercount = vorbis_ip.output->output_time();
494 }
495 }
496 if (!output_error)
497 vorbis_ip.output->close_audio();
498 /* fall through intentional */
499
500 play_cleanup:
501 g_free(title);
502 g_free(filename);
503
504 /*
505 * ov_clear closes the stream if its open. Safe to call on an
506 * uninitialized structure as long as we've zeroed it
507 */
508 g_mutex_lock(vf_mutex);
509 ov_clear(&vf);
510 g_mutex_unlock(vf_mutex);
511 vorbis_is_streaming = 0;
512 return NULL;
513 }
514
515 static void
516 vorbis_play(char *filename)
517 {
518 vorbis_playing = 1;
519 vorbis_bytes_streamed = 0;
520 vorbis_eos = 0;
521 output_error = FALSE;
522
523 thread = g_thread_create(vorbis_play_loop, g_strdup(filename), TRUE,
524 NULL);
525 }
526
527 static void
528 vorbis_stop(void)
529 {
530 if (vorbis_playing) {
531 vorbis_playing = 0;
532 g_thread_join(thread);
533 }
534 }
535
536 static void
537 vorbis_pause(short p)
538 {
539 vorbis_ip.output->pause(p);
540 }
541
542 static int
543 vorbis_time(void)
544 {
545 if (output_error)
546 return -2;
547 if (vorbis_eos && !vorbis_ip.output->buffer_playing())
548 return -1;
549 return vorbis_ip.output->output_time();
550 }
551
552 static void
553 vorbis_seek(int time)
554 {
555 if (vorbis_is_streaming)
556 return;
557
558 seekneeded = time;
559
560 while (seekneeded != -1)
561 xmms_usleep(20000);
562 }
563
564 static void
565 vorbis_get_song_info(char *filename, char **title, int *length)
566 {
567 TitleInput *tuple = get_song_tuple(filename);
568
569 *length = tuple->length;
570 *title = xmms_get_titlestring(vorbis_cfg.tag_override ?
571 vorbis_cfg.tag_format :
572 xmms_get_gentitle_format(),
573 tuple);
574
575 bmp_title_input_free(tuple);
576 }
577
578 static const gchar *
579 get_extension(const gchar * filename)
580 {
581 const gchar *ext;
582 if ((ext = strrchr(filename, '.')))
583 ++ext;
584 return ext;
585 }
586
587 /* Make sure you've locked vf_mutex */
588 static gboolean
589 vorbis_update_replaygain(float *scale)
590 {
591 vorbis_comment *comment;
592 char *rg_gain = NULL, *rg_peak_str = NULL;
593 float rg_peak;
594
595 if (!vorbis_cfg.use_replaygain && !vorbis_cfg.use_anticlip)
596 return FALSE;
597 if ((comment = ov_comment(&vf, -1)) == NULL)
598 return FALSE;
599
600 *scale = 1.0;
601
602 if (vorbis_cfg.use_replaygain) {
603 if (vorbis_cfg.replaygain_mode == REPLAYGAIN_MODE_ALBUM) {
604 rg_gain =
605 vorbis_comment_query(comment, "replaygain_album_gain", 0);
606 if (!rg_gain)
607 rg_gain = vorbis_comment_query(comment, "rg_audiophile", 0); /* Old */
608 }
609
610 if (!rg_gain)
611 rg_gain =
612 vorbis_comment_query(comment, "replaygain_track_gain", 0);
613 if (!rg_gain)
614 rg_gain = vorbis_comment_query(comment, "rg_radio", 0); /* Old */
615
616 /* FIXME: Make sure this string is the correct format first? */
617 if (rg_gain)
618 *scale = pow(10., atof(rg_gain) / 20);
619 }
620
621 if (vorbis_cfg.use_anticlip) {
622 if (vorbis_cfg.replaygain_mode == REPLAYGAIN_MODE_ALBUM)
623 rg_peak_str =
624 vorbis_comment_query(comment, "replaygain_album_peak", 0);
625
626 if (!rg_peak_str)
627 rg_peak_str =
628 vorbis_comment_query(comment, "replaygain_track_peak", 0);
629 if (!rg_peak_str)
630 rg_peak_str = vorbis_comment_query(comment, "rg_peak", 0); /* Old */
631
632 if (rg_peak_str)
633 rg_peak = atof(rg_peak_str);
634 else
635 rg_peak = 1;
636
637 if (*scale * rg_peak > 1.0)
638 *scale = 1.0 / rg_peak;
639 }
640
641 if (*scale != 1.0 || vorbis_cfg.use_booster) {
642 /* safety */
643 if (*scale > 15.0)
644 *scale = 15.0;
645
646 return TRUE;
647 }
648
649 return FALSE;
650 }
651
652 #if (G_BYTE_ORDER == G_BIG_ENDIAN)
653 # define GET_BYTE1(val) ((val) >> 8)
654 # define GET_BYTE2(val) ((val) & 0xff)
655 #else
656 # define GET_BYTE1(val) ((val) & 0xff)
657 # define GET_BYTE2(val) ((val) >> 8)
658 #endif
659
660 static long
661 vorbis_process_replaygain(float **pcm, int samples, int ch,
662 char *pcmout, float rg_scale)
663 {
664 int i, j;
665 /* ReplayGain processing */
666 for (i = 0; i < samples; i++)
667 for (j = 0; j < ch; j++) {
668 float sample = pcm[j][i] * rg_scale;
669 int value;
670
671 if (vorbis_cfg.use_booster) {
672 sample *= 2;
673
674 /* hard 6dB limiting */
675 if (sample < -0.5)
676 sample = tanh((sample + 0.5) / 0.5) * 0.5 - 0.5;
677 else if (sample > 0.5)
678 sample = tanh((sample - 0.5) / 0.5) * 0.5 + 0.5;
679 }
680
681 value = sample * 32767;
682 if (value > 32767)
683 value = 32767;
684 else if (value < -32767)
685 value = -32767;
686
687 *pcmout++ = GET_BYTE1(value);
688 *pcmout++ = GET_BYTE2(value);
689 }
690
691 return 2 * ch * samples;
692 }
693
694 /*
695 * Ok, nhjm449! Are you *happy* now?! -nenolod
696 */
697 static TitleInput *
698 get_tuple_for_vorbisfile(OggVorbis_File * vorbisfile, gchar *filename, gboolean is_stream)
699 {
700 TitleInput *tuple = NULL;
701 vorbis_comment *comment;
702
703 tuple = bmp_title_input_new();
704
705 tuple->file_name = g_path_get_basename(filename);
706 tuple->file_ext = get_extension(filename);
707 tuple->file_path = g_path_get_dirname(filename);
708
709 /* Retrieve the length */
710 if (is_stream == FALSE)
711 tuple->length = ov_time_total(vorbisfile, -1) * 1000;
712 else
713 tuple->length = -1;
714
715 if ((comment = ov_comment(vorbisfile, -1))) {
716 tuple->track_name =
717 g_strdup(vorbis_comment_query(comment, "title", 0));
718 tuple->performer =
719 g_strdup(vorbis_comment_query(comment, "artist", 0));
720 tuple->album_name =
721 g_strdup(vorbis_comment_query(comment, "album", 0));
722
723 if (vorbis_comment_query(comment, "tracknumber", 0) != NULL)
724 tuple->track_number =
725 atoi(vorbis_comment_query(comment, "tracknumber", 0));
726
727 tuple->date = g_strdup(vorbis_comment_query(comment, "date", 0));
728 tuple->genre = g_strdup(vorbis_comment_query(comment, "genre", 0));
729 tuple->comment =
730 g_strdup(vorbis_comment_query(comment, "comment", 0));
731 }
732
733 return tuple;
734 }
735
736 static TitleInput *
737 get_song_tuple(gchar *filename)
738 {
739 VFSFile *stream = NULL;
740 OggVorbis_File vfile; /* avoid thread interaction */
741 TitleInput *tuple = NULL;
742 gboolean is_stream = FALSE;
743
744 if (strncasecmp(filename, "http://", 7)) {
745 if ((stream = vfs_fopen(filename, "r")) == NULL)
746 return NULL;
747 }
748 else
749 is_stream = TRUE;
750
751 /*
752 * The open function performs full stream detection and
753 * machine initialization. If it returns zero, the stream
754 * *is* Vorbis and we're fully ready to decode.
755 */
756 if (ov_open_callbacks(stream, &vfile, NULL, 0, vorbis_callbacks) < 0) {
757 if (is_stream == FALSE)
758 vfs_fclose(stream);
759 return NULL;
760 }
761
762 tuple = get_tuple_for_vorbisfile(&vfile, filename, is_stream);
763
764 /*
765 * once the ov_open succeeds, the stream belongs to
766 * vorbisfile.a. ov_clear will fclose it
767 */
768 ov_clear(&vfile);
769
770 return tuple;
771 }
772
773 static gchar *
774 vorbis_generate_title(OggVorbis_File * vorbisfile, gchar * filename)
775 {
776 /* Caller should hold vf_mutex */
777 gchar *displaytitle = NULL;
778 TitleInput *input;
779
780 input = get_tuple_for_vorbisfile(vorbisfile, filename, vorbis_is_streaming);
781
782 if (!(displaytitle = xmms_get_titlestring(vorbis_cfg.tag_override ?
783 vorbis_cfg.tag_format :
784 xmms_get_gentitle_format(),
785 input))) {
786 if (!vorbis_is_streaming)
787 displaytitle = g_strdup(input->file_name);
788 else
789 displaytitle = vorbis_http_get_title(filename);
790 }
791
792 bmp_title_input_free(input);
793
794 return displaytitle;
795 }
796
797 static void
798 vorbis_aboutbox()
799 {
800 static GtkWidget *about_window;
801
802 if (about_window)
803 gdk_window_raise(about_window->window);
804 else
805 {
806 about_window = xmms_show_message(_("About Ogg Vorbis Audio Plugin"),
807 /*
808 * I18N: UTF-8 Translation: "Haavard Kvaalen" ->
809 * "H\303\245vard Kv\303\245len"
810 */
811 _
812 ("Ogg Vorbis Plugin by the Xiph.org Foundation\n\n"
813 "Original code by\n"
814 "Tony Arcieri <bascule@inferno.tusculum.edu>\n"
815 "Contributions from\n"
816 "Chris Montgomery <monty@xiph.org>\n"
817 "Peter Alm <peter@xmms.org>\n"
818 "Michael Smith <msmith@labyrinth.edu.au>\n"
819 "Jack Moffitt <jack@icecast.org>\n"
820 "Jorn Baayen <jorn@nl.linux.org>\n"
821 "Haavard Kvaalen <havardk@xmms.org>\n"
822 "Gian-Carlo Pascutto <gcp@sjeng.org>\n\n"
823 "Visit the Xiph.org Foundation at http://www.xiph.org/\n"),
824 _("Ok"), FALSE, NULL, NULL);
825 g_signal_connect(G_OBJECT(about_window), "destroy",
826 G_CALLBACK(gtk_widget_destroyed), &about_window);
827 }
828 }
829
830
831 static void
832 vorbis_init(void)
833 {
834 ConfigDb *db;
835 gchar *tmp = NULL;
836
837 memset(&vorbis_cfg, 0, sizeof(vorbis_config_t));
838 vorbis_cfg.http_buffer_size = 128;
839 vorbis_cfg.http_prebuffer = 25;
840 vorbis_cfg.proxy_port = 8080;
841 vorbis_cfg.proxy_use_auth = FALSE;
842 vorbis_cfg.proxy_user = NULL;
843 vorbis_cfg.proxy_pass = NULL;
844 vorbis_cfg.tag_override = FALSE;
845 vorbis_cfg.tag_format = NULL;
846 vorbis_cfg.use_anticlip = FALSE;
847 vorbis_cfg.use_replaygain = FALSE;
848 vorbis_cfg.replaygain_mode = REPLAYGAIN_MODE_TRACK;
849 vorbis_cfg.use_booster = FALSE;
850
851 db = bmp_cfg_db_open();
852 bmp_cfg_db_get_int(db, "vorbis", "http_buffer_size",
853 &vorbis_cfg.http_buffer_size);
854 bmp_cfg_db_get_int(db, "vorbis", "http_prebuffer",
855 &vorbis_cfg.http_prebuffer);
856 bmp_cfg_db_get_bool(db, "vorbis", "save_http_stream",
857 &vorbis_cfg.save_http_stream);
858 if (!bmp_cfg_db_get_string(db, "vorbis", "save_http_path",
859 &vorbis_cfg.save_http_path))
860 vorbis_cfg.save_http_path = g_strdup(g_get_home_dir());
861
862 bmp_cfg_db_get_bool(db, "vorbis", "tag_override",
863 &vorbis_cfg.tag_override);
864 if (!bmp_cfg_db_get_string(db, "vorbis", "tag_format",
865 &vorbis_cfg.tag_format))
866 vorbis_cfg.tag_format = g_strdup("%p - %t");
867 bmp_cfg_db_get_bool(db, "vorbis", "use_anticlip",
868 &vorbis_cfg.use_anticlip);
869 bmp_cfg_db_get_bool(db, "vorbis", "use_replaygain",
870 &vorbis_cfg.use_replaygain);
871 bmp_cfg_db_get_int(db, "vorbis", "replaygain_mode",
872 &vorbis_cfg.replaygain_mode);
873 bmp_cfg_db_get_bool(db, "vorbis", "use_booster", &vorbis_cfg.use_booster);
874
875 bmp_cfg_db_get_bool(db, NULL, "use_proxy", &vorbis_cfg.use_proxy);
876 bmp_cfg_db_get_string(db, NULL, "proxy_host", &vorbis_cfg.proxy_host);
877 bmp_cfg_db_get_string(db, NULL, "proxy_port", &tmp);
878
879 if (tmp != NULL)
880 vorbis_cfg.proxy_port = atoi(tmp);
881
882 bmp_cfg_db_get_bool(db, NULL, "proxy_use_auth", &vorbis_cfg.proxy_use_auth);
883 bmp_cfg_db_get_string(db, NULL, "proxy_user", &vorbis_cfg.proxy_user);
884 bmp_cfg_db_get_string(db, NULL, "proxy_pass", &vorbis_cfg.proxy_pass);
885
886 bmp_cfg_db_close(db);
887
888 vf_mutex = g_mutex_new();
889 }
890
891 static void
892 vorbis_cleanup(void)
893 {
894 g_free(vorbis_ip.description);
895 vorbis_ip.description = NULL;
896
897 if (vorbis_cfg.save_http_path) {
898 free(vorbis_cfg.save_http_path);
899 vorbis_cfg.save_http_path = NULL;
900 }
901
902 if (vorbis_cfg.proxy_host) {
903 free(vorbis_cfg.proxy_host);
904 vorbis_cfg.proxy_host = NULL;
905 }
906
907 if (vorbis_cfg.proxy_user) {
908 free(vorbis_cfg.proxy_user);
909 vorbis_cfg.proxy_user = NULL;
910 }
911
912 if (vorbis_cfg.proxy_pass) {
913 free(vorbis_cfg.proxy_pass);
914 vorbis_cfg.proxy_pass = NULL;
915 }
916
917 if (vorbis_cfg.tag_format) {
918 free(vorbis_cfg.tag_format);
919 vorbis_cfg.tag_format = NULL;
920 }
921
922 if (vorbis_cfg.title_encoding) {
923 free(vorbis_cfg.title_encoding);
924 vorbis_cfg.title_encoding = NULL;
925 }
926
927 g_strfreev(vorbis_tag_encoding_list);
928 g_mutex_free(vf_mutex);
929 }
930
931 static size_t
932 ovcb_read(void *ptr, size_t size, size_t nmemb, void *datasource)
933 {
934 size_t tmp;
935
936
937 if (vorbis_is_streaming) {
938 /* this is a stream */
939 tmp = vorbis_http_read(ptr, size * nmemb);
940 vorbis_bytes_streamed += tmp;
941 return tmp;
942 }
943
944 return vfs_fread(ptr, size, nmemb, (VFSFile *) datasource);
945 }
946
947 static int
948 ovcb_seek(void *datasource, int64_t offset, int whence)
949 {
950 if (vorbis_is_streaming) {
951 /* this is a stream */
952 /* streams aren't seekable */
953 return -1;
954 }
955
956 return vfs_fseek((VFSFile *) datasource, offset, whence);
957 }
958
959 static int
960 ovcb_close(void *datasource)
961 {
962 if (vorbis_is_streaming) {
963 /* this is a stream */
964 vorbis_http_close();
965 return 0;
966 }
967
968 return vfs_fclose((VFSFile *) datasource);
969 }
970
971 static long
972 ovcb_tell(void *datasource)
973 {
974 if (vorbis_is_streaming) {
975 /* this is a stream */
976 /* return bytes read */
977 return vorbis_bytes_streamed;
978 }
979
980 return vfs_ftell((VFSFile *) datasource);
981 }