feat(audio,ios): AVAudioSession PlayAndRecord+voiceChat in AppDelegate
iOS AVAudioSession must be configured BEFORE Flutter starts its
audio pipeline; the canonical place is application(_:didFinishLaunching\
WithOptions:) in AppDelegate.swift. This commit:
apps/chanora_flutter/ios/Runner/AppDelegate.swift:
* import AVFoundation
* In application(_:didFinishLaunchingWithOptions:), call
AVAudioSession.sharedInstance().setCategory(.playAndRecord,
mode: .voiceChat,
options: [.defaultToSpeaker, .allowBluetooth, .allowBluetoothA2DP])
followed by setActive(true). Failures are NSLogged but do not
block app launch — cpal's CoreAudio backend will still come up
against the default iOS routing.
This shape:
* routes the receiver/speaker like a phone call (.playAndRecord +
.voiceChat),
* engages on-device AEC / NS where supported,
* defaults to speaker so users don't have to hold the phone to
their ear,
* permits Bluetooth headsets (AirPods et al. just work).
crates/chanora_audio/src/engine.rs:
* Replace the iOS engine-start placeholder log line ('binding
pending — Chanora iOS audio is documented-only for Beta') with
an honest acknowledgment that the AVAudioSession configuration
lives Swift-side. The Rust engine acknowledges the request, then
cpal opens its CoreAudio streams against the session.
iOS-only Rust code is #[cfg(target_os = "ios")]-gated so this commit
is no-op on every other platform.
SRS-197: iOS/macOS audio routing contract. DEC-025: iOS officially
in scope for P0 (Focused PTT only — Apple's sandbox model has no
global PTT analogue).
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import Flutter
|
||||
import UIKit
|
||||
import Flutter
|
||||
import AVFoundation
|
||||
|
||||
@main
|
||||
@objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate {
|
||||
@@ -7,6 +8,33 @@ import UIKit
|
||||
_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||
) -> Bool {
|
||||
// Configure the iOS AVAudioSession for voice chat BEFORE Flutter
|
||||
// starts the audio engine. The category + mode combination tells
|
||||
// iOS to:
|
||||
// * route via the receiver/speaker like a phone call
|
||||
// (`.playAndRecord` + `.voiceChat`)
|
||||
// * engage hardware AEC / NS where the device supports it
|
||||
// * default the speaker output (so the user doesn't have to
|
||||
// hold the phone to their ear)
|
||||
// * permit Bluetooth headsets (so AirPods et al. just work)
|
||||
//
|
||||
// SRS-197 covers the iOS audio routing contract; this is the
|
||||
// matching iOS-side implementation. Failures are logged but do
|
||||
// not block app launch — the audio engine will still come up,
|
||||
// just at the iOS default playback route.
|
||||
do {
|
||||
let session = AVAudioSession.sharedInstance()
|
||||
try session.setCategory(
|
||||
.playAndRecord,
|
||||
mode: .voiceChat,
|
||||
options: [.defaultToSpeaker, .allowBluetooth, .allowBluetoothA2DP]
|
||||
)
|
||||
try session.setActive(true, options: [])
|
||||
NSLog("chanora_flutter: AVAudioSession configured (playAndRecord/voiceChat)")
|
||||
} catch {
|
||||
NSLog("chanora_flutter: AVAudioSession setup failed: \(error)")
|
||||
}
|
||||
|
||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user