@@ -43,10 +43,6 @@ void common_hal_audiodelays_echo_construct(audiodelays_echo_obj_t *self, uint32_
4343 }
4444 synthio_block_assign_slot (mix , & self -> mix , MP_QSTR_mix );
4545
46- // calculate buffer size for the set delay
47- mp_float_t f_delay_ms = synthio_block_slot_get (& self -> delay_ms );
48- self -> echo_buffer_len = self -> sample_rate / 1000.0f * f_delay_ms * (self -> channel_count * (self -> bits_per_sample / 8 ));
49-
5046 // Set the echo buffer for the max possible delay
5147 self -> max_delay_ms = max_delay_ms ;
5248 self -> max_echo_buffer_len = self -> sample_rate / 1000.0f * max_delay_ms * (self -> channel_count * (self -> bits_per_sample / 8 ));
@@ -57,9 +53,13 @@ void common_hal_audiodelays_echo_construct(audiodelays_echo_obj_t *self, uint32_
5753 }
5854 memset (self -> echo_buffer , 0 , self -> max_echo_buffer_len );
5955
56+ // calculate current echo buffer size for the set delay
57+ mp_float_t f_delay_ms = synthio_block_slot_get (& self -> delay_ms );
58+ self -> echo_buffer_len = self -> sample_rate / 1000.0f * f_delay_ms * (self -> channel_count * (self -> bits_per_sample / 8 ));
59+
6060 // read is where we store the incoming sample
6161 // write is what we send to the outgoing buffer
62- self -> echo_buffer_read_pos = self -> buffer_len / sizeof (uint32_t );
62+ self -> echo_buffer_read_pos = self -> buffer_len / sizeof (uint16_t );
6363 self -> echo_buffer_write_pos = 0 ;
6464
6565 self -> sample = NULL ;
@@ -168,7 +168,7 @@ void common_hal_audiodelays_echo_play(audiodelays_echo_obj_t *self, mp_obj_t sam
168168 audiosample_reset_buffer (self -> sample , false, 0 );
169169 audioio_get_buffer_result_t result = audiosample_get_buffer (self -> sample , false, 0 , (uint8_t * * )& self -> sample_remaining_buffer , & self -> sample_buffer_length );
170170 // Track length in terms of words.
171- self -> sample_buffer_length /= sizeof (uint32_t );
171+ self -> sample_buffer_length /= sizeof (uint16_t );
172172 self -> more_data = result == GET_BUFFER_MORE_DATA ;
173173
174174 return ;
@@ -179,35 +179,48 @@ void common_hal_audiodelays_echo_stop(audiodelays_echo_obj_t *self) {
179179 return ;
180180}
181181
182+ #define RANGE_LOW (-28000)
183+ #define RANGE_HIGH (28000)
184+ #define RANGE_SHIFT (16)
185+ #define RANGE_SCALE (0xfffffff / (32768 * 2 - RANGE_HIGH))
186+
187+ // dynamic range compression via a downward compressor with hard knee
188+ //
189+ // When the output value is within the range +-28000 (about 85% of full scale),
190+ // it is unchanged. Otherwise, it undergoes a gain reduction so that the
191+ // largest possible values, (+32768,-32767) * 2 (2 for echo and sample),
192+ // still fit within the output range
193+ //
194+ // This produces a much louder overall volume with multiple voices, without
195+ // much additional processing.
196+ //
197+ // https://en.wikipedia.org/wiki/Dynamic_range_compression
198+ static
199+ int16_t mix_down_sample (int32_t sample ) {
200+ if (sample < RANGE_LOW ) {
201+ sample = (((sample - RANGE_LOW ) * RANGE_SCALE ) >> RANGE_SHIFT ) + RANGE_LOW ;
202+ } else if (sample > RANGE_HIGH ) {
203+ sample = (((sample - RANGE_HIGH ) * RANGE_SCALE ) >> RANGE_SHIFT ) + RANGE_HIGH ;
204+ }
205+ return sample ;
206+ }
207+
182208audioio_get_buffer_result_t audiodelays_echo_get_buffer (audiodelays_echo_obj_t * self , bool single_channel_output , uint8_t channel ,
183209 uint8_t * * buffer , uint32_t * buffer_length ) {
184210
185- uint32_t * word_buffer = (uint32_t * )self -> buffer ;
186- uint32_t length = self -> buffer_len / sizeof (uint32_t );
187- uint32_t echo_buf_len = self -> echo_buffer_len / sizeof (uint32_t );
188- mp_float_t f_mix = synthio_block_slot_get (& self -> mix );
189- if (f_mix > 1.0 ) {
190- f_mix = 1.0 ;
191- } else if (f_mix < 0.0 ) {
192- f_mix = 0.0 ;
193- }
194- uint16_t mix = (uint16_t )(f_mix * (1 << 15 ));
211+ mp_float_t mix = MIN (1.0 , MAX (synthio_block_slot_get (& self -> mix ), 0.0 ));
212+ mp_float_t decay = MIN (1.0 , MAX (synthio_block_slot_get (& self -> decay ), 0.0 ));
195213
196- mp_float_t f_decay = synthio_block_slot_get (& self -> decay );
197- if (f_decay > 1.0 ) {
198- f_decay = 1.0 ;
199- } else if (f_decay < 0.0 ) {
200- f_decay = 0.0 ;
201- }
202- uint16_t decay = (uint16_t )(f_decay * (1 << 15 ));
214+ int16_t * word_buffer = (int16_t * )self -> buffer ;
215+ uint32_t length = self -> buffer_len / sizeof (uint16_t );
216+ int16_t * echo_buffer = (int16_t * )self -> echo_buffer ;
217+ uint32_t echo_buf_len = self -> echo_buffer_len / sizeof (uint16_t );
203218
204219 while (length != 0 ) {
205220 if (self -> sample_buffer_length == 0 ) {
206221 if (!self -> more_data ) {
207- if (self -> loop ) {
208- if (self -> sample ) {
209- audiosample_reset_buffer (self -> sample , false, 0 );
210- }
222+ if (self -> loop && self -> sample ) {
223+ audiosample_reset_buffer (self -> sample , false, 0 );
211224 } else {
212225 self -> sample = NULL ;
213226 }
@@ -216,26 +229,24 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
216229 // Load another buffer
217230 audioio_get_buffer_result_t result = audiosample_get_buffer (self -> sample , false, 0 , (uint8_t * * )& self -> sample_remaining_buffer , & self -> sample_buffer_length );
218231 // Track length in terms of words.
219- self -> sample_buffer_length /= sizeof (uint32_t );
232+ self -> sample_buffer_length /= sizeof (uint16_t ); // assuming 16 bit samples
220233 self -> more_data = result == GET_BUFFER_MORE_DATA ;
221234 }
222235 }
223236
224237 // If we have no sample keep the echo echoing
225238 if (self -> sample == NULL ) {
226239 if (MP_LIKELY (self -> bits_per_sample == 16 )) {
227- if (mix == 0 ) { // no effect and no sample sound
228- for (uint32_t i = 0 ; i < length ; i ++ ) {
229- word_buffer [i ] = 0 ;
230- }
240+ if (mix <= 0.01 ) { // no sample sound and with no mix, no echo
241+ memset (word_buffer , 0 , length * sizeof (uint16_t ));
231242 } else {
232243 // sample signed/unsigned won't matter as we have no sample
233244 for (uint32_t i = 0 ; i < length ; i ++ ) {
234- uint32_t echo = self -> echo_buffer [self -> echo_buffer_read_pos ++ ];
235- word_buffer [i ] = mult16signed (echo , decay );
236- self -> echo_buffer [self -> echo_buffer_write_pos ++ ] = word_buffer [i ];
245+ word_buffer [i ] = echo_buffer [self -> echo_buffer_read_pos ++ ] * decay ;
237246
238- word_buffer [i ] = mult16signed (word_buffer [i ], mix );
247+ echo_buffer [self -> echo_buffer_write_pos ++ ] = word_buffer [i ];
248+
249+ word_buffer [i ] = word_buffer [i ] * mix ;
239250
240251 if (self -> echo_buffer_read_pos >= echo_buf_len ) {
241252 self -> echo_buffer_read_pos = 0 ;
@@ -251,7 +262,7 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
251262 uint16_t * echo_hsrc = (uint16_t * )self -> echo_buffer ;
252263 for (uint32_t i = 0 ; i < length * 2 ; i ++ ) {
253264 uint32_t echo_word = unpack8 (echo_hsrc [i ]);
254- echo_word = mult16signed ( echo_word , decay ) ;
265+ echo_word = echo_word * decay ;
255266 hword_buffer [i ] = pack8 (echo_word );
256267
257268 echo_hsrc [self -> echo_buffer_write_pos ++ ] = hword_buffer [i ];
@@ -263,34 +274,37 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
263274 }
264275 }
265276 }
277+
266278 length = 0 ;
267279 } else { // we have a sample
268280 uint32_t n = MIN (self -> sample_buffer_length , length );
269- uint32_t * sample_src = self -> sample_remaining_buffer ;
281+ int16_t * sample_src = ( int16_t * ) self -> sample_remaining_buffer ;
270282
271283 if (MP_LIKELY (self -> bits_per_sample == 16 )) {
272- if (mix == 0 ) { // sample only
284+ if (mix <= 0.01 ) { // sample only
273285 for (uint32_t i = 0 ; i < n ; i ++ ) {
274286 word_buffer [i ] = sample_src [i ];
275287 }
276288 } else {
277289 for (uint32_t i = 0 ; i < n ; i ++ ) {
278- uint32_t sample_word = sample_src [i ];
279- if (MP_LIKELY ( !self -> samples_signed ) ) {
290+ int32_t sample_word = sample_src [i ];
291+ if (!self -> samples_signed ) {
280292 sample_word = tosigned16 (sample_word );
281293 }
282- uint32_t echo = self -> echo_buffer [self -> echo_buffer_read_pos ++ ];
283- word_buffer [i ] = add16signed (mult16signed (echo , decay ), sample_word );
284- self -> echo_buffer [self -> echo_buffer_write_pos ++ ] = word_buffer [i ];
285294
286- word_buffer [i ] = add16signed (mult16signed (sample_word , 32768 - mix ), mult16signed (word_buffer [i ], mix ));
295+ int32_t word = (echo_buffer [self -> echo_buffer_read_pos ++ ] * decay ) + sample_word ;
296+ word_buffer [i ] = mix_down_sample (word );
297+
298+ echo_buffer [self -> echo_buffer_write_pos ++ ] = word_buffer [i ];
287299
288300 if (self -> echo_buffer_read_pos >= echo_buf_len ) {
289301 self -> echo_buffer_read_pos = 0 ;
290302 }
291303 if (self -> echo_buffer_write_pos >= echo_buf_len ) {
292304 self -> echo_buffer_write_pos = 0 ;
293305 }
306+
307+ word_buffer [i ] = (sample_word * (1.0 - mix )) + (word_buffer [i ] * mix );
294308 }
295309 }
296310 } else { // bits per sample is 8
@@ -303,11 +317,11 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
303317 if (MP_LIKELY (!self -> samples_signed )) {
304318 sample_word = tosigned16 (sample_word );
305319 }
306- echo_word = mult16signed ( echo_word , decay ) ;
307- sample_word = add16signed ( sample_word , echo_word ) ;
320+ echo_word = echo_word * decay ;
321+ sample_word = sample_word + echo_word ;
308322 hword_buffer [i ] = pack8 (sample_word );
309323
310- echo_hsrc [self -> echo_buffer_write_pos ++ ] = pack8 (add16signed ( sample_word , unpack8 (hword_buffer [i ]) ));
324+ echo_hsrc [self -> echo_buffer_write_pos ++ ] = pack8 (sample_word + unpack8 (hword_buffer [i ]));
311325 if (self -> echo_buffer_read_pos >= echo_buf_len ) {
312326 self -> echo_buffer_read_pos = 0 ;
313327 }
@@ -323,6 +337,7 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
323337 self -> sample_buffer_length -= n ;
324338 }
325339 }
340+
326341 * buffer = (uint8_t * )self -> buffer ;
327342 * buffer_length = self -> buffer_len ;
328343 return GET_BUFFER_MORE_DATA ;
@@ -331,18 +346,12 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
331346void audiodelays_echo_get_buffer_structure (audiodelays_echo_obj_t * self , bool single_channel_output ,
332347 bool * single_buffer , bool * samples_signed , uint32_t * max_buffer_length , uint8_t * spacing ) {
333348
334- if (self -> sample != NULL ) {
335- audiosample_get_buffer_structure (self -> sample , single_channel_output , single_buffer , samples_signed , max_buffer_length , spacing );
336- * single_buffer = false;
337- * max_buffer_length = self -> buffer_len ;
349+ * single_buffer = true;
350+ * samples_signed = true;
351+ * max_buffer_length = self -> buffer_len ;
352+ if (single_channel_output ) {
353+ * spacing = self -> channel_count ;
338354 } else {
339- * single_buffer = true;
340- * samples_signed = true;
341- * max_buffer_length = self -> buffer_len ;
342- if (single_channel_output ) {
343- * spacing = self -> channel_count ;
344- } else {
345- * spacing = 1 ;
346- }
355+ * spacing = 1 ;
347356 }
348357}
0 commit comments