package { import flash.display.Sprite; import flash.display.Shape; import flash.display.StageScaleMode; import flash.display.SimpleButton; import flash.display.Bitmap; import flash.display.Loader; import flash.display.LoaderInfo; import flash.display.MovieClip; 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.TimerEvent; import flash.ui.Mouse; import flash.ui.MouseCursor; import flash.net.FileReference; import flash.net.FileFilter; import flash.media.SoundTransform; import flash.errors.EOFError; import flash.utils.*; [SWF(width=400, height=280, frameRate=30, backgroundColor=0xCCFFFF)] public class FLAFla 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 dec:SimpleButton; // デコード ボタン private var dec_msg:TextField; // デコード中表示 private var dec_prg: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 swf_f:SimpleButton; // 非圧縮 SWF ボタン private var swf_c:SimpleButton; // 圧縮 SWF ボタン private var flv:SimpleButton; // FLV ボタン private var err:TextField; // エラー表示 private var dec_tmr:Timer; // デコード用タイマー private var file:FileReference = null; // ファイル 読み込み/保存 private var loader:Loader = null; // ローダー private var movie:MovieClip = null; // ムービー private var snd_tr:SoundTransform; // サウンド属性 private var data:ByteArray; // 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 buff0:Vector.; // オーディオ データ バッファ L private var buff1:Vector.; // R private var coeff:Vector. = new Vector.(); // coefficients private var frame_samples:uint; // 1 フレームのサンプル数 private var sec_frames:uint; // 1 秒のフレーム数 private var pcm:ByteArray; // PCM データ private var vol_dx:int; // ボリューム マウス ダウン位置 private const ERR_FORMAT:String = "ファイルの形式が正しくないか、このプログラムでは対応していないバージョンのファイルです。"; public function FLAFla():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; var str:String; // タイトル txt = new TextField(); with(txt) { x = 0; y = 8; width = 400; height = 24; type = TextFieldType.DYNAMIC; selectable = false; text = "FLAC - Flash コンバータ"; 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; str = "開く"; upState = overState = hitTestState = draw_button_t(str, 40, 24, false); downState = draw_button_t(str, 40, 24, true); tabEnabled = false; addEventListener(MouseEvent.CLICK, load_click); } addChild(load); // ファイル名 fnam = new TextField(); with(fnam) { x = 68; y = 32; width = 316; 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); // デコード ボタン dec = new SimpleButton(); with(dec) { x = 16; y = 120; str = "デコード"; upState = overState = hitTestState = draw_button_t(str, 60, 24, false); downState = draw_button_t(str, 60, 24, true); tabEnabled = false; enabled = mouseEnabled = false; addEventListener(MouseEvent.CLICK, dec_click); } addChild(dec); // デコード中表示 dec_msg = new TextField(); with(dec_msg) { x = 92; y = 120; type = TextFieldType.DYNAMIC; selectable = false; visible = false; autoSize = TextFieldAutoSize.LEFT; text = "デコード中"; fmt = new TextFormat(); fmt.size = 12; setTextFormat(fmt); } addChild(dec_msg); // デコード進捗表示 dec_prg = new TextField(); with(dec_prg) { x = 92 + dec_msg.width + 8; y = 120; width = 48; height = 24; type = TextFieldType.DYNAMIC; selectable = false; fmt = new TextFormat(); with(fmt) { align = TextFormatAlign.RIGHT; size = 14; } defaultTextFormat = fmt; } addChild(dec_prg); // 再生/停止ボタン 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 = 48; y = 160; upState = overState = hitTestState = play_u; downState = play_d; tabEnabled = false; enabled = mouseEnabled = false; addEventListener(MouseEvent.CLICK, play_click); } addChild(play); // 経過時間 elapsed = new TextField(); with(elapsed) { x = 88; y = 160; 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 = 160; bmp.y = 163; addChild(bmp); vol_b = new Sprite(); with(vol_b) { x = 186; y = 160; 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 = 282; y = 164; 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); // 非圧縮 SWF ボタン swf_f = new SimpleButton(); with(swf_f) { x = 16; y = 200; str = "非圧縮 SWF 保存"; upState = overState = hitTestState = draw_button_t(str, 108, 24, false); downState = draw_button_t(str, 108, 24, true); tabEnabled = false; enabled = mouseEnabled = false; addEventListener(MouseEvent.CLICK, swf_click); } addChild(swf_f); // 圧縮 SWF ボタン swf_c = new SimpleButton(); with(swf_c) { x = 140; y = 200; str = "圧縮 SWF 保存"; upState = overState = hitTestState = draw_button_t(str, 100, 24, false); downState = draw_button_t(str, 100, 24, true); tabEnabled = false; enabled = mouseEnabled = false; addEventListener(MouseEvent.CLICK, swf_click); } addChild(swf_c); // FLV ボタン flv = new SimpleButton(); with(flv) { x = 256; y = 200; str = "FLV 保存"; upState = overState = hitTestState = draw_button_t(str, 72, 24, false); downState = draw_button_t(str, 72, 24, true); tabEnabled = false; enabled = mouseEnabled = false; addEventListener(MouseEvent.CLICK, flv_click); } addChild(flv); // エラー表示 err = new TextField(); with(err) { x = 16; y = 232; 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); dec_tmr = new Timer(1, 1); dec_tmr.addEventListener(TimerEvent.TIMER, decode); } // テキスト ボタンのイメージを作成する 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; 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 = ""; dec.enabled = dec.mouseEnabled = play.enabled = play.mouseEnabled = swf_f.enabled = swf_f.mouseEnabled = swf_c.enabled = swf_c.mouseEnabled = flv.enabled = flv.mouseEnabled = false; 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; return; } dec.enabled = dec.mouseEnabled = true; } private function load_io_error(e:IOErrorEvent):void { load_cancel(null); err.text = "ファイルの読み込みでエラーが発生しました。"; } // デコード private function dec_click(e:MouseEvent):void { err.text = ""; mouseChildren = false; dec_msg.visible = true; dec_prg.text = "0:00"; e.updateAfterEvent(); data.position = audio_pos; samples_sum = 0; sample_rate = 0; pcm = new ByteArray(); pcm.endian = Endian.LITTLE_ENDIAN; dec_tmr.reset(); dec_tmr.start(); } private function decode(e:TimerEvent):void { e.updateAfterEvent(); var sts:int = 0; for(var cnt:int = 20; cnt; cnt--) { switch(get_frame()) { case 1: // EOF sts = 1; break; case -1: err.text = ERR_FORMAT; // fall thru case -2: sts = -1; break; } if(!sts) { if(n_samples) { // total samples あり if(samples_sum + block_size >= n_samples) { block_size = n_samples - samples_sum; sts = 1; } } samples_sum += block_size; var sec:uint = uint(samples_sum / sample_rate); dec_prg.text = uint(sec / 60).toString() + ":" + ("0" + (sec % 60).toString()).substr(-2); // PCM のビット数を 16 に正規化 var mul:int; var div:int; var i:int = 0; if(sample_bits < 16) { switch(sample_bits) { case 4: mul = 0x1111; div = 0; break; case 5: mul = 0x8421; div = 4; break; case 6: mul = 0x1041; div = 2; break; case 7: mul = 0x4081; div = 5; break; default: mul = (1 << sample_bits) | 1; div = (sample_bits << 1) - 16; } for(; i < block_size; i++) { var val:int; val = (buff0[i] * mul) >> div; pcm.writeShort((val < -32768) ? -32768 : val); // mono or L if(n_channels > 1) { val = (buff1[i] * mul) >> div; pcm.writeShort((val < -32768) ? -32768 : val); // R } } } else { div = sample_bits - 16; for(; i < block_size; i++) { pcm.writeShort(buff0[i] >> div); // mono or L if(n_channels > 1) pcm.writeShort(buff1[i] >> div); // R } } } if(sts) { if(sts > 0) { // エラーでない switch(sample_rate) { case 5512: frame_samples = 1378; sec_frames = 4; break; case 11025: frame_samples = 2205; sec_frames = 5; break; case 22050: frame_samples = 2205; sec_frames = 10; break; default: frame_samples = 2205; sec_frames = 20; } play.enabled = play.mouseEnabled = swf_f.enabled = swf_f.mouseEnabled = swf_c.enabled = swf_c.mouseEnabled = flv.enabled = flv.mouseEnabled = true; } dec_msg.visible = false; dec_prg.text = ""; mouseChildren = true; return; } } dec_tmr.reset(); dec_tmr.start(); } // 再生/停止 private function play_click(e:MouseEvent):void { err.text = ""; if(loader) { // 再生中 stop_movie(); return; } with(play) { upState = overState = hitTestState = stop_u; downState = stop_d; } load.enabled = load.mouseEnabled = dec.enabled = dec.mouseEnabled = false; loader = new Loader(); loader.contentLoaderInfo.addEventListener(Event.INIT, loader_info_init); loader.loadBytes(create_swf(0)); } private function loader_info_init(e:Event):void { loader.contentLoaderInfo.removeEventListener(Event.INIT, loader_info_init); movie = MovieClip(loader.content); movie.addEventListener(Event.ENTER_FRAME, movie_enter_frame); snd_tr = new SoundTransform(); snd_tr.volume = (vol_t.x - vol_b.x) / 96; movie.soundTransform = snd_tr; } private function movie_enter_frame(e:Event):void { if(movie.currentFrame == movie.totalFrames) { stop_movie(); return; } // 経過時間表示 var sec:uint = uint((movie.currentFrame - 2) / sec_frames); elapsed.text = uint(sec / 60).toString() + ":" + ("0" + (sec % 60).toString()).substr(-2); } private function stop_movie():void { if(movie) { movie.removeEventListener(Event.ENTER_FRAME, movie_enter_frame); movie = null; } loader.unloadAndStop(); loader = null; with(play) { upState = overState = hitTestState = play_u; downState = play_d; } load.enabled = load.mouseEnabled = dec.enabled = dec.mouseEnabled = true; elapsed.text = ""; } // ボリューム つまみ以外 マウス ダウン 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(movie) { // 再生中 snd_tr.volume = (vol_t.x - vol_b.x) / 96; movie.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(movie) { // 再生中 snd_tr.volume = x / 96; movie.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; } // SWF 保存 private function swf_click(e:MouseEvent):void { err.text = ""; file = new FileReference(); with(file) { addEventListener(Event.CANCEL, save_cancel); addEventListener(Event.COMPLETE, save_complete); addEventListener(IOErrorEvent.IO_ERROR, save_io_error); } mouseChildren = false; file.save(create_swf((e.currentTarget == swf_f) ? 1 : 2), fnam.text + ".swf"); } // FLV 保存 private function flv_click(e:MouseEvent):void { err.text = ""; var out:ByteArray = new ByteArray(); out.endian = Endian.BIG_ENDIAN; var frame_bytes:uint = (n_channels == 1) ? frame_samples << 1 : frame_samples << 2; // 1 フレームのバイト数 with(out) { // FLV header writeUTFBytes("FLV"); // Signature writeByte(1); // Version writeByte(0x04); // TypeFlagsAudio, TypeFlagsVideo writeUnsignedInt(9); // DataOffset // FLV file body writeUnsignedInt(0); // PreviousTagSize0 var timestamp:uint = 0; var frame_ms:uint = 1000 / sec_frames; for(var off:int = 0; off < pcm.length; off += len) { var len:uint = (off + frame_bytes > pcm.length) ? pcm.length - off : frame_bytes; // Audio tag writeUnsignedInt((8 << 24) | (1 + len)); // TagType, DataSize writeUnsignedInt(((timestamp & 0xFFFFFF) << 8) | (timestamp >>> 24)); // Timestamp, TimestampExtended writeShort(0); // StreamID writeByte(0); var r_t:uint; switch(sample_rate) { case 5512: r_t = 0x0; break; case 11025: r_t = 0x4; break; case 22050: r_t = 0x8; break; default: r_t = 0xC; } if(n_channels > 1) r_t |= 0x1; writeByte(0x32 | r_t); // SoundFormat, SoundRate, SoundSize, SoundType // PCM データ writeBytes(pcm, off, len); // SoundData writeUnsignedInt(11 + 1 + len); // PreviousTagSize timestamp += frame_ms; } } file = new FileReference(); with(file) { addEventListener(Event.CANCEL, save_cancel); addEventListener(Event.COMPLETE, save_complete); addEventListener(IOErrorEvent.IO_ERROR, save_io_error); } mouseChildren = false; file.save(out, fnam.text + ".flv"); } private function save_cancel(e:Event):void { with(file) { removeEventListener(Event.CANCEL, save_cancel); removeEventListener(Event.COMPLETE, save_complete); removeEventListener(IOErrorEvent.IO_ERROR, save_io_error); } file = null; mouseChildren = true; } private function save_complete(e:Event):void { save_cancel(null); } private function save_io_error(e:IOErrorEvent):void { save_cancel(null); err.text = "ファイルの出力でエラーが発生しました。"; } // SWF 作成 private function create_swf(type:int):ByteArray { var out:ByteArray = new ByteArray(); out.endian = Endian.LITTLE_ENDIAN; with(out) { if(type == 2) { // 圧縮 SWF var out2:ByteArray = new ByteArray(); out2.endian = Endian.LITTLE_ENDIAN; create_swf_2(2, out2); writeUTFBytes("CWS"); // Signature } else { position = 8; create_swf_2(type, out); position = 0; writeUTFBytes("FWS"); // Signature } writeByte((type) ? 6 : 9); // Version if(type == 2) { // 圧縮 SWF writeUnsignedInt(8 + out2.length); // FileLength out2.compress(); writeBytes(out2); } else { writeUnsignedInt(length); // FileLength } } return out; } private function create_swf_2(type:int, out:ByteArray):void { var frame_bytes:uint = (n_channels == 1) ? frame_samples << 1 : frame_samples << 2; // 1 フレームのバイト数 with(out) { //00001010 10000000 writeShort(0x800A); // FrameSize writeShort(sec_frames << 8); // FrameRate var cnt:int = int(Math.ceil(pcm.length / frame_bytes)); writeShort((type) ? 1 + cnt : 1 + cnt + 1); // FrameCount // FileAttributes tag writeShort((69 << 6) | 4); // TagCodeAndLength writeByte((type) ? 0x00 : 0x08); // UseDirectBlit, UseGPU, HasMetadata, ActionScript3, UseNetwork writeShort(0x0000); writeByte(0x00); // SoundStreamHead2 writeShort((45 << 6) | 4); // TagCodeAndLength //0000XX1X 0011XX1X var r_t:uint; switch(sample_rate) { case 5512: r_t = 0x000; break; case 11025: r_t = 0x404; break; case 22050: r_t = 0x808; break; default: r_t = 0xC0C; } if(n_channels > 1) r_t |= 0x101; writeShort(0x3202 | r_t); // PlaybackSoundRate, PlaybackSoundSize, PlaybackSoundType, // StreamSoundCompression, StreamSoundRate, StreamSoundSize, StreamSoundType writeShort(frame_samples); // ShowFrame writeShort((1 << 6) | 0); // TagCodeAndLength var off:int = 0; for(; cnt; cnt--) { var len:int = (off + frame_bytes > pcm.length) ? pcm.length - off : frame_bytes; // SoundStreamBlock writeShort((19 << 6) | 0x3F); // TagCodeAndLength // フレームの時間分データがないとノイズが出る //writeInt(len); // Length writeInt(frame_bytes); // Length // PCM データ writeBytes(pcm, off, len); // SoundData if(cnt == 1) { // フレームの時間分データがないとノイズが出る for(; len < frame_bytes; len++) writeByte(0); if(type) { // DoAction writeShort((12 << 6) | 2); // TagCodeAndLength // ActionStop writeByte(0x07); // ActionCode writeByte(0); // ActionEndFlag } } // ShowFrame writeShort((1 << 6) | 0); // TagCodeAndLength off += frame_bytes; } if(!type) // ShowFrame writeShort((1 << 6) | 0); // TagCodeAndLength // End tag writeShort((0 << 6) | 0); // TagCodeAndLength } } // 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.(); if(n_channels > 1) buff1 = 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: wu = sample_rate_si; break; case 12: wu = data.readUnsignedByte() * 1000; break; case 13: wu = data.readUnsignedShort(); break; case 14: wu = data.readUnsignedShort() * 10; break; case 15: return -1; default: wu = sample_rate_tbl[wi]; } if(sample_rate) { if(wu != sample_rate) { err.text = "このプログラムは、可変サンプリング レートには対応していません。"; return -2; } } else { switch(sample_rate = wu) { case 5512: case 11025: case 22050: case 44100: break; default: err.text = "このプログラムは、5512 Hz/11025 Hz/22050 Hz/44100 Hz 以外のサンプリング レートには対応していません。"; 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 = block_size; if(n_channels > 1) 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; } var sample_sign:int = 0xFFFFFFFF << (sample_bits_adj - 1); 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 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; } } }