HAGIWO MOD1用にプログラミングしたスネア&ハイハット・シーケンサーです。
サンプラーのスネア・クローズハイハット&オープンハイハットの組み合わせに適したセッティングです。
何度か類似のプログラミングを投稿していますが、こちらがひとまずの完成版になります。
2025年7月20日に渋谷CIRCUS TOKYOで開催された「Patching for Modular」のライブで使ったシーケンサーです。
1つ目のシーケンサー、2つ目のシーケンサーとも、ライブ演奏で使いやすいスネア・ハットパターンをプログラミングしております。
1つ目のシーケンサーはスネアに特化しており、POT1でリズムパターンを設定します。ノブを回すごとにトリガー回数が増え、最大値で16ステップ全てのトリガーが鳴ります。ステップはシフトレジストしており、1周目は5ステップ目から有効になります。主にスネア音源との相性が良いシーケンサーです。
2つ目のシーケンサーも16ステップで固定されており、POT2でトリガー回数を設定します。また、POT3はランダムコイントスで、POT2で設定したパターンを、さらに確率的に2つの出力に振り分けます。こちらは1ステップ目から有効です。主にクローズハイハット、オープンハイハット音源の組み合わせと相性が良いシーケンサーです。
プログラミング内の {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},の箇所が16ステップシーケンスの書き込み部分にあたります。「0」がトリガーなしで「1」がトリガー発生です。数字を書き換えることで、お好みのリズムパターンに変更できます。
また、新たにランダムハイハットパターンを追加しました。P4ボタンを押すと、ハイハットパターンが、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;
const int ledPin = 3;
const int out1Pin = 9;
const int out2aPin = 10;
const int out2bPin = 11;
const int knob1Pin = A0; // 出力1パターン選択
const int knob2Pin = A1; // 出力2パターン選択
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;
// 出力1用固定パターン
const bool fixedPatterns1[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}
};
// 出力2用固定パターン(ランダム対象)
const bool fixedPatterns2[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,1,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0},
{0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0},
{0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0},
{0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,1},
{0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0},
{0,1,0,0,1,0,0,1,0,0,1,0,0,1,1,0},
{0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1},
{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 generateFixedPattern1(bool* pattern, int index) {
for (int i = 0; i < numSteps; i++) {
int shifted = (i – 4 + numSteps) % numSteps;
pattern[i] = fixedPatterns1[index][shifted];
}
}
void generateFixedPattern2(bool* pattern, int index) {
for (int i = 0; i < numSteps; i++) {
pattern[i] = fixedPatterns2[index][i];
}
}
int readKnobIndex(int analogPin) {
int raw = analogRead(analogPin);
int step = map(raw, 0, 1023, 0, 16);
if (step > 15) step = 15;
return step;
}
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);
pinMode(ledPin, OUTPUT);
pinMode(out1Pin, OUTPUT);
pinMode(out2aPin, OUTPUT);
pinMode(out2bPin, OUTPUT);
randomSeed(analogRead(A3));
generateFixedPattern1(pattern1, 0);
generateFixedPattern2(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;
triggered = true;
}
lastTriggerState = currentTrigger;
return triggered;
}
void updateModeButton() {
bool currentButton = digitalRead(buttonPin);
if (!currentButton && lastButtonState) {
randomMode = !randomMode;
if (randomMode) {
currentRandomPatternIndex = random(0, 16);
}
}
lastButtonState = currentButton;
}
void loop() {
updateModeButton();
if (checkClock()) {
digitalWrite(ledPin, HIGH);
delayMicroseconds(1000);
digitalWrite(ledPin, LOW);
int index1 = readKnobIndex(knob1Pin);
int index2 = readKnobIndex(knob2Pin);
generateFixedPattern1(pattern1, index1);
if (randomMode) {
if (currentStep == 0) {
currentRandomPatternIndex = random(0, 16);
}
generateFixedPattern2(pattern2, currentRandomPatternIndex);
} else {
generateFixedPattern2(pattern2, index2);
}
shiftRegister[shiftIndex] = pattern1[currentStep];
int delayedIndex = (shiftIndex + shiftSize – 4) % shiftSize;
if (shiftRegister[delayedIndex]) {
sendTrigger(out1Pin);
}
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;
}
}