Select Git revision
board.json.imp_meta
daisy-looper.ino 7.38 KiB
// Title: moogladder
// Description: Sweeps the cutoff with an lfo
// Hardware: Daisy Seed
// Author: Ben Sergentanis
#include "DaisyDuino.h"
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH110X.h>
#include <CpuLoadMeter.h>
#include "leds.h"
#include "potentiometers.h"
#include "buttons.h"
#include "looper.h"
#include "env_follower.h"
#include "helpers.h"
#include "ui.h"
#define BUFFER_LENGTH_SECONDS 5
static const size_t buffer_length = 48000 * BUFFER_LENGTH_SECONDS;
static float DSY_SDRAM_BSS buffer[buffer_length];
// Create instances of audio stuff
atoav::Looper looper;
static atoav::EnvelopeFollower input_envelope_follower;
DelayLine<float, 24000> delayline;
DSY_SDRAM_BSS ReverbSc reverb;
static Compressor compressor;
Oscillator lfo;
static SampleHold sample_and_hold;
static WhiteNoise noise;
static Metro tick;
// Initialize Buttons
Button button_1 = Button(D7);
Button button_2 = Button(D8);
Button button_3 = Button(D9);
Button button_4 = Button(D10);
Button button_5 = Button(D13);
Button button_6 = Button(D14);
// Initialize Potentiometers
Potentiometer pot_1 = Potentiometer(A0);
Potentiometer pot_2 = Potentiometer(A1);
Potentiometer pot_3 = Potentiometer(A3);
Potentiometer pot_4 = Potentiometer(A2);
Potentiometer pot_5 = Potentiometer(A4);
Potentiometer pot_6 = Potentiometer(A5);
Potentiometer pot_7 = Potentiometer(A6);
// RGB LED R G B
RGBLed rgb_led = RGBLed(A10, A9, A11);
// OLED Display
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET -1 // QT-PY / XIAO
Adafruit_SH1106G display = Adafruit_SH1106G(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// User Interface
Ui ui;
// Daisy
DaisyHardware hw;
// Variables for the Audio-Callback
size_t num_channels
;
float blocksize;
float drywetmix = 0.0f;
float delaymix = 0.0f;
float delaytime = 100.0f;
float reverbmix = 0.0f;
float lfo_amount = 0.0f;
float pitch_val = 0.5f;
// Actual audio-processing is orchestrated here
void AudioCallback(float **in, float **out, size_t size) {
float output = 0.0f;
float no_delay = 0.0f;
float wet_delay;
float out1, out2;
// set the delay
delayline.SetDelay(delaytime);
for (size_t i = 0; i < size; i++) {
uint8_t trig = tick.Process();
float lfo_value = lfo.Process();
float noise_value = noise.Process();
float rand = sample_and_hold.Process(trig, noise_value * 5.0f, sample_and_hold.MODE_SAMPLE_HOLD);
if (ui.rec_source == REC_SOURCE_NOISE) {
looper.Record(noise_value * 0.7f);
}
// When the metro ticks, trigger the envelope to start.
float random_amount = (lfo_amount -0.5f) * 2.0;
if (trig) {
// tick.SetFreq(rand / (0.1 + random_amount*1000.0f) + 1);
// If the dial is over 50% jump instead
if (lfo_amount > 0.5f) {
looper.addToPlayhead(rand * random_amount* 48000.0f);
looper.setPlaybackSpeed(pitch_val);
}
}
if (lfo_amount > 0.5f) {
looper.setPlaybackSpeed(pitch_val);
} else {
looper.setPlaybackSpeed(pitch_val + lfo_value * lfo_amount);
}
float looper_out;
// res.SetDamping(0.1+delaymix*0.2f);
// Record into the buffer
if (ui.rec_source == REC_SOURCE_PRE) {
looper.Record(in[1][i]);
}
input_envelope_follower.Process(in[1][i]);
// Process the Looper
looper_out = looper.Process();
// Mix the dry/Wet of the looper
output = drywetmix * looper_out + in[1][i] * (1.0f - drywetmix);
// output = output * (1.0f - resmix) + res.Process(output*resmix);
no_delay = output;
wet_delay = delayline.Read();
delayline.Write((wet_delay * delaymix * 0.95f)/2.0f + (no_delay*delaymix)/2.0f);
// Add the delay
output += wet_delay * delaymix;
// Compress the signal
compressor.Process(output);
// Process reverb
reverb.Process(output, output, &out1, &out2);
// Mix reverb with the dry signal depending on the amount dialed
output = output * (1.0f - reverbmix) + out1 * reverbmix;
// Record the output if needed
if (ui.rec_source == REC_SOURCE_OUT) {
looper.Record(output);
}
out[0][i] = out[1][i] = output;
}
}
void setup() {
float sample_rate;
// Initialize for Daisy pod at 48kHz
hw = DAISY.init(DAISY_SEED, AUDIO_SR_48K);
DAISY.SetAudioBlockSize(64);
num_channels = hw.num_channels;
sample_rate = DAISY.get_samplerate();
blocksize = 64.0f;
// Create a Tick and a noise source for the Sample and Hold
tick.Init(10, sample_rate);
noise.Init();
// Initialize Looper with the buffer
looper.Init(buffer, buffer_length);
// Initialize Envelope Follower for the Level LED
input_envelope_follower.Init(sample_rate);
input_envelope_follower.SetAttack(100.0);
input_envelope_follower.SetDecay(1000.0);
// Initialize Reverb
reverb.Init(sample_rate);
reverb.SetFeedback(0.95f);
reverb.SetLpFreq(18000.0f);
// Initialize Compressor
compressor.SetThreshold(-64.0f);
compressor.SetRatio(2.0f);
compressor.SetAttack(0.005f);
compressor.SetRelease(0.1250);
// Initialize the LFO for modulations
lfo.Init(sample_rate);
lfo.SetWaveform(Oscillator::WAVE_TRI);
lfo.SetAmp(1);
lfo.SetFreq(8.0);
// Initialize the Delay at a length of 1 second
delayline.Init();
delayline.SetDelay(sample_rate);
// Start serial communications
Serial.begin(250000);
Serial.println("Serial communication started");
// Initialize Display
display.begin(0x3C, true);
delay(50);
ui.Render();
// Initialize the LED
rgb_led.init();
// Set the analog read and write resolution to 12 bits
analogReadResolution(12);
// Set Knob names and display functions
pot_1.name = "Start";
pot_2.name = "Length";
pot_3.setDisplayMode("Pitch", 1000.0f, POT_DISPLAY_MODE_PERCENT);
pot_4.setDisplayMode("Mix", 100.0f, POT_DISPLAY_MODE_PERCENT);
pot_5.setDisplayMode("Delay", 100.0f, POT_DISPLAY_MODE_PERCENT);
pot_6.setDisplayMode("Reverb", 100.0f, POT_DISPLAY_MODE_PERCENT);
pot_7.setDisplayMode("LFO", 100.0f, POT_DISPLAY_MODE_PERCENT);
// Set Knob Scaling Modes
pot_3.setBipolar();
// Initialize Buttons (callbacks are assigned in the Ui class)
button_1.init();
button_2.init();
button_3.init();
button_4.init();
button_5.init();
button_6.init();
// Start the audio Callback
DAISY.begin(AudioCallback);
}
void loop() {
// Read the values from the potentiometers
float p1 = pot_1.read();
float p2 = pot_2.read();
float p3 = pot_3.read();
float p4 = pot_4.read();
float p5 = pot_5.read();
float p6 = pot_6.read();
float p7 = pot_7.read();
// Update the UI
ui.update();
// Read the buttons
button_1.read();
button_2.read();
button_3.read();
button_4.read();
button_5.read();
button_6.read();
// Set loop-start and loop-length with the potentiometers
ui.setLoop(p1, p2);
// Tune the pitch to the ten-fold of the potentiometer 3,
// a bipolar pot, so it returns values from -1.0 to +1.0
// Pitch should go 10 octaves up/down (10*1.0 = 1000%)
pitch_val = 10.0f * p3;
// Set other parameters (from 0.0 to 1.0)
drywetmix = p4;
delaymix = p5;
// Delaytime is in samples
delaytime = 100.0f + p5 * 23900.0f;
reverbmix = p6;
lfo_amount = p7;
// Render the UI (frame rate limited by UI_MAX_FPS in ui.h)
ui.Render();
// Set the Color and brightness of the RGB LED in 8 bits
rgb_led.setAudioLevelIndicator(int(input_envelope_follower.getValue() * 255));
}