playing with using just a linear mixer
This commit is contained in:
+57
-6
@@ -29,6 +29,7 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <memory.h>
|
||||
#include <math.h>
|
||||
#include <pthread.h>
|
||||
#include <alsa/asoundlib.h>
|
||||
#include "common.h"
|
||||
@@ -43,9 +44,9 @@ static void stop(void);
|
||||
static void flush(void);
|
||||
static uint32_t delay(void);
|
||||
static void volume(double vol);
|
||||
static void linear_volume(double vol);
|
||||
static void parameters(audio_parameters *info);
|
||||
static int has_mute = 0;
|
||||
static int has_db_vol = 0;
|
||||
static double set_volume;
|
||||
|
||||
audio_output audio_alsa = {
|
||||
@@ -209,12 +210,37 @@ static int init(int argc, char **argv) {
|
||||
alsa_mix_elem = snd_mixer_find_selem(alsa_mix_handle, alsa_mix_sid);
|
||||
if (!alsa_mix_elem)
|
||||
die("Failed to find mixer element");
|
||||
// Check that the output device has two playback channels
|
||||
snd_mixer_selem_channel_id_t chn;
|
||||
|
||||
|
||||
if (snd_mixer_selem_has_playback_volume(alsa_mix_elem) ||
|
||||
snd_mixer_selem_has_playback_switch(alsa_mix_elem)) {
|
||||
if (snd_mixer_selem_is_playback_mono(alsa_mix_elem)) {
|
||||
debug(1,"Mono Capable Output Device Only");
|
||||
} else {
|
||||
for (chn = 0; chn <= SND_MIXER_SCHN_LAST; chn++){
|
||||
if (snd_mixer_selem_has_playback_channel(alsa_mix_elem, chn)) {
|
||||
debug(1,"Playback channel \"%s\" discovered", snd_mixer_selem_channel_name(chn));
|
||||
// see if it's got a dB range
|
||||
int64_t cmin,cmax;
|
||||
if (snd_mixer_selem_get_playback_dB_range(alsa_mix_elem,&cmin,&cmax)==0)
|
||||
debug(1,"Range in dB is from %ld to %ld",cmin,cmax);
|
||||
else
|
||||
debug(1,"Can't get dB volume range");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
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_ask_playback_vol_dB(alsa_mix_elem, alsa_mix_minv, &alsa_mix_mindb) == 0) &&
|
||||
(snd_mixer_selem_ask_playback_vol_dB(alsa_mix_elem, alsa_mix_maxv, &alsa_mix_maxdb) == 0)) {
|
||||
has_db_vol = 1;
|
||||
if (snd_mixer_selem_get_playback_dB_range (alsa_mix_elem, &alsa_mix_mindb, &alsa_mix_maxdb) == 0) {
|
||||
// if ((snd_mixer_selem_ask_playback_vol_dB(alsa_mix_elem, alsa_mix_minv, &alsa_mix_mindb) == 0) &&
|
||||
// (snd_mixer_selem_ask_playback_vol_dB(alsa_mix_elem, alsa_mix_maxv, &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 == -9999999) {
|
||||
@@ -226,8 +252,17 @@ static int init(int argc, char **argv) {
|
||||
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
|
||||
inform("note: the hardware mixer specified -- \"%s\" -- does not have dB volume, so it can not be used for volume control.",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
|
||||
debug(1,"Max and min dB calculated are %d and %d.",alsa_mix_maxdb,alsa_mix_mindb);
|
||||
}
|
||||
}
|
||||
if (snd_mixer_selem_has_playback_switch(alsa_mix_elem)) {
|
||||
has_mute = 1;
|
||||
@@ -320,7 +355,7 @@ static void play(short buf[], int samples) {
|
||||
if (alsa_handle == NULL) {
|
||||
ret = open_alsa_device();
|
||||
if ((ret == 0) && (audio_alsa.volume))
|
||||
volume(set_volume);
|
||||
audio_alsa.volume(set_volume);
|
||||
}
|
||||
if (ret == 0) {
|
||||
pthread_mutex_lock(&alsa_mutex);
|
||||
@@ -390,6 +425,7 @@ static void parameters(audio_parameters *info) {
|
||||
}
|
||||
|
||||
static void volume(double vol) {
|
||||
debug(1,"Setting volume in dB.");
|
||||
set_volume = vol;
|
||||
double vol_setting = vol2attn(vol, alsa_mix_maxdb, alsa_mix_mindb);
|
||||
// debug(1,"Setting volume db to %f, for volume input of %f.",vol_setting/100,vol);
|
||||
@@ -398,3 +434,18 @@ static void volume(double vol) {
|
||||
if (has_mute)
|
||||
snd_mixer_selem_set_playback_switch_all(alsa_mix_elem, (vol != -144.0));
|
||||
}
|
||||
|
||||
static void linear_volume(double vol) {
|
||||
debug(1,"Setting linear volume for %f.",vol);
|
||||
set_volume = vol;
|
||||
double vol_setting = vol2attn(vol, 0, alsa_mix_mindb)/2000;
|
||||
debug(1,"Adjusted volume is %f.",vol_setting);
|
||||
double linear_volume = pow(10, vol_setting);
|
||||
debug(1,"Linear volume is %f.",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 (snd_mixer_selem_set_playback_volume_all(alsa_mix_elem, int_vol) != 0)
|
||||
die("Failed to set playback volume");
|
||||
if (has_mute)
|
||||
snd_mixer_selem_set_playback_switch_all(alsa_mix_elem, (vol != -144.0));
|
||||
}
|
||||
|
||||
@@ -1196,32 +1196,35 @@ void player_volume(double f) {
|
||||
// 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
|
||||
// So, we will pass this on without any weighting if we have a hardware mixer, as we expect the
|
||||
// mixer to be calibrated in dB.
|
||||
|
||||
// Here, we ask for an attenuation we will apply in software. The dB range of a value from 1 to
|
||||
// 65536 is about 48.1 dB (log10 of 65536 is 4.8164).
|
||||
// Thus, we ask our vol2attn function for an appropriate dB between -48.1 and 0 dB and translate
|
||||
// equal increments. Since the human ear's response is roughly logarithmic, we imagine these to
|
||||
// be power dB, i.e. from -30dB to 0dB.
|
||||
|
||||
// So, if we have a hardware mixer, we will pass this on to its dB volume settings.
|
||||
|
||||
// Without a hardware mixer, we have to do attenuation in software, so
|
||||
// here, we ask for an attenuation we will apply to the signal amplitude in software.
|
||||
// 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.
|
||||
|
||||
double scaled_volume = vol2attn(f, 0, -4810);
|
||||
double linear_volume = pow(10, scaled_volume / 1000);
|
||||
double scaled_volume = vol2attn(f, 0, -9630);
|
||||
double linear_volume = pow(10, scaled_volume / 20*100);
|
||||
|
||||
if (f == -144.0)
|
||||
linear_volume = 0.0;
|
||||
|
||||
if (config.output->volume) {
|
||||
config.output->volume(f); // volume will be sent as metadata by the config.output device
|
||||
linear_volume =
|
||||
1.0; // no attenuation needed -- this value is used as a flag to avoid calculations
|
||||
linear_volume = 1.0; // no attenuation needed -- this value is used as a flag to avoid calculations
|
||||
}
|
||||
|
||||
if (config.output->parameters)
|
||||
config.output->parameters(&audio_information);
|
||||
else {
|
||||
audio_information.airplay_volume = f;
|
||||
audio_information.minimum_volume_dB = -4810;
|
||||
audio_information.minimum_volume_dB = -9630;
|
||||
audio_information.maximum_volume_dB = 0;
|
||||
audio_information.current_volume_dB = scaled_volume;
|
||||
audio_information.has_true_mute = 0;
|
||||
@@ -1233,10 +1236,10 @@ void player_volume(double f) {
|
||||
fix_volume = 65536.0 * software_mixer_volume;
|
||||
pthread_mutex_unlock(&vol_mutex);
|
||||
#ifdef CONFIG_METADATA
|
||||
char *dv = malloc(64); // will be freed in the metadata thread
|
||||
char *dv = malloc(128); // will be freed in the metadata thread
|
||||
if (dv) {
|
||||
memset(dv, 0, 64);
|
||||
snprintf(dv, 63, "%.2f,%.2f,%.2f,%.2f", audio_information.airplay_volume,
|
||||
memset(dv, 0, 128);
|
||||
snprintf(dv, 127, "%.2f,%.2f,%.2f,%.2f", audio_information.airplay_volume,
|
||||
audio_information.current_volume_dB / 100.0,
|
||||
audio_information.minimum_volume_dB / 100.0,
|
||||
audio_information.maximum_volume_dB / 100.0);
|
||||
|
||||
Reference in New Issue
Block a user