Search Unity

Get list of Snapshots and Groups from an Audio Mixer?

Discussion in 'Audio & Video' started by infinitypbr, Feb 4, 2017.

  1. infinitypbr

    infinitypbr

    Joined:
    Nov 28, 2012
    Posts:
    3,149
    Hello!

    I want to get an array of all the snapshots and groups in an audio mixer. Is that possible? I don't think I see anything for that in the API.

    Thanks!
     
  2. Sovogal

    Sovogal

    Joined:
    Oct 15, 2016
    Posts:
    100
    You could try FindObjectsOfType <AudioMixerGroup> and then test for them being children of the AudioMixer with AudioMixer.FindMatchingGroups (group.name).

    Same with snapshots. The api is pretty minimal.
     
    infinitypbr likes this.
  3. Hikiko66

    Hikiko66

    Joined:
    May 5, 2013
    Posts:
    1,304
    I'm assuming you want this in editor mode?
    I haven't really worked much on editor scripting, so I'm not sure whether you can load mixers and use reflection to fetch that data in editor mode. I can fetch that data in playmode, though.

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5. using UnityEngine.Audio;
    6. using System.Reflection;
    7. using System;
    8.  
    9. public class FetchMixerData : MonoBehaviour {
    10.     public AudioMixer AudMix;
    11.     public AudioMixerGroup[] AudioMixerGroups;
    12.     public AudioMixerSnapshot[] AudioSnapshots;
    13.     public List<string> ExposedParams;
    14.     public List<string>[] GroupEffects;
    15.  
    16.  
    17.     // Use this for initialization
    18.     void Start () {
    19.  
    20.         AudMix = Resources.Load ("MainMixer") as AudioMixer;
    21.         SyncToMixer ();
    22.     }
    23.  
    24.     // Update is called once per frame
    25.     void Update () {
    26.  
    27.     }
    28.  
    29.     public void SyncToMixer()
    30.     {
    31.         Debug.Log("----Syncing to Mixer---------------------------------------------------------------------");
    32.         //Fetch all audio groups under MASTER
    33.         AudioMixerGroups = AudMix.FindMatchingGroups ("Master");
    34.    
    35.         Debug.Log("----AudioGroups----------------------------------------------------");
    36.         for (int x = 0; x < AudioMixerGroups.Length; x++) {
    37.             //Debug.Log(AudioMixerGroups[x].name);
    38.         }
    39.    
    40.         //PropertyInfo[] groupPropInf = AudioMixerGroups.GetType().GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
    41.         //MemberInfo[] groupMemberInf = AudioMixerGroups.GetType().GetMembers(BindingFlags.Public|BindingFlags.Static|BindingFlags.Instance);
    42.         //FieldInfo[] groupFieldInf = AudioMixerGroups.GetType ().GetFields (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
    43.    
    44.    
    45.         GroupEffects = new List<string>[AudioMixerGroups.Length];
    46.         for (int x = 0; x < AudioMixerGroups.Length; x++) {
    47.             Debug.Log("AudioGroup " + AudioMixerGroups[x].name + "---------------------------");
    48.             Debug.Log("----Effects----");
    49.             GroupEffects[x] = new List<string>();
    50.             Array effects = (Array)AudioMixerGroups[x].GetType().GetProperty("effects").GetValue(AudioMixerGroups[x], null);
    51.             for(int i = 0; i< effects.Length; i++)
    52.             {
    53.                 var o = effects.GetValue(i);
    54.                 string effect = (string)o.GetType().GetProperty("effectName").GetValue(o, null);
    55.                 GroupEffects[x].Add(effect);
    56.                 Debug.Log(effect);
    57.             }
    58.         }
    59.    
    60.         //PropertyInfo[] PropInf = AudMix.GetType().GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
    61.         //MemberInfo[] MemberInf = AudMix.GetType().GetMembers(BindingFlags.Public|BindingFlags.Static|BindingFlags.Instance);
    62.         //FieldInfo[] FieldInf = AudMix.GetType ().GetFields (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
    63.    
    64.         //Exposed Params
    65.         Array parameters = (Array)AudMix.GetType().GetProperty("exposedParameters").GetValue(AudMix, null);
    66.    
    67.         Debug.Log("----ExposedParams----------------------------------------------------");
    68.         for(int i = 0; i< parameters.Length; i++)
    69.         {
    70.             var o = parameters.GetValue(i);
    71.             string Param = (string)o.GetType().GetField("name").GetValue(o);
    72.             ExposedParams.Add(Param);
    73.             Debug.Log(Param);
    74.         }
    75.    
    76.         //Snapshots
    77.         AudioSnapshots = (AudioMixerSnapshot[])AudMix.GetType().GetProperty("snapshots").GetValue(AudMix, null);
    78.    
    79.         Debug.Log("----Snapshots----------------------------------------------------");
    80.         for(int i = 0; i< AudioSnapshots.Length; i++)
    81.         {
    82.             Debug.Log(AudioSnapshots[i].name);
    83.         }
    84.    
    85.         //PropertyInfo[] snapPropInf = Snapshots.GetType().GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
    86.         //MemberInfo[] snapMemberInf = Snapshots.GetType().GetMembers(BindingFlags.Public|BindingFlags.Static|BindingFlags.Instance);
    87.         //FieldInfo[] snapFieldInf = Snapshots.GetType ().GetFields (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
    88.    
    89.  
    90.     }
    91. }
    92.  
    93.  
    If anyone can get it working in editor scripting, can you post here?
    This is quite a popular question, and 99% of people would want to get this information from editor mode.
     
    Last edited: Feb 4, 2017
    Eotonisc, infinitypbr and Sovogal like this.
  4. infinitypbr

    infinitypbr

    Joined:
    Nov 28, 2012
    Posts:
    3,149

    I'm not sure what wouldn't be working -- everything in that script seemed to work in the Editor just fine.

    Any idea how to get the value for the Attenuation on each snapshot?
     
  5. Hikiko66

    Hikiko66

    Joined:
    May 5, 2013
    Posts:
    1,304
    It works in the editor in playmode, but most people will want that information in the editor in edit mode.

    Not too sure about snapshot attenuation or any of those details. I was just trying to see if I could get the basic info at the time.

    You need to set breakpoints and mouse over the objects being fetched at runtime.
    Then you can see the structure and try to find what you are looking for
     
    Last edited: Feb 5, 2017
    infinitypbr likes this.
  6. infinitypbr

    infinitypbr

    Joined:
    Nov 28, 2012
    Posts:
    3,149
    I'm new to C# and Reflection and such -- what do you mean "mouse over the objects being fetched at runtime"?

    I have expanded the code:

    Code (CSharp):
    1. PropertyInfo[] snapPropInf = audioSnapshots.GetType().GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
    2.         MemberInfo[] snapMemberInf = audioSnapshots.GetType().GetMembers(BindingFlags.Public|BindingFlags.Static|BindingFlags.Instance);
    3.         FieldInfo[] snapFieldInf = audioSnapshots.GetType ().GetFields (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
    4.  
    5.         Debug.Log ("snapPropInf: " + snapPropInf.Length);
    6.         for (int pi = 0; pi < snapPropInf.Length; pi++) {
    7.             Debug.Log ("snapPropInf[" + pi + "]: " + snapPropInf [pi]);
    8.         }
    9.         Debug.Log ("snapMemberInf: " + snapMemberInf.Length);
    10.         for (int mi = 0; mi < snapMemberInf.Length; mi++) {
    11.             Debug.Log ("snapMemberInf[" + mi + "]: " + snapMemberInf [mi]);
    12.         }
    13.         Debug.Log ("snapFieldInf: " + snapFieldInf.Length);
    14.         for (int fi = 0; fi < snapFieldInf.Length; fi++) {
    15.             Debug.Log ("snapFieldInf[" + fi + "]: " + snapFieldInf [fi]);
    16.         }
    However, I'm not really sure how to read the information the console is providing.

    For instance, I'm thinking that's how you knew to look for "effects" and "effectName" in this code:

    Code (CSharp):
    1. groupEffects = new List<string>[audioGroups.Length];
    2.         for (int x = 0; x < audioGroups.Length; x++) {
    3.             Debug.Log ("AudioGroup " + audioGroups[x].name);
    4.             groupEffects[x] = new List<string>();
    5.             Array effects = (Array)audioGroups[x].GetType().GetProperty("effects").GetValue(audioGroups[x], null);
    6.             for(int i = 0; i< effects.Length; i++)
    7.             {
    8.                 var o = effects.GetValue(i);
    9.                 string effect = (string)o.GetType().GetProperty("effectName").GetValue(o, null);
    10.                 groupEffects[x].Add(effect);
    11.                 Debug.Log(effect);
    12.             }
    13.         }
    Is that right? Or maybe I'm looking at this all wrong?
     
  7. Sovogal

    Sovogal

    Joined:
    Oct 15, 2016
    Posts:
    100
    It means set a breakpoint after you've retrieved all members, properties, and fields. Then you can inspect them one-by-one. If you're using Visual Studio, you can just mouse over the variables snapPropInfo (etc) and check out the values reflection returned. In MonoDevelop, I think you'd have to check in the Locals tab.
     
    infinitypbr likes this.
  8. infinitypbr

    infinitypbr

    Joined:
    Nov 28, 2012
    Posts:
    3,149
    Got it -- I crashed unity, but did see a lot of stuff in the locals tab. Not sure yet how to actually read it...but this will have to wait until after some unhealthy food and watching the only football game I watch each year!
     
  9. Sovogal

    Sovogal

    Joined:
    Oct 15, 2016
    Posts:
    100
    If you need to execute a method to inspect the value of a PropertyInfo object, you can just execute this code in the Immediate Window. Both IDEs have that feature.

    Code (CSharp):
    1. //assuming some object called AudioSnapshots is in scope
    2. int propertyIndex = 0;
    3. int snapShotIndex = 0;
    4. snapPropInfo = typeof(AudioMixerSnapshot).GetProperties();
    5. snapPropInfo[propertyIndex].GetValue(AudioSnapshots[snapShotIndex], null);
    And you can change the indexes to change the objects and properties you're inspecting.
     
  10. infinitypbr

    infinitypbr

    Joined:
    Nov 28, 2012
    Posts:
    3,149
    Ok, this may be above my pay grade right now, but I'll look into it more either tonight or tomorrow.

    For now, I have a some-what working script: http://infinitypbr.com/snapshotexporter.unitypackage

    The Problem(s):
    * For some reason, the volume is very low. I copied the code from the AudioClipCombiner (https://github.com/infinitypbr/AudioClipArrayCombiner), and to make sure I didn't make a mistake, I tested the music layers in the original, and the same effect happened.
    * I still need to figure out how to get the volume of each group for any given snapshot, and adjust the volume. Currently I just pass "1.0". *
    * Somehow Unity is crashing. It doesn't crash when I use the script, but often after I use it, and then after I do a few more things that seem to be unrelated, Unity does this thing where it (mac version) does the rainbow wheel, then mouse, then rainbow wheel, then mouse...and so on. No idea why, but I'm assuming it's something I'm doing.

    To Use:
    * Add the script to any object (I've been adding it to the main music object in my demo scenes, the same one with the "MixerControl" script).
    * Add the Audio Mixer that you'd like to use and click the Export button. It should do the rest, filling the data and doing the export (will only export the first snapshot currently, to speed things up). The resulting file will be in Assets/SFBayStudios/Exported Music/[Audio Mixer Name]/newFileName.wav

    If anyone is interested in trying it out and figuring out what's going on with the problems, the help would be appreciated!

    (The goal is to allow, with one click, the exporting of all snapshots from an Audio Mixer. This way you can mix a bunch of versions of a song, and then get all of them as single .wav files to use in the game, so you don't need to have all the layers)

    * Potential work around: I know the volume of each snapshot during play mode. The script can run during play mode. So if I link it to other info from the scene, it could control the scene -- basically load the snapshot in the scene, then export (knowing the volume levels), then load the next snapshot etc.

    This isn't the best since that means you (1) have to be in play mode and (2) have to have the file structure set up....but I think it would work.
     
    Last edited: Feb 7, 2017
  11. infinitypbr

    infinitypbr

    Joined:
    Nov 28, 2012
    Posts:
    3,149
    Update!

    Ok, I fixed the volume problem -- there was a line that divided the output by the number of clips. Turns out thats not needed.

    Also, I'm able to export based on the current mix in edit mode.

    Unfortunately, I'm not sure how to switch the mix via code in edit mode. Even in play mode, where I have code that switches the snapshot, it all happens the same frame so the values don't seem to update, and all exports are the same.

    Transitioning to a new snapshot doesn't work in editor mode at all via code.

    So my new thoughts are...

    1) Require play mode, and set up the code to wait one frame after switching to the next snapshot before exporting it.
    2) Instead of batch exporting, require the user to select a snapshot and click the "Export" button, which will export just a single snapshot. The name can be updated when it switches, so they don't have to do anything but select the snapshot and export, but it's not as fun as clicking one button for batch exporting.
     
  12. infinitypbr

    infinitypbr

    Joined:
    Nov 28, 2012
    Posts:
    3,149
    Another Update!

    Now I can export a single snapshot (using a custom name) in edit mode. User must select the snapshot in the Audio Mixer first.

    And then in play mode, a new option appears to batch export all the snapshots.

    It works! That's nice.

    BUT! There's a new problem. Popping. It doesn't always happen, but there's a lot of popping depending on what's playing, especially timpani hits. I tried lowering the overall output volume, but it only got quieter without removing the popping.

    I'll have to figure this one out, since it's pointless if the quality of the output pops.
     
  13. neoRiley

    neoRiley

    Joined:
    Dec 12, 2008
    Posts:
    162
    If you're in the editor, this will work -

    This example assumes 2 things:
    1. You have a mixer called "Music" located in "Assets/Audio/Ambience"
    2. you've created 2 snapshots called "FullVolume" and "NoVolume"

    Code (CSharp):
    1. var assetsAtPath = AssetDatabase.LoadAllAssetRepresentationsAtPath("Assets/Audio/Ambience/Music.mixer");
    2. Debug.Log($"assetsAtPath: {assetsAtPath.Length}");
    3.  
    4. foreach (var assetRepresentation in assetsAtPath)
    5. {
    6.     AudioMixerSnapshot snapshot = assetRepresentation as AudioMixerSnapshot;
    7.     if (snapshot != null)
    8.     {
    9.         Debug.Log($"mixer snapshots: {snapshot.name}");
    10.     }                  
    11. }
    output:
    mixer snapshots: FullVolume
    mixer snapshots: NoVolume
     
    Last edited: Feb 19, 2020