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コンしばらく使ってないね~