Sign in to follow this  
Evil Steve

DirectSound8: Notifications

Recommended Posts

Hi all, I seem to be having some oddities with my sound manager classes. I set up a 2 second DirectSound buffer for streaming music into, and I decode an ogg file into that buffer. I have 2 notifications in the buffer, one halfway though, and one at the end of the buffer. I have a worker thread that waits on the notification handles, and fills the sound buffers when the handles are signalled. Now that all works fine when I have one buffer. When I play two at once, the notifications spazz out. Here's some debug output:
Buffer 0x00A4A228 got notify 0 at 12226671
Buffer 0x00A4A228 got notify 1 at 12227671
Buffer 0x00A4A228 got notify 0 at 12228671
Buffer 0x00A4A228 got notify 1 at 12229671
Starting second buffer...
Buffer 0x00A4A228 got notify 0 at 12230671
Buffer 0x00E08CA8 got notify 0 at 12230703
Buffer 0x00E08CA8 got notify 1 at 12230734
Buffer 0x00A4A228 got notify 1 at 12231671
Buffer 0x00E08CA8 got notify 1 at 12231703
Buffer 0x00A4A228 got notify 0 at 12231718
Buffer 0x00E08CA8 got notify 0 at 12231750
Buffer 0x00A4A228 got notify 0 at 12232671
Buffer 0x00E08CA8 got notify 0 at 12232703
Buffer 0x00A4A228 got notify 1 at 12232718
Buffer 0x00E08CA8 got notify 1 at 12232750
Buffer 0x00A4A228 got notify 1 at 12233671
Buffer 0x00E08CA8 got notify 1 at 12233703
Buffer 0x00A4A228 got notify 0 at 12233718
Buffer 0x00E08CA8 got notify 0 at 12233750
Buffer 0x00A4A228 got notify 0 at 12234671
Buffer 0x00E08CA8 got notify 0 at 12234687
Buffer 0x00A4A228 got notify 1 at 12234718
Buffer 0x00E08CA8 got notify 1 at 12234750

The random value at the end is the return value from GetTickCount(). As you can see, it all works fine when I have only one buffer playing, but when I start the second one, first the second buffer gets both notifications hit within 30ms or so, then the first buffer gets its first notification hit 100ms after the second notification, followed by some more oddities. Leaving this playing causes all sorts of skipping and general audio noise. Is there something obvious I seem to be missing about notifications? Here's the function I use to create my sound buffer and set up the notifications:
LPDIRECTSOUNDBUFFER8 PAudioMgr::CreateStreamingBuffer(int nFrequencyInHz, int nBitsPerSample,
	bool bStereo, DWORD& dwBufferBytes, HANDLE& rhNotify1, HANDLE& rhNotify2)
{
WAVEFORMATEX theFormat;
DSBUFFERDESC theDesc;
LPDIRECTSOUNDBUFFER pBuffer;
LPDIRECTSOUNDBUFFER8 pNewBuffer;
LPDIRECTSOUNDNOTIFY8 pNotify;
DSBPOSITIONNOTIFY theNotify[2];
HRESULT hResult;
WORD wChannels = bStereo?2:1;

	Assert(m_pSound, "Audio engine not initialised");
	PApp& app = PApp::Get();

	// Fill in format and buffer description
	theFormat.cbSize = sizeof(WAVEFORMATEX);
	theFormat.wFormatTag = WAVE_FORMAT_PCM;
	theFormat.nChannels = wChannels;
	theFormat.nSamplesPerSec = nFrequencyInHz;
	theFormat.wBitsPerSample = (WORD)nBitsPerSample;
	theFormat.nBlockAlign = theFormat.nChannels * theFormat.wBitsPerSample/8;
	theFormat.nAvgBytesPerSec = theFormat.nSamplesPerSec*theFormat.nBlockAlign;
	memset(&theDesc, 0, sizeof(theDesc));
	theDesc.dwSize = sizeof(DSBUFFERDESC);
	theDesc.dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_CTRLVOLUME | DSBCAPS_GLOBALFOCUS;
	theDesc.dwBufferBytes = (s_dwStreamingBufferLen * theFormat.nAvgBytesPerSec) / 1000;
	theDesc.lpwfxFormat = &theFormat;

	// Create IDirectSoundBuffer
	hResult = m_pSound->CreateSoundBuffer(&theDesc, &pBuffer, NULL);
	if(FAILED(hResult))
	{
		std::wstringstream str;
		str << L"CreateSoundBuffer() failed. Error: ";
		str << DXGetErrorString(hResult);
		app.SetError(str.str());
		ELog::Get().ErrorFormat(L"AUDIO   : * %s\n", app.GetError().c_str());
		return NULL;
	}

	// Get a pointer to a IDirectSoundBuffer8
	hResult = pBuffer->QueryInterface(IID_IDirectSoundBuffer8, (void**)&pNewBuffer);
	pBuffer->Release();
	if(FAILED(hResult))
	{
		pBuffer->Release();

		std::wstringstream str;
		str << L"QueryInterface(IID_IDirectSoundBuffer8) failed. Error: ";
		str << DXGetErrorString(hResult);
		app.SetError(str.str());
		ELog::Get().ErrorFormat(L"AUDIO   : * %s\n", app.GetError().c_str());
		return NULL;
	}

	// Get a IDirectSoundNotify8 interface
	hResult = pNewBuffer->QueryInterface(IID_IDirectSoundNotify8, (void**)&pNotify);
	if(FAILED(hResult))
	{
		pNewBuffer->Release();
		pBuffer->Release();

		std::wstringstream str;
		str << L"QueryInterface(IID_IDirectSoundNotify8) failed. Error: ";
		str << DXGetErrorString(hResult);
		app.SetError(str.str());
		ELog::Get().ErrorFormat(L"AUDIO   : * %s\n", app.GetError().c_str());
		return NULL;
	}

	// Add notifications
	theNotify[0].dwOffset = theDesc.dwBufferBytes / 2;
	theNotify[0].hEventNotify = CreateEvent(NULL, FALSE, FALSE, NULL);
	theNotify[1].dwOffset = theDesc.dwBufferBytes - 1;
	theNotify[1].hEventNotify = CreateEvent(NULL, FALSE, FALSE, NULL);
	hResult = pNotify->SetNotificationPositions(2, &theNotify[0]);
	pNotify->Release();
	if(FAILED(hResult))
	{
		pNewBuffer->Release();
		pBuffer->Release();

		std::wstringstream str;
		str << L"SetNotificationPositions() failed. Error: ";
		str << DXGetErrorString(hResult);
		app.SetError(str.str());
		ELog::Get().ErrorFormat(L"AUDIO   : * %s\n", app.GetError().c_str());
		return NULL;
	}
	rhNotify1 = theNotify[0].hEventNotify;
	rhNotify2 = theNotify[1].hEventNotify;
	dwBufferBytes = theDesc.dwBufferBytes;

	ELog::Get().DebugFormat(L"AUDIO   : + Allocated DirectSound buffer 0x%p (%d-bit, %S, %dHz)\n",
		pNewBuffer, nBitsPerSample, bStereo?"stereo":"mono", nFrequencyInHz);
	return pNewBuffer;
}

On a possibly related note, is there anything I should be aware of when using DirectSound multithreaded? I currently wrap all my buffer accesses in a critical section, but I don't know if there's any rules about what threads can and cannot do things to a buffer. Searching through the SDK docs only brings up one relevant result which says that the thread that calls IDirectSoundBuffer8::Play() must live until the buffer is stopped - which it does, since it's the main thread calling play and stop, the worker thread only waits on handles and locks, fills and unlocks the buffers. This is all in C++, Feb 2007 DirectX 9 SDK Cheers, Steve

Share this post


Link to post
Share on other sites
I'm assuming you have onboard audio, right?. In my experience hardware notifications are unreliable on most audio devices, particularly on cheap ones, which means that in practice cannot be used. In my audio engine I've resorted to checking the read and write pointers of the buffer (created with DSBCAPS_GETCURRENTPOSITION2) every quarter of the buffer time duration and updating the buffer if needed. Another possibility is to use notifications with software buffers, which shouldn't be too expensive unless you are doing lots of 3D audio positioning, effects, etc... .

Share this post


Link to post
Share on other sites
Quote:
Original post by Abominacion
I'm assuming you have onboard audio, right?. In my experience hardware notifications are unreliable on most audio devices, particularly on cheap ones, which means that in practice cannot be used. In my audio engine I've resorted to checking the read and write pointers of the buffer (created with DSBCAPS_GETCURRENTPOSITION2) every quarter of the buffer time duration and updating the buffer if needed. Another possibility is to use notifications with software buffers, which shouldn't be too expensive unless you are doing lots of 3D audio positioning, effects, etc... .
Yup, on board sound. Creating the buffer with DSBCAPS_LOCSOFTWARE seems to have solved the problem though, thanks.

Is this going to cause me any performance problems? I doubt it'll make much difference on my crappy on board card, really [smile]

Also, is there any way to tell when the driver is going to lie to me like this? That'd allow me to only tell it to create the sound buffer in software if I need to.

Thanks again,
Steve

Share this post


Link to post
Share on other sites
Just doing software mixing is not going to hurt much if you don't have an obscene amount of sounds playing at the same time, and they are the same bitdepth, frequency and number of channels. If you start doing lots of 3D audio and doppler effects and whatnot, then yes, doing that in software may take a sizable chunk of your CPU resources. But don't take my word for it, profile both methods and see how much they cost.

As for enabling hardware notifications at runtime, I know they worked fine on my previous computer Audigy ZS2, so they'll most likely work on any modern Creative board. You could try checking the device name, and if it's "Creative", then use them. You could also default to hardware notifications, and have a checkbox to enable software mixing if the user has sound problems. It's kind of lame, but I've seen some commercial games doing it that way.

Share this post


Link to post
Share on other sites
Quote:
Original post by Abominacion
Just doing software mixing is not going to hurt much if you don't have an obscene amount of sounds playing at the same time, and they are the same bitdepth, frequency and number of channels. If you start doing lots of 3D audio and doppler effects and whatnot, then yes, doing that in software may take a sizable chunk of your CPU resources. But don't take my word for it, profile both methods and see how much they cost.
That's true I suppose. I'll do some profiling once I have more of my engine complete. I think I'll only need notifications for streaming buffers, and I don't expect there to be more than one or two of them at once anyway.

Quote:
Original post by Abominacion
As for enabling hardware notifications at runtime, I know they worked fine on my previous computer Audigy ZS2, so they'll most likely work on any modern Creative board. You could try checking the device name, and if it's "Creative", then use them. You could also default to hardware notifications, and have a checkbox to enable software mixing if the user has sound problems. It's kind of lame, but I've seen some commercial games doing it that way.
Searching for "Creative" in the vendor string sounds like a horrible idea [smile] I think I'll go for software processing by default and add a checkbox to enable hardware mode.

Thanks again for your help.

Share this post


Link to post
Share on other sites
Random update for anyone who cares: My Creative Sound Blaster Audigy II doesn't work with two hardware streaming buffers either. It's not a great card, but I wouldn't have considered it a particularly crap card.
I've been through my code, and I'm 99% certain that it's not my code that's at fault - the events are set by DirectSound at the wrong times.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this