【Arduino MIDIドラムシーケンサ⑤】スイッチでテンポチェンジ、その他改良
前回はタクトスイッチでリズムパターン(チャンネル)の変更を行いました。テンポをトリムポットで調整するのはふら付きの問題があったため、今回はテンポをスイッチで変更できるようにしたいと思います。前回の記事で割り込みについて勉強したので、早速組み込んでみたいと思います。
jakejake.hatenablog.com
前回からの変更点まとめ
- D2ピンとD3ピンは割り込みでテンポ変更に使用するようにした。
- 各チャンネルのテンポをタクトスイッチで調節出来るようにした。
- チャンネル変更用タクトスイッチの監視(Getchannel)を一小節ごとから一拍ごとに変更。
- LCDの切り替えタイミングをテンポスイッチ変更時もしくはチャンネル変更時に変更。
- 4分音符ごとにLEDを点灯。
コード
// 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-17 | Tara-chang | Added BUTTONS to change tempo #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, CRUSH}; byte VELOCITY[] = {127, 100, 70, 30}; const byte NUM_DRUM = sizeof(DRUM)/sizeof(DRUM[0]); const byte NUM_BEAT = 8; boolean RTM0[NUM_DRUM][NUM_BEAT] = { {0,0,1,0,0,0,1,0},//SNARE {1,0,0,0,1,1,0,0},//KICK {1,0,1,0,1,0,1,0},//CLHIHAT {0,0,0,0,0,0,0,0}//RIDE }; boolean RTM1[NUM_DRUM][NUM_BEAT] = { {0,0,1,0,0,0,1,0},//SNARE {1,0,0,0,0,1,0,1},//KICK {0,1,0,1,0,1,0,1},//CLHIHAT {0,0,0,0,0,0,0,0}//RIDE }; boolean RTM2[NUM_DRUM][NUM_BEAT] = { {0,0,0,0,1,0,0,0},//SNARE {1,1,1,1,1,1,1,1},//KICK {0,0,0,0,0,0,0,0},//CLHIHAT {1,0,0,0,1,0,0,0}//RIDE }; boolean RTM3[NUM_DRUM][NUM_BEAT] = { {0,0,1,0,0,0,1,0},//SNARE {1,0,1,0,1,0,1,0},//KICK {0,1,0,1,0,1,0,1},//CLHIHAT {0,0,0,0,0,0,0,0}//RIDE }; boolean RTM4[NUM_DRUM][NUM_BEAT] = { {1,1,1,1,1,1,1,1},//SNARE {1,0,1,0,1,0,1,0},//KICK {0,0,0,0,0,0,0,0},//CLHIHAT {1,0,1,0,1,0,1,0}//RIDE }; byte nt = 0; byte in = 0; byte i = 0; int BPM = 0; //X beats/60sec = 60/X sec/beat int interval;// = 60/double(BPM)*(4/double(NUM_BEAT))*1000; // boolean changed = 1; int INTERVALS[] = {150, 170, 200, 150}; byte CHANNELS[] = {0,1,2,3}; byte LEDPIN[] = {9, 11, 12, 13}; byte BUTPIN[] = {4, 5, 6, 8}; 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 { INTERVALS[CHANNEL]++; changed =1; } ISR(INT1_vect)//pin3 { changed =1; INTERVALS[CHANNEL]--; } void loop(){ for (nt = 0; nt < NUM_BEAT; nt++){ digitalWrite(LEDPIN[nt/2], 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(INTERVALS[NEXT_CHANNEL],NEXT_CHANNEL); changed=0; } delay(interval); digitalWrite(LEDPIN[nt/2], LOW); } BPM = INTERVALS[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{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)); }
動作
イイ感じに動いております。狙った通りのものが出来ると楽しいですね。
MIDIドラムシーケンサー作ってた。1ループが長いのでテンポチェンジのボタンは割り込みを使ってる。チャンネル変更は1小節区切りで。
— タラオメタル (@chem_phys_elec) May 17, 2021
使い道は別にないけどプログラミングが楽しい。16分にも対応してドラムプラグインももっと良いの使いたいな。 pic.twitter.com/uw9j3TCrGn
課題
・テンポ調整は1ずつ行いたいのに、結構な割合で2以上変化してしまう。
・16分に対応していない。