2009年9月23日水曜日

JavaScriptから生のMIDI信号を出力させてみる

ふと思い立ってJavaScriptからMidiを鳴らす方法を検索していたら、Midiファイルの再生ではなく Midi 信号をデバイスに送るやり方がどうも見当たらない。というわけで自分でなんとかしてみた。 やり方としては、JavaScript から Java Applet 経由でMidiデバイスを操作することにした。Applet の仕様は、とりあえず以下のような簡単なものとした。
  • MIDIレシーバの取得と解放は、Appletのイベントハンドラ、init() と destroy() で行う。
  • MIDI ショートメッセージの送信を対象とする。特にNOTE_ON, NOTE_OFF, PROGRAM_CHANGE はそのためのメソッドを用意する。
ソースは以下のような感じ
package xad.midi.applet;

import java.awt.*;
import javax.sound.midi.MidiDevice;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.Receiver;
import javax.sound.midi.ShortMessage;
import javax.sound.midi.MidiUnavailableException;

import static javax.sound.midi.ShortMessage.*;

public class MidiApplet extends java.applet.Applet {
 Receiver receiver = null;

 public void init(){
  try {
   receiver = MidiSystem.getReceiver();
  } catch (MidiUnavailableException e) {
   e.printStackTrace();
  }
 }

 public void destroy(){
  if (null != receiver) receiver.close();
 }

 public void sendNoteOn(int channel, int noteNo, int velocity) {
  send(NOTE_ON, channel, noteNo, velocity);
 }
 
 public void sendNoteOff(int channel, int noteNo) {
  send(NOTE_OFF, channel, noteNo, 0);
 }

 public void setProgram(int channel, int programNumber) {
  send(PROGRAM_CHANGE, channel, programNumber, 0);
 }

 public void send(int command, int channel, int data1, int data2) {
  try {
   ShortMessage on = new ShortMessage();
   on.setMessage(command, channel, data1, data2);
   receiver.send(on, -1);
  } catch(Exception e) {
   e.printStackTrace();
  }
 }
}
このアプレットを例えば以下のように HTML に組み込む。見えても仕方が無い Applet なので幅と高さを 0 にした(最初、css style の display に none を指定してみたが、見えなくなるだけでなく動作もしなくなってしまったので止めた)。
<applet
  codebase=コードベースのURL code=アプレットのクラスファイル 
  name="midiReceiver" style="width:0;height:0;"/></applet>
これを、例えば次のような JavaScript で動かす。
function getValueById(id) {
  return document.getElementById(id).value;
}
function play(duration) {
  noteNo = getValueById('noteOnNoteNo');
  velocity = getValueById('noteOnVelocity');
  document.midiReceiver.sendNoteOn(0, noteNo, velocity);
  setTimeout("document.midiReceiver.sendNoteOff(0, " + noteNo + ")", duration);
}
これを組み込んだのが以下の一群のテキストボックスとボタンで、以下のような動きにになる。
  • program change ボタンを押すと、テキストボックスに入力した楽器番号に対応する楽器に音色を変更する。番号と楽器の対応は General MIDI とか言う仕様で、たとえばピアノは 1~8、バイオリンは41というように定義されている。
  • note on ボタンを押すと、左のテキストボックスで指定した音番号の音を、右のテキストボックスで指定したベロシティで出力する。音番号もベロシティも0から127までの整数で、音番号は中央のCを60とする。この音を NOTE_ON で出力した後、JavaScript の SetTimeout で2秒待ってからNOTE_OFFで音を止める

まあ、なんとか思い通りに音が出てくる。この程度でも JavaScriptで音感訓練用の音当てゲーム的なものはできそう。

0 件のコメント:

コメントを投稿