Okay, I remembered the details of the first time I hit this problem. (I’ve got a 3 month old baby at home, so it takes me a little while to remember what I did last month…)
Search the web for how to use dynamically generate audio in flash and you’ll find a page from Adobe with an easy sample. The handler is trivially ported to haxe:
public function sineWaveGenerator(event:SampleDataEvent):Void
{
var i: Int;
for (i in 0...8192)
{
var n:Float = Math.sin((i + event.position) / Math.PI / 4);
event.data.writeFloat(n);
event.data.writeFloat(n);
}
}
Compile that for a flash target and it works great! But now compile it for Windows or Mac and you get white noise. (Okay, that’s not true anymore, but I’ll get to that in a moment.) Turns out the problem is that ByteArray.writeFloat() writes using whatever endianness is set in ByteArray.endian, but the bytes are read back out to send to the driver in lime C++ code with essentially:
double value = *(double *)&array.data;
So it’s always read out in endianness of the target.
The simple fix for this is to set the endianness of the array in that handler:
public function sineWaveGenerator(event:SampleDataEvent):Void
{
event.data.endian = Endian.LITTLE_ENDIAN;
...
}
But now this code no longer matches the flash original and it’s not obvious to a haxe developer looking at an examples from AS3 that they’d need to do this.
As mentioned earlier, if you have a recent version of openfl, you won’t be able to reproduce this because I already checked in a workaround for this specific case by defaulting SampleDataEvent to use little endian, as that’s far more common now. Of course, this workaround now means that if this is used on a big endian target, it will fail there.
I’d like to fix SampleDataEvent (and ByteArray in general) to default to the endianness of the target.
Although I thought at first that @player_03’s suggestion would work, I actually haven’t come up with a workable test in haxe. I can make a ByteArray and write some data into it in big endian order:
var endianTest = new ByteArray();
endianTest.writeByte(0xDE);
endianTest.writeByte(0xAD);
endianTest.writeByte(0xBE);
endianTest.writeByte(0xEF);
But then I can read it out both “correct” as a big endian value:
endianTest.endian = Endian.BIG_ENDIAN;
endianTest.position = 0;
var test = endianTest.readUnsignedInt();
trace('Reading as big endian returned 0x${StringTools.hex(test)}');
Produces:
Reading as big endian returned 0xDEADBEEF
and also “incorrect” as a little endian value:
endianTest.endian = Endian.LITTLE_ENDIAN;
endianTest.position = 0;
var test = endianTest.readUnsignedInt();
trace('Reading as little endian returned 0x${StringTools.hex(test)}');
produces:
Reading as little endian returned 0xEFBEADDE
As far as I can tell, that doesn’t give me any information about the endianness of the target.
I’ll look into the C++ solutions. It sounds like there is no existing solution for this, so perhaps I need to write a lime function to return the endianness of the target.