3
* arch-tag: Implementation of MP3 metadata loading
5
* Copyright (C) 2002 Jorn Baayen <jorn@nl.linux.org>
6
* Marco Pesenti Gritti <marco@it.gnome.org>
7
* Bastien Nocera <hadess@hadess.net>
8
* Seth Nickell <snickell@stanford.edu>
10
* This program is free software; you can redistribute it and/or modify
11
* it under the terms of the GNU General Public License as published by
12
* the Free Software Foundation; either version 2 of the License, or
13
* (at your option) any later version.
15
* This program is distributed in the hope that it will be useful,
16
* but WITHOUT ANY WARRANTY; without even the implied warranty of
17
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
* GNU General Public License for more details.
20
* You should have received a copy of the GNU General Public License
21
* along with this program; if not, write to the Free Software
22
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26
#include <libgnomevfs/gnome-vfs.h>
32
#include "rb-string-helpers.h"
34
#include "id3-vfs/id3-vfs.h"
36
#include "monkey-media-stream-info.h"
37
#include "monkey-media-private.h"
39
#include "mp3-stream-info-impl.h"
41
static void MP3_stream_info_impl_class_init (MP3StreamInfoImplClass *klass);
42
static void MP3_stream_info_impl_init (MP3StreamInfoImpl *ma);
43
static void MP3_stream_info_impl_finalize (GObject *object);
44
static void MP3_stream_info_impl_open_stream (MonkeyMediaStreamInfo *info);
45
static gboolean MP3_stream_info_impl_get_bitrate_info (MP3StreamInfoImpl *impl);
46
static gboolean MP3_stream_info_impl_get_value (MonkeyMediaStreamInfo *info,
47
MonkeyMediaStreamInfoField field,
50
static gboolean MP3_stream_info_impl_set_value (MonkeyMediaStreamInfo *info,
51
MonkeyMediaStreamInfoField field,
54
static char *MP3_stream_info_impl_id3_tag_get_utf8 (struct id3_tag *tag,
55
const char *field_name);
56
static int MP3_stream_info_impl_get_n_values (MonkeyMediaStreamInfo *info,
57
MonkeyMediaStreamInfoField field);
69
struct MP3StreamInfoImplPrivate
72
struct id3_vfs_file *file;
73
struct MP3BitrateInfo *info_num;
77
/* libid3tag doesn't define those, and we need them */
78
#define MM_ID3_FRAME_DISC "TPOS"
79
#define MM_ID3_FRAME_LEN "TLEN"
81
static GObjectClass *parent_class = NULL;
84
MP3_stream_info_impl_get_type (void)
86
static GType MP3_stream_info_impl_type = 0;
88
if (MP3_stream_info_impl_type == 0)
90
static const GTypeInfo our_info =
92
sizeof (MP3StreamInfoImplClass),
95
(GClassInitFunc) MP3_stream_info_impl_class_init,
98
sizeof (MP3StreamInfoImpl),
100
(GInstanceInitFunc) MP3_stream_info_impl_init
103
MP3_stream_info_impl_type = g_type_register_static (MONKEY_MEDIA_TYPE_STREAM_INFO,
108
return MP3_stream_info_impl_type;
112
MP3_stream_info_impl_class_init (MP3StreamInfoImplClass *klass)
114
GObjectClass *object_class = G_OBJECT_CLASS (klass);
115
MonkeyMediaStreamInfoClass *info_class = MONKEY_MEDIA_STREAM_INFO_CLASS (klass);
117
parent_class = g_type_class_peek_parent (klass);
119
object_class->finalize = MP3_stream_info_impl_finalize;
121
info_class->open_stream = MP3_stream_info_impl_open_stream;
122
info_class->get_n_values = MP3_stream_info_impl_get_n_values;
123
info_class->get_value = MP3_stream_info_impl_get_value;
124
info_class->set_value = MP3_stream_info_impl_set_value;
128
MP3_stream_info_impl_init (MP3StreamInfoImpl *impl)
130
impl->priv = g_new0 (MP3StreamInfoImplPrivate, 1);
134
MP3_stream_info_impl_finalize (GObject *object)
136
MP3StreamInfoImpl *impl;
138
g_return_if_fail (object != NULL);
139
g_return_if_fail (IS_MP3_STREAM_INFO_IMPL (object));
141
impl = MP3_STREAM_INFO_IMPL (object);
143
g_return_if_fail (impl->priv != NULL);
145
if (impl->priv->file != NULL)
146
id3_vfs_close (impl->priv->file);
148
g_free (impl->priv->info_num);
151
G_OBJECT_CLASS (parent_class)->finalize (object);
155
MP3_stream_info_impl_open_stream (MonkeyMediaStreamInfo *info)
157
MP3StreamInfoImpl *impl = MP3_STREAM_INFO_IMPL (info);
161
g_object_get (G_OBJECT (info),
166
impl->priv->file = id3_vfs_open (uri, ID3_FILE_MODE_READONLY);
168
if (impl->priv->file == NULL)
170
error = g_error_new (MONKEY_MEDIA_STREAM_INFO_ERROR,
171
MONKEY_MEDIA_STREAM_INFO_ERROR_OPEN_FAILED,
172
_("Failed to open file for reading"));
173
g_object_set (G_OBJECT (info), "error", error, NULL);
177
impl->priv->tag = id3_vfs_tag (impl->priv->file);
179
if (MP3_stream_info_impl_get_bitrate_info (impl) == FALSE)
181
error = g_error_new (MONKEY_MEDIA_STREAM_INFO_ERROR,
182
MONKEY_MEDIA_STREAM_INFO_ERROR_OPEN_FAILED,
183
_("Failed to gather information about the file"));
184
g_object_set (G_OBJECT (info), "error", error, NULL);
190
MP3_stream_info_impl_get_length_from_tag (MP3StreamInfoImpl *impl)
192
struct id3_frame const *frame;
194
/* The following is based on information from the
195
* ID3 tag version 2.4.0 Native Frames informal standard.
197
frame = id3_tag_findframe(impl->priv->tag, MM_ID3_FRAME_LEN, 0);
203
union id3_field const *field;
204
unsigned int nstrings;
205
id3_latin1_t *latin1;
207
field = id3_frame_field (frame, 1);
208
nstrings = id3_field_getnstrings (field);
212
latin1 = id3_ucs4_latin1duplicate
213
(id3_field_getstrings(field, 0));
218
/* "The 'Length' frame contains the length of the
219
* audio file in milliseconds, represented as a
222
if (atol(latin1) > 0)
223
/* monkey-media needs a duration in seconds */
224
impl->priv->info_num->time = atol(latin1)/1000;
230
MP3_stream_info_impl_get_bitrate_info (MP3StreamInfoImpl *impl)
232
if (impl->priv->info_num == NULL)
234
struct MP3BitrateInfo *info;
236
info = g_new0 (struct MP3BitrateInfo, 1);
237
if (id3_vfs_bitrate (impl->priv->file,
243
&info->channels) == 0)
245
impl->priv->info_num = info;
249
impl->priv->info_num = info;
251
MP3_stream_info_impl_get_length_from_tag (impl);
257
/* This tries to read and parse a tag whose value is "xx/yy" and returns
258
* the xx part as an int
261
get_current_number_from_tag (MP3StreamInfoImpl *impl, const gchar *tag)
267
tmp = MP3_stream_info_impl_id3_tag_get_utf8 (impl->priv->tag, tag);
274
parts = g_strsplit (tmp, "/", -1);
276
if (parts[0] != NULL)
277
num = atoi (parts[0]);
285
/* This tries to read and parse a tag whose value is "xx/yy" and returns
286
* the yy part as an int
289
get_total_number_from_tag (MP3StreamInfoImpl *impl, const gchar *tag)
295
tmp = MP3_stream_info_impl_id3_tag_get_utf8 (impl->priv->tag, tag);
302
parts = g_strsplit (tmp, "/", -1);
304
if (parts[0] != NULL && parts[1] != NULL)
305
num = atoi (parts[1]);
314
MP3_stream_info_impl_get_n_values (MonkeyMediaStreamInfo *info,
315
MonkeyMediaStreamInfoField field)
317
MP3StreamInfoImpl *impl;
319
gboolean ret = FALSE;
321
g_return_val_if_fail (IS_MP3_STREAM_INFO_IMPL (info), 0);
323
impl = MP3_STREAM_INFO_IMPL (info);
328
case MONKEY_MEDIA_STREAM_INFO_FIELD_TITLE:
329
tmp = MP3_stream_info_impl_id3_tag_get_utf8 (impl->priv->tag, ID3_FRAME_TITLE);
333
case MONKEY_MEDIA_STREAM_INFO_FIELD_ARTIST:
334
tmp = MP3_stream_info_impl_id3_tag_get_utf8 (impl->priv->tag, ID3_FRAME_ARTIST);
338
case MONKEY_MEDIA_STREAM_INFO_FIELD_ALBUM:
339
tmp = MP3_stream_info_impl_id3_tag_get_utf8 (impl->priv->tag, ID3_FRAME_ALBUM);
343
case MONKEY_MEDIA_STREAM_INFO_FIELD_DATE:
344
tmp = MP3_stream_info_impl_id3_tag_get_utf8 (impl->priv->tag, ID3_FRAME_YEAR);
348
case MONKEY_MEDIA_STREAM_INFO_FIELD_GENRE:
349
tmp = MP3_stream_info_impl_id3_tag_get_utf8 (impl->priv->tag, ID3_FRAME_GENRE);
353
case MONKEY_MEDIA_STREAM_INFO_FIELD_COMMENT:
354
tmp = MP3_stream_info_impl_id3_tag_get_utf8 (impl->priv->tag, ID3_FRAME_COMMENT);
358
case MONKEY_MEDIA_STREAM_INFO_FIELD_TRACK_NUMBER:
361
num = get_current_number_from_tag (impl, ID3_FRAME_TRACK);
362
return (num == -1?0:1);
365
case MONKEY_MEDIA_STREAM_INFO_FIELD_MAX_TRACK_NUMBER:
368
num = get_total_number_from_tag (impl, ID3_FRAME_TRACK);
369
return (num == -1?0:1);
372
case MONKEY_MEDIA_STREAM_INFO_FIELD_DISC_NUMBER:
375
num = get_current_number_from_tag (impl, MM_ID3_FRAME_DISC);
376
return (num == -1?0:1);
379
case MONKEY_MEDIA_STREAM_INFO_FIELD_MAX_DISC_NUMBER:
382
num = get_total_number_from_tag (impl, MM_ID3_FRAME_DISC);
383
return (num == -1?0:1);
386
case MONKEY_MEDIA_STREAM_INFO_FIELD_LOCATION:
387
case MONKEY_MEDIA_STREAM_INFO_FIELD_DESCRIPTION:
388
case MONKEY_MEDIA_STREAM_INFO_FIELD_VERSION:
389
case MONKEY_MEDIA_STREAM_INFO_FIELD_ISRC:
390
case MONKEY_MEDIA_STREAM_INFO_FIELD_ORGANIZATION:
391
case MONKEY_MEDIA_STREAM_INFO_FIELD_COPYRIGHT:
392
case MONKEY_MEDIA_STREAM_INFO_FIELD_CONTACT:
393
case MONKEY_MEDIA_STREAM_INFO_FIELD_LICENSE:
394
case MONKEY_MEDIA_STREAM_INFO_FIELD_PERFORMER:
398
case MONKEY_MEDIA_STREAM_INFO_FIELD_FILE_SIZE:
399
case MONKEY_MEDIA_STREAM_INFO_FIELD_DURATION:
403
case MONKEY_MEDIA_STREAM_INFO_FIELD_HAS_AUDIO:
404
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_CODEC_INFO:
405
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_BIT_RATE:
406
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_AVERAGE_BIT_RATE:
407
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_VARIABLE_BIT_RATE:
408
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_QUALITY:
409
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_SAMPLE_RATE:
410
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_CHANNELS:
412
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_SERIAL_NUMBER:
413
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_VENDOR:
414
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_ALBUM_GAIN:
415
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_TRACK_GAIN:
416
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_ALBUM_PEAK:
417
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_TRACK_PEAK:
418
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_TRM_ID:
422
case MONKEY_MEDIA_STREAM_INFO_FIELD_HAS_VIDEO:
432
MP3_stream_info_impl_get_value (MonkeyMediaStreamInfo *info,
433
MonkeyMediaStreamInfoField field,
437
MP3StreamInfoImpl *impl;
440
g_return_val_if_fail (IS_MP3_STREAM_INFO_IMPL (info), FALSE);
441
g_return_val_if_fail (value != NULL, FALSE);
443
impl = MP3_STREAM_INFO_IMPL (info);
445
if (MP3_stream_info_impl_get_n_values (info, field) <= 0)
451
case MONKEY_MEDIA_STREAM_INFO_FIELD_TITLE:
452
g_value_init (value, G_TYPE_STRING);
453
tmp = MP3_stream_info_impl_id3_tag_get_utf8 (impl->priv->tag, ID3_FRAME_TITLE);
454
g_value_set_string (value, tmp);
457
case MONKEY_MEDIA_STREAM_INFO_FIELD_ARTIST:
458
g_value_init (value, G_TYPE_STRING);
459
tmp = MP3_stream_info_impl_id3_tag_get_utf8 (impl->priv->tag, ID3_FRAME_ARTIST);
460
g_value_set_string (value, tmp);
463
case MONKEY_MEDIA_STREAM_INFO_FIELD_ALBUM:
464
g_value_init (value, G_TYPE_STRING);
465
tmp = MP3_stream_info_impl_id3_tag_get_utf8 (impl->priv->tag, ID3_FRAME_ALBUM);
466
g_value_set_string (value, tmp);
469
case MONKEY_MEDIA_STREAM_INFO_FIELD_DATE:
470
g_value_init (value, G_TYPE_STRING);
471
tmp = MP3_stream_info_impl_id3_tag_get_utf8 (impl->priv->tag, ID3_FRAME_YEAR);
472
g_value_set_string (value, tmp);
475
case MONKEY_MEDIA_STREAM_INFO_FIELD_GENRE:
476
g_value_init (value, G_TYPE_STRING);
477
tmp = MP3_stream_info_impl_id3_tag_get_utf8 (impl->priv->tag, ID3_FRAME_GENRE);
478
g_value_set_string (value, tmp);
481
case MONKEY_MEDIA_STREAM_INFO_FIELD_COMMENT:
482
g_value_init (value, G_TYPE_STRING);
483
tmp = MP3_stream_info_impl_id3_tag_get_utf8 (impl->priv->tag, ID3_FRAME_COMMENT);
484
g_value_set_string (value, tmp);
487
case MONKEY_MEDIA_STREAM_INFO_FIELD_TRACK_NUMBER:
490
num = get_current_number_from_tag (impl, ID3_FRAME_TRACK);
491
g_value_init (value, G_TYPE_INT);
492
g_value_set_int (value, num);
495
case MONKEY_MEDIA_STREAM_INFO_FIELD_MAX_TRACK_NUMBER:
498
num = get_total_number_from_tag (impl, ID3_FRAME_TRACK);
499
g_value_init (value, G_TYPE_INT);
500
g_value_set_int (value, num);
503
case MONKEY_MEDIA_STREAM_INFO_FIELD_DISC_NUMBER:
506
num = get_current_number_from_tag (impl, MM_ID3_FRAME_DISC);
507
g_value_init (value, G_TYPE_INT);
508
g_value_set_int (value, num);
511
case MONKEY_MEDIA_STREAM_INFO_FIELD_MAX_DISC_NUMBER:
514
num = get_total_number_from_tag (impl, MM_ID3_FRAME_DISC);
515
g_value_init (value, G_TYPE_INT);
516
g_value_set_int (value, num);
520
case MONKEY_MEDIA_STREAM_INFO_FIELD_LOCATION:
521
case MONKEY_MEDIA_STREAM_INFO_FIELD_DESCRIPTION:
522
case MONKEY_MEDIA_STREAM_INFO_FIELD_VERSION:
523
case MONKEY_MEDIA_STREAM_INFO_FIELD_ISRC:
524
case MONKEY_MEDIA_STREAM_INFO_FIELD_ORGANIZATION:
525
case MONKEY_MEDIA_STREAM_INFO_FIELD_COPYRIGHT:
526
case MONKEY_MEDIA_STREAM_INFO_FIELD_CONTACT:
527
case MONKEY_MEDIA_STREAM_INFO_FIELD_LICENSE:
528
case MONKEY_MEDIA_STREAM_INFO_FIELD_PERFORMER:
529
g_value_init (value, G_TYPE_STRING);
530
g_value_set_string (value, "");
534
case MONKEY_MEDIA_STREAM_INFO_FIELD_FILE_SIZE:
540
g_object_get (G_OBJECT (info), "location", &uri, NULL);
542
g_value_init (value, G_TYPE_LONG);
544
i = gnome_vfs_file_info_new ();
545
res = gnome_vfs_get_file_info (uri, i,
546
GNOME_VFS_FILE_INFO_FOLLOW_LINKS);
547
if (res == GNOME_VFS_OK)
548
g_value_set_long (value, i->size);
550
g_value_set_long (value, 0);
552
gnome_vfs_file_info_unref (i);
555
case MONKEY_MEDIA_STREAM_INFO_FIELD_DURATION:
556
g_value_init (value, G_TYPE_LONG);
558
if (impl->priv->info_num->vbr == 0)
560
GnomeVFSFileSize size;
563
MP3_stream_info_impl_get_value (info,
564
MONKEY_MEDIA_STREAM_INFO_FIELD_FILE_SIZE,
567
size = g_value_get_long (&val);
568
g_value_unset (&val);
570
if (impl->priv->info_num->bitrate > 0)
571
g_value_set_long (value, ((double) size / 1000.0f) / ((double) impl->priv->info_num->bitrate / 8.0f / 1000.0f));
573
g_value_set_long (value, 0);
575
g_value_set_long (value, impl->priv->info_num->time);
580
case MONKEY_MEDIA_STREAM_INFO_FIELD_HAS_AUDIO:
581
g_value_init (value, G_TYPE_BOOLEAN);
582
g_value_set_boolean (value, TRUE);
584
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_CODEC_INFO:
585
g_value_init (value, G_TYPE_STRING);
586
if (impl->priv->info_num->version != 3)
588
tmp = g_strdup_printf (_("MPEG %d Layer III"), impl->priv->info_num->version);
590
tmp = g_strdup (_("MPEG 2.5 Layer III"));
592
g_value_set_string (value, tmp);
595
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_BIT_RATE:
596
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_AVERAGE_BIT_RATE:
597
g_value_init (value, G_TYPE_INT);
598
g_value_set_int (value, impl->priv->info_num->bitrate / 1000);
600
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_TRM_ID:
602
g_value_init (value, G_TYPE_STRING);
603
g_value_set_string (value, NULL);
605
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_VARIABLE_BIT_RATE:
606
g_value_init (value, G_TYPE_BOOLEAN);
607
g_value_set_boolean (value, impl->priv->info_num->vbr);
609
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_SAMPLE_RATE:
610
g_value_init (value, G_TYPE_LONG);
611
g_value_set_long (value, impl->priv->info_num->samplerate);
613
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_CHANNELS:
614
g_value_init (value, G_TYPE_INT);
615
g_value_set_int (value, impl->priv->info_num->channels);
617
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_SERIAL_NUMBER:
618
g_value_init (value, G_TYPE_LONG);
619
g_value_set_long (value, 0);
621
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_VENDOR:
622
g_value_init (value, G_TYPE_STRING);
623
g_value_set_string (value, "");
625
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_ALBUM_GAIN:
626
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_TRACK_GAIN:
627
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_ALBUM_PEAK:
628
case MONKEY_MEDIA_STREAM_INFO_FIELD_AUDIO_TRACK_PEAK:
629
g_value_init (value, G_TYPE_DOUBLE);
630
g_value_set_double (value, 0.0);
634
case MONKEY_MEDIA_STREAM_INFO_FIELD_HAS_VIDEO:
635
g_value_init (value, G_TYPE_BOOLEAN);
636
g_value_set_boolean (value, FALSE);
641
g_warning ("Invalid field!");
642
g_value_init (value, G_TYPE_NONE);
650
MP3_stream_info_impl_set_value (MonkeyMediaStreamInfo *info,
651
MonkeyMediaStreamInfoField field,
660
MP3_stream_info_impl_id3_tag_get_utf8 (struct id3_tag *tag, const char *field_name)
662
unsigned int nstrings, j;
663
const struct id3_frame *frame;
664
const union id3_field *field;
665
const id3_ucs4_t *ucs4 = NULL;
666
id3_utf8_t *utf8 = NULL;
668
frame = id3_tag_findframe (tag, field_name, 0);
672
field = id3_frame_field(frame, 1);
673
nstrings = id3_field_getnstrings (field);
674
for (j = 0; j < nstrings; j++)
676
id3_utf8_t *tmp = NULL;
678
ucs4 = id3_field_getstrings (field, j);
680
if (strcmp (field_name, ID3_FRAME_GENRE) == 0)
681
ucs4 = id3_genre_name (ucs4);
683
tmp = id3_ucs4_utf8duplicate (ucs4);
684
if (strcmp (tmp, "") != 0)
690
if (utf8 && !g_utf8_validate (utf8, -1, NULL)) {
691
g_warning ("Invalid UTF-8 in %s field in mp3 file\n", field_name);
692
/* Should rb_unicodify really be used to convert ucs4 data to utf8? */
693
utf8 = rb_unicodify ((char *) ucs4);