@dawcore/transport 0.0.1 → 0.0.3

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/tick-timeline.ts","../src/timeline/tempo-map.ts","../src/audio/master-node.ts","../src/audio/track-node.ts","../src/audio/clip-player.ts","../src/audio/metronome-player.ts","../src/transport.ts","../src/adapter.ts"],"sourcesContent":["// packages/transport/src/index.ts\nexport type {\n SchedulerEvent,\n SchedulerListener,\n TransportOptions,\n TempoEntry,\n TransportPosition,\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 { TickTimeline } from './timeline/tick-timeline';\nexport { TempoMap } from './timeline/tempo-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 { 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 { SchedulerEvent, SchedulerListener } from '../types';\n\nexport interface SchedulerOptions {\n lookahead?: number;\n /** Called when the scheduler wraps at loopEnd — Transport uses this to seek the clock */\n onLoop?: (loopStartTime: number) => void;\n}\n\nexport class Scheduler<T extends SchedulerEvent> {\n private _lookahead: number;\n private _rightEdge = 0;\n private _listeners: Set<SchedulerListener<T>> = new Set();\n private _loopEnabled = false;\n private _loopStart = 0;\n private _loopEnd = 0;\n private _onLoop: ((loopStartTime: number) => void) | undefined;\n\n constructor(options: SchedulerOptions = {}) {\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 setLoop(enabled: boolean, start: number, end: number): void {\n if (enabled && start >= end) {\n console.warn(\n '[waveform-playlist] Scheduler.setLoop: start (' +\n start +\n ') must be less than end (' +\n end +\n ')'\n );\n return;\n }\n this._loopEnabled = enabled;\n this._loopStart = start;\n this._loopEnd = end;\n }\n\n reset(time: number): void {\n this._rightEdge = time;\n }\n\n advance(currentTime: number): void {\n const targetEdge = currentTime + this._lookahead;\n\n if (this._loopEnabled && this._loopEnd > this._loopStart) {\n const loopDuration = this._loopEnd - this._loopStart;\n let remaining = targetEdge - this._rightEdge;\n\n // Handle multiple loop wraps (loop region shorter than lookahead)\n while (remaining > 0) {\n const distToEnd = this._loopEnd - this._rightEdge;\n if (distToEnd <= 0 || distToEnd > remaining) {\n // No wrap needed — generate remaining window\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\n for (const listener of this._listeners) {\n listener.onPositionJump(this._loopStart);\n }\n // Seek clock back to loopStart\n this._onLoop?.(this._loopStart);\n this._rightEdge = this._loopStart;\n\n // Guard against infinite loop from zero-length loop regions\n if (loopDuration <= 0) break;\n }\n return;\n }\n\n if (targetEdge > this._rightEdge) {\n this._generateAndConsume(this._rightEdge, targetEdge);\n this._rightEdge = targetEdge;\n }\n }\n\n private _generateAndConsume(from: number, to: number): void {\n for (const listener of this._listeners) {\n try {\n const events = listener.generate(from, to);\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 this._scheduleFrame();\n });\n }\n}\n","export class SampleTimeline {\n private _sampleRate: number;\n\n constructor(sampleRate: number) {\n this._sampleRate = sampleRate;\n }\n\n get sampleRate(): number {\n return this._sampleRate;\n }\n\n samplesToSeconds(samples: number): number {\n return samples / this._sampleRate;\n }\n\n secondsToSamples(seconds: number): number {\n return Math.round(seconds * this._sampleRate);\n }\n}\n","import type { TransportPosition } from '../types';\n\nexport class TickTimeline {\n private _ppqn: number;\n\n constructor(ppqn: number = 960) {\n this._ppqn = ppqn;\n }\n\n get ppqn(): number {\n return this._ppqn;\n }\n\n ticksPerBeat(): number {\n return this._ppqn;\n }\n\n ticksPerBar(beatsPerBar: number): number {\n return this._ppqn * beatsPerBar;\n }\n\n toPosition(ticks: number, beatsPerBar: number): TransportPosition {\n const ticksPerBar = this.ticksPerBar(beatsPerBar);\n const bar = Math.floor(ticks / ticksPerBar) + 1;\n const remaining = ticks % ticksPerBar;\n const beat = Math.floor(remaining / this._ppqn) + 1;\n const tick = remaining % this._ppqn;\n return { bar, beat, tick };\n }\n\n fromPosition(bar: number, beat: number, tick: number, beatsPerBar: number): number {\n const ticksPerBar = this.ticksPerBar(beatsPerBar);\n return (bar - 1) * ticksPerBar + (beat - 1) * this._ppqn + tick;\n }\n}\n","import type { TempoEntry } from '../types';\n\n/** Mutable internal version of TempoEntry (exported interface has readonly secondsAtTick) */\ninterface MutableTempoEntry {\n tick: number;\n bpm: number;\n secondsAtTick: number;\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, bpm: initialBpm, secondsAtTick: 0 }];\n }\n\n getTempo(atTick: number = 0): number {\n const entry = this._entryAt(atTick);\n return entry.bpm;\n }\n\n setTempo(bpm: number, atTick: number = 0): void {\n if (atTick === 0) {\n this._entries[0] = { ...this._entries[0], bpm };\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 };\n } else {\n const secondsAtTick = this._ticksToSecondsInternal(atTick);\n this._entries.splice(i + 1, 0, { tick: atTick, bpm, secondsAtTick });\n i = i + 1;\n }\n this._recomputeCache(i);\n }\n\n ticksToSeconds(ticks: number): number {\n return this._ticksToSecondsInternal(ticks);\n }\n\n secondsToTicks(seconds: number): 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].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 const ticksPerSecond = (entry.bpm / 60) * this._ppqn;\n return entry.tick + secondsIntoSegment * ticksPerSecond;\n }\n\n beatsToSeconds(beats: number): number {\n return this.ticksToSeconds(beats * this._ppqn);\n }\n\n secondsToBeats(seconds: number): number {\n return this.secondsToTicks(seconds) / this._ppqn;\n }\n\n private _ticksToSecondsInternal(ticks: number): number {\n const entry = this._entryAt(ticks);\n const ticksIntoSegment = ticks - entry.tick;\n const secondsPerTick = 60 / (entry.bpm * this._ppqn);\n return entry.secondsAtTick + ticksIntoSegment * secondsPerTick;\n }\n\n private _entryAt(tick: number): TempoEntry {\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 _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 secondsPerTick = 60 / (prev.bpm * this._ppqn);\n this._entries[i] = {\n ...this._entries[i],\n secondsAtTick: prev.secondsAtTick + tickDelta * secondsPerTick,\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 { SchedulerEvent, SchedulerListener } from '../types';\nimport type { SampleTimeline } from '../timeline/sample-timeline';\nimport type { TrackNode } from './track-node';\n\nexport interface ClipEvent extends SchedulerEvent {\n trackId: string;\n clipId: string;\n audioBuffer: AudioBuffer;\n /** Offset into the audioBuffer (seconds) */\n offset: number;\n /** Duration to play (seconds) */\n duration: number;\n /** Clip gain multiplier */\n gain: number;\n /** Fade in duration in seconds */\n fadeInDuration: number;\n /** Fade out duration in seconds */\n fadeOutDuration: number;\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 _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 _loopEnd = 0;\n\n constructor(\n audioContext: AudioContext,\n sampleTimeline: SampleTimeline,\n toAudioTime: (transportTime: number) => number\n ) {\n this._audioContext = audioContext;\n this._sampleTimeline = sampleTimeline;\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 setLoop(enabled: boolean, _start: number, end: number): void {\n this._loopEnabled = enabled;\n this._loopEnd = end;\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(fromTime: number, toTime: number): 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 const clipStartTime = this._sampleTimeline.samplesToSeconds(clip.startSample);\n const clipDuration = this._sampleTimeline.samplesToSeconds(clip.durationSamples);\n const clipOffsetTime = this._sampleTimeline.samplesToSeconds(clip.offsetSamples);\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 (clipStartTime < fromTime) continue;\n if (clipStartTime >= toTime) continue;\n\n const fadeInDuration = clip.fadeIn\n ? this._sampleTimeline.samplesToSeconds(clip.fadeIn.duration ?? 0)\n : 0;\n const fadeOutDuration = clip.fadeOut\n ? this._sampleTimeline.samplesToSeconds(clip.fadeOut.duration ?? 0)\n : 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 duration = clipDuration;\n if (this._loopEnabled && clipStartTime + duration > this._loopEnd) {\n duration = this._loopEnd - clipStartTime;\n }\n\n events.push({\n trackId,\n clipId: clip.id,\n audioBuffer: clip.audioBuffer,\n transportTime: clipStartTime,\n offset: clipOffsetTime,\n duration,\n gain: clip.gain,\n fadeInDuration,\n fadeOutDuration,\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 // Guard against invalid offset\n if (event.offset >= event.audioBuffer.duration) {\n return;\n }\n\n const source = this._audioContext.createBufferSource();\n source.buffer = event.audioBuffer;\n\n // Convert transport time → AudioContext.currentTime for scheduling\n const when = this._toAudioTime(event.transportTime);\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.fadeInDuration;\n let fadeOut = event.fadeOutDuration;\n if (fadeIn + fadeOut > event.duration) {\n const ratio = event.duration / (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 + event.duration - fadeOut;\n gainNode.gain.setValueAtTime(event.gain, fadeOutStart);\n gainNode.gain.linearRampToValueAtTime(0, when + event.duration);\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, event.offset, event.duration);\n }\n\n onPositionJump(newTime: number): void {\n this.silence();\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 const clipStartTime = this._sampleTimeline.samplesToSeconds(clip.startSample);\n const clipDuration = this._sampleTimeline.samplesToSeconds(clip.durationSamples);\n const clipEndTime = clipStartTime + clipDuration;\n const clipOffsetTime = this._sampleTimeline.samplesToSeconds(clip.offsetSamples);\n\n // Check if clip spans the new position\n if (clipStartTime <= newTime && clipEndTime > newTime) {\n const offsetIntoClip = newTime - clipStartTime;\n const offset = clipOffsetTime + offsetIntoClip;\n const duration = clipEndTime - newTime;\n\n const fadeOutDuration = clip.fadeOut\n ? this._sampleTimeline.samplesToSeconds(clip.fadeOut.duration ?? 0)\n : 0;\n\n this.consume({\n trackId,\n clipId: clip.id,\n audioBuffer: clip.audioBuffer,\n transportTime: newTime,\n offset,\n duration,\n gain: clip.gain,\n fadeInDuration: 0,\n fadeOutDuration,\n });\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 { SchedulerEvent, SchedulerListener } from '../types';\nimport type { TempoMap } from '../timeline/tempo-map';\nimport type { TickTimeline } from '../timeline/tick-timeline';\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 _tickTimeline: TickTimeline;\n private _destination: AudioNode;\n private _toAudioTime: (transportTime: number) => number;\n private _enabled = false;\n private _beatsPerBar = 4;\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 tickTimeline: TickTimeline,\n destination: AudioNode,\n toAudioTime: (transportTime: number) => number\n ) {\n this._audioContext = audioContext;\n this._tempoMap = tempoMap;\n this._tickTimeline = tickTimeline;\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 setBeatsPerBar(beats: number): void {\n this._beatsPerBar = beats;\n }\n\n setClickSounds(accent: AudioBuffer, normal: AudioBuffer): void {\n this._accentBuffer = accent;\n this._normalBuffer = normal;\n }\n\n generate(fromTime: number, toTime: number): MetronomeEvent[] {\n if (!this._enabled || !this._accentBuffer || !this._normalBuffer) {\n return [];\n }\n\n const events: MetronomeEvent[] = [];\n const ppqn = this._tickTimeline.ppqn;\n\n // Convert time window to ticks\n const fromTicks = this._tempoMap.secondsToTicks(fromTime);\n const toTicks = this._tempoMap.secondsToTicks(toTime);\n\n // Find first beat at or after fromTicks\n const firstBeatTick = Math.ceil(fromTicks / ppqn) * ppqn;\n\n for (let tick = firstBeatTick; tick < toTicks; tick += ppqn) {\n const transportTime = this._tempoMap.ticksToSeconds(tick);\n const ticksPerBar = this._tickTimeline.ticksPerBar(this._beatsPerBar);\n const isAccent = tick % ticksPerBar === 0;\n\n events.push({\n transportTime,\n isAccent,\n buffer: isAccent ? this._accentBuffer : this._normalBuffer,\n });\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 source.start(this._toAudioTime(event.transportTime));\n }\n\n onPositionJump(_newTime: number): void {\n this.silence();\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 { ClipTrack } from '@waveform-playlist/core';\nimport type { TransportOptions } from './types';\nimport { Clock } from './core/clock';\nimport { Scheduler } from './core/scheduler';\nimport { Timer } from './core/timer';\nimport { SampleTimeline } from './timeline/sample-timeline';\nimport { TickTimeline } from './timeline/tick-timeline';\nimport { TempoMap } from './timeline/tempo-map';\nimport { ClipPlayer } from './audio/clip-player';\nimport { MetronomePlayer } from './audio/metronome-player';\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: () => 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 _tickTimeline: TickTimeline;\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 _listeners: Map<TransportEventType, Set<TransportEvents[TransportEventType]>> = new Map();\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 beatsPerBar = options.beatsPerBar ?? 4;\n const lookahead = options.schedulerLookahead ?? 0.2;\n\n Transport._validateOptions(sampleRate, ppqn, tempo, beatsPerBar, lookahead);\n\n this._clock = new Clock(audioContext);\n this._scheduler = new Scheduler({\n lookahead,\n onLoop: (loopStartTime: number) => {\n this._clock.seekTo(loopStartTime);\n },\n });\n this._sampleTimeline = new SampleTimeline(sampleRate);\n this._tickTimeline = new TickTimeline(ppqn);\n this._tempoMap = new TempoMap(ppqn, tempo);\n\n this._initAudioGraph(audioContext, beatsPerBar);\n\n this._timer = new Timer(() => {\n const time = this._clock.getTime();\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 // --- 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 // 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 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 this._clipPlayer.onPositionJump(currentTime);\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 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 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\n if (wasPlaying) {\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 if (wasPlaying) {\n this._clock.start();\n // Re-create sources for clips spanning the seek position\n this._clipPlayer.onPositionJump(time);\n this._timer.start();\n }\n }\n\n getCurrentTime(): number {\n return this._clock.getTime();\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 setLoop(enabled: boolean, start: number, end: number): void {\n if (enabled && start >= end) {\n console.warn(\n '[waveform-playlist] Transport.setLoop: start (' +\n start +\n ') must be less than end (' +\n end +\n ')'\n );\n return;\n }\n this._scheduler.setLoop(enabled, start, end);\n this._clipPlayer.setLoop(enabled, start, end);\n this._emit('loop');\n }\n\n // --- Tempo ---\n\n setTempo(bpm: number): void {\n this._tempoMap.setTempo(bpm);\n this._emit('tempochange');\n }\n\n getTempo(): number {\n return this._tempoMap.getTempo();\n }\n\n setBeatsPerBar(beats: number): void {\n this._metronomePlayer.setBeatsPerBar(beats);\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._metronomePlayer.setClickSounds(accent, normal);\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 this._listeners.get(event)!.add(cb);\n }\n\n off<K extends TransportEventType>(event: K, cb: TransportEvents[K]): void {\n this._listeners.get(event)?.delete(cb);\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 beatsPerBar: 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 (beatsPerBar <= 0 || !Number.isInteger(beatsPerBar)) {\n throw new Error(\n '[waveform-playlist] Transport: beatsPerBar must be a positive integer, got ' + beatsPerBar\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, beatsPerBar: number): 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(audioContext, this._sampleTimeline, toAudioTime);\n this._metronomePlayer = new MetronomePlayer(\n audioContext,\n this._tempoMap,\n this._tickTimeline,\n this._masterNode.input,\n toAudioTime\n );\n this._metronomePlayer.setBeatsPerBar(beatsPerBar);\n\n this._scheduler.addListener(this._clipPlayer);\n this._scheduler.addListener(this._metronomePlayer);\n }\n\n private _silenceAll(): void {\n this._clipPlayer.silence();\n this._metronomePlayer.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(event: TransportEventType): void {\n const listeners = this._listeners.get(event);\n if (listeners) {\n for (const cb of listeners) {\n try {\n cb();\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 { TransportOptions } 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 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 }\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.setLoop(enabled, start, end);\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/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 TransportPosition,\n MeterSignature,\n MeterEntry,\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 } 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 { 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 this._scheduleFrame();\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, TempoEntry } from '../types';\n\n/** Mutable internal version of TempoEntry (exported interface has readonly secondsAtTick) */\ninterface MutableTempoEntry {\n tick: Tick;\n bpm: number;\n secondsAtTick: number;\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, secondsAtTick: 0 }];\n }\n\n getTempo(atTick: Tick = 0 as Tick): number {\n const entry = this._entryAt(atTick);\n return entry.bpm;\n }\n\n setTempo(bpm: number, atTick: Tick = 0 as Tick): void {\n if (atTick === 0) {\n this._entries[0] = { ...this._entries[0], bpm };\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 };\n } else {\n const secondsAtTick = this._ticksToSecondsInternal(atTick);\n this._entries.splice(i + 1, 0, { tick: atTick, bpm, 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 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, secondsAtTick: 0 }];\n }\n\n private _ticksToSecondsInternal(ticks: Tick): number {\n const entry = this._entryAt(ticks);\n const ticksIntoSegment = ticks - entry.tick;\n const secondsPerTick = 60 / (entry.bpm * this._ppqn);\n return entry.secondsAtTick + ticksIntoSegment * secondsPerTick;\n }\n\n private _entryAt(tick: Tick): TempoEntry {\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 _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 secondsPerTick = 60 / (prev.bpm * this._ppqn);\n this._entries[i] = {\n ...this._entries[i],\n secondsAtTick: prev.secondsAtTick + tickDelta * secondsPerTick,\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 const fromSample = this._sampleTimeline.ticksToSamples(fromTick);\n const toSample = this._sampleTimeline.ticksToSamples(toTick);\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 const clipStartSample = clip.startSample;\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 (clipStartSample < fromSample) continue;\n if (clipStartSample >= toSample) 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 && clipStartSample + durationSamples > this._loopEndSamples) {\n durationSamples = this._loopEndSamples - clipStartSample;\n }\n\n const clipTick = this._sampleTimeline.samplesToTicks(clipStartSample as Sample);\n\n events.push({\n trackId,\n clipId: clip.id,\n audioBuffer: clip.audioBuffer,\n tick: clipTick,\n startSample: clipStartSample 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 const clipStartSample = clip.startSample;\n const clipEndSample = clipStartSample + clip.durationSamples;\n\n // Check if clip spans the new position\n if (clipStartSample <= newSample && clipEndSample > newSample) {\n const offsetIntoClipSamples = newSample - clipStartSample;\n const offsetSamples = clip.offsetSamples + offsetIntoClipSamples;\n let durationSamples = clipEndSample - newSample;\n\n // Clamp at loop boundary (same as generate)\n if (this._loopEnabled && newSample + durationSamples > this._loopEndSamples) {\n durationSamples = this._loopEndSamples - newSample;\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\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 { ClipTrack } from '@waveform-playlist/core';\nimport type { Tick, Sample, TransportOptions, MeterSignature } from './types';\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 { 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: () => void;\n meterchange: () => 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 _loopStartSeconds = 0;\n private _listeners: Map<TransportEventType, Set<TransportEvents[TransportEventType]>> = new Map();\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 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\n this._timer = new Timer(() => {\n const time = this._clock.getTime();\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 // --- 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 // 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 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 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 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\n if (wasPlaying) {\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 if (wasPlaying) {\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 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._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._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): void {\n this._tempoMap.setTempo(bpm, atTick);\n this._emit('tempochange');\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');\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 this._emit('meterchange');\n }\n\n clearMeters(): void {\n this._meterMap.clearMeters();\n this._emit('meterchange');\n }\n\n clearTempos(): void {\n this._tempoMap.clearTempos();\n this._emit('tempochange');\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._metronomePlayer.setClickSounds(accent, normal);\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 this._listeners.get(event)!.add(cb);\n }\n\n off<K extends TransportEventType>(event: K, cb: TransportEvents[K]): void {\n this._listeners.get(event)?.delete(cb);\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 _silenceAll(): void {\n this._clipPlayer.silence();\n this._metronomePlayer.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(event: TransportEventType): void {\n const listeners = this._listeners.get(event);\n if (listeners) {\n for (const cb of listeners) {\n try {\n cb();\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 { TransportOptions } 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 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 }\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 dispose(): void {\n this._transport.dispose();\n }\n}\n"],"mappings":"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","names":[]}