banshee r4963 - in trunk/banshee: . build libbanshee src/Backends/Banshee.GStreamer src/Backends/Banshee.GStreamer/Banshee.GStreamer src/Core/Banshee.Services src/Core/Banshee.Services/Banshee.MediaEngine src/Core/Banshee.Services/Banshee.ServiceStack src/Core/Banshee.ThickClient src/Core/Banshee.ThickClient/Banshee.Gui.TrackEditor src/Core/Banshee.ThickClient/Banshee.Gui.Widgets src/Extensions src/Extensions/Banshee.Bpm src/Extensions/Banshee.Bpm/Banshee.Bpm
- From: gburt svn gnome org
- To: svn-commits-list gnome org
- Subject: banshee r4963 - in trunk/banshee: . build libbanshee src/Backends/Banshee.GStreamer src/Backends/Banshee.GStreamer/Banshee.GStreamer src/Core/Banshee.Services src/Core/Banshee.Services/Banshee.MediaEngine src/Core/Banshee.Services/Banshee.ServiceStack src/Core/Banshee.ThickClient src/Core/Banshee.ThickClient/Banshee.Gui.TrackEditor src/Core/Banshee.ThickClient/Banshee.Gui.Widgets src/Extensions src/Extensions/Banshee.Bpm src/Extensions/Banshee.Bpm/Banshee.Bpm
- Date: Mon, 26 Jan 2009 03:45:46 +0000 (UTC)
Author: gburt
Date: Mon Jan 26 03:45:46 2009
New Revision: 4963
URL: http://svn.gnome.org/viewvc/banshee?rev=4963&view=rev
Log:
2009-01-25 Gabriel Burt <gabriel burt gmail com>
* libbanshee/Makefile.am:
* libbanshee/banshee-bpmdetector.c:
* build/build.environment.mk:
* configure.ac:
* src/Extensions/Banshee.Bpm/Banshee.Bpm.addin.xml:
* src/Extensions/Banshee.Bpm/Banshee.Bpm.csproj:
* src/Extensions/Banshee.Bpm/Banshee.Bpm/BpmDetectJob.cs:
* src/Extensions/Banshee.Bpm/Banshee.Bpm/BpmEntry.cs:
* src/Extensions/Banshee.Bpm/Banshee.Bpm/BpmService.cs:
* src/Extensions/Banshee.Bpm/Banshee.Bpm/BpmTapAdapter.cs:
* src/Extensions/Banshee.Bpm/Banshee.Bpm/BpmTrackEditorModifier.cs:
* src/Extensions/Banshee.Bpm/Makefile.am:
* src/Extensions/Makefile.am: New extension that adds a preference (disabled
by default) to automatically detect the BPM for all songs, and modified
the track editor dialog's BPM entry, adding a button to auto detect the
BPM, a button to play the song being edited, and a button to tap out the
BPM manually. This feature uses the GStreamer bpmdetect element.
* src/Backends/Banshee.GStreamer/Banshee.GStreamer.addin.xml:
* src/Backends/Banshee.GStreamer/Banshee.GStreamer/BpmDetector.cs:
* src/Backends/Banshee.GStreamer/Makefile.am:
* src/Core/Banshee.Services/Makefile.am:
* src/Core/Banshee.Services/Banshee.Services.addin.xml:
* src/Core/Banshee.Services/Banshee.MediaEngine/IBpmDetector.cs: Support
for various backends to provide bpm detection.
* src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/UserJobTileHost.cs:
* src/Core/Banshee.Services/Banshee.ServiceStack/IUserJob.cs:
* src/Core/Banshee.Services/Banshee.ServiceStack/UserJob.cs: Add
IsBackground property, and if set, don't show such jobs as normal job
tiles.
* src/Core/Banshee.ThickClient/Banshee.Gui.TrackEditor/FieldPage.cs:
* src/Core/Banshee.ThickClient/Banshee.Gui.TrackEditor/ITrackEditorModifier.cs:
* src/Core/Banshee.ThickClient/Banshee.Gui.TrackEditor/TrackEditorDialog.cs:
* src/Core/Banshee.ThickClient/Banshee.ThickClient.addin.xml:
* src/Core/Banshee.ThickClient/Makefile.am: Make the track editor more
extensible and modifiable, including the ability to remove fields.
Added:
trunk/banshee/libbanshee/banshee-bpmdetector.c
trunk/banshee/src/Backends/Banshee.GStreamer/Banshee.GStreamer/BpmDetector.cs
trunk/banshee/src/Core/Banshee.Services/Banshee.MediaEngine/IBpmDetector.cs
- copied, changed from r4962, /trunk/banshee/src/Core/Banshee.Services/Banshee.ServiceStack/IUserJob.cs
trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.TrackEditor/ITrackEditorModifier.cs
- copied, changed from r4962, /trunk/banshee/src/Core/Banshee.Services/Banshee.ServiceStack/IUserJob.cs
trunk/banshee/src/Extensions/Banshee.Bpm/
trunk/banshee/src/Extensions/Banshee.Bpm/Banshee.Bpm/
trunk/banshee/src/Extensions/Banshee.Bpm/Banshee.Bpm.addin.xml
trunk/banshee/src/Extensions/Banshee.Bpm/Banshee.Bpm.csproj
trunk/banshee/src/Extensions/Banshee.Bpm/Banshee.Bpm/BpmDetectJob.cs
trunk/banshee/src/Extensions/Banshee.Bpm/Banshee.Bpm/BpmEntry.cs
trunk/banshee/src/Extensions/Banshee.Bpm/Banshee.Bpm/BpmService.cs
trunk/banshee/src/Extensions/Banshee.Bpm/Banshee.Bpm/BpmTapAdapter.cs
trunk/banshee/src/Extensions/Banshee.Bpm/Banshee.Bpm/BpmTrackEditorModifier.cs
trunk/banshee/src/Extensions/Banshee.Bpm/Makefile.am
Modified:
trunk/banshee/.gitignore
trunk/banshee/ChangeLog
trunk/banshee/build/build.environment.mk
trunk/banshee/configure.ac
trunk/banshee/libbanshee/Makefile.am
trunk/banshee/src/Backends/Banshee.GStreamer/Banshee.GStreamer.addin.xml
trunk/banshee/src/Backends/Banshee.GStreamer/Makefile.am
trunk/banshee/src/Core/Banshee.Services/Banshee.ServiceStack/IUserJob.cs
trunk/banshee/src/Core/Banshee.Services/Banshee.ServiceStack/UserJob.cs
trunk/banshee/src/Core/Banshee.Services/Banshee.Services.addin.xml
trunk/banshee/src/Core/Banshee.Services/Makefile.am
trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.TrackEditor/FieldPage.cs
trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.TrackEditor/TrackEditorDialog.cs
trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/UserJobTileHost.cs
trunk/banshee/src/Core/Banshee.ThickClient/Banshee.ThickClient.addin.xml
trunk/banshee/src/Core/Banshee.ThickClient/Makefile.am
trunk/banshee/src/Extensions/Makefile.am
Modified: trunk/banshee/.gitignore
==============================================================================
--- trunk/banshee/.gitignore (original)
+++ trunk/banshee/.gitignore Mon Jan 26 03:45:46 2009
@@ -30,7 +30,7 @@
aclocal.m4
autom4te.cache/
bin/
-data/banshee-1.desktop
+data/desktop-files/*.desktop
install-sh
libtool
ltmain.sh
Modified: trunk/banshee/build/build.environment.mk
==============================================================================
--- trunk/banshee/build/build.environment.mk (original)
+++ trunk/banshee/build/build.environment.mk Mon Jan 26 03:45:46 2009
@@ -116,6 +116,7 @@
REF_EXTENSION_AUDIOCD = $(LINK_BANSHEE_THICKCLIENT_DEPS) $(LINK_MUSICBRAINZ_DEPS)
REF_EXTENSION_BOOKMARKS = $(LINK_BANSHEE_THICKCLIENT_DEPS)
REF_EXTENSION_BOOSCRIPT = $(LINK_BANSHEE_THICKCLIENT_DEPS) $(LINK_BOO)
+REF_EXTENSION_BPM = $(LINK_BANSHEE_THICKCLIENT_DEPS)
REF_EXTENSION_COVERART = $(LINK_BANSHEE_THICKCLIENT_DEPS)
REF_EXTENSION_DAAP = $(LINK_BANSHEE_THICKCLIENT_DEPS) $(LINK_ICSHARP_ZIP_LIB) $(LINK_MONO_ZEROCONF)
REF_EXTENSION_FILESYSTEMQUEUE = $(LINK_BANSHEE_THICKCLIENT_DEPS)
Modified: trunk/banshee/configure.ac
==============================================================================
--- trunk/banshee/configure.ac (original)
+++ trunk/banshee/configure.ac Mon Jan 26 03:45:46 2009
@@ -231,6 +231,7 @@
src/Extensions/Banshee.AudioCd/Makefile
src/Extensions/Banshee.Bookmarks/Makefile
src/Extensions/Banshee.BooScript/Makefile
+src/Extensions/Banshee.Bpm/Makefile
src/Extensions/Banshee.CoverArt/Makefile
src/Extensions/Banshee.Daap/Makefile
src/Extensions/Banshee.FileSystemQueue/Makefile
Modified: trunk/banshee/libbanshee/Makefile.am
==============================================================================
--- trunk/banshee/libbanshee/Makefile.am (original)
+++ trunk/banshee/libbanshee/Makefile.am Mon Jan 26 03:45:46 2009
@@ -21,6 +21,7 @@
banshee-player-video.c \
banshee-player-vis.c \
banshee-ripper.c \
+ banshee-bpmdetector.c \
banshee-tagger.c \
banshee-transcoder.c
Added: trunk/banshee/libbanshee/banshee-bpmdetector.c
==============================================================================
--- (empty file)
+++ trunk/banshee/libbanshee/banshee-bpmdetector.c Mon Jan 26 03:45:46 2009
@@ -0,0 +1,354 @@
+//
+// banshee-bpmdetector.c
+//
+// Author:
+// Gabriel Burt <gburt novell com>
+//
+// Copyright (C) 2008 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+#include <glib/gi18n.h>
+
+#include "banshee-gst.h"
+#include "banshee-tagger.h"
+
+typedef struct BansheeBpmDetector BansheeBpmDetector;
+
+typedef void (* BansheeBpmDetectorFinishedCallback) ();
+typedef void (* BansheeBpmDetectorProgressCallback) (double bpm);
+typedef void (* BansheeBpmDetectorErrorCallback) (const gchar *error, const gchar *debug);
+
+// Only analyze 20 seconds of audio per song
+#define BPM_DETECT_ANALYSIS_DURATION_MS 20*1000
+
+struct BansheeBpmDetector {
+ gboolean is_detecting;
+
+ // You can run this pipeline on the cmd line with:
+ // gst-launch -m filesrc location=/path/to/my.mp3 ! decodebin ! \
+ // audioconvert ! bpmdetect ! fakesink
+ GstElement *pipeline;
+ GstElement *filesrc;
+ GstElement *decodebin;
+ GstElement *audioconvert;
+ GstElement *bpmdetect;
+ GstElement *fakesink;
+
+ BansheeBpmDetectorProgressCallback progress_cb;
+ BansheeBpmDetectorFinishedCallback finished_cb;
+ BansheeBpmDetectorErrorCallback error_cb;
+};
+
+// ---------------------------------------------------------------------------
+// Private Functions
+// ---------------------------------------------------------------------------
+
+static void
+bbd_raise_error (BansheeBpmDetector *detector, const gchar *error, const gchar *debug)
+{
+ printf ("bpm_detect got error: %s %s\n", error, debug);
+ g_return_if_fail (detector != NULL);
+
+ if (detector->error_cb != NULL) {
+ detector->error_cb (error, debug);
+ }
+}
+
+static void
+bbd_pipeline_process_tag (const GstTagList *tag_list, const gchar *tag_name, BansheeBpmDetector *detector)
+{
+ const GValue *value;
+ gint value_count;
+ double bpm;
+
+ g_return_if_fail (detector != NULL);
+
+ if (detector->progress_cb == NULL) {
+ return;
+ }
+
+ if (strcmp (tag_name, GST_TAG_BEATS_PER_MINUTE)) {
+ return;
+ }
+
+ value_count = gst_tag_list_get_tag_size (tag_list, tag_name);
+ if (value_count < 1) {
+ return;
+ }
+
+ value = gst_tag_list_get_value_index (tag_list, tag_name, 0);
+ if (value != NULL && G_VALUE_HOLDS_DOUBLE (value)) {
+ bpm = g_value_get_double (value);
+ detector->progress_cb (bpm);
+ }
+}
+
+static gboolean
+bbd_pipeline_bus_callback (GstBus *bus, GstMessage *message, gpointer data)
+{
+ BansheeBpmDetector *detector = (BansheeBpmDetector *)data;
+
+ g_return_val_if_fail (detector != NULL, FALSE);
+
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_TAG: {
+ GstTagList *tags;
+ gst_message_parse_tag (message, &tags);
+ if (GST_IS_TAG_LIST (tags)) {
+ gst_tag_list_foreach (tags, (GstTagForeachFunc)bbd_pipeline_process_tag, detector);
+ gst_tag_list_free (tags);
+ }
+ break;
+ }
+
+ case GST_MESSAGE_ERROR: {
+ GError *error;
+ gchar *debug;
+
+ gst_message_parse_error (message, &error, &debug);
+ bbd_raise_error (detector, error->message, debug);
+ g_error_free (error);
+ g_free (debug);
+
+ detector->is_detecting = FALSE;
+ break;
+ }
+
+ case GST_MESSAGE_EOS: {
+ detector->is_detecting = FALSE;
+ gst_element_set_state (GST_ELEMENT (detector->pipeline), GST_STATE_NULL);
+
+ if (detector->finished_cb != NULL) {
+ detector->finished_cb ();
+ }
+ break;
+ }
+
+ default: break;
+ }
+
+ return TRUE;
+}
+
+static void
+bbd_new_decoded_pad(GstElement *decodebin, GstPad *pad,
+ gboolean last, gpointer data)
+{
+ GstCaps *caps;
+ GstStructure *str;
+ GstPad *audiopad;
+ BansheeBpmDetector *detector = (BansheeBpmDetector *)data;
+
+ g_return_if_fail(detector != NULL);
+
+ audiopad = gst_element_get_pad(detector->audioconvert, "sink");
+
+ if(GST_PAD_IS_LINKED(audiopad)) {
+ g_object_unref(audiopad);
+ return;
+ }
+
+ caps = gst_pad_get_caps(pad);
+ str = gst_caps_get_structure(caps, 0);
+
+ if(!g_strrstr(gst_structure_get_name(str), "audio")) {
+ gst_caps_unref(caps);
+ gst_object_unref(audiopad);
+ return;
+ }
+
+ gst_caps_unref(caps);
+ gst_pad_link(pad, audiopad);
+}
+
+static gboolean
+bbd_pipeline_construct (BansheeBpmDetector *detector)
+{
+ g_return_val_if_fail (detector != NULL, FALSE);
+
+ if (detector->pipeline != NULL) {
+ return TRUE;
+ }
+
+ detector->pipeline = gst_pipeline_new ("pipeline");
+ if (detector->pipeline == NULL) {
+ bbd_raise_error (detector, _("Could not create pipeline"), NULL);
+ return FALSE;
+ }
+
+ detector->filesrc = gst_element_factory_make ("filesrc", "filesrc");
+ if (detector->filesrc == NULL) {
+ bbd_raise_error (detector, _("Could not create filesrc element"), NULL);
+ return FALSE;
+ }
+
+ detector->decodebin = gst_element_factory_make ("decodebin", "decodebin");
+ if (detector->decodebin == NULL) {
+ bbd_raise_error (detector, _("Could not create decodebin plugin"), NULL);
+ return FALSE;
+ }
+
+ detector->audioconvert = gst_element_factory_make ("audioconvert", "audioconvert");
+ if (detector->audioconvert == NULL) {
+ bbd_raise_error (detector, _("Could not create audioconvert plugin"), NULL);
+ return FALSE;
+ }
+
+ detector->bpmdetect = gst_element_factory_make ("bpmdetect", "bpmdetect");
+ if (detector->bpmdetect == NULL) {
+ bbd_raise_error (detector, _("Could not create bpmdetect plugin"), NULL);
+ return FALSE;
+ }
+
+ detector->fakesink = gst_element_factory_make ("fakesink", "bpmfakesink");
+ if (detector->fakesink == NULL) {
+ bbd_raise_error (detector, _("Could not create fakesink plugin"), NULL);
+ return FALSE;
+ }
+
+ gst_bin_add_many (GST_BIN (detector->pipeline),
+ detector->filesrc, detector->decodebin, detector->audioconvert,
+ detector->bpmdetect, detector->fakesink, NULL);
+
+ if (!gst_element_link (detector->filesrc, detector->decodebin)) {
+ bbd_raise_error (detector, _("Could not link pipeline elements"), NULL);
+ return FALSE;
+ }
+
+ // decodebin and audioconvert are linked dynamically when the decodebin creates a new pad
+ g_signal_connect(detector->decodebin, "new-decoded-pad",
+ G_CALLBACK(bbd_new_decoded_pad), detector);
+
+ if (!gst_element_link_many (detector->audioconvert, detector->bpmdetect, detector->fakesink, NULL)) {
+ bbd_raise_error (detector, _("Could not link pipeline elements"), NULL);
+ return FALSE;
+ }
+
+ gst_bus_add_watch (gst_pipeline_get_bus (GST_PIPELINE (detector->pipeline)), bbd_pipeline_bus_callback, detector);
+
+ return TRUE;
+}
+
+// ---------------------------------------------------------------------------
+// Internal Functions
+// ---------------------------------------------------------------------------
+
+BansheeBpmDetector *
+bbd_new ()
+{
+ return g_new0 (BansheeBpmDetector, 1);
+}
+
+void
+bbd_cancel (BansheeBpmDetector *detector)
+{
+ g_return_if_fail (detector != NULL);
+
+ if (detector->pipeline != NULL && GST_IS_ELEMENT (detector->pipeline)) {
+ gst_element_set_state (GST_ELEMENT (detector->pipeline), GST_STATE_NULL);
+ gst_object_unref (GST_OBJECT (detector->pipeline));
+ detector->pipeline = NULL;
+ }
+}
+
+void
+bbd_destroy (BansheeBpmDetector *detector)
+{
+ g_return_if_fail (detector != NULL);
+
+ bbd_cancel (detector);
+
+ g_free (detector);
+ detector = NULL;
+}
+
+gboolean
+bbd_process_file (BansheeBpmDetector *detector, const gchar *path)
+{
+ static GstFormat format = GST_FORMAT_TIME;
+ gint64 duration, duration_ms, start_ms, end_ms;
+
+ g_return_val_if_fail (detector != NULL, FALSE);
+
+ if (!bbd_pipeline_construct (detector)) {
+ return FALSE;
+ }
+
+ detector->is_detecting = TRUE;
+ gst_element_set_state (detector->fakesink, GST_STATE_NULL);
+ g_object_set (G_OBJECT (detector->filesrc), "location", path, NULL);
+
+ // TODO listen for transition to STATE_PLAYING, then
+ // Determine how long the file is, and set the detector to base its analysis off the middle 30 seconds of the song
+
+ /*if (gst_element_query_duration (detector->fakesink, &format, &duration)) {
+ duration_ms = duration / GST_MSECOND;
+
+ start_ms = CLAMP((duration_ms / 2) - (BPM_DETECT_ANALYSIS_DURATION_MS/2), 0, duration_ms);
+ end_ms = CLAMP(start_ms + BPM_DETECT_ANALYSIS_DURATION_MS, start_ms, duration_ms);
+ printf("Analyzing song %s starting at %d ending at %d\n", path, start_ms/1000, end_ms/1000);
+
+ if (gst_element_seek (detector->fakesink, 1.0,
+ //GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_SKIP | GST_SEEK_FLAG_KEY_UNIT,
+ GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT,
+ GST_SEEK_TYPE_SET, start_ms * GST_MSECOND,
+ GST_SEEK_TYPE_SET, end_ms * GST_MSECOND))
+ {
+ }
+ }*/
+
+ gst_element_set_state (detector->pipeline, GST_STATE_PLAYING);
+ return TRUE;
+}
+
+void
+bbd_set_progress_callback (BansheeBpmDetector *detector, BansheeBpmDetectorProgressCallback cb)
+{
+ g_return_if_fail (detector != NULL);
+ detector->progress_cb = cb;
+}
+
+void
+bbd_set_finished_callback (BansheeBpmDetector *detector, BansheeBpmDetectorFinishedCallback cb)
+{
+ g_return_if_fail (detector != NULL);
+ detector->finished_cb = cb;
+}
+
+void
+bbd_set_error_callback (BansheeBpmDetector *detector, BansheeBpmDetectorErrorCallback cb)
+{
+ g_return_if_fail (detector != NULL);
+ detector->error_cb = cb;
+}
+
+gboolean
+bbd_get_is_detecting (BansheeBpmDetector *detector)
+{
+ g_return_val_if_fail (detector != NULL, FALSE);
+ return detector->is_detecting;
+}
Modified: trunk/banshee/src/Backends/Banshee.GStreamer/Banshee.GStreamer.addin.xml
==============================================================================
--- trunk/banshee/src/Backends/Banshee.GStreamer/Banshee.GStreamer.addin.xml (original)
+++ trunk/banshee/src/Backends/Banshee.GStreamer/Banshee.GStreamer.addin.xml Mon Jan 26 03:45:46 2009
@@ -28,4 +28,8 @@
<Transcoder class="Banshee.GStreamer.Transcoder"/>
</Extension>
+ <Extension path="/Banshee/MediaEngine/BpmDetector">
+ <BpmDetector class="Banshee.GStreamer.BpmDetector"/>
+ </Extension>
+
</Addin>
Added: trunk/banshee/src/Backends/Banshee.GStreamer/Banshee.GStreamer/BpmDetector.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Backends/Banshee.GStreamer/Banshee.GStreamer/BpmDetector.cs Mon Jan 26 03:45:46 2009
@@ -0,0 +1,192 @@
+//
+// BpmDetector.cs
+//
+// Author:
+// Gabriel Burt <gburt novell com>
+//
+// Copyright (C) 2008 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+using Mono.Unix;
+
+using Hyena;
+using Hyena.Data;
+
+using Banshee.Base;
+using Banshee.Streaming;
+using Banshee.MediaEngine;
+using Banshee.ServiceStack;
+using Banshee.Configuration;
+using Banshee.Preferences;
+
+namespace Banshee.GStreamer
+{
+ public class BpmDetector : IBpmDetector
+ {
+ private HandleRef handle;
+ private SafeUri current_uri;
+ private Dictionary<int, int> bpm_histogram = new Dictionary<int, int> ();
+
+ private BpmDetectorProgressHandler progress_cb;
+ private BpmDetectorFinishedHandler finished_cb;
+ //private BpmDetectorErrorHandler error_cb;
+
+ public event Action<SafeUri, int> FileFinished;
+
+ public BpmDetector ()
+ {
+ try {
+ handle = new HandleRef (this, bbd_new ());
+
+ progress_cb = new BpmDetectorProgressHandler (OnNativeProgress);
+ bbd_set_progress_callback (handle, progress_cb);
+
+ finished_cb = new BpmDetectorFinishedHandler (OnNativeFinished);
+ bbd_set_finished_callback (handle, finished_cb);
+ } catch (Exception e) {
+ throw new ApplicationException (Catalog.GetString ("Could not create BPM detection driver."), e);
+ }
+ }
+
+ public void Dispose ()
+ {
+ Reset ();
+
+ bbd_destroy (handle);
+ handle = new HandleRef (this, IntPtr.Zero);
+ }
+
+ public void Cancel ()
+ {
+ Dispose ();
+ }
+
+ public bool IsDetecting {
+ get { return bbd_get_is_detecting (handle); }
+ }
+
+ private void Reset ()
+ {
+ current_uri = null;
+ bpm_histogram.Clear ();
+ }
+
+ public void ProcessFile (SafeUri uri)
+ {
+ Reset ();
+ current_uri = uri;
+
+ string path = uri.LocalPath;
+ IntPtr path_ptr = GLib.Marshaller.StringToPtrGStrdup (path);
+ try {
+ Log.DebugFormat ("GStreamer running beat detection on {0}", path);
+ bbd_process_file (handle, path_ptr);
+ } catch (Exception e) {
+ Log.Exception (e);
+ } finally {
+ GLib.Marshaller.Free (path_ptr);
+ }
+ }
+
+ private void OnFileFinished (SafeUri uri, int bpm)
+ {
+ Action<SafeUri, int> handler = FileFinished;
+ if (handler != null) {
+ handler (uri, bpm);
+ }
+ }
+
+ private void OnNativeProgress (double bpm)
+ {
+ int rounded = (int) Math.Round (bpm);
+ if (!bpm_histogram.ContainsKey(rounded)) {
+ bpm_histogram[rounded] = 1;
+ } else {
+ bpm_histogram[rounded]++;
+ }
+ }
+
+ private void OnNativeFinished ()
+ {
+ SafeUri uri = current_uri;
+
+ int best_bpm = -1, best_bpm_count = 0;
+ foreach (int bpm in bpm_histogram.Keys) {
+ int count = bpm_histogram[bpm];
+ if (count > best_bpm_count) {
+ best_bpm_count = count;
+ best_bpm = bpm;
+ }
+ }
+
+ Reset ();
+ OnFileFinished (uri, best_bpm);
+ }
+
+ /*private void OnNativeError (IntPtr error, IntPtr debug)
+ {
+ string error_message = GLib.Marshaller.Utf8PtrToString (error);
+
+ if (debug != IntPtr.Zero) {
+ string debug_string = GLib.Marshaller.Utf8PtrToString (debug);
+ if (!String.IsNullOrEmpty (debug_string)) {
+ error_message = String.Format ("{0}: {1}", error_message, debug_string);
+ }
+ }
+
+ Log.Debug (error_message);
+ SafeUri uri = current_uri;
+ Reset ();
+ OnFileFinished (uri, 0);
+ }*/
+
+ private delegate void BpmDetectorProgressHandler (double bpm);
+ private delegate void BpmDetectorFinishedHandler ();
+ //private delegate void BpmDetectorErrorHandler (IntPtr error, IntPtr debug);
+
+ [DllImport ("libbanshee")]
+ private static extern IntPtr bbd_new ();
+
+ [DllImport ("libbanshee")]
+ private static extern void bbd_destroy (HandleRef handle);
+
+ [DllImport ("libbanshee")]
+ private static extern bool bbd_get_is_detecting (HandleRef handle);
+
+ [DllImport ("libbanshee")]
+ private static extern void bbd_process_file (HandleRef handle, IntPtr path);
+
+ [DllImport ("libbanshee")]
+ private static extern void bbd_set_progress_callback (HandleRef handle, BpmDetectorProgressHandler callback);
+
+ [DllImport ("libbanshee")]
+ private static extern void bbd_set_finished_callback (HandleRef handle, BpmDetectorFinishedHandler callback);
+
+ //[DllImport ("libbanshee")]
+ //private static extern void bbd_set_error_callback (HandleRef handle, BpmDetectorErrorHandler callback);
+ }
+}
Modified: trunk/banshee/src/Backends/Banshee.GStreamer/Makefile.am
==============================================================================
--- trunk/banshee/src/Backends/Banshee.GStreamer/Makefile.am (original)
+++ trunk/banshee/src/Backends/Banshee.GStreamer/Makefile.am Mon Jan 26 03:45:46 2009
@@ -3,6 +3,7 @@
LINK = $(REF_BACKEND_GSTREAMER)
SOURCES = \
Banshee.GStreamer/AudioCdRipper.cs \
+ Banshee.GStreamer/BpmDetector.cs \
Banshee.GStreamer/GstErrors.cs \
Banshee.GStreamer/PlayerEngine.cs \
Banshee.GStreamer/Service.cs \
Copied: trunk/banshee/src/Core/Banshee.Services/Banshee.MediaEngine/IBpmDetector.cs (from r4962, /trunk/banshee/src/Core/Banshee.Services/Banshee.ServiceStack/IUserJob.cs)
==============================================================================
--- /trunk/banshee/src/Core/Banshee.Services/Banshee.ServiceStack/IUserJob.cs (original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.MediaEngine/IBpmDetector.cs Mon Jan 26 03:45:46 2009
@@ -1,10 +1,10 @@
-//
-// IUserJob.cs
+//
+// IBpmDetector.cs
//
// Author:
-// Aaron Bockover <abockover novell com>
+// Gabriel Burt <gburt novell com>
//
-// Copyright (C) 2007 Novell, Inc.
+// Copyright (C) 2008 Novell, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -28,27 +28,17 @@
using System;
-using Hyena.Data;
+using Banshee.Base;
+using Banshee.MediaProfiles;
+using Banshee.Collection;
-namespace Banshee.ServiceStack
-{
- public interface IUserJob
+namespace Banshee.MediaEngine
+{
+ public interface IBpmDetector : IDisposable
{
- event EventHandler Finished;
- event EventHandler Updated;
-
+ event Action<SafeUri, int> FileFinished;
+
+ void ProcessFile (SafeUri uri);
void Cancel ();
-
- string Title { get; }
- string Status { get; }
- double Progress { get; }
- string [] IconNames { get; }
-
- string CancelMessage { get; }
- bool CanCancel { get; }
-
- bool IsFinished { get; }
- bool IsCancelRequested { get; }
- bool DelayShow { get; }
}
}
Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.ServiceStack/IUserJob.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.ServiceStack/IUserJob.cs (original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.ServiceStack/IUserJob.cs Mon Jan 26 03:45:46 2009
@@ -43,6 +43,7 @@
string Status { get; }
double Progress { get; }
string [] IconNames { get; }
+ bool IsBackground { get; }
string CancelMessage { get; }
bool CanCancel { get; }
Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.ServiceStack/UserJob.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.ServiceStack/UserJob.cs (original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.ServiceStack/UserJob.cs Mon Jan 26 03:45:46 2009
@@ -44,6 +44,7 @@
private bool is_cancel_requested;
private bool is_finished;
private bool delay_show;
+ private bool is_background;
private int update_freeze_ref;
@@ -164,6 +165,11 @@
}
}
}
+
+ public virtual bool IsBackground {
+ get { return is_background; }
+ set { is_background = value; }
+ }
public virtual string CancelMessage {
get { return cancel_message; }
Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Services.addin.xml
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Services.addin.xml (original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Services.addin.xml Mon Jan 26 03:45:46 2009
@@ -33,6 +33,10 @@
<ExtensionNode name="Transcoder"/>
</ExtensionPoint>
+ <ExtensionPoint path="/Banshee/MediaEngine/BpmDetector">
+ <ExtensionNode name="BpmDetector"/>
+ </ExtensionPoint>
+
<ExtensionPoint path="/Banshee/Platform/HardwareManager">
<ExtensionNode name="HardwareManager"/>
</ExtensionPoint>
Modified: trunk/banshee/src/Core/Banshee.Services/Makefile.am
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Makefile.am (original)
+++ trunk/banshee/src/Core/Banshee.Services/Makefile.am Mon Jan 26 03:45:46 2009
@@ -76,6 +76,7 @@
Banshee.Library/ThreadPoolImportSource.cs \
Banshee.Library/VideoLibrarySource.cs \
Banshee.MediaEngine/IAudioCdRipper.cs \
+ Banshee.MediaEngine/IBpmDetector.cs \
Banshee.MediaEngine/IEqualizer.cs \
Banshee.MediaEngine/IPlayerEngineService.cs \
Banshee.MediaEngine/ITranscoder.cs \
Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.TrackEditor/FieldPage.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.TrackEditor/FieldPage.cs (original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.TrackEditor/FieldPage.cs Mon Jan 26 03:45:46 2009
@@ -53,6 +53,8 @@
public struct FieldSlot
{
+ public Widget Parent;
+ public Widget Container;
public Widget Label;
public Widget Field;
public Button SyncButton;
@@ -61,8 +63,12 @@
public FieldValueClosure WriteClosure;
}
- private List<FieldSlot> field_slots = new List<FieldSlot> ();
private object tooltip_host;
+
+ private List<FieldSlot> field_slots = new List<FieldSlot> ();
+ public IEnumerable<FieldSlot> FieldSlots {
+ get { return field_slots; }
+ }
public FieldPage ()
{
@@ -121,6 +127,7 @@
{
FieldSlot slot = new FieldSlot ();
+ slot.Parent = parent;
slot.Label = label;
slot.Field = field;
slot.LabelClosure = labelClosure;
@@ -138,8 +145,6 @@
};
}
- field_slots.Add (slot);
-
Table table = new Table (1, 1, false);
table.ColumnSpacing = 1;
@@ -171,16 +176,24 @@
table.ShowAll ();
if ((options & FieldOptions.Shrink) == 0) {
+ slot.Container = table;
parent.PackStart (table, false, false, 0);
} else {
HBox shrink = new HBox ();
shrink.Show ();
+ slot.Container = shrink;
shrink.PackStart (table, false, false, 0);
parent.PackStart (shrink, false, false, 0);
}
+ field_slots.Add (slot);
return slot;
}
+
+ public void RemoveField (FieldSlot slot)
+ {
+ field_slots.Remove (slot);
+ }
public virtual void LoadTrack (EditorTrackInfo track)
{
Copied: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.TrackEditor/ITrackEditorModifier.cs (from r4962, /trunk/banshee/src/Core/Banshee.Services/Banshee.ServiceStack/IUserJob.cs)
==============================================================================
--- /trunk/banshee/src/Core/Banshee.Services/Banshee.ServiceStack/IUserJob.cs (original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.TrackEditor/ITrackEditorModifier.cs Mon Jan 26 03:45:46 2009
@@ -1,10 +1,10 @@
-//
-// IUserJob.cs
+//
+// ITrackEditorModifier.cs
//
// Author:
-// Aaron Bockover <abockover novell com>
+// Gabriel Burt <gburt novell com>
//
-// Copyright (C) 2007 Novell, Inc.
+// Copyright (C) 2008 Novell, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -28,27 +28,10 @@
using System;
-using Hyena.Data;
-
-namespace Banshee.ServiceStack
-{
- public interface IUserJob
+namespace Banshee.Gui.TrackEditor
+{
+ public interface ITrackEditorModifier
{
- event EventHandler Finished;
- event EventHandler Updated;
-
- void Cancel ();
-
- string Title { get; }
- string Status { get; }
- double Progress { get; }
- string [] IconNames { get; }
-
- string CancelMessage { get; }
- bool CanCancel { get; }
-
- bool IsFinished { get; }
- bool IsCancelRequested { get; }
- bool DelayShow { get; }
+ void Modify (TrackEditorDialog dialog);
}
}
Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.TrackEditor/TrackEditorDialog.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.TrackEditor/TrackEditorDialog.cs (original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.TrackEditor/TrackEditorDialog.cs Mon Jan 26 03:45:46 2009
@@ -111,6 +111,8 @@
BuildHeader ();
BuildNotebook ();
BuildFooter ();
+
+ LoadModifiers ();
LoadTrackToEditor ();
}
@@ -282,6 +284,18 @@
main_vbox.PackStart (button_box, false, false, 0);
button_box.ShowAll ();
}
+
+ private void LoadModifiers ()
+ {
+ foreach (TypeExtensionNode node in AddinManager.GetExtensionNodes ("/Banshee/Gui/TrackEditor/Modifier")) {
+ try {
+ ITrackEditorModifier mod = (ITrackEditorModifier)node.CreateInstance ();
+ mod.Modify (this);
+ } catch (Exception e) {
+ Hyena.Log.Exception ("Failed to initialize TrackEditor/Modifier extension node. Ensure it implements ITrackEditorModifier.", e);
+ }
+ }
+ }
public void ForeachWidget<T> (WidgetAction<T> action) where T : class
{
Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/UserJobTileHost.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/UserJobTileHost.cs (original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/UserJobTileHost.cs Mon Jan 26 03:45:46 2009
@@ -92,6 +92,10 @@
private void OnJobAdded (object o, UserJobEventArgs args)
{
+ if (args.Job.IsBackground) {
+ return;
+ }
+
ThreadAssist.ProxyToMain (delegate {
if (args.Job.DelayShow) {
// Give the Job 1 second to become more than 33% complete
Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.ThickClient.addin.xml
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.ThickClient.addin.xml (original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.ThickClient.addin.xml Mon Jan 26 03:45:46 2009
@@ -44,5 +44,10 @@
<Description>Defines a new notebook page for the track editor.</Description>
<ExtensionNode name="TrackEditorPage"/>
</ExtensionPoint>
+
+ <ExtensionPoint path="/Banshee/Gui/TrackEditor/Modifier">
+ <Description>Defines an extension for the track editor that can modify it in some way.</Description>
+ <ExtensionNode name="Modifier"/>
+ </ExtensionPoint>
</Addin>
Modified: trunk/banshee/src/Core/Banshee.ThickClient/Makefile.am
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Makefile.am (original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Makefile.am Mon Jan 26 03:45:46 2009
@@ -66,6 +66,7 @@
Banshee.Gui.TrackEditor/ICanUndo.cs \
Banshee.Gui.TrackEditor/IEditorField.cs \
Banshee.Gui.TrackEditor/ITrackEditorPage.cs \
+ Banshee.Gui.TrackEditor/ITrackEditorModifier.cs \
Banshee.Gui.TrackEditor/LyricsPage.cs \
Banshee.Gui.TrackEditor/PageNavigationEntry.cs \
Banshee.Gui.TrackEditor/PageType.cs \
Added: trunk/banshee/src/Extensions/Banshee.Bpm/Banshee.Bpm.addin.xml
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Extensions/Banshee.Bpm/Banshee.Bpm.addin.xml Mon Jan 26 03:45:46 2009
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Addin
+ id="Banshee.Bpm"
+ version="1.0"
+ compatVersion="1.0"
+ copyright="Â 2008 Novell Inc. Licensed under the MIT X11 license."
+ name="BPM Detection"
+ category="User Interface"
+ description="Detect the beats per minute (BPM) of your music"
+ author="Gabriel Burt"
+ url="http://banshee-project.org/"
+ defaultEnabled="true">
+
+ <Dependencies>
+ <Addin id="Banshee.Services" version="1.0"/>
+ <Addin id="Banshee.GStreamer" version="1.0"/>
+ </Dependencies>
+
+ <Extension path="/Banshee/ServiceManager/Service">
+ <Service class="Banshee.Bpm.BpmService"/>
+ </Extension>
+
+ <Extension path="/Banshee/Gui/TrackEditor/Modifier">
+ <Modifier class="Banshee.Bpm.BpmTrackEditorModifier"/>
+ </Extension>
+
+</Addin>
Added: trunk/banshee/src/Extensions/Banshee.Bpm/Banshee.Bpm.csproj
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Extensions/Banshee.Bpm/Banshee.Bpm.csproj Mon Jan 26 03:45:46 2009
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>8.0.50727</ProductVersion>
+ <ProjectGuid>{12984BDF-C565-4452-AD47-79BD3C440E28}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <UseParentDirectoryAsNamespace>true</UseParentDirectoryAsNamespace>
+ <AssemblyName>Banshee.InternetRadio</AssemblyName>
+ <SchemaVersion>2.0</SchemaVersion>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>..\..\..\bin</OutputPath>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
+ <CustomCommands>
+ <CustomCommands>
+ <Command type="Build" command="make" workingdir="${SolutionDir}" />
+ <Command type="Execute" command="make run" workingdir="${SolutionDir}" />
+ </CustomCommands>
+ </CustomCommands>
+ <AssemblyKeyFile>.</AssemblyKeyFile>
+ </PropertyGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\Core\Banshee.Core\Banshee.Core.csproj">
+ <Project>{2ADB831A-A050-47D0-B6B9-9C19D60233BB}</Project>
+ <Name>Banshee.Core</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\..\Core\Banshee.Services\Banshee.Services.csproj">
+ <Project>{B28354F0-BA87-44E8-989F-B864A3C7C09F}</Project>
+ <Name>Banshee.Services</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\..\Core\Banshee.ThickClient\Banshee.ThickClient.csproj">
+ <Project>{AC839523-7BDF-4AB6-8115-E17921B96EC6}</Project>
+ <Name>Banshee.ThickClient</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\..\Libraries\Hyena\Hyena.csproj">
+ <Project>{95374549-9553-4C1E-9D89-667755F90E12}</Project>
+ <Name>Hyena</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\..\Libraries\Mono.Media\Mono.Media.csproj">
+ <Project>{A7566CDC-6033-4A16-9E9D-87D05A627066}</Project>
+ <Name>Mono.Media</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup>
+ <Reference Include="gtk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Banshee.InternetRadio.addin.xml" />
+ <EmbeddedResource Include="Resources\ActiveSourceUI.xml">
+ <LogicalName>ActiveSourceUI.xml</LogicalName>
+ </EmbeddedResource>
+ <EmbeddedResource Include="Resources\GlobalUI.xml">
+ <LogicalName>GlobalUI.xml</LogicalName>
+ </EmbeddedResource>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Banshee.InternetRadio\InternetRadioSource.cs" />
+ <Compile Include="Banshee.InternetRadio\StationEditor.cs" />
+ <Compile Include="Banshee.InternetRadio\XspfMigrator.cs" />
+ <Compile Include="Banshee.InternetRadio\InternetRadioSourceContents.cs" />
+ </ItemGroup>
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+ <ProjectExtensions>
+ <MonoDevelop>
+ <Properties>
+ <GtkDesignInfo />
+ <MonoDevelop.Autotools.MakefileInfo IntegrationEnabled="true" RelativeMakefileName="./Makefile.am">
+ <BuildFilesVar Sync="true" Name="SOURCES" />
+ <DeployFilesVar />
+ <ResourcesVar Sync="true" Name="RESOURCES" />
+ <OthersVar />
+ <GacRefVar />
+ <AsmRefVar />
+ <ProjectRefVar />
+ </MonoDevelop.Autotools.MakefileInfo>
+ </Properties>
+ </MonoDevelop>
+ </ProjectExtensions>
+</Project>
\ No newline at end of file
Added: trunk/banshee/src/Extensions/Banshee.Bpm/Banshee.Bpm/BpmDetectJob.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Extensions/Banshee.Bpm/Banshee.Bpm/BpmDetectJob.cs Mon Jan 26 03:45:46 2009
@@ -0,0 +1,160 @@
+//
+// BpmDetectJob.cs
+//
+// Authors:
+// Gabriel Burt <gburt novell com>
+//
+// Copyright (C) 2008 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
+
+using Mono.Unix;
+using Mono.Addins;
+
+using Hyena;
+using Hyena.Data.Sqlite;
+
+using Banshee.Base;
+using Banshee.Collection;
+using Banshee.Collection.Database;
+using Banshee.Sources;
+using Banshee.Kernel;
+using Banshee.Metadata;
+using Banshee.MediaEngine;
+using Banshee.ServiceStack;
+using Banshee.Library;
+
+namespace Banshee.Bpm
+{
+ public class BpmDetectJob : UserJob, IJob
+ {
+ private PrimarySource music_library;
+ private int current_count;
+ private int current_track_id;
+ private IBpmDetector detector;
+
+ private static HyenaSqliteCommand count_query = new HyenaSqliteCommand (
+ "SELECT COUNT(*) FROM CoreTracks WHERE PrimarySourceID = ? AND (BPM = 0 OR BPM IS NULL)");
+
+ private static HyenaSqliteCommand select_query = new HyenaSqliteCommand (@"
+ SELECT DISTINCT Uri, UriType, TrackID
+ FROM CoreTracks
+ WHERE PrimarySourceID = ? AND (BPM = 0 OR BPM IS NULL) LIMIT 1");
+
+ private static HyenaSqliteCommand update_query = new HyenaSqliteCommand (
+ "UPDATE CoreTracks SET BPM = ?, DateUpdatedStamp = ? WHERE TrackID = ?");
+
+ public BpmDetectJob () : base (Catalog.GetString ("Detecting BPM"))
+ {
+ music_library = ServiceManager.SourceManager.MusicLibrary;
+ IconNames = new string [] {"audio-x-generic"};
+ IsBackground = true;
+ }
+
+ public void Start ()
+ {
+ Register ();
+ Scheduler.Schedule (this, JobPriority.Lowest);
+ }
+
+ private HyenaDataReader RunQuery ()
+ {
+ return new HyenaDataReader (ServiceManager.DbConnection.Query (select_query,
+ music_library.DbId
+ ));
+ }
+
+ public void Run ()
+ {
+ int total = ServiceManager.DbConnection.Query<int> (count_query, music_library.DbId);
+ if (total > 0) {
+ detector = GetDetector ();
+ detector.FileFinished += OnFileFinished;
+
+ DetectNext ();
+ }
+ }
+
+ private void DetectNext ()
+ {
+ if (IsCancelRequested) {
+ Hyena.Log.Debug ("BPM detection cancelled");
+ Finish ();
+ detector.Dispose ();
+ return;
+ }
+
+ int total = current_count + ServiceManager.DbConnection.Query<int> (count_query, music_library.DbId);
+ try {
+ using (HyenaDataReader reader = RunQuery ()) {
+ if (reader.Read ()) {
+ SafeUri uri = music_library.UriAndTypeToSafeUri (
+ reader.Get<TrackUriType> (1), reader.Get<string> (0)
+ );
+ current_track_id = reader.Get<int> (2);
+ detector.ProcessFile (uri);
+ } else {
+ Finish ();
+ detector.Dispose ();
+ }
+ }
+ } catch (Exception e) {
+ Log.Exception (e);
+ } finally {
+ Progress = (double) current_count / (double) total;
+ current_count++;
+ }
+ }
+
+ private void OnFileFinished (SafeUri uri, int bpm)
+ {
+ if (bpm > 0) {
+ Log.DebugFormat ("Saving BPM of {0} for {1}", bpm, uri);
+ ServiceManager.DbConnection.Execute (update_query, bpm, DateTime.Now, current_track_id);
+ } else {
+ Log.DebugFormat ("Unable to detect BPM for {0}", uri);
+ }
+ DetectNext ();
+ }
+
+ internal static IBpmDetector GetDetector ()
+ {
+ IBpmDetector detector = null;
+ foreach (TypeExtensionNode node in AddinManager.GetExtensionNodes ("/Banshee/MediaEngine/BpmDetector")) {
+ try {
+ detector = (IBpmDetector)node.CreateInstance (typeof (IBpmDetector));
+ } catch (Exception e) {
+ Log.Exception (e);
+ }
+
+ if (detector != null) {
+ break;
+ }
+ }
+ return detector;
+ }
+ }
+}
Added: trunk/banshee/src/Extensions/Banshee.Bpm/Banshee.Bpm/BpmEntry.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Extensions/Banshee.Bpm/Banshee.Bpm/BpmEntry.cs Mon Jan 26 03:45:46 2009
@@ -0,0 +1,182 @@
+//
+// BpmEntry.cs
+//
+// Author:
+// Gabriel Burt <gburt novell com>
+//
+// Copyright (C) 2008 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using Mono.Unix;
+using Gtk;
+
+using Mono.Addins;
+
+using Hyena;
+using Hyena.Gui;
+
+using Banshee.Base;
+using Banshee.ServiceStack;
+using Banshee.MediaEngine;
+using Banshee.Collection.Database;
+
+using Banshee.Gui;
+using Banshee.Gui.TrackEditor;
+
+namespace Banshee.Bpm
+{
+ public class BpmEntry : HBox, ICanUndo, IEditorField
+ {
+ private SpinButton bpm_entry;
+ private Button detect_button;
+ private BpmTapAdapter tap_adapter;
+ private EditorTrackInfo track;
+ private IBpmDetector detector;
+ private EditorEditableUndoAdapter<Entry> undo_adapter = new EditorEditableUndoAdapter<Entry> ();
+
+ public event EventHandler Changed;
+
+ public BpmEntry ()
+ {
+ detector = BpmDetectJob.GetDetector ();
+ if (detector != null) {
+ detector.FileFinished += OnFileFinished;
+ }
+
+ BuildWidgets ();
+
+ Destroyed += delegate {
+ if (detector != null) {
+ detector.Dispose ();
+ detector = null;
+ }
+ };
+ }
+
+ private void BuildWidgets ()
+ {
+ Spacing = 6;
+
+ bpm_entry = new SpinButton (0, 9999, 1);
+ bpm_entry.MaxLength = bpm_entry.WidthChars = 4;
+ bpm_entry.Digits = 0;
+ bpm_entry.Numeric = true;
+ bpm_entry.ValueChanged += OnChanged;
+ Add (bpm_entry);
+
+ if (detector != null) {
+ detect_button = new Button (Catalog.GetString ("Detect"));
+ detect_button.Clicked += OnDetectClicked;
+ Add (detect_button);
+ }
+
+ Image play = new Image ();
+ play.IconName = "media-playback-start";;
+ play.IconSize = (int)IconSize.Menu;
+ Button play_button = new Button (play);
+ play_button.Clicked += OnPlayClicked;
+ Add (play_button);
+
+ Button tap_button = new Button (Catalog.GetString ("Tap"));
+ tap_adapter = new BpmTapAdapter (tap_button);
+ tap_adapter.BpmChanged += OnTapBpmChanged;
+ Add (tap_button);
+
+ object tooltip_host = TooltipSetter.CreateHost ();
+
+ TooltipSetter.Set (tooltip_host, detect_button,
+ Catalog.GetString ("Have Banshee attempt to auto-detect the BPM of this song"));
+
+ TooltipSetter.Set (tooltip_host, play_button, Catalog.GetString ("Play this song"));
+
+ TooltipSetter.Set (tooltip_host, tap_button,
+ Catalog.GetString ("Tap this button to the beat to set the BPM for this song manually"));
+
+ ShowAll ();
+ }
+
+ public void DisconnectUndo ()
+ {
+ undo_adapter.DisconnectUndo ();
+ }
+
+ public void ConnectUndo (EditorTrackInfo track)
+ {
+ this.track = track;
+ tap_adapter.Reset ();
+ undo_adapter.ConnectUndo (bpm_entry, track);
+ }
+
+ public int Bpm {
+ get { return bpm_entry.ValueAsInt; }
+ set { bpm_entry.Value = value; }
+ }
+
+ private void OnChanged (object o, EventArgs args)
+ {
+ EventHandler handler = Changed;
+ if (handler != null) {
+ handler (this, EventArgs.Empty);
+ }
+ }
+
+ private void OnTapBpmChanged (int bpm)
+ {
+ Console.WriteLine ("Got Tap Bpm changed: {0}", bpm);
+ Bpm = bpm;
+ OnChanged (null, null);
+ }
+
+ private void OnDetectClicked (object o, EventArgs args)
+ {
+ if (track != null) {
+ detect_button.Sensitive = false;
+ detector.ProcessFile (track.Uri);
+ }
+ }
+
+ private void OnFileFinished (SafeUri uri, int bpm)
+ {
+ ThreadAssist.ProxyToMain (delegate {
+ detect_button.Sensitive = true;
+
+ if (track.Uri != uri || bpm == 0) {
+ return;
+ }
+
+ Log.DebugFormat ("Detected BPM of {0} for {1}", bpm, uri);
+ Bpm = bpm;
+ OnChanged (null, null);
+ });
+ }
+
+ private void OnPlayClicked (object o, EventArgs args)
+ {
+ if (track != null) {
+ ServiceManager.PlayerEngine.Open (track);
+ Gtk.ActionGroup actions = ServiceManager.Get<InterfaceActionService> ().PlaybackActions;
+ (actions["StopWhenFinishedAction"] as Gtk.ToggleAction).Active = true;
+ }
+ }
+ }
+}
Added: trunk/banshee/src/Extensions/Banshee.Bpm/Banshee.Bpm/BpmService.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Extensions/Banshee.Bpm/Banshee.Bpm/BpmService.cs Mon Jan 26 03:45:46 2009
@@ -0,0 +1,188 @@
+//
+// BpmService.cs
+//
+// Authors:
+// Gabriel Burt <gburt novell com>
+//
+// Copyright (C) 2008 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Data;
+
+using Mono.Unix;
+
+using Hyena;
+
+using Banshee.Base;
+using Banshee.Collection;
+using Banshee.Collection.Database;
+using Banshee.ServiceStack;
+using Banshee.Configuration;
+using Banshee.Library;
+using Banshee.Metadata;
+using Banshee.Networking;
+using Banshee.Sources;
+using Banshee.Preferences;
+
+namespace Banshee.Bpm
+{
+ public class BpmService : IExtensionService
+ {
+ private BpmDetectJob job;
+ private bool disposed;
+ private object sync = new object ();
+
+ public BpmService ()
+ {
+ }
+
+ void IExtensionService.Initialize ()
+ {
+ Banshee.MediaEngine.IBpmDetector detector = BpmDetectJob.GetDetector ();
+ if (detector == null) {
+ throw new ApplicationException ("No BPM detector available");
+ } else {
+ detector.Dispose ();
+ }
+
+ if (!ServiceStartup ()) {
+ ServiceManager.SourceManager.SourceAdded += OnSourceAdded;
+ }
+ }
+
+ private void OnSourceAdded (SourceAddedArgs args)
+ {
+ if (ServiceStartup ()) {
+ ServiceManager.SourceManager.SourceAdded -= OnSourceAdded;
+ }
+ }
+
+ private bool ServiceStartup ()
+ {
+ if (ServiceManager.SourceManager.MusicLibrary == null) {
+ return false;
+ }
+
+ ServiceManager.SourceManager.MusicLibrary.TracksAdded += OnTracksAdded;
+ InstallPreferences ();
+
+ Banshee.ServiceStack.Application.RunTimeout (4000, delegate {
+ Detect ();
+ return false;
+ });
+
+ return true;
+ }
+
+ public void Dispose ()
+ {
+ if (disposed) {
+ return;
+ }
+
+ ServiceManager.SourceManager.MusicLibrary.TracksAdded -= OnTracksAdded;
+ UninstallPreferences ();
+
+ disposed = true;
+ }
+
+ public void Detect ()
+ {
+ if (!Enabled) {
+ return;
+ }
+
+ lock (sync) {
+ if (job != null) {
+ return;
+ } else {
+ job = new BpmDetectJob ();
+ }
+ }
+
+ job.Finished += delegate { job = null; };
+ job.Start ();
+ }
+
+ private void OnTracksAdded (Source sender, TrackEventArgs args)
+ {
+ Detect ();
+ }
+
+ string IService.ServiceName {
+ get { return "BpmService"; }
+ }
+
+#region Preferences
+
+ private PreferenceBase enabled_pref;
+
+ private void InstallPreferences ()
+ {
+ PreferenceService service = ServiceManager.Get<PreferenceService> ();
+ if (service == null) {
+ return;
+ }
+
+ enabled_pref = service["general"]["misc"].Add (new SchemaPreference<bool> (EnabledSchema,
+ Catalog.GetString ("_Automatically detect BPM for all songs"),
+ Catalog.GetString ("Detect BPM for all songs that don't already have a value set"),
+ delegate { Enabled = EnabledSchema.Get (); }
+ ));
+ }
+
+ private void UninstallPreferences ()
+ {
+ PreferenceService service = ServiceManager.Get<PreferenceService> ();
+ if (service == null) {
+ return;
+ }
+
+ service["general"]["misc"].Remove (enabled_pref);
+ enabled_pref = null;
+ }
+
+#endregion
+
+ public bool Enabled {
+ get { return EnabledSchema.Get (); }
+ set {
+ EnabledSchema.Set (value);
+ if (value) {
+ Detect ();
+ } else {
+ if (job != null) {
+ job.Cancel ();
+ }
+ }
+ }
+ }
+
+ private static readonly SchemaEntry<bool> EnabledSchema = new SchemaEntry<bool> (
+ "plugins.bpm", "auto_enabled",
+ false,
+ "Automatically detect BPM on imported music",
+ "Automatically detect BPM on imported music"
+ );
+ }
+}
Added: trunk/banshee/src/Extensions/Banshee.Bpm/Banshee.Bpm/BpmTapAdapter.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Extensions/Banshee.Bpm/Banshee.Bpm/BpmTapAdapter.cs Mon Jan 26 03:45:46 2009
@@ -0,0 +1,134 @@
+//
+// BpmTapWidget.cs
+//
+// Author:
+// Gabriel Burt <gburt novell com>
+//
+// Copyright (C) 2008 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections.Generic;
+
+using Mono.Unix;
+
+using Gtk;
+
+using Hyena;
+
+using Banshee.Base;
+
+namespace Banshee.Bpm
+{
+ public class BpmTapAdapter
+ {
+ private const int NUM_PERIODS = 20;
+
+ private int [] tap_periods = new int [NUM_PERIODS];
+ private int tap_index;
+ private int last_tap;
+ private uint timeout_id = 0;
+
+ private Button tap_button;
+
+ private int avg_bpm;
+ public int AverageBpm {
+ get { return avg_bpm; }
+ }
+
+ public event Action<int> BpmChanged;
+
+ public BpmTapAdapter (Button button)
+ {
+ tap_button = button;
+ tap_button.Clicked += OnTapped;
+ }
+
+ public void Reset ()
+ {
+ Reset (false);
+ }
+
+ private void Reset (bool inclLabel)
+ {
+ for (int i = 0; i < NUM_PERIODS; i++) {
+ tap_periods[i] = 0;
+ }
+ tap_index = 0;
+ last_tap = 0;
+
+ if (inclLabel) {
+ avg_bpm = 0;
+ }
+ }
+
+ private void OnTapped (object o, EventArgs args)
+ {
+ int now = Environment.TickCount;
+
+ if (last_tap != 0) {
+ int period = now - last_tap;
+
+ Console.WriteLine ("{0} ms since last tap; eq {1} BPM", period, 60000/period);
+ tap_periods[tap_index] = period;
+ tap_index = (tap_index + 1) % (NUM_PERIODS - 1);
+ CalculateAverage ();
+
+ if (timeout_id != 0) {
+ GLib.Source.Remove (timeout_id);
+ }
+
+ // If the next tap doesn't come soon enough, Reset the tap history
+ timeout_id = GLib.Timeout.Add ((uint)(1.5 * period), OnTapTimeout);
+ }
+
+ last_tap = now;
+ }
+
+ private bool OnTapTimeout ()
+ {
+ Console.WriteLine ("OnTapTimeout");
+ Reset (false);
+ timeout_id = 0;
+ return false;
+ }
+
+ private void CalculateAverage ()
+ {
+ int sum = 0;
+ int count = 0;
+ for (int i = 0; i < NUM_PERIODS; i++) {
+ if (tap_periods[i] > 0) {
+ sum += tap_periods[i];
+ count++;
+ }
+ }
+
+ avg_bpm = 60000 * count / sum;
+
+ Action<int> handler = BpmChanged;
+ if (handler != null) {
+ handler (avg_bpm);
+ }
+ }
+ }
+}
Added: trunk/banshee/src/Extensions/Banshee.Bpm/Banshee.Bpm/BpmTrackEditorModifier.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Extensions/Banshee.Bpm/Banshee.Bpm/BpmTrackEditorModifier.cs Mon Jan 26 03:45:46 2009
@@ -0,0 +1,78 @@
+//
+// BpmTrackEditorModifier.cs
+//
+// Author:
+// Gabriel Burt <gburt novell com>
+//
+// Copyright (C) 2008 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections.Generic;
+using Mono.Unix;
+using Gtk;
+
+using Hyena;
+using Banshee.Base;
+using Banshee.Gui.TrackEditor;
+
+namespace Banshee.Bpm
+{
+ public class BpmTrackEditorModifier : ITrackEditorModifier
+ {
+ public BpmTrackEditorModifier ()
+ {
+ }
+
+ public void Modify (TrackEditorDialog dialog)
+ {
+ for (int i = 0; i < dialog.Notebook.NPages; i++) {
+ Widget page = dialog.Notebook.GetNthPage (i);
+ ExtraTrackDetailsPage extra_page = page as ExtraTrackDetailsPage;
+ if (extra_page != null) {
+ foreach (FieldPage.FieldSlot slot in extra_page.FieldSlots) {
+ // TODO this ia hacky - what if another Modifier added a 2nd SpinButton?
+ if (slot.Field is Gtk.SpinButton) {
+ // Remove it
+ (slot.Parent as Container).Remove (slot.Container);
+ extra_page.RemoveField (slot);
+
+ // Add our new and improved version back
+ extra_page.AddField (slot.Parent as Gtk.Box,
+ new BpmEntry (), null,
+ slot.LabelClosure,
+ delegate (EditorTrackInfo track, Widget widget) {
+ ((BpmEntry)widget).Bpm = track.Bpm; },
+ delegate (EditorTrackInfo track, Widget widget) {
+ track.Bpm = ((BpmEntry)widget).Bpm; },
+ FieldOptions.Shrink | FieldOptions.NoSync
+ );
+ break;
+ }
+ }
+
+ break;
+ }
+ }
+ }
+ }
+}
Added: trunk/banshee/src/Extensions/Banshee.Bpm/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Extensions/Banshee.Bpm/Makefile.am Mon Jan 26 03:45:46 2009
@@ -0,0 +1,17 @@
+ASSEMBLY = Banshee.Bpm
+TARGET = library
+LINK = $(REF_EXTENSION_BPM)
+INSTALL_DIR = $(EXTENSIONS_INSTALL_DIR)
+
+SOURCES = \
+ Banshee.Bpm/BpmDetectJob.cs \
+ Banshee.Bpm/BpmEntry.cs \
+ Banshee.Bpm/BpmService.cs \
+ Banshee.Bpm/BpmTapAdapter.cs \
+ Banshee.Bpm/BpmTrackEditorModifier.cs
+
+RESOURCES = \
+ Banshee.Bpm.addin.xml
+
+include $(top_srcdir)/build/build.mk
+
Modified: trunk/banshee/src/Extensions/Makefile.am
==============================================================================
--- trunk/banshee/src/Extensions/Makefile.am (original)
+++ trunk/banshee/src/Extensions/Makefile.am Mon Jan 26 03:45:46 2009
@@ -2,6 +2,7 @@
Banshee.AudioCd \
Banshee.Bookmarks \
Banshee.BooScript \
+ Banshee.Bpm \
Banshee.CoverArt \
Banshee.Daap \
Banshee.FileSystemQueue \
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]