Cabbage Logo
Back to Cabbage Site

CsoundUnity Package (UPM) development

Ok fine!

very noisy indeed

Mneawhile, Iā€™m trying the audio clip route, but itā€™s not working either. Iā€™ve tried

  • using the OnAudioRead() method: I can send audio to a node this way, but itā€™s noisy like the other attempts using OnAudioFilterRead()
  • Using AudioClip.SetData in teh yield callback, but Unity gives me an error saying I can only call SetData from the main thread. So setData must be called on the main thread, but we have no way to schedule calls to it unless we use some kind of yield callback.

I feel like Iā€™m going around in circles here :laughing:

Tried to sync the audiosources like this:

private void Update()
{
    foreach (var node in csoundUnityNodes)
    {
        node.AudioSource.timeSamples = this.audioSource.timeSamples;
    }
}

same noise

This can be done like I did in the CsoundFileWatcher, filling a queue of actions

static Queue<Action> _actionsQueue = new Queue<Action>();

fill the queue like this:

           lock (_actionsQueue)
                _actionsQueue.Enqueue(() =>
                {
                    //your action here
                });

and execute in the Update of any object like this:

float _lastUpdate;
float _timeBetweenUpdates = .2f;

private static void Update()
{
    var startTime = Time.realtimeSinceStartup;
    if (startTime > _lastUpdate + _timeBetweenUpdates)
        lock (_actionsQueue)
        {
            while (_actionsQueue.Count > 0)
            {
                var action = _actionsQueue.Dequeue();
                if (action == null)
                    continue;

                action();
            }
            _lastUpdate = Time.realtimeSinceStartup;
        }
}

Hmm. Ok, I have to return to something else now for a moment, but this is what my yield callback looks like. You need a:

private AudioClip clip;
private clipKsmpsIndex =0;
MYFLT[] channelSamples;

And the callback:

SetYieldCallback(() =>
{
    float[] newSamples = new float[DSPBuffersize];
    clip.SetData(newSamples, 0);
    for ( int i = 0; i < newSamples.Length; i++, clipKsmpsIndex++)
    {
        if(clipKsmpsIndex >= ksmps)
        {
            channelSamples = GetAudioChannel("oscil");
            clipKsmpsIndex = 0;
        }

        newSamples[i] = (float)channelSamples[clipKsmpsIndex];
    }

    clip.SetData(newSamples, 0);
});

But I donā€™t know, passing actions seems like weā€™re wasting time moving things around while there has to be an easy and fast way to do this

For example this guy says itā€™s working for him:

https://forum.unity.com/threads/multiple-audio-sources-simultaneously-playing-the-same-audio-clip-best-approach.541478/

Hmm, weā€™ve already tried that though? In his case he can write a full Unity DSP buffer of samples on each call to OnAudioFilterRead(). And then pick up a full bufferā€™s worth in his slave components. In our case we have two different buffer sizes, so we canā€™t update the sample array while OnAudioFilterRead is running. :exploding_head:

I still feel the audio clip way should work? I mean, how hard can it be to dynamically create an audio clip in a master object, and pass it to children?!

But we would have to use the SetData method, as we donā€™t want unique OnAudioRead methods for each Csound channel. But SetData that canā€™t be called on k-boundaries, unless we use some of your black magicā€¦:thinking:

ok fair enough Iā€™ll try with SetData!

Iā€™ll keep my fingers crossed. If we can send audio like this to child components it will be an amazing feature.

first I should convert the MYFLT to floatā€¦
I thought another thing Iā€™d like to try first, in the master OnAudioFilterRead we can fill an array of samples obtained from GetAudioChannel, and just then update the slaves.
Iā€™ll try this

Iā€™m pretty sure I tried that already, but have another go, I may v have screwed it upā€¦

Almost there, tried with a dictionary, just had lunch break :sweat_smile:

what do we get with GetAudioChannel? 32 samples with both audio channels (L R interleaved)?

The problem I see here:

Is that the PerformKsmps and the ksmpsIndex = 0 will happen when ksmpsIndex is 32 but will always miss one sample of the R channel

notice Iā€™m trying to reset later:

                        if (channel == (numChannels - 1))
                        {
                            ksmpsIndex = 0;
                        }

No, just a single array of 32 MYFLT samplesā€¦

I just tried filling a Unity DSP buffer sized array of samples from Csound, and then passing that to the child object, but I still get crappy audio. In my child Iā€™m trying to pick up channelSamples in my OnAudioFilterRead() method, but same result as alwaysā€¦

public void ProcessBlock(float[] samples, int numChannels)
{
    if (compiledOk)
    {
        
        for (int i = 0; i < samples.Length; i += numChannels, ksmpsIndex++)
        {
            for (uint channel = 0; channel < numChannels; channel++)
            {
                if (mute == true)
                    samples[i + channel] = 0.0f;
                else
                {
                    if ((ksmpsIndex >= ksmps) && (ksmps > 0))
                    {
                        tempArray = GetAudioChannel("oscil");
                        PerformKsmps();                            
                        ksmpsIndex = 0;
                    }

                    if (processClipAudio)
                    {
                        SetInputSample((int)ksmpsIndex, (int)channel, samples[i + channel] * zerdbfs);
                    }

                    channelSamples[i] = (float)tempArray[ksmpsIndex];                         
                }
            }
        }            
    }
}
channelSamples[i] = (float)tempArray[ksmpsIndex];

shouldnā€™t it be

channelSamples[i + channel] = (float)tempArray[i + channel];

?

EDIT No they are different size, let me think a bit

Yes, of course! My bad. So, weā€™re up and running with this :wink:

Gees, what a nightmare that was, and the solution was so obvious in the end!

Should I send you what I have and you can make it look like real c# code :laughing:

:partying_face:
nouo I had the feeling I was close too! but glad that youā€™re faster :sweat_smile: