Sign in to follow this  

Threads and Direct Sound

This topic is 4688 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Now I've been having this one problem that's driving me insane and it has partly to do with DirectSound but more to do with the thread itself. Now for some odd reason when I create the thread, any of the variables in the sound class can't be changed. So when the thread starts sound->m_threadactive = true; is set, but it won't set it so when Kill(); comes around it won't send the killthread signal to the thread to stop the loop. However if I set m_threadactive = true; after it's creation when it's time to kill the program it will run in an infinite while loop setting the kill handle for the thread.
class cSound
{
protected:
	cWindow* window;

	LPDIRECTSOUND8		m_sounddevice;
	LPDIRECTSOUNDBUFFER	m_bufferprimary;

	DWORD			m_maxchannels;
	short			m_totalbuffers;
	HANDLE*			m_events;
	short*			m_soundbufferid;
	cSoundLoader**	m_soundevents;

	HANDLE	m_threadhandle;
	DWORD	m_threadID;
	bool	m_threadactive;

	std::map<long, cSoundBufferManager*>	m_buffersused;
	std::map<long, cSoundBufferManager*>	m_buffersunused;


	bool	m_isloaded;

private:
	static DWORD WINAPI NotificationThread(void* lpParamater);

public:
	cSound(void);
	~cSound(void);

	bool Init(cWindow* pWindow, long samplerate, short bitspersample, short channels);

	LPDIRECTSOUND8		GetSoundDevice();
	LPDIRECTSOUNDBUFFER GetPrimaryBuffer();

	bool AcquireBuffer(cSoundLoader* psoundloader, cSoundBufferManager* psoundbuffermanager);
	bool UnAcquireBuffer(short bufferid, cSoundBufferManager* psoundbuffermanager);

	bool IsLoaded();

	void Kill();

};




// *************************************************
// Class: cSound
// Info: Sound class designed to encapsulate the device, primary buffer, and updating needs of several loader objects.
// *************************************************

	DWORD WINAPI cSound::NotificationThread(void* lpParamater)
	{
		cSound* sound = (cSound*)lpParamater;
		bool	active = true;
		DWORD	returnchan;
		DWORD	channel;
		MSG		msg;

		sound->m_threadactive = true;

		while (active == true){
			returnchan = MsgWaitForMultipleObjects((sound->m_maxchannels + 1), sound->m_events, FALSE, INFINITE, QS_ALLEVENTS);
			channel = returnchan - WAIT_OBJECT_0;

			if((channel >= 0) && (channel <= sound->m_maxchannels)){


			}
			else{
				if(channel == (sound->m_maxchannels + 1)){
					active = false;
				}
				if(channel > (sound->m_maxchannels + 1)){
					while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){
						if(msg.message == WM_QUIT){
							active = false;
							break;
						}
					}
				}
			}
		}

		sound->m_threadactive = false;

		return 0;
	};

	cSound::cSound(void)
		: window(NULL), m_sounddevice(NULL), m_bufferprimary(NULL) // Sound Variables
		, m_maxchannels(0), m_events(NULL), m_soundbufferid(NULL), m_soundevents(NULL), m_totalbuffers(0) // Buffer Variables
		, m_threadhandle(NULL), m_threadID(0), m_threadactive(false) // Thread Variables
		, m_isloaded(false) // Other
	{

	}

	cSound::~cSound(void)
	{
		Kill();	
	}

	bool cSound::Init(cWindow* pWindow, long samplerate, short bitspersample, short channels)
	{
		DSBUFFERDESC pribufdesc;
		WAVEFORMATEX wfex;
		DSCAPS devicecaps;

		Kill();
		assert(pWindow != NULL);
		if((window = pWindow) == NULL)
			return false;

		// Create the Device
		if(FAILED(DirectSoundCreate8(NULL, &m_sounddevice, NULL)))
			return false;

		// Set the Device priority
		if(FAILED(m_sounddevice->SetCooperativeLevel(window->GetWindow(), DSSCL_PRIORITY))){
			Kill();
			return false;
		}

		// Get Primary Buffer
		ZeroMemory(&pribufdesc, sizeof(DSBUFFERDESC));
		pribufdesc.dwSize        = sizeof(DSBUFFERDESC);
		pribufdesc.dwFlags       = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLVOLUME;
		pribufdesc.dwBufferBytes = 0;
		pribufdesc.lpwfxFormat   = NULL;
		if(FAILED(m_sounddevice->CreateSoundBuffer(&pribufdesc, &m_bufferprimary, NULL))){
			Kill();
			return FALSE;
		}

		// Set Primary Buffer Format
		ZeroMemory(&wfex, sizeof(WAVEFORMATEX)); 
		wfex.wFormatTag      = WAVE_FORMAT_PCM; 
		wfex.nChannels       = (WORD)channels;
		wfex.nSamplesPerSec  = samplerate;
		wfex.wBitsPerSample  = (WORD)bitspersample;
		wfex.nBlockAlign     = wfex.wBitsPerSample / 8 * wfex.nChannels;
		wfex.nAvgBytesPerSec = wfex.nSamplesPerSec * wfex.nBlockAlign;
		if(FAILED(m_bufferprimary->SetFormat(&wfex))){
			Kill();
			return false;
		}

		// Play primary buffer
		if(FAILED(m_bufferprimary->Play(0, 0, DSBPLAY_LOOPING))){
			Kill();
			return false;
		}

		// Get Device Caps
		devicecaps.dwSize = sizeof(DSCAPS); 
		if(FAILED(m_sounddevice->GetCaps(&devicecaps))){
			Kill();
			return false;
		}

		// Setup the max channels and their arrays
		m_maxchannels = devicecaps.dwMaxHwMixingAllBuffers - 1; // from (0 ~ Max - 1) as opposed to (1 ~ Max)

		m_events = new HANDLE[m_maxchannels + 1]; // Extra one is for stopping the thread
		m_soundbufferid = new short[m_maxchannels];
		m_soundevents = new cSoundLoader*[m_maxchannels];

		for(DWORD i = 0; (i <= m_maxchannels + 1); i++){
			if((m_events[i] = CreateEvent(NULL,FALSE,FALSE,NULL)) == NULL)
			return FALSE;
		}

		// Setup the processing thread
		if((m_threadhandle = CreateThread(NULL, 0, NotificationThread, (LPVOID)this, 0, &m_threadID)) == NULL){
			Kill();
			return false;
		}

		m_isloaded = true;
		return true;
	};

	void cSound::Kill()
	{
		// Kill the thread and free handles and variables. NOTE: If the thread doesn't get killed then the program can't fully shutdown.
		while(m_threadactive == true){
			SetEvent(m_events[m_maxchannels + 1]);
			Sleep(20);
		}

		if(m_threadhandle != NULL){
            CloseHandle(m_threadhandle);
			m_threadhandle = NULL;
		}
		m_threadID = NULL;
		m_threadactive = false;

		// Free all of the buffer variables
		for(std::map<long, cSoundBufferManager*>::iterator iunused = m_buffersused.begin(); iunused != m_buffersused.end(); iunused++){
			iunused->second->Free();
			delete iunused->second;
		}

		for(std::map<long, cSoundBufferManager*>::iterator iused = m_buffersused.begin(); iused != m_buffersused.end(); iused++){
			iused->second->Free();
			delete iused->second;
		}

		for(DWORD i = 0; (i <= m_maxchannels + 1); i++){
			if(m_events != NULL){
				CloseHandle(m_events[i]);
			}
		}

		m_maxchannels = 0;
		m_totalbuffers = 0;
		if(m_events)
            delete m_events;
		if(m_soundbufferid)
            delete m_soundbufferid;
		if(m_soundevents)
            delete m_soundevents;

		// Free device and primary buffer
		releaseCOM(m_bufferprimary);
		releaseCOM(m_sounddevice);

		// Remove window pointer and turn sound class off
		window = NULL;
		m_isloaded = false;		
	};




The error occurs at the top of the Kill function, or the NotificationThread. I'm not really sure why it's doing this.

Share this post


Link to post
Share on other sites
I don't really want to bump this, but this place is my last option for finding out whats wrong. Maybe I have made a mistake on the handlers? But that wouldn't explain why the NotificationThread can't set variables. I really can't explain it, from my point of view everything should work properly. If I start the thread it should turn threadactive on, and when the sound class is killed it should stay in a while loop until the thread is shutdown. Can anyone see any possible problems?

Thanks. :\

Share this post


Link to post
Share on other sites
You have to be careful, the compiler might be outsmarting you:

If not told otherwise the compiler assumes all variables being used in a thread, and not being changed from outside. You can use the keyword volatile for your bool flag to tell the compiler not to optimize things it shouldn't.

Even better, use Events to signal a working or kill state. Those are guaranteed to work threadsafe and you can't run into deadlocks accessing it.

Share this post


Link to post
Share on other sites
So declaring the thread a member of the sound object doesn't allow it to fully access the classes member variables? Also, if I declare the variable as volatile does that pose any problem with functions outside the class accessing it?

Share this post


Link to post
Share on other sites
Alright, here's an update. It's working better then it was before, but it's still not quitting the thread. I went through the thread with the debugger and the channel variable is always 0xffffffff, even if the events are being hit.

.h

class cSound
{
protected:
cWindow* window;

LPDIRECTSOUND8 m_sounddevice;
LPDIRECTSOUNDBUFFER m_bufferprimary;

DWORD m_maxchannels;
short m_totalbuffers;
HANDLE* m_events;
short* m_soundbufferid;
cSoundLoader** m_soundevents;

HANDLE m_threadhandle;
DWORD m_threadID;
bool m_threadactive;

std::map<long, cSoundBufferManager*> m_buffersused;
std::map<long, cSoundBufferManager*> m_buffersunused;


bool m_isloaded;

private:
static DWORD WINAPI NotificationThread(void* lpParamater);

public:
cSound(void);
~cSound(void);

bool Init(cWindow* pWindow, long samplerate, short bitspersample, short channels);

LPDIRECTSOUND8 GetSoundDevice();
LPDIRECTSOUNDBUFFER GetPrimaryBuffer();

bool AcquireBuffer(cSoundLoader* psoundloader, cSoundBufferManager* psoundbuffermanager);
bool UnAcquireBuffer(short bufferid, cSoundBufferManager* psoundbuffermanager);

bool IsLoaded();

void Kill();

};



.cpp

DWORD WINAPI cSound::NotificationThread(void* lpParamater)
{
LPVOID ThreadParam[5];
memcpy(&ThreadParam, lpParamater, sizeof(LPVOID)*5);

bool* threadactive = (bool*)ThreadParam[0];
DWORD* maxchannels = (DWORD*)ThreadParam[1];
HANDLE* events = (HANDLE*)ThreadParam[2];
short* soundbufferid = (short*)ThreadParam[3];
cSoundLoader** soundevents = (cSoundLoader**)ThreadParam[4];

bool active = true;
DWORD returnchan;
DWORD channel;
MSG msg;
char messagestr[32];

(*threadactive) = true;

while (active == true){
returnchan = MsgWaitForMultipleObjects(((*maxchannels) + 1), events, FALSE, INFINITE, QS_ALLEVENTS);
channel = returnchan - WAIT_OBJECT_0;

if((channel >= 0) && (channel <= (*maxchannels))){
sprintf(messagestr, "%i", channel);
MB(messagestr);

}
else{
if(channel == ((*maxchannels) + 1)){
active = false;
}
if(channel > ((*maxchannels) + 1)){
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){
if(msg.message == WM_QUIT){
active = false;
break;
}
}
}
}
}

(*threadactive) = false;

return 0;
};

bool cSound::Init(cWindow* pWindow, long samplerate, short bitspersample, short channels)
{
DSBUFFERDESC pribufdesc;
WAVEFORMATEX wfex;
DSCAPS devicecaps;
LPVOID ThreadParam[5];

Kill();
assert(pWindow != NULL);
if((window = pWindow) == NULL)
return false;

// Create the Device
if(FAILED(DirectSoundCreate8(NULL, &m_sounddevice, NULL)))
return false;

// Set the Device priority
if(FAILED(m_sounddevice->SetCooperativeLevel(window->GetWindow(), DSSCL_PRIORITY))){
Kill();
return false;
}

// Get Primary Buffer
ZeroMemory(&pribufdesc, sizeof(DSBUFFERDESC));
pribufdesc.dwSize = sizeof(DSBUFFERDESC);
pribufdesc.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLVOLUME;
pribufdesc.dwBufferBytes = 0;
pribufdesc.lpwfxFormat = NULL;
if(FAILED(m_sounddevice->CreateSoundBuffer(&pribufdesc, &m_bufferprimary, NULL))){
Kill();
return FALSE;
}

// Set Primary Buffer Format
ZeroMemory(&wfex, sizeof(WAVEFORMATEX));
wfex.wFormatTag = WAVE_FORMAT_PCM;
wfex.nChannels = (WORD)channels;
wfex.nSamplesPerSec = samplerate;
wfex.wBitsPerSample = (WORD)bitspersample;
wfex.nBlockAlign = wfex.wBitsPerSample / 8 * wfex.nChannels;
wfex.nAvgBytesPerSec = wfex.nSamplesPerSec * wfex.nBlockAlign;
if(FAILED(m_bufferprimary->SetFormat(&wfex))){
Kill();
return false;
}

// Play primary buffer
if(FAILED(m_bufferprimary->Play(0, 0, DSBPLAY_LOOPING))){
Kill();
return false;
}

// Get Device Caps
devicecaps.dwSize = sizeof(DSCAPS);
if(FAILED(m_sounddevice->GetCaps(&devicecaps))){
Kill();
return false;
}

// Setup the max channels and their arrays
m_maxchannels = devicecaps.dwMaxHwMixingAllBuffers;

m_events = new HANDLE[m_maxchannels + 1]; // Extra one is for stopping the thread
m_soundbufferid = new short[m_maxchannels];
m_soundevents = new cSoundLoader*[m_maxchannels];

m_maxchannels -= 1; // from (0 ~ Max - 1) as opposed to (1 ~ Max)

for(DWORD i = 0; i <= (m_maxchannels + 1); i++){
if((m_events[i] = CreateEvent(NULL,FALSE,FALSE,NULL)) == NULL){
Kill();
return false;
}
}

// Setup the processing thread
ThreadParam[0] = &m_threadactive;
ThreadParam[1] = &m_maxchannels;
ThreadParam[2] = m_events;
ThreadParam[3] = m_soundbufferid;
ThreadParam[4] = m_soundevents;

if((m_threadhandle = CreateThread(NULL, 0, NotificationThread, (LPVOID)&ThreadParam, 0, &m_threadID)) == NULL){
Kill();
return false;
}

while(m_threadactive != true){
Sleep(20);
}
m_isloaded = true;
return true;
};

void cSound::Kill()
{
// Kill the thread and free handles and variables. NOTE: If the thread doesn't get killed then the program can't fully shutdown.
while(m_threadactive == true){
SetEvent(m_events[0]); // Testing, and still nothing happens.
SetEvent(m_events[m_maxchannels + 1]);
Sleep(20); // Don't want to hog all of the CPU time, do you fatty?
}

if(m_threadhandle != NULL){
CloseHandle(m_threadhandle);
m_threadhandle = NULL;
}
m_threadID = NULL;
m_threadactive = false;

// Free all of the buffer variables
for(std::map<long, cSoundBufferManager*>::iterator iunused = m_buffersused.begin(); iunused != m_buffersused.end(); iunused++){
iunused->second->Free();
delete iunused->second;
}

for(std::map<long, cSoundBufferManager*>::iterator iused = m_buffersused.begin(); iused != m_buffersused.end(); iused++){
iused->second->Free();
delete iused->second;
}

for(DWORD i = 0; (i <= m_maxchannels + 1); i++){
if(m_events != NULL){
CloseHandle(m_events[i]);
}
}

m_maxchannels = 0;
m_totalbuffers = 0;
if(m_events)
delete m_events;
if(m_soundbufferid)
delete m_soundbufferid;
if(m_soundevents)
delete m_soundevents;

// Free device and primary buffer
releaseCOM(m_bufferprimary);
releaseCOM(m_sounddevice);

// Remove window pointer and turn sound class off
window = NULL;
m_isloaded = false;
};

Share this post


Link to post
Share on other sites

This topic is 4688 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

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