fix(voice): klarer Befehl = STOP, Gespraech = 30s passiv (kein zweiter Gong)
Nach dem Re-Arm-Fix rief die App resume() → das spielte einen zweiten Gong und oeffnete ein neues Aufnahme-Fenster, obwohl Stefan nur einen Steuerbefehl gab. Sein gewuenschtes Verhalten: - Klarer Befehl (Fast-Path, speak=false, z.B. Liedersteuerung) = KEINE Konversation → STOP: direkt zurueck aufs Wake-Word (kein Gong, keine Aufnahme, kein 30s-Fenster). - Gespraech (gesprochene Antwort) = kein neuer Gong, direkt 30s passives Lauschen (weiterreden wie mit einem Menschen), 30s still → Wake-Word. endConversation(skipPassive) neu: true springt direkt zu armed statt in passives Lauschen. onPlaybackFinished (Gespraech) → passiv (Vordergrund) bzw. direkt armed (Hintergrund); stille Fast-Path-Antwort → skipPassive; manueller Stop-Button → skipPassive. resume() bleibt nur noch Mic-Fail-Retry. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1212,11 +1212,10 @@ const ChatScreen: React.FC = () => {
|
|||||||
const _isSilent = (message.payload as any).speak === false
|
const _isSilent = (message.payload as any).speak === false
|
||||||
|| ((message.payload as any).answeredBy === 'fast-path');
|
|| ((message.payload as any).answeredBy === 'fast-path');
|
||||||
if (_isSilent && wakeWordService.isConversing()) {
|
if (_isSilent && wakeWordService.isConversing()) {
|
||||||
if (AppState.currentState === 'active') {
|
// Klarer Steuerbefehl (Liedersteuerung etc.) = KEINE Konversation →
|
||||||
wakeWordService.resume();
|
// STOP: direkt zurueck aufs Wake-Word. Kein Gong, keine Aufnahme,
|
||||||
} else {
|
// kein 30s-Fenster (skipPassive=true).
|
||||||
wakeWordService.endConversation().catch(() => {});
|
wakeWordService.endConversation(true).catch(() => {});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1446,12 +1445,11 @@ const ChatScreen: React.FC = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unsubPlayback = audioService.onPlaybackFinished(() => {
|
const unsubPlayback = audioService.onPlaybackFinished(() => {
|
||||||
if (!wakeWordService.isActive()) return;
|
if (!wakeWordService.isActive()) return;
|
||||||
if (AppState.currentState === 'active') {
|
// Gespraech (gesprochene Antwort): KEIN neuer Gong / kein Aufnahme-Fenster,
|
||||||
wakeWordService.resume();
|
// sondern direkt passives Lauschen (30s weiterreden wie mit einem Menschen).
|
||||||
} else {
|
// Im Hintergrund gibt's kein Multi-Turn → direkt re-armen (skipPassive).
|
||||||
console.log('[Chat] TTS fertig im Background → endConversation (kein Multi-Turn)');
|
const bg = AppState.currentState !== 'active';
|
||||||
wakeWordService.endConversation().catch(() => {});
|
wakeWordService.endConversation(bg).catch(() => {});
|
||||||
}
|
|
||||||
});
|
});
|
||||||
return () => unsubPlayback();
|
return () => unsubPlayback();
|
||||||
}, []);
|
}, []);
|
||||||
@@ -2072,7 +2070,7 @@ const ChatScreen: React.FC = () => {
|
|||||||
await audioService.stopStreamingRecording('user');
|
await audioService.stopStreamingRecording('user');
|
||||||
if (wakeWordService.isConversing()) {
|
if (wakeWordService.isConversing()) {
|
||||||
console.log('[Chat] Manueller Stop in Konversation → endConversation, zurueck zu armed');
|
console.log('[Chat] Manueller Stop in Konversation → endConversation, zurueck zu armed');
|
||||||
await wakeWordService.endConversation();
|
await wakeWordService.endConversation(true); // explizit gestoppt → STOP (kein 30s-Passiv)
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|||||||
@@ -441,7 +441,10 @@ class WakeWordService {
|
|||||||
* wuerde sonst OpenWakeWord.stop() rufen weil bargeListening noch true
|
* wuerde sonst OpenWakeWord.stop() rufen weil bargeListening noch true
|
||||||
* ist, und unseren frisch re-armierten Listener killen.
|
* ist, und unseren frisch re-armierten Listener killen.
|
||||||
*/
|
*/
|
||||||
async endConversation(): Promise<void> {
|
/** @param skipPassive true = KEIN passives Lauschen, direkt zurueck aufs
|
||||||
|
* Wake-Word (armed). Fuer klare Steuerbefehle (Fast-Path) — nach
|
||||||
|
* "nächster Titel" will Stefan kein 30s-Fenster, sondern Stop. */
|
||||||
|
async endConversation(skipPassive: boolean = false): Promise<void> {
|
||||||
if (this.state !== 'conversing') {
|
if (this.state !== 'conversing') {
|
||||||
import('./logger').then(m => m.reportAppDebug('wake.end',
|
import('./logger').then(m => m.reportAppDebug('wake.end',
|
||||||
`endConversation called but state=${this.state} → noop`)).catch(()=>{});
|
`endConversation called but state=${this.state} → noop`)).catch(()=>{});
|
||||||
@@ -461,7 +464,7 @@ class WakeWordService {
|
|||||||
// (Phase 3) filtert fremde Stimmen weg, der User kann ohne erneute
|
// (Phase 3) filtert fremde Stimmen weg, der User kann ohne erneute
|
||||||
// Anrede weitersprechen.
|
// Anrede weitersprechen.
|
||||||
const passiveMs = await loadPassiveListenMs();
|
const passiveMs = await loadPassiveListenMs();
|
||||||
if (passiveMs > 0 && this.nativeReady) {
|
if (!skipPassive && passiveMs > 0 && this.nativeReady) {
|
||||||
this.enterPassiveListening(passiveMs);
|
this.enterPassiveListening(passiveMs);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user