Can you access a list of markers at runtime?

First of all, I love the marker and tempo system in Fmod Studio - it’s great! One thing I’d really like to be able to do is get access to a list of all the markers in a given EventInstance. I’m working on a music game and I want to be able to mark specific, musically relevant moments in Fmod Studio that my game code can watch for and then react to with cool particle FX, animations, etc.

I was hoping that getSyncPoints() on the Sound class might contain marker data but my testing indicates that this isn’t the case. Is there another place this information is stored that I could get at, or is it simply not accessible? All I really need is a list of markers with their names and timeline positions so that I can use getTimelinePosition() to trigger events in my game when specific markers are reached.

If anyone knows if this is possible and/or how to achieve it I would be very grateful. Thank you for your time!

Currently this is not exposed to the API, but it is a good suggestion. We’ll look at exposing it in a future revision. There would be two ways of getting it, one would be from the event description, and another would be to receive a callback when passing a marker on an instance.

1 Like

Is this possible right now? When loading an event, I need my game to setup the environment based on which markers (names) are used in the event. I know that the callback is implemented, but I’d need the information even before that and not only when the marker is passed.

E.g. if I have a marker named “A” appearing throughout the event, I want to spawn an enemy that listens to markers named “A” to be triggered and then act. But if the current event only has markers B and C, I don’t want to spawn that enemy, as it could never act because of the missing “A” markers.

My fallback solution right now is to have a separate “metadata” file where I manually store information like this. But it would reduce the overhead immensely if I just could ask FMOD for a list of all markers and their positions in the event when loading it.

Thanks!

Hello Anteevy,

You are able to get callbacks from FMOD when a marker is passed using the FMOD_STUDIO_EVENT_CALLBACK_TYPE and the FMOD_STUDIO_TIMELINE_MARKER_PROPERTIES API calls.

https://www.fmod.org/docs/content/generated/FMOD_STUDIO_EVENT_CALLBACK_TYPE.html
https://www.fmod.org/docs/content/generated/FMOD_STUDIO_TIMELINE_MARKER_PROPERTIES.html

There isn’t a way to get a list of markers from an event without playing it, but you can still use your game code to simply spawn the enemy when you get a callback from marker “A”.

I hope this helps.
Richard

Thanks for the quick reply! I feared as much. I’m trying to setup everything before any markers are hit. But if that’s not possible, maybe I’ll just add extra markers at the start that contain the information I need in their name. This would at least be better than extra metadata files.

Hi Anteevy,

An alternative you can use is to enter all known markers into the event’s User Properties section (under the Overview on the far right hand side) and then to use Studio::EventDescription::getUserProperty to pull this information out.

https://www.fmod.org/docs/content/generated/FMOD_Studio_EventDescription_GetUserProperty.html

This way you will have the names of the markers without needing to play the event itself and without any extra metadata files.

Thanks,
Richard

Cool, that also helps a lot, didn’t know that. Thanks!

Did this ever get added to the API? I actually have a use case where I really do need to know where the markers are in the event ahead of time, before playback begins. Waiting for the callbacks to be fired is going to be far too late.

Hi Josh,

This has not been implemented yet, but it is in our tracker.

If you really need the exact times of markers you could use the FMOD Studio scripting API to grab the times of all markers in an event.

https://www.fmod.com/resources/documentation-studio?page=scripting-terminal-reference.html

Take a look into an event’s array of markerTracks for the position of any ManagedObject::NamedMarker

Thanks,
Richard

Yup, just came back to say I figured this out using the scripting API and it’s not very difficult (as long as you’re basically familiar with Javascript). For future searchers, here’s a quick crash course in how to do this:

First, make a folder called “Scripts” in your FMOD Studio project, and make an ExportMarkers.js inside that folder. This will be our simple Javascript plugin for automatically exporting the marker data from FMOD when we build our banks.

In that script, create a Javascript function with the signature “onBuild(success)” and bind it to the build event like so:

studio.project.buildEnded.connect(onBuild);

In your onBuild, “success” will be true for successful builds and false for failed builds. Handle failure however you like.

For successful builds, you can get an array of all events in the project like this:

studio.project.model.Event.findInstances()

Each event has an array called “markerTracks”, each MarkerTrack has an array called “markers”, and each marker has some different data depending on what kind of marker it is. The easiest way to get a feel for the different data layouts is to explore the data model in the FMOD Studio Console window (Ctrl+0). Here are some useful commands for that:

// Get all events
var events = studio.project.model.Event.findInstances()

// See a list of events
events

// See a list of marker tracks within an event
events[x].markerTracks

// See a list of markers with a track
events[x].markerTracks[y].markers

// Show the json data structure of a given marker
events[x].markerTracks[y].markers[z].dump()

In your Javascript, you basically just iterate over all events (and all their marker tracks (and all their markers)), extract whatever data you care about into some data structure that’s useful to you, then write the resulting data structure to some .json file you can then read in on the game side. The basic file I/O works like so:

var file = studio.system.getFile(outputPath);
file.open(studio.system.openMode.WriteOnly);
file.writeText(JSON.stringify(yourDataStructure));
file.close();

I’ll leave robust error checking, etc. as an exercise for the reader. For the outputPath, note that you can get the current path of the FMOD Studio project file with:

studio.project.filePath

In my case, our FMOD studio project lives inside our Unity project folder, so I just took studio.project.filePath and walked up the directory tree until I found the Unity project root, than tacked on “/Assets/Resources/markers.json”. You can do whatever makes the most sense for your game.

On the game side, you’ll need some way to deserialize the .json file into some usable data structure. Unity has a nice JSONUtility class that makes this easy. Once you’ve deserialized the data, it should be trivial to figure out what markers you have available, what they’re called, where they fall in time, and any other data you chose to capture in your export script.

Feel free to correct inaccuracies or point out better ways of doing this!

2 Likes

@josh.sutphin Big Thanks, I think I would have never had the patience to go through the whole script API to find each of these commands. Even with your hints it took me a more than a week to splat a script together. I never wrote a java script before, so I’m happy that I came this far :smiley:

As long as Fmod developers don’t add a function for that, my script might save some time.
I use the CSV file format. The file gets outputted to the Fmod Studio installation directory with the event name as file name.
Ofcourse you’d need to do some changes to the script, if you don’t like my data structure.

/*
	-------------------------------------------
	Exporting Markers
	-------------------------------------------
	!!!ATTENTION!!! 
	This script modifies your markers in order to sort them by time, this may not be in your interest.
	look further down the script for the "!!!ATTENTION!!!" comment.
	
	This Script was written for Fmod Studio 2.00.01
	it exports markers into a single csv file that can be read by Unreal Engine 4.22 into a Data table
	
	The data struct inside of the Unreal Engine to be linked should contain:
	IsActive(bool),Name(string),Start(integer),Position(integer),End(integer)
	
	the script awaits that every event track is dedicated to a single instrument,
	if it finds a single loop, transition or anything that has an undefined name,
	it will consider that event track not meant to be written into the CSV file.
*/


studio.menu.addMenuItem({ name: "Markers To File",
	// if no event is selectd, the script can not be executed
	isEnabled: function() { var event = studio.window.browserCurrent(); return event && event.isOfExactType("Event"); },
	keySequence: "Alt+E",
	execute: function() {
		//get the current selected event
		var event = studio.window.browserCurrent();

		//get all events and not only the selectd one
		//var events = studio.project.model.event.findInstances();

		//set CSV table column structure as first line (--- is the column header for the ID of every row in UE4)
		var FinalData = "---,Position,Name";
		var InstrumentData="";
		var i=0, start, position, end
		var timemargin=200;	//can be adjusted, will be used to calculate start and end
		//because UE4 awaits the values to be set in quotes and Jscript doesn't accept """ as string
		var quotes = String.fromCharCode(34);
		
		//console can be opened with ctl+0 in fmod studio 
		//console.log("############Sort#################");
		
		// Sort markers by time/position
		/* 
		-------------------------------------------
		##############!!!ATTENTION!!!##############
		-------------------------------------------
		this process edits the marker data in the Fmod Project
		it only swaps the positions and names of the markers, other data stays with the individual marker
		This is a simple bubble sort
		as default markers are in the markers array as they were created and not where they sit on the timeline
		*/
		for (x = 0; x < event.markerTracks.length; x++){
			var sorting = true;
			var tempP, tempN;
			
			while (sorting){
				sorting = false;
				
				for (y = 0; y+1 < event.markerTracks[x].markers.length; y++){
					//need a swap?
					if(event.markerTracks[x].markers[y].position > event.markerTracks[x].markers[y+1].position){
						//set sort to true for while loop
						sorting=true;
						// save value and name
						tempP = event.markerTracks[x].markers[y].position;
						tempN = event.markerTracks[x].markers[y].name;
						//interchange values and names
						event.markerTracks[x].markers[y].position = event.markerTracks[x].markers[y+1].position;
						event.markerTracks[x].markers[y].name = event.markerTracks[x].markers[y+1].name;
						event.markerTracks[x].markers[y+1].position = tempP;
						event.markerTracks[x].markers[y+1].name = tempN;
					}
				}

			}
		}
		
		//console can be opened with ctl+0 in fmod studio 
		//console.log("############Sort Finished#################");
		
		
		/*
		the writing process is seperated into FinalData and InstrumentData because the script will stop
		writing to InstrumentData if there is a single loop, transition or other stuff without a name
		in the event track, each event track shall only contain markers
		*/		
		for (x = 0; x < event.markerTracks.length; x++){
			for (y = 0; y < event.markerTracks[x].markers.length; y++){
				//loops,transistions and transition regions have an undefined name
				if(event.markerTracks[x].markers[y].name == undefined){
					InstrumentData="";	//delete possible written track markers
					//break for loop, because the event track contains loops,transitions,...
					break;
				}
				else{
					//recalculate position to a full millisecend format (as fmod puts that out)
					position = Math.round(event.markerTracks[x].markers[y].position*1000)
										
					//Add the marker time position and name to the InstrumentData variable
					InstrumentData += "\r\n" + i + "," + quotes + position + quotes + "," + quotes + event.markerTracks[x].markers[y].name + quotes;
					//index for the csv file, every line needs a unique ID
					i=i+1;
				}
			}
		//add line of data to variable that get written into the csv file
		FinalData += InstrumentData;
		//clear for next line of data
		InstrumentData="";
		}
		
		//File output, file can be found in the fmod studio install directory
		var file = studio.system.getFile("./"+event.name+".csv");
		file.open(studio.system.openMode.WriteOnly);
		file.writeText(FinalData);
		file.close();

	}
});
1 Like