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.