Adding effects to real-time recorded audio

Hello,

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.

Thanks.

Hi,

This can certainly be done using a Programmer Instrument. We have a scripting example: Unity Integration | Scripting Examples - Programmer Sound which outlines using Audio Tables with a programmer sound. However, since you already have a sound, rather than using a table or a file you’d just pass the sound to the programmer instrument.

Here is the updated switch statement:

Switch
{
    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;
}

But you may wish to do this another way.

Hope this helps!

Hello,

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);
}

In FMOD Editor what length should the sound be?
Screenshot_1919

I found the answer to my question in this topic

1 Like

Hi,

Thanks for sharing the solution.

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?

1 Like

Hi,

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:

Hope this helps!

1 Like

Thank you! Everything worked out.

1 Like