Rockbox open source high quality audio player as a Music Player Daemon
mpris rockbox mpd libadwaita audio rust zig deno
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

pcm: improve 32-bit software volume scaling

Replace the factor calculation from pcm-alsa.c, which
is based on signal *power* ratios, with the fp_factor()
calculation that is based on amplitude ratios. Because
power changes with the square of amplitude, this means
1 dB of power equals 2 dB of amplitude.

Rockbox's volume controls are amplitude-based, so the
smallest step size for pcm-alsa was effectively 2 dB.
The fp_factor() method supports 0.1 dB steps and is a
bit more accurate besides, so it's simply superior in
all respects (aside from taking a few more CPU cycles
to calculate the factor).

Change-Id: I34d143c225d8b5e085cde299fc405f83c13314bf

+45 -86
+22 -34
firmware/pcm_sw_volume.c
··· 51 51 52 52 /* take care of some defines for 32-bit software vol */ 53 53 #if (PCM_NATIVE_BITDEPTH > 16) /* >16-bit */ 54 - 55 54 # define HAVE_SWVOL_32 56 - # define PCM_VOL_SAMPLE_SIZE (2 * sizeof (int32_t)) 57 - # define PCM_DBL_BUF_SIZE_T int32_t 58 - 59 - # if !defined(PCM_DC_OFFSET_VALUE) 60 - /* PCM_DC_OFFSET_VALUE is only needed due to hardware quirk on Eros Q */ 61 - # define PCM_DC_OFFSET_VALUE 0 62 - # endif 63 - 55 + # define PCM_VOL_SAMPLE_SIZE (2 * sizeof (int32_t)) 56 + # define PCM_DBL_BUF_SIZE_T int32_t 64 57 #else /* 16-BIT */ 58 + # define PCM_VOL_SAMPLE_SIZE (2 * sizeof (int16_t)) 59 + # define PCM_DBL_BUF_SIZE_T int16_t 60 + #endif /* 16-BIT */ 65 61 66 - # define PCM_VOL_SAMPLE_SIZE PCM_SAMPLE_SIZE 67 - # define PCM_DBL_BUF_SIZE_T int16_t 62 + #if !defined(PCM_DC_OFFSET_VALUE) 63 + /* PCM_DC_OFFSET_VALUE is only needed due to hardware quirk on Eros Q */ 64 + # define PCM_DC_OFFSET_VALUE 0 65 + #endif 68 66 69 - #endif /* 16-BIT */ 67 + /* 68 + * 16-bit samples are scaled up to an effective bit depth 69 + * of (16+fracbits) bits and must be shifted to produce a 70 + * native bit depth sample. Positive values correspond to 71 + * right shifts (throwing away bits of the scaled result) 72 + * while negative values correspond to left shifts (where 73 + * the scaled result completely fits in the native sample). 74 + */ 75 + #define PCM_SCALE_SHIFT (16 + PCM_SW_VOLUME_FRACBITS - PCM_NATIVE_BITDEPTH) 70 76 71 77 /*** 72 78 ** Volume scaling routines ··· 83 89 /* Scale sample by PCM factor */ 84 90 static inline int32_t pcm_scale_sample(PCM_F_T f, int32_t s) 85 91 { 86 - #if defined(HAVE_SWVOL_32) 87 - return (f * s + PCM_DC_OFFSET_VALUE) >> (32 - PCM_NATIVE_BITDEPTH); 92 + #if PCM_SCALE_SHIFT > 0 93 + return (f * s + PCM_DC_OFFSET_VALUE) >> PCM_SCALE_SHIFT; 88 94 #else 89 - return (f * s) >> PCM_SW_VOLUME_FRACBITS; 95 + return (f * s + PCM_DC_OFFSET_VALUE) << (-PCM_SCALE_SHIFT); 90 96 #endif 91 97 } 92 98 ··· 347 353 { 348 354 if (volume == PCM_MUTE_LEVEL) 349 355 return 0; /* mute */ 350 - #if defined(HAVE_SWVOL_32) 351 - /* 352 - * 32-bit software volume taken from pcm-alsa.c 353 - */ 354 - volume += 48; /* -42dB .. 0dB => 5dB .. 48dB */ 355 - /* NOTE if vol_dB = 5 then vol_shift = 1 but r = 1 so we do vol_shift - 1 >= 0 356 - * otherwise vol_dB >= 0 implies vol_shift >= 2 so vol_shift - 2 >= 0 */ 357 - int vol_shift = volume / 3; 358 - int r = volume % 3; 359 - int32_t dig_vol_mult; 360 - if(r == 0) 361 - dig_vol_mult = 1 << vol_shift; 362 - else if(r == 1) 363 - dig_vol_mult = 1 << vol_shift | 1 << (vol_shift - 2); 364 - else 365 - dig_vol_mult = 1 << vol_shift | 1 << (vol_shift - 1); 366 - return dig_vol_mult; 367 - #else /* standard software volume */ 356 + 368 357 /* Centibels -> fixedpoint */ 369 358 return (uint32_t)fp_factor(fp_div(volume, 10, PCM_SW_VOLUME_FRACBITS), 370 359 PCM_SW_VOLUME_FRACBITS); 371 - #endif /* HAVE_SWVOL_32 */ 372 360 } 373 361 374 362
+3 -3
firmware/target/hosted/aigo/erosqlinux_codec.c
··· 204 204 205 205 int sw_volume_l = l <= min_pcm ? min_pcm : MIN(l, max_pcm); 206 206 int sw_volume_r = r <= min_pcm ? min_pcm : MIN(r, max_pcm); 207 - pcm_set_mixer_volume(sw_volume_l / 20, sw_volume_r / 20); 207 + pcm_set_mixer_volume(sw_volume_l, sw_volume_r); 208 208 } 209 209 210 210 static void audiohw_set_volume_v2(int vol_l, int vol_r) ··· 236 236 alsa_controls_set_ints("Right Playback Volume", 1, &r); 237 237 238 238 /* Dial back PCM mixer to avoid compression */ 239 - pcm_set_mixer_volume(global_settings.volume_limit / 2, global_settings.volume_limit / 2); 239 + pcm_set_mixer_volume(global_settings.volume_limit, global_settings.volume_limit); 240 240 } 241 241 242 242 void audiohw_set_volume(int vol_l, int vol_r) ··· 274 274 } else { 275 275 int sw_volume_l = l <= min_pcm ? min_pcm : MIN(l, max_pcm); 276 276 int sw_volume_r = r <= min_pcm ? min_pcm : MIN(r, max_pcm); 277 - pcm_set_mixer_volume(sw_volume_l / 20, sw_volume_r / 20); 277 + pcm_set_mixer_volume(sw_volume_l, sw_volume_r); 278 278 } 279 279 } 280 280 }
+6 -6
firmware/target/hosted/fiio/fiiolinux_codec.c
··· 95 95 if (ak_hw < 0) 96 96 return; 97 97 98 - vol[0] = vol_l / 20; 99 - vol[1] = vol_r / 20; 98 + vol[0] = vol_l; 99 + vol[1] = vol_r; 100 100 101 101 for (int i = 0; i < 2; i++) 102 102 { 103 - if (vol[i] > -56) 103 + if (vol[i] > -1120) 104 104 { 105 - if (vol[i] < -12) 105 + if (vol[i] < -240) 106 106 { 107 107 vol_hw[i] = 1; 108 - vol_sw[i] = vol[i] + 12; 108 + vol_sw[i] = vol[i] + 240; 109 109 } 110 110 else 111 111 { 112 - vol_hw[i] = 25 - (-vol[i] * 2); 112 + vol_hw[i] = 25 - (-vol[i] / 10); 113 113 vol_sw[i] = 0; 114 114 } 115 115 }
+7 -38
firmware/target/hosted/pcm-alsa.c
··· 50 50 #include "pcm_sampr.h" 51 51 #include "audiohw.h" 52 52 #include "pcm-alsa.h" 53 + #include "fixedpoint.h" 53 54 54 55 #include "logf.h" 55 56 ··· 267 268 } 268 269 269 270 #if defined(HAVE_ALSA_32BIT) 270 - /* Digital volume explanation: 271 - * with very good approximation (<0.1dB) the convertion from dB to multiplicative 272 - * factor, for dB>=0, is 2^(dB/3). We can then notice that if we write dB=3*k+r 273 - * then this is 2^k*2^(r/3) so we only need to look at r=0,1,2. For r=0 this is 274 - * 1, for r=1 we have 2^(1/3)~=1.25 so we approximate by 1+1/4, and 2^(2/3)~=1.5 275 - * so we approximate by 1+1/2. To go from negative to nonnegative we notice that 276 - * 48 dB => 63095 factor ~= 2^16 so we virtually pre-multiply everything by 2^(-16) 277 - * and add 48dB to the input volume. We cannot go lower -43dB because several 278 - * values between -48dB and -43dB would require a fractional multiplier, which is 279 - * stupid to implement for such very low volume. */ 280 - static int dig_vol_mult_l = 2 << 16; /* multiplicative factor to apply to each sample */ 281 - static int dig_vol_mult_r = 2 << 16; /* multiplicative factor to apply to each sample */ 271 + /* Multiplicative factors applied to each sample */ 272 + static int32_t dig_vol_mult_l = 0; 273 + static int32_t dig_vol_mult_r = 0; 274 + 282 275 void pcm_set_mixer_volume(int vol_db_l, int vol_db_r) 283 276 { 284 - if(vol_db_l > 0 || vol_db_r > 0 || vol_db_l < -43 || vol_db_r < -43) 285 - panicf("invalid pcm alsa volume %d %d", vol_db_l, vol_db_r); 286 - if(format != SND_PCM_FORMAT_S32_LE) 287 - panicf("this function assumes 32-bit sample size"); 288 - vol_db_l += 48; /* -42dB .. 0dB => 5dB .. 48dB */ 289 - vol_db_r += 48; /* -42dB .. 0dB => 5dB .. 48dB */ 290 - /* NOTE if vol_dB = 5 then vol_shift = 1 but r = 1 so we do vol_shift - 1 >= 0 291 - * otherwise vol_dB >= 0 implies vol_shift >= 2 so vol_shift - 2 >= 0 */ 292 - int vol_shift_l = vol_db_l / 3; 293 - int vol_shift_r = vol_db_r / 3; 294 - int r_l = vol_db_l % 3; 295 - int r_r = vol_db_r % 3; 296 - if(r_l == 0) 297 - dig_vol_mult_l = 1 << vol_shift_l; 298 - else if(r_l == 1) 299 - dig_vol_mult_l = 1 << vol_shift_l | 1 << (vol_shift_l - 2); 300 - else 301 - dig_vol_mult_l = 1 << vol_shift_l | 1 << (vol_shift_l - 1); 302 - logf("l: %d dB -> factor = %d", vol_db_l - 48, dig_vol_mult_l); 303 - if(r_r == 0) 304 - dig_vol_mult_r = 1 << vol_shift_r; 305 - else if(r_r == 1) 306 - dig_vol_mult_r = 1 << vol_shift_r | 1 << (vol_shift_r - 2); 307 - else 308 - dig_vol_mult_r = 1 << vol_shift_r | 1 << (vol_shift_r - 1); 309 - logf("r: %d dB -> factor = %d", vol_db_r - 48, dig_vol_mult_r); 277 + dig_vol_mult_l = fp_factor(fp_div(vol_db_l, 10, 16), 16); 278 + dig_vol_mult_r = fp_factor(fp_div(vol_db_r, 10, 16), 16); 310 279 } 311 280 #endif 312 281
+3 -1
firmware/target/hosted/pcm-alsa.h
··· 21 21 #define __PCM_ALSA_RB_H__ 22 22 23 23 #include <config.h> 24 + #include <limits.h> 24 25 25 26 #if defined(HAVE_ALSA_32BIT) 27 + 26 28 /* Set the PCM volume in dB: each sample with have this volume applied digitally 27 - * before being sent to ALSA. Volume must satisfy -43 <= dB <= 0 */ 29 + * before being sent to ALSA. Effective range -79 dB to 0 dB */ 28 30 void pcm_set_mixer_volume(int vol_db_l, int vol_db_r); 29 31 #endif 30 32
+1 -1
firmware/target/hosted/sonynwz/nwzlinux-codec.c
··· 415 415 printf(" set driver volume %d (%d dB)\n", drv_vol, curve->level[drv_vol] / 10); 416 416 nwz_set_driver_vol(drv_vol); 417 417 printf(" set digital volume %d dB\n", vol / 10); 418 - pcm_set_mixer_volume(vol / 10, vol / 10); 418 + pcm_set_mixer_volume(vol, vol); 419 419 } 420 420 421 421 void audiohw_close(void)
+3 -3
firmware/target/mips/ingenic_x1000/erosqnative/audiohw-erosqnative.c
··· 200 200 } 201 201 else /* PCM5102A */ 202 202 { 203 - l = l <= PCM5102A_VOLUME_MIN ? PCM_MUTE_LEVEL : (l / 20); 204 - r = r <= PCM5102A_VOLUME_MIN ? PCM_MUTE_LEVEL : (r / 20); 203 + l = l <= PCM5102A_VOLUME_MIN ? PCM_MUTE_LEVEL : l; 204 + r = r <= PCM5102A_VOLUME_MIN ? PCM_MUTE_LEVEL : r; 205 205 206 206 pcm_set_master_volume(l, r); 207 207 } ··· 214 214 es9018k2m_set_filter_roll_off(value); 215 215 } 216 216 } 217 - #endif /* !defined(BOOTLOADER) */ 217 + #endif /* !defined(BOOTLOADER) */