read mp3 decimal values - actionscript-3

I would like to read the be able to store the decibel values across intervals of a local mp3 into a text file. i think i can handle writing to a text file once i have the values(although any help would be great)
Best
i want to do this using AS3
and many thanks

First, you must load the MP3, for example, by using Sound.load(), or by including it in your library in the Flash IDE and exporting it for ActionScript. Then you can use Sound.extract() to grab the waveform data from an MP3. This will give you the sample data back for the interval packed in a ByteArray, which you can read out.
The samples are in the range of [-1.0, 1.0], so one simple way to calculate an intensity level for an interval is to find the maximum absolute value among the samples. Here's some example code:
var sound:Sound = new Sound();
sound.load(new URLRequest("sound.mp3"));
sound.addEventListener(Event.COMPLETE, onSoundLoaded);
function onSoundLoaded(event:Event):void {
var byteArray:ByteArray = new ByteArray();
sound.extract(byteArray, 4096);
byteArray.position = 0;
var max:Number = 0;
while(byteArray.position != byteArray.length)
{
var sample:Number = Math.abs(byteArray.readFloat());
if(sample > max) max = sample;
}
trace(max);
}
That will output the level for the first 4096 samples. You'll have to repeat this to get more values.

Related

Getting number of bytes of a bitmap

Goal is to show progress of jpeg encoding from bitmap. I have couple of bitmaps that need to be encoded. So I get the total number of bytes as was suggested here:
for (var i:int = 0; i < bitmaps.length; i++)
{
bmp = bitmaps[i];
total_bytes += bmp.getPixels(bmp.rect).length;
}
Then I'm trying to show progress when doing asychronous encoding. I get a ProgressEvent which gives me bytesLoaded. So I calculate the progress like so:
total_loaded_bytes += event.bytesLoaded;
var percentage:int = ((total_loaded_bytes / total_bytes) * 100);
However, total_bytes does not add up to total_loaded_bytes. Total bytes loaded is way highter.
A wrong approach to use bytesLoaded property. This is not to be blandly added up, as this contains the total of already loaded bytes bu the Loader object that issued the event. And a wrong approach on getting total bytes too, you need to use event.bytesTotal from within the progress event listener, since you are loading bytes, not pixels. Even if you are uploading. Also, the exact progress may be totally unavailable for asynchronous encoding, you are only displaying the uploading/downloading progress.
Update: to receive an accumulated progress, do something like this:
var loaders:Array=[]; // array of loaders. Fill manually
var progress:Array=[]; // fill with zeroes alongside loaders, should be same size
function updateProgress(e:ProgressEvent):void {
var loader:Loader=e.target as Loader;
if (!loader) return; // or skip type coercion, we just need the reference
var i:int=loaders.indexOf(loader); // now get an index from array
if (i<0) return; // not found, drop it
progress[i]=e.bytesLoaded; // and update progress array with new value
// now sum up progress array and divide by aggregated bytesTotal
// this one is up to you
}

AS3 mp3 import error: The AMF encoding of the arguments cannot exceed 40K

NOTE: I have seen the other question in Error #2084-The AMF Encoding of the arguments cannot exceed 40K
my problem is different. My array IS NOT 0 and it is less than 40960.
My code is a simple one. I got this mp3 recording fla from this link: http://www.jordansthings.com/blog/?p=5
It uses the shinemp3 encoder.
I just wanted to play the recorded sound rather than saving it. So I added the following to the button that saves the recorded file:
private function onWavClick(e:MouseEvent)
{
// WRITE ID3 TAGS
var sba:ByteArray = mp3Encoder.mp3Data;
sba.position = sba.length - 128
sba.writeMultiByte("TAG", "iso-8859-1");
sba.writeMultiByte("Microphone Test 1-2, 1-2 "+String.fromCharCode(0), "iso-8859-1"); // Title
sba.writeMultiByte("jordansthings "+String.fromCharCode(0), "iso-8859-1"); // Artist
sba.writeMultiByte("Jordan's Thingz Bop Volume 1 "+String.fromCharCode(0), "iso-8859-1"); // Album
sba.writeMultiByte("2010" + String.fromCharCode(0), "iso-8859-1"); // Year
sba.writeMultiByte("www.jordansthings.com " + String.fromCharCode(0), "iso-8859-1");// comments
sba.writeByte(57);
//new FileReference().save(sba, "FlashMicrophoneTest.mp3") // this saves the file. I don't need it.
// my addition
var snd:Sound = new Sound();
var channel:SoundChannel = new SoundChannel();
trace(sba.length);
snd.loadCompressedDataFromByteArray(sba,sba.length);
channel = snd.play();
}
Moreover: even if this works... I cannot load an array larger than 40K???
Before calling loadCompressedDataFromByteArray, you should set the position of your ByteArray to 0. For example:
sba.position = 0;
snd.loadCompressedDataFromByteArray(sba,sba.length);
I noticed that in my application the bytesAvailable on the ByteArray was 0. This was because the position on the ByteArray was at the end of the ByteArray. The error message is confusing because it says you are exceeding 40K while you are not. It should be saying that there is nothing to load from the ByteArray.
Also, I can confirm that I can load ByteArrays that are larger than 40K. I tested with 126KB mp3 ByteArray.
In my case the problem was not the size of the ByteArray I wanted to read. I was reading and playing 30 Mb mp3 files without problem (that's a lot, I know!). The problem was that I was reading the file many times, and after the first time the position of the ByteArray was at the end. So you have to restart to 0 the position any time you want to read that byteArray. For security, I assume it is a good practice.

flash as3 How to remove part of byteArray?

var fData:ByteArray = new ByteArray();
I need to remove some bytes in this array, but can't find any public method in Flash to do that.
I searched something like fData.remove(start,length) but with no success.
here is a code
function _dlProgressHandler(evt:ProgressEvent):void { //this is progressEvent for URLStream
............... ///some code
var ff:ByteArray = new ByteArray();
stream.readBytes(ff,0,stream.bytesAvailable);
fileData.writeBytes(ff,0,ff.length); //stream writes into fileData byteArray
//and here is cutter:
fileData.position=0;
fileData.writeBytes(ff,100,fileData.length);
fileData.length=fileData.length-100);
}
So, fileData cut itself unpredictably sometimes.
Sometimes old blocks're found twice, sometimes they're not found at all.
You can always just only read the bytes that you want, which will have the same effect as discarding the bytes that you don't want. As a very simple example, assume you have a ByteArray that is 10 bytes long and you want to discard the first 3 bytes:
var newBytes:ByteArray = new ByteArray();
newBytes.writeBytes(fData, 2, 7);
So instead of removing the bytes that you don't want from fData, you just create a new ByteArray and only get they bytes that you want from fData.
Obviously, if the sequence of bytes you want to remove is not just a sequence from the beginning or end of fData it will be a little more complicated, but the method remains the same: read the bytes that you want, instead of removing the ones you don't.
AS-3 is actually very nice sometimes. This removes bytes from your array anywhere you want. Beginning, middle or the end. Just need to check indices to avoid IndexOutOfBounds
var array: ByteArray = ...; // create the byte array or load one
var index: int = 4;
var count: int = 5;
array.position = index;
array.writeBytes(array, index + count, array.length - (index + count));
array.length = array.length - count;
I tested this and it works well, just the checks are missing
a byte array can write to itself

mixing 2 sounds from ByteArray

I have build a mixer and save all the sequence in an array and then play it again, now I want to save the mix as an MP3, I have looked for any way to save the mix and the answer is to load the sounds as byteArrays (sound.extract) I have acomplish that but I don't really know how to store all the sounds in just one ByteArray in order to save it as MP3, I got this code just for example, loading 2 audio files and store them in separate ByteArrays, and play each sound, does any body know how to store the 2 byteArrays in just one?
var mySound:Sound = new Sound();
var sourceSnd:Sound = new Sound();
var urlReq:URLRequest = new URLRequest("Track1.mp3");
sourceSnd.load(urlReq);
sourceSnd.addEventListener(Event.COMPLETE, loaded);
function loaded(event:Event):void
{
mySound.addEventListener(SampleDataEvent.SAMPLE_DATA, processSound);
//mySound.play();
}
var mySound2:Sound = new Sound();
var sourceSnd2:Sound = new Sound();
var urlReq2:URLRequest = new URLRequest("Track2.mp3");
sourceSnd2.load(urlReq2);
sourceSnd2.addEventListener(Event.COMPLETE, loaded2);
function loaded2(event:Event):void
{
mySound2.addEventListener(SampleDataEvent.SAMPLE_DATA, processSound2);
mySound2.play();
mySound.play();
}
function processSound(event:SampleDataEvent):void
{
var bytes:ByteArray = new ByteArray();
sourceSnd.extract(bytes, 8192);
event.data.writeBytes(bytes);
}
function processSound2(event:SampleDataEvent):void
{
var bytes:ByteArray = new ByteArray();
sourceSnd2.extract(bytes, 8192);
event.data.writeBytes(bytes);
}
been working on a similar system for a while, I'll do my best to give you some direction:
Your example code is not really mixing the MP3's - it's creating 2 more sounds to playback the loaded MP3's via the SampleDataEvent. What you need to do is create just one "output" Sound file that will hold/playback the resulting mixed sound. You can easily save that data as it happens and subsequently save that file as a new WAV/MP3/what-have-you.
real/psuedo-code (read:lazy) :
output = new Sound();
output.addEventListener( SampleDataEvent.SAMPLE_DATA , mixAudio );
song1 = new Sound / load the first mp3
song2 = new Sound / load the second mp3
// a byteArray for "recording" the output and subsequent file creation
recordedBytes = new ByteArray();
either wait until both mp3's are completely loaded, or run an enter-frame to determine when both Sounds are no longer buffering (Sound.isBuffering )
when the mp3's are ready:
// numbers to store some values
var left1:Number;
var right1:Number;
var left2:Number;
var right2:Number;
// bytearrays for extracting and mixing
var bytes1:ByteArray = new ByteArray( );
var bytes2:ByteArray = new ByteArray( );
// start the show
output.play();
function mixAudio( e:SampleDataEvent ):void{
//set bytearray positions to 0
bytes1.position = 0;
bytes2.position = 0;
//extract
song1.extract( bytes1, 8192 );
song2.extract( bytes2, 8192 );
// reset bytearray positions to 0 for reading the floats
bytes1.position = 0;
bytes2.position = 0;
// run through all the bytes/floats
var b:int = 0;
while( b < 8192 ){
left1 = bytes1.readFloat(); // gets "left" signal
right1 = bytes1.readFloat(); // gets "right" signal
left2 = bytes2.readFloat();
right2 = bytes2.readFloat();
// mix!
var newLeft:Number = ( left1 + left2 ) * .5;
var newRight:Number = ( right1 + right2 ) * .5;
// write the new stuff to the output sound's
e.data.writeFloat( newLeft );
e.data.writeFloat( newRight );
// write numbers to the "recording" byteArray
recordedBytes.writeFloat( newLeft );
recordedBytes.writeFloat( newRight );
b++;
}
}
Yes - you should really cap the possible output at -1/1. Do it. This is extremely un-optimized!
Ok. so that's the easy part! The tough part is really converting the final byteArray to MP3. The audio exists within Flash as PCM/uncompressed data, MP3 is obviously a compressed format. This "answer" is already way too long and all this info I've gleaned from several very smart folks.
You can easily adapt 'MicRecorder' to be a generic Sound data recorder:
http://www.bytearray.org/?p=1858
converting to MP3 will be a bitch: Thibault has another post on ByteArray.org - search for LAME MP3.
Excellent example/resource:
http://www.anttikupila.com/flash/soundfx-out-of-the-box-audio-filters-with-actionscript-3/
Look up Andre Michelle's open source 'Tonfall' project on Google code.
Look up Kevin Goldsmith's blog and labs - he's got great example on utilizing Pixel Bender with all this madness.
hope this helps!
PS - taking a cue from Andre, the optimal length of the audio buffer should be 3072. Give it a try on your machine!
If I understand your question properly, you need read the floating point data for each sample, sum them, and write the resulting value into your new audio stream. This would give a stright 50/50 mix.
I don't have access to a machine with a compiler right now, but it should be something like this (assuming bytes1 and bytes2 are two ByteArray objects of equal length):
for (int bcnt = bytes1.size(); bcnt; bcnt--)
bytes1.setByte(bcnt - 1, bytes2.getByte(bcnt - 1) + bytes1.getByte(bcnt - 1));
Of course, you would probably want to do some sort of overflow check, but that should be enough to put you on the right track.
if you have uncompressed audio, you can just add up the values of individual array elements in your ByteArray's. But you also have to handle capping for max/min values (no overflows). Pretty sure there is nothing equivalent for mixing MP3s - so you might have to decode, mix, and encode to MP3 again.

How to avoid create new bitmapData object in loop in AS3?

I want to store the bitmap data from _sampleTile in array, but I was wondering how to increase the performance. If I do it like this:
var _sampleTile:BitmapData;
var _arrayLenght:int = _tileClipArray.length;
for(var i:int = 0; i < _arrayLenght; ++i){
_sampleTile = new BitmapData(65, 65, false);
_sampleTile.draw(_tileClipArray[int(i)]);
_tileBitmapDataArray[i] = _sampleTile;
}
Then it would do too much constructing job in the loop, right? But if I do as bellow:
var _sampleTile:BitmapData = new BitmapData(65, 65, false);
var _arrayLenght:int = _tileClipArray.length;
for(var i:int = 0; i < _arrayLenght; ++i){
_sampleTile.fillRect(_sourceRectangle, 0x00FFFFFF);
_sampleTile.draw(_tileClipArray[int(i)]);
_tileBitmapDataArray[i] = _sampleTile.clone();
}
The .clone() returns a new BitmapData object so basically the result is the same, right?
In the second example if we replace the _sampleTile.clone() with _sampleTile - is it somehow possible to not store in array a reference to _sampleTile, but get the actual bitmapData from the _simpleTile?
No, you need to create a new BitmapData each iteration... either with clone() or new.
I see a couple alternatives though:
Make your creation asynchronous. Do just a few each frame, till you finish the whole batch.
Create a big BitmapData, draw all tiles in there and use references for the position of each tile. If the tiles are always the same, then you could eventually save the final BitmapData + positions and load them instead of creating them each time you run the application.