Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 11 of 11

Thread: Read/Write Buffer

  1. #1
    Junior Member
    Join Date
    Mar 2020
    Posts
    6

    Read/Write Buffer

    I own an Owl pedal (programmable effects pedal), which allows for uploading patches created using Max/MSP Gen. I was bumping up against some constraints of the hardware, so I recently purchased a Teensy 3.6 and the Blackaddr audio shield. Unfortunately, I'm brand new to anything Teensy or Arduino related, so it's uphill.

    With the Max/MSP Gen platform, I'm used to manipulating live audio samples by writing to a buffer on external RAM using a ramp LFO, and reading the buffer at different speeds/direction/size by manipulating another LFO. I've been looking for any way to do this programmatically with the Teensy but can't find any documentation or threads on creating, allocating, reading, or writing to buffers on external RAM... or maybe I'm just not using the right keywords. Most talk about changing sample rates, but that seems kinda overkill and would probably have tons of side effects with the timing of everything else.

    To summarize what I would like to do:
    Write to a buffer using a ramp to determine the array position. Then read from that buffer using another LFO to determine the array position.

    Any ideas?

  2. #2
    Senior Member
    Join Date
    Feb 2015
    Posts
    213
    That sounds very similar to what a user here was doing to create a chorus effect, his code is here: https://github.com/quarterturn/teensy3-ensemble-chorus

    He was using a fixed frequency LFO that was integrated into the chorus effect though.

  3. #3
    Junior Member
    Join Date
    Mar 2020
    Posts
    6
    Thanks for the link! This is all very different from the Audio examples or the BlackAddr audio examples. It looks like there are some crucial constants used, who's definitions I can't find and are completely unused in any of the examples... and vice versa. I'm not sure how or where they're writing to the external ram or reading it or how they define the speed at which they'd do it, but maybe I'm still just missing a lot of familiarity with their code. I'll keep looking.

  4. #4
    Senior Member
    Join Date
    Feb 2015
    Posts
    213
    At the current time, there is no way to just malloc() onto external RAM. The delay effect, for example, has a long duration version that is hard coded to use external RAM as the buffer. You can see that here: https://github.com/PaulStoffregen/Au...ct_delay_ext.h and .cpp as well.

    Also keep in mind that Teensy 4 has quite a bit of RAM on chip. How much are you needing?

  5. #5
    Junior Member
    Join Date
    Mar 2020
    Posts
    6
    I guess I'm definitely missing a lot of info. Still very new to this, and realize now that I'm not even sure how to get my sketch to loop at the audio sample rate rather than however fast the Teensy feels like running... unless I just put a wait(2.9) at the end of my loop. Maybe I'm still a few months of sifting through docs before I'm able to read or write to a buffer.

  6. #6
    Senior Member
    Join Date
    Feb 2015
    Posts
    213
    Well, reading your idea (different playback speeds) again, that sounds kinda similar to the granular effect, see here for example on it: https://github.com/PaulStoffregen/Au...r/Granular.ino

    The general idea is to use the GUI tool (https://www.pjrc.com/teensy/gui/index.html) to design the "patch" and then it'll be up to you to write the application logic that makes it "do stuff". I would recommend going through some examples before trying to jump into writing custom effects. But generally speaking you don't need insert delays in your loop - the synthesis and effects happen by interrupt and DMA, behind the scenes. It's pretty awesome.

  7. #7
    Junior Member
    Join Date
    Mar 2020
    Posts
    6
    THAT. Yeah. I kinda want the granular effect, but continuously write to a buffer array of int32, and read the packets forward, reverse, different speeds etc.
    What I did before (and what it sounds like I need to do) is:
    WRITE:
    1. Create an array in the external RAM of a size proportional to the milliseconds I want to record of the guitar, say 1 second.
    2. Somehow increment the array position it's writing to at realtime speed (however I'd calculate that).
    3. Return to position 0 when it gets to the end of the array so it can begin overwriting.
    READ:
    4. Cycle through the array or a section of the array to read the packets in realtime speed or multiple/fraction of speed.

    I have the guitar signal going through, so if I can get step 1 done, I'll definitely be in a good spot.
    Still lost on doc for accessing the SRAM and creating an audio packet array tho. So that'd be a good start.

  8. #8
    Senior Member
    Join Date
    Feb 2015
    Posts
    213
    Well, best to understand the example that I linked to. But you could do something like this (pseudocode basically, will not compile):
    Code:
    #include <Audio.h>
    #include <Wire.h>
    #include <SPI.h>
    #include <SD.h>
    #include <SerialFlash.h>
    
    // GUItool: begin automatically generated code
    AudioInputI2S            i2s1;           //xy=214,198
    AudioEffectGranular      granular1;      //xy=400,196
    AudioOutputI2S           i2s2;           //xy=652.8571662902832,201.42856311798096
    AudioConnection          patchCord1(i2s1, 0, granular1, 0);
    AudioConnection          patchCord2(granular1, 0, i2s2, 0);
    AudioConnection          patchCord3(granular1, 0, i2s2, 1);
    AudioControlSGTL5000     sgtl5000_1;     //xy=638,133
    // GUItool: end automatically generated code
    
    
    #define GRANULAR_MEMORY_SIZE 12800  // enough for 290 ms at 44.1 kHz
    int16_t granularMemory[GRANULAR_MEMORY_SIZE];
    
    void setup() {
      
      AudioMemory(10);
    
      sgtl5000_1.enable();
      sgtl5000_1.volume(0.5);
    
      // the Granular effect requires memory to operate
      granular1.begin(granularMemory, GRANULAR_MEMORY_SIZE);
    }
    
    void loop() {
    	float msec = // read potentiometer, etc
    
    	if(// read button here, or whatever you want...) {
    		granular1.beginFreeze(msec);
    	}
    
    	if(// read other button here) {
    		granular1.beginPitchShift(msec);
    	}
    
    	if(// some other condition) {
    		granular1.stop();
    	}
    }
    The granular effect, as it is now, can pitch shift and speed shift, but it doesn't support reversing.

  9. #9
    Junior Member
    Join Date
    Mar 2020
    Posts
    6
    Thanks. From what I saw from the AudioEffectGranular code it looked like it only supported wavs from an SD card, but cool to know that it accepts a normal audio stream.
    But you're right, it looks like I'm going to have to write my own granular AudioEffect to achieve what I want. (playing forward and backward, different speeds, different start and end points)
    It seems like all I need to get started is to access the SRAM and create an array of audio packets, but I'm sure there's much more to it than that.

  10. #10
    Junior Member
    Join Date
    Mar 2020
    Posts
    6
    Actually, it looks like I can reuse a lot of the code from the granular effect, though there's some heavy modifications that need to be made... especially when this is to be used with the BlackAddr audio shield for higher quality audio.
    Looking optimistic today!

  11. #11
    Senior Member
    Join Date
    Feb 2017
    Posts
    369
    Last I looked at it, the AudioEffectGranular class only worked with internal RAM, not external SPI RAM. So, you'll need to code that if you want large sample buffers. You'll need to be careful to read / write sufficiently large amounts of data at once as the the overhead associated with accessing it might kill you if you only work with a few words at a time.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •