0
0

Hello,

recently I had to perform FMOD::Channel RMS measurment which i’ve done by implementing custom DSP. In the code I allocate array (heap) which size is equal to numer of sound source channels – this array is used to store calculated RMS per channel. This array is passed to DSP object with FMOD::DSP::setUserData(). Assumption is made that when DSP read callback is fired the inchannels and outchannels numer will be equal to numer of sound source channels. Unfortunatelly it happends that for stereo sound the DSP read callback is fired with inCH/outCH numer equal to 8 (which is my default output mode), which in turn causes heap corruption.

My DSP network is rather simple.

[attachment=0:2dw9cja3]dsp_net_01.png[/attachment:2dw9cja3]

What can be the cause of such behaviour?

Best regards,
Lucas.

  • You must to post comments
0
0

Hi Lucas,
That graph is showing that the in/out channels is equal to 2? I can see 2 VU bars in that graph, and over that graph. The picture is small but i assume after your DSP is channel head, then that gets mixed to the channelgroup and upgraded to 8 as expected.

If you use ChannelGroup or System addDSP then a mix could upgrade the signal to 8, but if you use Channel::addDSP it will be 1 or 2 if the sound is mono or stereo.

  • You must to post comments
0
0

Hi Brett,

I have double checked my code and I’m sure that DSP read callback with 8 inCh is called for stereo sound.

I create FMOD::DSP instance like this:

[code:2bw9s6rf]
FMOD_DSP_DESCRIPTION dspDesc;
memset(&dspDesc, 0, sizeof(FMOD_DSP_DESCRIPTION));

strcpy_s(dspDesc.name, 32, "VolumeMeter");
dspDesc.create = volumeMeterDSP_Create_Callback;
dspDesc.release = volumeMeterDSP_Release_Callback;
dspDesc.reset = volumeMeterDSP_Reset_Callback;
dspDesc.read = volumeMeterDSP_Process_Callback;
dspDesc.channels = 0;
dspDesc.userdata = 0;

// This is done for each FMOD::Channel independantly
FMOD::DSP* pFVolumeMeterDSP;
pFSystem->createDSP(&dspDesc, &pFVolumeMeterDSP);

FMOD::Sound* pFSound = NULL;

// Get the FMOD::Sound the FMOD::Channel was assigned with.
pFChannel->GetCurrentSound(&pFSound);

int channelsNumber;
pFSound->getFormat(0, 0, &channelsNumber, 0); ///< In my case this is stero sound so channels number is 2

// Here’s my array
float rmsArray = new float[channelsNumber];
memset((void
)rmsArray, 0, sizeof(float)*channelsNumber);

// Pass the array as userdata
pFVolumeMeterDSP->setUserData((void*)rmsArray);

pFChannel->addDSP(pFVolumeMeterDSP, NULL);

pFVolumeMeterDSP->setActive(true);
[/code:2bw9s6rf]

Now the following callback is fired:

[code:2bw9s6rf]

FMOD_RESULT F_CALLBACK volumeMeterDSP_Process_Callback(FMOD_DSP_STATE* dsp_state,
float* inbuffer, float* outbuffer, unsigned int length,
int inchannels, int outchannels)
{
// Pass through, assumes we know what we are doing
memcpy(outbuffer, inbuffer, length * sizeof(float) * inchannels);

//////////////////////////////////////////////////////////////////////////
// Removed RMS calculation code.
//////////////////////////////////////////////////////////////////////////

FMOD::DSP *pFDSPInstance = (FMOD::DSP *)dsp_state-&gt;instance;

// This is the same instance we have created for stereo sound.
// But inchannels is 8!
if(pFDSPInstance)
{
    float* rmsArray = NULL;
    pFDSPInstance-&gt;getUserData((void**)&amp;rmsArray);

    for (int inChIdx = 0; inChIdx &lt; inchannels; ++inChIdx)
    {
        // Oh crap, heap is corrupted after inChIdx is &gt; 1
        rmsArray[inChIdx] = some_calculated_value;
    }
}

}
[/code:2bw9s6rf]

What can possibly go wrong?

Best regards,
Lucas.

  • You must to post comments
0
0

Ok, I took your code, and applied it to the playsound example in the examples.

Here is the code

[code:as3v3sn5]
/*===============================================================================================
PlaySound Example
Copyright (c), Firelight Technologies Pty, Ltd 2004-2011.

This example shows how to simply load and play multiple sounds. This is about the simplest
use of FMOD.
This makes FMOD decode the into memory when it loads. If the sounds are big and possibly take
up a lot of ram, then it would be better to use the FMOD_CREATESTREAM flag so that it is
streamed in realtime as it plays.
===============================================================================================*/

include <windows.h>

include <stdio.h>

include <conio.h>

include "../../api/inc/fmod.hpp"

include "../../api/inc/fmod_errors.h"

void ERRCHECK(FMOD_RESULT result)
{
if (result != FMOD_OK)
{
printf("FMOD error! (%d) %s\n", result, FMOD_ErrorString(result));
exit(-1);
}
}

FMOD_RESULT F_CALLBACK volumeMeterDSP_Process_Callback(FMOD_DSP_STATE* dsp_state,
float* inbuffer, float* outbuffer, unsigned int length,
int inchannels, int outchannels)
{
// Pass through, assumes we know what we are doing
memcpy(outbuffer, inbuffer, length * sizeof(float) * inchannels);

printf(&quot;\nchannels %d\n&quot;, inchannels);

return FMOD_OK;

}
int main(int argc, char *argv[])
{
FMOD::System *system;
FMOD::Sound *sound1, *sound2, *sound3;
FMOD::Channel *channel = 0;
FMOD_RESULT result;
int key;
unsigned int version;

/*
    Create a System object and initialize.
*/
result = FMOD::System_Create(&amp;system);
ERRCHECK(result);

result = system-&gt;getVersion(&amp;version);
ERRCHECK(result);

if (version &lt; FMOD_VERSION)
{
    printf(&quot;Error!  You are using an old version of FMOD %08x.  This program requires %08x\n&quot;, version, FMOD_VERSION);
    return 0;
}

result = system-&gt;init(32, FMOD_INIT_NORMAL, 0);
ERRCHECK(result);

result = system-&gt;createSound(&quot;../media/drumloop.wav&quot;, FMOD_HARDWARE, 0, &amp;sound1);
ERRCHECK(result);

result = sound1-&gt;setMode(FMOD_LOOP_OFF);    /* drumloop.wav has embedded loop points which automatically makes looping turn on, */
ERRCHECK(result);                           /* so turn it off here.  We could have also just put FMOD_LOOP_OFF in the above CreateSound call. */

result = system-&gt;createSound(&quot;../media/jaguar.wav&quot;, FMOD_SOFTWARE, 0, &amp;sound2);
ERRCHECK(result);

result = system-&gt;createSound(&quot;../media/swish.wav&quot;, FMOD_HARDWARE, 0, &amp;sound3);
ERRCHECK(result);

printf(&quot;===================================================================\n&quot;);
printf(&quot;PlaySound Example.  Copyright (c) Firelight Technologies 2004-2011.\n&quot;);
printf(&quot;===================================================================\n&quot;);
printf(&quot;\n&quot;);
printf(&quot;Press '1' to play a mono sound using hardware mixing\n&quot;);
printf(&quot;Press '2' to play a mono sound using software mixing\n&quot;);
printf(&quot;Press '3' to play a stereo sound using hardware mixing\n&quot;);
printf(&quot;Press 'Esc' to quit\n&quot;);
printf(&quot;\n&quot;);

/*
    Main loop.
*/
do
{
    if (_kbhit())
    {
        key = _getch();

        switch (key)
        {
            case '1' :
            {
                result = system-&gt;playSound(FMOD_CHANNEL_FREE, sound1, false, &amp;channel);
                ERRCHECK(result);
                break;
            }
            case '2' :
            {
                result = system-&gt;playSound(FMOD_CHANNEL_FREE, sound2, false, &amp;channel);
                ERRCHECK(result);
                break;
            }
            case '3' :
            {
                result = system-&gt;playSound(FMOD_CHANNEL_FREE, sound3, false, &amp;channel);
                ERRCHECK(result);

                FMOD_DSP_DESCRIPTION dspDesc;
                memset(&amp;dspDesc, 0, sizeof(FMOD_DSP_DESCRIPTION));

                strcpy_s(dspDesc.name, 32, &quot;VolumeMeter&quot;);
                dspDesc.create = 0; //volumeMeterDSP_Create_Callback;
                dspDesc.release = 0; //volumeMeterDSP_Release_Callback;
                dspDesc.reset = 0; //volumeMeterDSP_Reset_Callback;
                dspDesc.read = volumeMeterDSP_Process_Callback;
                dspDesc.channels = 0;
                dspDesc.userdata = 0;

                // This is done for each FMOD::Channel independantly
                FMOD::DSP* pFVolumeMeterDSP;
                system-&gt;createDSP(&amp;dspDesc, &amp;pFVolumeMeterDSP);

                channel-&gt;addDSP(pFVolumeMeterDSP, 0);
                break;
            }
        }
    }

    system-&gt;update();

    {
        unsigned int ms = 0;
        unsigned int lenms = 0;
        bool         playing = 0;
        bool         paused = 0;
        int          channelsplaying = 0;

        if (channel)
        {
            FMOD::Sound *currentsound = 0;

            result = channel-&gt;isPlaying(&amp;playing);
            if ((result != FMOD_OK) &amp;&amp; (result != FMOD_ERR_INVALID_HANDLE) &amp;&amp; (result != FMOD_ERR_CHANNEL_STOLEN))
            {
                ERRCHECK(result);
            }

            result = channel-&gt;getPaused(&amp;paused);
            if ((result != FMOD_OK) &amp;&amp; (result != FMOD_ERR_INVALID_HANDLE) &amp;&amp; (result != FMOD_ERR_CHANNEL_STOLEN))
            {
                ERRCHECK(result);
            }

            result = channel-&gt;getPosition(&amp;ms, FMOD_TIMEUNIT_MS);
            if ((result != FMOD_OK) &amp;&amp; (result != FMOD_ERR_INVALID_HANDLE) &amp;&amp; (result != FMOD_ERR_CHANNEL_STOLEN))
            {
                ERRCHECK(result);
            }

            channel-&gt;getCurrentSound(&amp;currentsound);
            if (currentsound)
            {
                result = currentsound-&gt;getLength(&amp;lenms, FMOD_TIMEUNIT_MS);
                if ((result != FMOD_OK) &amp;&amp; (result != FMOD_ERR_INVALID_HANDLE) &amp;&amp; (result != FMOD_ERR_CHANNEL_STOLEN))
                {
                    ERRCHECK(result);
                }
            }
        }

        system-&gt;getChannelsPlaying(&amp;channelsplaying);

        printf(&quot;Time %02d:%02d:%02d/%02d:%02d:%02d : %s : Channels Playing %2d\r&quot;, ms / 1000 / 60, ms / 1000 % 60, ms / 10 % 100, lenms / 1000 / 60, lenms / 1000 % 60, lenms / 10 % 100, paused ? &quot;Paused &quot; : playing ? &quot;Playing&quot; : &quot;Stopped&quot;, channelsplaying);
    }

    Sleep(10);

} while (key != 27);

printf(&quot;\n&quot;);

/*
    Shut down
*/
result = sound1-&gt;release();
ERRCHECK(result);
result = sound2-&gt;release();
ERRCHECK(result);
result = sound3-&gt;release();
ERRCHECK(result);
result = system-&gt;close();
ERRCHECK(result);
result = system-&gt;release();
ERRCHECK(result);

return 0;

}
[/code:as3v3sn5]

Feel free to try it yourself, it prints out inchannels = 2.
I see no reason for it to not give 2, unless before the metering dsp, an upmix happened, or you put it on the wrong object (like system or channelgroup).
Your own profiler screenshot is showing 2 as well, which is the confusing part. Is there some stack corruption going on?

  • You must to post comments
0
0

Hey Brett,

I have successfully triggerd this issue in the example code you provided. However several following conditions must be met:

  • Set speaker configuration in your sound driver control panel to 7.1
  • I have also set bit depth to 24 and sampling frequency to 48kHz if it matters somehow.

The sample code is slightly modified. It checks driver caps and sets speakermode and output settings according to caps returned for the default system sound device (i.e. zero index).
Also I have changed sleep time for the main thread from 10ms (as original) to 50ms as in my case (This is crucial).

Here is the code:

[code:22rcalzv]
/*===============================================================================================
PlaySound Example
Copyright (c), Firelight Technologies Pty, Ltd 2004-2011.

This example shows how to simply load and play multiple sounds. This is about the simplest
use of FMOD.
This makes FMOD decode the into memory when it loads. If the sounds are big and possibly take
up a lot of ram, then it would be better to use the FMOD_CREATESTREAM flag so that it is
streamed in realtime as it plays.
===============================================================================================*/

include <windows.h>

include <stdio.h>

include <conio.h>

include "../../api/inc/fmod.hpp"

include "../../api/inc/fmod_errors.h"

void ERRCHECK(FMOD_RESULT result)
{
if (result != FMOD_OK)
{
printf("FMOD error! (%d) %s\n", result, FMOD_ErrorString(result));
exit(-1);
}
}

FMOD_RESULT F_CALLBACK volumeMeterDSP_Process_Callback(FMOD_DSP_STATE* dsp_state,
float* inbuffer, float* outbuffer, unsigned int length,
int inchannels, int outchannels)
{
// Pass through, assumes we know what we are doing
memcpy(outbuffer, inbuffer, length * sizeof(float) * inchannels);

printf(&quot;\nchannels %d\n&quot;, inchannels);

return FMOD_OK;

}

define DB_2_AMP(x) powf(10.0f,x/20.0f)

define FLSET(value, flag) (value |= flag)

define FLREM(value, flag) (value &= ~(flag))

int main(int argc, char *argv[])
{
FMOD::System *system;
FMOD::Sound *sound1, *sound2, *sound3;
FMOD::Channel *channel = 0;
FMOD_RESULT result;
int key;
unsigned int version;

/*
    Create a System object and initialize.
*/
result = FMOD::System_Create(&amp;system);
ERRCHECK(result);

result = system-&gt;getVersion(&amp;version);
ERRCHECK(result);

if (version &lt; FMOD_VERSION)
{
    printf(&quot;Error!  You are using an old version of FMOD %08x.  This program requires %08x\n&quot;, version, FMOD_VERSION);
    return 0;
}

int selectedDriver = 0;

char driver_name[512];
result = system-&gt;getDriverInfo(selectedDriver, driver_name, sizeof(driver_name), 0);
ERRCHECK(result);

FMOD_CAPS fmodCaps;
int outputRate = 0;
FMOD_SPEAKERMODE fmodSpeakerMode;
system-&gt;getDriverCaps(selectedDriver, &amp;fmodCaps, &amp;outputRate, &amp;fmodSpeakerMode);
ERRCHECK(result);

printf(&quot;Driver name: &lt;%s&gt; SpeakerMode: &lt;%d&gt; OutputRate: &lt;%d&gt;\n&quot;, driver_name, fmodSpeakerMode, outputRate);

// fmodSpeakerMode should be FMOD_SPEAKERMODE_7POINT1 (i.e. 6)
result = system-&gt;setSpeakerMode(fmodSpeakerMode);
ERRCHECK(result);

result = system-&gt;init(32, FMOD_INIT_NORMAL, 0);
ERRCHECK(result);

result = system-&gt;createSound(&quot;../media/drumloop.wav&quot;, FMOD_SOFTWARE /*| FMOD_3D */, 0, &amp;sound1);
ERRCHECK(result);

result = sound1-&gt;setMode(FMOD_LOOP_OFF);    /* drumloop.wav has embedded loop points which automatically makes looping turn on, */
ERRCHECK(result);                           /* so turn it off here.  We could have also just put FMOD_LOOP_OFF in the above CreateSound call. */

result = system-&gt;createSound(&quot;../media/jaguar.wav&quot;, FMOD_SOFTWARE/* | FMOD_3D*/, 0, &amp;sound2);
ERRCHECK(result);

result = system-&gt;createSound(&quot;../media/highlight.wav&quot;, FMOD_SOFTWARE /*| FMOD_3D*/ , 0, &amp;sound3);
ERRCHECK(result);

printf(&quot;===================================================================\n&quot;);
printf(&quot;PlaySound Example.  Copyright (c) Firelight Technologies 2004-2011.\n&quot;);
printf(&quot;===================================================================\n&quot;);
printf(&quot;\n&quot;);
printf(&quot;Press '1' to play a mono sound using hardware mixing\n&quot;);
printf(&quot;Press '2' to play a mono sound using software mixing\n&quot;);
printf(&quot;Press '3' to play a stereo sound using hardware mixing\n&quot;);
printf(&quot;Press 'Esc' to quit\n&quot;);
printf(&quot;\n&quot;);

/*
    Main loop.
*/
do
{
    if (_kbhit())
    {
        key = _getch();

        switch (key)
        {
            case '1' :
            {
                result = system-&gt;playSound(FMOD_CHANNEL_FREE, sound1, false, &amp;channel);
                ERRCHECK(result);

                FMOD_DSP_DESCRIPTION dspDesc;
                memset(&amp;dspDesc, 0, sizeof(FMOD_DSP_DESCRIPTION));

                strcpy_s(dspDesc.name, 32, &quot;VolumeMeter&quot;);
                dspDesc.create = 0; //volumeMeterDSP_Create_Callback;
                dspDesc.release = 0; //volumeMeterDSP_Release_Callback;
                dspDesc.reset = 0; //volumeMeterDSP_Reset_Callback;
                dspDesc.read = volumeMeterDSP_Process_Callback;
                dspDesc.channels = 0;
                dspDesc.userdata = 0;

                // This is done for each FMOD::Channel independantly
                FMOD::DSP* pFVolumeMeterDSP;
                system-&gt;createDSP(&amp;dspDesc, &amp;pFVolumeMeterDSP);

                channel-&gt;addDSP(pFVolumeMeterDSP, 0);

                channel-&gt;setPaused(false);

                break;
            }
            case '2' :
            {
                result = system-&gt;playSound(FMOD_CHANNEL_FREE, sound2, false, &amp;channel);
                ERRCHECK(result);

                FMOD_DSP_DESCRIPTION dspDesc;
                memset(&amp;dspDesc, 0, sizeof(FMOD_DSP_DESCRIPTION));

                strcpy_s(dspDesc.name, 32, &quot;VolumeMeter&quot;);
                dspDesc.create = 0; //volumeMeterDSP_Create_Callback;
                dspDesc.release = 0; //volumeMeterDSP_Release_Callback;
                dspDesc.reset = 0; //volumeMeterDSP_Reset_Callback;
                dspDesc.read = volumeMeterDSP_Process_Callback;
                dspDesc.channels = 0;
                dspDesc.userdata = 0;

                // This is done for each FMOD::Channel independantly
                FMOD::DSP* pFVolumeMeterDSP;
                system-&gt;createDSP(&amp;dspDesc, &amp;pFVolumeMeterDSP);

                channel-&gt;addDSP(pFVolumeMeterDSP, 0);

                channel-&gt;setPaused(false);

                break;
            }
            case '3' :
            {
                result = system-&gt;playSound(FMOD_CHANNEL_FREE, sound3, true, &amp;channel);
                ERRCHECK(result);

                FMOD_DSP_DESCRIPTION dspDesc;
                memset(&amp;dspDesc, 0, sizeof(FMOD_DSP_DESCRIPTION));

                strcpy_s(dspDesc.name, 32, &quot;VolumeMeter&quot;);
                dspDesc.create = 0; //volumeMeterDSP_Create_Callback;
                dspDesc.release = 0; //volumeMeterDSP_Release_Callback;
                dspDesc.reset = 0; //volumeMeterDSP_Reset_Callback;
                dspDesc.read = volumeMeterDSP_Process_Callback;
                dspDesc.channels = 0;
                dspDesc.userdata = 0;

                // This is done for each FMOD::Channel independantly
                FMOD::DSP* pFVolumeMeterDSP;
                system-&gt;createDSP(&amp;dspDesc, &amp;pFVolumeMeterDSP);

                channel-&gt;addDSP(pFVolumeMeterDSP, 0);

                channel-&gt;setPaused(false);

                break;
            }
        }
    }

    system-&gt;update();

    {
        unsigned int ms = 0;
        unsigned int lenms = 0;
        bool         playing = 0;
        bool         paused = 0;

        if (channel)
        {
            FMOD::Sound *currentsound = 0;

            result = channel-&gt;setVolume(0.5f);
            if ((result != FMOD_OK) &amp;&amp; (result != FMOD_ERR_INVALID_HANDLE) &amp;&amp; (result != FMOD_ERR_CHANNEL_STOLEN))
            {
                ERRCHECK(result);
            }

            result = channel-&gt;isPlaying(&amp;playing);
            if ((result != FMOD_OK) &amp;&amp; (result != FMOD_ERR_INVALID_HANDLE) &amp;&amp; (result != FMOD_ERR_CHANNEL_STOLEN))
            {
                ERRCHECK(result);
            }

            result = channel-&gt;getPaused(&amp;paused);
            if ((result != FMOD_OK) &amp;&amp; (result != FMOD_ERR_INVALID_HANDLE) &amp;&amp; (result != FMOD_ERR_CHANNEL_STOLEN))
            {
                ERRCHECK(result);
            }

            result = channel-&gt;getPosition(&amp;ms, FMOD_TIMEUNIT_MS);
            if ((result != FMOD_OK) &amp;&amp; (result != FMOD_ERR_INVALID_HANDLE) &amp;&amp; (result != FMOD_ERR_CHANNEL_STOLEN))
            {
                ERRCHECK(result);
            }

            channel-&gt;getCurrentSound(&amp;currentsound);
            if (currentsound)
            {
                result = currentsound-&gt;getLength(&amp;lenms, FMOD_TIMEUNIT_MS);
                if ((result != FMOD_OK) &amp;&amp; (result != FMOD_ERR_INVALID_HANDLE) &amp;&amp; (result != FMOD_ERR_CHANNEL_STOLEN))
                {
                    ERRCHECK(result);
                }
            }

            printf(&quot;Time %02d:%02d:%02d/%02d:%02d:%02d : %s\r&quot;, ms / 1000 / 60, ms / 1000 % 60, ms / 10 % 100, lenms / 1000 / 60, lenms / 1000 % 60, lenms / 10 % 100, paused ? &quot;Paused &quot; : playing ? &quot;Playing&quot; : &quot;Stopped&quot;);
        }
    }

    Sleep(50); // Thats my thread update rate, change it to sample default (i.e. 10ms) and the issue goes away

} while (key != 27);

printf(&quot;\n&quot;);

/*
    Shut down
*/
result = sound1-&gt;release();
ERRCHECK(result);
result = sound2-&gt;release();
ERRCHECK(result);
result = sound3-&gt;release();
ERRCHECK(result);
result = system-&gt;close();
ERRCHECK(result);
result = system-&gt;release();
ERRCHECK(result);

return 0;

}
[/code:22rcalzv]

When you run this code you will notice that it is possible that nchannels 8 is printed just before the sound is reported to be stopped (it is not happening each time, so try to play sound several time untill nchannel8 is printed). What’s interesting – if you change sleep time back to 10ms, the problem just goes away. I’m also affraid that this is not machine/platform specific issue since I’m getting the same results on different machines and on consoles (XBO, PS4). Somehow rate at which system update is called affects this issue.

Best regards,
Lucas.

  • You must to post comments
0
0

Hi Lucas,
Sorry for the delay- please write to support@fmod.org if you want fast turnaround.
Well it looks like there is an extra mix that happens before the channel is stopped, and system::update basically updates it to stop in the mixer.

The extra mix sees that the wavetable is idle, so doesnt process it, and doesnt get the channel count being set to mono.
When it sees this, it just defaults the channel count to the system channel count, and as the unit is set to process anything, allows the 8 to go in instead of the 1 it had before.
The mixer could be updated to keep passing in whatever it had before (in fact fmod studio’s mixer does this) but as a quick workaround you can assume its safe to ignore any processing if the channel count changes, because it is feeding you silence at that point anyway. That would at least avoid your overrun.
regards,
Brett

  • You must to post comments
0
0

I’ve updated 4.44.28 which will come out in the next week. It will now pass the previous channel count through to the dsp callback if the sound has stopped.

  • You must to post comments
0
0

Thanks Brett!

I rosolved it by temporary allocating my array to output channel count. Great to hear that the issue is fixed in the next revision!

  • You must to post comments
0
0

Hello FMOD team! Any news on this issue?

  • You must to post comments
Showing 8 results
Your Answer

Please first to submit.