LoFiBeats・84分の作業用BGMを作成しました!

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

1_プログラミング

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

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

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

こんな人の役に立つかも

・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をコピーしました