Skip to content
This repository was archived by the owner on Apr 18, 2024. It is now read-only.

Commit a93771b

Browse files
authored
fix: LSDV-4572: Audio playback and playhead briefly get out of sync (#1212)
* fix: LSDV-4572: Sound sometimes delayed to playback visual cursor * sync currentTime with audio currentTime after buffering completes
1 parent 1e27ec5 commit a93771b

File tree

4 files changed

+17
-39
lines changed

4 files changed

+17
-39
lines changed

src/lib/AudioUltra/Controls/Player.ts

+13-10
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,6 @@ export class Player extends Destructable {
197197

198198
private playSource(start?: number, duration?: number) {
199199
this.stopWatch();
200-
this.timestamp = performance.now();
201200
this.connectSource();
202201

203202
if (!this.audio) return;
@@ -220,17 +219,21 @@ export class Player extends Destructable {
220219
this.bufferResolve = resolve;
221220
});
222221

223-
const promise = this.audio.el.play();
222+
const time = this.currentTime;
224223

225-
// Ensure that the audio can play before invoking the timer updates
226-
promise.then(() => {
227-
if (this.bufferPromise) {
228-
this.bufferPromise.then(() => {
224+
this.audio.el.play().then(() => {
225+
this.bufferPromise!.then(() => {
226+
this.timestamp = performance.now();
227+
228+
// We need to compensate for the time it took to load the buffer
229+
// otherwise the audio will be out of sync of the timer we use to
230+
// render updates
231+
if (this.audio?.el) {
232+
this.currentTime = time;
233+
this.audio.el.currentTime = this.currentTime;
229234
this.watch();
230-
});
231-
} else {
232-
this.watch();
233-
}
235+
}
236+
});
234237
});
235238
}
236239
}

src/lib/AudioUltra/Media/AudioDecoder.ts

+1-4
Original file line numberDiff line numberDiff line change
@@ -270,10 +270,7 @@ export class AudioDecoder extends Events<AudioDecoderEvents> {
270270

271271
const totalDuration = this.worker.duration;
272272

273-
// The duration start is offset by 1 second in the actual wasm decoder.
274-
// This is to ensure we correctly capture the entire data set for the waveform,
275-
// aligning it with the audio data when played back via the media element.
276-
let durationOffset = -1;
273+
let durationOffset = 0;
277274

278275
while (true) {
279276
yield new Promise((resolve, reject) => {

src/lib/AudioUltra/Media/MediaLoader.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ export class MediaLoader extends Destructable {
184184
}
185185
});
186186

187-
xhr.open('GET', url);
187+
xhr.open('GET', url, true);
188188
xhr.send();
189189
});
190190
}

src/lib/AudioUltra/Media/WaveformAudio.ts

+2-24
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ export class WaveformAudio extends Events<WaveformAudioEvents> {
112112
delete this.decoderPromise;
113113
this.decoder?.destroy();
114114
delete this.decoder;
115+
this.el?.removeEventListener('error', this.mediaReady);
115116
this.el?.removeEventListener('canplaythrough', this.mediaReady);
116117
this.el?.remove();
117118
delete this.el;
@@ -176,7 +177,6 @@ export class WaveformAudio extends Events<WaveformAudioEvents> {
176177

177178
this.el = document.createElement('audio');
178179
this.el.preload = 'auto';
179-
this.el.muted = true;
180180
this.el.setAttribute('data-testid', 'waveform-audio');
181181
this.el.style.display = 'none';
182182
document.body.appendChild(this.el);
@@ -198,9 +198,8 @@ export class WaveformAudio extends Events<WaveformAudioEvents> {
198198
if (this.mediaResolve) {
199199
this.mediaResolve?.();
200200
this.mediaResolve = undefined;
201-
await this.forceBuffer();
202-
this.invoke('canplay');
203201
}
202+
this.invoke('canplay');
204203
}
205204
};
206205

@@ -211,27 +210,6 @@ export class WaveformAudio extends Events<WaveformAudioEvents> {
211210
if (!this.src || !this.el) return;
212211

213212
this.el.src = this.src;
214-
this.el.load();
215-
}
216-
217-
/**
218-
* In order for the audio to playback sound immediately, we need to force the browser to buffer the audio.
219-
* This works by just playing the audio and then immediately pausing it.
220-
*/
221-
private async forceBuffer() {
222-
if (!this.el) return;
223-
224-
try {
225-
await this.el.play();
226-
this.el.pause();
227-
} catch {
228-
// ignore
229-
} finally {
230-
if (this.el) {
231-
this.el.currentTime = 0;
232-
this.el.muted = false;
233-
}
234-
}
235213
}
236214

237215
private createAudioDecoder() {

0 commit comments

Comments
 (0)