Mercurial > audlegacy-plugins
comparison src/madplug_x/plugin.c @ 2356:c5fa65cb26ca
make an experimental copy to update to new sound engine
author | Yoshiki Yazawa <yaz@cc.rim.or.jp> |
---|---|
date | Tue, 05 Feb 2008 01:11:50 +0900 |
parents | src/madplug/plugin.c@fd8271f07747 |
children | 19b670117a04 |
comparison
equal
deleted
inserted
replaced
2355:0962a6325b9b | 2356:c5fa65cb26ca |
---|---|
1 /* | |
2 * mad plugin for audacious | |
3 * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa, Eugene Zagidullin | |
4 * | |
5 * Portions derived from xmms-mad: | |
6 * Copyright (C) 2001-2002 Sam Clegg - See COPYING | |
7 * | |
8 * This program is free software; you can redistribute it and/or modify | |
9 * it under the terms of the GNU General Public License as published by | |
10 * the Free Software Foundation; under version 2 of the License. | |
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 02111-1307 USA | |
20 */ | |
21 | |
22 /* #define AUD_DEBUG 1 */ | |
23 | |
24 #include "config.h" | |
25 #include "plugin.h" | |
26 #include "input.h" | |
27 | |
28 #include <math.h> | |
29 | |
30 #include <gtk/gtk.h> | |
31 #include <audacious/util.h> | |
32 #include <audacious/configdb.h> | |
33 #include <stdarg.h> | |
34 #include <fcntl.h> | |
35 #include <audacious/vfs.h> | |
36 #include <sys/stat.h> | |
37 #include "SFMT.h" | |
38 #include "tuple.h" | |
39 | |
40 /* | |
41 * Global variables | |
42 */ | |
43 audmad_config_t *audmad_config; /**< global configuration */ | |
44 GMutex *mad_mutex; | |
45 GMutex *pb_mutex; | |
46 GCond *mad_cond; | |
47 | |
48 /* | |
49 * static variables | |
50 */ | |
51 static GThread *decode_thread; /**< the single decoder thread */ | |
52 static struct mad_info_t info; /**< info for current track */ | |
53 | |
54 #ifndef NOGUI | |
55 static GtkWidget *error_dialog = 0; | |
56 #endif | |
57 | |
58 extern gboolean scan_file(struct mad_info_t *info, gboolean fast); | |
59 | |
60 static gint mp3_bitrate_table[5][16] = { | |
61 { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1 }, /* MPEG1 L1 */ | |
62 { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1 }, /* MPEG1 L2 */ | |
63 { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1 }, /* MPEG1 L3 */ | |
64 { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, -1 }, /* MPEG2(.5) L1 */ | |
65 { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 } /* MPEG2(.5) L2,L3 */ | |
66 }; | |
67 | |
68 static gint mp3_samplerate_table[4][4] = { | |
69 { 11025, 12000, 8000, -1 }, /* MPEG2.5 */ | |
70 { -1, -1, -1, -1 }, /* Reserved */ | |
71 { 22050, 24000, 16000, -1 }, /* MPEG2 */ | |
72 { 44100, 48000, 32000, -1 } /* MPEG1 */ | |
73 }; | |
74 | |
75 /* | |
76 * Function extname (filename) | |
77 * | |
78 * Return pointer within filename to its extenstion, or NULL if | |
79 * filename has no extension. | |
80 * | |
81 */ | |
82 static gchar * | |
83 extname(const char *filename) | |
84 { | |
85 gchar *ext = strrchr(filename, '.'); | |
86 | |
87 if (ext != NULL) | |
88 ++ext; | |
89 | |
90 return ext; | |
91 } | |
92 | |
93 | |
94 void | |
95 audmad_config_compute(audmad_config_t *config) | |
96 { | |
97 /* set some config parameters by parsing text fields | |
98 (RG default gain, etc..) | |
99 */ | |
100 const gchar *text; | |
101 gdouble x; | |
102 | |
103 text = config->replaygain.preamp0_db; | |
104 if ( text != NULL ) | |
105 x = g_strtod(text, NULL); | |
106 else | |
107 x = 0; | |
108 config->replaygain.preamp0_scale = (x != 0) ? pow(10.0, x / 20) : 1; | |
109 AUDDBG("RG.preamp0=[%s] -> %g -> %g\n", text, x, config->preamp0_scale); | |
110 | |
111 text = config->replaygain.preamp1_db; | |
112 if ( text != NULL ) | |
113 x = g_strtod(text, NULL); | |
114 else | |
115 x = 0; | |
116 config->replaygain.preamp1_scale = (x != 0) ? pow(10.0, x / 20) : 1; | |
117 AUDDBG("RG.preamp1=[%s] -> %g -> %g\n", text, x, | |
118 config->replaygain.preamp1_scale); | |
119 | |
120 text = config->replaygain.preamp2_db; | |
121 if ( text != NULL ) | |
122 x = g_strtod(text, NULL); | |
123 else | |
124 x = 0; | |
125 config->replaygain.preamp2_scale = (x != 0) ? pow(10.0, x / 20) : 1; | |
126 AUDDBG("RG.preamp2=[%s] -> %g -> %g\n", text, x, | |
127 config->replaygain.preamp2_scale); | |
128 } | |
129 | |
130 static void | |
131 audmad_init() | |
132 { | |
133 ConfigDb *db = NULL; | |
134 | |
135 audmad_config = g_malloc0(sizeof(audmad_config_t)); | |
136 | |
137 audmad_config->dither = TRUE; | |
138 audmad_config->force_reopen_audio = FALSE; | |
139 audmad_config->fast_play_time_calc = TRUE; | |
140 audmad_config->use_xing = TRUE; | |
141 audmad_config->sjis = FALSE; | |
142 audmad_config->show_avg_vbr_bitrate = TRUE; | |
143 audmad_config->replaygain.enable = TRUE; | |
144 audmad_config->replaygain.track_mode = FALSE; | |
145 audmad_config->replaygain.anti_clip = FALSE; | |
146 audmad_config->replaygain.adaptive_scaler = FALSE; | |
147 audmad_config->title_override = FALSE; | |
148 | |
149 | |
150 db = aud_cfg_db_open(); | |
151 if (db) { | |
152 //audio | |
153 aud_cfg_db_get_bool(db, "MAD", "dither", &audmad_config->dither); | |
154 aud_cfg_db_get_bool(db, "MAD", "force_reopen_audio", | |
155 &audmad_config->force_reopen_audio); | |
156 | |
157 //metadata | |
158 aud_cfg_db_get_bool(db, "MAD", "fast_play_time_calc", | |
159 &audmad_config->fast_play_time_calc); | |
160 aud_cfg_db_get_bool(db, "MAD", "use_xing", | |
161 &audmad_config->use_xing); | |
162 aud_cfg_db_get_bool(db, "MAD", "sjis", &audmad_config->sjis); | |
163 | |
164 //misc | |
165 aud_cfg_db_get_bool(db, "MAD", "show_avg_vbr_bitrate", | |
166 &audmad_config->show_avg_vbr_bitrate); | |
167 | |
168 //gain control | |
169 aud_cfg_db_get_string(db, "MAD", "RG.preamp0_db", | |
170 &audmad_config->replaygain.preamp0_db); | |
171 aud_cfg_db_get_bool(db, "MAD", "RG.enable", | |
172 &audmad_config->replaygain.enable); | |
173 aud_cfg_db_get_bool(db, "MAD", "RG.track_mode", | |
174 &audmad_config->replaygain.track_mode); | |
175 aud_cfg_db_get_string(db, "MAD", "RG.preamp1_db", | |
176 &audmad_config->replaygain.preamp1_db); | |
177 aud_cfg_db_get_string(db, "MAD", "RG.preamp2_db", | |
178 &audmad_config->replaygain.preamp2_db); | |
179 aud_cfg_db_get_bool(db, "MAD", "RG.anti_clip", | |
180 &audmad_config->replaygain.anti_clip); | |
181 aud_cfg_db_get_bool(db, "MAD", "RG.adaptive_scaler", | |
182 &audmad_config->replaygain.adaptive_scaler); | |
183 | |
184 //text | |
185 aud_cfg_db_get_bool(db, "MAD", "title_override", | |
186 &audmad_config->title_override); | |
187 aud_cfg_db_get_string(db, "MAD", "id3_format", | |
188 &audmad_config->id3_format); | |
189 | |
190 aud_cfg_db_close(db); | |
191 } | |
192 | |
193 mad_mutex = g_mutex_new(); | |
194 pb_mutex = g_mutex_new(); | |
195 mad_cond = g_cond_new(); | |
196 audmad_config_compute(audmad_config); | |
197 | |
198 if (!audmad_config->replaygain.preamp0_db) | |
199 audmad_config->replaygain.preamp0_db = g_strdup("+0.00"); | |
200 | |
201 if (!audmad_config->replaygain.preamp1_db) | |
202 audmad_config->replaygain.preamp1_db = g_strdup("+6.00"); | |
203 if (!audmad_config->replaygain.preamp2_db) | |
204 audmad_config->replaygain.preamp2_db = g_strdup("+0.00"); | |
205 | |
206 if (!audmad_config->id3_format) | |
207 audmad_config->id3_format = g_strdup("(none)"); | |
208 | |
209 init_gen_rand(4357); | |
210 | |
211 aud_mime_set_plugin("audio/mpeg", mad_plugin); | |
212 } | |
213 | |
214 static void | |
215 audmad_cleanup() | |
216 { | |
217 g_free(audmad_config->replaygain.preamp0_db); | |
218 g_free(audmad_config->replaygain.preamp1_db); | |
219 g_free(audmad_config->replaygain.preamp2_db); | |
220 g_free(audmad_config->id3_format); | |
221 g_free(audmad_config); | |
222 | |
223 g_cond_free(mad_cond); | |
224 g_mutex_free(mad_mutex); | |
225 g_mutex_free(pb_mutex); | |
226 } | |
227 | |
228 static gboolean | |
229 mp3_head_check(guint32 head, gint *frameSize) | |
230 { | |
231 gint version, layer, bitIndex, bitRate, sampleIndex, sampleRate, padding; | |
232 | |
233 /* http://www.mp3-tech.org/programmer/frame_header.html | |
234 * Bits 21-31 must be set (frame sync) | |
235 */ | |
236 if ((head & 0xffe00000) != 0xffe00000) | |
237 return FALSE; | |
238 | |
239 /* check if layer bits (17-18) are good */ | |
240 layer = (head >> 17) & 0x3; | |
241 if (!layer) | |
242 return FALSE; /* 00 = reserved */ | |
243 layer = 4 - layer; | |
244 | |
245 /* check if bitrate index bits (12-15) are acceptable */ | |
246 bitIndex = (head >> 12) & 0xf; | |
247 | |
248 /* 1111 and 0000 are reserved values for all layers */ | |
249 if (bitIndex == 0xf || bitIndex == 0) | |
250 return FALSE; | |
251 | |
252 /* check samplerate index bits (10-11) */ | |
253 sampleIndex = (head >> 10) & 0x3; | |
254 if (sampleIndex == 0x3) | |
255 return FALSE; | |
256 | |
257 /* check version bits (19-20) and get bitRate */ | |
258 version = (head >> 19) & 0x03; | |
259 switch (version) { | |
260 case 0: /* 00 = MPEG Version 2.5 */ | |
261 case 2: /* 10 = MPEG Version 2 */ | |
262 if (layer == 1) | |
263 bitRate = mp3_bitrate_table[3][bitIndex]; | |
264 else | |
265 bitRate = mp3_bitrate_table[4][bitIndex]; | |
266 break; | |
267 | |
268 case 1: /* 01 = reserved */ | |
269 return FALSE; | |
270 | |
271 case 3: /* 11 = MPEG Version 1 */ | |
272 bitRate = mp3_bitrate_table[layer][bitIndex]; | |
273 break; | |
274 | |
275 default: | |
276 return FALSE; | |
277 } | |
278 | |
279 /* check layer II restrictions vs. bitrate */ | |
280 if (layer == 2) { | |
281 gint chanMode = (head >> 6) & 0x3; | |
282 | |
283 if (chanMode == 0x3) { | |
284 /* single channel with bitrate > 192 */ | |
285 if (bitRate > 192) | |
286 return FALSE; | |
287 } else { | |
288 /* any other mode with bitrates 32-56 and 80. | |
289 * NOTICE! this check is not entirely correct, but I think | |
290 * it is sufficient in most cases. | |
291 */ | |
292 if (((bitRate >= 32 && bitRate <= 56) || bitRate == 80)) | |
293 return FALSE; | |
294 } | |
295 } | |
296 | |
297 /* calculate approx. frame size */ | |
298 padding = (head >> 9) & 1; | |
299 sampleRate = mp3_samplerate_table[version][sampleIndex]; | |
300 if (layer == 1) | |
301 *frameSize = ((12 * bitRate * 1000 / sampleRate) + padding) * 4; | |
302 else | |
303 *frameSize = (144 * bitRate * 1000) / (sampleRate + padding); | |
304 | |
305 /* check if bits 16 - 19 are all set (MPEG 1 Layer I, not protected?) */ | |
306 if (((head >> 19) & 1) == 1 && | |
307 ((head >> 17) & 3) == 3 && ((head >> 16) & 1) == 1) | |
308 return FALSE; | |
309 | |
310 /* not sure why we check this, but ok! */ | |
311 if ((head & 0xffff0000) == 0xfffe0000) | |
312 return FALSE; | |
313 | |
314 return TRUE; | |
315 } | |
316 | |
317 static int | |
318 mp3_head_convert(const guchar * hbuf) | |
319 { | |
320 return ((unsigned long) hbuf[0] << 24) | | |
321 ((unsigned long) hbuf[1] << 16) | | |
322 ((unsigned long) hbuf[2] << 8) | (unsigned long) hbuf[3]; | |
323 } | |
324 | |
325 // audacious vfs fast version | |
326 static int | |
327 audmad_is_our_fd(char *filename, VFSFile *fin) | |
328 { | |
329 guint32 check; | |
330 gchar *ext = extname(filename); | |
331 gint cyc = 0, chkcount = 0, chksize = 4096; | |
332 guchar buf[4]; | |
333 guchar tmp[4096]; | |
334 gint ret, i, frameSize; | |
335 | |
336 info.remote = aud_vfs_is_remote(filename); | |
337 | |
338 /* I've seen some flac files beginning with id3 frames.. | |
339 so let's exclude known non-mp3 filename extensions */ | |
340 if ((ext != NULL) && | |
341 (!strcasecmp("flac", ext) || !strcasecmp("mpc", ext) || | |
342 !strcasecmp("tta", ext) || !strcasecmp("ogg", ext) || | |
343 !strcasecmp("wma", ext) ) | |
344 ) | |
345 return 0; | |
346 | |
347 if (fin == NULL) { | |
348 g_message("fin = NULL"); | |
349 return 0; | |
350 } | |
351 | |
352 if(aud_vfs_fread(buf, 1, 4, fin) == 0) { | |
353 gchar *tmp = g_filename_to_utf8(filename, -1, NULL, NULL, NULL); | |
354 g_message("aud_vfs_fread failed @1 %s", tmp); | |
355 g_free(tmp); | |
356 return 0; | |
357 } | |
358 | |
359 check = mp3_head_convert(buf); | |
360 | |
361 if (memcmp(buf, "ID3", 3) == 0) | |
362 return 1; | |
363 else if (memcmp(buf, "OggS", 4) == 0) | |
364 return 0; | |
365 else if (memcmp(buf, "RIFF", 4) == 0) | |
366 { | |
367 aud_vfs_fseek(fin, 4, SEEK_CUR); | |
368 if(aud_vfs_fread(buf, 1, 4, fin) == 0) { | |
369 gchar *tmp = g_filename_to_utf8(filename, -1, NULL, NULL, NULL); | |
370 g_message("aud_vfs_fread failed @2 %s", tmp); | |
371 g_free(tmp); | |
372 return 0; | |
373 } | |
374 | |
375 if (memcmp(buf, "RMP3", 4) == 0) | |
376 return 1; | |
377 } | |
378 | |
379 // check data for frame header | |
380 while (!mp3_head_check(check, &frameSize)) | |
381 { | |
382 if((ret = aud_vfs_fread(tmp, 1, chksize, fin)) == 0){ | |
383 gchar *tmp = g_filename_to_utf8(filename, -1, NULL, NULL, NULL); | |
384 g_message("aud_vfs_fread failed @3 %s", tmp); | |
385 g_free(tmp); | |
386 return 0; | |
387 } | |
388 for (i = 0; i < ret; i++) | |
389 { | |
390 check <<= 8; | |
391 check |= tmp[i]; | |
392 | |
393 if (mp3_head_check(check, &frameSize)) { | |
394 /* when the first matching frame header is found, we check for | |
395 * another frame by seeking to the approximate start of the | |
396 * next header ... also reduce the check size. | |
397 */ | |
398 if (++chkcount >= 3) return 1; | |
399 aud_vfs_fseek(fin, frameSize-4, SEEK_CUR); | |
400 check = 0; | |
401 chksize = 8; | |
402 } | |
403 } | |
404 | |
405 if (++cyc > 32) | |
406 return 0; | |
407 } | |
408 | |
409 return 1; | |
410 } | |
411 | |
412 // audacious vfs version | |
413 static int | |
414 audmad_is_our_file(char *filename) | |
415 { | |
416 VFSFile *fin = NULL; | |
417 gint rtn; | |
418 | |
419 fin = aud_vfs_fopen(filename, "rb"); | |
420 | |
421 if (fin == NULL) | |
422 return 0; | |
423 | |
424 rtn = audmad_is_our_fd(filename, fin); | |
425 aud_vfs_fclose(fin); | |
426 | |
427 return rtn; | |
428 } | |
429 | |
430 static void | |
431 audmad_stop(InputPlayback *playback) | |
432 { | |
433 AUDDBG("f: audmad_stop\n"); | |
434 g_mutex_lock(mad_mutex); | |
435 info.playback = playback; | |
436 g_mutex_unlock(mad_mutex); | |
437 | |
438 if (decode_thread) { | |
439 | |
440 g_mutex_lock(mad_mutex); | |
441 info.playback->playing = 0; | |
442 g_mutex_unlock(mad_mutex); | |
443 g_cond_signal(mad_cond); | |
444 | |
445 AUDDBG("waiting for thread\n"); | |
446 g_thread_join(decode_thread); | |
447 AUDDBG("thread done\n"); | |
448 | |
449 input_term(&info); | |
450 decode_thread = NULL; | |
451 | |
452 } | |
453 AUDDBG("e: audmad_stop\n"); | |
454 } | |
455 | |
456 static void | |
457 audmad_play_file(InputPlayback *playback) | |
458 { | |
459 gboolean rtn; | |
460 gchar *url = playback->filename; | |
461 | |
462 #ifdef AUD_DEBUG | |
463 { | |
464 gchar *tmp = g_filename_to_utf8(url, -1, NULL, NULL, NULL); | |
465 AUDDBG("playing %s\n", tmp); | |
466 g_free(tmp); | |
467 } | |
468 #endif /* DEBUG */ | |
469 | |
470 if (input_init(&info, url, NULL) == FALSE) { | |
471 g_message("error initialising input"); | |
472 return; | |
473 } | |
474 | |
475 // remote access must use fast scan. | |
476 rtn = input_get_info(&info, aud_vfs_is_remote(url) ? TRUE : audmad_config->fast_play_time_calc); | |
477 | |
478 if (rtn == FALSE) { | |
479 g_message("error reading input info"); | |
480 /* | |
481 * return; | |
482 * commenting this return seems to be a hacky fix for the damn lastfm plugin playback | |
483 * that used to work only for nenolod because of his fsck-ing lastfm subscription :p | |
484 */ | |
485 } | |
486 g_mutex_lock(pb_mutex); | |
487 info.playback = playback; | |
488 info.playback->playing = 1; | |
489 g_mutex_unlock(pb_mutex); | |
490 | |
491 decode_thread = g_thread_self(); | |
492 playback->set_pb_ready(playback); | |
493 decode_loop(&info); | |
494 } | |
495 | |
496 static void | |
497 audmad_pause(InputPlayback *playback, short paused) | |
498 { | |
499 g_mutex_lock(pb_mutex); | |
500 info.playback = playback; | |
501 g_mutex_unlock(pb_mutex); | |
502 playback->output->pause(paused); | |
503 } | |
504 | |
505 static void | |
506 audmad_mseek(InputPlayback *playback, gulong millisecond) | |
507 { | |
508 g_mutex_lock(pb_mutex); | |
509 info.playback = playback; | |
510 info.seek = millisecond; | |
511 g_mutex_unlock(pb_mutex); | |
512 } | |
513 | |
514 static void | |
515 audmad_seek(InputPlayback *playback, gint time) | |
516 { | |
517 audmad_mseek(playback, time * 1000); | |
518 } | |
519 | |
520 /** | |
521 * Scan the given file or URL. | |
522 * Fills in the title string and the track length in milliseconds. | |
523 */ | |
524 static void | |
525 audmad_get_song_info(char *url, char **title, int *length) | |
526 { | |
527 struct mad_info_t myinfo; | |
528 #ifdef AUD_DEBUG | |
529 gchar *tmp = g_filename_to_utf8(url, -1, NULL, NULL, NULL); | |
530 AUDDBG("f: audmad_get_song_info: %s\n", tmp); | |
531 g_free(tmp); | |
532 #endif /* DEBUG */ | |
533 | |
534 if (input_init(&myinfo, url, NULL) == FALSE) { | |
535 AUDDBG("error initialising input\n"); | |
536 return; | |
537 } | |
538 | |
539 if (input_get_info(&myinfo, info.remote ? TRUE : audmad_config->fast_play_time_calc) == TRUE) { | |
540 if(aud_tuple_get_string(myinfo.tuple, -1, "track-name")) | |
541 *title = g_strdup(aud_tuple_get_string(myinfo.tuple, -1, "track-name")); | |
542 else | |
543 *title = g_strdup(url); | |
544 | |
545 *length = aud_tuple_get_int(myinfo.tuple, FIELD_LENGTH, NULL); | |
546 if(*length == -1) | |
547 *length = mad_timer_count(myinfo.duration, MAD_UNITS_MILLISECONDS); | |
548 } | |
549 else { | |
550 *title = g_strdup(url); | |
551 *length = -1; | |
552 } | |
553 input_term(&myinfo); | |
554 AUDDBG("e: audmad_get_song_info\n"); | |
555 } | |
556 | |
557 static gboolean | |
558 audmad_fill_info(struct mad_info_t *info, VFSFile *fd) | |
559 { | |
560 if (fd == NULL || info == NULL) return FALSE; | |
561 AUDDBG("f: audmad_fill_info(): %s\n", fd->uri); | |
562 | |
563 if (input_init(info, fd->uri, fd) == FALSE) { | |
564 AUDDBG("audmad_fill_info(): error initialising input\n"); | |
565 return FALSE; | |
566 } | |
567 | |
568 info->fileinfo_request = FALSE; /* we don't need to read tuple again */ | |
569 return input_get_info(info, aud_vfs_is_remote(fd->uri) ? TRUE : audmad_config->fast_play_time_calc); | |
570 } | |
571 | |
572 static void | |
573 audmad_about() | |
574 { | |
575 static GtkWidget *aboutbox; | |
576 gchar *scratch; | |
577 | |
578 if (aboutbox != NULL) | |
579 return; | |
580 | |
581 scratch = g_strdup_printf( | |
582 _("Audacious MPEG Audio Plugin\n" | |
583 "\n" | |
584 "Compiled against libMAD version: %d.%d.%d%s\n" | |
585 "\n" | |
586 "Written by:\n" | |
587 " William Pitcock <nenolod@sacredspiral.co.uk>\n" | |
588 " Yoshiki Yazawa <yaz@cc.rim.or.jp>\n" | |
589 "\n" | |
590 "Portions derived from XMMS-MAD by:\n" | |
591 " Sam Clegg\n" | |
592 "\n" | |
593 "ReplayGain support by:\n" | |
594 " Samuel Krempp"), | |
595 MAD_VERSION_MAJOR, MAD_VERSION_MINOR, MAD_VERSION_PATCH, | |
596 MAD_VERSION_EXTRA); | |
597 | |
598 aboutbox = audacious_info_dialog(_("About MPEG Audio Plugin"), | |
599 scratch, | |
600 _("Ok"), FALSE, NULL, NULL); | |
601 | |
602 g_free(scratch); | |
603 | |
604 g_signal_connect(G_OBJECT(aboutbox), "destroy", | |
605 G_CALLBACK(gtk_widget_destroyed), &aboutbox); | |
606 } | |
607 | |
608 /** | |
609 * Display a GTK box containing the given error message. | |
610 * Taken from mpg123 plugin. | |
611 */ | |
612 void | |
613 audmad_error(char *error, ...) | |
614 { | |
615 #ifndef NOGUI | |
616 if (!error_dialog) { | |
617 va_list args; | |
618 char string[256]; | |
619 va_start(args, error); | |
620 vsnprintf(string, 256, error, args); | |
621 va_end(args); | |
622 GDK_THREADS_ENTER(); | |
623 error_dialog = | |
624 audacious_info_dialog(_("Error"), string, _("Ok"), FALSE, 0, 0); | |
625 gtk_signal_connect(GTK_OBJECT(error_dialog), "destroy", | |
626 GTK_SIGNAL_FUNC(gtk_widget_destroyed), | |
627 &error_dialog); | |
628 GDK_THREADS_LEAVE(); | |
629 } | |
630 #endif /* !NOGUI */ | |
631 } | |
632 | |
633 extern void audmad_configure(); | |
634 | |
635 static void | |
636 __set_and_free(Tuple *tuple, gint nfield, gchar *name, gchar *value) | |
637 { | |
638 aud_tuple_associate_string(tuple, nfield, name, value); | |
639 g_free(value); | |
640 } | |
641 | |
642 // tuple stuff | |
643 static Tuple * | |
644 __audmad_get_song_tuple(char *filename, VFSFile *fd) | |
645 { | |
646 Tuple *tuple = NULL; | |
647 gchar *string = NULL; | |
648 | |
649 struct id3_file *id3file = NULL; | |
650 struct id3_tag *tag = NULL; | |
651 | |
652 struct mad_info_t myinfo; | |
653 | |
654 gboolean local_fd = FALSE; | |
655 int length; | |
656 | |
657 #ifdef AUD_DEBUG | |
658 string = aud_str_to_utf8(filename); | |
659 AUDDBG("f: mad: audmad_get_song_tuple: %s\n", string); | |
660 g_free(string); | |
661 string = NULL; | |
662 #endif | |
663 | |
664 /* isn't is obfuscated? --eugene */ | |
665 | |
666 if(info.remote && mad_timer_count(info.duration, MAD_UNITS_SECONDS) <= 0){ | |
667 if((fd && aud_vfs_is_streaming(fd)) || (info.playback && info.playback->playing)) { | |
668 gchar *tmp = NULL; | |
669 tuple = aud_tuple_new_from_filename(filename); | |
670 | |
671 #ifdef AUD_DEBUG | |
672 if(info.playback) | |
673 AUDDBG("info.playback->playing = %d\n",info.playback->playing); | |
674 #endif | |
675 tmp = aud_vfs_get_metadata(info.infile ? info.infile : fd, "track-name"); | |
676 if(tmp){ | |
677 gchar *scratch; | |
678 | |
679 scratch = aud_str_to_utf8(tmp); | |
680 aud_tuple_associate_string(tuple, FIELD_TITLE, NULL, scratch); | |
681 g_free(tmp); | |
682 g_free(scratch); | |
683 | |
684 tmp = NULL; | |
685 } | |
686 tmp = aud_vfs_get_metadata(info.infile ? info.infile : fd, "stream-name"); | |
687 if(tmp){ | |
688 gchar *scratch; | |
689 | |
690 scratch = aud_str_to_utf8(tmp); | |
691 aud_tuple_associate_string(tuple, FIELD_TITLE, NULL, scratch); | |
692 g_free(tmp); | |
693 g_free(scratch); | |
694 | |
695 tmp = NULL; | |
696 } | |
697 | |
698 AUDDBG("audmad_get_song_tuple: track_name = %s\n", aud_tuple_get_string(tuple, -1, "track-name")); | |
699 AUDDBG("audmad_get_song_tuple: stream_name = %s\n", aud_tuple_get_string(tuple, -1, "stream-name")); | |
700 aud_tuple_associate_int(tuple, FIELD_LENGTH, NULL, -1); | |
701 aud_tuple_associate_int(tuple, FIELD_MTIME, NULL, 0); // this indicates streaming | |
702 AUDDBG("get_song_tuple: remote: tuple\n"); | |
703 return tuple; | |
704 } | |
705 AUDDBG("get_song_tuple: remote: NULL\n"); | |
706 } /* info.remote */ | |
707 | |
708 // if !fd, pre-open the file with aud_vfs_fopen() and reuse fd. | |
709 if(!fd) { | |
710 fd = aud_vfs_fopen(filename, "rb"); | |
711 if(!fd) | |
712 return NULL; | |
713 local_fd = TRUE; | |
714 } | |
715 | |
716 if (!audmad_fill_info(&myinfo, fd)) { | |
717 AUDDBG("get_song_tuple: error obtaining info\n"); | |
718 if (local_fd) aud_vfs_fclose(fd); | |
719 return NULL; | |
720 } | |
721 | |
722 tuple = aud_tuple_new(); | |
723 aud_tuple_associate_int(tuple, FIELD_LENGTH, NULL, -1); | |
724 | |
725 id3file = id3_file_vfsopen(fd, ID3_FILE_MODE_READONLY); | |
726 | |
727 if (id3file) { | |
728 | |
729 tag = id3_file_tag(id3file); | |
730 if (tag) { | |
731 __set_and_free(tuple, FIELD_ARTIST, NULL, input_id3_get_string(tag, ID3_FRAME_ARTIST)); | |
732 __set_and_free(tuple, FIELD_ALBUM, NULL, input_id3_get_string(tag, ID3_FRAME_ALBUM)); | |
733 __set_and_free(tuple, FIELD_TITLE, NULL, input_id3_get_string(tag, ID3_FRAME_TITLE)); | |
734 | |
735 // year | |
736 string = NULL; | |
737 string = input_id3_get_string(tag, ID3_FRAME_YEAR); //TDRC | |
738 if (!string) | |
739 string = input_id3_get_string(tag, "TYER"); | |
740 | |
741 if (string) { | |
742 aud_tuple_associate_int(tuple, FIELD_YEAR, NULL, atoi(string)); | |
743 g_free(string); | |
744 string = NULL; | |
745 } | |
746 | |
747 __set_and_free(tuple, FIELD_FILE_NAME, NULL, aud_uri_to_display_basename(filename)); | |
748 __set_and_free(tuple, FIELD_FILE_PATH, NULL, aud_uri_to_display_dirname(filename)); | |
749 aud_tuple_associate_string(tuple, FIELD_FILE_EXT, NULL, extname(filename)); | |
750 | |
751 // length | |
752 length = mad_timer_count(myinfo.duration, MAD_UNITS_MILLISECONDS); | |
753 aud_tuple_associate_int(tuple, FIELD_LENGTH, NULL, length); | |
754 | |
755 // track number | |
756 string = input_id3_get_string(tag, ID3_FRAME_TRACK); | |
757 if (string) { | |
758 aud_tuple_associate_int(tuple, FIELD_TRACK_NUMBER, NULL, atoi(string)); | |
759 g_free(string); | |
760 string = NULL; | |
761 } | |
762 // genre | |
763 __set_and_free(tuple, FIELD_GENRE, NULL, input_id3_get_string(tag, ID3_FRAME_GENRE)); | |
764 __set_and_free(tuple, FIELD_COMMENT, NULL, input_id3_get_string(tag, ID3_FRAME_COMMENT)); | |
765 AUDDBG("genre = %s\n", aud_tuple_get_string(tuple, FIELD_GENRE, NULL)); | |
766 } | |
767 id3_file_close(id3file); | |
768 } // id3file | |
769 else { // no id3tag | |
770 __set_and_free(tuple, FIELD_FILE_NAME, NULL, aud_uri_to_display_basename(filename)); | |
771 __set_and_free(tuple, FIELD_FILE_PATH, NULL, aud_uri_to_display_dirname(filename)); | |
772 aud_tuple_associate_string(tuple, FIELD_FILE_EXT, NULL, extname(filename)); | |
773 // length | |
774 length = mad_timer_count(myinfo.duration, MAD_UNITS_MILLISECONDS); | |
775 aud_tuple_associate_int(tuple, FIELD_LENGTH, NULL, length); | |
776 } | |
777 | |
778 aud_tuple_associate_string(tuple, FIELD_QUALITY, NULL, "lossy"); | |
779 aud_tuple_associate_int(tuple, FIELD_BITRATE, NULL, myinfo.bitrate / 1000); | |
780 | |
781 string = g_strdup_printf("MPEG-1 Audio Layer %d", myinfo.mpeg_layer); | |
782 aud_tuple_associate_string(tuple, FIELD_CODEC, NULL, string); | |
783 g_free(string); | |
784 | |
785 aud_tuple_associate_string(tuple, FIELD_MIMETYPE, NULL, "audio/mpeg"); | |
786 | |
787 input_term(&myinfo); | |
788 | |
789 if(local_fd) | |
790 aud_vfs_fclose(fd); | |
791 | |
792 AUDDBG("e: mad: audmad_get_song_tuple\n"); | |
793 return tuple; | |
794 } | |
795 | |
796 static Tuple * | |
797 audmad_get_song_tuple(char *filename) | |
798 { | |
799 return __audmad_get_song_tuple(filename, NULL); | |
800 } | |
801 | |
802 static Tuple * | |
803 audmad_probe_for_tuple(char *filename, VFSFile *fd) | |
804 { | |
805 if (!audmad_is_our_fd(filename, fd)) | |
806 return NULL; | |
807 | |
808 aud_vfs_rewind(fd); | |
809 | |
810 return __audmad_get_song_tuple(filename, fd); | |
811 } | |
812 | |
813 static gchar *fmts[] = { "mp3", "mp2", "mpg", "bmu", NULL }; | |
814 | |
815 InputPlugin mad_ip = { | |
816 .description = "MPEG Audio Plugin", | |
817 .init = audmad_init, | |
818 .about = audmad_about, | |
819 .configure = audmad_configure, | |
820 .is_our_file = audmad_is_our_file, | |
821 .play_file = audmad_play_file, | |
822 .stop = audmad_stop, | |
823 .pause = audmad_pause, | |
824 .seek = audmad_seek, | |
825 .cleanup = audmad_cleanup, | |
826 .get_song_info = audmad_get_song_info, | |
827 .get_song_tuple = audmad_get_song_tuple, | |
828 .is_our_file_from_vfs = audmad_is_our_fd, | |
829 .vfs_extensions = fmts, | |
830 .mseek = audmad_mseek, | |
831 .probe_for_tuple = audmad_probe_for_tuple, | |
832 .update_song_tuple = audmad_update_song_tuple, | |
833 }; | |
834 | |
835 InputPlugin *madplug_iplist[] = { &mad_ip, NULL }; | |
836 | |
837 SIMPLE_INPUT_PLUGIN(madplug, madplug_iplist); | |
838 | |
839 InputPlugin *mad_plugin = &mad_ip; |