FLAC.htm

戻る

<!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 3.5)</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;

  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);

  // Firefox 7 では nsIDOMFile オブジェクトの getAsBinary(),getAsDataURL() 等が削除された.
  // 代わりに,Firefox 3.6 で新設された FileReader オブジェクトを使って同様のことができるので,
  // FileReader オブジェクトが使えればそれを使い,使えなければ以前のままの処理とするように修正
  // した.
  if(typeof FileReader == "function") {
    file_reader = new FileReader();
    file_reader.onload = sel_file_fr;
    file_reader.readAsBinaryString(elem_file.files.item(0));
  }
  else {
    data = elem_file.files.item(0).getAsBinary();

    sel_file2();
  }
}

function sel_file_fr() {
  data = 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 = "";
  odd1 = undefined;
  odd2 = "";
  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 = 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;

      var id = setTimeout(trap, 0);
      var wave1 = odd2;
      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;
          wave1 += String.fromCharCode(d & 0xff, (d & 0xff00) >> 8);
          if(n_channels > 1) {
            if((d = Math.round(buff1[i] * r)) < -32768)
              d = -32768;
            wave1 += String.fromCharCode(d & 0xff, (d & 0xff00) >> 8);
          }
        }
      }
      else {
        var s = sample_bits - 16;
        for(; i < block_size; i++) {
          d = buff0[i] >> s;
          wave1 += String.fromCharCode(d & 0xff, (d & 0xff00) >> 8);
          if(n_channels > 1) {
            d = buff1[i] >> s;
            wave1 += String.fromCharCode(d & 0xff, (d & 0xff00) >> 8);
          }
        }
      }
      if(odd1 == undefined) {  // PCM データの先頭
        odd1 = wave1.substr(0, ODD1_LEN);
        i = (wave1.length - ODD1_LEN) % 3;
        wave += btoa(wave1.substr(ODD1_LEN, wave1.length - i - ODD1_LEN));
      }
      else {
        i = wave1.length % 3;
        wave += btoa(wave1.substr(0, wave1.length - i));
      }
      odd2 = (i) ? wave1.substr(- i) : "";
      clearTimeout(id);

      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 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,サイズを除く長さ

  var id = setTimeout(trap, 0);
  var hdr = "";
  for(i = 0; i < wave_header.length; i++)
    hdr += String.fromCharCode(wave_header[i]);
  wave = "data:audio/wave;base64," + btoa(hdr + odd1) + wave + btoa(odd2);
  elem_aud.src = wave;
  clearTimeout(id);
  if(elem_aud.src.length < wave.length) {
    trap();
    return;
  }
  wave = undefined;
  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 = 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.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 = [];
  buff1 = (n_channels == 1) ? undefined : [];

  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.length = block_size;
    if(n_channels > 1)
      buff1.length = 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
    // 単に,バッファに残っているビットを捨てるだけ
  }
  // Firefox 59 以上では使えなくなった
//catch(e if e == 0) {
//  return -1;
//}
  catch(e) {
    if(e == 0)
      return -1;
    throw e;
  }

  // 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;
}

//----------------------------------------------------------

  // WAVE ヘッダ
  wave_header = [
    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;
  ODD1_LEN = 1;  // 3 の倍数に不足するバイト数

  // sample rate テーブル
  sample_rate_tbl = [0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000];

  ERR_FORMAT = "ファイルの形式が正しくないか、このプログラムでは対応していないバージョンのファイルです。";

  coeff = new Array();  // coefficients
  coeff.length = 32;

  // CRC テーブル
  // x^16 + x^15 + x^2 + 1
  crc_tbl = new Array();
  crc_tbl.length = 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;

  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>