UNPKG

echogarden

Version:

An easy-to-use speech toolset. Includes tools for synthesis, recognition, alignment, speech translation, language detection, source separation and more.

567 lines (446 loc) 12.7 kB
import { decodeUtf8, encodeUtf8 } from "../encodings/Utf8.js" import { TypedArray } from "../typings/TypedArray.js" import { concatUint8Arrays } from "./Utilities.js" export class WasmMemoryManager { wasmModule: any private wasmAlloc: WasmAllocMethod private wasmFree: WasmFreeMethod private allocatedReferences = new Set<WasmRef>() constructor(wasmModule: any, options?: WasmMemoryManagerOptions) { options = options ?? {} this.wasmModule = wasmModule if (options.wasmAlloc) { this.wasmAlloc = options.wasmAlloc } else { if (!wasmModule._malloc) { throw new Error(`Couldn't find a '_malloc' function in the module and no custom 'wasmAlloc' was provided in the options`) } this.wasmAlloc = wasmModule._malloc } if (options.wasmFree) { this.wasmFree = options.wasmFree } else { if (!wasmModule._free) { throw new Error(`Couldn't find a '_malloc' function in the module and no custom 'wasmFree' was provided in the options`) } this.wasmFree = wasmModule._free } } allocInt8() { const address = this.alloc(1) return this.wrapInt8(address).clear() } wrapInt8(address: number) { const ref = new Int8Ref(address, this) this.allocatedReferences.add(ref) return ref } allocUint8() { const address = this.alloc(1) return this.wrapUint8(address).clear() } wrapUint8(address: number) { const ref = new Uint8Ref(address, this) this.allocatedReferences.add(ref) return ref } allocInt16() { const address = this.alloc(2) return this.wrapInt16(address).clear() } wrapInt16(address: number) { const ref = new Int16Ref(address, this) this.allocatedReferences.add(ref) return ref } allocUint16() { const address = this.alloc(2) return this.wrapUint16(address).clear() } wrapUint16(address: number) { const ref = new Uint16Ref(address, this) this.allocatedReferences.add(ref) return ref } allocInt32() { const address = this.alloc(4) return this.wrapInt32(address).clear() } wrapInt32(address: number) { const ref = new Int32Ref(address, this) this.allocatedReferences.add(ref) return ref } allocUint32() { const address = this.alloc(4) return this.wrapUint32(address).clear() } wrapUint32(address: number) { const ref = new Uint32Ref(address, this) this.allocatedReferences.add(ref) return ref } allocPointer() { const address = this.alloc(4) return this.wrapPointer(address).clear() } wrapPointer(address: number) { const ref = new PointerRef(address, this) this.allocatedReferences.add(ref) return ref } allocFloat32() { const address = this.alloc(4) return this.wrapFloat64(address).clear() } wrapFloat32(address: number) { const ref = new Float32Ref(address, this) this.allocatedReferences.add(ref) return ref } allocFloat64() { const address = this.alloc(8) return this.wrapFloat64(address).clear() } wrapFloat64(address: number) { const ref = new Float64Ref(address, this) this.allocatedReferences.add(ref) return ref } // Allocate or wrap arrays allocInt8Array(length: number) { const address = this.alloc(length << 0) return this.wrapInt8Array(address, length).clear() } wrapInt8Array(address: number, length: number) { const ref = new Int8ArrayRef(address, length, this) this.allocatedReferences.add(ref) return ref } allocUint8Array(length: number) { const address = this.alloc(length << 0) return this.wrapUint8Array(address, length).clear() } wrapUint8Array(address: number, length: number) { const ref = new Uint8ArrayRef(address, length, this) this.allocatedReferences.add(ref) return ref } allocInt16Array(length: number) { const address = this.alloc(length << 1) return this.wrapInt16Array(address, length).clear() } wrapInt16Array(address: number, length: number) { const ref = new Int16ArrayRef(address, length, this) this.allocatedReferences.add(ref) return ref } allocUint16Array(length: number) { const address = this.alloc(length << 1) return this.wrapUint16Array(address, length).clear() } wrapUint16Array(address: number, length: number) { const ref = new Uint16ArrayRef(address, length, this) this.allocatedReferences.add(ref) return ref } allocInt32Array(length: number) { const address = this.alloc(length << 2) return this.wrapInt32Array(address, length).clear() } wrapInt32Array(address: number, length: number) { const ref = new Int32ArrayRef(address, length, this) this.allocatedReferences.add(ref) return ref } allocUint32Array(length: number) { const address = this.alloc(length << 2) return this.wrapUint32Array(address, length).clear() } wrapUint32Array(address: number, length: number) { const ref = new Uint32ArrayRef(address, length, this) this.allocatedReferences.add(ref) return ref } allocFloat32Array(length: number) { const address = this.alloc(length << 2) return this.wrapFloat32Array(address, length).clear() } wrapFloat32Array(address: number, length: number) { const ref = new Float32ArrayRef(address, length, this) this.allocatedReferences.add(ref) return ref } allocFloat64Array(length: number) { const address = this.alloc(length << 3) return this.wrapFloat64Array(address, length).clear() } wrapFloat64Array(address: number, length: number) { const ref = new Float64ArrayRef(address, length, this) this.allocatedReferences.add(ref) return ref } allocNullTerminatedUtf8String(str: string) { const strBuffer = concatUint8Arrays([encodeUtf8(str), new Uint8Array(1)]) const ref = this.allocUint8Array(strBuffer.length) ref.view.set(strBuffer) return ref } wrapNullTerminatedUtf8String(address: number) { const ref = new NullTerminatedUtf8StringRef(address, this) this.allocatedReferences.add(ref) return ref } private alloc(size: number) { const ptr = this.wasmAlloc(size) return ptr } free(wasmReference: WasmRef) { if (wasmReference.isFreed) { return } this.wasmFree(wasmReference.address) this.allocatedReferences.delete(wasmReference) wasmReference.clearAddress() } freeAll() { for (const wasmReference of this.allocatedReferences) { this.free(wasmReference) } } detach<T extends WasmRef>(wasmReference: T) { this.allocatedReferences.delete(wasmReference) return wasmReference } } abstract class ValueRef<T extends number | string> { protected ptr: number private readonly manager: WasmMemoryManager protected get module() { return this.manager.wasmModule } constructor(ptr: number, manager: WasmMemoryManager) { this.ptr = ptr this.manager = manager } get value(): T { this.assertNotFreed() return this.getValue() } set value(newValue: T) { this.assertNotFreed() this.setValue(newValue) } abstract getValue(): T abstract setValue(newValue: T): void get address() { this.assertNotFreed() return this.ptr } clear() { this.assertNotFreed() if (typeof this.value == 'number') { this.value = 0 as any } else if (typeof this.value == 'string') { throw new Error('Unimplemented') } return this } free() { this.manager.free(this as any) } detach() { return this.manager.detach(this as any) as this } clearAddress() { this.ptr = 0 } get isFreed() { return this.ptr == 0 } protected assertNotFreed() { if (this.isFreed) { throw new Error('Attempt to read a freed WASM value reference.') } } } export class Int8Ref extends ValueRef<number> { getValue() { return this.module.HEAP8[this.ptr >>> 0] as number } setValue(newValue: number) { this.module.HEAP8[this.ptr >>> 0] = newValue } } export class Uint8Ref extends ValueRef<number> { getValue() { return this.module.HEAPU8[this.ptr >>> 0] as number } setValue(newValue: number) { this.module.HEAPU8[this.ptr >>> 0] = newValue } } export class Int16Ref extends ValueRef<number> { getValue() { return this.module.HEAP16[this.ptr >>> 1] as number } setValue(newValue: number) { this.module.HEAP16[this.ptr >>> 1] = newValue } } export class Uint16Ref extends ValueRef<number> { getValue() { return this.module.HEAPU16[this.ptr >>> 1] as number } setValue(newValue: number) { this.module.HEAPU16[this.ptr >>> 1] = newValue } } export class Int32Ref extends ValueRef<number> { getValue() { return this.module.HEAP32[this.ptr >>> 2] as number } setValue(newValue: number) { this.module.HEAP32[this.ptr >>> 2] = newValue } } export class Uint32Ref extends ValueRef<number> { getValue() { return this.module.HEAPU32[this.ptr >>> 2] as number } setValue(newValue: number) { this.module.HEAPU32[this.ptr >>> 2] = newValue } } export class PointerRef extends Uint32Ref { } export class Float32Ref extends ValueRef<number> { getValue() { return this.module.HEAPF32[this.ptr >>> 2] as number } setValue(newValue: number) { this.module.HEAPF32[this.ptr >>> 2] = newValue } } export class Float64Ref extends ValueRef<number> { getValue() { return this.module.HEAPF64[this.ptr >>> 3] as number } setValue(newValue: number) { this.module.HEAPF64[this.ptr >>> 3] = newValue } } export class NullTerminatedUtf8StringRef extends ValueRef<string> { getValue() { const ptr = this.ptr >>> 0 const heapU8 = this.module.HEAPU8 const endByteOffset = heapU8.subarray(ptr).indexOf(0) const strBytes = heapU8.subarray(ptr, ptr + endByteOffset) const str = decodeUtf8(strBytes) return str } setValue(newValue: string) { throw new Error('Unimplemented') } } abstract class TypedArrayRef<T extends TypedArray> { protected ptr: number readonly length: number private readonly manager: WasmMemoryManager get module() { return this.manager.wasmModule } constructor(ptr: number, length: number, manager: WasmMemoryManager) { this.ptr = ptr this.length = length this.manager = manager } get view() { this.assertNotFreed() return this.getView() } protected abstract getView(): T slice(start?: number, end?: number) { return this.view.slice(start, end) } get address() { this.assertNotFreed() return this.ptr } clear() { this.view.fill(0) return this } free() { this.manager.free(this) } clearAddress() { this.ptr = 0 } detach() { return this.manager.detach(this) } get isFreed() { return this.ptr == 0 } protected assertNotFreed() { if (this.isFreed) { throw new Error('Attempt to read a freed WASM typed array reference.') } } } export class Int8ArrayRef extends TypedArrayRef<Int8Array> { getView() { const startIndex = this.ptr >>> 0 return this.module.HEAP8.subarray(startIndex, startIndex + this.length) as Int8Array } } export class Uint8ArrayRef extends TypedArrayRef<Uint8Array> { getView() { const startIndex = this.ptr >>> 0 return this.module.HEAPU8.subarray(startIndex, startIndex + this.length) as Uint8Array } readAsNullTerminatedUtf8String(): string { let strBytes = this.view const indexOfFirstZero = strBytes.indexOf(0) if (indexOfFirstZero >= 0) { strBytes = strBytes.subarray(0, indexOfFirstZero) } const str = decodeUtf8(strBytes) return str } } export class Int16ArrayRef extends TypedArrayRef<Int16Array> { getView() { const startIndex = this.ptr >>> 1 return this.module.HEAP16.subarray(startIndex, startIndex + this.length) as Int16Array } } export class Uint16ArrayRef extends TypedArrayRef<Uint16Array> { getView() { const startIndex = this.ptr >>> 1 return this.module.HEAPU16.subarray(startIndex, startIndex + this.length) as Uint16Array } } export class Int32ArrayRef extends TypedArrayRef<Int32Array> { getView() { const startIndex = this.ptr >>> 2 return this.module.HEAP32.subarray(startIndex, startIndex + this.length) as Int32Array } } export class Uint32ArrayRef extends TypedArrayRef<Uint32Array> { getView() { const startIndex = this.ptr >>> 2 return this.module.HEAPU32.subarray(startIndex, startIndex + this.length) as Uint32Array } } export class Float32ArrayRef extends TypedArrayRef<Float32Array> { getView() { const startIndex = this.ptr >>> 2 return this.module.HEAPF32.subarray(startIndex, startIndex + this.length) as Float32Array } } export class Float64ArrayRef extends TypedArrayRef<Float64Array> { getView() { const startIndex = this.ptr >>> 3 return this.module.HEAPF64.subarray(startIndex, startIndex + this.length) as Float64Array } } export type WasmRef = ValueRef<number> | ValueRef<string> | TypedArrayRef<TypedArray> export interface WasmMemoryManagerOptions { wasmAlloc?: WasmAllocMethod wasmFree?: WasmFreeMethod } export type WasmAllocMethod = (size: number) => number export type WasmFreeMethod = (address: number) => void