forked from: Dance Hole
/**
* Copyright mitien ( http://wonderfl.net/user/mitien )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/5wxB
*/
// forked from shohei909's Dance Hole
// forked from Event's Instrument
/* 10月のJAMの課題
「視覚的にも楽しめる楽器アプリをつくってください。」に挑戦。
Sound with footprint.
あしあとに合わせて音が鳴る感じのアプリを作る。
*/
package {
import flash.display.Loader;
import flash.display.Sprite;
import gs.*;
[SWF(width="465",height="465",backgroundColor="0",frameRate="60")]
public class FlashTest extends Sprite {
private var loaders:Vector.<Loader>;
private var data:Data;
public function FlashTest() {
loaders = Data.load(); //画像のロード開始
var nowload:NowLoading = new NowLoading(stage,init); //ロード画面の表示。ロード終了後init()を実行させる。
for each(var loader:Loader in loaders){ nowload.addLoader(loader); } //ロード画面と画像のロードの状態をリンクさせる
}
private function init():void{
TweenLite.to(stage.getChildAt(1),1,{alpha:0,onComplete:function():void{stage.removeChildAt(1)} });
addChild( new DanceHole(465,465) )
}
}
}
import flash.filters.BlurFilter;
import flash.geom.*;
import flash.system.LoaderContext;
import flash.events.Event;
import flash.display.*;
import flash.net.*;
import frocessing.color.ColorHSV;
import org.si.sound.events.*;
import gs.*;
import gs.easing.*;
class DanceHole extends Bitmap{
private var black:BitmapData;
public var stop:Boolean = false;
public var count:int;
public var footCount:int = 0;
public var prints:Vector.<Print> = new Vector.<Print>;
public var data:Data;
public var footX:int = 0;
public var footY:int = 0;
public var step:int = 60; //歩幅
public var pace:int = 1;
public var color:ColorHSV = new ColorHSV(0,0.9,1,0.1);
public var filter:BlurFilter = new BlurFilter(2,2);
private var isDown:Boolean = false
function DanceHole(w:int,h:int){
this.addEventListener("addedToStage",init);
black = new BitmapData(w,h,true,0xFF000000);
data = new Data();
super( black.clone() );
}
private function init(e:Event):void{
this.removeEventListener("addedToStage",init);
this.addEventListener("exitFrame",onFrame);
SiON.driver.dm.addEventListener(SoundObjectEvent.ENTER_FRAME ,onBeat);
stage.addEventListener("mouseMove",onMove);
stage.addEventListener("mouseDown",onDown);
stage.addEventListener("mouseUp",onUp);
footX = mouseX;
footY = mouseY;
}
private function onFrame(e:Event):void{
if(!stop){
draw();
SiON.driver.bpm = 100 + prints.length*3;
}
}
private function onMove(e:Event):void{
if(!stop){
var dx:Number = mouseX - footX;
var dy:Number = mouseY - footY;
var r:Number = Math.sqrt(dx*dx+dy*dy);
var dir:Number = Math.atan2(dy,dx);
while( r > step){
footX += step * Math.cos(dir);
footY += step * Math.sin(dir);
prints.push( new Print(footX,footY,dir,color,footCount++%2==0?"l":"r", isDown) );
r -= step;
}
}
}
private function onUp(e:Event):void{isDown = false}
private function onDown(e:Event):void{isDown = true;}
private function onBeat(e:Event):void{
if(!stop){
count++;
color.h += 10;
sequence();
}
}
private function sequence():void{
var s:Boolean = true
if( count > pace ){ count = 0 }
if( count < 1 ){
if(s && prints.length > 0){
prints.reverse();
var p:Print = prints.pop();
prints.reverse();
var ct:ColorTransform = p.colorTransform;
ct.alphaMultiplier = 1;
ct.redOffset += 150;
ct.greenOffset += 150;
ct.blueOffset += 150;
data.draw(bitmapData,"foot",p.name,p.x,p.y,p.angle,p.colorTransform);
if(!p.mute){ SiON.ar(p.x/465, p.y/465, p.color, p.name == "l");
}else{ SiON.stop();}
}else{
SiON.stop();
}
}
}
private function draw():void{
var b:BitmapData = bitmapData;
for each(var p:Print in prints){
if(!p.mute){ data.draw(b,"foot",p.name,p.x,p.y,p.angle,p.colorTransform); }
}
b.unlock();
b.lock();
b.merge(black,b.rect,new Point,10,10,10,10);
b.applyFilter(b,b.rect,new Point,filter);
}
}
class Print{
public var x:Number,y:Number,angle:Number;
public var color:uint, colorTransform:ColorTransform, name:String, mute:Boolean;
function Print(x:Number,y:Number,dir:Number,color:ColorHSV,name:String,mute:Boolean){
this.x = x; this.y = y; this.angle = ( dir - Math.PI/2) ;
this.color = color.value;
this.colorTransform= new ColorTransform(1,1,1,0.8,color.r,color.g,color.b,0);
this.name = name;
this.mute = mute
}
}
class Data{
static public const URL:Object = {
"manFoot":"http://assets.wonderfl.net/images/related_images/4/4f/4fd0/4fd0652e356d7e09b373d8de9784a51732284d90"
}
static public const URL_NAME:Array=["manFoot"];
static public const IMG_NAME:Object = {
"foot":["l","r"]
}
static public const cellWidth:int=27,cellHeight:int=27;
//各オブジェクトに使うビットマップデータの設定
public var MAP_SET:Object = { "foot":"manFoot" }
//ビットマップを記録したオブジェクト。ロード後に使用可能
static public var imageMap:Object = {};
static public var imageCell:Object = {};
static public var imageRect:Object = {};
static private var loaders:Vector.<Loader> = new Vector.<Loader>();
//画像をロード。ローダーの配列を作る。
static public function load():Vector.<Loader>{
for each(var url:String in URL){
var loader:Loader = new Loader();
loaders.push(loader);
}
loaders[0].load(new URLRequest(URL[URL_NAME[0]]), new LoaderContext(true));
loaders[0].contentLoaderInfo.addEventListener("complete",onLoad,false,1000);
return loaders;
}
//bitmapdataに画像を描画する
public function draw(target:BitmapData,type:String,name:String,x:int,y:int,angle:Number,ct:ColorTransform):void{
var map:String = MAP_SET[type];
var b:BitmapData = imageCell[map][ IMG_NAME[type].indexOf(name) ];
if(-1 < IMG_NAME[type].indexOf(name)){
var mtr:Matrix = new Matrix(1,0,0,-1,-(b.width>>1),-(b.height>>1));
mtr.rotate( angle );
mtr.translate( x, y );
target.draw( b, mtr, ct );
}
}
static private var loadNum:int = 0;
static private function onLoad(e:Event):void{
e.currentTarget.removeEventListener("complete",onLoad);
var rect:Rectangle = e.currentTarget.content.getRect(e.currentTarget.content);
imageMap[URL_NAME[loadNum]]=new BitmapData(rect.width,rect.height,true,0x000000);
imageMap[URL_NAME[loadNum]].draw( e.currentTarget.content );
imageMap[URL_NAME[loadNum]].lock();
setImageRect(URL_NAME[loadNum]);
loadNum++;
if(URL_NAME.length>loadNum){
loaders[loadNum].load(new URLRequest(URL[URL_NAME[loadNum]]), new LoaderContext(true));
loaders[loadNum].contentLoaderInfo.addEventListener("complete",onLoad,false,1000);
}
}
static private function setImageRect(name:String):void{
imageCell[name] = [];
var map:BitmapData = imageMap[name];
var lineColor:uint = map.getPixel32(map.width-1,map.height-1);
var x:int = 0; var y:int=0; var height:int=0; var width:int=0; var count:int=0;
while(true){
width=0;height=0;
if(lineColor != map.getPixel32(x,y) ){
for(var i:int=1;i+x<map.width;i++){
if( lineColor == map.getPixel32(x+i,y) ){break;}
}
width=i;
for(var j:int=1;j+y<map.width;j++){
if( lineColor == map.getPixel32(x,y+j) ){break;}
}
height=j;
var rect:Rectangle = new Rectangle(x,y,width,height);
var rect2:Rectangle = new Rectangle(0,0,width,height);
var cell:BitmapData = new BitmapData(rect.width,rect.height,true,0x0)
cell.setVector( rect2,map.getVector( rect ) );
imageCell[name].push( cell );
}
x+=width+1;
if(x>=map.width){ y+=height+1;x=0; }
if(y>=map.height){ break; }
count++;
}
}
}
class NowLoading extends Sprite{
static public const COMPLETE:String = "complete";
public var loaders:Vector.<Object> = new Vector.<Object>;
public var bytesTotal:uint=0,bytesLoaded:uint=0;
private var _loaderNum:uint=0,_completedNum:uint=0,_openNum:uint=0; //ローダーの数
private var text:Bitmap, sprite:ProgressSprite;
private var onLoaded:Function;
private var LETTER:Object = {//文字
"1":[[0,1,1],[0,0,1],[0,0,1],[0,0,1],[0,0,1]],"2":[[1,1,1],[0,0,1],[0,1,1],[1,0,0],[1,1,1]],"3":[[1,1,1],[0,0,1],[1,1,1],[0,0,1],[1,1,1]],"4":[[1,0,1],[1,0,1],[1,0,1],[1,1,1],[0,0,1]],"5":[[1,1,1],[1,0,0],[1,1,1],[0,0,1],[1,1,1]],
"6":[[1,1,1],[1,0,0],[1,1,1],[1,0,1],[1,1,1]],"7":[[1,1,1],[0,0,1],[0,0,1],[0,0,1],[0,0,1]],"8":[[1,1,1],[1,0,1],[1,1,1],[1,0,1],[1,1,1]],"9":[[1,1,1],[1,0,1],[1,1,1],[0,0,1],[0,0,1]],"0":[[1,1,1],[1,0,1],[1,0,1],[1,0,1],[1,1,1]],
".":[[0],[0],[0],[0],[1]]," ":[[0],[0],[0],[0],[0]],"n":[[0,0,0],[0,0,0],[1,1,1],[1,0,1],[1,0,1]],"w":[[0,0,0,0,0],[0,0,0,0,0],[1,0,1,0,1],[1,0,1,0,1],[1,1,1,1,1]],"o":[[0,0,0],[0,0,0],[1,1,1],[1,0,1],[1,1,1]],
"a":[[0,0,0],[0,0,1],[1,1,1],[1,0,1],[1,1,1]],"l":[[1],[1],[1],[1],[1]],"i":[[1],[0],[1],[1],[1]],"d":[[0,0,1],[0,0,1],[1,1,1],[1,0,1],[1,1,1]],"g":[[0,0,0],[0,0,0],[1,1,1],[1,0,1],[1,1,1],[0,0,1],[1,1,1]],
"C":[[1,1,1],[1,0,0],[1,0,0],[1,0,0],[1,1,1]],"O":[[1,1,1],[1,0,1],[1,0,1],[1,0,1],[1,1,1]],"M":[[1,1,1,1,1],[1,0,1,0,1],[1,0,1,0,1],[1,0,1,0,1],[1,0,1,0,1]],"P":[[1,1,1],[1,0,1],[1,1,1],[1,0,0],[1,0,0]],
"T":[[1,1,1],[0,1,0],[0,1,0],[0,1,0],[0,1,0]],"L":[[1,0,0],[1,0,0],[1,0,0],[1,0,0],[1,1,1]],"E":[[1,1,1],[1,0,0],[1,1,1],[1,0,0],[1,1,1]]
}
//ステージと関数を渡す
public function NowLoading(stage:Stage, onLoaded:Function = null){
if(onLoaded == null){ this.onLoaded=nullFunc }else{ this.onLoaded=onLoaded }
sprite = new ProgressSprite(stage.stageWidth,stage.stageHeight);
text = new Bitmap( new BitmapData(30*4,8,true,0x00000000 ) );
stage.addChild(this); addChild(sprite); addChild(text);
with(text){scaleX=scaleY=1; blendMode="invert"; x=stage.stageWidth-text.width; y=stage.stageHeight-text.height;}
}
//ローダーの追加
public function addLoader(loader:Loader):Loader{ setListener(loader.contentLoaderInfo);_loaderNum++;return loader;}
public function addURLLoader(loader:URLLoader):URLLoader{setListener(loader); _loaderNum++; return loader;}
private function nullFunc():void{}
private function setListener(loader:*):void{
loader.addEventListener("open", onOpen);
loader.addEventListener("complete", onComplete);
loader.addEventListener("progress", update);
}
private function update(e:Event=null):void{
bytesLoaded=0; bytesTotal=0;
for each(var loadObj:Object in loaders){
bytesLoaded += loadObj.bytesLoaded;
bytesTotal += loadObj.bytesTotal;
};
sprite.progress(bytesLoaded/bytesTotal * _openNum/_loaderNum);
if(bytesTotal!=0){ setText( "now loading... "+(bytesLoaded/bytesTotal* _openNum/_loaderNum*100).toFixed(1) ); }
}
private function onOpen(e:Event):void{ _openNum++;loaders.push(e.currentTarget); bytesTotal+=e.currentTarget.bytesTotal; }
private function onComplete(e:Event):void{ _completedNum++;if(_loaderNum == _completedNum){ setText( "COMPLETE" );onLoaded(); } }
private function setText(str:String):void{
var b:BitmapData = text.bitmapData; var l:int = str.length; var position:int = b.width;
b.lock();b.fillRect(b.rect,0x000000);
for(var i:int=0;i<l;i++){
var letterData:Array = LETTER[str.substr(l-i-1,1)];position-=letterData[0].length+1;
for(var n:int=0;n<letterData.length;n++){ for(var m:int=0;m<letterData[n].length;m++){
if(letterData[n][m]==1){b.setPixel32(m+position,n+1,0xFF000000);}
} }
}
b.unlock();
}
}
//このスプライトを編集することでロード画面を変えることができる。
class ProgressSprite extends Sprite{
private var mapData:BitmapData,sphereData:BitmapData,noizeData:BitmapData;
private var bfRate:Number=0; //前の段階での進行度
private var drawRate:Number=0;
private var maxLevel:int = 5;
private var meter:Array = new Array();
//コンストラクタ
public function ProgressSprite(width:int,height:int):void{
mapData = new BitmapData(width,height,true,0x00000000);
addChild(new Bitmap(mapData)).blendMode="invert";
for(var i:int=0;i<maxLevel;i++){
meter[i]=0;
}
addEventListener("enterFrame",onFrame);
}
//ロードが進行したときに呼び出される。 rateはロードの進行度で0-1
public function progress(rate:Number):void{ bfRate = rate; }
//ロードの進行度に合わせて、描画。
private function draw(rate:Number, level:int=0):void{
var thick:int = mapData.height*(0.61803)/1.61803;
var floor:int = 0;
for(var i:int=1;i<level+1;i++){
thick*=(0.61803)/1.61803;
floor+=thick;
}
mapData.fillRect( new Rectangle(0,mapData.height-floor,mapData.width*rate,thick), 0x1000000*int(0xFF*(maxLevel-level+1)/(maxLevel)));
}
private function onFrame(e:Event):void{
for(var i:int=0;i<maxLevel;i++){
var n:int = Math.pow(2,i+2);
meter[i]=(bfRate+ meter[i]*(n-1))/n;
draw(meter[i],i);
}
}
}
//ライブラリ
//SiON-音声全般 http://www.libspark.org/wiki/keim/SiON
//サンプルコード:http://wonderfl.net/search?q=org.si.sion
import org.si.sion.*;
import org.si.sound.*;
import org.si.sound.synthesizers.*;
class MyDriver extends SiONDriver {
public var waveTableSynth:WaveTableSynth;
public var dm:DrumMachine = new DrumMachine(3, 8, 3, 2, 2, 2);
public var ar:Arpeggiator = new Arpeggiator();
public var fill:SiONData;
function MyDriver():void{
super();
volume = 1;
play();
bpm = 240;
dm.volume = 0.0;
dm.snareVolume = 0.6;
dm.bassVolume = 0.8;
dm.play();
ar.volume = 0.8;
ar.noteLength = 1;
ar.gateTime = 1;
ar.effectSend1 = ar.volume * 0.4;
ar.effectSend2 = ar.volume * 0.5;
waveTableSynth = new WaveTableSynth();
waveTableSynth.color = 0x1203acff;
waveTableSynth.releaseTime = 0.2;
ar.synthesizer = waveTableSynth;
ar.play();
}
}
class SiON{
//SiONDriverは一つしか作れないので、staticで作る。
static public var driver:MyDriver = new MyDriver();
static public function ar(x:Number,y:Number,color:int,left:Boolean):void{
driver.ar.pattern = left ? [0,-8] : [-8, 0];
driver.pan = x*2.4 - 1.2;
driver.ar.scaleIndex = - y * 20 + 10;
driver.waveTableSynth.color = color;
TweenLite.to(driver.ar, 0, {volume:0.8} );
}
static public function stop():void{
TweenLite.to(driver.ar, 4, {volume:0,ease:Expo.easeOut} );
}
}