DIY Arpeggiator

Some years ago I create two nice modular synths, but one thing what was really missing was an arpeggiator. Normally I used the sequencer of my drum machine to play the modular synths but with an arp you will have much more variance in your sound.  The last weeks I found the time to create such an Arpeggiator and will share it now with you.

 

The arp is based on an arduino nano an SSD1306 OLED Display and MCP 4725 DAC.

 

Function Description:

 

Parameters:

  • Tempo - adjustable on all arp modes
  • Note - Pitch
  • Gate - length of step - adjustable on all arp modes
  • Step - 1 - 8 adjustable on all arp modes

Buttons and Potentiometer:

There are two buttons, with Button1 you can step through the Modes (Up, Up/Down ... ) and go through the sequence step by step. With Button2 you can go through the Parameters.

 

Arp Modes:

"Up", "Down",  "UpDown", "Random Step", "Random Sequence"

 

With the adjustable parameters and the different arp mode you can set a lot of sound variation. 

As Input is a Gate signal needed and the output is a simple Controlled Voltage Out (CV by MCP4725 Dac). The sequence starts with the gate signal will be start new with next gate signal, independent from the sequence status.

 

Please be aware, there is no Gate Output on the Frontpanel, I route it synth internal to an 4 fold distributer panel.

scematic

  • red = 5V
  • blue = GND
  • yellow = IC2 Bus
  • black = Signals/Input/Output

Code

 

 #include <Wire.h>

#include <Adafruit_GFX.h>

#include <Adafruit_SSD1306.h>

#include <Adafruit_MCP4725.h>

 

#define SCREEN_WIDTH 128

#define SCREEN_HEIGHT 64

#define OLED_RESET -1

#define SSD1306_ADDRESS 0x3C

 

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

Adafruit_MCP4725 dac;

 

// 'Minart Modular final', 128x64px

const unsigned char image [] PROGMEM = {

            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 

            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x80, 

            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, 

            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 

            0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x80, 0x10, 0x00, 0x10, 0x88, 0x80, 0x08, 0xc4, 0x13, 

            0x00, 0xff, 0xfc, 0xff, 0xef, 0xfe, 0xff, 0xff, 0xf7, 0xbc, 0xff, 0xf9, 0xbf, 0xff, 0x9f, 0x00, 

            0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 

            0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x00, 

            0x01, 0xc3, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc7, 0x00, 

            0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 

            0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 

            0x00, 0xc0, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 

            0x01, 0xc0, 0x40, 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x02, 0x00, 0x20, 0x07, 0x00, 

            0x02, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 

            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 

            0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0xce, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 

            0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x00, 

            0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x63, 0xe9, 0x14, 0x40, 0x00, 0x00, 0x07, 0x00, 

            0x00, 0x80, 0x00, 0x00, 0x07, 0xff, 0xff, 0x83, 0x83, 0xfe, 0xfe, 0xc0, 0x00, 0x00, 0x06, 0x00, 

            0x00, 0xc0, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x03, 0x00, 

            0x01, 0xc0, 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3b, 0x07, 0x00, 

            0x00, 0xc0, 0x3f, 0x04, 0x0c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x03, 0x07, 0x00, 

            0x00, 0xc0, 0xfd, 0xde, 0x3f, 0x00, 0x03, 0x0e, 0x01, 0xf8, 0x07, 0x08, 0x3f, 0xfe, 0x05, 0x00, 

            0x00, 0xc1, 0x87, 0xff, 0xff, 0x87, 0x0f, 0xff, 0x83, 0xff, 0x1f, 0xbe, 0x7f, 0xf8, 0x07, 0x00, 

            0x00, 0xc1, 0xf3, 0xc7, 0xc7, 0x9f, 0x87, 0xef, 0xc7, 0x3f, 0x87, 0xde, 0x1e, 0x00, 0x07, 0x00, 

            0x00, 0xc0, 0xe3, 0xc7, 0xc7, 0x87, 0x87, 0xc3, 0xc0, 0x07, 0x87, 0x80, 0x0e, 0x00, 0x03, 0x00, 

            0x00, 0xc0, 0x03, 0xc7, 0xc7, 0x87, 0x87, 0xc7, 0xc0, 0x07, 0x87, 0xc0, 0x1f, 0x00, 0x07, 0x00, 

            0x01, 0xc0, 0x02, 0xc7, 0xc7, 0x87, 0x87, 0xc3, 0xc1, 0xfd, 0x87, 0xc0, 0x1f, 0x00, 0x06, 0x00, 

            0x00, 0xc0, 0x03, 0xc7, 0xc7, 0x87, 0x83, 0xc3, 0xc7, 0x87, 0x87, 0x80, 0x1f, 0x00, 0x07, 0x00, 

            0x01, 0xc0, 0x03, 0xc7, 0xc7, 0x87, 0x83, 0xc7, 0xcf, 0x07, 0x87, 0x80, 0x0f, 0x00, 0x07, 0x00, 

            0x00, 0xc0, 0x03, 0x47, 0xc7, 0x87, 0x87, 0xc3, 0xcf, 0x87, 0x87, 0x80, 0x1e, 0x30, 0x07, 0x00, 

            0x00, 0xc0, 0x07, 0xe7, 0xce, 0xcf, 0xc7, 0xc7, 0xef, 0xff, 0xe7, 0xc0, 0x3f, 0xe0, 0x06, 0x00, 

            0x00, 0x00, 0x03, 0xc3, 0x07, 0x87, 0xc7, 0xc3, 0xc1, 0xe3, 0xc3, 0xc0, 0x0f, 0x80, 0x42, 0x00, 

            0x01, 0x00, 0x00, 0x80, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 

            0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 

            0x01, 0xc0, 0x00, 0x31, 0x8e, 0x01, 0x01, 0x60, 0x00, 0x06, 0x02, 0x03, 0x10, 0x00, 0x07, 0x00, 

            0x00, 0xc0, 0x00, 0xff, 0xff, 0x87, 0xc7, 0xfc, 0xe3, 0x8e, 0x0f, 0xc7, 0xfc, 0x00, 0x07, 0x00, 

            0x00, 0xe0, 0x00, 0x39, 0xc7, 0x39, 0xe3, 0x9c, 0xf3, 0x87, 0x19, 0xe3, 0x98, 0x00, 0x07, 0x00, 

            0x00, 0xc0, 0x00, 0x39, 0xe7, 0x30, 0xe3, 0x9c, 0x73, 0x87, 0x00, 0xe3, 0x80, 0x00, 0x06, 0x00, 

            0x00, 0xc0, 0x00, 0x39, 0xe7, 0x38, 0xe3, 0x9c, 0x73, 0x8e, 0x07, 0xe3, 0x80, 0x00, 0x07, 0x01, 

            0x00, 0xc0, 0x00, 0x39, 0xe7, 0x38, 0xe3, 0x9c, 0x73, 0x8e, 0x38, 0xe3, 0x80, 0x00, 0x07, 0x00, 

            0x00, 0xc0, 0x00, 0x39, 0xe7, 0x3c, 0xe3, 0x9c, 0x7f, 0x8e, 0x38, 0xe3, 0x80, 0x00, 0x06, 0x00, 

            0x00, 0x80, 0x00, 0x7d, 0xef, 0x9f, 0x8e, 0xf3, 0xff, 0xff, 0xbf, 0xf7, 0xe0, 0x00, 0x07, 0x00, 

            0x00, 0xc0, 0x00, 0x10, 0x01, 0x00, 0x00, 0x80, 0x20, 0x82, 0x08, 0x41, 0x80, 0x00, 0x06, 0x00, 

            0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 

            0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 

            0x00, 0xc0, 0x00, 0x00, 0x07, 0xff, 0xff, 0xc7, 0xc7, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x07, 0x00, 

            0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 

            0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe7, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 

            0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 

            0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 

            0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 

            0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 

            0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 

            0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 

            0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x07, 0x00, 

            0x08, 0xc3, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc7, 0x00, 

            0x00, 0xc3, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x87, 0x00, 

            0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 

            0x02, 0xfc, 0x48, 0x71, 0xb7, 0xfd, 0x42, 0x3d, 0x56, 0xa2, 0x9e, 0x5a, 0x9f, 0x87, 0xcf, 0x00, 

            0x00, 0x7f, 0xff, 0xff, 0xfa, 0xff, 0xfe, 0xff, 0xff, 0x9b, 0xff, 0xff, 0xff, 0x9f, 0xfe, 0x00, 

            0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 

            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 

            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x20

};

 

#define TASTER1_PIN 3

#define TASTER3_PIN 5

#define POTI_PIN A0

#define GATE_INPUT_PIN 2

#define GATE_OUTPUT_PIN 6

 

// Globale Variablen

struct Step {

  int note;      // Notenhöhe

  int gateWidth; // Gate-Breite

};

 

Step sequence[8]; // Sequenz mit 8 festen Steps

int sequenceLength = 8;

int currentStep = 0; // Aktueller bearbeiteter Step

int tempo = 30; // Tempo in BPM

unsigned long lastStepTime = 0; // Zeit des letzten Steps

bool sequenceRunning = false; // Sequenz aktiv/inaktiv

bool lastGateState = false; // Zustand des letzten Gate-Signals

bool sequenceRestart = false; // Flag zum Neustarten der Sequenz bei neuem Gate

unsigned long lastPotUpdate = 0; // Zeit des letzten Potentiometer-Updates

const unsigned long potUpdateInterval = 200; // Mindestzeit zwischen Potentiometer-Updates (ms)

int parameterMode = 0; // 0: Tempo, 1: Note, 2: Gate

int lastPotValue = -1; // Initial auf -1 setzen, da es keinen gültigen Potentiometer-Wert gibt

const int potThreshold = 1; // Mindeständerung des Potentiometer-Werts

 

volatile bool gateTriggered = false; // Interrupt-gesteuertes Gate-Flag

bool randomEnabled = false; // Flag, ob Random-Modus aktiv ist

 

// Neue Modi

enum SequenceMode {

  //FREE_MODE,

  UP_MODE,

  DOWN_MODE,

  UP_DOWN_MODE,

  RANDOM_STEP_MODE,

  RANDOM_SEQ_MODE

};

SequenceMode currentMode = UP_MODE; // Standardmodus

 

// Liste harmonischer Noten 

const int harmonicNotes[] = {48, 50, 52, 53, 55, 57, 59, 60}; // MIDI-Noten für C-Dur

const int harmonicNoteCount = sizeof(harmonicNotes) / sizeof(harmonicNotes[0]);

 

void setup() {

  // Serial Monitor zur Debug-Ausgabe

  Serial.begin(9600);

 

  // Display initialisieren

  if (!display.begin(SSD1306_ADDRESS, 0x3C)) {

    while (true); // Anhalten, wenn das Display nicht initialisiert werden kann

  }

 

  display.clearDisplay();

  display.setTextSize(1);

  display.setTextColor(SSD1306_WHITE);

 

  // 1. Bild anzeigen

  display.drawBitmap(0, 0, image, 128, 64, WHITE);

  display.display();

  delay(2000);

 

  

  // Text anzeigen

  display.clearDisplay(); // Anzeige aktualisieren

  display.setCursor(0, 10);

  display.print(F("Arpeggiator"));

  display.setCursor(0, 20); // Weitere 10 Pixel nach unten

  display.print(F("Version: arp_3.6"));

  display.display(); // Anzeige aktualisieren

 

  delay(2000); // Begrüßungstext für 2 Sekunden anzeigen

 

  // DAC initialisieren

  if (!dac.begin(0x60)) {

    while (true); // Anhalten, wenn der DAC nicht initialisiert werden kann

  }

 

  // Pins konfigurieren

  pinMode(TASTER1_PIN, INPUT_PULLUP);

  pinMode(TASTER3_PIN, INPUT_PULLUP);

  pinMode(GATE_INPUT_PIN, INPUT);

  pinMode(GATE_OUTPUT_PIN, OUTPUT);

  digitalWrite(GATE_OUTPUT_PIN, LOW);

 

  // Interrupt für das Gate-Signal

  attachInterrupt(digitalPinToInterrupt(GATE_INPUT_PIN), handleGateInterrupt, RISING);

 

  // Sequenz initialisieren mit mitteltiefer Acid-Bassline

  sequence[0] = {58, 220}; 

  sequence[1] = {42, 170};  

  sequence[2] = {53, 100}; 

  sequence[3] = {46, 90}; 

  sequence[4] = {57, 170};  

  sequence[5] = {46, 120}; 

  sequence[6] = {42, 90};  

  sequence[7] = {43, 160}; 

 

  drawSequence();

}

 

void handleGateInterrupt() {

  gateTriggered = true;

  if (currentMode == UP_DOWN_MODE) {

    sequenceRunning = true;

  }

}

 

void loop() {

  bool taster1 = digitalRead(TASTER1_PIN) == LOW;

  bool taster3 = digitalRead(TASTER3_PIN) == LOW;

 

  // Gate-Signal starten und Sequenz neu starten, wenn nötig

  if (gateTriggered) {

    gateTriggered = false;

    sequenceRunning = true;

    sequenceRestart = true; // Signal, dass die Sequenz neu gestartet werden soll

    lastStepTime = millis();

  }

 

  // Navigation zwischen Steps oder Modi umschalten

  if (taster1 && parameterMode == 0) {

    currentMode = static_cast<SequenceMode>((currentMode + 1) % 5); // Modi durchschalten

    drawSequence();

    delay(200); // Entprellen

  } else if (taster1) {

    currentStep = (currentStep + 1) % sequenceLength;

    drawSequence();

    delay(200); // Entprellen

  }

 

  // Wechsel zwischen Parameter-Modi: Tempo, Note, Gate

  if (taster3) {

    parameterMode = (parameterMode + 1) % 3; // Umschalten zwischen 0, 1, 2

    drawSequence();

    delay(200); // Entprellen

  }

 

  // Potentiometer-Werte abhängig vom Modus anpassen

  unsigned long currentTime = millis();

  if (currentTime - lastPotUpdate >= potUpdateInterval) {

    int potValue = analogRead(POTI_PIN);

 

    // Prüfen, ob sich der Potentiometer-Wert signifikant geändert hat

    if (lastPotValue == -1 || abs(potValue - lastPotValue) > potThreshold) {

      lastPotValue = potValue; // Aktuellen Wert als Referenz speichern

 

      if (parameterMode == 0) {

        tempo = map(potValue, 0, 1023, 10, 60); // Tempo anpassen 

      } else if (parameterMode == 1) {

        sequence[currentStep].note = map(potValue, 0, 1023, 36, 62); // Notenhöhe anpassen (MIDI 36–84)

      } else if (parameterMode == 2) {

        sequence[currentStep].gateWidth = map(potValue, 0, 1023, 50, 240); // Gate-Breite anpassen

      }

 

      drawSequence(); // Änderungen im Display anzeigen

    } else {

      // Display aktualisieren, um andere Statusinformationen anzuzeigen

      drawSequence();

    }

 

    lastPotUpdate = currentTime; // Zeit des letzten Updates speichern

  }

 

  // Sequenz abspielen, wenn aktiv

  if (sequenceRunning) {

    playSequence();

  }

}

 

void drawSequence() {

  display.clearDisplay();

 

  int stepWidth = SCREEN_WIDTH / sequenceLength;

  for (int i = 0; i < sequenceLength; i++) {

    int x = i * stepWidth;

    int barHeight = map(sequence[i].note, 36, 84, 0, SCREEN_HEIGHT);

    int barWidth = map(sequence[i].gateWidth, 50, 240, 2, stepWidth);

 

    if (i == currentStep) {

      display.fillRect(x, SCREEN_HEIGHT - barHeight, barWidth, barHeight, SSD1306_WHITE);

    } else {

      display.drawRect(x, SCREEN_HEIGHT - barHeight, barWidth, barHeight, SSD1306_WHITE);

    }

  }

 

  // Aktueller Modus und Parameter anzeigen

  display.setCursor(0, 0);

  display.setTextSize(2); // Setze größere Textgröße (z.B. 2)

  display.setTextColor(SSD1306_WHITE);

 

  if (parameterMode == 0) {

    display.print(F("Tempo: "));

    display.print(tempo);

    display.setCursor(0, 18); // Unterhalb von Tempo anzeigen

    display.setTextSize(1);

    switch (currentMode) {

    //  case FREE_MODE: display.print(F("Mode: FREE")); break;

      case RANDOM_SEQ_MODE: display.print(F("RANDOM SEQ")); break;

      case UP_MODE: display.print(F("UP")); break;

      case DOWN_MODE: display.print(F("DOWN")); break;

      case UP_DOWN_MODE: display.print(F("UP/DOWN")); break;

      case RANDOM_STEP_MODE: display.print(F("RANDOM STEP")); break;

    }

  } else if (parameterMode == 1) {

    display.setTextSize(2); 

    display.print(F("Note: "));

    display.setCursor(60, 0);

    display.print(sequence[currentStep].note);

  } else if (parameterMode == 2) {

    display.setTextSize(2); 

    display.print(F("Gate: "));

    display.setCursor(60, 0); 

    display.print(sequence[currentStep].gateWidth);

  }

  display.setTextSize(1);

  display.display();

}

 

void playSequence() {

  unsigned long stepInterval = 60000 / tempo / sequenceLength; // Schrittintervall basierend auf Tempo

  static bool upDirection = true; // Für den UP_DOWN_MODE

  static int stepsRemaining = 0; // Schritte bis zum Stopp im UP_DOWN_MODE

 

  if (sequenceRestart) {

    currentStep = (currentMode == DOWN_MODE) ? sequenceLength - 1 : 0; // Startpunkt abhängig vom Modus

    sequenceRestart = false;

    stepsRemaining = (currentMode == UP_DOWN_MODE) ? 2 * sequenceLength - 2 : 0; // Hin- und Rücklauf initialisieren

 

    if (currentMode == RANDOM_SEQ_MODE) { // Random-Modus aktiv

      for (int i = 0; i < sequenceLength; i++) {

        sequence[i].note = harmonicNotes[random(0, harmonicNoteCount)]; // Zufällige Note aus der Liste

        sequence[i].gateWidth = random(50, 240); // Zufällige Gate-Breite im Bereich 50–150 ms

      }

    }

  }

 

  if (millis() - lastStepTime >= stepInterval) {

    lastStepTime = millis();

 

    Step step = sequence[currentStep];

 

    if (step.note != 36) { // Nur Gate ausgeben, wenn Note nicht 36 ist

      int dacValue = map(step.note, 36, 84, 0, 4095);

      dac.setVoltage(dacValue, false);

 

      digitalWrite(GATE_OUTPUT_PIN, HIGH);

      delay(step.gateWidth); // Gate an für die eingestellte Dauer

      digitalWrite(GATE_OUTPUT_PIN, LOW);

    }

 

    // UP_MODE logic

    if (currentMode == UP_MODE) {

      currentStep++;

      if (currentStep >= sequenceLength) {

        sequenceRunning = false; // Sequenz beenden nach 8 Schritten

        currentStep = 0;

      }

 

    // DOWN_MODE logic

    } else if (currentMode == DOWN_MODE) {

      currentStep--;

      if (currentStep < 0) {

        sequenceRunning = false; // Sequenz beenden nach 8 Schritten

        currentStep = sequenceLength - 1; // Zurück zum letzten Schritt

      }

 

        // UP_DOWN_MODE logic

    } else if (currentMode == UP_DOWN_MODE) {

      if (stepsRemaining > 0) {

        if (gateTriggered) {

          gateTriggered = false; // Reagiere sofort auf ein neues Gate

          upDirection = !upDirection; // Richtung wechseln

          // Schritte basierend auf aktueller Position neu berechnen

          stepsRemaining = upDirection

            ? (sequenceLength - 1 - currentStep) + currentStep

            : currentStep + (sequenceLength - 1 - currentStep);

        }

 

        if (upDirection) {

          if (currentStep < sequenceLength - 1) {

            currentStep++;

          } else {

            upDirection = false; // Richtung wechseln am Ende

            currentStep--;       // Direkt zum vorherigen Schritt

          }

        } else {

          if (currentStep > 0) {

            currentStep--;

          } else {

            upDirection = true; // Richtung wechseln am Anfang

            currentStep++;      // Direkt zum nächsten Schritt

          }

        }

        stepsRemaining--;

      }

 

      if (stepsRemaining <= 0) {

        if (!gateTriggered) {

          sequenceRunning = false; // Sequenz endgültig stoppen, wenn kein Gate kommt

        } else {

          gateTriggered = false; // Neues Gate-Flag zurücksetzen

          stepsRemaining = 2 * sequenceLength - 2; // Neues Hin- und Rücklaufintervall

        }

      }

    // RANDOM_STEP_MODE logic

    } else if (currentMode == RANDOM_STEP_MODE) {

      if (sequenceRestart || stepsRemaining <= 0) {

        sequenceRestart = false;

        stepsRemaining = 8; // Stelle sicher, dass 8 zufällige Schritte ausgeführt werden

      }

 

      if (stepsRemaining > 0) {

        currentStep = random(0, sequenceLength); // Zufälligen Schritt auswählen

 

        Step step = sequence[currentStep];

        if (step.note != 36) { // Nur Gate ausgeben, wenn Note nicht 36 ist

          int dacValue = map(step.note, 36, 84, 0, 4095);

          dac.setVoltage(dacValue, false);

 

          digitalWrite(GATE_OUTPUT_PIN, HIGH);

          //delay(step.gateWidth); 

          //delay(100);// Gate an für die eingestellte Dauer

          digitalWrite(GATE_OUTPUT_PIN, LOW);

        }

 

        stepsRemaining--;

        if (stepsRemaining <= 0) {

          sequenceRunning = false; // Beenden nach genau 8 zufälligen Schritten

      }

    }

    

    // RANDOM_SEQ_MODE logic

    } else if (currentMode == RANDOM_SEQ_MODE) {

      currentStep++;

      if (currentStep >= sequenceLength) {

        sequenceRunning = false; // Sequenz beenden nach 8 Schritten im Random-Modus

        currentStep = 0;         // Zurücksetzen auf den Anfang

      }

    }

  }

}

 

Front Panel