191 lines
6.5 KiB
TypeScript
191 lines
6.5 KiB
TypeScript
/// <reference path="BasicCodec.ts"/>
|
|
|
|
enum CodecWorkerType {
|
|
WORKER_OPUS
|
|
}
|
|
|
|
class CodecWrapper extends BasicCodec {
|
|
private _worker: Worker;
|
|
private _workerListener: {token: string, resolve: (data: any) => void}[] = [];
|
|
private _workerCallbackToken = "callback_token";
|
|
private _workerTokeIndex: number = 0;
|
|
type: CodecWorkerType;
|
|
|
|
private _initialized: boolean = false;
|
|
private _workerCallbackResolve: () => any;
|
|
private _workerCallbackReject: ($: any) => any;
|
|
|
|
private _initializePromise: Promise<Boolean>;
|
|
name(): string {
|
|
return "Worker for " + CodecWorkerType[this.type] + " Channels " + this.channelCount;
|
|
}
|
|
|
|
initialise() : Promise<Boolean> {
|
|
if(this._initializePromise) return this._initializePromise;
|
|
console.log("INIT!");
|
|
return this._initializePromise = this.spawnWorker().then(() => new Promise<Boolean>((resolve, reject) => {
|
|
const token = this.generateToken();
|
|
this.sendWorkerMessage({
|
|
command: "initialise",
|
|
type: this.type,
|
|
channelCount: this.channelCount,
|
|
token: token
|
|
});
|
|
|
|
this._workerListener.push({
|
|
token: token,
|
|
resolve: data => {
|
|
console.log("Init result: %o", data);
|
|
this._initialized = data["success"] == true;
|
|
if(data["success"] == true)
|
|
resolve();
|
|
else
|
|
reject(data.message);
|
|
}
|
|
})
|
|
}));
|
|
}
|
|
|
|
initialized() : boolean {
|
|
return this._initialized;
|
|
}
|
|
|
|
deinitialise() {
|
|
this.sendWorkerMessage({
|
|
command: "deinitialise"
|
|
});
|
|
}
|
|
|
|
decode(data: Uint8Array): Promise<AudioBuffer> {
|
|
let token = this.generateToken();
|
|
let result = new Promise<AudioBuffer>((resolve, reject) => {
|
|
this._workerListener.push(
|
|
{
|
|
token: token,
|
|
resolve: (data) => {
|
|
if(data.success) {
|
|
let array = new Float32Array(data.dataLength);
|
|
for(let index = 0; index < array.length; index++)
|
|
array[index] = data.data[index];
|
|
|
|
let audioBuf = this._audioContext.createBuffer(this.channelCount, array.length / this.channelCount, this._codecSampleRate);
|
|
for (let channel = 0; channel < this.channelCount; channel++)
|
|
for (let offset = 0; offset < audioBuf.length; offset++)
|
|
audioBuf.getChannelData(channel)[offset] = array[channel * audioBuf.length + offset];
|
|
resolve(audioBuf);
|
|
} else {
|
|
reject(data.message);
|
|
}
|
|
}
|
|
}
|
|
);
|
|
});
|
|
this.sendWorkerMessage({
|
|
command: "decodeSamples",
|
|
token: token,
|
|
data: data,
|
|
dataLength: data.length
|
|
});
|
|
return result;
|
|
}
|
|
|
|
encode(data: AudioBuffer) : Promise<Uint8Array> {
|
|
console.log(data);
|
|
let token = this.generateToken();
|
|
let result = new Promise<Uint8Array>((resolve, reject) => {
|
|
this._workerListener.push(
|
|
{
|
|
token: token,
|
|
resolve: (data) => {
|
|
if(data.success) {
|
|
let array = new Uint8Array(data.dataLength);
|
|
for(let index = 0; index < array.length; index++)
|
|
array[index] = data.data[index];
|
|
resolve(array);
|
|
} else {
|
|
reject(data.message);
|
|
}
|
|
}
|
|
}
|
|
);
|
|
});
|
|
|
|
let buffer = new Float32Array(this.channelCount * data.length);
|
|
for (let offset = 0; offset < data.length; offset++) {
|
|
for (let channel = 0; channel < this.channelCount; channel++)
|
|
buffer[offset * this.channelCount + channel] = data.getChannelData(channel)[offset];
|
|
}
|
|
//FIXME test if this is right!
|
|
|
|
this.sendWorkerMessage({
|
|
command: "encodeSamples",
|
|
token: token,
|
|
data: buffer,
|
|
dataLength: buffer.length
|
|
});
|
|
return result;
|
|
}
|
|
|
|
reset() : boolean {
|
|
this.sendWorkerMessage({
|
|
command: "reset"
|
|
});
|
|
return true;
|
|
}
|
|
|
|
constructor(type: CodecWorkerType, channelCount: number) {
|
|
super(48000);
|
|
this.type = type;
|
|
this.channelCount = channelCount;
|
|
}
|
|
|
|
private generateToken() {
|
|
return this._workerTokeIndex++ + "_token";
|
|
}
|
|
|
|
private sendWorkerMessage(message: any, transfare?: any[]) {
|
|
this._worker.postMessage(JSON.stringify(message), transfare);
|
|
}
|
|
|
|
private onWorkerMessage(message: any) {
|
|
if(!message["token"]) {
|
|
console.error("Invalid worker token!");
|
|
return;
|
|
}
|
|
|
|
if(message["token"] == this._workerCallbackToken) {
|
|
if(message["type"] == "loaded") {
|
|
console.log("Got loaded result: %o", message);
|
|
if(message["success"]) {
|
|
if(this._workerCallbackResolve)
|
|
this._workerCallbackResolve();
|
|
} else {
|
|
if(this._workerCallbackReject)
|
|
this._workerCallbackReject(message["message"]);
|
|
}
|
|
console.log("Worker initialized!");
|
|
}
|
|
console.log("Callback data!");
|
|
return;
|
|
}
|
|
|
|
for(let entry of this._workerListener) {
|
|
if(entry.token == message["token"]) {
|
|
entry.resolve(message);
|
|
this._workerListener.remove(entry);
|
|
return;
|
|
}
|
|
}
|
|
|
|
console.error("Could not find worker token entry! (" + message["token"] + ")");
|
|
}
|
|
|
|
private spawnWorker() : Promise<Boolean> {
|
|
return new Promise<Boolean>((resolve, reject) => {
|
|
this._workerCallbackReject = reject;
|
|
this._workerCallbackResolve = resolve;
|
|
this._worker = new Worker(settings.static("worker_directory", "js/workers/") + "WorkerCodec.js");
|
|
this._worker.onmessage = event => this.onWorkerMessage(JSON.parse(event.data));
|
|
});
|
|
}
|
|
} |