From 87705e9f675e60419f53ed49340192eec3bbe0ae Mon Sep 17 00:00:00 2001 From: Thomas Grill Date: Mon, 29 Dec 2025 17:05:24 +0100 Subject: [PATCH 1/4] Initialize to system sample rate --- AudioEffectDelay_OA_F32.h | 1 + 1 file changed, 1 insertion(+) diff --git a/AudioEffectDelay_OA_F32.h b/AudioEffectDelay_OA_F32.h index cde6281..13acd4a 100644 --- a/AudioEffectDelay_OA_F32.h +++ b/AudioEffectDelay_OA_F32.h @@ -67,6 +67,7 @@ class AudioEffectDelay_OA_F32 : public AudioStream_F32 tailindex = 0; maxblocks = 0; memset(queue, 0, sizeof(queue)); + setSampleRate_Hz(AUDIO_SAMPLE_RATE_EXACT); } AudioEffectDelay_OA_F32(const AudioSettings_F32 &settings) : AudioStream_F32(1,inputQueueArray) { From a32fad110bcf9410e0211034985c3799942cf0b5 Mon Sep 17 00:00:00 2001 From: Thomas Grill Date: Wed, 31 Dec 2025 13:28:22 +0100 Subject: [PATCH 2/4] Rewrote AudioEffectDelay_OA_F32 to use a C++-template with variable channel count --- AudioEffectDelay_OA_F32.cpp | 202 --------------------------------- AudioEffectDelay_OA_F32.h | 216 ++++++++++++++++++++++++++++++++++-- 2 files changed, 207 insertions(+), 211 deletions(-) diff --git a/AudioEffectDelay_OA_F32.cpp b/AudioEffectDelay_OA_F32.cpp index 36dc606..b3047fb 100644 --- a/AudioEffectDelay_OA_F32.cpp +++ b/AudioEffectDelay_OA_F32.cpp @@ -31,205 +31,3 @@ #include #include "AudioEffectDelay_OA_F32.h" -void AudioEffectDelay_OA_F32::update(void) -{ - //Serial.println("AudioEffectDelay_OA_F32: update()..."); - receiveIncomingData(); //put the in-coming audio data into the queue - discardUnneededBlocksFromQueue(); //clear out queued data this is no longer needed - transmitOutgoingData(); //put the queued data into the output - return; -} - -void AudioEffectDelay_OA_F32::receiveIncomingData(void) { - //Serial.println("AudioEffectDelay_OA_F32::receiveIncomingData: starting..."); - - //prepare the receiving queue - uint16_t head = headindex; //what block to write to - uint16_t tail = tailindex; //what block to read from - if (queue[head] == NULL) { - //if (!Serial) Serial.println("AudioEffectDelay_OA_F32::receiveIncomingData: Allocating queue[head]."); - queue[head] = allocate_f32(); - if (queue[head] == NULL) { - //if (!Serial) Serial.println("AudioEffectDelay_OA_F32::receiveIncomingData: Null memory 1. Returning."); - return; - } - } - - //prepare target memory nto which we'll copy the incoming data into the queue - int dest_ind = writeposition; //inclusive - if (dest_ind >= (queue[head]->full_length)) { - head++; dest_ind = 0; - if (head >= DELAY_QUEUE_SIZE_OA) head = 0; - if (queue[head] != NULL) { - if (head==tail) {tail++; if (tail >= DELAY_QUEUE_SIZE_OA) tail = 0; } - AudioStream_F32::release(queue[head]); - queue[head]=NULL; - } - } - if (queue[head]==NULL) { - queue[head] = allocate_f32(); - if (queue[head] == NULL) { - if (!Serial) Serial.println("AudioEffectDelay_OA_F32::receiveIncomingData: Null memory 2. Returning."); - return; - } - } - - //receive the in-coming audio data block - audio_block_f32_t *input = receiveReadOnly_f32(); - if (input == NULL) { - //if (!Serial) Serial.println("AudioEffectDelay_OA_F32::receiveIncomingData: Input data is NULL. Returning."); - return; - } - int n_copy = input->length; - last_received_block_id = input->id; - - // Now we'll loop over the individual samples of the in-coming data - float32_t *dest = queue[head]->data; - float32_t *source = input->data; - int end_write = dest_ind + n_copy; //this may go past the end of the destination array - int end_loop = min(end_write,(int)(queue[head]->full_length)); //limit to the end of the array - int src_count=0, dest_count=dest_ind; - for (int i=dest_ind; i= DELAY_QUEUE_SIZE_OA) head = 0; - if (queue[head] != NULL) { - if (head==tail) {tail++; if (tail >= DELAY_QUEUE_SIZE_OA) tail = 0; } - AudioStream_F32::release(queue[head]); - queue[head]=NULL; - } - - if (queue[head]==NULL) { - queue[head] = allocate_f32(); - if (queue[head] == NULL) { - Serial.println("AudioEffectDelay_OA_F32::receiveIncomingData: Null memory 3. Returning."); - AudioStream_F32::release(input); - return; - } - } - float32_t *dest = queue[head]->data; - end_loop = end_write - (queue[head]->full_length); - dest_count = dest_ind; - for (int i=dest_ind; i < end_loop; i++) dest[dest_count++]=source[src_count++]; - } - - AudioStream_F32::release(input); - writeposition = dest_count; - headindex = head; - tailindex = tail; - return; -} - - -void AudioEffectDelay_OA_F32::discardUnneededBlocksFromQueue(void) { - uint16_t head = headindex; //what block to write to - uint16_t tail = tailindex; //last useful block of data - uint32_t count; - - // discard unneeded blocks from the queue - if (head >= tail) { - count = head - tail; - } else { - count = DELAY_QUEUE_SIZE_OA + head - tail; - } -/* if (head>0) { - Serial.print("AudioEffectDelay_OA_F32::discardUnneededBlocksFromQueue: head, tail, count, maxblocks, DELAY_QUEUE_SIZE_OA: "); - Serial.print(head); Serial.print(", "); - Serial.print(tail); Serial.print(", "); - Serial.print(count); Serial.print(", "); - Serial.print(maxblocks); Serial.print(", "); - Serial.print(DELAY_QUEUE_SIZE_OA); Serial.print(", "); - Serial.println(); - } */ - if (count > maxblocks) { - count -= maxblocks; - do { - if (queue[tail] != NULL) { - AudioStream_F32::release(queue[tail]); - queue[tail] = NULL; - } - if (++tail >= DELAY_QUEUE_SIZE_OA) tail = 0; - } while (--count > 0); - } - tailindex = tail; -} - -void AudioEffectDelay_OA_F32::transmitOutgoingData(void) { - uint16_t head = headindex; //what block to write to - //uint16_t tail = tailindex; //last useful block of data - audio_block_f32_t *output; - int channel; //, index, prev, offset; - //const float32_t *src, *end; - //float32_t *dst; - - // transmit the delayed outputs using queue data - for (channel = 0; channel < 8; channel++) { - if (!(activemask & (1<length; - if (ref_samp_long < offset_samp) { //when (ref_samp_long - offset_samp) goes negative, the uint32_t will fail, so we do this logic check - ref_samp_long = ref_samp_long + (((uint32_t)(DELAY_QUEUE_SIZE_OA))*((uint32_t)AUDIO_BLOCK_SIZE_F32)); - } - ref_samp_long = ref_samp_long - offset_samp; - uint16_t source_queue_ind = (uint16_t)(ref_samp_long / ((uint32_t)AUDIO_BLOCK_SIZE_F32)); - int source_samp = (int)(ref_samp_long - (((uint32_t)source_queue_ind)*((uint32_t)AUDIO_BLOCK_SIZE_F32))); - - //pull the data from the first source data block - int dest_counter=0; - int n_output = output->length; - float32_t *dest = output->data; - audio_block_f32_t *source_block = queue[source_queue_ind]; - if (source_block == NULL) { - //fill destination with zeros for this source block - int Iend = min(source_samp+n_output,AUDIO_BLOCK_SIZE_F32); - for (int Isource = source_samp; Isource < Iend; Isource++) { - dest[dest_counter++]=0.0; - } - } else { - //fill destination with this source block's values - float32_t *source = source_block->data; - int Iend = min(source_samp+n_output,AUDIO_BLOCK_SIZE_F32); - for (int Isource = source_samp; Isource < Iend; Isource++) { - dest[dest_counter++]=source[Isource]; - } - } - - //pull the data from the second source data block, if needed - if (dest_counter < n_output) { - //yes, we need to keep filling the output - int Iend = n_output - dest_counter; //how many more data points do we need - source_queue_ind++; source_samp = 0; //which source block will we draw from (and reset the source sample counter) - if (source_queue_ind >= DELAY_QUEUE_SIZE_OA) source_queue_ind = 0; //wrap around on our source black. - source_block = queue[source_queue_ind]; //get the source block - if (source_block == NULL) { //does it have data? - //no, it doesn't have data. fill destination with zeros - for (int Isource = source_samp; Isource < Iend; Isource++) { - dest[dest_counter++] = 0.0; - } - } else { - //source block does have data. use this block's values - float32_t *source = source_block->data; - for (int Isource = source_samp; Isource < Iend; Isource++) { - dest[dest_counter++]=source[Isource]; - } - } - } - - //add the id of the last received audio block - output->id = last_received_block_id; - - //transmit and release - AudioStream_F32::transmit(output, channel); - AudioStream_F32::release(output); - } -} - - diff --git a/AudioEffectDelay_OA_F32.h b/AudioEffectDelay_OA_F32.h index 13acd4a..a88c85b 100644 --- a/AudioEffectDelay_OA_F32.h +++ b/AudioEffectDelay_OA_F32.h @@ -56,6 +56,7 @@ #define DELAY_QUEUE_SIZE_OA (6144 / AUDIO_BLOCK_SIZE_F32) #endif +template class AudioEffectDelay_OA_F32 : public AudioStream_F32 { //GUI: inputs:1, outputs:1 //this line used for automatic generation of GUI node @@ -85,7 +86,7 @@ class AudioEffectDelay_OA_F32 : public AudioStream_F32 //audio_block_len_samples = block_size; //this is the actual size that is being used in each audio_block_f32 } void delay(uint8_t channel, float milliseconds) { - if (channel >= 8) return; + if (channel >= channels) return; if (milliseconds < 0.0) milliseconds = 0.0; uint32_t n = (milliseconds*(sampleRate_Hz/1000.0))+0.5; uint32_t nmax = AUDIO_BLOCK_SIZE_F32 * (DELAY_QUEUE_SIZE_OA-1); @@ -109,13 +110,19 @@ class AudioEffectDelay_OA_F32 : public AudioStream_F32 } } void disable(uint8_t channel) { - if (channel >= 8) return; + if (channel >= channels) return; // diable this channel activemask &= ~(1< max) max = n; } - } while(++channel < 8); + } while(++channel < channels); maxblocks = max; } uint8_t activemask; // which output channels are active @@ -135,18 +142,209 @@ class AudioEffectDelay_OA_F32 : public AudioStream_F32 uint16_t maxblocks; // number of blocks needed in queue //#if DELAY_QUEUE_SIZE_OA * AUDIO_BLOCK_SAMPLES < 65535 // uint16_t writeposition; -// uint16_t delay_samps[8]; // # of samples to delay for each channel +// uint16_t delay_samps[channels]; // # of samples to delay for each channel //#else int writeposition; //position within current head buffer in the queue - uint32_t delay_samps[8]; // # of samples to delay for each channel + uint32_t delay_samps[channels]; // # of samples to delay for each channel //#endif audio_block_f32_t *queue[DELAY_QUEUE_SIZE_OA]; audio_block_f32_t *inputQueueArray[1]; float sampleRate_Hz = AUDIO_SAMPLE_RATE_EXACT; //default. from AudioStream.h?? //int audio_block_len_samples = AUDIO_BLOCK_SAMPLES; - void receiveIncomingData(void); - void discardUnneededBlocksFromQueue(void); - void transmitOutgoingData(void); + + void receiveIncomingData(void) + { + //Serial.println("AudioEffectDelay_OA_F32::receiveIncomingData: starting..."); + + //prepare the receiving queue + uint16_t head = headindex; //what block to write to + uint16_t tail = tailindex; //what block to read from + if (queue[head] == NULL) { + //if (!Serial) Serial.println("AudioEffectDelay_OA_F32::receiveIncomingData: Allocating queue[head]."); + queue[head] = allocate_f32(); + if (queue[head] == NULL) { + //if (!Serial) Serial.println("AudioEffectDelay_OA_F32::receiveIncomingData: Null memory 1. Returning."); + return; + } + } + + //prepare target memory nto which we'll copy the incoming data into the queue + int dest_ind = writeposition; //inclusive + if (dest_ind >= (queue[head]->full_length)) { + head++; dest_ind = 0; + if (head >= DELAY_QUEUE_SIZE_OA) head = 0; + if (queue[head] != NULL) { + if (head==tail) {tail++; if (tail >= DELAY_QUEUE_SIZE_OA) tail = 0; } + AudioStream_F32::release(queue[head]); + queue[head]=NULL; + } + } + if (queue[head]==NULL) { + queue[head] = allocate_f32(); + if (queue[head] == NULL) { + if (!Serial) Serial.println("AudioEffectDelay_OA_F32::receiveIncomingData: Null memory 2. Returning."); + return; + } + } + + //receive the in-coming audio data block + audio_block_f32_t *input = receiveReadOnly_f32(); + if (input == NULL) { + //if (!Serial) Serial.println("AudioEffectDelay_OA_F32::receiveIncomingData: Input data is NULL. Returning."); + return; + } + int n_copy = input->length; + last_received_block_id = input->id; + + // Now we'll loop over the individual samples of the in-coming data + float32_t *dest = queue[head]->data; + float32_t *source = input->data; + int end_write = dest_ind + n_copy; //this may go past the end of the destination array + int end_loop = min(end_write,(int)(queue[head]->full_length)); //limit to the end of the array + int src_count=0, dest_count=dest_ind; + for (int i=dest_ind; i= DELAY_QUEUE_SIZE_OA) head = 0; + if (queue[head] != NULL) { + if (head==tail) {tail++; if (tail >= DELAY_QUEUE_SIZE_OA) tail = 0; } + AudioStream_F32::release(queue[head]); + queue[head]=NULL; + } + + if (queue[head]==NULL) { + queue[head] = allocate_f32(); + if (queue[head] == NULL) { + Serial.println("AudioEffectDelay_OA_F32::receiveIncomingData: Null memory 3. Returning."); + AudioStream_F32::release(input); + return; + } + } + float32_t *dest = queue[head]->data; + end_loop = end_write - (queue[head]->full_length); + dest_count = dest_ind; + for (int i=dest_ind; i < end_loop; i++) dest[dest_count++]=source[src_count++]; + } + + AudioStream_F32::release(input); + writeposition = dest_count; + headindex = head; + tailindex = tail; + } + + void discardUnneededBlocksFromQueue(void) + { + uint16_t head = headindex; //what block to write to + uint16_t tail = tailindex; //last useful block of data + uint32_t count; + + // discard unneeded blocks from the queue + if (head >= tail) { + count = head - tail; + } else { + count = DELAY_QUEUE_SIZE_OA + head - tail; + } + /* if (head>0) { + Serial.print("AudioEffectDelay_OA_F32::discardUnneededBlocksFromQueue: head, tail, count, maxblocks, DELAY_QUEUE_SIZE_OA: "); + Serial.print(head); Serial.print(", "); + Serial.print(tail); Serial.print(", "); + Serial.print(count); Serial.print(", "); + Serial.print(maxblocks); Serial.print(", "); + Serial.print(DELAY_QUEUE_SIZE_OA); Serial.print(", "); + Serial.println(); + } */ + if (count > maxblocks) { + count -= maxblocks; + do { + if (queue[tail] != NULL) { + AudioStream_F32::release(queue[tail]); + queue[tail] = NULL; + } + if (++tail >= DELAY_QUEUE_SIZE_OA) tail = 0; + } while (--count > 0); + } + tailindex = tail; + } + + void transmitOutgoingData(void) + { + uint16_t head = headindex; //what block to write to + //uint16_t tail = tailindex; //last useful block of data + audio_block_f32_t *output; + int channel; //, index, prev, offset; + //const float32_t *src, *end; + //float32_t *dst; + + // transmit the delayed outputs using queue data + for (channel = 0; channel < channels; channel++) { + if (!(activemask & (1<length; + if (ref_samp_long < offset_samp) { //when (ref_samp_long - offset_samp) goes negative, the uint32_t will fail, so we do this logic check + ref_samp_long = ref_samp_long + (((uint32_t)(DELAY_QUEUE_SIZE_OA))*((uint32_t)AUDIO_BLOCK_SIZE_F32)); + } + ref_samp_long = ref_samp_long - offset_samp; + uint16_t source_queue_ind = (uint16_t)(ref_samp_long / ((uint32_t)AUDIO_BLOCK_SIZE_F32)); + int source_samp = (int)(ref_samp_long - (((uint32_t)source_queue_ind)*((uint32_t)AUDIO_BLOCK_SIZE_F32))); + + //pull the data from the first source data block + int dest_counter=0; + int n_output = output->length; + float32_t *dest = output->data; + audio_block_f32_t *source_block = queue[source_queue_ind]; + if (source_block == NULL) { + //fill destination with zeros for this source block + int Iend = min(source_samp+n_output,AUDIO_BLOCK_SIZE_F32); + for (int Isource = source_samp; Isource < Iend; Isource++) { + dest[dest_counter++]=0.0; + } + } else { + //fill destination with this source block's values + float32_t *source = source_block->data; + int Iend = min(source_samp+n_output,AUDIO_BLOCK_SIZE_F32); + for (int Isource = source_samp; Isource < Iend; Isource++) { + dest[dest_counter++]=source[Isource]; + } + } + + //pull the data from the second source data block, if needed + if (dest_counter < n_output) { + //yes, we need to keep filling the output + int Iend = n_output - dest_counter; //how many more data points do we need + source_queue_ind++; source_samp = 0; //which source block will we draw from (and reset the source sample counter) + if (source_queue_ind >= DELAY_QUEUE_SIZE_OA) source_queue_ind = 0; //wrap around on our source black. + source_block = queue[source_queue_ind]; //get the source block + if (source_block == NULL) { //does it have data? + //no, it doesn't have data. fill destination with zeros + for (int Isource = source_samp; Isource < Iend; Isource++) { + dest[dest_counter++] = 0.0; + } + } else { + //source block does have data. use this block's values + float32_t *source = source_block->data; + for (int Isource = source_samp; Isource < Iend; Isource++) { + dest[dest_counter++]=source[Isource]; + } + } + } + + //add the id of the last received audio block + output->id = last_received_block_id; + + //transmit and release + AudioStream_F32::transmit(output, channel); + AudioStream_F32::release(output); + } + } + unsigned long last_received_block_id = 0; }; From 7bac6e4230a2f2023cd191a76d604f0afb99c0be Mon Sep 17 00:00:00 2001 From: Thomas Grill Date: Wed, 31 Dec 2025 13:36:38 +0100 Subject: [PATCH 3/4] Change default class --- AudioEffectDelay_OA_F32.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/AudioEffectDelay_OA_F32.h b/AudioEffectDelay_OA_F32.h index a88c85b..5066f86 100644 --- a/AudioEffectDelay_OA_F32.h +++ b/AudioEffectDelay_OA_F32.h @@ -56,13 +56,13 @@ #define DELAY_QUEUE_SIZE_OA (6144 / AUDIO_BLOCK_SIZE_F32) #endif -template -class AudioEffectDelay_OA_F32 : public AudioStream_F32 +template +class AudioEffectDelay_OA_n_F32 : public AudioStream_F32 { //GUI: inputs:1, outputs:1 //this line used for automatic generation of GUI node //GUI: shortName:delay public: - AudioEffectDelay_OA_F32() : AudioStream_F32(1, inputQueueArray) { + AudioEffectDelay_OA_n_F32() : AudioStream_F32(1, inputQueueArray) { activemask = 0; headindex = 0; tailindex = 0; @@ -70,7 +70,7 @@ class AudioEffectDelay_OA_F32 : public AudioStream_F32 memset(queue, 0, sizeof(queue)); setSampleRate_Hz(AUDIO_SAMPLE_RATE_EXACT); } - AudioEffectDelay_OA_F32(const AudioSettings_F32 &settings) : + AudioEffectDelay_OA_n_F32(const AudioSettings_F32 &settings) : AudioStream_F32(1,inputQueueArray) { activemask = 0; headindex = 0; @@ -348,4 +348,6 @@ class AudioEffectDelay_OA_F32 : public AudioStream_F32 unsigned long last_received_block_id = 0; }; +using AudioEffectDelay_OA_F32=AudioEffectDelay_OA_n_F32<8>; + #endif From 7cec3ca98ef639fb3422806c43db9d15d7122e29 Mon Sep 17 00:00:00 2001 From: Thomas Grill Date: Thu, 1 Jan 2026 23:37:12 +0100 Subject: [PATCH 4/4] Made delay memory variable --- AudioEffectDelay_OA_F32.h | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/AudioEffectDelay_OA_F32.h b/AudioEffectDelay_OA_F32.h index 5066f86..9880996 100644 --- a/AudioEffectDelay_OA_F32.h +++ b/AudioEffectDelay_OA_F32.h @@ -41,26 +41,26 @@ // Are these too big??? I think the same as I16---half as much? <<<<<<<<<<<<<<<< #if defined(__IMXRT1062__) // 4.00 second maximum on Teensy 4.0 - #define DELAY_QUEUE_SIZE_OA (176512 / AUDIO_BLOCK_SAMPLES) + #define DELAY_MAX_MILLIS 4000 #elif defined(__MK66FX1M0__) // 2.41 second maximum on Teensy 3.6 - #define DELAY_QUEUE_SIZE_OA (106496 / AUDIO_BLOCK_SIZE_F32) + #define DELAY_MAX_MILLIS 2410 #elif defined(__MK64FX512__) // 1.67 second maximum on Teensy 3.5 - #define DELAY_QUEUE_SIZE_OA (73728 / AUDIO_BLOCK_SIZE_F32) + #define DELAY_MAX_MILLIS 1670 #elif defined(__MK20DX256__) // 0.45 second maximum on Teensy 3.1 & 3.2 - #define DELAY_QUEUE_SIZE_OA (19826 / AUDIO_BLOCK_SIZE_F32) + #define DELAY_MAX_MILLIS 450 #else // 0.14 second maximum on Teensy 3.0 - #define DELAY_QUEUE_SIZE_OA (6144 / AUDIO_BLOCK_SIZE_F32) + #define DELAY_MAX_MILLIS 140 #endif -template +static constexpr int DELAY_QUEUE_SIZE_OA_MAX = ((DELAY_MAX_MILLIS * AUDIO_SAMPLE_RATE) / (AUDIO_BLOCK_SIZE_F32*1000)); + +template class AudioEffectDelay_OA_n_F32 : public AudioStream_F32 { -//GUI: inputs:1, outputs:1 //this line used for automatic generation of GUI node -//GUI: shortName:delay public: AudioEffectDelay_OA_n_F32() : AudioStream_F32(1, inputQueueArray) { activemask = 0; @@ -349,5 +349,7 @@ class AudioEffectDelay_OA_n_F32 : public AudioStream_F32 }; using AudioEffectDelay_OA_F32=AudioEffectDelay_OA_n_F32<8>; +//GUI: inputs:1, outputs:1 //this line used for automatic generation of GUI node +//GUI: shortName:delay #endif