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