fix: avoid iOS talk mode audio tap crash
This commit is contained in:
@@ -23,6 +23,7 @@
|
|||||||
- Naming: match source names with `*.test.ts`; e2e in `*.e2e.test.ts`.
|
- Naming: match source names with `*.test.ts`; e2e in `*.e2e.test.ts`.
|
||||||
- Run `pnpm test` (or `pnpm test:coverage`) before pushing when you touch logic.
|
- Run `pnpm test` (or `pnpm test:coverage`) before pushing when you touch logic.
|
||||||
- Pure test additions/fixes generally do **not** need a changelog entry unless they alter user-facing behavior or the user asks for one.
|
- Pure test additions/fixes generally do **not** need a changelog entry unless they alter user-facing behavior or the user asks for one.
|
||||||
|
- Mobile: before using a simulator, check for connected real devices (iOS + Android) and prefer them when available.
|
||||||
|
|
||||||
## Commit & Pull Request Guidelines
|
## Commit & Pull Request Guidelines
|
||||||
- Create commits with `scripts/committer "<msg>" <file...>`; avoid manual `git add`/`git commit` so staging stays scoped.
|
- Create commits with `scripts/committer "<msg>" <file...>`; avoid manual `git add`/`git commit` so staging stays scoped.
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
- macOS menu: device list now uses `node.list` (devices only; no agent/tool presence entries).
|
- macOS menu: device list now uses `node.list` (devices only; no agent/tool presence entries).
|
||||||
- macOS menu: device list now shows connected nodes only.
|
- macOS menu: device list now shows connected nodes only.
|
||||||
- iOS node: fix ReplayKit screen recording crash caused by queue isolation assertions during capture.
|
- iOS node: fix ReplayKit screen recording crash caused by queue isolation assertions during capture.
|
||||||
|
- iOS Talk Mode: avoid audio tap queue assertions when starting recognition.
|
||||||
- iOS/Android nodes: bridge auto-connect refreshes stale tokens and settings now show richer bridge/device details.
|
- iOS/Android nodes: bridge auto-connect refreshes stale tokens and settings now show richer bridge/device details.
|
||||||
- iOS/Android nodes: status pill now surfaces camera activity instead of overlay toasts.
|
- iOS/Android nodes: status pill now surfaces camera activity instead of overlay toasts.
|
||||||
- iOS/Android/macOS nodes: camera snaps recompress to keep base64 payloads under 5 MB.
|
- iOS/Android/macOS nodes: camera snaps recompress to keep base64 payloads under 5 MB.
|
||||||
|
|||||||
@@ -253,7 +253,7 @@ private struct CanvasContent: View {
|
|||||||
private struct OverlayButton: View {
|
private struct OverlayButton: View {
|
||||||
let systemImage: String
|
let systemImage: String
|
||||||
let brighten: Bool
|
let brighten: Bool
|
||||||
var tint: Color? = nil
|
var tint: Color?
|
||||||
var isActive: Bool = false
|
var isActive: Bool = false
|
||||||
let action: () -> Void
|
let action: () -> Void
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import Speech
|
|||||||
@MainActor
|
@MainActor
|
||||||
@Observable
|
@Observable
|
||||||
final class TalkModeManager: NSObject {
|
final class TalkModeManager: NSObject {
|
||||||
|
private typealias SpeechRequest = SFSpeechAudioBufferRecognitionRequest
|
||||||
var isEnabled: Bool = false
|
var isEnabled: Bool = false
|
||||||
var isListening: Bool = false
|
var isListening: Bool = false
|
||||||
var isSpeaking: Bool = false
|
var isSpeaking: Bool = false
|
||||||
@@ -105,9 +106,8 @@ final class TalkModeManager: NSObject {
|
|||||||
let input = self.audioEngine.inputNode
|
let input = self.audioEngine.inputNode
|
||||||
let format = input.outputFormat(forBus: 0)
|
let format = input.outputFormat(forBus: 0)
|
||||||
input.removeTap(onBus: 0)
|
input.removeTap(onBus: 0)
|
||||||
input.installTap(onBus: 0, bufferSize: 2048, format: format) { [weak request] buffer, _ in
|
let tapBlock = Self.makeAudioTapAppendCallback(request: request)
|
||||||
request?.append(buffer)
|
input.installTap(onBus: 0, bufferSize: 2048, format: format, block: tapBlock)
|
||||||
}
|
|
||||||
|
|
||||||
self.audioEngine.prepare()
|
self.audioEngine.prepare()
|
||||||
try self.audioEngine.start()
|
try self.audioEngine.start()
|
||||||
@@ -135,6 +135,12 @@ final class TalkModeManager: NSObject {
|
|||||||
self.speechRecognizer = nil
|
self.speechRecognizer = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private nonisolated static func makeAudioTapAppendCallback(request: SpeechRequest) -> AVAudioNodeTapBlock {
|
||||||
|
{ buffer, _ in
|
||||||
|
request.append(buffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func handleTranscript(transcript: String, isFinal: Bool) async {
|
private func handleTranscript(transcript: String, isFinal: Bool) async {
|
||||||
let trimmed = transcript.trimmingCharacters(in: .whitespacesAndNewlines)
|
let trimmed = transcript.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
if self.isSpeaking, self.interruptOnSpeech {
|
if self.isSpeaking, self.interruptOnSpeech {
|
||||||
|
|||||||
Reference in New Issue
Block a user