Dsp_custom.cpp on C#

How can I implement dsp_cusom.cpp functionality on C#.
Give examples please.

Hi LoVic

Here is an example of the dsp code you could use in c# to implement dsp_custom.cpp.

  class DSPManagedData : IDisposable
    {
        public List<FMOD.StringHelper.ThreadSafeEncoding> encoders = new List<FMOD.StringHelper.ThreadSafeEncoding>(1);
        public IntPtr dspAllocation;

        public void Dispose()
        {
            foreach (FMOD.StringHelper.ThreadSafeEncoding encoder in encoders)
            {
                encoder.Dispose();
            }
            Marshal.FreeHGlobal(dspAllocation);
        }
    }

    // We use static buffers to avoid allocating on the mixer and marshalling unnecessary data
    public static float[] inBuffer;
    public static float[] outBuffer;

    [StructLayout(LayoutKind.Sequential)]
    struct PluginData
    {
        public float volume_linear;
        public int length_samples;
        public int channels;
    }

    public static FMOD.RESULT customDspCallback(ref FMOD.DSP_STATE dsp_state, IntPtr inbuffer, IntPtr outbuffer, uint length, int inchannels, ref int outchannels)
    {
        PluginData data = Marshal.PtrToStructure<PluginData>(dsp_state.plugindata);

        Marshal.Copy(inbuffer, inBuffer, 0, (int)(length * inchannels));

        /*
            This loop assumes inchannels = outchannels, which it will be if the DSP is created with '0' 
            as the number of channels in FMOD_DSP_DESCRIPTION.  
            Specifying an actual channel count will mean you have to take care of any number of channels coming in,
            but outputting the number of channels specified. Generally it is best to keep the channel 
            count at 0 for maximum compatibility.
        */
        for (uint samp = 0; samp < length; samp++)
        {
            /*
                Feel free to unroll this.
            */
            for (int chan = 0; chan < outchannels; chan++)
            {
                /* 
                    This DSP filter just scales the output volume! 
                    Input is modified, and sent to output.
                */
                outBuffer[(samp * inchannels) + chan] = inBuffer[(samp * inchannels) + chan] * data.volume_linear;
            }
        }
        Marshal.Copy(outBuffer, 0, outbuffer, (int)(length * outchannels));

        data.channels = inchannels;

        return FMOD.RESULT.OK;
    }
    public static FMOD.RESULT customCreateCallback(ref FMOD.DSP_STATE dsp_state)
    {
        uint blocksize = 0;

        CheckResult(Marshal.PtrToStructure<FMOD.DSP_STATE_FUNCTIONS>(dsp_state.functions).getblocksize(ref dsp_state, ref blocksize));

        PluginData data = new PluginData();
        data.volume_linear = 1.0f;
        data.length_samples = (int)blocksize;

        int size = Marshal.SizeOf(typeof(PluginData));
        IntPtr ptr = Marshal.AllocHGlobal(size);
        dsp_state.plugindata = ptr;
        Marshal.StructureToPtr(data, ptr, false);

        if (inBuffer == null || outBuffer == null)
        {
            inBuffer = new float[blocksize * 8];
            outBuffer = new float[blocksize * 8];
        }

        return FMOD.RESULT.OK;
    }

    public static FMOD.RESULT customReleaseCallback(ref FMOD.DSP_STATE dsp_state)
    {
        if (dsp_state.plugindata != IntPtr.Zero)
        {
            Marshal.FreeHGlobal(dsp_state.plugindata);
        }

        return FMOD.RESULT.OK;
    }

    public static FMOD.RESULT customGetParameterDataCallback(ref FMOD.DSP_STATE dsp_state, int index, ref IntPtr data, ref uint length, IntPtr valuestr)
    {
        if (index == 0)
        {
            uint blocksize = 0;

            CheckResult(Marshal.PtrToStructure<FMOD.DSP_STATE_FUNCTIONS>(dsp_state.functions).getblocksize(ref dsp_state, ref blocksize));

            data = dsp_state.plugindata;
            length = blocksize * 2 * sizeof(float);

            return FMOD.RESULT.OK;
        }

        return FMOD.RESULT.ERR_INVALID_PARAM;
    }

    public static FMOD.RESULT customSetParameterFloatCallback(ref FMOD.DSP_STATE dsp_state, int index, float value)
    {
        if (index == 1)
        {
            PluginData data = Marshal.PtrToStructure<PluginData>(dsp_state.plugindata);
            data.volume_linear = value;
            Marshal.StructureToPtr(data, dsp_state.plugindata, false);

            return FMOD.RESULT.OK;
        }

        return FMOD.RESULT.ERR_INVALID_PARAM;
    }

    public static FMOD.RESULT customGetParameterFloatCallback(ref FMOD.DSP_STATE dsp_state, int index, ref float value, IntPtr valstr)
    {
        if (index == 1)
        {
            PluginData data = Marshal.PtrToStructure<PluginData>(dsp_state.plugindata);

            value = data.volume_linear;
            if (valstr != IntPtr.Zero)
            {
                string output = String.Format("{0}", (int)((value * 100.0f) + 0.5f));
                valstr = Marshal.StringToHGlobalAnsi(output);
            }

            return FMOD.RESULT.OK;
        }

        return FMOD.RESULT.ERR_INVALID_PARAM;
    }
    public void TestCustomDSP(FMOD.Studio.System system)
    {
        DSPManagedData managedData = new DSPManagedData();

        FMOD.DSP_DESCRIPTION dspdesc = new FMOD.DSP_DESCRIPTION();
        FMOD.DSP_PARAMETER_DESC[] paramdesc = new FMOD.DSP_PARAMETER_DESC[2];
        string waveName = "wave data";
        var stringEncoder = new FMOD.StringHelper.ThreadSafeEncoding();
        paramdesc[0].name = stringEncoder.byteFromStringUTF8(waveName);
        paramdesc[0].type = FMOD.DSP_PARAMETER_TYPE.DATA;
        paramdesc[0].desc.datadesc.datatype = (int)FMOD.DSP_PARAMETER_DATA_TYPE.DSP_PARAMETER_DATA_TYPE_USER;
        paramdesc[0].description = waveName;

        string volumeName = "volume";
        paramdesc[1].name = stringEncoder.byteFromStringUTF8(volumeName);
        paramdesc[1].label = paramdesc[1].name;
        paramdesc[1].description = "linear volume in percent";
        paramdesc[1].type = FMOD.DSP_PARAMETER_TYPE.FLOAT;
        paramdesc[1].desc.floatdesc.min = 0;
        paramdesc[1].desc.floatdesc.max = 1;
        paramdesc[1].desc.floatdesc.defaultval = 1;
        paramdesc[1].desc.floatdesc.mapping.type = FMOD.DSP_PARAMETER_FLOAT_MAPPING_TYPE.DSP_PARAMETER_FLOAT_MAPPING_TYPE_AUTO;

        //string dspName = "My first DSP unit";
        //dspdesc.name = stringEncoder.byteFromStringUTF8(dspName);
        dspdesc.version = 0x00010000;
        dspdesc.numinputbuffers = 1;
        dspdesc.numoutputbuffers = 1;
        dspdesc.read = customDspCallback;
        dspdesc.create = customCreateCallback;
        dspdesc.release += customReleaseCallback;
        dspdesc.getparameterdata = customGetParameterDataCallback;
        dspdesc.setparameterfloat = customSetParameterFloatCallback;
        dspdesc.getparameterfloat = customGetParameterFloatCallback;
        dspdesc.numparameters = 2;

        stringEncoder.Dispose();

        int intPtrSize = Marshal.SizeOf(typeof(IntPtr));
        int size = Marshal.SizeOf(typeof(FMOD.DSP_PARAMETER_DESC));
        IntPtr ptr = Marshal.AllocHGlobal((intPtrSize + size) * dspdesc.numparameters);
        managedData.dspAllocation = ptr;
        dspdesc.paramdesc = ptr;
        IntPtr paramPtr = new IntPtr((long)ptr + (intPtrSize * dspdesc.numparameters));

        IntPtr[] ptrArray = new IntPtr[dspdesc.numparameters];
        for (int i = 0; i < paramdesc.Length; ++i)
        {
            ptrArray[i] = paramPtr;
            Marshal.StructureToPtr(paramdesc[i], paramPtr, false);
            paramPtr = new IntPtr((long)paramPtr + size);
        }
        Marshal.Copy(ptrArray, 0, ptr, dspdesc.numparameters);

        FMOD.System sys;
        CheckResult(system.getCoreSystem(out sys));
        FMOD.DSP custom;
        CheckResult(sys.createDSP(ref dspdesc, out custom));

        FMOD.ChannelGroup channelGroup;
        CheckResult(sys.getMasterChannelGroup(out channelGroup));

        CheckResult(channelGroup.addDSP(0, custom));
        CheckResult(custom.setParameterFloat(1, 1.0f));

        CheckResult(channelGroup.removeDSP(custom));
        CheckResult(custom.release());
        managedData.Dispose();
    }