加载延迟超出警告 - Programmer Instrument - Unity

Loading delay exceeded warning - Programmer Instrument - Unity

每当我尝试将 FMOD 中的示例代码用于 Programmer Instrument

时,我都会收到一条警告:“超出加载延迟,声音可能会在不正确的时间播放”

我想也许我需要等待 FMOD.Sound openState == READY,但这似乎没有用。我错过了什么吗?这是我的代码:

    public static void PlayDialogue(string key, Action callback = null)
    {
        // Create Instance
        FMOD.Studio.EventInstance dialogueInstance = FMODUnity.RuntimeManager.CreateInstance(instance.dialogueEvent);
        // Pin the key string in memory and pass a pointer through the user data
        GCHandle stringHandle = GCHandle.Alloc(key, GCHandleType.Pinned);
        dialogueInstance.setUserData(GCHandle.ToIntPtr(stringHandle));
        dialogueInstance.setCallback(instance.dialogueCallback);
    }
    
    [AOT.MonoPInvokeCallback(typeof(FMOD.Studio.EVENT_CALLBACK))]
    static FMOD.RESULT DialogueCallback(FMOD.Studio.EVENT_CALLBACK_TYPE type, IntPtr instancePtr, IntPtr parameterPtr)
    {
        // Get Instance
        FMOD.Studio.EventInstance instance = new FMOD.Studio.EventInstance(instancePtr);

        // Retrieve the user data
        IntPtr stringPtr;
        instance.getUserData(out stringPtr);

        // Get the string object
        GCHandle stringHandle = GCHandle.FromIntPtr(stringPtr);
        String key = stringHandle.Target as String;

        switch (type)
        {
            case FMOD.Studio.EVENT_CALLBACK_TYPE.CREATE_PROGRAMMER_SOUND:
                {
                    FMOD.MODE soundMode = FMOD.MODE.LOOP_NORMAL
                        | FMOD.MODE.CREATECOMPRESSEDSAMPLE
                        | FMOD.MODE.NONBLOCKING;
                    FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES programmerSoundProperties =
                        (FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES)Marshal.PtrToStructure(
                            parameterPtr, typeof(FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES));
                    FMOD.Sound dialogueSound;

                    if (key.Contains("."))
                    {
                        // Load Sound by Given Path
                        FMOD.RESULT soundResult = FMODUnity.RuntimeManager.CoreSystem.createSound(
                            Application.streamingAssetsPath + "/" + key, soundMode, out dialogueSound);
                        if (soundResult == FMOD.RESULT.OK)
                        {
                            programmerSoundProperties.sound = dialogueSound.handle;
                            programmerSoundProperties.subsoundIndex = -1;
                            Marshal.StructureToPtr(programmerSoundProperties, parameterPtr, false);
                            // Wait To Play
                            WaitForLoadThenPlay(instancePtr, parameterPtr);
                        }
                    }
                    else
                    {
                        // Load Sound Path
                        FMOD.Studio.SOUND_INFO dialogueSoundInfo;
                        FMOD.RESULT keyResult = FMODUnity.RuntimeManager.StudioSystem.getSoundInfo(key, out dialogueSoundInfo);
                        if (keyResult != FMOD.RESULT.OK) break;
                        // Load Sound
                        FMOD.RESULT soundResult = FMODUnity.RuntimeManager.CoreSystem.createSound(
                            dialogueSoundInfo.name_or_data, soundMode | dialogueSoundInfo.mode, ref dialogueSoundInfo.exinfo, out dialogueSound);
                        if (soundResult == FMOD.RESULT.OK)
                        {
                            programmerSoundProperties.sound = dialogueSound.handle;
                            programmerSoundProperties.subsoundIndex = dialogueSoundInfo.subsoundindex;
                            Marshal.StructureToPtr(programmerSoundProperties, parameterPtr, false);
                            // Wait To Play
                            WaitForLoadThenPlay(instancePtr, parameterPtr);
                        }
                    }
                    break;
                }
            case FMOD.Studio.EVENT_CALLBACK_TYPE.DESTROY_PROGRAMMER_SOUND:
                {
                    FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES parameter =
                        (FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES)Marshal.PtrToStructure(
                            parameterPtr, typeof(FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES));
                    FMOD.Sound sound = new FMOD.Sound(parameter.sound);
                    sound.release();
                    break;
                }
            case FMOD.Studio.EVENT_CALLBACK_TYPE.DESTROYED:
                {
                    // Now the event has been destroyed, unpin the string memory so it can be garbage collected
                    stringHandle.Free();
                    break;
                }
        }
        return FMOD.RESULT.OK;
    }

    private static void WaitForLoadThenPlay(IntPtr instancePtr, IntPtr parameterPtr)
        => instance.StartCoroutine(instance.WaitForLoadThenPlayRoutine(instancePtr, parameterPtr));
    private IEnumerator WaitForLoadThenPlayRoutine(IntPtr instancePtr, IntPtr parameterPtr)
    {
        // Get Instance
        FMOD.Studio.EventInstance instance = new FMOD.Studio.EventInstance(instancePtr);
        // Grab Sound Reference
        FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES parameter =
            (FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES)Marshal.PtrToStructure(
                parameterPtr, typeof(FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES));
        FMOD.Sound sound = new FMOD.Sound(parameter.sound);

        // Wait for Load
        FMOD.OPENSTATE state = FMOD.OPENSTATE.BUFFERING;
        uint percentbuffered;
        bool starving;
        bool diskbusy;
        while (state != FMOD.OPENSTATE.READY)
        {
            yield return null;
            sound.getOpenState(out state, out percentbuffered, out starving, out diskbusy);
        }
        instance.start();
        instance.release();
    }

这是我最后做的。我不知道它是否有意义,但它确实消除了警告。我真的希望他们能修复文档。示例脚本无法正常工作。

    public static void PlayDialogue(string key, Action callback = null) => instance.StartCoroutine(PlayDialogueRoutine(key, callback));
    public static IEnumerator PlayDialogueRoutine(string key, Action callback = null)
    {
        // Check if Already Playing
        if (instance.activeDialogue.ContainsKey(key))
        {
            Debug.LogError("Tried to play already playing dialogue");
            callback?.Invoke();
            yield break;
        }

        // Load Sound Path
        FMOD.Studio.SOUND_INFO dialogueSoundInfo;
        FMOD.RESULT keyResult = FMODUnity.RuntimeManager.StudioSystem.getSoundInfo(key, out dialogueSoundInfo);
        if (keyResult != FMOD.RESULT.OK)
        {
            Debug.LogError("Couldn't find dialogue with key: " + key);
            callback?.Invoke();
            yield break;
        }

        // Load Sound
        FMOD.Sound dialogueSound;
        FMOD.MODE soundMode = FMOD.MODE.LOOP_NORMAL
            | FMOD.MODE.CREATECOMPRESSEDSAMPLE
            | FMOD.MODE.NONBLOCKING;
        FMOD.RESULT soundResult = FMODUnity.RuntimeManager.CoreSystem.createSound(
            dialogueSoundInfo.name_or_data, soundMode | dialogueSoundInfo.mode,
            ref dialogueSoundInfo.exinfo, out dialogueSound);
        if (soundResult != FMOD.RESULT.OK)
        {
            Debug.LogError("Couldn't load sound: " + key);
            callback?.Invoke();
            yield break;
        }

        // Wait to Load
        int maxFrameWait = 120;
        FMOD.OPENSTATE openstate = FMOD.OPENSTATE.BUFFERING;
        uint percentbuffered;
        bool starving;
        bool diskbusy;
        while (openstate != FMOD.OPENSTATE.READY)
        {
            yield return null;
            dialogueSound.getOpenState(out openstate, out percentbuffered, out starving, out diskbusy);
            if (--maxFrameWait <= 0)
            {
                dialogueSound.release();
                Debug.LogError("Failed to load dialogue sound " + key);
                yield break;
            }
        }

        // Create Instance 
        FMOD.Studio.EventInstance dialogueInstance = FMODUnity.RuntimeManager.CreateInstance(instance.dialogueEvent);

        // Store Reference (and remove in play stopped callback) 
        instance.activeDialogue[key] = dialogueInstance;

        // Pin Memory
        FMODDialogueParameterWrapper soundWrapper = new FMODDialogueParameterWrapper(dialogueSound, dialogueSoundInfo, () =>
        {
            instance.activeDialogue.Remove(key);
            callback?.Invoke();
        });
        GCHandle soundWrapperHandle = GCHandle.Alloc(soundWrapper, GCHandleType.Pinned);
        dialogueInstance.setUserData(GCHandle.ToIntPtr(soundWrapperHandle));
        // Set Callback
        dialogueInstance.setCallback(instance.dialogueCallback,
            FMOD.Studio.EVENT_CALLBACK_TYPE.CREATE_PROGRAMMER_SOUND
            | FMOD.Studio.EVENT_CALLBACK_TYPE.CREATE_PROGRAMMER_SOUND
            | FMOD.Studio.EVENT_CALLBACK_TYPE.DESTROYED
            | FMOD.Studio.EVENT_CALLBACK_TYPE.STOPPED);

        // Play One Shot
        dialogueInstance.start();
        dialogueInstance.release();
    }

    [AOT.MonoPInvokeCallback(typeof(FMOD.Studio.EVENT_CALLBACK))]
    static FMOD.RESULT DialogueCallback(FMOD.Studio.EVENT_CALLBACK_TYPE type, IntPtr instancePtr, IntPtr parameterPtr)
    {
        // Get Instance
        FMOD.Studio.EventInstance instance = new FMOD.Studio.EventInstance(instancePtr);

        // Retrieve the user data FMODDialogueParameterWrapper
        IntPtr dialogueParameterWrapperPtr;
        FMOD.RESULT result = instance.getUserData(out dialogueParameterWrapperPtr);
        if (result != FMOD.RESULT.OK)
        {
            Debug.LogError("Failed to fetch user data for dialogue callback: " + result);
        }
        else if (dialogueParameterWrapperPtr != IntPtr.Zero)
        {
            GCHandle dialogueParameterWrapperHandle = GCHandle.FromIntPtr(dialogueParameterWrapperPtr);
            FMODDialogueParameterWrapper dialogueParameterWrapper =
                (FMODDialogueParameterWrapper)dialogueParameterWrapperHandle.Target;
            switch (type)
            {
                case FMOD.Studio.EVENT_CALLBACK_TYPE.CREATE_PROGRAMMER_SOUND:
                    {
                        FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES programmerSoundProperties =
                            (FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES)Marshal.PtrToStructure(
                                parameterPtr, typeof(FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES));
                        programmerSoundProperties.sound = dialogueParameterWrapper.sound.handle;
                        programmerSoundProperties.subsoundIndex = dialogueParameterWrapper.soundInfo.subsoundindex;
                        Marshal.StructureToPtr(programmerSoundProperties, parameterPtr, false);
                        break;
                    }
                case FMOD.Studio.EVENT_CALLBACK_TYPE.DESTROY_PROGRAMMER_SOUND:
                    {
                        FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES parameter =
                            (FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES)Marshal.PtrToStructure(
                                parameterPtr, typeof(FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES));
                        FMOD.Sound sound = new FMOD.Sound(parameter.sound);
                        sound.release();
                        break;
                    }
                case FMOD.Studio.EVENT_CALLBACK_TYPE.DESTROYED:
                    {
                        // Now the event has been destroyed, unpin the string memory so it can be garbage collected
                        Debug.Log("Freeing");
                        dialogueParameterWrapperHandle.Free();
                        break;
                    }
                case FMOD.Studio.EVENT_CALLBACK_TYPE.STOPPED:
                    {
                        dialogueParameterWrapper.onStopCallback?.Invoke();
                        break;
                    }
            }
        }
        return FMOD.RESULT.OK;
    }

        [StructLayout(LayoutKind.Sequential)]
class FMODDialogueParameterWrapper
{
    public FMOD.Sound sound;
    public FMOD.Studio.SOUND_INFO soundInfo;
    public Action onStopCallback;

    public FMODDialogueParameterWrapper(FMOD.Sound sound, FMOD.Studio.SOUND_INFO soundInfo, Action onStopCallback)
    {
        this.sound = sound;
        this.soundInfo = soundInfo;
        this.onStopCallback = onStopCallback;
    }
}