Cabbage Logo
Back to Cabbage Site

CsoundUnity Package (UPM) development

Btw about the FileWatcher, when the csd changes I reparse it, and obviously we lose all the settings made before on the channels of CsoundUnity instances.
I think that even if now the inspector is keeping changes, the preset feature is totally needed!

Good to know. Iā€™m just looking into the node component here. We need to access a ksmps sized buffer of audio from an audio rate channel. Our current getChannel() method returns a single k rate value. In C++ I can also access a-rate channel data, but as I said, itā€™s in the form of an array. So we would need to update this method:

public MYFLT GetChannel(string channel)
{
    return csound.GetChannel(channel);
}

which in turns means updating:

public MYFLT GetChannel(string channel)
{
    return Csound6.NativeMethods.csoundGetControlChannel(csound, channel, IntPtr.Zero);
}

What about providing a wrapper to this function:

// PUBLIC int csoundGetChannelPtr (CSOUND *, MYFLT **p, const char *name, int type)

If we get a pointer we can access the full sample array? My only concern is that we may need to use the same ksmpsIndex. My idea is something as simple as this:

void OnAudioFilterRead(float[] data, int channels)
{
   // MYFLT[] samples = csoundUnity.getChannel("bombSounds");
   //and pass samples to this component's audiosource buffer...
}

Iā€™m not sure about trying to access ksmpsIndex from the main CsoundUnity object. I donā€™t even know if Unity will let us. An independent ksmps index might work, but Iā€™ve no idea until I try it out.

[edit] So, the idea is that you generate your sounds in a master instance of CsoundUnity and send them to other objects using chnset:

instr 2
   a1 oscili 1, 200
   chnset a1 "oscil" 
endin

And it will be picked up in the node object by simply calling csoundUnity.GetChannel(). Once there, Unity will apply all the spatial attributes to it :wink: So we get sounds local to the object, that are generated elsewhere.

I pushed the FileWatcher update. It wasnā€™t easy as expected.
Tomorrow Iā€™ll look into the methods to add, this seems a great design but Iā€™m not sure I understand it totally. Maybe I need some sleep!

2 Likes

Iā€™ll take a look at the file watcher stuff tomorrow. Thatā€™s another big feature youā€™ve just added. Youā€™re on a roll!

1 Like

It seems I managed to

The problem is that I cannot filter the objects it shows by string, so thereā€™s every object of type ā€œDefaultAssetā€ in the window (called ā€œObjectPickerā€) that opens.


If we want to filter the files we have to open this window with EditorGUIUtility.ShowObjectPicker, but this can be called just from a custom button, thereā€™s no way to intercept the click in the above circle button. We could fake the button to be like the inspector field, but itā€™s a bit trivial!
The circle button can be drawn more or less like this:

var testRect = GUILayoutUtility.GetRect(0.0f, 20.0f, GUILayout.ExpandWidth(true));
GUI.Button(testRect, "testCircle", GUI.skin.GetStyle("IN ObjectField"));

Look at the difference between the inspector field where I can drag csd files (any other kind of file would null the csd reference of CsoundUnity) and the testCircle field:

Schermata 2020-05-11 alle 16.13.33

I think that if thereā€™s some more important feature to add this could be left for later!
Btw apart from the filter it seems to work. Iā€™ll push the update to dev2 so that we can choose later!
Then Iā€™ll look into the new methods to add.

I found this code in the example 9 of Csound6Net:

var bus = c.GetSoftwareBus();//Get bus and define the "amp" and "freq" channels of orc3
//Since orc3 instrument doesn't declare channels directly, define them here
var ampChannel = bus.AddControlChannel("amp", ChannelDirection.Input);
var freqChannel = bus.AddControlChannel("freq", ChannelDirection.Input);

//Prime with initial values accessing channel memory directly
//That is, it contains a call to csoundGetChannelPtr internally.
ampChannel.SetValueDirect(amps.Value); //Use 
freqChannel.SetValueDirect(freqs.Value);

Very interesting! Maybe Iā€™ll have a look at the GetSoftwareBus and SetValueDirect methods, and port them to CsoundUnity!!

Interesting, although Iā€™m not sure how useful they are. I canā€™t think of many reasons where one would need to declare a channel outside of Csound? The setValueDirect() methods just duplicate:

csoundUnity.setChannel()

Or am I missing something?

No It should be a not threadsafe version to manage AudioChannels, as stated by the docs:

    /// <summary>
    /// Provides slightly quicker, but not threadsafe, updates to a channel's current value
    /// as opposed to this class's threadsafe "Value" property.
    /// Saves a hashtable lookup of the channel by name in csound's memory.
    /// Uses csoundGetChannelPtr to acquire and keep the pointer to this channel's location
    /// in csound's unmanaged memory.
    /// Best used from the same thread such as when updating channels between calls to
    /// PerformKsmps or PerformBuffer.
    /// </summary>
    /// <param name="value"></param>
    public void SetValueDirect(double value)
    {
        var v = new double[1];
        v[0] = value;
        IntPtr pData = GetChannelPointer();//will throw exception if fails
        Marshal.Copy(v, 0, pData, 1);
    }

Value instead get/set an AudioChannel:

    /// <summary>
    /// Copies an audio channel's contents to/from a managed array for use in .net algorithms.
    /// This property is the threadsafe way to move values between .net and csound's unmanaged memory.
    /// </summary>
    public override object Value
    {
        get
        {
            Double[] dest = new Double[Resize(m_csound.Ksmps)];//include nchnls/nchnlss_i? no, not an output channel: just a single ksmps-sized buffer
            NativeMethods.csoundGetAudioChannel(m_csound.Engine, Name, m_buffer);
            Marshal.Copy(m_buffer, dest, 0, dest.Length);
            return (object)dest;
        }
        set
        {
            Double[] source = value as double[];
            Resize(m_csound.Ksmps);
            Marshal.Copy(source, 0, m_buffer, Math.Min(source.Length, m_bufsiz));
            NativeMethods.csoundSetAudioChannel(m_csound.Engine, Name, m_buffer);
        }
    }

CsoundUnity.SetChannel is for a ControlChannel:

public void SetChannel(string channel, MYFLT value)
{
    Csound6.NativeMethods.csoundSetControlChannel(csound, channel, value);
}

So maybe is what we need??
Iā€™m going to implement this!
Btw have you checked the dev2 branch? Itā€™s working good now!
But needs some tests!

Jut on the dev2 branch now. I never saw the getAudioChannel() method before. Nice one. thatā€™ll do nicely. Let me know when itā€™s pushed and I will test out my node interfaceā€¦ :wink:

Yes you can grab it now!

Ah, but this doesnā€™t have the GetAudioChannel or?

Not yet! Iā€™ll add it in a couple of hours, this morning I was busy!
This version has the new way of setting the csd

Cool. Do both ways work then in this version> Dragging to the drag field AND selecting from the assets browser?

Yes exactly, hope they work in the same way :sweat_smile:

I quickly put together the Get/SetAudioChannel methods, hope they wonā€™t lead to lots of crashes :smiley:
Iā€™ll test them now!

If you push I can test here too :wink:

Should be there! always on dev2

Great. Just testing hereā€¦

[edit] we have audio :wink: Now the problem is weaving the channel audio data into the audio source bufferā€¦

WOW!
It shouldnā€™t be difficult!
https://docs.unity3d.com/ScriptReference/AudioClip.SetData.html

Ha, no thatā€™s not the tricky bit. This SHOULD work, but Iā€™m wondering if not being synced to the ksmpsIndex of the master component is causing the audio problemsā€¦ :thinking:

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

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