feat(app): Wake-Word-Empfindlichkeit + Weiterreden-Fenster in Settings

Fehlauslösung im Auto: Spotify läuft über die Lautsprecher, das Mikro hört
mit, openWakeWord halluziniert "computer" rein (Log: wake.detect state=armed
→ leerer Transkript). Der Echo-Canceler kann nur ARIAs eigenes TTS
rausrechnen, nicht Spotify (fremde App, kein Referenzsignal).

- Wake-Word-Threshold jetzt konfigurierbar (loadWakeThreshold), Default von
  0.5 auf 0.6 hoch (strenger → weniger Fehlauslösung). Slider im Wake-Word-
  Settings-Bereich (0.30–0.90, greift beim "Speichern + Aktivieren").
- Weiterreden-Fenster (passives Lauschen) als Slider, Default 30s (10–60s).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-11 21:01:48 +02:00
co-authored by Claude Opus 4.8
parent 9fb29aa517
commit a5e2256a44
2 changed files with 109 additions and 3 deletions
+79
View File
@@ -105,6 +105,14 @@ import wakeWordService, {
KEYWORD_LABELS,
DEFAULT_KEYWORD,
WAKE_KEYWORD_STORAGE,
WAKE_THRESHOLD_DEFAULT,
WAKE_THRESHOLD_MIN,
WAKE_THRESHOLD_MAX,
loadWakeThreshold,
saveWakeThreshold,
PASSIVE_LISTEN_DEFAULT_MS,
loadPassiveListenMs,
savePassiveListenMs,
} from '../services/wakeword';
import ModeSelector from '../components/ModeSelector';
import QRScanner from '../components/QRScanner';
@@ -200,6 +208,8 @@ const SettingsScreen: React.FC = () => {
const [wakeKeyword, setWakeKeyword] = useState<string>(DEFAULT_KEYWORD);
const [wakeStatus, setWakeStatus] = useState<string>('');
const [wakeReadySound, setWakeReadySound] = useState<boolean>(true);
const [wakeThreshold, setWakeThreshold] = useState<number>(WAKE_THRESHOLD_DEFAULT);
const [passiveSec, setPassiveSec] = useState<number>(Math.round(PASSIVE_LISTEN_DEFAULT_MS / 1000));
const [editingPath, setEditingPath] = useState(false);
const [xttsVoice, setXttsVoice] = useState('');
const [loadingVoice, setLoadingVoice] = useState<string | null>(null);
@@ -322,6 +332,8 @@ const SettingsScreen: React.FC = () => {
if (saved && (WAKE_KEYWORDS as readonly string[]).includes(saved)) setWakeKeyword(saved);
});
isWakeReadySoundEnabled().then(setWakeReadySound);
loadWakeThreshold().then(setWakeThreshold).catch(() => {});
loadPassiveListenMs().then(ms => setPassiveSec(Math.round(ms / 1000))).catch(() => {});
updateService.getApkCacheSize().then(setApkCacheInfo).catch(() => {});
audioService.getTtsCacheSize().then(setTtsCacheInfo).catch(() => {});
AsyncStorage.getItem('aria_xtts_voice').then(saved => {
@@ -1862,6 +1874,40 @@ const SettingsScreen: React.FC = () => {
))}
</View>
<Text style={[styles.toggleLabel, {marginTop: 20}]}>Empfindlichkeit</Text>
<Text style={styles.toggleHint}>
Wie leicht das Wake-Word anspringt. Hoeher = strenger = weniger
Fehlauslösung (z.B. durch Musik/Radio, die das Mikro mithoert der
Echo-Canceler kann nur ARIAs eigene Stimme rausrechnen, nicht Spotify),
aber du musst evtl. deutlicher sprechen. Default: {WAKE_THRESHOLD_DEFAULT.toFixed(2)}.
Wird beim Speichern + Aktivieren" uebernommen.
</Text>
<View style={styles.prerollRow}>
<TouchableOpacity
style={styles.prerollButton}
onPress={() => {
const next = Math.max(WAKE_THRESHOLD_MIN, Math.round((wakeThreshold - 0.05) * 100) / 100);
setWakeThreshold(next);
saveWakeThreshold(next);
}}
disabled={wakeThreshold <= WAKE_THRESHOLD_MIN}
>
<Text style={styles.prerollButtonText}>0.05</Text>
</TouchableOpacity>
<Text style={styles.prerollValue}>{wakeThreshold.toFixed(2)}</Text>
<TouchableOpacity
style={styles.prerollButton}
onPress={() => {
const next = Math.min(WAKE_THRESHOLD_MAX, Math.round((wakeThreshold + 0.05) * 100) / 100);
setWakeThreshold(next);
saveWakeThreshold(next);
}}
disabled={wakeThreshold >= WAKE_THRESHOLD_MAX}
>
<Text style={styles.prerollButtonText}>+0.05</Text>
</TouchableOpacity>
</View>
<View style={{flexDirection: 'row', gap: 8, marginTop: 16, alignItems: 'center'}}>
<TouchableOpacity
style={[styles.connectButton, {flex: 1}]}
@@ -1907,6 +1953,39 @@ const SettingsScreen: React.FC = () => {
thumbColor={wakeReadySound ? '#FFFFFF' : '#666680'}
/>
</View>
<Text style={[styles.toggleLabel, {marginTop: 20}]}>Weiterreden-Fenster (Gespraech)</Text>
<Text style={styles.toggleHint}>
Nach einer gesprochenen ARIA-Antwort kannst du so lange einfach
weiterreden — ohne Wake-Word — bevor zurueck aufs Wake-Word geschaltet
wird. Reine Steuerbefehle (z.B. „nächster Titel") beenden sofort.
Default: {Math.round(PASSIVE_LISTEN_DEFAULT_MS / 1000)}s.
</Text>
<View style={styles.prerollRow}>
<TouchableOpacity
style={styles.prerollButton}
onPress={() => {
const next = Math.max(10, passiveSec - 5);
setPassiveSec(next);
savePassiveListenMs(next * 1000);
}}
disabled={passiveSec <= 10}
>
<Text style={styles.prerollButtonText}>5</Text>
</TouchableOpacity>
<Text style={styles.prerollValue}>{passiveSec} s</Text>
<TouchableOpacity
style={styles.prerollButton}
onPress={() => {
const next = Math.min(60, passiveSec + 5);
setPassiveSec(next);
savePassiveListenMs(next * 1000);
}}
disabled={passiveSec >= 60}
>
<Text style={styles.prerollButtonText}>+5</Text>
</TouchableOpacity>
</View>
</View>
</>)}