@@ -43,6 +43,22 @@ void common_hal_audiofilters_filter_construct(audiofilters_filter_obj_t *self,
4343
4444 self -> last_buf_idx = 1 ; // Which buffer to use first, toggle between 0 and 1
4545
46+ // This buffer will be used to process samples through the biquad filter
47+ self -> filter_buffer [0 ] = m_malloc (SYNTHIO_MAX_DUR * sizeof (int32_t ));
48+ if (self -> filter_buffer [0 ] == NULL ) {
49+ common_hal_audiofilters_filter_deinit (self );
50+ m_malloc_fail (SYNTHIO_MAX_DUR * sizeof (int32_t ));
51+ }
52+ memset (self -> filter_buffer [0 ], 0 , SYNTHIO_MAX_DUR * sizeof (int32_t ));
53+
54+ // This buffer will be used to mix original sample with processed signal
55+ self -> filter_buffer [1 ] = m_malloc (SYNTHIO_MAX_DUR * sizeof (int32_t ));
56+ if (self -> filter_buffer [1 ] == NULL ) {
57+ common_hal_audiofilters_filter_deinit (self );
58+ m_malloc_fail (SYNTHIO_MAX_DUR * sizeof (int32_t ));
59+ }
60+ memset (self -> filter_buffer [1 ], 0 , SYNTHIO_MAX_DUR * sizeof (int32_t ));
61+
4662 // Initialize other values most effects will need.
4763 self -> sample = NULL ; // The current playing sample
4864 self -> sample_remaining_buffer = NULL ; // Pointer to the start of the sample buffer we have not played
@@ -78,6 +94,8 @@ void common_hal_audiofilters_filter_deinit(audiofilters_filter_obj_t *self) {
7894 }
7995 self -> buffer [0 ] = NULL ;
8096 self -> buffer [1 ] = NULL ;
97+ self -> filter_buffer [0 ] = NULL ;
98+ self -> filter_buffer [1 ] = NULL ;
8199}
82100
83101mp_obj_t common_hal_audiofilters_filter_get_filter (audiofilters_filter_obj_t * self ) {
@@ -115,6 +133,8 @@ void audiofilters_filter_reset_buffer(audiofilters_filter_obj_t *self,
115133
116134 memset (self -> buffer [0 ], 0 , self -> buffer_len );
117135 memset (self -> buffer [1 ], 0 , self -> buffer_len );
136+ memset (self -> filter_buffer [0 ], 0 , SYNTHIO_MAX_DUR * sizeof (int32_t ));
137+ memset (self -> filter_buffer [1 ], 0 , SYNTHIO_MAX_DUR * sizeof (int32_t ));
118138
119139 synthio_biquad_filter_reset (& self -> filter_state );
120140}
@@ -166,6 +186,32 @@ void common_hal_audiofilters_filter_stop(audiofilters_filter_obj_t *self) {
166186 return ;
167187}
168188
189+ #define RANGE_LOW_16 (-28000)
190+ #define RANGE_HIGH_16 (28000)
191+ #define RANGE_SHIFT_16 (16)
192+ #define RANGE_SCALE_16 (0xfffffff / (32768 * 2 - RANGE_HIGH_16)) // 2 for echo+sample
193+
194+ // dynamic range compression via a downward compressor with hard knee
195+ //
196+ // When the output value is within the range +-28000 (about 85% of full scale),
197+ // it is unchanged. Otherwise, it undergoes a gain reduction so that the
198+ // largest possible values, (+32768,-32767) * 2 (2 for echo and sample),
199+ // still fit within the output range
200+ //
201+ // This produces a much louder overall volume with multiple voices, without
202+ // much additional processing.
203+ //
204+ // https://en.wikipedia.org/wiki/Dynamic_range_compression
205+ static
206+ int16_t mix_down_sample (int32_t sample ) {
207+ if (sample < RANGE_LOW_16 ) {
208+ sample = (((sample - RANGE_LOW_16 ) * RANGE_SCALE_16 ) >> RANGE_SHIFT_16 ) + RANGE_LOW_16 ;
209+ } else if (sample > RANGE_HIGH_16 ) {
210+ sample = (((sample - RANGE_HIGH_16 ) * RANGE_SCALE_16 ) >> RANGE_SHIFT_16 ) + RANGE_HIGH_16 ;
211+ }
212+ return sample ;
213+ }
214+
169215audioio_get_buffer_result_t audiofilters_filter_get_buffer (audiofilters_filter_obj_t * self , bool single_channel_output , uint8_t channel ,
170216 uint8_t * * buffer , uint32_t * buffer_length ) {
171217
@@ -221,34 +267,49 @@ audioio_get_buffer_result_t audiofilters_filter_get_buffer(audiofilters_filter_o
221267 }
222268 }
223269 } else {
224- for (uint32_t i = 0 ; i < n ; i ++ ) {
225- int32_t sample_word = 0 ;
226- if (MP_LIKELY (self -> bits_per_sample == 16 )) {
227- sample_word = sample_src [i ];
228- } else {
229- if (self -> samples_signed ) {
230- sample_word = sample_hsrc [i ];
270+ uint32_t i = 0 ;
271+ while (i < n ) {
272+ uint32_t n_samples = MIN (SYNTHIO_MAX_DUR , n - i );
273+
274+ // Fill filter buffer with samples
275+ for (uint32_t j = 0 ; j < n_samples ; j ++ ) {
276+ if (MP_LIKELY (self -> bits_per_sample == 16 )) {
277+ self -> filter_buffer [0 ][j ] = sample_src [i + j ];
231278 } else {
232- // Be careful here changing from an 8 bit unsigned to signed into a 32-bit signed
233- sample_word = (int8_t )(((uint8_t )sample_hsrc [i ]) ^ 0x80 );
279+ if (self -> samples_signed ) {
280+ self -> filter_buffer [0 ][j ] = sample_hsrc [i + j ];
281+ } else {
282+ // Be careful here changing from an 8 bit unsigned to signed into a 32-bit signed
283+ self -> filter_buffer [0 ][j ] = (int8_t )(((uint8_t )sample_hsrc [i + j ]) ^ 0x80 );
284+ }
234285 }
235286 }
236287
237- // TODO: Filter through synthio_biquad_filter_samples
288+ // Copy original signal for mixing back in later
289+ memcpy (self -> filter_buffer [1 ], self -> filter_buffer [0 ], n_samples );
238290
239- if (MP_LIKELY (self -> bits_per_sample == 16 )) {
240- word_buffer [i ] = (sample_word * (1.0 - mix )) + (word * mix );
241- if (!self -> samples_signed ) {
242- word_buffer [i ] ^= 0x8000 ;
243- }
244- } else {
245- int8_t mixed = (sample_word * (1.0 - mix )) + (word * mix );
246- if (self -> samples_signed ) {
247- hword_buffer [i ] = mixed ;
291+ // Process biquad filter
292+ synthio_biquad_filter_samples (& self -> filter_state , self -> filter_buffer [0 ], n_samples );
293+
294+ // Mix processed signal with original sample and transfer to output buffer
295+ for (uint32_t j = 0 ; j < n_samples ; j ++ ) {
296+ int32_t word = (self -> filter_buffer [1 ][j ] * (1.0 - mix )) + (self -> filter_buffer [0 ][j ] * mix );
297+ if (MP_LIKELY (self -> bits_per_sample == 16 )) {
298+ word_buffer [i + j ] = mix_down_sample (word );
299+ if (!self -> samples_signed ) {
300+ word_buffer [i + j ] ^= 0x8000 ;
301+ }
248302 } else {
249- hword_buffer [i ] = (uint8_t )mixed ^ 0x80 ;
303+ int8_t mixed = word ;
304+ if (self -> samples_signed ) {
305+ hword_buffer [i + j ] = mixed ;
306+ } else {
307+ hword_buffer [i + j ] = (uint8_t )mixed ^ 0x80 ;
308+ }
250309 }
251310 }
311+
312+ i += n_samples ;
252313 }
253314 }
254315
0 commit comments