MIDI メッセージを受け取った時の処理を付与する
MidiInputDevices は各配列にそれぞれデバイスが入っています。そのデバイスの onmidimessage プロパティにハンドラを付与すれば、MIDI メッセージが来た時にそのハンドラを実行してくれます。以下の部分ですね。
1
MidiInputDevices.forEach(o => (o.onmidimessage = getMIDIMessage(synth)))
Copied!
このハンドラには MIDI メッセージが引数として渡されます。ただ少しこの段階では分かりづらいのですが、この getMIDIMessage は単なる関数ではなく高階関数と呼ばれる関数を返す関数ですので、synth という名前で与えているのは MIDI メッセージではありません。これはあくまで関数を返す際に必要な値です。getMIDIMessage(synth) を実行して、帰ってきた関数がハンドラとして登録され、そのハンドラに MIDI メッセージが引数として渡されます。
index.js
1
import Synth from '/src/Synth'
2
import createAudioContext from '/src/WebAudioAPI/createAudioContext'
3
import getMIDIInputDevices from '/src/MIDI/getMIDIInputDevices'
4
import getMIDIMessage from '/src/MIDI/getMIDIMessage'
5
6
const audioContext = createAudioContext()
7
const destination = audioContext.destination
8
9
// synth インスタンスを作成
10
const synth = new Synth({
11
audioContext,
12
nextNode: destination,
13
})
14
15
// MIDI device にアクセスして、
16
// MIDI message を受け取った際に起動させるハンドラを登録する
17
const start = async () => {
18
// MIDI デバイスを取得する
19
const MidiInputDevices = await getMIDIInputDevices()
20
console.log({ MidiInputDevices })
21
if (MidiInputDevices) {
22
// 全ての MIDI デバイスへ
23
// MIDI メッセージを受け取った際に起動するハンドラを付与する
24
// getMIDIMessage がハンドラ
25
// ハンドラで使用する synth インスタンスを渡す
26
MidiInputDevices.forEach(o => (o.onmidimessage = getMIDIMessage(synth)))
27
}
28
}
29
30
start()
31
Copied!

getMIDIMessage を作る

getMIDIMessage.js
1
const getMIDIMessage = synth => message => {
2
const command = message.data[0]
3
const midiNoteNumber = message.data[1]
4
// command が 128 の時は
5
// noteoff 信号が来ているので velocity = 0 をかえす
6
// それ以外の時には data[2] に velocity が入っている
7
const velocity = command !== 128 ? message.data[2] : 0
8
9
console.log(
10
{
11
command: message.data[0],
12
midiNoteNumber: message.data[1],
13
velocity: message.data[2],
14
},
15
'got midi message!!'
16
)
17
18
switch (command) {
19
case 144: // noteOn
20
if (velocity === 0) {
21
const option = {
22
midiNoteNumber,
23
velocity,
24
}
25
synth.stop(option)
26
return option
27
}
28
29
{
30
const option = {
31
midiNoteNumber,
32
wave: 'sawtooth',
33
velocity,
34
}
35
36
synth.play(option)
37
return option
38
}
39
40
case 128: // noteOff
41
const option = {
42
midiNoteNumber,
43
}
44
synth.stop(option)
45
return option
46
47
default:
48
return 'default case'
49
}
50
}
51
52
export default getMIDIMessage
53
Copied!
まずこれは高階関数になっている点に留意してください。メッセージを受け取った時にならすシンセをまず引数として受け取って、関数を返します。返される関数をは message を受け取って、それを元に条件分岐しシンセを制御する関数です。
構造だけに着目すると以下のようになっています。
1
const getMIDIMessage = synth => message => {
2
// message で分岐する処理
3
}
Copied!
これを getMIDIMessage(synth) として実行すると以下のような関数を返します。この関数には引数として渡した synth を使うようになっているわけですね。
1
(message) => {
2
// message で分岐する処理
3
}
Copied!

message で分岐する処理

さて、受け取った MIDI メッセージの data というプロパティに必要な値が入っています。これは配列で、index 0 にはコマンドが、1 には MIDI Note Number が、それから command 128 以外の時には index 3 にヴェロシティが入っています。
これを元に分岐していくわけです。
MDI/getMIDIMessage.js
1
const command = message.data[0]
2
const midiNoteNumber = message.data[1]
3
// command が 128 の時は
4
// noteoff 信号が来ているので velocity = 0 をかえす
5
// それ以外の時には data[2] に velocity が入っている
6
const velocity = command !== 128 ? message.data[2] : 0
7
Copied!
条件分岐はまず command をみて、再生停止を振り分けます
    144: note on なので再生
    128: note off なので停止
さらに、144: note on の場合でも velocity が 0 の場合は停止だと判断します。
あとはその判定を元に synth を .play .stop するだけですね。
1
const func = () => {
2
switch (command) {
3
case 144: // noteOn
4
if (velocity === 0) {
5
const option = {
6
midiNoteNumber,
7
}
8
synth.stop(option)
9
return option
10
}
11
12
const option = {
13
midiNoteNumber,
14
wave: 'sawtooth',
15
velocity,
16
}
17
18
synth.play(option)
19
return option
20
21
case 128: // noteOff
22
const option = {
23
midiNoteNumber,
24
}
25
synth.stop(option)
26
return option
27
28
default:
29
return 'default case'
30
}
31
}
32
Copied!
さてこれで全て実装できました。
Last modified 3yr ago