audioplayr
Advanced tools
Comparing version
@@ -70,47 +70,50 @@ define(function() { return /******/ (function(modules) { // webpackBootstrap | ||
var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__, exports], __WEBPACK_AMD_DEFINE_RESULT__ = (function (require, exports) { | ||
var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
var __generator = (this && this.__generator) || function (thisArg, body) { | ||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; | ||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; | ||
function verb(n) { return function (v) { return step([n, v]); }; } | ||
function step(op) { | ||
if (f) throw new TypeError("Generator is already executing."); | ||
while (_) try { | ||
if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; | ||
if (y = 0, t) op = [0, t.value]; | ||
switch (op[0]) { | ||
case 0: case 1: t = op; break; | ||
case 4: _.label++; return { value: op[1], done: false }; | ||
case 5: _.label++; y = op[1]; op = [0]; continue; | ||
case 7: op = _.ops.pop(); _.trys.pop(); continue; | ||
default: | ||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } | ||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } | ||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } | ||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } | ||
if (t[2]) _.ops.pop(); | ||
_.trys.pop(); continue; | ||
} | ||
op = body.call(thisArg, _); | ||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } | ||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; | ||
} | ||
}; | ||
!(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__, exports, __webpack_require__(1), __webpack_require__(2)], __WEBPACK_AMD_DEFINE_RESULT__ = (function (require, exports, Sound_1, Storage_1) { | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
* Utility to try to play a sound, which may not be possible in headless | ||
* environments like PhantomJS. | ||
* Default name transform, which does nothing. | ||
* | ||
* @param sound An <audio> element to play. | ||
* @returns Whether the sound was able to play. | ||
* @param name Provided name to play. | ||
* @returns The same name. | ||
*/ | ||
var playSound = function (sound) { | ||
if (!sound || !sound.play) { | ||
return false; | ||
} | ||
// tslint:disable-next-line:no-floating-promises | ||
sound.play(); | ||
return true; | ||
}; | ||
var defaultNameTransform = function (name) { return name; }; | ||
/** | ||
* 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. | ||
* Playback for persistent and on-demand sounds and themes. | ||
*/ | ||
var pauseSound = function (sound) { | ||
if (!sound || !sound.pause) { | ||
return false; | ||
} | ||
sound.pause(); | ||
return true; | ||
}; | ||
/** | ||
* Carefully stops a sound. HTMLAudioElement don't natively have a .stop() | ||
* function, so this is the shim to do that. | ||
*/ | ||
var soundStop = function (sound) { | ||
pauseSound(sound); | ||
if (sound.readyState) { | ||
sound.currentTime = 0; | ||
} | ||
}; | ||
/** | ||
* An audio playback manager for persistent and on-demand themes and sounds. | ||
*/ | ||
var AudioPlayr = /** @class */ (function () { | ||
@@ -120,456 +123,447 @@ /** | ||
* | ||
* @param settings Settings to use for initialization. | ||
* @param settings Settings to be used for initialization. | ||
*/ | ||
function AudioPlayr(settings) { | ||
if (!settings.itemsHolder) { | ||
throw new Error("No ItemsHoldr given to AudioPlayr."); | ||
} | ||
this.itemsHolder = settings.itemsHolder; | ||
this.directory = settings.directory || "audio"; | ||
this.fileTypes = settings.fileTypes || ["mp3"]; | ||
this.getThemeDefault = settings.getThemeDefault || "Theme"; | ||
this.getVolumeLocal = settings.getVolumeLocal === undefined | ||
? 1 : settings.getVolumeLocal; | ||
/** | ||
* Created sounds, keyed by name. | ||
*/ | ||
this.sounds = {}; | ||
this.preloadLibraryFromSettings(settings.library || {}); | ||
var volumeInitial = this.itemsHolder.getItem("volume"); | ||
if (volumeInitial === undefined) { | ||
this.setVolume(1); | ||
} | ||
else { | ||
this.setVolume(volumeInitial); | ||
} | ||
this.setMuted(this.itemsHolder.getItem("muted") || false); | ||
this.createSound = settings.createSound || Sound_1.AudioElementSound.create; | ||
this.nameTransform = settings.nameTransform || defaultNameTransform; | ||
this.storage = settings.storage || new Storage_1.DefaultStorage(); | ||
} | ||
/** | ||
* @returns The listing of <audio> Elements, keyed by name. | ||
* Gets whether this is muted. | ||
* | ||
* @returns Whether this is muted. | ||
*/ | ||
AudioPlayr.prototype.getLibrary = function () { | ||
return this.library; | ||
AudioPlayr.prototype.getMuted = function () { | ||
var mutedRaw = this.storage.getItem(Storage_1.AudioSetting.Muted); | ||
return mutedRaw === undefined || mutedRaw === null | ||
? false | ||
: JSON.parse(mutedRaw); | ||
}; | ||
/** | ||
* @returns The allowed filetypes for audio files. | ||
* Gets the global sound volume in [0, 1]. | ||
* | ||
* @returns Global sound volume in [0, 1]. | ||
*/ | ||
AudioPlayr.prototype.getFileTypes = function () { | ||
return this.fileTypes; | ||
AudioPlayr.prototype.getVolume = function () { | ||
var volumeRaw = this.storage.getItem(Storage_1.AudioSetting.Volume); | ||
return volumeRaw === undefined || volumeRaw === null | ||
? 1 | ||
: JSON.parse(volumeRaw); | ||
}; | ||
/** | ||
* @returns The currently playing <audio> Elements, keyed by name. | ||
* Sets whether this is muted. | ||
* | ||
* @param muted Whether this is now muted. | ||
*/ | ||
AudioPlayr.prototype.getSounds = function () { | ||
return this.sounds; | ||
AudioPlayr.prototype.setMuted = function (muted) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var _this = this; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
this.storage.setItem(Storage_1.AudioSetting.Muted, JSON.stringify(muted)); | ||
return [4 /*yield*/, Promise.all(Object.keys(this.sounds) | ||
.map(function (name) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { | ||
return [2 /*return*/, this.sounds[name].setGlobalMuted(muted)]; | ||
}); }); }))]; | ||
case 1: | ||
_a.sent(); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
}; | ||
/** | ||
* @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; | ||
}; | ||
/** | ||
* @returns The current volume as a Number in [0,1], retrieved by the ItemsHoldr. | ||
*/ | ||
AudioPlayr.prototype.getVolume = function () { | ||
return parseFloat(this.itemsHolder.getItem("volume")) || 1; | ||
}; | ||
/** | ||
* Sets the current volume. If not muted, all sounds will have their volume | ||
* updated. | ||
* Sets the global sound volume in [0, 1]. | ||
* | ||
* @param volume A Number in [0,1] to set as the current volume. | ||
* @param volume New global sound volume in [0, 1]. | ||
*/ | ||
AudioPlayr.prototype.setVolume = function (volume) { | ||
if (!this.getMuted()) { | ||
for (var i in this.sounds) { | ||
this.sounds[i].volume = parseFloat(this.sounds[i].getAttribute("volumeReal") || "") * volume; | ||
} | ||
} | ||
this.itemsHolder.setItem("volume", volume.toString()); | ||
return __awaiter(this, void 0, void 0, function () { | ||
var _this = this; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
if (volume < 0 || volume > 1) { | ||
throw new Error("Volume must be within [0, 1]."); | ||
} | ||
this.storage.setItem(Storage_1.AudioSetting.Volume, volume); | ||
return [4 /*yield*/, Promise.all(Object.keys(this.sounds) | ||
.map(function (name) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { | ||
return [2 /*return*/, this.sounds[name].setGlobalVolume(volume)]; | ||
}); }); }))]; | ||
case 1: | ||
_a.sent(); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
}; | ||
/** | ||
* @returns Whether this is currently muted. | ||
* Plays a sound. | ||
* | ||
* @param name Name of a sound. | ||
* @param settings Any settings for the sound. | ||
* @returns A Promise for playing the sound. | ||
*/ | ||
AudioPlayr.prototype.getMuted = function () { | ||
return !!(parseFloat(this.itemsHolder.getItem("muted"))); | ||
AudioPlayr.prototype.play = function (name, settings) { | ||
if (settings === void 0) { settings = {}; } | ||
return __awaiter(this, void 0, void 0, function () { | ||
var alias, sound; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
name = this.nameTransform(name); | ||
alias = settings.alias === undefined | ||
? name | ||
: this.nameTransform(settings.alias); | ||
if (!{}.hasOwnProperty.call(this.sounds, alias)) return [3 /*break*/, 2]; | ||
return [4 /*yield*/, this.sounds[alias].stop()]; | ||
case 1: | ||
_a.sent(); | ||
_a.label = 2; | ||
case 2: | ||
sound = this.sounds[alias] = this.createSound(name, { | ||
globalMuted: this.getMuted(), | ||
globalVolume: this.getVolume(), | ||
localMuted: settings.muted === undefined | ||
? false | ||
: settings.muted, | ||
localVolume: settings.volume === undefined | ||
? 1 | ||
: settings.volume, | ||
loop: settings.loop === undefined | ||
? false | ||
: settings.loop, | ||
}); | ||
return [2 /*return*/, sound.play()]; | ||
} | ||
}); | ||
}); | ||
}; | ||
/** | ||
* Calls either setMutedOn or setMutedOff as is appropriate. | ||
* Pauses all sounds. | ||
* | ||
* @param muted The new status for muted. | ||
* @returns A Promise for pausing all sounds. | ||
*/ | ||
AudioPlayr.prototype.setMuted = function (muted) { | ||
if (muted) { | ||
this.setMutedOn(); | ||
} | ||
else { | ||
this.setMutedOff(); | ||
} | ||
AudioPlayr.prototype.pauseAll = function () { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var _this = this; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, Promise.all(Object.keys(this.sounds) | ||
.map(function (name) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { | ||
return [2 /*return*/, this.sounds[name].pause()]; | ||
}); }); }))]; | ||
case 1: | ||
_a.sent(); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
}; | ||
/** | ||
* Calls either setMutedOn or setMutedOff to toggle whether this is muted. | ||
* Un-pauses (plays) all sounds. | ||
* | ||
* @returns A Promise for resuming. | ||
*/ | ||
AudioPlayr.prototype.toggleMuted = function () { | ||
this.setMuted(!this.getMuted()); | ||
AudioPlayr.prototype.resumeAll = function () { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var _this = this; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, Promise.all(Object.keys(this.sounds) | ||
.map(function (name) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { | ||
return [2 /*return*/, this.sounds[name].play()]; | ||
}); }); }))]; | ||
case 1: | ||
_a.sent(); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
}; | ||
/** | ||
* Sets volume to 0 in all currently playing sounds and stores the muted | ||
* status as on in the internal ItemsHoldr. | ||
* Stops all sounds. | ||
* | ||
* @returns A Promise for stopping. | ||
*/ | ||
AudioPlayr.prototype.setMutedOn = function () { | ||
for (var i in this.sounds) { | ||
if (this.sounds.hasOwnProperty(i)) { | ||
this.sounds[i].volume = 0; | ||
} | ||
} | ||
this.itemsHolder.setItem("muted", "1"); | ||
AudioPlayr.prototype.stopAll = function () { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var _this = this; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, Promise.all(Object.keys(this.sounds) | ||
.map(function (name) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { | ||
return [2 /*return*/, this.sounds[name].stop()]; | ||
}); }); }))]; | ||
case 1: | ||
_a.sent(); | ||
this.sounds = {}; | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
}; | ||
/** | ||
* 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(); | ||
for (var i in this.sounds) { | ||
if (this.sounds.hasOwnProperty(i)) { | ||
var sound = this.sounds[i]; | ||
sound.volume = parseFloat(sound.getAttribute("volumeReal") || "") * volume; | ||
} | ||
} | ||
this.itemsHolder.setItem("muted", "0"); | ||
}; | ||
/** | ||
* @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; | ||
}; | ||
/** | ||
* Plays the sound of the given name. | ||
* Checks whether a sound under the alias exists. | ||
* | ||
* @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. | ||
* @param alias Alias to check under. | ||
* @param name Name the sound must have, if not the same as the name. | ||
* @returns Whether a sound exists under the alias. | ||
*/ | ||
AudioPlayr.prototype.play = function (name) { | ||
var sound; | ||
if (!this.sounds[name]) { | ||
if (!this.library[name]) { | ||
throw new Error("Unknown name given to AudioPlayr.play: '" + name + "'."); | ||
} | ||
sound = this.sounds[name] = this.library[name]; | ||
AudioPlayr.prototype.hasSound = function (alias, name) { | ||
alias = this.nameTransform(alias); | ||
if (!{}.hasOwnProperty.call(this.sounds, alias)) { | ||
return false; | ||
} | ||
else { | ||
sound = this.sounds[name]; | ||
} | ||
soundStop(sound); | ||
if (this.getMuted()) { | ||
sound.volume = 0; | ||
} | ||
else { | ||
sound.setAttribute("volumeReal", "1"); | ||
sound.volume = this.getVolume(); | ||
} | ||
// This gives enough time after a call to pause so a Promise exception | ||
// Does not occur during the buffering for play. | ||
setTimeout(playSound.bind(this), 1, sound); | ||
var used = parseFloat(sound.getAttribute("used") || ""); | ||
// If this is the song's first play, let it know how to stop | ||
if (!used) { | ||
sound.setAttribute("used", (used + 1).toString()); | ||
sound.addEventListener("ended", this.soundFinish.bind(this, name)); | ||
} | ||
sound.setAttribute("name", name); | ||
return sound; | ||
return name === undefined || this.sounds[alias].name === this.nameTransform(name); | ||
}; | ||
/** | ||
* Pauses all currently playing sounds. | ||
*/ | ||
AudioPlayr.prototype.pauseAll = function () { | ||
for (var i in this.sounds) { | ||
if (this.sounds.hasOwnProperty(i)) { | ||
pauseSound(this.sounds[i]); | ||
} | ||
return AudioPlayr; | ||
}()); | ||
exports.AudioPlayr = AudioPlayr; | ||
}).apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), | ||
__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); | ||
/***/ }), | ||
/* 1 */ | ||
/***/ (function(module, exports, __webpack_require__) { | ||
var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;var __assign = (this && this.__assign) || Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
var __generator = (this && this.__generator) || function (thisArg, body) { | ||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; | ||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; | ||
function verb(n) { return function (v) { return step([n, v]); }; } | ||
function step(op) { | ||
if (f) throw new TypeError("Generator is already executing."); | ||
while (_) try { | ||
if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; | ||
if (y = 0, t) op = [0, t.value]; | ||
switch (op[0]) { | ||
case 0: case 1: t = op; break; | ||
case 4: _.label++; return { value: op[1], done: false }; | ||
case 5: _.label++; y = op[1]; op = [0]; continue; | ||
case 7: op = _.ops.pop(); _.trys.pop(); continue; | ||
default: | ||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } | ||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } | ||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } | ||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } | ||
if (t[2]) _.ops.pop(); | ||
_.trys.pop(); continue; | ||
} | ||
}; | ||
op = body.call(thisArg, _); | ||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } | ||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; | ||
} | ||
}; | ||
!(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__, exports], __WEBPACK_AMD_DEFINE_RESULT__ = (function (require, exports) { | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
* Default sound settings for all sounds. | ||
*/ | ||
var defaultSoundSettings = { | ||
globalMuted: false, | ||
globalVolume: 1, | ||
localMuted: false, | ||
localVolume: 1, | ||
loop: false, | ||
}; | ||
/** | ||
* Wraps an <audio> element. | ||
*/ | ||
var AudioElementSound = /** @class */ (function () { | ||
/** | ||
* Un-pauses (resumes) all currently paused sounds. | ||
* Initializes a new instance of the AudioElementSound class. | ||
* | ||
* @param name Name of the sound. | ||
* @param settings Any settings for the sound. | ||
*/ | ||
AudioPlayr.prototype.resumeAll = function () { | ||
for (var i in this.sounds) { | ||
if (!this.sounds.hasOwnProperty(i)) { | ||
continue; | ||
} | ||
playSound(this.sounds[i]); | ||
} | ||
}; | ||
function AudioElementSound(name, settings) { | ||
this.name = name; | ||
this.settings = __assign({}, defaultSoundSettings, settings); | ||
this.element = document.createElement("audio"); | ||
this.element.src = name; | ||
this.element.loop = this.settings.loop; | ||
this.element.muted = this.settings.globalMuted || this.settings.localMuted; | ||
this.element.volume = this.settings.globalVolume * this.settings.localVolume; | ||
} | ||
/** | ||
* Pauses the currently playing theme, if there is one. | ||
* Pauses the sound. | ||
* | ||
* @returns A Promise for pausing the sound. | ||
*/ | ||
AudioPlayr.prototype.pauseTheme = function () { | ||
if (this.theme) { | ||
pauseSound(this.theme); | ||
} | ||
AudioElementSound.prototype.pause = function () { | ||
return __awaiter(this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
this.element.pause(); | ||
return [2 /*return*/]; | ||
}); | ||
}); | ||
}; | ||
/** | ||
* Resumes the theme, if there is one and it's paused. | ||
* Plays the sound. | ||
* | ||
* @returns A Promise for playing the sound. | ||
*/ | ||
AudioPlayr.prototype.resumeTheme = function () { | ||
if (this.theme) { | ||
playSound(this.theme); | ||
} | ||
AudioElementSound.prototype.play = function () { | ||
return __awaiter(this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
return [2 /*return*/, this.element.play()]; | ||
}); | ||
}); | ||
}; | ||
/** | ||
* Stops all sounds and any theme, and removes all references to them. | ||
* Sets whether this should loop. | ||
* | ||
* @param loop Whether to loop. | ||
* @returns A Promise for setting whether this should loop. | ||
*/ | ||
AudioPlayr.prototype.clearAll = function () { | ||
this.pauseAll(); | ||
this.clearTheme(); | ||
this.sounds = {}; | ||
AudioElementSound.prototype.setLoop = function (loop) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
this.element.loop = loop; | ||
return [2 /*return*/]; | ||
}); | ||
}); | ||
}; | ||
/** | ||
* 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. | ||
* Sets whether this is muted. | ||
* | ||
* @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. | ||
* @param muted Whether this is muted. | ||
* @returns A Promise for setting whether this is muted. | ||
*/ | ||
AudioPlayr.prototype.playLocal = function (name, location) { | ||
var sound = this.play(name); | ||
var volumeReal = this.getVolumeLocal instanceof Function | ||
? this.getVolumeLocal(location) | ||
: this.getVolumeLocal; | ||
sound.setAttribute("volumeReal", volumeReal.toString()); | ||
sound.volume = this.getMuted() | ||
? 0 | ||
: sound.volume = volumeReal * this.getVolume(); | ||
return sound; | ||
AudioElementSound.prototype.setGlobalMuted = function (muted) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
this.settings.globalMuted = muted; | ||
this.element.muted = this.settings.globalMuted || this.settings.localMuted; | ||
return [2 /*return*/]; | ||
}); | ||
}); | ||
}; | ||
/** | ||
* Pauses any previously playing theme and starts playback of a new theme. | ||
* Sets a new global volume in [0, 1]. | ||
* | ||
* @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. | ||
* @param volume New volume in [0, 1].. | ||
* @returns A Promise for setting a new volume. | ||
*/ | ||
AudioPlayr.prototype.playTheme = function (name, loop) { | ||
if (loop === void 0) { loop = true; } | ||
this.pauseTheme(); | ||
if (typeof name === "undefined") { | ||
// tslint:disable-next-line:no-parameter-reassignment | ||
name = this.getThemeDefault instanceof Function | ||
? this.getThemeDefault() | ||
: this.getThemeDefault; | ||
} | ||
// 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; | ||
AudioElementSound.prototype.setGlobalVolume = function (volume) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
this.settings.globalVolume = volume; | ||
this.element.volume = this.settings.globalVolume * this.settings.localVolume; | ||
return [2 /*return*/]; | ||
}); | ||
}); | ||
}; | ||
/** | ||
* Wrapper around playTheme that plays a sound, then a theme. This is | ||
* implemented using an event listener on the sound's ending. | ||
* Plays the sound. | ||
* | ||
* @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. | ||
* @returns A Promise for playing the sound. | ||
*/ | ||
AudioPlayr.prototype.playThemePrefixed = function (prefix, name, loop) { | ||
var sound = this.play(prefix); | ||
this.pauseTheme(); | ||
if (typeof name === "undefined") { | ||
// tslint:disable-next-line:no-parameter-reassignment | ||
name = this.getThemeDefault instanceof Function | ||
? this.getThemeDefault() | ||
: this.getThemeDefault; | ||
} | ||
this.addEventListener(prefix, "ended", this.playTheme.bind(this, name, loop)); | ||
return sound; | ||
AudioElementSound.prototype.stop = function () { | ||
return __awaiter(this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, this.pause()]; | ||
case 1: | ||
_a.sent(); | ||
this.element.currentTime = 0; | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
}; | ||
/** | ||
* 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. | ||
* Creates a new AudioElementSound. | ||
* | ||
* @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. | ||
* @param name Name of the sound. | ||
* @param settings Any settings for the sound. | ||
* @returns A new sound. | ||
*/ | ||
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); | ||
AudioElementSound.create = function (name, settings) { | ||
if (settings === void 0) { settings = {}; } | ||
return new AudioElementSound(name, settings); | ||
}; | ||
return AudioElementSound; | ||
}()); | ||
exports.AudioElementSound = AudioElementSound; | ||
}).apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), | ||
__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); | ||
/***/ }), | ||
/* 2 */ | ||
/***/ (function(module, exports, __webpack_require__) { | ||
var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__, exports], __WEBPACK_AMD_DEFINE_RESULT__ = (function (require, exports) { | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
* Keys for stored audio settings. | ||
*/ | ||
var AudioSetting; | ||
(function (AudioSetting) { | ||
/** | ||
* 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". | ||
* Key for whether sounds are muted. | ||
*/ | ||
AudioPlayr.prototype.removeEventListeners = function (name, event) { | ||
var sound = this.library[name]; | ||
if (!sound) { | ||
throw new Error("Unknown name given to removeEventListeners: '" + name + "'."); | ||
} | ||
if (!sound.addedEvents) { | ||
return; | ||
} | ||
var addedEvents = sound.addedEvents[event]; | ||
if (!addedEvents) { | ||
return; | ||
} | ||
for (var _i = 0, addedEvents_1 = addedEvents; _i < addedEvents_1.length; _i++) { | ||
var addedEvent = addedEvents_1[_i]; | ||
sound.removeEventListener(event, addedEvent); | ||
} | ||
addedEvents.length = 0; | ||
}; | ||
AudioSetting["Muted"] = "muted"; | ||
/** | ||
* 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. | ||
* Key for global sound volume. | ||
*/ | ||
AudioPlayr.prototype.addEventImmediate = function (name, event, callback) { | ||
if (!this.sounds.hasOwnProperty(name) || this.sounds[name].paused) { | ||
callback(); | ||
return; | ||
} | ||
this.sounds[name].addEventListener(event, callback); | ||
}; | ||
AudioSetting["Volume"] = "volume"; | ||
})(AudioSetting = exports.AudioSetting || (exports.AudioSetting = {})); | ||
/** | ||
* Default, transient storage. | ||
*/ | ||
var DefaultStorage = /** @class */ (function () { | ||
function DefaultStorage() { | ||
/** | ||
* Stored values. | ||
*/ | ||
this.values = {}; | ||
} | ||
/** | ||
* Called when a sound has completed to get it out of sounds. | ||
* Gets a stored value. | ||
* | ||
* @param name The name of the sound that just finished. | ||
* @param name Name of the value. | ||
* @returns Stored value, if it exists. | ||
*/ | ||
AudioPlayr.prototype.soundFinish = function (name) { | ||
if (this.sounds.hasOwnProperty(name)) { | ||
delete this.sounds[name]; | ||
} | ||
DefaultStorage.prototype.getItem = function (name) { | ||
return this.values[name]; | ||
}; | ||
/** | ||
* Loads every sound defined in the library via AJAX. Sounds are loaded | ||
* into <audio> elements via createAudio and stored in the library. | ||
*/ | ||
AudioPlayr.prototype.preloadLibraryFromSettings = function (librarySettings) { | ||
this.library = {}; | ||
this.directories = {}; | ||
for (var directoryName in librarySettings) { | ||
if (!librarySettings.hasOwnProperty(directoryName)) { | ||
continue; | ||
} | ||
var directory = {}; | ||
var directorySoundNames = librarySettings[directoryName]; | ||
for (var _i = 0, directorySoundNames_1 = directorySoundNames; _i < directorySoundNames_1.length; _i++) { | ||
var name_1 = directorySoundNames_1[_i]; | ||
this.library[name_1] = directory[name_1] = this.createAudio(name_1, directoryName); | ||
} | ||
this.directories[directoryName] = directory; | ||
} | ||
}; | ||
/** | ||
* Creates an audio element, gives it sources, and starts preloading. | ||
* Sets a stored value. | ||
* | ||
* @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. | ||
* @param name Name of the value. | ||
* @param value New value under the name. | ||
*/ | ||
AudioPlayr.prototype.createAudio = function (name, directory) { | ||
var sound = document.createElement("audio"); | ||
// Create an audio source for each child | ||
for (var _i = 0, _a = this.fileTypes; _i < _a.length; _i++) { | ||
var fileType = _a[_i]; | ||
var child = document.createElement("source"); | ||
child.type = "audio/" + fileType; | ||
child.src = this.directory + "/" + directory + "/" + fileType + "/" + name + "." + fileType; | ||
sound.appendChild(child); | ||
} | ||
// This preloads the sound. | ||
sound.volume = 0; | ||
sound.setAttribute("volumeReal", "1"); | ||
sound.setAttribute("used", "0"); | ||
playSound(sound); | ||
return sound; | ||
DefaultStorage.prototype.setItem = function (name, value) { | ||
this.values[name] = value; | ||
}; | ||
return AudioPlayr; | ||
return DefaultStorage; | ||
}()); | ||
exports.AudioPlayr = AudioPlayr; | ||
exports.DefaultStorage = DefaultStorage; | ||
}).apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), | ||
@@ -576,0 +570,0 @@ __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); |
@@ -10,9 +10,7 @@ { | ||
}, | ||
"dependencies": { | ||
"itemsholdr": "^0.7.1" | ||
}, | ||
"dependencies": {}, | ||
"description": "An audio playback manager for persistent and on-demand themes and sounds.", | ||
"devDependencies": { | ||
"@types/chai": "^4.0.4", | ||
"@types/lolex": "^1.5.32", | ||
"@types/lolex": "^2.1.1", | ||
"@types/mocha": "^2.2.44", | ||
@@ -72,3 +70,3 @@ "@types/sinon": "^4.0.0", | ||
"types": "./src/index.d.ts", | ||
"version": "0.7.1" | ||
"version": "0.7.2" | ||
} |
169
README.md
<!-- {{Top}} --> | ||
# AudioPlayr | ||
[](https://greenkeeper.io/) | ||
[](https://travis-ci.org/FullScreenShenanigans/AudioPlayr) | ||
@@ -9,2 +11,169 @@ [](http://badge.fury.io/js/audioplayr) | ||
## Usage | ||
### Constructor | ||
```typescript | ||
import { AudioPlayr } from "audioplayr"; | ||
const audioPlayer = new AudioPlayr(); | ||
audioPlayer.play("Hello world.mp3"); | ||
``` | ||
#### `nameTransform` | ||
By default, provided names are treated as the `src` file names for their sounds. | ||
You can define a mapping of names to file names by providing a `nameTransform` method. | ||
It should take in a `string` and return a `string`. | ||
```typescript | ||
const audioPlayer = new AudioPlayr({ | ||
nameTransform: (name: string) => `Sounds/${name}.mp3`, | ||
}); | ||
// Plays "Sounds/Hello world.mp3" | ||
audioPlayer.play("Hello world"); | ||
``` | ||
Internally, all sound names will be transformed with the `nameTransform`. | ||
#### `storage` | ||
By default, mute and volume settings aren't kept from state to state. | ||
You can pass a `storage` parameter to an `AudioPlayr` to keep them locally. | ||
It should be an object with `getItem(name: string): string` and `setItem(name: string, value: string)` members, such as `localStorage`. | ||
```typescript | ||
const audioPlayer = new AudioPlayr({ | ||
storage: localStorage, | ||
}); | ||
``` | ||
Keys that may be stored are: | ||
* `"muted"`: Whether sounds are muted. | ||
* `"volume"`: Global sound volume. | ||
See [`Storage.ts`](./src/Storage.ts) for the `AudioSetting` enum and `IAudioSettingsStorage` interface. | ||
### `play` | ||
Parameters: | ||
* `name: string`: Name of the audio file, used as its `src`. | ||
* `settings: Object` _(optional)_: Any additional options. | ||
Returns: `Promise<void>` for _starting_ playback. | ||
```typescript | ||
audioPlayer.play("beep.mp3"); | ||
``` | ||
If the same audio name is played twice, the first will be stopped before the second starts. | ||
```typescript | ||
audioPlayer.play("beep.mp3"); | ||
// ... | ||
audioPlayer.play("beep.mp3"); | ||
``` | ||
`settings` may contain any of the following keys: | ||
* `alias`: | ||
Changes what name the sound will be stored under. | ||
Defaults to the given name. | ||
As with `play`, if two sounds with the same `alias` are played, the first will be stopped before the second starts. | ||
```typescript | ||
audioPlayer.play("Overworld.mp3", { alias: "Theme" }); | ||
// ... | ||
audioPlayer.play("Underworld.mp3", { alias: "Theme" }); | ||
``` | ||
If a `nameTransform` was provided, it's applied to this alias as well. | ||
* `loop`: | ||
Whether the sound should loop continuously. | ||
Defaults to `false`. | ||
```typescript | ||
audioPlayer.play("Bloop.mp3", { loop: true }); | ||
``` | ||
* `muted`: | ||
Whether the sound should be muted. | ||
Defaults to `false`. | ||
```typescript | ||
audioPlayer.play("Boop.mp3", { muted: true }); | ||
``` | ||
If the `AudioPlayr` is globally muted, `muted: false` will be ignored. | ||
* `volume`: | ||
Volume as a number in `[0, 1]`. | ||
Defaults to `1`. | ||
```typescript | ||
audioPlayer.play("Bop.mp3", { volume: 0.5 }); | ||
``` | ||
The sound's playing volume is computed as this times the global volume. | ||
### `getMuted` | ||
Returns: `boolean` for whether all sounds are muted. | ||
### `getVolume` | ||
Returns: `number` in `[0, 1]` for global sound volume. | ||
### `setMuted` | ||
Parameters: | ||
* `muted: boolean`: Whether this all sounds are globally muted. | ||
Returns: `Promise<void>` for setting whether all sounds are globally muted. | ||
### `setVolume` | ||
Parameters: | ||
* `volume: number`: `number` in `[0, 1]` for global sound volume. | ||
Returns: `Promise<void>` for setting the global sound volume. | ||
### `pauseAll` | ||
Returns: `Promise<void>` for pausing all sounds. | ||
Pauses all sounds in parallel. | ||
This only affects sounds that are playing. | ||
### `resumeAll` | ||
Returns: `Promise<void>` for resuming all sounds. | ||
Resumes all sounds in parallel. | ||
This only affects sounds that are paused. | ||
### `stopAll` | ||
Returns: `Promise<void>` for stopping all sounds. | ||
Stops all sounds. | ||
Any individual sound settings are cleared. | ||
## `hasSound` | ||
Parameters: | ||
* `alias: string`: Alias to check under. | ||
* `name: string` _(optional)_: Name the sound must have, if not the same as `alias`. | ||
Returns: `boolean` for whether a sound exists under the alias. | ||
<!-- {{Development}} --> | ||
@@ -11,0 +180,0 @@ ## Development |
@@ -1,246 +0,86 @@ | ||
import { IAudioPlayr, IAudioPlayrSettings, ICreatedSound, IGetThemeDefault, IGetVolumeLocal } from "./IAudioPlayr"; | ||
import { IAudioPlayr, IAudioPlayrSettings, IPlaySettings } from "./IAudioPlayr"; | ||
/** | ||
* An audio playback manager for persistent and on-demand themes and sounds. | ||
* Playback for persistent and on-demand sounds and themes. | ||
*/ | ||
export declare class AudioPlayr implements IAudioPlayr { | ||
/** | ||
* What file types to add as sources to sounds. | ||
* Creates a new sound. | ||
*/ | ||
private readonly fileTypes; | ||
private readonly createSound; | ||
/** | ||
* Directory from which audio files are AJAXed upon startup. | ||
* Transforms a sound name into a file name. | ||
*/ | ||
private readonly directory; | ||
private readonly nameTransform; | ||
/** | ||
* Storage container for settings like volume and muted status. | ||
* Created sounds, keyed by name. | ||
*/ | ||
private readonly itemsHolder; | ||
/** | ||
* HTMLAudioElements keyed by their name. | ||
*/ | ||
private library; | ||
/** | ||
* Directories mapping folder names to sound libraries. | ||
*/ | ||
private directories; | ||
/** | ||
* Currently playing sound objects, keyed by name (excluding extensions). | ||
*/ | ||
private sounds; | ||
/** | ||
* The currently playing theme. | ||
* Stores mute and volume status locally. | ||
*/ | ||
private theme?; | ||
private readonly storage; | ||
/** | ||
* The name of the currently playing theme. | ||
*/ | ||
private themeName?; | ||
/** | ||
* The Function or Number used to determine what playLocal's volume is. | ||
*/ | ||
private getVolumeLocal; | ||
/** | ||
* The Function or String used to get a default theme name. | ||
*/ | ||
private getThemeDefault; | ||
/** | ||
* Initializes a new instance of the AudioPlayr class. | ||
* | ||
* @param settings Settings to use for initialization. | ||
* @param settings Settings to be used for initialization. | ||
*/ | ||
constructor(settings: IAudioPlayrSettings); | ||
/** | ||
* @returns The listing of <audio> Elements, keyed by name. | ||
*/ | ||
getLibrary(): {}; | ||
/** | ||
* @returns The allowed filetypes for audio files. | ||
*/ | ||
getFileTypes(): string[]; | ||
/** | ||
* @returns The currently playing <audio> Elements, keyed by name. | ||
*/ | ||
getSounds(): {}; | ||
/** | ||
* @returns The current playing theme's <audio> Element. | ||
*/ | ||
getTheme(): HTMLAudioElement | undefined; | ||
/** | ||
* @returns The name of the currently playing theme. | ||
*/ | ||
getThemeName(): string | undefined; | ||
/** | ||
* @returns The directory under which all filetype directories are to be located. | ||
*/ | ||
getDirectory(): string; | ||
/** | ||
* @returns The current volume as a Number in [0,1], retrieved by the ItemsHoldr. | ||
*/ | ||
getVolume(): number; | ||
/** | ||
* Sets the current volume. If not muted, all sounds will have their volume | ||
* updated. | ||
* Gets whether this is muted. | ||
* | ||
* @param volume A Number in [0,1] to set as the current volume. | ||
* @returns Whether this is muted. | ||
*/ | ||
setVolume(volume: number): void; | ||
/** | ||
* @returns Whether this is currently muted. | ||
*/ | ||
getMuted(): boolean; | ||
/** | ||
* Calls either setMutedOn or setMutedOff as is appropriate. | ||
* Gets the global sound volume in [0, 1]. | ||
* | ||
* @param muted The new status for muted. | ||
* @returns Global sound volume in [0, 1]. | ||
*/ | ||
setMuted(muted: boolean): void; | ||
getVolume(): number; | ||
/** | ||
* Calls either setMutedOn or setMutedOff to toggle whether this is muted. | ||
*/ | ||
toggleMuted(): void; | ||
/** | ||
* Sets volume to 0 in all currently playing sounds and stores the muted | ||
* status as on in the internal ItemsHoldr. | ||
*/ | ||
setMutedOn(): void; | ||
/** | ||
* Sets sound volumes to their actual volumes and stores the muted status | ||
* as off in the internal ItemsHoldr. | ||
*/ | ||
setMutedOff(): void; | ||
/** | ||
* @returns The Function or Number used as the volume setter for local sounds. | ||
*/ | ||
getGetVolumeLocal(): number | IGetVolumeLocal; | ||
/** | ||
* @param getVolumeLocal A new Function or Number to use as the volume setter | ||
* for local sounds. | ||
*/ | ||
setGetVolumeLocal(getVolumeLocalNew: number | IGetVolumeLocal): void; | ||
/** | ||
* @returns The Function or String used to get the default theme for playTheme. | ||
*/ | ||
getGetThemeDefault(): string | IGetThemeDefault; | ||
/** | ||
* @param getThemeDefaultNew A new Function or String to use as the source for | ||
* theme names in default playTheme calls. | ||
*/ | ||
setGetThemeDefault(getThemeDefaultNew: string | IGetThemeDefault): void; | ||
/** | ||
* Plays the sound of the given name. | ||
* Sets whether this is muted. | ||
* | ||
* @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. | ||
* @param muted Whether this is now muted. | ||
*/ | ||
play(name: string): ICreatedSound; | ||
setMuted(muted: boolean): Promise<void>; | ||
/** | ||
* Pauses all currently playing sounds. | ||
*/ | ||
pauseAll(): void; | ||
/** | ||
* Un-pauses (resumes) all currently paused sounds. | ||
*/ | ||
resumeAll(): void; | ||
/** | ||
* Pauses the currently playing theme, if there is one. | ||
*/ | ||
pauseTheme(): void; | ||
/** | ||
* Resumes the theme, if there is one and it's paused. | ||
*/ | ||
resumeTheme(): void; | ||
/** | ||
* Stops all sounds and any theme, and removes all references to them. | ||
*/ | ||
clearAll(): void; | ||
/** | ||
* Pauses and removes the theme, if there is one. | ||
*/ | ||
clearTheme(): void; | ||
/** | ||
* "Local" version of play that changes the output sound's volume depending | ||
* on the result of a getVolumeLocal call. | ||
* Sets the global sound volume in [0, 1]. | ||
* | ||
* @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. | ||
* @param volume New global sound volume in [0, 1]. | ||
*/ | ||
playLocal(name: string, location?: {}): HTMLAudioElement; | ||
setVolume(volume: number): Promise<void>; | ||
/** | ||
* Pauses any previously playing theme and starts playback of a new theme. | ||
* Plays a sound. | ||
* | ||
* @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. | ||
* @param name Name of a sound. | ||
* @param settings Any settings for the sound. | ||
* @returns A Promise for playing the sound. | ||
*/ | ||
playTheme(name?: string, loop?: boolean): ICreatedSound; | ||
play(name: string, settings?: Partial<IPlaySettings>): Promise<void>; | ||
/** | ||
* Wrapper around playTheme that plays a sound, then a theme. This is | ||
* implemented using an event listener on the sound's ending. | ||
* Pauses all sounds. | ||
* | ||
* @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. | ||
* @returns A Promise for pausing all sounds. | ||
*/ | ||
playThemePrefixed(prefix: string, name?: string, loop?: boolean): HTMLAudioElement; | ||
pauseAll(): Promise<void>; | ||
/** | ||
* 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. | ||
* Un-pauses (plays) all sounds. | ||
* | ||
* @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. | ||
* @returns A Promise for resuming. | ||
*/ | ||
addEventListener(name: string, event: string, callback: EventListenerOrEventListenerObject): void; | ||
resumeAll(): Promise<void>; | ||
/** | ||
* Clears all events added by this.addEventListener to a sound under a given | ||
* event. | ||
* Stops all sounds. | ||
* | ||
* @param name The name of the sound. | ||
* @param event The name of the event, such as "ended". | ||
* @returns A Promise for stopping. | ||
*/ | ||
removeEventListeners(name: string, event: string): void; | ||
stopAll(): Promise<void>; | ||
/** | ||
* Adds an event listener to a sound. If the sound doesn't exist or has | ||
* finished playing, it's called immediately. | ||
* Checks whether a sound under the alias exists. | ||
* | ||
* @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. | ||
* @param alias Alias to check under. | ||
* @param name Name the sound must have, if not the same as the name. | ||
* @returns Whether a sound exists under the alias. | ||
*/ | ||
addEventImmediate(name: string, event: string, callback: Function): void; | ||
/** | ||
* Called when a sound has completed to get it out of sounds. | ||
* | ||
* @param name The name of the sound that just finished. | ||
*/ | ||
private soundFinish(name); | ||
/** | ||
* Loads every sound defined in the library via AJAX. Sounds are loaded | ||
* into <audio> elements via createAudio and stored in the library. | ||
*/ | ||
private preloadLibraryFromSettings(librarySettings); | ||
/** | ||
* 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. | ||
*/ | ||
private createAudio(name, directory); | ||
hasSound(alias: string, name?: string): boolean; | ||
} |
@@ -1,46 +0,49 @@ | ||
define(["require", "exports"], function (require, exports) { | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
var __generator = (this && this.__generator) || function (thisArg, body) { | ||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; | ||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; | ||
function verb(n) { return function (v) { return step([n, v]); }; } | ||
function step(op) { | ||
if (f) throw new TypeError("Generator is already executing."); | ||
while (_) try { | ||
if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; | ||
if (y = 0, t) op = [0, t.value]; | ||
switch (op[0]) { | ||
case 0: case 1: t = op; break; | ||
case 4: _.label++; return { value: op[1], done: false }; | ||
case 5: _.label++; y = op[1]; op = [0]; continue; | ||
case 7: op = _.ops.pop(); _.trys.pop(); continue; | ||
default: | ||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } | ||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } | ||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } | ||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } | ||
if (t[2]) _.ops.pop(); | ||
_.trys.pop(); continue; | ||
} | ||
op = body.call(thisArg, _); | ||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } | ||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; | ||
} | ||
}; | ||
define(["require", "exports", "./Sound", "./Storage"], function (require, exports, Sound_1, Storage_1) { | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
* Utility to try to play a sound, which may not be possible in headless | ||
* environments like PhantomJS. | ||
* Default name transform, which does nothing. | ||
* | ||
* @param sound An <audio> element to play. | ||
* @returns Whether the sound was able to play. | ||
* @param name Provided name to play. | ||
* @returns The same name. | ||
*/ | ||
var playSound = function (sound) { | ||
if (!sound || !sound.play) { | ||
return false; | ||
} | ||
// tslint:disable-next-line:no-floating-promises | ||
sound.play(); | ||
return true; | ||
}; | ||
var defaultNameTransform = function (name) { return name; }; | ||
/** | ||
* 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. | ||
* Playback for persistent and on-demand sounds and themes. | ||
*/ | ||
var pauseSound = function (sound) { | ||
if (!sound || !sound.pause) { | ||
return false; | ||
} | ||
sound.pause(); | ||
return true; | ||
}; | ||
/** | ||
* Carefully stops a sound. HTMLAudioElement don't natively have a .stop() | ||
* function, so this is the shim to do that. | ||
*/ | ||
var soundStop = function (sound) { | ||
pauseSound(sound); | ||
if (sound.readyState) { | ||
sound.currentTime = 0; | ||
} | ||
}; | ||
/** | ||
* An audio playback manager for persistent and on-demand themes and sounds. | ||
*/ | ||
var AudioPlayr = /** @class */ (function () { | ||
@@ -50,453 +53,204 @@ /** | ||
* | ||
* @param settings Settings to use for initialization. | ||
* @param settings Settings to be used for initialization. | ||
*/ | ||
function AudioPlayr(settings) { | ||
if (!settings.itemsHolder) { | ||
throw new Error("No ItemsHoldr given to AudioPlayr."); | ||
} | ||
this.itemsHolder = settings.itemsHolder; | ||
this.directory = settings.directory || "audio"; | ||
this.fileTypes = settings.fileTypes || ["mp3"]; | ||
this.getThemeDefault = settings.getThemeDefault || "Theme"; | ||
this.getVolumeLocal = settings.getVolumeLocal === undefined | ||
? 1 : settings.getVolumeLocal; | ||
/** | ||
* Created sounds, keyed by name. | ||
*/ | ||
this.sounds = {}; | ||
this.preloadLibraryFromSettings(settings.library || {}); | ||
var volumeInitial = this.itemsHolder.getItem("volume"); | ||
if (volumeInitial === undefined) { | ||
this.setVolume(1); | ||
} | ||
else { | ||
this.setVolume(volumeInitial); | ||
} | ||
this.setMuted(this.itemsHolder.getItem("muted") || false); | ||
this.createSound = settings.createSound || Sound_1.AudioElementSound.create; | ||
this.nameTransform = settings.nameTransform || defaultNameTransform; | ||
this.storage = settings.storage || new Storage_1.DefaultStorage(); | ||
} | ||
/** | ||
* @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; | ||
}; | ||
/** | ||
* @returns The current volume as a Number in [0,1], retrieved by the ItemsHoldr. | ||
*/ | ||
AudioPlayr.prototype.getVolume = function () { | ||
return parseFloat(this.itemsHolder.getItem("volume")) || 1; | ||
}; | ||
/** | ||
* Sets the current volume. If not muted, all sounds will have their volume | ||
* updated. | ||
* Gets whether this is muted. | ||
* | ||
* @param volume A Number in [0,1] to set as the current volume. | ||
* @returns Whether this is muted. | ||
*/ | ||
AudioPlayr.prototype.setVolume = function (volume) { | ||
if (!this.getMuted()) { | ||
for (var i in this.sounds) { | ||
this.sounds[i].volume = parseFloat(this.sounds[i].getAttribute("volumeReal") || "") * volume; | ||
} | ||
} | ||
this.itemsHolder.setItem("volume", volume.toString()); | ||
}; | ||
/** | ||
* @returns Whether this is currently muted. | ||
*/ | ||
AudioPlayr.prototype.getMuted = function () { | ||
return !!(parseFloat(this.itemsHolder.getItem("muted"))); | ||
var mutedRaw = this.storage.getItem(Storage_1.AudioSetting.Muted); | ||
return mutedRaw === undefined || mutedRaw === null | ||
? false | ||
: JSON.parse(mutedRaw); | ||
}; | ||
/** | ||
* Calls either setMutedOn or setMutedOff as is appropriate. | ||
* Gets the global sound volume in [0, 1]. | ||
* | ||
* @param muted The new status for muted. | ||
* @returns Global sound volume in [0, 1]. | ||
*/ | ||
AudioPlayr.prototype.setMuted = function (muted) { | ||
if (muted) { | ||
this.setMutedOn(); | ||
} | ||
else { | ||
this.setMutedOff(); | ||
} | ||
AudioPlayr.prototype.getVolume = function () { | ||
var volumeRaw = this.storage.getItem(Storage_1.AudioSetting.Volume); | ||
return volumeRaw === undefined || volumeRaw === null | ||
? 1 | ||
: JSON.parse(volumeRaw); | ||
}; | ||
/** | ||
* 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 () { | ||
for (var 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(); | ||
for (var i in this.sounds) { | ||
if (this.sounds.hasOwnProperty(i)) { | ||
var sound = this.sounds[i]; | ||
sound.volume = parseFloat(sound.getAttribute("volumeReal") || "") * volume; | ||
} | ||
} | ||
this.itemsHolder.setItem("muted", "0"); | ||
}; | ||
/** | ||
* @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; | ||
}; | ||
/** | ||
* Plays the sound of the given name. | ||
* Sets whether this is muted. | ||
* | ||
* @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. | ||
* @param muted Whether this is now muted. | ||
*/ | ||
AudioPlayr.prototype.play = function (name) { | ||
var sound; | ||
if (!this.sounds[name]) { | ||
if (!this.library[name]) { | ||
throw new Error("Unknown name given to AudioPlayr.play: '" + name + "'."); | ||
} | ||
sound = this.sounds[name] = this.library[name]; | ||
} | ||
else { | ||
sound = this.sounds[name]; | ||
} | ||
soundStop(sound); | ||
if (this.getMuted()) { | ||
sound.volume = 0; | ||
} | ||
else { | ||
sound.setAttribute("volumeReal", "1"); | ||
sound.volume = this.getVolume(); | ||
} | ||
// This gives enough time after a call to pause so a Promise exception | ||
// Does not occur during the buffering for play. | ||
setTimeout(playSound.bind(this), 1, sound); | ||
var used = parseFloat(sound.getAttribute("used") || ""); | ||
// If this is the song's first play, let it know how to stop | ||
if (!used) { | ||
sound.setAttribute("used", (used + 1).toString()); | ||
sound.addEventListener("ended", this.soundFinish.bind(this, name)); | ||
} | ||
sound.setAttribute("name", name); | ||
return sound; | ||
AudioPlayr.prototype.setMuted = function (muted) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var _this = this; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
this.storage.setItem(Storage_1.AudioSetting.Muted, JSON.stringify(muted)); | ||
return [4 /*yield*/, Promise.all(Object.keys(this.sounds) | ||
.map(function (name) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { | ||
return [2 /*return*/, this.sounds[name].setGlobalMuted(muted)]; | ||
}); }); }))]; | ||
case 1: | ||
_a.sent(); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
}; | ||
/** | ||
* Pauses all currently playing sounds. | ||
*/ | ||
AudioPlayr.prototype.pauseAll = function () { | ||
for (var i in this.sounds) { | ||
if (this.sounds.hasOwnProperty(i)) { | ||
pauseSound(this.sounds[i]); | ||
} | ||
} | ||
}; | ||
/** | ||
* Un-pauses (resumes) all currently paused sounds. | ||
*/ | ||
AudioPlayr.prototype.resumeAll = function () { | ||
for (var i in this.sounds) { | ||
if (!this.sounds.hasOwnProperty(i)) { | ||
continue; | ||
} | ||
playSound(this.sounds[i]); | ||
} | ||
}; | ||
/** | ||
* Pauses the currently playing theme, if there is one. | ||
*/ | ||
AudioPlayr.prototype.pauseTheme = function () { | ||
if (this.theme) { | ||
pauseSound(this.theme); | ||
} | ||
}; | ||
/** | ||
* Resumes the theme, if there is one and it's paused. | ||
*/ | ||
AudioPlayr.prototype.resumeTheme = function () { | ||
if (this.theme) { | ||
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. | ||
* Sets the global sound volume in [0, 1]. | ||
* | ||
* @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. | ||
* @param volume New global sound volume in [0, 1]. | ||
*/ | ||
AudioPlayr.prototype.playLocal = function (name, location) { | ||
var sound = this.play(name); | ||
var volumeReal = this.getVolumeLocal instanceof Function | ||
? this.getVolumeLocal(location) | ||
: this.getVolumeLocal; | ||
sound.setAttribute("volumeReal", volumeReal.toString()); | ||
sound.volume = this.getMuted() | ||
? 0 | ||
: sound.volume = volumeReal * this.getVolume(); | ||
return sound; | ||
AudioPlayr.prototype.setVolume = function (volume) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var _this = this; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
if (volume < 0 || volume > 1) { | ||
throw new Error("Volume must be within [0, 1]."); | ||
} | ||
this.storage.setItem(Storage_1.AudioSetting.Volume, volume); | ||
return [4 /*yield*/, Promise.all(Object.keys(this.sounds) | ||
.map(function (name) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { | ||
return [2 /*return*/, this.sounds[name].setGlobalVolume(volume)]; | ||
}); }); }))]; | ||
case 1: | ||
_a.sent(); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
}; | ||
/** | ||
* Pauses any previously playing theme and starts playback of a new theme. | ||
* Plays a sound. | ||
* | ||
* @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. | ||
* @param name Name of a sound. | ||
* @param settings Any settings for the sound. | ||
* @returns A Promise for playing the sound. | ||
*/ | ||
AudioPlayr.prototype.playTheme = function (name, loop) { | ||
if (loop === void 0) { loop = true; } | ||
this.pauseTheme(); | ||
if (typeof name === "undefined") { | ||
// tslint:disable-next-line:no-parameter-reassignment | ||
name = this.getThemeDefault instanceof Function | ||
? this.getThemeDefault() | ||
: this.getThemeDefault; | ||
} | ||
// 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; | ||
AudioPlayr.prototype.play = function (name, settings) { | ||
if (settings === void 0) { settings = {}; } | ||
return __awaiter(this, void 0, void 0, function () { | ||
var alias, sound; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
name = this.nameTransform(name); | ||
alias = settings.alias === undefined | ||
? name | ||
: this.nameTransform(settings.alias); | ||
if (!{}.hasOwnProperty.call(this.sounds, alias)) return [3 /*break*/, 2]; | ||
return [4 /*yield*/, this.sounds[alias].stop()]; | ||
case 1: | ||
_a.sent(); | ||
_a.label = 2; | ||
case 2: | ||
sound = this.sounds[alias] = this.createSound(name, { | ||
globalMuted: this.getMuted(), | ||
globalVolume: this.getVolume(), | ||
localMuted: settings.muted === undefined | ||
? false | ||
: settings.muted, | ||
localVolume: settings.volume === undefined | ||
? 1 | ||
: settings.volume, | ||
loop: settings.loop === undefined | ||
? false | ||
: settings.loop, | ||
}); | ||
return [2 /*return*/, sound.play()]; | ||
} | ||
}); | ||
}); | ||
}; | ||
/** | ||
* Wrapper around playTheme that plays a sound, then a theme. This is | ||
* implemented using an event listener on the sound's ending. | ||
* Pauses all sounds. | ||
* | ||
* @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. | ||
* @returns A Promise for pausing all sounds. | ||
*/ | ||
AudioPlayr.prototype.playThemePrefixed = function (prefix, name, loop) { | ||
var sound = this.play(prefix); | ||
this.pauseTheme(); | ||
if (typeof name === "undefined") { | ||
// tslint:disable-next-line:no-parameter-reassignment | ||
name = this.getThemeDefault instanceof Function | ||
? this.getThemeDefault() | ||
: this.getThemeDefault; | ||
} | ||
this.addEventListener(prefix, "ended", this.playTheme.bind(this, name, loop)); | ||
return sound; | ||
AudioPlayr.prototype.pauseAll = function () { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var _this = this; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, Promise.all(Object.keys(this.sounds) | ||
.map(function (name) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { | ||
return [2 /*return*/, this.sounds[name].pause()]; | ||
}); }); }))]; | ||
case 1: | ||
_a.sent(); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
}; | ||
/** | ||
* 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. | ||
* Un-pauses (plays) all sounds. | ||
* | ||
* @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. | ||
* @returns A Promise for resuming. | ||
*/ | ||
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); | ||
AudioPlayr.prototype.resumeAll = function () { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var _this = this; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, Promise.all(Object.keys(this.sounds) | ||
.map(function (name) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { | ||
return [2 /*return*/, this.sounds[name].play()]; | ||
}); }); }))]; | ||
case 1: | ||
_a.sent(); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
}; | ||
/** | ||
* Clears all events added by this.addEventListener to a sound under a given | ||
* event. | ||
* Stops all sounds. | ||
* | ||
* @param name The name of the sound. | ||
* @param event The name of the event, such as "ended". | ||
* @returns A Promise for stopping. | ||
*/ | ||
AudioPlayr.prototype.removeEventListeners = function (name, event) { | ||
var sound = this.library[name]; | ||
if (!sound) { | ||
throw new Error("Unknown name given to removeEventListeners: '" + name + "'."); | ||
} | ||
if (!sound.addedEvents) { | ||
return; | ||
} | ||
var addedEvents = sound.addedEvents[event]; | ||
if (!addedEvents) { | ||
return; | ||
} | ||
for (var _i = 0, addedEvents_1 = addedEvents; _i < addedEvents_1.length; _i++) { | ||
var addedEvent = addedEvents_1[_i]; | ||
sound.removeEventListener(event, addedEvent); | ||
} | ||
addedEvents.length = 0; | ||
AudioPlayr.prototype.stopAll = function () { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var _this = this; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, Promise.all(Object.keys(this.sounds) | ||
.map(function (name) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { | ||
return [2 /*return*/, this.sounds[name].stop()]; | ||
}); }); }))]; | ||
case 1: | ||
_a.sent(); | ||
this.sounds = {}; | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
}; | ||
/** | ||
* Adds an event listener to a sound. If the sound doesn't exist or has | ||
* finished playing, it's called immediately. | ||
* Checks whether a sound under the alias exists. | ||
* | ||
* @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. | ||
* @param alias Alias to check under. | ||
* @param name Name the sound must have, if not the same as the name. | ||
* @returns Whether a sound exists under the alias. | ||
*/ | ||
AudioPlayr.prototype.addEventImmediate = function (name, event, callback) { | ||
if (!this.sounds.hasOwnProperty(name) || this.sounds[name].paused) { | ||
callback(); | ||
return; | ||
AudioPlayr.prototype.hasSound = function (alias, name) { | ||
alias = this.nameTransform(alias); | ||
if (!{}.hasOwnProperty.call(this.sounds, alias)) { | ||
return false; | ||
} | ||
this.sounds[name].addEventListener(event, callback); | ||
return name === undefined || this.sounds[alias].name === this.nameTransform(name); | ||
}; | ||
/** | ||
* 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]; | ||
} | ||
}; | ||
/** | ||
* Loads every sound defined in the library via AJAX. Sounds are loaded | ||
* into <audio> elements via createAudio and stored in the library. | ||
*/ | ||
AudioPlayr.prototype.preloadLibraryFromSettings = function (librarySettings) { | ||
this.library = {}; | ||
this.directories = {}; | ||
for (var directoryName in librarySettings) { | ||
if (!librarySettings.hasOwnProperty(directoryName)) { | ||
continue; | ||
} | ||
var directory = {}; | ||
var directorySoundNames = librarySettings[directoryName]; | ||
for (var _i = 0, directorySoundNames_1 = directorySoundNames; _i < directorySoundNames_1.length; _i++) { | ||
var name_1 = directorySoundNames_1[_i]; | ||
this.library[name_1] = directory[name_1] = this.createAudio(name_1, directoryName); | ||
} | ||
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"); | ||
// Create an audio source for each child | ||
for (var _i = 0, _a = this.fileTypes; _i < _a.length; _i++) { | ||
var fileType = _a[_i]; | ||
var child = document.createElement("source"); | ||
child.type = "audio/" + fileType; | ||
child.src = this.directory + "/" + directory + "/" + fileType + "/" + name + "." + fileType; | ||
sound.appendChild(child); | ||
} | ||
// This preloads the sound. | ||
sound.volume = 0; | ||
sound.setAttribute("volumeReal", "1"); | ||
sound.setAttribute("used", "0"); | ||
playSound(sound); | ||
return sound; | ||
}; | ||
return AudioPlayr; | ||
@@ -503,0 +257,0 @@ }()); |
@@ -1,632 +0,197 @@ | ||
import { IItemsHoldr } from "itemsholdr"; | ||
import { IAudioPlayr, IAudioPlayrSettings, INameTransform, IPlaySettings } from "./IAudioPlayr"; | ||
import { AudioElementSound, ICreateSound, ISound } from "./Sound"; | ||
import { AudioSetting, DefaultStorage, IAudioSettingsStorage } from "./Storage"; | ||
import { | ||
IAudioPlayr, IAudioPlayrSettings, ICreatedSound, IDirectoriesLibrary, IGetThemeDefault, | ||
IGetVolumeLocal, ILibrarySettings, ISoundsLibrary, | ||
} from "./IAudioPlayr"; | ||
/** | ||
* 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. | ||
* Created sounds, keyed by name. | ||
*/ | ||
const playSound = (sound: HTMLAudioElement): boolean => { | ||
if (!sound || !sound.play) { | ||
return false; | ||
} | ||
interface ISounds { | ||
[i: string]: ISound; | ||
} | ||
// tslint:disable-next-line:no-floating-promises | ||
sound.play(); | ||
return true; | ||
}; | ||
/** | ||
* Utility to try to pause a sound, which may not be possible in headless | ||
* environments like PhantomJS. | ||
* Default name transform, which does nothing. | ||
* | ||
* @param sound An <audio> element to pause. | ||
* @returns Whether the sound was able to pause. | ||
* @param name Provided name to play. | ||
* @returns The same name. | ||
*/ | ||
const pauseSound = (sound: HTMLAudioElement): boolean => { | ||
if (!sound || !sound.pause) { | ||
return false; | ||
} | ||
const defaultNameTransform = (name: string): string => name; | ||
sound.pause(); | ||
return true; | ||
}; | ||
/** | ||
* Carefully stops a sound. HTMLAudioElement don't natively have a .stop() | ||
* function, so this is the shim to do that. | ||
* Playback for persistent and on-demand sounds and themes. | ||
*/ | ||
const soundStop = (sound: HTMLAudioElement): void => { | ||
pauseSound(sound); | ||
if (sound.readyState) { | ||
sound.currentTime = 0; | ||
} | ||
}; | ||
/** | ||
* An audio playback manager for persistent and on-demand themes and sounds. | ||
*/ | ||
export class AudioPlayr implements IAudioPlayr { | ||
/** | ||
* What file types to add as sources to sounds. | ||
* Creates a new sound. | ||
*/ | ||
private readonly fileTypes: string[]; | ||
private readonly createSound: ICreateSound; | ||
/** | ||
* Directory from which audio files are AJAXed upon startup. | ||
* Transforms a sound name into a file name. | ||
*/ | ||
private readonly directory: string; | ||
private readonly nameTransform: INameTransform; | ||
/** | ||
* Storage container for settings like volume and muted status. | ||
* Created sounds, keyed by name. | ||
*/ | ||
private readonly itemsHolder: IItemsHoldr; | ||
private sounds: ISounds = {}; | ||
/** | ||
* HTMLAudioElements keyed by their name. | ||
* Stores mute and volume status locally. | ||
*/ | ||
private library: ISoundsLibrary; | ||
private readonly storage: IAudioSettingsStorage; | ||
/** | ||
* Directories mapping folder names to sound libraries. | ||
*/ | ||
private directories: IDirectoriesLibrary; | ||
/** | ||
* Currently playing sound objects, keyed by name (excluding extensions). | ||
*/ | ||
private sounds: ISoundsLibrary; | ||
/** | ||
* The currently playing theme. | ||
*/ | ||
private theme?: ICreatedSound; | ||
/** | ||
* The name of the currently playing theme. | ||
*/ | ||
private themeName?: string; | ||
/** | ||
* The Function or Number used to determine what playLocal's volume is. | ||
*/ | ||
private getVolumeLocal: number | IGetVolumeLocal; | ||
/** | ||
* The Function or String used to get a default theme name. | ||
*/ | ||
private getThemeDefault: string | IGetThemeDefault; | ||
/** | ||
* Initializes a new instance of the AudioPlayr class. | ||
* | ||
* @param settings Settings to use for initialization. | ||
* @param settings Settings to be used for initialization. | ||
*/ | ||
public constructor(settings: IAudioPlayrSettings) { | ||
if (!settings.itemsHolder) { | ||
throw new Error("No ItemsHoldr given to AudioPlayr."); | ||
} | ||
this.itemsHolder = settings.itemsHolder; | ||
this.directory = settings.directory || "audio"; | ||
this.fileTypes = settings.fileTypes || ["mp3"]; | ||
this.getThemeDefault = settings.getThemeDefault || "Theme"; | ||
this.getVolumeLocal = settings.getVolumeLocal === undefined | ||
? 1 : settings.getVolumeLocal; | ||
this.sounds = {}; | ||
this.preloadLibraryFromSettings(settings.library || {}); | ||
const volumeInitial: number | undefined = this.itemsHolder.getItem("volume"); | ||
if (volumeInitial === undefined) { | ||
this.setVolume(1); | ||
} else { | ||
this.setVolume(volumeInitial); | ||
} | ||
this.setMuted(this.itemsHolder.getItem("muted") || false); | ||
this.createSound = settings.createSound || AudioElementSound.create; | ||
this.nameTransform = settings.nameTransform || defaultNameTransform; | ||
this.storage = settings.storage || new DefaultStorage(); | ||
} | ||
/** | ||
* @returns The listing of <audio> Elements, keyed by name. | ||
*/ | ||
public getLibrary(): {} { | ||
return this.library; | ||
} | ||
/** | ||
* @returns The allowed filetypes for audio files. | ||
*/ | ||
public getFileTypes(): string[] { | ||
return this.fileTypes; | ||
} | ||
/** | ||
* @returns The currently playing <audio> Elements, keyed by name. | ||
*/ | ||
public getSounds(): {} { | ||
return this.sounds; | ||
} | ||
/** | ||
* @returns The current playing theme's <audio> Element. | ||
*/ | ||
public getTheme(): HTMLAudioElement | undefined { | ||
return this.theme; | ||
} | ||
/** | ||
* @returns The name of the currently playing theme. | ||
*/ | ||
public getThemeName(): string | undefined { | ||
return this.themeName; | ||
} | ||
/** | ||
* @returns The directory under which all filetype directories are to be located. | ||
*/ | ||
public getDirectory(): string { | ||
return this.directory; | ||
} | ||
/** | ||
* @returns The current volume as a Number in [0,1], retrieved by the ItemsHoldr. | ||
*/ | ||
public getVolume(): number { | ||
return parseFloat(this.itemsHolder.getItem("volume")) || 1; | ||
} | ||
/** | ||
* Sets the current volume. If not muted, all sounds will have their volume | ||
* updated. | ||
* Gets whether this is muted. | ||
* | ||
* @param volume A Number in [0,1] to set as the current volume. | ||
* @returns Whether this is muted. | ||
*/ | ||
public setVolume(volume: number): void { | ||
if (!this.getMuted()) { | ||
for (const i in this.sounds) { | ||
this.sounds[i].volume = parseFloat(this.sounds[i].getAttribute("volumeReal") || "") * volume; | ||
} | ||
} | ||
this.itemsHolder.setItem("volume", volume.toString()); | ||
} | ||
/** | ||
* @returns Whether this is currently muted. | ||
*/ | ||
public getMuted(): boolean { | ||
return !!(parseFloat(this.itemsHolder.getItem("muted"))); | ||
} | ||
const mutedRaw = this.storage.getItem(AudioSetting.Muted); | ||
/** | ||
* Calls either setMutedOn or setMutedOff as is appropriate. | ||
* | ||
* @param muted The new status for muted. | ||
*/ | ||
public setMuted(muted: boolean): void { | ||
if (muted) { | ||
this.setMutedOn(); | ||
} else { | ||
this.setMutedOff(); | ||
} | ||
return mutedRaw === undefined || mutedRaw === null | ||
? false | ||
: JSON.parse(mutedRaw); | ||
} | ||
/** | ||
* Calls either setMutedOn or setMutedOff to toggle whether this is muted. | ||
*/ | ||
public toggleMuted(): void { | ||
this.setMuted(!this.getMuted()); | ||
} | ||
/** | ||
* Sets volume to 0 in all currently playing sounds and stores the muted | ||
* status as on in the internal ItemsHoldr. | ||
*/ | ||
public setMutedOn(): void { | ||
for (const 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. | ||
*/ | ||
public setMutedOff(): void { | ||
const volume: number = this.getVolume(); | ||
for (const i in this.sounds) { | ||
if (this.sounds.hasOwnProperty(i)) { | ||
const sound: HTMLAudioElement = this.sounds[i]; | ||
sound.volume = parseFloat(sound.getAttribute("volumeReal") || "") * volume; | ||
} | ||
} | ||
this.itemsHolder.setItem("muted", "0"); | ||
} | ||
/** | ||
* @returns The Function or Number used as the volume setter for local sounds. | ||
*/ | ||
public getGetVolumeLocal(): number | IGetVolumeLocal { | ||
return this.getVolumeLocal; | ||
} | ||
/** | ||
* @param getVolumeLocal A new Function or Number to use as the volume setter | ||
* for local sounds. | ||
*/ | ||
public setGetVolumeLocal(getVolumeLocalNew: number | IGetVolumeLocal): void { | ||
this.getVolumeLocal = getVolumeLocalNew; | ||
} | ||
/** | ||
* @returns The Function or String used to get the default theme for playTheme. | ||
*/ | ||
public getGetThemeDefault(): string | IGetThemeDefault { | ||
return this.getThemeDefault; | ||
} | ||
/** | ||
* @param getThemeDefaultNew A new Function or String to use as the source for | ||
* theme names in default playTheme calls. | ||
*/ | ||
public setGetThemeDefault(getThemeDefaultNew: string | IGetThemeDefault): void { | ||
this.getThemeDefault = getThemeDefaultNew; | ||
} | ||
/** | ||
* Plays the sound of the given name. | ||
* Gets the global sound volume in [0, 1]. | ||
* | ||
* @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. | ||
* @returns Global sound volume in [0, 1]. | ||
*/ | ||
public play(name: string): ICreatedSound { | ||
let sound: ICreatedSound; | ||
public getVolume(): number { | ||
const volumeRaw = this.storage.getItem(AudioSetting.Volume); | ||
if (!this.sounds[name]) { | ||
if (!this.library[name]) { | ||
throw new Error(`Unknown name given to AudioPlayr.play: '${name}'.`); | ||
} | ||
sound = this.sounds[name] = this.library[name]; | ||
} else { | ||
sound = this.sounds[name]; | ||
} | ||
soundStop(sound); | ||
if (this.getMuted()) { | ||
sound.volume = 0; | ||
} else { | ||
sound.setAttribute("volumeReal", "1"); | ||
sound.volume = this.getVolume(); | ||
} | ||
// This gives enough time after a call to pause so a Promise exception | ||
// Does not occur during the buffering for play. | ||
setTimeout(playSound.bind(this), 1, sound); | ||
const used: number = parseFloat(sound.getAttribute("used") || ""); | ||
// If this is the song's first play, let it know how to stop | ||
if (!used) { | ||
sound.setAttribute("used", (used + 1).toString()); | ||
sound.addEventListener("ended", this.soundFinish.bind(this, name)); | ||
} | ||
sound.setAttribute("name", name); | ||
return sound; | ||
return volumeRaw === undefined || volumeRaw === null | ||
? 1 | ||
: JSON.parse(volumeRaw); | ||
} | ||
/** | ||
* Pauses all currently playing sounds. | ||
*/ | ||
public pauseAll(): void { | ||
for (const i in this.sounds) { | ||
if (this.sounds.hasOwnProperty(i)) { | ||
pauseSound(this.sounds[i]); | ||
} | ||
} | ||
} | ||
/** | ||
* Un-pauses (resumes) all currently paused sounds. | ||
*/ | ||
public resumeAll(): void { | ||
for (const i in this.sounds) { | ||
if (!this.sounds.hasOwnProperty(i)) { | ||
continue; | ||
} | ||
playSound(this.sounds[i]); | ||
} | ||
} | ||
/** | ||
* Pauses the currently playing theme, if there is one. | ||
*/ | ||
public pauseTheme(): void { | ||
if (this.theme) { | ||
pauseSound(this.theme); | ||
} | ||
} | ||
/** | ||
* Resumes the theme, if there is one and it's paused. | ||
*/ | ||
public resumeTheme(): void { | ||
if (this.theme) { | ||
playSound(this.theme); | ||
} | ||
} | ||
/** | ||
* Stops all sounds and any theme, and removes all references to them. | ||
*/ | ||
public clearAll(): void { | ||
this.pauseAll(); | ||
this.clearTheme(); | ||
this.sounds = {}; | ||
} | ||
/** | ||
* Pauses and removes the theme, if there is one. | ||
*/ | ||
public clearTheme(): void { | ||
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. | ||
* Sets whether this is muted. | ||
* | ||
* @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. | ||
* @param muted Whether this is now muted. | ||
*/ | ||
public playLocal(name: string, location?: {}): HTMLAudioElement { | ||
const sound: HTMLAudioElement = this.play(name); | ||
const volumeReal: number = this.getVolumeLocal instanceof Function | ||
? this.getVolumeLocal(location) | ||
: this.getVolumeLocal; | ||
public async setMuted(muted: boolean): Promise<void> { | ||
this.storage.setItem(AudioSetting.Muted, JSON.stringify(muted)); | ||
sound.setAttribute("volumeReal", volumeReal.toString()); | ||
sound.volume = this.getMuted() | ||
? 0 | ||
: sound.volume = volumeReal * this.getVolume(); | ||
return sound; | ||
await Promise.all( | ||
Object.keys(this.sounds) | ||
.map(async (name: string) => this.sounds[name].setGlobalMuted(muted))); | ||
} | ||
/** | ||
* Pauses any previously playing theme and starts playback of a new theme. | ||
* Sets the global sound volume in [0, 1]. | ||
* | ||
* @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. | ||
* @param volume New global sound volume in [0, 1]. | ||
*/ | ||
public playTheme(name?: string, loop: boolean = true): ICreatedSound { | ||
this.pauseTheme(); | ||
if (typeof name === "undefined") { | ||
// tslint:disable-next-line:no-parameter-reassignment | ||
name = this.getThemeDefault instanceof Function | ||
? this.getThemeDefault() | ||
: this.getThemeDefault; | ||
public async setVolume(volume: number): Promise<void> { | ||
if (volume < 0 || volume > 1) { | ||
throw new Error("Volume must be within [0, 1]."); | ||
} | ||
// If a theme already exists, kill it | ||
if (typeof this.theme !== "undefined" && this.theme.hasAttribute("name")) { | ||
delete this.sounds[this.theme.getAttribute("name")!]; | ||
} | ||
this.storage.setItem(AudioSetting.Volume, volume); | ||
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; | ||
await Promise.all( | ||
Object.keys(this.sounds) | ||
.map(async (name: string) => this.sounds[name].setGlobalVolume(volume))); | ||
} | ||
/** | ||
* Wrapper around playTheme that plays a sound, then a theme. This is | ||
* implemented using an event listener on the sound's ending. | ||
* Plays a sound. | ||
* | ||
* @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. | ||
* @param name Name of a sound. | ||
* @param settings Any settings for the sound. | ||
* @returns A Promise for playing the sound. | ||
*/ | ||
public playThemePrefixed(prefix: string, name?: string, loop?: boolean): HTMLAudioElement { | ||
const sound: HTMLAudioElement = this.play(prefix); | ||
public async play(name: string, settings: Partial<IPlaySettings> = {}): Promise<void> { | ||
name = this.nameTransform(name); | ||
const alias = settings.alias === undefined | ||
? name | ||
: this.nameTransform(settings.alias); | ||
this.pauseTheme(); | ||
if (typeof name === "undefined") { | ||
// tslint:disable-next-line:no-parameter-reassignment | ||
name = this.getThemeDefault instanceof Function | ||
? this.getThemeDefault() | ||
: this.getThemeDefault; | ||
if ({}.hasOwnProperty.call(this.sounds, alias)) { | ||
await this.sounds[alias].stop(); | ||
} | ||
this.addEventListener(prefix, "ended", this.playTheme.bind(this, name, loop)); | ||
const sound = this.sounds[alias] = this.createSound( | ||
name, | ||
{ | ||
globalMuted: this.getMuted(), | ||
globalVolume: this.getVolume(), | ||
localMuted: settings.muted === undefined | ||
? false | ||
: settings.muted, | ||
localVolume: settings.volume === undefined | ||
? 1 | ||
: settings.volume, | ||
loop: settings.loop === undefined | ||
? false | ||
: settings.loop, | ||
}); | ||
return sound; | ||
return sound.play(); | ||
} | ||
/** | ||
* 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. | ||
* Pauses all sounds. | ||
* | ||
* @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. | ||
* @returns A Promise for pausing all sounds. | ||
*/ | ||
public addEventListener(name: string, event: string, callback: EventListenerOrEventListenerObject): void { | ||
const sound: ICreatedSound = 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); | ||
public async pauseAll(): Promise<void> { | ||
await Promise.all( | ||
Object.keys(this.sounds) | ||
.map(async (name: string) => this.sounds[name].pause())); | ||
} | ||
/** | ||
* Clears all events added by this.addEventListener to a sound under a given | ||
* event. | ||
* Un-pauses (plays) all sounds. | ||
* | ||
* @param name The name of the sound. | ||
* @param event The name of the event, such as "ended". | ||
* @returns A Promise for resuming. | ||
*/ | ||
public removeEventListeners(name: string, event: string): void { | ||
const sound: ICreatedSound = this.library[name]; | ||
if (!sound) { | ||
throw new Error(`Unknown name given to removeEventListeners: '${name}'.`); | ||
} | ||
if (!sound.addedEvents) { | ||
return; | ||
} | ||
const addedEvents = sound.addedEvents[event]; | ||
if (!addedEvents) { | ||
return; | ||
} | ||
for (const addedEvent of addedEvents) { | ||
sound.removeEventListener(event, addedEvent); | ||
} | ||
addedEvents.length = 0; | ||
public async resumeAll(): Promise<void> { | ||
await Promise.all( | ||
Object.keys(this.sounds) | ||
.map(async (name: string) => this.sounds[name].play())); | ||
} | ||
/** | ||
* Adds an event listener to a sound. If the sound doesn't exist or has | ||
* finished playing, it's called immediately. | ||
* Stops all sounds. | ||
* | ||
* @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. | ||
* @returns A Promise for stopping. | ||
*/ | ||
public addEventImmediate(name: string, event: string, callback: Function): void { | ||
if (!this.sounds.hasOwnProperty(name) || this.sounds[name].paused) { | ||
callback(); | ||
return; | ||
} | ||
public async stopAll(): Promise<void> { | ||
await Promise.all( | ||
Object.keys(this.sounds) | ||
.map(async (name: string) => this.sounds[name].stop())); | ||
this.sounds[name].addEventListener(event, callback as EventListenerOrEventListenerObject); | ||
this.sounds = {}; | ||
} | ||
/** | ||
* Called when a sound has completed to get it out of sounds. | ||
* Checks whether a sound under the alias exists. | ||
* | ||
* @param name The name of the sound that just finished. | ||
* @param alias Alias to check under. | ||
* @param name Name the sound must have, if not the same as the name. | ||
* @returns Whether a sound exists under the alias. | ||
*/ | ||
private soundFinish(name: string): void { | ||
if (this.sounds.hasOwnProperty(name)) { | ||
delete this.sounds[name]; | ||
public hasSound(alias: string, name?: string): boolean { | ||
alias = this.nameTransform(alias); | ||
if (!{}.hasOwnProperty.call(this.sounds, alias)) { | ||
return false; | ||
} | ||
} | ||
/** | ||
* Loads every sound defined in the library via AJAX. Sounds are loaded | ||
* into <audio> elements via createAudio and stored in the library. | ||
*/ | ||
private preloadLibraryFromSettings(librarySettings: ILibrarySettings): void { | ||
this.library = {}; | ||
this.directories = {}; | ||
for (const directoryName in librarySettings) { | ||
if (!librarySettings.hasOwnProperty(directoryName)) { | ||
continue; | ||
} | ||
const directory: ISoundsLibrary = {}; | ||
const directorySoundNames: string[] = librarySettings[directoryName]; | ||
for (const name of directorySoundNames) { | ||
this.library[name] = directory[name] = this.createAudio(name, directoryName); | ||
} | ||
this.directories[directoryName] = directory; | ||
} | ||
return name === undefined || this.sounds[alias].name === this.nameTransform(name); | ||
} | ||
/** | ||
* 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. | ||
*/ | ||
private createAudio(name: string, directory: string): ICreatedSound { | ||
const sound: ICreatedSound = document.createElement("audio") as ICreatedSound; | ||
// Create an audio source for each child | ||
for (const fileType of this.fileTypes) { | ||
const child: HTMLSourceElement = document.createElement("source"); | ||
child.type = `audio/${fileType}`; | ||
child.src = `${this.directory}/${directory}/${fileType}/${name}.${fileType}`; | ||
sound.appendChild(child); | ||
} | ||
// This preloads the sound. | ||
sound.volume = 0; | ||
sound.setAttribute("volumeReal", "1"); | ||
sound.setAttribute("used", "0"); | ||
playSound(sound); | ||
return sound; | ||
} | ||
} |
@@ -1,35 +0,32 @@ | ||
import { IItemsHoldr } from "itemsholdr"; | ||
import { ICreateSound } from "./Sound"; | ||
import { IAudioSettingsStorage } from "./Storage"; | ||
/** | ||
* Lookup for directories to the sounds contained within. | ||
* Settings to play a sound. | ||
*/ | ||
export interface ILibrarySettings { | ||
[i: string]: string[]; | ||
export interface IPlaySettings { | ||
/** | ||
* Alias to store this under (by default, its name). | ||
*/ | ||
alias?: string; | ||
/** | ||
* Whether it should loop (by default, false). | ||
*/ | ||
loop?: boolean; | ||
/** | ||
* Whether this sound is muted (by default, false). | ||
*/ | ||
muted?: boolean; | ||
/** | ||
* Initial volume for this sound (by default, 1). | ||
*/ | ||
volume?: number; | ||
} | ||
export interface ICreatedSound extends HTMLAudioElement { | ||
addedEvents?: { | ||
[i: string]: EventListenerOrEventListenerObject[]; | ||
}; | ||
} | ||
/** | ||
* Lookup for HTMLAudioElements keyed by their names. | ||
* Transforms a sound name into a file name. | ||
* | ||
* @param name Provided name to play. | ||
* @returns Equivalent file name. | ||
*/ | ||
export interface ISoundsLibrary { | ||
[i: string]: ICreatedSound; | ||
} | ||
export declare type INameTransform = (name: string) => string; | ||
/** | ||
* Lookup for directories of sounds to their sound libraries. | ||
*/ | ||
export interface IDirectoriesLibrary { | ||
[i: string]: ISoundsLibrary; | ||
} | ||
/** | ||
* @param location Location requesting the volume. | ||
* @returns Volume for a playLocal call. | ||
*/ | ||
export declare type IGetVolumeLocal = (location?: {}) => number; | ||
/** | ||
* @returns Name of the default theme. | ||
*/ | ||
export declare type IGetThemeDefault = () => string; | ||
/** | ||
* Settings to initialize a new instance of an IAudioPlayr. | ||
@@ -39,213 +36,76 @@ */ | ||
/** | ||
* The names of the audio files to be preloaded for on-demand playback. | ||
* Creates a new sound. | ||
*/ | ||
library?: ILibrarySettings; | ||
createSound?: ICreateSound; | ||
/** | ||
* The directory in which all sub-directories of audio files are stored. | ||
* Transforms provided names into file names. | ||
*/ | ||
directory?: string; | ||
nameTransform?: INameTransform; | ||
/** | ||
* The allowed filetypes for each audio file. Each of these should have a | ||
* directory of their name under the main directory, which should contain | ||
* each file of the filetype. | ||
* Stores mute and volume status locally. | ||
*/ | ||
fileTypes?: string[]; | ||
/** | ||
* A storage container to store mute/volume status locally. | ||
*/ | ||
itemsHolder: IItemsHoldr; | ||
/** | ||
* A String or Function to get the default theme for playTheme calls. | ||
* Functions are called for a return value, and Strings are constant | ||
* (defaults to "Theme"). | ||
* | ||
*/ | ||
getThemeDefault?: string | (() => string); | ||
/** | ||
* A Number or Function to get the "local" volume for playLocal calls. | ||
* Functions are called for a return value, and Numbers are constant | ||
* (defaults to 1). | ||
* | ||
*/ | ||
getVolumeLocal?: number | (() => number); | ||
storage?: IAudioSettingsStorage; | ||
} | ||
/** | ||
* An audio playback manager for persistent and on-demand themes and sounds. | ||
* Playback for persistent and on-demand sounds and themes. | ||
*/ | ||
export interface IAudioPlayr { | ||
/** | ||
* @returns The listing of <audio> Elements, keyed by name. | ||
*/ | ||
getLibrary(): {}; | ||
/** | ||
* @returns The allowed filetypes for audio files. | ||
*/ | ||
getFileTypes(): string[]; | ||
/** | ||
* @returns The currently playing <audio> Elements, keyed by name. | ||
*/ | ||
getSounds(): {}; | ||
/** | ||
* @returns The current playing theme's <audio> Element. | ||
*/ | ||
getTheme(): HTMLAudioElement | undefined; | ||
/** | ||
* @returns The name of the currently playing theme. | ||
*/ | ||
getThemeName(): string | undefined; | ||
/** | ||
* @returns The directory under which all filetype directories are to be located. | ||
*/ | ||
getDirectory(): string; | ||
/** | ||
* @returns The current volume as a Number in [0,1], retrieved by the ItemsHoldr. | ||
*/ | ||
getVolume(): number; | ||
/** | ||
* Sets the current volume. If not muted, all sounds will have their volume | ||
* updated. | ||
* Gets whether this is muted. | ||
* | ||
* @param volume A Number in [0,1] to set as the current volume. | ||
* @returns Whether this is now muted. | ||
*/ | ||
setVolume(volume: number): void; | ||
/** | ||
* @returns Whether this is currently muted. | ||
*/ | ||
getMuted(): boolean; | ||
/** | ||
* Calls either setMutedOn or setMutedOff as is appropriate. | ||
* Gets the global sound volume in [0, 1]. | ||
* | ||
* @param muted The new status for muted. | ||
* @returns Global sound volume in [0, 1]. | ||
*/ | ||
setMuted(muted: boolean): void; | ||
getVolume(): number; | ||
/** | ||
* Calls either setMutedOn or setMutedOff to toggle whether this is muted. | ||
*/ | ||
toggleMuted(): void; | ||
/** | ||
* Sets volume to 0 in all currently playing sounds and stores the muted | ||
* status as on in the internal ItemsHoldr. | ||
*/ | ||
setMutedOn(): void; | ||
/** | ||
* Sets sound volumes to their actual volumes and stores the muted status | ||
* as off in the internal ItemsHoldr. | ||
*/ | ||
setMutedOff(): void; | ||
/** | ||
* @returns The Function or Number used as the volume setter for local sounds. | ||
*/ | ||
getGetVolumeLocal(): {}; | ||
/** | ||
* @param getVolumeLocal A new Function or Number to use as the volume setter | ||
* for local sounds. | ||
*/ | ||
setGetVolumeLocal(getVolumeLocalNew: {}): void; | ||
/** | ||
* @returns The Function or String used to get the default theme for playTheme. | ||
*/ | ||
getGetThemeDefault(): {}; | ||
/** | ||
* @param getThemeDefaultNew A new Function or String to use as the source for | ||
* theme names in default playTheme calls. | ||
*/ | ||
setGetThemeDefault(getThemeDefaultNew: {}): void; | ||
/** | ||
* Plays the sound of the given name. | ||
* Sets whether this is muted. | ||
* | ||
* @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. | ||
* @param muted Whether this is now muted. | ||
*/ | ||
play(name: string): HTMLAudioElement; | ||
setMuted(muted: boolean): Promise<void>; | ||
/** | ||
* Pauses all currently playing sounds. | ||
*/ | ||
pauseAll(): void; | ||
/** | ||
* Un-pauses (resumes) all currently paused sounds. | ||
*/ | ||
resumeAll(): void; | ||
/** | ||
* Pauses the currently playing theme, if there is one. | ||
*/ | ||
pauseTheme(): void; | ||
/** | ||
* Resumes the theme, if there is one and it's paused. | ||
*/ | ||
resumeTheme(): void; | ||
/** | ||
* Stops all sounds and any theme, and removes all references to them. | ||
*/ | ||
clearAll(): void; | ||
/** | ||
* Pauses and removes the theme, if there is one. | ||
*/ | ||
clearTheme(): void; | ||
/** | ||
* "Local" version of play that changes the output sound's volume depending | ||
* on the result of a getVolumeLocal call. | ||
* Sets the global sound volume in [0, 1]. | ||
* | ||
* @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. | ||
* @param volume New global sound volume in [0, 1]. | ||
*/ | ||
playLocal(name: string, location?: {}): HTMLAudioElement; | ||
setVolume(volume: number): Promise<void>; | ||
/** | ||
* Pauses any previously playing theme and starts playback of a new theme. | ||
* Plays a sound. | ||
* | ||
* @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. | ||
* @param name Name of a sound. | ||
* @param settings Any settings for the sound. | ||
* @returns A Promise for playing the sound. | ||
*/ | ||
playTheme(name?: string, loop?: boolean): HTMLAudioElement; | ||
play(name: string, settings?: IPlaySettings): Promise<void>; | ||
/** | ||
* Wrapper around playTheme that plays a sound, then a theme. This is | ||
* implemented using an event listener on the sound's ending. | ||
* Pauses all sounds. | ||
* | ||
* @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. | ||
* @returns A Promise for pausing all sounds. | ||
*/ | ||
playThemePrefixed(prefix?: string, name?: string, loop?: boolean): HTMLAudioElement; | ||
pauseAll(): Promise<void>; | ||
/** | ||
* 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. | ||
* Un-pauses (plays) all sounds. | ||
* | ||
* @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. | ||
* @returns A Promise for resuming. | ||
*/ | ||
addEventListener(name: string, event: string, callback: {}): void; | ||
resumeAll(): Promise<void>; | ||
/** | ||
* Clears all events added by this.addEventListener to a sound under a given | ||
* event. | ||
* Stops all sounds and any theme. | ||
* | ||
* @param name The name of the sound. | ||
* @param event The name of the event, such as "ended". | ||
* @returns A Promise for stopping all sounds. | ||
*/ | ||
removeEventListeners(name: string, event: string): void; | ||
stopAll(): Promise<void>; | ||
/** | ||
* Adds an event listener to a sound. If the sound doesn't exist or has | ||
* finished playing, it's called immediately. | ||
* Checks whether a sound under the alias exists. | ||
* | ||
* @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. | ||
* @param alias Alias to check under. | ||
* @param name Name the sound must have, if not the same as alias. | ||
* @returns Whether a sound exists under the alias. | ||
*/ | ||
addEventImmediate(name: string, event: string, callback: {}): void; | ||
hasSound(alias: string, name?: string): boolean; | ||
} |
@@ -1,40 +0,38 @@ | ||
import { IItemsHoldr } from "itemsholdr"; | ||
import { ICreateSound } from "./Sound"; | ||
import { IAudioSettingsStorage } from "./Storage"; | ||
/** | ||
* Lookup for directories to the sounds contained within. | ||
* Settings to play a sound. | ||
*/ | ||
export interface ILibrarySettings { | ||
[i: string]: string[]; | ||
} | ||
export interface IPlaySettings { | ||
/** | ||
* Alias to store this under (by default, its name). | ||
*/ | ||
alias?: string; | ||
export interface ICreatedSound extends HTMLAudioElement { | ||
addedEvents?: { [i: string]: EventListenerOrEventListenerObject[] }; | ||
} | ||
/** | ||
* Whether it should loop (by default, false). | ||
*/ | ||
loop?: boolean; | ||
/** | ||
* Lookup for HTMLAudioElements keyed by their names. | ||
*/ | ||
export interface ISoundsLibrary { | ||
[i: string]: ICreatedSound; | ||
} | ||
/** | ||
* Whether this sound is muted (by default, false). | ||
*/ | ||
muted?: boolean; | ||
/** | ||
* Lookup for directories of sounds to their sound libraries. | ||
*/ | ||
export interface IDirectoriesLibrary { | ||
[i: string]: ISoundsLibrary; | ||
/** | ||
* Initial volume for this sound (by default, 1). | ||
*/ | ||
volume?: number; | ||
} | ||
/** | ||
* @param location Location requesting the volume. | ||
* @returns Volume for a playLocal call. | ||
* Transforms a sound name into a file name. | ||
* | ||
* @param name Provided name to play. | ||
* @returns Equivalent file name. | ||
*/ | ||
export type IGetVolumeLocal = (location?: {}) => number; | ||
export type INameTransform = (name: string) => string; | ||
/** | ||
* @returns Name of the default theme. | ||
*/ | ||
export type IGetThemeDefault = () => string; | ||
/** | ||
* Settings to initialize a new instance of an IAudioPlayr. | ||
@@ -44,248 +42,87 @@ */ | ||
/** | ||
* The names of the audio files to be preloaded for on-demand playback. | ||
* Creates a new sound. | ||
*/ | ||
library?: ILibrarySettings; | ||
createSound?: ICreateSound; | ||
/** | ||
* The directory in which all sub-directories of audio files are stored. | ||
* Transforms provided names into file names. | ||
*/ | ||
directory?: string; | ||
nameTransform?: INameTransform; | ||
/** | ||
* The allowed filetypes for each audio file. Each of these should have a | ||
* directory of their name under the main directory, which should contain | ||
* each file of the filetype. | ||
* Stores mute and volume status locally. | ||
*/ | ||
fileTypes?: string[]; | ||
/** | ||
* A storage container to store mute/volume status locally. | ||
*/ | ||
itemsHolder: IItemsHoldr; | ||
/** | ||
* A String or Function to get the default theme for playTheme calls. | ||
* Functions are called for a return value, and Strings are constant | ||
* (defaults to "Theme"). | ||
* | ||
*/ | ||
getThemeDefault?: string | (() => string); | ||
/** | ||
* A Number or Function to get the "local" volume for playLocal calls. | ||
* Functions are called for a return value, and Numbers are constant | ||
* (defaults to 1). | ||
* | ||
*/ | ||
getVolumeLocal?: number | (() => number); | ||
storage?: IAudioSettingsStorage; | ||
} | ||
/** | ||
* An audio playback manager for persistent and on-demand themes and sounds. | ||
* Playback for persistent and on-demand sounds and themes. | ||
*/ | ||
export interface IAudioPlayr { | ||
/** | ||
* @returns The listing of <audio> Elements, keyed by name. | ||
*/ | ||
getLibrary(): {}; | ||
/** | ||
* @returns The allowed filetypes for audio files. | ||
*/ | ||
getFileTypes(): string[]; | ||
/** | ||
* @returns The currently playing <audio> Elements, keyed by name. | ||
*/ | ||
getSounds(): {}; | ||
/** | ||
* @returns The current playing theme's <audio> Element. | ||
*/ | ||
getTheme(): HTMLAudioElement | undefined; | ||
/** | ||
* @returns The name of the currently playing theme. | ||
*/ | ||
getThemeName(): string | undefined; | ||
/** | ||
* @returns The directory under which all filetype directories are to be located. | ||
*/ | ||
getDirectory(): string; | ||
/** | ||
* @returns The current volume as a Number in [0,1], retrieved by the ItemsHoldr. | ||
*/ | ||
getVolume(): number; | ||
/** | ||
* Sets the current volume. If not muted, all sounds will have their volume | ||
* updated. | ||
* Gets whether this is muted. | ||
* | ||
* @param volume A Number in [0,1] to set as the current volume. | ||
* @returns Whether this is now muted. | ||
*/ | ||
setVolume(volume: number): void; | ||
/** | ||
* @returns Whether this is currently muted. | ||
*/ | ||
getMuted(): boolean; | ||
/** | ||
* Calls either setMutedOn or setMutedOff as is appropriate. | ||
* Gets the global sound volume in [0, 1]. | ||
* | ||
* @param muted The new status for muted. | ||
* @returns Global sound volume in [0, 1]. | ||
*/ | ||
setMuted(muted: boolean): void; | ||
getVolume(): number; | ||
/** | ||
* Calls either setMutedOn or setMutedOff to toggle whether this is muted. | ||
*/ | ||
toggleMuted(): void; | ||
/** | ||
* Sets volume to 0 in all currently playing sounds and stores the muted | ||
* status as on in the internal ItemsHoldr. | ||
*/ | ||
setMutedOn(): void; | ||
/** | ||
* Sets sound volumes to their actual volumes and stores the muted status | ||
* as off in the internal ItemsHoldr. | ||
*/ | ||
setMutedOff(): void; | ||
/** | ||
* @returns The Function or Number used as the volume setter for local sounds. | ||
*/ | ||
getGetVolumeLocal(): {}; | ||
/** | ||
* @param getVolumeLocal A new Function or Number to use as the volume setter | ||
* for local sounds. | ||
*/ | ||
setGetVolumeLocal(getVolumeLocalNew: {}): void; | ||
/** | ||
* @returns The Function or String used to get the default theme for playTheme. | ||
*/ | ||
getGetThemeDefault(): {}; | ||
/** | ||
* @param getThemeDefaultNew A new Function or String to use as the source for | ||
* theme names in default playTheme calls. | ||
*/ | ||
setGetThemeDefault(getThemeDefaultNew: {}): void; | ||
/** | ||
* Plays the sound of the given name. | ||
* Sets whether this is muted. | ||
* | ||
* @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. | ||
* @param muted Whether this is now muted. | ||
*/ | ||
play(name: string): HTMLAudioElement; | ||
setMuted(muted: boolean): Promise<void>; | ||
/** | ||
* Pauses all currently playing sounds. | ||
*/ | ||
pauseAll(): void; | ||
/** | ||
* Un-pauses (resumes) all currently paused sounds. | ||
*/ | ||
resumeAll(): void; | ||
/** | ||
* Pauses the currently playing theme, if there is one. | ||
*/ | ||
pauseTheme(): void; | ||
/** | ||
* Resumes the theme, if there is one and it's paused. | ||
*/ | ||
resumeTheme(): void; | ||
/** | ||
* Stops all sounds and any theme, and removes all references to them. | ||
*/ | ||
clearAll(): void; | ||
/** | ||
* Pauses and removes the theme, if there is one. | ||
*/ | ||
clearTheme(): void; | ||
/** | ||
* "Local" version of play that changes the output sound's volume depending | ||
* on the result of a getVolumeLocal call. | ||
* Sets the global sound volume in [0, 1]. | ||
* | ||
* @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. | ||
* @param volume New global sound volume in [0, 1]. | ||
*/ | ||
playLocal(name: string, location?: {}): HTMLAudioElement; | ||
setVolume(volume: number): Promise<void>; | ||
/** | ||
* Pauses any previously playing theme and starts playback of a new theme. | ||
* Plays a sound. | ||
* | ||
* @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. | ||
* @param name Name of a sound. | ||
* @param settings Any settings for the sound. | ||
* @returns A Promise for playing the sound. | ||
*/ | ||
playTheme(name?: string, loop?: boolean): HTMLAudioElement; | ||
play(name: string, settings?: IPlaySettings): Promise<void>; | ||
/** | ||
* Wrapper around playTheme that plays a sound, then a theme. This is | ||
* implemented using an event listener on the sound's ending. | ||
* Pauses all sounds. | ||
* | ||
* @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. | ||
* @returns A Promise for pausing all sounds. | ||
*/ | ||
playThemePrefixed(prefix?: string, name?: string, loop?: boolean): HTMLAudioElement; | ||
pauseAll(): Promise<void>; | ||
/** | ||
* 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. | ||
* Un-pauses (plays) all sounds. | ||
* | ||
* @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. | ||
* @returns A Promise for resuming. | ||
*/ | ||
addEventListener(name: string, event: string, callback: {}): void; | ||
resumeAll(): Promise<void>; | ||
/** | ||
* Clears all events added by this.addEventListener to a sound under a given | ||
* event. | ||
* Stops all sounds and any theme. | ||
* | ||
* @param name The name of the sound. | ||
* @param event The name of the event, such as "ended". | ||
* @returns A Promise for stopping all sounds. | ||
*/ | ||
removeEventListeners(name: string, event: string): void; | ||
stopAll(): Promise<void>; | ||
/** | ||
* Adds an event listener to a sound. If the sound doesn't exist or has | ||
* finished playing, it's called immediately. | ||
* Checks whether a sound under the alias exists. | ||
* | ||
* @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. | ||
* @param alias Alias to check under. | ||
* @param name Name the sound must have, if not the same as alias. | ||
* @returns Whether a sound exists under the alias. | ||
*/ | ||
addEventImmediate(name: string, event: string, callback: {}): void; | ||
hasSound(alias: string, name?: string): boolean; | ||
} |
export * from "./AudioPlayr"; | ||
export * from "./IAudioPlayr"; | ||
export * from "./Sound"; | ||
export * from "./Storage"; |
@@ -1,2 +0,2 @@ | ||
define(["require", "exports", "./AudioPlayr"], function (require, exports, AudioPlayr_1) { | ||
define(["require", "exports", "./AudioPlayr", "./Sound", "./Storage"], function (require, exports, AudioPlayr_1, Sound_1, Storage_1) { | ||
"use strict"; | ||
@@ -8,2 +8,4 @@ function __export(m) { | ||
__export(AudioPlayr_1); | ||
__export(Sound_1); | ||
__export(Storage_1); | ||
}); |
export * from "./AudioPlayr"; | ||
export * from "./IAudioPlayr"; | ||
export * from "./Sound"; | ||
export * from "./Storage"; |
@@ -5,2 +5,3 @@ { | ||
"experimentalDecorators": true, | ||
"forceConsistentCasingInFileNames": true, | ||
"jsx": "react", | ||
@@ -7,0 +8,0 @@ "lib": ["dom", "es2015.collection", "es2015.promise", "es5"], |
{ | ||
"extends": "./node_modules/shenanigans-manager/setup/tslint.json", | ||
"linterOptions": { | ||
"exclude": [ | ||
"./node_modules/**/*" | ||
] | ||
}, | ||
"rules": { | ||
"ban-types": false, | ||
"completed-docs": false, | ||
"no-any": false, | ||
"no-non-null-assertion": false, | ||
"no-unsafe-any": false, | ||
@@ -9,0 +10,0 @@ "strict-boolean-expressions": false |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
0
-100%212
393.02%730855
-58.56%54
-3.57%3304
-10.14%- Removed
- Removed