• Uncategorized

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

HAGIWO MOD1用にプログラミングしたドラムシーケンサーです。

以前掲載したデュアルユークリッドシーケンサーの強化版で、スネアに対応した1つ目のシーケンサーをより使いやすいリズムパターンに修正しまし、新たにランダムスネアモードを追加しています。また、BPM 30などスローなBPMにも対応するよう修正しました。

1つ目のシーケンサーは16ステップで固定されており、POT1でリズムパターンを設定します。ノブを回すごとにトリガー回数が増え、最大値で16ステップ全てのトリガーが鳴ります。ステップはシフトレジストしており、1周目は5ステップ目から有効になります。主にスネア音源との相性が良いシーケンサーです。

2つ目のシーケンサーはユークリッドシーケンサーです。こちらも16ステップで固定されており、POT2でトリガー回数を設定します。また、POT3はランダムコイントスで、生成したユークリディアンパターンを、さらに確率的に2つの出力に振り分けます。こちらは1ステップ目から有効です。主にクローズハイハット、オープンハイハット音源の組み合わせと相性が良いシーケンサーです。

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

また、新たにランダムスネアパターンを追加しました。P4ボタンを押すと、0−8パターン目までのスネアパターンが、16ステップごとにランダムに再生されます。

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

注意事項2:リセットインがないため、不規則なクロックには対応していません。BPMが判定しずらいクロックを入れた場合、設定がリセットされる可能性があります。

注意事項3:現在の設定では、安定した動作が保証できるのはBPM30までとなります。さらに遅いテンポで使用したい場合は「triggerTimeout」を任意の数字に修正してください。

[機能紹介]

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

※トリガー長は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

P4:ランダムスネアパターンとPOT1の切り替えボタン

LEDF1に連動する

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

const int triggerInputPin = 17;

const int buttonPin = 4; // D4 に接続

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;

unsigned long lastTriggerReceived = 0;

bool lastTriggerState = LOW;

bool lastButtonState = HIGH;

bool randomMode = false;

int currentRandomPatternIndex = 0;

const unsigned long triggerTimeout = 500;

const int shiftSize = 8;

bool shiftRegister[shiftSize] = {false};

int shiftIndex = 0;

// 固定パターン

const bool fixedPatterns[16][numSteps] = {

{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},

{0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0},

{0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1},

{0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0},

{0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0},

{0,0,0,1,1,0,0,0,0,1,0,0,1,0,0,0},

{1,0,0,1,0,0,1,0,1,0,0,1,0,1,0,0},

{1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1},

{1,0,0,1,0,0,1,0,1,0,0,1,0,1,0,0},

{1,0,1,0,1,1,0,1,0,1,0,1,1,0,1,0},

{1,0,1,1,0,1,0,1,1,0,1,0,1,1,0,1},

{1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,1},

{1,0,1,0,1,0,1,1,0,1,0,1,1,0,1,1},

{1,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1},

{1,1,0,1,1,1,0,1,1,1,0,1,1,1,1,1},

{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}

};

void generateFixedPattern(bool* pattern, int index) {

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

int shifted = (i – 4 + numSteps) % numSteps;

pattern[i] = fixedPatterns[index][shifted];

}

}

void generateEuclideanPattern(bool* pattern, int pulses) {

if (pulses <= 0) {

for (int i = 0; i < numSteps; i++) pattern[i] = false;

} else if (pulses >= numSteps) {

for (int i = 0; i < numSteps; i++) pattern[i] = true;

} else {

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

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

}

}

}

int readKnobIndex(int analogPin) {

int raw = analogRead(analogPin);

int step = map(raw, 0, 1023, 0, 16);

return constrain(step, 0, 15);

}

int readPulseCount(int analogPin) {

int raw = analogRead(analogPin);

int step = map(raw, 0, 1023, 0, 17);

return constrain(step, 0, 16);

}

float readProbability() {

return analogRead(probKnobPin) / 1023.0;

}

void sendTrigger(int pin) {

digitalWrite(pin, HIGH);

delayMicroseconds(5000);

digitalWrite(pin, LOW);

}

void setup() {

pinMode(triggerInputPin, INPUT);

pinMode(buttonPin, INPUT_PULLUP); // D4に接続

pinMode(ledPin, OUTPUT);

pinMode(out1Pin, OUTPUT);

pinMode(out2aPin, OUTPUT);

pinMode(out2bPin, OUTPUT);

randomSeed(analogRead(A3));

generateFixedPattern(pattern1, 0);

generateEuclideanPattern(pattern2, 0);

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;

shiftIndex = 0;

}

lastTriggerReceived = now;

lastClockTime = now;

triggered = true;

}

lastTriggerState = currentTrigger;

return triggered;

}

void updateModeButton() {

bool currentButton = digitalRead(buttonPin);

if (!currentButton && lastButtonState) {

randomMode = !randomMode;

if (randomMode) {

currentRandomPatternIndex = random(0, 9); // 0〜8までのパターン

}

}

lastButtonState = currentButton;

}

void loop() {

updateModeButton();

if (checkClock()) {

digitalWrite(ledPin, HIGH);

delayMicroseconds(1000);

digitalWrite(ledPin, LOW);

int euclidPulses = readPulseCount(knob2Pin);

// ランダムモード時、16ステップごとにパターン変更

if (randomMode) {

if (currentStep == 0) {

currentRandomPatternIndex = random(0, 9); // 0〜8のみに制限

}

generateFixedPattern(pattern1, currentRandomPatternIndex);

} else {

int patternIndex = readKnobIndex(knob1Pin);

generateFixedPattern(pattern1, patternIndex);

}

generateEuclideanPattern(pattern2, euclidPulses);

// 出力1: シフトレジスタによる4ステップ遅延

shiftRegister[shiftIndex] = pattern1[currentStep];

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

if (shiftRegister[delayedIndex]) {

sendTrigger(out1Pin);

}

// 出力2: 分岐

if (pattern2[currentStep]) {

float prob = readProbability();

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

sendTrigger(out2bPin);

} else {

sendTrigger(out2aPin);

}

}

currentStep = (currentStep + 1) % numSteps;

shiftIndex = (shiftIndex + 1) % shiftSize;

}

}