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; }
static void bt_song_info_get_property (GObject * const object, const guint property_id, GValue * const value, GParamSpec * const pspec) { const BtSongInfo *const self = BT_SONG_INFO (object); return_if_disposed (); switch (property_id) { case SONG_INFO_SONG: g_value_set_object (value, self->priv->song); break; case SONG_INFO_TAGLIST: g_value_set_pointer (value, gst_tag_list_copy (self->priv->taglist)); break; case SONG_INFO_FILE_NAME: g_value_set_string (value, self->priv->file_name); break; case SONG_INFO_INFO: g_value_set_string (value, self->priv->info); break; case SONG_INFO_NAME: g_value_set_string (value, self->priv->name); break; case SONG_INFO_GENRE: g_value_set_string (value, self->priv->genre); break; case SONG_INFO_AUTHOR: g_value_set_string (value, self->priv->author); break; case SONG_INFO_BPM: g_value_set_ulong (value, self->priv->beats_per_minute); break; case SONG_INFO_TPB: g_value_set_ulong (value, self->priv->ticks_per_beat); break; case SONG_INFO_BARS: g_value_set_ulong (value, self->priv->bars); break; case SONG_INFO_CREATE_DTS: g_value_set_string (value, self->priv->create_dts); break; case SONG_INFO_CHANGE_DTS: g_value_set_string (value, self->priv->change_dts); break; case SONG_INFO_TICK_DURATION: g_value_set_uint64 (value, self->priv->tick_duration); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } }
static void bt_song_info_dispose (GObject * const object) { const BtSongInfo *const self = BT_SONG_INFO (object); return_if_disposed (); self->priv->dispose_has_run = TRUE; GST_DEBUG ("!!!! self=%p", self); g_object_try_weak_unref (self->priv->song); gst_tag_list_unref (self->priv->taglist); G_OBJECT_CLASS (bt_song_info_parent_class)->dispose (object); }
static void test_bt_song_info_tick_to_time (BT_TEST_ARGS) { BT_TEST_START; GST_INFO ("-- arrange --"); BtSongInfo *song_info = BT_SONG_INFO (check_gobject_get_object_property (song, "song-info")); g_object_set (song_info, "bpm", 250L, "tpb", 16L, NULL); GST_INFO ("-- act --"); GstClockTime ts = bt_song_info_tick_to_time (song_info, 8); GST_INFO ("-- assert --"); ck_assert_int_eq (ts, 120 * GST_MSECOND); GST_INFO ("-- cleanup --"); g_object_unref (song_info); BT_TEST_END; }
static void bt_song_info_finalize (GObject * const object) { const BtSongInfo *const self = BT_SONG_INFO (object); GST_DEBUG ("!!!! self=%p", self); g_date_free (self->priv->tag_date); g_free (self->priv->file_name); g_free (self->priv->info); g_free (self->priv->name); g_free (self->priv->genre); g_free (self->priv->author); g_free (self->priv->create_dts); g_free (self->priv->change_dts); G_OBJECT_CLASS (bt_song_info_parent_class)->finalize (object); }
static void test_bt_song_info_date_stamps (BT_TEST_ARGS) { BT_TEST_START; GST_INFO ("-- arrange --"); BtSongInfo *song_info = BT_SONG_INFO (check_gobject_get_object_property (song, "song-info")); GST_INFO ("-- act --"); gchar *create_dts = check_gobject_get_str_property (song_info, "create-dts"); GST_INFO ("-- assert --"); fail_unless (create_dts != NULL, NULL); ck_assert_gobject_str_eq (song_info, "change-dts", create_dts); GST_INFO ("-- cleanup --"); g_free (create_dts); g_object_unref (song_info); BT_TEST_END; }
static void test_bt_song_info_seconds_since_last_saved (BT_TEST_ARGS) { BT_TEST_START; GST_INFO ("-- arrange --"); BtSongInfo *song_info = BT_SONG_INFO (check_gobject_get_object_property (song, "song-info")); GST_INFO ("-- act --"); // TODO: waiting one sec makes this the slowest test :/ g_usleep (G_USEC_PER_SEC); gint ts = bt_song_info_get_seconds_since_last_saved (song_info); GST_INFO ("-- assert --"); ck_assert_int_gt (ts, 0); GST_INFO ("-- cleanup --"); g_object_unref (song_info); BT_TEST_END; }
static BtPersistence * bt_song_info_persistence_load (const GType type, const BtPersistence * const persistence, xmlNodePtr node, GError ** err, va_list var_args) { const BtSongInfo *const self = BT_SONG_INFO (persistence); GST_DEBUG ("PERSISTENCE::song-info"); g_assert (node); for (node = node->children; node; node = node->next) { if (!xmlNodeIsText (node)) { xmlNodePtr const child_node = node->children; if (child_node && xmlNodeIsText (child_node) && !xmlIsBlankNode (child_node)) { xmlChar *const elem = xmlNodeGetContent (child_node); if (elem) { const gchar *const property_name = (gchar *) node->name; GST_DEBUG (" \"%s\"=\"%s\"", property_name, elem); // depending on the name of the property, treat it's type if (!strncmp (property_name, "info", 4) || !strncmp (property_name, "name", 4) || !strncmp (property_name, "genre", 5) || !strncmp (property_name, "author", 6) || !strncmp (property_name, "create-dts", 10) || !strncmp (property_name, "change-dts", 10) ) { g_object_set ((gpointer) self, property_name, elem, NULL); } else if (!strncmp (property_name, "bpm", 3) || !strncmp (property_name, "tpb", 3) || !strncmp (property_name, "bars", 4)) { g_object_set ((gpointer) self, property_name, atol ((char *) elem), NULL); } xmlFree (elem); } } } } return BT_PERSISTENCE (persistence); }
static void test_bt_machine_state_not_overridden (BT_TEST_ARGS) { BT_TEST_START; GST_INFO ("-- arrange --"); BtSequence *sequence = (BtSequence *) check_gobject_get_object_property (song, "sequence"); BtSongInfo *song_info = BT_SONG_INFO (check_gobject_get_object_property (song, "song-info")); BtMachine *src = BT_MACHINE (bt_source_machine_new (song, "gen", "simsyn", 0L, NULL)); BtMachine *sink = BT_MACHINE (bt_sink_machine_new (song, "sink", NULL)); bt_wire_new (song, src, sink, NULL); BtCmdPattern *pattern = bt_cmd_pattern_new (song, src, BT_PATTERN_CMD_SOLO); GstElement *element = (GstElement *) check_gobject_get_object_property (src, "machine"); /* duration: 0:00:00.480000000 */ g_object_set (song_info, "bpm", 250L, "tpb", 16L, NULL); g_object_set (sequence, "length", 32L, NULL); bt_sequence_add_track (sequence, src, -1); bt_sequence_set_pattern (sequence, 4, 0, pattern); g_object_set (element, "wave", /* silence */ 4, NULL); GST_INFO ("-- act --"); g_object_set (src, "state", BT_MACHINE_STATE_MUTE, NULL); bt_machine_update_default_state_value (src); bt_song_play (song); check_run_main_loop_until_eos_or_error (song); GST_INFO ("-- assert --"); ck_assert_gobject_guint_eq (src, "state", BT_MACHINE_STATE_MUTE); GST_INFO ("-- cleanup --"); gst_object_unref (element); g_object_unref (pattern); g_object_unref (sequence); g_object_unref (song_info); BT_TEST_END; }
static void test_bt_song_info_update_tpb (BT_TEST_ARGS) { BT_TEST_START; GST_INFO ("-- arrange --"); GstClockTime t1, t2; BtSongInfo *song_info = BT_SONG_INFO (check_gobject_get_object_property (song, "song-info")); g_object_set (song_info, "tpb", 8, NULL); g_object_get (song_info, "tick-duration", &t1, NULL); GST_INFO ("-- act --"); g_object_set (song_info, "tpb", 4, NULL); g_object_get (song_info, "tick-duration", &t2, NULL); GST_INFO ("-- assert --"); ck_assert_uint64_eq (t2, t1 + t1); GST_INFO ("-- cleanup --"); g_object_unref (song_info); BT_TEST_END; }
static void bt_song_info_set_property (GObject * const object, const guint property_id, const GValue * const value, GParamSpec * const pspec) { const BtSongInfo *const self = BT_SONG_INFO (object); return_if_disposed (); switch (property_id) { case SONG_INFO_SONG: self->priv->song = BT_SONG (g_value_get_object (value)); g_object_try_weak_ref (self->priv->song); GST_DEBUG ("set the song for song-info: %p", self->priv->song); break; case SONG_INFO_FILE_NAME: g_free (self->priv->file_name); self->priv->file_name = g_value_dup_string (value); GST_DEBUG ("set the file-name for song_info: %s", self->priv->file_name); break; case SONG_INFO_INFO:{ const gchar *str = g_value_get_string (value); if ((self->priv->info != str) && (!self->priv->info || !str || strcmp (self->priv->info, str))) { g_free (self->priv->info); if (str) { self->priv->info = g_value_dup_string (value); gst_tag_list_add (self->priv->taglist, GST_TAG_MERGE_REPLACE, GST_TAG_DESCRIPTION, self->priv->info, NULL); } else { self->priv->info = NULL; gst_tag_list_remove_tag (self->priv->taglist, GST_TAG_DESCRIPTION); } GST_DEBUG ("set the info for song_info: %s", self->priv->info); } break; } case SONG_INFO_NAME:{ const gchar *str = g_value_get_string (value); if ((self->priv->name != str) && (!self->priv->name || !str || strcmp (self->priv->name, str))) { g_free (self->priv->name); if (str) { self->priv->name = g_value_dup_string (value); gst_tag_list_add (self->priv->taglist, GST_TAG_MERGE_REPLACE, GST_TAG_TITLE, self->priv->name, NULL); } else { self->priv->name = NULL; gst_tag_list_remove_tag (self->priv->taglist, GST_TAG_TITLE); } GST_DEBUG ("set the name for song_info: %s", self->priv->name); } break; } case SONG_INFO_GENRE:{ const gchar *str = g_value_get_string (value); if ((self->priv->genre != str) && (!self->priv->genre || !str || strcmp (self->priv->genre, str))) { g_free (self->priv->genre); if (str) { self->priv->genre = g_value_dup_string (value); gst_tag_list_add (self->priv->taglist, GST_TAG_MERGE_REPLACE, GST_TAG_GENRE, self->priv->genre, NULL); } else { self->priv->genre = NULL; gst_tag_list_remove_tag (self->priv->taglist, GST_TAG_GENRE); } GST_DEBUG ("set the genre for song_info: %s", self->priv->genre); } break; } case SONG_INFO_AUTHOR:{ const gchar *str = g_value_get_string (value); if ((self->priv->author != str) && (!self->priv->author || !str || strcmp (self->priv->author, str))) { g_free (self->priv->author); if (str) { self->priv->author = g_value_dup_string (value); gst_tag_list_add (self->priv->taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ARTIST, self->priv->author, NULL); } else { self->priv->author = NULL; gst_tag_list_remove_tag (self->priv->taglist, GST_TAG_ARTIST); } GST_DEBUG ("set the author for song_info: %s", self->priv->author); } break; } case SONG_INFO_BPM:{ gulong val = g_value_get_ulong (value); if (self->priv->beats_per_minute != val) { self->priv->beats_per_minute = val; gst_tag_list_add (self->priv->taglist, GST_TAG_MERGE_REPLACE, GST_TAG_BEATS_PER_MINUTE, (gdouble) self->priv->beats_per_minute, NULL); bt_song_info_tempo_changed (self); GST_DEBUG ("set the bpm for song_info: %lu", self->priv->beats_per_minute); } break; } case SONG_INFO_TPB:{ gulong val = g_value_get_ulong (value); if (self->priv->ticks_per_beat != val) { self->priv->ticks_per_beat = val; bt_song_info_tempo_changed (self); GST_DEBUG ("set the tpb for song_info: %lu", self->priv->ticks_per_beat); } } break; case SONG_INFO_BARS:{ gulong val = g_value_get_ulong (value); if (self->priv->bars != val) { self->priv->bars = val; bt_song_info_tempo_changed (self); GST_DEBUG ("set the bars for song_info: %lu", self->priv->bars); } break; } case SONG_INFO_CREATE_DTS:{ const gchar *const dts = g_value_get_string (value); if (dts) { if (strlen (dts) == DTS_LEN) { strcpy (self->priv->create_dts, dts); } } else { time_t now = time (NULL); /* this is ISO 8601 Date and Time Format * %F Equivalent to %Y-%m-%d (the ISO 8601 date format). (C99) * %T The time in 24-hour notation (%H:%M:%S). (SU) */ strftime (self->priv->create_dts, DTS_LEN + 1, "%FT%TZ", gmtime (&now)); } break; } case SONG_INFO_CHANGE_DTS:{ const gchar *const dts = g_value_get_string (value); if (dts) { if (strlen (dts) == DTS_LEN) { struct tm tm = { 0, }; strcpy (self->priv->change_dts, dts); // parse date and update tag strptime (dts, "%FT%TZ", &tm); g_date_set_time_t (self->priv->tag_date, mktime (&tm)); gst_tag_list_add (self->priv->taglist, GST_TAG_MERGE_REPLACE, GST_TAG_DATE, self->priv->tag_date, NULL); } } else { time_t now = time (NULL); strftime (self->priv->change_dts, DTS_LEN + 1, "%FT%TZ", gmtime (&now)); g_date_set_time_t (self->priv->tag_date, now); gst_tag_list_add (self->priv->taglist, GST_TAG_MERGE_REPLACE, GST_TAG_DATE, self->priv->tag_date, NULL); } break; } default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } }
/** * bt_song_info_new: * @song: the song the new instance belongs to * * Create a new instance * * Returns: the new instance or %NULL in case of an error */ BtSongInfo * bt_song_info_new (const BtSong * const song) { return BT_SONG_INFO (g_object_new (BT_TYPE_SONG_INFO, "song", song, NULL)); }