Cabbage Logo
Back to Cabbage Site

CsoundUnity Issues + Workflow Suggestions?

Hi all,

Got a rather colossal post here. Apologies if it’s too much for one.

I’m in the early stages of implementing CsoundUnity into my 3D Unity game.
I’m in the process of working out a most efficient and modular workflow for managing instruments in the game world. As this is new to me, i’m not sure what approach would be best when handling many different instruments, or how to organise CsoundUnity components sensibly.

I’d love to know how others go about managing their own audio systems in large game worlds that requires many different instruments. If you can spot anything in my current approach that seem counterintuitive, i’d love to hear it.


Context:

  • The game will have many different instruments for all manner of sounds.

  • The game has a Player, and multiple NPC’s that walk around.

  • For now, i’m just testing a footstep instrument, which is triggered through Animation Events, before I move onto further things. I’ve encountered some issues in getting this working for NPCs in particular.


Setup:

The scene hierarchy contains:

  • 1x central CabbageAudioManager script

The Player and NPC prefabs contain:

  • 1x Audio Source
  • 1x CsoundUnity component
  • 1x Custom instrument sound manager script (AudioFootStepManager).

The Player contains 1x Audio Listener component.

This setup is just early testing purposes, i’m aware it’s going to be pretty awful for performance to have so many CsoundUnity components all existing at the same time, as there is a relatively high density of NPCs. I haven’t figured a solution to this yet. Can I do more with less somehow?


CabbageAudioManager Script

In the hierarchy I have a single AudioSFXManager GameObject that contains a custom CabbageAudioManager script, which acts as a central manager responsible for setting instrument data passed into it by all instrument manager scripts. I use a static instance of this class for shared access of all instrument manager scripts.


CabbageAudioManager Example:

public class CabbageAudioManager : MonoBehaviour
{
    public static CabbageAudioManager Instance { get; private set; }

    private void Awake()
    {
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
        }
        else
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
        }
    }

    public void SetParameter(CsoundUnity cSound, string parameterName, float value)
    {
        cSound.SetChannel(parameterName, value);
        //Debug.Log($"Parameter {parameterName} set to: {value}");
    }

    public void SetTrigger(CsoundUnity cSound, string parameterName, bool state)
    {
        string stateValue = state ? "1" : "0";
        string triggerCommand = $"i\"{parameterName}\" 0 {stateValue}";
        cSound.SendScoreEvent(triggerCommand);
        //Debug.Log($"Trigger {parameterName} set to: {stateValue}");
    }
}

AudioInstrumentManager Scripts

I would plan to use custom AudioInstrumentManager scripts as convention for all different kinds of instruments (AudioFootStepManager, AudioRainManager, AudioDialogueManager etc…) since all instruments will be unique in how their parameters are referenced/handled.

AudioFootStepManager is a script that interfaces with the Cabbage footstep instrument, updating it’s values based on surface types, walking speed etc…
It gets the CsoundUnity component attached to the GameObject on Awake and passes this through to our CabbageAudioManager for triggering/processing the instrument.

The footstep instrument is used for the Player and the NPCS.
It’s triggered through Animation Events for each step taken, via a function ‘TriggerFootstep’ in AudioFootStepManager.


AudioFootStepManager Example

public class AudioFootStepManager : MonoBehaviour
{
    CsoundUnity cSoundObj;

    private const string Instrument = "Footstep";

    private const string GrassParam = "grassMix";
    private const string WoodParam = "woodMix";
    private const string StoneParam = "stoneMix";
    private const string MudMixParam = "mudMix";
    private const string WaterParam = "waterMix";
    private const string SnowParam = "snowMix";

    private int minVal = 0;
    private int maxVal = 1;

    private void Awake()
    {
         cSoundObj = GetComponent<CsoundUnity>();
    }

    private void Start()
    {
     // Instantly set ground material to grass initially
     SetTextureInstant(maxVal, minVal, minVal, minVal, minVal, minVal);

     // Make sure Steps, Drone and Sneak are disabled on Start. (Steps and Drone are utility modes for designing the instruments sound in Cabbage)
     CabbageAudioManager.Instance.SetTrigger(cSoundObj, continuousStepsParam, false);
     CabbageAudioManager.Instance.SetTrigger(cSoundObj, continuousDroneParam, false);
     CabbageAudioManager.Instance.SetTrigger(cSoundObj, SneakParam, false); 
    }

    // Instantly set the surface type values of the footstep instrument.*

    public void SetTextureInstant(float grass, float wood, float stone, float mudMix, float water, float snow)
    {
     CabbageAudioManager.Instance.SetParameter(cSoundObj, GrassParam, grass);
     CabbageAudioManager.Instance.SetParameter(cSoundObj, WoodParam, wood);
     CabbageAudioManager.Instance.SetParameter(cSoundObj, StoneParam, stone);
     CabbageAudioManager.Instance.SetParameter(cSoundObj, MudMixParam, mudMix);
     CabbageAudioManager.Instance.SetParameter(cSoundObj, WaterParam, water);
     CabbageAudioManager.Instance.SetParameter(cSoundObj, SnowParam, snow);
    }

    public void TriggerFootstep()
    {
     *// Trigger the footstep instrument, called by an Animation Event*
     CabbageAudioManager.Instance.SetTrigger(cSoundObj, Instrument, true);
    }
}

ISSUES:

----- Issue 1: -----
Error in the console

This error arises from the Player GameObject.
On Awake in play mode, the error log reads:

‘GameObject has multiple AudioSources and/or AudioListeners attached. While built-in filters like lowpass are instantiated separately, components implementing OnAudioFilterRead may only be used by either one AudioSource or AudioListener at a time.The reason for this is that any state information used by the callback exists only once in the component, and the source or listener calling it cannot be inferred from the callback. In this case the OnAudioFilterRead callback of script CsoundUnity was first attached to a component of type AudioListener on the game object Human after which a component of type AudioSource tried to attach it.’

Debugging efforts:

I have thoroughly checked it’s hierarchy and I definitely only have one AudioSource and one AudioListener on the Player. The NPCs also have only one AudioSource.
There are no other AudioListeners present. I have manually checked, and also searched for the type in the hierarchy, and the search confirms that there is only one on the Player GameObject. This hasn’t seemed to effect the sound however, the Player’s footstep sounds work fine.


----- Issue 2: -----
No footstep sound from NPC’s (EDIT: Resolved! See solution in further messages below)

The instrument itself is working fine for the Player. I’m hearing footsteps as triggered by the Animation Events and the instrument manager is indeed updating the instrument parameters correctly. Testing the button to trigger the footstep produces sound. All of this only seems to work for the Player.

I cannot hear anything on any NPCs, their CsoundUnity and AudioSource components are setup in the same way. However the instrument appears to be triggering correctly, I can see output in the NPC’s CsoundUnity meter as they walk, triggering the footstep. I just cannot hear them. Testing the button to trigger the footstep doesn’t produce any sound either.

Debugging efforts:

  • I checked the AudioSource’s min/max distances to ensure it’s set to a sensible distance, tried different values to ensure the sound wasn’t just being culled. This didn’t have any effect.

  • Ensured that the material type for the footstep instrument is set (Grass, Wood, Stone etc…). If this wasn’t set, then the instrument would be silent. It’s set to maxVal (1) in the Start method of AudioFootStepManager, and the inspector indeed reflects that this is true.

  • Enabled ‘Log Csound Output’ in CsoundUnity component to catch anything strange. It seems the instrument is being triggered, with ‘new alloc for instr Footstep’ appearing in the console for each step taken by the Player and NPCs (i’ve checked both in isolation). I did also see another message, appearing once for the Player and NPC: ‘insert_score_event(): invalid instrument number or name 0’. As I mentioned however, the footstep sound is working correctly for the Player so i’m not sure why this appears.


I’ve attached the current state of my WIP footstep instrument .csd also if this would help further.

FootstepSFX.csd (28.7 KB)

Would love anyones input on all this.

Thank you for reading!

Unity Version: Unity 2022.2.18
Cabbage Version: 2.9.98
CsoundUnity Version: 3.4.0

Hi @WillH0ward-1

First of all, the csd looks impressive, thanks for sharing and keep up the great work!

This is quite challenging for the current CsoundUnity possibilities.

My first thought would be using CsoundUnityChild

The idea is having a single CsoundUnity instance, where different instances of the same instrument send their output to different audio channels - that can be selected in the CsoundUnityChild component.
In this way, each instrument plays for its duration then it is removed from memory, so you don’t really need several CsoundUnity components in the scene if you have to play the same instrument multiple times.

On the Csound script, you can define audio channels for the output of each instrument using chnset:

       chnset asig, "chanL_NPC1"
       chnset asig, "chanR_NPC1"

but in your case you will have to keep track of the different characteristics of the ground the player/NPC is on, so your parameters could look like this:

iForce chnget "stepForce_NPC1" // yes you will have a lot of duplicated code

On the C# part, when sending a score, you can send the number of the channel as a parameter, and then do some routing with it on Csound so that only the NPC1 output or parameter is updated, like

if p5 == 1 then
   iForce chnget "stepForce_NPC1"
   chnset asig, "chanL_NPC1"
   chnset asig, "chanR_NPC1"
elseif p5 == 2 then
   iForce chnget "stepForce_NPC1"
   chnset asig, "chanL_NPC2"
   chnset asig, "chanR_NPC2"
endif

For now you will have to define the channels in advance, hoping that the number of your NPC doesn’t change during gameplay. Or you will have to reuse channels maybe.
Like this you will be able to select the correct output in each player/NPC CsoundUnityChild component.

I will give it a go to see if this works later this evening.

In the next big CsoundUnity version it will be possible to create those channels for CsoundUnityChildren on the fly.
It’s not documented yet but you can find a preview on the 3.5.0 branch, where you have those two new methods:

This branch is unstable at the moment so I wouldn’t suggest using it for production yet. But it could be good having a look, and maybe we can think of something else to add to make this process even smoother.

About the error, that is pretty strange, it could be that some of the GameObjects/prefabs has 2 AudioSources? I’d say remove CsoundUnity and the AudioSource completely and try adding CsoundUnity again.
Also try having a look if any of your prefabs has an AudioListener attached. There must be only one in the scene. (EDIT Sorry you have checked already)

EDIT: Issue 2

It could be related with the first error, it looks like there is something weird going on. I’d try resetting each CsoundUnity instance and then set the csd again in each of them, instead of setting all together. It seems somehow the OnAudioFilterRead calls are conflicting? I tried here and I can have several CsoundUnity instances sharing the same csd with no issues, I can even create copies at runtime and all of them stay indipendent.

Hope this helps

(EDIT: Issue 2 ‘No footstep sound from NPC’s’ solution here!)

Hey @giovannibedetti

Thank you so much for taking the time to respond, hugely appreciated. And thank you for the kind words!!

I’ll certainly give those options you presented a good look!! It does sound promising. I’ll get back to you with any progress.

A quick update too, I managed to stumble across a fix to the non-existent NPC footstep sounds!! The fix was simply enabling ‘Bypass Listener Effects’ in the NPC prefab’s Audio Source! No errors either so it’s all looking and sounding good. The frame rate seems to be more stable after this change also.

Honestly, no idea why this works… if anyone has a technical explanation i’m all ears.

The ’GameObject has multiple AudioSources and/or AudioListeners attached’ message persists but it doesn’t seem to have any effect on the audio itself, the error only appears once on Awake, so I think i’ll blissfully ignore it for now.

I’m not sure why you had to Bypass Listener Effects, probably you have an effect applied on the AudioListener?
It’s interesting to know that it affects the output of CsoundUnity AudioSources.
Probably it uses the same OnAudioFilterRead method we’re using in CsoundUnity to fill AudioSources with Csound output.
I will investigate, thanks for reporting!

1 Like