first commit
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
# soundfont-loader
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Lazy entry point for the SF2/SF3/DLS SoundFont pipeline. The loader is the **only** place in the package where `spessasynth_lib` is referenced — the import is a dynamic `import("spessasynth_lib")` so consumers who instantiate the plugin but never call `init()` pay zero bundle cost. The loader also accepts an injectable `moduleLoader` so tests can swap in a deterministic stub without touching the real upstream library.
|
||||
|
||||
The loader is responsible for three things: (1) lazy-resolving the optional upstream module and surfacing `SPESSASYNTH.MODULE_UNAVAILABLE` if it isn't installed; (2) fetching the SoundFont bytes from a URL or accepting them inline; (3) sniffing the RIFF magic to reject obviously-wrong inputs before handing them to `SoundBankLoader.fromArrayBuffer`. Parse failures from the upstream parser are caught and re-raised as `SPESSASYNTH.SOUNDFONT_PARSE_FAILED` with the underlying error on `cause` so log aggregation can correlate.
|
||||
|
||||
## Features
|
||||
|
||||
- Dynamic `import("spessasynth_lib")` — the only static reference in the codebase, ring-fenced inside `defaultModuleLoader`.
|
||||
- Injectable `moduleLoader` and `fetchImpl` for hermetic tests.
|
||||
- Magic-byte sniff (`RIFF…sfbk` or `RIFF…DLS `) — rejects non-SoundFont inputs with `UNSUPPORTED_FORMAT` before invoking the parser.
|
||||
- `AbortSignal` propagation to the fetch.
|
||||
- Returns parsed `SoundBankLike` + resolved `SpessaSynthModuleLike` + raw bytes so callers can construct the processor and (optionally) persist the bytes.
|
||||
- Strict typed error surface: `SOUNDFONT_LOAD_FAILED`, `UNSUPPORTED_FORMAT`, `SOUNDFONT_PARSE_FAILED`, `MODULE_UNAVAILABLE`.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { loadSoundFont } from "@suaveplan/audio-fx-plugin-spessasynth/loader";
|
||||
|
||||
const { bank, module } = await loadSoundFont({
|
||||
soundFontUrl: "/assets/gm.sf2",
|
||||
});
|
||||
const processor = new module.SpessaSynthProcessor(48_000);
|
||||
processor.soundBankManager.addSoundBank(bank, "main", 0);
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { loadSoundFont } from "@suaveplan/audio-fx-plugin-spessasynth/loader";
|
||||
|
||||
const ac = new AbortController();
|
||||
setTimeout(() => ac.abort(), 5_000);
|
||||
|
||||
const { bank, bytes, module } = await loadSoundFont({
|
||||
soundFontUrl: "https://cdn.example.com/orchestra.sf3",
|
||||
signal: ac.signal,
|
||||
fetchImpl: customFetcher,
|
||||
moduleLoader: async () => upstreamStubModule,
|
||||
});
|
||||
|
||||
// Persist bytes for offline reuse.
|
||||
await driveStore.put("orchestra.sf3", bytes);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `loadSoundFont(opts: LoadSoundFontOptions): Promise<LoadedSoundFont>`.
|
||||
- `LoadSoundFontOptions = { soundFontUrl?, soundFontBytes?, signal?, fetchImpl?, moduleLoader? }`. Exactly one of `soundFontUrl` / `soundFontBytes` is required.
|
||||
- `LoadedSoundFont = { bank, module, bytes }`.
|
||||
- `SpessaSynthModuleLike`, `SpessaSynthProcessorLike`, `SoundBankLike` — structural views of the upstream surface used elsewhere in the package.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The structural module / processor / bank types are declared inside the package rather than imported from `spessasynth_lib` so the optional dependency does not leak into the static type graph — the only place the upstream is named is inside `defaultModuleLoader`'s dynamic `import()`. The magic sniff is intentionally cheap (12 bytes) and runs before the parser, so consumers that hand the loader random Uint8Arrays (e.g. a corrupt cache entry) get a fast, specific error rather than a deep stack from the parser. The `ArrayBuffer` is sliced from the supplied `Uint8Array.buffer` using the `byteOffset`/`byteLength` window so callers can pass a `Uint8Array` view of a larger buffer without leaking the surrounding bytes into the parser.
|
||||
Reference in New Issue
Block a user