Auteur Sujet: C# et marshalling  (Lu 2702 fois)

0 Membres et 1 Invité sur ce sujet

Hey, ça faisait longtemps que je passais plus par ici ! Surtout de façon active !
Mais sans plus tarder, les doigts dans le merdier !

J'essaye de créer les bindings c# manquants du plugin WWise de Unity. En l'occurence, l'API audio input dont j'ai besoin pour faire du text to speech.


// Implements Audio Input API missing from the official C# bindings of AudioKinetic's WWise

using System.Runtime.InteropServices;

public enum AkResult
{
    AK_NotImplemented               = 0,
    AK_Success                      = 1,
    AK_Fail                         = 2,
    AK_PartialSuccess               = 3,
    AK_NotCompatible                = 4,
    AK_AlreadyConnected             = 5,
    AK_NameNotSet                   = 6,
    AK_InvalidFile                  = 7,
    AK_AudioFileHeaderTooLarge      = 8,
    AK_MaxReached                   = 9,
    AK_InputsInUsed                 = 10,
    AK_OutputsInUsed                = 11,
    AK_InvalidName                  = 12,
    AK_NameAlreadyInUse             = 13,
    AK_InvalidID                    = 14,
    AK_IDNotFound                   = 15,
    AK_InvalidInstanceID            = 16,
    AK_NoMoreData                   = 17,
    AK_NoSourceAvailable            = 18,
    AK_StateGroupAlreadyExists      = 19,
    AK_InvalidStateGroup            = 20,
    AK_ChildAlreadyHasAParent       = 21,
    AK_InvalidLanguage              = 22,
    AK_CannotAddItseflAsAChild      = 23,
    AK_TransitionNotFound           = 24,
    AK_TransitionNotStartable       = 25,
    AK_TransitionNotRemovable       = 26,
    AK_UsersListFull                = 27,
    AK_UserAlreadyInList            = 28,
    AK_UserNotInList                = 29,
    AK_NoTransitionPoint            = 30,
    AK_InvalidParameter             = 31,
    AK_ParameterAdjusted            = 32,
    AK_IsA3DSound                   = 33,
    AK_NotA3DSound                  = 34,
    AK_ElementAlreadyInList         = 35,
    AK_PathNotFound                 = 36,
    AK_PathNoVertices               = 37,
    AK_PathNotRunning               = 38,
    AK_PathNotPaused                = 39,
    AK_PathNodeAlreadyInList        = 40,
    AK_PathNodeNotInList            = 41,
    AK_VoiceNotFound                = 42,
    AK_DataNeeded                   = 43,
    AK_NoDataNeeded                 = 44,
    AK_DataReady                    = 45,
    AK_NoDataReady                  = 46,
    AK_NoMoreSlotAvailable          = 47,
    AK_SlotNotFound                 = 48,
    AK_ProcessingOnly               = 49,
    AK_MemoryLeak                   = 50,
    AK_CorruptedBlockList           = 51,
    AK_InsufficientMemory           = 52,
    AK_Cancelled                    = 53,
    AK_UnknownBankID                = 54,
    AK_IsProcessing                 = 55,
    AK_BankReadError                = 56,
    AK_InvalidSwitchType            = 57,
    AK_VoiceDone                    = 58,
    AK_UnknownEnvironment           = 59,
    AK_EnvironmentInUse             = 60,
    AK_UnknownObject                = 61,
    AK_NoConversionNeeded           = 62,
    AK_FormatNotReady               = 63,
    AK_WrongBankVersion             = 64,
    AK_DataReadyNoProcess           = 65,
    AK_FileNotFound                 = 66,
    AK_DeviceNotReady               = 67,
    AK_CouldNotCreateSecBuffer      = 68,
    AK_BankAlreadyLoaded            = 69,
    AK_RenderedFX                   = 71,
    AK_ProcessNeeded                = 72,
    AK_ProcessDone                  = 73,
    AK_MemManagerNotInitialized     = 74,
    AK_StreamMgrNotInitialized      = 75,
    AK_SSEInstructionsNotSupported  = 76,
    AK_Busy                         = 77,
    AK_UnsupportedChannelConfig     = 78,
    AK_PluginMediaNotAvailable      = 79,
    AK_MustBeVirtualized            = 80,
    AK_CommandTooLarge              = 81
};

public enum AkSpeakerConfiguration
{
    FrontLeft = 0x1,
    FrontRight = 0x2,
    FrontCenter = 0x4,
    LowFrequency = 0x8,
    BackLeft = 0x10,
    BackRight = 0x20,
    BackCenter = 0x100,
    SideLeft = 0x200,
    SideRight = 0x400,

    SetupMono = FrontCenter,
    Setup0_1 = LowFrequency,
    Setup1_1 = FrontCenter | LowFrequency,
    SetupStereo = FrontLeft | FrontRight,
    Setup2_1 = SetupStereo | LowFrequency,
    Setup3Stereo = SetupStereo | FrontCenter,
    Setup3_1 = Setup3Stereo | LowFrequency,
    Setup4 = SetupStereo | BackLeft | BackRight,
    Setup4_1 = Setup4 | LowFrequency,
    Setup5 = Setup4 | FrontCenter,
    Setup5_1 = Setup5 | LowFrequency,
    Setup6 = Setup4 | SideLeft | SideRight,
    Setup6_1 = Setup6 | LowFrequency,
    Setup7 = Setup6 | FrontCenter,
    Setup7_1 = Setup7 | LowFrequency,
    SetupSurround = SetupStereo | BackCenter,

    SetupDPL2 = Setup4,

    // And there's even more in AkSpeakerConfig.h...
}

// class AkAudioBuffer
// {
// ...00453 protected:
// #if defined (AK_WII_FAMILY_HW) || defined(AK_3DS)
//     void *          arData[AK_VOICE_MAX_NUM_CHANNELS]; 
// #else
//     void *          pData;             
// #endif
//     AkChannelMask   uChannelMask;       
// public:
//     AKRESULT        eState;             
// protected: 
//     AkUInt16        uMaxFrames;         
//
// public:
//     AkForceInline AkUInt16 MaxFrames() { return uMaxFrames; }
//     
//     AkUInt16        uValidFrames;
// };
[StructLayout(LayoutKind.Sequential)]
public class AkAudioBuffer
{
    public System.IntPtr pData; // data can be interleaved or separated per channel
    protected System.UInt32 ChannelMask;
    public AkResult State;
    protected System.UInt16 _MaxFrames;
    public System.UInt16 ValidFrames;

    public uint MaxFrames { get { return _MaxFrames; } }
}

// struct AkAudioFormat
// {
//     AkUInt32    uSampleRate;       
//
//     AkUInt32    uChannelMask    :18;
//     AkUInt32    uBitsPerSample  :6;
//     AkUInt32    uBlockAlign     :5;
//     AkUInt32    uTypeID         :2;
//     AkUInt32    uInterleaveID   :1;
// ...
// };
[StructLayout(LayoutKind.Sequential)]
public class AkAudioFormat
{
    public System.UInt32 SampleRate;
    private System.UInt32 bitfield;

    private const int ChannelMaskBits = 18;
    private const uint ChannelMaskMask = ((1 << ChannelMaskBits) - 1);
    public AkSpeakerConfiguration ChannelMask
    {
        get { return (AkSpeakerConfiguration)(bitfield & ChannelMaskMask); }
        set { bitfield = (bitfield & ~ChannelMaskMask) | ((uint)value & ChannelMaskMask); }
    }

    private const int BitsPerSampleBits = 6;
    private const int BitsPerSampleOffset = ChannelMaskBits;
    private const uint BitsPerSampleMask = ((1 << BitsPerSampleBits) - 1);
    public uint BitsPerSample
    {
        get { return (bitfield >> BitsPerSampleOffset) & BitsPerSampleMask; }
        set { bitfield = (bitfield & ~(BitsPerSampleMask << BitsPerSampleOffset)) | ((value & BitsPerSampleMask) << BitsPerSampleOffset); }
    }

    private const int BlockAlignBits = 5;
    private const int BlockAlignOffset = BitsPerSampleOffset + BitsPerSampleBits;
    private const uint BlockAlignMask = ((1 << BlockAlignBits) - 1);
    public uint BlockAlign
    {
        get { return (bitfield >> BlockAlignOffset) & BlockAlignMask; }
        set { bitfield = (bitfield & ~(BlockAlignMask << BlockAlignOffset)) | ((value & BlockAlignMask) << BlockAlignOffset); }
    }

    private const int TypeIDBits = 2;
    private const int TypeIDOffset = BlockAlignOffset + BlockAlignBits;
    private const uint TypeIDMask = ((1 << TypeIDBits) - 1);
    public enum DataTypeID { Integer, Float };
    public DataTypeID TypeID
    {
        get { return (DataTypeID)((bitfield >> TypeIDOffset) & TypeIDMask); }
        set { bitfield = (bitfield & ~(TypeIDMask << TypeIDOffset)) | (((uint)value & TypeIDMask) << TypeIDOffset); }
    }

    private const int InterleaveIDBits = 1;
    private const int InterleaveIDOffset = TypeIDBits + TypeIDOffset;
    private const uint InterleaveIDMask = ((1 << InterleaveIDBits) - 1);
    public bool InterleaveID
    {
        get { return ((bitfield >> InterleaveIDOffset) & InterleaveIDMask) == 0; }
        set { bitfield = (bitfield & ~(InterleaveIDMask << InterleaveIDOffset)) | (((value ? 1U : 0U) & InterleaveIDMask) << InterleaveIDOffset); }
    }
}

public class AkSoundEngineExtra
{
    // void (*AkAudioInputPluginExecuteCallbackFunc)(AkPlayingID in_playingID, AkAudioBuffer *io_pBufferOut)
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void AkAudioInputPluginExecuteCallbackFunc(uint playingID, AkAudioBuffer buffer);

    // void (*AkAudioInputPluginGetFormatCallbackFunc)(AkPlayingID in_playingID,AkAudioFormat &io_AudioFormat)
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void AkAudioInputPluginGetFormatCallbackFunc(uint playingID, ref AkAudioFormat format);

    // AkReal32 (*AkAudioInputPluginGetGainCallbackFunc)(AkPlayingID in_playingID)
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate float AkAudioInputPluginGetGainCallbackFunc(uint playingID);

    // void __cdecl SetAudioInputCallbacks(AkAudioInputPluginExecuteCallbackFunc in_pfnExecCallback, 
    //      AkAudioInputPluginGetFormatCallbackFunc in_pfnGetFormatCallback = NULL, 
    //      AkAudioInputPluginGetGainCallbackFunc in_pfnGetGainCallback = NULL)
    [DllImport("AkSoundEngine",
        EntryPoint = "?SetAudioInputCallbacks@@YAXP6AXKPAVAkAudioBuffer@@@ZP6AXKAAUAkAudioFormat@@@ZP6AMK@Z@Z",
        CallingConvention = CallingConvention.Cdecl)]
    public static extern void SetAudioInputCallbacks(
        [MarshalAs(UnmanagedType.FunctionPtr)] AkAudioInputPluginExecuteCallbackFunc execute,
        [MarshalAs(UnmanagedType.FunctionPtr)] AkAudioInputPluginGetFormatCallbackFunc getFormat = null,
        [MarshalAs(UnmanagedType.FunctionPtr)] AkAudioInputPluginGetGainCallbackFunc getGain = null);
}


Et ça... fini par planter. Mais ma callback est jamais appelée. J'ai pas encore bien réussi à comprendre pourquoi, c'est un peu le dawa à débugguer.

Mais du coup, j'ai tenté de faire juste un bout de code tout simple pour vérifier que j'ai bien pigé comment fonctionne le marshalling delegate-pointeur de fonction et rien que dans mon petit truc tout simple j'ai un comportement très bizarre.

test.dll:
Code: c++ [Sélectionner]

extern "C"
{
typedef void(*function)(int);
static function global_ptr;
__declspec(dllexport) void setFunction(function ptr) { global_ptr = ptr; }
__declspec(dllexport) void callFunction() { global_ptr(42); }
void DLLMain(){}
}


Program.cs

    class Program
    {
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void function(int i);

        static void test(int a)
        {
            System.Diagnostics.Debug.Write("plop\n" + a.ToString() + "\n");
        }

        [DllImport("test", CallingConvention = CallingConvention.Cdecl)]
        static extern void setFunction([MarshalAs(UnmanagedType.FunctionPtr)] function f);

        [DllImport("test", CallingConvention = CallingConvention.Cdecl)]
        static extern void callFunction();

        static void Main(string[] args)
        {
            function f = new function(test);
            setFunction(f);
            callFunction();
            System.Diagnostics.Debug.Write("finished\n");
        }
    }


Output:
Code: ascii [Sélectionner]

plop
7924944 < nombre aléatoire, probablement un pointeur...
finished


Une idée de ce que j'ai raté en lisant les diverses doc ?

L'idée étant de tout faire sans utiliser "unsafe" parce que ça devient compliqué avec unity après...

Pffffffff. Et comme d'hab, dès que je pose la question, je trouve mon problème et tout marche. Enfin, en ce qui concerne le morceau simplifié. Donc la mon problème venait pas du code mais probablement du fait que j'avais pas mis la build de test.dll en dépendance de l'appli c# et donc test.dll était pas recompilé depuis hier. Boulet.

Bon, bah je retourne à la partie WWise. Si quelqu'un y trouve une couille, je suis preneur la par contre :)