Hi.
I’ve been working on this custom DSP, that takes two .wav files in. Then we process the data to play it as a granular synthesizer for vehicle engine sound.
Everything is right until we use multiple instances of this particular event in fmod studio. In our research project we have two types of vehicle engines with two different sound characteristics, they sound totally different.
Now let’s talk about the problem. We define two instance of one event with these two different engine sounds. The data in two different vehicles are played from only one of these event instances. To elaborate more, let’s say Engine 1 is at
5k rpm and Engine 2 is at 6k. In Unreal engine, when we are using these two instances, Engine 1 is muted or is too far away from listener to be heard, and we only have Engine 2 near the listener. We can hear Engine 1 samples playing at 5k rpm.
even if we change the rpm for Engine 2 a few milliseconds we can hear Engine 2 properly but then Engine 1 is going to be replaced by the Engine 2.
I recreated the issue with the studio. I defined two instances of this dsp in one event but in different tracks. Set my files accordingly, muted one of the tracks and played the other. Started to play around with the rpm knob. Same result. I change the rpm on one track and some samples of the other track at its track specified rpm are being played.
How I defined my dsp?
We have two files one for accelerating the other for decelerating, then rpm as controlling input. We take these data as .wav file input. We handle .wav header by casting data to header structure:
function declration
void EngineSynth::DataHandler(void* data, WaveFileStructure*& OutInputFile, float*& OutStoredData)
in function definition
OutInputFile = (WaveFileStructure*)data;
This struct only have the header portion of the .wav file, right up until the very first audio sample data.
Then I iterate through the data portion and convert it to an array of float to work within -1 and 1 range:
if (OutInputFile->BitsPerSample == 16)
{
data16 = new int16_t[DataLength / 2];
data16 = (int16_t*)TempCharData;
for (int i = 0; i < DataLength / 2; i++)
{
OutStoredData[i] = (float)data16[i] / 32768.f;
}
}
The OutStoredData[i]
is my Accelerating/Decelerating float array defined in a class
private:
WaveFileStructure* AccelInputFile;
WaveFileStructure* DecelInputFile;
float* AccelData;
void* AccelRawVoid;
float* DecelData;
void* DecelRawVoid;
float EngineRPM;
And all the relevant F_Callbacks are:
FMOD_RESULT F_CALLBACK EngineSynthCreateDSP(FMOD_DSP_STATE* dsp)
{
dsp->plugindata = (EngineSynth*)FMOD_DSP_ALLOC(dsp, sizeof(EngineSynth));
if (!dsp->plugindata)
{
return FMOD_ERR_MEMORY;
}
return FMOD_OK;
}
FMOD_RESULT F_CALLBACK EngineSynthReleaseDSP(FMOD_DSP_STATE* dsp)
{
EngineSynth* state = (EngineSynth*)dsp->plugindata;
FMOD_DSP_FREE(dsp, state);
return FMOD_OK;
}
#ifdef EngineSynth_USEPROCESSCALLBACK
FMOD_RESULT F_CALLBACK EngineSynthDSP_Process(FMOD_DSP_STATE* dsp, unsigned int length, const FMOD_DSP_BUFFER_ARRAY* inbufferarray, FMOD_DSP_BUFFER_ARRAY* outbufferarray, FMOD_BOOL /*inputsidle*/, FMOD_DSP_PROCESS_OPERATION op)
{
EngineSynth* state = (EngineSynth*)dsp->plugindata;
if (op == FMOD_DSP_PROCESS_QUERY)
{
if (outbufferarray)
{
outbufferarray->buffernumchannels[0] = 1;
}
return FMOD_OK;
}
state->GenerateEngineSound(outbufferarray->buffers[0], length, inbufferarray->buffernumchannels[0], dsp);
return FMOD_OK;
}
#endif
FMOD_RESULT F_CALLBACK EngineSynthSetFloat(FMOD_DSP_STATE* dsp, int index, float value)
{
EngineSynth* state = (EngineSynth*)dsp->plugindata;
switch (index)
{
case EngineSynth_RPM:
state->SetRPM(value);
if (state->GetAccelInputFile()&& state ->GetDecelData())
{
AccelGran.UpdateQueueGrains(value, state->GetAccelData(), (state->GetAccelInputFile())->SubChunk2Size / 2);
DecelGran.UpdateQueueGrains(1 - value, state->GetDecelData(), (state->GetDecelInputFile())->SubChunk2Size / 2);
}
return FMOD_OK;
}
return FMOD_ERR_INVALID_PARAM;
}
FMOD_RESULT F_CALLBACK EngineSynthGetFloat(FMOD_DSP_STATE* dsp, int index, float* value, char* valuestr)
{
EngineSynth* state = (EngineSynth*)dsp->plugindata;
switch(index)
{
case EngineSynth_RPM:
*value = state->GetRPM();
if (valuestr) snprintf(valuestr, FMOD_DSP_GETPARAM_VALUESTR_LENGTH, "RPM ", state->GetRPM());
return FMOD_OK;
}
return FMOD_ERR_INVALID_PARAM;
}
FMOD_RESULT F_CALLBACK EngineSynthSetData(FMOD_DSP_STATE* dsp, int index, void* data, unsigned int length)
{
EngineSynth* state = (EngineSynth*)dsp->plugindata;
switch (index)
{
case EngineSynth_AccelFile:
{
state->SetAccelData(data);
return FMOD_OK;
}
case EngineSynth_DecelFile:
{
state->SetDecelData(data);
return FMOD_OK;
}
}
return FMOD_ERR_INVALID_PARAM;
}
FMOD_RESULT F_CALLBACK EngineSynthGetData(FMOD_DSP_STATE* dsp, int index, void** value, unsigned int* length, char* valuestr)
{
EngineSynth* state = (EngineSynth*)dsp->plugindata;
switch (index)
{
case EngineSynth_AccelFile:
*value = state->GetAccelData();
if (valuestr) snprintf(valuestr, FMOD_DSP_GETPARAM_VALUESTR_LENGTH, "Accel Data");
return FMOD_OK;
case EngineSynth_DecelFile:
*value = state->GetDecelData();
if (valuestr) snprintf(valuestr, FMOD_DSP_GETPARAM_VALUESTR_LENGTH, "Decel Data");
return FMOD_OK;
}
return FMOD_ERR_INVALID_PARAM;
}
I hope that all the necessary codes are posted.