Detect when the Shader is done mixing the audio - actionscript-3

so this the code with it i am able to mix several tracks
with a Shader done in pixel bender.
the problem here i don't know when the mixing is finish or all the sound reache their end
to be able to save the bytearray into a file any Event or something like that
help plz ?
package
{
import flash.display.*;
import flash.media.*;
import flash.events.*;
import flash.net.*;
import flash.utils.*;
import fl.controls.Slider;
import org.bytearray.micrecorder.encoder.WaveEncoder;
[SWF(width='500', height='380', frameRate='24')]
public class AudioMixer extends Sprite{
[Embed(source = "sound2.mp3")] private var Track1:Class;
[Embed(source = "sound1.mp3")] private var Track2:Class;
[Embed(source = "mix.pbj",mimeType = "application/octet-stream")]
private var EmbedShader:Class;
private var shader:Shader = new Shader(new EmbedShader());
private var sound:Vector.<Sound> = new Vector.<Sound>();
private var bytes:Vector.<ByteArray> = new Vector.<ByteArray>();
private var sliders:Vector.<Slider> = new Vector.<Slider>();
private var graph:Vector.<Shape> = new Vector.<Shape>();
private var recBA:ByteArray = new ByteArray();
private var BUFFER_SIZE:int = 0x800;
public var playback:Sound = new Sound();
public var container:Sprite = new Sprite();
public var isEvent:Boolean = false;
public function AudioMixer():void{
container.y = stage.stageHeight * .5;
addChild(container);
sound.push(new Track1(), new Track2(),new Track2(),new Track2(),new Track2(),new Track2(),new Track2(),new Track2(),new Track2(),new Track2(),new Track2(),new Track2());
for(var i:int = 0; i < sound.length; i++){
var slider:Slider = new Slider();
slider.maximum = 1;
slider.minimum = 0;
slider.snapInterval = 0.025;
slider.value = 0.8;
slider.rotation += -90;
slider.x = i * 40 + 25;
container.addChild(slider);
sliders.push(slider);
var line:Shape = new Shape();
line.graphics.lineStyle(1, 0x888888);
line.graphics.drawRect(i * 40 + 14, 0, 5, -80);
line.graphics.endFill();
container.addChild(line);
var shape:Shape = new Shape();
shape.graphics.beginFill(0x00cc00);
shape.graphics.drawRect(i * 40 + 15, 0, 3, -80);
shape.graphics.endFill();
container.addChild(shape);
graph.push(shape);
}
playback.addEventListener(SampleDataEvent.SAMPLE_DATA, onSoundData);
playback.play();
}
private function onSoundData(event:SampleDataEvent):void {
for(var i:int = 0; i < sound.length; i++){
bytes[i] = new ByteArray();
bytes[i].length = BUFFER_SIZE * 4 * 2;
sound[i].extract(bytes[i], BUFFER_SIZE);
var volume:Number = 0;
bytes[i].position = 0;
for(var j:int = 0; j < BUFFER_SIZE; j++){
volume += Math.abs(bytes[i].readFloat());
volume += Math.abs(bytes[i].readFloat());
}
volume = (volume / (BUFFER_SIZE * .5)) * sliders[i].value;
shader.data['track' + (i + 1)].width = BUFFER_SIZE / 1024;
shader.data['track' + (i + 1)].height = 512;
shader.data['track' + (i + 1)].input = bytes[i];
shader.data['vol' + (i + 1)].value = [sliders[i].value];
graph[i].scaleY = volume;
}
var shaderJob:ShaderJob = new ShaderJob(shader,event.data,BUFFER_SIZE / 1024,512);
shaderJob.start(true);
var shaderJob2:ShaderJob = new ShaderJob(shader,recBA,BUFFER_SIZE / 1024,512);
shaderJob2.start(true);
}
}
}

You can tell when a shader has completed it's job using the ShaderEvent.COMPLETE listener. Like so:
shaderJob.addEventListener(ShaderEvent.COMPLETE, onShaderComplete);
private function onShaderComplete(e:Event):void
{
//Do Something here
}
See this link for more details.
One thing about your code though. You're doing this shader job inside of a sampleDataEvent and I can see this being problematic (possibly) in the sense that your mixing may be out of sync with your playback (that is, if you plan on mixing live and writing the mixed data back into the sound stream). Anyway that's perhaps an issue for a new question. This should solve your problem with needing to know when the mixing is complete.
Note you also need to add "false" to the shaderJob.start(false) function. From the documentation about the ShaderEvent.COMPLETE:
"Dispatched when a ShaderJob that executes asynchronously finishes processing the data using the shader. A ShaderJob instance executes asynchronously when the start() method is called with a false value for the waitForCompletion parameter."
Update
In response to your inquiry about how to only process inside the sampleDataEvent if the sound isnt being processed:
private var isProcessing:Boolean = false;
private function onSoundData(event:SampleDataEvent):void {
if(isProcessing != true){
for(var i:int = 0; i < sound.length; i++){
bytes[i] = new ByteArray();
bytes[i].length = BUFFER_SIZE * 4 * 2;
sound[i].extract(bytes[i], BUFFER_SIZE);
var volume:Number = 0;
bytes[i].position = 0;
for(var j:int = 0; j < BUFFER_SIZE; j++){
volume += Math.abs(bytes[i].readFloat());
volume += Math.abs(bytes[i].readFloat());
}
volume = (volume / (BUFFER_SIZE * .5)) * sliders[i].value;
shader.data['track' + (i + 1)].width = BUFFER_SIZE / 1024;
shader.data['track' + (i + 1)].height = 512;
shader.data['track' + (i + 1)].input = bytes[i];
shader.data['vol' + (i + 1)].value = [sliders[i].value];
graph[i].scaleY = volume;
}
var shaderJob:ShaderJob = new ShaderJob(shader,event.data,BUFFER_SIZE / 1024,512);
shaderJob.start(false);
shaderJob.addEventListener(ShaderEvent.COMPLETE, onShaderComplete);
var shaderJob2:ShaderJob = new ShaderJob(shader,recBA,BUFFER_SIZE / 1024,512);
shaderJob2.start(false);
}
}
private function onShaderComplete(e:ShaderEvent):void
{
//Do something here
isProcessing = false;
}

Related

Type Coercion failed: cannot convert []#13a355b9 to flash.display.DisplayObject

I get this error when running, no compile errors.
package {
import flash.display.*;
import flash.events.*;
import flash.utils.*;
public class mainCode extends MovieClip{
//global variables go here
public var circled:Shape = new Shape();
public var circled2:Shape = new Shape();
public var circled3:Shape = new Shape();
public var angled:int = new int();
public var circlearray1:Array = new Array(4);
public var circlearray2:Array = new Array(4);
public function mainCode(){
makeCircle(circled, 100);
makeCircle(circled2, 50);
makeCircle(circled3, 50);
for(var i:int=0; i<4; i++){circlearray1[i] = new Array(20);}
for(var n:int=0; n<4; n++){circlearray2[n] = new Array(20);}
stage.addEventListener(Event.ENTER_FRAME, mainLoop);
}
//functions go here
private function mainLoop(e:Event){
trace(1);
angled+=1;
angled%=360;
circled.x = stage.stageWidth / 2;
circled.y = stage.stageHeight/ 2;
circled2.x = circled.x + 100*Math.cos(radians(angled));
circled2.y = circled.y + 100*Math.sin(radians(angled));
circled3.x = circled.x + 100*Math.cos(radians((angled + 180) % 360));
circled3.y = circled.y + 100*Math.sin(radians((angled + 180) % 360));
trace(2);
for (var i:int = 0; i < 4; i++)
{
trace(3);
if (circlearray1[i][0] != undefined)
{removeChild(circlearray1[i][0]);}
if (circlearray2[i][0] != undefined)
{removeChild(circlearray2[i][0]);}
trace(4);
for (var m:int = 0; m < 19; m++)
{
circlearray1[i][m] = circlearray1[m+1];
circlearray2[i][m] = circlearray2[m+1];
}
circlearray1[i][19] = new Shape();
circlearray2[i][19] = new Shape();
makeCircle(circlearray1[i][19], 25);
makeCircle(circlearray2[i][19], 25);
circlearray1[i][19].x = circled2.x + 50*Math.cos(radians(((-angled + (i*90)) * 2) % 360));
circlearray1[i][19].y = circled2.y + 50*Math.sin(radians(((-angled + (i*90)) * 2) % 360));
circlearray2[i][19].x = circled3.x + 50*Math.cos(radians(((-angled + (i*90)) * 2) % 360));
circlearray2[i][19].y = circled3.y + 50*Math.sin(radians(((-angled + (i*90)) * 2) % 360));
trace(8);
}
}
private function makeCircle(circles:Shape, radius:int)
{
circles.graphics.clear();
circles.graphics.lineStyle(2,0x000000);
circles.graphics.beginFill(0x990000);
circles.graphics.drawCircle(0,0,radius);
circles.graphics.endFill();
addChild(circles);
}
private function degrees(radians:Number):Number
{
return radians * 180/Math.PI;
}
private function radians(degrees:Number):Number
{
return degrees * Math.PI / 180;
}
private function killCircle(circlei:Shape):void {
removeChild(circlei);
}
}
}
I've traced it down to {removeChild(circlearray1[i][0]);}, which seems to be returning the error. I have no idea why it's doing this, I've tried alternatives such as circlearray1[i][0].parent.removeChild(circlearray1[i][0]); and if (circlearray1[i][0] is Shape) ... , but no dice.
For reference, I'm trying to make some circles circle around other circles, but have the outermost circles leave an "image lag" (or afterimage) while moving. I do this by creating objects and deleting them using for loops and multidimensional arrays, as I dont feel like typing out 50 create object calls manually and edit each one manually.
Probably, this part:
for (var m:int = 0; m < 19; m++)
{
circlearray1[i][m] = circlearray1[m+1];
circlearray2[i][m] = circlearray2[m+1];
}
You assign to endpoint elements, which you initially assume to be Shapes the elements which are Arrays. Then later you go at circlearray1[i][0] assuming it is Shape if it is not empty, but it is already an Array due to the assignment above. It's probably a typo and you meant this:
for (var m:int = 0; m < 19; m++)
{
circlearray1[i][m] = circlearray1[i][m+1];
circlearray2[i][m] = circlearray2[i][m+1];
}
There's actually a more clean way to do that. Array in AS3 is a lot like C# ArrayList, you can insert elements to either end and extract elements from either end without re-indexing elements manually:
trace(3);
// If you are sure the first element is not empty
// you can get its reference removing them from
// the Array at the same time.
removeChild(circlearray1[i].shift());
removeChild(circlearray2[i].shift());
trace(4);
// All Array elements are automatically shifted
// by -1 so you don't need to move each of them manually.
circlearray1[i][19] = new Shape();
circlearray2[i][19] = new Shape();

How to scale GraphicsPathCommand data?

Context: For a legacy Flex/Actionscript drawing app, I need to add scaling of simple symbols. The app uses the Graffiti lib for drawing and the resulting shape data is stored as GraphicsPathCommands serialized to XML using the Degrafa lib for save and reload. I need to enable the user to scale these graphics and then get updated path data which can be serialized. The symbols are simple but more complicated than simple geometry. For example:
Question: I converted the SVG data for this symbol to Actionscript GraphicsPathCommands and am able to draw it, and of course translation is easy – but I don't know how I would scale it, given a bounding box defined by a user dragging out a marquee rectangle in the app.
Does anyone know of either an Actionscript way of transforming the command data, or a Javascript snippet for scaling SVG which I can port to Actionscript?
For reference, an example of the Actionscript GraphicsPathCommands for drawing a star is below.
public function DrawPathExample()
{
var star_commands:Vector.<int> = new Vector.<int>(5, true);
star_commands[0] = GraphicsPathCommand.MOVE_TO;
star_commands[1] = GraphicsPathCommand.LINE_TO;
star_commands[2] = GraphicsPathCommand.LINE_TO;
star_commands[3] = GraphicsPathCommand.LINE_TO;
star_commands[4] = GraphicsPathCommand.LINE_TO;
var star_coord:Vector.<Number> = new Vector.<Number>(10, true);
star_coord[0] = 66; //x
star_coord[1] = 10; //y
star_coord[2] = 23;
star_coord[3] = 127;
star_coord[4] = 122;
star_coord[5] = 50;
star_coord[6] = 10;
star_coord[7] = 49;
star_coord[8] = 109;
star_coord[9] = 127;
graphics.beginFill(0x003366);
graphics.drawPath(star_commands, star_coord);
}
Solution
A full solution for interactively scaling GraphicsPathCommand data is below. The path data was derived from an SVG put through this SVGParser. It generates path drawing commands in the form of graphics.lineTo(28.4,16.8);. A couple of utility functions separate the data from the commands and store them in Vectors so the data can be serialized. I don't need to use arbitrary SWGs so I just hardcoded the data.
package classes
{
import flash.display.GraphicsPathCommand;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.geom.Rectangle;
public class DrawSVG extends Sprite
{
private var startPt:Point = new Point();
private var selectRect:Rectangle = new Rectangle();
private var viewBox:Rectangle = new Rectangle();
protected var commands:Vector.<int> = new Vector.<int>();
protected var drawingData:Vector.<Number> = new Vector.<Number>();
protected var sourceDrawingData:Vector.<Number> = new Vector.<Number>();
public function DrawSVG()
{
super();
this.addEventListener(Event.ADDED_TO_STAGE, setup);
setupWomanData();
}
private function setup(event:Event):void
{
stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
}
private function onMouseDown(event:MouseEvent):void
{
stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
this.graphics.clear();
// offset so graphic draws centered on click point
startPt = new Point(event.stageX - (viewBox.width /2), event.stageY - (viewBox.height /2));
selectRect = new Rectangle(startPt.x, startPt.y, viewBox.width, viewBox.height);
var kx:Number = selectRect.width / (viewBox.width);
var ky:Number = selectRect.height / (viewBox.height);
var scaleFactor:Number = kx < ky ? kx : ky;
drawSymbol(scaleFactor);
this.graphics.lineStyle(1, 0x000000);
this.graphics.drawRect(selectRect.x, selectRect.y, selectRect.width, selectRect.height);
}
private function onMouseMove(event:MouseEvent):void
{
selectRect.width = Math.max(viewBox.width, Math.abs(event.stageX - startPt.x));
selectRect.height = Math.max(viewBox.height, Math.abs(event.stageY - startPt.y));
var kx:Number = selectRect.width / (viewBox.width);
var ky:Number = selectRect.height / (viewBox.height);
var scaleFactor:Number = kx < ky ? kx : ky;
this.graphics.clear();
drawSymbol(scaleFactor);
this.graphics.lineStyle(1, 0x000000);
this.graphics.drawRect(selectRect.x, selectRect.y, viewBox.width * scaleFactor, viewBox.height * scaleFactor);
}
private function onMouseUp(event:MouseEvent):void
{
stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
this.graphics.clear();
createSprite(commands, drawingData);
}
private function drawSymbol(toScale:Number):void
{
drawingData.length = 0;
for (var i:int = 0; i < sourceDrawingData.length; i++) {
drawingData[i] = Math.max(sourceDrawingData[i], sourceDrawingData[i] * toScale);
drawingData[i] += i % 2 == 0 ? startPt.x : startPt.y ;
}
this.graphics.clear();
this.graphics.lineStyle();
this.graphics.beginFill(0xff0000);
this.graphics.drawPath(commands, drawingData);
this.graphics.endFill();
}
private function createSprite(command:Vector.<int>, coord:Vector.<Number>):Shape{
var s:Shape = new Shape();
addChild(s);
s.graphics.beginFill(0xff);
s.graphics.drawPath(command, coord);
s.graphics.endFill();
return s;
}
private function setupWomanData():void
{
commands = new Vector.<int>();
drawingData = new Vector.<Number>();
viewBox= new Rectangle(0, 0, 24.629, 52.336);
addMoveToCmd(12.31,10.3);
addCurveToCmd(13.37,10.3,14.3,9.89);
addCurveToCmd(15.24,9.48,15.94,8.78);
addCurveToCmd(16.64,8.08,17.05,7.14);
addCurveToCmd(17.46,6.2,17.46,5.15);
addCurveToCmd(17.46,4.1,17.05,3.16);
addCurveToCmd(16.64,2.23,15.94,1.52);
addCurveToCmd(15.24,0.82,14.3,0.41);
addCurveToCmd(13.37,0,12.31,0);
addCurveToCmd(11.26,0,10.33,0.41);
addCurveToCmd(9.39,0.82,8.69,1.52);
addCurveToCmd(7.98,2.23,7.57,3.16);
addCurveToCmd(7.16,4.1,7.16,5.15);
addCurveToCmd(7.16,6.2,7.57,7.14);
addCurveToCmd(7.98,8.08,8.69,8.78);
addCurveToCmd(9.39,9.48,10.33,9.89);
addCurveToCmd(11.26,10.3,12.31,10.3);
addLineToCmd(12.314,10.304);
addMoveToCmd(24.6,26.36);
addLineToCmd(20.7,12.77);
addCurveToCmd(20.62,12.3,20.39,11.91);
addCurveToCmd(20.15,11.51,19.81,11.23);
addCurveToCmd(19.47,10.94,19.04,10.78);
addCurveToCmd(18.61,10.62,18.14,10.62);
addLineToCmd(6.49,10.62);
addCurveToCmd(6.02,10.62,5.59,10.78);
addCurveToCmd(5.16,10.94,4.82,11.23);
addCurveToCmd(4.48,11.51,4.24,11.91);
addCurveToCmd(4.01,12.3,3.93,12.77);
addLineToCmd(0.03,26.36);
addCurveToCmd(0.01,26.4,0.01,26.45);
addCurveToCmd(-0.01,26.5,-0.01,26.55);
addCurveToCmd(0.01,26.6,0.01,26.65);
addCurveToCmd(0.02,26.69,0.03,26.74);
addCurveToCmd(-0.15,27.95,0.55,28.69);
addCurveToCmd(1.25,29.44,2.2,29.6);
addCurveToCmd(3.15,29.77,4.05,29.3);
addCurveToCmd(4.95,28.84,5.17,27.63);
addLineToCmd(6.85,21.37);
addLineToCmd(4.07,34.88);
addCurveToCmd(3.81,35.51,3.91,36.15);
addCurveToCmd(4,36.78,4.35,37.3);
addCurveToCmd(4.7,37.81,5.26,38.13);
addCurveToCmd(5.81,38.45,6.49,38.45);
addLineToCmd(6.78,38.45);
addLineToCmd(6.78,49.72);
addCurveToCmd(6.78,50.99,7.59,51.62);
addCurveToCmd(8.41,52.25,9.39,52.25);
addCurveToCmd(10.37,52.25,11.19,51.62);
addCurveToCmd(12,50.99,12,49.72);
addLineToCmd(12,38.45);
addLineToCmd(12.63,38.45);
addLineToCmd(12.63,49.72);
addCurveToCmd(12.63,50.99,13.44,51.62);
addCurveToCmd(14.26,52.25,15.24,52.25);
addCurveToCmd(16.22,52.25,17.04,51.62);
addCurveToCmd(17.85,50.99,17.85,49.72);
addLineToCmd(17.85,38.45);
addLineToCmd(18.14,38.45);
addCurveToCmd(18.82,38.45,19.38,38.13);
addCurveToCmd(19.93,37.81,20.28,37.3);
addCurveToCmd(20.63,36.78,20.72,36.14);
addCurveToCmd(20.81,35.51,20.56,34.87);
addLineToCmd(17.78,21.37);
addLineToCmd(19.45,27.58);
addCurveToCmd(19.67,28.79,20.57,29.27);
addCurveToCmd(21.47,29.75,22.43,29.6);
addCurveToCmd(23.38,29.45,24.08,28.7);
addCurveToCmd(24.78,27.96,24.6,26.74);
addCurveToCmd(24.61,26.69,24.62,26.65);
addCurveToCmd(24.63,26.6,24.63,26.55);
addCurveToCmd(24.63,26.5,24.62,26.45);
addCurveToCmd(24.62,26.4,24.6,26.36);
addLineToCmd(24.601,26.356);
}
protected function addCurveToCmd(p1:Number, p2:Number, p3:Number, p4:Number):void
{
commands.push(GraphicsPathCommand.CURVE_TO);
sourceDrawingData.push(p1);
sourceDrawingData.push(p2);
sourceDrawingData.push(p3);
sourceDrawingData.push(p4);
}
protected function addMoveToCmd(p1:Number, p2:Number):void
{
commands.push(GraphicsPathCommand.MOVE_TO);
sourceDrawingData.push(p1);
sourceDrawingData.push(p2);
}
protected function addLineToCmd(p1:Number, p2:Number):void
{
commands.push(GraphicsPathCommand.LINE_TO);
sourceDrawingData.push(p1);
sourceDrawingData.push(p2);
}
}
}
Seems like there is a pretty straightforward way to do this. It looks like the only thing to scale are the coordinates themselves, so you may just apply a scale factor.
Based on your example:
public function ASEntryPoint() {
var star_commands:Vector.<int> = new Vector.<int>(5, true);
star_commands[0] = GraphicsPathCommand.MOVE_TO;
star_commands[1] = GraphicsPathCommand.LINE_TO;
star_commands[2] = GraphicsPathCommand.LINE_TO;
star_commands[3] = GraphicsPathCommand.LINE_TO;
star_commands[4] = GraphicsPathCommand.LINE_TO;
var star_coord:Vector.<Number> = new Vector.<Number>(10, true);
star_coord[0] = 66; //x
star_coord[1] = 10; //y
star_coord[2] = 23;
star_coord[3] = 127;
star_coord[4] = 122;
star_coord[5] = 50;
star_coord[6] = 10;
star_coord[7] = 49;
star_coord[8] = 109;
star_coord[9] = 127;
//reference shape to detect initial size
var s:Shape = shapeInRect(star_commands, star_coord);
var bounds:Rectangle = s.getBounds(s);
s.graphics.lineStyle(1);
s.graphics.drawRect(bounds.x, bounds.y, bounds.width, bounds.height);
addChild(s);
//fit to target
var targetSize:Rectangle = new Rectangle(150, 100, 75, 60);
//detect lesser factor - assuming you need to preserve proportions
var kx:Number = targetSize.width / (bounds.width);
var ky:Number = targetSize.height / (bounds.height);
var toUse:Number = kx < ky ? kx : ky;
//apply to coords
for (var i:int = 0; i < star_coord.length; i++) {
//size
star_coord[i] *= toUse;
//fix initial offset
star_coord[i] -= i % 2 == 0 ? bounds.x * toUse : bounds.y * toUse;
}
//draw
addChild(shapeInRect(star_commands, star_coord, targetSize));
}
private function shapeInRect(command:Vector.<int>, coord:Vector.<Number>, rect:Rectangle = null):Shape{
var s:Shape = new Shape();
addChild(s);
s.graphics.beginFill(0x003366);
s.graphics.drawPath(command, coord);
s.graphics.endFill();
if (rect){
s.graphics.lineStyle(1);
s.graphics.drawRect(0, 0, rect.width, rect.height);
s.x = rect.x;
s.y = rect.y;
}
return s;
}

Actionscript Double Buffer Audio playback by upscaling

I've been working on a way to stream mic data to a server, cycle back to clients, and play back in a packet by packet manner. So far, I have the client connectivity, intercommunication, voice sending, voice receiving, buffer storage, and a broken playback. The voice coming back plays at the proper speed without scratchy noise, but it's only ever playing a % of the voice buffer, recycling, and playing the new first %. I need the client to only play sound data it retreives once (asside from resampling for proper audio speeds) and then never again.
package Voip
{
import flash.events.SampleDataEvent;
import flash.events.TimerEvent;
import flash.media.Sound;
import flash.system.System;
import flash.utils.ByteArray;
import flash.utils.Timer;
public class SoundObj
{
private var ID:int;
public var sound:Sound;
public var buf:ByteArray;
public var _vbuf:ByteArray;
public var _numSamples:int;
public var _phase:Number = 0;
public var killtimer:Timer = null;
public var _delaytimer:Timer = new Timer(1000, 1);
public function SoundObj(id:int)
{
ID = id;
buf = new ByteArray();
_vbuf = new ByteArray();
sound = new Sound();
sound.addEventListener(SampleDataEvent.SAMPLE_DATA, SoundBuffer, false, 0, true);
sound.play();
}
public function receive(bytes:ByteArray):void {
var i:int = _vbuf.position;
_vbuf.position = _vbuf.length;
_vbuf.writeBytes(bytes);
_vbuf.position = i;
_numSamples = _vbuf.length/4;
/*var i:int = buf.position;
buf.position = buf.length; // write to end
buf.writeBytes(bytes);
buf.position = i; // return to origin
if (_delaytimer == null) {
_delaytimer = new Timer(1000, 1);
_delaytimer.addEventListener(TimerEvent.TIMER, finaldata);
_delaytimer.start();
}
if (!_delaytimer.running) {
// timer not running, dump buffer and reset.
//var index:int = _vbuf.position;
//_vbuf.position = _vbuf.length;
//_vbuf.writeBytes(buf);
_vbuf = buf;
_vbuf.position = 0;
buf = new ByteArray();
//_vbuf.position = index;
//sound.extract(_vbuf, int(_vbuf.length * 44.1));
_phase = 0;
_numSamples = _vbuf.length/4;
// reset killtimer to silence timeout
killtimer = new Timer(1000, 1);
killtimer.addEventListener(TimerEvent.TIMER, killtimerEvent);
killtimer.start();
}*/
}
public function killtimerEvent(event:TimerEvent):void {
_delaytimer = null;
}
// send remaining data
public function finaldata(event:TimerEvent):void {
if (buf.length > 0) {
trace("adding final content");
//var _buf:ByteArray = new ByteArray();
//var index:int = int(_phase)*4;
//if (index >= _vbuf.length)
// index = _vbuf.position;
/*_buf.writeBytes(_vbuf, index, _vbuf.length-index);
_buf.writeBytes(buf);
buf = new ByteArray();*/
//_vbuf = _buf;
// add remaining buffer to playback
var index:int = _vbuf.position;
_vbuf.position = _vbuf.length;
_vbuf.writeBytes(buf);
_vbuf.position = index;
// wipe buffer
buf = new ByteArray();
//sound.extract(_vbuf, int(_vbuf.length * 44.1));
_phase = 0;
//_numSamples = _vbuf.length/4;
_numSamples = _vbuf.length/4;
// reset killtimer to silence timeout
killtimer = new Timer(1000, 1);
killtimer.addEventListener(TimerEvent.TIMER, killtimerEvent);
killtimer.start();
}
}
public function SoundBuffer(event:SampleDataEvent):void {
//try {
//trace("[SoundBuffer:"+ID+"]");
//sound.removeEventListener(SampleDataEvent.SAMPLE_DATA, SoundBuffer);
// buffer 4KB of data
for (var i:int = 0; i < 4096; i++)
{
var l:Number = 0;
var r:Number = 0;
if (_vbuf.length > int(_phase)*4) {
_vbuf.position = int(_phase)*4;
l = _vbuf.readFloat();
if (_vbuf.position < _vbuf.length)
r = _vbuf.readFloat();
else
r = l;
}
//if (_vbuf.position == _vbuf.length)
//_vbuf = new ByteArray();
event.data.writeFloat(l);
event.data.writeFloat(r);
_phase += (16/44.1);
if (_phase >= _numSamples) {
_phase -= _numSamples;
}
}
System.gc();
}
}
}
The initial idea was to create a SoundObj in my scene, use obj.receive(bytes) to add data to the buffer to be played back the next time the Sound player needed new data. I've been fiddling around trying to get it to work in one way or another since. The timers were designed to determine when to buffer more data, but never really worked as desired.
Proper double buffer, proper playback.
package VoipOnline
{
import flash.events.SampleDataEvent;
import flash.events.TimerEvent;
import flash.media.Sound;
import flash.system.System;
import flash.utils.ByteArray;
import flash.utils.Timer;
import flashx.textLayout.formats.Float;
public class SoundObj
{
public var ID:int;
public var sound:Sound;
internal var _readBuf:ByteArray;
internal var _writeBuf:ByteArray;
internal var n:Number;
internal var _phase:Number;
internal var _numSamples:int;
internal var myTimer:Timer;
internal var bytes:int;
public function SoundObj(id:int)
{
ID = id;
_readBuf = new ByteArray();
_writeBuf = new ByteArray();
bytes = 0;
myTimer = new Timer(10000, 0);
myTimer.addEventListener(TimerEvent.TIMER, timerHandler);
myTimer.start();
sound = new Sound();
sound.addEventListener(SampleDataEvent.SAMPLE_DATA, SoundBuffer);
sound.play();
}
public function receive(bytes:ByteArray):void
{
var i:int = _writeBuf.position;
_writeBuf.position = _writeBuf.length;
_writeBuf.writeBytes(bytes);
_writeBuf.position = i;
this.bytes += bytes.length;
}
private function timerHandler(e:TimerEvent):void{
trace((bytes/10) + " bytes per second.");
bytes = 0;
}
public function SoundBuffer(event:SampleDataEvent):void
{
//trace((_readBuf.length/8)+" in buffer, and "+(_writeBuf.length/8)+" waiting.");
for (var i:int = 0; i < 4096; i++)
{
var l:Number = 0; // silence
var r:Number = 0; // silence
if (_readBuf.length > int(_phase)*8) {
_readBuf.position = int(_phase)*8;
l = _readBuf.readFloat();
if (_readBuf.position < _readBuf.length)
r = _readBuf.readFloat();
else {
r = l;
Buffer();
}
} else {
Buffer();
}
event.data.writeFloat(l);
event.data.writeFloat(r);
_phase += 0.181;
}
}
private function Buffer():void {
// needs new data
// snip 4096 bytes
var buf:ByteArray = new ByteArray();
var len:int = (_writeBuf.length >= 4096 ? 4096 : _writeBuf.length);
buf.writeBytes(_writeBuf, 0, len);
// remove snippet
var tmp:ByteArray = new ByteArray();
tmp.writeBytes(_writeBuf, len, _writeBuf.length-len);
_writeBuf = tmp;
// plug in snippet
_readBuf = buf;
_writeBuf = new ByteArray();
_readBuf.position = 0;
_phase = 0;
}
}
}
These code snippets are based on this mic setup:
mic = Microphone.getMicrophone();
mic.addEventListener(SampleDataEvent.SAMPLE_DATA, this.micParse); // raw mic data stream handler
mic.codec = SoundCodec.SPEEX;
mic.setUseEchoSuppression(true);
mic.gain = 100;
mic.rate = 44;
mic.setSilenceLevel(voicelimit.value, 1);
After considerable testing, this seems to provide the best results so far. Little grainy, but it IS compressed and filterred. Some of the issues I'm having seem to be the fault of the server. I'm only receiving ~30% of the bytes I'm sending out. That being said, the code above works. You simply adjust the _phase increment to modify speed. (0.181 == 16/44/2) Credit will go where credit is due, even if his sample didn't quite solve the issues at hand, it was still a considerable step forward.
I have prepared some sample data and fed it into your example and got only noise sound. I have simplified your class to only two buffers one for receiving samples and second one for providing them. Hope this will work:
package {
import flash.events.*;
import flash.media.*;
import flash.utils.*;
public class SoundObj
{
private var ID:int;
public var sound:Sound;
public var _readBuf:ByteArray;
public var _writeBuf:ByteArray;
public function SoundObj(id:int)
{
ID = id;
_readBuf = new ByteArray();
_writeBuf = new ByteArray();
sound = new Sound();
sound.addEventListener(SampleDataEvent.SAMPLE_DATA, SoundBuffer);
sound.play();
}
public function receive(bytes:ByteArray):void
{
var i:int = _writeBuf.position;
_writeBuf.position = _writeBuf.length;
_writeBuf.writeBytes(bytes);
_writeBuf.position = i;
sound.play();
}
public function SoundBuffer(event:SampleDataEvent):void
{
for (var i:int = 0; i < 8192; i++)
{
if (_readBuf.position < _readBuf.length)
event.data.writeFloat(_readBuf.readFloat());
else
{
if (_writeBuf.length >= 81920)
{
_readBuf = _writeBuf;
_writeBuf = new ByteArray();
}
if (_readBuf.position < _readBuf.length)
event.data.writeFloat(_readBuf.readFloat());
else
{
//event.data.writeFloat( 0 );
}
}
}
}
}
}
// microphone sample parsing with rate change
function micParse(event:SampleDataEvent):void
{
var soundBytes:ByteArray = new ByteArray();
var i:uint = 0;
var n:Number = event.data.bytesAvailable * 44 / mic.rate * 2; // *2 for stereo
var f:Number = 0;
while(event.data.bytesAvailable)
{
i++;
var sample:Number = event.data.readFloat();
for (; f <= i; f+= mic.rate / 2 / 44)
{
soundBytes.writeFloat(sample);
}
}
snd.receive(soundBytes);
}

error while converting AS2 starfield code to AS3

I've tried to convert a nice AS2 script for starfirld effect to AS3 But i'm still getting strange errors
would really appreciate if any one could help me understand what am i doing wrong
here is the original AS2 code:
var stars = 100;
var maxSpeed = 16;
var minSpeed = 2;
var i = 0;
while (i < stars)
{
var mc = this.attachMovie("star", "star" + i, i);
mc._x = random(Stage.width);
mc._y = random(Stage.height);
mc.speed = random(maxSpeed - minSpeed) + minSpeed;
var size = random(2) + 6.000000E-001 * random(4);
mc._width = size;
mc._height = size;
++i;
} // end while
this.onEnterFrame = function ()
{
for (var _loc3 = 0; _loc3 < stars; ++_loc3)
{
var _loc2 = this["star" + _loc3];
if (_loc2._y > 0)
{
_loc2._y = _loc2._y - _loc2.speed;
continue;
} // end if
_loc2._y = Stage.height;
_loc2.speed = random(maxSpeed - minSpeed) + minSpeed;
_loc2._x = random(Stage.width);
} // end of for
};
and here is my AS3 version:
import flash.events.Event;
import flash.events.MouseEvent;
function starField():void
{
var stars:int = 100;
var maxSpeed:int = 16;
var minSpeed:int = 2;
var i:int = 0;
while (i < stars)
{
var mc = new Star();
addChild(mc)
mc._x = Math.random()(stage.stageWidth);
mc._y = Math.random()(stage.stageHeight);
mc.speed = Math.random()(maxSpeed - minSpeed) + minSpeed;
var size = Math.random()(2) + 6.000000E-001 * Math.random()(4);
mc._width = size;
mc._height = size;
++i;
} // end while
}
addEventListener(Event.ENTER_FRAME, update);
function update(_e:Event):void
{
for (var _loc3 = 0; _loc3 < 100; ++_loc3)
{
var _loc2 = this["star" + _loc3];
if (_loc2._y > 0)
{
_loc2._y = _loc2._y - _loc2.speed;
continue;
} // end if
_loc2._y = stage.stageHeight;
_loc2.speed = Math.random()(maxSpeed - minSpeed) + minSpeed;
_loc2._x = Math.random()(stage.stageWidth);
} // end of for
};
the error message I'm getting is: "TypeError: Error #1010: A term is undefined and has no properties. at _fla::MainTimeline/update()"
I understand it has a problem with the 'update' function but I'm net sure which term it refer to?
I'll bet a can of juice here is your problem:
var _loc2 = this["star" + _loc3];
put these into an associative array and access them from there.
#Discipol is right.
Just wanted to add a few more notes:
You can also use the display list to get the movie clip by name:
var _loc2:MovieClip = MovieClip(getChildByName("star" + _loc3));
You've got untyped variables and your are relying on MovieClip as a dynamic class to add properties (such as speed) at runtime. For a really simple project the impact is barely noticeable, but on the long run, for bigger projects, it's worth extending Sprite if you don't use the timeline and add the properties you need:
package {
import flash.display.Sprite;
import flash.events.Event;
public class Star extends Sprite {
private var speed:Number;
private var minSpeed:Number;
private var maxSpeed:Number;
public function Star(min:Number,max:Number) {
minSpeed = min;
maxSpeed = max;
var size = (Math.random()*2) + 1.82211880039 * (Math.random()*4);
width = size;
height = size;
this.addEventListener(Event.ADDED_TO_STAGE,reset);
}
private function reset(e:Event = null):void{
speed = (Math.random() * (maxSpeed-minSpeed)) + minSpeed;
x = Math.random() * stage.stageWidth;
if(e != null) y = Math.random() * stage.stageHeight;//initialized from added to stage event
else y = stage.stageHeight;//otherwise reset while updating
}
public function update():void{
if (y > 0) y -= speed;
else reset();
}
}
}
and the rest of the code would be as simple as:
var stars:int = 100;
var starClips:Vector.<Star> = new Vector.<Star>(stars,true);//a typed fixed vector is faster than a dynamically resizable untyped Array
for(var i:int = 0 ; i < stars; i++) starClips[i] = addChild(new Star(16,2)) as Star;
this.addEventListener(Event.ENTER_FRAME,updateStars);
function updateStars(e:Event):void{
for(var i:int = 0 ; i < stars; i++) starClips[i].update();
}

Action Script 3. How to count game time?

I'm creating Flash "memory" game, Idea to discover 2 equal cards. I need to add "Timer" in top of window which count in how many seconds all cards will be descovered.
Here is my code:
package
{
import flash.display.MovieClip;
import Card;
import Boarder;
import BlueBoard;
import flash.events.MouseEvent;
import RedBoard;
import Snow;
public class MemoryGame extends MovieClip
{
private var _card:Card;
private var _boarder:Boarder;
private var _blueBoard:BlueBoard;
private var _cardX:Number;
private var _cardY:Number;
private var _firstCard:*;
private var _totalMatches:Number;
private var _currentMatches:Number;
private var _redBoard:RedBoard;
private var _snow:Snow;
private var _cards:Array;
public var _message:String;
public function MemoryGame()
{
_cards = new Array();
_totalMatches = 4;
_currentMatches = 0;
createCards();
}
private function createCards():void
{
_cardX = 45;
_cardY = 10;
for(var i:Number = 0; i < 2; i++)
{
_card = new Card();
addChild(_card);
_boarder = new Boarder();
_card.setType(_boarder);
_card.x = _cardX;
_card.y = _cardY;
_cardX += _card.width + 50;
_card.addEventListener(MouseEvent.CLICK, checkCards);
_cards.push(_card);
}
for(var j:Number = 0; j < 2; j++)
{
_card = new Card();
addChild(_card);
_blueBoard = new BlueBoard();
_card.setType(_blueBoard);
_card.x = _cardX;
_card.y = _cardY;
_cardX += _card.width + 50;
_card.addEventListener(MouseEvent.CLICK, checkCards);
_cards.push(_card);
}
_cardX = 45;
_cardY = _card.height + 30;
for(var k:Number = 0; k < 2; k++)
{
_card = new Card();
addChild(_card);
_redBoard = new RedBoard();
_card.setType(_redBoard);
_card.x = _cardX;
_card.y = _cardY;
_cardX += _card.width + 50;
_card.addEventListener(MouseEvent.CLICK, checkCards);
_cards.push(_card);
}
for(var l:Number = 0; l < 2; l++)
{
_card = new Card();
addChild(_card);
_snow = new Snow();
_card.setType(_snow);
_card.x = _cardX;
_card.y = _cardY;
_cardX += _card.width + 50;
_card.addEventListener(MouseEvent.CLICK, checkCards);
_cards.push(_card);
}
randomizeCards(_cards);
}
private function checkCards(event:MouseEvent):void
{
event.currentTarget.removeEventListener(MouseEvent.CLICK, checkCards);
if(_firstCard == undefined)
{
_firstCard = event.currentTarget;
}
else if(String(_firstCard._type) == String(event.currentTarget._type))
{
trace("match");
_message = "match";
message_txt.text = _message;
_firstCard = undefined;
_currentMatches ++;
if(_currentMatches >= _totalMatches)
{
trace("YOU WIN !!!");
_message = "YOU WIN !!!";
message_txt.text = _message;
}
}
else
{
trace("wrong");
_message = "wrong";
message_txt.text = _message;
_firstCard.gotoAndPlay("flipBack");
event.currentTarget.gotoAndPlay("flipBack");
_firstCard.addEventListener(MouseEvent.CLICK, checkCards);
event.currentTarget.addEventListener(MouseEvent.CLICK, checkCards);
_firstCard = undefined;
}
}
private function randomizeCards(cards:Array):void
{
var randomCard1:Number;
var randomCard2:Number;
var card1X:Number;
var card1Y:Number;
for(var i:Number = 0; i < 10; i++)
{
randomCard1 = Math.floor(Math.random() * cards.length);
randomCard2 = Math.floor(Math.random() * cards.length);
card1X = cards[randomCard1].x;
card1Y = cards[randomCard1].y;
cards[randomCard1].x = cards[randomCard2].x;
cards[randomCard1].y = cards[randomCard2].y
cards[randomCard2].x = card1X;
cards[randomCard2].y = card1Y;
}
}
}
}
EDIT:
And I have one more question. When I will add this game to PHP, how can I add username and his time to database? I need write code in Action Script (swf file) or I can do it in php later? I mean in php can I use any method to get time from swf file and write it to database?
Could you help me? Thank you very much.
Here is an example of how to use the Timer class (flash.utils.Timer) to accomplished what you're asking:
var timer:Timer; //import flash.utils.Timer;
var txtTime:TextField;
var tmpTime:Number; //this will store the time when the game is started
//your constructor:
public function MemoryGame()
{
timer = new Timer(1000); //create a new timer that ticks every second.
timer.addEventListener(TimerEvent.TIMER, tick, false, 0, true); //listen for the timer tick
txtTime = new TextField();
addChild(txtTime);
tmpTime = flash.utils.getTimer();
timer.start(); //start the timer
//....the rest of your code
}
private function tick(e:Event):void {
txtTime.text = showTimePassed(flash.utils.getTimer() - tmpTime);
}
//this function will format your time like a stopwatch
function showTimePassed(startTime:int):String {
var leadingZeroMS:String = ""; //how many leading 0's to put in front of the miliseconds
var leadingZeroS:String = ""; //how many leading 0's to put in front of the seconds
var time = getTimer() - startTime; //this gets the amount of miliseconds elapsed
var miliseconds = (time % 1000); // modulus (%) gives you the remainder after dividing,
if (miliseconds < 10) { //if less than two digits, add a leading 0
leadingZeroMS = "0";
}
var seconds = Math.floor((time / 1000) % 60); //this gets the amount of seconds
if (seconds < 10) { //if seconds are less than two digits, add the leading zero
leadingZeroS = "0";
}
var minutes = Math.floor( (time / (60 * 1000) ) ); //60 seconds times 1000 miliseocnds gets the minutes
return minutes + ":" + leadingZeroS + seconds + "." + leadingZeroMS + miliseconds;
}
//in your you-win block of code:
var score = flash.utils.getTimer() - tmpTime; //this store how many milliseconds it took them to complete the game.
This creates a timer that will tick every second, and update a text field with the current amount of seconds elapsed
As per the second part of your request, here is a way you can do this (the database work needs to be done in PHP, this shows you how to send the data to a php page)
var urlLoader:URLLoader = new URLLoader();
urlLoader.addEventListener(Event.COMPLETE, scoreSaveResponse,false,0,true);
var request:URLRequest = new URLRequest("http://mysite.com/score.php");
var urlVars:URLVariables = new URLVariables();
urlVars.time = flash.utils.getTimer() - tmpTime;
urlVars.userName = "yourUserName";
//add any other parameters you want to pass to your PHP page
request.method = URLRequestMethod.POST;
urlLoader.data = urlVars;
urlLoader.load(request);
function scoreSaveResponse(e:Event):void {
//whatever you return from the php page is found in urlLoader.data
}