summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--audio_alsa.c777
-rw-r--r--common.c126
-rw-r--r--common.h3
-rw-r--r--player.c83
-rw-r--r--player.h2
-rw-r--r--rtsp.c12
6 files changed, 592 insertions, 411 deletions
diff --git a/audio_alsa.c b/audio_alsa.c
index 21e1c5e..ed0a54a 100644
--- a/audio_alsa.c
+++ b/audio_alsa.c
@@ -104,7 +104,9 @@ static char *alsa_mix_ctrl = "Master";
static int alsa_mix_index = 0;
static int hardware_mixer = 0;
static int has_softvol = 0;
+
int keep_dac_busy = 0; // true if the dac must be kept busy with samples
+int64_t dither_random_number_store = 0;
static int volume_set_request = 0; // set when an external request is made to set the volume.
int mute_request_pending = 0; // set when an external request is made to mute or unmute.
@@ -208,359 +210,17 @@ void do_snd_mixer_selem_set_playback_dB_all(snd_mixer_elem_t *mix_elem, double v
}
}
-static int init(int argc, char **argv) {
- // for debugging
- snd_output_stdio_attach(&output, stdout, 0);
-
- // debug(2,"audio_alsa init called.");
- int response = 0; // this will be what we return to the caller.
- const char *str;
- int value;
- // double dvalue;
-
- // set up default values first
- set_period_size_request = 0;
- set_buffer_size_request = 0;
- config.alsa_use_hardware_mute = 0; // don't use it by default
-
- config.audio_backend_latency_offset = 0;
- config.audio_backend_buffer_desired_length = 0.15;
-
- config.alsa_maximum_stall_time = 0.200; // 200 milliseconds -- if it takes longer, it's a problem
-
- // get settings from settings file first, allow them to be overridden by
- // command line options
-
- // do the "general" audio options. Note, these options are in the "general" stanza!
- parse_general_audio_options();
-
- if (config.cfg != NULL) {
- double dvalue;
-
- /* Get the Output Device Name. */
- if (config_lookup_string(config.cfg, "alsa.output_device", &str)) {
- alsa_out_dev = (char *)str;
- }
-
- /* Get the Mixer Type setting. */
-
- if (config_lookup_string(config.cfg, "alsa.mixer_type", &str)) {
- inform("The alsa mixer_type setting is deprecated and has been ignored. "
- "FYI, using the \"mixer_control_name\" setting automatically "
- "chooses a hardware mixer.");
- }
-
- /* Get the Mixer Device Name. */
- if (config_lookup_string(config.cfg, "alsa.mixer_device", &str)) {
- alsa_mix_dev = (char *)str;
- }
-
- /* Get the Mixer Control Name. */
- if (config_lookup_string(config.cfg, "alsa.mixer_control_name", &str)) {
- alsa_mix_ctrl = (char *)str;
- hardware_mixer = 1;
- }
-
- /* Get the disable_synchronization setting. */
- if (config_lookup_string(config.cfg, "alsa.disable_synchronization", &str)) {
- if (strcasecmp(str, "no") == 0)
- config.no_sync = 0;
- else if (strcasecmp(str, "yes") == 0)
- config.no_sync = 1;
- else {
- warn("Invalid disable_synchronization option choice \"%s\". It should be \"yes\" or "
- "\"no\". It is set to \"no\".");
- config.no_sync = 0;
- }
- }
-
- /* Get the mute_using_playback_switch setting. */
- if (config_lookup_string(config.cfg, "alsa.mute_using_playback_switch", &str)) {
- inform("The alsa \"mute_using_playback_switch\" setting is deprecated. "
- "Please use the \"use_hardware_mute_if_available\" setting instead.");
- if (strcasecmp(str, "no") == 0)
- config.alsa_use_hardware_mute = 0;
- else if (strcasecmp(str, "yes") == 0)
- config.alsa_use_hardware_mute = 1;
- else {
- warn("Invalid mute_using_playback_switch option choice \"%s\". It should be \"yes\" or "
- "\"no\". It is set to \"no\".");
- config.alsa_use_hardware_mute = 0;
- }
- }
-
- /* Get the use_hardware_mute_if_available setting. */
- if (config_lookup_string(config.cfg, "alsa.use_hardware_mute_if_available", &str)) {
- if (strcasecmp(str, "no") == 0)
- config.alsa_use_hardware_mute = 0;
- else if (strcasecmp(str, "yes") == 0)
- config.alsa_use_hardware_mute = 1;
- else {
- warn("Invalid use_hardware_mute_if_available option choice \"%s\". It should be \"yes\" or "
- "\"no\". It is set to \"no\".");
- 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)) {
- if (strcasecmp(str, "S16") == 0)
- config.output_format = SPS_FORMAT_S16;
- else if (strcasecmp(str, "S24") == 0)
- config.output_format = SPS_FORMAT_S24;
- else if (strcasecmp(str, "S24_3LE") == 0)
- config.output_format = SPS_FORMAT_S24_3LE;
- else if (strcasecmp(str, "S24_3BE") == 0)
- config.output_format = SPS_FORMAT_S24_3BE;
- else if (strcasecmp(str, "S32") == 0)
- config.output_format = SPS_FORMAT_S32;
- else if (strcasecmp(str, "U8") == 0)
- config.output_format = SPS_FORMAT_U8;
- else if (strcasecmp(str, "S8") == 0)
- config.output_format = SPS_FORMAT_S8;
- else {
- warn("Invalid output format \"%s\". It should be \"U8\", \"S8\", \"S16\", \"S24\", "
- "\"S24_3LE\", \"S24_3BE\" or "
- "\"S32\". It is set to \"S16\".",
- str);
- config.output_format = SPS_FORMAT_S16;
- }
- }
-
- /* Get the output rate, which must be a multiple of 44,100*/
- if (config_lookup_int(config.cfg, "alsa.output_rate", &value)) {
- debug(1, "alsa output rate is %d frames per second", value);
- switch (value) {
- case 44100:
- case 88200:
- case 176400:
- case 352800:
- config.output_rate = value;
- break;
- default:
- warn("Invalid output rate \"%d\". It should be a multiple of 44,100 up to 352,800. It is "
- "set to 44,100",
- value);
- config.output_rate = 44100;
- }
- }
-
- /* Get the use_mmap_if_available setting. */
- if (config_lookup_string(config.cfg, "alsa.use_mmap_if_available", &str)) {
- if (strcasecmp(str, "no") == 0)
- config.no_mmap = 1;
- else if (strcasecmp(str, "yes") == 0)
- config.no_mmap = 0;
- else {
- warn("Invalid use_mmap_if_available option choice \"%s\". It should be \"yes\" or \"no\". "
- "It is set to \"yes\".");
- config.no_mmap = 0;
- }
- }
- /* Get the optional period size value */
- if (config_lookup_int(config.cfg, "alsa.period_size", &value)) {
- set_period_size_request = 1;
- debug(1, "Value read for period size is %d.", value);
- if (value < 0) {
- warn("Invalid alsa period size setting \"%d\". It "
- "must be greater than 0. No setting is made.",
- value);
- set_period_size_request = 0;
- } else {
- period_size_requested = value;
- }
- }
-
- /* Get the optional buffer size value */
- if (config_lookup_int(config.cfg, "alsa.buffer_size", &value)) {
- set_buffer_size_request = 1;
- debug(1, "Value read for buffer size is %d.", value);
- if (value < 0) {
- warn("Invalid alsa buffer size setting \"%d\". It "
- "must be greater than 0. No setting is made.",
- value);
- set_buffer_size_request = 0;
- } else {
- buffer_size_requested = value;
- }
- }
-
- /* Get the optional alsa_maximum_stall_time setting. */
- if (config_lookup_float(config.cfg, "alsa.maximum_stall_time", &dvalue)) {
- if (dvalue < 0.0) {
- warn("Invalid alsa maximum write time setting \"%f\". It "
- "must be greater than 0. Default is \"%f\". No setting is made.",
- dvalue, config.alsa_maximum_stall_time);
- } else {
- config.alsa_maximum_stall_time = dvalue;
- }
- }
-
- // Get the optional "Keep DAC Busy setting"
- config_set_lookup_bool(config.cfg, "alsa.disable_standby_mode", &keep_dac_busy);
- debug(1, "alsa: disable_standby_mode is set to \"%s\".", keep_dac_busy ? "yes" : "no");
- }
-
- optind = 1; // optind=0 is equivalent to optind=1 plus special behaviour
- argv--; // so we shift the arguments to satisfy getopt()
- argc++;
- // some platforms apparently require optreset = 1; - which?
- int opt;
- while ((opt = getopt(argc, argv, "d:t:m:c:i:")) > 0) {
- switch (opt) {
- case 'd':
- alsa_out_dev = optarg;
- break;
-
- case 't':
- inform("The alsa backend -t option is deprecated and has been ignored. "
- "FYI, using the -c option automatically chooses a hardware "
- "mixer.");
- break;
-
- case 'm':
- alsa_mix_dev = optarg;
- break;
- case 'c':
- alsa_mix_ctrl = optarg;
- hardware_mixer = 1;
- break;
- case 'i':
- alsa_mix_index = strtol(optarg, NULL, 10);
- break;
- default:
- warn("Invalid audio option \"-%c\" specified -- ignored.", opt);
- help();
- }
- }
-
- if (optind < argc) {
- warn("Invalid audio argument: \"%s\" -- ignored", argv[optind]);
- }
-
- debug(1, "alsa: output device name is \"%s\".", alsa_out_dev);
-
- if (hardware_mixer) {
- int oldState;
- pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
-
- if (alsa_mix_dev == NULL)
- alsa_mix_dev = alsa_out_dev;
-
- // Now, start trying to initialise the alsa device with the settings obtained
- pthread_cleanup_debug_mutex_lock(&alsa_mutex, 1000, 1);
- if (open_mixer() == 1) {
- if (snd_mixer_selem_get_playback_volume_range(alsa_mix_elem, &alsa_mix_minv, &alsa_mix_maxv) <
- 0)
- debug(1, "Can't read mixer's [linear] min and max volumes.");
- else {
- if (snd_mixer_selem_get_playback_dB_range(alsa_mix_elem, &alsa_mix_mindb,
- &alsa_mix_maxdb) == 0) {
-
- audio_alsa.volume = &volume; // insert the volume function now we know it can do dB stuff
- audio_alsa.parameters = &parameters; // likewise the parameters stuff
- if (alsa_mix_mindb == SND_CTL_TLV_DB_GAIN_MUTE) {
- // For instance, the Raspberry Pi does this
- debug(1, "Lowest dB value is a mute");
- mixer_volume_setting_gives_mute = 1;
- alsa_mix_mute = SND_CTL_TLV_DB_GAIN_MUTE; // this may not be necessary -- it's always
- // going to be SND_CTL_TLV_DB_GAIN_MUTE, right?
- // debug(1, "Try minimum volume + 1 as lowest true attenuation value");
- if (snd_mixer_selem_ask_playback_vol_dB(alsa_mix_elem, alsa_mix_minv + 1,
- &alsa_mix_mindb) != 0)
- debug(1, "Can't get dB value corresponding to a minimum volume + 1.");
- }
- debug(1, "Hardware mixer has dB volume from %f to %f.", (1.0 * alsa_mix_mindb) / 100.0,
- (1.0 * alsa_mix_maxdb) / 100.0);
- } else {
- // use the linear scale and do the db conversion ourselves
- warn("The hardware mixer specified -- \"%s\" -- does not have "
- "a dB volume scale.",
- alsa_mix_ctrl);
-
- if (snd_ctl_open(&ctl, alsa_mix_dev, 0) < 0) {
- warn("Cannot open control \"%s\"", alsa_mix_dev);
- response = -1;
- }
- if (snd_ctl_elem_id_malloc(&elem_id) < 0) {
- debug(1, "Cannot allocate memory for control \"%s\"", alsa_mix_dev);
- elem_id = NULL;
- response = -2;
- } else {
- snd_ctl_elem_id_set_interface(elem_id, SND_CTL_ELEM_IFACE_MIXER);
- snd_ctl_elem_id_set_name(elem_id, alsa_mix_ctrl);
-
- if (snd_ctl_get_dB_range(ctl, elem_id, &alsa_mix_mindb, &alsa_mix_maxdb) == 0) {
- debug(1, "alsa: hardware mixer \"%s\" selected, with dB volume from %f to %f.",
- alsa_mix_ctrl, (1.0 * alsa_mix_mindb) / 100.0, (1.0 * alsa_mix_maxdb) / 100.0);
- has_softvol = 1;
- audio_alsa.volume =
- &volume; // insert the volume function now we know it can do dB stuff
- audio_alsa.parameters = &parameters; // likewise the parameters stuff
- } else {
- debug(1, "Cannot get the dB range from the volume control \"%s\"", alsa_mix_ctrl);
- }
- }
- /*
- debug(1, "Min and max volumes are %d and
- %d.",alsa_mix_minv,alsa_mix_maxv);
- alsa_mix_maxdb = 0;
- if ((alsa_mix_maxv!=0) && (alsa_mix_minv!=0))
- alsa_mix_mindb =
- -20*100*(log10(alsa_mix_maxv*1.0)-log10(alsa_mix_minv*1.0));
- else if (alsa_mix_maxv!=0)
- alsa_mix_mindb = -20*100*log10(alsa_mix_maxv*1.0);
- audio_alsa.volume = &linear_volume; // insert the linear volume function
- audio_alsa.parameters = &parameters; // likewise the parameters stuff
- debug(1,"Max and min dB calculated are %d and
- %d.",alsa_mix_maxdb,alsa_mix_mindb);
- */
- }
- }
- if (((config.alsa_use_hardware_mute == 1) &&
- (snd_mixer_selem_has_playback_switch(alsa_mix_elem))) ||
- mixer_volume_setting_gives_mute) {
- audio_alsa.mute = &mute; // insert the mute function now we know it can do muting stuff
- // debug(1, "Has mixer and mute ability we will use.");
- } else {
- // debug(1, "Has mixer but not using hardware mute.");
- }
- close_mixer();
- }
- debug_mutex_unlock(&alsa_mutex, 3); // release the mutex
-
- pthread_cleanup_pop(0);
- pthread_setcancelstate(oldState, NULL);
- } else {
- debug(1, "alsa: no hardware mixer selected.");
- }
- alsa_mix_handle = NULL;
-
- // so, now, if the option to keep the DAC running has been selected, start a thread to monitor the
- // length of the queue
- // if the queue gets too short, stuff it with silence
-
- desired_sample_rate = config.output_rate;
- sample_format = config.output_format;
-
- if (keep_dac_busy)
- pthread_create(&alsa_buffer_monitor_thread, NULL, &alsa_buffer_monitor_thread_code, NULL);
-
- return response;
-}
+void actual_close_alsa_device() {
+ if (alsa_handle) {
+ int derr;
+ if ((derr = snd_pcm_hw_free(alsa_handle)))
+ debug(1, "Error %d (\"%s\") freeing the output device hardware while closing it.", derr,
+ snd_strerror(derr));
-static void deinit(void) {
- int oldState;
- pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
- // debug(2,"audio_alsa deinit called.");
- stop();
- if (keep_dac_busy) {
- debug(1, "Cancel buffer monitor thread.");
- pthread_cancel(alsa_buffer_monitor_thread);
- debug(1, "Join buffer monitor thread.");
- pthread_join(alsa_buffer_monitor_thread, NULL);
+ if ((derr = snd_pcm_close(alsa_handle)))
+ debug(1, "Error %d (\"%s\") closing the output device.", derr, snd_strerror(derr));
+ alsa_handle = NULL;
}
- pthread_setcancelstate(oldState, NULL);
}
// assuming pthread cancellation is disabled
@@ -950,6 +610,370 @@ int open_alsa_device(void) {
return result;
}
+static int init(int argc, char **argv) {
+ // for debugging
+ snd_output_stdio_attach(&output, stdout, 0);
+
+ // debug(2,"audio_alsa init called.");
+ int response = 0; // this will be what we return to the caller.
+ const char *str;
+ int value;
+ // double dvalue;
+
+ // set up default values first
+ set_period_size_request = 0;
+ set_buffer_size_request = 0;
+ config.alsa_use_hardware_mute = 0; // don't use it by default
+
+ config.audio_backend_latency_offset = 0;
+ config.audio_backend_buffer_desired_length = 0.15;
+
+ config.alsa_maximum_stall_time = 0.200; // 200 milliseconds -- if it takes longer, it's a problem
+
+ // get settings from settings file first, allow them to be overridden by
+ // command line options
+
+ // do the "general" audio options. Note, these options are in the "general" stanza!
+ parse_general_audio_options();
+
+ if (config.cfg != NULL) {
+ double dvalue;
+
+ /* Get the Output Device Name. */
+ if (config_lookup_string(config.cfg, "alsa.output_device", &str)) {
+ alsa_out_dev = (char *)str;
+ }
+
+ /* Get the Mixer Type setting. */
+
+ if (config_lookup_string(config.cfg, "alsa.mixer_type", &str)) {
+ inform("The alsa mixer_type setting is deprecated and has been ignored. "
+ "FYI, using the \"mixer_control_name\" setting automatically "
+ "chooses a hardware mixer.");
+ }
+
+ /* Get the Mixer Device Name. */
+ if (config_lookup_string(config.cfg, "alsa.mixer_device", &str)) {
+ alsa_mix_dev = (char *)str;
+ }
+
+ /* Get the Mixer Control Name. */
+ if (config_lookup_string(config.cfg, "alsa.mixer_control_name", &str)) {
+ alsa_mix_ctrl = (char *)str;
+ hardware_mixer = 1;
+ }
+
+ /* Get the disable_synchronization setting. */
+ if (config_lookup_string(config.cfg, "alsa.disable_synchronization", &str)) {
+ if (strcasecmp(str, "no") == 0)
+ config.no_sync = 0;
+ else if (strcasecmp(str, "yes") == 0)
+ config.no_sync = 1;
+ else {
+ warn("Invalid disable_synchronization option choice \"%s\". It should be \"yes\" or "
+ "\"no\". It is set to \"no\".");
+ config.no_sync = 0;
+ }
+ }
+
+ /* Get the mute_using_playback_switch setting. */
+ if (config_lookup_string(config.cfg, "alsa.mute_using_playback_switch", &str)) {
+ inform("The alsa \"mute_using_playback_switch\" setting is deprecated. "
+ "Please use the \"use_hardware_mute_if_available\" setting instead.");
+ if (strcasecmp(str, "no") == 0)
+ config.alsa_use_hardware_mute = 0;
+ else if (strcasecmp(str, "yes") == 0)
+ config.alsa_use_hardware_mute = 1;
+ else {
+ warn("Invalid mute_using_playback_switch option choice \"%s\". It should be \"yes\" or "
+ "\"no\". It is set to \"no\".");
+ config.alsa_use_hardware_mute = 0;
+ }
+ }
+
+ /* Get the use_hardware_mute_if_available setting. */
+ if (config_lookup_string(config.cfg, "alsa.use_hardware_mute_if_available", &str)) {
+ if (strcasecmp(str, "no") == 0)
+ config.alsa_use_hardware_mute = 0;
+ else if (strcasecmp(str, "yes") == 0)
+ config.alsa_use_hardware_mute = 1;
+ else {
+ warn("Invalid use_hardware_mute_if_available option choice \"%s\". It should be \"yes\" or "
+ "\"no\". It is set to \"no\".");
+ 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)) {
+ if (strcasecmp(str, "S16") == 0)
+ config.output_format = SPS_FORMAT_S16;
+ else if (strcasecmp(str, "S24") == 0)
+ config.output_format = SPS_FORMAT_S24;
+ else if (strcasecmp(str, "S24_3LE") == 0)
+ config.output_format = SPS_FORMAT_S24_3LE;
+ else if (strcasecmp(str, "S24_3BE") == 0)
+ config.output_format = SPS_FORMAT_S24_3BE;
+ else if (strcasecmp(str, "S32") == 0)
+ config.output_format = SPS_FORMAT_S32;
+ else if (strcasecmp(str, "U8") == 0)
+ config.output_format = SPS_FORMAT_U8;
+ else if (strcasecmp(str, "S8") == 0)
+ config.output_format = SPS_FORMAT_S8;
+ else {
+ warn("Invalid output format \"%s\". It should be \"U8\", \"S8\", \"S16\", \"S24\", "
+ "\"S24_3LE\", \"S24_3BE\" or "
+ "\"S32\". It is set to \"S16\".",
+ str);
+ config.output_format = SPS_FORMAT_S16;
+ }
+ }
+
+ /* Get the output rate, which must be a multiple of 44,100*/
+ if (config_lookup_int(config.cfg, "alsa.output_rate", &value)) {
+ debug(1, "alsa output rate is %d frames per second", value);
+ switch (value) {
+ case 44100:
+ case 88200:
+ case 176400:
+ case 352800:
+ config.output_rate = value;
+ break;
+ default:
+ warn("Invalid output rate \"%d\". It should be a multiple of 44,100 up to 352,800. It is "
+ "set to 44,100",
+ value);
+ config.output_rate = 44100;
+ }
+ }
+
+ /* Get the use_mmap_if_available setting. */
+ if (config_lookup_string(config.cfg, "alsa.use_mmap_if_available", &str)) {
+ if (strcasecmp(str, "no") == 0)
+ config.no_mmap = 1;
+ else if (strcasecmp(str, "yes") == 0)
+ config.no_mmap = 0;
+ else {
+ warn("Invalid use_mmap_if_available option choice \"%s\". It should be \"yes\" or \"no\". "
+ "It is set to \"yes\".");
+ config.no_mmap = 0;
+ }
+ }
+ /* Get the optional period size value */
+ if (config_lookup_int(config.cfg, "alsa.period_size", &value)) {
+ set_period_size_request = 1;
+ debug(1, "Value read for period size is %d.", value);
+ if (value < 0) {
+ warn("Invalid alsa period size setting \"%d\". It "
+ "must be greater than 0. No setting is made.",
+ value);
+ set_period_size_request = 0;
+ } else {
+ period_size_requested = value;
+ }
+ }
+
+ /* Get the optional buffer size value */
+ if (config_lookup_int(config.cfg, "alsa.buffer_size", &value)) {
+ set_buffer_size_request = 1;
+ debug(1, "Value read for buffer size is %d.", value);
+ if (value < 0) {
+ warn("Invalid alsa buffer size setting \"%d\". It "
+ "must be greater than 0. No setting is made.",
+ value);
+ set_buffer_size_request = 0;
+ } else {
+ buffer_size_requested = value;
+ }
+ }
+
+ /* Get the optional alsa_maximum_stall_time setting. */
+ if (config_lookup_float(config.cfg, "alsa.maximum_stall_time", &dvalue)) {
+ if (dvalue < 0.0) {
+ warn("Invalid alsa maximum write time setting \"%f\". It "
+ "must be greater than 0. Default is \"%f\". No setting is made.",
+ dvalue, config.alsa_maximum_stall_time);
+ } else {
+ config.alsa_maximum_stall_time = dvalue;
+ }
+ }
+
+ // Get the optional "Keep DAC Busy setting"
+ config_set_lookup_bool(config.cfg, "alsa.disable_standby_mode", &keep_dac_busy);
+ debug(1, "alsa: disable_standby_mode is %s.", keep_dac_busy ? "on" : "off");
+ }
+
+ optind = 1; // optind=0 is equivalent to optind=1 plus special behaviour
+ argv--; // so we shift the arguments to satisfy getopt()
+ argc++;
+ // some platforms apparently require optreset = 1; - which?
+ int opt;
+ while ((opt = getopt(argc, argv, "d:t:m:c:i:")) > 0) {
+ switch (opt) {
+ case 'd':
+ alsa_out_dev = optarg;
+ break;
+
+ case 't':
+ inform("The alsa backend -t option is deprecated and has been ignored. "
+ "FYI, using the -c option automatically chooses a hardware "
+ "mixer.");
+ break;
+
+ case 'm':
+ alsa_mix_dev = optarg;
+ break;
+ case 'c':
+ alsa_mix_ctrl = optarg;
+ hardware_mixer = 1;
+ break;
+ case 'i':
+ alsa_mix_index = strtol(optarg, NULL, 10);
+ break;
+ default:
+ warn("Invalid audio option \"-%c\" specified -- ignored.", opt);
+ help();
+ }
+ }
+
+ if (optind < argc) {
+ warn("Invalid audio argument: \"%s\" -- ignored", argv[optind]);
+ }
+
+ debug(1, "alsa: output device name is \"%s\".", alsa_out_dev);
+
+ if (hardware_mixer) {
+ int oldState;
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
+
+ if (alsa_mix_dev == NULL)
+ alsa_mix_dev = alsa_out_dev;
+
+ // Now, start trying to initialise the alsa device with the settings obtained
+ pthread_cleanup_debug_mutex_lock(&alsa_mutex, 1000, 1);
+ if (open_mixer() == 1) {
+ if (snd_mixer_selem_get_playback_volume_range(alsa_mix_elem, &alsa_mix_minv, &alsa_mix_maxv) <
+ 0)
+ debug(1, "Can't read mixer's [linear] min and max volumes.");
+ else {
+ if (snd_mixer_selem_get_playback_dB_range(alsa_mix_elem, &alsa_mix_mindb,
+ &alsa_mix_maxdb) == 0) {
+
+ audio_alsa.volume = &volume; // insert the volume function now we know it can do dB stuff
+ audio_alsa.parameters = &parameters; // likewise the parameters stuff
+ if (alsa_mix_mindb == SND_CTL_TLV_DB_GAIN_MUTE) {
+ // For instance, the Raspberry Pi does this
+ debug(1, "Lowest dB value is a mute");
+ mixer_volume_setting_gives_mute = 1;
+ alsa_mix_mute = SND_CTL_TLV_DB_GAIN_MUTE; // this may not be necessary -- it's always
+ // going to be SND_CTL_TLV_DB_GAIN_MUTE, right?
+ // debug(1, "Try minimum volume + 1 as lowest true attenuation value");
+ if (snd_mixer_selem_ask_playback_vol_dB(alsa_mix_elem, alsa_mix_minv + 1,
+ &alsa_mix_mindb) != 0)
+ debug(1, "Can't get dB value corresponding to a minimum volume + 1.");
+ }
+ debug(1, "Hardware mixer has dB volume from %f to %f.", (1.0 * alsa_mix_mindb) / 100.0,
+ (1.0 * alsa_mix_maxdb) / 100.0);
+ } else {
+ // use the linear scale and do the db conversion ourselves
+ warn("The hardware mixer specified -- \"%s\" -- does not have "
+ "a dB volume scale.",
+ alsa_mix_ctrl);
+
+ if (snd_ctl_open(&ctl, alsa_mix_dev, 0) < 0) {
+ warn("Cannot open control \"%s\"", alsa_mix_dev);
+ response = -1;
+ }
+ if (snd_ctl_elem_id_malloc(&elem_id) < 0) {
+ debug(1, "Cannot allocate memory for control \"%s\"", alsa_mix_dev);
+ elem_id = NULL;
+ response = -2;
+ } else {
+ snd_ctl_elem_id_set_interface(elem_id, SND_CTL_ELEM_IFACE_MIXER);
+ snd_ctl_elem_id_set_name(elem_id, alsa_mix_ctrl);
+
+ if (snd_ctl_get_dB_range(ctl, elem_id, &alsa_mix_mindb, &alsa_mix_maxdb) == 0) {
+ debug(1, "alsa: hardware mixer \"%s\" selected, with dB volume from %f to %f.",
+ alsa_mix_ctrl, (1.0 * alsa_mix_mindb) / 100.0, (1.0 * alsa_mix_maxdb) / 100.0);
+ has_softvol = 1;
+ audio_alsa.volume =
+ &volume; // insert the volume function now we know it can do dB stuff
+ audio_alsa.parameters = &parameters; // likewise the parameters stuff
+ } else {
+ debug(1, "Cannot get the dB range from the volume control \"%s\"", alsa_mix_ctrl);
+ }
+ }
+ /*
+ debug(1, "Min and max volumes are %d and
+ %d.",alsa_mix_minv,alsa_mix_maxv);
+ alsa_mix_maxdb = 0;
+ if ((alsa_mix_maxv!=0) && (alsa_mix_minv!=0))
+ alsa_mix_mindb =
+ -20*100*(log10(alsa_mix_maxv*1.0)-log10(alsa_mix_minv*1.0));
+ else if (alsa_mix_maxv!=0)
+ alsa_mix_mindb = -20*100*log10(alsa_mix_maxv*1.0);
+ audio_alsa.volume = &linear_volume; // insert the linear volume function
+ audio_alsa.parameters = &parameters; // likewise the parameters stuff
+ debug(1,"Max and min dB calculated are %d and
+ %d.",alsa_mix_maxdb,alsa_mix_mindb);
+ */
+ }
+ }
+ if (((config.alsa_use_hardware_mute == 1) &&
+ (snd_mixer_selem_has_playback_switch(alsa_mix_elem))) ||
+ mixer_volume_setting_gives_mute) {
+ audio_alsa.mute = &mute; // insert the mute function now we know it can do muting stuff
+ // debug(1, "Has mixer and mute ability we will use.");
+ } else {
+ // debug(1, "Has mixer but not using hardware mute.");
+ }
+ close_mixer();
+ }
+ debug_mutex_unlock(&alsa_mutex, 3); // release the mutex
+
+ pthread_cleanup_pop(0);
+ pthread_setcancelstate(oldState, NULL);
+ } else {
+ debug(1, "alsa: no hardware mixer selected.");
+ }
+ alsa_mix_handle = NULL;
+
+ // so, now, if the option to keep the DAC running has been selected, start a thread to monitor the
+ // length of the queue
+ // if the queue gets too short, stuff it with silence
+
+ desired_sample_rate = config.output_rate;
+ sample_format = config.output_format;
+
+ if (response == 0) {
+ // try opening the device.
+ int ret = actual_open_alsa_device();
+ if (ret == 0)
+ actual_close_alsa_device();
+ else
+ die("Could not open the alsa device with the settings given.");
+ }
+
+ if (keep_dac_busy)
+ pthread_create(&alsa_buffer_monitor_thread, NULL, &alsa_buffer_monitor_thread_code, NULL);
+
+ return response;
+}
+
+static void deinit(void) {
+ int oldState;
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
+ // debug(2,"audio_alsa deinit called.");
+ stop();
+ if (keep_dac_busy) {
+ debug(1, "Cancel buffer monitor thread.");
+ pthread_cancel(alsa_buffer_monitor_thread);
+ debug(1, "Join buffer monitor thread.");
+ pthread_join(alsa_buffer_monitor_thread, NULL);
+ }
+ pthread_setcancelstate(oldState, NULL);
+}
+
static void start(int i_sample_rate, int i_sample_format) {
// debug(2,"audio_alsa start called.");
if (i_sample_rate == 0)
@@ -1239,8 +1263,8 @@ static void flush(void) {
if (alsa_handle) {
stall_monitor_start_time = 0;
if (keep_dac_busy == 0) {
- if ((derr = snd_pcm_drop(alsa_handle)))
- debug(1, "Error %d (\"%s\") dropping output device.", derr, snd_strerror(derr));
+ if ((derr = snd_pcm_drop(alsa_handle)))
+ debug(1, "Error %d (\"%s\") dropping output device.", derr, snd_strerror(derr));
if ((derr = snd_pcm_hw_free(alsa_handle)))
debug(1, "Error %d (\"%s\") freeing the output device hardware.", derr, snd_strerror(derr));
@@ -1346,7 +1370,7 @@ static void mute(int mute_state_requested) {
}
void do_mute(int mute_state_requested) {
- debug(3,"Setting mute to %d.",mute_state_requested);
+ debug(3, "Setting mute to %d.", mute_state_requested);
int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
@@ -1419,31 +1443,42 @@ void *alsa_buffer_monitor_thread_code(void *arg) {
pthread_cleanup_push(alsa_buffer_monitor_thread_cleanup_function, arg);
pthread_setcancelstate(oldState, NULL);
+ // The thinking is, if the device has a hardware mixer, then
+ // (if no source transformation is happening), Shairport Sync
+ // will deliver fill-in silences and the audio material without adding dither.
+ // So don't insert dither into the silences sent to keep the DAC busy.
+
+ int use_dither = 0;
+ if (hardware_mixer == 0)
+ use_dither = 1;
+
+ debug(1, "alsa: dither will %sbe added to inter-session silence.", use_dither ? "" : "not ");
+
int sleep_time_ms = 80;
int frames_of_silence = desired_sample_rate * sleep_time_ms / 1000;
size_t size_of_silence_buffer = frames_of_silence * frame_size;
// debug(1,"Silence buffer length: %u bytes.",size_of_silence_buffer);
void *silence = malloc(size_of_silence_buffer);
if (silence == NULL) {
- debug(1, "Failed to allocate memory for a silent frame buffer.");
+ debug(1, "alsa: failed to allocate memory for a silent frame buffer, thus "
+ "disable_standby_mode is \"off\".");
} else {
- }
-
- long buffer_size;
- int reply;
- while (1) {
- usleep((sleep_time_ms * 1000) / 2);
- reply = delay(&buffer_size);
- if (reply != 0)
- debug(1, "error %d during buffer monitoring of delay", reply);
- if (buffer_size < (((int)desired_sample_rate * sleep_time_ms) / (1000))) {
- // debug(1,"Writing %d frames of silence because only %lu frames remain in the
- // buffer.",frames_of_silence,buffer_size);
- memset(silence, 0, size_of_silence_buffer);
- play(silence, frames_of_silence);
+ long buffer_size;
+ int reply;
+ while (1) {
+ if (buffer_size < (((int)desired_sample_rate * sleep_time_ms) / (1000))) {
+ dither_random_number_store = generate_zero_frames(
+ silence, frames_of_silence, config.output_format, use_dither, // i.e. with dither
+ dither_random_number_store);
+ play(silence, frames_of_silence);
+ }
+ usleep((sleep_time_ms * 1000) / 2);
+ reply = delay(&buffer_size);
+ if (reply != 0)
+ debug(1, "error %d during buffer monitoring of delay", reply);
+ pthread_testcancel();
}
- pthread_testcancel();
}
- pthread_cleanup_pop(0);
+ pthread_cleanup_pop(1);
pthread_exit(NULL);
}
diff --git a/common.c b/common.c
index 053abbe..aa38007 100644
--- a/common.c
+++ b/common.c
@@ -1058,7 +1058,7 @@ uint64_t r64u() { return (ranval(&rx)); }
int64_t r64i() { return (ranval(&rx) >> 1); }
/* generate an array of 64-bit random numbers */
-const int ranarraylength = 1009; // these will be 8-byte numbers.
+const int ranarraylength = 1009 * 203; // these will be 8-byte numbers.
int ranarraynext;
@@ -1083,9 +1083,11 @@ uint64_t ranarrayval() {
void r64arrayinit() { ranarrayinit(); }
-uint64_t ranarray64u() { return (ranarrayval()); }
+// uint64_t ranarray64u() { return (ranarrayval()); }
+uint64_t ranarray64u() { return (ranval(&rx)); }
-int64_t ranarray64i() { return (ranarrayval() >> 1); }
+// int64_t ranarray64i() { return (ranarrayval() >> 1); }
+int64_t ranarray64i() { return (ranval(&rx) >> 1); }
uint32_t nctohl(const uint8_t *p) { // read 4 characters from *p and do ntohl on them
// this is to avoid possible aliasing violations
@@ -1325,3 +1327,121 @@ char *get_version_string() {
}
return version_string;
}
+
+int64_t generate_zero_frames(char *outp, size_t number_of_frames, enum sps_format_t format,
+ int with_dither, int64_t random_number_in) {
+ // return the last random number used
+ // assuming the buffer has been assigned
+
+ 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://www.users.qwest.net/%7Evolt42/cadenzarecording/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
+
+ // 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:
+ dither_mask = (int64_t)1 << (64 + 1 - 32);
+ break;
+ case SPS_FORMAT_S24:
+ case SPS_FORMAT_S24_3LE:
+ case SPS_FORMAT_S24_3BE:
+ dither_mask = (int64_t)1 << (64 + 1 - 24);
+ break;
+ case SPS_FORMAT_S16:
+ dither_mask = (int64_t)1 << (64 + 1 - 16);
+ break;
+ case SPS_FORMAT_S8:
+ case SPS_FORMAT_U8:
+ dither_mask = (int64_t)1 << (64 + 1 - 8);
+ break;
+ case SPS_FORMAT_UNKNOWN:
+ die("Unexpected SPS_FORMAT_UNKNOWN while calculating dither mask.");
+ }
+ dither_mask -= 1;
+ // int64_t r = r64i();
+ int64_t r = ranarray64i();
+
+ int64_t tpdf = (r & dither_mask) - (previous_random_number & dither_mask);
+
+ // add dither if permitted -- no need to check for clipping, as the sample is, uh, zero
+
+ if (with_dither != 0)
+ hyper_sample += tpdf;
+
+ // move the result to the desired position in the int64_t
+ char *op = p;
+ int result; // 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;
+ 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;
+ 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;
+ break;
+ case SPS_FORMAT_S24:
+ hyper_sample >>= (64 - 24);
+ *(int32_t *)op = hyper_sample;
+ result = 4;
+ break;
+ case SPS_FORMAT_S16:
+ hyper_sample >>= (64 - 16);
+ *(int16_t *)op = (int16_t)hyper_sample;
+ result = 2;
+ break;
+ case SPS_FORMAT_S8:
+ hyper_sample >>= (int8_t)(64 - 8);
+ *op = hyper_sample;
+ result = 1;
+ break;
+ case SPS_FORMAT_U8:
+ hyper_sample >>= (uint8_t)(64 - 8);
+ hyper_sample += 128;
+ *op = hyper_sample;
+ result = 1;
+ break;
+ default:
+ result = 0; // stop a compiler warning
+ die("Unexpected SPS_FORMAT_UNKNOWN while outputting samples");
+ }
+ p += result;
+ 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 3ac2f1a..17024f5 100644
--- a/common.h
+++ b/common.h
@@ -337,6 +337,7 @@ char *get_version_string(); // mallocs a string space -- remember to free it aft
void sps_nanosleep(const time_t sec,
const long nanosec); // waits for this time, even through interruptions
-int usleep_uncancellable(useconds_t usec);
+int64_t generate_zero_frames(char *outp, size_t number_of_frames, enum sps_format_t format,
+ int with_dither, int64_t random_number_in);
#endif // _COMMON_H
diff --git a/player.c b/player.c
index 9259aaa..816ff40 100644
--- a/player.c
+++ b/player.c
@@ -1032,7 +1032,12 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
// this could happen if the dac delay mysteriously grows between samples,
// which could happen in a transition between having no interpolation and
// having interpolated buffer numbers.
- debug(2,
+
+ // this will happen benignly if standby is being prevented, because a
+ // thread in the alsa back end will be stuffing frames of silence in there
+ // to keep it busy
+
+ debug(3,
"frame size (fs) < 0 with max_dac_delay of %lld and dac_delay of %ld",
max_dac_delay, dac_delay);
fs = 0;
@@ -1062,7 +1067,11 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
if (silence == NULL)
debug(1, "Failed to allocate %d byte silence buffer.", fs);
else {
- memset(silence, 0, conn->output_bytes_per_frame * fs);
+
+ conn->previous_random_number = generate_zero_frames(
+ silence, fs, config.output_format, conn->enable_dither,
+ conn->previous_random_number);
+
// debug(1,"Frames to start: %llu, DAC delay %d, buffer: %d
// packets.",exact_frame_gap,dac_delay,seq_diff(conn->ab_read,
// conn->ab_write, conn->ab_read));
@@ -1106,7 +1115,9 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
debug(1, "Failed to allocate %d frame silence buffer.", fs);
else {
// debug(1, "No delay function -- outputting %d frames of silence.", fs);
- memset(silence, 0, conn->output_bytes_per_frame * fs);
+ conn->previous_random_number =
+ generate_zero_frames(silence, fs, config.output_format,
+ conn->enable_dither, conn->previous_random_number);
config.output->play(silence, fs);
free(silence);
}
@@ -1427,22 +1438,22 @@ void player_thread_cleanup_handler(void *arg) {
mdns_dacp_monitor_set_id(NULL); // say we're not interested in following that DACP id any more
#endif
- debug(2, "Cancelling timing, control and audio threads...");
- debug(2, "Cancel timing thread.");
+ debug(3, "Cancelling timing, control and audio threads...");
+ debug(3, "Cancel timing thread.");
pthread_cancel(conn->rtp_timing_thread);
- debug(2, "Join timing thread.");
+ debug(3, "Join timing thread.");
pthread_join(conn->rtp_timing_thread, NULL);
- debug(2, "Timing thread terminated.");
- debug(2, "Cancel control thread.");
+ debug(3, "Timing thread terminated.");
+ debug(3, "Cancel control thread.");
pthread_cancel(conn->rtp_control_thread);
- debug(2, "Join control thread.");
+ debug(3, "Join control thread.");
pthread_join(conn->rtp_control_thread, NULL);
- debug(2, "Control thread terminated.");
- debug(2, "Cancel audio thread.");
+ debug(3, "Control thread terminated.");
+ debug(3, "Cancel audio thread.");
pthread_cancel(conn->rtp_audio_thread);
- debug(2, "Join audio thread.");
+ debug(3, "Join audio thread.");
pthread_join(conn->rtp_audio_thread, NULL);
- debug(2, "Audio thread terminated.");
+ debug(3, "Audio thread terminated.");
if (conn->outbuf) {
free(conn->outbuf);
@@ -1486,8 +1497,6 @@ void *player_thread_func(void *arg) {
conn->latency = 88200;
}
- config.output->start(config.output_rate, config.output_format); // will need a corresponding stop
-
init_decoder((int32_t *)&conn->stream.fmtp,
conn); // this sets up incoming rate, bit depth, channels.
// No pthread cancellation point in here
@@ -1625,10 +1634,16 @@ void *player_thread_func(void *arg) {
debug(3, "Dithering will be enabled because the input bit depth is greater than the output bit "
"depth");
}
- if (conn->fix_volume != 0x10000) {
+ if (config.output->parameters == NULL) {
debug(3, "Dithering will be enabled because the output volume is being altered in software");
}
+ if ((config.output->parameters == NULL) || (conn->input_bit_depth > output_bit_depth) ||
+ (config.playback_mode == ST_mono))
+ conn->enable_dither = 1;
+
+ config.output->start(config.output_rate, config.output_format); // will need a corresponding stop
+
// we need an intermediate "transition" buffer
// if ((input_rate!=config.output_rate) || (input_bit_depth!=output_bit_depth)) {
@@ -1766,8 +1781,9 @@ void *player_thread_func(void *arg) {
} else {
// the player may change the contents of the buffer, so it has to be zeroed each time;
// might as well malloc and freee it locally
- memset(silence, 0, conn->output_bytes_per_frame * conn->max_frames_per_packet *
- conn->output_sample_ratio);
+ conn->previous_random_number = generate_zero_frames(
+ silence, conn->max_frames_per_packet * conn->output_sample_ratio,
+ config.output_format, conn->enable_dither, conn->previous_random_number);
config.output->play(silence, conn->max_frames_per_packet * conn->output_sample_ratio);
free(silence);
}
@@ -1787,16 +1803,19 @@ void *player_thread_func(void *arg) {
} else {
// the player may change the contents of the buffer, so it has to be zeroed each time;
// might as well malloc and freee it locally
- memset(silence, 0, conn->output_bytes_per_frame * conn->max_frames_per_packet *
- conn->output_sample_ratio);
+ conn->previous_random_number = generate_zero_frames(
+ silence, conn->max_frames_per_packet * conn->output_sample_ratio,
+ config.output_format, conn->enable_dither, conn->previous_random_number);
config.output->play(silence, conn->max_frames_per_packet * conn->output_sample_ratio);
free(silence);
}
} else {
- int enable_dither = 0;
- if ((conn->fix_volume != 0x10000) || (conn->input_bit_depth > output_bit_depth) ||
+
+ if ((config.output->parameters == NULL) || (conn->input_bit_depth > output_bit_depth) ||
(config.playback_mode == ST_mono))
- enable_dither = 1;
+ conn->enable_dither = 1;
+ else
+ conn->enable_dither = 0;
// here, let's transform the frame of data, if necessary
@@ -2069,7 +2088,11 @@ void *player_thread_func(void *arg) {
size_t silence_length_sized = silence_length;
char *long_silence = malloc(conn->output_bytes_per_frame * silence_length_sized);
if (long_silence) {
- memset(long_silence, 0, conn->output_bytes_per_frame * silence_length_sized);
+
+ conn->previous_random_number =
+ generate_zero_frames(long_silence, silence_length_sized, config.output_format,
+ conn->enable_dither, conn->previous_random_number);
+
debug(2, "Play a silence of %d frames.", silence_length_sized);
config.output->play(long_silence, silence_length_sized);
free(long_silence);
@@ -2193,14 +2216,14 @@ void *player_thread_func(void *arg) {
(config.packet_stuffing == ST_basic)) {
play_samples =
stuff_buffer_basic_32((int32_t *)conn->tbuf, inbuflength, config.output_format,
- conn->outbuf, amount_to_stuff, enable_dither, conn);
+ conn->outbuf, amount_to_stuff, conn->enable_dither, conn);
}
#ifdef CONFIG_SOXR
else if (config.packet_stuffing == ST_soxr) {
// if (amount_to_stuff) debug(1,"Soxr stuff...");
play_samples = stuff_buffer_soxr_32((int32_t *)conn->tbuf, (int32_t *)conn->sbuf,
inbuflength, config.output_format, conn->outbuf,
- amount_to_stuff, enable_dither, conn);
+ amount_to_stuff, conn->enable_dither, conn);
}
#endif
@@ -2256,7 +2279,7 @@ void *player_thread_func(void *arg) {
// synchronising
play_samples =
stuff_buffer_basic_32((int32_t *)conn->tbuf, inbuflength, config.output_format,
- conn->outbuf, 0, enable_dither, conn);
+ conn->outbuf, 0, conn->enable_dither, conn);
if (conn->outbuf == NULL)
debug(1, "NULL outbuf to play -- skipping it.");
else
@@ -2780,16 +2803,16 @@ int player_stop(rtsp_conn_info *conn) {
// debuglev = 3;
debug(3, "player_stop");
if (conn->player_thread) {
- debug(2, "player_thread cancel...");
+ debug(3, "player_thread cancel...");
pthread_cancel(*conn->player_thread);
- debug(2, "player_thread join...");
+ debug(3, "player_thread join...");
if (pthread_join(*conn->player_thread, NULL) == -1) {
char errorstring[1024];
strerror_r(errno, (char *)errorstring, sizeof(errorstring));
debug(1, "Connection %d: error %d joining player thread: \"%s\".", conn->connection_number,
errno, (char *)errorstring);
} else {
- debug(2, "player_thread joined.");
+ debug(3, "player_thread joined.");
}
free(conn->player_thread);
conn->player_thread = NULL;
diff --git a/player.h b/player.h
index 413aa8d..7dd1c18 100644
--- a/player.h
+++ b/player.h
@@ -246,6 +246,8 @@ typedef struct {
// zero
uint32_t dacp_active_remote; // key to send to the remote controller
void *dapo_private_storage; // this is used for compatibility, if dacp stuff isn't enabled.
+
+ int enable_dither; // needed for filling silences before play actually starts
} rtsp_conn_info;
uint32_t modulo_32_offset(uint32_t from, uint32_t to);
diff --git a/rtsp.c b/rtsp.c
index bdd686a..eb2eaea 100644
--- a/rtsp.c
+++ b/rtsp.c
@@ -265,7 +265,7 @@ int have_player(rtsp_conn_info *conn) {
void player_watchdog_thread_cleanup_handler(void *arg) {
rtsp_conn_info *conn = (rtsp_conn_info *)arg;
- debug(2, "Connection %d: Watchdog Exit.", conn->connection_number);
+ debug(3, "Connection %d: Watchdog Exit.", conn->connection_number);
}
void *player_watchdog_thread_code(void *arg) {
@@ -2127,22 +2127,22 @@ void rtsp_conversation_thread_cleanup_function(void *arg) {
if (rc)
debug(1, "Connection %d: error %d destroying flush_mutex.", conn->connection_number, rc);
- debug(2, "Cancel watchdog thread.");
+ debug(3, "Cancel watchdog thread.");
pthread_cancel(conn->player_watchdog_thread);
- debug(2, "Join watchdog thread.");
+ debug(3, "Join watchdog thread.");
pthread_join(conn->player_watchdog_thread, NULL);
- debug(2, "Delete watchdog mutex.");
+ debug(3, "Delete watchdog mutex.");
pthread_mutex_destroy(&conn->watchdog_mutex);
debug(3, "Connection %d: Checking play lock.", conn->connection_number);
debug_mutex_lock(&playing_conn_lock, 1000000, 3); // get it
if (playing_conn == conn) { // if it's ours
- debug(1, "Connection %d: Unlocking play lock -- end of play session.", conn->connection_number);
+ debug(3, "Connection %d: Unlocking play lock.", conn->connection_number);
playing_conn = NULL; // let it go
}
debug_mutex_unlock(&playing_conn_lock, 3);
- debug(2, "Connection %d: RTSP thread terminated.", conn->connection_number);
+ debug(2, "Connection %d: terminated.", conn->connection_number);
conn->running = 0;
pthread_setcancelstate(oldState, NULL);
}