Mercurial > audlegacy-plugins
comparison src/madplug/input.c @ 610:862190d39e00 trunk
[svn] - add madplug. It is not yet hooked up, I'll do that later.
author | nenolod |
---|---|
date | Mon, 05 Feb 2007 12:28:01 -0800 |
parents | |
children | 3f7a52adfe0e |
comparison
equal
deleted
inserted
replaced
609:9b73eb35f4ff | 610:862190d39e00 |
---|---|
1 /* | |
2 * mad plugin for audacious | |
3 * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa | |
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 #include "config.h" | |
23 | |
24 #ifdef HAVE_ASSERT_H | |
25 #include <assert.h> | |
26 #endif /* HAVE_ASSERT_H */ | |
27 | |
28 #ifdef HAVE_SYS_TYPES_H | |
29 #include <sys/types.h> | |
30 #endif /* HAVE_SYS_TYPES_H */ | |
31 | |
32 #ifdef HAVE_SYS_SOCKET_H | |
33 #include <sys/socket.h> | |
34 #endif /* HAVE_SYS_SOCKET_H */ | |
35 | |
36 #ifdef HAVE_NETINET_IN_H | |
37 #include <netinet/in.h> | |
38 #endif /* HAVE_NETINET_IN_H */ | |
39 | |
40 #ifdef HAVE_ARPA_INET_H | |
41 #include <arpa/inet.h> | |
42 #endif /* HAVE_ARPA_INET_H */ | |
43 | |
44 #ifdef HAVE_NETDB_H | |
45 #include <netdb.h> | |
46 #endif /* HAVE_NETDB_H */ | |
47 | |
48 #ifdef HAVE_SYS_STAT_H | |
49 #include <sys/stat.h> | |
50 #endif /* HAVE_SYS_STAT_H */ | |
51 | |
52 #ifdef HAVE_SYS_TIME_H | |
53 #include <sys/time.h> | |
54 #endif /* HAVE_SYS_TIME_H */ | |
55 | |
56 #include <fcntl.h> | |
57 #include <errno.h> | |
58 #include <audacious/util.h> | |
59 | |
60 #include "input.h" | |
61 #include "replaygain.h" | |
62 | |
63 #define DIR_SEPARATOR '/' | |
64 #define HEADER_SIZE 256 | |
65 #define LINE_LENGTH 256 | |
66 | |
67 extern gboolean scan_file(struct mad_info_t *info, gboolean fast); | |
68 | |
69 /** | |
70 * init the mad_info_t struct. | |
71 */ | |
72 gboolean input_init(struct mad_info_t * info, const char *url) | |
73 { | |
74 #ifdef DEBUG | |
75 g_message("f: input_init"); | |
76 #endif | |
77 memset(info, 0, sizeof(struct mad_info_t)); | |
78 | |
79 info->fmt = FMT_S16_LE; | |
80 info->channels = -1; | |
81 info->mpeg_layer = -1; | |
82 info->size = -1; | |
83 info->freq = -1; | |
84 info->seek = -1; | |
85 info->duration = mad_timer_zero; | |
86 info->pos = mad_timer_zero; | |
87 info->url = g_strdup(url); | |
88 info->current_frame = 0; | |
89 info->frames = 0; | |
90 info->bitrate = 0; | |
91 info->vbr = 0; | |
92 info->mode = 0; | |
93 info->title = 0; | |
94 info->offset = 0; | |
95 | |
96 info->replaygain_album_str = 0; | |
97 info->replaygain_track_str = 0; | |
98 info->replaygain_album_peak_str = 0; | |
99 info->replaygain_track_peak_str = 0; | |
100 info->mp3gain_undo_str = 0; | |
101 info->mp3gain_minmax_str = 0; | |
102 | |
103 info->tuple = NULL; | |
104 info->filename = g_strdup(url); | |
105 | |
106 info->infile = vfs_fopen(info->filename, "rb"); | |
107 if (info->infile == NULL) | |
108 return FALSE; | |
109 | |
110 // obtain file size | |
111 vfs_fseek(info->infile, 0, SEEK_END); | |
112 info->size = vfs_ftell(info->infile); | |
113 vfs_fseek(info->infile, 0, SEEK_SET); | |
114 info->remote = info->size == 0 ? TRUE : FALSE; | |
115 | |
116 #ifdef DEBUG | |
117 g_message("i: info->size == %lu", info->size); | |
118 g_message("e: input_init"); | |
119 #endif | |
120 return TRUE; | |
121 } | |
122 | |
123 /* return length in letters */ | |
124 size_t mad_ucs4len(id3_ucs4_t *ucs) | |
125 { | |
126 id3_ucs4_t *ptr = ucs; | |
127 size_t len = 0; | |
128 | |
129 while(*ptr++ != 0) | |
130 len++; | |
131 | |
132 return len; | |
133 } | |
134 | |
135 /* duplicate id3_ucs4_t string. new string will be terminated with 0. */ | |
136 id3_ucs4_t *mad_ucs4dup(id3_ucs4_t *org) | |
137 { | |
138 id3_ucs4_t *new = NULL; | |
139 size_t len = mad_ucs4len(org); | |
140 | |
141 new = g_malloc0((len + 1) * sizeof(id3_ucs4_t)); | |
142 memcpy(new, org, len * sizeof(id3_ucs4_t)); | |
143 *(new + len) = 0; //terminate | |
144 | |
145 return new; | |
146 } | |
147 | |
148 #define BYTES(x) ((x) * sizeof(id3_ucs4_t)) | |
149 | |
150 id3_ucs4_t *mad_parse_genre(const id3_ucs4_t *string) | |
151 { | |
152 id3_ucs4_t *ret = NULL; | |
153 id3_ucs4_t *tmp = NULL; | |
154 id3_ucs4_t *genre = NULL; | |
155 id3_ucs4_t *ptr, *end, *tail, *tp; | |
156 size_t ret_len = 0; //num of ucs4 char! | |
157 size_t tmp_len = 0; | |
158 gboolean is_num = TRUE; | |
159 | |
160 tail = (id3_ucs4_t *)string + mad_ucs4len((id3_ucs4_t *)string); | |
161 | |
162 ret = g_malloc0(1024); // realloc() is too picky | |
163 | |
164 for(ptr = (id3_ucs4_t *)string; *ptr != 0 && ptr <= tail; ptr++) { | |
165 if(*ptr == '(') { | |
166 if(*(++ptr) == '(') { // escaped text like: ((something) | |
167 for(end = ptr; *end != ')' && *end != 0;) { // copy "(something)" | |
168 end++; | |
169 } | |
170 end++; //include trailing ')' | |
171 // ret = g_realloc(ret, BYTES(end - ptr + 1)); | |
172 memcpy(ret, ptr, BYTES(end - ptr)); | |
173 ret_len += (end - ptr); | |
174 *(ret + ret_len) = 0; //terminate | |
175 ptr = end + 1; | |
176 } | |
177 else { | |
178 // reference to an id3v1 genre code | |
179 for(end = ptr; *end != ')' && *end != 0;) { | |
180 end++; | |
181 } | |
182 | |
183 tmp = g_malloc0(BYTES(end - ptr + 1)); | |
184 memcpy(tmp, ptr, BYTES(end - ptr)); | |
185 *(tmp + (end - ptr)) = 0; //terminate | |
186 ptr += end - ptr; | |
187 | |
188 genre = (id3_ucs4_t *)id3_genre_name((const id3_ucs4_t *)tmp); | |
189 | |
190 g_free(tmp); | |
191 tmp = NULL; | |
192 | |
193 tmp_len = mad_ucs4len(genre); | |
194 | |
195 // ret = g_realloc(ret, BYTES(ret_len + tmp_len + 1)); | |
196 memcpy(ret + BYTES(ret_len), genre, BYTES(tmp_len)); | |
197 | |
198 ret_len += tmp_len; | |
199 *(ret + ret_len) = 0; //terminate | |
200 } | |
201 } | |
202 else { | |
203 for(end = ptr; *end != '(' && *end != 0; ) { | |
204 end++; | |
205 } | |
206 // scan string to determine whether a genre code number or not | |
207 tp = ptr; | |
208 is_num = TRUE; | |
209 while(tp < end) { | |
210 if(*tp < '0' || *tp > '9') { // anything else than number appears. | |
211 is_num = FALSE; | |
212 break; | |
213 } | |
214 tp++; | |
215 } | |
216 if(is_num) { | |
217 #ifdef DEBUG | |
218 printf("is_num!\n"); | |
219 #endif | |
220 tmp = g_malloc0(BYTES(end - ptr + 1)); | |
221 memcpy(tmp, ptr, BYTES(end - ptr)); | |
222 *(tmp + (end - ptr)) = 0; //terminate | |
223 ptr += end - ptr; | |
224 | |
225 genre = (id3_ucs4_t *)id3_genre_name((const id3_ucs4_t *)tmp); | |
226 #ifdef DEBUG | |
227 printf("genre length = %d\n", mad_ucs4len(genre)); | |
228 #endif | |
229 g_free(tmp); | |
230 tmp = NULL; | |
231 | |
232 tmp_len = mad_ucs4len(genre); | |
233 | |
234 // ret = g_realloc(ret, BYTES(ret_len + tmp_len + 1)); | |
235 memcpy(ret + BYTES(ret_len), genre, BYTES(tmp_len)); | |
236 | |
237 ret_len += tmp_len; | |
238 *(ret + ret_len) = 0; //terminate | |
239 } | |
240 else { // plain text | |
241 // ret = g_realloc(ret, BYTES(end - ptr + 1)); | |
242 #ifdef DEBUG | |
243 printf("plain!\n"); | |
244 printf("ret_len = %d\n", ret_len); | |
245 #endif | |
246 memcpy(ret + BYTES(ret_len), ptr, BYTES(end - ptr)); | |
247 ret_len = ret_len + (end - ptr); | |
248 *(ret + ret_len) = 0; //terminate | |
249 ptr += (end - ptr); | |
250 } | |
251 } | |
252 } | |
253 return ret; | |
254 } | |
255 | |
256 | |
257 gchar *input_id3_get_string(struct id3_tag * tag, char *frame_name) | |
258 { | |
259 gchar *rtn; | |
260 const id3_ucs4_t *string_const; | |
261 id3_ucs4_t *string; | |
262 struct id3_frame *frame; | |
263 union id3_field *field; | |
264 | |
265 frame = id3_tag_findframe(tag, frame_name, 0); | |
266 if (!frame) | |
267 return NULL; | |
268 | |
269 if (frame_name == ID3_FRAME_COMMENT) | |
270 field = id3_frame_field(frame, 3); | |
271 else | |
272 field = id3_frame_field(frame, 1); | |
273 | |
274 if (!field) | |
275 return NULL; | |
276 | |
277 if (frame_name == ID3_FRAME_COMMENT) | |
278 string_const = id3_field_getfullstring(field); | |
279 else | |
280 string_const = id3_field_getstrings(field, 0); | |
281 | |
282 if (!string_const) | |
283 return NULL; | |
284 | |
285 string = mad_ucs4dup((id3_ucs4_t *)string_const); | |
286 | |
287 if (frame_name == ID3_FRAME_GENRE) { | |
288 id3_ucs4_t *string2 = NULL; | |
289 string2 = mad_parse_genre(string); | |
290 g_free((void *)string); | |
291 string = string2; | |
292 } | |
293 | |
294 { | |
295 id3_utf8_t *string2 = id3_ucs4_utf8duplicate(string); | |
296 rtn = str_to_utf8(string2); | |
297 g_free(string2); | |
298 } | |
299 | |
300 #ifdef DEBUG | |
301 g_print("i: string = %s\n", rtn); | |
302 #endif | |
303 return rtn; | |
304 } | |
305 | |
306 /** | |
307 * read the ID3 tag | |
308 */ | |
309 static void input_read_tag(struct mad_info_t *info) | |
310 { | |
311 gchar *string = NULL; | |
312 TitleInput *title_input; | |
313 | |
314 if (info->tuple == NULL) { | |
315 title_input = bmp_title_input_new(); | |
316 info->tuple = title_input; | |
317 } | |
318 else | |
319 title_input = info->tuple; | |
320 | |
321 info->id3file = id3_file_open(info->filename, ID3_FILE_MODE_READONLY); | |
322 if (!info->id3file) { | |
323 return; | |
324 } | |
325 | |
326 info->tag = id3_file_tag(info->id3file); | |
327 if (!info->tag) { | |
328 return; | |
329 } | |
330 | |
331 title_input->performer = | |
332 input_id3_get_string(info->tag, ID3_FRAME_ARTIST); | |
333 title_input->track_name = | |
334 input_id3_get_string(info->tag, ID3_FRAME_TITLE); | |
335 title_input->album_name = | |
336 input_id3_get_string(info->tag, ID3_FRAME_ALBUM); | |
337 title_input->genre = input_id3_get_string(info->tag, ID3_FRAME_GENRE); | |
338 title_input->comment = | |
339 input_id3_get_string(info->tag, ID3_FRAME_COMMENT); | |
340 string = input_id3_get_string(info->tag, ID3_FRAME_TRACK); | |
341 if (string) { | |
342 title_input->track_number = atoi(string); | |
343 g_free(string); | |
344 string = NULL; | |
345 } | |
346 // year | |
347 string = NULL; | |
348 string = input_id3_get_string(info->tag, ID3_FRAME_YEAR); //TDRC | |
349 if (!string) | |
350 string = input_id3_get_string(info->tag, "TYER"); | |
351 | |
352 if (string) { | |
353 title_input->year = atoi(string); | |
354 g_free(string); | |
355 string = NULL; | |
356 } | |
357 | |
358 title_input->file_name = g_strdup(g_basename(info->filename)); | |
359 title_input->file_path = g_path_get_dirname(info->filename); | |
360 if ((string = strrchr(title_input->file_name, '.'))) { | |
361 title_input->file_ext = string + 1; | |
362 *string = '\0'; // make filename end at dot. | |
363 } | |
364 | |
365 info->title = xmms_get_titlestring(audmad_config.title_override == TRUE ? | |
366 audmad_config.id3_format : xmms_get_gentitle_format(), title_input); | |
367 } | |
368 | |
369 /** | |
370 * Retrieve meta-information about URL. | |
371 * For local files this means ID3 tag etc. | |
372 */ | |
373 gboolean input_get_info(struct mad_info_t *info, gboolean fast_scan) | |
374 { | |
375 #ifdef DEBUG | |
376 g_message("f: input_get_info: %s", info->title); | |
377 #endif /* DEBUG */ | |
378 input_read_tag(info); | |
379 input_read_replaygain(info); | |
380 | |
381 /* scan mp3 file, decoding headers unless fast_scan is set */ | |
382 if (scan_file(info, fast_scan) == FALSE) | |
383 return FALSE; | |
384 | |
385 /* reset the input file to the start */ | |
386 vfs_rewind(info->infile); | |
387 info->offset = 0; | |
388 | |
389 if (info->remote) | |
390 { | |
391 gchar *stream_name = vfs_get_metadata(info->infile, "stream-name"); | |
392 gchar *track_name = vfs_get_metadata(info->infile, "track-name"); | |
393 gchar *tmp = NULL; | |
394 | |
395 g_free(info->title); | |
396 g_free(info->tuple->track_name); | |
397 g_free(info->tuple->album_name); | |
398 | |
399 info->title = g_strdup(track_name); | |
400 info->tuple->track_name = g_strdup(track_name); | |
401 info->tuple->album_name = g_strdup(stream_name); | |
402 tmp = g_strdup_printf("%s (%s)", track_name, stream_name); | |
403 mad_plugin->set_info(tmp, | |
404 -1, // indicates the stream is unseekable | |
405 info->bitrate, info->freq, info->channels); | |
406 g_free(tmp); g_free(stream_name); g_free(track_name); | |
407 } | |
408 | |
409 /* use the filename for the title as a last resort */ | |
410 if (!info->title) | |
411 { | |
412 char *pos = strrchr(info->filename, DIR_SEPARATOR); | |
413 if (pos) | |
414 info->title = g_strdup(pos + 1); | |
415 else | |
416 info->title = g_strdup(info->filename); | |
417 } | |
418 | |
419 #ifdef DEBUG | |
420 g_message("e: input_get_info"); | |
421 #endif /* DEBUG */ | |
422 return TRUE; | |
423 } | |
424 | |
425 | |
426 /** | |
427 * Read data from the source given my madinfo into the buffer | |
428 * provided. Return the number of bytes read. | |
429 * @return 0 on EOF | |
430 * @return -1 on error | |
431 */ | |
432 int | |
433 input_get_data(struct mad_info_t *madinfo, guchar * buffer, | |
434 int buffer_size) | |
435 { | |
436 int len = 0; | |
437 #ifdef DEBUG | |
438 // g_message ("f: input_get_data: %d", buffer_size); | |
439 #endif | |
440 | |
441 /* simply read to data from the file */ | |
442 len = vfs_fread(buffer, 1, buffer_size, madinfo->infile); | |
443 | |
444 if (len == 0 && madinfo->playback) | |
445 madinfo->playback->eof = TRUE; | |
446 | |
447 if (madinfo->remote) | |
448 { | |
449 gchar *stream_name = vfs_get_metadata(madinfo->infile, "stream-name"); | |
450 gchar *track_name = vfs_get_metadata(madinfo->infile, "track-name"); | |
451 gchar *tmp = NULL; | |
452 | |
453 g_free(madinfo->title); | |
454 g_free(madinfo->tuple->track_name); | |
455 g_free(madinfo->tuple->album_name); | |
456 | |
457 madinfo->title = g_strdup(track_name); | |
458 madinfo->tuple->track_name = g_strdup(track_name); | |
459 madinfo->tuple->album_name = g_strdup(stream_name); | |
460 tmp = g_strdup_printf("%s (%s)", track_name, stream_name); | |
461 mad_plugin->set_info(tmp, | |
462 -1, // indicates the stream is unseekable | |
463 madinfo->bitrate, madinfo->freq, madinfo->channels); | |
464 g_free(tmp); g_free(stream_name); g_free(track_name); | |
465 } | |
466 | |
467 #ifdef DEBUG | |
468 // g_message ("e: input_get_data: size=%d offset=%d", len, madinfo->offset); | |
469 #endif | |
470 madinfo->offset += len; | |
471 return len; | |
472 } | |
473 | |
474 /** | |
475 * Free up all mad_info_t related resourses. | |
476 */ | |
477 gboolean input_term(struct mad_info_t * info) | |
478 { | |
479 #ifdef DEBUG | |
480 g_message("f: input_term"); | |
481 #endif | |
482 | |
483 if (info->title) | |
484 g_free(info->title); | |
485 if (info->url) | |
486 g_free(info->url); | |
487 if (info->filename) | |
488 g_free(info->filename); | |
489 if (info->infile) | |
490 vfs_fclose(info->infile); | |
491 if (info->id3file) | |
492 id3_file_close(info->id3file); | |
493 | |
494 if (info->replaygain_album_str) | |
495 g_free(info->replaygain_album_str); | |
496 if (info->replaygain_track_str) | |
497 g_free(info->replaygain_track_str); | |
498 if (info->replaygain_album_peak_str) | |
499 g_free(info->replaygain_album_peak_str); | |
500 if (info->replaygain_track_peak_str) | |
501 g_free(info->replaygain_track_peak_str); | |
502 if (info->mp3gain_undo_str) | |
503 g_free(info->mp3gain_undo_str); | |
504 if (info->mp3gain_minmax_str) | |
505 g_free(info->mp3gain_minmax_str); | |
506 | |
507 if (info->tuple) { | |
508 bmp_title_input_free(info->tuple); | |
509 info->tuple = NULL; | |
510 } | |
511 | |
512 /* set everything to zero in case it gets used again. */ | |
513 memset(info, 0, sizeof(struct mad_info_t)); | |
514 #ifdef DEBUG | |
515 g_message("e: input_term"); | |
516 #endif | |
517 return TRUE; | |
518 } |