Hi, I’m the sound designer for a project that’s using interactive music and we want to use a marker as a callback to reset a parameter during an event. I’m not a programmer and my programmer has never used FMOD before and we can’t get the timeline marker callbacks working properly. Using the example script in FMOD’s API he is able to read the first marker in the event, but no others. He is able to progress through the event normally so he should be passing the other markers in the timeline. The marker we need to use is called “To Loop Start” and it’s near the end of the event. Here is a copy of his script:
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 FMOD.StringWrapper lastMarker = new FMOD.StringWrapper();
}
TimelineInfo timelineInfo;
GCHandle timelineHandle;
FMOD.Studio.EVENT_CALLBACK beatCallback;
FMOD.Studio.EventInstance musicInstance;
void Start()
{
timelineInfo = new TimelineInfo();
// 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/Bard/Phase 1/3D/Bard Index Temp");
// 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();
}
void OnGUI()
{
GUILayout.Box(String.Format("Current Bar = {0}, Last Marker = {1}",timelineInfo.currentMusicBar,(string)timelineInfo.lastMarker));
}
[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));
timelineInfo.currentMusicBar = parameter.bar;
}
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;
}
break;
}
return FMOD.RESULT.OK;
}
}