From 48f9f88a7c219ff20b27ee6b19d957f0561c075d Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 14 May 2024 14:06:55 +0100 Subject: [PATCH 1/3] Terminate playback worker on destroy Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/WorkerManager.ts | 8 ++++++++ src/audio/Playback.ts | 1 + 2 files changed, 9 insertions(+) diff --git a/src/WorkerManager.ts b/src/WorkerManager.ts index 2ad5191b246..c2149195cd4 100644 --- a/src/WorkerManager.ts +++ b/src/WorkerManager.ts @@ -43,4 +43,12 @@ export class WorkerManager { this.worker.postMessage({ seq, ...request }); return deferred.promise; } + + /** + * Terminate the worker. + * This will prevent any further messages from being sent to the worker and clean up its resources. + */ + public terminate(): void { + this.worker.terminate(); + } } diff --git a/src/audio/Playback.ts b/src/audio/Playback.ts index dc2619d6922..0844deeb7bf 100644 --- a/src/audio/Playback.ts +++ b/src/audio/Playback.ts @@ -145,6 +145,7 @@ export class Playback extends EventEmitter implements IDestroyable, PlaybackInte this.removeAllListeners(); this.clock.destroy(); this.waveformObservable.close(); + this.worker.terminate(); if (this.element) { URL.revokeObjectURL(this.element.src); this.element.remove(); From 82d73b9ded5097330fc603766c869a9bab8b7899 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 14 May 2024 14:09:49 +0100 Subject: [PATCH 2/3] Reuse single PlaybackWorker between all Playbacks Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/PlaybackEncoder.ts | 34 ++++++++++++++++++++++++++++++++++ src/WorkerManager.ts | 8 -------- src/audio/Playback.ts | 15 ++++----------- 3 files changed, 38 insertions(+), 19 deletions(-) create mode 100644 src/PlaybackEncoder.ts diff --git a/src/PlaybackEncoder.ts b/src/PlaybackEncoder.ts new file mode 100644 index 00000000000..f2d52cec66d --- /dev/null +++ b/src/PlaybackEncoder.ts @@ -0,0 +1,34 @@ +/* +Copyright 2024 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// @ts-ignore - `.ts` is needed here to make TS happy +import { Request, Response } from "./workers/playback.worker"; +import { WorkerManager } from "./WorkerManager"; +import playbackWorkerFactory from "./workers/blurhashWorkerFactory"; + +export class PlaybackEncoder { + private static internalInstance = new PlaybackEncoder(); + + public static get instance(): PlaybackEncoder { + return PlaybackEncoder.internalInstance; + } + + private readonly worker = new WorkerManager(playbackWorkerFactory()); + + public getPlaybackWaveform(input: Float32Array): Promise { + return this.worker.call({ data: Array.from(input) }).then((resp) => resp.waveform); + } +} diff --git a/src/WorkerManager.ts b/src/WorkerManager.ts index c2149195cd4..2ad5191b246 100644 --- a/src/WorkerManager.ts +++ b/src/WorkerManager.ts @@ -43,12 +43,4 @@ export class WorkerManager { this.worker.postMessage({ seq, ...request }); return deferred.promise; } - - /** - * Terminate the worker. - * This will prevent any further messages from being sent to the worker and clean up its resources. - */ - public terminate(): void { - this.worker.terminate(); - } } diff --git a/src/audio/Playback.ts b/src/audio/Playback.ts index 0844deeb7bf..957d5417326 100644 --- a/src/audio/Playback.ts +++ b/src/audio/Playback.ts @@ -19,17 +19,14 @@ import { SimpleObservable } from "matrix-widget-api"; import { logger } from "matrix-js-sdk/src/logger"; import { defer } from "matrix-js-sdk/src/utils"; -// @ts-ignore - `.ts` is needed here to make TS happy -import { Request, Response } from "../workers/playback.worker.ts"; import { UPDATE_EVENT } from "../stores/AsyncStore"; import { arrayFastResample } from "../utils/arrays"; import { IDestroyable } from "../utils/IDestroyable"; import { PlaybackClock } from "./PlaybackClock"; import { createAudioContext, decodeOgg } from "./compat"; import { clamp } from "../utils/numbers"; -import { WorkerManager } from "../WorkerManager"; import { DEFAULT_WAVEFORM, PLAYBACK_WAVEFORM_SAMPLES } from "./consts"; -import playbackWorkerFactory from "../workers/playbackWorkerFactory"; +import { PlaybackEncoder } from "../PlaybackEncoder"; export enum PlaybackState { Decoding = "decoding", @@ -64,7 +61,6 @@ export class Playback extends EventEmitter implements IDestroyable, PlaybackInte private waveformObservable = new SimpleObservable(); private readonly clock: PlaybackClock; private readonly fileSize: number; - private readonly worker = new WorkerManager(playbackWorkerFactory()); /** * Creates a new playback instance from a buffer. @@ -145,7 +141,6 @@ export class Playback extends EventEmitter implements IDestroyable, PlaybackInte this.removeAllListeners(); this.clock.destroy(); this.waveformObservable.close(); - this.worker.terminate(); if (this.element) { URL.revokeObjectURL(this.element.src); this.element.remove(); @@ -210,7 +205,9 @@ export class Playback extends EventEmitter implements IDestroyable, PlaybackInte // Update the waveform to the real waveform once we have channel data to use. We don't // exactly trust the user-provided waveform to be accurate... - this.resampledWaveform = await this.makePlaybackWaveform(this.audioBuf.getChannelData(0)); + this.resampledWaveform = await PlaybackEncoder.instance.getPlaybackWaveform( + this.audioBuf.getChannelData(0), + ); } this.waveformObservable.update(this.resampledWaveform); @@ -223,10 +220,6 @@ export class Playback extends EventEmitter implements IDestroyable, PlaybackInte this.emit(PlaybackState.Stopped); // signal that we're not decoding anymore } - private makePlaybackWaveform(input: Float32Array): Promise { - return this.worker.call({ data: Array.from(input) }).then((resp) => resp.waveform); - } - private onPlaybackEnd = async (): Promise => { await this.context.suspend(); this.emit(PlaybackState.Stopped); From a67670389630a81e54c8d5e55ee20299e65388f9 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 14 May 2024 15:01:03 +0100 Subject: [PATCH 3/3] ... Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/PlaybackEncoder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PlaybackEncoder.ts b/src/PlaybackEncoder.ts index f2d52cec66d..a08292e01fb 100644 --- a/src/PlaybackEncoder.ts +++ b/src/PlaybackEncoder.ts @@ -17,7 +17,7 @@ limitations under the License. // @ts-ignore - `.ts` is needed here to make TS happy import { Request, Response } from "./workers/playback.worker"; import { WorkerManager } from "./WorkerManager"; -import playbackWorkerFactory from "./workers/blurhashWorkerFactory"; +import playbackWorkerFactory from "./workers/playbackWorkerFactory"; export class PlaybackEncoder { private static internalInstance = new PlaybackEncoder();