arduino+mozzi Drum module

#はじめに
ここ数年モジュラーシンセが気になっています。でも、揃えるとなるとスペース取るしお金かかるし、、
なので、arduino+mozziの組み合わせでいくつかシンセ作ったのでそれらを元にユーロラックに収まるような
形の物を作ってみようかと思います
※モジュール単体で音が鳴るのは厳密にはセミモジュラーというそうです





#ドラムマシーン
モジュラーはGATE信号というのを受けて動作させたりするらしいです。GATE信号はパルスなので
デジタルピンで受けた時に一回音が鳴るというのを作れたらと思います
また、このサイトによるとドラムの音は任意の時間（attack time）をかけて音量を上げてそこから
また別の任意の時間（decay time）をかけて音量を０にすればよいみたい。

さらにattack time+decay timeの間に周波数も下げていかないといけないようです。。

mozziにはADSR関数が用意されているので音量の上げ下げ（envelope）は簡単にできるのですが、、

周波数下げていくのをどうしたものか・・・と悩んでいたのですが、ハッともう一個envelope用意すればいいんじゃ？と
思ってやってみたらうまくいきました


#コード
何か制御を受けて音を鳴らすようにするには、ADSRのnoteONを使います。
※EADも用意されていますが、noteOnがありません。
　アタックとディケイだけ使いたいので、SとRは０にしています
実装は単純で、GATE信号を受けるピンがHIGHになったときにnoteOnが走るようにしているだけです。


```java:moduler_drum.ino
/*  Example applying an ADSR envelope to an audio signal
    with Mozzi sonification library.  This shows
    internal updates as well as interpolation at CONTROL_RATE, using both
    update() and next()  in updateControl().
    This is less computationally intensive than doing interpolation with
    next() in updateAudio(), but can be less smooth, especially with fast envelopes.

    Demonstrates a simple ADSR object being controlled with
    noteOn() and noteOff() instructions.

    Tim Barrass 2013-14, CC by-nc-sa.
*/

#include <MozziGuts.h>
#include <Oscil.h>
#include <EventDelay.h>
#include <ADSR.h>
#include <tables/sin2048_int8.h> // sine table for oscillator
#include <tables/saw2048_int8.h>//saw table for oscillator
#include <tables/square_analogue512_int8.h> // square table for oscillator
#include <tables/triangle2048_int8.h>//triangle table for oscillator
#include <tables/noise_static_1_16384_int8.h>//noise table for oscillator
#define CONTROL_RATE 150 // faster than usual to help smooth CONTROL_RATE adsr interpolation (next())

Oscil <SIN2048_NUM_CELLS, AUDIO_RATE> aSin1(SIN2048_DATA);
Oscil <SAW2048_NUM_CELLS, AUDIO_RATE> aSaw1(SAW2048_DATA);
Oscil <SQUARE_ANALOGUE512_NUM_CELLS, AUDIO_RATE> aSqu1(SQUARE_ANALOGUE512_DATA);
Oscil <TRIANGLE2048_NUM_CELLS, AUDIO_RATE> aTri1(TRIANGLE2048_DATA);
Oscil <NOISE_STATIC_1_16384_NUM_CELLS, AUDIO_RATE> aNoise1(NOISE_STATIC_1_16384_DATA);

Oscil <SIN2048_NUM_CELLS, AUDIO_RATE> aSin2(SIN2048_DATA);
Oscil <TRIANGLE2048_NUM_CELLS, AUDIO_RATE> aTri2(TRIANGLE2048_DATA);
Oscil <NOISE_STATIC_1_16384_NUM_CELLS, AUDIO_RATE> aNoise2(NOISE_STATIC_1_16384_DATA);




EventDelay noteDelay;
EventDelay kDelay;
ADSR <CONTROL_RATE, CONTROL_RATE> envelope;
ADSR <CONTROL_RATE, CONTROL_RATE> Fenvelope;

const int gate=2;
const int gate_led=3;

byte gain;
byte Fgain;
int TYPE,F1,F2,A,D;
float gate_frg=0;




void setup(){
  Serial.begin(115200);
  pinMode(gate,INPUT);
  pinMode(gate_led,OUTPUT);
  Fenvelope.setADLevels(255,0);　　　//周波数用のエンベロープの設定①
  noteDelay.set(2000); // 2 second countdown
  startMozzi(CONTROL_RATE);
}


unsigned int  a, d, s, r;

void updateControl(){
    int type=mozziAnalogRead(1);    
    a=mozziAnalogRead(2);
    d=mozziAnalogRead(4);
    int dl=mozziAnalogRead(5);
    byte attack_level = 255;
    decay_level=map(dl,2,1020,15,255);
    TYPE=map(type,2,1020,0,12);
    A=map(a,2,1020,5,150);
    D=map(d,2,1020,5,150);

    envelope.setADLevels(attack_level,decay_level);
    envelope.setTimes(A,D,0,0);
    Fenvelope.setTimes(0,A+D,0,0);//周波数用エンベロープの設定②
    digitalWrite(gate_led,digitalRead(gate));

  if(digitalRead(gate)==HIGH){　//GATEを受けた時にエンベロープが開始されるようにしています
    envelope.noteOn();
    Fenvelope.noteOn();    
    kDelay.start(A+D+2);　　　　　//エンベロープの時間が終わるまで待ちます
          
  }  
    int f2=mozziAnalogRead(3);
    int f1=mozziAnalogRead(0);
    F1=map(f1,2,1020,50,500);
    F2=map(f2,2,1020,50,1500);

    F1=int(F1*gate_frg);
    F2=int(F2*gate_frg);
    
    aSin1.setFreq(F1);
    aSaw1.setFreq(F1);
    aSqu1.setFreq(F1);
    aTri1.setFreq(F1);
    aNoise1.setFreq(F1);

    aSin2.setFreq(F2);
    aTri2.setFreq(F2);
    aNoise2.setFreq(F2);

    envelope.update();
    gain=envelope.next();

    Fenvelope.update();　　　　　　
    Fgain=Fenvelope.next();


    gate_frg=map(Fgain,0,255,100,0);//周波数用のエンベロープは設定①、②によりattack+decayの時間をかけて255→0になるので
    gate_frg=gate_frg/100;　　　　　 //map関数で100→0になるようにして使いやすいように100で割ってます

}



//下記の周波数の加算ですが、完全に好みの問題です。。通常だとドラムだったら正弦波をつかうらしいですが
//ノイジーなのが今の気分なので　

int updateAudio(){
  int aSig;
  int aSig1;
  int aSig2;
  int Sig;
  switch(TYPE){
    case 0:
      aSig1=aSin1.next();
      aSig2=aSin2.next();
      aSig=aSig1+aSig2;
      break;
    case 1:
      aSig1=aSin1.next();
      aSig2=aTri2.next();
      aSig=aSig1+aSig2;
      break;
    case 2:
      aSig1=aSin1.next();
      aSig2=aNoise2.next();
      aSig=aSig1+aSig2;
      break;
    case 3:
      aSig1=aSaw1.next();
      aSig2=aSin2.next();
      aSig=aSig1*aSig2/4;
      break;

    case 4:
      aSig1=aSqu1.next();
      aSig2=aSin2.next();
      aSig=aSig1*aSig2/4;
      break;
   case 5:
      aSig1=aSin1.next();
      aSig2=aSin2.next();
      aSig=aSig1+aSig2+aNoise2.next();
      break;
    case 6:
      aSig1=aSaw1.next();
      aSig2=aSin2.next();
      aSig=aSig1+aSig2+aNoise2.next();
      break;
    case 7:
      aSig1=aTri1.next();
      aSig2=aSin2.next();
      aSig=aSig1+aSig2+aNoise2.next();
      break;

 }
  Sig=(int)(gain*aSig)>>8;
  return Sig;
}

void loop(){
  audioHook(); // required here
}
```

#デモ動画
右端のモジュールが今回作ったやつです。
左上にあるKORG SQ1からGATE出してます。
※SQ1のLEDと、モジュールのLEDが同じタイミングで光ってるかと思います
　音もそのタイミングで鳴ってます

https://www.youtube.com/watch?v=6St89-L6qRY