gapless 4.1.2 → 4.2.1

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.d.ts CHANGED
@@ -122,9 +122,29 @@ declare class Track {
122
122
  private readonly _playbackMethod;
123
123
  private get ctx();
124
124
  private gainNode;
125
+ private _mediaElementSource;
126
+ private _html5GainNode;
125
127
  private sourceNode;
126
128
  audioBuffer: AudioBuffer | null;
127
129
  /** AudioContext.currentTime at the start of the current playback segment. */
130
+ /**
131
+ * Offset (in seconds) between the decoded buffer's "offset 0" and the music's
132
+ * "offset 0". Some MP3 files include ID3v2 metadata, encoder priming samples,
133
+ * or container padding at the start; HTML5 audio elements skip past these
134
+ * natively (audio.currentTime=0 means music start), but `decodeAudioData` in
135
+ * some browsers includes them in the decoded buffer (buffer offset 0 = file
136
+ * start, music actually starts at offset _bufferStartPaddingSec).
137
+ *
138
+ * Without this shift, calling source.start(when, audio.currentTime) plays
139
+ * `audio.currentTime` seconds AHEAD of what HTML5 was just outputting,
140
+ * sounding like a backward skip at crossover. We compute this once both
141
+ * the buffer and audio.duration are known, and apply it as
142
+ * source.start(when, trackTime + _bufferStartPaddingSec)
143
+ * everywhere we read from the buffer. User-facing time (currentTime/duration
144
+ * getters) continues to be reported in music-time, not buffer-time.
145
+ */
146
+ private _bufferStartPaddingSec;
147
+ private _bufferAlignmentMeasured;
128
148
  private _waRefCtxTime;
129
149
  /** Track position (seconds) at the start of the current playback segment. */
130
150
  private _waRefTrackTime;
@@ -167,9 +187,39 @@ declare class Track {
167
187
  toInfo(): TrackInfo;
168
188
  private _playHtml5;
169
189
  private _seekHtml5;
190
+ /**
191
+ * Mid-stream crossover: switch an actively-playing HTML5 track to Web Audio.
192
+ *
193
+ * Why this exists: we cannot reliably predict when an HTML5 <audio> element
194
+ * will fire 'ended' from within the AudioContext clock. Any prediction is
195
+ * at the mercy of the browser's audio pipeline (buffering stalls, codec
196
+ * padding differences, clock drift between audio.currentTime and
197
+ * ctx.currentTime over long sessions). Scheduling the next gapless track
198
+ * against that prediction is how overlap bugs happen.
199
+ *
200
+ * Instead, as soon as the buffer is decoded, we hand playback off to Web
201
+ * Audio while the track is still mid-song. From that point on, the track
202
+ * and all subsequent gapless transitions live on a single clock
203
+ * (AudioContext.currentTime), so scheduling is sample-accurate by
204
+ * construction — no prediction involved.
205
+ *
206
+ * Ordering: pause the HTML5 element FIRST, then start the source node at
207
+ * the captured offset. Pausing first ensures audio.currentTime is frozen
208
+ * before we read it as the Web Audio start offset, so there's no brief
209
+ * double-audio window at the crossover point.
210
+ */
211
+ private _crossoverHtml5ToWebAudio;
212
+ /**
213
+ * Reverted: alignment-based fixes (duration-delta and buffer-silence
214
+ * scanning) reduced the perceived skip on archive.org files but did not
215
+ * eliminate it, suggesting the residual gap isn't a buffer/timeline
216
+ * alignment problem at all. Leaving _bufferStartPaddingSec at 0 (no shift)
217
+ * until we have a confirmed root cause; the field and call sites are kept
218
+ * so we can re-introduce a fix without churning the source-start code.
219
+ */
220
+ private _maybeComputeBufferAlignment;
170
221
  private _startSourceNode;
171
222
  private _stopSourceNode;
172
- private _disconnectGain;
173
223
  private _seekWebAudio;
174
224
  private _handleWebAudioEnded;
175
225
  startProgressLoop(): void;
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import{createActor as z}from"xstate";var W=typeof window<"u",p;function q(){if(!W)return null;let o=window.AudioContext??window.webkitAudioContext;return o?new o:null}function k(){return p===void 0?null:p}function y(){p===void 0&&(p=q());let o=p;return o&&o.state==="suspended"?o.resume():Promise.resolve()}var g=typeof navigator<"u"&&"mediaSession"in navigator;function v(o){if(!g)return;let{mediaSession:e}=navigator;e.setActionHandler("play",o.onPlay),e.setActionHandler("pause",o.onPause),e.setActionHandler("nexttrack",o.onNext),e.setActionHandler("previoustrack",o.onPrevious),e.setActionHandler("seekto",t=>{t.seekTime!=null&&o.onSeek(t.seekTime)})}function P(o){if(g){if(!o){navigator.mediaSession.metadata=null;return}navigator.mediaSession.metadata=new MediaMetadata({title:o.title??"",artist:o.artist??"",album:o.album??"",artwork:o.artwork??[]})}}function b(o){g&&(navigator.mediaSession.playbackState=o?"playing":"paused")}function C(o,e,t=1){if(g)try{navigator.mediaSession.setPositionState({duration:o,position:e,playbackRate:t})}catch{}}import{setup as V,assign as h}from"xstate";function N(o){return V({types:{context:{},events:{}},guards:{hasNextTrack:({context:e})=>e.currentTrackIndex+1<e.trackCount,playImmediately:({event:e})=>!!e.playImmediately,willBeEmpty:({context:e})=>e.trackCount<=1,isRemovingCurrent:({context:e,event:t})=>t.index===e.currentTrackIndex},actions:{incrementTrackCount:h({trackCount:({context:e})=>e.trackCount+1}),decrementTrackCount:h({trackCount:({context:e})=>Math.max(0,e.trackCount-1),currentTrackIndex:({context:e,event:t})=>{let n=t,a=Math.max(0,e.trackCount-1);return n.index<e.currentTrackIndex?Math.max(0,e.currentTrackIndex-1):a>0?Math.min(e.currentTrackIndex,a-1):0}}),gotoTrackIndex:h({currentTrackIndex:({event:e})=>e.index}),advanceToNextTrack:h({currentTrackIndex:({context:e})=>{let t=e.currentTrackIndex+1;return t<e.trackCount?t:e.currentTrackIndex}}),goToPreviousTrack:h({currentTrackIndex:({context:e})=>Math.max(0,e.currentTrackIndex-1)}),advanceOnTrackEnd:h({currentTrackIndex:({context:e})=>Math.min(e.currentTrackIndex+1,e.trackCount-1)}),resetToFirstTrack:h({currentTrackIndex:()=>0}),deactivateCurrent:()=>{},deactivateEndedTrack:()=>{},activateAndPlayCurrent:()=>{},playOrContinueGapless:()=>{},cancelAllGapless:()=>{},notifyStartNewTrack:()=>{},notifyPlayNextTrack:()=>{},notifyPlayPreviousTrack:()=>{},notifyEnded:()=>{},updateMediaSessionMetadata:()=>{},preloadAhead:()=>{},playCurrent:()=>{},pauseCurrent:()=>{},seekCurrent:()=>{},seekCurrentToZero:()=>{},scheduleGapless:()=>{},cancelScheduledGapless:()=>{},cancelAndRescheduleGapless:()=>{}}}).createMachine({id:"queue",initial:"idle",context:o,on:{ADD_TRACK:{actions:"incrementTrackCount"},REMOVE_TRACK:{actions:"decrementTrackCount"}},states:{idle:{on:{PLAY:{target:"playing",actions:["playCurrent","updateMediaSessionMetadata","preloadAhead","scheduleGapless"]},GOTO:[{guard:"playImmediately",target:"playing",actions:["deactivateCurrent","cancelAllGapless","gotoTrackIndex","activateAndPlayCurrent","notifyStartNewTrack","updateMediaSessionMetadata","preloadAhead"]},{target:"paused",actions:["deactivateCurrent","cancelAllGapless","gotoTrackIndex","seekCurrentToZero","preloadAhead"]}],TRACK_LOADED:{actions:["preloadAhead"]}}},playing:{on:{REMOVE_TRACK:[{guard:"willBeEmpty",target:"idle",actions:["cancelAllGapless","decrementTrackCount"]},{guard:"isRemovingCurrent",actions:["cancelAllGapless","decrementTrackCount","activateAndPlayCurrent","notifyStartNewTrack","updateMediaSessionMetadata","preloadAhead"]},{actions:["decrementTrackCount","cancelAndRescheduleGapless","preloadAhead"]}],PAUSE:{target:"paused",actions:["cancelScheduledGapless","pauseCurrent"]},TOGGLE:{target:"paused",actions:["cancelScheduledGapless","pauseCurrent"]},NEXT:{actions:["deactivateCurrent","cancelAllGapless","advanceToNextTrack","activateAndPlayCurrent","notifyStartNewTrack","notifyPlayNextTrack","updateMediaSessionMetadata","preloadAhead"]},PREVIOUS:{actions:["deactivateCurrent","cancelAllGapless","goToPreviousTrack","activateAndPlayCurrent","notifyStartNewTrack","notifyPlayPreviousTrack","updateMediaSessionMetadata","preloadAhead"]},GOTO:[{guard:"playImmediately",actions:["deactivateCurrent","cancelAllGapless","gotoTrackIndex","activateAndPlayCurrent","notifyStartNewTrack","updateMediaSessionMetadata","preloadAhead"]},{actions:["deactivateCurrent","cancelAllGapless","gotoTrackIndex","seekCurrentToZero","preloadAhead"]}],SEEK:{actions:["seekCurrent","cancelAndRescheduleGapless"]},TRACK_ENDED:[{guard:"hasNextTrack",target:"playing",actions:["deactivateEndedTrack","advanceOnTrackEnd","playOrContinueGapless","notifyStartNewTrack","notifyPlayNextTrack","updateMediaSessionMetadata","preloadAhead"]},{target:"ended",actions:["deactivateEndedTrack","notifyEnded"]}],TRACK_LOADED:{actions:["scheduleGapless","preloadAhead"]}}},paused:{on:{REMOVE_TRACK:[{guard:"willBeEmpty",target:"idle",actions:["cancelAllGapless","decrementTrackCount"]},{guard:"isRemovingCurrent",actions:["cancelAllGapless","decrementTrackCount","preloadAhead"]},{actions:["decrementTrackCount","preloadAhead"]}],PLAY:{target:"playing",actions:["playCurrent","updateMediaSessionMetadata","preloadAhead","scheduleGapless"]},TOGGLE:{target:"playing",actions:["playCurrent","updateMediaSessionMetadata","preloadAhead","scheduleGapless"]},NEXT:{actions:["deactivateCurrent","cancelAllGapless","advanceToNextTrack","activateAndPlayCurrent","notifyStartNewTrack","notifyPlayNextTrack","updateMediaSessionMetadata","preloadAhead"]},PREVIOUS:{actions:["deactivateCurrent","cancelAllGapless","goToPreviousTrack","activateAndPlayCurrent","notifyStartNewTrack","notifyPlayPreviousTrack","updateMediaSessionMetadata","preloadAhead"]},GOTO:[{guard:"playImmediately",target:"playing",actions:["deactivateCurrent","cancelAllGapless","gotoTrackIndex","activateAndPlayCurrent","notifyStartNewTrack","updateMediaSessionMetadata","preloadAhead"]},{actions:["deactivateCurrent","cancelAllGapless","gotoTrackIndex","seekCurrentToZero","preloadAhead"]}],SEEK:{actions:["seekCurrent"]},TRACK_ENDED:[{guard:"hasNextTrack",target:"paused",actions:["deactivateEndedTrack","advanceOnTrackEnd","notifyStartNewTrack","updateMediaSessionMetadata"]},{target:"ended",actions:["deactivateEndedTrack","notifyEnded"]}],TRACK_LOADED:{actions:["preloadAhead"]}}},ended:{on:{ADD_TRACK:{target:"paused",actions:"incrementTrackCount"},PLAY:{target:"playing",actions:["resetToFirstTrack","playCurrent","updateMediaSessionMetadata","preloadAhead","scheduleGapless"]},GOTO:[{guard:"playImmediately",target:"playing",actions:["deactivateCurrent","cancelAllGapless","gotoTrackIndex","activateAndPlayCurrent","notifyStartNewTrack","updateMediaSessionMetadata","preloadAhead"]},{target:"paused",actions:["deactivateCurrent","cancelAllGapless","gotoTrackIndex","seekCurrentToZero","preloadAhead"]}]}}}})}import{createActor as Z,fromPromise as x}from"xstate";import{setup as Y,assign as d,spawnChild as Q}from"xstate";import{setup as K,assign as _,sendParent as f,fromPromise as E}from"xstate";var m=K({types:{context:{},input:{}},actors:{resolveUrl:E(async()=>null),fetchAudio:E(async()=>{}),decodeAudio:E(async()=>{})},guards:{shouldSkipHEAD:({context:o})=>o.skipHEAD}}).createMachine({id:"fetchDecode",initial:"resolvingUrl",context:{trackUrl:"",resolvedUrl:"",skipHEAD:!1},entry:_(({event:o})=>{let{input:e}=o;return{trackUrl:e.trackUrl,resolvedUrl:e.resolvedUrl,skipHEAD:e.skipHEAD}}),states:{resolvingUrl:{always:{guard:"shouldSkipHEAD",target:"fetching"},invoke:{id:"resolveUrl",src:"resolveUrl",input:({context:o})=>({trackUrl:o.trackUrl}),onDone:{target:"fetching",actions:[_({resolvedUrl:({event:o,context:e})=>o.output??e.resolvedUrl,skipHEAD:()=>!0}),f(({event:o,context:e})=>({type:"URL_RESOLVED",url:o.output??e.resolvedUrl}))]},onError:{target:"fetching",actions:_({skipHEAD:()=>!0})}}},fetching:{invoke:{id:"fetchAudio",src:"fetchAudio",input:({context:o})=>({resolvedUrl:o.resolvedUrl}),onDone:"decoding",onError:{target:"error",actions:f({type:"BUFFER_ERROR"})}}},decoding:{invoke:{id:"decodeAudio",src:"decodeAudio",input:()=>{},onDone:{target:"done",actions:f({type:"BUFFER_READY"})},onError:{target:"error",actions:f({type:"BUFFER_ERROR"})}}},done:{type:"final"},error:{type:"final"}}});function R(o){return Y({types:{context:{},events:{}},actors:{fetchDecode:m},guards:{canPlayWebAudio:()=>!1,isWebAudioOnly:()=>!1,canStartFetch:({context:e})=>e.webAudioLoadingState==="NONE"&&!e.fetchStarted},actions:{playHtml5:()=>{},startSourceNode:()=>{},startScheduledSourceNode:()=>{},startProgressLoop:()=>{},pauseHtml5:()=>{},freezePausedTime:()=>{},stopSourceNode:()=>{},disconnectGain:()=>{},stopProgressLoop:()=>{},reportProgress:()=>{},seekHtml5:()=>{},seekWebAudio:()=>{},resetHtml5Element:()=>{},resetTiming:()=>{},notifyTrackEnded:()=>{},triggerFetchForPendingPlay:()=>{},setPendingPlay:d({pendingPlay:()=>!0}),clearPendingPlay:d({pendingPlay:()=>!1}),setIsPlaying:d({isPlaying:()=>!0}),clearIsPlaying:d({isPlaying:()=>!1}),setLoadingState:d({webAudioLoadingState:()=>"LOADING"}),setLoadedState:d({webAudioLoadingState:()=>"LOADED"}),setErrorState:d({webAudioLoadingState:()=>"ERROR"}),clearScheduleAndLookahead:d({scheduledStartContextTime:()=>null,notifiedLookahead:()=>!1}),setPlayingWebAudio:d({isPlaying:()=>!0,webAudioLoadingState:()=>"LOADED",playbackType:()=>"WEBAUDIO"}),setScheduledGapless:d({isPlaying:()=>!0,webAudioLoadingState:()=>"LOADED",playbackType:()=>"WEBAUDIO",scheduledStartContextTime:({event:e})=>e.when}),clearPlayingAndSchedule:d({isPlaying:()=>!1,scheduledStartContextTime:()=>null,notifiedLookahead:()=>!1}),setNotifiedLookahead:d({notifiedLookahead:()=>!0}),setResolvedUrl:d({resolvedUrl:({event:e})=>e.url}),clearScheduledStart:d({scheduledStartContextTime:()=>null}),setPlayingWebAudioType:d({isPlaying:()=>!0,playbackType:()=>"WEBAUDIO"})}}).createMachine({id:"track",initial:"idle",context:o,on:{START_FETCH:{guard:"canStartFetch",actions:[d({webAudioLoadingState:()=>"LOADING",fetchStarted:()=>!0}),Q("fetchDecode",{id:"fetchDecode",input:({context:e})=>({trackUrl:e.trackUrl,resolvedUrl:e.resolvedUrl,skipHEAD:e.skipHEAD})})]}},states:{idle:{on:{HTML5_ENDED:{actions:["notifyTrackEnded"]},DEACTIVATE:{actions:["pauseHtml5","resetHtml5Element","resetTiming","stopProgressLoop","clearScheduleAndLookahead"]},ACTIVATE:{actions:["resetTiming","resetHtml5Element","clearScheduleAndLookahead"]},PLAY:[{guard:"canPlayWebAudio",target:"webaudio",actions:["setPlayingWebAudio","startSourceNode","startProgressLoop"]},{guard:"isWebAudioOnly",actions:["setPendingPlay","triggerFetchForPendingPlay"]},{target:"html5",actions:["setIsPlaying","playHtml5","startProgressLoop"]}],PLAY_WEBAUDIO:{target:"webaudio",actions:["setPlayingWebAudio","startSourceNode","startProgressLoop"]},SCHEDULE_GAPLESS:{target:"webaudio",actions:["setScheduledGapless","startScheduledSourceNode"]},PRELOAD:{target:"loading"},BUFFER_LOADING:{actions:"setLoadingState"},BUFFER_READY:[{guard:({context:e})=>e.pendingPlay,target:"webaudio",actions:["clearPendingPlay","setPlayingWebAudio","startSourceNode","startProgressLoop"]},{actions:"setLoadedState"}],BUFFER_ERROR:{actions:["setErrorState","clearPendingPlay"]},URL_RESOLVED:{actions:"setResolvedUrl"}}},html5:{on:{PAUSE:{actions:["clearIsPlaying","pauseHtml5","stopProgressLoop","reportProgress"]},PLAY:{actions:["setIsPlaying","playHtml5","startProgressLoop"]},BUFFER_LOADING:{actions:"setLoadingState"},PLAY_WEBAUDIO:{target:"webaudio",actions:"setPlayingWebAudio"},BUFFER_READY:{actions:"setLoadedState"},BUFFER_ERROR:{actions:"setErrorState"},SEEK:{actions:["seekHtml5","reportProgress"]},LOOKAHEAD_REACHED:{actions:"setNotifiedLookahead"},HTML5_ENDED:{target:"idle",actions:["clearIsPlaying","stopProgressLoop","notifyTrackEnded"]},ACTIVATE:{target:"idle",actions:["clearPlayingAndSchedule","pauseHtml5","stopProgressLoop","resetTiming","resetHtml5Element"]},URL_RESOLVED:{actions:"setResolvedUrl"},DEACTIVATE:{target:"idle",actions:["clearIsPlaying","pauseHtml5","resetHtml5Element","resetTiming","stopProgressLoop"]}}},loading:{on:{BUFFER_LOADING:{actions:"setLoadingState"},BUFFER_READY:[{guard:({context:e})=>e.pendingPlay,target:"webaudio",actions:["clearPendingPlay","setPlayingWebAudio","startSourceNode","startProgressLoop"]},{target:"idle",actions:"setLoadedState"}],BUFFER_ERROR:{target:"idle",actions:["setErrorState","clearPendingPlay"]},PLAY:[{guard:"canPlayWebAudio",target:"webaudio",actions:["setPlayingWebAudio","startSourceNode","startProgressLoop"]},{guard:"isWebAudioOnly",actions:["setPendingPlay","triggerFetchForPendingPlay"]},{target:"html5",actions:["setIsPlaying","playHtml5","startProgressLoop"]}],PLAY_WEBAUDIO:{target:"webaudio",actions:["setPlayingWebAudio","startSourceNode","startProgressLoop"]},SCHEDULE_GAPLESS:{target:"webaudio",actions:["setScheduledGapless","startScheduledSourceNode"]},ACTIVATE:{target:"idle",actions:["clearPlayingAndSchedule","resetTiming","resetHtml5Element"]},DEACTIVATE:{target:"idle",actions:["clearIsPlaying","resetTiming"]},URL_RESOLVED:{actions:"setResolvedUrl"}}},webaudio:{on:{PAUSE:{actions:["clearIsPlaying","freezePausedTime","stopSourceNode","disconnectGain","stopProgressLoop","reportProgress"]},PLAY:[{guard:"canPlayWebAudio",actions:["setIsPlaying","startSourceNode","startProgressLoop"]},{actions:"setIsPlaying"}],PLAY_WEBAUDIO:{actions:"setPlayingWebAudioType"},SEEK:{actions:["clearScheduledStart","seekWebAudio","reportProgress"]},SET_VOLUME:{},CANCEL_GAPLESS:{target:"idle",actions:["clearPlayingAndSchedule","stopSourceNode","disconnectGain","stopProgressLoop","resetTiming"]},LOOKAHEAD_REACHED:{actions:"setNotifiedLookahead"},WEBAUDIO_ENDED:{target:"idle",actions:["clearIsPlaying","stopProgressLoop","notifyTrackEnded"]},ACTIVATE:{target:"idle",actions:["clearPlayingAndSchedule","stopSourceNode","disconnectGain","stopProgressLoop","resetTiming","resetHtml5Element"]},DEACTIVATE:{target:"idle",actions:["clearPlayingAndSchedule","stopSourceNode","disconnectGain","resetTiming","resetHtml5Element","stopProgressLoop"]}}}}})}var X=5,L=15,T=class{index;metadata;_trackUrl;_resolvedUrl;skipHEAD;_pendingArrayBuffer=null;audio;_playbackMethod;get ctx(){if(this._playbackMethod==="HTML5_ONLY")return null;let e=k();return e&&!this.gainNode&&(this.gainNode=e.createGain(),this.gainNode.gain.value=this.audio.volume),e}gainNode=null;sourceNode=null;audioBuffer=null;_waRefCtxTime=0;_waRefTrackTime=0;pausedAtTrackTime=0;_actor;queueRef;rafId=null;_notifiedPreloadThreshold=!1;constructor(e){this.index=e.index,this._trackUrl=e.trackUrl,this._resolvedUrl=e.trackUrl,this.skipHEAD=e.skipHEAD??!1,this.metadata=e.metadata??{},this.queueRef=e.queue,this.audio=new Audio,this.audio.preload="none",this.audio.src=this._trackUrl,this.audio.volume=e.queue.volume,this.audio.controls=!1,this.audio.onerror=()=>{var c,u;let a=(c=this.audio.error)==null?void 0:c.code;if(a===1)return;let s=((u=this.audio.error)==null?void 0:u.message)??"unknown";this.queueRef.onError(new Error(`HTML5 audio error on track ${this.index} (code ${a}): ${s}`))},this.audio.onended=()=>{this.queueRef.onDebug(`audio.onended track=${this.index} machineState=${this._actor.getSnapshot().value} queueIdx=${this.queueRef.currentTrackIndex}`),this._actor.send({type:"HTML5_ENDED"})},this._playbackMethod=e.queue.playbackMethod;let t={trackUrl:this._trackUrl,resolvedUrl:this._trackUrl,skipHEAD:this.skipHEAD,playbackType:"HTML5",webAudioLoadingState:"NONE",isPlaying:!1,scheduledStartContextTime:null,notifiedLookahead:!1,fetchStarted:!1,pendingPlay:!1},n=R(t).provide({guards:{canPlayWebAudio:()=>!!(this.ctx&&this.audioBuffer&&this.gainNode),isWebAudioOnly:()=>this._playbackMethod==="WEBAUDIO_ONLY"},actors:{fetchDecode:m.provide({actors:{resolveUrl:x(async({signal:a})=>{let s=await fetch(this._trackUrl,{method:"HEAD",signal:a});return s.redirected&&s.url?(this._resolvedUrl=s.url,this.audio.src=s.url,s.url):null}),fetchAudio:x(async({input:a,signal:s})=>{let{resolvedUrl:c}=a,u=await fetch(c,{signal:s});if(!u.ok)throw new Error(`HTTP ${u.status} for ${c}`);this._pendingArrayBuffer=await u.arrayBuffer()}),decodeAudio:x(async()=>{let a=this._pendingArrayBuffer;if(this._pendingArrayBuffer=null,!a||!this.ctx)throw new Error("No ArrayBuffer or AudioContext");this.audioBuffer=await this.ctx.decodeAudioData(a),queueMicrotask(()=>this.queueRef.onTrackBufferReady(this))})}})},actions:{triggerFetchForPendingPlay:()=>{this.preload(),y()},playHtml5:()=>this._playHtml5(),startSourceNode:()=>{this._startSourceNode(this.pausedAtTrackTime)},startScheduledSourceNode:({context:a})=>{let s=a.scheduledStartContextTime;s===null||!this.ctx||!this.audioBuffer||!this.gainNode||(this._stopSourceNode(),this.sourceNode=this.ctx.createBufferSource(),this.sourceNode.buffer=this.audioBuffer,this.sourceNode.playbackRate.value=this.queueRef.playbackRate,this.sourceNode.connect(this.gainNode),this.gainNode.connect(this.ctx.destination),this.sourceNode.onended=this._handleWebAudioEnded,this.sourceNode.start(s,0),this._waRefCtxTime=s,this._waRefTrackTime=0,this.queueRef.onDebug(`startScheduledSourceNode track=${this.index} when=${s.toFixed(3)} ctxNow=${this.ctx.currentTime.toFixed(3)} delta=${(s-this.ctx.currentTime).toFixed(3)}s`))},startProgressLoop:()=>this.startProgressLoop(),pauseHtml5:()=>this.audio.pause(),freezePausedTime:()=>{let a=this.currentTime;this.pausedAtTrackTime=isFinite(a)?a:0},stopSourceNode:()=>this._stopSourceNode(),disconnectGain:()=>this._disconnectGain(),stopProgressLoop:()=>this._stopProgressLoop(),reportProgress:()=>{queueMicrotask(()=>this.queueRef.onProgress(this.toInfo()))},seekHtml5:()=>this._seekHtml5(),seekWebAudio:()=>this._seekWebAudio(),resetHtml5Element:()=>{this.audio.currentTime=0},resetTiming:()=>{this._waRefCtxTime=0,this._waRefTrackTime=0,this.pausedAtTrackTime=0},notifyTrackEnded:()=>{queueMicrotask(()=>this.queueRef.onTrackEnded(this))}}});this._actor=Z(n),this._actor.start()}play(){this.queueRef.onDebug(`Track.play() track=${this.index} machineState=${this._actor.getSnapshot().value} hasBuffer=${!!this.audioBuffer} hasCtx=${!!this.ctx} audioPaused=${this.audio.paused}`),this._actor.send({type:"PLAY"})}pause(){this._actor.send({type:"PAUSE"})}seek(e){if(!isFinite(e))return;let t=Math.max(0,isNaN(this.duration)?e:Math.min(e,this.duration));this.pausedAtTrackTime=t,this._actor.send({type:"SEEK",time:t})}setVolume(e){let t=Math.min(1,Math.max(0,e));this.audio.volume=t,this.gainNode&&(this.gainNode.gain.value=t),this._actor.send({type:"SET_VOLUME",volume:t})}setPlaybackRate(e){if(this.ctx&&this.sourceNode&&this._actor.getSnapshot().context.isPlaying){let t=this.sourceNode.playbackRate.value;this._waRefTrackTime=this._waRefTrackTime+(this.ctx.currentTime-this._waRefCtxTime)*t,this._waRefCtxTime=this.ctx.currentTime}this.audio.playbackRate=e,this.sourceNode&&(this.sourceNode.playbackRate.value=e)}preload(){this.queueRef.onDebug(`preload() track=${this.index} state=${this._actor.getSnapshot().value} hasBuffer=${!!this.audioBuffer} hasCtx=${!!this.ctx}`),this._actor.getSnapshot().value==="idle"&&this._actor.send({type:"PRELOAD"}),!this.audioBuffer&&(y(),this.ctx&&this._actor.send({type:"START_FETCH"}))}seekToEnd(e=6){let t=this.duration;!isNaN(t)&&t>e&&this.seek(t-e)}activate(){this._actor.send({type:"ACTIVATE"})}deactivate(){this.queueRef.onDebug(`Track.deactivate() track=${this.index} machineState=${this._actor.getSnapshot().value} isPlaying=${this.isPlaying}`),this._notifiedPreloadThreshold=!1,this._actor.send({type:"DEACTIVATE"}),this.queueRef.onDebug(`Track.deactivate() done track=${this.index} machineState=${this._actor.getSnapshot().value}`)}destroy(){var e;this.deactivate(),this._pendingArrayBuffer=null,this.audioBuffer=null,(e=this.gainNode)==null||e.disconnect(),this.gainNode=null,this._actor.stop()}cancelGaplessStart(){this._actor.getSnapshot().context.scheduledStartContextTime!==null&&this._actor.send({type:"CANCEL_GAPLESS"})}scheduleGaplessStart(e){!this.ctx||!this.audioBuffer||!this.gainNode||this._actor.send({type:"SCHEDULE_GAPLESS",when:e})}get currentTime(){let e=this._actor.getSnapshot();return e.value==="webaudio"?e.context.isPlaying?this.ctx?Math.max(0,this._waRefTrackTime+(this.ctx.currentTime-this._waRefCtxTime)*this.queueRef.playbackRate):0:this.pausedAtTrackTime:this.audio.currentTime}get duration(){return this._actor.getSnapshot().value==="html5"&&!isNaN(this.audio.duration)?this.audio.duration:this.audioBuffer?this.audioBuffer.duration:this.audio.duration}get isPaused(){let e=this._actor.getSnapshot();return e.value==="webaudio"?!e.context.isPlaying:this.audio.paused}get isPlaying(){return this._actor.getSnapshot().context.isPlaying}get trackUrl(){return this._resolvedUrl}get playbackType(){return this._actor.getSnapshot().context.playbackType}get webAudioLoadingState(){return this._actor.getSnapshot().context.webAudioLoadingState}get hasSourceNode(){return this.sourceNode!==null}get machineState(){return this._actor.getSnapshot().value}get scheduledStartContextTime(){return this._actor.getSnapshot().context.scheduledStartContextTime}get isBufferLoaded(){return this.audioBuffer!==null}toInfo(){var e;return{index:this.index,currentTime:this.currentTime,duration:this.duration,isPlaying:this.isPlaying,isPaused:this.isPaused,volume:((e=this.gainNode)==null?void 0:e.gain.value)??this.audio.volume,trackUrl:this.trackUrl,playbackType:this.playbackType,webAudioLoadingState:this.webAudioLoadingState,metadata:this.metadata,playbackRate:this.queueRef.playbackRate,machineState:this.machineState}}_playHtml5(){this.audio.preload!=="auto"&&(this.audio.preload="auto"),this.audio.playbackRate=this.queueRef.playbackRate;let e=this.audio.play();e&&e.catch(t=>{t instanceof Error&&t.name==="NotAllowedError"?this.queueRef.onPlayBlocked():t instanceof Error&&t.name==="AbortError"||this.queueRef.onError(t instanceof Error?t:new Error(String(t)))})}_seekHtml5(){let e=this.pausedAtTrackTime;isFinite(e)&&(this.audio.preload!=="auto"&&(this.audio.preload="auto"),this.audio.readyState>=HTMLMediaElement.HAVE_METADATA?this.audio.currentTime=e:(this.audio.addEventListener("loadedmetadata",()=>{this.audio.currentTime=e},{once:!0}),this.audio.load()))}_startSourceNode(e){!this.ctx||!this.audioBuffer||!this.gainNode||(this._stopSourceNode(),this.ctx.state==="suspended"&&this.ctx.resume(),this.sourceNode=this.ctx.createBufferSource(),this.sourceNode.buffer=this.audioBuffer,this.sourceNode.playbackRate.value=this.queueRef.playbackRate,this.sourceNode.connect(this.gainNode),this.gainNode.connect(this.ctx.destination),this.sourceNode.onended=this._handleWebAudioEnded,this._waRefCtxTime=this.ctx.currentTime,this._waRefTrackTime=e,this.sourceNode.start(0,e))}_stopSourceNode(){if(this.sourceNode){this.sourceNode.onended=null;try{this.sourceNode.stop()}catch{}try{this.sourceNode.disconnect()}catch{}this.sourceNode=null}}_disconnectGain(){if(!(!this.gainNode||!this.ctx))try{this.gainNode.disconnect(this.ctx.destination)}catch{}}_seekWebAudio(){let t=this._actor.getSnapshot().context.isPlaying,n=this.pausedAtTrackTime;this._stopSourceNode(),t&&this._startSourceNode(n)}_handleWebAudioEnded=()=>{this.queueRef.onDebug(`_handleWebAudioEnded track=${this.index} sourceNode=${!!this.sourceNode} queueIdx=${this.queueRef.currentTrackIndex}`),this.sourceNode&&this._actor.send({type:"WEBAUDIO_ENDED"})};startProgressLoop(){if(this.rafId!==null)return;let e=()=>{if(this.isPaused||!this.isPlaying){this.rafId=null;return}this.queueRef.onProgress(this.toInfo());let t=this.duration-this.currentTime;!this._actor.getSnapshot().context.notifiedLookahead&&!isNaN(t)&&t<=X&&(this._actor.send({type:"LOOKAHEAD_REACHED"}),queueMicrotask(()=>this.queueRef.onTrackBufferReady(this)));let a=isNaN(this.duration)?L:Math.min(this.duration*.2,L);!this._notifiedPreloadThreshold&&this.currentTime>=a&&(this._notifiedPreloadThreshold=!0,queueMicrotask(()=>this.queueRef.onPreloadReady(this))),this.rafId=requestAnimationFrame(e)};this.rafId=requestAnimationFrame(e)}_stopProgressLoop(){this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null)}};function D(o,e){let t=0;return(...n)=>{let a=performance.now();a-t<e||(t=a,o(...n))}}var I=5,A=class{_tracks=[];_actor;_onProgress;_onEnded;_onPlayNextTrack;_onPlayPreviousTrack;_onStartNewTrack;_onError;_onPlayBlocked;_onDebug;playbackMethod;_volume;_preloadNumTracks;_playbackRate;_scheduledNextIndex=null;_throttledUpdatePositionState=D((e,t,n)=>C(e,t,n),1e3);constructor(e={}){let{tracks:t=[],onProgress:n,onEnded:a,onPlayNextTrack:s,onPlayPreviousTrack:c,onStartNewTrack:u,onError:M,onPlayBlocked:U,onDebug:O,playbackMethod:w="HYBRID",trackMetadata:G=[],volume:H=1,preloadNumTracks:B=2,playbackRate:F=1}=e;this._volume=Math.min(1,Math.max(0,H)),this._preloadNumTracks=Math.max(0,B),this._playbackRate=Math.min(4,Math.max(.25,F)),this.playbackMethod=w,this._onProgress=n,this._onEnded=a,this._onPlayNextTrack=s,this._onPlayPreviousTrack=c,this._onStartNewTrack=u,this._onError=M,this._onPlayBlocked=U,this._onDebug=O,this._tracks=t.map((i,r)=>new T({trackUrl:i,index:r,queue:this,metadata:G[r]}));let $=N({currentTrackIndex:0,trackCount:this._tracks.length}).provide({actions:{deactivateCurrent:({context:i})=>{var r;(r=this._trackAt(i.currentTrackIndex))==null||r.deactivate()},deactivateEndedTrack:({context:i})=>{var r;(r=this._trackAt(i.currentTrackIndex))==null||r.deactivate()},activateAndPlayCurrent:({context:i})=>{let r=this._trackAt(i.currentTrackIndex);r&&(r.activate(),this._scheduledNextIndex!==r.index&&r.play())},playOrContinueGapless:({context:i})=>{let r=this._trackAt(i.currentTrackIndex);r&&(this._scheduledNextIndex!==r.index?r.play():(this._scheduledNextIndex=null,this.onDebug(`onTrackEnded: gapless track ${r.index} \u2014 sourceNode=${r.hasSourceNode} isPlaying=${r.isPlaying} machineState=${r.machineState}`),r.startProgressLoop()))},cancelAllGapless:()=>this._cancelScheduledGapless(),notifyStartNewTrack:({context:i})=>{var l;let r=this._trackAt(i.currentTrackIndex);r&&((l=this._onStartNewTrack)==null||l.call(this,r.toInfo()))},notifyPlayNextTrack:({context:i})=>{var l;let r=this._trackAt(i.currentTrackIndex);r&&((l=this._onPlayNextTrack)==null||l.call(this,r.toInfo()))},notifyPlayPreviousTrack:({context:i})=>{var l;let r=this._trackAt(i.currentTrackIndex);r&&((l=this._onPlayPreviousTrack)==null||l.call(this,r.toInfo()))},notifyEnded:()=>{var i;return(i=this._onEnded)==null?void 0:i.call(this)},updateMediaSessionMetadata:({context:i})=>{let r=this._trackAt(i.currentTrackIndex);r&&P(r.metadata)},preloadAhead:({context:i})=>{this._preloadAhead(i.currentTrackIndex)},playCurrent:({context:i})=>{var r;(r=this._trackAt(i.currentTrackIndex))==null||r.play()},pauseCurrent:({context:i})=>{var r;(r=this._trackAt(i.currentTrackIndex))==null||r.pause()},seekCurrent:({context:i,event:r})=>{var S;let l=r;(S=this._trackAt(i.currentTrackIndex))==null||S.seek(l.time)},seekCurrentToZero:({context:i})=>{var r;(r=this._trackAt(i.currentTrackIndex))==null||r.seek(0)},scheduleGapless:({context:i})=>{this._tryScheduleGapless(i.currentTrackIndex)},cancelScheduledGapless:()=>{this._cancelScheduledGapless()},cancelAndRescheduleGapless:({context:i})=>{this._cancelScheduledGapless(),this._tryScheduleGapless(i.currentTrackIndex)}}});this._actor=z($),this._actor.subscribe(i=>{b(i.value==="playing")}),this._actor.start(),v({onPlay:()=>this.play(),onPause:()=>{this._actor.getSnapshot().value==="playing"&&this.pause()},onNext:()=>this.next(),onPrevious:()=>this.previous(),onSeek:i=>this.seek(i)})}play(){this._currentTrack&&this._actor.send({type:"PLAY"})}pause(){this._actor.send({type:"PAUSE"})}togglePlayPause(){this._actor.getSnapshot().value==="playing"?this.pause():this.play()}next(){this._actor.getSnapshot().context.currentTrackIndex+1>=this._tracks.length||this._actor.send({type:"NEXT"})}previous(){let e=this._currentTrack;if(e&&e.currentTime>8){e.seek(0),e.play();return}this._actor.send({type:"PREVIOUS"})}gotoTrack(e,t=!1){e<0||e>=this._tracks.length||(this.onDebug(`gotoTrack(${e}, playImmediately=${t}) queueState=${this._actor.getSnapshot().value} curIdx=${this._actor.getSnapshot().context.currentTrackIndex}`),this._actor.send({type:"GOTO",index:e,playImmediately:t}))}seek(e){this._actor.send({type:"SEEK",time:e})}setVolume(e){let t=Math.min(1,Math.max(0,e));this._volume=t;for(let n of this._tracks)n.setVolume(t)}setPlaybackRate(e){var a;let t=Math.min(4,Math.max(.25,e));this._playbackRate=t,(a=this._currentTrack)==null||a.setPlaybackRate(t),this._cancelScheduledGapless();let n=this._actor.getSnapshot();n.value==="playing"&&this._tryScheduleGapless(n.context.currentTrackIndex)}addTrack(e,t={}){let n=this._tracks.length,a=t.metadata??{};this._tracks.push(new T({trackUrl:e,index:n,queue:this,skipHEAD:t.skipHEAD,metadata:a})),this._actor.send({type:"ADD_TRACK"})}removeTrack(e){if(!(e<0||e>=this._tracks.length)){this._tracks[e].destroy(),this._tracks.splice(e,1);for(let t=e;t<this._tracks.length;t++)this._tracks[t].index=t;this._scheduledNextIndex===e?this._scheduledNextIndex=null:this._scheduledNextIndex!==null&&this._scheduledNextIndex>e&&this._scheduledNextIndex--,this._actor.send({type:"REMOVE_TRACK",index:e})}}resumeAudioContext(){return y()}destroy(){for(let e of this._tracks)e.destroy();this._tracks=[],this._actor.stop()}get currentTrack(){var e;return(e=this._currentTrack)==null?void 0:e.toInfo()}get currentTrackIndex(){return this._actor.getSnapshot().context.currentTrackIndex}get tracks(){return this._tracks.map(e=>e.toInfo())}get isPlaying(){return this._actor.getSnapshot().value==="playing"}get isPaused(){return this._actor.getSnapshot().value==="paused"}get volume(){return this._volume}get preloadNumTracks(){return this._preloadNumTracks}set preloadNumTracks(e){this._preloadNumTracks=Math.max(0,e);let t=this._actor.getSnapshot();t.value==="playing"&&this._preloadAhead(t.context.currentTrackIndex)}get playbackRate(){return this._playbackRate}get queueSnapshot(){let e=this._actor.getSnapshot();return{state:e.value,context:e.context}}onTrackEnded(e){let t=this._actor.getSnapshot();if(this.onDebug(`onTrackEnded track=${e.index} queueState=${t.value} curIdx=${t.context.currentTrackIndex}`),e!==this._trackAt(t.context.currentTrackIndex))return;this._actor.send({type:"TRACK_ENDED"});let n=this._actor.getSnapshot();this.onDebug(`onTrackEnded after TRACK_ENDED \u2192 queueState=${n.value} curIdx=${n.context.currentTrackIndex}`)}onTrackBufferReady(e){this._actor.send({type:"TRACK_LOADED",index:e.index})}onProgress(e){var t;e.index===this._actor.getSnapshot().context.currentTrackIndex&&(isNaN(e.duration)||this._throttledUpdatePositionState(e.duration,e.currentTime,this._playbackRate),(t=this._onProgress)==null||t.call(this,e))}onError(e){var t;(t=this._onError)==null||t.call(this,e)}onPlayBlocked(){var e;this._actor.send({type:"PAUSE"}),(e=this._onPlayBlocked)==null||e.call(this)}onPreloadReady(e){let t=this._actor.getSnapshot();e.index===t.context.currentTrackIndex&&this._preloadAhead(t.context.currentTrackIndex)}onDebug(e){var t;(t=this._onDebug)==null||t.call(this,e)}_trackAt(e){return this._tracks[e]}get _currentTrack(){return this._tracks[this._actor.getSnapshot().context.currentTrackIndex]}_preloadAhead(e){let t=this._trackAt(e);if(t&&t.playbackType==="HTML5"&&t.isPlaying){let a=isNaN(t.duration)?15:Math.min(t.duration*.2,15);if(t.currentTime<a){this.onDebug(`_preloadAhead: deferring \u2014 HTML5 track ${e} at ${t.currentTime.toFixed(1)}s (threshold=${a.toFixed(1)}s)`);return}}let n=e+this._preloadNumTracks+1;this.onDebug(`_preloadAhead(${e}) limit=${n} trackCount=${this._tracks.length}`);for(let a=e+1;a<this._tracks.length&&a<n;a++){let s=this._tracks[a];if(s.isBufferLoaded)this.onDebug(`_preloadAhead: track ${a} already loaded`);else{this.onDebug(`_preloadAhead: starting preload for track ${a}`),s.preload();break}}}_cancelScheduledGapless(){if(this._scheduledNextIndex===null)return;let e=this._trackAt(this._scheduledNextIndex);e&&(e.cancelGaplessStart(),this.onDebug(`_cancelScheduledGapless: cancelled track ${this._scheduledNextIndex}`)),this._scheduledNextIndex=null}_tryScheduleGapless(e){let t=k();if(!t||this.playbackMethod==="HTML5_ONLY")return;let n=e+1;if(n>=this._tracks.length)return;let a=this._tracks[e],s=this._tracks[n];if(!a.isBufferLoaded||!s.isBufferLoaded||this._scheduledNextIndex===n||!a.isPlaying)return;let c=this._computeTrackEndTime(a);if(c===null||c<t.currentTime+.01)return;let u=c-t.currentTime;if(a.playbackType==="HTML5"&&u>I){this.onDebug(`_tryScheduleGapless: deferring \u2014 HTML5 track ${e} has ${u.toFixed(1)}s remaining (max lookahead=${I}s)`);return}s.scheduleGaplessStart(c),this.onDebug(`_tryScheduleGapless: scheduled track ${n} at endTime=${c.toFixed(3)} (in ${(c-t.currentTime).toFixed(1)}s) curPlaybackType=${a.playbackType}`),this._scheduledNextIndex=n}_computeTrackEndTime(e){let t=k();if(!t||!e.isBufferLoaded)return null;let n=e.duration;if(isNaN(n))return null;if(e.scheduledStartContextTime!==null)return e.scheduledStartContextTime+n/this._playbackRate;let a=(n-e.currentTime)/this._playbackRate;return a<=0?null:t.currentTime+a}};export{A as Queue,A as default};
1
+ import{createActor as j}from"xstate";var q=typeof window<"u",p;function V(){if(!q)return null;let o=window.AudioContext??window.webkitAudioContext;return o?new o:null}function k(){return p===void 0?null:p}function y(){p===void 0&&(p=V());let o=p;return o&&o.state==="suspended"?o.resume():Promise.resolve()}var g=typeof navigator<"u"&&"mediaSession"in navigator;function P(o){if(!g)return;let{mediaSession:e}=navigator;e.setActionHandler("play",o.onPlay),e.setActionHandler("pause",o.onPause),e.setActionHandler("nexttrack",o.onNext),e.setActionHandler("previoustrack",o.onPrevious),e.setActionHandler("seekto",t=>{t.seekTime!=null&&o.onSeek(t.seekTime)})}function b(o){if(g){if(!o){navigator.mediaSession.metadata=null;return}navigator.mediaSession.metadata=new MediaMetadata({title:o.title??"",artist:o.artist??"",album:o.album??"",artwork:o.artwork??[]})}}function N(o){g&&(navigator.mediaSession.playbackState=o?"playing":"paused")}function R(o,e,t=1){if(g)try{navigator.mediaSession.setPositionState({duration:o,position:e,playbackRate:t})}catch{}}import{setup as K,assign as h}from"xstate";function C(o){return K({types:{context:{},events:{}},guards:{hasNextTrack:({context:e})=>e.currentTrackIndex+1<e.trackCount,playImmediately:({event:e})=>!!e.playImmediately,willBeEmpty:({context:e})=>e.trackCount<=1,isRemovingCurrent:({context:e,event:t})=>t.index===e.currentTrackIndex},actions:{incrementTrackCount:h({trackCount:({context:e})=>e.trackCount+1}),decrementTrackCount:h({trackCount:({context:e})=>Math.max(0,e.trackCount-1),currentTrackIndex:({context:e,event:t})=>{let r=t,a=Math.max(0,e.trackCount-1);return r.index<e.currentTrackIndex?Math.max(0,e.currentTrackIndex-1):a>0?Math.min(e.currentTrackIndex,a-1):0}}),gotoTrackIndex:h({currentTrackIndex:({event:e})=>e.index}),advanceToNextTrack:h({currentTrackIndex:({context:e})=>{let t=e.currentTrackIndex+1;return t<e.trackCount?t:e.currentTrackIndex}}),goToPreviousTrack:h({currentTrackIndex:({context:e})=>Math.max(0,e.currentTrackIndex-1)}),advanceOnTrackEnd:h({currentTrackIndex:({context:e})=>Math.min(e.currentTrackIndex+1,e.trackCount-1)}),resetToFirstTrack:h({currentTrackIndex:()=>0}),deactivateCurrent:()=>{},deactivateEndedTrack:()=>{},activateAndPlayCurrent:()=>{},playOrContinueGapless:()=>{},cancelAllGapless:()=>{},notifyStartNewTrack:()=>{},notifyPlayNextTrack:()=>{},notifyPlayPreviousTrack:()=>{},notifyEnded:()=>{},updateMediaSessionMetadata:()=>{},preloadAhead:()=>{},playCurrent:()=>{},pauseCurrent:()=>{},seekCurrent:()=>{},seekCurrentToZero:()=>{},scheduleGapless:()=>{},cancelScheduledGapless:()=>{},cancelAndRescheduleGapless:()=>{}}}).createMachine({id:"queue",initial:"idle",context:o,on:{ADD_TRACK:{actions:"incrementTrackCount"},REMOVE_TRACK:{actions:"decrementTrackCount"}},states:{idle:{on:{PLAY:{target:"playing",actions:["playCurrent","updateMediaSessionMetadata","preloadAhead","scheduleGapless"]},GOTO:[{guard:"playImmediately",target:"playing",actions:["deactivateCurrent","cancelAllGapless","gotoTrackIndex","activateAndPlayCurrent","notifyStartNewTrack","updateMediaSessionMetadata","preloadAhead"]},{target:"paused",actions:["deactivateCurrent","cancelAllGapless","gotoTrackIndex","seekCurrentToZero","preloadAhead"]}],TRACK_LOADED:{actions:["preloadAhead"]}}},playing:{on:{REMOVE_TRACK:[{guard:"willBeEmpty",target:"idle",actions:["cancelAllGapless","decrementTrackCount"]},{guard:"isRemovingCurrent",actions:["cancelAllGapless","decrementTrackCount","activateAndPlayCurrent","notifyStartNewTrack","updateMediaSessionMetadata","preloadAhead"]},{actions:["decrementTrackCount","cancelAndRescheduleGapless","preloadAhead"]}],PAUSE:{target:"paused",actions:["cancelScheduledGapless","pauseCurrent"]},TOGGLE:{target:"paused",actions:["cancelScheduledGapless","pauseCurrent"]},NEXT:{actions:["deactivateCurrent","cancelAllGapless","advanceToNextTrack","activateAndPlayCurrent","notifyStartNewTrack","notifyPlayNextTrack","updateMediaSessionMetadata","preloadAhead"]},PREVIOUS:{actions:["deactivateCurrent","cancelAllGapless","goToPreviousTrack","activateAndPlayCurrent","notifyStartNewTrack","notifyPlayPreviousTrack","updateMediaSessionMetadata","preloadAhead"]},GOTO:[{guard:"playImmediately",actions:["deactivateCurrent","cancelAllGapless","gotoTrackIndex","activateAndPlayCurrent","notifyStartNewTrack","updateMediaSessionMetadata","preloadAhead"]},{actions:["deactivateCurrent","cancelAllGapless","gotoTrackIndex","seekCurrentToZero","preloadAhead"]}],SEEK:{actions:["seekCurrent","cancelAndRescheduleGapless"]},TRACK_ENDED:[{guard:"hasNextTrack",target:"playing",actions:["deactivateEndedTrack","advanceOnTrackEnd","playOrContinueGapless","notifyStartNewTrack","notifyPlayNextTrack","updateMediaSessionMetadata","preloadAhead"]},{target:"ended",actions:["deactivateEndedTrack","notifyEnded"]}],TRACK_LOADED:[{guard:({context:e,event:t})=>t.index===e.currentTrackIndex,actions:["cancelAndRescheduleGapless","preloadAhead"]},{actions:["scheduleGapless","preloadAhead"]}]}},paused:{on:{REMOVE_TRACK:[{guard:"willBeEmpty",target:"idle",actions:["cancelAllGapless","decrementTrackCount"]},{guard:"isRemovingCurrent",actions:["cancelAllGapless","decrementTrackCount","preloadAhead"]},{actions:["decrementTrackCount","preloadAhead"]}],PLAY:{target:"playing",actions:["playCurrent","updateMediaSessionMetadata","preloadAhead","scheduleGapless"]},TOGGLE:{target:"playing",actions:["playCurrent","updateMediaSessionMetadata","preloadAhead","scheduleGapless"]},NEXT:{actions:["deactivateCurrent","cancelAllGapless","advanceToNextTrack","activateAndPlayCurrent","notifyStartNewTrack","notifyPlayNextTrack","updateMediaSessionMetadata","preloadAhead"]},PREVIOUS:{actions:["deactivateCurrent","cancelAllGapless","goToPreviousTrack","activateAndPlayCurrent","notifyStartNewTrack","notifyPlayPreviousTrack","updateMediaSessionMetadata","preloadAhead"]},GOTO:[{guard:"playImmediately",target:"playing",actions:["deactivateCurrent","cancelAllGapless","gotoTrackIndex","activateAndPlayCurrent","notifyStartNewTrack","updateMediaSessionMetadata","preloadAhead"]},{actions:["deactivateCurrent","cancelAllGapless","gotoTrackIndex","seekCurrentToZero","preloadAhead"]}],SEEK:{actions:["seekCurrent"]},TRACK_ENDED:[{guard:"hasNextTrack",target:"paused",actions:["deactivateEndedTrack","advanceOnTrackEnd","notifyStartNewTrack","updateMediaSessionMetadata"]},{target:"ended",actions:["deactivateEndedTrack","notifyEnded"]}],TRACK_LOADED:{actions:["preloadAhead"]}}},ended:{on:{ADD_TRACK:{target:"paused",actions:"incrementTrackCount"},PLAY:{target:"playing",actions:["resetToFirstTrack","playCurrent","updateMediaSessionMetadata","preloadAhead","scheduleGapless"]},GOTO:[{guard:"playImmediately",target:"playing",actions:["deactivateCurrent","cancelAllGapless","gotoTrackIndex","activateAndPlayCurrent","notifyStartNewTrack","updateMediaSessionMetadata","preloadAhead"]},{target:"paused",actions:["deactivateCurrent","cancelAllGapless","gotoTrackIndex","seekCurrentToZero","preloadAhead"]}]}}}})}import{createActor as X,fromPromise as x}from"xstate";import{setup as Q,assign as c,spawnChild as Z}from"xstate";import{setup as Y,assign as E,sendParent as T,fromPromise as S}from"xstate";var m=Y({types:{context:{},input:{}},actors:{resolveUrl:S(async()=>null),fetchAudio:S(async()=>{}),decodeAudio:S(async()=>{})},guards:{shouldSkipHEAD:({context:o})=>o.skipHEAD}}).createMachine({id:"fetchDecode",initial:"resolvingUrl",context:{trackUrl:"",resolvedUrl:"",skipHEAD:!1},entry:E(({event:o})=>{let{input:e}=o;return{trackUrl:e.trackUrl,resolvedUrl:e.resolvedUrl,skipHEAD:e.skipHEAD}}),states:{resolvingUrl:{always:{guard:"shouldSkipHEAD",target:"fetching"},invoke:{id:"resolveUrl",src:"resolveUrl",input:({context:o})=>({trackUrl:o.trackUrl}),onDone:{target:"fetching",actions:[E({resolvedUrl:({event:o,context:e})=>o.output??e.resolvedUrl,skipHEAD:()=>!0}),T(({event:o,context:e})=>({type:"URL_RESOLVED",url:o.output??e.resolvedUrl}))]},onError:{target:"fetching",actions:E({skipHEAD:()=>!0})}}},fetching:{invoke:{id:"fetchAudio",src:"fetchAudio",input:({context:o})=>({resolvedUrl:o.resolvedUrl}),onDone:"decoding",onError:{target:"error",actions:T({type:"BUFFER_ERROR"})}}},decoding:{invoke:{id:"decodeAudio",src:"decodeAudio",input:()=>{},onDone:{target:"done",actions:T({type:"BUFFER_READY"})},onError:{target:"error",actions:T({type:"BUFFER_ERROR"})}}},done:{type:"final"},error:{type:"final"}}});function L(o){return Q({types:{context:{},events:{}},actors:{fetchDecode:m},guards:{canPlayWebAudio:()=>!1,isWebAudioOnly:()=>!1,canStartFetch:({context:e})=>e.webAudioLoadingState==="NONE"&&!e.fetchStarted},actions:{playHtml5:()=>{},startSourceNode:()=>{},crossoverHtml5ToWebAudio:()=>{},notifyBufferReady:()=>{},startScheduledSourceNode:()=>{},startProgressLoop:()=>{},pauseHtml5:()=>{},freezePausedTime:()=>{},stopSourceNode:()=>{},stopProgressLoop:()=>{},reportProgress:()=>{},seekHtml5:()=>{},seekWebAudio:()=>{},resetHtml5Element:()=>{},resetTiming:()=>{},notifyTrackEnded:()=>{},triggerFetchForPendingPlay:()=>{},setPendingPlay:c({pendingPlay:()=>!0}),clearPendingPlay:c({pendingPlay:()=>!1}),setIsPlaying:c({isPlaying:()=>!0}),clearIsPlaying:c({isPlaying:()=>!1}),setLoadingState:c({webAudioLoadingState:()=>"LOADING"}),setLoadedState:c({webAudioLoadingState:()=>"LOADED"}),setErrorState:c({webAudioLoadingState:()=>"ERROR"}),clearScheduleAndLookahead:c({scheduledStartContextTime:()=>null,notifiedLookahead:()=>!1}),setPlayingWebAudio:c({isPlaying:()=>!0,webAudioLoadingState:()=>"LOADED",playbackType:()=>"WEBAUDIO"}),setScheduledGapless:c({isPlaying:()=>!0,webAudioLoadingState:()=>"LOADED",playbackType:()=>"WEBAUDIO",scheduledStartContextTime:({event:e})=>e.when}),clearPlayingAndSchedule:c({isPlaying:()=>!1,scheduledStartContextTime:()=>null,notifiedLookahead:()=>!1}),setNotifiedLookahead:c({notifiedLookahead:()=>!0}),setResolvedUrl:c({resolvedUrl:({event:e})=>e.url}),clearScheduledStart:c({scheduledStartContextTime:()=>null}),setPlayingWebAudioType:c({isPlaying:()=>!0,playbackType:()=>"WEBAUDIO"}),setPlaybackTypeWebAudio:c({playbackType:()=>"WEBAUDIO"}),clearNotifiedLookahead:c({notifiedLookahead:()=>!1})}}).createMachine({id:"track",initial:"idle",context:o,on:{START_FETCH:{guard:"canStartFetch",actions:[c({webAudioLoadingState:()=>"LOADING",fetchStarted:()=>!0}),Z("fetchDecode",{id:"fetchDecode",input:({context:e})=>({trackUrl:e.trackUrl,resolvedUrl:e.resolvedUrl,skipHEAD:e.skipHEAD})})]}},states:{idle:{on:{HTML5_ENDED:{actions:["notifyTrackEnded"]},DEACTIVATE:{actions:["pauseHtml5","resetHtml5Element","resetTiming","stopProgressLoop","clearScheduleAndLookahead"]},ACTIVATE:{actions:["resetTiming","resetHtml5Element","clearScheduleAndLookahead"]},PLAY:[{guard:"canPlayWebAudio",target:"webaudio",actions:["setPlayingWebAudio","startSourceNode","startProgressLoop"]},{guard:"isWebAudioOnly",actions:["setPendingPlay","triggerFetchForPendingPlay"]},{target:"html5",actions:["setIsPlaying","playHtml5","startProgressLoop","triggerFetchForPendingPlay"]}],PLAY_WEBAUDIO:{target:"webaudio",actions:["setPlayingWebAudio","startSourceNode","startProgressLoop"]},SCHEDULE_GAPLESS:{target:"webaudio",actions:["setScheduledGapless","startScheduledSourceNode"]},PRELOAD:{target:"loading"},BUFFER_LOADING:{actions:"setLoadingState"},BUFFER_READY:[{guard:({context:e})=>e.pendingPlay,target:"webaudio",actions:["clearPendingPlay","setPlayingWebAudio","startSourceNode","startProgressLoop","notifyBufferReady"]},{actions:["setLoadedState","notifyBufferReady"]}],BUFFER_ERROR:{actions:["setErrorState","clearPendingPlay"]},URL_RESOLVED:{actions:"setResolvedUrl"}}},html5:{on:{PAUSE:{actions:["clearIsPlaying","pauseHtml5","stopProgressLoop","reportProgress"]},PLAY:{actions:["setIsPlaying","playHtml5","startProgressLoop"]},BUFFER_LOADING:{actions:"setLoadingState"},PLAY_WEBAUDIO:{target:"webaudio",actions:"setPlayingWebAudio"},BUFFER_READY:{target:"webaudio",actions:["setLoadedState","crossoverHtml5ToWebAudio","setPlaybackTypeWebAudio","clearNotifiedLookahead","notifyBufferReady"]},BUFFER_ERROR:{actions:"setErrorState"},SEEK:{actions:["seekHtml5","reportProgress"]},LOOKAHEAD_REACHED:{actions:"setNotifiedLookahead"},HTML5_ENDED:{target:"idle",actions:["clearIsPlaying","stopProgressLoop","notifyTrackEnded"]},ACTIVATE:{target:"idle",actions:["clearPlayingAndSchedule","pauseHtml5","stopProgressLoop","resetTiming","resetHtml5Element"]},URL_RESOLVED:{actions:"setResolvedUrl"},DEACTIVATE:{target:"idle",actions:["clearIsPlaying","pauseHtml5","resetHtml5Element","resetTiming","stopProgressLoop"]}}},loading:{on:{BUFFER_LOADING:{actions:"setLoadingState"},BUFFER_READY:[{guard:({context:e})=>e.pendingPlay,target:"webaudio",actions:["clearPendingPlay","setPlayingWebAudio","startSourceNode","startProgressLoop","notifyBufferReady"]},{target:"idle",actions:["setLoadedState","notifyBufferReady"]}],BUFFER_ERROR:{target:"idle",actions:["setErrorState","clearPendingPlay"]},PLAY:[{guard:"canPlayWebAudio",target:"webaudio",actions:["setPlayingWebAudio","startSourceNode","startProgressLoop"]},{guard:"isWebAudioOnly",actions:["setPendingPlay","triggerFetchForPendingPlay"]},{target:"html5",actions:["setIsPlaying","playHtml5","startProgressLoop","triggerFetchForPendingPlay"]}],PLAY_WEBAUDIO:{target:"webaudio",actions:["setPlayingWebAudio","startSourceNode","startProgressLoop"]},SCHEDULE_GAPLESS:{target:"webaudio",actions:["setScheduledGapless","startScheduledSourceNode"]},ACTIVATE:{target:"idle",actions:["clearPlayingAndSchedule","resetTiming","resetHtml5Element"]},DEACTIVATE:{target:"idle",actions:["clearIsPlaying","resetTiming"]},URL_RESOLVED:{actions:"setResolvedUrl"}}},webaudio:{on:{PAUSE:{actions:["clearIsPlaying","freezePausedTime","stopSourceNode","stopProgressLoop","reportProgress"]},PLAY:[{guard:"canPlayWebAudio",actions:["setIsPlaying","startSourceNode","startProgressLoop"]},{actions:"setIsPlaying"}],PLAY_WEBAUDIO:{actions:"setPlayingWebAudioType"},SEEK:{actions:["clearScheduledStart","seekWebAudio","reportProgress"]},SET_VOLUME:{},CANCEL_GAPLESS:{target:"idle",actions:["clearPlayingAndSchedule","stopSourceNode","stopProgressLoop","resetTiming"]},LOOKAHEAD_REACHED:{actions:"setNotifiedLookahead"},WEBAUDIO_ENDED:{target:"idle",actions:["clearIsPlaying","stopProgressLoop","notifyTrackEnded"]},ACTIVATE:{target:"idle",actions:["clearPlayingAndSchedule","stopSourceNode","stopProgressLoop","resetTiming","resetHtml5Element"]},DEACTIVATE:{target:"idle",actions:["clearPlayingAndSchedule","stopSourceNode","resetTiming","resetHtml5Element","stopProgressLoop"]}}}}})}var z=5,D=15,A=.03,f=class{index;metadata;_trackUrl;_resolvedUrl;skipHEAD;_pendingArrayBuffer=null;audio;_playbackMethod;get ctx(){if(this._playbackMethod==="HTML5_ONLY")return null;let e=k();if(e&&!this.gainNode){this.gainNode=e.createGain(),this.gainNode.gain.value=this.audio.volume,this.gainNode.connect(e.destination);try{this._mediaElementSource=e.createMediaElementSource(this.audio),this._html5GainNode=e.createGain(),this._html5GainNode.gain.value=1,this._mediaElementSource.connect(this._html5GainNode),this._html5GainNode.connect(this.gainNode),this.audio.volume=1}catch{this._mediaElementSource=null,this._html5GainNode=null}}return e}gainNode=null;_mediaElementSource=null;_html5GainNode=null;sourceNode=null;audioBuffer=null;_bufferStartPaddingSec=0;_bufferAlignmentMeasured=!1;_waRefCtxTime=0;_waRefTrackTime=0;pausedAtTrackTime=0;_actor;queueRef;rafId=null;_notifiedPreloadThreshold=!1;constructor(e){this.index=e.index,this._trackUrl=e.trackUrl,this._resolvedUrl=e.trackUrl,this.skipHEAD=e.skipHEAD??!1,this.metadata=e.metadata??{},this.queueRef=e.queue,this.audio=new Audio,this.audio.preload="none",e.queue.playbackMethod!=="HTML5_ONLY"&&(this.audio.crossOrigin="anonymous"),this.audio.src=this._trackUrl,this.audio.volume=e.queue.volume,this.audio.controls=!1,this.audio.onerror=()=>{var d,u;let a=(d=this.audio.error)==null?void 0:d.code;if(a===1)return;let s=((u=this.audio.error)==null?void 0:u.message)??"unknown";this.queueRef.onError(new Error(`HTML5 audio error on track ${this.index} (code ${a}): ${s}`))},this.audio.onended=()=>{this.queueRef.onDebug(`audio.onended track=${this.index} machineState=${this._actor.getSnapshot().value} queueIdx=${this.queueRef.currentTrackIndex}`),this._actor.send({type:"HTML5_ENDED"})},this._playbackMethod=e.queue.playbackMethod;let t={trackUrl:this._trackUrl,resolvedUrl:this._trackUrl,skipHEAD:this.skipHEAD,playbackType:"HTML5",webAudioLoadingState:"NONE",isPlaying:!1,scheduledStartContextTime:null,notifiedLookahead:!1,fetchStarted:!1,pendingPlay:!1},r=L(t).provide({guards:{canPlayWebAudio:()=>!!(this.ctx&&this.audioBuffer&&this.gainNode),isWebAudioOnly:()=>this._playbackMethod==="WEBAUDIO_ONLY"},actors:{fetchDecode:m.provide({actors:{resolveUrl:x(async({signal:a})=>{let s=await fetch(this._trackUrl,{method:"HEAD",signal:a});return s.redirected&&s.url?(this._resolvedUrl=s.url,s.url):null}),fetchAudio:x(async({input:a,signal:s})=>{let{resolvedUrl:d}=a,u=await fetch(d,{signal:s});if(!u.ok)throw new Error(`HTTP ${u.status} for ${d}`);this._pendingArrayBuffer=await u.arrayBuffer()}),decodeAudio:x(async()=>{let a=this._pendingArrayBuffer;if(this._pendingArrayBuffer=null,!a||!this.ctx)throw new Error("No ArrayBuffer or AudioContext");this.audioBuffer=await this.ctx.decodeAudioData(a),this._maybeComputeBufferAlignment()})}})},actions:{triggerFetchForPendingPlay:()=>{this.preload(),y()},playHtml5:()=>this._playHtml5(),startSourceNode:()=>{this._startSourceNode(this.pausedAtTrackTime)},crossoverHtml5ToWebAudio:({context:a})=>{this._crossoverHtml5ToWebAudio(a.isPlaying)},startScheduledSourceNode:({context:a})=>{let s=a.scheduledStartContextTime;s===null||!this.ctx||!this.audioBuffer||!this.gainNode||(this._maybeComputeBufferAlignment(),this._stopSourceNode(),this.sourceNode=this.ctx.createBufferSource(),this.sourceNode.buffer=this.audioBuffer,this.sourceNode.playbackRate.value=this.queueRef.playbackRate,this.sourceNode.connect(this.gainNode),this.sourceNode.onended=this._handleWebAudioEnded,this.sourceNode.start(s,this._bufferStartPaddingSec),this._waRefCtxTime=s,this._waRefTrackTime=0,this.queueRef.onDebug(`startScheduledSourceNode track=${this.index} when=${s.toFixed(3)} ctxNow=${this.ctx.currentTime.toFixed(3)} delta=${(s-this.ctx.currentTime).toFixed(3)}s padding=${this._bufferStartPaddingSec.toFixed(3)}s`))},startProgressLoop:()=>this.startProgressLoop(),pauseHtml5:()=>this.audio.pause(),freezePausedTime:()=>{let a=this.currentTime;this.pausedAtTrackTime=isFinite(a)?a:0},stopSourceNode:()=>this._stopSourceNode(),stopProgressLoop:()=>this._stopProgressLoop(),reportProgress:()=>{queueMicrotask(()=>this.queueRef.onProgress(this.toInfo()))},seekHtml5:()=>this._seekHtml5(),seekWebAudio:()=>this._seekWebAudio(),resetHtml5Element:()=>{this.audio.currentTime=0},resetTiming:()=>{this._waRefCtxTime=0,this._waRefTrackTime=0,this.pausedAtTrackTime=0},notifyTrackEnded:()=>{queueMicrotask(()=>this.queueRef.onTrackEnded(this))},notifyBufferReady:()=>{queueMicrotask(()=>this.queueRef.onTrackBufferReady(this))}}});this._actor=X(r),this._actor.start()}play(){this.queueRef.onDebug(`Track.play() track=${this.index} machineState=${this._actor.getSnapshot().value} hasBuffer=${!!this.audioBuffer} hasCtx=${!!this.ctx} audioPaused=${this.audio.paused}`),this._actor.send({type:"PLAY"})}pause(){this._actor.send({type:"PAUSE"})}seek(e){if(!isFinite(e))return;let t=Math.max(0,isNaN(this.duration)?e:Math.min(e,this.duration));this.pausedAtTrackTime=t,this._actor.send({type:"SEEK",time:t})}setVolume(e){let t=Math.min(1,Math.max(0,e));this._mediaElementSource?this.audio.volume=1:this.audio.volume=t,this.gainNode&&(this.gainNode.gain.value=t),this._actor.send({type:"SET_VOLUME",volume:t})}setPlaybackRate(e){if(this.ctx&&this.sourceNode&&this._actor.getSnapshot().context.isPlaying){let t=this.sourceNode.playbackRate.value;this._waRefTrackTime=this._waRefTrackTime+(this.ctx.currentTime-this._waRefCtxTime)*t,this._waRefCtxTime=this.ctx.currentTime}this.audio.playbackRate=e,this.sourceNode&&(this.sourceNode.playbackRate.value=e)}preload(){this.queueRef.onDebug(`preload() track=${this.index} state=${this._actor.getSnapshot().value} hasBuffer=${!!this.audioBuffer} hasCtx=${!!this.ctx}`),this._actor.getSnapshot().value==="idle"&&this._actor.send({type:"PRELOAD"}),!this.audioBuffer&&(y(),this.ctx&&this._actor.send({type:"START_FETCH"}))}seekToEnd(e=6){let t=this.duration;!isNaN(t)&&t>e&&this.seek(t-e)}activate(){this._actor.send({type:"ACTIVATE"})}deactivate(){this.queueRef.onDebug(`Track.deactivate() track=${this.index} machineState=${this._actor.getSnapshot().value} isPlaying=${this.isPlaying}`),this._notifiedPreloadThreshold=!1,this._actor.send({type:"DEACTIVATE"}),this.queueRef.onDebug(`Track.deactivate() done track=${this.index} machineState=${this._actor.getSnapshot().value}`)}destroy(){var e;this.deactivate(),this._pendingArrayBuffer=null,this.audioBuffer=null,(e=this.gainNode)==null||e.disconnect(),this.gainNode=null,this._actor.stop()}cancelGaplessStart(){this._actor.getSnapshot().context.scheduledStartContextTime!==null&&this._actor.send({type:"CANCEL_GAPLESS"})}scheduleGaplessStart(e){!this.ctx||!this.audioBuffer||!this.gainNode||this._actor.send({type:"SCHEDULE_GAPLESS",when:e})}get currentTime(){let e=this._actor.getSnapshot();return e.value==="webaudio"?e.context.isPlaying?this.ctx?Math.max(0,this._waRefTrackTime+(this.ctx.currentTime-this._waRefCtxTime)*this.queueRef.playbackRate):0:this.pausedAtTrackTime:this.audio.currentTime}get duration(){return this._actor.getSnapshot().value==="html5"&&!isNaN(this.audio.duration)?this.audio.duration:this.audioBuffer?this.audioBuffer.duration:this.audio.duration}get isPaused(){let e=this._actor.getSnapshot();return e.value==="webaudio"?!e.context.isPlaying:this.audio.paused}get isPlaying(){return this._actor.getSnapshot().context.isPlaying}get trackUrl(){return this._resolvedUrl}get playbackType(){return this._actor.getSnapshot().context.playbackType}get webAudioLoadingState(){return this._actor.getSnapshot().context.webAudioLoadingState}get hasSourceNode(){return this.sourceNode!==null}get machineState(){return this._actor.getSnapshot().value}get scheduledStartContextTime(){return this._actor.getSnapshot().context.scheduledStartContextTime}get isBufferLoaded(){return this.audioBuffer!==null}toInfo(){var e;return{index:this.index,currentTime:this.currentTime,duration:this.duration,isPlaying:this.isPlaying,isPaused:this.isPaused,volume:((e=this.gainNode)==null?void 0:e.gain.value)??this.audio.volume,trackUrl:this.trackUrl,playbackType:this.playbackType,webAudioLoadingState:this.webAudioLoadingState,metadata:this.metadata,playbackRate:this.queueRef.playbackRate,machineState:this.machineState}}_playHtml5(){this.audio.preload!=="auto"&&(this.audio.preload="auto"),this.audio.playbackRate=this.queueRef.playbackRate;let e=this.audio.play();e&&e.catch(t=>{t instanceof Error&&t.name==="NotAllowedError"?this.queueRef.onPlayBlocked():t instanceof Error&&t.name==="AbortError"||this.queueRef.onError(t instanceof Error?t:new Error(String(t)))})}_seekHtml5(){let e=this.pausedAtTrackTime;isFinite(e)&&(this.audio.preload!=="auto"&&(this.audio.preload="auto"),this.audio.readyState>=HTMLMediaElement.HAVE_METADATA?this.audio.currentTime=e:(this.audio.addEventListener("loadedmetadata",()=>{this.audio.currentTime=e},{once:!0}),this.audio.load()))}_crossoverHtml5ToWebAudio(e){if(!this.ctx||!this.audioBuffer||!this.gainNode)return;let t=this.audio.currentTime;if(this.pausedAtTrackTime=isFinite(t)?t:0,!e){this.audio.pause(),this.queueRef.onDebug(`crossoverHtml5ToWebAudio track=${this.index} offset=${this.pausedAtTrackTime.toFixed(3)} wasPlaying=false`);return}let r=this.ctx.currentTime,a=r+A;if(this._html5GainNode){this._html5GainNode.gain.cancelScheduledValues(r),this._html5GainNode.gain.setValueAtTime(1,r),this._html5GainNode.gain.linearRampToValueAtTime(0,a);let s=this.ctx,d=this._html5GainNode;setTimeout(()=>{this.audio.pause(),s&&d&&(d.gain.cancelScheduledValues(s.currentTime),d.gain.setValueAtTime(1,s.currentTime))},A*1e3+5)}else{let s=this.audio.volume;this.audio.volume=0,this.audio.pause(),this.audio.volume=s}this._startSourceNode(this.pausedAtTrackTime,A),this.queueRef.onDebug(`crossoverHtml5ToWebAudio track=${this.index} offset=${this.pausedAtTrackTime.toFixed(3)} wasPlaying=true fade=${A}s mediaSource=${!!this._html5GainNode}`)}_maybeComputeBufferAlignment(){this.audioBuffer&&(this._bufferAlignmentMeasured||(this._bufferStartPaddingSec=0,this._bufferAlignmentMeasured=!0,this.queueRef.onDebug(`_maybeComputeBufferAlignment track=${this.index} bufferDur=${this.audioBuffer.duration.toFixed(3)}s html5Dur=${isNaN(this.audio.duration)?"NaN":this.audio.duration.toFixed(3)+"s"} (alignment shift disabled \u2014 see comment)`)))}_startSourceNode(e,t=0){if(!(!this.ctx||!this.audioBuffer||!this.gainNode)){if(this._maybeComputeBufferAlignment(),this._stopSourceNode(),this.ctx.state==="suspended"&&this.ctx.resume(),this.sourceNode=this.ctx.createBufferSource(),this.sourceNode.buffer=this.audioBuffer,this.sourceNode.playbackRate.value=this.queueRef.playbackRate,t>0){let r=this.ctx.createGain(),a=this.ctx.currentTime;r.gain.setValueAtTime(0,a),r.gain.linearRampToValueAtTime(1,a+t),this.sourceNode.connect(r),r.connect(this.gainNode)}else this.sourceNode.connect(this.gainNode);this.sourceNode.onended=this._handleWebAudioEnded,this._waRefCtxTime=this.ctx.currentTime,this._waRefTrackTime=e,this.sourceNode.start(0,e+this._bufferStartPaddingSec)}}_stopSourceNode(){if(this.sourceNode){this.sourceNode.onended=null;try{this.sourceNode.stop()}catch{}try{this.sourceNode.disconnect()}catch{}this.sourceNode=null}}_seekWebAudio(){let t=this._actor.getSnapshot().context.isPlaying,r=this.pausedAtTrackTime;this._stopSourceNode(),t&&this._startSourceNode(r)}_handleWebAudioEnded=()=>{this.queueRef.onDebug(`_handleWebAudioEnded track=${this.index} sourceNode=${!!this.sourceNode} queueIdx=${this.queueRef.currentTrackIndex}`),this.sourceNode&&this._actor.send({type:"WEBAUDIO_ENDED"})};startProgressLoop(){if(this.rafId!==null)return;let e=()=>{if(this.isPaused||!this.isPlaying){this.rafId=null;return}this.queueRef.onProgress(this.toInfo());let t=this.duration-this.currentTime;!this._actor.getSnapshot().context.notifiedLookahead&&!isNaN(t)&&t<=z&&(this._actor.send({type:"LOOKAHEAD_REACHED"}),queueMicrotask(()=>this.queueRef.onTrackBufferReady(this)));let a=isNaN(this.duration)?D:Math.min(this.duration*.2,D);!this._notifiedPreloadThreshold&&this.currentTime>=a&&(this._notifiedPreloadThreshold=!0,queueMicrotask(()=>this.queueRef.onPreloadReady(this))),this.rafId=requestAnimationFrame(e)};this.rafId=requestAnimationFrame(e)}_stopProgressLoop(){this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null)}};function I(o,e){let t=0;return(...r)=>{let a=performance.now();a-t<e||(t=a,o(...r))}}var M=5,_=class{_tracks=[];_actor;_onProgress;_onEnded;_onPlayNextTrack;_onPlayPreviousTrack;_onStartNewTrack;_onError;_onPlayBlocked;_onDebug;playbackMethod;_volume;_preloadNumTracks;_playbackRate;_scheduledNextIndex=null;_throttledUpdatePositionState=I((e,t,r)=>R(e,t,r),1e3);constructor(e={}){let{tracks:t=[],onProgress:r,onEnded:a,onPlayNextTrack:s,onPlayPreviousTrack:d,onStartNewTrack:u,onError:O,onPlayBlocked:U,onDebug:w,playbackMethod:G="HYBRID",trackMetadata:H=[],volume:B=1,preloadNumTracks:F=2,playbackRate:$=1}=e;this._volume=Math.min(1,Math.max(0,B)),this._preloadNumTracks=Math.max(0,F),this._playbackRate=Math.min(4,Math.max(.25,$)),this.playbackMethod=G,this._onProgress=r,this._onEnded=a,this._onPlayNextTrack=s,this._onPlayPreviousTrack=d,this._onStartNewTrack=u,this._onError=O,this._onPlayBlocked=U,this._onDebug=w,this._tracks=t.map((n,i)=>new f({trackUrl:n,index:i,queue:this,metadata:H[i]}));let W=C({currentTrackIndex:0,trackCount:this._tracks.length}).provide({actions:{deactivateCurrent:({context:n})=>{var i;(i=this._trackAt(n.currentTrackIndex))==null||i.deactivate()},deactivateEndedTrack:({context:n})=>{var i;(i=this._trackAt(n.currentTrackIndex))==null||i.deactivate()},activateAndPlayCurrent:({context:n})=>{let i=this._trackAt(n.currentTrackIndex);i&&(i.activate(),this._scheduledNextIndex!==i.index&&i.play())},playOrContinueGapless:({context:n})=>{let i=this._trackAt(n.currentTrackIndex);i&&(this._scheduledNextIndex!==i.index?i.play():(this._scheduledNextIndex=null,this.onDebug(`onTrackEnded: gapless track ${i.index} \u2014 sourceNode=${i.hasSourceNode} isPlaying=${i.isPlaying} machineState=${i.machineState}`),i.startProgressLoop()))},cancelAllGapless:()=>this._cancelScheduledGapless(),notifyStartNewTrack:({context:n})=>{var l;let i=this._trackAt(n.currentTrackIndex);i&&((l=this._onStartNewTrack)==null||l.call(this,i.toInfo()))},notifyPlayNextTrack:({context:n})=>{var l;let i=this._trackAt(n.currentTrackIndex);i&&((l=this._onPlayNextTrack)==null||l.call(this,i.toInfo()))},notifyPlayPreviousTrack:({context:n})=>{var l;let i=this._trackAt(n.currentTrackIndex);i&&((l=this._onPlayPreviousTrack)==null||l.call(this,i.toInfo()))},notifyEnded:()=>{var n;return(n=this._onEnded)==null?void 0:n.call(this)},updateMediaSessionMetadata:({context:n})=>{let i=this._trackAt(n.currentTrackIndex);i&&b(i.metadata)},preloadAhead:({context:n})=>{this._preloadAhead(n.currentTrackIndex)},playCurrent:({context:n})=>{var i;(i=this._trackAt(n.currentTrackIndex))==null||i.play()},pauseCurrent:({context:n})=>{var i;(i=this._trackAt(n.currentTrackIndex))==null||i.pause()},seekCurrent:({context:n,event:i})=>{var v;let l=i;(v=this._trackAt(n.currentTrackIndex))==null||v.seek(l.time)},seekCurrentToZero:({context:n})=>{var i;(i=this._trackAt(n.currentTrackIndex))==null||i.seek(0)},scheduleGapless:({context:n})=>{this._tryScheduleGapless(n.currentTrackIndex)},cancelScheduledGapless:()=>{this._cancelScheduledGapless()},cancelAndRescheduleGapless:({context:n})=>{this._cancelScheduledGapless(),this._tryScheduleGapless(n.currentTrackIndex)}}});this._actor=j(W),this._actor.subscribe(n=>{N(n.value==="playing")}),this._actor.start(),P({onPlay:()=>this.play(),onPause:()=>{this._actor.getSnapshot().value==="playing"&&this.pause()},onNext:()=>this.next(),onPrevious:()=>this.previous(),onSeek:n=>this.seek(n)})}play(){this._currentTrack&&this._actor.send({type:"PLAY"})}pause(){this._actor.send({type:"PAUSE"})}togglePlayPause(){this._actor.getSnapshot().value==="playing"?this.pause():this.play()}next(){this._actor.getSnapshot().context.currentTrackIndex+1>=this._tracks.length||this._actor.send({type:"NEXT"})}previous(){let e=this._currentTrack;if(e&&e.currentTime>8){e.seek(0),e.play();return}this._actor.send({type:"PREVIOUS"})}gotoTrack(e,t=!1){e<0||e>=this._tracks.length||(this.onDebug(`gotoTrack(${e}, playImmediately=${t}) queueState=${this._actor.getSnapshot().value} curIdx=${this._actor.getSnapshot().context.currentTrackIndex}`),this._actor.send({type:"GOTO",index:e,playImmediately:t}))}seek(e){this._actor.send({type:"SEEK",time:e})}setVolume(e){let t=Math.min(1,Math.max(0,e));this._volume=t;for(let r of this._tracks)r.setVolume(t)}setPlaybackRate(e){var a;let t=Math.min(4,Math.max(.25,e));this._playbackRate=t,(a=this._currentTrack)==null||a.setPlaybackRate(t),this._cancelScheduledGapless();let r=this._actor.getSnapshot();r.value==="playing"&&this._tryScheduleGapless(r.context.currentTrackIndex)}addTrack(e,t={}){let r=this._tracks.length,a=t.metadata??{};this._tracks.push(new f({trackUrl:e,index:r,queue:this,skipHEAD:t.skipHEAD,metadata:a})),this._actor.send({type:"ADD_TRACK"})}removeTrack(e){if(!(e<0||e>=this._tracks.length)){this._tracks[e].destroy(),this._tracks.splice(e,1);for(let t=e;t<this._tracks.length;t++)this._tracks[t].index=t;this._scheduledNextIndex===e?this._scheduledNextIndex=null:this._scheduledNextIndex!==null&&this._scheduledNextIndex>e&&this._scheduledNextIndex--,this._actor.send({type:"REMOVE_TRACK",index:e})}}resumeAudioContext(){return y()}destroy(){for(let e of this._tracks)e.destroy();this._tracks=[],this._actor.stop()}get currentTrack(){var e;return(e=this._currentTrack)==null?void 0:e.toInfo()}get currentTrackIndex(){return this._actor.getSnapshot().context.currentTrackIndex}get tracks(){return this._tracks.map(e=>e.toInfo())}get isPlaying(){return this._actor.getSnapshot().value==="playing"}get isPaused(){return this._actor.getSnapshot().value==="paused"}get volume(){return this._volume}get preloadNumTracks(){return this._preloadNumTracks}set preloadNumTracks(e){this._preloadNumTracks=Math.max(0,e);let t=this._actor.getSnapshot();t.value==="playing"&&this._preloadAhead(t.context.currentTrackIndex)}get playbackRate(){return this._playbackRate}get queueSnapshot(){let e=this._actor.getSnapshot();return{state:e.value,context:e.context}}onTrackEnded(e){let t=this._actor.getSnapshot();if(this.onDebug(`onTrackEnded track=${e.index} queueState=${t.value} curIdx=${t.context.currentTrackIndex}`),e!==this._trackAt(t.context.currentTrackIndex))return;this._actor.send({type:"TRACK_ENDED"});let r=this._actor.getSnapshot();this.onDebug(`onTrackEnded after TRACK_ENDED \u2192 queueState=${r.value} curIdx=${r.context.currentTrackIndex}`)}onTrackBufferReady(e){this._actor.send({type:"TRACK_LOADED",index:e.index})}onProgress(e){var t;e.index===this._actor.getSnapshot().context.currentTrackIndex&&(isNaN(e.duration)||this._throttledUpdatePositionState(e.duration,e.currentTime,this._playbackRate),(t=this._onProgress)==null||t.call(this,e))}onError(e){var t;(t=this._onError)==null||t.call(this,e)}onPlayBlocked(){var e;this._actor.send({type:"PAUSE"}),(e=this._onPlayBlocked)==null||e.call(this)}onPreloadReady(e){let t=this._actor.getSnapshot();e.index===t.context.currentTrackIndex&&this._preloadAhead(t.context.currentTrackIndex)}onDebug(e){var t;(t=this._onDebug)==null||t.call(this,e)}_trackAt(e){return this._tracks[e]}get _currentTrack(){return this._tracks[this._actor.getSnapshot().context.currentTrackIndex]}_preloadAhead(e){let t=this._trackAt(e);if(t!=null&&t.isPlaying&&t.playbackType==="HTML5"&&t.webAudioLoadingState==="LOADING"){this.onDebug(`_preloadAhead(${e}): deferring all \u2014 current track buffer still loading`);return}let r=t!=null&&t.isPlaying&&(()=>{let s=isNaN(t.duration)?15:Math.min(t.duration*.2,15);return t.currentTime<s})(),a=e+this._preloadNumTracks+1;this.onDebug(`_preloadAhead(${e}) limit=${a} trackCount=${this._tracks.length} belowThreshold=${r}`);for(let s=e+1;s<this._tracks.length&&s<a;s++){let d=this._tracks[s];if(s>e+1&&r){this.onDebug(`_preloadAhead: deferring track ${s} (current at ${t.currentTime.toFixed(1)}s, below threshold)`);return}if(d.isBufferLoaded)this.onDebug(`_preloadAhead: track ${s} already loaded`);else{this.onDebug(`_preloadAhead: starting preload for track ${s}`),d.preload();return}}}_cancelScheduledGapless(){if(this._scheduledNextIndex===null)return;let e=this._trackAt(this._scheduledNextIndex);e&&(e.cancelGaplessStart(),this.onDebug(`_cancelScheduledGapless: cancelled track ${this._scheduledNextIndex}`)),this._scheduledNextIndex=null}_tryScheduleGapless(e){let t=k();if(!t||this.playbackMethod==="HTML5_ONLY")return;let r=e+1;if(r>=this._tracks.length)return;let a=this._tracks[e],s=this._tracks[r];if(!a.isBufferLoaded||!s.isBufferLoaded||this._scheduledNextIndex===r||!a.isPlaying)return;let d=this._computeTrackEndTime(a);if(d===null||d<t.currentTime+.01)return;let u=d-t.currentTime;if(a.playbackType==="HTML5"&&u>M){this.onDebug(`_tryScheduleGapless: deferring \u2014 HTML5 track ${e} has ${u.toFixed(1)}s remaining (max lookahead=${M}s)`);return}s.scheduleGaplessStart(d),this.onDebug(`_tryScheduleGapless: scheduled track ${r} at endTime=${d.toFixed(3)} (in ${(d-t.currentTime).toFixed(1)}s) curPlaybackType=${a.playbackType}`),this._scheduledNextIndex=r}_computeTrackEndTime(e){let t=k();if(!t||!e.isBufferLoaded)return null;let r=e.duration;if(isNaN(r))return null;if(e.scheduledStartContextTime!==null)return e.scheduledStartContextTime+r/this._playbackRate;let a=(r-e.currentTime)/this._playbackRate;return a<=0?null:t.currentTime+a}};export{_ as Queue,_ as default};
2
2
  //# sourceMappingURL=index.mjs.map