1
1
// This file is part of the CircuitPython project: https://circuitpython.org
2
2
//
3
- // SPDX-FileCopyrightText: Copyright (c) 2024 Mark Komus
3
+ // SPDX-FileCopyrightText: Copyright (c) 2024 Mark Komus, Cooper Dalrymple
4
4
//
5
5
// SPDX-License-Identifier: MIT
6
6
#include "shared-bindings/audiodelays/Echo.h"
11
11
void common_hal_audiodelays_echo_construct (audiodelays_echo_obj_t * self , uint32_t max_delay_ms ,
12
12
mp_obj_t delay_ms , mp_obj_t decay , mp_obj_t mix ,
13
13
uint32_t buffer_size , uint8_t bits_per_sample ,
14
- bool samples_signed , uint8_t channel_count , uint32_t sample_rate ) {
14
+ bool samples_signed , uint8_t channel_count , uint32_t sample_rate , bool freq_shift ) {
15
+
16
+ // Set whether the echo shifts frequencies as the delay changes like a doppler effect
17
+ self -> freq_shift = freq_shift ;
15
18
16
19
// Basic settings every effect and audio sample has
17
20
// These are the effects values, not the source sample(s)
@@ -82,15 +85,17 @@ void common_hal_audiodelays_echo_construct(audiodelays_echo_obj_t *self, uint32_
82
85
}
83
86
memset (self -> echo_buffer , 0 , self -> max_echo_buffer_len );
84
87
85
- // calculate current echo buffer size we use for the given delay
88
+ // calculate everything needed for the current delay
86
89
mp_float_t f_delay_ms = synthio_block_slot_get (& self -> delay_ms );
87
- self -> current_delay_ms = f_delay_ms ;
88
- self -> echo_buffer_len = self -> sample_rate / 1000.0f * f_delay_ms * (self -> channel_count * sizeof (uint16_t ));
90
+ recalculate_delay (self , f_delay_ms );
89
91
90
92
// read is where we read previous echo from delay_ms ago to play back now
91
93
// write is where the store the latest playing sample to echo back later
92
94
self -> echo_buffer_read_pos = self -> buffer_len / sizeof (uint16_t );
93
95
self -> echo_buffer_write_pos = 0 ;
96
+
97
+ // where we read the previous echo from delay_ms ago to play back now (for freq shift)
98
+ self -> echo_buffer_left_pos = self -> echo_buffer_right_pos = 0 ;
94
99
}
95
100
96
101
bool common_hal_audiodelays_echo_deinited (audiodelays_echo_obj_t * self ) {
@@ -109,7 +114,6 @@ void common_hal_audiodelays_echo_deinit(audiodelays_echo_obj_t *self) {
109
114
self -> buffer [1 ] = NULL ;
110
115
}
111
116
112
-
113
117
mp_obj_t common_hal_audiodelays_echo_get_delay_ms (audiodelays_echo_obj_t * self ) {
114
118
return self -> delay_ms .obj ;
115
119
}
@@ -123,23 +127,32 @@ void common_hal_audiodelays_echo_set_delay_ms(audiodelays_echo_obj_t *self, mp_o
123
127
}
124
128
125
129
void recalculate_delay (audiodelays_echo_obj_t * self , mp_float_t f_delay_ms ) {
126
- // Calculate the current echo buffer length in bytes
130
+ if (self -> freq_shift ) {
131
+ // Calculate the rate of iteration over the echo buffer with 8 sub-bits
132
+ self -> echo_buffer_rate = MAX (self -> max_delay_ms / f_delay_ms * 256.0f , 1.0 );
133
+ self -> echo_buffer_len = self -> max_echo_buffer_len ;
134
+ } else {
135
+ // Calculate the current echo buffer length in bytes
136
+ uint32_t new_echo_buffer_len = self -> sample_rate / 1000.0f * f_delay_ms * (self -> channel_count * sizeof (uint16_t ));
137
+
138
+ // Check if our new echo is too long for our maximum buffer
139
+ if (new_echo_buffer_len > self -> max_echo_buffer_len ) {
140
+ return ;
141
+ } else if (new_echo_buffer_len < 0.0 ) { // or too short!
142
+ return ;
143
+ }
127
144
128
- uint32_t new_echo_buffer_len = self -> sample_rate / 1000.0f * f_delay_ms * (self -> channel_count * sizeof (uint16_t ));
145
+ // If the echo buffer is larger then our audio buffer weird things happen
146
+ if (new_echo_buffer_len < self -> buffer_len ) {
147
+ return ;
148
+ }
129
149
130
- // Check if our new echo is too long for our maximum buffer
131
- if (new_echo_buffer_len > self -> max_echo_buffer_len ) {
132
- return ;
133
- } else if (new_echo_buffer_len < 0.0 ) { // or too short!
134
- return ;
135
- }
150
+ self -> echo_buffer_len = new_echo_buffer_len ;
136
151
137
- // If the echo buffer is larger then our audio buffer weird things happen
138
- if (new_echo_buffer_len < self -> buffer_len ) {
139
- return ;
152
+ // Clear the now unused part of the buffer or some weird artifacts appear
153
+ memset (self -> echo_buffer + self -> echo_buffer_len , 0 , self -> max_echo_buffer_len - self -> echo_buffer_len );
140
154
}
141
155
142
- self -> echo_buffer_len = new_echo_buffer_len ;
143
156
self -> current_delay_ms = f_delay_ms ;
144
157
}
145
158
@@ -159,6 +172,16 @@ void common_hal_audiodelays_echo_set_mix(audiodelays_echo_obj_t *self, mp_obj_t
159
172
synthio_block_assign_slot (arg , & self -> mix , MP_QSTR_mix );
160
173
}
161
174
175
+ bool common_hal_audiodelays_echo_get_freq_shift (audiodelays_echo_obj_t * self ) {
176
+ return self -> freq_shift ;
177
+ }
178
+
179
+ void common_hal_audiodelays_echo_set_freq_shift (audiodelays_echo_obj_t * self , bool freq_shift ) {
180
+ self -> freq_shift = freq_shift ;
181
+ uint32_t delay_ms = (uint32_t )synthio_block_slot_get (& self -> delay_ms );
182
+ recalculate_delay (self , delay_ms );
183
+ }
184
+
162
185
uint32_t common_hal_audiodelays_echo_get_sample_rate (audiodelays_echo_obj_t * self ) {
163
186
return self -> sample_rate ;
164
187
}
@@ -257,6 +280,10 @@ int16_t mix_down_sample(int32_t sample) {
257
280
audioio_get_buffer_result_t audiodelays_echo_get_buffer (audiodelays_echo_obj_t * self , bool single_channel_output , uint8_t channel ,
258
281
uint8_t * * buffer , uint32_t * buffer_length ) {
259
282
283
+ if (!single_channel_output ) {
284
+ channel = 0 ;
285
+ }
286
+
260
287
// get the effect values we need from the BlockInput. These may change at run time so you need to do bounds checking if required
261
288
mp_float_t mix = MIN (1.0 , MAX (synthio_block_slot_get (& self -> mix ), 0.0 ));
262
289
mp_float_t decay = MIN (1.0 , MAX (synthio_block_slot_get (& self -> decay ), 0.0 ));
@@ -278,6 +305,15 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
278
305
int16_t * echo_buffer = (int16_t * )self -> echo_buffer ;
279
306
uint32_t echo_buf_len = self -> echo_buffer_len / sizeof (uint16_t );
280
307
308
+ // Set our echo buffer position accounting for stereo
309
+ uint32_t echo_buffer_pos = 0 ;
310
+ if (self -> freq_shift ) {
311
+ echo_buffer_pos = self -> echo_buffer_left_pos ;
312
+ if (channel == 1 ) {
313
+ echo_buffer_pos = self -> echo_buffer_right_pos ;
314
+ }
315
+ }
316
+
281
317
// Loop over the entire length of our buffer to fill it, this may require several calls to get data from the sample
282
318
while (length != 0 ) {
283
319
// Check if there is no more sample to play, we will either load more data, reset the sample if loop is on or clear the sample
@@ -314,27 +350,46 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
314
350
} else {
315
351
// Since we have no sample we can just iterate over the our entire remaining buffer and finish
316
352
for (uint32_t i = 0 ; i < length ; i ++ ) {
317
- int16_t echo = echo_buffer [self -> echo_buffer_read_pos ++ ] * decay ;
318
- echo_buffer [self -> echo_buffer_write_pos ++ ] = echo ;
353
+ int16_t echo , word = 0 ;
354
+ uint32_t next_buffer_pos = 0 ;
355
+
356
+ if (self -> freq_shift ) {
357
+ echo = echo_buffer [echo_buffer_pos >> 8 ];
358
+ next_buffer_pos = echo_buffer_pos + self -> echo_buffer_rate ;
359
+
360
+ word = echo * decay ;
361
+ for (uint32_t j = echo_buffer_pos >> 8 ; j < next_buffer_pos >> 8 ; j ++ ) {
362
+ echo_buffer [j % echo_buf_len ] = word ;
363
+ }
364
+ } else {
365
+ echo = echo_buffer [self -> echo_buffer_read_pos ++ ];
366
+ word = echo * decay ;
367
+ echo_buffer [self -> echo_buffer_write_pos ++ ] = word ;
368
+ }
369
+
370
+ word = echo * mix ;
319
371
320
372
if (MP_LIKELY (self -> bits_per_sample == 16 )) {
321
- word_buffer [i ] = echo * mix ;
373
+ word_buffer [i ] = word ;
322
374
if (!self -> samples_signed ) {
323
375
word_buffer [i ] ^= 0x8000 ;
324
376
}
325
377
} else {
326
- echo = echo * mix ;
327
- hword_buffer [i ] = echo ;
378
+ hword_buffer [i ] = (int8_t )word ;
328
379
if (!self -> samples_signed ) {
329
380
hword_buffer [i ] ^= 0x80 ;
330
381
}
331
382
}
332
383
333
- if (self -> echo_buffer_read_pos >= echo_buf_len ) {
334
- self -> echo_buffer_read_pos = 0 ;
335
- }
336
- if (self -> echo_buffer_write_pos >= echo_buf_len ) {
337
- self -> echo_buffer_write_pos = 0 ;
384
+ if (self -> freq_shift ) {
385
+ echo_buffer_pos = next_buffer_pos % (echo_buf_len << 8 );
386
+ } else {
387
+ if (self -> echo_buffer_read_pos >= echo_buf_len ) {
388
+ self -> echo_buffer_read_pos = 0 ;
389
+ }
390
+ if (self -> echo_buffer_write_pos >= echo_buf_len ) {
391
+ self -> echo_buffer_write_pos = 0 ;
392
+ }
338
393
}
339
394
}
340
395
}
@@ -370,22 +425,44 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
370
425
}
371
426
}
372
427
373
- int32_t echo = echo_buffer [self -> echo_buffer_read_pos ++ ] * decay ;
374
- int32_t word = echo + sample_word ;
428
+ int32_t echo , word = 0 ;
429
+ uint32_t next_buffer_pos = 0 ;
430
+ if (self -> freq_shift ) {
431
+ echo = echo_buffer [echo_buffer_pos >> 8 ];
432
+ next_buffer_pos = echo_buffer_pos + self -> echo_buffer_rate ;
433
+ word = echo * decay + sample_word ;
434
+ } else {
435
+ echo = echo_buffer [self -> echo_buffer_read_pos ++ ];
436
+ word = echo * decay + sample_word ;
437
+ }
375
438
376
439
if (MP_LIKELY (self -> bits_per_sample == 16 )) {
377
440
word = mix_down_sample (word );
378
- echo_buffer [self -> echo_buffer_write_pos ++ ] = (int16_t )word ;
441
+ if (self -> freq_shift ) {
442
+ for (uint32_t j = echo_buffer_pos >> 8 ; j < next_buffer_pos >> 8 ; j ++ ) {
443
+ echo_buffer [j % echo_buf_len ] = (int16_t )word ;
444
+ }
445
+ } else {
446
+ echo_buffer [self -> echo_buffer_write_pos ++ ] = (int16_t )word ;
447
+ }
379
448
} else {
380
449
// Do not have mix_down for 8 bit so just hard cap samples into 1 byte
381
450
if (word > 127 ) {
382
451
word = 127 ;
383
452
} else if (word < -128 ) {
384
453
word = -128 ;
385
454
}
386
- echo_buffer [self -> echo_buffer_write_pos ++ ] = (int8_t )word ;
455
+ if (self -> freq_shift ) {
456
+ for (uint32_t j = echo_buffer_pos >> 8 ; j < next_buffer_pos >> 8 ; j ++ ) {
457
+ echo_buffer [j % echo_buf_len ] = (int8_t )word ;
458
+ }
459
+ } else {
460
+ echo_buffer [self -> echo_buffer_write_pos ++ ] = (int8_t )word ;
461
+ }
387
462
}
388
463
464
+ word = echo + sample_word ;
465
+
389
466
if (MP_LIKELY (self -> bits_per_sample == 16 )) {
390
467
word_buffer [i ] = (sample_word * (1.0 - mix )) + (word * mix );
391
468
if (!self -> samples_signed ) {
@@ -400,11 +477,15 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
400
477
}
401
478
}
402
479
403
- if (self -> echo_buffer_read_pos >= echo_buf_len ) {
404
- self -> echo_buffer_read_pos = 0 ;
405
- }
406
- if (self -> echo_buffer_write_pos >= echo_buf_len ) {
407
- self -> echo_buffer_write_pos = 0 ;
480
+ if (self -> freq_shift ) {
481
+ echo_buffer_pos = next_buffer_pos % (echo_buf_len << 8 );
482
+ } else {
483
+ if (self -> echo_buffer_read_pos >= echo_buf_len ) {
484
+ self -> echo_buffer_read_pos = 0 ;
485
+ }
486
+ if (self -> echo_buffer_write_pos >= echo_buf_len ) {
487
+ self -> echo_buffer_write_pos = 0 ;
488
+ }
408
489
}
409
490
}
410
491
}
@@ -418,6 +499,14 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
418
499
}
419
500
}
420
501
502
+ if (self -> freq_shift ) {
503
+ if (channel == 0 ) {
504
+ self -> echo_buffer_left_pos = echo_buffer_pos ;
505
+ } else if (channel == 1 ) {
506
+ self -> echo_buffer_right_pos = echo_buffer_pos ;
507
+ }
508
+ }
509
+
421
510
// Finally pass our buffer and length to the calling audio function
422
511
* buffer = (uint8_t * )self -> buffer [self -> last_buf_idx ];
423
512
* buffer_length = self -> buffer_len ;
0 commit comments