@nent/core
Version:
991 lines (976 loc) • 32.8 kB
JavaScript
/*!
* NENT 2022
*/
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const index$1 = require('./index-1829aebc.js');
const index = require('./index-637e8c28.js');
const logging = require('./logging-37c154cf.js');
const state = require('./state-f97ff0e6.js');
const factory = require('./factory-0d7ddff9.js');
const interfaces$2 = require('./interfaces-1da33056.js');
const interfaces = require('./interfaces-f9ceab29.js');
const values = require('./values-b2399e33.js');
const tracks = require('./tracks-10e1ccb7.js');
const interfaces$1 = require('./interfaces-95d0415a.js');
const promises = require('./promises-463f4e01.js');
const state$1 = require('./state-9c7a0826.js');
require('./index-96f3ab3f.js');
require('./mutex-7ae5ebad.js');
require('./memory-33fe6263.js');
/* It's a list of audio tracks that can be played, stopped, and destroyed */
class AudioList {
constructor() {
this.items = [];
}
/**
* It returns true if the length of the items array is equal to 0
* @returns The length of the items array.
*/
isEmpty() {
return this.items.length == 0;
}
/**
* If the length of the items array is greater than 0, return true
* @returns The length of the items array.
*/
hasItems() {
return this.items.length > 0;
}
/**
* Find the first item in the items array that has a trackId property that matches the trackId
* parameter. If no item is found, return null
* @param {string} trackId - The trackId of the track you want to find.
* @returns The trackId of the track that is being searched for.
*/
findTrack(trackId) {
return this.items.find(a => a.trackId == trackId) || null;
}
/**
* It stops all the items in the items array.
*/
stop() {
this.items.forEach(t => {
if (t.playing())
t.stop();
});
}
/**
* It destroys all the items in the array.
*/
destroy() {
this.items.forEach(a => a.destroy());
}
/**
* It filters out the audio tracks that have a discard strategy that matches one of the reasons
* passed in
* @param {DiscardStrategy[]} reasons - DiscardStrategy[]
*/
discard(...reasons) {
const eligibleAudio = (audio) => !reasons.includes(audio.discard);
this.items = this.items.filter(i => eligibleAudio(i)) || null;
}
}
/* It's a wrapper around the Howler library that allows us to play audio files */
class AudioTrack {
/**
* It creates a new Howl instance, and assigns it to the sound property of the AudioTrack instance
* @param {AudioInfo} audio - AudioInfo
* @param {Listener} [onEnd] - A callback function that is called when the audio track ends.
*/
constructor(audio, onEnd) {
this.discard = interfaces.DiscardStrategy.route;
this.loop = false;
const { trackId, src, type, loop } = audio;
values.requireValue(trackId, 'trackId', 'n-audio: track');
values.requireValue(src, 'src', 'n-audio: track');
values.requireValue(type, 'type', 'n-audio: track');
Object.assign(this, audio, {
onEnd,
});
const sound = new Howl({
src,
loop: type === 'music' ? loop : false,
onload: () => {
var _a;
(_a = this.onLoad) === null || _a === void 0 ? void 0 : _a.call(this, this);
},
onend: () => {
onEnd === null || onEnd === void 0 ? void 0 : onEnd.call(this, this);
},
onloaderror: (_id, error) => {
logging.warn(`n-audio: An error occurred for audio track ${trackId}: ${error}`);
onEnd === null || onEnd === void 0 ? void 0 : onEnd.call(this);
},
onplayerror: () => {
sound.once('unlock', () => {
sound.play();
});
},
preload: true,
autoplay: false,
html5: false,
});
this.sound = sound;
}
/**
* It returns a boolean value that indicates whether the sound is currently playing
* @returns The sound is being returned.
*/
playing() {
return this.sound.playing();
}
/**
* It returns true if the volume of the sound is 0, and false otherwise
* @returns The volume of the sound.
*/
muted() {
return this.sound.volume() == 0;
}
/**
* It returns the state of the sound
* @returns The state of the sound object.
*/
state() {
return this.sound.state();
}
/**
* If the sound is loaded, play it. If it's loading, wait for it to load and then play it
*/
start() {
if (this.sound.state() === 'loaded') {
this.play();
}
else if (this.sound.state() === 'loading') {
this.sound.once(interfaces.AUDIO_EVENTS.Loaded, () => {
this.play();
});
}
}
/**
* "Play the sound, but fade it in over half a second."
*
* The first line sets the volume to 0. This is important because if we don't do this, the sound will
* play at full volume for a split second before fading in
*/
play() {
var _a;
this.sound.volume(0);
this.sound.play();
this.sound.fade(0, ((_a = window.Howler) === null || _a === void 0 ? void 0 : _a.volume()) || 0.5, 500);
}
/**
* It pauses the sound
*/
pause() {
this.sound.pause();
}
/**
* It fades the volume of the sound to 0 over 500 milliseconds, then stops the sound
*/
stop() {
var _a;
this.sound.fade(((_a = window.Howler) === null || _a === void 0 ? void 0 : _a.volume()) || 0.5, 0, 500);
this.sound.stop();
}
/**
* It sets the mute property of the sound object to the value of the mute parameter
* @param {boolean} mute - boolean - true to mute, false to unmute
*/
mute(mute) {
this.sound.mute(mute);
}
/**
* The resume function plays the sound
*/
resume() {
this.sound.play();
}
/**
* This function sets the volume of the sound to the number passed in
* @param {number} set - The volume you want to set the sound to.
*/
setVolume(set) {
this.sound.volume(set);
}
/**
* This function seeks to a specific time in the audio file
* @param {number} time - The time in seconds to seek to.
*/
seek(time) {
this.sound.seek(time);
}
/**
* It unloads the sound from the game.
*/
destroy() {
this.sound.unload();
}
}
/* It loads audio tracks and keeps them in memory until they are no longer needed */
class AudioLoader extends AudioList {
/**
* It loads an audio track into the player
* @param {AudioInfo} info - AudioInfo - This is the audio info object that contains the track id,
* the track name, the track url, and the track duration.
* @param {Listener} onChanged - This is a callback function that is called when the track is loaded.
* @returns The track is being returned.
*/
load(info, onChanged) {
const found = this.findTrack(info.trackId);
if (found)
return;
const track = new AudioTrack(info, () => {
if (track.discard != interfaces.DiscardStrategy.none) {
track === null || track === void 0 ? void 0 : track.destroy();
}
onChanged();
});
this.items.push(track);
}
}
/* It's a list of audio tracks that can be queued and played in order */
class AudioQueue extends AudioList {
/**
* It adds a new track to the queue
* @param {AudioInfo} info - AudioInfo - This is the audio info object that is passed to the
* queueAudio function.
* @param {Listener} onEnd - Listener - This is a function that is called when the track is finished
* playing.
* @returns The length of the array.
*/
queueAudio(info, onEnd) {
const found = this.findTrack(info.trackId);
if (found)
return;
const track = new AudioTrack(info, () => {
if (track.discard != interfaces.DiscardStrategy.none) {
track === null || track === void 0 ? void 0 : track.destroy();
}
onEnd();
});
return this.items.push(track);
}
/**
* It takes an AudioTrack and a Listener, and returns the index of the inserted track
* @param {AudioTrack} track - AudioTrack - The track to insert
* @param {Listener} onEnd - Listener
* @returns The index of the inserted item.
*/
insertTrack(track, onEnd) {
const originalOnEnd = track.onEnd;
track.onEnd = () => {
originalOnEnd === null || originalOnEnd === void 0 ? void 0 : originalOnEnd.call(track, track);
onEnd();
};
return this.items.unshift(track);
}
/**
* "If there are items in the queue, return the first one, otherwise return null."
*
* The first line of the function is a TypeScript type annotation. It's saying that the function will
* return either an AudioTrack or null
* @returns The first item in the array or null if the array is empty.
*/
getNext() {
return this.items.shift() || null;
}
}
/* Exporting the class so that it can be used in other files. */
class PlayerBase {
constructor() {
this.active = null;
}
/**
* If the active audio is not null, pause it
*/
pause() {
var _a;
(_a = this.active) === null || _a === void 0 ? void 0 : _a.pause();
}
/**
* If the active audio is not null, then call the play() method on it
*/
play() {
var _a;
(_a = this.active) === null || _a === void 0 ? void 0 : _a.play();
}
/**
* If the active audio is not null, then call the stop() method on it
*/
stop() {
var _a;
(_a = this.active) === null || _a === void 0 ? void 0 : _a.stop();
}
/**
* If the active audio is not null, then call the resume() method on it
*/
resume() {
var _a;
(_a = this.active) === null || _a === void 0 ? void 0 : _a.resume();
}
/**
* It sets the mute state of the active player
* @param {boolean} mute - boolean - Whether to mute or unmute the video
*/
mute(mute) {
var _a;
(_a = this.active) === null || _a === void 0 ? void 0 : _a.mute(mute);
}
/**
* If the active strategy is being discarded, destroy it
* @param {DiscardStrategy[]} reasons - DiscardStrategy[]
*/
discard(...reasons) {
var _a;
if (this.active && reasons.includes(this.active.discard)) {
(_a = this.active) === null || _a === void 0 ? void 0 : _a.destroy();
this.active = null;
}
}
}
/* It's a class that manages the loading, playing, and queuing of audio */
class MusicPlayer extends PlayerBase {
/**
* The constructor function is a special function that is called when an object is created from a
* class
* @param {Listener} changed - Listener - This is the listener that will be called when the value of
* the property changes.
*/
constructor(changed) {
super();
this.changed = changed;
this.loader = new AudioLoader();
this.queue = new AudioQueue();
}
/**
* It queues an audio file, and if there is no active audio file, it plays the queued audio file
* @param {AudioInfo} info - AudioInfo - The audio info of the audio you want to queue.
*/
queueAudio(info) {
var _a;
this.queue.queueAudio(info, () => {
var _a;
this.active = this.queue.getNext();
(_a = this.active) === null || _a === void 0 ? void 0 : _a.play();
this.changed();
});
if (this.active == null) {
this.active = this.queue.getNext();
(_a = this.active) === null || _a === void 0 ? void 0 : _a.play();
}
this.changed();
}
/**
* It loads an audio file, and then calls the changed function
* @param {AudioInfo} info - AudioInfo
*/
load(info) {
this.loader.load(info, this.changed);
this.changed();
}
/**
* It plays a track
* @param {string} trackId - The id of the track to play
*/
async playTrack(trackId) {
var _a;
const track = this.loader.findTrack(trackId);
if (track) {
this.loader.stop();
this.queue.insertTrack(track, () => {
var _a;
this.active = this.queue.getNext();
(_a = this.active) === null || _a === void 0 ? void 0 : _a.play();
this.changed();
});
this.discard(interfaces.DiscardStrategy.next);
this.active = this.queue.getNext();
(_a = this.active) === null || _a === void 0 ? void 0 : _a.play();
await tracks.markTrack(trackId);
this.changed();
}
}
/**
* If the loader is discarded, discard the queue and notify the world that the queue has changed.
* @param {DiscardStrategy[]} reasons - DiscardStrategy[]
*/
discard(...reasons) {
super.discard(...reasons);
this.loader.discard(...reasons);
this.queue.discard(...reasons);
this.changed();
}
/**
* If the active audio is not null, or the loader has items, or the queue has items, then return true
* @returns A boolean value.
*/
hasAudio() {
return (this.active != null ||
this.loader.hasItems() ||
this.queue.hasItems());
}
/**
* It destroys the active scene, the loader, and the queue.
*/
destroy() {
var _a;
(_a = this.active) === null || _a === void 0 ? void 0 : _a.destroy();
this.loader.destroy();
this.queue.destroy();
}
}
/* It's a wrapper around an AudioLoader that keeps track of the currently playing track and allows you
to play a new track */
class SoundPlayer extends PlayerBase {
/**
* The constructor function is a special function that is called when an object is created from a
* class
* @param {Listener} changed - Listener - This is the listener that will be called when the value of
* the property changes.
*/
constructor(changed) {
super();
this.changed = changed;
this.loader = new AudioLoader();
}
/**
* It loads an audio file, and when it's done loading, it sets the active audio to null and calls the
* changed function
* @param {AudioInfo} info - AudioInfo
*/
load(info) {
this.loader.load(info, () => {
this.active = null;
this.changed();
});
this.changed();
}
/**
* It plays a track
* @param {string} trackId - The id of the track to play.
*/
async playTrack(trackId) {
const track = this.loader.findTrack(trackId);
if (track) {
this.loader.stop();
this.active = null;
this.discard(interfaces.DiscardStrategy.next);
this.active = track;
track.play();
await tracks.markTrack(trackId);
this.changed();
}
}
/**
* If the active strategy is being discarded, destroy it and set it to null
* @param {DiscardStrategy[]} reasons - DiscardStrategy[]
*/
discard(...reasons) {
var _a;
if (this.active && reasons.includes(this.active.discard)) {
(_a = this.active) === null || _a === void 0 ? void 0 : _a.destroy();
this.active = null;
}
this.loader.discard(...reasons);
this.changed();
}
/**
* If the active item is not null, or the loader has items, then return true
* @returns A boolean value.
*/
hasAudio() {
return this.active != null || this.loader.hasItems();
}
/**
* It destroys the active scene and the loader.
*/
destroy() {
var _a;
(_a = this.active) === null || _a === void 0 ? void 0 : _a.destroy();
this.loader.destroy();
}
}
/* It listens to the audio listener and emits a `changed` event when the audio listener changes */
class AudioDataProvider {
/**
* A constructor function that takes in an audioListener as a parameter. It then creates a new
* EventEmitter and assigns it to the changed property. It then creates a change function that is
* debounced and emits a changed event. It then subscribes to the audioListener's changed event and
* calls the change function.
* @param {AudioActionListener} audioListener - AudioActionListener - this is the service that
* listens for changes to the audio data.
*/
constructor(audioListener) {
this.audioListener = audioListener;
this.changed = new index.EventEmitter();
const change = promises.debounce(1000, () => {
this.changed.emit(interfaces$1.DATA_EVENTS.DataChanged, {
provider: 'audio',
});
}, true);
this.listenerSubscription = this.audioListener.changed.on('changed', () => {
change();
});
}
async get(key) {
switch (key) {
// Global
case 'hasAudio':
return this.audioListener.hasAudio().toString();
case 'isPlaying':
return this.audioListener.isPlaying().toString();
// Music files
case 'loadedMusic':
return this.audioListener.music
? JSON.stringify(this.audioListener.music.loader.items)
: null;
case 'queuedMusic':
return this.audioListener.music
? JSON.stringify(this.audioListener.music.queue.items)
: null;
case 'currentMusic':
return this.audioListener.music.active
? JSON.stringify(this.audioListener.music.active)
: null;
// Sound files
case 'loadedSounds':
return this.audioListener.sound.loader.items
? JSON.stringify(this.audioListener.sound.loader.items)
: null;
case 'currentSound':
return this.audioListener.sound.active
? JSON.stringify(this.audioListener.sound.active)
: null;
default:
return null;
}
}
async set(_key, _value) {
// do nothing
}
/**
* It destroys the listener subscription.
*/
destroy() {
this.listenerSubscription();
}
}
/* It listens for audio commands and events, and then plays the audio */
class AudioActionListener {
/**
* "This function is called when the class is instantiated, and it sets up the music and sound
* players, and subscribes to the event bus."
*
* The first thing we do is create a new EventEmitter called "changed". This is used to notify the
* rest of the app that the audio state has changed
* @param {Window} window - Window - the window object
* @param {IEventEmitter} eventBus - This is the event bus that the game uses to communicate with the
* game engine.
* @param {IEventEmitter} actionBus - IEventEmitter - this is the event bus that is used to send
* actions to the game.
* @param {boolean} enableDataProvider - boolean - whether to enable the data provider
* @param {boolean} [debug=false] - boolean - if true, will log all events to the console
*/
constructor(window, eventBus, actionBus, enableDataProvider, debug = false) {
this.window = window;
this.eventBus = eventBus;
this.actionBus = actionBus;
this.enableDataProvider = enableDataProvider;
this.debug = debug;
this.muted = false;
this.volume = 0;
this.changed = new index.EventEmitter();
this.music = new MusicPlayer(() => {
this.changed.emit('changed');
});
this.sound = new SoundPlayer(() => {
this.changed.emit('changed');
});
this.volume = 1;
this.stateEnabledSubscription = state.onChange('audioEnabled', enabled => {
if (enabled)
this.subscribe();
else
this.unsubscribe();
});
if (state.state.audioEnabled)
this.subscribe();
}
subscribe() {
const enableDataProvider = () => {
if (this.enableDataProvider && this.provider == undefined) {
this.provider = new AudioDataProvider(this);
factory.addDataProvider('audio', this.provider);
}
};
if (state.state.audioEnabled)
enableDataProvider();
this.stateDataSubscription = state.onChange('dataEnabled', enabled => {
var _a;
if (enabled) {
enableDataProvider();
}
else {
factory.removeDataProvider('audio');
(_a = this.provider) === null || _a === void 0 ? void 0 : _a.destroy();
this.provider = undefined;
}
});
if (state$1.state.muted)
this.mute();
this.stateMutedSubscription = state$1.onChange('muted', mute => {
if (mute)
this.mute();
else
this.play();
});
this.actionSubscription = this.actionBus.on(interfaces.AUDIO_TOPIC, async (ev) => {
var _a, _b;
logging.debugIf(this.debug, `audio-listener: action received ${ev.command}${((_a = ev.data) === null || _a === void 0 ? void 0 : _a.type) || ''}:${((_b = ev.data) === null || _b === void 0 ? void 0 : _b.trackId) || ''}`);
await this.commandReceived(ev.command, ev.data);
});
this.eventSubscription = this.eventBus.on(interfaces$2.ROUTE_EVENTS.RouteChanged, () => {
logging.debugIf(this.debug, 'audio-listener: route changed received');
this.music.discard(interfaces.DiscardStrategy.route, interfaces.DiscardStrategy.next);
this.sound.discard(interfaces.DiscardStrategy.route, interfaces.DiscardStrategy.next);
});
this.changed.emit('changed');
}
unsubscribe() {
var _a, _b, _c, _d;
(_a = this.eventSubscription) === null || _a === void 0 ? void 0 : _a.call(this);
(_b = this.actionSubscription) === null || _b === void 0 ? void 0 : _b.call(this);
(_c = this.stateDataSubscription) === null || _c === void 0 ? void 0 : _c.call(this);
(_d = this.stateMutedSubscription) === null || _d === void 0 ? void 0 : _d.call(this);
this.music.destroy();
this.sound.destroy();
if (this.provider) {
factory.removeDataProvider('audio');
this.provider.destroy();
}
this.changed.emit('changed');
}
// Public Members
/**
* `return Boolean(this.music.active?.playing() || this.sound.active?.playing() || false)`
*
* The `Boolean()` function is a JavaScript function that returns a boolean value.
*
* The `this.music.active?.playing()` is a TypeScript null-safe operator. It's a way to check if the
* `active` property of the `music` object is not null. If it's not null, then it will return the
* `playing()` function. If it is null, then it will return null.
*
* The `this.sound.active?.playing()` is the same as the above, but for the `sound` object.
*
* The `||` is a JavaScript operator that returns the first value that is not null.
*
* The `false` is a JavaScript boolean value.
*
* So, if the `music` object is not
* @returns Boolean
*/
isPlaying() {
var _a, _b;
return Boolean(((_a = this.music.active) === null || _a === void 0 ? void 0 : _a.playing()) ||
((_b = this.sound.active) === null || _b === void 0 ? void 0 : _b.playing()) ||
false);
}
/**
* It returns true if the music or sound has audio.
* @returns A boolean value.
*/
hasAudio() {
return this.music.hasAudio() || this.sound.hasAudio();
}
/**
* It pauses the music and sound, and then emits a changed event
*/
pause() {
this.music.pause();
this.sound.pause();
this.changed.emit('changed');
}
/**
* If the audio is enabled, play the music and sound, and emit a changed event
* @returns the value of the function.
*/
play() {
if (!state.state.audioEnabled)
return;
this.music.play();
this.sound.play();
this.changed.emit('changed');
}
/**
* It stops the music and sound, and then emits a changed event
*/
stop() {
this.music.stop();
this.sound.stop();
this.changed.emit('changed');
}
/**
* It resumes the music and sound effects if audio is enabled
* @returns the value of the expression.
*/
resume() {
if (!state.state.audioEnabled)
return;
this.music.resume();
this.sound.resume();
this.changed.emit('changed');
}
/**
* It sets the audioState.muted property to the value of the mute parameter, and then sets the muted
* property of the music and sound objects to the value of the mute parameter, and then sets the
* muted property of the AudioManager object to the value of the mute parameter, and then emits the
* changed event
* @param mute - boolean - Whether to mute or unmute the audio.
*/
mute(mute = !this.muted) {
state$1.state.muted = mute;
this.music.mute(mute);
this.sound.mute(mute);
this.muted = mute;
this.changed.emit('changed');
}
/**
* If the current track is the one we want to seek, then seek it
* @param {AudioType} type - AudioType - This is the type of audio you want to seek.
* @param {string} trackId - The trackId of the audio track to seek.
* @param {number} seek - The time in seconds to seek to.
*/
seek(type, trackId, seek) {
const current = type == interfaces.AudioType.music ? this.music.active : this.sound.active;
if (current && current.trackId === trackId) {
current.seek(seek);
this.changed.emit('changed');
}
}
setVolume(value) {
var _a;
(_a = this.window.Howler) === null || _a === void 0 ? void 0 : _a.volume(value);
this.muted = value == 0;
this.volume = value;
this.changed.emit('changed');
}
// Private members
async commandReceived(command, data) {
switch (command) {
case interfaces.AUDIO_COMMANDS.load: {
const audio = data;
const { type } = audio;
if (type == interfaces.AudioType.sound) {
this.sound.load(audio);
}
else if (type == interfaces.AudioType.music) {
this.music.load(audio);
}
else {
return;
}
this.eventBus.emit(interfaces.AUDIO_TOPIC, interfaces.AUDIO_EVENTS.Loaded, audio.trackId);
break;
}
case interfaces.AUDIO_COMMANDS.play: {
const audio = data;
const { type, trackId, src } = audio;
if (type == interfaces.AudioType.music) {
if (src)
this.music.load(audio);
this.music.playTrack(trackId);
}
else {
if (src)
this.sound.load(audio);
this.sound.playTrack(trackId);
}
this.eventBus.emit(interfaces.AUDIO_TOPIC, interfaces.AUDIO_EVENTS.Played, trackId);
break;
}
case interfaces.AUDIO_COMMANDS.queue: {
const audio = data;
const { type, trackId } = audio;
if (type != interfaces.AudioType.music)
return;
this.music.queueAudio(audio);
this.eventBus.emit(interfaces.AUDIO_TOPIC, interfaces.AUDIO_EVENTS.Queued, trackId);
break;
}
case interfaces.AUDIO_COMMANDS.start: {
const audio = data;
const { type, trackId } = audio;
if (type == interfaces.AudioType.music) {
await this.music.playTrack(trackId);
}
else if (type == interfaces.AudioType.sound) {
await this.sound.playTrack(trackId);
}
else {
return;
}
this.eventBus.emit(interfaces.AUDIO_TOPIC, interfaces.AUDIO_EVENTS.Started, trackId);
break;
}
case interfaces.AUDIO_COMMANDS.pause: {
this.pause();
break;
}
case interfaces.AUDIO_COMMANDS.resume: {
this.resume();
break;
}
case interfaces.AUDIO_COMMANDS.mute: {
const { value } = data;
this.mute(value);
break;
}
case interfaces.AUDIO_COMMANDS.seek: {
const audio = data;
const { type, trackId, value } = audio;
if (trackId) {
this.seek(type, trackId, value);
}
break;
}
case interfaces.AUDIO_COMMANDS.stop: {
this.stop();
break;
}
case interfaces.AUDIO_COMMANDS.volume: {
const { value } = data;
this.setVolume(value);
break;
}
}
}
/**
* It unsubscribes from the stateEnabledSubscription and stateMutedSubscription.
*/
destroy() {
this.unsubscribe();
this.stateEnabledSubscription();
this.stateMutedSubscription();
}
}
const audioCss = ":host{--display:inline-block;--width:200px;--color:white;--background-color:#000;--border:1px solid white;--fill:white;--icon-size:1rem}:host(.hidden){display:none}div{display:var(--display);max-width:var(--width);background-color:var(--background-color);border:var(--border);fill:var(--fill);padding:0.5em;color:var(--color)}.button{cursor:pointer}.button svg{display:flex;height:var(--icon-size);width:var(--icon-size)}p{margin:0}";
const Audio = class {
constructor(hostRef) {
index$1.registerInstance(this, hostRef);
this.loaded = false;
this.error = null;
this.stats = {
m: 0,
ml: 0,
mq: 0,
s: 0,
sl: 0,
};
/**
* The Howler.js Script Reference
*/
this.howlerVersion = '2.2.3';
/**
* The display mode enabled shows player state and stats.
* No track information or duration is to be displayed.
*/
this.display = false;
/**
* Use debug for verbose logging. Useful for figuring
* things out.
*/
this.debug = false;
/**
* Experimental support for providing audio-data in the
* data-provider system.
*/
this.dataProvider = false;
}
enableAudio() {
state.state.audioEnabled = true;
}
async componentWillLoad() {
logging.debugIf(this.debug, 'n-audio: loading');
state$1.state.debug = this.debug;
if (state$1.state.hasAudioComponent) {
this.error = `Duplicate Audio Player`;
return;
}
if (state.state.dataEnabled) {
const storage = (await factory.getDataProvider('storage'));
const storedValue = await (storage === null || storage === void 0 ? void 0 : storage.get('audio-enabled'));
if (storedValue) {
state.state.audioEnabled = storedValue != 'false';
}
state$1.state.muted = (await (storage === null || storage === void 0 ? void 0 : storage.get('audio-muted'))) == 'true';
this.audioStateSubscription = state$1.onChange('muted', async (m) => {
await (storage === null || storage === void 0 ? void 0 : storage.set('audio-muted', m.toString()));
});
this.commonStateSubscription = state.onChange('audioEnabled', async (m) => {
await (storage === null || storage === void 0 ? void 0 : storage.set('audio-enabled', m.toString()));
});
}
}
registerServices() {
if (this.loaded)
return;
logging.debugIf(this.debug, `n-audio: loading listener`);
this.actions = new AudioActionListener(window, index.eventBus, index.actionBus, this.dataProvider, this.debug);
this.actionSubscription = this.actions.changed.on('changed', () => {
this.updateState();
});
state$1.state.hasAudioComponent = true;
this.loaded = true;
}
updateState() {
var _a, _b, _c, _d, _e;
this.stats = {
m: ((_a = this.actions) === null || _a === void 0 ? void 0 : _a.music.active) ? 1 : 0,
ml: ((_b = this.actions) === null || _b === void 0 ? void 0 : _b.music.loader.items.length) || 0,
mq: ((_c = this.actions) === null || _c === void 0 ? void 0 : _c.music.queue.items.length) || 0,
s: ((_d = this.actions) === null || _d === void 0 ? void 0 : _d.sound.active) ? 1 : 0,
sl: ((_e = this.actions) === null || _e === void 0 ? void 0 : _e.sound.loader.items.length) || 0,
};
}
Error() {
return (index$1.h(index$1.Host, { hidden: !this.display }, index$1.h("div", null, index$1.h("p", { class: "error" }, this.error))));
}
Disabled() {
return (index$1.h(index$1.Host, { hidden: !this.display }, index$1.h("div", null, index$1.h("p", null, "Audio Disabled"), index$1.h("button", { onClick: () => {
this.enableAudio();
} }, "Enable"))));
}
Audio() {
if (!this.display)
return null;
return (index$1.h("div", null, index$1.h("p", null, "Audio ", this.actions.isPlaying() ? 'Playing' : 'Ready'), index$1.h("span", { title: "m=music s=sound l=loaded q=queued" }, "M:", this.stats.m, "\u00A0MQ:", this.stats.mq, "\u00A0ML:", this.stats.ml, "\u00A0S:", this.stats.s, "\u00A0SL:", this.stats.sl)));
}
NoAudio() {
if (!this.display)
return null;
return (index$1.h("div", null, index$1.h("p", null, "No Audio")));
}
render() {
var _a;
if (this.error)
return this.Error();
if (!state.state.audioEnabled)
return this.Disabled();
return (index$1.h(index$1.Host, { hidden: !this.display }, index$1.h("n-content-reference", { inline: true, onReferenced: () => {
this.registerServices();
}, "script-src": `https://cdn.jsdelivr.net/npm/howler@${this.howlerVersion}/dist/howler.core.min.js` }), ((_a = this.actions) === null || _a === void 0 ? void 0 : _a.hasAudio()) ? this.Audio() : this.NoAudio()));
}
disconnectedCallback() {
var _a, _b, _c, _d, _e;
state$1.state.hasAudioComponent = false;
(_a = this.stateSubscription) === null || _a === void 0 ? void 0 : _a.call(this);
(_b = this.actionSubscription) === null || _b === void 0 ? void 0 : _b.call(this);
(_c = this.audioStateSubscription) === null || _c === void 0 ? void 0 : _c.call(this);
(_d = this.commonStateSubscription) === null || _d === void 0 ? void 0 : _d.call(this);
(_e = this.actions) === null || _e === void 0 ? void 0 : _e.destroy();
}
get el() { return index$1.getElement(this); }
};
Audio.style = audioCss;
exports.n_audio = Audio;