# @suaveplan/audio-ir-core - Foundation contract for the @suaveplan audio-ir family: ImpulseResponse / IrManifestEntry / IrManifest Zod schemas, in-memory IrRegistry, AUDIO_IR.* error-code registry, and OpenTelemetry surface. Universal; zero @suaveplan/audio-* runtime deps. Foundation contract for the `@suaveplan/audio-ir-*` family. Universal; zero `@suaveplan/audio-*` runtime deps. Every sibling package (`audio-ir-loader`, `audio-ir-{mic,speaker,room}-plugin-`) consumes this package. ## Documentation tree - [error-codes](./src/error-codes/error-codes.md) — Defines the closed registry of every error code the `@suaveplan/audio-ir-core` runtime is allowed to throw, plus the `A… - [error-codes](./src/error-codes/error-codes.md) — Defines the closed registry of every error code the `@suaveplan/audio-ir-core` runtime is allowed to throw, plus the `A… - [otel](./src/otel/otel.md) — Owns every span, counter, and observable gauge the registry emits. The foundation is the layer that knows about the reg… - [otel](./src/otel/otel.md) — Owns every span, counter, and observable gauge the registry emits. The foundation is the layer that knows about the reg… - [registrar](./src/registrar/registrar.md) — `MutableUriPluginRegistrar` is the canonical collector interface every `audio-ir-{family}-plugin-` content packag… - [registrar](./src/registrar/registrar.md) — `MutableUriPluginRegistrar` is the canonical collector interface every `audio-ir-{family}-plugin-` content packag… - [registry](./src/registry/registry.md) — In-memory `Map` registry — the central seam between content packages (which register entries)… - [register-manifest](./src/registry/register-manifest.md) — Thin iterator over `manifest.entries` that calls `registry.register(entry, { manifestSlug: manifest.packageSlug, manife… - [registry](./src/registry/registry.md) — In-memory `Map` registry — the central seam between content packages (which register entries)… - [impulse-response](./src/types/impulse-response.md) — Defines the in-memory shape of a fully-decoded impulse response — the value `@suaveplan/audio-ir-loader.load(id)` retur… - [manifest-entry](./src/types/manifest-entry.md) — Defines the on-the-wire shape of a single entry inside an `IrManifest`. An entry is the smallest unit a content package… - [manifest](./src/types/manifest.md) — Defines the on-the-wire shape of a complete IR manifest — the package-level contract a content package (e.g. `audio-ir-… ## What ships - `ImpulseResponse` / `IrManifestEntry` / `IrManifest` Zod schemas — all frozen on parse, with cross-field refinements (ID-prefix coherence, family coherence, channel-count + duration coherence). - `createIrRegistry()` → `IrRegistry` with `register`, `has`, `get`, `list`, `clear`. - `registerManifest(registry, manifest)` bulk helper. - `AUDIO_IR.*` error-code registry — exactly five codes, every one reachable from a real production call site. - OpenTelemetry surface — `audio_ir.registrations_total`, `audio_ir.lookups_total`, `audio_ir.errors_total`, `audio_ir.registry_size{family}`. ## Modules | Module | Subpath | Docs | | --- | --- | --- | | Manifest entry schema | `./types` | [`src/types/manifest-entry.md`](./src/types/manifest-entry.md) | | Manifest schema | `./types` | [`src/types/manifest.md`](./src/types/manifest.md) | | `ImpulseResponse` schema | `./types` | [`src/types/impulse-response.md`](./src/types/impulse-response.md) | | `AUDIO_IR.*` codes | `./error-codes` | [`src/error-codes/error-codes.md`](./src/error-codes/error-codes.md) | | In-memory registry | `./registry` | [`src/registry/registry.md`](./src/registry/registry.md) | | `registerManifest` helper | `./registry` | [`src/registry/register-manifest.md`](./src/registry/register-manifest.md) | | OTel instruments | `./otel` | [`src/otel/otel.md`](./src/otel/otel.md) | | URI plugin registrar | `./registrar` | [`src/registrar/registrar.md`](./src/registrar/registrar.md) | ## Design The cross-cutting design for the IR family lives in That file is the source of truth for the family layering, ID derivation rule, URI strategy, and OTel split. ## Quick install This package is wave-0 foundation — depended on, not depending on, the rest of the audio-ir family. Pull it directly: ```ts import { createIrRegistry, IrManifestSchema, registerManifest, } from "@suaveplan/audio-ir-core"; const registry = createIrRegistry(); const manifest = IrManifestSchema.parse(rawManifestFromContentPackage); registerManifest(registry, manifest); ``` ## Composing IR content packages Every `audio-ir-{family}-plugin-` content package exposes a `register(registry, collector, options?)` function. `collector` is a `MutableUriPluginRegistrar` — a one-method object `{ use(plugin) }`. Pre- construct a collector, call every content package's `register(...)`, then hand the gathered `plugins` snapshot to `createStorageUriResolver` once: ```ts import { createIrRegistry, createUriPluginCollector, } from "@suaveplan/audio-ir-core"; import { register as registerClub } from "@suaveplan/audio-ir-room-plugin-club"; import { register as registerPool } from "@suaveplan/audio-ir-room-plugin-pool"; import { createStorageUriResolver } from "@suaveplan/storage-uri"; const registry = createIrRegistry(); const collector = createUriPluginCollector(); // Each content package calls collector.use(plugin) for every bundle plugin. registerPool(registry, collector); registerClub(registry, collector); // `collector.plugins` is a frozen snapshot — safe to hand off directly. const resolver = createStorageUriResolver({ plugins: collector.plugins }); ``` The collector is order-preserving and snapshot-frozen, so consumers can compose multiple content packages — or fan out to multiple resolvers (parent + inner, sharded, future builder) — without each content package needing to agree on resolver shape. See [`src/registrar/registrar.md`](./src/registrar/registrar.md) for the full API reference.