0
0

I’m having two issues with FMODEx.

First one is that FMOD_CHANNEL_CALLBACKTYPE_END callback is called outside of the fmodSystem->update() under certain condition. It occurs when (probably) fmod runs out of free channels, as in the following example:
[code:29y0qg9w]
for (int i = 0; i < 1000; ++i) {
SoundInstance* soundInstance = new SoundInstance(fileName);
soundInstance->play();
}
[/code:29y0qg9w]
The callback is triggered at i == 100, i.e. at 101st iteration. It looks like FMOD creates a pool of 100 channels and once all of them got busy, FMOD frees the first one as if it is played to end.

This behaviour contradicts with the one described in the manual:
"Currently callbacks are driven by System::update and will only occur when this function is called."

Second issue might be related to the first one, it occurs at the very same code right at the callback.

In the code posted above I was creating a new SoundInstance at each iteration. Consider the following costructor body:
[code:29y0qg9w]
SoundInstance::SoundInstance(const char* fileName)
{
FMOD_RESULT result = _system->createStream(fileName, FMOD_SOFTWARE|FMOD_LOOP_NORMAL|FMOD_2D, 0, &_sound);
assert(result == FMOD_OK);
}
[/code:29y0qg9w]
I.e. I create a separate sound stream for each SoundInstance. This is on par with a manual, that states:
"… a stream only has 1 decode buffer and file handle, and therefore can only be played once. … Open multiple streams to have them play concurrently."

My play() method is as follows:
[code:29y0qg9w]
void play()
{
FMOD_RESULT result;
result = _system->playSound(FMOD_CHANNEL_FREE, _sound, false, &_channel);
assert(result == FMOD_OK);

result = _channel-&gt;setCallback(FMOD_CHANNEL_CALLBACKTYPE_END, &amp;onStopHandler, (int)(void*)this);
assert(result == FMOD_OK);

}
[/code:29y0qg9w]
I.e. I create new channel and subscribe for a FMOD_CHANNEL_CALLBACKTYPE_END event passing a this pointer as a command parameter (which is unused according to the documentation, and yes, it is unsafe on x64).

The event is recieved while in a _system->playSound(…); method body.

Once I recieve that callback event, I release my FMOD::Sound object, because I don’t need it anymore.
[code:29y0qg9w]
static FMOD_RESULT F_CALLBACK onStopHandler(FMOD_CHANNEL channel, FMOD_CHANNEL_CALLBACKTYPE type,
int command, unsigned int commanddata1, unsigned int commanddata2)
{
SoundInstance
thisPtr;

switch (type) {
    case FMOD_CHANNEL_CALLBACKTYPE_END:
        thisPtr = (SoundInstance*)(void*)command;
        thisPtr-&gt;onStop();
        break;

    default:
        assert(false);
        break;
    }

return FMOD_OK;

}

void SoundInstance::onStop()
{
_channel = 0;
_sound->release();
}
[/code:29y0qg9w]
Everything seems to be allright, but once my onStopHandler returns, the program crashes immediately with the following message:

Access violation reading location 0xfeeefeee.

Strange enough, it only occurs when I debug the program via Visual Studio (I use VS2005), running it outside of the IDE or with a Ctrl+F5 doesn’t reveal the issue.

Full source code listing is posted here: http://www.everfall.com/paste/id.php?lypkhx36hbm1

Could you please clarify whether the following bahaviour is expected and I am doing something wrong?

Thank you, Koroskin Denis.

  • You must to post comments
0
0

[quote="calvinlin":1ir4v8wa]When you attempt to play an event and it steals a channel, you would expect the callback to be triggered immediately, right?[/quote:1ir4v8wa]No I don’t! That’s unacceptable! It is fine if you steal the channel, but don’t notify about it unless I ask you (via FMOD::System::update() call).

[quote="calvinlin":1ir4v8wa]it makes no sense for all callbacks to only be triggered in system->update().[/quote:1ir4v8wa]No, it all does! That’s a contract I rely on.

[quote="calvinlin":1ir4v8wa]Our handler for FMOD_EVENT_CALLBACKTYPE_STOLEN simply marks the game level sound object as stolen and clears the event instance handle it has.[/quote:1ir4v8wa]I don’t get FMOD_EVENT_CALLBACKTYPE_STOLEN but FMOD_CHANNEL_CALLBACKTYPE_END. This, however, makes little difference.

[quote="brett":1ir4v8wa]the only callback that now calls outside of update is when you call Channel::stop that i know of, why is this even an issue?[/quote:1ir4v8wa]But I shown you code sample that you can compile, execute and get an evidence that System::createSound() does generate a callback, too!

And yes, it is an issue.

My sound processing is split into three parts:
[code:1ir4v8wa]/* 1 / gameLogicManager->update();
/
2 / soundManager->update();
/
3 */ fmodSystem->update();[/code:1ir4v8wa]
During first one, game logic is being executed. User might create some objects, fire sound events on them etc. An important thing is that everything is put into queue and nothing is performed indeed.

During second one, the events are executed as a deferred procedure. These envolve creating some fmod objects, playing/stopping them etc.

Third one is fmodSystem->update(). This step involves Fmod-generated events handling etc.

The most important part of this design is that none of the three overlap and interfere and I can develop any of the three independently without thinking about some possible side effects that callbacks can introduce.

Imagine that gameLogicManager iterates over its game objects and constantly calls play on of them:
[code:1ir4v8wa]foreach (object in objects) { // game logic is usually scripted in a language other than C++
object.play(); // calls soundManager methods under the hood
}[/code:1ir4v8wa]
what if a soundStopped event would be triggered while in a middle of a collection? We can’t destroy the object or erase it from the collection during this callback because this would invalidated the container’s internal state. We can’t play some other object because this might generate some other callback as a result. It becomes non-deterministic, unreliable and unsafe. Why the scripter should think about all these consequences? He is not a programmer, after all!

That’s why I ensure my collegues that whatever you do with soundManager won’t cause side effects. Ever. This greatly simplifies the code and makes it more robust. And it is simple to implement. The only think you should ensure is that soundManager don’t generate callbacks unless explicitly requested (soundManager->update()) and so does underlying sound system (FMOD). That’s it.

Did I convince you?

  • You must to post comments
0
0

Here are the last entries of fmod.log:
[code:2il0w8yy]
FMOD: CodecOggVorbis::openInternal : attempting to open as OGG..
FMOD: SystemI::createSoundInternal : Format has 0 subsounds.
FMOD: SystemI::createSoundInternal : Create as FMOD_CREATESTREAM
FMOD: System::createSoundInternal : decode buffersize = 8704 : blocksize = 256
FMOD: SystemI::createSample : mode 000000ca length 8704 samples, lengthbytes 11451
FMOD: SystemI::createSample : subsamples = 1, channels = 1
FMOD: SystemI::createSample : subsample 0. output = 0036B2E0
FMOD: SystemI::createSample : mSoftware = 0036B2E0
FMOD: OutputSoftware::createSample : lengthpcm 8704, lengthbytes 11451, channels 1, format 2, mode 000000ca
FMOD: OutputSoftware::createSample : done
FMOD: SystemI::createSample : done
FMOD: System::createSoundInternal : Seek stream to start
FMOD: System::createSoundInternal : flush stream buffer
FMOD: System::createSoundInternal : flush successful.
FMOD: System::createSoundInternal : switch file handle from small blocking single buffered to large nonblocking doublebuffered.
FMOD: SystemI::createSoundInternal : No name found in file, use filename.
FMOD: SystemI::createSoundInternal : done. OpenState now = FMOD_OPENSTATE_READY. 182747 bytes used

FMOD: SoundI::release : shoot.ogg (02FAD158)
FMOD: SoundI::release : remove stream samples. (02FAD158)
FMOD: SoundI::release : (02FAD400)
FMOD: SoundI::release : free this. (02FAD400)
FMOD: SoundI::release : done (02FAD400)
FMOD: SoundI::release : release codec. (02FAD158)
FMOD: Codec::release :
FMOD: Codec::release : Close file (mFile = 02F9FB88)
FMOD: Plugin::release : (02F9FD48)
FMOD: Plugin::release : done
FMOD: Codec::release :
FMOD: SoundI::release : free this. (02FAD158)
FMOD: SoundI::release : done (02FAD158)
[/code:2il0w8yy]

  • You must to post comments
0
0

this just bit me.

do i have out of date docs or is this fixed in a recent update??

because i read the entry for FMOD_CHANNEL_CALLBACK and FMOD_CHANNEL_CALLBACKTYPE and they both specifically say "Note! Currently the user must call System::update for these callbacks to trigger!".

this is incorrect with the version of FMOD im using "0x00042004", as very recently i encountered a situation where a channel end callback was triggered outside of the update() call.

the reason this bit me was because i was tracking sound resources with a vector which was being iterated over when the callback happened to be triggered.

the callback changes the vector and therefore invalidates its iterators, which of course leads to a crash in the concurrent iteration.

i can of course work around the issue, but i also think the docs on the callbacks should be fixed to say that channel end callbacks can be called outside of update(), or alternatively the system should be fixed so that the callback isnt triggered outside of update().

  • You must to post comments
0
0

Although the channel has finished fmod is not finished with the sound object internally. It basically is getting to 101st iteration, then it needs to find an available channel, to do that it has to look at the channel’s associated sound, which you have released; that is why FMOD is accessing 0xfeeefeee.

  • You must to post comments
0
0

It is nice that you provided some information of what is going on under the hood, but I still tend to think that is is not an expected behaviour, and your answer didn’t clarify anything.

> Although the channel has finished fmod is not finished with the sound object internally.

Does it mean that I am not allowed to call Sound::release() while in a callback? If so, why doesn’t it return something like FMOD_ERR_CANT_RELEASE_OBJECT and how do I know that FMOD is finished with it? Some other callback? I found nothing about it in the manual. In fact, it makes little sense.

Even if FMOD didn’t finish with it I am finished with it, that’s why I call Sound::release(). If there are any references to it left internally then a call to Sound::release() should make some mark or decrement some reference counter and not lead to object destruction.

Is it ok that a callback is called outside of the System::update() method? If yes, is there any way to enforce this contract (although I do understand that it is sometimes usefull to release and reuse old channels, but in my case I prefer the system->createSound() to return FMOD_ERR_CHANNEL_ALLOC, FMOD_NO_CHANNELS_LEFT or even FMOD_ERR_INTERNAL but not an unwanted callback on error) or should I change my code so that it doesn’t rely on it anymore?

May I call Sound::release() in a callback? If no, how do I know that FMOD is finished with Sound and it is safe to call Sound::release()?

Is it ok that FMOD fails with access violation (for any reason, not only my case)?

Sorry for being a bit sharp, we have a submission to Sony in a few days and I am a bit nervous about this issue.

Thank you, Koroskin Denis.

  • You must to post comments
0
0

Denis, how did you generate fmod.log file? / Zoran

  • You must to post comments
0
0

[quote:59q2rhjj]we have a submission to Sony in a few days[/quote:59q2rhjj]
If your question is urgent the best course of action is to email support@fmod.org. Half our staff are out of the office this week but are still checking email. I’ll do my best to answer your questions.

[quote:59q2rhjj]Does it mean that I am not allowed to call Sound::release() while in a callback?[/quote:59q2rhjj]
No the only problem here is that the channel is still valid and the sound it’s using has been released. Although you are finished with both the channel and the sound, if you wanted you could call _channel->play() and they would play again. They’re not cleaned up until it is necessary (i.e. 101st iteration)

[quote:59q2rhjj]…how do I know that FMOD is finished with Sound and it is safe to call Sound::release()? [/quote:59q2rhjj]
It’s not graceful but if you want to know as soon as a channel is invalid you can poll FMOD::Channel->isPlaying() until it returns FMOD_ERR_INVALID_HANDLE.

[quote:59q2rhjj]…in my case I prefer the system->createSound() to return FMOD_ERR_CHANNEL_ALLOC, FMOD_NO_CHANNELS_LEFT or even FMOD_ERR_INTERNAL but not an unwanted callback on error) or should I change my code so that it doesn’t rely on it anymore? [/quote:59q2rhjj]
Yours is a non-standard implementation, sorry I couldn’t be of more assistance. I realise that your problem is urgent and I’ll keep trying to think of something. If you can email support someone will get back to you shortly. Thanks for your patience.

  • You must to post comments
0
0

Thanks for your reply.

[quote:2krddo75][quote:2krddo75]Does it mean that I am not allowed to call Sound::release() while in a callback?
[/quote:2krddo75]No the only problem here is that the channel is still valid and the sound it’s using has been released.
[/quote:2krddo75]I don’t think it should be a problem. The following code works like a sharm:
[code:2krddo75]/0/ FMOD::Sound* sound;
/1/ FMOD::Channel* channel;
/2/ FMOD_RESULT result;
/3/ result = _system->createSound(fileName, FMOD_SOFTWARE|FMOD_LOOP_NORMAL|FMOD_2D, 0, &sound);
/*4*/ assert(result == FMOD_OK);
/*5*/
/*6*/ result = _system->playSound(FMOD_CHANNEL_FREE, sound, false, &channel);
/*7*/ assert(result == FMOD_OK);
/*8*/
/*9*/ sound->release();
/*A*/
/*B*/ while (true) {
/*C*/ _system->update();
/*D*/ }[/code:2krddo75]
Releasing the sound object at line 9 prevents channel from playing, but it doesn’t lead to crash. Commenting this line out makes the channel play as usual. It means that the problem occurs in a callback only.

(I still think that making a callback call outside of the system->update() is a bad idea.)

[quote:2krddo75]Yours is a non-standard implementation.[/quote:2krddo75]Why is it so? I think this is a common solution: you create a separate sound object per each streaming audio, a separate channel per sound to play it, a separateclass instance to store this sound/channel pair. I subscribe for a callback to get a notification that sound is stopped so that I don’t do O(n) loop over all the sound instances every frame. Once it is stopped, I don’t need it anymore and I destroy it.

I could also[quote:2krddo75]poll the FMOD::Channel->isPlaying() until it returns FMOD_ERR_INVALID_HANDLE[/quote:2krddo75]but [quote:2krddo75]It’s not graceful[/quote:2krddo75]
and I agree with you.

  • You must to post comments
0
0

Dont release your sound from a callback. Put it in a list or set a flag from the end callback, and do it in your own update function. This will solve your issue.

We generally dont recommend releasing a sound from a callback but we’ll look into it and try and make that special case more robust.

  • You must to post comments
0
0

[quote:1vquahrg]Dont release your sound from a callback. Put it in a list or set a flag from the end callback, and do it in your own update function. This will solve your issue.[/quote:1vquahrg]

Yes, I know that.

[quote:1vquahrg]We generally dont recommend releasing a sound from a callback but we’ll look into it and try and make that special case more robust.[/quote:1vquahrg]
I didn’t find any information regarding this in your manual, but thanks anyway.

The only issue left is, in fact, the original one: a callback which is triggered outside of the system->update(). This behaviour contradicts with the statement in the documentation.

Is it going to be fixed or a manual updated?

Thinking aloud, I find callbacks triggered during, say, channel->isPlaying() or system->createSound() a bad design decision. User code usually has deep call stack during these calls because it processes some internal logic with many levels of abstractions. It means that many objects are in such a state that their invariants don’t evaluate to true and many assumptions are wrong and preconditions not satisfied.

And please, be more open and don’t hesitate to admit errors! Our conversation would be much shorter and more pleasant if you would say "Oops, that’s a bug, we will fix that as time permits" or "No, this is by design, how come that docs don’t describe this behaviour?" or "No, you must have missed important note at the following page: …".

Thank you!

  • You must to post comments
0
0

We haven’t had any major problems with when callbacks are triggered by FMOD. When you attempt to play an event and it steals a channel, you would expect the callback to be triggered immediately, right? I think that’s simply a documentation problem because it makes no sense for all callbacks to only be triggered in system->update().

Our handler for FMOD_EVENT_CALLBACKTYPE_STOLEN simply marks the game level sound object as stolen and clears the event instance handle it has.

Edit: Oops, you’re not using the event system layer, are you?

  • You must to post comments
0
0

the only callback that now calls outside of update is when you call Channel::stop that i know of, why is this even an issue?

  • You must to post comments
Showing 12 results
Your Answer

Please first to submit.