@dawcore/transport 0.0.9 → 0.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/core/clock.ts","../src/core/scheduler.ts","../src/core/timer.ts","../src/timeline/sample-timeline.ts","../src/timeline/tempo-map.ts","../src/timeline/meter-map.ts","../src/audio/master-node.ts","../src/audio/track-node.ts","../src/audio/clip-player.ts","../src/audio/metronome-player.ts","../src/audio/count-in-player.ts","../src/audio/click-sounds.ts","../src/transport.ts","../src/adapter.ts"],"sourcesContent":["// packages/transport/src/index.ts\nexport type {\n Tick,\n Sample,\n SchedulerEvent,\n SchedulerListener,\n TransportOptions,\n TempoEntry,\n TempoInterpolation,\n TransportPosition,\n MeterSignature,\n MeterEntry,\n CountInMode,\n CountInEventData,\n TempoChangeEventData,\n MeterChangeEventData,\n} from './types';\n\nexport { Clock } from './core/clock';\nexport { Scheduler, type SchedulerOptions } from './core/scheduler';\nexport { Timer } from './core/timer';\nexport { SampleTimeline } from './timeline/sample-timeline';\nexport { TempoMap, type SetTempoOptions } from './timeline/tempo-map';\nexport { MeterMap } from './timeline/meter-map';\nexport { MasterNode } from './audio/master-node';\nexport { TrackNode } from './audio/track-node';\nexport { ClipPlayer, type ClipEvent } from './audio/clip-player';\nexport { MetronomePlayer, type MetronomeEvent } from './audio/metronome-player';\nexport type { CountInEvent } from './audio/count-in-player';\nexport type { ClickSoundOptions } from './audio/click-sounds';\nexport { Transport, type TransportEvents } from './transport';\nexport { NativePlayoutAdapter } from './adapter';\n","export class Clock {\n private _audioContext: AudioContext;\n private _running = false;\n private _audioTimeAtStart = 0;\n private _clockTimeAtStart = 0;\n\n constructor(audioContext: AudioContext) {\n this._audioContext = audioContext;\n }\n\n start(): void {\n if (this._running) return;\n this._audioTimeAtStart = this._audioContext.currentTime;\n this._running = true;\n }\n\n stop(): void {\n if (!this._running) return;\n this._clockTimeAtStart = this.getTime();\n this._running = false;\n }\n\n reset(): void {\n this._running = false;\n this._clockTimeAtStart = 0;\n this._audioTimeAtStart = 0;\n }\n\n getTime(): number {\n if (this._running) {\n return this._clockTimeAtStart + (this._audioContext.currentTime - this._audioTimeAtStart);\n }\n return this._clockTimeAtStart;\n }\n\n seekTo(time: number): void {\n if (this._running) {\n this._clockTimeAtStart = time;\n this._audioTimeAtStart = this._audioContext.currentTime;\n } else {\n this._clockTimeAtStart = time;\n }\n }\n\n /**\n * Convert transport time to AudioContext.currentTime space.\n * Used by players to schedule AudioBufferSourceNode.start(when).\n */\n toAudioTime(transportTime: number): number {\n return this._audioContext.currentTime + (transportTime - this.getTime());\n }\n\n isRunning(): boolean {\n return this._running;\n }\n}\n","import type { Tick, SchedulerEvent, SchedulerListener } from '../types';\nimport type { TempoMap } from '../timeline/tempo-map';\n\nexport interface SchedulerOptions {\n lookahead?: number;\n /** Called when the scheduler wraps at loopEnd.\n * Receives loopStart, loopEnd, and the currentTimeSeconds snapshot from\n * advance() so the Transport can compute the correct clock seek target\n * without re-reading the live AudioContext.currentTime. */\n onLoop?: (loopStartSeconds: number, loopEndSeconds: number, currentTimeSeconds: number) => void;\n}\n\nexport class Scheduler<T extends SchedulerEvent> {\n private _lookahead: number;\n private _rightEdge = 0; // integer ticks\n private _listeners: Set<SchedulerListener<T>> = new Set();\n private _loopEnabled = false;\n private _loopStart = 0; // integer ticks\n private _loopEnd = 0; // integer ticks\n private _onLoop:\n | ((loopStartSeconds: number, loopEndSeconds: number, currentTimeSeconds: number) => void)\n | undefined;\n private _tempoMap: TempoMap;\n\n constructor(tempoMap: TempoMap, options: SchedulerOptions = {}) {\n this._tempoMap = tempoMap;\n this._lookahead = options.lookahead ?? 0.2;\n this._onLoop = options.onLoop;\n }\n\n addListener(listener: SchedulerListener<T>): void {\n this._listeners.add(listener);\n }\n\n removeListener(listener: SchedulerListener<T>): void {\n this._listeners.delete(listener);\n }\n\n /** Primary API — ticks as source of truth */\n setLoop(enabled: boolean, startTick: Tick, endTick: Tick): void {\n if (enabled && (!Number.isFinite(startTick) || !Number.isFinite(endTick))) {\n console.warn(\n '[waveform-playlist] Scheduler.setLoop: non-finite tick values (' +\n startTick +\n ', ' +\n endTick +\n ')'\n );\n return;\n }\n if (enabled && startTick >= endTick) {\n console.warn(\n '[waveform-playlist] Scheduler.setLoop: startTick (' +\n startTick +\n ') must be less than endTick (' +\n endTick +\n ')'\n );\n return;\n }\n this._loopEnabled = enabled;\n this._loopStart = Math.round(startTick);\n this._loopEnd = Math.round(endTick);\n }\n\n /** Convenience — converts seconds to ticks via TempoMap */\n setLoopSeconds(enabled: boolean, startSec: number, endSec: number): void {\n const startTick = this._tempoMap.secondsToTicks(startSec);\n const endTick = this._tempoMap.secondsToTicks(endSec);\n this.setLoop(enabled, startTick, endTick);\n }\n\n /** Reset scheduling cursor. Takes seconds (from Clock), converts to ticks. */\n reset(timeSeconds: number): void {\n this._rightEdge = this._tempoMap.secondsToTicks(timeSeconds);\n }\n\n /** Advance the scheduling window. Takes seconds (from Clock), converts to ticks. */\n advance(currentTimeSeconds: number): void {\n const targetTick = this._tempoMap.secondsToTicks(currentTimeSeconds + this._lookahead);\n\n if (this._loopEnabled && this._loopEnd > this._loopStart) {\n const loopDuration = this._loopEnd - this._loopStart;\n let remaining = targetTick - this._rightEdge;\n\n while (remaining > 0) {\n const distToEnd = this._loopEnd - this._rightEdge;\n if (distToEnd <= 0 || distToEnd > remaining) {\n this._generateAndConsume(this._rightEdge, this._rightEdge + remaining);\n this._rightEdge += remaining;\n break;\n }\n // Generate up to loopEnd\n this._generateAndConsume(this._rightEdge, this._loopEnd);\n remaining -= distToEnd;\n // Notify listeners of position jump (in ticks)\n for (const listener of this._listeners) {\n listener.onPositionJump(this._loopStart as Tick);\n }\n // Seek clock — passes the currentTimeSeconds snapshot so Transport\n // uses the same clock reading as advance(), not a live re-read.\n this._onLoop?.(\n this._tempoMap.ticksToSeconds(this._loopStart as Tick),\n this._tempoMap.ticksToSeconds(this._loopEnd as Tick),\n currentTimeSeconds\n );\n this._rightEdge = this._loopStart;\n\n // Guard against infinite loop\n if (loopDuration <= 0) break;\n }\n return;\n }\n\n if (targetTick > this._rightEdge) {\n this._generateAndConsume(this._rightEdge, targetTick);\n this._rightEdge = targetTick;\n }\n }\n\n private _generateAndConsume(fromTick: number, toTick: number): void {\n for (const listener of this._listeners) {\n try {\n const events = listener.generate(fromTick as Tick, toTick as Tick);\n for (const event of events) {\n try {\n listener.consume(event);\n } catch (err) {\n console.warn('[waveform-playlist] Scheduler: error consuming event:', String(err));\n }\n }\n } catch (err) {\n console.warn('[waveform-playlist] Scheduler: error generating events:', String(err));\n }\n }\n }\n}\n","export class Timer {\n private _onTick: () => void;\n private _rafId: number | null = null;\n private _running = false;\n\n constructor(onTick: () => void) {\n this._onTick = onTick;\n }\n\n start(): void {\n if (this._running) return;\n this._running = true;\n this._scheduleFrame();\n }\n\n stop(): void {\n this._running = false;\n if (this._rafId !== null) {\n cancelAnimationFrame(this._rafId);\n this._rafId = null;\n }\n }\n\n private _scheduleFrame(): void {\n this._rafId = requestAnimationFrame(() => {\n if (!this._running) return;\n try {\n this._onTick();\n } catch (err) {\n console.warn('[waveform-playlist] Timer tick error:', String(err));\n }\n // Check _running again — _onTick() may have called stop()\n if (this._running) {\n this._scheduleFrame();\n }\n });\n }\n}\n","import type { Tick, Sample } from '../types';\nimport type { TempoMap } from './tempo-map';\n\nexport class SampleTimeline {\n private _sampleRate: number;\n private _tempoMap: TempoMap | null = null;\n\n constructor(sampleRate: number) {\n this._sampleRate = sampleRate;\n }\n\n get sampleRate(): number {\n return this._sampleRate;\n }\n\n setTempoMap(tempoMap: TempoMap): void {\n this._tempoMap = tempoMap;\n }\n\n samplesToSeconds(samples: Sample): number {\n return samples / this._sampleRate;\n }\n\n secondsToSamples(seconds: number): Sample {\n return Math.round(seconds * this._sampleRate) as Sample;\n }\n\n ticksToSamples(ticks: Tick): Sample {\n if (!this._tempoMap) {\n throw new Error(\n '[waveform-playlist] SampleTimeline: tempoMap not set — call setTempoMap() first'\n );\n }\n return Math.round(this._tempoMap.ticksToSeconds(ticks) * this._sampleRate) as Sample;\n }\n\n samplesToTicks(samples: Sample): Tick {\n if (!this._tempoMap) {\n throw new Error(\n '[waveform-playlist] SampleTimeline: tempoMap not set — call setTempoMap() first'\n );\n }\n return this._tempoMap.secondsToTicks(samples / this._sampleRate);\n }\n}\n","import type { Tick, TempoInterpolation } from '../types';\n\nconst CURVE_EPSILON = 1e-15;\n/** Number of subdivisions for trapezoidal integration over curved segments */\nconst CURVE_SUBDIVISIONS = 64;\n\n/**\n * Möbius-Ease curve: maps x in [0,1] to [0,1] with shape controlled by slope.\n * slope = 0.5 → linear. slope < 0.5 → concave. slope > 0.5 → convex.\n * Reference: http://werner.yellowcouch.org/Papers/fastenv12/index.html\n */\nfunction curveNormalizedAt(x: number, slope: number): number {\n if (slope > 0.499999 && slope < 0.500001) return x;\n const p = Math.max(CURVE_EPSILON, Math.min(1 - CURVE_EPSILON, slope));\n return ((p * p) / (1 - p * 2)) * (Math.pow((1 - p) / p, 2 * x) - 1);\n}\n\n/** Mutable internal version of TempoEntry (exported interface has readonly fields) */\ninterface MutableTempoEntry {\n tick: Tick;\n bpm: number;\n interpolation: TempoInterpolation;\n secondsAtTick: number;\n}\n\nexport interface SetTempoOptions {\n interpolation?: TempoInterpolation;\n}\n\nexport class TempoMap {\n private _ppqn: number;\n private _entries: MutableTempoEntry[];\n\n constructor(ppqn: number = 960, initialBpm: number = 120) {\n this._ppqn = ppqn;\n this._entries = [{ tick: 0 as Tick, bpm: initialBpm, interpolation: 'step', secondsAtTick: 0 }];\n }\n\n getTempo(atTick: Tick = 0 as Tick): number {\n return this._getTempoAt(atTick);\n }\n\n setTempo(bpm: number, atTick: Tick = 0 as Tick, options?: SetTempoOptions): void {\n const interpolation = options?.interpolation ?? 'step';\n\n if (typeof interpolation === 'object' && interpolation.type === 'curve') {\n const s = interpolation.slope;\n if (!Number.isFinite(s) || s <= 0 || s >= 1) {\n throw new Error(\n '[waveform-playlist] TempoMap: curve slope must be between 0 and 1 (exclusive), got ' + s\n );\n }\n }\n\n if (atTick === 0) {\n // First entry is always 'step' — there's no previous entry to ramp from\n this._entries[0] = { ...this._entries[0], bpm, interpolation: 'step' };\n this._recomputeCache(0);\n return;\n }\n // Find insertion point\n let i = this._entries.length - 1;\n while (i > 0 && this._entries[i].tick > atTick) i--;\n\n if (this._entries[i].tick === atTick) {\n this._entries[i] = { ...this._entries[i], bpm, interpolation };\n } else {\n const secondsAtTick = this._ticksToSecondsInternal(atTick);\n this._entries.splice(i + 1, 0, { tick: atTick, bpm, interpolation, secondsAtTick });\n i = i + 1;\n }\n this._recomputeCache(i);\n }\n\n ticksToSeconds(ticks: Tick): number {\n return this._ticksToSecondsInternal(ticks);\n }\n\n secondsToTicks(seconds: number): Tick {\n let lo = 0;\n let hi = this._entries.length - 1;\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1;\n if (this._entries[mid].secondsAtTick <= seconds) {\n lo = mid;\n } else {\n hi = mid - 1;\n }\n }\n const entry = this._entries[lo];\n const secondsIntoSegment = seconds - entry.secondsAtTick;\n\n const nextEntry = lo < this._entries.length - 1 ? this._entries[lo + 1] : null;\n if (nextEntry && nextEntry.interpolation === 'linear') {\n return Math.round(\n entry.tick +\n this._secondsToTicksLinear(\n secondsIntoSegment,\n entry.bpm,\n nextEntry.bpm,\n nextEntry.tick - entry.tick\n )\n ) as Tick;\n }\n\n if (nextEntry && typeof nextEntry.interpolation === 'object') {\n return Math.round(\n entry.tick +\n this._secondsToTicksCurve(\n secondsIntoSegment,\n entry.bpm,\n nextEntry.bpm,\n nextEntry.tick - entry.tick,\n nextEntry.interpolation.slope\n )\n ) as Tick;\n }\n\n // Step: constant BPM\n const ticksPerSecond = (entry.bpm / 60) * this._ppqn;\n return Math.round(entry.tick + secondsIntoSegment * ticksPerSecond) as Tick;\n }\n\n beatsToSeconds(beats: number): number {\n return this.ticksToSeconds((beats * this._ppqn) as Tick);\n }\n\n secondsToBeats(seconds: number): number {\n return this.secondsToTicks(seconds) / this._ppqn;\n }\n\n clearTempos(): void {\n const first = this._entries[0];\n this._entries = [{ tick: 0 as Tick, bpm: first.bpm, interpolation: 'step', secondsAtTick: 0 }];\n }\n\n /** Get the interpolated BPM at a tick position */\n private _getTempoAt(atTick: Tick): number {\n const entryIndex = this._entryIndexAt(atTick);\n const entry = this._entries[entryIndex];\n const nextEntry = entryIndex < this._entries.length - 1 ? this._entries[entryIndex + 1] : null;\n\n if (nextEntry && nextEntry.interpolation !== 'step') {\n const segmentTicks = nextEntry.tick - entry.tick;\n const ticksInto = atTick - entry.tick;\n if (segmentTicks > 0) {\n const progress = ticksInto / segmentTicks;\n if (nextEntry.interpolation === 'linear') {\n return entry.bpm + (nextEntry.bpm - entry.bpm) * progress;\n }\n // Curve (Möbius-Ease)\n const t = curveNormalizedAt(progress, nextEntry.interpolation.slope);\n return entry.bpm + (nextEntry.bpm - entry.bpm) * t;\n }\n }\n\n return entry.bpm;\n }\n\n private _ticksToSecondsInternal(ticks: Tick): number {\n const entryIndex = this._entryIndexAt(ticks);\n const entry = this._entries[entryIndex];\n const ticksIntoSegment = ticks - entry.tick;\n const nextEntry = entryIndex < this._entries.length - 1 ? this._entries[entryIndex + 1] : null;\n\n if (nextEntry && nextEntry.interpolation === 'linear') {\n const segmentTicks = nextEntry.tick - entry.tick;\n return (\n entry.secondsAtTick +\n this._ticksToSecondsLinear(ticksIntoSegment, entry.bpm, nextEntry.bpm, segmentTicks)\n );\n }\n\n if (nextEntry && typeof nextEntry.interpolation === 'object') {\n const segmentTicks = nextEntry.tick - entry.tick;\n return (\n entry.secondsAtTick +\n this._ticksToSecondsCurve(\n ticksIntoSegment,\n entry.bpm,\n nextEntry.bpm,\n segmentTicks,\n nextEntry.interpolation.slope\n )\n );\n }\n\n // Step: constant BPM\n const secondsPerTick = 60 / (entry.bpm * this._ppqn);\n return entry.secondsAtTick + ticksIntoSegment * secondsPerTick;\n }\n\n /**\n * Exact integration for a linear BPM ramp using the logarithmic formula.\n * For bpm(t) = bpm0 + r*t where r = (bpm1-bpm0)/T:\n * seconds = (T * 60) / (ppqn * (bpm1-bpm0)) * ln(bpmAtTick / bpm0)\n */\n private _ticksToSecondsLinear(\n ticks: number,\n bpm0: number,\n bpm1: number,\n totalSegmentTicks: number\n ): number {\n if (totalSegmentTicks === 0) return 0;\n const bpmAtTick = bpm0 + (bpm1 - bpm0) * (ticks / totalSegmentTicks);\n // Degenerate case: no ramp (avoids ln(1)/0 = 0/0)\n if (Math.abs(bpm1 - bpm0) < 1e-10) {\n return (ticks * 60) / (bpm0 * this._ppqn);\n }\n // Exact: ∫₀ᵗ 60/(ppqn * bpm(u)) du = (T * 60 / (ppqn * deltaBpm)) * ln(bpmAtTick/bpm0)\n const deltaBpm = bpm1 - bpm0;\n return ((totalSegmentTicks * 60) / (this._ppqn * deltaBpm)) * Math.log(bpmAtTick / bpm0);\n }\n\n /**\n * Inverse of _ticksToSecondsLinear: given seconds, return ticks.\n * Closed-form via exponential: bpmAtTick = bpm0 * exp(seconds * deltaBpm * ppqn / (60 * T))\n * then ticks = (bpmAtTick - bpm0) * T / deltaBpm\n *\n * Note: exp(log(x)) has ~1 ULP floating-point error, so round-trips depend on\n * Math.round() in the caller (secondsToTicks). This is sufficient for all tested\n * BPM ranges (10–300 BPM) but is not algebraically exact like the previous\n * trapezoidal/quadratic approach was.\n */\n private _secondsToTicksLinear(\n seconds: number,\n bpm0: number,\n bpm1: number,\n totalSegmentTicks: number\n ): number {\n if (totalSegmentTicks === 0 || seconds === 0) return 0;\n // Degenerate case: no ramp\n if (Math.abs(bpm1 - bpm0) < 1e-10) {\n return (seconds * bpm0 * this._ppqn) / 60;\n }\n const deltaBpm = bpm1 - bpm0;\n const bpmAtTick = bpm0 * Math.exp((seconds * deltaBpm * this._ppqn) / (60 * totalSegmentTicks));\n return ((bpmAtTick - bpm0) / deltaBpm) * totalSegmentTicks;\n }\n\n /**\n * Subdivided trapezoidal integration for a Möbius-Ease tempo curve.\n * The BPM at progress p is: bpm0 + curveNormalizedAt(p, slope) * (bpm1 - bpm0).\n * We subdivide into CURVE_SUBDIVISIONS intervals and apply trapezoidal rule.\n */\n private _ticksToSecondsCurve(\n ticks: number,\n bpm0: number,\n bpm1: number,\n totalSegmentTicks: number,\n slope: number\n ): number {\n if (totalSegmentTicks === 0 || ticks === 0) return 0;\n const n = CURVE_SUBDIVISIONS;\n const dt = ticks / n;\n let seconds = 0;\n let prevBpm = bpm0;\n for (let i = 1; i <= n; i++) {\n const progress = (dt * i) / totalSegmentTicks;\n const curBpm = bpm0 + curveNormalizedAt(progress, slope) * (bpm1 - bpm0);\n // Trapezoidal rule for this subdivision\n seconds += (((dt * 60) / this._ppqn) * (1 / prevBpm + 1 / curBpm)) / 2;\n prevBpm = curBpm;\n }\n return seconds;\n }\n\n /**\n * Inverse of _ticksToSecondsCurve: given seconds into a curved segment,\n * return ticks. Uses binary search since there's no closed-form inverse.\n */\n private _secondsToTicksCurve(\n seconds: number,\n bpm0: number,\n bpm1: number,\n totalSegmentTicks: number,\n slope: number\n ): number {\n if (totalSegmentTicks === 0 || seconds === 0) return 0;\n // Binary search: find ticks such that _ticksToSecondsCurve(ticks) ≈ seconds.\n // Need totalSegmentTicks / 2^N < 0.5 for Math.round() to land on the right\n // tick. Iterations = ceil(log2(2 * totalSegmentTicks)), clamped to [1, 40].\n const iterations = Math.min(40, Math.max(1, Math.ceil(Math.log2(2 * totalSegmentTicks))));\n let lo = 0;\n let hi = totalSegmentTicks;\n for (let i = 0; i < iterations; i++) {\n const mid = (lo + hi) / 2;\n const midSeconds = this._ticksToSecondsCurve(mid, bpm0, bpm1, totalSegmentTicks, slope);\n if (midSeconds < seconds) {\n lo = mid;\n } else {\n hi = mid;\n }\n }\n return (lo + hi) / 2;\n }\n\n private _entryIndexAt(tick: Tick): number {\n let lo = 0;\n let hi = this._entries.length - 1;\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1;\n if (this._entries[mid].tick <= tick) {\n lo = mid;\n } else {\n hi = mid - 1;\n }\n }\n return lo;\n }\n\n private _recomputeCache(fromIndex: number): void {\n for (let i = Math.max(1, fromIndex); i < this._entries.length; i++) {\n const prev = this._entries[i - 1];\n const tickDelta = this._entries[i].tick - prev.tick;\n const entry = this._entries[i];\n\n let segmentSeconds: number;\n if (entry.interpolation === 'linear') {\n segmentSeconds = this._ticksToSecondsLinear(tickDelta, prev.bpm, entry.bpm, tickDelta);\n } else if (typeof entry.interpolation === 'object') {\n segmentSeconds = this._ticksToSecondsCurve(\n tickDelta,\n prev.bpm,\n entry.bpm,\n tickDelta,\n entry.interpolation.slope\n );\n } else {\n // Step: constant BPM from previous entry\n const secondsPerTick = 60 / (prev.bpm * this._ppqn);\n segmentSeconds = tickDelta * secondsPerTick;\n }\n\n this._entries[i] = {\n ...entry,\n secondsAtTick: prev.secondsAtTick + segmentSeconds,\n };\n }\n }\n}\n","// packages/transport/src/timeline/meter-map.ts\nimport type { Tick, MeterEntry, MeterSignature } from '../types';\n\ninterface MutableMeterEntry {\n tick: Tick;\n numerator: number;\n denominator: number;\n barAtTick: number;\n}\n\nfunction isPowerOf2(n: number): boolean {\n return n > 0 && (n & (n - 1)) === 0;\n}\n\nexport class MeterMap {\n private _ppqn: number;\n private _entries: MutableMeterEntry[];\n\n constructor(ppqn: number, numerator: number = 4, denominator: number = 4) {\n this._ppqn = ppqn;\n this._entries = [{ tick: 0 as Tick, numerator, denominator, barAtTick: 0 }];\n }\n\n get ppqn(): number {\n return this._ppqn;\n }\n\n getMeter(atTick: Tick = 0 as Tick): MeterSignature {\n const entry = this._entryAt(atTick);\n return { numerator: entry.numerator, denominator: entry.denominator };\n }\n\n setMeter(numerator: number, denominator: number, atTick: Tick = 0 as Tick): void {\n this._validateMeter(numerator, denominator);\n\n if (atTick < 0) {\n throw new Error('[waveform-playlist] MeterMap: atTick must be non-negative, got ' + atTick);\n }\n\n if (atTick === 0) {\n this._entries[0] = { ...this._entries[0], numerator, denominator };\n // Re-snap downstream entries to bar boundaries of the new meter\n this._resnapDownstreamEntries(0);\n this._recomputeCache(0);\n return;\n }\n\n // Snap to bar boundary of preceding meter\n const snapped = this._snapToBarBoundary(atTick);\n if (snapped !== atTick) {\n console.warn(\n '[waveform-playlist] MeterMap.setMeter: tick ' +\n atTick +\n ' is not on a bar boundary, snapped to ' +\n snapped\n );\n }\n\n let i = this._entries.length - 1;\n while (i > 0 && this._entries[i].tick > snapped) i--;\n\n if (this._entries[i].tick === snapped) {\n this._entries[i] = { ...this._entries[i], numerator, denominator };\n } else {\n const barAtTick = this._computeBarAtTick(snapped);\n this._entries.splice(i + 1, 0, { tick: snapped as Tick, numerator, denominator, barAtTick });\n i = i + 1;\n }\n this._resnapDownstreamEntries(i);\n this._recomputeCache(i);\n }\n\n removeMeter(atTick: Tick): void {\n if (atTick === 0) {\n throw new Error('[waveform-playlist] MeterMap: cannot remove meter at tick 0');\n }\n const idx = this._entries.findIndex((e) => e.tick === atTick);\n if (idx > 0) {\n this._entries.splice(idx, 1);\n this._recomputeCache(idx);\n } else if (idx === -1) {\n console.warn('[waveform-playlist] MeterMap.removeMeter: no entry at tick ' + atTick);\n }\n }\n\n clearMeters(): void {\n const first = this._entries[0];\n this._entries = [{ ...first, barAtTick: 0 }];\n }\n\n ticksPerBeat(atTick: Tick = 0 as Tick): number {\n const entry = this._entryAt(atTick);\n return this._ppqn * (4 / entry.denominator);\n }\n\n ticksPerBar(atTick: Tick = 0 as Tick): number {\n const entry = this._entryAt(atTick);\n return entry.numerator * this._ppqn * (4 / entry.denominator);\n }\n\n barToTick(bar: number): Tick {\n if (bar < 1) {\n throw new Error('[waveform-playlist] MeterMap: bar must be >= 1, got ' + bar);\n }\n const targetBar = bar - 1; // 0-indexed\n for (let i = 0; i < this._entries.length; i++) {\n const nextBar = i < this._entries.length - 1 ? this._entries[i + 1].barAtTick : Infinity;\n if (targetBar < nextBar) {\n const barsInto = targetBar - this._entries[i].barAtTick;\n const tpb = this._ticksPerBarForEntry(this._entries[i]);\n return (this._entries[i].tick + barsInto * tpb) as Tick;\n }\n }\n return 0 as Tick; // unreachable — last iteration always matches (nextBar = Infinity)\n }\n\n tickToBar(tick: Tick): number {\n const entry = this._entryAt(tick);\n const ticksInto = tick - entry.tick;\n const tpb = this._ticksPerBarForEntry(entry);\n return entry.barAtTick + Math.floor(ticksInto / tpb) + 1; // 1-indexed\n }\n\n isBarBoundary(tick: Tick): boolean {\n const entry = this._entryAt(tick);\n const ticksInto = tick - entry.tick;\n const tpb = this._ticksPerBarForEntry(entry);\n return ticksInto % tpb === 0;\n }\n\n /** Internal: get the full entry at a tick (for MetronomePlayer beat grid anchoring) */\n getEntryAt(tick: Tick): MeterEntry {\n return this._entryAt(tick);\n }\n\n private _entryAt(tick: Tick): MutableMeterEntry {\n let lo = 0;\n let hi = this._entries.length - 1;\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1;\n if (this._entries[mid].tick <= tick) {\n lo = mid;\n } else {\n hi = mid - 1;\n }\n }\n return this._entries[lo];\n }\n\n private _ticksPerBarForEntry(entry: MutableMeterEntry): number {\n return entry.numerator * this._ppqn * (4 / entry.denominator);\n }\n\n private _snapToBarBoundary(atTick: Tick): Tick {\n const entry = this._entryAt(atTick);\n const tpb = this._ticksPerBarForEntry(entry);\n const ticksInto = atTick - entry.tick;\n if (ticksInto % tpb === 0) return atTick;\n // Snap forward to next bar boundary\n return (entry.tick + Math.ceil(ticksInto / tpb) * tpb) as Tick;\n }\n\n private _computeBarAtTick(tick: Tick): number {\n const entry = this._entryAt(tick);\n const ticksInto = tick - entry.tick;\n const tpb = this._ticksPerBarForEntry(entry);\n return entry.barAtTick + ticksInto / tpb;\n }\n\n private _recomputeCache(fromIndex: number): void {\n for (let i = Math.max(1, fromIndex); i < this._entries.length; i++) {\n const prev = this._entries[i - 1];\n const tickDelta = this._entries[i].tick - prev.tick;\n const tpb = this._ticksPerBarForEntry(prev);\n this._entries[i] = {\n ...this._entries[i],\n barAtTick: prev.barAtTick + tickDelta / tpb,\n };\n }\n }\n\n /**\n * After changing a meter entry, re-snap downstream entries to bar boundaries\n * of their preceding meter so barAtTick stays integer.\n */\n private _resnapDownstreamEntries(fromIndex: number): void {\n for (let i = Math.max(1, fromIndex + 1); i < this._entries.length; i++) {\n const prev = this._entries[i - 1];\n const tpb = this._ticksPerBarForEntry(prev);\n const tick = this._entries[i].tick;\n const ticksIntoPrev = tick - prev.tick;\n if (ticksIntoPrev % tpb !== 0) {\n const snapped = prev.tick + Math.ceil(ticksIntoPrev / tpb) * tpb;\n console.warn(\n '[waveform-playlist] MeterMap: meter change moved entry from tick ' +\n tick +\n ' to ' +\n snapped +\n ' (bar boundary alignment)'\n );\n this._entries[i] = { ...this._entries[i], tick: snapped as Tick };\n }\n }\n }\n\n private _validateMeter(numerator: number, denominator: number): void {\n if (!Number.isInteger(numerator) || numerator < 1 || numerator > 32) {\n throw new Error(\n '[waveform-playlist] MeterMap: numerator must be an integer 1-32, got ' + numerator\n );\n }\n if (!isPowerOf2(denominator) || denominator > 32) {\n throw new Error(\n '[waveform-playlist] MeterMap: denominator must be a power of 2 (1-32), got ' + denominator\n );\n }\n }\n}\n","export class MasterNode {\n private _gainNode: GainNode;\n\n constructor(audioContext: AudioContext) {\n this._gainNode = audioContext.createGain();\n }\n\n get input(): AudioNode {\n return this._gainNode;\n }\n\n get output(): AudioNode {\n return this._gainNode;\n }\n\n setVolume(value: number): void {\n this._gainNode.gain.value = value;\n }\n\n dispose(): void {\n try {\n this._gainNode.disconnect();\n } catch (err) {\n console.warn('[waveform-playlist] MasterNode.dispose: error disconnecting:', String(err));\n }\n }\n}\n","export class TrackNode {\n readonly id: string;\n private _volumeNode: GainNode;\n private _panNode: StereoPannerNode;\n private _muteNode: GainNode;\n private _destination: AudioNode | null = null;\n private _effectsInput: AudioNode | null = null;\n\n constructor(id: string, audioContext: AudioContext) {\n this.id = id;\n this._volumeNode = audioContext.createGain();\n this._panNode = audioContext.createStereoPanner();\n this._panNode.channelCount = 2;\n this._muteNode = audioContext.createGain();\n\n // Wire: volume → pan → mute (caller connects output via connectOutput)\n this._volumeNode.connect(this._panNode);\n this._panNode.connect(this._muteNode);\n }\n\n /** Where clip sources connect */\n get input(): GainNode {\n return this._volumeNode;\n }\n\n /** Connect this track's output to a destination (master node) */\n connectOutput(destination: AudioNode): void {\n this._destination = destination;\n this._muteNode.connect(destination);\n }\n\n setVolume(value: number): void {\n this._volumeNode.gain.value = value;\n }\n\n setPan(value: number): void {\n this._panNode.pan.value = value;\n }\n\n setMute(muted: boolean): void {\n this._muteNode.gain.value = muted ? 0 : 1;\n }\n\n connectEffects(effectsInput: AudioNode): void {\n // Clean up previous effects connection first\n if (this._effectsInput) {\n this.disconnectEffects();\n }\n // Disconnect mute from destination\n this._muteNode.disconnect();\n // Route mute → effects input\n this._muteNode.connect(effectsInput);\n this._effectsInput = effectsInput;\n }\n\n disconnectEffects(): void {\n if (this._effectsInput && this._destination) {\n this._muteNode.disconnect();\n // Restore direct routing: mute → destination\n this._muteNode.connect(this._destination);\n this._effectsInput = null;\n }\n }\n\n dispose(): void {\n for (const node of [this._volumeNode, this._panNode, this._muteNode]) {\n try {\n node.disconnect();\n } catch (err) {\n console.warn(\n '[waveform-playlist] TrackNode.dispose: error disconnecting node:',\n String(err)\n );\n }\n }\n }\n}\n","import type { ClipTrack, AudioClip } from '@waveform-playlist/core';\nimport type { Tick, Sample, SchedulerEvent, SchedulerListener } from '../types';\nimport type { SampleTimeline } from '../timeline/sample-timeline';\nimport type { TempoMap } from '../timeline/tempo-map';\nimport type { TrackNode } from './track-node';\n\nexport interface ClipEvent extends SchedulerEvent {\n trackId: string;\n clipId: string;\n audioBuffer: AudioBuffer;\n /** Clip position on timeline (integer samples) */\n startSample: Sample;\n /** Offset into audioBuffer (integer samples) */\n offsetSamples: Sample;\n /** Duration to play (integer samples) */\n durationSamples: Sample;\n /** Clip gain multiplier */\n gain: number;\n /** Fade in duration (integer samples) */\n fadeInDurationSamples: Sample;\n /** Fade out duration (integer samples) */\n fadeOutDurationSamples: Sample;\n}\n\ninterface TrackClipState {\n track: ClipTrack;\n clips: AudioClip[];\n}\n\nexport class ClipPlayer implements SchedulerListener<ClipEvent> {\n private _audioContext: AudioContext;\n private _sampleTimeline: SampleTimeline;\n private _tempoMap: TempoMap;\n private _toAudioTime: (transportTime: number) => number;\n private _tracks: Map<string, TrackClipState> = new Map();\n private _trackNodes: Map<string, TrackNode> = new Map();\n private _activeSources: Map<AudioBufferSourceNode, { trackId: string; gainNode: GainNode }> =\n new Map();\n private _loopEnabled = false;\n private _loopEndSamples = 0;\n\n constructor(\n audioContext: AudioContext,\n sampleTimeline: SampleTimeline,\n tempoMap: TempoMap,\n toAudioTime: (transportTime: number) => number\n ) {\n this._audioContext = audioContext;\n this._sampleTimeline = sampleTimeline;\n this._tempoMap = tempoMap;\n this._toAudioTime = toAudioTime;\n }\n\n setTracks(tracks: ClipTrack[], trackNodes: Map<string, TrackNode>): void {\n this._tracks.clear();\n this._trackNodes = trackNodes;\n for (const track of tracks) {\n this._tracks.set(track.id, { track, clips: track.clips });\n }\n }\n\n /** Set loop region using ticks. startTick is unused — loop clamping only needs\n * the end boundary; mid-clip restart at loopStart is handled by onPositionJump. */\n setLoop(enabled: boolean, _startTick: Tick, endTick: Tick): void {\n this._loopEnabled = enabled;\n this._loopEndSamples = this._sampleTimeline.ticksToSamples(endTick);\n }\n\n /** Set loop region using samples directly */\n setLoopSamples(enabled: boolean, _startSample: Sample, endSample: Sample): void {\n this._loopEnabled = enabled;\n this._loopEndSamples = endSample;\n }\n\n updateTrack(trackId: string, track: ClipTrack): void {\n this._tracks.set(trackId, { track, clips: track.clips });\n this._silenceTrack(trackId);\n }\n\n generate(fromTick: Tick, toTick: Tick): ClipEvent[] {\n const events: ClipEvent[] = [];\n\n for (const [trackId, state] of this._tracks) {\n for (const clip of state.clips) {\n if (clip.durationSamples === 0) continue;\n if (!clip.audioBuffer) continue;\n\n // Use startTick when available, fall back to sample-derived tick\n const clipTick: number =\n clip.startTick !== undefined\n ? clip.startTick\n : (this._sampleTimeline.samplesToTicks(clip.startSample as Sample) as number);\n\n // Only schedule when the clip START falls within this window.\n // Clips that started in a previous window are already playing\n // (AudioBufferSourceNode runs for its full duration).\n // Mid-clip starts (seek, loop wrap) are handled by onPositionJump().\n if (clipTick < (fromTick as number)) continue;\n if (clipTick >= (toTick as number)) continue;\n\n const fadeInDurationSamples = clip.fadeIn ? (clip.fadeIn.duration ?? 0) : 0;\n const fadeOutDurationSamples = clip.fadeOut ? (clip.fadeOut.duration ?? 0) : 0;\n\n // Clamp duration at loopEnd so the source stops exactly at the\n // loop boundary. onPositionJump handles the mid-clip restart.\n let durationSamples = clip.durationSamples;\n if (this._loopEnabled && clip.startSample + durationSamples > this._loopEndSamples) {\n durationSamples = this._loopEndSamples - clip.startSample;\n }\n\n events.push({\n trackId,\n clipId: clip.id,\n audioBuffer: clip.audioBuffer,\n tick: clipTick as Tick,\n startSample: clip.startSample as Sample,\n offsetSamples: clip.offsetSamples as Sample,\n durationSamples: durationSamples as Sample,\n gain: clip.gain,\n fadeInDurationSamples: fadeInDurationSamples as Sample,\n fadeOutDurationSamples: fadeOutDurationSamples as Sample,\n });\n }\n }\n\n return events;\n }\n\n consume(event: ClipEvent): void {\n const trackNode = this._trackNodes.get(event.trackId);\n if (!trackNode) {\n console.warn(\n '[waveform-playlist] ClipPlayer.consume: no TrackNode for trackId \"' +\n event.trackId +\n '\", clipId \"' +\n event.clipId +\n '\" — clip will not play'\n );\n return;\n }\n\n const sampleRate = this._sampleTimeline.sampleRate;\n const offsetSeconds = event.offsetSamples / sampleRate;\n const durationSeconds = event.durationSamples / sampleRate;\n\n // Guard against invalid offset\n if (offsetSeconds >= event.audioBuffer.duration) {\n console.warn(\n '[waveform-playlist] ClipPlayer.consume: offset (' +\n offsetSeconds +\n 's) exceeds audioBuffer.duration (' +\n event.audioBuffer.duration +\n 's) for clipId \"' +\n event.clipId +\n '\" — clip will not play'\n );\n return;\n }\n\n const source = this._audioContext.createBufferSource();\n source.buffer = event.audioBuffer;\n\n // Convert tick → seconds → AudioContext.currentTime for scheduling\n const transportSeconds = this._tempoMap.ticksToSeconds(event.tick);\n const when = this._toAudioTime(transportSeconds);\n\n // Create a gain node for per-clip gain and fades\n const gainNode = this._audioContext.createGain();\n gainNode.gain.value = event.gain;\n\n // Apply fades (AudioParam scheduling uses AudioContext time)\n // Clamp fades so they don't overlap (split duration evenly if they would)\n let fadeIn = event.fadeInDurationSamples / sampleRate;\n let fadeOut = event.fadeOutDurationSamples / sampleRate;\n if (fadeIn + fadeOut > durationSeconds) {\n const ratio = durationSeconds / (fadeIn + fadeOut);\n fadeIn *= ratio;\n fadeOut *= ratio;\n }\n\n if (fadeIn > 0) {\n gainNode.gain.setValueAtTime(0, when);\n gainNode.gain.linearRampToValueAtTime(event.gain, when + fadeIn);\n }\n if (fadeOut > 0) {\n const fadeOutStart = when + durationSeconds - fadeOut;\n gainNode.gain.setValueAtTime(event.gain, fadeOutStart);\n gainNode.gain.linearRampToValueAtTime(0, when + durationSeconds);\n }\n\n source.connect(gainNode);\n gainNode.connect(trackNode.input);\n\n this._activeSources.set(source, {\n trackId: event.trackId,\n gainNode,\n });\n\n // Clean up when source finishes\n source.addEventListener('ended', () => {\n this._activeSources.delete(source);\n try {\n gainNode.disconnect();\n } catch (err) {\n console.warn('[waveform-playlist] ClipPlayer: error disconnecting gain node:', String(err));\n }\n });\n\n source.start(when, offsetSeconds, durationSeconds);\n }\n\n onPositionJump(newTick: Tick): void {\n this.silence();\n\n const newSample = this._sampleTimeline.ticksToSamples(newTick);\n\n // Re-schedule mid-clip sources for clips that span the new position\n for (const [trackId, state] of this._tracks) {\n for (const clip of state.clips) {\n if (clip.durationSamples === 0) continue;\n if (!clip.audioBuffer) continue;\n\n // Start comparison in ticks (strict < to avoid double-scheduling)\n const clipTick: number =\n clip.startTick !== undefined\n ? clip.startTick\n : (this._sampleTimeline.samplesToTicks(clip.startSample as Sample) as number);\n\n if (clipTick >= (newTick as number)) continue; // hasn't started yet\n\n // End comparison in samples (duration is audio-file-relative)\n const clipEndSample = clip.startSample + clip.durationSamples;\n if (clipEndSample <= (newSample as number)) continue; // already finished\n\n const offsetIntoClipSamples = (newSample as number) - clip.startSample;\n const offsetSamples = clip.offsetSamples + offsetIntoClipSamples;\n let durationSamples = clipEndSample - (newSample as number);\n\n // Clamp at loop boundary (same as generate)\n if (this._loopEnabled && (newSample as number) + durationSamples > this._loopEndSamples) {\n durationSamples = this._loopEndSamples - (newSample as number);\n }\n if (durationSamples <= 0) continue;\n\n const fadeOutDurationSamples = clip.fadeOut ? (clip.fadeOut.duration ?? 0) : 0;\n\n this.consume({\n trackId,\n clipId: clip.id,\n audioBuffer: clip.audioBuffer,\n tick: newTick as Tick,\n startSample: newSample as Sample,\n offsetSamples: offsetSamples as Sample,\n durationSamples: durationSamples as Sample,\n gain: clip.gain,\n fadeInDurationSamples: 0 as Sample,\n fadeOutDurationSamples: fadeOutDurationSamples as Sample,\n });\n }\n }\n }\n\n silence(): void {\n for (const [source, { gainNode }] of this._activeSources) {\n try {\n source.stop();\n } catch (err) {\n console.warn('[waveform-playlist] ClipPlayer.silence: error stopping source:', String(err));\n }\n try {\n gainNode.disconnect();\n } catch (err) {\n console.warn('[waveform-playlist] ClipPlayer.silence: error disconnecting:', String(err));\n }\n }\n this._activeSources.clear();\n }\n\n private _silenceTrack(trackId: string): void {\n const toDelete: AudioBufferSourceNode[] = [];\n for (const [source, info] of this._activeSources) {\n if (info.trackId === trackId) {\n try {\n source.stop();\n } catch (err) {\n console.warn(\n '[waveform-playlist] ClipPlayer._silenceTrack: error stopping source:',\n String(err)\n );\n }\n try {\n info.gainNode.disconnect();\n } catch (err) {\n console.warn(\n '[waveform-playlist] ClipPlayer._silenceTrack: error disconnecting:',\n String(err)\n );\n }\n toDelete.push(source);\n }\n }\n for (const source of toDelete) {\n this._activeSources.delete(source);\n }\n }\n}\n","import type { Tick, SchedulerEvent, SchedulerListener } from '../types';\nimport type { TempoMap } from '../timeline/tempo-map';\nimport { MeterMap } from '../timeline/meter-map';\n\nexport interface MetronomeEvent extends SchedulerEvent {\n isAccent: boolean;\n buffer: AudioBuffer;\n}\n\nexport class MetronomePlayer implements SchedulerListener<MetronomeEvent> {\n private _audioContext: AudioContext;\n private _tempoMap: TempoMap;\n private _meterMap: MeterMap;\n private _destination: AudioNode;\n private _toAudioTime: (transportTime: number) => number;\n private _enabled = false;\n private _accentBuffer: AudioBuffer | null = null;\n private _normalBuffer: AudioBuffer | null = null;\n private _activeSources: Set<AudioBufferSourceNode> = new Set();\n\n constructor(\n audioContext: AudioContext,\n tempoMap: TempoMap,\n meterMap: MeterMap,\n destination: AudioNode,\n toAudioTime: (transportTime: number) => number\n ) {\n this._audioContext = audioContext;\n this._tempoMap = tempoMap;\n this._meterMap = meterMap;\n this._destination = destination;\n this._toAudioTime = toAudioTime;\n }\n\n setEnabled(enabled: boolean): void {\n this._enabled = enabled;\n if (!enabled) {\n this.silence();\n }\n }\n\n setClickSounds(accent: AudioBuffer, normal: AudioBuffer): void {\n this._accentBuffer = accent;\n this._normalBuffer = normal;\n }\n\n generate(fromTick: Tick, toTick: Tick): MetronomeEvent[] {\n if (!this._enabled || !this._accentBuffer || !this._normalBuffer) {\n return [];\n }\n\n const events: MetronomeEvent[] = [];\n\n // Snap to first beat: align to beat grid anchored at the active meter entry\n let entry = this._meterMap.getEntryAt(fromTick);\n let beatSize = this._meterMap.ticksPerBeat(fromTick);\n const tickIntoSection = fromTick - entry.tick;\n let tick = entry.tick + Math.ceil(tickIntoSection / beatSize) * beatSize;\n\n while (tick < toTick) {\n const tickPos = tick as Tick;\n // Re-snap at meter boundaries\n const currentEntry = this._meterMap.getEntryAt(tickPos);\n if (currentEntry.tick !== entry.tick) {\n entry = currentEntry;\n beatSize = this._meterMap.ticksPerBeat(tickPos);\n }\n\n const isAccent = this._meterMap.isBarBoundary(tickPos);\n\n events.push({\n tick: tickPos,\n isAccent,\n buffer: isAccent ? this._accentBuffer : this._normalBuffer,\n });\n\n beatSize = this._meterMap.ticksPerBeat(tickPos);\n tick += beatSize;\n }\n\n return events;\n }\n\n consume(event: MetronomeEvent): void {\n const source = this._audioContext.createBufferSource();\n source.buffer = event.buffer;\n source.connect(this._destination);\n\n this._activeSources.add(source);\n source.addEventListener('ended', () => {\n this._activeSources.delete(source);\n try {\n source.disconnect();\n } catch (err) {\n console.warn(\n '[waveform-playlist] MetronomePlayer: error disconnecting source:',\n String(err)\n );\n }\n });\n\n const transportTime = this._tempoMap.ticksToSeconds(event.tick);\n source.start(this._toAudioTime(transportTime));\n }\n\n onPositionJump(_newTick: Tick): void {\n // Don't silence — clicks are short one-shots that finish naturally.\n // Calling silence() here kills clicks scheduled in the lookahead window\n // that haven't played yet, causing the last beat before a loop wrap\n // to be cut off.\n }\n\n silence(): void {\n for (const source of this._activeSources) {\n try {\n source.stop();\n } catch (err) {\n console.warn(\n '[waveform-playlist] MetronomePlayer.silence: error stopping source:',\n String(err)\n );\n }\n try {\n source.disconnect();\n } catch (err) {\n console.warn(\n '[waveform-playlist] MetronomePlayer.silence: error disconnecting:',\n String(err)\n );\n }\n }\n this._activeSources.clear();\n }\n}\n","import type { Tick, SchedulerEvent, SchedulerListener } from '../types';\nimport type { TempoMap } from '../timeline/tempo-map';\nimport type { MeterMap } from '../timeline/meter-map';\n\nexport interface CountInEvent extends SchedulerEvent {\n isAccent: boolean;\n buffer: AudioBuffer;\n beat: number;\n totalBeats: number;\n}\n\ninterface CountInConfig {\n totalBeats: number;\n accentBuffer: AudioBuffer;\n normalBuffer: AudioBuffer;\n meterMap: MeterMap;\n /** TempoMap for tick→seconds conversion in consume(). Must match the\n * count-in scheduler's TempoMap (locked to BPM at the play position). */\n tempoMap: TempoMap;\n onBeat: (beat: number, totalBeats: number) => void;\n}\n\nexport class CountInPlayer implements SchedulerListener<CountInEvent> {\n private _audioContext: AudioContext;\n private _tempoMap: TempoMap;\n private _destination: AudioNode;\n private _toAudioTime: (transportTime: number) => number;\n private _activeSources: Set<AudioBufferSourceNode> = new Set();\n\n private _totalBeats = 0;\n private _beatsGenerated = 0;\n private _accentBuffer: AudioBuffer | null = null;\n private _normalBuffer: AudioBuffer | null = null;\n private _meterMap: MeterMap | null = null;\n private _onBeat: ((beat: number, totalBeats: number) => void) | null = null;\n\n constructor(\n audioContext: AudioContext,\n tempoMap: TempoMap,\n destination: AudioNode,\n toAudioTime: (transportTime: number) => number\n ) {\n this._audioContext = audioContext;\n this._tempoMap = tempoMap;\n this._destination = destination;\n this._toAudioTime = toAudioTime;\n }\n\n configure(config: CountInConfig): void {\n this._totalBeats = config.totalBeats;\n this._beatsGenerated = 0;\n this._accentBuffer = config.accentBuffer;\n this._normalBuffer = config.normalBuffer;\n this._meterMap = config.meterMap;\n this._tempoMap = config.tempoMap;\n this._onBeat = config.onBeat;\n }\n\n generate(fromTick: Tick, toTick: Tick): CountInEvent[] {\n if (!this._accentBuffer || !this._normalBuffer || !this._meterMap) {\n return [];\n }\n\n const events: CountInEvent[] = [];\n const meterMap = this._meterMap;\n\n // Walk the beat grid anchored at the active meter entry (same algorithm as MetronomePlayer)\n let entry = meterMap.getEntryAt(fromTick);\n let beatSize = meterMap.ticksPerBeat(fromTick);\n const tickIntoSection = fromTick - entry.tick;\n let tick = entry.tick + Math.ceil(tickIntoSection / beatSize) * beatSize;\n\n while (tick < toTick && this._beatsGenerated < this._totalBeats) {\n const tickPos = tick as Tick;\n\n // Re-snap at meter boundaries\n const currentEntry = meterMap.getEntryAt(tickPos);\n if (currentEntry.tick !== entry.tick) {\n entry = currentEntry;\n beatSize = meterMap.ticksPerBeat(tickPos);\n }\n\n this._beatsGenerated++;\n const isAccent = meterMap.isBarBoundary(tickPos);\n\n events.push({\n tick: tickPos,\n isAccent,\n buffer: isAccent ? this._accentBuffer : this._normalBuffer,\n beat: this._beatsGenerated,\n totalBeats: this._totalBeats,\n });\n\n beatSize = meterMap.ticksPerBeat(tickPos);\n tick += beatSize;\n }\n\n return events;\n }\n\n consume(event: CountInEvent): void {\n try {\n const source = this._audioContext.createBufferSource();\n source.buffer = event.buffer;\n source.connect(this._destination);\n\n this._activeSources.add(source);\n source.addEventListener('ended', () => {\n this._activeSources.delete(source);\n try {\n source.disconnect();\n } catch (err) {\n console.warn(\n '[waveform-playlist] CountInPlayer: error disconnecting source:',\n String(err)\n );\n }\n });\n\n const transportTime = this._tempoMap.ticksToSeconds(event.tick);\n source.start(this._toAudioTime(transportTime));\n } catch (err) {\n console.warn(\n '[waveform-playlist] CountInPlayer.consume: failed to schedule beat ' +\n event.beat +\n '/' +\n event.totalBeats +\n ': ' +\n String(err)\n );\n }\n\n // Beat callback stays outside try/catch so count-in events still\n // fire even if one beat fails to schedule audio.\n this._onBeat?.(event.beat, event.totalBeats);\n }\n\n onPositionJump(_newTick: Tick): void {\n // No-op — clicks are short one-shots that finish naturally.\n }\n\n silence(): void {\n for (const source of this._activeSources) {\n try {\n source.stop();\n } catch (err) {\n console.warn(\n '[waveform-playlist] CountInPlayer.silence: error stopping source:',\n String(err)\n );\n }\n try {\n source.disconnect();\n } catch (err) {\n console.warn(\n '[waveform-playlist] CountInPlayer.silence: error disconnecting:',\n String(err)\n );\n }\n }\n this._activeSources.clear();\n }\n}\n","export interface ClickSoundOptions {\n /** Frequency for accent click (beat 1). Default: 1000 Hz */\n accentFrequency?: number;\n /** Frequency for normal click (other beats). Default: 800 Hz */\n normalFrequency?: number;\n}\n\nconst DEFAULT_ACCENT_FREQUENCY = 1000;\nconst DEFAULT_NORMAL_FREQUENCY = 800;\nconst ACCENT_DURATION = 0.04; // 40ms\nconst NORMAL_DURATION = 0.03; // 30ms\n\nfunction synthesizeClick(\n audioContext: AudioContext,\n frequency: number,\n duration: number\n): AudioBuffer {\n const sampleRate = audioContext.sampleRate;\n const length = Math.ceil(sampleRate * duration);\n const buffer = audioContext.createBuffer(1, length, sampleRate);\n const data = buffer.getChannelData(0);\n\n for (let i = 0; i < length; i++) {\n const t = i / sampleRate;\n const envelope = Math.exp(-t * 50);\n data[i] = Math.sin(2 * Math.PI * frequency * t) * envelope;\n }\n\n return buffer;\n}\n\nexport function createDefaultClickSounds(\n audioContext: AudioContext,\n options?: ClickSoundOptions\n): { accent: AudioBuffer; normal: AudioBuffer } {\n const accentFreq = options?.accentFrequency ?? DEFAULT_ACCENT_FREQUENCY;\n const normalFreq = options?.normalFrequency ?? DEFAULT_NORMAL_FREQUENCY;\n\n if (accentFreq <= 0 || normalFreq <= 0) {\n console.warn(\n '[waveform-playlist] createDefaultClickSounds: frequency must be positive, got accent=' +\n accentFreq +\n ' normal=' +\n normalFreq\n );\n }\n\n return {\n accent: synthesizeClick(audioContext, accentFreq, ACCENT_DURATION),\n normal: synthesizeClick(audioContext, normalFreq, NORMAL_DURATION),\n };\n}\n","import type { ClipTrack } from '@waveform-playlist/core';\nimport type {\n Tick,\n Sample,\n TransportOptions,\n MeterSignature,\n CountInMode,\n CountInEventData,\n TempoChangeEventData,\n MeterChangeEventData,\n} from './types';\nimport type { SetTempoOptions } from './timeline/tempo-map';\nimport { Clock } from './core/clock';\nimport { Scheduler } from './core/scheduler';\nimport { Timer } from './core/timer';\nimport { SampleTimeline } from './timeline/sample-timeline';\nimport { TempoMap } from './timeline/tempo-map';\nimport { MeterMap } from './timeline/meter-map';\nimport { ClipPlayer } from './audio/clip-player';\nimport { MetronomePlayer } from './audio/metronome-player';\nimport { CountInPlayer } from './audio/count-in-player';\nimport { createDefaultClickSounds } from './audio/click-sounds';\nimport { MasterNode } from './audio/master-node';\nimport { TrackNode } from './audio/track-node';\n\nexport interface TransportEvents {\n play: () => void;\n pause: () => void;\n stop: () => void;\n loop: () => void;\n tempochange: (event: TempoChangeEventData) => void;\n meterchange: (event: MeterChangeEventData) => void;\n countIn: (event: CountInEventData) => void;\n countInEnd: () => void;\n}\n\ntype TransportEventType = keyof TransportEvents;\n\nexport class Transport {\n private _audioContext: AudioContext;\n private _clock: Clock;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private _scheduler: Scheduler<any>;\n private _timer: Timer;\n private _sampleTimeline: SampleTimeline;\n private _meterMap: MeterMap;\n private _tempoMap: TempoMap;\n private _clipPlayer!: ClipPlayer;\n private _metronomePlayer!: MetronomePlayer;\n private _masterNode!: MasterNode;\n private _trackNodes: Map<string, TrackNode> = new Map();\n private _tracks: ClipTrack[] = [];\n private _soloedTrackIds: Set<string> = new Set();\n private _mutedTrackIds: Set<string> = new Set();\n private _playing = false;\n private _endTime: number | undefined;\n private _loopEnabled = false;\n private _loopStartTick: Tick = 0 as Tick;\n private _loopStartSeconds = 0;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private _listeners: Map<TransportEventType, Set<(...args: any[]) => void>> = new Map();\n\n // --- Count-In state ---\n private _countInEnabled = false;\n private _countInBars = 1;\n private _countInMode: CountInMode = 'recording-only';\n private _recording = false;\n private _countingIn = false;\n private _countInStartPosition = 0;\n private _countInDuration = 0;\n private _countInPlayer!: CountInPlayer;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private _countInScheduler: Scheduler<any> | null = null;\n private _accentBuffer: AudioBuffer | null = null;\n private _normalBuffer: AudioBuffer | null = null;\n private _schedulerLookahead: number = 0.2;\n private _ppqn: number = 960;\n\n constructor(audioContext: AudioContext, options: TransportOptions = {}) {\n this._audioContext = audioContext;\n\n const sampleRate = options.sampleRate ?? audioContext.sampleRate;\n const ppqn = options.ppqn ?? 960;\n const tempo = options.tempo ?? 120;\n const numerator = options.numerator ?? 4;\n const denominator = options.denominator ?? 4;\n const lookahead = options.schedulerLookahead ?? 0.2;\n\n this._ppqn = ppqn;\n this._schedulerLookahead = lookahead;\n\n Transport._validateOptions(sampleRate, ppqn, tempo, numerator, denominator, lookahead);\n\n this._clock = new Clock(audioContext);\n this._sampleTimeline = new SampleTimeline(sampleRate);\n this._meterMap = new MeterMap(ppqn, numerator, denominator);\n this._tempoMap = new TempoMap(ppqn, tempo);\n\n this._scheduler = new Scheduler(this._tempoMap, {\n lookahead,\n onLoop: (loopStartSeconds: number, loopEndSeconds: number, currentTimeSeconds: number) => {\n // The wrap fires in the middle of advance(), which runs ahead of\n // real time by the lookahead. Post-wrap events use toAudioTime()\n // which reads the clock, so the seek target must place loopStart\n // at the audio-time of the boundary — not at \"now\".\n // Uses the currentTimeSeconds snapshot from advance() to avoid\n // re-reading the live AudioContext.currentTime (sub-ms drift).\n const timeToBoundary = loopEndSeconds - currentTimeSeconds;\n this._clock.seekTo(loopStartSeconds - timeToBoundary);\n },\n });\n this._sampleTimeline.setTempoMap(this._tempoMap);\n\n this._initAudioGraph(audioContext);\n this._initCountIn(audioContext, options);\n\n this._timer = new Timer(() => {\n const time = this._clock.getTime();\n if (this._countingIn) {\n this._countInScheduler?.advance(time);\n // Transition when the full bar duration has elapsed — not when the\n // last beat is consumed. This gives the last beat its full ring-out\n // and places the transition exactly at the bar boundary.\n if (time >= this._countInDuration) {\n this._finishCountIn();\n }\n return;\n }\n if (this._endTime !== undefined && time >= this._endTime) {\n this.stop();\n return;\n }\n this._scheduler.advance(time);\n });\n }\n\n get audioContext(): AudioContext {\n return this._audioContext;\n }\n\n get ppqn(): number {\n return this._ppqn;\n }\n\n // --- Playback ---\n\n play(startTime?: number, endTime?: number): void {\n if (this._playing) return;\n\n if (startTime !== undefined) {\n this._clock.seekTo(startTime);\n }\n\n // Check if count-in should activate\n if (this._shouldCountIn()) {\n this._startCountIn(endTime);\n return;\n }\n\n // Always reset scheduler to current position — after pause, the old\n // rightEdge is stale and clips whose startTime is before it won't\n // be picked up by generate().\n const currentTime = this._clock.getTime();\n this._scheduler.reset(currentTime);\n\n this._endTime = endTime;\n\n this._clock.start();\n\n // Re-create sources for clips spanning the current position.\n // After pause, silence() killed all active sources. generate() only\n // picks up clips whose startTime falls in the window, so clips that\n // started before the current position need mid-clip sources.\n const currentTick = this._tempoMap.secondsToTicks(currentTime);\n this._clipPlayer.onPositionJump(currentTick);\n\n this._timer.start();\n this._playing = true;\n this._emit('play');\n }\n\n pause(): void {\n if (!this._playing) return;\n\n if (this._countingIn) {\n this._cancelCountIn();\n this._clock.stop();\n this._playing = false;\n this._emit('pause');\n return;\n }\n\n this._timer.stop();\n this._clock.stop();\n this._silenceAll();\n this._playing = false;\n this._emit('pause');\n }\n\n stop(): void {\n const wasPlaying = this._playing;\n if (this._countingIn) {\n this._cancelCountIn();\n }\n this._timer.stop();\n this._clock.reset();\n this._scheduler.reset(0);\n this._silenceAll();\n this._playing = false;\n this._endTime = undefined;\n if (wasPlaying) {\n this._emit('stop');\n }\n }\n\n seek(time: number): void {\n const wasPlaying = this._playing;\n const wasCountingIn = this._countingIn;\n\n if (wasCountingIn) {\n this._cancelCountIn();\n this._playing = false;\n }\n\n if (wasPlaying && !wasCountingIn) {\n this._timer.stop();\n }\n\n this._silenceAll();\n this._clock.seekTo(time);\n this._scheduler.reset(time);\n // Clear stale endTime — seeking past a previous endTime shouldn't\n // cause immediate stop on the next play()\n this._endTime = undefined;\n\n // Resume playback only if was playing normally (not counting in)\n if (wasPlaying && !wasCountingIn) {\n this._clock.start();\n // Re-create sources for clips spanning the seek position\n const seekTick = this._tempoMap.secondsToTicks(time);\n this._clipPlayer.onPositionJump(seekTick);\n this._timer.start();\n }\n }\n\n getCurrentTime(): number {\n if (this._countingIn) {\n return this._countInStartPosition;\n }\n const t = this._clock.getTime();\n // After a loop wrap, the clock is briefly behind loopStart (the seek\n // target accounts for lookahead offset). Clamp for display purposes.\n if (this._loopEnabled && t < this._loopStartSeconds) {\n return this._loopStartSeconds;\n }\n return t;\n }\n\n isPlaying(): boolean {\n return this._playing;\n }\n\n // --- Tracks ---\n\n setTracks(tracks: ClipTrack[]): void {\n // Dispose existing track nodes\n for (const node of this._trackNodes.values()) {\n node.dispose();\n }\n this._trackNodes.clear();\n this._soloedTrackIds.clear();\n this._mutedTrackIds.clear();\n\n this._tracks = tracks;\n\n // Create track nodes\n for (const track of tracks) {\n const trackNode = new TrackNode(track.id, this._audioContext);\n trackNode.setVolume(track.volume);\n trackNode.setPan(track.pan);\n trackNode.connectOutput(this._masterNode.input);\n this._trackNodes.set(track.id, trackNode);\n\n if (track.muted) {\n this._mutedTrackIds.add(track.id);\n }\n if (track.soloed) {\n this._soloedTrackIds.add(track.id);\n }\n }\n\n this._applyMuteState();\n this._clipPlayer.setTracks(tracks, this._trackNodes);\n }\n\n addTrack(track: ClipTrack): void {\n const trackNode = new TrackNode(track.id, this._audioContext);\n trackNode.setVolume(track.volume);\n trackNode.setPan(track.pan);\n trackNode.connectOutput(this._masterNode.input);\n this._trackNodes.set(track.id, trackNode);\n\n if (track.muted) {\n this._mutedTrackIds.add(track.id);\n }\n if (track.soloed) {\n this._soloedTrackIds.add(track.id);\n }\n\n this._tracks = [...this._tracks, track];\n this._applyMuteState();\n this._clipPlayer.setTracks(this._tracks, this._trackNodes);\n }\n\n removeTrack(trackId: string): void {\n const node = this._trackNodes.get(trackId);\n if (node) {\n node.dispose();\n this._trackNodes.delete(trackId);\n }\n this._soloedTrackIds.delete(trackId);\n this._mutedTrackIds.delete(trackId);\n this._tracks = this._tracks.filter((t) => t.id !== trackId);\n this._applyMuteState();\n this._clipPlayer.setTracks(this._tracks, this._trackNodes);\n }\n\n updateTrack(trackId: string, track: ClipTrack): void {\n this._tracks = this._tracks.map((t) => (t.id === trackId ? track : t));\n\n const node = this._trackNodes.get(trackId);\n if (node) {\n node.setVolume(track.volume);\n node.setPan(track.pan);\n }\n\n // Update mute/solo\n if (track.muted) {\n this._mutedTrackIds.add(trackId);\n } else {\n this._mutedTrackIds.delete(trackId);\n }\n if (track.soloed) {\n this._soloedTrackIds.add(trackId);\n } else {\n this._soloedTrackIds.delete(trackId);\n }\n\n this._applyMuteState();\n this._clipPlayer.updateTrack(trackId, track);\n }\n\n // --- Track Controls ---\n\n setTrackVolume(trackId: string, volume: number): void {\n const node = this._trackNodes.get(trackId);\n if (!node) {\n console.warn('[waveform-playlist] setTrackVolume: unknown trackId \"' + trackId + '\"');\n return;\n }\n node.setVolume(volume);\n }\n\n setTrackPan(trackId: string, pan: number): void {\n const node = this._trackNodes.get(trackId);\n if (!node) {\n console.warn('[waveform-playlist] setTrackPan: unknown trackId \"' + trackId + '\"');\n return;\n }\n node.setPan(pan);\n }\n\n setTrackMute(trackId: string, muted: boolean): void {\n if (muted) {\n this._mutedTrackIds.add(trackId);\n } else {\n this._mutedTrackIds.delete(trackId);\n }\n this._applyMuteState();\n }\n\n setTrackSolo(trackId: string, soloed: boolean): void {\n if (soloed) {\n this._soloedTrackIds.add(trackId);\n } else {\n this._soloedTrackIds.delete(trackId);\n }\n this._applyMuteState();\n }\n\n // --- Master ---\n\n setMasterVolume(volume: number): void {\n this._masterNode.setVolume(volume);\n }\n\n // --- Loop ---\n\n /** Primary loop API — ticks as source of truth */\n setLoop(enabled: boolean, startTick: Tick, endTick: Tick): void {\n if (enabled && startTick >= endTick) {\n console.warn(\n '[waveform-playlist] Transport.setLoop: startTick (' +\n startTick +\n ') must be less than endTick (' +\n endTick +\n ')'\n );\n return;\n }\n this._loopEnabled = enabled;\n this._loopStartTick = startTick;\n this._loopStartSeconds = this._tempoMap.ticksToSeconds(startTick);\n this._scheduler.setLoop(enabled, startTick, endTick);\n this._clipPlayer.setLoop(enabled, startTick, endTick);\n this._emit('loop');\n }\n\n /** Convenience — converts seconds to ticks */\n setLoopSeconds(enabled: boolean, startSec: number, endSec: number): void {\n const startTick = this._tempoMap.secondsToTicks(startSec);\n const endTick = this._tempoMap.secondsToTicks(endSec);\n this.setLoop(enabled, startTick, endTick);\n }\n\n /** Convenience — sets loop in samples */\n setLoopSamples(enabled: boolean, startSample: Sample, endSample: Sample): void {\n if (enabled && (!Number.isFinite(startSample) || !Number.isFinite(endSample))) {\n console.warn(\n '[waveform-playlist] Transport.setLoopSamples: non-finite sample values (' +\n startSample +\n ', ' +\n endSample +\n ')'\n );\n return;\n }\n if (enabled && startSample >= endSample) {\n console.warn(\n '[waveform-playlist] Transport.setLoopSamples: startSample (' +\n startSample +\n ') must be less than endSample (' +\n endSample +\n ')'\n );\n return;\n }\n const startTick = this._sampleTimeline.samplesToTicks(startSample);\n const endTick = this._sampleTimeline.samplesToTicks(endSample);\n this._loopEnabled = enabled;\n this._loopStartTick = startTick;\n this._loopStartSeconds = this._tempoMap.ticksToSeconds(startTick);\n this._clipPlayer.setLoopSamples(enabled, startSample, endSample);\n this._scheduler.setLoop(enabled, startTick, endTick);\n this._emit('loop');\n }\n\n // --- Tempo ---\n\n setTempo(bpm: number, atTick?: Tick, options?: SetTempoOptions): void {\n this._tempoMap.setTempo(bpm, atTick, options);\n // Recompute cached loop start — tempo change invalidates tick→seconds mapping\n if (this._loopEnabled) {\n this._loopStartSeconds = this._tempoMap.ticksToSeconds(this._loopStartTick);\n }\n this._emit('tempochange', { bpm, atTick: atTick ?? (0 as Tick) });\n }\n\n getTempo(atTick?: Tick): number {\n return this._tempoMap.getTempo(atTick);\n }\n\n // --- Meter ---\n\n setMeter(numerator: number, denominator: number, atTick?: Tick): void {\n this._meterMap.setMeter(numerator, denominator, atTick);\n this._emit('meterchange', { numerator, denominator, atTick: atTick ?? (0 as Tick) });\n }\n\n getMeter(atTick?: Tick): MeterSignature {\n return this._meterMap.getMeter(atTick);\n }\n\n removeMeter(atTick: Tick): void {\n this._meterMap.removeMeter(atTick);\n const meter = this._meterMap.getMeter(atTick);\n this._emit('meterchange', {\n numerator: meter.numerator,\n denominator: meter.denominator,\n atTick,\n });\n }\n\n clearMeters(): void {\n this._meterMap.clearMeters();\n const meter = this._meterMap.getMeter();\n this._emit('meterchange', {\n numerator: meter.numerator,\n denominator: meter.denominator,\n atTick: 0 as Tick,\n });\n }\n\n clearTempos(): void {\n this._tempoMap.clearTempos();\n if (this._loopEnabled) {\n this._loopStartSeconds = this._tempoMap.ticksToSeconds(this._loopStartTick);\n }\n this._emit('tempochange', { bpm: this._tempoMap.getTempo(), atTick: 0 as Tick });\n }\n\n barToTick(bar: number): Tick {\n return this._meterMap.barToTick(bar);\n }\n\n tickToBar(tick: Tick): number {\n return this._meterMap.tickToBar(tick);\n }\n\n /** Convert transport time (seconds) to tick position, using the tempo map. */\n timeToTick(seconds: number): Tick {\n return this._tempoMap.secondsToTicks(seconds);\n }\n\n /** Convert tick position to transport time (seconds), using the tempo map. */\n tickToTime(tick: Tick): number {\n return this._tempoMap.ticksToSeconds(tick);\n }\n\n // --- Metronome ---\n\n setMetronomeEnabled(enabled: boolean): void {\n this._metronomePlayer.setEnabled(enabled);\n }\n\n setMetronomeClickSounds(accent: AudioBuffer, normal: AudioBuffer): void {\n this._accentBuffer = accent;\n this._normalBuffer = normal;\n this._metronomePlayer.setClickSounds(accent, normal);\n }\n\n // --- Count-In ---\n\n static readonly MIN_COUNT_IN_BARS = 1;\n static readonly MAX_COUNT_IN_BARS = 8;\n\n setCountIn(enabled: boolean): void {\n this._countInEnabled = enabled;\n }\n\n setCountInBars(bars: number): void {\n const rounded = Math.round(bars);\n if (rounded < Transport.MIN_COUNT_IN_BARS) {\n console.warn(\n '[waveform-playlist] Transport.setCountInBars: clamping ' +\n bars +\n ' to ' +\n Transport.MIN_COUNT_IN_BARS\n );\n this._countInBars = Transport.MIN_COUNT_IN_BARS;\n return;\n }\n if (rounded > Transport.MAX_COUNT_IN_BARS) {\n console.warn(\n '[waveform-playlist] Transport.setCountInBars: clamping ' +\n bars +\n ' to ' +\n Transport.MAX_COUNT_IN_BARS\n );\n this._countInBars = Transport.MAX_COUNT_IN_BARS;\n return;\n }\n this._countInBars = rounded;\n }\n\n setCountInMode(mode: CountInMode): void {\n this._countInMode = mode;\n }\n\n setRecording(recording: boolean): void {\n this._recording = recording;\n }\n\n isCountingIn(): boolean {\n return this._countingIn;\n }\n\n // --- Effects Hook ---\n\n connectTrackOutput(trackId: string, node: AudioNode): void {\n const trackNode = this._trackNodes.get(trackId);\n if (!trackNode) {\n console.warn('[waveform-playlist] connectTrackOutput: unknown trackId \"' + trackId + '\"');\n return;\n }\n trackNode.connectEffects(node);\n }\n\n disconnectTrackOutput(trackId: string): void {\n const trackNode = this._trackNodes.get(trackId);\n if (!trackNode) {\n console.warn('[waveform-playlist] disconnectTrackOutput: unknown trackId \"' + trackId + '\"');\n return;\n }\n trackNode.disconnectEffects();\n }\n\n // --- Events ---\n\n on<K extends TransportEventType>(event: K, cb: TransportEvents[K]): void {\n if (!this._listeners.has(event)) {\n this._listeners.set(event, new Set());\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n this._listeners.get(event)!.add(cb as (...args: any[]) => void);\n }\n\n off<K extends TransportEventType>(event: K, cb: TransportEvents[K]): void {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n this._listeners.get(event)?.delete(cb as (...args: any[]) => void);\n }\n\n // --- Dispose ---\n\n dispose(): void {\n this.stop();\n for (const node of this._trackNodes.values()) {\n node.dispose();\n }\n this._trackNodes.clear();\n this._masterNode.dispose();\n this._listeners.clear();\n }\n\n // --- Private ---\n\n private static _validateOptions(\n sampleRate: number,\n ppqn: number,\n tempo: number,\n numerator: number,\n denominator: number,\n lookahead: number\n ): void {\n if (sampleRate <= 0) {\n throw new Error(\n '[waveform-playlist] Transport: sampleRate must be positive, got ' + sampleRate\n );\n }\n if (ppqn <= 0 || !Number.isInteger(ppqn)) {\n throw new Error(\n '[waveform-playlist] Transport: ppqn must be a positive integer, got ' + ppqn\n );\n }\n if (tempo <= 0) {\n throw new Error('[waveform-playlist] Transport: tempo must be positive, got ' + tempo);\n }\n if (!Number.isInteger(numerator) || numerator < 1 || numerator > 32) {\n throw new Error(\n '[waveform-playlist] Transport: numerator must be an integer 1-32, got ' + numerator\n );\n }\n if (denominator <= 0 || (denominator & (denominator - 1)) !== 0 || denominator > 32) {\n throw new Error(\n '[waveform-playlist] Transport: denominator must be a power of 2 (1-32), got ' + denominator\n );\n }\n if (lookahead <= 0) {\n throw new Error(\n '[waveform-playlist] Transport: schedulerLookahead must be positive, got ' + lookahead\n );\n }\n }\n\n private _initAudioGraph(audioContext: AudioContext): void {\n this._masterNode = new MasterNode(audioContext);\n this._masterNode.output.connect(audioContext.destination);\n\n const toAudioTime = (transportTime: number) => this._clock.toAudioTime(transportTime);\n\n this._clipPlayer = new ClipPlayer(\n audioContext,\n this._sampleTimeline,\n this._tempoMap,\n toAudioTime\n );\n this._metronomePlayer = new MetronomePlayer(\n audioContext,\n this._tempoMap,\n this._meterMap,\n this._masterNode.input,\n toAudioTime\n );\n\n this._scheduler.addListener(this._clipPlayer);\n this._scheduler.addListener(this._metronomePlayer);\n }\n\n private _initCountIn(audioContext: AudioContext, options: TransportOptions): void {\n const toAudioTime = (transportTime: number) => this._clock.toAudioTime(transportTime);\n this._countInPlayer = new CountInPlayer(\n audioContext,\n this._tempoMap,\n this._masterNode.input,\n toAudioTime\n );\n\n try {\n const { accent, normal } = createDefaultClickSounds(audioContext, {\n accentFrequency: options.accentFrequency,\n normalFrequency: options.normalFrequency,\n });\n this._accentBuffer = accent;\n this._normalBuffer = normal;\n this._metronomePlayer.setClickSounds(accent, normal);\n } catch (err) {\n console.warn(\n '[waveform-playlist] Transport: failed to create default click sounds. ' +\n 'Metronome and count-in will be silent until setMetronomeClickSounds() is called. ' +\n 'Error: ' +\n String(err)\n );\n }\n }\n\n private _shouldCountIn(): boolean {\n if (!this._countInEnabled) return false;\n if (!this._accentBuffer || !this._normalBuffer) {\n console.warn('[waveform-playlist] Transport: count-in skipped — no click sounds loaded');\n return false;\n }\n if (this._countInMode === 'recording-only' && !this._recording) return false;\n return true;\n }\n\n private _startCountIn(endTime?: number): void {\n const currentTime = this._clock.getTime();\n this._countInStartPosition = currentTime;\n this._countingIn = true;\n this._playing = true;\n this._endTime = endTime;\n\n const playPositionTick = this._tempoMap.secondsToTicks(currentTime);\n const meter = this._meterMap.getMeter(playPositionTick);\n const totalBeats = meter.numerator * this._countInBars;\n\n // Calculate full bar duration in seconds — transition happens at the bar\n // boundary, not when the last beat is consumed. This gives the last beat\n // its full ring-out time.\n const bpmAtPosition = this._tempoMap.getTempo(playPositionTick);\n const ticksPerBeat = this._ppqn * (4 / meter.denominator);\n const countInTicks = totalBeats * ticksPerBeat;\n const countInTempoMap = new TempoMap(this._ppqn, bpmAtPosition);\n // Dedicated MeterMap locked to the meter at the play position — same\n // isolation as countInTempoMap. The count-in scheduler's tick space starts\n // at 0, so the main MeterMap's entries would be misinterpreted.\n const countInMeterMap = new MeterMap(this._ppqn, meter.numerator, meter.denominator);\n this._countInDuration = countInTempoMap.ticksToSeconds(countInTicks as Tick);\n\n this._countInScheduler = new Scheduler(countInTempoMap, {\n lookahead: this._schedulerLookahead,\n });\n\n this._countInPlayer.configure({\n totalBeats,\n accentBuffer: this._accentBuffer!,\n normalBuffer: this._normalBuffer!,\n meterMap: countInMeterMap,\n tempoMap: countInTempoMap,\n onBeat: (beat, total) => {\n this._emit('countIn', { beat, totalBeats: total });\n },\n });\n\n this._countInScheduler.addListener(this._countInPlayer);\n this._countInScheduler.reset(0);\n this._clock.seekTo(0);\n this._clock.start();\n // Use the main timer to drive the count-in scheduler (avoids spawning a second rAF chain)\n this._timer.start();\n }\n\n private _finishCountIn(): void {\n this._countInScheduler?.removeListener(this._countInPlayer);\n this._countInScheduler = null;\n // Don't silence — clicks are short one-shots (~40ms) that finish naturally.\n this._countingIn = false;\n this._emit('countInEnd');\n\n // Transition to normal playback at original position.\n // The timer is already running (driving the count-in scheduler via the\n // _countingIn branch). Setting _countingIn=false above makes the next\n // tick fall through to the main scheduler branch — no stop/restart needed.\n this._clock.seekTo(this._countInStartPosition);\n const currentTime = this._clock.getTime();\n this._scheduler.reset(currentTime);\n const currentTick = this._tempoMap.secondsToTicks(currentTime);\n this._clipPlayer.onPositionJump(currentTick);\n this._emit('play');\n }\n\n private _cancelCountIn(): void {\n this._countInPlayer.silence();\n this._countInScheduler?.removeListener(this._countInPlayer);\n this._countInScheduler = null;\n this._countingIn = false;\n // Stop the main timer (which was driving the count-in scheduler)\n this._timer.stop();\n }\n\n private _silenceAll(): void {\n this._clipPlayer.silence();\n this._metronomePlayer.silence();\n this._countInPlayer.silence();\n }\n\n private _applyMuteState(): void {\n const hasSolo = this._soloedTrackIds.size > 0;\n\n for (const [trackId, node] of this._trackNodes) {\n const isExplicitlyMuted = this._mutedTrackIds.has(trackId);\n const isSoloMuted = hasSolo && !this._soloedTrackIds.has(trackId);\n\n // Explicit mute takes precedence — a track that is both soloed AND muted stays muted\n node.setMute(isExplicitlyMuted || isSoloMuted);\n }\n }\n\n private _emit<K extends TransportEventType>(\n event: K,\n ...args: Parameters<TransportEvents[K]>\n ): void {\n const listeners = this._listeners.get(event);\n if (listeners) {\n for (const cb of listeners) {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (cb as (...a: any[]) => void)(...args);\n } catch (err) {\n console.warn(\n '[waveform-playlist] Transport \"' + event + '\" listener threw:',\n String(err)\n );\n }\n }\n }\n }\n}\n","import type { ClipTrack } from '@waveform-playlist/core';\nimport type { PlayoutAdapter } from '@waveform-playlist/engine';\nimport { Transport } from './transport';\nimport type { Tick, TransportOptions, CountInMode } from './types';\n\nexport class NativePlayoutAdapter implements PlayoutAdapter {\n private _transport: Transport;\n private _audioContext: AudioContext;\n\n constructor(audioContext: AudioContext, options?: TransportOptions) {\n this._audioContext = audioContext;\n this._transport = new Transport(audioContext, options);\n }\n\n get audioContext(): AudioContext {\n return this._audioContext;\n }\n\n get ppqn(): number {\n return this._transport.ppqn;\n }\n\n get transport(): Transport {\n return this._transport;\n }\n\n async init(): Promise<void> {\n if (this._audioContext.state === 'closed') {\n throw new Error('[waveform-playlist] Cannot init: AudioContext is closed');\n }\n if (this._audioContext.state === 'suspended') {\n await this._audioContext.resume();\n // Safari's audio thread may not be ready to output audio immediately\n // after resume() resolves. Wait for currentTime to advance past the\n // output latency, indicating the hardware pipeline is warm.\n // Minimum warmup ensures the audio thread has time to spin up even\n // when outputLatency reports 0 (Chrome on low-latency hardware).\n const MIN_WARMUP = 0.02; // 20ms\n const warmupTarget = Math.max(MIN_WARMUP, this._audioContext.outputLatency ?? MIN_WARMUP);\n if (this._audioContext.currentTime < warmupTarget) {\n const MAX_WARMUP_MS = 2000;\n await new Promise<void>((resolve) => {\n const startMs = performance.now();\n const check = () => {\n if (this._audioContext.currentTime >= warmupTarget) {\n resolve();\n } else if (\n this._audioContext.state === 'closed' ||\n performance.now() - startMs > MAX_WARMUP_MS\n ) {\n console.warn(\n '[waveform-playlist] AudioContext warmup timed out' +\n ' (currentTime=' +\n this._audioContext.currentTime +\n ', target=' +\n warmupTarget +\n ', state=' +\n this._audioContext.state +\n '). Proceeding without warmup.'\n );\n resolve();\n } else {\n requestAnimationFrame(check);\n }\n };\n requestAnimationFrame(check);\n });\n }\n }\n }\n\n setTracks(tracks: ClipTrack[]): void {\n this._transport.setTracks(tracks);\n }\n\n addTrack(track: ClipTrack): void {\n this._transport.addTrack(track);\n }\n\n removeTrack(trackId: string): void {\n this._transport.removeTrack(trackId);\n }\n\n updateTrack(trackId: string, track: ClipTrack): void {\n this._transport.updateTrack(trackId, track);\n }\n\n play(startTime: number, endTime?: number): void {\n this._transport.play(startTime, endTime);\n }\n\n pause(): void {\n this._transport.pause();\n }\n\n stop(): void {\n this._transport.stop();\n }\n\n seek(time: number): void {\n this._transport.seek(time);\n }\n\n getCurrentTime(): number {\n return this._transport.getCurrentTime();\n }\n\n isPlaying(): boolean {\n return this._transport.isPlaying();\n }\n\n setMasterVolume(volume: number): void {\n this._transport.setMasterVolume(volume);\n }\n\n setTrackVolume(trackId: string, volume: number): void {\n this._transport.setTrackVolume(trackId, volume);\n }\n\n setTrackMute(trackId: string, muted: boolean): void {\n this._transport.setTrackMute(trackId, muted);\n }\n\n setTrackSolo(trackId: string, soloed: boolean): void {\n this._transport.setTrackSolo(trackId, soloed);\n }\n\n setTrackPan(trackId: string, pan: number): void {\n this._transport.setTrackPan(trackId, pan);\n }\n\n setLoop(enabled: boolean, start: number, end: number): void {\n this._transport.setLoopSeconds(enabled, start, end);\n }\n\n setCountIn(enabled: boolean): void {\n this._transport.setCountIn(enabled);\n }\n\n setCountInBars(bars: number): void {\n this._transport.setCountInBars(bars);\n }\n\n setCountInMode(mode: CountInMode): void {\n this._transport.setCountInMode(mode);\n }\n\n setRecording(recording: boolean): void {\n this._transport.setRecording(recording);\n }\n\n isCountingIn(): boolean {\n return this._transport.isCountingIn();\n }\n\n setTempo(bpm: number, atTick?: number): void {\n this._transport.setTempo(bpm, atTick !== undefined ? (atTick as Tick) : undefined);\n }\n\n setMeter(numerator: number, denominator: number, atTick?: number): void {\n this._transport.setMeter(\n numerator,\n denominator,\n atTick !== undefined ? (atTick as Tick) : undefined\n );\n }\n\n ticksToSeconds(tick: number): number {\n return this._transport.tickToTime(tick as Tick);\n }\n\n secondsToTicks(seconds: number): number {\n return this._transport.timeToTick(seconds) as number;\n }\n\n dispose(): void {\n this._transport.dispose();\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/core/clock.ts","../src/core/scheduler.ts","../src/core/timer.ts","../src/timeline/sample-timeline.ts","../src/timeline/tempo-map.ts","../src/timeline/meter-map.ts","../src/audio/master-node.ts","../src/audio/track-node.ts","../src/audio/clip-player.ts","../src/audio/metronome-player.ts","../src/audio/count-in-player.ts","../src/audio/click-sounds.ts","../src/transport.ts","../src/adapter.ts"],"sourcesContent":["// packages/transport/src/index.ts\nexport type {\n Tick,\n Sample,\n SchedulerEvent,\n SchedulerListener,\n TransportOptions,\n TempoEntry,\n TempoInterpolation,\n TransportPosition,\n MeterSignature,\n MeterEntry,\n CountInMode,\n CountInEventData,\n TempoChangeEventData,\n MeterChangeEventData,\n} from './types';\n\nexport { Clock } from './core/clock';\nexport { Scheduler, type SchedulerOptions } from './core/scheduler';\nexport { Timer } from './core/timer';\nexport { SampleTimeline } from './timeline/sample-timeline';\nexport { TempoMap, type SetTempoOptions } from './timeline/tempo-map';\nexport { MeterMap } from './timeline/meter-map';\nexport { MasterNode } from './audio/master-node';\nexport { TrackNode } from './audio/track-node';\nexport { ClipPlayer, type ClipEvent } from './audio/clip-player';\nexport { MetronomePlayer, type MetronomeEvent } from './audio/metronome-player';\nexport type { CountInEvent } from './audio/count-in-player';\nexport type { ClickSoundOptions } from './audio/click-sounds';\nexport { Transport, type TransportEvents } from './transport';\nexport { NativePlayoutAdapter } from './adapter';\n","export class Clock {\n private _audioContext: AudioContext;\n private _running = false;\n private _audioTimeAtStart = 0;\n private _clockTimeAtStart = 0;\n\n constructor(audioContext: AudioContext) {\n this._audioContext = audioContext;\n }\n\n start(): void {\n if (this._running) return;\n this._audioTimeAtStart = this._audioContext.currentTime;\n this._running = true;\n }\n\n stop(): void {\n if (!this._running) return;\n this._clockTimeAtStart = this.getTime();\n this._running = false;\n }\n\n reset(): void {\n this._running = false;\n this._clockTimeAtStart = 0;\n this._audioTimeAtStart = 0;\n }\n\n getTime(): number {\n if (this._running) {\n return this._clockTimeAtStart + (this._audioContext.currentTime - this._audioTimeAtStart);\n }\n return this._clockTimeAtStart;\n }\n\n seekTo(time: number): void {\n if (this._running) {\n this._clockTimeAtStart = time;\n this._audioTimeAtStart = this._audioContext.currentTime;\n } else {\n this._clockTimeAtStart = time;\n }\n }\n\n /**\n * Convert transport time to AudioContext.currentTime space.\n * Used by players to schedule AudioBufferSourceNode.start(when).\n */\n toAudioTime(transportTime: number): number {\n return this._audioContext.currentTime + (transportTime - this.getTime());\n }\n\n isRunning(): boolean {\n return this._running;\n }\n}\n","import type { Tick, SchedulerEvent, SchedulerListener } from '../types';\nimport type { TempoMap } from '../timeline/tempo-map';\n\nexport interface SchedulerOptions {\n lookahead?: number;\n /** Called when the scheduler wraps at loopEnd.\n * Receives loopStart, loopEnd, and the currentTimeSeconds snapshot from\n * advance() so the Transport can compute the correct clock seek target\n * without re-reading the live AudioContext.currentTime. */\n onLoop?: (loopStartSeconds: number, loopEndSeconds: number, currentTimeSeconds: number) => void;\n}\n\nexport class Scheduler<T extends SchedulerEvent> {\n private _lookahead: number;\n private _rightEdge = 0; // integer ticks\n private _listeners: Set<SchedulerListener<T>> = new Set();\n private _loopEnabled = false;\n private _loopStart = 0; // integer ticks\n private _loopEnd = 0; // integer ticks\n private _onLoop:\n | ((loopStartSeconds: number, loopEndSeconds: number, currentTimeSeconds: number) => void)\n | undefined;\n private _tempoMap: TempoMap;\n\n constructor(tempoMap: TempoMap, options: SchedulerOptions = {}) {\n this._tempoMap = tempoMap;\n this._lookahead = options.lookahead ?? 0.2;\n this._onLoop = options.onLoop;\n }\n\n addListener(listener: SchedulerListener<T>): void {\n this._listeners.add(listener);\n }\n\n removeListener(listener: SchedulerListener<T>): void {\n this._listeners.delete(listener);\n }\n\n /** Primary API — ticks as source of truth */\n setLoop(enabled: boolean, startTick: Tick, endTick: Tick): void {\n if (enabled && (!Number.isFinite(startTick) || !Number.isFinite(endTick))) {\n console.warn(\n '[waveform-playlist] Scheduler.setLoop: non-finite tick values (' +\n startTick +\n ', ' +\n endTick +\n ')'\n );\n return;\n }\n if (enabled && startTick >= endTick) {\n console.warn(\n '[waveform-playlist] Scheduler.setLoop: startTick (' +\n startTick +\n ') must be less than endTick (' +\n endTick +\n ')'\n );\n return;\n }\n this._loopEnabled = enabled;\n this._loopStart = Math.round(startTick);\n this._loopEnd = Math.round(endTick);\n }\n\n /** Convenience — converts seconds to ticks via TempoMap */\n setLoopSeconds(enabled: boolean, startSec: number, endSec: number): void {\n const startTick = this._tempoMap.secondsToTicks(startSec);\n const endTick = this._tempoMap.secondsToTicks(endSec);\n this.setLoop(enabled, startTick, endTick);\n }\n\n /** Reset scheduling cursor. Takes seconds (from Clock), converts to ticks. */\n reset(timeSeconds: number): void {\n this._rightEdge = this._tempoMap.secondsToTicks(timeSeconds);\n }\n\n /** Advance the scheduling window. Takes seconds (from Clock), converts to ticks. */\n advance(currentTimeSeconds: number): void {\n const targetTick = this._tempoMap.secondsToTicks(currentTimeSeconds + this._lookahead);\n\n if (this._loopEnabled && this._loopEnd > this._loopStart) {\n const loopDuration = this._loopEnd - this._loopStart;\n let remaining = targetTick - this._rightEdge;\n\n while (remaining > 0) {\n const distToEnd = this._loopEnd - this._rightEdge;\n if (distToEnd <= 0 || distToEnd > remaining) {\n this._generateAndConsume(this._rightEdge, this._rightEdge + remaining);\n this._rightEdge += remaining;\n break;\n }\n // Generate up to loopEnd\n this._generateAndConsume(this._rightEdge, this._loopEnd);\n remaining -= distToEnd;\n // Notify listeners of position jump (in ticks)\n for (const listener of this._listeners) {\n listener.onPositionJump(this._loopStart as Tick);\n }\n // Seek clock — passes the currentTimeSeconds snapshot so Transport\n // uses the same clock reading as advance(), not a live re-read.\n this._onLoop?.(\n this._tempoMap.ticksToSeconds(this._loopStart as Tick),\n this._tempoMap.ticksToSeconds(this._loopEnd as Tick),\n currentTimeSeconds\n );\n this._rightEdge = this._loopStart;\n\n // Guard against infinite loop\n if (loopDuration <= 0) break;\n }\n return;\n }\n\n if (targetTick > this._rightEdge) {\n this._generateAndConsume(this._rightEdge, targetTick);\n this._rightEdge = targetTick;\n }\n }\n\n private _generateAndConsume(fromTick: number, toTick: number): void {\n for (const listener of this._listeners) {\n try {\n const events = listener.generate(fromTick as Tick, toTick as Tick);\n for (const event of events) {\n try {\n listener.consume(event);\n } catch (err) {\n console.warn('[waveform-playlist] Scheduler: error consuming event:', String(err));\n }\n }\n } catch (err) {\n console.warn('[waveform-playlist] Scheduler: error generating events:', String(err));\n }\n }\n }\n}\n","export class Timer {\n private _onTick: () => void;\n private _rafId: number | null = null;\n private _running = false;\n\n constructor(onTick: () => void) {\n this._onTick = onTick;\n }\n\n start(): void {\n if (this._running) return;\n this._running = true;\n this._scheduleFrame();\n }\n\n stop(): void {\n this._running = false;\n if (this._rafId !== null) {\n cancelAnimationFrame(this._rafId);\n this._rafId = null;\n }\n }\n\n private _scheduleFrame(): void {\n this._rafId = requestAnimationFrame(() => {\n if (!this._running) return;\n try {\n this._onTick();\n } catch (err) {\n console.warn('[waveform-playlist] Timer tick error:', String(err));\n }\n // Check _running again — _onTick() may have called stop()\n if (this._running) {\n this._scheduleFrame();\n }\n });\n }\n}\n","import type { Tick, Sample } from '../types';\nimport type { TempoMap } from './tempo-map';\n\nexport class SampleTimeline {\n private _sampleRate: number;\n private _tempoMap: TempoMap | null = null;\n\n constructor(sampleRate: number) {\n this._sampleRate = sampleRate;\n }\n\n get sampleRate(): number {\n return this._sampleRate;\n }\n\n setTempoMap(tempoMap: TempoMap): void {\n this._tempoMap = tempoMap;\n }\n\n samplesToSeconds(samples: Sample): number {\n return samples / this._sampleRate;\n }\n\n secondsToSamples(seconds: number): Sample {\n return Math.round(seconds * this._sampleRate) as Sample;\n }\n\n ticksToSamples(ticks: Tick): Sample {\n if (!this._tempoMap) {\n throw new Error(\n '[waveform-playlist] SampleTimeline: tempoMap not set — call setTempoMap() first'\n );\n }\n return Math.round(this._tempoMap.ticksToSeconds(ticks) * this._sampleRate) as Sample;\n }\n\n samplesToTicks(samples: Sample): Tick {\n if (!this._tempoMap) {\n throw new Error(\n '[waveform-playlist] SampleTimeline: tempoMap not set — call setTempoMap() first'\n );\n }\n return this._tempoMap.secondsToTicks(samples / this._sampleRate);\n }\n}\n","import type { Tick, TempoInterpolation } from '../types';\n\nconst CURVE_EPSILON = 1e-15;\n/** Number of subdivisions for trapezoidal integration over curved segments */\nconst CURVE_SUBDIVISIONS = 64;\n\n/**\n * Möbius-Ease curve: maps x in [0,1] to [0,1] with shape controlled by slope.\n * slope = 0.5 → linear. slope < 0.5 → concave. slope > 0.5 → convex.\n * Reference: http://werner.yellowcouch.org/Papers/fastenv12/index.html\n */\nfunction curveNormalizedAt(x: number, slope: number): number {\n if (slope > 0.499999 && slope < 0.500001) return x;\n const p = Math.max(CURVE_EPSILON, Math.min(1 - CURVE_EPSILON, slope));\n return ((p * p) / (1 - p * 2)) * (Math.pow((1 - p) / p, 2 * x) - 1);\n}\n\n/** Mutable internal version of TempoEntry (exported interface has readonly fields) */\ninterface MutableTempoEntry {\n tick: Tick;\n bpm: number;\n interpolation: TempoInterpolation;\n secondsAtTick: number;\n}\n\nexport interface SetTempoOptions {\n interpolation?: TempoInterpolation;\n}\n\nexport class TempoMap {\n private _ppqn: number;\n private _entries: MutableTempoEntry[];\n\n constructor(ppqn: number = 960, initialBpm: number = 120) {\n TempoMap._validateBpm(initialBpm);\n this._ppqn = ppqn;\n this._entries = [{ tick: 0 as Tick, bpm: initialBpm, interpolation: 'step', secondsAtTick: 0 }];\n }\n\n /** A non-finite or non-positive BPM silently corrupts the secondsAtTick\n * cache (Infinity, or non-monotonic values that break the binary search\n * in secondsToTicks) — reject it at the boundary instead. */\n private static _validateBpm(bpm: number): void {\n if (!Number.isFinite(bpm) || bpm <= 0) {\n throw new Error(\n '[waveform-playlist] TempoMap: bpm must be a finite positive number, got ' + bpm\n );\n }\n }\n\n getTempo(atTick: Tick = 0 as Tick): number {\n return this._getTempoAt(atTick);\n }\n\n setTempo(bpm: number, atTick: Tick = 0 as Tick, options?: SetTempoOptions): void {\n TempoMap._validateBpm(bpm);\n const interpolation = options?.interpolation ?? 'step';\n\n if (typeof interpolation === 'object' && interpolation.type === 'curve') {\n const s = interpolation.slope;\n if (!Number.isFinite(s) || s <= 0 || s >= 1) {\n throw new Error(\n '[waveform-playlist] TempoMap: curve slope must be between 0 and 1 (exclusive), got ' + s\n );\n }\n }\n\n if (atTick === 0) {\n // First entry is always 'step' — there's no previous entry to ramp from\n this._entries[0] = { ...this._entries[0], bpm, interpolation: 'step' };\n this._recomputeCache(0);\n return;\n }\n // Find insertion point\n let i = this._entries.length - 1;\n while (i > 0 && this._entries[i].tick > atTick) i--;\n\n if (this._entries[i].tick === atTick) {\n this._entries[i] = { ...this._entries[i], bpm, interpolation };\n } else {\n const secondsAtTick = this._ticksToSecondsInternal(atTick);\n this._entries.splice(i + 1, 0, { tick: atTick, bpm, interpolation, secondsAtTick });\n i = i + 1;\n }\n this._recomputeCache(i);\n }\n\n ticksToSeconds(ticks: Tick): number {\n return this._ticksToSecondsInternal(ticks);\n }\n\n secondsToTicks(seconds: number): Tick {\n let lo = 0;\n let hi = this._entries.length - 1;\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1;\n if (this._entries[mid].secondsAtTick <= seconds) {\n lo = mid;\n } else {\n hi = mid - 1;\n }\n }\n const entry = this._entries[lo];\n const secondsIntoSegment = seconds - entry.secondsAtTick;\n\n const nextEntry = lo < this._entries.length - 1 ? this._entries[lo + 1] : null;\n if (nextEntry && nextEntry.interpolation === 'linear') {\n return Math.round(\n entry.tick +\n this._secondsToTicksLinear(\n secondsIntoSegment,\n entry.bpm,\n nextEntry.bpm,\n nextEntry.tick - entry.tick\n )\n ) as Tick;\n }\n\n if (nextEntry && typeof nextEntry.interpolation === 'object') {\n return Math.round(\n entry.tick +\n this._secondsToTicksCurve(\n secondsIntoSegment,\n entry.bpm,\n nextEntry.bpm,\n nextEntry.tick - entry.tick,\n nextEntry.interpolation.slope\n )\n ) as Tick;\n }\n\n // Step: constant BPM\n const ticksPerSecond = (entry.bpm / 60) * this._ppqn;\n return Math.round(entry.tick + secondsIntoSegment * ticksPerSecond) as Tick;\n }\n\n beatsToSeconds(beats: number): number {\n return this.ticksToSeconds((beats * this._ppqn) as Tick);\n }\n\n secondsToBeats(seconds: number): number {\n return this.secondsToTicks(seconds) / this._ppqn;\n }\n\n clearTempos(): void {\n const first = this._entries[0];\n this._entries = [{ tick: 0 as Tick, bpm: first.bpm, interpolation: 'step', secondsAtTick: 0 }];\n }\n\n /** Get the interpolated BPM at a tick position */\n private _getTempoAt(atTick: Tick): number {\n const entryIndex = this._entryIndexAt(atTick);\n const entry = this._entries[entryIndex];\n const nextEntry = entryIndex < this._entries.length - 1 ? this._entries[entryIndex + 1] : null;\n\n if (nextEntry && nextEntry.interpolation !== 'step') {\n const segmentTicks = nextEntry.tick - entry.tick;\n const ticksInto = atTick - entry.tick;\n if (segmentTicks > 0) {\n const progress = ticksInto / segmentTicks;\n if (nextEntry.interpolation === 'linear') {\n return entry.bpm + (nextEntry.bpm - entry.bpm) * progress;\n }\n // Curve (Möbius-Ease)\n const t = curveNormalizedAt(progress, nextEntry.interpolation.slope);\n return entry.bpm + (nextEntry.bpm - entry.bpm) * t;\n }\n }\n\n return entry.bpm;\n }\n\n private _ticksToSecondsInternal(ticks: Tick): number {\n const entryIndex = this._entryIndexAt(ticks);\n const entry = this._entries[entryIndex];\n const ticksIntoSegment = ticks - entry.tick;\n const nextEntry = entryIndex < this._entries.length - 1 ? this._entries[entryIndex + 1] : null;\n\n if (nextEntry && nextEntry.interpolation === 'linear') {\n const segmentTicks = nextEntry.tick - entry.tick;\n return (\n entry.secondsAtTick +\n this._ticksToSecondsLinear(ticksIntoSegment, entry.bpm, nextEntry.bpm, segmentTicks)\n );\n }\n\n if (nextEntry && typeof nextEntry.interpolation === 'object') {\n const segmentTicks = nextEntry.tick - entry.tick;\n return (\n entry.secondsAtTick +\n this._ticksToSecondsCurve(\n ticksIntoSegment,\n entry.bpm,\n nextEntry.bpm,\n segmentTicks,\n nextEntry.interpolation.slope\n )\n );\n }\n\n // Step: constant BPM\n const secondsPerTick = 60 / (entry.bpm * this._ppqn);\n return entry.secondsAtTick + ticksIntoSegment * secondsPerTick;\n }\n\n /**\n * Exact integration for a linear BPM ramp using the logarithmic formula.\n * For bpm(t) = bpm0 + r*t where r = (bpm1-bpm0)/T:\n * seconds = (T * 60) / (ppqn * (bpm1-bpm0)) * ln(bpmAtTick / bpm0)\n */\n private _ticksToSecondsLinear(\n ticks: number,\n bpm0: number,\n bpm1: number,\n totalSegmentTicks: number\n ): number {\n if (totalSegmentTicks === 0) return 0;\n const bpmAtTick = bpm0 + (bpm1 - bpm0) * (ticks / totalSegmentTicks);\n // Degenerate case: no ramp (avoids ln(1)/0 = 0/0)\n if (Math.abs(bpm1 - bpm0) < 1e-10) {\n return (ticks * 60) / (bpm0 * this._ppqn);\n }\n // Exact: ∫₀ᵗ 60/(ppqn * bpm(u)) du = (T * 60 / (ppqn * deltaBpm)) * ln(bpmAtTick/bpm0)\n const deltaBpm = bpm1 - bpm0;\n return ((totalSegmentTicks * 60) / (this._ppqn * deltaBpm)) * Math.log(bpmAtTick / bpm0);\n }\n\n /**\n * Inverse of _ticksToSecondsLinear: given seconds, return ticks.\n * Closed-form via exponential: bpmAtTick = bpm0 * exp(seconds * deltaBpm * ppqn / (60 * T))\n * then ticks = (bpmAtTick - bpm0) * T / deltaBpm\n *\n * Note: exp(log(x)) has ~1 ULP floating-point error, so round-trips depend on\n * Math.round() in the caller (secondsToTicks). This is sufficient for all tested\n * BPM ranges (10–300 BPM) but is not algebraically exact like the previous\n * trapezoidal/quadratic approach was.\n */\n private _secondsToTicksLinear(\n seconds: number,\n bpm0: number,\n bpm1: number,\n totalSegmentTicks: number\n ): number {\n if (totalSegmentTicks === 0 || seconds === 0) return 0;\n // Degenerate case: no ramp\n if (Math.abs(bpm1 - bpm0) < 1e-10) {\n return (seconds * bpm0 * this._ppqn) / 60;\n }\n const deltaBpm = bpm1 - bpm0;\n const bpmAtTick = bpm0 * Math.exp((seconds * deltaBpm * this._ppqn) / (60 * totalSegmentTicks));\n return ((bpmAtTick - bpm0) / deltaBpm) * totalSegmentTicks;\n }\n\n /**\n * Subdivided trapezoidal integration for a Möbius-Ease tempo curve.\n * The BPM at progress p is: bpm0 + curveNormalizedAt(p, slope) * (bpm1 - bpm0).\n * We subdivide into CURVE_SUBDIVISIONS intervals and apply trapezoidal rule.\n */\n private _ticksToSecondsCurve(\n ticks: number,\n bpm0: number,\n bpm1: number,\n totalSegmentTicks: number,\n slope: number\n ): number {\n if (totalSegmentTicks === 0 || ticks === 0) return 0;\n const n = CURVE_SUBDIVISIONS;\n const dt = ticks / n;\n let seconds = 0;\n let prevBpm = bpm0;\n for (let i = 1; i <= n; i++) {\n const progress = (dt * i) / totalSegmentTicks;\n const curBpm = bpm0 + curveNormalizedAt(progress, slope) * (bpm1 - bpm0);\n // Trapezoidal rule for this subdivision\n seconds += (((dt * 60) / this._ppqn) * (1 / prevBpm + 1 / curBpm)) / 2;\n prevBpm = curBpm;\n }\n return seconds;\n }\n\n /**\n * Inverse of _ticksToSecondsCurve: given seconds into a curved segment,\n * return ticks. Uses binary search since there's no closed-form inverse.\n */\n private _secondsToTicksCurve(\n seconds: number,\n bpm0: number,\n bpm1: number,\n totalSegmentTicks: number,\n slope: number\n ): number {\n if (totalSegmentTicks === 0 || seconds === 0) return 0;\n // Binary search: find ticks such that _ticksToSecondsCurve(ticks) ≈ seconds.\n // Need totalSegmentTicks / 2^N < 0.5 for Math.round() to land on the right\n // tick. Iterations = ceil(log2(2 * totalSegmentTicks)), clamped to [1, 40].\n const iterations = Math.min(40, Math.max(1, Math.ceil(Math.log2(2 * totalSegmentTicks))));\n let lo = 0;\n let hi = totalSegmentTicks;\n for (let i = 0; i < iterations; i++) {\n const mid = (lo + hi) / 2;\n const midSeconds = this._ticksToSecondsCurve(mid, bpm0, bpm1, totalSegmentTicks, slope);\n if (midSeconds < seconds) {\n lo = mid;\n } else {\n hi = mid;\n }\n }\n return (lo + hi) / 2;\n }\n\n private _entryIndexAt(tick: Tick): number {\n let lo = 0;\n let hi = this._entries.length - 1;\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1;\n if (this._entries[mid].tick <= tick) {\n lo = mid;\n } else {\n hi = mid - 1;\n }\n }\n return lo;\n }\n\n private _recomputeCache(fromIndex: number): void {\n for (let i = Math.max(1, fromIndex); i < this._entries.length; i++) {\n const prev = this._entries[i - 1];\n const tickDelta = this._entries[i].tick - prev.tick;\n const entry = this._entries[i];\n\n let segmentSeconds: number;\n if (entry.interpolation === 'linear') {\n segmentSeconds = this._ticksToSecondsLinear(tickDelta, prev.bpm, entry.bpm, tickDelta);\n } else if (typeof entry.interpolation === 'object') {\n segmentSeconds = this._ticksToSecondsCurve(\n tickDelta,\n prev.bpm,\n entry.bpm,\n tickDelta,\n entry.interpolation.slope\n );\n } else {\n // Step: constant BPM from previous entry\n const secondsPerTick = 60 / (prev.bpm * this._ppqn);\n segmentSeconds = tickDelta * secondsPerTick;\n }\n\n this._entries[i] = {\n ...entry,\n secondsAtTick: prev.secondsAtTick + segmentSeconds,\n };\n }\n }\n}\n","// packages/transport/src/timeline/meter-map.ts\nimport type { Tick, MeterEntry, MeterSignature } from '../types';\n\ninterface MutableMeterEntry {\n tick: Tick;\n numerator: number;\n denominator: number;\n barAtTick: number;\n}\n\nfunction isPowerOf2(n: number): boolean {\n return n > 0 && (n & (n - 1)) === 0;\n}\n\nexport class MeterMap {\n private _ppqn: number;\n private _entries: MutableMeterEntry[];\n\n constructor(ppqn: number, numerator: number = 4, denominator: number = 4) {\n this._ppqn = ppqn;\n // Same gate setMeter() applies — without it, an invalid initial meter\n // (numerator 0, non-power-of-2 denominator) silently corrupts every\n // bar computation that follows.\n this._validateMeter(numerator, denominator);\n this._entries = [{ tick: 0 as Tick, numerator, denominator, barAtTick: 0 }];\n }\n\n get ppqn(): number {\n return this._ppqn;\n }\n\n getMeter(atTick: Tick = 0 as Tick): MeterSignature {\n const entry = this._entryAt(atTick);\n return { numerator: entry.numerator, denominator: entry.denominator };\n }\n\n setMeter(numerator: number, denominator: number, atTick: Tick = 0 as Tick): void {\n this._validateMeter(numerator, denominator);\n\n if (atTick < 0) {\n throw new Error('[waveform-playlist] MeterMap: atTick must be non-negative, got ' + atTick);\n }\n\n if (atTick === 0) {\n this._entries[0] = { ...this._entries[0], numerator, denominator };\n // Re-snap downstream entries to bar boundaries of the new meter\n this._resnapDownstreamEntries(0);\n this._recomputeCache(0);\n return;\n }\n\n // Snap to bar boundary of preceding meter\n const snapped = this._snapToBarBoundary(atTick);\n if (snapped !== atTick) {\n console.warn(\n '[waveform-playlist] MeterMap.setMeter: tick ' +\n atTick +\n ' is not on a bar boundary, snapped to ' +\n snapped\n );\n }\n\n let i = this._entries.length - 1;\n while (i > 0 && this._entries[i].tick > snapped) i--;\n\n if (this._entries[i].tick === snapped) {\n this._entries[i] = { ...this._entries[i], numerator, denominator };\n } else {\n const barAtTick = this._computeBarAtTick(snapped);\n this._entries.splice(i + 1, 0, { tick: snapped as Tick, numerator, denominator, barAtTick });\n i = i + 1;\n }\n this._resnapDownstreamEntries(i);\n this._recomputeCache(i);\n }\n\n removeMeter(atTick: Tick): void {\n if (atTick === 0) {\n throw new Error('[waveform-playlist] MeterMap: cannot remove meter at tick 0');\n }\n const idx = this._entries.findIndex((e) => e.tick === atTick);\n if (idx > 0) {\n this._entries.splice(idx, 1);\n this._recomputeCache(idx);\n } else if (idx === -1) {\n console.warn('[waveform-playlist] MeterMap.removeMeter: no entry at tick ' + atTick);\n }\n }\n\n clearMeters(): void {\n const first = this._entries[0];\n this._entries = [{ ...first, barAtTick: 0 }];\n }\n\n ticksPerBeat(atTick: Tick = 0 as Tick): number {\n const entry = this._entryAt(atTick);\n return this._ppqn * (4 / entry.denominator);\n }\n\n ticksPerBar(atTick: Tick = 0 as Tick): number {\n const entry = this._entryAt(atTick);\n return entry.numerator * this._ppqn * (4 / entry.denominator);\n }\n\n barToTick(bar: number): Tick {\n if (bar < 1) {\n throw new Error('[waveform-playlist] MeterMap: bar must be >= 1, got ' + bar);\n }\n const targetBar = bar - 1; // 0-indexed\n for (let i = 0; i < this._entries.length; i++) {\n const nextBar = i < this._entries.length - 1 ? this._entries[i + 1].barAtTick : Infinity;\n if (targetBar < nextBar) {\n const barsInto = targetBar - this._entries[i].barAtTick;\n const tpb = this._ticksPerBarForEntry(this._entries[i]);\n return (this._entries[i].tick + barsInto * tpb) as Tick;\n }\n }\n return 0 as Tick; // unreachable — last iteration always matches (nextBar = Infinity)\n }\n\n tickToBar(tick: Tick): number {\n const entry = this._entryAt(tick);\n const ticksInto = tick - entry.tick;\n const tpb = this._ticksPerBarForEntry(entry);\n return entry.barAtTick + Math.floor(ticksInto / tpb) + 1; // 1-indexed\n }\n\n isBarBoundary(tick: Tick): boolean {\n const entry = this._entryAt(tick);\n const ticksInto = tick - entry.tick;\n const tpb = this._ticksPerBarForEntry(entry);\n return ticksInto % tpb === 0;\n }\n\n /** Internal: get the full entry at a tick (for MetronomePlayer beat grid anchoring) */\n getEntryAt(tick: Tick): MeterEntry {\n return this._entryAt(tick);\n }\n\n private _entryAt(tick: Tick): MutableMeterEntry {\n let lo = 0;\n let hi = this._entries.length - 1;\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1;\n if (this._entries[mid].tick <= tick) {\n lo = mid;\n } else {\n hi = mid - 1;\n }\n }\n return this._entries[lo];\n }\n\n private _ticksPerBarForEntry(entry: MutableMeterEntry): number {\n return entry.numerator * this._ppqn * (4 / entry.denominator);\n }\n\n private _snapToBarBoundary(atTick: Tick): Tick {\n const entry = this._entryAt(atTick);\n const tpb = this._ticksPerBarForEntry(entry);\n const ticksInto = atTick - entry.tick;\n if (ticksInto % tpb === 0) return atTick;\n // Snap forward to next bar boundary\n return (entry.tick + Math.ceil(ticksInto / tpb) * tpb) as Tick;\n }\n\n private _computeBarAtTick(tick: Tick): number {\n const entry = this._entryAt(tick);\n const ticksInto = tick - entry.tick;\n const tpb = this._ticksPerBarForEntry(entry);\n return entry.barAtTick + ticksInto / tpb;\n }\n\n private _recomputeCache(fromIndex: number): void {\n for (let i = Math.max(1, fromIndex); i < this._entries.length; i++) {\n const prev = this._entries[i - 1];\n const tickDelta = this._entries[i].tick - prev.tick;\n const tpb = this._ticksPerBarForEntry(prev);\n this._entries[i] = {\n ...this._entries[i],\n barAtTick: prev.barAtTick + tickDelta / tpb,\n };\n }\n }\n\n /**\n * After changing a meter entry, re-snap downstream entries to bar boundaries\n * of their preceding meter so barAtTick stays integer.\n */\n private _resnapDownstreamEntries(fromIndex: number): void {\n for (let i = Math.max(1, fromIndex + 1); i < this._entries.length; i++) {\n const prev = this._entries[i - 1];\n const tpb = this._ticksPerBarForEntry(prev);\n const tick = this._entries[i].tick;\n const ticksIntoPrev = tick - prev.tick;\n if (ticksIntoPrev % tpb !== 0) {\n const snapped = prev.tick + Math.ceil(ticksIntoPrev / tpb) * tpb;\n console.warn(\n '[waveform-playlist] MeterMap: meter change moved entry from tick ' +\n tick +\n ' to ' +\n snapped +\n ' (bar boundary alignment)'\n );\n this._entries[i] = { ...this._entries[i], tick: snapped as Tick };\n }\n }\n }\n\n private _validateMeter(numerator: number, denominator: number): void {\n if (!Number.isInteger(numerator) || numerator < 1 || numerator > 32) {\n throw new Error(\n '[waveform-playlist] MeterMap: numerator must be an integer 1-32, got ' + numerator\n );\n }\n if (!isPowerOf2(denominator) || denominator > 32) {\n throw new Error(\n '[waveform-playlist] MeterMap: denominator must be a power of 2 (1-32), got ' + denominator\n );\n }\n }\n}\n","export class MasterNode {\n private _gainNode: GainNode;\n\n constructor(audioContext: AudioContext) {\n this._gainNode = audioContext.createGain();\n }\n\n get input(): AudioNode {\n return this._gainNode;\n }\n\n get output(): AudioNode {\n return this._gainNode;\n }\n\n setVolume(value: number): void {\n this._gainNode.gain.value = value;\n }\n\n dispose(): void {\n try {\n this._gainNode.disconnect();\n } catch (err) {\n console.warn('[waveform-playlist] MasterNode.dispose: error disconnecting: ' + String(err));\n }\n }\n}\n","export class TrackNode {\n readonly id: string;\n private _volumeNode: GainNode;\n private _panNode: StereoPannerNode;\n private _muteNode: GainNode;\n private _destination: AudioNode | null = null;\n private _effectsInput: AudioNode | null = null;\n\n constructor(id: string, audioContext: AudioContext) {\n this.id = id;\n this._volumeNode = audioContext.createGain();\n this._panNode = audioContext.createStereoPanner();\n this._panNode.channelCount = 2;\n this._muteNode = audioContext.createGain();\n\n // Wire: volume → pan → mute (caller connects output via connectOutput)\n this._volumeNode.connect(this._panNode);\n this._panNode.connect(this._muteNode);\n }\n\n /** Where clip sources connect */\n get input(): GainNode {\n return this._volumeNode;\n }\n\n /** Connect this track's output to a destination (master node) */\n connectOutput(destination: AudioNode): void {\n this._destination = destination;\n this._muteNode.connect(destination);\n }\n\n setVolume(value: number): void {\n this._volumeNode.gain.value = value;\n }\n\n setPan(value: number): void {\n this._panNode.pan.value = value;\n }\n\n setMute(muted: boolean): void {\n this._muteNode.gain.value = muted ? 0 : 1;\n }\n\n connectEffects(effectsInput: AudioNode): void {\n // Clean up previous effects connection first\n if (this._effectsInput) {\n this.disconnectEffects();\n }\n // Disconnect mute from destination\n this._muteNode.disconnect();\n // Route mute → effects input\n this._muteNode.connect(effectsInput);\n this._effectsInput = effectsInput;\n }\n\n disconnectEffects(): void {\n if (this._effectsInput && this._destination) {\n this._muteNode.disconnect();\n // Restore direct routing: mute → destination\n this._muteNode.connect(this._destination);\n this._effectsInput = null;\n }\n }\n\n dispose(): void {\n for (const node of [this._volumeNode, this._panNode, this._muteNode]) {\n try {\n node.disconnect();\n } catch (err) {\n console.warn(\n '[waveform-playlist] TrackNode.dispose: error disconnecting node:',\n String(err)\n );\n }\n }\n }\n}\n","import type { ClipTrack, AudioClip } from '@waveform-playlist/core';\nimport type { Tick, Sample, SchedulerEvent, SchedulerListener } from '../types';\nimport type { SampleTimeline } from '../timeline/sample-timeline';\nimport type { TempoMap } from '../timeline/tempo-map';\nimport type { TrackNode } from './track-node';\n\nexport interface ClipEvent extends SchedulerEvent {\n trackId: string;\n clipId: string;\n audioBuffer: AudioBuffer;\n /** Clip position on timeline (integer samples, at the TIMELINE sample rate) */\n startSample: Sample;\n /** Offset into audioBuffer (integer samples, at the BUFFER's own sample\n * rate — they index the buffer, not the timeline) */\n offsetSamples: Sample;\n /** Duration to play (integer samples, at the BUFFER's own sample rate) */\n durationSamples: Sample;\n /** Clip gain multiplier */\n gain: number;\n /** Fade in duration (integer samples, at the TIMELINE sample rate) */\n fadeInDurationSamples: Sample;\n /** Fade out duration (integer samples, at the TIMELINE sample rate) */\n fadeOutDurationSamples: Sample;\n}\n\ninterface TrackClipState {\n track: ClipTrack;\n clips: AudioClip[];\n}\n\nexport class ClipPlayer implements SchedulerListener<ClipEvent> {\n private _audioContext: AudioContext;\n private _sampleTimeline: SampleTimeline;\n private _tempoMap: TempoMap;\n private _toAudioTime: (transportTime: number) => number;\n private _tracks: Map<string, TrackClipState> = new Map();\n private _trackNodes: Map<string, TrackNode> = new Map();\n private _activeSources: Map<AudioBufferSourceNode, { trackId: string; gainNode: GainNode }> =\n new Map();\n private _loopEnabled = false;\n private _loopEndSamples = 0;\n\n constructor(\n audioContext: AudioContext,\n sampleTimeline: SampleTimeline,\n tempoMap: TempoMap,\n toAudioTime: (transportTime: number) => number\n ) {\n this._audioContext = audioContext;\n this._sampleTimeline = sampleTimeline;\n this._tempoMap = tempoMap;\n this._toAudioTime = toAudioTime;\n }\n\n setTracks(tracks: ClipTrack[], trackNodes: Map<string, TrackNode>): void {\n this._tracks.clear();\n this._trackNodes = trackNodes;\n for (const track of tracks) {\n this._tracks.set(track.id, { track, clips: track.clips });\n }\n }\n\n /** Set loop region using ticks. startTick is unused — loop clamping only needs\n * the end boundary; mid-clip restart at loopStart is handled by onPositionJump. */\n setLoop(enabled: boolean, _startTick: Tick, endTick: Tick): void {\n this._loopEnabled = enabled;\n this._loopEndSamples = this._sampleTimeline.ticksToSamples(endTick);\n }\n\n /** Set loop region using samples directly */\n setLoopSamples(enabled: boolean, _startSample: Sample, endSample: Sample): void {\n this._loopEnabled = enabled;\n this._loopEndSamples = endSample;\n }\n\n updateTrack(trackId: string, track: ClipTrack): void {\n this._tracks.set(trackId, { track, clips: track.clips });\n this._silenceTrack(trackId);\n }\n\n generate(fromTick: Tick, toTick: Tick): ClipEvent[] {\n const events: ClipEvent[] = [];\n\n for (const [trackId, state] of this._tracks) {\n for (const clip of state.clips) {\n if (clip.durationSamples === 0) continue;\n if (!clip.audioBuffer) continue;\n\n // Use startTick when available, fall back to sample-derived tick\n const clipTick: number =\n clip.startTick !== undefined\n ? clip.startTick\n : (this._sampleTimeline.samplesToTicks(clip.startSample as Sample) as number);\n\n // Only schedule when the clip START falls within this window.\n // Clips that started in a previous window are already playing\n // (AudioBufferSourceNode runs for its full duration).\n // Mid-clip starts (seek, loop wrap) are handled by onPositionJump().\n if (clipTick < (fromTick as number)) continue;\n if (clipTick >= (toTick as number)) continue;\n\n // Two sample-count domains are in play and must not be mixed:\n // timeline samples (clip.startSample, the loop boundary) and\n // buffer samples (clip.offsetSamples / durationSamples index the\n // audio file, which may have a different rate than the timeline).\n const timelineRate = this._sampleTimeline.sampleRate;\n const bufferRate = clip.audioBuffer.sampleRate;\n\n // Fade.duration is SECONDS (the convention shared with the Tone\n // adapter); pack as timeline samples so consume() recovers the\n // seconds with one division.\n const fadeInDurationSamples = clip.fadeIn\n ? Math.round((clip.fadeIn.duration ?? 0) * timelineRate)\n : 0;\n const fadeOutDurationSamples = clip.fadeOut\n ? Math.round((clip.fadeOut.duration ?? 0) * timelineRate)\n : 0;\n\n // Clamp duration at loopEnd so the source stops exactly at the\n // loop boundary. The clamp lives in timeline samples; the event's\n // duration stays in buffer samples.\n let durationSamples = clip.durationSamples;\n if (this._loopEnabled) {\n const durationTimelineSamples = Math.round(\n (clip.durationSamples / bufferRate) * timelineRate\n );\n const allowedTimelineSamples = this._loopEndSamples - clip.startSample;\n if (durationTimelineSamples > allowedTimelineSamples) {\n durationSamples = Math.round((allowedTimelineSamples / timelineRate) * bufferRate);\n }\n }\n\n events.push({\n trackId,\n clipId: clip.id,\n audioBuffer: clip.audioBuffer,\n tick: clipTick as Tick,\n startSample: clip.startSample as Sample,\n offsetSamples: clip.offsetSamples as Sample,\n durationSamples: durationSamples as Sample,\n gain: clip.gain,\n fadeInDurationSamples: fadeInDurationSamples as Sample,\n fadeOutDurationSamples: fadeOutDurationSamples as Sample,\n });\n }\n }\n\n return events;\n }\n\n consume(event: ClipEvent): void {\n const trackNode = this._trackNodes.get(event.trackId);\n if (!trackNode) {\n console.warn(\n '[waveform-playlist] ClipPlayer.consume: no TrackNode for trackId \"' +\n event.trackId +\n '\", clipId \"' +\n event.clipId +\n '\" — clip will not play'\n );\n return;\n }\n\n // offsetSamples/durationSamples index the BUFFER, so the buffer's own\n // rate converts them to seconds; a 48 kHz file on a 44.1 kHz timeline\n // would otherwise start ~8.8% too deep and play the wrong duration.\n const bufferRate = event.audioBuffer.sampleRate;\n const offsetSeconds = event.offsetSamples / bufferRate;\n const durationSeconds = event.durationSamples / bufferRate;\n\n // Guard against invalid offset\n if (offsetSeconds >= event.audioBuffer.duration) {\n console.warn(\n '[waveform-playlist] ClipPlayer.consume: offset (' +\n offsetSeconds +\n 's) exceeds audioBuffer.duration (' +\n event.audioBuffer.duration +\n 's) for clipId \"' +\n event.clipId +\n '\" — clip will not play'\n );\n return;\n }\n\n const source = this._audioContext.createBufferSource();\n source.buffer = event.audioBuffer;\n\n // Convert tick → seconds → AudioContext.currentTime for scheduling\n const transportSeconds = this._tempoMap.ticksToSeconds(event.tick);\n const when = this._toAudioTime(transportSeconds);\n\n // Create a gain node for per-clip gain and fades\n const gainNode = this._audioContext.createGain();\n gainNode.gain.value = event.gain;\n\n // Apply fades (AudioParam scheduling uses AudioContext time). Fade\n // durations are packed as TIMELINE samples (see generate()).\n // Clamp fades so they don't overlap (split duration evenly if they would)\n const timelineRate = this._sampleTimeline.sampleRate;\n let fadeIn = event.fadeInDurationSamples / timelineRate;\n let fadeOut = event.fadeOutDurationSamples / timelineRate;\n if (fadeIn + fadeOut > durationSeconds) {\n const ratio = durationSeconds / (fadeIn + fadeOut);\n fadeIn *= ratio;\n fadeOut *= ratio;\n }\n\n if (fadeIn > 0) {\n gainNode.gain.setValueAtTime(0, when);\n gainNode.gain.linearRampToValueAtTime(event.gain, when + fadeIn);\n }\n if (fadeOut > 0) {\n const fadeOutStart = when + durationSeconds - fadeOut;\n gainNode.gain.setValueAtTime(event.gain, fadeOutStart);\n gainNode.gain.linearRampToValueAtTime(0, when + durationSeconds);\n }\n\n source.connect(gainNode);\n gainNode.connect(trackNode.input);\n\n this._activeSources.set(source, {\n trackId: event.trackId,\n gainNode,\n });\n\n // Clean up when source finishes\n source.addEventListener('ended', () => {\n this._activeSources.delete(source);\n try {\n gainNode.disconnect();\n } catch (err) {\n console.warn('[waveform-playlist] ClipPlayer: error disconnecting gain node:', String(err));\n }\n });\n\n source.start(when, offsetSeconds, durationSeconds);\n }\n\n onPositionJump(newTick: Tick): void {\n this.silence();\n\n const newSample = this._sampleTimeline.ticksToSamples(newTick);\n\n // Re-schedule mid-clip sources for clips that span the new position\n for (const [trackId, state] of this._tracks) {\n for (const clip of state.clips) {\n if (clip.durationSamples === 0) continue;\n if (!clip.audioBuffer) continue;\n\n // Start comparison in ticks (strict < to avoid double-scheduling)\n const clipTick: number =\n clip.startTick !== undefined\n ? clip.startTick\n : (this._sampleTimeline.samplesToTicks(clip.startSample as Sample) as number);\n\n if (clipTick >= (newTick as number)) continue; // hasn't started yet\n\n const timelineRate = this._sampleTimeline.sampleRate;\n const bufferRate = clip.audioBuffer.sampleRate;\n const bufToTimeline = (n: number) => Math.round((n / bufferRate) * timelineRate);\n const timelineToBuf = (n: number) => Math.round((n / timelineRate) * bufferRate);\n\n // End comparison on the TIMELINE axis: the clip occupies its\n // buffer-domain duration scaled to timeline samples.\n const clipEndSample = clip.startSample + bufToTimeline(clip.durationSamples);\n if (clipEndSample <= (newSample as number)) continue; // already finished\n\n // How far into the clip we are, on the timeline — converted to\n // buffer samples before indexing the buffer.\n const offsetIntoClipSamples = (newSample as number) - clip.startSample;\n const offsetSamples = clip.offsetSamples + timelineToBuf(offsetIntoClipSamples);\n let remainingTimelineSamples = clipEndSample - (newSample as number);\n\n // Clamp at loop boundary (same as generate), in timeline samples\n if (\n this._loopEnabled &&\n (newSample as number) + remainingTimelineSamples > this._loopEndSamples\n ) {\n remainingTimelineSamples = this._loopEndSamples - (newSample as number);\n }\n if (remainingTimelineSamples <= 0) continue;\n const durationSamples = timelineToBuf(remainingTimelineSamples);\n\n // Fade.duration is seconds; pack as timeline samples (see generate())\n const fadeOutDurationSamples = clip.fadeOut\n ? Math.round((clip.fadeOut.duration ?? 0) * timelineRate)\n : 0;\n\n this.consume({\n trackId,\n clipId: clip.id,\n audioBuffer: clip.audioBuffer,\n tick: newTick as Tick,\n startSample: newSample as Sample,\n offsetSamples: offsetSamples as Sample,\n durationSamples: durationSamples as Sample,\n gain: clip.gain,\n fadeInDurationSamples: 0 as Sample,\n fadeOutDurationSamples: fadeOutDurationSamples as Sample,\n });\n }\n }\n }\n\n silence(): void {\n for (const [source, { gainNode }] of this._activeSources) {\n try {\n source.stop();\n } catch (err) {\n console.warn('[waveform-playlist] ClipPlayer.silence: error stopping source:', String(err));\n }\n try {\n gainNode.disconnect();\n } catch (err) {\n console.warn('[waveform-playlist] ClipPlayer.silence: error disconnecting:', String(err));\n }\n }\n this._activeSources.clear();\n }\n\n private _silenceTrack(trackId: string): void {\n const toDelete: AudioBufferSourceNode[] = [];\n for (const [source, info] of this._activeSources) {\n if (info.trackId === trackId) {\n try {\n source.stop();\n } catch (err) {\n console.warn(\n '[waveform-playlist] ClipPlayer._silenceTrack: error stopping source:',\n String(err)\n );\n }\n try {\n info.gainNode.disconnect();\n } catch (err) {\n console.warn(\n '[waveform-playlist] ClipPlayer._silenceTrack: error disconnecting:',\n String(err)\n );\n }\n toDelete.push(source);\n }\n }\n for (const source of toDelete) {\n this._activeSources.delete(source);\n }\n }\n}\n","import type { Tick, SchedulerEvent, SchedulerListener } from '../types';\nimport type { TempoMap } from '../timeline/tempo-map';\nimport { MeterMap } from '../timeline/meter-map';\n\nexport interface MetronomeEvent extends SchedulerEvent {\n isAccent: boolean;\n buffer: AudioBuffer;\n}\n\nexport class MetronomePlayer implements SchedulerListener<MetronomeEvent> {\n private _audioContext: AudioContext;\n private _tempoMap: TempoMap;\n private _meterMap: MeterMap;\n private _destination: AudioNode;\n private _toAudioTime: (transportTime: number) => number;\n private _enabled = false;\n private _accentBuffer: AudioBuffer | null = null;\n private _normalBuffer: AudioBuffer | null = null;\n private _activeSources: Set<AudioBufferSourceNode> = new Set();\n\n constructor(\n audioContext: AudioContext,\n tempoMap: TempoMap,\n meterMap: MeterMap,\n destination: AudioNode,\n toAudioTime: (transportTime: number) => number\n ) {\n this._audioContext = audioContext;\n this._tempoMap = tempoMap;\n this._meterMap = meterMap;\n this._destination = destination;\n this._toAudioTime = toAudioTime;\n }\n\n setEnabled(enabled: boolean): void {\n this._enabled = enabled;\n if (!enabled) {\n this.silence();\n }\n }\n\n setClickSounds(accent: AudioBuffer, normal: AudioBuffer): void {\n this._accentBuffer = accent;\n this._normalBuffer = normal;\n }\n\n generate(fromTick: Tick, toTick: Tick): MetronomeEvent[] {\n if (!this._enabled || !this._accentBuffer || !this._normalBuffer) {\n return [];\n }\n\n const events: MetronomeEvent[] = [];\n\n // Snap to first beat: align to beat grid anchored at the active meter entry\n let entry = this._meterMap.getEntryAt(fromTick);\n let beatSize = this._meterMap.ticksPerBeat(fromTick);\n const tickIntoSection = fromTick - entry.tick;\n let tick = entry.tick + Math.ceil(tickIntoSection / beatSize) * beatSize;\n\n while (tick < toTick) {\n const tickPos = tick as Tick;\n // Re-snap at meter boundaries\n const currentEntry = this._meterMap.getEntryAt(tickPos);\n if (currentEntry.tick !== entry.tick) {\n entry = currentEntry;\n beatSize = this._meterMap.ticksPerBeat(tickPos);\n }\n\n const isAccent = this._meterMap.isBarBoundary(tickPos);\n\n events.push({\n tick: tickPos,\n isAccent,\n buffer: isAccent ? this._accentBuffer : this._normalBuffer,\n });\n\n beatSize = this._meterMap.ticksPerBeat(tickPos);\n tick += beatSize;\n }\n\n return events;\n }\n\n consume(event: MetronomeEvent): void {\n const source = this._audioContext.createBufferSource();\n source.buffer = event.buffer;\n source.connect(this._destination);\n\n this._activeSources.add(source);\n source.addEventListener('ended', () => {\n this._activeSources.delete(source);\n try {\n source.disconnect();\n } catch (err) {\n console.warn(\n '[waveform-playlist] MetronomePlayer: error disconnecting source:',\n String(err)\n );\n }\n });\n\n const transportTime = this._tempoMap.ticksToSeconds(event.tick);\n source.start(this._toAudioTime(transportTime));\n }\n\n onPositionJump(_newTick: Tick): void {\n // Don't silence — clicks are short one-shots that finish naturally.\n // Calling silence() here kills clicks scheduled in the lookahead window\n // that haven't played yet, causing the last beat before a loop wrap\n // to be cut off.\n }\n\n silence(): void {\n for (const source of this._activeSources) {\n try {\n source.stop();\n } catch (err) {\n console.warn(\n '[waveform-playlist] MetronomePlayer.silence: error stopping source:',\n String(err)\n );\n }\n try {\n source.disconnect();\n } catch (err) {\n console.warn(\n '[waveform-playlist] MetronomePlayer.silence: error disconnecting:',\n String(err)\n );\n }\n }\n this._activeSources.clear();\n }\n}\n","import type { Tick, SchedulerEvent, SchedulerListener } from '../types';\nimport type { TempoMap } from '../timeline/tempo-map';\nimport type { MeterMap } from '../timeline/meter-map';\n\nexport interface CountInEvent extends SchedulerEvent {\n isAccent: boolean;\n buffer: AudioBuffer;\n beat: number;\n totalBeats: number;\n}\n\ninterface CountInConfig {\n totalBeats: number;\n accentBuffer: AudioBuffer;\n normalBuffer: AudioBuffer;\n meterMap: MeterMap;\n /** TempoMap for tick→seconds conversion in consume(). Must match the\n * count-in scheduler's TempoMap (locked to BPM at the play position). */\n tempoMap: TempoMap;\n onBeat: (beat: number, totalBeats: number) => void;\n}\n\nexport class CountInPlayer implements SchedulerListener<CountInEvent> {\n private _audioContext: AudioContext;\n private _tempoMap: TempoMap;\n private _destination: AudioNode;\n private _toAudioTime: (transportTime: number) => number;\n private _activeSources: Set<AudioBufferSourceNode> = new Set();\n\n private _totalBeats = 0;\n private _beatsGenerated = 0;\n private _accentBuffer: AudioBuffer | null = null;\n private _normalBuffer: AudioBuffer | null = null;\n private _meterMap: MeterMap | null = null;\n private _onBeat: ((beat: number, totalBeats: number) => void) | null = null;\n\n constructor(\n audioContext: AudioContext,\n tempoMap: TempoMap,\n destination: AudioNode,\n toAudioTime: (transportTime: number) => number\n ) {\n this._audioContext = audioContext;\n this._tempoMap = tempoMap;\n this._destination = destination;\n this._toAudioTime = toAudioTime;\n }\n\n configure(config: CountInConfig): void {\n this._totalBeats = config.totalBeats;\n this._beatsGenerated = 0;\n this._accentBuffer = config.accentBuffer;\n this._normalBuffer = config.normalBuffer;\n this._meterMap = config.meterMap;\n this._tempoMap = config.tempoMap;\n this._onBeat = config.onBeat;\n }\n\n generate(fromTick: Tick, toTick: Tick): CountInEvent[] {\n if (!this._accentBuffer || !this._normalBuffer || !this._meterMap) {\n return [];\n }\n\n const events: CountInEvent[] = [];\n const meterMap = this._meterMap;\n\n // Walk the beat grid anchored at the active meter entry (same algorithm as MetronomePlayer)\n let entry = meterMap.getEntryAt(fromTick);\n let beatSize = meterMap.ticksPerBeat(fromTick);\n const tickIntoSection = fromTick - entry.tick;\n let tick = entry.tick + Math.ceil(tickIntoSection / beatSize) * beatSize;\n\n while (tick < toTick && this._beatsGenerated < this._totalBeats) {\n const tickPos = tick as Tick;\n\n // Re-snap at meter boundaries\n const currentEntry = meterMap.getEntryAt(tickPos);\n if (currentEntry.tick !== entry.tick) {\n entry = currentEntry;\n beatSize = meterMap.ticksPerBeat(tickPos);\n }\n\n this._beatsGenerated++;\n const isAccent = meterMap.isBarBoundary(tickPos);\n\n events.push({\n tick: tickPos,\n isAccent,\n buffer: isAccent ? this._accentBuffer : this._normalBuffer,\n beat: this._beatsGenerated,\n totalBeats: this._totalBeats,\n });\n\n beatSize = meterMap.ticksPerBeat(tickPos);\n tick += beatSize;\n }\n\n return events;\n }\n\n consume(event: CountInEvent): void {\n try {\n const source = this._audioContext.createBufferSource();\n source.buffer = event.buffer;\n source.connect(this._destination);\n\n this._activeSources.add(source);\n source.addEventListener('ended', () => {\n this._activeSources.delete(source);\n try {\n source.disconnect();\n } catch (err) {\n console.warn(\n '[waveform-playlist] CountInPlayer: error disconnecting source:',\n String(err)\n );\n }\n });\n\n const transportTime = this._tempoMap.ticksToSeconds(event.tick);\n source.start(this._toAudioTime(transportTime));\n } catch (err) {\n console.warn(\n '[waveform-playlist] CountInPlayer.consume: failed to schedule beat ' +\n event.beat +\n '/' +\n event.totalBeats +\n ': ' +\n String(err)\n );\n }\n\n // Beat callback stays outside try/catch so count-in events still\n // fire even if one beat fails to schedule audio.\n this._onBeat?.(event.beat, event.totalBeats);\n }\n\n onPositionJump(_newTick: Tick): void {\n // No-op — clicks are short one-shots that finish naturally.\n }\n\n silence(): void {\n for (const source of this._activeSources) {\n try {\n source.stop();\n } catch (err) {\n console.warn(\n '[waveform-playlist] CountInPlayer.silence: error stopping source:',\n String(err)\n );\n }\n try {\n source.disconnect();\n } catch (err) {\n console.warn(\n '[waveform-playlist] CountInPlayer.silence: error disconnecting:',\n String(err)\n );\n }\n }\n this._activeSources.clear();\n }\n}\n","export interface ClickSoundOptions {\n /** Frequency for accent click (beat 1). Default: 1000 Hz */\n accentFrequency?: number;\n /** Frequency for normal click (other beats). Default: 800 Hz */\n normalFrequency?: number;\n}\n\nconst DEFAULT_ACCENT_FREQUENCY = 1000;\nconst DEFAULT_NORMAL_FREQUENCY = 800;\nconst ACCENT_DURATION = 0.04; // 40ms\nconst NORMAL_DURATION = 0.03; // 30ms\n\nfunction synthesizeClick(\n audioContext: AudioContext,\n frequency: number,\n duration: number\n): AudioBuffer {\n const sampleRate = audioContext.sampleRate;\n const length = Math.ceil(sampleRate * duration);\n const buffer = audioContext.createBuffer(1, length, sampleRate);\n const data = buffer.getChannelData(0);\n\n for (let i = 0; i < length; i++) {\n const t = i / sampleRate;\n const envelope = Math.exp(-t * 50);\n data[i] = Math.sin(2 * Math.PI * frequency * t) * envelope;\n }\n\n return buffer;\n}\n\nexport function createDefaultClickSounds(\n audioContext: AudioContext,\n options?: ClickSoundOptions\n): { accent: AudioBuffer; normal: AudioBuffer } {\n const accentFreq = options?.accentFrequency ?? DEFAULT_ACCENT_FREQUENCY;\n const normalFreq = options?.normalFrequency ?? DEFAULT_NORMAL_FREQUENCY;\n\n if (accentFreq <= 0 || normalFreq <= 0) {\n console.warn(\n '[waveform-playlist] createDefaultClickSounds: frequency must be positive, got accent=' +\n accentFreq +\n ' normal=' +\n normalFreq\n );\n }\n\n return {\n accent: synthesizeClick(audioContext, accentFreq, ACCENT_DURATION),\n normal: synthesizeClick(audioContext, normalFreq, NORMAL_DURATION),\n };\n}\n","import type { ClipTrack } from '@waveform-playlist/core';\nimport type {\n Tick,\n Sample,\n TransportOptions,\n MeterSignature,\n CountInMode,\n CountInEventData,\n TempoChangeEventData,\n MeterChangeEventData,\n} from './types';\nimport type { SetTempoOptions } from './timeline/tempo-map';\nimport { Clock } from './core/clock';\nimport { Scheduler } from './core/scheduler';\nimport { Timer } from './core/timer';\nimport { SampleTimeline } from './timeline/sample-timeline';\nimport { TempoMap } from './timeline/tempo-map';\nimport { MeterMap } from './timeline/meter-map';\nimport { ClipPlayer } from './audio/clip-player';\nimport { MetronomePlayer } from './audio/metronome-player';\nimport { CountInPlayer } from './audio/count-in-player';\nimport { createDefaultClickSounds } from './audio/click-sounds';\nimport { MasterNode } from './audio/master-node';\nimport { TrackNode } from './audio/track-node';\n\nexport interface TransportEvents {\n play: () => void;\n pause: () => void;\n stop: () => void;\n loop: () => void;\n tempochange: (event: TempoChangeEventData) => void;\n meterchange: (event: MeterChangeEventData) => void;\n countIn: (event: CountInEventData) => void;\n countInEnd: () => void;\n}\n\ntype TransportEventType = keyof TransportEvents;\n\nexport class Transport {\n private _audioContext: AudioContext;\n private _clock: Clock;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private _scheduler: Scheduler<any>;\n private _timer: Timer;\n private _sampleTimeline: SampleTimeline;\n private _meterMap: MeterMap;\n private _tempoMap: TempoMap;\n private _clipPlayer!: ClipPlayer;\n private _metronomePlayer!: MetronomePlayer;\n private _masterNode!: MasterNode;\n private _trackNodes: Map<string, TrackNode> = new Map();\n private _tracks: ClipTrack[] = [];\n private _soloedTrackIds: Set<string> = new Set();\n private _mutedTrackIds: Set<string> = new Set();\n private _playing = false;\n private _endTime: number | undefined;\n private _loopEnabled = false;\n private _loopStartTick: Tick = 0 as Tick;\n private _loopStartSeconds = 0;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private _listeners: Map<TransportEventType, Set<(...args: any[]) => void>> = new Map();\n\n // --- Count-In state ---\n private _countInEnabled = false;\n private _countInBars = 1;\n private _countInMode: CountInMode = 'recording-only';\n private _recording = false;\n private _countingIn = false;\n private _countInStartPosition = 0;\n private _countInDuration = 0;\n private _countInPlayer!: CountInPlayer;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private _countInScheduler: Scheduler<any> | null = null;\n private _accentBuffer: AudioBuffer | null = null;\n private _normalBuffer: AudioBuffer | null = null;\n private _schedulerLookahead: number = 0.2;\n private _ppqn: number = 960;\n\n constructor(audioContext: AudioContext, options: TransportOptions = {}) {\n this._audioContext = audioContext;\n\n const sampleRate = options.sampleRate ?? audioContext.sampleRate;\n const ppqn = options.ppqn ?? 960;\n const tempo = options.tempo ?? 120;\n const numerator = options.numerator ?? 4;\n const denominator = options.denominator ?? 4;\n const lookahead = options.schedulerLookahead ?? 0.2;\n\n this._ppqn = ppqn;\n this._schedulerLookahead = lookahead;\n\n Transport._validateOptions(sampleRate, ppqn, tempo, numerator, denominator, lookahead);\n\n this._clock = new Clock(audioContext);\n this._sampleTimeline = new SampleTimeline(sampleRate);\n this._meterMap = new MeterMap(ppqn, numerator, denominator);\n this._tempoMap = new TempoMap(ppqn, tempo);\n\n this._scheduler = new Scheduler(this._tempoMap, {\n lookahead,\n onLoop: (loopStartSeconds: number, loopEndSeconds: number, currentTimeSeconds: number) => {\n // The wrap fires in the middle of advance(), which runs ahead of\n // real time by the lookahead. Post-wrap events use toAudioTime()\n // which reads the clock, so the seek target must place loopStart\n // at the audio-time of the boundary — not at \"now\".\n // Uses the currentTimeSeconds snapshot from advance() to avoid\n // re-reading the live AudioContext.currentTime (sub-ms drift).\n const timeToBoundary = loopEndSeconds - currentTimeSeconds;\n this._clock.seekTo(loopStartSeconds - timeToBoundary);\n },\n });\n this._sampleTimeline.setTempoMap(this._tempoMap);\n\n this._initAudioGraph(audioContext);\n this._initCountIn(audioContext, options);\n\n this._timer = new Timer(() => {\n const time = this._clock.getTime();\n if (this._countingIn) {\n this._countInScheduler?.advance(time);\n // Transition when the full bar duration has elapsed — not when the\n // last beat is consumed. This gives the last beat its full ring-out\n // and places the transition exactly at the bar boundary.\n if (time >= this._countInDuration) {\n this._finishCountIn();\n }\n return;\n }\n if (this._endTime !== undefined && time >= this._endTime) {\n this.stop();\n return;\n }\n this._scheduler.advance(time);\n });\n }\n\n get audioContext(): AudioContext {\n return this._audioContext;\n }\n\n get ppqn(): number {\n return this._ppqn;\n }\n\n // --- Playback ---\n\n play(startTime?: number, endTime?: number): void {\n if (this._playing) return;\n\n if (startTime !== undefined) {\n this._clock.seekTo(startTime);\n }\n\n // Check if count-in should activate\n if (this._shouldCountIn()) {\n this._startCountIn(endTime);\n return;\n }\n\n // Always reset scheduler to current position — after pause, the old\n // rightEdge is stale and clips whose startTime is before it won't\n // be picked up by generate().\n const currentTime = this._clock.getTime();\n this._scheduler.reset(currentTime);\n\n this._endTime = endTime;\n\n this._clock.start();\n\n // Re-create sources for clips spanning the current position.\n // After pause, silence() killed all active sources. generate() only\n // picks up clips whose startTime falls in the window, so clips that\n // started before the current position need mid-clip sources.\n const currentTick = this._tempoMap.secondsToTicks(currentTime);\n this._clipPlayer.onPositionJump(currentTick);\n\n this._timer.start();\n this._playing = true;\n this._emit('play');\n }\n\n pause(): void {\n if (!this._playing) return;\n\n if (this._countingIn) {\n this._cancelCountIn();\n this._clock.stop();\n this._playing = false;\n this._emit('pause');\n return;\n }\n\n this._timer.stop();\n this._clock.stop();\n this._silenceAll();\n this._playing = false;\n this._emit('pause');\n }\n\n stop(): void {\n const wasPlaying = this._playing;\n if (this._countingIn) {\n this._cancelCountIn();\n }\n this._timer.stop();\n this._clock.reset();\n this._scheduler.reset(0);\n this._silenceAll();\n this._playing = false;\n this._endTime = undefined;\n if (wasPlaying) {\n this._emit('stop');\n }\n }\n\n seek(time: number): void {\n const wasPlaying = this._playing;\n const wasCountingIn = this._countingIn;\n\n if (wasCountingIn) {\n this._cancelCountIn();\n this._playing = false;\n }\n\n if (wasPlaying && !wasCountingIn) {\n this._timer.stop();\n }\n\n this._silenceAll();\n this._clock.seekTo(time);\n this._scheduler.reset(time);\n // Clear stale endTime — seeking past a previous endTime shouldn't\n // cause immediate stop on the next play()\n this._endTime = undefined;\n\n // Resume playback only if was playing normally (not counting in)\n if (wasPlaying && !wasCountingIn) {\n this._clock.start();\n // Re-create sources for clips spanning the seek position\n const seekTick = this._tempoMap.secondsToTicks(time);\n this._clipPlayer.onPositionJump(seekTick);\n this._timer.start();\n }\n }\n\n getCurrentTime(): number {\n if (this._countingIn) {\n return this._countInStartPosition;\n }\n const t = this._clock.getTime();\n // After a loop wrap, the clock is briefly behind loopStart (the seek\n // target accounts for lookahead offset). Clamp for display purposes.\n if (this._loopEnabled && t < this._loopStartSeconds) {\n return this._loopStartSeconds;\n }\n return t;\n }\n\n isPlaying(): boolean {\n return this._playing;\n }\n\n // --- Tracks ---\n\n setTracks(tracks: ClipTrack[]): void {\n // Dispose existing track nodes\n for (const node of this._trackNodes.values()) {\n node.dispose();\n }\n this._trackNodes.clear();\n this._soloedTrackIds.clear();\n this._mutedTrackIds.clear();\n\n this._tracks = tracks;\n\n // Create track nodes\n for (const track of tracks) {\n const trackNode = new TrackNode(track.id, this._audioContext);\n trackNode.setVolume(track.volume);\n trackNode.setPan(track.pan);\n trackNode.connectOutput(this._masterNode.input);\n this._trackNodes.set(track.id, trackNode);\n\n if (track.muted) {\n this._mutedTrackIds.add(track.id);\n }\n if (track.soloed) {\n this._soloedTrackIds.add(track.id);\n }\n }\n\n this._applyMuteState();\n this._clipPlayer.setTracks(tracks, this._trackNodes);\n }\n\n addTrack(track: ClipTrack): void {\n const trackNode = new TrackNode(track.id, this._audioContext);\n trackNode.setVolume(track.volume);\n trackNode.setPan(track.pan);\n trackNode.connectOutput(this._masterNode.input);\n this._trackNodes.set(track.id, trackNode);\n\n if (track.muted) {\n this._mutedTrackIds.add(track.id);\n }\n if (track.soloed) {\n this._soloedTrackIds.add(track.id);\n }\n\n this._tracks = [...this._tracks, track];\n this._applyMuteState();\n this._clipPlayer.setTracks(this._tracks, this._trackNodes);\n }\n\n removeTrack(trackId: string): void {\n const node = this._trackNodes.get(trackId);\n if (node) {\n node.dispose();\n this._trackNodes.delete(trackId);\n }\n this._soloedTrackIds.delete(trackId);\n this._mutedTrackIds.delete(trackId);\n this._tracks = this._tracks.filter((t) => t.id !== trackId);\n this._applyMuteState();\n this._clipPlayer.setTracks(this._tracks, this._trackNodes);\n }\n\n updateTrack(trackId: string, track: ClipTrack): void {\n this._tracks = this._tracks.map((t) => (t.id === trackId ? track : t));\n\n const node = this._trackNodes.get(trackId);\n if (node) {\n node.setVolume(track.volume);\n node.setPan(track.pan);\n }\n\n // Update mute/solo\n if (track.muted) {\n this._mutedTrackIds.add(trackId);\n } else {\n this._mutedTrackIds.delete(trackId);\n }\n if (track.soloed) {\n this._soloedTrackIds.add(trackId);\n } else {\n this._soloedTrackIds.delete(trackId);\n }\n\n this._applyMuteState();\n this._clipPlayer.updateTrack(trackId, track);\n }\n\n // --- Track Controls ---\n\n setTrackVolume(trackId: string, volume: number): void {\n const node = this._trackNodes.get(trackId);\n if (!node) {\n console.warn('[waveform-playlist] setTrackVolume: unknown trackId \"' + trackId + '\"');\n return;\n }\n node.setVolume(volume);\n }\n\n setTrackPan(trackId: string, pan: number): void {\n const node = this._trackNodes.get(trackId);\n if (!node) {\n console.warn('[waveform-playlist] setTrackPan: unknown trackId \"' + trackId + '\"');\n return;\n }\n node.setPan(pan);\n }\n\n setTrackMute(trackId: string, muted: boolean): void {\n if (muted) {\n this._mutedTrackIds.add(trackId);\n } else {\n this._mutedTrackIds.delete(trackId);\n }\n this._applyMuteState();\n }\n\n setTrackSolo(trackId: string, soloed: boolean): void {\n if (soloed) {\n this._soloedTrackIds.add(trackId);\n } else {\n this._soloedTrackIds.delete(trackId);\n }\n this._applyMuteState();\n }\n\n // --- Master ---\n\n setMasterVolume(volume: number): void {\n this._masterNode.setVolume(volume);\n }\n\n // --- Loop ---\n\n /** Primary loop API — ticks as source of truth */\n setLoop(enabled: boolean, startTick: Tick, endTick: Tick): void {\n if (enabled && startTick >= endTick) {\n console.warn(\n '[waveform-playlist] Transport.setLoop: startTick (' +\n startTick +\n ') must be less than endTick (' +\n endTick +\n ')'\n );\n return;\n }\n this._loopEnabled = enabled;\n this._loopStartTick = startTick;\n this._loopStartSeconds = this._tempoMap.ticksToSeconds(startTick);\n this._scheduler.setLoop(enabled, startTick, endTick);\n this._clipPlayer.setLoop(enabled, startTick, endTick);\n this._emit('loop');\n }\n\n /** Convenience — converts seconds to ticks */\n setLoopSeconds(enabled: boolean, startSec: number, endSec: number): void {\n const startTick = this._tempoMap.secondsToTicks(startSec);\n const endTick = this._tempoMap.secondsToTicks(endSec);\n this.setLoop(enabled, startTick, endTick);\n }\n\n /** Convenience — sets loop in samples */\n setLoopSamples(enabled: boolean, startSample: Sample, endSample: Sample): void {\n if (enabled && (!Number.isFinite(startSample) || !Number.isFinite(endSample))) {\n console.warn(\n '[waveform-playlist] Transport.setLoopSamples: non-finite sample values (' +\n startSample +\n ', ' +\n endSample +\n ')'\n );\n return;\n }\n if (enabled && startSample >= endSample) {\n console.warn(\n '[waveform-playlist] Transport.setLoopSamples: startSample (' +\n startSample +\n ') must be less than endSample (' +\n endSample +\n ')'\n );\n return;\n }\n const startTick = this._sampleTimeline.samplesToTicks(startSample);\n const endTick = this._sampleTimeline.samplesToTicks(endSample);\n this._loopEnabled = enabled;\n this._loopStartTick = startTick;\n this._loopStartSeconds = this._tempoMap.ticksToSeconds(startTick);\n this._clipPlayer.setLoopSamples(enabled, startSample, endSample);\n this._scheduler.setLoop(enabled, startTick, endTick);\n this._emit('loop');\n }\n\n // --- Tempo ---\n\n setTempo(bpm: number, atTick?: Tick, options?: SetTempoOptions): void {\n this._tempoMap.setTempo(bpm, atTick, options);\n // Recompute cached loop start — tempo change invalidates tick→seconds mapping\n if (this._loopEnabled) {\n this._loopStartSeconds = this._tempoMap.ticksToSeconds(this._loopStartTick);\n }\n this._emit('tempochange', { bpm, atTick: atTick ?? (0 as Tick) });\n }\n\n getTempo(atTick?: Tick): number {\n return this._tempoMap.getTempo(atTick);\n }\n\n // --- Meter ---\n\n setMeter(numerator: number, denominator: number, atTick?: Tick): void {\n this._meterMap.setMeter(numerator, denominator, atTick);\n this._emit('meterchange', { numerator, denominator, atTick: atTick ?? (0 as Tick) });\n }\n\n getMeter(atTick?: Tick): MeterSignature {\n return this._meterMap.getMeter(atTick);\n }\n\n removeMeter(atTick: Tick): void {\n this._meterMap.removeMeter(atTick);\n const meter = this._meterMap.getMeter(atTick);\n this._emit('meterchange', {\n numerator: meter.numerator,\n denominator: meter.denominator,\n atTick,\n });\n }\n\n clearMeters(): void {\n this._meterMap.clearMeters();\n const meter = this._meterMap.getMeter();\n this._emit('meterchange', {\n numerator: meter.numerator,\n denominator: meter.denominator,\n atTick: 0 as Tick,\n });\n }\n\n clearTempos(): void {\n this._tempoMap.clearTempos();\n if (this._loopEnabled) {\n this._loopStartSeconds = this._tempoMap.ticksToSeconds(this._loopStartTick);\n }\n this._emit('tempochange', { bpm: this._tempoMap.getTempo(), atTick: 0 as Tick });\n }\n\n barToTick(bar: number): Tick {\n return this._meterMap.barToTick(bar);\n }\n\n tickToBar(tick: Tick): number {\n return this._meterMap.tickToBar(tick);\n }\n\n /** Convert transport time (seconds) to tick position, using the tempo map. */\n timeToTick(seconds: number): Tick {\n return this._tempoMap.secondsToTicks(seconds);\n }\n\n /** Convert tick position to transport time (seconds), using the tempo map. */\n tickToTime(tick: Tick): number {\n return this._tempoMap.ticksToSeconds(tick);\n }\n\n // --- Metronome ---\n\n setMetronomeEnabled(enabled: boolean): void {\n this._metronomePlayer.setEnabled(enabled);\n }\n\n setMetronomeClickSounds(accent: AudioBuffer, normal: AudioBuffer): void {\n this._accentBuffer = accent;\n this._normalBuffer = normal;\n this._metronomePlayer.setClickSounds(accent, normal);\n }\n\n // --- Count-In ---\n\n static readonly MIN_COUNT_IN_BARS = 1;\n static readonly MAX_COUNT_IN_BARS = 8;\n\n setCountIn(enabled: boolean): void {\n this._countInEnabled = enabled;\n }\n\n setCountInBars(bars: number): void {\n const rounded = Math.round(bars);\n if (rounded < Transport.MIN_COUNT_IN_BARS) {\n console.warn(\n '[waveform-playlist] Transport.setCountInBars: clamping ' +\n bars +\n ' to ' +\n Transport.MIN_COUNT_IN_BARS\n );\n this._countInBars = Transport.MIN_COUNT_IN_BARS;\n return;\n }\n if (rounded > Transport.MAX_COUNT_IN_BARS) {\n console.warn(\n '[waveform-playlist] Transport.setCountInBars: clamping ' +\n bars +\n ' to ' +\n Transport.MAX_COUNT_IN_BARS\n );\n this._countInBars = Transport.MAX_COUNT_IN_BARS;\n return;\n }\n this._countInBars = rounded;\n }\n\n setCountInMode(mode: CountInMode): void {\n this._countInMode = mode;\n }\n\n setRecording(recording: boolean): void {\n this._recording = recording;\n }\n\n isCountingIn(): boolean {\n return this._countingIn;\n }\n\n // --- Effects Hook ---\n\n connectTrackOutput(trackId: string, node: AudioNode): void {\n const trackNode = this._trackNodes.get(trackId);\n if (!trackNode) {\n console.warn('[waveform-playlist] connectTrackOutput: unknown trackId \"' + trackId + '\"');\n return;\n }\n trackNode.connectEffects(node);\n }\n\n disconnectTrackOutput(trackId: string): void {\n const trackNode = this._trackNodes.get(trackId);\n if (!trackNode) {\n console.warn('[waveform-playlist] disconnectTrackOutput: unknown trackId \"' + trackId + '\"');\n return;\n }\n trackNode.disconnectEffects();\n }\n\n /** The master output AudioNode. Connect your own nodes (analyzers, recorders, etc.)\n * in parallel. The transport already routes this to audioContext.destination. */\n get masterOutputNode(): AudioNode {\n return this._masterNode.output;\n }\n\n // --- Events ---\n\n on<K extends TransportEventType>(event: K, cb: TransportEvents[K]): void {\n if (!this._listeners.has(event)) {\n this._listeners.set(event, new Set());\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n this._listeners.get(event)!.add(cb as (...args: any[]) => void);\n }\n\n off<K extends TransportEventType>(event: K, cb: TransportEvents[K]): void {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n this._listeners.get(event)?.delete(cb as (...args: any[]) => void);\n }\n\n // --- Dispose ---\n\n dispose(): void {\n this.stop();\n for (const node of this._trackNodes.values()) {\n node.dispose();\n }\n this._trackNodes.clear();\n this._masterNode.dispose();\n this._listeners.clear();\n }\n\n // --- Private ---\n\n private static _validateOptions(\n sampleRate: number,\n ppqn: number,\n tempo: number,\n numerator: number,\n denominator: number,\n lookahead: number\n ): void {\n if (sampleRate <= 0) {\n throw new Error(\n '[waveform-playlist] Transport: sampleRate must be positive, got ' + sampleRate\n );\n }\n if (ppqn <= 0 || !Number.isInteger(ppqn)) {\n throw new Error(\n '[waveform-playlist] Transport: ppqn must be a positive integer, got ' + ppqn\n );\n }\n if (tempo <= 0) {\n throw new Error('[waveform-playlist] Transport: tempo must be positive, got ' + tempo);\n }\n if (!Number.isInteger(numerator) || numerator < 1 || numerator > 32) {\n throw new Error(\n '[waveform-playlist] Transport: numerator must be an integer 1-32, got ' + numerator\n );\n }\n if (denominator <= 0 || (denominator & (denominator - 1)) !== 0 || denominator > 32) {\n throw new Error(\n '[waveform-playlist] Transport: denominator must be a power of 2 (1-32), got ' + denominator\n );\n }\n if (lookahead <= 0) {\n throw new Error(\n '[waveform-playlist] Transport: schedulerLookahead must be positive, got ' + lookahead\n );\n }\n }\n\n private _initAudioGraph(audioContext: AudioContext): void {\n this._masterNode = new MasterNode(audioContext);\n this._masterNode.output.connect(audioContext.destination);\n\n const toAudioTime = (transportTime: number) => this._clock.toAudioTime(transportTime);\n\n this._clipPlayer = new ClipPlayer(\n audioContext,\n this._sampleTimeline,\n this._tempoMap,\n toAudioTime\n );\n this._metronomePlayer = new MetronomePlayer(\n audioContext,\n this._tempoMap,\n this._meterMap,\n this._masterNode.input,\n toAudioTime\n );\n\n this._scheduler.addListener(this._clipPlayer);\n this._scheduler.addListener(this._metronomePlayer);\n }\n\n private _initCountIn(audioContext: AudioContext, options: TransportOptions): void {\n const toAudioTime = (transportTime: number) => this._clock.toAudioTime(transportTime);\n this._countInPlayer = new CountInPlayer(\n audioContext,\n this._tempoMap,\n this._masterNode.input,\n toAudioTime\n );\n\n try {\n const { accent, normal } = createDefaultClickSounds(audioContext, {\n accentFrequency: options.accentFrequency,\n normalFrequency: options.normalFrequency,\n });\n this._accentBuffer = accent;\n this._normalBuffer = normal;\n this._metronomePlayer.setClickSounds(accent, normal);\n } catch (err) {\n console.warn(\n '[waveform-playlist] Transport: failed to create default click sounds. ' +\n 'Metronome and count-in will be silent until setMetronomeClickSounds() is called. ' +\n 'Error: ' +\n String(err)\n );\n }\n }\n\n private _shouldCountIn(): boolean {\n if (!this._countInEnabled) return false;\n if (!this._accentBuffer || !this._normalBuffer) {\n console.warn('[waveform-playlist] Transport: count-in skipped — no click sounds loaded');\n return false;\n }\n if (this._countInMode === 'recording-only' && !this._recording) return false;\n return true;\n }\n\n private _startCountIn(endTime?: number): void {\n const currentTime = this._clock.getTime();\n this._countInStartPosition = currentTime;\n this._countingIn = true;\n this._playing = true;\n this._endTime = endTime;\n\n const playPositionTick = this._tempoMap.secondsToTicks(currentTime);\n const meter = this._meterMap.getMeter(playPositionTick);\n const totalBeats = meter.numerator * this._countInBars;\n\n // Calculate full bar duration in seconds — transition happens at the bar\n // boundary, not when the last beat is consumed. This gives the last beat\n // its full ring-out time.\n const bpmAtPosition = this._tempoMap.getTempo(playPositionTick);\n const ticksPerBeat = this._ppqn * (4 / meter.denominator);\n const countInTicks = totalBeats * ticksPerBeat;\n const countInTempoMap = new TempoMap(this._ppqn, bpmAtPosition);\n // Dedicated MeterMap locked to the meter at the play position — same\n // isolation as countInTempoMap. The count-in scheduler's tick space starts\n // at 0, so the main MeterMap's entries would be misinterpreted.\n const countInMeterMap = new MeterMap(this._ppqn, meter.numerator, meter.denominator);\n this._countInDuration = countInTempoMap.ticksToSeconds(countInTicks as Tick);\n\n this._countInScheduler = new Scheduler(countInTempoMap, {\n lookahead: this._schedulerLookahead,\n });\n\n this._countInPlayer.configure({\n totalBeats,\n accentBuffer: this._accentBuffer!,\n normalBuffer: this._normalBuffer!,\n meterMap: countInMeterMap,\n tempoMap: countInTempoMap,\n onBeat: (beat, total) => {\n this._emit('countIn', { beat, totalBeats: total });\n },\n });\n\n this._countInScheduler.addListener(this._countInPlayer);\n this._countInScheduler.reset(0);\n this._clock.seekTo(0);\n this._clock.start();\n // Use the main timer to drive the count-in scheduler (avoids spawning a second rAF chain)\n this._timer.start();\n }\n\n private _finishCountIn(): void {\n this._countInScheduler?.removeListener(this._countInPlayer);\n this._countInScheduler = null;\n // Don't silence — clicks are short one-shots (~40ms) that finish naturally.\n this._countingIn = false;\n this._emit('countInEnd');\n\n // Transition to normal playback at original position.\n // The timer is already running (driving the count-in scheduler via the\n // _countingIn branch). Setting _countingIn=false above makes the next\n // tick fall through to the main scheduler branch — no stop/restart needed.\n this._clock.seekTo(this._countInStartPosition);\n const currentTime = this._clock.getTime();\n this._scheduler.reset(currentTime);\n const currentTick = this._tempoMap.secondsToTicks(currentTime);\n this._clipPlayer.onPositionJump(currentTick);\n this._emit('play');\n }\n\n private _cancelCountIn(): void {\n this._countInPlayer.silence();\n this._countInScheduler?.removeListener(this._countInPlayer);\n this._countInScheduler = null;\n this._countingIn = false;\n // Stop the main timer (which was driving the count-in scheduler)\n this._timer.stop();\n }\n\n private _silenceAll(): void {\n this._clipPlayer.silence();\n this._metronomePlayer.silence();\n this._countInPlayer.silence();\n }\n\n private _applyMuteState(): void {\n const hasSolo = this._soloedTrackIds.size > 0;\n\n for (const [trackId, node] of this._trackNodes) {\n const isExplicitlyMuted = this._mutedTrackIds.has(trackId);\n const isSoloMuted = hasSolo && !this._soloedTrackIds.has(trackId);\n\n // Explicit mute takes precedence — a track that is both soloed AND muted stays muted\n node.setMute(isExplicitlyMuted || isSoloMuted);\n }\n }\n\n private _emit<K extends TransportEventType>(\n event: K,\n ...args: Parameters<TransportEvents[K]>\n ): void {\n const listeners = this._listeners.get(event);\n if (listeners) {\n for (const cb of listeners) {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (cb as (...a: any[]) => void)(...args);\n } catch (err) {\n console.warn(\n '[waveform-playlist] Transport \"' + event + '\" listener threw:',\n String(err)\n );\n }\n }\n }\n }\n}\n","import type { ClipTrack } from '@waveform-playlist/core';\nimport type { PlayoutAdapter } from '@waveform-playlist/engine';\nimport { Transport } from './transport';\nimport type { Tick, TransportOptions, CountInMode } from './types';\n\nexport class NativePlayoutAdapter implements PlayoutAdapter {\n private _transport: Transport;\n private _audioContext: AudioContext;\n\n constructor(audioContext: AudioContext, options?: TransportOptions) {\n this._audioContext = audioContext;\n this._transport = new Transport(audioContext, options);\n }\n\n get audioContext(): AudioContext {\n return this._audioContext;\n }\n\n get ppqn(): number {\n return this._transport.ppqn;\n }\n\n get transport(): Transport {\n return this._transport;\n }\n\n async init(): Promise<void> {\n if (this._audioContext.state === 'closed') {\n throw new Error('[waveform-playlist] Cannot init: AudioContext is closed');\n }\n if (this._audioContext.state === 'suspended') {\n await this._audioContext.resume();\n // Safari's audio thread may not be ready to output audio immediately\n // after resume() resolves. Wait for currentTime to advance past the\n // output latency, indicating the hardware pipeline is warm.\n // Minimum warmup ensures the audio thread has time to spin up even\n // when outputLatency reports 0 (Chrome on low-latency hardware).\n const MIN_WARMUP = 0.02; // 20ms\n const warmupTarget = Math.max(MIN_WARMUP, this._audioContext.outputLatency ?? MIN_WARMUP);\n if (this._audioContext.currentTime < warmupTarget) {\n const MAX_WARMUP_MS = 2000;\n await new Promise<void>((resolve) => {\n const startMs = performance.now();\n const check = () => {\n if (this._audioContext.currentTime >= warmupTarget) {\n resolve();\n } else if (\n this._audioContext.state === 'closed' ||\n performance.now() - startMs > MAX_WARMUP_MS\n ) {\n console.warn(\n '[waveform-playlist] AudioContext warmup timed out' +\n ' (currentTime=' +\n this._audioContext.currentTime +\n ', target=' +\n warmupTarget +\n ', state=' +\n this._audioContext.state +\n '). Proceeding without warmup.'\n );\n resolve();\n } else {\n requestAnimationFrame(check);\n }\n };\n requestAnimationFrame(check);\n });\n }\n }\n }\n\n setTracks(tracks: ClipTrack[]): void {\n this._transport.setTracks(tracks);\n }\n\n addTrack(track: ClipTrack): void {\n this._transport.addTrack(track);\n }\n\n removeTrack(trackId: string): void {\n this._transport.removeTrack(trackId);\n }\n\n updateTrack(trackId: string, track: ClipTrack): void {\n this._transport.updateTrack(trackId, track);\n }\n\n play(startTime: number, endTime?: number): void {\n this._transport.play(startTime, endTime);\n }\n\n pause(): void {\n this._transport.pause();\n }\n\n stop(): void {\n this._transport.stop();\n }\n\n seek(time: number): void {\n this._transport.seek(time);\n }\n\n getCurrentTime(): number {\n return this._transport.getCurrentTime();\n }\n\n isPlaying(): boolean {\n return this._transport.isPlaying();\n }\n\n setMasterVolume(volume: number): void {\n this._transport.setMasterVolume(volume);\n }\n\n setTrackVolume(trackId: string, volume: number): void {\n this._transport.setTrackVolume(trackId, volume);\n }\n\n setTrackMute(trackId: string, muted: boolean): void {\n this._transport.setTrackMute(trackId, muted);\n }\n\n setTrackSolo(trackId: string, soloed: boolean): void {\n this._transport.setTrackSolo(trackId, soloed);\n }\n\n setTrackPan(trackId: string, pan: number): void {\n this._transport.setTrackPan(trackId, pan);\n }\n\n setLoop(enabled: boolean, start: number, end: number): void {\n this._transport.setLoopSeconds(enabled, start, end);\n }\n\n setCountIn(enabled: boolean): void {\n this._transport.setCountIn(enabled);\n }\n\n setCountInBars(bars: number): void {\n this._transport.setCountInBars(bars);\n }\n\n setCountInMode(mode: CountInMode): void {\n this._transport.setCountInMode(mode);\n }\n\n setRecording(recording: boolean): void {\n this._transport.setRecording(recording);\n }\n\n isCountingIn(): boolean {\n return this._transport.isCountingIn();\n }\n\n setTempo(bpm: number, atTick?: number): void {\n this._transport.setTempo(bpm, atTick !== undefined ? (atTick as Tick) : undefined);\n }\n\n setMeter(numerator: number, denominator: number, atTick?: number): void {\n this._transport.setMeter(\n numerator,\n denominator,\n atTick !== undefined ? (atTick as Tick) : undefined\n );\n }\n\n ticksToSeconds(tick: number): number {\n return this._transport.tickToTime(tick as Tick);\n }\n\n secondsToTicks(seconds: number): number {\n return this._transport.timeToTick(seconds) as number;\n }\n\n get masterOutputNode(): AudioNode {\n return this._transport.masterOutputNode;\n }\n\n dispose(): void {\n this._transport.dispose();\n }\n}\n"],"mappings":"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","names":[]}