From 19fbededcfc77c83ca2c076026412157ee5fc956 Mon Sep 17 00:00:00 2001 From: David Crook Date: Sat, 25 May 2019 13:01:22 -0600 Subject: [PATCH 01/33] mqtt port config changes 1. update example shairport-sync.conf file 2. update config file mqtt port parsing Signed-off-by: David Crook --- scripts/shairport-sync.conf | 4 ++-- shairport.c | 13 +++++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/scripts/shairport-sync.conf b/scripts/shairport-sync.conf index 7b030d6d..cd342d8e 100644 --- a/scripts/shairport-sync.conf +++ b/scripts/shairport-sync.conf @@ -31,7 +31,7 @@ general = // volume_control_profile = "standard" ; // use this advanced setting to specify how the airplay volume is transferred to the mixer volume. // "standard" makes the volume change more quickly at lower volumes and slower at higher volumes. // "flat" makes the volume change at the same rate at all volumes. -// volume_range_combined_hardware_priority = "no"; // when extending the volume range by combining the built-in software attenuator with the hardware mixer attenuator, set this to "yes" to reduce volume by using the hardware mixer first, then the built-in software attenuator. +// volume_range_combined_hardware_priority = "no"; // when extending the volume range by combining the built-in software attenuator with the hardware mixer attenuator, set this to "yes" to reduce volume by using the hardware mixer first, then the built-in software attenuator. // run_this_when_volume_is_set = "/full/path/to/application/and/args"; // Run the specified application whenever the volume control is set or changed. // The desired AirPlay volume is appended to the end of the command line – leave a space if you want it treated as an extra argument. // AirPlay volume goes from 0 to -30 and -144 means "mute". @@ -199,7 +199,7 @@ mqtt = { // enabled = "no"; // set this to yes to enable the mqtt-metadata-service // hostname = "iot.eclipse.org"; // Hostname of the MQTT Broker -// port = "1883"; +// port = 1883; // Port on the MQTT Broker to connect to // username = NULL; //set this to a string with your username, to enable username authentication // password = NULL; //set this to a string with your password, to enable username & password authentication // capath = NULL; //set this to the folder with the CA-Certificates to be accepted for the server certificate. If not set, TLS is not used diff --git a/shairport.c b/shairport.c index 9e9400b3..667a565b 100644 --- a/shairport.c +++ b/shairport.c @@ -992,7 +992,6 @@ int parse_options(int argc, char **argv) { #endif #ifdef CONFIG_MQTT - int tmpval = 0; config_set_lookup_bool(config.cfg, "mqtt.enabled", &config.mqtt_enabled); if (config.mqtt_enabled && !config.metadata_enabled) { die("You need to have metadata enabled in order to use mqtt"); @@ -1001,11 +1000,13 @@ int parse_options(int argc, char **argv) { config.mqtt_hostname = (char *)str; // TODO: Document that, if this is false, whole mqtt func is disabled } - if (config_lookup_int(config.cfg, "mqtt.port", &tmpval)) { - config.mqtt_port = tmpval; - } else { - // TODO: Is this the correct way to set a default value? - config.mqtt_port = 1883; + config.mqtt_port = 1883; + if (config_lookup_int(config.cfg, "mqtt.port", &value)) { + if ((value < 0) || (value > 65535)) + die("Invalid mqtt port number \"%sd\". It should be between 0 and 65535, default is 1883", + value); + else + config.mqtt_port = value; } if (config_lookup_string(config.cfg, "mqtt.username", &str)) { From 76ad4e6a73709f6c42bd9a8ccd6a85844096719e Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Wed, 29 May 2019 16:00:47 +0100 Subject: [PATCH 02/33] enable specification of specific formats and/or speeds to turn off automatic format/speed selection, fixing a bug. Clarify some of the warnings. Fix a few typos in the configuration file sample. --- audio_alsa.c | 61 ++++++++++++++++++++++--------------- scripts/shairport-sync.conf | 8 ++--- 2 files changed, 41 insertions(+), 28 deletions(-) diff --git a/audio_alsa.c b/audio_alsa.c index cd692f1d..a6de218c 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -1037,6 +1037,8 @@ static int init(int argc, char **argv) { /* Get the output format, using the same names as aplay does*/ if (config_lookup_string(config.cfg, "alsa.output_format", &str)) { + int temp_output_format_auto_requested = config.output_format_auto_requested; + config.output_format_auto_requested = 0; // assume a valid format will be given. if (strcasecmp(str, "S16") == 0) config.output_format = SPS_FORMAT_S16; else if (strcasecmp(str, "S16_LE") == 0) @@ -1066,11 +1068,12 @@ static int init(int argc, char **argv) { else if (strcasecmp(str, "auto") == 0) config.output_format_auto_requested = 1; else { - warn("Invalid output format \"%s\". It should be \"U8\", \"S8\", " + config.output_format_auto_requested = temp_output_format_auto_requested; //format was invalid; recall the original setting + warn("Invalid output format \"%s\". It should be \"auto\", \"U8\", \"S8\", " "\"S16\", \"S24\", \"S24_LE\", \"S24_BE\", " "\"S24_3LE\", \"S24_3BE\" or " - "\"S32\", \"S32_LE\", \"S32_BE\". It is set to \"%s\".", - sps_format_description_string(config.output_format)); + "\"S32\", \"S32_LE\", \"S32_BE\". It remains set to \"%s\".", str, + config.output_format_auto_requested == 1 ? "auto" : sps_format_description_string(config.output_format)); } } @@ -1078,25 +1081,35 @@ static int init(int argc, char **argv) { if (strcasecmp(str, "auto") == 0) { config.output_rate_auto_requested = 1; } else { - /* Get the output rate, which must be a multiple of 44,100*/ - if (config_lookup_int(config.cfg, "alsa.output_rate", &value)) { - debug(1, "alsa output rate is %d frames per second", value); - switch (value) { - case 44100: - case 88200: - case 176400: - case 352800: - config.output_rate = value; - break; - default: - warn("Invalid output rate \"%d\". It should be \"auto\" or a multiple of 44,100 up " - "to 352,800. It is " - "set to %d.", - value,config.output_rate); - } - } + if (config.output_rate_auto_requested == 1) + warn("Invalid output rate \"%s\". It should be \"auto\", 44100, 88200, 176400 or 352800. " + "It remains set to \"auto\". Note: numbers should not be placed in quotes.", str); + else + warn("Invalid output rate \"%s\". It should be \"auto\", 44100, 88200, 176400 or 352800. " + "It remains set to %d. Note: numbers should not be placed in quotes.", str, config.output_rate); } - } + } + + /* Get the output rate, which must be a multiple of 44,100*/ + if (config_lookup_int(config.cfg, "alsa.output_rate", &value)) { + debug(1, "alsa output rate is %d frames per second", value); + switch (value) { + case 44100: + case 88200: + case 176400: + case 352800: + config.output_rate = value; + config.output_rate_auto_requested = 0; + break; + default: + if (config.output_rate_auto_requested == 1) + warn("Invalid output rate \"%d\". It should be \"auto\", 44100, 88200, 176400 or 352800. " + "It remains set to \"auto\".",value); + else + warn("Invalid output rate \"%d\".It should be \"auto\", 44100, 88200, 176400 or 352800. " + "It remains set to %d.", value, config.output_rate); + } + } /* Get the use_mmap_if_available setting. */ if (config_lookup_string(config.cfg, "alsa.use_mmap_if_available", &str)) { @@ -1107,7 +1120,7 @@ static int init(int argc, char **argv) { else { warn("Invalid use_mmap_if_available option choice \"%s\". It should be " "\"yes\" or \"no\". " - "It is set to \"yes\"."); + "It remains set to \"yes\"."); config.no_mmap = 0; } } @@ -1163,7 +1176,7 @@ static int init(int argc, char **argv) { else { warn("Invalid disable_standby_mode option choice \"%s\". It should be " "\"always\", \"auto\" or \"never\". " - "It is set to \"never\"."); + "It remains set to \"never\"."); } } @@ -1179,7 +1192,7 @@ static int init(int argc, char **argv) { else { warn("Invalid use_precision_timing option choice \"%s\". It should be " "\"yes\", \"auto\" or \"no\". " - "It is set to \"auto\"."); + "It remains set to \"%s\".", config.use_precision_timing == YNA_NO ? "no" : config.use_precision_timing == YNA_AUTO ? "auto" : "yes"); } } diff --git a/scripts/shairport-sync.conf b/scripts/shairport-sync.conf index cd342d8e..a17d6337 100644 --- a/scripts/shairport-sync.conf +++ b/scripts/shairport-sync.conf @@ -84,7 +84,7 @@ alsa = // mixer_control_name = "PCM"; // the name of the mixer to use to adjust output volume. If not specified, volume in adjusted in software. // mixer_device = "default"; // the mixer_device default is whatever the output_device is. Normally you wouldn't have to use this. // output_rate = "auto"; // can be "auto", 44100, 88200, 176400 or 352800, but the device must have the capability. -// output_format = "auto"; // can be "auto", ""U8", "S8", "S16", "S16_LE", "S16_BE", "S24", "S24_LE", "S24_BE", "S24_3LE", "S24_3BE", "S32", "S32_LE" or "S32_BE" but the device must have the capability. Except where stated using (*LE or *BE), endianness matches that of the processor. +// output_format = "auto"; // can be "auto", "U8", "S8", "S16", "S16_LE", "S16_BE", "S24", "S24_LE", "S24_BE", "S24_3LE", "S24_3BE", "S32", "S32_LE" or "S32_BE" but the device must have the capability. Except where stated using (*LE or *BE), endianness matches that of the processor. // disable_synchronization = "no"; // Set to "yes" to disable synchronization. Default is "no". // period_size = ; // Use this optional advanced setting to set the alsa period size near to this value // buffer_size = ; // Use this optional advanced setting to set the alsa buffer size near to this value @@ -200,8 +200,8 @@ mqtt = // enabled = "no"; // set this to yes to enable the mqtt-metadata-service // hostname = "iot.eclipse.org"; // Hostname of the MQTT Broker // port = 1883; // Port on the MQTT Broker to connect to -// username = NULL; //set this to a string with your username, to enable username authentication -// password = NULL; //set this to a string with your password, to enable username & password authentication +// username = NULL; //set this to a string to your username in order to enable username authentication +// password = NULL; //set this to a string you your password in order to enable username & password authentication // capath = NULL; //set this to the folder with the CA-Certificates to be accepted for the server certificate. If not set, TLS is not used // cafile = NULL; //this may be used as an (exclusive) alternative to capath with a single file for all ca-certificates // certfile = NULL; //set this to a string to a user certificate to enable MQTT Client certificates. keyfile must also be set! @@ -216,7 +216,7 @@ mqtt = // Available commands are "command", "beginff", "beginrew", "mutetoggle", "nextitem", "previtem", "pause", "playpause", "play", "stop", "playresume", "shuffle_songs", "volumedown", "volumeup" } -// Diagnostic settings. These are for diagnostic and debugging only. Normally you sould leave them commented out +// Diagnostic settings. These are for diagnostic and debugging only. Normally you should leave them commented out diagnostics = { // disable_resend_requests = "no"; // set this to yes to stop Shairport Sync from requesting the retransmission of missing packets. Default is "no". From 7f2e6b1aac6740168c7fac5b1054ed234e420159 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Wed, 29 May 2019 16:18:51 +0100 Subject: [PATCH 03/33] Add support for S32_LE, S32_BE, S24_LE and S24_BE wqhen generating frames of dithered silence --- common.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/common.c b/common.c index b097ddab..0f72f6f9 100644 --- a/common.c +++ b/common.c @@ -1442,6 +1442,30 @@ int64_t generate_zero_frames(char *outp, size_t number_of_frames, enum sps_forma *(int32_t *)op = hyper_sample; result = 4; break; + case SPS_FORMAT_S32_LE: + hyper_sample >>= (64 - 32); + byt = (uint8_t)hyper_sample; + *op++ = byt; + byt = (uint8_t)(hyper_sample >> 8); + *op++ = byt; + byt = (uint8_t)(hyper_sample >> 16); + *op++ = byt; + byt = (uint8_t)(hyper_sample >> 24); + *op++ = byt; + result = 4; + break; + case SPS_FORMAT_S32_BE: + hyper_sample >>= (64 - 32); + byt = (uint8_t)(hyper_sample >> 24); + *op++ = byt; + byt = (uint8_t)(hyper_sample >> 16); + *op++ = byt; + byt = (uint8_t)(hyper_sample >> 8); + *op++ = byt; + byt = (uint8_t)hyper_sample; + *op++ = byt; + result = 4; + break; case SPS_FORMAT_S24_3LE: hyper_sample >>= (64 - 24); byt = (uint8_t)hyper_sample; @@ -1467,6 +1491,28 @@ int64_t generate_zero_frames(char *outp, size_t number_of_frames, enum sps_forma *(int32_t *)op = hyper_sample; result = 4; break; + case SPS_FORMAT_S24_LE: + hyper_sample >>= (64 - 24); + byt = (uint8_t)hyper_sample; + *op++ = byt; + byt = (uint8_t)(hyper_sample >> 8); + *op++ = byt; + byt = (uint8_t)(hyper_sample >> 16); + *op++ = byt; + *op++ = 0; + result = 4; + break; + case SPS_FORMAT_S24_BE: + hyper_sample >>= (64 - 24); + *op++ = 0; + byt = (uint8_t)(hyper_sample >> 16); + *op++ = byt; + byt = (uint8_t)(hyper_sample >> 8); + *op++ = byt; + byt = (uint8_t)hyper_sample; + *op++ = byt; + result = 4; + break; case SPS_FORMAT_S16_LE: hyper_sample >>= (64 - 16); byt = (uint8_t)hyper_sample; @@ -1501,7 +1547,7 @@ int64_t generate_zero_frames(char *outp, size_t number_of_frames, enum sps_forma break; default: result = 0; // stop a compiler warning - die("Unexpected SPS_FORMAT_UNKNOWN while outputting samples"); + die("Unexpected SPS_FORMAT_* with index %d while outputting silence",format); } p += result; previous_random_number = r; From d91e41d976e3ea15f441eb96b2d6446b84586882 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Wed, 29 May 2019 17:26:44 +0100 Subject: [PATCH 04/33] Add missing support for S32_LE, S32_BE, S24_LE and S24_LE formats. --- player.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/player.c b/player.c index 08c9203e..1cd88ea9 100644 --- a/player.c +++ b/player.c @@ -1673,10 +1673,15 @@ void *player_thread_func(void *arg) { case SPS_FORMAT_S24_3BE: conn->output_bytes_per_frame = 6; break; + case SPS_FORMAT_S24: + case SPS_FORMAT_S24_LE: + case SPS_FORMAT_S24_BE: conn->output_bytes_per_frame = 8; break; case SPS_FORMAT_S32: + case SPS_FORMAT_S32_LE: + case SPS_FORMAT_S32_BE: conn->output_bytes_per_frame = 8; break; default: From 25f0fbc24da7f115bb6a509259b8c5993460f3bc Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Sun, 2 Jun 2019 13:42:19 +0100 Subject: [PATCH 05/33] Update INSTALL.md --- INSTALL.md | 1 - 1 file changed, 1 deletion(-) diff --git a/INSTALL.md b/INSTALL.md index 33058c8c..8d8dc7df 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -9,7 +9,6 @@ Do the usual update and upgrade: ``` # apt-get update # apt-get upgrade -# rpi-update ``` (Separately, if you haven't done so already, consider using the `raspi-config` tool to expand the file system to use the entire card.) From 85cc1bdd97991547424e646483b3e0fcd78f1137 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Sun, 2 Jun 2019 17:56:47 +0100 Subject: [PATCH 06/33] Fix the dither calculation -- it was 12 dB too high, really noticable on the U8 setting. --- common.c | 12 +++++++----- player.c | 12 +++++++----- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/common.c b/common.c index 0f72f6f9..9775b07e 100644 --- a/common.c +++ b/common.c @@ -1376,12 +1376,14 @@ int64_t generate_zero_frames(char *outp, size_t number_of_frames, enum sps_forma int64_t hyper_sample = 0; // add a TPDF dither -- see - // http://www.users.qwest.net/%7Evolt42/cadenzarecording/DitherExplained.pdf + // http://educypedia.karadimov.info/library/DitherExplained.pdf // and the discussion around https://www.hydrogenaud.io/forums/index.php?showtopic=16963&st=25 // I think, for a 32 --> 16 bits, the range of // random numbers needs to be from -2^16 to 2^16, i.e. from -65536 to 65536 inclusive, not from // -32768 to +32767 + + // Actually, what would be generated here is from -65535 to 65535, i.e. one less on the limits. // See the original paper at // http://www.ece.rochester.edu/courses/ECE472/resources/Papers/Lipshitz_1992.pdf @@ -1392,23 +1394,23 @@ int64_t generate_zero_frames(char *outp, size_t number_of_frames, enum sps_forma case SPS_FORMAT_S32: case SPS_FORMAT_S32_LE: case SPS_FORMAT_S32_BE: - dither_mask = (int64_t)1 << (64 + 1 - 32); + dither_mask = (int64_t)1 << (64 - 32); break; case SPS_FORMAT_S24: case SPS_FORMAT_S24_LE: case SPS_FORMAT_S24_BE: case SPS_FORMAT_S24_3LE: case SPS_FORMAT_S24_3BE: - dither_mask = (int64_t)1 << (64 + 1 - 24); + dither_mask = (int64_t)1 << (64 - 24); break; case SPS_FORMAT_S16: case SPS_FORMAT_S16_LE: case SPS_FORMAT_S16_BE: - dither_mask = (int64_t)1 << (64 + 1 - 16); + dither_mask = (int64_t)1 << (64 - 16); break; case SPS_FORMAT_S8: case SPS_FORMAT_U8: - dither_mask = (int64_t)1 << (64 + 1 - 8); + dither_mask = (int64_t)1 << (64 - 8); break; case SPS_FORMAT_UNKNOWN: die("Unexpected SPS_FORMAT_UNKNOWN while calculating dither mask."); diff --git a/player.c b/player.c index 1cd88ea9..92bda3a5 100644 --- a/player.c +++ b/player.c @@ -650,12 +650,14 @@ static inline void process_sample(int32_t sample, char **outp, enum sps_format_t if (dither) { // add a TPDF dither -- see - // http://www.users.qwest.net/%7Evolt42/cadenzarecording/DitherExplained.pdf + // http://educypedia.karadimov.info/library/DitherExplained.pdf // and the discussion around https://www.hydrogenaud.io/forums/index.php?showtopic=16963&st=25 // I think, for a 32 --> 16 bits, the range of // random numbers needs to be from -2^16 to 2^16, i.e. from -65536 to 65536 inclusive, not from // -32768 to +32767 + + // Actually, what would be generated here is from -65535 to 65535, i.e. one less on the limits. // See the original paper at // http://www.ece.rochester.edu/courses/ECE472/resources/Papers/Lipshitz_1992.pdf @@ -666,23 +668,23 @@ static inline void process_sample(int32_t sample, char **outp, enum sps_format_t case SPS_FORMAT_S32: case SPS_FORMAT_S32_LE: case SPS_FORMAT_S32_BE: - dither_mask = (int64_t)1 << (64 + 1 - 32); + dither_mask = (int64_t)1 << (64 - 32); break; case SPS_FORMAT_S24: case SPS_FORMAT_S24_LE: case SPS_FORMAT_S24_BE: case SPS_FORMAT_S24_3LE: case SPS_FORMAT_S24_3BE: - dither_mask = (int64_t)1 << (64 + 1 - 24); + dither_mask = (int64_t)1 << (64 - 24); break; case SPS_FORMAT_S16: case SPS_FORMAT_S16_LE: case SPS_FORMAT_S16_BE: - dither_mask = (int64_t)1 << (64 + 1 - 16); + dither_mask = (int64_t)1 << (64 - 16); break; case SPS_FORMAT_S8: case SPS_FORMAT_U8: - dither_mask = (int64_t)1 << (64 + 1 - 8); + dither_mask = (int64_t)1 << (64 - 8); break; case SPS_FORMAT_UNKNOWN: die("Unexpected SPS_FORMAT_UNKNOWN while calculating dither mask."); From 0c5c4bfe3f9618f63695b3127404272b1ec81a06 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Sun, 2 Jun 2019 18:04:51 +0100 Subject: [PATCH 07/33] Update RELEASENOTES-DEVELOPMENT.md --- RELEASENOTES-DEVELOPMENT.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index 17d06b2d..27f17189 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -1,3 +1,11 @@ +The Inevitable Version 3.3.1rc0 +==== +**Bug Fixes** +* Fix a bug in the MQTT documentation and add sanity checking for the port chosen -- thanks to [David Crook](https://github.com/idcrook). +* Fix a bug that caused manual format and rate setting to be ignored -- thanks to [Jörg Krause](https://github.com/joerg-krause). +* Add missing support for format settings S24_LE, S24_BE, S32_LE and S32_BE. +* Fix a bug that caused dither to be too loud. + Version 3.3rc8 ==== **Enhancements** From 9d96906402529045934ed24d0a5909378b886d36 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Sun, 2 Jun 2019 18:05:40 +0100 Subject: [PATCH 08/33] Update configure.ac --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 3df35d40..fae0f675 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.50]) -AC_INIT([shairport-sync], [3.3], [mikebrady@eircom.net]) +AC_INIT([shairport-sync], [3.3.1rc0], [mikebrady@eircom.net]) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([shairport.c]) AC_CONFIG_HEADERS([config.h]) From 4bafe1c982e33392e72e16068c1005b3c6c5fe07 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 3 Jun 2019 10:03:54 +0100 Subject: [PATCH 09/33] quieten a compilation warning --- audio_alsa.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/audio_alsa.c b/audio_alsa.c index a6de218c..f24d02d1 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -192,7 +192,9 @@ static void help(void) { " -c mixer-control set the mixer control name, default is to use no mixer.\n" " -m mixer-device set the mixer device, default is the output device.\n" " -i mixer-index set the mixer index, default is 0.\n"); - system("if [ -d /proc/asound ] ; then echo \" hardware output devices:\" ; ls -al /proc/asound/ 2>/dev/null | grep '\\->' | tr -s ' ' | cut -d ' ' -f 9 | while read line; do echo \" \\\"hw:$line\\\"\" ; done ; fi"); + int r = system("if [ -d /proc/asound ] ; then echo \" hardware output devices:\" ; ls -al /proc/asound/ 2>/dev/null | grep '\\->' | tr -s ' ' | cut -d ' ' -f 9 | while read line; do echo \" \\\"hw:$line\\\"\" ; done ; fi"); + if (r != 0) + debug(2, "error %d executing a script to list alsa hardware device names", r); } void set_alsa_out_dev(char *dev) { alsa_out_dev = dev; } From 3ca3408255c042d47d1a3acfa4361fb1ee72b1d8 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Tue, 4 Jun 2019 15:32:10 +0100 Subject: [PATCH 10/33] Fix error message for invalid standby mode choice --- audio_alsa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audio_alsa.c b/audio_alsa.c index f24d02d1..0c2702d9 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -1178,7 +1178,7 @@ static int init(int argc, char **argv) { else { warn("Invalid disable_standby_mode option choice \"%s\". It should be " "\"always\", \"auto\" or \"never\". " - "It remains set to \"never\"."); + "It remains set to \"never\".", str); } } From bf91d35bfb6b4ea65abefe5ac915c5f8dc29b2e6 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Tue, 4 Jun 2019 15:33:30 +0100 Subject: [PATCH 11/33] Update RELEASENOTES-DEVELOPMENT.md --- RELEASENOTES-DEVELOPMENT.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index 27f17189..06e0520f 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -1,10 +1,11 @@ -The Inevitable Version 3.3.1rc0 +The Inevitable Version 3.3.1rc1 ==== **Bug Fixes** * Fix a bug in the MQTT documentation and add sanity checking for the port chosen -- thanks to [David Crook](https://github.com/idcrook). * Fix a bug that caused manual format and rate setting to be ignored -- thanks to [Jörg Krause](https://github.com/joerg-krause). * Add missing support for format settings S24_LE, S24_BE, S32_LE and S32_BE. * Fix a bug that caused dither to be too loud. +* Fix error message for invalid standby mode choice. Version 3.3rc8 ==== From 854942df9232fce0bc375dc94eae8a4ed5f19b20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Krause?= Date: Tue, 4 Jun 2019 20:47:43 +0200 Subject: [PATCH 12/33] Debug requested period size value --- audio_alsa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audio_alsa.c b/audio_alsa.c index 0c2702d9..956deba5 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -512,7 +512,7 @@ int actual_open_alsa_device(int do_auto_setup) { } if (set_period_size_request != 0) { - debug(1, "Attempting to set the period size"); + debug(1, "Attempting to set the period size to %lu", period_size_requested); ret = snd_pcm_hw_params_set_period_size_near(alsa_handle, alsa_params, &period_size_requested, &dir); if (ret < 0) { From 9d624e40c5a540c5adaa7540f4304919d22e2df7 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Wed, 5 Jun 2019 15:31:08 +0100 Subject: [PATCH 13/33] Update RELEASENOTES-DEVELOPMENT.md --- RELEASENOTES-DEVELOPMENT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index 06e0520f..55dc9ad4 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -5,7 +5,7 @@ The Inevitable Version 3.3.1rc1 * Fix a bug that caused manual format and rate setting to be ignored -- thanks to [Jörg Krause](https://github.com/joerg-krause). * Add missing support for format settings S24_LE, S24_BE, S32_LE and S32_BE. * Fix a bug that caused dither to be too loud. -* Fix error message for invalid standby mode choice. +* Fix error message for invalid `disable_standby_mode` choice -- thanks to [Tim Curtis](https://github.com/moodeaudio) at [Moode Audio](https://moodeaudio.org). Version 3.3rc8 ==== From 413df6d063bc0d865ff9e969cd0194525394644d Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Fri, 7 Jun 2019 17:48:45 +0100 Subject: [PATCH 14/33] Correct a compilation error in Alpine Linux --- dacp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dacp.c b/dacp.c index cea3e7f5..7f162f79 100644 --- a/dacp.c +++ b/dacp.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include "metadata_hub.h" From dbea82c2120e16f04c8fd4e1dba80811dae08a9f Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 17 Jun 2019 07:54:04 +0100 Subject: [PATCH 15/33] restore full code for checking for precise timing but stop making it necessary for disable standby mode. --- audio_alsa.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 5 deletions(-) diff --git a/audio_alsa.c b/audio_alsa.c index 956deba5..f56cb478 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -162,12 +162,70 @@ int standard_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay, // use this to allow the use of standard or precision delay calculations, with standard the, uh, standard. int (*delay_and_status)(snd_pcm_state_t *state, snd_pcm_sframes_t *delay, enum yndk_type *using_update_timestamps) = standard_delay_and_status; +// this will return true if the DAC can return precision delay information and false if not +// if it is not yet known, it will test the output device to find out + +// note -- once it has done the test, it decides -- even if the delay comes back with +// "don't know", it will take that as a "No" and remember it. +// If you want it to check again, set precision_delay_available_status to YNDK_DONT_KNOW +// first. + int precision_delay_available() { - // this is very crude -- if the device is a hardware device, then it's assumed the delay is precise - const char *output_device_name = snd_pcm_name(alsa_handle); - return (strstr(output_device_name,"hw:") == output_device_name); + if (precision_delay_available_status == YNDK_DONT_KNOW) { + // At present, the only criterion as to whether precision delay is available + // is whether the device driver returns non-zero update timestamps + // If it does, it is considered precision delay is available + // Otherwise, it's considered to be unavailable + + // To test, we play a silence buffer (fairly large to avoid underflow) + // and then we check the delay return. It will tell us if it + // was able to use the (non-zero) update timestamps + + int frames_of_silence = 4410; + size_t size_of_silence_buffer = frames_of_silence * frame_size; + void *silence = malloc(size_of_silence_buffer); + if (silence == NULL) { + debug(1, "alsa: precision_delay_available -- failed to " + "allocate memory for a " + "silent frame buffer."); + } else { + pthread_cleanup_push(malloc_cleanup, silence); + int use_dither = 0; + if ((hardware_mixer == 0) && (config.ignore_volume_control == 0) && + (config.airplay_volume != 0.0)) + use_dither = 1; + dither_random_number_store = + generate_zero_frames(silence, frames_of_silence, config.output_format, + use_dither, // i.e. with dither + dither_random_number_store); + // debug(1,"Play %d frames of silence with most_recent_write_time of + // %" PRIx64 ".", + // frames_of_silence,most_recent_write_time); + do_play(silence, frames_of_silence); + pthread_cleanup_pop(1); + // now we can get the delay, and we'll note if it uses update timestamps + enum yndk_type uses_update_timestamps; + snd_pcm_state_t state; + snd_pcm_sframes_t delay; + int ret = delay_and_status(&state, &delay, &uses_update_timestamps); + // debug(3,"alsa: precision_delay_available asking for delay and status with a return status of %d, a delay of %ld and a uses_update_timestamps of %d.", ret, delay, uses_update_timestamps); + if (ret == 0) { + if (uses_update_timestamps == YNDK_YES) { + precision_delay_available_status = YNDK_YES; + debug(2,"alsa: precision delay timing available."); + } else { + precision_delay_available_status = YNDK_NO; + debug(2,"alsa: precision delay timing not available."); + if (config.disable_standby_mode != disable_standby_off) + inform("Note: disable_standby_mode has been turned off because the output device is not capable of precision delay timing."); + } + } + } + } + return (precision_delay_available_status == YNDK_YES); } + // static int play_number; // static int64_t accumulated_delay, accumulated_da_delay; int alsa_characteristics_already_listed = 0; @@ -1850,8 +1908,8 @@ void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) { // and config.keep_dac_busy is true (at the present, this has to be the case // to be in the // abm_connected state in the first place...) then do the silence-filling - // thing, if needed, and if the output device is capable of precision delay. - if ((alsa_backend_state != abm_disconnected) && (config.keep_dac_busy != 0) && precision_delay_available()) { + // thing, if needed /* only if the output device is capable of precision delay */. + if ((alsa_backend_state != abm_disconnected) && (config.keep_dac_busy != 0) /* && precision_delay_available() */ ) { int reply; long buffer_size = 0; snd_pcm_state_t state; From cd9982c43b0d0f8d952dbc2905590ef0a7d6398d Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 17 Jun 2019 09:39:04 +0100 Subject: [PATCH 16/33] if disable_standby_mode is enabled, disable if if more than 20 underruns occur for 50 silence buffers at the start. --- audio_alsa.c | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/audio_alsa.c b/audio_alsa.c index f56cb478..0913a174 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -66,6 +66,7 @@ void *alsa_buffer_monitor_thread_code(void *arg); static void volume(double vol); void do_volume(double vol); int prepare(void); +int do_play(void *buf, int samples); static void parameters(audio_parameters *info); int mute(int do_mute); // returns true if it actually is allowed to use the mute @@ -120,6 +121,8 @@ int frame_size; // in bytes for interleaved stereo int alsa_device_initialised; // boolean to ensure the initialisation is only // done once + +enum yndk_type precision_delay_available_status = YNDK_DONT_KNOW; // initially, we don't know if the device can do precision delay snd_pcm_t *alsa_handle = NULL; static snd_pcm_hw_params_t *alsa_params = NULL; @@ -212,12 +215,12 @@ int precision_delay_available() { if (ret == 0) { if (uses_update_timestamps == YNDK_YES) { precision_delay_available_status = YNDK_YES; - debug(2,"alsa: precision delay timing available."); + debug(2,"alsa: precision delay timing is available."); } else { precision_delay_available_status = YNDK_NO; - debug(2,"alsa: precision delay timing not available."); - if (config.disable_standby_mode != disable_standby_off) - inform("Note: disable_standby_mode has been turned off because the output device is not capable of precision delay timing."); + debug(2,"alsa: precision delay timing is not available."); + // if (config.disable_standby_mode != disable_standby_off) + // inform("Note: disable_standby_mode has been turned off because the output device is not capable of precision delay timing."); } } } @@ -1622,19 +1625,20 @@ int do_play(void *buf, int samples) { measurement_data_is_valid = 0; if (ret == -EPIPE) { /* underrun */ debug(1, "alsa: underrun while writing %d samples to alsa device.", samples); - ret = snd_pcm_recover(alsa_handle, ret, debuglev > 0 ? 1 : 0); - if (ret < 0) { - warn("alsa: can't recover from SND_PCM_STATE_XRUN: %s.", snd_strerror(ret)); + int tret = snd_pcm_recover(alsa_handle, ret, debuglev > 0 ? 1 : 0); + if (tret < 0) { + warn("alsa: can't recover from SND_PCM_STATE_XRUN: %s.", snd_strerror(tret)); } } else if (ret == -ESTRPIPE) { /* suspended */ debug(1, "alsa: suspended while writing %d samples to alsa device.", samples); - while ((ret = snd_pcm_resume(alsa_handle)) == -EAGAIN) { + int tret; + while ((tret = snd_pcm_resume(alsa_handle)) == -EAGAIN) { sleep(1); /* wait until the suspend flag is released */ - if (ret < 0) { + if (tret < 0) { warn("alsa: can't recover from SND_PCM_STATE_SUSPENDED state, " "snd_pcm_prepare() " "failed: %s.", - snd_strerror(ret)); + snd_strerror(tret)); } } } else { @@ -1875,8 +1879,11 @@ void alsa_buffer_monitor_thread_cleanup_function(__attribute__((unused)) void */ void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) { + int frame_count = 0; + int error_count = 0; + int error_threshold_exceeded = 0; int okb = -1; - while (1) { + while (error_threshold_exceeded == 0) { // if too many play errors occur early on, we will turn off the disable stanby mode if (okb != config.keep_dac_busy) { debug(2,"keep_dac_busy is now \"%s\"",config.keep_dac_busy == 0 ? "no" : "yes"); okb = config.keep_dac_busy; @@ -1944,6 +1951,7 @@ void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) { "allocate memory for a " "silent frame buffer."); } else { + int ret; pthread_cleanup_push(malloc_cleanup, silence); int use_dither = 0; if ((hardware_mixer == 0) && (config.ignore_volume_control == 0) && @@ -1956,8 +1964,19 @@ void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) { // debug(1,"Play %d frames of silence with most_recent_write_time of // %" PRIx64 ".", // frames_of_silence,most_recent_write_time); - do_play(silence, frames_of_silence); + ret = do_play(silence, frames_of_silence); + frame_count++; pthread_cleanup_pop(1); + if (ret < 0) { + error_count++; + char errorstring[1024]; + strerror_r(-ret, (char *)errorstring, sizeof(errorstring)); + debug(2, "alsa: alsa_buffer_monitor_thread_code error %d (\"%s\") writing %d samples to alsa device -- %d errors in %d trials.", ret, (char *)errorstring, frames_of_silence, error_count, frame_count); + if ((error_count > 20) && (frame_count < 50)) { + warn("disable_standby_mode has been turned off because too many underruns occurred. Are you outputting to a virtual device or running in a virtual machine?"); + error_threshold_exceeded = 1; + } + } } } } From 65abca2562cc86997b39c0589ad6c62abc949b70 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 17 Jun 2019 09:47:56 +0100 Subject: [PATCH 17/33] change the threshold for turning off disable standby mode to more than 40 underruns in the first 100 writes. --- audio_alsa.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/audio_alsa.c b/audio_alsa.c index 0913a174..bac1d4b6 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -219,8 +219,6 @@ int precision_delay_available() { } else { precision_delay_available_status = YNDK_NO; debug(2,"alsa: precision delay timing is not available."); - // if (config.disable_standby_mode != disable_standby_off) - // inform("Note: disable_standby_mode has been turned off because the output device is not capable of precision delay timing."); } } } @@ -1972,8 +1970,8 @@ void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) { char errorstring[1024]; strerror_r(-ret, (char *)errorstring, sizeof(errorstring)); debug(2, "alsa: alsa_buffer_monitor_thread_code error %d (\"%s\") writing %d samples to alsa device -- %d errors in %d trials.", ret, (char *)errorstring, frames_of_silence, error_count, frame_count); - if ((error_count > 20) && (frame_count < 50)) { - warn("disable_standby_mode has been turned off because too many underruns occurred. Are you outputting to a virtual device or running in a virtual machine?"); + if ((error_count > 40) && (frame_count < 100)) { + warn("disable_standby_mode has been turned off because too many underruns occurred. Is Shairport Sync outputting to a virtual device or running in a virtual machine?"); error_threshold_exceeded = 1; } } From a46c761aa680d25609e60c663aad25e54dceca16 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 17 Jun 2019 10:04:23 +0100 Subject: [PATCH 18/33] make snd_pcm_recover silent when the debug level is 0, not the other way around, duh. --- audio_alsa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audio_alsa.c b/audio_alsa.c index bac1d4b6..692d73c6 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -1623,7 +1623,7 @@ int do_play(void *buf, int samples) { measurement_data_is_valid = 0; if (ret == -EPIPE) { /* underrun */ debug(1, "alsa: underrun while writing %d samples to alsa device.", samples); - int tret = snd_pcm_recover(alsa_handle, ret, debuglev > 0 ? 1 : 0); + int tret = snd_pcm_recover(alsa_handle, ret, debuglev > 0 ? 0 : 1); if (tret < 0) { warn("alsa: can't recover from SND_PCM_STATE_XRUN: %s.", snd_strerror(tret)); } From 9199bf24ddcc352d3728e25a12dbe2d746a5951e Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 17 Jun 2019 10:38:20 +0100 Subject: [PATCH 19/33] fix the detection of the availability of precision timing information -- actuall, renable it --- audio_alsa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audio_alsa.c b/audio_alsa.c index 692d73c6..26a0014d 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -210,7 +210,7 @@ int precision_delay_available() { enum yndk_type uses_update_timestamps; snd_pcm_state_t state; snd_pcm_sframes_t delay; - int ret = delay_and_status(&state, &delay, &uses_update_timestamps); + int ret = precision_delay_and_status(&state, &delay, &uses_update_timestamps); // debug(3,"alsa: precision_delay_available asking for delay and status with a return status of %d, a delay of %ld and a uses_update_timestamps of %d.", ret, delay, uses_update_timestamps); if (ret == 0) { if (uses_update_timestamps == YNDK_YES) { From 7013b0cc3d1fe9edead493f0a6f97d3a0ca44ffe Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 17 Jun 2019 19:43:23 +0100 Subject: [PATCH 20/33] refine the selection of precision timing to be allowed only if the device is a real hardware device --- audio_alsa.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/audio_alsa.c b/audio_alsa.c index 26a0014d..2ef94661 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -175,10 +175,14 @@ int (*delay_and_status)(snd_pcm_state_t *state, snd_pcm_sframes_t *delay, enum y int precision_delay_available() { if (precision_delay_available_status == YNDK_DONT_KNOW) { - // At present, the only criterion as to whether precision delay is available + // this is very crude -- if the device is a hardware device, then it's assumed the delay is precise + const char *output_device_name = snd_pcm_name(alsa_handle); + int is_a_real_hardware_device = (strstr(output_device_name,"hw:") == output_device_name); + + // The criteria as to whether precision delay is available // is whether the device driver returns non-zero update timestamps - // If it does, it is considered precision delay is available - // Otherwise, it's considered to be unavailable + // If it does, and the device is a hardware device (i.e. its name begins with "hw:0"), + // it is considered that precision delay is available. Otherwise, it's considered to be unavailable. // To test, we play a silence buffer (fairly large to avoid underflow) // and then we check the delay return. It will tell us if it @@ -213,12 +217,16 @@ int precision_delay_available() { int ret = precision_delay_and_status(&state, &delay, &uses_update_timestamps); // debug(3,"alsa: precision_delay_available asking for delay and status with a return status of %d, a delay of %ld and a uses_update_timestamps of %d.", ret, delay, uses_update_timestamps); if (ret == 0) { - if (uses_update_timestamps == YNDK_YES) { + if ((uses_update_timestamps == YNDK_YES) && (is_a_real_hardware_device)) { precision_delay_available_status = YNDK_YES; debug(2,"alsa: precision delay timing is available."); } else { - precision_delay_available_status = YNDK_NO; - debug(2,"alsa: precision delay timing is not available."); + if ((uses_update_timestamps == YNDK_YES) && (!is_a_real_hardware_device)) { + debug(2,"alsa: precision delay timing is not available because it's not definitely a hardware device."); + } else { + debug(2,"alsa: precision delay timing is not available."); + } + precision_delay_available_status = YNDK_NO; } } } @@ -1435,14 +1443,13 @@ int precision_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay, // user information if (update_timestamp_ns == 0) { if (delay_type_notified != 1) { - inform("Note: the alsa output device \"%s\" is not capable of high precision delay timing.", snd_pcm_name(alsa_handle)); - debug(1,"alsa: delay_and_status must use snd_pcm_delay() to calculate delay"); + debug(2,"alsa: update timestamps unavailable"); delay_type_notified = 1; } } else { // diagnostic if (delay_type_notified != 0) { - debug(2,"alsa: delay_and_status using snd_pcm_status_get_delay() to calculate delay"); + debug(2,"alsa: update timestamps available"); delay_type_notified = 0; } } From 1ca92262425a827783a65026d684728152eea8d4 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 17 Jun 2019 22:25:47 +0100 Subject: [PATCH 21/33] Update audio_alsa.c --- audio_alsa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audio_alsa.c b/audio_alsa.c index 2ef94661..7eab2178 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -181,7 +181,7 @@ int precision_delay_available() { // The criteria as to whether precision delay is available // is whether the device driver returns non-zero update timestamps - // If it does, and the device is a hardware device (i.e. its name begins with "hw:0"), + // If it does, and the device is a hardware device (i.e. its name begins with "hw:"), // it is considered that precision delay is available. Otherwise, it's considered to be unavailable. // To test, we play a silence buffer (fairly large to avoid underflow) From 34b96729a2cd0a7f557de068a69be4c406136126 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 17 Jun 2019 22:27:03 +0100 Subject: [PATCH 22/33] Refine the precision timing code Only allow it if the output device is a hardware device --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 943cf4be..ba439484 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.50]) -AC_INIT([shairport-sync], [3.3.2d0], [mikebrady@eircom.net]) +AC_INIT([shairport-sync], [3.3.2d1], [mikebrady@eircom.net]) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([shairport.c]) AC_CONFIG_HEADERS([config.h]) From 55488ad24c518ae2d5893351ca577ed3e2febb62 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 17 Jun 2019 22:30:44 +0100 Subject: [PATCH 23/33] Update RELEASENOTES-DEVELOPMENT.md --- RELEASENOTES-DEVELOPMENT.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index 55dc9ad4..bcd33c62 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -1,3 +1,8 @@ +Version 3.3.2d1 +==== +**Enhancements** +* Allow `disable_standby_mode` to operate on any output device, not just real hardware devices. If there are too many underruns while outputting silence, the disable standby mode will be turned off. + The Inevitable Version 3.3.1rc1 ==== **Bug Fixes** From 7fc977d8d346d90c1f262dbd033c0bb431fde919 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 17 Jun 2019 22:44:12 +0100 Subject: [PATCH 24/33] Change comments for use_precision_timing --- scripts/shairport-sync.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/shairport-sync.conf b/scripts/shairport-sync.conf index a17d6337..59e1c339 100644 --- a/scripts/shairport-sync.conf +++ b/scripts/shairport-sync.conf @@ -91,8 +91,8 @@ alsa = // use_mmap_if_available = "yes"; // Use this optional advanced setting to control whether MMAP-based output is used to communicate with the DAC. Default is "yes" // use_hardware_mute_if_available = "no"; // Use this optional advanced setting to control whether the hardware in the DAC is used for muting. Default is "no", for compatibility with other audio players. // maximum_stall_time = 0.200; // Use this optional advanced setting to control how long to wait for data to be consumed by the output device before considering it an error. It should never approach 200 ms. -// use_precision_timing = "auto"; // If the output device is a real hardware device, precision timing will be used, which is needed for "disable_standby_mode" below. Choose "no" for more compatible standard timing, choose yes to force the use of precision timing, which may cause problems. -// disable_standby_mode = "never"; // This setting prevents the DAC from entering the standby mode. Some DACs make small "popping" noises when they go in and out of standby mode. Settings can be: "always", "auto" or "never". Default is "never", but only for backwards compatibility. The "auto" setting prevents entry to standby mode while Shairport Sync is in the "active" mode. You can use "yes" instead of "always" and "no" instead of "never". Needs precision timing to be available. +// use_precision_timing = "auto"; // Use this optional advanced setting to control how Shairport Sync gathers timing information. When set to "auto", if the output device is a real hardware device, precision timing will be used. Choose "no" for more compatible standard timing, choose "yes" to force the use of precision timing, which may cause problems. +// disable_standby_mode = "never"; // This setting prevents the DAC from entering the standby mode. Some DACs make small "popping" noises when they go in and out of standby mode. Settings can be: "always", "auto" or "never". Default is "never", but only for backwards compatibility. The "auto" setting prevents entry to standby mode while Shairport Sync is in the "active" mode. You can use "yes" instead of "always" and "no" instead of "never". }; // Parameters for the "sndio" audio back end. All are optional. From c8b0be30b4d47f64bcc6f35dd0cb691f7979640c Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Wed, 19 Jun 2019 19:06:54 +0100 Subject: [PATCH 25/33] find . \( -name \*.cpp -or -name \*.h -or -name \*.c \) -exec clang-format -i -style='{BasedOnStyle: llvm, IndentWidth: 2, ColumnLimit: 100}' '{}' \; --- activity_monitor.c | 23 +- activity_monitor.h | 2 +- audio.c | 2 +- audio.h | 2 +- audio_alsa.c | 560 +++++++++++++++++++++++---------------------- audio_dummy.c | 1 - audio_jack.c | 77 +++---- audio_pipe.c | 9 +- audio_sndio.c | 7 +- audio_soundio.c | 5 +- common.c | 55 +++-- common.h | 40 ++-- dacp.c | 26 +-- dbus-service.c | 68 +++--- mdns_dns_sd.c | 2 +- metadata_hub.c | 26 +-- player.c | 20 +- rtp.c | 33 ++- shairport.c | 187 +++++++-------- 19 files changed, 575 insertions(+), 570 deletions(-) diff --git a/activity_monitor.c b/activity_monitor.c index c6966bcb..544f0362 100644 --- a/activity_monitor.c +++ b/activity_monitor.c @@ -46,7 +46,6 @@ #include "dbus-service.h" #endif - enum am_state state; enum ps_state { ps_inactive, ps_active } player_state; @@ -73,10 +72,10 @@ void going_active(int block) { if (config.disable_standby_mode == disable_standby_auto) { #ifdef CONFIG_DBUS_INTERFACE - if (dbus_service_is_running()) - shairport_sync_set_disable_standby(SHAIRPORT_SYNC(shairportSyncSkeleton), TRUE); - else - config.keep_dac_busy = 1; + if (dbus_service_is_running()) + shairport_sync_set_disable_standby(SHAIRPORT_SYNC(shairportSyncSkeleton), TRUE); + else + config.keep_dac_busy = 1; #else config.keep_dac_busy = 1; #endif @@ -100,12 +99,12 @@ void going_inactive(int block) { if (config.disable_standby_mode == disable_standby_auto) { #ifdef CONFIG_DBUS_INTERFACE - if (dbus_service_is_running()) - shairport_sync_set_disable_standby(SHAIRPORT_SYNC(shairportSyncSkeleton), FALSE); - else - config.keep_dac_busy = 0; + if (dbus_service_is_running()) + shairport_sync_set_disable_standby(SHAIRPORT_SYNC(shairportSyncSkeleton), FALSE); + else + config.keep_dac_busy = 0; #else - config.keep_dac_busy = 0; + config.keep_dac_busy = 0; #endif } } @@ -255,9 +254,7 @@ void *activity_monitor_thread_code(void *arg) { pthread_exit(NULL); } -enum am_state activity_status() { - return (state); -} +enum am_state activity_status() { return (state); } void activity_monitor_start() { // debug(1,"activity_monitor_start"); diff --git a/activity_monitor.h b/activity_monitor.h index d9e1ffd6..6c786523 100644 --- a/activity_monitor.h +++ b/activity_monitor.h @@ -5,4 +5,4 @@ enum am_state { am_inactive, am_active, am_timing_out }; void activity_monitor_start(); void activity_monitor_stop(); void activity_monitor_signify_activity(int active); // 0 means inactive, non-zero means active -enum am_state activity_status(); // true if non inactive; false if inactive +enum am_state activity_status(); // true if non inactive; false if inactive diff --git a/audio.c b/audio.c index 78686376..cd3f2cb2 100644 --- a/audio.c +++ b/audio.c @@ -116,7 +116,7 @@ void audio_ls_outputs(void) { printf("Settings and options for the audio backend \"%s\":\n", (*out)->name); (*out)->help(); } else { - printf("There are no settings or options for the audio backend \"%s\".\n", (*out)->name); + printf("There are no settings or options for the audio backend \"%s\".\n", (*out)->name); } } } diff --git a/audio.h b/audio.h index 5fc5f391..856f184b 100644 --- a/audio.h +++ b/audio.h @@ -18,7 +18,7 @@ typedef struct { int (*init)(int argc, char **argv); // at end of program void (*deinit)(void); - + int (*prepare)(void); // looks and sets stuff in the config data structure void (*start)(int sample_rate, int sample_format); diff --git a/audio_alsa.c b/audio_alsa.c index 7eab2178..99fb945c 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -37,9 +37,9 @@ #include "config.h" -#include "common.h" #include "activity_monitor.h" #include "audio.h" +#include "common.h" enum alsa_backend_mode { abm_disconnected, @@ -48,8 +48,8 @@ enum alsa_backend_mode { } alsa_backend_state; // under the control of alsa_mutex typedef struct { - snd_pcm_format_t alsa_code; - int frame_size; + snd_pcm_format_t alsa_code; + int frame_size; } format_record; static void help(void); @@ -72,8 +72,9 @@ static void parameters(audio_parameters *info); int mute(int do_mute); // returns true if it actually is allowed to use the mute static double set_volume; static int output_method_signalled = 0; // for reporting whether it's using mmap or not -int delay_type_notified = -1; // for controlling the reporting of whether the output device can do precison delays (e.g. alsa->pulsaudio virtual devices can't) -int use_monotonic_clock = 0; // this value will be set when the hardware is initialised +int delay_type_notified = -1; // for controlling the reporting of whether the output device can do + // precison delays (e.g. alsa->pulsaudio virtual devices can't) +int use_monotonic_clock = 0; // this value will be set when the hardware is initialised audio_output audio_alsa = { .name = "alsa", @@ -122,8 +123,9 @@ int frame_size; // in bytes for interleaved stereo int alsa_device_initialised; // boolean to ensure the initialisation is only // done once -enum yndk_type precision_delay_available_status = YNDK_DONT_KNOW; // initially, we don't know if the device can do precision delay - +enum yndk_type precision_delay_available_status = + YNDK_DONT_KNOW; // initially, we don't know if the device can do precision delay + snd_pcm_t *alsa_handle = NULL; static snd_pcm_hw_params_t *alsa_params = NULL; static snd_pcm_sw_params_t *alsa_swparams = NULL; @@ -155,15 +157,17 @@ int volume_based_mute_is_active = 0; // set when muting is being done by a setting the volume to a magic value // use this to allow the use of snd_pcm_writei or snd_pcm_mmap_writei -snd_pcm_sframes_t (*alsa_pcm_write)(snd_pcm_t *, const void *, - snd_pcm_uframes_t) = snd_pcm_writei; +snd_pcm_sframes_t (*alsa_pcm_write)(snd_pcm_t *, const void *, snd_pcm_uframes_t) = snd_pcm_writei; +int precision_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay, + enum yndk_type *using_update_timestamps); +int standard_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay, + enum yndk_type *using_update_timestamps); -int precision_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay, enum yndk_type *using_update_timestamps); -int standard_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay, enum yndk_type *using_update_timestamps); - -// use this to allow the use of standard or precision delay calculations, with standard the, uh, standard. -int (*delay_and_status)(snd_pcm_state_t *state, snd_pcm_sframes_t *delay, enum yndk_type *using_update_timestamps) = standard_delay_and_status; +// use this to allow the use of standard or precision delay calculations, with standard the, uh, +// standard. +int (*delay_and_status)(snd_pcm_state_t *state, snd_pcm_sframes_t *delay, + enum yndk_type *using_update_timestamps) = standard_delay_and_status; // this will return true if the DAC can return precision delay information and false if not // if it is not yet known, it will test the output device to find out @@ -175,19 +179,21 @@ int (*delay_and_status)(snd_pcm_state_t *state, snd_pcm_sframes_t *delay, enum y int precision_delay_available() { if (precision_delay_available_status == YNDK_DONT_KNOW) { - // this is very crude -- if the device is a hardware device, then it's assumed the delay is precise + // this is very crude -- if the device is a hardware device, then it's assumed the delay is + // precise const char *output_device_name = snd_pcm_name(alsa_handle); - int is_a_real_hardware_device = (strstr(output_device_name,"hw:") == output_device_name); + int is_a_real_hardware_device = (strstr(output_device_name, "hw:") == output_device_name); // The criteria as to whether precision delay is available // is whether the device driver returns non-zero update timestamps // If it does, and the device is a hardware device (i.e. its name begins with "hw:"), - // it is considered that precision delay is available. Otherwise, it's considered to be unavailable. - + // it is considered that precision delay is available. Otherwise, it's considered to be + // unavailable. + // To test, we play a silence buffer (fairly large to avoid underflow) - // and then we check the delay return. It will tell us if it + // and then we check the delay return. It will tell us if it // was able to use the (non-zero) update timestamps - + int frames_of_silence = 4410; size_t size_of_silence_buffer = frames_of_silence * frame_size; void *silence = malloc(size_of_silence_buffer); @@ -213,20 +219,23 @@ int precision_delay_available() { // now we can get the delay, and we'll note if it uses update timestamps enum yndk_type uses_update_timestamps; snd_pcm_state_t state; - snd_pcm_sframes_t delay; + snd_pcm_sframes_t delay; int ret = precision_delay_and_status(&state, &delay, &uses_update_timestamps); - // debug(3,"alsa: precision_delay_available asking for delay and status with a return status of %d, a delay of %ld and a uses_update_timestamps of %d.", ret, delay, uses_update_timestamps); + // debug(3,"alsa: precision_delay_available asking for delay and status with a return status + // of %d, a delay of %ld and a uses_update_timestamps of %d.", ret, delay, + // uses_update_timestamps); if (ret == 0) { if ((uses_update_timestamps == YNDK_YES) && (is_a_real_hardware_device)) { precision_delay_available_status = YNDK_YES; - debug(2,"alsa: precision delay timing is available."); + debug(2, "alsa: precision delay timing is available."); } else { if ((uses_update_timestamps == YNDK_YES) && (!is_a_real_hardware_device)) { - debug(2,"alsa: precision delay timing is not available because it's not definitely a hardware device."); + debug(2, "alsa: precision delay timing is not available because it's not definitely a " + "hardware device."); } else { - debug(2,"alsa: precision delay timing is not available."); - } - precision_delay_available_status = YNDK_NO; + debug(2, "alsa: precision delay timing is not available."); + } + precision_delay_available_status = YNDK_NO; } } } @@ -234,7 +243,6 @@ int precision_delay_available() { return (precision_delay_available_status == YNDK_YES); } - // static int play_number; // static int64_t accumulated_delay, accumulated_da_delay; int alsa_characteristics_already_listed = 0; @@ -259,7 +267,9 @@ static void help(void) { " -c mixer-control set the mixer control name, default is to use no mixer.\n" " -m mixer-device set the mixer device, default is the output device.\n" " -i mixer-index set the mixer index, default is 0.\n"); - int r = system("if [ -d /proc/asound ] ; then echo \" hardware output devices:\" ; ls -al /proc/asound/ 2>/dev/null | grep '\\->' | tr -s ' ' | cut -d ' ' -f 9 | while read line; do echo \" \\\"hw:$line\\\"\" ; done ; fi"); + int r = system("if [ -d /proc/asound ] ; then echo \" hardware output devices:\" ; ls -al " + "/proc/asound/ 2>/dev/null | grep '\\->' | tr -s ' ' | cut -d ' ' -f 9 | while " + "read line; do echo \" \\\"hw:$line\\\"\" ; done ; fi"); if (r != 0) debug(2, "error %d executing a script to list alsa hardware device names", r); } @@ -343,66 +353,51 @@ void actual_close_alsa_device() { } } -// This array is a sequence of the output rates to be tried if automatic speed selection is requested. +// This array is a sequence of the output rates to be tried if automatic speed selection is +// requested. // There is no benefit to upconverting the frame rate, other than for compatibility. // The lowest rate that the DAC is capable of is chosen. unsigned int auto_speed_output_rates[] = { - 44100, - 88200, - 176400, - 352800, + 44100, 88200, 176400, 352800, }; -// This array is of all the formats known to Shairport Sync, in order of the SPS_FORMAT definitions, with their equivalent alsa codes and their frame sizes. -// If just one format is requested, then its entry is searched for in the array and checked on the device +// This array is of all the formats known to Shairport Sync, in order of the SPS_FORMAT definitions, +// with their equivalent alsa codes and their frame sizes. +// If just one format is requested, then its entry is searched for in the array and checked on the +// device // If auto format is requested, then each entry in turn is tried until a working format is found. // So, it should be in the search order. - - format_record fr[] = { - {SND_PCM_FORMAT_UNKNOWN,0}, // unknown - {SND_PCM_FORMAT_S8,2}, - {SND_PCM_FORMAT_U8,2}, - {SND_PCM_FORMAT_S16,4}, - {SND_PCM_FORMAT_S16_LE,4}, - {SND_PCM_FORMAT_S16_BE,4}, - {SND_PCM_FORMAT_S24,4}, - {SND_PCM_FORMAT_S24_LE,8}, - {SND_PCM_FORMAT_S24_BE,8}, - {SND_PCM_FORMAT_S24_3LE,6}, - {SND_PCM_FORMAT_S24_3BE,6}, - {SND_PCM_FORMAT_S32,8}, - {SND_PCM_FORMAT_S32_LE,8}, - {SND_PCM_FORMAT_S32_BE,8}, - {SND_PCM_FORMAT_UNKNOWN,0}, // auto - {SND_PCM_FORMAT_UNKNOWN,0}, // illegal - }; - - // This array is the sequence of formats to be tried if automatic selection of the format is requested. - // Ideally, audio should pass through Shairport Sync unaltered, apart from occasional interpolation. - // If the user chooses a hardware mixer, then audio could go straight through, unaltered, as signed 16 bit stereo. - // However, the user might, at any point, select an option that requires modification, such as stereo to mono mixing, - // additional volume attenuation, convolution, and so on. For this reason, - // we look for the greatest depth the DAC is capable of, since upconverting it is completely lossless. - // If audio processing is required, then the dither that must be added will - // be added at the lowest possible level. - // Hence, selecting the greatest bit depth is always either beneficial or neutral. - - enum sps_format_t auto_format_check_sequence[] = { - SPS_FORMAT_S32, - SPS_FORMAT_S32_LE, - SPS_FORMAT_S32_BE, - SPS_FORMAT_S24, - SPS_FORMAT_S24_LE, - SPS_FORMAT_S24_BE, - SPS_FORMAT_S24_3LE, - SPS_FORMAT_S24_3BE, - SPS_FORMAT_S16, - SPS_FORMAT_S16_LE, - SPS_FORMAT_S16_BE, - SPS_FORMAT_S8, - SPS_FORMAT_U8, - }; + +format_record fr[] = { + {SND_PCM_FORMAT_UNKNOWN, 0}, // unknown + {SND_PCM_FORMAT_S8, 2}, {SND_PCM_FORMAT_U8, 2}, {SND_PCM_FORMAT_S16, 4}, + {SND_PCM_FORMAT_S16_LE, 4}, {SND_PCM_FORMAT_S16_BE, 4}, {SND_PCM_FORMAT_S24, 4}, + {SND_PCM_FORMAT_S24_LE, 8}, {SND_PCM_FORMAT_S24_BE, 8}, {SND_PCM_FORMAT_S24_3LE, 6}, + {SND_PCM_FORMAT_S24_3BE, 6}, {SND_PCM_FORMAT_S32, 8}, {SND_PCM_FORMAT_S32_LE, 8}, + {SND_PCM_FORMAT_S32_BE, 8}, {SND_PCM_FORMAT_UNKNOWN, 0}, // auto + {SND_PCM_FORMAT_UNKNOWN, 0}, // illegal +}; + +// This array is the sequence of formats to be tried if automatic selection of the format is +// requested. +// Ideally, audio should pass through Shairport Sync unaltered, apart from occasional interpolation. +// If the user chooses a hardware mixer, then audio could go straight through, unaltered, as signed +// 16 bit stereo. +// However, the user might, at any point, select an option that requires modification, such as +// stereo to mono mixing, +// additional volume attenuation, convolution, and so on. For this reason, +// we look for the greatest depth the DAC is capable of, since upconverting it is completely +// lossless. +// If audio processing is required, then the dither that must be added will +// be added at the lowest possible level. +// Hence, selecting the greatest bit depth is always either beneficial or neutral. + +enum sps_format_t auto_format_check_sequence[] = { + SPS_FORMAT_S32, SPS_FORMAT_S32_LE, SPS_FORMAT_S32_BE, SPS_FORMAT_S24, SPS_FORMAT_S24_LE, + SPS_FORMAT_S24_BE, SPS_FORMAT_S24_3LE, SPS_FORMAT_S24_3BE, SPS_FORMAT_S16, SPS_FORMAT_S16_LE, + SPS_FORMAT_S16_BE, SPS_FORMAT_S8, SPS_FORMAT_U8, +}; // assuming pthread cancellation is disabled // if do_auto_setting is true and auto format or auto speed has been requested, @@ -420,7 +415,8 @@ int actual_open_alsa_device(int do_auto_setup) { */ int ret, dir = 0; - unsigned int actual_sample_rate; // this will be given the rate requested and will be given the actual rate + unsigned int + actual_sample_rate; // this will be given the rate requested and will be given the actual rate // snd_pcm_uframes_t frames = 441 * 10; snd_pcm_uframes_t actual_buffer_length; snd_pcm_access_t access; @@ -444,7 +440,8 @@ int actual_open_alsa_device(int do_auto_setup) { } else { char errorstring[1024]; strerror_r(-ret, (char *)errorstring, sizeof(errorstring)); - warn("alsa: error %d (\"%s\") opening alsa device \"%s\".", ret, (char *)errorstring, alsa_out_dev); + warn("alsa: error %d (\"%s\") opening alsa device \"%s\".", ret, (char *)errorstring, + alsa_out_dev); } return ret; } @@ -455,8 +452,8 @@ int actual_open_alsa_device(int do_auto_setup) { ret = snd_pcm_hw_params_any(alsa_handle, alsa_params); if (ret < 0) { die("audio_alsa: Broken configuration for device \"%s\": no configurations " - "available", - alsa_out_dev); + "available", + alsa_out_dev); return ret; } @@ -484,7 +481,6 @@ int actual_open_alsa_device(int do_auto_setup) { snd_strerror(ret)); return ret; } - ret = snd_pcm_hw_params_set_channels(alsa_handle, alsa_params, 2); if (ret < 0) { @@ -494,90 +490,92 @@ int actual_open_alsa_device(int do_auto_setup) { } snd_pcm_format_t sf; - + if ((do_auto_setup == 0) || (config.output_format_auto_requested == 0)) { // no auto format - if ((config.output_format > SPS_FORMAT_UNKNOWN) && (config.output_format < SPS_FORMAT_AUTO)) { - sf = fr[config.output_format].alsa_code; - frame_size = fr[config.output_format].frame_size; - } else { - warn("alsa: unexpected output format %d. Set to S16_LE.",config.output_format); - config.output_format = SPS_FORMAT_S16_LE; - sf = fr[config.output_format].alsa_code; - frame_size = fr[config.output_format].frame_size; - } - ret = snd_pcm_hw_params_set_format(alsa_handle, alsa_params, sf); - if (ret < 0) { - warn("audio_alsa: Alsa sample format %d not available for device \"%s\": %s", sf, - alsa_out_dev, snd_strerror(ret)); - return ret; - } + if ((config.output_format > SPS_FORMAT_UNKNOWN) && (config.output_format < SPS_FORMAT_AUTO)) { + sf = fr[config.output_format].alsa_code; + frame_size = fr[config.output_format].frame_size; + } else { + warn("alsa: unexpected output format %d. Set to S16_LE.", config.output_format); + config.output_format = SPS_FORMAT_S16_LE; + sf = fr[config.output_format].alsa_code; + frame_size = fr[config.output_format].frame_size; + } + ret = snd_pcm_hw_params_set_format(alsa_handle, alsa_params, sf); + if (ret < 0) { + warn("audio_alsa: Alsa sample format %d not available for device \"%s\": %s", sf, + alsa_out_dev, snd_strerror(ret)); + return ret; + } } else { // auto format - int number_of_formats_to_try; - enum sps_format_t *formats; - formats = auto_format_check_sequence; - number_of_formats_to_try = sizeof(auto_format_check_sequence)/sizeof(sps_format_t); - int i = 0; - int format_found = 0; - enum sps_format_t trial_format = SPS_FORMAT_UNKNOWN; - while ((i < number_of_formats_to_try) && (format_found == 0)) { - trial_format = formats[i]; - sf = fr[trial_format].alsa_code; - frame_size = fr[trial_format].frame_size; - ret = snd_pcm_hw_params_set_format(alsa_handle, alsa_params, sf); - if (ret == 0) - format_found = 1; - else - i++; - } - if (ret == 0) { - config.output_format = trial_format; - debug(1,"alsa: output format chosen is \"%s\".",sps_format_description_string(config.output_format)); - } else { - warn("audio_alsa: Could not automatically set the output format for device \"%s\": %s", - alsa_out_dev, snd_strerror(ret)); - return ret; - } + int number_of_formats_to_try; + enum sps_format_t *formats; + formats = auto_format_check_sequence; + number_of_formats_to_try = sizeof(auto_format_check_sequence) / sizeof(sps_format_t); + int i = 0; + int format_found = 0; + enum sps_format_t trial_format = SPS_FORMAT_UNKNOWN; + while ((i < number_of_formats_to_try) && (format_found == 0)) { + trial_format = formats[i]; + sf = fr[trial_format].alsa_code; + frame_size = fr[trial_format].frame_size; + ret = snd_pcm_hw_params_set_format(alsa_handle, alsa_params, sf); + if (ret == 0) + format_found = 1; + else + i++; + } + if (ret == 0) { + config.output_format = trial_format; + debug(1, "alsa: output format chosen is \"%s\".", + sps_format_description_string(config.output_format)); + } else { + warn("audio_alsa: Could not automatically set the output format for device \"%s\": %s", + alsa_out_dev, snd_strerror(ret)); + return ret; + } } - + if ((do_auto_setup == 0) || (config.output_rate_auto_requested == 0)) { // no auto format - actual_sample_rate = config.output_rate; // this is the requested rate -- it'll be changed to the actual rate - ret = snd_pcm_hw_params_set_rate_near(alsa_handle, alsa_params, &actual_sample_rate, &dir); - if (ret < 0) { - warn("audio_alsa: Rate %iHz not available for playback: %s", config.output_rate, - snd_strerror(ret)); - return ret; - } - } else { - int number_of_speeds_to_try; - unsigned int *speeds; - - speeds = auto_speed_output_rates; - number_of_speeds_to_try = sizeof(auto_speed_output_rates)/sizeof(int); - - int i = 0; - int speed_found = 0; - - while ((i < number_of_speeds_to_try) && (speed_found == 0)) { - actual_sample_rate = speeds[i]; - ret = snd_pcm_hw_params_set_rate_near(alsa_handle, alsa_params, &actual_sample_rate, &dir); - if (ret == 0) { - speed_found = 1; - if (actual_sample_rate != speeds[i]) - warn("Speed requested: %d. Speed available: %d.",speeds[i],actual_sample_rate); - } else { - i++; - } - } - if (ret == 0) { - config.output_rate = actual_sample_rate; - debug(1,"alsa: output speed chosen is %d.",config.output_rate); - } else { - warn("audio_alsa: Could not automatically set the output rate for device \"%s\": %s", - alsa_out_dev, snd_strerror(ret)); - return ret; - } - } - + actual_sample_rate = + config.output_rate; // this is the requested rate -- it'll be changed to the actual rate + ret = snd_pcm_hw_params_set_rate_near(alsa_handle, alsa_params, &actual_sample_rate, &dir); + if (ret < 0) { + warn("audio_alsa: Rate %iHz not available for playback: %s", config.output_rate, + snd_strerror(ret)); + return ret; + } + } else { + int number_of_speeds_to_try; + unsigned int *speeds; + + speeds = auto_speed_output_rates; + number_of_speeds_to_try = sizeof(auto_speed_output_rates) / sizeof(int); + + int i = 0; + int speed_found = 0; + + while ((i < number_of_speeds_to_try) && (speed_found == 0)) { + actual_sample_rate = speeds[i]; + ret = snd_pcm_hw_params_set_rate_near(alsa_handle, alsa_params, &actual_sample_rate, &dir); + if (ret == 0) { + speed_found = 1; + if (actual_sample_rate != speeds[i]) + warn("Speed requested: %d. Speed available: %d.", speeds[i], actual_sample_rate); + } else { + i++; + } + } + if (ret == 0) { + config.output_rate = actual_sample_rate; + debug(1, "alsa: output speed chosen is %d.", config.output_rate); + } else { + warn("audio_alsa: Could not automatically set the output rate for device \"%s\": %s", + alsa_out_dev, snd_strerror(ret)); + return ret; + } + } + if (set_period_size_request != 0) { debug(1, "Attempting to set the period size to %lu", period_size_requested); ret = snd_pcm_hw_params_set_period_size_near(alsa_handle, alsa_params, &period_size_requested, @@ -648,8 +646,8 @@ int actual_open_alsa_device(int do_auto_setup) { warn("Can't set the D/A converter to sample rate %d.", config.output_rate); return -EINVAL; } - - use_monotonic_clock = snd_pcm_hw_params_is_monotonic(alsa_params); + + use_monotonic_clock = snd_pcm_hw_params_is_monotonic(alsa_params); ret = snd_pcm_hw_params_get_buffer_size(alsa_params, &actual_buffer_length); if (ret < 0) { @@ -680,11 +678,10 @@ int actual_open_alsa_device(int do_auto_setup) { snd_strerror(ret)); return ret; } - + ret = snd_pcm_prepare(alsa_handle); if (ret < 0) { - warn("audio_alsa: Unable to prepare the device: \"%s\": %s.", alsa_out_dev, - snd_strerror(ret)); + warn("audio_alsa: Unable to prepare the device: \"%s\": %s.", alsa_out_dev, snd_strerror(ret)); return ret; } @@ -716,14 +713,13 @@ int actual_open_alsa_device(int do_auto_setup) { "length (%ld) you have chosen.", actual_buffer_length, config.audio_backend_buffer_desired_length); } - - - if (config.use_precision_timing == YNA_YES) + + if (config.use_precision_timing == YNA_YES) delay_and_status = precision_delay_and_status; else if (config.use_precision_timing == YNA_AUTO) { if (precision_delay_available()) { delay_and_status = precision_delay_and_status; - debug(2,"alsa: precision timing selected for \"auto\" mode"); + debug(2, "alsa: precision timing selected for \"auto\" mode"); } } @@ -1020,7 +1016,7 @@ static int init(int argc, char **argv) { stall_monitor_start_time = 0; stall_monitor_frame_count = 0; - + config.disable_standby_mode = disable_standby_off; config.keep_dac_busy = 0; config.use_precision_timing = YNA_AUTO; @@ -1102,12 +1098,11 @@ static int init(int argc, char **argv) { config.alsa_use_hardware_mute = 0; } } - /* Get the output format, using the same names as aplay does*/ if (config_lookup_string(config.cfg, "alsa.output_format", &str)) { - int temp_output_format_auto_requested = config.output_format_auto_requested; - config.output_format_auto_requested = 0; // assume a valid format will be given. + int temp_output_format_auto_requested = config.output_format_auto_requested; + config.output_format_auto_requested = 0; // assume a valid format will be given. if (strcasecmp(str, "S16") == 0) config.output_format = SPS_FORMAT_S16; else if (strcasecmp(str, "S16_LE") == 0) @@ -1137,48 +1132,54 @@ static int init(int argc, char **argv) { else if (strcasecmp(str, "auto") == 0) config.output_format_auto_requested = 1; else { - config.output_format_auto_requested = temp_output_format_auto_requested; //format was invalid; recall the original setting + config.output_format_auto_requested = + temp_output_format_auto_requested; // format was invalid; recall the original setting warn("Invalid output format \"%s\". It should be \"auto\", \"U8\", \"S8\", " "\"S16\", \"S24\", \"S24_LE\", \"S24_BE\", " "\"S24_3LE\", \"S24_3BE\" or " - "\"S32\", \"S32_LE\", \"S32_BE\". It remains set to \"%s\".", str, - config.output_format_auto_requested == 1 ? "auto" : sps_format_description_string(config.output_format)); + "\"S32\", \"S32_LE\", \"S32_BE\". It remains set to \"%s\".", + str, config.output_format_auto_requested == 1 ? "auto" : sps_format_description_string( + config.output_format)); } } if (config_lookup_string(config.cfg, "alsa.output_rate", &str)) { - if (strcasecmp(str, "auto") == 0) { - config.output_rate_auto_requested = 1; - } else { - if (config.output_rate_auto_requested == 1) - warn("Invalid output rate \"%s\". It should be \"auto\", 44100, 88200, 176400 or 352800. " - "It remains set to \"auto\". Note: numbers should not be placed in quotes.", str); - else - warn("Invalid output rate \"%s\". It should be \"auto\", 44100, 88200, 176400 or 352800. " - "It remains set to %d. Note: numbers should not be placed in quotes.", str, config.output_rate); - } - } - - /* Get the output rate, which must be a multiple of 44,100*/ - if (config_lookup_int(config.cfg, "alsa.output_rate", &value)) { - debug(1, "alsa output rate is %d frames per second", value); - switch (value) { - case 44100: - case 88200: - case 176400: - case 352800: - config.output_rate = value; - config.output_rate_auto_requested = 0; - break; - default: - if (config.output_rate_auto_requested == 1) - warn("Invalid output rate \"%d\". It should be \"auto\", 44100, 88200, 176400 or 352800. " - "It remains set to \"auto\".",value); - else - warn("Invalid output rate \"%d\".It should be \"auto\", 44100, 88200, 176400 or 352800. " - "It remains set to %d.", value, config.output_rate); - } - } + if (strcasecmp(str, "auto") == 0) { + config.output_rate_auto_requested = 1; + } else { + if (config.output_rate_auto_requested == 1) + warn("Invalid output rate \"%s\". It should be \"auto\", 44100, 88200, 176400 or 352800. " + "It remains set to \"auto\". Note: numbers should not be placed in quotes.", + str); + else + warn("Invalid output rate \"%s\". It should be \"auto\", 44100, 88200, 176400 or 352800. " + "It remains set to %d. Note: numbers should not be placed in quotes.", + str, config.output_rate); + } + } + + /* Get the output rate, which must be a multiple of 44,100*/ + if (config_lookup_int(config.cfg, "alsa.output_rate", &value)) { + debug(1, "alsa output rate is %d frames per second", value); + switch (value) { + case 44100: + case 88200: + case 176400: + case 352800: + config.output_rate = value; + config.output_rate_auto_requested = 0; + break; + default: + if (config.output_rate_auto_requested == 1) + warn("Invalid output rate \"%d\". It should be \"auto\", 44100, 88200, 176400 or 352800. " + "It remains set to \"auto\".", + value); + else + warn("Invalid output rate \"%d\".It should be \"auto\", 44100, 88200, 176400 or 352800. " + "It remains set to %d.", + value, config.output_rate); + } + } /* Get the use_mmap_if_available setting. */ if (config_lookup_string(config.cfg, "alsa.use_mmap_if_available", &str)) { @@ -1232,12 +1233,13 @@ static int init(int argc, char **argv) { } } - /* Get the optional disable_standby_mode setting. */ if (config_lookup_string(config.cfg, "alsa.disable_standby_mode", &str)) { - if ((strcasecmp(str, "no") == 0) || (strcasecmp(str, "off") == 0) || (strcasecmp(str, "never") == 0)) + if ((strcasecmp(str, "no") == 0) || (strcasecmp(str, "off") == 0) || + (strcasecmp(str, "never") == 0)) config.disable_standby_mode = disable_standby_off; - else if ((strcasecmp(str, "yes") == 0) || (strcasecmp(str, "on") == 0) || (strcasecmp(str, "always") == 0)) { + else if ((strcasecmp(str, "yes") == 0) || (strcasecmp(str, "on") == 0) || + (strcasecmp(str, "always") == 0)) { config.disable_standby_mode = disable_standby_always; config.keep_dac_busy = 1; } else if (strcasecmp(str, "auto") == 0) @@ -1245,15 +1247,17 @@ static int init(int argc, char **argv) { else { warn("Invalid disable_standby_mode option choice \"%s\". It should be " "\"always\", \"auto\" or \"never\". " - "It remains set to \"never\".", str); + "It remains set to \"never\".", + str); } } - if (config_lookup_string(config.cfg, "alsa.use_precision_timing", &str)) { - if ((strcasecmp(str, "no") == 0) || (strcasecmp(str, "off") == 0) || (strcasecmp(str, "never") == 0)) + if ((strcasecmp(str, "no") == 0) || (strcasecmp(str, "off") == 0) || + (strcasecmp(str, "never") == 0)) config.use_precision_timing = YNA_NO; - else if ((strcasecmp(str, "yes") == 0) || (strcasecmp(str, "on") == 0) || (strcasecmp(str, "always") == 0)) { + else if ((strcasecmp(str, "yes") == 0) || (strcasecmp(str, "on") == 0) || + (strcasecmp(str, "always") == 0)) { config.use_precision_timing = YNA_YES; config.keep_dac_busy = 1; } else if (strcasecmp(str, "auto") == 0) @@ -1261,11 +1265,17 @@ static int init(int argc, char **argv) { else { warn("Invalid use_precision_timing option choice \"%s\". It should be " "\"yes\", \"auto\" or \"no\". " - "It remains set to \"%s\".", config.use_precision_timing == YNA_NO ? "no" : config.use_precision_timing == YNA_AUTO ? "auto" : "yes"); + "It remains set to \"%s\".", + config.use_precision_timing == YNA_NO ? "no" : config.use_precision_timing == YNA_AUTO + ? "auto" + : "yes"); } } - debug(1, "alsa: disable_standby_mode is \"%s\".", config.disable_standby_mode == disable_standby_off ? "never" : config.disable_standby_mode == disable_standby_always ? "always" : "auto"); + debug(1, "alsa: disable_standby_mode is \"%s\".", + config.disable_standby_mode == disable_standby_off + ? "never" + : config.disable_standby_mode == disable_standby_always ? "always" : "auto"); } optind = 1; // optind=0 is equivalent to optind=1 plus special behaviour @@ -1362,14 +1372,15 @@ int set_mute_state() { close_mixer(); } debug_mutex_unlock(&alsa_mixer_mutex, 3); // release the mutex - pthread_cleanup_pop(0); // release the mutex + pthread_cleanup_pop(0); // release the mutex pthread_setcancelstate(oldState, NULL); return response; } -static void start(__attribute__((unused)) int i_sample_rate, __attribute__((unused)) int i_sample_format) { +static void start(__attribute__((unused)) int i_sample_rate, + __attribute__((unused)) int i_sample_format) { debug(3, "audio_alsa start called."); - + frame_index = 0; measurement_data_is_valid = 0; @@ -1381,31 +1392,33 @@ static void start(__attribute__((unused)) int i_sample_rate, __attribute__((unus } } -int standard_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay, enum yndk_type *using_update_timestamps) { +int standard_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay, + enum yndk_type *using_update_timestamps) { int ret = 0; if (using_update_timestamps) *using_update_timestamps = YNDK_NO; *state = snd_pcm_state(alsa_handle); if ((*state == SND_PCM_STATE_RUNNING) || (*state == SND_PCM_STATE_DRAINING)) { - ret = snd_pcm_delay(alsa_handle,delay); + ret = snd_pcm_delay(alsa_handle, delay); } else { - // not running, thus no delay information, thus can't check for frame - // rates + // not running, thus no delay information, thus can't check for frame + // rates frame_index = 0; // we'll be starting over... measurement_data_is_valid = 0; - *delay = 0; + *delay = 0; } - + stall_monitor_start_time = 0; // zero if not initialised / not started / zeroed by flush stall_monitor_frame_count = 0; // set to delay at start of time, incremented by any writes return ret; } -int precision_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay, enum yndk_type *using_update_timestamps) { +int precision_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay, + enum yndk_type *using_update_timestamps) { snd_pcm_status_t *alsa_snd_pcm_status; snd_pcm_status_alloca(&alsa_snd_pcm_status); - + if (using_update_timestamps) *using_update_timestamps = YNDK_DONT_KNOW; @@ -1414,12 +1427,12 @@ int precision_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay, int ret = snd_pcm_status(alsa_handle, alsa_snd_pcm_status); if (ret == 0) { - - // must be 1.1 or later to use snd_pcm_status_get_driver_htstamp + +// must be 1.1 or later to use snd_pcm_status_get_driver_htstamp #if SND_LIB_MINOR == 0 - snd_pcm_status_get_htstamp(alsa_snd_pcm_status, &update_timestamp); + snd_pcm_status_get_htstamp(alsa_snd_pcm_status, &update_timestamp); #else - snd_pcm_status_get_driver_htstamp(alsa_snd_pcm_status, &update_timestamp); + snd_pcm_status_get_driver_htstamp(alsa_snd_pcm_status, &update_timestamp); #endif *state = snd_pcm_status_get_state(alsa_snd_pcm_status); @@ -1439,41 +1452,41 @@ int precision_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay, else *using_update_timestamps = YNDK_YES; } - -// user information + + // user information if (update_timestamp_ns == 0) { if (delay_type_notified != 1) { - debug(2,"alsa: update timestamps unavailable"); + debug(2, "alsa: update timestamps unavailable"); delay_type_notified = 1; } } else { -// diagnostic + // diagnostic if (delay_type_notified != 0) { - debug(2,"alsa: update timestamps available"); + debug(2, "alsa: update timestamps available"); delay_type_notified = 0; } } if (update_timestamp_ns == 0) { - ret = snd_pcm_delay (alsa_handle,delay); + ret = snd_pcm_delay(alsa_handle, delay); } else { *delay = snd_pcm_status_get_delay(alsa_snd_pcm_status); -/* -// It seems that the alsa library uses CLOCK_REALTIME before 1.0.28, even though -// the check for monotonic returns true. Might have to watch out for this. - #if SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR < 28 - clock_gettime(CLOCK_REALTIME, &tn); - #else - clock_gettime(CLOCK_MONOTONIC, &tn); - #endif -*/ + /* + // It seems that the alsa library uses CLOCK_REALTIME before 1.0.28, even though + // the check for monotonic returns true. Might have to watch out for this. + #if SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR < 28 + clock_gettime(CLOCK_REALTIME, &tn); + #else + clock_gettime(CLOCK_MONOTONIC, &tn); + #endif + */ if (use_monotonic_clock) clock_gettime(CLOCK_MONOTONIC, &tn); else clock_gettime(CLOCK_REALTIME, &tn); - + uint64_t time_now_ns = tn.tv_sec * (uint64_t)1000000000 + tn.tv_nsec; // see if it's stalled @@ -1491,8 +1504,8 @@ int precision_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay, debug(2, "DAC seems to have stalled with time_now: %lx,%lx" ", update_timestamp: %lx,%lx, stall_monitor_start_time %" PRIX64 ", stall_monitor_error_threshold %" PRIX64 ".", - tn.tv_sec, tn.tv_nsec, update_timestamp.tv_sec, update_timestamp.tv_nsec, stall_monitor_start_time, - stall_monitor_error_threshold); + tn.tv_sec, tn.tv_nsec, update_timestamp.tv_sec, update_timestamp.tv_nsec, + stall_monitor_start_time, stall_monitor_error_threshold); ret = sps_extra_code_output_stalled; } } else { @@ -1649,9 +1662,9 @@ int do_play(void *buf, int samples) { } else { char errorstring[1024]; strerror_r(-ret, (char *)errorstring, sizeof(errorstring)); - debug(1, "alsa: error %d (\"%s\") writing %d samples to alsa device.", ret, (char *)errorstring, samples); + debug(1, "alsa: error %d (\"%s\") writing %d samples to alsa device.", ret, + (char *)errorstring, samples); } - } } } else { @@ -1684,7 +1697,7 @@ int do_open(int do_auto_setup) { // set accordingly // do_mute(0); // complete unmute } - + alsa_backend_state = abm_connected; // only do this if it really opened it. } } else { @@ -1739,11 +1752,11 @@ int play(void *buf, int samples) { if (alsa_backend_state != abm_playing) { debug(2, "alsa: play() -- alsa_backend_state => abm_playing"); alsa_backend_state = abm_playing; - + // mute_requested_internally = 0; // stop requesting a mute for backend's own - // reasons, which might have been a flush - //debug(2, "play() set_mute_state"); - //set_mute_state(); // try to action the request and return a status + // reasons, which might have been a flush + // debug(2, "play() set_mute_state"); + // set_mute_state(); // try to action the request and return a status // do_mute(0); // unmute for backend's reason } ret = do_play(buf, samples); @@ -1755,16 +1768,15 @@ int play(void *buf, int samples) { } int prepare(void) { - // this will leave the DAC open / connected. + // this will leave the DAC open / connected. int ret = 0; pthread_cleanup_debug_mutex_lock(&alsa_mutex, 50000, 0); if (alsa_backend_state == abm_disconnected) { - ret = do_open(1); // do auto setup + ret = do_open(1); // do auto setup if (ret == 0) debug(2, "alsa: prepare() -- opened output device"); - } debug_mutex_unlock(&alsa_mutex, 0); @@ -1888,9 +1900,10 @@ void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) { int error_count = 0; int error_threshold_exceeded = 0; int okb = -1; - while (error_threshold_exceeded == 0) { // if too many play errors occur early on, we will turn off the disable stanby mode + while (error_threshold_exceeded == + 0) { // if too many play errors occur early on, we will turn off the disable stanby mode if (okb != config.keep_dac_busy) { - debug(2,"keep_dac_busy is now \"%s\"",config.keep_dac_busy == 0 ? "no" : "yes"); + debug(2, "keep_dac_busy is now \"%s\"", config.keep_dac_busy == 0 ? "no" : "yes"); okb = config.keep_dac_busy; } if ((config.keep_dac_busy != 0) && (alsa_device_initialised == 0)) { @@ -1921,7 +1934,8 @@ void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) { // to be in the // abm_connected state in the first place...) then do the silence-filling // thing, if needed /* only if the output device is capable of precision delay */. - if ((alsa_backend_state != abm_disconnected) && (config.keep_dac_busy != 0) /* && precision_delay_available() */ ) { + if ((alsa_backend_state != abm_disconnected) && + (config.keep_dac_busy != 0) /* && precision_delay_available() */) { int reply; long buffer_size = 0; snd_pcm_state_t state; @@ -1976,9 +1990,13 @@ void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) { error_count++; char errorstring[1024]; strerror_r(-ret, (char *)errorstring, sizeof(errorstring)); - debug(2, "alsa: alsa_buffer_monitor_thread_code error %d (\"%s\") writing %d samples to alsa device -- %d errors in %d trials.", ret, (char *)errorstring, frames_of_silence, error_count, frame_count); + debug(2, "alsa: alsa_buffer_monitor_thread_code error %d (\"%s\") writing %d samples " + "to alsa device -- %d errors in %d trials.", + ret, (char *)errorstring, frames_of_silence, error_count, frame_count); if ((error_count > 40) && (frame_count < 100)) { - warn("disable_standby_mode has been turned off because too many underruns occurred. Is Shairport Sync outputting to a virtual device or running in a virtual machine?"); + warn("disable_standby_mode has been turned off because too many underruns " + "occurred. Is Shairport Sync outputting to a virtual device or running in a " + "virtual machine?"); error_threshold_exceeded = 1; } } diff --git a/audio_dummy.c b/audio_dummy.c index 483dbe42..026ef78a 100644 --- a/audio_dummy.c +++ b/audio_dummy.c @@ -54,7 +54,6 @@ static int play(__attribute__((unused)) void *buf, __attribute__((unused)) int s static void stop(void) { debug(1, "dummy audio stopped\n"); } - audio_output audio_dummy = {.name = "dummy", .help = NULL, .init = &init, diff --git a/audio_jack.c b/audio_jack.c index 746d3d61..83398e2c 100644 --- a/audio_jack.c +++ b/audio_jack.c @@ -20,10 +20,10 @@ #include "audio.h" #include "common.h" -#include #include #include #include +#include #include #include @@ -64,7 +64,7 @@ audio_output audio_jack = {.name = "jack", // So make it exactly the number of incoming audio channels! #define NPORTS 2 static jack_port_t *port[NPORTS]; -static const char* port_name[NPORTS] = { "out_L", "out_R" }; +static const char *port_name[NPORTS] = {"out_L", "out_R"}; static jack_client_t *client; static jack_nframes_t sample_rate; @@ -76,7 +76,6 @@ static int flush_please = 0; static jack_latency_range_t latest_latency_range[NPORTS]; static int64_t time_of_latest_transfer; - static inline jack_default_audio_sample_t sample_conv(short sample) { // It sounds correct, but I don't understand it. // Zero int needs to be zero float. Check. @@ -88,9 +87,8 @@ static inline jack_default_audio_sample_t sample_conv(short sample) { } static void deinterleave_and_convert(const char *interleaved_input_buffer, - jack_default_audio_sample_t* jack_output_buffer[], - jack_nframes_t offset, - jack_nframes_t nframes) { + jack_default_audio_sample_t *jack_output_buffer[], + jack_nframes_t offset, jack_nframes_t nframes) { jack_nframes_t f; // We're dealing with 16bit audio here: short *ifp = (short *)interleaved_input_buffer; @@ -111,7 +109,7 @@ static void deinterleave_and_convert(const char *interleaved_input_buffer, static int process(jack_nframes_t nframes, __attribute__((unused)) void *arg) { jack_default_audio_sample_t *buffer[NPORTS]; // Expect an array of two elements because of possible ringbuffer wrap-around: - jack_ringbuffer_data_t v[2] = { 0 }; + jack_ringbuffer_data_t v[2] = {0}; jack_nframes_t i, thisbuf; int frames_written = 0; int frames_required = 0; @@ -130,7 +128,7 @@ static int process(jack_nframes_t nframes, __attribute__((unused)) void *arg) { thisbuf = v[i].len / bytes_per_frame; if (thisbuf > nframes) { frames_required = nframes; - } else { + } else { frames_required = thisbuf; } deinterleave_and_convert(v[i].buf, buffer, frames_written, frames_required); @@ -154,13 +152,13 @@ static int process(jack_nframes_t nframes, __attribute__((unused)) void *arg) { // This is the JACK graph reorder callback. Now we know some JACK connections // have changed, so we recompute the latency. -static int graph(__attribute__((unused)) void * arg) { +static int graph(__attribute__((unused)) void *arg) { int latency = 0; debug(2, "JACK graph reorder callback called."); - for (int i=0; i> 32; @@ -339,12 +336,12 @@ int play(void *buf, int samples) { bytes_to_transfer = samples * bytes_per_frame; // It's ok to lock here since we're not in the realtime callback: pthread_mutex_lock(&buffer_mutex); - bytes_transferred = jack_ringbuffer_write(jackbuf, buf, bytes_to_transfer); - time_of_latest_transfer = get_absolute_time_in_fp(); + bytes_transferred = jack_ringbuffer_write(jackbuf, buf, bytes_to_transfer); + time_of_latest_transfer = get_absolute_time_in_fp(); pthread_mutex_unlock(&buffer_mutex); if (bytes_transferred < bytes_to_transfer) { - warn("JACK ringbuffer overrun. Only wrote %d of %d bytes.", - bytes_transferred, bytes_to_transfer); + warn("JACK ringbuffer overrun. Only wrote %d of %d bytes.", bytes_transferred, + bytes_to_transfer); } return 0; } diff --git a/audio_pipe.c b/audio_pipe.c index f0ced371..4ec9b240 100644 --- a/audio_pipe.c +++ b/audio_pipe.c @@ -46,16 +46,17 @@ 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 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." + // "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) && (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); + 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; } diff --git a/audio_sndio.c b/audio_sndio.c index a591add4..54e1763d 100644 --- a/audio_sndio.c +++ b/audio_sndio.c @@ -76,7 +76,8 @@ struct sndio_formats { static struct sndio_formats formats[] = {{"S8", SPS_FORMAT_S8, 44100, 8, 1, 1, SIO_LE_NATIVE}, {"U8", SPS_FORMAT_U8, 44100, 8, 1, 0, SIO_LE_NATIVE}, {"S16", SPS_FORMAT_S16, 44100, 16, 2, 1, SIO_LE_NATIVE}, - {"AUTOMATIC", SPS_FORMAT_S16, 44100, 16, 2, 1, SIO_LE_NATIVE}, // TODO: make this really automatic? + {"AUTOMATIC", SPS_FORMAT_S16, 44100, 16, 2, 1, + SIO_LE_NATIVE}, // TODO: make this really automatic? {"S24", SPS_FORMAT_S24, 44100, 24, 4, 1, SIO_LE_NATIVE}, {"S24_3LE", SPS_FORMAT_S24_3LE, 44100, 24, 3, 1, 1}, {"S24_3BE", SPS_FORMAT_S24_3BE, 44100, 24, 3, 1, 0}, @@ -176,7 +177,6 @@ static int init(int argc, char **argv) { written = played = 0; time_of_last_onmove_cb = 0; at_least_one_onmove_cb_seen = 0; - for (i = 0; i < sizeof(formats) / sizeof(formats[0]); i++) { if (formats[i].fmt == config.output_format) { @@ -187,7 +187,7 @@ static int init(int argc, char **argv) { break; } } - + if (!sio_setpar(hdl, &par) || !sio_getpar(hdl, &par)) die("sndio: failed to set audio parameters"); for (i = 0, found = 0; i < sizeof(formats) / sizeof(formats[0]); i++) { @@ -200,7 +200,6 @@ static int init(int argc, char **argv) { } if (!found) die("sndio: could not set output device to the required format and rate."); - framesize = par.bps * par.pchan; config.output_rate = par.rate; diff --git a/audio_soundio.c b/audio_soundio.c index 7983733b..53de2acb 100644 --- a/audio_soundio.c +++ b/audio_soundio.c @@ -27,9 +27,8 @@ static void write_callback(struct SoundIoOutStream *outstream, int frame_count_m int fill_bytes = soundio_ring_buffer_fill_count(ring_buffer); int fill_count = fill_bytes / outstream->bytes_per_frame; - debug(3, - "[--->>] frame_count_min: %d , frame_count_max: %d , fill_bytes: %d , fill_count: %d , " - "outstream->bytes_per_frame: %d", + debug(3, "[--->>] frame_count_min: %d , frame_count_max: %d , fill_bytes: %d , fill_count: %d , " + "outstream->bytes_per_frame: %d", frame_count_min, frame_count_max, fill_bytes, fill_count, outstream->bytes_per_frame); if (frame_count_min > fill_count) { diff --git a/common.c b/common.c index 9775b07e..bdbb9a87 100644 --- a/common.c +++ b/common.c @@ -87,9 +87,11 @@ void set_alsa_out_dev(char *); #endif -const char * sps_format_description_string_array[] = {"unknown", "S8", "U8" ,"S16", "S16_LE", "S16_BE", "S24", "S24_LE", "S24_BE", "S24_3LE", "S24_3BE", "S32", "S32_LE", "S32_BE", "auto", "invalid" }; +const char *sps_format_description_string_array[] = { + "unknown", "S8", "U8", "S16", "S16_LE", "S16_BE", "S24", "S24_LE", + "S24_BE", "S24_3LE", "S24_3BE", "S32", "S32_LE", "S32_BE", "auto", "invalid"}; -const char * sps_format_description_string(enum sps_format_t format) { +const char *sps_format_description_string(enum sps_format_t format) { if ((format >= SPS_FORMAT_UNKNOWN) && (format <= SPS_FORMAT_AUTO)) return sps_format_description_string_array[format]; else @@ -115,12 +117,10 @@ void do_sps_log(__attribute__((unused)) int prio, const char *t, ...) { va_start(args, t); vsnprintf(s, sizeof(s), t, args); va_end(args); - fprintf(stderr,"%s\n",s); + fprintf(stderr, "%s\n", s); } -void log_to_stderr() { - sps_log = do_sps_log; -} +void log_to_stderr() { sps_log = do_sps_log; } shairport_cfg config; @@ -183,7 +183,7 @@ void die(const char *format, ...) { else if ((debuglev) && (config.debugger_show_elapsed_time)) sps_log(LOG_ERR, "% 20.9f|*fatal error: %s", tss, s); else - sps_log(LOG_ERR, "fatal error: %s", s); + sps_log(LOG_ERR, "fatal error: %s", s); pthread_setcancelstate(oldState, NULL); abort(); // exit() doesn't always work, by heaven. } @@ -436,7 +436,7 @@ char *base64_enc(uint8_t *input, int length) { b64 = BIO_push(b64, bmem); BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); BIO_write(b64, input, length); - (void) BIO_flush(b64); + (void)BIO_flush(b64); BIO_get_mem_ptr(b64, &bptr); char *buf = (char *)malloc(bptr->length); @@ -468,7 +468,7 @@ uint8_t *base64_dec(char *input, int *outlen) { BIO_write(bmem, input, inlen); while (inlen++ & 3) BIO_write(bmem, "=", 1); - (void) BIO_flush(bmem); + (void)BIO_flush(bmem); int bufsize = strlen(input) * 3 / 4 + 1; uint8_t *buf = malloc(bufsize); @@ -862,17 +862,17 @@ double flat_vol2attn(double vol, long max_db, long min_db) { double vol2attn(double vol, long max_db, long min_db) { - // We use a little coordinate geometry to build a transfer function from the volume passed in to - // the device's dynamic range. (See the diagram in the documents folder.) The x axis is the - // "volume in" which will be from -30 to 0. The y axis will be the "volume out" which will be from - // the bottom of the range to the top. We build the transfer function from one or more lines. We - // characterise each line with two numbers: the first is where on x the line starts when y=0 (x - // can be from 0 to -30); the second is where on y the line stops when when x is -30. thus, if the - // line was characterised as {0,-30}, it would be an identity transfer. Assuming, for example, a - // dynamic range of lv=-60 to hv=0 Typically we'll use three lines -- a three order transfer - // function First: {0,30} giving a gentle slope -- the 30 comes from half the dynamic range - // Second: {-5,-30-(lv+30)/2} giving a faster slope from y=0 at x=-12 to y=-42.5 at x=-30 - // Third: {-17,lv} giving a fast slope from y=0 at x=-19 to y=-60 at x=-30 +// We use a little coordinate geometry to build a transfer function from the volume passed in to +// the device's dynamic range. (See the diagram in the documents folder.) The x axis is the +// "volume in" which will be from -30 to 0. The y axis will be the "volume out" which will be from +// the bottom of the range to the top. We build the transfer function from one or more lines. We +// characterise each line with two numbers: the first is where on x the line starts when y=0 (x +// can be from 0 to -30); the second is where on y the line stops when when x is -30. thus, if the +// line was characterised as {0,-30}, it would be an identity transfer. Assuming, for example, a +// dynamic range of lv=-60 to hv=0 Typically we'll use three lines -- a three order transfer +// function First: {0,30} giving a gentle slope -- the 30 comes from half the dynamic range +// Second: {-5,-30-(lv+30)/2} giving a faster slope from y=0 at x=-12 to y=-42.5 at x=-30 +// Third: {-17,lv} giving a fast slope from y=0 at x=-19 to y=-60 at x=-30 #define order 3 @@ -1000,7 +1000,7 @@ ssize_t non_blocking_write_with_timeout(int fd, const void *buf, size_t count, i } ssize_t non_blocking_write(int fd, const void *buf, size_t count) { - return non_blocking_write_with_timeout(fd,buf,count,5000); // default is 5 seconds. + return non_blocking_write_with_timeout(fd, buf, count, 5000); // default is 5 seconds. } /* from @@ -1189,9 +1189,8 @@ int sps_pthread_mutex_timedlock(pthread_mutex_t *mutex, useconds_t dally_time, et = (et * 1000000) >> 32; // microseconds char errstr[1000]; if (r == ETIMEDOUT) - debug(debuglevel, - "timed out waiting for a mutex, having waiting %f seconds, with a maximum " - "waiting time of %d microseconds. \"%s\".", + debug(debuglevel, "timed out waiting for a mutex, having waiting %f seconds, with a maximum " + "waiting time of %d microseconds. \"%s\".", (1.0 * et) / 1000000, dally_time, debugmessage); else debug(debuglevel, "error %d: \"%s\" waiting for a mutex: \"%s\".", r, @@ -1293,10 +1292,10 @@ char *get_version_string() { strcpy(version_string, PACKAGE_VERSION); #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"); @@ -1382,7 +1381,7 @@ int64_t generate_zero_frames(char *outp, size_t number_of_frames, enum sps_forma // I think, for a 32 --> 16 bits, the range of // random numbers needs to be from -2^16 to 2^16, i.e. from -65536 to 65536 inclusive, not from // -32768 to +32767 - + // Actually, what would be generated here is from -65535 to 65535, i.e. one less on the limits. // See the original paper at @@ -1549,7 +1548,7 @@ int64_t generate_zero_frames(char *outp, size_t number_of_frames, enum sps_forma break; default: result = 0; // stop a compiler warning - die("Unexpected SPS_FORMAT_* with index %d while outputting silence",format); + die("Unexpected SPS_FORMAT_* with index %d while outputting silence", format); } p += result; previous_random_number = r; diff --git a/common.h b/common.h index ae7f5cd3..2a960d41 100644 --- a/common.h +++ b/common.h @@ -34,18 +34,10 @@ enum dbus_session_type { #define sps_extra_code_output_state_cannot_make_ready 32769 // yeah/no/auto -enum yna_type { - YNA_AUTO = -1, - YNA_NO = 0, - YNA_YES = 1 -} yna_type; +enum yna_type { YNA_AUTO = -1, YNA_NO = 0, YNA_YES = 1 } yna_type; // yeah/no/dont-care -enum yndk_type { - YNDK_DONT_KNOW = -1, - YNDK_NO = 0, - YNDK_YES = 1 -} yndk_type; +enum yndk_type { YNDK_DONT_KNOW = -1, YNDK_NO = 0, YNDK_YES = 1 } yndk_type; enum endian_type { SS_LITTLE_ENDIAN = 0, @@ -56,7 +48,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 + ST_auto, // use soxr if compiled for it and if the soxr_index is low enough } s_type; enum playback_mode_type { @@ -95,7 +87,7 @@ enum sps_format_t { SPS_FORMAT_S16_BE, SPS_FORMAT_S24, SPS_FORMAT_S24_LE, - SPS_FORMAT_S24_BE, + SPS_FORMAT_S24_BE, SPS_FORMAT_S24_3LE, SPS_FORMAT_S24_3BE, SPS_FORMAT_S32, @@ -105,7 +97,7 @@ enum sps_format_t { SPS_FORMAT_INVALID, } sps_format_t; -const char * sps_format_description_string(enum sps_format_t format); +const char *sps_format_description_string(enum sps_format_t format); typedef struct { config_t *cfg; @@ -172,7 +164,7 @@ typedef struct { uint32_t userSuppliedLatency; // overrides all other latencies -- use with caution uint32_t fixedLatencyOffset; // add this to all automatic latencies supplied to get the actual // total latency - // the total latency will be limited to the min and max-latency values, if supplied +// the total latency will be limited to the min and max-latency values, if supplied #ifdef CONFIG_LIBDAEMON int daemonise; int daemonise_store_pid; // don't try to save a PID file @@ -192,7 +184,8 @@ typedef struct { 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 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 @@ -215,16 +208,18 @@ typedef struct { // fixed latency there might be in the audio path double audio_backend_silent_lead_in_time; // the length of the silence that should precede a play. double active_state_timeout; // the amount of time from when play ends to when the system leaves - // into the "active" mode. - uint32_t volume_range_db; // the range, in dB, from max dB to min dB. Zero means use the mixer's - // native range. - int volume_range_hw_priority; // when extending the volume range by combining sw and hw attenuators, lowering the volume, use all the hw attenuation before using + // into the "active" mode. + uint32_t volume_range_db; // the range, in dB, from max dB to min dB. Zero means use the mixer's + // native range. + int volume_range_hw_priority; // when extending the volume range by combining sw and hw + // attenuators, lowering the volume, use all the hw attenuation + // before using // sw attenuation enum volume_control_profile_type volume_control_profile; - int output_format_auto_requested; // true if the configuration requests auto configuration + int output_format_auto_requested; // true if the configuration requests auto configuration enum sps_format_t output_format; - int output_rate_auto_requested; // true if the configuration requests auto configuration + int output_rate_auto_requested; // true if the configuration requests auto configuration unsigned int output_rate; #ifdef CONFIG_CONVOLUTION @@ -283,7 +278,8 @@ int get_requested_connection_state_to_output(); void set_requested_connection_state_to_output(int v); -ssize_t non_blocking_write_with_timeout(int fd, const void *buf, size_t count, int timeout); // timeout in milliseconds +ssize_t non_blocking_write_with_timeout(int fd, const void *buf, size_t count, + int timeout); // timeout in milliseconds ssize_t non_blocking_write(int fd, const void *buf, size_t count); // used in a few places diff --git a/dacp.c b/dacp.c index 7f162f79..0b6db70e 100644 --- a/dacp.c +++ b/dacp.c @@ -39,8 +39,8 @@ #include #include #include -#include #include +#include #include #include "metadata_hub.h" @@ -112,10 +112,7 @@ static void response_code(void *opaque, int code) { } static const struct http_funcs responseFuncs = { - response_realloc, - response_body, - response_header, - response_code, + response_realloc, response_body, response_header, response_code, }; // static pthread_mutex_t dacp_conversation_lock = PTHREAD_MUTEX_INITIALIZER; @@ -323,10 +320,9 @@ int dacp_send_command(const char *command, char **body, ssize_t *bodysize) { // debug(1,"Sent command\"%s\" with a response body of size %d.",command,response.size); // debug(1,"dacp_conversation_lock released."); } else { - debug(3, - "dacp_send_command: could not acquire a lock on the dacp transmit/receive section " - "when attempting to " - "send the command \"%s\". Possible timeout?", + debug(3, "dacp_send_command: could not acquire a lock on the dacp transmit/receive section " + "when attempting to " + "send the command \"%s\". Possible timeout?", command); response.code = 494; // This client is already busy } @@ -378,7 +374,7 @@ void set_dacp_server_information(rtsp_conn_info *conn) { if ((conn->dacp_id == NULL) || (strcmp(conn->dacp_id, dacp_server.dacp_id) != 0)) { if (conn->dacp_id) - strncpy(dacp_server.dacp_id, conn->dacp_id, sizeof(dacp_server.dacp_id)-1); + strncpy(dacp_server.dacp_id, conn->dacp_id, sizeof(dacp_server.dacp_id) - 1); else dacp_server.dacp_id[0] = '\0'; dacp_server.port = 0; @@ -423,9 +419,8 @@ void set_dacp_server_information(rtsp_conn_info *conn) { void dacp_monitor_port_update_callback(char *dacp_id, uint16_t port) { debug_mutex_lock(&dacp_server_information_lock, 500000, 2); - debug(3, - "dacp_monitor_port_update_callback with Remote ID \"%s\", target ID \"%s\" and port " - "number %d.", + debug(3, "dacp_monitor_port_update_callback with Remote ID \"%s\", target ID \"%s\" and port " + "number %d.", dacp_id, dacp_server.dacp_id, port); if (strcmp(dacp_id, dacp_server.dacp_id) == 0) { dacp_server.port = port; @@ -473,9 +468,8 @@ void *dacp_monitor_thread_code(__attribute__((unused)) void *na) { (metadata_store.advanced_dacp_server_active != 0); metadata_store.dacp_server_active = 0; metadata_store.advanced_dacp_server_active = 0; - debug(2, - "setting dacp_server_active and advanced_dacp_server_active to 0 with an update " - "flag value of %d", + debug(2, "setting dacp_server_active and advanced_dacp_server_active to 0 with an update " + "flag value of %d", ch); metadata_hub_modify_epilog(ch); while (dacp_server.scan_enable == 0) { diff --git a/dbus-service.c b/dbus-service.c index 47c5b846..e18a3511 100644 --- a/dbus-service.c +++ b/dbus-service.c @@ -433,12 +433,14 @@ gboolean notify_drift_tolerance_callback(ShairportSync *skeleton, } gboolean notify_disable_standby_mode_callback(ShairportSync *skeleton, - __attribute__((unused)) gpointer user_data) { + __attribute__((unused)) gpointer user_data) { char *th = (char *)shairport_sync_get_disable_standby_mode(skeleton); - if ((strcasecmp(th, "no") == 0) || (strcasecmp(th, "off") == 0) || (strcasecmp(th, "never") == 0)) { + if ((strcasecmp(th, "no") == 0) || (strcasecmp(th, "off") == 0) || + (strcasecmp(th, "never") == 0)) { config.disable_standby_mode = disable_standby_off; config.keep_dac_busy = 0; - } else if ((strcasecmp(th, "yes") == 0) || (strcasecmp(th, "on") == 0) || (strcasecmp(th, "always") == 0)) { + } else if ((strcasecmp(th, "yes") == 0) || (strcasecmp(th, "on") == 0) || + (strcasecmp(th, "always") == 0)) { config.disable_standby_mode = disable_standby_always; config.keep_dac_busy = 1; } else if (strcasecmp(th, "auto") == 0) @@ -446,17 +448,17 @@ gboolean notify_disable_standby_mode_callback(ShairportSync *skeleton, else { warn("An unrecognised disable_standby_mode: \"%s\" was requested via D-Bus interface.", th); switch (config.disable_standby_mode) { - case disable_standby_off: - shairport_sync_set_disable_standby_mode(skeleton, "off"); - break; - case disable_standby_always: - shairport_sync_set_disable_standby_mode(skeleton, "always"); - break; - case disable_standby_auto: - shairport_sync_set_disable_standby_mode(skeleton, "auto"); - break; - default: - break; + case disable_standby_off: + shairport_sync_set_disable_standby_mode(skeleton, "off"); + break; + case disable_standby_always: + shairport_sync_set_disable_standby_mode(skeleton, "always"); + break; + case disable_standby_auto: + shairport_sync_set_disable_standby_mode(skeleton, "auto"); + break; + default: + break; } } return TRUE; @@ -661,7 +663,7 @@ static void on_dbus_name_acquired(GDBusConnection *connection, const gchar *name G_CALLBACK(notify_alacdecoder_callback), NULL); g_signal_connect(shairportSyncSkeleton, "notify::disable-standby-mode", G_CALLBACK(notify_disable_standby_mode_callback), NULL); - g_signal_connect(shairportSyncSkeleton, "notify::volume-control-profile", + g_signal_connect(shairportSyncSkeleton, "notify::volume-control-profile", G_CALLBACK(notify_volume_control_profile_callback), NULL); g_signal_connect(shairportSyncSkeleton, "notify::disable-standby", G_CALLBACK(notify_disable_standby_callback), NULL); @@ -749,21 +751,21 @@ static void on_dbus_name_acquired(GDBusConnection *connection, const gchar *name debug(1, ">> Active set to \"false\""); switch (config.disable_standby_mode) { - case disable_standby_off: - shairport_sync_set_disable_standby_mode(SHAIRPORT_SYNC(shairportSyncSkeleton), "off"); - debug(1, ">> disable standby mode set to \"off\""); - break; - case disable_standby_always: - shairport_sync_set_disable_standby_mode(SHAIRPORT_SYNC(shairportSyncSkeleton), "always"); - debug(1, ">> disable standby mode set to \"always\""); - break; - case disable_standby_auto: - shairport_sync_set_disable_standby_mode(SHAIRPORT_SYNC(shairportSyncSkeleton), "auto"); - debug(1, ">> disable standby mode set to \"auto\""); - break; - default: - debug(1,"invalid disable_standby mode!"); - break; + case disable_standby_off: + shairport_sync_set_disable_standby_mode(SHAIRPORT_SYNC(shairportSyncSkeleton), "off"); + debug(1, ">> disable standby mode set to \"off\""); + break; + case disable_standby_always: + shairport_sync_set_disable_standby_mode(SHAIRPORT_SYNC(shairportSyncSkeleton), "always"); + debug(1, ">> disable standby mode set to \"always\""); + break; + case disable_standby_auto: + shairport_sync_set_disable_standby_mode(SHAIRPORT_SYNC(shairportSyncSkeleton), "auto"); + debug(1, ">> disable standby mode set to \"auto\""); + break; + default: + debug(1, "invalid disable_standby mode!"); + break; } #ifdef CONFIG_SOXR @@ -902,9 +904,7 @@ void stop_dbus_service() { g_bus_unown_name(ownerID); else debug(1, "Zero OwnerID for \"org.gnome.ShairportSync\"."); - service_is_running = 0; + service_is_running = 0; } -int dbus_service_is_running() { - return service_is_running; -} +int dbus_service_is_running() { return service_is_running; } diff --git a/mdns_dns_sd.c b/mdns_dns_sd.c index cf178f1d..95bae443 100644 --- a/mdns_dns_sd.c +++ b/mdns_dns_sd.c @@ -24,8 +24,8 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -#include "common.h" #include "mdns.h" +#include "common.h" #include #include #include diff --git a/metadata_hub.c b/metadata_hub.c index 4d7def38..400c1e55 100644 --- a/metadata_hub.c +++ b/metadata_hub.c @@ -46,8 +46,8 @@ #include "metadata_hub.h" #ifdef CONFIG_MBEDTLS -#include #include +#include #endif #ifdef CONFIG_POLARSSL @@ -223,7 +223,7 @@ char *metadata_write_image_file(const char *buf, int len) { char *path = NULL; // this will be what is returned uint8_t img_md5[16]; - // uint8_t ap_md5[16]; +// uint8_t ap_md5[16]; #ifdef CONFIG_OPENSSL MD5_CTX ctx; @@ -233,17 +233,17 @@ char *metadata_write_image_file(const char *buf, int len) { #endif #ifdef CONFIG_MBEDTLS - #if MBEDTLS_VERSION_MINOR >= 7 - mbedtls_md5_context tctx; - mbedtls_md5_starts_ret(&tctx); - mbedtls_md5_update_ret(&tctx, (const unsigned char *)buf, len); - mbedtls_md5_finish_ret(&tctx, img_md5); - #else - mbedtls_md5_context tctx; - mbedtls_md5_starts(&tctx); - mbedtls_md5_update(&tctx, (const unsigned char *)buf, len); - mbedtls_md5_finish(&tctx, img_md5); - #endif +#if MBEDTLS_VERSION_MINOR >= 7 + mbedtls_md5_context tctx; + mbedtls_md5_starts_ret(&tctx); + mbedtls_md5_update_ret(&tctx, (const unsigned char *)buf, len); + mbedtls_md5_finish_ret(&tctx, img_md5); +#else + mbedtls_md5_context tctx; + mbedtls_md5_starts(&tctx); + mbedtls_md5_update(&tctx, (const unsigned char *)buf, len); + mbedtls_md5_finish(&tctx, img_md5); +#endif #endif #ifdef CONFIG_POLARSSL diff --git a/player.c b/player.c index 92bda3a5..17982813 100644 --- a/player.c +++ b/player.c @@ -656,7 +656,7 @@ static inline void process_sample(int32_t sample, char **outp, enum sps_format_t // I think, for a 32 --> 16 bits, the range of // random numbers needs to be from -2^16 to 2^16, i.e. from -65536 to 65536 inclusive, not from // -32768 to +32767 - + // Actually, what would be generated here is from -65535 to 65535, i.e. one less on the limits. // See the original paper at @@ -1522,7 +1522,8 @@ int stuff_buffer_soxr_32(int32_t *inptr, int32_t *scratchBuffer, int length, debug(3, "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, stat_n <= 1 ? 0.0 : sqrtf(stat_M2 / (stat_n - 1)), longest_soxr_execution_time_us); + stat_n, stat_mean, stat_n <= 1 ? 0.0 : sqrtf(stat_M2 / (stat_n - 1)), + longest_soxr_execution_time_us); stat_n = 0; stat_mean = 0.0; stat_M2 = 0.0; @@ -1675,7 +1676,7 @@ void *player_thread_func(void *arg) { case SPS_FORMAT_S24_3BE: conn->output_bytes_per_frame = 6; break; - + case SPS_FORMAT_S24: case SPS_FORMAT_S24_LE: case SPS_FORMAT_S24_BE: @@ -1801,7 +1802,6 @@ void *player_thread_func(void *arg) { if ((config.output->parameters == NULL) || (conn->input_bit_depth > output_bit_depth) || (config.playback_mode == ST_mono)) conn->enable_dither = 1; - // remember, the output device may never have been initialised prior to this call config.output->start(config.output_rate, config.output_format); // will need a corresponding stop @@ -2386,15 +2386,17 @@ void *player_thread_func(void *arg) { if ((current_delay < conn->dac_buffer_queue_minimum_length) || (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 + ((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 { // soxr requested or auto requested with the index less or equal to the threshold + } 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); @@ -2943,8 +2945,8 @@ int player_play(rtsp_conn_info *conn) { command_start(); // call on the output device to prepare itself if ((config.output) && (config.output->prepare)) - config.output->prepare(); - + config.output->prepare(); + pthread_t *pt = malloc(sizeof(pthread_t)); if (pt == NULL) die("Couldn't allocate space for pthread_t"); diff --git a/rtp.c b/rtp.c index 7496cc70..2e1e7f9f 100644 --- a/rtp.c +++ b/rtp.c @@ -137,9 +137,8 @@ void *rtp_audio_receiver(void *arg) { stat_mean += stat_delta / stat_n; stat_M2 += stat_delta * (time_interval_us - stat_mean); if (stat_n % 2500 == 0) { - debug(2, - "Packet reception interval stats: mean, standard deviation and max for the last " - "2,500 packets in microseconds: %10.1f, %10.1f, %10.1f.", + debug(2, "Packet reception interval stats: mean, standard deviation and max for the last " + "2,500 packets in microseconds: %10.1f, %10.1f, %10.1f.", stat_mean, sqrtf(stat_M2 / (stat_n - 1)), longest_packet_time_interval_us); stat_n = 0; stat_mean = 0.0; @@ -369,10 +368,9 @@ void *rtp_control_receiver(void *arg) { if (la != conn->latency) { conn->latency = la; - debug(3, - "New latency detected: %" PRIu32 ", sync latency: %" PRIu32 - ", minimum latency: %" PRIu32 ", maximum " - "latency: %" PRIu32 ", fixed offset: %" PRIu32 ".", + debug(3, "New latency detected: %" PRIu32 ", sync latency: %" PRIu32 + ", minimum latency: %" PRIu32 ", maximum " + "latency: %" PRIu32 ", fixed offset: %" PRIu32 ".", la, sync_rtp_timestamp - rtp_timestamp_less_latency, conn->minimum_latency, conn->maximum_latency, config.fixedLatencyOffset); } @@ -396,11 +394,11 @@ void *rtp_control_receiver(void *arg) { remote_frame_time_interval; // an IEEE double calculation with a 32-bit // numerator and 64-bit denominator // integers - conn->remote_frame_rate = conn->remote_frame_rate * - (uint64_t)0x100000000; // this should just change the - // [binary] exponent in the IEEE - // FP representation; the - // mantissa should be unaffected. + conn->remote_frame_rate = + conn->remote_frame_rate * (uint64_t)0x100000000; // this should just change the + // [binary] exponent in the IEEE + // FP representation; the + // mantissa should be unaffected. } else { conn->remote_frame_rate = 0.0; // use as a flag. } @@ -824,7 +822,7 @@ void *rtp_timing_receiver(void *arg) { conn->local_to_remote_time_gradient = (1.0 * mtl) / mbl; else { conn->local_to_remote_time_gradient = 1.0; - debug(1,"rtp_timing_receiver: mbl is 0"); + debug(1, "rtp_timing_receiver: mbl is 0"); } } else { conn->local_to_remote_time_gradient = 1.0; @@ -1105,7 +1103,7 @@ int sanitised_source_rate_information(uint32_t *frames, uint64_t *time, rtsp_con if (local_time) calculated_frame_rate = ((1.0 * local_frames) / local_time) * one_fp; else - debug(1,"sanitised_source_rate_information: local_time is zero"); + debug(1, "sanitised_source_rate_information: local_time is zero"); if ((local_time == 0) || ((calculated_frame_rate / conn->input_rate) > 1.002) || ((calculated_frame_rate / conn->input_rate) < 0.998)) { debug(3, "input frame rate out of bounds at %.2f fps.", calculated_frame_rate); @@ -1188,7 +1186,7 @@ int local_time_to_frame(uint64_t time, uint32_t *frame, rtsp_conn_info *conn) { if (time_difference) frame_interval = (time_interval * frame_difference) / time_difference; else - debug(1,"local_time_to_frame: time_difference is zero"); + debug(1, "local_time_to_frame: time_difference is zero"); if (reference_time_was_earlier) { // debug(1,"Frame interval is %" PRId64 " frames.",frame_interval); *frame = (conn->reference_timestamp + frame_interval); @@ -1240,9 +1238,8 @@ void rtp_request_resend(seq_t first, uint32_t count, rtsp_conn_info *conn) { (struct sockaddr *)&conn->rtp_client_control_socket, msgsize) == -1) { char em[1024]; strerror_r(errno, em, sizeof(em)); - debug(1, - "Error %d using sendto to an audio socket: \"%s\". Backing off for 1/16th of a " - "second.", + debug(1, "Error %d using sendto to an audio socket: \"%s\". Backing off for 1/16th of a " + "second.", errno, em); conn->rtp_time_of_last_resend_request_error_fp = time_of_sending_fp; } else { diff --git a/shairport.c b/shairport.c index 667a565b..499b4c0b 100644 --- a/shairport.c +++ b/shairport.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -38,10 +39,8 @@ #include #include #include -#include #include - #include "config.h" #ifdef CONFIG_MBEDTLS @@ -97,15 +96,14 @@ #endif #ifdef CONFIG_SOXR -#include #include +#include #endif #ifdef CONFIG_CONVOLUTION #include #endif - #ifdef CONFIG_LIBDAEMON pid_t pid; #endif @@ -165,33 +163,34 @@ void print_version(void) { } #ifdef CONFIG_SOXR -pthread_t soxr_time_check_thread; -void* soxr_time_check(__attribute__((unused)) void *arg) { +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 *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 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; // - + const double frequency = 440; // + int i; - + int number_of_iterations = 0; uint64_t soxr_start_time = get_absolute_time_in_fp(); - uint64_t loop_until_time = (uint64_t)0x180000000 + soxr_start_time; // loop for a second and a half, 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++; - for (i = 0; i < buffer_length ; i++) { - double w = sin(i * (frequency + number_of_iterations * 2) * 2 * M_PI/44100); + for (i = 0; i < buffer_length; i++) { + 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; } - + soxr_io_spec_t io_spec; io_spec.itype = SOXR_INT32_I; io_spec.otype = SOXR_INT32_I; @@ -201,37 +200,39 @@ void* soxr_time_check(__attribute__((unused)) void *arg) { 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. - + 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. - + 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; + (((get_absolute_time_in_fp() - soxr_start_time) * 1000000) >> 32) * 1.0; // free(outbuffer); // free(inbuffer); - config.soxr_delay_index = (int)(0.9 + soxr_execution_time_us/(number_of_iterations *1000)); - debug(2,"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."); + config.soxr_delay_index = (int)(0.9 + soxr_execution_time_us / (number_of_iterations * 1000)); + debug(2, "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); + debug(1, "\"%s\" interpolation has been chosen.", + config.soxr_delay_index <= config.soxr_delay_threshold ? "soxr" : "basic"); + pthread_exit(NULL); } #endif @@ -428,10 +429,14 @@ 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.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 + 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 +// this is because some hw mixers mute at the bottom of their range, and they don't always advertise +// this fact #ifdef CONFIG_METADATA_HUB config.cover_art_cache_dir = "/tmp/shairport-sync/.cache/coverart"; @@ -536,13 +541,13 @@ int parse_options(int argc, char **argv) { config.packet_stuffing = ST_soxr; #else warn("The soxr option not available because this version of shairport-sync was built " - "without libsoxr " - "support. Change the \"general/interpolation\" setting in the configuration file."); + "without libsoxr " + "support. Change the \"general/interpolation\" setting in the configuration file."); #endif else 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)) { @@ -550,9 +555,9 @@ int parse_options(int argc, char **argv) { 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); + "between 0 and 100, " + "inclusive. Default is %d (milliseconds).", + value, config.soxr_delay_threshold); } #endif @@ -1003,10 +1008,10 @@ int parse_options(int argc, char **argv) { config.mqtt_port = 1883; if (config_lookup_int(config.cfg, "mqtt.port", &value)) { if ((value < 0) || (value > 65535)) - die("Invalid mqtt port number \"%sd\". It should be between 0 and 65535, default is 1883", - value); + die("Invalid mqtt port number \"%sd\". It should be between 0 and 65535, default is 1883", + value); else - config.mqtt_port = value; + config.mqtt_port = value; } if (config_lookup_string(config.cfg, "mqtt.username", &str)) { @@ -1051,11 +1056,11 @@ int parse_options(int argc, char **argv) { } config_set_lookup_bool(config.cfg, "mqtt.enable_remote", &config.mqtt_enable_remote); #ifndef CONFIG_AVAHI - if (config.mqtt_enable_remote) { - die("You have enabled MQTT remote control which requires shairport-sync to be built with " - "Avahi, but your installation is not using avahi. Please reinstall/recompile with " - "avahi enabled, or disable remote control."); - } + if (config.mqtt_enable_remote) { + die("You have enabled MQTT remote control which requires shairport-sync to be built with " + "Avahi, but your installation is not using avahi. Please reinstall/recompile with " + "avahi enabled, or disable remote control."); + } #endif #endif } @@ -1125,7 +1130,6 @@ int parse_options(int argc, char **argv) { // here, we are finally finished reading the options - #ifdef CONFIG_LIBDAEMON if ((daemonisewith) && (daemonisewithout)) die("Select either daemonize_with_pid_file or daemonize_without_pid_file -- you have selected " @@ -1138,7 +1142,9 @@ int parse_options(int argc, char **argv) { #else /* Check if we are called with -d or --daemon or -j or justDaemoniseNoPIDFile options*/ if ((daemonisewith != 0) || (daemonisewithout != 0)) { - fprintf(stderr,"%s was built without libdaemon, so does not support daemonisation using the -d, --deamon, -j or --justDaemoniseNoPIDFile options\n",config.appName); + fprintf(stderr, "%s was built without libdaemon, so does not support daemonisation using the " + "-d, --deamon, -j or --justDaemoniseNoPIDFile options\n", + config.appName); exit(EXIT_FAILURE); } @@ -1313,16 +1319,16 @@ void main_cleanup_handler(__attribute__((unused)) void *arg) { debug(2, "Deinitialise the audio backend."); config.output->deinit(); } - + #ifdef CONFIG_LIBDAEMON -// only do this if you are the daemon + // only do this if you are the daemon if (pid == 0) { daemon_retval_send(0); daemon_pid_file_remove(); daemon_signal_done(); - } + } #endif - + debug(2, "Exit..."); exit(EXIT_SUCCESS); } @@ -1366,12 +1372,12 @@ int main(int argc, char **argv) { die("can not allocate memory for the app name!"); free(basec); - // debug(1,"startup"); +// debug(1,"startup"); #ifdef CONFIG_LIBDAEMON daemon_set_verbosity(LOG_DEBUG); #else - setlogmask (LOG_UPTO (LOG_DEBUG)); - openlog(NULL,0,LOG_DAEMON); + setlogmask(LOG_UPTO(LOG_DEBUG)); + openlog(NULL, 0, LOG_DAEMON); #endif atexit(exit_function); @@ -1410,8 +1416,9 @@ int main(int argc, char **argv) { // config.statistics_requested = 0; // don't print stats in the log // config.userSuppliedLatency = 0; // zero means none supplied - - config.debugger_show_relative_time = 1; // by default, log the time back to the previous debug message + + config.debugger_show_relative_time = + 1; // by default, log the time back to the previous debug message config.resyncthreshold = 0.05; // 50 ms config.timeout = 120; // this number of seconds to wait for [more] audio before switching to idle. config.tolerance = @@ -1419,14 +1426,13 @@ int main(int argc, char **argv) { config.buffer_start_fill = 220; config.port = 5000; - #ifdef CONFIG_SOXR - config.packet_stuffing = ST_auto; // use soxr interpolation by default if support has been included and if the CPU is fast enough + 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); // config.service_name = malloc(20 + 100); @@ -1488,7 +1494,6 @@ int main(int argc, char **argv) { #endif - // parse arguments into config -- needed to locate pid_dir int audio_arg = parse_options(argc, argv); @@ -1499,7 +1504,7 @@ int main(int argc, char **argv) { } /* Check if we are called with -k or --kill option */ - if (killOption != 0) { + if (killOption != 0) { #ifdef CONFIG_LIBDAEMON int ret; @@ -1512,7 +1517,8 @@ int main(int argc, char **argv) { } return ret < 0 ? 1 : 0; #else - fprintf(stderr,"%s was built without libdaemon, so does not support the -k or --kill option\n",config.appName); + fprintf(stderr, "%s was built without libdaemon, so does not support the -k or --kill option\n", + config.appName); return 1; #endif } @@ -1591,7 +1597,7 @@ int main(int argc, char **argv) { daemon_signal_done(); return 0; } - + if (daemon_pid_file_create() < 0) { daemon_log(LOG_ERR, "Could not create PID file (%s).", strerror(errno)); @@ -1608,7 +1614,7 @@ int main(int argc, char **argv) { } #endif - debug(1,"Started!"); + debug(1, "Started!"); main_thread_id = pthread_self(); if (!main_thread_id) debug(1, "Main thread is set up to be NULL!"); @@ -1618,7 +1624,6 @@ 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(); @@ -1628,7 +1633,7 @@ int main(int argc, char **argv) { } else { debug(1, "can't print the version information!"); } - + debug(1, "log verbosity is %d.", debuglev); config.output = audio_get_output(config.output_name); @@ -1701,7 +1706,9 @@ 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 \"%s\".", config.packet_stuffing == ST_basic ? "basic" : config.packet_stuffing == ST_soxr ? "soxr" : "auto"); + 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); @@ -1715,25 +1722,25 @@ int main(int argc, char **argv) { debug(1, "volume_max_db is not set"); debug(1, "volume range in dB (zero means use the range specified by the mixer): %u.", config.volume_range_db); - debug( - 1, - "volume_range_combined_hardware_priority (1 means hardware mixer attenuation is used first) is %d.", - config.volume_range_hw_priority); + debug(1, "volume_range_combined_hardware_priority (1 means hardware mixer attenuation is used " + "first) is %d.", + config.volume_range_hw_priority); debug(1, "playback_mode is %d (0-stereo, 1-mono, 1-reverse_stereo, 2-both_left, 3-both_right).", config.playback_mode); debug(1, "disable_synchronization is %d.", config.no_sync); debug(1, "use_mmap_if_available is %d.", config.no_mmap ? 0 : 1); - debug(1, "output_format automatic selection is %sabled.", config.output_format_auto_requested ? "en" : "dis"); + debug(1, "output_format automatic selection is %sabled.", + config.output_format_auto_requested ? "en" : "dis"); if (config.output_format_auto_requested == 0) - debug(1, - "output_format is \"%s\".", - sps_format_description_string(config.output_format)); - debug(1, "output_rate automatic selection is %sabled.", config.output_rate_auto_requested ? "en" : "dis"); + debug(1, "output_format is \"%s\".", sps_format_description_string(config.output_format)); + debug(1, "output_rate automatic selection is %sabled.", + config.output_rate_auto_requested ? "en" : "dis"); if (config.output_rate_auto_requested == 0) - debug(1, "output_rate is %d.", config.output_rate); + debug(1, "output_rate is %d.", config.output_rate); debug(1, "audio backend desired buffer length is %f seconds.", config.audio_backend_buffer_desired_length); - debug(1, "audio_backend_buffer_interpolation_threshold_in_seconds is %f seconds.", config.audio_backend_buffer_interpolation_threshold_in_seconds); + debug(1, "audio_backend_buffer_interpolation_threshold_in_seconds is %f seconds.", + config.audio_backend_buffer_interpolation_threshold_in_seconds); debug(1, "audio backend latency offset is %f seconds.", config.audio_backend_latency_offset); debug(1, "audio backend silence lead-in time is %f seconds. A value -1.0 means use the default.", config.audio_backend_silent_lead_in_time); @@ -1783,7 +1790,7 @@ 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); + pthread_create(&soxr_time_check_thread, NULL, &soxr_time_check, NULL); #endif #ifdef CONFIG_OPENSSL From a8faacd3fcf7868622e2f079a2d8284f50235371 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Thu, 20 Jun 2019 10:45:27 +0100 Subject: [PATCH 26/33] Expose some of the settings used in the disable_standby_mode. Specifically, firstly, the disable_standby_mode_silence_threshold, which is the amount of audio in the output device's hardware buffer. It should normally be close to the value givein in the audio_backend_buffer_desired_length_in_seconds setting. If it drops to this value, silence is added to the buffer to prevent the output device from becoming idle. Secondly, the disable_standby_mode_silence_scan_interval is the time between checks of the output device's hardware buffer. The code for generating frames of possibly-dithered silence has been simplified. --- audio_alsa.c | 68 ++++++----- common.c | 224 +++++++++++++++--------------------- common.h | 5 +- scripts/shairport-sync.conf | 2 + 4 files changed, 140 insertions(+), 159 deletions(-) diff --git a/audio_alsa.c b/audio_alsa.c index 99fb945c..69aaa0a3 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -1003,16 +1003,14 @@ static int init(int argc, char **argv) { config.audio_backend_buffer_interpolation_threshold_in_seconds = 0.120; // below this, basic interpolation will be used to save time. config.alsa_maximum_stall_time = 0.200; // 200 milliseconds -- if it takes longer, it's a problem - config.audio_backend_silence_threshold = + config.disable_standby_mode_silence_threshold = 0.040; // start sending silent frames if the delay goes below this time - config.audio_backend_silence_scan_interval = 0.004; // check silence threshold this often + config.disable_standby_mode_silence_scan_interval = 0.004; // check silence threshold this often stall_monitor_error_threshold = (uint64_t)1000000 * config.alsa_maximum_stall_time; // stall time max to microseconds; stall_monitor_error_threshold = (stall_monitor_error_threshold << 32) / 1000000; // now in fp form - debug(1, - "stall_monitor_error_threshold is 0x%" PRIx64 ", with alsa_maximum_stall_time of %f sec.", - stall_monitor_error_threshold, config.alsa_maximum_stall_time); + debug(1, "alsa: alsa_maximum_stall_time of %f sec.", config.alsa_maximum_stall_time); stall_monitor_start_time = 0; stall_monitor_frame_count = 0; @@ -1233,6 +1231,29 @@ static int init(int argc, char **argv) { } } + /* Get the optional disable_standby_mode_silence_threshold setting. */ + if (config_lookup_float(config.cfg, "alsa.disable_standby_mode_silence_threshold", &dvalue)) { + if (dvalue < 0.0) { + warn("Invalid alsa disable_standby_mode_silence_threshold setting \"%f\". It " + "must be greater than 0. Default is \"%f\". No setting is made.", + dvalue, config.disable_standby_mode_silence_threshold); + } else { + config.disable_standby_mode_silence_threshold = dvalue; + } + } + + /* Get the optional disable_standby_mode_silence_scan_interval setting. */ + if (config_lookup_float(config.cfg, "alsa.disable_standby_mode_silence_scan_interval", + &dvalue)) { + if (dvalue < 0.0) { + warn("Invalid alsa disable_standby_mode_silence_scan_interval setting \"%f\". It " + "must be greater than 0. Default is \"%f\". No setting is made.", + dvalue, config.disable_standby_mode_silence_scan_interval); + } else { + config.disable_standby_mode_silence_scan_interval = dvalue; + } + } + /* Get the optional disable_standby_mode setting. */ if (config_lookup_string(config.cfg, "alsa.disable_standby_mode", &str)) { if ((strcasecmp(str, "no") == 0) || (strcasecmp(str, "off") == 0) || @@ -1276,6 +1297,10 @@ static int init(int argc, char **argv) { config.disable_standby_mode == disable_standby_off ? "never" : config.disable_standby_mode == disable_standby_always ? "always" : "auto"); + debug(1, "alsa: disable_standby_mode_silence_threshold is %f seconds.", + config.disable_standby_mode_silence_threshold); + debug(1, "alsa: disable_standby_mode_silence_scan_interval is %f seconds.", + config.disable_standby_mode_silence_scan_interval); } optind = 1; // optind=0 is equivalent to optind=1 plus special behaviour @@ -1643,7 +1668,7 @@ int do_play(void *buf, int samples) { measurement_data_is_valid = 0; if (ret == -EPIPE) { /* underrun */ debug(1, "alsa: underrun while writing %d samples to alsa device.", samples); - int tret = snd_pcm_recover(alsa_handle, ret, debuglev > 0 ? 0 : 1); + int tret = snd_pcm_recover(alsa_handle, ret, 1); if (tret < 0) { warn("alsa: can't recover from SND_PCM_STATE_XRUN: %s.", snd_strerror(tret)); } @@ -1898,9 +1923,9 @@ void alsa_buffer_monitor_thread_cleanup_function(__attribute__((unused)) void void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) { int frame_count = 0; int error_count = 0; - int error_threshold_exceeded = 0; + int error_detected = 0; int okb = -1; - while (error_threshold_exceeded == + while (error_detected == 0) { // if too many play errors occur early on, we will turn off the disable stanby mode if (okb != config.keep_dac_busy) { debug(2, "keep_dac_busy is now \"%s\"", config.keep_dac_busy == 0 ? "no" : "yes"); @@ -1911,7 +1936,7 @@ void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) { "do_alsa_device_init_if_needed."); do_alsa_device_init_if_needed(); } - int sleep_time_ms = (int)(config.audio_backend_silence_scan_interval * 1000); + int sleep_time_ms = (int)(config.disable_standby_mode_silence_scan_interval * 1000); pthread_cleanup_debug_mutex_lock(&alsa_mutex, 200000, 0); // check possible state transitions here if ((alsa_backend_state == abm_disconnected) && (config.keep_dac_busy != 0)) { @@ -1950,25 +1975,19 @@ void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) { (char *)errorstring); } long buffer_size_threshold = - (long)(config.audio_backend_silence_threshold * config.output_rate); + (long)(config.disable_standby_mode_silence_threshold * config.output_rate); + size_t size_of_silence_buffer; if (buffer_size < buffer_size_threshold) { uint64_t sleep_time_in_fp = sleep_time_ms; sleep_time_in_fp = sleep_time_in_fp << 32; sleep_time_in_fp = sleep_time_in_fp / 1000; - // debug(1,"alsa: sleep_time: %d ms or 0x%" PRIx64 " in fp - // form.",sleep_time_ms,sleep_time_in_fp); int frames_of_silence = - // (config.output_rate * - // sleep_time_ms * 2) / 1000; int frames_of_silence = 1024; - size_t size_of_silence_buffer = frames_of_silence * frame_size; - // debug(1, "alsa: alsa_buffer_monitor_thread_code -- silence buffer - // length: %u bytes.", - // size_of_silence_buffer); + size_of_silence_buffer = frames_of_silence * frame_size; void *silence = malloc(size_of_silence_buffer); if (silence == NULL) { - debug(1, "alsa: alsa_buffer_monitor_thread_code -- failed to " - "allocate memory for a " - "silent frame buffer."); + warn("disable_standby_mode has been turned off because a memory allocation error " + "occurred."); + error_detected = 1; } else { int ret; pthread_cleanup_push(malloc_cleanup, silence); @@ -1980,12 +1999,9 @@ void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) { generate_zero_frames(silence, frames_of_silence, config.output_format, use_dither, // i.e. with dither dither_random_number_store); - // debug(1,"Play %d frames of silence with most_recent_write_time of - // %" PRIx64 ".", - // frames_of_silence,most_recent_write_time); ret = do_play(silence, frames_of_silence); frame_count++; - pthread_cleanup_pop(1); + pthread_cleanup_pop(1); // free malloced buffer if (ret < 0) { error_count++; char errorstring[1024]; @@ -1997,7 +2013,7 @@ void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) { warn("disable_standby_mode has been turned off because too many underruns " "occurred. Is Shairport Sync outputting to a virtual device or running in a " "virtual machine?"); - error_threshold_exceeded = 1; + error_detected = 1; } } } diff --git a/common.c b/common.c index bdbb9a87..39648161 100644 --- a/common.c +++ b/common.c @@ -1368,61 +1368,62 @@ int64_t generate_zero_frames(char *outp, size_t number_of_frames, enum sps_forma // return the last random number used // assuming the buffer has been assigned + // add a TPDF dither -- see + // http://educypedia.karadimov.info/library/DitherExplained.pdf + // and the discussion around https://www.hydrogenaud.io/forums/index.php?showtopic=16963&st=25 + + // I think, for a 32 --> 16 bits, the range of + // random numbers needs to be from -2^16 to 2^16, i.e. from -65536 to 65536 inclusive, not from + // -32768 to +32767 + + // Actually, what would be generated here is from -65535 to 65535, i.e. one less on the limits. + + // See the original paper at + // http://www.ece.rochester.edu/courses/ECE472/resources/Papers/Lipshitz_1992.pdf + // by Lipshitz, Wannamaker and Vanderkooy, 1992. + + int64_t dither_mask = 0; + switch (format) { + case SPS_FORMAT_S32: + case SPS_FORMAT_S32_LE: + case SPS_FORMAT_S32_BE: + dither_mask = (int64_t)1 << (64 - 32); + break; + case SPS_FORMAT_S24: + case SPS_FORMAT_S24_LE: + case SPS_FORMAT_S24_BE: + case SPS_FORMAT_S24_3LE: + case SPS_FORMAT_S24_3BE: + dither_mask = (int64_t)1 << (64 - 24); + break; + case SPS_FORMAT_S16: + case SPS_FORMAT_S16_LE: + case SPS_FORMAT_S16_BE: + dither_mask = (int64_t)1 << (64 - 16); + break; + case SPS_FORMAT_S8: + case SPS_FORMAT_U8: + dither_mask = (int64_t)1 << (64 - 8); + break; + case SPS_FORMAT_UNKNOWN: + die("Unexpected SPS_FORMAT_UNKNOWN while calculating dither mask."); + break; + case SPS_FORMAT_AUTO: + die("Unexpected SPS_FORMAT_AUTO while calculating dither mask."); + break; + case SPS_FORMAT_INVALID: + die("Unexpected SPS_FORMAT_INVALID while calculating dither mask."); + break; + } + dither_mask -= 1; + int64_t previous_random_number = random_number_in; char *p = outp; size_t sample_number; for (sample_number = 0; sample_number < number_of_frames * 2; sample_number++) { int64_t hyper_sample = 0; - // add a TPDF dither -- see - // http://educypedia.karadimov.info/library/DitherExplained.pdf - // and the discussion around https://www.hydrogenaud.io/forums/index.php?showtopic=16963&st=25 - // I think, for a 32 --> 16 bits, the range of - // random numbers needs to be from -2^16 to 2^16, i.e. from -65536 to 65536 inclusive, not from - // -32768 to +32767 - - // Actually, what would be generated here is from -65535 to 65535, i.e. one less on the limits. - - // See the original paper at - // http://www.ece.rochester.edu/courses/ECE472/resources/Papers/Lipshitz_1992.pdf - // by Lipshitz, Wannamaker and Vanderkooy, 1992. - - int64_t dither_mask = 0; - switch (format) { - case SPS_FORMAT_S32: - case SPS_FORMAT_S32_LE: - case SPS_FORMAT_S32_BE: - dither_mask = (int64_t)1 << (64 - 32); - break; - case SPS_FORMAT_S24: - case SPS_FORMAT_S24_LE: - case SPS_FORMAT_S24_BE: - case SPS_FORMAT_S24_3LE: - case SPS_FORMAT_S24_3BE: - dither_mask = (int64_t)1 << (64 - 24); - break; - case SPS_FORMAT_S16: - case SPS_FORMAT_S16_LE: - case SPS_FORMAT_S16_BE: - dither_mask = (int64_t)1 << (64 - 16); - break; - case SPS_FORMAT_S8: - case SPS_FORMAT_U8: - dither_mask = (int64_t)1 << (64 - 8); - break; - case SPS_FORMAT_UNKNOWN: - die("Unexpected SPS_FORMAT_UNKNOWN while calculating dither mask."); - break; - case SPS_FORMAT_AUTO: - die("Unexpected SPS_FORMAT_AUTO while calculating dither mask."); - break; - case SPS_FORMAT_INVALID: - die("Unexpected SPS_FORMAT_INVALID while calculating dither mask."); - break; - } - dither_mask -= 1; - // int64_t r = r64i(); int64_t r = ranarray64i(); int64_t tpdf = (r & dither_mask) - (previous_random_number & dither_mask); @@ -1434,126 +1435,87 @@ int64_t generate_zero_frames(char *outp, size_t number_of_frames, enum sps_forma // move the result to the desired position in the int64_t char *op = p; - int result; // this is the length of the sample + int sample_length; // this is the length of the sample - uint8_t byt; switch (format) { case SPS_FORMAT_S32: hyper_sample >>= (64 - 32); *(int32_t *)op = hyper_sample; - result = 4; + sample_length = 4; break; case SPS_FORMAT_S32_LE: - hyper_sample >>= (64 - 32); - byt = (uint8_t)hyper_sample; - *op++ = byt; - byt = (uint8_t)(hyper_sample >> 8); - *op++ = byt; - byt = (uint8_t)(hyper_sample >> 16); - *op++ = byt; - byt = (uint8_t)(hyper_sample >> 24); - *op++ = byt; - result = 4; + *op++ = (uint8_t)(hyper_sample >> (64 - 32)); // 32 bits, ls byte + *op++ = (uint8_t)(hyper_sample >> (64 - 32 + 8)); // 32 bits, less significant middle byte + *op++ = (uint8_t)(hyper_sample >> (64 - 32 + 16)); // 32 bits, more significant middle byte + *op = (uint8_t)(hyper_sample >> (64 - 32 + 24)); // 32 bits, ms byte + sample_length = 4; break; case SPS_FORMAT_S32_BE: - hyper_sample >>= (64 - 32); - byt = (uint8_t)(hyper_sample >> 24); - *op++ = byt; - byt = (uint8_t)(hyper_sample >> 16); - *op++ = byt; - byt = (uint8_t)(hyper_sample >> 8); - *op++ = byt; - byt = (uint8_t)hyper_sample; - *op++ = byt; - result = 4; + *op++ = (uint8_t)(hyper_sample >> (64 - 32 + 24)); // 32 bits, ms byte + *op++ = (uint8_t)(hyper_sample >> (64 - 32 + 16)); // 32 bits, more significant middle byte + *op++ = (uint8_t)(hyper_sample >> (64 - 32 + 8)); // 32 bits, less significant middle byte + *op = (uint8_t)(hyper_sample >> (64 - 32)); // 32 bits, ls byte + sample_length = 4; break; case SPS_FORMAT_S24_3LE: - hyper_sample >>= (64 - 24); - byt = (uint8_t)hyper_sample; - *op++ = byt; - byt = (uint8_t)(hyper_sample >> 8); - *op++ = byt; - byt = (uint8_t)(hyper_sample >> 16); - *op++ = byt; - result = 3; + *op++ = (uint8_t)(hyper_sample >> (64 - 24)); // 24 bits, ls byte + *op++ = (uint8_t)(hyper_sample >> (64 - 24 + 8)); // 24 bits, middle byte + *op = (uint8_t)(hyper_sample >> (64 - 24 + 16)); // 24 bits, ms byte + sample_length = 3; break; case SPS_FORMAT_S24_3BE: - hyper_sample >>= (64 - 24); - byt = (uint8_t)(hyper_sample >> 16); - *op++ = byt; - byt = (uint8_t)(hyper_sample >> 8); - *op++ = byt; - byt = (uint8_t)hyper_sample; - *op++ = byt; - result = 3; + *op++ = (uint8_t)(hyper_sample >> (64 - 24 + 16)); // 24 bits, ms byte + *op++ = (uint8_t)(hyper_sample >> (64 - 24 + 8)); // 24 bits, middle byte + *op = (uint8_t)(hyper_sample >> (64 - 24)); // 24 bits, ls byte + sample_length = 3; break; case SPS_FORMAT_S24: hyper_sample >>= (64 - 24); *(int32_t *)op = hyper_sample; - result = 4; + sample_length = 4; break; case SPS_FORMAT_S24_LE: - hyper_sample >>= (64 - 24); - byt = (uint8_t)hyper_sample; - *op++ = byt; - byt = (uint8_t)(hyper_sample >> 8); - *op++ = byt; - byt = (uint8_t)(hyper_sample >> 16); - *op++ = byt; - *op++ = 0; - result = 4; + *op++ = (uint8_t)(hyper_sample >> (64 - 24)); // 24 bits, ls byte + *op++ = (uint8_t)(hyper_sample >> (64 - 24 + 8)); // 24 bits, middle byte + *op++ = (uint8_t)(hyper_sample >> (64 - 24 + 16)); // 24 bits, ms byte + *op = 0; + sample_length = 4; break; case SPS_FORMAT_S24_BE: - hyper_sample >>= (64 - 24); *op++ = 0; - byt = (uint8_t)(hyper_sample >> 16); - *op++ = byt; - byt = (uint8_t)(hyper_sample >> 8); - *op++ = byt; - byt = (uint8_t)hyper_sample; - *op++ = byt; - result = 4; + *op++ = (uint8_t)(hyper_sample >> (64 - 24 + 16)); // 24 bits, ms byte + *op++ = (uint8_t)(hyper_sample >> (64 - 24 + 8)); // 24 bits, middle byte + *op = (uint8_t)(hyper_sample >> (64 - 24)); // 24 bits, ls byte + sample_length = 4; break; case SPS_FORMAT_S16_LE: - hyper_sample >>= (64 - 16); - byt = (uint8_t)hyper_sample; - *op++ = byt; - byt = (uint8_t)(hyper_sample >> 8); - *op++ = byt; - result = 2; + *op++ = (uint8_t)(hyper_sample >> (64 - 16)); + *op++ = (uint8_t)(hyper_sample >> (64 - 16 + 8)); // 16 bits, ms byte + sample_length = 2; break; case SPS_FORMAT_S16_BE: - hyper_sample >>= (64 - 16); - byt = (uint8_t)(hyper_sample >> 8); - *op++ = byt; - byt = (uint8_t)hyper_sample; - *op++ = byt; - result = 2; + *op++ = (uint8_t)(hyper_sample >> (64 - 16 + 8)); // 16 bits, ms byte + *op = (uint8_t)(hyper_sample >> (64 - 16)); + sample_length = 2; break; case SPS_FORMAT_S16: - hyper_sample >>= (64 - 16); - *(int16_t *)op = (int16_t)hyper_sample; - result = 2; + *(int16_t *)op = (int16_t)(hyper_sample >> (64 - 16)); + sample_length = 2; break; case SPS_FORMAT_S8: - hyper_sample >>= (int8_t)(64 - 8); - *op = hyper_sample; - result = 1; + *op = (int8_t)(hyper_sample >> (64 - 8)); + sample_length = 1; break; case SPS_FORMAT_U8: - hyper_sample >>= (uint8_t)(64 - 8); - hyper_sample += 128; - *op = hyper_sample; - result = 1; + *op = 128 + (uint8_t)(hyper_sample >> (64 - 8)); + sample_length = 1; break; default: - result = 0; // stop a compiler warning + sample_length = 0; // stop a compiler warning die("Unexpected SPS_FORMAT_* with index %d while outputting silence", format); } - p += result; + p += sample_length; previous_random_number = r; } - // hack - // memset(outp,0,number_of_frames * 4); return previous_random_number; } diff --git a/common.h b/common.h index 2a960d41..e9ec58c5 100644 --- a/common.h +++ b/common.h @@ -201,8 +201,9 @@ typedef struct { double audio_backend_buffer_interpolation_threshold_in_seconds; // below this, soxr interpolation // will not occur -- it'll be // basic interpolation instead. - double audio_backend_silence_threshold; // below this, silence will be added to the output buffer - double audio_backend_silence_scan_interval; // check the threshold this often + double disable_standby_mode_silence_threshold; // below this, silence will be added to the output + // buffer + double disable_standby_mode_silence_scan_interval; // check the threshold this often double audio_backend_latency_offset; // this will be the offset in seconds to compensate for any // fixed latency there might be in the audio path diff --git a/scripts/shairport-sync.conf b/scripts/shairport-sync.conf index 59e1c339..5e6089aa 100644 --- a/scripts/shairport-sync.conf +++ b/scripts/shairport-sync.conf @@ -93,6 +93,8 @@ alsa = // maximum_stall_time = 0.200; // Use this optional advanced setting to control how long to wait for data to be consumed by the output device before considering it an error. It should never approach 200 ms. // use_precision_timing = "auto"; // Use this optional advanced setting to control how Shairport Sync gathers timing information. When set to "auto", if the output device is a real hardware device, precision timing will be used. Choose "no" for more compatible standard timing, choose "yes" to force the use of precision timing, which may cause problems. // disable_standby_mode = "never"; // This setting prevents the DAC from entering the standby mode. Some DACs make small "popping" noises when they go in and out of standby mode. Settings can be: "always", "auto" or "never". Default is "never", but only for backwards compatibility. The "auto" setting prevents entry to standby mode while Shairport Sync is in the "active" mode. You can use "yes" instead of "always" and "no" instead of "never". +// disable_standby_mode_silence_threshold = 0.040; // Use this optional advanced setting to control how little audio should remain in the output buffer before the disable_standby code should start sending silence to the output device. +// disable_standby_mode_silence_scan_interval = 0.004; // Use this optional advanced setting to control how often the amount of audio remaining in the output buffer should be checked. }; // Parameters for the "sndio" audio back end. All are optional. From 7d6b52edd4ef2d75694a7a7855e3fcdc8bec1eaa Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Thu, 20 Jun 2019 10:53:57 +0100 Subject: [PATCH 27/33] Expose some of the settings used in the disable_standby_mode and tidy up some of the silence-generating code. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index ba439484..09540012 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.50]) -AC_INIT([shairport-sync], [3.3.2d1], [mikebrady@eircom.net]) +AC_INIT([shairport-sync], [3.3.2d2], [mikebrady@eircom.net]) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([shairport.c]) AC_CONFIG_HEADERS([config.h]) From d78528a59ef9b134b87af61be5e0bf69316e1cfd Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Fri, 5 Jul 2019 09:20:43 +0100 Subject: [PATCH 28/33] Update README.md --- README.md | 97 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 66 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index ec6bb82b..804ffbc4 100644 --- a/README.md +++ b/README.md @@ -535,38 +535,62 @@ Some Statistics --------------- If you turn on the `general` `statistics` setting, a heading like this will be output to the console or log file: ``` -sync error in milliseconds, net correction in ppm, corrections in ppm, total packets, missing packets, late packets, too late packets, resend requests, min DAC queue size, min buffer occupancy, max buffer occupancy +sync error in milliseconds, net correction in ppm, corrections in ppm, total packets, missing packets, late packets, too late packets, resend requests, min DAC queue size, min buffer occupancy, max buffer occupancy, source nominal frames per second, source actual frames per second, output frames per second, source clock drift in ppm, source clock drift sample count, rough calculated correction in ppm ``` This will be followed by the statistics themselves at regular intervals, for example: ``` - -0.7, 0.0, 0.0, 1003, 0, 0, 0, 0, 6085, 222, 230 - -0.8, 0.0, 0.0, 2006, 0, 0, 0, 0, 6087, 222, 230 - -1.1, 0.0, 0.0, 3009, 0, 0, 0, 0, 6078, 223, 230 - -1.1, 0.0, 0.0, 4012, 0, 0, 0, 0, 6078, 222, 230 - -1.1, 0.0, 0.0, 5015, 0, 0, 0, 0, 6071, 223, 230 - -1.1, 0.0, 0.0, 6018, 0, 0, 0, 0, 6081, 223, 230 - -1.0, 0.0, 0.0, 7021, 0, 0, 0, 0, 6078, 223, 230 - -0.8, 0.0, 0.0, 8024, 0, 0, 0, 0, 6085, 223, 230 - -0.3, 0.0, 0.0, 9027, 0, 0, 0, 0, 6111, 224, 230 - -0.4, 0.0, 0.0, 10030, 0, 0, 0, 0, 6104, 223, 230 - -0.8, 0.0, 0.0, 11033, 0, 0, 0, 0, 6096, 223, 230 - -0.5, 0.0, 0.0, 12036, 0, 0, 0, 0, 6094, 224, 230 - -0.7, 0.0, 0.0, 13039, 0, 0, 0, 0, 6095, 222, 230 - -0.7, 0.0, 0.0, 14042, 0, 0, 0, 0, 6094, 222, 230 - -0.2, 0.0, 0.0, 15045, 0, 0, 0, 0, 6111, 223, 230 - -0.6, 0.0, 0.0, 16048, 0, 2, 0, 2, 6090, 210, 230 - -0.6, 0.0, 0.0, 17051, 0, 2, 0, 2, 6097, 220, 230 - -0.5, 0.0, 0.0, 18054, 0, 2, 0, 2, 6097, 224, 230 - -0.4, 0.0, 0.0, 19057, 0, 2, 0, 2, 6098, 222, 230 - -0.4, 0.0, 0.0, 20060, 0, 2, 0, 2, 6107, 223, 230 - -0.4, 0.0, 0.0, 21063, 0, 2, 0, 2, 6111, 222, 230 - -0.4, 0.0, 0.0, 22066, 0, 3, 0, 3, 6108, 210, 230 - -0.3, 0.0, 0.0, 23069, 0, 4, 0, 4, 6106, 214, 245 - -0.4, 0.0, 0.0, 24072, 0, 4, 0, 4, 6104, 232, 245 - -0.3, 0.0, 0.0, 25075, 0, 4, 0, 4, 6069, 232, 246 - -0.3, 0.0, 0.0, 26078, 0, 4, 0, 4, 6110, 232, 245 - -0.3, 0.0, 0.0, 27081, 0, 4, 0, 4, 6088, 231, 245 - -0.2, 0.0, 0.0, 28084, 0, 4, 0, 4, 6090, 232, 245 + -1.30, 22.9, 22.9, 1003, 0, 0, 0, 0, 7118, 211, 227, 44100.00, 43991.81, 44099.85, 0.00, 0, -3.38 + -1.87, 5.7, 5.7, 2006, 0, 0, 0, 0, 7824, 209, 226, 44100.02, 44020.00, 44099.92, 0.00, 0, -2.27 + -1.47, 0.0, 0.0, 3009, 0, 0, 0, 0, 8505, 218, 226, 44100.03, 44097.32, 44099.97, 0.00, 0, -1.26 + -0.85, 0.0, 0.0, 4012, 0, 0, 0, 0, 8525, 209, 226, 44100.02, 44098.24, 44099.95, 0.00, 0, -1.56 + -0.16, 0.0, 0.0, 5015, 0, 0, 0, 0, 8477, 209, 227, 44100.00, 44098.32, 44099.89, 0.00, 0, -2.39 + -0.43, 0.0, 0.0, 6018, 0, 0, 0, 0, 8533, 214, 226, 44100.00, 44091.52, 44099.90, 0.00, 0, -2.18 + -0.65, 0.0, 0.0, 7021, 0, 0, 0, 0, 8537, 220, 226, 44100.00, 44097.46, 44099.92, 0.00, 0, -1.81 + -0.32, 0.0, 0.0, 8024, 0, 0, 0, 0, 8545, 219, 226, 44100.01, 44089.73, 44099.93, 0.00, 0, -1.87 + -0.16, 0.0, 0.0, 9027, 0, 0, 0, 0, 8561, 220, 226, 44100.01, 44084.12, 44099.92, 0.00, 0, -2.04 + 0.02, 0.0, 0.0, 10030, 0, 0, 0, 0, 8569, 218, 226, 44100.00, 44091.64, 44099.96, 0.00, 0, -0.93 + 0.40, 0.0, 0.0, 11033, 0, 0, 0, 0, 8581, 219, 226, 44100.00, 44094.52, 44099.92, 26.46, 10, -28.22 + 0.58, 0.0, 0.0, 12036, 0, 0, 0, 0, 8589, 218, 226, 44100.00, 44093.36, 44099.92, 24.04, 12, -25.98 + 0.82, 0.0, 0.0, 13039, 0, 0, 0, 0, 8609, 217, 226, 44100.01, 44095.28, 44099.94, 25.55, 14, -26.97 + 1.05, 0.0, 0.0, 14042, 0, 0, 0, 0, 8605, 207, 226, 44100.00, 44096.62, 44099.94, 26.10, 16, -27.61 + 1.31, 0.0, 0.0, 15045, 0, 0, 0, 0, 8633, 209, 226, 44100.00, 44096.14, 44099.92, 26.56, 17, -28.49 + 1.55, 2.8, 2.8, 16048, 0, 0, 0, 0, 7650, 210, 226, 44100.01, 44099.75, 44099.93, 26.84, 18, -28.54 + 1.77, 0.0, 0.0, 17051, 0, 0, 0, 0, 8642, 218, 226, 44100.00, 44099.94, 44099.95, 27.26, 20, -28.52 + 1.99, -19.8, 19.8, 18054, 0, 0, 0, 0, 8648, 218, 226, 44100.00, 44097.73, 44099.94, 27.20, 23, -28.52 + 2.00, -22.7, 22.7, 19057, 0, 0, 0, 0, 8586, 218, 226, 44100.00, 44100.09, 44099.95, 27.30, 25, -28.60 + 2.03, -56.6, 56.6, 20060, 0, 0, 0, 0, 8586, 219, 226, 44100.00, 44101.32, 44099.94, 27.35, 28, -28.87 + 1.97, -14.2, 14.2, 21063, 0, 0, 0, 0, 8556, 219, 226, 44100.00, 44100.30, 44099.93, 27.58, 30, -29.22 + 1.99, -11.3, 11.3, 22066, 0, 0, 0, 0, 8586, 217, 226, 44100.00, 44099.21, 44099.93, 27.52, 32, -29.19 + 1.99, -22.7, 22.7, 23069, 0, 0, 0, 0, 8587, 217, 226, 44100.00, 44100.39, 44099.94, 27.08, 34, -28.52 + 2.03, -45.3, 45.3, 24072, 0, 0, 0, 0, 8534, 220, 226, 44100.00, 44096.84, 44099.93, 27.37, 36, -29.03 + 2.05, -76.5, 76.5, 25075, 0, 0, 0, 0, 8579, 219, 227, 44100.00, 44098.66, 44099.94, 28.29, 37, -29.59 + 1.96, -19.8, 19.8, 26078, 0, 0, 0, 0, 8596, 219, 226, 44100.00, 44099.57, 44099.92, 28.80, 38, -30.54 + 1.94, -25.5, 25.5, 27081, 0, 0, 0, 0, 8607, 218, 227, 44100.00, 44098.79, 44099.93, 29.54, 41, -31.07 + 1.97, -17.0, 17.0, 28084, 0, 0, 0, 0, 8598, 219, 227, 44100.00, 44099.64, 44099.94, 29.54, 41, -30.94 + 1.86, 0.0, 0.0, 29087, 0, 0, 0, 0, 8629, 217, 226, 44100.00, 44100.37, 44099.93, 29.51, 42, -31.25 + 2.01, -53.8, 53.8, 30090, 0, 0, 0, 0, 8612, 220, 227, 44100.00, 44099.61, 44099.93, 29.54, 44, -31.04 + 1.93, -5.7, 5.7, 31093, 0, 0, 0, 0, 8642, 218, 226, 44100.00, 44100.83, 44099.93, 29.26, 45, -30.80 + 2.00, -34.0, 34.0, 32096, 0, 0, 0, 0, 8584, 218, 226, 44100.00, 44101.57, 44099.94, 28.96, 47, -30.35 + 2.01, -28.3, 28.3, 33099, 0, 0, 0, 0, 8631, 218, 227, 44100.00, 44100.89, 44099.94, 28.67, 49, -30.13 + 1.98, -28.3, 28.3, 34102, 0, 0, 0, 0, 8609, 219, 227, 44100.00, 44101.54, 44099.93, 28.60, 51, -30.32 + 2.01, -68.0, 68.0, 35105, 0, 0, 0, 0, 8592, 218, 226, 44100.00, 44098.76, 44099.93, 28.51, 52, -30.06 + 1.88, -5.7, 5.7, 36108, 0, 0, 0, 0, 8643, 215, 226, 44100.00, 44099.07, 44099.93, 28.41, 54, -29.95 + 1.95, -14.2, 14.2, 37111, 0, 0, 0, 0, 8521, 205, 226, 44100.00, 44097.64, 44099.93, 28.29, 55, -29.96 + 2.01, -34.0, 34.0, 38114, 0, 0, 0, 0, 8553, 219, 241, 44100.00, 44114.10, 44099.94, 28.15, 56, -29.64 + 1.97, -147.3, 147.3, 39117, 0, 0, 0, 0, 8564, 228, 241, 44100.00, 44117.06, 44099.92, 28.55, 58, -30.39 + 1.07, 0.0, 0.0, 40120, 0, 0, 0, 0, 8617, 229, 242, 44100.00, 44116.11, 44099.93, 28.44, 59, -29.95 + 1.35, 0.0, 0.0, 41123, 0, 0, 0, 0, 8607, 229, 241, 44100.00, 44111.73, 44099.93, 28.30, 61, -30.01 + 1.53, 0.0, 0.0, 42126, 0, 0, 0, 0, 8645, 227, 241, 44100.00, 44113.60, 44099.94, 28.08, 62, -29.56 + 1.82, 0.0, 0.0, 43129, 0, 0, 0, 0, 8649, 229, 241, 44100.00, 44112.52, 44099.93, 28.03, 64, -29.55 + 2.02, -34.0, 34.0, 44132, 0, 0, 0, 0, 8619, 228, 241, 44100.00, 44115.39, 44099.93, 28.00, 66, -29.57 + 2.02, -51.0, 51.0, 45135, 0, 0, 0, 0, 8354, 220, 241, 44100.00, 44112.59, 44099.93, 27.94, 68, -29.52 + 1.91, -25.5, 25.5, 46138, 0, 0, 0, 0, 8460, 215, 241, 44100.00, 44112.81, 44099.93, 27.89, 69, -29.44 + 1.90, -14.2, 14.2, 47141, 0, 0, 0, 0, 8661, 229, 241, 44100.00, 44113.62, 44099.93, 27.86, 72, -29.50 + 1.99, -79.3, 79.3, 48144, 0, 0, 0, 0, 8416, 228, 241, 44100.00, 44111.05, 44099.94, 27.86, 73, -29.33 + 1.57, -22.7, 22.7, 49147, 0, 0, 0, 0, 8585, 227, 242, 44100.00, 44113.42, 44099.93, 27.49, 74, -29.11 + 1.60, 0.0, 0.0, 50150, 0, 0, 0, 0, 8549, 229, 241, 44100.00, 44112.52, 44099.93, 27.44, 77, -29.02 + 1.88, -19.8, 19.8, 51153, 0, 0, 0, 0, 8641, 204, 242, 44100.00, 44100.43, 44099.94, 27.49, 79, -28.97 +Playback Stopped. Total playing time 00:07:01. Input: 44100.43, output: 44099.94 frames per second. ``` "Sync error in milliseconds" is the average deviation from exact synchronisation. The first line of the example above indicates that the output is on average 0.7 milliseconds behind exact synchronisation. Sync is allowed to drift by the `general` `drift_tolerance_in_seconds` setting — (± 0.002 seconds) by default — before a correction will be made. @@ -575,11 +599,22 @@ This will be followed by the statistics themselves at regular intervals, for exa "Corrections in ppm" is the number of frame insertions plus the number of frame deletions (i.e. the total number of corrections), given as a moving average in parts per million. The closer this is to the net corrections, the fewer "unnecessary" corrections that are being made. Third party programs tend to have much larger levels of corrections. -For reference, a drift of one second per day is approximately 11.57 ppm. Left uncorrected, even a drift this small between two audio outputs will be audible after a short time. The above sample is from an iPhone 6 to Shairport Sync running on a WiFi-connected Raspberry Pi 3 using a cheap no-name USB DAC. +"Min DAC queue size" is the minimum size the queue of samples in the output device's hardware buffer was measured at. It is meant to stand at 0.2 seconds = 8,820 frames at 44,100 frames per second, and will go low if the processor is very busy. If it goes below about 2,000 then it's an indication that the processor can't really keep up. + +"Source nominal frames per second" is the rate at which audio is being sent to Shairport Sync according to information supplied by the source itself. + +"Source actual frames per second" is the rate at which frames of audio are actually received by Shairport Sync. This can vary a great deal due to network conditions, but over a long time (more than 30 minutes) it should settle down to an accurate value. It does not account for retransmitted packets. + +"Output frames per second" is the actual rate at which frames of audio are taken by the output device. On a system with a well-conditioned `ntp`-based clock (and without output underruns) this figure should be very accurate after playing material continuously for a period. + +"Source clock drift in ppm" is an estimate of the difference in timekeeping between the audio source and the Shairport Sync devive. It is calculated from a linear regression of drift sample data. The number of samples the estimate is based on is given in the next column, "Source clock drift sample count". + +"Rough calculated correction in ppm" is a very crude estimate of the amount of interpolation that needs to applied, on average, to keep sync. It is not really to be relied on at this time. + +For reference, a drift of one second per day is approximately 11.57 ppm. Left uncorrected, even a drift this small between two audio outputs will be audible after a short time. The above sample is from an iPhone XS Max to Shairport Sync running on a WiFi-connected Raspberry Pi 3 using an [IQaudIO Pi-DigiAMP+](http://iqaudio.co.uk/hats/9-pi-digiamp.html). It's not unusual to have resend requests, late packets and even missing packets if some part of the connection to the Shairport Sync device is over WiFi. Late packets can sometimes be asked for and received multiple times. Sometimes late packets are sent and arrive too late, but have already been sent and received in time, so weren't needed anyway... -"Min DAC queue size" is the minimum size the queue of samples in the output device's hardware buffer was measured at. It is meant to stand at 0.15 seconds = 6,615 frames at 44,100 frames per second, and will go low if the processor is very busy. If it goes below about 2,000 then it's an indication that the processor can't really keep up. WiFi Issues --------- From 1dd0d828363ccc0ce41e61c36cce1c0bd6b7054a Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Fri, 5 Jul 2019 10:09:50 +0100 Subject: [PATCH 29/33] Update README.md --- README.md | 106 +++++++++++++++++++++++++++--------------------------- 1 file changed, 54 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index 804ffbc4..5a7acbe7 100644 --- a/README.md +++ b/README.md @@ -539,58 +539,60 @@ sync error in milliseconds, net correction in ppm, corrections in ppm, total pac ``` This will be followed by the statistics themselves at regular intervals, for example: ``` - -1.30, 22.9, 22.9, 1003, 0, 0, 0, 0, 7118, 211, 227, 44100.00, 43991.81, 44099.85, 0.00, 0, -3.38 - -1.87, 5.7, 5.7, 2006, 0, 0, 0, 0, 7824, 209, 226, 44100.02, 44020.00, 44099.92, 0.00, 0, -2.27 - -1.47, 0.0, 0.0, 3009, 0, 0, 0, 0, 8505, 218, 226, 44100.03, 44097.32, 44099.97, 0.00, 0, -1.26 - -0.85, 0.0, 0.0, 4012, 0, 0, 0, 0, 8525, 209, 226, 44100.02, 44098.24, 44099.95, 0.00, 0, -1.56 - -0.16, 0.0, 0.0, 5015, 0, 0, 0, 0, 8477, 209, 227, 44100.00, 44098.32, 44099.89, 0.00, 0, -2.39 - -0.43, 0.0, 0.0, 6018, 0, 0, 0, 0, 8533, 214, 226, 44100.00, 44091.52, 44099.90, 0.00, 0, -2.18 - -0.65, 0.0, 0.0, 7021, 0, 0, 0, 0, 8537, 220, 226, 44100.00, 44097.46, 44099.92, 0.00, 0, -1.81 - -0.32, 0.0, 0.0, 8024, 0, 0, 0, 0, 8545, 219, 226, 44100.01, 44089.73, 44099.93, 0.00, 0, -1.87 - -0.16, 0.0, 0.0, 9027, 0, 0, 0, 0, 8561, 220, 226, 44100.01, 44084.12, 44099.92, 0.00, 0, -2.04 - 0.02, 0.0, 0.0, 10030, 0, 0, 0, 0, 8569, 218, 226, 44100.00, 44091.64, 44099.96, 0.00, 0, -0.93 - 0.40, 0.0, 0.0, 11033, 0, 0, 0, 0, 8581, 219, 226, 44100.00, 44094.52, 44099.92, 26.46, 10, -28.22 - 0.58, 0.0, 0.0, 12036, 0, 0, 0, 0, 8589, 218, 226, 44100.00, 44093.36, 44099.92, 24.04, 12, -25.98 - 0.82, 0.0, 0.0, 13039, 0, 0, 0, 0, 8609, 217, 226, 44100.01, 44095.28, 44099.94, 25.55, 14, -26.97 - 1.05, 0.0, 0.0, 14042, 0, 0, 0, 0, 8605, 207, 226, 44100.00, 44096.62, 44099.94, 26.10, 16, -27.61 - 1.31, 0.0, 0.0, 15045, 0, 0, 0, 0, 8633, 209, 226, 44100.00, 44096.14, 44099.92, 26.56, 17, -28.49 - 1.55, 2.8, 2.8, 16048, 0, 0, 0, 0, 7650, 210, 226, 44100.01, 44099.75, 44099.93, 26.84, 18, -28.54 - 1.77, 0.0, 0.0, 17051, 0, 0, 0, 0, 8642, 218, 226, 44100.00, 44099.94, 44099.95, 27.26, 20, -28.52 - 1.99, -19.8, 19.8, 18054, 0, 0, 0, 0, 8648, 218, 226, 44100.00, 44097.73, 44099.94, 27.20, 23, -28.52 - 2.00, -22.7, 22.7, 19057, 0, 0, 0, 0, 8586, 218, 226, 44100.00, 44100.09, 44099.95, 27.30, 25, -28.60 - 2.03, -56.6, 56.6, 20060, 0, 0, 0, 0, 8586, 219, 226, 44100.00, 44101.32, 44099.94, 27.35, 28, -28.87 - 1.97, -14.2, 14.2, 21063, 0, 0, 0, 0, 8556, 219, 226, 44100.00, 44100.30, 44099.93, 27.58, 30, -29.22 - 1.99, -11.3, 11.3, 22066, 0, 0, 0, 0, 8586, 217, 226, 44100.00, 44099.21, 44099.93, 27.52, 32, -29.19 - 1.99, -22.7, 22.7, 23069, 0, 0, 0, 0, 8587, 217, 226, 44100.00, 44100.39, 44099.94, 27.08, 34, -28.52 - 2.03, -45.3, 45.3, 24072, 0, 0, 0, 0, 8534, 220, 226, 44100.00, 44096.84, 44099.93, 27.37, 36, -29.03 - 2.05, -76.5, 76.5, 25075, 0, 0, 0, 0, 8579, 219, 227, 44100.00, 44098.66, 44099.94, 28.29, 37, -29.59 - 1.96, -19.8, 19.8, 26078, 0, 0, 0, 0, 8596, 219, 226, 44100.00, 44099.57, 44099.92, 28.80, 38, -30.54 - 1.94, -25.5, 25.5, 27081, 0, 0, 0, 0, 8607, 218, 227, 44100.00, 44098.79, 44099.93, 29.54, 41, -31.07 - 1.97, -17.0, 17.0, 28084, 0, 0, 0, 0, 8598, 219, 227, 44100.00, 44099.64, 44099.94, 29.54, 41, -30.94 - 1.86, 0.0, 0.0, 29087, 0, 0, 0, 0, 8629, 217, 226, 44100.00, 44100.37, 44099.93, 29.51, 42, -31.25 - 2.01, -53.8, 53.8, 30090, 0, 0, 0, 0, 8612, 220, 227, 44100.00, 44099.61, 44099.93, 29.54, 44, -31.04 - 1.93, -5.7, 5.7, 31093, 0, 0, 0, 0, 8642, 218, 226, 44100.00, 44100.83, 44099.93, 29.26, 45, -30.80 - 2.00, -34.0, 34.0, 32096, 0, 0, 0, 0, 8584, 218, 226, 44100.00, 44101.57, 44099.94, 28.96, 47, -30.35 - 2.01, -28.3, 28.3, 33099, 0, 0, 0, 0, 8631, 218, 227, 44100.00, 44100.89, 44099.94, 28.67, 49, -30.13 - 1.98, -28.3, 28.3, 34102, 0, 0, 0, 0, 8609, 219, 227, 44100.00, 44101.54, 44099.93, 28.60, 51, -30.32 - 2.01, -68.0, 68.0, 35105, 0, 0, 0, 0, 8592, 218, 226, 44100.00, 44098.76, 44099.93, 28.51, 52, -30.06 - 1.88, -5.7, 5.7, 36108, 0, 0, 0, 0, 8643, 215, 226, 44100.00, 44099.07, 44099.93, 28.41, 54, -29.95 - 1.95, -14.2, 14.2, 37111, 0, 0, 0, 0, 8521, 205, 226, 44100.00, 44097.64, 44099.93, 28.29, 55, -29.96 - 2.01, -34.0, 34.0, 38114, 0, 0, 0, 0, 8553, 219, 241, 44100.00, 44114.10, 44099.94, 28.15, 56, -29.64 - 1.97, -147.3, 147.3, 39117, 0, 0, 0, 0, 8564, 228, 241, 44100.00, 44117.06, 44099.92, 28.55, 58, -30.39 - 1.07, 0.0, 0.0, 40120, 0, 0, 0, 0, 8617, 229, 242, 44100.00, 44116.11, 44099.93, 28.44, 59, -29.95 - 1.35, 0.0, 0.0, 41123, 0, 0, 0, 0, 8607, 229, 241, 44100.00, 44111.73, 44099.93, 28.30, 61, -30.01 - 1.53, 0.0, 0.0, 42126, 0, 0, 0, 0, 8645, 227, 241, 44100.00, 44113.60, 44099.94, 28.08, 62, -29.56 - 1.82, 0.0, 0.0, 43129, 0, 0, 0, 0, 8649, 229, 241, 44100.00, 44112.52, 44099.93, 28.03, 64, -29.55 - 2.02, -34.0, 34.0, 44132, 0, 0, 0, 0, 8619, 228, 241, 44100.00, 44115.39, 44099.93, 28.00, 66, -29.57 - 2.02, -51.0, 51.0, 45135, 0, 0, 0, 0, 8354, 220, 241, 44100.00, 44112.59, 44099.93, 27.94, 68, -29.52 - 1.91, -25.5, 25.5, 46138, 0, 0, 0, 0, 8460, 215, 241, 44100.00, 44112.81, 44099.93, 27.89, 69, -29.44 - 1.90, -14.2, 14.2, 47141, 0, 0, 0, 0, 8661, 229, 241, 44100.00, 44113.62, 44099.93, 27.86, 72, -29.50 - 1.99, -79.3, 79.3, 48144, 0, 0, 0, 0, 8416, 228, 241, 44100.00, 44111.05, 44099.94, 27.86, 73, -29.33 - 1.57, -22.7, 22.7, 49147, 0, 0, 0, 0, 8585, 227, 242, 44100.00, 44113.42, 44099.93, 27.49, 74, -29.11 - 1.60, 0.0, 0.0, 50150, 0, 0, 0, 0, 8549, 229, 241, 44100.00, 44112.52, 44099.93, 27.44, 77, -29.02 - 1.88, -19.8, 19.8, 51153, 0, 0, 0, 0, 8641, 204, 242, 44100.00, 44100.43, 44099.94, 27.49, 79, -28.97 -Playback Stopped. Total playing time 00:07:01. Input: 44100.43, output: 44099.94 frames per second. + 0.15, 0.0, 0.0, 1003, 0, 0, 0, 0, 8539, 208, 225, 44099.95, 44274.21, 44099.68, 0.00, 0, -6.12 + 0.34, 0.0, 0.0, 2006, 0, 0, 0, 0, 8591, 206, 225, 44099.99, 44169.96, 44099.76, 0.00, 0, -5.27 + 0.89, 0.0, 0.0, 3009, 0, 0, 0, 0, 8571, 218, 225, 44100.01, 44152.81, 44099.85, 0.00, 0, -3.66 + 0.85, 0.0, 0.0, 4012, 0, 0, 0, 0, 8619, 218, 225, 44100.00, 44142.80, 44099.80, 0.00, 0, -4.62 + 1.29, 0.0, 0.0, 5015, 0, 0, 0, 0, 8623, 218, 225, 44100.00, 44129.39, 44099.83, 0.00, 0, -3.85 + 1.41, 0.0, 0.0, 6018, 0, 0, 0, 0, 8619, 216, 225, 44099.99, 44127.74, 44099.86, 0.00, 0, -2.76 + 1.53, 0.0, 0.0, 7021, 0, 0, 0, 0, 8603, 218, 225, 44100.00, 44109.60, 44099.81, 0.00, 0, -4.21 + 1.81, 0.0, 0.0, 8024, 0, 0, 0, 0, 8655, 215, 225, 44100.00, 44110.01, 44099.82, 0.00, 0, -4.16 + 2.00, -82.1, 82.1, 9027, 0, 0, 0, 0, 8481, 208, 225, 44100.00, 44110.69, 44099.83, 0.00, 0, -3.97 + 1.96, -8.5, 8.5, 10030, 0, 0, 0, 0, 8599, 209, 225, 44099.99, 44108.84, 44099.86, 0.00, 0, -3.02 + 1.94, 0.0, 0.0, 11033, 0, 0, 0, 0, 8583, 217, 225, 44100.00, 44109.33, 44099.87, 0.00, 0, -2.93 + 1.90, -5.7, 5.7, 12036, 0, 0, 0, 0, 8601, 219, 225, 44100.00, 44110.01, 44099.85, 20.47, 10, -23.88 + 2.02, -36.8, 36.8, 13039, 0, 0, 0, 0, 8488, 216, 225, 44100.00, 44107.18, 44099.85, 20.39, 12, -23.83 + 1.96, -17.0, 17.0, 14042, 0, 0, 0, 0, 8554, 218, 226, 44100.00, 44108.80, 44099.84, 20.63, 15, -24.13 + 1.99, -31.2, 31.2, 15045, 0, 0, 0, 0, 8592, 216, 225, 44100.00, 44106.85, 44099.85, 20.95, 18, -24.39 + 1.98, -28.3, 28.3, 16048, 0, 0, 0, 0, 8627, 219, 225, 44099.99, 44109.01, 44099.88, 21.23, 20, -23.90 + 2.01, -31.2, 31.2, 17051, 0, 0, 0, 0, 8582, 217, 225, 44100.00, 44108.86, 44099.84, 21.57, 22, -25.10 + 1.96, -19.8, 19.8, 18054, 0, 0, 0, 0, 8556, 215, 226, 44100.00, 44103.79, 44099.86, 21.59, 24, -24.85 + 1.98, -25.5, 25.5, 19057, 0, 0, 0, 0, 8563, 219, 225, 44100.00, 44102.62, 44099.86, 22.26, 25, -25.47 + 2.00, -28.3, 28.3, 20060, 0, 0, 0, 0, 8569, 214, 225, 44100.00, 44103.22, 44099.88, 22.57, 27, -25.41 + 1.98, -42.5, 42.5, 21063, 0, 0, 0, 0, 8570, 216, 225, 44100.00, 44104.14, 44099.85, 22.74, 28, -26.08 + 1.89, -31.2, 31.2, 22066, 0, 0, 0, 0, 8524, 218, 225, 44100.00, 44104.66, 44099.87, 22.89, 30, -25.89 + 1.96, 0.0, 0.0, 23069, 0, 0, 0, 0, 8650, 218, 225, 44100.00, 44105.36, 44099.88, 22.92, 32, -25.73 + 1.98, -34.0, 34.0, 24072, 0, 0, 0, 0, 8475, 219, 225, 44100.00, 44106.04, 44099.88, 22.90, 34, -25.70 + 2.07, -138.8, 138.8, 25075, 0, 0, 0, 0, 8490, 218, 226, 44100.00, 44104.93, 44099.87, 23.87, 35, -26.92 + 1.33, 0.0, 0.0, 26078, 0, 0, 0, 0, 8617, 216, 225, 44100.00, 44104.66, 44099.88, 23.68, 38, -26.43 + 1.67, -22.7, 22.7, 27081, 0, 0, 0, 0, 8562, 217, 225, 44100.00, 44106.16, 44099.87, 25.06, 40, -27.93 + 1.98, -28.3, 28.3, 28084, 0, 0, 0, 0, 8544, 218, 225, 44100.00, 44105.21, 44099.87, 25.91, 41, -28.84 + 1.99, -22.7, 22.7, 29087, 0, 0, 0, 0, 8569, 219, 225, 44100.00, 44102.30, 44099.87, 25.91, 41, -28.74 + 1.66, -56.6, 56.6, 30090, 0, 0, 0, 0, 8554, 218, 225, 44100.00, 44103.07, 44099.86, 26.38, 44, -29.46 + 1.44, 0.0, 0.0, 31093, 0, 0, 0, 0, 8642, 218, 225, 44100.00, 44102.18, 44099.87, 26.08, 46, -28.98 + 1.56, 0.0, 0.0, 32096, 0, 0, 0, 0, 8647, 218, 225, 44100.00, 44102.76, 44099.87, 25.79, 48, -28.64 + 1.68, 0.0, 0.0, 33099, 0, 0, 0, 0, 8647, 217, 225, 44100.00, 44103.41, 44099.88, 25.66, 49, -28.51 + 1.92, -14.2, 14.2, 34102, 0, 0, 0, 0, 8639, 217, 225, 44100.00, 44102.38, 44099.87, 25.45, 51, -28.40 + 2.00, -45.3, 45.3, 35105, 0, 0, 0, 0, 8593, 216, 225, 44100.00, 44103.23, 44099.86, 25.35, 53, -28.61 + 1.98, -17.0, 17.0, 36108, 0, 0, 0, 0, 8609, 218, 225, 44100.00, 44102.54, 44099.87, 25.21, 55, -28.12 + 1.99, -31.2, 31.2, 37111, 0, 0, 0, 0, 8575, 217, 225, 44100.00, 44101.98, 44099.88, 25.11, 57, -27.88 + 1.99, -14.2, 14.2, 38114, 0, 0, 0, 0, 8624, 218, 225, 44100.00, 44104.44, 44099.87, 24.95, 60, -27.97 + 1.99, -28.3, 28.3, 39117, 0, 0, 0, 0, 8582, 209, 225, 44100.00, 44104.91, 44099.86, 24.88, 62, -27.98 + 1.86, -17.0, 17.0, 40120, 0, 0, 0, 0, 8504, 207, 225, 44100.00, 44104.52, 44099.88, 24.66, 63, -27.39 + 1.96, -56.6, 56.6, 41123, 0, 0, 0, 0, 8517, 212, 226, 44100.00, 44102.29, 44099.88, 24.54, 66, -27.34 + 1.90, -36.8, 36.8, 42126, 0, 0, 0, 0, 8363, 218, 240, 44100.00, 44116.79, 44099.87, 24.55, 68, -27.48 + 1.89, 0.0, 0.0, 43129, 0, 0, 0, 0, 8487, 227, 241, 44100.00, 44117.57, 44099.88, 24.56, 69, -27.32 + 1.99, -22.7, 22.7, 44132, 0, 0, 0, 0, 8576, 227, 240, 44100.00, 44118.53, 44099.87, 24.57, 70, -27.44 + 1.98, -39.7, 39.7, 45135, 0, 0, 0, 0, 8593, 228, 240, 44100.00, 44113.88, 44099.87, 24.60, 73, -27.43 + 2.00, -34.0, 34.0, 46138, 0, 0, 0, 0, 8577, 227, 240, 44100.00, 44117.10, 44099.88, 24.65, 75, -27.46 + 1.98, -22.7, 22.7, 47141, 0, 0, 0, 0, 8591, 204, 240, 44100.00, 44102.78, 44099.87, 24.67, 77, -27.59 + 1.98, -17.0, 17.0, 48144, 0, 0, 0, 0, 8587, 216, 225, 44100.00, 44102.38, 44099.88, 24.67, 79, -27.42 + 1.99, -28.3, 28.3, 49147, 0, 0, 0, 0, 8590, 214, 225, 44100.00, 44102.70, 44099.87, 24.66, 80, -27.57 + 1.85, -62.3, 62.3, 50150, 0, 0, 0, 0, 8518, 217, 226, 44100.00, 44103.16, 44099.87, 24.70, 83, -27.55 + 1.90, -28.3, 28.3, 51153, 0, 0, 0, 0, 8617, 216, 225, 44100.00, 44102.60, 44099.88, 24.66, 85, -27.42 + 1.89, -5.7, 5.7, 52156, 0, 0, 0, 0, 8615, 218, 225, 44100.00, 44103.37, 44099.88, 24.65, 86, -27.39 + 1.96, -19.8, 19.8, 53159, 0, 0, 0, 0, 8600, 219, 225, 44100.00, 44103.77, 44099.87, 24.62, 89, -27.53 + 1.99, -22.7, 22.7, 54162, 0, 0, 0, 0, 8567, 216, 225, 44100.00, 44103.12, 44099.87, 24.57, 92, -27.45 ``` "Sync error in milliseconds" is the average deviation from exact synchronisation. The first line of the example above indicates that the output is on average 0.7 milliseconds behind exact synchronisation. Sync is allowed to drift by the `general` `drift_tolerance_in_seconds` setting — (± 0.002 seconds) by default — before a correction will be made. From 175ae0a65031c78275d3bd2d024f9140ac1d0885 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Sun, 21 Jul 2019 09:06:37 +0200 Subject: [PATCH 30/33] Fix a bug that sometimes caused a crash when a service name was specified in the configuration file. The fix was to be more systematic in allocating and deallocating memory for termporary strings. Thanks to Chris Boot, Ari Sovijarvi and Jeroen Massar for the bug report. Fixes Debian Bug report #925577. Allow the rtsp_listen_loop to exit if the RTSP port (usually port 5000) is not available. This allows the system to exit cleanly rather than abort. --- rtsp.c | 222 ++++++++++++++++++++++++++-------------------------- shairport.c | 56 +++++++------ 2 files changed, 142 insertions(+), 136 deletions(-) diff --git a/rtsp.c b/rtsp.c index 20fc6b8b..4a72250a 100644 --- a/rtsp.c +++ b/rtsp.c @@ -2572,7 +2572,7 @@ void rtsp_listen_loop(void) { } else #endif family = "IPv4"; - debug(1, "Unable to listen on %s port %d. The error is: \"%s\".", family, config.port, + debug(1, "unable to listen on %s port %d. The error is: \"%s\".", family, config.port, strerror(errno)); continue; } @@ -2585,127 +2585,127 @@ void rtsp_listen_loop(void) { freeaddrinfo(info); - if (!nsock) - die("Could not establish a service on port %d -- program terminating. Is another instance of " - "Shairport Sync running on this device?", - config.port); - - int maxfd = -1; - fd_set fds; - FD_ZERO(&fds); - for (i = 0; i < nsock; i++) { - if (sockfd[i] > maxfd) - maxfd = sockfd[i]; - } - - mdns_register(); - - pthread_setcancelstate(oldState, NULL); - int acceptfd; - struct timeval tv; - pthread_cleanup_push(rtsp_listen_loop_cleanup_handler, (void *)sockfd); - do { - pthread_testcancel(); - tv.tv_sec = 60; - tv.tv_usec = 0; - - for (i = 0; i < nsock; i++) - FD_SET(sockfd[i], &fds); - - ret = select(maxfd + 1, &fds, 0, 0, &tv); - if (ret < 0) { - if (errno == EINTR) - continue; - break; + if (nsock) { + int maxfd = -1; + fd_set fds; + FD_ZERO(&fds); + for (i = 0; i < nsock; i++) { + if (sockfd[i] > maxfd) + maxfd = sockfd[i]; } - cleanup_threads(); + mdns_register(); - acceptfd = -1; - for (i = 0; i < nsock; i++) { - if (FD_ISSET(sockfd[i], &fds)) { - acceptfd = sockfd[i]; + pthread_setcancelstate(oldState, NULL); + int acceptfd; + struct timeval tv; + pthread_cleanup_push(rtsp_listen_loop_cleanup_handler, (void *)sockfd); + do { + pthread_testcancel(); + tv.tv_sec = 60; + tv.tv_usec = 0; + + for (i = 0; i < nsock; i++) + FD_SET(sockfd[i], &fds); + + ret = select(maxfd + 1, &fds, 0, 0, &tv); + if (ret < 0) { + if (errno == EINTR) + continue; break; } - } - if (acceptfd < 0) // timeout - continue; - rtsp_conn_info *conn = malloc(sizeof(rtsp_conn_info)); - if (conn == 0) - die("Couldn't allocate memory for an rtsp_conn_info record."); - memset(conn, 0, sizeof(rtsp_conn_info)); - conn->connection_number = RTSP_connection_index++; - socklen_t slen = sizeof(conn->remote); + cleanup_threads(); - conn->fd = accept(acceptfd, (struct sockaddr *)&conn->remote, &slen); - if (conn->fd < 0) { - debug(1, "Connection %d: New connection on port %d not accepted:", conn->connection_number, - config.port); - perror("failed to accept connection"); - free(conn); - } else { - SOCKADDR *local_info = (SOCKADDR *)&conn->local; - socklen_t size_of_reply = sizeof(*local_info); - memset(local_info, 0, sizeof(SOCKADDR)); - if (getsockname(conn->fd, (struct sockaddr *)local_info, &size_of_reply) == 0) { - - // IPv4: - if (local_info->SAFAMILY == AF_INET) { - char ip4[INET_ADDRSTRLEN]; // space to hold the IPv4 string - char remote_ip4[INET_ADDRSTRLEN]; // space to hold the IPv4 string - struct sockaddr_in *sa = (struct sockaddr_in *)local_info; - inet_ntop(AF_INET, &(sa->sin_addr), ip4, INET_ADDRSTRLEN); - unsigned short int tport = ntohs(sa->sin_port); - sa = (struct sockaddr_in *)&conn->remote; - inet_ntop(AF_INET, &(sa->sin_addr), remote_ip4, INET_ADDRSTRLEN); - unsigned short int rport = ntohs(sa->sin_port); - debug(2, "Connection %d: new connection from %s:%u to self at %s:%u.", - conn->connection_number, remote_ip4, rport, ip4, tport); + acceptfd = -1; + for (i = 0; i < nsock; i++) { + if (FD_ISSET(sockfd[i], &fds)) { + acceptfd = sockfd[i]; + break; } -#ifdef AF_INET6 - if (local_info->SAFAMILY == AF_INET6) { - // IPv6: + } + if (acceptfd < 0) // timeout + continue; - char ip6[INET6_ADDRSTRLEN]; // space to hold the IPv6 string - char remote_ip6[INET6_ADDRSTRLEN]; // space to hold the IPv6 string - struct sockaddr_in6 *sa6 = - (struct sockaddr_in6 *)local_info; // pretend this is loaded with something - inet_ntop(AF_INET6, &(sa6->sin6_addr), ip6, INET6_ADDRSTRLEN); - u_int16_t tport = ntohs(sa6->sin6_port); - - sa6 = (struct sockaddr_in6 *)&conn->remote; // pretend this is loaded with something - inet_ntop(AF_INET6, &(sa6->sin6_addr), remote_ip6, INET6_ADDRSTRLEN); - u_int16_t rport = ntohs(sa6->sin6_port); - debug(2, "Connection %d: new connection from [%s]:%u to self at [%s]:%u.", - conn->connection_number, remote_ip6, rport, ip6, tport); - } -#endif + rtsp_conn_info *conn = malloc(sizeof(rtsp_conn_info)); + if (conn == 0) + die("Couldn't allocate memory for an rtsp_conn_info record."); + memset(conn, 0, sizeof(rtsp_conn_info)); + conn->connection_number = RTSP_connection_index++; + socklen_t slen = sizeof(conn->remote); + conn->fd = accept(acceptfd, (struct sockaddr *)&conn->remote, &slen); + if (conn->fd < 0) { + debug(1, "Connection %d: New connection on port %d not accepted:", conn->connection_number, + config.port); + perror("failed to accept connection"); + free(conn); } else { - debug(1, "Error figuring out Shairport Sync's own IP number."); - } - // usleep(500000); - // pthread_t rtsp_conversation_thread; - // conn->thread = rtsp_conversation_thread; - // conn->stop = 0; // record's memory has been zeroed - // conn->authorized = 0; // record's memory has been zeroed - // fcntl(conn->fd, F_SETFL, O_NONBLOCK); + SOCKADDR *local_info = (SOCKADDR *)&conn->local; + socklen_t size_of_reply = sizeof(*local_info); + memset(local_info, 0, sizeof(SOCKADDR)); + if (getsockname(conn->fd, (struct sockaddr *)local_info, &size_of_reply) == 0) { - ret = pthread_create(&conn->thread, NULL, rtsp_conversation_thread_func, - conn); // also acts as a memory barrier - if (ret) { - char errorstring[1024]; - strerror_r(ret, (char *)errorstring, sizeof(errorstring)); - die("Connection %d: cannot create an RTSP conversation thread. Error %d: \"%s\".", - conn->connection_number, ret, (char *)errorstring); - } - debug(3, "Successfully created RTSP receiver thread %d.", conn->connection_number); - conn->running = 1; // this must happen before the thread is tracked - track_thread(conn); - } - } while (1); + // IPv4: + if (local_info->SAFAMILY == AF_INET) { + char ip4[INET_ADDRSTRLEN]; // space to hold the IPv4 string + char remote_ip4[INET_ADDRSTRLEN]; // space to hold the IPv4 string + struct sockaddr_in *sa = (struct sockaddr_in *)local_info; + inet_ntop(AF_INET, &(sa->sin_addr), ip4, INET_ADDRSTRLEN); + unsigned short int tport = ntohs(sa->sin_port); + sa = (struct sockaddr_in *)&conn->remote; + inet_ntop(AF_INET, &(sa->sin_addr), remote_ip4, INET_ADDRSTRLEN); + unsigned short int rport = ntohs(sa->sin_port); + debug(2, "Connection %d: new connection from %s:%u to self at %s:%u.", + conn->connection_number, remote_ip4, rport, ip4, tport); + } + #ifdef AF_INET6 + if (local_info->SAFAMILY == AF_INET6) { + // IPv6: - pthread_cleanup_pop(1); // should never happen - debug(1, "Oops -- fell out of the RTSP select loop"); + char ip6[INET6_ADDRSTRLEN]; // space to hold the IPv6 string + char remote_ip6[INET6_ADDRSTRLEN]; // space to hold the IPv6 string + struct sockaddr_in6 *sa6 = + (struct sockaddr_in6 *)local_info; // pretend this is loaded with something + inet_ntop(AF_INET6, &(sa6->sin6_addr), ip6, INET6_ADDRSTRLEN); + u_int16_t tport = ntohs(sa6->sin6_port); + + sa6 = (struct sockaddr_in6 *)&conn->remote; // pretend this is loaded with something + inet_ntop(AF_INET6, &(sa6->sin6_addr), remote_ip6, INET6_ADDRSTRLEN); + u_int16_t rport = ntohs(sa6->sin6_port); + debug(2, "Connection %d: new connection from [%s]:%u to self at [%s]:%u.", + conn->connection_number, remote_ip6, rport, ip6, tport); + } + #endif + + } else { + debug(1, "Error figuring out Shairport Sync's own IP number."); + } + // usleep(500000); + // pthread_t rtsp_conversation_thread; + // conn->thread = rtsp_conversation_thread; + // conn->stop = 0; // record's memory has been zeroed + // conn->authorized = 0; // record's memory has been zeroed + // fcntl(conn->fd, F_SETFL, O_NONBLOCK); + + ret = pthread_create(&conn->thread, NULL, rtsp_conversation_thread_func, + conn); // also acts as a memory barrier + if (ret) { + char errorstring[1024]; + strerror_r(ret, (char *)errorstring, sizeof(errorstring)); + die("Connection %d: cannot create an RTSP conversation thread. Error %d: \"%s\".", + conn->connection_number, ret, (char *)errorstring); + } + debug(3, "Successfully created RTSP receiver thread %d.", conn->connection_number); + conn->running = 1; // this must happen before the thread is tracked + track_thread(conn); + } + } while (1); + pthread_cleanup_pop(1); // should never happen + } else { + warn("could not establish a service on port %d -- program terminating. Is another instance of " + "Shairport Sync running on this device?", + config.port); + } + // debug(1, "Oops -- fell out of the RTSP select loop"); } diff --git a/shairport.c b/shairport.c index 499b4c0b..b301aecf 100644 --- a/shairport.c +++ b/shairport.c @@ -200,11 +200,17 @@ void *soxr_time_check(__attribute__((unused)) void *arg) { size_t odone; + //int oldState; + //pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable + 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. + //pthread_setcancelstate(oldState, NULL); + + pthread_testcancel(); io_spec.itype = SOXR_INT32_I; io_spec.otype = SOXR_INT32_I; @@ -212,11 +218,13 @@ void *soxr_time_check(__attribute__((unused)) void *arg) { io_spec.e = NULL; io_spec.flags = 0; + //pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable 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. + //pthread_setcancelstate(oldState, NULL); } double soxr_execution_time_us = @@ -1162,25 +1170,27 @@ int parse_options(int argc, char **argv) { if (tdebuglev != 0) debuglev = tdebuglev; - /* if the Service Name wasn't specified, do it now */ - - if (raw_service_name == NULL) - raw_service_name = strdup("%H"); - // now, do the substitutions in the service name char hostname[100]; gethostname(hostname, 100); - char *i1 = str_replace(raw_service_name, "%h", hostname); - if (raw_service_name) { - free(raw_service_name); - raw_service_name = NULL; - } + + + + char *i0; + if (raw_service_name == NULL) + i0 = strdup("%H"); // this is the default it the Service Name wasn't specified + else + i0 = strdup(raw_service_name); + + // here, do the substitutions for %h, %H, %v and %V + char *i1 = str_replace(i0, "%h", hostname); if ((hostname[0] >= 'a') && (hostname[0] <= 'z')) hostname[0] = hostname[0] - 0x20; // convert a lowercase first letter into a capital letter char *i2 = str_replace(i1, "%H", hostname); char *i3 = str_replace(i2, "%v", PACKAGE_VERSION); char *vs = get_version_string(); - config.service_name = str_replace(i3, "%V", vs); + config.service_name = str_replace(i3, "%V", vs); // service name complete + free(i0); free(i1); free(i2); free(i3); @@ -1277,8 +1287,8 @@ const char *pid_file_proc(void) { #endif void main_cleanup_handler(__attribute__((unused)) void *arg) { - // it doesn't look like this is called when the main function is cancelled eith a pthread cancel. - debug(2, "main cleanup handler called."); + // it doesn't look like this is called when the main function is cancelled with a pthread cancel. + debug(1, "main cleanup handler called."); #ifdef CONFIG_MQTT if (config.mqtt_enabled) { // terminate_mqtt(); @@ -1320,6 +1330,11 @@ void main_cleanup_handler(__attribute__((unused)) void *arg) { config.output->deinit(); } +#ifdef CONFIG_SOXR + // be careful -- not sure if the thread can be cancelled cleanly, so wait for it to shut down + pthread_join(soxr_time_check_thread, NULL); +#endif + #ifdef CONFIG_LIBDAEMON // only do this if you are the daemon if (pid == 0) { @@ -1334,7 +1349,7 @@ void main_cleanup_handler(__attribute__((unused)) void *arg) { } void exit_function() { - debug(2, "exit function called..."); + debug(1, "exit function called..."); main_cleanup_handler(NULL); if (conns) free(conns); // make sure the connections have been deleted first @@ -1643,7 +1658,7 @@ int main(int argc, char **argv) { } config.output->init(argc - audio_arg, argv + audio_arg); - pthread_cleanup_push(main_cleanup_handler, NULL); + // pthread_cleanup_push(main_cleanup_handler, NULL); // daemon_log(LOG_NOTICE, "startup"); @@ -1854,15 +1869,6 @@ int main(int argc, char **argv) { #endif activity_monitor_start(); - - // debug(1, "Successful Startup"); rtsp_listen_loop(); - - // should not reach this... - // daemon_log(LOG_NOTICE, "Unexpected exit..."); - // daemon_retval_send(0); - // daemon_pid_file_remove(); - pthread_cleanup_pop(1); - debug(1, "Odd exit point"); - pthread_exit(NULL); + return 0; } From e6bd5b15568c96982333daa5bcb046c43082273b Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Sun, 21 Jul 2019 08:52:27 +0100 Subject: [PATCH 31/33] Fix a bug that sometimes caused a crash when a service name was specified in the configuration file. Some other minor changes. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 09540012..adcefe3c 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.50]) -AC_INIT([shairport-sync], [3.3.2d2], [mikebrady@eircom.net]) +AC_INIT([shairport-sync], [3.3.2d3], [mikebrady@eircom.net]) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([shairport.c]) AC_CONFIG_HEADERS([config.h]) From 336e1bb1c39967a26f0da7ffb9f336adaf651941 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 22 Jul 2019 13:34:35 +0100 Subject: [PATCH 32/33] remove some detritus --- shairport.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/shairport.c b/shairport.c index b301aecf..6d74cae4 100644 --- a/shairport.c +++ b/shairport.c @@ -180,7 +180,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)0x180000000 + soxr_start_time; // loop for a second and a half, max + (uint64_t)0x180000000 + soxr_start_time; // loop for a second and a half, max -- no need to be able to cancel it, do _don't even try_! while (get_absolute_time_in_fp() < loop_until_time) { number_of_iterations++; @@ -200,31 +200,23 @@ void *soxr_time_check(__attribute__((unused)) void *arg) { size_t odone; - //int oldState; - //pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable - 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. - //pthread_setcancelstate(oldState, NULL); - pthread_testcancel(); - 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; - //pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable 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. - //pthread_setcancelstate(oldState, NULL); } double soxr_execution_time_us = From addc744bfa6c76b8a8f8604668f6b8134de4bdac Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Wed, 24 Jul 2019 08:51:55 +0200 Subject: [PATCH 33/33] Update RELEASENOTES-DEVELOPMENT.md --- RELEASENOTES-DEVELOPMENT.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/RELEASENOTES-DEVELOPMENT.md b/RELEASENOTES-DEVELOPMENT.md index bcd33c62..a5fc6ce8 100644 --- a/RELEASENOTES-DEVELOPMENT.md +++ b/RELEASENOTES-DEVELOPMENT.md @@ -1,3 +1,8 @@ +Version 3.3.2d2 +==== +**Bug Fix** +* Fix a double-free memory bug. Thanks to [Chris Boot](https://github.com/bootc) for reporting this bug, which came through the Debian BTS: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=925577. The fix is different from the suggested patch. + Version 3.3.2d1 ==== **Enhancements**