Search
j0ke.net Open Build Service
>
Projects
>
multimedia
:
SL11
>
xmms
> xmms-1.2.11-id3v2edit.diff
Sign Up
|
Log In
Username
Password
Cancel
Overview
Repositories
Revisions
Requests
Users
Advanced
Attributes
Meta
File xmms-1.2.11-id3v2edit.diff of Package xmms
--- Input/mpg123/common.c +++ Input/mpg123/common.c @@ -288,8 +288,8 @@ mpg123_id3v2_destroy(tag); id3_close(id3d); } - g_free(id3buf); + g_free(id3buf); return TRUE; } --- Input/mpg123/fileinfo.c +++ Input/mpg123/fileinfo.c @@ -27,42 +27,96 @@ #include <gdk/gdkkeysyms.h> #include "mpg123.h" +#define MAX_STR_LEN 100 +#define MAX_ENTRY_LEN2 1023 + static GtkWidget *window = NULL; -static GtkWidget *filename_entry, *id3_frame; -static GtkWidget *title_entry, *artist_entry, *album_entry, *year_entry; -static GtkWidget *tracknum_entry, *comment_entry, *genre_combo; +static GtkWidget *notebook = NULL; +static GtkWidget *filename_entry, *id3v1_frame, *id3v2_frame; +static GtkWidget *v1_checkbox, *v2_checkbox; +static GtkWidget *v1_title_entry, *v1_artist_entry, *v1_album_entry, *v1_year_entry, *v1_tracknum_entry, *v1_comment_entry; +static GtkWidget *v2_title_entry, *v2_artist_entry, *v2_album_entry, *v2_year_entry, *v2_tracknum_entry, *v2_comment_entry, + *v2_composer_entry, *v2_orig_artist_entry, *v2_url_entry, *v2_encoded_by_entry; +static GtkWidget *v1_genre_combo, *v2_genre_combo; static GtkWidget *mpeg_level, *mpeg_bitrate, *mpeg_samplerate, *mpeg_flags; static GtkWidget *mpeg_fileinfo; +static GPtrArray *v1_labels_list = NULL, *v2_labels_list = NULL; // TODO: Where will these be freed? static GList *genre_list; struct genre_item { const char *name; int id; }; -static int current_genre; +static int v1_current_genre; +static int v2_current_genre; static char *current_filename; extern char *mpg123_filename; extern int mpg123_bitrate, mpg123_frequency, mpg123_layer, mpg123_lsf, mpg123_mode; extern gboolean mpg123_stereo, mpg123_mpeg25; -#define MAX_STR_LEN 100 - -static void label_set_text(GtkWidget * label, char *str, ...) +static void label_set_text(GtkWidget * label, const char *str, ...) G_GNUC_PRINTF(2, 3); -static void set_entry_tag(GtkEntry * entry, char * tag, int length) +static void set_entry_tag_v1(GtkEntry * entry, const char * tag, int length) { char *text = g_strchomp(g_strndup(tag, length)); gtk_entry_set_text(entry, text); g_free(text); } -static void get_entry_tag(GtkEntry * entry, char * tag, int length) +static void get_entry_tag_v1(GtkEntry * entry, char * tag, int length) { strncpy(tag, gtk_entry_get_text(entry), length); } +void copy_entry_tag_v1(GtkEntry * src, GtkEntry * dest, int length) +{ + set_entry_tag_v1(dest, gtk_entry_get_text(src), length); + return; +} + +static void set_entry_tag_v2(GtkEntry * entry, struct id3_tag* id3, guint32 frame_type) +{ + struct id3_frame* frame; + + frame = id3_get_frame(id3, frame_type, 1); + if (frame != NULL) + { + char *text, *url, *comment; + + text = id3_get_text(frame); + if (text != NULL) + { + gtk_entry_set_text(entry, text); + g_free(text); + return; + } + + url = id3_get_url(frame); + if (url != NULL) + { + gtk_entry_set_text(entry, url); + g_free(url); + return; + } + + comment = id3_get_comment(frame); + if (comment != NULL) + { + gtk_entry_set_text(entry, comment); + g_free(url); + return; + } + } +} + +void copy_entry_tag_v2(GtkEntry * src, GtkEntry * dest) +{ + gtk_entry_set_text(dest, gtk_entry_get_text(src)); + return; +} + static int genre_find_index(GList *genre_list, int id) { int idx = 0; @@ -74,6 +128,24 @@ idx++; genre_list = genre_list->next; } + if (!genre_list) + return 0; + return idx; +} + +static int genre_find_index_str(GList *genre_list, const char* str) +{ + int idx = 0; + while (genre_list) + { + struct genre_item *item = genre_list->data; + if (strcmp(item->name, str) == 0) + break; + idx++; + genre_list = genre_list->next; + } + if (!genre_list) + return 0; return idx; } @@ -83,9 +155,9 @@ return strcasecmp(ga->name, gb->name); } -static void save_cb(GtkWidget * w, gpointer data) +static void remove_id3v1(void) { - int fd; + int fd, len; struct id3v1tag_t tag; char *msg = NULL; @@ -94,71 +166,36 @@ if ((fd = open(current_filename, O_RDWR)) != -1) { - int tracknum; - - lseek(fd, -128, SEEK_END); + len = lseek(fd, -128, SEEK_END); read(fd, &tag, sizeof (struct id3v1tag_t)); if (!strncmp(tag.tag, "TAG", 3)) - lseek(fd, -128, SEEK_END); - else - lseek(fd, 0, SEEK_END); - tag.tag[0] = 'T'; - tag.tag[1] = 'A'; - tag.tag[2] = 'G'; - get_entry_tag(GTK_ENTRY(title_entry), tag.title, 30); - get_entry_tag(GTK_ENTRY(artist_entry), tag.artist, 30); - get_entry_tag(GTK_ENTRY(album_entry), tag.album, 30); - get_entry_tag(GTK_ENTRY(year_entry), tag.year, 4); - tracknum = atoi(gtk_entry_get_text(GTK_ENTRY(tracknum_entry))); - if (tracknum > 0) - { - get_entry_tag(GTK_ENTRY(comment_entry), - tag.u.v1_1.comment, 28); - tag.u.v1_1.__zero = 0; - tag.u.v1_1.track_number = MIN(tracknum, 255); + { + if (ftruncate(fd, len)) + msg = g_strdup_printf(_("%s\n" + "Unable to truncate file: %s"), + _("Couldn't remove tag!"), + strerror(errno)); } - else - get_entry_tag(GTK_ENTRY(comment_entry), - tag.u.v1_0.comment, 30); - tag.genre = current_genre; - if (write(fd, &tag, sizeof (tag)) != sizeof (tag)) - msg = g_strdup_printf(_("%s\nUnable to write to file: %s"), - _("Couldn't write tag!"), - strerror(errno)); close(fd); } else msg = g_strdup_printf(_("%s\nUnable to open file: %s"), - _("Couldn't write tag!"), + _("Couldn't remove tag!"), strerror(errno)); if (msg) { - GtkWidget *mwin = xmms_show_message(_("File Info"), msg, - _("OK"), FALSE, NULL, NULL); + GtkWidget *mwin = xmms_show_message(_("File Info"), msg, _("OK"), + FALSE, NULL, NULL); gtk_window_set_transient_for(GTK_WINDOW(mwin), GTK_WINDOW(window)); g_free(msg); } - else - gtk_widget_destroy(window); -} - -static void label_set_text(GtkWidget * label, char *str, ...) -{ - va_list args; - char tempstr[MAX_STR_LEN]; - - va_start(args, str); - g_vsnprintf(tempstr, MAX_STR_LEN, str, args); - va_end(args); - - gtk_label_set_text(GTK_LABEL(label), tempstr); } -static void remove_id3_cb(GtkWidget * w, gpointer data) +static void save_cb(GtkWidget * w, gpointer data) { - int fd, len; + int fd; struct id3v1tag_t tag; char *msg = NULL; @@ -167,36 +204,112 @@ if ((fd = open(current_filename, O_RDWR)) != -1) { - len = lseek(fd, -128, SEEK_END); - read(fd, &tag, sizeof (struct id3v1tag_t)); + if (!GTK_TOGGLE_BUTTON(v1_checkbox)->active) { + /* Try to save id3v1 tag */ + int tracknum; - if (!strncmp(tag.tag, "TAG", 3)) - { - if (ftruncate(fd, len)) - msg = g_strdup_printf( - _("%s\n" - "Unable to truncate file: %s"), - _("Couldn't remove tag!"), - strerror(errno)); + lseek(fd, -128, SEEK_END); + read(fd, &tag, sizeof (struct id3v1tag_t)); + + if (!strncmp(tag.tag, "TAG", 3)) + lseek(fd, -128, SEEK_END); + else + lseek(fd, 0, SEEK_END); + tag.tag[0] = 'T'; + tag.tag[1] = 'A'; + tag.tag[2] = 'G'; + get_entry_tag_v1(GTK_ENTRY(v1_title_entry), tag.title, 30); + get_entry_tag_v1(GTK_ENTRY(v1_artist_entry), tag.artist, 30); + get_entry_tag_v1(GTK_ENTRY(v1_album_entry), tag.album, 30); + get_entry_tag_v1(GTK_ENTRY(v1_year_entry), tag.year, 4); + tracknum = atoi(gtk_entry_get_text(GTK_ENTRY(v1_tracknum_entry))); + if (tracknum > 0) + { + get_entry_tag_v1(GTK_ENTRY(v1_comment_entry), tag.u.v1_1.comment, 28); + tag.u.v1_1.__zero = 0; + tag.u.v1_1.track_number = MIN(tracknum, 255); + } + else + get_entry_tag_v1(GTK_ENTRY(v1_comment_entry), + tag.u.v1_0.comment, 30); + tag.genre = v1_current_genre; + if (write(fd, &tag, sizeof (tag)) != sizeof (tag)) + msg = g_strdup_printf(_("%s\nUnable to write to file: %s"), + _("Couldn't write tag!"), + strerror(errno)); + } else { + /* Remove the id3v1 tag from the file */ + remove_id3v1(); } - else - msg = strdup(_("No tag to remove!")); - close(fd); + + if (!GTK_TOGGLE_BUTTON(v2_checkbox)->active) { + struct id3_tag* id3; + + lseek(fd, SEEK_SET, 0); + id3 = id3_open_fd(fd, 0); + if (id3 == NULL) + id3 = id3_new(); + + if (id3 != NULL) + { + id3_set_text(id3_get_or_add_frame(id3, ID3_TIT2), gtk_entry_get_text(GTK_ENTRY(v2_title_entry))); + id3_set_text(id3_get_or_add_frame(id3, ID3_TPE1), gtk_entry_get_text(GTK_ENTRY(v2_artist_entry))); + id3_set_text(id3_get_or_add_frame(id3, ID3_TALB), gtk_entry_get_text(GTK_ENTRY(v2_album_entry))); + id3_set_text(id3_get_or_add_frame(id3, ID3_TYER), gtk_entry_get_text(GTK_ENTRY(v2_year_entry))); + id3_set_comment(id3_get_or_add_frame(id3, ID3_COMM), gtk_entry_get_text(GTK_ENTRY(v2_comment_entry))); + id3_set_text(id3_get_or_add_frame(id3, ID3_TRCK), gtk_entry_get_text(GTK_ENTRY(v2_tracknum_entry))); + if (v2_current_genre != 0xff) + { + /* Essentially the same behavior as Winamp2's ID3v2 tagger */ + char genre[255]; + snprintf(genre, sizeof(genre), "(%d)%s", + v2_current_genre, + gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(v2_genre_combo)->entry))); + id3_set_text(id3_get_or_add_frame(id3, ID3_TCON), genre); + } + else + { + id3_set_text(id3_get_or_add_frame(id3, ID3_TCON), gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(v2_genre_combo)->entry))); + } + id3_set_text(id3_get_or_add_frame(id3, ID3_TCOM), gtk_entry_get_text(GTK_ENTRY(v2_composer_entry))); + id3_set_url(id3_get_or_add_frame(id3, ID3_WCOM), gtk_entry_get_text(GTK_ENTRY(v2_url_entry))); + id3_set_text(id3_get_or_add_frame(id3, ID3_TOPE), gtk_entry_get_text(GTK_ENTRY(v2_orig_artist_entry))); + id3_set_text(id3_get_or_add_frame(id3, ID3_TENC), gtk_entry_get_text(GTK_ENTRY(v2_encoded_by_entry))); + + id3_write_tag_filename(id3, current_filename); + } + + } else { + id3_remove_tag_filename(current_filename); + } + + if (fd) + close(fd); } else msg = g_strdup_printf(_("%s\nUnable to open file: %s"), - _("Couldn't remove tag!"), + _("Couldn't write tag!"), strerror(errno)); if (msg) { - GtkWidget *mwin = xmms_show_message(_("File Info"), msg, - _("OK"), FALSE, NULL, NULL); + GtkWidget *mwin = xmms_show_message(_("File Info"), msg, _("OK"), + FALSE, NULL, NULL); gtk_window_set_transient_for(GTK_WINDOW(mwin), GTK_WINDOW(window)); g_free(msg); } - else - gtk_widget_destroy(window); +} + +static void label_set_text(GtkWidget * label, const char *str, ...) +{ + va_list args; + char tempstr[MAX_STR_LEN]; + + va_start(args, str); + g_vsnprintf(tempstr, MAX_STR_LEN, str, args); + va_end(args); + + gtk_label_set_text(GTK_LABEL(label), tempstr); } static void set_mpeg_level_label(gboolean mpeg25, int lsf, int layer) @@ -219,28 +332,141 @@ static void file_info_http(char *filename) { - gtk_widget_set_sensitive(id3_frame, FALSE); + gtk_widget_set_sensitive(id3v1_frame, FALSE); + gtk_widget_set_sensitive(id3v2_frame, FALSE); if (mpg123_filename && !strcmp(filename, mpg123_filename) && mpg123_bitrate != 0) { - set_mpeg_level_label(mpg123_mpeg25, mpg123_lsf, mpg123_layer); + set_mpeg_level_label(mpg123_mpeg25, mpg123_lsf, + mpg123_layer); label_set_text(mpeg_bitrate, _("Bitrate: %d kb/s"), - mpg123_bitrate); + mpg123_bitrate); label_set_text(mpeg_samplerate, _("Samplerate: %d Hz"), - mpg123_frequency); + mpg123_frequency); label_set_text(mpeg_flags, "%s", - channel_mode_name(mpg123_mode)); + channel_mode_name(mpg123_mode)); } } -static void genre_selected(GtkList *list, GtkWidget *w, gpointer data) +void copy_v2_to_v1_cb(GtkButton *button, gpointer user_data) +{ + copy_entry_tag_v1(GTK_ENTRY(v2_title_entry), GTK_ENTRY(v1_title_entry), 30); + copy_entry_tag_v1(GTK_ENTRY(v2_artist_entry), GTK_ENTRY(v1_artist_entry), 30); + copy_entry_tag_v1(GTK_ENTRY(v2_album_entry), GTK_ENTRY(v1_album_entry), 30); + copy_entry_tag_v1(GTK_ENTRY(v2_year_entry), GTK_ENTRY(v1_year_entry), 4); + copy_entry_tag_v1(GTK_ENTRY(v2_comment_entry), GTK_ENTRY(v1_comment_entry), 30); + copy_entry_tag_v1(GTK_ENTRY(v2_tracknum_entry), GTK_ENTRY(v1_tracknum_entry), 3); + + gtk_list_select_item(GTK_LIST(GTK_COMBO(v1_genre_combo)->list), genre_find_index(genre_list, v2_current_genre)); + return; +} + +void copy_v1_to_v2_cb(GtkButton *button, gpointer user_data) +{ + copy_entry_tag_v2(GTK_ENTRY(v1_title_entry), GTK_ENTRY(v2_title_entry)); + copy_entry_tag_v2(GTK_ENTRY(v1_artist_entry), GTK_ENTRY(v2_artist_entry)); + copy_entry_tag_v2(GTK_ENTRY(v1_album_entry), GTK_ENTRY(v2_album_entry)); + copy_entry_tag_v2(GTK_ENTRY(v1_year_entry), GTK_ENTRY(v2_year_entry)); + copy_entry_tag_v2(GTK_ENTRY(v1_comment_entry), GTK_ENTRY(v2_comment_entry)); + copy_entry_tag_v2(GTK_ENTRY(v1_tracknum_entry), GTK_ENTRY(v2_tracknum_entry)); + + gtk_list_select_item(GTK_LIST(GTK_COMBO(v2_genre_combo)->list), genre_find_index(genre_list, v1_current_genre)); + return; +} + +void v1_toggle_cb (GtkWidget *widget, gpointer data) +{ + int i = 0; + if (GTK_TOGGLE_BUTTON (widget)->active) + { + // If control reaches here, the toggle button is down + // Gray out labels + for (i = 0; i < v1_labels_list->len; i++) { + gtk_widget_set_sensitive(GTK_WIDGET( g_ptr_array_index(v1_labels_list, i) ), FALSE); + } + gtk_widget_set_sensitive(v1_title_entry, FALSE); + gtk_widget_set_sensitive(v1_artist_entry, FALSE); + gtk_widget_set_sensitive(v1_album_entry, FALSE); + gtk_widget_set_sensitive(v1_year_entry, FALSE); + gtk_widget_set_sensitive(v1_tracknum_entry, FALSE); + gtk_widget_set_sensitive(v1_comment_entry, FALSE); + gtk_widget_set_sensitive(v1_genre_combo, FALSE); + } else { + + // If control reaches here, the toggle button is up + // Enable labels + for (i = 0; i < v1_labels_list->len; i++) { + gtk_widget_set_sensitive(GTK_WIDGET( g_ptr_array_index(v1_labels_list, i) ), TRUE); + } + gtk_widget_set_sensitive(v1_title_entry, TRUE); + gtk_widget_set_sensitive(v1_artist_entry, TRUE); + gtk_widget_set_sensitive(v1_album_entry, TRUE); + gtk_widget_set_sensitive(v1_year_entry, TRUE); + gtk_widget_set_sensitive(v1_tracknum_entry, TRUE); + gtk_widget_set_sensitive(v1_comment_entry, TRUE); + gtk_widget_set_sensitive(v1_genre_combo, TRUE); + } +} + +void v2_toggle_cb (GtkWidget *widget, gpointer data) +{ + int i = 0; + if (GTK_TOGGLE_BUTTON (widget)->active) + { + // If control reaches here, the toggle button is down + // Gray out labels + for (i = 0; i < v2_labels_list->len; i++) { + gtk_widget_set_sensitive(GTK_WIDGET( g_ptr_array_index(v2_labels_list, i) ), FALSE); + } + gtk_widget_set_sensitive(v2_title_entry, FALSE); + gtk_widget_set_sensitive(v2_artist_entry, FALSE); + gtk_widget_set_sensitive(v2_album_entry, FALSE); + gtk_widget_set_sensitive(v2_year_entry, FALSE); + gtk_widget_set_sensitive(v2_tracknum_entry, FALSE); + gtk_widget_set_sensitive(v2_comment_entry, FALSE); + gtk_widget_set_sensitive(v2_composer_entry, FALSE); + gtk_widget_set_sensitive(v2_orig_artist_entry, FALSE); + gtk_widget_set_sensitive(v2_url_entry, FALSE); + gtk_widget_set_sensitive(v2_encoded_by_entry, FALSE); + gtk_widget_set_sensitive(v2_genre_combo, FALSE); + } else { + + // If control reaches here, the toggle button is up + // Enable labels + for (i = 0; i < v2_labels_list->len; i++) { + gtk_widget_set_sensitive(GTK_WIDGET( g_ptr_array_index(v2_labels_list, i) ), TRUE); + } + gtk_widget_set_sensitive(v2_title_entry, TRUE); + gtk_widget_set_sensitive(v2_artist_entry, TRUE); + gtk_widget_set_sensitive(v2_album_entry, TRUE); + gtk_widget_set_sensitive(v2_year_entry, TRUE); + gtk_widget_set_sensitive(v2_tracknum_entry, TRUE); + gtk_widget_set_sensitive(v2_comment_entry, TRUE); + gtk_widget_set_sensitive(v2_composer_entry, TRUE); + gtk_widget_set_sensitive(v2_orig_artist_entry, TRUE); + gtk_widget_set_sensitive(v2_url_entry, TRUE); + gtk_widget_set_sensitive(v2_encoded_by_entry, TRUE); + gtk_widget_set_sensitive(v2_genre_combo, TRUE); + } +} +static void v1_genre_selected(GtkList *list, GtkWidget *w, gpointer data) +{ + void * p; + p = gtk_object_get_data(GTK_OBJECT(w), "genre_id"); + if (p != NULL) + v1_current_genre = GPOINTER_TO_INT(p); + else + v1_current_genre = 0; +} + +static void v2_genre_selected(GtkList *list, GtkWidget *w, gpointer data) { void * p; p = gtk_object_get_data(GTK_OBJECT(w), "genre_id"); if (p != NULL) - current_genre = GPOINTER_TO_INT(p); + v2_current_genre = GPOINTER_TO_INT(p); else - current_genre = 0; + v2_current_genre = 0; } static void genre_set_popdown(GtkWidget *combo, GList *genres) @@ -269,7 +495,7 @@ void mpg123_file_info_box(char *filename) { int i; - struct id3v1tag_t tag; + struct id3v1tag_t id3v1tag; FILE *fh; char *tmp, *title; const char *emphasis[4]; @@ -284,205 +510,397 @@ if (!window) { - GtkWidget *vbox, *hbox, *left_vbox, *table; - GtkWidget *mpeg_frame, *mpeg_box; - GtkWidget *label, *filename_hbox; - GtkWidget *bbox, *save, *remove_id3, *cancel; - + GtkWidget *window_vbox, + *id3v1_vbox, *id3v2_vbox, *id3v1_frame_vbox, *id3v2_frame_vbox, + *mpeg_lvbox, *mpeg_rvbox, *mpeg_hbox, *mpeg_box, *mpeg_frame, + *bbox, *save, *close, *copy_to, *copy_from, + *table, *label, *filename_hbox; + + v1_labels_list = g_ptr_array_new(); + v2_labels_list = g_ptr_array_new(); + window = gtk_window_new(GTK_WINDOW_DIALOG); - gtk_window_set_policy(GTK_WINDOW(window), FALSE, FALSE, FALSE); - gtk_signal_connect(GTK_OBJECT(window), "destroy", - gtk_widget_destroyed, &window); - gtk_signal_connect(GTK_OBJECT(window), "key_press_event", - file_info_box_keypress_cb, NULL); + gtk_signal_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &window); + gtk_signal_connect(GTK_OBJECT(window), "key_press_event", file_info_box_keypress_cb, NULL); gtk_container_set_border_width(GTK_CONTAINER(window), 10); - vbox = gtk_vbox_new(FALSE, 10); - gtk_container_add(GTK_CONTAINER(window), vbox); - + window_vbox = gtk_vbox_new(FALSE,10); filename_hbox = gtk_hbox_new(FALSE, 5); - gtk_box_pack_start(GTK_BOX(vbox), filename_hbox, - FALSE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(window_vbox), filename_hbox, FALSE, TRUE, 0); label = gtk_label_new(_("Filename:")); - gtk_box_pack_start(GTK_BOX(filename_hbox), label, - FALSE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(filename_hbox), label, FALSE, TRUE, 0); filename_entry = xmms_entry_new(); gtk_editable_set_editable(GTK_EDITABLE(filename_entry), FALSE); - gtk_box_pack_start(GTK_BOX(filename_hbox), - filename_entry, TRUE, TRUE, 0); - - hbox = gtk_hbox_new(FALSE, 10); - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); - - left_vbox = gtk_vbox_new(FALSE, 10); - gtk_box_pack_start(GTK_BOX(hbox), left_vbox, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(filename_hbox), filename_entry, TRUE, TRUE, 0); + + /* Set up the genres list */ + + if (!genre_list) + { + struct genre_item *item; + + for (i = 0; i < GENRE_MAX; i++) + { + item = g_malloc(sizeof (*item)); + item->name = gettext(mpg123_id3_genres[i]); + item->id = i; + genre_list = g_list_prepend(genre_list, item); + } + item = g_malloc(sizeof (*item)); + item->name = ""; + item->id = 0xff; + genre_list = g_list_prepend(genre_list, item); + genre_list = g_list_sort(genre_list, genre_comp_func); + } + + notebook = gtk_notebook_new (); + gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP); + + /* ID3v2 page */ - id3_frame = gtk_frame_new(_("ID3 Tag:")); - gtk_box_pack_start(GTK_BOX(left_vbox), id3_frame, - FALSE, FALSE, 0); + id3v2_vbox = gtk_vbox_new(FALSE, 0); - table = gtk_table_new(5, 5, FALSE); + id3v2_frame = gtk_frame_new("ID3v2 Information"); + gtk_box_pack_start(GTK_BOX(id3v2_vbox), id3v2_frame, FALSE, FALSE, 0); + + id3v2_frame_vbox = gtk_vbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(id3v2_frame), id3v2_frame_vbox); + + table = gtk_table_new(6, 6, FALSE); gtk_container_set_border_width(GTK_CONTAINER(table), 5); - gtk_container_add(GTK_CONTAINER(id3_frame), table); + gtk_widget_set_usize(GTK_WIDGET(table), 400, -1); + gtk_box_pack_start(GTK_BOX(id3v2_frame_vbox), table, FALSE, FALSE, 0); - label = gtk_label_new(_("Title:")); + v2_checkbox = gtk_check_button_new_with_label ("Disable ID3v2 Tag"); + gtk_signal_connect(GTK_OBJECT(v2_checkbox), "toggled", GTK_SIGNAL_FUNC(v2_toggle_cb), NULL); + gtk_table_attach(GTK_TABLE(table), v2_checkbox, 1, 3, 0, 1, GTK_FILL, GTK_FILL, 0, 2); + + label = gtk_label_new("Track number:"); + g_ptr_array_add(v2_labels_list, (gpointer)label); gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); - gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, - GTK_FILL, GTK_FILL, 5, 5); + gtk_table_attach(GTK_TABLE(table), label, 3, 4, 0, 1, + GTK_FILL, GTK_FILL, 5,5); - title_entry = gtk_entry_new_with_max_length(30); - gtk_table_attach(GTK_TABLE(table), title_entry, 1, 4, 0, 1, - GTK_FILL | GTK_EXPAND | GTK_SHRINK, - GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5); + v2_tracknum_entry = gtk_entry_new_with_max_length(3); + gtk_widget_set_usize(v2_tracknum_entry, 20, -1); + gtk_table_attach(GTK_TABLE(table), v2_tracknum_entry, 4, 5, 0, 1, + GTK_FILL, GTK_FILL, 0, 2); - label = gtk_label_new(_("Artist:")); + label = gtk_label_new("Title:"); + g_ptr_array_add(v2_labels_list, (gpointer)label); gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); - gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, - GTK_FILL, GTK_FILL, 5, 5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 5, 5); - artist_entry = gtk_entry_new_with_max_length(30); - gtk_table_attach(GTK_TABLE(table), artist_entry, 1, 4, 1, 2, - GTK_FILL | GTK_EXPAND | GTK_SHRINK, - GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5); + v2_title_entry = gtk_entry_new_with_max_length(MAX_ENTRY_LEN2); + gtk_table_attach(GTK_TABLE(table), v2_title_entry, 1, 5, 1, 2, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 2); - label = gtk_label_new(_("Album:")); + label = gtk_label_new("Artist:"); + g_ptr_array_add(v2_labels_list, (gpointer)label); gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); gtk_table_attach(GTK_TABLE(table), label, 0, 1, 2, 3, - GTK_FILL, GTK_FILL, 5, 5); + GTK_FILL, GTK_FILL, 5, 5); - album_entry = gtk_entry_new_with_max_length(30); - gtk_table_attach(GTK_TABLE(table), album_entry, 1, 4, 2, 3, - GTK_FILL | GTK_EXPAND | GTK_SHRINK, - GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5); + v2_artist_entry = gtk_entry_new_with_max_length(MAX_ENTRY_LEN2); + gtk_table_attach(GTK_TABLE(table), v2_artist_entry, 1, 5, 2, 3, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 2); - label = gtk_label_new(_("Comment:")); + label = gtk_label_new("Album:"); + g_ptr_array_add(v2_labels_list, (gpointer)label); gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); gtk_table_attach(GTK_TABLE(table), label, 0, 1, 3, 4, - GTK_FILL, GTK_FILL, 5, 5); + GTK_FILL, GTK_FILL, 5, 5); - comment_entry = gtk_entry_new_with_max_length(30); - gtk_table_attach(GTK_TABLE(table), comment_entry, 1, 4, 3, 4, - GTK_FILL | GTK_EXPAND | GTK_SHRINK, - GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5); + v2_album_entry = gtk_entry_new_with_max_length(MAX_ENTRY_LEN2); + gtk_table_attach(GTK_TABLE(table), v2_album_entry, 1, 5, 3, 4, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 2); - label = gtk_label_new(_("Year:")); + label = gtk_label_new("Comment:"); + g_ptr_array_add(v2_labels_list, (gpointer)label); gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); gtk_table_attach(GTK_TABLE(table), label, 0, 1, 4, 5, - GTK_FILL, GTK_FILL, 5, 5); + GTK_FILL, GTK_FILL, 5, 5); - year_entry = gtk_entry_new_with_max_length(4); - gtk_widget_set_usize(year_entry, 40, -1); - gtk_table_attach(GTK_TABLE(table), year_entry, 1, 2, 4, 5, - GTK_FILL | GTK_EXPAND | GTK_SHRINK, - GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5); - - label = gtk_label_new(_("Track number:")); - gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); - gtk_table_attach(GTK_TABLE(table), label, 2, 3, 4, 5, - GTK_FILL, GTK_FILL, 5, 5); - - tracknum_entry = gtk_entry_new_with_max_length(3); - gtk_widget_set_usize(tracknum_entry, 40, -1); - gtk_table_attach(GTK_TABLE(table), tracknum_entry, 3, 4, 4, 5, - GTK_FILL | GTK_EXPAND | GTK_SHRINK, - GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5); + v2_comment_entry = gtk_entry_new_with_max_length(MAX_ENTRY_LEN2); + gtk_table_attach(GTK_TABLE(table), v2_comment_entry, 1, 5, 4, 5, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 2); - label = gtk_label_new(_("Genre:")); + label = gtk_label_new("Year:"); + g_ptr_array_add(v2_labels_list, (gpointer)label); gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); gtk_table_attach(GTK_TABLE(table), label, 0, 1, 5, 6, - GTK_FILL, GTK_FILL, 5, 5); + GTK_FILL, GTK_FILL, 5, 5); - genre_combo = gtk_combo_new(); - gtk_entry_set_editable(GTK_ENTRY(GTK_COMBO(genre_combo)->entry), - FALSE); - if (!genre_list) - { - struct genre_item *item; + v2_year_entry = gtk_entry_new_with_max_length(4); + gtk_widget_set_usize(v2_year_entry, 45, -1); + gtk_table_attach(GTK_TABLE(table), v2_year_entry, 1, 2, 5, 6, + GTK_FILL, GTK_FILL, 0, 2); - for (i = 0; i < GENRE_MAX; i++) - { - item = g_malloc(sizeof (*item)); - item->name = gettext(mpg123_id3_genres[i]); - item->id = i; - genre_list = g_list_prepend(genre_list, item); - } - item = g_malloc(sizeof (*item)); - item->name = ""; - item->id = 0xff; - genre_list = g_list_prepend(genre_list, item); - genre_list = g_list_sort(genre_list, genre_comp_func); - } - genre_set_popdown(genre_combo, genre_list); - gtk_signal_connect(GTK_OBJECT(GTK_COMBO(genre_combo)->list), - "select-child", genre_selected, NULL); - - gtk_table_attach(GTK_TABLE(table), genre_combo, 1, 4, 5, 6, - GTK_FILL | GTK_EXPAND | GTK_SHRINK, - GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5); + label = gtk_label_new("Genre:"); + g_ptr_array_add(v2_labels_list, (gpointer)label); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 2, 3, 5, 6, + GTK_FILL, GTK_FILL, 5, 5); + + v2_genre_combo = gtk_combo_new(); + gtk_combo_set_value_in_list(GTK_COMBO(v2_genre_combo), FALSE, TRUE); + + genre_set_popdown(v2_genre_combo, genre_list); + gtk_signal_connect(GTK_OBJECT(GTK_COMBO(v2_genre_combo)->list), + "select-child", v2_genre_selected, NULL); + + gtk_table_attach(GTK_TABLE(table), v2_genre_combo, 3, 5, 5, 6, + GTK_FILL | GTK_SHRINK, GTK_FILL | + GTK_SHRINK, 0, 2); + + label = gtk_label_new("Composer:"); + g_ptr_array_add(v2_labels_list, (gpointer)label); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 6, 7, + GTK_FILL, GTK_FILL, 5, 5); + + v2_composer_entry = gtk_entry_new_with_max_length(MAX_ENTRY_LEN2); + gtk_table_attach(GTK_TABLE(table), v2_composer_entry, 1, 5, 6, 7, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 2); + + label = gtk_label_new("Orig. Artist:"); + g_ptr_array_add(v2_labels_list, (gpointer)label); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 7, 8, + GTK_FILL, GTK_FILL, 5, 5); + + v2_orig_artist_entry = gtk_entry_new_with_max_length(MAX_ENTRY_LEN2); + gtk_table_attach(GTK_TABLE(table), v2_orig_artist_entry, 1, 5, 7, 8, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 2); + + label = gtk_label_new("URL:"); + g_ptr_array_add(v2_labels_list, (gpointer)label); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 8, 9, + GTK_FILL, GTK_FILL, 5, 5); + + v2_url_entry = gtk_entry_new_with_max_length(MAX_ENTRY_LEN2); + gtk_table_attach(GTK_TABLE(table), v2_url_entry, 1, 5, 8, 9, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 2); + label = gtk_label_new("Encoded By:"); + g_ptr_array_add(v2_labels_list, (gpointer)label); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 9, 10, + GTK_FILL, GTK_FILL, 5, 5); + + v2_encoded_by_entry = gtk_entry_new_with_max_length(MAX_ENTRY_LEN2); + gtk_table_attach(GTK_TABLE(table), v2_encoded_by_entry, 1, 5, 9, 10, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 2); bbox = gtk_hbutton_box_new(); - gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), - GTK_BUTTONBOX_END); - gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5); - gtk_box_pack_start(GTK_BOX(left_vbox), bbox, FALSE, FALSE, 0); - - save = gtk_button_new_with_label(_("Save")); - gtk_signal_connect(GTK_OBJECT(save), "clicked", save_cb, NULL); - GTK_WIDGET_SET_FLAGS(save, GTK_CAN_DEFAULT); - gtk_box_pack_start(GTK_BOX(bbox), save, TRUE, TRUE, 0); - gtk_widget_grab_default(save); - - remove_id3 = gtk_button_new_with_label(_("Remove ID3")); - gtk_signal_connect(GTK_OBJECT(remove_id3), "clicked", - remove_id3_cb, NULL); - GTK_WIDGET_SET_FLAGS(remove_id3, GTK_CAN_DEFAULT); - gtk_box_pack_start(GTK_BOX(bbox), remove_id3, TRUE, TRUE, 0); - - cancel = gtk_button_new_with_label(_("Cancel")); - gtk_signal_connect_object(GTK_OBJECT(cancel), "clicked", - gtk_widget_destroy, GTK_OBJECT(window)); - GTK_WIDGET_SET_FLAGS(cancel, GTK_CAN_DEFAULT); - gtk_box_pack_start(GTK_BOX(bbox), cancel, TRUE, TRUE, 0); + gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_START); + gtk_button_box_set_spacing (GTK_BUTTON_BOX (bbox), 0); + gtk_container_set_border_width(GTK_CONTAINER(bbox), 5); + gtk_box_pack_start(GTK_BOX(id3v2_frame_vbox), bbox, FALSE, FALSE, 0); + + copy_to = gtk_button_new_with_label("ID3v2 to ID3v1"); + gtk_signal_connect(GTK_OBJECT(copy_to), "clicked", GTK_SIGNAL_FUNC(copy_v2_to_v1_cb), NULL); + /* remove the next line to thicken the button width */ + GTK_WIDGET_SET_FLAGS(copy_to, GTK_CAN_DEFAULT); + gtk_box_pack_start(GTK_BOX(bbox), copy_to, FALSE, TRUE, 0); + + label = gtk_label_new ("ID3v2"); + gtk_notebook_append_page (GTK_NOTEBOOK (notebook), id3v2_vbox, label); + + /* ID3v1 page */ + + id3v1_vbox = gtk_vbox_new(FALSE, 10); + id3v1_frame = gtk_frame_new("ID3v1 Information"); + gtk_box_pack_start(GTK_BOX(id3v1_vbox), id3v1_frame, TRUE, TRUE, 0); + + id3v1_frame_vbox = gtk_vbox_new(FALSE,10); + gtk_container_add(GTK_CONTAINER(id3v1_frame), id3v1_frame_vbox); + + table = gtk_table_new(6, 6, FALSE); + gtk_container_set_border_width(GTK_CONTAINER(table), 5); + //gtk_widget_set_usize(GTK_WIDGET(table), 325, -1); + //gtk_container_add(GTK_CONTAINER(id3v1_frame), table); + gtk_box_pack_start(GTK_BOX(id3v1_frame_vbox), table, FALSE, FALSE, 0); + + v1_checkbox = gtk_check_button_new_with_label ("Disable ID3v1 Tag"); + gtk_signal_connect(GTK_OBJECT(v1_checkbox), "toggled", GTK_SIGNAL_FUNC(v1_toggle_cb), NULL); + gtk_table_attach(GTK_TABLE(table), v1_checkbox, 1, 3, 0, 1, + GTK_FILL, GTK_FILL, 0, 2); + + label = gtk_label_new("Track number:"); + g_ptr_array_add(v1_labels_list, (gpointer)label); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 3, 4, 0, 1, + GTK_FILL, GTK_FILL, 5,5); + + v1_tracknum_entry = gtk_entry_new_with_max_length(3); + gtk_widget_set_usize(v1_tracknum_entry, 20, -1); + gtk_table_attach(GTK_TABLE(table), v1_tracknum_entry, 4, 5, 0, 1, + GTK_FILL, GTK_FILL, 0, 2); - mpeg_frame = gtk_frame_new(_("MPEG Info:")); - gtk_box_pack_start(GTK_BOX(hbox), mpeg_frame, FALSE, FALSE, 0); + label = gtk_label_new("Title:"); + g_ptr_array_add(v1_labels_list, (gpointer)label); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, + GTK_FILL, GTK_FILL, 5, 5); + + v1_title_entry = gtk_entry_new_with_max_length(30); + gtk_table_attach(GTK_TABLE(table), v1_title_entry, 1, 5, 1, 2, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 2); + + label = gtk_label_new("Artist:"); + g_ptr_array_add(v1_labels_list, (gpointer)label); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 2, 3, + GTK_FILL, GTK_FILL, 5, 5); + + v1_artist_entry = gtk_entry_new_with_max_length(30); + gtk_table_attach(GTK_TABLE(table), v1_artist_entry, 1, 5, 2, 3, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 2); + + label = gtk_label_new("Album:"); + g_ptr_array_add(v1_labels_list, (gpointer)label); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 3, 4, + GTK_FILL, GTK_FILL, 5, 5); + + v1_album_entry = gtk_entry_new_with_max_length(30); + gtk_table_attach(GTK_TABLE(table), v1_album_entry, 1, 5, 3, 4, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 2); + + label = gtk_label_new("Comment:"); + g_ptr_array_add(v1_labels_list, (gpointer)label); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 4, 5, + GTK_FILL, GTK_FILL, 5, 5); + + v1_comment_entry = gtk_entry_new_with_max_length(30); + gtk_table_attach(GTK_TABLE(table), v1_comment_entry, 1, 5, 4, 5, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, + GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 2); + + label = gtk_label_new("Year:"); + g_ptr_array_add(v1_labels_list, (gpointer)label); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 5, 6, + GTK_FILL, GTK_FILL, 5, 5); + + v1_year_entry = gtk_entry_new_with_max_length(4); + gtk_widget_set_usize(v1_year_entry, 45, -1); + gtk_table_attach(GTK_TABLE(table), v1_year_entry, 1, 2, 5, 6, + GTK_FILL, GTK_FILL, 0, 2); + + label = gtk_label_new("Genre:"); + g_ptr_array_add(v1_labels_list, (gpointer)label); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 2, 3, 5, 6, + GTK_FILL, GTK_FILL, 5, 5); + + v1_genre_combo = gtk_combo_new(); + gtk_entry_set_editable(GTK_ENTRY(GTK_COMBO(v1_genre_combo)->entry), FALSE); + genre_set_popdown(v1_genre_combo, genre_list); + gtk_signal_connect(GTK_OBJECT(GTK_COMBO(v1_genre_combo)->list), + "select-child", v1_genre_selected, NULL); + + gtk_table_attach(GTK_TABLE(table), v1_genre_combo, 3, 5, 5, 6, + GTK_FILL | GTK_SHRINK, GTK_FILL | + GTK_SHRINK, 0, 2); + + bbox = gtk_hbutton_box_new(); + gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_START); + gtk_button_box_set_spacing (GTK_BUTTON_BOX (bbox), 0); + gtk_container_set_border_width(GTK_CONTAINER(bbox), 5); + gtk_box_pack_start(GTK_BOX(id3v1_frame_vbox), bbox, FALSE, FALSE, 0); + + copy_from = gtk_button_new_with_label("ID3v1 to ID3v2"); + gtk_signal_connect(GTK_OBJECT(copy_from), "clicked", GTK_SIGNAL_FUNC(copy_v1_to_v2_cb), NULL); + // remove the next line to thicken the button width + GTK_WIDGET_SET_FLAGS(copy_from, GTK_CAN_DEFAULT); + gtk_box_pack_start(GTK_BOX(bbox), copy_from, FALSE, TRUE, 0); + + label = gtk_label_new ("ID3v1"); + gtk_notebook_append_page (GTK_NOTEBOOK (notebook), id3v1_vbox, label); + + /* MPEG info page */ + + mpeg_frame = gtk_frame_new("MPEG Information"); + mpeg_hbox = gtk_hbox_new(FALSE,50); + gtk_container_add(GTK_CONTAINER(mpeg_frame), mpeg_hbox); + + mpeg_lvbox = gtk_vbox_new(FALSE, 5); + gtk_container_set_border_width(GTK_CONTAINER(mpeg_lvbox), 10); + gtk_box_pack_start(GTK_BOX(mpeg_hbox), mpeg_lvbox, FALSE, FALSE, 0); mpeg_box = gtk_vbox_new(FALSE, 5); - gtk_container_add(GTK_CONTAINER(mpeg_frame), mpeg_box); + gtk_box_pack_start(GTK_BOX(mpeg_hbox), mpeg_box, FALSE, FALSE, 0); gtk_container_set_border_width(GTK_CONTAINER(mpeg_box), 10); gtk_box_set_spacing(GTK_BOX(mpeg_box), 0); mpeg_level = gtk_label_new(""); - gtk_widget_set_usize(mpeg_level, 120, -2); - gtk_misc_set_alignment(GTK_MISC(mpeg_level), 0, 0); - gtk_box_pack_start(GTK_BOX(mpeg_box), mpeg_level, FALSE, FALSE, 0); - - mpeg_bitrate = gtk_label_new(""); - gtk_misc_set_alignment(GTK_MISC(mpeg_bitrate), 0, 0); - gtk_label_set_justify(GTK_LABEL(mpeg_bitrate), - GTK_JUSTIFY_LEFT); - gtk_box_pack_start(GTK_BOX(mpeg_box), - mpeg_bitrate, FALSE, FALSE, 0); + //gtk_widget_set_usize(mpeg_level, 120, -2); + gtk_label_set_justify (GTK_LABEL(mpeg_level), GTK_JUSTIFY_LEFT); + gtk_misc_set_alignment(GTK_MISC(mpeg_level), 0, 0.5); + gtk_box_pack_start(GTK_BOX(mpeg_lvbox), mpeg_level, FALSE, FALSE, 0); mpeg_samplerate = gtk_label_new(""); - gtk_misc_set_alignment(GTK_MISC(mpeg_samplerate), 0, 0); - gtk_box_pack_start(GTK_BOX(mpeg_box), mpeg_samplerate, - FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL(mpeg_samplerate), GTK_JUSTIFY_LEFT); + gtk_misc_set_alignment(GTK_MISC(mpeg_samplerate), 0, 0.5); + gtk_box_pack_start(GTK_BOX(mpeg_lvbox), mpeg_samplerate, FALSE, FALSE, 0); + + mpeg_fileinfo = gtk_label_new(""); + gtk_label_set_justify (GTK_LABEL(mpeg_fileinfo), GTK_JUSTIFY_LEFT); + gtk_misc_set_alignment(GTK_MISC(mpeg_fileinfo), 0, 0.5); + gtk_box_pack_start(GTK_BOX(mpeg_lvbox), mpeg_fileinfo, FALSE, FALSE, 0); + + mpeg_rvbox = gtk_vbox_new(FALSE, 5); + gtk_box_pack_start(GTK_BOX(mpeg_hbox), mpeg_rvbox, FALSE, FALSE, 0); + gtk_container_set_border_width(GTK_CONTAINER(mpeg_rvbox), 10); + + mpeg_bitrate = gtk_label_new(""); + gtk_label_set_justify (GTK_LABEL(mpeg_bitrate), GTK_JUSTIFY_LEFT); + gtk_misc_set_alignment(GTK_MISC(mpeg_bitrate), 0, 0.5); + gtk_box_pack_start(GTK_BOX(mpeg_rvbox), mpeg_bitrate, FALSE, FALSE, 0); mpeg_flags = gtk_label_new(""); - gtk_misc_set_alignment(GTK_MISC(mpeg_flags), 0, 0); - gtk_label_set_justify(GTK_LABEL(mpeg_flags), GTK_JUSTIFY_LEFT); - gtk_box_pack_start(GTK_BOX(mpeg_box), mpeg_flags, - FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL(mpeg_flags), GTK_JUSTIFY_LEFT); + gtk_misc_set_alignment(GTK_MISC(mpeg_flags), 0, 0.5); + gtk_box_pack_start(GTK_BOX(mpeg_rvbox), mpeg_flags, FALSE, FALSE, 0); - mpeg_fileinfo = gtk_label_new(""); - gtk_misc_set_alignment(GTK_MISC(mpeg_fileinfo), 0, 0); - gtk_label_set_justify(GTK_LABEL(mpeg_fileinfo), - GTK_JUSTIFY_LEFT); - gtk_box_pack_start(GTK_BOX(mpeg_box), mpeg_fileinfo, - FALSE, FALSE, 0); + label = gtk_label_new ("MPEG"); + gtk_notebook_append_page (GTK_NOTEBOOK (notebook), mpeg_frame, label); + + /* add notebook to window vbox */ + gtk_box_pack_start(GTK_BOX(window_vbox), notebook, FALSE, FALSE, 0); + /* add button box to window vbox */ + bbox = gtk_hbutton_box_new(); + gtk_box_pack_start(GTK_BOX(window_vbox), bbox, FALSE, FALSE, 0); + + gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END); + gtk_button_box_set_spacing (GTK_BUTTON_BOX (bbox), 5); + + save = gtk_button_new_with_label("Save"); + gtk_signal_connect(GTK_OBJECT(save), "clicked", GTK_SIGNAL_FUNC(save_cb), NULL); + gtk_box_pack_start(GTK_BOX(bbox), save, TRUE, TRUE, 5); + + close = gtk_button_new_with_label("Close"); + gtk_signal_connect_object(GTK_OBJECT(close), "clicked", + GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(window)); + gtk_box_pack_start(GTK_BOX(bbox), close, TRUE, TRUE, 5); + + gtk_container_add(GTK_CONTAINER(window), window_vbox); gtk_widget_show_all(window); } @@ -500,73 +918,128 @@ title = g_strdup(g_basename(filename)); if ((tmp = strrchr(title, '.')) != NULL) *tmp = '\0'; - gtk_entry_set_text(GTK_ENTRY(title_entry), title); + gtk_entry_set_text(GTK_ENTRY(v1_title_entry), title); + gtk_entry_set_text(GTK_ENTRY(v2_title_entry), title); g_free(title); - gtk_entry_set_text(GTK_ENTRY(artist_entry), ""); - gtk_entry_set_text(GTK_ENTRY(album_entry), ""); - gtk_entry_set_text(GTK_ENTRY(year_entry), ""); - gtk_entry_set_text(GTK_ENTRY(tracknum_entry), ""); - gtk_entry_set_text(GTK_ENTRY(comment_entry), ""); - gtk_list_select_item(GTK_LIST(GTK_COMBO(genre_combo)->list), + gtk_entry_set_text(GTK_ENTRY(v1_artist_entry), ""); + gtk_entry_set_text(GTK_ENTRY(v1_album_entry), ""); + gtk_entry_set_text(GTK_ENTRY(v1_year_entry), ""); + gtk_entry_set_text(GTK_ENTRY(v1_tracknum_entry), ""); + gtk_entry_set_text(GTK_ENTRY(v1_comment_entry), ""); + + gtk_list_select_item(GTK_LIST(GTK_COMBO(v1_genre_combo)->list), genre_find_index(genre_list, 0xff)); + + gtk_entry_set_text(GTK_ENTRY(v2_artist_entry), ""); + gtk_entry_set_text(GTK_ENTRY(v2_album_entry), ""); + gtk_entry_set_text(GTK_ENTRY(v2_year_entry), ""); + gtk_entry_set_text(GTK_ENTRY(v2_tracknum_entry), ""); + gtk_entry_set_text(GTK_ENTRY(v2_comment_entry), ""); + gtk_entry_set_text(GTK_ENTRY(v2_composer_entry), ""); + gtk_entry_set_text(GTK_ENTRY(v2_orig_artist_entry), ""); + gtk_entry_set_text(GTK_ENTRY(v2_url_entry), ""); + gtk_entry_set_text(GTK_ENTRY(v2_encoded_by_entry), ""); + gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(v2_genre_combo)->entry), ""); + gtk_label_set_text(GTK_LABEL(mpeg_level), "MPEG ?, layer ?"); gtk_label_set_text(GTK_LABEL(mpeg_bitrate), ""); gtk_label_set_text(GTK_LABEL(mpeg_samplerate), ""); gtk_label_set_text(GTK_LABEL(mpeg_flags), ""); gtk_label_set_text(GTK_LABEL(mpeg_fileinfo), ""); - if (!strncasecmp(filename, "http://", 7)) { file_info_http(filename); return; } - gtk_widget_set_sensitive(id3_frame, TRUE); + gtk_widget_set_sensitive(id3v1_frame, TRUE); + gtk_widget_set_sensitive(id3v2_frame, TRUE); if ((fh = fopen(current_filename, "rb")) != NULL) { struct frame frm; gboolean id3_found = FALSE; + struct id3_tag *id3 = NULL; guint8 *buf; double tpf; int pos; xing_header_t xing_header; guint32 num_frames; - - fseek(fh, -sizeof (tag), SEEK_END); - if (fread(&tag, 1, sizeof (tag), fh) == sizeof (tag)) + /* + * Try reading ID3v2 tag. + */ + fseek(fh, 0, SEEK_SET); + id3 = id3_open_fp(fh, 0); + if (id3 != NULL) { - if (!strncmp(tag.tag, "TAG", 3)) + struct id3_frame* frame; + + set_entry_tag_v2(GTK_ENTRY(v2_title_entry), id3, ID3_TIT2); + set_entry_tag_v2(GTK_ENTRY(v2_artist_entry), id3, ID3_TPE1); + set_entry_tag_v2(GTK_ENTRY(v2_album_entry), id3, ID3_TALB); + set_entry_tag_v2(GTK_ENTRY(v2_comment_entry), id3, ID3_COMM); + set_entry_tag_v2(GTK_ENTRY(v2_composer_entry), id3, ID3_TCOM); + set_entry_tag_v2(GTK_ENTRY(v2_orig_artist_entry), id3, ID3_TOPE); + set_entry_tag_v2(GTK_ENTRY(v2_url_entry), id3, ID3_WCOM); + set_entry_tag_v2(GTK_ENTRY(v2_encoded_by_entry), id3, ID3_TENC); + set_entry_tag_v2(GTK_ENTRY(v2_tracknum_entry), id3, ID3_TRCK); + set_entry_tag_v2(GTK_ENTRY(v2_year_entry), id3, ID3_TYER); + + frame = id3_get_frame(id3, ID3_TCON, 1); + if (frame != NULL) { - id3_found = TRUE; - set_entry_tag(GTK_ENTRY(title_entry), - tag.title, 30); - set_entry_tag(GTK_ENTRY(artist_entry), - tag.artist, 30); - set_entry_tag(GTK_ENTRY(album_entry), - tag.album, 30); - set_entry_tag(GTK_ENTRY(year_entry), - tag.year, 4); - /* Check for v1.1 tags */ - if (tag.u.v1_1.__zero == 0 && tag.u.v1_1.track_number > 0) - { - char *temp = g_strdup_printf("%d", tag.u.v1_1.track_number); - set_entry_tag(GTK_ENTRY(comment_entry), - tag.u.v1_1.comment, 28); - gtk_entry_set_text(GTK_ENTRY(tracknum_entry), temp); - g_free(temp); - } - else + char* genre = id3_get_content(frame); + if (genre != NULL) { - set_entry_tag(GTK_ENTRY(comment_entry), - tag.u.v1_0.comment, 30); - gtk_entry_set_text(GTK_ENTRY(tracknum_entry), ""); + int genre_idx = genre_find_index_str(genre_list, genre); + if (genre_idx > 0) + gtk_list_select_item(GTK_LIST(GTK_COMBO(v2_genre_combo)->list), genre_idx); + else + gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(v2_genre_combo)->entry), genre); } - - gtk_list_select_item(GTK_LIST(GTK_COMBO(genre_combo)->list), genre_find_index(genre_list, tag.genre)); } + + id3_close(id3); + } + else + { + /* Grey out the id3v2 tab */ + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(v2_checkbox), TRUE); + } + + /* + * Try reading ID3v1 tag. + */ + fseek(fh, -sizeof (id3v1tag), SEEK_END); + if ( (fread(&id3v1tag, 1, sizeof (id3v1tag), fh) == sizeof (id3v1tag)) && !strncmp(id3v1tag.tag, "TAG", 3)) + { + id3_found = TRUE; + set_entry_tag_v1(GTK_ENTRY(v1_title_entry), id3v1tag.title, 30); + set_entry_tag_v1(GTK_ENTRY(v1_artist_entry), id3v1tag.artist, 30); + set_entry_tag_v1(GTK_ENTRY(v1_album_entry), id3v1tag.album, 30); + set_entry_tag_v1(GTK_ENTRY(v1_year_entry), id3v1tag.year, 4); + /* Check for v1.1 tags */ + if (id3v1tag.u.v1_1.__zero == 0 && id3v1tag.u.v1_1.track_number > 0) + { + char *temp = g_strdup_printf("%d", id3v1tag.u.v1_1.track_number); + set_entry_tag_v1(GTK_ENTRY(v1_comment_entry), id3v1tag.u.v1_1.comment, 28); + gtk_entry_set_text(GTK_ENTRY(v1_tracknum_entry), temp); + g_free(temp); } + else + { + set_entry_tag_v1(GTK_ENTRY(v1_comment_entry), id3v1tag.u.v1_0.comment, 30); + gtk_entry_set_text(GTK_ENTRY(v1_tracknum_entry), ""); + } + gtk_list_select_item(GTK_LIST(GTK_COMBO(v1_genre_combo)->list), genre_find_index(genre_list, id3v1tag.genre)); + } + else + { + // Grey out id3v1 tab + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(v1_checkbox), TRUE); + } + rewind(fh); if (!mpg123_get_first_frame(fh, &frm, &buf)) --- Input/mpg123/id3.c +++ Input/mpg123/id3.c @@ -22,6 +22,7 @@ #include <sys/types.h> #include <sys/uio.h> +#include <sys/stat.h> #include <glib.h> #include <fcntl.h> #include <unistd.h> @@ -49,7 +50,7 @@ */ static int id3_seek_mem(struct id3_tag *id3, int offset) { - if (id3->id3_pos + offset > id3->id3_tagsize || + if (id3->id3_pos + offset > id3->id3_totalsize || id3->id3_pos + offset < 0) { id3_error(id3, "seeking beyond tag boundary"); @@ -77,7 +78,7 @@ /* * Check boundary. */ - if (id3->id3_pos + size > id3->id3_tagsize) + if (id3->id3_pos + size > id3->id3_totalsize) return NULL; /* @@ -118,7 +119,7 @@ /* * Check boundary. */ - if (id3->id3_pos + offset > id3->id3_tagsize || + if (id3->id3_pos + offset > id3->id3_totalsize || id3->id3_pos + offset < 0) return -1; @@ -148,8 +149,8 @@ /* * Check boundary. */ - if (id3->id3_pos + size > id3->id3_tagsize) - return NULL; + if (id3->id3_pos + size > id3->id3_totalsize) + size = id3->id3_totalsize - id3->id3_pos; /* * If buffer is NULL, we use the default buffer. @@ -205,7 +206,7 @@ /* * Check boundary. */ - if (id3->id3_pos + offset > id3->id3_tagsize || + if (id3->id3_pos + offset > id3->id3_totalsize || id3->id3_pos + offset < 0) return -1; @@ -263,8 +264,8 @@ /* * Check boundary. */ - if (id3->id3_pos + size > id3->id3_tagsize) - size = id3->id3_tagsize - id3->id3_pos; + if (id3->id3_pos + size > id3->id3_totalsize) + size = id3->id3_totalsize - id3->id3_pos; /* * If buffer is NULL, we use the default buffer. @@ -338,6 +339,32 @@ return NULL; } +/* + * Function id3_new() + * + * Creates a new ID3 tag structure. Useful for creating + * a new tag. + * + */ +struct id3_tag *id3_new() +{ + struct id3_tag *id3; + + /* + * Allocate ID3 structure. + */ + id3 = g_malloc0( sizeof(struct id3_tag) ); + + if (id3 != NULL) + { + id3_init_tag ( id3 ); + id3->id3_type = ID3_TYPE_NONE; + id3->id3_seek = NULL; + id3->id3_read = NULL; + } + + return id3; +} /* * Function id3_open_fd (fd, flags) @@ -493,7 +520,7 @@ if (id3->id3_newtag) return 0; else - return id3->id3_tagsize + 3 + sizeof(id3_taghdr_t); + return id3->id3_totalsize + 3 + sizeof(id3_taghdr_t); } #endif @@ -560,6 +587,81 @@ return 0; } +/* + * Function id3_remove_tag_filename(filename) + * + * Remove the ID3v2 tag from the file indicated by filename. Takes care of resizing + * the file, if needed. Returns 0 upon success, or -1 if an error occured. + * + */ +int id3_remove_tag_filename(const char* filename) +{ + struct id3_tag *current_id3; + int fd; + int current_totalsize; + struct stat stat_buf; + + fd = open(filename, O_RDWR); + if (fd == -1) + return -1; + + /* + * Figure out how large the current tag is. + */ + current_id3 = id3_open_fd(fd, 0); + if (current_id3 != NULL) + { + /* We use MAX to make sure an erroneous tag doesn't confuse us */ + current_totalsize = MAX(current_id3->id3_totalsize, 0); + id3_close(current_id3); + } + else + { + current_totalsize = 0; + } + + if (current_totalsize <= 0) + return 0; + + /* + * Rewrite the file. + */ + + stat(filename, &stat_buf); + + /* Re-position all the data current_totalsize bytes backwards */ + { + int read_pos, write_pos; + size_t read_size; + char buf[4096]; + + /* TODO: Add error handling to IO operations */ + + /* We reposition the data by going forwards, each time reading + a block and copying it backward. That way, we never write + over a block which we intend to read later. */ + write_pos = 0; + read_pos = write_pos + current_totalsize; + + do + { + lseek(fd, read_pos, SEEK_SET); + read_size = read(fd, buf, sizeof(buf)); + read_pos += read_size; + + /* Write whatever block we've got */ + lseek(fd, write_pos, SEEK_SET); + write(fd, buf, read_size); + write_pos += read_size; + } + while (read_size > 0); + + ftruncate(fd, stat_buf.st_size - current_totalsize); + } + + close(fd); + return 0; +} /* * Function id3_write_tag (id3, fd) @@ -567,30 +669,25 @@ * Wrtite the ID3 tag to the indicated file descriptor. Return 0 * upon success, or -1 if an error occured. * + * Warning: This function is called by id3_write_tag_filename and should + * not be called standalone, as it doesn't perform seeking + * and doesn't enlarge the file as necessary, and thus might + * OVERWRITE THE AUDIO DATA. + * */ int id3_write_tag(struct id3_tag *id3, int fd) { struct id3_frame *fr; GList *node; - int size = 0; char buf[ID3_TAGHDR_SIZE]; /* - * Calculate size of ID3 tag. - */ - for (node = id3->id3_frame; node != NULL; node = node->next) - { - fr = node->data; - size += fr->fr_size + ID3_FRAMEHDR_SIZE; - } - - /* * Write tag header. */ buf[0] = id3->id3_version; buf[1] = id3->id3_revision; buf[2] = id3->id3_flags; - ID3_SET_SIZE28(size, buf[3], buf[4], buf[5], buf[6]); + ID3_SET_SIZE28(id3->id3_size, buf[3], buf[4], buf[5], buf[6]); if (safe_write(fd, "ID3", 3) == -1) return -1; @@ -617,19 +714,203 @@ * TODO: Support compressed headers, encoded * headers, and grouping info. */ - /* fhdr.fh_id = fr->fr_desc ? g_htonl(fr->fr_desc->fd_id) : 0; */ - fhdr[3] = (fr->fr_size >> 24) & 0xff; - fhdr[4] = (fr->fr_size >> 16) & 0xff; - fhdr[5] = (fr->fr_size >> 8) & 0xff; - fhdr[6] = fr->fr_size & 0xff; - fhdr[7] = (fr->fr_flags >> 8) & 0xff; - fhdr[8] = fr->fr_flags & 0xff; + fhdr[0] = fr->fr_desc->fd_idstr[0]; + fhdr[1] = fr->fr_desc->fd_idstr[1]; + fhdr[2] = fr->fr_desc->fd_idstr[2]; + fhdr[3] = fr->fr_desc->fd_idstr[3]; + fhdr[4] = (fr->fr_raw_size >> 24) & 0xff; + fhdr[5] = (fr->fr_raw_size >> 16) & 0xff; + fhdr[6] = (fr->fr_raw_size >> 8) & 0xff; + fhdr[7] = fr->fr_raw_size & 0xff; + fhdr[8] = (fr->fr_flags >> 8) & 0xff; + fhdr[9] = fr->fr_flags & 0xff; if (safe_write(fd, fhdr, sizeof(fhdr)) == -1) return -1; - if (safe_write(fd, fr->fr_data, fr->fr_size) == -1) + if (safe_write(fd, fr->fr_raw_data, fr->fr_raw_size) == -1) return -1; } return 0; } + + +/* + * Function id3_write_tag_file(id3, filename) + * + * Write the ID3 tag to the file indicated by filename. Takes care of enlarging + * the file, if needed. Returns 0 upon success, or -1 if an error occured. + * + */ +int id3_write_tag_filename(struct id3_tag *id3, const char* filename) +{ + struct id3_tag *current_id3; + int fd; + int current_totalsize, new_totalsize; + GList* node; + + fd = open(filename, O_RDWR); + if (fd == -1) + return -1; + + /* + * Figure out how large the current tag is. + */ + current_id3 = id3_open_fd(fd, 0); + if (current_id3 != NULL) + { + /* We use MAX to make sure an erroneous tag doesn't confuse us */ + current_totalsize = MAX(current_id3->id3_totalsize, 0); + id3_close(current_id3); + } + else + { + current_totalsize = 0; + } + + /* + * Figure out how large the new tag will be. + */ + new_totalsize = 10; + node = id3->id3_frame; + while (node != NULL) + { + struct id3_frame* fr = node->data; + + { + char* text = id3_get_text(fr); + if (text != NULL) + { + int len = strlen(text); + g_free(text); + + if (len == 0) + { + /* We skip over frames with empty text */ + node = node->next; + id3_delete_frame(fr); + continue; + } + } + } + + { + char* url = id3_get_url(fr); + if (url != NULL) + { + int len = strlen(url); + g_free(url); + + if (len == 0) + { + /* We skip over frames with empty URLs */ + node = node->next; + id3_delete_frame(fr); + continue; + } + } + } + + new_totalsize += fr->fr_raw_size + ID3_FRAMEHDR_SIZE; + node = node->next; + } + new_totalsize += 0; /* no extended header, no footer, no padding */ + id3->id3_flags = 0; + + /* + * Determine whether we need to rewrite the file to make place for the new tag. + */ + if (new_totalsize > current_totalsize) + { + struct stat stat_buf; + int grow_size; + + stat(filename, &stat_buf); + grow_size = new_totalsize - current_totalsize; + ftruncate(fd, stat_buf.st_size + grow_size); + + /* truncate adds "sparse" zeros; we'll turn them into real ones, + which occupy space on disk, to make sure we actually have + the disk space before we start enlarging the file */ + { + int remaining = grow_size; + char buf[1024] = { 0 }; + lseek(fd, stat_buf.st_size, SEEK_SET); + while (remaining > 0) + { + int ret = write(fd, buf, MIN(sizeof(buf), remaining)); + if (ret >= 0) + remaining -= ret; + else + { + id3_error(id3, "Unable to enlarge file for the new tag"); + ftruncate(fd, stat_buf.st_size); + close(fd); + return -1; + } + } + } + + /* and now, re-position all the data grow_size bytes forward */ + { + int area_to_move_size = stat_buf.st_size - current_totalsize; + int read_pos, write_pos; + size_t read_size, read_size_desired; + char buf[4096]; + + /* TODO: Add error handling to IO operations */ + + /* We reposition the data by going backwards, each time reading + a block and copying it forward. That way, we never write + over a block which we intend to read later. */ + write_pos = lseek(fd, 0, SEEK_END); + read_pos = write_pos - grow_size; + + /* While we still have bytes to move... */ + while(area_to_move_size > 0) + { + /* Get 1 block or the remaining bytes -- the smallest of the two */ + read_size_desired = MIN(area_to_move_size, sizeof(buf)); + read_pos -= read_size_desired; + lseek(fd, read_pos, SEEK_SET); + read_size = read(fd, buf, read_size_desired); + + /* Write whatever block we've got */ + write_pos -= read_size; + lseek(fd, write_pos, SEEK_SET); + write(fd, buf, read_size); + + area_to_move_size -= read_size; + } + } + } + else + { + new_totalsize = current_totalsize; + } + + id3->id3_size = new_totalsize - 10; + + /* Zero-out the ID3v2 tag area */ + { + char buf[1024] = {0}; + size_t remaining; + + lseek(fd, 0, SEEK_SET); + for(remaining = new_totalsize; remaining > 0; remaining -= MIN(remaining,sizeof(buf))) + { + write(fd, buf, MIN(remaining,sizeof(buf))); + } + } + + /* Write the new tag */ + lseek(fd, 0, SEEK_SET); + if (id3_write_tag(id3, fd) == -1) + { + close(fd); + return -1; + } + + close(fd); + return 0; +} --- Input/mpg123/id3.h +++ Input/mpg123/id3.h @@ -22,6 +22,7 @@ #define ID3_H #include <glib.h> +#include <string.h> /* * Option flags to id3_open_*(). @@ -49,8 +50,9 @@ int id3_version; /* Major ID3 version number */ int id3_revision; /* ID3 revision number */ + int id3_size; /* Size of ID3 tag (as dictated by header) */ - int id3_tagsize; /* Total size of ID3 tag */ + int id3_totalsize; /* Total size of ID3 tag (including header, footer and padding) */ int id3_pos; /* Current position within tag */ char *id3_error_msg; /* Last error message */ @@ -140,8 +142,6 @@ #define ID3_ENCODING_UTF16BE 0x02 #define ID3_ENCODING_UTF8 0x03 - - /* * ID3 frame id numbers. */ @@ -322,50 +322,51 @@ */ /* From id3.c */ -struct id3_tag *id3_open_mem(void *, int); -struct id3_tag *id3_open_fd(int, int); -struct id3_tag *id3_open_fp(FILE *, int); -int id3_set_output(struct id3_tag *, char *); -int id3_close(struct id3_tag *); -int id3_tell(struct id3_tag *); -int id3_alter_file(struct id3_tag *); -int id3_write_tag(struct id3_tag *, int); +struct id3_tag *id3_new(); +struct id3_tag *id3_open_mem(void *ptr, int flags); +struct id3_tag *id3_open_fd(int fd, int flags); +struct id3_tag *id3_open_fp(FILE *fp, int flags); +int id3_close(struct id3_tag *id3); +int id3_tell(struct id3_tag *id3); +int id3_alter_file(struct id3_tag *id3); +int id3_write_tag(struct id3_tag *id3, int fd); +int id3_write_tag_filename(struct id3_tag *id3, const char* filename); +int id3_remove_tag_filename(const char* filename); /* From id3_frame.c */ int id3_read_frame(struct id3_tag *id3); -struct id3_frame *id3_get_frame(struct id3_tag *, guint32, int); +struct id3_frame *id3_get_frame(struct id3_tag *id3, guint32 type, int num); int id3_delete_frame(struct id3_frame *frame); -struct id3_frame *id3_add_frame(struct id3_tag *, guint32); -int id3_decompress_frame(struct id3_frame *); -void id3_destroy_frames(struct id3_tag *id); +struct id3_frame *id3_add_frame(struct id3_tag *id3, guint32 type); +struct id3_frame *id3_get_or_add_frame(struct id3_tag *id3, guint32 type); +int id3_decompress_frame(struct id3_frame *frame); +void id3_destroy_frames(struct id3_tag *id3); void id3_frame_clear_data(struct id3_frame *frame); /* From id3_frame_text.c */ guint id3_string_size(guint8 encoding, const char* text); char* id3_string_decode(guint8 encoding, const char* text); -gint8 id3_get_encoding(struct id3_frame *); -int id3_set_encoding(struct id3_frame *, gint8); -char *id3_get_text(struct id3_frame *); -char *id3_get_comment(struct id3_frame *); -char *id3_get_text_desc(struct id3_frame *); -int id3_get_text_number(struct id3_frame *); -int id3_set_text(struct id3_frame *, char *); -int id3_set_text_number(struct id3_frame *, int); +gint8 id3_get_encoding(struct id3_frame *frame); +int id3_set_encoding(struct id3_frame *frame, gint8 encoding); +char *id3_get_text(struct id3_frame *frame); +char *id3_get_comment(struct id3_frame *frame); +char *id3_get_text_desc(struct id3_frame *frame); +int id3_get_text_number(struct id3_frame *frame); +int id3_set_text(struct id3_frame *frame, char *text); +int id3_set_comment(struct id3_frame *frame, char *comment); +int id3_set_text_number(struct id3_frame *frame, int number); gboolean id3_frame_is_text(struct id3_frame *frame); /* From id3_frame_content.c */ -char *id3_get_content(struct id3_frame *); +char *id3_get_content(struct id3_frame *frame); /* From id3_frame_url.c */ -char *id3_get_url(struct id3_frame *); -char *id3_get_url_desc(struct id3_frame *); +char *id3_get_url(struct id3_frame *frame); +char *id3_get_url_desc(struct id3_frame *frame); +void id3_set_url(struct id3_frame *frame, const char* url); /* From id3_tag.c */ void id3_init_tag(struct id3_tag *id3); int id3_read_tag(struct id3_tag *id3); -char *convert_from_utf16(const unsigned char *utf16); -char *convert_from_utf16be(const unsigned char *utf16); - - #endif /* ID3_H */ --- Input/mpg123/id3_frame.c +++ Input/mpg123/id3_frame.c @@ -283,7 +283,7 @@ */ if (!((buf[0] >= '0' && buf[0] <= '9') || (buf[0] >= 'A' && buf[0] <= 'Z'))) { - id3->id3_seek(id3, id3->id3_tagsize - id3->id3_pos); + id3->id3_seek(id3, id3->id3_totalsize - id3->id3_pos); return 0; } id = ID3_FRAME_ID(buf[0], buf[1], buf[2], buf[3]); @@ -308,7 +308,7 @@ */ frame->fr_desc = find_frame_description(id); - + /* * Check if frame had a valid id. */ @@ -385,6 +385,29 @@ } /* + * Function id3_get_or_add_frame (id3, type) + * + * Search in the list of frames for the ID3-tag, and return the first frame + * of the indicated type. If no frame of the indicated type exists yet, + * it will add one and return it. + * + */ +struct id3_frame *id3_get_or_add_frame(struct id3_tag *id3, guint32 type) +{ + struct id3_frame* fr; + + fr = id3_get_frame(id3, type, 1); + if (fr != NULL) + { + return fr; + } + else + { + return id3_add_frame(id3, type); + } +} + +/* * Function decompress_frame(frame) * * Uncompress the indicated frame. Return 0 upon success, or -1 if @@ -517,27 +540,26 @@ */ int id3_delete_frame(struct id3_frame *frame) { - GList *list = frame->fr_owner->id3_frame; + struct id3_tag* id3 = frame->fr_owner; + GList *list = id3->id3_frame; int ret; /* * Search for frame in list. */ - if (g_list_find(list, frame) != NULL) - { - /* - * Frame does not exist in frame list. - */ - ret = -1; - } - else - { + if (g_list_find(list, frame) != NULL) { /* * Remove frame from frame list. */ list = g_list_remove(list, frame); - frame->fr_owner->id3_altered = 1; - ret = 0; + id3->id3_frame = list; + id3->id3_altered = 1; + ret = 0; + } else { + /* + * Frame does not exist in frame list. + */ + ret = -1; } /* @@ -704,7 +726,7 @@ */ if (!((buf[0] >= '0' && buf[0] <= '9') || (buf[0] >= 'A' && buf[0] <= 'Z'))) { - id3->id3_seek(id3, id3->id3_tagsize - id3->id3_pos); + id3->id3_seek(id3, id3->id3_size - id3->id3_pos); return 0; } --- Input/mpg123/id3_frame_text.c +++ Input/mpg123/id3_frame_text.c @@ -155,6 +155,11 @@ char *id3_get_text(struct id3_frame *frame) { int offset = 0; + + /* Do we even have data for this frame */ + if (!frame->fr_data) + return NULL; + /* Type check */ if (frame->fr_desc->fd_idstr[0] != 'T') return NULL; @@ -374,3 +379,54 @@ return id3_string_decode(ID3_TEXT_FRAME_ENCODING(frame), ID3_TEXT_FRAME_PTR(frame) + offset); } + +/* + * Function id3_set_comment (frame, text) + * + * Set text for the indicated frame. Return 0 upon + * success, or -1 if an error occured. + * + */ +int id3_set_comment(struct id3_frame *frame, char *text) +{ + int *intp; + + /* Type check */ + if (frame->fr_desc->fd_id != ID3_COMM) + return -1; + + /* + * Release memory occupied by previous data. + */ + id3_frame_clear_data(frame); + + /* + * Allocate memory for new data. + */ + frame->fr_raw_size = 13 + strlen(text); + frame->fr_raw_data = g_malloc(frame->fr_raw_size + 1); /* <encode>XXXComments\0<comment><\0> + + /* Save time... we just need to zero out the start, not the whole + * block, so don't waste time with a calloc() + */ + + ((guint8 *)frame->fr_raw_data)[0] = ID3_ENCODING_ISO_8859_1; + ((guint8 *)frame->fr_raw_data)[1] = 0x58; + ((guint8 *)frame->fr_raw_data)[2] = 0x58; + ((guint8 *)frame->fr_raw_data)[3] = 0x58; + + memcpy((char *) frame->fr_raw_data + 4, "Comments", 9); + + /* + * Copy contents. + */ + memcpy((char *) frame->fr_raw_data + 13, text, strlen(text) + 1); + + frame->fr_altered = 1; + frame->fr_owner->id3_altered = 1; + + frame->fr_data = frame->fr_raw_data; + frame->fr_size = frame->fr_raw_size; + + return 0; +} --- Input/mpg123/id3_frame_url.c +++ Input/mpg123/id3_frame_url.c @@ -34,6 +34,11 @@ char *id3_get_url(struct id3_frame *frame) { int offset = 0; + + /* Do we even have data for this frame */ + if (!frame->fr_data) + return NULL; + /* Type check */ if (frame->fr_desc->fd_idstr[0] != 'W') return NULL; @@ -80,3 +85,32 @@ return id3_string_decode(ID3_TEXT_FRAME_ENCODING(frame), ID3_TEXT_FRAME_PTR(frame)); } + +/* + * Function id3_set_url (frame) + * + * Sets URL of frame. + * + */ +void id3_set_url(struct id3_frame *frame, const char* url) +{ + /* Type check */ + if ( frame->fr_desc->fd_idstr[0] != 'W' ) + return; + + /* Check if frame is compressed */ + if (id3_decompress_frame(frame) == -1) + return; + + /* + * Allocate memory for new data. + */ + frame->fr_raw_size = strlen(url) + 1; + frame->fr_raw_data = g_malloc(frame->fr_raw_size + 1); + + /* + * Copy contents. + */ + *(gint8 *) frame->fr_raw_data = ID3_ENCODING_ISO_8859_1; + memcpy((char *) frame->fr_raw_data + 1, url, frame->fr_raw_size); +} --- Input/mpg123/id3_header.h +++ Input/mpg123/id3_header.h @@ -39,22 +39,21 @@ #define ID3_THFLAG_USYNC 0x80 #define ID3_THFLAG_EXT 0x40 #define ID3_THFLAG_EXP 0x20 +#define ID3_THFLAG_FOOTER 0x10 #define ID3_SET_SIZE28(size, a, b, c, d) \ -do { \ - a = (size >> (24 + 3)) & 0x7f; \ - b = (size >> (16 + 2)) & 0x7f; \ - c = (size >> ( 8 + 1)) & 0x7f; \ +{ \ + a = (size >> (24 - 3)) & 0x7f; \ + b = (size >> (16 - 2)) & 0x7f; \ + c = (size >> ( 8 - 1)) & 0x7f; \ d = size & 0x7f; \ -} while (0) +} #define ID3_GET_SIZE28(a, b, c, d) \ -(((a & 0x7f) << (24 - 3)) | \ - ((b & 0x7f) << (16 - 2)) | \ - ((c & 0x7f) << ( 8 - 1)) | \ - ((d & 0x7f))) - - + (((a & 0x7f) << (24 - 3)) | \ + ((b & 0x7f) << (16 - 2)) | \ + ((c & 0x7f) << ( 8 - 1)) | \ + ((d & 0x7f))) \ /* * Layout for the extended header. --- Input/mpg123/id3_tag.c +++ Input/mpg123/id3_tag.c @@ -39,7 +39,8 @@ id3->id3_version = 3; id3->id3_revision = 0; id3->id3_flags = ID3_THFLAG_USYNC | ID3_THFLAG_EXP; - id3->id3_tagsize = 0; + id3->id3_size = 0; + id3->id3_totalsize = 0; id3->id3_altered = 1; id3->id3_newtag = 1; @@ -63,13 +64,14 @@ int id3_read_tag(struct id3_tag *id3) { char *buf; + guint8 padding; /* * We know that the tag will be at least this big. * * tag header + "ID3" */ - id3->id3_tagsize = ID3_TAGHDR_SIZE + 3; + id3->id3_totalsize = ID3_TAGHDR_SIZE + 3; if (!(id3->id3_oflags & ID3_OPENF_NOCHK)) { @@ -100,9 +102,11 @@ id3->id3_version = buf[0]; id3->id3_revision = buf[1]; id3->id3_flags = buf[2]; - id3->id3_tagsize = ID3_GET_SIZE28(buf[3], buf[4], buf[5], buf[6]); + id3->id3_size = ID3_GET_SIZE28(buf[3], buf[4], buf[5], buf[6]); + id3->id3_totalsize += id3->id3_size; + if (id3->id3_flags & ID3_THFLAG_FOOTER) + id3->id3_totalsize += 10; id3->id3_newtag = 0; - id3->id3_pos = 0; if (id3->id3_version < 2 || id3->id3_version > 4) return -1; @@ -120,14 +124,34 @@ /* * Parse frames. */ - while (id3->id3_pos < id3->id3_tagsize) + while (id3->id3_pos < id3->id3_size) { if (id3_read_frame(id3) == -1) return -1; } - if (id3->id3_frame == NULL) - return -1; + /* + * Like id3lib, we try to find unstandard padding (not within + * the tag size). This is important to know when we strip + * the tag or replace it. + * Another option might be looking for an MPEG sync, but we don't do it. + */ + + id3->id3_seek(id3, id3->id3_totalsize - id3->id3_pos); + + /* Temporarily increase totalsize, to try reading beyong the boundary */ + ++id3->id3_totalsize; + + while (id3->id3_read(id3, &padding, sizeof(padding)) != NULL) + { + if (padding == 0) + ++id3->id3_totalsize; + else + break; + } + + /* Decrease totalsize after we temporarily increased it */ + --id3->id3_totalsize; return 0; } --- Input/mpg123/mpg123.c +++ Input/mpg123/mpg123.c @@ -131,9 +131,9 @@ #ifdef USE_SIMD fr->dct36 = funcs_dct36[0]; - + if (CPU_HAS_3DNOW() && !p8 && - (mpg123_cfg.default_synth == SYNTH_3DNOW || + (mpg123_cfg.default_synth == SYNTH_3DNOW || mpg123_cfg.default_synth == SYNTH_AUTO)) { fr->synth = funcs[3][ds]; /* 3DNow! optimized synth_1to1() */ @@ -322,11 +322,11 @@ if (!strncasecmp(filename, "http://", 7)) { /* We assume all http:// (except those ending in .ogg) are mpeg -- why do we do that? */ ext = strrchr(filename, '.'); - if (ext) + if (ext) { - if (!strncasecmp(ext, ".ogg", 4)) + if (!strncasecmp(ext, ".ogg", 4)) return FALSE; - if (!strncasecmp(ext, ".rm", 3) || + if (!strncasecmp(ext, ".rm", 3) || !strncasecmp(ext, ".ra", 3) || !strncasecmp(ext, ".rpm", 4) || !strncasecmp(ext, ".fla", 4) || @@ -536,7 +536,7 @@ * Function mpg123_get_id3v2 (id3d, tag) * * Get desired contents from the indicated id3tag and store it in - * `tag'. + * `tag'. * */ struct id3v2tag_t* mpg123_id3v2_get(struct id3_tag *id3d) @@ -552,6 +552,10 @@ tag->track_number = id3v2_get_num(id3d, ID3_TRCK); tag->comment = id3v2_get_text(id3d, ID3_COMM); tag->genre = id3v2_get_text(id3d, ID3_TCON); + tag->composer = id3v2_get_text(id3d, ID3_TCOM); + tag->orig_artist = id3v2_get_text(id3d, ID3_TOPE); + tag->url = id3v2_get_text(id3d, ID3_WCOM); + tag->encoded_by = id3v2_get_text(id3d, ID3_TENC); return tag; } @@ -824,7 +828,7 @@ static int mpg123_seek(struct frame *fr, xing_header_t *xh, gboolean vbr, int time) { int jumped = -1; - + if (xh) { int percent = ((double) time * 100.0) / @@ -991,7 +995,7 @@ mpg123_info->output_audio = FALSE; continue; } - + } } if(mpg123_freqs[fr.sampling_frequency] != mpg123_frequency || mpg123_stereo != fr.stereo) @@ -1029,12 +1033,12 @@ mpg123_info->output_audio = FALSE; continue; } - } + } } - + if (tabsel_123[fr.lsf][fr.lay - 1][fr.bitrate_index] != mpg123_bitrate) mpg123_bitrate = tabsel_123[fr.lsf][fr.lay - 1][fr.bitrate_index]; - + if (!disp_count) { disp_count = 20; @@ -1153,7 +1157,7 @@ if (aboutbox != NULL) return; - + aboutbox = xmms_show_message( _("About MPEG Layer 1/2/3 plugin"), _("mpg123 decoding engine by Michael Hipp <mh@mpg123.de>\n" --- Input/mpg123/mpg123.h +++ Input/mpg123/mpg123.h @@ -1,5 +1,5 @@ /* - * mpg123 defines + * mpg123 defines * used source: musicout.h from mpegaudio package */ @@ -79,6 +79,10 @@ char *album; char *comment; char *genre; + char *composer; + char *orig_artist; + char *url; + char *encoded_by; int year; int track_number; }; @@ -300,7 +304,6 @@ int mpg123_decode_header(struct frame *fr, unsigned long newhead); double mpg123_compute_bpf(struct frame *fr); double mpg123_compute_tpf(struct frame *fr); -guint mpg123_strip_spaces(char *src, size_t n); struct id3v2tag_t* mpg123_id3v2_get(struct id3_tag *id3d); void mpg123_id3v2_destroy(struct id3v2tag_t* tag); char *mpg123_format_song_title(struct id3v2tag_t *tag, char *filename);