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!