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

Thread: Audio Looping investigations

  1. #1
    Member propa's Avatar
    Join Date
    Apr 2018
    Location
    UK
    Posts
    93

    Audio Looping investigations

    Hello,

    I'd like to discuss looping, I've found three ways so far, forum user Moo, Cutlasses and Bleep labs have all posted code to that will allow looping and control of a sampled sound to some degree, each have their own drawbacks and advantages.

    Here's what I've found so far..

    if (Teensy >= 3.5) {Moo’s code, Cutlasses code}

    +'s Moo's reads from SD, and has a large range and reverse, similarly Cutlass has reverse, and file scanning(changing placement of playback head), and loop end shortening. Very cool. -'s Moo's only reads from SD, and only in RAW, requires 3.5 or 3.6 to perform well, SPI SD reading from audio shield is slow compared to dedicated SD on 3.5/3.6, that doesn't block any other pins. Cutlasses code I haven't tested and can't compile due to an error with this
    Code:
     void          update( ADC& adc );
    line, but the video evidence is convincingly cool. If anyone can figure out what I'm doing wrong, or can get it to compile themselves I'd be interested to hear how you did it.

    Here's the link to Cutlasses code: https://github.com/cutlasses/AudioFreezeV2

    and here's the link to moo's code: https://github.com/newdigate/Audio/t...-raw-resampled

    if (Teensy <= 3.2 && (u == qwik || durty)) {Bleep’s Tape Delay}
    Cheap, quick looper using Tape delay to grab a loop of feeding back delay as a simple answer to an over-dubbing looper. To install download bleep's fork of audio, take out TapeDelayEffect.h and TapeDelayEffect.cpp and put into your current audio library, also change audio.h to include a reference to you newly added audio effect.

    This code creates a simple overdubbing looper controlled based around a delay line, when a switch is depressed it stares into the void of infinity waiting for a cheap buzz, I mean, when you let go of the button it stops looping.

    Code:
    /*
       Copyright (c) 2018 John-Michael Reed
       bleeplabs.com
    
       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 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.
    
    
       This is a looper based on the Tape Delay effect by Bleep Labs
    
    */
    
    #include <Audio.h>
    #include <Wire.h>
    #include <SPI.h>
    #include <SD.h>
    #include <SerialFlash.h>
    #include <Bounce.h>
    
    // GUItool: begin automatically generated code
    AudioInputI2S            i2s1;           //xy=104,218
    AudioMixer4              mixer1;         //xy=315,240
    AudioEffectTapeDelay         tapeDelay1;         //xy=473,232
    AudioOutputI2S           i2s2;           //xy=701.4285774230957,192.8571548461914
    
    AudioConnection          patchCord1(i2s1, 0, mixer1, 0);
    AudioConnection          patchCord2(mixer1, tapeDelay1);
    
    AudioConnection          patchCord3(tapeDelay1, 0, i2s2, 0);
    AudioConnection          patchCord4(tapeDelay1, 0, i2s2, 1);
    
    AudioConnection          patchCord5(tapeDelay1, 0, mixer1, 1);
    
    AudioControlSGTL5000     sgtl5000_1;     //xy=725.9999961853027,277.4285707473755
    // GUItool: end automatically generated code
    
    Bounce button1 = Bounce(6, 15);
    Bounce button2 = Bounce(7, 15);
    
    Bounce inputSelecta = Bounce(1, 15);
    Bounce modeSelecta = Bounce(2, 15);
    
    int led1 = 3;
    int led2 = 4;
    
    #define pot1_pin A1
    #define pot2_pin A7
    
    #define DELAY_MAX_LEN 25000
    short tape_delay_bank[DELAY_MAX_LEN] = {};
    
    float delay_time;
    float feedback_level;
    
    
    bool micOn;
    bool inLoopMode;
    
    bool loopRecButton;
    bool deleteButton;
    
    uint32_t cm, prev[4];
    
    void setup() {
      
      AudioNoInterrupts();
      AudioMemory(10);
      pinMode(0, INPUT_PULLUP);
      pinMode(6, INPUT_PULLUP);
      pinMode(7, INPUT_PULLUP);
      pinMode(8, INPUT_PULLUP);
      
      pinMode(1, INPUT_PULLUP);
      pinMode(2, INPUT_PULLUP);
      pinMode(led1, OUTPUT);
      pinMode(led2, OUTPUT);
      sgtl5000_1.enable();
      sgtl5000_1.volume(0.5);
      sgtl5000_1.inputSelect(AUDIO_INPUT_MIC);
      sgtl5000_1.micGain(36);
      tapeDelay1.begin(tape_delay_bank, DELAY_MAX_LEN, 5000, 0, 0); //bank to use, size of bank, delay time in samples , rate reduction, interpolation time
      //rate reduction of 0 means the delay will increment at the same rate as playback
      //at 1 we double out delay time by halving the sample rate. 2 quadrules it and so on.   
      //Halving the sample rate is nothing to be scared of!
     
      //interpolation time is how ling it takes for the "read head" to get to the desired dlay length. 
      //0 is as quickly as possible, 10 is a standard tape delay sound and 20 is getting a little slow and crazy
      mixer1.gain(0, .5);
      mixer1.gain(1, .5); //feedback level
      AudioInterrupts();
      analogReadAveraging(41);
    }
    
    void loop() {
      button1.update();
      button2.update();
      
      inputSelecta.update();
      modeSelecta.update();
      // read knobs, scale to 0-1.0 numbers
      float knobA2 = (float)analogRead(A1);
      float knobA3 = (float)analogRead(A7) / 64.0;
      float knobA4 = (float)analogRead(A6);
    
      sine1.frequency(knobA4);
    
      if (inputSelecta.fallingEdge()) 
      { 
        digitalWrite(led1,HIGH);
        sgtl5000_1.inputSelect(AUDIO_INPUT_MIC);
        Serial.println("MIC IN SELECTED");
        micOn = true;
      }
    
      if (inputSelecta.risingEdge()) 
      {
        digitalWrite(led1,LOW);
        sgtl5000_1.inputSelect(AUDIO_INPUT_LINEIN);
        Serial.println("LINE IN SELECTED");
        micOn = false;
      }
    
      if (modeSelecta.fallingEdge()) 
      { 
        Serial.println("Normal Delay Mode");
        mixer1.gain(0, 0.5);
        mixer1.gain(1, 0.5);
        digitalWrite(led2,HIGH);
        inLoopMode = true;
      }
    
      if (modeSelecta.risingEdge()) 
      {
        Serial.println("Simple Looping Mode");
        digitalWrite(led2,LOW);
        inLoopMode = false;
      }
     
      cm = millis();
    
      if (button1.fallingEdge()) 
      {
        loopRecButton = true;
      }
      
      if (button1.risingEdge()) 
      {
        loopRecButton = false;
      }
    
      if (button2.fallingEdge()) 
      {
        
        deleteButton = true;
      }
      
      if (button2.risingEdge()) 
      {
        deleteButton = false;
      }
    
      if (deleteButton)
      {
        sgtl5000_1.muteLineout();
        mixer1.gain(1, 0.0); // Would be better if it faded instead of clicking at the end
        
        //Serial.println("deleteButton");
      }
    
      if (!deleteButton)
      {
        sgtl5000_1.unmuteLineout();
      }
    
      if (!inLoopMode)
      { // This is normal delay mode, will feedback, feedback control seems to behave a bit weirdly
        if (cm - prev[1] > 5) { //to redusce nosie ist's best not to do this too rapidly and to smooth the pot reading.
        prev[1] = cm;
        delay_time = analogRead(A1) * ( DELAY_MAX_LEN / 1023.00);
    
        if (loopRecButton && !deleteButton)
        { 
          mixer1.gain(0, 0.5);
          feedback_level = 1.0;
        }
        if (!loopRecButton)
        { 
        mixer1.gain(0, 0.0);
        }
        
        mixer1.gain(1, feedback_level);
        tapeDelay1.length(delay_time);
      }
      }
    
      if (inLoopMode)
      {
        if (cm - prev[1] > 5) 
        { //to redusce nosie ist's best not to do this too rapidly and to smooth the pot reading.
        prev[1] = cm;
        delay_time = analogRead(pot1_pin) * ( DELAY_MAX_LEN / 1023.00);
        feedback_level = analogRead(pot2_pin) /1023;
        //mixer1.gain(0, 0.5);
        mixer1.gain(1, feedback_level);
        tapeDelay1.length(delay_time);
      }
      }
    
      if (cm - prev[0] > 500) 
      {
        prev[0] = cm;
    //
    //    Serial.print(AudioProcessorUsageMax());
    //    Serial.print(" ");
    //    Serial.println(AudioMemoryUsageMax());
    
    //    Serial.println(delay_time);
    //    Serial.println();
    
        AudioProcessorUsageMaxReset();
        AudioMemoryUsageMaxReset();
      }
    
    }

    Also if anyone would like to add their efforts in this investigation, please be my guest, I'd appreciate the contribution. The ideal outcome would be an object we can all use or a decent thread to point to when this question inevitably arrises again.

    Points will be given, or gold stars, or rainbow coloured unicorn flakes.

  2. #2
    Member propa's Avatar
    Join Date
    Apr 2018
    Location
    UK
    Posts
    93
    In further investigations: https://github.com/cutlasses/AudioFreeze AudioFreeze v1 works without any compiler hiccups, but overflows the RAM on 3.2. I should be getting a 3.6 soon and will give it a test.

    I have been able to get AudioFreezeV2 to compile if I take out all references to the interface code.

    There are some head scratching differences, in v2 it calls "void update( ADC & adc );" in v1 it's just void update(), I don't understand what's going wrong with the "ADC& adc" part.

    Code:
    struct IO
    {
      ADC                         adc;
      AudioInputAnalog            audio_input;
      AudioOutputAnalog           audio_output;
    
      IO() :
        adc(),
        audio_input(A0),
        audio_output()
      {
      }
    };
    Here's the whole project, if anyone can compile it please let me know what I'm missing: https://github.com/cutlasses/AudioFreezeV2

    I think this ADC& adc is really the culprit, but i just can't figure out why.

    Code:
    class DIAL
    {
      int           m_data_pin;
    
      int           m_current_value;
      
    public:
    
      DIAL( int data_pin );
    
      bool          update( ADC& adc );
      float         value() const;  
    };
    I've watched the talk he's done at Electro Magnetic Field and says the code is open source, and says feel free to remix: http://www.cutlasses.co.uk/live/elec...ield-festival/

    I assume the code works and I'm doing something incorrect and dumb, or don't have the right library installed, if anyone has any insight ADC& adc problem I'm very keen to hear.

  3. #3
    Junior Member
    Join Date
    Jan 2017
    Posts
    14
    Hi,

    interesting, thanks for sharing. I tried Cutlasses code as well, but I cannot get it to compile at all. I removed all the interface stuff but it still overflows in ram. region `RAM' overflowed by 190104 bytes

    I had a look at the audiofreezeeffect.h but that does not have that much code at all. Tbh, I had a look for a good hour and as far as I can tell the audio interface stuff is only to make it easy to add buttons and whatnot to the device. I cannot seem to find where the actual audio manipulation bit of the 'new' code is. I mean, I could find e.g. how (and where it is defined) the reverse sample play is called, but I did not see where that piece of code actually sits. The video does look really cool, but as mentioned, once you strip out all the knobs and buttons from the files, there's not much left, is it perhaps incomplete?

    EDIT: ok in a final attempts I noticed there was one ino file I did not look at, I was expecting the 'magic' to be in a .h or .cpp file... having another look now.

  4. #4
    Member propa's Avatar
    Join Date
    Apr 2018
    Location
    UK
    Posts
    93
    Quote Originally Posted by bobkruijer View Post
    Hi,

    interesting, thanks for sharing. I tried Cutlasses code as well, but I cannot get it to compile at all. I removed all the interface stuff but it still overflows in ram. region `RAM' overflowed by 190104 bytes

    I had a look at the audiofreezeeffect.h but that does not have that much code at all. Tbh, I had a look for a good hour and as far as I can tell the audio interface stuff is only to make it easy to add buttons and whatnot to the device. I cannot seem to find where the actual audio manipulation bit of the 'new' code is. I mean, I could find e.g. how (and where it is defined) the reverse sample play is called, but I did not see where that piece of code actually sits. The video does look really cool, but as mentioned, once you strip out all the knobs and buttons from the files, there's not much left, is it perhaps incomplete?

    EDIT: ok in a final attempts I noticed there was one ino file I did not look at, I was expecting the 'magic' to be in a .h or .cpp file... having another look now.
    I got it to compile when targeting 3.6, you're right the interface parts are just for the knobs and buttons.

    The magic is in this file: https://github.com/cutlasses/AudioFr...eezeEffect.ino write to buffer, read buffer, write sample, read sample, set_freeze and set center all look like the actual audio functions. It's just another .ino he's included, would be silly if changing it to .cpp would fix everything.

  5. #5
    Member propa's Avatar
    Join Date
    Apr 2018
    Location
    UK
    Posts
    93
    Quote Originally Posted by bobkruijer View Post
    ... is it perhaps incomplete?
    Well v1 definitely compiles without error when targeting the 3.6, I wouldn't assume it's incomplete. See if you can compile the other version he has on github

  6. #6
    Junior Member
    Join Date
    Jan 2017
    Posts
    14
    OK, so both do not compile for me on the 3.2 - maybe I need to get a 3.6 to try these.

  7. #7
    Member propa's Avatar
    Join Date
    Apr 2018
    Location
    UK
    Posts
    93
    Quote Originally Posted by bobkruijer View Post
    OK, so both do not compile for me on the 3.2 - maybe I need to get a 3.6 to try these.
    The other annoying find when using the 3.6 without the audio shield, is that the using the built in ADC through the analog pins completely knocks out use of analogRead, which is so gutting.

    I thought about a work around but don't know if it's possible as adc AudioInputAnalog has no functions*, and that's to make sure you never polled analogread while feeding audio to adc, then when finished listening to adc (when looping has commenced) you go back to analog read.

    *Maybe a function called .TurnOff() is needed. Ideally if adc is going to inhibit the function of reading analog read it would be great to have the ability to switch between either one in code. Please chime in if anyone thinks this would be a welcome addition, I think it's debilitating to loose all those precious pins!

  8. #8
    Member propa's Avatar
    Join Date
    Apr 2018
    Location
    UK
    Posts
    93
    Quote Originally Posted by bobkruijer View Post
    OK, so both do not compile for me on the 3.2 - maybe I need to get a 3.6 to try these.
    Hey I've figured out how to get Cutlasses code AudioFfreezev2 to compile, and to anyone that finds this and has the same issue. You need to download this library as well to get it to compile successfully: https://github.com/pedvide/ADC

  9. #9
    Member propa's Avatar
    Join Date
    Apr 2018
    Location
    UK
    Posts
    93
    I got Cutlasses code to compile on the 3.6 only to find once you go past a certain input volume it glitches the audio out for no reason and crashes the program, and not specified in the code either, as in this is a horrible unintentional glitch (at least you'd hope).

    The effect is almost like an accidental grain loop, really annoying and frustrating, and I have no idea why someone would release broken code, or invite people at a hack to his github when it's in such a state. Good looking code, wish it sounded half as nice as the code looks.

    on 3.6 with audio shield Moo's code just hangs and crashes the serial monitor, then the app, sometimes the computer.

    So hopes of looping anything are slimming. The range on Bleep labs granular when using the 3.6 isn't as large as other forum users would have you beleive, I have it maxing before 1000ms, around 500/600ms, which is too small for most applications.

    So if anyone has any other library's or code to explore, please hit me up. This is frustrating and boring as hell now, would appreciate any points in the right direction.

    I tried extending the tape delay based one as well, but even with the 3.6 it just hasn't got enough memory. I can't seem to go beyond 32000 for some reason:

    #define DELAY_MAX_LEN 32000
    short tape_delay_bank[DELAY_MAX_LEN] = {};

Posting Permissions

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