Skip to content

Commit

Permalink
Merge pull request #49 from Picovoice/wasm-detach
Browse files Browse the repository at this point in the history
Handle resampler wasm memory detach
  • Loading branch information
laves authored Jul 18, 2023
2 parents 0cc38cb + 40d7c98 commit 24e0ce8
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 29 deletions.
2 changes: 1 addition & 1 deletion demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"author": "Picovoice Inc",
"license": "Apache-2.0",
"dependencies": {
"@picovoice/web-voice-processor": "^4.0.6",
"@picovoice/web-voice-processor": "^4.0.7",
"http-server": "^14.0.0",
"wavefile": "^11.0.0"
}
Expand Down
22 changes: 18 additions & 4 deletions demo/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,19 @@
# yarn lockfile v1


"@picovoice/web-voice-processor@^4.0.6":
version "4.0.6"
resolved "https://registry.yarnpkg.com/@picovoice/web-voice-processor/-/web-voice-processor-4.0.6.tgz#4769283b82f64d3625794f7290d47c6d477a3f41"
integrity sha512-Ykfy6hrWFpOklfeN7rSJb5CGim8wDu7J+l8imRYyQxWHWVV1Wu5S8FW69zkJmwiDG2Wx+M2+h0SCMS+hNM5qow==
"@picovoice/web-utils@=1.3.1":
version "1.3.1"
resolved "https://registry.yarnpkg.com/@picovoice/web-utils/-/web-utils-1.3.1.tgz#d417e98604a650b54a8e03669015ecf98c2383ec"
integrity sha512-jcDqdULtTm+yJrnHDjg64hARup+Z4wNkYuXHNx6EM8+qZkweBq9UA6XJrHAlUkPnlkso4JWjaIKhz3x8vZcd3g==
dependencies:
commander "^9.2.0"

"@picovoice/web-voice-processor@^4.0.7":
version "4.0.7"
resolved "https://registry.yarnpkg.com/@picovoice/web-voice-processor/-/web-voice-processor-4.0.7.tgz#5df76c3b283a4c90ee53865cdd5dab0099acb823"
integrity sha512-LtZDNrtezi7B/1CWeda4YE7M+SAKN7ALvGf+j357TmmJRqnMnt21urAiZ90zSMUbFCoYqKIeq8rMGU7RkKaeEw==
dependencies:
"@picovoice/web-utils" "=1.3.1"

ansi-styles@^4.1.0:
version "4.3.0"
Expand Down Expand Up @@ -56,6 +65,11 @@ color-name@~1.1.4:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==

commander@^9.2.0:
version "9.5.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30"
integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==

corser@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/corser/-/corser-2.0.1.tgz#8eda252ecaab5840dcd975ceb90d9370c819ff87"
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@picovoice/web-voice-processor",
"version": "4.0.6",
"version": "4.0.7",
"description": "Real-time audio processing for voice, in web browsers",
"entry": "src/index.ts",
"module": "dist/esm/index.js",
Expand Down
75 changes: 52 additions & 23 deletions src/resampler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ class Resampler {
private static _wasm: string;
public static _version: string;

private _isWasmMemoryDetached: boolean = false;

private constructor(handleWasm: ResamplerWasmOutput) {
Resampler._version = handleWasm.version;

Expand Down Expand Up @@ -244,6 +246,10 @@ class Resampler {
inputFrame: Int16Array | Float32Array,
outputBuffer: Int16Array,
): number {
if (this._isWasmMemoryDetached) {
return 0;
}

if (inputFrame.length > this._inputBufferLength) {
throw new Error(`InputFrame length '${inputFrame.length}' must be smaller than ${this._inputBufferLength}.`);
}
Expand All @@ -263,24 +269,29 @@ class Resampler {
throw new Error(`Invalid inputFrame type: ${typeof inputFrame}. Expected Float32Array or Int16Array.`);
}

this._memoryBuffer.set(
inputBuffer,
this._inputBufferAddress / Int16Array.BYTES_PER_ELEMENT,
);
try {
this._memoryBuffer.set(
inputBuffer,
this._inputBufferAddress / Int16Array.BYTES_PER_ELEMENT,
);

const processedSamples = this._pvResamplerProcess(
this._objectAddress,
this._inputBufferAddress,
inputFrame.length,
this._outputBufferAddress,
);
for (let i = 0; i < processedSamples; i++) {
outputBuffer[i] = this._memoryBufferView.getInt16(
this._outputBufferAddress + i * Int16Array.BYTES_PER_ELEMENT,
true,
const processedSamples = this._pvResamplerProcess(
this._objectAddress,
this._inputBufferAddress,
inputFrame.length,
this._outputBufferAddress,
);
for (let i = 0; i < processedSamples; i++) {
outputBuffer[i] = this._memoryBufferView.getInt16(
this._outputBufferAddress + i * Int16Array.BYTES_PER_ELEMENT,
true,
);
}
return processedSamples;
} catch (error: any) {
this._errorHandler();
throw error;
}
return processedSamples;
}

public reset(): void {
Expand All @@ -305,17 +316,35 @@ class Resampler {
}

public getNumRequiredInputSamples(numSample: number): number {
return this._pvResamplerConvertNumSamplesToInputSampleRate(
this._objectAddress,
numSample,
);
try {
return this._pvResamplerConvertNumSamplesToInputSampleRate(
this._objectAddress,
numSample,
);
} catch (error: any) {
this._errorHandler();
throw error;
}
}

public getNumRequiredOutputSamples(numSample: number): number {
return this._pvResamplerConvertNumSamplesToOutputSampleRate(
this._objectAddress,
numSample,
);
try {
return this._pvResamplerConvertNumSamplesToOutputSampleRate(
this._objectAddress,
numSample,
);
} catch (error: any) {
this._errorHandler();
throw error;
}
}

private _errorHandler(): void {
if (this._memoryBuffer.length === 0) {
this._isWasmMemoryDetached = true;
this.release();
throw new Error("Invalid memory state: browser might have cleaned resources automatically. Re-initialize Resampler.");
}
}
}

Expand Down
23 changes: 23 additions & 0 deletions src/resampler_worker_handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import Resampler from './resampler';

let accumulator: BufferAccumulator | null = null;
let resampler: Resampler | null = null;
let isResamplerDetached = false;
let initParams: any = {};

class BufferAccumulator {
private readonly _frameLength: number;
Expand Down Expand Up @@ -80,6 +82,13 @@ onmessage = async function (event: MessageEvent<ResamplerWorkerRequest>): Promis
event.data.frameLength,
);

initParams = {
inputSampleRate: event.data.inputSampleRate,
outputSampleRate: event.data.outputSampleRate,
filterOrder: event.data.filterOrder,
frameLength: event.data.frameLength,
};

accumulator = new BufferAccumulator(
resampler.frameLength,
resampler.inputBufferLength);
Expand All @@ -96,6 +105,15 @@ onmessage = async function (event: MessageEvent<ResamplerWorkerRequest>): Promis
}
break;
case 'process':
if (isResamplerDetached) {
isResamplerDetached = false;
resampler = await Resampler.create(
initParams.inputSampleRate,
initParams.outputSampleRate,
initParams.filterOrder,
initParams.frameLength,
);
}
if (resampler === null) {
self.postMessage({
command: 'error',
Expand All @@ -107,6 +125,11 @@ onmessage = async function (event: MessageEvent<ResamplerWorkerRequest>): Promis
const {inputFrame} = event.data;
accumulator?.process(inputFrame);
} catch (e: any) {
if (e.message.includes('Invalid memory state')) {
resampler.release();
resampler = null;
isResamplerDetached = true;
}
self.postMessage({
command: 'error',
message: e.message,
Expand Down

0 comments on commit 24e0ce8

Please sign in to comment.