diff --git a/Makefile.am b/Makefile.am index 83fc2132..635b4aea 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 06bb2ce2..e42a8bed 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -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`. diff --git a/audio_alsa.c b/audio_alsa.c index ea6ab91f..1abc2961 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -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; diff --git a/dacp.c b/dacp.c index 81e7b530..31497915 100644 --- a/dacp.c +++ b/dacp.c @@ -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; diff --git a/documents/sample dbus commands b/documents/sample dbus commands index 0a7c398d..23804159 100644 --- a/documents/sample dbus commands +++ b/documents/sample dbus commands @@ -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 \ No newline at end of file +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 diff --git a/player.c b/player.c index 3c2a1c28..3f3fa505 100644 --- a/player.c +++ b/player.c @@ -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."); diff --git a/player.h b/player.h index 078a96a0..3bc415e4 100644 --- a/player.h +++ b/player.h @@ -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; diff --git a/rtp.c b/rtp.c index f15cbf02..253318f2 100644 --- a/rtp.c +++ b/rtp.c @@ -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); diff --git a/scripts/shairport-sync.freebsd b/scripts/shairport-sync.freebsd index 9088bd82..4e812dd0 100644 --- a/scripts/shairport-sync.freebsd +++ b/scripts/shairport-sync.freebsd @@ -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" } diff --git a/scripts/shairport-sync.in b/scripts/shairport-sync.in index 6bcfe9fe..f229f19b 100755 --- a/scripts/shairport-sync.in +++ b/scripts/shairport-sync.in @@ -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 } #