Why does the Page Faults of audiodg.exe increase when I call functions from the xaudio2 library to play WAV audio files?

Ruxin Ji 0 Reputation points
2024-02-02T05:56:36.7733333+00:00

I use the following code to play the system default audio file. After repeating the playback for a while, the Page Faults of audiodg.exe increase. Is there any mistake in my usage? During runtime, you can create an MFC Dialog to invoke the following code, and it requires referencing xaudio2.lib. I ran it for approximately 10 hours, and there is a noticeable increase in Page Faults of audiodg.exe.

#pragma once

class XAudioPlayer;
class Command
{
public:
	explicit Command() = default;
	virtual ~Command() = default;
public:
	virtual void Execute(XAudioPlayer* player) = 0;
};
using CommandPtr = shared_ptr<Command>;

class CommandQueue
{
public:
	explicit CommandQueue() = default;
	virtual ~CommandQueue();
public:
	CommandPtr Pop();
	void Push(CommandPtr command);
	void Clear();
private:
	mutex _mutex;
	list<CommandPtr> _commands;
};

class XAudio_PlayCommand : public Command
{
public:
	explicit XAudio_PlayCommand(
		CString filePath,
		UINT beepRate)
	{
		_filePath = filePath;
		_beepRate = beepRate;
	}
	virtual ~XAudio_PlayCommand() = default;
private:
	void Execute(XAudioPlayer* player) override;
private:
	CString _filePath;
	UINT _beepRate;
};

class XAudio_StopCommand : public Command
{
public:
	explicit XAudio_StopCommand() = default;
	virtual ~XAudio_StopCommand() = default;
private:
	void Execute(XAudioPlayer* player) override;
};

class XAudioPlayer : public IXAudio2VoiceCallback
{
	enum
	{
		LPH_SHUTDOWN = 0,
		LPH_CMD,
		LPH_COUNT
	};
	class XAudioBuffer
	{
	public:
		XAudioBuffer() = default;
		~XAudioBuffer()
		{
			if (hFile)
			{
				::CloseHandle(hFile);
			}
			if (buffer.pAudioData)
			{
				delete[] buffer.pAudioData;
			}
			if (pSourceVoice)
			{
				pSourceVoice = nullptr;
			}
		}
		IXAudio2SourceVoice* pSourceVoice = nullptr;
		XAUDIO2_BUFFER buffer = { 0 };
		HANDLE hFile = nullptr;
	};
	using XAudioBufferPtr = std::shared_ptr<XAudioBuffer>;
public:
	XAudioPlayer();
	virtual ~XAudioPlayer();
public:
	HRESULT initAudioPlayer();
	void sendPlaySoundCommand(CString filePath = L"", UINT beepRate = 1);
	void sendStopSoundCommand();
public:
	void processLoop();
	void processPlay(CString filePath, UINT beepRate);
	void processStop();
public:
	void STDMETHODCALLTYPE OnStreamEnd() override;
	void STDMETHODCALLTYPE OnVoiceProcessingPassStart(UINT32) override {}
	void STDMETHODCALLTYPE OnVoiceProcessingPassEnd() override {}
	void STDMETHODCALLTYPE OnBufferStart(void*) override {}
	void STDMETHODCALLTYPE OnBufferEnd(void*) override {}
	void STDMETHODCALLTYPE OnLoopEnd(void*) override {}
	void STDMETHODCALLTYPE OnVoiceError(void*, HRESULT) override {}
private:
	void startUp();
	void shutDown();
	void cleanUp();
private:
	void addCommand(CommandPtr command);
	HRESULT findChunk(HANDLE hFile, DWORD fourcc, DWORD& dwChunkSize, DWORD& dwChunkDataPosition);
	HRESULT readChunkData(HANDLE hFile, void* buffer, DWORD buffersize, DWORD bufferoffset);
private:
	CString _defaultSoundFile;
	CString _lastSoundFile;
	IXAudio2* _pXAudio2 = nullptr;
	IXAudio2MasteringVoice* _pMasterVoice = nullptr;
	atomic_bool _continueLoop = false;
	HANDLE _onStreamEnd = nullptr;
	std::map<CString, XAudioBufferPtr> _audioMap;
	CommandQueue _cmdQ;
	std::thread _thread;
	std::array<HANDLE, LPH_COUNT> _events = { nullptr };
	std::mutex _mutex;
	std::condition_variable _terminate;
};
using XAudioPlayerPtr = std::shared_ptr<XAudioPlayer>;

#include "pch.h"
#include "XAudioPlayer.h"

#define fourccRIFF 'FFIR'
#define fourccDATA 'atad'
#define fourccFMT ' tmf'
#define fourccWAVE 'EVAW'
#define fourccXWMA 'AMWX'
#define fourccDPDS 'sdpd'

#define DEFAULT_SOUND_PATH L"%SystemRoot%\\media\\Windows Background.wav"
#define DIRECT_SOUND_PATH L"C:\\Program Files (x86)\\Common Files\\Sound"
unsigned __stdcall _XAudioPlayer(LPVOID lpParameter)
{
	HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
	if (SUCCEEDED(hr))
	{
		auto p = reinterpret_cast<XAudioPlayer*>(lpParameter);
		if (nullptr != p)
		{
			p->processLoop();
		}
	}
	CoUninitialize();
	return 0;
}

XAudioPlayer::XAudioPlayer()
{
}

XAudioPlayer::~XAudioPlayer()
{
	cleanUp();
}

HRESULT XAudioPlayer::initAudioPlayer()
{
	WCHAR expandedPath[MAX_PATH] = { 0 };
	DWORD result = ExpandEnvironmentStrings(DEFAULT_SOUND_PATH, expandedPath, MAX_PATH);
	if (result > 0 && result <= MAX_PATH)
	{
		_defaultSoundFile = expandedPath;
	}

	HRESULT hr = E_FAIL;
	if (nullptr == _pXAudio2)
	{
		hr = XAudio2Create(&_pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR);
	}
	if (FAILED(hr) || nullptr == _pXAudio2)
	{
		return hr;
	}

	hr = _pXAudio2->CreateMasteringVoice(&_pMasterVoice);
	if (FAILED(hr) || nullptr == _pMasterVoice)
	{
		return hr;
	}
	startUp();
	return S_OK;
}

void XAudioPlayer::sendPlaySoundCommand(CString filePath, UINT beepRate)
{
	if (beepRate == 0)
	{
		return;
	}

	if (filePath.IsEmpty())
	{
		filePath = _defaultSoundFile;
	}
	else
	{
		filePath.Insert(0, DIRECT_SOUND_PATH);
	}
	sendStopSoundCommand();
	CommandPtr command = make_shared<XAudio_PlayCommand>(filePath, beepRate);
	addCommand(command);
}

void XAudioPlayer::sendStopSoundCommand()
{
	_continueLoop = false;
	SetEvent(_onStreamEnd);
	{
		auto locked = std::unique_lock<std::mutex>(_mutex);
		_terminate.notify_one();
	}
	CommandPtr command = make_shared<XAudio_StopCommand>();
	addCommand(command);
}

void XAudioPlayer::processLoop()
{
	HANDLE handles[2] = {
	   _events[LPH_SHUTDOWN],
	   _events[LPH_CMD]
	};

	while (true)
	{
		DWORD dw = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
		switch (dw)
		{
		case LPH_SHUTDOWN:
			return;
		case LPH_CMD:
		{
			auto p = _cmdQ.Pop();
			while (p)
			{
				p->Execute(this);
				p = _cmdQ.Pop();
			}
		}
		break;
		case WAIT_TIMEOUT:
			break;
		default:
			break;
		}
	}
}

void XAudioPlayer::processPlay(CString filePath, UINT beepRate)
{
	HRESULT hr = S_OK;
	_continueLoop = true;
	_lastSoundFile = filePath;
	auto item = std::make_shared<XAudioBuffer>();
	auto iter = _audioMap.find(filePath);
	if (iter == _audioMap.end())
	{
		const TCHAR* strFileName = filePath;
		HANDLE hFile = CreateFile(strFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
		if (INVALID_HANDLE_VALUE == hFile)
		{
			HRESULT_FROM_WIN32(GetLastError());
			return;
		}

		item->hFile = hFile;
		DWORD dwResult = SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
		if (INVALID_SET_FILE_POINTER == dwResult)
		{
			HRESULT_FROM_WIN32(GetLastError());
			return;
		}

		XAUDIO2_BUFFER buffer = { 0 };
		WAVEFORMATEXTENSIBLE wfx = { 0 };
		IXAudio2SourceVoice* pSourceVoice = nullptr;
		DWORD dwChunkSize = 0;
		DWORD dwChunkPosition = 0;
		hr = findChunk(hFile, fourccRIFF, dwChunkSize, dwChunkPosition);
		if (FAILED(hr))
		{
			return;
		}

		DWORD filetype;
		hr = readChunkData(hFile, &filetype, sizeof(DWORD), dwChunkPosition);
		if (FAILED(hr) || filetype != fourccWAVE)
		{
			return;
		}

		hr = findChunk(hFile, fourccFMT, dwChunkSize, dwChunkPosition);
		if (FAILED(hr))
		{
			return;
		}

		hr = readChunkData(hFile, &(wfx), dwChunkSize, dwChunkPosition);
		if (FAILED(hr))
		{
			return;
		}

		hr = findChunk(hFile, fourccDATA, dwChunkSize, dwChunkPosition);
		if (FAILED(hr))
		{
			return;
		}

		BYTE* pDataBuffer = new BYTE[dwChunkSize];
		hr = readChunkData(hFile, pDataBuffer, dwChunkSize, dwChunkPosition);
		if (FAILED(hr))
		{
			return;
		}

		buffer.AudioBytes = dwChunkSize;
		buffer.pAudioData = pDataBuffer;
		buffer.Flags = XAUDIO2_END_OF_STREAM;
		item->buffer = buffer;
		hr = _pXAudio2->CreateSourceVoice(&pSourceVoice, (WAVEFORMATEX*)&(wfx), 0, XAUDIO2_DEFAULT_FREQ_RATIO, this);
		if (FAILED(hr) || nullptr == pSourceVoice)
		{
			_pMasterVoice->DestroyVoice();
			return;
		}
		item->pSourceVoice = pSourceVoice;
		_audioMap.insert(std::make_pair(filePath, item));
	}
	else
	{
		item = iter->second;
	}

	if (nullptr == item || nullptr == item->pSourceVoice)
	{
		return;
	}

	while (_continueLoop)
	{
		hr = item->pSourceVoice->SubmitSourceBuffer(&item->buffer);
		if (FAILED(hr))
		{
			item->pSourceVoice->DestroyVoice();
			return;
		}

		hr = item->pSourceVoice->Start(0, XAUDIO2_COMMIT_NOW);
		if (FAILED(hr))
		{
			item->pSourceVoice->DestroyVoice();
			return;
		}

		WaitForSingleObject(_onStreamEnd, INFINITE);
		if (_continueLoop)
		{
			auto locked = std::unique_lock<std::mutex>(_mutex);
			_terminate.wait_for(locked, std::chrono::milliseconds(beepRate * 1000));
		}
	}
}

void XAudioPlayer::processStop()
{
	if (_lastSoundFile.GetLength() == 0)
	{
		return;
	}

	auto iter = _audioMap.find(_lastSoundFile);
	if (iter != _audioMap.end())
	{
		auto audioBuffer = iter->second;
		if (audioBuffer && audioBuffer->pSourceVoice)
		{
			audioBuffer->pSourceVoice->Stop();
			audioBuffer->pSourceVoice->FlushSourceBuffers();
		}
	}
}

void __stdcall XAudioPlayer::OnStreamEnd()
{
	SetEvent(_onStreamEnd);
}

void XAudioPlayer::startUp()
{
	for (auto& itr : _events)
	{
		if (!itr)
		{
			itr = CreateEvent(nullptr, FALSE, FALSE, nullptr);
		}
	}
	_onStreamEnd = CreateEvent(nullptr, FALSE, FALSE, nullptr);

	if (!_thread.native_handle())
	{
		_thread = std::thread(&_XAudioPlayer, this);
	}
}

void XAudioPlayer::shutDown()
{
	sendStopSoundCommand();
	if (_thread.joinable())
	{
		SetEvent(_events[LPH_SHUTDOWN]);
		_thread.join();
	}

	if (_onStreamEnd)
	{
		::CloseHandle(_onStreamEnd);
	}
	_cmdQ.Clear();
}

void XAudioPlayer::cleanUp()
{
	shutDown();
	_audioMap.clear();
	if (_pMasterVoice)
	{
		_pMasterVoice->DestroyVoice();
		_pMasterVoice = nullptr;
	}
	if (_pXAudio2)
	{
		_pXAudio2->Release();
		_pXAudio2 = nullptr;
	}
}

void XAudioPlayer::addCommand(CommandPtr command)
{
	_cmdQ.Push(command);
	SetEvent(_events[LPH_CMD]);
}

HRESULT XAudioPlayer::findChunk(HANDLE hFile, DWORD fourcc, DWORD& dwChunkSize, DWORD& dwChunkDataPosition)
{
	HRESULT hr = S_OK;
	if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, 0, NULL, FILE_BEGIN))
	{
		return HRESULT_FROM_WIN32(GetLastError());
	}

	DWORD dwChunkType;
	DWORD dwChunkDataSize;
	DWORD dwRIFFDataSize = 0;
	DWORD dwFileType;
	DWORD bytesRead = 0;
	DWORD dwOffset = 0;

	while (hr == S_OK)
	{
		DWORD dwRead;
		if (0 == ReadFile(hFile, &dwChunkType, sizeof(DWORD), &dwRead, NULL))
			hr = HRESULT_FROM_WIN32(GetLastError());
		if (0 == ReadFile(hFile, &dwChunkDataSize, sizeof(DWORD), &dwRead, NULL))
			hr = HRESULT_FROM_WIN32(GetLastError());
		switch (dwChunkType)
		{
		case fourccRIFF:
		{
			dwRIFFDataSize = dwChunkDataSize;
			dwChunkDataSize = 4;
			if (0 == ReadFile(hFile, &dwFileType, sizeof(DWORD), &dwRead, NULL))
				hr = HRESULT_FROM_WIN32(GetLastError());
			break;
		}
		default:
		{
			if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, dwChunkDataSize, NULL, FILE_CURRENT))
				return HRESULT_FROM_WIN32(GetLastError());
		}
		}
		dwOffset += sizeof(DWORD) * 2;
		if (dwChunkType == fourcc)
		{
			dwChunkSize = dwChunkDataSize;
			dwChunkDataPosition = dwOffset;
			return S_OK;
		}
		dwOffset += dwChunkDataSize;
		if (bytesRead >= dwRIFFDataSize)
		{
			return S_FALSE;
		}
	}
	return S_OK;
}

HRESULT XAudioPlayer::readChunkData(HANDLE hFile, void* buffer, DWORD buffersize, DWORD bufferoffset)
{
	HRESULT hr = S_OK;
	if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, bufferoffset, NULL, FILE_BEGIN))
	{
		return HRESULT_FROM_WIN32(GetLastError());
	}
	DWORD dwRead = 0;
	if (0 == ReadFile(hFile, buffer, buffersize, &dwRead, NULL))
	{
		hr = HRESULT_FROM_WIN32(GetLastError());
	}
	return hr;
}

CommandQueue::~CommandQueue()
{
	Clear();
}

CommandPtr CommandQueue::Pop()
{
	lock_guard<mutex> lock(_mutex);
	if (true == _commands.empty())
	{
		return nullptr;
	}
	CommandPtr p = _commands.front();
	_commands.pop_front();
	return p;
}

void CommandQueue::Push(CommandPtr command)
{
	if (nullptr != command)
	{
		lock_guard<mutex> lock(_mutex);
		_commands.push_back(command);
	}
}

void CommandQueue::Clear()
{
	lock_guard<mutex> lock(_mutex);
	_commands.clear();
}

void XAudio_PlayCommand::Execute(XAudioPlayer* player)
{
	if (player)
	{
		player->processPlay(_filePath, _beepRate);
	}
}

void XAudio_StopCommand::Execute(XAudioPlayer* player)
{
	if (player)
	{
		player->processStop();
	}
}

void CMFCApplication3Dlg::OnBnClickedButton1()
{
    XAudioPlayerPtr player; 
	player = make_shared<XAudioPlayer>();
	player->initAudioPlayer();

	CString filePath;
	player->sendPlaySoundCommand(filePath, 1);
}
Windows 10
Windows 10
A Microsoft operating system that runs on personal computers and tablets.
11,197 questions
C++
C++
A high-level, general-purpose programming language, created as an extension of the C programming language, that has object-oriented, generic, and functional features in addition to facilities for low-level memory manipulation.
3,637 questions
{count} votes