@@ -15,26 +15,46 @@ void common_hal_audiodelays_echo_construct(audiodelays_echo_obj_t *self, uint32_
1515 uint32_t buffer_size , uint8_t bits_per_sample ,
1616 bool samples_signed , uint8_t channel_count , uint32_t sample_rate ) {
1717
18- self -> bits_per_sample = bits_per_sample ;
19- self -> samples_signed = samples_signed ;
20- self -> channel_count = channel_count ;
21- self -> sample_rate = sample_rate ;
18+ // Basic settings every effect and audio sample has
19+ // These are the effects values, not the source sample(s)
20+ self -> bits_per_sample = bits_per_sample ; // Most common is 16, but 8 is also supported in many places
21+ self -> samples_signed = samples_signed ; // Are the samples we provide signed (common is true)
22+ self -> channel_count = channel_count ; // Channels can be 1 for mono or 2 for stereo
23+ self -> sample_rate = sample_rate ; // Sample rate for the effect, this generally needs to match all audio objects
24+
25+ // To smooth things out as CircuitPython is doing other tasks most audio objects have a buffer
26+ // A double buffer is set up here so the audio output can use DMA on buffer 1 while we
27+ // write to and create buffer 2.
28+ // This buffer is what is passed to the audio component that plays the effect.
29+ // Samples are set sequentially. For stereo audio they are passed L/R/L/R/...
30+ self -> buffer_len = buffer_size ; // in bytes
2231
23- self -> buffer_len = buffer_size ;
2432 self -> buffer [0 ] = m_malloc (self -> buffer_len );
2533 if (self -> buffer [0 ] == NULL ) {
2634 common_hal_audiodelays_echo_deinit (self );
2735 m_malloc_fail (self -> buffer_len );
2836 }
2937 memset (self -> buffer [0 ], 0 , self -> buffer_len );
38+
3039 self -> buffer [1 ] = m_malloc (self -> buffer_len );
3140 if (self -> buffer [1 ] == NULL ) {
3241 common_hal_audiodelays_echo_deinit (self );
3342 m_malloc_fail (self -> buffer_len );
3443 }
3544 memset (self -> buffer [1 ], 0 , self -> buffer_len );
36- self -> last_buf_idx = 1 ;
3745
46+ self -> last_buf_idx = 1 ; // Which buffer to use first, toggle between 0 and 1
47+
48+ // Initialize other values most effects will need.
49+ self -> sample = NULL ; // The current playing sample
50+ self -> sample_remaining_buffer = NULL ; // Pointer to the start of the sample buffer we have not played
51+ self -> sample_buffer_length = 0 ; // How many samples do we have left to play (these may be 16 bit!)
52+ self -> loop = false; // When the sample is done do we loop to the start again or stop (e.g. in a wav file)
53+ self -> more_data = false; // Is there still more data to read from the sample or did we finish
54+
55+ // The below section sets up the echo effect's starting values. For a different effect this section will change
56+
57+ // If we did not receive a BlockInput we need to create a default float value
3858 if (decay == MP_OBJ_NULL ) {
3959 decay = mp_obj_new_float (0.7 );
4060 }
@@ -50,30 +70,28 @@ void common_hal_audiodelays_echo_construct(audiodelays_echo_obj_t *self, uint32_
5070 }
5171 synthio_block_assign_slot (mix , & self -> mix , MP_QSTR_mix );
5272
53- // Set the echo buffer for the max possible delay
73+ // Many effects may need buffers of what was played this shows how it was done for the echo
74+ // A maximum length buffer was created and then the current echo length can be dynamically changes
75+ // without having to reallocate a large chunk of memory.
76+
77+ // Allocate the echo buffer for the max possible delay
5478 self -> max_delay_ms = max_delay_ms ;
55- self -> max_echo_buffer_len = self -> sample_rate / 1000.0f * max_delay_ms * (self -> channel_count * (self -> bits_per_sample / 8 ));
79+ self -> max_echo_buffer_len = self -> sample_rate / 1000.0f * max_delay_ms * (self -> channel_count * (self -> bits_per_sample / 8 )); // bytes
5680 self -> echo_buffer = m_malloc (self -> max_echo_buffer_len );
5781 if (self -> echo_buffer == NULL ) {
5882 common_hal_audiodelays_echo_deinit (self );
5983 m_malloc_fail (self -> max_echo_buffer_len );
6084 }
6185 memset (self -> echo_buffer , 0 , self -> max_echo_buffer_len );
6286
63- // calculate current echo buffer size for the set delay
87+ // calculate current echo buffer size we use for the given delay
6488 mp_float_t f_delay_ms = synthio_block_slot_get (& self -> delay_ms );
6589 self -> echo_buffer_len = self -> sample_rate / 1000.0f * f_delay_ms * (self -> channel_count * (self -> bits_per_sample / 8 ));
6690
67- // read is where we store the incoming sample
91+ // read is where we store the incoming sample + previous echo
6892 // write is what we send to the outgoing buffer
69- self -> echo_buffer_read_pos = self -> buffer_len / sizeof ( uint16_t );
93+ self -> echo_buffer_read_pos = self -> buffer_len / ( self -> bits_per_sample / 8 );
7094 self -> echo_buffer_write_pos = 0 ;
71-
72- self -> sample = NULL ;
73- self -> sample_remaining_buffer = NULL ;
74- self -> sample_buffer_length = 0 ;
75- self -> loop = false;
76- self -> more_data = false;
7795}
7896
7997bool common_hal_audiodelays_echo_deinited (audiodelays_echo_obj_t * self ) {
@@ -153,6 +171,10 @@ bool common_hal_audiodelays_echo_get_playing(audiodelays_echo_obj_t *self) {
153171}
154172
155173void common_hal_audiodelays_echo_play (audiodelays_echo_obj_t * self , mp_obj_t sample , bool loop ) {
174+ // When a sample is to be played we must ensure the samples values matches what we expect
175+ // Then we reset the sample and get the first buffer to play
176+ // The get_buffer function will actually process that data
177+
156178 if (audiosample_sample_rate (sample ) != self -> sample_rate ) {
157179 mp_raise_ValueError (MP_ERROR_TEXT ("The sample's sample rate does not match" ));
158180 }
@@ -170,19 +192,23 @@ void common_hal_audiodelays_echo_play(audiodelays_echo_obj_t *self, mp_obj_t sam
170192 if (samples_signed != self -> samples_signed ) {
171193 mp_raise_ValueError (MP_ERROR_TEXT ("The sample's signedness does not match" ));
172194 }
195+
173196 self -> sample = sample ;
174197 self -> loop = loop ;
175198
176199 audiosample_reset_buffer (self -> sample , false, 0 );
177200 audioio_get_buffer_result_t result = audiosample_get_buffer (self -> sample , false, 0 , (uint8_t * * )& self -> sample_remaining_buffer , & self -> sample_buffer_length );
178- // Track length in terms of words.
179- self -> sample_buffer_length /= sizeof (uint16_t );
201+
202+ // Track remaining sample length in terms of bytes per sample
203+ self -> sample_buffer_length /= (self -> bits_per_sample / 8 );
180204 self -> more_data = result == GET_BUFFER_MORE_DATA ;
181205
182206 return ;
183207}
184208
185209void common_hal_audiodelays_echo_stop (audiodelays_echo_obj_t * self ) {
210+ // When the sample is set to stop playing do any cleanup here
211+ // For echo we clear the sample but the echo continues until the object reading our effect stops
186212 self -> sample = NULL ;
187213 return ;
188214}
@@ -216,40 +242,48 @@ int16_t mix_down_sample(int32_t sample) {
216242audioio_get_buffer_result_t audiodelays_echo_get_buffer (audiodelays_echo_obj_t * self , bool single_channel_output , uint8_t channel ,
217243 uint8_t * * buffer , uint32_t * buffer_length ) {
218244
245+ // get the effect values we need from the BlockInput. These may change at run time so you need to do bounds checking if required
219246 mp_float_t mix = MIN (1.0 , MAX (synthio_block_slot_get (& self -> mix ), 0.0 ));
220247 mp_float_t decay = MIN (1.0 , MAX (synthio_block_slot_get (& self -> decay ), 0.0 ));
221248
249+ // Switch our buffers to the other buffer
222250 self -> last_buf_idx = !self -> last_buf_idx ;
251+
252+ // If we are using 16 bit samples we need a 16 bit pointer, for 8 bit we can just use the buffer
223253 int16_t * word_buffer = (int16_t * )self -> buffer [self -> last_buf_idx ];
224- uint32_t length = self -> buffer_len / sizeof (uint16_t );
254+ uint32_t length = self -> buffer_len / (self -> bits_per_sample / 8 );
255+
225256 int16_t * echo_buffer = (int16_t * )self -> echo_buffer ;
226- uint32_t echo_buf_len = self -> echo_buffer_len / sizeof ( uint16_t );
257+ uint32_t echo_buf_len = self -> echo_buffer_len / ( self -> bits_per_sample / 8 );
227258
259+ // Loop over the entire length of our buffer to fill it, this may require several calls to get data from the sample
228260 while (length != 0 ) {
261+
262+ // Check if there is no more sample to play
229263 if (self -> sample_buffer_length == 0 ) {
230- if (!self -> more_data ) {
231- if (self -> loop && self -> sample ) {
264+ if (!self -> more_data ) { // The sample has indicated it has no more data to play
265+ if (self -> loop && self -> sample ) { // If we are supposed to loop reset the sample to the start
232266 audiosample_reset_buffer (self -> sample , false, 0 );
233- } else {
267+ } else { // If we were not supposed to loop the sample, stop playing it but we still need to play the echo
234268 self -> sample = NULL ;
235269 }
236270 }
237271 if (self -> sample ) {
238- // Load another buffer
272+ // Load another sample buffer to play
239273 audioio_get_buffer_result_t result = audiosample_get_buffer (self -> sample , false, 0 , (uint8_t * * )& self -> sample_remaining_buffer , & self -> sample_buffer_length );
240274 // Track length in terms of words.
241- self -> sample_buffer_length /= sizeof ( uint16_t ); // assuming 16 bit samples
275+ self -> sample_buffer_length /= ( self -> bits_per_sample / 8 );
242276 self -> more_data = result == GET_BUFFER_MORE_DATA ;
243277 }
244278 }
245279
246280 // If we have no sample keep the echo echoing
247281 if (self -> sample == NULL ) {
248282 if (MP_LIKELY (self -> bits_per_sample == 16 )) {
249- if (mix <= 0.01 ) { // no sample sound and with no mix, no echo
283+ if (mix <= 0.01 ) { // Mix of 0 is pure sample sound. We have no sample so no sound
250284 memset (word_buffer , 0 , length * sizeof (uint16_t ));
251285 } else {
252- // sample signed/unsigned won't matter as we have no sample
286+ // Since we have no sample we can just iterate over the our entire buffer
253287 for (uint32_t i = 0 ; i < length ; i ++ ) {
254288 word_buffer [i ] = echo_buffer [self -> echo_buffer_read_pos ++ ] * decay ;
255289
@@ -267,30 +301,36 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
267301
268302 }
269303 } else { // bits per sample is 8
270- uint16_t * hword_buffer = (uint16_t * )word_buffer ;
271- uint16_t * echo_hsrc = (uint16_t * )self -> echo_buffer ;
272- for (uint32_t i = 0 ; i < length * 2 ; i ++ ) {
273- uint32_t echo_word = unpack8 (echo_hsrc [i ]);
274- echo_word = echo_word * decay ;
275- hword_buffer [i ] = pack8 (echo_word );
276-
277- echo_hsrc [self -> echo_buffer_write_pos ++ ] = hword_buffer [i ];
278- if (self -> echo_buffer_read_pos >= echo_buf_len ) {
279- self -> echo_buffer_read_pos = 0 ;
280- }
281- if (self -> echo_buffer_write_pos >= echo_buf_len ) {
282- self -> echo_buffer_write_pos = 0 ;
304+ /* Still to be updated
305+ uint16_t *hword_buffer = (uint16_t *)word_buffer;
306+ uint16_t *echo_hsrc = (uint16_t *)self->echo_buffer;
307+ for (uint32_t i = 0; i < length * 2; i++) {
308+ uint32_t echo_word = unpack8(echo_hsrc[i]);
309+ echo_word = echo_word * decay;
310+ hword_buffer[i] = pack8(echo_word);
311+
312+ echo_hsrc[self->echo_buffer_write_pos++] = hword_buffer[i];
313+ if (self->echo_buffer_read_pos >= echo_buf_len) {
314+ self->echo_buffer_read_pos = 0;
315+ }
316+ if (self->echo_buffer_write_pos >= echo_buf_len) {
317+ self->echo_buffer_write_pos = 0;
318+ }
283319 }
284- }
320+ */
285321 }
286322
287323 length = 0 ;
288- } else { // we have a sample
324+ } else {
325+ // we have a sample to play and echo
326+
327+ // Determine how many bytes we can process to our buffer, the less of the sample we have left and our buffer remaining
289328 uint32_t n = MIN (self -> sample_buffer_length , length );
329+
290330 int16_t * sample_src = (int16_t * )self -> sample_remaining_buffer ;
291331
292332 if (MP_LIKELY (self -> bits_per_sample == 16 )) {
293- if (mix <= 0.01 ) { // sample only
333+ if (mix <= 0.01 ) { // if mix is zero pure sample only
294334 for (uint32_t i = 0 ; i < n ; i ++ ) {
295335 word_buffer [i ] = sample_src [i ];
296336 }
@@ -317,27 +357,29 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
317357 }
318358 }
319359 } else { // bits per sample is 8
320- uint16_t * hword_buffer = (uint16_t * )word_buffer ;
321- uint16_t * sample_hsrc = (uint16_t * )sample_src ;
322- uint16_t * echo_hsrc = (uint16_t * )self -> echo_buffer ;
323- for (uint32_t i = 0 ; i < n * 2 ; i ++ ) {
324- uint32_t sample_word = unpack8 (sample_hsrc [i ]);
325- uint32_t echo_word = unpack8 (echo_hsrc [i ]);
326- if (MP_LIKELY (!self -> samples_signed )) {
327- sample_word = tosigned16 (sample_word );
328- }
329- echo_word = echo_word * decay ;
330- sample_word = sample_word + echo_word ;
331- hword_buffer [i ] = pack8 (sample_word );
360+ /* to be updated
361+ uint16_t *hword_buffer = (uint16_t *)word_buffer;
362+ uint16_t *sample_hsrc = (uint16_t *)sample_src;
363+ uint16_t *echo_hsrc = (uint16_t *)self->echo_buffer;
364+ for (uint32_t i = 0; i < n * 2; i++) {
365+ uint32_t sample_word = unpack8(sample_hsrc[i]);
366+ uint32_t echo_word = unpack8(echo_hsrc[i]);
367+ if (MP_LIKELY(!self->samples_signed)) {
368+ sample_word = tosigned16(sample_word);
369+ }
370+ echo_word = echo_word * decay;
371+ sample_word = sample_word + echo_word;
372+ hword_buffer[i] = pack8(sample_word);
332373
333- echo_hsrc [self -> echo_buffer_write_pos ++ ] = pack8 (sample_word + unpack8 (hword_buffer [i ]));
334- if (self -> echo_buffer_read_pos >= echo_buf_len ) {
335- self -> echo_buffer_read_pos = 0 ;
336- }
337- if (self -> echo_buffer_write_pos >= echo_buf_len ) {
338- self -> echo_buffer_write_pos = 0 ;
374+ echo_hsrc[self->echo_buffer_write_pos++] = pack8(sample_word + unpack8(hword_buffer[i]));
375+ if (self->echo_buffer_read_pos >= echo_buf_len) {
376+ self->echo_buffer_read_pos = 0;
377+ }
378+ if (self->echo_buffer_write_pos >= echo_buf_len) {
379+ self->echo_buffer_write_pos = 0;
380+ }
339381 }
340- }
382+ */
341383 }
342384
343385 length -= n ;
0 commit comments