Scriptを使ってMIDIを流してみる 技術メモ

 ヴィジュアルリプレイ製作のために、Flashからmidiファイルを操作する方法を研究していたのですが、その途中で判明した事柄についてメモしています。
 このメモよりマトモに、Flashからmidiを鳴らす方法について解説したページはこちら
 基本的には、FlashからJavaScript・VBScript・JScriptを経由して、midiを流したりフェードアウトさせたりします。(実際に作成したもの
 このページはあくまでメモ書きにすぎません。他の人が読んだ時に分かり難い箇所があるかも知れません。予めご了承下さい。
 (ひょっとしたら間違っている箇所もあるかも知れないので、鵜呑みにはしないで下さい)
 尚、Flashソフトが古いため、AS2.0の内容になっています。ご注意下さい。
 下の方が古く、上の方が新しくなっています。


☆Vista版WMPの問題
 WindowsVista版WindowsMediaPlayerでは、JScriptにより音量セットが上手く行かない。少なくとも、XP版WMPを動かしていたスクリプトでは動かない。
 これはVistaでアプリケーションごとにユーザーが音量を変更できるようになった、仕様が原因であるという見方が有力のようだ。
 違う方法で音量が操作できるという噂も聞いたのだが、未調査。
 とりあえずVistaの場合は、IEでもQTを利用することにした(QTは支障なく動作する模様)。

☆読込済みBGMの管理
 今回BGMの読込は、「読込が必要となるBGMの番号」、「読込済みの番号」、「早送り/巻き戻しの方向」の3つの要素で管理する方法にしてみた。
 割と単純に管理できそうなものだったが、しかしこの3要素を状況状況によって正しく指定できず、バグの温床地帯となってしまった。
 主な原因は、この三つの要素をJavaScript・ActionScript間できちんと共有しなかったため。
 そんなに難しいことではなかったのだけれども、少し面倒がってしまったのが敗因。
 JavaScriptで動くmidiとActionScriptで動くmp3を切り替えられるようにするのだから、読込に関係するパラメーターはきちんと共有すべきだった。
 反省。

☆プラグイン書き出し後すぐの再生
 QTは、プラグインをinnerHTMLを利用して書き出した直後に再生を指示すると、正常に曲をロードできないケースがあるようだ。
 再生前にsetTimeoutなどで0.1秒ほど待つと、きちんとロードする。

☆プラグインをページ内に書き出す
 プラグインはJavaScriptのdocument.writeを使ってページ内に書き出すことができる。これを利用してブラウザやユーザーの選択ごとに違うプラグインをページ内に読み込むことができる。
 ただし、色々と問題が多い。
 ページが読み込まれた時にdocument.writeが実行される分には、特に問題ない。
 しかしIEの場合、documnet.writeを関数内に記述すると、関数を実行した時に新しいページが開かれ、そこに書き出されてしまう。
 Fxの場合、関数内に記述しても同じページ内に書き出されるが、どこに書き出されるのかは一定でないらしい(特にオブジェクトを書き出した場合)。関数の実行されるタイミングとページの読込の進捗との差により、場所が変わるのではないかと思う。
 ただし、documnet.write実行後にページを再読込すると、プラグインしか表示されない現象が発生することがある。

 一般的なテキストの場合、DOMを利用することでこの問題を解決することができる。
 しかしobjectを書き出す場合、DOMでは上手く行かないらしい。ブラウザのバグの温床地帯だとか言う説もあるが、原因がバグなのかそもそもそういう仕様なのか、良く分からない。
 innerHTMLを使うことでこの問題は解決できるが、IE以外のブラウザにはinnerHTMLが上手く動かないものもあるらしい。
 とは言いつつも、今のところFireFoxでは問題なく動いているので、innerHTMLを採用している。

☆SetVariableで渡せる値はストリング
 JavaScript側からFlash側に値をセットできるSetVariableだが、渡せる値はString。
 例え受け取った値が「true」であっても、それは"true"という文字列であって、Booleanでない。
 Booleanと勘違いして「あれ、上手く動かないな?」というのは、特にしっかり値をモニターしている時にこそ起こりがちな失敗なので、注意。
 尚、IEもしくはVBScriptのどちらかを経由した変数の場合なのか、"True"とTが大文字になるケースがあるようである。全て小文字に変換しておくのが良いだろう。
 Stringなので、大文字小文字にもちょっと注意が必要である。

☆FlashとJavaScriptの通信失敗
 htmlが初めて開かれた時(キャッシュが存在しない時)、FlashとJavaScriptの間の連携がロード後すぐ実行されるようになっていると、連携に失敗することがある(特にIE7)。
 原因は、Flash側の読み込みが終っておらず準備が終了していなかったり、JavaScriptやJavaScriptが呼び出すプラグインが準備できていなかったりするせいだと見られる。(Flashではこのトラブルを良く見る気がする)
 1秒か2秒でこの準備は終了するようだが、時間がいつもどの環境でも一定という訳ではないようだ。
 解決策として、JavaScriptからFlashに変数を渡す関数を、Flash側からonEnterFrameで呼び出し、変数が渡ったことを確認してからFlashの処理を進める形を取った。

☆FlashからJavaScriptを呼び出す
 AS2.0の場合、いくつか方法がある。
 ・ExternalInterfaceクラス
 ・getURL
 ・fscommand

 このうち、ExternalInterfaceが一番便利らしいのだが、FlashPlayer8.0以上対応。PolarBlueの開発環境は7.0なので非対応(苦笑)。
 その他の二つは7.0以前に使われていた方法。
 getURLは手軽だが、IEではgetURLでJavaScriptを呼び出す度にmidiの演奏が停止するという問題点がある(ページ遷移と認識されているらしい)。
 少し手間だがfscommandが良い。ただしIEではfscommandではJavaScriptが呼び出せないようで、一回VBScriptをfscommandより呼び出し、それを経由してJavaScriptへ連結させるのが良い。
 (FxでFireBugを利用していると、fscommandが上手く動かなくなるケースがあるようだ)
 それぞの具体的な方法は、リンク先を参照。

☆何も存在しない要素を読み込んでしまう
 配列にBGMファイル名のリストを入れておき、それを順次先読みする場合の話。
 配列の最後の要素の先読みが終った後に、先読みの指示をスクリプトが出しても、QuickTimeは特に問題なく無視してくれる。しかしWindowsMediaPlayerの場合、「再生しようとしているファイルの拡張子( )がファイル形式と一致しません」と言うエラーメッセージが出る。
 先読みは現在のBGMの再生直後に開始するようにスクリプトを書いているので、あたかも今再生しているBGMに問題があるような印象になることに注意。
 上記エラーメッセージで( )内が空白の場合は、何も存在しない要素を読み込む指示が出ていて、それが原因のエラーである可能性が高い。
 対策は、配列の要素を全て先読みした場合、先読みを実行しないこと。
 余談ながら、次の曲へ移動する関数の実行にも、配列の長さで制限をかけた方が良いかも知れない。

☆QuickTime Plug-in オブジェクトが複数ある場合
 midiの曲データー内のパラメーターは、その曲がセットされているオブジェクトだけでなく、他のオブジェクトの設定にも影響を与える模様。
 従って、あるオブジェクトの再生中に、違うオブジェクトにリセットをかけてからそのオブジェクトに曲をロードする(次の曲ではリセットしたオブジェクトで再生)、という方式ではリセットは上手くかからない(再生中の曲の影響がリセット後にも残る)。
 それを逆手に取る事もできる。一つリセット専用のオブジェクトを配置し、他のオブジェクトの再生直前にリセット専用オブジェクトでリセットをかける。するとPlayしようとしているオブジェクトでリセットを実行しなくても、上手くリセットできるらしい。
 再生するオブジェクトでリセットをかけようとすると、折角先読みしたオブジェクトにリセット用midiをSetURL()し、その後再び曲をSetURL()し直すという羽目になる。が、この方式ではその手間をかけずに済むというメリットはある。
 ただ、いかにも裏技っぽいので、QTのバージョンアップで使えなくなる懸念がある。

☆midiリセット
 midiはリセットという動作を行わないと、音のバランスや音色がおかしくなることがある。
 midiデーター自体にもリセットの指示を埋め込めるが、midiの作者によっては(意図的/意図せず)リセットを埋め込まないことがある。
 その場合、利用者側でmidiリセットを行う必要がある。
 PolarBlueで利用している/利用予定の音楽素材サイトの曲は、大抵リセットをかける必要がある模様。
 もっとも、QuickTimeなどのプラグインを一つだけ読み込み、順次曲を再生させて行く場合には、リセットが必要になるケースはあまり無いようだ(自動的にリセットしている?)
 しかし複数のプラグインを配置して順次曲をロードしたり再生したりする場合、QuickTimeではリセットが上手くかからない事がある。(WindowsMediaPlayerは大丈夫だと思われる)
 音源リセットをするための曲を提供してくれているサイトがいくつかある。
 今回は「FANTATHEATER //annex//」の「GMreset.MID」を利用させてもらった。
 尚、リセット用midiを流した後、1秒程度待たないとリセットがきちんと適用されないらしい。
 参考:シッタカS岡の…出張版知恵袋 本当に最終回 - MIDIリセットの重要性を知ろう!

☆ループ
 単純にloopをtrueにしただけでは、loop時に曲が途切れ、スムーズにループしない。
 演奏の起動などに時間がかかるようだ。

 対策として次の方法を試してみた。
 1.プラグインを二つ読み込んでおく。
 2.一つ目のプラグインの演奏終了が近づいたら、起動時間分だけ二つ目のプラグインにフライングさせて演奏開始。

 しかし、上手く行かなかった。
 ・Windows Media Playerの場合、何度か再生しているうちに再生速度がおかしくなった。しかしWMP自身は正常なrateで演奏していると認識しているらしい。曲と曲の間に間が空くとこの現象は発生しない。リセットのエラーか、システムへの過負荷?
 ・QuickTimeの場合、曲の末尾の音が延びて変な音になる。こちらもシステムへの過負荷だろうか?

 現時点では解決策が見当たらないので、残念ながら途切れ目のないループは諦めた。
 曲の末尾近くでフェードアウトしてからループさせることにした。

☆Time
 WMPのSetTimeは秒単位でしか指定できない模様(小数が指定できない)。
 QTは単位が可変らしい。BGMの拍によって異なる?
 ちなみに一つDurationで曲の長さを測ってみたら、156秒の曲に93377を返した。その曲は1/1667秒単位らしい。何それ。

☆WMPのduration
 WindowsMediaPlayerのcurrentMedia.duration(メディアの長さの取得)は、再生開始後少し経ってからでないと取得できない。
 1000ミリ秒後に取得するようにしたら、上手く取得できた。もっと早くてもできるかも知れない。

☆URLのset
 WMPもQTも、スクリプトでsetURLすると曲が自動的に再生される。
 その際、AutoPlayのパラメーターは無視されてしまう?
 先読みする場合、直後にstopさせる必要がある。
 (ただしタグでプラグインを埋め込んだ場合には、autoplay要素で制御可能)

 尚、QTはタグでプラグインを埋め込む時、必ずURLを指定しないと動かないようだ。

☆BGMリストの入ったjsファイル(bgmlist.js)が下位フォルダにある場合。
 各BGMファイルのパスは次のようになる。
 QuickTimeはbgmlist.jsからの相対パス
 WindowsMediaPlayerはプラグインを埋め込んだhtmlからの相対パス

☆プラグインの検出
 IEはVBScriptでないと検出できない。JavaScriptのNavigatorクラスにIEで機能しないものがあるらしい。
 FxはJavaScriptで行える。

☆QuickTimeのStop
 QuickTimeのStop()は、再生時間が保持される。
 Stopしてから再生すると、続きから再生されてしまう。
 Rewind()で頭に戻れるので、頭に戻ってから再生するようにすると良い。

☆WMPの音量セット失敗
 WindowsMediaPlayerは、オンライン上で再生する時、音量セットに失敗することがある。
 曲ごとの音量を外部js内の配列に記述していて、かつ操作するプラグインをスクリプトで自動生成させ、そこにアクセス…という操作をしているのだが、どうやら操作が多すぎて曲の再生命令が実行されるまでに音量セットが終らないらしい。
 音量セットを再生後にも複数回実行させることで、改善はした。
 ただし極度に速度が遅い環境では、これでも上手く行かないことがあるかも知れない。
 音量がセットしてあることをチェックしてから再生させるように改めた方が安心か。

☆音量
 QuickTime … 0~256
 WindowsMediaPlayer(ver7以降?) … 0~100
 最大音量に対して同じ割合を指定しても、再生音量がどうも違う。
 音量の基準として参照しているものが違う? 最終的にはユーザーに調整してもらうのが良いか。



 参考サイト:  

Visual Replay Makingに戻る