Rebuild the volume control calculations. Implement software mute. Try to delay alsa initialisation as long as possible.

This commit is contained in:
Mike Brady
2019-01-30 09:24:10 +00:00
parent aa5698fce2
commit c5ff0dfed0
5 changed files with 324 additions and 318 deletions
+2 -1
View File
@@ -47,7 +47,8 @@ typedef struct {
void (*parameters)(audio_parameters *info);
// may be NULL, in which case software muting is used.
void (*mute)(int do_mute);
// also, will return a 1 if it is actually using the mute facility, 0 otherwise
int (*mute)(int do_mute);
} audio_output;
+125 -99
View File
@@ -59,7 +59,7 @@ static void volume(double vol);
void do_volume(double vol);
static void parameters(audio_parameters *info);
static void mute(int do_mute);
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;
@@ -93,6 +93,7 @@ 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
snd_pcm_t *alsa_handle = NULL;
static snd_pcm_hw_params_t *alsa_params = NULL;
static snd_pcm_sw_params_t *alsa_swparams = NULL;
@@ -620,12 +621,117 @@ int open_alsa_device(void) {
return result;
}
int 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
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);
}
}
return response;
}
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.
alsa_device_initialised = 0;
const char *str;
int value;
// double dvalue;
@@ -871,101 +977,9 @@ static int init(int argc, char **argv) {
}
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.");
}
do_alsa_device_init_if_needed();
alsa_mix_handle = NULL;
// so, now, if the option to keep the DAC running has been selected, start a thread to monitor the
@@ -994,7 +1008,7 @@ static void deinit(void) {
}
static void start(int i_sample_rate, int i_sample_format) {
// debug(2,"audio_alsa start called.");
debug(1,"audio_alsa start called.");
if (i_sample_rate == 0)
desired_sample_rate = 44100; // default
else
@@ -1010,6 +1024,10 @@ 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.");
do_alsa_device_init_if_needed();
}
}
int delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay) {
@@ -1380,7 +1398,10 @@ static void linear_volume(double vol) {
}
*/
static void mute(int mute_state_requested) {
int mute(int 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;
@@ -1388,6 +1409,7 @@ static void mute(int mute_state_requested) {
do_mute(mute_state_requested);
debug_mutex_unlock(&alsa_mutex, 3);
pthread_cleanup_pop(0); // release the mutex
return response;
}
void do_mute(int mute_state_requested) {
@@ -1447,8 +1469,12 @@ void alsa_buffer_monitor_thread_cleanup_function(__attribute__((unused)) void *a
void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) {
while (1) {
int sleep_time_ms = (int)(config.audio_backend_silence_scan_interval * 1000);
pthread_cleanup_debug_mutex_lock(&alsa_mutex, 10000, 1);
pthread_cleanup_debug_mutex_lock(&alsa_mutex, 20000, 1);
// check possible state transitions here
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.");
do_alsa_device_init_if_needed();
}
if ((alsa_backend_state == abm_disconnected) && (config.keep_dac_busy != 0)) {
// open the dac and move to abm_connected mode
if (do_open() == 0)
+2
View File
@@ -182,6 +182,8 @@ typedef struct {
// into the "active" mode.
uint32_t volume_range_db; // the range, in dB, from max dB to min dB. Zero means use the mixer's
// native range.
int volume_range_hw_priority; // when lowering the volume, use all the hw attenuation before using
// sw attenuation
enum sps_format_t output_format;
enum volume_control_profile_type volume_control_profile;
int output_rate;
+186 -210
View File
@@ -1662,6 +1662,7 @@ void *player_thread_func(void *arg) {
(config.playback_mode == ST_mono))
conn->enable_dither = 1;
// remember, the output device may never have been initialised prior to this call
config.output->start(config.output_rate, config.output_format); // will need a corresponding stop
// we need an intermediate "transition" buffer
@@ -2276,8 +2277,14 @@ void *player_thread_func(void *arg) {
else {
if (play_samples == 0)
debug(1, "play_samples==0 skipping it (1).");
else
else {
if (conn->software_mute_enabled) {
generate_zero_frames(
conn->outbuf, play_samples,
config.output_format, conn->enable_dither, conn->previous_random_number);
}
config.output->play(conn->outbuf, play_samples);
}
}
// check for loss of sync
@@ -2311,8 +2318,14 @@ void *player_thread_func(void *arg) {
conn->outbuf, 0, conn->enable_dither, conn);
if (conn->outbuf == NULL)
debug(1, "NULL outbuf to play -- skipping it.");
else
else {
if (conn->software_mute_enabled) {
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*)!
}
}
// mark the frame as finished
@@ -2530,235 +2543,198 @@ void *player_thread_func(void *arg) {
pthread_exit(NULL);
}
// takes the volume as specified by the airplay protocol
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
// no cancellation points here if we assume that the mute call to the back end has no cancellation
// points
// The volume ranges -144.0 (mute) or -30 -- 0. See
// http://git.zx2c4.com/Airtunes2/about/#setting-volume
// By examination, the -30 -- 0 range is linear on the slider; i.e. the slider is calibrated in 30
// equal increments. Since the human ear's response is roughly logarithmic, we imagine these to
// be power dB, i.e. from -30dB to 0dB.
// We may have a hardware mixer, and if so, we will give it priority.
// If a desired volume range is given, then we will try to accommodate it from
// the top of the hardware mixer's range downwards.
// If no desired volume range is given, we will use the native resolution of the hardware mixer,
// if any,
// or failing that, the software mixer. The software mixer has a range of from -96.3 dB up to 0
// dB,
// corresponding to a multiplier of 1 to 65535.
// Otherwise, we will accommodate the desired volume range in the combination of the software and
// hardware mixer
// Intuitively (!), it seems best to give the hardware mixer as big a role as possible, so
// we will use its full range and then accommodate the rest of the attenuation in software.
// A problem is that we don't know whether the lowest hardware volume actually mutes the output
// so we must assume that it does, and for this reason, the volume control goes at the "bottom" of
// the adjustment range
// The dB range of a value from 1 to 65536 is about 96.3 dB (log10 of 65536 is 4.8164).
// Since the levels correspond with amplitude, they correspond to voltage, hence voltage dB,
// or 20 times the log of the ratio. Then multiplied by 100 for convenience.
// Thus, we ask our vol2attn function for an appropriate dB between -96.3 and 0 dB and translate
// it back to a number.
int32_t hw_min_db, hw_max_db, hw_range_db, min_db,
max_db; // hw_range_db is a flag; if 0 means no mixer
if (config.output->parameters) { // no cancellation points in here
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) {
volume_mode = vol_hw_only;
audio_parameters audio_information;
// have a hardware mixer
config.output->parameters(&audio_information);
hw_max_db = audio_information.maximum_volume_dB;
hw_min_db = audio_information.minimum_volume_dB;
hw_range_db = hw_max_db - hw_min_db;
} else {
// don't have a hardware mixer
hw_max_db = hw_min_db = hw_range_db = 0;
}
int32_t sw_min_db = -9630;
int32_t sw_max_db = 0;
int32_t sw_range_db = sw_max_db - sw_min_db;
int32_t desired_range_db = 0; // this is also used as a flag; if 0 means no desired range
if (config.volume_range_db)
desired_range_db = (int32_t)trunc(config.volume_range_db * 100);
// This is wrong, I think -- it doesn't work properly if the volume range is composite and you
// want to set a maximum value which should affect the hardware mixer.
if (config.volume_max_db_set) {
if (hw_range_db) {
if (((config.volume_max_db * 100) < hw_max_db) &&
((config.volume_max_db * 100) > hw_min_db)) {
hw_max_db = (int)config.volume_max_db * 100;
hw_range_db = hw_max_db - hw_min_db;
} else {
inform("The volume_max_db setting is out of range of the hardware mixers's limits of %d dB "
"to %d dB. It will be ignored.",
(int)(hw_max_db / 100), (int)(hw_min_db / 100));
}
} else {
if (((config.volume_max_db * 100) < sw_max_db) &&
((config.volume_max_db * 100) > sw_min_db)) {
sw_max_db = (int)config.volume_max_db * 100;
sw_range_db = sw_max_db - sw_min_db;
} else {
inform("The volume_max_db setting is out of range of the software attenuation's limits of "
"0 dB to -96.3 dB. It will be ignored.");
}
}
}
if (desired_range_db) {
// debug(1,"An attenuation range of %d is requested.",desired_range_db);
// we have a desired volume range.
if (hw_range_db) {
// we have a hardware mixer
if (hw_range_db >= desired_range_db) {
// the hardware mixer can accommodate the desired range
max_db = hw_max_db;
min_db = max_db - desired_range_db;
} else {
// we have a hardware mixer and a desired range greater than the mixer's range.
if ((hw_range_db + sw_range_db) < desired_range_db) {
inform("The volume attenuation range %f is greater than can be accommodated by the "
"hardware and software -- set to %f.",
config.volume_range_db, hw_range_db + sw_range_db);
desired_range_db = hw_range_db + sw_range_db;
}
min_db = hw_min_db;
max_db = min_db + desired_range_db;
}
} else {
// we have a desired volume range and no hardware mixer
if (sw_range_db < desired_range_db) {
inform("The volume attenuation range %f is greater than can be accommodated by the "
"software -- set to %f.",
config.volume_range_db, sw_range_db);
desired_range_db = sw_range_db;
}
max_db = sw_max_db;
min_db = max_db - desired_range_db;
}
} else {
// we do not have a desired volume range, so use the mixer's volume range, if there is one.
// debug(1,"No attenuation range requested.");
if (hw_range_db) {
min_db = hw_min_db;
max_db = hw_max_db;
} else {
min_db = sw_min_db;
max_db = sw_max_db;
}
}
/*
if (config.volume_max_db_set) {
if ((config.volume_max_db*100<=max_db) && (config.volume_max_db*100>=min_db)) {
debug(1,"Reducing the maximum volume from %d to %d.",max_db/100,config.volume_max_db);
max_db = (int)(config.volume_max_db*100);
if (((config.volume_max_db * 100) <= hw_max_db) &&
((config.volume_max_db * 100) >= hw_min_db))
hw_max_db = (int32_t)config.volume_max_db * 100;
else if (config.volume_range_db) {
hw_max_db = hw_min_db;
sw_max_db = (config.volume_max_db * 100) - hw_min_db;
} else {
inform("The value of volume_max_db is invalid. It must be in the range %d to
%d.",max_db,min_db);
}
warn("The maximum output level is outside the range of the hardware mixer -- ignored");
}
}
*/
double hardware_attenuation = 0.0, software_attenuation = 0.0;
double scaled_attenuation = hw_min_db + sw_min_db;
// now, we can map the input to the desired output volume
if ((airplay_volume == -144.0) && (config.ignore_volume_control == 0)) {
// do a mute
// needed even with hardware mute, as when sound is unmuted it might otherwise be very loud.
hardware_attenuation = hw_min_db;
if (config.output->mute) {
// allow the audio material to reach the mixer, but mute the mixer
// it the mute is removed externally, the material with be there
software_attenuation = sw_max_db - (max_db - hw_max_db); // e.g. if the hw_max_db is +4 and
// the max is +40, this will be -36
// (all by 100, of course)
// debug(1,"Mute, with hardware mute and software_attenuation set to
// %d.",software_attenuation);
config.output->mute(1); // use real mute if it's there
} else {
if (config.output->volume == NULL) { // if there is also no hardware volume control
software_attenuation = sw_min_db; // set any software output to zero too
// debug(1,"Mute, with no hardware mute and software_attenuation set to
// %d.",software_attenuation);
}
// 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
if (config.volume_range_db) {
// see if the range requested exceeds the hardware range available
int32_t desired_range_db = (int32_t)trunc(config.volume_range_db * 100);
if ((desired_range_db) > (hw_max_db - hw_min_db)) {
volume_mode = vol_both;
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
sw_min_db = (sw_max_db - desired_sw_range);
}
}
} else {
if (config.output->mute)
config.output->mute(0); // unmute mute if it's there
if (config.ignore_volume_control == 1)
scaled_attenuation = max_db;
else 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
else
debug(1, "Unrecognised volume control profile");
if (hw_range_db) {
// if there is a hardware mixer
if (scaled_attenuation <= hw_max_db) {
// the attenuation is so low that's it's in the hardware mixer's range
// debug(1,"Attenuation all taken care of by the hardware mixer.");
hardware_attenuation = scaled_attenuation;
software_attenuation = sw_max_db - (max_db - hw_max_db); // e.g. if the hw_max_db is +4 and
// the max is +40, this will be -36
// (all by 100, of course)
} else {
// debug(1,"Attenuation taken care of by hardware and software mixer.");
hardware_attenuation = hw_max_db; // the hardware mixer is turned up full
software_attenuation = sw_max_db - (max_db - scaled_attenuation);
}
} else {
// if there is no hardware mixer, the scaled_volume is the software volume
// debug(1,"Attenuation all taken care of by the software mixer.");
software_attenuation = scaled_attenuation;
// debug(1,"has no hardware mixer");
volume_mode = vol_sw_only;
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;
}
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);
}
}
// 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.");
else {
conn->software_mute_enabled = 1;
debug(1,"software mute is enabled.");
}
if ((config.output->volume) && (hw_range_db)) {
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);
}
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
// %f",software_attenuation,temp_fix_volume,airplay_volume);
} else {
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;
}
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
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;
conn->fix_volume = temp_fix_volume;
memory_barrier(); // no cancellation points
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);
}
}
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);
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
// %f",software_attenuation,temp_fix_volume,airplay_volume);
if (config.loudness)
loudness_set_volume(software_attenuation / 100);
conn->fix_volume = temp_fix_volume;
memory_barrier(); // no cancellation points
if (config.logOutputLevel) {
inform("Output Level set to: %.2f dB.", scaled_attenuation / 100.0);
}
if (config.loudness)
loudness_set_volume(software_attenuation / 100);
}
if (config.logOutputLevel) {
inform("Output Level set to: %.2f dB.", scaled_attenuation / 100.0);
}
#ifdef CONFIG_METADATA
char *dv = malloc(128); // will be freed in the metadata thread
if (dv) {
memset(dv, 0, 128);
if (config.ignore_volume_control == 1)
snprintf(dv, 127, "%.2f,%.2f,%.2f,%.2f", airplay_volume, 0.0, 0.0, 0.0);
else
snprintf(dv, 127, "%.2f,%.2f,%.2f,%.2f", airplay_volume, scaled_attenuation / 100.0,
min_db / 100.0, max_db / 100.0);
send_ssnc_metadata('pvol', dv, strlen(dv), 1);
}
char *dv = malloc(128); // will be freed in the metadata thread
if (dv) {
memset(dv, 0, 128);
if (config.ignore_volume_control == 1)
snprintf(dv, 127, "%.2f,%.2f,%.2f,%.2f", airplay_volume, 0.0, 0.0, 0.0);
else
snprintf(dv, 127, "%.2f,%.2f,%.2f,%.2f", airplay_volume, scaled_attenuation / 100.0,
min_db / 100.0, max_db / 100.0);
send_ssnc_metadata('pvol', dv, strlen(dv), 1);
}
#endif
// here, store the volume for possible use in the future
// here, store the volume for possible use in the future
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);
}
}
config.airplay_volume = airplay_volume;
}
+9 -8
View File
@@ -68,14 +68,15 @@ typedef struct {
} stream_cfg;
typedef struct {
int connection_number; // for debug ID purposes, nothing else...
int resend_interval; // this is really just for debugging
int AirPlayVersion; // zero if not an AirPlay session. Used to help calculate latency
uint32_t latency; // the actual latency used for this play session
uint32_t minimum_latency; // set if an a=min-latency: line appears in the ANNOUNCE message; zero
// otherwise
uint32_t maximum_latency; // set if an a=max-latency: line appears in the ANNOUNCE message; zero
// otherwise
int connection_number; // for debug ID purposes, nothing else...
int resend_interval; // this is really just for debugging
int AirPlayVersion; // zero if not an AirPlay session. Used to help calculate latency
uint32_t latency; // the actual latency used for this play session
uint32_t minimum_latency; // set if an a=min-latency: line appears in the ANNOUNCE message; zero
// otherwise
uint32_t maximum_latency; // set if an a=max-latency: line appears in the ANNOUNCE message; zero
// otherwise
int software_mute_enabled; // if we don't have a real mute that we can use
int fd;
int authorized; // set if a password is required and has been supplied