Added arpeggiato, fixed notes. TODO: Fix menu options for arpeggio. Persistent config/save+load option

This commit is contained in:
superminaren 2026-02-17 15:55:08 +01:00
parent 07917c0e6e
commit e780df7d22
1 changed files with 133 additions and 101 deletions

View File

@ -28,12 +28,19 @@ Adafruit_SSD1306 display(OLED_W, OLED_H, &Wire);
// Modes
int mode = 0;
String mode_names[4] = {"Vol", "Sou", "Oct", "Env"};
String mode_names[7] = { "Vol", "Sou", "Oct", "Env", "Arp", "ArpΔ", "Arp"};
int mode_count = sizeof(mode_names) / sizeof(mode_names[0]);
// Octave
int octave = 2;
// Arpeggio
//int arp_pattern[5] = {1 ,3 ,5 ,3 ,1 };
int arp_pattern[5] = {0 ,4 ,7 ,3 ,1 };
int arpeggio = 1; // 0 = Off, 1 =
int arp_notes = 3; // Now many notes to arpeggiate
int arp_delay = 150; // 300 ms
// ---- Audio ----
//constexpr uint32_t SAMPLE_RATE = 96000;
@ -80,10 +87,13 @@ void initNotes() {
struct Voice {
bool active = false;
float freq = 0;
float startFreq = 0;
float phase1 = 0;
float phase2 = 0;
float env = 0;
float amp = 0;
long arp_time = 0;
int arp_position = 0;
};
Voice voices[MAX_VOICES];
@ -92,8 +102,11 @@ void noteOn(float freq) {
for (auto &v : voices) {
if (!v.active) {
v.active = true;
v.freq = freq*(funcKey ? 1.25f : 1.0f);
v.freq = freq;
v.startFreq = freq;
v.env = VOL;
v.arp_time = millis();
v.arp_position = 0;
v.phase1 = 0.5f;
v.phase2 = 0.5f;
return;
@ -103,7 +116,7 @@ void noteOn(float freq) {
void noteOff(float freq) {
for (auto &v : voices) {
if (v.active && fabs(v.freq - freq) < 0.01f) {
if (v.active && fabs(v.startFreq - freq) < 0.01f) {
v.active = false;
}
}
@ -146,11 +159,16 @@ int genWaveTable(){
}
return 0;
}
float transposeSemitones(float freq, int semitones) {
return freq * powf(2.0f, semitones / 12.0f);
}
/* =========================================================
SYNTH STREAM
========================================================= */
class JunoStream : public Stream {
class SynthStream : public Stream {
public:
uint8_t frame[4];
uint8_t index = 4;
@ -167,7 +185,9 @@ public:
float scope[128]{};
int scopeIndex = 0;
int available() override { return 4; }
int available() override {
return 4;
}
int read() override {
if (index >= 4) {
@ -177,8 +197,12 @@ public:
return frame[index++];
}
int peek() override { return -1; }
size_t write(uint8_t) override { return 0; }
int peek() override {
return -1;
}
size_t write(uint8_t) override {
return 0;
}
private:
void generate() {
@ -188,6 +212,13 @@ private:
if (!v.active) continue;
float osc = 0.0f;
if (arpeggio>0 && (millis()-v.arp_time)>arp_delay){
v.arp_time = millis();
v.arp_position++;
v.arp_position = v.arp_position % arp_notes;
v.freq = transposeSemitones(v.startFreq, arp_pattern[v.arp_position]);
}
// Defined voices
switch (sound) {
// Saw+Sine
@ -228,10 +259,13 @@ private:
mix += osc * v.amp;
}
// Handle reverb
mix = (handleReverb(mix) + mix) / 2.0f;
lp += cutoff * (mix - lp);
float out = lp * 0.4f;
//out = (handleReverb(out)+out)/2.0f;
int r = (delayIndex - 480 + DELAY_SAMPLES) % DELAY_SAMPLES;
float ch = out + delayL[r] * 0.6f;
@ -256,7 +290,7 @@ private:
========================================================= */
I2SStream i2s;
JunoStream synth;
SynthStream synth;
StreamCopy copier(i2s, synth);
/* =========================================================
@ -269,7 +303,7 @@ uint16_t scanMatrix() {
for (int x = 0; x < 4; x++) {
digitalWrite(X_PINS[x], HIGH);
delayMicroseconds(5);
delayMicroseconds(10);
for (int y = 0; y < 4; y++) {
if (digitalRead(Y_PINS[y])) {
@ -311,7 +345,8 @@ void handleInputs(){
if (keys & (1 << 13)) {
switch (mode) {
case 0: // Volume
if(VOL > 0.1f){ VOL-=0.1f; } break;
if (VOL > 0.1f) { VOL -= 0.1f; }
break;
case 1: // Sound
sound = sound - 1;
if (sound < 0) { sound = sound_count - 1; }
@ -396,8 +431,7 @@ void drawScope() {
mid - synth.scope[i1] * scale * height,
x + 1,
mid - synth.scope[i2] * scale * height,
SSD1306_WHITE
);
SSD1306_WHITE);
}
}
@ -432,8 +466,6 @@ void drawInfo(){
display.print(mode_names[mode]);
display.setCursor(90, 18);
display.print("Oct:" + String(octave));
}
void updateOLED(uint16_t keys) {