Duration of a programmer sound

Hi,
I need to know how long is a programmer sound that i’m playing. For this purpouse I’ve built a XML parser (I’m working with unity) that process all the file in the /Metadata/AudioFile and extract the length of every audio file used in the Project.
This worked fine until I noticed that some XML are missing. I mean that I have some audio files that are used in some events, but doesn’t have a related XML in the Metadata folder.

Is there a reason why some assets doesn’t have an XML?

PS: I know that I could use the callback system, but it’is a little bit buggy and sometimes it doesn’t call the programmer_sound_destroyed callback and I don’t know why. Don’t know if this is a problem with your api or a problem in my scripts, but I want a safety measure that assure me I can catch the end of a programmer sound, and knowing the length of the asset was a smoothly working solution.

Thanks for your help,
Simone

Hello Simone,

I’m afraid we can’t provide help with the XML issue, that is a Unity issue so please check with them on the Unity forums.

The length of the programmer sound depends on two things:

  1. If the programmer instrument in your event is set to be async or not
  2. The length of the asset being used in the instrument

The easiest method would be to set the programmer instrument to be async and just use the length of the assets you’re using. You’d need to contact Unity about why some of your audio files don’t have XML.

You could also try using Sound::getLength within the programmer sound function(s) to get the length of the asset you are using, but this will only work at runtime.

In regards to the programmer sounds not creating the programmer_sound_destroyed callback, could you please post a snippet of your code to see if something is going wrong there? Our internal tests show all callbacks behaving normally.

Thanks,
Richard

2 Likes

Probably I wasn’t clear in my question.
The XMLs are the ones that FMOD studio produces in the metadata folder. It’s not something related to Unity, Unity is only the software where I run my parser.
I also noticed that when an XML is missing if I search the related asset name in the asset palette in FMOD studio, the search doesn’t give me results. So this is definitly an FMOD studio problem, not generating some asset XML.

Regarding the solution you proposed, the one related to une Sound::getLength for the async programmer sound, I’ve alredy tried, but I always get length=0. I’m doing this at runtime in the callback “FMOD.Studio.EVENT_CALLBACK_TYPE.CREATE_PROGRAMMER_SOUND” after I have created the sound.

Here is the script that is using the callbacks. We are using an enum that let us process the callback in the update because if I call a function directly in the callback Unity crashes.
The rest of the code is pretty straight, I copied it from an example that I found on the web and that was also in some tutorial on your website.

public class DialogueActor : BaseObject
{
public IActorDelegate actorDelegate;

[FMODUnity.EventRef]
public string asset;

// [HideInInspector]
private bool _isActing = false;

private EventInstance dialogueInstance;

public bool isActing {
	get {
		return _isActing;
	}
}

private FMOD.Studio.EVENT_CALLBACK dialogueCallback;
private LaunchEventEnum launchEvent = LaunchEventEnum.none;


private string _lastKeyDebug = "Not Initialized";
// Update is called once per frame
void Update ()
{
	switch (launchEvent) {
	case LaunchEventEnum.audioStopped:
		if (verboseMode) {
			Debug.Log ("Audio stopped");
		}
		if (actorDelegate != null) {
			actorDelegate.onActorStopTalking (this);
		}
		Debug.LogError (Time.frameCount + "\t" + _lastKeyDebug + "\tSTOP_EVENT");
		if (verboseMode) {
		}
		launchEvent = LaunchEventEnum.none;
		break;
	case LaunchEventEnum.audioStarted:
		launchEvent = LaunchEventEnum.none;
		break;
	default:
		break;
	}
}

/// <summary>
/// Plaies the dialogue.
/// </summary>
/// <param name="key">FMOD Key.</param>
public void PlayDialogue (string key)
{

// Debug.Log (Time.frameCount + “\t” + key + "\tisacting: " + _isActing);
if (!_isActing) {
_isActing = true;
dialogueCallback = new FMOD.Studio.EVENT_CALLBACK (DialogueEventCallback);

		Debug.LogError (Time.frameCount + "\t" + key + "\tPLAY");
		dialogueInstance = FMOD_AudioController.createEventInstance (asset, gameObject.name, gameObject.transform);

		if (verboseMode) {
			Debug.Log ("Sono nel playDialog dell'attore e isActing: " + _isActing);
			Debug.Log ("playdialogue: " + gameObject.name + "\t" + key);
			Debug.Log ("Dialogue instance: " + dialogueInstance.isValid ());
		}

		//FMODUnity.RuntimeManager.AttachInstanceToGameObject (dialogueInstance, gameObject.transform, rigidBody: null);

		//// 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 (dialogueCallback);
		FMOD_AudioController.play (dialogueInstance, gameObject.name, true, this.transform);
		FMOD_AudioController.release (dialogueInstance, gameObject.name);
		_lastKeyDebug = key;
	}
}

/// <summary>
/// Stops the dialogue.
/// </summary>
/// <param name="key">Key.</param>
public void StopDialogue (string key)
{
	
	if (_isActing) {
		_isActing = false;

		if (verboseMode) {
			Debug.Log ("Stopdialogue: " + gameObject.name + "\t" + key);
			Debug.Log ("Dialogue instance: " + dialogueInstance);
		}
		FMOD_AudioController.stop (dialogueInstance, STOP_MODE.ALLOWFADEOUT, gameObject.name);
		FMOD_AudioController.release (dialogueInstance, gameObject.name);

	}
}

private FMOD.RESULT DialogueEventCallback (FMOD.Studio.EVENT_CALLBACK_TYPE type, FMOD.Studio.EventInstance instance, IntPtr parameterPtr)
{
	// Recreate the event instance C# object
	// 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:
		{
			//if (actorDelegate != null)
			//{
			//    actorDelegate.onActorBeginTalking(this);
			//}

// Debug.Log (“SOUND_STARTED”);
launchEvent = LaunchEventEnum.audioStarted;

			//Debug.Log ("key: " + key);
			var parameter = (FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES)Marshal.PtrToStructure (parameterPtr, typeof(FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES));
			FMOD.Studio.SOUND_INFO dialogueSoundInfo;
			var keyResult = FMODUnity.RuntimeManager.StudioSystem.getSoundInfo (key, out dialogueSoundInfo);
			//Debug.Log("KeyResult: " + keyResult);
			if (keyResult != FMOD.RESULT.OK) {
				break;
			}
			FMOD.Sound dialogueSound;
			var soundResult = FMODUnity.RuntimeManager.LowlevelSystem.createSound (dialogueSoundInfo.name_or_data, dialogueSoundInfo.mode, ref dialogueSoundInfo.exinfo, out dialogueSound);
			if (soundResult == FMOD.RESULT.OK) {
				parameter.sound = dialogueSound.handle;

				uint length = uint.MaxValue;
				dialogueSound.getLength (out length, FMOD.TIMEUNIT.MS);
				Debug.Log ("MS LENGTH: " + length + " - " + _lastKeyDebug);

				dialogueSound.getLength (out length, FMOD.TIMEUNIT.MODORDER);
				Debug.Log ("MODORDER LENGTH: " + length + " - " + _lastKeyDebug);
				dialogueSound.getLength (out length, FMOD.TIMEUNIT.MODPATTERN);
				Debug.Log ("MODPATTERN LENGTH: " + length + " - " + _lastKeyDebug);
				dialogueSound.getLength (out length, FMOD.TIMEUNIT.PCM);
				Debug.Log ("PCM LENGTH: " + length + " - " + _lastKeyDebug);
				dialogueSound.getLength (out length, FMOD.TIMEUNIT.RAWBYTES);
				Debug.Log ("RAWBYTES LENGTH: " + length + " - " + _lastKeyDebug);
				parameter.subsoundIndex = dialogueSoundInfo.subsoundindex;
				Marshal.StructureToPtr (parameter, parameterPtr, false);
			}
		}
		break;
	case FMOD.Studio.EVENT_CALLBACK_TYPE.DESTROY_PROGRAMMER_SOUND:
		{    //Debug.Log("DESTROY PROGRAMEMR SOUND");
			var parameter = (FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES)Marshal.PtrToStructure (parameterPtr, typeof(FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES));

			var sound = new FMOD.Sound ();
			sound.handle = parameter.sound;
			sound.release ();
			//Debug.Log ("Stopping");
		}
		break;
	case FMOD.Studio.EVENT_CALLBACK_TYPE.STOPPED:

// Debug.Log (“STOPPED”);
break;
case FMOD.Studio.EVENT_CALLBACK_TYPE.SOUND_STOPPED:
// Debug.Log (“SOUND_STOPPED”);
// StartCoroutine(launchOnActorStopTalking());
_isActing = false;
launchEvent = LaunchEventEnum.audioStopped;
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.LogError ("DESTROY " + _lastKeyDebug);
stringHandle.Free ();
// Debug.Log (“Destroyed”);

		break;
	}
	return FMOD.RESULT.OK;
}

public float getIntensity ()
{
	if (dialogueInstance.isValid ()) {
		float intensity = FMOD_AudioController.getParameterFinalValue (dialogueInstance, FMOD_ParameterEnum.ShaderIntensity, gameObject.name);
		if (verboseMode) {
			Debug.Log (" value" + intensity);
		}
		return intensity;
	}
	else {

// Debug.LogError (“[Actor] The Actor attached to the object " + this.gameObject.name + " can’t get the sound intensity, because the event is not initiazlized”);
return 0;
}
}

private IEnumerator launchOnActorStopTalking ()
{
	yield return null;

	if (actorDelegate != null) {
		actorDelegate.onActorStopTalking (this);
	}
}

private enum LaunchEventEnum
{
	none,
	audioStarted,
	audioStopped,
}

}

public enum ActorEnum
{
JeanDialogues = 0,
JeanInside = 6,
Scott = 1,
CharlesDialogues = 2,
Rachel = 3,
Warden = 4,
Bob = 5,
CharlesTapes = 7,
Radio = 8,
Operator = 9,
Table = 10,
SFX = 11
}

public interface IActorDelegate
{
void onActorBeginTalking (DialogueActor actor);

void onActorStopTalking (DialogueActor actor);

}

Thank you for your support,
Simone

Hi Simone,

Your code looks fine, but it is sounding like you are placing your entire FMOD Studio project into your Unity project.

The XMLs are generated by your FMOD Studio project but ideally you shouldn’t be placing these files or parsing them within your game engine.

Also, I’m not sure how you are passing keys into this programmer sound system.

If FMOD::Sound is returning 0 but playing audio, it is likely you are using an audio table and passing keys into this. In this situation, the FMOD::Sound will return 0 as it is actually using a Bank rather than an actual sound file.

If you just need a static list of all the audio files you are using, I would recommend using the FMOD Studio scripting API from within your FMOD Studio project to get these.

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

Thanks,
Richard

I know I shouldn’t use the XMLs of the FMOD project, but I was trying to find a workaround for my problem. The missing XML is surely a bug that has effect in FMOD Studio to, like I was saying it makes the search of an asset file buggy.

“Also, I’m not sure how you are passing keys into this programmer sound system.” I don’t know what you mean with this, but I can confirm what you’ve said: “If FMOD::Sound is returning 0 but playing audio, it is likely you are using an audio table and passing keys into this. In this situation, the FMOD::Sound will return 0 as it is actually using a Bank rather than an actual sound file”.
Is there another way to do this? Am I doing something wrong in the implementation of using audioTables and programmer sounds?

However, I’ve looked at your solution to retrieve the static list of the sound length (that is actually what I need), but I wasn’t able to do that. I studied the documentation you linked and i wrote this script:

studio.menu.addMenuItem({ name: “Asset Lengths”,
isEnabled: function() {},

execute: function() {
  var items = studio.project.workspace.audioBinManager.masterAudioBinFolder.items;
     for(var i=0; i < items.length; i++) {
       console.log("fileName " + items[i].name);
       console.log("length " + items[i].length);
     }
},

});

I get errors on this line: “studio.project.workspace.audioBinManager.masterAudioBinFolder.items;” but I cant understand how to get the list of the asset files to parse in for cycle.

I really appreciate your support!!
Regards,
Simone

Hi Simone,

The only time an asset will not be in the XML is if the asset is placed directly into the Assets folder and not added through FMOD Studio. It will appear in your assets browser with a question mark icon and a “New!” tag. This is by design and it won’t be truly imported until it is moved or you right click and choose “Import”.

If you have a few “New!” assets, search for “#unimported” to get a list in the assets browser. You can then bulk select and import them.

In regards to your code, first you should replace the items variable with this:

var items = studio.project.workspace.masterAssetFolder.assets;

You can also set isEnabled to just true to enable it all the time.

In regards to the programmer sound keys - you can also pass a path in as a key, which is why I was asking. If you are using audio tables and passing keys then you won’t be able to pull the length of the FMOD::Sound that way.

Thanks,
Richard

3 Likes

I resolved using an FMOD studio script like you suggested!

Really thanks for your help!