正在收集回调委托?

发布于 2024-12-03 04:41:03 字数 1621 浏览 0 评论 0原文

一直在用 FMOD 进行 C# 游戏开发,但我很早就遇到了一个似乎无法解决的障碍。我想做一些分支音频的东西,并将一些游戏动作同步到节拍等,所以我尝试将同步点添加到我的音乐轨道中。代码如下:

public class Music
{
    private Sound music;
    private Channel channel;
    private IntPtr syncPtr;

    public string File { get; private set; }  

    public Music(string file)
    {
        File = file;
    }

    public void Load()
    {
        music = new Sound();
        Audio.System.createSound(File, MODE.HARDWARE, ref music);
    }

    public void Unload()
    {
        music.release();
    }

    public virtual void Play()
    {
        Audio.System.playSound(channel == null ? CHANNELINDEX.FREE : CHANNELINDEX.REUSE, music, false, ref channel);
        music.addSyncPoint(500, TIMEUNIT.MS, "wooo", ref syncPtr);
        channel.setCallback(channelCallback);
    }

    private RESULT channelCallback(IntPtr channelraw, CHANNEL_CALLBACKTYPE type, IntPtr commanddata1, IntPtr commanddata2)
    {
        if (type == CHANNEL_CALLBACKTYPE.SYNCPOINT)
            Console.WriteLine("sync!");

        return RESULT.OK;
    }
}

然后...

m = new Music(MUS_TUTORIAL);  //m is static
m.Load();
m.Play();

歌曲加载并正常播放...直到达到我添加的 500 毫秒同步点。此时,VC# 从 FMOD.EventSystem.update() 中输出以下错误:

对类型为垃圾收集的委托进行了回调 '游戏!FMOD.CHANNEL_CALLBACK::调用'。这可能会导致应用程序崩溃、损坏和 数据丢失。将委托传递给非托管代码时,它们必须由 托管应用程序,直到保证它们永远不会被调用。

所以不知何故,FMOD 失去了我通过它的代表的踪迹。保存委托的 Music 实例尚未被垃圾收集 - 我现在将其存储在静态变量中 - 但我也尝试过使用静态方法,但无济于事。如果我禁用 CallbackOnCollectedDelegate MDA,错误将变为空引用异常,因此 MDA 不会出错。我想我一定是没有完全理解 FMOD 在这里做什么。

有 C# + FMOD 专家能够看到我的错误吗?

Been messing around with FMOD for C# game development and I've hit a snag early on that I can't seem to get around. I want to do some branching audio stuff and sync some gameplay action to beats and such, so I've tried adding syncpoints to my music tracks. Here's code:

public class Music
{
    private Sound music;
    private Channel channel;
    private IntPtr syncPtr;

    public string File { get; private set; }  

    public Music(string file)
    {
        File = file;
    }

    public void Load()
    {
        music = new Sound();
        Audio.System.createSound(File, MODE.HARDWARE, ref music);
    }

    public void Unload()
    {
        music.release();
    }

    public virtual void Play()
    {
        Audio.System.playSound(channel == null ? CHANNELINDEX.FREE : CHANNELINDEX.REUSE, music, false, ref channel);
        music.addSyncPoint(500, TIMEUNIT.MS, "wooo", ref syncPtr);
        channel.setCallback(channelCallback);
    }

    private RESULT channelCallback(IntPtr channelraw, CHANNEL_CALLBACKTYPE type, IntPtr commanddata1, IntPtr commanddata2)
    {
        if (type == CHANNEL_CALLBACKTYPE.SYNCPOINT)
            Console.WriteLine("sync!");

        return RESULT.OK;
    }
}

And then...

m = new Music(MUS_TUTORIAL);  //m is static
m.Load();
m.Play();

The song loads and plays fine... until it hits that 500ms syncpoint I added. At this point, VC# spits out the following error from within FMOD.EventSystem.update():

A callback was made on a garbage collected delegate of type
'Game!FMOD.CHANNEL_CALLBACK::Invoke'. This may cause application crashes, corruption and
data loss. When passing delegates to unmanaged code, they must be kept alive by the
managed application until it is guaranteed that they will never be called.

So somehow FMOD is losing track of the delegate I passed it. The Music instance that holds the delegate has not been garbage collected - I'm storing it in a static variable for now - but I've tried with a static method too to no avail. If I disable the CallbackOnCollectedDelegate MDA the error becomes a null reference exception, so the MDA isn't mistaken. I assume I must just not fully understand what FMOD is doing here.

Are any C# + FMOD gurus able to see my mistake?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

心在旅行 2024-12-10 04:41:03
    channel.setCallback(channelCallback);

这就是问题陈述。 FMod 是非托管代码。您将在此处创建一个委托对象并将其传递给非托管代码。问题是,垃圾收集器无法跟踪本机代码持有的引用。下一次垃圾收集将发现没有对该对象的引用并收集它。当本机代码进行回调时,Kaboom。

您需要自己保留一个引用,这样就不会发生这种情况:

public class Music
{
    private SomeDelegateType callback
    //...
    public Music(string file)
    {
        File = file;
        callback = new SomeDelegateType(channelCallback);
    }

    public virtual void Play()
    {
        Audio.System.playSound(channel == null ? CHANNELINDEX.FREE : CHANNELINDEX.REUSE, music, false, ref channel);
        music.addSyncPoint(500, TIMEUNIT.MS, "wooo", ref syncPtr);
        channel.setCallback(callback);
    }

您需要从 FMod 包装器代码中找到实际的委托类型,我只是猜测“SomeDelegateType”。

    channel.setCallback(channelCallback);

That's the problem statement. FMod is unmanaged code. You are creating a delegate object here and passing it to the unmanaged code. Problem is, the garbage collector cannot track references held by native code. The next garbage collection will find no references to the object and collect it. Kaboom when the native code makes the callback.

You need to keep a reference yourself so this won't happen:

public class Music
{
    private SomeDelegateType callback
    //...
    public Music(string file)
    {
        File = file;
        callback = new SomeDelegateType(channelCallback);
    }

    public virtual void Play()
    {
        Audio.System.playSound(channel == null ? CHANNELINDEX.FREE : CHANNELINDEX.REUSE, music, false, ref channel);
        music.addSyncPoint(500, TIMEUNIT.MS, "wooo", ref syncPtr);
        channel.setCallback(callback);
    }

You need to find the actual delegate type from the FMod wrapper code, I merely guessed at "SomeDelegateType".

吐个泡泡 2024-12-10 04:41:03

我在 VB.NET 和自定义 C++ DLL 之间遇到了类似的问题。感谢@Hans 修复。我多次感谢这个网站帮助我解决了所有问题。添加我的问题+解决方案希望能帮助其他人在不同的上下文中看到相同的解决方案。

声明这一点(在模块中)

Public Delegate Sub CB_FUNC(ByVal x As Integer, ByVal y As Integer)
Public Declare Sub vidProc_cb_MouseClick Lib "C:\Users\.....\vidProc\product\vidProc.dll" (ByVal addr_update As CB_FUNC)

最初:

在button_click子中进行了一个简单的调用:

vidProc_cb_MouseClick(AddressOf updateXY)

我会得到“CallbackOnCollectedDelegate”错误。不是立即,而是在与表单上的其他对象交互之后,然后尝试调用回调(在我的例子中是在 OpenCV 窗口中单击鼠标)。

修复方法:

1)声明(在表单类,声明中)

Private addr_update As CB_FUNC

2)在表单加载上定义addr_update

addr_update = New CB_FUNC(AddressOf updateXY)

3)使用新指针调用我的“设置回调”函数(在button_click子中)

vidProc_cb_MouseClick(addr_update)

我想我理解@Hans并正确实现了它(我无法重现该错误)。希望这对某人有帮助。

I had a similar problem between VB.NET and a custom C++ DLL. Fixed thanks to @Hans. I owe this site many times over for all the problems it's got me through. Adding my problem+solution in hopes it helps others seeing the same solution in a different context.

Declared this (in a module)

Public Delegate Sub CB_FUNC(ByVal x As Integer, ByVal y As Integer)
Public Declare Sub vidProc_cb_MouseClick Lib "C:\Users\.....\vidProc\product\vidProc.dll" (ByVal addr_update As CB_FUNC)

Originally:

Had a simple call in a button_click sub:

vidProc_cb_MouseClick(AddressOf updateXY)

I would get the 'CallbackOnCollectedDelegate' error. Not immediately, but after interacting with other objects on the form, then trying to invoke the callback (which in my case was a mouse click in an OpenCV window).

Fixed by:

1) Declaring (in the Form Class, Declarations)

Private addr_update As CB_FUNC

2) Defining addr_update on Form Load

addr_update = New CB_FUNC(AddressOf updateXY)

3) Calling my 'set callback' function with the new pointer (in the button_click sub)

vidProc_cb_MouseClick(addr_update)

I think I understood @Hans and implemented it correctly (I can't reproduce the error). Hope this helps someone.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文