@@ -43,6 +43,22 @@ void common_hal_audiofilters_filter_construct(audiofilters_filter_obj_t *self,
43
43
44
44
self -> last_buf_idx = 1 ; // Which buffer to use first, toggle between 0 and 1
45
45
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
+
46
62
// Initialize other values most effects will need.
47
63
self -> sample = NULL ; // The current playing sample
48
64
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) {
78
94
}
79
95
self -> buffer [0 ] = NULL ;
80
96
self -> buffer [1 ] = NULL ;
97
+ self -> filter_buffer [0 ] = NULL ;
98
+ self -> filter_buffer [1 ] = NULL ;
81
99
}
82
100
83
101
mp_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,
115
133
116
134
memset (self -> buffer [0 ], 0 , self -> buffer_len );
117
135
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 ));
118
138
119
139
synthio_biquad_filter_reset (& self -> filter_state );
120
140
}
@@ -166,6 +186,32 @@ void common_hal_audiofilters_filter_stop(audiofilters_filter_obj_t *self) {
166
186
return ;
167
187
}
168
188
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
+
169
215
audioio_get_buffer_result_t audiofilters_filter_get_buffer (audiofilters_filter_obj_t * self , bool single_channel_output , uint8_t channel ,
170
216
uint8_t * * buffer , uint32_t * buffer_length ) {
171
217
@@ -221,34 +267,49 @@ audioio_get_buffer_result_t audiofilters_filter_get_buffer(audiofilters_filter_o
221
267
}
222
268
}
223
269
} 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 ];
231
278
} 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
+ }
234
285
}
235
286
}
236
287
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 );
238
290
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
+ }
248
302
} 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
+ }
250
309
}
251
310
}
311
+
312
+ i += n_samples ;
252
313
}
253
314
}
254
315
0 commit comments