Unity FMOD 16 speakers audio output

Hi
I am trying to set up a 16-speaker system using MOTU hardware, and I’m using FMOD in Unity to play audio. In Unity, an audio source moves around the player in different directions, and I want to hear this spatial movement across my speaker array.
Currently, when the audio source moves around the player in a circle, for example, it does not sound like the sound is being distributed correctly across all speakers or going around the listener. The FMOD Preferences are set to ASIO. Any ideas what I am doing wrong or what I am missing in my set up? Thank you in advance

Hi,

I can’t precisely diagnose the issue without hearing the output behavior from your 16 speakers, but it’s likely there’s an issue with how the output of the FMOD system is being upmixed to 16 channels. While the Core API supports up to 32 raw channels, the Studio API has a maximum format of 7.1.4 (without getting a little hacky), so it’s likely that upmix from the output doesn’t match what the MOTU hardware is expecting.

I have a few questions:

  • What version of FMOD are you using?
  • What surround format, if any, is the MOTU hardware using?
  • What exact audio behavior are you observing, and what are you expecting? i.e. are some speakers skipped, or is FMOD outputting in what seems like 7.1.4 where you’d instead like 16 raw channels around the listener?

While it may not resolve the issue, you can try grabbing the master channel group of your FMOD Studio system’s underlying Core system with System::getMasterChannelGroup, then using ChannelControl::setMixMatrix to customize the mix matrix to your liking.

Hi,

Thanks for getting back to me.

We’re using FMOD Studio version 2.03.14.

The MOTU hardware is not using any surround format. It’s simply configured to output to 16 separate speakers, with each speaker connected directly to its own channel.

The behavior we’re seeing is that FMOD appears to be treating the output as a standard surround setup (similar to 7.1.4) rather than 16 discrete channels. We currently have the FMOD output channel count set to Auto.

For example, when we place an audio source to the player’s right side in Unity, we would expect the sound to come primarily from the speakers physically located on the right side of the installation. Instead, the sound is routed to speakers at the front, and some speakers seem to be skipped entirely on different sound source positions around the player. This makes it appear as though FMOD is applying surround speaker mapping rather than addressing all 16 output channels independently.

Hi
I think I’ve managed to get the audio to distribute through 16 the speakers the way I want, but it only works when using the Master Group (RuntimeManager.CoreSystem.getMasterChannelGroup(out masterGroup)).

However, using only the Master Group causes different audio sources to behave as if they are linked together. For example, if I have a bird sound coming from the left and wind coming from the right, and I move them (forward and backward), they all start mixing together and coming from the same speakers. Instead of the bird staying on the left speaker and the wind staying on the right, everything collapses into a single mixed output.

As soon as I switch to .getChannelGroup(out channelGroup), the spatial distribution in Unity stops working correctly. Any idea why the channel group is behaving like this?

I am sorry but FMOD_MAX_CHANNEL_WIDTH = 32 ??

Whoops, I misremembered. Thanks for pointing that out.

I have done a project with similar speaker setup and we’re decided to use Dolby Atmos for Home Theater driver, finally. It fixed a lot of speaker distribution problems that we had with other drivers, so you might wanna try this if it is possible to link Dolby Atmos for Home Theater and MOTU to work together

Hi

I managed to make multiple audio sources work and distribute sound through all speakers correctly, meaning that when an audio source moves around the listener in the scene, the sound is correctly distributed across each speaker. However, there is still one issue.

Even though I have multiple audio sources, it seems they are all being mixed on the main bus at the end, which prevents me from controlling each audio event separately. As a result, I hear a combined audio mix from the speakers rather than distinct audio signals from each event.

For example, consider a bee sound on the left and a waterfall sound on the right relative to the listener in Unity. Instead of hearing them as separate sources, I hear a mixed bee-and-waterfall signal coming from both the left and right speakers. If the bee sound source starts moving, the bee and waterfall sounds are effectively combined, so I hear the combined sound move rather than just the bee sound, while another combined signal (representing the waterfall sound) remains on the right side. However, I expect the bee sound to remain independent from the waterfall sound. When the bee source moves in the scene, it should move between speakers independently and only mix with the waterfall sound when they are spatially close. They should separate again when the bee moves away.

Currently, I am implementing a global 16-channel spatial mix. Each sound calculates its own distance-based loudness and directional weighting, and a central mixer combines all active sounds into a shared spatial field, which is then applied through FMOD’s master mix matrix.

So, is there any way to separate the audio output so that sounds only mix when they are supposed to, and not all the time?

Again, it’s difficult to precisely diagnose the issue without hearing the output behavior myself, but from your description, it sounds like your events’ panning info is being lost at some point when being upmixed/downmixed. There are 3 stages at which this might be occurring:

  • The FMOD system’s mixer format → the master bus output format
  • The master bus output format → the ASIO driver’s format
  • The ASIO driver format → MOTU hardware

I suspect the first or second stage is the issue.

Can I get some more info from you?

  • Are you using the Studio API (i.e. banks, events), or the Core API (i.e. individually created sounds)?
  • How specifically have you managed to “make multiple audio sources work”, as mentioned in your first paragraph?
  • From what you can tell, are the Bee and Waterfall sounds together because of a channel mixing issue, or because they’re being provided with the same 3D data for spatialization using ChannelControl::set3DAttributes or Studio::EventInstance::set3DAttributes (or whatever other method you’re using to spatialize them)?

If possivle, it would be great if you could upload a stripped down version of your FMOD and Unity projects to your FMOD User profile so that I can take a closer look.

We were trying to use Studio API and have just mono audio events without any effects in FMOD.

On each audio source we have a MOTUSound script that:
Loads an FMOD sound from fmodPath.
Creates a dedicated FMOD channel/group for that sound.
Starts playing the sound.
Measures distance between the sound object and the listener.
Reduces volume as the listener moves farther away.
Calculates the sound’s direction relative to the listener.
Computes how much of the sound should go to each of 8 speakers.
Normalizes the speaker weights so they add up correctly.
Sends the final speaker mix to FMOD using a mix matrix.
Stops and releases FMOD resources when destroyed.

Then we have a mixer code that:
Maintains a list of all active MOTUSound sources.
Gets access to FMOD’s master output group.
Collects speaker weights from every sound source.
Combines all sources into a single 8-speaker output mix.
Uses each source’s volume as its contribution strength.
Prevents speaker levels from exceeding 100% (anti-clipping).
Applies the combined 8-channel mix to the master output.

Here are two codes:

using UnityEngine;
using FMOD;
using FMODUnity;

public class MOTUSound : MonoBehaviour
{
    \[Header("FMOD Sound Path (Core API)")\]
    public string fmodPath;

    [Header("Listener")]
    public Transform listener;

    [Header("Distance")]
    public float maxDistance = 20f;

    [Range(0f, 1f)]
    public float volume = 1f;

    public float[] weights = new float[8];

    private FMOD.Sound sound;
    private FMOD.Channel channel;
    private FMOD.ChannelGroup group;

    void Start()
    {
        RuntimeManager.CoreSystem.createChannelGroup(
            gameObject.name,
            out group);

        RuntimeManager.CoreSystem.createSound(
            fmodPath,
            FMOD.MODE.DEFAULT,
            out sound);

        RuntimeManager.CoreSystem.playSound(
            sound,
            group,
            false,
            out channel);
    }

    void Update()
    {
        if (!channel.hasHandle() || listener == null) return;

        float distance =
            Vector3.Distance(
                transform.position,
                listener.position);

        float t =
            1f - Mathf.Clamp01(
                distance / maxDistance);

        volume = Mathf.Pow(t, 2.2f);

        channel.setVolume(volume);

        Vector3 offset =
            transform.position -
            listener.position;

        float angle =
            SpatialMath.AngleFromDirection(offset);

        float total = 0f;

        for (int i = 0; i < 8; i++)
        {
            weights[i] =
                SpatialMath.SmoothWeight(
                    angle,
                    SpeakerLayout.Angles[i]);

            total += weights[i];
        }

        if (total > 0.0001f)
        {
            for (int i = 0; i < 8; i++)
                weights[i] /= total;
        }

        float[] matrix = new float[8];

        for (int i = 0; i < 8; i++)
            matrix[i] = weights[i];

        channel.setMixMatrix(matrix, 8, 1, 1);
    }

    void OnDestroy()
    {
        if (channel.hasHandle())
            channel.stop();

        if (sound.hasHandle())
            sound.release();

        if (group.hasHandle())
            group.release();
    }
}

using UnityEngine;
using FMODUnity;
using FMOD;
using System.Collections.Generic;

public class MOTUSpatialMixer : MonoBehaviour
{
    private static readonly List sources = new();

    private ChannelGroup masterGroup;

    private float[] final = new float[8];

    public static void Register(MOTUSound s)
    {
        if (!sources.Contains(s))
            sources.Add(s);
    }

    public static void Unregister(MOTUSound s)
    {
        sources.Remove(s);
    }

    void Start()
    {
        RuntimeManager.CoreSystem.getMasterChannelGroup(
            out masterGroup);
    }

    void Update()
    {
        if (!masterGroup.hasHandle())
            return;

        System.Array.Clear(
            final,
            0,
            final.Length);

        for (int s = 0; s < sources.Count; s++)
        {
            var src = sources[s];

            if (src == null)
                continue;

            float energy = src.volume;

            for (int i = 0; i < 8; i++)
            {
                final[i] +=
                    src.weights[i] * energy;
            }
        }

        float max = 0f;

        for (int i = 0; i < 8; i++)
            max = Mathf.Max(max, final[i]);

        if (max > 1f)
        {
            float inv = 1f / max;

            for (int i = 0; i < 8; i++)
                final[i] *= inv;
        }

        masterGroup.setMixMatrix(
            final,
            8,
            1,
            1);
    }
}

Sure, I will upload the projects to the user profile