Tag Archives: xWMA

xWMA files

When working on my engine, I often come across points where information on how to solve a problem is lacking. I was going to make this post on encryption, but that will be a lengthy post, so I will tackle an easy topic. I decided to work a little on sound and figured that since i was already using the Directx Library, I might as well continue, right? So, I loaded up the SDK, and loaded up the XAudio2BasicSound tutorial. The tutorial itself is decent, but one part is missing: how to load xWMA files. After doing a little research, I found this post. If you scroll down to a post made by Jammy1986, you will see that he attached some code for people to learn from. I am always critical of the-right-way-to-do-something, and while the information I needed was taken from his post; his code was absolutely horrendous. I mean, why would anyone write code like that? How can someone think to themselves that that is the right way to code. There is no reason to post such bad code, but I digress, I learned what I needed from it, so it was not all that bad –I guess. (As you can probably tell, I am very nit picky with at least some common sense in doing things.)

So, here is the code, it is basically a cleaned up version of the tutorial, I removed alot of the windows file handling crap that was in the code. To me, this code is much cleaner and easier to understand than what I started with. This will load either waves or xwma files.

As always, do with this code as you wish it is free for any use. Hopefully, someone can use it for some good.

cSoundEngine.h

cSoundEngine.cpp

 

//HEADER

#ifndef CSOUNDENGINE_H
#define CSOUNDENGINE_H
#include <xaudio2.h>
#include <xaudio2fx.h>
#include <string>

class cSoundEngine {
public:

    IXAudio2* pXAudio2;
    IXAudio2MasteringVoice* pMasteringVoice;
    IXAudio2SourceVoice* pSourceVoice;
    BYTE* pbSampleData;
	UINT32* xwmabuffer;

	cSoundEngine();
	~cSoundEngine();

	bool Init();
	void DeInit();

	void Play2D(const std::string& file);

};

extern cSoundEngine SoundEngine;
#endif

////////////////////////////////CPP FILE

#include "cSoundEngine.h"
#include <dxerr.h>
#include <iostream>
#include <fstream>

cSoundEngine SoundEngine;
// little endian format
#define fourccRIFF 'FFIR'
#define fourccDATA 'atad'
#define fourccFMT ' tmf'
#define fourccWAVE 'EVAW'
#define fourccXWMA 'AMWX'
#define fourccDPDS 'sdpd'

#define DEBUG 1// DEBUG IS ON,, GO DEBUG!!
#if defined(DEBUG) | defined(_DEBUG)
	#ifndef HR
	#define HR(x){																\
		HRESULT hr = (x);														\
		if(FAILED(hr)){															\
			DXTraceA(__FILE__, (DWORD)__LINE__, hr, #x, true);					\
		}																		\
	}
	#endif
	#ifndef OUTPUT_DEBUG_MSG // nothing special about this, I use the console for standard debug info
	#define OUTPUT_DEBUG_MSG(x) {												\
		std::cout<<x<<std::endl;											    \
	}
	#endif
#else
	#ifndef HR
	#define HR(x) (x)
	#endif
	#ifndef OUTPUT_ERROR_MSG
	#define OUTPUT_ERROR_MSG(x) (x)
	#endif
	#ifndef OUTPUT_DEBUG_MSG
	#define OUTPUT_DEBUF_MSG(x) (x)
	#endif
#endif 

// no need to check if(x), in c++, delete always checks that before deleting
#ifndef RELEASECOM
#define RELEASECOM(x) { if(x) x->Release(); x=0; }
#endif
#ifndef DELETE_ARRAY
#define DELETE_ARRAY(x) { delete[] (x);  x=NULL; }
#endif

cSoundEngine::cSoundEngine(){
	pXAudio2=0;
    pMasteringVoice=0;
    pSourceVoice=0;
	xwmabuffer=0;
	pbSampleData=0;

}
cSoundEngine::~cSoundEngine(){
	DeInit();
}

bool cSoundEngine::Init(){
	CoInitializeEx(NULL, COINIT_MULTITHREADED);
	HR(XAudio2Create( &pXAudio2, 0 )) ;
    HR(pXAudio2->CreateMasteringVoice( &pMasteringVoice ) );
	return true;
}
void cSoundEngine::DeInit(){
    if( pSourceVoice ){
        pSourceVoice->DestroyVoice();
        pSourceVoice = NULL;
    }

    if( pMasteringVoice ) {
        pMasteringVoice->DestroyVoice();
        pMasteringVoice = NULL;
    }
	if(pXAudio2){
		pXAudio2->StopEngine();
		RELEASECOM( pXAudio2 );
	}
	DELETE_ARRAY(xwmabuffer);
	DELETE_ARRAY(pbSampleData);
    CoUninitialize();
}
bool FindChunk(std::ifstream& stream, DWORD fourcc, DWORD& dwChunkSize, DWORD& dwChunkDataPosition){
	stream.seekg(0, std::ios::beg);
    DWORD dwChunkType(0), dwChunkDataSize(0), dwRIFFDataSize(0), dwFileType(0), dwOffset(0);
	while(!stream.eof()){// as long as we have not hit the end of the file
		// riff standard is always type, followied by the chunk size. They are always 4 bytes too,
		stream.read(reinterpret_cast<char*>(&dwChunkType), 4);
		stream.read(reinterpret_cast<char*>(&dwChunkDataSize), 4);
        if( fourccRIFF ==dwChunkType){// at the riff header
            dwRIFFDataSize = dwChunkDataSize;
            dwChunkDataSize = 4;
			stream.read(reinterpret_cast<char*>(&dwFileType), 4);
		} else stream.seekg(dwChunkDataSize, std::ios::cur);// skip this chunk
         dwOffset += sizeof(DWORD) * 2;
        if (dwChunkType == fourcc){// found what we were looking for
            dwChunkSize = dwChunkDataSize;
            dwChunkDataPosition = dwOffset;
            return true;
        }
        dwOffset += dwChunkDataSize;
    }
	char* temp = reinterpret_cast<char*>(&fourcc);
	OUTPUT_DEBUG_MSG("Could not find the chunk "<<temp[0]<<temp[1]<<temp[2]<<temp[3]);
	return false;// if this is hit, it means what we were searching for was not found
}
void ReadChunkData(std::ifstream& stream, void * buffer, DWORD buffersize, DWORD bufferoffset){
	stream.seekg(bufferoffset, std::ios::beg);
	stream.read(reinterpret_cast<char*>(buffer), buffersize);
}

void cSoundEngine::Play2D(const std::string& file){
	std::ifstream f(file.c_str(), std::ios::beg | std::ios::binary);
	if(!f){
		OUTPUT_DEBUG_MSG("Could not play 2D sound due to file: " +file +" not found!");
		return;
	}
	WAVEFORMATEXTENSIBLE wfx = {0};
	XAUDIO2_BUFFER buffer = {0};
	buffer.Flags = XAUDIO2_END_OF_STREAM; // tell the source voice not to expect any data after this buffer
	buffer.LoopCount =XAUDIO2_LOOP_INFINITE;// loop over and over
	XAUDIO2_BUFFER_WMA wmaBuffer = {0};
	XAUDIO2_BUFFER_WMA* tem =0;// this is always used
	DWORD dwChunkSize;
	DWORD dwChunkPosition;
	//check the file type, should be fourccWAVE or 'XWMA'
	FindChunk(f,fourccRIFF,dwChunkSize, dwChunkPosition );
	DWORD filetype;
	ReadChunkData(f, &filetype, sizeof(DWORD), dwChunkPosition);
	if (filetype != fourccWAVE){
		if(filetype != fourccXWMA){
			f.close();
			OUTPUT_DEBUG_MSG("File is not a .wav, or a xwma file... uh oh");
			return;
		}
	}
	FindChunk(f,fourccFMT, dwChunkSize, dwChunkPosition );
	ReadChunkData(f, &wfx, dwChunkSize, dwChunkPosition );
	FindChunk(f,fourccDATA,dwChunkSize, dwChunkPosition );
	pbSampleData = new BYTE[dwChunkSize];
	buffer.AudioBytes = dwChunkSize;  //buffer containing audio data
	buffer.pAudioData = pbSampleData;  //size of the audio buffer in bytes
	ReadChunkData(f, pbSampleData, dwChunkSize, dwChunkPosition);
	if(filetype == fourccXWMA){// the file is an xwma file... we need an extra peice of data
		FindChunk(f,fourccDPDS, dwChunkSize, dwChunkPosition );
		// the chunksize is how many bytess there are, so we have to divide by sizeof(uint32t) to get the number of uint32's in the buffer
		//so chunksize will be like 24, but that is how many bytes, we need 24/4 which is 6. Because there are 6 uint32's
		wmaBuffer.PacketCount = dwChunkSize / sizeof(UINT32);
		xwmabuffer = new UINT32[wmaBuffer.PacketCount];
		ReadChunkData(f, xwmabuffer, dwChunkSize, dwChunkPosition);
		wmaBuffer.pDecodedPacketCumulativeBytes= xwmabuffer;
		tem=&wmaBuffer;// set this to be passed for correctly playing xwma files
	}
	f.close();
	HR(pXAudio2->CreateSourceVoice( &pSourceVoice, (WAVEFORMATEX*)&wfx) );
	HR(pSourceVoice->SubmitSourceBuffer( &buffer, tem ) );
	HR(pSourceVoice->Start( 0, XAUDIO2_COMMIT_NOW ) );

}