From 042dd0d03670ace193dbf4163f7e25c8232edeb3 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 15 Apr 2019 11:13:14 +0100 Subject: [PATCH 01/17] Fix a warning about ending a funrtion without a return value. --- mdns_external.c | 68 ++++++++++++++++++++++++------------------------- 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/mdns_external.c b/mdns_external.c index 2eae8204..2ae23c61 100644 --- a/mdns_external.c +++ b/mdns_external.c @@ -1,6 +1,7 @@ /* * mDNS registration handler. This file is part of Shairport. * Copyright (c) Paul Lietar 2013 + * Amendments and updates copyright (c) Mike Brady 2014 -- 2019 * All rights reserved. * * Permission is hereby granted, free of charge, to any person @@ -24,8 +25,8 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -#include "common.h" #include "mdns.h" +#include "common.h" #include #include #include @@ -41,45 +42,42 @@ int mdns_pid = 0; */ static int fork_execvp(const char *file, char *const argv[]) { int execpipe[2]; - int pid = 0; - if (pipe(execpipe) < 0) { - return -1; - } + int response = -1; + if (pipe(execpipe) >= 0) { - if (fcntl(execpipe[1], F_SETFD, fcntl(execpipe[1], F_GETFD) | FD_CLOEXEC) < 0) { - close(execpipe[0]); - close(execpipe[1]); - return -1; - } + if (fcntl(execpipe[1], F_SETFD, fcntl(execpipe[1], F_GETFD) | FD_CLOEXEC) < 0) { + close(execpipe[0]); + close(execpipe[1]); + } else { - pid = fork(); - if (pid < 0) { - close(execpipe[0]); - close(execpipe[1]); - return -1; - } else if (pid == 0) { // Child - close(execpipe[0]); // Close the read end - execvp(file, argv); + int pid = fork(); + if (pid < 0) { + close(execpipe[0]); + close(execpipe[1]); + } else if (pid == 0) { // Child + close(execpipe[0]); // Close the read end + execvp(file, argv); + // If we reach this point then execve has failed. + // Write erno's value into the pipe and exit. + if (write(execpipe[1], &errno, sizeof(errno)) != sizeof(errno)) + debug(1, + "Execve has failed and there was a further error writing an error message, duh."); + die("mdns_external: execve has failed."); + } else { // Parent + close(execpipe[1]); // Close the write end - // If we reach this point then execve has failed. - // Write erno's value into the pipe and exit. - if (write(execpipe[1], &errno, sizeof(errno)) != sizeof(errno)) - debug(1, "Execve has failed and there was a further error writing an error message, duh."); - die("mdns_external: execve has failed."); - // return 0; // Just to make the compiler happy. - } else { // Parent - close(execpipe[1]); // Close the write end - - int childErrno; - // Block until child closes the pipe or sends errno. - if (read(execpipe[0], &childErrno, sizeof(childErrno)) == - sizeof(childErrno)) { // We received errno - errno = childErrno; - return -1; - } else { // Child closed the pipe. execvp was successful. - return pid; + int childErrno; + // Block until child closes the pipe or sends errno. + if (read(execpipe[0], &childErrno, sizeof(childErrno)) == + sizeof(childErrno)) { // We received errno + errno = childErrno; + } else { + response = pid; + } + } } } + return response; } static int mdns_external_avahi_register(char *apname, __attribute__((unused)) int port) { From 16235071eedb286d53306336b1fcc5ef245dfdcd Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 15 Apr 2019 11:13:51 +0100 Subject: [PATCH 02/17] Default to "basic" stuffing even if soxr support is included. --- scripts/shairport-sync.conf | 1 + shairport.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/scripts/shairport-sync.conf b/scripts/shairport-sync.conf index a979431c..2bcba159 100644 --- a/scripts/shairport-sync.conf +++ b/scripts/shairport-sync.conf @@ -15,6 +15,7 @@ general = // %V for the full version string, e.g. 3.3-OpenSSL-Avahi-ALSA-soxr-metadata-sysconfdir:/etc // Overall length can not exceed 50 characters. Example: "Shairport Sync %v on %H". // password = "secret"; // leave this commented out if you don't want to require a password +// for this "soxr" option to be available in the next setting, to be operative, Shairport Sync must be built with the configuration flag --with-soxr // interpolation = "basic"; // aka "stuffing". Default is "basic", alternative is "soxr". Use "soxr" only if you have a reasonably fast processor. // Note: if Shairport Sync is build with the --with-soxr configuration flag, interpolation will be "soxr" by default. // output_backend = "alsa"; // Run "shairport-sync -h" to get a list of all output_backends, e.g. "alsa", "pipe", "stdout". The default is the first one. diff --git a/shairport.c b/shairport.c index 7e98bf27..cb9f132c 100644 --- a/shairport.c +++ b/shairport.c @@ -1312,11 +1312,15 @@ int main(int argc, char **argv) { 0.002; // this number of seconds of timing error before attempting to correct it. config.buffer_start_fill = 220; config.port = 5000; + +/* #ifdef CONFIG_SOXR config.packet_stuffing = ST_soxr; // use soxr interpolation by default if support has been included #else config.packet_stuffing = ST_basic; // simple interpolation or deletion #endif +*/ + // char hostname[100]; // gethostname(hostname, 100); // config.service_name = malloc(20 + 100); From 2dd184165a97cece924e9094898e4293dae613c2 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 15 Apr 2019 11:46:42 +0100 Subject: [PATCH 03/17] add soxr interpolation statistics gathering. --- player.c | 53 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/player.c b/player.c index 546b474e..d37a28c3 100644 --- a/player.c +++ b/player.c @@ -891,7 +891,7 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) { curframe->given_timestamp; // we will keep buffering until we are // supposed to start playing this have_sent_prefiller_silence = 0; - + // debug(1, "First packet timestamp is %" PRId64 ".", conn->first_packet_timestamp); // say we have started playing here @@ -959,20 +959,22 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) { conn); conn->first_packet_time_to_play = should_be_time; - + // we want the frames of silence sent at the start to be fairly large in case the output - // device's minimum buffer size is large. But they can't be greater than the silent lead_in time - // which is either the agreed latency or the silent lead-in time specified by the setting + // device's minimum buffer size is large. But they can't be greater than the silent + // lead_in time + // which is either the agreed latency or the silent lead-in time specified by the + // setting // In fact, if should be some fraction of them, to allow for adjustment. - + int64_t max_dac_delay = conn->latency; if (config.audio_backend_silent_lead_in_time >= 0) max_dac_delay = - (int64_t)(config.audio_backend_silent_lead_in_time * conn->input_rate); - + (int64_t)(config.audio_backend_silent_lead_in_time * conn->input_rate); + max_dac_delay = max_dac_delay / 4; - - // debug(1,"max_dac_delay is %" PRIu64 " frames.", max_dac_delay); + + // debug(1,"max_dac_delay is %" PRIu64 " frames.", max_dac_delay); int64_t filler_size = max_dac_delay; @@ -1333,12 +1335,19 @@ static int stuff_buffer_basic_32(int32_t *inptr, int length, enum sps_format_t l // (d) outputs the result in the approprate format // formats accepted so far include U8, S8, S16, S24, S24_3LE, S24_3BE and S32 +int32_t stat_n = 0; +double stat_mean = 0.0; +double stat_M2 = 0.0; +double longest_soxr_execution_time_us = 0.0; +int64_t packets_processed = 0; + int stuff_buffer_soxr_32(int32_t *inptr, int32_t *scratchBuffer, int length, enum sps_format_t l_output_format, char *outptr, int stuff, int dither, rtsp_conn_info *conn) { if (scratchBuffer == NULL) { die("soxr scratchBuffer not initialised."); } + packets_processed++; int tstuff = stuff; if ((stuff > 1) || (stuff < -1) || (length < 100)) { // debug(1, "Stuff argument to stuff_buffer must be from -1 to +1 and length >100."); @@ -1357,6 +1366,8 @@ int stuff_buffer_soxr_32(int32_t *inptr, int32_t *scratchBuffer, int length, size_t odone; + uint64_t soxr_start_time = get_absolute_time_in_fp(); + soxr_error_t error = soxr_oneshot(length, length + tstuff, 2, // Rates and # of chans. inptr, length, NULL, // Input. scratchBuffer, length + tstuff, &odone, // Output. @@ -1369,6 +1380,18 @@ int stuff_buffer_soxr_32(int32_t *inptr, int32_t *scratchBuffer, int length, if (odone > (size_t)(length + 1)) die("odone = %u!\n", odone); + // mean and variance calculations from "online_variance" algorithm at + // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Online_algorithm + + double soxr_execution_time_us = + (((get_absolute_time_in_fp() - soxr_start_time) * 1000000) >> 32) * 1.0; + if (soxr_execution_time_us > longest_soxr_execution_time_us) + longest_soxr_execution_time_us = soxr_execution_time_us; + stat_n += 1; + double stat_delta = soxr_execution_time_us - stat_mean; + stat_mean += stat_delta / stat_n; + stat_M2 += stat_delta * (soxr_execution_time_us - stat_mean); + int i; int32_t *ip, *op; ip = inptr; @@ -1411,6 +1434,18 @@ int stuff_buffer_soxr_32(int32_t *inptr, int32_t *scratchBuffer, int length, process_sample(*ip++, &l_outptr, l_output_format, conn->fix_volume, dither, conn); }; } + + if (packets_processed % 2500 == 0) { + debug(1, "soxr_oneshot execution time in microseconds: mean, standard deviation and max " + "for %" PRId32 " interpolations in the last " + "2500 packets. %10.1f, %10.1f, %10.1f.", + stat_n, stat_mean, sqrtf(stat_M2 / (stat_n - 1)), longest_soxr_execution_time_us); + stat_n = 0; + stat_mean = 0.0; + stat_M2 = 0.0; + longest_soxr_execution_time_us = 0.0; + } + conn->amountStuffed = tstuff; return length + tstuff; } From 026d2114cc5715682ba5ae7745ce0b58fc6b090b Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 15 Apr 2019 16:25:47 +0100 Subject: [PATCH 04/17] ignore build_details.h --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 6f76aec5..d95c96e6 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ shairport-sync.core /dbus-interface.* /mpris-interface.* /lib_*.* +build_details.h # Some eclipse project files .cproject From c985ee6059ba1c5e2853f7004ca44b7b4ca6711b Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 15 Apr 2019 16:26:43 +0100 Subject: [PATCH 05/17] Include build_details.h as part of Shairport Sync --- Makefile.am | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Makefile.am b/Makefile.am index 3842686d..229930bd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -8,8 +8,12 @@ bin_PROGRAMS = shairport-sync # See below for the flags for the test client program -shairport_sync_SOURCES = shairport.c rtsp.c mdns.c common.c rtp.c player.c alac.c audio.c loudness.c activity_monitor.c +shairport_sync_SOURCES = build_details.h shairport.c rtsp.c mdns.c common.c rtp.c player.c alac.c audio.c loudness.c activity_monitor.c + +# from Stack Overflow, with thanks: https://stackoverflow.com/questions/54303270/how-to-execute-shell-commands-in-automake-makefile-am +build_details.h: + echo "#define GITDESCRIPTION \"`git describe --dirty --always 2>/dev/null`\"" >$@ if BUILD_FOR_FREEBSD AM_CXXFLAGS = -I/usr/local/include -Wno-multichar -Wall -Wextra -pthread -DSYSCONFDIR=\"$(sysconfdir)\" AM_CFLAGS = -Wno-multichar -Wall -Wextra -pthread -DSYSCONFDIR=\"$(sysconfdir)\" @@ -101,9 +105,9 @@ if USE_DACP_CLIENT shairport_sync_SOURCES += dacp.c tinyhttp/chunk.c tinyhttp/header.c tinyhttp/http.c endif -BUILT_SOURCES = -noinst_HEADERS = -CLEANFILES = +BUILT_SOURCES = build_details.h +noinst_HEADERS = build_details.h +CLEANFILES = build_details.h shairport_sync_LDADD = noinst_LIBRARIES = From 1b1348e1ac6dd1ba799be9e8d7c55bfe4edb3862 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 15 Apr 2019 16:27:44 +0100 Subject: [PATCH 06/17] Add git commit and dirty status if available. Slightly reorder the debug messages at the start. --- common.c | 10 ++++++++-- shairport.c | 24 +++++++++++++----------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/common.c b/common.c index 5fae31af..2376ae48 100644 --- a/common.c +++ b/common.c @@ -83,6 +83,8 @@ #include #endif +#include "build_details.h" + #ifdef CONFIG_ALSA void set_alsa_out_dev(char *); #endif @@ -1282,11 +1284,15 @@ char *get_version_string() { char *version_string = malloc(1024); if (version_string) { strcpy(version_string, PACKAGE_VERSION); + if (strlen(GITDESCRIPTION)) { + strcat(version_string, "-"); + strcat(version_string, GITDESCRIPTION); + } #ifdef CONFIG_LIBDAEMON - strcat(version_string, "-libdaemon"); + strcat(version_string, "-libdaemon"); #endif #ifdef CONFIG_MBEDTLS - strcat(version_string, "-mbedTLS"); + strcat(version_string, "-mbedTLS"); #endif #ifdef CONFIG_POLARSSL strcat(version_string, "-PolarSSL"); diff --git a/shairport.c b/shairport.c index cb9f132c..54573004 100644 --- a/shairport.c +++ b/shairport.c @@ -1510,6 +1510,19 @@ int main(int argc, char **argv) { // make sure the program can create files that group and world can read umask(S_IWGRP | S_IWOTH); + + /* print out version */ + + char *version_dbs = get_version_string(); + if (version_dbs) { + debug(1, "software version: \"%s\"", version_dbs); + free(version_dbs); + } else { + debug(1, "can't print the version information!"); + } + + debug(1, "log verbosity is %d.", debuglev); + config.output = audio_get_output(config.output_name); if (!config.output) { audio_ls_outputs(); @@ -1555,18 +1568,7 @@ int main(int argc, char **argv) { BUFFER_FRAMES * 352 - 22050); } - /* print out version */ - - char *version_dbs = get_version_string(); - if (version_dbs) { - debug(1, "software version: \"%s\"", version_dbs); - free(version_dbs); - } else { - debug(1, "can't print the version information!"); - } - /* Print out options */ - debug(1, "log verbosity is %d.", debuglev); debug(1, "disable resend requests is %s.", config.disable_resend_requests ? "on" : "off"); debug(1, "diagnostic_drop_packet_fraction is %f. A value of 0.0 means no packets will be dropped " "deliberately.", From 6cbaa15bdd5f480d0385e9bf916c354596407079 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 15 Apr 2019 17:07:34 +0100 Subject: [PATCH 07/17] Turn on the configuration flag for building soxr support in when the pkg_config is not available. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index e466adbe..f019be3f 100644 --- a/configure.ac +++ b/configure.ac @@ -184,8 +184,8 @@ AC_ARG_WITH(ssl, [ choose --with-ssl=openssl, --with-ssl=mbedtls or --with-ssl=p # Look for soxr flag AC_ARG_WITH(soxr, [ --with-soxr = choose libsoxr for high-quality interpolation], [ AC_MSG_RESULT(>>Including support for soxr-based interpolation) + AC_DEFINE([CONFIG_SOXR], 1, [Include support for using the SoX Resampler library for interpolation]) if test "x${with_pkg_config}" = xyes ; then - AC_DEFINE([CONFIG_SOXR], 1, [Include support for using the SoX Resampler library for interpolation]) PKG_CHECK_MODULES( [SOXR], [soxr], [LIBS="${SOXR_LIBS} ${LIBS}"], From 3a96a5a62db23acbe834c2ec7f264811bfb9ee91 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 15 Apr 2019 17:16:30 +0100 Subject: [PATCH 08/17] very noisy debug of soxr! --- player.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/player.c b/player.c index d37a28c3..7f409f35 100644 --- a/player.c +++ b/player.c @@ -1435,10 +1435,10 @@ int stuff_buffer_soxr_32(int32_t *inptr, int32_t *scratchBuffer, int length, }; } - if (packets_processed % 2500 == 0) { + if (packets_processed % 1 == 0) { debug(1, "soxr_oneshot execution time in microseconds: mean, standard deviation and max " "for %" PRId32 " interpolations in the last " - "2500 packets. %10.1f, %10.1f, %10.1f.", + "1 packets. %10.1f, %10.1f, %10.1f.", stat_n, stat_mean, sqrtf(stat_M2 / (stat_n - 1)), longest_soxr_execution_time_us); stat_n = 0; stat_mean = 0.0; From 8cdb4dc70e0f8cb9ef44e563b380e87c3cab706c Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 15 Apr 2019 17:22:52 +0100 Subject: [PATCH 09/17] a bit less noisy debug/timing of soxr! --- player.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/player.c b/player.c index 7f409f35..55cf01a6 100644 --- a/player.c +++ b/player.c @@ -1435,10 +1435,10 @@ int stuff_buffer_soxr_32(int32_t *inptr, int32_t *scratchBuffer, int length, }; } - if (packets_processed % 1 == 0) { + if (packets_processed % 10 == 0) { debug(1, "soxr_oneshot execution time in microseconds: mean, standard deviation and max " "for %" PRId32 " interpolations in the last " - "1 packets. %10.1f, %10.1f, %10.1f.", + "10 packets. %10.1f, %10.1f, %10.1f.", stat_n, stat_mean, sqrtf(stat_M2 / (stat_n - 1)), longest_soxr_execution_time_us); stat_n = 0; stat_mean = 0.0; From d0917fc3046a96cd5df2beca21afb7fe595c9f54 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Tue, 16 Apr 2019 09:10:45 +0100 Subject: [PATCH 10/17] Add code to time soxr_oneshot without playing anything. It's in the wrong place, but it's a start. --- player.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 2 deletions(-) diff --git a/player.c b/player.c index 55cf01a6..31c157e2 100644 --- a/player.c +++ b/player.c @@ -432,6 +432,72 @@ static void free_audio_buffers(rtsp_conn_info *conn) { free(conn->audio_buffer[i].data); } + +void soxr_time_check() { + const int buffer_length = 352; + //int32_t inbuffer[buffer_length*2]; + //int32_t outbuffer[(buffer_length+1)*2]; + + int32_t *outbuffer = (int32_t*)malloc((buffer_length+1)*2*sizeof(int32_t)); + int32_t *inbuffer = (int32_t*)malloc((buffer_length)*2*sizeof(int32_t)); + + // generate a sample signal + const double frequency = 440; // + + int i; + + const int number_of_iterations = 100; + uint64_t soxr_start_time = get_absolute_time_in_fp(); + int j; + for (j = 0; j < number_of_iterations; j++) { + + + for (i = 0; i < buffer_length ; i++) { + double w = sin(i * (frequency + j * 2) * 2 * M_PI/44100); + int32_t wint = (int32_t)(w * INT32_MAX); + inbuffer[i * 2] = wint; + inbuffer[i * 2 + 1] = wint; + } + + + + soxr_io_spec_t io_spec; + io_spec.itype = SOXR_INT32_I; + io_spec.otype = SOXR_INT32_I; + io_spec.scale = 1.0; // this seems to crash if not = 1.0 + io_spec.e = NULL; + io_spec.flags = 0; + + size_t odone; + + soxr_oneshot(buffer_length, buffer_length + 1, 2, // Rates and # of chans. + inbuffer, buffer_length, NULL, // Input. + outbuffer, buffer_length + 1, &odone, // Output. + &io_spec, // Input, output and transfer spec. + NULL, NULL); // Default configuration. + /* + io_spec.itype = SOXR_INT32_I; + io_spec.otype = SOXR_INT32_I; + io_spec.scale = 1.0; // this seems to crash if not = 1.0 + io_spec.e = NULL; + io_spec.flags = 0; + + soxr_oneshot(buffer_length, buffer_length - 1, 2, // Rates and # of chans. + inbuffer, buffer_length, NULL, // Input. + outbuffer, buffer_length - 1, &odone, // Output. + &io_spec, // Input, output and transfer spec. + NULL, NULL); // Default configuration. + */ + } + + + double soxr_execution_time_us = + (((get_absolute_time_in_fp() - soxr_start_time) * 1000000) >> 32) * 1.0; + free(outbuffer); + free(inbuffer); + debug(1,"Execution time for %d soxr interpolations: %10.1f microseconds.", number_of_iterations, soxr_execution_time_us); +} + void player_put_packet(seq_t seqno, uint32_t actual_timestamp, uint8_t *data, int len, rtsp_conn_info *conn) { @@ -1385,6 +1451,7 @@ int stuff_buffer_soxr_32(int32_t *inptr, int32_t *scratchBuffer, int length, double soxr_execution_time_us = (((get_absolute_time_in_fp() - soxr_start_time) * 1000000) >> 32) * 1.0; + // debug(1,"soxr_execution_time_us: %10.1f",soxr_execution_time_us); if (soxr_execution_time_us > longest_soxr_execution_time_us) longest_soxr_execution_time_us = soxr_execution_time_us; stat_n += 1; @@ -1435,10 +1502,10 @@ int stuff_buffer_soxr_32(int32_t *inptr, int32_t *scratchBuffer, int length, }; } - if (packets_processed % 10 == 0) { + if (packets_processed % 500 == 0) { debug(1, "soxr_oneshot execution time in microseconds: mean, standard deviation and max " "for %" PRId32 " interpolations in the last " - "10 packets. %10.1f, %10.1f, %10.1f.", + "500 packets. %10.1f, %10.1f, %10.1f.", stat_n, stat_mean, sqrtf(stat_M2 / (stat_n - 1)), longest_soxr_execution_time_us); stat_n = 0; stat_mean = 0.0; @@ -1531,6 +1598,7 @@ void player_thread_cleanup_handler(void *arg) { } void *player_thread_func(void *arg) { + soxr_time_check(); rtsp_conn_info *conn = (rtsp_conn_info *)arg; // pthread_cleanup_push(player_thread_initial_cleanup_handler, arg); conn->packet_count = 0; From ca8acb4a3f2e38d1e04b151870ad4a53d8aa16c5 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Tue, 16 Apr 2019 12:59:02 +0100 Subject: [PATCH 11/17] Move the soxr timer checker to run as soon as the system powers up. --- player.c | 71 ++--------------------------------------------- shairport.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 69 deletions(-) diff --git a/player.c b/player.c index 31c157e2..6f3dc35a 100644 --- a/player.c +++ b/player.c @@ -432,72 +432,6 @@ static void free_audio_buffers(rtsp_conn_info *conn) { free(conn->audio_buffer[i].data); } - -void soxr_time_check() { - const int buffer_length = 352; - //int32_t inbuffer[buffer_length*2]; - //int32_t outbuffer[(buffer_length+1)*2]; - - int32_t *outbuffer = (int32_t*)malloc((buffer_length+1)*2*sizeof(int32_t)); - int32_t *inbuffer = (int32_t*)malloc((buffer_length)*2*sizeof(int32_t)); - - // generate a sample signal - const double frequency = 440; // - - int i; - - const int number_of_iterations = 100; - uint64_t soxr_start_time = get_absolute_time_in_fp(); - int j; - for (j = 0; j < number_of_iterations; j++) { - - - for (i = 0; i < buffer_length ; i++) { - double w = sin(i * (frequency + j * 2) * 2 * M_PI/44100); - int32_t wint = (int32_t)(w * INT32_MAX); - inbuffer[i * 2] = wint; - inbuffer[i * 2 + 1] = wint; - } - - - - soxr_io_spec_t io_spec; - io_spec.itype = SOXR_INT32_I; - io_spec.otype = SOXR_INT32_I; - io_spec.scale = 1.0; // this seems to crash if not = 1.0 - io_spec.e = NULL; - io_spec.flags = 0; - - size_t odone; - - soxr_oneshot(buffer_length, buffer_length + 1, 2, // Rates and # of chans. - inbuffer, buffer_length, NULL, // Input. - outbuffer, buffer_length + 1, &odone, // Output. - &io_spec, // Input, output and transfer spec. - NULL, NULL); // Default configuration. - /* - io_spec.itype = SOXR_INT32_I; - io_spec.otype = SOXR_INT32_I; - io_spec.scale = 1.0; // this seems to crash if not = 1.0 - io_spec.e = NULL; - io_spec.flags = 0; - - soxr_oneshot(buffer_length, buffer_length - 1, 2, // Rates and # of chans. - inbuffer, buffer_length, NULL, // Input. - outbuffer, buffer_length - 1, &odone, // Output. - &io_spec, // Input, output and transfer spec. - NULL, NULL); // Default configuration. - */ - } - - - double soxr_execution_time_us = - (((get_absolute_time_in_fp() - soxr_start_time) * 1000000) >> 32) * 1.0; - free(outbuffer); - free(inbuffer); - debug(1,"Execution time for %d soxr interpolations: %10.1f microseconds.", number_of_iterations, soxr_execution_time_us); -} - void player_put_packet(seq_t seqno, uint32_t actual_timestamp, uint8_t *data, int len, rtsp_conn_info *conn) { @@ -1502,10 +1436,10 @@ int stuff_buffer_soxr_32(int32_t *inptr, int32_t *scratchBuffer, int length, }; } - if (packets_processed % 500 == 0) { + if (packets_processed % 1250 == 0) { debug(1, "soxr_oneshot execution time in microseconds: mean, standard deviation and max " "for %" PRId32 " interpolations in the last " - "500 packets. %10.1f, %10.1f, %10.1f.", + "1250 packets. %10.1f, %10.1f, %10.1f.", stat_n, stat_mean, sqrtf(stat_M2 / (stat_n - 1)), longest_soxr_execution_time_us); stat_n = 0; stat_mean = 0.0; @@ -1598,7 +1532,6 @@ void player_thread_cleanup_handler(void *arg) { } void *player_thread_func(void *arg) { - soxr_time_check(); rtsp_conn_info *conn = (rtsp_conn_info *)arg; // pthread_cleanup_push(player_thread_initial_cleanup_handler, arg); conn->packet_count = 0; diff --git a/shairport.c b/shairport.c index 54573004..7ddb77dc 100644 --- a/shairport.c +++ b/shairport.c @@ -41,6 +41,7 @@ #include #include + #include "config.h" #ifdef CONFIG_MBEDTLS @@ -95,6 +96,11 @@ #include #endif +#ifdef CONFIG_SOXR +#include +#include +#endif + #ifdef CONFIG_CONVOLUTION #include #endif @@ -158,6 +164,75 @@ void print_version(void) { } } +#ifdef CONFIG_SOXR + +void soxr_time_check() { + const int buffer_length = 352; + //int32_t inbuffer[buffer_length*2]; + //int32_t outbuffer[(buffer_length+1)*2]; + + int32_t *outbuffer = (int32_t*)malloc((buffer_length+1)*2*sizeof(int32_t)); + int32_t *inbuffer = (int32_t*)malloc((buffer_length)*2*sizeof(int32_t)); + + // generate a sample signal + const double frequency = 440; // + + int i; + + const int number_of_iterations = 100; + uint64_t soxr_start_time = get_absolute_time_in_fp(); + int j; + for (j = 0; j < number_of_iterations; j++) { + + + for (i = 0; i < buffer_length ; i++) { + double w = sin(i * (frequency + j * 2) * 2 * M_PI/44100); + int32_t wint = (int32_t)(w * INT32_MAX); + inbuffer[i * 2] = wint; + inbuffer[i * 2 + 1] = wint; + } + + + + soxr_io_spec_t io_spec; + io_spec.itype = SOXR_INT32_I; + io_spec.otype = SOXR_INT32_I; + io_spec.scale = 1.0; // this seems to crash if not = 1.0 + io_spec.e = NULL; + io_spec.flags = 0; + + size_t odone; + + soxr_oneshot(buffer_length, buffer_length + 1, 2, // Rates and # of chans. + inbuffer, buffer_length, NULL, // Input. + outbuffer, buffer_length + 1, &odone, // Output. + &io_spec, // Input, output and transfer spec. + NULL, NULL); // Default configuration. + /* + io_spec.itype = SOXR_INT32_I; + io_spec.otype = SOXR_INT32_I; + io_spec.scale = 1.0; // this seems to crash if not = 1.0 + io_spec.e = NULL; + io_spec.flags = 0; + + soxr_oneshot(buffer_length, buffer_length - 1, 2, // Rates and # of chans. + inbuffer, buffer_length, NULL, // Input. + outbuffer, buffer_length - 1, &odone, // Output. + &io_spec, // Input, output and transfer spec. + NULL, NULL); // Default configuration. + */ + } + + + double soxr_execution_time_us = + (((get_absolute_time_in_fp() - soxr_start_time) * 1000000) >> 32) * 1.0; + free(outbuffer); + free(inbuffer); + debug(1,"Execution time for %d soxr interpolations: %10.1f microseconds.", number_of_iterations, soxr_execution_time_us); +} + +#endif + void usage(char *progname) { printf("Usage: %s [options...]\n", progname); printf(" or: %s [options...] -- [audio output-specific options]\n", progname); @@ -1720,6 +1795,10 @@ int main(int argc, char **argv) { } #endif +#ifdef CONFIG_SOXR + soxr_time_check(); +#endif + activity_monitor_start(); // debug(1, "Successful Startup"); From 9aa8f91c0e8ca4b7c7674ce9bc1623be48beaab0 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Tue, 16 Apr 2019 16:40:56 +0100 Subject: [PATCH 12/17] Add a new interpolation setting -- "auto" which will choose soxr if supported and the soxr delay is less or equal to the threshold of 30 (milliseconds). --- common.h | 3 ++ player.c | 12 ++++--- scripts/shairport-sync.conf | 4 +-- shairport.c | 69 +++++++++++++++++++++++-------------- 4 files changed, 56 insertions(+), 32 deletions(-) diff --git a/common.h b/common.h index 1a2dd8a5..0ba8dc5e 100644 --- a/common.h +++ b/common.h @@ -56,6 +56,7 @@ enum endian_type { enum stuffing_type { ST_basic = 0, // straight deletion or insertion of a frame in a 352-frame packet ST_soxr, // use libsoxr to make a 352 frame packet one frame longer or shorter + ST_auto, // use soxr if compiled for it and if the soxr_index is low enough } s_type; enum playback_mode_type { @@ -179,6 +180,8 @@ typedef struct { int cmd_blocking, cmd_start_returns_output; double tolerance; // allow this much drift before attempting to correct it enum stuffing_type packet_stuffing; + int soxr_delay_index; + int soxr_delay_threshold; // the soxr delay must be less or equal to this for soxr interpolation to be enabled under the auto setting int decoders_supported; int use_apple_decoder; // set to 1 if you want to use the apple decoder instead of the original by // David Hammerton diff --git a/player.c b/player.c index 6f3dc35a..c222e001 100644 --- a/player.c +++ b/player.c @@ -2282,15 +2282,19 @@ void *player_thread_func(void *arg) { } } +#ifdef CONFIG_SOXR if ((current_delay < conn->dac_buffer_queue_minimum_length) || - (config.packet_stuffing == ST_basic)) { + (config.packet_stuffing == ST_basic) || + (config.soxr_delay_index == 0) || // not computed yet + ((config.packet_stuffing == ST_auto) && (config.soxr_delay_index > config.soxr_delay_threshold)) // if the CPU is deemed too slow + ) { +#endif play_samples = stuff_buffer_basic_32((int32_t *)conn->tbuf, inbuflength, config.output_format, conn->outbuf, amount_to_stuff, conn->enable_dither, conn); - } #ifdef CONFIG_SOXR - else if (config.packet_stuffing == ST_soxr) { - // if (amount_to_stuff) debug(1,"Soxr stuff..."); + } + else { // soxr requested or auto requested with the index less or equal to the threshold play_samples = stuff_buffer_soxr_32((int32_t *)conn->tbuf, (int32_t *)conn->sbuf, inbuflength, config.output_format, conn->outbuf, amount_to_stuff, conn->enable_dither, conn); diff --git a/scripts/shairport-sync.conf b/scripts/shairport-sync.conf index 2bcba159..c340ff8f 100644 --- a/scripts/shairport-sync.conf +++ b/scripts/shairport-sync.conf @@ -15,9 +15,7 @@ general = // %V for the full version string, e.g. 3.3-OpenSSL-Avahi-ALSA-soxr-metadata-sysconfdir:/etc // Overall length can not exceed 50 characters. Example: "Shairport Sync %v on %H". // password = "secret"; // leave this commented out if you don't want to require a password -// for this "soxr" option to be available in the next setting, to be operative, Shairport Sync must be built with the configuration flag --with-soxr -// interpolation = "basic"; // aka "stuffing". Default is "basic", alternative is "soxr". Use "soxr" only if you have a reasonably fast processor. -// Note: if Shairport Sync is build with the --with-soxr configuration flag, interpolation will be "soxr" by default. +// interpolation = "auto"; // aka "stuffing". Default is "auto". Alternatives are "basic" or "soxr". Choose "soxr" only if you have a reasonably fast processor and Shairport Sync has been built with "soxr" support. // output_backend = "alsa"; // Run "shairport-sync -h" to get a list of all output_backends, e.g. "alsa", "pipe", "stdout". The default is the first one. // mdns_backend = "avahi"; // Run "shairport-sync -h" to get a list of all mdns_backends. The default is the first one. // port = 5000; // Listen for service requests on this port diff --git a/shairport.c b/shairport.c index 7ddb77dc..ef3d187d 100644 --- a/shairport.c +++ b/shairport.c @@ -165,14 +165,14 @@ void print_version(void) { } #ifdef CONFIG_SOXR - -void soxr_time_check() { +pthread_t soxr_time_check_thread; +void* soxr_time_check(__attribute__((unused)) void *arg) { const int buffer_length = 352; - //int32_t inbuffer[buffer_length*2]; - //int32_t outbuffer[(buffer_length+1)*2]; + int32_t inbuffer[buffer_length*2]; + int32_t outbuffer[(buffer_length+1)*2]; - int32_t *outbuffer = (int32_t*)malloc((buffer_length+1)*2*sizeof(int32_t)); - int32_t *inbuffer = (int32_t*)malloc((buffer_length)*2*sizeof(int32_t)); + //int32_t *outbuffer = (int32_t*)malloc((buffer_length+1)*2*sizeof(int32_t)); + //int32_t *inbuffer = (int32_t*)malloc((buffer_length)*2*sizeof(int32_t)); // generate a sample signal const double frequency = 440; // @@ -192,8 +192,6 @@ void soxr_time_check() { inbuffer[i * 2 + 1] = wint; } - - soxr_io_spec_t io_spec; io_spec.itype = SOXR_INT32_I; io_spec.otype = SOXR_INT32_I; @@ -208,7 +206,7 @@ void soxr_time_check() { outbuffer, buffer_length + 1, &odone, // Output. &io_spec, // Input, output and transfer spec. NULL, NULL); // Default configuration. - /* + io_spec.itype = SOXR_INT32_I; io_spec.otype = SOXR_INT32_I; io_spec.scale = 1.0; // this seems to crash if not = 1.0 @@ -220,15 +218,18 @@ void soxr_time_check() { outbuffer, buffer_length - 1, &odone, // Output. &io_spec, // Input, output and transfer spec. NULL, NULL); // Default configuration. - */ + } - double soxr_execution_time_us = (((get_absolute_time_in_fp() - soxr_start_time) * 1000000) >> 32) * 1.0; - free(outbuffer); - free(inbuffer); - debug(1,"Execution time for %d soxr interpolations: %10.1f microseconds.", number_of_iterations, soxr_execution_time_us); + // free(outbuffer); + // free(inbuffer); + config.soxr_delay_index = (int)(0.9 + soxr_execution_time_us/(number_of_iterations *1000)); + if ((config.soxr_delay_index > config.soxr_delay_threshold) && (config.packet_stuffing == ST_soxr)) + inform("this device is probably too slow to do \"soxr\"-based interpolation. Consider choosing the \'basic\" setting."); + debug(1,"soxr_delay_index: %d, soxr_delay_threshold: %d, thus probably %scapable of doing \"soxr\" interpolation. Interpolation setting is %d (0-basic, 1-soxr, 2-auto).", config.soxr_delay_index, config.soxr_delay_threshold, config.soxr_delay_index <= config.soxr_delay_threshold ? "" : "not ", config.packet_stuffing); + pthread_exit(NULL); } #endif @@ -425,8 +426,8 @@ int parse_options(int argc, char **argv) { config.fixedLatencyOffset = 11025; // this sounds like it works properly. config.diagnostic_drop_packet_fraction = 0.0; config.active_state_timeout = 10.0; - config.volume_range_hw_priority = - 0; // if combining software and hardware volume control, give the software priority + config.soxr_delay_threshold = 30; // the soxr measurement time (milliseconds) of two oneshots must not exceed this if soxr interpolation is to be chosen automatically. + config.volume_range_hw_priority = 0; // if combining software and hardware volume control, give the software priority // i.e. when reducing volume, reduce the sw first before reducing the software. // this is because some hw mixers mute at the bottom of their range, and they don't always advertise this fact @@ -526,6 +527,8 @@ int parse_options(int argc, char **argv) { if (config_lookup_string(config.cfg, "general.interpolation", &str)) { if (strcasecmp(str, "basic") == 0) config.packet_stuffing = ST_basic; + else if (strcasecmp(str, "auto") == 0) + config.packet_stuffing = ST_auto; else if (strcasecmp(str, "soxr") == 0) #ifdef CONFIG_SOXR config.packet_stuffing = ST_soxr; @@ -535,8 +538,21 @@ int parse_options(int argc, char **argv) { "support. Change the \"general/interpolation\" setting in the configuration file."); #endif else - die("Invalid interpolation option choice. It should be \"basic\" or \"soxr\""); + die("Invalid interpolation option choice. It should be \"auto\", \"basic\" or \"soxr\""); } + +#ifdef CONFIG_SOXR + /* Get the soxr_delay_threshold setting. */ + if (config_lookup_int(config.cfg, "general.soxr_delay_threshold", &value)) { + if ((value >= 0) && (value <= 100)) + config.soxr_delay_threshold = value; + else + warn("Invalid general soxr_delay_threshold setting option choice \"%d\". It should be " + "between 0 and 100, " + "inclusive. Default is %d (milliseconds).", + value, config.soxr_delay_threshold); + } +#endif /* Get the statistics setting. */ if (config_set_lookup_bool(config.cfg, "general.statistics", @@ -1073,6 +1089,8 @@ int parse_options(int argc, char **argv) { case 'S': if (strcmp(stuffing, "basic") == 0) config.packet_stuffing = ST_basic; + else if (strcmp(stuffing, "auto") == 0) + config.packet_stuffing = ST_auto; else if (strcmp(stuffing, "soxr") == 0) #ifdef CONFIG_SOXR config.packet_stuffing = ST_soxr; @@ -1388,13 +1406,13 @@ int main(int argc, char **argv) { config.buffer_start_fill = 220; config.port = 5000; -/* + #ifdef CONFIG_SOXR - config.packet_stuffing = ST_soxr; // use soxr interpolation by default if support has been included + config.packet_stuffing = ST_auto; // use soxr interpolation by default if support has been included and if the CPU is fast enough #else config.packet_stuffing = ST_basic; // simple interpolation or deletion #endif -*/ + // char hostname[100]; // gethostname(hostname, 100); @@ -1668,7 +1686,8 @@ int main(int argc, char **argv) { debug(1, "active_state_timeout is %f seconds.", config.active_state_timeout); debug(1, "mdns backend \"%s\".", config.mdns_name); debug(2, "userSuppliedLatency is %d.", config.userSuppliedLatency); - debug(1, "stuffing option is \"%d\" (0-basic, 1-soxr).", config.packet_stuffing); + debug(1, "interpolation setting is \"%d\" (0-basic, 1-soxr, 2-auto).", config.packet_stuffing); + debug(1, "interpolation soxr_delay_threshold is %d.", config.soxr_delay_threshold); debug(1, "resync time is %f seconds.", config.resyncthreshold); debug(1, "allow a session to be interrupted: %d.", config.allow_session_interruption); debug(1, "busy timeout time is %d.", config.timeout); @@ -1735,6 +1754,10 @@ int main(int argc, char **argv) { uint8_t ap_md5[16]; +#ifdef CONFIG_SOXR + pthread_create(&soxr_time_check_thread, NULL, &soxr_time_check, NULL); +#endif + #ifdef CONFIG_OPENSSL MD5_CTX ctx; MD5_Init(&ctx); @@ -1795,10 +1818,6 @@ int main(int argc, char **argv) { } #endif -#ifdef CONFIG_SOXR - soxr_time_check(); -#endif - activity_monitor_start(); // debug(1, "Successful Startup"); From db8fc8057fe19a660133fedabc7b27e2f6a26251 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Tue, 16 Apr 2019 22:45:45 +0100 Subject: [PATCH 13/17] Always include the up-to-date abbreviated git hash if we are in the git repository. --- Makefile.am | 6 ++++-- common.c | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Makefile.am b/Makefile.am index 229930bd..4aaf53af 100644 --- a/Makefile.am +++ b/Makefile.am @@ -12,8 +12,10 @@ shairport_sync_SOURCES = build_details.h shairport.c rtsp.c mdns.c common.c rtp. # from Stack Overflow, with thanks: https://stackoverflow.com/questions/54303270/how-to-execute-shell-commands-in-automake-makefile-am -build_details.h: - echo "#define GITDESCRIPTION \"`git describe --dirty --always 2>/dev/null`\"" >$@ +build_details.h: FORCE + ( [ -e $(CURDIR)/.git ] && echo "#define GITDESCRIPTION \"`git describe --dirty --always 2>/dev/null`\"" >$@ ) || echo "" >$@ +FORCE: + if BUILD_FOR_FREEBSD AM_CXXFLAGS = -I/usr/local/include -Wno-multichar -Wall -Wextra -pthread -DSYSCONFDIR=\"$(sysconfdir)\" AM_CFLAGS = -Wno-multichar -Wall -Wextra -pthread -DSYSCONFDIR=\"$(sysconfdir)\" diff --git a/common.c b/common.c index 2376ae48..98ebe36c 100644 --- a/common.c +++ b/common.c @@ -1284,10 +1284,12 @@ char *get_version_string() { char *version_string = malloc(1024); if (version_string) { strcpy(version_string, PACKAGE_VERSION); +#ifdef GITDESCRIPTION if (strlen(GITDESCRIPTION)) { strcat(version_string, "-"); strcat(version_string, GITDESCRIPTION); } +#endif #ifdef CONFIG_LIBDAEMON strcat(version_string, "-libdaemon"); #endif From a69aedf2252d7006fbb86db632414532682bdbc3 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Tue, 16 Apr 2019 23:11:30 +0100 Subject: [PATCH 14/17] Take a fixed amount of time to check the soxr_delay_index, not a fixed number of iterations. --- shairport.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/shairport.c b/shairport.c index ef3d187d..f8f567af 100644 --- a/shairport.c +++ b/shairport.c @@ -179,14 +179,14 @@ void* soxr_time_check(__attribute__((unused)) void *arg) { int i; - const int number_of_iterations = 100; + int number_of_iterations = 0; uint64_t soxr_start_time = get_absolute_time_in_fp(); - int j; - for (j = 0; j < number_of_iterations; j++) { - + uint64_t loop_until_time = (uint64_t)0x100000000 + soxr_start_time; // loop for a second, max + while (get_absolute_time_in_fp() < loop_until_time) { + number_of_iterations++; for (i = 0; i < buffer_length ; i++) { - double w = sin(i * (frequency + j * 2) * 2 * M_PI/44100); + double w = sin(i * (frequency + number_of_iterations * 2) * 2 * M_PI/44100); int32_t wint = (int32_t)(w * INT32_MAX); inbuffer[i * 2] = wint; inbuffer[i * 2 + 1] = wint; @@ -227,8 +227,8 @@ void* soxr_time_check(__attribute__((unused)) void *arg) { // free(inbuffer); config.soxr_delay_index = (int)(0.9 + soxr_execution_time_us/(number_of_iterations *1000)); if ((config.soxr_delay_index > config.soxr_delay_threshold) && (config.packet_stuffing == ST_soxr)) - inform("this device is probably too slow to do \"soxr\"-based interpolation. Consider choosing the \'basic\" setting."); - debug(1,"soxr_delay_index: %d, soxr_delay_threshold: %d, thus probably %scapable of doing \"soxr\" interpolation. Interpolation setting is %d (0-basic, 1-soxr, 2-auto).", config.soxr_delay_index, config.soxr_delay_threshold, config.soxr_delay_index <= config.soxr_delay_threshold ? "" : "not ", config.packet_stuffing); + inform("this device is probably too slow to do \"soxr\" based interpolation. Consider choosing the \"basic\" or \"auto\" interpolation setting."); + debug(1,"this device is probably %scapable of doing \"soxr\" interpolation. The soxr_delay_index is %d (lower is better) and the soxr_delay_threshold is %d. Interpolation setting is %d (0-basic, 1-soxr, 2-auto).", config.soxr_delay_index <= config.soxr_delay_threshold ? "" : "not ", config.soxr_delay_index, config.soxr_delay_threshold, config.packet_stuffing); pthread_exit(NULL); } From 8568b58b494a2517d5e5cec48e6effac4985f62b Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Wed, 17 Apr 2019 16:33:08 +0100 Subject: [PATCH 15/17] Tidy up some debug and information messages --- shairport.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/shairport.c b/shairport.c index f8f567af..da3d334d 100644 --- a/shairport.c +++ b/shairport.c @@ -181,7 +181,7 @@ void* soxr_time_check(__attribute__((unused)) void *arg) { int number_of_iterations = 0; uint64_t soxr_start_time = get_absolute_time_in_fp(); - uint64_t loop_until_time = (uint64_t)0x100000000 + soxr_start_time; // loop for a second, max + uint64_t loop_until_time = (uint64_t)0x180000000 + soxr_start_time; // loop for a second and a half, max while (get_absolute_time_in_fp() < loop_until_time) { number_of_iterations++; @@ -226,9 +226,11 @@ void* soxr_time_check(__attribute__((unused)) void *arg) { // free(outbuffer); // free(inbuffer); config.soxr_delay_index = (int)(0.9 + soxr_execution_time_us/(number_of_iterations *1000)); - if ((config.soxr_delay_index > config.soxr_delay_threshold) && (config.packet_stuffing == ST_soxr)) - inform("this device is probably too slow to do \"soxr\" based interpolation. Consider choosing the \"basic\" or \"auto\" interpolation setting."); - debug(1,"this device is probably %scapable of doing \"soxr\" interpolation. The soxr_delay_index is %d (lower is better) and the soxr_delay_threshold is %d. Interpolation setting is %d (0-basic, 1-soxr, 2-auto).", config.soxr_delay_index <= config.soxr_delay_threshold ? "" : "not ", config.soxr_delay_index, config.soxr_delay_threshold, config.packet_stuffing); + debug(1,"soxr_delay_index: %d.", config.soxr_delay_index); + if ((config.packet_stuffing == ST_soxr) && (config.soxr_delay_index > config.soxr_delay_threshold)) + inform("Note: this device may be too slow for \"soxr\" interpolation. Consider choosing the \"basic\" or \"auto\" interpolation setting."); + if (config.packet_stuffing == ST_auto) + debug(1,"\"%s\" interpolation has been chosen.", config.soxr_delay_index <= config.soxr_delay_threshold ? "soxr" : "basic"); pthread_exit(NULL); } @@ -1686,7 +1688,7 @@ int main(int argc, char **argv) { debug(1, "active_state_timeout is %f seconds.", config.active_state_timeout); debug(1, "mdns backend \"%s\".", config.mdns_name); debug(2, "userSuppliedLatency is %d.", config.userSuppliedLatency); - debug(1, "interpolation setting is \"%d\" (0-basic, 1-soxr, 2-auto).", config.packet_stuffing); + debug(1, "interpolation setting is \"%s\".", config.packet_stuffing == ST_basic ? "basic" : config.packet_stuffing == ST_soxr ? "soxr" : "auto"); debug(1, "interpolation soxr_delay_threshold is %d.", config.soxr_delay_threshold); debug(1, "resync time is %f seconds.", config.resyncthreshold); debug(1, "allow a session to be interrupted: %d.", config.allow_session_interruption); From 7ad8907356c9302d88ff8082b7b761b7f56e0b67 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Wed, 17 Apr 2019 16:43:18 +0100 Subject: [PATCH 16/17] quieten a debug message --- player.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/player.c b/player.c index c222e001..c802ca9d 100644 --- a/player.c +++ b/player.c @@ -1437,7 +1437,7 @@ int stuff_buffer_soxr_32(int32_t *inptr, int32_t *scratchBuffer, int length, } if (packets_processed % 1250 == 0) { - debug(1, "soxr_oneshot execution time in microseconds: mean, standard deviation and max " + debug(2, "soxr_oneshot execution time in microseconds: mean, standard deviation and max " "for %" PRId32 " interpolations in the last " "1250 packets. %10.1f, %10.1f, %10.1f.", stat_n, stat_mean, sqrtf(stat_M2 / (stat_n - 1)), longest_soxr_execution_time_us); From 4c57ca08c4f1ff06fa5e18fdaa781ea20646959a Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Sat, 20 Apr 2019 10:01:42 +0100 Subject: [PATCH 17/17] Remove a few bogus warnings. --- audio_pipe.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/audio_pipe.c b/audio_pipe.c index e97f6034..a26bc9f3 100644 --- a/audio_pipe.c +++ b/audio_pipe.c @@ -46,9 +46,16 @@ int warned = 0; static void start(__attribute__((unused)) int sample_rate, __attribute__((unused)) int sample_format) { - // this will leave fd as -1 if a reader hasn't been attached + + + // this will leave fd as -1 if a reader hasn't been attached to the pipe + // we check that it's not a "real" error though. From the "man 2 open" page: + // "ENXIO O_NONBLOCK | O_WRONLY is set, the named file is a FIFO, and no process has the FIFO open for reading." fd = open(pipename, O_WRONLY | O_NONBLOCK); - if ((fd < -1) && (warned == 0)) { + if ((fd == -1) && (errno != ENXIO) && (warned == 0)) { + char errorstring[1024]; + strerror_r(errno, (char *)errorstring, sizeof(errorstring)); + debug(1, "pipe: start -- error %d (\"%s\") opening the pipe named \"%s\".", errno, (char*)errorstring, pipename); warn("Error %d opening the pipe named \"%s\".", errno, pipename); warned = 1; } @@ -68,7 +75,7 @@ static int play(void *buf, int samples) { warn("Error %d writing to the pipe named \"%s\": \"%s\".", errno, pipename, errorstring); warned = 1; } - } else if ((fd == -1) && (warned == 0)) { + } else if ((fd == -1) && (errno != ENXIO) && (warned == 0)) { strerror_r(errno, (char *)errorstring, 1024); warn("Error %d opening the pipe named \"%s\": \"%s\".", errno, pipename, errorstring); warned = 1;