From 6c91304400943103672a4ae15cd0f4eb5dd57a37 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 8 Dec 2025 22:24:12 +0100 Subject: [PATCH] macos: refine speech noise floor tracking --- .../Sources/Clawdis/VoiceWakeRuntime.swift | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/apps/macos/Sources/Clawdis/VoiceWakeRuntime.swift b/apps/macos/Sources/Clawdis/VoiceWakeRuntime.swift index eddb50495..9408ffd2d 100644 --- a/apps/macos/Sources/Clawdis/VoiceWakeRuntime.swift +++ b/apps/macos/Sources/Clawdis/VoiceWakeRuntime.swift @@ -111,7 +111,9 @@ actor VoiceWakeRuntime { input.installTap(onBus: 0, bufferSize: 2048, format: format) { [weak self, weak request] buffer, _ in request?.append(buffer) if let rms = Self.rmsLevel(buffer: buffer) { - Task { await self?.noteAudioLevel(rms: rms) } + Task.detached { [weak self] in + await self?.noteAudioLevel(rms: rms) + } } } @@ -315,6 +317,33 @@ actor VoiceWakeRuntime { self.restartRecognizer() } + // MARK: - Audio level handling + + private func noteAudioLevel(rms: Double) { + guard self.isCapturing else { return } + + // Update adaptive noise floor: faster when lower energy (quiet), slower when loud. + let alpha: Double = rms < self.noiseFloorRMS ? 0.08 : 0.01 + self.noiseFloorRMS = max(1e-7, self.noiseFloorRMS + (rms - self.noiseFloorRMS) * alpha) + + let threshold = max(self.minSpeechRMS, self.noiseFloorRMS * self.speechBoostFactor) + if rms >= threshold { + self.lastHeard = Date() + } + } + + private static func rmsLevel(buffer: AVAudioPCMBuffer) -> Double? { + guard let channelData = buffer.floatChannelData?.pointee else { return nil } + let frameCount = Int(buffer.frameLength) + guard frameCount > 0 else { return nil } + var sum: Double = 0 + for i in 0..