1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2005 Miika Pekkarinen
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
27 #include "compressor.h"
30 #include "replaygain.h"
32 #include "core_alloc.h"
33 #include "fixedpoint.h"
36 /* Define LOGF_ENABLE to enable logf output in this file */
37 /*#define LOGF_ENABLE*/
40 /* 16-bit samples are scaled based on these constants. The shift should be
44 #define WORD_FRACBITS 27
46 #define NATIVE_DEPTH 16
47 #define SMALL_SAMPLE_BUF_COUNT 128 /* Per channel */
48 #define DEFAULT_GAIN 0x01000000
50 /* enums to index conversion properly with stereo mode and other settings */
53 SAMPLE_INPUT_LE_NATIVE_I_STEREO
= STEREO_INTERLEAVED
,
54 SAMPLE_INPUT_LE_NATIVE_NI_STEREO
= STEREO_NONINTERLEAVED
,
55 SAMPLE_INPUT_LE_NATIVE_MONO
= STEREO_MONO
,
56 SAMPLE_INPUT_GT_NATIVE_I_STEREO
= STEREO_INTERLEAVED
+ STEREO_NUM_MODES
,
57 SAMPLE_INPUT_GT_NATIVE_NI_STEREO
= STEREO_NONINTERLEAVED
+ STEREO_NUM_MODES
,
58 SAMPLE_INPUT_GT_NATIVE_MONO
= STEREO_MONO
+ STEREO_NUM_MODES
,
59 SAMPLE_INPUT_GT_NATIVE_1ST_INDEX
= STEREO_NUM_MODES
64 SAMPLE_OUTPUT_MONO
= 0,
66 SAMPLE_OUTPUT_DITHERED_MONO
,
67 SAMPLE_OUTPUT_DITHERED_STEREO
73 long error
[3]; /* 00h */
74 long random
; /* 0ch */
80 int32_t gain
; /* 00h - Direct path gain */
81 int32_t coefs
[3]; /* 04h - Coefficients for the shelving filter */
82 int32_t history
[4]; /* 10h - Format is x[n - 1], y[n - 1] for both channels */
83 int32_t delay
[13][2]; /* 20h */
84 int32_t *index
; /* 88h - Current pointer into the delay line */
88 /* Current setup is one lowshelf filters three peaking filters and one
89 * highshelf filter. Varying the number of shelving filters make no sense,
90 * but adding peaking filters is possible.
94 char enabled
[5]; /* 00h - Flags for active filters */
95 struct eqfilter filters
[5]; /* 08h - packing is 4? */
99 /* Include header with defines which functions are implemented in assembly
100 code for the target */
103 /* Typedefs keep things much neater in this case */
104 typedef void (*sample_input_fn_type
)(int count
, const char *src
[],
106 typedef int (*resample_fn_type
)(int count
, struct dsp_data
*data
,
107 const int32_t *src
[], int32_t *dst
[]);
108 typedef void (*sample_output_fn_type
)(int count
, struct dsp_data
*data
,
109 const int32_t *src
[], int16_t *dst
);
111 /* Single-DSP channel processing in place */
112 typedef void (*channels_process_fn_type
)(int count
, int32_t *buf
[]);
113 /* DSP local channel processing in place */
114 typedef void (*channels_process_dsp_fn_type
)(int count
, struct dsp_data
*data
,
118 ***************************************************************************/
122 struct dsp_data data
; /* Config members for use in external routines */
123 long codec_frequency
; /* Sample rate of data coming from the codec */
124 long frequency
; /* Effective sample rate after pitch shift (if any) */
128 int32_t tdspeed_percent
; /* Speed% * PITCH_SPEED_PRECISION */
129 #ifdef HAVE_PITCHSCREEN
130 bool tdspeed_active
; /* Timestretch is in use */
132 #ifdef HAVE_SW_TONE_CONTROLS
133 /* Filter struct for software bass/treble controls */
134 struct eqfilter tone_filter
;
136 /* Functions that change depending upon settings - NULL if stage is
138 sample_input_fn_type input_samples
;
139 resample_fn_type resample
;
140 sample_output_fn_type output_samples
;
141 /* These will be NULL for the voice codec and is more economical that
143 channels_process_dsp_fn_type apply_gain
;
144 channels_process_fn_type apply_crossfeed
;
145 channels_process_fn_type eq_process
;
146 channels_process_fn_type channels_process
;
147 channels_process_dsp_fn_type compressor_process
;
150 /* General DSP config */
151 static struct dsp_config dsp_conf
[2] IBSS_ATTR
; /* 0=A, 1=V */
153 static struct dither_data dither_data
[2] IBSS_ATTR
; /* 0=left, 1=right */
154 static long dither_mask IBSS_ATTR
;
155 static long dither_bias IBSS_ATTR
;
157 struct crossfeed_data crossfeed_data IDATA_ATTR
= /* A */
159 .index
= (int32_t *)crossfeed_data
.delay
163 static struct eq_state eq_data
; /* A */
165 /* Software tone controls */
166 #ifdef HAVE_SW_TONE_CONTROLS
167 static int prescale
; /* A/V */
168 static int bass
; /* A/V */
169 static int treble
; /* A/V */
172 /* Settings applicable to audio codec only */
173 #ifdef HAVE_PITCHSCREEN
174 static int32_t pitch_ratio
= PITCH_SPEED_100
;
175 static int big_sample_locks
;
177 static int channels_mode
;
180 static bool dither_enabled
;
181 static long eq_precut
;
182 static long track_gain
;
183 static bool new_gain
;
184 static long album_gain
;
185 static long track_peak
;
186 static long album_peak
;
187 static long replaygain
;
188 static bool crossfeed_enabled
;
190 #define AUDIO_DSP (dsp_conf[CODEC_IDX_AUDIO])
191 #define VOICE_DSP (dsp_conf[CODEC_IDX_VOICE])
193 /* The internal format is 32-bit samples, non-interleaved, stereo. This
194 * format is similar to the raw output from several codecs, so the amount
195 * of copying needed is minimized for that case.
198 #define RESAMPLE_RATIO 4 /* Enough for 11,025 Hz -> 44,100 Hz */
199 #define SMALL_RESAMPLE_BUF_COUNT (SMALL_SAMPLE_BUF_COUNT * RESAMPLE_RATIO)
200 #define BIG_SAMPLE_BUF_COUNT SMALL_RESAMPLE_BUF_COUNT
201 #define BIG_RESAMPLE_BUF_COUNT (BIG_SAMPLE_BUF_COUNT * RESAMPLE_RATIO)
203 static int32_t small_sample_buf
[2][SMALL_SAMPLE_BUF_COUNT
] IBSS_ATTR
;
204 static int32_t small_resample_buf
[2][SMALL_RESAMPLE_BUF_COUNT
] IBSS_ATTR
;
206 #ifdef HAVE_PITCHSCREEN
207 static int32_t (* big_sample_buf
)[BIG_SAMPLE_BUF_COUNT
] = NULL
;
208 static int32_t (* big_resample_buf
)[BIG_RESAMPLE_BUF_COUNT
] = NULL
;
211 static int sample_buf_count
= SMALL_SAMPLE_BUF_COUNT
;
212 static int32_t *sample_buf
[2] = { small_sample_buf
[0], small_sample_buf
[1] };
213 static int resample_buf_count
= SMALL_RESAMPLE_BUF_COUNT
;
214 static int32_t *resample_buf
[2] = { small_resample_buf
[0], small_resample_buf
[1] };
216 #ifdef HAVE_PITCHSCREEN
217 int32_t sound_get_pitch(void)
222 void sound_set_pitch(int32_t percent
)
224 pitch_ratio
= percent
;
225 dsp_configure(&AUDIO_DSP
, DSP_SWITCH_FREQUENCY
,
226 AUDIO_DSP
.codec_frequency
);
229 static void tdspeed_set_pointers( bool time_stretch_active
)
231 if( time_stretch_active
)
233 sample_buf_count
= BIG_SAMPLE_BUF_COUNT
;
234 resample_buf_count
= BIG_RESAMPLE_BUF_COUNT
;
235 sample_buf
[0] = big_sample_buf
[0];
236 sample_buf
[1] = big_sample_buf
[1];
237 resample_buf
[0] = big_resample_buf
[0];
238 resample_buf
[1] = big_resample_buf
[1];
242 sample_buf_count
= SMALL_SAMPLE_BUF_COUNT
;
243 resample_buf_count
= SMALL_RESAMPLE_BUF_COUNT
;
244 sample_buf
[0] = small_sample_buf
[0];
245 sample_buf
[1] = small_sample_buf
[1];
246 resample_buf
[0] = small_resample_buf
[0];
247 resample_buf
[1] = small_resample_buf
[1];
251 static void tdspeed_setup(struct dsp_config
*dspc
)
253 /* Assume timestretch will not be used */
254 dspc
->tdspeed_active
= false;
256 tdspeed_set_pointers( false );
258 if (!dsp_timestretch_available())
259 return; /* Timestretch not enabled or buffer not allocated */
261 if (dspc
->tdspeed_percent
== 0)
262 dspc
->tdspeed_percent
= PITCH_SPEED_100
;
265 dspc
->codec_frequency
== 0 ? NATIVE_FREQUENCY
: dspc
->codec_frequency
,
266 dspc
->stereo_mode
!= STEREO_MONO
,
267 dspc
->tdspeed_percent
))
268 return; /* Timestretch not possible or needed with these parameters */
270 /* Timestretch is to be used */
271 dspc
->tdspeed_active
= true;
273 tdspeed_set_pointers( true );
277 static int move_callback(int handle
, void* current
, void* new)
279 (void)handle
;(void)current
;
281 if ( big_sample_locks
> 0 )
282 return BUFLIB_CB_CANNOT_MOVE
;
284 big_sample_buf
= new;
286 /* no allocation without timestretch enabled */
287 tdspeed_set_pointers( true );
291 static void lock_sample_buf( bool lock
)
299 static struct buflib_callbacks ops
= {
300 .move_callback
= move_callback
,
301 .shrink_callback
= NULL
,
305 void dsp_timestretch_enable(bool enabled
)
307 /* Hook to set up timestretch buffer on first call to settings_apply() */
308 static int handle
= -1;
312 return; /* already allocated and enabled */
314 /* Set up timestretch buffers */
315 big_sample_buf
= &small_resample_buf
[0];
316 handle
= core_alloc_ex("resample buf",
317 2 * BIG_RESAMPLE_BUF_COUNT
* sizeof(int32_t),
319 big_sample_locks
= 0;
320 enabled
= handle
>= 0;
324 /* success, now setup tdspeed */
325 big_resample_buf
= core_get_data(handle
);
328 tdspeed_setup(&AUDIO_DSP
);
334 dsp_set_timestretch(PITCH_SPEED_100
);
341 big_sample_buf
= NULL
;
345 void dsp_set_timestretch(int32_t percent
)
347 AUDIO_DSP
.tdspeed_percent
= percent
;
348 tdspeed_setup(&AUDIO_DSP
);
351 int32_t dsp_get_timestretch()
353 return AUDIO_DSP
.tdspeed_percent
;
356 bool dsp_timestretch_available()
358 return (global_settings
.timestretch_enabled
&& big_sample_buf
);
360 #endif /* HAVE_PITCHSCREEN */
362 /* Convert count samples to the internal format, if needed. Updates src
363 * to point past the samples "consumed" and dst is set to point to the
364 * samples to consume. Note that for mono, dst[0] equals dst[1], as there
365 * is no point in processing the same data twice.
368 /* convert count 16-bit mono to 32-bit mono */
369 static void sample_input_lte_native_mono(
370 int count
, const char *src
[], int32_t *dst
[])
372 const int16_t *s
= (int16_t *) src
[0];
373 const int16_t * const send
= s
+ count
;
374 int32_t *d
= dst
[0] = dst
[1] = sample_buf
[0];
375 int scale
= WORD_SHIFT
;
379 *d
++ = *s
++ << scale
;
385 /* convert count 16-bit interleaved stereo to 32-bit noninterleaved */
386 static void sample_input_lte_native_i_stereo(
387 int count
, const char *src
[], int32_t *dst
[])
389 const int32_t *s
= (int32_t *) src
[0];
390 const int32_t * const send
= s
+ count
;
391 int32_t *dl
= dst
[0] = sample_buf
[0];
392 int32_t *dr
= dst
[1] = sample_buf
[1];
393 int scale
= WORD_SHIFT
;
398 #ifdef ROCKBOX_LITTLE_ENDIAN
399 *dl
++ = (slr
>> 16) << scale
;
400 *dr
++ = (int32_t)(int16_t)slr
<< scale
;
401 #else /* ROCKBOX_BIG_ENDIAN */
402 *dl
++ = (int32_t)(int16_t)slr
<< scale
;
403 *dr
++ = (slr
>> 16) << scale
;
410 /* convert count 16-bit noninterleaved stereo to 32-bit noninterleaved */
411 static void sample_input_lte_native_ni_stereo(
412 int count
, const char *src
[], int32_t *dst
[])
414 const int16_t *sl
= (int16_t *) src
[0];
415 const int16_t *sr
= (int16_t *) src
[1];
416 const int16_t * const slend
= sl
+ count
;
417 int32_t *dl
= dst
[0] = sample_buf
[0];
418 int32_t *dr
= dst
[1] = sample_buf
[1];
419 int scale
= WORD_SHIFT
;
423 *dl
++ = *sl
++ << scale
;
424 *dr
++ = *sr
++ << scale
;
431 /* convert count 32-bit mono to 32-bit mono */
432 static void sample_input_gt_native_mono(
433 int count
, const char *src
[], int32_t *dst
[])
435 dst
[0] = dst
[1] = (int32_t *)src
[0];
436 src
[0] = (char *)(dst
[0] + count
);
439 /* convert count 32-bit interleaved stereo to 32-bit noninterleaved stereo */
440 static void sample_input_gt_native_i_stereo(
441 int count
, const char *src
[], int32_t *dst
[])
443 const int32_t *s
= (int32_t *)src
[0];
444 const int32_t * const send
= s
+ 2*count
;
445 int32_t *dl
= dst
[0] = sample_buf
[0];
446 int32_t *dr
= dst
[1] = sample_buf
[1];
454 src
[0] = (char *)send
;
457 /* convert 32 bit-noninterleaved stereo to 32-bit noninterleaved stereo */
458 static void sample_input_gt_native_ni_stereo(
459 int count
, const char *src
[], int32_t *dst
[])
461 dst
[0] = (int32_t *)src
[0];
462 dst
[1] = (int32_t *)src
[1];
463 src
[0] = (char *)(dst
[0] + count
);
464 src
[1] = (char *)(dst
[1] + count
);
468 * sample_input_new_format()
470 * set the to-native sample conversion function based on dsp sample parameters
473 * needs syncing with changes to the following dsp parameters:
474 * * dsp->stereo_mode (A/V)
475 * * dsp->sample_depth (A/V)
477 static void sample_input_new_format(struct dsp_config
*dsp
)
479 static const sample_input_fn_type sample_input_functions
[] =
481 [SAMPLE_INPUT_LE_NATIVE_I_STEREO
] = sample_input_lte_native_i_stereo
,
482 [SAMPLE_INPUT_LE_NATIVE_NI_STEREO
] = sample_input_lte_native_ni_stereo
,
483 [SAMPLE_INPUT_LE_NATIVE_MONO
] = sample_input_lte_native_mono
,
484 [SAMPLE_INPUT_GT_NATIVE_I_STEREO
] = sample_input_gt_native_i_stereo
,
485 [SAMPLE_INPUT_GT_NATIVE_NI_STEREO
] = sample_input_gt_native_ni_stereo
,
486 [SAMPLE_INPUT_GT_NATIVE_MONO
] = sample_input_gt_native_mono
,
489 int convert
= dsp
->stereo_mode
;
491 if (dsp
->sample_depth
> NATIVE_DEPTH
)
492 convert
+= SAMPLE_INPUT_GT_NATIVE_1ST_INDEX
;
494 dsp
->input_samples
= sample_input_functions
[convert
];
498 #ifndef DSP_HAVE_ASM_SAMPLE_OUTPUT_MONO
499 /* write mono internal format to output format */
500 static void sample_output_mono(int count
, struct dsp_data
*data
,
501 const int32_t *src
[], int16_t *dst
)
503 const int32_t *s0
= src
[0];
504 const int scale
= data
->output_scale
;
505 const int dc_bias
= 1 << (scale
- 1);
509 int32_t lr
= clip_sample_16((*s0
++ + dc_bias
) >> scale
);
514 #endif /* DSP_HAVE_ASM_SAMPLE_OUTPUT_MONO */
516 /* write stereo internal format to output format */
517 #ifndef DSP_HAVE_ASM_SAMPLE_OUTPUT_STEREO
518 static void sample_output_stereo(int count
, struct dsp_data
*data
,
519 const int32_t *src
[], int16_t *dst
)
521 const int32_t *s0
= src
[0];
522 const int32_t *s1
= src
[1];
523 const int scale
= data
->output_scale
;
524 const int dc_bias
= 1 << (scale
- 1);
528 *dst
++ = clip_sample_16((*s0
++ + dc_bias
) >> scale
);
529 *dst
++ = clip_sample_16((*s1
++ + dc_bias
) >> scale
);
532 #endif /* DSP_HAVE_ASM_SAMPLE_OUTPUT_STEREO */
535 * The "dither" code to convert the 24-bit samples produced by libmad was
536 * taken from the coolplayer project - coolplayer.sourceforge.net
538 * This function handles mono and stereo outputs.
540 static void sample_output_dithered(int count
, struct dsp_data
*data
,
541 const int32_t *src
[], int16_t *dst
)
543 const int32_t mask
= dither_mask
;
544 const int32_t bias
= dither_bias
;
545 const int scale
= data
->output_scale
;
546 const int32_t min
= data
->clip_min
;
547 const int32_t max
= data
->clip_max
;
548 const int32_t range
= max
- min
;
552 for (ch
= 0; ch
< data
->num_channels
; ch
++)
554 struct dither_data
* const dither
= &dither_data
[ch
];
555 const int32_t *s
= src
[ch
];
558 for (i
= 0, d
= &dst
[ch
]; i
< count
; i
++, s
++, d
+= 2)
560 int32_t output
, sample
;
563 /* Noise shape and bias (for correct rounding later) */
565 sample
+= dither
->error
[0] - dither
->error
[1] + dither
->error
[2];
566 dither
->error
[2] = dither
->error
[1];
567 dither
->error
[1] = dither
->error
[0]/2;
569 output
= sample
+ bias
;
571 /* Dither, highpass triangle PDF */
572 random
= dither
->random
*0x0019660dL
+ 0x3c6ef35fL
;
573 output
+= (random
& mask
) - (dither
->random
& mask
);
574 dither
->random
= random
;
576 /* Round sample to output range */
580 dither
->error
[0] = sample
- output
;
583 if ((uint32_t)(output
- min
) > (uint32_t)range
)
591 /* Quantize and store */
592 *d
= output
>> scale
;
596 if (data
->num_channels
== 2)
599 /* Have to duplicate left samples into the right channel since
600 pcm buffer and hardware is interleaved stereo */
611 * sample_output_new_format()
613 * set the from-native to ouput sample conversion routine
616 * needs syncing with changes to the following dsp parameters:
617 * * dsp->stereo_mode (A/V)
618 * * dither_enabled (A)
620 static void sample_output_new_format(struct dsp_config
*dsp
)
622 static const sample_output_fn_type sample_output_functions
[] =
625 sample_output_stereo
,
626 sample_output_dithered
,
627 sample_output_dithered
630 int out
= dsp
->data
.num_channels
- 1;
632 if (dsp
== &AUDIO_DSP
&& dither_enabled
)
635 dsp
->output_samples
= sample_output_functions
[out
];
639 * Linear interpolation resampling that introduces a one sample delay because
640 * of our inability to look into the future at the end of a frame.
642 #ifndef DSP_HAVE_ASM_RESAMPLING
643 static int dsp_downsample(int count
, struct dsp_data
*data
,
644 const int32_t *src
[], int32_t *dst
[])
646 int ch
= data
->num_channels
- 1;
647 uint32_t delta
= data
->resample_data
.delta
;
651 /* Rolled channel loop actually showed slightly faster. */
654 /* Just initialize things and not worry too much about the relatively
655 * uncommon case of not being able to spit out a sample for the frame.
657 const int32_t *s
= src
[ch
];
658 int32_t last
= data
->resample_data
.last_sample
[ch
];
660 data
->resample_data
.last_sample
[ch
] = s
[count
- 1];
662 phase
= data
->resample_data
.phase
;
665 /* Do we need last sample of previous frame for interpolation? */
669 while (pos
< (uint32_t)count
)
671 *d
++ = last
+ FRACMUL((phase
& 0xffff) << 15, s
[pos
] - last
);
679 /* Wrap phase accumulator back to start of next frame. */
680 data
->resample_data
.phase
= phase
- (count
<< 16);
684 static int dsp_upsample(int count
, struct dsp_data
*data
,
685 const int32_t *src
[], int32_t *dst
[])
687 int ch
= data
->num_channels
- 1;
688 uint32_t delta
= data
->resample_data
.delta
;
692 /* Rolled channel loop actually showed slightly faster. */
695 /* Should always be able to output a sample for a ratio up to RESAMPLE_RATIO */
696 const int32_t *s
= src
[ch
];
697 int32_t last
= data
->resample_data
.last_sample
[ch
];
699 data
->resample_data
.last_sample
[ch
] = s
[count
- 1];
701 phase
= data
->resample_data
.phase
;
706 *d
++ = last
+ FRACMUL((phase
& 0xffff) << 15, s
[0] - last
);
711 while (pos
< (uint32_t)count
)
714 *d
++ = last
+ FRACMUL((phase
& 0xffff) << 15, s
[pos
] - last
);
721 /* Wrap phase accumulator back to start of next frame. */
722 data
->resample_data
.phase
= phase
& 0xffff;
725 #endif /* DSP_HAVE_ASM_RESAMPLING */
727 static void resampler_new_delta(struct dsp_config
*dsp
)
729 dsp
->data
.resample_data
.delta
= (unsigned long)
730 dsp
->frequency
* 65536LL / NATIVE_FREQUENCY
;
732 if (dsp
->frequency
== NATIVE_FREQUENCY
)
734 /* NOTE: If fully glitch-free transistions from no resampling to
735 resampling are desired, last_sample history should be maintained
736 even when not resampling. */
737 dsp
->resample
= NULL
;
738 dsp
->data
.resample_data
.phase
= 0;
739 dsp
->data
.resample_data
.last_sample
[0] = 0;
740 dsp
->data
.resample_data
.last_sample
[1] = 0;
742 else if (dsp
->frequency
< NATIVE_FREQUENCY
)
743 dsp
->resample
= dsp_upsample
;
745 dsp
->resample
= dsp_downsample
;
748 /* Resample count stereo samples. Updates the src array, if resampling is
749 * done, to refer to the resampled data. Returns number of stereo samples
750 * for further processing.
752 static inline int resample(struct dsp_config
*dsp
, int count
, int32_t *src
[])
759 lock_sample_buf( true );
760 count
= dsp
->resample(count
, &dsp
->data
, (const int32_t **)src
, dst
);
763 src
[1] = dst
[dsp
->data
.num_channels
- 1];
764 lock_sample_buf( false );
768 static void dither_init(struct dsp_config
*dsp
)
770 memset(dither_data
, 0, sizeof (dither_data
));
771 dither_bias
= (1L << (dsp
->data
.frac_bits
- NATIVE_DEPTH
));
772 dither_mask
= (1L << (dsp
->data
.frac_bits
+ 1 - NATIVE_DEPTH
)) - 1;
775 void dsp_dither_enable(bool enable
)
777 struct dsp_config
*dsp
= &AUDIO_DSP
;
778 dither_enabled
= enable
;
779 sample_output_new_format(dsp
);
782 /* Applies crossfeed to the stereo signal in src.
783 * Crossfeed is a process where listening over speakers is simulated. This
784 * is good for old hard panned stereo records, which might be quite fatiguing
785 * to listen to on headphones with no crossfeed.
787 #ifndef DSP_HAVE_ASM_CROSSFEED
788 static void apply_crossfeed(int count
, int32_t *buf
[])
790 int32_t *hist_l
= &crossfeed_data
.history
[0];
791 int32_t *hist_r
= &crossfeed_data
.history
[2];
792 int32_t *delay
= &crossfeed_data
.delay
[0][0];
793 int32_t *coefs
= &crossfeed_data
.coefs
[0];
794 int32_t gain
= crossfeed_data
.gain
;
795 int32_t *di
= crossfeed_data
.index
;
801 for (i
= 0; i
< count
; i
++)
806 /* Filter delayed sample from left speaker */
807 acc
= FRACMUL(*di
, coefs
[0]);
808 acc
+= FRACMUL(hist_l
[0], coefs
[1]);
809 acc
+= FRACMUL(hist_l
[1], coefs
[2]);
810 /* Save filter history for left speaker */
814 /* Filter delayed sample from right speaker */
815 acc
= FRACMUL(*di
, coefs
[0]);
816 acc
+= FRACMUL(hist_r
[0], coefs
[1]);
817 acc
+= FRACMUL(hist_r
[1], coefs
[2]);
818 /* Save filter history for right speaker */
822 /* Now add the attenuated direct sound and write to outputs */
823 buf
[0][i
] = FRACMUL(left
, gain
) + hist_r
[1];
824 buf
[1][i
] = FRACMUL(right
, gain
) + hist_l
[1];
826 /* Wrap delay line index if bigger than delay line size */
827 if (di
>= delay
+ 13*2)
830 /* Write back local copies of data we've modified */
831 crossfeed_data
.index
= di
;
833 #endif /* DSP_HAVE_ASM_CROSSFEED */
836 * dsp_set_crossfeed(bool enable)
839 * needs syncing with changes to the following dsp parameters:
840 * * dsp->stereo_mode (A)
842 void dsp_set_crossfeed(bool enable
)
844 crossfeed_enabled
= enable
;
845 AUDIO_DSP
.apply_crossfeed
= (enable
&& AUDIO_DSP
.data
.num_channels
> 1)
846 ? apply_crossfeed
: NULL
;
849 void dsp_set_crossfeed_direct_gain(int gain
)
851 crossfeed_data
.gain
= get_replaygain_int(gain
* 10) << 7;
852 /* If gain is negative, the calculation overflowed and we need to clamp */
853 if (crossfeed_data
.gain
< 0)
854 crossfeed_data
.gain
= 0x7fffffff;
857 /* Both gains should be below 0 dB */
858 void dsp_set_crossfeed_cross_params(long lf_gain
, long hf_gain
, long cutoff
)
860 int32_t *c
= crossfeed_data
.coefs
;
861 long scaler
= get_replaygain_int(lf_gain
* 10) << 7;
863 cutoff
= 0xffffffff/NATIVE_FREQUENCY
*cutoff
;
865 /* Divide cutoff by sqrt(10^(hf_gain/20)) to place cutoff at the -3 dB
866 * point instead of shelf midpoint. This is for compatibility with the old
867 * crossfeed shelf filter and should be removed if crossfeed settings are
868 * ever made incompatible for any other good reason.
870 cutoff
= fp_div(cutoff
, get_replaygain_int(hf_gain
*5), 24);
871 filter_shelf_coefs(cutoff
, hf_gain
, false, c
);
872 /* Scale coefs by LF gain and shift them to s0.31 format. We have no gains
873 * over 1 and can do this safely
875 c
[0] = FRACMUL_SHL(c
[0], scaler
, 4);
876 c
[1] = FRACMUL_SHL(c
[1], scaler
, 4);
880 /* Apply a constant gain to the samples (e.g., for ReplayGain).
881 * Note that this must be called before the resampler.
883 #ifndef DSP_HAVE_ASM_APPLY_GAIN
884 static void dsp_apply_gain(int count
, struct dsp_data
*data
, int32_t *buf
[])
886 const int32_t gain
= data
->gain
;
889 for (ch
= 0; ch
< data
->num_channels
; ch
++)
891 int32_t *d
= buf
[ch
];
894 for (i
= 0; i
< count
; i
++)
895 d
[i
] = FRACMUL_SHL(d
[i
], gain
, 8);
898 #endif /* DSP_HAVE_ASM_APPLY_GAIN */
900 /* Combine all gains to a global gain. */
901 static void set_gain(struct dsp_config
*dsp
)
903 /* gains are in S7.24 format */
904 dsp
->data
.gain
= DEFAULT_GAIN
;
906 /* Replay gain not relevant to voice */
907 if (dsp
== &AUDIO_DSP
&& replaygain
)
909 dsp
->data
.gain
= replaygain
;
912 if (dsp
->eq_process
&& eq_precut
)
914 dsp
->data
.gain
= fp_mul(dsp
->data
.gain
, eq_precut
, 24);
917 #ifdef HAVE_SW_VOLUME_CONTROL
918 if (global_settings
.volume
< SW_VOLUME_MAX
||
919 global_settings
.volume
> SW_VOLUME_MIN
)
921 int vol_gain
= get_replaygain_int(global_settings
.volume
* 100);
922 dsp
->data
.gain
= (long) (((int64_t) dsp
->data
.gain
* vol_gain
) >> 24);
926 if (dsp
->data
.gain
== DEFAULT_GAIN
)
932 dsp
->data
.gain
>>= 1; /* convert gain to S8.23 format */
935 dsp
->apply_gain
= dsp
->data
.gain
!= 0 ? dsp_apply_gain
: NULL
;
939 * Update the amount to cut the audio before applying the equalizer.
941 * @param precut to apply in decibels (multiplied by 10)
943 void dsp_set_eq_precut(int precut
)
945 eq_precut
= get_replaygain_int(precut
* -10);
946 set_gain(&AUDIO_DSP
);
950 * Synchronize the equalizer filter coefficients with the global settings.
952 * @param band the equalizer band to synchronize
954 void dsp_set_eq_coefs(int band
)
958 unsigned long cutoff
, q
;
960 /* Adjust setting pointer to the band we actually want to change */
961 setting
= &global_settings
.eq_band0_cutoff
+ (band
* 3);
963 /* Convert user settings to format required by coef generator functions */
964 cutoff
= 0xffffffff / NATIVE_FREQUENCY
* (*setting
++);
971 /* NOTE: The coef functions assume the EMAC unit is in fractional mode,
972 which it should be, since we're executed from the main thread. */
974 /* Assume a band is disabled if the gain is zero */
977 eq_data
.enabled
[band
] = 0;
982 eq_ls_coefs(cutoff
, q
, gain
, eq_data
.filters
[band
].coefs
);
984 eq_hs_coefs(cutoff
, q
, gain
, eq_data
.filters
[band
].coefs
);
986 eq_pk_coefs(cutoff
, q
, gain
, eq_data
.filters
[band
].coefs
);
988 eq_data
.enabled
[band
] = 1;
992 /* Apply EQ filters to those bands that have got it switched on. */
993 static void eq_process(int count
, int32_t *buf
[])
995 static const int shifts
[] =
997 EQ_SHELF_SHIFT
, /* low shelf */
998 EQ_PEAK_SHIFT
, /* peaking */
999 EQ_PEAK_SHIFT
, /* peaking */
1000 EQ_PEAK_SHIFT
, /* peaking */
1001 EQ_SHELF_SHIFT
, /* high shelf */
1003 unsigned int channels
= AUDIO_DSP
.data
.num_channels
;
1006 /* filter configuration currently is 1 low shelf filter, 3 band peaking
1007 filters and 1 high shelf filter, in that order. we need to know this
1008 so we can choose the correct shift factor.
1010 for (i
= 0; i
< 5; i
++)
1012 if (!eq_data
.enabled
[i
])
1014 eq_filter(buf
, &eq_data
.filters
[i
], count
, channels
, shifts
[i
]);
1019 * Use to enable the equalizer.
1021 * @param enable true to enable the equalizer
1023 void dsp_set_eq(bool enable
)
1025 AUDIO_DSP
.eq_process
= enable
? eq_process
: NULL
;
1026 set_gain(&AUDIO_DSP
);
1029 static void dsp_set_stereo_width(int value
)
1031 long width
, straight
, cross
;
1033 width
= value
* 0x7fffff / 100;
1037 straight
= (0x7fffff + width
) / 2;
1038 cross
= straight
- width
;
1042 /* straight = (1 + width) / (2 * width) */
1043 straight
= ((int64_t)(0x7fffff + width
) << 22) / width
;
1044 cross
= straight
- 0x7fffff;
1047 dsp_sw_gain
= straight
<< 8;
1048 dsp_sw_cross
= cross
<< 8;
1052 * Implements the different channel configurations and stereo width.
1055 /* SOUND_CHAN_STEREO mode is a noop so has no function - just outline one for
1058 static void channels_process_sound_chan_stereo(int count
, int32_t *buf
[])
1060 /* The channels are each just themselves */
1061 (void)count
; (void)buf
;
1065 #ifndef DSP_HAVE_ASM_SOUND_CHAN_MONO
1066 static void channels_process_sound_chan_mono(int count
, int32_t *buf
[])
1068 int32_t *sl
= buf
[0], *sr
= buf
[1];
1072 int32_t lr
= *sl
/2 + *sr
/2;
1077 #endif /* DSP_HAVE_ASM_SOUND_CHAN_MONO */
1079 #ifndef DSP_HAVE_ASM_SOUND_CHAN_CUSTOM
1080 static void channels_process_sound_chan_custom(int count
, int32_t *buf
[])
1082 const int32_t gain
= dsp_sw_gain
;
1083 const int32_t cross
= dsp_sw_cross
;
1084 int32_t *sl
= buf
[0], *sr
= buf
[1];
1090 *sl
++ = FRACMUL(l
, gain
) + FRACMUL(r
, cross
);
1091 *sr
++ = FRACMUL(r
, gain
) + FRACMUL(l
, cross
);
1094 #endif /* DSP_HAVE_ASM_SOUND_CHAN_CUSTOM */
1096 static void channels_process_sound_chan_mono_left(int count
, int32_t *buf
[])
1098 /* Just copy over the other channel */
1099 memcpy(buf
[1], buf
[0], count
* sizeof (*buf
));
1102 static void channels_process_sound_chan_mono_right(int count
, int32_t *buf
[])
1104 /* Just copy over the other channel */
1105 memcpy(buf
[0], buf
[1], count
* sizeof (*buf
));
1108 #ifndef DSP_HAVE_ASM_SOUND_CHAN_KARAOKE
1109 static void channels_process_sound_chan_karaoke(int count
, int32_t *buf
[])
1111 int32_t *sl
= buf
[0], *sr
= buf
[1];
1115 int32_t ch
= *sl
/2 - *sr
/2;
1120 #endif /* DSP_HAVE_ASM_SOUND_CHAN_KARAOKE */
1122 static void dsp_set_channel_config(int value
)
1124 static const channels_process_fn_type channels_process_functions
[] =
1126 /* SOUND_CHAN_STEREO = All-purpose index for no channel processing */
1127 [SOUND_CHAN_STEREO
] = NULL
,
1128 [SOUND_CHAN_MONO
] = channels_process_sound_chan_mono
,
1129 [SOUND_CHAN_CUSTOM
] = channels_process_sound_chan_custom
,
1130 [SOUND_CHAN_MONO_LEFT
] = channels_process_sound_chan_mono_left
,
1131 [SOUND_CHAN_MONO_RIGHT
] = channels_process_sound_chan_mono_right
,
1132 [SOUND_CHAN_KARAOKE
] = channels_process_sound_chan_karaoke
,
1135 if ((unsigned)value
>= ARRAYLEN(channels_process_functions
) ||
1136 AUDIO_DSP
.stereo_mode
== STEREO_MONO
)
1138 value
= SOUND_CHAN_STEREO
;
1141 /* This doesn't apply to voice */
1142 channels_mode
= value
;
1143 AUDIO_DSP
.channels_process
= channels_process_functions
[value
];
1146 #if CONFIG_CODEC == SWCODEC
1148 #ifdef HAVE_SW_TONE_CONTROLS
1149 static void set_tone_controls(void)
1151 filter_bishelf_coefs(0xffffffff/NATIVE_FREQUENCY
*200,
1152 0xffffffff/NATIVE_FREQUENCY
*3500,
1153 bass
, treble
, -prescale
,
1154 AUDIO_DSP
.tone_filter
.coefs
);
1155 /* Sync the voice dsp coefficients */
1156 memcpy(&VOICE_DSP
.tone_filter
.coefs
, AUDIO_DSP
.tone_filter
.coefs
,
1157 sizeof (VOICE_DSP
.tone_filter
.coefs
));
1161 /* Hook back from firmware/ part of audio, which can't/shouldn't call apps/
1164 int dsp_callback(int msg
, intptr_t param
)
1168 #ifdef HAVE_SW_TONE_CONTROLS
1169 case DSP_CALLBACK_SET_PRESCALE
:
1171 set_tone_controls();
1173 /* prescaler is always set after calling any of these, so we wait with
1174 * calculating coefs until the above case is hit.
1176 case DSP_CALLBACK_SET_BASS
:
1179 case DSP_CALLBACK_SET_TREBLE
:
1182 #ifdef HAVE_SW_VOLUME_CONTROL
1183 case DSP_CALLBACK_SET_SW_VOLUME
:
1184 set_gain(&AUDIO_DSP
);
1188 case DSP_CALLBACK_SET_CHANNEL_CONFIG
:
1189 dsp_set_channel_config(param
);
1191 case DSP_CALLBACK_SET_STEREO_WIDTH
:
1192 dsp_set_stereo_width(param
);
1201 /* Process and convert src audio to dst based on the DSP configuration,
1202 * reading count number of audio samples. dst is assumed to be large
1203 * enough; use dsp_output_count() to get the required number. src is an
1204 * array of pointers; for mono and interleaved stereo, it contains one
1205 * pointer to the start of the audio data and the other is ignored; for
1206 * non-interleaved stereo, it contains two pointers, one for each audio
1207 * channel. Returns number of bytes written to dst.
1209 int dsp_process(struct dsp_config
*dsp
, char *dst
, const char *src
[], int count
)
1211 static int32_t *tmp
[2]; /* tdspeed_doit() needs it static */
1212 static long last_yield
;
1216 #if defined(CPU_COLDFIRE)
1217 /* set emac unit for dsp processing, and save old macsr, we're running in
1218 codec thread context at this point, so can't clobber it */
1219 unsigned long old_macsr
= coldfire_get_macsr();
1220 coldfire_set_macsr(EMAC_FRACTIONAL
| EMAC_SATURATE
);
1224 dsp_set_replaygain(); /* Gain has changed */
1226 /* Perform at least one yield before starting */
1227 last_yield
= current_tick
;
1230 /* Testing function pointers for NULL is preferred since the pointer
1231 will be preloaded to be used for the call if not. */
1234 int samples
= MIN(sample_buf_count
, count
);
1237 dsp
->input_samples(samples
, src
, tmp
);
1239 #ifdef HAVE_PITCHSCREEN
1240 if (dsp
->tdspeed_active
)
1241 samples
= tdspeed_doit(tmp
, samples
);
1244 int chunk_offset
= 0;
1248 t2
[0] = tmp
[0]+chunk_offset
;
1249 t2
[1] = tmp
[1]+chunk_offset
;
1251 int chunk
= MIN(sample_buf_count
, samples
);
1252 chunk_offset
+= chunk
;
1255 if (dsp
->apply_gain
)
1256 dsp
->apply_gain(chunk
, &dsp
->data
, t2
);
1258 if (dsp
->resample
&& (chunk
= resample(dsp
, chunk
, t2
)) <= 0)
1259 break; /* I'm pretty sure we're downsampling here */
1261 if (dsp
->apply_crossfeed
)
1262 dsp
->apply_crossfeed(chunk
, t2
);
1264 if (dsp
->eq_process
)
1265 dsp
->eq_process(chunk
, t2
);
1267 #ifdef HAVE_SW_TONE_CONTROLS
1268 if ((bass
| treble
) != 0)
1269 eq_filter(t2
, &dsp
->tone_filter
, chunk
,
1270 dsp
->data
.num_channels
, FILTER_BISHELF_SHIFT
);
1273 if (dsp
->channels_process
)
1274 dsp
->channels_process(chunk
, t2
);
1276 if (dsp
->compressor_process
)
1277 dsp
->compressor_process(chunk
, &dsp
->data
, t2
);
1279 dsp
->output_samples(chunk
, &dsp
->data
, (const int32_t **)t2
, (int16_t *)dst
);
1282 dst
+= chunk
* sizeof (int16_t) * 2;
1284 /* yield at least once each tick */
1285 tick
= current_tick
;
1286 if (TIME_AFTER(tick
, last_yield
))
1294 #if defined(CPU_COLDFIRE)
1295 /* set old macsr again */
1296 coldfire_set_macsr(old_macsr
);
1301 /* Given count number of input samples, calculate the maximum number of
1302 * samples of output data that would be generated (the calculation is not
1303 * entirely exact and rounds upwards to be on the safe side; during
1304 * resampling, the number of samples generated depends on the current state
1305 * of the resampler).
1307 /* dsp_input_size MUST be called afterwards */
1308 int dsp_output_count(struct dsp_config
*dsp
, int count
)
1310 #ifdef HAVE_PITCHSCREEN
1311 if (dsp
->tdspeed_active
)
1312 count
= tdspeed_est_output_size();
1316 count
= (int)(((unsigned long)count
* NATIVE_FREQUENCY
1317 + (dsp
->frequency
- 1)) / dsp
->frequency
);
1320 /* Now we have the resampled sample count which must not exceed
1321 * resample_buf_count to avoid resample buffer overflow. One
1322 * must call dsp_input_count() to get the correct input sample
1325 if (count
> resample_buf_count
)
1326 count
= resample_buf_count
;
1331 /* Given count output samples, calculate number of input samples
1332 * that would be consumed in order to fill the output buffer.
1334 int dsp_input_count(struct dsp_config
*dsp
, int count
)
1336 /* count is now the number of resampled input samples. Convert to
1337 original input samples. */
1340 /* Use the real resampling delta =
1341 * dsp->frequency * 65536 / NATIVE_FREQUENCY, and
1342 * round towards zero to avoid buffer overflows. */
1343 count
= (int)(((unsigned long)count
*
1344 dsp
->data
.resample_data
.delta
) >> 16);
1347 #ifdef HAVE_PITCHSCREEN
1348 if (dsp
->tdspeed_active
)
1349 count
= tdspeed_est_input_size(count
);
1355 static void dsp_set_gain_var(long *var
, long value
)
1361 static void dsp_update_functions(struct dsp_config
*dsp
)
1363 sample_input_new_format(dsp
);
1364 sample_output_new_format(dsp
);
1365 if (dsp
== &AUDIO_DSP
)
1366 dsp_set_crossfeed(crossfeed_enabled
);
1369 intptr_t dsp_configure(struct dsp_config
*dsp
, int setting
, intptr_t value
)
1376 case CODEC_IDX_AUDIO
:
1377 return (intptr_t)&AUDIO_DSP
;
1378 case CODEC_IDX_VOICE
:
1379 return (intptr_t)&VOICE_DSP
;
1381 return (intptr_t)NULL
;
1384 case DSP_SET_FREQUENCY
:
1385 memset(&dsp
->data
.resample_data
, 0, sizeof (dsp
->data
.resample_data
));
1386 /* Fall through!!! */
1387 case DSP_SWITCH_FREQUENCY
:
1388 dsp
->codec_frequency
= (value
== 0) ? NATIVE_FREQUENCY
: value
;
1389 /* Account for playback speed adjustment when setting dsp->frequency
1390 if we're called from the main audio thread. Voice UI thread should
1391 not need this feature.
1393 #ifdef HAVE_PITCHSCREEN
1394 if (dsp
== &AUDIO_DSP
)
1395 dsp
->frequency
= pitch_ratio
* dsp
->codec_frequency
/ PITCH_SPEED_100
;
1398 dsp
->frequency
= dsp
->codec_frequency
;
1400 resampler_new_delta(dsp
);
1401 #ifdef HAVE_PITCHSCREEN
1406 case DSP_SET_SAMPLE_DEPTH
:
1407 dsp
->sample_depth
= value
;
1409 if (dsp
->sample_depth
<= NATIVE_DEPTH
)
1411 dsp
->data
.frac_bits
= WORD_FRACBITS
;
1412 dsp
->sample_bytes
= sizeof (int16_t); /* samples are 16 bits */
1413 dsp
->data
.clip_max
= ((1 << WORD_FRACBITS
) - 1);
1414 dsp
->data
.clip_min
= -((1 << WORD_FRACBITS
));
1418 dsp
->data
.frac_bits
= value
;
1419 dsp
->sample_bytes
= sizeof (int32_t); /* samples are 32 bits */
1420 dsp
->data
.clip_max
= (1 << value
) - 1;
1421 dsp
->data
.clip_min
= -(1 << value
);
1424 dsp
->data
.output_scale
= dsp
->data
.frac_bits
+ 1 - NATIVE_DEPTH
;
1425 sample_input_new_format(dsp
);
1429 case DSP_SET_STEREO_MODE
:
1430 dsp
->stereo_mode
= value
;
1431 dsp
->data
.num_channels
= value
== STEREO_MONO
? 1 : 2;
1432 dsp_update_functions(dsp
);
1433 #ifdef HAVE_PITCHSCREEN
1439 dsp
->stereo_mode
= STEREO_NONINTERLEAVED
;
1440 dsp
->data
.num_channels
= 2;
1441 dsp
->sample_depth
= NATIVE_DEPTH
;
1442 dsp
->data
.frac_bits
= WORD_FRACBITS
;
1443 dsp
->sample_bytes
= sizeof (int16_t);
1444 dsp
->data
.output_scale
= dsp
->data
.frac_bits
+ 1 - NATIVE_DEPTH
;
1445 dsp
->data
.clip_max
= ((1 << WORD_FRACBITS
) - 1);
1446 dsp
->data
.clip_min
= -((1 << WORD_FRACBITS
));
1447 dsp
->codec_frequency
= dsp
->frequency
= NATIVE_FREQUENCY
;
1449 if (dsp
== &AUDIO_DSP
)
1458 dsp_update_functions(dsp
);
1459 resampler_new_delta(dsp
);
1460 #ifdef HAVE_PITCHSCREEN
1463 if (dsp
== &AUDIO_DSP
)
1468 memset(&dsp
->data
.resample_data
, 0,
1469 sizeof (dsp
->data
.resample_data
));
1470 resampler_new_delta(dsp
);
1472 #ifdef HAVE_PITCHSCREEN
1475 if (dsp
== &AUDIO_DSP
)
1479 case DSP_SET_TRACK_GAIN
:
1480 if (dsp
== &AUDIO_DSP
)
1481 dsp_set_gain_var(&track_gain
, value
);
1484 case DSP_SET_ALBUM_GAIN
:
1485 if (dsp
== &AUDIO_DSP
)
1486 dsp_set_gain_var(&album_gain
, value
);
1489 case DSP_SET_TRACK_PEAK
:
1490 if (dsp
== &AUDIO_DSP
)
1491 dsp_set_gain_var(&track_peak
, value
);
1494 case DSP_SET_ALBUM_PEAK
:
1495 if (dsp
== &AUDIO_DSP
)
1496 dsp_set_gain_var(&album_peak
, value
);
1506 int get_replaygain_mode(bool have_track_gain
, bool have_album_gain
)
1510 bool track
= ((global_settings
.replaygain_type
== REPLAYGAIN_TRACK
)
1511 || ((global_settings
.replaygain_type
== REPLAYGAIN_SHUFFLE
)
1512 && global_settings
.playlist_shuffle
));
1514 type
= (!track
&& have_album_gain
) ? REPLAYGAIN_ALBUM
1515 : have_track_gain
? REPLAYGAIN_TRACK
: -1;
1520 void dsp_set_replaygain(void)
1526 if ((global_settings
.replaygain_type
!= REPLAYGAIN_OFF
) ||
1527 global_settings
.replaygain_noclip
)
1529 bool track_mode
= get_replaygain_mode(track_gain
!= 0,
1530 album_gain
!= 0) == REPLAYGAIN_TRACK
;
1531 long peak
= (track_mode
|| !album_peak
) ? track_peak
: album_peak
;
1533 if (global_settings
.replaygain_type
!= REPLAYGAIN_OFF
)
1535 gain
= (track_mode
|| !album_gain
) ? track_gain
: album_gain
;
1537 if (global_settings
.replaygain_preamp
)
1539 long preamp
= get_replaygain_int(
1540 global_settings
.replaygain_preamp
* 10);
1542 gain
= (long) (((int64_t) gain
* preamp
) >> 24);
1548 /* So that noclip can work even with no gain information. */
1549 gain
= DEFAULT_GAIN
;
1552 if (global_settings
.replaygain_noclip
&& (peak
!= 0)
1553 && ((((int64_t) gain
* peak
) >> 24) >= DEFAULT_GAIN
))
1555 gain
= (((int64_t) DEFAULT_GAIN
<< 24) / peak
);
1558 if (gain
== DEFAULT_GAIN
)
1560 /* Nothing to do, disable processing. */
1565 /* Store in S7.24 format to simplify calculations. */
1567 set_gain(&AUDIO_DSP
);
1571 * Called by the menu system to configure the compressor process */
1572 void dsp_set_compressor(void)
1574 /* enable/disable the compressor */
1575 AUDIO_DSP
.compressor_process
= compressor_update() ?
1576 compressor_process
: NULL
;