Hobby Science&Experiment

愛と工作の日々

趣味でやっている工作や勉強したことのメモ書きです。

【Arduino MIDIドラムシーケンサ⑥】16分対応

ドラムシーケンサの続きです。8分だけではかなり単調なパターンしか演奏できないので、16部対応させてみました。好きなドラムパターンを再現できるのは楽しいですね。メトロノームの代わりに使うと楽しいです。

スケッチ

// Arduino MIDI Drum sequencer 
// Sending MIDI messages to the host PC.
// DAW: Waveform Free
//
// Author: Tara-chang
// 
// Revision History:
// Date        |    Author    |  Change
// ---------------------------------------------------
// 2021-05-09  |  Tara-chang  |  Initial Release
// 2021-05-09  |  Tara-chang  |  Added LCD and Trimpot to tempo control 
// 2021-05-16  |  Tara-chang  |  Added BUTTONS to change channel 
// 2021-05-19  |  Tara-chang  |  Sixteenth note available 


#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
#include <MIDI.h> 
MIDI_CREATE_DEFAULT_INSTANCE();

#include <avr/io.h>
#include <avr/interrupt.h>

#define CRUSH 49 //C#3
#define KICK 36 //C2
#define CLAP 39 //D#2
#define SNARE 38 //D2
#define RIDE 51 //D#3
#define HITOM 50 //D3
#define MIDTOM 48 //C3
#define LOWTOM 45 //A2
#define OPHIHAT 46 //A#2
#define CLHIHAT 42//F#2

byte DRUM[] = {SNARE, KICK, CLHIHAT, OPHIHAT, CRUSH, RIDE};
byte VELOCITY[] = {127, 100, 70, 70, 60, 60};
int BPMLIST[] = {175, 146, 150, 133, 150};
const byte NUM_DRUM = sizeof(DRUM)/sizeof(DRUM[0]);
const byte NUM_BEAT = 16;

boolean RTM0[NUM_DRUM][NUM_BEAT] = {
 //1   2   3   4   5   6   7   8
  {0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0},//SNARE
  {1,0,0,0,1,1,1,0,1,0,0,0,1,1,1,0},//KICK
  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},//CLHIHAT
  {1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0},//OPHIHAT
  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},//CRUSH
  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}//RIDE
  };

boolean RTM1[NUM_DRUM][NUM_BEAT] = {
 //1   2   3   4   5   6   7   8
  {0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0},//SNARE
  {1,0,0,1,0,1,0,0,1,0,0,1,0,1,0,0},//KICK
  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},//CLHIHAT  
  {1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0},//OPHIHAT
  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},//CRUSH
  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}//RIDE
  };

boolean RTM2[NUM_DRUM][NUM_BEAT] = {
 //1   2   3   4   5   6   7   8
  {0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0},//SNARE
  {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},//KICK
  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},//CLHIHAT
  {1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0},//OPHIHAT  
  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},//CRUSH
  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}//RIDE
  };

boolean RTM3[NUM_DRUM][NUM_BEAT] = {
 //1   2   3   4   5   6   7   8
  {0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0},//SNARE
  {1,0,0,1,0,0,1,0,0,0,1,0,1,0,1,0},//KICK
  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},//CLHIHAT
  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},//OPHIHAT  
  {1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0},//CRUSH
  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}//RIDE
  };

boolean RTM4[NUM_DRUM][NUM_BEAT] = {
 //1   2   3   4   5   6   7   8
  {0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0},//SNARE
  {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},//KICK
  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},//CLHIHAT
  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},//OPHIHAT  
  {1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0},//CRUSH
  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}//RIDE
  };

byte nt = 0;
byte in = 0;
byte i = 0;
byte BPM = 0; //X beats/60sec = 60/X sec/beat
byte interval;// = 60/double(BPM)*(4/double(NUM_BEAT))*1000; // 

boolean changed = 1;
byte CHANNELS[] = {0, 1, 2, 3, 4};
byte BUTPIN[] =   {4, 5, 6, 8, 7};
byte LEDPIN[] =   {9,11,12,13};

const byte NUM_CHANNELS = sizeof(CHANNELS)/sizeof(CHANNELS[0]);
const byte NUM_BUTPIN = sizeof(BUTPIN)/sizeof(BUTPIN[0]);
const byte NUM_LEDPIN = sizeof(LEDPIN)/sizeof(LEDPIN[0]);

byte NEXT_CHANNEL = 1;
byte CHANNEL = 0;
byte BUTTON_INT0 = 2;
byte BUTTON_INT1 = 3;

byte firstline;
byte secondline;
byte nowchannel;

void setup()
{ 
  Serial.begin(115200);
  MIDI.begin(4); //

  for (i=0;i<NUM_BUTPIN;i++){   
    pinMode(BUTPIN[i], INPUT);
    digitalWrite(BUTPIN[i], HIGH);     
  }
  for (i=0;i<NUM_LEDPIN;i++){   
    pinMode(LEDPIN[i], OUTPUT);
    digitalWrite(LEDPIN[i], LOW);     
  }
  
  lcd.init();
  lcd.backlight();
  lcd.clear();

  pinMode(BUTTON_INT0, INPUT);
  digitalWrite(BUTTON_INT0, HIGH);    // Enable pullup resistor
  pinMode(BUTTON_INT1, INPUT);
  digitalWrite(BUTTON_INT1, HIGH); 
  
  EICRA |= (1 << ISC01);    // Trigger on falling edge
  EIMSK |= (1 << INT0);     // Enable external interrupt INT0 = pin2
  EICRA |= (1 << ISC11);    // Trigger on falling edge
  EIMSK |= (1 << INT1);     // Enable external interrupt INT1 = pin3
  sei();                    // Enable global interrupts

}

ISR(INT0_vect)//pin2
{
  BPMLIST[CHANNEL]++;
  changed =1;      
}

ISR(INT1_vect)//pin3
{
  changed =1;
  BPMLIST[CHANNEL]--;      
}


void loop(){    
  for (nt = 0; nt < NUM_BEAT; nt++){
    digitalWrite(LEDPIN[nt/(NUM_BEAT/4)], HIGH);
    for (in = 0; in < NUM_DRUM; in++){    
           
      if (GetRTM(CHANNEL, in, nt)){
      MIDI.sendNoteOn(DRUM[in],VELOCITY[in],1);  // (pitch, velocity, channel)         
      MIDI.sendNoteOff(DRUM[in],0,1);   //        
      }
    }
    NEXT_CHANNEL = Getchannel(CHANNEL);
    if((changed)){
      LCDisplay(BPMLIST[NEXT_CHANNEL],NEXT_CHANNEL);
      changed=0;
    }
    delay(interval);
    digitalWrite(LEDPIN[nt/(NUM_BEAT/4)], LOW);     
    }

  BPM = BPMLIST[NEXT_CHANNEL];
  interval = 60/double(BPM)*(4/double(NUM_BEAT))*1000;
  CHANNEL = NEXT_CHANNEL;            
}

byte Getchannel(byte oldchannel){
    for (i=0;i<NUM_BUTPIN;i++){
      if ((digitalRead(BUTPIN[i])==LOW)){
        nowchannel = CHANNELS[i];
        changed=1;
      }
      else{
      }    
    }
    return nowchannel;
}

boolean GetRTM(byte nowchannel, byte in, byte nt){
    if(nowchannel==0){return RTM0[in][nt];}
    else if(nowchannel==1){return RTM1[in][nt];}
    else if(nowchannel==2){return RTM2[in][nt];}
    else if(nowchannel==3){return RTM3[in][nt];}
    else if(nowchannel==4){return RTM4[in][nt];}
    else{return 0;}
}

void LCDisplay(byte firstline, byte secondline){
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Tempo:"+String(firstline));
    lcd.setCursor(0, 1);
    lcd.print("Ch:"+String(secondline));
}

動作

毎度ながら音小さめです。

課題

0,0,0…と数字が並んでると可読性、デバッグ性が悪いですね…。4分ごとにカッコで囲むとか出来たら良い感じだと思いますが、そこまでやるかはわかりません。32部以上とか2小節以上の対応はしたいですね。いちいちDAWを起動するのは面倒ですがギター・ベース練習用のドラムマシンとして使えそうではあります。