![]() |
<!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 7)</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="stat" STYLE="margin-left:1em"></SPAN> <INPUT TYPE=TEXT ID="time" READONLY STYLE="border:none; background-color:transparent; font-size:medium"> </TD></TR></TABLE> </FORM> </DIV> <SCRIPT TYPE="text/javascript"> <!-- function sel_file() { elem_ttl.textContent = elem_art.textContent = ""; elem_play.disabled = true; ready = false; if(url_cre) { URL.revokeObjectURL(elem_aud.src); url_cre = false; } elem_aud = new Audio(); elem_aud.autoplay = true; elem_aud.addEventListener("play", aud_play, false); elem_aud.addEventListener("ended", aud_ended, false); elem_aud.addEventListener("timeupdate", aud_timeupdate, false); elem_aud.addEventListener("error", aud_error, false); file_reader = new FileReader(); file_reader.onload = sel_file_fr; file_reader.readAsArrayBuffer(elem_file.files.item(0)); } function sel_file_fr() { data = new Uint8Array(file_reader.result); file_reader = undefined; sel_file2(); } 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; if(ready) { elem_aud.currentTime = 0; elem_aud.play(); return; } // デコード開始 wave_bb = new MozBlobBuilder(); wave_buff = new Uint8Array(32768); wave_buff_len = 0; samples_sum = 0; elem_stat.textContent = "デコード中"; elem_time.value = "0:00"; last_sec = 0; i_data = i_audio; sample_rate = 0; setTimeout(frame, 0); } // フレーム デコード function frame() { if(!running) { // 停止 wave_bb = undefined; wave_buff = undefined; elem_stat.textContent = ""; aud_ended(); return; } for(var cnt = 50; cnt; cnt--) { var sts; // FRAME switch(sts = get_frame()) { case 0: // データあり if(n_samples) { // total samples あり if(samples_sum + block_size >= n_samples) { block_size = n_samples - samples_sum; sts = 1; // 終了 if(!block_size) break; } } var i = 0; var d; if(sample_bits < 16) { var r = 0x7fff / (0x7fff >> (16 - sample_bits)); for(; i < block_size; i++) { if((d = Math.round(buff0[i] * r)) < -32768) d = -32768; put_wave(d); if(n_channels > 1) { if((d = Math.round(buff1[i] * r)) < -32768) d = -32768; put_wave(d); } } } else { var s = sample_bits - 16; for(; i < block_size; i++) { d = buff0[i] >> s; put_wave(d); if(n_channels > 1) { d = buff1[i] >> s; put_wave(d); } } } samples_sum += block_size; var sec = Math.floor(samples_sum / sample_rate); if(sec != last_sec) { elem_time.value = String(Math.floor(sec / 60)) + ":" + ("0" + String(sec % 60)).substr(-2); last_sec = sec; } break; case -1: alert(ERR_FORMAT); // fall thru case -2: elem_stat.textContent = ""; aud_ended(); return; } if(sts) { // 終了 elem_stat.textContent = "再生準備中"; elem_time.value = ""; setTimeout(dec_end, 0); return; } } setTimeout(frame, 0); } function put_wave(d) { if(wave_buff_len == wave_buff.length) { try { wave_bb.append(wave_buff.buffer); } catch(e) { trap(); return; } wave_buff_len = 0; } wave_buff[wave_buff_len++] = d & 0xff; wave_buff[wave_buff_len++] = (d & 0xff00) >> 8; } function dec_end() { var i; data = undefined; wave_header[I_CHANNELS] = i = (n_channels == 1) ? 1 : 2; wave_int4(I_SAMPLES_SEC, sample_rate); wave_int4(I_BYTES_SEC, sample_rate << i); wave_header[I_ALIGN] = 1 << i; var size = samples_sum << i; wave_int4(I_DATA_SIZE, size); wave_int4(I_RIFF_SIZE, size + wave_header.length - 8); // RIFF チャンクの ID,サイズを除く長さ try { var bb = new MozBlobBuilder(); bb.append(wave_header.buffer); bb.append(wave_bb.getBlob()); wave_bb = undefined; if(wave_buff_len) bb.append((new Uint8Array(wave_buff.subarray(0, wave_buff_len))).buffer); wave_buff = undefined; elem_aud.src = URL.createObjectURL(bb.getBlob("audio/wave")); url_cre = true; } catch(e) { trap(); return; } elem_aud.load(); } function aud_play() { ready = true; elem_stat.textContent = ""; elem_time.value = "0:00"; last_sec = 0; } function aud_ended() { elem_time.value = ""; elem_play.value = "再生"; running = false; elem_file.disabled = false; } function aud_timeupdate() { if(!running) { // 停止 elem_aud.pause(); aud_ended(); return; } var sec = Math.floor(elem_aud.currentTime); if(sec != last_sec) { elem_time.value = String(Math.floor(sec / 60)) + ":" + ("0" + String(sec % 60)).substr(-2); last_sec = sec; } } function aud_error() { alert("Audio エレメントでエラーが発生しました。\nエラー コード: " + String(elem_aud.error.code)); clear(); } function trap() { alert("処理に失敗しました。恐らくデータが大きすぎます。"); clear(); } function clear() { wave_bb = undefined; wave_buff = undefined; elem_stat.textContent = ""; aud_ended(); elem_file.value = ""; elem_ttl.textContent = elem_art.textContent = ""; elem_play.disabled = true; } // WAVE ヘッダに 4 バイト Integer をセットする. function wave_int4(i_wave, val) { wave_header[i_wave ] = val & 0xff; wave_header[i_wave + 1] = (val & 0xff00) >> 8; wave_header[i_wave + 2] = (val & 0xff0000) >> 16; wave_header[i_wave + 3] = val >>> 24; } // FLAC **************************************************** // METADATA_BLOCK // 戻り値 - 0: 正常,-1: エラー,-2: エラー(メッセージ出力済み) function get_metadata() { if(data.length < 4) return -1; i_data = 0; if(data_int4() != 0x664c6143) // fLaC return -1; 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 Uint8Array(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 = ""; for(; len; len--) comment += String.fromCharCode(data[i_data++]); 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[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[i_data++]; } // データの配列から 2 バイト Integer を取り出す. function data_int2() { var val = (data[i_data] << 8) | data[i_data + 1]; i_data += 2 return val; } // データの配列から 3 バイト Integer を取り出す. function data_int3() { var val = (data[i_data] << 16) | (data[i_data + 1] << 8) | data[i_data + 2]; i_data += 3 return val; } // データの配列から 4 バイト Integer を取り出す. // 結果の MSB が 1 の場合,Integer の値としては負になる. function data_int4() { var val = (data[i_data] << 24) | (data[i_data + 1] << 16) | (data[i_data + 2] << 8) | data[i_data + 3]; i_data += 4 return val; } // データの配列から little endian の 4 バイト Integer を取り出す. // 結果の MSB が 1 の場合,Integer の値としては負になる. function data_int4_le() { var val = data[i_data] | (data[i_data + 1] << 8) | (data[i_data + 2] << 16) | (data[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[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) { utf16 += String.fromCharCode(((c & 0x1f) << 6) | utf8.charCodeAt(i + 1) & 0x3f); i += 2; } else if((c & 0xf0) == 0xe0) { utf16 += String.fromCharCode(((c & 0x0f) << 12) | ((utf8.charCodeAt(i + 1) & 0x3f) << 6) | utf8.charCodeAt(i + 2) & 0x3f); i += 3; } else { utf16 += utf8[i++]; } } return utf16; } //---------------------------------------------------------- // WAVE ヘッダ wave_header_const = [ 0x52, 0x49, 0x46, 0x46, // RIFF 0x00, 0x00, 0x00, 0x00, // チャンク サイズ 0x57, 0x41, 0x56, 0x45, // WAVE // fmt 0x66, 0x6d, 0x74, 0x20, // fmt 0x10, 0x00, 0x00, 0x00, // チャンク サイズ 16 0x01, 0x00, // wFormatTag PCM 0x00, 0x00, // wChannels 0x00, 0x00, 0x00, 0x00, // dwSamplesPerSec 0x00, 0x00, 0x00, 0x00, // dwAvgBytesPerSec 0x00, 0x00, // wBlockAlign 0x10, 0x00, // wBitsPerSample 16 // data 0x64, 0x61, 0x74, 0x61, // data 0x00, 0x00, 0x00, 0x00, // チャンク サイズ ]; I_RIFF_SIZE = 4; I_CHANNELS = 22; I_SAMPLES_SEC = 24; I_BYTES_SEC = 28; I_ALIGN = 32; I_DATA_SIZE = 40; // sample rate テーブル sample_rate_tbl = [0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000]; ERR_FORMAT = "ファイルの形式が正しくないか、このプログラムでは対応していないバージョンのファイルです。"; wave_header = new Uint8Array(wave_header_const); 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]; ready = false; running = false; url_cre = false; elem_file = document.getElementById("file"); elem_ttl = document.getElementById("ttl"); elem_art = document.getElementById("art"); elem_play = document.getElementById("play"); elem_stat = document.getElementById("stat"); elem_time = document.getElementById("time"); // ページを再ロードしたときのため document.forms[0].reset(); elem_file.disabled = false; elem_play.disabled = true; //--> </SCRIPT> </BODY> </HTML> |