diff --git a/README.md b/README.md index ec6bb82b8..5a7acbe7d 100644 --- a/README.md +++ b/README.md @@ -535,38 +535,64 @@ 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 + 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. @@ -575,11 +601,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 --------- diff --git a/activity_monitor.c b/activity_monitor.c index c6966bcba..544f0362a 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 d9e1ffd69..6c7865237 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 786863768..cd3f2cb2c 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 5fc5f391f..856f184b4 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 956deba5f..69aaa0a31 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); @@ -66,13 +66,15 @@ 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 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", @@ -120,7 +122,10 @@ 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; static snd_pcm_sw_params_t *alsa_swparams = NULL; @@ -152,20 +157,90 @@ 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); +// 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; -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); +// 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 -// 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; +// 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) { + // 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, 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) + // 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 = 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) && (is_a_real_hardware_device)) { + precision_delay_available_status = YNDK_YES; + 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."); + } else { + debug(2, "alsa: precision delay timing is not available."); + } + precision_delay_available_status = YNDK_NO; + } + } + } + } + return (precision_delay_available_status == YNDK_YES); } // static int play_number; @@ -192,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); } @@ -276,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, @@ -353,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; @@ -377,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; } @@ -388,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; } @@ -417,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) { @@ -427,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, @@ -581,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) { @@ -613,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; } @@ -649,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"); } } @@ -940,20 +1003,18 @@ 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; - + config.disable_standby_mode = disable_standby_off; config.keep_dac_busy = 0; config.use_precision_timing = YNA_AUTO; @@ -1035,12 +1096,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) @@ -1070,48 +1130,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)) { @@ -1165,12 +1231,36 @@ 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) || (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) @@ -1178,15 +1268,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) @@ -1194,11 +1286,21 @@ 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"); + 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 @@ -1295,14 +1397,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; @@ -1314,31 +1417,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; @@ -1347,12 +1452,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); @@ -1372,42 +1477,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) { - 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 + // 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; } } 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 @@ -1425,8 +1529,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 { @@ -1564,27 +1668,28 @@ 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, 1); + 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 { 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 { @@ -1617,7 +1722,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 { @@ -1672,11 +1777,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); @@ -1688,16 +1793,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); @@ -1817,10 +1921,14 @@ 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_detected = 0; int okb = -1; - while (1) { + 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"); + 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)) { @@ -1828,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)) { @@ -1850,8 +1958,9 @@ 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; @@ -1866,26 +1975,21 @@ 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); int use_dither = 0; if ((hardware_mixer == 0) && (config.ignore_volume_control == 0) && @@ -1895,11 +1999,23 @@ 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); - do_play(silence, frames_of_silence); - pthread_cleanup_pop(1); + ret = do_play(silence, frames_of_silence); + frame_count++; + pthread_cleanup_pop(1); // free malloced buffer + 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 > 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_detected = 1; + } + } } } } diff --git a/audio_dummy.c b/audio_dummy.c index 483dbe426..026ef78aa 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 746d3d614..83398e2c5 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 f0ced371a..4ec9b2402 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 a591add4c..54e1763d8 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 7983733b3..53de2acbc 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 9775b07ef..396481612 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"); @@ -1369,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); @@ -1435,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 - die("Unexpected SPS_FORMAT_* with index %d while outputting silence",format); + 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 a80a8f978..ea5834418 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 @@ -208,23 +201,26 @@ 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 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 +279,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 cea3e7f5d..0b6db70ed 100644 --- a/dacp.c +++ b/dacp.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -111,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; @@ -322,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 } @@ -377,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; @@ -422,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; @@ -472,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 47c5b846a..e18a35116 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 cf178f1d7..95bae443a 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 4d7def388..400c1e55a 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 92bda3a5c..179828138 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 7496cc70c..2e1e7f9f4 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/rtsp.c b/rtsp.c index 20fc6b8b5..4a72250af 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; + if (nsock) { + int maxfd = -1; + fd_set fds; + FD_ZERO(&fds); + for (i = 0; i < nsock; i++) { + if (sockfd[i] > maxfd) + maxfd = sockfd[i]; + } - for (i = 0; i < nsock; i++) - FD_SET(sockfd[i], &fds); + mdns_register(); - ret = select(maxfd + 1, &fds, 0, 0, &tv); - if (ret < 0) { - if (errno == EINTR) - continue; - break; - } + 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; - cleanup_threads(); + for (i = 0; i < nsock; i++) + FD_SET(sockfd[i], &fds); - acceptfd = -1; - for (i = 0; i < nsock; i++) { - if (FD_ISSET(sockfd[i], &fds)) { - acceptfd = sockfd[i]; + 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); - - 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); - } -#ifdef AF_INET6 - if (local_info->SAFAMILY == AF_INET6) { - // IPv6: - - 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); + cleanup_threads(); + + acceptfd = -1; + for (i = 0; i < nsock; i++) { + if (FD_ISSET(sockfd[i], &fds)) { + acceptfd = sockfd[i]; + break; } -#endif + } + 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); + + 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); - - 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); + 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); + } + #ifdef AF_INET6 + if (local_info->SAFAMILY == AF_INET6) { + // IPv6: + + 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 - pthread_cleanup_pop(1); // should never happen - debug(1, "Oops -- fell out of the RTSP select loop"); + } 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/scripts/shairport-sync.conf b/scripts/shairport-sync.conf index a17d6337b..5e6089aa7 100644 --- a/scripts/shairport-sync.conf +++ b/scripts/shairport-sync.conf @@ -91,8 +91,10 @@ 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". +// 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. diff --git a/shairport.c b/shairport.c index 667a565b5..6d74cae4b 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 -- 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++; - 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); } @@ -1156,25 +1162,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); @@ -1271,8 +1279,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(); @@ -1313,22 +1321,27 @@ void main_cleanup_handler(__attribute__((unused)) void *arg) { debug(2, "Deinitialise the audio backend."); 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 + // 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); } 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 @@ -1366,12 +1379,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 +1423,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 +1433,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 +1501,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 +1511,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 +1524,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 +1604,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 +1621,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 +1631,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 +1640,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); @@ -1638,7 +1650,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"); @@ -1701,7 +1713,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 +1729,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 +1797,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 @@ -1847,15 +1861,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; }