/****************************************************************************
*
* SPUSOUND.C
*
* PROGRAMMER : A. Twat
*    VERSION : 00.00
*    CREATED : 05/06/98
*   MODIFIED : 05/06/98
*       TABS : 5
*
* DESCRIPTION
*	Low-level sound functions
*
*****************************************************************************/

/*---------------------------------------------------------------------------
 *	Import Headers
\*--------------------------------------------------------------------------*/

#include <sys\types.h>
#include <libspu.h>
#include "typedefs.h"
#include "spusound.h"
#include "..\game\text.h"

/*---------------------------------------------------------------------------
 *	Constants
\*--------------------------------------------------------------------------*/

//#define LOG

/*---------------------------------------------------------------------------
 *	Locals
\*--------------------------------------------------------------------------*/

static uint8 LabSampleType[24];
static uint8 LabLeftVolume[24];
static uint8 LabRightVolume[24];
static uint8 LabFreeChannel[24];
static int LnFreeChannels;
static long LlVABAddr;
static uint32 LadwSampleAddr[MAX_VAG_SAMPLES];
static int LnSamplesLoaded;
static uint8 LabSPUMallocArea[SPU_MALLOC_RECSIZ*(MAX_SPU_MALLOCS+1)];

/*---------------------------------------------------------------------------
 *	Macros
\*--------------------------------------------------------------------------*/

#ifdef LOG
#define Log_(format,args...) DebugOut(format "\n" , ## args)
#else
#define Log_(args...)
#endif

/*---------------------------------------------------------------------------
 *	Local Functions
\*--------------------------------------------------------------------------*/

static int SPU_AllocChannel()
{
	if ((LnFreeChannels == 0) && (SPU_UpdateStatus() == 0))
		return -1;

	return LabFreeChannel[--LnFreeChannels];
}

static void SPU_FreeChannel(int nChannel)
{
	LabSampleType[nChannel] = SPU_SAMPLETYPE_OFF;
	LabLeftVolume[nChannel] = 0;
	LabRightVolume[nChannel] = 0;
	LabFreeChannel[LnFreeChannels++] = (uint8)nChannel;
}

/*---------------------------------------------------------------------------
 *	Public Functions
\*--------------------------------------------------------------------------*/

int SPU_Play(int nSample,int nLeftVolume,int nRightVolume,int nPitch,int nSampleType)
	{
	int nChannel;
	SpuVoiceAttr sva;

	if (nSample>=LnSamplesLoaded)
		{
		Log_("SPU_Play: Invalid sample number: %d (%d loaded)",nSample,LnSamplesLoaded);
		return ERR_SOUND_FAILED;
		}
//	Log_("SPU_Play(%d,%d,%d,%d,%d)",nSample,nLeftVolume,nRightVolume,nPitch,nSampleType);
	nChannel=SPU_AllocChannel();
	if (nChannel<0)
		return ERR_SOUND_NO_FREE_SLOTS;
	//Log_("nChannel=%d",nChannel);
	LabSampleType[nChannel]=(uint8)nSampleType;
	LabLeftVolume[nChannel]=(uint8)nLeftVolume;
	LabRightVolume[nChannel]=(uint8)nRightVolume;
	sva.mask=SPU_VOICE_VOLL|SPU_VOICE_VOLR|SPU_VOICE_PITCH|SPU_VOICE_WDSA;
    sva.voice=SPU_VOICECH(nChannel);
	sva.volume.left=nLeftVolume;
	sva.volume.right=nRightVolume;
	sva.pitch = (SAMPLE_RATE*nPitch) >> 16;
	sva.addr=LadwSampleAddr[nSample];
   	SpuSetKeyOnWithAttr(&sva);
	return nChannel;
	}

void SPU_AdjustVolume(int nChannel,int nLeftVolume,int nRightVolume)
	{
	LabLeftVolume[nChannel]=(uint8)nLeftVolume;
	LabRightVolume[nChannel]=(uint8)nRightVolume;
	SpuSetVoiceVolume(nChannel,nLeftVolume,nRightVolume);
	}

void SPU_Stop(int nChannel)
{
	SpuSetKey(SPU_OFF, SPU_VOICECH(nChannel));

	if (LabSampleType[nChannel] != SPU_SAMPLETYPE_OFF)
		SPU_FreeChannel(nChannel);
}

void SPU_StopAll(void)
	{
	Log_("SPU_StopAll");
	SpuSetKey(SPU_OFF,SPU_ALLCH);
	while (SPU_UpdateStatus()!=24)
		;
	}

static void SPU_Pause(int nChannel)
	{
	switch (LabSampleType[nChannel])
		{
		case SPU_SAMPLETYPE_OFF:
			break;
		case SPU_SAMPLETYPE_NORMAL:
			SpuSetKey(SPU_OFF,SPU_VOICECH(nChannel));
			SPU_FreeChannel(nChannel);
			break;
		case SPU_SAMPLETYPE_LOOPED:
			SpuSetVoiceVolume(nChannel,0,0);
			break;
		}
	}

void SPU_PauseAll(void)
	{
	int nChannel;

	Log_("SPU_PauseAll");
	for (nChannel=0;nChannel<24;++nChannel)
		SPU_Pause(nChannel);
	}

static void SPU_Unpause(int nChannel)
	{
	switch (LabSampleType[nChannel])
		{
		case SPU_SAMPLETYPE_OFF:
		case SPU_SAMPLETYPE_NORMAL:
			break;
		case SPU_SAMPLETYPE_LOOPED:
			SpuSetVoiceVolume(nChannel,LabLeftVolume[nChannel],LabRightVolume[nChannel]);
			break;
		}
	}

void SPU_UnpauseAll(void)
	{
	int nChannel;

	Log_("SPU_UnpauseAll");
	for (nChannel=0;nChannel<24;++nChannel)
		SPU_Unpause(nChannel);
	}

int SPU_Status(int nChannel)
{
	uint8 bStatus;

	bStatus = SpuGetKeyStatus(SPU_VOICECH(nChannel));

	if ((bStatus != SPU_ON)
	&&  (bStatus != SPU_OFF_ENV_ON)
	&&  (LabSampleType[nChannel] != SPU_SAMPLETYPE_OFF))
		SPU_FreeChannel(nChannel);

	return LabSampleType[nChannel];
}

int SPU_UpdateStatus(void)
{
	int nChannel;
	uint8 abStatus[24];

	SpuGetAllKeysStatus(abStatus);

	for ( nChannel=0 ; nChannel<24 ; nChannel++ )
	{
		if ((abStatus[nChannel] != SPU_ON)
		&&  (abStatus[nChannel] != SPU_OFF_ENV_ON)
		&&  (LabSampleType[nChannel] != SPU_SAMPLETYPE_OFF))
			SPU_FreeChannel(nChannel);
	}
/*
	for ( nChannel=0 ; nChannel<24 ; nChannel++ )
		exit_message[nChannel] = '0'+abStatus[nChannel];
	exit_message[24]=0;
	PrintDbug(4,10,exit_message);

	for ( nChannel=0 ; nChannel<24 ; nChannel++ )
		exit_message[nChannel]='0'+LabSampleType[nChannel];
	PrintDbug(4,11,exit_message);

	for ( nChannel=0 ; nChannel<24 ; nChannel++ )
		exit_message[nChannel]='A'+LabFreeChannel[nChannel];
	exit_message[LnFreeChannels]=0;
	PrintDbug(4,12,exit_message);
*/
	return LnFreeChannels;
}

int SPU_LoadSamples(int nSamples,uint32* padwOffset,void* pVAB,int nVABSize)
	{
	int nSample;

	Log_(__FUNCTION__);
	Log_("nSamples=%d; nVABSize=%d",nSamples,nVABSize);
	if (nSamples>MAX_VAG_SAMPLES)
		{
		Log_("Too many samples (max %d)",nSamples);
		return FALSE;
		}
	if (nVABSize>0x80000)
		{
		Log_("VAB is > 512K");
		return FALSE;
		}
	if (LnSamplesLoaded)
		SPU_FreeSamples();
	LlVABAddr=SpuMalloc(nVABSize);
	if (LlVABAddr==SPU_ERROR)
		return FALSE;
	SpuIsTransferCompleted(SPU_TRANSFER_WAIT);
	SpuSetTransferStartAddr(LlVABAddr);
	SpuWrite(pVAB,nVABSize);
	SpuIsTransferCompleted(SPU_TRANSFER_WAIT);
	for (nSample=0;nSample<nSamples;++nSample)
		LadwSampleAddr[nSample]=LlVABAddr+padwOffset[nSample];
	LnSamplesLoaded=nSamples;
	Log_("loaded %d samples",nSamples);
	return TRUE;
	}

void SPU_FreeSamples(void)
	{
	Log_("SPU_FreeSamples");
	SPU_StopAll();
	if (LnSamplesLoaded)
		SpuFree(LlVABAddr);
	LlVABAddr=0;
	LnSamplesLoaded=0;
	}

void SPU_Init(void)
{
	int nChannel;
//	SpuVoiceAttr sva;

	Log_("SPU_Init");

	SpuInit();
	SpuInitMalloc(MAX_SPU_MALLOCS, LabSPUMallocArea);
	SpuSetTransferMode(SpuTransByDMA);
	SpuSetCommonMasterVolume(0x3fff, 0x3fff);

/* -------- initialise reverb */

//	SpuSetReverbModeType(SPU_REV_MODE_CLEAR_WA|SPU_REV_MODE_ROOM);
	SpuSetReverbModeType(SPU_REV_MODE_CLEAR_WA|SPU_REV_MODE_STUDIO_B);
	SpuSetReverbModeDepth(0x1fff, 0x1fff);
	SpuSetReverbVoice(SpuOn, SPU_ALLCH);
	SpuSetReverb(SpuOn);

/*	sva.voice=SPU_ALLCH;
	sva.mask=SPU_VOICE_VOLMODEL|SPU_VOICE_VOLMODER;
	sva.volmode.left=sva.volmode.right=SPU_VOICE_EXPIncN;
	SpuSetVoiceAttr(&sva);*/

	LnFreeChannels=0;
	for (nChannel=0;nChannel<24;++nChannel)
		SPU_FreeChannel(nChannel);
	LlVABAddr=0;
	LnSamplesLoaded=0;
	}

void SPU_Cleanup(void)
	{
	Log_("SPU_Cleanup");
	SPU_FreeSamples();
	SpuQuit();
	}


static sint16 DepthTable[] = {
	0,
	0x1000,
	0x1800,
	0x4000,
	0x7fff
};

void S_SetReverbType(sint16 reverb)
{
	sint16 depth;
	static sint16 CurrentReverb = -1;

	if (reverb != CurrentReverb)
	{
		CurrentReverb = reverb;
		depth = DepthTable[reverb];

		SpuSetReverbModeDepth(depth, depth);
		SpuSetReverb(SpuOn);
	}
}

