forked from: Simple spectrum analyzer (SiON FFT module + FP10.1 microphone)
Mashed:
forked
and
http://wonderfl.net/c/wpbB
/**
* Copyright aobyrne ( http://wonderfl.net/user/aobyrne )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/qGdx
*/
/**
* SoundMixer.computeSpectrum の第3引数 stretchFactor
* による波形への影響を調べています
*
* stretchFactor =
* 0 1 2 3
* 4 5 6 7
* 8 9 10 11
* 12 13 14 15
*
* インタラクションはステージ上クリックによるFFTModeの切り替えのみ
*
* computeSpectrumメソッドを使っているため、
* Youtubeやニコニコ動画が裏で流れていると失敗します
*/
package
{
import flash.display.Sprite
public class EffectableClient extends Sprite
{
public function EffectableClient()
{
addChild(new Effectable)
}
}
}
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Graphics;
import flash.display.GraphicsPathCommand;
import flash.display.GraphicsPathWinding;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.SampleDataEvent;
import flash.media.Sound;
import flash.media.SoundMixer;
import flash.net.URLRequest;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.utils.ByteArray;
import org.si.utils.FFT;
import frocessing.color.ColorLerp;
class Effectable extends Sprite
{
//----------------------------------------
//CLASS CONSTANT
private const SOUND_SAMPLING_RATE:Number = 44.1;
private const SOUND_BUFFER_LENGTH:Number = 2048;
private const W:uint = 465;
private const H:uint = 465;
private const PI:Number = Math.PI;
private const PI2:Number = PI * 2;
//----------------------------------------
//VARIABLES
private var _spectrums:Vector.<Number>;
private var _dynamicSound:Sound;
private var _soundBytes:ByteArray;
private var _soundBytesPosition:uint;
private var _soundBytesTotal:uint;
private var _verts:Vector.<Vector.<Number>>;
private var _waves:Vector.<Sprite>;
private var _useFFTMode:Boolean=true;
private var _field:TextField;
private var voicePrint:BitmapData = new BitmapData(465, 256, false, 0);
//----------------------------------------
//STAGE INSTANCES
//----------------------------------------
//METHODS
/**
* Constructor.
*/
public function Effectable():void
{
if (stage)
{
init(null);
}
else
{
addEventListener(Event.ADDED_TO_STAGE, init);
}
}
private function init(e:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
_initFFT();
addChild(new Bitmap(voicePrint)).y = 100;
var n:uint = 16;
var d:uint = Math.sqrt(n);
_waves = new Vector.<Sprite>(n, true);
_verts = new Vector.<Vector.<Number>>(n, true);
for (var i:uint = 0; i < n; ++i)
{
var wave:Sprite = addChild( new Sprite() ) as Sprite;
wave.x = int(i % d) * (W / d) + W / d / 2;
wave.y = int(i / d) * (H / d) + H / d / 2;
_waves[i] = wave;
_verts[i] = new Vector.<Number>(256, true);
for (var j:uint = 0; j < 256; ++j) _verts[i][j] = 0;
}
_spectrums = new Vector.<Number>(512, true);
_field = addChild( new TextField() ) as TextField;
_field.x = 2;
_field.y = 2;
_field.width = W;
_field.height = H;
_field.defaultTextFormat = new TextFormat("MS GOTHIC, _ゴシック", 12);
_field.selectable = false;
_field.text = "loading sound...";
_field.visible = false;
//stage.addEventListener(MouseEvent.MOUSE_UP, _stageMouseUpHandler);
_loadSound();
}
private function _stageMouseUpHandler(e:MouseEvent = null):void
{
_useFFTMode = !_useFFTMode;
_field.text = (_useFFTMode) ? "fft (click stage to change fft mode)" :
"raw (click stage to change fft mode)" ;
}
private function _loadSound():void
{
// var base:String = "assets/";
var base:String = "http://lab.alumican.net/wonderfl/effectable/";
var sound:Sound = new Sound();
// sound.load( new URLRequest(base + "bit158-2.mp3") );
sound.load( new URLRequest(base + "bit184-1.mp3") );
// sound.load( new URLRequest(base + "bit187-1.mp3") );
sound.addEventListener(Event.COMPLETE, _loadSoundCompleteHandler);
}
private function _loadSoundCompleteHandler(e:Event):void
{
_soundBytes = new ByteArray();
var sound:Sound = e.currentTarget as Sound;
sound.removeEventListener(Event.COMPLETE, _loadSoundCompleteHandler);
sound.extract(_soundBytes, sound.length * SOUND_SAMPLING_RATE * 8);
_soundBytesTotal = _soundBytes.length;
_soundBytesPosition = 0;
_dynamicSound = new Sound();
_dynamicSound.addEventListener(SampleDataEvent.SAMPLE_DATA, _soundSampleDataHandler);
_dynamicSound.play();
_useFFTMode = false;
_stageMouseUpHandler();
addEventListener(Event.ENTER_FRAME, _update);
}
private function _update(e:Event):void
{
var n:uint = _waves.length;
for (var i:uint = 0; i < n; ++i) _getSpectrums(i);
}
private function _drawWave(index:uint):void
{
_getSpectrums(index);
var verts:Vector.<Number> = _verts[index];
var commands:Vector.<int> = new Vector.<int>();
var vertices:Vector.<Number> = new Vector.<Number>();
var angle:Number = 0;
var radius:Number = 20;
var n:uint = _spectrums.length / 2;
for (var i:uint = 0; i < n; ++i)
{
verts[i] += ( (_useFFTMode ? Math.sqrt(_spectrums[i]) : _spectrums[i]) * radius - verts[i]) * 0.3;
var r:Number = radius + verts[i];
angle = i * PI2 / n;
var x:Number = r * Math.cos(angle);
var y:Number = r * Math.sin(angle);
vertices.push(x, y);
commands.push(GraphicsPathCommand.LINE_TO);
}
commands[0] = GraphicsPathCommand.MOVE_TO;
commands.push(GraphicsPathCommand.LINE_TO);
vertices.push(vertices[0], vertices[1]);
var g:Graphics = _waves[index].graphics;
g.clear();
g.lineStyle(0, 0x000000, 0);
g.beginFill(0x000000);
g.drawPath(commands, vertices, GraphicsPathWinding.EVEN_ODD);
g.endFill();
}
private function _soundSampleDataHandler(e:SampleDataEvent):void
{
for (var i:uint = 0; i < SOUND_BUFFER_LENGTH; ++i)
{
_soundBytesPosition += 8;
if (_soundBytesPosition > _soundBytesTotal - 8) _soundBytesPosition -= _soundBytesTotal;
else if (_soundBytesPosition < 7 ) _soundBytesPosition += _soundBytesTotal;
_soundBytes.position = _soundBytesPosition;
e.data.writeFloat( _soundBytes.readFloat() / 2 );
e.data.writeFloat( _soundBytes.readFloat() / 2 );
}
for (e.data.position=0; e.data.bytesAvailable>0;) {
_execFFT(e.data);
_updateImage();
} }
private function _getSpectrums(stretchFactor:uint = 0):void
{
var bytes:ByteArray = new ByteArray();
SoundMixer.computeSpectrum(bytes, _useFFTMode, stretchFactor);
for (var i:uint = 0; i < 512; ++i) _spectrums[i] = bytes.readFloat();
}
private function _updateImage() : void {
voicePrint.scroll(-4, 0);
for (var i:int=0; i < 256; i++) {
voicePrint.setPixel(461, 255-i, grad[frm[3][i]]);
voicePrint.setPixel(462, 255-i, grad[frm[2][i]]);
voicePrint.setPixel(463, 255-i, grad[frm[1][i]]);
voicePrint.setPixel(464, 255-i, grad[frm[0][i]]);
}
}
private var src:Vector.<Vector.<Number>> = new Vector.<Vector.<Number>>(4);
private var dst:Vector.<Vector.<Number>> = new Vector.<Vector.<Number>>(4);
private var amp:Vector.<Vector.<Number>> = new Vector.<Vector.<Number>>(4);
private var frm:Vector.<Vector.<int>> = new Vector.<Vector.<int>>(4);
private var hamm:Vector.<Number> = new Vector.<Number>(2048);
private var fft:FFT = new FFT(2048);
private var grad:Array, scale:Number;
private function _initFFT() : void {
var i:int;
for (i=0; i<4; i++) {
src[i] = new Vector.<Number>(2048);
dst[i] = new Vector.<Number>(2048);
amp[i] = new Vector.<Number>(1024);
frm[i] = new Vector.<int>(256);
}
for (i=0; i<2048; i++) hamm[i]=0.54-Math.cos(i*0.00306796)*0.46;
ColorLerp.mode = "hsv";
grad = ColorLerp.gradient(0x000040, 0xff0000, 256);
scale = 25;
}
private function _execFFT(data:ByteArray) : void {
var index:int, i:int, j:int, n:Number,
f:Vector.<int>, a:Vector.<Number>,
s0:Vector.<Number> = src[0],
s1:Vector.<Number> = src[1],
s2:Vector.<Number> = src[2],
s3:Vector.<Number> = src[3];
for (index=3; index>=0; --index) {
for (i=0; i<512; i++) {
n = data.readFloat();
j = i; s0[j] = n * hamm[j];
j += 512; s1[j] = n * hamm[j];
j += 512; s2[j] = n * hamm[j];
j += 512; s3[j] = n * hamm[j];
}
a = amp[index];
f = frm[index];
fft.setData(s3).calcDCT().getMagnitude(a)
for (i=0; i<256; i++) { // from 86Hz to 5.6kHz
n = Math.log(a[i+4]*scale);
f[i] = int(((n<0) ? 0 : (n>1) ? 1 : n) * 255);
}
s3 = s2;
s2 = s1;
s1 = s0;
s0 = src[index];
}
}
}