18

一直在使用 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() 中吐出以下错误:

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

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

是否有任何 C# + FMOD 大师能够看到我的错误?

4

2 回答 2

32
    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”。

于 2011-09-04T21:33:15.537 回答
0

我在 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 并正确实现了它(我无法重现错误)。希望这可以帮助某人。

于 2015-02-19T02:55:58.720 回答