Release 2016.11.26 / Update 2019.1.20

ArduinoでMIDI送信

僕がDTMを始めた頃はパソコンだけで完結できるような環境はありませんでした。MIDIという規格によって、演奏データと実際の音を分けて鳴らすのが当たり前で、接続に右往左往してました。今ではMIDIは音楽関係の機器やソフトウェアでは必ず互換機能として搭載されてますし、簡単です。端末だけで音楽を作ることもできます。時の流れは恐ろしい。

今回はArduinoを使ってMIDI音源を鳴らしてみます。

“ゲージ判定”を使った、スイッチ読み取りライブラリを作ってみました。
(2017.6.26 追記)

こんな感じです。

この記事を書くにあたりこちらを参考にさせていただきました。

準備

用意するもの

付加的パーツ(無くても問題ないです)

DINコネクタ

MIDIケーブルはDIN 5ピンが規格として使われていて、秋月電子マルツパーツなどパーツショップでも手に入ります。

5つ接続ピンがありますが、結線するのはVCC、GND、通信用(TX)の3つだけです。

midi_connector_illust

MIDIインターフェース・MIDI音源

信号を受信して音を鳴らすためにMIDI機器が必要になります。シンセサイザー、ドラムマシン、電子ピアノや一部のファミリーキーボードなどMIDI IN端子があれば接続できると思います。また、最近はパソコン上でシンセを再現して音を鳴らすシーケンサーソフトも多様にあり、フリーで使えるものもあるので、それで確認する方法もあります。

Studio One Prime

インストール、細かい使い方は長くなるので割愛しますが、Mac/Windowsどちらでも扱えます。フリーでここまで出来るのは中々スゴイ話ですね。上記のデモビデオの音もこれで鳴らしています。ただし、PCに接続するにはMIDIインターフェースなるものが必要になります。

配線図

midi_5sw_breadboard

MIDIとは

MIDIとは1980年代に制定された、電子楽器(主にデジタルシンセサイザーなど)のプロトコルやインターフェイスに関する規格であり、Musical Instrument Digital Interfaceの略になります。

それまでの電子楽器は、それぞれのメーカーが独自の規格で外部からコントロールするような仕様になっていました。ある程度足並みは揃えてはいたけれど、「この鍵盤ではあのシンセの音色は鳴らせない」みたいな状況は普通だったわけです。

それがデジタル機器の台頭に合わせ、そこら辺のバラバラな仕様を統一しようという動きが高まり、MIDIが共通項として誕生することになりました。以来30年以上、音楽の環境には欠かせない存在として今も活躍しています。

midi_sw_midi_connect

MIDIは一言で言えばデジタルデータ版の楽譜です。ただし、楽譜では音符一つで長さを表現しますが、MIDIでは鳴らす信号と止める信号を送って初めて一つの音符となります。そしてそのやり取りをするためのデータ書式や物理的なケーブル配線など全般も含まれます。

MIDI信号の中身

例えば、MIDI鍵盤とシンセサイザーがあったら、MIDI端子を接続するだけで鍵盤でシンセが鳴らせるようになります。これは鍵盤を押した時に「ドの音を鳴らせ」とMIDI端子を通って命令が流れ、シンセ側がそれを受信実行するから実現しています。

一般的なデジタルシンセ・ピアノはほぼサンプラー的な仕組みになっています。ものすごく単純に言うと、ピアノの鍵盤1音づつにCDデッキを用意して、押した台数だけ再生、停止をしているような状態です。なので、根本的にはオンオフの信号を渡すことができれば音が鳴らせます。

MIDIで音を出すために必要な最低限のデータは、

  • チャンネル(1~16 CH)
  • 音のオンかオフの指定
  • 音階(ノートナンバー)
  • ベロシティ(音の強弱)

になります。つまり、これらを「MIDIメッセージ」として相手の機器に送信してやればいいのです。

MIDIメッセージ

「MIDI信号の送信」なんていうと難しそうですが、実は、

31.25Kbps (±1%) の非同期方式シリアル転送

による通信なので、フォーマットに沿って「Serial」データを送るだけで実現できます(投げっぱなしの一方通行ですが)。つまり、普段Arduinoで使うSerial.printと同じような手順です。

MIDIメッセージは複数のByteデータをひとつの命令として送ります。具体的には先頭に「ステータスバイト」と呼ばれるヘッダ的な1Byteを送り、その後実データとなる「データバイト」を送ります。つまり1つのMIDIメッセージには表題と本文といった風に、最低2Byte必要になります。

また「ステータス」と「データ」を判別するために各バイトの最大ビットがステータス(表題)の場合は1、データ(本文)の場合は0という決まりになっています。つまり、

ステータスバイトは128~255
データバイトは0~127

の数値内にそれぞれ収まっていないといけません。

MIDIデータフォーマット

midi_triger_status_and_data

ステータスバイトでは下4bitでMIDIチャンネル、残り3bitでデータの種類を指定します。ここら辺は16進数が絡む話なので、わからない方はこちらを読んでみてください。

MIDIでは音階にそれぞれ番号が振り分けられて、Noteはその鍵盤位置の数値になります。

midi_triger_keyboard

MIDIキーボードで打鍵するように音を出すには、「ノートオン」メッセージと「ノートオフ」メッセージを送ります。

  ステータスバイト 1Byte目 データバイト 2、3Byte
数値範囲 上位3bit
8~15
下位4bit
0~15
2Byte目
0~127
3Byte目
0~127
ノートオン 0x90 MIDI Ch Note Velocity
ノートオフ 0x80 MIDI Ch Note Velocity

その他のMIDIメッセージの仕様を知りたい方はこちらやWeb検索で参照してください。

midi_sw_5_t_sw

Arduino スケッチ

1音を出す送信

MIDI 2 チャンネルのC-3を鳴らしたければ、

ちゃんと接続されていれば、1秒ごとに「ド」の音が出るはずです。思っていた以上に簡単な話ですね。0~15の16進数に対して、MIDIチャンネルは1-16なのに注意してください。CH 2 → 0x91

MIDI信号の送信を関数にして簡略化します。

スイッチで音のオンオフ送信

スイッチを使って音のオンオフを制御できるようにします。とりあえず1つのスイッチから。以前紹介したゲージ判定方式でスイッチを機能させます。これなら、チャタリングをプログラミングだけで回避できます。

どうでしょうか?押している間だけ音が鳴るようになったと思います。押している間増加するgauge変数のカウントが、PUSH_SHORTと同じになったときだけノートオンを送ります。Main loop最後で、gauge変数がPUSH_SHORT分あるか(押したか)判定し、ノートオフを送る仕組みです。

じゃあ、このやり方でスイッチを増やそうなんて思っちゃいますが、これだと1つのスイッチしか扱えません。問題はWhile文でgaugeカウントしていることにあります。工夫が必要です。

解決法はこうです「カウントする変数の値を保持する」。

考え方はさっきと同じですが、メインループ内の読み取りは一回だけで、何回かの繰り返しの中でアクションを起こす仕組みに変更しています。

digitealRead判定の累計で対応を変えていることがポイントです。とにかくスイッチが押されていたらgauge変数が増加、そうでなかったらカウントリセット、その時gauge変数が貯まっていたらノートオフ送信。そして、gauge変数がPUSH_SHORT定数と均等の時だけノートオン送信。順番がややこしい感じですが、やっていることはそんな難しいことではありません。

これも関数化しちゃいます。

スイッチ判定を返り値のある関数にします。押した瞬間を判定したら1、それ以外の押している間は2、離したと判定したら255を返すようにして、その数値でアクションを分けるようにしています。

複数のスイッチに対応して送信

さて、ここまで出来れば複数のスイッチを扱うことができるようになります。単純にスイッチ分だけ変数を配列にすればいいだけです。

ピンの数”PIN_NUM”をdefineして、その分だけ必要な変数を配列にします。接続したスイッチのピン番号(pins)、MIDIノート番号(notes)、それぞれの判定に使うgauge変数など。PIN_NUMとピン番号の数、それに対応するMIDIノート番号の数が合致すれば、Arduinoで扱える分だけスイッチは簡単に追加できます。

ちなみにLEDの明滅を、押しているかどうかに依存するよう変更しています。

モニタリング&チューニング

最後に、一応OLEDへデータが表示できるようにします。また、スイッチのgaugeカウンターのチューニングも行います。

u8glibのディスプレイ設定は環境に合わせて変更します。使い方が分からなければこちらをご覧ください。また、ディスプレイを用意しなくてもシリアルモニタにも数値が出るので、そちらで確認出来ます。

ところで、急に音が出なくなったと思います。これはスケッチが長くなったため、カウンターの増加するインターバルが伸びたためです。試しに長押してみてください。大分遅れて音が出るようになっています。

PUSH_SHORTの定数を自分の押したカウントにあわせます。自分は10くらいでいいかなと思います。ちなみに1ケタ台はチャタリングかと思います。

MIDIに関する設定はここを変更できます。チャンネルはCH。残念ながらオンオフしか扱えないので、ベロシティは単一でしか変更できません。notesはオクターブC-3のドレミファソになっています。MIDIでは10CHがドラム音色と相場が決まっているので、下記のように変えれば、その音が出ると思います。

まとめ

ArduinoでMIDIを扱うにはMIDI用のシールドを手に入れて、MIDIライブラリを使うのが常套手段だとは思いますが、ケーブルとSoftwareSerialだけでもこれだけ出来ました。もっと深く掘り下げていけば、ひょっとしたら自作のフィジカルコントローラみたいのもできるかもしれません。

スケッチ:MIDIボタンx5

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください