chore: restore product scaffold to rollback baseline
This commit is contained in:
@@ -34,54 +34,32 @@ import AVFoundation
|
||||
let session = AVAudioSession.sharedInstance()
|
||||
try session.setCategory(
|
||||
.playAndRecord,
|
||||
mode: .default,
|
||||
// Mode rationale (re-revisited after the "low playback
|
||||
// volume" investigation, May 2026):
|
||||
mode: .voiceChat,
|
||||
// Mode rationale (May 2026, .voiceChat reinstated):
|
||||
//
|
||||
// We've cycled through .voiceChat -> .default -> .voiceChat
|
||||
// -> .default. Final answer is .default with
|
||||
// .defaultToSpeaker, driven by these findings:
|
||||
// We previously used .default mode after discovering that
|
||||
// .voiceChat routed output through iOS's in-call audio
|
||||
// channel, which made speaker output barely audible. That
|
||||
// bug was caused by cpal's RemoteIO unit binding to a stale
|
||||
// physical transducer — after migrating to coreaudio-rs +
|
||||
// kAudioUnitSubType_VoiceProcessingIO (see
|
||||
// crates/chanora_audio/src/ios_voice_unit.rs) the route
|
||||
// binding is correct under either mode because VPIO re-binds
|
||||
// on overrideOutputAudioPort.
|
||||
//
|
||||
// The earlier "speaker selector silent" bug under
|
||||
// .voiceChat was caused by cpal's RemoteIO unit binding
|
||||
// to a stale physical transducer. After migrating to
|
||||
// coreaudio-rs + kAudioUnitSubType_VoiceProcessingIO
|
||||
// (see crates/chanora_audio/src/ios_voice_unit.rs) the
|
||||
// route binding is correct under either mode because
|
||||
// VPIO is the canonical voice unit and re-binds on
|
||||
// overrideOutputAudioPort. So route switching is no
|
||||
// longer a deciding factor.
|
||||
// .voiceChat advantages over .default:
|
||||
// * Tells iOS this is a VoIP session — other apps' audio
|
||||
// is properly ducked/paused instead of competing.
|
||||
// * Enables correct Bluetooth HFP negotiation without
|
||||
// manual workarounds.
|
||||
// * iOS treats the audio session as a "call" for priority
|
||||
// purposes (won't be interrupted by notification sounds).
|
||||
// * System-level CallKit integration (lock-screen controls).
|
||||
//
|
||||
// The "broken playback quality" bug under either
|
||||
// .voiceChat or .default (with VPIO) was actually NOT
|
||||
// a VPIO problem at all. iOS has TWO independent audio
|
||||
// channels: the in-call channel (used by .voiceChat /
|
||||
// .videoChat modes) and the media channel (used by
|
||||
// .default). The in-call channel:
|
||||
// * Routes through the phone-call audio path
|
||||
// * Aggressively ducks non-voice content to the
|
||||
// earpiece (Apple's "speakerphone vs ear" UX)
|
||||
// * Volume controlled by separate in-call volume
|
||||
// hardware, not the side buttons (when not in a
|
||||
// phone call)
|
||||
// The media channel:
|
||||
// * Routes through the standard media playback path
|
||||
// * No automatic ducking
|
||||
// * Volume controlled by the side volume buttons
|
||||
//
|
||||
// Even with VPIO + .voiceChat producing a perfectly
|
||||
// good signal, iOS's in-call channel routing made it
|
||||
// play at "earpiece" loudness on the speaker too \u2014
|
||||
// user-perceived as "broken and poor" because the
|
||||
// signal is technically there but barely audible against
|
||||
// the loud iPhone speaker's noise floor.
|
||||
//
|
||||
// Twilio's video-quickstart-ios and Daily.co's patched
|
||||
// WebRTC both document the same workaround: use .default
|
||||
// mode with .defaultToSpeaker option even when using
|
||||
// VPIO for AEC. The VPIO unit itself still does its job
|
||||
// (echo cancellation, noise suppression, AGC on the mic
|
||||
// path) \u2014 only the playback routing changes.
|
||||
// .defaultToSpeaker ensures output goes to the main speaker
|
||||
// (not the earpiece) by default when no headphones are
|
||||
// connected, compensating for the in-call channel's tendency
|
||||
// to route to the earpiece.
|
||||
//
|
||||
// References:
|
||||
// * https://github.com/twilio/video-quickstart-ios/issues/522
|
||||
@@ -91,9 +69,6 @@ import AVFoundation
|
||||
// .defaultToSpeaker : route output to the main speaker
|
||||
// (not the earpiece) by default
|
||||
// when no headphones are connected.
|
||||
// This is what makes the audio
|
||||
// actually audible at normal
|
||||
// loudness.
|
||||
// .allowBluetoothHFP : permit Bluetooth Hands-Free
|
||||
// Profile headsets as both input
|
||||
// and output.
|
||||
@@ -245,7 +220,7 @@ import AVFoundation
|
||||
let session = AVAudioSession.sharedInstance()
|
||||
try session.setCategory(
|
||||
.playAndRecord,
|
||||
mode: .default,
|
||||
mode: .voiceChat,
|
||||
options: [.defaultToSpeaker, .allowBluetoothHFP, .allowBluetoothA2DP]
|
||||
)
|
||||
try session.setPreferredIOBufferDuration(0.02)
|
||||
|
||||
Reference in New Issue
Block a user