CMX API ver.0.21チュートリアル
第1回「はじめの一歩」

北原 鉄朗(JST CrestMuse/関西学院大学理工学研究科)
2007年9月12日

はじめに

本連載では,CrestMuseXML (CMX) APIを使ったプログラミングについてチュートリアル形式で紹介していきます.CrestMuseXMLは,CrestMuseプロジェクトで開発を進めている,音楽情報処理研究のためのXMLベースの共通データフォーマットです.本プロジェクトでは,単にデータフォーマットを定めるだけでなく,このフォーマットで書かれたデータを扱うためのJavaベースのクラスライブラリを提供しています.本連載は,このクラスライブラリを使って,簡単な音楽情報処理のプログラムを書けるようになることを目的としています.

CrestMuseXMLとは

CrestMuseXMLは,上で述べたとおり,音楽情報処理研究のための共通データフォーマットです.これに関する詳細情報はここにありますので,ぜひご覧ください.特に音楽情報科学研究会での発表論文が最もきちんと書いておりますので,ご一読されることをお勧めします.

CrestMuseXMLは,実はCrestMuseXMLという1つのXMLフォーマットがあるわけではありません.そうではなく,様々なXMLフォーマットが集まってCrestMuseXMLが出来上がっています(図1).これは拡張性を考慮した結果で,複数のXMLフォーマットが集まって音楽の様々な側面を表現することで,そこに新たなXMLフォーマットを追加することで,容易に仕様を拡張することができます.もしも,あらゆる側面の音楽データを1つのXMLフォーマットで表現されているとすると,そこに新たな仕様を追加しようと思ったら,XMLフォーマットをいじらなければなりません.そうすると,個々の研究者が自分の事情に合わせて個々に拡張するのは難しくなってしまいます.それに対して,CrestMuseXMLでは個々の研究者が自由にXMLフォーマットを追加できるようにして,拡張性を確保しています.この連載で解説しますCMX APIはCrestMuseXMLに含まれる各種XMLフォーマットを統一的に扱う仕組みを提供しますので,複数のXMLフォーマットを扱うためにわずらわしくなることもありません.


図1 CrestMuseXMLの全体像.

CrestMuseXMLはこれからどんどん仕様を充実させていくわけですが,2008年に国際会議ICMPC 10のワークショップとして開催される演奏生成コンテストRenconで利用される予定になっていることから,演奏生成(楽器制御パラメータ)に関する部分から仕様策定を始めています.CrestMuseXMLに現在含まれているXMLフォーマットを列挙すると以下のようになります.

以下,それぞれについて簡単に説明していきます.

CMX APIの概要

CMX APIとは,上で述べたとおり,CrestMuseXMLに基づいて作成された各種音楽データの読み書きや処理をするためのクラスライブラリです.このクラスライブラリには,大きく分けて次の2つの目的があります.

なお,以下,CrestMuseXMLに含まれる各種のXMLフォーマットに基づいて音楽データを記述したものを「CrestMuseXMLドキュメント」と呼ぶこととします. 多くの場合,音楽情報処理システムというのは,何らかの音楽データを入力し,それを処理(加工)し,その結果を出力するという風になっています.ここでは,入出力データ形式をCrestMuseXMLとし,CrestMuseXMLドキュメントを読み込んで何らかの処理をして結果をCrestMuseXMLドキュメントとして書き出す,という一連の流れを雛型として提供します(図2). ただし,データを入力しながら即座に処理をして出力をしていくというリアルタイム型のシステムは今回は考えず,次回以降のバージョンアップ時にそれ用のAPIを提供していく予定です. また,本連載では,GUIではなく,コマンドラインからファイル名やオプションなどを指定する方式(CUI)を想定します.


図2 CMX APIの基本コンセプト.

上で述べた目的を達成するために,CMX APIは以下の方針で作られています.

ファイルラッパクラスはXMLフォーマットごとに用意されます.たとえば,MusicXMLであればMusicXMLWrapperクラス,DeviationInstanceXMLであればDeviationInstanceWrapperクラスというのが提供されます.これらのクラスは共通の構造となっており,共通の基底クラスCMXFileWrapperを継承して設計されています.また,コマンドのための共通基底クラスCMXCommandでは,コマンドライン上で指定された名前のファイルを読み込んでCMXFileWrapperクラス(正確にはそのサブクラス)のオブジェクトを生成します.その後,抽象メソッドrunを呼び出し,最後に指定された出力用オブジェクトをファイル(または画面)に出力します.そのため,ユーザはrunメソッドをオーバーライドし,ここに行いたい処理を書いて,あとは少しのメソッドさえ記述すればコマンドが完成できるようになっています(図3).


図3 CMX APIのクラス図(簡略版).UMLによる表記.

準備1:CMX API ver.0.21の用意

まず,CMX APIをダウンロードしましょう.ダウンロードはここからできます.zipファイルとjarファイルがありますが,zipファイルはソースコードjavadocで生成したドキュメントも一緒に含まれています.一方,jarファイルはJavaから直接(解凍せずに)実行できるようになっているものです.

CMX APIをクラスライブラリとしてのみ用いて,CMX APIの改変等を行う予定がないのであれば,jarファイルをダウンロードして,このjarファイルをJavaから「見える」ようにしておけばOKです.これには主に次の2つの方法があります.

Eclipseをお使いの方はEclipse上で設定できます.これについては橋田さんに解説ページを作っていただこうと考えています.ご期待ください.

CMX API自体も一部書き換えながら利用したい場合(現状ではその方が多いかもしれません)は,ソースファイルも含んだzipファイルをダウンロードして,適切なディレクトリ(以下,$CMX_HOMEとします)に解凍する必要があります.解凍すると以下のディレクトリが現れます.

実行時に参照するのはクラスファイルですので,$CMX_HOME/classesにCLASSPATHを通す必要があります.また,ソースをいじってコンパイルするときはクラスファイルが$CMX_HOME/classesに生成されるようにしてください(たとえば,$CMX_HOME上で
%javac -d classes -sourcepath src src/jp/crestmuse/cmx/*/*.java
みたいな感じにすれば大丈夫なはずです.ちなみに,この「%」はコマンドプロンプトを意味します.UNIXに馴染のない人には申し訳ないですが,慣れてください).

準備2:XercesとXalanの用意

CMX APIではXMLドキュメントを自前でパースしているわけではなく,外部のXMLパーサを利用しています.CMX APIでは一応JAXPというAPIにしたがって実装しているので,本当は自分の好きなXMLパーサを利用できるはずですが,開発担当の我々が利用しているという点で,Xercesを使うことをおすすめします.XercesはApache XML Projectが提供しているオープンソースのXMLパーサです.まずはこれをダウンロードしましょう.

もう1つ,XalanというXSLTプロセッサも必要です.CMX APIでは,Xalanに含まれる機能のうちXPath処理に関する部分を利用します.ここの部分に関しては,Xalanのクラスを直接呼び出しており,必ずXalanを利用する必要があります(上のJAXPのように特定のプロセッサに限定されないような書き方ができるはずで,将来的にはそうする予定ですが,いまのところはそうなっていません).ということで,これもwebページからダウンロードしましょう.

XercesとXalanをダウンロードしたら,解凍しましょう.そうすると,jarファイルがたくさん出てくるので,それを上で述べたいづれかの方法でJavaから「見える」ようにしましょう(どうやらすべてのjarファイルがいるわけではないようですが,どれがいるのか,どれがいらないかはまだ僕自身は把握してません).

CMX APIのクラス構造を知ろう

「CMX APIの概要」では,CMX APIにはCrestMuseXMLドキュメントをラップするクラス(ファイルラッパクラス)とコマンドの雛型となるクラスがあると書きました.ここでは,具体的にどんなクラスが用意されているのか,もう少し細かく見ていきましょう.

Javaのクラスライブラリならなんでもそうだと思いますが,クラス構造を知るにはjavadocが生成したドキュメントを見ます.CMX API ver.0.21のドキュメントはここにあります.これを見ると,CMX APIは4つのパッケージにわかれていることが分かります.

以下,パッケージ名に言及するときは「jp.crestmuse.cmx」は省略します. commandsパッケージには,文字どおり「コマンド」に関係したクラスが入っており,コマンドを作るための雛型となる抽象基底クラスCMXCommandの他,CMXCommandを継承して作られたコマンドがいくつか納められています.このパッケージに入っているコマンドはそれ自体有用なだけでなく,CMXCommandを継承してコマンドを作る方法のサンプルとしても有用といえます.filewrappersパッケージには,文字どおりファイルラッパクラスとそれに関係するクラスがふくめられています.handlersパッケージには「XXHandler」という名前のインターフェイスがいくつか入っていますが,これについては後日改めて触れます.miscパッケージには,CMXCommandやファイルラッパクラスなどが利用する様々なクラスが含まれています.

filewrappersパッケージをもう少し詳しく見てみましょう.まず,CMXFileWrapperというクラスがあります.CMXFileWrapperクラスは抽象クラスになっていて,addChildなど様々なメソッドが定義されています.個々のメソッドについては必要に応じておいおいやっていきます.MusicXMLWrapper,DeviationInstanceWrapper,MIDIXMLWrapper,SCCXMLWrapperクラスはそれぞれMusicXML,DeviationIntanceXML,MIDI XML,SCCXMLドキュメントをラップするするもので,いずれもCMXFileWrapperクラスを継承して作られています.また,これらのクラスの各々に様々な内部クラス(入れ子のクラス)があることがわかります.たとえば,MusicXMLWrapperにはNoteという内部クラスがあります(以下,これを「MusicXMLWrapper.Noteクラス」と書きます).これはMusicXMLドキュメントのnote要素をラップするものです.DeviationDataSetクラスはdeviation情報を一時的に格納するためのクラスで,この連載で後日改めて取り上げます.NodeInterfaceクラスは各ファイルラッパクラスに含まれる内部クラスの基底クラスとなっており,ここでは詳細の説明は省略します.

次に,commandsパッケージを見てみましょう.commandsパッケージにはCMXCommandクラスという抽象クラスがあります.CMX APIを利用してコマンドを作る場合,原則的にこのクラスを継承して作ることになります.このクラスの仕組みは次の項で詳しく述べたいと思います.

CMXCommandを継承してコマンドをつくろう

やっとCrestMuseXMLおよびCMX APIの中身の説明が終わりました.さっそく,CMX APIを使って簡単なプログラムを作ってみましょう.ここでは,MusicXMLドキュメントを読み込んでその内容を画面に表示するという至極簡単なものを取り上げたいと思います.上で何度も述べた通り,コマンドを作るにはCMXCommandクラスを継承します.そこで,以下では説明モードに逆戻りしてCMXCommandクラスがどういう設計になっているかを簡単に述べたいと思います.

CMXCommandクラスはいわゆるTemplate Methodパターンに基づいた設計になっています.Template Methodパターンは処理の流れだけをあらかじめ基底クラスで記述しておき,個々のステップでの処理内容をサブクラス側で記述するというものです.つまり,「Aの後にBを実行し,さらにCを実行する-----これをファイルの数だけ繰り返す」といった全体の処理の流れがすでに基底クラスで実装されているのですが,A,B,Cといった処理の名前だけ決まっていて,その中身は未定義になっており,中身はサブクラス側で定義するというのがTemplate Methodパターンです.個々のステップに対応するメソッドが基底クラスでは抽象メソッドなどになっており,それをサブクラスがオーバーライドすることで,これを実現します.このメカニズムは,別にデザインパターンを参考にしたわけでなく,自分で編みだしたわけですが,GoFによる23個のデザインパターンの1つになっているところから,どうやらいろいろな場面で使われている典型的な仕組みのようです.

CMXCommandクラスでは,

というメソッドが用意されており,これらのメソッドを決められた順序でstartメソッドが呼び出します.startメソッドは,次のように振る舞います.
  1. preprocメソッドを呼び出す.
  2. コマンドライン引数に指定されたファイル名を1つ取得し,そのファイルを読み込む.
  3. runメソッドを呼び出す.
  4. 処理結果をファイルに書き込む(または標準出力に書き出す).
  5. コマンドライン引数に指定されたファイル名すべてに対して2〜4を繰り返す.
  6. postprocメソッドを呼び出す.
なお,runメソッドは抽象メソッドとなっており,継承する上で必ずオーバーライドする必要がありますが,preproc,postprocメソッドは何もしないメソッドとして実装されており,特に前処理,後処理が必要なければオーバーライドする必要はありません. この設計により,読み込んだファイルをどう処理するかをrunメソッドに書きさえすれば,コマンドを完成させることができます(ただし,startメソッドを呼び出すmainメソッドは必要).

以上でCMX APIの大まかな構造を理解したところで,とりあえずCMXCommandを継承したクラスを作ってみましょう.


import jp.crestmuse.cmx.commands.*;

public class MyCommand1 extends CMXCommand {
  protected void run() {
    // ここに何かを書く
  }

  public static void main(String[] args) {
    MyCommand1 c = new MyCommand1();
    try {
      c.start(args);
    } catch (Exception e) {
      c.showErrorMessage(e);
      System.exit(1);
    }
  }
}
コンパイルが成功したら実行してみましょう.
%java MyCommand1 renconsample-s001.xml
CMXCommandを継承したコマンドでは,処理対象となるファイルをコマンドラインの引数に指定します.実行したら指定したファイル名が表示されてそのまま終わったかと思います.これはrunメソッドに何も書いていないからです.次に,2つのファイルを並べて指定してみましょう.
%java MyCommand1 renconsample-s001.xml renconsample-s002.xml
指定したファイルが順番に表示されたことと思います.このように,ファイルを複数並べると順番に処理してくれます.ということは,UNIX系のシェルを使っているならば,ワイルドカードが利用できるということになります.これについては,省略します.

runメソッドに処理を書いてみよう

次に実際にrunメソッドに何か書いてみましょう.runメソッドが呼び出される時点ではすでに指定されたファイルは読み込まれています.読み込まれたファイルの内容は,indata()メソッドを介してCMXFileWrapperオブジェクトとして取得できます.ただし,正確には読み込まれたファイルの形式に応じたサブクラスのオブジェクトで,たとえばMusicXMLファイルを読み込んだのであれば,実態はMusicXMLWrapperオブジェクトとなっています.ここでは,MusicXMLファイルを前提としてますので,indata()メソッドで得られたCMXFileWrapperオブジェクトをMusicXMLWrapperにダウンキャストします.


import jp.crestmuse.cmx.commands.*;
import jp.crestmuse.cmx.filewrappers.*;

public class MyCommand1 extends CMXCommand {
  protected void run() {
    MusicXMLWrapper musicxml = (MusicXMLWrapper)indata();
    if (musicxml.hasMovementTitle())
      System.out.println(musicxml.getMovementTitle());
  }

  public static void main(String[] args) {
    MyCommand1 c = new MyCommand1();
    try {
      c.start(args);
    } catch (Exception e) {
      c.showErrorMessage(e);
      System.exit(1);
    }
  }
}
MusicXMLドキュメントでは,score-partwise要素の子としてmovement-titleという要素を持てるので,持っていればその中身を表示するというものです.打ち込んでみたら実際に実行してみましょう.実行結果とファイルの中身のmovement-title要素のテキストとが一致しているかどうか確認してみましょう.

おわりに

連載形式でお送りしてきましたCMX APIチュートリアルですが,第1回の今回はCrestMuseXMLおよびCMX APIの説明に終始し,ごくごく簡単なプログラミングを体験していただきました.次回は,MusicXMLドキュメントから音楽データを取得する方法について解説していきたいと思います.