[rhythmbox] add support for beats per minute tag (bug #454889)
- From: Jonathan Matthew <jmatthew src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [rhythmbox] add support for beats per minute tag (bug #454889)
- Date: Sat, 12 Jun 2010 12:47:18 +0000 (UTC)
commit 512f875ebc36a72bc6e2f9ad5fcba5ddc821b6f8
Author: Mattias Eriksson <snaggen acc umu se>
Date: Sat Jun 12 22:45:22 2010 +1000
add support for beats per minute tag (bug #454889)
backends/gstreamer/rb-encoder-gst.c | 7 ++++
bindings/python/rb.defs | 1 +
data/ui/general-prefs.ui | 30 +++++++++++++----
data/ui/song-info.ui | 58 ++++++++++++++++++++++++++++++--
metadata/rb-metadata-common.c | 3 ++
metadata/rb-metadata-gst-common.c | 4 ++
metadata/rb-metadata.h | 1 +
rhythmdb/rhythmdb-private.h | 1 +
rhythmdb/rhythmdb-tree.c | 3 ++
rhythmdb/rhythmdb.c | 49 +++++++++++++++++++--------
rhythmdb/rhythmdb.h | 1 +
shell/rb-shell-preferences.c | 10 +++++-
sources/rb-browser-source.c | 1 +
sources/rb-playlist-source.c | 1 +
widgets/rb-entry-view.c | 36 ++++++++++++++++++++
widgets/rb-entry-view.h | 1 +
widgets/rb-query-creator-properties.c | 49 ++++++++++++++++++++++++++-
widgets/rb-song-info.c | 35 ++++++++++++++++++-
18 files changed, 260 insertions(+), 31 deletions(-)
---
diff --git a/backends/gstreamer/rb-encoder-gst.c b/backends/gstreamer/rb-encoder-gst.c
index 87eefa4..bea8915 100644
--- a/backends/gstreamer/rb-encoder-gst.c
+++ b/backends/gstreamer/rb-encoder-gst.c
@@ -492,6 +492,7 @@ add_tags_from_entry (RBEncoderGst *encoder,
GstTagList *tags;
gboolean result = TRUE;
gulong day;
+ gdouble bpm;
tags = gst_tag_list_new ();
@@ -526,6 +527,12 @@ add_tags_from_entry (RBEncoderGst *encoder,
add_string_tag (tags, GST_TAG_MERGE_APPEND, GST_TAG_ARTIST_SORTNAME, entry, RHYTHMDB_PROP_ARTIST_SORTNAME);
add_string_tag (tags, GST_TAG_MERGE_APPEND, GST_TAG_ALBUM_SORTNAME, entry, RHYTHMDB_PROP_ALBUM_SORTNAME);
+ /* is zero a valid BPM? */
+ bpm = rhythmdb_entry_get_double (entry, RHYTHMDB_PROP_BPM);
+ if (bpm > 0.001) {
+ gst_tag_list_add (tags, GST_TAG_MERGE_APPEND, GST_TAG_BEATS_PER_MINUTE, bpm, NULL);
+ }
+
{
GstIterator *iter;
gboolean done;
diff --git a/bindings/python/rb.defs b/bindings/python/rb.defs
index 44d3012..de4c489 100644
--- a/bindings/python/rb.defs
+++ b/bindings/python/rb.defs
@@ -278,6 +278,7 @@
'("first-seen" "RB_ENTRY_VIEW_COL_FIRST_SEEN")
'("last-seen" "RB_ENTRY_VIEW_COL_LAST_SEEN")
'("location" "RB_ENTRY_VIEW_COL_LOCATION")
+ '("bpm" "RB_ENTRY_VIEW_COL_BPM")
'("error" "RB_ENTRY_VIEW_COL_ERROR")
)
)
diff --git a/data/ui/general-prefs.ui b/data/ui/general-prefs.ui
index 8528236..f317d0f 100644
--- a/data/ui/general-prefs.ui
+++ b/data/ui/general-prefs.ui
@@ -406,8 +406,8 @@
</packing>
</child>
<child>
- <object class="GtkCheckButton" id="location_check">
- <property name="label" translatable="yes">Lo_cation</property>
+ <object class="GtkCheckButton" id="bpm_check">
+ <property name="label" translatable="yes">BPM</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
@@ -418,14 +418,28 @@
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">5</property>
- <property name="bottom_attach">6</property>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
- <placeholder/>
+ <object class="GtkCheckButton" id="location_check">
+ <property name="label" translatable="yes">Lo_cation</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="rb_shell_preferences_column_check_changed_cb"/>
+ </object>
+ <packing>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
</child>
<child>
<object class="GtkCheckButton" id="comment_check">
@@ -438,8 +452,10 @@
<signal name="toggled" handler="rb_shell_preferences_column_check_changed_cb"/>
</object>
<packing>
- <property name="top_attach">6</property>
- <property name="bottom_attach">7</property>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
diff --git a/data/ui/song-info.ui b/data/ui/song-info.ui
index e6d7810..9fcc6c4 100644
--- a/data/ui/song-info.ui
+++ b/data/ui/song-info.ui
@@ -248,6 +248,56 @@
</packing>
</child>
<child>
+ <object class="GtkEntry" id="song_info_bpm">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">True</property>
+ <accessibility>
+ <relation type="labelled-by" target="bpm_label"/>
+ </accessibility>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">8</property>
+ <property name="bottom_attach">9</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <object class="GtkLabel" id="bpm_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">BPM:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">song_info_bpm</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </object>
+ <packing>
+ <property name="top_attach">8</property>
+ <property name="bottom_attach">9</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
<object class="GtkLabel" id="comment_label">
<property name="visible">True</property>
<property name="xalign">0</property>
@@ -256,8 +306,8 @@
<property name="mnemonic_widget">song_info_comment</property>
</object>
<packing>
- <property name="top_attach">8</property>
- <property name="bottom_attach">9</property>
+ <property name="top_attach">9</property>
+ <property name="bottom_attach">10</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
@@ -279,8 +329,8 @@
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">8</property>
- <property name="bottom_attach">9</property>
+ <property name="top_attach">9</property>
+ <property name="bottom_attach">10</property>
</packing>
</child>
<child>
diff --git a/metadata/rb-metadata-common.c b/metadata/rb-metadata-common.c
index d0a4655..192502f 100644
--- a/metadata/rb-metadata-common.c
+++ b/metadata/rb-metadata-common.c
@@ -57,6 +57,7 @@
* @RB_METADATA_FIELD_TRACK_PEAK: Track peak volume level
* @RB_METADATA_FIELD_ALBUM_GAIN: Album gain in dB for replaygain
* @RB_METADATA_FIELD_ALBUM_PEAK: Album peak volume level
+ * @RB_METADATA_FIELD_BPM: Beats Per Minute
* @RB_METADATA_FIELD_LANGUAGE_CODE: Language code (ISO-639-1)
* @RB_METADATA_FIELD_MUSICBRAINZ_TRACKID: MusicBrainz track ID
* @RB_METADATA_FIELD_MUSICBRAINZ_ARTISTID: MusicBrainz artist ID
@@ -121,6 +122,7 @@ rb_metadata_get_field_type (RBMetaDataField field)
case RB_METADATA_FIELD_TRACK_PEAK:
case RB_METADATA_FIELD_ALBUM_GAIN:
case RB_METADATA_FIELD_ALBUM_PEAK:
+ case RB_METADATA_FIELD_BPM:
return G_TYPE_DOUBLE;
default:
@@ -199,6 +201,7 @@ rb_metadata_field_get_type (void)
ENUM_ENTRY (RB_METADATA_FIELD_TRACK_PEAK, "replaygain-track-peak"),
ENUM_ENTRY (RB_METADATA_FIELD_ALBUM_GAIN, "replaygain-album-gain"),
ENUM_ENTRY (RB_METADATA_FIELD_ALBUM_PEAK, "replaygain-album-peak"),
+ ENUM_ENTRY (RB_METADATA_FIELD_BPM, "beats-per-minute"),
ENUM_ENTRY (RB_METADATA_FIELD_LANGUAGE_CODE, "language-code"),
ENUM_ENTRY (RB_METADATA_FIELD_MUSICBRAINZ_TRACKID, "musicbrainz-trackid"),
ENUM_ENTRY (RB_METADATA_FIELD_MUSICBRAINZ_ARTISTID, "musicbrainz-artistid"),
diff --git a/metadata/rb-metadata-gst-common.c b/metadata/rb-metadata-gst-common.c
index db20aa8..63386cc 100644
--- a/metadata/rb-metadata-gst-common.c
+++ b/metadata/rb-metadata-gst-common.c
@@ -115,6 +115,8 @@ rb_metadata_gst_tag_to_field (const char *tag)
return RB_METADATA_FIELD_ALBUM_GAIN;
else if (!strcmp (tag, GST_TAG_ALBUM_PEAK))
return RB_METADATA_FIELD_ALBUM_PEAK;
+ else if (!strcmp (tag, GST_TAG_BEATS_PER_MINUTE))
+ return RB_METADATA_FIELD_BPM;
else if (!strcmp (tag, GST_TAG_MUSICBRAINZ_TRACKID))
return RB_METADATA_FIELD_MUSICBRAINZ_TRACKID;
else if (!strcmp (tag, GST_TAG_MUSICBRAINZ_ARTISTID))
@@ -192,6 +194,8 @@ rb_metadata_gst_field_to_gst_tag (RBMetaDataField field)
return GST_TAG_ALBUM_GAIN;
case RB_METADATA_FIELD_ALBUM_PEAK:
return GST_TAG_ALBUM_PEAK;
+ case RB_METADATA_FIELD_BPM:
+ return GST_TAG_BEATS_PER_MINUTE;
case RB_METADATA_FIELD_MUSICBRAINZ_TRACKID:
return GST_TAG_MUSICBRAINZ_TRACKID;
case RB_METADATA_FIELD_MUSICBRAINZ_ARTISTID:
diff --git a/metadata/rb-metadata.h b/metadata/rb-metadata.h
index 57a89ce..3b3c84d 100644
--- a/metadata/rb-metadata.h
+++ b/metadata/rb-metadata.h
@@ -60,6 +60,7 @@ typedef enum
RB_METADATA_FIELD_ALBUM_GAIN, /* double */
RB_METADATA_FIELD_ALBUM_PEAK, /* double */
RB_METADATA_FIELD_LANGUAGE_CODE, /* string */
+ RB_METADATA_FIELD_BPM, /* double */
RB_METADATA_FIELD_MUSICBRAINZ_TRACKID, /* string */
RB_METADATA_FIELD_MUSICBRAINZ_ARTISTID, /* string */
RB_METADATA_FIELD_MUSICBRAINZ_ALBUMID, /* string */
diff --git a/rhythmdb/rhythmdb-private.h b/rhythmdb/rhythmdb-private.h
index 103dee3..e87c0e0 100644
--- a/rhythmdb/rhythmdb-private.h
+++ b/rhythmdb/rhythmdb-private.h
@@ -90,6 +90,7 @@ struct RhythmDBEntry_ {
gulong discnum;
gulong duration;
gulong bitrate;
+ double bpm;
GDate date;
/* filesystem */
diff --git a/rhythmdb/rhythmdb-tree.c b/rhythmdb/rhythmdb-tree.c
index 963e764..becd748 100644
--- a/rhythmdb/rhythmdb-tree.c
+++ b/rhythmdb/rhythmdb-tree.c
@@ -1024,6 +1024,9 @@ save_entry (RhythmDBTree *db,
case RHYTHMDB_PROP_LOCATION:
save_entry_string(ctx, elt_name, rb_refstring_get (entry->location));
break;
+ case RHYTHMDB_PROP_BPM:
+ save_entry_double(ctx, elt_name, entry->bpm);
+ break;
case RHYTHMDB_PROP_MOUNTPOINT:
save_entry_string_if_set (ctx, elt_name, rb_refstring_get (entry->mountpoint));
break;
diff --git a/rhythmdb/rhythmdb.c b/rhythmdb/rhythmdb.c
index cc329dc..833c0cb 100644
--- a/rhythmdb/rhythmdb.c
+++ b/rhythmdb/rhythmdb.c
@@ -588,6 +588,9 @@ metadata_field_from_prop (RhythmDBPropType prop,
case RHYTHMDB_PROP_DATE:
*field = RB_METADATA_FIELD_DATE;
return TRUE;
+ case RHYTHMDB_PROP_BPM:
+ *field = RB_METADATA_FIELD_BPM;
+ return TRUE;
case RHYTHMDB_PROP_MUSICBRAINZ_TRACKID:
*field = RB_METADATA_FIELD_MUSICBRAINZ_TRACKID;
return TRUE;
@@ -930,7 +933,7 @@ stat_thread_main (RhythmDBStatThreadData *data)
g_list_free (data->stat_list);
data->db->priv->stat_thread_running = FALSE;
-
+
rb_debug ("exiting stat thread");
result = g_slice_new0 (RhythmDBEvent);
result->db = data->db; /* need to unref? */
@@ -1548,7 +1551,7 @@ rhythmdb_commit_internal (RhythmDB *db,
GThread *thread)
{
g_mutex_lock (db->priv->change_mutex);
-
+
if (sync_changes) {
g_hash_table_foreach (db->priv->changed_entries, (GHFunc) sync_entry_changed, db);
}
@@ -2068,6 +2071,16 @@ set_props_from_metadata (RhythmDB *db,
RB_METADATA_FIELD_ARTIST,
RHYTHMDB_PROP_ARTIST,
_("Unknown"));
+
+ /* beats per minute */
+ if (rb_metadata_get (metadata,
+ RB_METADATA_FIELD_BPM,
+ &val)) {
+ rhythmdb_entry_set_internal (db, entry, TRUE,
+ RHYTHMDB_PROP_BPM, &val);
+ g_value_unset (&val);
+ }
+
/* album */
set_metadata_string_with_default (db, metadata, entry,
RB_METADATA_FIELD_ALBUM,
@@ -2147,7 +2160,7 @@ rhythmdb_process_stat_event (RhythmDB *db,
RhythmDBEntry *entry;
RhythmDBAction *action;
GFileType file_type;
-
+
if (event->entry != NULL) {
entry = event->entry;
} else {
@@ -2578,7 +2591,7 @@ rhythmdb_process_metadata_load (RhythmDB *db,
gboolean processing;
rb_metadata_get_missing_plugins (event->metadata, &missing_plugins, &plugin_descriptions);
-
+
rb_debug ("missing plugins during metadata load for %s", rb_refstring_get (event->real_uri));
g_mutex_lock (event->db->priv->metadata_lock);
@@ -2720,7 +2733,7 @@ static void
rhythmdb_execute_stat_mount_ready_cb (GObject *source, GAsyncResult *result, RhythmDBEvent *event)
{
GError *error = NULL;
-
+
g_file_mount_enclosing_volume_finish (G_FILE (source), result, &error);
if (error != NULL) {
event->error = make_access_failed_error (rb_refstring_get (event->real_uri), error);
@@ -2752,7 +2765,7 @@ rhythmdb_execute_stat (RhythmDB *db,
event->real_uri = rb_refstring_new (uri);
file = g_file_new_for_uri (uri);
-
+
g_mutex_lock (db->priv->stat_mutex);
db->priv->outstanding_stats = g_list_prepend (db->priv->outstanding_stats, event);
g_mutex_unlock (db->priv->stat_mutex);
@@ -2794,7 +2807,7 @@ rhythmdb_execute_stat (RhythmDB *db,
g_mutex_lock (event->db->priv->stat_mutex);
event->db->priv->outstanding_stats = g_list_remove (event->db->priv->outstanding_stats, event);
g_mutex_unlock (event->db->priv->stat_mutex);
-
+
rhythmdb_push_event (event->db, event);
g_object_unref (file);
}
@@ -2810,7 +2823,7 @@ rhythmdb_execute_load (RhythmDB *db,
resolved = rb_uri_resolve_symlink (uri, &error);
if (resolved != NULL) {
GFile *file;
-
+
file = g_file_new_for_uri (uri);
event->file_info = g_file_query_info (file,
RHYTHMDB_FILE_INFO_ATTRIBUTES,
@@ -3135,7 +3148,7 @@ rhythmdb_add_to_stat_list (RhythmDB *db,
result->entry_type = type;
result->ignore_type = ignore_type;
result->error_type = error_type;
-
+
if (entry != NULL) {
result->entry = rhythmdb_entry_ref (entry);
}
@@ -3346,19 +3359,19 @@ void
rhythmdb_save (RhythmDB *db)
{
int new_save_count;
-
+
rb_debug("saving the rhythmdb and blocking");
g_mutex_lock (db->priv->saving_mutex);
new_save_count = db->priv->save_count + 1;
-
+
rhythmdb_save_async (db);
-
+
/* wait until this save request is being processed */
while (db->priv->save_count < new_save_count) {
g_cond_wait (db->priv->saving_condition, db->priv->saving_mutex);
}
-
+
/* wait until it's done */
while (db->priv->saving) {
g_cond_wait (db->priv->saving_condition, db->priv->saving_mutex);
@@ -3497,7 +3510,7 @@ rhythmdb_entry_set_internal (RhythmDB *db,
g_assert_not_reached ();
break;
}
-
+
if (nop == FALSE && (entry->flags & RHYTHMDB_ENTRY_INSERTED) && notify_if_inserted) {
record_entry_change (db, entry, propid, &old_value, value);
}
@@ -3629,6 +3642,9 @@ rhythmdb_entry_set_internal (RhythmDB *db,
entry->last_played = g_value_get_ulong (value);
entry->flags |= RHYTHMDB_ENTRY_LAST_PLAYED_DIRTY;
break;
+ case RHYTHMDB_PROP_BPM:
+ entry->bpm = g_value_get_double (value);
+ break;
case RHYTHMDB_PROP_MUSICBRAINZ_TRACKID:
rb_refstring_unref (entry->musicbrainz_trackid);
entry->musicbrainz_trackid = rb_refstring_new (g_value_get_string (value));
@@ -3896,7 +3912,7 @@ rhythmdb_entry_move_to_trash (RhythmDB *db,
uri,
error->message);
g_error_free (error);
-
+
} else {
rhythmdb_entry_set_visibility (db, entry, FALSE);
}
@@ -4479,6 +4495,7 @@ rhythmdb_prop_type_get_type (void)
ENUM_ENTRY (RHYTHMDB_PROP_POST_TIME, "Podcast time of post (gulong) [post-time]"),
ENUM_ENTRY (RHYTHMDB_PROP_KEYWORD, "Keywords applied to track (gchararray) [keyword]"),
+ ENUM_ENTRY (RHYTHMDB_PROP_BPM, "Beats per minute (gdouble) [beats-per-minute]"),
{ 0, 0, 0 }
};
g_assert ((sizeof (values) / sizeof (values[0]) - 1) == RHYTHMDB_NUM_PROPERTIES);
@@ -5616,6 +5633,8 @@ rhythmdb_entry_get_double (RhythmDBEntry *entry,
return 1.0;
case RHYTHMDB_PROP_RATING:
return entry->rating;
+ case RHYTHMDB_PROP_BPM:
+ return entry->bpm;
default:
g_assert_not_reached ();
return 0.0;
diff --git a/rhythmdb/rhythmdb.h b/rhythmdb/rhythmdb.h
index 970945e..0dcb309 100644
--- a/rhythmdb/rhythmdb.h
+++ b/rhythmdb/rhythmdb.h
@@ -189,6 +189,7 @@ typedef enum
RHYTHMDB_PROP_PLAYBACK_ERROR,
RHYTHMDB_PROP_FIRST_SEEN_STR,
RHYTHMDB_PROP_LAST_SEEN_STR,
+ RHYTHMDB_PROP_BPM,
/* synthetic properties */
RHYTHMDB_PROP_SEARCH_MATCH,
diff --git a/shell/rb-shell-preferences.c b/shell/rb-shell-preferences.c
index f261c91..b057312 100644
--- a/shell/rb-shell-preferences.c
+++ b/shell/rb-shell-preferences.c
@@ -113,6 +113,7 @@ struct RBShellPreferencesPrivate
GtkWidget *play_count_check;
GtkWidget *last_played_check;
GtkWidget *first_seen_check;
+ GtkWidget *bpm_check;
GtkWidget *quality_check;
GtkWidget *year_check;
GtkWidget *location_check;
@@ -231,6 +232,8 @@ rb_shell_preferences_init (RBShellPreferences *shell_preferences)
GTK_WIDGET (gtk_builder_get_object (builder, "last_played_check"));
shell_preferences->priv->quality_check =
GTK_WIDGET (gtk_builder_get_object (builder, "quality_check"));
+ shell_preferences->priv->bpm_check =
+ GTK_WIDGET (gtk_builder_get_object (builder, "bpm_check"));
shell_preferences->priv->year_check =
GTK_WIDGET (gtk_builder_get_object (builder, "year_check"));
shell_preferences->priv->first_seen_check =
@@ -467,6 +470,8 @@ rb_shell_preferences_column_check_changed_cb (GtkCheckButton *butt,
colname = "RHYTHMDB_PROP_LAST_PLAYED";
else if (butt == GTK_CHECK_BUTTON (shell_preferences->priv->year_check))
colname = "RHYTHMDB_PROP_DATE";
+ else if (butt == GTK_CHECK_BUTTON (shell_preferences->priv->bpm_check))
+ colname = "RHYTHMDB_PROP_BPM";
else if (butt == GTK_CHECK_BUTTON (shell_preferences->priv->quality_check))
colname = "RHYTHMDB_PROP_BITRATE";
else if (butt == GTK_CHECK_BUTTON (shell_preferences->priv->first_seen_check))
@@ -566,9 +571,12 @@ rb_shell_preferences_sync (RBShellPreferences *shell_preferences)
shell_preferences->priv->first_seen_check,
columns, "RHYTHMDB_PROP_FIRST_SEEN");
rb_shell_preferences_sync_column_button (shell_preferences,
- shell_preferences->priv->quality_check,
+ shell_preferences->priv->quality_check,
columns, "RHYTHMDB_PROP_BITRATE");
rb_shell_preferences_sync_column_button (shell_preferences,
+ shell_preferences->priv->bpm_check,
+ columns, "RHYTHMDB_PROP_BPM");
+ rb_shell_preferences_sync_column_button (shell_preferences,
shell_preferences->priv->location_check,
columns, "RHYTHMDB_PROP_LOCATION");
}
diff --git a/sources/rb-browser-source.c b/sources/rb-browser-source.c
index 9d837af..9007612 100644
--- a/sources/rb-browser-source.c
+++ b/sources/rb-browser-source.c
@@ -407,6 +407,7 @@ rb_browser_source_constructed (GObject *object)
rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_DURATION, FALSE);
rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_QUALITY, FALSE);
rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_PLAY_COUNT, FALSE);
+ rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_BPM, FALSE);
rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_COMMENT, FALSE);
rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_LOCATION, FALSE);
diff --git a/sources/rb-playlist-source.c b/sources/rb-playlist-source.c
index c37bc5a..f7cb118 100644
--- a/sources/rb-playlist-source.c
+++ b/sources/rb-playlist-source.c
@@ -373,6 +373,7 @@ rb_playlist_source_constructed (GObject *object)
rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_LOCATION, FALSE);
rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_LAST_PLAYED, FALSE);
rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_FIRST_SEEN, FALSE);
+ rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_BPM, FALSE);
rb_entry_view_set_columns_clickable (source->priv->songs, FALSE);
rb_playlist_source_setup_entry_view (source, source->priv->songs);
diff --git a/widgets/rb-entry-view.c b/widgets/rb-entry-view.c
index dabbe59..69832af 100644
--- a/widgets/rb-entry-view.c
+++ b/widgets/rb-entry-view.c
@@ -963,6 +963,31 @@ rb_entry_view_rating_cell_data_func (GtkTreeViewColumn *column,
}
static void
+rb_entry_view_bpm_cell_data_func (GtkTreeViewColumn *column,
+ GtkCellRenderer *renderer,
+ GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ struct RBEntryViewCellDataFuncData *data)
+{
+ RhythmDBEntry *entry;
+ char *str;
+ gdouble val;
+
+ entry = rhythmdb_query_model_iter_to_entry (data->view->priv->model, iter);
+
+ val = rhythmdb_entry_get_double (entry, data->propid);
+
+ if (val > 0.001)
+ str = g_strdup_printf ("%.2f", val);
+ else
+ str = g_strdup ("");
+
+ g_object_set (renderer, "text", str, NULL);
+ g_free (str);
+ rhythmdb_entry_unref (entry);
+}
+
+static void
rb_entry_view_long_cell_data_func (GtkTreeViewColumn *column,
GtkCellRenderer *renderer,
GtkTreeModel *tree_model,
@@ -1650,6 +1675,16 @@ rb_entry_view_append_column (RBEntryView *view,
key = "Location";
ellipsize = TRUE;
break;
+ case RB_ENTRY_VIEW_COL_BPM:
+ propid = RHYTHMDB_PROP_BPM;
+ cell_data->propid = propid;
+ cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_bpm_cell_data_func;
+ sort_func = (GCompareDataFunc) rhythmdb_query_model_double_ceiling_sort_func;
+ title = _("BPM");
+ key = "BPM";
+ strings[0] = title;
+ strings[1] = "999.99";
+ break;
case RB_ENTRY_VIEW_COL_ERROR:
propid = RHYTHMDB_PROP_PLAYBACK_ERROR;
cell_data->propid = RHYTHMDB_PROP_PLAYBACK_ERROR;
@@ -2692,6 +2727,7 @@ rb_entry_view_column_get_type (void)
ENUM_ENTRY (RB_ENTRY_VIEW_COL_FIRST_SEEN, "First Seen"),
ENUM_ENTRY (RB_ENTRY_VIEW_COL_LAST_SEEN, "Last Seen"),
ENUM_ENTRY (RB_ENTRY_VIEW_COL_LOCATION, "Location"),
+ ENUM_ENTRY (RB_ENTRY_VIEW_COL_BPM, "BPM"),
ENUM_ENTRY (RB_ENTRY_VIEW_COL_ERROR, "Error"),
{ 0, 0, 0 }
};
diff --git a/widgets/rb-entry-view.h b/widgets/rb-entry-view.h
index 03f5fa6..ff2612b 100644
--- a/widgets/rb-entry-view.h
+++ b/widgets/rb-entry-view.h
@@ -59,6 +59,7 @@ typedef enum {
RB_ENTRY_VIEW_COL_FIRST_SEEN,
RB_ENTRY_VIEW_COL_LAST_SEEN,
RB_ENTRY_VIEW_COL_LOCATION,
+ RB_ENTRY_VIEW_COL_BPM,
RB_ENTRY_VIEW_COL_ERROR
} RBEntryViewColumn;
diff --git a/widgets/rb-query-creator-properties.c b/widgets/rb-query-creator-properties.c
index 5175bc1..f6dfbbb 100644
--- a/widgets/rb-query-creator-properties.c
+++ b/widgets/rb-query-creator-properties.c
@@ -39,6 +39,7 @@
const RBQueryCreatorPropertyType string_property_type;
const RBQueryCreatorPropertyType escaped_string_property_type;
const RBQueryCreatorPropertyType rating_property_type;
+const RBQueryCreatorPropertyType double_property_type;
const RBQueryCreatorPropertyType integer_property_type;
const RBQueryCreatorPropertyType year_property_type;
const RBQueryCreatorPropertyType duration_property_type;
@@ -52,6 +53,9 @@ static void escapedStringCriteriaGetWidgetData (GtkWidget *widget, GValue *val);
static GtkWidget * ratingCriteriaCreateWidget (gboolean *constrain);
static void ratingCriteriaSetWidgetData (GtkWidget *widget, GValue *val);
static void ratingCriteriaGetWidgetData (GtkWidget *widget, GValue *val);
+static GtkWidget * doubleCriteriaCreateWidget (gboolean *constrain);
+static void doubleCriteriaSetWidgetData (GtkWidget *widget, GValue *val);
+static void doubleCriteriaGetWidgetData (GtkWidget *widget, GValue *val);
static GtkWidget * integerCriteriaCreateWidget (gboolean *constrain);
static void integerCriteriaSetWidgetData (GtkWidget *widget, GValue *val);
static void integerCriteriaGetWidgetData (GtkWidget *widget, GValue *val);
@@ -86,7 +90,7 @@ const RBQueryCreatorPropertyOption property_options[] =
{ NC_("query-criteria", "Bitrate"), RHYTHMDB_PROP_BITRATE, RHYTHMDB_PROP_BITRATE, &integer_property_type },
{ NC_("query-criteria", "Duration"), RHYTHMDB_PROP_DURATION, RHYTHMDB_PROP_DURATION, &duration_property_type },
-
+ { NC_("query-criteria", "Beats Per Minute"), RHYTHMDB_PROP_BPM, RHYTHMDB_PROP_BPM, &double_property_type },
{ NC_("query-criteria", "Time of Last Play"), RHYTHMDB_PROP_LAST_PLAYED, RHYTHMDB_PROP_LAST_PLAYED, &relative_time_property_type },
{ NC_("query-criteria", "Time Added to Library"), RHYTHMDB_PROP_FIRST_SEEN, RHYTHMDB_PROP_FIRST_SEEN, &relative_time_property_type },
};
@@ -112,6 +116,7 @@ const RBQueryCreatorSortOption sort_options[] =
{ NC_("query-sort", "Last Played"), "LastPlayed", N_("W_ith more recently played tracks first") },
{ NC_("query-sort", "Date Added"), "FirstSeen", N_("W_ith more recently added tracks first") },
{ NC_("query-sort", "Comment"), "Comment", N_("_In reverse alphabetical order") },
+ { NC_("query-sort", "Beats Per Minute"), "BPM", N_("W_ith faster tempo tracks first") },
};
const int num_sort_options = G_N_ELEMENTS (sort_options);
@@ -183,6 +188,15 @@ const RBQueryCreatorPropertyType rating_property_type =
ratingCriteriaGetWidgetData
};
+const RBQueryCreatorPropertyType double_property_type =
+{
+ G_N_ELEMENTS (numeric_criteria_options),
+ numeric_criteria_options,
+ doubleCriteriaCreateWidget,
+ doubleCriteriaSetWidgetData,
+ doubleCriteriaGetWidgetData
+};
+
const RBQueryCreatorPropertyType integer_property_type =
{
G_N_ELEMENTS (numeric_criteria_options),
@@ -336,6 +350,37 @@ ratingCriteriaGetWidgetData (GtkWidget *widget, GValue *val)
}
/*
+ * Implementation for the double properties, using a single GtkSpinButton.
+ */
+
+static GtkWidget *
+doubleCriteriaCreateWidget (gboolean *constrain)
+{
+ GtkWidget *spin;
+ spin = gtk_spin_button_new_with_range (0.0, G_MAXDOUBLE, 1.0);
+ gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spin), 2);
+ return spin;
+}
+
+static void
+doubleCriteriaSetWidgetData (GtkWidget *widget, GValue *val)
+{
+ gdouble num = g_value_get_double (val);
+ g_assert (num <= G_MAXDOUBLE);
+
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), num );
+}
+
+static void
+doubleCriteriaGetWidgetData (GtkWidget *widget, GValue *val)
+{
+ gdouble num = gtk_spin_button_get_value (GTK_SPIN_BUTTON (widget));
+ g_assert (num >= 0);
+
+ g_value_init (val, G_TYPE_DOUBLE);
+ g_value_set_double (val, num);
+}
+/*
* Implementation for the integer properties, using a single GtkSpinButton.
*/
@@ -519,7 +564,7 @@ relativeTimeCriteriaCreateWidget (gboolean *constrain)
timeOption = create_time_unit_option_menu (time_unit_options, G_N_ELEMENTS (time_unit_options));
gtk_combo_box_set_active (GTK_COMBO_BOX (timeOption), time_unit_options_default);
gtk_box_pack_start (box, timeOption, TRUE, TRUE, 0);
-
+
g_signal_connect_object (timeOption, "changed",
G_CALLBACK (update_time_unit_limits),
timeSpin, 0);
diff --git a/widgets/rb-song-info.c b/widgets/rb-song-info.c
index 59f1ef1..c0773d2 100644
--- a/widgets/rb-song-info.c
+++ b/widgets/rb-song-info.c
@@ -135,6 +135,7 @@ struct RBSongInfoPrivate
GtkTextBuffer *comment_buffer;
GtkWidget *playback_error_box;
GtkWidget *playback_error_label;
+ GtkWidget *bpm;
GtkWidget *artist_sortname;
GtkWidget *album_sortname;
@@ -379,6 +380,7 @@ rb_song_info_construct_single (RBSongInfo *song_info, GtkBuilder *builder, gbool
song_info->priv->track_cur = GTK_WIDGET (gtk_builder_get_object (builder, "song_info_track_cur"));
song_info->priv->bitrate = GTK_WIDGET (gtk_builder_get_object (builder, "song_info_bitrate"));
song_info->priv->duration = GTK_WIDGET (gtk_builder_get_object (builder, "song_info_duration"));
+ song_info->priv->bpm = GTK_WIDGET (gtk_builder_get_object (builder, "song_info_bpm"));
song_info->priv->location = GTK_WIDGET (gtk_builder_get_object (builder, "song_info_location"));
song_info->priv->filesize = GTK_WIDGET (gtk_builder_get_object (builder, "song_info_filesize"));
song_info->priv->date_added = GTK_WIDGET (gtk_builder_get_object (builder, "song_info_dateadded"));
@@ -396,6 +398,7 @@ rb_song_info_construct_single (RBSongInfo *song_info, GtkBuilder *builder, gbool
rb_builder_boldify_label (builder, "play_count_label");
rb_builder_boldify_label (builder, "duration_label");
rb_builder_boldify_label (builder, "bitrate_label");
+ rb_builder_boldify_label (builder, "bpm_label");
/* whenever you press a mnemonic, the associated GtkEntry's text gets highlighted */
g_signal_connect_object (G_OBJECT (song_info->priv->title),
@@ -925,6 +928,18 @@ rb_song_info_populate_num_field (GtkEntry *field, gulong num)
}
static void
+rb_song_info_populate_dnum_field (GtkEntry *field, gdouble num)
+{
+ char *tmp;
+ if (num > 0)
+ tmp = g_strdup_printf ("%.2f", num);
+ else
+ tmp = g_strdup (_("Unknown"));
+ gtk_entry_set_text (field, tmp);
+ g_free (tmp);
+}
+
+static void
rb_song_info_populate_dialog_multiple (RBSongInfo *song_info)
{
gboolean mixed_artists = FALSE;
@@ -1055,6 +1070,7 @@ rb_song_info_populate_dialog (RBSongInfo *song_info)
const char *text;
char *tmp;
gulong num;
+ gdouble dnum;
g_assert (song_info->priv->current_entry);
@@ -1081,7 +1097,8 @@ rb_song_info_populate_dialog (RBSongInfo *song_info)
rb_song_info_populate_num_field (GTK_ENTRY (song_info->priv->track_cur), num);
num = rhythmdb_entry_get_ulong (song_info->priv->current_entry, RHYTHMDB_PROP_DISC_NUMBER);
rb_song_info_populate_num_field (GTK_ENTRY (song_info->priv->disc_cur), num);
-
+ dnum = rhythmdb_entry_get_double (song_info->priv->current_entry, RHYTHMDB_PROP_BPM);
+ rb_song_info_populate_dnum_field (GTK_ENTRY (song_info->priv->bpm), dnum);
text = rhythmdb_entry_get_string (song_info->priv->current_entry, RHYTHMDB_PROP_COMMENT);
gtk_text_buffer_set_text (song_info->priv->comment_buffer, text, -1);
@@ -1612,6 +1629,7 @@ rb_song_info_sync_entry_single (RBSongInfo *dialog)
const char *album_sortname;
const char *album_artist_sortname;
const char *entry_string;
+ const char *bpm_str;
char *comment = NULL;
char *endptr;
GType type;
@@ -1619,6 +1637,8 @@ rb_song_info_sync_entry_single (RBSongInfo *dialog)
gulong discnum;
gulong year;
gulong entry_val;
+ gdouble bpm;
+ gdouble dentry_val;
GValue val = {0,};
gboolean changed = FALSE;
RhythmDBEntry *entry = dialog->priv->current_entry;
@@ -1697,7 +1717,18 @@ rb_song_info_sync_entry_single (RBSongInfo *dialog)
if (date)
g_date_free (date);
}
-
+ bpm_str = gtk_entry_get_text (GTK_ENTRY (dialog->priv->bpm));
+ bpm = g_strtod (bpm_str, &endptr);
+ dentry_val = rhythmdb_entry_get_double (entry, RHYTHMDB_PROP_BPM);
+ if ((endptr != bpm_str) && (bpm != dentry_val)) {
+ type = rhythmdb_get_property_type (dialog->priv->db,
+ RHYTHMDB_PROP_BPM);
+ g_value_init (&val, type);
+ g_value_set_double (&val, bpm);
+ rhythmdb_entry_set (dialog->priv->db, entry, RHYTHMDB_PROP_BPM, &val);
+ g_value_unset (&val);
+ changed = TRUE;
+ }
entry_string = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE);
if (g_strcmp0 (title, entry_string)) {
type = rhythmdb_get_property_type (dialog->priv->db,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]