Initial Commit
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
import {
|
||||
AUDIO_ENGINE_BROWSER_CODES,
|
||||
BrowserAudioError,
|
||||
} from "../error-codes/error-codes.js";
|
||||
import type { BrowserAdapterOptions } from "./browser-adapter.types.js";
|
||||
import { constructStandardizedContext } from "./standardized-audio-context-bridge.js";
|
||||
|
||||
export interface AdapterContextLike {
|
||||
readonly sampleRate: number;
|
||||
readonly audioWorklet?: unknown;
|
||||
readonly destination: unknown;
|
||||
resume?: () => Promise<void>;
|
||||
close?: () => Promise<void>;
|
||||
createGain(): unknown;
|
||||
createStereoPanner(): unknown;
|
||||
createDynamicsCompressor(): unknown;
|
||||
createAnalyser(): unknown;
|
||||
createBufferSource(): unknown;
|
||||
createScriptProcessor(
|
||||
bufferSize: number,
|
||||
numberOfInputChannels: number,
|
||||
numberOfOutputChannels: number
|
||||
): unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a `BaseAudioContext`-shaped value for the adapter. Uses the
|
||||
* caller-supplied `context` option verbatim when present, otherwise
|
||||
* dynamically imports `standardized-audio-context` and constructs a
|
||||
* realtime `AudioContext`. Resumes the context before returning.
|
||||
*
|
||||
* Throws `BrowserAudioError(CONTEXT_FAILED)` for every failure mode.
|
||||
*/
|
||||
export async function resolveContext(args: {
|
||||
readonly injected?: unknown;
|
||||
readonly latencyHint?: string;
|
||||
readonly sampleRate?: number;
|
||||
readonly signal?: AbortSignal;
|
||||
}): Promise<{
|
||||
readonly context: AdapterContextLike;
|
||||
readonly ownsContext: boolean;
|
||||
}> {
|
||||
if (args.signal?.aborted) {
|
||||
throw new BrowserAudioError(
|
||||
AUDIO_ENGINE_BROWSER_CODES.CONTEXT_FAILED,
|
||||
"audio-engine-browser.init-aborted",
|
||||
{ cause: args.signal.reason }
|
||||
);
|
||||
}
|
||||
const resolved =
|
||||
args.injected !== undefined
|
||||
? { ctx: args.injected as AdapterContextLike, ownsContext: false }
|
||||
: await constructStandardizedContext(args);
|
||||
const ctx = resolved.ctx;
|
||||
const ownsContext = resolved.ownsContext;
|
||||
try {
|
||||
await ctx.resume?.();
|
||||
} catch (cause) {
|
||||
throw new BrowserAudioError(
|
||||
AUDIO_ENGINE_BROWSER_CODES.CONTEXT_FAILED,
|
||||
"audio-engine-browser.context-resume-failed",
|
||||
{ cause }
|
||||
);
|
||||
}
|
||||
return { context: ctx, ownsContext };
|
||||
}
|
||||
|
||||
export function readyOptions(options: BrowserAdapterOptions): {
|
||||
readonly injected?: unknown;
|
||||
readonly latencyHint?: string;
|
||||
readonly sampleRate?: number;
|
||||
} {
|
||||
const out: {
|
||||
injected?: unknown;
|
||||
latencyHint?: string;
|
||||
sampleRate?: number;
|
||||
} = {};
|
||||
if (options.context !== undefined) out.injected = options.context;
|
||||
if (typeof options.latencyHint === "string")
|
||||
out.latencyHint = options.latencyHint;
|
||||
if (options.sampleRate !== undefined) out.sampleRate = options.sampleRate;
|
||||
return out;
|
||||
}
|
||||
|
||||
export function throwAlreadyDisposed(): never {
|
||||
throw new BrowserAudioError(
|
||||
AUDIO_ENGINE_BROWSER_CODES.ALREADY_DISPOSED,
|
||||
"audio-engine-browser.already-disposed"
|
||||
);
|
||||
}
|
||||
|
||||
export function throwNotInitialised(): never {
|
||||
throw new BrowserAudioError(
|
||||
AUDIO_ENGINE_BROWSER_CODES.CONTEXT_FAILED,
|
||||
"audio-engine-browser.not-initialised"
|
||||
);
|
||||
}
|
||||
|
||||
export function throwTrackNotFound(trackId: string): never {
|
||||
throw new BrowserAudioError(
|
||||
AUDIO_ENGINE_BROWSER_CODES.TRACK_NOT_FOUND,
|
||||
"audio-engine-browser.track-not-found",
|
||||
{ context: { trackId } }
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user