commit 400e3393ae7c565ecaca350ef3e96d73e6e0bd18 Author: phantomjinx <p.g.richard...@phantomjinx.co.uk> Date: Fri Dec 9 22:01:25 2011 +0000
Fix for restoring functionality of track normalization * Ensure that internal soundcheck functions populate a GError rather than displaying a warning dialog. The latter does not work well inside threads! * Save up all error messages until the end of the normalization run * Use the platform progress bar rather than bringing up a custom dialog * Provide the action for normalizing a playlist to the playlist plugins menus. libgtkpod/file.c | 26 ++-- libgtkpod/file.h | 2 +- libgtkpod/tools.c | 221 ++++++++++---------- plugins/playlist_display/playlist_display.ui | 1 + .../playlist_display/playlist_display_actions.c | 12 + .../playlist_display/playlist_display_actions.h | 1 + plugins/playlist_display/plugin.c | 8 + plugins/track_display/plugin.c | 12 +- 8 files changed, 153 insertions(+), 130 deletions(-) --- diff --git a/libgtkpod/file.c b/libgtkpod/file.c index 6551145..7f3b3cc 100644 --- a/libgtkpod/file.c +++ b/libgtkpod/file.c @@ -2056,35 +2056,35 @@ void parse_offline_playcount(void) { * * Return value: TRUE, if gain could be read */ -gboolean read_soundcheck(Track *track) { - gchar *path; +gboolean read_soundcheck(Track *track, GError **error) { + gchar *path, *buf; FileType *filetype; gboolean result = FALSE; - GError *error = NULL; - gchar *msg = g_strdup_printf(_("Failed to read sound check from track because")); g_return_val_if_fail (track, FALSE); path = get_file_name_from_source(track, SOURCE_PREFER_LOCAL); + if (!path) { + buf = g_strdup_printf(_("Failed to read sound check from track with no path setting.")); + gtkpod_log_error(error, buf); + g_free(buf); + return FALSE; + } + filetype = determine_filetype(path); if (! filetype) { - gtkpod_warning(_("%s\n\nfiletype of %s is not recognised."), msg, path); + buf = g_strdup_printf(_("Failed to read sound check from track because filetype is not recognised.")); + gtkpod_log_error(error, buf); + g_free(buf); } else { - if (!filetype_read_soundcheck(filetype, path, track, &error)) { - if (error) { - gtkpod_warning(_("%s\n\n%s"), msg, error->message); - } else { - gtkpod_warning(_("%s\n\n%s"), msg, UNKNOWN_ERROR); - } - } else { + if (filetype_read_soundcheck(filetype, path, track, error)) { // track read successfully result = TRUE; } } g_free(path); - g_free(msg); return result; } diff --git a/libgtkpod/file.h b/libgtkpod/file.h index 01ba239..e62e71b 100644 --- a/libgtkpod/file.h +++ b/libgtkpod/file.h @@ -89,7 +89,7 @@ void gp_info_deleted_tracks (iTunesDB *itdb, void update_charset_info (Track *track); void parse_offline_playcount (void); -gboolean read_soundcheck (Track *track); +gboolean read_soundcheck (Track *track, GError **error); gboolean read_lyrics_from_file (Track *track, gchar **lyrics); gboolean write_lyrics_to_file (Track *track); diff --git a/libgtkpod/tools.c b/libgtkpod/tools.c index 0fa6e19..ae90ad8 100644 --- a/libgtkpod/tools.c +++ b/libgtkpod/tools.c @@ -33,6 +33,7 @@ #include "misc.h" #include "misc_track.h" #include "prefs.h" +#include "gp_private.h" #include "tools.h" #include <errno.h> #include <fcntl.h> @@ -44,6 +45,12 @@ #include <unistd.h> #include <glib/gi18n-lib.h> +/* Structure to keep all necessary information */ +struct nm { + Track *track; /* track to be normalised */ + GError *error; /* Errors generated during the normalisation */ +}; + /*pipe's definition*/ enum { READ = 0, WRITE = 1 @@ -75,10 +82,11 @@ static gboolean mutex_data = FALSE; * Return value: TRUE if the command ran successfully, FALSE if any * error occurred. */ -static gboolean run_exec_on_track(const gchar *commandline, const gchar *track_path) { +static gboolean run_exec_on_track(const gchar *commandline, const gchar *track_path, GError **error) { gchar *command_full_path = NULL; gchar *command = NULL; gchar *command_base = NULL; + gchar *buf; const gchar *nextarg; gboolean success = FALSE; gboolean percs = FALSE; @@ -105,7 +113,9 @@ static gboolean run_exec_on_track(const gchar *commandline, const gchar *track_p command_full_path = g_find_program_in_path(command); if (!command_full_path) { - gtkpod_warning(_("Could not find '%s'.\nPlease specifiy the exact path in the Tools section of the preference dialog or install the program if it is not installed on your system.\n\n"), command); + buf = g_strdup_printf(_("Could not find '%s'.\nPlease specifiy the exact path in the Tools section of the preference dialog or install the program if it is not installed on your system.\n\n"), command); + gtkpod_log_error(error, buf); + g_free(buf); goto cleanup; } @@ -187,7 +197,9 @@ static gboolean run_exec_on_track(const gchar *commandline, const gchar *track_p else ret = 2; if (ret > 1) { - gtkpod_warning(_("Execution of '%s' failed.\n\n"), command_full_path); + buf = g_strdup_printf(_("Execution of '%s' failed.\n\n"), command_full_path); + gtkpod_log_error(error, buf); + g_free(buf); } else { success = TRUE; @@ -203,38 +215,44 @@ static gboolean run_exec_on_track(const gchar *commandline, const gchar *track_p } /* reread the soundcheck value from the file */ -static gboolean nm_get_soundcheck(Track *track) { - gchar *path; +static gboolean nm_get_soundcheck(Track *track, GError **error) { + gchar *path, *buf; gchar *commandline = NULL; FileType *filetype; g_return_val_if_fail (track, FALSE); - if (read_soundcheck(track)) + if (read_soundcheck(track, error)) return TRUE; + if (error && *error) + return FALSE; + path = get_file_name_from_source(track, SOURCE_PREFER_LOCAL); filetype = determine_filetype(path); if (!path || !filetype) { - gchar *buf = get_track_info(track, FALSE); - gtkpod_warning(_("Normalization failed: file not available (%s).\n\n"), buf); + buf = get_track_info(track, FALSE); + buf = g_strdup_printf(_("Normalization failed: file not available (%s)."), buf); + gtkpod_log_error(error, buf); g_free(buf); return FALSE; } commandline = filetype_get_gain_cmd(filetype); if (commandline) { - if (run_exec_on_track(commandline, path)) { + if (run_exec_on_track(commandline, path, error)) { g_free(path); - return read_soundcheck(track); + return read_soundcheck(track, error); } } else { - gtkpod_warning(_("Normalization failed for file %s: file type not supported.\n" - "To normalize mp3 and aac files ensure the following commands paths have been set in the Tools section\n" - "\tmp3 files: mp3gain\n" + buf = g_strdup_printf(_("Normalization failed for file %s: file type not supported." + "To normalize mp3 and aac files ensure the following commands paths have been set in the Tools section" + "\tmp3 files: mp3gain" "\taac files: aacgain"), path); + gtkpod_log_error(error, buf); + g_free(buf); } return FALSE; @@ -242,8 +260,9 @@ static gboolean nm_get_soundcheck(Track *track) { #ifdef G_THREADS_ENABLED /* Threaded getTrackGain*/ -static gpointer th_nm_get_soundcheck(gpointer track) { - gboolean success = nm_get_soundcheck((Track *) track); +static gpointer th_nm_get_soundcheck(gpointer data) { + struct nm *nm = data; + gboolean success = nm_get_soundcheck(nm->track, &(nm->error)); g_mutex_lock (mutex); mutex_data = TRUE; /* signal that thread will end */ g_cond_signal (cond); @@ -270,20 +289,36 @@ void nm_new_tracks(iTunesDB *itdb) { g_list_free(tracks); } -static void normalization_abort(gboolean *abort) { - *abort = TRUE; +static void nm_report_errors_and_free(GString *errors) { + if (errors && errors->len > 0) { + gtkpod_confirmation(-1, /* gint id, */ + TRUE, /* gboolean modal, */ + _("Normalization Errors"), /* title */ + _("Errors created by track normalisation"), /* label */ + errors->str, /* scrolled text */ + NULL, 0, NULL, /* option 1 */ + NULL, 0, NULL, /* option 2 */ + TRUE, /* gboolean confirm_again, */ + "show_normalization_errors",/* confirm_again_key,*/ + CONF_NULL_HANDLER, /* ConfHandler ok_handler,*/ + NULL, /* don't show "Apply" button */ + NULL, /* cancel_handler,*/ + NULL, /* gpointer user_data1,*/ + NULL); /* gpointer user_data2,*/ + + g_string_free(errors, TRUE); + } } void nm_tracks_list(GList *list) { gint count, succ_count, n; + gdouble fraction = 0; + gdouble old_fraction = 0; guint32 old_soundcheck; gboolean success; - static gboolean abort; - GtkWidget *dialog, *progress_bar, *label, *track_label; - GtkWidget *image, *hbox; - GtkWidget *content_area; - time_t diff, start, fullsecs, hrs, mins, secs; gchar *progtext = NULL; + struct nm *nm; + GString *errors = g_string_new(""); /* Errors generated during the normalisation */ #ifdef G_THREADS_ENABLED GThread *thread = NULL; @@ -296,46 +331,6 @@ void nm_tracks_list(GList *list) { block_widgets(); - /* create the dialog window */ - dialog - = gtk_dialog_new_with_buttons(_("Information"), GTK_WINDOW (gtkpod_app), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL, GTK_RESPONSE_NONE, NULL); - - /* emulate gtk_message_dialog_new */ - image = gtk_image_new_from_stock(GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_DIALOG); - label = gtk_label_new(_("Press button to abort.")); - - gtk_misc_set_alignment(GTK_MISC (image), 0.5, 0.0); - gtk_label_set_line_wrap(GTK_LABEL (label), TRUE); - gtk_label_set_selectable(GTK_LABEL (label), TRUE); - - /* hbox to put the image+label in */ - hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); - gtk_box_pack_start(GTK_BOX (hbox), image, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX (hbox), label, FALSE, FALSE, 0); - - /* Create the progress bar */ - progress_bar = gtk_progress_bar_new(); - progtext = g_strdup(_("Normalizing...")); - gtk_progress_bar_set_text(GTK_PROGRESS_BAR (progress_bar), progtext); - g_free(progtext); - - /* Create label for track name */ - track_label = gtk_label_new(NULL); - gtk_label_set_line_wrap(GTK_LABEL (label), TRUE); - gtk_label_set_selectable(GTK_LABEL (label), TRUE); - - /* Indicate that user wants to abort */ - g_signal_connect_swapped (G_OBJECT (dialog), "response", - G_CALLBACK (normalization_abort), - &abort); - - /* Add the image/label + progress bar to dialog */ - content_area = gtk_dialog_get_content_area(GTK_DIALOG (dialog)); - gtk_box_pack_start(GTK_BOX (content_area), hbox, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX (content_area), track_label, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX (content_area), progress_bar, FALSE, FALSE, 0); - gtk_widget_show_all(dialog); - while (widgets_blocked && gtk_events_pending()) gtk_main_iteration(); @@ -343,55 +338,36 @@ void nm_tracks_list(GList *list) { n = g_list_length(list); count = 0; /* tracks processed */ succ_count = 0; /* tracks normalized */ - abort = FALSE; - start = time(NULL); if (n == 0) { - /* FIXME we should tell something*/ - - } - else { - /* we need ***much*** longer timeout */ - g_message("TODO tools:nm_tracks_list - statusbar\n"); - // gtkpod_statusbar_timeout (30*STATUSBAR_TIMEOUT); + // nothing to do + return; } - while (!abort && (list != NULL)) { - Track *track = list->data; - gchar *label_buf = g_strdup_printf("%d/%d", count, n); - gtk_label_set_text(GTK_LABEL (track_label), label_buf); + gtkpod_statusbar_reset_progress(100); - g_message("TODO tools:nm_tracks_list - statusbar\n"); - // gtkpod_statusbar_message (_("%s - %s"), - // track->artist, track->title); - C_FREE (label_buf); + nm = g_malloc0(sizeof(struct nm)); + while (list) { + nm->track = list->data; + nm->error = NULL; while (widgets_blocked && gtk_events_pending()) gtk_main_iteration(); /* need to know so we can update the display when necessary */ - old_soundcheck = track->soundcheck; + old_soundcheck = nm->track->soundcheck; #ifdef G_THREADS_ENABLED mutex_data = FALSE; - thread = g_thread_create (th_nm_get_soundcheck, track, TRUE, NULL); + + thread = g_thread_create (th_nm_get_soundcheck, nm, TRUE, NULL); if (thread) { - gboolean first_abort = TRUE; g_mutex_lock (mutex); do { while (widgets_blocked && gtk_events_pending()) gtk_main_iteration(); /* wait a maximum of 10 ms */ - if (abort && first_abort) { - first_abort = FALSE; - progtext = g_strdup(_("Aborting...")); - gtk_progress_bar_set_text(GTK_PROGRESS_BAR (progress_bar), progtext); - g_free(progtext); - gtkpod_statusbar_message(_("Will abort after current mp3gain process ends.")); - while (widgets_blocked && gtk_events_pending()) - gtk_main_iteration(); - } g_get_current_time(>ime); g_time_val_add(>ime, 20000); g_cond_timed_wait (cond, mutex, >ime); @@ -402,54 +378,69 @@ void nm_tracks_list(GList *list) { } else { g_warning ("Thread creation failed, falling back to default.\n"); - success = nm_get_soundcheck(track); + success = nm_get_soundcheck(nm->track, &(nm->error)); + } #else - success = nm_get_soundcheck (track); + success = nm_get_soundcheck (nm->track, nm->error); #endif /*normalization part*/ if (!success) { - gchar *path = get_file_name_from_source(track, SOURCE_PREFER_LOCAL); - gtkpod_warning(_("'%s-%s' (%s) could not be normalized.\n\n"), track->artist, track->title, path ? path : ""); + gchar *path = get_file_name_from_source(nm->track, SOURCE_PREFER_LOCAL); + + if (nm->error) { + errors = g_string_append(errors, g_strdup_printf(_("'%s-%s' (%s) could not be normalized. %s\n"), nm->track->artist, nm->track->title, path ? path : "", nm->error->message)); + } + else { + errors = g_string_append(errors, g_strdup_printf(_("'%s-%s' (%s) could not be normalized. Unknown error.\n"), nm->track->artist, nm->track->title, path ? path : "")); + } + g_free(path); } else { ++succ_count; - if (old_soundcheck != track->soundcheck) { - gtkpod_track_updated(track); - data_changed(track->itdb); + if (old_soundcheck != nm->track->soundcheck) { + gtkpod_track_updated(nm->track); + data_changed(nm->track->itdb); } } /*end normalization*/ ++count; - gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR (progress_bar), (gdouble) count / n); - - diff = time(NULL) - start; - fullsecs = (diff * n / count) - diff; - hrs = fullsecs / 3600; - mins = (fullsecs % 3600) / 60; - secs = ((fullsecs % 60) / 5) * 5; - /* don't bounce up too quickly (>10% change only) */ - /* left = ((mins < left) || (100*mins >= 110*left)) ? mins : left;*/ - progtext = g_strdup_printf(_("%d%% (%d:%02d:%02d left)"), count * 100 / n, (int) hrs, (int) mins, (int) secs); - gtk_progress_bar_set_text(GTK_PROGRESS_BAR (progress_bar), progtext); + fraction = (gdouble) count / (gdouble) n; + progtext = g_strdup_printf(_("%d%% (%d tracks left)"), count * 100 / n, n - count); + + gdouble ticks = fraction - old_fraction; + gtkpod_statusbar_increment_progress_ticks(ticks * 100, progtext); + + old_fraction = fraction; g_free(progtext); + if (fraction == 1) { + /* All finished */ + gtkpod_statusbar_reset_progress(100); + gtkpod_statusbar_message(ngettext ("Normalized %d of %d track.", "Normalized %d of %d tracks.", n), count, n); + } + while (widgets_blocked && gtk_events_pending()) gtk_main_iteration(); + list = g_list_next(list); + + if (nm->error) + g_error_free(nm->error); + } /*end while*/ - g_message("TODO tools:nm_tracks_list - statusbar\n"); - // gtkpod_statusbar_timeout (0); + g_free(nm); + + nm_report_errors_and_free(errors); - // gtkpod_statusbar_message (ngettext ("Normalized %d of %d tracks.", - // "Normalized %d of %d tracks.", n), - // count, n); + gtkpod_statusbar_message (ngettext ("Normalized %d of %d tracks.", + "Normalized %d of %d tracks.", n), + count, n); - gtk_widget_destroy(dialog); release_widgets(); } diff --git a/plugins/playlist_display/playlist_display.ui b/plugins/playlist_display/playlist_display.ui index 1b22e0d..178901c 100644 --- a/plugins/playlist_display/playlist_display.ui +++ b/plugins/playlist_display/playlist_display.ui @@ -36,6 +36,7 @@ <menuitem name="Selected Playlist" action="ActionUpdatePlaylist" /> </menu> <menuitem name="Sync Playlist with Dir(s)" action="ActionSyncPlaylistWithDir"/> + <menuitem name="Normalize Playlist(s)" action="ActionNormalizePlaylist"/> </placeholder> </menu> <menu name="MenuEdit" action="ActionMenuEdit"> diff --git a/plugins/playlist_display/playlist_display_actions.c b/plugins/playlist_display/playlist_display_actions.c index 8f34f17..71e42de 100644 --- a/plugins/playlist_display/playlist_display_actions.c +++ b/plugins/playlist_display/playlist_display_actions.c @@ -43,6 +43,7 @@ #include "libgtkpod/misc_playlist.h" #include "libgtkpod/file.h" #include "libgtkpod/syncdir.h" +#include "libgtkpod/tools.h" #include <gdk/gdk.h> /* Callback after directories to add have been selected */ @@ -703,3 +704,14 @@ void on_sync_playlists_with_dirs(GtkAction *action, PlaylistDisplayPlugin* plugi playlists = playlists->next; } } + +void on_normalize_selected_playlist (GtkMenuItem *menuitem, gpointer user_data) { + GList *playlists = pm_get_selected_playlists(); + while (playlists) { + Playlist *pl = playlists->data; + if (pl) { + nm_tracks_list (pl->members); + } + playlists = playlists->next; + } +} diff --git a/plugins/playlist_display/playlist_display_actions.h b/plugins/playlist_display/playlist_display_actions.h index 5d7b6d2..c1f6fc2 100644 --- a/plugins/playlist_display/playlist_display_actions.h +++ b/plugins/playlist_display/playlist_display_actions.h @@ -69,5 +69,6 @@ void on_delete_selected_playlists_including_tracks_from_device(GtkAction *action void on_update_selected_playlists (GtkAction *action, PlaylistDisplayPlugin* plugin); void on_sync_playlists_with_dirs(GtkAction *action, PlaylistDisplayPlugin* plugin); +void on_normalize_selected_playlist (GtkMenuItem *menuitem, gpointer user_data); #endif diff --git a/plugins/playlist_display/plugin.c b/plugins/playlist_display/plugin.c index 407c55c..9ef7016 100644 --- a/plugins/playlist_display/plugin.c +++ b/plugins/playlist_display/plugin.c @@ -119,6 +119,14 @@ static GtkActionEntry playlist_actions[] = G_CALLBACK (on_sync_playlists_with_dirs) }, { + "ActionNormalizePlaylist", + GTK_STOCK_EXECUTE, + N_("Normalize"), + NULL, + NULL, + G_CALLBACK (on_normalize_selected_playlist) + }, + { ACTION_NEW_PLAYLIST_MENU, NULL, N_("_New Playlist"), diff --git a/plugins/track_display/plugin.c b/plugins/track_display/plugin.c index 69b0634..a0e1e65 100644 --- a/plugins/track_display/plugin.c +++ b/plugins/track_display/plugin.c @@ -36,6 +36,7 @@ #include "libgtkpod/gtkpod_app_iface.h" #include "libgtkpod/gp_private.h" #include "libgtkpod/prefs.h" +#include "libgtkpod/tools.h" #include "plugin.h" #include "display_tracks.h" #include "track_display_actions.h" @@ -135,6 +136,8 @@ static gboolean activate_track_display_plugin(AnjutaPlugin *plugin) { gtk_widget_show_all(track_display_plugin->track_window); anjuta_shell_add_widget(plugin->shell, track_display_plugin->track_window, "TrackDisplayPlugin", _(" Playlist Tracks"), NULL, ANJUTA_SHELL_PLACEMENT_TOP, NULL); + gtkpod_register_track_command(TRACK_COMMAND(track_display_plugin)); + return TRUE; /* FALSE if activation failed */ } @@ -211,8 +214,15 @@ static void ipreferences_iface_init(IAnjutaPreferencesIface* iface) { iface->unmerge = ipreferences_unmerge; } +static void track_command_iface_init(TrackCommandInterface *iface) { + iface->id = "track_display_normalise_track_command"; + iface->text = _("Normalise"); + iface->execute = nm_tracks_list; +} + ANJUTA_PLUGIN_BEGIN (TrackDisplayPlugin, track_display_plugin); - ANJUTA_PLUGIN_ADD_INTERFACE(ipreferences, IANJUTA_TYPE_PREFERENCES);ANJUTA_PLUGIN_END +ANJUTA_PLUGIN_ADD_INTERFACE(track_command, TRACK_COMMAND_TYPE); +ANJUTA_PLUGIN_ADD_INTERFACE(ipreferences, IANJUTA_TYPE_PREFERENCES);ANJUTA_PLUGIN_END ; ANJUTA_SIMPLE_PLUGIN (TrackDisplayPlugin, track_display_plugin) ------------------------------------------------------------------------------ Learn Windows Azure Live! Tuesday, Dec 13, 2011 Microsoft is holding a special Learn Windows Azure training event for developers. It will provide a great way to learn Windows Azure and what it provides. You can attend the event by watching it streamed LIVE online. Learn more at http://p.sf.net/sfu/ms-windowsazure _______________________________________________ gtkpod-cvs2 mailing list gtkpod-cvs2@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/gtkpod-cvs2