<!DOCTYPE HTML>
<HTML LANG="ja">
<HEAD>
<META CHARSET="Shift_JIS">
<TITLE>帰ってきた FLAC プレイヤー</TITLE>
</HEAD>
<BODY STYLE="background-color:#CCFFFF">
<DIV STYLE="text-align:center">
<BR>
<B><SPAN STYLE="color:#CC0000">帰ってきた FLAC プレイヤー<BR>(Firefox 4)</SPAN></B>
<BR><BR>
<FORM>
<TABLE STYLE="margin-left:auto; margin-right:auto"><TR><TD STYLE="text-align:left; white-space:nowrap">
<B>ファイル:</B><BR>
<INPUT TYPE=FILE ID="file" SIZE=60 onChange="sel_file()"><BR><BR>
<B>タイトル:</B> <SPAN ID="ttl" STYLE="white-space:pre"></SPAN><BR><BR>
<B>アーティスト:</B> <SPAN ID="art" STYLE="white-space:pre"></SPAN><BR><BR>
<INPUT TYPE=BUTTON ID="play" VALUE="再生" DISABLED onClick="play_stop()">
<SPAN ID="time" STYLE="margin-left:1em"></SPAN>
</TD></TR></TABLE>
</FORM>
</DIV>
<SCRIPT TYPE="text/javascript">
<!--
function sel_file() {
elem_ttl.textContent = elem_art.textContent = "";
elem_play.disabled = true;
data = undefined;
file_reader = new FileReader();
file_reader.onload = sel_file_fr;
file_reader.onerror = sel_file_err;
file_reader.readAsBinaryString(elem_file.files.item(0));
}
function sel_file_fr() {
data = file_reader.result;
file_reader = undefined;
sel_file2();
}
function sel_file_err() {
alert("ファイル読み込みでエラーが発生しました。");
}
function sel_file2() {
// METADATA_BLOCK
switch(get_metadata()) {
case -1:
alert(ERR_FORMAT);
return;
case -2:
return;
}
elem_play.disabled = false;
}
function play_stop() {
if(running) {
// 停止
running = false;
return;
}
elem_file.disabled = true;
elem_play.value = "停止";
running = true;
// 再生開始
elem_aud = undefined;
samples = undefined;
samples_len = 0;
samples_written = 0;
eos = 0;
buff_off = block_size = 0;
elem_time.textContent = "0:00";
last_sec = 0;
i_data = i_audio;
sample_rate = 0;
setTimeout(frame, 0);
}
// 再生
function frame() {
if(!running) { // 停止
aud_end();
return;
}
for(; ; ) {
if(samples_written) {
if(samples_written - elem_aud.mozCurrentSampleOffset() >= min_ahead)
break;
}
for(; ; ) {
if(samples != undefined) {
if(samples_len == samples.length)
break;
}
if(buff_off == block_size) {
if(eos)
break;
// FRAME
switch(get_frame()) {
case -1:
alert(ERR_FORMAT);
// fall thru
case -2:
aud_end();
return;
case 1: // EOF
eos = 1; // ファイル読み込み終了
break;
default: // データあり
if(samples == undefined) {
min_ahead = (n_channels == 1) ? sample_rate >> 1 : (sample_rate & ~0x1); // 500ms
samples = new Float32Array((min_ahead >> 2) & ~0x1); // 125ms 分
}
max_val = 0x1 << (sample_bits - 1);
if(n_samples) { // total samples あり
var n = samples_written + samples_len;
if(n_channels > 1)
n /= 2;
if(n + block_size >= n_samples) {
block_size = n_samples - n;
eos = 1; // ファイル読み込み終了
}
}
buff_off = 0;
}
if(buff_off == block_size) // データ終了
break;
}
for(; buff_off < block_size; buff_off++) {
if(samples_len == samples.length)
break;
samples[samples_len++] = buff0[buff_off] / max_val;
if(n_channels > 1)
samples[samples_len++] = buff1[buff_off] / max_val;
}
}
if(elem_aud == undefined) {
elem_aud = new Audio();
elem_aud.mozSetup((n_channels == 1) ? 1 : 2, sample_rate);
}
var written = elem_aud.mozWriteAudio(samples.subarray(0, samples_len));
samples_written += written;
if(written < samples_len)
samples.set(samples.subarray(written, samples_len)); // 残りをシフト
samples_len -= written;
if(eos && buff_off == block_size) { // データ終了
eos = 2;
break;
}
}
var sec = Math.floor(elem_aud.mozCurrentSampleOffset() / ((n_channels == 1) ? sample_rate : (sample_rate << 1)));
if(sec != last_sec) {
elem_time.textContent = String(Math.floor(sec / 60)) + ":" + ("0" + String(sec % 60)).substr(-2);
last_sec = sec;
}
if(eos == 2) {
setTimeout(aud_end, Math.floor((samples_written - elem_aud.mozCurrentSampleOffset())
/ ((n_channels == 1) ? sample_rate : (sample_rate << 1)) * 1000) + 100);
return;
}
setTimeout(frame, 100);
}
function aud_end() {
elem_aud = undefined;
samples = undefined;
elem_time.textContent = "";
elem_play.value = "再生";
running = false;
elem_file.disabled = false;
}
// FLAC ****************************************************
// METADATA_BLOCK
// 戻り値 - 0: 正常,-1: エラー,-2: エラー(メッセージ出力済み)
function get_metadata() {
if(data.substr(0, 4) != "fLaC")
return -1;
i_data = 4;
for(; ; ) {
var i;
// METADATA_BLOCK_HEADER
if(i_data + 4 > data.length)
return -1;
var flg_typ = data_int1();
var len = data_int3();
if(i_data + len > data.length)
return -1;
// METADATA_BLOCK_DATA
switch(flg_typ & 0x7f) { // BLOCK_TYPE
case 0: // STREAMINFO
if(len < 34)
return -1;
min_block = data_int2(); // minimum block size
max_block = data_int2(); // maximum block size
min_frame = data_int3(); // minimum frame size
max_frame = data_int3(); // maximum frame size
var w = data_int4();
sample_rate_si = w >>> 12; // sample rate
if(sample_rate_si == 0 || sample_rate_si > 655350)
return -1;
n_channels = ((w & 0xe00) >> 9) + 1; // number of channels
if(n_channels > 6)
return -1;
sample_bits_si = ((w & 0x1f0) >> 4) + 1; // bits per sample
if(sample_bits_si < 4)
return -1;
if(sample_bits_si > 24) {
alert("このプログラムは、24 ビットを超えるサンプル サイズには対応していません。");
return -2;
}
n_samples = (((w & 0xf) << 16) | data_int2()) * 0x10000; // total samples
n_samples += data_int2();
// MD5 signature
signature = new Array();
signature.length = 16;
for(i = 0; i < 16; i++)
signature[i] = data_int1();
break;
case 4: // VORBIS_COMMENT
// 曲タイトル,アーティスト名を取得
var i_skip = i_data + len;
if(len < 4)
return -1;
if((len = data_int4_le()) & 0x80000000) // vendor_length
return -1;
if(i_data + len > data.length)
return -1;
i_data += len; // vendor_string
if(i_data + 4 > data.length)
return -1;
var n_comments = data_int4_le(); // user_comment_list_length
if(n_comments & 0x80000000)
return -1;
var f = 0x3;
for(i = 0; i < n_comments; i++) {
if(i_data + 4 > data.length)
return -1;
if((len = data_int4_le()) & 0x80000000) // length
return -1;
if(i_data + len > data.length)
return -1;
var comment = data.substr(i_data, len); // comment
i_data += len;
if(comment.substr(0, 6).toUpperCase() == "TITLE=") {
elem_ttl.textContent = utf8to16(comment.substr(6));
f &= ~0x1;
}
if(comment.substr(0, 7).toUpperCase() == "ARTIST=") {
elem_art.textContent = utf8to16(comment.substr(7));
f &= ~0x2;
}
if(!f)
break;
}
i_data = i_skip;
break;
case 127:
return -1;
default:
i_data += len;
}
if(flg_typ & 0x80) // last metadata block
break;
}
i_audio = i_data; // オーディオ開始位置保存
buff0 = new Int32Array(256);
buff1 = (n_channels == 1) ? undefined : new Int32Array(256);
return 0;
}
// FRAME
// 戻り値 - 0: 正常,1: EOF,-1: エラー,-2: エラー(メッセージ出力済み)
function get_frame() {
var w;
var i;
// FRAME_HEADER
var i_header = i_data;
if(i_data == data.length) // EOF
return 1;
if(i_data + 5 > data.length)
return -1;
w = data_int2(); // sync code, blocking strategy
if((w & 0xfffc) != 0xfff8) // sync code ?
return -1;
var blocking = w & 0x1; // blocking strategy(未使用)
var bs_sr = data_int1(); // block size, sample rate
w = data_int1(); // channel assignment, sample size
var channel_assign = w >> 4; // channel assignment
if(channel_assign & 0x8) {
if(n_channels != 2)
return -1;
if(channel_assign > 10)
return -1;
}
else {
if(channel_assign + 1 != n_channels)
return -1;
}
// bits per sample
switch((w & 0xe) >> 1) {
case 0:
sample_bits = sample_bits_si;
break;
case 1:
sample_bits = 8;
break;
case 2:
sample_bits = 12;
break;
case 4:
sample_bits = 16;
break;
case 5:
sample_bits = 20;
break;
case 6:
sample_bits = 24;
break;
default:
return -1;
}
// sample number/frame number(未使用)
var number;
w = data_int1();
if(w & 0x80) {
if(!(w & 0x40))
return -1;
var c = 1;
for(var bit = 0x20; bit; bit >>= 1) {
if(!(w & bit))
break;
c++;
}
if(!bit)
return -1;
number = w & (0x3f >> c);
for(; c; c--) {
if(i_data + 1 > data.length)
return -1;
w = data_int1();
if((w & 0xc0) != 0x80)
return -1;
number = number * 64 + (w & 0x3f);
}
}
else {
number = w;
}
// block size
if((w = bs_sr >> 4) & 0x8) {
block_size = 1 << w; // 256/512/1024/2048/4096/8192/16384/32768
}
else {
switch(w) {
case 0:
return -1;
case 1:
block_size = 192;
break;
case 6:
if(i_data + 1 > data.length)
return -1;
block_size = data_int1() + 1;
break;
case 7:
if(i_data + 2 > data.length)
return -1;
block_size = data_int2() + 1;
break;
default:
block_size = 576 << (w - 2); // 576/1152/2304/4608
}
}
// sample rate
switch(w = bs_sr & 0xf) {
case 0:
w = sample_rate_si;
break;
case 12:
if(i_data + 1 > data.length)
return -1;
w = data_int1() * 1000;
break;
case 13:
if(i_data + 2 > data.length)
return -1;
w = data_int2();
break;
case 14:
if(i_data + 2 > data.length)
return -1;
w = data_int2() * 10;
break;
case 15:
return -1;
default:
w = sample_rate_tbl[w];
}
if(sample_rate) {
if(w != sample_rate) {
alert("このプログラムは、可変サンプリング レートには対応していません。");
return -2;
}
}
else {
sample_rate = w;
}
// CRC-8(未使用)
if(i_data + 1 > data.length)
return -1;
data_int1();
if(block_size > buff0.length) {
buff0 = new Int32Array(block_size);
if(n_channels > 1)
buff1 = new Int32Array(block_size);
}
// ここからはビット単位で読み込み
try {
data_bit = 0;
for(var i_ch = 0; i_ch < n_channels; i_ch++) {
// SUBFRAME
var buff;
switch(i_ch) {
case 0:
buff = buff0;
break;
case 1:
buff = buff1;
break;
default:
buff = null;
}
// sample size 調整
var sample_bits_adj = sample_bits;
switch(channel_assign) {
case 8: // left/side
if(i_ch == 1)
sample_bits_adj++;
break;
case 9: // right/side
if(i_ch == 0)
sample_bits_adj++;
break;
case 10: // mid/side
if(i_ch == 1)
sample_bits_adj++;
break;
}
if(get_bits(1)) // zero
return -1;
var sub_type = get_bits(6); // subframe type
// wasted bits-per-sample
w = get_bits(1);
var wasted = 0;
if(w) {
for(; ; ) {
wasted++;
if(get_bits(1))
break;
}
sample_bits_adj -= wasted;
}
var sample_sign = 0xffffffff << (sample_bits_adj - 1);
var order; // predictor order
if (sub_type & 0x20) { // SUBFRAME_LPC
order = (sub_type & 0x1f) + 1; // order
i = 0;
if(buff) {
for(; i < order; i++)
buff[i] = (get_bits(sample_bits_adj) + sample_sign) ^ sample_sign;
}
else {
for(; i < order; i++)
get_bits(sample_bits_adj);
}
var prec = get_bits(4) + 1; // coefficient precision
if(prec == 16)
return -1;
// coefficient shift
w = get_bits(5);
// シフトがマイナスになることがあるのか?
var shift;
var shift_dir;
if(w & 0x10) { // マイナス
shift = 0x10000 >> (w & 0xf);
shift_dir = false;
}
else {
shift = 1 << w;
shift_dir = true;
}
w = 0xffffffff << (prec - 1);
for(i = 0; i < order; i++)
coeff[i] = (get_bits(prec) + w) ^ w;
// RESIDUAL
if(residual(buff, order))
return -1;
if(buff) {
for(i = order; i < block_size; i++) {
var sum = 0;
for(var j = 0, k = i - 1; j < order; j++, k--)
sum += buff[k] * coeff[j];
buff[i] += Math.floor((shift_dir) ? sum / shift : sum * shift);
}
}
}
else if(sub_type & 0x10) { // reserved
return -1;
}
else if(sub_type & 0x08) {
order = sub_type & 0x07;
if(order <= 4) { // SUBFRAME_FIXED
i = 0;
if(buff) {
for(; i < order; i++)
buff[i] = (get_bits(sample_bits_adj) + sample_sign) ^ sample_sign;
}
else {
for(; i < order; i++)
get_bits(sample_bits_adj);
}
// RESIDUAL
if(residual(buff, order))
return -1;
// データ
if(buff) {
switch(order) {
case 1:
for(; i < block_size; i++)
buff[i] += buff[i - 1];
break;
case 2:
for(; i < block_size; i++)
buff[i] += (buff[i - 1] << 1) - buff[i - 2];
break;
case 3:
for(; i < block_size; i++)
buff[i] += 3 * (buff[i - 1] - buff[i - 2]) + buff[i - 3];
break;
case 4:
for(; i < block_size; i++)
buff[i] += ((buff[i - 1] + buff[i - 3]) << 2) - 6 * buff[i - 2] - buff[i - 4];
break;
}
}
}
else { // reserved
return -1;
}
}
else if(sub_type & 0x06) { // reserved
return -1;
}
else if(sub_type & 0x01) { // SUBFRAME_VERBATIM
i = 0;
if(buff) {
for(; i < block_size; i++)
buff[i] = (get_bits(sample_bits_adj) + sample_sign) ^ sample_sign;
}
else {
for(; i < block_size; i++)
get_bits(sample_bits_adj);
}
}
else { // SUBFRAME_CONSTANT
w = (get_bits(sample_bits_adj) + sample_sign) ^ sample_sign;
if(buff) {
for(i = 0; i < block_size; i++)
buff[i] = w;
}
}
// wasted bits
if(wasted) {
if(buff) {
for(i = 0; i < block_size; i++)
buff[i] <<= wasted;
}
}
}
// Interchannel Decorrelation
if(channel_assign >= 8) {
i = 0;
switch(channel_assign) {
case 8: // left/side
for(; i < block_size; i++)
buff1[i] = buff0[i] - buff1[i];
break;
case 9: // right/side
for(; i < block_size; i++)
buff0[i] += buff1[i];
break;
default: // mid/side
for(; i < block_size; i++) {
var side = buff1[i];
var mid = (buff0[i] << 1) | (side & 0x1);
buff0[i] = (mid + side) >> 1;
buff1[i] = (mid - side) >> 1;
}
}
}
// zero-padding
// 単に,バッファに残っているビットを捨てるだけ
}
catch(e if e == 0) {
return -1;
}
// FRAME_FOOTER
if(i_data + 2 > data.length)
return -1;
// CRC-16
// x^16 + x^15 + x^2 + 1
var crc = 0;
for(i = i_header; i < i_data; )
crc = ((crc & 0xff) << 8) ^ crc_tbl[(crc >> 8) ^ data.charCodeAt(i++)];
if(data_int2() != crc)
return -1;
return 0;
}
// Residual
// 戻り値 - 0: 正常,-1: エラー
function residual(buff, pred_order) {
var param_bits;
var escape;
switch(get_bits(2)) { // coding method
case 0: // RESIDUAL_CODING_METHOD_PARTITIONED_RICE
param_bits = 4;
escape = 15;
break;
case 1: // RESIDUAL_CODING_METHOD_PARTITIONED_RICE2
param_bits = 5;
escape = 31;
break;
default: // reserved
return -1;
}
var part_order = get_bits(4); // partition order
var n_parts = 1 << part_order;
var part_size = block_size >> part_order; // partition size
var off = 0;
for(var i_part = 0; i_part < n_parts; i_part++) {
// RICE_PARTITION,RICE2_PARTITION
var param = get_bits(param_bits); // rice parameter
var i = (i_part) ? 0 : pred_order;
if(param == escape) { // escape
var n = get_bits(5);
if(!n)
return -1;
if(buff) {
var sign = 0xffffffff << (n - 1);
for(; i < part_size; i++)
buff[off + i] = (get_bits(n) + sign) ^ sign;
}
else {
for(; i < part_size; i++)
get_bits(n);
}
}
else {
if(buff) {
for(; i < part_size; i++) {
var val = 0;
while(!get_bits(1))
val++;
if(param)
val = (val << param) | get_bits(param);
buff[off + i] = (val & 0x1) ? ~(val >> 1) : val >> 1;
}
}
else {
for(; i < part_size; i++) {
while(!get_bits(1))
;
if(param)
get_bits(param);
}
}
}
off += part_size;
}
return 0;
}
// データの配列から 1 バイト Integer を取り出す.
function data_int1() {
return data.charCodeAt(i_data++);
}
// データの配列から 2 バイト Integer を取り出す.
function data_int2() {
var val = (data.charCodeAt(i_data) << 8) | data.charCodeAt(i_data + 1);
i_data += 2
return val;
}
// データの配列から 3 バイト Integer を取り出す.
function data_int3() {
var val = (data.charCodeAt(i_data) << 16) | (data.charCodeAt(i_data + 1) << 8)
| data.charCodeAt(i_data + 2);
i_data += 3
return val;
}
// データの配列から 4 バイト Integer を取り出す.
// 結果の MSB が 1 の場合,Integer の値としては負になる.
function data_int4() {
var val = (data.charCodeAt(i_data) << 24) | (data.charCodeAt(i_data + 1) << 16)
| (data.charCodeAt(i_data + 2) << 8) | data.charCodeAt(i_data + 3);
i_data += 4
return val;
}
// データの配列から little endian の 4 バイト Integer を取り出す.
// 結果の MSB が 1 の場合,Integer の値としては負になる.
function data_int4_le() {
var val = data.charCodeAt(i_data) | (data.charCodeAt(i_data + 1) << 8)
| (data.charCodeAt(i_data + 2) << 16) | (data.charCodeAt(i_data + 3) << 24);
i_data += 4
return val;
}
// ビット単位読み込み
function get_bits(n) {
var bits = 0;
for(; n; n--) {
if(!data_bit) {
if(i_data == data.length)
throw 0;
data_byte = data.charCodeAt(i_data++);
data_bit = 0x80;
}
bits <<= 1;
if(data_byte & data_bit)
bits |= 0x1;
data_bit >>= 1;
}
return bits;
}
// UTF-8 → UTF-16 変換
function utf8to16(utf8) {
var utf16 = "";
for(var i = 0; i < utf8.length; ) {
var c = utf8.charCodeAt(i);
if ((c & 0xe0) == 0xc0) {
if(i + 2 > utf8.length)
break;
utf16 += String.fromCharCode(((c & 0x1f) << 6) | utf8.charCodeAt(i + 1) & 0x3f);
i += 2;
}
else if((c & 0xf0) == 0xe0) {
if(i + 3 > utf8.length)
break;
utf16 += String.fromCharCode(((c & 0x0f) << 12) | ((utf8.charCodeAt(i + 1) & 0x3f) << 6)
| utf8.charCodeAt(i + 2) & 0x3f);
i += 3;
}
else if((c & 0xf0) == 0xf0) {
if(i + 4 > utf8.length)
break;
utf16 += String.fromCharCode(
0xd800 | ((((c & 0x07) << 8) | ((utf8.charCodeAt(i + 1) & 0x30) << 2)) - 0x40)
| ((utf8.charCodeAt(i + 1) & 0xf) << 2) | ((utf8.charCodeAt(i + 2) & 0x30) >> 4),
0xdc00 | ((utf8.charCodeAt(i + 2) & 0xf) << 6) | utf8.charCodeAt(i + 3) & 0x3f);
i += 4;
}
else {
utf16 += utf8[i++];
}
}
return utf16;
}
//----------------------------------------------------------
// sample rate テーブル
sample_rate_tbl = [0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000];
ERR_FORMAT = "ファイルの形式が正しくないか、このプログラムでは対応していないバージョンのファイルです。";
coeff = new Int16Array(32); // coefficients
// CRC テーブル
// x^16 + x^15 + x^2 + 1
crc_tbl = new Uint16Array(256);
crc_tbl[0x00] = 0;
crc_tbl[0x01] = 0x8005;
crc_tbl[0x02] = 0x800f;
crc_tbl[0x04] = 0x801b;
crc_tbl[0x08] = 0x8033;
crc_tbl[0x10] = 0x8063;
crc_tbl[0x20] = 0x80c3;
crc_tbl[0x40] = 0x8183;
crc_tbl[0x80] = 0x8303;
for(i = 3; i < 256; i++)
crc_tbl[i] = crc_tbl[i & 0x01] ^ crc_tbl[i & 0x02] ^ crc_tbl[i & 0x04] ^ crc_tbl[i & 0x08]
^ crc_tbl[i & 0x10] ^ crc_tbl[i & 0x20] ^ crc_tbl[i & 0x40] ^ crc_tbl[i & 0x80];
running = false;
elem_file = document.getElementById("file");
elem_ttl = document.getElementById("ttl");
elem_art = document.getElementById("art");
elem_play = document.getElementById("play");
elem_time = document.getElementById("time");
// ページを再ロードしたときのため
document.forms[0].reset();
elem_file.disabled = false;
elem_play.disabled = true;
//-->
</SCRIPT>
</BODY>
</HTML>
|