Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  WakeWordGate.swift   Sprache: unbekannt

 
Spracherkennung für: .swift vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

import Foundation

public struct WakeWordSegment: Sendable, Equatable {
    public let text: String
    public let start: TimeInterval
    public let duration: TimeInterval
    public let range: Range<String.Index>?

    public init(text: String, start: TimeInterval, duration: TimeInterval, range: Range<String.Index>? = nil) {
        self.text = text
        self.start = start
        self.duration = duration
        self.range = range
    }

    public var end: TimeInterval { start + duration }
}

public struct WakeWordGateConfig: Sendable, Equatable {
    public var triggers: [String]
    public var minPostTriggerGap: TimeInterval
    public var minCommandLength: Int

    public init(
        triggers: [String],
        minPostTriggerGap: TimeInterval = 0.45,
        minCommandLength: Int = 1) {
        self.triggers = triggers
        self.minPostTriggerGap = minPostTriggerGap
        self.minCommandLength = minCommandLength
    }
}

public struct WakeWordGateMatch: Sendable, Equatable {
    public let triggerEndTime: TimeInterval
    public let postGap: TimeInterval
    public let command: String

    public init(triggerEndTime: TimeInterval, postGap: TimeInterval, command: String) {
        self.triggerEndTime = triggerEndTime
        self.postGap = postGap
        self.command = command
    }
}

public enum WakeWordGate {
    private struct Token {
        let normalized: String
        let start: TimeInterval
        let end: TimeInterval
        let range: Range<String.Index>?
        let text: String
    }

    private struct TriggerTokens {
        let tokens: [String]
    }

    private struct MatchCandidate {
        let index: Int
        let triggerEnd: TimeInterval
        let gap: TimeInterval
    }

    public static func match(
        transcript: String,
        segments: [WakeWordSegment],
        config: WakeWordGateConfig)
    -> WakeWordGateMatch? {
        let triggerTokens = normalizeTriggers(config.triggers)
        guard !triggerTokens.isEmpty else { return nil }

        let tokens = normalizeSegments(segments)
        guard !tokens.isEmpty else { return nil }

        var best: MatchCandidate?

        for trigger in triggerTokens {
            let count = trigger.tokens.count
            guard count > 0, tokens.count > count else { continue }
            for i in 0...(tokens.count - count - 1) {
                let matched = (0..<count).allSatisfy { tokens[i + $0].normalized == trigger.tokens[$0] }
                if !matched { continue }

                let triggerEnd = tokens[i + count - 1].end
                let nextToken = tokens[i + count]
                let gap = nextToken.start - triggerEnd
                if gap < config.minPostTriggerGap { continue }

                if let best, i <= best.index { continue }

                best = MatchCandidate(index: i, triggerEnd: triggerEnd, gap: gap)
            }
        }

        guard let best else { return nil }
        let command = commandText(transcript: transcript, segments: segments, triggerEndTime: best.triggerEnd)
            .trimmingCharacters(in: Self.whitespaceAndPunctuation)
        guard command.count >= config.minCommandLength else { return nil }
        return WakeWordGateMatch(triggerEndTime: best.triggerEnd, postGap: best.gap, command: command)
    }

    public static func commandText(
        transcript _: String,
        segments: [WakeWordSegment],
        triggerEndTime: TimeInterval)
    -> String {
        let threshold = triggerEndTime + 0.001
        var commandWords: [String] = []
        commandWords.reserveCapacity(segments.count)
        for segment in segments where segment.start >= threshold {
            let normalized = normalizeToken(segment.text)
            if normalized.isEmpty { continue }
            commandWords.append(segment.text)
        }
        return commandWords.joined(separator: " ").trimmingCharacters(in: Self.whitespaceAndPunctuation)
    }

    public static func matchesTextOnly(text: String, triggers: [String]) -> Bool {
        guard !text.isEmpty else { return false }
        let normalized = text.lowercased()
        for trigger in triggers {
            let token = trigger.trimmingCharacters(in: whitespaceAndPunctuation).lowercased()
            if token.isEmpty { continue }
            if normalized.contains(token) { return true }
        }
        return false
    }

    public static func stripWake(text: String, triggers: [String]) -> String {
        var out = text
        for trigger in triggers {
            let token = trigger.trimmingCharacters(in: whitespaceAndPunctuation)
            guard !token.isEmpty else { continue }
            out = out.replacingOccurrences(of: token, with: "", options: [.caseInsensitive])
        }
        return out.trimmingCharacters(in: whitespaceAndPunctuation)
    }

    private static func normalizeTriggers(_ triggers: [String]) -> [TriggerTokens] {
        var output: [TriggerTokens] = []
        for trigger in triggers {
            let tokens = trigger
                .split(whereSeparator: { $0.isWhitespace })
                .map { normalizeToken(String($0)) }
                .filter { !$0.isEmpty }
            if tokens.isEmpty { continue }
            output.append(TriggerTokens(tokens: tokens))
        }
        return output
    }

    private static func normalizeSegments(_ segments: [WakeWordSegment]) -> [Token] {
        segments.compactMap { segment in
            let normalized = normalizeToken(segment.text)
            guard !normalized.isEmpty else { return nil }
            return Token(
                normalized: normalized,
                start: segment.start,
                end: segment.end,
                range: segment.range,
                text: segment.text)
        }
    }

    private static func normalizeToken(_ token: String) -> String {
        token
            .trimmingCharacters(in: whitespaceAndPunctuation)
            .lowercased()
    }

    private static let whitespaceAndPunctuation = CharacterSet.whitespacesAndNewlines
        .union(.punctuationCharacters)
}

#if canImport(Speech)
import Speech

public enum WakeWordSpeechSegments {
    public static func from(transcription: SFTranscription, transcript: String) -> [WakeWordSegment] {
        transcription.segments.map { segment in
            let range = Range(segment.substringRange, in: transcript)
            return WakeWordSegment(
                text: segment.substring,
                start: segment.timestamp,
                duration: segment.duration,
                range: range)
        }
    }
}
#endif

[Dauer der Verarbeitung: 0.28 Sekunden, vorverarbeitet 2026-04-27]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge