package { import flash.display.Sprite; import flash.display.Shape; import flash.display.StageScaleMode; import flash.display.SimpleButton; import flash.display.Bitmap; import flash.geom.ColorTransform; import flash.text.TextField; import flash.text.TextFieldType; import flash.text.TextFieldAutoSize; import flash.text.TextFormat; import flash.text.TextFormatAlign; import flash.events.Event; import flash.events.MouseEvent; import flash.events.IOErrorEvent; import flash.events.SampleDataEvent; import flash.events.TimerEvent; import flash.ui.Mouse; import flash.ui.MouseCursor; import flash.net.FileReference; import flash.net.FileFilter; import flash.media.Sound; import flash.media.SoundChannel; import flash.media.SoundTransform; import flash.errors.EOFError; import flash.utils.*; [SWF(width=400, height=200, frameRate=30, backgroundColor=0xCCFFFF)] public class FLAC extends Sprite { [Embed(source="play.gif")] private var Bitmap_play:Class; [Embed(source="stop.gif")] private var Bitmap_stop:Class; [Embed(source="vol_i.gif")] private var Bitmap_vol_i:Class; [Embed(source="vol_b.gif")] private var Bitmap_vol_b:Class; [Embed(source="vol_t.gif")] private var Bitmap_vol_t:Class; private var load:SimpleButton; // 読み込みボタン private var fnam:TextField; // ファイル名 private var title:TextField; // 曲タイトル private var artist:TextField; // アーティスト名 private var play:SimpleButton; // 再生/停止ボタン private var play_u:Sprite; // 再生ボタン アップ イメージ private var play_d:Sprite; // ダウン イメージ private var stop_u:Sprite; // 停止ボタン アップ イメージ private var stop_d:Sprite; // ダウン イメージ private var elapsed:TextField; // 経過時間 private var vol_b:Sprite; // ボリューム private var vol_t:Sprite; // ボリューム つまみ private var err:TextField; // エラー表示 private var timer:Timer; // 経過時間表示用タイマー private var file:FileReference; // ファイル読み込み private var snd:Sound; // サウンド private var snd_ch:SoundChannel = null; // サウンド チャネル private var snd_tr:SoundTransform; // サウンド属性 private var data:ByteArray = null; // FLAC データ private var data_byte:uint; // FLAC データ バイト バッファ private var data_bit:uint; // ビット位置 // STREAMINFO private var min_block:uint; // minimum block size private var max_block:uint; // maximum block size private var min_frame:uint; // minimum frame size private var max_frame:uint; // maximum frame size private var sample_rate_si:uint; // sample rate private var n_channels:uint; // number of channels private var sample_bits_si:uint; // bits per sample private var n_samples:Number; // total samples private var signature:ByteArray; // MD5 signature private var sample_rate_tbl:Array // sample rate テーブル = [0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000]; private var sample_bits:uint; // bits per sample private var block_size:uint; // block size private var sample_rate:uint; // sample rate private var audio_pos:uint; // オーディオ開始位置 private var samples_sum:Number; // サンプル数累計 private var eos:Boolean; // ストリーム終了 private var buff0:Vector.; // オーディオ データ バッファ L private var buff1:Vector.; // R private var i_buff:int; // オーディオ データ バッファ インデックス private var coeff:Vector. = new Vector.(); // coefficients private var sample_max:uint; // サンプル最大値 private var vol_dx:int; // ボリューム マウス ダウン位置 private const ERR_FORMAT:String = "ファイルの形式が正しくないか、このプログラムでは対応していないバージョンのファイルです。"; public function FLAC():void { if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE, init); } private function init(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, init); stage.scaleMode = StageScaleMode.NO_SCALE; var txt:TextField; var fmt:TextFormat; var bmp:Bitmap; // タイトル txt = new TextField(); with(txt) { x = 0; y = 8; width = 400; height = 24; type = TextFieldType.DYNAMIC; selectable = false; text = "FLAC プレイヤー"; fmt = new TextFormat(); with(fmt) { align = TextFormatAlign.CENTER; size = 12; color = 0xCC0000; bold = true; } setTextFormat(fmt); } addChild(txt); // 読み込みボタン load = new SimpleButton(); with(load) { x = 16; y = 32; var lbl:String = "ファイル"; upState = overState = hitTestState = draw_button_t(lbl, 60, 24, false); downState = draw_button_t(lbl, 60, 24, true); tabEnabled = false; addEventListener(MouseEvent.CLICK, load_click); } addChild(load); // ファイル名 fnam = new TextField(); with(fnam) { x = 88; y = 32; width = 296; height = 24; type = TextFieldType.DYNAMIC; selectable = false; fmt = new TextFormat(); fmt.size = 14; defaultTextFormat = fmt; } addChild(fnam); // 曲タイトル txt = new TextField(); with(txt) { x = 16; y = 64; type = TextFieldType.DYNAMIC; selectable = false; autoSize = TextFieldAutoSize.LEFT; text = "タイトル:"; fmt = new TextFormat(); with(fmt) { size = 12; bold = true; } setTextFormat(fmt); } addChild(txt); title = new TextField(); with(title) { x = 16 + txt.width + 2; y = 64; width = 400 - 16 - x; height = 24; type = TextFieldType.DYNAMIC; selectable = false; fmt = new TextFormat(); fmt.size = 14; defaultTextFormat = fmt; } addChild(title); // アーティスト名 txt = new TextField(); with(txt) { x = 16; y = 88; type = TextFieldType.DYNAMIC; selectable = false; autoSize = TextFieldAutoSize.LEFT; text = "アーティスト:"; fmt = new TextFormat(); with(fmt) { size = 12; bold = true; } setTextFormat(fmt); } addChild(txt); artist = new TextField(); with(artist) { x = 16 + txt.width + 2; y = 88; width = 400 - 16 - x; height = 24; type = TextFieldType.DYNAMIC; selectable = false; fmt = new TextFormat(); fmt.size = 14; defaultTextFormat = fmt; } addChild(artist); // 再生/停止ボタン bmp = new Bitmap_play(); play_u = draw_button_b(bmp, 32, 24, false); play_d = draw_button_b(new Bitmap(bmp.bitmapData.clone()), 32, 24, true); bmp = new Bitmap_stop(); stop_u = draw_button_b(bmp, 32, 24, false); stop_d = draw_button_b(new Bitmap(bmp.bitmapData.clone()), 32, 24, true); play = new SimpleButton(); with(play) { x = 32; y = 120; upState = overState = hitTestState = play_u; downState = play_d; tabEnabled = false; addEventListener(MouseEvent.CLICK, play_click); } addChild(play); // 経過時間 elapsed = new TextField(); with(elapsed) { x = 72; y = 120; width = 48; height = 24; type = TextFieldType.DYNAMIC; selectable = false; fmt = new TextFormat(); with(fmt) { align = TextFormatAlign.RIGHT; size = 14; } defaultTextFormat = fmt; } addChild(elapsed); // ボリューム bmp = new Bitmap_vol_i(); bmp.x = 144; bmp.y = 123; addChild(bmp); vol_b = new Sprite(); with(vol_b) { x = 170; y = 120; bmp = new Bitmap_vol_b(); bmp.x = bmp.y = 0; addChild(bmp); addEventListener(MouseEvent.MOUSE_DOWN, vol_b_mouse_down); addEventListener(MouseEvent.MOUSE_UP, vol_mouse_up1); addEventListener(MouseEvent.MOUSE_OVER, vol_mouse_over); addEventListener(MouseEvent.MOUSE_OUT, vol_mouse_out); } addChild(vol_b); vol_t = new Sprite(); with(vol_t) { x = 266; y = 124; bmp = new Bitmap_vol_t(); bmp.x = bmp.y = 0; addChild(bmp); addEventListener(MouseEvent.MOUSE_DOWN, vol_mouse_down); addEventListener(MouseEvent.MOUSE_UP, vol_mouse_up1); addEventListener(MouseEvent.MOUSE_OVER, vol_mouse_over); addEventListener(MouseEvent.MOUSE_OUT, vol_mouse_out); } addChild(vol_t); // エラー表示 err = new TextField(); with(err) { x = 16; y = 152; width = 368; height = 44; type = TextFieldType.DYNAMIC; selectable = false; multiline = true; wordWrap = true; fmt = new TextFormat(); with(fmt) { size = 12; color = 0xFF0000; } defaultTextFormat = fmt; } addChild(err); timer = new Timer(100, 0); timer.addEventListener(TimerEvent.TIMER, timer_timer); } // テキスト ボタンのイメージを作成する private function draw_button_t(lbl:String, w:int, h:int, down:Boolean):Sprite { var s:Sprite = new Sprite(); with(s.graphics) { lineStyle(2, 0xCC0000); beginFill((down) ? 0xCC0000 : 0xFFFFFF); drawRoundRect(0, 0, w, h, 10); endFill(); } var txt:TextField = new TextField(); with(txt) { type = TextFieldType.DYNAMIC; txt.text = lbl; selectable = false; autoSize = TextFieldAutoSize.LEFT; var fmt:TextFormat = new TextFormat(); with(fmt) { size = 12; color = (down) ? 0xFFFFFF : 0x000000; } setTextFormat(fmt); x = (w - width) >> 1; y = (h - height) >> 1; } s.addChild(txt); return s; } // ビットマップ ボタンのイメージを作成する private function draw_button_b(lbl:Bitmap, w:int, h:int, down:Boolean):Sprite { var s:Sprite = new Sprite(); with(s.graphics) { lineStyle(2, 0xCC0000); beginFill((down) ? 0xCC0000 : 0xFFFFFF); drawRoundRect(0, 0, w, h, 10); endFill(); } with(lbl) { x = (w - width) >> 1; y = (h - height) >> 1; if(down) bitmapData.colorTransform(bitmapData.rect, new ColorTransform(0, 0, 0, 1, 0xFF, 0xFF, 0xFF, 0)); } s.addChild(lbl); return s; } // ファイル読み込み private function load_click(e:MouseEvent):void { err.text = ""; file = new FileReference(); with(file) { addEventListener(Event.SELECT, load_select); addEventListener(Event.CANCEL, load_cancel); addEventListener(Event.COMPLETE, load_complete); addEventListener(IOErrorEvent.IO_ERROR, load_io_error); } mouseChildren = false; file.browse([new FileFilter("FLAC ファイル(*.flac,*.fla)", "*.flac;*.fla"), new FileFilter("すべてのファイル", "*")]); } private function load_select(e:Event):void { fnam.text = file.name; title.text = artist.text = ""; file.load(); } private function load_cancel(e:Event):void { with(file) { removeEventListener(Event.SELECT, load_select); removeEventListener(Event.CANCEL, load_cancel); removeEventListener(Event.COMPLETE, load_complete); removeEventListener(IOErrorEvent.IO_ERROR, load_io_error); } file = null; mouseChildren = true; } private function load_complete(e:Event):void { data = file.data; load_cancel(null); switch(get_metadata()) { case -1: err.text = ERR_FORMAT; // fall thru case -2: data = null; break; } } private function load_io_error(e:IOErrorEvent):void { load_cancel(null); fnam.text = ""; data = null; err.text = "ファイルの読み込みでエラーが発生しました。"; } // 再生/停止 private function play_click(e:MouseEvent):void { err.text = ""; if(!data) return; if(snd_ch) { // 再生中 snd_ch.stop(); snd_ch_complete(null); return; } with(play) { upState = overState = hitTestState = stop_u; downState = stop_d; } load.enabled = load.mouseEnabled = false; data.position = audio_pos; samples_sum = 0; eos = false; block_size = i_buff = 0; snd = new Sound(); snd.addEventListener(SampleDataEvent.SAMPLE_DATA, snd_sample_data); snd_tr = new SoundTransform(); snd_tr.volume = (vol_t.x - vol_b.x) / 96; snd_ch = snd.play(0, 0, snd_tr); snd_ch.addEventListener(Event.SOUND_COMPLETE, snd_ch_complete); timer.reset(); timer.start(); } // ボリューム つまみ以外 マウス ダウン private function vol_b_mouse_down(e:MouseEvent):void { var x:int = e.localX - 4; if (x < 0) x = 0; else if(x > 96) x = 96; vol_t.x = vol_b.x + x; vol_mouse_down(e); if(snd_ch) { // 再生中 snd_tr.volume = (vol_t.x - vol_b.x) / 96; snd_ch.soundTransform = snd_tr; } } // ボリューム マウス ダウン private function vol_mouse_down(e:MouseEvent):void { vol_dx = e.stageX - vol_t.x + vol_b.x; vol_b.removeEventListener(MouseEvent.MOUSE_OUT, vol_mouse_out); vol_t.removeEventListener(MouseEvent.MOUSE_OUT, vol_mouse_out); stage.addEventListener(MouseEvent.MOUSE_UP, vol_mouse_up2); stage.addEventListener(MouseEvent.MOUSE_MOVE, vol_mouse_move); } // ボリューム マウス アップ 1 private function vol_mouse_up1(e:MouseEvent):void { Mouse.cursor = MouseCursor.BUTTON; } // ボリューム マウス アップ 2 private function vol_mouse_up2(e:MouseEvent):void { stage.removeEventListener(MouseEvent.MOUSE_UP, vol_mouse_up2); stage.removeEventListener(MouseEvent.MOUSE_MOVE, vol_mouse_move); vol_b.addEventListener(MouseEvent.MOUSE_OUT, vol_mouse_out); vol_t.addEventListener(MouseEvent.MOUSE_OUT, vol_mouse_out); if(!vol_b.hitTestPoint(e.stageX, e.stageY) && !vol_t.hitTestPoint(e.stageX, e.stageY)) Mouse.cursor = MouseCursor.AUTO; } // ボリューム マウス 移動 private function vol_mouse_move(e:MouseEvent):void { var x:int = e.stageX - vol_dx; if (x < 0) x = 0; else if(x > 96) x = 96; vol_t.x = vol_b.x + x; if(snd_ch) { // 再生中 snd_tr.volume = x / 96; snd_ch.soundTransform = snd_tr; } } private function vol_mouse_over(e:MouseEvent):void { if(!e.buttonDown) Mouse.cursor = MouseCursor.BUTTON; } private function vol_mouse_out(e:MouseEvent):void { if(!e.buttonDown) Mouse.cursor = MouseCursor.AUTO; } // サウンド private function snd_sample_data(e:SampleDataEvent):void { if(i_buff < 0) return; for(var cnt:int = 8192; cnt; cnt--) { if(i_buff == block_size) { // バッファ データ終了 if(eos) { i_buff = -1; return; } switch(get_frame()) { case 1: // EOF i_buff = -1; return; case -1: err.text = ERR_FORMAT; // fall thru case -2: if(snd_ch) { // 再生中 // sampleData イベントでサウンドを停止すると,Flash プレイヤーが異常終了する. //snd_ch.stop(); snd_ch_complete(null); } else { i_buff = -1; } return; } if(n_samples) { // total samples あり if(samples_sum + block_size >= n_samples) { block_size = n_samples - samples_sum; eos = true; } } samples_sum += block_size; i_buff = 0; sample_max = 1 << (sample_bits - 1); } var val:Number; val = Number(buff0[i_buff]) / sample_max; // L e.data.writeFloat(val); val = Number(buff1[i_buff]) / sample_max; // R e.data.writeFloat(val); i_buff++; } } // 再生終了 private function snd_ch_complete(e:Event):void { timer.stop(); timer_timer(null); snd_ch.removeEventListener(Event.SOUND_COMPLETE, snd_ch_complete); snd_ch = null; snd.removeEventListener(SampleDataEvent.SAMPLE_DATA, snd_sample_data); snd = null; with(play) { upState = overState = hitTestState = play_u; downState = play_d; } load.enabled = load.mouseEnabled = true; setTimeout(erase_elapsed, 100); } // 経過時間表示 private function timer_timer(e:TimerEvent):void { if(snd_ch) { // 再生中 var sec:uint = uint(snd_ch.position / 1000); elapsed.text = uint(sec / 60).toString() + ":" + ("0" + (sec % 60).toString()).substr(-2); } } // 経過時間消去 private function erase_elapsed():void { elapsed.text = ""; } // FLAC ******************************************** // METADATA_BLOCK // 戻り値 - 0: 正常,-1: エラー,-2: エラー(メッセージ出力済み) private function get_metadata():int { data.endian = Endian.BIG_ENDIAN; try { if(data.readUnsignedInt() != 0x664C6143) // fLaC でない return -1; var wu:uint; for(; ; ) { // METADATA_BLOCK_HEADER var hdr:uint = data.readUnsignedInt(); var len:uint = hdr & 0xFFFFFF; // METADATA_BLOCK_DATA switch((hdr & 0x7F000000) >> 24) { // BLOCK_TYPE case 0: // STREAMINFO min_block = data.readUnsignedShort(); // minimum block size max_block = data.readUnsignedShort(); // maximum block size min_frame = data.readUnsignedByte() << 16; // minimum frame size min_frame |= data.readUnsignedShort(); max_frame = data.readUnsignedByte() << 16; // maximum frame size max_frame |= data.readUnsignedShort(); wu = data.readUnsignedInt(); sample_rate_si = wu >> 12; // sample rate if(sample_rate_si == 0 || sample_rate_si > 655350) return -1; n_channels = ((wu & 0xE00) >> 9) + 1; // number of channels if(n_channels > 6) return -1; sample_bits_si = ((wu & 0x1F0) >> 4) + 1; // bits per sample if(sample_bits_si < 4) return -1; if(sample_bits_si > 24) { err.text = "このプログラムは、24 ビットを超えるサンプル サイズには対応していません。"; return -2; } n_samples = (wu & 0xF) * 4294967296 + data.readUnsignedInt(); // total samples // MD5 signature signature = new ByteArray(); data.readBytes(signature, 0, 16); break; case 4: // VORBIS_COMMENT // 曲タイトル,アーティスト名を取得 var skip:uint = data.position + len; data.endian = Endian.LITTLE_ENDIAN; len = data.readUnsignedInt(); // vendor_length data.position += len; // vendor_string var n_comments:uint = data.readUnsignedInt(); // user_comment_list_length var f:int = 0x3; for(var u:uint = 0; u < n_comments; u++) { len = data.readUnsignedInt(); // length var comment:String = data.readUTFBytes(len); // comment if(comment.substr(0, 6).toUpperCase() == "TITLE=") { title.text = comment.substr(6); f &= ~0x1; } if(comment.substr(0, 7).toUpperCase() == "ARTIST=") { artist.text = comment.substr(7); f &= ~0x2; } if(!f) break; } data.endian = Endian.BIG_ENDIAN; data.position = skip; break; case 127: return -1; default: data.position += len; } if(hdr & 0x80000000) // last metadata block break; } audio_pos = data.position; // オーディオ開始位置保存 buff0 = new Vector.(); buff1 = (n_channels == 1) ? buff0 : new Vector.(); } catch(e:EOFError) { return -1; } return 0; } // FRAME // 戻り値 - 0: 正常,1: EOF,-1: エラー,-2: エラー(メッセージ出力済み) private function get_frame():int { try { var wu:uint; var wi:int; var i:int; // FRAME_HEADER var hdr_start:uint = data.position; var pos1:uint, pos2:uint; var crc:uint; if(!data.bytesAvailable) // EOF return 1; wu = data.readUnsignedShort(); // sync code, blocking strategy if((wu & 0xFFFC) != 0xFFF8) // sync code ? return -1; var blocking:uint = wu & 0x1; // blocking strategy(未使用) var bs_sr:uint = data.readUnsignedByte(); // block size, sample rate wu = data.readUnsignedByte(); // channel assignment, sample size var channel_assign:uint = wu >> 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((wu & 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:Number; wu = data.readUnsignedByte(); if(wu & 0x80) { if(!(wu & 0x40)) return -1; var c:int = 1; for(var bit:uint = 0x20; bit; bit >>= 1) { if(!(wu & bit)) break; c++; } if(!bit) return -1; number = wu & (0x3F >> c); for(; c; c--) { wu = data.readUnsignedByte(); if((wu & 0xC0) != 0x80) return -1; number = number * 64 + (wu & 0x3F); } } else { number = wu; } // block size if((wi = bs_sr >> 4) & 0x8) { block_size = 1 << wi; // 256/512/1024/2048/4096/8192/16384/32768 } else { switch(wi) { case 0: return -1; case 1: block_size = 192; break; case 6: block_size = data.readUnsignedByte() + 1; break; case 7: block_size = data.readUnsignedShort() + 1; break; default: block_size = 576 << (wi - 2); // 576/1152/2304/4608 } } // sample rate switch(wi = bs_sr & 0xF) { case 0: sample_rate = sample_rate_si; break; case 12: sample_rate = data.readUnsignedByte() * 1000; break; case 13: sample_rate = data.readUnsignedShort(); break; case 14: sample_rate = data.readUnsignedShort() * 10; break; case 15: return -1; default: sample_rate = sample_rate_tbl[wi]; } if(sample_rate != 44100) { err.text = "このプログラムは、44.1 KHz 以外のサンプリング レートには対応していません。"; return -2; } // CRC-8 // x^8 + x^2 + x^1 + 1 // このプログラムではシークをしないので,CRC-8 は使わなくてもよいと思うが, // 一応参考のために入れてあります. pos2 = data.position; crc = 0; for(pos1 = hdr_start; pos1 < pos2; ) { crc ^= data[pos1++]; crc ^= (crc << 2) ^ (crc << 1); wu = crc & 0xFF00; crc = (crc & 0xFF) ^ (wu >> 6) ^ (wu >> 7) ^ (wu >> 8); } if(data.readUnsignedByte() != crc) return -1; if(block_size > buff0.length) buff0.length = buff1.length = block_size; // ここからはビット単位で読み込み data_bit = 0; for(var i_ch:int = 0; i_ch < n_channels; i_ch++) { // SUBFRAME var buff:Vector.; switch(i_ch) { case 0: buff = buff0; break; case 1: buff = buff1; break; default: buff = null; } // sample size 調整 var sample_bits_adj:uint = 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:uint = get_bits(6); // subframe type // wasted bits-per-sample wu = get_bits(1); var wasted:uint = 0; if(wu) { for(; ; ) { wasted++; if(get_bits(1)) break; } sample_bits_adj -= wasted; } var sample_sign:int = 0xFFFFFFFF << (sample_bits_adj - 1); var order:uint; // predictor order if (sub_type & 0x20) { // SUBFRAME_LPC order = (sub_type & 0x1F) + 1; // order i = 0; if(buff) { for(; i < order; i++) buff[i] = (int(get_bits(sample_bits_adj)) + sample_sign) ^ sample_sign; } else { for(; i < order; i++) get_bits(sample_bits_adj); } var prec:uint = get_bits(4) + 1; // coefficient precision if(prec == 16) return -1; // coefficient shift wu = get_bits(5); // シフトがマイナスになることがあるのか? var shift:uint; var shift_dir:Boolean; if(wu & 0x10) { // マイナス shift = 0x10000 >> (wu & 0xF); shift_dir = false; } else { shift = 1 << wu; shift_dir = true; } if(order > coeff.length) coeff.length = order; wi = 0xFFFFFFFF << (prec - 1); for(i = 0; i < order; i++) coeff[i] = (int(get_bits(prec)) + wi) ^ wi; // RESIDUAL if(residual(buff, order)) return -1; if(buff) { for(i = order; i < block_size; i++) { var sum:Number = 0; for(var j:int = 0, k:int = i - 1; j < order; j++, k--) sum += Number(buff[k]) * coeff[j]; buff[i] += int(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] = (int(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] = (int(get_bits(sample_bits_adj)) + sample_sign) ^ sample_sign; } else { for(; i < block_size; i++) get_bits(sample_bits_adj); } } else { // SUBFRAME_CONSTANT wi = (int(get_bits(sample_bits_adj)) + sample_sign) ^ sample_sign; if(buff) { for(i = 0; i < block_size; i++) buff[i] = wi; } } // 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:int = buff1[i]; var mid:int = (buff0[i] << 1) | (side & 0x1); buff0[i] = (mid + side) >> 1; buff1[i] = (mid - side) >> 1; } } } // zero-padding // 単に,バッファに残っているビットを捨てるだけ // FRAME_FOOTER // CRC-16 // x^16 + x^15 + x^2 + 1 pos2 = data.position; crc = 0; for(pos1 = hdr_start; pos1 < pos2; ) { wu = (crc >> 8) ^ data[pos1++]; for(i = 7; i; i--) wu ^= wu >> 1; crc = (((crc << 8) | wu) ^ (wu << 15) ^ (wu << 2)) & 0xFFFF; } if(data.readUnsignedShort() != crc) return -1; } catch(e:EOFError) { return -1; } return 0; } // Residual // 戻り値 - 0: 正常,-1: エラー private function residual(buff:Vector., pred_order:uint):int { var param_bits:int; var escape:uint; 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:uint = get_bits(4); // partition order var n_parts:uint = 1 << part_order; var part_size:uint = block_size >> part_order; // partition size var off:int = 0; for(var i_part:int = 0; i_part < n_parts; i_part++) { // RICE_PARTITION,RICE2_PARTITION var param:uint = get_bits(param_bits); // rice parameter var i:int = (i_part) ? 0 : pred_order; var val:int; if(param == escape) { // escape var n:uint = get_bits(5); if(!n) return -1; var sign:int = 0xFFFFFFFF << (n - 1); for(; i < part_size; i++) { val = (int(get_bits(n)) + sign) ^ sign; if(buff) buff[off + i] = val; } } else { for(; i < part_size; i++) { val = 0; while(!get_bits(1)) val++; if(param) val = (val << param) | get_bits(param); if(buff) buff[off + i] = (val & 0x1) ? ~(val >> 1) : val >> 1; } } off += part_size; } return 0; } // ビット単位読み込み private function get_bits(n:uint):uint { var bits:uint = 0; for(; n; n--) { if(!data_bit) { data_byte = data.readUnsignedByte(); data_bit = 0x80; } bits <<= 1; if(data_byte & data_bit) bits |= 0x1; data_bit >>= 1; } return bits; } } }