]> git.ipfire.org Git - thirdparty/shairport-sync.git/blame - dbus-service.c
Update check_classic_systemd_full.yml
[thirdparty/shairport-sync.git] / dbus-service.c
CommitLineData
1f75fe4e
MB
1/*
2 * This file is part of Shairport Sync.
720a271e 3 * Copyright (c) Mike Brady 2018 -- 2022
1f75fe4e
MB
4 * All rights reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person
7 * obtaining a copy of this software and associated documentation
8 * files (the "Software"), to deal in the Software without
9 * restriction, including without limitation the rights to use,
10 * copy, modify, merge, publish, distribute, sublicense, and/or
11 * sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 * OTHER DEALINGS IN THE SOFTWARE.
25 */
26
54d761ff 27#include <inttypes.h>
d4eae76f 28#include <stdio.h>
340e365a 29#include <stdlib.h>
d4eae76f
MB
30#include <string.h>
31
32#include "config.h"
33
34#include "common.h"
35#include "player.h"
36#include "rtsp.h"
37
38#include "rtp.h"
39
40#include "dacp.h"
9d9b0a18 41#include "metadata_hub.h"
d4eae76f 42
df7c48f0 43#include "dbus-service.h"
9d9b0a18 44
2f2442f4
MB
45#ifdef CONFIG_CONVOLUTION
46#include <FFTConvolver/convolver.h>
47#endif
48
85ba78b6
MB
49ShairportSync *shairportSyncSkeleton;
50
97a4766e
MB
51int service_is_running = 0;
52
051326b4
MB
53ShairportSyncDiagnostics *shairportSyncDiagnosticsSkeleton = NULL;
54ShairportSyncRemoteControl *shairportSyncRemoteControlSkeleton = NULL;
55ShairportSyncAdvancedRemoteControl *shairportSyncAdvancedRemoteControlSkeleton = NULL;
d4eae76f 56
f1d45034
MB
57guint ownerID = 0;
58
8991f342 59void dbus_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused)) void *userdata) {
69b3b5d7 60 char response[100];
839c778a 61 gboolean current_status, new_status;
ec665d9b 62
cb69c1dd 63 const char *th;
69b3b5d7
MB
64 shairport_sync_advanced_remote_control_set_volume(shairportSyncAdvancedRemoteControlSkeleton,
65 argc->speaker_volume);
66
67 shairport_sync_remote_control_set_airplay_volume(shairportSyncRemoteControlSkeleton,
68 argc->airplay_volume);
69
ec665d9b 70 shairport_sync_remote_control_set_client(shairportSyncRemoteControlSkeleton, argc->client_ip);
e8b8c6ed
MB
71 shairport_sync_remote_control_set_client_name(shairportSyncRemoteControlSkeleton,
72 argc->client_name);
ec665d9b 73
54d761ff
MB
74 // although it's a DACP server, the server is in fact, part of the the AirPlay "client" (their
75 // term).
69b3b5d7
MB
76 if (argc->dacp_server_active) {
77 shairport_sync_remote_control_set_available(shairportSyncRemoteControlSkeleton, TRUE);
78 } else {
79 shairport_sync_remote_control_set_available(shairportSyncRemoteControlSkeleton, FALSE);
80 }
81
82 if (argc->advanced_dacp_server_active) {
83 shairport_sync_advanced_remote_control_set_available(shairportSyncAdvancedRemoteControlSkeleton,
84 TRUE);
85 } else {
86 shairport_sync_advanced_remote_control_set_available(shairportSyncAdvancedRemoteControlSkeleton,
87 FALSE);
88 }
c2e3fa5a 89
365843fa 90 if (argc->progress_string) {
c2e3fa5a
MB
91 // debug(1, "Check progress string");
92 th = shairport_sync_remote_control_get_progress_string(shairportSyncRemoteControlSkeleton);
93 if ((th == NULL) || (strcasecmp(th, argc->progress_string) != 0)) {
94 // debug(1, "Progress string should be changed");
95 shairport_sync_remote_control_set_progress_string(shairportSyncRemoteControlSkeleton,
96 argc->progress_string);
97 }
365843fa 98 }
69b3b5d7 99
64858a23
MB
100 if (argc->frame_position_string) {
101 // debug(1, "Check frame position string");
102 th = shairport_sync_get_frame_position(shairportSyncSkeleton);
103 if ((th == NULL) || (strcasecmp(th, argc->frame_position_string) != 0)) {
104 // debug(1, "Frame position string should be changed");
40446668 105 shairport_sync_set_frame_position(shairportSyncSkeleton, argc->frame_position_string);
64858a23
MB
106 }
107 }
108
37f060f4
MB
109 if (argc->first_frame_position_string) {
110 // debug(1, "Check first frame position string");
111 th = shairport_sync_get_first_frame_position(shairportSyncSkeleton);
112 if ((th == NULL) || (strcasecmp(th, argc->first_frame_position_string) != 0)) {
113 // debug(1, "First frame position string should be changed");
d6536a8e
MB
114 shairport_sync_set_first_frame_position(shairportSyncSkeleton,
115 argc->first_frame_position_string);
37f060f4
MB
116 }
117 }
118
d67909b8
MB
119 if (argc->stream_type) {
120 // debug(1, "Check stream type");
121 th = shairport_sync_remote_control_get_stream_type(shairportSyncRemoteControlSkeleton);
122 if ((th == NULL) || (strcasecmp(th, argc->stream_type) != 0)) {
123 // debug(1, "Stream type string should be changed");
124 shairport_sync_remote_control_set_stream_type(shairportSyncRemoteControlSkeleton,
e8b8c6ed 125 argc->stream_type);
d67909b8
MB
126 }
127 }
d67909b8 128
69b3b5d7
MB
129 switch (argc->player_state) {
130 case PS_NOT_AVAILABLE:
131 shairport_sync_remote_control_set_player_state(shairportSyncRemoteControlSkeleton,
132 "Not Available");
07b7e64a 133 break;
69b3b5d7
MB
134 case PS_STOPPED:
135 shairport_sync_remote_control_set_player_state(shairportSyncRemoteControlSkeleton, "Stopped");
136 break;
137 case PS_PAUSED:
138 shairport_sync_remote_control_set_player_state(shairportSyncRemoteControlSkeleton, "Paused");
139 break;
140 case PS_PLAYING:
141 shairport_sync_remote_control_set_player_state(shairportSyncRemoteControlSkeleton, "Playing");
142 break;
143 default:
144 debug(1, "This should never happen.");
145 }
146
147 switch (argc->play_status) {
148 case PS_NOT_AVAILABLE:
07b7e64a
MB
149 strcpy(response, "Not Available");
150 break;
69b3b5d7 151 case PS_STOPPED:
07b7e64a 152 strcpy(response, "Stopped");
69b3b5d7
MB
153 break;
154 case PS_PAUSED:
07b7e64a 155 strcpy(response, "Paused");
69b3b5d7
MB
156 break;
157 case PS_PLAYING:
07b7e64a 158 strcpy(response, "Playing");
69b3b5d7
MB
159 break;
160 default:
161 debug(1, "This should never happen.");
162 }
163
07b7e64a
MB
164 th = shairport_sync_advanced_remote_control_get_playback_status(
165 shairportSyncAdvancedRemoteControlSkeleton);
166
167 // only set this if it's different
168 if ((th == NULL) || (strcasecmp(th, response) != 0)) {
169 debug(3, "Playback Status should be changed");
170 shairport_sync_advanced_remote_control_set_playback_status(
171 shairportSyncAdvancedRemoteControlSkeleton, response);
172 }
173
69b3b5d7
MB
174 switch (argc->repeat_status) {
175 case RS_NOT_AVAILABLE:
176 strcpy(response, "Not Available");
177 break;
178 case RS_OFF:
179 strcpy(response, "Off");
180 break;
181 case RS_ONE:
182 strcpy(response, "One");
183 break;
184 case RS_ALL:
185 strcpy(response, "All");
186 break;
187 default:
188 debug(1, "This should never happen.");
189 }
07b7e64a 190 th = shairport_sync_advanced_remote_control_get_loop_status(
69b3b5d7
MB
191 shairportSyncAdvancedRemoteControlSkeleton);
192
193 // only set this if it's different
194 if ((th == NULL) || (strcasecmp(th, response) != 0)) {
07b7e64a 195 debug(3, "Loop Status should be changed");
69b3b5d7
MB
196 shairport_sync_advanced_remote_control_set_loop_status(
197 shairportSyncAdvancedRemoteControlSkeleton, response);
198 }
199
200 switch (argc->shuffle_status) {
201 case SS_NOT_AVAILABLE:
839c778a 202 new_status = FALSE;
69b3b5d7
MB
203 break;
204 case SS_OFF:
839c778a 205 new_status = FALSE;
69b3b5d7
MB
206 break;
207 case SS_ON:
839c778a 208 new_status = TRUE;
69b3b5d7
MB
209 break;
210 default:
839c778a
MB
211 new_status = FALSE;
212 debug(1, "Unknown shuffle status -- this should never happen.");
213 }
ec665d9b 214
839c778a
MB
215 current_status = shairport_sync_advanced_remote_control_get_shuffle(
216 shairportSyncAdvancedRemoteControlSkeleton);
ec665d9b 217
839c778a
MB
218 // only set this if it's different
219 if (current_status != new_status) {
220 debug(3, "Shuffle State should be changed");
54d761ff
MB
221 shairport_sync_advanced_remote_control_set_shuffle(shairportSyncAdvancedRemoteControlSkeleton,
222 new_status);
69b3b5d7
MB
223 }
224
86b6d7bf
MB
225 // Build the metadata array
226 debug(2, "Build metadata");
227 GVariantBuilder *dict_builder = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
69b3b5d7 228
86b6d7bf 229 // Add in the artwork URI if it exists.
69b3b5d7 230 if (argc->cover_art_pathname) {
86b6d7bf 231 GVariant *artUrl = g_variant_new("s", argc->cover_art_pathname);
69b3b5d7
MB
232 g_variant_builder_add(dict_builder, "{sv}", "mpris:artUrl", artUrl);
233 }
234
d67909b8
MB
235 // Add in the Track ID based on the 'mper' metadata if it is valid
236 if (argc->item_id_is_valid != 0) {
69b3b5d7 237 char trackidstring[128];
789ba1ee 238 snprintf(trackidstring, sizeof(trackidstring), "/org/gnome/ShairportSync/%" PRIX64 "",
86b6d7bf 239 argc->item_id);
69b3b5d7
MB
240 GVariant *trackid = g_variant_new("o", trackidstring);
241 g_variant_builder_add(dict_builder, "{sv}", "mpris:trackid", trackid);
242 }
243
d67909b8 244 // Add in the Song Data Kind based on the 'asdk' metadata if it is valid
e8b8c6ed
MB
245 // It seems that this is 0 for a timed play, e.g. a track or an album, but is 1 for an untimed
246 // play, such as a stream.
247
d67909b8
MB
248 if (argc->song_data_kind_is_valid != 0) {
249 GVariant *songdatakind = g_variant_new_uint32(argc->song_data_kind);
250 g_variant_builder_add(dict_builder, "{sv}", "sps:songdatakind", songdatakind);
251 }
252
86b6d7bf
MB
253 // Add the track name if it exists
254 if (argc->track_name) {
255 GVariant *track_name = g_variant_new("s", argc->track_name);
256 g_variant_builder_add(dict_builder, "{sv}", "xesam:title", track_name);
69b3b5d7
MB
257 }
258
86b6d7bf
MB
259 // Add the album name if it exists
260 if (argc->album_name) {
261 GVariant *album_name = g_variant_new("s", argc->album_name);
262 g_variant_builder_add(dict_builder, "{sv}", "xesam:album", album_name);
69b3b5d7
MB
263 }
264
64858a23 265 // Add the artist name list if it exists
86b6d7bf
MB
266 if (argc->artist_name) {
267 GVariantBuilder *artist_as = g_variant_builder_new(G_VARIANT_TYPE("as"));
268 g_variant_builder_add(artist_as, "s", argc->artist_name);
269 GVariant *artists = g_variant_builder_end(artist_as);
270 g_variant_builder_unref(artist_as);
69b3b5d7
MB
271 g_variant_builder_add(dict_builder, "{sv}", "xesam:artist", artists);
272 }
273
64858a23
MB
274 // Add the album artist list if it exists
275 if (argc->album_artist_name) {
276 GVariantBuilder *album_artist_as = g_variant_builder_new(G_VARIANT_TYPE("as"));
277 g_variant_builder_add(album_artist_as, "s", argc->album_artist_name);
278 GVariant *album_artists = g_variant_builder_end(album_artist_as);
279 g_variant_builder_unref(album_artist_as);
280 g_variant_builder_add(dict_builder, "{sv}", "xesam:albumArtist", album_artists);
281 }
282
283 // Add the composer list if it exists
284 if (argc->composer) {
285 GVariantBuilder *composer_as = g_variant_builder_new(G_VARIANT_TYPE("as"));
286 g_variant_builder_add(composer_as, "s", argc->composer);
287 GVariant *composers = g_variant_builder_end(composer_as);
288 g_variant_builder_unref(composer_as);
289 g_variant_builder_add(dict_builder, "{sv}", "xesam:composer", composers);
290 }
291
292 // Add the genre list if it exists
86b6d7bf
MB
293 if (argc->genre) {
294 GVariantBuilder *genre_as = g_variant_builder_new(G_VARIANT_TYPE("as"));
295 g_variant_builder_add(genre_as, "s", argc->genre);
296 GVariant *genre = g_variant_builder_end(genre_as);
297 g_variant_builder_unref(genre_as);
298 g_variant_builder_add(dict_builder, "{sv}", "xesam:genre", genre);
69b3b5d7
MB
299 }
300
d67909b8 301 if (argc->songtime_in_milliseconds_is_valid != 0) {
86b6d7bf
MB
302 uint64_t track_length_in_microseconds = argc->songtime_in_milliseconds;
303 track_length_in_microseconds *= 1000; // to microseconds in 64-bit precision
304 // Make up the track name and album name
d67909b8 305 // debug(1, "Set tracklength to %" PRId64 ".", track_length_in_microseconds);
86b6d7bf
MB
306 GVariant *tracklength = g_variant_new("x", track_length_in_microseconds);
307 g_variant_builder_add(dict_builder, "{sv}", "mpris:length", tracklength);
ec665d9b 308 }
86b6d7bf 309
69b3b5d7
MB
310 GVariant *dict = g_variant_builder_end(dict_builder);
311 g_variant_builder_unref(dict_builder);
69b3b5d7 312 shairport_sync_remote_control_set_metadata(shairportSyncRemoteControlSkeleton, dict);
c0b1dc54
MB
313}
314
8453778a
MB
315static gboolean on_handle_set_volume(ShairportSyncAdvancedRemoteControl *skeleton,
316 GDBusMethodInvocation *invocation, const gint volume,
317 __attribute__((unused)) gpointer user_data) {
86b6d7bf 318 debug(2, "Set volume to %d.", volume);
8453778a
MB
319 dacp_set_volume(volume);
320 shairport_sync_advanced_remote_control_complete_set_volume(skeleton, invocation);
321 return TRUE;
322}
323
49a9cb9d 324static gboolean on_handle_fast_forward(ShairportSyncRemoteControl *skeleton,
d692fab9
MB
325 GDBusMethodInvocation *invocation,
326 __attribute__((unused)) gpointer user_data) {
c0b1dc54 327 send_simple_dacp_command("beginff");
49a9cb9d 328 shairport_sync_remote_control_complete_fast_forward(skeleton, invocation);
c0b1dc54
MB
329 return TRUE;
330}
331
49a9cb9d 332static gboolean on_handle_rewind(ShairportSyncRemoteControl *skeleton,
d692fab9
MB
333 GDBusMethodInvocation *invocation,
334 __attribute__((unused)) gpointer user_data) {
c0b1dc54 335 send_simple_dacp_command("beginrew");
49a9cb9d 336 shairport_sync_remote_control_complete_rewind(skeleton, invocation);
c0b1dc54
MB
337 return TRUE;
338}
339
49a9cb9d 340static gboolean on_handle_toggle_mute(ShairportSyncRemoteControl *skeleton,
d692fab9
MB
341 GDBusMethodInvocation *invocation,
342 __attribute__((unused)) gpointer user_data) {
c0b1dc54 343 send_simple_dacp_command("mutetoggle");
49a9cb9d 344 shairport_sync_remote_control_complete_toggle_mute(skeleton, invocation);
c0b1dc54
MB
345 return TRUE;
346}
347
49a9cb9d 348static gboolean on_handle_next(ShairportSyncRemoteControl *skeleton,
d692fab9 349 GDBusMethodInvocation *invocation,
c0b1dc54
MB
350 __attribute__((unused)) gpointer user_data) {
351 send_simple_dacp_command("nextitem");
49a9cb9d 352 shairport_sync_remote_control_complete_next(skeleton, invocation);
c0b1dc54
MB
353 return TRUE;
354}
355
49a9cb9d 356static gboolean on_handle_previous(ShairportSyncRemoteControl *skeleton,
d692fab9
MB
357 GDBusMethodInvocation *invocation,
358 __attribute__((unused)) gpointer user_data) {
c0b1dc54 359 send_simple_dacp_command("previtem");
49a9cb9d 360 shairport_sync_remote_control_complete_previous(skeleton, invocation);
c0b1dc54
MB
361 return TRUE;
362}
363
49a9cb9d 364static gboolean on_handle_pause(ShairportSyncRemoteControl *skeleton,
d692fab9
MB
365 GDBusMethodInvocation *invocation,
366 __attribute__((unused)) gpointer user_data) {
c0b1dc54 367 send_simple_dacp_command("pause");
49a9cb9d 368 shairport_sync_remote_control_complete_pause(skeleton, invocation);
c0b1dc54
MB
369 return TRUE;
370}
371
49a9cb9d 372static gboolean on_handle_play_pause(ShairportSyncRemoteControl *skeleton,
d692fab9
MB
373 GDBusMethodInvocation *invocation,
374 __attribute__((unused)) gpointer user_data) {
c0b1dc54 375 send_simple_dacp_command("playpause");
49a9cb9d 376 shairport_sync_remote_control_complete_play_pause(skeleton, invocation);
c0b1dc54
MB
377 return TRUE;
378}
379
49a9cb9d 380static gboolean on_handle_play(ShairportSyncRemoteControl *skeleton,
d692fab9 381 GDBusMethodInvocation *invocation,
c0b1dc54
MB
382 __attribute__((unused)) gpointer user_data) {
383 send_simple_dacp_command("play");
49a9cb9d 384 shairport_sync_remote_control_complete_play(skeleton, invocation);
c0b1dc54
MB
385 return TRUE;
386}
387
49a9cb9d 388static gboolean on_handle_stop(ShairportSyncRemoteControl *skeleton,
d692fab9 389 GDBusMethodInvocation *invocation,
c0b1dc54
MB
390 __attribute__((unused)) gpointer user_data) {
391 send_simple_dacp_command("stop");
49a9cb9d 392 shairport_sync_remote_control_complete_stop(skeleton, invocation);
c0b1dc54
MB
393 return TRUE;
394}
395
49a9cb9d 396static gboolean on_handle_resume(ShairportSyncRemoteControl *skeleton,
d692fab9
MB
397 GDBusMethodInvocation *invocation,
398 __attribute__((unused)) gpointer user_data) {
c0b1dc54 399 send_simple_dacp_command("playresume");
49a9cb9d 400 shairport_sync_remote_control_complete_resume(skeleton, invocation);
c0b1dc54
MB
401 return TRUE;
402}
403
49a9cb9d 404static gboolean on_handle_shuffle_songs(ShairportSyncRemoteControl *skeleton,
d692fab9
MB
405 GDBusMethodInvocation *invocation,
406 __attribute__((unused)) gpointer user_data) {
c0b1dc54 407 send_simple_dacp_command("shuffle_songs");
49a9cb9d 408 shairport_sync_remote_control_complete_shuffle_songs(skeleton, invocation);
c0b1dc54
MB
409 return TRUE;
410}
411
49a9cb9d 412static gboolean on_handle_volume_up(ShairportSyncRemoteControl *skeleton,
d692fab9
MB
413 GDBusMethodInvocation *invocation,
414 __attribute__((unused)) gpointer user_data) {
c0b1dc54 415 send_simple_dacp_command("volumeup");
49a9cb9d 416 shairport_sync_remote_control_complete_volume_up(skeleton, invocation);
c0b1dc54
MB
417 return TRUE;
418}
419
49a9cb9d 420static gboolean on_handle_volume_down(ShairportSyncRemoteControl *skeleton,
d692fab9
MB
421 GDBusMethodInvocation *invocation,
422 __attribute__((unused)) gpointer user_data) {
c0b1dc54 423 send_simple_dacp_command("volumedown");
49a9cb9d 424 shairport_sync_remote_control_complete_volume_down(skeleton, invocation);
c0b1dc54
MB
425 return TRUE;
426}
427
fe8198a2 428static gboolean on_handle_set_airplay_volume(ShairportSyncRemoteControl *skeleton,
54d761ff
MB
429 GDBusMethodInvocation *invocation,
430 const gdouble volume,
431 __attribute__((unused)) gpointer user_data) {
fe8198a2
MB
432 debug(2, "Set airplay volume to %.6f.", volume);
433 char command[256] = "";
434 snprintf(command, sizeof(command), "setproperty?dmcp.device-volume=%.6f", volume);
435 send_simple_dacp_command(command);
436 shairport_sync_remote_control_complete_set_airplay_volume(skeleton, invocation);
437 return TRUE;
438}
439
c0b1dc54 440gboolean notify_elapsed_time_callback(ShairportSyncDiagnostics *skeleton,
d692fab9 441 __attribute__((unused)) gpointer user_data) {
c0b1dc54
MB
442 // debug(1, "\"notify_elapsed_time_callback\" called.");
443 if (shairport_sync_diagnostics_get_elapsed_time(skeleton)) {
444 config.debugger_show_elapsed_time = 1;
445 debug(1, ">> start including elapsed time in logs");
446 } else {
447 config.debugger_show_elapsed_time = 0;
448 debug(1, ">> stop including elapsed time in logs");
449 }
450 return TRUE;
451}
452
453gboolean notify_delta_time_callback(ShairportSyncDiagnostics *skeleton,
d692fab9 454 __attribute__((unused)) gpointer user_data) {
c0b1dc54
MB
455 // debug(1, "\"notify_delta_time_callback\" called.");
456 if (shairport_sync_diagnostics_get_delta_time(skeleton)) {
457 config.debugger_show_relative_time = 1;
458 debug(1, ">> start including delta time in logs");
459 } else {
460 config.debugger_show_relative_time = 0;
461 debug(1, ">> stop including delta time in logs");
462 }
463 return TRUE;
464}
465
db8f10cf 466gboolean notify_file_and_line_callback(ShairportSyncDiagnostics *skeleton,
54d761ff 467 __attribute__((unused)) gpointer user_data) {
db8f10cf
MB
468 // debug(1, "\"notify_file_and_line_callback\" called.");
469 if (shairport_sync_diagnostics_get_file_and_line(skeleton)) {
470 config.debugger_show_file_and_line = 1;
471 debug(1, ">> start including file and line in logs");
472 } else {
473 config.debugger_show_file_and_line = 0;
474 debug(1, ">> stop including file and line in logs");
475 }
476 return TRUE;
477}
478
c0b1dc54 479gboolean notify_statistics_callback(ShairportSyncDiagnostics *skeleton,
d692fab9 480 __attribute__((unused)) gpointer user_data) {
c0b1dc54
MB
481 // debug(1, "\"notify_statistics_callback\" called.");
482 if (shairport_sync_diagnostics_get_statistics(skeleton)) {
483 debug(1, ">> start logging statistics");
e3faba14
MB
484 if (config.statistics_requested == 0)
485 statistics_row = 0; // redraw the header line
c0b1dc54
MB
486 config.statistics_requested = 1;
487 } else {
488 debug(1, ">> stop logging statistics");
489 config.statistics_requested = 0;
490 }
491 return TRUE;
492}
493
494gboolean notify_verbosity_callback(ShairportSyncDiagnostics *skeleton,
d692fab9 495 __attribute__((unused)) gpointer user_data) {
c0b1dc54
MB
496 gint th = shairport_sync_diagnostics_get_verbosity(skeleton);
497 if ((th >= 0) && (th <= 3)) {
d692fab9 498 if (th == 0)
c0b1dc54 499 debug(1, ">> log verbosity set to %d.", th);
e3faba14
MB
500 if (((debuglev == 0) && (th != 0)) || ((debuglev != 0) && (th == 0)))
501 statistics_row = 0; // if the debug level changes, redraw the header line
c0b1dc54
MB
502 debuglev = th;
503 debug(1, ">> log verbosity set to %d.", th);
504 } else {
505 debug(1, ">> invalid log verbosity: %d. Ignored.", th);
cbbe84d8 506 shairport_sync_diagnostics_set_verbosity(skeleton, debuglev);
c0b1dc54
MB
507 }
508 return TRUE;
c025bf00
MB
509}
510
a74261bb 511gboolean notify_disable_standby_callback(ShairportSync *skeleton,
2e442853 512 __attribute__((unused)) gpointer user_data) {
a74261bb
MB
513 // debug(1, "\"notify_disable_standby_callback\" called.");
514 if (shairport_sync_get_disable_standby(skeleton)) {
515 debug(1, ">> activating disable standby");
516 config.keep_dac_busy = 1;
517 } else {
518 debug(1, ">> deactivating disable standby");
519 config.keep_dac_busy = 0;
520 }
521 return TRUE;
522}
523
2f2442f4
MB
524#ifdef CONFIG_CONVOLUTION
525gboolean notify_convolution_callback(ShairportSync *skeleton,
54d761ff 526 __attribute__((unused)) gpointer user_data) {
2f2442f4
MB
527 // debug(1, "\"notify_convolution_callback\" called.");
528 if (shairport_sync_get_convolution(skeleton)) {
529 debug(1, ">> activating convolution");
530 config.convolution = 1;
54d761ff
MB
531 config.convolver_valid =
532 convolver_init(config.convolution_ir_file, config.convolution_max_length);
2f2442f4
MB
533 } else {
534 debug(1, ">> deactivating convolution");
535 config.convolution = 0;
536 }
537 return TRUE;
538}
539#else
540gboolean notify_convolution_callback(__attribute__((unused)) ShairportSync *skeleton,
54d761ff 541 __attribute__((unused)) gpointer user_data) {
2f2442f4
MB
542 warn(">> Convolution support is not built in to this build of Shairport Sync.");
543 return TRUE;
544}
545#endif
546
547#ifdef CONFIG_CONVOLUTION
548gboolean notify_convolution_gain_callback(ShairportSync *skeleton,
54d761ff 549 __attribute__((unused)) gpointer user_data) {
2f2442f4
MB
550
551 gdouble th = shairport_sync_get_convolution_gain(skeleton);
552 if ((th <= 0.0) && (th >= -100.0)) {
553 debug(1, ">> setting convolution gain to %f.", th);
554 config.convolution_gain = th;
555 } else {
556 debug(1, ">> invalid convolution gain: %f. Ignored.", th);
557 shairport_sync_set_convolution_gain(skeleton, config.convolution_gain);
558 }
559 return TRUE;
560}
561#else
562gboolean notify_convolution_gain_callback(__attribute__((unused)) ShairportSync *skeleton,
54d761ff 563 __attribute__((unused)) gpointer user_data) {
2f2442f4
MB
564 warn(">> Convolution support is not built in to this build of Shairport Sync.");
565 return TRUE;
566}
567#endif
568#ifdef CONFIG_CONVOLUTION
569gboolean notify_convolution_impulse_response_file_callback(ShairportSync *skeleton,
54d761ff
MB
570 __attribute__((unused))
571 gpointer user_data) {
2f2442f4
MB
572 char *th = (char *)shairport_sync_get_convolution_impulse_response_file(skeleton);
573 if (config.convolution_ir_file)
574 free(config.convolution_ir_file);
575 config.convolution_ir_file = strdup(th);
54d761ff
MB
576 debug(1, ">> setting configuration impulse response filter file to \"%s\".",
577 config.convolution_ir_file);
578 config.convolver_valid =
579 convolver_init(config.convolution_ir_file, config.convolution_max_length);
2f2442f4
MB
580 return TRUE;
581}
582#else
54d761ff
MB
583gboolean notify_convolution_impulse_response_file_callback(__attribute__((unused))
584 ShairportSync *skeleton,
585 __attribute__((unused))
586 gpointer user_data) {
587 __attribute__((unused)) char *th =
588 (char *)shairport_sync_get_convolution_impulse_response_file(skeleton);
2f2442f4
MB
589 return TRUE;
590}
591#endif
592
2f2442f4 593gboolean notify_loudness_callback(ShairportSync *skeleton,
54d761ff 594 __attribute__((unused)) gpointer user_data) {
2f2442f4
MB
595 // debug(1, "\"notify_loudness_callback\" called.");
596 if (shairport_sync_get_loudness(skeleton)) {
597 debug(1, ">> activating loudness");
d4eae76f
MB
598 config.loudness = 1;
599 } else {
2f2442f4 600 debug(1, ">> deactivating loudness");
d4eae76f
MB
601 config.loudness = 0;
602 }
603 return TRUE;
604}
605
8991f342
MB
606gboolean notify_loudness_threshold_callback(ShairportSync *skeleton,
607 __attribute__((unused)) gpointer user_data) {
d4eae76f
MB
608 gdouble th = shairport_sync_get_loudness_threshold(skeleton);
609 if ((th <= 0.0) && (th >= -100.0)) {
b9cf91b2 610 debug(1, ">> setting loudness threshold to %f.", th);
d4eae76f
MB
611 config.loudness_reference_volume_db = th;
612 } else {
b9cf91b2 613 debug(1, ">> invalid loudness threshold: %f. Ignored.", th);
cbbe84d8 614 shairport_sync_set_loudness_threshold(skeleton, config.loudness_reference_volume_db);
d4eae76f
MB
615 }
616 return TRUE;
617}
618
df18e3a0 619gboolean notify_drift_tolerance_callback(ShairportSync *skeleton,
5141e2f5 620 __attribute__((unused)) gpointer user_data) {
df18e3a0
MB
621 gdouble dt = shairport_sync_get_drift_tolerance(skeleton);
622 if ((dt >= 0.0) && (dt <= 2.0)) {
8b7e844e 623 debug(1, ">> setting drift tolerance to %f seconds.", dt);
df18e3a0
MB
624 config.tolerance = dt;
625 } else {
a74261bb 626 debug(1, ">> invalid drift tolerance: %f seconds. Ignored.", dt);
df18e3a0
MB
627 shairport_sync_set_drift_tolerance(skeleton, config.tolerance);
628 }
629 return TRUE;
630}
631
8b7e844e 632gboolean notify_volume_callback(ShairportSync *skeleton,
fd880056 633 __attribute__((unused)) gpointer user_data) {
8b7e844e
MB
634 gdouble iv = shairport_sync_get_volume(skeleton);
635 if (((iv >= -30.0) && (iv <= 0.0)) || (iv == -144.0)) {
05dfcdfd 636 debug(2, ">> setting volume to %7.4f.", iv);
fd880056 637
986f9587
MB
638 pthread_cleanup_debug_mutex_lock(&principal_conn_lock, 100000, 1);
639
640 if (principal_conn != NULL) {
641 player_volume(iv, principal_conn);
642 principal_conn->own_airplay_volume = iv;
643 principal_conn->own_airplay_volume_set = 1;
8201903a 644 }
986f9587 645 pthread_cleanup_pop(1); // release the principal_conn lock
8201903a
MB
646 config.airplay_volume = iv;
647 config.last_access_to_volume_info_time = get_absolute_time_in_ns();
8b7e844e
MB
648 } else {
649 debug(1, ">> invalid volume: %f. Ignored.", iv);
650 shairport_sync_set_volume(skeleton, config.airplay_volume);
651 }
652 return TRUE;
653}
654
34351a99 655gboolean notify_disable_standby_mode_callback(ShairportSync *skeleton,
c8b0be30 656 __attribute__((unused)) gpointer user_data) {
34351a99 657 char *th = (char *)shairport_sync_get_disable_standby_mode(skeleton);
c8b0be30
MB
658 if ((strcasecmp(th, "no") == 0) || (strcasecmp(th, "off") == 0) ||
659 (strcasecmp(th, "never") == 0)) {
34351a99
MB
660 config.disable_standby_mode = disable_standby_off;
661 config.keep_dac_busy = 0;
c8b0be30
MB
662 } else if ((strcasecmp(th, "yes") == 0) || (strcasecmp(th, "on") == 0) ||
663 (strcasecmp(th, "always") == 0)) {
34351a99
MB
664 config.disable_standby_mode = disable_standby_always;
665 config.keep_dac_busy = 1;
83c0405d
MB
666 } else if (strcasecmp(th, "auto") == 0)
667 config.disable_standby_mode = disable_standby_auto;
34351a99
MB
668 else {
669 warn("An unrecognised disable_standby_mode: \"%s\" was requested via D-Bus interface.", th);
670 switch (config.disable_standby_mode) {
c8b0be30
MB
671 case disable_standby_off:
672 shairport_sync_set_disable_standby_mode(skeleton, "off");
673 break;
674 case disable_standby_always:
675 shairport_sync_set_disable_standby_mode(skeleton, "always");
676 break;
677 case disable_standby_auto:
678 shairport_sync_set_disable_standby_mode(skeleton, "auto");
679 break;
680 default:
681 break;
34351a99
MB
682 }
683 }
684 return TRUE;
685}
686
6dc30c6c 687gboolean notify_alacdecoder_callback(ShairportSync *skeleton,
340e365a
MB
688 __attribute__((unused)) gpointer user_data) {
689 char *th = (char *)shairport_sync_get_alacdecoder(skeleton);
c9b3d2a2 690#ifdef CONFIG_APPLE_ALAC
340e365a 691 if (strcasecmp(th, "hammerton") == 0)
6dc30c6c 692 config.use_apple_decoder = 0;
340e365a 693 else if (strcasecmp(th, "apple") == 0)
6dc30c6c 694 config.use_apple_decoder = 1;
cbbe84d8
MB
695 else {
696 warn("An unrecognised ALAC decoder: \"%s\" was requested via D-Bus interface.", th);
697 if (config.use_apple_decoder == 0)
698 shairport_sync_set_alacdecoder(skeleton, "hammerton");
699 else
700 shairport_sync_set_alacdecoder(skeleton, "apple");
701 }
340e365a 702// debug(1,"Using the %s ALAC decoder.", ((config.use_apple_decoder==0) ? "Hammerton" : "Apple"));
6dc30c6c 703#else
340e365a 704 if (strcasecmp(th, "hammerton") == 0) {
6dc30c6c
MB
705 config.use_apple_decoder = 0;
706 // debug(1,"Using the Hammerton ALAC decoder.");
cbbe84d8
MB
707 } else {
708 warn("An unrecognised ALAC decoder: \"%s\" was requested via D-Bus interface. (Possibly "
709 "support for this decoder was not compiled "
710 "into this version of Shairport Sync.)",
340e365a 711 th);
cbbe84d8
MB
712 shairport_sync_set_alacdecoder(skeleton, "hammerton");
713 }
6dc30c6c
MB
714#endif
715 return TRUE;
716}
717
718gboolean notify_interpolation_callback(ShairportSync *skeleton,
340e365a
MB
719 __attribute__((unused)) gpointer user_data) {
720 char *th = (char *)shairport_sync_get_interpolation(skeleton);
c9b3d2a2 721#ifdef CONFIG_SOXR
340e365a 722 if (strcasecmp(th, "basic") == 0)
6dc30c6c 723 config.packet_stuffing = ST_basic;
340e365a 724 else if (strcasecmp(th, "soxr") == 0)
6dc30c6c 725 config.packet_stuffing = ST_soxr;
848467a8
MB
726 else if (strcasecmp(th, "auto") == 0)
727 config.packet_stuffing = ST_auto;
cbbe84d8
MB
728 else {
729 warn("An unrecognised interpolation method: \"%s\" was requested via the D-Bus interface.", th);
730 switch (config.packet_stuffing) {
731 case ST_basic:
732 shairport_sync_set_interpolation(skeleton, "basic");
733 break;
734 case ST_soxr:
735 shairport_sync_set_interpolation(skeleton, "soxr");
736 break;
848467a8
MB
737 case ST_auto:
738 shairport_sync_set_interpolation(skeleton, "auto");
739 break;
cbbe84d8
MB
740 default:
741 debug(1, "This should never happen!");
742 shairport_sync_set_interpolation(skeleton, "basic");
743 break;
744 }
745 }
6dc30c6c 746#else
340e365a 747 if (strcasecmp(th, "basic") == 0)
6dc30c6c 748 config.packet_stuffing = ST_basic;
cbbe84d8
MB
749 else {
750 warn("An unrecognised interpolation method: \"%s\" was requested via the D-Bus interface. "
751 "(Possibly support for this method was not compiled "
752 "into this version of Shairport Sync.)",
753 th);
754 shairport_sync_set_interpolation(skeleton, "basic");
755 }
6dc30c6c 756#endif
6dc30c6c
MB
757 return TRUE;
758}
759
760gboolean notify_volume_control_profile_callback(ShairportSync *skeleton,
340e365a
MB
761 __attribute__((unused)) gpointer user_data) {
762 char *th = (char *)shairport_sync_get_volume_control_profile(skeleton);
cbbe84d8
MB
763 // enum volume_control_profile_type previous_volume_control_profile =
764 // config.volume_control_profile;
340e365a 765 if (strcasecmp(th, "standard") == 0)
6dc30c6c 766 config.volume_control_profile = VCP_standard;
340e365a 767 else if (strcasecmp(th, "flat") == 0)
6dc30c6c 768 config.volume_control_profile = VCP_flat;
ebe1ca17 769 else if (strcasecmp(th, "dasl_tapered") == 0)
770 config.volume_control_profile = VCP_dasl_tapered;
cbbe84d8 771 else {
340e365a 772 warn("Unrecognised Volume Control Profile: \"%s\".", th);
cbbe84d8
MB
773 switch (config.volume_control_profile) {
774 case VCP_standard:
775 shairport_sync_set_volume_control_profile(skeleton, "standard");
776 break;
777 case VCP_flat:
778 shairport_sync_set_volume_control_profile(skeleton, "flat");
779 break;
ebe1ca17 780 case VCP_dasl_tapered:
781 shairport_sync_set_volume_control_profile(skeleton, "dasl_tapered");
baf51cf2 782 break;
cbbe84d8
MB
783 default:
784 debug(1, "This should never happen!");
785 shairport_sync_set_volume_control_profile(skeleton, "standard");
786 break;
787 }
788 }
789 return TRUE;
790}
791
792gboolean notify_shuffle_callback(ShairportSyncAdvancedRemoteControl *skeleton,
793 __attribute__((unused)) gpointer user_data) {
794 // debug(1,"notify_shuffle_callback called");
795 if (shairport_sync_advanced_remote_control_get_shuffle(skeleton))
796 send_simple_dacp_command("setproperty?dacp.shufflestate=1");
797 else
798 send_simple_dacp_command("setproperty?dacp.shufflestate=0");
cbbe84d8
MB
799 return TRUE;
800}
801
802gboolean notify_loop_status_callback(ShairportSyncAdvancedRemoteControl *skeleton,
803 __attribute__((unused)) gpointer user_data) {
804 // debug(1,"notify_loop_status_callback called");
805 char *th = (char *)shairport_sync_advanced_remote_control_get_loop_status(skeleton);
806 // enum volume_control_profile_type previous_volume_control_profile =
807 // config.volume_control_profile;
8bf83c14 808 // debug(1, "notify_loop_status_callback called with loop status of \"%s\".", th);
cbbe84d8
MB
809 if (strcasecmp(th, "off") == 0)
810 send_simple_dacp_command("setproperty?dacp.repeatstate=0");
811 else if (strcasecmp(th, "one") == 0)
812 send_simple_dacp_command("setproperty?dacp.repeatstate=1");
813 else if (strcasecmp(th, "all") == 0)
814 send_simple_dacp_command("setproperty?dacp.repeatstate=2");
815 else if (strcasecmp(th, "not available") != 0) {
816 warn("Illegal Loop Request: \"%s\".", th);
817 switch (metadata_store.repeat_status) {
818 case RS_NOT_AVAILABLE:
819 shairport_sync_advanced_remote_control_set_loop_status(skeleton, "Not Available");
820 break;
821 case RS_OFF:
822 shairport_sync_advanced_remote_control_set_loop_status(skeleton, "Off");
823 break;
824 case RS_ONE:
825 shairport_sync_advanced_remote_control_set_loop_status(skeleton, "One");
826 break;
827 case RS_ALL:
828 shairport_sync_advanced_remote_control_set_loop_status(skeleton, "All");
829 break;
830 default:
831 debug(1, "This should never happen!");
832 shairport_sync_advanced_remote_control_set_loop_status(skeleton, "Off");
833 break;
834 }
835 }
6dc30c6c
MB
836 return TRUE;
837}
838
a4eaace7 839static gboolean on_handle_quit(ShairportSync *skeleton, GDBusMethodInvocation *invocation,
c8c70b60
MB
840 __attribute__((unused)) const gchar *command,
841 __attribute__((unused)) gpointer user_data) {
4963c65a
MB
842 debug(1, ">> quit requested");
843 type_of_exit_cleanup = TOE_dbus; // request an exit cleanup that is compatible with dbus
844 exit(EXIT_SUCCESS);
a4eaace7
MB
845 shairport_sync_complete_quit(skeleton, invocation);
846 return TRUE;
847}
848
d4eae76f 849static gboolean on_handle_remote_command(ShairportSync *skeleton, GDBusMethodInvocation *invocation,
8991f342
MB
850 const gchar *command,
851 __attribute__((unused)) gpointer user_data) {
ec665d9b 852 debug(1, "RemoteCommand with command \"%s\".", command);
fe8198a2 853 int reply = 0;
ec665d9b 854 char *client_reply = NULL;
fe8198a2 855 ssize_t reply_size = 0;
ec665d9b
MB
856 reply = dacp_send_command((const char *)command, &client_reply, &reply_size);
857 char *client_reply_hex = alloca(reply_size * 2 + 1);
858 if (client_reply_hex) {
859 char *p = client_reply_hex;
860 if (client_reply) {
861 char *q = client_reply;
fe8198a2
MB
862 int i;
863 for (i = 0; i < reply_size; i++) {
864 snprintf(p, 3, "%02X", *q);
865 p += 2;
866 q++;
867 }
868 }
869 *p = '\0';
870 }
ec665d9b 871 shairport_sync_complete_remote_command(skeleton, invocation, reply, client_reply_hex);
d4eae76f
MB
872 return TRUE;
873}
874
fd880056 875static gboolean on_handle_drop_session(ShairportSync *skeleton, GDBusMethodInvocation *invocation,
b81fb39a 876 __attribute__((unused)) gpointer user_data) {
b81fb39a
MB
877 get_play_lock(NULL, 1); // stop any current session and don't replace it
878 shairport_sync_complete_drop_session(skeleton, invocation);
879 return TRUE;
880}
881
64858a23 882static gboolean on_handle_set_frame_position_update_interval(ShairportSync *skeleton,
40446668
MB
883 GDBusMethodInvocation *invocation,
884 const gdouble seconds,
885 __attribute__((unused))
886 gpointer user_data) {
64858a23
MB
887 debug(1, ">> set frame position update interval to %.6f.", seconds);
888 config.metadata_progress_interval = seconds;
889 shairport_sync_complete_set_frame_position_update_interval(skeleton, invocation);
890 return TRUE;
891}
892
d692fab9 893static void on_dbus_name_acquired(GDBusConnection *connection, const gchar *name,
8453778a 894 __attribute__((unused)) gpointer user_data) {
d4eae76f 895
c36a7822
MB
896 // debug(1, "Shairport Sync native D-Bus interface \"%s\" acquired on the %s bus.", name,
897 // (config.dbus_service_bus_type == DBT_session) ? "session" : "system");
03aefda0 898
74f41a17 899 shairportSyncSkeleton = shairport_sync_skeleton_new();
74f41a17 900 g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(shairportSyncSkeleton), connection,
d4eae76f
MB
901 "/org/gnome/ShairportSync", NULL);
902
03aefda0
MB
903 shairportSyncDiagnosticsSkeleton = shairport_sync_diagnostics_skeleton_new();
904 g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(shairportSyncDiagnosticsSkeleton),
905 connection, "/org/gnome/ShairportSync", NULL);
906
907 shairportSyncRemoteControlSkeleton = shairport_sync_remote_control_skeleton_new();
908 g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(shairportSyncRemoteControlSkeleton),
909 connection, "/org/gnome/ShairportSync", NULL);
910
911 shairportSyncAdvancedRemoteControlSkeleton =
912 shairport_sync_advanced_remote_control_skeleton_new();
69b3b5d7 913
03aefda0
MB
914 g_dbus_interface_skeleton_export(
915 G_DBUS_INTERFACE_SKELETON(shairportSyncAdvancedRemoteControlSkeleton), connection,
916 "/org/gnome/ShairportSync", NULL);
917
918 g_signal_connect(shairportSyncSkeleton, "notify::interpolation",
919 G_CALLBACK(notify_interpolation_callback), NULL);
920 g_signal_connect(shairportSyncSkeleton, "notify::alacdecoder",
921 G_CALLBACK(notify_alacdecoder_callback), NULL);
34351a99
MB
922 g_signal_connect(shairportSyncSkeleton, "notify::disable-standby-mode",
923 G_CALLBACK(notify_disable_standby_mode_callback), NULL);
c8b0be30 924 g_signal_connect(shairportSyncSkeleton, "notify::volume-control-profile",
03aefda0 925 G_CALLBACK(notify_volume_control_profile_callback), NULL);
2e442853 926 g_signal_connect(shairportSyncSkeleton, "notify::disable-standby",
a74261bb 927 G_CALLBACK(notify_disable_standby_callback), NULL);
2f2442f4
MB
928 g_signal_connect(shairportSyncSkeleton, "notify::convolution",
929 G_CALLBACK(notify_convolution_callback), NULL);
930 g_signal_connect(shairportSyncSkeleton, "notify::convolution-gain",
931 G_CALLBACK(notify_convolution_gain_callback), NULL);
932 g_signal_connect(shairportSyncSkeleton, "notify::convolution-impulse-response-file",
933 G_CALLBACK(notify_convolution_impulse_response_file_callback), NULL);
54d761ff
MB
934 g_signal_connect(shairportSyncSkeleton, "notify::loudness", G_CALLBACK(notify_loudness_callback),
935 NULL);
03aefda0
MB
936 g_signal_connect(shairportSyncSkeleton, "notify::loudness-threshold",
937 G_CALLBACK(notify_loudness_threshold_callback), NULL);
df18e3a0
MB
938 g_signal_connect(shairportSyncSkeleton, "notify::drift-tolerance",
939 G_CALLBACK(notify_drift_tolerance_callback), NULL);
fd880056
MB
940 g_signal_connect(shairportSyncSkeleton, "notify::volume", G_CALLBACK(notify_volume_callback),
941 NULL);
03aefda0 942
c8c70b60 943 g_signal_connect(shairportSyncSkeleton, "handle-quit", G_CALLBACK(on_handle_quit), NULL);
a4eaace7 944
03aefda0
MB
945 g_signal_connect(shairportSyncSkeleton, "handle-remote-command",
946 G_CALLBACK(on_handle_remote_command), NULL);
947
fd880056
MB
948 g_signal_connect(shairportSyncSkeleton, "handle-drop-session", G_CALLBACK(on_handle_drop_session),
949 NULL);
b81fb39a 950
64858a23 951 g_signal_connect(shairportSyncSkeleton, "handle-set-frame-position-update-interval",
40446668 952 G_CALLBACK(on_handle_set_frame_position_update_interval), NULL);
64858a23 953
03aefda0
MB
954 g_signal_connect(shairportSyncDiagnosticsSkeleton, "notify::verbosity",
955 G_CALLBACK(notify_verbosity_callback), NULL);
956
957 g_signal_connect(shairportSyncDiagnosticsSkeleton, "notify::statistics",
958 G_CALLBACK(notify_statistics_callback), NULL);
959
960 g_signal_connect(shairportSyncDiagnosticsSkeleton, "notify::elapsed-time",
961 G_CALLBACK(notify_elapsed_time_callback), NULL);
962
963 g_signal_connect(shairportSyncDiagnosticsSkeleton, "notify::delta-time",
964 G_CALLBACK(notify_delta_time_callback), NULL);
965
db8f10cf
MB
966 g_signal_connect(shairportSyncDiagnosticsSkeleton, "notify::file-and-line",
967 G_CALLBACK(notify_file_and_line_callback), NULL);
968
03aefda0
MB
969 g_signal_connect(shairportSyncRemoteControlSkeleton, "handle-fast-forward",
970 G_CALLBACK(on_handle_fast_forward), NULL);
971 g_signal_connect(shairportSyncRemoteControlSkeleton, "handle-rewind",
972 G_CALLBACK(on_handle_rewind), NULL);
973 g_signal_connect(shairportSyncRemoteControlSkeleton, "handle-toggle-mute",
974 G_CALLBACK(on_handle_toggle_mute), NULL);
975 g_signal_connect(shairportSyncRemoteControlSkeleton, "handle-next", G_CALLBACK(on_handle_next),
976 NULL);
977 g_signal_connect(shairportSyncRemoteControlSkeleton, "handle-previous",
978 G_CALLBACK(on_handle_previous), NULL);
979 g_signal_connect(shairportSyncRemoteControlSkeleton, "handle-pause", G_CALLBACK(on_handle_pause),
980 NULL);
981 g_signal_connect(shairportSyncRemoteControlSkeleton, "handle-play-pause",
982 G_CALLBACK(on_handle_play_pause), NULL);
983 g_signal_connect(shairportSyncRemoteControlSkeleton, "handle-play", G_CALLBACK(on_handle_play),
984 NULL);
985 g_signal_connect(shairportSyncRemoteControlSkeleton, "handle-stop", G_CALLBACK(on_handle_stop),
986 NULL);
987 g_signal_connect(shairportSyncRemoteControlSkeleton, "handle-resume",
988 G_CALLBACK(on_handle_resume), NULL);
989 g_signal_connect(shairportSyncRemoteControlSkeleton, "handle-shuffle-songs",
990 G_CALLBACK(on_handle_shuffle_songs), NULL);
991 g_signal_connect(shairportSyncRemoteControlSkeleton, "handle-volume-up",
992 G_CALLBACK(on_handle_volume_up), NULL);
993 g_signal_connect(shairportSyncRemoteControlSkeleton, "handle-volume-down",
994 G_CALLBACK(on_handle_volume_down), NULL);
fe8198a2
MB
995 g_signal_connect(shairportSyncRemoteControlSkeleton, "handle-set-airplay-volume",
996 G_CALLBACK(on_handle_set_airplay_volume), NULL);
997
03aefda0
MB
998 g_signal_connect(shairportSyncAdvancedRemoteControlSkeleton, "handle-set-volume",
999 G_CALLBACK(on_handle_set_volume), NULL);
1000
cbbe84d8
MB
1001 g_signal_connect(shairportSyncAdvancedRemoteControlSkeleton, "notify::shuffle",
1002 G_CALLBACK(notify_shuffle_callback), NULL);
1003
1004 g_signal_connect(shairportSyncAdvancedRemoteControlSkeleton, "notify::loop-status",
1005 G_CALLBACK(notify_loop_status_callback), NULL);
1006
03aefda0 1007 add_metadata_watcher(dbus_metadata_watcher, NULL);
69b3b5d7 1008
74f41a17 1009 shairport_sync_set_loudness_threshold(SHAIRPORT_SYNC(shairportSyncSkeleton),
d4eae76f 1010 config.loudness_reference_volume_db);
5141e2f5 1011 shairport_sync_set_drift_tolerance(SHAIRPORT_SYNC(shairportSyncSkeleton), config.tolerance);
8b7e844e 1012 shairport_sync_set_volume(SHAIRPORT_SYNC(shairportSyncSkeleton), config.airplay_volume);
40446668 1013
c9b3d2a2 1014#ifdef CONFIG_APPLE_ALAC
4590b08e 1015 if (config.use_apple_decoder == 0) {
340e365a 1016 shairport_sync_set_alacdecoder(SHAIRPORT_SYNC(shairportSyncSkeleton), "hammerton");
4590b08e
MB
1017 debug(1, ">> ALACDecoder set to \"hammerton\"");
1018 } else {
340e365a 1019 shairport_sync_set_alacdecoder(SHAIRPORT_SYNC(shairportSyncSkeleton), "apple");
4590b08e
MB
1020 debug(1, ">> ALACDecoder set to \"apple\"");
1021 }
6dc30c6c 1022#else
340e365a 1023 shairport_sync_set_alacdecoder(SHAIRPORT_SYNC(shairportSyncSkeleton), "hammerton");
4590b08e 1024 debug(1, ">> ALACDecoder set to \"hammerton\"");
5141e2f5 1025
6dc30c6c
MB
1026#endif
1027
34351a99
MB
1028 shairport_sync_set_active(SHAIRPORT_SYNC(shairportSyncSkeleton), FALSE);
1029 debug(1, ">> Active set to \"false\"");
1030
1031 switch (config.disable_standby_mode) {
c8b0be30
MB
1032 case disable_standby_off:
1033 shairport_sync_set_disable_standby_mode(SHAIRPORT_SYNC(shairportSyncSkeleton), "off");
1034 debug(1, ">> disable standby mode set to \"off\"");
1035 break;
1036 case disable_standby_always:
1037 shairport_sync_set_disable_standby_mode(SHAIRPORT_SYNC(shairportSyncSkeleton), "always");
1038 debug(1, ">> disable standby mode set to \"always\"");
1039 break;
1040 case disable_standby_auto:
1041 shairport_sync_set_disable_standby_mode(SHAIRPORT_SYNC(shairportSyncSkeleton), "auto");
1042 debug(1, ">> disable standby mode set to \"auto\"");
1043 break;
1044 default:
1045 debug(1, "invalid disable_standby mode!");
1046 break;
34351a99
MB
1047 }
1048
c9b3d2a2 1049#ifdef CONFIG_SOXR
4590b08e 1050 if (config.packet_stuffing == ST_basic) {
340e365a 1051 shairport_sync_set_interpolation(SHAIRPORT_SYNC(shairportSyncSkeleton), "basic");
4590b08e 1052 debug(1, ">> interpolation set to \"basic\" (soxr support built in)");
848467a8
MB
1053 } else if (config.packet_stuffing == ST_auto) {
1054 shairport_sync_set_interpolation(SHAIRPORT_SYNC(shairportSyncSkeleton), "auto");
1055 debug(1, ">> interpolation set to \"auto\" (soxr support built in)");
4590b08e 1056 } else {
340e365a 1057 shairport_sync_set_interpolation(SHAIRPORT_SYNC(shairportSyncSkeleton), "soxr");
4590b08e
MB
1058 debug(1, ">> interpolation set to \"soxr\"");
1059 }
6dc30c6c 1060#else
848467a8
MB
1061 if (config.packet_stuffing == ST_basic) {
1062 shairport_sync_set_interpolation(SHAIRPORT_SYNC(shairportSyncSkeleton), "basic");
1063 debug(1, ">> interpolation set to \"basic\" (no soxr support)");
1064 } else if (config.packet_stuffing == ST_auto) {
1065 shairport_sync_set_interpolation(SHAIRPORT_SYNC(shairportSyncSkeleton), "auto");
1066 debug(1, ">> interpolation set to \"auto\" (no soxr support)");
1067 }
6dc30c6c
MB
1068#endif
1069
340e365a
MB
1070 if (config.volume_control_profile == VCP_standard)
1071 shairport_sync_set_volume_control_profile(SHAIRPORT_SYNC(shairportSyncSkeleton), "standard");
ebe1ca17 1072 else if (config.volume_control_profile == VCP_dasl_tapered)
986f9587
MB
1073 shairport_sync_set_volume_control_profile(SHAIRPORT_SYNC(shairportSyncSkeleton),
1074 "dasl_tapered");
6dc30c6c 1075 else
340e365a
MB
1076 shairport_sync_set_volume_control_profile(SHAIRPORT_SYNC(shairportSyncSkeleton), "flat");
1077
a74261bb
MB
1078 if (config.keep_dac_busy == 0) {
1079 shairport_sync_set_disable_standby(SHAIRPORT_SYNC(shairportSyncSkeleton), FALSE);
1080 } else {
1081 shairport_sync_set_disable_standby(SHAIRPORT_SYNC(shairportSyncSkeleton), TRUE);
1082 }
1083
d4eae76f 1084 if (config.loudness == 0) {
2f2442f4 1085 shairport_sync_set_loudness(SHAIRPORT_SYNC(shairportSyncSkeleton), FALSE);
d4eae76f 1086 } else {
2f2442f4 1087 shairport_sync_set_loudness(SHAIRPORT_SYNC(shairportSyncSkeleton), TRUE);
d4eae76f 1088 }
340e365a 1089
2f2442f4
MB
1090#ifdef CONFIG_CONVOLUTION
1091 if (config.convolution == 0) {
1092 shairport_sync_set_convolution(SHAIRPORT_SYNC(shairportSyncSkeleton), FALSE);
1093 } else {
1094 shairport_sync_set_convolution(SHAIRPORT_SYNC(shairportSyncSkeleton), TRUE);
1095 }
1096 if (config.convolution_ir_file)
54d761ff
MB
1097 shairport_sync_set_convolution_impulse_response_file(SHAIRPORT_SYNC(shairportSyncSkeleton),
1098 config.convolution_ir_file);
f2930387 1099// else
54d761ff
MB
1100// shairport_sync_set_convolution_impulse_response_file(SHAIRPORT_SYNC(shairportSyncSkeleton),
1101// NULL);
2f2442f4 1102#endif
ec665d9b 1103
3e4b67e9 1104 shairport_sync_set_service_name(SHAIRPORT_SYNC(shairportSyncSkeleton), config.service_name);
9af924dd 1105 shairport_sync_set_output_rate(SHAIRPORT_SYNC(shairportSyncSkeleton), config.output_rate);
e8b8c6ed
MB
1106 shairport_sync_set_output_format(SHAIRPORT_SYNC(shairportSyncSkeleton),
1107 sps_format_description_string(config.output_format));
3e4b67e9 1108
720a271e
MB
1109#ifdef CONFIG_AIRPLAY_2
1110 shairport_sync_set_protocol(SHAIRPORT_SYNC(shairportSyncSkeleton), "AirPlay 2");
1111#else
1112 shairport_sync_set_protocol(SHAIRPORT_SYNC(shairportSyncSkeleton), "AirPlay");
1113#endif
1114
340e365a
MB
1115 shairport_sync_set_version(SHAIRPORT_SYNC(shairportSyncSkeleton), PACKAGE_VERSION);
1116 char *vs = get_version_string();
1117 shairport_sync_set_version_string(SHAIRPORT_SYNC(shairportSyncSkeleton), vs);
6dc30c6c
MB
1118 if (vs)
1119 free(vs);
1120
d692fab9
MB
1121 shairport_sync_diagnostics_set_verbosity(
1122 SHAIRPORT_SYNC_DIAGNOSTICS(shairportSyncDiagnosticsSkeleton), debuglev);
1123
c0b1dc54
MB
1124 // debug(2,">> log verbosity is %d.",debuglev);
1125
1126 if (config.statistics_requested == 0) {
d692fab9
MB
1127 shairport_sync_diagnostics_set_statistics(
1128 SHAIRPORT_SYNC_DIAGNOSTICS(shairportSyncDiagnosticsSkeleton), FALSE);
c0b1dc54
MB
1129 // debug(1, ">> statistics logging is off");
1130 } else {
d692fab9
MB
1131 shairport_sync_diagnostics_set_statistics(
1132 SHAIRPORT_SYNC_DIAGNOSTICS(shairportSyncDiagnosticsSkeleton), TRUE);
c0b1dc54
MB
1133 // debug(1, ">> statistics logging is on");
1134 }
d692fab9 1135
c0b1dc54 1136 if (config.debugger_show_elapsed_time == 0) {
d692fab9
MB
1137 shairport_sync_diagnostics_set_elapsed_time(
1138 SHAIRPORT_SYNC_DIAGNOSTICS(shairportSyncDiagnosticsSkeleton), FALSE);
c0b1dc54
MB
1139 // debug(1, ">> elapsed time is included in log entries");
1140 } else {
d692fab9
MB
1141 shairport_sync_diagnostics_set_elapsed_time(
1142 SHAIRPORT_SYNC_DIAGNOSTICS(shairportSyncDiagnosticsSkeleton), TRUE);
c0b1dc54
MB
1143 // debug(1, ">> elapsed time is not included in log entries");
1144 }
1145
1146 if (config.debugger_show_relative_time == 0) {
d692fab9
MB
1147 shairport_sync_diagnostics_set_delta_time(
1148 SHAIRPORT_SYNC_DIAGNOSTICS(shairportSyncDiagnosticsSkeleton), FALSE);
c0b1dc54
MB
1149 // debug(1, ">> delta time is included in log entries");
1150 } else {
d692fab9
MB
1151 shairport_sync_diagnostics_set_delta_time(
1152 SHAIRPORT_SYNC_DIAGNOSTICS(shairportSyncDiagnosticsSkeleton), TRUE);
c0b1dc54
MB
1153 // debug(1, ">> delta time is not included in log entries");
1154 }
1155
db8f10cf
MB
1156 if (config.debugger_show_file_and_line == 0) {
1157 shairport_sync_diagnostics_set_file_and_line(
1158 SHAIRPORT_SYNC_DIAGNOSTICS(shairportSyncDiagnosticsSkeleton), FALSE);
1159 // debug(1, ">> file and line is included in log entries");
1160 } else {
1161 shairport_sync_diagnostics_set_file_and_line(
1162 SHAIRPORT_SYNC_DIAGNOSTICS(shairportSyncDiagnosticsSkeleton), TRUE);
1163 // debug(1, ">> file and line is not included in log entries");
1164 }
1165
cbbe84d8
MB
1166 shairport_sync_remote_control_set_player_state(shairportSyncRemoteControlSkeleton,
1167 "Not Available");
1168 shairport_sync_advanced_remote_control_set_playback_status(
1169 shairportSyncAdvancedRemoteControlSkeleton, "Not Available");
69b3b5d7
MB
1170
1171 shairport_sync_advanced_remote_control_set_loop_status(shairportSyncAdvancedRemoteControlSkeleton,
1172 "Not Available");
cbbe84d8 1173
c36a7822
MB
1174 debug(1, "Shairport Sync native D-Bus service started at \"%s\" on the %s bus.", name,
1175 (config.dbus_service_bus_type == DBT_session) ? "session" : "system");
97a4766e 1176 service_is_running = 1;
74f41a17
MB
1177}
1178
8991f342
MB
1179static void on_dbus_name_lost_again(__attribute__((unused)) GDBusConnection *connection,
1180 __attribute__((unused)) const gchar *name,
1181 __attribute__((unused)) gpointer user_data) {
dfd90bac 1182 warn("could not acquire a Shairport Sync native D-Bus interface \"%s\" on the %s bus.", name,
c36a7822 1183 (config.dbus_service_bus_type == DBT_session) ? "session" : "system");
74f41a17
MB
1184}
1185
8991f342
MB
1186static void on_dbus_name_lost(__attribute__((unused)) GDBusConnection *connection,
1187 __attribute__((unused)) const gchar *name,
1188 __attribute__((unused)) gpointer user_data) {
dfd90bac 1189 // debug(1, "could not acquire a Shairport Sync native D-Bus interface \"%s\" on the %s bus --
c36a7822 1190 // will try adding the process "
f3ef6b5e
MB
1191 // "number to the end of it.",
1192 // name, (config.dbus_service_bus_type == DBT_session) ? "session" : "system");
74f41a17
MB
1193 pid_t pid = getpid();
1194 char interface_name[256] = "";
08a7d4ff 1195 snprintf(interface_name, sizeof(interface_name), "org.gnome.ShairportSync.i%d", pid);
f74e2c2c
MB
1196 GBusType dbus_bus_type = G_BUS_TYPE_SYSTEM;
1197 if (config.dbus_service_bus_type == DBT_session)
1198 dbus_bus_type = G_BUS_TYPE_SESSION;
c36a7822
MB
1199 // debug(1, "Looking for a Shairport Sync native D-Bus interface \"%s\" on the %s bus.",
1200 // interface_name,(config.dbus_service_bus_type == DBT_session) ? "session" : "system");
f74e2c2c
MB
1201 g_bus_own_name(dbus_bus_type, interface_name, G_BUS_NAME_OWNER_FLAGS_NONE, NULL,
1202 on_dbus_name_acquired, on_dbus_name_lost_again, NULL, NULL);
d4eae76f
MB
1203}
1204
1205int start_dbus_service() {
d692fab9 1206 // shairportSyncSkeleton = NULL;
74f41a17 1207 GBusType dbus_bus_type = G_BUS_TYPE_SYSTEM;
bafdd94e 1208 if (config.dbus_service_bus_type == DBT_session)
74f41a17 1209 dbus_bus_type = G_BUS_TYPE_SESSION;
c36a7822
MB
1210 // debug(1, "Looking for a Shairport Sync native D-Bus interface \"org.gnome.ShairportSync\" on
1211 // the %s bus.",(config.dbus_service_bus_type == DBT_session) ? "session" : "system");
f74e2c2c
MB
1212 ownerID = g_bus_own_name(dbus_bus_type, "org.gnome.ShairportSync", G_BUS_NAME_OWNER_FLAGS_NONE,
1213 NULL, on_dbus_name_acquired, on_dbus_name_lost, NULL, NULL);
d4eae76f
MB
1214 return 0; // this is just to quieten a compiler warning
1215}
f1d45034
MB
1216
1217void stop_dbus_service() {
405a028f 1218 debug(2, "stopping dbus service");
62fca43f 1219 if (ownerID) {
f1d45034 1220 g_bus_unown_name(ownerID);
62fca43f 1221 } else if (service_is_running != 0) {
e8b8c6ed 1222 debug(1, "Zero OwnerID for running \"org.gnome.ShairportSync\" dbus service.");
62fca43f 1223 }
c8b0be30 1224 service_is_running = 0;
97a4766e
MB
1225}
1226
c8b0be30 1227int dbus_service_is_running() { return service_is_running; }