JUCEプログラミング、Create MIDI data、MIDIを扱うアプリ2

cppを最後まで実装して動作するところまでやります。

MidiMessageクラスの使い方もすこしだけ詳しくなるね

JUCEのチュートリアル、MIDIの「Create MIDI data」チュートリアルを前回に引き続きやっていきます。このチュートリアルでは、ZIPでダウンロードできる「MidiMessageTutorial_01.h」の内容に相当します。また、チュートリアル内の項目の「The MidiMessage class」の内容となります。

JUCE
tutorial_midi_message - JUCE Tutorial: Create MIDI data This tutorial introduces the MidiMessage class, which is used for representing MIDI data. The MidiBuffer class is also introduced for...

こんな人の役に立つかも

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

・JUCEの「Create MIDI data」チュートリアルを進めている人

・JUCEでMIDIを扱いたい人

目次

MainComponent.cpp

logMessage関数

juce::Stringクラスのオブジェクトを引数として受け取り、midiMessagesBox(GUIの右のjuce::TextEditorクラスのオブジェクト)へテキストを出力する関数です。

void MainComponent::logMessage(const juce::String& m)
{
    //キャレット(入力カーソル)を最後に持ってきます。
    midiMessagesBox.moveCaretToEnd();
    //文字をキャレットの場所に挿入します。
    midiMessagesBox.insertTextAtCaret(m + juce::newLine);
}

TextEditorクラスの関数を利用して、文字列を出力しています。

あわせて読みたい

getMidiMessageDescription関数

この関数は、引数に受け取るMidiMessageが、どんなMIDIメッセージなのかを判定してその内容を文字列に変換する関数です。

MidiMessageクラスには、「getDescription」という同様の機能をもつ関数があります。チュートリアルでは、理解を深めるために、自分で実装をしているとのことでした。

juce::String MainComponent::getMidiMessageDescription(const juce::MidiMessage& m)
{
    //[1]MIDIメッセージの内容をすべての種類について条件判定します。
    if (m.isNoteOn())           return "Note on " + juce::MidiMessage::getMidiNoteName(m.getNoteNumber(), true, true, 3);
    if (m.isNoteOff())          return "Note off " + juce::MidiMessage::getMidiNoteName(m.getNoteNumber(), true, true, 3);
    if (m.isProgramChange())    return "Program change " + juce::String(m.getProgramChangeNumber());
    if (m.isPitchWheel())       return "Pitch wheel " + juce::String(m.getPitchWheelValue());
    if (m.isAftertouch())       return "After touch " + juce::MidiMessage::getMidiNoteName(m.getNoteNumber(), true, true, 3) + ": " + juce::String(m.getAfterTouchValue());
    if (m.isChannelPressure())  return "Channel pressure " + juce::String(m.getChannelPressureValue());
    if (m.isAllNotesOff())      return "All notes off";
    if (m.isAllSoundOff())      return "All sound off";
    if (m.isMetaEvent())        return "Meta event";

    //[2]MIDIコントローラの名前を取得します。
    if (m.isController())
    {
        juce::String name(juce::MidiMessage::getControllerName(m.getControllerNumber()));

        if (name.isEmpty())
            name = "[" + juce::String(m.getControllerNumber()) + "]";

        return "Controller " + name + ": " + juce::String(m.getControllerValue());
    }

    //[3]システムメッセージなどの場合は、次の行に到達します。
    return juce::String::toHexString(m.getRawData(), m.getRawDataSize());
}

[1]の条件判定で、すべてのタイプのMIDIメッセージについて判定しています。ノートオンとノートオフのときに使われている、「getNoteNumber」関数は、Midiメッセージタイプがオンオフに関わらずノートナンバーを取得してきますので、このように事前にノートオンなのか、オフなのかを「isNoteOn」「isNoteOff」「isNoteOnOrOff」関数で確認する必要があるようです。

[2]では、MIDIコントローラの名称を取得するようです。

最後の[3]では、MIDIメッセージタイプに該当しないシステムメッセージなどがここに到達します。MidiMessageクラスの「getRawData」関数で、生のメッセージデータを取得しています。

コンストラクタ

コンストラクタの[1]メンバイニシャライザでstartTime変数を初期化して、[2]以降のインスタンスの初期化を行いました。

MainComponent::MainComponent()
    : startTime(juce::Time::getMillisecondCounterHiRes() * 0.001)
//[1]
{
    //[2]各種GUIなどインスタンスの初期化です。
    addAndMakeVisible(bassDrumButton);
    bassDrumButton.setButtonText("Bass Drum (36)");
    bassDrumButton.onClick = [this] { setNoteNumber(36); };

    addAndMakeVisible(snareDrumButton);
    snareDrumButton.setButtonText("Snare Drum (38)");
    snareDrumButton.onClick = [this] { setNoteNumber(38); };

    addAndMakeVisible(closedHiHatButton);
    closedHiHatButton.setButtonText("Closed HH (42)");
    closedHiHatButton.onClick = [this] { setNoteNumber(42); };

    addAndMakeVisible(openHiHatButton);
    openHiHatButton.setButtonText("Open HH (46)");
    openHiHatButton.onClick = [this] { setNoteNumber(46); };

    addAndMakeVisible(volumeLabel);
    volumeLabel.setText("Volume (CC7)", juce::dontSendNotification);

    addAndMakeVisible(volumeSlider);
    volumeSlider.setRange(0, 127, 1);
    volumeSlider.onValueChange = [this]
    {
        auto message = juce::MidiMessage::controllerEvent(midiChannel, 7, (int)volumeSlider.getValue());
        message.setTimeStamp(juce::Time::getMillisecondCounterHiRes() * 0.001 - startTime);
        addMessageToList(message);
    };

    //[3]GUI右側のテキストエディタの初期化です。
    addAndMakeVisible(midiMessagesBox);
    midiMessagesBox.setMultiLine(true);
    midiMessagesBox.setReturnKeyStartsNewLine(true);
    midiMessagesBox.setReadOnly(true);
    midiMessagesBox.setScrollbarsShown(true);
    midiMessagesBox.setCaretVisible(false);
    midiMessagesBox.setPopupMenuEnabled(true);
    midiMessagesBox.setColour(juce::TextEditor::backgroundColourId, juce::Colour(0x32ffffff));
    midiMessagesBox.setColour(juce::TextEditor::outlineColourId, juce::Colour(0x1c000000));
    midiMessagesBox.setColour(juce::TextEditor::shadowColourId, juce::Colour(0x16000000));

    setSize(800, 300);
}

[2]では、ボタンとスライダーを初期化しています。ボタンのハンドラとして、以前作成した「setNoteNumber」関数でMIDIメッセージを発生+GUIの右にテキスト表示するようにしています。

[3]は、TextEditorクラスのmidiMessagesBoxを初期化します。

基本的なクラスの初期化はだいぶ慣れてきました。

基本的にこのパラメータをコピペして変更していくと楽だね

resized関数

GUIの配置(ボタンとスライダーとテキストボックス)を行います。

void MainComponent::resized()
{
    //次のようにGUIを配置します。
    auto halfWidth = getWidth() / 2;

    auto buttonsBounds = getLocalBounds().withWidth(halfWidth).reduced(10);

    bassDrumButton.setBounds(buttonsBounds.getX(), 10, buttonsBounds.getWidth(), 20);
    snareDrumButton.setBounds(buttonsBounds.getX(), 40, buttonsBounds.getWidth(), 20);
    closedHiHatButton.setBounds(buttonsBounds.getX(), 70, buttonsBounds.getWidth(), 20);
    openHiHatButton.setBounds(buttonsBounds.getX(), 100, buttonsBounds.getWidth(), 20);
    volumeLabel.setBounds(buttonsBounds.getX(), 190, buttonsBounds.getWidth(), 20);
    volumeSlider.setBounds(buttonsBounds.getX(), 220, buttonsBounds.getWidth(), 20);

    midiMessagesBox.setBounds(getLocalBounds().withWidth(halfWidth).withX(halfWidth).reduced(10));
}

MIDIコントローラを接続してどうなるかも確認してみたいです。

そういえば、KORGのMIDIコンしばらく使ってないね~

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次