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 }