Skip to content

Commit bbd3e01

Browse files
committed
Comments and couple tweaks
1 parent a851f10 commit bbd3e01

File tree

2 files changed

+106
-64
lines changed

2 files changed

+106
-64
lines changed

shared-module/audiodelays/Echo.c

Lines changed: 105 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -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

7997
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) {
153171
}
154172

155173
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+
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

185209
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
186212
self->sample = NULL;
187213
return;
188214
}
@@ -216,40 +242,48 @@ int16_t mix_down_sample(int32_t sample) {
216242
audioio_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;

shared-module/audiodelays/Echo.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ typedef struct {
2828
uint8_t last_buf_idx;
2929
uint32_t buffer_len; // max buffer in bytes
3030

31-
uint32_t *sample_remaining_buffer;
31+
uint8_t *sample_remaining_buffer;
3232
uint32_t sample_buffer_length;
3333

3434
bool loop;

0 commit comments

Comments
 (0)