Move docs/ package mirror into document-navigator plugin
Relocate the ~6k-file docs tree from the repo root into plugins/document-navigator/docs/ so the plugin is self-contained and portable. docs/superpowers/ (specs and plans) stays at the repo root. Moved via cp+rm rather than git mv because directory rename() fails with EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the identical-content files at commit time. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
# @suaveplan/audio-transport - Universal musical clock and transport: sample-accurate scheduling with PPQ, BPM automation, swing, time signatures, and loop regions. Zero browser or Node dependencies.
|
||||
|
||||
Universal musical clock and transport: sample-accurate scheduling with PPQ, BPM automation, swing, time signatures, and loop regions. Zero browser or Node dependencies.
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
## Documentation tree
|
||||
|
||||
- [error-codes](./src/error-codes/error-codes.md) — This module defines the canonical error codes, metadata, and error class for the `@suaveplan/audio-transport` package.…
|
||||
- [error-codes](./src/error-codes/error-codes.md) — This module defines the canonical error codes, metadata, and error class for the `@suaveplan/audio-transport` package.…
|
||||
- [scheduler](./src/scheduler/scheduler.md) — The `TransportScheduler` class drives the lookahead loop that polls the transport and fires scheduled callbacks. It bri…
|
||||
- [scheduler](./src/scheduler/scheduler.md) — The `TransportScheduler` class drives the lookahead loop that polls the transport and fires scheduled callbacks. It bri…
|
||||
- [scheduler.schemas](./src/scheduler/scheduler.schemas.md) — Zod boundary schema for `TransportScheduler` construction options.
|
||||
- [signal](./src/signal/signal.md) — The `Signal` class implements a piecewise automation curve for scalar parameters such as BPM. It stores automation poin…
|
||||
- [signal](./src/signal/signal.md) — The `Signal` class implements a piecewise automation curve for scalar parameters such as BPM. It stores automation poin…
|
||||
- [signal.schemas](./src/signal/signal.schemas.md) — Zod boundary schema for `Signal` construction options.
|
||||
- [tick-source](./src/tick-source/tick-source.md) — The `TickSource` class converts between audio time (seconds) and musical ticks, integrating tempo curves for sample-acc…
|
||||
- [tick-source](./src/tick-source/tick-source.md) — The `TickSource` class converts between audio time (seconds) and musical ticks, integrating tempo curves for sample-acc…
|
||||
- [tick-source.schemas](./src/tick-source/tick-source.schemas.md) — Zod boundary schema for `TickSource` construction options.
|
||||
- [time](./src/time/time.md) — This module parses musical time tokens and converts between ticks, seconds, and bars:beats:sixteenths notation.
|
||||
- [time](./src/time/time.md) — This module parses musical time tokens and converts between ticks, seconds, and bars:beats:sixteenths notation.
|
||||
- [time.schemas](./src/time/time.schemas.md) — Zod boundary schemas for time-related types.
|
||||
- [transport](./src/transport/transport.md) — The `Transport` class is the core musical clock and scheduler. It manages playback state, event scheduling, loop region…
|
||||
- [transport](./src/transport/transport.md) — The `Transport` class is the core musical clock and scheduler. It manages playback state, event scheduling, loop region…
|
||||
- [transport.schemas](./src/transport/transport.schemas.md) — Zod boundary schema for `Transport` construction options.
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
bun add @suaveplan/audio-transport
|
||||
```
|
||||
|
||||
## Quick start
|
||||
|
||||
```ts
|
||||
import { Transport, TransportScheduler } from "@suaveplan/audio-transport";
|
||||
|
||||
const clock = () => Date.now() / 1000;
|
||||
const tr = new Transport({ clock, bpm: 120, ppq: 192 });
|
||||
const scheduler = new TransportScheduler(tr, clock);
|
||||
|
||||
tr.scheduleRepeat((time) => {
|
||||
console.log("beat at", time);
|
||||
}, "4n");
|
||||
|
||||
tr.start();
|
||||
scheduler.start();
|
||||
```
|
||||
|
||||
## Modules
|
||||
|
||||
- [Transport](src/transport/transport.md) — musical clock and event scheduler
|
||||
- [TransportScheduler](src/scheduler/scheduler.md) — lookahead poll loop
|
||||
- [TickSource](src/tick-source/tick-source.md) — audio time to musical ticks
|
||||
- [Signal](src/signal/signal.md) — tempo automation curves
|
||||
- [Time](src/time/time.md) — musical time parsing and conversion
|
||||
- [Error Codes](src/error-codes.md) — structured error registry
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
# Error Codes
|
||||
|
||||
This module defines the canonical error codes, metadata, and error class for the `@suaveplan/audio-transport` package. Every throwing site in the library uses one of these codes so that callers can handle failures programmatically without parsing human-readable messages.
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Audio transport involves many operations that can fail in predictable ways: invalid time tokens, out-of-range BPM values, missing scheduled events, and operations on a disposed transport. Centralising the error codes in one module guarantees consistency across the package and gives consumers a stable contract for error handling.
|
||||
|
||||
## Features
|
||||
|
||||
- Five error codes covering time, BPM, scheduler, events, and disposal
|
||||
- `TransportError` extends `SuaveplanError` with a public constructor
|
||||
- `AUDIO_TRANSPORT_ERROR_METADATA` maps each code to a description and severity
|
||||
- `registerTransportErrorCodes()` alias for spec compliance
|
||||
- Self-registering on import
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { AUDIO_TRANSPORT_CODES, TransportError } from "@suaveplan/audio-transport";
|
||||
|
||||
throw new TransportError(
|
||||
AUDIO_TRANSPORT_CODES.INVALID_TIME,
|
||||
"audio_transport.transport.invalid_loop_end",
|
||||
{ context: { loopStart: "0:0:0", loopEnd: "0:0:0" } }
|
||||
);
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import {
|
||||
AUDIO_TRANSPORT_CODES,
|
||||
AUDIO_TRANSPORT_ERROR_METADATA,
|
||||
TransportError,
|
||||
} from "@suaveplan/audio-transport";
|
||||
|
||||
function describeFailure(err: TransportError): string {
|
||||
const meta = AUDIO_TRANSPORT_ERROR_METADATA[err.code];
|
||||
return `${meta.severity}: ${meta.description}`;
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `AUDIO_TRANSPORT_CODES` — `defineCodes("AUDIO_TRANSPORT", [...])` constant
|
||||
- `AUDIO_TRANSPORT_ERROR_METADATA` — readonly map of code → `{ description, severity }`
|
||||
- `TransportError` — error class with public constructor `(code, messageKey, options?)`
|
||||
- `registerTransportErrorCodes()` / `registerAudioTransportErrorCodes()` — no-op aliases
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The registry is self-registering on import. `TransportError` widens the protected `SuaveplanError` constructor to public so that factory and class methods can instantiate it directly. All five codes are exercised by the test suite. The `TransportErrorCode` type alias and `registerTransportErrorCodes` function exist purely for spec compliance and resolve to the same runtime values as their `AudioTransport*` counterparts.
|
||||
@@ -0,0 +1,58 @@
|
||||
# TransportScheduler
|
||||
|
||||
The `TransportScheduler` class drives the lookahead loop that polls the transport and fires scheduled callbacks. It bridges the `Transport` and the wall clock, ensuring that musical events are delivered with sample-accurate timing even when the underlying poll timer is coarse.
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
In real-time audio systems, callbacks must fire at precise musical times. A naive approach would poll the transport on every sample, which is prohibitively expensive. Instead, `TransportScheduler` polls at a coarse interval (~100 Hz) and asks the transport to process the entire time window in one go. The transport then uses `TickSource.forEachTickBetween` to visit every musical tick in the window and invoke callbacks at their exact audio times.
|
||||
|
||||
## Features
|
||||
|
||||
- Poll-driven lookahead using `@suaveplan/timing`'s `createScheduler`
|
||||
- `start()` / `stop()` lifecycle
|
||||
- `AbortSignal` support for mid-run cancellation
|
||||
- OpenTelemetry `transport.tick` span per poll
|
||||
- Throws `AUDIO_TRANSPORT_SCHEDULER_NOT_STARTED` for invalid operations
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { Transport, TransportScheduler } from "@suaveplan/audio-transport";
|
||||
|
||||
const tr = new Transport();
|
||||
const scheduler = new TransportScheduler(tr, () => Date.now() / 1000);
|
||||
tr.start();
|
||||
scheduler.start();
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
const ac = new AbortController();
|
||||
scheduler.start({ signal: ac.signal });
|
||||
// Later:
|
||||
ac.abort();
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### Constructor
|
||||
|
||||
`new TransportScheduler(transport, clock, options?)`
|
||||
|
||||
### Methods
|
||||
|
||||
- `start(options?)` — begin polling; accepts `AbortSignal`
|
||||
- `stop()` — halt polling
|
||||
|
||||
### Properties
|
||||
|
||||
- `running` — boolean
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The scheduler uses real timers via `createScheduler`. Each poll creates an OpenTelemetry span named `transport.tick`. Calling `stop()` before `start()` throws `AUDIO_TRANSPORT_SCHEDULER_NOT_STARTED`. The scheduler does not emit FSM events directly; those are emitted synchronously by the `Transport` when `start`, `stop`, or `pause` are called. The `_tick` and `_scheduleNextPoll` methods contain defensive guards for the case where `stop()` races with a scheduled callback.
|
||||
@@ -0,0 +1,32 @@
|
||||
# TransportSchedulerOptionsSchema
|
||||
|
||||
Zod boundary schema for `TransportScheduler` construction options.
|
||||
|
||||
## Purpose
|
||||
|
||||
Validates the options passed when constructing a `TransportScheduler`.
|
||||
|
||||
## Features
|
||||
|
||||
- Requires `transport` (any — runtime-validated)
|
||||
- Requires `clock` as a function returning `number`
|
||||
- Optional `pollIntervalMs` as positive number
|
||||
|
||||
## Usage
|
||||
|
||||
```ts
|
||||
import { TransportSchedulerOptionsSchema } from "@suaveplan/audio-transport";
|
||||
|
||||
const parsed = TransportSchedulerOptionsSchema.safeParse({
|
||||
transport: myTransport,
|
||||
clock: () => Date.now() / 1000,
|
||||
});
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `TransportSchedulerOptionsSchema` — `z.object({ transport, clock, pollIntervalMs })`
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
`transport` is typed as `z.any()` because Zod cannot express the `Transport` class type at schema level. The constructor performs the actual instance check.
|
||||
@@ -0,0 +1,63 @@
|
||||
# Signal
|
||||
|
||||
The `Signal` class implements a piecewise automation curve for scalar parameters such as BPM. It stores automation points and evaluates the curve at any audio time, supporting step, linear, and exponential ramps.
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Musical performances often require smooth tempo changes. `Signal` provides the mathematical foundation for these changes by scheduling automation points and interpolating between them. The `TickSource` reads the BPM curve through this `Signal` so that tempo ramps produce a continuously varying tick rate.
|
||||
|
||||
## Features
|
||||
|
||||
- `setValueAtTime` for step changes
|
||||
- `linearRampToValueAtTime` for linear interpolation
|
||||
- `exponentialRampToValueAtTime` for exponential curves
|
||||
- `getValueAtTime` for curve evaluation at arbitrary times
|
||||
- `cancelScheduledValues` for clearing future points
|
||||
- Rejects non-positive and non-finite values with `AUDIO_TRANSPORT_INVALID_BPM`
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { Signal } from "@suaveplan/audio-transport";
|
||||
|
||||
const bpm = new Signal({ initialValue: 120 });
|
||||
bpm.linearRampToValueAtTime(240, 1);
|
||||
console.log(bpm.getValueAtTime(0.5)); // 180
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
const bpm = new Signal({ initialValue: 100 });
|
||||
bpm.setValueAtTime(120, 0.5);
|
||||
bpm.exponentialRampToValueAtTime(240, 1.5);
|
||||
console.log(bpm.getValueAtTime(1)); // ~169.7
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### Constructor
|
||||
|
||||
`new Signal(options?)`
|
||||
|
||||
### Properties
|
||||
|
||||
- `initialValue` — value before any points
|
||||
- `value` — convenience getter returning value at time 0
|
||||
- `points` — readonly array of automation points
|
||||
|
||||
### Methods
|
||||
|
||||
- `setValueAtTime(value, time)` — step change
|
||||
- `linearRampToValueAtTime(value, time)` — linear ramp
|
||||
- `exponentialRampToValueAtTime(value, time)` — exponential ramp
|
||||
- `getValueAtTime(time)` — evaluate curve
|
||||
- `cancelScheduledValues(time)` — remove points at or after time
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Points are stored sorted by time. Exponential ramps require strictly positive start and target values; otherwise `AUDIO_TRANSPORT_INVALID_BPM` is thrown. The curve evaluation handles interpolation before the first point, between points, and after the last point.
|
||||
@@ -0,0 +1,27 @@
|
||||
# SignalOptionsSchema
|
||||
|
||||
Zod boundary schema for `Signal` construction options.
|
||||
|
||||
## Purpose
|
||||
|
||||
Validates the options passed when constructing a `Signal`.
|
||||
|
||||
## Features
|
||||
|
||||
- Optional `initialValue` as positive number
|
||||
|
||||
## Usage
|
||||
|
||||
```ts
|
||||
import { SignalOptionsSchema } from "@suaveplan/audio-transport";
|
||||
|
||||
const parsed = SignalOptionsSchema.safeParse({ initialValue: 130 });
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `SignalOptionsSchema` — `z.object({ initialValue })`
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The schema is minimal because `Signal` only needs an initial value. All other behavior is controlled via method calls after construction.
|
||||
@@ -0,0 +1,67 @@
|
||||
# TickSource
|
||||
|
||||
The `TickSource` class converts between audio time (seconds) and musical ticks, integrating tempo curves for sample-accurate scheduling.
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`TickSource` is the mathematical engine behind `Transport`. It translates wall-clock seconds into musical ticks and back, accounting for tempo ramps and anchors. The integration is exact for constant, linear, and exponential tempo segments, ensuring that `getTicksAtTime` and `getTimeOfTick` remain mutual inverses within floating-point tolerance.
|
||||
|
||||
## Features
|
||||
|
||||
- Exact inverse conversions under constant tempo
|
||||
- Integration of linear and exponential tempo ramps
|
||||
- `forEachTickBetween` for sample-accurate tick iteration
|
||||
- `setTicksAtTime` for anchoring the counter
|
||||
- Start, stop, and pause lifecycle
|
||||
- No wall-clock reads — all methods take explicit times
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { TickSource, Signal } from "@suaveplan/audio-transport";
|
||||
|
||||
const ts = new TickSource({ ppq: 192, bpm: new Signal({ initialValue: 120 }) });
|
||||
ts.start(0);
|
||||
const ticks = ts.getTicksAtTime(1); // 384
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
const bpm = new Signal({ initialValue: 120 });
|
||||
bpm.linearRampToValueAtTime(240, 1);
|
||||
const ts = new TickSource({ ppq: 192, bpm });
|
||||
ts.start(0);
|
||||
ts.forEachTickBetween(0, 1, (time, tick) => {
|
||||
console.log("tick", tick, "at", time);
|
||||
});
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### Constructor
|
||||
|
||||
`new TickSource(options?)`
|
||||
|
||||
### Properties
|
||||
|
||||
- `ppq` — pulses per quarter note
|
||||
- `bpm` — `SignalLike` tempo curve
|
||||
|
||||
### Methods
|
||||
|
||||
- `getTicksAtTime(time)` — ticks at audio time
|
||||
- `getTimeOfTick(tick, before?)` — audio time of tick
|
||||
- `setTicksAtTime(ticks, time)` — anchor counter
|
||||
- `start(time, offset?)` — begin counting
|
||||
- `stop(time)` — stop counting
|
||||
- `pause(time)` — pause counting
|
||||
- `forEachTickBetween(start, end, callback)` — iterate ticks
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Tick integration uses closed-form integrals for constant, linear, and exponential tempo segments. `getTimeOfTick` uses binary search over the integrated curve. The `forEachTickBetween` method guarantees no duplicate or skipped ticks across consecutive windows. The source maintains an internal `_running` flag and `_offsetTicks` value so that start, stop, and pause operations preserve the accumulated tick count correctly.
|
||||
@@ -0,0 +1,28 @@
|
||||
# TickSourceOptionsSchema
|
||||
|
||||
Zod boundary schema for `TickSource` construction options.
|
||||
|
||||
## Purpose
|
||||
|
||||
Validates the options passed when constructing a `TickSource`.
|
||||
|
||||
## Features
|
||||
|
||||
- Optional `ppq` as positive integer
|
||||
- Optional `bpm` as `Signal` instance
|
||||
|
||||
## Usage
|
||||
|
||||
```ts
|
||||
import { TickSourceOptionsSchema } from "@suaveplan/audio-transport";
|
||||
|
||||
const parsed = TickSourceOptionsSchema.safeParse({ ppq: 96 });
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `TickSourceOptionsSchema` — `z.object({ ppq, bpm })`
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
`bpm` uses `z.instanceof(Signal)` because the tick source needs the concrete `Signal` class for segment building.
|
||||
@@ -0,0 +1,59 @@
|
||||
# Time Parsing and Conversion
|
||||
|
||||
This module parses musical time tokens and converts between ticks, seconds, and bars:beats:sixteenths notation.
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Musical time is expressed in many forms: subdivision notation, measure counts, bar positions, and raw seconds. `parseTimeToken` turns all of these into a canonical `{ ticks, seconds }` representation. The conversion helpers then provide round-tripping between ticks, seconds, and human-readable bar positions.
|
||||
|
||||
## Features
|
||||
|
||||
- Subdivision notation (`"4n"`, `"8n"`, `"16n"`, `"8t"`, `"16t"`)
|
||||
- Measure notation (`"1m"`)
|
||||
- Bars:beats:sixteenths (`"2:1:0"`)
|
||||
- Relative seconds (`"+1"`)
|
||||
- Absolute measures (`"@4m"`)
|
||||
- Raw numeric ticks
|
||||
- Bidirectional conversion helpers
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { parseTimeToken } from "@suaveplan/audio-transport";
|
||||
|
||||
const ctx = { ppq: 192, timeSignature: 4, bpm: 120 };
|
||||
const result = parseTimeToken("4n", ctx);
|
||||
console.log(result.ticks); // 192
|
||||
console.log(result.seconds); // 0.5
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import {
|
||||
parseTimeToken,
|
||||
toBarsBeatsSixteenths,
|
||||
fromBarsBeatsSixteenths,
|
||||
} from "@suaveplan/audio-transport";
|
||||
|
||||
const ctx = { ppq: 192, timeSignature: 4, bpm: 120 };
|
||||
const ticks = fromBarsBeatsSixteenths("1:2:3", ctx);
|
||||
const bbs = toBarsBeatsSixteenths(ticks, ctx);
|
||||
console.log(bbs); // "1:2:3"
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `parseTimeToken(token, ctx)` → `{ ticks, seconds }`
|
||||
- `ticksToSeconds(ticks, ctx)` → `number`
|
||||
- `secondsToTicks(seconds, ctx)` → `number`
|
||||
- `toBarsBeatsSixteenths(ticks, ctx)` → `string`
|
||||
- `fromBarsBeatsSixteenths(token, ctx)` → `number`
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
All conversions assume constant tempo. `parseTimeToken` throws `AUDIO_TRANSPORT_INVALID_TIME` for unrecognised tokens. Numeric tokens are treated as raw ticks and returned directly. The regex-based parsers are ordered from most specific to least specific so that subdivision tokens are tried before relative tokens, avoiding ambiguity. The `bars:beats:sixteenths` format uses zero-based indexing for all three components. `toBarsBeatsSixteenths` floors each component rather than rounding, which is the conventional behaviour for DAW-style position displays. `fromBarsBeatsSixteenths` validates the input format with a strict regex and throws `AUDIO_TRANSPORT_INVALID_TIME` for malformed strings.
|
||||
@@ -0,0 +1,36 @@
|
||||
# Time Schemas
|
||||
|
||||
Zod boundary schemas for time-related types.
|
||||
|
||||
## Purpose
|
||||
|
||||
Provides validation schemas for `TransportTime`, `Ticks`, `BarsBeatsSixteenths`, `TimeContext`, and `TimeParseResult`.
|
||||
|
||||
## Features
|
||||
|
||||
- `TransportTimeSchema` — string or non-negative number
|
||||
- `TicksSchema` — non-negative number
|
||||
- `BarsBeatsSixteenthsSchema` — regex `"bars:beats:sixteenths"`
|
||||
- `TimeContextSchema` — `{ ppq, timeSignature, bpm }`
|
||||
- `TimeParseResultSchema` — `{ ticks, seconds }`
|
||||
|
||||
## Usage
|
||||
|
||||
```ts
|
||||
import { TransportTimeSchema } from "@suaveplan/audio-transport";
|
||||
|
||||
TransportTimeSchema.parse("4n");
|
||||
TransportTimeSchema.parse(192);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `TransportTimeSchema` — `z.union([z.string().min(1), z.number().nonnegative()])`
|
||||
- `TicksSchema` — `z.number().nonnegative()`
|
||||
- `BarsBeatsSixteenthsSchema` — `z.string().regex(/^(-?\d+):(-?\d+):(-?\d+)$/)`
|
||||
- `TimeContextSchema` — `z.object({ ppq, timeSignature, bpm })`
|
||||
- `TimeParseResultSchema` — `z.object({ ticks, seconds })`
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
`TransportTimeSchema` accepts both string tokens and raw numeric ticks so that callers can pass either form interchangeably.
|
||||
@@ -0,0 +1,92 @@
|
||||
# Transport
|
||||
|
||||
The `Transport` class is the core musical clock and scheduler. It manages playback state, event scheduling, loop regions, swing, and tempo automation.
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`Transport` provides a sample-accurate musical time base for audio applications. It converts between audio time (seconds) and musical ticks using a configurable PPQ and tempo curve, and it fires scheduled callbacks with sample-accurate times.
|
||||
|
||||
## Features
|
||||
|
||||
- Playback FSM with states `stopped`, `playing`, and `paused`
|
||||
- Event scheduling: one-shot, repeat, and cancel
|
||||
- Loop regions with automatic wrap and `loop` event
|
||||
- Swing with configurable subdivision
|
||||
- BPM automation via `Signal`
|
||||
- Typed event bus (`start`, `stop`, `pause`, `loop`, `bpm`)
|
||||
- Zod boundary validation on construction
|
||||
- OpenTelemetry instrumentation for schedules and ticks
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { Transport } from "@suaveplan/audio-transport";
|
||||
|
||||
const tr = new Transport({ bpm: 120, ppq: 192 });
|
||||
const id = tr.scheduleOnce((time) => {
|
||||
console.log("tick at", time);
|
||||
}, "0:1:0");
|
||||
|
||||
tr.start();
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { Transport } from "@suaveplan/audio-transport";
|
||||
|
||||
const tr = new Transport({
|
||||
bpm: 120,
|
||||
ppq: 192,
|
||||
loop: true,
|
||||
loopStart: "0:0:0",
|
||||
loopEnd: "1:0:0",
|
||||
});
|
||||
|
||||
tr.on("loop", (time) => console.log("looped at", time));
|
||||
tr.scheduleRepeat((time) => console.log("beat", time), "4n");
|
||||
tr.start();
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### Constructor
|
||||
|
||||
`new Transport(options?: TransportOptions)`
|
||||
|
||||
### Properties
|
||||
|
||||
- `bpm` — `SignalLike` with automation support
|
||||
- `ppq` — pulses per quarter note (readonly)
|
||||
- `timeSignature` — beats per measure (readonly)
|
||||
- `swing` — value in `[0, 1]`
|
||||
- `swingSubdivision` — `"8n"` or `"16n"`
|
||||
- `loop` — boolean
|
||||
- `loopStart` / `loopEnd` — `TransportTime`
|
||||
- `state` — `"stopped" | "playing" | "paused"`
|
||||
- `position` — `BarsBeatsSixteenths` string
|
||||
- `ticks` — current tick count
|
||||
- `seconds` — current clock time
|
||||
|
||||
### Methods
|
||||
|
||||
- `start(time?, offset?)` — start or resume playback
|
||||
- `stop(time?)` — stop playback
|
||||
- `pause(time?)` — pause playback
|
||||
- `schedule(callback, time)` — schedule a one-shot callback
|
||||
- `scheduleOnce(callback, time)` — schedule and auto-remove
|
||||
- `scheduleRepeat(callback, interval, start?)` — repeating callback
|
||||
- `cancel(eventId)` — remove a scheduled event
|
||||
- `cancelAfter(time)` — remove events at or after time
|
||||
- `toSeconds(time)` — convert token to seconds
|
||||
- `toTicks(time)` — convert token to ticks
|
||||
- `dispose()` — tear down
|
||||
- `on(event, listener)` / `off(event, listener)` — event bus
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The constructor validates options via `TransportOptionsSchema`. All wall-clock reads go through the injected `Clock`. The default clock is `() => 0` to preserve universal purity.
|
||||
@@ -0,0 +1,33 @@
|
||||
# TransportOptionsSchema
|
||||
|
||||
Zod boundary schema for `Transport` construction options.
|
||||
|
||||
## Purpose
|
||||
|
||||
Every public entry point validates its options before use. `TransportOptionsSchema` defines the shape, types, and constraints accepted by the `Transport` constructor.
|
||||
|
||||
## Features
|
||||
|
||||
- Validates `bpm`, `ppq`, `swing`, `timeSignature` as positive numbers
|
||||
- Validates `loop` as boolean
|
||||
- Accepts `loopStart`, `loopEnd`, `swingSubdivision` as string or number
|
||||
- Accepts `clock` as a function returning `number`
|
||||
|
||||
## Usage
|
||||
|
||||
```ts
|
||||
import { TransportOptionsSchema } from "@suaveplan/audio-transport";
|
||||
|
||||
const parsed = TransportOptionsSchema.safeParse({ ppq: 192 });
|
||||
if (!parsed.success) {
|
||||
console.error(parsed.error.issues);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `TransportOptionsSchema` — `z.object({ bpm, ppq, swing, swingSubdivision, timeSignature, loop, loopStart, loopEnd, clock, logger })`
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The schema is used inside `validateOptions` in `transport.ts`. A parse failure throws `TransportError` with code `AUDIO_TRANSPORT_INVALID_TIME`.
|
||||
Reference in New Issue
Block a user