Thank you Cameron.
Unity is on 2018.1.1f1 (64bit)
and FMOD 1.10.05
The file is edited but just invoking a callback and I’m passing the emitter EventInstance -> I assume the error comes from there. Any tipp how to avoid this ?
using System;
using System.Runtime.InteropServices;
using UnityEngine;
class ScriptUsageTimeline : MonoBehaviour
{
// Variables that are modified in the callback need to be part of a seperate class.
// This class needs to be ‘blittable’ otherwise it can’t be pinned in memory.
[StructLayout(LayoutKind.Sequential)]
class TimelineInfo
{
public int currentMusicBar = 0;
public int currentMusicBeat = 0;
public FMOD.StringWrapper lastMarker = new FMOD.StringWrapper();
}
TimelineInfo timelineInfo;
GCHandle timelineHandle;
FMOD.Studio.EVENT_CALLBACK beatCallback;
FMOD.Studio.EventInstance musicInstance;
public FMODUnity.StudioEventEmitter battleMusicEmitter;
static Battle battleScript;
void Start()
{
timelineInfo = new TimelineInfo();
battleScript = GetComponent<Battle>();
// Explicitly create the delegate object and assign it to a member so it doesn't get freed
// by the garbage collected while it's being used
beatCallback = new FMOD.Studio.EVENT_CALLBACK(BeatEventCallback);
//musicInstance = FMODUnity.RuntimeManager.CreateInstance("event:/music/battles/hati_skol_battlemusic");
musicInstance = battleMusicEmitter.EventInstance;
// Pin the class that will store the data modified during the callback
timelineHandle = GCHandle.Alloc(timelineInfo, GCHandleType.Pinned);
// Pass the object through the userdata of the instance
musicInstance.setUserData(GCHandle.ToIntPtr(timelineHandle));
musicInstance.setCallback(beatCallback, FMOD.Studio.EVENT_CALLBACK_TYPE.TIMELINE_BEAT | FMOD.Studio.EVENT_CALLBACK_TYPE.TIMELINE_MARKER);
//musicInstance.start();
}
void OnDestroy()
{
musicInstance.stop(FMOD.Studio.STOP_MODE.IMMEDIATE);
musicInstance.release();
timelineHandle.Free();
}
[AOT.MonoPInvokeCallback(typeof(FMOD.Studio.EVENT_CALLBACK))]
static FMOD.RESULT BeatEventCallback(FMOD.Studio.EVENT_CALLBACK_TYPE type, FMOD.Studio.EventInstance instance, IntPtr parameterPtr)
{
// Retrieve the user data
IntPtr timelineInfoPtr;
instance.getUserData(out timelineInfoPtr);
// Get the object to store beat and marker details
GCHandle timelineHandle = GCHandle.FromIntPtr(timelineInfoPtr);
TimelineInfo timelineInfo = (TimelineInfo)timelineHandle.Target;
switch (type)
{
case FMOD.Studio.EVENT_CALLBACK_TYPE.TIMELINE_BEAT:
{
var parameter = (FMOD.Studio.TIMELINE_BEAT_PROPERTIES)Marshal.PtrToStructure(parameterPtr, typeof(FMOD.Studio.TIMELINE_BEAT_PROPERTIES));
if(parameter.bar != timelineInfo.currentMusicBar)
battleScript.TriggerPhase();
battleScript.BeatTest();
timelineInfo.currentMusicBar = parameter.bar;
timelineInfo.currentMusicBeat = parameter.beat;
//Debug.Log(String.Format("Current Bar = {0}, Last Marker = {1}", timelineInfo.currentMusicBar, (string)timelineInfo.lastMarker));
}
break;
case FMOD.Studio.EVENT_CALLBACK_TYPE.TIMELINE_MARKER:
{
var parameter = (FMOD.Studio.TIMELINE_MARKER_PROPERTIES)Marshal.PtrToStructure(parameterPtr, typeof(FMOD.Studio.TIMELINE_MARKER_PROPERTIES));
timelineInfo.lastMarker = parameter.name;
Debug.Log(timelineInfo.lastMarker);
}
break;
}
return FMOD.RESULT.OK;
}
Thanks for your patience, it looks like there is a chance that callback will be called one last time as the FMOD system releases but after the GCHandle has already been freed.
By overwriting the musicInstances user data in the OnDestroy, we can avoid this.