How to create programmer sound from AUDIOCLIP

Hi. Using your coding example for unity https://www.fmod.com/docs/2.02/unity/examples-programmer-sounds.html it loads a file from STREAMING ASSETS. I already have an AUDIOCLIP in a c# script I want to play in-game (it’s fetched and downloaded dynamically while the game is running). How would I change the example script to play an audioclip rather than a streaming file?

Hi,

This is quite an involved process and there are a couple of things to keep in mind. You will have to re-enable Unity Audio which will restrict you from building to Xbox One, and Xbox Series X|S.

The major change will be the information that we pass into our userData which instead of being the key to access the Audio Table it will be the information from the Audio Clip required to make an FMOD.Sound.

So first we will need to make a class to contain the information we need form the Audio clip.

Class set up
class SoundRequirements
{
	public string name;
	public int samples;
	public int channels; 
	public FMOD.SOUND_FORMAT format;
	public int defaultFrequence;
	public float[] sampleData;

	public SoundRequirements(string name, int samples, int channel, FMOD.SOUND_FORMAT format, int defaultFrequence, float[] sampleData)
	{
		this.name = name;
		this.samples = samples;
		this.channels = channel;
		this.format = format;
		this.defaultFrequence = defaultFrequence;
		this.sampleData = sampleData;
	}
}

Assigning the majority of those variables is pretty straight forward but getting the sampleData requires you to get that from the Audio Clip beforehand:

// Get the sample data from the clip and assign in to an array 
float[] sampleData = new float[unityAudioClip.samples * unityAudioClip.channels];
unityAudioClip.GetData(sampleData, 0);

Passing in the class to the userData is the same as the example, however, retrieving it and creating the sound is quite different. So I will include my Callback function:

Callback
[AOT.MonoPInvokeCallback(typeof(FMOD.Studio.EVENT_CALLBACK))]
static FMOD.RESULT PlayFileCallBackUsingAudoiFile(FMOD.Studio.EVENT_CALLBACK_TYPE type, IntPtr instPrt, IntPtr paramsPrt)
{
	FMOD.Studio.EventInstance inst = new FMOD.Studio.EventInstance(instPrt);

	if (!inst.isValid())
	{
		return FMOD.RESULT.ERR_EVENT_NOTFOUND;
	}

	// Retrieving the user data from the instance
	inst.getUserData(out IntPtr clipDataPtr);
	GCHandle clipHandle = GCHandle.FromIntPtr(clipDataPtr);
	// Assinging the our data to a new struct so we can access all the information 
	SoundRequirements clip = clipHandle.Target as SoundRequirements; 

	// Depending on the callback type will decide what happens next 
	switch (type)
	{
		case FMOD.Studio.EVENT_CALLBACK_TYPE.CREATE_PROGRAMMER_SOUND:
			{
				// This is what we will use to pass the sound back out to our instance 
				var param = (FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES)Marshal.PtrToStructure(paramsPrt, typeof(FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES));

				// Retrieve the masterGroup, or the channel group you wish to play the clip too 
				ERRCHECK(FMODUnity.RuntimeManager.CoreSystem.getMasterChannelGroup(out FMOD.ChannelGroup masterGroup), "Failed to get masterGroup from core system");

				// Calculating the length of the audio clip by the samples and channels 
				uint lenBytes = (uint)(clip.samples * clip.channels * sizeof(float));

				// Sound exit info to be used when creating the sound 
				FMOD.CREATESOUNDEXINFO soundInfo = new FMOD.CREATESOUNDEXINFO();
				soundInfo.cbsize = Marshal.SizeOf(typeof(FMOD.CREATESOUNDEXINFO));
				soundInfo.length = lenBytes;
				soundInfo.format = FMOD.SOUND_FORMAT.PCMFLOAT;
				soundInfo.defaultfrequency = clip.defaultFrequence;
				soundInfo.numchannels = clip.channels;

				// Creating the sound using soundInfo
				FMOD.RESULT result = ERRCHECK(FMODUnity.RuntimeManager.CoreSystem.createSound(clip.name, FMOD.MODE.OPENUSER, ref soundInfo, out FMOD.Sound sound), "Failed to create sound");
				if (result != FMOD.RESULT.OK)
					return result; 

				// Now we have created our sound, we need to give it the sample data from the audio clip 
				result = ERRCHECK(sound.@lock(0, lenBytes, out IntPtr ptr1, out IntPtr ptr2, out uint len1, out uint len2), "Failed to lock sound");
				if (result != FMOD.RESULT.OK)
					return result; 

				Marshal.Copy(clip.sampleData, 0, ptr1, (int)(len1 / sizeof(float)));
				if (len2 > 0)
					Marshal.Copy(clip.sampleData, (int)(len1 / sizeof(float)), ptr2, (int)(len2 / sizeof(float)));

				result = ERRCHECK(sound.unlock(ptr1, ptr2, len1, len2), "Failed to unlock sound");
				if (result != FMOD.RESULT.OK)
					return result; 

				ERRCHECK(sound.setMode(FMOD.MODE.DEFAULT), "Failed to set the sound mode");


				if (result == FMOD.RESULT.OK)
				{
					param.sound = sound.handle;
					param.subsoundIndex = -1;
					// Passing the sound back out again to be played 
					Marshal.StructureToPtr(param, paramsPrt, false);
				}
				else
					return result;
			}
			break;
		case FMOD.Studio.EVENT_CALLBACK_TYPE.DESTROY_PROGRAMMER_SOUND:
			{
				var param = (FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES)Marshal.PtrToStructure(paramsPrt, typeof(FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES));
				var sound = new FMOD.Sound(param.sound);
				FMOD.RESULT result = ERRCHECK(sound.release(), "Failed to release sound");
				if (result != FMOD.RESULT.OK)
					return result; 
			}
			break;
		case FMOD.Studio.EVENT_CALLBACK_TYPE.DESTROYED:
			clipHandle.Free();
			break;
	}
	return FMOD.RESULT.OK; 
}

/// <summary>
/// Debug FMOD Function results only in Editor 
/// </summary>
private static FMOD.RESULT ERRCHECK(FMOD.RESULT result, string failMsg)
{
#if UNITY_EDITOR
	if (result != FMOD.RESULT.OK)
	{
		Debug.Log(failMsg + " with result: " + result);
		return result; 
	}
#endif
	return result;
}

After that, you should be able to use an Audio Clip to create a programmer sound. Remember to re-enable Unity Audio. If you have any more questions please let me know.