/****************************************************************************\
*                                                                 
* tpShinobiSnd.c -   TrueMotion audio interface and playback code  
*                                                                 
*       TrueMotion SDK                                       
*       Copyright (c) 1994-98, The Duck Corp. All rights reserved.
*                                                                 
*       Created: Aug 24, 1998                                    
*                                                                 
\***************************************************************************/

#include "duck_hfb.h"
#include "duck_dxl.h"

#include <shinobi.h>

#include "tpApi.h"

#define TX_BLOCK_SIZE 0x1000 /* size of block to transfer */

#define NUM_BLOCKS 9	/* number of decompressed blocks to buffer */
#define DX_BLOCK_SIZE TX_BLOCK_SIZE*NUM_BLOCKS /* total size of buffer (samples) */

int dxSamplesFromStream(MovieHandle movie, int limit);

void tpVSyncHandler(MovieHandle movie)
{
	movie->counter++;
}

static long tp_SyncFrame(MovieHandle movie)
{
	/* generate the frame counter */
	
	if (movie->speedTest)
		return 0x7fffffff;

	/* FWG 9-9-98 */
	if (movie->noAudio || !movie->aStream)
    {
		int frameWanted;

		/* no audio in the file, or turned off */
		/* only accepts 15, 30 and 60 fps */
		switch(movie->frameRate){
		case 15:
			/*return movie->counter/4;*/
			frameWanted =  movie->counter/4;
			break;
		case 30:
			/*return movie->counter/2;*/
			frameWanted =  movie->counter/2;
			break;
		default:
			/*return movie->counter;*/
			frameWanted =  movie->counter;
			break;
		}

		return frameWanted;
	}else 
	{
		unsigned long total_smp_frame;
		long frameWanted, modifSamp;
		long curAdr;
		
		sdPstmGetTotalSmpFrame(movie->pcmStHndl,0, &total_smp_frame);
		sdPstmGetCurAdr(movie->pcmStHndl,0, &curAdr);

		if (total_smp_frame < 0)
			total_smp_frame = 0;

		modifSamp = ((total_smp_frame + movie->pcmStExtraSamples));
		
		if (modifSamp < 0)
			return 0;
		
		modifSamp >>= 1;
		

		frameWanted = (modifSamp * movie->frameRate) / (movie->AudioSampleRate>>1);

		if (frameWanted < 0)
			frameWanted = 0;

		return frameWanted;
	}
}

/***********************************************\
* decompress samples from audio stream
\***********************************************/
static int dxSamplesFromStream(MovieHandle movie, int limit)
{
    int gotten = 0;
    int sampleCount;

    sampleCount = limit;

    while((gotten = DXL_dxAudio(movie->audSrc,movie->audDst,sampleCount)) != sampleCount)
	{
        /*  if gotten != samplecount there must not have been enough 
        samples in the chunk so get another chunk and try again */
        sampleCount -= gotten;

        if (movie->aIndex > -1)
            HFB_ReleaseStreamingData(movie->xBuffer,movie->aIndex);
        
        movie->aIndex = HFB_GetStreamingData(movie->aStream,(void **)&movie->aData, 
                            &movie->aLength, DUK_FORWARD, 1);

        /* if we succesfully got some audio data, use it */
        if (movie->aIndex > -1)
		{
            DXL_AlterXAudioData(movie->audSrc,(unsigned char *)movie->aData,movie->aLength);
		} 
		else if (movie->aIndex == -1) 
		{	/* if no audio is left, fill the buffer with silence */         
            DXL_AlterXAudioData(movie->audSrc,NULL,0);
		} 
		else /* nothing ready in the buffer aSamples for the next request */
            return (limit -= sampleCount); 
    }
    return limit;
}

static PLAYER_STATUS_CODE tp_MovieAudioSetup(MovieHandle movie)
{   
    /* setup audio resources specific to this movie */
	int aFrameRate = 0;
	int rv,i;
	DKWAVEFORM *wave;
	int BytesPerSec,BitsPerSample,aFormat;

    HFB_GetFrameRates(movie->vStream,movie->aStream,&movie->frameRate,&aFrameRate);	

    movie->samplesPerFrame = TX_BLOCK_SIZE;
	movie->txBlock = 0;
    movie->aIndex = -1;
	
	if (movie->noAudio || !movie->aStream)
        return TP_OK; 

	if (!movie->audDst)
	{
		movie->audDst = DXL_CreateAudioDst(NULL,NULL,DX_BLOCK_SIZE,8,2,0);
		
		if (movie->audDst)
		{
			for (i = 0 ; i < 2; i++)
			{
				movie->rawPtr[i] = (unsigned char *)syMalloc((DX_BLOCK_SIZE*2)+32);
				movie->alignedPtr[i] = 
					(unsigned char *)((((unsigned long)movie->rawPtr[i])+31) & 0xFFFFFFE0 ); 
			}
		}
	}

    wave = HFB_GetAudioInfo(movie->aStream,&movie->numAudioChannels,&movie->AudioSampleRate,
                &BytesPerSec,&aFormat);

    if(!wave)
		return TP_OK;

    if (!movie->audSrc)
	{
		movie->audSrc = DXL_CreateXAudioSrc(wave, (unsigned char *)-1, 0); 
		if(!movie->audSrc)
			return TP_OK;
    }

    if ((aFormat == 1) && ((BytesPerSec/movie->numAudioChannels) == movie->AudioSampleRate))
	{
        DXL_AlterAudioDst(movie->audDst,
			movie->alignedPtr[0],
			movie->alignedPtr[1],
			DX_BLOCK_SIZE,8,
			movie->numAudioChannels,movie->AudioSampleRate);
		BitsPerSample = 8;
	}
    else
	{
        DXL_AlterAudioDst(movie->audDst,
			movie->alignedPtr[0],
			movie->alignedPtr[1],
			DX_BLOCK_SIZE,16,
			movie->numAudioChannels,movie->AudioSampleRate);
		BitsPerSample = 16;
	}

    /* initialize audio destination */
    if (movie->aStream){ 
        DXL_AlterXAudioData(movie->audSrc,movie->alignedPtr[0],0);
		if (movie->from_frame > 0){
            int skipAudio = 0; 
            skipAudio = (movie->from_frame*movie->AudioSampleRate)/movie->frameRate;
            /*skipAudio -= HFB_getDataPosition(movie->aStream,-1);*/
            if (skipAudio > 0){
				int noaudio = movie->noAudio;

				movie->noAudio = 1; /* temporarily disable audio */
                tp_AudioProcess(movie, skipAudio);
				movie->noAudio = noaudio;
            }
        }
    }

	/* we are already set just return? */
	if (!movie->pcmStHndl)
	{
		if (movie->numAudioChannels == 1)
		{
			if ( sdPstmOpenPort( &movie->pcmStHndl, 1, 0) != SDE_ERR_NOTHING ){
				movie->pcmStHndl = NULL;
				return TP_OK;
			}
		}
		else
		{
			if ( sdPstmOpenPort( &movie->pcmStHndl, 2, 0, 1) != SDE_ERR_NOTHING ){ 
				movie->pcmStHndl = NULL;
				return TP_OK;
			}
		}
		
		if (!movie->mem_blk)
			sdMemBlkCreate( &movie->mem_blk );
	}

	/* set initial sound parameters */
	if (!movie->pcmStVolumeSet)
	{
		movie->pcmStVolume = 0x7F;
		movie->pcmStVolumeSet = 1;
	}

	/* Should this number use the global volume number? */
	sdPstmSetBaseVol( movie->pcmStHndl, 0/*movie->pcmStVolume*/);

	movie->setOutVol(movie, movie->pcmStVolume);
	
	if (movie->numAudioChannels > 1)
	{
		if (!movie->pcmStPanpotLeftSet)
		{
			movie->pcmStPanpotLeft = -15;
			movie->pcmStPanpotLeftSet = 1;
		}
		if (!movie->pcmStPanpotRightSet)
		{
			movie->pcmStPanpotRight = 15;
			movie->pcmStPanpotRightSet = 1;
		}
		
		movie->setOutPan(movie, 0, movie->pcmStPanpotLeft);
		movie->setOutPan(movie, 1, movie->pcmStPanpotRight);
	}
	else
	{
		if (!movie->pcmStPanpotLeftSet)
		{
			movie->pcmStPanpotLeft = 0;
			movie->pcmStPanpotLeftSet = 1;
		}
		
		movie->setOutPan(movie, 0, movie->pcmStPanpotLeft);
	}

	return TP_OK;
} 

static PLAYER_STATUS_CODE tp_MovieAudioShutDown(MovieHandle movie)
{
    /* release audio resources specific to this movie */
	
	if(movie->aStream)
        tp_AudioStop(movie);

	if (movie->audSrc)
	{
		DXL_DestroyXAudioSrc(movie->audSrc);
		movie->audSrc = NULL;
	}
	
    if (movie->pcmStHndl)
		sdPstmClosePort( movie->pcmStHndl);

	if (movie->mem_blk)
		sdMemBlkDestroy( movie->mem_blk );

	movie->pcmStLastPauseNumSamples = 0; /* FWG 4-14-99 */
	
	movie->mem_blk = NULL;
	movie->pcmStHndl = NULL;

	return TP_OK;
}

/* start audio hardware playback */
static PLAYER_STATUS_CODE tp_AudioStart(MovieHandle movie)
{
	int i;
	int timeForTpAudio;
	unsigned long total_smp_frame;
	unsigned long sampPerVideoFrame;
	unsigned long checkForHardwareClear;
	SDE_ERR				error;

	checkForHardwareClear = 0;	/* Init to zero for noAudio and no aStream. */

	if((!movie->noAudio) && (movie->aStream)) 
	{
		if (movie->pcmStLastPauseNumSamples)
		{
			long curAdr;
			
			sdPstmGetTotalSmpFrame(movie->pcmStHndl,0, &total_smp_frame);
			sdPstmGetCurAdr(movie->pcmStHndl,0, &curAdr);
			
			/* From HFB next frame to play, since count started at 0 = the num of frames played. */
			
			sampPerVideoFrame = movie->AudioSampleRate / movie->frameRate;
			movie->pcmStExtraSamples += total_smp_frame - ((movie->videoFrame)* sampPerVideoFrame);
			
			/* DC Hardware addr ptr gets reset to 0 */
			/* 0x4000*/ /* size of hardware ring buffer */
		}

		tp_AudioProcess(movie, movie->AudioSampleRate); /* This should be just a copy */
		
		error = sdPstmPlay( movie->pcmStHndl, SDE_PCM_TYPE_16BIT_PCM, 
			(movie->AudioSampleRate == 44100) ? SDD_PSTM_FREQ_44100:SDD_PSTM_FREQ_22050, 
			0);

		/* Sometimes when we start->stop->start the function that returns how 
		many samples played, still returns number of samples played when stopped. The 
		number that should be returned should be close to zero. This seems to only happen 
		for a few milliseconds then everything reutrns to normal behavior.
		*/
		sdPstmGetTotalSmpFrame(movie->pcmStHndl,0, &checkForHardwareClear);
	}
	else
	{
		/* Reset counter */
		movie->counter = 0;
	}

	if (movie->pcmStLastPauseNumSamples)
	{
		/* Check for start->stop->start condition during the playback of one movie. */
		do 
		{
			tp_AudioProcess(movie, movie->AudioSampleRate);
			sdSysServer();
			
			sdPstmGetTotalSmpFrame(movie->pcmStHndl,0, &total_smp_frame);
		} while (movie->pcmStLastPauseNumSamples <= total_smp_frame);
	}
	else if (checkForHardwareClear > 4000)
	{
		/* Check for start->stop->start condition when playing more than one movie. */
		do 
		{
			tp_AudioProcess(movie, movie->AudioSampleRate);
			sdSysServer();
			
			sdPstmGetTotalSmpFrame(movie->pcmStHndl,0, &checkForHardwareClear);
		} while (checkForHardwareClear > 4000);
	}
	else
	{
		tp_AudioProcess(movie, movie->AudioSampleRate);
		sdSysServer();
	}

	movie->videoFrame = 0;

	return TP_OK;
}

/* stop audio hardware playback */
static PLAYER_STATUS_CODE tp_AudioStop(MovieHandle movie)
{
	unsigned long total_smp_frame;

	/*movie->txBlock = movie->numSamples = movie->counter = 0;*/

	if((!movie->noAudio) && (movie->aStream))
	{
		SDE_ERR error;
		unsigned int modSampBy;
		long curAdr;

		error = sdPstmStop(movie->pcmStHndl);
		if ((error == SDE_ERR_NO_INIT) ||
			(error == SDE_ERR_HANDLE_NULL) ||
			(error == SDE_ERR_HOST_CMD_BUF_NO_ENOUGH) )
		{
			return TP_UNKNOWN_ERROR;
		}

		sdPstmGetTotalSmpFrame(movie->pcmStHndl,0, &total_smp_frame);
		sdPstmGetCurAdr(movie->pcmStHndl,0, &curAdr);
		movie->pcmStLastPauseNumSamples = total_smp_frame;
	}
	
	sdSysFlushHostCmd();

	return TP_OK;
}

static PLAYER_STATUS_CODE tp_AudioProcess(MovieHandle movie,int limit) /* process up to "limit" samples */
{
	int maxSamples = DX_BLOCK_SIZE-movie->numSamples;

	if (!movie->aStream)
		return 0; /* if there's no audio stream, there's nothing to do here */

	if (movie->noAudio)
	{
        if(movie->aStream) 
		{
			movie->aIndex = HFB_GetStreamingData(movie->aStream,(void **)&movie->aData, 
								    &movie->aLength, DUK_FORWARD, 1);

			if ((movie->aIndex > -1) && (!movie->stayResident))
			       HFB_ReleaseStreamingData(movie->xBuffer,movie->aIndex);
        }        
		return TP_OK;
	}

	if (limit > maxSamples)
		limit = maxSamples;

	if(limit)
	{
		unsigned long entry;
		/* decompress audio into system mem audio buffer 
		   (regardless of noAudio setting in case we turn audio on later) */

		limit = dxSamplesFromStream(movie, limit);

		if (limit)
		{
			if (!movie->noAudio) /* don't perform the transfer if audio is disabled */
				movie->numSamples += limit;

			for(entry = 0; entry < 512; entry++){
			  *((volatile unsigned long *)(0xF4000000 | (entry << 5))) = 0x00000000; 
			  /* OC address array write */
			}
		}
	}

	if (movie->numSamples >= TX_BLOCK_SIZE)
	{
		/* transfer decompressed audio to the hardware ring buffer */
		int i=0;
		Bool flg;
		SDE_ERR ret;

		sdSysServer();

		ret = sdPstmIsTransferWaveData( movie->pcmStHndl, 0, TX_BLOCK_SIZE*2, &flg);

		if ( flg != true ) 
			return TP_OK;

		if (movie->numAudioChannels > 1) /* make certain second channel available */
		{
			ret = sdPstmIsTransferWaveData( movie->pcmStHndl, 1, TX_BLOCK_SIZE*2, &flg);

			if ( flg != true ) 
				return TP_OK;
		}

		/* ok to transfer next block of decompressed audio for each channel */
		for(i = 0; i < movie->numAudioChannels; i++)
		{
			sdMemBlkSetPrm( movie->mem_blk, (void *)(movie->alignedPtr[i]+(movie->txBlock*TX_BLOCK_SIZE*2)), 
				TX_BLOCK_SIZE*2, SDD_MEMBLK_SYNC_FUNC, NULL);

			if ( sdPstmTransferWaveData( movie->pcmStHndl, i, movie->mem_blk) != SDE_ERR_NOTHING )
				return TP_OK;
		}

		if (++movie->txBlock >= NUM_BLOCKS)
			movie->txBlock = 0;

		movie->numSamples -= TX_BLOCK_SIZE;
	}
	return TP_OK;
}

static int numAudioRefs = 0;

static PLAYER_STATUS_CODE tp_AudioShutDown(MovieHandle movie)
{
	/* final audio de-initialization, releases buffers, objects and library */

	if (movie->audDst)
	{
		syFree(movie->rawPtr[0]);
		syFree(movie->rawPtr[1]);
        DXL_DestroyAudioDst(movie->audDst);
        movie->audDst = (DXL_AUDIODST_HANDLE)NULL;
    }

    if (!--numAudioRefs) /* release after last reference closed */
		DXL_ExitAudio();

	return TP_OK;
}

static PLAYER_STATUS_CODE tp_mwShiSetOutVol(MovieHandle movie, Sint32 vol)
{
	movie->pcmStVolumeSet = 1;
	movie->pcmStVolume = vol;

	if (movie->pcmStHndl)
	{
		sdPstmSetVol(  movie->pcmStHndl, 0, (Sint8)movie->pcmStVolume, 0);
		
		if (movie->numAudioChannels > 1)
		{
			sdPstmSetVol(  movie->pcmStHndl, 1, (Sint8)movie->pcmStVolume, 0);		
		}	
	}

	return TP_OK;
}

Sint32 tp_mwShiGetOutVol(MovieHandle movie)
{
	SDS_PSTM_STAT		stat;
	SDE_ERR				error;

	error = sdPstmGetStat(movie->pcmStHndl, 0, &stat);

	if ((error==SDE_ERR_NO_INIT) || (error==SDE_ERR_HANDLE_NULL))
	{
		return (Sint32)error;
	}

	/* Getting output volume */
	return (Sint32)stat.m_Vol;
}

static PLAYER_STATUS_CODE tp_mwShiSetOutPan(MovieHandle movie, Sint32 c, Sint32 p)
{
	if (c == 0)
	{
		/* left channel */
		movie->pcmStPanpotLeft = p;
		movie->pcmStPanpotLeftSet = 1;
	}
	else
	{
		/* right channel */
		movie->pcmStPanpotRight = p;
		movie->pcmStPanpotRightSet = 1;
	}

	if (movie->pcmStHndl)
	{
		sdPstmSetPan(  movie->pcmStHndl, (Sint8)c, (Sint8)p, 0);
	}

	return TP_OK;
}

Sint32	tp_mwShiGetOutPan(MovieHandle movie, Sint32 c)
{
	SDS_PSTM_STAT		stat;
	SDE_ERR				error;

	error = sdPstmGetStat(movie->pcmStHndl, (Sint8)c, &stat);

	if ((error==SDE_ERR_NO_INIT) || (error==SDE_ERR_HANDLE_NULL))
	{
		return (Sint32)error;
	}

	/*	Getting panpot(chno:0=L/1=R, pan:-15=L/15=R/0=center)		*/
	return  (Sint32)stat.m_Pan;
}


PLAYER_STATUS_CODE tp_setupAudioDriver(MovieHandle movie)
{
	int i;

	if (!numAudioRefs++) /* open only on first ("outermost") reference */
		DXL_InitAudio(1,1);

	movie->audDst = NULL;

	movie->setupAudioDriver = tp_setupAudioDriver;
	movie->shutdownAudioDriver = tp_AudioShutDown;

	movie->startDecompressingAudio = tp_MovieAudioSetup;
	movie->stopDecompressingAudio = tp_MovieAudioShutDown;

	movie->decompressAudio = tp_AudioProcess;

	movie->startAudioPlayback = tp_AudioStart;
	movie->stopAudioPlayback = tp_AudioStop;

	movie->getSyncFrame = tp_SyncFrame;
	
	movie->setOutVol = tp_mwShiSetOutVol;
	movie->getOutVol = tp_mwShiGetOutVol;
	movie->setOutPan = tp_mwShiSetOutPan;
	movie->getOutPan = tp_mwShiGetOutPan;

	movie->pcmStHndl = NULL; /* FWG 4-14-99 */
	movie->pcmStLastPauseNumSamples = 0;
	
	return TP_OK;
}
