Struggling with Actionscript computeSpectrum and beat detection - actionscript-3

I am trying to understand the values given back to me from the computeSpectrum method. I want to work with the lower frequencies pick out the bass drum of a track. The numbers I am getting back from the byteArray make no sense. For example, it says that the value is 0 when there is clearly a sound playing. What am I missing here...I do know beat detection is not easy and have looked at most of the posts here on the subject...It's just that the numbers that are returned to me make no sense, can somebody explain them to me? Thanks in advance.
My Code:
import flash.display.Graphics;
import flash.events.Event;
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.media.SoundMixer;
import flash.net.URLRequest;
import flash.display.MovieClip;
var snd: Sound = new Sound();
var req: URLRequest = new URLRequest("mySong.mp3");
snd.load(req);
var channel: SoundChannel;
channel = snd.play();
addEventListener(Event.ENTER_FRAME, onEnterFrame);
snd.addEventListener(Event.SOUND_COMPLETE, onPlaybackComplete);
const CHANNEL_LENGTH: int = 256;
const BUFFER_LENGTH: int = 512;
var bytes:ByteArray = new ByteArray();
function onEnterFrame(event: Event): void
{
SoundMixer.computeSpectrum(bytes, true, 0);
for (var i:int = 0; i < CHANNEL_LENGTH; i++) // channel_length = 256
{
var sampleValue:Number = bytes.readFloat() * 200;
var byteArrayIndex = bytes.position;
trace(byteArrayIndex, sampleValue);
}
}
function onPlaybackComplete(e:Event):void
{
removeEventListener(Event.ENTER_FRAME, onEnterFrame);
}

Nice to meet you again, I think I'm more understand your mind this time.
If you are going to check whether there exist a 'beat' at a specific time of the song;
I may suggest this method for reference:
import flash.events.Event;
var snd: Sound = new Sound();
var req: URLRequest = new URLRequest("mySong.mp3");
snd.load(req);
var channel: SoundChannel;
channel = snd.play();
addEventListener(Event.ENTER_FRAME, onEnterFrame);
snd.addEventListener(Event.SOUND_COMPLETE, onPlaybackComplete);
// I want to specify which region of byteData is passing to my check function.
// this two value help me to extract the sample I need.
var checkLength:int = 20;
var checkStart:int = 0;
var theshold:Number = 0.5; // How big the value should I determine it is the 'beat'.
const CHANNEL_LENGTH: int = 256;
const BUFFER_LENGTH: int = 512;
var myArray:Array;
var bytes:ByteArray = new ByteArray();
function onEnterFrame(event: Event): void
{
SoundMixer.computeSpectrum(bytes, true, 0);
myArray = [];
for (var i:int = checkStart; i < checkLength; i+=8) // extract the sample
{
var sampleValue:Number = bytes.readFloat();
myArray.push(sampleValue);
}
if( CheckBeat( myArray ))
{
trace("here maybe a beat!!", getTimer());
}
}
function CheckBeat(valueArray:Array):Boolean // check the whether 'beat' exist
{
var meanValue:Number = valueArray[0];
for(var i:int = 1; i < valueArray.length; i++)
{
meanValue = (meanValue + valueArray[i])/2;
}
return meanValue > theshold;
}
function onPlaybackComplete(e:Event):void
{
removeEventListener(Event.ENTER_FRAME, onEnterFrame);
}
But this method is just an Estimation, and not accurate enough at all.

Related

Stage dimensions in a new NativeWindow

i'm building a flash desktop App where the user clicks on a button and opens a SWF file (a game) in a new window, i used a NativeWindow to achieve it, i did the folowing:
var windowOptions:NativeWindowInitOptions = new NativeWindowInitOptions();
windowOptions.systemChrome = NativeWindowSystemChrome.STANDARD;
windowOptions.type = NativeWindowType.NORMAL;
var newWindow:NativeWindow = new NativeWindow(windowOptions);
newWindow.stage.scaleMode = StageScaleMode.NO_SCALE;
newWindow.stage.align = StageAlign.TOP_LEFT;
newWindow.bounds = new Rectangle(100, 100, 2000, 800);
newWindow.activate();
var aLoader:Loader = new Loader;
aLoader.load(new URLRequest(path));
newWindow.stage.addChild(aLoader);
the result is this:
the game doesn't take the whole space available. When i change the "NO_SCALE" to "EXACT_FIT":
newWindow.stage.scaleMode = StageScaleMode.EXACT_FIT;
i got this:
it only shows the top-left corner of the game. I also tried "SHOW_ALL" and "NO_BORDER". I got the same result as "EXACT_FIT".
When i open the SWF file of the game separatly it's displayed normally:
any idea how can i display the SWF game as the image above?
The best solution for that is to have the dimensions of your external game in pixel. you have to stretch the loader to fit it in your stage. if you are try to load different games with different sizes to your stage, save the swf dimensions with their URL addresses.
var extSWFW:Number = 550;
var extSWFH:Number = 400;
var extURL:String = "./Data/someSwf.swf";
var mainStageWidth:Number = 1024;
var mainStageHeight:Nubmer = 768;
var aLoader:Loader = new Loader;
aLoader.load(new URLRequest(extURL));
newWindow.stage.addChild(aLoader);
aLoader.scaleY = aLoader.scaleX = Math.min(mainStageWidth/extSWFW,mainStageHeight/extSWFH);
It is not very difficult to figure dimensions of a loaded SWF because it is engraved into it's header, you can read and parse it upon loading. Here's the working sample:
package assortie
{
import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Rectangle;
import flash.net.URLRequest;
import flash.utils.ByteArray;
public class Dimensions extends Sprite
{
private var loader:Loader;
public function Dimensions()
{
super();
loader = new Loader;
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaded);
loader.load(new URLRequest("outer.swf"));
}
private function onLoaded(e:Event):void
{
var aBytes:ByteArray = loader.contentLoaderInfo.bytes;
var aRect:Rectangle = new Rectangle;
var aRead:BitReader = new BitReader(aBytes, 8);
var perEntry:uint = aRead.readUnsigned(5);
aRect.left = aRead.readSigned(perEntry) / 20;
aRect.right = aRead.readSigned(perEntry) / 20;
aRect.top = aRead.readSigned(perEntry) / 20;
aRect.bottom = aRead.readSigned(perEntry) / 20;
aRead.dispose();
aRead = null;
trace(aRect);
}
}
}
import flash.utils.ByteArray;
internal class BitReader
{
private var bytes:ByteArray;
private var position:int;
private var bits:String;
public function BitReader(source:ByteArray, start:int)
{
bits = "";
bytes = source;
position = start;
}
public function readSigned(length:int):int
{
var aSign:int = (readBits(1) == "1")? -1: 1;
return aSign * int(readUnsigned(length - 1));
}
public function readUnsigned(length:int):uint
{
return parseInt(readBits(length), 2);
}
private function readBits(length:int):String
{
while (length > bits.length) readAhead();
var result:String = bits.substr(0, length);
bits = bits.substr(length);
return result;
}
static private const Z:String = "00000000";
private function readAhead():void
{
var wasBits:String = bits;
var aByte:String = bytes[position].toString(2);
bits += Z.substr(aByte.length) + aByte;
position++;
}
public function dispose():void
{
bits = null;
bytes = null;
}
}

Generating Random text on Collision AS3

I would like to generate some random words when an object hits another one.
I've tried it but only one word comes up and never changes again.
Also would it be better if I make a new class for the random texts or is including it in the Main class good enough? Thanks!
Here's my current code:
package {
import flash.display.MovieClip;
import flash.utils.Timer;
import flash.events.TimerEvent;
import flash.events.Event;
public class Main extends MovieClip {
public var cell:Cell;
public var group:Array;
public var gameTimer:Timer;
public var score:int = 0;
var array:Array = new Array ("Apples",
"Bananas",
"Grapes",
"Oranges",
"Pineapples"); //create an array of possible strings
var randomIndex:int = Math.random () * array.length;
//public var randomTxt:String;
public function Main() {
group = new Array();
gameTimer = new Timer (25);
gameTimer.addEventListener(TimerEvent.TIMER,moveCell);
gameTimer.start();
}
private function createCell(_x:Number = 0, _y:Number = 0):void {
var cell:Cell = new Cell(_x, _y);
group.push(cell);
addChild(cell);
}
function moveCell (timerEvent:TimerEvent):void {
if (Math.random() < 0.01) {
var randomX:Number = Math.random()*800;
var randomY:Number = Math.random()*600;
createCell(randomX, randomY);
}
for (var i:int = 0; i < group.length; i++)
{
var cell:Cell = Cell(group[i]);
if (cell.hitTestObject(island))
{
cell.parent.removeChild(cell);
group.splice(i,1);
score++;
txtWordDisplay.text = "Killed by" + array [randomIndex];
}
}
scoreOutPut.text = score.toString();
}
}
}
It's always the same word because you only set the randomIndex once at the beginning of your program.
A simple fix would be to update that value when the collision happens:
if (cell.hitTestObject(island))
{
...
randomIndex = Math.floor(Math.random () * array.length);
txtWordDisplay.text = "Killed by" + array [randomIndex];
}

ActionScript - how to put 10 different dishes (loaders) in a random sequence?

Hello im with a doubt and need your help if its possible.
I have a class dish and i load 1 file ".swf", and i have a little game that works like this:
The dish moves in the x and y axis and i when i click in the dish it falls down.
But i want to have not only 1 dish i want have 10 different dishes with diferent images.
And i want that they appear in a random sequence.
But i dont have ideia how i cant do that...someone can give me "light"?
I use this variables to load my dish to the stage in my game project.
var load_dish:Loader = new Loader();
var path:URLRequest = new URLRequest("dish.swf");
Here's an example of what you could do to get a random dish each time :
// whenever you need to create a new dish
var dishFilenames:Array = ['dish1.swf', 'dish2.swf', 'dish3.swf']; // etc etc
var randomIndex:int = Math.random() * dishFilenames.length;
var filename:String = dishFilenames[randomIndex];
var load_dish:Loader = new Loader();
var path:URLRequest = new URLRequest(filename);
thank for the answer!
so i cant have all the dishes in the same "swf"?
I need to create swf as many as i need?
My class dish is like this until now:
I dont have other way to do that?(with only a swf that contains all the dishes)
package {
import flash.events.Event;
import flash.display.Loader;
import flash.net.URLRequest;
import flash.events.MouseEvent;
public class Dishe {
var velX: int;
var velY: int ;
var game:Game; //i have also a class game to control everyting
var broken_dish:URLRequest = new URLRequest("broken_dish.swf");
var gravity:int = 2;
var dishFilenames:Array = [ 'dish.swf','second_dish.swf'];
var randomIndex:int = Math.random() * dishFilenames.length;
var filename:String = dishFilenames[randomIndex];
var load_dish:Loader = new Loader();
var path:URLRequest = new URLRequest(filename);
public function Dishe(e:Game, vX:int, vY:int)
{
velX = vX;
velY = vY;
load_dish.x = -180;
load_dish.y = randomBetween(250,-5);
load_dish.load(caminho);
game = e;
game.myStage.addChild(load_dishe);
load_dish.addEventListener(MouseEvent.CLICK, _shoot);
}
public function broken_dish(e:Event)
{
velY += gravity;
load_dish.y +=velY ;
if(load_dish.y >= game.myStage.stageHeight)
{
game.game_states(Game.state_playing);
}
}
public function _enterFrame(e:Event):void
{
if(load_dish.content!=null)
{
load_dish.x += velX;
load_dish.y += velY *(1 - (load_dish.x / game.myStage.stageWidth) * 2 );
if(load_dish.x > game.myStage.stageWidth)
{
load_dish.y = randomBetween(250,-5);
load_dish.x = -180;
}
}
}
public function _shoot(e:MouseEvent):void
{
trace("nice!!");
game.game_states(Game.state_stop);
load_dishe.load(broken_dish);
}
function randomBetween(a:int, b:int) : int {
return a + int(Math.round( (b-a)*Math.random() ));
}
}
}

display random images from folder

What should I use too pick random images from a folder and display it on the sage?
I know I will neeed:
1. Math.random thingy - to roll random numbers
2. XML file - which I don't know how to incorporate int the flash file
3. a folder with pictures, which I already have
Any ideas what else?
1.To placed image, fla as follows.
2.Try the following. below code is just a skeleton. you try more extended.
import flash.display.Bitmap;
import flash.events.Event;
import flash.net.URLRequest;
import flash.display.Loader;
var imgRequest:URLRequest;
var randCount:int = 6*Math.random();
function loadImage():void
{
for(var i:int = 0; i<randCount; i++)
{
var imgLoader:Loader = new Loader();
imgRequest = new URLRequest();
imgRequest.url = "img/img" + int(6*Math.random()) +".jpg";
trace(imgRequest.url);
imgLoader.load(imgRequest);
imgLoader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, unloadedImg);
imgLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadedImg);
}
}
function onLoadedImg(e:Event):void
{
e.currentTarget.removeEventListener(Event.COMPLETE, onLoadedImg);
var bmp:Bitmap = e.currentTarget.content;
bmp.x = Math.random() * stage.stageWidth;
bmp.y = Math.random() * stage.stageHeight;
bmp.width = 200;
bmp.height = 200;
this.addChild(bmp);
}
function unloadedImg(e:IOErrorEvent):void
{
e.currentTarget.removeEventListener(IOErrorEvent.IO_ERROR, unloadedImg);
trace("load Failed:" + e);
}
loadImage();
You need to make an array of the images and then make the math random;
var selected:Array=[];//new array
while (selected.length<4) {
//myArray is the array with your pictures.
var si:int=Math.floor(Math.random()*_myArray.length);
if (selected.indexOf(si)==-1) selected.push(si);
}
trace(_myArray[selected[0]]); // first selected flag
trace(_myArray[selected[3]]); // fourth selected flag

Chess board interface. Cannot remove child. Any suggestions?

Hello
I am in the process of creating a chess board where you can move the pieces. Currently i am working on the rook and the coding is below. I know it is not elegant and probably the most inefficient code out there, but this is day 2 of my actionscript 3.0 life and i am kinda a beginner. Anyway, so the thing is, when you click the piece the code below figures out the possible ways to go. Then green squares appear at those places. You can then press those green squares and then the rook will move there.
Ok, now to the problem. The squares will not go away. I want them all to be deleted when i have clicked on one of them and the rook will move there.
I have tried removeChild(), but since that happens in a different function it does not work. So if you are so kind to look through the code and suggest a solution, your help is much appreciated.
Kind Regards Emile
https://picasaweb.google.com/109156245246626370734/Jun42011?authkey=Gv1sRgCMy4v_b01aikzAE&feat=directlink
import flash.display.Sprite
import flash.events.MouseEvent
import flash.text.TextField;
import flash.geom.Point;
import caurina.transitions.*
myPoint.addEventListener(MouseEvent.MOUSE_DOWN, startMove);
function startMove(evt:MouseEvent) {
var boxNum:int = Math.floor(myPoint.y/100)+1;
for (var i:int = 1; i <boxNum; i++) {
var box:Ball = new Ball();
box.x = myPoint.x;
box.y = myPoint.y - i * box.height;
addChild(box);
Tweener.addTween(box, {alpha:0.5});
box.buttonMode = true;
box.addEventListener(MouseEvent.ROLL_OVER, onOver,
false, 0, true);
box.addEventListener(MouseEvent.ROLL_OUT, onOut,
false, 0, true);
box.addEventListener(MouseEvent.MOUSE_DOWN, onclick);
}
var boxNum1:int = Math.floor((800-myPoint.y)/100)+1;
for (var i:int = 1; i <boxNum1; i++) {
var box1:Ball = new Ball();
box1.x = myPoint.x;
box1.y = myPoint.y + i * box.height;
addChild(box1);
Tweener.addTween(box1, {alpha:0.5});
box1.buttonMode = true;
box1.addEventListener(MouseEvent.ROLL_OVER, onOver,
false, 0, true);
box1.addEventListener(MouseEvent.ROLL_OUT, onOut,
false, 0, true);
box1.addEventListener(MouseEvent.CLICK, onclick);
}
var boxNum2:int = Math.floor(myPoint.x/100)+1;
for (var i:int = 1; i <boxNum2; i++) {
var box2:Ball = new Ball();
box2.x = myPoint.x - i * box.height;
box2.y = myPoint.y;
addChild(box2);
Tweener.addTween(box2, {alpha:0.5});
box2.buttonMode = true;
box2.addEventListener(MouseEvent.ROLL_OVER, onOver,
false, 0, true);
box2.addEventListener(MouseEvent.ROLL_OUT, onOut,
false, 0, true);
box2.addEventListener(MouseEvent.CLICK, onclick);
}
var boxNum3:int = Math.floor((800-myPoint.x)/100)+1;
for (var i:int = 1; i <boxNum3; i++) {
var box3:Ball = new Ball();
box3.x = myPoint.x + i * box.height;
box3.y = myPoint.y;
addChild(box3);
Tweener.addTween(box3, {alpha:0.5});
box3.buttonMode = true;
box3.addEventListener(MouseEvent.ROLL_OVER, onOver, false, 0, true);
box3.addEventListener(MouseEvent.ROLL_OUT, onOut, false, 0, true);
box3.addEventListener(MouseEvent.CLICK, onclick);
}
}
function onOver(evt:Event):void {
var box:MovieClip = MovieClip(evt.target);
addChild(box)
box.scaleX = box.scaleY = 1.1;
}
function onOut(evt:Event):void {
evt.target.scaleX = evt.target.scaleY = 1;
}
function onclick(Event:MouseEvent):void {
var xcod:int = Math.ceil(mouseX/100)*100-50;
var ycod:int = Math.ceil(mouseY/100)*100-50;
Tweener.addTween(myPoint, {x:xcod, y:ycod, time:1, transition:"linear"});
}
alxx's answer is correct, you wouldn't need to keep a special list for them. The other way you could do it, using an Array to save references, would look like this:
var boxes:Array = new Array();
function startMove(evt:MouseEvent):void {
...
var box:Ball = new Ball();
addChild(box);
boxes.push(box);
...
var box1:Ball = new Ball();
addChild(box1);
boxes.push(box1);
...
}
function onClick(evt:MouseEvent):void {
for each (var box:Ball in boxes) {
removeChild(box);
}
boxes = new Array();
}
You can put temporary highlights in separate Sprite. Then your board will looks as follows:
Stage children: base board, highlights, pieces, in that order.
When you need to remove highlights, you can iterate highlights' children with numChildren and getChildAt and call removeChild on each, you don't even need a special list for them.