11// This file is part of the CircuitPython project: https://circuitpython.org
22//
3- // SPDX-FileCopyrightText: Copyright (c) 2024 Mark Komus
3+ // SPDX-FileCopyrightText: Copyright (c) 2024 Mark Komus, Cooper Dalrymple
44//
55// SPDX-License-Identifier: MIT
66#include "shared-bindings/audiodelays/Echo.h"
1111void common_hal_audiodelays_echo_construct (audiodelays_echo_obj_t * self , uint32_t max_delay_ms ,
1212 mp_obj_t delay_ms , mp_obj_t decay , mp_obj_t mix ,
1313 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 ;
1518
1619 // Basic settings every effect and audio sample has
1720 // 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_
8285 }
8386 memset (self -> echo_buffer , 0 , self -> max_echo_buffer_len );
8487
85- // calculate current echo buffer size we use for the given delay
88+ // calculate everything needed for the current delay
8689 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 );
8991
9092 // read is where we read previous echo from delay_ms ago to play back now
9193 // write is where the store the latest playing sample to echo back later
9294 self -> echo_buffer_read_pos = self -> buffer_len / sizeof (uint16_t );
9395 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 ;
9499}
95100
96101bool 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) {
109114 self -> buffer [1 ] = NULL ;
110115}
111116
112-
113117mp_obj_t common_hal_audiodelays_echo_get_delay_ms (audiodelays_echo_obj_t * self ) {
114118 return self -> delay_ms .obj ;
115119}
@@ -123,23 +127,32 @@ void common_hal_audiodelays_echo_set_delay_ms(audiodelays_echo_obj_t *self, mp_o
123127}
124128
125129void 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+ }
127144
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+ }
129149
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 ;
136151
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 );
140154 }
141155
142- self -> echo_buffer_len = new_echo_buffer_len ;
143156 self -> current_delay_ms = f_delay_ms ;
144157}
145158
@@ -159,6 +172,16 @@ void common_hal_audiodelays_echo_set_mix(audiodelays_echo_obj_t *self, mp_obj_t
159172 synthio_block_assign_slot (arg , & self -> mix , MP_QSTR_mix );
160173}
161174
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+
162185uint32_t common_hal_audiodelays_echo_get_sample_rate (audiodelays_echo_obj_t * self ) {
163186 return self -> sample_rate ;
164187}
@@ -257,6 +280,10 @@ int16_t mix_down_sample(int32_t sample) {
257280audioio_get_buffer_result_t audiodelays_echo_get_buffer (audiodelays_echo_obj_t * self , bool single_channel_output , uint8_t channel ,
258281 uint8_t * * buffer , uint32_t * buffer_length ) {
259282
283+ if (!single_channel_output ) {
284+ channel = 0 ;
285+ }
286+
260287 // get the effect values we need from the BlockInput. These may change at run time so you need to do bounds checking if required
261288 mp_float_t mix = MIN (1.0 , MAX (synthio_block_slot_get (& self -> mix ), 0.0 ));
262289 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 *
278305 int16_t * echo_buffer = (int16_t * )self -> echo_buffer ;
279306 uint32_t echo_buf_len = self -> echo_buffer_len / sizeof (uint16_t );
280307
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+
281317 // Loop over the entire length of our buffer to fill it, this may require several calls to get data from the sample
282318 while (length != 0 ) {
283319 // 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 *
314350 } else {
315351 // Since we have no sample we can just iterate over the our entire remaining buffer and finish
316352 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 ;
319371
320372 if (MP_LIKELY (self -> bits_per_sample == 16 )) {
321- word_buffer [i ] = echo * mix ;
373+ word_buffer [i ] = word ;
322374 if (!self -> samples_signed ) {
323375 word_buffer [i ] ^= 0x8000 ;
324376 }
325377 } else {
326- echo = echo * mix ;
327- hword_buffer [i ] = echo ;
378+ hword_buffer [i ] = (int8_t )word ;
328379 if (!self -> samples_signed ) {
329380 hword_buffer [i ] ^= 0x80 ;
330381 }
331382 }
332383
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+ }
338393 }
339394 }
340395 }
@@ -370,22 +425,44 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
370425 }
371426 }
372427
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+ }
375438
376439 if (MP_LIKELY (self -> bits_per_sample == 16 )) {
377440 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+ }
379448 } else {
380449 // Do not have mix_down for 8 bit so just hard cap samples into 1 byte
381450 if (word > 127 ) {
382451 word = 127 ;
383452 } else if (word < -128 ) {
384453 word = -128 ;
385454 }
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+ }
387462 }
388463
464+ word = echo + sample_word ;
465+
389466 if (MP_LIKELY (self -> bits_per_sample == 16 )) {
390467 word_buffer [i ] = (sample_word * (1.0 - mix )) + (word * mix );
391468 if (!self -> samples_signed ) {
@@ -400,11 +477,15 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
400477 }
401478 }
402479
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+ }
408489 }
409490 }
410491 }
@@ -418,6 +499,14 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
418499 }
419500 }
420501
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+
421510 // Finally pass our buffer and length to the calling audio function
422511 * buffer = (uint8_t * )self -> buffer [self -> last_buf_idx ];
423512 * buffer_length = self -> buffer_len ;
0 commit comments