I tried many things, spent a night with chat gpt and drank a round with the stepper motor gang, but the problem remains.
why is the Oled slowing down my while loop?
I trigger it only on encoder and button events, but that single draw slows down my sequencer clock, the lfo, envelopes…
I would love a running light for my sequencer, but the way it is, such a thing is just not possible.
I was successful showing parameter names and values, but the adc jitter triggers the oled allthe time, slowing down the code too much.
here is my code, sorry it is big, but at least for beginners, it shows some things like successful mpr121 and the oled drawing at least something…
there are oleds in everything, what is the secret?
#include "daisysp.h"
#include <stdio.h>
#include <string.h>
#include "daisy_seed.h"
#include "dev/oled_ssd130x.h"
#include "dev/mpr121.h"
#include "dsp.h"
using namespace daisysp;
using namespace daisy;
using MyOledDisplay = OledDisplay<SSD130xI2c128x64Driver>;
dsy_gpio ledR_1_pin, ledG_1_pin, ledB_1_pin;
uint16_t currTouched;
uint16_t lastTouched;
// Define the frequencies for the two octaves surrounding middle C
double scale[][12] = {
// Octave below middle C
{130.81, 138.59, 146.83, 155.56, 164.81, 174.61, 185.00, 196.00, 207.65, 220.00, 233.08, 246.94},
// Middle C and the octave above
{261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.99, 392.00, 415.30, 440.00, 466.16, 493.88},
// Octave above middle C
{523.25, 554.37, 587.33, 622.25, 659.26, 698.46, 739.99, 783.99, 830.61, 880.00, 932.33, 987.77}};
DaisySeed hw;
MyOledDisplay display;
Mpr121I2C mpr121;
Encoder encoder;
Switch button;
static Oscillator osc;
static Oscillator lfo;
static Svf filt;
static Metro clock;
static Adsr env;
static Adsr envM;
float gate;
float envout;
int seqF;
float trig;
float sig;
int decO=0;
int trigo = 0;
float keyF=0;
int ctfO=0;
int beat[8] {1, 1, 1, 1, 1, 1, 1, 1};
int beato[8] {0, 0, 0, 0, 0 ,0 ,0 ,0};
int seqP[8] {0, 0, 0, 0, 0, 0, 0, 0};
static void AudioCallback(AudioHandle::InterleavingInputBuffer in,
AudioHandle::InterleavingOutputBuffer out,
size_t size)
{
// Declare a DelayLine of MAX_DELAY number of floats.
for(size_t i = 0; i < size; i += 2)
{
// Use envelope to control the amplitude of the oscillator.
envout = env.Process(trig && gate);
osc.SetAmp(envout);
sig = osc.Process();
filt.Process(sig);
// left out
out[i] = filt.Low();
// right out
out[i + 1] = filt.Low();
}
}
int main(void)
{
// initialize seed hardware and oscillator daisysp module
float sample_rate;
AudioHandle::Config audio_config;
audio_config.samplerate = SaiHandle::Config::SampleRate::SAI_96KHZ; // Set sample rate to 96kHz
audio_config.postgain = 1.0f; // Set post gain (volume control)
hw.Configure();
hw.Init();
hw.SetAudioBlockSize(4);
sample_rate = hw.AudioSampleRate();
//Set button to pin 28, to be updated at a 1kHz samplerate
button.Init(hw.GetPin(9), 100);
osc.Init(sample_rate);
lfo.Init(sample_rate);
//init metro object
clock.Init(2, sample_rate);
clock.SetFreq (200);
//Set envelope parameters
env.Init(sample_rate);
env.SetTime(ADSR_SEG_ATTACK, .1);
env.SetTime(ADSR_SEG_DECAY, .5);
env.SetTime(ADSR_SEG_RELEASE, .5);
env.SetSustainLevel(.5);
//Mod>Env Init
envM.Init(sample_rate);
envM.SetTime(ADSR_SEG_ATTACK, .1);
envM.SetTime(ADSR_SEG_DECAY, .5);
envM.SetTime(ADSR_SEG_RELEASE, .5);
envM.SetSustainLevel(.5);
// Set parameters for oscillator
osc.SetWaveform(osc.WAVE_POLYBLEP_SAW);
osc.SetFreq(440);
osc.SetAmp(0.5);
lfo.SetWaveform(lfo.WAVE_SIN);
lfo.SetFreq(.5);
// Initialize Filter, and set parameters.
filt.Init(sample_rate);
filt.SetFreq(500.0);
filt.SetRes(0.85);
filt.SetDrive(0.8);
// R 1 on pin 27
ledR_1_pin.pin = hw.GetPin(28);
ledR_1_pin.mode = DSY_GPIO_MODE_OUTPUT_PP;
ledR_1_pin.pull = DSY_GPIO_NOPULL;
dsy_gpio_init(&ledR_1_pin);
dsy_gpio_write(&ledR_1_pin, true);
// R 1 on pin 27
ledG_1_pin.pin = hw.GetPin(29);
ledG_1_pin.mode = DSY_GPIO_MODE_OUTPUT_PP;
ledG_1_pin.pull = DSY_GPIO_NOPULL;
dsy_gpio_init(&ledG_1_pin);
dsy_gpio_write(&ledG_1_pin, true);
// R 1 on pin 27
ledB_1_pin.pin = hw.GetPin(30);
ledB_1_pin.mode = DSY_GPIO_MODE_OUTPUT_PP;
ledB_1_pin.pull = DSY_GPIO_NOPULL;
dsy_gpio_init(&ledB_1_pin);
dsy_gpio_write(&ledB_1_pin, true);
//Configure pin 21 as an ADC input. This is where we'll read the knob.
// Create an array of two AdcChannelConfig objects
const int num_adc_channels = 11;
AdcChannelConfig my_adc_config[num_adc_channels];
// Initialize the first one connected to A0
my_adc_config[0].InitSingle(hw.GetPin(21));
my_adc_config[1].InitSingle(hw.GetPin(20));
my_adc_config[2].InitSingle(hw.GetPin(19));
my_adc_config[3].InitSingle(hw.GetPin(18));
my_adc_config[4].InitSingle(hw.GetPin(17));
my_adc_config[5].InitSingle(hw.GetPin(16));
my_adc_config[6].InitSingle(hw.GetPin(22));
my_adc_config[7].InitSingle(hw.GetPin(23));
my_adc_config[8].InitSingle(hw.GetPin(24));
my_adc_config[9].InitMux(hw.GetPin(15), 8, seed::D3, seed::D4, seed::D5);
my_adc_config[10].InitMux(hw.GetPin(25), 8, seed::D0, seed::D1, seed::D2);
//Initialize the adc with the config we just made
hw.adc.Init(my_adc_config, num_adc_channels);
//Start reading values
hw.adc.Start();
/** Initialize our Encoder */
encoder.Init(seed::D6, seed::D7, seed::D8);
// start callback
hw.StartAudio(AudioCallback);
// touch works
// Initialize MPR121 with I2C configuration
Mpr121I2C::Config mprConfig;
mprConfig.transport_config.periph = I2CHandle::Config::Peripheral::I2C_1;
mprConfig.transport_config.speed = I2CHandle::Config::Speed::I2C_400KHZ;
mprConfig.transport_config.mode = I2CHandle::Config::Mode::I2C_MASTER;
mprConfig.transport_config.scl = Pin(PORTB,8); // Replace with your SCL pin
mprConfig.transport_config.sda = Pin(PORTB,9); // Replace with your SDA pin
// Set additional MPR121 configuration if needed
mprConfig.touch_threshold = 12;
mprConfig.release_threshold = 6;
if(mpr121.Init(mprConfig) != Mpr121I2C::OK)
{
// Handle initialization error
while(1)
{
// Error handling code (blinking LED, etc.)
}
}
// Setup to print
hw.StartLog();
/** Create a variable to store the value we'll print out to USB */
int output_value = 0;
// oled
// Configure the Display
MyOledDisplay::Config disp_cfg;
disp_cfg.driver_config.transport_config.i2c_address = 0x3C;
disp_cfg.driver_config.transport_config.i2c_config.periph = I2CHandle::Config::Peripheral::I2C_1;
disp_cfg.driver_config.transport_config.i2c_config.speed = I2CHandle::Config::Speed::I2C_1MHZ;
disp_cfg.driver_config.transport_config.i2c_config.mode = I2CHandle::Config::Mode::I2C_MASTER;
disp_cfg.driver_config.transport_config.i2c_config.pin_config.scl = {DSY_GPIOB, 8};
disp_cfg.driver_config.transport_config.i2c_config.pin_config.sda = {DSY_GPIOB, 9};
// And Initialize
display.Init(disp_cfg);
display.Fill(false);
display.SetCursor(20, 10);
display.WriteString("MONOFONK", Font_11x18, true);
display.SetCursor(2, 42);
display.WriteString("Pointless Innovations", Font_6x8, true);
display.Update();
int step = 0;
int cnt = 0;
while(1) {
encoder.Debounce();
/* This will return a -1 if the encoder was turned counter clockwise, or
* a 1 if the encoder was turned clockwise, or 0 if the encoder was not rotated.
*/
int increment = encoder.Increment();
if((increment > 0) && (encoder.Pressed() == 0))
{
/** increase our output value and print it */
output_value += 1;
if (output_value > 7)
{
output_value = 0;
}
hw.PrintLine("Output Value:\t%d", output_value);
}
else if((increment < 0) && (encoder.Pressed() == 0))
{
/** decrease our output value and print it */
output_value -= 1;
if (output_value < 0)
{
output_value = 7;
}
hw.PrintLine("Output Value:\t%d", output_value);
}
//Debounce the button
button.Debounce();
//If the button is pressed, turn the LED on
hw.SetLed(button.Pressed());
if(button.RisingEdge()){
if (beat[output_value] == 1)
{
beat[output_value] = 0;
}
else
{
beat[output_value] = 1;
}
}
/* if (encoder.FallingEdge())
{
if (beat[output_value] == 1)
{
beat[output_value] = 0;
}
else
{
beat[output_value] = 1;
}
}
*/
if (beat[output_value] == 1)
{
dsy_gpio_write(&ledB_1_pin, false);
}
else
{
dsy_gpio_write(&ledB_1_pin, true);
}
if((encoder.Pressed()) && (increment > 0))
{
/** increase our output value and print it */
seqP[output_value] += 1;
hw.PrintLine("Pitch:\t%d", seqP[output_value]);
}
else if((encoder.Pressed()) && (increment < 0))
{
/** decrease our output value and print it */
seqP[output_value] -= 1;
hw.PrintLine("Pitch:\t%d", seqP[output_value]);
}
// Read touch status
currTouched = mpr121.Touched();
// Process the touch status as needed
for(int i = 0; i < 12; i++)
{
if((currTouched & (1 << i)) && !(lastTouched& (1 << i)))
{
// Channel i is touched, print the channel number
hw.PrintLine("Channel %d touched", i);
keyF = (scale[0][i] * .5);
trig = 1;
//hw.SetLed(true);
dsy_gpio_write(&ledR_1_pin, false );
dsy_gpio_write(&ledG_1_pin, false );
}
if(!(currTouched & (1 << i)) && (lastTouched& (1 << i)))
{
// Channel i is released, print the channel number
hw.PrintLine("Channel %d released", i);
trig = 0;
//#hw.SetLed(false);
dsy_gpio_write(&ledR_1_pin, true );
dsy_gpio_write(&ledG_1_pin, true );
}
}
lastTouched = currTouched;
//Sequencer
if (trig != trigo){
step=7;
cnt=0;
clock.Reset();}
if (trig > 0){
clock.SetFreq(hw.adc.GetFloat(5) * 240);
if (clock.Process()){
if(cnt == 0) {
gate = 1;
if (step == 7){
step=0;
}
else{
step++;
}
} else if(cnt == 3) {
gate = 0;
}
if (cnt < 4) {
cnt++;
} else {
cnt = 0;
}
}
trigo=trig;
}
if (hw.adc.GetFloat(6) > 0)
{
gate = beat[step] * gate;
seqF = mtof(seqP[step]*4);
}
else
{
gate = 1;
seqF = 0;
}
float lfoF = hw.adc.GetMuxFloat(9, 0);
lfo.SetFreq(lfoF * lfoF * 99.99 + .01);
float Cutfone=0;
//osc.SetFreq(finalF * 1200 + 45);
float Menv = envM.Process(trig && gate);
float peak = hw.adc.GetMuxFloat(10, 5) * Menv;
float Cut = hw.adc.GetMuxFloat(10, 4);
float lfoamt = lfo.Process() * hw.adc.GetMuxFloat(9, 1) * 4000;
filt.SetFreq((Cut * Cut + peak) * 4500 + 45 + lfoamt);
filt.SetRes(hw.adc.GetMuxFloat(10, 6) * 1.1f);
float baseF= (mtof(hw.adc.GetFloat(0) * 84 - 24));
float lfopamt = lfo.Process() * hw.adc.GetMuxFloat(9, 2);
float Menvpamt = Menv * hw.adc.GetMuxFloat(9, 7);
float lfopwamt(lfo.Process() * hw.adc.GetFloat(7));
osc.SetFreq((Menvpamt * Menvpamt * 4000) + (lfopamt * lfopamt * 1000) + keyF + baseF + seqF);
osc.SetWaveform(hw.adc.GetMuxFloat(10, 7) * 7 );
osc.SetPw(hw.adc.GetFloat(8)+lfopwamt);
clock.SetFreq(4 * (hw.adc.GetFloat(6) * 1.9f + .1f));
//Set envelope parameters
env.SetTime(ADSR_SEG_ATTACK, hw.adc.GetMuxFloat(10, 0));
float dec = hw.adc.GetMuxFloat(10, 1);
env.SetTime(ADSR_SEG_DECAY, dec * dec);
env.SetTime(ADSR_SEG_RELEASE, hw.adc.GetMuxFloat(10, 3));
env.SetSustainLevel(trig * hw.adc.GetMuxFloat(10, 2));
//Set Mod envelope parameters
envM.SetTime(ADSR_SEG_ATTACK, hw.adc.GetMuxFloat(9, 3));
float decM = hw.adc.GetMuxFloat(9, 4);
envM.SetTime(ADSR_SEG_DECAY, decM * decM);
envM.SetTime(ADSR_SEG_RELEASE, hw.adc.GetMuxFloat(9, 6));
envM.SetSustainLevel(trig * hw.adc.GetMuxFloat(9, 5));
//OlED Stuff
/*fonepole(Cutfone, Cut, .000001);
int newcut = Cutfone * 10000000;
if (newcut != ctfO){
display.SetCursor(40, 2);
display.WriteString("Cutoff", Font_6x8, true);
display.SetCursor(50, 22);
/
FixedCapStr<16> str("");
str.AppendInt(newcut);
display.WriteString(str, Font_11x18, true);
display.Update();
}
int newdec = decM * 100;
if (newdec != decO){
display.SetCursor(4, 2);
display.WriteString("Decay", Font_6x8, true);
display.SetCursor(14, 22);
FixedCapStr<16> str1("");
str1.AppendInt(newdec);
display.WriteString(str1, Font_11x18, true);
display.Update();
}*/
for(int x = 0; x < 4; x++)
{
if(encoder.FallingEdge() || encoder.Increment() || button.FallingEdge())
{
display.Fill(false);
display.SetCursor(25, 2);
display.WriteString("Pitch & Gate", Font_7x10, true);
display.DrawCircle(10, 53, beat[0] *4+1, true);
display.DrawCircle(25, 53, beat[1] *4+1, true);
display.DrawCircle(40, 53, beat[2] *4+1, true);
display.DrawCircle(55, 53, beat[3] *4+1, true);
display.DrawCircle(70, 53, beat[4] *4+1, true);
display.DrawCircle(85, 53, beat[5] *4+1, true);
display.DrawCircle(100, 53, beat[6] *4+1, true);
display.DrawCircle(115, 53, beat[7] *4+1, true);
display.DrawLine(output_value*15 + 4, 63, output_value*15+16, 63, true);
display.DrawArc(10, 32, 6, 90, seqP[0]*-10, true);
display.DrawArc(25, 32, 6, 90, seqP[1]*-10, true);
display.DrawArc(40, 32, 6, 90, seqP[2]*-10, true);
display.DrawArc(55, 32, 6, 90, seqP[3]*-10, true);
display.DrawArc(70, 32, 6, 90, seqP[4]*-10, true);
display.DrawArc(85, 32, 6, 90, seqP[5]*-10, true);
display.DrawArc(100, 32, 6, 90, seqP[6]*-10, true);
display.DrawArc(115, 32, 6, 90, seqP[7]*-10, true);
display.Update();
}
}
//decO = newdec;
//ctfO = newcut;
//System :: Delay(50);
}
}