I am attempting to read the data directly from a SharedObject (.sol) file in as3. I've been able to extract the header information :
// HEADER BYTES
var header1:int = stream.readShort();
// LENGTH
var length:int = header1 & 0x3f;
if (length == 0x3f)
length = stream.readInt();
// FILETYPE - should be "TCSO"
var sig:String = stream.readUTFBytes( 4 );
// PAD: Unused, 6 bytes long 0x00 0x04 0x00 0x00 0x00 0x00 0x00
var pad:ByteArray = new ByteArray();
stream.readBytes( pad, 0, 6 );
// NAME
// 2 byte short length
var nameLength:int = stream.readUnsignedShort();
var name:String = stream.readUTFBytes( nameLength );
var amfVersion:int = stream.readInt();
But I am having troubles interpreting the data that follows:
var data:ByteArray = new ByteArray();
stream.readBytes( data );
I was under the belief it was stored as AMF data and hence the ByteArray.readObject function should correctly decode it:
var sharedObjectData:Object = data.readObject();
However this fails with all of my test objects with a Range Error.
Does anyone know the format of the data in a SharedObject or how to decode it?
REASON: I am doing this because we have an app deployed and accidentally changed the name of the swf which made the SharedObject inaccessible using the SharedObject class.
ie. SharedObject in app_new.swf can't read #SharedObjects/app.swf/objectname.sol
So need to parse the sol SharedObject file using direct File access.
I don't use sharedObjects (never needed them) so I can only advise about bytes. Anytime you get "out of range/bounds" error it can be fixed with checking & correcting the byte array's .position.
In your case, first trace position and length of data to be sure there is enough data in front (from current position) to read an Object out of those remaining bytes.
Other options to try for solving "Out of range" error: (use .writeBytes instead of .readBytes)
var data:ByteArray = new ByteArray();
//or replace "stream.position" with offset value where you think "Object" data begins
data.writeBytes( stream, stream.position, stream.bytesAvailable );
data.position = 0; //reset before reading
var sharedObjectData:Object = data.readObject();
Also look at Inflate and Uncompress just incase data must be expanded before usage.
Related
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.
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
I am trying to understand the significance of null bytes in a ByteArray. Do they act like a terminator? I mean, can we not write further into the ByteArray once a null byte has been written?
For instance,
import flash.utils.*;
public class print3r{
public function print3r{
Util.print(nullout());
}
public function nullout:ByteArray (){
var bytes:ByteArray = new ByteArray();
bytes.writeInt(((403705888 + 1) - 1)); // Non Printable Characters
bytes.writeInt(((403705872 - 1) + 1)); // Non Printable Characters
bytes.writeInt(0x18101000); // Notice the NullByte in this DWORD
bytes.writeInt(0x41424344); // ASCII Characters ABCD
return bytes;
}
}
new print3r;
This gives a blank output.
Now, if I replace the DWORD, 0x18101000 with 0x18101010, this time I can see the ASCII padding, ABCD in the output.
My question is that, is it possible to write past the null byte into the ByteArray()?
The reason I ask is because I have seen in an ActionScript code, that a lot of writeInt and writeByte operations are performed on the ByteArray even after the null byte is written.
Thanks.
is it possible to write past the null byte into the ByteArray()?
Of course it is. ByteArray -- is a chunk of raw data. You can write whatever you like there, and you can read in whatever way you like (using zero bytes as delimiters or whatever else you may want to do).
What you see when you send your bytes to standard output with trace(), depends solely on what you actually do with your data to convert it to a string. There are several ways of converting an array of bytes to string. So, your question is missing the explanation of what Util.print() method does.
Here are several options for converting bytes to a string:
Loop through bytes and output characters, encoding is up to you.
Read a string with ByteArray.readUTFBytes(). This method reads utf-encoded symbols; it stops when zero character is encountered.
Read a string with ByteArray.readUTF(). This method expects your string to be prefixed with unsigned short indicating its length. In other terms it is the same as ByteArray.readUTFBytes().
Use ByteArray.toString(). This is what happens when you simply do trace(byteArray);. This method ignores zero bytes and outputs the rest. This method uses System.useCodePage setting to decide on the encoding, and can use UTF BOM if the data begins with it.
Here are some tests that illustrate the above:
var test:ByteArray = new ByteArray();
// latin (1 byte per character)
test.writeUTFBytes("ABC");
// zero byte
test.writeByte(0);
// cyrillic (2 bytes per character)
test.writeUTFBytes("\u0410\u0411\u0412");
trace(test); // ABCАБВ
trace(test.toString()); // ABCАБВ
test.position = 0;
trace(test.readUTFBytes(test.length)); // ABC
// simple loop
var output:String = "";
var byte:uint;
for (var i:uint = 0; i<test.length; i+=1) {
byte = uint(test[i]);
if (output.length && i%4 == 0) {
output += " ";
}
output += (byte > 0xF ? "" : "0") + byte.toString(16);
}
trace(output); // 41424300 d090d091 d092
Writing a null to a byte array has no significance as far as I know. The print function might however use it as a string terminator.
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.
var bytes:ByteArray = new ByteArray;
bytes.writeInt(0);
trace(bytes.length); // prints 4
trace(bytes.toString().length); // prints 4
When I run the above code the output suggests that every character in the string returned by toString contains one byte from the ByteArray. This is of course great if you want to display the content of the ByteArray, but not so great if you want to send its content encoded in a string and the size of the string matters.
Is it possible to get a string from the ByteArray where every character in the string contains two bytes from the ByteArray?
You can reinterpret your ByteArray as containing only shorts. This lets you read two bytes at a time and get a single number value representing them both. Next, you can take these numbers and reinterpret them as being character codes. Finally, create a String from these character codes and you're done.
public static function encode(ba:ByteArray):String {
var origPos:uint = ba.position;
var result:Array = new Array();
for (ba.position = 0; ba.position < ba.length - 1; )
result.push(ba.readShort());
if (ba.position != ba.length)
result.push(ba.readByte() << 8);
ba.position = origPos;
return String.fromCharCode.apply(null, result);
}
There is one special circumstance to pay attention to. If you try reading a short from a ByteArray when there is only one byte remaining in it, an exception will be thrown. In this case, you should call readByte with the value shifted 8 bits instead. This is the same as if the original ByteArray had an extra 0 byte at the end. (making it even in length)
Now, as for decoding this String... Get the character code of each character, and place them into a new ByteArray as shorts. It will be identical to the original, except if the original had an odd number of bytes, in which case the decoded ByteArray will have an extra 0 byte at the end.
public static function decode(str:String):ByteArray {
var result:ByteArray = new ByteArray();
for (var i:int = 0; i < str.length; ++i) {
result.writeShort(str.charCodeAt(i));
}
result.position = 0;
return result;
}
In action:
var ba:ByteArray = new ByteArray();
ba.writeInt(47);
ba.writeUTF("Goodbye, cruel world!");
var str:String = encode(ba);
ba = decode(str);
trace(ba.readInt());
trace(ba.readUTF());
Your question is a bit confusing. You have written a 4 byte int to your ByteArray. You haven't written any characters (unicode or other) to it. If you want to pass text, write text and pass it as UTF8. It will take less space than having two bytes for every character, at least for most western languages.
But honestly, I'm not sure I understood what you are trying to accomplish. Do you want to send numbers or text? What backend are you talking to? Do you need a ByteArray at all?