12-08 drywetmidi


https://adventar.org/calendars/3353 8日目のエントリーです。

what is drywetmidi?

drywetmidiは.NET Framework用に開発されたMIDIライブラリです。APIの一部はWinMM APIを利用しており、Windows専用になっています。Linux環境がメインである筆者はこのライブラリを使ったことがありませんが、drywetmidiには面白いAPIがあるので、かいつまんで紹介しようと思います。おそらく大部分はクロスプラットフォームで使えると思います。

drywetmidiはstrongly-typedなMIDIイベント表現を用いているもので、(先日portmidiとrtmidiのエントリーでも書きましたが)筆者はあまりこの種の表現が好きではないので、あまり使いたがることはないだろうと思います。ただし音楽編集のために作成された一連のAPIは面白いものなので、知っておいて損はないとも思っています。

カスタムmetaイベントとカスタムチャンク

MIDIメッセージとしては機能的な意味をもたないのですが、metaイベントは楽曲のさまざまな情報をバイト列で含めることができます。典型的なmetaイベントとしては、作曲者情報や曲タイトルなどがあります。筆者が知っている例で面白いものとしては、Vocaloid 2 Editorが出力する*.vsqファイルには歌詞に対応するmetaイベントが含まれていました(筆者が知る限りでは、その後Vocaloid 3の*.xvsqではXML形式になり、Vocaloid 5ではJSON形式になっていました)。

metaイベントはイベント種別とバイナリデータ(バイト列)以上の定義は何もありません。たとえば文字列を含める場合でも、それがUTF-8エンコーディングなのか、それ以外の何かしらのエンコーディングなのかを知る術はありません。つまりmetaイベントのシリアライズ・デシリアライズは処理系次第ということです。

drywetmidiでは、metaイベントの種別(これはFFhの次の1バイトで識別されます)に対応するシリアライザ・デシリアライザを登録しておくことで、自動的に所定のオブジェクトとバイト列を相互変換できます。

また、標準MIDIファイル(SMF)にはヘッダチャンクとトラックチャンクのみ規定されていますが、カスタムチャンクを定義して含めることもできます。筆者はカスタムチャンクを必要とする場面に出くわしたことがないのですが、世の中にはそういう処理系もあるかもしれません。

さまざまな時間表現

MIDI楽曲において重要な要素のひとつは、テンポとの関わりも考慮しつつ、時間をどのように表現するかです。楽曲における時間の表現は何通りか考えられるもので、SMFにおいてもTimeSignatureとTempoをどう指定するかが時間の計算に影響してきます。

drywetmidiでは時間の間隔をITimeSpanというインターフェースで表現しており、このインターフェースは時間に対する四則演算とClone()のみ定義します。具体的には次のような時間表現があります。

  • MetricTimeSpan: メートル法の世界における時間表現で、内容は.NETのSystem.TimeSpan型です。
  • MidiTimeSpan: MIDIにおけるticksとして時間を表したものです。内容はlongの値のみです。
  • BarBeatTimeSpan: 音楽的な小節番号と拍、さらにticksを用いて時間を表したものです。全ての構成要素が正の値である必要があります。
  • MathTimeSpan: 時間に対する演算の結果として生成される時間表現です。
  • MusicalTimeSpan: 音符の音楽的な表現(1/n分音符の倍数など)によって時間を表したものです。

これらのうちいくつかは楽曲のテンポの値やその変化によって値が変わってくるものであり、特定の種類の型に変換するためにはTempoMapという楽曲中のテンポの変遷を表すオブジェクトを引数に渡してやる必要があります。以下のコードはREADME.mdからの抜粋ですが、MIDIファイル上のticksで表された時間表現をミリ秒単位に変換するためにはテンポ情報が必要です。

TempoMap tempoMap = midiFile.GetTempoMap();
TimeSpan midiFileDuration = midiFile.GetTimedEvents()
                                    .LastOrDefault(e => e.Event is NoteOffEvent)
                                    ?.TimeAs<MetricTimeSpan>(tempoMap) ?? new MetricTimeSpan();

MusicTheory

Melanchall.DryWetMidi.MusicTheory名前空間には、音楽理論に基づいて音階を定義したり計算したりするメンバーが定義されています。たとえば…

  • Note: 音階名を定義したり音階を表現したりします
  • Interval: 1度〜12度までの2音階の開きを表します
  • Scale: NoteIntervalをもとに調を定義します

今回は深入りしませんが、Melanchall.DryWetMidi.Smf.Interaction名前空間には、さらにコードの計算などを行うChordManagerなどが定義されているので、興味がある人は調べてみるとよいでしょう(よく作られる類のAPIではないかと思います)。

他にも、GM/GM2音色などが定義されたクラスもあります。

Patterns/Actions

drywetmidiは高水準の音楽オブジェクトの編集のために作成されたライブラリのようで、「現在のコンテキスト」に大して、音符を加えたり、音符を移動したり、コードを設定したり、といったアクションと、その集合体であるパターンを表すAPIが定義されています。いわゆるDAWのようなものを作成したい場合に有用そうです。