How do I get the parent from a AudioMixerGroup Object
The title says it all.
I have a SoundSource that plays into an AudioMixerGroup. I can get the MixerGroups' mixer with AudioMixerGroup.audioMixer
but I would like to walk the complete tree of all MixerGroups up↑ to the Master.
something like this: GroupA1 → GroupA → Master
Getting groups with AudioMixer.FindMatchingGroups(output.name)
doesn't help because I have to know what they are called for that.
Answer by Agustin0987 · Jul 23, 2020 at 09:22 AM
Blockquote
Hi @MrFuzzzy i know is a little bit late, but in case you have not found a solution for this (or if some else needs a solution for this same issue) I am posting my solution. So here is what I did to get the "hierarchy" of MixerSoundGroups:
private class GD //FYI, GD stands for GroupData
{
public GD Parent;
public AudioMixerGroup Group;
public List<GD> Children;
public int Depth;
public GD(AudioMixerGroup group, GD parent, int depth)
{
Group = group;
Children = new List<GD>();
Parent = parent;
Depth = depth;
}
}
private GD GetGroupsData(AudioMixer mixer)
{
AudioMixerGroup[] groups = mixer.FindMatchingGroups("Master");
GD masterData = new GD(groups[0], null, 0);
SetChildren(masterData, mixer);
return masterData;
}
private void SetChildren(GD parent, AudioMixer mixer)
{
if (parent== null)
return;
AudioMixerGroup[] children = mixer.FindMatchingGroups(parent.Group.name).Skip(1).ToArray();
if (children.Length != 0)
{
int i = 0;
while (i < children.Length)
{
GD child = new GD(children[i], parent, parent.Depth + 1);
parent.Children.Add(child);
SetChildren(child, mixer);
int subChildrenCount = mixer.FindMatchingGroups(child.Group.name).Skip(1).ToArray().Length
i += subChildrenCount > 0 ? subChildrenCount + 1 : 1;
}
}
}
//Somwhere in your code call 'GetGroupsData' like this:
GD masterData = GetGroupsData(youAudioMixerInstance);
Very nice, thanks @Agustin0987. I put it in a little class to log everything. $$anonymous$$aybe someone else needs this:
using System.Collections.Generic;
using System.Linq;
using UnityEngine.Audio;
using UnityEngine;
public class $$anonymous$$ixerGroupData
{
public $$anonymous$$ixerGroupData Parent;
public Audio$$anonymous$$ixerGroup Group;
public List<$$anonymous$$ixerGroupData> Children;
public int Depth;
public $$anonymous$$ixerGroupData(Audio$$anonymous$$ixerGroup group, $$anonymous$$ixerGroupData parent, int depth)
{
Group = group;
Children = new List<$$anonymous$$ixerGroupData>();
Parent = parent;
Depth = depth;
}
public static $$anonymous$$ixerGroupData GetGroupsData(Audio$$anonymous$$ixer mixer)
{
Audio$$anonymous$$ixerGroup[] groups = mixer.Find$$anonymous$$atchingGroups("$$anonymous$$aster");
$$anonymous$$ixerGroupData masterData = new $$anonymous$$ixerGroupData(groups[0], null, 0);
SetChildren(masterData, mixer);
return masterData;
}
private static void SetChildren($$anonymous$$ixerGroupData parent, Audio$$anonymous$$ixer mixer)
{
if (parent == null)
return;
Audio$$anonymous$$ixerGroup[] children = mixer.Find$$anonymous$$atchingGroups(parent.Group.name).Skip(1).ToArray();
if (children.Length != 0)
{
int i = 0;
while (i < children.Length)
{
$$anonymous$$ixerGroupData child = new $$anonymous$$ixerGroupData(children[i], parent, parent.Depth + 1);
parent.Children.Add(child);
SetChildren(child, mixer);
int subChildrenCount = mixer.Find$$anonymous$$atchingGroups(child.Group.name).Skip(1).ToArray().Length;
i += subChildrenCount > 0 ? subChildrenCount + 1 : 1;
}
}
}
public void LogAll()
{
logAll(this);
}
private static void logAll($$anonymous$$ixerGroupData group, int depth = 0)
{
string whiteSpace = new string(' ', depth * 3);
Debug.Log($"{whiteSpace}{group.Group.name}");
foreach ($$anonymous$$ixerGroupData child in group.Children)
{
logAll(child, depth + 1);
}
}
}
Answer by sindrijo_ · Sep 14, 2021 at 11:45 AM
I wrote another one that handles hierarchies with duplicated names and sub-branches.
using System;
using System.Collections.Generic;
using UnityEngine.Audio;
public static class AudioMixerGroupExtensions
{
/// <summary>
/// This method resolves the 'Hierarchy' of the AudioMixer's AudioMixerGroups.
/// It resolves the correct hierarchy even if AudioMixerGroup names or even
/// whole branches are duplicated. It can do this because AudioMixer.FindMatchingGroups returns
/// it's results in a depth-first order.
/// </summary>
/// <param name="mixer">The <see cref="AudioMixer"/> to resolve the hierarchy for.</param>
/// <returns>A AudioMixerGroupHierarchy</returns>
public static IAudioMixerGroupHierarchy GetMixerGroupHierarchy(this AudioMixer mixer)
{
return AudioMixerGroupHierarchy.ResolveHierarchy(mixer);
}
private class AudioMixerGroupHierarchy : IAudioMixerGroupHierarchy
{
private readonly AudioMixerGroupHierarchy parent;
private readonly AudioMixerGroup mixerGroup;
private readonly List<AudioMixerGroupHierarchy> children;
private readonly string path;
private readonly int depth;
private AudioMixerGroupHierarchy(AudioMixerGroup mixerGroup, AudioMixerGroupHierarchy parent, int depth)
{
this.mixerGroup = mixerGroup;
this.parent = parent;
this.depth = depth;
path = $"{parent?.path}/{mixerGroup.name}";
children = new List<AudioMixerGroupHierarchy>();
}
internal static IAudioMixerGroupHierarchy ResolveHierarchy(AudioMixer mixer)
{
// We are relying on the fact that FindMatchingGroups returns
// the mixers in a depth-first order, meaning as we iterate
// the list we will never see an element whose parent we have
// not seen before
var depthFirstOrderedMixerGroups = mixer.FindMatchingGroups("");
var root = new AudioMixerGroupHierarchy(depthFirstOrderedMixerGroups[0], null, 0);
var ancestorStack = new Stack<AudioMixerGroupHierarchy>();
ancestorStack.Push(root);
// Iterate over all mixerGroups, resolving the hierarchy by
// testing results of mixer.FindMatchingGroups(searchPath)
// against the mixerGroup we are iterating on.
// Skip the first element which is the root.
for (var i = 1; i < depthFirstOrderedMixerGroups.Length; i++)
{
var subMixer = depthFirstOrderedMixerGroups[i];
RETEST_ELEMENT:
var parent = ancestorStack.Peek();
var searchPath = $"{parent.path}/{subMixer.name}";
var searchPathResults = mixer.FindMatchingGroups(searchPath);
var index = Array.IndexOf(searchPathResults, subMixer);
if (index < 0)
{
// If the entry was not found in this searchPath, then search in the previous parent
ancestorStack.Pop();
// Debug.LogWarning($"Entry not at {searchPath}, back-track to previous parent {ancestorStack.Peek().Path} and re-iterate");
goto RETEST_ELEMENT; // <-- WARNING GOTO
}
var isProbableNode = searchPathResults.Length != 1;
// A LEAF may be falsely detected as a NODE:
// If the path of this node is a partial path of another deeper path
// there is no way to verify it is a LEAF before the whole tree has been resolved
// The only side-effect is an extra iteration, where we test the leaf-node
// for children before back-tracking.
var node = new AudioMixerGroupHierarchy(subMixer, parent, ancestorStack.Count);
parent.children.Add(node);
if (isProbableNode)
{
ancestorStack.Push(node);
}
}
return root;
}
IAudioMixerGroupHierarchy IAudioMixerGroupHierarchy.Parent => parent;
AudioMixerGroup IAudioMixerGroupHierarchy.Group => mixerGroup;
IReadOnlyList<IAudioMixerGroupHierarchy> IAudioMixerGroupHierarchy.Children => children;
int IAudioMixerGroupHierarchy.Depth => depth;
string IAudioMixerGroupHierarchy.Path => path;
}
}
public interface IAudioMixerGroupHierarchy
{
public IAudioMixerGroupHierarchy Parent { get; }
public AudioMixerGroup Group { get; }
public IReadOnlyList<IAudioMixerGroupHierarchy> Children { get; }
public int Depth { get; }
public string Path { get; }
}