• Column

【HAGIWO MOD1】 デュアルユークリッドシーケンサー× ランダムコイントス/プログラムコード[モジュラーシンセ] 

HAGIWO MOD1用にプログラミングしたデュアルユークリッド・シーケンサーです。

主にサンプラーのスネア・クローズハイハット&オープンハイハットの組み合わせに適したセッティングです。

1つ目のシーケンサーは16ステップで固定されており、POT1でトリガー回数を設定します。ステップはシフトレジストしており、1周目は5ステップ目から有効になります。

2つ目のシーケンサーも16ステップで固定されており、POT2でトリガー回数を設定します。また、POT3はランダムコイントスで、生成したユークリィアンパターンを、さらに確率的に2つの出力に振り分けます。こちらは1ステップ目から有効です。

1つ目のシーケンサーをスネアに、2つ目のシーケンサーの出力をCLOSE/OPENハイハットに振り分けることで、有機的でランダムなドラムパターンを生成できます。

注意事項:chatGPTで作成したプロンプトです。プログラミング知識は一切ないため、動作の安定性は保証はできません。趣味用途でお使いください

[機能紹介]

※入力したクロックをもとに、リズムパターンを生成する

※トリガー長は5msで設定しています。トリガー長は delayMicroseconds(5000); の箇所で変更可能です。(5ms=5000)

POT1:出力1のユークリディアンパターンが使用するトリガー数を設定します。

POT2:出力2のユークリディアンパターンが使用するトリガー数を設定します。

POT3:出力2で設定したユークリディアンパターンを、確率でD10pin、D11pinに振り分けます。中間でおよそ50:50、最小にすると常にD10pinから出力され、最大にすると常にD11pinから出力されます

F1:トリガーIN

F2:出力1のトリガー出力

F3:出力2のトリガー出力1

F4:出力2のトリガー出力2

LEDF1に連動する

[プログラム](ArduinoでMOD1にプログラミングしてください)

// 2-output Euclidean sequencer with probabilistic branching like Mutable Instruments Branches

// Now includes 4-step delay on Output 1 using an 8-bit shift register emulation

const int triggerInputPin = 17;

const int buttonPin = 2;

const int ledPin = 3;

const int out1Pin = 9;

const int out2aPin = 10;

const int out2bPin = 11;

const int knob1Pin = A0;

const int knob2Pin = A1;

const int probKnobPin = A2;

const int numSteps = 16;

bool pattern1[numSteps];

bool pattern2[numSteps];

int currentStep = 0;

unsigned long lastClockTime = 0;

bool internalClock = false;

unsigned long lastTapTime = 0;

unsigned long bpmInterval = 0;

bool lastButtonState = HIGH;

bool lastTriggerState = LOW;

unsigned long lastTriggerReceived = 0;

const unsigned long triggerTimeout = 300;

// — 8-bit shift register for delayed output1 —

const int shiftSize = 8;

bool shiftRegister[shiftSize] = {false};  // stores pattern1 history

int shiftIndex = 0;

// — Functions —

void generateEuclideanPattern(bool* pattern, int pulses) {

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

    pattern[i] = (i * pulses) % numSteps < pulses;

  }

}

void sendTrigger(int pin) {

  digitalWrite(pin, HIGH);

  delayMicroseconds(5000);

  digitalWrite(pin, LOW);

}

int readKnobSteps(int analogPin) {

  int raw = analogRead(analogPin);

  int step = raw / (1024 / (numSteps + 1));

  if (step > numSteps) step = numSteps;

  return step;

}

float readProbability() {

  return analogRead(probKnobPin) / 1023.0;

}

void setup() {

  pinMode(triggerInputPin, INPUT);

  pinMode(buttonPin, INPUT_PULLUP);

  pinMode(ledPin, OUTPUT);

  pinMode(out1Pin, OUTPUT);

  pinMode(out2aPin, OUTPUT);

  pinMode(out2bPin, OUTPUT);

  generateEuclideanPattern(pattern1, 0);

  generateEuclideanPattern(pattern2, 0);

  randomSeed(analogRead(A3));

  lastTriggerState = digitalRead(triggerInputPin);

  lastTriggerReceived = millis();

}

bool checkClock() {

  bool triggered = false;

  bool currentTrigger = digitalRead(triggerInputPin);

  unsigned long now = millis();

  if (currentTrigger && !lastTriggerState) {

    if (now – lastTriggerReceived > triggerTimeout) {

      currentStep = 0;

    }

    lastTriggerReceived = now;

    lastClockTime = now;

    internalClock = false;

    triggered = true;

  }

  lastTriggerState = currentTrigger;

  if (internalClock && bpmInterval > 0 && now – lastClockTime >= bpmInterval) {

    lastClockTime = now;

    triggered = true;

  }

  return triggered;

}

void updateTapTempo() {

  bool currentButton = digitalRead(buttonPin);

  if (!currentButton && lastButtonState) {

    unsigned long now = millis();

    if (lastTapTime > 0) {

      bpmInterval = now – lastTapTime;

      internalClock = true;

    }

    lastTapTime = now;

  }

  lastButtonState = currentButton;

}

void loop() {

  updateTapTempo();

  if (checkClock()) {

    digitalWrite(ledPin, HIGH);

    delayMicroseconds(1000);

    digitalWrite(ledPin, LOW);

    int steps1 = readKnobSteps(knob1Pin);

    int steps2 = readKnobSteps(knob2Pin);

    generateEuclideanPattern(pattern1, steps1);

    generateEuclideanPattern(pattern2, steps2);

    // — Store current pattern1 state into shift register —

    shiftRegister[shiftIndex] = pattern1[currentStep];

    // — Calculate delayed index (4 steps behind current) —

    int delayedIndex = (shiftIndex + shiftSize – 4) % shiftSize;

    if (shiftRegister[delayedIndex]) {

      sendTrigger(out1Pin);  // delayed output

    }

    // — Normal Output 2 with branching —

    if (pattern2[currentStep]) {

      float prob = readProbability();

      if (random(0, 1000) < prob * 1000.0) {

        sendTrigger(out2bPin);

      } else {

        sendTrigger(out2aPin);

      }

    }

    // — Advance step and shift index —

    currentStep = (currentStep + 1) % numSteps;

    shiftIndex = (shiftIndex + 1) % shiftSize;

  }

}