=== added file '.bzrignore'
--- .bzrignore	1970-01-01 00:00:00 +0000
+++ .bzrignore	2007-05-03 09:09:53 +0000
@@ -0,0 +1,56 @@
+Makefile
+Makefile.in
+.libs
+.deps
+*.lo
+*.la
+*.rb-plugin
+./autom4te*.cache
+./stamp-h*
+./libtool
+./ltmain.sh
+./intltool*
+./compile
+./confdefs.h
+./configure
+./config.*
+./aclocal.m4
+./install-sh
+./missing
+./mkinstalldirs
+./py-compile
+./INSTALL
+./depcomp
+./gtk-doc.make
+./gnome-doc-utils.make
+bindings/python/rb.c
+bindings/python/rhythmdb.c
+data/org.gnome.Rhythmbox.service
+data/playlists.xml
+data/rhythmbox.desktop
+data/rhythmbox.desktop.in
+help/rhythmbox-*.omf
+help/*/.xml2po.mo
+help/*/rhythmbox.xml
+lib/*-inline.h
+lib/rb-marshal.[ch]
+macros/gnome-doc-utils.m4
+macros/gtk-doc.m4
+macros/intltool.m4
+metadata/rhythmbox-metadata
+metadata/test-metadata
+plugins/visualizer/rb-visualizer-glue.h
+po/Makefile.in.in
+po/POTFILES
+po/stamp-it
+po/.intltool-merge-cache
+po/*.gmo
+remote/dbus/rhythmbox-client
+shell/*.tab
+shell/rb-playlist-manager-glue.h
+shell/rb-shell-binding.h
+shell/rb-shell-glue.h
+shell/rb-shell-player-binding.h
+shell/rb-shell-player-glue.h
+shell/rhythmbox
+tests/bench-rhythmdb-load

=== added directory 'plugins/fmradio'
=== added file 'plugins/fmradio/Makefile.am'
--- plugins/fmradio/Makefile.am	1970-01-01 00:00:00 +0000
+++ plugins/fmradio/Makefile.am	2007-05-13 14:43:13 +0000
@@ -0,0 +1,42 @@
+
+plugindir = $(PLUGINDIR)/fmradio
+plugin_LTLIBRARIES = libfmradio.la
+
+libfmradio_la_SOURCES = \
+  rb-fm-radio-plugin.h \
+  rb-fm-radio-plugin.c \
+  rb-fm-radio-source.c \
+  rb-fm-radio-source.h \
+  rb-fm-radio-gst-src.c \
+  rb-radio-tuner.h \
+  rb-radio-tuner-v4l2.c
+
+libfmradio_la_LDFLAGS = $(PLUGIN_LIBTOOL_FLAGS)
+libfmradio_la_LIBADD = $(top_builddir)/corelib/librhythmbox-core.la
+
+INCLUDES = \
+  -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \
+  -DG_LOG_DOMAIN=\"Rhythmbox\" \
+  -I$(top_srcdir) \
+  -I$(top_srcdir)/lib \
+  -I$(top_srcdir)/metadata \
+  -I$(top_srcdir)/rhythmdb \
+  -I$(top_srcdir)/widgets \
+  -I$(top_srcdir)/sources \
+  -I$(top_srcdir)/plugins \
+  -I$(top_srcdir)/shell \
+  -I$(top_srcdir)/backends \
+  -DPIXMAP_DIR=\""$(datadir)/pixmaps"\" \
+  -DSHARE_DIR=\"$(pkgdatadir)\" \
+  -DDATADIR=\""$(datadir)"\" \
+  $(RHYTHMBOX_CFLAGS) \
+  $(TOTEM_PLPARSER_CFLAGS) \
+  $(WARN_CFLAGS) \
+  -D_XOPEN_SOURCE -D_BSD_SOURCE
+
+plugin_in_files = fmradio.rb-plugin.desktop.in
+%.rb-plugin: %.rb-plugin.desktop.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*po) ; $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache
+
+BUILT_SOURCES = $(plugin_in_files:.rb-plugin.desktop.in=.rb-plugin)
+
+plugin_DATA = fmradio-ui.xml $(BUILT_SOURCES)

=== added file 'plugins/fmradio/fmradio-ui.xml'
--- plugins/fmradio/fmradio-ui.xml	1970-01-01 00:00:00 +0000
+++ plugins/fmradio/fmradio-ui.xml	2007-05-13 10:55:30 +0000
@@ -0,0 +1,19 @@
+<ui>
+  <menubar name="MenuBar">
+    <menu name="MusicMenu" action="Music">
+      <placeholder name="MusicMenuNewPluginPlaceholder">
+        <menuitem name="MusicNewFMRadioStationMenu" action="MusicNewFMRadioStation"/>
+      </placeholder>
+    </menu>
+  </menubar>
+
+  <popup name="FMRadioSourcePopup">
+    <menuitem name="FMRadioSrcPopupNewStation" action="MusicNewFMRadioStation"/>
+  </popup>
+
+  <popup name="FMRadioViewPopup">
+    <menuitem name="DeleteFMRadioPopup" action="EditDelete"/>
+    <separator/>
+    <menuitem name="PropertiesFMRadioPopup" action="MusicProperties"/>
+  </popup>
+</ui>

=== added file 'plugins/fmradio/fmradio.rb-plugin.desktop.in'
--- plugins/fmradio/fmradio.rb-plugin.desktop.in	1970-01-01 00:00:00 +0000
+++ plugins/fmradio/fmradio.rb-plugin.desktop.in	2007-04-13 15:48:25 +0000
@@ -0,0 +1,8 @@
+[RB Plugin]
+Module=fmradio
+IAge=1
+_Name=FM Radio
+_Description=Support for FM radio broadcasting services
+Authors=James Henstridge
+Copyright=Copyright © 2007 James Henstridge
+Website=http://www.rhythmbox.org/

=== added file 'plugins/fmradio/rb-fm-radio-gst-src.c'
--- plugins/fmradio/rb-fm-radio-gst-src.c	1970-01-01 00:00:00 +0000
+++ plugins/fmradio/rb-fm-radio-gst-src.c	2007-05-13 14:53:52 +0000
@@ -0,0 +1,194 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
+ *
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "rb-debug.h"
+
+#include <gst/gst.h>
+
+#define RB_TYPE_FM_RADIO_SRC (rb_fm_radio_src_get_type())
+#define RB_FM_RADIO_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),RB_TYPE_FM_RADIO_SRC,RBFMRadioSrc))
+#define RB_FM_RADIO_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),RB_TYPE_FM_RADIO_SRC,RBFMRadioSrcClass))
+#define RB_IS_LASTFM_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),RB_TYPE_FM_RADIO_SRC))
+#define RB_IS_LASTFM_SRC_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),RB_TYPE_FM_RADIO_SRC))
+
+typedef struct _RBFMRadioSrc RBFMRadioSrc;
+typedef struct _RBFMRadioSrcClass RBFMRadioSrcClass;
+
+struct _RBFMRadioSrc
+{
+	GstBin parent;
+
+	GstElement *audiotestsrc;
+	GstPad *ghostpad;
+};
+
+struct _RBFMRadioSrcClass
+{
+	GstBinClass parent_class;
+};
+
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+	GST_PAD_SRC,
+	GST_PAD_ALWAYS,
+	GST_STATIC_CAPS_ANY);
+
+static GstElementDetails rb_fm_radio_src_details =
+GST_ELEMENT_DETAILS ("RB Silence Source",
+	"Source/File",
+	"Outputs buffers of silence",
+	"James Henstridge <james@jamesh.id.au>");
+
+static void rb_fm_radio_src_uri_handler_init (gpointer g_iface,
+					      gpointer iface_data);
+
+static void
+_do_init (GType fmradio_src_type)
+{
+	static const GInterfaceInfo urihandler_info = {
+		rb_fm_radio_src_uri_handler_init,
+		NULL,
+		NULL
+	};
+
+	g_type_add_interface_static (fmradio_src_type, GST_TYPE_URI_HANDLER,
+				     &urihandler_info);
+}
+
+GST_BOILERPLATE_FULL (RBFMRadioSrc, rb_fm_radio_src,
+		      GstBin, GST_TYPE_BIN, _do_init);
+
+static void
+rb_fm_radio_src_base_init (gpointer g_class)
+{
+	GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+	gst_element_class_add_pad_template (element_class,
+		gst_static_pad_template_get (&srctemplate));
+	gst_element_class_set_details (element_class, &rb_fm_radio_src_details);
+}
+
+static void
+rb_fm_radio_src_init (RBFMRadioSrc *src, RBFMRadioSrcClass *klass)
+{
+	GstPad *pad;
+
+	rb_debug ("creating rb silence src element");
+
+	src->audiotestsrc = gst_element_factory_make ("audiotestsrc", NULL);
+	gst_bin_add (GST_BIN (src), src->audiotestsrc);
+	gst_object_ref (src->audiotestsrc);
+
+	/* set the audiotestsrc to generate silence (wave type 4) */
+	g_object_set (src->audiotestsrc,
+		      "wave", 4,
+		      NULL);
+
+	pad = gst_element_get_pad (src->audiotestsrc, "src");
+	src->ghostpad = gst_ghost_pad_new ("src", pad);
+	gst_element_add_pad (GST_ELEMENT (src), src->ghostpad);
+	gst_object_ref (src->ghostpad);
+	gst_object_unref (pad);
+	
+}
+
+static void
+rb_fm_radio_src_finalize (GObject *object)
+{
+	RBFMRadioSrc *src = RB_FM_RADIO_SRC (object);
+
+	if (src->ghostpad)
+		gst_object_unref (src->ghostpad);
+	if (src->audiotestsrc)
+		gst_object_unref (src->audiotestsrc);
+
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+rb_fm_radio_src_class_init (RBFMRadioSrcClass *klass)
+{
+	GObjectClass *object_class;
+
+	object_class = G_OBJECT_CLASS (klass);
+	object_class->finalize = rb_fm_radio_src_finalize;
+}
+
+
+/* URI handler interface */
+
+static guint
+rb_fm_radio_src_uri_get_type (void)
+{
+	return GST_URI_SRC;
+}
+
+static gchar **
+rb_fm_radio_src_uri_get_protocols (void)
+{
+	static gchar *protocols[] = {"xrbsilence", NULL};
+	return protocols;
+}
+
+static const gchar *
+rb_fm_radio_src_uri_get_uri (GstURIHandler *handler)
+{
+	return "xrbsilence:///";
+}
+
+static gboolean
+rb_fm_radio_src_uri_set_uri (GstURIHandler *handler,
+			   const gchar *uri)
+{
+	if (g_str_has_prefix (uri, "xrbsilence://") == FALSE)
+		return FALSE;
+
+	return TRUE;
+}
+
+static void
+rb_fm_radio_src_uri_handler_init (gpointer g_iface,
+				gpointer iface_data)
+{
+	GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
+
+	iface->get_type = rb_fm_radio_src_uri_get_type;
+	iface->get_protocols = rb_fm_radio_src_uri_get_protocols;
+	iface->get_uri = rb_fm_radio_src_uri_get_uri;
+	iface->set_uri = rb_fm_radio_src_uri_set_uri;
+}
+
+static gboolean
+plugin_init (GstPlugin *plugin)
+{
+	gboolean ret = gst_element_register (plugin, "rbsilencesrc", GST_RANK_PRIMARY, RB_TYPE_FM_RADIO_SRC);
+	return ret;
+}
+
+GST_PLUGIN_DEFINE_STATIC (GST_VERSION_MAJOR,
+			  GST_VERSION_MINOR,
+			  "rbsilencesrc",
+			  "element to output silence",
+			  plugin_init,
+			  VERSION,
+			  "GPL",
+			  PACKAGE,
+			  "");

=== added file 'plugins/fmradio/rb-fm-radio-plugin.c'
--- plugins/fmradio/rb-fm-radio-plugin.c	1970-01-01 00:00:00 +0000
+++ plugins/fmradio/rb-fm-radio-plugin.c	2007-05-13 14:45:27 +0000
@@ -0,0 +1,119 @@
+/* -*- mode: C; c-basic-offset: 8 -*- */
+
+#include <config.h>
+
+#include <glib/gi18n-lib.h>
+
+#include "rb-debug.h"
+#include "rb-plugin.h"
+
+#include "rb-fm-radio-source.h"
+#include "rb-radio-tuner.h"
+
+#define RB_TYPE_FM_RADIO_PLUGIN         (rb_fm_radio_plugin_get_type ())
+#define RB_FM_RADIO_PLUGIN(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_FM_RADIO_PLUGIN, RBFMRadioPlugin))
+#define RB_FM_RADIO_PLUGIN_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), RB_TYPE_FM_RADIO_PLUGIN, RBFMRadioPluginClass))
+#define RB_IS_FM_RADIO_PLUGIN(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), RB_TYPE_FM_RADIO_PLUGIN))
+#define RB_IS_FM_RADIO_PLUGIN_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), RB_TYPE_FM_RADIO_PLUGIN))
+#define RB_FM_RADIO_PLUGIN_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), RB_TYPE_FM_RADIO_PLUGIN, RBFMRadioPluginClass))
+
+typedef struct _RBFMRadioPlugin RBFMRadioPlugin;
+typedef struct _RBFMRadioPluginClass RBFMRadioPluginClass;
+
+struct _RBFMRadioPlugin {
+	RBPlugin parent;
+	RBSource *source;
+	guint ui_merge_id;
+};
+
+struct _RBFMRadioPluginClass {
+	RBPluginClass parent_class;
+};
+
+G_MODULE_EXPORT GType register_rb_plugin (GTypeModule *module);
+GType rb_fm_radio_plugin_get_type (void) G_GNUC_CONST;
+
+static void impl_activate (RBPlugin *plugin, RBShell *shell);
+static void impl_deactivate (RBPlugin *plugin, RBShell *shell);
+
+RB_PLUGIN_REGISTER (RBFMRadioPlugin, rb_fm_radio_plugin);
+
+static void
+rb_fm_radio_plugin_init (RBFMRadioPlugin *plugin)
+{
+	rb_debug ("RBFMRadioPlugin initialising");
+}
+
+static void
+rb_fm_radio_plugin_finalize (GObject *object)
+{
+	rb_debug ("RBIRadioPlugin finalising");
+
+	G_OBJECT_CLASS (rb_fm_radio_plugin_parent_class)->finalize (object);
+}
+
+static void
+impl_activate (RBPlugin *plugin, RBShell *shell)
+{
+	RBFMRadioPlugin *pi = RB_FM_RADIO_PLUGIN (plugin);
+	RBRadioTuner *tuner;
+	GtkUIManager *uimanager;
+	char *filename;
+
+	tuner = rb_radio_tuner_new (NULL, NULL);
+	if (tuner == NULL)
+		return;
+
+	rb_radio_tuner_set_mute (tuner, TRUE);
+	rb_radio_tuner_update (tuner);
+	pi->source = rb_fm_radio_source_new (shell, tuner);
+	rb_shell_append_source (shell, pi->source, NULL);
+	g_object_unref (tuner);
+
+	filename = rb_plugin_find_file (plugin, "fmradio-ui.xml");
+	if (filename != NULL) {
+		g_object_get (shell, "ui-manager", &uimanager, NULL);
+		pi->ui_merge_id = gtk_ui_manager_add_ui_from_file (uimanager,
+								   filename,
+								   NULL);
+		g_object_unref (uimanager);
+		g_free(filename);
+	} else {
+		g_warning ("Unable to find file: fmradio-ui.xml");
+	}
+	
+}
+
+static void
+impl_deactivate (RBPlugin *plugin, RBShell *shell)
+{
+	RBFMRadioPlugin *pi = RB_FM_RADIO_PLUGIN (plugin);
+	GtkUIManager *uimanager;
+
+	if (pi->source) {
+		rb_source_delete_thyself (pi->source);
+		pi->source = NULL;
+	}
+
+	if (pi->ui_merge_id) {
+		g_object_get (shell, "ui-manager", &uimanager, NULL);
+		gtk_ui_manager_remove_ui (uimanager, pi->ui_merge_id);
+		g_object_unref (uimanager);
+		pi->ui_merge_id = 0;
+	}
+}
+
+static void
+rb_fm_radio_plugin_class_init (RBFMRadioPluginClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+        RBPluginClass *plugin_class = RB_PLUGIN_CLASS (klass);
+
+        object_class->finalize = rb_fm_radio_plugin_finalize;
+
+        plugin_class->activate = impl_activate;
+        plugin_class->deactivate = impl_deactivate;
+
+	RB_PLUGIN_REGISTER_TYPE (rb_radio_tuner);
+	RB_PLUGIN_REGISTER_TYPE (rb_fm_radio_source);
+}

=== added file 'plugins/fmradio/rb-fm-radio-source.c'
--- plugins/fmradio/rb-fm-radio-source.c	1970-01-01 00:00:00 +0000
+++ plugins/fmradio/rb-fm-radio-source.c	2007-05-21 08:49:20 +0000
@@ -0,0 +1,425 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ *  Copyright (C) 2007  James Henstridge <james@jamesh.id.au>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
+ *
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+
+#include "rhythmdb-query-model.h"
+#include "rb-debug.h"
+#include "rb-shell-player.h"
+#include "rb-entry-view.h"
+#include "rb-uri-dialog.h"
+#include "rb-util.h"
+
+#include "rb-fm-radio-source.h"
+#include "rb-radio-tuner.h"
+
+static void     rb_fm_radio_source_class_init  (RBFMRadioSourceClass *class);
+static void     rb_fm_radio_source_init        (RBFMRadioSource *self);
+static GObject *rb_fm_radio_source_constructor (GType type,
+						guint n_construct_properties,
+						GObjectConstructParam *construct_properties);
+static void     rb_fm_radio_source_dispose     (GObject *object);
+static char    *rb_fm_radio_source_get_playback_uri (RhythmDBEntry *entry, gpointer data);
+static void     rb_fm_radio_source_do_query    (RBFMRadioSource *self);
+static void     rb_fm_radio_source_songs_view_sort_order_changed (
+						RBEntryView *view,
+						RBFMRadioSource *self);
+static void      rb_fm_radio_source_songs_view_show_popup (
+						RBEntryView *view,
+						gboolean over_entry,
+						RBFMRadioSource *self);
+
+static void      rb_fm_radio_source_cmd_new_station (GtkAction *action,
+						     RBFMRadioSource *self);
+
+static void playing_entry_changed (RBShellPlayer *player, RhythmDBEntry *entry,
+				   RBFMRadioSource *self);
+
+static void         impl_delete         (RBSource *source);
+static gboolean     impl_show_popup     (RBSource *source);
+static GList       *impl_get_ui_actions (RBSource *source);
+static RBEntryView *impl_get_entry_view (RBSource *source);
+
+
+struct _RBFMRadioSourcePrivate {
+	RhythmDB *db;
+	RBShellPlayer *player;
+	RhythmDBEntryType entry_type;
+	RhythmDBEntry *playing_entry;
+
+	RBEntryView *stations;
+
+	RBRadioTuner *tuner;
+
+	GtkActionGroup *action_group;
+};
+
+static GtkActionEntry rb_fm_radio_source_actions[] = {
+	{ "MusicNewFMRadioStation", GTK_STOCK_NEW, N_("New FM R_adio Station"),
+	  NULL, N_("Create a new FM Radio station"),
+	  G_CALLBACK (rb_fm_radio_source_cmd_new_station) },
+};
+
+RB_PLUGIN_DEFINE_TYPE (RBFMRadioSource, rb_fm_radio_source, RB_TYPE_SOURCE);
+
+static void
+rb_fm_radio_source_class_init (RBFMRadioSourceClass *class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (class);
+	RBSourceClass *source_class = RB_SOURCE_CLASS (class);
+
+	object_class->constructor = rb_fm_radio_source_constructor;
+	object_class->dispose = rb_fm_radio_source_dispose;
+
+	g_type_class_add_private (class, sizeof (RBFMRadioSourcePrivate));
+
+	source_class->impl_can_copy = (RBSourceFeatureFunc) rb_false_function;
+	source_class->impl_can_delete = (RBSourceFeatureFunc) rb_true_function;
+	source_class->impl_can_pause = (RBSourceFeatureFunc) rb_false_function;
+	source_class->impl_delete = impl_delete;
+	source_class->impl_show_popup = impl_show_popup;
+	source_class->impl_get_entry_view = impl_get_entry_view;
+	source_class->impl_get_ui_actions = impl_get_ui_actions;
+}
+
+static void
+rb_fm_radio_source_init (RBFMRadioSource *self)
+{
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (
+		self, RB_TYPE_FM_RADIO_SOURCE, RBFMRadioSourcePrivate);
+}
+
+static GObject *
+rb_fm_radio_source_constructor (GType type, guint n_construct_properties,
+				GObjectConstructParam *construct_properties)
+{
+	RBFMRadioSource *self;
+	RBShell *shell;
+
+	self = RB_FM_RADIO_SOURCE (
+		G_OBJECT_CLASS (rb_fm_radio_source_parent_class)->constructor
+		(type, n_construct_properties, construct_properties));
+
+	g_object_get (self,
+		      "shell", &shell,
+		      "entry-type", &self->priv->entry_type,
+		      NULL);
+	g_object_get (shell,
+		      "db", &self->priv->db,
+		      "shell-player", &self->priv->player,
+		      NULL);
+	g_object_unref (shell);
+
+	self->priv->action_group = _rb_source_register_action_group (
+		RB_SOURCE (self),
+		"FMRadioActions",
+		rb_fm_radio_source_actions,
+		G_N_ELEMENTS (rb_fm_radio_source_actions),
+		self);
+
+	self->priv->stations = rb_entry_view_new (self->priv->db,
+						  G_OBJECT (self->priv->player),
+						  NULL,
+						  FALSE, FALSE);
+	rb_entry_view_append_column (self->priv->stations,
+				     RB_ENTRY_VIEW_COL_TITLE, TRUE);
+	rb_entry_view_append_column (self->priv->stations,
+				     RB_ENTRY_VIEW_COL_RATING, TRUE);
+	rb_entry_view_append_column (self->priv->stations,
+				     RB_ENTRY_VIEW_COL_LAST_PLAYED, TRUE);
+
+	g_signal_connect_object (G_OBJECT (self->priv->stations),
+				 "sort-order-changed",
+				 G_CALLBACK (rb_fm_radio_source_songs_view_sort_order_changed),
+				 self, 0);
+	/* sort order */
+	
+	g_signal_connect_object (self->priv->stations, "show_popup",
+		G_CALLBACK (rb_fm_radio_source_songs_view_show_popup),
+		self, 0);
+
+	gtk_container_add (GTK_CONTAINER (self),
+			   GTK_WIDGET (self->priv->stations));
+	gtk_widget_show_all (GTK_WIDGET (self));
+
+	rb_fm_radio_source_do_query (self);
+
+	g_signal_connect_object (G_OBJECT (self->priv->player),
+				 "playing-song-changed",
+				 G_CALLBACK (playing_entry_changed),
+				 self, 0);
+
+	return G_OBJECT (self);
+}
+
+RBSource *
+rb_fm_radio_source_new (RBShell *shell, RBRadioTuner *tuner)
+{
+	RBFMRadioSource *self;
+	RhythmDBEntryType entry_type;
+	RhythmDB *db;
+
+	g_object_get (shell, "db", &db, NULL);
+
+	entry_type = rhythmdb_entry_type_get_by_name (db, "fmradio-station");
+	if (entry_type == RHYTHMDB_ENTRY_TYPE_INVALID) {
+		entry_type = rhythmdb_entry_register_type (db, "fmradio-station");
+		entry_type->save_to_disk = TRUE;
+		entry_type->can_sync_metadata = (RhythmDBEntryCanSyncFunc) rb_true_function;
+		entry_type->sync_metadata = (RhythmDBEntrySyncFunc) rb_null_function;
+		entry_type->get_playback_uri = rb_fm_radio_source_get_playback_uri;
+	}
+	self = g_object_new (RB_TYPE_FM_RADIO_SOURCE,
+			     "name", _("FM Radio"),
+			     "shell", shell,
+			     "entry-type", entry_type,
+			     NULL);
+	self->priv->tuner = g_object_ref (tuner);
+	rb_shell_register_entry_type_for_source (shell, RB_SOURCE (self),
+						 entry_type);
+	g_object_unref (db);
+	return RB_SOURCE (self);
+}
+
+static char *
+rb_fm_radio_source_get_playback_uri (RhythmDBEntry *entry, gpointer data)
+{
+	return g_strdup("xrbsilence:///");
+}
+
+static void
+rb_fm_radio_source_dispose (GObject *object)
+{
+	RBFMRadioSource *self = (RBFMRadioSource *)object;
+
+	if (self->priv->playing_entry) {
+		rhythmdb_entry_unref (self->priv->playing_entry);
+		self->priv->playing_entry = NULL;
+	}
+
+	if (self->priv->db) {
+		g_object_unref (self->priv->db);
+		self->priv->db = NULL;
+	}
+
+	if (self->priv->tuner) {
+		g_object_unref (self->priv->tuner);
+		self->priv->tuner = NULL;
+	}
+
+	if (self->priv->action_group) {
+		g_object_unref (self->priv->action_group);
+		self->priv->action_group = NULL;
+	}
+
+	G_OBJECT_CLASS (rb_fm_radio_source_parent_class)->dispose (object);
+}
+
+static void
+rb_fm_radio_source_do_query (RBFMRadioSource *self)
+{
+	RhythmDBQueryModel *station_query_model;
+	GPtrArray *query;
+
+        query = rhythmdb_query_parse (self->priv->db,
+                                      RHYTHMDB_QUERY_PROP_EQUALS,
+                                      RHYTHMDB_PROP_TYPE,
+                                      self->priv->entry_type,
+                                      RHYTHMDB_QUERY_END);
+	station_query_model = rhythmdb_query_model_new_empty (self->priv->db);
+	rhythmdb_do_full_query_parsed (
+		self->priv->db, RHYTHMDB_QUERY_RESULTS (station_query_model),
+		query);
+	rhythmdb_query_free (query);
+	rb_entry_view_set_model (self->priv->stations, station_query_model);
+	g_object_set (self, "query-model", station_query_model, NULL);
+	g_object_unref (station_query_model);
+}
+
+static void
+rb_fm_radio_source_songs_view_sort_order_changed (RBEntryView *view,
+						  RBFMRadioSource *self)
+{
+	rb_entry_view_resort_model (view);
+}
+
+static void
+rb_fm_radio_source_songs_view_show_popup (RBEntryView *view,
+					  gboolean over_entry,
+					  RBFMRadioSource *self)
+{
+	if (self == NULL)
+		return;
+
+	if (over_entry)
+		_rb_source_show_popup (RB_SOURCE (self), "/FMRadioViewPopup");
+	else
+		_rb_source_show_popup (RB_SOURCE (self), "/FMRadioSourcePopup");
+}
+
+void
+rb_fm_radio_source_add_station (RBFMRadioSource *self, const char *frequency,
+				const char *title)
+{
+	RhythmDBEntry *entry;
+	gchar *uri, *end = NULL;
+	GValue val = { 0, };
+	
+	/* Check that the location is a double */
+	g_ascii_strtod (frequency, &end);
+	if (end == NULL || end[0] != '\0') {
+		rb_debug ("%s is not a frequency", frequency);
+		return;
+	}
+	uri = g_strconcat ("fmradio:", frequency, NULL);
+
+	entry = rhythmdb_entry_lookup_by_location (self->priv->db, uri);
+	if (entry) {
+		rb_debug ("uri %s already in db", uri);
+		g_free (uri);
+		return;
+	}
+
+	entry = rhythmdb_entry_new (self->priv->db, self->priv->entry_type,
+				    uri);
+	g_free (uri);
+	if (!entry)
+		return;
+
+	g_value_init (&val, G_TYPE_STRING);
+	if (title)
+		g_value_set_static_string (&val, title);
+	else
+		g_value_set_static_string (&val, frequency);
+	rhythmdb_entry_set (self->priv->db, entry, RHYTHMDB_PROP_TITLE, &val);
+	g_value_unset (&val);
+
+	g_value_init (&val, G_TYPE_DOUBLE);
+	g_value_set_double (&val, 0.0);
+	rhythmdb_entry_set (self->priv->db, entry, RHYTHMDB_PROP_RATING, &val);
+	g_value_unset (&val);
+
+	rhythmdb_commit (self->priv->db);
+}
+
+static void
+new_station_location_added (RBURIDialog *dialog, const char *frequency,
+			    RBFMRadioSource *self)
+{
+	rb_fm_radio_source_add_station (self, frequency, NULL);
+}
+
+static void
+rb_fm_radio_source_cmd_new_station (GtkAction *action, RBFMRadioSource *self)
+{
+	GtkWidget *dialog;
+
+	dialog = rb_uri_dialog_new (_("New FM Radio Station"),
+				    _("Frequency of radio station"));
+	g_signal_connect_object (dialog, "location-added",
+				 G_CALLBACK (new_station_location_added),
+				 self, 0);
+	gtk_dialog_run (GTK_DIALOG (dialog));
+	gtk_object_destroy (GTK_OBJECT (dialog));
+}
+
+static void
+playing_entry_changed (RBShellPlayer *player, RhythmDBEntry *entry,
+		       RBFMRadioSource *self)
+{
+	const gchar *location;
+	gdouble frequency;
+	gboolean was_playing = FALSE;
+
+	if (entry == self->priv->playing_entry)
+		return;
+
+	if (self->priv->playing_entry != NULL) {
+		rb_source_update_play_statistics (RB_SOURCE (self),
+						  self->priv->db,
+						  self->priv->playing_entry);
+		rhythmdb_entry_unref (self->priv->playing_entry);
+		self->priv->playing_entry = NULL;
+		was_playing = TRUE;
+	}
+
+	if (entry != NULL &&
+	    rhythmdb_entry_get_entry_type (entry) == self->priv->entry_type) {
+		self->priv->playing_entry = rhythmdb_entry_ref (entry);
+		location = rhythmdb_entry_get_string (entry,
+						      RHYTHMDB_PROP_LOCATION);
+		if (g_str_has_prefix (location, "fmradio:")) {
+			frequency = g_ascii_strtod(
+				&location[strlen("fmradio:")], NULL);
+			if (!was_playing)
+				rb_radio_tuner_set_mute (self->priv->tuner,
+							 FALSE);
+			rb_radio_tuner_set_frequency (self->priv->tuner,
+						      frequency);
+		}
+	} else {
+		if (was_playing)
+			rb_radio_tuner_set_mute (self->priv->tuner, TRUE);
+	}
+	
+}
+
+static void
+impl_delete (RBSource *source)
+{
+	RBFMRadioSource *self = RB_FM_RADIO_SOURCE (source);
+	GList *selection, *tmp;
+
+	selection = rb_entry_view_get_selected_entries (self->priv->stations);
+	for (tmp = selection; tmp != NULL; tmp = tmp->next) {
+		RhythmDBEntry *entry = tmp->data;
+
+		rhythmdb_entry_delete (self->priv->db, entry);
+		rhythmdb_commit (self->priv->db);
+		rhythmdb_entry_unref (entry);
+	}
+	g_list_free (selection);
+}
+
+static gboolean
+impl_show_popup (RBSource *source)
+{
+	_rb_source_show_popup (source, "/FMRadioSourcePopup");
+	return TRUE;
+}
+
+static GList *
+impl_get_ui_actions (RBSource *source)
+{
+	return g_list_prepend (NULL, g_strdup ("MusicNewFMRadioStation"));
+}
+
+static RBEntryView *
+impl_get_entry_view (RBSource *source)
+{
+	RBFMRadioSource *self = (RBFMRadioSource *)source;
+
+	return self->priv->stations;
+}

=== added file 'plugins/fmradio/rb-fm-radio-source.h'
--- plugins/fmradio/rb-fm-radio-source.h	1970-01-01 00:00:00 +0000
+++ plugins/fmradio/rb-fm-radio-source.h	2007-05-21 08:42:08 +0000
@@ -0,0 +1,63 @@
+/*
+ *  Copyright (C) 2007 James Henstridge <james@jamesh.id.au>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
+ *
+ */
+
+#ifndef RB_FM_RADIO_SOURCE_H
+#define RB_FM_RADIO_SOURCE_H
+
+#include "rb-shell.h"
+#include "rb-plugin.h"
+#include "rb-source.h"
+#include "rb-radio-tuner.h"
+
+G_BEGIN_DECLS
+
+#define RB_TYPE_FM_RADIO_SOURCE         (rb_fm_radio_source_get_type ())
+#define RB_FM_RADIO_SOURCE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_FM_RADIO_SOURCE, RBFMRadioSource))
+#define RB_FM_RADIO_SOURCE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), RB_TYPE_FM_RADIO_SOURCE, RBFMRadioSourceClass))
+#define RB_IS_FM_RADIO_SOURCE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), RB_TYPE_FM_RADIO_SOURCE))
+#define RB_IS_FM_RADIO_SOURCE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), RB_TYPE_FM_RADIO_SOURCE))
+#define RB_FM_RADIO_SOURCE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), RB_TYPE_FM_RADIO_SOURCE, RBFMRadioSourceClass))
+
+typedef struct _RBFMRadioSource RBFMRadioSource;
+typedef struct _RBFMRadioSourceClass RBFMRadioSourceClass;
+typedef struct _RBFMRadioSourcePrivate RBFMRadioSourcePrivate;
+
+struct _RBFMRadioSource {
+	RBSource parent;
+
+	RBFMRadioSourcePrivate *priv;
+};
+
+struct _RBFMRadioSourceClass {
+	RBSourceClass parent;
+};
+
+GType     rb_fm_radio_source_get_type	       (void);
+GType     rb_fm_radio_source_register_type     (GTypeModule *plugin);
+
+RBSource *rb_fm_radio_source_new               (RBShell *shell,
+						RBRadioTuner *tuner);
+
+void	  rb_fm_radio_source_add_station       (RBFMRadioSource *source,
+						const char *frequency,
+						const char *title);
+
+G_END_DECLS
+
+#endif /* RB_FM_RADIO_SOURCE_H */

=== added file 'plugins/fmradio/rb-radio-tuner-v4l2.c'
--- plugins/fmradio/rb-radio-tuner-v4l2.c	1970-01-01 00:00:00 +0000
+++ plugins/fmradio/rb-radio-tuner-v4l2.c	2007-05-13 14:55:30 +0000
@@ -0,0 +1,200 @@
+/* -*- mode: C; c-basic-offset: 8 -*- */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#include <linux/videodev2.h>
+
+#include <glib.h>
+
+#include "rb-plugin.h"
+#include "rb-radio-tuner.h"
+
+struct _RBRadioTunerPrivate {
+	int fd;
+	guint32 range_low;
+	guint32 range_high;
+	guint32 current_frequency;
+	guint32 freq_mul;
+};
+
+RB_PLUGIN_DEFINE_TYPE (RBRadioTuner, rb_radio_tuner, G_TYPE_OBJECT);
+
+static void rb_radio_tuner_finalize (GObject *object);
+
+static void
+rb_radio_tuner_class_init (RBRadioTunerClass *class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+	object_class->finalize = rb_radio_tuner_finalize;
+
+	g_type_class_add_private (class, sizeof (RBRadioTunerPrivate));
+}
+
+static void
+rb_radio_tuner_init (RBRadioTuner *self)
+{
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, RB_TYPE_RADIO_TUNER,
+						 RBRadioTunerPrivate);
+	self->priv->fd = -1;
+}
+
+static void
+rb_radio_tuner_finalize (GObject *object)
+{
+	RBRadioTuner *self = (RBRadioTuner *)object;
+
+	g_free (self->card_name);
+	self->card_name = NULL;
+	if (self->priv->fd >= 0)
+		close (self->priv->fd);
+	self->priv->fd = -1;
+
+        G_OBJECT_CLASS (rb_radio_tuner_parent_class)->finalize (object);
+}
+
+RBRadioTuner *
+rb_radio_tuner_new (const gchar *devname, GError **err)
+{
+	int fd = -1;
+	struct v4l2_capability caps;
+	struct v4l2_tuner tuner;
+	RBRadioTuner *self;
+
+	if (devname == NULL)
+		devname = "/dev/radio0";
+
+	fd = open(devname, O_RDONLY);
+	if (fd < 0) {
+		g_warning("Could not open device %s", devname);
+		goto err;
+	}
+	memset (&caps, 0, sizeof (caps));
+	if (ioctl (fd, VIDIOC_QUERYCAP, &caps) < 0) {
+		g_warning("Could not query device capabilities: %s",
+			g_strerror(errno));
+		goto err;
+	}
+	if ((caps.capabilities & V4L2_CAP_TUNER) == 0) {
+		g_warning("Device is not a tuner");
+		goto err;
+	}
+
+	/* check the tuner */
+	memset(&tuner, 0, sizeof(tuner));
+	tuner.index = 0;
+	if (ioctl(fd, VIDIOC_G_TUNER, &tuner) < 0) {
+		g_warning("Could not query tuner info: %s", g_strerror(errno));
+		goto err;
+	}
+	if (tuner.type != V4L2_TUNER_RADIO) {
+		g_warning("Device is not a radio tuner");
+		goto err;
+	}
+
+
+	self = RB_RADIO_TUNER (g_object_new (RB_TYPE_RADIO_TUNER, NULL));
+
+	/* fill in information */
+	self->priv->fd = fd;
+	self->card_name = g_strndup((const char *)caps.card,
+				    sizeof(caps.card));
+	self->priv->range_low = tuner.rangelow;
+	self->priv->range_high = tuner.rangehigh;
+	if ((tuner.capability & V4L2_TUNER_CAP_LOW) != 0)
+		self->priv->freq_mul = 16000;
+	else
+		self->priv->freq_mul = 16;
+
+	self->min_freq = (double)self->priv->range_low / self->priv->freq_mul;
+	self->max_freq = (double)self->priv->range_high / self->priv->freq_mul;
+
+	rb_radio_tuner_update (self);
+	return self;
+
+ err:
+	if (fd >= 0)
+		close (fd);
+	return NULL;
+}
+
+void
+rb_radio_tuner_update (RBRadioTuner *self)
+{
+	struct v4l2_tuner tuner;
+	struct v4l2_control control;
+	struct v4l2_frequency frequency;
+	gboolean has_changed = FALSE;
+
+	memset (&tuner, 0, sizeof (tuner));
+	tuner.index = 0;
+	if (ioctl (self->priv->fd, VIDIOC_G_TUNER, &tuner) >= 0) {
+		if (self->is_stereo != (tuner.audmode==V4L2_TUNER_MODE_STEREO))
+			has_changed = TRUE;
+		self->is_stereo = (tuner.audmode==V4L2_TUNER_MODE_STEREO);
+
+		if (self->signal != tuner.signal)
+			has_changed = TRUE;
+		self->signal = tuner.signal;
+	}
+
+	memset (&control, 0, sizeof (control));
+	control.id = V4L2_CID_AUDIO_MUTE;
+	if (ioctl (self->priv->fd, VIDIOC_G_CTRL, &control) >= 0) {
+		control.value = !!control.value;
+		if (self->is_muted != control.value)
+			has_changed = TRUE;
+		self->is_muted = control.value;
+	}
+
+	memset (&frequency, 0, sizeof (frequency));
+	frequency.tuner = 0;
+	if (ioctl (self->priv->fd, VIDIOC_G_FREQUENCY, &frequency) >= 0) {
+		if (self->priv->current_frequency != frequency.frequency)
+			has_changed = TRUE;
+		self->priv->current_frequency = frequency.frequency;
+		self->frequency = (double)frequency.frequency
+			/ self->priv->freq_mul;
+	}
+
+#if 0
+	if (has_changed) {
+		g_signal_emit (self, CHANGED);
+	}
+#endif
+}
+
+gboolean
+rb_radio_tuner_set_frequency (RBRadioTuner *self, double frequency)
+{
+	struct v4l2_frequency freq;
+	guint new_freq;
+
+	new_freq = frequency * self->priv->freq_mul;
+	new_freq = CLAMP (new_freq,
+			  self->priv->range_low, self->priv->range_high);
+
+	memset (&freq, 0, sizeof (freq));
+	freq.tuner = 0;
+	freq.type = V4L2_TUNER_RADIO;
+	freq.frequency = new_freq;
+	return ioctl (self->priv->fd, VIDIOC_S_FREQUENCY, &freq) >= 0;
+}
+
+gboolean
+rb_radio_tuner_set_mute (RBRadioTuner *self, gboolean mute)
+{
+	struct v4l2_control control;
+
+	memset(&control, 0, sizeof(control));
+	control.id = V4L2_CID_AUDIO_MUTE;
+	control.value = !!mute;
+	return ioctl (self->priv->fd, VIDIOC_S_CTRL, &control) >= 0;
+}

=== added file 'plugins/fmradio/rb-radio-tuner.h'
--- plugins/fmradio/rb-radio-tuner.h	1970-01-01 00:00:00 +0000
+++ plugins/fmradio/rb-radio-tuner.h	2007-04-13 07:28:33 +0000
@@ -0,0 +1,56 @@
+/* -*- mode: C; c-basic-offset: 8 -*- */
+
+#ifndef RB_RADIO_TUNER_H
+#define RB_RADIO_TUNER_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define RB_TYPE_RADIO_TUNER         (rb_radio_tuner_get_type ())
+#define RB_RADIO_TUNER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_RADIO_TUNER, RBRadioTuner))
+#define RB_RADIO_TUNER_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), RB_TYPE_RADIO_TUNER, RBRadioTunerClass))
+#define RB_IS_RADIO_TUNER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), RB_TYPE_RADIO_TUNER))
+#define RB_IS_RADIO_TUNER_CLASS(o)  (G_TYPE_CHECK_CLASS_TYPE ((k), RB_TYPE_RADIO_TUNER))
+#define RB_RADIO_TUNER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), RB_TYPE_RADIO_TUNER, RBRadioTunerClass))
+
+typedef struct _RBRadioTuner RBRadioTuner;
+typedef struct _RBRadioTunerPrivate RBRadioTunerPrivate;
+typedef struct _RBRadioTunerClass RBRadioTunerClass;
+
+struct _RBRadioTuner {
+	GObject parent;
+	RBRadioTunerPrivate *priv;
+
+	gchar *card_name;
+
+	double frequency;
+	double min_freq;
+	double max_freq;
+
+	guint32 signal;
+
+	guint is_stereo : 1;
+	guint is_muted : 1;
+};
+
+struct _RBRadioTunerClass {
+	GObjectClass parent_class;
+
+	void (* changed) (RBRadioTuner *self);
+};
+
+GType         rb_radio_tuner_get_type      (void);
+GType         rb_radio_tuner_register_type (GTypeModule *module);
+
+RBRadioTuner *rb_radio_tuner_new           (const gchar *devname,
+					    GError **err);
+void          rb_radio_tuner_update        (RBRadioTuner *self);
+gboolean      rb_radio_tuner_set_frequency (RBRadioTuner *self,
+					    double frequency);
+gboolean      rb_radio_tuner_set_mute      (RBRadioTuner *self,
+					    gboolean mute);
+
+G_END_DECLS
+
+#endif /* RB_RADIO_TUNER_H */

=== modified file 'ChangeLog'
--- ChangeLog	2007-05-21 09:03:17 +0000
+++ ChangeLog	2007-05-21 09:03:44 +0000
@@ -1,3 +1,13 @@
+2007-05-21  James Henstridge  <james@jamesh.id.au>
+
+	* plugins/fmradio/rb-fm-radio-source.c (playing_entry_changed):
+	only mute/unmute the radio when we need to turn it off or on.
+	Also change the radio station locations to use an "fmradio:"
+	prefix.
+	(rb_fm_radio_source_add_station): prepend "fmradio:" to the radio
+	frequency to form a URI for the entry.  Also check to make sure
+	the frequency can be parsed as a floating point number.
+
 2007-05-20  Jonathan Matthew  <jonathan@kaolin.wh9.net>
 
 	* podcast/rb-podcast-manager.c: (rb_podcast_manager_shutdown):
@@ -41,6 +51,14 @@
 	crashes on updates that occur before the playbin is created
 	(bug #438897).
 
+2007-05-14  James Henstridge  <james@jamesh.id.au>
+
+	* plugins/Makefile.am (SUBDIRS): don't include fmradio in the
+	subdirs unless ENABLE_FM_RADIO is set.
+
+	* configure.ac: only enable the fm radio plugin if we can find the
+	V4L2 header file.
+
 2007-05-12  Jonathan Matthew  <jonathan@kaolin.wh9.net>
 
 	* configure.ac:
@@ -189,6 +207,23 @@
 	(rb_audioscrobbler_gconf_changed_cb):
 	Handle last.fm username or password being unset.  Fixes #426312.
 
+2007-05-03  James Henstridge  <james@jamesh.id.au>
+
+	* plugins/fmradio/rb-fm-radio-source.c: 
+	* plugins/fmradio/rb-fm-radio-source.h: 
+	* plugins/fmradio/rb-fm-radio-plugin.c: only create the source if
+	we can open the radio device.  Tune and mute/unmute the radio on
+	song change.
+
+	* plugins/fmradio/rb-fm-radio-source.c (playing_entry_changed):
+	flesh out the source implementation so that I can catch song changes.
+
+	* plugins/fmradio/rb-fm-radio-source.[ch]: a source for the FM
+	radio plugin.
+
+	* plugins/fmradio/rb-fm-radio-gst-src.c: a source element that
+	makes use of audiotestsrc to produce silence.
+
 2007-05-02  Jonathan Matthew  <jonathan@kaolin.wh9.net>
 
 	* backends/gstreamer/rb-player-gst-xfade.c:
@@ -417,6 +452,14 @@
 	* plugins/ipod/rb-ipod-source.c: (rb_ipod_get_itunesdb_path),
 	(hal_udi_is_ipod): get rid of unused #ifdef'ed iPod code
 
+2007-04-13  James Henstridge  <james@jamesh.id.au>
+
+	* plugins/fmradio/rb-fm-radio-plugin.c: plugin init boilerplate.
+
+	* plugins/fmradio/rb-radio-tuner.h: 
+	* plugins/fmradio/rb-radio-tuner-v4l2.c: start of V4L2 Radio tuner
+	code for an FM radio plugin.
+
 2007-04-10  Jonathan Matthew  <jonathan@kaolin.wh9.net>
 
 	* plugins/audioscrobbler/rb-lastfm-source.c:

=== modified file 'configure.ac'
--- configure.ac	2007-05-14 02:54:52 +0000
+++ configure.ac	2007-05-14 03:18:59 +0000
@@ -713,6 +713,7 @@
 		unset SAVE_CFLAGS
 	done
 	unset option
+	AC_SUBST(WARN_CFLAGS)
 
 	SAVE_CFLAGS="$CFLAGS"
 	CFLAGS="$CFLAGS -Wno-error"
@@ -885,6 +886,19 @@
 fi
 AM_CONDITIONAL(ENABLE_VALA, test "x$with_vala" = "xyes")
 
+dnl ================================================================
+dnl FM Radio Plugin
+dnl ================================================================
+AC_CHECK_HEADER
+AC_MSG_CHECKING([whether FM radio support is requested])
+AC_ARG_ENABLE([fm-radio],
+  AC_HELP_STRING([--disable-fm-radio],[Disable FM radio support]),,
+  enable_fm_radio=yes)
+if test "x$enable_fm_radio" != xno; then
+  AC_CHECK_HEADER([linux/videodev2.h],,[enable_fm_radio=no])
+fi
+AM_CONDITIONAL(ENABLE_FM_RADIO, test "x$enable_fm_radio" != xno)
+
 
 dnl check, for unit tests
 AM_PATH_CHECK([], have_check=yes, have_check=no)
@@ -940,6 +954,7 @@
 plugins/audioscrobbler/Makefile
 plugins/cd-recorder/Makefile
 plugins/daap/Makefile
+plugins/fmradio/Makefile
 plugins/ipod/Makefile
 plugins/iradio/Makefile
 plugins/lirc/Makefile
@@ -1076,5 +1091,10 @@
 else
 	AC_MSG_NOTICE([** using glib 2.13 version of gsequence])
 fi
+if test "x$enable_fm_radio" != xno; then
+	AC_MSG_NOTICE([** FM radio support enabled])
+else
+	AC_MSG_NOTICE([   FM radio support disabled])
+fi
 
 AC_MSG_NOTICE([End options])

=== modified file 'plugins/Makefile.am'
--- plugins/Makefile.am	2007-05-03 09:18:12 +0000
+++ plugins/Makefile.am	2007-05-14 03:20:36 +0000
@@ -46,6 +46,10 @@
 SUBDIRS += daap
 endif
 
+if ENABLE_FM_RADIO
+SUBDIRS += fmradio
+endif
+
 noinst_LTLIBRARIES = librbplugins.la
 
 librbplugins_la_SOURCES =				\

