AS3 playback of sound byte array doesn't begin at the start - actionscript-3

I'm currently recording and storing a ByteArray of sound and then playing it back. But for some reason the playback starting position of the ByteArray is 163840, not 0 as I need it to be.
Would anyone have any ideas why this might be happening?
Thanks,
Mark
var soundBA:ByteArray = new ByteArray();
var sound:Sound = new Sound();
var ch:SoundChannel = new SoundChannel();
var recordingsArray:Array = new Array();
//imagine I have successfully recorded and stored the sound into recordingsArray
soundBA.clear();
soundBA.length = 0;
//I collect the recorded byteArray within an array
soundBA.writeBytes(recordingsArray[0]);
soundBA.position = 0;
trace("Start POS "+soundBA.position); //traces 0
sound.addEventListener(SampleDataEvent.SAMPLE_DATA, sound_sampleDataHandler, false, 0, true);
ch=sound.play();
this.addEventListener(Event.ENTER_FRAME, updateSeek, false, 0, true);
public function updateSeek(event:Event):void {
trace("current Pos "+soundBA.position); //the first trace event is "current Pos 163840"
}
function sound_sampleDataHandler(event:SampleDataEvent):void {
for (var i:int = 0; i < 8192; i++)
{
if (soundBA.bytesAvailable < 4)
{
break;
}
var sample:Number = soundBA.readFloat();
event.data.writeFloat(sample);
event.data.writeFloat(sample);
}
}

This is because soundBA.position is position in byte array, not position of playback. It runs ahead of playback position because of sound lag. To determine current playback position use SoundChannel.position:
public function updateSeek(event:Event):void {
trace("current pos in ms: " + ch.position);
trace("current pos in bytes: " + (ch.position * 44.1 * 4 * 2));
trace("current pos in %: " + (100 * ch.position / sound.length));
}
UPD: I was referring to the case when sound is decoded using additional Sound object, e.g.:
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.SampleDataEvent;
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.net.URLRequest;
import flash.utils.ByteArray;
public class SoundTest extends Sprite
{
private var soundSrc:Sound;
private var soundPlayer:Sound;
private var soundData:ByteArray;
private var soundChannel:SoundChannel;
public function SoundTest()
{
soundSrc = new Sound();
soundSrc.addEventListener(Event.COMPLETE, startPlayback);
soundSrc.load(new URLRequest("sound.mp3"));
}
private function startPlayback(e:Event = null):void
{
soundData = new ByteArray();
soundSrc.extract(soundData, soundSrc.length * 44.1, 0);
soundData.position = 0;
soundPlayer = new Sound();
soundPlayer.addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleData);
soundChannel = soundPlayer.play();
addEventListener(Event.ENTER_FRAME, updateTime);
}
private function onSampleData(e:SampleDataEvent):void
{
for (var i:int = 0; i < 8192; i++)
{
if (soundData.bytesAvailable < 4)
{
break;
}
var sampleL:Number = soundData.readFloat();
var sampleR:Number = soundData.readFloat();
e.data.writeFloat(sampleL);
e.data.writeFloat(sampleR);
}
}
private function updateTime(e:Event):void
{
trace("current pos in ms: " + soundChannel.position);
trace("current pos in bytes: " + (soundChannel.position * 44.1 * 4 * 2));
trace("current pos in % (method 1): " + (100 * soundChannel.position / soundSrc.length));
// it also works
trace("current pos in % (method 2): " + (100 * soundChannel.position / (soundData.length / (44.1 * 4 * 2))));
}
}
}

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();

No looping of generated audio stored in ByteArray and loaded into Sound object?

EDIT 2: This problem still exists, but seems to be a bug. Adobe Sound class does not send the Sound.length value after loading a ByteArray. Here is the bug report I filed (please vote for it!):
https://bugbase.adobe.com/index.cfm?event=bug&id=3749649
= = = = =
The following code works to produce a sound once -- it plays the correct sound, but does not loop. I believe it should. I cannot seem to debug it
It also does not seem to throw a SOUND_COMPLETE event. Am I missing something here?
EDIT: Still broken, but I updated the code below so you can test it. Just copy to a class and call testSound():
private var NUM_SAMPLES:int = 16384 * 2;
private var soundByteArray:ByteArray;
private var volume:Number = 1;
private var channel:SoundChannel = new SoundChannel();
private var RATE:int = 44100;
public function testSound():void
{
var baseSound:Sound = new Sound();
storeAudio();
var trans:SoundTransform = new SoundTransform(volume, 0);
SoundMixer.soundTransform = trans;
soundByteArray.position = 0;
baseSound.loadPCMFromByteArray(soundByteArray, NUM_SAMPLES, "float", true, RATE);
soundByteArray.position = 0;
baseSound.addEventListener(flash.events.Event.SOUND_COMPLETE, onPlaybackComplete);
trace("loaded 1: " + baseSound.length);
trace("loaded 2: " + baseSound.bytesLoaded);
trace("loaded 3: " + baseSound.bytesTotal);
channel = baseSound.play(0, 20, trans);
channel.addEventListener(flash.events.Event.SOUND_COMPLETE, onPlaybackComplete);
}
protected function onPlaybackComplete(event:flash.events.Event):void
{
trace("onPlaybackComplete" + channel.position);
}
private function storeAudio():void
{
soundByteArray = new ByteArray();
for (var i:Number = 0; i < NUM_SAMPLES; i++)
{
soundByteArray.writeFloat(
Math.sin(
((i / RATE))
* Math.PI * 2 * 440
)
);
soundByteArray.writeFloat(
Math.sin(
((i / RATE))
* Math.PI * 2 * 440
)
);
}
trace("storeAudio i = " + i + ", " + soundByteArray.length);
}
OK I appreciate you've accepted this issue as a bug and possibly moved on. However I used a mini-hack to replay the loadPCMFromByteArray. Rather than rely on Sound.Complete just write code that knows when the PCM audio bytes length is fully reached.
By converting bytes length to milliseconds and using channel.position you will get essentially the same result as the .Complete feature (anyone can correct me if I'm wrong here). If you really need an event firing, i.e for the sake of some function that relies on that feedback, then you can simply despatch your own custom event and listen out for that one instead of Sound.Complete
From testing I reason that the continous glitch/click sound is actually Flash trying to play the sound forward but not moving very far into the PCM data's final bytes.
Think of it as a very audible version of an E-O-F error found in ByteArrays but then also running from an internal (never ending?) while loop just to pleasure your ears.
Some notes before code:
At measured sound ending I tried.. soundByteArray.position = 0; Not good! channel.position trace shows as stuck on the Ending pos amount. Also tried
channel = baseSound.play(0); Not good! channel.position trace shows as stuck at the zero pos. Both gave stuttering sound
Also whilst I didnt try it this time, I have looped sampleData before without glitches so I'm sure it could be worth considering copying the PCM over to a sampleData setup also and see if that works better with looping & firing a Sound.Complete etc.
If it's just simple tones you are generating & looping you don't even need to use PCMdata just go with dynamic sound generation using sampleData as first choice. If however you involve PCM samples of vocals or band music then you will need the hack below to replay on sound ending
So anyways, for now here is some code demonstration to illustrate the looping hack
package
{
import flash.display.MovieClip;
import flash.events.*;
import flash.utils.*;
import flash.media.*;
import flash.text.*;
public class testSound extends MovieClip
{
private var BIT_TYPE:int = 16; //16bit audio
private var RATE:int = 44100;
private var NUM_SAMPLES:int = 8192; //16384 * 2;
private var NUM_CHANNEL:int = 2; //if stereo
private var NUM_TEMP:int =0; //adjustable number for test without changing others
public var NUM_TONE_FREQ:int = 440;
private var soundByteArray:ByteArray;
private var volume:Number = 1;
private var channel:SoundChannel = new SoundChannel();
public var final_samples:int = 0;
public var time_total:Number; //dont use Integers here - wont always be correct
public var time_kbps:Number; //"bytes per second" count
public var loop_count:int = 0;
public var timerCount:Number = 0;
public var timerSecs:Number = 0;
public var timer:Timer;
public var trans:SoundTransform;
public var baseSound:Sound = new Sound();
public var timeText:TextField;
public var txtFormat:TextFormat;
public function testSound():void
{
//correct NUM_SAMPLES helps with end-time check
NUM_SAMPLES *= NUM_CHANNEL * BIT_TYPE;
trans = new SoundTransform(volume, 0);
channel.soundTransform = trans; //SoundMixer.soundTransform = trans;
soundByteArray = new ByteArray();
soundByteArray.position = 0;
//setup textField for debug feedback
setupTextFBack();
//generate PCM
storeAudio();
}
protected function onPlaybackComplete(event:flash.events.Event):void
{
//only works if you are passing your PCM to sampleData events,
trace("onPlaybackComplete" + channel.position);
}
private function storeAudio():void
{
for (var i:Number = 0; i < NUM_SAMPLES; i++)
{
soundByteArray.writeFloat
( Math.sin((i / RATE) * Math.PI * 2 * NUM_TONE_FREQ) );
soundByteArray.writeFloat
( Math.sin((i / RATE) * Math.PI * 2 * NUM_TONE_FREQ) );
}
trace("storeAudio samples (i) = " + i + ", ByteArray length: " + soundByteArray.length);
final_samples = i;
playAudio();
}
public function playAudio():void
{
soundByteArray.position = 0;
baseSound.loadPCMFromByteArray(soundByteArray, final_samples, "float", true, RATE);
channel = baseSound.play(); //channel = baseSound.play(0, 0, trans);
setupTimeCount(); //count when play starts
time_kbps = (RATE * NUM_CHANNEL * BIT_TYPE) / 4; //not /8 because time is never doubled on stereo
time_total = (soundByteArray.length / time_kbps);
time_total = Math.round(time_total * 100) / 100;
trace ("=== DEBUG INFO : (loop: "+loop_count+ ") =========================================");
trace ("*** Playing beyond Total Time (PCM end) creates sound glitch issues ");
trace ("*** Re-Play on a continous Tone will creates short click when it stops to replay ");
trace ("*** If PCM was music/vocals this click might not be perceived by ear if looped right");
trace ("====================================================================");
trace ("Total Kb/sec : " + time_kbps + " bytes per sec");
trace ("Total time : " + time_total + " secs" );
//trim Total millisecs just to avoid any glitches/clicks. Test & fine-tune
time_total -= 0.314; //PI divided by 10. Need fine-tune? Hell Yes!
trace ("Total (trim) : " + time_total + " secs" );
}
public function setupTimeCount():void
{
timer = new Timer(100);
timer.addEventListener(TimerEvent.TIMER, timerHandler);
timerCount = 0;
timer.start();
}
function timerHandler(Event:TimerEvent):void
{
timerCount += 100;
checkTime(timerCount);
//trace("channel.pos = " + channel.position); //for debug only
}
function checkTime(miliseconds:int) : void
{
timerSecs = miliseconds/1000;
timeText.text = ("elapsed : " + timerSecs);
//if (timerSecs >= time_total)
if ( channel.position >= (time_total * 1000) )
{
reloopAudio();
}
}
function reloopAudio():void
{
channel.stop(); //else you get stutter from going forward
timer.stop();
trace("attempting replay / loop..");
loop_count += 1;
playAudio(); //redo playing function
}
public function setupTextFBack():void
{
txtFormat = new TextFormat();
txtFormat.size = 20;
txtFormat.font = "Arial";
timeText = new TextField();
timeText.defaultTextFormat = txtFormat;
timeText.antiAliasType = AntiAliasType.ADVANCED;
timeText.x = stage.stageWidth / 2 ;
timeText.y = stage.stageHeight / 2 ;
timeText.textColor = 0xFF0000;
timeText.text = " ";
timeText.width = 200;
addChild(timeText);
}
}
}

create sine wave audio with as3 - sweep up and down frequency

I'm making sinewave sound with AS3 using SampleDataEvent. I can make a pure sinewave easily enough, but if i try to sweep through frequencies i get horrible popping. Here's the code i'm using - any help would be great.
package
{
import flash.display.*;
import flash.events.*;
import flash.media.*;
import flash.utils.Timer;
public class DynamicSound extends Sprite
{
private var sound:Sound;
private var noise:Number = 0;
private var f:Number = 1000;
private var v:Number = 1;
private var sweepDown:Boolean = true;
// make the sound
public function DynamicSound():void
{
sound = new Sound();
sound.addEventListener(SampleDataEvent.SAMPLE_DATA, onCallback);
sound.play();
sweep();
}
// create the sinewave
private function onCallback(e:SampleDataEvent):void
{
for (var i:int = 0; i < 8192; i++)
{
noise += 1;
var sampleNumber = noise;
e.data.writeFloat(v * Math.sin(sampleNumber * f / 44100));
e.data.writeFloat(v * Math.sin(sampleNumber * f / 44100));
}
}
// sweep up and down frequency
private function sweep() {
var timer:Timer = new Timer(100);
timer.addEventListener(TimerEvent.TIMER, onTimer);
timer.start();
function onTimer(event:TimerEvent):void
{
if(sweepDown){
f--;
} else {
f++;
}
if (f <=600 ){
sweepDown = false;
}
if (f >= 1000) {
sweepDown = true;
}
trace(f);
}
}
}
}
I believe the fault is with the approach, the sweep needs to be gradual, when you step it like that you create an abrupt change in the sound wave, which is interpreted as a short high-frequency signal - a pop or click. The way I'd recommend you do this modulation would be inside the callback loop.
set a destination freq (dF) and a current frequency(cF), and instead of doing an abrupt change set cF = cF*0.8 + dF*0.2 inside the loop, this should remove the abrupt change and have it happen over several samples.
//When the frequency changes, the phase will also change.
//By adjusting the phase, there will no longer be horrible popping.
//This will solve the problem:
var f_old:Number = f;
noise=noise*f_old/f; f_old=f;
//Copy/paste in the first frame of the main timeline:
import flash.display.*;
import flash.events.*;
import flash.media.*;
import flash.utils.Timer;
var sound:Sound;
var noise:Number = 0;
var f:Number = 1000;
var v:Number = 1;
var sweepDown:Boolean = true;
var f_old:Number = f;
// make the sound
sound = new Sound();
sound.addEventListener(SampleDataEvent.SAMPLE_DATA, onCallback);
sound.play();
sweep();
// create the sinewave
function onCallback(e:SampleDataEvent):void
{
for (var i:int = 0; i < 8192; i++)
{
noise += 1;
var sampleNumber = noise;
e.data.writeFloat(v * Math.sin(sampleNumber * f / 44100));
e.data.writeFloat(v * Math.sin(sampleNumber * f / 44100));
}
}
// sweep up and down frequency
function sweep() {
var timer:Timer = new Timer(100);
timer.addEventListener(TimerEvent.TIMER, onTimer);
timer.start();
function onTimer(event:TimerEvent):void
{
if(sweepDown){
f--;
} else {
f++;
}
if (f <=600 ){
sweepDown = false;
}
if (f >= 1000) {
sweepDown = true;
}
trace(f);
//Keep the instantaneous value at the same level:
noise=noise*f_old/f;
f_old=f;
}
}
//Explanation:
//The instantaneous value of the wave must not change at the moment of frequency change.
//In this script the instantaneous value is determined by:
//v * Math.sin(sampleNumber * f / 44100)
//noise = sampleNumber → v * Math.sin(sampleNumber * f / 44100) = v * Math.sin(noise * f / 44100)
//Keep the instantaneous value at the same level:
//v * Math.sin(noise_new * f / 44100) = v * Math.sin(noise* f_old / 44100)
//Cancel out v*Math.sin and /44100 →
//noise_new * f = noise* f_old → noise_new = noise*f_old/f
//Because noise gets a new value, there is no need to give noise the name noise_new. →
//noise = noise*f_old/f

ActionScript - Ignoring Passed Arguments?

i've been studying this code example for Rung-Kutta physics but i don't understand what is happening with the acceleration(p:Point, v:Point):Point function. the function accepts 2 point objects as required arguments but doesn't use them in the function while simply returning a new point.
i'm unfamiliar with this style of argument passing. can someone explain the significance of this function to me?
the source is from Keith Peters' book Advanced ActionScript 3.0 Animation, Chapter 6 - Advanced Physics, page 246.
package {
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.geom.Point;
import flash.utils.getTimer;
public class RK2 extends Sprite
{
private var _ball:Sprite;
private var _position:Point;
private var _velocity:Point;
private var _gravity:Number = 32;
private var _bounce:Number = -0.6;
private var _oldTime:int;
private var _pixelsPerFoot:Number = 10;
public function RK2()
{
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
_ball = new Sprite();
_ball.graphics.beginFill(0xff0000);
_ball.graphics.drawCircle(0, 0, 20);
_ball.graphics.endFill();
_ball.x = 50;
_ball.y = 50;
addChild(_ball);
_velocity = new Point(10, 0);
_position = new Point(_ball.x / _pixelsPerFoot, _ball.y / _pixelsPerFoot);
_oldTime = getTimer();
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
private function onEnterFrame(event:Event):void
{
var time:int = getTimer();
var elapsed:Number = (time - _oldTime) / 1000;
_oldTime = time;
var accel1:Point = acceleration(_position, _velocity);
var position2:Point = new Point();
position2.x = _position.x + _velocity.x * elapsed;
position2.y = _position.y + _velocity.y * elapsed;
var velocity2:Point = new Point();
velocity2.x = _velocity.x + accel1.x * elapsed;
velocity2.y = _velocity.y + accel1.x * elapsed;
var accel2:Point = acceleration(position2, velocity2);
_position.x += (_velocity.x + velocity2.x) / 2 * elapsed;
_position.y += (_velocity.y + velocity2.y) / 2 * elapsed;
_velocity.x += (accel1.x + accel2.x) / 2 * elapsed;
_velocity.y += (accel1.y + accel2.y) / 2 * elapsed;
if(_position.y > (stage.stageHeight - 20) / _pixelsPerFoot)
{
_position.y = (stage.stageHeight - 20) / _pixelsPerFoot;
_velocity.y *= _bounce;
}
if(_position.x > (stage.stageWidth - 20) / _pixelsPerFoot)
{
_position.x = (stage.stageWidth - 20) / _pixelsPerFoot;
_velocity.x *= _bounce
}
else if(_position.x < 20 / _pixelsPerFoot)
{
_position.x = 20 / _pixelsPerFoot;
_velocity.x *= _bounce;
}
_ball.x = _position.x * _pixelsPerFoot;
_ball.y = _position.y * _pixelsPerFoot;
}
private function acceleration(p:Point, v:Point):Point
{
return new Point(0, _gravity);
}
}
}
I think the author may be using the method acceleration as a place holder, perhaps for updates on a subsequent chapter.
Of course as it is right now, the acceleration method could be rewritten as
private function acceleration(...rest):Point {
return new Point(0, _gravity);
}
Or the arguments could be removed completely (though that would require the places where the method is called to be updated to not contain any arguments.)
This isn't a style of programming per se, but, I have seen this type of placeholder code put into books before.
You also could set the arguments with a null as default, so they are optional.
private function acceleration(p:Point = null, v:Point = null):Point
{
return new Point(0, _gravity);
}

Detect when the Shader is done mixing the audio

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;
}