見出し画像

700円で作るGATE BURST/DELAYモジュール-モジュラーシンセ自作

Arduinoプログラミングに挑戦しつつ、モジュラーシンセサイザー GATE BURST/DELAYモジュールを自作したので、その備忘録。

背景
コードの書けないシステムエンジニア脱却のために始めたプログラミングの10作品目。前回、OLEDを使った6CHゲートシーケンサを自作したが、インターフェースの都合上、swing機能を織り込むことができなかった。

有機的なリズムを作るためにswing、すなわちDELAYが欲しいと思い作成に至った。またGATE DELAYだけでは機能が単純すぎるので、グリッチな音を出せるGATE BURSTの機能も同時に実装することにした。モジュラーシンセのシーケンサには、BURST機能を有するものも多いからね。

プログラムもハードもシンプルなので、今回は大した学びは得られない。設計とプログラムは4時間も掛からなかった。

画像1

制作物のスペック
ユーロラック規格 3U 6HPサイズ
電源:40mA ( at 5V ) / 41mA ( at12V )
5V単電源で動作可能。または12V単電源で動作可能。

モード
DELAYモードとBURST(GLITCH)モードの2モードから選択。

DELAYモード
DELAY時間:0ms~1023ms(ツマミで設定、CVアサイン)
パルス幅:1ms~4097ms(ツマミで設定)
TRIGGERを受けると、DELAY時間だけ待機して、指定パルス幅のゲートを出力する。TRIGGER to GATEとしても、GATE to TRIGGERとしても機能する。

BURST(GLITCH)モード
FREQUENCY:5~512ms(ツマミで設定)
TIMES:1~33回(ツマミで設定、CVアサイン)
TRIGGERを受けると、TIMESの指定回数、FREQUENCYの間隔でGATE出力をする。

画像2

プログラム
以前作成したCLOCK MULTI/DIVのTimerのプログラムを流用。1ms毎に割り込みをかけて制御をしている。
特に工夫した点は無い。プログラムの量も少ない。

画像4

ハード
シンプルな入出力回路。特記事項は無い。
これまで制限抵抗を470Ωにしてきたが、消費電流を減らすために1kΩに増やしている。
増やすに当たっては、様々なメーカーの出力回路のカウンターエンジニアリングをした。

市販のGATE DELAY,BURSTモジュールは1万円を切る安価なものが多いが、ソフト的にもハード的にもシンプルだからだと思う。シンプルではあるが、使い方次第では面白い音が出せる。

画像3

ソースコード
粗末ではあるが公開する。
悪いところがあれば、指摘を貰えると学びにつながるのでうれしい。

#include <MsTimer2.h>

//specification
//ディレイ中にゲート入力があった場合、ゲート出力は0のままディレイはリセットされる。
//ワイズタイム中にゲート入力があった場合、ゲート出力は0になりディレイからリスタートする。

//ゲート入出力
unsigned long ms_count = 0;//タイマーのカウント。ゲートINで0になり、カウントを始める
byte gate_in = 0 ;//外部ゲート入力状況。0=LOW,1=HIGH。
byte old_gate_in = 0;//
byte gate_detect_latch = 0;//ゲート出力が完了するまで1のまま。
byte gate_out = 0;//ゲート出力可否。0=LOW,1=HIGH
byte sw_in = 0;//メカswの入力
byte old_sw_in = 0;
byte onoff_in = 1;//onoff入力。内部プルアップのため、1=ON又はジャック未接続,0=OFF,

//モード選択
byte mode = 0;//1=delayモード,0=glitchモード

//delayモード
byte delay_on = 0;//ゲート出力。0=LOW,1=HIGH。ディレイ時間カウント後に1になる。
int delay_knob = 0;//knob1から読み取った値
int delay_CV = 0;//CVから読み取った値
int delay_time = 0;//knobとCVの合算値(計算式は別に記載)
int width_knob = 0;//knob2から読み取った値
int width = 1;//ゲート出力時間

//glitchモード
byte times = 1;//バーストの回数,knobとCVの合算値(計算式は別に記載)
int times_knob = 0;//
int times_CV = 0;//
int freq = 0;//
int time_onoff = 0;//
byte time_count = 1;//出力したら1増える。timesに達したら0に戻り、gate_latchを0にする


void setup() {

 pinMode(2, INPUT);//外部クロックIN
 pinMode(3, INPUT_PULLUP);//mode選択
 pinMode(4, INPUT_PULLUP);//PUSH SW
 pinMode(6, OUTPUT);//GATE OUT
 pinMode(9, INPUT_PULLUP);//ON_OFF GATE IN


 //開発用シリアル通信
 //Serial.begin(9600);

 //Timer用設定
 MsTimer2::set(1, timer_count); // 1ms毎にタイマーカウント
 MsTimer2::start();//外部入力Highになったら、次のHighまでカウント
}

void loop() {
 old_gate_in = gate_in ;//gate0→1を検出する用
 old_sw_in = sw_in;

 //-------------モード選択-----------------
 mode = digitalRead(3);//1=delayモード,0=glitchモード

 //-------------on/off_in判定-----------------
 onoff_in = digitalRead(9);

 if (onoff_in == 1) {

   //--------------------delayモード------------------------
   if ( mode == 1) {
     //delay設定
     delay_knob = analogRead(1);
     delay_CV = analogRead(6);
     delay_time = delay_knob + delay_CV;

     if (delay_time > 1023) {
       delay_time = 1023;
     }

     //width設定
     width_knob = analogRead(2);
     width = width_knob * 4 + 1 ;//1ms~4097msの値をとる

     //外部ゲート入力検出
     gate_in = digitalRead(2);
     sw_in = digitalRead(4);//internal pullupのため0=ON,1=OFF

     if (gate_in == 1 && old_gate_in == 0) {
       gate_detect_latch = 1;
       ms_count = 0;
     }

     else if (sw_in == 0 && old_sw_in == 1) {
       gate_detect_latch = 1;
       ms_count = 0;
     }

     //ゲート出力判定

     if ( gate_detect_latch == 1 && ms_count >= delay_time) {
       gate_out = 1;
     }

     else {
       gate_out = 0;
     }

     if (gate_detect_latch == 1 && ms_count >= delay_time + width) {
       gate_detect_latch = 0;
     }

     //ゲート出力
     if (gate_out == 1 ) {
       digitalWrite(6, HIGH);
     }
     else {
       digitalWrite(6, LOW);
     }
   }
   //--------------------glitchモード------------------------

   if ( mode == 0) {

     //delay設定
     freq = analogRead(1) / 2 + 5; //5~512msの値
     time_onoff = 5 + freq;//ON時間が5ms、OFF時間がFREQ

     //times設定
     times_CV =  analogRead(6);
     times_knob = analogRead(2);
     times = (times_CV + times_knob) / 32 + 1; //1~33の値

     if (times > 33) {
       times = 32;
     }

     //外部ゲート入力検出
     gate_in = digitalRead(2);
     sw_in = digitalRead(4);//internal pullupのため0=ON,1=OFF

     if (gate_in == 1 && old_gate_in == 0) {
       gate_detect_latch = 1;
       time_count = 0;
     }

     else if (sw_in == 0 && old_sw_in == 1) {
       gate_detect_latch = 1;
       time_count = 0;
     }

     //ゲート出力
     if (gate_detect_latch == 1 && time_count <= times) {

       if (ms_count <= time_onoff / 2 ) {
         gate_out = 1;
       }

       else if (ms_count < time_onoff && ms_count > time_onoff / 2) {
         gate_out = 0;
       }

       else if (ms_count >= time_onoff ) {
         ms_count = 0;
         time_count++;
       }
     }
     if (gate_out == 1 ) {
       digitalWrite(6, HIGH);
     }
     else {
       digitalWrite(6, LOW);
     }
   }
 }

 if (onoff_in == 0) {
   digitalWrite(6, LOW);
 }
 // 開発用 serial通信すると、ゲート出力動作が不安定になる。
 //  Serial.print(sw_in);
 //  Serial.print(",");
 //  Serial.print(gate_in * 10);
 //  Serial.println("");
}

//タイマーカウント
void timer_count() {
 ms_count ++;
}

この記事が気に入ったらサポートをしてみませんか?