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の切り替えボタン
LED:F1に連動する
[プログラム](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;
}
}