Cabbage Logo
Back to Cabbage Site

CsoundUnity Package (UPM) development

Confirmed.
I’ll look into this in the afternoon.
Yes please use this thread if you find bugs in the dev2 branch!

1 Like

Found the problem.
But I don’t know how to solve it! for sure @rorywalsh knows what’s going on :smiley:
Basically, with GetAudioChannel we get an array of MYFLT with length 16, while it should be of 32.
Or is it correct?
Then when setting again the namedAudioChannelDataDict from the namedAudioChannelTempBufferDict we have an out of bounds exception because ksmpsIndex is 16 and the array length is 16.
:thinking:
The question is: how could it work when it is working?

I found that GetKsmps() is returning 16, can it change at runtime?
:hushed:

@Alex try this fix:

in Process block change the line 558:

if ((ksmpsIndex >= ksmps) && (ksmps > 0))

with

if ((ksmpsIndex >= GetKsmps()) && (GetKsmps() > 0))

it should work like this, or at least no more exceptions.

Remains the question: why ksmps changes at runtime?

Yes, but in this case it’s simple because Iain has set ksmps to 16 in his orchestra. I thought we override this anyway?

So maybe the options are not working!

We are doing this:

    Csound6.NativeMethods.csoundSetOption(csound, "-n");
    var parms = GetParams();
    parms.control_rate_override = 32;
    parms.sample_rate_override = AudioSettings.outputSampleRate;
    parms.e0dbfs_override = 1;
    SetParams(parms);
    int ret = Csound6.NativeMethods.csoundCompileCsdText(csound, csdFile);

This looks fine to me…? What happens with a simple example, does the ksmps revert back to whatever is in the .csd file?

Tried with basic example setting ksmps 64, when I read the params in the override_ksmps I have 0, which I assume is correct so the csd ksmps is mantained.
After setting the new params I get ksmps 32, which is correct too!
I’ll keep calling GetParams anywhere to see where it changes

I don’t think calling GetParams() will return anything useful. As far as I know that data structure is not updated again once a performance starts.

OK then I’ll try polling GetKsmps() in OnAudioFilterRead
The good thing with this is that we could have finally found the real problem that was hiding from our sight (and so explain why sometimes audio was going crazy)

I can confirm that SetParams is not working:

Schermata 2020-05-19 alle 16.15.00

I’ll take a look into this myself a little later…

Also tried placing it after the csoundCompileCsdText, no change

I have lots of crashes when going back to pause, I saw this message in the Editor logs:

CheckDisalowAllocation. Allocating memory when it is not allowed to allocate memory. Label used for allocation: String.

I think it could be related to CsoundLogging?
I’ll investigate now: :male_detective:

About the new method CreateTableInstrument, I couldn’t make it work, lots of crashes!

the method is this:

public int CreateTableInstrument(int tableNumber, int tableLength)
{
    string createTableInstrument = String.Format(@"schedule 9999, 0, 0
    instr 9999
     gisampletable{0} ftgen {0}, 0, {1}, -2, 0, 0
    endin",
    tableNumber, -tableLength * AudioSettings.outputSampleRate);

    return CompileOrc(createTableInstrument);
}

and I’m calling it inside another method, where then I’d like to know if the table has been created (I’d like to read lots of zeros), and then set the table with the supplied samples.

public void CreateTable(int tableNumber, MYFLT[] samples)
{
    if (samples.Length < 1) return;

    var resTable = CreateTableInstrument(tableNumber, samples.Length);
    if (resTable != 1) return;

    int res = GetTable(out MYFLT[] test, tableNumber);
    /// res is always -1!
    if (res != -1) {
        // log 0 values
        for (var i = 0; i < test.Length; i++)
        {
            Debug.Log($"{i}: {test[i]}");
        }
        // set values 
        for (var i = 0; i < samples.Length; i++)
        {
            SetTable(tableNumber, i, samples[i]);
        }
    }
}

CompileOrc returns 0, so I assume the orc string I’m passing is correct.
The problem is that GetTable is always returning -1, so table is not found.

GetTable is this:

/// <summary>
/// Stores values to function table 'tableNum' in tableValues, and returns the table length (not including the guard point). 
/// If the table does not exist, tableValues is set to NULL and -1 is returned.
/// </summary>
public int GetTable(out MYFLT[] tableValues, int numTable)
{
    int len = Csound6.NativeMethods.csoundTableLength(csound, numTable);
    if (len < 1)
    {
        tableValues = null;
        return -1;
    }

    IntPtr tablePtr = new IntPtr();
    tableValues = new MYFLT[len];
    int res = Csound6.NativeMethods.csoundGetTable(csound, out tablePtr, numTable);
    if (res != -1)
        Marshal.Copy(tablePtr, tableValues, 0, len);
    else tableValues = null;
    Marshal.FreeHGlobal(tablePtr);
    return res;
}

What am I doing wrong?

(As you probably noticed I added some more checks around to avoid crashes, most of the time I’m just restarting Unity again and again :yawning_face:)

Everything seems good until the call to

Marshal.FreeHGlobal(tablePtr)

If I don’t free the pointer it works!
But then we have a leaking code…
We would need a call to free the pointer in Csound :thinking:
Is there something like this?

I pushed an update. For now I just set the IntPtr to zero in GetTable, hope it will not leak too much, but at least no more crashes.
The code to load a table in Csound from clips in Resources/Samples is something like this:

    var count = 0;
    foreach (var clip in clips)
    {
        var name = "Samples/" + clip.name;

        Debug.Log("loading clip " + name);
        var samples = CsoundUnity.GetSamples(name, CsoundUnity.SamplesOrigin.Resources);
        Debug.Log("samples read: " + samples.Length);
        if (samples.Length > 0)
        {
            var tn = 900 + count;
            var res = csoundUnity.CreateTable(tn, samples);

            Debug.Log(res == 0 ? $"<color=green>Table {tn} created</color>" : $"<color=red>Error: Couldn't create Table {tn} </color>");
        }
        yield return new WaitForEndOfFrame();

        count++;
    }

GetSamples is this (not complete yet):

public static MYFLT[] GetSamples(string source, SamplesOrigin origin)
{
    MYFLT[] res = new MYFLT[0];

    switch (origin)
    {
        case SamplesOrigin.Resources:
            var src = Resources.Load<AudioClip>(source);
            if (src == null)
            {
                res = null;
                break;
            }
            var data = new float[src.samples];
            src.GetData(data, 0);
            res = new MYFLT[src.samples];
            var s = 0;
            foreach (var d in data)
            {
                res[s] = (MYFLT)d;
                s++;
            }
            break;
        case SamplesOrigin.StreamingAssets:
            break;
        case SamplesOrigin.External:
            break;
    }

    return res;
}

Just read this in Csound6Net:

    /// <summary>
    /// Produces a managed copy of this table containing its current contents in csound 
    /// at the time of copying.
    /// This version uses the legacy csoundGetTable function which is not threadsafe.
    /// Use the threadsafe CopyOut and CopyIn methods which are new with csound 6.0.
    /// </summary>

Should have used TableCopyIn!

 /// Copy the contents of an array source into a given function table 
 /// The table number is assumed to be valid, and the table needs to have sufficient space to receive all the array contents.
 /// </summary>
 public void TableCopyIn(int table, MYFLT[] source)

I’m surprised you didn’t? I was calling it in the sample code I posted yesterday, but not because I knew it was threadsafe, but because it could push loads of samples at once :laughing:

Sorry I didn’t get around to looking into this, I’ve quite a lot of end-of-semester corrections to go through.

Totally forgot of its existence! Let’s say I did some debugging around :slight_smile:
Later I can finish to update the Sequencer example to make it work with tables, or do you want to do this?