F#中Csound API含char数组字段的结构体数组封送问题
Csound API 封送问题:正确实现CS_AUDIODEVICE与csoundGetAudioDevList绑定
Csound API定义了CS_AUDIODEVICE结构体:
typedef struct { char device_name[64]; char device_id[64]; char rt_module[64]; int max_nchnls; int isOutput; } CS_AUDIODEVICE;
还有一个csoundGetAudioDevList API函数:
PUBLIC int csoundGetAudioDevList( CSOUND* csound, CS_AUDIODEVICE* list, int isOutput )
该函数通常调用两次:第一次传入空指针获取设备数量,第二次传入大小为「设备数 × CS_AUDIODEVICE大小」的指针,以获取实际的设备数组。
我在定义DLLImport声明并正确封送数据时遇到了极大困难。在F#中,不透明结构体CSOUND定义如下,且可正常工作:
[<Struct>] type CSOUND = struct end
尝试1
[<Struct; StructLayout(LayoutKind.Sequential)>] type CS_AUDIODEVICE = [<MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)>] val device_name: string // char[64] [<MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)>] val device_id: string // char[64] [<MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)>] val rt_module: string // char[64] val max_nchnls: int val isOutput: int [<DllImport(csoundDLLPath, CallingConvention = CallingConvention.Cdecl)>] extern int csoundGetAudioDevList(CSOUND* csound, CS_AUDIODEVICE[] devices, int isOutput) let numberOfDevices = csoundGetAudioDevList(csound, [||], 1) let mutable devices = [| for _ in 1..numberOfDevices -> CS_AUDIODEVICE() |] csoundGetAudioDevList(csound, devices, 1) |> ignore devices
按照官方文档和社区解答的说法,这个实现应该能正常运行,但实际直接崩溃,在F#交互环境(FSI)中仅显示Session termination detected。
尝试2
我尝试重新定义结构体:
[<Struct; StructLayout(LayoutKind.Sequential)>] type CS_AUDIODEVICE = [<MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)>] val device_name: char[] // char[64] [<MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)>] val device_id: char[] // char[64] [<MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)>] val rt_module: char[] // char[64] val max_nchnls: int val isOutput: int
其余代码保持不变,但结果依然崩溃。
尝试3
之前遇到复杂封送问题时,我常用IntPtr处理,于是尝试了这个变体:
[<DllImport(csoundDLLPath, CallingConvention = CallingConvention.Cdecl)>] extern int csoundGetAudioDevList(CSOUND* csound, IntPtr devices, int isOutput) let numberOfDevices = csoundGetAudioDevList(csound, IntPtr.Zero, 1) let mutable pointer = IntPtr(numberOfDevices * Marshal.SizeOf(CS_AUDIODEVICE)) let mutable devices = [| for _ in 1..numberOfDevices -> CS_AUDIODEVICE() |] Marshal.StructureToPtr(devices, pointer, true) csoundGetAudioDevList(csound, pointer, 1) |> ignore //for i in 1..numberOfDevices do // devices[i-1] <- Marshal.PtrToStructure<CS_AUDIODEVICE>(pointer) // pointer <- IntPtr.Add(pointer, Marshal.SizeOf<CS_AUDIODEVICE>()) devices <- Marshal.PtrToStructure<CS_AUDIODEVICE[]>(pointer) devices
我参考了社区解答和过往经验,但问题仍未解决。
我完全无法解决该问题:多数操作会静默崩溃,部分返回难以理解的堆栈跟踪,甚至有操作直接导致CLR崩溃。我无法修改目标API,只能通过官方文档调试,这让我十分困扰。
基于过往DLL绑定经验,有些理论可行的方案实际无法运行,因此恳请提供可行实现方案并解释原理。我的核心问题如下:
CS_AUDIODEVICE结构体应如何正确定义?csoundGetAudioDevList函数的正确DLLImport声明是什么?- 如何从DLL中封送音频设备列表?
- 该实现的原理是什么?
感谢您的帮助!
完整可复现代码
(需Csound API,Windows平台可从二进制包中获取Csound-6.18.1-windows-x64-binaries\build\Release\csound64.dll)
open System.Runtime.InteropServices [<Literal>] let csoundDLLPath = @"<path to Csound DLL>" [<Struct>] type CSOUND = struct end [<Struct; StructLayout(LayoutKind.Sequential)>] type CS_AUDIODEVICE = ??? [<DllImport(csoundDLLPath, CallingConvention = CallingConvention.Cdecl)>] extern CSOUND* csoundCreate() [<DllImport(csoundDLLPath, CallingConvention = CallingConvention.Cdecl)>] extern int csoundStart(CSOUND* csound) [<DllImport(csoundDLLPath, CallingConvention = CallingConvention.Cdecl)>] extern int csoundSetOption(CSOUND* csound, string option) [<DllImport(csoundDLLPath, CallingConvention = CallingConvention.Cdecl)>] extern int csoundGetAudioDevList(CSOUND* csound, ???, int isOutput) let c = csoundCreate() csoundSetOption(c, "-odac") // 启用系统音频设备作为Csound输出设备 csoundStart c // 此处调用csoundGetAudioDevList
内容的提问来源于stack exchange,提问作者bmitc
相关产品推荐
相关产品推荐

