From 62f3b0a5253654498cbd6b8fdf0b3a7c751c7998 Mon Sep 17 00:00:00 2001 From: Holger Wirtz Date: Fri, 3 Feb 2023 18:14:39 +0100 Subject: [PATCH] Testet delay on external RAM - and it works now! --- MicroDexed.ino | 2 +- .../src/effect_delay_ext8.cpp | 4 +- .../effect_delay_ext8/src/effect_delay_ext8.h | 12 +- third-party/effect_delay_ext8/src/extmem8.cpp | 495 ++++++++++++++++++ third-party/effect_delay_ext8/src/extmem8.h | 93 ++++ 5 files changed, 597 insertions(+), 9 deletions(-) create mode 100644 third-party/effect_delay_ext8/src/extmem8.cpp create mode 100644 third-party/effect_delay_ext8/src/extmem8.h diff --git a/MicroDexed.ino b/MicroDexed.ino index 8e19574..19f7bc8 100644 --- a/MicroDexed.ino +++ b/MicroDexed.ino @@ -242,7 +242,7 @@ FLASHMEM void create_audio_dexed_chain(uint8_t instance_id) { chorus_mixer[instance_id] = new AudioMixer<2>(); delay_fb_mixer[instance_id] = new AudioMixer<2>(); #if defined(USE_DELAY_8M) - delay_fx[instance_id] = new AudioEffectDelayExternal8(AUDIO_MEMORY_PSRAM64, DELAY_MAX_TIME); + delay_fx[instance_id] = new AudioEffectDelayExternal8(AUDIO_MEMORY8_EXTMEM, DELAY_MAX_TIME); #else delay_fx[instance_id] = new AudioEffectDelay(); #endif diff --git a/third-party/effect_delay_ext8/src/effect_delay_ext8.cpp b/third-party/effect_delay_ext8/src/effect_delay_ext8.cpp index 1e1b573..1a347f6 100644 --- a/third-party/effect_delay_ext8/src/effect_delay_ext8.cpp +++ b/third-party/effect_delay_ext8/src/effect_delay_ext8.cpp @@ -25,7 +25,7 @@ */ #include -#include "extmem.h" +#include "extmem8.h" #include "effect_delay_ext8.h" @@ -36,7 +36,7 @@ void AudioEffectDelayExternal8::update(void) // grab incoming data and put it into the memory block = receiveReadOnly(); - if (memory_type >= AUDIO_MEMORY_UNDEFINED) { + if (memory_type >= AUDIO_MEMORY8_UNDEFINED) { // ignore input and do nothing if undefined memory type release(block); return; diff --git a/third-party/effect_delay_ext8/src/effect_delay_ext8.h b/third-party/effect_delay_ext8/src/effect_delay_ext8.h index 553aa18..aac5741 100644 --- a/third-party/effect_delay_ext8/src/effect_delay_ext8.h +++ b/third-party/effect_delay_ext8/src/effect_delay_ext8.h @@ -29,22 +29,22 @@ #include "Arduino.h" #include "AudioStream.h" #include "spi_interrupt.h" -#include "extmem.h" +#include "extmem8.h" -class AudioEffectDelayExternal8 : public AudioStream, public AudioExtMem +class AudioEffectDelayExternal8 : public AudioStream, public AudioExtMem8 { public: - AudioEffectDelayExternal8(AudioEffectDelayMemoryType_t type, float milliseconds=1e6) + AudioEffectDelayExternal8(AudioEffectDelayMemoryType8_t type, float milliseconds=1e6) : AudioStream(1, inputQueueArray), - AudioExtMem(type, (milliseconds*(AUDIO_SAMPLE_RATE_EXACT/1000.0f))+0.5f), + AudioExtMem8(type, (milliseconds*(AUDIO_SAMPLE_RATE_EXACT/1000.0f))+0.5f), activemask(0) {} - AudioEffectDelayExternal8() : AudioEffectDelayExternal8(AUDIO_MEMORY_23LC1024) {} + AudioEffectDelayExternal8() : AudioEffectDelayExternal8(AUDIO_MEMORY8_23LC1024) {} ~AudioEffectDelayExternal8() {} void delay(uint8_t channel, float milliseconds) { - if (channel >= 8 || memory_type >= AUDIO_MEMORY_UNDEFINED) return; + if (channel >= 8 || memory_type >= AUDIO_MEMORY8_UNDEFINED) return; if (milliseconds < 0.0f) milliseconds = 0.0f; uint32_t n = (milliseconds*(AUDIO_SAMPLE_RATE_EXACT/1000.0f))+0.5f; n += AUDIO_BLOCK_SAMPLES; diff --git a/third-party/effect_delay_ext8/src/extmem8.cpp b/third-party/effect_delay_ext8/src/extmem8.cpp new file mode 100644 index 0000000..ffbbfc7 --- /dev/null +++ b/third-party/effect_delay_ext8/src/extmem8.cpp @@ -0,0 +1,495 @@ +/* Audio Library for Teensy 3.X + * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com + * + * Development of this audio library was funded by PJRC.COM, LLC by sales of + * Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop + * open source software by purchasing Teensy or other PJRC products. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice, development funding notice, and this permission + * notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include "extmem8.h" + +//#define INTERNAL_TEST + +// While 20 MHz (Teensy actually uses 16 MHz in most cases) and even 24 MHz +// have worked well in testing at room temperature with 3.3V power, to fully +// meet all the worst case timing specs, the SPI clock low time would need +// to be 40ns (12.5 MHz clock) for the single chip case and 51ns (9.8 MHz +// clock) for the 6-chip memoryboard with 74LCX126 buffers. +// +// Timing analysis and info is here: +// https://forum.pjrc.com/threads/29276-Limits-of-delay-effect-in-audio-library?p=97506&viewfull=1#post97506 +#define SPISETTING SPISettings(20'000'000, MSBFIRST, SPI_MODE0) + +// Use these with the audio adaptor board (should be adjustable by the user...) +#define SPIRAM_MOSI_PIN 7 +#define SPIRAM_MISO_PIN 12 +#define SPIRAM_SCK_PIN 14 + +#define SPIRAM_CS_PIN 6 + +#define MEMBOARD_CS0_PIN 2 +#define MEMBOARD_CS1_PIN 3 +#define MEMBOARD_CS2_PIN 4 + +#define SIZEOF_SAMPLE (sizeof(((audio_block_t*) 0)->data[0])) + +static const uint32_t NOT_ENOUGH_MEMORY = 0xFFFFFFFF; + +// This memory size array needs to match the sizes of +// the entries in AudioEffectDelayMemoryType8_t +const uint32_t AudioExtMem8::memSizeSamples[AUDIO_MEMORY8_UNDEFINED] = {65536,393216,262144,4194304,8000}; +AudioExtMem8* AudioExtMem8::first[AUDIO_MEMORY8_UNDEFINED] = {nullptr}; + + +AudioExtMem8::~AudioExtMem8() +{ + switch (memory_type) + { + case AUDIO_MEMORY8_HEAP: + free((void*) memory_begin); + break; + +#if defined(ARDUINO_TEENSY41) + case AUDIO_MEMORY8_EXTMEM: + extmem_free((void*) memory_begin); + break; +#endif // defined(ARDUINO_TEENSY41) + + // audio SPI memory is tracked by AudioExtMem8 + // objects thenselves - no need to free + default: + break; + } + linkOut(); +} + +void AudioExtMem8::linkIn(void) +{ + if (memory_type < AUDIO_MEMORY8_UNDEFINED) + { + AudioExtMem8** ppEM = &first[memory_type]; + + while (nullptr != *ppEM) + { + if (memory_begin > (*ppEM)->memory_begin) + ppEM = &((*ppEM)->next); + else + break; + } + next = *ppEM; + *ppEM = this; + } +} + + +void AudioExtMem8::linkOut(void) +{ + if (memory_type < AUDIO_MEMORY8_UNDEFINED) // This Never Happens... + { + AudioExtMem8** ppEM = &first[memory_type]; + + while (nullptr != *ppEM) + { + if (this != *ppEM) + ppEM = &((*ppEM)->next); + else + { + *ppEM = next; + break; + } + } + next = nullptr; // not really necessary, but... + } +} + + +/** + * Find space for given number of samples. This MUST be called before the + * newly-created AudioExtMem8 object is linked into the allocation list. + */ +uint32_t AudioExtMem8::findSpace(AudioEffectDelayMemoryType8_t memory_type, uint32_t samples) +{ + uint32_t result = 0; + bool gotOne = false; + + if (memory_type < AUDIO_MEMORY8_UNDEFINED) // This Never Happens... + { + AudioExtMem8** ppEM = &first[memory_type]; + + do + { + uint32_t next_start; + if (nullptr == *ppEM) // end of list, or first memory allocation + next_start = memSizeSamples[memory_type]; + else // we've found an object using memory + { + AudioExtMem8* nextObj = (*ppEM)->next; + result = (*ppEM)->memory_begin + (*ppEM)->memory_length; // end of object's allocation + if (nullptr != nextObj) + next_start = nextObj->memory_begin; + else + next_start = memSizeSamples[memory_type]; + + ppEM = &((*ppEM)->next); + } + + // simple-minded allocation: first found fit + if (samples <= (next_start - result)) + gotOne = true; + + } while (!gotOne && nullptr != *ppEM); + } + + if (!gotOne) + result = NOT_ENOUGH_MEMORY; + + return result; +} + + +/** + * Find maximum contiguous space in a memory. + */ +uint32_t AudioExtMem8::findMaxSpace(AudioEffectDelayMemoryType8_t memory_type) +{ + uint32_t result = 0; + uint32_t samples = 0; + + if (memory_type < AUDIO_MEMORY8_UNDEFINED) // This Never Happens... + { + AudioExtMem8** ppEM = &first[memory_type]; + + do + { + uint32_t next_start; + if (nullptr == *ppEM) // end of list, or first memory allocation + next_start = memSizeSamples[memory_type]; + else // we've found an object using memory + { + AudioExtMem8* nextObj = (*ppEM)->next; + result = (*ppEM)->memory_begin + (*ppEM)->memory_length; // end of object's allocation + if (nullptr != nextObj) + next_start = nextObj->memory_begin; + else + next_start = memSizeSamples[memory_type]; + + ppEM = &((*ppEM)->next); + } + + // if space is bigger, bump the answer + if (samples <= (next_start - result)) + samples = (next_start - result); + + } while (nullptr != *ppEM); + } + + return samples; +} + + +void AudioExtMem8::initialize(AudioEffectDelayMemoryType8_t type, uint32_t samples) +{ + //uint32_t memsize, avail; + uint32_t avail; + void* mem; + +#if defined(INTERNAL_TEST) + type = AUDIO_MEMORY8_INTERNAL; +#endif // defined(INTERNAL_TEST) + + head_offset = 0; + memory_type = type; + + SPI.setMOSI(SPIRAM_MOSI_PIN); + SPI.setMISO(SPIRAM_MISO_PIN); + SPI.setSCK(SPIRAM_SCK_PIN); + + SPI.begin(); + //memsize = memSizeSamples[type]; + //Serial.printf("Requested %d samples\n",samples); + + switch (type) + { + case AUDIO_MEMORY8_PSRAM64: + case AUDIO_MEMORY8_23LC1024: + case AUDIO_MEMORY8_CY15B104: + pinMode(SPIRAM_CS_PIN, OUTPUT); + digitalWriteFast(SPIRAM_CS_PIN, HIGH); + break; + + case AUDIO_MEMORY8_MEMORYBOARD: + pinMode(MEMBOARD_CS0_PIN, OUTPUT); + pinMode(MEMBOARD_CS1_PIN, OUTPUT); + pinMode(MEMBOARD_CS2_PIN, OUTPUT); + digitalWriteFast(MEMBOARD_CS0_PIN, LOW); + digitalWriteFast(MEMBOARD_CS1_PIN, LOW); + digitalWriteFast(MEMBOARD_CS2_PIN, LOW); + pinMode(SPIRAM_CS_PIN, OUTPUT); + digitalWriteFast(SPIRAM_CS_PIN, HIGH); + break; + + case AUDIO_MEMORY8_INTERNAL: + case AUDIO_MEMORY8_HEAP: + case AUDIO_MEMORY8_EXTMEM: + break; + + default: + samples = 0; + break; + } + +#define noOLD_ALLOCATE +#if defined(OLD_ALLOCATE) + avail = memsize - allocated[type]; + if (avail < AUDIO_BLOCK_SAMPLES*2+1) { + memory_type = AUDIO_MEMORY8_UNDEFINED; + //return; + } + if (samples > avail) samples = avail; + memory_begin = allocated[type]; + allocated[type] += samples; +#else + switch (type) + { + // SPI memory + // Emulate old behaviour: allocate biggest possible chunk + // of delay memory if asked for more than is available. + // Slightly different in dynamic system because of fragmentation, + // but should be the same if used with legacy static design. + case AUDIO_MEMORY8_PSRAM64: + case AUDIO_MEMORY8_23LC1024: + case AUDIO_MEMORY8_CY15B104: + case AUDIO_MEMORY8_MEMORYBOARD: + avail = findMaxSpace(type); + if (samples > avail) + samples = avail; + //Serial.printf("findSpace says we could use %08lX\n",findSpace(type,samples)); + memory_begin = findSpace(type,samples); + break; + + // processor heap: could be useful on Teensy 4.x etc. + // In this case don't fill heap if asked for too much + case AUDIO_MEMORY8_HEAP: + mem = malloc(samples * SIZEOF_SAMPLE); + if (nullptr != mem) + memory_begin = (uint32_t) mem; + else + memory_begin = NOT_ENOUGH_MEMORY; + break; + +#if defined(ARDUINO_TEENSY41) + // PSRAM external memory + case AUDIO_MEMORY8_EXTMEM: + mem = extmem_malloc(samples * SIZEOF_SAMPLE); + if (nullptr != mem) + memory_begin = (uint32_t) mem; + else + memory_begin = NOT_ENOUGH_MEMORY; + break; +#endif // defined(ARDUINO_TEENSY41) + + default: // invalid memory type + memory_begin = NOT_ENOUGH_MEMORY; + break; + } + if (NOT_ENOUGH_MEMORY == memory_begin) + memory_type = AUDIO_MEMORY8_UNDEFINED; +#endif // defined(OLD_ALLOCATE) + + if (AUDIO_MEMORY8_UNDEFINED != memory_type) + { + memory_length = samples; + zero(0, memory_length); + linkIn(); + } + else + memory_length = 0; +} + + +#ifdef INTERNAL_TEST +static int16_t testmem[8000]; // testing only +#endif + +void AudioExtMem8::SPIreadMany(int16_t* data, uint32_t samples) +{ + if (nullptr != data) + { + while (samples--) + *data++ = (int16_t)(SPI.transfer16(0)); + } + else + { + while (samples--) + (int16_t)(SPI.transfer16(0)); + } +} + + +void AudioExtMem8::SPIwriteMany(const int16_t* data, uint32_t samples) +{ + if (nullptr != data) + { + while (samples--) + SPI.transfer16(*data++); + } + else + { + while (samples--) + SPI.transfer16(0); + } +} + +void AudioExtMem8::read(uint32_t offset, uint32_t count, int16_t *data) +{ + uint32_t addr = memory_begin + offset; + +#ifdef INTERNAL_TEST + if (nullptr != data) while (count) { *data++ = testmem[addr++]; count--; } // testing only +#else + switch (memory_type) + { + case AUDIO_MEMORY8_23LC1024: + case AUDIO_MEMORY8_PSRAM64: + case AUDIO_MEMORY8_CY15B104: + addr *= SIZEOF_SAMPLE; + SPI.beginTransaction(SPISETTING); + digitalWriteFast(SPIRAM_CS_PIN, LOW); + SPI.transfer16((0x03 << 8) | (addr >> 16)); + SPI.transfer16(addr & 0xFFFF); + SPIreadMany(data,count); + digitalWriteFast(SPIRAM_CS_PIN, HIGH); + SPI.endTransaction(); + break; + + case AUDIO_MEMORY8_MEMORYBOARD: + SPI.beginTransaction(SPISETTING); + while (count) { + uint32_t chip = (addr >> 16) + 1; + digitalWriteFast(MEMBOARD_CS0_PIN, chip & 1); + digitalWriteFast(MEMBOARD_CS1_PIN, chip & 2); + digitalWriteFast(MEMBOARD_CS2_PIN, chip & 4); + uint32_t chipaddr = (addr & 0xFFFF) * SIZEOF_SAMPLE; + SPI.transfer16((0x03 << 8) | (chipaddr >> 16)); + SPI.transfer16(chipaddr & 0xFFFF); + uint32_t num = 0x10000 - (addr & 0xFFFF); + if (num > count) num = count; + count -= num; + addr += num; + SPIreadMany(data,num); + } + digitalWriteFast(MEMBOARD_CS0_PIN, LOW); + digitalWriteFast(MEMBOARD_CS1_PIN, LOW); + digitalWriteFast(MEMBOARD_CS2_PIN, LOW); + SPI.endTransaction(); + break; + + case AUDIO_MEMORY8_HEAP: +#if defined(ARDUINO_TEENSY41) + case AUDIO_MEMORY8_EXTMEM: +#endif // defined(ARDUINO_TEENSY41) + addr = memory_begin + offset*SIZEOF_SAMPLE; + if (nullptr != data) + memcpy(data,(void*) addr,count*SIZEOF_SAMPLE); + break; + + default: + break; + } +#endif +} + +void AudioExtMem8::write(uint32_t offset, uint32_t count, const int16_t *data) +{ + uint32_t addr = memory_begin + offset; + +#ifdef INTERNAL_TEST + while (count) { testmem[addr++] = *data++; count--; } // testing only +#else + switch (memory_type) + { + case AUDIO_MEMORY8_23LC1024: + case AUDIO_MEMORY8_PSRAM64: + addr *= SIZEOF_SAMPLE; + SPI.beginTransaction(SPISETTING); + digitalWriteFast(SPIRAM_CS_PIN, LOW); + SPI.transfer16((0x02 << 8) | (addr >> 16)); + SPI.transfer16(addr & 0xFFFF); + SPIwriteMany(data,count); + digitalWriteFast(SPIRAM_CS_PIN, HIGH); + SPI.endTransaction(); + break; + + case AUDIO_MEMORY8_CY15B104: + addr *= SIZEOF_SAMPLE; + + SPI.beginTransaction(SPISETTING); + digitalWriteFast(SPIRAM_CS_PIN, LOW); + SPI.transfer(0x06); //write-enable before every write + digitalWriteFast(SPIRAM_CS_PIN, HIGH); + asm volatile ("NOP\n NOP\n NOP\n NOP\n NOP\n NOP\n"); + digitalWriteFast(SPIRAM_CS_PIN, LOW); + SPI.transfer16((0x02 << 8) | (addr >> 16)); + SPI.transfer16(addr & 0xFFFF); + SPIwriteMany(data,count); + digitalWriteFast(SPIRAM_CS_PIN, HIGH); + SPI.endTransaction(); + break; + + case AUDIO_MEMORY8_MEMORYBOARD: + SPI.beginTransaction(SPISETTING); + while (count) { + uint32_t chip = (addr >> 16) + 1; + digitalWriteFast(MEMBOARD_CS0_PIN, chip & 1); + digitalWriteFast(MEMBOARD_CS1_PIN, chip & 2); + digitalWriteFast(MEMBOARD_CS2_PIN, chip & 4); + uint32_t chipaddr = (addr & 0xFFFF) * SIZEOF_SAMPLE; + SPI.transfer16((0x02 << 8) | (chipaddr >> 16)); + SPI.transfer16(chipaddr & 0xFFFF); + uint32_t num = 0x10000 - (addr & 0xFFFF); + if (num > count) num = count; + count -= num; + addr += num; + SPIwriteMany(data,num); + } + digitalWriteFast(MEMBOARD_CS0_PIN, LOW); + digitalWriteFast(MEMBOARD_CS1_PIN, LOW); + digitalWriteFast(MEMBOARD_CS2_PIN, LOW); + SPI.endTransaction(); + break; + + case AUDIO_MEMORY8_HEAP: +#if defined(ARDUINO_TEENSY41) + case AUDIO_MEMORY8_EXTMEM: +#endif // defined(ARDUINO_TEENSY41) + addr = memory_begin + offset*SIZEOF_SAMPLE; + if (nullptr != data) + memcpy((void*) addr,data,count*SIZEOF_SAMPLE); + else + memset((void*) addr,0,count*SIZEOF_SAMPLE); + break; + + default: + break; + } +#endif +} diff --git a/third-party/effect_delay_ext8/src/extmem8.h b/third-party/effect_delay_ext8/src/extmem8.h new file mode 100644 index 0000000..1a4100a --- /dev/null +++ b/third-party/effect_delay_ext8/src/extmem8.h @@ -0,0 +1,93 @@ +/* Audio Library for Teensy 3.X + * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com + * + * Development of this audio library was funded by PJRC.COM, LLC by sales of + * Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop + * open source software by purchasing Teensy or other PJRC products. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice, development funding notice, and this permission + * notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _extmem8_h_ +#define _extmem8_h_ +#include "Arduino.h" +#include "spi_interrupt.h" + +// A quick experiment with a Teensy 4.1 +// suggests a 1-input 3-tap delay line uses: +// mem type CPU% +// Heap +// EXTMEM 1.3% +// SPI PSRAM 12% (40MHz SPI clock) +// SPI PSRAM 19% (20MHz SPI clock) + +enum AudioEffectDelayMemoryType8_t { + AUDIO_MEMORY8_23LC1024 = 0, // 128k x 8 S-RAM (1.48s @ 44kHz / 16 bit) + AUDIO_MEMORY8_MEMORYBOARD = 1, // 6x 128k x 8 (8.9s @ 44kHz / 16 bit) + AUDIO_MEMORY8_CY15B104 = 2, // 512k x 8 F-RAM (5.9s @ 44kHz / 16 bit) + AUDIO_MEMORY8_PSRAM64 = 3, // 64Mb / 8MB PSRAM (95s @ 44kHz / 16 bit) + AUDIO_MEMORY8_INTERNAL = 4, // 8000 samples (181ms), for test only! + AUDIO_MEMORY8_HEAP = 5, + AUDIO_MEMORY8_EXTMEM = 6, + AUDIO_MEMORY8_UNDEFINED +}; + +#define IS_SPI_TYPE (AUDIO_MEMORY8_23LC1024 == memory_type || \ + AUDIO_MEMORY8_MEMORYBOARD == memory_type || \ + AUDIO_MEMORY8_CY15B104 == memory_type || \ + AUDIO_MEMORY8_PSRAM64 == memory_type) + +class AudioExtMem8 +{ +public: + AudioExtMem8(AudioEffectDelayMemoryType8_t type, uint32_t samples = AUDIO_SAMPLE_RATE_EXACT) + : memory_begin(0) + { + initialize(type, samples); + } + AudioExtMem8() : AudioExtMem8(AUDIO_MEMORY8_23LC1024, 65536) {} + ~AudioExtMem8(); + float getMaxDelay(void) {return (float) memory_length * 1000.0f / AUDIO_SAMPLE_RATE_EXACT;} + +private: + void initialize(AudioEffectDelayMemoryType8_t type, uint32_t samples); + inline static void SPIreadMany(int16_t* data, uint32_t samples); + inline static void SPIwriteMany(const int16_t* data, uint32_t samples); + //static uint32_t allocated[AUDIO_MEMORY8_UNDEFINED]; + const static uint32_t memSizeSamples[AUDIO_MEMORY8_UNDEFINED]; + static AudioExtMem8* first[AUDIO_MEMORY8_UNDEFINED]; // linked lists of all SPI memory types + AudioExtMem8* next; // next allocation after this one + void linkIn(void); // link new AudioExtMem8 object into allocation chain + void linkOut(void); // unlink this AudioExtMem8 object from allocation chain + static uint32_t findSpace(AudioEffectDelayMemoryType8_t memory_type, uint32_t samples); // find a space for a delay buffer + static uint32_t findMaxSpace(AudioEffectDelayMemoryType8_t memory_type); // find max available space for a delay buffer + uint32_t memory_begin; // the first address in the memory we're using + +protected: + void read(uint32_t address, uint32_t count, int16_t *data); + void write(uint32_t address, uint32_t count, const int16_t *data); + void zero(uint32_t address, uint32_t count) { + write(address, count, NULL); + } + uint32_t memory_length; // the amount of memory we're using + uint32_t head_offset; // head index (incoming) data into external memory + AudioEffectDelayMemoryType8_t memory_type; // 0=23LC1024, 1=Frank's Memoryboard, etc. +}; + +#endif // _extmem8.h_