Cabbage Logo
Back to Cabbage Site

CsoundUnity Package (UPM) development

Actually, this is better, but still not right:

public void ProcessBlock(float[] samples, int numChannels)
{
    
    //print(channelSamples.Length);
    for (int i = 0; i < samples.Length; i += numChannels, ksmpsIndex++)
    {
        for (uint channel = 0; channel < numChannels; channel++)
        {
            if (ksmpsIndex >= csoundUnity.GetKsmps())
            {
                channelSamples = csoundUnity.GetAudioChannel(csoundChannels[0]);
                ksmpsIndex = 0;
            }
                

            samples[i + channel] = (float)channelSamples[ksmpsIndex];
            
        }
    }
}

The audio data we obtain is interleaved right?
Then it must be right!

Looks right to me. In this case a single channel of audio is coming from Csound. So we give each Unity channel the same sample as we move through the buffer.

Unity-Channel1-Sample1 = Csound-Sample1
Unity-Channel2-Sample1 = Csound-Sample1
Unity-Channel1-Sample2 = Csound-Sample2
Unity-Channel2-Sample2 = Csound-Sample2

etcā€¦

I think itā€™s an issue with the ksmpsIndex counter. That was my first concern when I started thinking about this. I hope Iā€™m wrongā€¦

As long as it is a submultiple of the ā€œsamplesā€ length, I see no problem!
It should call the GetAudioChannel method 64 times.
Maybe thereā€™s some problem with Marshal.AllocHGlobal/Marshal.FreeHGlobal on the IntPtr of the buffer??
What kind of audio problem are you hearing?

How do you figure that? Iā€™m calling it each time we run through ksmps worth of samples?

I look at that, but it seems fine to?

The pitch is too high, and there are periodic dropouts.

Iā€™m trying to figure out if this is expected behaviour. My stinct tell me it is. It seems to me that we should only be calling csoundUnity.GetAudioChannel() on each k-boundary. The question is how.

There is a csoundSetYieldCallback() that gets triggered on each k-boundary? But Iā€™ve no idea how to implement callback functions in c#?
https://csound.com/docs/api/group___t_h_r_e_a_d_i_n_g.html#ga55b35afa169c48e68d6b13674d616295
If we could add a callback in our CsoundUnity ā€˜nodeā€™ component we could call csoundGetAudioChannel() there. Hmmā€¦

Because everytime OnAudioFilterRead is called, Unity sends a block of 2048 samples, so 2048 / 32 = 64

The pointer management seems fine, itā€™s like we managed the others arraysā€¦

I can try to implement the csoundSetYieldCallback() method, hoping it helps somehow!

Comā€™on itā€™s the last hard thing to do then this package will be awesome! And powerful as hell!!!

Yup! Agreed. Let me know how you get on with the callback method. A quick google search makes it appear possible at least!

I wrote something but it doesnā€™t work, maybe I donā€™t understand how it works!
can you try it? I already pushed it!

Just looking now. I am trying to write a wrapper in CsoundUnity for it, but am kind of lostā€¦?

Yes youā€™re right you have to use it inside OnAudioFilterRead!
Give me one sec

Basically we need to register a function with this method. In C I would do something like this:

int callback(CSOUND* csound){
//do something
}

and then:

csoundSetYieldCallback(callback);

Ok try now, you should implement the body of the YieldCallback found at line 486 of CsoundUnity. But it doesnā€™t seem to work!

Before something ugly happens delete the line 168 of CsoundUnityBridge, there are two allocations of the Callback!

This looks good, but I need to define my callback function in a local script, not in CsoundUnityā€¦

And I register it after I create my CsoundUnity objectā€¦

But right now Iā€™m having issues wrapping the new methods you wrote. When i try the following in CsoundUnity:

public void SetYieldCallback(csoundcsharp.Csound6.NativeMethods.YieldCallback callback)
{
    csound.SetYieldCallback(callback);
}

I get this error about accessibility?

yes because itā€™s internal!
Shouldnā€™t the callback be set once? Itā€™s in the Awake of CsoundUnity line 228

The idea here is that we write a function in our local script, say a cube controller. We then pass this function to csound.SetYieldCallback(). Then on every k cycle our function is called. You can register as many callback functions are you wish.

It would look like this:

private void Awake()
{
    csoundUnity = csoundUnityGameObject.GetComponent<CsoundUnity>();
    if (!csoundUnity)
        Debug.LogError("CsoundUnity was not found?");
}

void Start()
{
    csoundUnity.SetYieldCallback(YieldCallback);
}

event csoundcsharp.Csound6.NativeMethods.YieldCallback YieldCallback = new csoundcsharp.Csound6.NativeMethods.YieldCallback((csd) =>
{
    Debug.Log($"callback? ");
    return 1;
});

[edit] btw, this is just how I have used these kind of functions in C/C++. Iā€™ve no idea if this is feasible with C#, but for me, the callback stuff worked Ok. We just need to make it possible to register the callback outside of CsoundUnityā€¦

Ok got it, Iā€™ll wrap it

1 Like

Ok itā€™s there!