arraybuffer

The ArrayBuffer is a data type that is used to represent a generic, fixed-length binary data buffer. You can’t directly manipulate the contents of an ArrayBuffer; instead, you create an ArrayBufferView object which represents the buffer in a specific format, and use that to read and write the contents of the buffer.
表示二进制数据的原始缓冲区,该缓冲区用于存储各种类型化数组的数据。 无法直接读取或写入 ArrayBuffer,但可根据需要将其传递到类型化数组或 DataView 对象 来解释原始缓冲区。

为什么会用到 arrayBuffer

有一个需求,获取到前端实时语音并通过 websocket 传到后端,具体的实现过程我后面会再更新一篇博客讲.这里简单说下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
let constraints = {
audio: true,
video: false
};
//这里获取到的是音频数据
navigator.mediaDevices
.getUserMedia(constraints)
//mediaStream是获取到的音频流
.then(mediaStream => {
//使用 audioContext 来处理获取到的音频流
this.audioContext = new (window.AudioContext ||
window.webkitAudioContext)();
let audioInput = this.audioContext.createMediaStreamSource(mediaStream);
//createScriptProcessor中可以填入缓冲区大小,不填则自动选择
//缓冲区大小为 2的指数次方
let recorder =
this.audioContext.createScriptProcessor();
//sampleRate为声音采样率
console.log(this.audioContext.sampleRate);
//onaudioprocess代表缓冲区满了以后触发的操作
recorder.onaudioprocess = this.recorderProcess;
audioInput.connect(recorder);
recorder.connect(this.audioContext.destination);
})


recorderProcess(e) {
//left 为获取到的单声道音频流
//类型为 float32Array
const left = e.inputBuffer.getChannelData(1);
this.sendMessage(this.changeRate(left));
},
//处理音频流
changeRate(e) {
//变更采样率
//浏览器无法手动设置采样率,所以要根据需求的采样率进行修改
//浏览器的的采样率是 44100,而我们需要的采样率是 16000
let t = e.length;
let sampleRate = 44100.0;
let outputSampleRate = 16000.0;
var s = 0,
o = sampleRate / outputSampleRate,
u = Math.ceil((t * outputSampleRate) / sampleRate),
a = new Float32Array(u);
for (let i = 0; i < u; i++) {
a[i] = e[Math.floor(s)];
s += o;
}
//float32Array 转化为 int16Array,因为后端需要的是 16 位深的pcm 数据
let b = new Int16Array(a.buffer);
console.log(b);
return b;
}

arraybuffer

原始缓冲区的创建

var buffer  = new ArrayBuffer(30);

buffer 实例拥有一个 byteLength 的属性,用于获取 buffer 的 size,一个只有 IE11+ 以及 ios6+ 支持的 slice 方法,用于对 buffer 长度进行截取操作。

ArrayBuffer slice(
unsigned long begin
unsigned long end Optional
);

可以测试这个 DEMO:

1
2
3
4
5
6
7
8
9
10
var buffer = new ArrayBuffer(12);
var x = new Int32Array(buffer);
x[1] = 1234;
var slice = buffer.slice(4);
var y = new Int32Array(slice);
console.log(x[1]);
console.log(y[0]);
x[1] = 6789;
console.log(x[1]);
console.log(y[0]);

数据化数组

类型化数组类型表示可编制索引和操纵的 ArrayBuffer 对象 的各种视图。 所有数组类型的长度均固定。

WX20191120-103621@2x

Int 就是整型,Uint 为无符号整形,Float 为浮点型,这些是 C 语言中的基本概念,我就不具体解释了。由于这些视图化结构都是大同小异,本文只对 Float32Array 类型作说明,读者可以举一反三。

Float32Array 跟 Array 是十分类似的,只不过他每一个元素都是都是一个 32位(4字节) 的浮点型数据。Float32Array 一旦创建其大小不能再修改。

我们可以直接创建一个 Float32Array:

1
2
3
4
5
var x = new Float32Array(2);
x[0] = 17;
console.log(x[0]); // 17
console.log(x[1]); // 0
console.log(x.length); // 2

需要有这么一个概念,他依然是一个数组,只不过该数组中的每个元素都是 Float 32 位的数据类型,再如:
1
2
3
4
var x = new Float32Array([17, -45.3]);
console.log(x[0]); // 17
console.log(x[1]); // -45.29999923706055
console.log(x.length); // 2

我们把一个数组的值直接赋给了 x 这个 Float32Array 对象,那么在储存之前会将它转换成一个 32位浮点数。

由于该类数组的每个元素都是同一类型,所以在堆栈模型中,他们全部会被压入到栈之中,因此数据化数组都是值类型,他并不是引用类型!这个要引起注意,从下面的例子中也可以反映出来:

1
2
3
4
5
6
7
var x = new Float32Array([17, -45.3]);
var y = new Float32Array(x);
console.log(x[0]); // 17
console.log(x[1]); //-45.29999923706055
console.log(x.length); // 2
x[0] = -2;
console.log(y[0]); // 17, y的值没变

将 x 的值复制给 y,修改 x[0], y[0] 并没有变化。

类型转换

直接从ArrayBuffer执行操作

1
2
3
4
5
6
7
8
9
10
11
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]

thank u !