🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

XAudio2 and OGG

Started by
-1 comments, last by the-beast 16 years, 1 month ago
I've recently converted the DirectSound and OGG code at www.flipcode.com/archives/Ogg_Vorbis_Player_Class.shtml (with the help of the DirectX 9 March 2008 SDK examples) and produced the following code. Since there is an acute shortage of XAudio2 examples at the moment I've decided to make this code available on here to help others. Note; this code has been pulled straight from one of my projects and as such is not a tutorial but an example. As usual if anyone has any suggestions feel free to give them. To use this you will need to have the Direct X 9 SDK (I used the March 2008 version) and the OGG Vorbis stuff from http://xiph.org/downloads I downloaded the files libogg-1.1.3.zip and libvorbis-1.2.0.zip. Since their is no OGG SDK available you will have to compile these things manually. Once you have done you will need to link to the following OGG/Vorbis libraries: libogg.lib libvorbis.lib libvorbisfile.lib Place the following OGG/Vorbis files where your project can access then libogg.dll libvorbis.dll libvorbisfile.dll ogg.h os_types.h codec.h vorbisenc.h vorbisfile.h To use this class you need to do
 
CAudio* m_pAudioBg = new CAudio();
if (m_pAudioBg->InitialiseAudio())			//Setup OGG playback
m_pAudioBg->LoadSound("myFile.ogg");	//Sound to play - directory location hard coded into sound class

m_pAudioBg->Play();


Make sure you call m_pAudioBg->Update(); every so often otherwise the audio stream will starve and you will lose your audio To decrease the volume

volume = volume - 0.01;
if(volume < 0)
	volume = 0;

m_pAudioBg->AlterVolume(volume);

To increase the volume

volume = volume + 0.01;
if(volume > 1.0)
	volume = 1.0;

m_pAudioBg->AlterVolume(volume);

Audio.h

#pragma once
//#include "base.h"

#include <xaudio2.h>

#include <codec.h>
#include <vorbisfile.h>

#define STREAMING_BUFFER_SIZE 65536*10
#define MAX_BUFFER_COUNT 3

class CAudio //:
//public CBase
{
public:
	CAudio(void);
	virtual ~CAudio(void);

	bool IsPlaying();
	void Stop();
	bool Play(bool loop = true);
	bool LoadSound(const char* szSoundFilePath);
	bool InitialiseAudio();
	void AlterVolume(float fltVolume);
	void GetVolume(float &fltVolume);
	void Pause();
	void Update();

private:
	IXAudio2* pXAudio2;
	IXAudio2MasteringVoice* pMasteringVoice;
	IXAudio2SourceVoice* pSourceVoice;

	UINT32 flags;
	char buffers[MAX_BUFFER_COUNT][STREAMING_BUFFER_SIZE];
	bool bFileOpened;
	OggVorbis_File vf;
	bool isRunning;
	bool boolIsPaused;
	bool bAlmostDone;
	bool bDone;
	bool bLoop;
	DWORD currentDiskReadBuffer;

	void resetParams();
};

Audio.app

//Safely release and delete objects
#define SafeRelease(pInterface) if(pInterface != NULL) {pInterface->Release(); pInterface=NULL;}
#define SafeDelete(pObject) if(pObject != NULL) {delete pObject; pObject=NULL;}

#include "Audio.h"

//OGG handling code is from a tutorial @ www.flipcode.com

//Built with:
//OGG version 1.1.3
//Vorbis version 1.2.0

CAudio::CAudio(void)
{
	pXAudio2 = NULL;
	pMasteringVoice = NULL;
	pSourceVoice = NULL;

	resetParams();

#ifdef _DEBUG
	flags |= XAUDIO2_DEBUG_ENGINE;
#endif

	CoInitializeEx(NULL, COINIT_MULTITHREADED);

	//LogInfo("<li>Audio created OK");
}

CAudio::~CAudio(void)
{
	if (pSourceVoice != NULL)
	{
		pSourceVoice->Stop(0);
		pSourceVoice->DestroyVoice();
	}

	if (pMasteringVoice != NULL)
		pMasteringVoice->DestroyVoice();

	SafeRelease(pXAudio2);

	if (bFileOpened)
		ov_clear(&vf);

	CoUninitialize();

	//LogInfo("<li>Audio destroyed OK");
}

void CAudio::resetParams()
{
	bFileOpened = false;
	isRunning = false;
	boolIsPaused = false;
	bLoop = false;
	bDone = false;
	bAlmostDone = false;
	currentDiskReadBuffer = 0;
	flags = 0;
}

bool CAudio::InitialiseAudio()
{
	HRESULT hr;
	if (FAILED(hr = XAudio2Create( &pXAudio2, flags)))
	{
		//LogError("<li>Failed to init XAudio2 engine: %#X", hr );
		return false;
	}

	if (FAILED(hr = pXAudio2->CreateMasteringVoice(&pMasteringVoice)))
	{
		//LogError("<li>Failed creating mastering voice: %#X", hr);
		return false;
	}

	return true;
}

bool CAudio::LoadSound(const char* szSoundFileName)
{
	//WCHAR wstrSoundPath[MAX_PATH];
	char strSoundPath[MAX_PATH];

	//Get the our applications "sounds" directory.
	GetCurrentDirectory(MAX_PATH, strSoundPath);
	strcat_s(strSoundPath, MAX_PATH, "\\Sounds\\");
	strcat_s(strSoundPath, MAX_PATH, szSoundFileName);

	//Convert the path to unicode.
	//MultiByteToWideChar(CP_ACP, 0, strSoundPath, -1, wstrSoundPath, MAX_PATH);

	//If we already have a file open then kill the current voice setup
	if (bFileOpened)
	{
		pSourceVoice->Stop(0);
		pSourceVoice->DestroyVoice();

		ov_clear(&vf);

		resetParams();
	}

	FILE *f;
	errno_t err;

	if ((err  = fopen_s( &f, strSoundPath, "rb" )) != 0)
	{
		//LogError("<li>Failed to open audio: %s", strSoundPath);

		char szBuffer[MAX_PATH];
		 _strerror_s(szBuffer, MAX_PATH, NULL);
		//LogError("<li>Reason: %s", szBuffer);
		return false;
	}

	//ov_open(f, &vf, NULL, 0);	//Windows does not like this function so we use ov_open_callbacks() instead
	if (ov_open_callbacks(f, &vf, NULL, 0, OV_CALLBACKS_DEFAULT) < 0)
	{
		fclose(f);
		return false;
	}
	else
		bFileOpened = true;

	//The vorbis_info struct keeps the most of the interesting format info
	vorbis_info *vi = ov_info(&vf, -1);

	//Set the wave format
	WAVEFORMATEX wfm;
	memset(&wfm, 0, sizeof(wfm));

	wfm.cbSize          = sizeof(wfm);
 	   wfm.nChannels       = vi->channels;
	wfm.wBitsPerSample  = 16;                    //Ogg vorbis is always 16 bit
	wfm.nSamplesPerSec  = vi->rate;
	wfm.nAvgBytesPerSec = wfm.nSamplesPerSec * wfm.nChannels * 2;
 	wfm.nBlockAlign     = 2 * wfm.nChannels;
	wfm.wFormatTag      = 1;

	DWORD pos = 0;
	int sec = 0;
	int ret = 1;

	memset(&buffers[currentDiskReadBuffer], 0, sizeof(buffers[currentDiskReadBuffer]));

	//Read in the bits
	while(ret && pos<STREAMING_BUFFER_SIZE)
	{
		ret = ov_read(&vf, buffers[currentDiskReadBuffer]+pos, STREAMING_BUFFER_SIZE-pos, 0, 2, 1, &sec);
		pos += ret;
	}

	HRESULT hr;

	//Create the source voice
	if (FAILED(hr = pXAudio2->CreateSourceVoice(&pSourceVoice, &wfm)))
	{
		//LogError("<li>Error %#X creating source voice", hr);
		return false;
	}

	//Submit the wave sample data using an XAUDIO2_BUFFER structure
	XAUDIO2_BUFFER buffer = {0};
	buffer.pAudioData = (BYTE*)&buffers[currentDiskReadBuffer];
  	buffer.AudioBytes = STREAMING_BUFFER_SIZE;
	
	if(FAILED(hr = pSourceVoice->SubmitSourceBuffer(&buffer)))
	{
		//LogError("<li>Error %#X submitting source buffer", hr);
		return false;
	}

	currentDiskReadBuffer++;

	return true;
}

bool CAudio::Play(bool loop)
{
	if (pSourceVoice == NULL)
	{
		//LogError("<li>Error: pSourceVoice NOT created");
		return false;
	}

	HRESULT hr;

	if(FAILED(hr = pSourceVoice->Start(0)))
	{
		//LogError("<li>Error %#X submitting source buffer", hr);
	}

	XAUDIO2_VOICE_STATE state;
	pSourceVoice->GetState(&state);
	isRunning = (state.BuffersQueued > 0) != 0;

	bLoop = loop;
	bDone = false;
	bAlmostDone = false;
	boolIsPaused = false;

	return isRunning;
}

void CAudio::Stop()
{
	if (pSourceVoice == NULL)
		return;

	//XAUDIO2_FLUSH_BUFFERS according to MSDN is meant to flush the buffers after the voice is stopped
	//unfortunately the March 2008 release of the SDK does not include this parameter in the xaudio files
	//and I have been unable to ascertain what its value is
	//pSourceVoice->Stop(XAUDIO2_FLUSH_BUFFERS);
	pSourceVoice->Stop(0);

	boolIsPaused = false;
	isRunning = false;
}

bool CAudio::IsPlaying()
{
	/*XAUDIO2_VOICE_STATE state;
	pSourceVoice->GetState(&state);
	return (state.BuffersQueued > 0) != 0;*/

	return isRunning;
}


//Alter the volume up and down
void CAudio::AlterVolume(float fltVolume)
{
	if (pSourceVoice == NULL)
		return;

	pSourceVoice->SetVolume(fltVolume);			//Current voice volume
	//pMasteringVoice->SetVolume(fltVolume);	//Playback device volume
}

//Return the current volume
void CAudio::GetVolume(float &fltVolume)
{
	if (pSourceVoice == NULL)
		return;

	pSourceVoice->GetVolume(&fltVolume);
	//pMasteringVoice->GetVolume(&fltVolume);
}

void CAudio::Pause()
{
	if (pSourceVoice == NULL)
		return;

	if (boolIsPaused)
	{
		pSourceVoice->Start(0);	//Unless we tell it otherwise the voice resumes playback from its last position
		boolIsPaused = false;
	}
	else
	{
		pSourceVoice->Stop(0);
		boolIsPaused = true;
	}
}

void CAudio::Update()
{
	if (pSourceVoice == NULL)
		return;

	if (!isRunning)
		return;

	//Do we have any free buffers?
    XAUDIO2_VOICE_STATE state;
    pSourceVoice->GetState( &state );
	if ( state.BuffersQueued < MAX_BUFFER_COUNT - 1 )
	{
		if (bDone && !bLoop)
		{
			pSourceVoice->Stop(0);
		}

		//Got to use this trick because otherwise all the bits wont play
		if (bAlmostDone && !bLoop)
			bDone = true;

		memset(&buffers[currentDiskReadBuffer], 0, sizeof(buffers[currentDiskReadBuffer]));

		DWORD pos = 0;
        	int sec = 0;
        	int ret = 1;
                
        	while(ret && pos<STREAMING_BUFFER_SIZE)
        	{
            		ret = ov_read(&vf, buffers[currentDiskReadBuffer]+pos, STREAMING_BUFFER_SIZE-pos, 0, 2, 1, &sec);
            		pos += ret;
        	}

        	//Reached the end?
        	if (!ret && bLoop)
        	{
			//We are looping so restart from the beginning
			//NOTE: sound with sizes smaller than BUFSIZE may be cut off

			ret = 1;
			ov_pcm_seek(&vf, 0);
			while(ret && pos<STREAMING_BUFFER_SIZE)
			{
				ret = ov_read(&vf, buffers[currentDiskReadBuffer]+pos, STREAMING_BUFFER_SIZE-pos, 0, 2, 1, &sec);
				pos += ret;
			}
		}
		else if (!ret && !(bLoop))
        	{
            		//Not looping so fill the rest with 0
            		//while(pos<size)
            		//    *(buffers[currentDiskReadBuffer]+pos)=0; pos ++;

            		//And say that after the current section no other section follows
            		bAlmostDone = true;
        	}

		XAUDIO2_BUFFER buffer = {0};
		buffer.pAudioData = (BYTE*)&buffers[currentDiskReadBuffer];
		if (bAlmostDone)
			buffer.Flags = XAUDIO2_END_OF_STREAM;	//Tell the source voice not to expect any data after this buffer
		buffer.AudioBytes = STREAMING_BUFFER_SIZE;

		HRESULT hr;
		if( FAILED(hr = pSourceVoice->SubmitSourceBuffer( &buffer ) ) )
		{
			//LogError("<li>Error %#X submitting source buffer\n", hr );
			return;
		}
		
		currentDiskReadBuffer++;
		currentDiskReadBuffer %= MAX_BUFFER_COUNT;
	}
}

I use 2 functions called LogInfo and LogError to log stuff. The code for these functions is not available but I've left the commented out calls in the code so that you can implement your own logger. I hope this will prove to be of some help to people. Edited 10/09/2009: Finally had some spare time to tidy the format of the post up. [Edited by - the-beast on August 10, 2009 5:41:35 AM]

This topic is closed to new replies.

Advertisement