MIDI メッセージを受け取った時の処理を付与する

MidiInputDevices は各配列にそれぞれデバイスが入っています。そのデバイスの onmidimessage プロパティにハンドラを付与すれば、MIDI メッセージが来た時にそのハンドラを実行してくれます。以下の部分ですね。

MidiInputDevices.forEach(o => (o.onmidimessage = getMIDIMessage(synth)))

このハンドラには MIDI メッセージが引数として渡されます。ただ少しこの段階では分かりづらいのですが、この getMIDIMessage は単なる関数ではなく高階関数と呼ばれる関数を返す関数ですので、synth という名前で与えているのは MIDI メッセージではありません。これはあくまで関数を返す際に必要な値です。getMIDIMessage(synth) を実行して、帰ってきた関数がハンドラとして登録され、そのハンドラに MIDI メッセージが引数として渡されます。

index.js
import Synth from '/src/Synth'
import createAudioContext from '/src/WebAudioAPI/createAudioContext'
import getMIDIInputDevices from '/src/MIDI/getMIDIInputDevices'
import getMIDIMessage from '/src/MIDI/getMIDIMessage'

const audioContext = createAudioContext()
const destination = audioContext.destination

// synth インスタンスを作成
const synth = new Synth({
  audioContext,
  nextNode: destination,
})

// MIDI device にアクセスして、
// MIDI message を受け取った際に起動させるハンドラを登録する
const start = async () => {
  // MIDI デバイスを取得する
  const MidiInputDevices = await getMIDIInputDevices()
  console.log({ MidiInputDevices })
  if (MidiInputDevices) {
    // 全ての MIDI デバイスへ
    // MIDI メッセージを受け取った際に起動するハンドラを付与する
    // getMIDIMessage がハンドラ
    // ハンドラで使用する synth インスタンスを渡す
    MidiInputDevices.forEach(o => (o.onmidimessage = getMIDIMessage(synth)))
  }
}

start()

getMIDIMessage を作る

https://codesandbox.io/s/km42r386r5?module=%2Fsrc%2FMIDI%2FgetMIDIMessage.js

まずこれは高階関数になっている点に留意してください。メッセージを受け取った時にならすシンセをまず引数として受け取って、関数を返します。返される関数をは message を受け取って、それを元に条件分岐しシンセを制御する関数です。

構造だけに着目すると以下のようになっています。

これを getMIDIMessage(synth) として実行すると以下のような関数を返します。この関数には引数として渡した synth を使うようになっているわけですね。

message で分岐する処理

さて、受け取った MIDI メッセージの data というプロパティに必要な値が入っています。これは配列で、index 0 にはコマンドが、1 には MIDI Note Number が、それから command 128 以外の時には index 3 にヴェロシティが入っています。

これを元に分岐していくわけです。

条件分岐はまず command をみて、再生停止を振り分けます

  • 144: note on なので再生

  • 128: note off なので停止

さらに、144: note on の場合でも velocity が 0 の場合は停止だと判断します。

あとはその判定を元に synth を .play .stop するだけですね。

さてこれで全て実装できました。

Last updated