まずはボタンから。
前回はMixxxをRaspberry Piにインストールしました
非力なRaspberry Pi 2Bでディスプレイも小さいですが、動作自体は問題なさそうです。
今回は、Mixxxを操作するハードウェアを作っていきます。
以前作ったデバイスを再利用
ハードウェアを作ると言っても、モノ自体は以前作ったデバイスを再利用していきます。
こんな感じで、5つのボタンをGNDとデジタルIOピンにつないだものです。
マイコン部分はArduino Leonardo互換機のSeeeduino Liteです。Atmega32U4が搭載されているのでUSBネイティブサポートも使えて、USBデバイスとしてエミュレーションも可能です。よって以て、USB接続できるMIDIデバイスとしてもエミュレーション可能なはず…。
いざプログラミング
ハードウェアは再利用するので、それを制御するロジックを書き換えていきます。コーディングはArduino IDEで。 久しぶりのダウンロード・起動だったので、UIがガラッと変わっていたのでびっくりしました。
3億年ぶりにArduino IDE開いてる。 pic.twitter.com/WSeqC8HAGs
— ポキオ (@pokiiio) 2023年3月15日
さて、プログラミング自体は下記のArduinoのドキュメントを参考にコーディングしていきます。
ポイントは…
- Native USB Port対応をしているボードを使う
- MIDIを扱うためにMIDIUSBライブラリを使う
こんな感じ。MIDI系のライブラリは複数あってカオスですが、今回はArduinoドキュメントに従っていきます。
コードは最後の方に貼っておきますが、MIDIのやり取りとしてはノートオン・ノートオフを使っていきます。
void noteOn(byte channel, byte pitch, byte velocity) { Serial.println("noteOn"); midiEventPacket_t noteOn = { 0x09, 0x90 | channel, pitch, velocity }; MidiUSB.sendMIDI(noteOn); } void noteOff(byte channel, byte pitch, byte velocity) { Serial.println("noteOff"); midiEventPacket_t noteOff = { 0x08, 0x80 | channel, pitch, velocity }; MidiUSB.sendMIDI(noteOff); }
こんな感じで、ノート(鍵盤)を押す・離すという情報をプログラム的に送信します。鍵盤なので、どの音階をどのくらいの速さ(強さ)で押すか、などの情報も送ります。今回は中央のド(C3)からソ(G3)までを5つのボタンに割り当てて、ノートオン・ノートオフで送信します。チャネルと速度は固定のものを使いました。
ドレミファソをMixxxのコントロールに割り当てる
今回ド〜ソまでの5つの音階が表現できるMIDIデバイスが出来たので、それぞれDeck1/2のPlay・Cueに割り当てていきます。(5つのボタンのうち4つだけアサインしてみます)
理想としてはこんな割り当て。実際にMixxxでマッピングしていきます。
自作MIDIデバイスをRaspberry Piに接続してからMixxxを起動して、設定画面を開くとコントローラーというタブでデバイスが認識されているようです。
一応、プリセットという項目を覗いてみると、主要なDJコントローラーのマッピングには対応してそうですが、今回はプリセットは使わず、ラーニングウィザードを使って自力でマッピングしていきます。
ラーニングウィザードを開くと、Mixxx上の要素が一覧で並んでいて、それぞれに信号のアサインが出来ます。
とりあえず、Deck1/2のPlayとCueを探しては、実際にデバイスのボタンを押して、ボタンと制御の紐付けを行いました。
こんな感じ。
いざDJ
4ボタンだけなので、凝ったことは出来ませんが、ちゃんとボタンと演奏がリンクしていました。
雑感として
- 理論的にはボタンやスライダーを追加していけばDJコントローラーは自作できる
- ボタンがチャタるので、何かしらの制御が必要そう
- ハードウェアボタンを同時に押したり操作したりすると、処理が追いつくか(他の処理をブロッキングしないか)が心配
- ボタンが増えるとマッピングが面倒
こんな感じですねぇ。自作あるあるですが、やっぱり製品ってすげぇ…ってなりますね(笑)
とりあえず、横フェーダー・縦フェーダー、イコライザ、できればジョグあたりも追加してみたいところ・・・。
コードはこちら
#include <MIDIUSB.h> #include <MIDIUSB_Defs.h> #include <frequencyToNote.h> #include <pitchToFrequency.h> #include <pitchToNote.h> #define BUTTON1 3 #define BUTTON2 5 #define BUTTON3 7 #define BUTTON4 8 #define BUTTON5 10 #define C3 48 #define D3 50 #define E3 52 #define F3 53 #define G3 55 #define DELAY 100 void setup() { pinMode(BUTTON1, INPUT_PULLUP); pinMode(BUTTON2, INPUT_PULLUP); pinMode(BUTTON3, INPUT_PULLUP); pinMode(BUTTON4, INPUT_PULLUP); pinMode(BUTTON5, INPUT_PULLUP); Serial.begin(9600); } void loop() { if (digitalRead(BUTTON1) == 0) { Serial.println("BUTTON1"); noteOn(5, C3, 64); MidiUSB.flush(); delay(DELAY); noteOff(5, C3, 64); MidiUSB.flush(); } if (digitalRead(BUTTON2) == 0) { Serial.println("BUTTON2"); noteOn(5, D3, 64); MidiUSB.flush(); delay(DELAY); noteOff(5, D3, 64); MidiUSB.flush(); } if (digitalRead(BUTTON3) == 0) { Serial.println("BUTTON3"); noteOn(5, E3, 64); MidiUSB.flush(); delay(DELAY); noteOff(5, E3, 64); MidiUSB.flush(); } if (digitalRead(BUTTON4) == 0) { Serial.println("BUTTON4"); noteOn(5, F3, 64); MidiUSB.flush(); delay(DELAY); noteOff(5, F3, 64); MidiUSB.flush(); } if (digitalRead(BUTTON5) == 0) { Serial.println("BUTTON5"); noteOn(5, G3, 64); MidiUSB.flush(); delay(DELAY); noteOff(5, G3, 64); MidiUSB.flush(); } } void noteOn(byte channel, byte pitch, byte velocity) { Serial.println("noteOn"); midiEventPacket_t noteOn = { 0x09, 0x90 | channel, pitch, velocity }; MidiUSB.sendMIDI(noteOn); } void noteOff(byte channel, byte pitch, byte velocity) { Serial.println("noteOff"); midiEventPacket_t noteOff = { 0x08, 0x80 | channel, pitch, velocity }; MidiUSB.sendMIDI(noteOff); } void controlChange(byte channel, byte control, byte value) { Serial.println("controlChange"); midiEventPacket_t event = { 0x0B, 0xB0 | channel, control, value }; MidiUSB.sendMIDI(event); }
最後に…
誰かPCDJを私に恵んでください…。