PIC AVR 工作室別館 arduinoの館->TopPage->接続くん2->MIDIシールド

MIDIマスターキーボードシールド

MIDI関係の実験をするときに、リアルタイムで色々なコントロール信号などを自由自在に出力したかったので、 MIDI鍵盤っぽいArduino用シールドを作ってみました。

限りなく、自分の欲望を満たすためだけの代物です。

Bタイプ基板を1枚の広さに、1オクターブ分のキーと、可変抵抗1個、十字ジョイスティック(センターリターン)1個、 及び機能選択用のファンクションボタン(橙色)を1個搭載したシールド形状の基板です。(下の緑色のは、秋月製Arduino互換基板)

ファンクションボタンに、可変抵抗、ジョイスティック、鍵盤(ボタン)を組み合わせて、 スケッチ次第で色々な機能を搭載させることが出来ます。(色々なMIDI音源を繋いで、コントロールチェンジとかの効果をあれこれ比較可能)

ねらい

既存MIDIキーボードの欠点

元々は、各種MIDI音源ごとにコントロールチェンジでどんな効果が出るのか(音源ごとの微妙な違い)を探ってみるのに、 既存のMIDIキーボードでは色々不便だなぁと思ったのが発端です。

いわゆるMIDIマスターキーボードは、既に手元に2個あるんだけど、ピッチベンドやモジュレーション、ベロシティーなどの信号を、 あまり自由自在に設定できず不便。自分で色々プログラムを書き換えて動作できたら…

「色んな信号が出せる」シールドが欲しい

そこで、「演奏の入力(操作)」と「MIDI出力」の機能だけ持たせておいて、あとはArduinoのスケッチを書き換えるだけで、 色々な信号を自由自在に出せるようにしたいと思ったわけです。

シールドサイズなので、あまり凝ったことは出来ませんが、1オクターブ分くらいのキーボード(タクトスイッチ)と、 可変抵抗、センターリターンタイプの十字ジョイスティックあたりがあれば、後はスケッチをアレコレ書き換えるだけで、 自由自在な信号が出力できるはずだろう…と。

オクターブシフトや、各種コントロールチェンジ信号などの生成は、基板上の各種部品と自由自在にリンクさせて動作させることが出来ます。

で、冒頭の写真の様に、Bタイプ基板に部品を並べつつ、Arduinoの入出力端子の数とにらめっこしつつ、 可能な範囲の機能を盛り込んでみたのが、今回のシールドというわけです。

和音:いくつでもOK。MIDI出力端子搭載(DIN5)。電源はArduino基板から供給します。

まぁ、DTM用の打ち込みキーボードとして使えるような代物を目指す訳ではありませんが、 casioのGZ-5程度の打ち込みが可能なMIDIマスターキーボードとして使える程度の機能を盛り込みます。

スペックを詰める

制約事項

作る上で、大きな制約事項は2つあります。

一つは、ArduinoのコアCPU(今回はAT-MEGA328をチョイス)の入出力端子の数。もう一つは、Bタイプ基板のサイズによる配置面積。

Arduinoの入出力端子は、デジタル端子がD0~D13の14個、アナログ端子が6個あります。このデジタル端子のうち、D0とD1はシリアル入出力用で、 Arduinoのスケッチ書き込みやMIDI出力にも使います。なので、これらはシリアル用として固定にしておきます。

またD13は、Arduino標準ではLED出力に割り当たっていて、この回路と入力端子を共存するのは色々と面倒なので、 D13はLED出力のまま残しておくことにします。

結果、デジタル端子(特に入力用)として使えるのは、D2~D12の11個となります。

一方、アナログ端子6個は、アナログ入力にも、デジタル入出力にも、どちらにも切り替えて使用することができます。

アナログ入力として必要なのは、「可変抵抗」「ジョイスティックX軸」「ジョイスティックY軸」の3つです。 残りはデジタル入力用にまわすことが可能です。

入出力端子の割り当て

キーボード入力部分は、写真の通り、部品面積(フットプリント)の小さいタクトスイッチを使うことを念頭においているので、 基本的には「大量のデジタル入力端子」が必要となります。このため、アナログ端子のうち、 残った3つはデジタル端子に切り替えて使うことにします。

結果、デジタル入力には、11+3=14個を割り当てます。

「ド~ド」の1オクターブを搭載するとしたら、13個のキーが必要です。すると、デジタル入力は残り1個です。

残り1個は、「ファンクションキー」として搭載することにして、スケッチ次第で色々な用途(オクターブシフトやプログラムチェンジなど) に切り替えて使用することにします。本当は、もっとたくさんのファンクションキーを載せられればよかったんですが、多分この辺が限界でしょう。

I2CのIOエキスパンダ(MCP23017など)を使えば、I2C端子だけでたくさんのデジタル入出力が可能になるんですが、 Bタイプ基板サイズで、DIPのICを使う範囲では、サイズ的に厳しそうです。

結果、音階入力用(1オクターブ分)に13個のデジタル入力、ファンクション入力用に1個のデジタル入力、 ベロシティーやモジュレーション、ピッチベンド用に3個のアナログ入力、Arduino-IDEとのインターフェースやMIDI出力用にシリアル入出力、 といった具合に割り当てることにします。

(オクターブシフトを使わないかぎり、1オクターブの範囲でしか音程を作れません。オクターブシフトは、どこかで盛り込む必要)

(なお、MIDI出力に使うシリアル出力端子(TX)は、Arduino-IDEからスケッチを書き込む際にも使用されるはずなので、 スケッチ書き込み時にMIDI音源が接続してあると、MIDI音源が誤動作を起こす恐れがあります。 シリアル/MIDIまわりの接続の仕方(接続/切断のタイミング)には、注意が必要です)

搭載スペックとゴール

以上を踏まえて、1オクターブ分のキー入力、1個のファンクションキー、1個のアナログ入力(センターリターン無し)、 2個のアナログ入力(センターリターンあり)、MIDI出力端子、といった機能を搭載します。

また、Bタイプ万能基板に入りきる必要があります。 そのため、「無理の無い範囲かつ出来るだけ整然とした配置」が実現出来るように考慮します。

電源は、Arduino基板側から5Vの供給を受けて動作する方針とします。Arduino側は、USB接続でPCと繋いでいても(シリアル接続でPC内蔵音源)、 PCと切り離し、単独(Arduino基板側のDCソケットからの電源供給)でMIDIマスターキーボードとしても、どちらでも使えるように考慮します。

具体的には、シールドのIO端子の「5V」端子から5Vを受けて、動作させることにします。

なお、MIDI信号の通信速度は、「MIDI機器用の312500bps」と「PC接続用の38400bps」を、スケッチ中で簡単に切り替えられるようにしておき、 「MIDI音源機器」「PC内蔵MIDI音源(UART経由)」のどちらにでも切り替えて使えるようにします。

んで、このMIDIマスターキーボードを使いたい人がいるかどうかは別として、もし使いたい人がいたとしたら、 とりあえずの「雛形」として、「汎用的なMIDIマスターキーボード」として使える、Arduino用のスケッチを書くところまでを、 このページのゴールとします。

(個々の要件に合わせて、そのスケッチを修正することで、色々使いまわすための材料にできるように…)

設計内容(ハードまわり)

Arduinoなので、あまり深く考えず、とりあえず作り始めることにします。

作る上でやっかいになるのは、「ハード」部分の配線の取り回し(特に、どのピンに何を接続するか)だと思うので、 まずは配線からザックリ考えていきます。

配線図(回路図)

Bタイプ基板に部品を並べていって、極力、ピン配置の順序は規則性を持たせるように考えてみました。 (クリックで別窓に等倍表示します)

※※スイッチ類の間違えを訂正しました(正論理→負論理)※※
(2015.5.9)

ドレミファソラシドの8個は、D2~D9に順に並べて、半音はD18、D17、D10、D11、D12に並べてみました。D19はファンクションキーに。 とりあえず、今後もしプリント基板で作り直したとしても、あまり配線がごちゃごちゃしないで済むように考えてみたつもりですが、 プリント基板化して量産するほどの必要性は今のところないので、万能基板のまま使う予定。

MIDI出力は、シリアル端子をそのまま使います。MIDIの5V出力の場合は、220Ω抵抗2個を使って、 通信相手機器内のフォトインタラプタにカレントループで繋ぐのですが、手持ちの220Ωが無かったので、300Ωで代用しました。 (短いケーブルなら、これでも支障はでないだろう…と)

アナログI/Fは3つ。A0はボリウム(センターリターン無し)なので、ベロシティーの設定に使用する目論見。 A1はジョイスティックの横軸でモジュレーションに、A2は縦軸でピッチベンドに使用する目論見。

A0~A2のアナログ入力や、ド~ドのキー入力端子は、D19のファンクションキーと組み合わせることで、 色々な設定が出来るように含みを持たせておきます。

部品配置

配線の具合と、操作系の弄りやすさを念頭に、こんな感じで配置してみました。

MIDIコネクタは、基板上に配置したかったので、SparkFunで売っている 4UCON製のメスコネクタを使いました。

MIDIのDIN5コネクタのピン配置はこんな感じです。(5番がTXで、4番がVCC)

裏面はこんな感じ。配線やハンダが汚いのは内緒。

設計内容(ソフトまわり)

ソフトまわりは、用途に応じて色々修正しながら使えるようにしたいので、とりあえず最低限「キーボードとして機能する」 という範囲だけ作っておきます。

ザックリの盛り込む機能は、ド~ドの1オクターブのノートオン、ノートオフが出来ること、 ベロシティー調整、モジュレーション、ピッチベンドが機能すること、及び、 ファンクションキーにプログラムチェンジ(ただし音色は固定)といった、シンプルな範囲にとどめておきます。

使用するMIDIチャンネルは1番(0x00)に固定としておき、必要に応じて適当なチャンネルに修正しながら使うことにします。

スケッチ

/**********************************************/
/***     MIDI shield controller             ***/
/***                      for Arduino       ***/
/***                      version 0.2       ***/
/***   2013. 9. 3  created by Nekosan       ***/
/***   2013. 9. 4  modified 0.2 by Nekosan  ***/
/***                                        ***/
/**********************************************/


/* declare constant values */

const int velocity_pin = 0;        // potentiometer
const int pitch_bend_pin = 2;      // vertical
const int modulation_pin = 1;      // horizontal (cc#1)
const int led = 13;

const int note[13] = {60,61,62,63,64,65,66,67,68,69,70,71,72};
  // 24::120
const int key[14] = {2,18,3,17,4,5,10,6,11,7,12,8,9 ,19};
  // c c# d d# e f f# g g# a a# b c  func


/* declare global variables */ 

int now_notes[14] = {1,1,1,1,1,1,1,1,1,1,1,1,1, 1};
int old_notes[14] = {1,1,1,1,1,1,1,1,1,1,1,1,1, 1};
  /* active low */

int now_velocity;
int now_pitch_bend;
int now_modulation;

int old_pitch_bend;
int old_modulation;



/****************/
/* sub routines */
/****************/

void key_input(){
  int i;
  
  /* input keys */
  for (i=0;i<14;i++){
    now_notes[i] = digitalRead(key[i]);
  }
  
  now_velocity = analogRead(velocity_pin) / 8;
  now_pitch_bend = analogRead(pitch_bend_pin) / 8;
  now_modulation = analogRead(modulation_pin) / 4 - 128;
  if (now_modulation < 0) {
    now_modulation = 0;
  }
}


void check_and_output(){
  int i;
  
  for (i=0;i<13;i++){
    if (now_notes[i] == old_notes[i]) {
      /* note is not changed */
    } else {
      /* check note on or off */
      if (now_notes[i] == LOW) {  // active low
        note_on(0 , note[i] , now_velocity);
      } else {
        note_off(0 , note[i]);
      }
    }
  }
}


void store_old(){
  int i;
  
  for (i=0;i<13;i++) {
    old_notes[i] = now_notes[i];
  }
  
  old_pitch_bend = now_pitch_bend;
  old_modulation = now_modulation;
}


void note_on(int channel ,int note ,int velocity) {
  note_out(0x90+channel , note , velocity);
}


void note_off(int channel ,int note) {
  note_out(0x80+channel , note , 0);
}


void pitch_bend(int channel) {
  if (now_pitch_bend != old_pitch_bend) {
    pitch_bend_out(channel ,0 ,now_pitch_bend);
  }
}


void modulation(int channel) {
  if (now_modulation != old_modulation) {
    control_change(channel ,1 ,now_modulation);
    //1 = modulation (MSB only)
  }
}


void funtion_key(){
  int i;
  
  if (now_notes[13] == LOW) {  // funcyion
    program_change(0 ,65);  // Soprano Sax
  }
}



/*****************************/
/* sub routines for midi-out */
/*****************************/

void note_out(int channel,int note,int velocity){
  Serial.write(channel);    // 0::15
  Serial.write(note);       // 24::120
  Serial.write(velocity);    //0:127
}


void pitch_bend_out(int channel,int LSB,int MSB){
  Serial.write(0xe0 + channel);
  Serial.write(LSB);
  Serial.write(MSB);
}


void control_change(int CC,int LSB,int MSB){
  Serial.write(0xb0 + CC);
  Serial.write(LSB);
  Serial.write(MSB);
}


void program_change(int channel,int program){
  Serial.write(0xc0 + channel);
  Serial.write(program);
}



/**************/
/* main logic */
/**************/

void setup() {
  int i;
  
  Serial.begin(38400);      // connect to PC
  //Serial.begin(31250);      //connect to MIDI synth

  for (i=0;i<14;i++){
    pinMode(key[i], INPUT);
  }
  pinMode(led, OUTPUT);
  
  old_pitch_bend = analogRead(pitch_bend_pin) / 8;
  old_modulation = analogRead(modulation_pin) / 4 - 128;
  if (old_modulation < 0) {
    old_modulation = 0;
  }
}


void loop(){
  /* input from keys and pots */
  key_input();

  /* function key */
  funtion_key();

  /* check push or release / output note on/off */
  check_and_output();
  
  /* pitch bend */
  pitch_bend(0);
  
  /* modulation */
  modulation(0);
  
  /* store "old notes" */
  store_old();
  
}

補足

Arduino-IDE 1.0.5でコンパイルが通っています。

MIDIキーボードなので、当然ながら、「和音」入力は可能にしてあります。音源側が対応していれば、全押しで最大13音同時です。 (大抵のMIDI音源は、32音とか同時に出ますよね)

とりあえずスケッチを書いてみた…という状態なので、チャンネルの指定がスケッチ中で定数"0"として指定してあって、 行儀が悪いです。あとでキレイに直して使う予定。

スケッチ中で指定している「通信速度」は、MIDI音源に繋ぐ場合は「31250bps」に修正してください(スケッチ中のコメント参照)。

PC内蔵音源で鳴らす場合(Arduinoのシリアルケーブル及びシリアル-MIDI変換ソフトなどを経由)には「38400bps」のままで使ってください。

まとめ

Arduinoなので、単純なキーボード程度のモノは、さくっと作れて、やっぱりラクチンでした。

配線の取り回しをアレコレ考えている時間が一番長かった気がします。

当初の目的だった、色々なMIDI音源に繋いで音を鳴らしたときに、 どんな風に出るのかを試す実験ができて、とりあえず満足。

プリント基板作るにしても、この回路では、片面に納めるのは難しそうな気がしてきました。 I2C接続のI/Oエキスパンダ「MCP23017」とか使って、もう少しスマートに作った方がいいかもしれません。基板面積が許せば。

お好きなように改造してお使いください。