A ♪SOUND mind in a SOUND <body> : Z-MUSIC for the Web

# はじめに
Z-MUSIC一式をJavaScriptから利用できるようにしたのでその解説です。Web AudioとWeb MIDI両方使ってます。

# Z-MUSIC
## ご存知ですか？
MML(Music Macro Language)ベースの音楽制作・演奏システムで、20世紀にはサウンドドライバと呼ばれていた類いのソフトウェアです。X68000上で動作し、Oh!Xで紙面展開していたため、X68000ではデファクト的な存在でした。なお、もう1つのデファクトであるMDXはパソコン通信を中心に広まっていたため、パソコン通信環境のないユーザーにとっては得てして貧弱な標準環境を除いてZ-MUSICが唯一の環境でした。

## 3つのバージョン
Z-MUSICにはv1系からv3系まで3つのバージョンがあります。v2までは互換性があり、v3で方針転換から互換性がなくなったため、最終的にはv2向けデータとv3向けデータの2種類が存在します。

## ファイル
制作にはZMS、ZMD、CNF、ZPDと4種類のファイルが使われます。それぞれ演奏データ、サンプリングデータに対してソースとバイナリに当たります。表にまとめてみました。

| \                 | ソース | バイナリ |
|:-----------------:|:-----:|:-------:|
|**演奏データ**       | ZMS   | ZMD    |
|**サンプリングデータ**| CNF   | ZPD     |

このうちCNFファイルはデータ制作者以外には馴染みがないかもしれません。Z-MUSICでは演奏データ（MML）に関してはソース公開が推奨されていたためZMSが一緒に配布されていることは良くありました。一方でサンプリングデータはZPDだけ配布されている事が多かったように思います。v1からv2にかけてはソースレベルでの高い互換性が保証されていた事もありますし、多くの演奏ツールがZMSからの高速な直接再生を実現していました。ゲームでの利用など極端に高い応答性を求めない限りはZMSで扱っていてもストレスを感じることはありませんでした。

# X68000以外の環境での利用
## MIDI制作環境として
2000年前後にはWindows向けなどで互換環境を提供するソフトが現れてきました。ただし主な用途はMMLを使ったMIDIデータ（SMF）制作であり、内蔵音源を使った既存曲の再生を考慮したものではありませんでした。また実機でv2からv3への移行が進まなかったのとは対象的に、Windows向けの環境ではv3がそれなりに使われていた気がします。これはv2がゲームの組み込みを意識して最適化されていたのに対し、v3がより充実した音楽制作機能へと舵を切った（速度より機能を重視した）結果かと思います。

## オリジナル音源再生向けとして
実はあまり存在していなかったようです。ZMDRIVEというWindows向けのソフトが唯一でしょうか。他にはX68000エミュレータ上で再生するという方法がとられてきました。

# Z-MUSIC for the Web
## 仕組み
run68と呼ばれるX68000向けのCUIをDOS上で走らせるツールがありました。これをEmscriptenを用いてJavaScriptにコンパイルしています。またZ-MUSICを動かすためにDOS CALL、IOCS CALL、I/Oアクセス、trap命令などをフックして適宜エミュレーションを追加、入れ替えしています。このあたりの技術詳細については[SION2 HD](https://bit.ly/sion2)と合わせて後日記事を書く予定です。

## 動作バージョン
v1.10とv2.08が動作するようになっています。SION2添付バージョンとv2系（たぶん）最後の公式リリースです。おそらく他のバージョンもそのまま動かせるとは思いますが、特に必要もないと思うので今のところこの2バージョンの対応だけを考えています。

## 対応音源
内蔵FM音源（OPM）とADPCMについてはX68Soundを移植して鳴らしています。また内部的にPCM8のドライバを偽って直接X68Soundを叩くPCM8互換ドライバを内蔵しており、Z-MUSICはこのX68Soundと直結したPCM8を制御下においてPCM再生を行っています。

MIDIにも対応済みです。Web MIDIを使って繋がっている出力デバイス全てに対してメッセージをブロードキャストしています。WindowsではOS標準のソフトウェア音源がセキュリティの懸念から使用できませんが、実ハードを繋げばオリジナル同様に演奏可能なはずです。

ちなみにZ-MUSICからは仮想的なMIDI音源ボードに対してバイトストリームのデータを送りつけてきます。Web MIDI APIはメッセージ単位での送信を受け付け、ランニングステータスは受け付けない・正しいメッセージ以外はエラーで送れない、などの安全弁がついているため、それに合わせてJavaScriptでバイトストリームを正しいメッセージ列に変換する必要がありました。ランニングステータス以外にもリアルタイムメッセージの割り込みなども正しく分離する必要があり、それなり面倒な処理になるかと思います。僕自身、色々な環境で何度も書いてきたコードですが、いまだにバグを仕込むことが多々あります。という事で、バイトストリーム→メッセージ変換のコードも参考になるようなら適宜再利用して頂けたら、と思います。ライブラリみたいな形で分けてはいませんが[prolog.js](https://github.com/toyoshim/z-music.js/blob/master/src/prolog.js)の中のmidiOut()という関数を見てもらえれば良いかと思います。

またZ-MUSICにはピッチベンドの際に時折計算を間違えてデータ2バイトの最上位ビットが立ってしまう事があるようです。またv2ではランニングステータスを活用しています。これらは上記のライブラリの層で訂正・展開して送信しています。

## 利用方法
```html:標準的な使い方
<script src="zmusic.js"></script>
<script>

// zmdとzpdにArrayBufferとしてデータを格納済みとする

ZMUSIC.install().then(() => {
  ZMUSIC.play(zmd, zpd);
});
</script>
```

基本はこれだけです。実装上の問題で複数インスタンスに対応していないため、このようなインターフェースになっています。

ZPDが不要な場合、あるいは前回再生した曲の物を使いましたい場合には省略できます。

```js:ZPDを省略
ZMUSIC.install().then(() => {
  ZMUSIC.play(zmd);
});
```

ZPDだけ設定したい場合には第一引数をnullやundefinedなどを入れてください。

```js:ZPDだけ設定
ZMUSIC.install().then(() => {
  ZMUSIC.play(null, zpd);
});
```

ZMSを再生するにはcompileAndPlayを呼びます。

```js:ZMSを再生
ZMUSIC.install().then(() => {
  ZMUSIC.compileAndPlay(zms);
});
```

install()には使いたいZ-MUSICのバージョンや、任意の引数を指定する事もできます。ワークを取らないとZMSの内部コンパイルに失敗するので注意してください。

```js:SION2向けの設定例
ZMUSIC.install(['ZMUSIC110.X', '-n', '-u', -'t0', '-w0']);
```

インターフェース向けのドキュメントはまだ整備できていないのですが、[こちら](https://github.com/toyoshim/z-music.js/blob/master/src/prolog.js)のソースを見ればアノテーションから必要な事は読み取れるかと思います。

## 開発ページ
GitHub上で[z-music.js](https://github.com/toyoshim/z-music.js)としてリポジトリ管理しています。再利用やバグ報告などお待ちしております。また、対応した音楽プレイヤー等を作ってみたい人がいればAPI追加等ご相談にのります。FM音源レジスタの値を取得する、波形を取得するなども対応できるかと思います。