Skip to content

Commit

Permalink
Split volume function into read-only property and a method for settin…
Browse files Browse the repository at this point in the history
…g it. Making the property read-write is too awkward and kludgy.
  • Loading branch information
mikebrady committed Mar 10, 2018
1 parent f4b9e99 commit 39f159b
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 124 deletions.
114 changes: 110 additions & 4 deletions dacp.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ int dacp_send_command(const char *command, char **body, ssize_t *bodysize) {
// 495 Error receiving response
// 494 This client is already busy
// 493 Client failed to send a message
// 492 Argement out of range

// try to do this transaction on the DACP server, but don't wait for more than 20 ms to be allowed
// to do it.
Expand Down Expand Up @@ -162,7 +163,7 @@ int dacp_send_command(const char *command, char **body, ssize_t *bodysize) {

// only do this one at a time -- not sure it is necessary, but better safe than sorry

// int mutex_reply = pthread_mutex_timedlock(&dacp_conversation_lock, &mutex_wait_time);
// int mutex_reply = pthread_mutex_timedlock(&dacp_conversation_lock, &mutex_wait_time);
int mutex_reply = pthread_mutex_lock(&dacp_conversation_lock);
if (mutex_reply == 0) {

Expand Down Expand Up @@ -662,12 +663,12 @@ int dacp_get_client_volume(int32_t *result) {
}

int dacp_set_include_speaker_volume(int64_t machine_number, int32_t vo) {
debug(1, "dacp_set_include_speaker_volume to %" PRId32 ".", vo);
debug(2, "dacp_set_include_speaker_volume to %" PRId32 ".", vo);
char message[1000];
memset(message, 0, sizeof(message));
sprintf(message, "setproperty?include-speaker-id=%" PRId64 "&dmcp.volume=%" PRId32 "",
machine_number, vo);
debug(1, "sending \"%s\"", message);
debug(2, "sending \"%s\"", message);
return send_simple_dacp_command(message);
// should return 204
}
Expand All @@ -677,7 +678,7 @@ int dacp_set_speaker_volume(int64_t machine_number, int32_t vo) {
memset(message, 0, sizeof(message));
sprintf(message, "setproperty?speaker-id=%" PRId64 "&dmcp.volume=%" PRId32 "", machine_number,
vo);
debug(1, "sending \"%s\"", message);
debug(2, "sending \"%s\"", message);
return send_simple_dacp_command(message);
// should return 204
}
Expand Down Expand Up @@ -852,3 +853,108 @@ int dacp_get_volume(int32_t *the_actual_volume) {
}
return http_response;
}

int dacp_set_volume(int32_t vo) {
int http_response = 492; // argument out of range
if ((vo >= 0) && (vo <= 100)) {
// get the information we need -- the absolute volume, the speaker list, our ID
struct dacp_speaker_stuff speaker_info[50];
int32_t overall_volume;
http_response = dacp_get_client_volume(&overall_volume);
if (http_response == 200) {
int speaker_count;
http_response = dacp_get_speaker_list((dacp_spkr_stuff *)&speaker_info, 50, &speaker_count);
if (http_response == 200) {
// get our machine number
uint16_t *hn = (uint16_t *)config.hw_addr;
uint32_t *ln = (uint32_t *)(config.hw_addr + 2);
uint64_t t1 = ntohs(*hn);
uint64_t t2 = ntohl(*ln);
int64_t machine_number = (t1 << 32) + t2; // this form is useful

// Let's find our own speaker in the array and pick up its relative volume
int i;
int32_t active_speakers = 0;
for (i = 0; i < speaker_count; i++) {
if (speaker_info[i].speaker_number == machine_number) {
debug(2, "Our speaker number found: %ld with relative volume.", machine_number,
speaker_info[i].volume);
}
if (speaker_info[i].active == 1) {
active_speakers++;
}
}

if (active_speakers == 1) {
// must be just this speaker
debug(2, "Remote-setting volume to %d on just one speaker.", vo);
http_response = dacp_set_include_speaker_volume(machine_number, vo);
} else if (active_speakers == 0) {
debug(2, "No speakers!");
} else {
debug(2, "Speakers: %d, active: %d", speaker_count, active_speakers);
if (vo >= overall_volume) {
debug(2, "Multiple speakers active, but desired new volume is highest");
http_response = dacp_set_include_speaker_volume(machine_number, vo);
} else {
// the desired volume is less than the current overall volume and there is more than
// one
// speaker
// we must find out the highest other speaker volume.
// If the desired volume is less than it, we must set the current_overall volume to
// that
// highest volume
// and set our volume relative to it.
// If the desired volume is greater than the highest current volume, then we can just
// go
// ahead
// with dacp_set_include_speaker_volume, setting the new current overall volume to the
// desired new level
// with the speaker at 100%

int32_t highest_other_volume = 0;
for (i = 0; i < speaker_count; i++) {
if ((speaker_info[i].speaker_number != machine_number) &&
(speaker_info[i].active == 1) &&
(speaker_info[i].volume > highest_other_volume)) {
highest_other_volume = speaker_info[i].volume;
}
}
highest_other_volume = (highest_other_volume * overall_volume + 50) / 100;
if (highest_other_volume <= vo) {
debug(2,
"Highest other volume %d is less than or equal to the desired new volume %d.",
highest_other_volume, vo);
http_response = dacp_set_include_speaker_volume(machine_number, vo);
} else {
debug(2, "Highest other volume %d is greater than the desired new volume %d.",
highest_other_volume, vo);
// if the present overall volume is higher than the highest other volume at present,
// then bring it down to it.
if (overall_volume > highest_other_volume) {
debug(2, "Lower overall volume to new highest volume.");
http_response = dacp_set_include_speaker_volume(
machine_number,
highest_other_volume); // set the overall volume to the highest one
}
int32_t desired_relative_volume =
(vo * 100 + (highest_other_volume / 2)) / highest_other_volume;
debug(2, "Set our speaker volume relative to the highest volume.");
http_response = dacp_set_speaker_volume(
machine_number,
desired_relative_volume); // set the overall volume to the highest one
}
}
}
} else {
debug(2, "Can't get speakers list");
}
} else {
debug(2, "Can't get client volume");
}

} else {
debug(2, "Invalid volume: %d -- ignored.", vo);
}
return http_response;
}
1 change: 1 addition & 0 deletions dacp.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ int dacp_set_include_speaker_volume(int64_t machine_number, int32_t vo);
int dacp_get_client_volume(int32_t *result);
int dacp_get_volume(
int32_t *the_actual_volume); // get the speaker volume information from the DACP source
int dacp_set_volume(int32_t vo); // set the volume of our speaker
122 changes: 8 additions & 114 deletions dbus-service.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,118 +44,12 @@ gboolean notify_loudness_threshold_callback(ShairportSync *skeleton,
return TRUE;
}

gboolean notify_volume_callback(ShairportSync *skeleton,
__attribute__((unused)) gpointer user_data) {
metadata_hub_read_prolog();
int md_speaker_volume = metadata_store.speaker_volume;
metadata_hub_read_epilog();
gint gvo = shairport_sync_get_volume(skeleton);
int32_t vo = gvo;
if ((vo >= 0) && (vo <= 100)) {
if (vo !=
md_speaker_volume) { // this is to stop an infinite loop of setting->checking->setting...
// get the information we need -- the absolute volume, the speaker list, our ID
struct dacp_speaker_stuff speaker_info[50];
int32_t overall_volume;
int http_response = dacp_get_client_volume(&overall_volume);
if (http_response == 200) {
int speaker_count;
http_response = dacp_get_speaker_list((dacp_spkr_stuff *)&speaker_info, 50, &speaker_count);
if (http_response == 200) {
// get our machine number
uint16_t *hn = (uint16_t *)config.hw_addr;
uint32_t *ln = (uint32_t *)(config.hw_addr + 2);
uint64_t t1 = ntohs(*hn);
uint64_t t2 = ntohl(*ln);
int64_t machine_number = (t1 << 32) + t2; // this form is useful

// Let's find our own speaker in the array and pick up its relative volume
int i;
int32_t active_speakers = 0;
for (i = 0; i < speaker_count; i++) {
if (speaker_info[i].speaker_number == machine_number) {
debug(2, "Our speaker number found: %ld with relative volume.", machine_number,
speaker_info[i].volume);
}
if (speaker_info[i].active == 1) {
active_speakers++;
}
}

if (active_speakers == 1) {
// must be just this speaker
debug(2, "Remote-setting volume to %d on just one speaker.", vo);
dacp_set_include_speaker_volume(machine_number, vo);
} else if (active_speakers == 0) {
debug(2, "No speakers!");
} else {
debug(2, "Speakers: %d, active: %d", speaker_count, active_speakers);
if (vo >= overall_volume) {
debug(2, "Multiple speakers active, but desired new volume is highest");
dacp_set_include_speaker_volume(machine_number, vo);
} else {
// the desired volume is less than the current overall volume and there is more than
// one
// speaker
// we must find out the highest other speaker volume.
// If the desired volume is less than it, we must set the current_overall volume to
// that
// highest volume
// and set our volume relative to it.
// If the desired volume is greater than the highest current volume, then we can just
// go
// ahead
// with dacp_set_include_speaker_volume, setting the new current overall volume to the
// desired new level
// with the speaker at 100%

int32_t highest_other_volume = 0;
for (i = 0; i < speaker_count; i++) {
if ((speaker_info[i].speaker_number != machine_number) &&
(speaker_info[i].active == 1) &&
(speaker_info[i].volume > highest_other_volume)) {
highest_other_volume = speaker_info[i].volume;
}
}
highest_other_volume = (highest_other_volume * overall_volume + 50) / 100;
if (highest_other_volume <= vo) {
debug(2,
"Highest other volume %d is less than or equal to the desired new volume %d.",
highest_other_volume, vo);
dacp_set_include_speaker_volume(machine_number, vo);
} else {
debug(2, "Highest other volume %d is greater than the desired new volume %d.",
highest_other_volume, vo);
// if the present overall volume is higher than the highest other volume at present,
// then bring it down to it.
if (overall_volume > highest_other_volume) {
debug(2, "Lower overall volume to new highest volume.");
dacp_set_include_speaker_volume(
machine_number,
highest_other_volume); // set the overall volume to the highest one
}
int32_t desired_relative_volume =
(vo * 100 + (highest_other_volume / 2)) / highest_other_volume;
debug(2, "Set our speaker volume relative to the highest volume.");
dacp_set_speaker_volume(
machine_number,
desired_relative_volume); // set the overall volume to the highest one
}
}
}
} else {
debug(2, "Can't get speakers list");
}
} else {
debug(2, "Can't get client volume");
}
} // else {
// debug(1, "No need to remote-set volume to %d, as it is already set to this value.", vo);
// }

} else {
debug(2, "Invalid volume: %d -- ignored.", vo);
}
static gboolean on_handle_set_volume(ShairportSync *skeleton, GDBusMethodInvocation *invocation,
const gint volume,
__attribute__((unused)) gpointer user_data) {
// debug(1, "1 Set volume to d.", volume);
dacp_set_volume(volume);
shairport_sync_complete_set_volume(skeleton, invocation);
return TRUE;
}

Expand Down Expand Up @@ -194,10 +88,10 @@ static void on_dbus_name_acquired(GDBusConnection *connection, const gchar *name
G_CALLBACK(notify_loudness_filter_active_callback), NULL);
g_signal_connect(shairportSyncSkeleton, "notify::loudness-threshold",
G_CALLBACK(notify_loudness_threshold_callback), NULL);
g_signal_connect(shairportSyncSkeleton, "notify::volume", G_CALLBACK(notify_volume_callback),
&metadata_store);
g_signal_connect(shairportSyncSkeleton, "handle-remote-command",
G_CALLBACK(on_handle_remote_command), NULL);
g_signal_connect(shairportSyncSkeleton, "handle-set-volume", G_CALLBACK(on_handle_set_volume),
NULL);

add_metadata_watcher(dbus_metadata_watcher, NULL);

Expand Down
5 changes: 4 additions & 1 deletion org.gnome.ShairportSync.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
<interface name="org.gnome.ShairportSync">
<property name="LoudnessFilterActive" type="b" access="readwrite" />
<property name="LoudnessThreshold" type="d" access="readwrite" />
<property name="Volume" type="d" access="readwrite" />
<property name="Volume" type="i" access="read" />
<method name="RemoteCommand">
<arg name="command" type="s" direction="in" />
</method>
<method name="SetVolume">
<arg name="volume" type="i" direction="in" />
</method>
</interface>
</node>
2 changes: 1 addition & 1 deletion org.mpris.MediaPlayer2.Player.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
<property name='Metadata' type='a{sv}' access='read'>
<annotation name="org.qtproject.QtDBus.QtTypeName" value="QVariantMap"/>
</property>
<property name='Volume' type='d' access='readwrite'/>
<property name='Volume' type='d' access='read'/>
<property name='Position' type='x' access='read'/>
<property name='MinimumRate' type='d' access='read'/>
<property name='MaximumRate' type='d' access='read'/>
Expand Down
8 changes: 4 additions & 4 deletions shairport-sync-dbus-test-client.c
Original file line number Diff line number Diff line change
Expand Up @@ -132,13 +132,13 @@ int main(int argc, char *argv[]) {

g_print("Starting test...\n");

shairport_sync_set_volume(SHAIRPORT_SYNC(proxy), 20);
shairport_sync_call_set_volume(SHAIRPORT_SYNC(proxy), 20, NULL, NULL, 0);
sleep(5);
shairport_sync_set_volume(SHAIRPORT_SYNC(proxy), 100);
shairport_sync_call_set_volume(SHAIRPORT_SYNC(proxy), 100, NULL, NULL, 0);
sleep(5);
shairport_sync_set_volume(SHAIRPORT_SYNC(proxy), 40);
shairport_sync_call_set_volume(SHAIRPORT_SYNC(proxy), 40, NULL, NULL, 0);
sleep(5);
shairport_sync_set_volume(SHAIRPORT_SYNC(proxy), 60);
shairport_sync_call_set_volume(SHAIRPORT_SYNC(proxy), 60, NULL, NULL, 0);
// sleep(1);
/*
shairport_sync_set_volume(SHAIRPORT_SYNC(proxy), 10);
Expand Down

0 comments on commit 39f159b

Please sign in to comment.