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;
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;
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 = ¶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);
+ }
+ }
+ /*
+ 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;
+}
+
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;
}
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 = ¶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);
- }
- }
- /*
- 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);
- } 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
}
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
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) {
}
*/
-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;
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) {
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)
(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
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
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
pthread_exit(NULL);
}
-// takes the volume as specified by the airplay protocol
void player_volume_without_notification(double airplay_volume, rtsp_conn_info *conn) {
-
- // 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
+ 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
+ // 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;
+ if (config.volume_max_db_set) {
+ 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 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.");
- }
+ warn("The maximum output level is outside the range of the hardware mixer -- 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;
+
+ // 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 {
- // 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;
+ // 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);
+ }
}
-
- /*
- 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);
- } else {
- inform("The value of volume_max_db is invalid. It must be in the range %d to
- %d.",max_db,min_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.");
+
}
- }
- */
- 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);
+ 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;
}
- }
- } 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);
+ 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;
+
+ 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);
+
+ conn->fix_volume = temp_fix_volume;
+ memory_barrier(); // no cancellation points
+
+ if (config.loudness)
+ loudness_set_volume(software_attenuation / 100);
}
- } 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;
- }
- }
-
- 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);
-
- conn->fix_volume = temp_fix_volume;
- memory_barrier(); // no cancellation points
- if (config.loudness)
- loudness_set_volume(software_attenuation / 100);
- if (config.logOutputLevel) {
- inform("Output Level set to: %.2f dB.", scaled_attenuation / 100.0);
- }
+ 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;
}