Autoplay next item in AudioTable

Hi everyone!

So I have a WALKMAN in my game that’s using an AudioTable together with Programmer Instrument to play music tracks. The tracks play and everything works except one crucial part, autoplay.

I know which key is the next one I want to play, I just simply can’t get it to work without freezing the game.

After a track has been played I want it to play the next one from an audio table. I can provide the next key no problem but everything seems to crash. This is my programmer instrument callback, everything is matching FMOD’s example except case EVENT_CALLBACK_TYPE.DESTROYED: where I try to play the next in my list.

Doing this freezes the game completely, anyone got a clue on how I can implement this?

Here’s my programmer music callback

[AOT.MonoPInvokeCallback(typeof(EVENT_CALLBACK))]
static RESULT MusicEventCallback(EVENT_CALLBACK_TYPE type, EventInstance instance, IntPtr parameterPtr)
{
	// Retrieve the user data
	instance.getUserData(out var stringPtr);

	// Get the string object
	GCHandle stringHandle = GCHandle.FromIntPtr(stringPtr);
	string key = stringHandle.Target as string;

	switch (type)
	{
		case EVENT_CALLBACK_TYPE.CREATE_PROGRAMMER_SOUND:
		{
			const MODE soundMode = MODE.LOOP_NORMAL | MODE.CREATECOMPRESSEDSAMPLE | MODE.NONBLOCKING;
			var parameter = (PROGRAMMER_SOUND_PROPERTIES) Marshal.PtrToStructure(parameterPtr, typeof(PROGRAMMER_SOUND_PROPERTIES));

			var keyResult = RuntimeManager.StudioSystem.getSoundInfo(key, out var dialogueSoundInfo);
			if (keyResult != RESULT.OK)
			{
				break;
			}
		
			var soundResult = RuntimeManager.CoreSystem.createSound(dialogueSoundInfo.name_or_data, soundMode | dialogueSoundInfo.mode, ref dialogueSoundInfo.exinfo, out var dialogueSound);
			if (soundResult == RESULT.OK)
			{
				parameter.sound = dialogueSound.handle;
				parameter.subsoundIndex = dialogueSoundInfo.subsoundindex;
				Marshal.StructureToPtr(parameter, parameterPtr, false);
			}

			break;
		}
		case EVENT_CALLBACK_TYPE.DESTROY_PROGRAMMER_SOUND:
		{
			var parameter = (PROGRAMMER_SOUND_PROPERTIES) Marshal.PtrToStructure(parameterPtr, typeof(PROGRAMMER_SOUND_PROPERTIES));
			var sound = new Sound {handle = parameter.sound};
			sound.release();
			
			CassettePlayer.instance.PlayNext(); // Play next song
			break;
		}
		case EVENT_CALLBACK_TYPE.DESTROYED:
		{
			// Now the event has been destroyed, unpin the string memory so it can be garbage collected
			stringHandle.Free();
			break;
		}
	}

	return RESULT.OK;
}

One way to do this would be to use a looping multi-instrument in the event that contains a programmer instrument, then in the CREATE_PROGRAMMER_SOUND you could get the key before playing it. You could also use a parameter or sustain point to control when the event finishes.

Hi Cameron, thanks for the reply.

What do you mean getting the key before playing it, how do you intend on using that information? What I essentially needs is a way to know when the programmer instrument is done playing, I tried adding a method call to DESTROYED and DESTROY_PROGRAMMER_SOUND callback event without luck as that freezes Unity.

I’m not sure entirely of the cause of the freeze, depending on how the event is setup it sounds like it could be due to the event naturally ending/stopping as the instrument finishes while you try to update the audio table key.

By doing it the way I said, with a looping multi-instrument, you can avoid trying to update the event from a destroy callback.

switch (type)
{
    case EVENT_CALLBACK_TYPE.CREATE_PROGRAMMER_SOUND:
    {
        ...
        string key = GetNextKey();
        ...

I just realized what made the editor freeze, the callback where I’m trying to play the next track is also accessing a GameObject which will cause unity to freeze if it doesn’t happen on the main thread…

Will see if everything is fixed if I work around that! :slight_smile: