I have a float32Array from decodeAudioData method, which I want to convert it to Uint8Array while preserving the float32 IEEE 754 audio data.
So far I tried,
var length = float32Array.length;
var emptyBuffer = new ArrayBuffer(length * 4);
var view = new DataView(emptyBuffer);
for (var i = 0; i < length; i++) {
view.setFloat32(i * 4, float32Array[i], true);
}
var outputArray = new Uint8Array(length);
for (var j = 0; j < length; j++) {
outputArray[j] = view.getUint8(j * 4);
}
return outputArray;
Edit:
I just need to hold on to the binary representation just as in this answer.
I have a float32Array from decodeAudioData method, which I want to convert it to Uint8Array while preserving the float32 IEEE 754 audio data.
So far I tried,
var length = float32Array.length;
var emptyBuffer = new ArrayBuffer(length * 4);
var view = new DataView(emptyBuffer);
for (var i = 0; i < length; i++) {
view.setFloat32(i * 4, float32Array[i], true);
}
var outputArray = new Uint8Array(length);
for (var j = 0; j < length; j++) {
outputArray[j] = view.getUint8(j * 4);
}
return outputArray;
Edit:
I just need to hold on to the binary representation just as in this answer.
Share Improve this question edited Mar 18, 2018 at 21:04 starkm asked Mar 18, 2018 at 14:05 starkmstarkm 1,0291 gold badge12 silver badges23 bronze badges 14-
Why not just map a
Uint8Array
over the same buffer? Then you don't have to copy anything. – Pointy Commented Mar 18, 2018 at 14:09 - Also you did not really describe what the problem is with your code. What goes wrong? Are there errors reported? – Pointy Commented Mar 18, 2018 at 14:10
- 1 @Pointy Could you explain the mapping a little bit more? Is preserving IEEE 754 representation possible that way? – starkm Commented Mar 18, 2018 at 14:59
- 1 It would be very helpful if you could add to the question with specific input and output numeric values you want in the input and output arrays. – Pointy Commented Mar 18, 2018 at 17:41
- 2 I notice that you're using buffers for audio which means your values will be between [1,-1] in float. If you convert this directly to 8-bit type you will only get 2 or 3 numbers in this range (-1,0 and 1 depending on rounding and sign). Are you trying to convert the data to a file format storage such as 8-bit 16-bit audio? In that case you need to scale the numbers to the interval those formats represents. There is no way to preserve the actual numbers in a float representation as 8-bit numbers unless they happen to be [0,255] or signed [-128,127] integers. – user1693593 Commented Mar 18, 2018 at 21:18
2 Answers
Reset to default 4var output = new Uint8Array(float32Array.length);
for (var i = 0; i < float32Array.length; i++) {
var tmp = Math.max(-1, Math.min(1, float32Array[i]));
tmp = tmp < 0 ? (tmp * 0x8000) : (tmp * 0x7FFF);
tmp = tmp / 256;
output[i] = tmp + 128;
}
return output;
Anyone got doubt in mind can test this easily with Audacity's Import Raw Data feature.
- Download the sample raw data which I decoded from a video file using Web Audio Api's decodeAudioData method.
Convert the Float32Array that sample raw data is filled with to Uint8Array by using the method above (or use your own method e.g.
new Uint8Array(float32Array.buffer)
to hear the corrupted sizzling sound) and download the uint8 pcm file.forceDownload(new Blob([output], { type: 'application/octet-binary' }));
Encode your downloaded data in Audacity using File-> Import -> Raw Data... Encoding should be set to Unsigned 8-bit PCM and Sample Rate should be 16000 Hz since the original decoded audio file was in 16000 Hz.
It's not very clear what you're asking; or, rather, what it appears you're asking is a thing that makes no sense.
A Float32Array
instance is a view onto a buffer of "raw" byte values, like all typed arrays. Each element of the array represents 4 of those raw bytes. Extracting a value via a simple array lookup:
var n = float32array[1];
implicitly interprets those 4 bytes as an IEEE 32-bit floating point value, and then that value is converted to a standard JavaScript number. JavaScript numbers are always 64-bit IEEE floating point values.
Similarly, a Uint8Array
is a view onto a buffer, and each element gives the unsigned integer value of one byte. That is,
var n = uint8array[1];
accesses that element, interprets it as an unsigned one-byte integer, and converts that to a JavaScript number.
So: if you want to examine a list of 32-bit floating point values as the raw integer value of each byte, you can create a Uint8Array
that "shares" the same buffer as the Float32Array
:
var uintShared = new Uint8Array(float32array.buffer);
The number values you'd see from looking at the Uint8Array
values will not appear to have anything to do with the number values you get from looking at the Float32Array
elements, which is to be expected.
On the other hand, if you want to create a new Uint8Array
to hold the apparent values from the Float32Array
, you can just create a new array of the same length and copy each value:
var uintCopy = new Uint8Array(float32array.length);
for (let i = 0; i < float32array.length; ++i)
uintCopy[i] = float32array[i]; // deeply problematic; see below
Now that won't work too well, in general, because the numeric range of values in a Float32Array
is vastly greater than that of values in the Uint8Array
. For one thing, the 32-bit floating point values can be negative. What's more, even if you know that the floating point values are all integers in the range 0 to 255, you definitely will not get the same bit patterns in the Uint8Array
, for the simple reason that a 32-bit floating point number is just not the same as an 8-bit unsigned integer. To "preserve the IEEE-754 representation" makes no sense.
So that's the reality of the situation. If you were to explain why you think you want to somehow cram all 32 bits of a 32-bit IEEE float into an 8-bit unsigned integer, it would be possible to provide a more directly helpful answer.