//---------------------------------------------------------------------------
#pragma hdrstop
#include "AudioPlayer.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)

//---------------------------------------------------------------------------
TAudioPlayer::TAudioPlayer()
{
	FHandle = NULL;
}

//---------------------------------------------------------------------------
TAudioPlayer::~TAudioPlayer()
{
	Stop();
}

//---------------------------------------------------------------------------
void TAudioPlayer::Prepare()
{
	for (TStreams::iterator i = FStreams.begin(); i != FStreams.end(); i++)
	{
		if (i->Source && !i->Prepared)
		{
			waveOutPause(i->hwo);
			WriteAudioBuf(*i);
			i->Prepared = true;
		}
	}
}

//---------------------------------------------------------------------------
void TAudioPlayer::Start()
{
	Prepare();
	for (TStreams::iterator i = FStreams.begin(); i != FStreams.end(); i++)
	{
		if (i->Source)
		{
			waveOutRestart(i->hwo);
		}
	}
}

//---------------------------------------------------------------------------
void TAudioPlayer::Stop()
{
	TStreams Streams = FStreams;
	FStreams.clear();
	for (TStreams::iterator i = Streams.begin(); i != Streams.end(); i++)
	{
		waveOutReset(i->hwo);
		for (int j = 0; j < AudioBufNum; j++)
		{
			waveOutUnprepareHeader(i->hwo, &i->wh[j], sizeof(i->wh[j]));
			delete[] i->wh[j].lpData;
		}
		waveOutClose(i->hwo);
	}
/*
	for (TStreams::iterator i = FStreams.begin(); i != FStreams.end(); i++)
	{
		waveOutReset(i->hwo);
		for (int j = 0; j < AudioBufNum; j++)
		{
			waveOutUnprepareHeader(i->hwo, &i->wh[j], sizeof(i->wh[j]));
			delete[] i->wh[j].lpData;
		}
		waveOutClose(i->hwo);
	}
	FStreams.clear();
*/
}

//---------------------------------------------------------------------------
int TAudioPlayer::Init(int ARate, int AChannels, int ABits, TAudioSource *ASource)
{
	TAudioInfo ai;
	ai.dwf.wFormatTag = WAVE_FORMAT_PCM;
	ai.dwf.nChannels = AChannels;
	ai.dwf.nSamplesPerSec = ARate;
	ai.dwf.wBitsPerSample = ABits;
	ai.dwf.nBlockAlign = ai.dwf.nChannels * (ai.dwf.wBitsPerSample / 8);
	ai.dwf.nAvgBytesPerSec = ai.dwf.nBlockAlign * ai.dwf.nSamplesPerSec;
	ai.dwf.cbSize = 0;
	ai.Playing = true;
	ai.Prepared = false;
	ai.AudioBuf = NULL;
	ai.AudioBufIndex = 0;
	ai.AudioBufOut = 0;
	ai.AudioBufPos = 0;
	ai.Source = ASource;
	unsigned int Index = FStreams.size();

	if (waveOutOpen(&ai.hwo, WAVE_MAPPER, &ai.dwf, (DWORD)FHandle, 0, CALLBACK_WINDOW) == MMSYSERR_NOERROR)
	{
		bool ok = true;
		for (int i = 0; i < AudioBufNum; i++)
		{
			ai.wh[i].lpData = NULL;
		}
		for (int i = 0; i < AudioBufNum; i++)
		{
			ai.wh[i].dwBufferLength = (int(ai.dwf.nAvgBytesPerSec / (AudioBufNum)) / ai.dwf.nBlockAlign) * ai.dwf.nBlockAlign;
			ai.wh[i].lpData = new char[ai.wh[i].dwBufferLength];
			ai.wh[i].dwFlags = WHDR_DONE;
			if (waveOutPrepareHeader(ai.hwo, &ai.wh[i], sizeof(ai.wh[i])) != MMSYSERR_NOERROR)
			{
				delete[] ai.wh[i].lpData;
				ai.wh[i].lpData = NULL;
				ok = false;
				break;
			}
		}
		if (ok)
		{
			FStreams.push_back(ai);
			return Index;
		}
		else
		{
			for (int i = 0; i < AudioBufNum; i++)
			{
				if (ai.wh[i].lpData != NULL)
				{
					waveOutUnprepareHeader(ai.hwo, &ai.wh[i], sizeof(ai.wh[i]));
					delete[] ai.wh[i].lpData;
				}
			}
			waveOutClose(ai.hwo);
		}
	}
	return -1;
}

//---------------------------------------------------------------------------
void TAudioPlayer::SetSource(unsigned int AStream, TAudioSource *ASource)
{
	FStreams[AStream].Source = ASource;
}

//---------------------------------------------------------------------------
bool TAudioPlayer::WriteAudioBuf(TAudioInfo &Info)
{
	Info.AudioBufOut = 0;
	for (int i = 0; i < AudioBufNum; i++)
	{
		if (Info.wh[i].dwFlags & WHDR_INQUEUE)
			Info.AudioBufOut++;
	}

	while (Info.AudioBufOut < AudioBufNum)
	{
		int l = 0, i = 0;
		while (l < Info.wh[Info.AudioBufIndex].dwBufferLength)
		{

			if (Info.AudioBufPos == 0)
			{
				if (Info.Source->GetPos() < Info.Source->GetLen())
				Info.AudioBuf = Info.Source->GetData(Info.AudioBufPos);
			}

			if (Info.AudioBufPos == 0)
			{
				Info.Playing = false;
				memset(Info.wh[Info.AudioBufIndex].lpData + l, 0, Info.wh[Info.AudioBufIndex].dwBufferLength - l);
				waveOutWrite(Info.hwo, &Info.wh[Info.AudioBufIndex], sizeof(WAVEHDR/*Info.wh[Info.AudioBufIndex]*/));
				Info.AudioBufOut++;
				return false;
			}

			i = Info.AudioBufPos;
			if (i > (Info.wh[Info.AudioBufIndex].dwBufferLength - l))
			{
				i = (Info.wh[Info.AudioBufIndex].dwBufferLength - l);
			}
			memcpy(Info.wh[Info.AudioBufIndex].lpData + l, Info.AudioBuf, i);
			memmove(Info.AudioBuf, Info.AudioBuf + i, Info.AudioBufPos - i);
			Info.AudioBufPos -= i;
			l += i;
		}

		waveOutWrite(Info.hwo, &Info.wh[Info.AudioBufIndex], sizeof(WAVEHDR/*Info.wh[Info.AudioBufIndex]*/));
		Info.AudioBufOut++;

		Info.AudioBufIndex++;
		if (Info.AudioBufIndex >= AudioBufNum)
		{
			Info.AudioBufIndex = 0;
		}
	}
	return true;
}

//---------------------------------------------------------------------------
bool TAudioPlayer::WndProc(Messages::TMessage &Message)
{
	if (Message.Msg == MM_WOM_DONE)
	{
		TStreams::iterator i = FStreams.begin();
		while (i != FStreams.end())
		{
			if (i->hwo == (HWAVEOUT)Message.WParam)
			{
				if (i->Playing)
				{
					i->AudioBufOut--;
					WriteAudioBuf(*i);
				}
				break;
			}
			i++;
		}
		return true;
	}
	return false;
}

//---------------------------------------------------------------------------
unsigned int TAudioPlayer::GetAudioBufOut(unsigned int AStream)
{
	return FStreams[AStream].AudioBufOut;
}

//---------------------------------------------------------------------------
unsigned int TAudioPlayer::GetCount()
{
	return FStreams.size();
}

