diff --git a/Tone/core/context/AudioContext.ts b/Tone/core/context/AudioContext.ts index 1ef4e10fd..73d35d029 100644 --- a/Tone/core/context/AudioContext.ts +++ b/Tone/core/context/AudioContext.ts @@ -1,7 +1,11 @@ import { + IAudioWorkletNodeOptions, AudioContext as stdAudioContext, + AudioWorkletNode as stdAudioWorkletNode, OfflineAudioContext as stdOfflineAudioContext, } from "standardized-audio-context"; +import { assert } from "../util/Debug"; +import { isDefined } from "../util/TypeCheck"; /** * Create a new AudioContext @@ -75,3 +79,9 @@ export function setAudioContext(context: AnyAudioContext): void { theWindow.TONE_AUDIO_CONTEXT = globalContext; } } + +export function createAudioWorkletNode(context: AnyAudioContext, name: string, options?: Partial): AudioWorkletNode { + assert(isDefined(stdAudioWorkletNode), "This node only works in a secure context (https or localhost)"); + // @ts-ignore + return new stdAudioWorkletNode(context, name, options); +} diff --git a/Tone/core/context/Context.ts b/Tone/core/context/Context.ts index 92c1d1943..eaea7cfea 100644 --- a/Tone/core/context/Context.ts +++ b/Tone/core/context/Context.ts @@ -3,10 +3,10 @@ import { Seconds } from "../type/Units"; import { isAudioContext } from "../util/AdvancedTypeCheck"; import { optionsFromArguments } from "../util/Defaults"; import { Emitter } from "../util/Emitter"; -import { Omit } from "../util/Interface"; +import { noOp, Omit } from "../util/Interface"; import { Timeline } from "../util/Timeline"; -import { isString } from "../util/TypeCheck"; -import { AnyAudioContext, getAudioContext } from "./AudioContext"; +import { isDefined, isString } from "../util/TypeCheck"; +import { AnyAudioContext, createAudioWorkletNode, getAudioContext } from "./AudioContext"; import { closeContext, initializeContext } from "./ContextInitialization"; type Transport = import("../clock/Transport").Transport; @@ -257,6 +257,48 @@ export class Context extends Emitter<"statechange" | "tick"> implements BaseAudi this._destination = d; } + //-------------------------------------------- + // AUDIO WORKLET + //-------------------------------------------- + + /** + * Maps a module name to promise of the addModule method + */ + private _workletModules: Map> = new Map() + + /** + * Create an audio worklet node from a name and options. The module + * must first be loaded using [[addAudioWorkletModule]]. + */ + createAudioWorkletNode( + name: string, + options?: Partial + ): AudioWorkletNode { + return createAudioWorkletNode(this.rawContext, name, options); + } + + /** + * Add an AudioWorkletProcessor module + * @param url The url of the module + * @param name The name of the module + */ + async addAudioWorkletModule(url: string, name: string): Promise { + this.assert(isDefined(this.rawContext.audioWorklet), "AudioWorkletNode is only available in a secure context (https or localhost)"); + if (!this._workletModules.has(name)) { + this._workletModules.set(name, this.rawContext.audioWorklet.addModule(url)); + } + await this._workletModules.get(name); + } + + /** + * Returns a promise which resolves when all of the worklets have been loaded on this context + */ + protected async workletsAreReady(): Promise { + const promises: Promise[] = []; + this._workletModules.forEach(promise => promises.push(promise)); + await Promise.all(promises); + } + //--------------------------- // TICKER //--------------------------- diff --git a/Tone/core/context/OfflineContext.ts b/Tone/core/context/OfflineContext.ts index cde1b1dfe..c3ed7d45b 100644 --- a/Tone/core/context/OfflineContext.ts +++ b/Tone/core/context/OfflineContext.ts @@ -80,7 +80,8 @@ export class OfflineContext extends Context { /** * Render the output of the OfflineContext */ - render(): Promise { + async render(): Promise { + await this.workletsAreReady(); this._renderClock(); return this._context.startRendering(); }