Fix 8-bit dithering, fix software volume control

This commit is contained in:
Mike Brady
2016-09-11 15:40:59 +01:00
parent e527d9f55f
commit e9fef46fdd
+22 -61
View File
@@ -625,11 +625,8 @@ static inline void process_sample(int32_t sample, char **outp, enum sps_format_t
int dither) {
int64_t hyper_sample = sample;
int result;
// first, modify volume, if necessary
if (volume == 0x10000)
hyper_sample <<= 32;
else
hyper_sample *= (volume << 16); // this is 32*32 -> 64 bit bit multiplication -- we may need to
int64_t hyper_volume = (int64_t) volume<<16;
hyper_sample = hyper_sample * hyper_volume; // this is 64 bit bit multiplication -- we may need to
// dither it down to its
// target resolution
@@ -661,7 +658,7 @@ static inline void process_sample(int32_t sample, char **outp, enum sps_format_t
break;
case SPS_FORMAT_S8:
case SPS_FORMAT_U8:
dither_mask = (int64_t)1 << (64 + 1 - 24);
dither_mask = (int64_t)1 << (64 + 1 - 8);
break;
}
dither_mask -= 1;
@@ -717,54 +714,6 @@ static inline void process_sample(int32_t sample, char **outp, enum sps_format_t
*outp += result;
}
static inline int32_t dithered_vol_32(int32_t sample, int output_precision) {
if ((fix_volume == 0x1000) && (output_precision == 32)) {
return sample;
} else {
int64_t s = sample; // 64 bit signed
int64_t v = fix_volume & 0x1FFFF; // 17 bit unsigned made a 33 bit unsigned
v <<= 16;
s = s * v; // this is 32*32 -> 64 bit bit multiplication -- we may need to dither it down to its
// target resolution
// add a TPDF dither -- see
// http://www.users.qwest.net/%7Evolt42/cadenzarecording/DitherExplained.pdf
// and the discussion around https://www.hydrogenaud.io/forums/index.php?showtopic=16963&st=25
// I think, for a 32 --> 16 bits, the range of
// random numbers needs to be from -2^16 to 2^16, i.e. from -65536 to 65536 inclusive, not from
// -32768 to +32767
// See the original paper at
// http://www.ece.rochester.edu/courses/ECE472/resources/Papers/Lipshitz_1992.pdf
// by Lipshitz, Wannamaker and Vanderkooy, 1992.
int64_t r = r64i();
int64_t mask = (int64_t)1 << (64 + 1 - output_precision);
mask -= 1;
int64_t tpdf = (r & mask) - (previous_random_number & mask);
previous_random_number = r;
// Check there's no clipping -- if there is, use INT64_MAX or INT64_MIN
if (tpdf >= 0) {
if (INT64_MAX - tpdf >= s)
s += tpdf;
else
s = INT64_MAX;
} else {
if (INT64_MIN - tpdf <= s)
s += tpdf;
else
s = INT64_MIN;
}
s >>= (64 - output_precision);
return s;
}
}
static inline short dithered_vol(short sample) {
long out;
@@ -1585,6 +1534,10 @@ static void *player_thread_func(void *arg) {
int output_bit_depth = 16; // default;
switch (config.output_format) {
case SPS_FORMAT_S8:
case SPS_FORMAT_U8:
output_bit_depth = 8;
break;
case SPS_FORMAT_S16_LE:
output_bit_depth = 16;
break;
@@ -1597,6 +1550,15 @@ static void *player_thread_func(void *arg) {
}
debug(1, "Output bit depth is %d.", output_bit_depth);
if (input_bit_depth>output_bit_depth) {
debug(1,"Dithering will be enabled because the input bit depth is greater than the output bit depth");
}
if (fix_volume!=0x10000) {
debug(1,"Dithering will be enabled the output volume is being altered in software");
}
// if we are changing any of the parameters of the input, like sample rate or sample depth, then
// we
@@ -1700,11 +1662,9 @@ static void *player_thread_func(void *arg) {
}
} else {
// int enable_dither = 0;
int enable_dither =
!((fix_volume == 0x10000) &&
(input_bit_depth ==
output_bit_depth)); // avoid dither on audio being sent through without alteration
int enable_dither = 0;
if ((fix_volume!=0x10000) || (input_bit_depth>output_bit_depth))
enable_dither=1;
// here, let's transform the frame of data, if necessary
@@ -2363,11 +2323,12 @@ void player_volume(double 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);
pthread_mutex_lock(&vol_mutex);
fix_volume = temp_fix_volume;
pthread_mutex_unlock(&vol_mutex);
#ifdef CONFIG_METADATA
char *dv = malloc(128); // will be freed in the metadata thread
if (dv) {