Limits of delay effect in audio library

Wanted to let folks know that I was able to successfully get 6ea CY15B104Qs on Franks memory board and can now delay/loop >35 seconds of audio! :) There were hurdles:
* To solder the (wide) chips to the board I bent the leads under and soldered carefully. A scary proposition with these expensive parts. But done in the right order and checking work under a microscope, it can (does) work.
* I updated the lib for another type to allow for this config. That's archived here:https://github.com/SensoriumEmbedded/Audio

I was thinking about adding Franks CS architecture and SRAMs (with wide footprints) to this project: https://oshpark.com/shared_projects/yTlhWecX
I'll certainly post here if I do. Thank you all for your wonderful efforts! -Travis
 
AH I just came across this. Perfect for my idea.
When flying, the ATC can be very busy and loud over the headset. This will allow me to record 9seconds live, and be able to click a button for playback if I missed any communication.
 
I'm also curious about adding ram for longer delay with the audio library and a teensy 4.1. Can I use both the extra ram on the 4.1 and the audio board at the same time? thanks!
 
I made a memory board and am not able to get it to work. I am using it in conjunction with the multichannel tdm board. The code loads without any problems. When I replace my extended delays with typical delay, then the delay works, but when I use the extended delay, there is no delayed signal in the output. Also btw, I am using 6 23LC1024 chips on the board and as far as I can tell all my solders look clean and the board is oriented correctly. The board is mounted on top of my teensy 3.6. Any help solving this issue would be much appreciated.

Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

//#include <WireKinetis.h>
//#include <i2c_t3.h>  //this is for teensy when using i2C ports other than the otiginal default ports


unsigned char frequencyH = 0;
unsigned char frequencyL = 0;
unsigned int frequencyB;
double frequency,  StationFreq, Station;
unsigned char byt1, byt2, byt3, byt4, byt5,StationFlag;
unsigned short StationSect1, StationSect2;
float ProgStation[10];
int StationTotal;




// GUItool: begin automatically generated code
AudioInputTDM            tdm1;           //xy=180.20000076293945,443.60001373291016
AudioEffectDelay         delay1;         //xy=367.00000381469727,451.00000762939453
AudioEffectDelayExternal delayExt2(AUDIO_MEMORY_23LC1024);;      //xy=371.00000762939453,597.9999847412109

AudioMixer4              mixer1;         //xy=663.200023651123,437.6001091003418
AudioAmplifier           amp1;           //xy=775.200023651123,359.6000123023987
AudioOutputTDM           tdm2;           //xy=946.2000122070312,417.6000061035156
AudioConnection          patchCord1(tdm1, 0, mixer1, 0);
AudioConnection          patchCord2(tdm1, 4, delay1, 0);
AudioConnection          patchCord3(tdm1, 8, delayExt2, 0);
AudioConnection          patchCord4(delay1, 0, mixer1, 1);
AudioConnection          patchCord5(delayExt2, 0, mixer1, 2);
AudioConnection          patchCord6(mixer1, amp1);
AudioConnection          patchCord7(amp1, 0, tdm2, 0);
AudioConnection          patchCord8(amp1, 0, tdm2, 2);
AudioControlCS42448      cs42448_1;      //xy=187,681.9999828338623
// GUItool: end automatically generated code





void setup() {
Wire1.setSDA(38);
Wire1.setSCL(37);
Wire1.begin(0x60);
Serial.begin(9600);

cs42448_1.enable();
cs42448_1.volume(50);
AudioMemory(800);
delay1.delay(0, 500);
delayExt2.delay(0, 1000);
amp1.gain(3.0);

setFrequency(99.9, 0);
}

void loop() {
  // put your main code here, to run repeatedly:

  setFrequency(99.9, 0);
  delay(500);
  setFrequency(85.5, 1);
  delay(2000);
  setFrequency(92.9, 0);
  delay(500);
  setFrequency(85.5, 1);
  delay(2000);
  setFrequency(99.1, 0);
  delay(500);
  setFrequency(85.5, 1);
  delay(2000);
}

void setFrequency(float frequency, int Mute){
  //int sensorValue = digitalRead(2);
  frequencyB = 4 * (frequency * 1000000 + 225000) / 32768;  //  # Frequency distribution for two bytes (according to the data sheet)
  frequencyH = frequencyB >> 8;   //  #int (freq14bit / 256)  shifts to the right 8 places, aka adds 8 0's to the front,  essentially gets rid of second byte 
  if (Mute == 1){
    frequencyH = frequencyH | 0x80;
   Serial.print("Mute on: ");
   Serial.println(Mute);
  };  // adds ability to turn on and off mute
  frequencyL = frequencyB & 0XFF;  //essentially the & 0XFF is just taking off the first byte maybe should not be needed?
  Wire1.beginTransmission(0x60);
  Wire1.write(frequencyH);   //  (MUTE bit; Frequency H)  // MUTE is 0x80 and search true is 0x40
  Wire1.write(frequencyL); // (frequency L)
  Wire1.write(0xB0);  //  # 3.bajt (SUD; SSL1, SSL2; HLSI, MS, MR, ML; SWP1)
  Wire1.write(0x10);  //  (SWP2; STBY, BL; XTAL; smut; HCC, SNC, SI)
  Wire1.write((byte)0x00);  // # (PLREFF; DTC; 0; 0; 0; 0; 0; 0)
  Wire1.endTransmission();
}

here are a few pictures of the hardware
 

Attachments

  • 20210309_220641.jpg
    20210309_220641.jpg
    146.1 KB · Views: 69
  • 20210309_220554.jpg
    20210309_220554.jpg
    138.4 KB · Views: 65
  • 20210309_220603.jpg
    20210309_220603.jpg
    128 KB · Views: 62
I was playing around with the 23LC1024, and found the PSRAM 8MB chip to be drop in compatible, with just a few tweaks to AudioEffectDelayExternal, mainly just changing the amount of ram to 8388608 (allowing 95108ms, 95 seconds) of delay. That's like having 10.6 memoryboard4's at your disposal, or or 16x 4-mbit fram chips. All for the cost of one inexpensive chip and a soic to dip adapter.

I'm thinking of adding a second chip as well for delays of 190 seconds.

I'll post the code tweaks this evening. I am using a Teensy 4.1 and also modified the SPI pins to get AudioEffectDelayExternal working for me.

So excited about this simple change, especially with fram so expensive.
 
Here are the tweaks I ended up making to effect_delay_ext.h and effect_delay_ext.cpp. I added an audio memory type AUDIO_MEMORY_PSRAM_8MB and the the supporting code for that:

$ diff ./arduino/arduino-1.8.15/effect_delay_ext.h ./arduino/arduino-1.8.15/hardware/teensy/avr/libraries/Audio/effect_delay_ext.h
37c37,38
< AUDIO_MEMORY_UNDEFINED = 3
---
> AUDIO_MEMORY_PSRAM_8MB = 3, // 8192k x 8 PS-RAM
> AUDIO_MEMORY_UNDEFINED = 4

and

$ diff ./arduino/arduino-1.8.15/effect_delay_ext.cpp ./arduino/arduino-1.8.15/hardware/teensy/avr/libraries/Audio/effect_delay_ext.cpp
43,47c43,52
< #define SPIRAM_MOSI_PIN 7
< #define SPIRAM_MISO_PIN 12
< #define SPIRAM_SCK_PIN 14
<
< #define SPIRAM_CS_PIN 6
---
> //#define SPIRAM_MOSI_PIN 7
> //#define SPIRAM_MISO_PIN 12
> //#define SPIRAM_SCK_PIN 14
>
> //#define SPIRAM_CS_PIN 6
>
> #define SPIRAM_MOSI_PIN 11 //-----------------
> #define SPIRAM_MISO_PIN 12 // for Teensy 4.1
> #define SPIRAM_SCK_PIN 13 //
> #define SPIRAM_CS_PIN 36 //-----------------
130c135,136
<
---
> SPI.setCS(SPIRAM_CS_PIN); // added for Teensy 4.1
>
157a164,172
> } else if (type == AUDIO_MEMORY_PSRAM_8MB) {
> #ifdef INTERNAL_TEST
> memsize = 8000;
> #else
> memsize = (2^23); // 8388608 bytes
> #endif
> pinMode(SPIRAM_CS_PIN, OUTPUT);
> digitalWriteFast(SPIRAM_CS_PIN, HIGH);
>
187c202,204
< memory_type == AUDIO_MEMORY_CY15B104) {
---
> memory_type == AUDIO_MEMORY_CY15B104 ||
> memory_type == AUDIO_MEMORY_PSRAM_8MB)
> {
232c249,250
< if (memory_type == AUDIO_MEMORY_23LC1024) {
---
> if (memory_type == AUDIO_MEMORY_23LC1024 ||
> memory_type == AUDIO_MEMORY_PSRAM_8MB ) {
 
Example:

Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioInputI2S2           i2s2_1;         //xy=123.75,368.75
AudioAmplifier           preamp;         //xy=279.75,482.7500057220459
AudioEffectDelayExternal delay1(AUDIO_MEMORY_PSRAM_8MB,95108);         //xy=505.75,458.75
//AudioEffectDelay         delay1;         //xy=505.75,458.75
AudioMixer4              mixer1;         //xy=860.75,411.75
AudioAmplifier           amp1;           //xy=1021.75,417.75
AudioOutputSPDIF3        spdif3_1;       //xy=1225.75,536.75
AudioConnection          patchCord1(i2s2_1, 0, preamp, 0);
AudioConnection          patchCord2(preamp, delay1);
AudioConnection          patchCord3(delay1, 0, mixer1, 0);
AudioConnection          patchCord4(delay1, 1, mixer1, 1);
AudioConnection          patchCord5(delay1, 2, mixer1, 2);
AudioConnection          patchCord6(delay1, 3, mixer1, 3);
AudioConnection          patchCord7(mixer1, amp1);
AudioConnection          patchCord8(amp1, 0, spdif3_1, 0);
AudioConnection          patchCord9(amp1, 0, spdif3_1, 1);
// GUItool: end automatically generated code

void setup() {
  AudioMemory(100);
  preamp.gain(1.8F);
  mixer1.gain(0,0.5F);
  mixer1.gain(1,0.5F);
  delay1.delay( 0, 95000 );
  delay1.delay( 1, 100 );
  delay1.delay( 2, 0 );
  amp1.gain(2.2F);
}
void loop() {
  // put your main code here, to run repeatedly:
  delay(1000);
}

I'm thinking of adding optional SPI parameters to the object so that multiple instances could be created using different SPI ports or CS pins that refer to different 8MB chips.

Also, I'm fairly confident that Frank's Memoryboard could hold 6 x 8MB (48MB or 9.5 minutes of delay), but I don't need that that much delay loop yet, so holding off on that thought for now.

Here's the board with SOIC adapter. I also turned up the SPI speed to 90000000 without ill effect, so external delay object initializes much faster.

You can see the SPDIF3 output via LED (the yellow and green wires going off the top of board) and the MEMS microphones I'm using on AudioInputI2s2

psram AudioEffectDelayExternal.jpg
 
I was playing around with the 23LC1024, and found the PSRAM 8MB chip to be drop in compatible, with just a few tweaks to AudioEffectDelayExternal, mainly just changing the amount of ram to 8388608 (allowing 95108ms, 95 seconds) of delay. That's like having 10.6 memoryboard4's at your disposal, or or 16x 4-mbit fram chips. All for the cost of one inexpensive chip and a soic to dip adapter.

what's the part nr. of this kind of PSRAM? I'm very interested.. the F-RAM is very expensive..
 
Example:

Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioInputI2S2           i2s2_1;         //xy=123.75,368.75
AudioAmplifier           preamp;         //xy=279.75,482.7500057220459
AudioEffectDelayExternal delay1(AUDIO_MEMORY_PSRAM_8MB,95108);         //xy=505.75,458.75
//AudioEffectDelay         delay1;         //xy=505.75,458.75
AudioMixer4              mixer1;         //xy=860.75,411.75
AudioAmplifier           amp1;           //xy=1021.75,417.75
AudioOutputSPDIF3        spdif3_1;       //xy=1225.75,536.75
AudioConnection          patchCord1(i2s2_1, 0, preamp, 0);
AudioConnection          patchCord2(preamp, delay1);
AudioConnection          patchCord3(delay1, 0, mixer1, 0);
AudioConnection          patchCord4(delay1, 1, mixer1, 1);
AudioConnection          patchCord5(delay1, 2, mixer1, 2);
AudioConnection          patchCord6(delay1, 3, mixer1, 3);
AudioConnection          patchCord7(mixer1, amp1);
AudioConnection          patchCord8(amp1, 0, spdif3_1, 0);
AudioConnection          patchCord9(amp1, 0, spdif3_1, 1);
// GUItool: end automatically generated code

void setup() {
  AudioMemory(100);
  preamp.gain(1.8F);
  mixer1.gain(0,0.5F);
  mixer1.gain(1,0.5F);
  delay1.delay( 0, 95000 );
  delay1.delay( 1, 100 );
  delay1.delay( 2, 0 );
  amp1.gain(2.2F);
}
void loop() {
  // put your main code here, to run repeatedly:
  delay(1000);
}

I'm thinking of adding optional SPI parameters to the object so that multiple instances could be created using different SPI ports or CS pins that refer to different 8MB chips.

Also, I'm fairly confident that Frank's Memoryboard could hold 6 x 8MB (48MB or 9.5 minutes of delay), but I don't need that that much delay loop yet, so holding off on that thought for now.

Here's the board with SOIC adapter. I also turned up the SPI speed to 90000000 without ill effect, so external delay object initializes much faster.

You can see the SPDIF3 output via LED (the yellow and green wires going off the top of board) and the MEMS microphones I'm using on AudioInputI2s2

View attachment 25567

Hi Robz!

Did you ever end up trying the Frank's memory board with 6x PSRAM chips? I'm interested in trying this out myself but would like to double check before ordering anything.

Another more broad question, not particularly to you Robz. Of course if you can help, it's very welcome.
What is the maximum number of PSRAM chips that can be connected to the Teensy 4.0 or 4.1 without the additional chips of the memory board?

Best,
Miro
 
I was playing around with the 23LC1024, and found the PSRAM 8MB chip to be drop in compatible, with just a few tweaks to AudioEffectDelayExternal, mainly just changing the amount of ram to 8388608 (allowing 95108ms, 95 seconds) of delay.
Can I point out that you need to change the memory amount to 4194304, as the AudioEffectDelayExternal code works in samples, not bytes.
 
Hi robz,

thanks for testing this and creating a patch. I tried to integrate this into an additional library, but have some problems getting the whole thing to run. Unfortunately the whole thing seems to crash. The test program for the RAM works though. So I hope you have an idea what is going wrong?

Before posting my code, these are small changes I made:
- I renamed the class to AudioEffectDelayExternal8 and the enum type to AudioEffectDelayMemoryType8_t to be able to use the library next to the original library.
- I extended the array static uint32_t allocated[4]; to four (and initialized it with zeroes), because of the four different memory types used.
- Last, I reduced the memsize for initialization to 2^22 (see post above): memsize = (2^22); // 2^23 = 8388608 bytes, but 2^22 = 4194304 samples!

Here is my library code and my test program:
effect_delay_ext8.h:
Code:
#ifndef effect_delay_ext8_h_
#define effect_delay_ext8_h_
#include "Arduino.h"
#include "AudioStream.h"
#include "spi_interrupt.h"

enum AudioEffectDelayMemoryType8_t {
        AUDIO_MEMORY8_23LC1024 = 0,     // 128k x 8 S-RAM
        AUDIO_MEMORY8_MEMORYBOARD = 1,
        AUDIO_MEMORY8_CY15B104 = 2,     // 512k x 8 F-RAM
        AUDIO_MEMORY8_PSRAM_8MB = 3,    // 8192k x 8 PS-RAM 
        AUDIO_MEMORY8_UNDEFINED = 4
};

class AudioEffectDelayExternal8 : public AudioStream
{
public:
        AudioEffectDelayExternal8() : AudioStream(1, inputQueueArray) {
                initialize(AUDIO_MEMORY8_23LC1024, 65536);
        }
        AudioEffectDelayExternal8(AudioEffectDelayMemoryType8_t type, float milliseconds=1e6)
          : AudioStream(1, inputQueueArray) {
                uint32_t n = (milliseconds*(AUDIO_SAMPLE_RATE_EXACT/1000.0f))+0.5f;
                initialize(type, n);
        }

        void delay(uint8_t channel, float milliseconds) {
                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;
                if (n > memory_length - AUDIO_BLOCK_SAMPLES)
                        n = memory_length - AUDIO_BLOCK_SAMPLES;
                delay_length[channel] = n;
                uint8_t mask = activemask;
                if (activemask == 0) AudioStartUsingSPI();
                activemask = mask | (1<<channel);
        }
        void disable(uint8_t channel) {
                if (channel >= 8) return;
                uint8_t mask = activemask & ~(1<<channel);
                activemask = mask;
                if (mask == 0) AudioStopUsingSPI();
        }
        virtual void update(void);
private:
        void initialize(AudioEffectDelayMemoryType8_t type, uint32_t samples);
        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_begin;    // the first address in the memory we're using
        uint32_t memory_length;   // the amount of memory we're using
        uint32_t head_offset;     // head index (incoming) data into external memory
        uint32_t delay_length[8]; // # of sample delay for each channel (128 = no delay)
        uint8_t  activemask;      // which output channels are active
        uint8_t  memory_type;     // 0=23LC1024, 1=Frank's Memoryboard, 3=CY15B104 ,4=APS6404L-3SQR
        static uint32_t allocated[4];
        audio_block_t *inputQueueArray[1];
};

#endif

effect_delay_ext8.cpp
Code:
#include <Arduino.h>
#include "effect_delay_ext8.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(20000000, 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 SPIRAM_MOSI_PIN 11 //-----------------
#define SPIRAM_MISO_PIN 12 // for Teensy 4.1
#define SPIRAM_SCK_PIN 13 //
#define SPIRAM_CS_PIN 36 //-----------------
                         //
#define MEMBOARD_CS0_PIN 2
#define MEMBOARD_CS1_PIN 3
#define MEMBOARD_CS2_PIN 4

uint32_t AudioEffectDelayExternal8::allocated[4] = {0, 0, 0, 0};

void AudioEffectDelayExternal8::update(void)
{
        audio_block_t *block;
        uint32_t n, channel, read_offset;

        // grab incoming data and put it into the memory
        block = receiveReadOnly();
        if (memory_type >= AUDIO_MEMORY8_UNDEFINED) {
                // ignore input and do nothing if undefined memory type
                release(block);
                return;
        }
        if (block) {
                if (head_offset + AUDIO_BLOCK_SAMPLES <= memory_length) {
                        // a single write is enough
                        write(head_offset, AUDIO_BLOCK_SAMPLES, block->data);
                        head_offset += AUDIO_BLOCK_SAMPLES;
                } else {
                        // write wraps across end-of-memory
                        n = memory_length - head_offset;
                        write(head_offset, n, block->data);
                        head_offset = AUDIO_BLOCK_SAMPLES - n;
                        write(0, head_offset, block->data + n);
                }
                release(block);
        } else {
                // if no input, store zeros, so later playback will
                // not be random garbage previously stored in memory
                if (head_offset + AUDIO_BLOCK_SAMPLES <= memory_length) {
                        zero(head_offset, AUDIO_BLOCK_SAMPLES);
                        head_offset += AUDIO_BLOCK_SAMPLES;
                } else {
                        n = memory_length - head_offset;
                        zero(head_offset, n);
                        head_offset = AUDIO_BLOCK_SAMPLES - n;
                        zero(0, head_offset);
                }
        }

        // transmit the delayed outputs
        for (channel = 0; channel < 8; channel++) {
                if (!(activemask & (1<<channel))) continue;
                block = allocate();
                if (!block) continue;
                // compute the delayed location where we read
                if (delay_length[channel] <= head_offset) {
                        read_offset = head_offset - delay_length[channel];
                } else {
                        read_offset = memory_length + head_offset - delay_length[channel];
                }
                if (read_offset + AUDIO_BLOCK_SAMPLES <= memory_length) {
                        // a single read will do it
                        read(read_offset, AUDIO_BLOCK_SAMPLES, block->data);
                } else {
                        // read wraps across end-of-memory
                        n = memory_length - read_offset;
                        read(read_offset, n, block->data);
                        read(0, AUDIO_BLOCK_SAMPLES - n, block->data + n);
                }
                transmit(block, channel);
                release(block);
        }
}

void AudioEffectDelayExternal8::initialize(AudioEffectDelayMemoryType8_t type, uint32_t samples)
{
        uint32_t memsize, avail;

        activemask = 0;
        head_offset = 0;
        memory_type = type;

        SPI.setMOSI(SPIRAM_MOSI_PIN);
        SPI.setMISO(SPIRAM_MISO_PIN);
        SPI.setSCK(SPIRAM_SCK_PIN);
        SPI.setCS(SPIRAM_CS_PIN); // added for Teensy 4.1

        SPI.begin();

        if (type == AUDIO_MEMORY8_23LC1024) {
#ifdef INTERNAL_TEST
                memsize = 8000;
#else
                memsize = 65536;
#endif
                pinMode(SPIRAM_CS_PIN, OUTPUT);
                digitalWriteFast(SPIRAM_CS_PIN, HIGH);
        } else if (type == AUDIO_MEMORY8_MEMORYBOARD) {
                memsize = 393216;
                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);
        } else if (type == AUDIO_MEMORY8_CY15B104) {
#ifdef INTERNAL_TEST
                memsize = 8000;
#else
                memsize = 262144;
#endif
                pinMode(SPIRAM_CS_PIN, OUTPUT);
                digitalWriteFast(SPIRAM_CS_PIN, HIGH);
        } else if (type == AUDIO_MEMORY8_PSRAM_8MB) {
                #ifdef INTERNAL_TEST
                memsize = 8000;
                #else
                memsize = (2^22); // 2^23 = 8388608 bytes
                #endif
                pinMode(SPIRAM_CS_PIN, OUTPUT);
                digitalWriteFast(SPIRAM_CS_PIN, HIGH);
        } else {
                return;
        }
        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;
        memory_length = samples;

        zero(0, memory_length);
}


#ifdef INTERNAL_TEST
static int16_t testmem[8000]; // testing only
#endif

void AudioEffectDelayExternal8::read(uint32_t offset, uint32_t count, int16_t *data)
{
        uint32_t addr = memory_begin + offset;

#ifdef INTERNAL_TEST
        while (count) { *data++ = testmem[addr++]; count--; } // testing only
#else
        if (memory_type == AUDIO_MEMORY8_23LC1024 || 
                memory_type == AUDIO_MEMORY8_CY15B104 ||
                memory_type == AUDIO_MEMORY8_PSRAM_8MB)
        {
                addr *= 2;
                SPI.beginTransaction(SPISETTING);
                digitalWriteFast(SPIRAM_CS_PIN, LOW);
                SPI.transfer16((0x03 << 8) | (addr >> 16));
                SPI.transfer16(addr & 0xFFFF);
                while (count) {
                        *data++ = (int16_t)(SPI.transfer16(0));
                        count--;
                }
                digitalWriteFast(SPIRAM_CS_PIN, HIGH);
                SPI.endTransaction();
        } else if (memory_type == 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) << 1;
                        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;
                        do {
                                *data++ = (int16_t)(SPI.transfer16(0));
                        } while (--num > 0);
                }
                digitalWriteFast(MEMBOARD_CS0_PIN, LOW);
                digitalWriteFast(MEMBOARD_CS1_PIN, LOW);
                digitalWriteFast(MEMBOARD_CS2_PIN, LOW);
                SPI.endTransaction();
        }
#endif
}

void AudioEffectDelayExternal8::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
        if (memory_type == AUDIO_MEMORY8_23LC1024 ||
                memory_type == AUDIO_MEMORY8_PSRAM_8MB ) {
                addr *= 2;
                SPI.beginTransaction(SPISETTING);
                digitalWriteFast(SPIRAM_CS_PIN, LOW);
                SPI.transfer16((0x02 << 8) | (addr >> 16));
                SPI.transfer16(addr & 0xFFFF);
                while (count) {
                        int16_t w = 0;
                        if (data) w = *data++;
                        SPI.transfer16(w);
                        count--;
                }
                digitalWriteFast(SPIRAM_CS_PIN, HIGH);
                SPI.endTransaction();
        } else if (memory_type == AUDIO_MEMORY8_CY15B104) {
                addr *= 2;

                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);
                while (count) {
                        int16_t w = 0;
                        if (data) w = *data++;
                        SPI.transfer16(w);
                        count--;
                }
                digitalWriteFast(SPIRAM_CS_PIN, HIGH);
                SPI.endTransaction();
        } else if (memory_type == 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) << 1;
                        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;
                        do {
                                int16_t w = 0;
                                if (data) w = *data++;
                                SPI.transfer16(w);
                        } while (--num > 0);
                }
                digitalWriteFast(MEMBOARD_CS0_PIN, LOW);
                digitalWriteFast(MEMBOARD_CS1_PIN, LOW);
                digitalWriteFast(MEMBOARD_CS2_PIN, LOW);
                SPI.endTransaction();
        }
#endif
}

... and the test program:
Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <effect_delay_ext8.h>

// GUItool: begin automatically generated code
AudioSynthWaveformSine   sine1;          //xy=382,432
AudioAmplifier           amp1;           //xy=606,441
AudioEffectDelayExternal8 delayExt1(AUDIO_MEMORY_PSRAM_8MB,95108);      //xy=770,321
AudioMixer4              mixer1;         //xy=983,386
AudioOutputI2S           i2s1;           //xy=1131,343
AudioConnection          patchCord1(sine1, amp1);
AudioConnection          patchCord2(amp1, delayExt1);
AudioConnection          patchCord3(amp1, 0, mixer1, 1);
AudioConnection          patchCord4(delayExt1, 0, mixer1, 0);
AudioConnection          patchCord5(mixer1, 0, i2s1, 0);
AudioConnection          patchCord6(mixer1, 0, i2s1, 1);
AudioControlSGTL5000     sgtl5000;     //xy=943,541
// GUItool: end automatically generated code


void setup() {
  AudioMemory(100);

  sgtl5000.enable();
  sgtl5000.lineOutLevel(29);
  sgtl5000.dacVolumeRamp();
  sgtl5000.dacVolume(1.0);
  sgtl5000.unmuteHeadphone();
  sgtl5000.unmuteLineout();
  sgtl5000.volume(0.8,0.8); // Headphone volume
  sgtl5000.audioProcessorDisable();
  sgtl5000.autoVolumeDisable();
  sgtl5000.surroundSoundDisable();
  sgtl5000.enhanceBassDisable();
  delayExt1.delay(0,100);
  sine1.amplitude(1.0);
  sine1.frequency(440);
  sine1.phase(0.0);
  mixer1.gain(0,0.5);
  mixer1.gain(1,1.0);
  amp1.gain(1.0);
}

void loop() {
  amp1.gain(1.0);
  delay(200);
  amp1.gain(0.0);
  delay(2000);
}

The first problem I am getting: error: 'AUDIO_MEMORY_PSRAM_8MB' was not declared in this scop, so I replaced it with "3" for 8MB memory. But my test program does not work - even serial messages are not shown. Do you have an idea what is going wrong?

TIA, Holger
 
The first problem I am getting: error: 'AUDIO_MEMORY_PSRAM_8MB' was not declared in this scop, so I replaced it with "3" for 8MB memory.
This part is fairly easy: AUDIO_MEMORY_PSRAM_8MB isn't declared, because you called it AUDIO_MEMORY8_PSRAM_8MB.
But my test program does not work - even serial messages are not shown. Do you have an idea what is going wrong?
Not sure why serial messages aren't showing up, but ... it sounds as if you've added PSRAM to the Teensy 4.1 to give it some EXTMEM, and generated an edited AudioEffectDelayExternal8 which expects the PSRAM to be on the audio adaptor, which it isn't!

I have a pull request in which nearly got merged but didn't because of a conflict (that I've fixed, but I think we have to wait for Paul's stack to pop a few levels before he re-addresses this). This lets you use PSRAM on the audio adaptor or as EXTMEM (or indeed both) for your delay memory; you could give that a try. There isn't an example, but I did update the info pane in the Design Tool.
 
This part is fairly easy: AUDIO_MEMORY_PSRAM_8MB isn't declared, because you called it AUDIO_MEMORY8_PSRAM_8MB.

Argh! Ok, I see the problem...

Not sure why serial messages aren't showing up, but ... it sounds as if you've added PSRAM to the Teensy 4.1 to give it some EXTMEM, and generated an edited AudioEffectDelayExternal8 which expects the PSRAM to be on the audio adaptor, which it isn't!

Ok, thanks for the hint! I totally forgot that the memory may also be soldered on the audio board. Yes, my memory is on the T_4.1 itself.

I have a pull request in which nearly got merged but didn't because of a conflict (that I've fixed, but I think we have to wait for Paul's stack to pop a few levels before he re-addresses this). This lets you use PSRAM on the audio adaptor or as EXTMEM (or indeed both) for your delay memory; you could give that a try. There isn't an example, but I did update the info pane in the Design Tool.

Wow, you have written a complete memory management for EXTMEM - that's nice! I hope your PR will be merged soon!

Thanks for your help! I will try my version again with fixed pin numbers and come back with the result soon!

Regards, Holger
 
I found this interesting Excel sheet in a forum thread: https://forum.pjrc.com/attachment.php?attachmentid=20853&d=1593888859.

I found MISO(54)/MOSI(50) and SCK(49), but CS is missing. Perhaps CS is 52? Does anyone know if this makes sense?
If I'm reading it right CS on the "back memory chips" is either 48 or 51 (the little grey cells, M. Poirot)

Ok, thanks for the hint! I totally forgot that the memory may also be soldered on the audio board. Yes, my memory is on the T_4.1 itself.

Wow, you have written a complete memory management for EXTMEM - that's nice! I hope your PR will be merged soon!

Thanks for your help! I will try my version again with fixed pin numbers and come back with the result soon!

Regards, Holger
I can't take credit for the EXTMEM heap management, that's already part of Teensyduino; not much documentation, though - the Teensy 4.1 page is about it as far as I can tell. I did hack together the on-audio-adaptor memory "management" (because I wanted it ready for dynamic audio objects), though I'll freely confess it's a bit crude and could be improved on.
 
If I'm reading it right CS on the "back memory chips" is either 48 or 51 (the little grey cells, M. Poirot)

Ahhh, yep - but does not work either :(

I tried to get the crash report:
Code:
11:02:05.534 -> Latest crash report:
11:02:05.534 -> CrashReport:
11:02:05.534 ->   A problem occurred at (system time) 11:1:58
11:02:05.534 ->   Code was executing from address 0xE478
11:02:05.534 ->   CFSR: 82
11:02:05.534 -> 	(DACCVIOL) Data Access Violation
11:02:05.534 -> 	(MMARVALID) Accessed Address: 0x2 (nullptr)
11:02:05.534 -> 	  Check code at 0xE478 - very likely a bug!

addr2line -e AudioDelay8M.ino.elf 0xE478
...../.arduino15/packages/teensy/hardware/avr/1.57.2/cores/teensy4/AudioStream.cpp:128

... which points to
Code:
AudioStream::release()

At this point I can't get any further with my current knowledge... let's see if I can find the error. At least it will take a while until I have understood the whole. Maybe I will manage to test your PR somehow - maybe I will get further with it.

Regards, Holger
 
I have a pull request in which nearly got merged

I just tried the PR, but unfortunately it does not work. I put a 200ms sine signal into the delay and get nothing (except the original signal). With AudioEffectDelay() everything works... re is the code I used:
Code:
#include <Arduino.h>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>

// GUItool: begin automatically generated code
AudioSynthWaveformSine sine1;                                     //xy=382,432
AudioAmplifier amp1;                                              //xy=606,441
AudioEffectDelayExternal delayExt1(AUDIO_MEMORY_PSRAM64, 2000);  //xy=770,321
AudioMixer4 mixer1;                                               //xy=983,386
AudioOutputI2S i2s1;                                              //xy=1131,343
AudioConnection patchCord1(sine1, amp1);
AudioConnection patchCord2(amp1, delayExt1);
AudioConnection patchCord3(amp1, 0, mixer1, 1);
AudioConnection patchCord4(delayExt1, 0, mixer1, 0);
AudioConnection patchCord5(mixer1, 0, i2s1, 0);
AudioConnection patchCord6(mixer1, 0, i2s1, 1);
AudioControlSGTL5000 sgtl5000;  //xy=943,541
// GUItool: end automatically generated code


void setup() {
  Serial.begin(230400);
  delay(50);
  Serial.println("<setup begin>");

  AudioMemory(200);

  sgtl5000.enable();
  sgtl5000.lineOutLevel(29);
  sgtl5000.dacVolumeRamp();
  sgtl5000.dacVolume(1.0);
  sgtl5000.unmuteHeadphone();
  sgtl5000.unmuteLineout();
  sgtl5000.volume(0.8, 0.8);  // Headphone volume
  sgtl5000.audioProcessorDisable();
  sgtl5000.autoVolumeDisable();
  sgtl5000.surroundSoundDisable();
  sgtl5000.enhanceBassDisable();
  delayExt1.delay(0, 333);
  sine1.amplitude(1.0);
  sine1.frequency(440);
  sine1.phase(0.0);
  mixer1.gain(0, 0.5);
  mixer1.gain(1, 1.0);
  amp1.gain(0.0);

  Serial.println("<setup end>");
}

void loop() {
  Serial.print("<SINE ON>");
  amp1.gain(1.0);
  delay(200);
  amp1.gain(0.0);
  Serial.println("<SINE OFF>");
  delay(2000);
}

Doe you have an idea?
 
I just tried the PR, but unfortunately it does not work. I put a 200ms sine signal into the delay and get nothing (except the original signal). With AudioEffectDelay() everything works... re is the code I used:
Code:
#include <Arduino.h>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>

// GUItool: begin automatically generated code
AudioSynthWaveformSine sine1;                                     //xy=382,432
AudioAmplifier amp1;                                              //xy=606,441
AudioEffectDelayExternal delayExt1([COLOR="#FF0000"]AUDIO_MEMORY_PSRAM64[/COLOR], 2000);  //xy=770,321
AudioMixer4 mixer1;                                               //xy=983,386
AudioOutputI2S i2s1;                                              //xy=1131,343
AudioConnection patchCord1(sine1, amp1);
AudioConnection patchCord2(amp1, delayExt1);
AudioConnection patchCord3(amp1, 0, mixer1, 1);
AudioConnection patchCord4(delayExt1, 0, mixer1, 0);
AudioConnection patchCord5(mixer1, 0, i2s1, 0);
AudioConnection patchCord6(mixer1, 0, i2s1, 1);
AudioControlSGTL5000 sgtl5000;  //xy=943,541
// GUItool: end automatically generated code


void setup() {
  Serial.begin(230400);
  delay(50);
  Serial.println("<setup begin>");

  AudioMemory(200);

  sgtl5000.enable();
  sgtl5000.lineOutLevel(29);
  sgtl5000.dacVolumeRamp();
  sgtl5000.dacVolume(1.0);
  sgtl5000.unmuteHeadphone();
  sgtl5000.unmuteLineout();
  sgtl5000.volume(0.8, 0.8);  // Headphone volume
  sgtl5000.audioProcessorDisable();
  sgtl5000.autoVolumeDisable();
  sgtl5000.surroundSoundDisable();
  sgtl5000.enhanceBassDisable();
  delayExt1.delay(0, 333);
  sine1.amplitude(1.0);
  sine1.frequency(440);
  sine1.phase(0.0);
  mixer1.gain(0, 0.5);
  mixer1.gain(1, 1.0);
  amp1.gain(0.0);

  Serial.println("<setup end>");
}

void loop() {
  Serial.print("<SINE ON>");
  amp1.gain(1.0);
  delay(200);
  amp1.gain(0.0);
  Serial.println("<SINE OFF>");
  delay(2000);
}

Doe you have an idea?
It looks as if you've requested to use delay memory fitted to the audio adaptor, but previously you said you put it on the Teensy 4.1 as EXTMEM - try changing that statement to AudioEffectDelayExternal delayExt1(AUDIO_MEMORY_EXTMEM, 2000);

The various options are listed in AudioEffectDelayMemoryType_t in extmem.h, but not very well-commented. The first 4 use SPI memory on the adaptor; don't use the next one; then the others are in heap and EXTMEM.
 
It looks as if you've requested to use delay memory fitted to the audio adaptor, but previously you said you put it on the Teensy 4.1 as EXTMEM - try changing that statement to AudioEffectDelayExternal delayExt1(AUDIO_MEMORY_EXTMEM, 2000);

The various options are listed in AudioEffectDelayMemoryType_t in extmem.h, but not very well-commented. The first 4 use SPI memory on the adaptor; don't use the next one; then the others are in heap and EXTMEM.

Yep - sorry for my misunderstanding! Now it works like a charme!!!

Many thanks for this - MicroDexed now supports loooooooong delays and some more memory for variables is available. I created a library which can be added to the local library folder and which does not overlap with the Teensy library (until the PR is finally recorded): https://codeberg.org/dcoredump/MicroDexed/src/branch/dev/third-party/effect_delay_ext8
 
Back
Top