FLAC4.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 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="time" STYLE="margin-left:1em"></SPAN>
</TD></TR></TABLE>
</FORM>

</DIV>

<SCRIPT TYPE="text/javascript">
<!--

function read_file(len, cb) {
  var end = file_pos + len;
  if(end > file.size) {
    alert(ERR_FORMAT);
    return;
  }
  file_cb = cb;
  file_reader.readAsBinaryString((slice) ? file.slice(file_pos, end) : file.mozSlice(file_pos, end));
  file_pos = end;
}

function file_end() {
  data = file_reader.result;
  i_data = 0;

  file_cb();
}

function file_err() {
  alert("ファイル読み込みでエラーが発生しました。");
}

function sel_file() {
  elem_ttl.textContent = elem_art.textContent = "";
  elem_play.disabled = true;

  file = elem_file.files.item(0);

  file_pos = 0;
  get_metadata();
}

function sel_file_end() {
  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;
  file_pos = pos_audio;
  data = "";
  i_data = 0;
  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
        get_frame();
        return;
      }

      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 frame_cont(eof) {
  if(eof) {
    eos = 1;  // ファイル読み込み終了
  }
  else {
    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;
  }

  frame();
}

function aud_end() {
  elem_aud = undefined;
  samples = undefined;
  data = undefined;

  elem_time.textContent = "";
  elem_play.value = "再生";
  running = false;
  elem_file.disabled = false;
}

// FLAC ****************************************************

// メタデータ
function get_metadata() {
  read_file(4, get_metadata_2);
}

function get_metadata_2() {
  if(data.substr(0, 4) != "fLaC") {
    alert(ERR_FORMAT);
    return;
  }

  get_metadata_3();
}

// METADATA_BLOCK
function get_metadata_3() {
  // METADATA_BLOCK_HEADER
  read_file(4, get_metadata_4);
}

function get_metadata_4() {
  flg_typ = data_int1();  // flag, block type
  len_meta = data_int3();  // length of metadata

  // METADATA_BLOCK_DATA
  read_file(len_meta, get_metadata_5);
}

function get_metadata_5() {
  var i;

  try {
    switch(flg_typ & 0x7f) {  // BLOCK_TYPE
    case 0:  // STREAMINFO
      if(len_meta < 34)
        throw 0;
      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)
        throw 0;
      n_channels = ((w & 0xe00) >> 9) + 1;  // number of channels
      if(n_channels > 6)
        throw 0;
      sample_bits_si = ((w & 0x1f0) >> 4) + 1;  // bits per sample
      if(sample_bits_si < 4)
        throw 0;
      if(sample_bits_si > 24) {
        alert("このプログラムは、24 ビットを超えるサンプル サイズには対応していません。");
        return;
      }
      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 len;
      if(len_meta < 4)
        throw 0;
      if((len = data_int4_le()) & 0x80000000)  // vendor_length
        throw 0;
      if(i_data + len > len_meta)
        throw 0;
      i_data += len;  // vendor_string

      if(i_data + 4 > data.length)
        throw 0;
      var n_comments = data_int4_le();  // user_comment_list_length
      if(n_comments & 0x80000000)
        throw 0;
      var f = 0x3;
      for(i = 0; i < n_comments; i++) {
        if(i_data + 4 > len_meta)
          throw 0;
        if((len = data_int4_le()) & 0x80000000)  // length
          throw 0;
        if(i_data + len > len_meta)
          throw 0;
        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;
      }
      break;

    case 127:
      throw 0;
    }
  }
  catch(e if e == 0) {
    alert(ERR_FORMAT);
    return;
  }

  if(flg_typ & 0x80) {  // last metadata block
    pos_audio = file_pos;  // オーディオ開始位置保存

    buff0 = new Int32Array(256);
    buff1 = (n_channels == 1) ? undefined : new Int32Array(256);

    sel_file_end();

    return;
  }

  get_metadata_3();
}

// FRAME
function get_frame() {
  // FRAME_HEADER
  // FRAME_HEADER は最大 16 バイトなので,16 バイト読み込む.
  // (ただし,ファイルの終端を超えない範囲で)
  data = data.substr(i_data);
  if(!data.length && file_pos == file.size) {  // EOF
    frame_cont(true);
    return;
  }
  var len = 16 - data.length;
  if(len > 0) {
    if(file_pos + len > file.size) {
      len = file.size - file_pos;
      if(data.length + len < 5) {
        frame_error(ERR_FORMAT);
        return;
      }
    }
    if(len > 0) {
      prev_data = data;

      read_file(len, get_frame_2);
      return;
    }
  }
  i_data = 0;

  get_frame_3();
}

function get_frame_2() {
  data = prev_data + data;

  get_frame_3();
}

function get_frame_3() {
  var w;

  try {
    w = data_int2();  // sync code, blocking strategy
    if((w & 0xfffc) != 0xfff8)  // sync code ?
      throw 0;
    var blocking = w & 0x1;  // blocking strategy(未使用)

    var bs_sr = data_int1();  // block size, sample rate

    w = data_int1();  // channel assignment, sample size
    channel_assign = w >> 4;  // channel assignment
    if(channel_assign & 0x8) {
      if(n_channels != 2)
        throw 0;
      if(channel_assign > 10)
        throw 0;
    }
    else {
      if(channel_assign + 1 != n_channels)
        throw 0;
    }
    // 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:
      throw 0;
    }

    // sample number/frame number(未使用)
    var number;
    w = data_int1();
    if(w & 0x80) {
      if(!(w & 0x40))
        throw 0;
      var c = 1;
      for(var bit = 0x20; bit; bit >>= 1) {
        if(!(w & bit))
          break;
        c++;
      }
      if(!bit)
        throw 0;
      number = w & (0x3f >> c);
      for(; c; c--) {
        if(i_data + 1 > data.length)
          throw 0;
        w = data_int1();
        if((w & 0xc0) != 0x80)
          throw 0;
        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:
        throw 0;
      case 1:
        block_size = 192;
        break;
      case 6:
        if(i_data + 1 > data.length)
          throw 0;
        block_size = data_int1() + 1;
        break;
      case 7:
        if(i_data + 2 > data.length)
          throw 0;
        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)
        throw 0;
      w = data_int1() * 1000;
      break;
    case 13:
      if(i_data + 2 > data.length)
        throw 0;
      w = data_int2();
      break;
    case 14:
      if(i_data + 2 > data.length)
        throw 0;
      w = data_int2() * 10;
      break;
    case 15:
      throw 0;
    default:
      w = sample_rate_tbl[w];
    }
    if(sample_rate) {
      if(w != sample_rate) {
        frame_error("このプログラムは、可変サンプリング レートには対応していません。");
        return;
      }
    }
    else {
      sample_rate = w;
    }

    // CRC-8(未使用)
    if(i_data + 1 > data.length)
      throw 0;
    data_int1();
  }
  catch(e if e == 0) {
    frame_error(ERR_FORMAT);
    return;
  }

  // CRC-16
  // x^16 + x^15 + x^2 + 1
  crc_16 = 0;
  for(var i = 0; i < i_data; )
    crc_16 = ((crc_16 & 0xff) << 8) ^ crc_tbl[(crc_16 >> 8) ^ data.charCodeAt(i++)];

  if(block_size > buff0.length) {
    buff0 = new Int32Array(block_size);
    if(n_channels > 1)
      buff1 = new Int32Array(block_size);
  }

  // ここからはビット単位で読み込み
  data_bit = 0;

  i_ch = 0;
  get_frame_4();
}

function get_frame_4() {
  // SUBFRAME
  // SUBFRAME は可変長なので,1 チャネルの SUBFRAME 全体が含まれると思われるバイト数読み込む.
  // (ただし,ファイルの終端を超えない範囲で)
  // このプログラムでは対応サンプル サイズを最大 24 ビットとしている.
  // サンプル サイズ調整により,SUBFRAME のサンプル サイズは 1 ビット増える場合がある.
  // したがって,SUBFRAME のデコード後のデータの大きさは最大で ブロック長×25 ビット.
  // デコード前の大きさは,通常はデコード後の大きさより小さいはずであるが,条件によっては
  // デコード後より大きくなる場合もあるかも知れない.
  // (そのような場合は SUBFRAME_VERBATIM を使うはずなので,デコード前の大きさの方が大きい
  // 場合は考慮しなくても,たぶん大丈夫だろうとは思うが)
  // 念のため,ブロック長×32 ビット分読み込むようにする.
  // (ただし,そこには SUBFRAME_HEADER も含まれる)
  data = data.substr(i_data);
  var len = (block_size << 2) - data.length;
  if(len > 0) {
    if(file_pos + len > file.size)
      len = file.size - file_pos;
    if(len > 0) {
      prev_data = data;

      read_file(len, get_frame_5);
      return;
    }
  }
  i_data = 0;

  get_frame_6();
}

function get_frame_5() {
  data = prev_data + data;

  get_frame_6();
}

function get_frame_6() {
  var w;
  var i;

  try {
    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
      throw 0;
    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)
        throw 0;
      // 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
      residual(buff, order);

      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
      throw 0;
    }
    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
        residual(buff, order);

        // データ
        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
        throw 0;
      }
    }
    else if(sub_type & 0x06) {  // reserved
      throw 0;
    }
    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;
      }
    }
  }
  catch(e if e == 0) {
    frame_error(ERR_FORMAT);
    return;
  }

  // CRC-16
  // x^16 + x^15 + x^2 + 1
  for(i = 0; i < i_data; )
    crc_16 = ((crc_16 & 0xff) << 8) ^ crc_tbl[(crc_16 >> 8) ^ data.charCodeAt(i++)];

  if(++i_ch < n_channels) {
    // 次チャネル
    get_frame_4();
    return;
  }

  // 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
  // 単に,バッファに残っているビットを捨てるだけ

  // FRAME_FOOTER
  data = data.substr(i_data);
  var len = 2 - data.length;
  if(len > 0) {
    if(file_pos + len > file.size) {
      len = file.size - file_pos;
      if(data.length + len < 2) {
        frame_error(ERR_FORMAT);
        return;
      }
    }
    if(len > 0) {
      prev_data = data;

      read_file(len, get_frame_7);
      return;
    }
  }
  i_data = 0;

  get_frame_8();
}

function get_frame_7() {
  data = prev_data + data;

  get_frame_8();
}

function get_frame_8() {
  // CRC-16
  if(data_int2() != crc_16) {
    frame_error(ERR_FORMAT);
    return;
  }

  frame_cont(false);
}

// Residual
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
    throw 0;
  }

  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)
        throw 0;
      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;
  }
}

function frame_error(msg) {
  alert(msg);

  aud_end();
}

// データの配列から 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) {
      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;
}

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

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

  file_reader = new FileReader();
  file_reader.onload = file_end;
  file_reader.onerror = file_err;
  slice = (typeof File.prototype.slice == "function");

  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>