Soundtrack AB Repeat Player作成時のメモ
前回の記事の続きです。
どういう要素を寄せ集めて作ったのか個人的なメモがてら書き残していこうと思います。
デバッグについて
まずはHTAのデバッグ方法です。
単純にHTAファイルを起動してしまうとコンソールのログ等を確認することができません。
ファイル自体は単なるHTML + CSS + Javascriptのファイルなので、IEでデバッグが行えればひとまず何とかなりそうです。
単純にIEでHTAファイルを開こうとすると別ウインドウでHTAが起動してしまいうまく行きません。
そこで拡張子を「.html」にリネームするとIEで開いてデバッグすることができます。
しかしデバッグのたびに拡張子を「.html」に変更するのはなかなか面倒です。
そこでコマンドプロンプトを管理者権限で起動し、以下のコマンドを打ちます。
cd {HTAファイルがあるディレクトリ}
mklink /h {ファイル名}.html {ファイル名}.hta
するとHTAファイルへのハードリンクが「.html」の拡張子で作成されるため、あとはIEでこのハードリンクを開いてあげれば
- 動作確認時:「.hta」ファイルを起動させる。
- デバッグ時:「.html」ファイルをIEで開く。
という感じでファイルをリネームすることなく使い分けすることができます。
プレイヤーについて
次に実際に曲を再生するプレイヤー部分をどうするかですが、HTAで動かす際に裏でIEが動いているので
それならばWindows標準のものを使っちゃおうということで「Windows Media Player 6.4」を埋め込むことにしました。
こんな感じでobjectタグで埋め込むことができます。
<object id="player" classid="CLSID:22D6F312-B0F6-11D0-94AB-0080C74C7E95">
<param name="FileName" value="c:\xxx\sample.wma" />
<param name="AutoStart" value="true" />
<param name="ShowPositionControls" value="false" />
</object>
どこまで対応しているかわかりませんが、
- wma
- wav
- mp3
- mp4
- mid
は再生できることを確認しました。おそらくWindows Media Playerで再生できるファイルは一通り再生できるんじゃないかと思います。
プレイヤー部分が用意できたら後はプレイヤーをコントロールする仕組みが必要です。
WebArchiveにマニュアルが残っていました。
色々と制御するための仕組みが用意されており、今回は
- PlayStateChangeイベント
- 再生状態が変わったときに呼ばれるイベント。
- EndOfStreamイベント
- 曲の終わりまで再生しきった際に呼ばれるイベント。
- FileNameプロパティ
- 再生するファイルパスの取得&設定ができる。
- CurrentPositionプロパティ
- 再生位置の取得&設定ができる。
- Playメソッド
- 再生開始用のメソッド
- Stopメソッド
- 再生停止用のメソッド
などを使って、定期的に再生位置を監視しながらA-B間再生の機能を実現しました。
ローカルファイルの読み書きについて
プレイヤー単体ではここまでの仕組みがあれば実現できますが、アプリケーションの設定ファイルやプレイリストの内容を保存できないとなかなか面倒です。
Javascriptでローカルのファイルを操作するための仕組みとしてHTML5で追加されたFile APIがありますが、
これはinput[type=file]等でユーザーが選択したファイルにしかアクセスできないため、
「起動時に自動的に設定ファイルを読む」ということができません。
そこでActiveXの力を借ります。
var fso = new ActiveXObject("Scripting.FileSystemObject");
// 書き込み
var stream = fso.CreateTextFile(".\\setting.txt", true);
stream.WriteLine("書き込む内容");
stream.Close();
// 読み込み
stream = fso.OpenTextFile(".\\setting.txt");
while (!stream.AtEndOfStream) {
var line = stream.ReadLine();
console.log(line);
}
stream.Close();
という感じでファイルの読み書きが行えます。
FileSystemObjectでファイル操作をする際に相対パスでファイル名を指定すると
- 「.hta」ファイルで起動した場合:起動した「.hta」ファイルがあるディレクトリから相対指定
- 「.html」ファイルでIEで動かした場合:デスクトップから相対指定
となるようです。
この挙動の違いはデバッグ時に少々面倒なのでいつか統一できる方法を見つけたいところです・・・。
ファイルの複数選択について
再生したいファイルをinput[type=file]で選択する際に1ファイルしか選べないと不便なので複数ファイル選べるようにしました。
input[type=file]タグに「multiple="multiple"」属性を指定するとIEでもファイルを複数選ぶことができます。(※IE 10以上)ファイルを複数選んだ後にJavascriptで
console.log(document.getElementById("ファイル選択要素のID").value);
とやると
C:\dir1\dir2\file1.wav, C:\dir1\dir2\file2.wav
というように「カンマ + スペース1つ」区切りでファイルのパスを取得することができます。
※HTMLファイルやHTAファイルを直接開いている場合のみこのようなパスが取得できます。
WEBサーバーで配信されたHTML上で同じことをやると「C:\fakepath\ファイル名.wav」というように「fakepathに置き換えられてしまいます。
これを1ファイルずつに分解するために単純に
"取得したファイルパス".split(", ");
とやって分解してしまうとディレクトリ名やファイル名に「カンマ + スペース1つ」が入っていた場合にパスが壊れてしまいます。
File APIを使うと
var files = document.getElementById("ファイル選択要素のID").files;
for (var i = 0, numFiles = files.length; i < numFiles; i++) {
console.log(files[i].name); // ファイル名
}
という感じでちゃんと1ファイルずつの情報が取得できるのですが、
ファイルの名前やファイルの内容は取得できてもファイルのフルパスを取得することはできません。
そこでもう一度ActiveXの力を借りました。
var fso = new ActiveXObject("Scripting.FileSystemObject");
var temp_file_list = document.getElementById("add_file_select").value.split(", ");
var file_list = [];
var temp_file_path = "";
for (var i = 0; i < temp_file_list.length; i++) {
temp_file_path += temp_file_list[i];
if (fso.FileExists(temp_file_path)) {
file_list.push(temp_file_path);
temp_file_path = "";
} else {
temp_file_path += ", ";
}
}
console.log(file_list);
// ["C:\dir1\dir2\file1.wav", "C:\dir1\dir2\file2.wav"]
「カンマ + スペース1つ」でsplitし、分解したファイルパスをFileSystemObjectのFileExistsを使って実在するファイルパスなのかチェックしていきます。
実在するファイルパスであれば結果配列に退避しておき、
実在しないファイルパスであればディレクトリ名 or ファイル名に「カンマ + スペース1つ」が含まれているのだろうということで
次の処理対象のファイルパスとくっつけたうえでFileSystemObjectのFileExistsにて実在するファイルパスなのかチェックします。
この方法で分解したとしても込み入ったケースでは間違ったファイルパスで処理してしまう可能性はありますが、
そうそうないケースであろうと思うのでいったんこの形にしました。
HTA実行時のIEのバージョンについて
単純にHTMLファイルを作ってHTAを実行させると互換表示になってしまうらしく、
console.logを含んだコードを実行すると「consoleが見つからない」というようなエラーで止まってしまいます。
headタグ内に
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
を入れれば互換表示がOFFになるので、IE11が入っている環境ではIE11標準モードで動くようになるので、エラーにならずに実行できます。
大まかなところはこんな感じです。
バリバリWindows&IE依存のアプリケーションになってしまいましたが、
個人的に欲しかったモノが実現できたのでそれでヨシですね!