@@ -43,10 +43,6 @@ void common_hal_audiodelays_echo_construct(audiodelays_echo_obj_t *self, uint32_
43
43
}
44
44
synthio_block_assign_slot (mix , & self -> mix , MP_QSTR_mix );
45
45
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
-
50
46
// Set the echo buffer for the max possible delay
51
47
self -> max_delay_ms = max_delay_ms ;
52
48
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_
57
53
}
58
54
memset (self -> echo_buffer , 0 , self -> max_echo_buffer_len );
59
55
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
+
60
60
// read is where we store the incoming sample
61
61
// 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 );
63
63
self -> echo_buffer_write_pos = 0 ;
64
64
65
65
self -> sample = NULL ;
@@ -168,7 +168,7 @@ void common_hal_audiodelays_echo_play(audiodelays_echo_obj_t *self, mp_obj_t sam
168
168
audiosample_reset_buffer (self -> sample , false, 0 );
169
169
audioio_get_buffer_result_t result = audiosample_get_buffer (self -> sample , false, 0 , (uint8_t * * )& self -> sample_remaining_buffer , & self -> sample_buffer_length );
170
170
// Track length in terms of words.
171
- self -> sample_buffer_length /= sizeof (uint32_t );
171
+ self -> sample_buffer_length /= sizeof (uint16_t );
172
172
self -> more_data = result == GET_BUFFER_MORE_DATA ;
173
173
174
174
return ;
@@ -179,35 +179,48 @@ void common_hal_audiodelays_echo_stop(audiodelays_echo_obj_t *self) {
179
179
return ;
180
180
}
181
181
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
+
182
208
audioio_get_buffer_result_t audiodelays_echo_get_buffer (audiodelays_echo_obj_t * self , bool single_channel_output , uint8_t channel ,
183
209
uint8_t * * buffer , uint32_t * buffer_length ) {
184
210
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 ));
195
213
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 );
203
218
204
219
while (length != 0 ) {
205
220
if (self -> sample_buffer_length == 0 ) {
206
221
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 );
211
224
} else {
212
225
self -> sample = NULL ;
213
226
}
@@ -216,26 +229,24 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
216
229
// Load another buffer
217
230
audioio_get_buffer_result_t result = audiosample_get_buffer (self -> sample , false, 0 , (uint8_t * * )& self -> sample_remaining_buffer , & self -> sample_buffer_length );
218
231
// 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
220
233
self -> more_data = result == GET_BUFFER_MORE_DATA ;
221
234
}
222
235
}
223
236
224
237
// If we have no sample keep the echo echoing
225
238
if (self -> sample == NULL ) {
226
239
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 ));
231
242
} else {
232
243
// sample signed/unsigned won't matter as we have no sample
233
244
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 ;
237
246
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 ;
239
250
240
251
if (self -> echo_buffer_read_pos >= echo_buf_len ) {
241
252
self -> echo_buffer_read_pos = 0 ;
@@ -251,7 +262,7 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
251
262
uint16_t * echo_hsrc = (uint16_t * )self -> echo_buffer ;
252
263
for (uint32_t i = 0 ; i < length * 2 ; i ++ ) {
253
264
uint32_t echo_word = unpack8 (echo_hsrc [i ]);
254
- echo_word = mult16signed ( echo_word , decay ) ;
265
+ echo_word = echo_word * decay ;
255
266
hword_buffer [i ] = pack8 (echo_word );
256
267
257
268
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 *
263
274
}
264
275
}
265
276
}
277
+
266
278
length = 0 ;
267
279
} else { // we have a sample
268
280
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 ;
270
282
271
283
if (MP_LIKELY (self -> bits_per_sample == 16 )) {
272
- if (mix == 0 ) { // sample only
284
+ if (mix <= 0.01 ) { // sample only
273
285
for (uint32_t i = 0 ; i < n ; i ++ ) {
274
286
word_buffer [i ] = sample_src [i ];
275
287
}
276
288
} else {
277
289
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 ) {
280
292
sample_word = tosigned16 (sample_word );
281
293
}
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 ];
285
294
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 ];
287
299
288
300
if (self -> echo_buffer_read_pos >= echo_buf_len ) {
289
301
self -> echo_buffer_read_pos = 0 ;
290
302
}
291
303
if (self -> echo_buffer_write_pos >= echo_buf_len ) {
292
304
self -> echo_buffer_write_pos = 0 ;
293
305
}
306
+
307
+ word_buffer [i ] = (sample_word * (1.0 - mix )) + (word_buffer [i ] * mix );
294
308
}
295
309
}
296
310
} else { // bits per sample is 8
@@ -303,11 +317,11 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
303
317
if (MP_LIKELY (!self -> samples_signed )) {
304
318
sample_word = tosigned16 (sample_word );
305
319
}
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 ;
308
322
hword_buffer [i ] = pack8 (sample_word );
309
323
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 ]));
311
325
if (self -> echo_buffer_read_pos >= echo_buf_len ) {
312
326
self -> echo_buffer_read_pos = 0 ;
313
327
}
@@ -323,6 +337,7 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
323
337
self -> sample_buffer_length -= n ;
324
338
}
325
339
}
340
+
326
341
* buffer = (uint8_t * )self -> buffer ;
327
342
* buffer_length = self -> buffer_len ;
328
343
return GET_BUFFER_MORE_DATA ;
@@ -331,18 +346,12 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
331
346
void audiodelays_echo_get_buffer_structure (audiodelays_echo_obj_t * self , bool single_channel_output ,
332
347
bool * single_buffer , bool * samples_signed , uint32_t * max_buffer_length , uint8_t * spacing ) {
333
348
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 ;
338
354
} 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 ;
347
356
}
348
357
}
0 commit comments