 /***************************************************************************************
 *This program is free software; you can redistribute it and/or modify					*
 * it under the terms of the GNU General Public License as published by					*
 * the Free Software Foundation; either version 2 of the License, or					*
 * (at your option) any later version.													*
 *																						*
 * The GPL can be found at: http://www.gnu.org/copyleft/gpl.html						*
 *																						*
 *																						*	
 ****************************************************************************************
 * Authors:																			  *
 *          Marc Dukette                                                              *
 **************************************************************************************/

#include "stdafx.h"
#include "avidecaps.h"
#include "audiothread.h"
#include "pocketmvp.h"

#define MAX_WRITE 50
#define MP3_BUFFER_SIZE  (4096)
char  mp3_buffer[8192];
int LastWritten=0;
int LastDone=0;
int Written=0;
int FirstChunk=0;
int currentpos;

#ifdef DECODE_AUDIO
int before;
int after;
extern int max;
#endif

extern videoinfo* vidinfo;
extern HWAVEOUT m_hwo;
extern int totMsTime;
extern __int64 totAudio;
extern int nShouldbe;
extern int tot;
extern HANDLE eventAudio;
extern HANDLE eventNextFrame;
extern HANDLE ha;
extern UserOptions PlayerOptions;
extern reader rd;
extern int br;
extern bool bPaused;
extern bool bEnd;
extern bool EndReached;
extern bool bSkipChunk;
extern pInit Init;
extern pExit Exit;
extern pSeekStart SeekStart;
extern pSeekEnd SeekEnd;
extern pDecompressAudio DecompressAudio;
extern pGetHeaderInfo GetHeaderInfo;
extern void GetDisplayName();
extern bool RawAudio;
unsigned char * PCM_Last=NULL;
int PCM_LastSize;
TCHAR ID3_Title[200];
TCHAR ID3_Artist[200];
TCHAR ID3_Album[200];
// This is an example of an exported function.
void PCM_Init(int Equalizer,char* eq)
{
	currentpos=0;
	PCM_Last=0;
	PCM_LastSize=0;
}
// This is an example of an exported function.
void PCM_Exit(void)
{
}
void PCM_SeekStart()
{
	currentpos=0;
}

void PCM_SeekEnd()
{
	currentpos=0;
}

//static char buffer[40000];
int PCM_GetHeaderInfo(unsigned char * inbuff, int insize, int* Freq, int* ch, int* BitRate,LPTSTR ID3_Title,LPTSTR ID3_Artist,LPTSTR ID3_Album)
{
//	*Freq=44100;
	  *BitRate=vidinfo->a_bits;//128000;
//	  *ch=2;
	  *Freq=vidinfo->a_rate;
	  *ch=vidinfo->a_chans;
	  return 0;
}

int PCM_DecompressAudio(unsigned char * inbuff, int insize, char *outmemory, int outmemsize, int *done, int* inputpos)
{
	if (inbuff)
	{
		PCM_Last=inbuff;
		PCM_LastSize=insize;
		*done=0;
		return 0;
	}
	memcpy(outmemory,PCM_Last,PCM_LastSize);
	currentpos+=PCM_LastSize;
	*inputpos=currentpos;
	*done=PCM_LastSize;
	return 32768;    

}



void CALLBACK WaveProc( 
                                HWAVEOUT hwo, 
                                UINT uMsg, 
                                DWORD dwInstance, 
                                DWORD dwParam1, 
                                DWORD dwParam2 )
{
    if( WOM_DONE == uMsg )
    {
		TCHAR temp[10];
		if (vidinfo&&(!vidinfo->AFAP)) nShouldbe=(int)(((__int64)(((LPWAVEHDR)dwParam1)->dwUser)*(__int64)tot/totAudio)+1);
//		OutputDebugString(_itow(nShouldbe,temp,10));
//		OutputDebugString(_T("\r\n"));
		SetEvent(eventNextFrame);
		LastDone=((LPWAVEHDR)dwParam1)->dwUser;
		waveOutUnprepareHeader( m_hwo, (LPWAVEHDR)dwParam1, sizeof(WAVEHDR) );
		Written--;
		if (Written==MAX_WRITE) SetEvent(eventAudio);        
	    delete (void*)dwParam1;
    }
}

static void Write(LPWAVEHDR pwh,int cbData,int offset,HANDLE event)
{
	if (!offset||(!cbData))
	{
	    delete (void*)pwh;
		return;
	}
	if (Written>MAX_WRITE)  
	{
		WaitForSingleObject(event,INFINITE);
		ResetEvent(event);
		if (bSkipChunk)
		{
			bSkipChunk=false;
			return;
		}
		if (bEnd) return;
	}
	
	pwh->dwFlags=pwh->dwLoops = 0;
	pwh->dwBytesRecorded=pwh->dwBufferLength = cbData;
	pwh->dwUser=offset;


	waveOutPrepareHeader( m_hwo, pwh, sizeof(WAVEHDR) );

	waveOutWrite( m_hwo, pwh, sizeof(WAVEHDR) );
	Written++;
	LastWritten=offset;
    return;
}

DWORD WINAPI AudioThread(LPVOID lpParameter)
{
    int             status=0; 
	int				nFirst=1;
	int pos=0;
	int played;
	LPWAVEHDR pwh;
	Written=0;
    int nb_read=0;
	int Freq;
	int ch;

	int totPlayed=0;
	WAVEFORMATEX   m_wfx;
	HANDLE eventNextFrame=CreateEvent(NULL,true,false,_T("eventNextFrame"));
	HANDLE eventAudioStopped=CreateEvent(NULL,true,false,_T("eventAudioStopped"));
	HANDLE eventAudio=CreateEvent(NULL,true,true,_T("eventAudio"));
	if (!RawAudio)
	{
		Init =(pInit)GetProcAddress((HINSTANCE)ha,_T("Init"));
		Exit =(pExit)GetProcAddress((HINSTANCE)ha,_T("Exit"));
		SeekStart =(pSeekStart)GetProcAddress((HINSTANCE)ha,_T("SeekStart"));
		SeekEnd =(pSeekEnd)GetProcAddress((HINSTANCE)ha,_T("SeekEnd"));
		DecompressAudio =(pDecompressAudio)GetProcAddress((HINSTANCE)ha,_T("DecompressAudio"));
		GetHeaderInfo =(pGetHeaderInfo)GetProcAddress((HINSTANCE)ha,_T("GetHeaderInfo"));
	}
	else
	{
		Init =(pInit)PCM_Init;
		Exit =(pExit)PCM_Exit;
		SeekStart =(pSeekStart)PCM_SeekStart;
		SeekEnd =(pSeekEnd)PCM_SeekEnd;
		DecompressAudio =(pDecompressAudio)PCM_DecompressAudio;
		GetHeaderInfo =(pGetHeaderInfo)PCM_GetHeaderInfo;
	}
	Freq=0;
	Init(PlayerOptions.EnableEQ, PlayerOptions.Eq);
	rd.READER_ReadAudio((char *)mp3_buffer,MP3_BUFFER_SIZE);
	while((status=GetHeaderInfo(mp3_buffer,MP3_BUFFER_SIZE,&Freq,&ch,&br,ID3_Title,ID3_Artist,ID3_Album))!=0)
	{
		if (status==32768) status=0;
		if (status>0) 
		{
			//status--;
			memmove(mp3_buffer,mp3_buffer+(MP3_BUFFER_SIZE-status),status);
		}
		int nb_read=rd.READER_ReadAudio((char *)mp3_buffer+status,MP3_BUFFER_SIZE-status);
	}
	//RefreshDisplay();
//	memmove(mp3_buffer+1,mp3_buffer,MP3_BUFFER_SIZE-1);
	if (!RawAudio) status=MP3_BUFFER_SIZE;
	m_wfx.wFormatTag=WAVE_FORMAT_PCM;
	m_wfx.nSamplesPerSec=Freq; 
	m_wfx.nChannels=ch; 
	m_wfx.wBitsPerSample=16; 
	m_wfx.nBlockAlign=ch*m_wfx.wBitsPerSample/8; 
	m_wfx.nAvgBytesPerSec=m_wfx.nBlockAlign*m_wfx.nSamplesPerSec;
	m_wfx.cbSize=0;
	totAudio=vidinfo->audio_bytes;
	if (!br&&(vidinfo->video_frames))
		totAudio=((__int64)totMsTime*(__int64)m_wfx.nAvgBytesPerSec)/(__int64)1000;
	FirstChunk=1;
	MMRESULT mmr;
	mmr=waveOutOpen( &m_hwo, 
					   WAVE_MAPPER, 
					   &m_wfx, 
					   (DWORD)WaveProc, 
					   (DWORD)NULL, 
#ifdef _X86_
					   WAVE_ALLOWSYNC|CALLBACK_FUNCTION );
#else
						WAVE_MAPPED|CALLBACK_FUNCTION);
#endif
	if( mmr != MMSYSERR_NOERROR  )
	{
		if (mmr==WAVERR_BADFORMAT)
		{
			Exit();
			Init(PlayerOptions.EnableEQ,0);
			switch(Freq)
			{
				case 48000:
					Freq=44100;
					break;
				default:
					Freq=22050;
					break;
			}
			GetHeaderInfo(mp3_buffer,MP3_BUFFER_SIZE-1,&Freq,&ch,&br,ID3_Title,ID3_Artist,ID3_Album);
			m_wfx.nSamplesPerSec=Freq; 
			mmr=waveOutOpen( &m_hwo, 
					   WAVE_MAPPER, 
					   &m_wfx, 
					   (DWORD)WaveProc, 
					   (DWORD)NULL, 
#ifdef _X86_
					   WAVE_ALLOWSYNC|CALLBACK_FUNCTION );
#else
						WAVE_MAPPED|CALLBACK_FUNCTION);
#endif
		}
		else
		{
			MessageBox(0,_T("Error in Audio"),_T("Wave Open Error"),MB_OK);
			return 0;
		}
	}
	vidinfo->a_chans=ch;
	vidinfo->a_rate=Freq;
	GetDisplayName();
	if (!lpParameter) waveOutPause(m_hwo);	
#ifdef DECODE_AUDIO
	before=0;
#endif
	do 
	{	
		SetEvent(eventNextFrame);
		if (status==32768) status=0;
		if (status>0) 
		{
			//status--;
			memmove(mp3_buffer,mp3_buffer+(MP3_BUFFER_SIZE-status),status);
		}
		int nb_read=0;
		if (MP3_BUFFER_SIZE-status)
		{
			nb_read=rd.READER_ReadAudio((char *)mp3_buffer+status,MP3_BUFFER_SIZE-status);
			if (!nb_read)
			{
				status=-1;
				continue;
			}
		}
		pwh = (LPWAVEHDR) new BYTE[ sizeof( WAVEHDR ) + 8192 ];

		pwh->lpData = (LPSTR) &pwh[1];

#ifdef DECODE_AUDIO
//		if (before)
//		{
//			after=GetTickCount()-before;
//			//if (after>30)
//				max++;
//		}
//		before=GetTickCount();
#endif
		status=DecompressAudio(mp3_buffer,nb_read+status,pwh->lpData,4800,&played,&pos) ; 
		
		totPlayed+=played;
#ifndef DECODE_AUDIO
		if (br>0||(!vidinfo->video_frames))
			Write(pwh,played,pos,eventAudio);
		else
			Write(pwh,played,totPlayed,eventAudio);
#else
		delete pwh;
#endif	
		if (!status)
		{
			do 
			{	
				if (!bPaused)
				{
				
					pwh = (LPWAVEHDR) new BYTE[ sizeof( WAVEHDR ) + 8192 ];
					
					pwh->lpData = (LPSTR) &pwh[1];
#ifdef DECODE_AUDIO
//					before=GetTickCount();
#endif
					status=DecompressAudio(NULL,0,pwh->lpData,4800,&played,&pos) ; 
#ifdef DECODE_AUDIO
//					after=GetTickCount()-before;
//					if (after>max) max=after;
#endif
					if (FirstChunk&&(played))
					{
						played=0;
						FirstChunk=0;
					}


					totPlayed+=played;
#ifndef DECODE_AUDIO
					if (br>0||(!vidinfo->video_frames))
						Write(pwh,played,pos,eventAudio);
					else
						Write(pwh,played,totPlayed,eventAudio);
#else
					delete pwh;
#endif
				}
				else
				{
					status = 0;
					Sleep(1);
				}
			}while ((!status)&&(!bEnd));
		}
    } while ((status > 0)&&(!bEnd)&&(status!=MP3_BUFFER_SIZE));
#ifndef DECODE_AUDIO
	waveOutRestart(m_hwo);
	if (!bEnd)
	{
		while (LastWritten!=LastDone) Sleep(10);
	}
	else
	{
#ifndef _X86_
		waveOutReset(m_hwo);
#endif
	}
#endif
	waveOutClose(m_hwo);
	Exit();
	Init=NULL;
	if (!bEnd) EndReached=true;
	bEnd=1;
	SetEvent(eventNextFrame);
	SetEvent(eventAudioStopped);
	CloseHandle(eventNextFrame);
	CloseHandle(eventAudioStopped);
	CloseHandle(eventAudio);
    return 0;
}
