@@ -15,26 +15,46 @@ void common_hal_audiodelays_echo_construct(audiodelays_echo_obj_t *self, uint32_
15
15
uint32_t buffer_size , uint8_t bits_per_sample ,
16
16
bool samples_signed , uint8_t channel_count , uint32_t sample_rate ) {
17
17
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
22
31
23
- self -> buffer_len = buffer_size ;
24
32
self -> buffer [0 ] = m_malloc (self -> buffer_len );
25
33
if (self -> buffer [0 ] == NULL ) {
26
34
common_hal_audiodelays_echo_deinit (self );
27
35
m_malloc_fail (self -> buffer_len );
28
36
}
29
37
memset (self -> buffer [0 ], 0 , self -> buffer_len );
38
+
30
39
self -> buffer [1 ] = m_malloc (self -> buffer_len );
31
40
if (self -> buffer [1 ] == NULL ) {
32
41
common_hal_audiodelays_echo_deinit (self );
33
42
m_malloc_fail (self -> buffer_len );
34
43
}
35
44
memset (self -> buffer [1 ], 0 , self -> buffer_len );
36
- self -> last_buf_idx = 1 ;
37
45
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
38
58
if (decay == MP_OBJ_NULL ) {
39
59
decay = mp_obj_new_float (0.7 );
40
60
}
@@ -50,30 +70,28 @@ void common_hal_audiodelays_echo_construct(audiodelays_echo_obj_t *self, uint32_
50
70
}
51
71
synthio_block_assign_slot (mix , & self -> mix , MP_QSTR_mix );
52
72
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
54
78
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
56
80
self -> echo_buffer = m_malloc (self -> max_echo_buffer_len );
57
81
if (self -> echo_buffer == NULL ) {
58
82
common_hal_audiodelays_echo_deinit (self );
59
83
m_malloc_fail (self -> max_echo_buffer_len );
60
84
}
61
85
memset (self -> echo_buffer , 0 , self -> max_echo_buffer_len );
62
86
63
- // calculate current echo buffer size for the set delay
87
+ // calculate current echo buffer size we use for the given delay
64
88
mp_float_t f_delay_ms = synthio_block_slot_get (& self -> delay_ms );
65
89
self -> echo_buffer_len = self -> sample_rate / 1000.0f * f_delay_ms * (self -> channel_count * (self -> bits_per_sample / 8 ));
66
90
67
- // read is where we store the incoming sample
91
+ // read is where we store the incoming sample + previous echo
68
92
// 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 );
70
94
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;
77
95
}
78
96
79
97
bool 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) {
153
171
}
154
172
155
173
void 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
+
156
178
if (audiosample_sample_rate (sample ) != self -> sample_rate ) {
157
179
mp_raise_ValueError (MP_ERROR_TEXT ("The sample's sample rate does not match" ));
158
180
}
@@ -170,19 +192,23 @@ void common_hal_audiodelays_echo_play(audiodelays_echo_obj_t *self, mp_obj_t sam
170
192
if (samples_signed != self -> samples_signed ) {
171
193
mp_raise_ValueError (MP_ERROR_TEXT ("The sample's signedness does not match" ));
172
194
}
195
+
173
196
self -> sample = sample ;
174
197
self -> loop = loop ;
175
198
176
199
audiosample_reset_buffer (self -> sample , false, 0 );
177
200
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 );
180
204
self -> more_data = result == GET_BUFFER_MORE_DATA ;
181
205
182
206
return ;
183
207
}
184
208
185
209
void 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
186
212
self -> sample = NULL ;
187
213
return ;
188
214
}
@@ -216,40 +242,48 @@ int16_t mix_down_sample(int32_t sample) {
216
242
audioio_get_buffer_result_t audiodelays_echo_get_buffer (audiodelays_echo_obj_t * self , bool single_channel_output , uint8_t channel ,
217
243
uint8_t * * buffer , uint32_t * buffer_length ) {
218
244
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
219
246
mp_float_t mix = MIN (1.0 , MAX (synthio_block_slot_get (& self -> mix ), 0.0 ));
220
247
mp_float_t decay = MIN (1.0 , MAX (synthio_block_slot_get (& self -> decay ), 0.0 ));
221
248
249
+ // Switch our buffers to the other buffer
222
250
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
223
253
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
+
225
256
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 );
227
258
259
+ // Loop over the entire length of our buffer to fill it, this may require several calls to get data from the sample
228
260
while (length != 0 ) {
261
+
262
+ // Check if there is no more sample to play
229
263
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
232
266
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
234
268
self -> sample = NULL ;
235
269
}
236
270
}
237
271
if (self -> sample ) {
238
- // Load another buffer
272
+ // Load another sample buffer to play
239
273
audioio_get_buffer_result_t result = audiosample_get_buffer (self -> sample , false, 0 , (uint8_t * * )& self -> sample_remaining_buffer , & self -> sample_buffer_length );
240
274
// 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 );
242
276
self -> more_data = result == GET_BUFFER_MORE_DATA ;
243
277
}
244
278
}
245
279
246
280
// If we have no sample keep the echo echoing
247
281
if (self -> sample == NULL ) {
248
282
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
250
284
memset (word_buffer , 0 , length * sizeof (uint16_t ));
251
285
} 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
253
287
for (uint32_t i = 0 ; i < length ; i ++ ) {
254
288
word_buffer [i ] = echo_buffer [self -> echo_buffer_read_pos ++ ] * decay ;
255
289
@@ -267,30 +301,36 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
267
301
268
302
}
269
303
} 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
+ }
283
319
}
284
- }
320
+ */
285
321
}
286
322
287
323
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
289
328
uint32_t n = MIN (self -> sample_buffer_length , length );
329
+
290
330
int16_t * sample_src = (int16_t * )self -> sample_remaining_buffer ;
291
331
292
332
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
294
334
for (uint32_t i = 0 ; i < n ; i ++ ) {
295
335
word_buffer [i ] = sample_src [i ];
296
336
}
@@ -317,27 +357,29 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t *
317
357
}
318
358
}
319
359
} 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);
332
373
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
+ }
339
381
}
340
- }
382
+ */
341
383
}
342
384
343
385
length -= n ;
0 commit comments