Search Unity

Question AudioSource added through script can't be hooked into AudioMixerGroup?

Discussion in 'Audio & Video' started by Balakay612, Mar 13, 2023.

  1. Balakay612

    Balakay612

    Joined:
    Feb 14, 2022
    Posts:
    5
    Hi all,

    I'm still trying to wrap my head around lots of things, but I can't seem to wrap my head around this: I have several sounds that I want to place in a "SFX" AudioMixerGroup (a child of a master mixer with a single sibling named "Music). I'm trying to manage my audio through a singleton AudoMixerManager class that contains three public fields: one for the master mixer and one each for the child mixer groups. (These are populated within the Inspector.) Looks like this:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Audio;
    3.  
    4. public class AudioMixerManager : Singleton<AudioMixerManager>
    5. {
    6.     public AudioMixer m_MasterMixer;
    7.    
    8.     public AudioMixerGroup m_SFXGroup;
    9.     public AudioMixerGroup m_MusicGroup;
    10.  
    11.     public AudioMixer MasterMixer
    12.     {
    13.         get => m_MasterMixer;
    14.     }
    15.  
    16.     public AudioMixerGroup SFXGroup
    17.     {
    18.         get => m_SFXGroup;
    19.     }
    20.  
    21.     public AudioMixerGroup MusicGroup
    22.     {
    23.         get => m_MusicGroup;
    24.     }
    25. }
    In my player controller script (RocketController.cs), I'm creating my source (for example) like this:

    Code (CSharp):
    1. // Create and attach AudioSources and assign clips:
    2.         m_MainThrustSource = gameObject.AddComponent<AudioSource>();
    3.         m_MainThrustSource.outputAudioMixerGroup = m_AudioMixerManager.SFXGroup;
    4.         m_MainThrustSource.clip = m_MainThrustClip;
    When I run the game, the bars in the mixer don't move, and tweaking the volume during play doesn't change the volume, either. If I try to Debug.Log the outputAudioMixerGroup of the audio source, I get a message saying NullReference. I'm trying to make sure the AudioMixerManager script gets loaded before any scripts attempt to reference the audio mixers, but the console suggests things are trying to reference the audio mixer before it's ready. I have no idea why I can't seem to get my audio sources to feed into the mixer. Any advice? Thank you so much!
     
  2. SeventhString

    SeventhString

    Unity Technologies

    Joined:
    Jan 12, 2023
    Posts:
    421
    Hi @Balakay612

    It looks like your AudioMixerManager might not have been provided with mixer groups initially, otherwise your object would certainly not return a null reference error if it fetched those values from the manager. You code does not seem to be wrong, but your data might be.

    When checking your gameobject at runtime, you should see to what group mixer group they are assigned in the AudioSource inspector. If nothing shows up there, it's because the value wasn't updated.

    I imagine your Singleton inherits from MonoBehaviour? If it's the case, you should be able to check at runtime if its data is also properly provisioned.

    Let me know what you find. Cheers!
     
  3. Balakay612

    Balakay612

    Joined:
    Feb 14, 2022
    Posts:
    5
    Thanks for taking a look. Yes, my Singleton inherits from MonoBehaviour (it's based on an example I got from the samyam off YouTube). At runtime, my AudioSource does indeed have no AudioMixer assigned. At this point, I'm not sure what the right approach is to ensure my mixers get assigned properly, nor where to look next. If, at runtime, I manually assign the mixer to the audiosources, of course it works. Thanks for any additional help!

    Oh! It's worth noting that within the editor, I did create an AudioMixer and assign two child mixers to the "master" mixer group that gets created by default. So at design time, three mixers (should?) exist with the AudioMixer: Master, SFX, and Music; SFX and Music are the children of Master (and siblings of one another).
     
    Last edited: Mar 14, 2023
  4. SeventhString

    SeventhString

    Unity Technologies

    Joined:
    Jan 12, 2023
    Posts:
    421
    I really just want to be sure that your AudioMixerManager singleton has its mixer and mixergroups set at runtime. If you're simply spawning the singleton by code and never "feed" him with the proper mixer references, it will never be able to forward them to anyone. Basically, at design time, make sure that your singleton exists somewhere and that it has all it needs to provide non-null references to anyone calling him.
     
  5. Balakay612

    Balakay612

    Joined:
    Feb 14, 2022
    Posts:
    5
    I think I'm setting things correctly at design time: After creating an AudioMixer by right-clicking and choosing Audio Mixer, I then created the two child mixer groups under the Master group. I have a game object to which I've attached my AudioMixerManager script (above); in the scene, I plugged the mixer plus the relevant groups (I think?) into the serialized (public) fields exposed through the script. Here's a screen shot (attached).

    Am I missing a step? I realized I never reference the parent of the SFX and Music mixer groups, but my thought is that referencing them through the aforementioned public fields would give Unity enough info to "find" them? Thanks again for the help!
     

    Attached Files:

  6. SeventhString

    SeventhString

    Unity Technologies

    Joined:
    Jan 12, 2023
    Posts:
    421
    I think everything is alright on this side. The only thing left I'd be looking at is your singleton code. The only reason I see you'd get null references on your AudioMixerManager members would be that the singleton code returns a new instance (with empty members) while disregarding the existence of that current game object.

    A clean way to work around this would also be to look into ScriptableObjects. This is a great way to main unique instances of data/code. If you can include the SO in a prefab or reference it on another manager, it's certainly worth considering it, as a better practice IMO. You might want to look into this.
     
  7. Balakay612

    Balakay612

    Joined:
    Feb 14, 2022
    Posts:
    5
    I've read about SOs before, but I'm not terribly familiar with how to implement them. Definitely worth a look: thanks for the link. Here's what my singleton code looks like:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.SceneManagement;
    3.  
    4. public class Singleton<T> : MonoBehaviour where T : Component
    5. {
    6.     static T m_Instance;
    7.  
    8.     public static T Instance
    9.     {
    10.         get
    11.         {
    12.             if (m_Instance == null)
    13.             {
    14.                 Scene _activeScene = SceneManager.GetActiveScene();
    15.                 SceneManager.SetActiveScene(SceneManager.GetSceneByName("Managers"));
    16.  
    17.                 GameObject _obj = new GameObject();
    18.                 _obj.name = typeof(T).Name;
    19.                 _obj.hideFlags = HideFlags.HideAndDontSave;
    20.                 m_Instance = _obj.AddComponent<T>();
    21.  
    22.                 SceneManager.SetActiveScene(_activeScene);
    23.             }
    24.  
    25.             return m_Instance;
    26.         }
    27.     }
    28.  
    29.     void OnDestroy()
    30.     {
    31.         if (m_Instance == this) m_Instance = null;
    32.     }
    33. }
     
  8. SeventhString

    SeventhString

    Unity Technologies

    Joined:
    Jan 12, 2023
    Posts:
    421
    I believe we've found the issue. Your singleton does not look for an existing instance of the object on its first call, it's creating its own.

    I've been using this for a time, let me know if it fixes your issue.
    Cheers!

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. /* IMPORTANT!!! To use Awake in a derived class you need to do it this way
    4. * protected override void Awake()
    5. * {
    6. *     base.Awake();
    7. *     //Your code goes here
    8. * }
    9. * */
    10.  
    11. public abstract class Singleton<T> : MonoBehaviour where T : Component
    12. {
    13.     private static T instance;
    14.  
    15.     private static bool m_applicationIsQuitting = false;
    16.  
    17.     public static T GetInstance()
    18.     {
    19.         if (m_applicationIsQuitting)
    20.         {
    21.             return null;
    22.         }
    23.  
    24.         if (instance == null)
    25.         {
    26.             instance = FindObjectOfType<T>();
    27.             if (instance == null)
    28.             {
    29.                 GameObject obj = new GameObject();
    30.                 obj.name = typeof(T).Name;
    31.                 instance = obj.AddComponent<T>();
    32.             }
    33.         }
    34.  
    35.         return instance;
    36.     }
    37.  
    38.     protected virtual void Awake()
    39.     {
    40.         if (instance == null)
    41.         {
    42.             instance = this as T;
    43.             DontDestroyOnLoad(gameObject);
    44.         }
    45.         else if (instance != this as T)
    46.         {
    47.             Destroy(gameObject);
    48.         }
    49.         else
    50.         {
    51.             DontDestroyOnLoad(gameObject);
    52.         }
    53.     }
    54.  
    55.     private void OnApplicationQuit()
    56.     {
    57.         m_applicationIsQuitting = true;
    58.     }
    59. }
    60.  
    61. // Same as above, but without "DontDestroyOnLoad"
    62. public abstract class SceneSingleton<T> : MonoBehaviour where T : Component
    63. {
    64.     private static T instance;
    65.  
    66.     private static bool m_applicationIsQuitting = false;
    67.  
    68.     public static T GetInstance()
    69.     {
    70.         if (m_applicationIsQuitting)
    71.         {
    72.             return null;
    73.         }
    74.  
    75.         if (instance == null)
    76.         {
    77.             instance = FindObjectOfType<T>();
    78.             if (instance == null)
    79.             {
    80.                 GameObject obj = new GameObject();
    81.                 obj.name = typeof(T).Name;
    82.                 instance = obj.AddComponent<T>();
    83.             }
    84.         }
    85.  
    86.         return instance;
    87.     }
    88.  
    89.     protected virtual void Awake()
    90.     {
    91.         if (instance == null)
    92.         {
    93.             instance = this as T;
    94.         }
    95.         else if (instance != this as T)
    96.         {
    97.             Destroy(gameObject);
    98.         }
    99.     }
    100.  
    101.     private void OnApplicationQuit()
    102.     {
    103.         m_applicationIsQuitting = true;
    104.     }
    105. }
    106.  
     
    Last edited: Mar 17, 2023
    Balakay612 likes this.
  9. Balakay612

    Balakay612

    Joined:
    Feb 14, 2022
    Posts:
    5
    Hey! I think that worked! Thank you sooo much! I made some minor modifications to what you provided (shown below) and added the
    protected override void Awake()
    calls to the relevant classes, and it seems like my mixergroups are "connected" at runtime now (the VU-meter-looking bars are dancing!), and testing SetFloat() reduces the volume as I expected it to. I think I have everything I need to hook into the UI stuff I still need to build. Again, thank you so much!

    Here's my tweak to get reference to the scene management stuff I have going on behind everything:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.SceneManagement;
    3.  
    4. public abstract class Singleton<T> : MonoBehaviour where T : Component
    5. {
    6.     private static T m_Instance;
    7.  
    8.     private static bool m_ApplicationIsQuitting = false;
    9.  
    10.     public static T Instance
    11.     {
    12.         get
    13.         {
    14.             if (m_ApplicationIsQuitting) return null;
    15.  
    16.             if (m_Instance == null)
    17.             {
    18.                 m_Instance = FindObjectOfType<T>();
    19.  
    20.                 if (m_Instance == null)
    21.                 {
    22.                     Scene _activeScene = SceneManager.GetActiveScene();
    23.                     SceneManager.SetActiveScene(SceneManager.GetSceneByName("Managers"));
    24.  
    25.                     GameObject _go = new GameObject();
    26.                     _go.name = typeof(T).Name;
    27.                     _go.hideFlags = HideFlags.HideAndDontSave;
    28.  
    29.                     m_Instance = _go.AddComponent<T>();
    30.  
    31.                     SceneManager.SetActiveScene(_activeScene);
    32.                 }
    33.             }
    34.  
    35.             return m_Instance;
    36.         }
    37.     }
    38.  
    39.     protected virtual void Awake()
    40.     {
    41.         if (m_Instance == null)
    42.         {
    43.             m_Instance = this as T;
    44.             DontDestroyOnLoad(gameObject);
    45.         }
    46.         else if (m_Instance != this as T)
    47.         {
    48.             Destroy(gameObject);
    49.         }
    50.         else
    51.         {
    52.             DontDestroyOnLoad(gameObject);
    53.         }
    54.     }
    55.  
    56.     private void OnApplicationQuit()
    57.     {
    58.         m_ApplicationIsQuitting = true;
    59.     }
    60. }
     
    SeventhString likes this.