本連載では,CrestMuseXML (CMX) APIを使ったプログラミングについてチュートリアル形式で紹介していきます.CrestMuseXMLは,CrestMuseプロジェクトで開発を進めている,音楽情報処理研究のためのXMLベースの共通データフォーマットです.本プロジェクトでは,単にデータフォーマットを定めるだけでなく,このフォーマットで書かれたデータを扱うためのJavaベースのクラスライブラリを提供しています.本連載は,このクラスライブラリを使って,簡単な音楽情報処理のプログラムを書けるようになることを目的としています.
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とは,上で述べたとおり,CrestMuseXMLに基づいて作成された各種音楽データの読み書きや処理をするためのクラスライブラリです.このクラスライブラリには,大きく分けて次の2つの目的があります.
図2 CMX APIの基本コンセプト.
上で述べた目的を達成するために,CMX APIは以下の方針で作られています.
図3 CMX APIのクラス図(簡略版).UMLによる表記.
まず,CMX APIをダウンロードしましょう.ダウンロードはここからできます.zipファイルとjarファイルがありますが,zipファイルはソースコードやjavadocで生成したドキュメントも一緒に含まれています.一方,jarファイルはJavaから直接(解凍せずに)実行できるようになっているものです.
CMX APIをクラスライブラリとしてのみ用いて,CMX APIの改変等を行う予定がないのであれば,jarファイルをダウンロードして,このjarファイルをJavaから「見える」ようにしておけばOKです.これには主に次の2つの方法があります.
CMX API自体も一部書き換えながら利用したい場合(現状ではその方が多いかもしれません)は,ソースファイルも含んだzipファイルをダウンロードして,適切なディレクトリ(以下,$CMX_HOMEとします)に解凍する必要があります.解凍すると以下のディレクトリが現れます.
%javac -d classes -sourcepath src src/jp/crestmuse/cmx/*/*.javaみたいな感じにすれば大丈夫なはずです.ちなみに,この「%」はコマンドプロンプトを意味します.UNIXに馴染のない人には申し訳ないですが,慣れてください).
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にはCrestMuseXMLドキュメントをラップするクラス(ファイルラッパクラス)とコマンドの雛型となるクラスがあると書きました.ここでは,具体的にどんなクラスが用意されているのか,もう少し細かく見ていきましょう.
Javaのクラスライブラリならなんでもそうだと思いますが,クラス構造を知るにはjavadocが生成したドキュメントを見ます.CMX API ver.0.21のドキュメントはここにあります.これを見ると,CMX APIは4つのパッケージにわかれていることが分かります.
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を利用してコマンドを作る場合,原則的にこのクラスを継承して作ることになります.このクラスの仕組みは次の項で詳しく述べたいと思います.
やっと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クラスでは,
以上で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.xmlCMXCommandを継承したコマンドでは,処理対象となるファイルをコマンドラインの引数に指定します.実行したら指定したファイル名が表示されてそのまま終わったかと思います.これはrunメソッドに何も書いていないからです.次に,2つのファイルを並べて指定してみましょう.
%java MyCommand1 renconsample-s001.xml renconsample-s002.xml指定したファイルが順番に表示されたことと思います.このように,ファイルを複数並べると順番に処理してくれます.ということは,UNIX系のシェルを使っているならば,ワイルドカードが利用できるということになります.これについては,省略します.
次に実際に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要素のテキストとが一致しているかどうか確認してみましょう.