DirectSound bug - please help me test
BlitzMax Forums/BlitzMax Programming/DirectSound bug - please help me test
| ||
Hi, I believe I have found a serious bug with the DirectSound driver but I need to verify it. It breaks my current game with a MAV when DirectSound is the chosen driver. I was investigating using DirectSound in WinME and Vista instead of OpenAL which also has some (different) problems becaue FreeAudio is not viable on those systems due to breakup/lag etc. I've spent HOURS whittling down the code into a test app as follows: Strict SetAudioDriver("DirectSound") 'comment this out for no crash Const MAX_CHANNELS=4 Global channels:TChannel[MAX_CHANNELS] Global CurrentChannel=0 For Local i=0 To MAX_CHANNELS-1 channels[i]=AllocChannel() Next Local Counter=0 Local sound:TSound = LoadSound("test.wav") Graphics 400,300,0 test() test() 'if you comment this line out, it doesn't crash on my PC ... go figure! GCCollect() While Not KeyHit(KEY_ESCAPE) Cls Counter:+1 If Counter=5 Then Play(Sound) Counter=0 EndIf DrawText CurrentChannel,10,10 Flip Wend Function test() Local TestSound:TSound = LoadSound("test.wav") Play(TestSound) End Function Function Play(sample:TSound) Local ch:TChannel=channels[CurrentChannel] CueSound(sample, ch) ResumeChannel(ch) CurrentChannel:+1 If CurrentChannel=MAX_CHANNELS Then CurrentChannel=0 End Function You need this sample: http://www.greyaliengames.com/misc/test.wav (18k) Basically if I put that sample in the same folder as the app and run it I get a "Unhandled Memory Exception Error". With Debug enabled it appears to be happening in Stop() called by Cue() called by Cue() called by CueSound(). I noticed that self._buff._succ._succ:TBuf = null and I'm not sure if it should be (but that could be a red herring). This bug needs weirdly specific circumstances to occur. I basically figured out that if you load in a sound and play it and then free up that sound (but not the channel), then later on you try to use that channel again, DirectSound crashes. Yet FreeAudio and OpenAL do NOT crash. In those other drivers you can quite happily trigger a sound in a channel, kill of the sound and the channel will keep playing and is reuseable. With DirectSound the channel keeps playing, but it's not reuseable later. However, in my above code, if I comment out one of the Test() function calls, it doesn't crash! If I comment out the GCCollect it doesn't crash, weird. This must just be something to do with then the GC decides to collect the local variable of TestSound in the Test() function. First I'd like to verify that other people get the same crash, and if they do then I can raise it as an official bug. My theory is that DirectSound doesn't like it when you free up a sound that is playing yet the other drivers are happy with this. Why do I want to free up a sound that is playing? Well imagine a screen with a button with a special click sound. The user clicks the button, the sound plays on a GLOBAL channel, and then the screen is freed up and a new screen is showing. The global button click channel may continue playing for a little bit even though the sound has now been freed up. Then later on the global channel is reused - works in FreeAudio and OpenAL but not DirectSound. Any help appreciated thanks, it would be nice to get to the bottom of this. |
| ||
Crash confirmed in Vista. |
| ||
Thanks, that's a useful test as it shows it's not XP or my PC. (It also crashes onmy sons WinME PC) |
| ||
Crashes for me in Vista (32bit Home Premium) as well. First time I tried it it played it fine though, but I had WinAMP already playing in case that made a difference so I stopped that, and tried again, and now it always crashes even if I restart WinAMP. I have a Realtek AC'97 Audio onboard SFX card. |
| ||
OK thanks for testing. It's a weird problem because if small changes are made it can't be duplicated (like removing one of the Test() calls or removing GCCollect()). So we know it's not OS related nor Sound card related (I have a SB Audigy 2). I'll post it in the bug forum now. |
| ||
Unhandled Memory Exception Error Works fine with Const MAX_CHANNELS=3 |
| ||
yeah exactly it's really weird. |
| ||
Is there a chance of getting an unbugged code? Right now you locally flood the channels by loading a sound and playing it. But you never free it again, which simply means that you take care that it actually must die. Just because the sound goes out of scope, the channel won't be freed instantan. So you either need to stop the sound again or use one of the channel array entries to keep track of it. |
| ||
I'm sorry I'm not quite following you. I know that if a sound goes out of scope that a channel will not be freed (and I don't want to free my channels). HOWEVER, there seems to be some issue with DirectSound where weirdly if a sound does go out of scope, something goes WRONG with the channel meaning it cannot be reused. Although it's hard to duplicte precisely as it doesn't ALWAYS seem to happen. I noticed it in my game when I moved over the buttons. Sometimes it would crash on the first button and sometimes on button 20, there was no decent reason why unless DirectSound has an internal "next free channel" pointer that is separate from the app or something... There is nothing wrong with declaring a reuseable array of TChannel and then using it over an over providing you don't call StopChannel which frees up the channel. This approach works very well with FreeAudio and OpenAL but not DirectSound. I've released 2 games using this approach. Maybe you can explain a bit more or give some example about what you mean? Thanks :-) |
| ||
There is nothing wrong by declaring a reusable array of TChannel. EDIT: OK forget the stop one. Went through the sources and Play -> Cue -> channel.cue -> stops itself before doing anything. But during that I actually found out that it is the Stop that crashes the app?! |
| ||
GA, if you put a delay 1000 before your cuesound the problem does not occur. This *does* suggest it is a timing issue but why Directsound gets it while the others don't.... no idea. |
| ||
Dreamora: That source doesn't compile, but if you take out the globalChannel param from your Play() call in function test() it does, but it still crashes. So you agree something weird is going on? TonyG: Yep, it seems to be some kind of timing issue todo with if a sound is playing or not when it's freed up. Certainly the behaviour of the drivers should be consistent but it's not... |
| ||
I think I found the problem. In brl.directsoundaudio directsoundaudio.bmx TDirectSoundSound.Create Local desc:DSBUFFERDESC=New DSBUFFERDESC desc.dwSize=SizeOf(DSBUFFERDESC) desc.dwFlags=DSBCAPS_STATIC|DSBCAPS_GLOBALFOCUS|DSBCAPS_CTRLPAN|DSBCAPS_CTRLVOLUME|DSBCAPS_CTRLFREQUENCY If _driver._mode=1 Or (flags & 2)<>2 desc.dwFlags:|DSBCAPS_LOCSOFTWARE desc.dwBufferBytes=size desc.lpwfxFormat=fmt should look like this I think. Reason is the effect of this flag: From DX SDK: "The buffer is in on-board hardware memory." bad idea with onboard sound cards actually. Local desc:DSBUFFERDESC=New DSBUFFERDESC desc.dwSize=SizeOf(DSBUFFERDESC) desc.dwFlags=DSBCAPS_GLOBALFOCUS|DSBCAPS_CTRLPAN|DSBCAPS_CTRLVOLUME|DSBCAPS_CTRLFREQUENCY If _driver._mode=1 Or (flags & 2)<>2 desc.dwFlags:|DSBCAPS_LOCSOFTWARE desc.dwBufferBytes=size desc.lpwfxFormat=fmt At least for me it works without dieing on stop |
| ||
Sounds EXCELLENT, great research! However, I can't test this yet due to my compile problems as mentioned in the other thread. I'll have to post the proposed fix in the Bug thread and see if Mark or Skidracer (or whoever wrote it) thinks that it's safe and will make it standard...unless there's any potential problems with leaving that flag out... |
| ||
It doesn't fix the problem for me. <edit> but that was on a 1.24 machine. <edit> and on 1.26. |
| ||
Strange ... I runs with 16 channels here on my system (Realtek HD Audio onboard) without crashing ... |
| ||
Thanks Tony. OK I made that change (basically removing DSBCAPS_STATIC right?) and recompiled in V1.26 and unfortunately the bug is still there. It loops through the 4 channels and bombs when channel number = 1 (the 2nd channel) :-( Hope BRL can get to the bottom of this. |
| ||
Just run the Grey example test on 1.24 and 1.26 1.24 - it runs fine 1.26 - confirmed error 'Unhandled Memory Exception Error' I've not tested the fix posted. |
| ||
Very strange bug. If I set the max counter to only 1, no error. If I set it to 20, no error. If I run test() 4 times in a row, no error. Very strange. |
| ||
degac: OK thanks. That's weird as it's fine in your 1.24 but not TonyG's...wonder if you had different syncmod states? TaskMaster: Yep it's really weird, I KNEW the bug existsed as I'd seen it in my game, I could make it happen every time I ran it but NOT in the same place, there was a random feel to it. So I wrote this app and at first couldn't get the bug until I jiggled the code around to the state you see it in now. Small changes and it won't crash, it's very odd. |