changeset 2694:c2b82432c1b6 trunk

[svn] Added basic DBus support, disabled by default with a configuration option to enable it. The general and playback information/manipulation methods are currently the only ones implemented. That is version, play, pause, stop, playing, paused, stopped, status, and seek. There are stubs for many unimplemented methods.
author magma
date Sat, 05 May 2007 15:37:54 -0700
parents b47f5577bea0
children 4c4c8b294287
files ChangeLog configure.ac mk/rules.mk.in src/audacious/Makefile src/audacious/build_stamp.c src/audacious/dbus.c src/audacious/dbus.h src/audacious/main.c src/audacious/objects.xml
diffstat 9 files changed, 606 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Wed May 02 10:34:59 2007 -0700
+++ b/ChangeLog	Sat May 05 15:37:54 2007 -0700
@@ -1,3 +1,12 @@
+2007-05-02 17:34:59 +0000  Michael Farber <01mf02@gmail.com>
+  revision [4412]
+  - Made the Space key pause/unpause playback (like e.g. mplayer)
+  
+  
+  trunk/src/audacious/ui_main.c |    5 ++++-
+  1 file changed, 4 insertions(+), 1 deletion(-)
+
+
 2007-05-01 04:07:19 +0000  Yoshiki Yazawa <yaz@cc.rim.or.jp>
   revision [4410]
   - make --enable-chardet default.
--- a/configure.ac	Wed May 02 10:34:59 2007 -0700
+++ b/configure.ac	Sat May 05 15:37:54 2007 -0700
@@ -212,6 +212,41 @@
 AC_SUBST(CHARDET_LIBS)
 AC_SUBST(SUBDIR_GUESS)
 
+dnl D-Bus support
+dnl ========================
+AC_ARG_ENABLE(dbus,
+    [  --enable-dbus    enable D-Bus support (default=no)],
+    [enable_dbus=$enableval],
+    [enable_dbus="no"]
+)
+
+AM_CONDITIONAL(USE_DBUS, test "x$enable_dbus" = "xyes")
+if test "x$enable_dbus" = "xyes"; then
+	PKG_CHECK_MODULES(DBUS, [dbus-1 >= 0.60 dbus-glib-1 >= 0.60 gthread-2.0],
+										enable_dbus="yes", [
+		AC_MSG_RESULT(no)
+		enable_dbus="no"
+	])
+	AC_PATH_PROG(DBUS_BINDING_TOOL, dbus-binding-tool, no)
+	AC_PATH_PROG(GLIB_GENMARSHAL, glib-genmarshal, no)
+	if test "x$DBUS_BINDING_TOOL" = "xno" || test "x$GLIB_GENMARSHAL" = "xno" || test "x$enable_dbus" = "xno" ; then
+		enable_dbus="no"
+	else
+		AC_DEFINE(USE_DBUS, 1, [Define if D-Bus support enabled])
+		AC_DEFINE(DBUS_SERVICES_DIR, "$datadir/dbus-1/services",
+			[Location of D-Bus services directory])
+		DBUS_C=dbus.c
+		DBUS_BINDINGS_H=dbus-bindings.h
+		AC_SUBST(DBUS_C)
+		AC_SUBST(DBUS_BINDINGS_H)
+		AC_SUBST(USE_DBUS)
+		AC_SUBST(DBUS_CFLAGS)
+		AC_SUBST(DBUS_LIBS)
+		AC_SUBST(DBUS_SERVICES_DIR)
+		AC_SUBST(DBUS_BINDING_TOOL)
+	fi
+fi
+
 dnl libsamplerate support
 dnl ========================
 AC_ARG_ENABLE(samplerate,
@@ -438,6 +473,7 @@
 echo 
 echo "  Automatic character code detection:     $enable_chardet"
 echo "  Sample rate upconversion:               $enable_samplerate"
+echo "  D-Bus support:                          $enable_dbus"
 echo
 
 if test "$beep_cv_lib_xlibs_threadsafe" = "no"; then
--- a/mk/rules.mk.in	Wed May 02 10:34:59 2007 -0700
+++ b/mk/rules.mk.in	Sat May 05 15:37:54 2007 -0700
@@ -97,6 +97,11 @@
 CYGPATH_W ?= @CYGPATH_W@
 DATADIRNAME ?= @DATADIRNAME@
 DCT64 ?= @DCT64@
+DBUS_C ?= @DBUS_C@
+DBUS_BINDINGS_H ?= @DBUS_BINDINGS_H@
+DBUS_BINDING_TOOL ?= @DBUS_BINDING_TOOL@
+DBUS_CFLAGS ?= @DBUS_CFLAGS@
+DBUS_LIBS ?= @DBUS_LIBS@
 DEFS ?= @DEFS@
 DEPDIR ?= @DEPDIR@
 ECHO ?= @ECHO@
--- a/src/audacious/Makefile	Wed May 02 10:34:59 2007 -0700
+++ b/src/audacious/Makefile	Sat May 05 15:37:54 2007 -0700
@@ -5,6 +5,7 @@
 SUBDIRS = widgets glade images ui
 
 OBJECTIVE_BINS = audacious
+OBJECTIVE_LIBS_NOINST += $(DBUS_BINDINGS_H)
 
 LDFLAGS += $(AUDLDFLAGS)
 
@@ -14,6 +15,7 @@
 	$(samplerate_LIBS) \
 	$(CHARDET_LIBS) \
 	$(GTK_LIBS) \
+	$(DBUS_LIBS) \
 	$(MOWGLI_LIBS) \
 	$(LIBGLADE_LIBS) \
 	$(REGEX_LIBS) \
@@ -25,6 +27,7 @@
 	$(LIBGLADE_CFLAGS) \
 	$(BEEP_DEFINES) \
 	$(ARCH_DEFINES) \
+	$(DBUS_CFLAGS) \
 	$(samplerate_CFLAGS) \
 	$(REGEX_CFLAGS) \
 	-D_AUDACIOUS_CORE \
@@ -55,6 +58,7 @@
 SOURCES = \
 	build_stamp.c \
 	controlsocket.c \
+	$(DBUS_C) \
 	dnd.c \
 	dock.c \
 	effect.c \
@@ -119,4 +123,7 @@
 	$(CXX) $(LDFLAGS) $(OBJECTS) $(LDADD) -o $@ 
 	@printf "%10s     %-20s\n" LINK $@
 
+dbus-bindings.h:
+	$(DBUS_BINDING_TOOL) --mode=glib-server --prefix=audacious_remote objects.xml > $@
+
 OBJECTIVE_DATA = audacious.desktop:$(datadir)/applications
--- a/src/audacious/build_stamp.c	Wed May 02 10:34:59 2007 -0700
+++ b/src/audacious/build_stamp.c	Sat May 05 15:37:54 2007 -0700
@@ -1,2 +1,2 @@
 #include <glib.h>
-const gchar *svn_stamp = "20070501-4410";
+const gchar *svn_stamp = "20070502-4412";
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/audacious/dbus.c	Sat May 05 15:37:54 2007 -0700
@@ -0,0 +1,229 @@
+/*
+*
+* Author: Ben Tucker <bnt@interchange.ubc.ca>, (C) 2007
+*
+* 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 Street, Fifth Floor, Boston, MA 02110-1301  USA
+*
+*/
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <glib.h>
+#include <dbus/dbus-glib-bindings.h>
+#include "dbus.h"
+#include "dbus-bindings.h"
+
+#include "main.h"
+#include "ui_equalizer.h"
+#include "ui_main.h"
+#include "input.h"
+#include "playback.h"
+#include "playlist.h"
+#include "ui_playlist.h"
+#include "ui_preferences.h"
+#include "libaudacious/beepctrl.h"
+#include "memorypool.h"
+#include "titlestring.h"
+#include "ui_jumptotrack.h"
+
+G_DEFINE_TYPE(RemoteObject, audacious_remote, G_TYPE_OBJECT);
+
+void audacious_remote_class_init(RemoteObjectClass *class) {}
+
+void audacious_remote_init(RemoteObject *object) {
+	GError *error = NULL;
+	DBusGProxy *driver_proxy;
+	unsigned int request_ret;
+
+	// Initialize the DBus connection
+	object->connection = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
+	if (object->connection == NULL) {
+		g_warning("Unable to connect to dbus: %s", error->message);
+		g_error_free(error);
+		return;
+	}
+	
+	dbus_g_object_type_install_info(audacious_remote_get_type(),
+																	&dbus_glib_audacious_remote_object_info);
+	
+	// Register DBUS path
+	dbus_g_connection_register_g_object(object->connection, DBUS_OBJECT_PATH,
+																			G_OBJECT(object));
+
+	// Register the service name, the constants here are defined in
+	// dbus-glib-bindings.h
+	driver_proxy = dbus_g_proxy_new_for_name(object->connection,
+																					 DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
+																					 DBUS_INTERFACE_DBUS);
+
+	if (!org_freedesktop_DBus_request_name(driver_proxy, DBUS_SERVICE, 0,
+			&request_ret, &error)) {
+		g_warning("Unable to register service: %s", error->message);
+		g_error_free(error);
+	}
+	
+	g_object_unref(driver_proxy);
+}
+
+RemoteObject *init_dbus() {
+	RemoteObject *object;
+	g_type_init();
+	object = g_object_new(audacious_remote_get_type(), NULL);
+	return object;
+}
+
+// Audacious General Information
+gboolean audacious_remote_version(RemoteObject *obj, gchar **version,
+																	GError **error) {
+	*version = g_strdup(VERSION);
+	return TRUE;
+}
+
+// Playback Information/Manipulation
+gboolean audacious_remote_play(RemoteObject *obj, GError **error) {
+	if (playback_get_paused())
+		playback_pause();
+	else if (playlist_get_length(playlist_get_active()))
+		playback_initiate();
+	else
+		mainwin_eject_pushed();
+	return TRUE;
+}
+
+gboolean audacious_remote_pause(RemoteObject *obj, GError **error) {
+	playback_pause();
+	return TRUE;
+}
+
+gboolean audacious_remote_stop(RemoteObject *obj, GError **error) {
+	ip_data.stop = TRUE;
+	playback_stop();
+	ip_data.stop = FALSE;
+	mainwin_clear_song_info();
+	return TRUE;
+}
+
+gboolean audacious_remote_playing(RemoteObject *obj, gboolean *is_playing,
+																	GError **error) {
+	*is_playing = playback_get_playing();
+	return TRUE;
+}
+
+gboolean audacious_remote_paused(RemoteObject *obj, gboolean *is_paused,
+																 GError **error) {
+	*is_paused = playback_get_paused();
+	return TRUE;
+}
+
+gboolean audacious_remote_stopped(RemoteObject *obj, gboolean *is_stopped,
+																	GError **error) {
+	*is_stopped = !playback_get_playing();
+	return TRUE;
+}
+
+gboolean audacious_remote_status(RemoteObject *obj, gchar **status,
+																 GError **error) {
+	if (playback_get_paused())
+		*status = g_strdup("paused");
+	else if (playback_get_playing())
+		*status = g_strdup("playing");
+	else
+		*status = g_strdup("stopped");
+	return TRUE;
+}
+
+gboolean audacious_remote_seek(RemoteObject *obj, guint pos, GError **error) {
+	if (playlist_get_current_length(playlist_get_active()) > 0 &&
+			pos < (guint)playlist_get_current_length(playlist_get_active()))
+			playback_seek(pos / 1000);
+
+	return TRUE;
+}
+
+// Playlist Information/Manipulation
+gboolean audacious_remote_position(RemoteObject *obj, int *pos, GError **error)
+{
+	return TRUE;
+}
+
+gboolean audacious_remote_advance(RemoteObject *obj, GError **error) {
+	return TRUE;
+}
+
+gboolean audacious_remote_reverse(RemoteObject *obj, GError **error) {
+	return TRUE;
+}
+
+gboolean audacious_remote_length(RemoteObject *obj, int *length,
+																 GError **error) {
+	return TRUE;
+}
+
+gboolean audacious_remote_song_title(RemoteObject *obj, int pos,
+																		 gchar **title, GError **error) {
+	return TRUE;
+}
+
+gboolean audacious_remote_song_filename(RemoteObject *obj, int pos,
+																				gchar **filename, GError **error) {
+	return TRUE;
+}
+
+gboolean audacious_remote_song_length(RemoteObject *obj, int pos, int *length,
+																			GError **error) {
+	return TRUE;
+}
+
+gboolean audacious_remote_song_frames(RemoteObject *obj, int pos, int *length,
+																			GError **error) {
+	return TRUE;
+}
+
+gboolean audacious_remote_jump(RemoteObject *obj, int pos, GError **error) {
+	return TRUE;
+}
+
+gboolean audacious_remote_add_url(RemoteObject *obj, gchar *url,
+																	GError **error) {
+	return TRUE;
+}
+
+gboolean audacious_remote_delete(RemoteObject *obj, int pos, GError **error) {
+	return TRUE;
+}
+
+gboolean audacious_remote_clear(RemoteObject *obj, GError **error) {
+	return TRUE;
+}
+
+gboolean audacious_remote_repeating(RemoteObject *obj, gboolean *is_repeating,
+																		GError **error) {
+	return TRUE;
+}
+
+gboolean audacious_remote_repeat(RemoteObject *obj, GError **error) {
+	return TRUE;
+}
+
+gboolean audacious_remote_shuffling(RemoteObject *obj, gboolean *is_shuffling,
+																		GError **error) {
+	return TRUE;
+}
+
+gboolean audacious_remote_shuffle(RemoteObject *obj, GError **error) {
+	return TRUE;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/audacious/dbus.h	Sat May 05 15:37:54 2007 -0700
@@ -0,0 +1,88 @@
+/*
+*
+* Author: Ben Tucker <bnt@interchange.ubc.ca>, (C) 2007
+*
+* 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 Street, Fifth Floor, Boston, MA 02110-1301  USA
+*
+*/
+
+#ifndef _AUDDBUS_H
+#define _AUDDBUS_H
+
+#include <glib.h>
+
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/dbus-glib.h>
+
+#define DBUS_SERVICE "org.audacious"
+#define DBUS_OBJECT_PATH "/org/audacious"
+
+typedef struct {
+	GObject parent;
+	DBusGConnection *connection;
+} RemoteObject;
+
+typedef struct {
+	GObjectClass parent_class;
+} RemoteObjectClass;
+
+RemoteObject *init_dbus();
+//static void audacious_remote_init(RemoteObject *object);
+//static void audacious_remote_class_init(RemoteObjectClass *class);
+
+// Audacious General Information
+gboolean audacious_remote_version(RemoteObject *obj, gchar **version,
+																	GError **error);
+
+// Playback Information/Manipulation
+gboolean audacious_remote_play(RemoteObject *obj, GError **error);
+gboolean audacious_remote_pause(RemoteObject *obj, GError **error);
+gboolean audacious_remote_stop(RemoteObject *obj, GError **error);
+gboolean audacious_remote_playing(RemoteObject *obj, gboolean *is_playing,
+																	GError **error);
+gboolean audacious_remote_paused(RemoteObject *obj, gboolean *is_paused,
+																 GError **error);
+gboolean audacious_remote_stopped(RemoteObject *obj, gboolean *is_stopped,
+																	GError **error);
+gboolean audacious_remote_status(RemoteObject *obj, gchar **status,
+																 GError **error);
+gboolean audacious_remote_seek(RemoteObject *obj, guint pos, GError **error);
+
+// Playlist Information/Manipulation
+gboolean audacious_remote_position(RemoteObject *obj, int *pos, GError **error);
+gboolean audacious_remote_advance(RemoteObject *obj, GError **error);
+gboolean audacious_remote_reverse(RemoteObject *obj, GError **error);
+gboolean audacious_remote_length(RemoteObject *obj, int *length,
+																 GError **error);
+gboolean audacious_remote_song_title(RemoteObject *obj, int pos,
+																		gchar **title, GError **error);
+gboolean audacious_remote_song_filename(RemoteObject *obj, int pos,
+																			 gchar **filename, GError **error);
+gboolean audacious_remote_song_length(RemoteObject *obj, int pos, int *length,
+																		 GError **error);
+gboolean audacious_remote_song_frames(RemoteObject *obj, int pos, int *length,
+																		 GError **error);
+gboolean audacious_remote_jump(RemoteObject *obj, int pos, GError **error);
+gboolean audacious_remote_add_url(RemoteObject *obj, gchar *url,
+																	GError **error);
+gboolean audacious_remote_delete(RemoteObject *obj, int pos, GError **error);
+gboolean audacious_remote_clear(RemoteObject *obj, GError **error);
+gboolean audacious_remote_repeating(RemoteObject *obj, gboolean *is_repeating,
+																		GError **error);
+gboolean audacious_remote_repeat(RemoteObject *obj, GError **error);
+gboolean audacious_remote_shuffling(RemoteObject *obj, gboolean *is_shuffling,
+																		GError **error);
+gboolean audacious_remote_shuffle(RemoteObject *obj, GError **error);
+#endif // !_AUDDBUS_H
--- a/src/audacious/main.c	Wed May 02 10:34:59 2007 -0700
+++ b/src/audacious/main.c	Sat May 05 15:37:54 2007 -0700
@@ -49,6 +49,10 @@
 #include "libaudacious/beepctrl.h"
 #include "vfs.h"
 
+#ifdef USE_DBUS
+#  include "dbus.h"
+#endif
+
 #include "controlsocket.h"
 #include "dnd.h"
 #include "effect.h"
@@ -1184,6 +1188,10 @@
     if (options.headless != 1)
         GDK_THREADS_LEAVE();
 
+#ifdef USE_DBUS
+		RemoteObject *object = init_dbus();
+#endif
+
     ctrlsocket_start();
 
     handle_cmd_line_options(&options, FALSE);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/audacious/objects.xml	Sat May 05 15:37:54 2007 -0700
@@ -0,0 +1,223 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!--
+  Defined:
+  CMD_GET_VERSION
+  CMD_PLAY
+  CMD_PAUSE
+  CMD_STOP
+  CMD_IS_PLAYING
+  CMD_GET_PLAYLIST_POS
+  CMD_SET_PLAYLIST_POS
+  CMD_GET_PLAYLIST_LENGTH
+  CMD_PLAYLIST_CLEAR
+	CMD_GET_OUTPUT_TIME
+	CMD_JUMP_TO_TIME
+	CMD_GET_PLAYLIST_FILE
+	CMD_GET_PLAYLIST_TITLE
+	CMD_GET_PLAYLIST_TIME
+	CMD_PLAYLIST_PREV
+	CMD_PLAYLIST_NEXT
+	CMD_TOGGLE_REPEAT
+	CMD_TOGGLE_SHUFFLE
+  CMD_PLAYLIST_ADD_URL_STRING
+	CMD_PLAYLIST_DELETE
+	CMD_IS_REPEAT
+	CMD_IS_SHUFFLE
+
+  
+  Remaining:
+  CMD_PLAYLIST_ADD
+	CMD_GET_VOLUME
+	CMD_SET_VOLUME
+	CMD_GET_SKIN
+	CMD_SET_SKIN
+	CMD_GET_INFO
+	CMD_GET_EQ_DATA
+	CMD_SET_EQ_DATA
+	CMD_PL_WIN_TOGGLE
+	CMD_EQ_WIN_TOGGLE
+	CMD_SHOW_PREFS_BOX
+	CMD_TOGGLE_AOT
+	CMD_SHOW_ABOUT_BOX
+	CMD_EJECT
+	CMD_PING?
+	CMD_GET_BALANCE
+	CMD_MAIN_WIN_TOGGLE
+	CMD_IS_EQ_WIN
+	CMD_IS_PL_WIN
+	CMD_IS_MAIN_WIN
+	CMD_GET_EQ
+	CMD_GET_EQ_PREAMP
+	CMD_GET_EQ_BAND
+	CMD_SET_EQ
+	CMD_SET_EQ_PREAMP
+	CMD_SET_EQ_BAND
+	CMD_QUIT
+	CMD_PLAYLIST_INS_URL_STRING
+	CMD_PLAYLIST_INS
+	CMD_PLAY_PAUSE
+	CMD_PLAYQUEUE_ADD
+	CMD_GET_PLAYQUEUE_LENGTH
+	CMD_PLAYQUEUE_REMOVE
+	CMD_TOGGLE_ADVANCE
+	CMD_IS_ADVANCE
+	CMD_ACTIVATE
+	CMD_SHOW_JTF_BOX
+	CMD_PLAYQUEUE_CLEAR
+	CMD_PLAYQUEUE_IS_QUEUED
+	CMD_PLAYQUEUE_GET_POS
+	CMD_PLAYQUEUE_GET_QPOS
+	CMD_PLAYLIST_ENQUEUE_TO_TEMP
+	CMD_PLAYLIST_GET_TUPLE_DATA
+-->
+
+<node name="/org/audacious">
+	<!-- Audacious General Information -->
+	<interface name="org.audacious.general">
+		<!-- Audacious version -->
+		<method name="Version">
+			<arg type="s" direction="out" name="version"/>
+		</method>
+	</interface>
+
+	<!-- Playback Information/Manipulation -->
+	<interface name="org.audacious.playback">
+		<!-- Begin or resume playback -->
+		<method name="Play"/>
+
+		<!-- Pause playback -->
+		<method name="Pause"/>
+
+		<!-- Stop playback -->
+		<method name="Stop"/>
+
+		<!-- Is playback playing? -->
+		<method name="Playing">
+			<!-- Return true if playing, false otherwise -->
+			<arg type="b" direction="out" name="is_playing"/>
+		</method>
+
+		<!-- Is playback paused? -->
+		<method name="Paused">
+			<!-- Return true if paused, false otherwise -->
+			<arg type="b" direction="out" name="is_paused"/>
+		</method>
+
+		<!-- Is playback stopped? -->
+		<method name="Stopped">
+			<!-- Return true if stopped, false otherwise -->
+			<arg type="b" direction="out" name="is_stopped"/>
+		</method>
+
+		<!-- What is the playback status? -->
+		<method name="Status">
+			<!-- Return the status as a string: -->
+			<!-- one of {"playing" "paused" "stopped"} -->
+			<arg type="s" direction="out" name="status"/>
+		</method>
+
+		<!-- Seek to some absolute position in the current song -->
+		<method name="Seek">
+			<!-- Position of song, in ms, to seek to -->
+			<arg type="i" name="pos"/>
+
+			<!-- Return true on success, or false if position out of range? -->
+			<!-- <arg type="b" direction="out"/> -->
+		</method>
+	</interface>
+
+	<!-- Playlist Information/Manipulation -->
+	<interface name="org.audacious.playlist">
+		<!-- Playlist position -->
+		<method name="Position">
+			<!-- Return position of current song in current playlist -->
+			<arg type="i" direction="out" name="pos"/>
+		</method>
+
+		<!-- Skip ahead one song in the current playlist -->
+		<method name="Advance"/>
+
+		<!-- Skip backwards one song in the current playlist -->
+		<method name="Reverse"/>
+
+		<!-- Playlist length -->
+		<method name="Length">
+			<!-- Return length of current playlist -->
+			<arg type="i" direction="out" name="length"/>
+		</method>
+
+		<!-- Get a song's title -->
+		<method name="SongTitle">
+			<!-- Song position in the playlist -->
+			<arg type="i" name="pos"/>
+
+			<!-- Return title of desired song -->
+			<arg type="s" direction="out" name="title"/>
+		</method>
+
+		<!-- Get a song's filename -->
+		<method name="SongFilename">
+			<!-- Song position in the playlist -->
+			<arg type="i" name="pos"/>
+
+			<!-- Return filename of desired song -->
+			<arg type="s" direction="out" name="filename"/>
+		</method>
+
+		<!-- Get the length of some song, in seconds -->
+		<method name="SongLength">
+			<!-- Song position in the playlist -->
+			<arg type="i" name="pos"/>
+
+			<!-- Return length, in seconds, of desired song -->
+			<arg type="i" direction="out" name="length"/>
+		</method>
+
+		<!-- Get the length of some song, in frames -->
+		<method name="SongFrames">
+			<!-- Song position in the playlist -->
+			<arg type="i" name="pos"/>
+
+			<!-- Return length, in frames, of desired song -->
+			<arg type="i" direction="out" name="length"/>
+		</method>
+
+		<!-- Jump to some position in the playlist -->
+		<method name="Jump">
+			<!-- Song position to jump to -->
+			<arg type="i" name="pos"/>
+		</method>
+
+		<!-- Add some URL to the current playlist -->
+		<method name="AddUrl">
+			<!-- URL to add -->
+			<arg type="s" name="url"/>
+		</method>
+
+		<!-- Delete some song from the playlist -->
+		<method name="Delete">
+			<!-- Position of song to delete -->
+			<arg type="i" name="pos"/>
+		</method>
+
+		<!-- Clear the playlist -->
+		<method name="Clear"/>
+
+		<!-- Query repeat status -->
+		<method name="Repeating">
+			<arg type="b" direction="out" name="is_repeating"/>
+		</method>
+
+		<!-- Toggle repeat -->
+		<method name="Repeat"/>
+
+		<!-- Query shuffle status -->
+		<method name="Shuffling">
+			<arg type="b" direction="out" name="is_shuffling"/>
+		</method>
+
+		<!-- Toggle shuffle -->
+		<method name="Shuffle"/>
+	</interface>
+</node>