using System;
using System.Collections.Generic;
using UnityEngine;
using System.Runtime.InteropServices;
using UnityEngine.InputSystem;
// https://qa.fmod.com/t/how-to-create-programmer-sound-from-audioclip/19466/2
public class PlayAudioClipInFmod : MonoBehaviour
{
public AudioClip clip;
//public string event_name_str = "event:/VO/testProgrammerInstrumentVO";
public FMODUnity.EventReference EventName;
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 FMOD.Studio.EVENT_CALLBACK(PlayFileCallBackUsingAudioFile);
if (clip != null)
PlayClipInFmod(clip);
}
public void PlayClipInFmod(AudioClip audioclip)
{
UnityEngine.Debug.Log("PlayClipinFmod " + audioclip.name);
// Get the sample data from the clip and assign in to an array
float[] audioclip_data = new float[audioclip.samples * audioclip.channels];
audioclip.GetData(audioclip_data, 0);
// We no longer require the 'key' parameter as it was used to access the FMOD audio table.
// string key = audioclip.name;
// We are getting the information out of the Unity Audio clip passed into the function
SoundRequirements sound_requirements = new SoundRequirements(
// The name of the clip we are playing, makes it easier to identify when in code
audioclip.name,
// Parameters required to create sound exit info: https://fmod.com/docs/2.02/api/core-api-system.html#fmod_createsoundexinfo
audioclip.samples,
audioclip.channels,
FMOD.SOUND_FORMAT.PCMFLOAT,
audioclip.frequency,
// The sample data that will be copied into the sound when we create it
audioclip_data);
//
// Renamed this for clarity.
var audioClipInstance = FMODUnity.RuntimeManager.CreateInstance(EventName);
// Instead of passing in the key we will pass in the sound requirements that we have created.
GCHandle stringHandle = GCHandle.Alloc(sound_requirements);
audioClipInstance.setUserData(GCHandle.ToIntPtr(stringHandle));
// The callback to make the create the sound and assign it to the instance
audioClipInstance.setCallback(dialogueCallback);
// Play the sound
audioClipInstance.start();
// Release the memory, however if you would like to access parameters or other functions of the instane,
// you don't have to release it now.
audioClipInstance.release();
}
FMOD.Studio.EVENT_CALLBACK dialogueCallback;
#if UNITY_EDITOR
void Reset()
{
EventName = FMODUnity.EventReference.Find("event:/Character/Radio/Command");
}
#endif
[AOT.MonoPInvokeCallback(typeof(FMOD.Studio.EVENT_CALLBACK))]
static FMOD.RESULT PlayFileCallBackUsingAudioFile(FMOD.Studio.EVENT_CALLBACK_TYPE type, IntPtr instPrt, IntPtr paramsPrt)
{
UnityEngine.Debug.Log("PlayFileCallBackUsingAudioFile event type " + type.ToString());
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.defaultFrequency;
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;
}
///
/// Debug FMOD Function results only in Editor
///
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;
}
}
class SoundRequirements
{
public string name;
public int samples;
public int channels;
public FMOD.SOUND_FORMAT format;
public int defaultFrequency;
public float[] sampleData;
///
///
///
///
///
///
///
///
///
public SoundRequirements(string name, int samples, int channel, FMOD.SOUND_FORMAT format, int defaultFrequency, float[] sampleData)
{
this.name = name;
this.samples = samples;
this.channels = channel;
this.format = format;
this.defaultFrequency = defaultFrequency;
this.sampleData = sampleData;
}
}