From a1c02d1288ea25d33a1d1f0567ef94f7b1fbda57 Mon Sep 17 00:00:00 2001 From: rohithzmoi <166651631+rohithzmoi@users.noreply.github.com> Date: Thu, 12 Sep 2024 14:06:03 +0530 Subject: [PATCH] Update AudioSessionManager.mm Better Audio state management and backkround Signed-off-by: rohithzmoi <166651631+rohithzmoi@users.noreply.github.com> --- iOS_OnlyFIles/AudioSessionManager.mm | 181 +++++++++++++++++---------- 1 file changed, 114 insertions(+), 67 deletions(-) diff --git a/iOS_OnlyFIles/AudioSessionManager.mm b/iOS_OnlyFIles/AudioSessionManager.mm index 6888ca5..cf6e3e2 100644 --- a/iOS_OnlyFIles/AudioSessionManager.mm +++ b/iOS_OnlyFIles/AudioSessionManager.mm @@ -17,35 +17,61 @@ #import #import +#include "droidstar.h" +// External C functions for calling process connect and clearing the audio buffer +extern "C" void callProcessConnect(); +extern "C" void clearAudioBuffer(); +@interface AudioSessionManager : NSObject { + UIBackgroundTaskIdentifier _bgTask; // To manage background tasks +} +- (void)setupAVAudioSession; +- (void)setupBackgroundAudio; +- (void)startBackgroundTask; +- (void)stopBackgroundTask; +@end -extern "C" void AudioEngine_stop_playback(); -extern "C" void AudioEngine_start_playback(); +@implementation AudioSessionManager -static UIBackgroundTaskIdentifier bgTask = UIBackgroundTaskInvalid; - - -extern "C" void setupAVAudioSession() { +- (void)setupAVAudioSession { @try { AVAudioSession *session = [AVAudioSession sharedInstance]; NSError *error = nil; NSLog(@"Setting up AVAudioSession..."); - [[NSNotificationCenter defaultCenter] removeObserver:session]; + + // Use category options suitable for VoIP or low-latency audio BOOL success = [session setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionAllowBluetooth | AVAudioSessionCategoryOptionMixWithOthers | - AVAudioSessionCategoryOptionDefaultToSpeaker + AVAudioSessionCategoryOptionDefaultToSpeaker | + AVAudioSessionCategoryOptionAllowBluetoothA2DP error:&error]; if (!success || error) { NSLog(@"Error setting AVAudioSession category: %@, code: %ld", error.localizedDescription, (long)error.code); return; } - NSLog(@"AVAudioSession category set to PlayAndRecord with DefaultToSpeaker option"); + NSLog(@"AVAudioSession category set to PlayAndRecord with required options"); - + // Set mode to VoiceChat or VoIP to reduce latency + success = [session setMode:AVAudioSessionModeVoiceChat error:&error]; + if (!success || error) { + NSLog(@"Error setting AVAudioSession mode: %@, code: %ld", error.localizedDescription, (long)error.code); + return; + } + NSLog(@"AVAudioSession mode set to VoiceChat"); + + // To Ensure audio is always routed to the speaker + success = [session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:&error]; + if (!success || error) { + NSLog(@"Error overriding audio output to speaker: %@, code: %ld", error.localizedDescription, (long)error.code); + } else { + NSLog(@"Audio output overridden to speaker"); + } + + // Activate session success = [session setActive:YES error:&error]; if (!success || error) { NSLog(@"Error activating AVAudioSession: %@, code: %ld", error.localizedDescription, (long)error.code); @@ -53,29 +79,33 @@ extern "C" void setupAVAudioSession() { } NSLog(@"AVAudioSession activated successfully"); - + // Handle audio interruptions [[NSNotificationCenter defaultCenter] addObserverForName:AVAudioSessionInterruptionNotification object:session queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { - AVAudioSessionInterruptionType interruptionType = (AVAudioSessionInterruptionType)[note.userInfo[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue]; + NSUInteger interruptionType = [note.userInfo[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue]; if (interruptionType == AVAudioSessionInterruptionTypeBegan) { NSLog(@"Audio session interruption began"); - } else if (interruptionType == AVAudioSessionInterruptionTypeEnded) { NSLog(@"Audio session interruption ended, attempting to reactivate..."); + [self setupAVAudioSession]; NSError *activationError = nil; BOOL reactivationSuccess = [session setActive:YES error:&activationError]; if (!reactivationSuccess) { NSLog(@"Error re-activating AVAudioSession after interruption: %@, code: %ld", activationError.localizedDescription, (long)activationError.code); } else { NSLog(@"Audio session successfully reactivated after interruption"); - + [session setCategory:AVAudioSessionCategoryPlayback + withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker + error:nil]; + [session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:nil]; + clearAudioBuffer(); // Clear the buffer to ensure current audio } } }]; - + // Handle route changes (e.g., when Bluetooth or headphones are connected/disconnected) [[NSNotificationCenter defaultCenter] addObserverForName:AVAudioSessionRouteChangeNotification object:session queue:[NSOperationQueue mainQueue] @@ -86,75 +116,35 @@ extern "C" void setupAVAudioSession() { reason == AVAudioSessionRouteChangeReasonNewDeviceAvailable || reason == AVAudioSessionRouteChangeReasonOverride) { NSLog(@"Audio route change detected, attempting to reactivate..."); - // Call C++ function to stop playback - AudioEngine_stop_playback(); + [self setupAVAudioSession]; NSError *activationError = nil; BOOL reactivationSuccess = [session setActive:YES error:&activationError]; - AudioEngine_start_playback(); if (!reactivationSuccess) { NSLog(@"Error re-activating AVAudioSession after route change: %@, code: %ld", activationError.localizedDescription, (long)activationError.code); } else { NSLog(@"Audio session successfully reactivated after route change"); - - // Call C++ function to start playback - //AudioEngine_start_playback(); + [session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:nil]; } } }]; + + // Start background task to keep the app alive longer - Fingers Crossed :D + [self startBackgroundTask]; } @catch (NSException *exception) { NSLog(@"Exception setting up AVAudioSession: %@", exception.reason); } } - -extern "C" void deactivateAVAudioSession() { +- (void)setupBackgroundAudio { @try { - AVAudioSession *session = [AVAudioSession sharedInstance]; - NSError *error = nil; - - NSLog(@"Deactivating AVAudioSession..."); - - - BOOL success = [session setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&error]; - if (!success || error) { - NSLog(@"Error deactivating AVAudioSession: %@, code: %ld", error.localizedDescription, (long)error.code); - return; - } - - NSLog(@"AVAudioSession deactivated successfully"); - - - [[NSNotificationCenter defaultCenter] removeObserver:session]; - } - @catch (NSException *exception) { - NSLog(@"Exception deactivating AVAudioSession: %@", exception.reason); - } -} - - -extern "C" void setupBackgroundAudio() { - @try { - if (bgTask != UIBackgroundTaskInvalid) { - [[UIApplication sharedApplication] endBackgroundTask:bgTask]; - bgTask = UIBackgroundTaskInvalid; - } - - - bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ - NSLog(@"Background task expired. Cleaning up..."); - [[UIApplication sharedApplication] endBackgroundTask:bgTask]; - bgTask = UIBackgroundTaskInvalid; - }]; - - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ AVAudioSession *session = [AVAudioSession sharedInstance]; NSError *error = nil; NSLog(@"Configuring AVAudioSession for background..."); - + // Deactivate the session before setting the category [session setActive:NO error:nil]; BOOL success = [session setCategory:AVAudioSessionCategoryPlayAndRecord @@ -168,6 +158,7 @@ extern "C" void setupBackgroundAudio() { NSLog(@"Error setting AVAudioSession category for background: %@, code: %ld", error.localizedDescription, (long)error.code); } else { NSLog(@"AVAudioSession category set successfully for background audio"); + success = [session setActive:YES error:&error]; if (!success || error) { NSLog(@"Error activating AVAudioSession in background: %@, code: %ld", error.localizedDescription, (long)error.code); @@ -176,14 +167,70 @@ extern "C" void setupBackgroundAudio() { } } - - if (bgTask != UIBackgroundTaskInvalid) { - [[UIApplication sharedApplication] endBackgroundTask:bgTask]; - bgTask = UIBackgroundTaskInvalid; - } + // Ensure the audio output is overridden to speake, else audio may at times play through earpiece or volume will be very less + [session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:nil]; }); } @catch (NSException *exception) { NSLog(@"Exception setting up AVAudioSession: %@", exception.reason); } } + +- (void)startBackgroundTask { + _bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ + [[UIApplication sharedApplication] endBackgroundTask:_bgTask]; + _bgTask = UIBackgroundTaskInvalid; + }]; +} + +- (void)stopBackgroundTask { + if (_bgTask != UIBackgroundTaskInvalid) { + [[UIApplication sharedApplication] endBackgroundTask:_bgTask]; + _bgTask = UIBackgroundTaskInvalid; + } +} + +@end + +// Expose the setup and management functions to C +extern "C" void setupAVAudioSession() { + AudioSessionManager *audioManager = [[AudioSessionManager alloc] init]; + [audioManager setupAVAudioSession]; +} + +extern "C" void deactivateAVAudioSession() { + @try { + AVAudioSession *session = [AVAudioSession sharedInstance]; + NSError *error = nil; + + NSLog(@"Deactivating AVAudioSession..."); + + BOOL success = [session setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&error]; + if (!success || error) { + NSLog(@"Error deactivating AVAudioSession: %@, code: %ld", error.localizedDescription, (long)error.code); + return; + } + + NSLog(@"AVAudioSession deactivated successfully"); + } + @catch (NSException *exception) { + NSLog(@"Exception deactivating AVAudioSession: %@", exception.reason); + } +} + +// Handle app entering background +extern "C" void setupBackgroundAudio() { + AudioSessionManager *audioManager = [[AudioSessionManager alloc] init]; + [audioManager setupBackgroundAudio]; +} + +// Handle app entering foreground +extern "C" void handleAppEnteringForeground() { + AudioSessionManager *audioManager = [[AudioSessionManager alloc] init]; + [audioManager setupAVAudioSession]; +} + +// function to clear the audio buffer +extern "C" void clearAudioBuffer() { + NSLog(@"Clearing audio buffer to ensure current audio playback"); +}