1

I created a FMOD Plugin on C++ and generated both dynamic library (.dylib) and static library (.a).

I am able to successfully use the dynamic library with FMOD Studio GUI and also I’m able to use it in Unity3d Mac simulator (which picks the dynamic library from Assets/Plugins. But I’m having problems getting things working on the iPhone.

For iOS, I need to use the static library (*.a), which means I have to manually load the plugin in Unity3d (C#), generate the Xcode project and installed it on the iPhone. I tried implementing a basic function in C and load it on C# and it works fine. That means that static library is generated properly and that I can load it successfully on the iPhone and use it. But I can’t get working the FMOD Description Function.

Here is my code, when I run it in the iPhone, i’m getting this error:

“FmodDescPtr is IntPtr.Zero”

Thanks in advanced for your help! I’ve been struggling with this for 5 days and haven’t been able to solve it.

C++ side:

F_DECLSPEC F_DLLEXPORT int F_STDCALL AigooGetDSPDescription2(FMOD_DSP_DESCRIPTION *FmodDesc)
{

// defines the user interface, maximum distance knob
static float distance_mapping_values[] = { 0, 1, 5, 20, 100, 500, 10000 };
static float distance_mapping_scale[] = { 0, 1, 2, 3, 4, 4.5, 5 };

// defines the 3D location attributes
FMOD_DSP_INIT_PARAMDESC_DATA(p_3d_attributes, "3D Attributes", "",  "",  FMOD_DSP_PARAMETER_DATA_TYPE_3DATTRIBUTES);


FMOD_DSP_DESCRIPTION Aigoo_Simple_Plugin_Desc =
{
    FMOD_PLUGIN_SDK_VERSION,
    "AigooSimplePlugin6",    // name
    0x00010000,     // plug-in version
    1,              // number of input buffers to process
    1,              // number of output buffers to process
    Aigoo_Simple_Plugin_dspcreate,
    Aigoo_Simple_Plugin_dsprelease,
    Aigoo_Simple_Plugin_dspreset,
    Aigoo_Simple_Plugin_dspread,
    0,
    0,
    AIGOO_SIMPLE_PLUGIN_NUM_PARAMETERS,
    Aigoo_Simple_Plugin_dspparam,
    Aigoo_Simple_Plugin_dspsetparamfloat,
    0, // Aigoo_Simple_Plugin_dspsetparamint,
    0, // Aigoo_Simple_Plugin_dspsetparambool,
    Aigoo_Simple_Plugin_dspsetparamdata,
    Aigoo_Simple_Plugin_dspgetparamfloat,
    0, // Aigoo_Simple_Plugin_dspgetparamint,
    0, // Aigoo_Simple_Plugin_dspgetparambool,
    Aigoo_Simple_Plugin_dspgetparamdata,
    Aigoo_Simple_Plugin_shouldiprocess,
    0,                                      // userdata
    Aigoo_Simple_Plugin_sys_register,
    Aigoo_Simple_Plugin_sys_deregister,
    Aigoo_Simple_Plugin_sys_mix
};


FmodDesc = &Aigoo_Simple_Plugin_Desc;

return 9291983;  //this is to test that I'm able to get this value on C# and iOS side
}

C# side:

public class AigooPlugInHandler {
    [DllImport ("__Internal")]
    public static extern int AigooGetDSPDescription2(out IntPtr FmodDesc);
}

public class MyAigooClass : MonoBehaviour 
{
    if (Application.platform == RuntimePlatform.IPhonePlayer) {
        IntPtr FmodDescPtr;
        plugin_result = AigooPlugInHandler.AigooGetDSPDescription2(out FmodDescPtr);
        //printing plugin_result returns 9291983 which is the value returned from C 
        if (FmodDescPtr != IntPtr.Zero)
        {
            DSP_DESCRIPTION FmodDesc = (DSP_DESCRIPTION)Marshal.PtrToStructure(FmodDescPtr, typeof(DSP_DESCRIPTION));
            FmodDesc.numinputbuffers = 1;
            ERRCHECK(sys.registerDSP(ref FmodDesc, out mmdsp_handle));
        }
        else
            Console.WriteLine("FmodDescPtr is IntPtr.Zero");    
    }
}

This is the original structure for DSP_DESCRIPTION:

[StructLayout(LayoutKind.Sequential)] //original:
public struct DSP_DESCRIPTION
{
    public uint pluginsdkversion;                          /* [w] The plugin SDK version this plugin is built for.  set to this to FMOD_PLUGIN_SDK_VERSION defined above. */
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
    public char[]                      name;               /* [w] Name of the unit to be displayed in the network. */
    public uint                        version;            /* [w] Plugin writer's version number. */
    public int                         numinputbuffers;    /* [w] Number of input buffers to process.  Use 0 for DSPs that only generate sound and 1 for effects that process incoming sound. */
    public int                         numoutputbuffers;   /* [w] Number of audio output buffers.  Only one output buffer is currently supported. */
    public DSP_CREATECALLBACK          create;             /* [w] Create callback.  This is called when DSP unit is created.  Can be null. */
    public DSP_RELEASECALLBACK         release;            /* [w] Release callback.  This is called just before the unit is freed so the user can do any cleanup needed for the unit.  Can be null. */
    public DSP_RESETCALLBACK           reset;              /* [w] Reset callback.  This is called by the user to reset any history buffers that may need resetting for a filter, when it is to be used or re-used for the first time to its initial clean state.  Use to avoid clicks or artifacts. */
    public DSP_READCALLBACK            read;               /* [w] Read callback.  Processing is done here.  Can be null. */
    public DSP_PROCESS_CALLBACK        process;            /* [w] Process callback.  Can be specified instead of the read callback if any channel format changes occur between input and output.  This also replaces shouldiprocess and should return an error if the effect is to be bypassed.  Can be null. */
    public DSP_SETPOSITIONCALLBACK     setposition;        /* [w] Setposition callback.  This is called if the unit wants to update its position info but not process data.  Can be null. */

    public int                         numparameters;      /* [w] Number of parameters used in this filter.  The user finds this with DSP::getNumParameters */
    public IntPtr                      paramdesc;          /* [w] Variable number of parameter structures. */
    public DSP_SETPARAM_FLOAT_CALLBACK setparameterfloat;  /* [w] This is called when the user calls DSP.setParameterFloat. Can be null. */
    public DSP_SETPARAM_INT_CALLBACK   setparameterint;    /* [w] This is called when the user calls DSP.setParameterInt.   Can be null. */
    public DSP_SETPARAM_BOOL_CALLBACK  setparameterbool;   /* [w] This is called when the user calls DSP.setParameterBool.  Can be null. */
    public DSP_SETPARAM_DATA_CALLBACK  setparameterdata;   /* [w] This is called when the user calls DSP.setParameterData.  Can be null. */
    public DSP_GETPARAM_FLOAT_CALLBACK getparameterfloat;  /* [w] This is called when the user calls DSP.getParameterFloat. Can be null. */
    public DSP_GETPARAM_INT_CALLBACK   getparameterint;    /* [w] This is called when the user calls DSP.getParameterInt.   Can be null. */
    public DSP_GETPARAM_BOOL_CALLBACK  getparameterbool;   /* [w] This is called when the user calls DSP.getParameterBool.  Can be null. */
    public DSP_GETPARAM_DATA_CALLBACK  getparameterdata;   /* [w] This is called when the user calls DSP.getParameterData.  Can be null. */
    public DSP_SHOULDIPROCESS_CALLBACK shouldiprocess;     /* [w] This is called before processing.  You can detect if inputs are idle and return FMOD_OK to process, or any other error code to avoid processing the effect.  Use a count down timer to allow effect tails to process before idling! */
    public IntPtr                      userdata;           /* [w] Optional. Specify 0 to ignore. This is user data to be attached to the DSP unit during creation.  Access via DSP::getUserData. */
}

Thanks, Carlos

Carlos Chacon
  • 241
  • 1
  • 4
  • 17

1 Answers1

0

Looks like Aigoo_Simple_Plugin_Desc is being allocated on the stack rather than the heap.

Instead of:

FMOD_DSP_DESCRIPTION Aigoo_Simple_Plugin_Desc = ...

...try the following. Note how the FMOD_DSP_DESCRIPTION parameter has been changed to a pointer to a pointer:

F_DECLSPEC F_DLLEXPORT int F_STDCALL
AigooGetDSPDescription2(FMOD_DSP_DESCRIPTION **ppFmodDesc)
{
    FMOD_DSP_DESCRIPTION* Aigoo_Simple_Plugin_DescPtr = new FMOD_DSP_DESCRIPTION ();
    // initialise Aigoo_Simple_Plugin_DescPtr 
    .
    .
    .
    *ppFmodDesc = Aigoo_Simple_Plugin_DescPtr;  
    .
    .
    .

The specified structure must be blittable or have layout information

Try adding [StructLayout(LayoutKind.Explicit)] to your struct on c# side.

  • Hi @Roy thanks for your reply. The previous error is fixed but I'm getting a new error: `ArgumentException: The specified structure must be blittable or have layout information` – Carlos Chacon Oct 03 '15 at 16:52
  • after debug narrowed down the error to this call: ` DSP_DESCRIPTION FmodDesc = (DSP_DESCRIPTION)Marshal.PtrToStructure(FmodDescPtr, typeof(DSP_DESCRIPTION)); ` – Carlos Chacon Oct 03 '15 at 16:56
  • @CarlosChacon Great! See edit for a suggestion for your new question –  Oct 04 '15 at 01:40
  • Hi @Roy, thanks for your follow up help on this ! See my edit of the post for the original structure declaration, which is using: LayoutKind.Sequential. This comes from the FMOD SDK, not sure if I change it will break something else. But I still tried changing it to LayoutKind.Explicit, it caused compilation errors for all the variables in the structure. `Error CS0625: ``FMOD.DSP_DESCRIPTION.pluginsdkversion': Instance field types marked with StructLayout(LayoutKind.Explicit) must have a FieldOffset attribute (CS0625) (Assembly-CSharp-firstpass)`. – Carlos Chacon Oct 04 '15 at 04:27
  • I read MSDN documentation on this error and it expect that each variable has a [FieldOffset(0)] defined. – Carlos Chacon Oct 04 '15 at 04:31
  • Hi @CarlosChacon. Sorry not sure what to do here sadly –  Oct 04 '15 at 10:13
  • Hey @Roy thanks for your trying to help, I'll be taking baby steps, with my own strict and sending it from C to C#. Got it working with simple variable types (int) using the pointer fixes that you suggested in your first response. So your help was useful. I'll be posting updates on how this go as I try more things. I really need to get this working on the next couple of days. – Carlos Chacon Oct 04 '15 at 17:42
  • @CarlosChacon No worries. Hope you get it going. If you do solve it, _post it_ as an _answer_ then you can _accept_ it. :) Have a good day –  Oct 05 '15 at 00:10
  • Hi @Roy, I found out the Error `ArgumentException: The specified structure must be blittable or have layout information` is caused by the callback variables in the structure. Any clues on this? – Carlos Chacon Oct 05 '15 at 02:26
  • @CarlosChacon No sorry. Try looking at [Axarydax's answer here](http://stackoverflow.com/questions/25439983/error-the-specified-structure-must-be-blittable-or-have-layout-information). Also [itowlson's answer here](http://stackoverflow.com/questions/2079868/marshal-ptrtostructure-throwing-system-argumentexception-error) also looks promising. –  Oct 05 '15 at 02:37
  • Hi @Roy the problem seems to be with the callback, do you think the changing the structure to layout kind.explicit will help with that? – Carlos Chacon Oct 05 '15 at 05:47
  • Yes. Also use `FieldOffset` to guarantee [Arrays must start on 4-byte boundaries](http://stackoverflow.com/questions/25439983/error-the-specified-structure-must-be-blittable-or-have-layout-information) –  Oct 05 '15 at 07:00