feat(ios): audio overhaul, Join/Leave Voice, channel-id sync fix, stereo mic capture
Three iOS client problems fixed plus a new core stereo-mic capture ABI: 1. Channel-id sync bug (mic button permanently dimmed): SessionState never synced currentChannelId from the self user's channelId on connect, so the mic button (gated on currentChannelId == 0) stayed dimmed. Added syncSelfChannel() (mirrors macOS MainWindowController.swift:461,491,522); called from init/.channelList/.userJoined/.userLeft/.userUpdated/.joinResult. Added applyServerMuteState() + serverMuted/serverDeafened to VoiceState. 2. Join/Leave Voice button: replaced icon-only mic toggle with explicit text button (parity with macOS). Mute/deafen disable when not in voice. 3. IOSAudioRouter.swift (new): full AVAudioSession routing layer — input port selection, built-in mic orientation/polar patterns, Bluetooth HFP/A2DP/Off modes, Standard/Raw mic processing, stereo capture, AirPlay, UserDefaults persistence. AudioSessionManager delegates to it. 4. Core stereo-mic capture (append-only ABI): vc_set_capture_channels() lets the core open the mic device in stereo (2-ch interleaved). LocalStream gains capture_channels; ensure_audio_running reads it; audio_engine.cpp capture_accum_ + on_capture updated to channel-aware accumulation. Test test_stereo_mic_capture (headless, L!=R stereo round-trip). Swift wrapper VoiceCatClient.setCaptureChannels. 5. Settings UI rework: AVAudioSession-derived input/output tree replaces miniaudio device picker. 6. iOS deployment target raised to 18.0 (Package.swift + project.pbxproj). swift-tools-version 6.0 with swiftLanguageModes .v5. Docs: tech-stack.md, architecture.md, voice.md, roadmap.md, building.md updated; stale 'vc_audio_suspend/resume deferred' claims corrected. Verified: ctest --preset dev 21/21 green; swift test 6/6 green; xcodebuild -target VoiceCatiOS -sdk iphonesimulator BUILD SUCCEEDED.
This commit is contained in:
@@ -135,6 +135,52 @@ public struct Device: Sendable, Equatable, Identifiable {
|
||||
}
|
||||
}
|
||||
|
||||
/// iOS audio input port — derived from `AVAudioSession.availableInputs`. Unlike the
|
||||
/// miniaudio-based `Device` (which returns ~2 entries on iOS), this exposes the real
|
||||
/// AVAudioSession input ports (builtInMic, bluetoothHFP, headsetMic, usbAudio, airPlay)
|
||||
/// with their data sources (orientation: front/back/top/bottom) and polar patterns
|
||||
/// (omni/cardioid/subcardioid/bidirectional). Used by `IOSAudioRouter` + `SettingsView`.
|
||||
public struct IOSAudioInputPort: Identifiable, Hashable {
|
||||
public let id: String // port UID (stable across route changes)
|
||||
public let name: String // human-readable port name
|
||||
public let portType: String // AVAudioSession.Port raw value as string
|
||||
public let dataSources: [IOSAudioDataSource]?
|
||||
public let isSelected: Bool // true if this is the current preferredInput
|
||||
|
||||
public init(id: String, name: String, portType: String,
|
||||
dataSources: [IOSAudioDataSource]?, isSelected: Bool) {
|
||||
self.id = id; self.name = name; self.portType = portType
|
||||
self.dataSources = dataSources; self.isSelected = isSelected
|
||||
}
|
||||
}
|
||||
|
||||
/// iOS audio data source — a sub-selection of an input port (e.g. built-in mic
|
||||
/// orientation: front/back/top/bottom). May have polar pattern options.
|
||||
public struct IOSAudioDataSource: Identifiable, Hashable {
|
||||
public let id: String // dataSource UID
|
||||
public let name: String // "Front", "Back", "Top", "Bottom"
|
||||
public let polarPatterns: [String]? // AVAudioSession.PolarPattern raw values
|
||||
public let isSelected: Bool // true if this is the current preferredDataSource
|
||||
public let selectedPolarPattern: String?
|
||||
|
||||
public init(id: String, name: String, polarPatterns: [String]?,
|
||||
isSelected: Bool, selectedPolarPattern: String?) {
|
||||
self.id = id; self.name = name; self.polarPatterns = polarPatterns
|
||||
self.isSelected = isSelected; self.selectedPolarPattern = selectedPolarPattern
|
||||
}
|
||||
}
|
||||
|
||||
/// iOS audio output route — read-only display of `AVAudioSession.currentRoute.outputs`.
|
||||
public struct IOSAudioOutputRoute: Identifiable, Hashable {
|
||||
public let id: String // port UID
|
||||
public let name: String // human-readable route name
|
||||
public let portType: String // AVAudioSession.Port raw value as string
|
||||
|
||||
public init(id: String, name: String, portType: String) {
|
||||
self.id = id; self.name = name; self.portType = portType
|
||||
}
|
||||
}
|
||||
|
||||
/// Effective Opus configuration — mirrors `vc_audio_config`.
|
||||
public struct AudioConfig: Sendable, Equatable {
|
||||
public let codec: UInt32 // 0 = OPUS
|
||||
|
||||
Reference in New Issue
Block a user