Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -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 } }
);
}