示例#1
0
static void
on_page_switched (GtkNotebook * notebook, GParamSpec * arg, gpointer user_data)
{
  BtMainPages *self = BT_MAIN_PAGES (user_data);
  BtSong *song;
  GHashTable *properties;
  GtkWidget *page;
  gchar *prop;
  guint page_num;

  // get objects
  g_object_get (self->priv->app, "song", &song, NULL);
  if (!song) {
    return;
  }

  g_object_get (notebook, "page", &page_num, NULL);
  GST_INFO ("page has switched : self=%p, page=%d", self, page_num);

  // ensure the new page gets focused, this sounds like a hack though
  page = gtk_notebook_get_nth_page (notebook, page_num);
  GTK_WIDGET_GET_CLASS (page)->focus (page, GTK_DIR_TAB_FORWARD);

  // remember page
  bt_child_proxy_get (song, "setup::properties", &properties, NULL);
  prop = g_strdup_printf ("%u", page_num);
  g_hash_table_insert (properties, g_strdup ("active-page"), prop);

  // release the reference
  g_object_unref (song);
  GST_INFO ("page-switched done");
}
示例#2
0
static xmlNodePtr
bt_song_info_persistence_save (const BtPersistence * const persistence,
    xmlNodePtr const parent_node)
{
  const BtSongInfo *const self = BT_SONG_INFO (persistence);
  xmlNodePtr node = NULL;

  GST_DEBUG ("PERSISTENCE::song-info");

  if ((node = xmlNewChild (parent_node, NULL, XML_CHAR_PTR ("meta"), NULL))) {
    if (!strcmp (self->priv->name, DEFAULT_SONG_NAME)) {
      gchar *file_path = NULL, *file_name, *ext;

      bt_child_proxy_get (self->priv->song, "song-io::file-name", &file_path,
          NULL);
      if (file_path) {
        file_name = g_path_get_basename (file_path);
        if ((ext = strrchr (file_name, '.'))) {
          *ext = '\0';
        }
        GST_INFO ("using '%s' instead of default title", file_name);
        g_object_set ((gpointer) self, "name", file_name, NULL);
        g_free (file_name);
        g_free (file_path);
      }
    }

    if (self->priv->info) {
      xmlNewChild (node, NULL, XML_CHAR_PTR ("info"),
          XML_CHAR_PTR (self->priv->info));
    }
    if (self->priv->name) {
      xmlNewChild (node, NULL, XML_CHAR_PTR ("name"),
          XML_CHAR_PTR (self->priv->name));
    }
    if (self->priv->genre) {
      xmlNewChild (node, NULL, XML_CHAR_PTR ("genre"),
          XML_CHAR_PTR (self->priv->genre));
    }
    if (self->priv->author) {
      xmlNewChild (node, NULL, XML_CHAR_PTR ("author"),
          XML_CHAR_PTR (self->priv->author));
    }
    if (self->priv->create_dts) {
      xmlNewChild (node, NULL, XML_CHAR_PTR ("create-dts"),
          XML_CHAR_PTR (self->priv->create_dts));
    }
    if (self->priv->change_dts) {
      xmlNewChild (node, NULL, XML_CHAR_PTR ("change-dts"),
          XML_CHAR_PTR (self->priv->change_dts));
    }
    xmlNewChild (node, NULL, XML_CHAR_PTR ("bpm"),
        XML_CHAR_PTR (bt_str_format_ulong (self->priv->beats_per_minute)));
    xmlNewChild (node, NULL, XML_CHAR_PTR ("tpb"),
        XML_CHAR_PTR (bt_str_format_ulong (self->priv->ticks_per_beat)));
    xmlNewChild (node, NULL, XML_CHAR_PTR ("bars"),
        XML_CHAR_PTR (bt_str_format_ulong (self->priv->bars)));
  }
  return node;
}
示例#3
0
static void
on_song_changed (const BtEditApplication * app, GParamSpec * arg,
    gpointer user_data)
{
  BtMainPages *self = BT_MAIN_PAGES (user_data);
  BtSong *song;
  GHashTable *properties;
  gchar *prop;

  GST_INFO ("song has changed : app=%p, self=%p", app, self);
  // get song from app
  g_object_get (self->priv->app, "song", &song, NULL);
  if (!song) {
    return;
  }
  // restore page
  bt_child_proxy_get (song, "setup::properties", &properties, NULL);
  if ((prop = (gchar *) g_hash_table_lookup (properties, "active-page"))) {
    guint page = atoi (prop);

    gtk_notebook_set_current_page (GTK_NOTEBOOK (self), page);
    GST_INFO ("reactivate page %d", page);
  }
  // release the reference
  g_object_unref (song);
  GST_INFO ("song has changed done");
}
示例#4
0
static void
on_song_play_pos_notify (BtSong * song, GParamSpec * arg, gpointer user_data)
{
  gulong pos, length;

  bt_child_proxy_get ((gpointer) song, "sequence::length", &length,
      "play-pos", &pos, NULL);
  GST_DEBUG ("%ld < %lu < %lu", old_pos, pos, length);
  if ((pos >= length) || (pos < old_pos)) {
    GST_INFO ("we reached the end");
  }
  old_pos = pos;
}
示例#5
0
static void
test_bt_child_proxy_get (BT_TEST_ARGS)
{
  BT_TEST_START;
  GST_INFO ("-- arrange --");
  gulong length;

  GST_INFO ("-- act --");
  bt_child_proxy_get (song, "sequence::length", &length, NULL);

  GST_INFO ("-- assert --");
  ck_assert_int_gt (length, 0);

  GST_INFO ("-- cleanup --");
  BT_TEST_END;
}
示例#6
0
static void
test_bt_child_proxy_set (BT_TEST_ARGS)
{
  BT_TEST_START;
  GST_INFO ("-- arrange --");
  gboolean loop;

  GST_INFO ("-- act --");
  bt_child_proxy_set (song, "sequence::loop", TRUE, NULL);

  GST_INFO ("-- assert --");
  bt_child_proxy_get (song, "sequence::loop", &loop, NULL);
  ck_assert_int_eq (TRUE, loop);

  GST_INFO ("-- cleanup --");
  BT_TEST_END;
}
示例#7
0
static void
test_bt_child_proxy_set_property (BT_TEST_ARGS)
{
  BT_TEST_START;
  GST_INFO ("-- arrange --");
  gboolean loop;
  GValue value = { 0, };
  g_value_init (&value, G_TYPE_BOOLEAN);
  g_value_set_boolean (&value, TRUE);

  GST_INFO ("-- act --");
  bt_child_proxy_set_property ((GObject *) song, "sequence::loop", &value);

  GST_INFO ("-- assert --");
  bt_child_proxy_get (song, "sequence::loop", &loop, NULL);
  ck_assert_int_eq (TRUE, loop);

  GST_INFO ("-- cleanup --");
  g_value_unset (&value);
  BT_TEST_END;
}
示例#8
0
/**
 * bt_edit_application_save_song:
 * @self: the application instance to save a song from
 * @file_name: the song filename to save
 * @err: where to store the error message in case of an error, or %NULL
 *
 * Saves a song.
 *
 * Returns: true for success
 */
gboolean
bt_edit_application_save_song (const BtEditApplication * self,
    const char *file_name, GError ** err)
{
  gboolean res = FALSE;
  BtSongIO *saver;

  g_return_val_if_fail (BT_IS_EDIT_APPLICATION (self), FALSE);

  GST_INFO ("song name = %s", file_name);

  if ((saver = bt_song_io_from_file (file_name, err))) {
    gchar *old_file_name = NULL, *bak_file_name = NULL;

    bt_edit_application_ui_lock (self);
    g_signal_connect (saver, "notify::status",
        G_CALLBACK (on_songio_status_changed), (gpointer) self);

    // update the time-stamp
    bt_child_proxy_set (self->priv->song, "song-info::change-dts", NULL, NULL);

    bt_child_proxy_get (self->priv->song, "song-info::file-name",
        &old_file_name, NULL);

    /* save file saving (bak files)
     * save
     *   new file (!old_file_name)
     *     chosen file-name already exist
     *       - move to <existing>.bak
     *       - save newfile
     *       - if saving failed, move <existing>.bak back
     *       - if saving worked, delete <existing>.bak
     *     chosen file-name does not exist
     *       - save newfile
     *   existing file
     *     - move to <existing>.bak
     *       - save newfile
     *       - if saving failed, move <existing>.bak back
     * save-as
     *   new file (!old_file_name)
     *     like save of a new-file
     *   existing file
     *     chosen file-name already exist
     *       - like save of an existing file
     *     chosen file-name does not exist
     *       - save newfile
     *
     * - check how other apps do it (check if inodes change if various scenarios)
     * - when loading a file, should we keep the file-handle open, so then when
     *   saving, we can just update it?
     *   - this can help with the wavetable (only updated changed wavetable slots)
     *   - if we can't update it, we can use gsf_input_copy() for external files
     *     (if unchanged)
     * - if the user deletes the file that is currently open -> user error
     */
    if (g_file_test (file_name, G_FILE_TEST_EXISTS)) {
      bak_file_name = g_strconcat (file_name, ".bak", NULL);
      g_rename (file_name, bak_file_name);
    }
    if (bt_song_io_save (saver, self->priv->song, err)) {
      res = TRUE;
      if (!old_file_name || strcmp (old_file_name, file_name)) {
        // saving worked, we remove the bak file as
        // - there was no old_file_name and/or
        // - user has chosen to overwrite this file
        g_unlink (bak_file_name);
      }
    } else {
      GST_WARNING ("could not save song \"%s\"", file_name);
      if (bak_file_name) {
        // saving failed, so move a file we renamed to .bak back
        g_rename (bak_file_name, file_name);
      }
    }
    GST_INFO ("saving done");
    self->priv->unsaved = FALSE;
    g_object_notify (G_OBJECT (self), "unsaved");
    self->priv->need_dts_reset = TRUE;

    bt_edit_application_ui_unlock (self);

    g_free (old_file_name);
    g_free (bak_file_name);
    g_object_unref (saver);
  } else {
    GST_WARNING ("Unknown extension \"%s\"", file_name);
  }
  return res;
}
示例#9
0
/**
 * 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;
}
示例#10
0
/**
 * 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;
}