Refactored app to include all the ink.js typography.

This commit is contained in:
2025-04-01 22:43:19 +00:00
parent 89b8cf8311
commit 53f9eb9265
16 changed files with 3940 additions and 858 deletions
+193
View File
@@ -0,0 +1,193 @@
/**
* AudioManager Module
* Manages loading and playback of non-TTS audio effects triggered by tags.
*/
export class AudioManager {
constructor() {
this.sounds = new Map();
this.currentAudio = null;
this.currentLoop = null;
}
/**
* Load a sound file
* @param {string} id - The identifier for the sound
* @param {string} url - The URL of the sound file
* @returns {Promise} A promise that resolves when the sound is loaded
*/
loadSound(id, url) {
return new Promise((resolve, reject) => {
const audio = new Audio(url);
audio.addEventListener('canplaythrough', () => {
this.sounds.set(id, audio);
resolve(audio);
}, { once: true });
audio.addEventListener('error', (error) => {
reject(error);
});
audio.load();
});
}
/**
* Play a sound
* @param {string} id - The identifier for the sound
* @param {boolean} loop - Whether to loop the sound
* @returns {HTMLAudioElement|null} The audio element or null if not found
*/
playSound(id, loop = false) {
const audio = this.sounds.get(id);
if (!audio) {
console.warn(`Sound with id "${id}" not found.`);
return null;
}
if (loop) {
if (this.currentLoop) {
this.currentLoop.pause();
this.currentLoop.currentTime = 0;
}
audio.loop = true;
this.currentLoop = audio;
} else {
if (this.currentAudio) {
this.currentAudio.pause();
this.currentAudio.currentTime = 0;
}
this.currentAudio = audio;
}
audio.play().catch(error => {
console.error('Error playing audio:', error);
});
return audio;
}
/**
* Play a sound from a URL directly (without preloading)
* @param {string} url - The URL of the sound file
* @param {boolean} loop - Whether to loop the sound
* @returns {HTMLAudioElement} The audio element
*/
playSoundFromUrl(url, loop = false) {
if (loop) {
if (this.currentLoop) {
this.currentLoop.pause();
this.currentLoop.removeAttribute('src');
this.currentLoop.load();
}
this.currentLoop = new Audio(url);
this.currentLoop.loop = true;
this.currentLoop.play().catch(error => {
console.error('Error playing audio loop:', error);
});
return this.currentLoop;
} else {
if (this.currentAudio) {
this.currentAudio.pause();
this.currentAudio.removeAttribute('src');
this.currentAudio.load();
}
this.currentAudio = new Audio(url);
this.currentAudio.play().catch(error => {
console.error('Error playing audio:', error);
});
return this.currentAudio;
}
}
/**
* Stop a specific sound
* @param {string} id - The identifier for the sound
*/
stopSound(id) {
const audio = this.sounds.get(id);
if (audio) {
audio.pause();
audio.currentTime = 0;
}
}
/**
* Stop all sounds
*/
stopAllSounds() {
if (this.currentAudio) {
this.currentAudio.pause();
this.currentAudio.currentTime = 0;
this.currentAudio = null;
}
if (this.currentLoop) {
this.currentLoop.pause();
this.currentLoop.currentTime = 0;
this.currentLoop = null;
}
this.sounds.forEach(audio => {
audio.pause();
audio.currentTime = 0;
});
}
/**
* Set the volume for all sounds
* @param {number} volume - The volume level (0.0 to 1.0)
*/
setVolume(volume) {
this.sounds.forEach(audio => {
audio.volume = volume;
});
if (this.currentAudio) {
this.currentAudio.volume = volume;
}
if (this.currentLoop) {
this.currentLoop.volume = volume;
}
}
/**
* Check if a sound is currently playing
* @param {string} id - The identifier for the sound
* @returns {boolean} Whether the sound is playing
*/
isPlaying(id) {
const audio = this.sounds.get(id);
return audio ? !audio.paused : false;
}
/**
* Fade out the current audio
* @param {number} duration - The duration of the fade in milliseconds
* @returns {Promise} A promise that resolves when the fade is complete
*/
fadeOutCurrentAudio(duration = 1000) {
return new Promise((resolve) => {
if (!this.currentAudio || this.currentAudio.paused) {
resolve();
return;
}
const audio = this.currentAudio;
const initialVolume = audio.volume;
const volumeStep = initialVolume / (duration / 50);
let currentVolume = initialVolume;
const fadeInterval = setInterval(() => {
currentVolume -= volumeStep;
if (currentVolume <= 0) {
clearInterval(fadeInterval);
audio.pause();
audio.currentTime = 0;
audio.volume = initialVolume; // Reset volume for future use
resolve();
} else {
audio.volume = currentVolume;
}
}, 50);
});
}
}