SEC.SI CARE

Custom Voice Trigger Training

Speaker Verification · On-Device

TFLite d-vector · Spectral Subtraction · Wiener Filter · Cosine Similarity ≥ 85%

TFLite / CoreML
Noise Cancellation
85% Threshold
Haptic 3× Confirm

End-to-End Pipeline · 8 Stufen

01Enrollment

5× Trigger-Wort sprechen → d-vector Centroid → AES-256 lokal speichern

02Noise Calibration

500ms Umgebungsrauschen → Noise-Profil für Spectral Subtraction

03Continuous Listening

1.5s Sliding Window → Noise Filter → MFCC → TFLite Embedding

04Cosine Similarity

Live-Embedding vs. Voiceprint → Score berechnen (0.0 – 1.0)

0585% Gate

Score ≥ 0.85 → weiter | Score < 0.85 → verwerfen (Fremdstimme)

06Haptic 3×

3 kurze Vibrationen → Nutzerin weiß: Trigger erkannt

073s Abbruch-Fenster

Anti-False-Positive Gate — Abbruch jederzeit möglich

08Emergency Dispatch

GPS + Encrypted Payload → SEC.SI Leitstelle + Session-Eskalation

Aufnahmen für Baseline

128

d-vector Dimensionen

AES

256-bit lokal gespeichert

pseudocode · speaker-enrollment
// ─── MODUL 1: SPEAKER ENROLLMENT (Einlern-Phase) ────────────────
// Framework: TensorFlow Lite (Android) / CoreML (iOS)
// Modell:    d-vector Speaker Embedding (128-dim, GE2E Loss)
// Ziel:      Voiceprint der Besitzerin lokal speichern

MODULE SpeakerEnrollment:

  CONST SAMPLE_RATE      = 16000         // 16 kHz Mono
  CONST EMBEDDING_DIM    = 128           // d-vector Dimension
  CONST ENROLLMENT_COUNT = 5             // 5 Aufnahmen für Baseline
  CONST MIN_DURATION_MS  = 800           // Mind. 0.8s pro Sample
  CONST MAX_DURATION_MS  = 3000          // Max. 3s pro Sample

  STATE voiceprintEmbeddings = []        // Gesammelte Embeddings

  FUNCTION collectEnrollmentSamples():
    FOR i IN range(ENROLLMENT_COUNT):
      UI.prompt("Sprechen Sie das Triggerwort: Aufnahme " + (i+1))
      raw_pcm  = AudioCapture.record(MAX_DURATION_MS)       // RAM only
      filtered = NoiseFilter.apply(raw_pcm)                  // Modul 2
      mfcc     = MFCC.extract(filtered, n_mels=40, n_fft=512)

      IF mfcc.duration_ms < MIN_DURATION_MS:
        UI.warn("Aufnahme zu kurz – bitte wiederholen")
        CONTINUE

      embedding = TFLite.infer("speaker_encoder.tflite", mfcc)
      // embedding.shape = [128] → kompakter Stimmabdruck

      voiceprintEmbeddings.append(embedding)
      raw_pcm.destroy()                  // Privacy: sofort löschen

    END FOR

    IF voiceprintEmbeddings.length < 3:
      THROW EnrollmentError("Zu wenige gültige Samples")

  FUNCTION buildVoiceprint():
    // Centroid der Embeddings = robusterer Voiceprint
    centroid = mean(voiceprintEmbeddings, axis=0)
    centroid = L2Normalize(centroid)

    // Sicherheitscheck: Inter-Sample Cosine-Similarity > 0.75
    FOR EACH pair IN combinations(voiceprintEmbeddings, 2):
      sim = cosineSimilarity(pair[0], pair[1])
      IF sim < 0.75:
        UI.warn("Inkonsistente Stimmproben – bitte neu aufnehmen")

    // Verschlüsselt lokal speichern (AES-256, Key aus Biometrie-Gate)
    encrypted = AES256.encrypt(centroid, key: BiometricKeystore.get())
    LocalStorage.store("secsi_voiceprint", encrypted)

    voiceprintEmbeddings.clear()         // RAM bereinigen
    RETURN centroid

Spectral Subtraction: Noise-Power-Spektrum vom Signal abziehen — eliminiert stationäres Rauschen (Lüfter, Verkehr)

Wiener-Filter: SNR-basierter Gain für nicht-stationäre Geräusche (Stimmengewirr, Musik)

Phase-Preservation: Originalphase bleibt erhalten — natürlicher Klang nach Filterung

pseudocode · noise-cancellation
// ─── MODUL 2: FREQUENZ-FILTER · NOISE CANCELLATION ─────────────
// Algorithmus: Spectral Subtraction + Wiener Filter (kombiniert)
// Ziel:        Umgebungsgeräusche herausrechnen vor Embedding

MODULE NoiseFilter:

  CONST FFT_SIZE         = 512
  CONST HOP_LENGTH       = 128
  CONST NOISE_FLOOR_DB   = -60.0
  CONST ALPHA_OVER       = 2.0          // Over-subtraction Faktor
  CONST BETA_FLOOR       = 0.001        // Spektraler Boden (kein Null-Signal)

  STATE noiseProfile = null             // Kalibriert bei Session-Start

  FUNCTION calibrateNoise(duration_ms = 500):
    // 500ms Hintergrundgeräusch aufnehmen BEVOR User spricht
    noise_pcm    = AudioCapture.record(duration_ms, silent=True)
    noise_frames = STFT(noise_pcm, FFT_SIZE, HOP_LENGTH)
    noiseProfile = mean(|noise_frames|², axis=time)   // Mittleres Noise-Power-Spektrum
    noise_pcm.destroy()

  FUNCTION apply(raw_pcm):
    IF noiseProfile == null:
      calibrateNoise()

    // 1. Short-Time Fourier Transform
    frames = STFT(raw_pcm, FFT_SIZE, HOP_LENGTH)
    mag    = |frames|                    // Magnitude
    phase  = angle(frames)              // Phase beibehalten

    // 2. Spectral Subtraction
    //    Ŝ(ω) = max(|X(ω)| - α·N(ω), β·|X(ω)|)
    noise_mag    = sqrt(noiseProfile)
    subtracted   = max(mag - ALPHA_OVER * noise_mag, BETA_FLOOR * mag)

    // 3. Wiener-Filter Glättung (Spectral Gain)
    //    G(ω) = SNR(ω) / (SNR(ω) + 1)  — optimaler Wiener-Gain
    signal_power  = subtracted²
    noise_power   = noiseProfile
    snr           = signal_power / max(noise_power, 1e-10)
    wiener_gain   = snr / (snr + 1.0)

    filtered_mag  = subtracted * wiener_gain

    // 4. Rücktransformation
    filtered_frames = filtered_mag * exp(j * phase)   // Phase rekonstruieren
    filtered_pcm    = iSTFT(filtered_frames, FFT_SIZE, HOP_LENGTH)

    RETURN filtered_pcm                               // Bereinigtes Signal

Cosine Similarity Formel:

sim = (v₁ · v₂) / (‖v₁‖ · ‖v₂‖)

< 0.85

Fremdstimme → Ignore

0.85–0.92

Validiert → Haptic

> 0.92

Hohe Konfidenz

pseudocode · speaker-validation
// ─── MODUL 3: VALIDIERUNGS-FUNKTION · > 85% THRESHOLD ──────────
// Speaker Verification: Cosine-Similarity des Live-Embeddings
// gegen gespeicherten Voiceprint (d-vector Centroid)

MODULE SpeakerValidator:

  CONST MATCH_THRESHOLD  = 0.85         // Mindest-Übereinstimmung
  CONST SLIDING_WINDOW   = 1500         // ms — kontinuierliches Fenster
  CONST CONFIRM_WINDOW   = 3000         // ms — Haptic + Abbruch-Chance
  CONST MAX_RETRIES      = 3            // Fehlversuche vor Lockout

  STATE failCount        = 0
  STATE voiceprint       = null         // Geladener Voiceprint

  FUNCTION loadVoiceprint():
    encrypted  = LocalStorage.get("secsi_voiceprint")
    IF encrypted == null: RETURN null
    centroid   = AES256.decrypt(encrypted, key: BiometricKeystore.get())
    voiceprint = L2Normalize(centroid)

  FUNCTION validate(live_pcm) → ValidationResult:
    IF voiceprint == null: loadVoiceprint()

    // 1. Noise-Cancellation
    filtered   = NoiseFilter.apply(live_pcm)

    // 2. MFCC Feature Extraction
    mfcc       = MFCC.extract(filtered, n_mels=40, n_fft=512)

    // 3. Speaker Embedding via TFLite
    live_emb   = TFLite.infer("speaker_encoder.tflite", mfcc)
    live_emb   = L2Normalize(live_emb)

    // 4. Cosine Similarity
    //    sim = (live_emb · voiceprint) / (‖live_emb‖ · ‖voiceprint‖)
    similarity = cosineSimilarity(live_emb, voiceprint)

    filtered.destroy()                 // Privacy

    IF similarity >= MATCH_THRESHOLD:
      failCount = 0
      RETURN { match: true, score: similarity, action: "TRIGGER" }
    ELSE:
      failCount += 1
      IF failCount >= MAX_RETRIES:
        UI.warn("Zu viele Fehlversuche – Listener pausiert 60s")
        SLEEP(60000)
        failCount = 0
      RETURN { match: false, score: similarity, action: "IGNORE" }

  FUNCTION onValidationSuccess(result):
    // DUAL GATE: Stimme erkannt UND Überschwellenwert
    LOG("voice_match", score: result.score, ts: now())

    // Haptic Feedback starten (Modul 4)
    HapticLoop.trigger()

    // 3 Sekunden Abbruch-Fenster
    cancelToken = UI.showCancelBanner(3000)
    WAIT 3000

    IF cancelToken.wasCancelled():
      LOG("trigger_cancelled_by_user", ts: now())
      RETURN

    // Schwelle bestätigt + kein Abbruch → Notfall auslösen
    EmergencyDispatch.fire()

3× 150ms · Pause 120ms

iOS: CHHapticEngine · Android: VibrationEffect.createWaveform · Web: navigator.vibrate

Bedeutung: "Hilfe ist unterwegs" — codiertes SOS-Pattern

pseudocode · haptic-loop + emergency-dispatch
// ─── MODUL 4: HAPTIC FEEDBACK LOOP ──────────────────────────────
// Plattform: iOS (CHHapticEngine) / Android (VibrationEffect)
// Bedeutung: 3× kurz = "Hilfe ist unterwegs" — codiertes SOS-Pattern

MODULE HapticLoop:

  // iOS: CHHapticPattern via AudioServicesPlaySystemSoundWithCompletion
  // Android: VibrationEffect.createWaveform()

  CONST PATTERN_IOS = [
    { sharpness: 0.8, intensity: 1.0, duration: 0.15 },   // Puls 1
    { pause: 0.12 },
    { sharpness: 0.8, intensity: 1.0, duration: 0.15 },   // Puls 2
    { pause: 0.12 },
    { sharpness: 0.8, intensity: 1.0, duration: 0.15 },   // Puls 3
  ]

  CONST PATTERN_ANDROID = {
    timings:    [0, 150, 120, 150, 120, 150],  // ms: delay, on, off, on, off, on
    amplitudes: [0, 255,   0, 255,   0, 255],  // 255 = max Intensität
    repeatIdx:  -1                              // Kein Loop — einmalig
  }

  FUNCTION trigger():
    platform = Device.getPlatform()

    IF platform == "iOS":
      engine = CHHapticEngine.init()
      engine.playPattern(PATTERN_IOS)

    ELSE IF platform == "Android":
      vibrator = Vibrator.get()
      vibrator.vibrate(VibrationEffect.createWaveform(
        PATTERN_ANDROID.timings,
        PATTERN_ANDROID.amplitudes,
        PATTERN_ANDROID.repeatIdx
      ))

    ELSE:  // Web / Fallback
      // Web Vibration API (Android Chrome)
      navigator.vibrate([150, 120, 150, 120, 150])

    // Visuelle Bestätigung parallel
    UI.flash(color: "#22c55e", duration_ms: 600)    // Grünes Aufleuchten
    UI.showToast("✅ Erkannt — Hilfe ist unterwegs", duration: 3000)

    LOG("haptic_triggered", pattern: "3x_confirm", ts: now())

// ─── EMERGENCY DISPATCH (nach Haptic + Bestätigung) ─────────────

MODULE EmergencyDispatch:

  FUNCTION fire():
    location = GPS.getHighAccuracy(timeout_ms: 500)

    payload = {
      event:              "sos_voice_trigger",
      trigger_type:       "speaker_verified",
      voice_match_score:  SpeakerValidator.lastScore,
      haptic_confirmed:   true,
      location: {
        lat:          location.lat,
        lng:          location.lng,
        accuracy_m:   location.accuracy,
        timestamp:    ISO8601.now()
      },
      session_id:         Session.currentId,
      device_ts:          UnixMillis.now()
    }

    // AES-256-GCM verschlüsseln
    encrypted = AES256GCM.encrypt(payload, key: ECDH.sessionKey())

    // An SEC.SI Leitstelle senden
    HTTP.post("/api/v2/sos/ping", encrypted, headers: {
      "Authorization":        "Bearer " + Session.jwt,
      "X-Trigger-Type":       "voice_verified",
      "X-Speaker-Score":      SpeakerValidator.lastScore
    })

    // Lokalen Alarm-Status setzen
    Session.escalate(level: "intervention")
    UI.activateSOSMode()

Security Summary · Schutzebenen

Speaker Verification

Nur die Stimme der Besitzerin löst aus — d-vector Cosine Similarity

Noise Cancellation

Spectral Subtraction + Wiener Filter eliminiert Umgebungsgeräusche

85% Threshold

Doppeltes Gate: Keyword-Erkennung UND Sprecher-Verifikation

3× Lockout

Nach 3 Fehlversuchen: 60s Listener-Pause gegen Brute-Force

AES-256 Voiceprint

Stimmabdruck lokal verschlüsselt im Biometric Keystore

Privacy by Design

Audio-Buffer niemals gespeichert — nur Embedding-Vektor im RAM

SEC.SI GERMANY · Voice Trigger Training v1.0 · On-Device · Confidential

Sicherheit Macht SEC.SI