Search Unity

Questions: Audio Session Category & Interruptions

Discussion in 'iOS and tvOS' started by HulloImJay, Feb 2, 2021.

  1. HulloImJay

    HulloImJay

    Joined:
    Mar 26, 2012
    Posts:
    89
    We're trying to change the audio session category on iOS with mixed results and could really use some help. Not a native developer.

    Use case: Our game is dialogue-heavy and does not currently support subtitles. Thus, we do not want to mute the game even when the phone is switched to silent. (It would be unplayable.)

    Following the iOS guidelines here:

    https://developer.apple.com/design/human-interface-guidelines/ios/user-interaction/audio/

    Question 1:
    I believe that Unity locks us into the "Solo ambient" or "Ambient" modes depending on whether or not "Mute other audio sources" is ticked in the Player Settings. Is that correct?

    Question 2:
    Are there best practises for overriding this in Unity to select a different audio mode? We're currently following this post and this method does work for initially setting the audio category to "playback": https://forum.unity.com/threads/problem-with-mute-button-ios-silent-button.387579/#post-4727657

    Question 3:
    The above approach has a problem: If a phone call or alarm goes off during play, it appears to reset the audio category of the audio session, so the game is muted by the silence switch again. Assuming there are not some other best practises we should follow, how can one respond to this case and set the session back to the desired category? Is there any Unity interface for handling this type of interruption or do we need to detect the state change in native code somehow?
     
  2. HulloImJay

    HulloImJay

    Joined:
    Mar 26, 2012
    Posts:
    89
    Partial solution: In OnApplicationPause(), call again the native code which sets the audio sessions category.

    This is a partial solution because some events, such as a timer going off, interrupt the audio session but do NOT pause the game if you swipe them away.
     
  3. aihodge

    aihodge

    Joined:
    Nov 23, 2014
    Posts:
    163
    I can chime in on #3 - from my experience the only way to gracefully handle all possible audio interruptions is in the context of a native plugin where you register to observe audio interruptions as NSNotifications, and re-build/configure your audio session pipeline accordingly.
     
  4. HulloImJay

    HulloImJay

    Joined:
    Mar 26, 2012
    Posts:
    89
    Sorry for taking so long to reply, but I don't suppose you have any tips or links for a non-native iOS dev on how one would do that? I don't mind delving into a little Objective C but it's not an area I know well so I haven't got a clue where to start hooking into native events.
     
  5. HulloImJay

    HulloImJay

    Joined:
    Mar 26, 2012
    Posts:
    89
    Here is a work-around which changes the native call to only switch the session category if it isn't already set to what we want. I then just call this function every 0.5s. Could be every frame, perhaps. Not noticing the performance impact of this native call although I can't say I analysed it with the profiling tools.

    This approach means that the audio session will still be interrupted briefly when a call comes in, but will be restored shortly thereafter.

    AudioSessionSetter.mm:
    Code (CSharp):
    1. // Source: https://forum.unity.com/threads/problem-with-mute-button-ios-silent-button.387579/#post-4727657
    2.  
    3. #import "AVFoundation/AVFoundation.h"
    4.  
    5. @implementation AudioSessionSetter
    6. extern "C" {
    7.    
    8.    void _SetAudioSession()
    9.    {
    10.         if ([AVAudioSession sharedInstance].category != AVAudioSessionCategoryPlayback)
    11.         {
    12.             NSLog(@"Changing audio session. \n");
    13.             [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
    14.             [[AVAudioSession sharedInstance] setActive:YES error:nil];
    15.         }
    16.    }
    17. }
    18. @end
    AudioSessionSetter.cs:
    Code (CSharp):
    1. using System.Runtime.InteropServices;
    2. using UnityEngine;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5.  
    6. public class AudioSessionSetter : MonoBehaviour
    7. {
    8.     // -------------------------------------------------------------------------
    9.     // MonoBehaviour Calls
    10.     // -------------------------------------------------------------------------
    11.     private void Awake()
    12.     {
    13.         SetAudioSession();
    14.         StartCoroutine(RefreshSessionRoutine());
    15.     }
    16.  
    17.     void OnApplicationPause(bool pause)
    18.     {
    19.         if (!pause)
    20.         {
    21.             SetAudioSession();
    22.         }
    23.     }
    24.  
    25.     // Call for reset every so often in case we've been forced into a new mode.
    26.     IEnumerator RefreshSessionRoutine ()
    27.     {
    28.         while (true)
    29.         {
    30.             yield return new WaitForSecondsRealtime(0.5f);
    31.             SetAudioSession();
    32.         }
    33.     }
    34.  
    35.     // Does not apply on mobile?
    36.     // void OnApplicationFocus(bool focus)
    37.     // {
    38.     //     if (focus)
    39.     //     {
    40.     //         Debug.Log("App refocusing. Will reset audio session.");
    41.     //         SetAudioSession();
    42.     //     }
    43.     // }
    44.  
    45.     // -------------------------------------------------------------------------
    46.     // Native Code Calls
    47.     // -------------------------------------------------------------------------
    48. #if UNITY_IOS && !UNITY_EDITOR
    49.     [DllImport("__Internal")]
    50.     private static extern void _SetAudioSession();
    51.  
    52.     // -------------------------------------------------------------------------
    53.     // DOES NOT DO ANYTHING IF SESSION HASN'T CHANGED.
    54.     public static void SetAudioSession()
    55.     {
    56.         _SetAudioSession();
    57.     }
    58. #else
    59.     // -------------------------------------------------------------------------
    60.     public static void SetAudioSession()
    61.     {
    62.         //not implemented --> fallback
    63.     }
    64. #endif
    65. }
    66.  
     
    dimitris_baud likes this.