fix: 5er-Bundle — Wake-Word, Spotify-Latenz, File-Limit, Connection-Refused

- WakeWord Doppel-Trigger: detectionInProgress-Guard gegen Native-Event-
  Race + setBackground/setForeground statt setResumeCooldown im AppState.
- Media-Pause beim App-Oeffnen: 1.5s Startup-Suppression im Kotlin
  emitDetected() — Mikro-Spin-up-Spike triggert kein false-positive mehr.
- Spotify Fast-Path im Brain: einfache Media-Commands (naechster Track,
  pause, play, lauter, ...) matchen via Regex und gehen direkt aufs
  spotify-Skill statt durch Claude. ~1.5s statt 5-10s pro Befehl.
- File-Limit auf 1 GB hochgezogen (war 70 MB). RVS maxPayload +
  Bridge max_size auf 1500 MB; Node-Heap im RVS-Container auf 4 GB.
- TriggerBrowser / Datei-Manager Connection-Refused: brainApi._send
  fast-failt bei disconnected RVS statt 30s zu timeouten, und beide
  UIs reloaden automatisch beim Reconnect-Event.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-06 08:27:08 +02:00
parent 886b4409d2
commit e82e07e3a2
10 changed files with 249 additions and 11 deletions
@@ -49,6 +49,12 @@ class OpenWakeWordModule(reactContext: ReactApplicationContext) : ReactContextBa
private const val EMBEDDING_DIM = 96
private const val MEL_BINS = 32
private const val DEFAULT_WW_INPUT_FRAMES = 16 // Fallback wenn Modell-Metadata fehlt
// Nach record.startRecording() erzeugt das Mikro fuer ~1s einen Spin-up-Spike
// (DC-Offset, AGC-Settling) der vom Wake-Word-Klassifikator faelschlich als
// Trigger eingestuft werden kann. Folge: App pausiert beim Oeffnen die Musik,
// weil der False-Positive die AudioFocus-Switch-Logik anwirft (Stefan-Bug 06/2026).
// Loesung: in dieser Phase keine Detections an JS weiterleiten.
private const val STARTUP_SUPPRESSION_MS = 1500L
}
private val env: OrtEnvironment = OrtEnvironment.getEnvironment()
@@ -95,6 +101,8 @@ class OpenWakeWordModule(reactContext: ReactApplicationContext) : ReactContextBa
private val embBuffer: ArrayDeque<FloatArray> = ArrayDeque(32) // Ringpuffer letzter Embeddings
private var consecutiveAboveThreshold: Int = 0
private var lastDetectionMs: Long = 0L
// Zeitpunkt des letzten startRecording — fuer STARTUP_SUPPRESSION_MS-Fenster
private var recordingStartedMs: Long = 0L
/**
* Initialisiert die ONNX-Sessions fuer ein bestimmtes Wake-Word.
@@ -206,6 +214,7 @@ class OpenWakeWordModule(reactContext: ReactApplicationContext) : ReactContextBa
resetInferenceState()
running.set(true)
record.startRecording()
recordingStartedMs = System.currentTimeMillis()
// PARTIAL_WAKE_LOCK greifen damit die CPU nicht in Doze geht und
// die JS-Bridge die emit("WakeWordDetected")-Events live verarbeitet.
@@ -313,6 +322,11 @@ class OpenWakeWordModule(reactContext: ReactApplicationContext) : ReactContextBa
}
private fun emitDetected() {
val sinceStart = System.currentTimeMillis() - recordingStartedMs
if (sinceStart in 0 until STARTUP_SUPPRESSION_MS) {
Log.i(TAG, "Wake-Word emit unterdrueckt (sinceStart=${sinceStart}ms < ${STARTUP_SUPPRESSION_MS}ms — Mikro-Spin-up-Spike)")
return
}
val params = com.facebook.react.bridge.Arguments.createMap().apply {
putString("model", modelName)
}