Bring up to date with 3.3d2 from the raspberry pi

This commit is contained in:
Mike Brady
2018-08-22 14:41:03 +01:00
10 changed files with 177 additions and 131 deletions
+1 -3
View File
@@ -8,7 +8,7 @@ shairport_sync_SOURCES = shairport.c rtsp.c mdns.c mdns_external.c common.c rtp.
AM_CFLAGS = -Wno-multichar -Wall -Wextra -Wno-clobbered -Wno-psabi -pthread -DSYSCONFDIR=\"$(sysconfdir)\"
if BUILD_FOR_FREEBSD
AM_CXXFLAGS = -I/usr/local/include -Wno-multichar -Wall -Wextra -Wno-clobbered -Wno-psabi -pthread -DSYSCONFDIR=\"$(sysconfdir)\" -O2
AM_CXXFLAGS = -I/usr/local/include -Wno-multichar -Wall -Wextra -pthread -DSYSCONFDIR=\"$(sysconfdir)\" -O2
else
if BUILD_FOR_OPENBSD
AM_CXXFLAGS = -I/usr/local/include -Wno-multichar -Wall -Wextra -Wno-clobbered -Wno-psabi -pthread -DSYSCONFDIR=\"$(sysconfdir)\"
@@ -144,8 +144,6 @@ endif
if INSTALL_SYSTEMV
getent group shairport-sync &>/dev/null || groupadd -r shairport-sync >/dev/null
getent passwd shairport-sync &> /dev/null || useradd -r -M -g shairport-sync -s /usr/bin/nologin -G audio shairport-sync >/dev/null
[ -e /var/run/shairport-sync ] || mkdir -p /var/run/shairport-sync
chown shairport-sync:shairport-sync /var/run/shairport-sync
[ -e $(DESTDIR)$(sysconfdir)/init.d ] || mkdir -p $(DESTDIR)$(sysconfdir)/init.d
[ -f $(DESTDIR)$(sysconfdir)/init.d/shairport-sync ] || cp scripts/shairport-sync $(DESTDIR)$(sysconfdir)/init.d/
endif
+3 -1
View File
@@ -1,6 +1,8 @@
Version 3.3d2
====
Modify the code that synchronises the Shairport Sync system's clock with the source clock to try to take account of the sources's true rate, which (oddly, e.g. iTunes on a Mac) might not be exactly 44,100 fps. Also try to interpolate for the measured drift between the standard three-second timing snapshots. These changes make a very slight difference from time to time, of the order of microseconds, and it's not clear yet how reliable the drift interpolation is.
Modify the code that synchronises the Shairport Sync system's clock with the source clock to try to take account of the sources's nominal rate, which (oddly, e.g. iTunes on a Mac) might not be exactly 44,100 fps. Also try to interpolate for the measured drift between the standard three-second timing snapshots. These changes make a very slight difference from time to time, of the order of microseconds, and it's not clear yet how reliable the drift interpolation is.
Add some extra fields to the `statistics` output, including nominal source rate, actual input rate, actual output rate, source clock drift, calculated correction needed in ppm. All thesrt numbers agree to a high degree, but the generation of them is fragile and susceptible to errors when there are problems like underrun, and they are not actually running averages, which would be genuinely useful. But they show promise!
**Bug Fixes**
* Fix a bug in the `dbus` native interface which would silently switch `soxr` interpolation to `basic`.
+15 -2
View File
@@ -658,7 +658,7 @@ int open_alsa_device(void) {
snd_strerror(ret));
return -7;
}
// check parameters after attempting to set them…
if (set_period_size_request != 0) {
@@ -884,6 +884,12 @@ int delay(long *the_delay) {
snd_pcm_recover(alsa_handle, reply, 1);
frame_index = 0;
measurement_data_is_valid = 0;
} else {
if (*the_delay == 0) {
// there's nothing in the pipeline, so we can't measure frame rate.
frame_index = 0; // we'll be starting over...
measurement_data_is_valid = 0;
}
}
} else {
frame_index = 0; // we'll be starting over...
@@ -987,8 +993,15 @@ static int play(void *buf, int samples) {
snd_pcm_recover(alsa_handle, err2, 1);
frame_index = 0;
measurement_data_is_valid = 0;
} else {
if (fl == 0) {
// there's nothing in the pipeline, so we can't measure frame rate.
frame_index = 0; // we'll be starting over...
measurement_data_is_valid = 0;
}
}
measurement_time = get_absolute_time_in_fp();
uint64_t tf = get_absolute_time_in_fp();
frames_played_at_measurement_time = frames_sent_for_playing - fl;
if (frame_index == start_measurement_from_this_frame) {
frames_played_at_measurement_start_time = frames_played_at_measurement_time;
+1 -1
View File
@@ -277,7 +277,7 @@ int dacp_send_command(const char *command, char **body, ssize_t *bodysize) {
}
if (http_iserror(&rt)) {
debug(1, "Error parsing data.");
debug(3, "Error parsing data.");
free(response.body);
response.body = NULL;
response.malloced_size = 0;
+6 -1
View File
@@ -20,4 +20,9 @@ dbus-send --system --print-reply --type=method_call --dest=org.gnome.ShairportSy
#Remote Control commands include: Play, Pause, PlayPause, Resume, Stop, Next, Previous, VolumeUp, VolumeDown, ToggleMute, FastForward, Rewind, ShuffleSongs
# Set Volume using Advanced Remote Control
dbus-send --system --print-reply --type=method_call --dest=org.gnome.ShairportSync '/org/gnome/ShairportSync' org.gnome.ShairportSync.AdvancedRemoteControl.SetVolume int32:50
dbus-send --system --print-reply --type=method_call --dest=org.gnome.ShairportSync '/org/gnome/ShairportSync' org.gnome.ShairportSync.AdvancedRemoteControl.SetVolume int32:50
# Get Drift Tolerance
dbus-send --print-reply --system --dest=org.gnome.ShairportSync /org/gnome/ShairportSync org.freedesktop.DBus.Properties.Get string:org.gnome.ShairportSync string:DriftTolerance
# Set Drift Tolerance to 1 millisecond
dbus-send --print-reply --system --dest=org.gnome.ShairportSync /org/gnome/ShairportSync org.freedesktop.DBus.Properties.Set string:org.gnome.ShairportSync string:DriftTolerance variant:double:0.001
+108 -66
View File
@@ -1282,6 +1282,9 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
if (notified_buffer_empty == 0) {
debug(3, "Buffers exhausted.");
notified_buffer_empty = 1;
conn->initial_reference_time = 0;
conn->initial_reference_timestamp = 0;
conn->input_frame_rate_starting_point_is_valid = 0;
}
do_wait = 1;
}
@@ -1293,7 +1296,7 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
time_to_wait_for_wakeup_fp *= 4 * 352; // four full 352-frame packets
time_to_wait_for_wakeup_fp /= 3; // four thirds of a packet time
time_to_wait_for_wakeup_fp = (uint64_t)0x200000000/(uint64_t)1000; // two millisecond
time_to_wait_for_wakeup_fp = (uint64_t)0x200000000 / (uint64_t)1000; // two millisecond
#ifdef COMPILE_FOR_LINUX_AND_FREEBSD_AND_CYGWIN_AND_OPENBSD
uint64_t time_of_wakeup_fp = local_time_now + time_to_wait_for_wakeup_fp;
@@ -1813,10 +1816,12 @@ void *player_thread_func(void *arg) {
"min DAC queue size, "
"min buffer occupancy, "
"max buffer occupancy, "
"source frames per second, "
"average input frames per second, "
"source nominal frames per second, "
"source actual frames per second, "
"output frames per second, "
"client:local clock drift in ppm");
"source clock drift in ppm, "
"source clock drift sample count, "
"rough calculated correction in ppm");
} else {
inform("sync error in milliseconds, "
"total packets, "
@@ -1827,7 +1832,10 @@ void *player_thread_func(void *arg) {
"min DAC queue size, "
"min buffer occupancy, "
"max buffer occupancy, "
"input frames per second.");
"source nominal frames per second, "
"source actual frames per second, "
"source clock drift in ppm, "
"source clock drift sample count");
}
} else {
inform("sync error in milliseconds, "
@@ -1838,7 +1846,10 @@ void *player_thread_func(void *arg) {
"resend requests, "
"min buffer occupancy, "
"max buffer occupancy, "
"input frames per second.");
"source nominal frames per second, "
"source actual frames per second, "
"source clock drift in ppm, "
"source clock drift sample count");
}
}
@@ -2096,10 +2107,20 @@ void *player_thread_func(void *arg) {
int64_t should_be_frame;
local_time_to_frame(local_time_now, &should_be_frame, conn);
<<<<<<< HEAD
//if (abs(td_in_frames + rt - should_be_frame) > 10 * conn->output_sample_ratio)
// debug(1, "Difference between old and new frame number is %" PRId64 " frames.",
// td_in_frames + rt - should_be_frame);
=======
int64_t absolute_difference_in_frames = td_in_frames + rt - should_be_frame;
if (absolute_difference_in_frames < 0)
absolute_difference_in_frames = -absolute_difference_in_frames;
if (absolute_difference_in_frames > 10 * conn->output_sample_ratio)
debug(1, "Difference between old and new frame number is %" PRId64 " frames.",
td_in_frames + rt - should_be_frame);
>>>>>>> 347e63ba906e80bfc01596bacfbd18fbe0b6f940
// this is the actual delay, including the latency we actually want, which will
// fluctuate a good bit about a potentially rising or falling trend.
@@ -2119,8 +2140,8 @@ void *player_thread_func(void *arg) {
delay - (conn->latency * conn->output_sample_ratio +
(int64_t)(config.audio_backend_latency_offset *
config.output_rate)); // int64_t from int64_t - int32_t, so okay
//debug(1,"%" PRId64 "",sync_error,inbuflength);
// debug(1,"%" PRId64 "",sync_error,inbuflength);
// not too sure if abs() is implemented for int64_t, so we'll do it manually
int64_t abs_sync_error = sync_error;
@@ -2181,24 +2202,25 @@ void *player_thread_func(void *arg) {
amount_to_stuff = 1;
}
*/
if (amount_to_stuff==0) {
if (amount_to_stuff == 0) {
// use a "V" shaped function to decide if stuffing should occur
int64_t s = r64i();
s = s>>31;
s = s >> 31;
s = s * config.tolerance * config.output_rate;
s = (s>>32)+config.tolerance * config.output_rate; //should be a number from 0 to config.tolerance * config.output_rate;
if ((sync_error>0) && (sync_error>s)) {
//debug(1,"Extra stuff -1");
s = (s >> 32) + config.tolerance * config.output_rate; // should be a number from 0
// to config.tolerance *
// config.output_rate;
if ((sync_error > 0) && (sync_error > s)) {
// debug(1,"Extra stuff -1");
amount_to_stuff = -1;
}
if ((sync_error<0) && (sync_error<(-s))) {
//debug(1,"Extra stuff +1");
if ((sync_error < 0) && (sync_error < (-s))) {
// debug(1,"Extra stuff +1");
amount_to_stuff = 1;
}
}
}
// try to keep the corrections definitely below 1 in 1000 audio frames
// calculate the time elapsed since the play session started.
@@ -2276,14 +2298,15 @@ void *player_thread_func(void *arg) {
tbuf32[2 * i + 1] = fbuf_r[i];
}
}
if ((current_delay < DAC_BUFFER_QUEUE_MINIMUM_LENGTH) || (config.packet_stuffing == ST_basic)) {
if ((current_delay < DAC_BUFFER_QUEUE_MINIMUM_LENGTH) ||
(config.packet_stuffing == ST_basic)) {
play_samples =
stuff_buffer_basic_32((int32_t *)conn->tbuf, inbuflength, config.output_format,
conn->outbuf, amount_to_stuff, enable_dither, conn);
conn->outbuf, amount_to_stuff, enable_dither, conn);
}
#ifdef HAVE_LIBSOXR
else if (config.packet_stuffing == ST_soxr) {
else if (config.packet_stuffing == ST_soxr) {
// if (amount_to_stuff) debug(1,"Soxr stuff...");
play_samples = stuff_buffer_soxr_32((int32_t *)conn->tbuf, (int32_t *)conn->sbuf,
inbuflength, config.output_format, conn->outbuf,
@@ -2437,10 +2460,10 @@ void *player_thread_func(void *arg) {
conn->frame_rate =
(1.0 * frames_played) /
elapsed_play_time; // an IEEE double calculation with two 64-bit integers
conn->frame_rate = conn->frame_rate *
(uint64_t)0x100000000; // this should just change the [binary]
// exponent in the IEEE FP representation;
// the mantissa should be unaffected.
conn->frame_rate =
conn->frame_rate * (uint64_t)0x100000000; // this should just change the [binary]
// exponent in the IEEE FP representation;
// the mantissa should be unaffected.
} else {
conn->frame_rate = 0.0;
}
@@ -2459,22 +2482,23 @@ void *player_thread_func(void *arg) {
if ((config.output->delay)) {
if (config.no_sync == 0) {
inform("%*.2f," /* Sync error in milliseconds */
"%*.1f," /* net correction in ppm */
"%*.1f," /* corrections in ppm */
"%*d," /* total packets */
"%*llu," /* missing packets */
"%*llu," /* late packets */
"%*llu," /* too late packets */
"%*llu," /* resend requests */
"%*lli," /* min DAC queue size */
"%*d," /* min buffer occupancy */
"%*d," /* max buffer occupancy */
"%*.2f," /* remote frame rate */
"%*.2f," /* actual (average) input frame rate */
"%*.2f," /* output frame rate */
"%*.2f," /* output frame rate */
"%*d", /* sample count */
inform("%*.2f," /* Sync error in milliseconds */
"%*.1f," /* net correction in ppm */
"%*.1f," /* corrections in ppm */
"%*d," /* total packets */
"%*" PRIu64 "," /* missing packets */
"%*" PRIu64 "," /* late packets */
"%*" PRIu64 "," /* too late packets */
"%*" PRIu64 "," /* resend requests */
"%*" PRId64 "," /* min DAC queue size */
"%*" PRId32 "," /* min buffer occupancy */
"%*" PRId32 "," /* max buffer occupancy */
"%*.2f," /* source nominal frame rate */
"%*.2f," /* source actual (average) frame rate */
"%*.2f," /* output frame rate */
"%*.2f," /* source clock drift */
"%*d," /* source clock drift sample count */
"%*.2f", /* rough calculated correction in ppm */
10,
1000 * moving_average_sync_error / config.output_rate, 10,
moving_average_correction * 1000000 / (352 * conn->output_sample_ratio),
@@ -2485,41 +2509,59 @@ void *player_thread_func(void *arg) {
minimum_dac_queue_size, 5, minimum_buffer_occupancy, 5,
maximum_buffer_occupancy, 11, conn->remote_frame_rate, 11,
conn->input_frame_rate, 11, conn->frame_rate, 10,
(1.0 - conn->local_to_remote_time_gradient) * 1000000, 6,
conn->local_to_remote_time_gradient_sample_count);
(conn->local_to_remote_time_gradient - 1.0) * 1000000, 6,
conn->local_to_remote_time_gradient_sample_count, 10,
(conn->frame_rate > 0.0)
? ((conn->frame_rate -
conn->remote_frame_rate * conn->output_sample_ratio *
conn->local_to_remote_time_gradient) *
1000000) /
conn->frame_rate
: 0.0);
} else {
inform("%*.2f," /* Sync error in milliseconds */
"%*d," /* total packets */
"%*llu," /* missing packets */
"%*llu," /* late packets */
"%*llu," /* too late packets */
"%*llu," /* resend requests */
"%*lli," /* min DAC queue size */
"%*d," /* min buffer occupancy */
"%*d," /* max buffer occupancy */
"%*.2f", /* input frame rate */
inform("%*.2f," /* Sync error in milliseconds */
"%*d," /* total packets */
"%*" PRIu64 "," /* missing packets */
"%*" PRIu64 "," /* late packets */
"%*" PRIu64 "," /* too late packets */
"%*" PRIu64 "," /* resend requests */
"%*" PRId64 "," /* min DAC queue size */
"%*" PRId32 "," /* min buffer occupancy */
"%*" PRId32 "," /* max buffer occupancy */
"%*.2f," /* source nominal frame rate */
"%*.2f," /* source actual (average) frame rate */
"%*.2f," /* source clock drift */
"%*d", /* source clock drift sample count */
10,
1000 * moving_average_sync_error / config.output_rate, 12, play_number, 7,
conn->missing_packets, 7, conn->late_packets, 7, conn->too_late_packets, 7,
conn->resend_requests, 7, minimum_dac_queue_size, 5,
minimum_buffer_occupancy, 5, maximum_buffer_occupancy, 11,
conn->input_frame_rate);
conn->remote_frame_rate, 11, conn->input_frame_rate, 10,
(conn->local_to_remote_time_gradient - 1.0) * 1000000, 6,
conn->local_to_remote_time_gradient_sample_count);
}
} else {
inform("%*.2f," /* Sync error in milliseconds */
"%*d," /* total packets */
"%*llu," /* missing packets */
"%*llu," /* late packets */
"%*llu," /* too late packets */
"%*llu," /* resend requests */
"%*d," /* min buffer occupancy */
"%*d," /* max buffer occupancy */
"%*.2f", /* input frame rate */
inform("%*.2f," /* Sync error in milliseconds */
"%*d," /* total packets */
"%*" PRIu64 "," /* missing packets */
"%*" PRIu64 "," /* late packets */
"%*" PRIu64 "," /* too late packets */
"%*" PRIu64 "," /* resend requests */
"%*" PRId32 "," /* min buffer occupancy */
"%*" PRId32 "," /* max buffer occupancy */
"%*.2f," /* source nominal frame rate */
"%*.2f," /* source actual (average) frame rate */
"%*.2f," /* source clock drift */
"%*d", /* source clock drift sample count */
10,
1000 * moving_average_sync_error / config.output_rate, 12, play_number, 7,
conn->missing_packets, 7, conn->late_packets, 7, conn->too_late_packets, 7,
conn->resend_requests, 5, minimum_buffer_occupancy, 5,
maximum_buffer_occupancy, 11, conn->input_frame_rate);
maximum_buffer_occupancy, 11, conn->remote_frame_rate, 11,
conn->input_frame_rate, 10,
(conn->local_to_remote_time_gradient - 1.0) * 1000000, 6,
conn->local_to_remote_time_gradient_sample_count);
}
} else {
inform("No frames received in the last sampling interval.");
+1 -1
View File
@@ -24,7 +24,7 @@
#include "alac.h"
#include "audio.h"
#define time_ping_history 64 // at 1 per three seconds, approximately three minutes of records
#define time_ping_history 128 // at 1 per three seconds, approximately six minutes of records
typedef struct time_ping_record {
uint64_t local_to_remote_difference;
+29 -30
View File
@@ -765,41 +765,40 @@ void *rtp_timing_receiver(void *arg) {
x_bar += (conn->time_pings[cc].local_time >> 12);
sample_count++;
}
y_bar = y_bar / sample_count;
x_bar = x_bar / sample_count;
int64_t xid, yid;
int64_t mtl, mbl;
mtl = 0;
mbl = 0;
for (cc = 0; cc < conn->time_ping_count; cc++)
if ((conn->time_pings[cc].chosen) &&
(conn->time_pings[cc].sequence_number > (settling_time / 3))) {
uint64_t slt = conn->time_pings[cc].local_time >> 12;
if (slt > x_bar)
xid = slt - x_bar;
else
xid = -(x_bar - slt);
uint64_t srt = conn->time_pings[cc].remote_time >> 12;
if (srt > y_bar)
yid = srt - y_bar;
else
yid = -(y_bar - srt);
mtl = mtl + xid * yid;
mbl = mbl + xid * xid;
}
conn->local_to_remote_time_gradient_sample_count = sample_count;
if (sample_count > sample_point_minimum) {
y_bar = y_bar / sample_count;
x_bar = x_bar / sample_count;
int64_t xid, yid;
int64_t mtl, mbl;
mtl = 0;
mbl = 0;
for (cc = 0; cc < conn->time_ping_count; cc++)
if ((conn->time_pings[cc].chosen) &&
(conn->time_pings[cc].sequence_number > (settling_time / 3))) {
uint64_t slt = conn->time_pings[cc].local_time >> 12;
if (slt > x_bar)
xid = slt - x_bar;
else
xid = -(x_bar - slt);
uint64_t srt = conn->time_pings[cc].remote_time >> 12;
if (srt > y_bar)
yid = srt - y_bar;
else
yid = -(y_bar - srt);
mtl = mtl + xid * yid;
mbl = mbl + xid * xid;
}
conn->local_to_remote_time_gradient_sample_count = sample_count;
conn->local_to_remote_time_gradient = (1.0 * mtl) / mbl;
// debug(1,"Drift is %12.2f ppm, based on %d
// samples.",(1.0-conn->local_to_remote_time_gradient)*1000000,sample_count);
} else {
conn->local_to_remote_time_gradient = 1.0;
}
// debug(1,"local to remote time gradient is %12.2f ppm, based on %d
// samples.",conn->local_to_remote_time_gradient*1000000,sample_count);
} else {
debug(2, "Time ping turnaround time: %lld us -- it looks like a timing ping was lost.",
(return_time * 1000000) >> 32);
+1 -1
View File
@@ -14,7 +14,7 @@ stop_cmd="${name}_stop"
shairport_sync_start()
{
checkyesno shairport_sync_enable && echo "Starting shairport-sync." && \
checkyesno shairport_sync_enable && echo "Starting shairport-sync." && \
su -m shairport-sync -c "/usr/local/bin/shairport-sync -d"
}
+12 -25
View File
@@ -23,8 +23,7 @@ DAEMON=@prefix@/bin/$NAME
# impossible to pass as arguments.
# Instead, we add the arguments directly to the relevant line in the do_start() function below
PIDFILE=/var/run/$NAME.pid
PIDFILE=/var/run/$NAME/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
# Exit if the package is not installed
@@ -48,31 +47,19 @@ do_start()
{
# Return
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
|| return 1
# 1 if PID directory didn't exist and couldn't be created with approproate permission
# 2 if daemon was already running
# 3 if daemon could not be started
[ -e /var/run/shairport-sync ] || ( mkdir -p /var/run/shairport-sync && chown shairport-sync:shairport-sync /var/run/shairport-sync ) || return 1
start-stop-daemon -c shairport-sync:shairport-sync --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null || return 2
# This script is set to start running after all other services have started.
# However, if you find that Shairport Sync is still being started before what it needs is ready,
# uncomment the next line to get the script to wait for three seconds before attempting to start Shairport Sync.
# sleep 3
# This script is set to start running after all other services have started.
# However, if you find that Shairport Sync is still being started before what it needs is ready,
# uncomment the next line to get the script to wait for three seconds before attempting to start Shairport Sync.
# sleep 3
# Uncomment just one of the following start-stop-daemon lines, or comment them all out and add your own.
# Shairport Sync will read settings from the configuration file (/etc/shairport-sync.conf by default) and will then apply any command line options.
# In the default script, the first line is uncommented, selecting daemon mode (-d). Settings will be taken from the configuration file.
# BTW, if you're using software volume control, you may have to use alsamixer or similar to set the output device's volume to its maximum level first
# BTW2, you can use alsamixer to find device identifiers (e.g. hw:1) and mixer names (e.g. "Speaker"). No need to change ALSA's defaults.
# BTW3, the argument after -a is simply the name the shairport service will be visible as.
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- -d || return 2
# start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- -d -a "Topping TP30 or Griffin iMic on Raspberry Pi" -- -d hw:1 -t hardware -c "PCM" || return 2
# start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- -d -a "'3D Sound' USB Soundcard on Raspberry Pi" -- -d hw:1 -t hardware -c "Speaker" || return 2
# BTW, that "3D Sound" USB soundcard sometimes has the mixer name "Headphone" rather than "Speaker" -- use alsamixer to check.
# start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- -d -a "IQaudIO" -- -d hw:1 -t hardware -c "Playback Digital" || return 2
# BTW, newer versions of IQaudIO have a different mixer name -- use alsamixer to check.
# Add code here, if necessary, that waits for the process to be ready
# to handle requests from services started subsequently which depend
# on this one. As a last resort, sleep for some time.
# Settings from the configuration file (@sysconfdir@/shairport-sync.conf by default)
start-stop-daemon -c shairport-sync:shairport-sync --start --quiet --pidfile $PIDFILE --exec $DAEMON -- -d || return 3
}
#