mirror of
				https://github.com/Rockbox/rockbox.git
				synced 2025-10-26 23:36:37 -04:00 
			
		
		
		
	DSP Compressor: Sidechain, Exponential Atk/Rls
This is an improvement to the current compressor which I have added to my own Sansa Fuze V2 build. I am submitting here in case others find it interesting. Features added to the existing compressor: Attack, Look-ahead, Sidechain Filtering. Exponential attack and release characteristic response. Benefits from adding missing features: Attack: Preserve perceived "brightness" of tone by letting onset transients come through at a higher level than the rest of the compressed program material. Look-ahead: With Attack comes clipping on the leading several cycles of a transient onset. With look-ahead function, this can be pre-emptively mitigated with a slower gain change (less distortion). Look-ahead limiting is implemented to prevent clipping while keeping gain change ramp to an interval near 3ms instead of instant attack. The existing compressor implementation distorts the leading edge of a transient by causing instant gain change, resulting in log() distortion. This sounds "woofy" to me. Exponential Attack/Release: eMore natural sounding. On attack, this is a true straight line of 10dB per attack interval. Release is a little different, however, sounds natural as an analog compressor. Sidechain Filtering: Mild high-pass filter reduces response to low frequency onsets. For example, a hard kick drum is less likely to make the whole of the program material appear to fade in and out. Combined with a moderate attack time, such a transient will ride through with minimal audible artifact. Overall these changes make dynamic music sound more "open", more natural. The goal of a compressor is to make dyanamic music sound louder without necessarily sounding as though it has been compressed. I believe these changes come closer to this goal. Enjoy. If not, I am enjoying it Change-Id: I664eace546c364b815b4dc9ed4a72849231a0eb2 Reviewed-on: http://gerrit.rockbox.org/626 Tested: Purling Nayuki <cyq.yzfl@gmail.com> Reviewed-by: Michael Giacomelli <giac2000@hotmail.com>
This commit is contained in:
		
							parent
							
								
									5b5f0755d6
								
							
						
					
					
						commit
						d0918b98fa
					
				
					 7 changed files with 323 additions and 91 deletions
				
			
		|  | @ -11910,6 +11910,23 @@ | |||
|     swcodec: "Soft Knee" | ||||
|   </voice> | ||||
| </phrase> | ||||
| <phrase> | ||||
|   id: LANG_COMPRESSOR_ATTACK | ||||
|   desc: in sound settings | ||||
|   user: core | ||||
|   <source> | ||||
|     *: none | ||||
|     swcodec: "Attack Time" | ||||
|   </source> | ||||
|   <dest> | ||||
|     *: none | ||||
|     swcodec: "Attack Time" | ||||
|   </dest> | ||||
|   <voice> | ||||
|     *: none | ||||
|     swcodec: "Attack Time" | ||||
|   </voice> | ||||
| </phrase> | ||||
| <phrase> | ||||
|   id: LANG_COMPRESSOR_RELEASE | ||||
|   desc: in sound settings | ||||
|  |  | |||
|  | @ -140,12 +140,15 @@ static int timestretch_callback(int action,const struct menu_item_ex *this_item) | |||
|     MENUITEM_SETTING(compressor_knee, | ||||
|                      &global_settings.compressor_settings.knee, | ||||
|                      lowlatency_callback); | ||||
|     MENUITEM_SETTING(compressor_attack, | ||||
|                      &global_settings.compressor_settings.attack_time, | ||||
|                      lowlatency_callback); | ||||
|     MENUITEM_SETTING(compressor_release, | ||||
|                      &global_settings.compressor_settings.release_time, | ||||
|                      lowlatency_callback); | ||||
|     MAKE_MENU(compressor_menu,ID2P(LANG_COMPRESSOR), NULL, Icon_NOICON, | ||||
|               &compressor_threshold, &compressor_gain, &compressor_ratio, | ||||
|               &compressor_knee, &compressor_release); | ||||
|               &compressor_knee, &compressor_attack, &compressor_release); | ||||
| #endif | ||||
| 
 | ||||
| #if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F) | ||||
|  |  | |||
|  | @ -1653,7 +1653,11 @@ const struct settings_list settings[] = { | |||
|     CHOICE_SETTING(F_SOUNDSETTING|F_NO_WRAP, compressor_settings.knee, | ||||
|                    LANG_COMPRESSOR_KNEE, 1, "compressor knee", | ||||
|                    "hard knee,soft knee", compressor_set, 2, | ||||
|                    ID2P(LANG_COMPRESSOR_HARD_KNEE), ID2P(LANG_COMPRESSOR_SOFT_KNEE)), | ||||
|                    ID2P(LANG_COMPRESSOR_HARD_KNEE), ID2P(LANG_COMPRESSOR_SOFT_KNEE)),  | ||||
|     INT_SETTING_NOWRAP(F_SOUNDSETTING, compressor_settings.attack_time, | ||||
|                        LANG_COMPRESSOR_ATTACK, 5, | ||||
|                        "compressor attack time", UNIT_MS, 0, 30, | ||||
|                        5, NULL, NULL, compressor_set),                   | ||||
|     INT_SETTING_NOWRAP(F_SOUNDSETTING, compressor_settings.release_time, | ||||
|                        LANG_COMPRESSOR_RELEASE, 500, | ||||
|                        "compressor release time", UNIT_MS, 100, 1000, | ||||
|  |  | |||
|  | @ -632,6 +632,7 @@ Vanja Cvelbar | |||
| Richard Quirk | ||||
| Kirill Stryaponoff | ||||
| Roman Poltoradnev | ||||
| Ryan Billing | ||||
| 
 | ||||
| The libmad team | ||||
| The wavpack team | ||||
|  |  | |||
|  | @ -23,44 +23,155 @@ | |||
| #include "fracmul.h" | ||||
| #include <string.h> | ||||
| 
 | ||||
| /* Define LOGF_ENABLE to enable logf output in this file */ | ||||
| /*#define LOGF_ENABLE*/ | ||||
| /* Define LOGF_ENABLE to enable logf output in this file
 | ||||
|  * #define LOGF_ENABLE | ||||
|  */ | ||||
| #include "logf.h" | ||||
| #include "dsp_proc_entry.h" | ||||
| #include "compressor.h" | ||||
| #include "dsp_misc.h" | ||||
| 
 | ||||
| #define UNITY (1L << 24)                    /* unity gain in S7.24 format */ | ||||
| #define MAX_DLY 960                         /* Max number of samples to delay | ||||
|                                                output (960 = 5ms @ 192 kHz) | ||||
|                                             */ | ||||
| #define MAX_CH  4                           /* Is there a good malloc() or equal | ||||
|                                                for rockbox? | ||||
|                                             */ | ||||
| #define DLY_TIME 3                          /* milliseconds */ | ||||
| 
 | ||||
| static struct compressor_settings curr_set; /* Cached settings */ | ||||
| 
 | ||||
| static int32_t comp_rel_slope IBSS_ATTR;   /* S7.24 format */ | ||||
| static int32_t comp_makeup_gain IBSS_ATTR; /* S7.24 format */ | ||||
| static int32_t comp_curve[66] IBSS_ATTR;   /* S7.24 format */ | ||||
| static int32_t release_gain IBSS_ATTR;     /* S7.24 format */ | ||||
| static int32_t comp_makeup_gain IBSS_ATTR;  /* S7.24 format */ | ||||
| static int32_t comp_curve[66] IBSS_ATTR;    /* S7.24 format */ | ||||
| static int32_t release_gain IBSS_ATTR;      /* S7.24 format */ | ||||
| static int32_t release_holdoff IBSS_ATTR;   /* S7.24 format */ | ||||
| 
 | ||||
| #define UNITY (1L << 24)                   /* unity gain in S7.24 format */ | ||||
| /* 1-pole filter coefficients for exponential attack/release times */ | ||||
| static int32_t rlsca IBSS_ATTR;             /* Release 'alpha' */ | ||||
| static int32_t rlscb IBSS_ATTR;             /* Release 'beta' */ | ||||
| 
 | ||||
| static int32_t attca IBSS_ATTR;             /* Attack 'alpha' */ | ||||
| static int32_t attcb IBSS_ATTR;             /* Attack 'beta'  */ | ||||
| 
 | ||||
| static int32_t limitca IBSS_ATTR;           /* Limiter Attack 'alpha' */ | ||||
| 
 | ||||
| /* 1-pole filter coefficients for sidechain pre-emphasis filters */ | ||||
| static int32_t hp1ca IBSS_ATTR;             /* hpf1 'alpha' */ | ||||
| static int32_t hp2ca IBSS_ATTR;             /* hpf2 'beta'  */ | ||||
| 
 | ||||
| /* 1-pole hp filter state variables for pre-emphasis filters */ | ||||
| static int32_t hpfx1 IBSS_ATTR;             /* hpf1 and hpf2 x[n-1] */ | ||||
| static int32_t hp1y1 IBSS_ATTR;             /* hpf2 y[n-1]  */ | ||||
| static int32_t hp2y1 IBSS_ATTR;             /* hpf2 y[n-1]  */ | ||||
| 
 | ||||
| /* Delay Line for look-ahead compression */ | ||||
| static int32_t labuf[MAX_CH][MAX_DLY];      /* look-ahead buffer */ | ||||
| static int32_t  delay_time; | ||||
| static int32_t  delay_write; | ||||
| static int32_t  delay_read; | ||||
| 
 | ||||
| /** 1-Pole LP Filter first coefficient computation
 | ||||
|  *  Returns S7.24 format integer used for "a" coefficient | ||||
|  *  rc: "RC Time Constant", or time to decay to 1/e | ||||
|  *  fs: Sampling Rate | ||||
|  *  Interpret attack and release time as an RC time constant | ||||
|  *    (time to decay to 1/e) | ||||
|  *  1-pole filters use approximation | ||||
|  *      a0 = 1/(fs*rc + 1) | ||||
|  *      b1 = 1.0 - a0 | ||||
|  *      fs = Sampling Rate | ||||
|  *      rc = Time to decay to 1/e | ||||
|  *  y[n] = a0*x[n] + b1*y[n-1] | ||||
|  * | ||||
|  *  According to simulation on Intel hardware | ||||
|  *  this algorithm produces < 2% error for rc < ~100ms | ||||
|  *  For rc 100ms - 1000ms, error approaches 0% | ||||
|  *  For compressor attack/release times, this is more than adequate. | ||||
|  * | ||||
|  *  Error was measured against the more rigorous computation: | ||||
|  *  a0 = 1.0 - e^(-1.0/(fs*rc)) | ||||
|  */ | ||||
| 
 | ||||
| int32_t get_lpf_coeff(int32_t rc, int32_t fs, int32_t rc_units) | ||||
| { | ||||
|     int32_t c = fs*rc; | ||||
|     c /= rc_units; | ||||
|     c += 1; | ||||
|     c = UNITY/c; | ||||
|     return c; | ||||
| } | ||||
| 
 | ||||
| /** Coefficients to get 10dB change per time period "rc"
 | ||||
|  *  from 1-pole LP filter topology | ||||
|  *  This function is better used to match behavior of | ||||
|  *  linear release which was implemented prior to implementation | ||||
|  *  of exponential attack/release function | ||||
|  */ | ||||
| 
 | ||||
| int32_t get_att_rls_coeff(int32_t rc, int32_t fs) | ||||
| { | ||||
|     int32_t c = UNITY/fs; | ||||
|     c *= 1152;              /* 1000 * 10/( 20*log10( 1/e ) ) */ | ||||
|     c /= rc; | ||||
|     return c; | ||||
| } | ||||
| 
 | ||||
| /** COMPRESSOR UPDATE
 | ||||
|  *  Called via the menu system to configure the compressor process */ | ||||
|  *  Called via the menu system to configure the compressor process | ||||
|  */ | ||||
| static bool compressor_update(struct dsp_config *dsp, | ||||
|                               const struct compressor_settings *settings) | ||||
| { | ||||
|     /* make settings values useful */ | ||||
|     int  threshold  = settings->threshold; | ||||
|     bool auto_gain  = settings->makeup_gain == 1; | ||||
|     int  threshold   = settings->threshold; | ||||
|     bool auto_gain   = settings->makeup_gain == 1; | ||||
|     static const int comp_ratios[] = { 2, 4, 6, 10, 0 }; | ||||
|     int  ratio      = comp_ratios[settings->ratio]; | ||||
|     bool soft_knee  = settings->knee == 1; | ||||
|     int  release    = settings->release_time * | ||||
|                             dsp_get_output_frequency(dsp) / 1000; | ||||
|     int  ratio       = comp_ratios[settings->ratio]; | ||||
|     bool soft_knee   = settings->knee == 1; | ||||
|     int32_t  release = settings->release_time; | ||||
|     int32_t  attack  = settings->attack_time; | ||||
| 
 | ||||
|     bool changed = settings == &curr_set; /* If frequency change */ | ||||
|     /* Compute Attack and Release Coefficients */ | ||||
|     int32_t fs =   dsp_get_output_frequency(dsp); | ||||
| 
 | ||||
|     /* Release */ | ||||
|     rlsca = get_att_rls_coeff(release, fs); | ||||
|     rlscb = UNITY - rlsca ; | ||||
| 
 | ||||
|     /* Attack */ | ||||
|     if(attack > 0) | ||||
|     { | ||||
|         attca = get_att_rls_coeff(attack, fs); | ||||
|         attcb = UNITY - attca ; | ||||
|     } | ||||
|     else { | ||||
|         attca = UNITY; | ||||
|         attcb = 0; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /* Sidechain pre-emphasis filter coefficients */ | ||||
|     hp1ca = fs + 0x003C1; /** The "magic" constant is 1/RC.  This filter
 | ||||
|                            *  cut-off is approximately 237 Hz | ||||
|                            */ | ||||
|     hp1ca = UNITY/hp1ca; | ||||
|     hp1ca *= fs; | ||||
| 
 | ||||
|     hp2ca = fs + 0x02065; /* The "magic" constant is 1/RC.  This filter
 | ||||
|                            * cut-off is approximately 2.18 kHz | ||||
|                            */ | ||||
|     hp2ca = UNITY/hp2ca; | ||||
|     hp2ca *= fs; | ||||
| 
 | ||||
|     bool changed = settings == &curr_set; /* If frequency changes */ | ||||
|     bool active  = threshold < 0; | ||||
| 
 | ||||
|     if (memcmp(settings, &curr_set, sizeof (curr_set))) | ||||
|     { | ||||
|         /* Compressor settings have changed since last call */ | ||||
|         changed = true; | ||||
|              | ||||
| 
 | ||||
| #if defined(ROCKBOX_HAS_LOGF) && defined(LOGF_ENABLE) | ||||
|         if (settings->threshold != curr_set.threshold) | ||||
|         { | ||||
|  | @ -91,6 +202,10 @@ static bool compressor_update(struct dsp_config *dsp, | |||
|         { | ||||
|             logf("   Compressor Release: %d", release); | ||||
|         } | ||||
|         if (settings->attack_time != cur_set.attack_time) | ||||
|         { | ||||
|             logf("   Compressor Attack: %d", attack); | ||||
|         } | ||||
| #endif | ||||
| 
 | ||||
|         curr_set = *settings; | ||||
|  | @ -125,18 +240,18 @@ static bool compressor_update(struct dsp_config *dsp, | |||
|         int32_t offset; /* S15.16 format */ | ||||
|     } db_curve[5]; | ||||
| 
 | ||||
|     /** Set up the shape of the compression curve first as decibel
 | ||||
|         values */ | ||||
|     /* db_curve[0] = bottom of knee
 | ||||
|                [1] = threshold | ||||
|                [2] = top of knee | ||||
|                [3] = 0 db input | ||||
|                [4] = ~+12db input (2 bits clipping overhead) */ | ||||
|     /** Set up the shape of the compression curve first as decibel values
 | ||||
|      *  db_curve[0] = bottom of knee | ||||
|      *          [1] = threshold | ||||
|      *          [2] = top of knee | ||||
|      *          [3] = 0 db input | ||||
|      *          [4] = ~+12db input (2 bits clipping overhead) | ||||
|      */ | ||||
| 
 | ||||
|     db_curve[1].db = threshold << 16; | ||||
|     if (soft_knee) | ||||
|     { | ||||
|         /* bottom of knee is 3dB below the threshold for soft knee*/ | ||||
|         /* bottom of knee is 3dB below the threshold for soft knee */ | ||||
|         db_curve[0].db = db_curve[1].db - (3 << 16); | ||||
|         /* top of knee is 3dB above the threshold for soft knee */ | ||||
|         db_curve[2].db = db_curve[1].db + (3 << 16); | ||||
|  | @ -175,24 +290,28 @@ static bool compressor_update(struct dsp_config *dsp, | |||
|     } | ||||
| 
 | ||||
|     /** Now set up the comp_curve table with compression offsets in the
 | ||||
|         form of gain factors in S7.24 format */ | ||||
|     /* comp_curve[0] is 0 (-infinity db) input */ | ||||
|      * form of gain factors in S7.24 format | ||||
|      * comp_curve[0] is 0 (-infinity db) input | ||||
|      */ | ||||
|     comp_curve[0] = UNITY; | ||||
|     /* comp_curve[1 to 63] are intermediate compression values 
 | ||||
|        corresponding to the 6 MSB of the input values of a non-clipped | ||||
|        signal */ | ||||
|     /** comp_curve[1 to 63] are intermediate compression values 
 | ||||
|      * corresponding to the 6 MSB of the input values of a non-clipped | ||||
|      * signal | ||||
|      */ | ||||
|     for (int i = 1; i < 64; i++) | ||||
|     { | ||||
|         /* db constants are stored as positive numbers;
 | ||||
|            make them negative here */ | ||||
|         /** db constants are stored as positive numbers;
 | ||||
|          * make them negative here | ||||
|          */ | ||||
|         int32_t this_db = -db[i]; | ||||
| 
 | ||||
|         /* no compression below the knee */ | ||||
|         if (this_db <= db_curve[0].db) | ||||
|             comp_curve[i] = UNITY; | ||||
| 
 | ||||
|         /* if soft knee and below top of knee,
 | ||||
|            interpolate along soft knee slope */ | ||||
|         /** if soft knee and below top of knee,
 | ||||
|          * interpolate along soft knee slope | ||||
|          */ | ||||
|         else if (soft_knee && (this_db <= db_curve[2].db)) | ||||
|             comp_curve[i] = fp_factor(fp_mul( | ||||
|                 ((this_db - db_curve[0].db) / 6), | ||||
|  | @ -204,14 +323,22 @@ static bool compressor_update(struct dsp_config *dsp, | |||
|                 fp_div((db_curve[1].db - this_db), db_curve[1].db, 16), | ||||
|                 db_curve[3].offset, 16), 16) << 8; | ||||
|     } | ||||
|     /* comp_curve[64] is the compression level of a maximum level,
 | ||||
|        non-clipped signal */ | ||||
|     /** comp_curve[64] is the compression level of a maximum level,
 | ||||
|      * non-clipped signal | ||||
|      */ | ||||
|     comp_curve[64] = fp_factor(db_curve[3].offset, 16) << 8; | ||||
| 
 | ||||
|     /* comp_curve[65] is the compression level of a maximum level,
 | ||||
|        clipped signal */ | ||||
|     /** comp_curve[65] is the compression level of a maximum level,
 | ||||
|      * clipped signal | ||||
|      */ | ||||
|     comp_curve[65] = fp_factor(db_curve[4].offset, 16) << 8; | ||||
| 
 | ||||
|     /** if using auto peak, then makeup gain is max offset -
 | ||||
|      * 3dB headroom | ||||
|      */ | ||||
|     comp_makeup_gain = auto_gain ? | ||||
|         fp_factor(-(db_curve[3].offset) - 0x4AC4, 16) << 8 : UNITY; | ||||
| 
 | ||||
| #if defined(ROCKBOX_HAS_LOGF) && defined(LOGF_ENABLE) | ||||
|     logf("\n   *** Compression Offsets ***"); | ||||
|     /* some settings for display only, not used in calculations */ | ||||
|  | @ -233,20 +360,10 @@ static bool compressor_update(struct dsp_config *dsp, | |||
|         if (i % 4 == 0) DEBUGF("\n"); | ||||
|     } | ||||
|     DEBUGF("\n"); | ||||
| 
 | ||||
|     logf("Makeup gain:\t%.6f", (float)comp_makeup_gain / UNITY); | ||||
| #endif | ||||
| 
 | ||||
|     /* if using auto peak, then makeup gain is max offset -
 | ||||
|        .1dB headroom */ | ||||
|     comp_makeup_gain = auto_gain ? | ||||
|         fp_factor(-(db_curve[3].offset) - 0x199A, 16) << 8 : UNITY; | ||||
|     logf("Makeup gain:\t%.6f", (float)comp_makeup_gain / UNITY); | ||||
| 
 | ||||
|     /* calculate per-sample gain change a rate of 10db over release time
 | ||||
|      */ | ||||
|     comp_rel_slope = 0xAF0BB2 / release; | ||||
|     logf("Release slope:\t%.6f", (float)comp_rel_slope / UNITY); | ||||
| 
 | ||||
|     release_gain = UNITY; | ||||
|     return active; | ||||
| } | ||||
| 
 | ||||
|  | @ -258,39 +375,41 @@ static inline int32_t get_compression_gain(struct sample_format *format, | |||
|                                            int32_t sample) | ||||
| { | ||||
|     const int frac_bits_offset = format->frac_bits - 15; | ||||
|      | ||||
| 
 | ||||
|     /* sample must be positive */ | ||||
|     if (sample < 0) | ||||
|         sample = -(sample + 1); | ||||
|          | ||||
| 
 | ||||
|     /* shift sample into 15 frac bit range */ | ||||
|     if (frac_bits_offset > 0) | ||||
|         sample >>= frac_bits_offset; | ||||
|     if (frac_bits_offset < 0) | ||||
|         sample <<= -frac_bits_offset; | ||||
|      | ||||
| 
 | ||||
|     /* normal case: sample isn't clipped */ | ||||
|     if (sample < (1 << 15)) | ||||
|     { | ||||
|         /* index is 6 MSB, rem is 9 LSB */ | ||||
|         int index = sample >> 9; | ||||
|         int32_t rem = (sample & 0x1FF) << 22; | ||||
|          | ||||
|         /* interpolate from the compression curve:
 | ||||
|             higher gain - ((rem / (1 << 31)) * (higher gain - lower gain)) */ | ||||
| 
 | ||||
|         /** interpolate from the compression curve:
 | ||||
|          * higher gain - ((rem / (1 << 31)) * (higher gain - lower gain)) | ||||
|          */ | ||||
|         return comp_curve[index] - (FRACMUL(rem, | ||||
|             (comp_curve[index] - comp_curve[index + 1]))); | ||||
|     } | ||||
|     /* sample is somewhat clipped, up to 2 bits of overhead */ | ||||
|     if (sample < (1 << 17)) | ||||
|     { | ||||
|         /* straight interpolation:
 | ||||
|             higher gain - ((clipped portion of sample * 4/3 | ||||
|             / (1 << 31)) * (higher gain - lower gain)) */ | ||||
|         /** straight interpolation:
 | ||||
|          *  higher gain - ((clipped portion of sample * 4/3 | ||||
|          *  / (1 << 31)) * (higher gain - lower gain)) | ||||
|          */ | ||||
|         return comp_curve[64] - (FRACMUL(((sample - (1 << 15)) / 3) << 16, | ||||
|             (comp_curve[64] - comp_curve[65]))); | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     /* sample is too clipped, return invalid value */ | ||||
|     return -1; | ||||
| } | ||||
|  | @ -322,55 +441,115 @@ static void compressor_process(struct dsp_proc_entry *this, | |||
| 
 | ||||
|     while (count-- > 0) | ||||
|     { | ||||
|         /* use lowest (most compressed) gain factor of the output buffer
 | ||||
|            sample pair for both samples (mono is also handled correctly here) | ||||
|          */ | ||||
| 
 | ||||
|         /* Use the average of the channels */ | ||||
| 
 | ||||
|         int32_t sample_gain = UNITY; | ||||
|         int32_t x = 0; | ||||
|         int32_t tmpx = 0; | ||||
|         int32_t in_buf_max_level = 0; | ||||
|         for (int ch = 0; ch < num_chan; ch++) | ||||
|         { | ||||
|             int32_t this_gain = get_compression_gain(&buf->format, *in_buf[ch]); | ||||
|             if (this_gain < sample_gain) | ||||
|                 sample_gain = this_gain; | ||||
|             tmpx = *in_buf[ch]; | ||||
|             x += tmpx; | ||||
|             labuf[ch][delay_write] = tmpx; | ||||
|             /* Limiter detection */ | ||||
|             if(tmpx < 0) tmpx = -(tmpx + 1); | ||||
|             if(tmpx > in_buf_max_level) in_buf_max_level = tmpx; | ||||
|         } | ||||
|          | ||||
|         /* perform release slope; skip if no compression and no release slope
 | ||||
| 
 | ||||
|         /** Divide it by the number of channels, roughly
 | ||||
|          *  It will be exact if the number of channels a power of 2 | ||||
|          *  it will be imperfect otherwise.  Real division costs too | ||||
|          *  much here, and most of the time it will be 2 channels (stereo) | ||||
|          */ | ||||
|         if ((sample_gain != UNITY) || (release_gain != UNITY)) | ||||
|         { | ||||
|             /* if larger offset than previous slope, start new release slope
 | ||||
|              */ | ||||
|             if ((sample_gain <= release_gain) && (sample_gain > 0)) | ||||
|             { | ||||
|         x >>= (num_chan >> 1); | ||||
| 
 | ||||
|         /** 1p HP Filters: y[n] = a*(y[n-1] + x - x[n-1])
 | ||||
|          *  Zero and Pole in the same place to reduce computation | ||||
|          *  Run the first pre-emphasis filter | ||||
|          */ | ||||
|         int32_t tmp1 = x - hpfx1 + hp1y1; | ||||
|         hp1y1 = FRACMUL_SHL(hp1ca, tmp1, 7); | ||||
| 
 | ||||
|         /* Run the second pre-emphasis filter */ | ||||
|         tmp1 = x - hpfx1 + hp2y1; | ||||
|         hp2y1 = FRACMUL_SHL(hp2ca, tmp1, 7); | ||||
|         hpfx1 = x; | ||||
| 
 | ||||
|         /* Apply weighted sum to the pre-emphasis network */ | ||||
|         sample_gain = (x>>1) + hp1y1 + (hp2y1<<1); /* x/2 + hp1 + 2*hp2 */ | ||||
|         sample_gain >>= 1; | ||||
|         sample_gain += sample_gain >> 1; | ||||
|         sample_gain = get_compression_gain(&buf->format, sample_gain); | ||||
| 
 | ||||
|         /* Exponential Attack and Release */ | ||||
| 
 | ||||
|        if ((sample_gain <= release_gain) && (sample_gain > 0)) | ||||
|        { | ||||
|            /* Attack */ | ||||
|            if(attca != UNITY) | ||||
|            { | ||||
|                int32_t this_gain = FRACMUL_SHL(release_gain, attcb, 7); | ||||
|                this_gain +=  FRACMUL_SHL(sample_gain, attca, 7); | ||||
|                release_gain = this_gain; | ||||
|            } | ||||
|            else | ||||
|            { | ||||
|                 release_gain = sample_gain; | ||||
|            } | ||||
|            /** reset it to delay time so it cannot release before the
 | ||||
|             *  delayed signal releases | ||||
|             */ | ||||
|            release_holdoff = delay_time;    | ||||
|        } | ||||
|        else | ||||
|        /* Reverse exponential decay to current gain value */ | ||||
|        { | ||||
|             /* Don't start release while output is still above thresh */ | ||||
|             if(release_holdoff > 0) | ||||
|             { | ||||
|                 release_holdoff--; | ||||
|             } | ||||
|             else | ||||
|             /* keep sloping towards unity gain (and ignore invalid value) */ | ||||
|             { | ||||
|                 release_gain += comp_rel_slope; | ||||
|                 if (release_gain > UNITY) | ||||
|                 { | ||||
|                     release_gain = UNITY; | ||||
|                 } | ||||
|                /* Release */ | ||||
|                int32_t this_gain = FRACMUL_SHL(release_gain, rlscb, 7); | ||||
|                this_gain +=  FRACMUL_SHL(sample_gain,rlsca,7); | ||||
|                release_gain = this_gain; | ||||
|             } | ||||
| 
 | ||||
|        } | ||||
| 
 | ||||
|         /** total gain factor is the product of release gain and makeup gain,
 | ||||
|          *  but avoid computation if possible | ||||
|          */ | ||||
| 
 | ||||
|         int32_t total_gain = FRACMUL_SHL(release_gain, comp_makeup_gain, 7); | ||||
| 
 | ||||
|         /* Look-ahead limiter */ | ||||
|         int32_t test_gain = FRACMUL_SHL(total_gain, in_buf_max_level, 3); | ||||
|         if( test_gain > UNITY) | ||||
|         { | ||||
|             release_gain -= limitca; | ||||
|         } | ||||
|          | ||||
|         /* total gain factor is the product of release gain and makeup gain,
 | ||||
|            but avoid computation if possible */ | ||||
|         int32_t total_gain = ((release_gain == UNITY) ? comp_makeup_gain : | ||||
|             (comp_makeup_gain == UNITY) ? release_gain : | ||||
|                 FRACMUL_SHL(release_gain, comp_makeup_gain, 7)); | ||||
|          | ||||
|         /* Implement the compressor: apply total gain factor (if any) to the
 | ||||
|            output buffer sample pair/mono sample */ | ||||
| 
 | ||||
|         /** Implement the compressor: apply total gain factor (if any) to the
 | ||||
|          *  output buffer sample pair/mono sample | ||||
|          */ | ||||
|         if (total_gain != UNITY) | ||||
|         { | ||||
|             for (int ch = 0; ch < num_chan; ch++) | ||||
|             { | ||||
|                 *in_buf[ch] = FRACMUL_SHL(total_gain, *in_buf[ch], 7); | ||||
|               *in_buf[ch]  = FRACMUL_SHL(total_gain, labuf[ch][delay_read], 7); | ||||
|             } | ||||
|         } | ||||
|         in_buf[0]++; | ||||
|         in_buf[1]++; | ||||
|         delay_write++; | ||||
|         delay_read++; | ||||
|         if(delay_write >= MAX_DLY) delay_write = 0; | ||||
|         if(delay_read >= MAX_DLY) delay_read = 0; | ||||
|     } | ||||
| 
 | ||||
|     (void)this; | ||||
|  | @ -382,6 +561,8 @@ static intptr_t compressor_configure(struct dsp_proc_entry *this, | |||
|                                      unsigned int setting, | ||||
|                                      intptr_t value) | ||||
| { | ||||
|     int i,j; | ||||
| 
 | ||||
|     switch (setting) | ||||
|     { | ||||
|     case DSP_PROC_INIT: | ||||
|  | @ -394,7 +575,29 @@ static intptr_t compressor_configure(struct dsp_proc_entry *this, | |||
|         /* Fall-through */ | ||||
|     case DSP_RESET: | ||||
|     case DSP_FLUSH: | ||||
| 
 | ||||
|         release_gain = UNITY; | ||||
|         for(i=0; i<MAX_CH; i++) | ||||
|         { | ||||
|             for(j=0; j<MAX_DLY; j++) | ||||
|             { | ||||
|                 labuf[i][j] = 0;  /* All Silence */ | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         /* Delay Line Read/Write Pointers */ | ||||
|         int32_t fs =   dsp_get_output_frequency(dsp); | ||||
|         delay_read = 0; | ||||
|         delay_write = (DLY_TIME*fs/1000); | ||||
|         if(delay_write >= MAX_DLY) { | ||||
|             delay_write = MAX_DLY - 1; /* Limit to the max allocated buffer */ | ||||
|         } | ||||
| 
 | ||||
|         delay_time = delay_write; | ||||
|         release_holdoff = delay_write; | ||||
|         limitca = get_att_rls_coeff(DLY_TIME, fs); /** Attack time for
 | ||||
|                                                     *  look-ahead limiter | ||||
|                                                     */ | ||||
|         break; | ||||
| 
 | ||||
|     case DSP_SET_OUT_FREQUENCY: | ||||
|  |  | |||
|  | @ -28,6 +28,7 @@ struct compressor_settings | |||
|     int ratio; | ||||
|     int knee; | ||||
|     int release_time; | ||||
|     int attack_time; | ||||
| }; | ||||
| 
 | ||||
| void dsp_set_compressor(const struct compressor_settings *settings); | ||||
|  |  | |||
|  | @ -603,6 +603,9 @@ non-compressed signal to a compressed signal.  Hard Knee means that the | |||
| transition occurs precisely at the threshold.  The Soft Knee setting smoothes | ||||
| the transition from plus or minus three decibels around the threshold. | ||||
| 
 | ||||
| The \setting{Attack Time} setting sets the delay in milliseconds between the | ||||
| input signal exceeding the activation threshold and acting upon it. | ||||
| 
 | ||||
| The \setting{Release Time} setting sets the recovery time after the signal is | ||||
| compressed.  Once the compressor determines that compression is necessary, | ||||
| the input signal is reduced appropriately, but the gain isn't allowed to | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue