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

<a href="http://qiita.com/advent-calendar/2015/siv3d" target="_blank">Siv3D Advent Calendar 2015</a> 13 日目の記事です。
Siv3D June 2015 から、MIDI の再生に対応した環境では MIDI メッセージを直接送信して音楽演奏ができるようになりました。しかし、<a href="http://www2.odn.ne.jp/~cbu69490/MIDI/MIDIlect/MIDIlect3.html" target="_blank">MIDI メッセージの規格</a> に従って適切な数値を送信しなければならないので、MIDI に慣れていない場合には少し扱いづらいです。

今日は、MIDI メッセージの送信を補助するユーティリティヘッダを公開します。
この機能は Siv3D January 2016 に標準で実装される予定です。

```cpp: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) };
		}
	}
}
```
## 使用例

```cpp: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 さんです。
よろしくお願いします。