How can we avoid NOISE SUPRESSION and Echo Cancellation in iOS? - ios

I am using GoogleWebRTC to achieve VOIP calls.. While talk, it's keep on creating some noise suppression and echo cancellation in almost all network modes.. Could you please anyone help me with this?
pod 'GoogleWebRTC'
Android Platform used following stuff to avoid Noise Suppression
WebRtcAudioUtils.setWebRtcBasedAcousticEchoCanceler(true);
WebRtcAudioUtils.setWebRtcBasedAutomaticGainControl(true);
WebRtcAudioUtils.setWebRtcBasedNoiseSuppressor(true);
For iOS Platform
Please suggest me, what to use to avoid Noise Suppression and Echo Cancellation
Thanks

A good idea could be to take a look at your AVAudioSession notifications after the stream is established.
In the object that retains the stream add an observer for the routeChangeNotification
//inside an object that is retained during your stream
init() {
NotificationCenter.default.addObserver(forName: AVAudioSession.routeChangeNotification, object: nil, queue: nil, using: routeChange)
}
private func routeChange(_ n: Notification) {
do {
try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .videoChat, options: [.mixWithOthers, .defaultToSpeaker, .allowBluetooth, .allowAirPlay])
} catch {
print(error)
}
}
deinit {
NotificationCenter.default.removeObserver(self, name: AVAudioSession.routeChangeNotification, object: nil)
}
setting your AVAudioSessions category to playAndRecord in the obove options is what you want for a video chat.

Related

App doesn't route audio to headphones (initially)

I have a VOIP app implemented using the Sinch SDK and CallKit. Everything works fine, apart from when the device has headphones plugged in. In the latter case, when the call starts, audio is still routed through the main speaker of the device. If I unplug and plug the headphones back in - during the call -, audio is then correctly routed to the headphones.
All I am doing is
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
guard let c = self.currentCall else {
action.fail()
return
}
c.answer()
self.communicationClient.audioController().configureAudioSessionForCallKitCall()
action.fulfill()
}
Shouldn't this be taken care automatically by the OS?
It seems like the Sinch SDK overrides the output audio port. Try to run this code just after the audio session has been configured:
do {
try AVAudioSession.sharedInstance().overrideOutputAudioPort(.none)
} catch {
print("OverrideOutputAudioPort failed: \(error)")
}
If it doesn't work, try to configure the audio session by yourself, instead of relying on Sinch SDK if you can. Replace the configureAudioSessionForCallKitCall call with something like this:
let session = AVAudioSession.sharedInstance()
do {
try session.setCategory(
.playAndRecord,
mode: .voiceChat,
options: [.allowBluetooth, .allowBluetoothA2DP])
try session.setActive(true)
} catch {
print("Unable to activate audio session: \(error)")
}

AVAudioSession in bluetooth switches strangely when interrupting playback of other apps

I'm working on a VoIP app and i have handled the interruption of AVAudioSession in normal cases. But i find that in bluetooth mode, when i start a call, the switching process from PlayBack to PlayAndRecord will not complete instantly. There will be a short play in low quality and loud volume during the switching time. After that, the playback will not resume even though the call is over. This phenomenon appears randomly. Mostly at the beginning time, and then everything is OK. But after several times of calling, it happens again, which confuses me a lot.
I guessed at the beginning that it might be the problem of bluetooth category, so i added the options [.allowBluetooth, .allowBluetoothA2DP]. But it did not work. Here's my code.
// setting of AVAudioSession
public static let voipCall: AudioSessionScenario = {
if #available(iOS 10.0, *) {
return AudioSessionScenario("voip", category: .playAndRecord, mode: .voiceChat, options: [.allowBluetooth, .allowBluetoothA2DP])
} else {
return AudioSessionScenario("voip", category: .playAndRecord, mode: .voiceChat, options: [.allowBluetooth])
}
}()
// when user starts a call
AVAudioSession.queue.async {
AVAudioSession.entry(AudioSessionScenario.voipCall)
}
// the entry method does nothing but set the setting of sharedInstance
if #available(iOS 10.0, *) {
try session.setCategory(category, mode: scenario.mode, options: scenario.options)
} else {
AVAudioSession.sharedInstance().perform(NSSelectorFromString("setCategory:withOptions:error:"), with: category, with: scenario.options)
}
I expect that there is no strange play in the switching process and the playback will resume after the call. It works well in normal cases including speaker and wired headset. Bluetooth devices like Bose QC30 or Airpods have the unstable problem. Does anyone know the reason? Thx.

AudioKit handling of AVAudioSessionInterruption

After receiving a phone call or just having the phone ring our background play enabled AudioKit app goes silent for good and I am not sure how to handle that. The only way to restart sound output is to kill and restart the app. Other interruptions like enabling and using Siri work without a hitch and the app's sound is ducked during the event.
Typically an app can register itself to receive notifications (e.g. NSNotification.Name.AVAudioSessionInterruption) to detect an AVAudioSession interruption, but how does one retrieve the AVSession object that is normally passed into the notification?
NotificationCenter.default.addObserver(self, selector: #selector(AppDelegate.sessionInterrupted(_:)),
name: NSNotification.Name.AVAudioSessionInterruption,
object: MISSING_AK_AVAUDIOSESSION_REF)
Furthermore, if one were able to successfully implement audio interrupt notifications, what happens with AudioKit? It is not designed to be "restarted" or put on hold. Any help would be much appreciated.
This is going to depend on your app how you handle this. At very least, you'll want to do Audiokit.stop() and then Audiokit.start() when the interruption is finished.
You'll want to register for the notification with something like this:
NotificationCenter.default.addObserver(self,
selector: #selector(handleInterruption),
name: .AVAudioSessionInterruption,
object: nil)
Then handle it with something like this:
#objc internal func handleInterruption(_ notification: Notification) {
guard let info = notification.userInfo,
let typeValue = info[AVAudioSessionInterruptionTypeKey] as? UInt,
let type = AVAudioSessionInterruptionType(rawValue: typeValue) else {
return
}
//...handle each type here
}

Stop method's execution - Swift

When my watchKit app goes to background it fires the delegate method applicationWillResignActive. Method documentation says it can be used to pause ongoing tasks.
I have an ongoing method that i want to be stopped or broken by the use of the external method. How do i do that?
Example
func method1(){
// performing some actions
}
func breakMethod1(){
// running this method can stop (break) the execution of method1
}
This is, of course, assuming that your app has been architected so that breakMethod1() will definitely cancel the action occurring in method1().
You should set up an observer for an NSNotification at the beginning of method1() like so:
let notificationCenter = NSNotificationCenter.defaultCenter()
notificationCenter.addObserver(self, selector: "breakMethod1", name: UIApplicationWillResignActiveNotification, object: nil)
And for the sake of cleanup, you should also remove this observer after it's been triggered like so:
notificationCenter.removeObserver(self, name: UIApplicationWillResignActiveNotification, object: nil)

In Swift, how to ivalidate NSTimer in AppDelegate when application going background?

I need to translate my iOS application from obj-c to swift. I have a NStimer in ViewController that loads metadata from shoutcast every 30 seconds, but when application resign active it stops, when enter foreground it runs again.
Edit: OK. Problem solved! I added two observers in viewDidLoad with name UIApplicationWillResignActiveNotification and UIApplicationWillEnterForegroundNotification, like below:
override func viewDidLoad() {
NSLog("System Version is \(UIDevice.currentDevice().systemVersion)");
super.viewDidLoad()
self.runTimer()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "invalidateTimer", name: UIApplicationWillResignActiveNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "runTimer", name: UIApplicationWillEnterForegroundNotification, object: nil)
}
and I made two functions. First one for run timer:
func runTimer(){
loadMetadata()
myTimer.invalidate()
NSLog("timer run");
myTimer = NSTimer.scheduledTimerWithTimeInterval(30.0, target: self, selector: "loadMetadata", userInfo: nil, repeats: true)
let mainLoop = NSRunLoop.mainRunLoop()
mainLoop.addTimer(myTimer, forMode: NSDefaultRunLoopMode)
}
and second to stop it:
func invalidateTimer(){
myTimer.invalidate()
NSLog("timer invalidated %u", myTimer);
}
I hope this can help someone. :)
I suggest you use the appropriate system for your task: https://developer.apple.com/library/ios/documentation/iphone/conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.html#//apple_ref/doc/uid/TP40007072-CH4-SW56
Apps that need to check for new content periodically can ask the
system to wake them up so that they can initiate a fetch operation for
that content. To support this mode, enable the Background fetch option
from the Background modes section of the Capabilities tab in your
Xcode project. (You can also enable this support by including the
UIBackgroundModes key with the fetch value in your app’s Info.plist
file.)...
When a good opportunity arises, the system wakes or launches your app
into the background and calls the app delegate’s
application:performFetchWithCompletionHandler: method. Use that method
to check for new content and initiate a download operation if content
is available.

Resources