From 84550b2c548ed4238dca2fc6a839c11bb3f41460 Mon Sep 17 00:00:00 2001 From: Rov Juvano Date: Wed, 28 May 2008 21:46:05 -0400 Subject: [PATCH] Add advanced controls - Add advanced scaletempo controls - Add media playback button bar - Add file open dialog - Add playlist controls - Group menu items into submenus - Move menu to window - Add timeout parameter for status_bar - Toggle visibility of play and pause actions - Allow playlist to contain filenames --- src/demo-gui.c | 461 ++++++++++++++++++++++++++++++++++++++++++++++-------- src/demo-main.c | 23 +-- src/demo-player.c | 116 +++++++++++--- 3 files changed, 504 insertions(+), 96 deletions(-) diff --git a/src/demo-gui.c b/src/demo-gui.c index 31e287c..bf77410 100644 --- a/src/demo-gui.c +++ b/src/demo-gui.c @@ -17,6 +17,7 @@ #include #include +#include #include "demo-gui.h" #undef G_LOG_DOMAIN @@ -48,6 +49,10 @@ typedef struct _DemoGuiPrivate GtkRange *seek_range; GtkLabel *amount_played; GtkLabel *amount_to_play; + GtkAction *play_action; + GtkAction *pause_action; + GtkAction *open_file; + GtkAction *playlist_next; } DemoGuiPrivate; #define DEMO_GUI_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), DEMO_TYPE_GUI, DemoGuiPrivate)) @@ -66,9 +71,12 @@ pop_status_bar (gpointer data) { return FALSE; } +#define DEFAULT_STATUS_BAR_TIMEOUT 2 static void status_bar_printf (GtkStatusbar *sb, - gchar const *format, ...) + guint seconds, + gchar const *format, + ...) { va_list args; gchar msg[80]; @@ -133,7 +141,7 @@ demo_gui_seek_bar_change (GtkRange *range, DemoGuiPrivate *priv = DEMO_GUI_GET_PRIVATE (gui); gint new_second = (gint)value; - status_bar_printf (priv->status_bar, "Seeking to %i second", new_second); + status_bar_printf (priv->status_bar, DEFAULT_STATUS_BAR_TIMEOUT, "Seeking to %i second", new_second); demo_player_seek_to (priv->player, new_second); return FALSE; @@ -150,24 +158,63 @@ demo_gui_do_change_rate (GtkAction *action, gdouble scale_amount = g_value_get_double (g_value_array_get_nth (gvalues, 1)); DemoGuiPrivate *priv = DEMO_GUI_GET_PRIVATE (gui); - status_bar_printf (priv->status_bar, "Changing rate by %3.2lf", scale_amount); + status_bar_printf (priv->status_bar, DEFAULT_STATUS_BAR_TIMEOUT, "Changing rate by %3.2lf", scale_amount); demo_player_scale_rate (priv->player, scale_amount); } static void +demo_gui_do_set_rate (GtkAction *action, + gpointer data) +{ + GValueArray *gvalues = (GValueArray *)data; + DemoGui *gui = g_value_get_object (g_value_array_get_nth (gvalues, 0)); + gdouble new_rate = g_value_get_double (g_value_array_get_nth (gvalues, 1)); + DemoGuiPrivate *priv = DEMO_GUI_GET_PRIVATE (gui); + + status_bar_printf (priv->status_bar, DEFAULT_STATUS_BAR_TIMEOUT, "Setting rate to %3.2lf", new_rate); + + demo_player_set_rate (priv->player, new_rate); +} + +static gboolean demo_gui_do_rate_entered (GtkWidget *widget, gpointer data) { DemoGui *gui = DEMO_GUI (data); DemoGuiPrivate *priv = DEMO_GUI_GET_PRIVATE (gui); - double new_rate; - - sscanf (gtk_entry_get_text (GTK_ENTRY (widget)), "%lf", &new_rate); + gchar *err = NULL; + const gchar *text = gtk_entry_get_text (GTK_ENTRY (widget)); + double new_rate = g_strtod (text, &err); + if (*err) { + gtk_widget_error_bell (priv->window); + status_bar_printf (priv->status_bar, DEFAULT_STATUS_BAR_TIMEOUT, "Invalid rate: %s", text); + return TRUE; + } - status_bar_printf (priv->status_bar, "Setting rate to %3.2lf", new_rate); + status_bar_printf (priv->status_bar, DEFAULT_STATUS_BAR_TIMEOUT, "Setting rate to %3.2lf", new_rate); demo_player_set_rate (priv->player, new_rate); + return FALSE; +} + +static void +demo_gui_do_toggle_advanced (GtkAction *action, + gpointer data) +{ + GValueArray *gvalues = (GValueArray *)data; + DemoGui *gui = DEMO_GUI (g_value_get_object (g_value_array_get_nth (gvalues, 0))); + GtkWidget *stride_ui = GTK_WIDGET (g_value_get_object (g_value_array_get_nth (gvalues, 1))); + GtkWidget *overlap_ui = GTK_WIDGET (g_value_get_object (g_value_array_get_nth (gvalues, 2))); + GtkWidget *search_ui = GTK_WIDGET (g_value_get_object (g_value_array_get_nth (gvalues, 3))); + DemoGuiPrivate *priv = DEMO_GUI_GET_PRIVATE (gui); + + status_bar_printf (priv->status_bar, 1, "Toggling advanced mode"); + + gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)); + gtk_widget_set_sensitive (stride_ui, active); + gtk_widget_set_sensitive (overlap_ui, active); + gtk_widget_set_sensitive (search_ui, active); } static void @@ -175,12 +222,12 @@ demo_gui_do_seek (GtkAction *action, gpointer data) { GValueArray *gvalues = (GValueArray *)data; - DemoGui *gui = g_value_get_object (g_value_array_get_nth (gvalues, 0)); + DemoGui *gui = DEMO_GUI (g_value_get_object (g_value_array_get_nth (gvalues, 0))); gint seconds = g_value_get_int (g_value_array_get_nth (gvalues, 1)); DemoGuiPrivate *priv = DEMO_GUI_GET_PRIVATE (gui); - status_bar_printf (priv->status_bar, "Seeking %i seconds", seconds); + status_bar_printf (priv->status_bar, DEFAULT_STATUS_BAR_TIMEOUT, "Requesting seek by %i seconds", seconds); demo_player_seek_by (priv->player, seconds); } @@ -197,7 +244,7 @@ demo_gui_do_play (GtkAction *action, return; } - status_bar_printf (priv->status_bar, "Trying to start playing"); + status_bar_printf (priv->status_bar, DEFAULT_STATUS_BAR_TIMEOUT, "Requesting playback start"); demo_player_play (priv->player); } @@ -214,7 +261,7 @@ demo_gui_do_pause (GtkAction *action, return; } - status_bar_printf (priv->status_bar, "Trying to pause playing"); + status_bar_printf (priv->status_bar, DEFAULT_STATUS_BAR_TIMEOUT, "Requesting playback pause"); demo_player_pause (priv->player); } @@ -226,12 +273,94 @@ demo_gui_do_play_pause (GtkAction *action, DemoGui *gui = DEMO_GUI (data); DemoGuiPrivate *priv = DEMO_GUI_GET_PRIVATE (gui); - status_bar_printf (priv->status_bar, "Trying to toggle playing"); + status_bar_printf (priv->status_bar, DEFAULT_STATUS_BAR_TIMEOUT, "Reqesting playback toggle"); if (priv->is_playing) - demo_player_pause (priv->player); + gtk_action_activate (priv->pause_action); else - demo_player_play (priv->player); + gtk_action_activate (priv->play_action); +} + +static void +demo_gui_do_open_file (GtkAction *action, + gpointer data) +{ + DemoGui *gui = DEMO_GUI (data); + DemoGuiPrivate *priv = DEMO_GUI_GET_PRIVATE (gui); + + GtkWidget *dialog = gtk_file_chooser_dialog_new ("Open File", + GTK_WINDOW (priv->window), + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + NULL); + + if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { + char *filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); + GError *err = NULL; + g_list_free (priv->uris); + priv->uris = NULL; + priv->now_playing = NULL; + demo_player_load_uri (priv->player, g_filename_to_uri (filename, NULL, &err)); + g_free (filename); + } + gtk_widget_destroy (dialog); +} + +static void +demo_gui_do_playlist_prev (GtkAction *action, + gpointer data) +{ + DemoGui *gui = DEMO_GUI (data); + DemoGuiPrivate *priv = DEMO_GUI_GET_PRIVATE (gui); + + if (priv->now_playing) { + if (priv->now_playing->prev) { + status_bar_printf (priv->status_bar, DEFAULT_STATUS_BAR_TIMEOUT, "Playlist previous"); + priv->now_playing = priv->now_playing->prev; + } else { + priv->now_playing = NULL; + gtk_widget_error_bell (priv->window); + status_bar_printf (priv->status_bar, DEFAULT_STATUS_BAR_TIMEOUT, "Beginning of playlist"); + return; + } + } else if (priv->uris) { + status_bar_printf (priv->status_bar, DEFAULT_STATUS_BAR_TIMEOUT, "Playlist previous: wrap"); + priv->now_playing = g_list_last (priv->uris); + } else { + gtk_action_activate (priv->open_file); + return; + } + + demo_player_load_uri (priv->player, priv->now_playing->data); +} + +static void +demo_gui_do_playlist_next (GtkAction *action, + gpointer data) +{ + DemoGui *gui = DEMO_GUI (data); + DemoGuiPrivate *priv = DEMO_GUI_GET_PRIVATE (gui); + + if (priv->now_playing) { + if (priv->now_playing->next) { + status_bar_printf (priv->status_bar, DEFAULT_STATUS_BAR_TIMEOUT, "Playlist next"); + priv->now_playing = priv->now_playing->next; + } else { + priv->now_playing = NULL; + gtk_widget_error_bell (priv->window); + status_bar_printf (priv->status_bar, DEFAULT_STATUS_BAR_TIMEOUT, "End of playlist"); + return; + } + } else if (priv->uris) { + status_bar_printf (priv->status_bar, DEFAULT_STATUS_BAR_TIMEOUT, "Playlist next: wrap"); + priv->now_playing = priv->uris; + } else { + gtk_action_activate (priv->open_file); + return; + } + + demo_player_load_uri (priv->player, priv->now_playing->data); } static void @@ -242,6 +371,42 @@ demo_gui_do_quit (gpointer source, g_signal_emit (DEMO_GUI (data), demo_gui_signals[SIGNAL_QUITING], 0, NULL); } +static gboolean +demo_gui_request_set_stride (GtkSpinButton *spinbutton, + gpointer data) +{ + DemoGui *gui = DEMO_GUI (data); + DemoGuiPrivate *priv = DEMO_GUI_GET_PRIVATE (gui); + guint new_stride = gtk_spin_button_get_value_as_int (spinbutton); + status_bar_printf (priv->status_bar, DEFAULT_STATUS_BAR_TIMEOUT, "Reqesting setting stride to %u ms", new_stride); + g_object_set (G_OBJECT (priv->player), "stride", new_stride, NULL); + return TRUE; +} + +static gboolean +demo_gui_request_set_overlap (GtkSpinButton *spinbutton, + gpointer data) +{ + DemoGui *gui = DEMO_GUI (data); + DemoGuiPrivate *priv = DEMO_GUI_GET_PRIVATE (gui); + gdouble new_overlap = gtk_spin_button_get_value_as_int (spinbutton); + status_bar_printf (priv->status_bar, DEFAULT_STATUS_BAR_TIMEOUT, "Requesting setting overlap to %2.0lf%%", new_overlap); + g_object_set (G_OBJECT (priv->player), "overlap", new_overlap/100.0, NULL); + return TRUE; +} + +static gboolean +demo_gui_request_set_search (GtkSpinButton *spinbutton, + gpointer data) +{ + DemoGui *gui = DEMO_GUI (data); + DemoGuiPrivate *priv = DEMO_GUI_GET_PRIVATE (gui); + guint new_search = gtk_spin_button_get_value_as_int (spinbutton); + status_bar_printf (priv->status_bar, DEFAULT_STATUS_BAR_TIMEOUT, "Requesting setting search to %u ms", new_search); + g_object_set (G_OBJECT (priv->player), "search", new_search, NULL); + return TRUE; +} + /* Callbacks from signals */ static void @@ -251,10 +416,10 @@ demo_gui_rate_changed (DemoPlayer *player, { DemoGui *gui = DEMO_GUI (data); DemoGuiPrivate *priv = DEMO_GUI_GET_PRIVATE (gui); - status_bar_printf (priv->status_bar, "Rate changed to %3.2lf", new_rate); + status_bar_printf (priv->status_bar, DEFAULT_STATUS_BAR_TIMEOUT, "Rate changed to %3.2lf", new_rate); gchar e[6]; - snprintf (e, 6, "%3.2f", new_rate); + g_snprintf (e, 6, "%3.2f", new_rate); gtk_entry_set_text (GTK_ENTRY (priv->rate_entry), e); } @@ -266,7 +431,12 @@ demo_gui_playing_started (DemoPlayer *player, DemoGuiPrivate *priv = DEMO_GUI_GET_PRIVATE (gui); priv->is_playing = TRUE; - status_bar_printf (priv->status_bar, "Playing started"); + status_bar_printf (priv->status_bar, DEFAULT_STATUS_BAR_TIMEOUT, "Playing started"); + + gtk_action_set_sensitive (priv->play_action, FALSE); + gtk_action_set_sensitive (priv->pause_action, TRUE); + gtk_action_set_visible (priv->play_action, FALSE); + gtk_action_set_visible (priv->pause_action, TRUE); if (priv->position_updater_id) { g_source_remove (priv->position_updater_id); @@ -283,6 +453,11 @@ demo_gui_playing_paused (DemoPlayer *player, DemoGui *gui = DEMO_GUI (data); DemoGuiPrivate *priv = DEMO_GUI_GET_PRIVATE (gui); + gtk_action_set_sensitive (priv->play_action, TRUE); + gtk_action_set_sensitive (priv->pause_action, FALSE); + gtk_action_set_visible (priv->play_action, TRUE); + gtk_action_set_visible (priv->pause_action, FALSE); + priv->is_playing = FALSE; if (priv->position_updater_id) @@ -290,7 +465,7 @@ demo_gui_playing_paused (DemoPlayer *player, priv->position_updater_id = 0; update_position (gui); - status_bar_printf (priv->status_bar, "Playing paused"); + status_bar_printf (priv->status_bar, DEFAULT_STATUS_BAR_TIMEOUT, "Playing paused"); } static void @@ -299,7 +474,66 @@ demo_gui_playing_ended (DemoPlayer *player, { DemoGui *gui = DEMO_GUI (data); DemoGuiPrivate *priv = DEMO_GUI_GET_PRIVATE (gui); - status_bar_printf (priv->status_bar, "Playing ended"); + status_bar_printf (priv->status_bar, DEFAULT_STATUS_BAR_TIMEOUT, "Playing ended"); + gtk_action_activate (priv->playlist_next); +} + +static void +demo_gui_stride_changed (DemoPlayer *player, + GParamSpec *pspec, + gpointer data) +{ + GValueArray *gvalues = (GValueArray *)data; + DemoGui *gui = DEMO_GUI (g_value_get_object (g_value_array_get_nth (gvalues, 0))); + GtkEntry *entry = GTK_ENTRY (g_value_get_object (g_value_array_get_nth (gvalues, 1))); + DemoGuiPrivate *priv = DEMO_GUI_GET_PRIVATE (gui); + + guint new_stride; + g_object_get (G_OBJECT (player), "stride", &new_stride, NULL); + status_bar_printf (priv->status_bar, DEFAULT_STATUS_BAR_TIMEOUT, "Stride changed to %u", new_stride); + + gchar e[6]; + snprintf (e, 6, "%u", new_stride); + gtk_entry_set_text (entry, e); +} + +static void +demo_gui_overlap_changed (DemoPlayer *player, + GParamSpec *pspec, + gpointer data) +{ + GValueArray *gvalues = (GValueArray *)data; + DemoGui *gui = DEMO_GUI (g_value_get_object (g_value_array_get_nth (gvalues, 0))); + GtkEntry *entry = GTK_ENTRY (g_value_get_object (g_value_array_get_nth (gvalues, 1))); + DemoGuiPrivate *priv = DEMO_GUI_GET_PRIVATE (gui); + + gdouble new_overlap; + g_object_get (G_OBJECT (player), "overlap", &new_overlap, NULL); + new_overlap *= 100; + status_bar_printf (priv->status_bar, DEFAULT_STATUS_BAR_TIMEOUT, "Overlap changed to %2.0lf%%", new_overlap); + + gchar e[6]; + snprintf (e, 6, "%2.0f", new_overlap); + gtk_entry_set_text (entry, e); +} + +static void +demo_gui_search_changed (DemoPlayer *player, + GParamSpec *pspec, + gpointer data) +{ + GValueArray *gvalues = (GValueArray *)data; + DemoGui *gui = DEMO_GUI (g_value_get_object (g_value_array_get_nth (gvalues, 0))); + GtkEntry *entry = GTK_ENTRY (g_value_get_object (g_value_array_get_nth (gvalues, 1))); + DemoGuiPrivate *priv = DEMO_GUI_GET_PRIVATE (gui); + + guint new_search; + g_object_get (G_OBJECT (player), "search", &new_search, NULL); + status_bar_printf (priv->status_bar, DEFAULT_STATUS_BAR_TIMEOUT, "Search changed to %u", new_search); + + gchar e[6]; + snprintf (e, 6, "%u", new_search); + gtk_entry_set_text (entry, e); } @@ -314,15 +548,15 @@ demo_gui_set_player_func (DemoGui *gui, g_signal_handlers_disconnect_by_func (G_OBJECT (priv->player), G_CALLBACK (demo_gui_playing_started), gui); g_signal_handlers_disconnect_by_func (G_OBJECT (priv->player), G_CALLBACK (demo_gui_playing_paused), gui); g_signal_handlers_disconnect_by_func (G_OBJECT (priv->player), G_CALLBACK (demo_gui_playing_ended), gui); + g_object_unref (priv->player); } + g_object_ref (player); priv->player = player; g_signal_connect (G_OBJECT (priv->player), "rate-changed", G_CALLBACK (demo_gui_rate_changed), gui); g_signal_connect (G_OBJECT (priv->player), "playing-started", G_CALLBACK (demo_gui_playing_started), gui); g_signal_connect (G_OBJECT (priv->player), "playing-paused", G_CALLBACK (demo_gui_playing_paused), gui); g_signal_connect (G_OBJECT (priv->player), "playing-ended", G_CALLBACK (demo_gui_playing_ended), gui); - - if (priv->now_playing) - demo_player_load_uri (priv->player, priv->now_playing->data); + priv->is_playing = FALSE; } static void @@ -331,9 +565,6 @@ demo_gui_set_playlist_func (DemoGui *gui, { DemoGuiPrivate *priv = DEMO_GUI_GET_PRIVATE (gui); priv->uris = uris; - priv->now_playing = priv->uris; - if (priv->player && priv->now_playing) - demo_player_load_uri (priv->player, priv->now_playing->data); } typedef struct _ActionEntry { @@ -352,7 +583,7 @@ typedef struct _ActionEntry { static GValueArray * build_gvalue_array (guint n_values, - ...) + ...) { va_list args; va_start (args, n_values); @@ -399,7 +630,6 @@ create_action (ActionEntry *p) g_signal_connect (G_OBJECT (p->action), "activate", p->callback, p->data); } - static void demo_gui_show_func (DemoGui *gui) { DemoGuiPrivate *priv = DEMO_GUI_GET_PRIVATE (gui); @@ -428,7 +658,7 @@ demo_gui_show_func (DemoGui *gui) { "_Slower", "decrease playback rate", GTK_STOCK_GO_DOWN, accel_group, action_group, G_CALLBACK (demo_gui_do_change_rate), - build_gvalue_array (2, G_TYPE_OBJECT, gui, G_TYPE_DOUBLE, 0.943874312681694) + build_gvalue_array (2, G_TYPE_OBJECT, gui, G_TYPE_DOUBLE, pow (2, -1.0/12)) }; create_action (slower_sm); @@ -438,7 +668,7 @@ demo_gui_show_func (DemoGui *gui) { "_Faster", "increase playback rate", GTK_STOCK_GO_UP, accel_group, action_group, G_CALLBACK (demo_gui_do_change_rate), - build_gvalue_array (2, G_TYPE_OBJECT, gui, G_TYPE_DOUBLE, 1.0594630943593) + build_gvalue_array (2, G_TYPE_OBJECT, gui, G_TYPE_DOUBLE, pow (2, 1.0/12)) }; create_action (faster_sm); @@ -452,6 +682,16 @@ demo_gui_show_func (DemoGui *gui) { }; create_action (faster_lg); + ActionEntry *normal = &(ActionEntry){ + NULL, NULL, + "backslash", "normal", + "_Normal", "playback normal rate", + GTK_STOCK_CLEAR, accel_group, action_group, + G_CALLBACK (demo_gui_do_set_rate), + build_gvalue_array (2, G_TYPE_OBJECT, gui, G_TYPE_DOUBLE, 1.0) + }; + create_action (normal); + ActionEntry *rewind_lg = &(ActionEntry){ NULL, NULL, "Left", "seek-rewind-large", @@ -492,8 +732,6 @@ demo_gui_show_func (DemoGui *gui) { }; create_action (forward_lg); - GValue *is_playing = g_new0 (GValue, 1); - g_value_set_boolean (g_value_init (is_playing, G_TYPE_BOOLEAN), TRUE); ActionEntry *pause = &(ActionEntry){ NULL, NULL, "p", "pause", @@ -524,11 +762,38 @@ demo_gui_show_func (DemoGui *gui) { }; create_action (play_pause); + ActionEntry *open_file = &(ActionEntry){ + NULL, NULL, + "o", "open-file", + "Open File", "Open file for playing", + GTK_STOCK_OPEN, accel_group, action_group, + G_CALLBACK (demo_gui_do_open_file), gui + }; + create_action (open_file); + + ActionEntry *playlist_prev = &(ActionEntry){ + NULL, NULL, + "less", "playlist-previous", + "Previous", "Previous in playlist", + GTK_STOCK_MEDIA_PREVIOUS, accel_group, action_group, + G_CALLBACK (demo_gui_do_playlist_prev), gui + }; + create_action (playlist_prev); + + ActionEntry *playlist_next = &(ActionEntry){ + NULL, NULL, + "greater", "playlist-next", + "Next", "Next in playlist", + GTK_STOCK_MEDIA_NEXT, accel_group, action_group, + G_CALLBACK (demo_gui_do_playlist_next), gui + }; + create_action (playlist_next); + ActionEntry *quit = &(ActionEntry){ NULL, NULL, "q", "quit", "Quit", "Quit demo", - GTK_STOCK_MEDIA_STOP, accel_group, action_group, + GTK_STOCK_QUIT, accel_group, action_group, G_CALLBACK (demo_gui_do_quit), gui }; create_action (quit); @@ -540,39 +805,62 @@ demo_gui_show_func (DemoGui *gui) { gtk_entry_set_width_chars (GTK_ENTRY (rate_entry), 5); g_signal_connect (G_OBJECT (rate_entry), "activate", G_CALLBACK (demo_gui_do_rate_entered), gui); - GtkWidget *menu = gtk_menu_new (); - gtk_menu_set_accel_group (GTK_MENU (menu), accel_group); - gtk_menu_shell_append (GTK_MENU_SHELL (menu), gtk_action_create_menu_item (faster_lg->action)); - gtk_menu_shell_append (GTK_MENU_SHELL (menu), gtk_action_create_menu_item (faster_sm->action)); - gtk_menu_shell_append (GTK_MENU_SHELL (menu), gtk_action_create_menu_item (slower_sm->action)); - gtk_menu_shell_append (GTK_MENU_SHELL (menu), gtk_action_create_menu_item (slower_lg->action)); - gtk_menu_shell_append (GTK_MENU_SHELL (menu), gtk_action_create_menu_item (rewind_lg->action)); - gtk_menu_shell_append (GTK_MENU_SHELL (menu), gtk_action_create_menu_item (rewind_sm->action)); - gtk_menu_shell_append (GTK_MENU_SHELL (menu), gtk_action_create_menu_item (forward_sm->action)); - gtk_menu_shell_append (GTK_MENU_SHELL (menu), gtk_action_create_menu_item (forward_lg->action)); - gtk_menu_shell_append (GTK_MENU_SHELL (menu), gtk_action_create_menu_item (play->action)); - gtk_menu_shell_append (GTK_MENU_SHELL (menu), gtk_action_create_menu_item (pause->action)); - gtk_menu_shell_append (GTK_MENU_SHELL (menu), gtk_action_create_menu_item (play_pause->action)); - gtk_menu_shell_append (GTK_MENU_SHELL (menu), gtk_action_create_menu_item (quit->action)); - GtkWidget *menu_button = GTK_WIDGET (gtk_menu_tool_button_new (NULL, "More")); - gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (menu_button), menu); - GtkWidget *toolbox = gtk_hbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (toolbox), slower_sm->button, FALSE, FALSE, 2); gtk_box_pack_start (GTK_BOX (toolbox), rate_label, FALSE, FALSE, 2); gtk_box_pack_start (GTK_BOX (toolbox), rate_entry, FALSE, FALSE, 2); gtk_box_pack_start (GTK_BOX (toolbox), faster_sm->button, FALSE, FALSE, 2); - gtk_box_pack_start (GTK_BOX (toolbox), pause->button, FALSE, FALSE, 2); - gtk_box_pack_start (GTK_BOX (toolbox), play->button, FALSE, FALSE, 2); - gtk_box_pack_start (GTK_BOX (toolbox), menu_button, FALSE, FALSE, 2); - - GtkWidget *video_window = gtk_drawing_area_new (); - gtk_drawing_area_size (GTK_DRAWING_AREA (video_window), 640, 360); - - GtkWidget *amount_played = gtk_label_new ("00:00:00.00"); - GtkWidget *amount_to_play = gtk_label_new ("-00:00:00.00"); - gtk_label_set_width_chars (GTK_LABEL (amount_played), 12); - gtk_label_set_width_chars (GTK_LABEL (amount_to_play), 12); + gtk_box_pack_start (GTK_BOX (toolbox), normal->button, FALSE, FALSE, 2); + + + GtkWidget *stride_ui = gtk_spin_button_new (GTK_ADJUSTMENT (gtk_adjustment_new (60, 1, 1000, 1, 10, 0)), 0, 0); + GtkWidget *overlap_ui = gtk_spin_button_new (GTK_ADJUSTMENT (gtk_adjustment_new (20, 0, 100, 5, 10, .00001)), 0, 0); + GtkWidget *search_ui = gtk_spin_button_new (GTK_ADJUSTMENT (gtk_adjustment_new (14, 0, 1000, 1, 10, 0)), 0, 0); + gtk_widget_set_sensitive (stride_ui, FALSE); + gtk_widget_set_sensitive (overlap_ui, FALSE); + gtk_widget_set_sensitive (search_ui, FALSE); + g_signal_connect (G_OBJECT (stride_ui), "output", G_CALLBACK (demo_gui_request_set_stride), gui); + g_signal_connect (G_OBJECT (overlap_ui), "output", G_CALLBACK (demo_gui_request_set_overlap), gui); + g_signal_connect (G_OBJECT (search_ui), "output", G_CALLBACK (demo_gui_request_set_search), gui); + g_signal_connect (G_OBJECT (priv->player), "notify::stride", G_CALLBACK (demo_gui_stride_changed), + build_gvalue_array (2, G_TYPE_OBJECT, gui, G_TYPE_OBJECT, stride_ui)); + g_signal_connect (G_OBJECT (priv->player), "notify::overlap", G_CALLBACK (demo_gui_overlap_changed), + build_gvalue_array (2, G_TYPE_OBJECT, gui, G_TYPE_OBJECT, overlap_ui)); + g_signal_connect (G_OBJECT (priv->player), "notify::search", G_CALLBACK (demo_gui_search_changed), + build_gvalue_array (2, G_TYPE_OBJECT, gui, G_TYPE_OBJECT, search_ui)); + GtkWidget *propbox = gtk_hbox_new (FALSE, 0); + GtkWidget *adv_check = gtk_check_button_new (); + gtk_box_pack_start (GTK_BOX (propbox), gtk_label_new ("stride:"), FALSE, FALSE, 2); + gtk_box_pack_start (GTK_BOX (propbox), stride_ui, FALSE, FALSE, 2); + gtk_box_pack_start (GTK_BOX (propbox), gtk_label_new ("overlap:"), FALSE, FALSE, 2); + gtk_box_pack_start (GTK_BOX (propbox), overlap_ui, FALSE, FALSE, 2); + gtk_box_pack_start (GTK_BOX (propbox), gtk_label_new ("search:"), FALSE, FALSE, 2); + gtk_box_pack_start (GTK_BOX (propbox), search_ui, FALSE, FALSE, 2); + gtk_box_pack_start (GTK_BOX (propbox), adv_check, FALSE, FALSE, 2); + + GtkAction *toggle_advanced = GTK_ACTION (gtk_toggle_action_new ("advanced", "Advanced", "Toggle advanced controls", 0)); + gtk_action_group_add_action_with_accel (action_group, toggle_advanced, "a"); + gtk_action_set_accel_group (toggle_advanced, accel_group); + gtk_action_connect_accelerator (toggle_advanced); + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (toggle_advanced), FALSE); + gtk_action_connect_proxy (toggle_advanced, adv_check); + gtk_button_set_label (GTK_BUTTON (adv_check), "enable"); + g_signal_connect (G_OBJECT (toggle_advanced), "activate", G_CALLBACK (demo_gui_do_toggle_advanced), + build_gvalue_array (4, G_TYPE_OBJECT, gui, G_TYPE_OBJECT, stride_ui, G_TYPE_OBJECT, overlap_ui, G_TYPE_OBJECT, search_ui)); + + + GtkWidget *media_controls = gtk_hbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (media_controls), playlist_prev->button, FALSE, FALSE, 2); + gtk_box_pack_start (GTK_BOX (media_controls), rewind_sm->button, FALSE, FALSE, 2); + gtk_box_pack_start (GTK_BOX (media_controls), play->button, FALSE, FALSE, 2); + gtk_box_pack_start (GTK_BOX (media_controls), pause->button, FALSE, FALSE, 2); + gtk_box_pack_start (GTK_BOX (media_controls), forward_sm->button, FALSE, FALSE, 2); + gtk_box_pack_start (GTK_BOX (media_controls), playlist_next->button, FALSE, FALSE, 2); + + GtkWidget *amount_played = gtk_label_new ("?:??:??"); + GtkWidget *amount_to_play = gtk_label_new ("-?:??:??"); + gtk_label_set_width_chars (GTK_LABEL (amount_played), 8); + gtk_label_set_width_chars (GTK_LABEL (amount_to_play), 8); gtk_misc_set_alignment (GTK_MISC (amount_played), 1, 1); gtk_misc_set_alignment (GTK_MISC (amount_to_play), 0, 1); GtkWidget *seek_range = gtk_hscale_new ( @@ -587,12 +875,53 @@ demo_gui_show_func (DemoGui *gui) { GtkWidget *status_bar = gtk_statusbar_new (); + /* Menubar*/ + GtkWidget *file_menu = gtk_menu_new (); + gtk_menu_set_accel_group (GTK_MENU (file_menu), accel_group); + gtk_menu_shell_append (GTK_MENU_SHELL (file_menu), gtk_action_create_menu_item (open_file->action)); + gtk_menu_shell_append (GTK_MENU_SHELL (file_menu), gtk_action_create_menu_item (quit->action)); + GtkWidget *file_menu_item = gtk_menu_item_new_with_mnemonic ("_File"); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (file_menu_item), file_menu); + + GtkWidget *media_menu = gtk_menu_new (); + gtk_menu_set_accel_group (GTK_MENU (media_menu), accel_group); + gtk_menu_shell_append (GTK_MENU_SHELL (media_menu), gtk_action_create_menu_item (rewind_lg->action)); + gtk_menu_shell_append (GTK_MENU_SHELL (media_menu), gtk_action_create_menu_item (rewind_sm->action)); + gtk_menu_shell_append (GTK_MENU_SHELL (media_menu), gtk_action_create_menu_item (forward_sm->action)); + gtk_menu_shell_append (GTK_MENU_SHELL (media_menu), gtk_action_create_menu_item (forward_lg->action)); + gtk_menu_shell_append (GTK_MENU_SHELL (media_menu), gtk_action_create_menu_item (play->action)); + gtk_menu_shell_append (GTK_MENU_SHELL (media_menu), gtk_action_create_menu_item (pause->action)); + gtk_menu_shell_append (GTK_MENU_SHELL (media_menu), gtk_action_create_menu_item (play_pause->action)); + gtk_menu_shell_append (GTK_MENU_SHELL (media_menu), gtk_action_create_menu_item (playlist_prev->action)); + gtk_menu_shell_append (GTK_MENU_SHELL (media_menu), gtk_action_create_menu_item (playlist_next->action)); + GtkWidget *media_menu_item = gtk_menu_item_new_with_mnemonic ("_Media"); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (media_menu_item), media_menu); + + GtkWidget *demo_menu = gtk_menu_new (); + gtk_menu_set_accel_group (GTK_MENU (demo_menu), accel_group); + gtk_menu_shell_append (GTK_MENU_SHELL (demo_menu), gtk_action_create_menu_item (faster_lg->action)); + gtk_menu_shell_append (GTK_MENU_SHELL (demo_menu), gtk_action_create_menu_item (faster_sm->action)); + gtk_menu_shell_append (GTK_MENU_SHELL (demo_menu), gtk_action_create_menu_item (slower_sm->action)); + gtk_menu_shell_append (GTK_MENU_SHELL (demo_menu), gtk_action_create_menu_item (slower_lg->action)); + gtk_menu_shell_append (GTK_MENU_SHELL (demo_menu), gtk_action_create_menu_item (normal->action)); + gtk_menu_shell_append (GTK_MENU_SHELL (demo_menu), gtk_action_create_menu_item (toggle_advanced)); + GtkWidget *demo_menu_item = gtk_menu_item_new_with_mnemonic ("_Scaletempo"); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (demo_menu_item), demo_menu); + + GtkWidget *menu_bar = gtk_menu_bar_new (); + gtk_menu_shell_append (GTK_MENU_SHELL (menu_bar), file_menu_item); + gtk_menu_shell_append (GTK_MENU_SHELL (menu_bar), media_menu_item); + gtk_menu_shell_append (GTK_MENU_SHELL (menu_bar), demo_menu_item); + + /* Toplevel Window */ gtk_window_set_title (GTK_WINDOW (window), "Scaletempo Demo"); GtkWidget *toplevel_box = gtk_vbox_new (FALSE, 0); gtk_container_set_border_width (GTK_CONTAINER (toplevel_box), 3); gtk_container_add (GTK_CONTAINER (window), toplevel_box); + gtk_box_pack_start (GTK_BOX (toplevel_box), menu_bar, FALSE, FALSE, 2); + gtk_box_pack_start (GTK_BOX (toplevel_box), media_controls, FALSE, FALSE, 2); gtk_box_pack_start (GTK_BOX (toplevel_box), toolbox, FALSE, FALSE, 2); - gtk_box_pack_start (GTK_BOX (toplevel_box), video_window, TRUE, TRUE, 2); + gtk_box_pack_start (GTK_BOX (toplevel_box), propbox, FALSE, FALSE, 2); gtk_box_pack_start (GTK_BOX (toplevel_box), seek_bar, FALSE, FALSE, 2); gtk_box_pack_start (GTK_BOX (toplevel_box), status_bar, FALSE, FALSE, 2); @@ -602,10 +931,18 @@ demo_gui_show_func (DemoGui *gui) { priv->seek_range = GTK_RANGE (seek_range); priv->amount_played = GTK_LABEL (amount_played); priv->amount_to_play = GTK_LABEL (amount_to_play); + priv->play_action = GTK_ACTION (play->action); + priv->pause_action = GTK_ACTION (pause->action); + priv->open_file = GTK_ACTION (open_file->action); + priv->playlist_next = GTK_ACTION (playlist_next->action); + + gtk_action_set_sensitive (priv->pause_action, FALSE); + gtk_action_set_visible (priv->pause_action, FALSE); gtk_widget_show_all (window); - status_bar_printf (GTK_STATUSBAR (status_bar), "Welcome to the Scaletempo demo."); gtk_widget_grab_focus (seek_range); + gtk_action_activate (priv->playlist_next); + status_bar_printf (GTK_STATUSBAR (status_bar), 5, "Welcome to the Scaletempo demo."); GError *error; if (!g_thread_create ((GThreadFunc)gtk_main, NULL, FALSE, &error)) { diff --git a/src/demo-main.c b/src/demo-main.c index c1e50f2..4e97bb4 100644 --- a/src/demo-main.c +++ b/src/demo-main.c @@ -46,11 +46,8 @@ int main (int argc, char *argv[]) { gchar **uris = NULL; - gchar *filter_name = "identity"; const GOptionEntry entries[] = { - { "filter", '\0', 0, G_OPTION_ARG_STRING, &filter_name, - "filter", "NAME" }, { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &uris, "Special option that collects any remaining arguments for us" }, { NULL, } @@ -71,13 +68,8 @@ main (int argc, char *argv[]) } g_option_context_free (ctx); - if (uris == NULL || *uris == NULL) { - g_print ("Please specify a URI to play\n\n"); - return -1; - } - DemoGui *gui = g_object_new (DEMO_TYPE_GUI, NULL); - DemoPlayer *player = g_object_new (DEMO_TYPE_PLAYER, "filter", filter_name, NULL); + DemoPlayer *player = g_object_new (DEMO_TYPE_PLAYER, NULL); g_signal_connect (player, "error", G_CALLBACK (handle_error_message), "PLAYER ERROR: %s\n"); g_signal_connect (gui, "error", G_CALLBACK (handle_error_message), "GUI ERROR: %s\n"); demo_gui_set_player (gui, player); @@ -85,14 +77,15 @@ main (int argc, char *argv[]) GMainLoop *loop = g_main_loop_new (NULL, FALSE); g_signal_connect (gui, "quiting", G_CALLBACK (handle_quit), loop); - int i, num = g_strv_length (uris); - GList *uri_list = NULL; - for (i=0; irate != new_rate) { priv->rate = new_rate; g_signal_emit (player, demo_player_signals[SIGNAL_RATE_CHANGE], 0, new_rate); @@ -134,6 +137,10 @@ demo_player_build_pipeline (DemoPlayer *player) { DemoPlayerPrivate *priv = DEMO_PLAYER_GET_PRIVATE (player); priv->pipeline = NULL; + if (!priv->scaletempo) { + return; + } + GstElement *filter = priv->scaletempo; GstElement *playbin, *vsink; @@ -150,8 +157,8 @@ demo_player_build_pipeline (DemoPlayer *player) const gchar *audiosink_name = has_gconf ? "gconfaudiosink" : "autoaudiosink"; GstElement *audioline = gst_bin_new ("audioline"); - GstElement *filter, *format, *resample, *asink; - MAKE_ELEMENT (audioline, filter, priv->filter, "filter"); + GstElement *format, *resample, *asink; + gst_bin_add (GST_BIN (audioline), filter); MAKE_ELEMENT (audioline, format, "audioconvert", "format"); MAKE_ELEMENT (audioline, resample, "audioresample", "resample"); MAKE_ELEMENT (audioline, asink, audiosink_name, "audio_sink"); @@ -172,13 +179,15 @@ demo_player_build_pipeline (DemoPlayer *player) g_signal_connect (bus, "message::eos", G_CALLBACK (demo_player_eos_cb), player); gst_object_unref (bus); - priv->pipeline = playbin; + priv->scaletempo = filter; + priv->pipeline = playbin; } /* method implementations */ -static void _set_rate (DemoPlayer *player, - gdouble new_rate) +static void +_set_rate (DemoPlayer *player, + gdouble new_rate) { if (new_rate == 0) { g_signal_emit (player, demo_player_signals[SIGNAL_ERROR], 0, "Cannot set playback to zero. Pausing instead."); @@ -195,6 +204,7 @@ static void _set_rate (DemoPlayer *player, pos = GST_CLOCK_TIME_NONE; seek_type = GST_SEEK_TYPE_NONE; } + if (!gst_element_seek (priv->pipeline, new_rate, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, seek_type, pos, @@ -238,15 +248,60 @@ demo_player_load_uri_func (DemoPlayer *player, gchar *uri) { DemoPlayerPrivate *priv = DEMO_PLAYER_GET_PRIVATE (player); - g_message ("Loading URI: %s", uri); if (!priv->pipeline) { demo_player_build_pipeline (player); if (!priv->pipeline) { - g_signal_emit (player, demo_player_signals[SIGNAL_ERROR], 0, "Could not load uri"); + g_signal_emit (player, demo_player_signals[SIGNAL_ERROR], 0, "Could not build player"); return; } } + if (!g_str_has_prefix (uri, "file:///")) { + GError *err = NULL; + if (g_path_is_absolute (uri)) { + uri = g_filename_to_uri (uri, NULL, &err); + } else { + gchar *curdir = g_get_current_dir (); + gchar *absolute_path = g_strconcat (curdir, G_DIR_SEPARATOR_S, uri, NULL); + uri = g_filename_to_uri (absolute_path, NULL, &err); + g_free (absolute_path); + g_free (curdir); + } + if (err) { + gchar *msg = g_strconcat ("Could not load uri: ", err->message, NULL); + g_signal_emit (player, demo_player_signals[SIGNAL_ERROR], 0, msg); + return; + } + } + + g_message ("Loading URI: %s", uri); + + GstState end_state = (GST_STATE (priv->pipeline) == GST_STATE_PLAYING) ? GST_STATE_PLAYING : GST_STATE_PAUSED; + GstStateChangeReturn ret = gst_element_set_state (priv->pipeline, GST_STATE_NULL); + if (ret == GST_STATE_CHANGE_ASYNC) { + ret = gst_element_get_state (priv->pipeline, NULL, NULL, 10 * GST_SECOND); + } + if (ret != GST_STATE_CHANGE_SUCCESS) { + g_signal_emit (player, demo_player_signals[SIGNAL_ERROR], 0, "Unable to load uri"); + return; + } + g_object_set (G_OBJECT (priv->pipeline), "uri", uri, NULL); + + gdouble rate; + g_object_get (G_OBJECT (priv->scaletempo), "rate", &rate, NULL); + + if (rate && rate != 1.0) { + GstStateChangeReturn ret = gst_element_set_state (priv->pipeline, GST_STATE_PAUSED); + if (ret == GST_STATE_CHANGE_ASYNC) { + ret = gst_element_get_state (priv->pipeline, NULL, NULL, 10 * GST_SECOND); + } + if (ret != GST_STATE_CHANGE_SUCCESS) { + g_signal_emit (player, demo_player_signals[SIGNAL_ERROR], 0, "Unable to keep playback rate"); + } + _set_rate (player, rate); + } + + gst_element_set_state (priv->pipeline, end_state); } static void @@ -468,8 +523,14 @@ demo_player_get_property (GObject *object, case PROP_RATE: g_value_set_double (value, priv->rate); break; - case PROP_FILTER: - g_value_set_string (value, priv->filter); + case PROP_STRIDE: + g_object_get_property (G_OBJECT (priv->scaletempo), "stride", value); + break; + case PROP_OVERLAP: + g_object_get_property (G_OBJECT (priv->scaletempo), "overlap", value); + break; + case PROP_SEARCH: + g_object_get_property (G_OBJECT (priv->scaletempo), "search", value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -486,8 +547,14 @@ demo_player_set_property (GObject *object, DemoPlayer *player = DEMO_PLAYER (object); DemoPlayerPrivate *priv = DEMO_PLAYER_GET_PRIVATE (player); switch (property_id) { - case PROP_FILTER: - priv->filter = g_value_dup_string (value); + case PROP_STRIDE: + g_object_set_property (G_OBJECT (priv->scaletempo), "stride", value); + break; + case PROP_OVERLAP: + g_object_set_property (G_OBJECT (priv->scaletempo), "overlap", value); + break; + case PROP_SEARCH: + g_object_set_property (G_OBJECT (priv->scaletempo), "search", value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -503,8 +570,11 @@ demo_player_init (GTypeInstance *instance, { DemoPlayer *player = (DemoPlayer *)instance; DemoPlayerPrivate *priv = DEMO_PLAYER_GET_PRIVATE (player); + priv->scaletempo = gst_element_factory_make ("scaletempo", "scaletempo"); + if (!priv->scaletempo) { + g_error ("Unable to make scaletempo element."); + } priv->rate = 1.0; - priv->filter = "identity"; priv->pipeline = NULL; priv->ignore_state_change = FALSE; } @@ -537,9 +607,17 @@ demo_player_class_init (gpointer klass, g_param_spec_double ("rate", "Rate", "Current playback rate", -128, 128, 1.0, G_PARAM_READABLE)); - g_object_class_install_property (as_object_class, PROP_FILTER, - g_param_spec_string ("filter", "Filter", "Filter to handle rate change", - "identity", G_PARAM_READWRITE)); + g_object_class_install_property (as_object_class , PROP_STRIDE, + g_param_spec_uint ("stride", "Stride Length", "Length in milliseconds to output each stride", + 1, 10000, 60, G_PARAM_READWRITE)); + + g_object_class_install_property (as_object_class , PROP_OVERLAP, + g_param_spec_double ("overlap", "Overlap Length", "Percentage of stride to overlap", + 0, 1, .2, G_PARAM_READWRITE)); + + g_object_class_install_property (as_object_class , PROP_SEARCH, + g_param_spec_uint ("search", "Search Length", "Length in milliseconds to search for best overlap position", + 0, 10000, 14, G_PARAM_READWRITE)); /* Signals */ GType type = G_TYPE_FROM_CLASS (klass); -- 2.11.4.GIT