Audio Output plugin

Hi,

I am looking at how to create an Audio Output plugin to bypass the default FMOD audio output plugin. I have a custom DSP plugin works with the FMOD studio, but I would also like to control the audio output by my own code. Is it possible? I only found out a MP3 audio out sample plugin which only works with FMOD Ex module, not FMOD studio module. Could anyone please tell me whether this approach works with the current FMOD system?

Thanks,

Cindy

Hi Cindy,
You should be able to use the output_mp3 example as a basis for an FMOD Studio low level plugin, the interface is practically the same.

1 Like

I did use the output_mp3 as an example to test how the audio output plugin works with the FMOD studio framework as it compared to the FMOD Ex framework. I noticed that with the FMOD studio framework, the init and close audio output plugin call back functions are never being called with FMOD studio framework, while both are being called with FMOD Ex framework. In fact, I only see audio output plugin update callback and audio output plugin GetHandle Callback are called with the FMOD studio. Are these correct behavior?

Thanks,

Hi Cindy, it definitely works, just needs a little bit of porting.
This is what I used to register it before init. (you dont need to make a dll, you can use registerOutput inside your code).

unsigned int outputhandle;
result = gSystem->registerOutput(FMODGetOutputDescription(), &outputhandle);
ERRCHECK(result);

Next, the getDriverCaps functions is gone, so remove that

Next, getDriverName is replaced with getDriverInfo so use that instead
ie

FMOD_RESULT F_CALLBACK OutputMP3_GetNumDriversCallback(FMOD_OUTPUT_STATE *output, int *numdrivers);
//FMOD_RESULT F_CALLBACK OutputMP3_GetDriverNameCallback(FMOD_OUTPUT_STATE *output, int id, char *name, int namelen);
FMOD_RESULT F_CALLBACK OutputMP3_GetDriverNameCallback(FMOD_OUTPUT_STATE *output, int id, char *name, int namelen, FMOD_GUID *guid, int *systemrate, FMOD_SPEAKERMODE *speakermode, int *speakermodechannels);
//FMOD_RESULT F_CALLBACK OutputMP3_InitCallback(FMOD_OUTPUT_STATE *output, int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels,     FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, const void *hwnd);

Next, init footprint changed a bit, so

//FMOD_RESULT F_CALLBACK OutputMP3_InitCallback(FMOD_OUTPUT_STATE *output, int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, const void *hwnd)
FMOD_RESULT F_CALLBACK OutputMP3_InitCallback(FMOD_OUTPUT_STATE *output_state, int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, FMOD_SPEAKERMODE *speakermode, int *speakermodechannels, FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, void *extradriverdata)
{
    outputmp3_state *state;
	BE_CONFIG	     beConfig = {0,};
    BE_ERR		     err = 0;
    char            filename[256];

    *speakermode = FMOD_SPEAKERMODE_STEREO;
    *speakermodechannels = 2;

This forces the output mode to the format you want, rather than the error check. If you set 5.1 it should automatically downmix to stereo if the output says it is stereo.

Apart from this, i had to cast a few things to get it to compile, but it worked fine and produced an mp3.

Note that this plugin will crash the program if it requests 1152 samples and the mixer is set to 1024 or any other block size. The app will have to force the dspbuffersize (with System::setDSPBufferSize) to 1152 in this case to get the blocks to line up.
The plugin is supposed to respect whatever the user wants, so the plugin should really be doing some buffering here. I’ll look at adding it and adding the output_mp3 example to the windows plugin_dev example folder for Studio.

1 Like

the 1024/1152 thing is really mp3 specific, but i just worked around it by calling readfrommixer in smaller blocks, ie 1024 first then 128 for example. As I said it will be in an updated example. I also added FMOD_ERR_TOOMANYSAMPLES to be returned to stop it overwriting/crashing in this case.

I managed to get the audio output plugin works in the studio framework. But I noticed that the FMOD_OUTPUT_READFROMMIXER is called every 102ms while in DSP plugin case, the read callback is called every 40ms. The system callback for FMOD_SYSTEM_CALLBACK_PREMIX is also around 40ms. Is this always the case? Is there a way to reduce the 102ms? Does this mean I have to use FMOD_OUTPUT_GETPOSITION_CALLBACK with polling enabled?

Thanks,

Also, do you have any pseudo code on how to use the polling enabled method? The combined use of lock callback, unlock callback and getposition callback.

Thanks,

As it says in the doc, polling = true means you have to use lock/unlock/getposition callbacks.
if polling = false, then you have a thread of your own, or hardware callback, that wants you to provide it with multichannel audio data. You do that via calling readfrommixer.

Your 102ms vs 40ms issue is entirely up to you, if you call readfrommixer with 256 as the parameter, then @48khz, that is 5.333ms.

You could call the mixer with 1 sample at a time if you really wanted to (not recommended!)

To elaborate on polling mode, it means fmod does the mixing when you’ve told it enough data is available. polling means FMOD is calling you, and not polling means you are calling FMOD.

In polling mode, FMOD will rely on getpos/lock/unlock being present or it will not work.
When fmod calls your getPosition callback, it is expecting it to increment until enough data is available for a mix block (ie System::getDSPBufferSize, lets say 1024 by default), and when getPosition callback increments far enough to say 1024 is available, it will immediately call your lock/unlock callbacks.

A polling example is a directsound buffer. You create one, and play it. You then use IDirectSoundBuffer::GetCurrentPosition to determine where the directsound playhead is, then when the 1024 delta happens like I mentioned earlier, it will call lock to grab a writeable pointer so it can mix to it, which you return using IDirectSoundBuffer::lock, and then when fmod is finished mixing to that pointer, it will call your unlock callback, where you’d call IDirectSoundBuffer::unlock.

Thanks for your help, the audio out plugin works now with the low level FMOD studio APIs. But it seems there is no way to let this audio out plugin dll to be recognized with the studio directly. I see my customized DSP plugin can be recognized with the studio under Insert Effect->Plug-in Effects. Does the studio tool allow a customized audio out plugin?

Thanks,

Hi Cindy,
FMOD Studio does not attempt to load output plugins, though that is an interesting idea. It is a runtime only thing. If you were using studio it might make more sense to use live connect to an app using the new output mode.

1 Like

More questions here: The audio out plugin that works on the window platform does not work on a PS4 platform. The DSP read callback function *outbuffer is always 0 on PS4 while it has value on Windows. Any idea why it is a NULL pointer on PS4? It seems the readfrommixer() from the audio out plugin triggers the read callback from the DSP plugin.

Thanks,

I can’t see any obvious difference between PS4 and PC. Are you setting all the out arguments of Init()?

Did you mean the InitCallback function for the audio out plugin? The following is set in the code:

output_state->plugindata = state; // has memory allocated in the code.
*outputformat = FMOD_SOUND_FORMAT_PCMFLOAT;
*speakermode = FMOD_SPEAKERMODE_MONO;
*speakermodechannels = 1;

Did I miss anything?

Thanks,