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:
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:
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...