propa
Well-known member
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
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/tree/play-audio-sd-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.
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.
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 );
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/tree/play-audio-sd-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.