/*	
	Sound.c
	Veneer for islsfx2
*/

#include "global.h"
#include "sound.h"
#include "map_draw.h"
#include "main.h"
#include "options.h"
#include "maths.h"

#include "puzzles.h"	// rqd for the post-FMA music restart
#include "fma.h"
#include "map_play.h"
#include "gametext.h"
#include "pause.h"

#include <ac.h>
#include <a64thunk.h>
#include <am.h>
#include <sound.h>
#include "islsfx2.h"
#include "overlays.h"

unsigned long sbb_names[MAX_SOUND_BANKS] = {0,0,0,0};
unsigned long spu_addrs[MAX_SOUND_BANKS] = {0,0,0,0};
SfxBankType * sbh_addrs[MAX_SOUND_BANKS] = {0,0,0,0};
XAFileType * music, * ref_music;
int musicPaused=0;

SfxBankType *pSfxGenericBank = NULL;	// re-introduced by Fred...

extern AM_BANK_PTR			audio64Banks[16];
extern SfxBankType			*sampleBanks[16];

struct
{
	SfxSampleType	*pSample;
	int				volume;
	VECTOR			*pos;
}dynamicChan[24];


// make sure this is always correct or funny things will happen

// The best way to do this I think is to have all the 8 track xa's first and put the 32 track ones after

// Languages moved to subdirectory to get round PSX's max-findable-files-per-directory
char * xa_file_names[] =
{
	"tune05.adx",			// TUNE_5_COOPS_D				 0
	"tune06.adx",			// TUNE_6_COOPS_N				 1
	"tune09.adx",			// TUNE_9_COG					 2
	"tune10.adx",			// TUNE_10_FLYMACHINE			 3
	"tune11.adx",			// TUNE_11_MRST					 4
	"tune07.adx",			// TUNE_7_WORKING				 5
	"tune08.adx",			// TUNE_8_GOLF					 6
	"",
	"tune01.adx",			// TUNE_1_INTRO					 7
	"tune04.adx",			// TUNE_4_FAILED				 8
	"tune02.adx",			// TUNE_2_BRIEFING				 9
	"tune03.adx",			// TUNE_3_COMPLETE				10
	"tune12.adx",			// TUNE_12_DIES					11
	"tune13.adx"			// TUNE_13_GAMEOVER				12
	"",
	"",
	"",
	"",
	"test.adx",				// TUNE_AT1						16
	"test.adx",				// TUNE_AT2						17
	"test.adx",				// TUNE_AT3						18
	"test.adx",				// TUNE_AT4						19
	"test.adx",				// TUNE_AT5						20
	"test.adx",				// TUNE_AT6						21
	"test.adx",				// TUNE_AT7						22
	"test.adx",				// TUNE_AT8						23
};
//#define	NUMBEROF8TRACKXA	3
//#define N_XA_TRACKS (sizeof(xa_file_names) / 4)
#define NUMBEROF8TRACKXA (sizeof(xa_file_names) / 4)
#define NUMBER_OF_FMA_TRACKS (24)
#define N_XA_TRACKS (NUMBEROF8TRACKXA + NUMBER_OF_FMA_TRACKS)


int music_playing = -1;
int music_to_resume = -1;
int music_queued = -1;
int music_fader = 256;

char music_fade_current = 0;
char music_fade_time = 0;
char music_fade_dirn = 0;


// =========== Internal Utils ===============

int soundFindFreeBank()
{
	int i;
	for(i = 0; i < MAX_SOUND_BANKS; i++)
	{
		// ma - find free sample bank
		if(!sampleBanks[i])
			return i;
		
//		if(!sbb_names[i])
//			return i;
	}
	return -1;
}
int soundFindCRCBank(unsigned long crc)
{
	int i;
	for(i = 0; i < MAX_SOUND_BANKS; i++)
	{
		if(sbb_names[i]==crc)
			return i;
	}
	return -1;
}

int soundFindShortestAngle(int ang1, int ang2)
{
	int temp = ang1 - ang2;

	if ((temp > -2048) && (temp <= 2048))
		return temp;
	else if (temp > 2048)
		return ang1 - ang2 - 4096;
	else
		return ang1 + 4096 - ang2;
}

/*
int soundCalcMonoFade(VECTOR *position, int *vols, int zerodist)
{
	int cameraAngle, soundAngle, offsetAngle;
	int	distanceX, distanceY, distanceZ, distance;
	int tmpLeftVol, tmpRightVol;
	ULONG calc;
	VECTOR *campos;
	VECTOR *camdirn;

	// CPW: DB("pos = (%d, %d, %d)\n", position->vx, position->vy, position->vz);

	campos = (VECTOR *)&mapCamData.gameCam.vpx;
	camdirn = (VECTOR *)&mapCamData.realCam.vrx;	// which, conveniently, is a zero-relative direction already

	distanceX = (position->vx) - campos->vx;	// Get dist from cam
	distanceY = (position->vy) - campos->vy;
	distanceZ = (position->vz) - campos->vz;

	calc=distanceX * distanceX + distanceY * distanceY + distanceZ * distanceZ;

	if (calc==0)
	{
		distance=0;
	}
	else
	{
		distance = utilSqrt(calc)>>16;
	}

	if(distance > zerodist)	// if sound is too far to be heard, don't bother with positioning
	{
//		DB("Dist %d is beyond %d\n",distance,zerodist);
		return 0;
	}

	vols[0] = (256 * distance) / zerodist;
	vols[1] = vols[0];

	return 1;
}
*/

// Stereo calculations, ripped from GloverPSX
// Vols[0],Vols[1] are returned as 0...256 (erk, not 255)
int soundCalc3DPanFade(VECTOR *position, int *vols, int zerodist)
{
	int cameraAngle, soundAngle, offsetAngle;
	int	distanceX, distanceY, distanceZ, distance;
	int tmpLeftVol, tmpRightVol;
	ULONG calc;
	VECTOR *campos;
	VECTOR *camdirn;

	// CPW: DB("pos = (%d, %d, %d)\n", position->vx, position->vy, position->vz);

	campos = (VECTOR *)&mapCamData.gameCam.vpx;
	camdirn = (VECTOR *)&mapCamData.realCam.vrx;	// which, conveniently, is a zero-relative direction already

	// CPW: shift down 6 is to stop overflows
	distanceX = ((position->vx) - campos->vx)>>6;	// Get dist from cam
	distanceY = ((position->vy) - campos->vy)>>6;
	distanceZ = ((position->vz) - campos->vz)>>6;

	calc=distanceX * distanceX + distanceY * distanceY + distanceZ * distanceZ;;

	if (calc==0)
	{
		distance=0;
	}
	else
	{
		distance = utilSqrt(calc)>>10;	// CPW: was 16
	}

	if(distance > zerodist)	// if sound is too far to be heard, don't bother with positioning
	{
//		DB("Dist %d is beyond %d\n",distance,zerodist);
		return 0;
	}
	
	if(!optionData.sound.stereo)
	{
		vols[0] = 256 - ((256 * distance) / zerodist);
		vols[1] = vols[0];

		return 1;
	}

// find the angle of the sound with respect to the camera's position
	cameraAngle = (utilCalcAngle(camdirn->vx,camdirn->vz)) & 4095;
	soundAngle  = (utilCalcAngle((position->vx>>12) - campos->vx, (position->vz>>12) - campos->vz)) & 4095;
	offsetAngle = soundFindShortestAngle(soundAngle & 4095, cameraAngle & 4095);


	if(abs(offsetAngle) > 1024)					// sound is behind us
	{
		if(offsetAngle > 0)
		{
			offsetAngle = 2048 - offsetAngle;	// 1024...2048
		}
		else
		{
			offsetAngle = -2048 - offsetAngle;
		}
	}

//  offsetAngle = -1024...+1024

	tmpRightVol = (offsetAngle + 1024) / 8;	// standard stereo panning

	tmpLeftVol  = 255 - tmpRightVol;

// Distance scaling of the volume
	vols[0] = 256 - (((-tmpLeftVol  * distance) / zerodist) + tmpLeftVol);
	vols[1] = 256 - (((-tmpRightVol * distance) / zerodist) + tmpRightVol);
	
// CPW: 	DB("leftvol = %d, rightvol = %d, dist = %d\n",vols[0],vols[1], distance);

	return 1;
}

// Do a stereo sfx at full volume (eg - level bosses)
// Vols[0],Vols[1] are returned as 0...256 (erk, not 255)
int soundCalc3DPanOnly(VECTOR *position, int *vols)
{
	int cameraAngle, soundAngle, offsetAngle;
	int	distanceX, distanceZ, distance;
	int tmpLeftVol, tmpRightVol;
	ULONG calc;

	VECTOR *campos;
	VECTOR *camdirn;

	campos = (VECTOR *)&mapCamData.gameCam.vpx;
	camdirn = (VECTOR *)&mapCamData.realCam.vrx;	// which, conveniently, is a zero-relative direction already


	
	distanceX = (position->vx>>12) - campos->vx;	// get the sound's distance from the camera
	distanceZ = (position->vz>>12) - campos->vz;

	calc=distanceX * distanceX + distanceZ * distanceZ;

// find the angle of the sound with respect to the camera's position
	cameraAngle = (utilCalcAngle(camdirn->vx,camdirn->vz)) & 4095;
	soundAngle  = (utilCalcAngle((position->vx>>12) - campos->vx, (position->vz>>12) - campos->vz)) & 4095;
	offsetAngle = soundFindShortestAngle(soundAngle & 4095, cameraAngle & 4095);

	if(abs(offsetAngle) > 1024)					// sound is behind us
	{
		if(offsetAngle > 0)
		{
			offsetAngle = 2048 - offsetAngle;
		}
		else
		{
			offsetAngle = -2048 - offsetAngle;
		}
	}

//  offsetAngle = -1024...+1024
	tmpRightVol = (offsetAngle + 1024) / 8;	// standard stereo panning

	tmpLeftVol  = 255 - tmpRightVol;

	vols[0] = tmpLeftVol;
	vols[1] = tmpRightVol;

//	DB("leftvol = %d, rightvol = %d\n",vols[0],vols[1]);

	return 1;
}


// =========== Loading & Freeing Banks ===============


// Leafnames, in upper case, please
int soundLoadJustBody2(char *fname, int path_and_leaf, unsigned long crc)
{
	char name[100];
	int i;

 	i = soundFindFreeBank();
	if(i!=-1)
	{
		name[i] = utilStr2CRC(fname);

		gdFsChangeDir("\\");
		gdFsChangeDir("AUDIO");
		spu_addrs[i] = sfxLoadSampleBankBody(fname);
		if(!spu_addrs[i])
		{
			debugPrintf("FAILED TO LOAD SOUND BANK %s\n",fname);
			return -1;
		}
// Don't fill in the name CRC unless the bank was loaded
		sbb_names[i] = crc;	
	}
	return i;
}

int soundLoadJustBody(char *leafname)
{
	return soundLoadJustBody2(leafname,1,utilStr2CRC(leafname));
}


// This won't be needed once we've put the sbh's into bff files, but for now...
/*
SfxBankType *soundLoadBodyAndHeader(char *leafname)
{
#ifndef _WINDOWS
	unsigned long crc;
	char name[100];
	int i;
	SfxBankType *sbh = NULL;

	i = soundLoadJustBody(leafname);
	if(i != -1)
	{
		sprintf(name,"%s\\%s.SBH",leafname, leafname);
		sbh = (SfxBankType *)fileLoad(name,NULL);
		ASSERT(sbh);	// to match the release version "fileLoad"
		sfxFixupSampleBankHeader(sbh, spu_addrs[i]);
		sbh_addrs[i] = sbh;
	}

	return sbh;

#else
	return NULL;
#endif

}
*/
SfxBankType *soundLoadBodyAndHeaderFull(char *fname, unsigned long crc)
{
#ifndef _WINDOWS
	char name[100];
	int i;
	SfxBankType *sbh = NULL;

	i = soundLoadJustBody2(fname,0,crc);
/*ma
	if(i != -1)
	{
		sprintf(name,"%s.SBH",fname);
		sbh = (SfxBankType *)fileLoad(name,NULL);
		ASSERT(sbh);	// to match the release version "fileLoad"
		sfxFixupSampleBankHeader(sbh, spu_addrs[i]);
		sbh_addrs[i] = sbh;
	}
*/
	if(i != -1)
		return (SfxBankType*)sampleBanks[i];
	else
		return NULL;

#else
	return NULL;
#endif

}



// A bff sound segment is just an SBH, which is assumed to link into a previously loaded & spu-transferred SBB
int soundLinkBFF(SOUND_BFF *sb)
{
	SfxBankType *sbh;
	int i;

// Note - Levels which have a sound bank per language will have multiple SBH segments in the BFF file,
// Only one of which will have a "name_crc" which matches a loaded SBB. So this will still work
	i = soundFindCRCBank(sb->name_crc);
	if(i != -1)
	{
		sbh = sfxFixupSampleBankHeader(&sb->sbh, spu_addrs[i]);
		sbh_addrs[i] = sbh;
	}
	return i;
}


void soundFreeBank(int i, int free)
{
	if(i == -1)
		return;

	if(spu_addrs[i])
	{
		sfxUnloadSampleBank(sbh_addrs[i]);
		spu_addrs[i] = 0;
	}

	if(sbh_addrs[i])
	{
		// Unhook it from the list of samples to test against
		sfxRemoveSampleBank(sbh_addrs[i], free);
		sbh_addrs[i] = 0;
	}
	sbb_names[i] = 0;
}

// Unhook a BFF'd SBH from the list of samples checked for with "findinallbanks"
void soundUnlinkBFF(SOUND_BFF *sb)
{
	int i;
	for(i = 0; i < MAX_SOUND_BANKS; i++)
	{
// Note - Levels which have a sound bank per language will have multiple SBH segments in the BFF file,
// Only one of which will have a "name_crc" which matches a loaded SBB. So this will still work
		if(sbh_addrs[i]==&sb->sbh)
		{
			soundFreeBank(i,0);
		}
	}
}





// =========== Top-End stuff ===============

int sound_GenericBank = -1;
void soundSetup()
{
	int ch;
	unsigned long spu_addr;
	SfxBankType *sbh;
	sfxInitialise(SFX_REVERB_MODE_OFF);
	// CPW: sfxInitialise(SFX_REVERB_MODE_ECHO);

	// CPW: sfxSetReverb(5, 100);

// Testing....
//	sound_GenericBank = soundLoadBodyAndHeader("TEST");
	
	sfxOn();
	sfxStartSound();
}

void soundShutdown()
{
// Testing...
//	soundFreeBank(sound_GenericBank);
//	sound_GenericBank = -1;


}

void soundReset(void)
{
	int i;

	for(i=0; i<24; i++)
	{
		dynamicChan[i].pSample = NULL;
		sfxStopChannel(i);
	}
}

void soundCycle()
{
	if(music)
	{
		if (ADXT_GetStat(music->adxt) == ADXT_STAT_PLAYEND)
		{
			if (music->loop)
			{
				gdFsChangeDir("\\");						// Change to the audio directory on the GD
				gdFsChangeDir("AUDIO");						// 

				ADXT_StartFname(music->adxt, music->fileInfo);
			}
			else
				sound_XA_Stop();
		} 
	} 




	   
// Deal with a single queued XA track...
//	if(XAData.currXA == NULL && XAData.currXA->status == 0 && music_queued != -1)
	if(XAData.currXA == NULL && music_queued != -1)
	{
		sound_XA_Start(music_queued);
		music_queued = -1;
	}

// Restart act music after FMA sequences
	if(levinfo)
	{
		if(music_playing == -1 
			&& !fmaStatus.playing 
			&& !mapplay_leave_level_flag 
			&& levinfo->level_number != LEVNUMBER_A4_CONSTRUCT
			&& !pauseOn)
		{
			sound_XA_Start(levinfo->tune_number);

		}
	}

// fade in/out of music
	if(music_fade_dirn != 0)
	{
		int vol;

		music_fade_current += music_fade_dirn;
		music_fader = music_fade_current * 256 / music_fade_time;

		if(music_fade_current == 0 || music_fade_current == music_fade_time)
		{
			music_fade_dirn = 0;
		}

		vol = optionData.sound.musicvol * 100 / OPTIONS_MAX_VOLUME;
		vol = vol * music_fader / 256;

		if(XAData.prevXA)
			XAData.prevXA->vol = vol;
		if(XAData.currXA)
			XAData.currXA->vol = vol;


//		DB("fader = %d\n",music_fader);

		SpuSetCommonCDVolume((vol*0x7fff)/100);

	}
}


// =========== Playing samples ===============

// These started off as Mermaid routines, but got horribly savaged to link into the Glover stereo code
// Volume = 0xllrr
int soundPlaySample(SfxSampleType *sample, short volume, int pitch)
{
	int vl,vr, r;
	if(!sample)
		return -1;

	VERIFY(sample);

	vl = (volume>>8)&0xff;
	vr = volume&0xff;

	// CPW: DB("frame %ld, sample %p\n", frame, sample);

	r = sfxPlaySample( sample, vl,vr, pitch*10);
	return r;
}



// BP: The same as above but with a different volume - taken from optionData.sound.speechvol
int soundPlaySpeech(SfxSampleType *sample, int pitch)
{
	int vl,vr, r;
	if(!sample)
		return -1;

	VERIFY(sample);

	vl = vr = optionData.sound.speechvol & 0xff;
	
	
	sfxSetGlobalVolume(255);			// Global volume to max...
	r = sfxPlaySample(sample, vl,vr, pitch*10);
	sfxSetGlobalVolume(optionData.sound.sfxvol);	// put it back
	return r;
}



int soundPlayStereo(SfxSampleType *pS, int *pV, int volume, int pitch)
{
	int ch;

	if(volume > 255)	// if "volume" is 0...255, and vols[0],vols[1] are 0...256, then wer're ok
		volume = 255;

	pV[0] = (pV[0] * volume) >>8;
	pV[1] = (pV[1] * volume) >>8;

	volume = (pV[0] << 8) | pV[1];
// CPW: 	DB("SOUND: Play sample %p, %04X, %d\n", pS, volume, pS->inSPURam);
	ch = soundPlaySample(pS, volume, pitch);

	if(ch<0)
	{
// CPW: 		DB(" *** Sample refused to play! ***\n");
	}

	return ch;
}


//#define ZERO_DIST 8192	// RFW - upped to 32768: 8192 was prototyped on the (rather close-up) fredtest level
#define ZERO_DIST 12000

int soundPlaySamplePanFade(SfxSampleType *sample, int volume, int pitch,VECTOR *at)
{

#ifndef _WINDOWS
	int d;
	int vols[2];
	int ch;
	int r;

// CPW: 	static int lastchannel = -1;

	VERIFY(sample);

	r = soundCalc3DPanFade(at, &vols[0], ZERO_DIST);

	if(!r)
	{
		// CPW: DB("SOUND: soundCalc3DPanFade(%p(%d, %d, %d), vol, %d) failed!\n", at, at->vx, at->vy, at->vz, ZERO_DIST);
		if(sample->flags & SAMPLEFLAGS_LOOPED)
			vols[0] = vols[1] = 1;
		else
			return(-1);
	}

	if(volume > 255)
		volume = 255;

	ch = soundPlayStereo(sample, vols, volume, pitch);

/*	if(ch == lastchannel)
	{
		utilPrintf("ERK!\n");
		ASSERT(0);
	}

	lastchannel = ch;
*/
	if(ch>=0)
	{
		dynamicChan[ch].pSample = sample;
		dynamicChan[ch].pos = at;
		dynamicChan[ch].volume = volume;
	}
	else
	{
		ASSERT(0);
	}

	return ch;
#else
	return 0;
#endif
}


void soundMuteAllChannels(void)
{
	int i;

	for(i=0; i<24; i++)
	{
		sfxSetChannelVolume(i, 0, 0);
	}
}

void soundMaintainSamplePanFade(void)
{
#ifndef _WINDOWS
	int i;
	int pV[2];

	for(i=0; i<24; i++)
	{
		SfxSampleType *pS = dynamicChan[i].pSample;

		if(pS && (pS->flags & SAMPLEFLAGS_LOOPED))
		{
			// CPW: check that sample is playing like it ought to, because the PSX sound hardware is so shitty
/*			if(sfxGetChannelStatus(i) == NULL)
			{
				utilPrintf("SOUND: Stupid shitty crappy PSX sound hardware!\n");
				soundPlayStereo(pS, 0, 0, 0);
			}

			utilPrintf("shit\n");
*/
			// CPW: looped sample is still playing.
			if(!soundCalc3DPanFade(dynamicChan[i].pos, &pV[0], ZERO_DIST))
			{
// CPW: 				DB("SOUND: Update looped samp (%p(%d, %d, %d), vol, %d) failed!\n",
// CPW: 					dynamicChan[i].pos, dynamicChan[i].pos->vx, dynamicChan[i].pos->vy, dynamicChan[i].pos->vz, ZERO_DIST);

				sfxSetChannelVolume(i, 0, 0);
				continue;
			}
			else
			{
// CPW: 				DB("SOUND: Update looped samp (%p(%d, %d, %d), vol, %d) SUCCEEDED\n",
// CPW: 					dynamicChan[i].pos, dynamicChan[i].pos->vx, dynamicChan[i].pos->vy, dynamicChan[i].pos->vz, ZERO_DIST);
			}
			
			// CPW: It's in range, so update its stereo-ness

			// CPW: soundPlayStereo(sample, vols, volume, pitch);

			pV[0] = (pV[0] * dynamicChan[i].volume) >>8;
			pV[1] = (pV[1] * dynamicChan[i].volume) >>8;

			sfxSetChannelVolume(i, pV[0], pV[1]);
		}
	}
#endif
}


int soundPlaySamplePanOnly(SfxSampleType *sample, int volume, int pitch,VECTOR *at)
{
	int vols[2];

	soundCalc3DPanOnly(at, &vols[0]);
	return soundPlayStereo(sample, vols, volume, pitch);
}

void soundStopSample(SfxSampleType *pS)
{
	int i;

	for(i=0; i<24; i++)
	{
		if(pS == dynamicChan[i].pSample)
		{
			sfxStopChannel(i);

			dynamicChan[i].pSample = NULL;
		}
	}
}


/*
void soundMaintainPan(void)
{
	char s[32];
	int ch;

	for(ch=0; ch<24; ch++)
	{
			if(sfxGetChannelStatus(ch))
			{	// CPW: channel is playing a sample
				int volume;
				int vols[2];

				volume = dynamicChan[ch].volume;

				soundCalc3DPanFade(dynamicChan[ch].pos, &vols[0], ZERO_DIST);
				// CPW: soundCalc3DPanOnly(dynamicChan[ch].pos, &vols[0]);

				if(volume > 255)	// if "volume" is 0...255, and vols[0],vols[1] are 0...256, then wer're ok
					volume = 255;

				vols[0] = (vols[0] * volume) >>8;
				vols[1] = (vols[1] * volume) >>8;

				DB("Vol = %d, %d\n", vols[0], vols[1]);

				sfxSetChannelVolume(ch, vols[0], vols[1]);

				if((vols[0] + vols[1]) == 0)
				{
					sfxStopChannel(ch);
				}

				s[ch] = '1';
			}
			else
			{
				s[ch] = '0';
			}

	}

	s[ch] = 0;

	// CPW: DB("Chan status = %s\n", s);
}
*/




// =========== X.A.-Playing Veneer ==========


void sound_XA_VolRefresh()
{
	int vol;
	vol = optionData.sound.musicvol;
#ifndef _WINDOWS
#ifdef DREAMCAST
	SpuSetCommonCDVolume((vol*0x7fff)/100);
#else
	SpuSetCommonCDVolume((vol*0x7fff)/100,(vol*0x7fff)/100);
#endif
#endif
}

void sound_XA_StartFadeOut(int n_frames)
{
	music_fade_current = n_frames;
	music_fade_time = n_frames;
	music_fade_dirn = -1;
}
void sound_XA_StartFadeIn(int n_frames)
{
	music_fade_current = 0;
	music_fade_time = n_frames;
	music_fade_dirn = 1;
	if(XAData.prevXA)
		XAData.prevXA->vol = 0;
	if(XAData.currXA)
		XAData.currXA->vol = 0;

}
void sound_XAInit()
{
	int i;
	XAFileType *music;

	FMEMZERO(&ref_music,N_XA_TRACKS * sizeof(XAFileType));

//	for(i = 0; i < N_XA_TRACKS; i++)
	for(i = 0; i < NUMBEROF8TRACKXA; i++)
	{
		if(XAgetStatus())
		{
			music = XAgetFileInfo(xa_file_names[i]);	// the ";1" is added internally
			if(music)
			{
				ref_music[i] = *music;
				FREE(music);
			}
		}
	}
}


char *lang_dirs[LANG_NUMLANGS] = {"ENG","FRE","GER","ITA","SPA","ENG"};

void sound_XA_FindLangFiles(int lang)
{
	char fname[40];
	int i;
	XAFileType *music;

	for(i = 0; i < NUMBER_OF_FMA_TRACKS; i++)
	{
		if(i+1 < 10)
		{
			sprintf(fname,"\\%s\\0%i.XA",lang_dirs[lang],i+1);
		}
		else
		{
			sprintf(fname,"\\%s\\%i.XA",lang_dirs[lang],i+1);
		}

		if(XAgetStatus())
		{
			music = XAgetFileInfo(fname);	// the ";1" is added internally
			if(music)
			{
				ref_music[NUMBEROF8TRACKXA+i] = *music;
				FREE(music);
			}
		}
	}
}

int sound_LoadLanguageBankBody(int lang, char *leafname)
{
	char fname[40];
	unsigned long crc;

	sprintf(fname,"%s_%s",leafname,lang_dirs[lang]);
	crc = utilStr2CRC(fname);
	sprintf(fname,"%s\\%s_%s",leafname,leafname,lang_dirs[lang]);

	return( soundLoadJustBody2(fname, 0, crc));

}

void sound_FreeLanguageBank()
{
	if(pSfxGenericBank)
	{
		if(sampleBanks[0])
		{
			amHeapFree(audio64Banks[0]);
			audio64Banks[0] = NULL;
			syFree(sampleBanks[0]);
			sampleBanks[0] = NULL;
		}

//ma	sfxUnloadSampleBank(pSfxGenericBank);
//		sfxDestroySampleBank(pSfxGenericBank);	// We haven't GOT a RAM copy of the SBB
//ma	sfxRemoveSampleBank(pSfxGenericBank,1);	// Remove from set used for searching
		pSfxGenericBank = NULL;
	}
}

void sound_LoadLanguageBank(int lang)
{
	char fname[40];
	unsigned long crc;

	overlayLoadingBegin(1);
	sound_FreeLanguageBank();
	sprintf(fname,"GEN_%s",lang_dirs[lang]);
	crc = utilStr2CRC(fname);
	sprintf(fname,"SOUND\\GEN_%s",lang_dirs[lang]);
	pSfxGenericBank = (SfxBankType *)soundLoadBodyAndHeaderFull(fname,crc);
}

void sound_XA_Start(int tune)
{
	int file_no;
	int channel;
	int loop;

	if(tune < 0) tune = -1;	// RFW - I'm getting -ve inputs during the fma

	if(tune & SOUND_XA_NOLOOP)
	{
		loop = 0;
	}
	else
	{
		loop = 1;
	}
	tune &= 127;

	if(music_playing >= 0)
	{
		sound_XA_Stop();
	}

	if(tune == -1)	// This will happen if we try to "resume" from nothing (which can happen)
	{
		music_playing = tune;
		music_to_resume = tune;
		return;
	}

	file_no = tune;			//xa_tracks_list[menu_choice].file_no;
	channel = tune & 7;		//xa_tracks_list[menu_choice].channel;

//	XAsetStatus(1);	// On golds, this is set according to the return value of cdinit

	music = XAgetFileInfo(xa_file_names[file_no]);	// the ";1" is added internally
	if(!music)
	{
		tune = -1;
		music_playing = tune;
		music_to_resume = tune;
		XAsetStatus(0);
	}

/*
	if(!ref_music[file_no].startPos)
	{
		music_playing = -1;
		music_to_resume = -1;
		return;
	}
*/
	music_fader = 256;


	XAstart(1);	// 1 = double speed


	XAplayChannel(music, channel, loop, optionData.sound.musicvol);	// "1" = loop-enable
	music_playing = tune;
	music_to_resume = tune;
	utilPrintf("XA Starting tune %d\n",tune);
}

void sound_XA_Stop()
{
	if(music_playing >= 0)
	{
		XAstop();
		if(music)
		{
			ADXT_Destroy(music->adxt);		// Free the file handle
			FREE(music);		
		}
		music_playing = -1;
		music = NULL;
	}
	music_to_resume = -1;
	music_queued = -1;
}

// As we're mixing 8 and 32 track XA's what Im going to do is put the 8 track ones first in the list so I can work out
// the track number for the 32 track one's
void sound_XA_StartFma(int tune)
{
	int file_no;
	int channel;
	int loop;
	int	newVolume,temp;
	char fmaleafname[32];

	if(tune < 0) tune = -1;	// RFW - I'm getting -ve inputs during the fma

	file_no=NUMBEROF8TRACKXA;
	
	// NOTE fma XA's should never loop
/*	if(tune & SOUND_XA_NOLOOP)
	{
		loop = 0;
	}
	else
	{
		loop = 1;
	}
	tune &= 127;
*/
	loop=0;
	if(music_playing >= 0)
	{
		sound_XA_Stop();
	}

	if(tune == -1)	// This will happen if we try to "resume" from nothing (which can happen)
	{
		music_playing = tune;
		music_to_resume = tune;
		return;
	}

	DB("tune %d\n",tune);
	file_no += tune >> 5;	//xa_tracks_list[menu_choice].file_no;
	channel = tune & 31;		//xa_tracks_list[menu_choice].channel;

/*
	if(!ref_music[file_no].startPos)
	{
		DB("Not playing non-present fma track\n");
		music_playing = -1;
		music_to_resume = -1;
		return;
	}
*/
	DB("XA start Fma was asked to play file num %d channel num %d\n",file_no,channel);

//	XAsetStatus(1);	// On golds, this is set according to the return value of cdinit

	sprintf(fmaleafname, "FMA%03i.ADX", tune);
	music = XAgetFileInfo(fmaleafname);	// the ";1" is added internally

	if(!music)
	{
		tune = -1;
		music_playing = tune;
		music_to_resume = tune;
		XAsetStatus(0);
	}

	music_fader = 256;

	XAstart(1);	// 1 = double speed


	XAplayChannelFMA(music, channel, loop, optionData.sound.speechvol);	// "1" = loop-enable

	//DB("org volume = %d new volume = %d\n",(optionData.sound.speechvol * 100 / OPTIONS_MAX_VOLUME),newVolume);

	music_playing = tune;
	music_to_resume = tune;
	utilPrintf("XA Starting tune %d\n",tune);
}



// After calling here we may ask to resume, or we might switch to another tune
// Thing is, we need to keep track of where the cd was when it stopped....

// Note - doesn't overwrite the "queued" track
void sound_XA_PauseForLoading()
{
	XAstorePrevious();
//	XAstop();
	if(music)
		ADXT_Pause(music->adxt, 1);
	musicPaused=1;
}

void sound_XA_EnsurePaused()
{
	if(music_playing != -1)
	{
		sound_XA_PauseForLoading();
	}
}

void sound_XA_Resume(int tune)
{
char leafnamebuffer[128];

// If "tune" is the same as the tune-to-resume, then carry on where we left off
	if(tune == music_to_resume || tune == -1)
	{
		int file_no;
		int channel;

		if(tune==-1)
		{
			tune = music_to_resume;
		}
		if(tune == -1)	// This will happen if we try to "resume" from nothing (which can happen)
		{
			music_playing = tune;
			music_to_resume = tune;
			return;
		}

		if(music_playing >= 0 && !musicPaused)
		{
			sound_XA_Stop();
		}

		file_no = tune; // >> 3;
		channel = tune & 7;		//xa_tracks_list[menu_choice].channel;

		XAsetStatus(1);
		if (musicPaused)
		{
//			XAplayChannel(music, channel, 1, optionData.sound.musicvol * 100 / OPTIONS_MAX_VOLUME);
			if(music)
				ADXT_Pause(music->adxt, 0);
			musicPaused = 0;
		} else {

			music = XAgetFileInfo(xa_file_names[file_no]);	// the ";1" is added internally
			XAstart(1);										// 1 = double speed
			XAplayChannel(music, channel, 1, optionData.sound.musicvol);			// 1 = loop enable, 90 = volume (which should be got from options, really)
		}

		music_playing = tune;
		music_to_resume = tune;

		utilPrintf("XA Resuming tune %d\n",tune);



		XArestartPrevious();

	}
	else
	{
		// else start at the very beginning
		sound_XA_Start(tune);
		music_queued = -1;
	} 
}

void sound_XA_Queue(int tune)
{
	if(XAData.currXA)
	{
		XAData.currXA->loop = 0;
	}
	music_queued = tune;
}



