// ------------------------------------------------- // // 手書き波形でサウンド再生 // // // ------------------------------------------------- package { import flash.events.*; import flash.display.*; import flash.net.*; import flash.text.*; import flash.utils.*; import flash.system.*; import flash.geom.*; import flash.filters.*; import flash.ui.*; import flash.media.*; public class Main extends Sprite { public function Main() { // ------------------------------------------------- // コンストラクタ // ------------------------------------------------- // フレームレート stage.frameRate = 30; // 100%表示 stage.scaleMode = StageScaleMode.NO_SCALE; // 左上 stage.align = StageAlign.TOP_LEFT; stage.align = "TL"; // ステージサイズ var w:uint; var h:uint; // 周波数 var cycle:Number = 220; // 入力サウンドバッファサイズ var SOUND_BUFFER_SIZE:int = 200; // 入力サウンドバッファ var src:Object = { buffer:new Vector.<Number>(), // バッファ pos:0 // 転送位置 } var i:Number; for(i=0;i<SOUND_BUFFER_SIZE;i++){ src.buffer[i] = -Math.sin(i/SOUND_BUFFER_SIZE * Math.PI*2) * 0.5; } // グラフィック var g:Graphics; // 上画面 var sprite:Sprite = new Sprite(); g = sprite.graphics; g.beginFill ( 0xF0F0F0 , 1.0 ); g.drawRoundRect ( 0 , 0 , SOUND_BUFFER_SIZE , 100 , 5 , 5 ); g.endFill(); g.lineStyle ( 0 , 0xFFFFFF , 1.0); g.moveTo( 0,50); g.lineTo(SOUND_BUFFER_SIZE,50); addChild(sprite); sprite.x = 10; sprite.y = 10; // 上画面描画領域 var shape:Shape = new Shape(); sprite.addChild(shape); function Render():void{ g = shape.graphics; g.clear(); g.lineStyle (0, 0x0000FF, 0.2); // 線のスタイル var i:int; var buf:Vector.<Number> = src.buffer; for(i=0;i<buf.length-2;i++){ g.moveTo(i ,buf[i ] * 50 + 50); g.lineTo(i+1,buf[i+1] * 50 + 50); } } Render(); // リザルト作成 var result : Sprite = new Sprite(); addChild(result); result.x = 0; // スライダーコメント作成 var tf_slider_c:TextField = new TextField(); tf_slider_c.x = 10; tf_slider_c.y = 0; tf_slider_c.width = 300; tf_slider_c.height = 20; tf_slider_c.border = false; tf_slider_c.text = "周波数"; result.addChild(tf_slider_c); // スライダー数値作成 var tf_slider_v:TextField = new TextField(); tf_slider_v.x = 60; tf_slider_v.y = 0; tf_slider_v.width = 80; tf_slider_v.height = 20; tf_slider_v.border = true; // 書式 var tf_slider_v_fmt:TextFormat = new TextFormat(); tf_slider_v_fmt.font = "MS ゴシック"; // フォント名 tf_slider_v_fmt.align = TextFormatAlign.CENTER; // 整列 tf_slider_v.defaultTextFormat = tf_slider_v_fmt; // スライダー数値配置 tf_slider_v.text = ""; result.addChild(tf_slider_v); // スライダー作成 var slider:SliderH = new SliderH(stage); slider.x = 10; slider.y = 30; result.addChild(slider); slider.setMinimum(55); slider.setMaximum(440); // スライダーが更新された slider.setListener(function(v:Number):void{ cycle = Math.floor(slider.value * 10) / 10; tf_slider_v.text = String(cycle); }); slider.value = cycle; // ボタン作成 var button_play:Button = new Button(stage); button_play.x = 10; button_play.y = 60; button_play.setSize(80,20); button_play.setLabel("再生開始"); result.addChild(button_play); // ボタンが押された button_play.addEventListener(MouseEvent.CLICK,function(e:MouseEvent):void{ SoundPlay(); }); // ボタン作成 var button_stop:Button = new Button(stage); button_stop.x = 100; button_stop.y = 60; button_stop.setSize(80,20); button_stop.setLabel("停止"); result.addChild(button_stop); // ボタンが押された button_stop.addEventListener(MouseEvent.CLICK,function(e:MouseEvent):void{ SoundStop(); }); // リサイズ時に再配置 stage.addEventListener(Event.RESIZE,ResizeFunc); function ResizeFunc(e:Event):void{ w = stage.stageWidth; h = stage.stageHeight; // 上画面位置 sprite.scaleX = (w - 10 - 10) / SOUND_BUFFER_SIZE; sprite.scaleY = (h - 10 - 100 - 10) / 100; // リザルト位置 result.y = h - 100 + 10; // スライダー slider.setSize(w - 20,10); } ResizeFunc(null); // サウンドパラメータ var sampling:uint = 2048; // 1度に転送するサンプリング数 var loop:Boolean = true; // ループあり var loop_begin:uint = 0; // ループ開始地点 // 出力用サウンドオブジェクト var sound_obj:Sound = null; var channel:SoundChannel = null; // 波形用ワーク var wave:Object = { pos : 0 // 再生位置 }; // サウンドデータ要求時に呼び出されるイベント function SampleDataFunc(event:SampleDataEvent):void{ var out_buffer:ByteArray = event.data; // 開始直後のノイズ対策 if(event.position == 0){ for(i=0;i < 4096;i++){ // 左チャンネル out_buffer.writeFloat(0.0); // 右チャンネル out_buffer.writeFloat(0.0); } return; } // 再生速度 var speed : Number = SOUND_BUFFER_SIZE / (44100 / cycle); var i:int; var buf:Vector.<Number> = src.buffer; for(i=0;i<sampling;i++){ var data : Number = buf[Math.floor(wave.pos)]; // 左右チャンネル out_buffer.writeFloat(data); out_buffer.writeFloat(data); wave.pos += speed; if(wave.pos > SOUND_BUFFER_SIZE - 1){ wave.pos -= SOUND_BUFFER_SIZE - 1; } } } // サウンド開始 function SoundPlay():void{ // 前回のサウンド停止 SoundStop(); // ソース用サウンドオブジェクト作成 sound_obj = new Sound(); // 新しいオーディオデータ要求時に呼び出されるイベント sound_obj.addEventListener(SampleDataEvent.SAMPLE_DATA, SampleDataFunc); // 再生開始 channel = sound_obj.play(); } // サウンド終了 function SoundStop():void{ if(channel){ channel.stop(); channel = null; } } // マウス処理 var push:Boolean = false; var mouse_pos_old:Point; // 前回のマウス位置 var mouse_pos_new:Point; // 今回のマウス位置 sprite.addEventListener(MouseEvent.MOUSE_DOWN,function(e:MouseEvent):void{ push = true; mouse_pos_old = mouse_pos_new = new Point(shape.mouseX,shape.mouseY); }); stage.addEventListener(MouseEvent.MOUSE_MOVE,function(e:MouseEvent):void{ mouse_pos_old = mouse_pos_new; mouse_pos_new = new Point(shape.mouseX,shape.mouseY); if(push){ MouseDrag(mouse_pos_old,mouse_pos_new); } }); stage.addEventListener(MouseEvent.MOUSE_UP,function(e:MouseEvent):void{ push = false; }); function MouseDrag(p0:Point,p1:Point):void{ var buf:Vector.<Number> = src.buffer; var pos0 : Point = new Point(p0.x,p0.y); var pos1 : Point = new Point(p1.x,p1.y); pos0.y = pos0.y / 50.0 - 1.0; pos1.y = pos1.y / 50.0 - 1.0; var d : Number; var min:Number; var max:Number; if(pos0.x < pos1.x){ min = pos0.x; max = pos1.x; }else{ min = pos1.x; max = pos0.x; } min = Math.floor(min + 0.5) + 0.5; max = Math.floor(max + 0.5) + 0.5; var i:Number; for(;min<max;min+=1.0){ if(min < 0) continue; if(min > SOUND_BUFFER_SIZE-1) continue; var nx:Number = -(pos1.y - pos0.y); var ny:Number = (pos1.x - pos0.x); d = -(nx * min -(pos0.x * nx + pos0.y * ny)) / (0 + ny); if(d < -1.0) d = -1.0; if(d > 1.0) d = 1.0; buf[Math.floor(min)] = d; } Render(); } } } } import flash.events.*; import flash.display.*; import flash.net.*; import flash.text.*; import flash.utils.*; import flash.system.*; import flash.geom.*; import flash.filters.*; import flash.ui.*; import flash.media.*; // ------------------------------------------------- // ボタン // ------------------------------------------------- internal class Button extends Sprite { private var _width:Number; private var _height:Number; private var _text:TextField; private var _background:Sprite; public function Button(stage:Stage) { var slider:Button = this; // 背景用スプライト作成 _background = new Sprite(); addChild(_background); // テキストフィールド _text = new TextField(); addChild(_text); _text.x = 0; _text.y = 0; _text.selectable = false; // 書式 var format:TextFormat = new TextFormat(); format.align = TextFormatAlign.CENTER; // 整列 format.font = "MS ゴシック"; // フォント名 format.size = 14; // 文字のポイントサイズ format.color = 0x202020; // 文字の色 _text.defaultTextFormat = format; // マウスオーバーで少し明るく addEventListener(MouseEvent.MOUSE_OVER,function(e:MouseEvent):void{ var color : ColorTransform = new ColorTransform(1,1,1,1,8,8,8,0); transform.colorTransform = color; }); // マウスアウトで元に戻す addEventListener(MouseEvent.MOUSE_OUT,function(e:MouseEvent):void{ var color : ColorTransform = new ColorTransform(1,1,1,1,0,0,0,0); transform.colorTransform = color; }); // デフォルト値 setSize(100,100); update(); } // リサイズ public function setSize(w:Number,h:Number):void{ // 背景リサイズ _width = w; _height = h; update(); } // ラベルセット public function setLabel(str:String):void{ _text.text = str; update(); } // 描画更新 private function update():void{ // 背景描画 var g:Graphics = _background.graphics; // 角丸矩形描画 g.clear(); g.lineStyle ( 0 , 0x808080 , 1.0,false,LineScaleMode.NONE,CapsStyle.ROUND,JointStyle.ROUND); g.beginFill ( 0xF0F0F0 , 1.0 ); g.drawRoundRect ( 0 , 0 , _width , _height , 5 , 5 ); g.endFill(); // テキスト位置修正 _text.width = _width; _text.height = _height; } } // ------------------------------------------------- // 水平方向スライダー // ------------------------------------------------- internal class SliderH extends Sprite { private var _value:Number; private var _minimum:Number; private var _maximum:Number; private var _width:Number; private var _height:Number; private var _width_bar:Number; private var _drag:Boolean; private var _listener:Function; private var _bar:Sprite; private var _background:Sprite; public function SliderH(stage:Stage) { var slider:SliderH = this; var g:Graphics; // 背景配置 _background = new Sprite(); addChild(_background); // バー配置 _bar = new Sprite(); addChild(_bar); _bar.x = 1; _bar.y = 1; // 背景描画 g = _background.graphics; g.lineStyle ( 0 , 0xB0B0B0 , 1.0); g.beginFill ( 0xF0F0F0 , 1.0 ); g.drawRect ( 0 , 0 , 100 , 100); g.endFill(); // バー描画 g = _bar.graphics; g.lineStyle ( 0 , 0x808080 , 1.0); g.beginFill ( 0xA0A0A0 , 1.0 ); g.drawRect ( 0 , 0 , 100 , 100); g.endFill(); // マウスイベント stage.addEventListener(MouseEvent.MOUSE_DOWN,function(e:MouseEvent):void{ if(!_drag){ if(slider.hitTestPoint ( e.stageX , e.stageY , false )){ if(e.buttonDown){ _drag = true; var color : ColorTransform = new ColorTransform(1,1,1,1,4,4,4,0); transform.colorTransform = color; DragEvent(e); } } } }); // マウスイベント stage.addEventListener(MouseEvent.MOUSE_MOVE,function(e:MouseEvent):void{ if(_drag){ if(!e.buttonDown){ _drag = false; // マウスアウトで元に戻す var color : ColorTransform = new ColorTransform(1,1,1,1,0,0,0,0); transform.colorTransform = color; } } if(_drag) DragEvent(e); }); function DragEvent(e:MouseEvent):void{ // ステージマウス座標をローカル座標系に落とし込む var p : Point = new Point(e.stageX,e.stageY); var m : Matrix = slider.transform.matrix; m.invert(); p = m.transformPoint(p); // バーの位置更新 _bar.x = p.x - _bar.width/2; if(_bar.x < 0) _bar.x = 0; if(_bar.x > _width - _bar.width){ _bar.x = _width - _bar.width; } // バーの位置からデフォルト値を決定 var d:Number = (_bar.x) / (_width - _bar.width); d = (_maximum - _minimum) * d + _minimum; value = d; e.updateAfterEvent(); } // デフォルト値 _value = 0.0; _minimum = 0.0; _maximum = 1.0; _drag = false; _listener = null; setSize(100,10); setSizeBar(20); update(); } // 最小値セット public function setMinimum(v:Number):void{ _minimum = v; update(); } // 最大値セット public function setMaximum(v:Number):void{ _maximum = v; update(); } // 通常値取得 public function get value():Number{ return _value; } // 通常値セット public function set value(v:Number):void{ _value = v; update(); // 通知 if(_listener != null) _listener(_value); } // 更新通知 public function setListener(func:Function):void{ _listener = func; } // リサイズ public function setSize(w:Number,h:Number):void{ // 背景リサイズ _width = w; _height = h; update(); } // バーのリサイズ public function setSizeBar(w:Number):void{ // 背景リサイズ _width_bar = w; update(); } // 描画更新 private function update():void{ // リサイズ _background.scaleX = _width / 100; _background.scaleY = _height / 100; _bar.scaleX = (_width_bar - 2) / 100; _bar.scaleY = (_height - 2) / 100; // バーの位置 var length : Number = _width - 2 - _bar.width; var d : Number = (_value - _minimum) / (_maximum - _minimum); _bar.x = length * d + 1; } } 手書き波形でサウンド再生