1
0
forked from sent/waves
waves/public/assets/g/mario/References/AudioPlayr-0.2.1.js
2025-04-09 17:11:14 -05:00

562 lines
22 KiB
JavaScript

/// <reference path="ItemsHoldr-0.2.1.ts" />
var AudioPlayr;
(function (AudioPlayr_1) {
"use strict";
/**
* An audio library to automate preloading and controlled playback of multiple
* audio tracks, with support for different browsers' preferred file types.
*/
var AudioPlayr = (function () {
/**
* Initializes a new instance of the AudioPlayr class.
*
* @param settings Settings to use for initialization.
*/
function AudioPlayr(settings) {
var volumeInitial;
if (typeof settings.library === "undefined") {
throw new Error("No library given to AudioPlayr.");
}
if (typeof settings.directory === "undefined") {
throw new Error("No directory given to AudioPlayr.");
}
if (typeof settings.fileTypes === "undefined") {
throw new Error("No fileTypes given to AudioPlayr.");
}
if (!settings.ItemsHolder) {
throw new Error("No ItemsHoldr given to AudioPlayr.");
}
this.ItemsHolder = settings.ItemsHolder;
this.directory = settings.directory;
this.fileTypes = settings.fileTypes;
this.getThemeDefault = settings.getThemeDefault || "Theme";
this.getVolumeLocal = typeof settings.getVolumeLocal === "undefined"
? 1 : settings.getVolumeLocal;
// Sounds should always start blank
this.sounds = {};
// Preload everything!
this.generateLibraryFromSettings(settings.library);
volumeInitial = this.ItemsHolder.getItem("volume");
if (volumeInitial === undefined) {
this.setVolume(1);
}
else {
this.setVolume(this.ItemsHolder.getItem("volume"));
}
this.setMuted(this.ItemsHolder.getItem("muted") || false);
}
/* Simple getters
*/
/**
* @returns The listing of <audio> Elements, keyed by name.
*/
AudioPlayr.prototype.getLibrary = function () {
return this.library;
};
/**
* @returns The allowed filetypes for audio files.
*/
AudioPlayr.prototype.getFileTypes = function () {
return this.fileTypes;
};
/**
* @returns The currently playing <audio> Elements, keyed by name.
*/
AudioPlayr.prototype.getSounds = function () {
return this.sounds;
};
/**
* @returns The current playing theme's <audio> Element.
*/
AudioPlayr.prototype.getTheme = function () {
return this.theme;
};
/**
* @returns The name of the currently playing theme.
*/
AudioPlayr.prototype.getThemeName = function () {
return this.themeName;
};
/**
* @returns The directory under which all filetype directories are to be located.
*/
AudioPlayr.prototype.getDirectory = function () {
return this.directory;
};
/* Playback modifiers
*/
/**
* @returns The current volume as a Number in [0,1], retrieved by the ItemsHoldr.
*/
AudioPlayr.prototype.getVolume = function () {
return Number(this.ItemsHolder.getItem("volume") || 0);
};
/**
* Sets the current volume. If not muted, all sounds will have their volume
* updated.
*
* @param volume A Number in [0,1] to set as the current volume.
*/
AudioPlayr.prototype.setVolume = function (volume) {
var i;
if (!this.getMuted()) {
for (i in this.sounds) {
if (this.sounds.hasOwnProperty(i)) {
this.sounds[i].volume = Number(this.sounds[i].getAttribute("volumeReal")) * volume;
}
}
}
this.ItemsHolder.setItem("volume", volume.toString());
};
/**
* @returns Whether this is currently muted.
*/
AudioPlayr.prototype.getMuted = function () {
return Boolean(Number(this.ItemsHolder.getItem("muted")));
};
/**
* Calls either setMutedOn or setMutedOff as is appropriate.
*
* @param muted The new status for muted.
*/
AudioPlayr.prototype.setMuted = function (muted) {
this.getMuted() ? this.setMutedOn() : this.setMutedOff();
};
/**
* Calls either setMutedOn or setMutedOff to toggle whether this is muted.
*/
AudioPlayr.prototype.toggleMuted = function () {
this.setMuted(!this.getMuted());
};
/**
* Sets volume to 0 in all currently playing sounds and stores the muted
* status as on in the internal ItemsHoldr.
*/
AudioPlayr.prototype.setMutedOn = function () {
var i;
for (i in this.sounds) {
if (this.sounds.hasOwnProperty(i)) {
this.sounds[i].volume = 0;
}
}
this.ItemsHolder.setItem("muted", "1");
};
/**
* Sets sound volumes to their actual volumes and stores the muted status
* as off in the internal ItemsHoldr.
*/
AudioPlayr.prototype.setMutedOff = function () {
var volume = this.getVolume(), sound, i;
for (i in this.sounds) {
if (this.sounds.hasOwnProperty(i)) {
sound = this.sounds[i];
sound.volume = Number(sound.getAttribute("volumeReal")) * volume;
}
}
this.ItemsHolder.setItem("muted", "0");
};
/* Other modifiers
*/
/**
* @returns The Function or Number used as the volume setter for local sounds.
*/
AudioPlayr.prototype.getGetVolumeLocal = function () {
return this.getVolumeLocal;
};
/**
* @param getVolumeLocal A new Function or Number to use as the volume setter
* for local sounds.
*/
AudioPlayr.prototype.setGetVolumeLocal = function (getVolumeLocalNew) {
this.getVolumeLocal = getVolumeLocalNew;
};
/**
* @returns The Function or String used to get the default theme for playTheme.
*/
AudioPlayr.prototype.getGetThemeDefault = function () {
return this.getThemeDefault;
};
/**
* @param getThemeDefaultNew A new Function or String to use as the source for
* theme names in default playTheme calls.
*/
AudioPlayr.prototype.setGetThemeDefault = function (getThemeDefaultNew) {
this.getThemeDefault = getThemeDefaultNew;
};
/* Playback
*/
/**
* Plays the sound of the given name.
*
* @param name The name of the sound to play.
* @returns The sound's <audio> element, now playing.
* @remarks Internally, this stops any previously playing sound of that name
* and starts a new one, with volume set to the current volume and
* muted status. If the name wasn't previously being played (and
* therefore a new Element has been created), an event listener is
* added to delete it from sounds after.
*/
AudioPlayr.prototype.play = function (name) {
var sound, used;
// If the sound isn't yet being played, see if it's in the library
if (!this.sounds.hasOwnProperty(name)) {
// If the sound also isn't in the library, it's unknown
if (!this.library.hasOwnProperty(name)) {
throw new Error("Unknown name given to AudioPlayr.play: '" + name + "'.");
}
sound = this.sounds[name] = this.library[name];
}
else {
sound = this.sounds[name];
}
this.soundStop(sound);
if (this.getMuted()) {
sound.volume = 0;
}
else {
sound.setAttribute("volumeReal", "1");
sound.volume = this.getVolume();
}
this.playSound(sound);
used = Number(sound.getAttribute("used"));
// If this is the song's first play, let it know how to stop
if (!used) {
sound.setAttribute("used", String(used + 1));
sound.addEventListener("ended", this.soundFinish.bind(this, name));
}
sound.setAttribute("name", name);
return sound;
};
/**
* Pauses all currently playing sounds.
*/
AudioPlayr.prototype.pauseAll = function () {
var i;
for (i in this.sounds) {
if (this.sounds.hasOwnProperty(i)) {
this.pauseSound(this.sounds[i]);
}
}
};
/**
* Un-pauses (resumes) all currently paused sounds.
*/
AudioPlayr.prototype.resumeAll = function () {
var i;
for (i in this.sounds) {
if (!this.sounds.hasOwnProperty(i)) {
continue;
}
this.playSound(this.sounds[i]);
}
};
/**
* Pauses the currently playing theme, if there is one.
*/
AudioPlayr.prototype.pauseTheme = function () {
if (this.theme) {
this.pauseSound(this.theme);
}
};
/**
* Resumes the theme, if there is one and it's paused.
*/
AudioPlayr.prototype.resumeTheme = function () {
if (this.theme) {
this.playSound(this.theme);
}
};
/**
* Stops all sounds and any theme, and removes all references to them.
*/
AudioPlayr.prototype.clearAll = function () {
this.pauseAll();
this.clearTheme();
this.sounds = {};
};
/**
* Pauses and removes the theme, if there is one.
*/
AudioPlayr.prototype.clearTheme = function () {
if (!this.theme) {
return;
}
this.pauseTheme();
delete this.sounds[this.theme.getAttribute("name")];
this.theme = undefined;
this.themeName = undefined;
};
/**
* "Local" version of play that changes the output sound's volume depending
* on the result of a getVolumeLocal call.
*
* @param name The name of the sound to play.
* @param location An argument for getVolumeLocal, if that's a Function.
* @returns The sound's <audio> element, now playing.
*/
AudioPlayr.prototype.playLocal = function (name, location) {
if (location === void 0) { location = undefined; }
var sound = this.play(name), volumeReal;
switch (this.getVolumeLocal.constructor) {
case Function:
volumeReal = this.getVolumeLocal(location);
break;
case Number:
volumeReal = this.getVolumeLocal;
break;
default:
volumeReal = Number(this.getVolumeLocal) || 1;
break;
}
sound.setAttribute("volumeReal", String(volumeReal));
if (this.getMuted()) {
sound.volume = 0;
}
else {
sound.volume = volumeReal * this.getVolume();
}
return sound;
};
/**
* Pauses any previously playing theme and starts playback of a new theme.
*
* @param name The name of the sound to be used as the theme. If not
* provided, getThemeDefault is used to
* provide one.
* @param loop Whether the theme should always loop (by default, true).
* @returns The theme's <audio> element, now playing.
* @remarks This is different from normal sounds in that it normally loops
* and is controlled by pauseTheme and co. If loop is on and the
* sound wasn't already playing, an event listener is added for
* when it ends.
*/
AudioPlayr.prototype.playTheme = function (name, loop) {
if (name === void 0) { name = undefined; }
if (loop === void 0) { loop = undefined; }
this.pauseTheme();
// Loop defaults to true
loop = typeof loop !== "undefined" ? loop : true;
// If name isn't given, use the default getter
if (typeof (name) === "undefined") {
switch (this.getThemeDefault.constructor) {
case Function:
name = this.getThemeDefault();
break;
default:
name = this.getThemeDefault;
break;
}
}
// If a theme already exists, kill it
if (typeof this.theme !== "undefined" && this.theme.hasAttribute("name")) {
delete this.sounds[this.theme.getAttribute("name")];
}
this.themeName = name;
this.theme = this.sounds[name] = this.play(name);
this.theme.loop = loop;
// If it's used (no repeat), add the event listener to resume theme
if (this.theme.getAttribute("used") === "1") {
this.theme.addEventListener("ended", this.playTheme.bind(this));
}
return this.theme;
};
/**
* Wrapper around playTheme that plays a sound, then a theme. This is
* implemented using an event listener on the sound's ending.
*
* @param prefix The name of a sound to play before the theme.
* @param name The name of the sound to be used as the theme. If not
* provided, getThemeDefault is used to
* provide one.
* @param loop Whether the theme should always loop (by default, false).
* @returns The sound's <audio> element, now playing.
*/
AudioPlayr.prototype.playThemePrefixed = function (prefix, name, loop) {
var sound = this.play(prefix);
this.pauseTheme();
// If name isn't given, use the default getter
if (typeof (name) === "undefined") {
switch (this.getThemeDefault.constructor) {
case Function:
name = this.getThemeDefault();
break;
default:
name = this.getThemeDefault;
break;
}
}
this.addEventListener(prefix, "ended", this.playTheme.bind(this, prefix + " " + name, loop));
return sound;
};
/* Public utilities
*/
/**
* Adds an event listener to a currently playing sound. The sound will keep
* track of event listeners via an .addedEvents attribute, so they can be
* cancelled later.
*
* @param name The name of the sound.
* @param event The name of the event, such as "ended".
* @param callback The Function to be called by the event.
*/
AudioPlayr.prototype.addEventListener = function (name, event, callback) {
var sound = this.library[name];
if (!sound) {
throw new Error("Unknown name given to addEventListener: '" + name + "'.");
}
if (!sound.addedEvents) {
sound.addedEvents = {};
}
if (!sound.addedEvents[event]) {
sound.addedEvents[event] = [callback];
}
else {
sound.addedEvents[event].push(callback);
}
sound.addEventListener(event, callback);
};
/**
* Clears all events added by this.addEventListener to a sound under a given
* event.
*
* @param name The name of the sound.
* @param event The name of the event, such as "ended".
*/
AudioPlayr.prototype.removeEventListeners = function (name, event) {
var sound = this.library[name], events, i;
if (!sound) {
throw new Error("Unknown name given to removeEventListeners: '" + name + "'.");
}
if (!sound.addedEvents) {
return;
}
events = sound.addedEvents[event];
if (!events) {
return;
}
for (i = 0; i < events.length; i += 1) {
sound.removeEventListener(event, events[i]);
}
events.length = 0;
};
/**
* Adds an event listener to a sound. If the sound doesn't exist or has
* finished playing, it's called immediately.
*
* @param name The name of the sound.
* @param event The name of the event, such as "onended".
* @param callback The Function to be called by the event.
*/
AudioPlayr.prototype.addEventImmediate = function (name, event, callback) {
if (!this.sounds.hasOwnProperty(name) || this.sounds[name].paused) {
callback();
return;
}
this.sounds[name].addEventListener(event, callback);
};
/* Private utilities
*/
/**
* Called when a sound has completed to get it out of sounds.
*
* @param name The name of the sound that just finished.
*/
AudioPlayr.prototype.soundFinish = function (name) {
if (this.sounds.hasOwnProperty(name)) {
delete this.sounds[name];
}
};
/**
* Carefully stops a sound. HTMLAudioElement don't natively have a .stop()
* function, so this is the shim to do that.
*/
AudioPlayr.prototype.soundStop = function (sound) {
this.pauseSound(sound);
if (sound.readyState) {
sound.currentTime = 0;
}
};
/* Private loading / resetting
*/
/**
* Loads every sound defined in the library via AJAX. Sounds are loaded
* into <audio> elements via createAudio and stored in the library.
*/
AudioPlayr.prototype.generateLibraryFromSettings = function (librarySettings) {
var directory = {}, directorySoundNames, directoryName, name, j;
this.library = {};
this.directories = {};
// For each given directory (e.g. names, themes):
for (directoryName in librarySettings) {
if (!librarySettings.hasOwnProperty(directoryName)) {
continue;
}
directory = {};
directorySoundNames = librarySettings[directoryName];
// For each audio file to be loaded in that directory:
for (j = 0; j < directorySoundNames.length; j += 1) {
name = directorySoundNames[j];
// Create the sound and store it in the container
this.library[name] = directory[name] = this.createAudio(name, directoryName);
}
// The full directory is stored in the master directories
this.directories[directoryName] = directory;
}
};
/**
* Creates an audio element, gives it sources, and starts preloading.
*
* @param name The name of the sound to play.
* @param sectionName The name of the directory containing the sound.
* @returns An <audio> element ocntaining the sound, currently playing.
*/
AudioPlayr.prototype.createAudio = function (name, directory) {
var sound = document.createElement("audio"), sourceType, child, i;
// Create an audio source for each child
for (i = 0; i < this.fileTypes.length; i += 1) {
sourceType = this.fileTypes[i];
child = document.createElement("source");
child.type = "audio/" + sourceType;
child.src = this.directory + "/" + directory + "/" + sourceType + "/" + name + "." + sourceType;
sound.appendChild(child);
}
// This preloads the sound.
sound.volume = 0;
sound.setAttribute("volumeReal", "1");
sound.setAttribute("used", "0");
this.playSound(sound);
return sound;
};
/**
* Utility to try to play a sound, which may not be possible in headless
* environments like PhantomJS.
*
* @param sound An <audio> element to play.
* @returns Whether the sound was able to play.
*/
AudioPlayr.prototype.playSound = function (sound) {
if (sound && sound.play) {
sound.play();
return true;
}
return false;
};
/**
* Utility to try to pause a sound, which may not be possible in headless
* environments like PhantomJS.
*
* @param sound An <audio> element to pause.
* @returns Whether the sound was able to pause.
*/
AudioPlayr.prototype.pauseSound = function (sound) {
if (sound && sound.pause) {
sound.pause();
return true;
}
return false;
};
return AudioPlayr;
})();
AudioPlayr_1.AudioPlayr = AudioPlayr;
})(AudioPlayr || (AudioPlayr = {}));