From 4fdd8884a865b491e7cccb41ab9de89fd17213ad Mon Sep 17 00:00:00 2001 From: "Josefo (jozrftamson)" <107008476+jozrftamson@users.noreply.github.com> Date: Sat, 14 Mar 2026 02:14:28 +0100 Subject: [PATCH] feat: add mute controls for agent audio --- README.md | 6 ++++++ src/agent.ts | 39 ++++++++++++++++++++++++++++++++++++++- src/animator.ts | 23 ++++++++++++++++++++++- 3 files changed, 66 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5b57f61..53f84ec 100755 --- a/README.md +++ b/README.md @@ -78,6 +78,12 @@ agent.speak("When all else fails, bind some paper together. My name is Clippy.") // Speak with text-to-speech (uses Web Speech API) agent.speak("Hello! I'm here to help.", { tts: true }); +// Disable all animation sounds and text-to-speech +agent.mute(); + +// Re-enable audio later +agent.unmute(); + // Keep the balloon open until manually closed agent.speak("Read this carefully.", { hold: true }); diff --git a/src/agent.ts b/src/agent.ts index 6ac2205..80d23c1 100755 --- a/src/agent.ts +++ b/src/agent.ts @@ -30,6 +30,7 @@ export default class Agent { _pointerDownHandle: (e: MouseEvent | TouchEvent) => void; _dblClickHandle: () => void; _tts: { rate: number; pitch: number; voice: string } | undefined; + _muted: boolean; /** * @param {string} mapUrl - URL to the agent's sprite sheet @@ -53,6 +54,7 @@ export default class Agent { this._animator = new Animator(this._el, mapUrl, data, sounds); this._balloon = new Balloon(this._el); this._tts = data.tts; + this._muted = false; this._setupEvents(); } @@ -335,6 +337,7 @@ export default class Agent { stop() { this._queue.clear(); this._animator.exitAnimation(); + this._animator.stopAllSounds(); this._balloon.hide(); if (this._tts && "speechSynthesis" in window) { speechSynthesis.cancel(); @@ -358,6 +361,40 @@ export default class Agent { return this._animator.animations(); } + /** + * Mute animation sounds and text-to-speech + */ + mute() { + this.setMuted(true); + } + + /** + * Unmute animation sounds and text-to-speech + */ + unmute() { + this.setMuted(false); + } + + /** + * Enable or disable audio output + * @param {boolean} muted + */ + setMuted(muted: boolean) { + this._muted = muted; + this._animator.setMuted(muted); + if (muted && this._tts && "speechSynthesis" in window) { + speechSynthesis.cancel(); + } + } + + /** + * Check if audio output is muted + * @returns {boolean} + */ + isMuted() { + return this._muted; + } + /** * Play a random non-idle animation * @returns {boolean} @@ -660,7 +697,7 @@ export default class Agent { } _speakTTS(text: string) { - if (!this._tts || !("speechSynthesis" in window)) return; + if (this._muted || !this._tts || !("speechSynthesis" in window)) return; speechSynthesis.cancel(); const utterance = new SpeechSynthesisUtterance(text.replaceAll("\n", " ")); utterance.rate = this._tts.rate; diff --git a/src/animator.ts b/src/animator.ts index fd05e71..b067963 100755 --- a/src/animator.ts +++ b/src/animator.ts @@ -12,6 +12,7 @@ export default class Animator { _endCallback: Function | undefined; _started: boolean; _sounds: { [key: string]: HTMLAudioElement }; + _muted: boolean; currentAnimationName: string | undefined; _overlays: HTMLElement[]; _loop: number | undefined; @@ -34,6 +35,7 @@ export default class Animator { this._endCallback = undefined; this._started = false; this._sounds = {}; + this._muted = false; this.currentAnimationName = undefined; this.preloadSounds(sounds); this._overlays = [this._el]; @@ -192,6 +194,7 @@ export default class Animator { * @private */ _playSound() { + if (this._muted) return; let s = this._currentFrame.sound; if (!s) return; let audio = this._sounds[s]; @@ -253,6 +256,24 @@ export default class Animator { window.clearTimeout(this._loop); } + setMuted(muted: boolean) { + this._muted = muted; + if (muted) { + this.stopAllSounds(); + } + } + + isMuted() { + return this._muted; + } + + stopAllSounds() { + for (const key in this._sounds) { + this._sounds[key].pause(); + this._sounds[key].currentTime = 0; + } + } + /** * Resume animation */ @@ -266,8 +287,8 @@ export default class Animator { this._currentFrame = undefined; this._endCallback = undefined; this._started = false; + this.stopAllSounds(); for (const key in this._sounds) { - this._sounds[key].pause(); this._sounds[key].src = ""; } this._sounds = {};