
// zlib open source license
//
// Copyright (c) 2010 to 2013 David Forsgren Piuva
// 
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// 
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 
//    1. The origin of this software must not be misrepresented; you must not
//    claim that you wrote the original software. If you use this software
//    in a product, an acknowledgment in the product documentation would be
//    appreciated but is not required.
// 
//    2. Altered source versions must be plainly marked as such, and must not be
//    misrepresented as being the original software.
// 
//    3. This notice may not be removed or altered from any source
//    distribution.

#include "../stdafx.h"
#include "soundclass.h"
#include "math.h"

#define LoopForward(min,var,max) for(var=min;var<=max;var++)
#define LoopBackward(min,var,max) for(var=max;var>=min;var--)
#define LoopForwardStartAndLength(var,start,length) LoopForward(start,var,((start)+(length))-1)
#define LoopBackwardStartAndLength(var,start,length) LoopBackward(start,var,((start)+(length))-1)
#define LoopForwardLengthFromZero(var,length) LoopForward(0,var,(length)-1)
#define LoopBackwardLengthFromZero(var,length) LoopBackward(0,var,(length)-1)
#ifndef SAFE_DELETE
#define SAFE_DELETE(p)	   { if(p) { delete (p);	 (p)=NULL; } }
#endif
#ifndef SAFE_DELETE_ARRAY
#define SAFE_DELETE_ARRAY(p) { if(p) { delete[] (p);   (p)=NULL; } }
#endif
#ifndef SAFE_RELEASE
#define SAFE_RELEASE(p)	  { if(p) { (p)->Release(); (p)=NULL; } }
#endif

const int BitsInNumber = 10;
const int MaxPacketSize = (PowerOfTwo_0to15[BitsInNumber]) - 1;
const int MinLoopSize = ((2 + BitsInNumber) * 2) + 1;
const int RangeMultiplier = 128;
const float PositionWeight = 0.25f;

void SoundClass::ConstructorHelper(IDirectSound8* Engine,HWND Window, bool LoadAs3DSound, bool Editable, MessageQueue* NewMQ, float NewMetersPerDistanceUnit, int NewNumberOfCopies) {
	int I;
	if (Engine == NULL) {
		MQ->InsertMessage(L"SoundClass::SoundClass: Could not initiate without an engine.\n");
		Flag_Loaded = false;
		return;
	}
	MQ = NewMQ;
	m_pSoundEngineInterface = Engine;
	Flag_3DSound = LoadAs3DSound;
	Flag_IsEditable = Editable;
	Flag_NumberOfCopies = NewNumberOfCopies;
	MetersPerDistanceUnit = NewMetersPerDistanceUnit;
	LastPlayedIndex = 0;
	LastEnd = 0;
	R = 0;
	SoundCollection = new SoundCopy[Flag_NumberOfCopies];
	waveData = NULL;
	if (!SoundCollection) {
		MQ->InsertMessage(L"SoundClass::SoundClass: Could not allocate sound array.\n");
		Flag_Loaded = false;
		return;
	}
	
	Flag_Loaded = false;
	LoopForwardLengthFromZero(I,Flag_NumberOfCopies) {
		SoundCollection[I].SecondaryBuffer = NULL;
		SoundCollection[I].Secondary3DBuffer = NULL;
		SoundCollection[I].LastAssigned_3DPosition = DVector3(0,0,0);
		SoundCollection[I].LastAssigned_Volume = 1;
		SoundCollection[I].LastAssigned_Speed = 1;
	}
	
	// Get minimum and maximum frequency
	DSCAPS dscaps;
	memset(&dscaps, 0, sizeof(DSCAPS));
	dscaps.dwSize = sizeof(DSCAPS);
	if(FAILED(m_pSoundEngineInterface->GetCaps(&dscaps))) {
		MinFrequency = 11025;
		MaxFrequency = 44100;
		MQ->InsertMessage(L"SoundClass::SoundClass: Could not get sound capabilities.\n");
		Flag_Loaded = false;
		return;
	} else {
		MinFrequency = dscaps.dwMinSecondarySampleRate;
		MaxFrequency = dscaps.dwMaxSecondarySampleRate;
	}
}

// Create empty sound
SoundClass::SoundClass(IDirectSound8* Engine,HWND Window,int SampleRate, int Channels, long SamplesPerChannel,int NewNumberOfCopies, bool LoadAs3DSound, MessageQueue* NewMQ, float NewMetersPerDistanceUnit) {
	ConstructorHelper(Engine,Window,LoadAs3DSound,true,NewMQ,NewMetersPerDistanceUnit,NewNumberOfCopies);
	
	// Create an empty sound
	AllocateNewWaveData(SampleRate,Channels,SamplesPerChannel);
	
	// Reset to zero
	int t; int c;
	LoopForwardLengthFromZero(c,GetNumberOfChannels()) {
		LoopForwardLengthFromZero(t,GetSamplesPerChannel()) {
			WriteWaveData(t,c,0.0f);
		}
	}
	
	// Update buffers
	if (!UpdateBuffers()) {
		MQ->InsertMessage(L"SoundClass::SoundClass: Could not update buffers after creating an empty sound.\n");
		return;
	}
	
	Flag_Loaded = true;
}

// Load sound from file
SoundClass::SoundClass(IDirectSound8* Engine,HWND Window,wchar_t* Filename,int NewNumberOfCopies, bool LoadAs3DSound, bool Editable, MessageQueue* NewMQ, float NewMetersPerDistanceUnit) {
	ConstructorHelper(Engine,Window,LoadAs3DSound,Editable,NewMQ,NewMetersPerDistanceUnit,NewNumberOfCopies);
	
	// Load a wave file onto a secondary buffer.
	if(!Load(Filename)) {
		Flag_Loaded = false;
		return;
	}
	
	Flag_Loaded = true;
}

SoundClass::~SoundClass() {
	int I;
	SAFE_DELETE_ARRAY(waveData)
	LoopForwardLengthFromZero(I,Flag_NumberOfCopies) {
		Stop(I);
		SAFE_RELEASE(SoundCollection[I].SecondaryBuffer)
		SAFE_RELEASE(SoundCollection[I].Secondary3DBuffer)
	}
	SAFE_DELETE_ARRAY(SoundCollection)
}

long SoundClass::LinearToMillibel(float LinearVolume) {
	long Result;
	if (LinearVolume >= 1.0f) {
		// Full volume
		Result = 0;
	} else if (LinearVolume <= 0.0f) {
		// Absolute silence
		Result = -10000;
	} else {
		// log2 (x) = logy (x) / logy (2)
		Result = (long)(-300.0f * (log10f(1 / LinearVolume) / log10f(2.0f)));
	}
	if (Result > 0) {
		Result = 0;
	} else if (Result < -10000) {
		Result = -10000;
	}
	return Result;
}

bool SoundClass::SetVolume(int Index, float Volume) {
	if (Index < 0 || Index >= Flag_NumberOfCopies) { MQ->InsertMessage(L"SoundClass::SetVolume: Index is out of bound.\n"); return false; }
	if (SoundCollection[Index].SecondaryBuffer == NULL) { MQ->InsertMessage(L"SoundClass::SetVolume: SecondaryBuffer is NULL.\n"); return false; }
	// Set volume
	if(FAILED(SoundCollection[Index].SecondaryBuffer->SetVolume(LinearToMillibel(Volume)))) {
		MQ->InsertMessage(L"SoundClass::SetVolume: Could not set volume.\n");
		return false;
	} else {
		SoundCollection[Index].LastAssigned_Volume = Volume;
		return true;
	}
}

bool SoundClass::SetSpeed(int Index, float Speed) {
	DWORD Frequency;
	if (Index < 0 || Index >= Flag_NumberOfCopies) { MQ->InsertMessage(L"SoundClass::SetSpeed: Index is out of bound.\n"); return false; }
	if (SoundCollection[Index].SecondaryBuffer == NULL) { MQ->InsertMessage(L"SoundClass::SetSpeed: SecondaryBuffer is NULL.\n"); return false; }
	Frequency = (DWORD)((float)waveFileHeader.sampleRate * Speed);
	if (Frequency < MinFrequency) {
		Frequency = MinFrequency;
	} else if (Frequency > MaxFrequency) {
		Frequency = MaxFrequency;
	}
	if(FAILED(SoundCollection[Index].SecondaryBuffer->SetFrequency(Frequency))) {
		MQ->InsertMessage(L"SoundClass::SetSpeed: Could not set speed.\n");
		return false;
	} else {
		SoundCollection[Index].LastAssigned_Speed = Speed;
		return true;
	}
}

bool SoundClass::Set3DPosition(int Index, float X, float Y, float Z) {
	if (Index < 0 || Index >= Flag_NumberOfCopies) { MQ->InsertMessage(L"SoundClass::Set3DPosition: Index is out of bound.\n"); return false; }
	if (SoundCollection[Index].Secondary3DBuffer == NULL) { MQ->InsertMessage(L"SoundClass::Set3DPosition: The sound buffer was not created with 3D position capability.\n"); return false; }
	if (Flag_3DSound) {
		if(FAILED(SoundCollection[Index].Secondary3DBuffer->SetPosition(
		  X * MetersPerDistanceUnit,
		  Y * MetersPerDistanceUnit,
		  Z * MetersPerDistanceUnit,
		  DS3D_IMMEDIATE))) {
			MQ->InsertMessage(L"SoundClass::Set3DPosition: Could not set position.\n");
			return false;
		} else {
			SoundCollection[Index].LastAssigned_3DPosition = DVector3(X,Y,Z);
			return true;
		}
	} else {
		MQ->InsertMessage(L"SoundClass::Set3DPosition: Could not set position because the sound is not a 3D sound.\n");
		return false;
	}
}

bool SoundClass::Stop(int Index) {
	if (SoundCollection[Index].SecondaryBuffer == NULL) { MQ->InsertMessage(L"SoundClass::StopStop: SecondaryBuffer is NULL.\n"); return false; }
	if(FAILED(SoundCollection[Index].SecondaryBuffer->Stop())) {
		MQ->InsertMessage(L"SoundClass::Stop: Could not stop the sound.\n");
		return false;
	}
	return true;
}

bool SoundClass::Play(int Index, bool FromBeginning, bool Looping) {
	HRESULT hr;
	if (Index < 0 || Index >= Flag_NumberOfCopies) { MQ->InsertMessage(L"SoundClass::Play: Index is out of bound.\n"); return false; }
	if (SoundCollection[Index].SecondaryBuffer == NULL) { MQ->InsertMessage(L"SoundClass::Play: SecondaryBuffer is NULL.\n"); return false; }
	
	// Remember what index we played the last time
	LastPlayedIndex = Index;
	
	// Play from the beginning.
	if (FromBeginning) {
		if(FAILED(SoundCollection[Index].SecondaryBuffer->SetCurrentPosition(0))) {
			MQ->InsertMessage(L"SoundClass::Play: Could not reset time.\n"); return false;
		}
	}
	
	// Play the content of the sound buffer.
	if (Looping) {
		hr = SoundCollection[Index].SecondaryBuffer->Play(0, 0, DSBPLAY_LOOPING);
	} else {
		hr = SoundCollection[Index].SecondaryBuffer->Play(0, 0, 0);
	}
	
	if(FAILED(hr)) {
		MQ->InsertMessage(L"SoundClass::Play: Could not play the sound.\n");
		return false;
	}
	return true;
}

bool SoundClass::UpdateBuffers(void){
	int I;
	WAVEFORMATEX waveFormat;
	DSBUFFERDESC bufferDesc;
	IDirectSoundBuffer* tempBuffer;
	char* bufferPtr;
	long bufferSize;
	
	// Stop the sounds and release any old buffers
	if (Flag_Loaded) {
		LoopForwardLengthFromZero(I,Flag_NumberOfCopies) {
			Stop(I);
			SAFE_RELEASE(SoundCollection[I].SecondaryBuffer)
			SAFE_RELEASE(SoundCollection[I].Secondary3DBuffer)
		}
	}
	
	// Set the wave format of secondary buffer.
	waveFormat.wFormatTag = WAVE_FORMAT_PCM;
	waveFormat.nSamplesPerSec = waveFileHeader.sampleRate;
	waveFormat.wBitsPerSample = waveFileHeader.bitsPerSample;
	waveFormat.nChannels = waveFileHeader.numChannels;
	waveFormat.nBlockAlign = waveFileHeader.blockAlign;
	waveFormat.nAvgBytesPerSec = waveFileHeader.bytesPerSecond;
	waveFormat.cbSize = 0;
	
	// Set the buffer description of the secondary sound buffer that the wave file will be loaded onto.
	bufferDesc.dwSize = sizeof(DSBUFFERDESC);
	bufferDesc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY;
	if (Flag_3DSound) bufferDesc.dwFlags = bufferDesc.dwFlags | DSBCAPS_CTRL3D;
	bufferDesc.dwBufferBytes = waveFileHeader.dataSize;
	bufferDesc.dwReserved = 0;
	bufferDesc.lpwfxFormat = &waveFormat;
	bufferDesc.guid3DAlgorithm = GUID_NULL;
	
	LoopForwardLengthFromZero(I,Flag_NumberOfCopies) {
		// Create a temporary sound buffer with the specific buffer settings.
		if(FAILED(m_pSoundEngineInterface->CreateSoundBuffer(&bufferDesc, &tempBuffer, NULL))) {
			MQ->InsertMessage(L"SoundClass::UpdateBuffers: Could not create the temporary sound buffer.\n");
			return false;
		}
		
		// Test the buffer format against the direct sound 8 interface and create the secondary buffer.
		if (tempBuffer == NULL) {
			MQ->InsertMessage(L"SoundClass::UpdateBuffers: Could not create tempBuffer.\n");
			return false;
		} else {
			if(FAILED(tempBuffer->QueryInterface(IID_IDirectSoundBuffer8, (void**)&SoundCollection[I].SecondaryBuffer))) {
				MQ->InsertMessage(L"SoundClass::UpdateBuffers: Could not create the secondary sound buffer.\n");
				return false;
			}
		}
		
		// Release the temporary buffer.
		SAFE_RELEASE(tempBuffer)
		
		// Lock the secondary buffer to write wave data into it.
		if(FAILED(SoundCollection[I].SecondaryBuffer->Lock(0, waveFileHeader.dataSize, (void**)&bufferPtr, (DWORD*)&bufferSize, NULL, 0, 0))) {
			MQ->InsertMessage(L"SoundClass::UpdateBuffers: Could not lock the secondary sound buffer.\n");
			return false;
		}
		
		// Copy the wave data into the buffer.
		memcpy(bufferPtr, waveData, (int)waveFileHeader.dataSize);
		
		// Unlock the secondary buffer after the data has been written to it.
		if(FAILED(SoundCollection[I].SecondaryBuffer->Unlock((void*)bufferPtr, bufferSize, NULL, 0))) {
			MQ->InsertMessage(L"SoundClass::UpdateBuffers: Could not unlock the secondary buffer.\n");
			return false;
		}
		
		if (Flag_3DSound) {
			// Get the 3D interface to the secondary sound buffer.
			if(FAILED(SoundCollection[I].SecondaryBuffer->QueryInterface(IID_IDirectSound3DBuffer8, (void**)&SoundCollection[I].Secondary3DBuffer))) {
				MQ->InsertMessage(L"SoundClass::UpdateBuffers: Could not get the 3D interface to the secondary sound buffer.\n");
				return false;
			}
		}
	}
	return true;
}

float SoundClass::ReadWaveData(int Time, int Channel) {
	int Offset;
	if (!waveData) {
		if (!Flag_IsEditable) {
			MQ->InsertMessage(L"SoundClass::ReadWaveData: Only editable sounds can use this method.\n" );
			return 0.0f;
		} else {
			MQ->InsertMessage(L"SoundClass::ReadWaveData: The wave data does not exist.\n" );
			return 0.0f;
		}
	}
	if (Channel < 0 || Channel > waveFileHeader.numChannels - 1) {
		MQ->InsertMessage(L"SoundClass::ReadWaveData: The channel is out of bound.\n" );
		return 0.0f;
	}
	Offset = (Time * waveFileHeader.blockAlign) + (Channel * 2);
	if (Offset < 0 || (unsigned long)(Offset + 1) > waveFileHeader.dataSize - 1) {
		MQ->InsertMessage(L"SoundClass::ReadWaveData: The time is out of bound.\n" );
		return 0.0f;
	}
	if (waveFileHeader.bitsPerSample == 16) {
		return ((float)*(short int*)(waveData + Offset)) / 32767.0f;
	} else {
		MQ->InsertMessage(L"SoundClass::ReadWaveData: Unsupported bit depth!\n" );
		return 0.0f;
	}
}

void SoundClass::WriteWaveData(int Time, int Channel, float NewValue) {
	int Offset; int RoundedValue;
	if (!waveData) {
		if (!Flag_IsEditable) {
			MQ->InsertMessage(L"SoundClass::WriteWaveData: Only editable sounds can use this method.\n" );
			return;
		} else {
			MQ->InsertMessage(L"SoundClass::WriteWaveData: The wave data does not exist.\n" );
			return;
		}
	}
	if (Channel < 0 || Channel > waveFileHeader.numChannels - 1) {
		MQ->InsertMessage(L"SoundClass::WriteWaveData: The channel is out of bound.\n" );
		return;
	}
	Offset = (Time * waveFileHeader.blockAlign) + (Channel * 2);
	if (Offset < 0 || (unsigned long)(Offset + 1) > waveFileHeader.dataSize - 1) {
		MQ->InsertMessage(L"SoundClass::WriteWaveData: The time is out of bound.\n" );
		return;
	}
	if (waveFileHeader.bitsPerSample == 16) {
		// Scale and round to closest integer
		RoundedValue = FloatToClosestInt(NewValue * 32767.0f);
		// Clamp
		if (RoundedValue < 	-32768) {
			RoundedValue = -32768;
		} else if (RoundedValue > 32767) {
			RoundedValue = 32767;
		}
		// Write
		*(short int*)(waveData + Offset) = (short int)RoundedValue;
	} else {
		MQ->InsertMessage(L"WriteWaveData: Unsupported bit depth!\n" );
	}
}

void SoundClass::WriteNumber(int TheValue, ByteArray* OutputArray) {
	UINT16 ULength;
	//MQ->InsertMessage(L"SoundClass::WriteNumberWriteNumber %i using %i bits to %i.\n",TheValue,BitsInNumber,OutputArray->GetSizeInBits_ExcludingUnusedBits());
	long I;
	int PlaceFromLeftInULength;
	if (TheValue > MaxPacketSize) {
		MQ->InsertMessage(L"SoundClass::WriteNumberWARNING! WriteNumber: Maximum packet size is exceeded.\n");
	}
	ULength = (UINT16)TheValue;
	PlaceFromLeftInULength = 16 - BitsInNumber;
	LoopForward(2,I,BitsInNumber + 1) {
		OutputArray->InsertBit(GetBitFromUINT16(ULength,PlaceFromLeftInULength));
		PlaceFromLeftInULength++;
	}
}

long SoundClass::ReadNumber(ByteArray* InputArray) {
	UINT16 ULength;
	long I;
	int PlaceFromLeftInULength;
	int PlaceFromLeftInData;
	int Result;
	ULength = 0;
	PlaceFromLeftInULength = 16 - BitsInNumber;
	PlaceFromLeftInData = R;
	LoopForward(2,I,BitsInNumber + 1) {
		SetBitInUINT16(&ULength,PlaceFromLeftInULength,InputArray->GetBit(PlaceFromLeftInData));
		PlaceFromLeftInULength++;
		PlaceFromLeftInData++;
	}
	Result = (int)ULength;
	if (Result > MaxPacketSize) {
		MQ->InsertMessage(L"SoundClass::ReadNumberWARNING! ReadNumber: Maximum packet size is exceeded.\n");
	}
	//MQ->InsertMessage(L"SoundClass::ReadNumberReadNumber %i using %i bits from %i.\n",Result,BitsInNumber,R);
	return Result;
}

void SoundClass::CreateLoopPackage(long Start, long End, bool Bit, ByteArray* OutputArray) {
	//MQ->InsertMessage(L"SoundClass::CreateLoopPackageCreateLoopPackage: [%i..%i] containing %i.\n",Start,End-1,Bit);
	if(LastEnd != Start) {
		MQ->InsertMessage(L"SoundClass::CreateLoopPackageWARNING! CreateLoopPackage: LastEnd != Start");
	}
	//MQ->InsertMessage(L"SoundClass::CreateLoopPackageCreateLoopPackage: writing keyword using 2 bits.\n");
	OutputArray->InsertBit(false);
	OutputArray->InsertBit(Bit);
	WriteNumber(End - Start,OutputArray);
	LastEnd = End;
}

void SoundClass::CreateLoopPackages(long Start, long End, bool Bit, ByteArray* OutputArray) {
	long CurrentStart;
	//MQ->InsertMessage(L"SoundClass::CreateLoopPackagesCreateLoopPackages: [%i..%i] containing %i.\n",Start,End-1,Bit);
	if (End > Start) {
		CurrentStart = Start;
		while (CurrentStart < End) {
			CreateLoopPackage(CurrentStart, MinL(End,CurrentStart + MaxPacketSize), Bit, OutputArray);
			CurrentStart = CurrentStart + MaxPacketSize;
		}
	}
}

void SoundClass::CreateRawPackage(long Start, long End, ByteArray* InputArray, ByteArray* OutputArray) {
	long I;
	//MQ->InsertMessage(L"SoundClass::CreateRawPackageCreateRawPackage: [%i..%i]\n",Start,End-1);
	if(LastEnd != Start) {
		MQ->InsertMessage(L"SoundClass::CreateRawPackageWARNING! CreateLoopPackage: LastEnd != Start");
	}
	//MQ->InsertMessage(L"SoundClass::CreateRawPackageCreateLoopPackage: writing keyword using 2 bits.\n");
	OutputArray->InsertBit(true);
	OutputArray->InsertBit(false);
	WriteNumber(End - Start,OutputArray);
	LoopForward(Start,I,End-1) {
		OutputArray->InsertBit(InputArray->GetBit(I));
	}
	LastEnd = End;
}

void SoundClass::CreateRawPackages(long Start, long End, ByteArray* InputArray, ByteArray* OutputArray) {
	long CurrentStart;
	//MQ->InsertMessage(L"SoundClass::CreateRawPackagesCreateRawPackage: [%i..%i]\n",Start,End-1);
	if (End > Start) {
		CurrentStart = Start;
		while (CurrentStart < End) {
			CreateRawPackage(CurrentStart, MinL(End,CurrentStart + MaxPacketSize), InputArray, OutputArray);
			CurrentStart = CurrentStart + MaxPacketSize;
		}
	}
}

void SoundClass::RunLengthEncode(ByteArray* InputArray, ByteArray* OutputArray) {
	long InputSizeInBits; long RawStart; long LoopStart; int LastBit; int NewBit;
	InputSizeInBits = InputArray->GetSizeInBits();
	OutputArray->Resize(0);
	//MQ->InsertMessage(L"SoundClass::RunLengthEncodeRunLengthEncode: [0..%i]\n",InputSizeInBits-1);
	//MQ->InsertMessage(L"SoundClass::RunLengthEncodeInput:"); InputArray->PrintContent();
	RawStart = 0;
	LoopStart = 0;
	LastEnd = 0;
	R = 0;
	LastBit = 2; // 0 = false, 1 = true, 2 = Invalid
	while (R < InputSizeInBits) {
		NewBit = InputArray->GetBit(R);
		if ( R == InputSizeInBits - 1 ) {
			// Reached the end
			if ( NewBit == LastBit && (R + 1) - LoopStart >= MinLoopSize ) {
				if (RawStart < LoopStart) {
					//MQ->InsertMessage(L"SoundClass::RunLengthEncodeStoring final raw data before the final loop\n");
					CreateRawPackages(RawStart, LoopStart, InputArray, OutputArray);
				}
				//MQ->InsertMessage(L"SoundClass::RunLengthEncodeStoring final loop\n");
				CreateLoopPackages(LoopStart, R + 1, (LastBit > 0), OutputArray);
			} else {
				//MQ->InsertMessage(L"SoundClass::RunLengthEncodeStoring final raw data\n");
				CreateRawPackages(RawStart, R + 1, InputArray, OutputArray);
			}
		} else {
			// Just another bit
			if ( NewBit != LastBit ) {
				// The current loop is broken
				if (R - LoopStart >= MinLoopSize) {
					if (RawStart < LoopStart) {
						//MQ->InsertMessage(L"SoundClass::RunLengthEncodeStoring raw data before a loop\n");
						CreateRawPackages(RawStart, LoopStart, InputArray, OutputArray);
					}
					//MQ->InsertMessage(L"SoundClass::RunLengthEncodeStoring a loop\n");
					CreateLoopPackages(LoopStart, R, (LastBit > 0), OutputArray);
					RawStart = R;
				}
				LoopStart = R;
			}
		}
		LastBit = NewBit;
		R++;
	}
	// Ending keyword
	OutputArray->InsertBit(true);
	OutputArray->InsertBit(true);
	OutputArray->LeaveUnusedBits();
	//MQ->InsertMessage(L"SoundClass::RunLengthEncodeOutput:"); OutputArray->PrintContent();
}

bool SoundClass::RunLengthDecode(ByteArray* InputArray, ByteArray* OutputArray) {
	long I; long InputSizeInBits; long Length; int FirstBit; int SecondBit;
	InputSizeInBits = InputArray->GetSizeInBits();
	OutputArray->Resize(0);
	//MQ->InsertMessage(L"SoundClass::RunLengthDecodeRunLengthDecode: [0..%i]\n",InputSizeInBits-1);
	//MQ->InsertMessage(L"SoundClass::RunLengthDecodeInput:"); InputArray->PrintContent();
	Length = 0;
	R = 0;
	while (R < InputSizeInBits) {
		if (InputSizeInBits - R >= 2) {
			FirstBit = InputArray->GetBit(R);
			SecondBit = InputArray->GetBit(R+1);
			R = R + 2;
			if (FirstBit == 1 && SecondBit == 1) {
				// End
				//MQ->InsertMessage(L"SoundClass::RunLengthDecodeReached the end at %i.\n",R);
				return true;
			} else {
				if (InputSizeInBits - R >= (2 + BitsInNumber)) {
					Length = ReadNumber(InputArray);
					R = R + BitsInNumber;
					if (FirstBit == 0) {
						// Looped data
						//MQ->InsertMessage(L"SoundClass::RunLengthDecodeGenerating looped data of length %i.\n",Length);
						LoopForwardLengthFromZero(I,Length) {
							OutputArray->InsertBit((SecondBit > 0));
						}
					} else if (InputSizeInBits - R >= Length) {
						// Raw data
						//MQ->InsertMessage(L"SoundClass::RunLengthDecodeReading raw data of length %i from %i to %i.\n{",Length,R,R+(Length-1));
						LoopForwardLengthFromZero(I,Length) {
							//MQ->InsertMessage(L"SoundClass::RunLengthDecode%i",InputArray->GetBit(R+I));
							OutputArray->InsertBit(InputArray->GetBit(R+I));
						}
						R = R + Length;
						//MQ->InsertMessage(L"SoundClass::RunLengthDecode}\n",Length);
					} else {
						MQ->InsertMessage(L"SoundClass::RunLengthDecodeWARNING! Not enough remaining bits for the length.\n");
						return false;
					}
				} else {
					MQ->InsertMessage(L"SoundClass::RunLengthDecodeWARNING! Not enough remaining bits for keyword and length.\n");
					return false;
				}
			}
		} else {
			MQ->InsertMessage(L"SoundClass::RunLengthDecodeWARNING! Not enough remaining bits for the keyword.\n");
			return false;
		}
	}
	MQ->InsertMessage(L"SoundClass::RunLengthDecodeWARNING! Reached the end without ending.\n");
	return false;
}

void SoundClass::LosslessEncode(ByteArray* RefArray, ByteArray* ExtraBuffer) {
	// RefArray >> RefArray
	//RefArray->DeriveInModulo();
	
	// Don't flip more than one bit between adjacent values.
	// RefArray >> RefArray
	RefArray->SmoothByte();
	
	// Sort bits by significance.
	// RefArray >> ExtraBuffer
	RefArray->MakeCopyWithSortedBits(ExtraBuffer); 
	
	// Apply run length encoding
	RunLengthEncode(ExtraBuffer,RefArray);
}

void SoundClass::LosslessDecode(ByteArray* RefArray, ByteArray* ExtraBuffer) {
	// Reverse the run length encoding
	// RefArray >> ExtraBuffer
	if (!RunLengthDecode(RefArray,ExtraBuffer)) {
		MQ->InsertMessage(L"SoundClass::LosslessDecodeWARNING! Could not reverse the run length encoding.\n");
	}
	
	// The inverse to sorting bits by significance.
	// ExtraBuffer >> RefArray
	ExtraBuffer->MakeCopyWithSortedBits_Inverse(RefArray);
	
	// The inverse to smooth byte
	// RefArray >> RefArray
	RefArray->SmoothByte_Inverse();
	
	// RefArray >> RefArray
	//RefArray->DeriveInModulo_Inverse();
}

float SoundClass::NonLinearScale(float X) {
	if (X < 0) {
		return -sqrtf(-X);
	} else {
		return sqrtf(X);
	}
}

float SoundClass::LinearScale(float X) {
	if (X < 0) {
		return -(X * X);
	} else {
		return X * X;
	}
}

unsigned char SoundClass::FloatToByte(float F) {
	return RoundAndClampToByte((NonLinearScale(F) * RangeMultiplier) + 128);
}

float SoundClass::ByteToFloat(unsigned char B) {
	return LinearScale((float)(B - 128) / RangeMultiplier);
}

void SoundClass::AllocateNewWaveData(int SampleRate, int Channels, long SamplesPerChannel) {
	// Resize waveData.
	waveFileHeader.sampleRate = SampleRate;
	waveFileHeader.numChannels = Channels;
	waveFileHeader.dataSize = SamplesPerChannel * 2 * Channels;
	waveFileHeader.chunkSize = waveFileHeader.dataSize + 36;
	
	waveFileHeader.chunkId[0] = 'R';
	waveFileHeader.chunkId[1] = 'I';
	waveFileHeader.chunkId[2] = 'F';
	waveFileHeader.chunkId[3] = 'F';
	waveFileHeader.chunkSize = (2 * Channels * SamplesPerChannel) + 36;
		waveFileHeader.format[0] = 'W';
		waveFileHeader.format[1] = 'A';
		waveFileHeader.format[2] = 'V';
		waveFileHeader.format[3] = 'E';
		//---------------------
		waveFileHeader.subChunkId[0] = 'f';
		waveFileHeader.subChunkId[1] = 'm';
		waveFileHeader.subChunkId[2] = 't';
		waveFileHeader.subChunkId[3] = ' ';
		waveFileHeader.subChunkSize = 16;
			waveFileHeader.audioFormat = WAVE_FORMAT_PCM;
			waveFileHeader.numChannels = Channels;
			waveFileHeader.sampleRate = SampleRate;
			waveFileHeader.bytesPerSecond = SampleRate * 2 * Channels;
			waveFileHeader.blockAlign = 2 * Channels;
			waveFileHeader.bitsPerSample = 16;
		//---------------------
		waveFileHeader.dataChunkId[0] = 'd'; waveFileHeader.dataChunkId[1] = 'a'; waveFileHeader.dataChunkId[2] = 't'; waveFileHeader.dataChunkId[3] = 'a';
		waveFileHeader.dataSize = 2 * Channels * SamplesPerChannel;
			// Data
			// No padding byte is needed for 16 bit sounds.
	
	SAFE_DELETE_ARRAY(waveData)
	waveData = new char[waveFileHeader.dataSize];
	if(!waveData) { MQ->InsertMessage(L"SoundClass::AllocateNewWaveData: Could not allocate the sound buffer.\n"); }
}

bool SoundClass::Save(wchar_t* Filename) {
	if (!Flag_IsEditable) {
		MQ->InsertMessage(L"Only editable sounds can be saved.\n" );
		return false;
	}
	if (lengthOfString_wide(Filename) > 259) {
		MQ->InsertMessage(L"SoundClass::Save: The filename is too long.\n" );
		return false;
	}
	int L = lengthOfString_wide(Filename);
	if (Filename[L-4] == '.' && (Filename[L-3] == 'W' || Filename[L-3] == 'w') && (Filename[L-2] == 'A' || Filename[L-2] == 'a') && (Filename[L-1] == 'V' || Filename[L-1] == 'v')) {
		return SaveWaveFile(Filename);
	} else if (Filename[L-4] == '.' && (Filename[L-3] == 'D' || Filename[L-3] == 'd') && (Filename[L-2] == 'S' || Filename[L-2] == 's') && (Filename[L-1] == 'F' || Filename[L-1] == 'f')) {
		return SaveDSF1File(Filename);
	} else {
		MQ->InsertMessage(L"The file extension is not known.\n" );
		return false;
	}
}

// This should get an extensionless filename from the shell
// The shell use removeExtension to remove any file extension
bool SoundClass::Load(wchar_t* Filename) {
	wchar_t extendedFilename[260];
	
	if (lengthOfString_wide(Filename) > 255) {
		MQ->InsertMessage(L"SoundClass::Load: The filename is too long.\n" );
		return false;
	}
	
	if (FileExist(Filename)) {
		// Load the explicit filename if it exist
		int L = lengthOfString_wide(Filename);
		if (Filename[L-4] == '.' && (Filename[L-3] == 'W' || Filename[L-3] == 'w') && (Filename[L-2] == 'A' || Filename[L-2] == 'a') && (Filename[L-1] == 'V' || Filename[L-1] == 'v')) {
			return LoadWaveFile(Filename);
		} else if (Filename[L-4] == '.' && (Filename[L-3] == 'D' || Filename[L-3] == 'd') && (Filename[L-2] == 'S' || Filename[L-2] == 's') && (Filename[L-1] == 'F' || Filename[L-1] == 'f')) {
			return LoadDSF1File(Filename);
		} else {
			MQ->InsertMessage(L"SoundClass::Load: The sound engine can only load sound from 16 bit wave(raw sound) and DSF(near lossless compression).");
			return NULL;
		}
	} else {
		replaceAfterDot(Filename,extendedFilename,L"wav");
		if (FileExist(extendedFilename)) {
			// Load the wave file if it exist
			return LoadWaveFile(extendedFilename);
		} else {
			replaceAfterDot(Filename,extendedFilename,L"dsf");
			if (FileExist(extendedFilename)) {
				// Load the DSF1 file if it exist
				return LoadDSF1File(extendedFilename);
			} else {
				MQ->InsertMessage(L"SoundClass::Load: Could not find the file.");
				return NULL;
			}
		}
	}
}

bool SoundClass::SaveWaveFile(wchar_t* Filename) {
	FILE* filePtr;
	
	// Delete any previous file with that name
	_wremove(Filename);
	
	// Open the wave file in binary.
	if( (_wfopen_s(&filePtr, Filename, L"wb")) != 0 ) {
		MQ->InsertMessage(L"SoundClass::SaveWaveFile: Could not open the file for writing.\n" );
		return false;
	}
	
	// Save the wave file header.
	fwrite(&waveFileHeader,sizeof(waveFileHeader),1,filePtr);
	
	// Write the wave data to the file.
	fwrite(waveData, 1, waveFileHeader.dataSize, filePtr);
	
	// Close the file.
	if (filePtr) {
		if(fclose(filePtr) != 0) {
			MQ->InsertMessage(L"SoundClass::SaveWaveFile: Could not close the file.\n");
			return false;
		}
	}
	
	return true;
}

bool SoundClass::LoadWaveFile(wchar_t* Filename) {
	FILE* filePtr;
	
	// Open the wave file in binary.
	if(_wfopen_s(&filePtr, Filename, L"rb") != 0) {
		MQ->InsertMessage(L"SoundClass::LoadWaveFile: Could not open wave file for reading.\n");
		return false;
	}
	
	// Read in the wave file header.
	if(fread(&waveFileHeader, sizeof(waveFileHeader), 1, filePtr) != 1) {
		MQ->InsertMessage(L"SoundClass::LoadWaveFile: Could not read the wave file header.\n");
		fclose(filePtr);
		return false;
	}
	
	// Check that it is a PCM RIFF WAVE file.
	if((waveFileHeader.chunkId[0] != 'R') || (waveFileHeader.chunkId[1] != 'I') || (waveFileHeader.chunkId[2] != 'F') || (waveFileHeader.chunkId[3] != 'F')) { MQ->InsertMessage(L"SoundClass::LoadWaveFile: The file is not RIFF.\n"); fclose(filePtr); return false; }
	if((waveFileHeader.format[0] != 'W') || (waveFileHeader.format[1] != 'A') || (waveFileHeader.format[2] != 'V') || (waveFileHeader.format[3] != 'E')) { MQ->InsertMessage(L"SoundClass::LoadWaveFile: The file is not WAVE.\n"); fclose(filePtr); return false; }
	if((waveFileHeader.subChunkId[0] != 'f') || (waveFileHeader.subChunkId[1] != 'm') || (waveFileHeader.subChunkId[2] != 't') || (waveFileHeader.subChunkId[3] != ' ')) { MQ->InsertMessage(L"SoundClass::LoadWaveFile: The file is not WAVE.\n"); fclose(filePtr); return false; }
	if((waveFileHeader.dataChunkId[0] != 'd') || (waveFileHeader.dataChunkId[1] != 'a') || (waveFileHeader.dataChunkId[2] != 't') || (waveFileHeader.dataChunkId[3] != 'a')) { MQ->InsertMessage(L"SoundClass::LoadWaveFile: No data chunk header was found in the wave file.\n"); fclose(filePtr); return false; }
	if(waveFileHeader.audioFormat != WAVE_FORMAT_PCM || waveFileHeader.bitsPerSample != 16) {
		MQ->InsertMessage(L"SoundClass::LoadWaveFile: The wave file is not in 16 bit PCM format.\n");
		fclose(filePtr);
		return false;
	}
	
	// Check that the wave file was recorded in mono format.
	if(Flag_3DSound && waveFileHeader.numChannels != 1) {
		MQ->InsertMessage(L"SoundClass::LoadWaveFile: 3D sounds may not have more than one channel.\n");
		fclose(filePtr);
		return false;
	}
	
	// Resize waveData.
	SAFE_DELETE_ARRAY(waveData)
	waveData = new char[(int)waveFileHeader.dataSize];
	if(!waveData) { MQ->InsertMessage(L"SoundClass::LoadWave: FileCould not allocate the temporary sound buffer.\n"); fclose(filePtr); return false; }
	
	// Read waveData from file.
	if(fread(waveData, 1, (int)waveFileHeader.dataSize, filePtr) != (int)waveFileHeader.dataSize) {
		MQ->InsertMessage(L"SoundClass::LoadWaveFile: The file's content could not be retrieved from the file.\n"); fclose(filePtr); return false;
	}
	
	// Close the file.
	if (filePtr) {
		if(fclose(filePtr) != 0) {
			MQ->InsertMessage(L"SoundClass::LoadWaveFile: Could not close the file.\n");
			return false;
		}
	}
	
	// Copy waveFileHeader and waveData to the sound buffers.
	if (!UpdateBuffers()) {
		return false;
	}
	
	if (!Flag_IsEditable) {
		SAFE_DELETE_ARRAY(waveData)
	}
	return true;
}

bool SoundClass::SaveDSF1File(wchar_t* Filename) {
	// Lossy compression
	long T; long C; float Sample[3];
	float Height; float Velocity; unsigned char RoundedSample;
	float FollowPosition; float FollowAcceleration;
	long RawSizeInBytes; long RawSamples; long RawSamplesPerChannel;
	
	// Compress and store in CompressedResult
		CompressedData CompressedResult;
		memset(&CompressedResult, 0, sizeof(CompressedData));
		CompressedResult.Header.sampleRate = waveFileHeader.sampleRate;
		CompressedResult.Header.numChannels = waveFileHeader.numChannels;
		RawSizeInBytes = waveFileHeader.dataSize;
		RawSamples = RawSizeInBytes / 2;
		RawSamplesPerChannel = RawSamples / waveFileHeader.numChannels;
		CompressedResult.Content = new ByteArray(0,MQ);
		CompressedResult.ExtraBuffer = new ByteArray(0,MQ);
		
		// Lossy compression
		LoopForwardLengthFromZero(C,waveFileHeader.numChannels) {
			// Initialize to 0 in case of errors
			Sample[1] = 0.0f; Sample[2] = 0.0f;
			// Read the first height.
			Sample[0] = ReadWaveData(0,C);
			// Round it for storing and stearing
			RoundedSample = FloatToByte(Sample[0]);
			Height = ByteToFloat(RoundedSample);
			CompressedResult.Content->InsertByte(RoundedSample);
			Velocity = 0;
			Sample[1] = Sample[0];
			LoopForward(1,T,RawSamplesPerChannel - 1) {
				// Read the next height
				Sample[0] = ReadWaveData(T,C);
				// Round it for storing and stearing
				RoundedSample = FloatToByte(Sample[0]);
				// How should it accelerate to get to the right position at once
				FollowPosition = Sample[0] - (Height + Velocity);
				if (T > 2) {
					// How should it accelerate like the original sound
					FollowAcceleration = Sample[2] - Sample[1] * 2 + Sample[0];
					// Lerp between the 2 behaviours to make it follow the original sound
					RoundedSample = FloatToByte((FollowPosition * PositionWeight) + (FollowAcceleration * (1 - PositionWeight)));
				} else {
					// Only follow the position when there are not enough history to derive with
					RoundedSample = FloatToByte(FollowPosition);
				}
				// Store the result
				CompressedResult.Content->InsertByte(RoundedSample);
				
				// Use the result to steer in the right direction
				Velocity = Velocity + ByteToFloat(RoundedSample);
				// Move
				Height = Height + Velocity;
				
				// Remember the value for derivation in the next iteration
				Sample[2] = Sample[1];
				Sample[1] = Sample[0];
			}
		}
		
		// Lossless compression
		LosslessEncode(CompressedResult.Content,CompressedResult.ExtraBuffer);
		
		CompressedResult.Header.size = CompressedResult.Content->GetSizeInBytes();
		CompressedResult.Header.filetype[0] = 'D';
		CompressedResult.Header.filetype[1] = 'S';
		CompressedResult.Header.filetype[2] = 'F';
		CompressedResult.Header.filetype[3] = '1';
	
	// Save to file
	FILE* filePtr;
	
	// Delete any previous file with that name
	_wremove(Filename);
	
	// Open the wave file in binary.
	if( (_wfopen_s(&filePtr, Filename, L"wb")) != 0 ) {
		MQ->InsertMessage(L"SoundClass::SaveDSF1File: Could not open the file for writing.\n" );
		SAFE_DELETE(CompressedResult.Content) SAFE_DELETE(CompressedResult.ExtraBuffer)
		return false;
	}
	
	// Save the file header.
	fwrite(&CompressedResult.Header,sizeof(CompressedHeaderType),1,filePtr);
	
	// Write the content to the file.
	fwrite(CompressedResult.Content->TheData, 1, CompressedResult.Header.size, filePtr);
	
	// Close the file.
	if (filePtr) {
		if(fclose(filePtr) != 0) {
			MQ->InsertMessage(L"SoundClass::SaveDSF1File: Could not close the file.\n");
			SAFE_DELETE(CompressedResult.Content) SAFE_DELETE(CompressedResult.ExtraBuffer)
			return false;
		}
	}
	
	SAFE_DELETE(CompressedResult.Content) SAFE_DELETE(CompressedResult.ExtraBuffer)
	
	return true;
}

bool SoundClass::LoadDSF1File(wchar_t* Filename) {
	long I; long T; long C;
	float Height; float Velocity; unsigned char RoundedSample;
	
	CompressedData CompressedResult;
	memset(&CompressedResult, 0, sizeof(CompressedData));
	//CompressedResult.Header
	//RawSizeInBytes = waveFileHeader.dataSize;
	//RawSamples = RawSizeInBytes / 2;
	//RawSamplesPerChannel = RawSamples / waveFileHeader.numChannels;
	CompressedResult.Content = new ByteArray(0,MQ);
	CompressedResult.ExtraBuffer = new ByteArray(0,MQ);
	// Load from file
	
	FILE* filePtr;
	
	// Open the wave file in binary.
	if(_wfopen_s(&filePtr, Filename, L"rb") != 0) {
		MQ->InsertMessage(L"SoundClass::LoadDSF1File: Could not open wave file for reading.\n");
		SAFE_DELETE(CompressedResult.Content) SAFE_DELETE(CompressedResult.ExtraBuffer)
		return false;
	}
	
	// Read in the file header.
	if(fread(&CompressedResult.Header, sizeof(CompressedHeaderType), 1, filePtr) != 1) {
		MQ->InsertMessage(L"SoundClass::LoadDSF1File: Could not read the file header.\n");
		fclose(filePtr);
		SAFE_DELETE(CompressedResult.Content) SAFE_DELETE(CompressedResult.ExtraBuffer)
		return false;
	}
	
	if (CompressedResult.Header.filetype[0] != 'D'
	 || CompressedResult.Header.filetype[1] != 'S'
	 || CompressedResult.Header.filetype[2] != 'F'
	 || CompressedResult.Header.filetype[3] != '1') {
		MQ->InsertMessage(L"SoundClass::LoadDSF1File: The file does not start with DSF1.\n");
		SAFE_DELETE(CompressedResult.Content) SAFE_DELETE(CompressedResult.ExtraBuffer)
		return false;
	}
	
	// Check that the wave file was recorded in mono format.
	if(Flag_3DSound && CompressedResult.Header.numChannels != 1) {
		MQ->InsertMessage(L"SoundClass::LoadDSF1File: 3D sounds may not have more than one channel.\n");
		fclose(filePtr);
		SAFE_DELETE(CompressedResult.Content) SAFE_DELETE(CompressedResult.ExtraBuffer)
		return false;
	}
	
	// resize the buffer holding the data
	CompressedResult.Content->Resize(CompressedResult.Header.size);
	
	// Read waveData from file.
	if(fread(CompressedResult.Content->TheData, 1, (int)CompressedResult.Header.size, filePtr) != (int)CompressedResult.Header.size) {
		MQ->InsertMessage(L"SoundClass::LoadDSF1File: The file's content could not be retrieved from the file.\n");
		fclose(filePtr);
		SAFE_DELETE(CompressedResult.Content) SAFE_DELETE(CompressedResult.ExtraBuffer)
		return false;
	}
	
	// Close the file.
	if (filePtr) {
		if(fclose(filePtr) != 0) {
			MQ->InsertMessage(L"SoundClass::LoadDSF1File: Could not close the file.\n");
			SAFE_DELETE(CompressedResult.Content) SAFE_DELETE(CompressedResult.ExtraBuffer)
			return false;
		}
	}
	
	if (!Flag_IsEditable) {
		SAFE_DELETE_ARRAY(waveData)
	}
	
	// Lossless decompression
	LosslessDecode(CompressedResult.Content,CompressedResult.ExtraBuffer);
	
	AllocateNewWaveData(CompressedResult.Header.sampleRate,CompressedResult.Header.numChannels,CompressedResult.Content->GetSizeInBytes() / CompressedResult.Header.numChannels);
	
	// Lossy decompression
	long DecompressedSamples;
	long DecompressedSamplesPerChannel;
	DecompressedSamples = CompressedResult.Content->GetSizeInBytes();
	DecompressedSamplesPerChannel = DecompressedSamples / CompressedResult.Header.numChannels;
	LoopForwardLengthFromZero(C,CompressedResult.Header.numChannels) {
		I = 0;
		RoundedSample = CompressedResult.Content->GetByte(0);
		Height = ByteToFloat(RoundedSample);
		WriteWaveData(0,C,Height);
		Velocity = 0.0f;
		I++;
		LoopForward(1,T,DecompressedSamplesPerChannel - 1) {
			RoundedSample = CompressedResult.Content->GetByte(I);
			Velocity = Velocity + ByteToFloat(RoundedSample);
			Height = Height + Velocity;
			WriteWaveData(T,C,Height);
			I++;
		}
	}
	
	// Copy waveFileHeader and waveData to the sound buffers.
	if (!UpdateBuffers()) {
		SAFE_DELETE(CompressedResult.Content) SAFE_DELETE(CompressedResult.ExtraBuffer)
		return false;
	}
		
	// Clean up
	SAFE_DELETE(CompressedResult.Content)
	SAFE_DELETE(CompressedResult.ExtraBuffer)
	
	return true;
}

bool SoundClass::IsPlaying(int Index) {
	if (Index < 0 || Index >= Flag_NumberOfCopies) { MQ->InsertMessage(L"SoundClass::IsPlaying: Index is out of bound.\n"); return false; }
	if (SoundCollection[Index].SecondaryBuffer == NULL) { MQ->InsertMessage(L"SoundClass::IsPlaying: SecondaryBuffer is NULL.\n"); return false; }
	DWORD Status = 0;
	if (FAILED(SoundCollection[Index].SecondaryBuffer->GetStatus(&Status))) {
		MQ->InsertMessage(L"SoundClass::IsPlaying: Failed to get the buffer status.\n");
		return false;
	} else {
		return (Status & DSBSTATUS_PLAYING) != 0;
	}
}

int SoundClass::GetSampleRate(void) {
	return waveFileHeader.sampleRate;
}

int SoundClass::GetNumberOfChannels(void) {
	return waveFileHeader.numChannels;
}

int SoundClass::GetSamplesPerChannel(void) {
	return (int)waveFileHeader.dataSize / (2 * (int)waveFileHeader.numChannels);
}

float SoundClass::GetSpeed(int Index) {
	if (Index < 0 || Index >= Flag_NumberOfCopies) { MQ->InsertMessage(L"SoundClass::GetSpeed: Index is out of bound.\n"); return 0.0f; }
	if (SoundCollection[Index].SecondaryBuffer == NULL) { MQ->InsertMessage(L"SoundClass::GetSpeed: SecondaryBuffer is NULL.\n"); return 0.0f; }
	return SoundCollection[Index].LastAssigned_Speed;
}

float SoundClass::GetVolume(int Index) {
	if (Index < 0 || Index >= Flag_NumberOfCopies) { MQ->InsertMessage(L"SoundClass::GetVolume: Index is out of bound.\n"); return 0.0f; }
	if (SoundCollection[Index].SecondaryBuffer == NULL) { MQ->InsertMessage(L"SoundClass::GetVolume: SecondaryBuffer is NULL.\n"); return 0.0f; }
	return SoundCollection[Index].LastAssigned_Volume;
}

DVector3 SoundClass::Get3DPosition(int Index) {
	if (Index < 0 || Index >= Flag_NumberOfCopies) { MQ->InsertMessage(L"SoundClass::Get3DPosition: Index is out of bound.\n"); return DVector3(0,0,0); }
	if (SoundCollection[Index].SecondaryBuffer == NULL) { MQ->InsertMessage(L"SoundClass::Get3DPosition: SecondaryBuffer is NULL.\n"); return DVector3(0,0,0); }
	if (!Flag_3DSound) {
		MQ->InsertMessage(L"SoundClass::Get3DPosition: The sound buffer is not a 3D sound and does not have a valid position.\n");
	}
	return SoundCollection[Index].LastAssigned_3DPosition;
}

void SoundClass::SetMetersPerDistanceUnit(float Meters) {
	int I;
	if (Flag_3DSound) {
		MetersPerDistanceUnit = Meters;
		LoopForwardLengthFromZero(I,Flag_NumberOfCopies) {
			if(FAILED(SoundCollection[I].Secondary3DBuffer->SetPosition(
			  SoundCollection[I].LastAssigned_3DPosition.x * MetersPerDistanceUnit,
			  SoundCollection[I].LastAssigned_3DPosition.y * MetersPerDistanceUnit,
			  SoundCollection[I].LastAssigned_3DPosition.z * MetersPerDistanceUnit,
			  DS3D_IMMEDIATE))) {
				MQ->InsertMessage(L"SoundClass::SetMetersPerDistanceUnit: Could not rescale position.\n");
				return;
			}
		}
	} else {
		MQ->InsertMessage(L"SoundClass::SetMetersPerDistanceUnit: Could not rescale the position because the sound is not a 3D sound.\n");
	}
}
