JUCEプログラミング、Handling Midi Events3、MIDI入力の取り扱い

MIDI入力に関係する関数について進めていきます。

意外と複雑な処理がまっているかも

引き続き、Handling Midi Eventsのチュートリアルです。「Handling external MIDI input」と「The MIDI keyboard state and component」の部分となります。外部MIDIデバイスからのMIDIメッセージの受信と、仮想MIDIキーボードの状態を取得する点の内容となります。

チュートリアルページはこちらになります。

あわせて読みたい
JUCE: Tutorial: Handling MIDI events

こんな人の役に立つかも

・JUCEプログラミングを勉強している人

・JUCEの「Handling Midi Events」チュートリアルを進めている人

・JUCEでMIDIを扱いたい人

目次

外部MIDIデバイスからの受信

handleIncomingMidiMessage関数

MidiInputCallbackクラスの関数で、overrideしている関数です。(MainComponent.hでMidiInputCallbackを継承しています。)

あわせて読みたい
JUCE: MidiInputCallback Class Reference

この関数は、外部のMIDI入力デバイスからMIDIイベントが到達したときに、呼び出されます。優先度の高いシステムスレッドで行われる(OS側での処理)ので、この中でUIの処理などを行わないように注意します。

//外部MIDIデバイスからのMIDIメッセージを受信します。
void MainComponent::handleIncomingMidiMessage(juce::MidiInput* source, const juce::MidiMessage& message)
{
    //[1]このスコープで、isAddingFromImdiInput変数をTrueにします。
    const juce::ScopedValueSetter<bool> scopedInputFlag(isAddingFromMidiInput, true);
    keyboardState.processNextMidiEvent(message);//[2]
    postMessageToList(message, source->getName());//[3]
}

[1]の「ScopeValueSetter」で、この関数実行中だけ「isAddingInputFlag」をtrueにしています。スコープ単位で変数を代入できるような関数です。

あわせて読みたい
JUCE: ScopedValueSetter< ValueType > Class Template Reference

「isAddingInputFlag」は外部MIDIデバイスからの受信メッセージである場合はTrueになるようなフラグということになります。

[2]では、kyeboardStateクラスの「processMidiEvent」関数にMIDIメッセージを与えてMIDIキーボードのノートオンノートオフの状態を変化させます。

あわせて読みたい
JUCE: MidiKeyboardState Class Reference

[3]で、後程定義するPostMessageToList関数でMIDIメッセージを出力します。

MidiKeyboardStateのリスナー関数

handleNoteOn関数

この関数は、MidiKeyboardStateの状態がノートオンになったとき(processNextMidiBuffer()関数やnoteOn()が実行されたとき)に呼び出されます。音声処理スレッドで実行されるらしいので、ここでもUI処理や、処理をブロックしないように注意します。

もちろん、MidiKyeboardComponent(GUIのキーボード)はMidiKyeboardStateを可視化したのもなので、GUIキーボードのノートオンでもこのリスナーが動作します。

void MainComponent::handleNoteOn(juce::MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity)
{
    if (!isAddingFromMidiInput)
    {
        auto m = juce::MidiMessage::noteOn(midiChannel, midiNoteNumber, velocity);
        m.setTimeStamp(juce::Time::getMillisecondCounterHiRes() * 0.001);
        postMessageToList(m, "On-Screen Keyboard");
    }
}

isAddingFromMidiInputフラグがFalseのとき(外部デバイスからのMIDIイベントでないとき)は条件内の処理が実行されます。MIDIノートオンメッセージを作成して、タイムスタンプを格納し、postMessageToListでMIDIメッセージを出力します。

handleNoteOff関数

先ほどのnoteOn関数と同様です。ノートオフのタイミングで実行されます。

void MainComponent::handleNoteOff(juce::MidiKeyboardState*, int midiChannel, int midiNoteNumber, float /*velocity*/)
{
    if (!isAddingFromMidiInput)
    {
        auto m = juce::MidiMessage::noteOff(midiChannel, midiNoteNumber);
        m.setTimeStamp(juce::Time::getMillisecondCounterHiRes() * 0.001);
        postMessageToList(m, "On-Screen Keyboard");
    }
}

今回の関数の関係性

外部MIDIデバイスからのMIDIメッセージはhandleIncomingMidiMessage関数で処理されますが、その時、GUIのキーボードに状態を反映(keyboardState)したいので、processNextMidiEventにMIDIメッセージを渡します。そうすると、keyboardStateのリスナーのhandleNoteOnが動作しますので、外部MIDIデバイスのときはフラグを立ててhandleNoteOn内の処理を行わないようにしなければいけません。

ノートオンをそれぞれ外部MIDIデバイスと、GUIのキーボードから行った場合の処理を図にしてみました。

外部MIDIデバイスのときのフラグは「isAddingFromInput」で管理していて、handleIncomingMidiMessage開始時にTrueになります。そのため、この関数内部から呼び出されるprocessNextMidiEventが実行されて、handleNoteOnが処理開始されるときには、isAddingMidiInputはTrueで、handleNoteOnの処理は条件から外れて、なにもしないで終了することになります。

フラグの管理や、処理の順番が意外と複雑でした。

よかったらシェアしてね!
目次
閉じる