DirectSound bug - please help me test

BlitzMax Forums/BlitzMax Programming/DirectSound bug - please help me test

Grey Alien(Posted 2007) [#1]
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.


JazzieB(Posted 2007) [#2]
Crash confirmed in Vista.


Grey Alien(Posted 2007) [#3]
Thanks, that's a useful test as it shows it's not XP or my PC. (It also crashes onmy sons WinME PC)


TMK(Posted 2007) [#4]
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.


Grey Alien(Posted 2007) [#5]
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.


Vlad(Posted 2007) [#6]
Unhandled Memory Exception Error

Works fine with Const MAX_CHANNELS=3


Grey Alien(Posted 2007) [#7]
yeah exactly it's really weird.


Dreamora(Posted 2007) [#8]
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.


Grey Alien(Posted 2007) [#9]
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 :-)


Dreamora(Posted 2007) [#10]
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?!


tonyg(Posted 2007) [#11]
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.


Grey Alien(Posted 2007) [#12]
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...


Dreamora(Posted 2007) [#13]
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


Grey Alien(Posted 2007) [#14]
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...


tonyg(Posted 2007) [#15]
It doesn't fix the problem for me.
<edit> but that was on a 1.24 machine.
<edit> and on 1.26.


Dreamora(Posted 2007) [#16]
Strange ... I runs with 16 channels here on my system (Realtek HD Audio onboard) without crashing ...


Grey Alien(Posted 2007) [#17]
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.


degac(Posted 2007) [#18]
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.


TaskMaster(Posted 2007) [#19]
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.


Grey Alien(Posted 2007) [#20]
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.