// 音に反応するフォント FontDance // 音が出るので注意して下さい package { import flash.display.Sprite; import flash.display.StageQuality; import com.flashdynamix.utils.SWFProfiler; [SWF(backgroundColor="0xffffff", frameRate="30")] public class FontDanceController extends Sprite { public function FontDanceController():void { //SWFProfiler.init(stage, this); stage.quality = StageQuality.HIGH; //背景をしく graphics.beginFill(0x151515); graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight); graphics.endFill(); //配置 var trick:FontDance = new FontDance(this); addChild(trick); } } } import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.BlendMode; import flash.display.DisplayObjectContainer; import flash.display.GradientType; import flash.display.SpreadMethod; import flash.display.Sprite; import flash.events.Event; import flash.filters.ConvolutionFilter; import flash.geom.Matrix; import flash.geom.Point; import flash.media.SoundMixer; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.text.TextFormat; import flash.utils.ByteArray; class FontDance extends Sprite { //この2つを設定すると色々な文字で同じ演出が可能 //protected const TARGET_TEXT:String = "WONDERFL"; //protected const SCALE:Number = 0.7; //文字大きさ(調整用) protected const TARGET_TEXT:String = "DON"; protected const SCALE:Number = 1.0; //文字大きさ(調整用) protected var _s:DisplayObjectContainer; protected var _centerX:Number; //中心X protected var _centerY:Number; //中心Y protected var _soundM:soundManager; protected var _view:Sprite = new Sprite(); protected var _pointList:Vector.<Point> = new Vector.<Point>(); //文字のアウトラインのポイント protected var _distList:Vector.<Number> = new Vector.<Number>(); //ポイントの真ん中からの距離 protected var _radiusList:Vector.<Number> = new Vector.<Number>(); //ポイントの半径の長さ protected var _colorList:Vector.<uint> = new Vector.<uint>(); //ポイントの色 protected var _colorPattern:Vector.<uint> = new Vector.<uint>(); //色パターン protected var _pointViewList:Vector.<Sprite> = new Vector.<Sprite>(); //各ポイントのビュー public function FontDance(s:DisplayObjectContainer):void { _s = s; //中点 _centerX = s.stage.stageWidth / 2; _centerY = s.stage.stageHeight / 2; init(); } protected function init():void { setColor(); //色初期化 setPoint(); //文字アウトラインポイント初期化 _soundM = new soundManager(false, true); //addChild(_view); _soundM.addEventListener(soundManager.SAMPLE_DATA, processSound); _soundM.addEventListener(soundManager.COMPLETE, complete); addEventListener(Event.ENTER_FRAME, update); } protected function setColor():void { //色設定 _colorPattern.push(0x444444); _colorPattern.push(0x555555); _colorPattern.push(0x666666); } protected function setPoint():void { //テキスト設置 var txt:TextField = new TextField(); var tf:TextFormat = new TextFormat("_sans", 100); txt.defaultTextFormat = tf; txt.autoSize = TextFieldAutoSize.LEFT; txt.text = TARGET_TEXT; //ビットマップ設置 var txtBmpData:BitmapData = new BitmapData( txt.width, txt.height, true, 0xffffffff); txtBmpData.draw(txt); //文字のエッジ検出 var cf : ConvolutionFilter = new ConvolutionFilter(3, 3, [-1, -1, -1, -1, 8, -1, -1, -1, -1]); txtBmpData.applyFilter(txtBmpData, txtBmpData.rect, new Point(), cf); var txtBmp:Bitmap = new Bitmap(txtBmpData); //addChild(txtBmp); //ポイント設置 var offset:Point = new Point(_centerX - txt.textWidth / 2, _centerY - txt.textHeight / 2); var p:Point, sp:Sprite; for ( var i:int = 0; i < txtBmpData.width; i++ ) { for ( var j:int = 0; j < txtBmpData.height; j++ ) { if (txtBmpData.getPixel(i, j) == 0xffffff) { //初期化 _pointList.push(new Point(i, j).add(offset)); } } } //ランダムに減らす var tmpList:Vector.<Point> = new Vector.<Point>(); while (_pointList.length>512) { i = Math.floor(Math.random() * _pointList.length); tmpList = _pointList.slice(0, i); tmpList = tmpList.concat(_pointList.slice(i+1)); _pointList = tmpList.slice(); } for each(p in _pointList) { _distList.push(0.0); _radiusList.push(3.0); _colorList.push(_colorPattern[Math.floor(Math.random() * _colorPattern.length)]); sp = new Sprite(); sp.blendMode = BlendMode.ADD; //ブレンドモードオン _pointViewList.push(addChild(sp) as Sprite); } } protected function processSound(e:Event):void { setSoundData(); } private function setSoundData():void { var bytes:ByteArray = new ByteArray(); const CHANNEL_LENGTH:int = 256; //stretchFactor高めに設定 SoundMixer.computeSpectrum(bytes, true, 64); //左チャンネル for (var i:int = 0; i < CHANNEL_LENGTH; i++) { var val:Number = bytes.readFloat(); _distList[i] = val * 0.01; //距離設定 _radiusList[i] = val * 8 + 2; //半径設定 } //右チャンネル for (i = CHANNEL_LENGTH; i > 0; i--) { val = bytes.readFloat(); _distList[255 + i] = val * 0.01; //距離設定 _radiusList[255 + i] = val * 8 + 2; //半径設定 } } protected function complete(e:Event):void { trace("complete"); _soundM.removeEventListener(soundManager.SAMPLE_DATA, processSound); removeEventListener(Event.ENTER_FRAME, update); } protected function update(e:Event):void { var xx:Number, yy:Number; var p:Point, mat:Matrix = new Matrix();; //_view.graphics.clear(); for (var i:int=0; i < _pointList.length; i++ ) { p = _pointList[i]; xx = (p.x - _centerX) * SCALE + _centerX; yy = (p.y - _centerY) * SCALE + _centerY; xx = (p.x - _centerX) * SCALE + _centerX; yy = (p.y - _centerY) * SCALE + _centerY; xx - _centerX > 0 ? xx += Math.pow(xx - _centerX, 2) * _distList[i] : xx -= Math.pow(xx - _centerX, 2) * _distList[i]; yy - _centerY > 0 ? yy += Math.pow(yy - _centerY, 2) * _distList[i] * 2 : yy -= Math.pow(yy - _centerY, 2) * _distList[i] * 2; xx = xx - _radiusList[i] / 2; yy = yy - _radiusList[i] / 2; mat.createGradientBox(_radiusList[i] * 2, _radiusList[i] * 2, 0, -_radiusList[i], -_radiusList[i]); _pointViewList[i].graphics.clear(); //見た目の演出 _pointViewList[i].graphics.beginGradientFill( GradientType.RADIAL, [_colorList[i] , 0x222222], [1, 0.1], [0xAA, 0xFF], mat, SpreadMethod.REFLECT); _pointViewList[i].graphics.drawCircle(xx, yy, _radiusList[i]); //_view.graphics.drawCircle(xx, yy, _radiusList[i]); } } } import flash.events.Event; import flash.events.EventDispatcher; import flash.events.SampleDataEvent; import flash.media.Sound; import flash.media.SoundMixer; import flash.net.URLRequest; import flash.utils.ByteArray; class soundManager extends EventDispatcher { public static const COMPLETE:String = "COMPLETE"; public static const SAMPLE_DATA:String = "SAMPLE_DATA"; //ディスパッチャ用 private const TYPE:uint = 1; private const SOUND_SAMPLING_RATE:Number = 44.1; //サンプリングレート private const SOUND_BUFFER_LENGTH:Number = 2048; //サウンドの取得バッフャ数 private var _sourceSnd:Sound; //元音源 private var _outputSnd:Sound; //コピー音源 private var _urlReq:URLRequest = new URLRequest("http://linkalink.jp/enok/wonderfl/font_dance/1.mp3"); //private var _urlReq:URLRequest = new URLRequest("http://techno.to/sound/COR020-2/1.mp3"); private var _soundBytes:ByteArray; private var _soundBytesPosition:uint; private var _soundBytesTotal:uint; private var _repeat:Boolean; private var _useFFTMode:Boolean; public function soundManager(repeat:Boolean = true, useFFTMode:Boolean=false):void { _repeat = repeat; _useFFTMode = useFFTMode; init(); } private function init():void { _sourceSnd = new Sound(); _outputSnd = new Sound(); _sourceSnd.load(_urlReq); _sourceSnd.addEventListener(Event.COMPLETE, loadSoundComplete); } private function loadSoundComplete(e:Event):void { _sourceSnd.removeEventListener(Event.COMPLETE, loadSoundComplete); //trace(_sourceSnd.length, _sourceSnd.length * SOUND_SAMPLING_RATE, _sourceSnd.length * SOUND_SAMPLING_RATE * 8); if (TYPE == 1) { //音の全データを一括で取得 _soundBytes = new ByteArray(); _sourceSnd.extract(_soundBytes, _sourceSnd.length * SOUND_SAMPLING_RATE * 8); _soundBytesTotal = _soundBytes.length; _soundBytesPosition = 0; } _outputSnd = new Sound(); _outputSnd.addEventListener(SampleDataEvent.SAMPLE_DATA, processSound); _outputSnd.play(); } private function processSound(e:SampleDataEvent):void { var end:Boolean = false; if (TYPE == 1) { for (var i:uint = 0; i < SOUND_BUFFER_LENGTH; ++i) { if(_soundBytesPosition > _soundBytesTotal - 8){ if (_repeat) { _soundBytesPosition -= _soundBytesTotal; }else { _outputSnd.removeEventListener(SampleDataEvent.SAMPLE_DATA, processSound); end = true; dispatchEvent(new Event(COMPLETE)); break; } } if(!end){ _soundBytes.position = _soundBytesPosition; e.data.writeFloat( _soundBytes.readFloat() / 8 ); e.data.writeFloat( _soundBytes.readFloat() / 8 ); _soundBytesPosition += 8; } } }else if (TYPE == 2) { //現在の音を取得するパターン、何故かビットレートが早くなってしまう var bytes:ByteArray = new ByteArray(); _sourceSnd.extract(bytes, 512 * 8); bytes.position = 0; var cnt:uint = 0; while (bytes.bytesAvailable > 0) { e.data.writeFloat(bytes.readFloat()); e.data.writeFloat(bytes.readFloat()); if (bytes.bytesAvailable > 0){ bytes.position += 8; } cnt++; } } dispatchEvent(new Event(SAMPLE_DATA)); } } DON forked from: Saqoosha challenge for professionals