Index: apps/settings.c =================================================================== --- apps/settings.c (revision 21906) +++ apps/settings.c (working copy) @@ -940,6 +940,7 @@ dsp_dither_enable(global_settings.dithering_enabled); dsp_timestretch_enable(global_settings.timestretch_enabled); + dsp_set_compressor(global_settings.compressor_level); #endif #ifdef HAVE_SPDIF_POWER Index: apps/lang/english.lang =================================================================== --- apps/lang/english.lang (revision 21906) +++ apps/lang/english.lang (working copy) @@ -12655,3 +12655,20 @@ pitchscreen: "Rate" + + id: LANG_COMPRESSOR + desc: in sound settings + user: core + + *: none + swcodec: "Compressor Preamp" + + + *: none + swcodec: "Compressor Preamp" + + + *: none + swcodec: "Compressor Preamp" + + Index: apps/settings.h =================================================================== --- apps/settings.h (revision 21906) +++ apps/settings.h (working copy) @@ -744,6 +744,7 @@ bool pitch_mode_semitone; #if CONFIG_CODEC == SWCODEC bool pitch_mode_timestretch; + int compressor_level; #endif #endif /* If values are just added to the end, no need to bump plugin API Index: apps/menus/sound_menu.c =================================================================== --- apps/menus/sound_menu.c (revision 21906) +++ apps/menus/sound_menu.c (working copy) @@ -105,6 +105,8 @@ &global_settings.timestretch_enabled, timestretch_callback); MENUITEM_SETTING(dithering_enabled, &global_settings.dithering_enabled, lowlatency_callback); + MENUITEM_SETTING(compressor_level, + &global_settings.compressor_level, lowlatency_callback); #endif #if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F) @@ -138,6 +140,7 @@ #if CONFIG_CODEC == SWCODEC ,&crossfeed_menu, &equalizer_menu, &dithering_enabled ,×tretch_enabled + ,&compressor_level #endif #if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F) ,&loudness,&avc,&superbass,&mdb_enable,&mdb_strength Index: apps/fixedpoint.c =================================================================== --- apps/fixedpoint.c (revision 21906) +++ apps/fixedpoint.c (working copy) @@ -400,4 +400,5 @@ /* factor = 10 ^ (decibels / 20) */ return fp_exp10(FP_DIV_FRAC(decibels, (20L << fracbits)), fracbits); } + #endif /* !PLUGIN and !CODEC */ Index: apps/dsp.c =================================================================== --- apps/dsp.c (revision 21906) +++ apps/dsp.c (working copy) @@ -36,6 +36,10 @@ #include "fixedpoint.h" #include "fracmul.h" +/* Define LOGF_ENABLE to enable logf output in this file */ +#define LOGF_ENABLE +#include "logf.h" + /* 16-bit samples are scaled based on these constants. The shift should be * no more than 15. */ @@ -165,6 +169,7 @@ int32_t tdspeed_percent; /* Speed% * PITCH_SPEED_PRECISION */ bool tdspeed_active; /* Timestretch is in use */ int frac_bits; + long compressor_preamp; /* compressor amp gain in S7.24 format */ #ifdef HAVE_SW_TONE_CONTROLS /* Filter struct for software bass/treble controls */ struct eqfilter tone_filter; @@ -180,6 +185,7 @@ channels_process_fn_type apply_crossfeed; channels_process_fn_type eq_process; channels_process_fn_type channels_process; + channels_process_fn_type compressor_process; }; /* General DSP config */ @@ -890,6 +896,13 @@ (long) (((int64_t) dsp->data.gain * eq_precut) >> 24); } + /* only preamp for the compressor if the compressor is active and sample + * depth allows safe pre-amping (12 dB is OK with 29 or less frac bits) */ + if ((dsp->compressor_preamp) && (dsp->frac_bits <= 29)) + { + dsp->data.gain = fp_mul(dsp->data.gain, dsp->compressor_preamp, 24); + } + if (dsp->data.gain == DEFAULT_GAIN) { dsp->data.gain = 0; @@ -1015,6 +1028,138 @@ dsp_sw_cross = cross << 8; } +static void compressor_process(int count, int32_t *buf[]) +{ + bool neg[2]; + int i, larger; + int32_t *ext_buf[2], sample[2], x; + const int fracbits = AUDIO_DSP.frac_bits; + const long one_half = 1L << (fracbits - 1); + long gain; + + /* don't process if calculation might overflow */ + if (AUDIO_DSP.frac_bits > 29) return; + + for (i = 0; i < 2; i++) + ext_buf[i] = buf[i]; + + while (count-- > 0) + { + /* + if (sample < -0.5) + sample = tanh((sample + 0.5) / (1-0.5)) * (1-0.5) - 0.5; + else if (sample > 0.5) + sample = tanh((sample - 0.5) / (1-0.5)) * (1-0.5) + 0.5; + */ + for (i = 0; i < 2; i++) + { + sample[i] = *ext_buf[i]; + if ((neg[i] = (sample[i] < 0))) + sample[i] = -(sample[i] + 1); + } + larger = (sample[1] > sample[0]) ? 1 : 0; + x = sample[larger]; + + /* large gains will overflow so just clip */ + if (x >= (3L << fracbits)) + x = (1L << fracbits) - 1; + + /* compress samples above -6dB using + tanh((sample - 0.5) / (1-0.5)) * (1-0.5) + 0.5 */ + else if (x > one_half) + { + /* scale to 16 frac bits */ + if (fracbits > 16) + x >>= (fracbits - 16); + else if (fracbits < 16) + x <<= (16 - fracbits); + + /* calculate (x - 0.5) * 2, then double again for + use in tanh(x/2) function below */ + x = (x - (1L << 15)) << 2; + + /* Calculate exp(x) using 16 frac bits. Code taken from + http://www.quinapalus.com/efunc.html*/ + long t,y; + const long one = (1L << 16); + y=0x00010000; + t=x-0x58B91; if(t>=0) x=t,y<<=8; + t=x-0x2C5C8; if(t>=0) x=t,y<<=4; + t=x-0x162E4; if(t>=0) x=t,y<<=2; + t=x-0x0B172; if(t>=0) x=t,y<<=1; + t=x-0x067CD; if(t>=0) x=t,y+=y>>1; + t=x-0x03920; if(t>=0) x=t,y+=y>>2; + t=x-0x01E27; if(t>=0) x=t,y+=y>>3; + t=x-0x00F85; if(t>=0) x=t,y+=y>>4; + t=x-0x007E1; if(t>=0) x=t,y+=y>>5; + t=x-0x003F8; if(t>=0) x=t,y+=y>>6; + t=x-0x001FE; if(t>=0) x=t,y+=y>>7; + if(x&0x000100) y+=y>>8; + if(x&0x000080) y+=y>>9; + if(x&0x000040) y+=y>>10; + if(x&0x000020) y+=y>>11; + if(x&0x000010) y+=y>>12; + if(x&0x000008) y+=y>>13; + if(x&0x000004) y+=y>>14; + if(x&0x000002) y+=y>>15; + if(x&0x000001) y+=y>>16; + x = fp_mul(x + one, y, 16); + + /* tanh(x) = (exp(2x)-1)/(exp(2x)+1) + x was already doubled above */ + x = fp_div(x - one, x + one, 16); + + /* scale back to fracbits */ + if (fracbits > 16) + x <<= (fracbits - 16); + else if (fracbits < 16) + x >>= (16 - fracbits); + + /* x = x/2 + 0.5 */ + x = (x >> 1) + one_half; + + /* guard against rounding error overflows */ + if (x >= (1L << fracbits)) + x = (1L << fracbits) - 1; + + } + + gain = fp_div(x, sample[larger], fracbits); + + for (i = 0; i < 2; i++) + { + sample[i] = fp_mul(sample[i], gain, fracbits); + if (neg[i]) + sample[i] = -sample[i] - 1; + *ext_buf[i]++ = sample[i]; + } + } +} + +void dsp_set_compressor(int compressor_level) +{ + if (compressor_level > 0) + { + /* enable compressor process */ + AUDIO_DSP.compressor_process = compressor_process; + /* compressor preamp is a gain factor in S7.24 format */ + AUDIO_DSP.compressor_preamp = + fp_factor((((long)compressor_level << 24) / 10), 24); + } + else + { + /* disable compressor process*/ + AUDIO_DSP.compressor_process = NULL; + AUDIO_DSP.compressor_preamp = 0; + } + + set_gain(&AUDIO_DSP); + + logf("Compressor enable: %s\tCompressor amp: %.8f", + AUDIO_DSP.compressor_process ? "Yes" : "No", + (float)AUDIO_DSP.compressor_preamp / (1 << 24)); +} + /** * Implements the different channel configurations and stereo width. */ @@ -1236,6 +1381,9 @@ if (dsp->channels_process) dsp->channels_process(chunk, t2); + if (dsp->compressor_process) + dsp->compressor_process(chunk, t2); + dsp->output_samples(chunk, &dsp->data, (const int32_t **)t2, (int16_t *)dst); written += chunk; Index: apps/settings_list.c =================================================================== --- apps/settings_list.c (revision 21906) +++ apps/settings_list.c (working copy) @@ -1236,6 +1236,12 @@ /* timestretch */ OFFON_SETTING(F_SOUNDSETTING, timestretch_enabled, LANG_TIMESTRETCH, false, "timestretch enabled", dsp_timestretch_enable), + + /* compressor */ + INT_SETTING_NOWRAP(F_SOUNDSETTING, compressor_level, + LANG_COMPRESSOR, 0, + "compressor level", UNIT_DB, 0, MAX_COMPRESSOR_GAIN, + 5, db_format, get_dec_talkid, dsp_set_compressor), #endif #ifdef HAVE_WM8758 SOUND_SETTING(F_NO_WRAP, bass_cutoff, LANG_BASS_CUTOFF, Index: apps/dsp.h =================================================================== --- apps/dsp.h (revision 21906) +++ apps/dsp.h (working copy) @@ -26,6 +26,7 @@ #include #define NATIVE_FREQUENCY 44100 +#define MAX_COMPRESSOR_GAIN 80 /* 8 dB */ enum { STEREO_INTERLEAVED = 0, @@ -88,5 +89,6 @@ void dsp_set_timestretch(int32_t percent); int32_t dsp_get_timestretch(void); int dsp_callback(int msg, intptr_t param); +void dsp_set_compressor(int compressor_level); #endif