1 | /**
|
---|
2 | * @file Provides an interface for playing transcription audio.
|
---|
3 | * @author Carl Stephens
|
---|
4 | * @module
|
---|
5 | */
|
---|
6 |
|
---|
7 | class LoadedAudio {
|
---|
8 | /**
|
---|
9 | * Initialises a new instance of the {@link LoadedAudio} class.
|
---|
10 | * @param {String | null} id The ID of the transcription for which this audio belongs to.
|
---|
11 | * @param {String | null} url The audio object URL.
|
---|
12 | */
|
---|
13 | constructor(id, url) {
|
---|
14 | /** @type {String | null} The ID of the transcription for which this audio belongs to. */
|
---|
15 | this.id = id;
|
---|
16 |
|
---|
17 | /** @type {String | null} The audio object URL. */
|
---|
18 | this.url = url;
|
---|
19 | }
|
---|
20 | }
|
---|
21 |
|
---|
22 | /**
|
---|
23 | * Polls an audio element's current time and updates the vuex store accordingly.
|
---|
24 | * @param {Audio} audioElement The audio element.
|
---|
25 | * @param {*} store The vuex store to update.
|
---|
26 | */
|
---|
27 | function pollAudioTime(audioElement, store) {
|
---|
28 | let lastTime = 0;
|
---|
29 |
|
---|
30 | (function poll() {
|
---|
31 | if (audioElement.currentTime !== lastTime) {
|
---|
32 | lastTime = audioElement.currentTime;
|
---|
33 | store.commit("playbackStateSetTime", { id: store.state.playbackState.id, time: lastTime });
|
---|
34 | }
|
---|
35 |
|
---|
36 | requestAnimationFrame(poll);
|
---|
37 | })();
|
---|
38 | }
|
---|
39 |
|
---|
40 | /**
|
---|
41 | * Loads an audio file.
|
---|
42 | * @param {HTMLAudioElement} player The audio player.
|
---|
43 | * @param {TranscriptionViewModel} transcription The name of the requested audio file.
|
---|
44 | * @param {LoadedAudio} current The currently loaded audio.
|
---|
45 | * @returns {LoadedAudio} If a new audio file was loaded, a new audio tracking object, else the current one.
|
---|
46 | */
|
---|
47 | function loadAudio(player, transcription, current) {
|
---|
48 | // TODO: Need to profile on some larger tracks; this may not be worth it? Better to cache a URL object instead?
|
---|
49 | if (current.url !== null) {
|
---|
50 | URL.revokeObjectURL(current.url);
|
---|
51 | }
|
---|
52 |
|
---|
53 | const urlObject = URL.createObjectURL(transcription.file);
|
---|
54 | player.src = urlObject;
|
---|
55 | player.load();
|
---|
56 |
|
---|
57 | return new LoadedAudio(transcription.id, urlObject);
|
---|
58 | }
|
---|
59 |
|
---|
60 | export default class AudioPlayback {
|
---|
61 | /**
|
---|
62 | * Initialises the {@link AudioPlayback} class.
|
---|
63 | * @param {*} store The vuex store to update.
|
---|
64 | */
|
---|
65 | static initialise(store) {
|
---|
66 | /** @type The vuex store. */
|
---|
67 | this.store = store;
|
---|
68 |
|
---|
69 | this.player = new Audio();
|
---|
70 | this.loadedAudio = new LoadedAudio(null, null);
|
---|
71 | this.requestedPlaybackTime = 0;
|
---|
72 |
|
---|
73 | pollAudioTime(this.player, store);
|
---|
74 |
|
---|
75 | this.player.addEventListener("ended", function() {
|
---|
76 | store.commit("playbackStateSetIsPlaying", false);
|
---|
77 | });
|
---|
78 | }
|
---|
79 |
|
---|
80 | static onCanPlayThrough() {
|
---|
81 | AudioPlayback.player.removeEventListener("canplaythrough", AudioPlayback.onCanPlayThrough);
|
---|
82 |
|
---|
83 | AudioPlayback.player.currentTime = AudioPlayback.requestedPlaybackTime;
|
---|
84 | AudioPlayback.store.commit("playbackStateSetLength", { id: AudioPlayback.loadedAudio.id, time: AudioPlayback.player.duration });
|
---|
85 | }
|
---|
86 |
|
---|
87 | /**
|
---|
88 | * Loads a transcription's audio file.
|
---|
89 | * @param {String} id The ID of the transcription to load the audio of.
|
---|
90 | * @param {Number} startTime The time into the audio to set the current playback time to. Leave negative to select the last value, or zero.
|
---|
91 | */
|
---|
92 | static async load(id, startTime = -1) {
|
---|
93 | this.player.addEventListener("canplaythrough", this.onCanPlayThrough);
|
---|
94 |
|
---|
95 | const playbackTimes = this.store.state.playbackState.playbackTimes;
|
---|
96 |
|
---|
97 | this.requestedPlaybackTime = startTime;
|
---|
98 | if (playbackTimes.has(id) && startTime < 0) {
|
---|
99 | this.requestedPlaybackTime = playbackTimes.get(id);
|
---|
100 | }
|
---|
101 |
|
---|
102 | if (this.loadedAudio.id !== id) {
|
---|
103 | this.loadedAudio = loadAudio(this.player, this.store.state.rawTranscriptions.get(id), this.loadedAudio);
|
---|
104 |
|
---|
105 | this.store.commit("playbackStateSetID", id);
|
---|
106 | }
|
---|
107 |
|
---|
108 | this.player.currentTime = AudioPlayback.requestedPlaybackTime;
|
---|
109 | this.store.commit("playbackStateSetLength", { id: id, time: this.player.duration });
|
---|
110 | }
|
---|
111 |
|
---|
112 | /**
|
---|
113 | * Plays a transcription's audio file.
|
---|
114 | * @param {String} id The ID of the transcription to play audio for.
|
---|
115 | * @param {Number} startTime The time into the audio at which to start playing. Leave negative to resume playback if paused.
|
---|
116 | */
|
---|
117 | static async play(id, startTime = -1) {
|
---|
118 | // Start at the beginning if we've reached the end
|
---|
119 | if (this.player.duration <= this.player.currentTime && startTime < 0) {
|
---|
120 | await this.load(id, 0);
|
---|
121 | }
|
---|
122 | else {
|
---|
123 | await this.load(id, startTime);
|
---|
124 | }
|
---|
125 |
|
---|
126 | await this.player.play();
|
---|
127 | this.store.commit("playbackStateSetIsPlaying", true);
|
---|
128 | }
|
---|
129 |
|
---|
130 | /**
|
---|
131 | * Sets the current time of the audio player.
|
---|
132 | * @param {Number} time The time to scrub to.
|
---|
133 | * @param {Boolean} fromCurrent Indicates if the time is an offset.
|
---|
134 | */
|
---|
135 | static scrub(time, fromCurrent) {
|
---|
136 | const newTime = fromCurrent ? this.player.currentTime + time : time;
|
---|
137 | this.player.currentTime = newTime;
|
---|
138 | }
|
---|
139 |
|
---|
140 | /**
|
---|
141 | * Pauses playback.
|
---|
142 | */
|
---|
143 | static pause() {
|
---|
144 | this.player.pause();
|
---|
145 | this.store.commit("playbackStateSetIsPlaying", false);
|
---|
146 | }
|
---|
147 |
|
---|
148 | /**
|
---|
149 | * Resumes playback.
|
---|
150 | * @param {Number} startTime The time into the audio at which to resume playback.
|
---|
151 | */
|
---|
152 | static resume(startTime = -1) {
|
---|
153 | this.play(this.loadedAudio.id, startTime);
|
---|
154 | }
|
---|
155 | }
|
---|