各コンポーネントをインスタンス化し, ボタンに対して各クラスの関数を関連付けします。
//************************************************************************ //2012/03/22 //手書き波形をFFT //Proggramed by Koten-Kairoya //************************************************************************ package { import flash.display.Graphics; import flash.display.Sprite; import flash.display.Bitmap; import flash.events.Event; import flash.events.MouseEvent; import flash.events.TextEvent; import flash.text.*; //画面サイズと背景色,フレームレートを設定 [SWF(width = "522", height = "490", frameRate = "30", backgroundColor = "0xffffff")] public class Main extends Sprite { //================================================================ //各種スプライト //時間領域描画用スプライト public var canvas:Canvas = new Canvas(512, 200); //FFT結果表示用スプライト public var fftcanvas:fftCanvas = new fftCanvas(512, 220); //区切り線,ボタン配置用スプライト public var sp_button:Sprite = new Sprite(); //================================================================ //各種ボタン public var sin_Button:LightGreenButton = new LightGreenButton("sin(t)"); public var sin20_Button:LightGreenButton = new LightGreenButton("sin(20t)"); public var clear_button:LightCyanButton = new LightCyanButton("消去"); public var fft_button:LightOrangeButton = new LightOrangeButton("FFT"); //================================================================ //main関数 public function Main():void { if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE, init); } //================================================================ //コンストラクタ private function init(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, init); // entry point //============================================================ //FFT結果表示用スプライト表示 addChild(fftcanvas); fftcanvas.x = 5 fftcanvas.y = 265; //============================================================ //テキストの設定 var tf_time:TextField = new TextField(); var tf_frequency:TextField = new TextField(); var tf_origin1:TextField = new TextField(); var tf_origin2:TextField = new TextField(); var tf_t:TextField = new TextField(); var tf_omega:TextField = new TextField(); var format:TextFormat = new TextFormat(); //フォント設定 format.size = 20; format.color = 0x000000; format.font = "Arial"; tf_time.defaultTextFormat = format; tf_frequency.defaultTextFormat = format; tf_origin1.defaultTextFormat = format; tf_origin2.defaultTextFormat = format; tf_t.defaultTextFormat = format; tf_omega.defaultTextFormat = format; //自動サイズ設定 tf_time.autoSize = TextFieldAutoSize.LEFT; tf_frequency.autoSize = TextFieldAutoSize.LEFT; tf_origin1.autoSize = TextFieldAutoSize.LEFT; tf_origin2.autoSize = TextFieldAutoSize.LEFT; tf_t.autoSize = TextFieldAutoSize.LEFT; tf_omega.autoSize = TextFieldAutoSize.LEFT; //テキスト tf_time.text = "時間領域波形"; tf_frequency.text = "周波数領域スペクトル"; tf_origin1.text = "o"; tf_origin2.text = "1"; tf_t.text = "t"; tf_omega.text = "ω"; //マウスでドラッグしたときに反転しないようにする tf_time.selectable = false; tf_frequency.selectable = false; tf_origin1.selectable = false; tf_origin2.selectable = false; tf_t.selectable = false; tf_omega.selectable = false; //各TextFieldを表示し,座標を設定 addChild(tf_time); tf_time.x = 5; tf_time.y = 15; addChild(tf_frequency); tf_frequency.x = 5; tf_frequency.y = 275; addChild(tf_origin1); tf_origin1.x = 3; tf_origin1.y = 100; addChild(tf_origin2); tf_origin2.x = 3; tf_origin2.y = 463; addChild(tf_t); tf_t.x = 505; tf_t.y = 75; addChild(tf_omega); tf_omega.x = 498; tf_omega.y = 463;//455; //============================================================ //時間領域波形描画用スプライトを表示 addChild(canvas); canvas.x = 5; canvas.y = 5; //============================================================ //ボタン配置用スプライトを用意 sp_button.graphics.beginFill(0xfffacd, 1); sp_button.graphics.drawRect(0, 0, 512, 60); sp_button.graphics.endFill(); sp_button.graphics.lineStyle(5, 0x696969); sp_button.graphics.moveTo(0, 2); sp_button.graphics.lineTo(512, 2); sp_button.graphics.moveTo(0, 60); sp_button.graphics.lineTo(512, 60); sp_button.x = 5; sp_button.y = 205; addChild(sp_button); //============================================================ //sin(t)ボタンを登録 sp_button.addChild(sin_Button); sin_Button.x = 64 - sin_Button.width / 2; sin_Button.y = sp_button.height/2 - sin_Button.height/2; sin_Button.addEventListener(MouseEvent.CLICK, onSinButtonClick); //sin(20t)ボタンを登録 sp_button.addChild(sin20_Button); sin20_Button.x = 192 - sin20_Button.width / 2; sin20_Button.y = sp_button.height/2 - sin20_Button.height/2; sin20_Button.addEventListener(MouseEvent.CLICK, onSin20ButtonClick); //消去ボタンを登録 sp_button.addChild(clear_button); clear_button.x = 320 - clear_button.width/2; clear_button.y = sp_button.height/2 - clear_button.height/2; clear_button.addEventListener(MouseEvent.CLICK, onClearButton1Click); //fftボタンを登録 sp_button.addChild(fft_button); fft_button.x = 448 - fft_button.width / 2; fft_button.y = sp_button.height/2 - fft_button.height/2; fft_button.addEventListener(MouseEvent.CLICK, onFFTButtonClick); //============================================================ //外枠を描く this.graphics.lineStyle(10, 0x696969); this.graphics.drawRect(0, 0, 522, 490); } //================================================================ //sin(t)ボタンが押された時に呼ばれる関数 private function onSinButtonClick(e:MouseEvent):void { canvas.clear(); fftcanvas.clear(); for (var i:int = 0; i < canvas.Width; i++ ) { canvas.data[i] = canvas.Height/4*Math.sin( 2 * Math.PI / canvas.Width * i ); } canvas.makegraph(); } //================================================================ //sin(20t)ボタンが押された時に呼ばれる関数 private function onSin20ButtonClick(e:MouseEvent):void { canvas.clear(); fftcanvas.clear(); for (var i:int = 0; i < canvas.Width; i++ ) { canvas.data[i] = canvas.Height/4*Math.sin(20* 2 * Math.PI / canvas.Width * i ); } canvas.makegraph(); } //================================================================ //消去ボタンが押された時に呼ばれる関数 private function onClearButton1Click(e:MouseEvent):void { //時間領域波形・周波数領域波形ともに表示をクリア,データもクリア。 canvas.clear(); fftcanvas.clear(); } //================================================================ //FFTボタンがクリックされた時に呼ばれる関数 private function onFFTButtonClick(e:MouseEvent):void { //実際にメモリ上にあるデータを薄い青色で表示 canvas.makegraph(); //fft画面とデータをクリア。 fftcanvas.clear(); //時間領域波形データをfftデータへコピーする for (var i:int = 0; i < 512; i++ ) { //canvasのデータをfftcanvasのデータの実部へコピーする fftcanvas.data[i][0] = canvas.data[i]; //虚部のデータはゼロ fftcanvas.data[i][1] = 0; } //fft実行 fftcanvas.fft(); } } }
マウスドラッグにより,スプライト上に波形を描きます。 512ポイントデータを作製します。
package { import flash.display.Bitmap; import flash.display.Shape; import flash.display.Sprite; import flash.events.MouseEvent; import flash.events.Event; import flash.text.*; public class Canvas extends Sprite { //色の設定 public var backgroundColor:int = 0x0000ff; public var lineColor:int = 0x0000FF; public var lineColor2:int = 0x1E90FF; //キャンバスの寸法 public var Width:int = 0; public var Height:int = 0; //クリック中フラグ public var click_flag:Boolean = false; //座標記録用 public var pre_x:Number = 0; public var pre_y:Number = 0; //データ記録用 public var data:Array = new Array(); //軸関係テキスト public var origin_text:TextField = new TextField(); public var t_text:TextField = new TextField(); public var format:TextFormat = new TextFormat(); //コンストラクタ public function Canvas(width:int, height:int) { //寸法を保存 this.Width = width; this.Height = height; //キャンバスを初期化 this.clear(); //キャンバスにイベントリスナを登録 addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown); addEventListener(MouseEvent.MOUSE_UP, onMouseUp); addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove); addEventListener(MouseEvent.ROLL_OUT, onMouseRollout); } //================================================================ //キャンバス上の波形を消去する関数 public function clear():void { //背景色で塗りつぶす this.graphics.clear(); this.graphics.lineStyle(1, backgroundColor, 0.1); this.graphics.beginFill(backgroundColor, 0.1); this.graphics.drawRect(0, 0, Width, Height); this.graphics.endFill(); //軸を描く this.graphics.lineStyle(2, 0x000000); this.graphics.moveTo(0, Height / 2); this.graphics.lineTo(Width, Height / 2); //補助線 this.graphics.lineStyle(1, 0x6f6f6f); this.graphics.moveTo(Width/2, 0); this.graphics.lineTo(Width / 2, Height); this.graphics.moveTo(Width/4, 0); this.graphics.lineTo(Width / 4, Height); this.graphics.moveTo(3* Width/4, 0); this.graphics.lineTo(3 * Width/4, Height); this.graphics.moveTo(0, Height/4); this.graphics.lineTo(Width, Height/4); this.graphics.moveTo(0, 3*Height/4); this.graphics.lineTo(Width, 3 * Height / 4); //矢印 this.graphics.lineStyle(1, 0x000000); this.graphics.beginFill(0x000000); this.graphics.moveTo(Width, Height/2); this.graphics.lineTo(Width - 12, Height/2 - 5); this.graphics.lineTo(Width - 12, Height/2 + 5); this.graphics.lineTo(Width, Height/2); this.graphics.endFill(); //データをリセットする for (var i:int = 0; i < Width; i++ ) { data[i] =0; } } //================================================================ //グラフ再描画関数 public function makegraph():void { for (var i:int = 0; i < Width - 1; i++ ) { //薄い青色で,data配列中のデータを元にグラフを描く this.graphics.lineStyle(3, lineColor2); this.graphics.moveTo(i, -data[i]+100); this.graphics.lineTo(i+1, -data[i+1]+100); } } //================================================================ //キャンバス上でマウスのボタンが押された時に呼ばれる関数 private function onMouseDown(e:MouseEvent):void { //click中フラグを立てておく click_flag = true; } //================================================================ //キャンバス上でマウスのボタンが離された時に呼ばれる関数 private function onMouseUp(e:MouseEvent):void { //click中フラグを下げる click_flag = false; } //================================================================ //キャンバス上でマウスが動いた場合に呼ばれる関数 private function onMouseMove(e:MouseEvent):void { //マウスのボタンが押されていれば,軌跡を描画する if (click_flag == true) { //線分を描く this.graphics.lineStyle(3, lineColor); this.graphics.moveTo(pre_x, pre_y); this.graphics.lineTo(e.localX, e.localY); //データ配列に現在座標を追加 //ただし,下方向に座標が大きくなるので,通常のグラフのように見せるにはマイナスを付ける必要あり。 //canvasの中心をx軸とするため,オフセット調整で Height/2を足しておく。 data[e.localX] = -e.localY + Height/2; } //過去の座標を更新 pre_x = e.localX; pre_y = e.localY; } //================================================================ //キャンバス上からマウスが領域外に行ったときに呼ばれる関数 private function onMouseRollout(e:MouseEvent ):void { //click_flagを下げておく click_flag = false; } } }
このクラス内の“data[]”に与えられたデータをFFT処理し,表示します。 512ポイントFFTです。
package { import flash.display.Bitmap; import flash.display.Sprite; import flash.events.MouseEvent; import flash.events.Event; import flash.text.*; import flash.utils.ByteArray; public class fftCanvas extends Sprite { //色の設定 public var backgroundColor:int = 0xFFF0F5; public var lineColor:int = 0xFF7500; //キャンバスの寸法 public var Width:int = 0; public var Height:int = 0; //データ記録用 public var data:Array = new Array(); //最終的にスペクトル情報を保存する配列 public var final_data:Array = new Array(); //テキスト関係 public var origin_text:TextField = new TextField(); public var format:TextFormat = new TextFormat(); //コンストラクタ public function fftCanvas(width:int, height:int) { //寸法を保存 this.Width = width; this.Height = height; //キャンバスを初期化 this.clear(); //原点のテキスト format.font = "Arial"; format.size = 20; format.color = 0x000000; origin_text.text = "o" origin_text.autoSize = TextFieldAutoSize.LEFT; origin_text.selectable = false; origin_text.y = 195; origin_text.x = -2; origin_text.setTextFormat(format); origin_text.defaultTextFormat = format; //addChild(origin_text); } //================================================================ //キャンバス上の関数を消去する関数 public function clear():void { //背景色で塗りつぶす this.graphics.clear(); this.graphics.lineStyle(1, backgroundColor); this.graphics.beginFill(backgroundColor, 1); this.graphics.drawRect(0, 0, Width, Height); this.graphics.endFill(); //中心線を描く this.graphics.lineStyle(1, 0x6f6f6f); this.graphics.moveTo(Width/2, 0); this.graphics.lineTo(Width / 2, Height); this.graphics.moveTo(Width/2-1, 0); this.graphics.lineTo(Width / 2 - 1, Height); //矢印,座標軸を描く this.graphics.lineStyle(4, 0x000000); this.graphics.moveTo(0, Height-20); this.graphics.lineTo(500, Height-20); this.graphics.lineStyle(1, 0x000000); this.graphics.beginFill(0x000000); this.graphics.moveTo(Width, Height-20); this.graphics.lineTo(Width - 12, Height-20 - 5); this.graphics.lineTo(Width - 12, Height-20 + 5); this.graphics.lineTo(Width, Height-20); this.graphics.endFill(); //データをリセット for (var i:int = 0; i < Width ; i++ ) { data[i] = [0, 0]; final_data[i] = 0; } } //================================================================ //FFT演算関数(わかりやすくするために若干冗長) //================================================================ public function fft():void { //============================================================ //各種変数 //============================================================ //for文用変数 var i:int; //バタフライ演算の2つのデータのインターバル var AB_interval:int = data.length / 2; //現在演算中の段数(ステップ数)を示す変数 var step_index:int; //各ステップの中で,現在演算中のブロックを示す変数 var block_index:int; //各ブロックの中で,現在演算中のデータ番号を示す変数 var data_index:int; var data_index_B:int; //バタフライ演算中の一時保存データ(実部,虚部) var data_temp_re:Number; var data_temp_im:Number; var data_temp_reB:Number; var data_temp_imB:Number; //回転因子が進む最小単位 var w:Number = 2 * Math.PI / data.length; //回転因子のインデックス var Twiddle_index:int; //? var AB_interval2:int = 0; //規格化用変数 var max_val:Number = 0; //============================================================ //FFT演算実行 //============================================================ //FFT演算の段数は,データ総数がn点の場合,log2(n)段だけ繰り返す。 for (step_index = 1; step_index < data.length; step_index = step_index * 2 ) { AB_interval2 = AB_interval * 2; //各段の中で,バタフライ演算のまとまりは2^(段数-1)回だけある。 for (block_index=0; block_index < data.length; block_index += AB_interval2 ) { //回転因子をリセット Twiddle_index = 0; //各バタフライ演算のまとまりの中で,バタフライ演算の回数はデータ総数nの場合,n/2回ずつある。 for (data_index = block_index; data_index < block_index + AB_interval; data_index++ ) { //データBのインデックスは,データAのインデックス+AB_interbalで指定される。 data_index_B = data_index + AB_interval; //================================================ //バタフライ演算実行。 //元のデータAを一時保存。 data_temp_re = data[data_index][0]; data_temp_im = data[data_index][1]; data_temp_reB = data[data_index_B][0]; data_temp_imB = data[data_index_B][1]; //新しいデータAを計算: A = (a + b) data[data_index][0] = data_temp_re + data_temp_reB; //実部 data[data_index][1] = data_temp_im + data_temp_imB; //虚部 //新しいデータBを計算: B = (a - b)*exp(jw) これを複素演算する関数"mulTwiddle"を別に用意。 data[data_index_B][0] = mulTwiddle( data_temp_re - data_temp_reB, data_temp_im - data_temp_imB, w*Twiddle_index, true); //実部 data[data_index_B][1] = mulTwiddle( data_temp_re - data_temp_reB, data_temp_im - data_temp_imB, w*Twiddle_index, false); //虚部 //================================================ //回転因子を進める Twiddle_index = Twiddle_index + step_index; } //1ブロックあたりの演算終了 } //1段あたりの演算終了 //1段分の演算終了後,データAとデータBの間隔を半分にする。 AB_interval = AB_interval / 2; } //FFT演算終了(すべての段で演算終了) //============================================================ //ビットリバース処理 //============================================================ for (i = 0; i < data.length; i++ ) { if (i < bitReverse(i)) { data_temp_re = data[i][0]; data_temp_im = data[i][1]; //データを入れ替える data[i][0] = data[bitReverse(i)][0]; data[i][1] = data[bitReverse(i)][1]; data[bitReverse(i)][0] = data_temp_re; data[bitReverse(i)][1] = data_temp_im; } } //============================================================ //複素数の絶対値を取る //============================================================ for (i = 0; i < data.length; i++ ) { //絶対値計算 final_data[i] = (Math.sqrt( Math.pow(data[i][0], 2) + Math.pow(data[i][1], 2) )); //最大値を探す if (max_val < final_data[i]) { max_val = final_data[i]; } } //============================================================ //最大値を用いて規格化 //============================================================ for (i = 0; i < final_data.length; i++ ) { final_data[i] *= (Height-20)/max_val; } //============================================================ //グラフ更新 //============================================================ for (i = 0; i < data.length; i++ ) { this.graphics.lineStyle(3, lineColor); this.graphics.moveTo(i-1, Height-20); this.graphics.lineTo(i - 1, -1 * final_data[i] + Height-20); //矢印,座標軸を描く this.graphics.lineStyle(4, 0x000000); this.graphics.moveTo(0, Height - 20); this.graphics.lineTo(500, Height - 20); this.graphics.lineStyle(1, 0x000000); this.graphics.beginFill(0x000000); this.graphics.moveTo(Width, Height - 20); this.graphics.lineTo(Width - 12, Height-20 - 5); this.graphics.lineTo(Width - 12, Height-20 + 5); this.graphics.lineTo(Width, Height-20); this.graphics.endFill(); } } //================================================================ //回転因子("Twiddle factor"):exp(-jω)との積をとる関数 //================================================================ private function mulTwiddle(re:Number, im:Number, theta:Number, re_flag:Boolean ):Number { //回転因子: exp(-jw) = cos(w) -jsin(w) //ある複素数 (a + jb) と上の回転因子との積を考えると, // (a + jb) * (cos(w) - jsin(w)) = acos(w) + bsin(w) + jbcos(w) - jasin(w) //実部: acos(w) + bsin(w) //虚部: bcos(w) - asin(w) if (re_flag == true) { return re * Math.cos(theta) + im * Math.sin(theta); } else { return im * Math.cos(theta) - re * Math.sin(theta); } } //================================================================ //ビット反転関数 //================================================================ private function bitReverse(number:int):int { //全ビット数 var bit_num:int = Math.log(data.length) / Math.log(2); //for文の繰り返し用にビット数をコピーしておく var loop_num:int = bit_num; //ビット配列(各要素が0か1で,2進数データを表す) var bit_array:Array = new Array(); //最終的に返す値用変数 var return_val:int; //2進数では,0ビット目から始まるので,「ビット数-1」ビット目がMSBが対応する。 bit_num -= 1; //ビット数分だけ繰り返す for (var i:int = 0; i < loop_num; i++ ) { if (number >= Math.pow(2, bit_num)) { //引き算する number -= Math.pow(2, bit_num); //その桁に"1"をセット bit_array.push(1); } else { //その桁に"0"をセット bit_array.push(0); } //桁をひとつ下げる bit_num -= 1 ; } //ビット逆順に取り出す for (i = loop_num-1; i >=0 ; i-- ) { //ビットが"1"なら値を加算。 if (bit_array.pop() == 1) { //2^nの値をreturn_valに加算して2進→10進変換 return_val += Math.pow(2,i ); } } return return_val; } } }
普通のボタンクラスです。 このクラス内ではイベントとして,単にボタンの画像を入れ替える処理のみを実装しています。 このボタンをインスタンスするクラス内で,再度イベントリスナを登録し,所望の処理を実装します。
※“LightGreenButton_off.png”と“LightGreenButton_on.png”という画像を読み込んでいます。
※このクラスの他に,Mainクラスでインスタンス化されている“LightCyanButton”,“LightOrangeButton”は このクラスで使用する画像を入れ替えただけのものです。 それぞれ,“LightCyanButton_off.png”と“LightCyanButton_on.png”, “LightOrangeButton_off.png”と“LightOrangeButton_on.png”の画像が必要になります。 適当に自前の画像で代用可です。
package { import flash.display.Sprite; import flash.text.*; import flash.events.MouseEvent; import flash.display.Bitmap; public class LightGreenButton extends Sprite { //画像ファイルを読み込む [Embed(source = "./LightGreenButton_off.png")] public var LightGreenButton_off_Img:Class; public var button_off:Bitmap = new LightGreenButton_off_Img(); [Embed(source = "./LightGreenButton_on.png")] public var LightGreenButton_on_Img:Class; public var button_on:Bitmap = new LightGreenButton_on_Img(); //テキスト public var text:TextField = new TextField(); public var format:TextFormat = new TextFormat(); //クリック中フラグ public var button_flag:Boolean = false; //コンストラクタ(ボタンのサイズは画像依存,引数は表示するテキスト) public function LightGreenButton(button_text:String) { //ボタンoffの画像を表示 addChild(button_off); //テキスト関係 format.size = 28; format.color = 0x000000; format.font = "Arial"; text.defaultTextFormat = format; text.setTextFormat(format); text.autoSize = TextFieldAutoSize.LEFT; text.selectable = false; text.text = button_text; addChild(text); text.x = this.width / 2 - text.width / 2; text.y = this.height/2 - text.height / 2; //イベントリスナを登録 addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown); addEventListener(MouseEvent.MOUSE_UP, onMouseUp); addEventListener(MouseEvent.ROLL_OUT, onMouseRollout); } private function onMouseDown(e:MouseEvent):void { if (button_flag == false) { button_flag = true; removeChild(button_off); addChild(button_on); setChildIndex(text, 1); } } private function onMouseUp(e:MouseEvent):void { if (button_flag == true) { button_flag = false; removeChild(button_on); addChild(button_off); setChildIndex(text, 1); } } private function onMouseRollout(e:MouseEvent):void { if (button_flag == true) { button_flag = false; removeChild(button_on); addChild(button_off); setChildIndex(text, 1); } } } }