Files
openclaw/apps/macos/Sources/OpenClaw/TalkRecognitionCaptureLifecycle.swift
Vincent Koc 000db97c82 feat(talk): add macOS realtime Gateway relay (#128204)
* feat(talk): add macOS realtime relay transport

Co-authored-by: Zhilong Zheng <zhengzhilong1115@gmail.com>

* feat(talk): integrate realtime relay with macOS Talk

Co-authored-by: Zhilong Zheng <zhengzhilong1115@gmail.com>

* fix(talk): keep realtime bootstrap config redacted

Co-authored-by: Zhilong Zheng <zhengzhilong1115@gmail.com>

* fix(talk): surface failed native fallback

Co-authored-by: Zhilong Zheng <zhengzhilong1115@gmail.com>

* test(talk): inject deterministic relay capture

Co-authored-by: Zhilong Zheng <zhengzhilong1115@gmail.com>

---------

Co-authored-by: Zhilong Zheng <zhengzhilong1115@gmail.com>
2026-08-23 07:30:30 -07:00

58 lines
1.7 KiB
Swift

@preconcurrency import AVFoundation
import Foundation
import Speech
struct PreparedRecognitionCapture {
let request: SFSpeechAudioBufferRecognitionRequest
let engine: AVAudioEngine
let activeInputResolution: AudioInputDeviceResolution
func discard() {
self.request.endAudio()
self.engine.inputNode.removeTap(onBus: 0)
self.engine.stop()
}
}
enum TalkAudioInputError: LocalizedError {
case unavailable
case invalidFormat
var errorDescription: String? {
switch self {
case .unavailable: "Selected input and system default are unavailable"
case .invalidFormat: "Selected audio input has no usable format"
}
}
}
enum TalkRecognitionCaptureLifecycle {
static func configure(_ request: SFSpeechAudioBufferRecognitionRequest) {
SpeechRecognitionRequestPolicy.configureInteractiveTranscription(request)
}
static func start<Capture>(
isCurrent: () -> Bool,
prepare: (_ enableVoiceProcessing: Bool) throws -> Capture,
discard: (Capture) -> Void,
publish: (Capture) -> Void,
onFailure: (_ enableVoiceProcessing: Bool, _ error: Error) -> Void) -> Bool
{
for enableVoiceProcessing in [true, false] {
guard isCurrent() else { return false }
do {
let capture = try prepare(enableVoiceProcessing)
guard isCurrent() else {
discard(capture)
return false
}
publish(capture)
return true
} catch {
onFailure(enableVoiceProcessing, error)
}
}
return false
}
}