For example, I can’t see anywhere for the code to know what Audio Table I want to point to; it only has the Key for the specific bit of audio. Is there something I have to add to the event so, when the code looks for the user data, it knows which table to look at? I’ve tried adding the full path ‘bank:/Master Bank/Audio Table/Key’ to see if that works but I’m pretty sure I’m just pulling at straws.
Also, I am a little unsure how the DialogueEventCallback knows which event to look at as it is using IntPtr instancePtr for the name of the event instance to create. However, that isn’t a path to an event. I’m guessing it’s taking the event path in the PlayDialogue method, with dialogueInstance.setCallback, but that’s a set, rather than a get, so I’m guessing that it’s not giving data to the callback.
The last thing I’m on unsure on is that the DialogueEventCallback method doesn’t seem to have a return. I’d imagine that the dialogueInstance.setCallback needs some value to effect our event, but if it doesn’t then the .setUserData must be the important part? If so, how does the key being stored in memory help us change the audio in the programmer sound?
Anyway, I thank you in advance. For most bits of code, I can understand it but this just isn’t clicking for me.
When you load an FMOD Studio bank that has an audio table attached to it, the keys in that audio table are front loaded into your game. There is no need to point to the audio table itself as this is done automatically.
It’s important to have each key be unique if you are loading multiple banks that have audio tables. If there are any keys that are duplicates across audio tables, the most recently loaded bank will take priority.
To understand how DialogueEventCallback works it’s important to realise that the FMOD.Studio.EventInstance objects created in C# are actually wrappers for unamanged objects created by the FMOD studio runtime library. The unmanaged objects do all the work and the C# wrappers store links to the unmanaged objects and allow C# code to perform operations on the unamanged objects.
When PlayDialogue calls FMODUnity.RuntimeManager.CreateInstance a new unmanaged event instance is created by the FMOD studio runtime library and a wrapper for that unmanaged object is returned and stored in dialogueInstance. The remainder of the PlayDialogue function performs a number of operations on the event instance. The wrapper is used to store the audio table key which was passed to PlayDialogue in the event instance’s user data, and uses the setCallback method to tell the FMOD studio runtime library which function to call to handle callbacks for the event instance. Because the audio table key string needs to be kept until the event instance is finished with it we have to pin the memory for the string in PlayDialogue. When the PlayDialogue function returns the dialogueInstance wrapper goes out of scope and can be cleaned up, but the unmanaged event instance sticks around (it will be cleaned up later on by the FMOD studio runtime library, after the event has finished playing).
Later on, the callback is invoked by the FMOD studio runtime library when it needs to create the programmer sound for the event. The instancePtr parameter is a link to the same unmanaged event instance which was created by PlayDialogue. The callback can’t do anything directly with the unmanaged event instance so it creates a new C# wrapper for it. It’s important to understand that this is just a new wrapper, and not a new event instance, so when the callback retrieves the user data from the event instance it will get the audio table key which was stored there in PlayDialogue.
When the callback is called to process the CREATE_PROGRAMMER_SOUND event the extra parameterPtr parameter is used to pass a link to an FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES structure. The callback has to create an unmanaged FMOD sound for the event to play and store it in the structure. This is done in the example by using the audio table key retrieved from the event instance’s user data to lookup the information needed to create the correct sound using FMODUnity.RuntimeManager.LowlevelSystem.createSound. The output FMOD.Sound from FMODUnity.RuntimeManager.LowlevelSystem.createSound is actually a C# wrapper for the unmanaged FMOD sound and we use dialogueSound.getRaw() to retrieve a link to the unmanaged FMOD sound which needs to be returned in the PROGRAMMER_SOUND_PROPERTIES structure. This is the important / interesting output of the callback in this case.
The callback will be called again to cleanup the programmer sound when the event is finished with it. In this case the instancePtr parameter is once again a link to the unmanaged event instance and parameterPtr is a link to an FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES structure which contains a link to an unmanaged FMOD sound which the callback needs to clean up. The callback creates a new C# wrapper for the lowlevel sound and calls release on the wrapper to clean it up.
The callback is called one last time when the event is destroyed. This is the right time to unpin the string memory containing the audio table key which was originally passed to PlayDialogue so that it can be cleaned up by the garbage collector.
I hope this has cleared up how to use programmer sounds.
Thank you so much, Richard! That was a great explanation and I think I understand it now.
Also, I have got my code working! The fault was in the line ‘parameter.subsoundIndex = dialogueSoundInfo.subsoundindex’. For some reason, it thought the line was misspelt and all it took was to type the line in again. I really wish I did that at the beginning but at least I now have a better understanding of how the code works!
The key used in programmer sounds from audio tables are basically alias’ pointing to the actual sound file itself so there isn’t really a way for FMOD to decide which key to use. You need to provide the key in order for FMOD to playback the sound you need.
The Programmer Sound Name is either a key from your audio table or the path to your sound file.
If using the files path, in Unreal Engine 4 this file needs to be within the actual project’s folder (eg. “MyUE4Project\Content\sound_files\gunshot.wav”). The path will then become the file name in relation to the project’s root folder (eg. “sound_files\gunshot.wav”).
Please note two things:
Dragging and dropping the sound files into the Unreal Editor will not work. It needs to be placed into the actual folder structure via Explorer or Finder.
You cannot audition the programmer sound in the Details tab. You can only hear this through the Play in editor.
You can add programmer sounds to a multi sound by right clicking in the playlist and choosing to add Programmer Sound, however you cannot specify the individual programmer sounds through the Editor’s UI. This will need to be scripted. Again, you will need to provide the key for these sounds, but you can script randomization of these.
The subsoundIndex property is used for choosing sound files within containers such as an FSB. If you are not loading an FSB file then don’t worry about this. It will default to -1 and play whatever you passed into the “sound” argument.