将Float32Array转换为Int16Array。

8

我想要将一个Float32Array转换成一个Int16Array

这是我的代码(我没有提供data)。

  var data = ...; /*new Float32Array();*/
  var dataAsInt16Array = new Int16Array(data.length);
  for(var i=0; i<data.length; i++){
    dataAsInt16Array[i] = parseInt(data[i]*32767,10);
  }

我不确定自己是否做得正确,正在寻求一些指导。

6个回答

7
您可以直接从ArrayBuffer中进行操作。
var dataAsInt16Array = new Int16Array(data.buffer);

var f32 = new Float32Array(4);
f32[0] = 0.1, f32[1] = 0.2, f32[2] = 0.3, f32[3] = 0.4;
// [0.10000000149011612, 0.20000000298023224, 0.30000001192092896, 0.4000000059604645]

var i16 = new Int16Array(f32.buffer);
// [-13107, 15820, -13107, 15948, -26214, 16025, -13107, 16076]

// and back again
new Float32Array(i16.buffer);
// [0.10000000149011612, 0.20000000298023224, 0.30000001192092896, 0.4000000059604645]

我为这个答案投了赞成票,但后来意识到它是错误的。Float32Array的缓冲区是原始字节的数组。将原始的32位浮点字节视为整数是一件奇怪的事情。 - Sergey P. aka azure
@SergeyP.akaazure 这就是 OP 所问的。 - Paul S.
请注意,f32.buffer 可能包含比原始 Int16Array 使用的更多数据。适当的做法是使用 new Float32Array(i16.buffer, i16.byteOffset, i16.byteLength / Float32Array.BYTES_PER_ELEMENT);。 - heiner

6
如果你想转换原始的底层数据,你可以使用Paul S.在回答中描述的方法。但要注意,如果你处理的是Float32类型的32位IEEE 754表示形式,你将不会得到相同的数字。当使用像Int16这样的新视图时,你看到的是二进制表示,而不是原始数字。如果你想得到数字,你必须手动进行转换,只需修改你的代码为:
var data = ...; /*new Float32Array();*/
var len = data.length, i = 0;
var dataAsInt16Array = new Int16Array(len);

while(i < len)
  dataAsInt16Array[i] = convert(data[i++]);

function convert(n) {
   var v = n < 0 ? n * 32768 : n * 32767;       // convert in range [-32768, 32767]
   return Math.max(-32768, Math.min(32768, v)); // clamp
}

你能解释一下你的代码吗?在不丢失数据的情况下,保留IEEE 754表示是否可行?这种方式转换为无符号整数也是合法的吗?使用DataView的setFloat32 - getInt16方法可以实现相同的功能吗?@K3N - starkm
@starkm 的保留取决于数字,如果它是整数且在16位范围内,则可以,否则不可能。DV的get(U)Int16()也会做类似的事情,并处理字节顺序和内存对齐(由于这些原因它可能会稍微慢一些)。如果你将底层原始缓冲区复制为Int16,然后使用Float32视图从同一缓冲区读回这些值,那么你就应该没问题了(只要字节顺序相同)。 - user1693593
尝试使用DataViews做这个,但我卡住了 : )。如果您查看我的问题,将会非常有帮助,因为试图解释我正在尝试做什么是很痛苦的。@K3N - starkm

4
    var floatbuffer = audioProcEvent.inputBuffer.getChannelData(0);
    var int16Buffer = new Int16Array(floatbuffer.length);

    for (var i = 0, len = floatbuffer.length; i < len; i++) {
        if (floatbuffer[i] < 0) {
            int16Buffer[i] = 0x8000 * floatbuffer[i];
        } else {
            int16Buffer[i] = 0x7FFF * floatbuffer[i];
        }
    }

2

从 ECMAScript 2015 开始,TypedArray.from 方法可以将任何类型的数组(包括可迭代对象)转换成指定类型的数组。

因此,将 Float32Array 转换为 Uint8Array 现在变得非常简单:

const floatArray = new Float32Array()
const intArray = Int16Array.from(floatArray)

虽然有截断,但仍然可以使用。


2

这里是将Float32Array转换和缩放为Int16Array的答案,结合了robjtede和StuS的回答。在Float32Array中,范围为1到-1的缩放变为Int16Array中的32767和-32768:

最初的回答:

myF32Array=Float32Array.from([1,0.5,0.75,-0.5,-1])
myI16Array=Int16Array.from(myF32Array.map(x => (x>0 ? x*0x7FFF : x*0x8000)))
myNewF32Array=Float32Array.from(Float32Array.from(myI16Array).map(x=>x/0x8000))
console.log(myF32Array)
console.log(myI16Array)
console.log(myNewF32Array)

//output
> Float32Array [1, 0.5, 0.75, -0.5, -1]
> Int16Array [32767, 16383, 24575, -16384, -32768]
> Float32Array [0.999969482421875, 0.499969482421875, 0.749969482421875, -0.5, -1]

0

看起来你不仅想要将数据格式转换,而且还想处理原始数据并以不同的格式存储它。

Float32Array 转换成 Int16Array 的直接方法就是这么简单:

var a = new Int16Array(myFloat32Array);

针对数据处理,您可以采用您在问题中提供的方法。我不确定是否需要调用parseInt


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接