static void bt_edit_application_set_property (GObject * object, guint property_id, const GValue * value, GParamSpec * pspec) { BtEditApplication *self = BT_EDIT_APPLICATION (object); return_if_disposed (); switch (property_id) { case EDIT_APPLICATION_SONG: #ifdef USE_DEBUG if (G_OBJECT_REF_COUNT (self->priv->song) != 1) { GST_DEBUG ("old song: %" G_OBJECT_REF_COUNT_FMT, G_OBJECT_LOG_REF_COUNT (self->priv->song)); } #endif g_object_try_unref (self->priv->song); self->priv->song = BT_SONG (g_value_dup_object (value)); GST_DEBUG ("new song: %" G_OBJECT_REF_COUNT_FMT, G_OBJECT_LOG_REF_COUNT (self->priv->song)); break; case EDIT_APPLICATION_UNSAVED: self->priv->unsaved = g_value_get_boolean (value); GST_INFO ("set the unsaved flag to %d for the song", self->priv->unsaved); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } }
static GObject * bt_edit_application_constructor (GType type, guint n_construct_params, GObjectConstructParam * construct_params) { GObject *object; if (G_UNLIKELY (!singleton)) { object = G_OBJECT_CLASS (bt_edit_application_parent_class)->constructor (type, n_construct_params, construct_params); singleton = BT_EDIT_APPLICATION (object); g_object_add_weak_pointer (object, (gpointer *) (gpointer) & singleton); //GST_DEBUG("<<<"); GST_INFO ("new edit app instantiated"); // create or ref the shared ui resources singleton->priv->ui_resources = bt_ui_resources_new (); // create the interaction controller registry singleton->priv->ic_registry = btic_registry_new (); btic_registry_start_discovery (); // create the playback controllers (we need to create them all as they watch // the settings them self) singleton->priv->pbc_socket = bt_playback_controller_socket_new (); singleton->priv->pbc_ic = bt_playback_controller_ic_new (); // create the editor change log singleton->priv->change_log = bt_change_log_new (); g_signal_connect (singleton->priv->change_log, "notify::can-undo", G_CALLBACK (on_changelog_can_undo_changed), (gpointer) singleton); // create the audio_session singleton->priv->audio_session = bt_audio_session_new (); // create main window GST_INFO ("new edit app created, app: %" G_OBJECT_REF_COUNT_FMT, G_OBJECT_LOG_REF_COUNT (singleton)); singleton->priv->main_window = bt_main_window_new (); gtk_widget_show_all (GTK_WIDGET (singleton->priv->main_window)); GST_INFO ("new main_window shown"); // warning: dereferencing type-punned pointer will break strict-aliasing rules g_object_add_weak_pointer (G_OBJECT (singleton->priv->main_window), (gpointer *) (gpointer) & singleton->priv->main_window); GST_INFO ("new edit app created, app: %" G_OBJECT_REF_COUNT_FMT, G_OBJECT_LOG_REF_COUNT (singleton)); //GST_DEBUG(">>>"); } else { object = g_object_ref (singleton); } return object; }
static void check_gst_log_handler (GstDebugCategory * category, GstDebugLevel level, const gchar * file, const gchar * function, gint line, GObject * object, GstDebugMessage * _message, gpointer data) { const gchar *message = gst_debug_message_get (_message); gchar *msg, *obj_str; const gchar *level_str, *cat_str; GstClockTime elapsed; //-- check message contents if (__check_method && (strstr (function, __check_method) != NULL) && __check_test && (strstr (message, __check_test) != NULL)) __check_error_trapped = TRUE; else if (__check_method && (strstr (function, __check_method) != NULL) && !__check_test) __check_error_trapped = TRUE; else if (__check_test && (strstr (message, __check_test) != NULL) && !__check_method) __check_error_trapped = TRUE; if (level > gst_debug_category_get_threshold (category)) return; elapsed = GST_CLOCK_DIFF (_priv_bt_info_start_time, gst_util_get_timestamp ()); level_str = gst_debug_level_get_name (level); cat_str = gst_debug_category_get_name (category); if (object) { if (GST_IS_OBJECT (object)) { obj_str = g_strdup_printf ("<%s,%" G_OBJECT_REF_COUNT_FMT ">", GST_OBJECT_NAME (object), G_OBJECT_LOG_REF_COUNT (object)); } else if (GST_IS_OBJECT (object)) { obj_str = g_strdup_printf ("<%s,%" G_OBJECT_REF_COUNT_FMT ">", G_OBJECT_TYPE_NAME (object), G_OBJECT_LOG_REF_COUNT (object)); } else { obj_str = g_strdup_printf ("%p", object); } } else { obj_str = g_strdup (""); } msg = g_alloca (95 + strlen (cat_str) + strlen (level_str) + strlen (message) + strlen (file) + strlen (function) + strlen (obj_str)); g_sprintf (msg, "%" GST_TIME_FORMAT " %" PID_FMT " %" PTR_FMT " %-7s %20s %s:%d:%s:%s %s", GST_TIME_ARGS (elapsed), getpid (), g_thread_self (), level_str, cat_str, file, line, function, obj_str, message); g_free (obj_str); check_print_handler (msg); }
static void bt_main_pages_init_ui (const BtMainPages * self) { gtk_widget_set_name (GTK_WIDGET (self), "song views"); GST_INFO ("before creating pages, app: %" G_OBJECT_REF_COUNT_FMT, G_OBJECT_LOG_REF_COUNT (self->priv->app)); // don't emit notify::page for each add g_object_freeze_notify ((GObject *) self); if (!BT_EDIT_UI_CONFIG ("no-machines-page")) { // add wigets for machine view self->priv->machines_page = bt_main_page_machines_new (self); bt_main_pages_add_tab (self, GTK_WIDGET (self->priv->machines_page), _("machines"), "buzztrax_tab_machines", _("machines used in the song and their wires")); } if (!BT_EDIT_UI_CONFIG ("no-patterns-page")) { // add wigets for pattern view self->priv->patterns_page = bt_main_page_patterns_new (self); bt_main_pages_add_tab (self, GTK_WIDGET (self->priv->patterns_page), _("patterns"), "buzztrax_tab_patterns", _("event pattern editor")); } if (!BT_EDIT_UI_CONFIG ("no-sequence-page")) { // add wigets for sequence view self->priv->sequence_page = bt_main_page_sequence_new (self); bt_main_pages_add_tab (self, GTK_WIDGET (self->priv->sequence_page), _("sequence"), "buzztrax_tab_sequence", _("song sequence editor")); } if (!BT_EDIT_UI_CONFIG ("no-wavetable-page")) { // add wigets for waves view self->priv->waves_page = bt_main_page_waves_new (self); bt_main_pages_add_tab (self, GTK_WIDGET (self->priv->waves_page), _("wave table"), "buzztrax_tab_waves", _("sample wave table editor")); } if (!BT_EDIT_UI_CONFIG ("no-info-page")) { // add widgets for song info view self->priv->info_page = bt_main_page_info_new (self); bt_main_pages_add_tab (self, GTK_WIDGET (self->priv->info_page), _("information"), "buzztrax_tab_info", _("song meta data editor")); } // @idea add widgets for machine help view // GTK_STOCK_HELP icon // embed mozilla/gtk-html/webkit-gtk g_object_thaw_notify ((GObject *) self); // register event handlers g_signal_connect_object (self->priv->app, "notify::song", G_CALLBACK (on_song_changed), (gpointer) self, 0); g_signal_connect ((gpointer) self, "notify::page", G_CALLBACK (on_page_switched), (gpointer) self); GST_DEBUG (" done"); }
static void bt_edit_application_dispose (GObject * object) { BtEditApplication *self = BT_EDIT_APPLICATION (object); return_if_disposed (); self->priv->dispose_has_run = TRUE; /* This should destroy the window as this is a child of the app. * Problem 1: On the other hand, this *NEVER* gets called as long as the window keeps its * strong reference to the app. * Solution 1: Only use weak refs when reffing upstream objects */ GST_DEBUG ("!!!! self=%p", self); if (self->priv->song) { GST_INFO ("song: %" G_OBJECT_REF_COUNT_FMT, G_OBJECT_LOG_REF_COUNT (self->priv->song)); bt_song_stop (self->priv->song); g_object_unref (self->priv->song); self->priv->song = NULL; } if (self->priv->main_window) { GST_INFO ("main_window: %" G_OBJECT_REF_COUNT_FMT, G_OBJECT_LOG_REF_COUNT (self->priv->main_window)); //main-menu.c::on_menu_quit_activate gtk_widget_destroy (GTK_WIDGET (self->priv->main_window)); } GST_DEBUG (" more unrefs"); g_object_try_unref (self->priv->ui_resources); g_object_try_unref (self->priv->pbc_socket); g_object_try_unref (self->priv->pbc_ic); g_object_try_unref (self->priv->ic_registry); g_object_try_unref (self->priv->change_log); g_object_try_unref (self->priv->audio_session); GST_DEBUG (" chaining up"); G_OBJECT_CLASS (bt_edit_application_parent_class)->dispose (object); GST_DEBUG (" done"); }
static void bt_wavetable_dispose (GObject * const object) { const BtWavetable *const self = BT_WAVETABLE (object); GList *node; return_if_disposed (); self->priv->dispose_has_run = TRUE; GST_DEBUG ("!!!! self=%p", self); g_object_try_weak_unref (self->priv->song); // unref list of waves if (self->priv->waves) { for (node = self->priv->waves; node; node = g_list_next (node)) { GST_DEBUG (" free wave : %" G_OBJECT_REF_COUNT_FMT, G_OBJECT_LOG_REF_COUNT (node->data)); g_object_try_unref (node->data); node->data = NULL; } } G_OBJECT_CLASS (bt_wavetable_parent_class)->dispose (object); }
static void on_song_changed (const BtEditApplication * app, GParamSpec * arg, gpointer user_data) { BtMainPageInfo *self = BT_MAIN_PAGE_INFO (user_data); BtSong *song; GtkTextBuffer *buffer; gchar *name, *genre, *author, *create_dts, *change_dts; gulong bpm, tpb, bars; gchar *info; GST_INFO ("song has changed : app=%p, self=%p", app, self); g_object_try_unref (self->priv->song_info); // get song from app g_object_get (self->priv->app, "song", &song, NULL); if (!song) { self->priv->song_info = NULL; return; } GST_INFO ("song: %" G_OBJECT_REF_COUNT_FMT, G_OBJECT_LOG_REF_COUNT (song)); g_object_get (song, "song-info", &self->priv->song_info, NULL); // update info fields g_object_get (self->priv->song_info, "name", &name, "genre", &genre, "author", &author, "info", &info, "bpm", &bpm, "tpb", &tpb, "bars", &bars, "create-dts", &create_dts, "change-dts", &change_dts, NULL); g_signal_handlers_block_matched (self->priv->name, G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, 0, 0, NULL, on_name_changed, (gpointer) self); gtk_entry_set_text (self->priv->name, safe_string (name)); g_free (name); g_signal_handlers_unblock_matched (self->priv->name, G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, 0, 0, NULL, on_name_changed, (gpointer) self); g_signal_handlers_block_matched (self->priv->genre, G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, 0, 0, NULL, on_genre_changed, (gpointer) self); gtk_entry_set_text (self->priv->genre, safe_string (genre)); g_free (genre); g_signal_handlers_unblock_matched (self->priv->genre, G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, 0, 0, NULL, on_genre_changed, (gpointer) self); g_signal_handlers_block_matched (self->priv->author, G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, 0, 0, NULL, on_author_changed, (gpointer) self); gtk_entry_set_text (self->priv->author, safe_string (author)); g_free (author); g_signal_handlers_unblock_matched (self->priv->author, G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, 0, 0, NULL, on_author_changed, (gpointer) self); g_signal_handlers_block_matched (self->priv->bpm, G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, 0, 0, NULL, on_bpm_changed, (gpointer) self); gtk_spin_button_set_value (self->priv->bpm, (gdouble) bpm); g_signal_handlers_unblock_matched (self->priv->bpm, G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, 0, 0, NULL, on_bpm_changed, (gpointer) self); g_signal_handlers_block_matched (self->priv->tpb, G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, 0, 0, NULL, on_tpb_changed, (gpointer) self); gtk_spin_button_set_value (self->priv->tpb, (gdouble) tpb); g_signal_handlers_unblock_matched (self->priv->tpb, G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, 0, 0, NULL, on_tpb_changed, (gpointer) self); g_signal_handlers_block_matched (self->priv->beats, G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, 0, 0, NULL, on_beats_changed, (gpointer) self); gtk_spin_button_set_value (self->priv->beats, (gdouble) (bars / tpb)); g_signal_handlers_unblock_matched (self->priv->beats, G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, 0, 0, NULL, on_beats_changed, (gpointer) self); /* the iso date is not nice for the user * struct tm tm; * char dts[255]; * * strptime(create_dts, "%FT%TZ", &tm); * strftime(dts, sizeof(buf), "%F %T", &tm); * * but the code below is simpler and works too :) */ create_dts[10] = ' '; create_dts[19] = '\0'; gtk_entry_set_text (self->priv->date_created, create_dts); g_free (create_dts); change_dts[10] = ' '; change_dts[19] = '\0'; gtk_entry_set_text (self->priv->date_changed, change_dts); g_free (change_dts); buffer = gtk_text_view_get_buffer (self->priv->info); g_signal_handlers_block_matched (buffer, G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, 0, 0, NULL, on_info_changed, (gpointer) self); gtk_text_buffer_set_text (buffer, safe_string (info), -1); g_free (info); g_signal_handlers_unblock_matched (buffer, G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, 0, 0, NULL, on_info_changed, (gpointer) self); g_signal_connect (self->priv->song_info, "notify::name", G_CALLBACK (on_name_notify), (gpointer) self); // release the references g_object_unref (song); GST_INFO ("song has changed done"); }
/** * bt_edit_application_load_song: * @self: the application instance to load a new song in * @file_name: the song filename to load * @err: where to store the error message in case of an error, or %NULL * * Loads a new song. If there is a previous song instance it will be freed. * * Returns: true for success */ gboolean bt_edit_application_load_song (const BtEditApplication * self, const char *file_name, GError ** err) { gboolean res = FALSE; BtSongIO *loader; BtSong *song; g_return_val_if_fail (BT_IS_EDIT_APPLICATION (self), FALSE); GST_INFO ("song name = %s", file_name); if ((loader = bt_song_io_from_file (file_name, err))) { BtSetup *setup; GList *missing_machines, *missing_waves; bt_edit_application_ui_lock (self); g_signal_connect (loader, "notify::status", G_CALLBACK (on_songio_status_changed), (gpointer) self); // create new song and release the previous one song = bt_song_new (BT_APPLICATION (self)); g_object_set ((gpointer) self, "song", NULL, NULL); #ifdef USE_DEBUG // do sanity check that bin is empty { GstBin *bin; g_object_get ((gpointer) self, "bin", &bin, NULL); if (GST_BIN_NUMCHILDREN (bin)) { GList *node = GST_BIN_CHILDREN (bin); GST_WARNING ("bin.num_children=%d has left-overs", GST_BIN_NUMCHILDREN (bin)); while (node) { GST_WARNING_OBJECT (node->data, "removing object %" G_OBJECT_REF_COUNT_FMT, G_OBJECT_LOG_REF_COUNT (node->data)); gst_bin_remove (bin, GST_ELEMENT (node->data)); node = GST_BIN_CHILDREN (bin); } } gst_object_unref (bin); } #endif // this is synchronous execution // https://github.com/Buzztrax/buzztrax/issues/52 // if we bump glib from 2.32 -> 2.36 we can use GTask and // g_task_run_in_thread() if (bt_song_io_load (loader, song, err)) { BtMachine *machine; // get sink-machine g_object_get (song, "setup", &setup, NULL); if ((machine = bt_setup_get_machine_by_type (setup, BT_TYPE_SINK_MACHINE))) { if (bt_machine_enable_input_post_level (machine)) { // DEBUG //bt_song_write_to_highlevel_dot_file(song); // DEBUG // set new song g_object_set ((gpointer) self, "song", song, NULL); res = TRUE; GST_INFO ("new song activated"); } else { GST_WARNING ("Can't add input level/gain element in sink machine"); } GST_DEBUG ("unreffing stuff after loading"); g_object_unref (machine); } else { GST_WARNING ("Can't look up sink machine"); } g_object_unref (setup); } else { GST_WARNING ("could not load song \"%s\"", file_name); } self->priv->unsaved = FALSE; g_object_notify (G_OBJECT (self), "unsaved"); bt_edit_application_ui_unlock (self); // get missing element info bt_child_proxy_get (song, "setup::missing-machines", &missing_machines, "wavetable::missing-waves", &missing_waves, NULL); // tell about missing machines and/or missing waves if (missing_machines || missing_waves) { GtkWidget *dialog; if ((dialog = GTK_WIDGET (bt_missing_song_elements_dialog_new (missing_machines, missing_waves)))) { bt_edit_application_attach_child_window (self, GTK_WINDOW (dialog)); gtk_widget_show_all (dialog); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); } } g_object_unref (song); g_object_unref (loader); } else { GST_WARNING ("Unknown extension \"%s\"", file_name); } return res; }
/** * bt_edit_application_new_song: * @self: the application instance to create a new song in * * Creates a new blank song instance. If there is a previous song instance it * will be freed. * * Returns: %TRUE for success */ gboolean bt_edit_application_new_song (const BtEditApplication * self) { gboolean res = FALSE; BtSong *song; BtSetup *setup; BtMachine *machine; gchar *id; gulong bars; GError *err = NULL; g_return_val_if_fail (BT_IS_EDIT_APPLICATION (self), FALSE); // create new song song = bt_song_new (BT_APPLICATION (self)); bt_child_proxy_get (song, "setup", &setup, "song-info::bars", &bars, NULL); // make initial song length 4 timelines bt_child_proxy_set (song, "sequence::length", bars * 4, NULL); // add audiosink id = bt_setup_get_unique_machine_id (setup, "master"); machine = BT_MACHINE (bt_sink_machine_new (song, id, &err)); if (err == NULL) { GHashTable *properties; GST_DEBUG ("sink-machine=%" G_OBJECT_REF_COUNT_FMT, G_OBJECT_LOG_REF_COUNT (machine)); g_object_get (machine, "properties", &properties, NULL); if (properties) { gchar str[G_ASCII_DTOSTR_BUF_SIZE]; g_hash_table_insert (properties, g_strdup ("xpos"), g_strdup (g_ascii_dtostr (str, G_ASCII_DTOSTR_BUF_SIZE, 0.0))); g_hash_table_insert (properties, g_strdup ("ypos"), g_strdup (g_ascii_dtostr (str, G_ASCII_DTOSTR_BUF_SIZE, 0.0))); } if (bt_machine_enable_input_post_level (machine)) { GST_DEBUG ("sink-machine=%" G_OBJECT_REF_COUNT_FMT, G_OBJECT_LOG_REF_COUNT (machine)); // set new song in application g_object_set ((gpointer) self, "song", song, NULL); res = TRUE; } else { GST_WARNING ("Can't add input level/gain element in sink machine"); } GST_DEBUG ("sink-machine=%" G_OBJECT_REF_COUNT_FMT, G_OBJECT_LOG_REF_COUNT (machine)); } else { GST_WARNING ("Can't create sink machine: %s", err->message); g_error_free (err); gst_object_unref (machine); } g_free (id); self->priv->unsaved = FALSE; g_object_notify (G_OBJECT (self), "unsaved"); // release references g_object_unref (setup); g_object_unref (song); return res; }
gint main (gint argc, gchar ** argv) { gboolean res = FALSE; gchar *command = NULL, *input_file_name = NULL; BtEditApplication *app; GOptionContext *ctx = NULL; GOptionGroup *group; GError *err = NULL; #ifdef ENABLE_NLS setlocale (LC_ALL, ""); bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); textdomain (GETTEXT_PACKAGE); #endif /* ENABLE_NLS */ bt_setup_for_local_install (); GOptionEntry options[] = { {"version", '\0', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) parse_goption_arg, N_("Print application version"), NULL} , {"command", 'c', 0, G_OPTION_ARG_STRING, &command, N_("Command name"), "{load}"} , {"input-file", 'i', 0, G_OPTION_ARG_FILENAME, &input_file_name, N_("Input file name"), N_("<songfile>")} , {NULL} }; // init libraries ctx = g_option_context_new (NULL); //g_option_context_add_main_entries (ctx, options, GETTEXT_PACKAGE); group = g_option_group_new ("main", _("buzztrax-edit options"), _("Show buzztrax-edit options"), argv[0], NULL); g_option_group_add_entries (group, options); g_option_group_set_translation_domain (group, GETTEXT_PACKAGE); g_option_context_set_main_group (ctx, group); bt_init_add_option_groups (ctx); g_option_context_add_group (ctx, btic_init_get_option_group ()); g_option_context_add_group (ctx, gtk_get_option_group (TRUE)); g_option_context_add_group (ctx, clutter_get_option_group_without_init ()); g_option_context_add_group (ctx, gtk_clutter_get_option_group ()); if (!g_option_context_parse (ctx, &argc, &argv, &err)) { g_print ("Error initializing: %s\n", safe_string (err->message)); g_error_free (err); goto Done; } GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "bt-edit", 0, "music production environment / editor ui"); // give some global context info g_set_prgname ("buzztrax-edit"); g_set_application_name ("Buzztrax"); gtk_window_set_default_icon_name ("buzztrax"); g_setenv ("PULSE_PROP_media.role", "production", TRUE); extern gboolean bt_memory_audio_src_plugin_init (GstPlugin * const plugin); gst_plugin_register_static (GST_VERSION_MAJOR, GST_VERSION_MINOR, "memoryaudiosrc", "Plays audio from memory", bt_memory_audio_src_plugin_init, VERSION, "LGPL", PACKAGE, PACKAGE_NAME, "http://www.buzztrax.org"); GST_INFO ("starting: thread=%p", g_thread_self ()); app = bt_edit_application_new (); // set a default command, if a file is given if (!command && BT_IS_STRING (input_file_name)) { command = g_strdup ("l"); } if (command) { // depending on the options call the correct method if (!strcmp (command, "l") || !strcmp (command, "load")) { if (!BT_IS_STRING (input_file_name)) { usage (argc, argv, ctx); // if commandline options where wrong, just start res = bt_edit_application_run (app); } else { res = bt_edit_application_load_and_run (app, input_file_name); } } else { usage (argc, argv, ctx); // if commandline options where wrong, just start res = bt_edit_application_run (app); } } else { res = bt_edit_application_run (app); } // free application GST_INFO ("app %" G_OBJECT_REF_COUNT_FMT, G_OBJECT_LOG_REF_COUNT (app)); g_object_unref (app); Done: g_free (command); g_free (input_file_name); g_option_context_free (ctx); return !res; }