2018-03-02 19:39:46 +00:00
|
|
|
class SampleBuffer {
|
|
|
|
buffer: Float32Array;
|
|
|
|
index: number;
|
|
|
|
|
|
|
|
constructor(buffer) {
|
|
|
|
this.buffer = buffer;
|
|
|
|
this.index = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-27 16:20:49 +00:00
|
|
|
abstract class Codec {
|
2018-03-02 19:39:46 +00:00
|
|
|
on_encoded_data: (Uint8Array) => void = ($) => {};
|
|
|
|
|
|
|
|
protected _sampleBuffer: SampleBuffer[] = [];
|
|
|
|
sampleRate: number = 120;
|
|
|
|
|
2018-02-27 16:20:49 +00:00
|
|
|
constructor(){}
|
|
|
|
|
|
|
|
abstract name() : string;
|
|
|
|
abstract initialise();
|
|
|
|
abstract deinitialise();
|
|
|
|
|
|
|
|
|
|
|
|
abstract decode(data: Uint8Array) : Float32Array | string;
|
2018-03-02 19:39:46 +00:00
|
|
|
|
|
|
|
protected abstract encode(data: Float32Array) : Uint8Array | string;
|
|
|
|
|
|
|
|
protected bufferedSamples(max: number = 0) : number {
|
|
|
|
let value = 0;
|
|
|
|
for(let i = 0; i < this._sampleBuffer.length && value < max; i++)
|
|
|
|
value += this._sampleBuffer[i].buffer.length - this._sampleBuffer[i].index;
|
|
|
|
console.log(value + " / " + max);
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
encodeSamples(array: Float32Array) : boolean | string {
|
|
|
|
console.log("encode");
|
|
|
|
this._sampleBuffer.push(new SampleBuffer(array));
|
|
|
|
|
|
|
|
while(this.bufferedSamples(this.sampleRate) >= this.sampleRate) {
|
|
|
|
let buffer = new Float32Array(this.sampleRate);
|
|
|
|
let index = 0;
|
|
|
|
while(index < this.sampleRate) {
|
|
|
|
let buf = this._sampleBuffer[0];
|
|
|
|
let len = Math.min(buf.buffer.length - buf.index, this.sampleRate - index);
|
|
|
|
buffer.set(buf.buffer.subarray(buf.index, buf.index + len));
|
|
|
|
index += len;
|
|
|
|
buf.index += len;
|
|
|
|
console.log(buf.index + " - " + buf.buffer.length);
|
|
|
|
if(buf.index == buf.buffer.length)
|
|
|
|
this._sampleBuffer.pop_front();
|
|
|
|
}
|
|
|
|
|
|
|
|
let result = this.encode(buffer);
|
|
|
|
if(result instanceof Uint8Array) this.on_encoded_data(result);
|
|
|
|
else return result;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2018-02-27 16:20:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
class OpusCodec extends Codec {
|
|
|
|
private nativeHandle: any;
|
|
|
|
private channelCount: number = 1;
|
|
|
|
|
|
|
|
private fn_newHandle: any;
|
|
|
|
private fn_decode: any;
|
2018-02-28 19:49:56 +00:00
|
|
|
private fn_encode: any;
|
2018-02-27 16:20:49 +00:00
|
|
|
|
|
|
|
constructor() {
|
|
|
|
super();
|
|
|
|
}
|
|
|
|
|
|
|
|
name(): string {
|
|
|
|
return "Opus";
|
|
|
|
}
|
|
|
|
|
|
|
|
initialise() {
|
2018-02-28 19:49:56 +00:00
|
|
|
this.fn_newHandle = Module.cwrap("codec_opus_createNativeHandle", "pointer", ["number"]);
|
2018-02-27 16:20:49 +00:00
|
|
|
this.fn_decode = Module.cwrap("codec_opus_decode", "number", ["pointer", "pointer", "number", "number"]); /* codec_opus_decode(handle, buffer, length, maxlength) */
|
2018-02-28 19:49:56 +00:00
|
|
|
this.fn_encode = Module.cwrap("codec_opus_encode", "number", ["pointer", "pointer", "number", "number"]);
|
2018-02-27 16:20:49 +00:00
|
|
|
|
2018-02-28 19:49:56 +00:00
|
|
|
this.nativeHandle = this.fn_newHandle(1);
|
2018-02-27 16:20:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
deinitialise() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
decode(data: Uint8Array): Float32Array | string {
|
|
|
|
let maxBytes = 4096;
|
|
|
|
let buffer = Module._malloc(maxBytes);
|
|
|
|
let heapBytes = new Uint8Array(Module.HEAPU8.buffer, buffer, maxBytes);
|
|
|
|
heapBytes.set(data);
|
|
|
|
let result = this.fn_decode(this.nativeHandle, heapBytes.byteOffset, data.byteLength, maxBytes);
|
|
|
|
if(result < 0) {
|
|
|
|
Module._free(buffer);
|
2018-02-28 19:49:56 +00:00
|
|
|
return "invalid result on decode (" + result + ")";
|
2018-02-27 16:20:49 +00:00
|
|
|
}
|
2018-03-02 19:39:46 +00:00
|
|
|
let buf = Module.HEAPF32.slice(heapBytes.byteOffset / 4, (heapBytes.byteOffset / 4) + (result * this.channelCount));
|
2018-02-27 16:20:49 +00:00
|
|
|
Module._free(buffer);
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2018-02-28 19:49:56 +00:00
|
|
|
encode(data: Float32Array): Uint8Array | string {
|
2018-03-02 19:39:46 +00:00
|
|
|
let maxBytes = data.byteLength;
|
2018-02-28 19:49:56 +00:00
|
|
|
let buffer = Module._malloc(maxBytes);
|
|
|
|
let heapBytes = new Uint8Array(Module.HEAPU8.buffer, buffer, maxBytes);
|
2018-03-02 19:39:46 +00:00
|
|
|
heapBytes.set(new Uint8Array(data.buffer));
|
|
|
|
let result = this.fn_encode(this.nativeHandle, heapBytes.byteOffset, data.length, maxBytes);
|
2018-02-28 19:49:56 +00:00
|
|
|
if(result < 0) {
|
|
|
|
Module._free(buffer);
|
|
|
|
return "invalid result on encode (" + result + ")";
|
2018-02-27 16:20:49 +00:00
|
|
|
}
|
2018-02-28 19:49:56 +00:00
|
|
|
let buf = Module.HEAP8.slice(heapBytes.byteOffset, heapBytes.byteOffset + result);
|
|
|
|
Module._free(buffer);
|
|
|
|
return Uint8Array.from(buf);
|
2018-02-27 16:20:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private _arrayToHeap(typedArray: any){
|
|
|
|
let numBytes = typedArray.length * typedArray.BYTES_PER_ELEMENT;
|
|
|
|
let ptr = Module._malloc(numBytes);
|
|
|
|
let heapBytes = new Uint8Array(Module.HEAPU8.buffer, ptr, numBytes);
|
|
|
|
heapBytes.set(new Uint8Array(typedArray.buffer));
|
|
|
|
return heapBytes;
|
|
|
|
}
|
|
|
|
}
|