I've found some excellent demos of how to mix together sound objects together for live playback. See the working example bellow...
But can it be done programmatically without any playback so I can just output the mixed file? Also I'll be adding some volume change info along the way so it'll need to be added in small chunks like how the play buffer works.
[Embed(source = "audio/track01.mp3")]
private var Track1:Class;
[Embed(source = "audio/track02.mp3")]
private var Track2:Class;
[Embed(source = "audio/track03.mp3")]
private var Track3:Class;
[Embed(source = "audio/track04.mp3")]
private var Track4:Class;[Embed(source = "AudioMixerFilter2.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.<Number> = new Vector.<Number>();
private var sliderVol:int = 1;
private var BUFFER_SIZE:int = 0x800;
public var playback:Sound = new Sound();
public function startAudioMixer(event:FlexEvent):void{
sound.push(new Track1(), new Track2(), new Track3(), new Track4());
sliders.push(sliderVol,sliderVol,sliderVol,sliderVol);
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)) * sliderVol; // SLIDER VOL WILL CHANGE
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]];
}
var shaderJob:ShaderJob = new ShaderJob(shader,event.data,BUFFER_SIZE / 1024,512);
shaderJob.start(true);
}
Easiest way would be to just forget about the Pixel Bender stuff.
Once the Sounds are loaded, use an ENTER_FRAME that uses Sound.extract to get a smallish ByteArray from each Sound, then read through all four extracted ByteArrays doing some basic math to arrive at the 'mixed' values for the left & right signals. Write those values to the "final/mixed/output" ByteArray. Repeat the process each frame until you're at the end of the sounds. If the Sounds aren't all the identical length, you'll need to figure out how to handle that as well.
If you need to perform a mix where the amplitude of each track changes over time, it'd be a good challenge, but would take time to set up.
While you're at it, check out Andre Michelle's Tonfall project... It's a complex but great place to start with understanding the ins/outs of audio in AS3.
Related
So I'm creating a monopoly game, and I've got two functions which read/write information about the game to/from a database so it can be retrieved between postbacks. I also have a start game function which initialises everything to 0 and then calls the writeToDatabase(). createArrays() just creates a list of players/squares, squares have been removed as they'not relevant.
The problem I'm having is I believe it is only storing some of the values within the loop. When the start game function is called it correctly sets the players positions to 0, and their money to 1500, however for some reason when reading from the database again the 4th players details immediately go back to what they were during the previous session.
private void readFromDatabase()
{
createArrays();
for (int i = 0; i < players.Count(); i++)
{
players[i].SetID(Convert.ToInt32(players_table.GetRow(i)["ID"]));
players[i].SetPosition(Convert.ToInt32(players_table.GetRow(i)["position"]));
players[i].SetMoney(Convert.ToInt32(players_table.GetRow(i)["money"]));
players[i].SetInJail(Convert.ToBoolean(players_table.GetRow(i)["isInJail"]));
players[i].SetIsBankrupt(Convert.ToBoolean(players_table.GetRow(i)["isBankrupt"]));
}
currentPlayerID = Convert.ToInt32(gameinfo_table.GetRow(0)["CurrentPlayerID"]);
currentSquareID = Convert.ToInt32(gameinfo_table.GetRow(0)["CurrentSquareID"]);
freeParkingAmount = Convert.ToInt32(gameinfo_table.GetRow(0)["FreeParkingAmount"]);
currentPlayer = players[currentPlayerID];
CurrentSquare = squares[currentSquareID];
}
private void writeToDatabase()
{
CurrentSquare = squares[currentSquareID];
for (int i = 0; i < players.Count(); i++)
{
SQLDatabase.DatabaseRow prow = players_table.GetRow(players[i].GetID());
prow["ID"] = players[i].GetID().ToString();
prow["position"] = players[i].GetPosition().ToString();
prow["money"] = players[i].GetMoney().ToString();
prow["isInJail"] = players[i].IsInJail().ToString();
prow["isBankrupt"] = players[i].IsBankrupt().ToString();
players_table.Update(prow);
}
SQLDatabase.DatabaseRow girow = gameinfo_table.GetRow(0);
girow["CurrentPlayerID"] = currentPlayerID.ToString();
girow["CurrentSquareID"] = CurrentSquare.GetID().ToString();
girow["FreeParkingAmount"] = freeParkingAmount.ToString();
gameinfo_table.Update(girow);
}
private void startGame()
{
createArrays();
currentPlayerID = 0;
currentPlayer = players[0];
CurrentSquare = squares[0];
writeToDatabase();
}
private void createArrays()
{
for (int i = 0; i < 4; i++)
{
Player player = new Player(i);
players.Add(player);
}
}
I really can't work out why this is happening, as when it creates the player array it loops through 4 times, therefore creating 4 brand new players, and immediately overwriting the details in the database. It just seems odd it works perfectly fine for the first 3 players but the 4ths details don't seem to change. Any help would be greatly appreciated, I'm sure it something simple but I have spent hours on this and haven't got anywhere.
I'm trying to draw an object masked with a circle, but the mask applied is more like a circle's bounding rectangle
I tried using circle drawn with drawCircle method:
private function squareToRound(ability:MovieClip):MovieClip
{
var container:MovieClip = new MovieClip();
var newAbility:MovieClip = ability;
var newMask:MovieClip = new MovieClip();
newAbility.width = 43;
newAbility.height = 43;
newMask.x = newAbility.x + newAbility.width / 2;
newMask.y = newAbility.y + newAbility.height / 2;
container.addChild(newAbility);
newMask.graphics.beginFill();
newMask.graphics.drawCircle(0, 0, 8);
newMask.graphics.endFill();
container.addChild(newMask);
newAbility.mask = newMask;
return container;
}
... and as an asset from SWF:
private function squareToRound(ability:MovieClip):MovieClip
{
var container:MovieClip = new MovieClip();
var newAbility:MovieClip = ability;
var newMask:MovieClip = new CircleAbilityMask();
newAbility.width = 43;
newAbility.height = 43;
newMask.x = newAbility.x + newAbility.width / 2;
newMask.y = newAbility.y + newAbility.height / 2;
container.addChild(newAbility);
container.addChild(newMask);
newAbility.mask = newMask;
return container;
}
The results are equal.
I checked what the mask looks like by commenting "newAbility.mask = newMask;" line
This should be resolved in the current OpenFL release:
https://github.com/openfl/openfl/blob/develop/CHANGELOG.md#650-11102017
You can also use Cairo (on native platforms) or Canvas (on HTML5) for smoother/better masking support:
openfl test html5 -Dcanvas
openfl test windows -Dcairo
These are also used when you bitmapData.draw, sprite.cacheAsBitmap = true or other APIs that trigger a software render rather than GL.
I'm trying to figure out how to extrude an already loaded .obj object to make it "thicker". I think I'm looking for a way to scale my object not from its anchor point but scaling it by each polygon normal.
A classic example would be to take a "ring" object. If you scale it up with just the normal scale methods it just gets bigger from the center but I want the ring to become thicker/thinner. Its called a 'normal scale' in cinema 4d.
Here's some example code of what I currently have, and which isn't giving me the expected result.
var objLoader = new THREE.OBJLoader();
var material = new THREE.MeshLambertMaterial({color:'yellow', shading:THREE.FlatShading});
objLoader.load('objects/gun/M1911.obj', function (obj) {
obj.traverse(function (child) {
if (child instanceof THREE.Mesh) {
child.geometry.computeVertexNormals();
child.material = material;
}
});
obj.material = material;
obj.scale.set(7, 7, 7);
scene.add(obj);
});
scaleGeo: function (geo, ratio) {
for (var i = 0; i < geo.faces.length; i++) {
var faceI = geo.faces[i];
var vertexArr = [faceI.a, faceI.b, faceI.c];
for (var j = 0; j < vertexArr.length; j++) {
var vertexJ = geo.vertices[vertexArr[j]];
var normalJ = faceI.vertexNormals[j];
if (vertexJ.hasScale) continue;
vertexJ.x += normalJ.x * ratio;
vertexJ.y += normalJ.y * ratio;
vertexJ.z += normalJ.z * ratio;
vertexJ.hasScale = true;
}
}
return geo;
},
The scenario:
I have a flash website (here) that imports some of its content from a wordpress site (here) via the Wordpress's RSS (I added a plugin to include pages in the RSS). This isn't a brilliant idea I know, but for a long list of reasons that I won't go into it's the setup I'm stuck with :/
The Problem:
When importing the RSS feed (it's downloaded into the SWF at runtime and read like an XML), I can't get the filesize. This means the preloader isn't accurate (I just manually guessed a number for it to work with for now, which obviously will grow over time). How do I get this file size?
I know there may be certain settings on the server that compresses the feed/files (GZip or such), but I'm very unfamiliar with wordpress architecture and despite my best efforts I couldn't find where this setting would be applied.
This is the preloader update code. The 113*1024 is where I want to put rss_loader.bytesTotal (the loader of the WordPress content), but when I do rss_loader.bytesTotal returns as 0:
function update_loading(e:Event):void
{
var total:Number = stage.loaderInfo.bytesTotal + (113 * 1024) + css_loader.bytesTotal;
var loaded:Number = stage.loaderInfo.bytesLoaded + rss_loader.bytesLoaded + css_loader.bytesLoaded;
if (total == loaded)
{
updatePicture(360);
loading_mc.perc.text = "100%";
removeEventListener(Event.ENTER_FRAME,update_loading);
nextFrame();
}
else
{
var deg:Number = (loaded / total) * 360;
updatePicture(deg);
loading_mc.perc.text = String(int((loaded / total) * 100))+"%";
}
}
Here's the RSS import code, though I don't believe it's very relevant:
var rss_xml:XML = new XML();
var post_xml:XML = new XML();
var page_xml:XML = new XML();
var rss_loader:URLLoader;
rss_loader = new URLLoader(new URLRequest("http://news.sulsc.org/feed"));
function rss_loaded(e:Event):void
{
rss_xml = XML(e.target.data);
var rss_raw:String = String(rss_xml);
rss_raw = rss_raw.split(":encoded").join("");
rss_raw = rss_raw.split("\n").join("");
rss_raw = rss_raw.split('<p style="text-align: right;">').join('<p class="right">');
rss_raw = rss_raw.split('<p style="text-align: center;">').join('<p class="centre">');
rss_raw = rss_raw.split('<p style="text-align: left;">').join('<p class="left">');
rss_raw = rss_raw.split('<p style="text-align: justify;">').join('<p class="just">');
var heading_replace:RegExp = new RegExp("(</h[0-9]>)(<h[0-9]>)","g9");
var underline_replace:RegExp = new RegExp('<span style="text-decoration: underline;">(.*?)</span>',"gi");
var bold_replace:RegExp = new RegExp('<strong>(.*?)</strong>',"gi");
var italic_replace:RegExp = new RegExp('<em>(.*?)</em>',"gi");
rss_raw = rss_raw.replace(heading_replace,'$1<p class="space"></p>$2');
rss_raw = rss_raw.replace(underline_replace,'<u>$1</u>');
rss_raw = rss_raw.replace(bold_replace,'<b>$1</b>');
rss_raw = rss_raw.replace(italic_replace,'<i>$1</i>');
var page_test:RegExp = new RegExp("http://(news.|www.|)sulsc\\.org/(|wordpress/)\\?page_id=[0-9]+","gi");
page_xml = XML(rss_raw);
post_xml = XML(rss_raw);
rss_xml = null;
for (var i:uint; i<post_xml.channel.item.length(); i++)
{
if (page_test.test(post_xml.channel.item[i]) == true)
{
post_xml.channel.item[i] = "";
i--;
}
if (i == post_xml.channel.item.length()-1)
{
rss_loader.bytesLoaded = 113 * 1024;
}
}
}
I ran into a similar issue with a project last year. Turns out the server wasn't returning the correct header for Flash to pick up on. I can't remember which header it needs to populate bytesTotal, unfortunately, but I did come up with a quick work around.
this.urlLoader.addEventListenever( HTTPStatusEvent.HTTP_RESPONSE_STATUS, this.statusResponse );
private function statusResponse(e:HTTPStatusEvent = null):void{
for ( var i:Number = 0; i < e.responseHeaders.length; i++ ) {
var current:Object = e.responseHeaders[i];
if ( current.name.toLowerCase() == "content-length" ) {
this.bytesTotal = current.value;
break;
}
}
}
This worked for me, although it obviously relies on the server sending a Content-Length header.
Is it possible in an air application to start a download, pause it and after that resume it?
I want to download very big files (1-3Gb) and I need to be sure if the connection is interrupted, then the next time the user tries to download the file it's start from the last position.
Any ideas and source code samples would be appreciated.
Yes, you would want to use the URLStream class (URLLoader doesn't support partial downloads) and the HTTP Range header. Note that there are some onerous security restrictions on the Range header, but it should be fine in an AIR application. Here's some untested code that should give you the general idea.
private var _us:URLStream;
private var _buf:ByteArray;
private var _offs:uint;
private var _paused:Boolean;
private var _intervalId:uint;
...
private function init():void {
_buf = new ByteArray();
_offs = 0;
var ur:URLRequest = new URLRequest( ... uri ... );
_us = new URLStream();
_paused = false;
_intervalId = setInterval(500, partialLoad);
}
...
private function partialLoad():void {
var len:uint = _us.bytesAvailable;
_us.readBytes(_buf, _offs, len);
_offs += len;
if (_paused) {
_us.close();
clearInterval(_intervalId);
}
}
...
private function pause():void {
_paused = true;
}
...
private function resume():void {
var ur:URLRequest = new URLRequest(... uri ...);
ur.requestHeaders = [new URLRequestHeader("Range", "bytes=" + _offs + "-")];
_us.load(ur);
_paused = false;
_intervalId = setInterval(500, partialLoad);
}
if you are targeting mobile devices, maybe you should take a look at this native extension: http://myappsnippet.com/download-manager-air-native-extension/ it supports simultaneous resumable downloads with multi-section chunks to download files as fast as possible.