From 730214d30b5dc7266f350bbc4a986afcbef70c78 Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Mon, 16 Feb 2026 09:29:02 +0100
Subject: [PATCH] feat: stop command (stopAllAudio), sequential audio queue
with stop support
Co-authored-by: Cursor
---
src/bot/audioProcedure.ts | 34 +++++++++++++++++++++++++++++++++-
src/bot/orchestrator.ts | 7 +++++++
2 files changed, 40 insertions(+), 1 deletion(-)
diff --git a/src/bot/audioProcedure.ts b/src/bot/audioProcedure.ts
index 8f32732..128fe17 100644
--- a/src/bot/audioProcedure.ts
+++ b/src/bot/audioProcedure.ts
@@ -22,6 +22,7 @@ export class AudioProcedure {
private _initScriptInjected: boolean = false;
private _audioQueue: Array<{ audioData: string; format: 'mp3' | 'wav' | 'pcm' }> = [];
private _isPlaying: boolean = false;
+ private _stopRequested: boolean = false;
constructor(page: Page, logger: Logger) {
this._page = page;
@@ -138,8 +139,9 @@ export class AudioProcedure {
private async _processAudioQueue(): Promise {
if (this._isPlaying) return;
this._isPlaying = true;
+ this._stopRequested = false;
- while (this._audioQueue.length > 0) {
+ while (this._audioQueue.length > 0 && !this._stopRequested) {
const item = this._audioQueue.shift()!;
try {
await this._playAudioInternal(item.audioData, item.format);
@@ -148,7 +150,37 @@ export class AudioProcedure {
}
}
+ if (this._stopRequested) {
+ this._audioQueue = [];
+ this._logger.info('Audio queue cleared due to stop request');
+ }
+
this._isPlaying = false;
+ this._stopRequested = false;
+ }
+
+ /**
+ * Stop all audio immediately: stop current playback and clear the queue.
+ * Called when a user says " STOP" or similar.
+ */
+ async stopAllAudio(): Promise {
+ this._logger.info('Stop all audio requested');
+ this._stopRequested = true;
+ this._audioQueue = [];
+
+ try {
+ await this._page.evaluate(() => {
+ const ctx = (window as any).__ttsAudioContext as AudioContext;
+ if (ctx) {
+ // Suspend immediately stops all audio output
+ ctx.suspend();
+ // Resume after a short delay so future audio can play
+ setTimeout(() => ctx.resume(), 100);
+ }
+ });
+ } catch {
+ // Page might not be ready
+ }
}
/**
diff --git a/src/bot/orchestrator.ts b/src/bot/orchestrator.ts
index 389bf96..468e1d7 100644
--- a/src/bot/orchestrator.ts
+++ b/src/bot/orchestrator.ts
@@ -334,6 +334,13 @@ export class BotOrchestrator {
this.sendChatMessageToMeeting(chatMsg.text);
break;
+ case 'stopAudio':
+ this._logger.info('Stop audio command received from Gateway');
+ if (this._audioProcedure) {
+ this._audioProcedure.stopAllAudio();
+ }
+ break;
+
case 'pong':
// Heartbeat response
break;