Skip to content

Commit

Permalink
Add SetAirplayVolume to the D-Bus RemoteControl interface.
Browse files Browse the repository at this point in the history
Add SetVolume to the MPRIS interface.
Hook up the Volume property in the MPRIS interface.
Modify RemoteCommand in the D-Bus interface to return the HTTP status and response.
Change the type of airplay_volume from int to double in the metadata hub.
Add a few sample commands in the D-Bus document.
  • Loading branch information
mikebrady committed Dec 20, 2019
1 parent ed9f8d1 commit fe8198a
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 6 deletions.
2 changes: 2 additions & 0 deletions dacp.c
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,8 @@ void *dacp_monitor_thread_code(__attribute__((unused)) void *na) {
// debug(1,"dacp_monitor_thread_code: command: \"%s\"",command);
result = dacp_send_command(command, &response, &le);
// debug(1,"Response to \"%s\" is %d.",command,result);
// remember: unless the revision_number you pass in is 1,
// response will be 200 only if there's something new to report.
if (result == 200) {
// if (0) {
char *sp = response;
Expand Down
2 changes: 2 additions & 0 deletions dacp.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ void relinquish_dacp_server_information(rtsp_conn_info *conn); // tell the DACP
// longer associated with it.
void dacp_monitor_port_update_callback(
char *dacp_id, uint16_t port); // a callback to say the port is no longer in use

int dacp_send_command(const char *command, char **body, ssize_t *bodysize);
int send_simple_dacp_command(const char *command);

int dacp_set_include_speaker_volume(int64_t machine_number, int32_t vo);
Expand Down
40 changes: 37 additions & 3 deletions dbus-service.c
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,19 @@ static gboolean on_handle_volume_down(ShairportSyncRemoteControl *skeleton,
return TRUE;
}

static gboolean on_handle_set_airplay_volume(ShairportSyncRemoteControl *skeleton,
GDBusMethodInvocation *invocation, const gdouble volume,
__attribute__((unused)) gpointer user_data) {
debug(2, "Set airplay volume to %.6f.", volume);
char command[256] = "";
snprintf(command, sizeof(command), "setproperty?dmcp.device-volume=%.6f", volume);
send_simple_dacp_command(command);
shairport_sync_remote_control_complete_set_airplay_volume(skeleton, invocation);
return TRUE;
}



gboolean notify_elapsed_time_callback(ShairportSyncDiagnostics *skeleton,
__attribute__((unused)) gpointer user_data) {
// debug(1, "\"notify_elapsed_time_callback\" called.");
Expand Down Expand Up @@ -714,12 +727,30 @@ static gboolean on_handle_quit(ShairportSync *skeleton, GDBusMethodInvocation *i
static gboolean on_handle_remote_command(ShairportSync *skeleton, GDBusMethodInvocation *invocation,
const gchar *command,
__attribute__((unused)) gpointer user_data) {
debug(1, "RemoteCommand with command \"%s\".", command);
send_simple_dacp_command((const char *)command);
shairport_sync_complete_remote_command(skeleton, invocation);
debug(1, "RemoteCommand with command \"%s\".", command);
int reply = 0;
char *server_reply = NULL;
ssize_t reply_size = 0;
reply = dacp_send_command((const char *)command, &server_reply, &reply_size);
char *server_reply_hex = alloca(reply_size * 2 + 1);
if (server_reply_hex) {
char *p = server_reply_hex;
if (server_reply) {
char *q = server_reply;
int i;
for (i = 0; i < reply_size; i++) {
snprintf(p, 3, "%02X", *q);
p += 2;
q++;
}
}
*p = '\0';
}
shairport_sync_complete_remote_command(skeleton, invocation, reply, server_reply_hex);
return TRUE;
}


static void on_dbus_name_acquired(GDBusConnection *connection, const gchar *name,
__attribute__((unused)) gpointer user_data) {

Expand Down Expand Up @@ -814,6 +845,9 @@ static void on_dbus_name_acquired(GDBusConnection *connection, const gchar *name
G_CALLBACK(on_handle_volume_up), NULL);
g_signal_connect(shairportSyncRemoteControlSkeleton, "handle-volume-down",
G_CALLBACK(on_handle_volume_down), NULL);
g_signal_connect(shairportSyncRemoteControlSkeleton, "handle-set-airplay-volume",
G_CALLBACK(on_handle_set_airplay_volume), NULL);


g_signal_connect(shairportSyncAdvancedRemoteControlSkeleton, "handle-set-volume",
G_CALLBACK(on_handle_set_volume), NULL);
Expand Down
15 changes: 15 additions & 0 deletions documents/sample dbus commands
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# For the "Native" Shairport Sync D-Bus Interface support, Shairport Sync must be built with the D-Bus interface support. Add the '--with-dbus-interface' flag at the ./configure stage.
# There is a simple test client that you can have built -- add the '--with-dbus-test-client' flag at the ./configure stage. You'll get an executable called shairport-sync-dbus-test-client

# Get Log Verbosity
dbus-send --print-reply --system --dest=org.gnome.ShairportSync /org/gnome/ShairportSync org.freedesktop.DBus.Properties.Get string:org.gnome.ShairportSync.Diagnostics string:Verbosity
# Return Log Verbosity
Expand Down Expand Up @@ -59,6 +62,11 @@ dbus-send --print-reply --system --dest=org.gnome.ShairportSync /org/gnome/Shair
# Set Convolution Impulse Response File:
dbus-send --print-reply --system --dest=org.gnome.ShairportSync /org/gnome/ShairportSync org.freedesktop.DBus.Properties.Set string:org.gnome.ShairportSync string:ConvolutionImpulseResponseFile variant:string:"/etc/shairport-sync/boom.wav"

# Set Airplay Volume using Remote Control. Airplay Volume is between -30.0 and 0.0 and maps linearly onto the slider, with -30.0 being lowest and 0.0 being highest.
dbus-send --system --print-reply --type=method_call --dest=org.gnome.ShairportSync '/org/gnome/ShairportSync' org.gnome.ShairportSync.RemoteControl.SetAirplayVolume double:-10.0


# AdvancedRemoteControl interface.
# Some commands and properties are accessible only through the AdvancedRemoteControl interface.
# However, only iTunes or the macOS Music app from 10.15.2 onwards
# provide the functionality needed for the AdvancedRemoteControl interface to work.
Expand All @@ -70,3 +78,10 @@ dbus-send --print-reply --system --dest=org.gnome.ShairportSync /org/gnome/Shair
# Set Volume using Advanced Remote Control -- only works if the org.gnome.ShairportSync.AdvancedRemoteControl is available.
dbus-send --system --print-reply --type=method_call --dest=org.gnome.ShairportSync '/org/gnome/ShairportSync' org.gnome.ShairportSync.AdvancedRemoteControl.SetVolume int32:50


# MPRIS interface commands.
# For MPRIS support, Shairport Sync must be built with the MPRIS interface support. Add the '--with-mpris-interface' flag at the ./configure stage.
# There is a simple test client that you can have built -- add the '--with-mpris-test-client' flag at the ./configure stage. You'll get an executable called shairport-sync-mpris-test-client.
# This is mostly compatible with the MPRIS standard, except that Volume is read-only, with a separate SetVolume method.
# Set Volume, which must be between 0.0 and 1.0 and maps linearly onto the slider.
dbus-send --system --print-reply --type=method_call --dest=org.mpris.MediaPlayer2.ShairportSync '/org/mpris/MediaPlayer2' org.mpris.MediaPlayer2.Player.SetVolume double:0.3
2 changes: 1 addition & 1 deletion metadata_hub.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ typedef struct metadata_bundle {

int speaker_volume; // this is the actual speaker volume, allowing for the main volume and the
// speaker volume control
int airplay_volume;
double airplay_volume;

metadata_watcher watchers[number_of_watchers]; // functions to call if the metadata is changed.
void *watchers_data[number_of_watchers]; // their individual data
Expand Down
36 changes: 34 additions & 2 deletions mpris-service.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,28 @@
#include "metadata_hub.h"
#include "mpris-service.h"

double airplay_volume_to_mpris_volume(double sp) {
if (sp < -30.0)
sp = -30.0;
if (sp > 0.0)
sp = 0.0;
sp = (sp/30.0)+1;
return sp;
}

double mpris_volume_to_airplay_volume(double sp) {
sp = (sp-1.0)*30.0;
if (sp < -30.0)
sp = -30.0;
if (sp > 0.0)
sp = 0.0;
return sp;
}

void mpris_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused)) void *userdata) {
// debug(1, "MPRIS metadata watcher called");
char response[100];

media_player2_player_set_volume(mprisPlayerPlayerSkeleton, airplay_volume_to_mpris_volume(argc->airplay_volume));
switch (argc->repeat_status) {
case RS_NOT_AVAILABLE:
strcpy(response, "Not Available");
Expand Down Expand Up @@ -229,6 +247,18 @@ static gboolean on_handle_play(MediaPlayer2Player *skeleton, GDBusMethodInvocati
return TRUE;
}

static gboolean on_handle_set_volume(MediaPlayer2Player *skeleton,
GDBusMethodInvocation *invocation, const gdouble volume,
__attribute__((unused)) gpointer user_data) {
double ap_volume = mpris_volume_to_airplay_volume(volume);
debug(2, "Set mpris volume to %.6f, i.e. airplay volume to %.6f.", volume, ap_volume);
char command[256] = "";
snprintf(command, sizeof(command), "setproperty?dmcp.device-volume=%.6f", ap_volume);
send_simple_dacp_command(command);
media_player2_player_complete_play(skeleton, invocation);
return TRUE;
}

static void on_mpris_name_acquired(GDBusConnection *connection, const gchar *name,
__attribute__((unused)) gpointer user_data) {

Expand All @@ -254,7 +284,6 @@ static void on_mpris_name_acquired(GDBusConnection *connection, const gchar *nam

media_player2_player_set_playback_status(mprisPlayerPlayerSkeleton, "Stopped");
media_player2_player_set_loop_status(mprisPlayerPlayerSkeleton, "None");
media_player2_player_set_volume(mprisPlayerPlayerSkeleton, 0.5);
media_player2_player_set_minimum_rate(mprisPlayerPlayerSkeleton, 1.0);
media_player2_player_set_maximum_rate(mprisPlayerPlayerSkeleton, 1.0);
media_player2_player_set_can_go_next(mprisPlayerPlayerSkeleton, TRUE);
Expand All @@ -274,6 +303,9 @@ static void on_mpris_name_acquired(GDBusConnection *connection, const gchar *nam
g_signal_connect(mprisPlayerPlayerSkeleton, "handle-next", G_CALLBACK(on_handle_next), NULL);
g_signal_connect(mprisPlayerPlayerSkeleton, "handle-previous", G_CALLBACK(on_handle_previous),
NULL);
g_signal_connect(mprisPlayerPlayerSkeleton, "handle-set-volume", G_CALLBACK(on_handle_set_volume),
NULL);


add_metadata_watcher(mpris_metadata_watcher, NULL);

Expand Down
5 changes: 5 additions & 0 deletions org.gnome.ShairportSync.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
<property name="DriftTolerance" type="d" access="readwrite" />
<method name="RemoteCommand">
<arg name="command" type="s" direction="in" />
<arg name="reply" type="i" direction="out" />
<arg name="response" type="s" direction="out" />
</method>
<property name="VolumeControlProfile" type="s" access="readwrite" />
<property name="Interpolation" type="s" access="readwrite" />
Expand Down Expand Up @@ -45,6 +47,9 @@
<property name='ProgressString' type='s' access='read'/>
<property name='Server' type='s' access='read'/>
<property name='AirplayVolume' type='d' access='read'/>
<method name="SetAirplayVolume">
<arg name="volume" type="d" direction="in" />
</method>
<property name='Metadata' type='a{sv}' access='read'>
<annotation name="org.qtproject.QtDBus.QtTypeName" value="QVariantMap"/>
</property>
Expand Down
3 changes: 3 additions & 0 deletions org.mpris.MediaPlayer2.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@
<annotation name="org.qtproject.QtDBus.QtTypeName" value="QVariantMap"/>
</property>
<property name='Volume' type='d' access='read'/>
<method name='SetVolume'>
<arg name='volume' type='d' direction='in' />
</method>
<property name='Position' type='x' access='read'/>
<property name='MinimumRate' type='d' access='read'/>
<property name='MaximumRate' type='d' access='read'/>
Expand Down

0 comments on commit fe8198a

Please sign in to comment.