Exiting play mode while programmer sound is playing causes warnings

When exiting play mode after playing a programmer sound, FMOD will log a warning:
“[FMOD] EventInstance::flushReleaseQueue : Event instance has programmer sounds pending release, update thread will be blocked until programmer sounds are released.”

I can’t seem to figure out how to prevent this warning.

Here’s my code that handles playing programmer sounds:

public class NarrativeAudioPlayer : MonoBehaviour {

	#region Constants

	public const string EventPath = "event:/VO/vo_presenter";

	#endregion

	#region Fields

	[SerializeField] private EventReference voEvent;

	#endregion

	#region Events

	private EVENT_CALLBACK dialogueCallback;
	private EventInstance dialogue;

	public delegate void AudioCallback(NarrativeAudioPlayer audioPlayer, string key, ulong uniqueId, NetworkRunner runner);
	public static event AudioCallback onPlayAudio = default;

	#endregion

	#region Unity Methods

	void Start() {

		// 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

		dialogueCallback = new EVENT_CALLBACK(OnDialogueEvent);
	}

#if UNITY_EDITOR
	void Reset() {

		voEvent = EventReference.Find(EventPath);
	}
#endif
	#endregion

	#region Public Methods

	public EventInstance PlayDialogue(string key, ulong uniqueId, NetworkRunner runner, bool stopPrevious) {

		if (string.IsNullOrEmpty(key)) {
			return default;
		}

		//stop previous before playing new if required

		if (stopPrevious) {

			Stop();
		}

		//create new dialogue

		dialogue = RuntimeManager.CreateInstance(voEvent);

		// Pin the key string in memory and pass a pointer through the user data
		GCHandle stringHandle = GCHandle.Alloc(key);
			
		dialogue.setUserData(GCHandle.ToIntPtr(stringHandle));
		dialogue.setCallback(dialogueCallback);
		dialogue.start();
		dialogue.release();

		onPlayAudio?.Invoke(this, key, uniqueId, runner);

		return dialogue;
	}

	public void Pause() {

		if (dialogue.isValid()) {

			dialogue.setPaused(true);
		}
	}

	public void Resume() {

		if (dialogue.isValid()) {

			dialogue.setPaused(false);
		}
	}

	public void Stop() {

		if (dialogue.isValid()) {

			dialogue.release();
			dialogue.stop(FMOD.Studio.STOP_MODE.ALLOWFADEOUT);
			dialogue = default;
		}
	}

	#endregion

	#region Private Methods

	private static void CreateProgrammerSound(string key, IntPtr parameterPtr) {

		//FMOD.MODE soundMode = FMOD.MODE.LOOP_NORMAL | FMOD.MODE.CREATECOMPRESSEDSAMPLE | FMOD.MODE.NONBLOCKING;
		FMOD.MODE soundMode = FMOD.MODE.CREATESTREAM;
		var parameter = (PROGRAMMER_SOUND_PROPERTIES)Marshal.PtrToStructure(parameterPtr, typeof(PROGRAMMER_SOUND_PROPERTIES));

		SOUND_INFO dialogueSoundInfo;
		var keyResult = RuntimeManager.StudioSystem.getSoundInfo(key, out dialogueSoundInfo);

		if (keyResult != FMOD.RESULT.OK) {
			return;
		}

		FMOD.Sound dialogueSound;
		var soundResult = RuntimeManager.CoreSystem.createSound(dialogueSoundInfo.name_or_data, soundMode | dialogueSoundInfo.mode, ref dialogueSoundInfo.exinfo, out dialogueSound);

		if (soundResult == FMOD.RESULT.OK) {

			parameter.sound = dialogueSound.handle;
			parameter.subsoundIndex = dialogueSoundInfo.subsoundindex;
			Marshal.StructureToPtr(parameter, parameterPtr, false);
		}
	}

	private static void DestroyProgrammerSound(IntPtr parameterPtr) {

		var parameter = (PROGRAMMER_SOUND_PROPERTIES)Marshal.PtrToStructure(parameterPtr, typeof(PROGRAMMER_SOUND_PROPERTIES));
		var sound = new FMOD.Sound(parameter.sound);
		sound.release();
	}

	#endregion

	#region Events Callback

	[MonoPInvokeCallback(typeof(EVENT_CALLBACK))]
	private static FMOD.RESULT OnDialogueEvent(EVENT_CALLBACK_TYPE type, IntPtr instancePtr, IntPtr parameterPtr) {

		EventInstance instance = new 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;

		if (type == EVENT_CALLBACK_TYPE.CREATE_PROGRAMMER_SOUND) {

			CreateProgrammerSound(key, parameterPtr);
		}
		else if (type == EVENT_CALLBACK_TYPE.DESTROY_PROGRAMMER_SOUND) {

			DestroyProgrammerSound(parameterPtr);
		}
		else if (type == EVENT_CALLBACK_TYPE.DESTROYED) {

			// Now the event has been destroyed, unpin the string memory so it can be garbage collected
			stringHandle.Free();
		}

		return FMOD.RESULT.OK;
	}

	#endregion
}

Is there anything I’m doing wrong here?

Using FMOD 2.02.13 for Unity

1 Like

Hi,

Thank you for bringing this to our attention. You are not doing anything wrong, unfortunately, there is no way to avoid this warning. I have passed this on to our development team to look into further.

1 Like

Ok no problem. Seems to be harmless anyway

1 Like

Exactly, it is just a warning so it shouldn’t affect your project.