Rebuild the hardware mute code -- clear up the logic. Needs to check if disconnected though.
This commit is contained in:
+284
-228
@@ -51,7 +51,6 @@ static int play(void *buf, int samples);
|
||||
static void stop(void);
|
||||
static void flush(void);
|
||||
int delay(long *the_delay);
|
||||
void do_mute(int request);
|
||||
int get_rate_information(uint64_t *elapsed_time, uint64_t *frames_played);
|
||||
void *alsa_buffer_monitor_thread_code(void *arg);
|
||||
|
||||
@@ -61,7 +60,7 @@ void do_volume(double vol);
|
||||
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;
|
||||
static int output_method_signalled = 0; // for reporting whether it's using mmap or not
|
||||
|
||||
audio_output audio_alsa = {
|
||||
.name = "alsa",
|
||||
@@ -75,25 +74,40 @@ audio_output audio_alsa = {
|
||||
.delay = &delay,
|
||||
.play = &play,
|
||||
.rate_info = &get_rate_information,
|
||||
.mute = NULL, // a function will be provided if it can, and is allowed to, do hardware mute
|
||||
.volume = NULL, // a function will be provided if it can do hardware volume
|
||||
.mute = NULL, // a function will be provided if it can, and is allowed to,
|
||||
// do hardware mute
|
||||
.volume = NULL, // a function will be provided if it can do hardware volume
|
||||
.parameters = NULL}; // a function will be provided if it can do hardware volume
|
||||
|
||||
static pthread_mutex_t alsa_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
pthread_t alsa_buffer_monitor_thread;
|
||||
|
||||
// for deciding when to activate mute
|
||||
// there are two sources of requests to mute -- the backend itself, e.g. when it
|
||||
// is flushing
|
||||
// and the player, e.g. when volume goes down to -144, i.e. mute.
|
||||
|
||||
// we may not be allowed to use hardware mute, so we must reflect that too.
|
||||
|
||||
int mute_requested_externally = 0;
|
||||
int mute_requested_internally = 0;
|
||||
|
||||
// for tracking how long the output device has stalled
|
||||
uint64_t stall_monitor_start_time; // zero if not initialised / not started / zeroed by flush
|
||||
long stall_monitor_frame_count; // set to delay at start of time, incremented by any writes
|
||||
uint64_t stall_monitor_error_threshold; // if the time is longer than this, it's an error
|
||||
uint64_t stall_monitor_start_time; // zero if not initialised / not started /
|
||||
// zeroed by flush
|
||||
long stall_monitor_frame_count; // set to delay at start of time, incremented by
|
||||
// any writes
|
||||
uint64_t stall_monitor_error_threshold; // if the time is longer than this, it's
|
||||
// an error
|
||||
|
||||
static snd_output_t *output = NULL;
|
||||
static unsigned int desired_sample_rate;
|
||||
static enum sps_format_t sample_format;
|
||||
int frame_size; // in bytes for interleaved stereo
|
||||
|
||||
int alsa_device_initialised; // boolean to ensure the initialisation is only done once
|
||||
int alsa_device_initialised; // boolean to ensure the initialisation is only
|
||||
// done once
|
||||
snd_pcm_t *alsa_handle = NULL;
|
||||
static snd_pcm_hw_params_t *alsa_params = NULL;
|
||||
static snd_pcm_sw_params_t *alsa_swparams = NULL;
|
||||
@@ -114,13 +128,13 @@ static int has_softvol = 0;
|
||||
|
||||
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.
|
||||
int overriding_mute_state_requested = 0; // 1 = mute; 0 = unmute requested
|
||||
int mixer_volume_setting_gives_mute =
|
||||
0; // set when it is discovered that particular mixer volume setting causes a mute.
|
||||
long alsa_mix_mute; // setting the volume to this value mutes output, if
|
||||
// mixer_volume_setting_gives_mute is true
|
||||
static int volume_set_request = 0; // set when an external request is made to set the volume.
|
||||
|
||||
int mixer_volume_setting_gives_mute = 0; // set when it is discovered that
|
||||
// particular mixer volume setting
|
||||
// causes a mute.
|
||||
long alsa_mix_mute; // setting the volume to this value mutes output, if
|
||||
// mixer_volume_setting_gives_mute is true
|
||||
int volume_based_mute_is_active =
|
||||
0; // set when muting is being done by a setting the volume to a magic value
|
||||
|
||||
@@ -223,8 +237,9 @@ 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));
|
||||
debug(1, "Error %d (\"%s\") freeing the output device hardware while "
|
||||
"closing it.",
|
||||
derr, snd_strerror(derr));
|
||||
|
||||
if ((derr = snd_pcm_close(alsa_handle)))
|
||||
debug(1, "Error %d (\"%s\") closing the output device.", derr, snd_strerror(derr));
|
||||
@@ -241,7 +256,8 @@ int actual_open_alsa_device(void) {
|
||||
/*
|
||||
const snd_pcm_uframes_t requested_buffer_headroom =
|
||||
minimal_buffer_headroom + 2048; // we ask for this much headroom in the
|
||||
// hardware buffer, but we'll accept less
|
||||
// hardware buffer, but we'll accept
|
||||
less
|
||||
*/
|
||||
|
||||
int ret, dir = 0;
|
||||
@@ -250,17 +266,20 @@ int actual_open_alsa_device(void) {
|
||||
snd_pcm_uframes_t actual_buffer_length;
|
||||
snd_pcm_access_t access;
|
||||
|
||||
// ensure no calls are made to the alsa device enquiring about the buffer length if
|
||||
// ensure no calls are made to the alsa device enquiring about the buffer
|
||||
// length if
|
||||
// synchronisation is disabled.
|
||||
if (config.no_sync != 0)
|
||||
audio_alsa.delay = NULL;
|
||||
|
||||
// ensure no calls are made to the alsa device enquiring about the buffer length if
|
||||
// ensure no calls are made to the alsa device enquiring about the buffer
|
||||
// length if
|
||||
// synchronisation is disabled.
|
||||
if (config.no_sync != 0)
|
||||
audio_alsa.delay = NULL;
|
||||
|
||||
// ret = snd_pcm_open(&alsa_handle, alsa_out_dev, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
|
||||
// ret = snd_pcm_open(&alsa_handle, alsa_out_dev, SND_PCM_STREAM_PLAYBACK,
|
||||
// SND_PCM_NONBLOCK);
|
||||
ret = snd_pcm_open(&alsa_handle, alsa_out_dev, SND_PCM_STREAM_PLAYBACK, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@@ -369,7 +388,8 @@ int actual_open_alsa_device(void) {
|
||||
snd_pcm_uframes_t actual_period_size;
|
||||
snd_pcm_hw_params_get_period_size(alsa_params, &actual_period_size, &dir);
|
||||
if (actual_period_size != period_size_requested)
|
||||
inform("Actual period size set to a different value than requested. Requested: %lu, actual "
|
||||
inform("Actual period size set to a different value than requested. "
|
||||
"Requested: %lu, actual "
|
||||
"setting: %lu",
|
||||
period_size_requested, actual_period_size);
|
||||
}
|
||||
@@ -386,7 +406,8 @@ int actual_open_alsa_device(void) {
|
||||
snd_pcm_uframes_t actual_buffer_size;
|
||||
snd_pcm_hw_params_get_buffer_size(alsa_params, &actual_buffer_size);
|
||||
if (actual_buffer_size != buffer_size_requested)
|
||||
inform("Actual period size set to a different value than requested. Requested: %lu, actual "
|
||||
inform("Actual period size set to a different value than requested. "
|
||||
"Requested: %lu, actual "
|
||||
"setting: %lu",
|
||||
buffer_size_requested, actual_buffer_size);
|
||||
}
|
||||
@@ -405,7 +426,8 @@ int actual_open_alsa_device(void) {
|
||||
snd_pcm_uframes_t actual_period_size;
|
||||
snd_pcm_hw_params_get_period_size(alsa_params, &actual_period_size, &dir);
|
||||
if (actual_period_size != period_size_requested)
|
||||
inform("Actual period size set to a different value than requested. Requested: %lu, actual "
|
||||
inform("Actual period size set to a different value than requested. "
|
||||
"Requested: %lu, actual "
|
||||
"setting: %lu",
|
||||
period_size_requested, actual_period_size);
|
||||
}
|
||||
@@ -414,7 +436,8 @@ int actual_open_alsa_device(void) {
|
||||
snd_pcm_uframes_t actual_buffer_size;
|
||||
snd_pcm_hw_params_get_buffer_size(alsa_params, &actual_buffer_size);
|
||||
if (actual_buffer_size != buffer_size_requested)
|
||||
inform("Actual period size set to a different value than requested. Requested: %lu, actual "
|
||||
inform("Actual period size set to a different value than requested. "
|
||||
"Requested: %lu, actual "
|
||||
"setting: %lu",
|
||||
buffer_size_requested, actual_buffer_size);
|
||||
}
|
||||
@@ -433,8 +456,9 @@ int actual_open_alsa_device(void) {
|
||||
|
||||
ret = snd_pcm_sw_params_current(alsa_handle, alsa_swparams);
|
||||
if (ret < 0) {
|
||||
warn("audio_alsa: Unable to get current sw parameters for device \"%s\": %s.", alsa_out_dev,
|
||||
snd_strerror(ret));
|
||||
warn("audio_alsa: Unable to get current sw parameters for device \"%s\": "
|
||||
"%s.",
|
||||
alsa_out_dev, snd_strerror(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -476,9 +500,9 @@ int actual_open_alsa_device(void) {
|
||||
buffer_size);
|
||||
}
|
||||
*/
|
||||
debug(1,
|
||||
"The alsa buffer is smaller (%lu bytes) than the desired backend buffer "
|
||||
"length (%ld) you have chosen.",
|
||||
debug(1, "The alsa buffer is smaller (%lu bytes) than the desired backend "
|
||||
"buffer "
|
||||
"length (%ld) you have chosen.",
|
||||
actual_buffer_length, config.audio_backend_buffer_desired_length);
|
||||
}
|
||||
|
||||
@@ -499,7 +523,10 @@ int actual_open_alsa_device(void) {
|
||||
|
||||
// ret = snd_pcm_hw_params_any(alsa_handle, alsa_params);
|
||||
// if (ret < 0) {
|
||||
// die("audio_alsa: Cannpot get configuration for device \"%s\": no
|
||||
// die("audio_alsa: Cannpot get configuration for
|
||||
// device
|
||||
//\"%s\":
|
||||
// no
|
||||
// configurations
|
||||
//"
|
||||
// "available",
|
||||
@@ -622,105 +649,116 @@ int open_alsa_device(void) {
|
||||
}
|
||||
|
||||
int do_alsa_device_init_if_needed() {
|
||||
debug(1,"do_alsa_device_init_if_needed()");
|
||||
debug(1, "do_alsa_device_init_if_needed()");
|
||||
int response = 0;
|
||||
// do any alsa device initialisation (general case) if needed
|
||||
// at present, this is only needed if a hardware mixer is being used
|
||||
// if there's a hardware mixer, it needs to be initialised before first use
|
||||
if (alsa_device_initialised == 0) {
|
||||
alsa_device_initialised = 1;
|
||||
if (hardware_mixer) {
|
||||
int oldState;
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
|
||||
alsa_device_initialised = 1;
|
||||
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;
|
||||
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) {
|
||||
// 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 = ¶meters; // 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 = ¶meters; // likewise the parameters stuff
|
||||
} else {
|
||||
debug(1, "Cannot get the dB range from the volume control \"%s\"", alsa_mix_ctrl);
|
||||
audio_alsa.volume = &volume; // insert the volume function now we
|
||||
// know it can do dB stuff
|
||||
audio_alsa.parameters = ¶meters; // 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, "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 = ¶meters; // 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
|
||||
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);
|
||||
|
||||
pthread_cleanup_pop(0);
|
||||
pthread_setcancelstate(oldState, NULL);
|
||||
}
|
||||
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 = ¶meters; // 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 = ¶meters; // 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);
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
@@ -766,7 +804,8 @@ static int init(int argc, char **argv) {
|
||||
// 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!
|
||||
// do the "general" audio options. Note, these options are in the "general"
|
||||
// stanza!
|
||||
parse_general_audio_options();
|
||||
|
||||
if (config.cfg != NULL) {
|
||||
@@ -803,7 +842,8 @@ static int init(int argc, char **argv) {
|
||||
else if (strcasecmp(str, "yes") == 0)
|
||||
config.no_sync = 1;
|
||||
else {
|
||||
warn("Invalid disable_synchronization option choice \"%s\". It should be \"yes\" or "
|
||||
warn("Invalid disable_synchronization option choice \"%s\". It should "
|
||||
"be \"yes\" or "
|
||||
"\"no\". It is set to \"no\".");
|
||||
config.no_sync = 0;
|
||||
}
|
||||
@@ -818,7 +858,8 @@ static int init(int argc, char **argv) {
|
||||
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 "
|
||||
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;
|
||||
}
|
||||
@@ -831,7 +872,8 @@ static int init(int argc, char **argv) {
|
||||
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 "
|
||||
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;
|
||||
}
|
||||
@@ -854,7 +896,8 @@ static int init(int argc, char **argv) {
|
||||
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\", "
|
||||
warn("Invalid output format \"%s\". It should be \"U8\", \"S8\", "
|
||||
"\"S16\", \"S24\", "
|
||||
"\"S24_3LE\", \"S24_3BE\" or "
|
||||
"\"S32\". It is set to \"S16\".",
|
||||
str);
|
||||
@@ -873,7 +916,8 @@ static int init(int argc, char **argv) {
|
||||
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 "
|
||||
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;
|
||||
@@ -887,7 +931,8 @@ static int init(int argc, char **argv) {
|
||||
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\". "
|
||||
warn("Invalid use_mmap_if_available option choice \"%s\". It should be "
|
||||
"\"yes\" or \"no\". "
|
||||
"It is set to \"yes\".");
|
||||
config.no_mmap = 0;
|
||||
}
|
||||
@@ -977,10 +1022,11 @@ static int init(int argc, char **argv) {
|
||||
}
|
||||
|
||||
debug(1, "alsa: output device name is \"%s\".", alsa_out_dev);
|
||||
|
||||
|
||||
alsa_mix_handle = NULL; // indicates alsa handle isn't open
|
||||
|
||||
// so, now, if the option to keep the DAC running has been selected, start a thread to monitor the
|
||||
// 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
|
||||
|
||||
@@ -1005,8 +1051,39 @@ static void deinit(void) {
|
||||
pthread_setcancelstate(oldState, NULL);
|
||||
}
|
||||
|
||||
int set_mute_state() {
|
||||
debug(1, "set_mute_state");
|
||||
int response = 1; // some problem expected, e.g. no mixer or not allowed to use it
|
||||
if (config.alsa_use_hardware_mute == 1) {
|
||||
int mute = 0;
|
||||
if ((mute_requested_externally != 0) || (mute_requested_internally != 0))
|
||||
mute = 1;
|
||||
if (open_mixer() == 1) {
|
||||
response = 0; // okay if actually using the mute facility
|
||||
if (mute == 1) {
|
||||
debug(1, "Hardware mute switched on");
|
||||
if (snd_mixer_selem_has_playback_switch(alsa_mix_elem))
|
||||
snd_mixer_selem_set_playback_switch_all(alsa_mix_elem, 0);
|
||||
else {
|
||||
volume_based_mute_is_active = 1;
|
||||
do_snd_mixer_selem_set_playback_dB_all(alsa_mix_elem, alsa_mix_mute);
|
||||
}
|
||||
} else {
|
||||
debug(1, "Hardware mute switched off");
|
||||
if (snd_mixer_selem_has_playback_switch(alsa_mix_elem))
|
||||
snd_mixer_selem_set_playback_switch_all(alsa_mix_elem, 1);
|
||||
else {
|
||||
volume_based_mute_is_active = 0;
|
||||
do_snd_mixer_selem_set_playback_dB_all(alsa_mix_elem, set_volume);
|
||||
}
|
||||
}
|
||||
close_mixer();
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
static void start(int i_sample_rate, int i_sample_format) {
|
||||
debug(1,"audio_alsa start called.");
|
||||
debug(1, "audio_alsa start called.");
|
||||
if (i_sample_rate == 0)
|
||||
desired_sample_rate = 44100; // default
|
||||
else
|
||||
@@ -1023,7 +1100,7 @@ static void start(int i_sample_rate, int i_sample_format) {
|
||||
stall_monitor_start_time = 0;
|
||||
stall_monitor_frame_count = 0;
|
||||
if (alsa_device_initialised == 0) {
|
||||
debug(1,"alsa: start() calling do_alsa_device_init_if_needed.");
|
||||
debug(1, "alsa: start() calling do_alsa_device_init_if_needed.");
|
||||
do_alsa_device_init_if_needed();
|
||||
}
|
||||
}
|
||||
@@ -1065,10 +1142,9 @@ int delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay) {
|
||||
|
||||
if (((update_timestamp_ns - stall_monitor_start_time) > stall_monitor_error_threshold) ||
|
||||
((time_now_ns - stall_monitor_start_time) > stall_monitor_error_threshold)) {
|
||||
debug(2,
|
||||
"DAC seems to have stalled with time_now_ns: %" PRIX64
|
||||
", update_timestamp_ns: %" PRIX64 ", stall_monitor_start_time %" PRIX64
|
||||
", stall_monitor_error_threshold %" PRIX64 ".",
|
||||
debug(2, "DAC seems to have stalled with time_now_ns: %" PRIX64
|
||||
", update_timestamp_ns: %" PRIX64 ", stall_monitor_start_time %" PRIX64
|
||||
", stall_monitor_error_threshold %" PRIX64 ".",
|
||||
time_now_ns, update_timestamp_ns, stall_monitor_start_time,
|
||||
stall_monitor_error_threshold);
|
||||
ret = sps_extra_code_output_stalled;
|
||||
@@ -1088,12 +1164,14 @@ int delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay) {
|
||||
|
||||
*delay = *delay - frames_played_since_last_interrupt_sized;
|
||||
}
|
||||
} else { // not running, thus no delay information, thus can't check for stall
|
||||
} else { // not running, thus no delay information, thus can't check for
|
||||
// stall
|
||||
*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
|
||||
|
||||
// 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;
|
||||
}
|
||||
@@ -1104,13 +1182,15 @@ int delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay) {
|
||||
}
|
||||
|
||||
int delay(long *the_delay) {
|
||||
// returns 0 if the device is in a valid state -- SND_PCM_STATE_RUNNING or SND_PCM_STATE_PREPARED
|
||||
// returns 0 if the device is in a valid state -- SND_PCM_STATE_RUNNING or
|
||||
// SND_PCM_STATE_PREPARED
|
||||
// or SND_PCM_STATE_DRAINING
|
||||
// and returns the actual delay if running or 0 if prepared in *the_delay
|
||||
|
||||
// otherwise return an error code
|
||||
// the error code could be a Unix errno code or a snderror code, or
|
||||
// the sps_extra_code_output_stalled or the sps_extra_code_output_state_cannot_make_ready codes
|
||||
// the sps_extra_code_output_stalled or the
|
||||
// sps_extra_code_output_state_cannot_make_ready codes
|
||||
int ret = 0;
|
||||
*the_delay = 0;
|
||||
if (alsa_handle == NULL)
|
||||
@@ -1212,7 +1292,8 @@ int do_play(void *buf, int samples) {
|
||||
while ((ret = snd_pcm_resume(alsa_handle)) == -EAGAIN) {
|
||||
sleep(1); /* wait until the suspend flag is released */
|
||||
if (ret < 0) {
|
||||
warn("alsa: can't recover from SND_PCM_STATE_SUSPENDED state, snd_pcm_prepare() "
|
||||
warn("alsa: can't recover from SND_PCM_STATE_SUSPENDED state, "
|
||||
"snd_pcm_prepare() "
|
||||
"failed: %s.",
|
||||
snd_strerror(ret));
|
||||
}
|
||||
@@ -1221,8 +1302,9 @@ int do_play(void *buf, int samples) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
debug(1, "alsa: device status returns fault status %d and SND_PCM_STATE_* %d for play.", ret,
|
||||
state);
|
||||
debug(1, "alsa: device status returns fault status %d and SND_PCM_STATE_* "
|
||||
"%d for play.",
|
||||
ret, state);
|
||||
frame_index = 0;
|
||||
measurement_data_is_valid = 0;
|
||||
}
|
||||
@@ -1234,15 +1316,21 @@ int do_play(void *buf, int samples) {
|
||||
int do_open() {
|
||||
int ret = 0;
|
||||
if (alsa_backend_state != abm_disconnected)
|
||||
debug(1, "alsa: do_open() -- opening the output device when it is already connected");
|
||||
debug(1, "alsa: do_open() -- opening the output device when it is already "
|
||||
"connected");
|
||||
if (alsa_handle == NULL) {
|
||||
// debug(1,"alsa: do_open() -- opening the output device");
|
||||
ret = open_alsa_device();
|
||||
if (ret == 0) {
|
||||
mute_requested_internally = 0;
|
||||
if (audio_alsa.volume)
|
||||
do_volume(set_volume);
|
||||
if (audio_alsa.mute)
|
||||
do_mute(0);
|
||||
if (audio_alsa.mute) {
|
||||
debug(1, "do_open() set_mute_state");
|
||||
set_mute_state(); // the mute_requested_externally flag will have been
|
||||
// set accordingly
|
||||
// do_mute(0); // complete unmute
|
||||
}
|
||||
alsa_backend_state = abm_connected; // only do this if it really opened it.
|
||||
}
|
||||
} else {
|
||||
@@ -1253,7 +1341,8 @@ int do_open() {
|
||||
|
||||
int do_close() {
|
||||
if (alsa_backend_state == abm_disconnected)
|
||||
debug(1, "alsa: do_close() -- closing the output device when it is already disconnected");
|
||||
debug(1, "alsa: do_close() -- closing the output device when it is already "
|
||||
"disconnected");
|
||||
int derr = 0;
|
||||
if (alsa_handle) {
|
||||
// debug(1,"alsa: do_close() -- closing the output device");
|
||||
@@ -1277,7 +1366,8 @@ int do_close() {
|
||||
int play(void *buf, int samples) {
|
||||
|
||||
// play() will change the state of the alsa_backend_mode to abm_playing
|
||||
// also, if the present alsa_backend_state is abm_disconnected, then first the DAC must be
|
||||
// also, if the present alsa_backend_state is abm_disconnected, then first the
|
||||
// DAC must be
|
||||
// connected
|
||||
|
||||
// debug(3,"audio_alsa play called.");
|
||||
@@ -1289,13 +1379,17 @@ int play(void *buf, int samples) {
|
||||
ret = do_open();
|
||||
if (ret == 0)
|
||||
debug(2, "alsa: play() -- opened output device");
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
if (alsa_backend_state != abm_playing) {
|
||||
debug(2, "alsa: play() -- alsa_backend_state => abm_playing");
|
||||
alsa_backend_state = abm_playing;
|
||||
do_mute(0);
|
||||
mute_requested_internally = 0; // stop requesting a mute for backend's own
|
||||
// reasons, which might have been a flush
|
||||
debug(1, "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);
|
||||
}
|
||||
@@ -1308,7 +1402,10 @@ int play(void *buf, int samples) {
|
||||
static void flush(void) {
|
||||
// debug(2,"audio_alsa flush called.");
|
||||
pthread_cleanup_debug_mutex_lock(&alsa_mutex, 10000, 1);
|
||||
do_mute(1);
|
||||
mute_requested_internally = 1; // request a mute for backend's reasons
|
||||
debug(1, "flush() set_mute_state");
|
||||
set_mute_state();
|
||||
// do_mute(1); // mute for backend's own reasons
|
||||
if (alsa_backend_state != abm_disconnected) { // must be playing or connected...
|
||||
if (config.keep_dac_busy != 0) {
|
||||
debug(2, "alsa: flush() -- alsa_backend_state => abm_connected.");
|
||||
@@ -1334,7 +1431,8 @@ static void parameters(audio_parameters *info) {
|
||||
info->maximum_volume_dB = alsa_mix_maxdb;
|
||||
}
|
||||
|
||||
void do_volume(double vol) { // caller is assumed to have the alsa_mutex when using this function
|
||||
void do_volume(double vol) { // caller is assumed to have the alsa_mutex when
|
||||
// using this function
|
||||
debug(3, "Setting volume db to %f.", vol);
|
||||
int oldState;
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
|
||||
@@ -1386,7 +1484,8 @@ static void linear_volume(double vol) {
|
||||
if (hardware_mixer && alsa_mix_handle) {
|
||||
double linear_volume = pow(10, vol);
|
||||
// debug(1,"Linear volume is %f.",linear_volume);
|
||||
long int_vol = alsa_mix_minv + (alsa_mix_maxv - alsa_mix_minv) * linear_volume;
|
||||
long int_vol = alsa_mix_minv + (alsa_mix_maxv - alsa_mix_minv) *
|
||||
linear_volume;
|
||||
// debug(1,"Setting volume to %ld, for volume input of %f.",int_vol,vol);
|
||||
if (alsa_mix_handle) {
|
||||
if (snd_mixer_selem_set_playback_volume_all(alsa_mix_elem, int_vol) != 0)
|
||||
@@ -1397,71 +1496,17 @@ static void linear_volume(double vol) {
|
||||
}
|
||||
*/
|
||||
|
||||
int mute(int mute_state_requested) {
|
||||
debug(1,"alsa: mute(%d)",mute_state_requested);
|
||||
int response = 0;
|
||||
if (config.alsa_use_hardware_mute == 1)
|
||||
response = 1; // return true if actually using the mute facility
|
||||
// debug(1,"External Mute Request: %d",mute_state_requested);
|
||||
pthread_cleanup_debug_mutex_lock(&alsa_mutex, 10000, 1);
|
||||
mute_request_pending = 1;
|
||||
overriding_mute_state_requested = mute_state_requested;
|
||||
do_mute(mute_state_requested);
|
||||
debug_mutex_unlock(&alsa_mutex, 3);
|
||||
pthread_cleanup_pop(0); // release the mutex
|
||||
return response;
|
||||
int mute(int mute_state_requested) { // these would be for external reasons, not
|
||||
// because of the
|
||||
// state of the backend.
|
||||
debug(1, "alsa: mute(%d)", mute_state_requested);
|
||||
mute_requested_externally = mute_state_requested; // request a mute for external reasons
|
||||
debug(1, "mute(%d) set_mute_state", mute_state_requested);
|
||||
return set_mute_state();
|
||||
}
|
||||
|
||||
void do_mute(int mute_state_requested) {
|
||||
debug(1, "alsa: do_mute(%d).", mute_state_requested);
|
||||
int oldState;
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
|
||||
|
||||
// if a mute is requested now, then
|
||||
// if an external mute request is in place, leave everything muted
|
||||
// otherwise, if an external mute request is pending, action it
|
||||
// otherwise, action the do_mute request
|
||||
|
||||
int local_mute_state_requested =
|
||||
overriding_mute_state_requested; // go with whatever was asked by the external "mute" call
|
||||
|
||||
// The mute state requested will be actioned unless mute_request_pending is set
|
||||
// If it is set, then that the pending request will be actioned.
|
||||
// If the hardware isn't there, or we are not allowed to use it, nothing will be done
|
||||
// The caller must have the alsa mutex
|
||||
|
||||
if (config.alsa_use_hardware_mute == 1) {
|
||||
if (mute_request_pending == 0)
|
||||
local_mute_state_requested = mute_state_requested;
|
||||
if (open_mixer() == 1) {
|
||||
if (local_mute_state_requested) {
|
||||
// debug(1,"Playback Switch mute actually done");
|
||||
if (snd_mixer_selem_has_playback_switch(alsa_mix_elem))
|
||||
snd_mixer_selem_set_playback_switch_all(alsa_mix_elem, 0);
|
||||
else {
|
||||
// debug(1,"Activating volume-based mute.");
|
||||
volume_based_mute_is_active = 1;
|
||||
do_snd_mixer_selem_set_playback_dB_all(alsa_mix_elem, alsa_mix_mute);
|
||||
}
|
||||
} else if (overriding_mute_state_requested == 0) {
|
||||
// debug(1,"Playback Switch unmute actually done");
|
||||
if (snd_mixer_selem_has_playback_switch(alsa_mix_elem))
|
||||
snd_mixer_selem_set_playback_switch_all(alsa_mix_elem, 1);
|
||||
else {
|
||||
// debug(1,"Deactivating volume-based mute.");
|
||||
volume_based_mute_is_active = 0;
|
||||
do_snd_mixer_selem_set_playback_dB_all(alsa_mix_elem, set_volume);
|
||||
}
|
||||
}
|
||||
close_mixer();
|
||||
}
|
||||
}
|
||||
mute_request_pending = 0;
|
||||
pthread_setcancelstate(oldState, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
void alsa_buffer_monitor_thread_cleanup_function(__attribute__((unused)) void *arg) {
|
||||
void alsa_buffer_monitor_thread_cleanup_function(__attribute__((unused)) void
|
||||
*arg) {
|
||||
debug(1, "alsa: alsa_buffer_monitor_thread_cleanup_function called.");
|
||||
}
|
||||
*/
|
||||
@@ -1469,7 +1514,8 @@ void alsa_buffer_monitor_thread_cleanup_function(__attribute__((unused)) void *a
|
||||
void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) {
|
||||
while (1) {
|
||||
if ((config.keep_dac_busy != 0) && (alsa_device_initialised == 0)) {
|
||||
debug(1,"alsa: alsa_buffer_monitor_thread_code() calling do_alsa_device_init_if_needed.");
|
||||
debug(1, "alsa: alsa_buffer_monitor_thread_code() calling "
|
||||
"do_alsa_device_init_if_needed.");
|
||||
do_alsa_device_init_if_needed();
|
||||
}
|
||||
int sleep_time_ms = (int)(config.audio_backend_silence_scan_interval * 1000);
|
||||
@@ -1478,18 +1524,23 @@ void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) {
|
||||
if ((alsa_backend_state == abm_disconnected) && (config.keep_dac_busy != 0)) {
|
||||
// open the dac and move to abm_connected mode
|
||||
if (do_open() == 0)
|
||||
debug(2, "alsa: alsa_buffer_monitor_thread_code() -- output device opened; alsa_backend_state => abm_connected");
|
||||
debug(2, "alsa: alsa_buffer_monitor_thread_code() -- output device opened; "
|
||||
"alsa_backend_state => abm_connected");
|
||||
} else if ((alsa_backend_state == abm_connected) && (config.keep_dac_busy == 0)) {
|
||||
stall_monitor_start_time = 0;
|
||||
frame_index = 0;
|
||||
measurement_data_is_valid = 0;
|
||||
debug(2, "alsa: alsa_buffer_monitor_thread_code() -- closing the output device");
|
||||
debug(2, "alsa: alsa_buffer_monitor_thread_code() -- closing the output "
|
||||
"device");
|
||||
do_close();
|
||||
debug(2, "alsa: alsa_buffer_monitor_thread_code() -- alsa_backend_state => abm_disconnected");
|
||||
debug(2, "alsa: alsa_buffer_monitor_thread_code() -- alsa_backend_state "
|
||||
"=> abm_disconnected");
|
||||
}
|
||||
// now, if the backend is not in the abm_disconnected state
|
||||
// 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 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
|
||||
if ((alsa_backend_state != abm_disconnected) && (config.keep_dac_busy != 0)) {
|
||||
int reply;
|
||||
long buffer_size = 0;
|
||||
@@ -1511,15 +1562,18 @@ void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) {
|
||||
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 = (desired_sample_rate *
|
||||
// form.",sleep_time_ms,sleep_time_in_fp); int frames_of_silence =
|
||||
// (desired_sample_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.",
|
||||
// debug(1, "alsa: alsa_buffer_monitor_thread_code -- silence buffer
|
||||
// length: %u bytes.",
|
||||
// size_of_silence_buffer);
|
||||
void *silence = malloc(size_of_silence_buffer);
|
||||
if (silence == NULL) {
|
||||
debug(1, "alsa: alsa_buffer_monitor_thread_code -- failed to allocate memory for a "
|
||||
debug(1, "alsa: alsa_buffer_monitor_thread_code -- failed to "
|
||||
"allocate memory for a "
|
||||
"silent frame buffer.");
|
||||
} else {
|
||||
pthread_cleanup_push(malloc_cleanup, silence);
|
||||
@@ -1527,10 +1581,12 @@ void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) {
|
||||
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 ".",
|
||||
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);
|
||||
|
||||
@@ -285,18 +285,16 @@ static int alac_decode(short *dest, int *destlen, uint8_t *buf, int len, rtsp_co
|
||||
}
|
||||
|
||||
if (outsize > toutsize) {
|
||||
debug(2,
|
||||
"Output from alac_decode larger (%d bytes, not frames) than expected (%d bytes) -- "
|
||||
"truncated, but buffer overflow possible! Encrypted = %d.",
|
||||
debug(2, "Output from alac_decode larger (%d bytes, not frames) than expected (%d bytes) -- "
|
||||
"truncated, but buffer overflow possible! Encrypted = %d.",
|
||||
outsize, toutsize, conn->stream.encrypted);
|
||||
reply = -1; // output packet is the wrong size
|
||||
}
|
||||
|
||||
*destlen = outsize / conn->input_bytes_per_frame;
|
||||
if ((outsize % conn->input_bytes_per_frame) != 0)
|
||||
debug(1,
|
||||
"Number of audio frames (%d) does not correspond exactly to the number of bytes (%d) "
|
||||
"and the audio frame size (%d).",
|
||||
debug(1, "Number of audio frames (%d) does not correspond exactly to the number of bytes (%d) "
|
||||
"and the audio frame size (%d).",
|
||||
*destlen, outsize, conn->input_bytes_per_frame);
|
||||
return reply;
|
||||
}
|
||||
@@ -456,10 +454,9 @@ void player_put_packet(seq_t seqno, uint32_t actual_timestamp, uint8_t *data, in
|
||||
if ((conn->flush_rtp_timestamp != 0) && (actual_timestamp != conn->flush_rtp_timestamp) &&
|
||||
(modulo_32_offset(actual_timestamp, conn->flush_rtp_timestamp) <
|
||||
conn->input_rate * 10)) { // if it's less than 10 seconds
|
||||
debug(3,
|
||||
"Dropping flushed packet in player_put_packet, seqno %u, timestamp %" PRIu32
|
||||
", flushing to "
|
||||
"timestamp: %" PRIu32 ".",
|
||||
debug(3, "Dropping flushed packet in player_put_packet, seqno %u, timestamp %" PRIu32
|
||||
", flushing to "
|
||||
"timestamp: %" PRIu32 ".",
|
||||
seqno, actual_timestamp, conn->flush_rtp_timestamp);
|
||||
conn->initial_reference_time = 0;
|
||||
conn->initial_reference_timestamp = 0;
|
||||
@@ -852,10 +849,9 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
|
||||
(curframe->given_timestamp != conn->flush_rtp_timestamp) &&
|
||||
(modulo_32_offset(curframe->given_timestamp, conn->flush_rtp_timestamp) <
|
||||
conn->input_rate * 10)) { // if it's less than ten seconds
|
||||
debug(3,
|
||||
"Dropping flushed packet in buffer_get_frame, seqno %u, timestamp %" PRIu32
|
||||
", flushing to "
|
||||
"timestamp: %" PRIu32 ".",
|
||||
debug(3, "Dropping flushed packet in buffer_get_frame, seqno %u, timestamp %" PRIu32
|
||||
", flushing to "
|
||||
"timestamp: %" PRIu32 ".",
|
||||
curframe->sequence_number, curframe->given_timestamp, conn->flush_rtp_timestamp);
|
||||
curframe->ready = 0;
|
||||
curframe->resend_level = 0;
|
||||
@@ -938,7 +934,8 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
|
||||
frame_to_local_time(conn->first_packet_timestamp + conn->latency +
|
||||
(uint32_t)(config.audio_backend_latency_offset *
|
||||
conn->input_rate), // this will go modulo 2^32
|
||||
&should_be_time, conn);
|
||||
&should_be_time,
|
||||
conn);
|
||||
|
||||
conn->first_packet_time_to_play = should_be_time;
|
||||
|
||||
@@ -959,7 +956,8 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
|
||||
frame_to_local_time(conn->first_packet_timestamp + conn->latency +
|
||||
(uint32_t)(config.audio_backend_latency_offset *
|
||||
conn->input_rate), // this should go modulo 2^32
|
||||
&should_be_time, conn);
|
||||
&should_be_time,
|
||||
conn);
|
||||
|
||||
conn->first_packet_time_to_play = should_be_time;
|
||||
|
||||
@@ -1030,9 +1028,8 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
|
||||
// this might happen if a big clock adjustment was made at just the wrong
|
||||
// time.
|
||||
|
||||
debug(1,
|
||||
"Run a bit past the exact start time by %" PRId64
|
||||
" frames with a DAC delay of %ld frames.",
|
||||
debug(1, "Run a bit past the exact start time by %" PRId64
|
||||
" frames with a DAC delay of %ld frames.",
|
||||
-exact_frame_gap, dac_delay);
|
||||
if (config.output->flush)
|
||||
config.output->flush();
|
||||
@@ -1190,7 +1187,8 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
|
||||
(uint32_t)(config.audio_backend_latency_offset * conn->input_rate) -
|
||||
(uint32_t)(config.audio_backend_buffer_desired_length *
|
||||
conn->input_rate), // this will go modulo 2^32
|
||||
&time_to_play, conn);
|
||||
&time_to_play,
|
||||
conn);
|
||||
|
||||
if (local_time_now >= time_to_play) {
|
||||
do_wait = 0;
|
||||
@@ -1670,17 +1668,17 @@ void *player_thread_func(void *arg) {
|
||||
// if ((input_rate!=config.output_rate) || (input_bit_depth!=output_bit_depth)) {
|
||||
// debug(1,"Define tbuf of length
|
||||
// %d.",output_bytes_per_frame*(max_frames_per_packet*output_sample_ratio+max_frame_size_change));
|
||||
conn->tbuf = malloc(
|
||||
sizeof(int32_t) * 2 *
|
||||
(conn->max_frames_per_packet * conn->output_sample_ratio + conn->max_frame_size_change));
|
||||
conn->tbuf =
|
||||
malloc(sizeof(int32_t) * 2 * (conn->max_frames_per_packet * conn->output_sample_ratio +
|
||||
conn->max_frame_size_change));
|
||||
if (conn->tbuf == NULL)
|
||||
die("Failed to allocate memory for the transition buffer.");
|
||||
|
||||
// initialise this, because soxr stuffing might be chosen later
|
||||
|
||||
conn->sbuf = malloc(
|
||||
sizeof(int32_t) * 2 *
|
||||
(conn->max_frames_per_packet * conn->output_sample_ratio + conn->max_frame_size_change));
|
||||
conn->sbuf =
|
||||
malloc(sizeof(int32_t) * 2 * (conn->max_frames_per_packet * conn->output_sample_ratio +
|
||||
conn->max_frame_size_change));
|
||||
if (conn->sbuf == NULL)
|
||||
die("Failed to allocate memory for the sbuf buffer.");
|
||||
|
||||
@@ -1968,9 +1966,8 @@ void *player_thread_func(void *arg) {
|
||||
SUCCESSOR(conn->last_seqno_read); // int32_t from seq_t, i.e. uint16_t, so okay.
|
||||
if (inframe->sequence_number !=
|
||||
conn->last_seqno_read) { // seq_t, ei.e. uint16_t and int32_t, so okay
|
||||
debug(2,
|
||||
"Player: packets out of sequence: expected: %u, got: %u, with ab_read: %u "
|
||||
"and ab_write: %u.",
|
||||
debug(2, "Player: packets out of sequence: expected: %u, got: %u, with ab_read: %u "
|
||||
"and ab_write: %u.",
|
||||
conn->last_seqno_read, inframe->sequence_number, conn->ab_read, conn->ab_write);
|
||||
conn->last_seqno_read = inframe->sequence_number; // reset warning...
|
||||
}
|
||||
@@ -2195,7 +2192,7 @@ void *player_thread_func(void *arg) {
|
||||
#ifdef CONFIG_CONVOLUTION
|
||||
|| config.convolution
|
||||
#endif
|
||||
) {
|
||||
) {
|
||||
int32_t *tbuf32 = (int32_t *)conn->tbuf;
|
||||
float fbuf_l[inbuflength];
|
||||
float fbuf_r[inbuflength];
|
||||
@@ -2279,9 +2276,8 @@ void *player_thread_func(void *arg) {
|
||||
debug(1, "play_samples==0 skipping it (1).");
|
||||
else {
|
||||
if (conn->software_mute_enabled) {
|
||||
generate_zero_frames(
|
||||
conn->outbuf, play_samples,
|
||||
config.output_format, conn->enable_dither, conn->previous_random_number);
|
||||
generate_zero_frames(conn->outbuf, play_samples, config.output_format,
|
||||
conn->enable_dither, conn->previous_random_number);
|
||||
}
|
||||
config.output->play(conn->outbuf, play_samples);
|
||||
}
|
||||
@@ -2320,9 +2316,8 @@ void *player_thread_func(void *arg) {
|
||||
debug(1, "NULL outbuf to play -- skipping it.");
|
||||
else {
|
||||
if (conn->software_mute_enabled) {
|
||||
generate_zero_frames(
|
||||
conn->outbuf, play_samples,
|
||||
config.output_format, conn->enable_dither, conn->previous_random_number);
|
||||
generate_zero_frames(conn->outbuf, play_samples, config.output_format,
|
||||
conn->enable_dither, conn->previous_random_number);
|
||||
}
|
||||
config.output->play(conn->outbuf, play_samples); // remove the (short*)!
|
||||
}
|
||||
@@ -2445,41 +2440,42 @@ void *player_thread_func(void *arg) {
|
||||
|
||||
if ((config.output->delay)) {
|
||||
if (config.no_sync == 0) {
|
||||
inform(
|
||||
"%*.2f," /* Sync error in milliseconds */
|
||||
"%*.1f," /* net correction in ppm */
|
||||
"%*.1f," /* corrections in ppm */
|
||||
"%*d," /* total packets */
|
||||
"%*" PRIu64 "," /* missing packets */
|
||||
"%*" PRIu64 "," /* late packets */
|
||||
"%*" PRIu64 "," /* too late packets */
|
||||
"%*" PRIu64 "," /* resend requests */
|
||||
"%*" PRId64 "," /* min DAC queue size */
|
||||
"%*" PRId32 "," /* min buffer occupancy */
|
||||
"%*" PRId32 "," /* max buffer occupancy */
|
||||
"%*.2f," /* source nominal frame rate */
|
||||
"%*.2f," /* source actual (average) frame rate */
|
||||
"%*.2f," /* output frame rate */
|
||||
"%*.2f," /* source clock drift */
|
||||
"%*d," /* source clock drift sample count */
|
||||
"%*.2f", /* rough calculated correction in ppm */
|
||||
10, 1000 * moving_average_sync_error / config.output_rate, 10,
|
||||
moving_average_correction * 1000000 / (352 * conn->output_sample_ratio), 10,
|
||||
moving_average_insertions_plus_deletions * 1000000 /
|
||||
(352 * conn->output_sample_ratio),
|
||||
12, play_number, 7, conn->missing_packets, 7, conn->late_packets, 7,
|
||||
conn->too_late_packets, 7, conn->resend_requests, 7, minimum_dac_queue_size,
|
||||
5, minimum_buffer_occupancy, 5, maximum_buffer_occupancy, 11,
|
||||
conn->remote_frame_rate, 11, conn->input_frame_rate, 11, conn->frame_rate, 10,
|
||||
(conn->local_to_remote_time_gradient - 1.0) * 1000000, 6,
|
||||
conn->local_to_remote_time_gradient_sample_count, 10,
|
||||
(conn->frame_rate > 0.0)
|
||||
? ((conn->frame_rate - conn->remote_frame_rate *
|
||||
conn->output_sample_ratio *
|
||||
conn->local_to_remote_time_gradient) *
|
||||
1000000) /
|
||||
conn->frame_rate
|
||||
: 0.0);
|
||||
inform("%*.2f," /* Sync error in milliseconds */
|
||||
"%*.1f," /* net correction in ppm */
|
||||
"%*.1f," /* corrections in ppm */
|
||||
"%*d," /* total packets */
|
||||
"%*" PRIu64 "," /* missing packets */
|
||||
"%*" PRIu64 "," /* late packets */
|
||||
"%*" PRIu64 "," /* too late packets */
|
||||
"%*" PRIu64 "," /* resend requests */
|
||||
"%*" PRId64 "," /* min DAC queue size */
|
||||
"%*" PRId32 "," /* min buffer occupancy */
|
||||
"%*" PRId32 "," /* max buffer occupancy */
|
||||
"%*.2f," /* source nominal frame rate */
|
||||
"%*.2f," /* source actual (average) frame rate */
|
||||
"%*.2f," /* output frame rate */
|
||||
"%*.2f," /* source clock drift */
|
||||
"%*d," /* source clock drift sample count */
|
||||
"%*.2f", /* rough calculated correction in ppm */
|
||||
10,
|
||||
1000 * moving_average_sync_error / config.output_rate, 10,
|
||||
moving_average_correction * 1000000 / (352 * conn->output_sample_ratio),
|
||||
10, moving_average_insertions_plus_deletions * 1000000 /
|
||||
(352 * conn->output_sample_ratio),
|
||||
12, play_number, 7, conn->missing_packets, 7, conn->late_packets, 7,
|
||||
conn->too_late_packets, 7, conn->resend_requests, 7,
|
||||
minimum_dac_queue_size, 5, minimum_buffer_occupancy, 5,
|
||||
maximum_buffer_occupancy, 11, conn->remote_frame_rate, 11,
|
||||
conn->input_frame_rate, 11, conn->frame_rate, 10,
|
||||
(conn->local_to_remote_time_gradient - 1.0) * 1000000, 6,
|
||||
conn->local_to_remote_time_gradient_sample_count, 10,
|
||||
(conn->frame_rate > 0.0)
|
||||
? ((conn->frame_rate -
|
||||
conn->remote_frame_rate * conn->output_sample_ratio *
|
||||
conn->local_to_remote_time_gradient) *
|
||||
1000000) /
|
||||
conn->frame_rate
|
||||
: 0.0);
|
||||
} else {
|
||||
inform("%*.2f," /* Sync error in milliseconds */
|
||||
"%*d," /* total packets */
|
||||
@@ -2494,9 +2490,10 @@ void *player_thread_func(void *arg) {
|
||||
"%*.2f," /* source actual (average) frame rate */
|
||||
"%*.2f," /* source clock drift */
|
||||
"%*d", /* source clock drift sample count */
|
||||
10, 1000 * moving_average_sync_error / config.output_rate, 12, play_number,
|
||||
7, conn->missing_packets, 7, conn->late_packets, 7, conn->too_late_packets,
|
||||
7, conn->resend_requests, 7, minimum_dac_queue_size, 5,
|
||||
10,
|
||||
1000 * moving_average_sync_error / config.output_rate, 12, play_number, 7,
|
||||
conn->missing_packets, 7, conn->late_packets, 7, conn->too_late_packets, 7,
|
||||
conn->resend_requests, 7, minimum_dac_queue_size, 5,
|
||||
minimum_buffer_occupancy, 5, maximum_buffer_occupancy, 11,
|
||||
conn->remote_frame_rate, 11, conn->input_frame_rate, 10,
|
||||
(conn->local_to_remote_time_gradient - 1.0) * 1000000, 6,
|
||||
@@ -2515,9 +2512,10 @@ void *player_thread_func(void *arg) {
|
||||
"%*.2f," /* source actual (average) frame rate */
|
||||
"%*.2f," /* source clock drift */
|
||||
"%*d", /* source clock drift sample count */
|
||||
10, 1000 * moving_average_sync_error / config.output_rate, 12, play_number,
|
||||
7, conn->missing_packets, 7, conn->late_packets, 7, conn->too_late_packets,
|
||||
7, conn->resend_requests, 5, minimum_buffer_occupancy, 5,
|
||||
10,
|
||||
1000 * moving_average_sync_error / config.output_rate, 12, play_number, 7,
|
||||
conn->missing_packets, 7, conn->late_packets, 7, conn->too_late_packets, 7,
|
||||
conn->resend_requests, 5, minimum_buffer_occupancy, 5,
|
||||
maximum_buffer_occupancy, 11, conn->remote_frame_rate, 11,
|
||||
conn->input_frame_rate, 10,
|
||||
(conn->local_to_remote_time_gradient - 1.0) * 1000000, 6,
|
||||
@@ -2544,14 +2542,16 @@ void *player_thread_func(void *arg) {
|
||||
}
|
||||
|
||||
void player_volume_without_notification(double airplay_volume, rtsp_conn_info *conn) {
|
||||
debug(1,"player_volume_without_notification %f",airplay_volume);
|
||||
//first, see if we are hw only, sw only, both with hw attenuation on the top or both with sw attenuation on top
|
||||
debug(1, "player_volume_without_notification %f", airplay_volume);
|
||||
// first, see if we are hw only, sw only, both with hw attenuation on the top or both with sw
|
||||
// attenuation on top
|
||||
|
||||
enum volume_mode_type {vol_sw_only, vol_hw_only, vol_both} volume_mode;
|
||||
|
||||
// take account of whether there is a hardware mixer, if a max volume has been specified and if a range has been specified
|
||||
enum volume_mode_type { vol_sw_only, vol_hw_only, vol_both } volume_mode;
|
||||
|
||||
// take account of whether there is a hardware mixer, if a max volume has been specified and if a
|
||||
// range has been specified
|
||||
// the range might imply that both hw and software mixers are needed, so calculate this
|
||||
|
||||
|
||||
int32_t hw_max_db, hw_min_db;
|
||||
int32_t sw_max_db = 0, sw_min_db = -9630;
|
||||
if (config.output->parameters) {
|
||||
@@ -2569,9 +2569,9 @@ void player_volume_without_notification(double airplay_volume, rtsp_conn_info *c
|
||||
sw_max_db = (config.volume_max_db * 100) - hw_min_db;
|
||||
} else {
|
||||
warn("The maximum output level is outside the range of the hardware mixer -- ignored");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// here, we have set limits on the hw_max_db and the sw_max_db
|
||||
// but we haven't actually decided whether we need both hw and software attenuation
|
||||
// only if a range is specified could we need both
|
||||
@@ -2583,9 +2583,9 @@ void player_volume_without_notification(double airplay_volume, rtsp_conn_info *c
|
||||
int32_t desired_sw_range = desired_range_db - (hw_max_db - hw_min_db);
|
||||
if ((sw_max_db - desired_sw_range) < sw_min_db)
|
||||
warn("The range requested is too large to accommodate -- ignored.");
|
||||
else
|
||||
else
|
||||
sw_min_db = (sw_max_db - desired_sw_range);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// debug(1,"has no hardware mixer");
|
||||
@@ -2593,112 +2593,116 @@ void player_volume_without_notification(double airplay_volume, rtsp_conn_info *c
|
||||
if (config.volume_max_db_set) {
|
||||
if (((config.volume_max_db * 100) <= sw_max_db) &&
|
||||
((config.volume_max_db * 100) >= sw_min_db))
|
||||
sw_max_db = (int32_t)config.volume_max_db * 100;
|
||||
sw_max_db = (int32_t)config.volume_max_db * 100;
|
||||
}
|
||||
if (config.volume_range_db) {
|
||||
// see if the range requested exceeds the software range available
|
||||
int32_t desired_range_db = (int32_t)trunc(config.volume_range_db * 100);
|
||||
if ((desired_range_db) > (sw_max_db - sw_min_db))
|
||||
warn("The range requested is too large to accommodate -- ignored.");
|
||||
else
|
||||
sw_min_db = (sw_max_db - desired_range_db);
|
||||
}
|
||||
// see if the range requested exceeds the software range available
|
||||
int32_t desired_range_db = (int32_t)trunc(config.volume_range_db * 100);
|
||||
if ((desired_range_db) > (sw_max_db - sw_min_db))
|
||||
warn("The range requested is too large to accommodate -- ignored.");
|
||||
else
|
||||
sw_min_db = (sw_max_db - desired_range_db);
|
||||
}
|
||||
}
|
||||
|
||||
// here, we know whether it's hw volume control only, sw only or both, and we have the hw and sw limits.
|
||||
|
||||
// here, we know whether it's hw volume control only, sw only or both, and we have the hw and sw
|
||||
// limits.
|
||||
// if it's both, we haven't decided whether hw or sw should be on top
|
||||
// we have to consider the settings ignore_volume_control and mute.
|
||||
|
||||
|
||||
if (config.ignore_volume_control == 0) {
|
||||
if (airplay_volume == -144.0) {
|
||||
|
||||
if ((config.output->mute) && (config.output->mute(1) != 0))
|
||||
debug(1,"hardware mute is enabled.");
|
||||
|
||||
if ((config.output->mute) && (config.output->mute(1) == 0))
|
||||
debug(1, "player: hardware mute is enabled.");
|
||||
else {
|
||||
conn->software_mute_enabled = 1;
|
||||
debug(1,"software mute is enabled.");
|
||||
|
||||
debug(1, "player: software mute is enabled.");
|
||||
}
|
||||
|
||||
} else {
|
||||
int32_t max_db,min_db;
|
||||
int32_t max_db, min_db;
|
||||
switch (volume_mode) {
|
||||
case vol_hw_only:
|
||||
max_db = hw_max_db;
|
||||
min_db = hw_min_db;
|
||||
break;
|
||||
case vol_sw_only:
|
||||
max_db = sw_max_db;
|
||||
min_db = sw_min_db;
|
||||
break;
|
||||
case vol_both:
|
||||
debug(1,"dB range passed is hw: %d, sw: %d, total: %d", hw_max_db - hw_min_db, sw_max_db - sw_min_db, (hw_max_db - hw_min_db) + (sw_max_db - sw_min_db));
|
||||
max_db = (hw_max_db - hw_min_db) + (sw_max_db - sw_min_db); // this should be the range requested
|
||||
min_db = 0;
|
||||
break;
|
||||
default:
|
||||
debug(1,"error in pv -- not in a volume mode");
|
||||
break;
|
||||
case vol_hw_only:
|
||||
max_db = hw_max_db;
|
||||
min_db = hw_min_db;
|
||||
break;
|
||||
case vol_sw_only:
|
||||
max_db = sw_max_db;
|
||||
min_db = sw_min_db;
|
||||
break;
|
||||
case vol_both:
|
||||
debug(1, "dB range passed is hw: %d, sw: %d, total: %d", hw_max_db - hw_min_db,
|
||||
sw_max_db - sw_min_db, (hw_max_db - hw_min_db) + (sw_max_db - sw_min_db));
|
||||
max_db =
|
||||
(hw_max_db - hw_min_db) + (sw_max_db - sw_min_db); // this should be the range requested
|
||||
min_db = 0;
|
||||
break;
|
||||
default:
|
||||
debug(1, "error in pv -- not in a volume mode");
|
||||
break;
|
||||
}
|
||||
double scaled_attenuation = 0.0;
|
||||
if (config.volume_control_profile == VCP_standard)
|
||||
scaled_attenuation = vol2attn(airplay_volume, max_db, min_db); // no cancellation points
|
||||
else if (config.volume_control_profile == VCP_flat)
|
||||
scaled_attenuation = flat_vol2attn(airplay_volume, max_db, min_db); // no cancellation points
|
||||
scaled_attenuation =
|
||||
flat_vol2attn(airplay_volume, max_db, min_db); // no cancellation points
|
||||
else
|
||||
debug(1, "Unrecognised volume control profile");
|
||||
|
||||
|
||||
// so here we have the scaled attenuation. If it's for hw or sw only, it's straightforward.
|
||||
double hardware_attenuation = 0.0;
|
||||
double software_attenuation = 0.0;
|
||||
|
||||
switch (volume_mode) {
|
||||
case vol_hw_only:
|
||||
hardware_attenuation = scaled_attenuation;
|
||||
break;
|
||||
case vol_sw_only:
|
||||
software_attenuation = scaled_attenuation;
|
||||
break;
|
||||
case vol_both:
|
||||
// here, we now the attenuation required, so we have to apportion it to the sw and hw mixers
|
||||
// if we give the hw priority, that means when lowering the volume, set the hw volume to its lowest
|
||||
// before using the sw attenuation.
|
||||
// similarly, if we give the sw priority, that means when lowering the volume, set the sw volume to its lowest
|
||||
// before using the hw attenuation.
|
||||
// one imagines that hw priority is likely to be much better
|
||||
if (config.volume_range_hw_priority) {
|
||||
// hw priority
|
||||
if ((sw_max_db - sw_min_db) > scaled_attenuation) {
|
||||
software_attenuation = sw_min_db + scaled_attenuation;
|
||||
hardware_attenuation = hw_min_db;
|
||||
} else {
|
||||
software_attenuation = sw_max_db;
|
||||
hardware_attenuation = hw_min_db + scaled_attenuation - (sw_max_db - sw_min_db);
|
||||
}
|
||||
} else {
|
||||
// sw priority
|
||||
if ((hw_max_db - hw_min_db) > scaled_attenuation) {
|
||||
hardware_attenuation = hw_min_db + scaled_attenuation;
|
||||
software_attenuation = sw_min_db;
|
||||
} else {
|
||||
hardware_attenuation = hw_max_db;
|
||||
software_attenuation = sw_min_db + scaled_attenuation - (hw_max_db - hw_min_db);
|
||||
}
|
||||
case vol_hw_only:
|
||||
hardware_attenuation = scaled_attenuation;
|
||||
break;
|
||||
case vol_sw_only:
|
||||
software_attenuation = scaled_attenuation;
|
||||
break;
|
||||
case vol_both:
|
||||
// here, we now the attenuation required, so we have to apportion it to the sw and hw mixers
|
||||
// if we give the hw priority, that means when lowering the volume, set the hw volume to its
|
||||
// lowest
|
||||
// before using the sw attenuation.
|
||||
// similarly, if we give the sw priority, that means when lowering the volume, set the sw
|
||||
// volume to its lowest
|
||||
// before using the hw attenuation.
|
||||
// one imagines that hw priority is likely to be much better
|
||||
if (config.volume_range_hw_priority) {
|
||||
// hw priority
|
||||
if ((sw_max_db - sw_min_db) > scaled_attenuation) {
|
||||
software_attenuation = sw_min_db + scaled_attenuation;
|
||||
hardware_attenuation = hw_min_db;
|
||||
} else {
|
||||
software_attenuation = sw_max_db;
|
||||
hardware_attenuation = hw_min_db + scaled_attenuation - (sw_max_db - sw_min_db);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
debug(1,"error in pv -- not in a volume mode");
|
||||
break;
|
||||
} else {
|
||||
// sw priority
|
||||
if ((hw_max_db - hw_min_db) > scaled_attenuation) {
|
||||
hardware_attenuation = hw_min_db + scaled_attenuation;
|
||||
software_attenuation = sw_min_db;
|
||||
} else {
|
||||
hardware_attenuation = hw_max_db;
|
||||
software_attenuation = sw_min_db + scaled_attenuation - (hw_max_db - hw_min_db);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
debug(1, "error in pv -- not in a volume mode");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (((volume_mode == vol_hw_only) || (volume_mode == vol_both)) && (config.output->volume)) {
|
||||
config.output->volume(hardware_attenuation); // otherwise set the output to the lowest value
|
||||
// debug(1,"Hardware attenuation set to %f for airplay volume of
|
||||
// %f.",hardware_attenuation,airplay_volume);
|
||||
conn->fix_volume = 0x10000;
|
||||
}
|
||||
|
||||
|
||||
if ((volume_mode == vol_sw_only) || (volume_mode == vol_both)) {
|
||||
double temp_fix_volume = 65536.0 * pow(10, software_attenuation / 2000);
|
||||
// debug(1,"Software attenuation set to %f, i.e %f out of 65,536, for airplay volume of
|
||||
@@ -2711,7 +2715,6 @@ void player_volume_without_notification(double airplay_volume, rtsp_conn_info *c
|
||||
loudness_set_volume(software_attenuation / 100);
|
||||
}
|
||||
|
||||
|
||||
if (config.logOutputLevel) {
|
||||
inform("Output Level set to: %.2f dB.", scaled_attenuation / 100.0);
|
||||
}
|
||||
@@ -2733,8 +2736,10 @@ void player_volume_without_notification(double airplay_volume, rtsp_conn_info *c
|
||||
if (config.output->mute)
|
||||
config.output->mute(0);
|
||||
conn->software_mute_enabled = 0;
|
||||
|
||||
debug(1,"pv: volume mode is %d, software_attenuation: %f, hardware_attenuation: %f, muting is disabled.", volume_mode, software_attenuation, hardware_attenuation);
|
||||
|
||||
debug(1, "pv: volume mode is %d, software_attenuation: %f, hardware_attenuation: %f, muting "
|
||||
"is disabled.",
|
||||
volume_mode, software_attenuation, hardware_attenuation);
|
||||
}
|
||||
}
|
||||
config.airplay_volume = airplay_volume;
|
||||
|
||||
Reference in New Issue
Block a user