I’m making a voice chat for a game and I need the ability to apply various effects to the sound that I receive from the microphone depending on the value of some parameters, for example, to apply an “underwater” effect (may be snapshot) to the sound from the microphone when the player is underwater, is it possible to do this? Currently I have a sound structure and samples from a microphone.
{
case FMOD.Studio.EVENT_CALLBACK_TYPE.CREATE_PROGRAMMER_SOUND:
{
// We can remove all this since we already have the sound
//FMOD.MODE soundMode = FMOD.MODE.LOOP_NORMAL | FMOD.MODE.CREATECOMPRESSEDSAMPLE | FMOD.MODE.NONBLOCKING;
// We need to keep this
var parameter = (FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES)Marshal.PtrToStructure(parameterPtr, typeof(FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES));
// Remove this
// if (key.Contains("."))
// {
// FMOD.Sound dialogueSound;
// var soundResult = FMODUnity.RuntimeManager.CoreSystem.createSound(Application.streamingAssetsPath + "/" + key, soundMode, out dialogueSound);
// if (soundResult == FMOD.RESULT.OK)
// {
// parameter.sound = dialogueSound.handle;
// parameter.subsoundIndex = -1;
// Marshal.StructureToPtr(parameter, parameterPtr, false);
// }
// }
// else
// {
// FMOD.Studio.SOUND_INFO dialogueSoundInfo;
// var keyResult = FMODUnity.RuntimeManager.StudioSystem.getSoundInfo(key, out dialogueSoundInfo);
// if (keyResult != FMOD.RESULT.OK)
// {
// break;
// }
// FMOD.Sound dialogueSound;
// var soundResult = FMODUnity.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);
// }
// }
// We now need to pass the sound to the programmer instrument
parameter.sound = dialogueSound.handle;
parameter.subsoundIndex = dialogueSoundInfo.subsoundindex;
Marshal.StructureToPtr(parameter, parameterPtr, false);
// Thats it!
break;
}
case FMOD.Studio.EVENT_CALLBACK_TYPE.DESTROY_PROGRAMMER_SOUND:
{
var parameter = (FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES)Marshal.PtrToStructure(parameterPtr, typeof(FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES));
var sound = new FMOD.Sound(parameter.sound);
sound.release();
break;
}
case FMOD.Studio.EVENT_CALLBACK_TYPE.DESTROYED:
{
// Now the event has been destroyed, unpin the string memory so it can be garbage collected
stringHandle.Free();
break;
}
}
Remember to pass in the sound as the event instances User Data . To do this I create a simple class to hold the sound for me:
public class UserDataClass
{
public FMOD.Sound sound;
}
I used the code from the example and your implementation of the switch, after running the PlaySound method I hear the last second of my speech spoken into the microphone, when I launch PlaySound again I get a warning below
[FMOD] SoundSourceInstrumentInstance::startChannelIfReady : Resource type 1 for instrument {a7c321b6-eaed-4cba-8921-66d6cbc1d093} has returned 30 and will now stop.
I don’t quite understand how to work with a programmer instrument and a sound that is overwritten every moment: should I run the PlaySound method every 1 second (for example) or can I link the sound to the programmer instrument and it will update automatically?
This is what I have at the moment.
_soundHolder is a variable of a class that contains Sound
[ContextMenu("Play Sound")]
private void PlaySound()
{
var dialogueInstance = RuntimeManager.CreateInstance(_eventReference);
GCHandle soundHolderHandle = GCHandle.Alloc(_soundHolder);
dialogueInstance.setUserData(GCHandle.ToIntPtr(soundHolderHandle));
dialogueInstance.setCallback(dialogueCallback);
dialogueInstance.start();
dialogueInstance.release();
}
[AOT.MonoPInvokeCallback(typeof(FMOD.Studio.EVENT_CALLBACK))]
private static RESULT DialogueEventCallback(FMOD.Studio.EVENT_CALLBACK_TYPE type, IntPtr instancePtr, IntPtr parameterPtr)
{
FMOD.Studio.EventInstance instance = new FMOD.Studio.EventInstance(instancePtr);
instance.getUserData(out IntPtr stringPtr);
GCHandle stringHandle = GCHandle.FromIntPtr(stringPtr);
IFMODSoundHolder soundHolder = stringHandle.Target as IFMODSoundHolder;
UnityEngine.Debug.Log(type);
switch (type)
{
case FMOD.Studio.EVENT_CALLBACK_TYPE.CREATE_PROGRAMMER_SOUND:
{
var parameter = (FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES)Marshal.PtrToStructure(parameterPtr, typeof(FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES));
parameter.sound = soundHolder.Sound.handle;
Marshal.StructureToPtr(parameter, parameterPtr, false);
break;
}
case FMOD.Studio.EVENT_CALLBACK_TYPE.DESTROY_PROGRAMMER_SOUND:
{
var parameter = (FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES)Marshal.PtrToStructure(parameterPtr, typeof(FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES));
var sound = new Sound(parameter.sound);
sound.release();
break;
}
case FMOD.Studio.EVENT_CALLBACK_TYPE.DESTROYED:
{
stringHandle.Free();
break;
}
}
return RESULT.OK;
}
private void Start()
{
dialogueCallback = new FMOD.Studio.EVENT_CALLBACK(DialogueEventCallback);
}
One more question: how to correctly change snapshots depending on a particular situation? As far as I understand, I cannot make a loop region in the parameter sheet, so this method is immediately discarded, is it worth writing my own implementation or are there other methods?
An option could be adding a looping region to the Timeline sheet which will keep the event playing and then having your snapshots on a parameter sheet: