0
0

I am using FMOD 4.32.03. My application creates a sound with mode: FMOD_OPENONLY | FMOD_CREATESTREAM | FMOD_OPENUSER | FMOD_LOOP_NORMAL. It sets a "pcmreadcallback" function that adds PCM data. This works. But it is not seamless. The playsound has gaps and sharp sounds between each callback. I tried "realtimestitching". I can not get setSubSoundSentence to work (it hangs). I wanted to try "setDelay()" instead. But I can not find a post or example that deals with setDelay on a user created sound. I would like a suggestion on how to make user created sounds seamless or an example. thank you.

  • You must to post comments
0
0

Additionally I read that to have seamless playback use "realtimestitching" example. So here is my try at that. The problem here is that "if (currentsubsoundid != subsoundid)" sometimes is never true when i run this logic. Sometimes it is true but sporadic. Weird… And overall I hear NO sound playing ever.

The "data" is small (640 bytes). I read on a post that small sounds are an issue with "realtimestitching". Is that true? The data is PCM.

[code:2lfmcla0]void music_worker()
{
try
{
PlayerEx *player = PlayerEx::getInstance();

FMOD::Sound *parentSound ; 

FMOD_MODE   mode ; 

FMOD_CREATESOUNDEXINFO  createsoundexinfo;

memset(&createsoundexinfo, 0, sizeof(FMOD_CREATESOUNDEXINFO)); 
createsoundexinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO); 
createsoundexinfo.defaultfrequency = 8000; 
createsoundexinfo.numsubsounds = 2;   
 createsoundexinfo.numchannels = 1;
createsoundexinfo.format = FMOD_SOUND_FORMAT_PCM16; 

FMOD::Channel *channel = 0;
FMOD::Sound      *subsound[2];
unsigned int      subsoundid;
pBuff[0] = new unsigned char[MainAudio->fullBytes_];
pBuff[1] = new unsigned char[MainAudio->fullBytes_];

memcpy(pBuff[0], &MainAudio->audioData_[MainAudio->audioPos_],MainAudio->fullBytes_);
MainAudio->audioPos_ += MainAudio->fullBytes_;
memcpy(pBuff[1], &MainAudio->audioData_[MainAudio->audioPos_],MainAudio->fullBytes_);
MainAudio->audioPos_ += MainAudio->fullBytes_;

/*
    Create the 'parent' stream that contains the substreams.  Set it to loop so that it loops between subsound 0 and 1.
*/

player->result_ = player->system_->createStream(0, FMOD_LOOP_NORMAL | FMOD_OPENUSER, &createsoundexinfo, &parentSound);
player->ERRCHECK("music_worker", player->result_);


FMOD_CREATESOUNDEXINFO  subsoundexinfo;

memset(&subsoundexinfo, 0, sizeof(FMOD_CREATESOUNDEXINFO)); 
subsoundexinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO); /* required.*/
subsoundexinfo.decodebuffersize = 8000; 
subsoundexinfo.length = MainAudio->fullBytes_; 
subsoundexinfo.numchannels = 1;
subsoundexinfo.defaultfrequency = 8000; 
subsoundexinfo.format = FMOD_SOUND_FORMAT_PCM16; 

mode =  FMOD_OPENMEMORY | FMOD_OPENRAW | FMOD_LOOP_OFF | FMOD_2D | FMOD_HARDWARE;
player->result_ = player->system_->createStream((const char*)pBuff[0], mode, &subsoundexinfo, &subsound[0]);
player->ERRCHECK("music_worker", player->result_);

player->result_ = player->system_->createStream((const char*)pBuff[1], mode, &subsoundexinfo, &subsound[1]);
player->ERRCHECK("music_worker", player->result_);

player->result_ = parentSound->setSubSound(0, subsound[0]);
player->ERRCHECK("music_worker", player->result_);

player->result_ = parentSound->setSubSound(1, subsound[1]);
player->ERRCHECK("music_worker", player->result_);

/*
    Set up the gapless sentence to contain these first 2 streams.
*/
{
    int soundlist[2] = { 0, 1 };

    player->result_ = parentSound->setSubSoundSentence(soundlist, 2);
    player->ERRCHECK("music_worker", player->result_);
}

subsoundid = 0;     

/*
    Play the sound.
*/

player->result_ = player->system_->playSound(FMOD_CHANNEL_FREE, parentSound, false, &channel);
player->ERRCHECK("music_worker", player->result_);
if (player->result_ != FMOD_OK)
    return;

/*
    Main loop.
*/
do
{
    unsigned int currentsubsoundid;
    player->result_ = player->system_->update();
    player->ERRCHECK("music_worker", player->result_);

    if (channel)
    {
        bool playing;
        channel->isPlaying(&playing);
        if (!playing)
        {
            printf("\nStopped\n");
            break;
        }
    }
    player->result_ = channel->getPosition(&currentsubsoundid, (FMOD_TIMEUNIT)(FMOD_TIMEUNIT_SENTENCE_SUBSOUND | FMOD_TIMEUNIT_BUFFERED));
    player->ERRCHECK("music_worker", player->result_);

    if (currentsubsoundid != subsoundid)
    {
        /* 
            Release the sound that isn't playing any more. 
        */
        player->result_ = subsound[subsoundid]->release();       
        player->ERRCHECK("music_worker", player->result_);

        /* 
            Replace it with a new sound in our list.
        */
        delete [] pBuff[subsoundid];
        pBuff[subsoundid] = new unsigned char[MainAudio->fullBytes_];
        memcpy(pBuff[subsoundid], &MainAudio->audioData_[MainAudio->audioPos_],MainAudio->fullBytes_);
        MainAudio->audioPos_ += MainAudio->fullBytes_;
        player->result_ = player->system_->createStream((const char*)pBuff[subsoundid], mode, &subsoundexinfo, &subsound[subsoundid]);
        player->ERRCHECK("music_worker", player->result_);

        player->result_ = parentSound->setSubSound(subsoundid, subsound[subsoundid]);
        player->ERRCHECK("music_worker", player->result_);

        printf("Replacing subsound %d / 2 \n", subsoundid);
        subsoundid = currentsubsoundid;
    }      
    Sleep(100);
} while (true);
parentSound->release();
delete [] pBuff[0];
delete [] pBuff[1];

}
catch(std::exception e)
{
std::cerr << e.what() << std::endl;
}
PlayerEx::deleteInstance();
}[/code:2lfmcla0]

  • You must to post comments
0
0

I am using streams with a user defined PCM read callback. When I do
[code:2mdgxilz]if (allStreamsHaveBufferedData)
{
system->lockDSP();
system->playSound(stream1, ... );
system->playSound(stream2, ... );
system->playSound(stream3, ... );
system->unlockDSP();
}[/code:2mdgxilz]

The read callback function is never called. Isn’t it suppose to be called by playSound()? I created the stream with FMOD_OPENONLY. so there is no pre-buffering of my streams.

What do you mean by allStreamsHaveBufferedData? That the stream’s buffer has data, the read callback has been performed? Why isn’t my PCM readcallback not being reached?

  • You must to post comments
0
0

Here is more info on the laptop I am using:
Intel i7 M640, 2.8GHz
4.0 GB RAM
Vista Enterprise, SP2
Sound Card devices listed: IDT High Definition Audio CODEC, Intel(R) Display Audio

Here is the callback function. It is global.
[code:axzvzgko]FMOD_RESULT F_CALLBACK pcmreadaudio(FMOD_SOUND sound, void *data, unsigned int datalen)
{
unsigned char *mono8bitbuffer = (unsigned char *)data;
FMOD::Sound
pSound = (FMOD::Sound) sound;
Audio
audioData = NULL;

pSound-&gt;getUserData((void**) &amp;audioData);
audioData-&gt;playing_ = true;
pSound-&gt;seekData(0);

if(audioData-&gt;audioPos_ &lt; audioData-&gt;GetAudioData().size())
{
    memcpy(mono8bitbuffer, (unsigned char*)&amp;audioData-&gt;audioData_[audioData-&gt;audioPos_],datalen);
    audioData-&gt;audioPos_ += datalen;
}

return FMOD_OK;

}
[/code:axzvzgko]

The "system" is a singleton class for using fmod. All calls on are same thread.
Here is the constructor:
[code:axzvzgko]PlayerEx::PlayerEx(): system_(NULL), result_(FMOD_OK), version_(0), numdrivers_(0)
{

/*
Create a system_ object and initialize.
*/
result_ = FMOD::System_Create(&amp;system_);
if (!ERRCHECK(&quot;PlayerEx&quot;, result_)) return;
result_ = system_-&gt;getVersion(&amp;version_);
if (!ERRCHECK(&quot;PlayerEx&quot;, result_)) return;
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;
}
result_ = system_-&gt;getNumDrivers(&amp;numdrivers_);
if (!ERRCHECK(&quot;PlayerEx&quot;, result_)) return;
if (numdrivers_ == 0)
{
    result_ = system_-&gt;setOutput(FMOD_OUTPUTTYPE_NOSOUND);
    ERRCHECK(&quot;PlayerEx&quot;, result_);
    printf(&quot;Error!  No sound can be output!\n&quot;);
}
else
{
    result_ = system_-&gt;getDriverCaps(0, &amp;caps_, 0, 0, &amp;speakermode_);
    ERRCHECK(&quot;PlayerEx&quot;, result_);
    /*
    Set the user selected speaker mode.
    */
    result_ = system_-&gt;setSpeakerMode(speakermode_);
    ERRCHECK(&quot;PlayerEx&quot;, result_);
    if (caps_ &amp; FMOD_CAPS_HARDWARE_EMULATED)
    {
        /*
        The user has the 'Acceleration' slider set to off! This is really bad
        for latency! You might want to warn the user about this.
        */
        result_ = system_-&gt;setDSPBufferSize(1024, 10);
        ERRCHECK(&quot;PlayerEx&quot;, result_);
    }
    result_ = system_-&gt;getDriverInfo(0, name_, 256, 0);
    ERRCHECK(&quot;PlayerEx&quot;, result_);
    if (strstr(name_, &quot;SigmaTel&quot;))
    {
        /*
        Sigmatel sound devices crackle for some reason if the format is PCM 16bit.
        PCM floating point output seems to solve it.
        */
        result_ = system_-&gt;setSoftwareFormat(8000, FMOD_SOUND_FORMAT_PCMFLOAT, 0,2, FMOD_DSP_RESAMPLER_LINEAR);
        ERRCHECK(&quot;PlayerEx&quot;, result_);
        //printf(&quot;Set Sigmatel to use only 2 channels\n&quot;);
    }
    else
    {
        result_ = system_-&gt;setSoftwareFormat(8000, FMOD_SOUND_FORMAT_PCM16, 0,2, FMOD_DSP_RESAMPLER_LINEAR);
        ERRCHECK(&quot;PlayerEx&quot;, result_);
        //printf(&quot;Set to use only 2 channels\n&quot;);
    }
}
result_ = system_-&gt;init(1, FMOD_INIT_NORMAL, 0);
if (result_ == FMOD_ERR_OUTPUT_CREATEBUFFER)
{
    /*
    Ok, the speaker mode selected isn't supported by this soundcard. Switch it
    back to stereo...
    */
    result_ = system_-&gt;setSpeakerMode(FMOD_SPEAKERMODE_STEREO);
    ERRCHECK(&quot;PlayerEx&quot;, result_);
    /*
    ... and re-init.
    */
    result_ = system_-&gt;init(100, FMOD_INIT_NORMAL, 0);
    if (!ERRCHECK(&quot;PlayerEx&quot;, result_)) return;
}

}
[/code:axzvzgko]

Here is the method that is called to create a FMOD::Sound. The return value of the FMOD::Sound is saved for later use.
[code:axzvzgko]FMOD::Sound PlayerEx::CreateMonoStream(Audio streamAudio, int numBytes)
{
FMOD::Sound *sound;
FMOD_MODE mode = FMOD_OPENONLY | FMOD_CREATESTREAM | FMOD_OPENRAW | FMOD_OPENUSER | FMOD_HARDWARE | FMOD_LOOP_NORMAL;
int channels = 1;
FMOD_CREATESOUNDEXINFO createsoundexinfo;

memset(&amp;createsoundexinfo, 0, sizeof(FMOD_CREATESOUNDEXINFO)); 
createsoundexinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO); /* required.*/
createsoundexinfo.decodebuffersize = 8000; 
createsoundexinfo.length = numBytes; 
createsoundexinfo.numchannels = channels;
createsoundexinfo.defaultfrequency = 8000; 
createsoundexinfo.format = FMOD_SOUND_FORMAT_PCM16; 
createsoundexinfo.pcmreadcallback =  pcmreadaudio; /* User callback for reading. */
//createsoundexinfo.pcmsetposcallback =  pcmsetposcallback; /* User callback for seeking. */
createsoundexinfo.userdata = streamAudio;    

result_ = system_-&gt;createSound(0, mode, &amp;createsoundexinfo, &amp;sound);
ERRCHECK(&quot;CreateMonoStream&quot;, result_);
return sound;

} [/code:axzvzgko]

Another thread fills in data into a class member Audio::std::vector<unsigned char> audioData_;
Each fill is in increments of createsoundexinfo.length set in CreateMonoStream().
Once the first "fill" happens PlayIt() is called with the FMOD::Sound *sound created from the CreateMonoStream()

Now the call to PLAY the FMOD::Sound
[code:axzvzgko]void PlayerEx::PlayIt(FMOD::Sound *sound)
{
FMOD::Channel *channel = 0;
if (NULL == sound)
return;

/*
Play the sound.
*/
result_ = system_-&gt;playSound(FMOD_CHANNEL_FREE, sound, 0, &amp;channel); 
ERRCHECK(&quot;PlayIt(FMOD::Sound *sound)&quot;, result_);
printf(&quot;playSound\n&quot;);

/*
Main loop.
*/

do
{
    system_-&gt;update();
    if (channel)
    {
        bool playing;
        channel-&gt;isPlaying(&amp;playing);
        if (!playing)
        {
            printf(&quot;\nStopped\n&quot;);
            break;
        }
    }
}while (1); //endless loop // key != 27)

}[/code:axzvzgko]

It plays each "createsoundexinfo.length" worth of sound, but there are gaps/spikes of noise between them.
Almost like a "hiccup".
Sorry for the long post. Please let me know if you see something wrong.thanks.

  • You must to post comments
0
0

Make sure that unlockDSP is being called otherwise the mixer will stay locked, which would prevent the pcmread callback from firing.

  • You must to post comments
0
0

[quote:2ii9hxze]I am using FMOD 4.32.03. My application creates a sound with mode: FMOD_OPENONLY | FMOD_CREATESTREAM | FMOD_OPENUSER | FMOD_LOOP_NORMAL. It sets a "pcmreadcallback" function that adds PCM data. This works. But it is not seamless.[/quote:2ii9hxze]

It should be seamless, can you post a code snippet of Sound creation and your callback?

  • You must to post comments
0
0

Since you’re creating the streams you can probably just lock the mixer and call play on all the streams. Something like this:

[code:1juai249]
if (allStreamsHaveBufferedData)
{
system->lockDSP();
system->playSound(stream1, ... );
system->playSound(stream2, ... );
system->playSound(stream3, ... );
system->unlockDSP();
}[/code:1juai249]

  • You must to post comments
0
0

non-trival huh? Well I am really committed with my current implementation. So I really do not want to start over with something else. Please let me know how to do [quote:bii7688z]interleaving the streams together and using Channel::setInputChannelMix to mute certain streams[/quote:bii7688z]. I appreciate any assistance. My deadline is within the week and I feel a little desperate. Any psuedo code, or example, will save me.

  • You must to post comments
0
0

Synchronizing streams is a non-trivial task. The best way to do it is by interleaving the streams together and using Channel::setInputChannelMix to mute certain streams.

  • You must to post comments
0
0

Thanks Bret. I now have multiple streams playing well. Is there a way to synchronize these streams?

  • You must to post comments
0
0

You should try to pause the sound from inside the mixer. Set a flag inside the mixer and pause it from the main thread. To make sure it doesn’t repeat sound while it’s waiting for the pause just memset the outbuffer to zero to create silence.

  • You must to post comments
0
0

Thanks. I actually changed my code to buffer enough data prior to calling "playSound()".

I have a flag in my "userData" class that indicates that there is NO more data. What should pcmreadaudio() return if there is no more data, to get "playSound" to stop playing?

The channel seems to stay in the "playing" state and repeats playing the last data in the buffer.

  • You must to post comments
0
0

If there is not enough data when FMOD requests data, you are in trouble because that data is getting pulled through the mixer. If you stall the mixer by sleeping in your callback that will cause the whole mixer to have gaps. The issue is you are telling FMOD to play a sound that you don’t have enough data for. I would reccomend that you wait until you have buffered a sufficient amount of data before playing the sound.

  • You must to post comments
0
0

I kept looking at my FMOD_RESULT F_CALLBACK pcmreadaudio() function. It seems the user data (audioData->audioData_[audioData->audioPos_]) was just not ready with enough data!!! So silence or garbage was filled in "mono8bitbuffer" and caused gaps.

Can I loop in pcmreadaudio() until there is enough data? So I am always returning user data?

What should pcmreadaudio() return if there is no more (or not enough) user data, to get "playSound" to stop playing?

  • You must to post comments
Showing 13 results
Your Answer

Please first to submit.