# Supersonic C# Wrapper (DSP callback problem)

**URL:** <https://qa.fmod.com/t/supersonic-c-wrapper-dsp-callback-problem/12497>\
**Category:** FMOD Engine\
**Created:** [May 16, 2016, 2:55pm UTC](https://qa.fmod.com/t/supersonic-c-wrapper-dsp-callback-problem/12497 "2016-05-16T14:55:39Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![Pizza-Kun](https://avatars.discourse-cdn.com/v4/letter/p/3d9bf3/32.png) [@Pizza-Kun](https://qa.fmod.com/u/Pizza-Kun)\
**Post date:** [May 16, 2016, 2:55pm UTC](https://qa.fmod.com/t/supersonic-c-wrapper-dsp-callback-problem/12497/1 "2016-05-16T14:55:39Z")

</div>

Hi, i’m using an FMOD C# wrapper that you can found here [https://github.com/PizzaKun/SupersonicSound](https://github.com/PizzaKun/SupersonicSound)

All works correctly except the DSP callback on OSX using FMOD 1.07.0, the problem is that when i call DSP.getUserData(ref IntPtr Data) mono throw a StackOverflowException; the same code works perfectly on windows, the library version is the same.

this is an example of my code:

Initialize DSP:

```
FMOD.DSP_READCALLBACK Callback = new FMOD.DSP_READCALLBACK (CallbackGlitcher);

char [] Name = new char[32];
"Test".ToCharArray().CopyTo(Name, 0);
Desc.name = Name;
Desc.version = 0x00010000;
Desc.numinputbuffers = 1;
Desc.numoutputbuffers = 1;
Desc.read = Callback;
Desc.userdata = (IntPtr)GCHandle.Alloc (this);

Res = system._system.createDSP(ref Desc, out handler);

```

DSP callback:

```
static FMOD.RESULT CallbackGlitcher(ref FMOD.DSP_STATE S, IntPtr In, IntPtr Out, uint Len, int ChInCount, ref int ChOutCount)
{
	// Get current class pointer
	FMOD.DSP Handler;
	Handle = new FMOD.DSP(S.instance);

	IntPtr Data;
	Handler.getUserData (out Data); // <- Stack overflow

	return FMOD.RESULT.OK;
}

```

If I call getUserData out of the callback it works.

Thanks.

(ps. The exception is thrown by FMOD\_DSP\_GetUserData on fmod.cs)

---

<div class="post-metadata">

**Author:** ![nick1](https://avatars.discourse-cdn.com/v4/letter/n/47e85d/32.png) [@nick1](https://qa.fmod.com/u/nick1)\
**Post date:** [May 18, 2016, 3:43am UTC](https://qa.fmod.com/t/supersonic-c-wrapper-dsp-callback-problem/12497/2 "2016-05-18T03:43:24Z")

</div>

I would check all the usual issues with managed code. Make sure **all** the data being passed to native code is being preserved (i.e. not being garbage collected). This includes your delegate objects.

As you can imagine getUserData is a simple function. It’s unlikely to be overflowing the stack. It’s more likely a corruption of stack variables because of some other reason.

---

<div class="post-metadata">

**Author:** ![Pizza-Kun](https://avatars.discourse-cdn.com/v4/letter/p/3d9bf3/32.png) [@Pizza-Kun](https://qa.fmod.com/u/Pizza-Kun)\
**Post date:** [May 20, 2016, 5:11pm UTC](https://qa.fmod.com/t/supersonic-c-wrapper-dsp-callback-problem/12497/3 "2016-05-20T17:11:54Z")

</div>

Hi Nicholas Wilcox, sorry for the late reply and thank you for answering; as you requested I’ll give you ll the code related to the DSPs and will link the repository where you can find the classes and the audo system I’m implementing; you’ll find a C# solution in the repository which contains 5 projects. You can find all the code referring to FMOD in the pEngine/Audio project; the TestShared/TestScene.cs file contains the part I’m using to test the DSPs.

Repository: [https://github.com/PizzaKun/pEngine](https://github.com/PizzaKun/pEngine)  
Audio folder: [https://github.com/PizzaKun/pEngine/tree/master/pEngine/Audio](https://github.com/PizzaKun/pEngine/tree/master/pEngine/Audio)

This is the (generic) DSP class

```
using System;
using System.Collections.Generic;

using System.Runtime.InteropServices;

using SupersonicSound.LowLevel;

namespace pEngine.Audio
{
	public class DSP : IAudioDSP, IDisposable
    {
        public interface ISharedData
        {
			
        }

        public DSP(AudioManager System, string Name)
        {
            system = System;
            name = Name;
        }

        protected void Initialize(FMOD.DSP_READCALLBACK Callback, object Instance)
        {
            FMOD.RESULT Res;

			///////////////////////////////////////
			/// Make sure that is all allocated ///
			///////////////////////////////////////

			descriptor = new FMOD.DSP_DESCRIPTION();

			objectReference = (IntPtr)GCHandle.Alloc (Instance);
			callbackReference = Callback;

            char [] N = new char[32];
            Name.ToCharArray().CopyTo(N, 0);

			descriptor.name = N;
			descriptor.version = 0x00010000;
			descriptor.numinputbuffers = 1;
			descriptor.numoutputbuffers = 1;
			descriptor.read = callbackReference;
			descriptor.userdata = objectReference;

            Res = system.Handle.createDSP(ref descriptor, out handler);
            if (Res != FMOD.RESULT.OK)
                throw new InvalidDSPException();

			// Prevent DSP handler be collected
			DSPHandle = (IntPtr)GCHandle.Alloc(handler);
        }

		public void Dispose()
		{
			handler.release ();
		}

        /// <summary>
        /// Bypass this DSP.
        /// </summary>
        public bool Bypass
        {
            get
            {
                bool bypass;
                handler.getBypass(out bypass);
                return bypass;
            }
            set
            {
                handler.setBypass(value);
            }
        }

        #region Meta

        string name;

        /// <summary>
        /// Name of the current DSP
        /// </summary>
        public string Name { get { return name; } }

        #endregion

        #region Handlers

		FMOD.DSP_READCALLBACK callbackReference;
		FMOD.DSP_DESCRIPTION descriptor;
		FMOD.DSP handler;

		IntPtr objectReference;
		IntPtr DSPHandle;

        protected AudioManager system;

        /// <summary>
        /// Hanler to this DSP.
        /// </summary>
        public FMOD.DSP Handle { get { return handler; } }

        #endregion
    }

    #region Exceptions

    public class InvalidDSPException : Exception
    {

    }

    #endregion
}

```

This is a specific DSP

```
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

using SupersonicSound.LowLevel;

namespace pEngine.Audio
{
	public class BarGateDSP : DSP
    {
        static FMOD.RESULT CallbackBarGate(ref FMOD.DSP_STATE S, IntPtr In, IntPtr Out, uint Len, int ChInCount, ref int ChOutCount)
        {
            float Volume = 0;

            // Get current class pointer
			FMOD.DSP FmodDSP = new FMOD.DSP(S.instance);

			IntPtr EngineDSP;

			//TODO: on OSX stack overflow.
			FmodDSP.getUserData (out EngineDSP);

			BarGateDSP H = (((GCHandle)EngineDSP).Target) as BarGateDSP;

            for (uint Sample = 0; Sample < Len; Sample++)
            {

                // Current time count
                H.Time += (float)(1.0 / (H.Frequency / 1000.0));

                // Bar calculation
                if (((int)(((H.Time / 1000.0) / 60.0) * H.BPM * H.Divisor) % H.Bars) == 0 && H.Low)
                {
                    H.Low = false;
                    H.StartEasing = H.Time;
                }
                if (((int)(((H.Time / 1000.0) / 60.0) * H.BPM * H.Divisor) % H.Bars) != 0 && !H.Low)
                {
                    H.Low = true;
                    H.StartEasing = H.Time;
                }

                // Easing
                if (!H.Low)
                    Volume = (H.MaxVol / 100) - (H.EasingTime == 0 ? 0 : ((H.Time - H.StartEasing) / (float)H.EasingTime));
                else if (H.Low)
                    Volume = (H.EasingTime == 0 ? 0 : ((H.Time - H.StartEasing) / (float)H.EasingTime));

                Volume = Math.Min(Volume, 1.0F);
                Volume = Math.Max(Volume, 0.0F);

                // Output
                for (int CH = 0; CH < ChOutCount; CH++)
                {
                    unsafe
                    {
                        float* BOut = (float*)Out;
                        float* BIn = (float*)In;
                        BOut[(Sample * ChOutCount) + CH] = BIn[(Sample * ChInCount) + CH] * Volume;
                    }
                }
            }

			// Main DSP code...
			return FMOD.RESULT.OK;
        }

        /// <summary>
        /// Make a new instance of <see cref="BarGateDSP"/>.
        /// </summary>
        /// <param name="System">Audio system reference.</param>
        public BarGateDSP(AudioManager System) : base(System, "BarGate")
        {

        }

        /// <summary>
        /// Initialize this DSP.
        /// </summary>
        public void Initialize()
        {
            Callback = new FMOD.DSP_READCALLBACK(CallbackBarGate);
            base.Initialize(Callback, this);

			Bypass = true;

			IntPtr Test;

			// If i call getUserData at this point, stack overflow exception will
			// not triggered, but an FMOD thread will not close when i try to close the
			// application
			// Handle.getUserData(out Test);
			// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        }

        /// <summary>
        /// Reset the DSP internal state.
        /// </summary>
        public void Reset()
        {
            Time = 0;
            StartEasing = 0;
            Low = false;
        }

        /// <summary>
        /// Enable this DSP.
        /// </summary>
        public void Enable()
        {
            Bypass = false;
            Reset();
        }

        /// <summary>
        /// Disable this DSP.
        /// </summary>
        public void Disable()
        {
            Bypass = true;
            Reset();
        }

        #region Properties

        uint freq = 0;

        /// <summary>
        /// Beat divisor.
        /// </summary>
        public uint Divisor { get; set; } = 4;

        /// <summary>
        /// Number of bars between each state change.
        /// </summary>
        public uint Bars { get; set; } = 2;

        /// <summary>
        /// Song beats per minute.
        /// </summary>
        public uint BPM { get; set; } = 120;

        /// <summary>
        /// Song reproduction frequency;
        /// Default value: 0
        /// 0 means the DSP autodetect the frequency.
        /// </summary>
        public uint Frequency
        {
            get
            {
                if (freq == 0)
                    return (uint)system.Frequency;
                else return freq;
            }
            set
            {
                freq = value;
            }
        }

        /// <summary>
        /// Volume on gate open.
        /// </summary>
        public uint MaxVol { get; set; } = 100;

        /// <summary>
        /// Volume on gate close.
        /// </summary>
        public uint MinVol { get; set; } = 0;

        /// <summary>
        /// Milliseconds of the high - low voume transition
        /// (Default 2 for smooth volume change)
        /// </summary>
        public double EasingTime { get; set; } = 2;

        #endregion

        #region State

        static FMOD.DSP_READCALLBACK Callback;

        float Time = 0;

        float StartEasing = 0;

        bool Low = false;

        #endregion
    }
}
```

---

<div class="post-metadata">

**Author:** ![nick1](https://avatars.discourse-cdn.com/v4/letter/n/47e85d/32.png) [@nick1](https://qa.fmod.com/u/nick1)\
**Post date:** [May 23, 2016, 2:08am UTC](https://qa.fmod.com/t/supersonic-c-wrapper-dsp-callback-problem/12497/4 "2016-05-23T02:08:23Z")

</div>

> [@](#):
>
> Hi Nicholas Wilcox, sorry for the late reply and thank you for answering; as you requested I’ll give you ll the code related to the DSPs and will link the repository where you can find the classes and the audo system I’m implementing; you’ll find a C# solution in the repository which contains 5 projects. You can find all the code referring to FMOD in the pEngine/Audio project; the TestShared/TestScene.cs file contains the part I’m using to test the DSPs.
> 
> Repository: [GitHub - piRepos/pEngine-del: 2D game engine](https://github.com/PizzaKun/pEngine)  
> Audio folder: [pEngine-del/pEngine/Audio at master · piRepos/pEngine-del · GitHub](https://github.com/PizzaKun/pEngine/tree/master/pEngine/Audio)
> 
> This is the (generic) DSP class
> 
> ```
> using System;
> using System.Collections.Generic;
> 
> using System.Runtime.InteropServices;
> 
> using SupersonicSound.LowLevel;
> 
> namespace pEngine.Audio
> {
> public class DSP : IAudioDSP, IDisposable
> {
> public interface ISharedData
> {
> 			
> }
> 
> public DSP(AudioManager System, string Name)
> {
> system = System;
> name = Name;
> }
> 
> protected void Initialize(FMOD.DSP_READCALLBACK Callback, object Instance)
> {
> FMOD.RESULT Res;
> 
> ///////////////////////////////////////
> /// Make sure that is all allocated ///
> ///////////////////////////////////////
> 
> descriptor = new FMOD.DSP_DESCRIPTION();
> 
> objectReference = (IntPtr)GCHandle.Alloc (Instance);
> callbackReference = Callback;
> 
> char [] N = new char[32];
> Name.ToCharArray().CopyTo(N, 0);
> 
> descriptor.name = N;
> descriptor.version = 0x00010000;
> descriptor.numinputbuffers = 1;
> descriptor.numoutputbuffers = 1;
> descriptor.read = callbackReference;
> descriptor.userdata = objectReference;
> 
> Res = system.Handle.createDSP(ref descriptor, out handler);
> if (Res != FMOD.RESULT.OK)
> throw new InvalidDSPException();
> 
> // Prevent DSP handler be collected
> DSPHandle = (IntPtr)GCHandle.Alloc(handler);
> }
> 
> public void Dispose()
> {
> handler.release ();
> }
> 
> /// <summary>
> /// Bypass this DSP.
> /// </summary>
> public bool Bypass
> {
> get
> {
> bool bypass;
> handler.getBypass(out bypass);
> return bypass;
> }
> set
> {
> handler.setBypass(value);
> }
> }
> 
> #region Meta
> 
> string name;
> 
> /// <summary>
> /// Name of the current DSP
> /// </summary>
> public string Name { get { return name; } }
> 
> #endregion
> 
> #region Handlers
> 
> FMOD.DSP_READCALLBACK callbackReference;
> FMOD.DSP_DESCRIPTION descriptor;
> FMOD.DSP handler;
> 
> IntPtr objectReference;
> IntPtr DSPHandle;
> 
> protected AudioManager system;
> 
> /// <summary>
> /// Hanler to this DSP.
> /// </summary>
> public FMOD.DSP Handle { get { return handler; } }
> 
> #endregion
> }
> 
> #region Exceptions
> 
> public class InvalidDSPException : Exception
> {
> 
> }
> 
> #endregion
> }
> 
> ```
> 
> This is a specific DSP
> 
> ```
> using System;
> using System.Collections.Generic;
> using System.Runtime.InteropServices;
> 
> using SupersonicSound.LowLevel;
> 
> namespace pEngine.Audio
> {
> public class BarGateDSP : DSP
> {
> static FMOD.RESULT CallbackBarGate(ref FMOD.DSP_STATE S, IntPtr In, IntPtr Out, uint Len, int ChInCount, ref int ChOutCount)
> {
> float Volume = 0;
> 
> // Get current class pointer
> FMOD.DSP FmodDSP = new FMOD.DSP(S.instance);
> 
> IntPtr EngineDSP;
> 
> //TODO: on OSX stack overflow.
> FmodDSP.getUserData (out EngineDSP);
> 
> BarGateDSP H = (((GCHandle)EngineDSP).Target) as BarGateDSP;
> 
> for (uint Sample = 0; Sample < Len; Sample++)
> {
> 
> // Current time count
> H.Time += (float)(1.0 / (H.Frequency / 1000.0));
> 
> // Bar calculation
> if (((int)(((H.Time / 1000.0) / 60.0) * H.BPM * H.Divisor) % H.Bars) == 0 && H.Low)
> {
> H.Low = false;
> H.StartEasing = H.Time;
> }
> if (((int)(((H.Time / 1000.0) / 60.0) * H.BPM * H.Divisor) % H.Bars) != 0 && !H.Low)
> {
> H.Low = true;
> H.StartEasing = H.Time;
> }
> 
> // Easing
> if (!H.Low)
> Volume = (H.MaxVol / 100) - (H.EasingTime == 0 ? 0 : ((H.Time - H.StartEasing) / (float)H.EasingTime));
> else if (H.Low)
> Volume = (H.EasingTime == 0 ? 0 : ((H.Time - H.StartEasing) / (float)H.EasingTime));
> 
> Volume = Math.Min(Volume, 1.0F);
> Volume = Math.Max(Volume, 0.0F);
> 
> // Output
> for (int CH = 0; CH < ChOutCount; CH++)
> {
> unsafe
> {
> float* BOut = (float*)Out;
> float* BIn = (float*)In;
> BOut[(Sample * ChOutCount) + CH] = BIn[(Sample * ChInCount) + CH] * Volume;
> }
> }
> }
> 
> // Main DSP code...
> return FMOD.RESULT.OK;
> }
> 
> /// <summary>
> /// Make a new instance of <see cref="BarGateDSP"/>.
> /// </summary>
> /// <param name="System">Audio system reference.</param>
> public BarGateDSP(AudioManager System) : base(System, "BarGate")
> {
> 
> }
> 
> /// <summary>
> /// Initialize this DSP.
> /// </summary>
> public void Initialize()
> {
> Callback = new FMOD.DSP_READCALLBACK(CallbackBarGate);
> base.Initialize(Callback, this);
> 
> Bypass = true;
> 
> IntPtr Test;
> 
> // If i call getUserData at this point, stack overflow exception will
> // not triggered, but an FMOD thread will not close when i try to close the
> // application
> // Handle.getUserData(out Test);
> // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> }
> 
> /// <summary>
> /// Reset the DSP internal state.
> /// </summary>
> public void Reset()
> {
> Time = 0;
> StartEasing = 0;
> Low = false;
> }
> 
> /// <summary>
> /// Enable this DSP.
> /// </summary>
> public void Enable()
> {
> Bypass = false;
> Reset();
> }
> 
> /// <summary>
> /// Disable this DSP.
> /// </summary>
> public void Disable()
> {
> Bypass = true;
> Reset();
> }
> 
> #region Properties
> 
> uint freq = 0;
> 
> /// <summary>
> /// Beat divisor.
> /// </summary>
> public uint Divisor { get; set; } = 4;
> 
> /// <summary>
> /// Number of bars between each state change.
> /// </summary>
> public uint Bars { get; set; } = 2;
> 
> /// <summary>
> /// Song beats per minute.
> /// </summary>
> public uint BPM { get; set; } = 120;
> 
> /// <summary>
> /// Song reproduction frequency;
> /// Default value: 0
> /// 0 means the DSP autodetect the frequency.
> /// </summary>
> public uint Frequency
> {
> get
> {
> if (freq == 0)
> return (uint)system.Frequency;
> else return freq;
> }
> set
> {
> freq = value;
> }
> }
> 
> /// <summary>
> /// Volume on gate open.
> /// </summary>
> public uint MaxVol { get; set; } = 100;
> 
> /// <summary>
> /// Volume on gate close.
> /// </summary>
> public uint MinVol { get; set; } = 0;
> 
> /// <summary>
> /// Milliseconds of the high - low voume transition
> /// (Default 2 for smooth volume change)
> /// </summary>
> public double EasingTime { get; set; } = 2;
> 
> #endregion
> 
> #region State
> 
> static FMOD.DSP_READCALLBACK Callback;
> 
> float Time = 0;
> 
> float StartEasing = 0;
> 
> bool Low = false;
> 
> #endregion
> }
> }
> 
> ```

The code looks OK as first pass. Things you can try: create all your GCHandles with the pinned type, skip creating a FMOD.DSP object in your callback and directly call DSP.FMOD\_DSP\_GetUserData with S.instance (you’ll have to hack the source to make it public), wrap the body of all callbacks with try/catch to make sure no exceptions are propagated back to native code which cannot handle them properly.
