Song player's generated play button now works.

This commit is contained in:
Harrison Deng 2022-04-18 01:19:59 -05:00
parent b6df52fde5
commit 7e401a5472
8 changed files with 90 additions and 83 deletions

View File

@ -8,10 +8,11 @@
"scripts": { "scripts": {
"build:prod": "webpack --config webpack.prod.cjs", "build:prod": "webpack --config webpack.prod.cjs",
"build:dev": "webpack --config webpack.dev.cjs", "build:dev": "webpack --config webpack.dev.cjs",
"build": "webpack --config webpack.dev.cjs", "build": "npm run build:dev",
"test:junit": "mocha tests/** --reporter mocha-junit-reporter --reporter-options mochaFile=./junit/test_results.xml", "test:junit": "mocha tests/** --reporter mocha-junit-reporter --reporter-options mochaFile=./junit/test_results.xml",
"test:console": "mocha tests/**", "test:console": "mocha tests/**",
"test": "mocha tests/**", "test": "npm run test:console",
"watch": "webpack --watch --config webpack.dev.cjs",
"docs": "esdoc" "docs": "esdoc"
}, },
"author": "", "author": "",

View File

@ -37,19 +37,20 @@ export default class PlayListSong {
*/ */
fetchAudio(onReady) { fetchAudio(onReady) {
if (this.#audio) { if (this.#audio) {
if (this.ready) { if (this.#ready) {
if (onReady) onReady(this.#audio); if (onReady) onReady(this.#audio);
} }
return this.#audio; return this.#audio;
} }
this.#audio = new Audio(this._url); this.#audio = new Audio(this.#url);
console.log("Audio is not ready. Loading.");
this.#audio.addEventListener("canplaythrough", () => { const listener = () => {
this.ready = true; this.#ready = true;
if (onReady && this.isAudioInstantiated()) { if (onReady && this.isAudioInstantiated()) {
onReady(this.#audio); onReady(this.#audio);
} }
}); };
this.#audio.addEventListener("canplaythrough", listener, { once: true });
return this.#audio; return this.#audio;
} }
@ -58,7 +59,7 @@ export default class PlayListSong {
* The name of the song to be displayed. * The name of the song to be displayed.
*/ */
get displayName() { get displayName() {
return this._displayName; return this.#displayName;
} }
/** /**
@ -66,7 +67,7 @@ export default class PlayListSong {
* The author of the song. * The author of the song.
*/ */
get author() { get author() {
return this._getAuthor; return this.#author;
} }
/** /**
@ -74,7 +75,7 @@ export default class PlayListSong {
* The url at which the file for this song can be found. * The url at which the file for this song can be found.
*/ */
get url() { get url() {
return this._url; return this.#url;
} }
/** /**
@ -156,7 +157,7 @@ export default class PlayListSong {
this.#audio.pause(); this.#audio.pause();
this.unloadVisualizer(); this.unloadVisualizer();
this.#audio = null; this.#audio = null;
this.ready = false; this.#ready = false;
} }
/** /**
@ -165,9 +166,9 @@ export default class PlayListSong {
* @returns {Visualizer} returns the visualizer. * @returns {Visualizer} returns the visualizer.
*/ */
getVisualizer(fftSize = 1024) { getVisualizer(fftSize = 1024) {
if (this._visualizer && this._visualizer.getFftSize() === fftSize) return this._visualizer; if (this.#visualizer && this.#visualizer.getFftSize() === fftSize) return this.#visualizer;
this._visualizer = new Visualizer(this.fetchAudio(), fftSize); this.#visualizer = new Visualizer(this.fetchAudio(), fftSize);
return this._visualizer; return this.#visualizer;
} }
/** /**
@ -175,7 +176,7 @@ export default class PlayListSong {
* @returns {boolean} returns true if and only if the visualizer is instantiated. * @returns {boolean} returns true if and only if the visualizer is instantiated.
*/ */
isVisualizerInstantiated() { isVisualizerInstantiated() {
return this._visualizer ? true : false; return this.#visualizer ? true : false;
} }
/** /**
@ -183,7 +184,7 @@ export default class PlayListSong {
* Stops the visualizer. * Stops the visualizer.
*/ */
unloadVisualizer() { unloadVisualizer() {
this._visualizer.stop(); this.#visualizer.stop();
this._visualizer = null; this.#visualizer = null;
} }
} }

View File

@ -7,11 +7,10 @@ export default class SongPlayer {
#playlist; #playlist;
#current = 0; #current = 0;
#volume = 1; #volume = 1;
#playing = true; #autoplay = false;
#playlistChangeListeners = []; #playlistChangeListeners = [];
#currentSongChangeListeners = []; #currentSongChangeListeners = [];
#volumeChangeListeners = []; #volumeChangeListeners = [];
#playingChangeListeners = [];
#allAudioEventListeners = {}; #allAudioEventListeners = {};
/** /**
@ -19,20 +18,26 @@ export default class SongPlayer {
* @param {SongPlaylist} playlist the playlist of songs that this player is in charge of. * @param {SongPlaylist} playlist the playlist of songs that this player is in charge of.
*/ */
constructor(playlist) { constructor(playlist) {
this.#playlist = playlist; this.playlist = playlist;
this.addEventListenerToCurrentAudio("ended", () => {
this.next();
});
} }
/** /**
* The new playlist of songs that this player is in charge of. * The new playlist of songs that this player is in charge of.
*/ */
set playlist(playlist) { set playlist(playlist) {
this.#playlist.unloadAllAudio(); this.#playlist?.unloadAllAudio();
const old = this.#playlist; const old = this.#playlist;
this.#playlist = playlist; this.#playlist = playlist;
this.#current = 0; if (!this.changeCurrentSongIndex(0)) {
throw new Error("The provided playlist has no songs.");
}
this.#playlistChangeListeners.forEach(playlistChangeListener => { this.#playlistChangeListeners.forEach(playlistChangeListener => {
playlistChangeListener(old, this.getPlaylist()); playlistChangeListener(old, this.getPlaylist());
}); });
} }
/** /**
@ -48,7 +53,7 @@ export default class SongPlayer {
* @returns {boolean} true if and only if successful in going to the next song. * @returns {boolean} true if and only if successful in going to the next song.
*/ */
next() { next() {
return this.changeCurrentSongIndex(this.getCurrentSongIndex() + 1); return this.changeCurrentSongIndex(this.currentSongIndex + 1);
} }
/** /**
@ -57,7 +62,7 @@ export default class SongPlayer {
* @returns {boolean} true if and only if successful in going to the previous song. * @returns {boolean} true if and only if successful in going to the previous song.
*/ */
previous() { previous() {
return this.changeCurrentSongIndex(this.getCurrentSongIndex() - 1); return this.changeCurrentSongIndex(this.currentSongIndex - 1);
} }
/** /**
@ -66,27 +71,28 @@ export default class SongPlayer {
* @returns {boolean} true if and only if successful jumping to the given index. * @returns {boolean} true if and only if successful jumping to the given index.
*/ */
changeCurrentSongIndex(index) { changeCurrentSongIndex(index) {
console.log("Changing current song to " + index);
if (index >= this.#playlist.total()) return false; if (index >= this.#playlist.total()) return false;
if (index <= 0) return false; if (index < 0) return false;
Object.keys(this.#allAudioEventListeners).forEach(key => { Object.keys(this.#allAudioEventListeners).forEach(key => {
const listeners = this.#allAudioEventListeners[key]; const listeners = this.#allAudioEventListeners[key];
listeners.forEach(listener => { listeners.forEach(listener => {
this.getCurrentSong().getAudio().removeEventListener(key, listener); this.currentSong.fetchAudio().removeEventListener(key, listener);
}); });
}); });
this.getCurrentSong().unloadAudio(); this.currentSong.unloadAudio();
const old = this.getCurrentSong(); const old = this.currentSong;
this.#current = index; this.#current = index;
this.#currentSongChangeListeners.forEach(currentChangeListener => { this.#currentSongChangeListeners.forEach(currentChangeListener => {
currentChangeListener(old, this.getCurrentSong()); currentChangeListener(old, this.currentSong);
}); });
Object.keys(this.#allAudioEventListeners).forEach(key => { Object.keys(this.#allAudioEventListeners).forEach(key => {
const listeners = this.#allAudioEventListeners[key]; const listeners = this.#allAudioEventListeners[key];
listeners.forEach(listener => { listeners.forEach(listener => {
this.getCurrentSong().getAudio().addEventListener(key, listener); this.currentSong.fetchAudio().addEventListener(key, listener);
}); });
}); });
if (this.#playing) { if (this.#autoplay) {
this.playCurrent(); this.playCurrent();
} }
return true; return true;
@ -101,23 +107,24 @@ export default class SongPlayer {
} }
playCurrent() { playCurrent() {
this.getCurrentSong().getAudio((audio) => { console.log("Attempting to play...");
audio.volume = this.#volume; this.currentSong.fetchAudio((audio) => {
audio.volume = this.volume;
audio.play(); audio.play();
console.log("playing...");
}); });
this.#playing = true;
} }
pauseCurrent() { pauseCurrent() {
this.getCurrentSong().getAudio().pause(); console.log("Pausing.");
this.#playing = true; this.currentSong.fetchAudio().pause();
} }
/** /**
* Toggles whether or not the current song is playing. * Toggles whether or not the current song is playing.
*/ */
togglePlay() { togglePlay() {
if (this.#playing) { if (this.playing) {
this.pauseCurrent(); this.pauseCurrent();
} else { } else {
this.playCurrent(); this.playCurrent();
@ -131,7 +138,7 @@ export default class SongPlayer {
if (volume > 1) volume = 1; if (volume > 1) volume = 1;
if (volume < 0) volume = 0; if (volume < 0) volume = 0;
this.#volume = volume; this.#volume = volume;
this.getCurrentSong().getAudio().volume = this.#volume; this.currentSong.fetchAudio().volume = this.#volume;
} }
/** /**
@ -149,8 +156,8 @@ export default class SongPlayer {
* @returns {boolean} true if and only if the position to seek to is within the duration of the track. This also means that if the track has not finished loading the duration data, than this will always return false. * @returns {boolean} true if and only if the position to seek to is within the duration of the track. This also means that if the track has not finished loading the duration data, than this will always return false.
*/ */
seek(position) { seek(position) {
if (position > this.getCurrentSong().getAudio().duration || position < 0) return false; if (position > this.currentSong.fetchAudio().duration || position < 0) return false;
this.getCurrentSong().getAudio(audio => { this.currentSong.fetchAudio(audio => {
audio.currentTime = position; audio.currentTime = position;
}); });
return true; return true;
@ -161,7 +168,7 @@ export default class SongPlayer {
* How many seconds into the audio track has been played in seconds. * How many seconds into the audio track has been played in seconds.
*/ */
get currentPosition() { get currentPosition() {
return this.getCurrentSong().getAudio().currentTime; return this.currentSong.fetchAudio().currentTime;
} }
/** /**
@ -169,7 +176,7 @@ export default class SongPlayer {
* The total length of the song, or NaN if this information has not loaded yet. * The total length of the song, or NaN if this information has not loaded yet.
*/ */
get currentDuration() { get currentDuration() {
return this.getCurrentSong().getAudio().duration; return this.currentSong.fetchAudio().duration;
} }
/** /**
@ -183,18 +190,18 @@ export default class SongPlayer {
const playButton = document.createElement("button"); const playButton = document.createElement("button");
playButton.classList.add("player"); playButton.classList.add("player");
playButton.classList.add("play-btn"); playButton.classList.add("play-btn");
if (!this.#playing) playButton.classList.add("paused"); if (this.playing) playButton.classList.add("pause");
playButton.addEventListener("click", () => { playButton.addEventListener("click", () => {
console.log("Generated play button has been pressed.");
this.togglePlay(); this.togglePlay();
}); });
this.addPlayChangeListener((old, current) => { this.addEventListenerToCurrentAudio("play", () => {
if (current) { playButton.classList.add("pause");
playButton.classList.remove("paused"); });
} else { this.addEventListenerToCurrentAudio("pause", () => {
playButton.classList.add("paused"); playButton.classList.remove("pause");
}
}); });
return playButton; return playButton;
} }
@ -219,12 +226,15 @@ export default class SongPlayer {
return previousButton; return previousButton;
} }
/**
* The current {@link PlaylistSong}.
*/
get currentSong() { get currentSong() {
return this.#playlist.songAtIndex(this.#current); return this.#playlist.songAtIndex(this.#current);
} }
/** /**
* @callback changeListener * @typedef {function} changeListener
* @param {*} old the previous value. * @param {*} old the previous value.
* @param {*} current the the current (new) value. * @param {*} current the the current (new) value.
*/ */
@ -303,30 +313,6 @@ export default class SongPlayer {
} }
/**
*
* @param {changeListener} listener the listener that is called when the player's volume is changed.
* @returns {boolean} true if and only if the listener was successfully added.
*/
addPlayChangeListener(listener) {
if (this.#playingChangeListeners.includes(listener)) return false;
this.#playingChangeListeners.push(listener);
return true;
}
/**
*
* @param {changeListener} listener the play change listener to remove.
* @returns {boolean} true if and only if a listener was successfully removed from the callback list.
*/
removePlayChangeListener(listener) {
const removeIndex = this.#playingChangeListeners.indexOf(listener);
if (removeIndex < 0) return false;
this.#playingChangeListeners.splice(removeIndex, 1);
return true;
}
/** /**
* *
* @param {string} type the type of the listener on the {@link HTMLAudioElement}. * @param {string} type the type of the listener on the {@link HTMLAudioElement}.
@ -341,7 +327,7 @@ export default class SongPlayer {
} }
if (typeListeners.includes(eventListener)) return false; if (typeListeners.includes(eventListener)) return false;
typeListeners.push(eventListener); typeListeners.push(eventListener);
this.getCurrentSong().getAudio().addEventListener(type, eventListener); this.currentSong.fetchAudio().addEventListener(type, eventListener);
return true; return true;
} }
@ -357,7 +343,14 @@ export default class SongPlayer {
const removeIndex = typeListeners.indexOf(eventListener); const removeIndex = typeListeners.indexOf(eventListener);
if (removeIndex < 0) return false; if (removeIndex < 0) return false;
typeListeners.splice(removeIndex, 1); typeListeners.splice(removeIndex, 1);
this.getCurrentSong().getAudio().removeEventListener(type, eventListener); this.currentSong.fetchAudio().removeEventListener(type, eventListener);
return true; return true;
} }
/**
* If the player is playing any songs right now.
*/
get playing() {
return this.currentSong.isAudioInstantiated() && !this.currentSong.fetchAudio().paused;
}
} }

View File

@ -33,7 +33,7 @@ export default class SongPlaylist {
if (index >= this.#list.length) { if (index >= this.#list.length) {
return null; return null;
} }
return this.list[index]; return this.#list[index];
} }
/** /**

View File

@ -10,7 +10,7 @@
border-width: 30px 0px 30px 50px; border-width: 30px 0px 30px 50px;
} }
.player.play-btn.paused { .player.play-btn.pause {
border-style: double; border-style: double;
border-width: 0px 0px 0px 60px; border-width: 0px 0px 0px 60px;
} }

View File

@ -1,7 +1,8 @@
{ {
"cSpell.words": [ "cSpell.words": [
"audioshowkit", "audioshowkit",
"audioshowkitshowcase" "audioshowkitshowcase",
"Rosen"
], ],
"html.format.wrapLineLength": 0 "html.format.wrapLineLength": 0
} }

View File

@ -16,8 +16,10 @@
<h1>Welcome to the AudioShowKit Showcase!</h1> <h1>Welcome to the AudioShowKit Showcase!</h1>
<div> <div>
<h2>Song Player</h2> <h2>Song Player</h2>
<p>The song player acts as a easier way for developers to set up multiple audio sources to play while a user <p>The song player acts as a easier way for developers to set up multiple audio sources to play while a user is browsing through their site.</p>
is browsing through their site.</p>
<h3>Example</h3>
<div id="player-showcase"></div>
</div> </div>
<div> <div>
<h2>Visualizer and Visualizer Update Manager</h2> <h2>Visualizer and Visualizer Update Manager</h2>
@ -31,6 +33,7 @@
Ranges of bins, where the parameters are the elapsed time since last updating, and an array of the amplitudes. Ranges of bins, where the parameters are the elapsed time since last updating, and an array of the amplitudes.
</li> </li>
</ul> </ul>
<div id="visualizer-showcase"></div>
</div> </div>
<div> <div>
<h2>Mappings</h2> <h2>Mappings</h2>

View File

@ -1 +1,9 @@
console.log("test"); console.log("Beginning showcase..");
const ask = window.audioshowkit;
const playlist = ask.player.createSongPlaylist("Demo playlist");
playlist.add("/audio/moments.mp3", "moments", "Lost Identities x Robbie Rosen");
playlist.add("/audio/XXI.mp3", "XXI", "QR");
const player = ask.player.createSongPlayer(playlist);
const playerElem = document.getElementById("player-showcase");
playerElem.appendChild(player.generatePlayElement());