Files
suaveplan/openspec/archive/2026-05-23-enterprise-audio-engine-server/proposal.md
2026-05-27 23:29:53 +01:00

6.8 KiB

OpenSpec Change: enterprise-audio-engine-server

  • Change ID: enterprise-audio-engine-server
  • Package: @suaveplan/audio-engine-server
  • Category: server
  • Target path: packages/media/audio/audio-engine-server
  • Status: PROPOSED
  • Date: 2026-05-14
  • Design: ./design.md
  • Spec delta: ./specs/audio-engine-server/spec.md

Summary

Deliver @suaveplan/audio-engine-server on the genesis branch from scratch at enterprise quality. Server-side file I/O adapters for the genesis audio ecosystem. This package is intentionally thin — all DSP, planning, streaming, and timeline logic lives in universal packages (audio-core, audio-timeline, audio-transport). This package only provides file source adapters, file sink adapters, and a server timeline adapter that renders to file output instead of speakers.

Batch processing is universal (uses @suaveplan/queue + @suaveplan/streams) and is documented in the master spec as a cross-cutting pattern, not a server-specific feature.


Current-state audit (genesis)

The package does not exist on genesis yet — this is a fresh build. No prior source, no test results, no coverage data. The old enterprise-browser-audio-engine was browser-only and had no server equivalent.


Available workspace deps on genesis

Package Role
@suaveplan/audio-core (workspace:^) AudioDocument, PlanEngine, ProcessPipeline
@suaveplan/audio-timeline (workspace:^) TimelineEngine, Track, Clip
@suaveplan/audio-transport (workspace:^) Transport
@suaveplan/audio-codecs (workspace:^) CodecRegistry, encodeTo, decodeSource
@suaveplan/streams (workspace:^) TransformStream, ReadableStream, WritableStream primitives
@suaveplan/error (workspace:^) SuaveplanError + defineCodes; the package exports AudioEngineServerError (extending AudioError from @suaveplan/audio-core) carrying AUDIO_ENGINE_SERVER.* codes.
@suaveplan/types (workspace:^) LoggerLike
@suaveplan/opentelemetry (workspace:^) OTel spans, counters, histograms
zod (catalog:forms) Zod schemas for option types

Dev deps: vitest, @vitest/coverage-istanbul, fast-check, mitata (all catalog:testing).


Public API

Core types

interface FileSourceOptions {
  fs?: FsLike          // injectable fs adapter for testing
  fetch?: typeof fetch // injectable fetch for URLs
}

class FileSourceAdapter {
  constructor(options?: FileSourceOptions)
  async load(path: string, opts?: LoadOptions): Promise<AudioDocument>
  createReadStream(path: string): ReadableStream<Uint8Array>
  async loadMany(paths: string[], opts?: LoadOptions): Promise<AudioDocument[]>
}

interface FileSinkOptions {
  fs?: FsLike
}

class FileSinkAdapter {
  constructor(options?: FileSinkOptions)
  async save(doc: AudioDocument, path: string, format: string, opts?: SaveOptions): Promise<void>
  createWriteStream(path: string): WritableStream<Uint8Array>
  async saveMany(docs: AudioDocument[], pathFormat: string): Promise<void>
}

interface ServerAdapterOptions {
  outputPath?: string
  format?: string
  sampleRate?: number
  ppqn?: number
}

class ServerTimelineAdapter implements TimelineAdapter {
  constructor(options?: ServerAdapterOptions)
  init(options?: { signal?: AbortSignal }): Promise<void>
  setTracks(tracks: Track[]): void
  addTrack(track: Track): void
  removeTrack(trackId: string): void
  updateTrack(trackId: string, track: Track): void
  play(time?: number): void
  pause(time?: number): void
  stop(time?: number): void
  seek(time: number): void
  getCurrentTime(): number
  setLoop(start: number, end: number, enabled: boolean): void
  setMasterVolume(volume: number): void
  setTrackVolume(trackId: string, volume: number): void
  setTrackMute(trackId: string, muted: boolean): void
  setTrackSolo(trackId: string, soloed: boolean): void
  setTrackPan(trackId: string, pan: number): void
  setTempo(bpm: number, atTick?: number): void
  ticksToSeconds(ticks: number): number
  secondsToTicks(seconds: number): number
  readonly lookAhead: 0
  readonly ppqn: number
  dispose(): void
  /** Extra non-interface method: render the timeline to a finite output, useful for non-realtime export. */
  render(options?: { startTime?: number; endTime?: number; signal?: AbortSignal }): Promise<void>
}

New additions for genesis

  1. Injectable fs adapter — FileSourceAdapter and FileSinkAdapter accept an FsLike interface for testing without touching the real filesystem.
  2. Streaming load/save — createReadStream / createWriteStream return WHATWG streams compatible with @suaveplan/streams.
  3. Batch helpers — loadMany / saveMany for bulk operations.
  4. Server playback = render — ServerTimelineAdapter.play() renders as fast as possible via audio-core.streamPlan() piped to codec encoder and file sink.
  5. OTel instrumentation — spans for file.load, file.save, server.render, counters for bytes read/written.
  6. Property-based tests — load then save → file exists, round-trip preserves sample count.
  7. Benchmark suite — load 100MB WAV, save 100MB FLAC.

File layout

packages/media/audio/audio-engine-server/
  package.json
  tsconfig.json
  vitest.config.ts
  src/
    index.ts
    error-codes.ts
    source/
      file-source.ts
      file-source.md
      file-source.types.ts
      file-source.test.ts
      index.ts
    sink/
      file-sink.ts
      file-sink.md
      file-sink.types.ts
      file-sink.test.ts
      index.ts
    adapter/
      server-adapter.ts
      server-adapter.md
      server-adapter.types.ts
      server-adapter.test.ts
      index.ts
    __benchmarks__/
      io.bench.ts
  tests/
    integration.test.ts
    property.test.ts

Impact

Breaking changes: none — fresh build, package does not yet exist on genesis.

Prerequisites: @suaveplan/audio-core, @suaveplan/audio-timeline, @suaveplan/audio-transport, @suaveplan/audio-codecs, @suaveplan/streams, @suaveplan/error, @suaveplan/types, @suaveplan/opentelemetry published to Verdaccio.

No universal package may depend on this. It is a leaf node in the dependency graph.

Affected dependents

@suaveplan/audio-engine-server is a leaf node — its only downstream consumer is the audio CLI, which does not exist on genesis yet (it lands in a later change in this same batch). It is created against this package's spec delta, not retrofitted.

  • @suaveplan/audio-cli — [ ] tests run / [ ] docs reviewed / [x] no action needed (reason: does not exist yet; created in enterprise-audio-cli)

Once landed, any subsequent change to this package's public surface MUST run bunx turbo test --filter=...@suaveplan/audio-engine-server per openspec/AGENTS.md §13 step 10.