Edited at
Siv3DDay 13

Siv3D の MIDI メッセージ送信ユーティリティ

More than 3 years have passed since last update.

Siv3D Advent Calendar 2015 13 日目の記事です。

Siv3D June 2015 から、MIDI の再生に対応した環境では MIDI メッセージを直接送信して音楽演奏ができるようになりました。しかし、MIDI メッセージの規格 に従って適切な数値を送信しなければならないので、MIDI に慣れていない場合には少し扱いづらいです。

今日は、MIDI メッセージの送信を補助するユーティリティヘッダを公開します。

この機能は Siv3D January 2016 に標準で実装される予定です。


MidiUtility.hpp

//-----------------------------------------------

//
// This file is part of the Siv3D Engine.
//
// Copyright (C) 2008-2015 Ryo Suzuki
//
// Licensed under the MIT License.
//
//-----------------------------------------------

# pragma once
# include <Siv3D.hpp>

namespace s3d
{
/// <summary>
/// 音階
/// </summary>
enum class PitchName : uint8
{
C_1 = 0,
CS_1,
DF_1 = CS_1,
D_1,
DS_1,
EF_1 = DS_1,
E_1,
FF_1 = E_1,
F_1,
ES_1 = F_1,
FS_1,
GF_1 = FS_1,
G_1,
GS_1,
AF_1 = GS_1,
A_1,
AS_1,
BF_1 = AS_1,
B_1,

CF0 = B_1,
C0,
BS_1 = C0,
CS0,
DF0 = CS0,
D0,
DS0,
EF0 = DS0,
E0,
FF0 = E0,
F0,
ES0 = F0,
FS0,
GF0 = FS0,
G0,
GS0,
AF0 = GS0,
A0,
AS0,
BF0 = AS0,
B0,

CF1 = B0,
C1,
BS0 = C1,
CS1,
DF1 = CS1,
D1,
DS1,
EF1 = DS1,
E1,
FF1 = E1,
F1,
ES1 = F1,
FS1,
GF1 = FS1,
G1,
GS1,
AF1 = GS1,
A1,
AS1,
BF1 = AS1,
B1,

CF2 = B1,
C2,
BS1 = C2,
CS2,
DF2 = CS2,
D2,
DS2,
EF2 = DS2,
E2,
FF2 = E2,
F2,
ES2 = F2,
FS2,
GF2 = FS2,
G2,
GS2,
AF2 = GS2,
A2,
AS2,
BF2 = AS2,
B2,

CF3 = B2,
C3,
BS2 = C3,
CS3,
DF3 = CS3,
D3,
DS3,
EF3 = DS3,
E3,
FF3 = E3,
F3,
ES3 = F3,
FS3,
GF3 = FS3,
G3,
GS3,
AF3 = GS3,
A3,
AS3,
BF3 = AS3,
B3,

CF4 = B3,
C4,
BS3 = C4,
CS4,
DF4 = CS4,
D4,
DS4,
EF4 = DS4,
E4,
FF4 = E4,
F4,
ES4 = F4,
FS4,
GF4 = FS4,
G4,
GS4,
AF4 = GS4,
A4,
AS4,
BF4 = AS4,
B4,

CF5 = B4,
C5,
BS4 = C5,
CS5,
DF5 = CS5,
D5,
DS5,
EF5 = DS5,
E5,
FF5 = E5,
F5,
ES5 = F5,
FS5,
GF5 = FS5,
G5,
GS5,
AF5 = GS5,
A5,
AS5,
BF5 = AS5,
B5,

CF6 = B5,
C6,
BS5 = C6,
CS6,
DF6 = CS6,
D6,
DS6,
EF6 = DS6,
E6,
FF6 = E6,
F6,
ES6 = F6,
FS6,
GF6 = FS6,
G6,
GS6,
AF6 = GS6,
A6,
AS6,
BF6 = AS6,
B6,

CF7 = B6,
C7,
BS6 = C7,
CS7,
DF7 = CS7,
D7,
DS7,
EF7 = DS7,
E7,
FF7 = E7,
F7,
ES7 = F7,
FS7,
GF7 = FS7,
G7,
GS7,
AF7 = GS7,
A7,
AS7,
BF7 = AS7,
B7,

CF8 = B7,
C8,
BS7 = C8,
CS8,
DF8 = CS8,
D8,
DS8,
EF8 = DS8,
E8,
FF8 = E8,
F8,
ES8 = F8,
FS8,
GF8 = FS8,
G8,
GS8,
AF8 = GS8,
A8,
AS8,
BF8 = AS8,
B8,

CF9 = B8,
C9,
BS8 = C9,
CS9,
DF9 = CS9,
D9,
DS9,
EF9 = DS9,
E9,
FF9 = E9,
F9,
ES9 = F9,
FS9,
GF9 = FS9,
G9,
};

/// <summary>
/// GM 音色
/// </summary>
enum class GMInstrument : uint8
{
// Piano
Piano1,
Piano2,
Piano3,
Piano4,
ElectricPiano1,
ElectricPiano2,
Harpsichord,
Clavinet,

// Chromatic Percussion
Celesta,
Glockenspiel,
MusicBox,
Vibraphone,
Marimba,
Xylophone,
TubularBells,
Dulcimer,

// Organ
DrawbarOrgan,
PercussiveOrgan,
RockOrgan,
ChurchOrgan,
ReedOrgan,
Accordion,
Harmonica,
TangoAccordion,

//Guitar
NylonGuitar,
SteelGuitar,
JazzGuitar,
CleanGuitar,
MutedGuitar,
OverdrivenGuitar,
DistortionGuitar,
GuitarHarmonics,

// Bass
AcousticBass,
FingeredBass,
PickedBass,
FretlessBass,
SlapBass1,
SlapBass2,
SynthBass1,
SynthBass2,

// Strings
Violin,
Viola,
Cello,
Contrabass,
TremoloStrings,
PizzicatoStrings,
OrchestralHarp,
Timpani,

// Ensemble
StringEnsemble1,
StringEnsemble2,
SynthStrings1,
SynthStrings2,
ChoirAahs,
VoiceOohs,
SynthChoir,
OrchestraHit,

// Brass
Trumpet,
Trombone,
Tuba,
MutedTrumpet,
FrenchHorn,
BrassSection,
SynthBrass1,
SynthBrass2,

// Reed
SopranoSax,
AltoSax,
TenorSax,
BaritoneSax,
Oboe,
EnglishHorn,
Bassoon,
Clarinet,

// Pipe
Piccolo,
Flute,
Recorder,
PanFlute,
Blownbottle,
Shakuhachi,
Whistle,
Ocarina,

// Synth Lead
SquareWave,
SawWave,
SynCalliope,
ChifferLead,
Charang,
SoloVox,
FifthSawWave,
BassAndLead,

// Synth Pad
Fantasia,
WarmPad,
Polysynth,
SpaceVoice,
BowedGlass,
MetalPad,
HaloPad,
SweepPad,

// Synth Effects
IceRain,
Soundtrack,
Crystal,
Atmosphere,
Brightness,
Goblin,
EchoDrops,
StarTheme,

// Ethnic
Sitar,
Banjo,
Shamisen,
Koto,
Kalimba,
Bagpipe,
Fiddle,
Shanai,

// Percussive
TinkleBell,
Agogo,
SteelDrums,
Woodblock,
TaikoDrum,
MelodicTom,
SynthDrum,
ReverseCymbal,

// Sound effects
GuitarFretNoise,
BreathNoise,
Seashore,
BirdTweet,
TelephoneRing,
Helicopter,
Applause,
Gunshot,
};

/// <summary>
/// MIDI メッセージ・ユーティリティ
/// </summary>
namespace MidiMessage
{
/// <summary>
/// 音色を変更する MIDI メッセージを作成します。
/// </summary>
/// <param name="ch">
/// チャンネル [0, 15]
/// </param>
/// <param name="instrument">
/// 音色
/// </param>
/// <returns>
/// 音色を変更する MIDI メッセージ
/// </returns>
inline Array<uint8> SetInstrument(uint32 ch, uint32 instrument)
{
return{ uint8(0xC0 | (ch & 0xF)), uint8(instrument & 0x7F) };
}

/// <summary>
/// 音色を変更する MIDI メッセージを作成します。
/// </summary>
/// <param name="ch">
/// チャンネル [0, 15]
/// </param>
/// <param name="instrument">
/// 音色
/// </param>
/// <returns>
/// 音色を変更する MIDI メッセージ
/// </returns>
inline Array<uint8> SetInstrument(uint32 ch, GMInstrument instrument)
{
return{ uint8(0xC0 | (ch & 0xF)), uint8(static_cast<uint8>(instrument) & 0x7F) };
}

/// <summary>
/// ノート・オンの MIDI メッセージを作成します。
/// </summary>
/// <param name="ch">
/// チャンネル [0, 15]
/// </param>
/// <param name="note">
/// ノート番号
/// </param>
/// <param name="velocity">
/// ベロシティ
/// </param>
/// <returns>
/// ノート・オンの MIDI メッセージ
/// </returns>
inline Array<uint8> NoteOn(uint32 ch, uint32 note, uint8 velocity = 64)
{
return{ uint8(0x90 | (ch & 0xF)), uint8(note & 0x7F), velocity };
}

/// <summary>
/// ノート・オンの MIDI メッセージを作成します。
/// </summary>
/// <param name="ch">
/// チャンネル [0, 15]
/// </param>
/// <param name="note">
/// ノート番号
/// </param>
/// <param name="velocity">
/// ベロシティ
/// </param>
/// <returns>
/// ノート・オンの MIDI メッセージ
/// </returns>
inline Array<uint8> NoteOn(uint32 ch, PitchName note, uint8 velocity = 64)
{
return{ uint8(0x90 | (ch & 0xF)), uint8(static_cast<uint8>(note) & 0x7F), velocity };
}

/// <summary>
/// ノート・オフの MIDI メッセージを作成します。
/// </summary>
/// <param name="ch">
/// チャンネル [0, 15]
/// </param>
/// <param name="note">
/// ノート番号
/// </param>
/// <param name="velocity">
/// ベロシティ
/// </param>
/// <returns>
/// ノート・オフの MIDI メッセージ
/// </returns>
inline Array<uint8> NoteOff(uint32 ch, uint32 note, uint8 velocity = 64)
{
return{ uint8(0x80 | (ch & 0xF)), uint8(note & 0x7F), velocity };
}

/// <summary>
/// ノート・オフの MIDI メッセージを作成します。
/// </summary>
/// <param name="ch">
/// チャンネル [0, 15]
/// </param>
/// <param name="note">
/// ノート番号
/// </param>
/// <param name="velocity">
/// ベロシティ
/// </param>
/// <returns>
/// ノート・オフの MIDI メッセージ
/// </returns>
inline Array<uint8> NoteOff(uint32 ch, PitchName note, uint8 velocity = 64)
{
return{ uint8(0x80 | (ch & 0xF)), uint8(static_cast<uint8>(note) & 0x7F), velocity };
}

/// <summary>
/// 音量を変更する MIDI メッセージを作成します。
/// </summary>
/// <param name="ch">
/// チャンネル [0, 15]
/// </param>
/// <param name="volume">
/// 音量 [0.0, 1.0]
/// </param>
/// <returns>
/// 音量を変更する MIDI メッセージ
/// </returns>
inline Array<uint8> SetVolume(uint32 ch, double volume)
{
return{ uint8(0xB0 | (ch & 0xF)), uint8(0x07), static_cast<uint8>(Clamp(255 * volume, 0.0, 255.0)) };
}

/// <summary>
/// パンポットを変更する MIDI メッセージを作成します。
/// </summary>
/// <param name="ch">
/// チャンネル [0, 15]
/// </param>
/// <param name="panpot">
/// パンポット [-1.0, 1.0]
/// </param>
/// <returns>
/// パンポットを変更する MIDI メッセージ
/// </returns>
inline Array<uint8> SetPanpot(uint32 ch, double panpot)
{
return{ uint8(0xB0 | (ch & 0xF)), uint8(0x0A), static_cast<uint8>(Clamp(64 + 63.5 * panpot, 0.0, 127.0)) };
}

/// <summary>
/// 全てのノートをオフにする MIDI メッセージを作成します。
/// </summary>
/// <param name="ch">
/// チャンネル [0, 15]
/// </param>
/// <returns>
/// 全てのノートをオフにする MIDI メッセージ
/// </returns>
inline Array<uint8> AllSoundOff(uint32 ch)
{
return{ uint8(0xB0 | (ch & 0xF)), uint8(0x78), uint8(0x00) };
}
}
}



使用例


Main.cpp

// This is free and unencumbered software released into the public domain.

// For more information, please refer to <http://unlicense.org/>

# include <Siv3D.hpp>
# include "MidiUtility.hpp"

void Main()
{
const Array<std::pair<Key, GMInstrument>> instruments
{
{ Input::Key1, GMInstrument::Piano1 },
{ Input::Key2, GMInstrument::Marimba },
{ Input::Key3, GMInstrument::Harmonica },
{ Input::Key4, GMInstrument::JazzGuitar },
{ Input::Key5, GMInstrument::Violin },
{ Input::Key6, GMInstrument::VoiceOohs },
{ Input::Key7, GMInstrument::FrenchHorn },
{ Input::Key8, GMInstrument::SquareWave },
};

const Array<std::pair<Key, PitchName>> keys
{
{ Input::KeyA, PitchName::C4 },
{ Input::KeyS, PitchName::D4 },
{ Input::KeyD, PitchName::E4 },
{ Input::KeyF, PitchName::F4 },
{ Input::KeyG, PitchName::G4 },
{ Input::KeyH, PitchName::A4 },
{ Input::KeyJ, PitchName::B4 },
{ Input::KeyK, PitchName::C5 },
};

while (System::Update())
{
for (const auto& instrument : instruments)
{
if (instrument.first.clicked)
{
Midi::SendMessage(MidiMessage::SetInstrument(0, instrument.second));
}
}

for (const auto& key : keys)
{
if (key.first.clicked)
{
Midi::SendMessage(MidiMessage::NoteOn(0, key.second));
}
else if (key.first.released)
{
Midi::SendMessage(MidiMessage::NoteOff(0, key.second));
}
}
}
}


明日の記事は @matsu7874 さんです。

よろしくお願いします。