/***************************************************************************
 *
 * Audio.c
 *
 *************************************************************************
 *
 *
 *
 *************************************************************************/

/*
 * includes
 */

#include "ult_64.h"
#include "memory.h"
#include "file.h"
#include "threads.h"
#include "audio.h"


extern void
	Stack_Setup( void *_stack, s32 size );


#define MAX_CHANNELS 16

#define FLAG_REPEAT	1
#define FLAG_PAUSE	2

#define STACK_SIZE (2*1024)


typedef enum
{
	TYPE_NONE,
	TYPE_PCM,
	TYPE_ADPCM,
	TYPE_GSM
} EType;

typedef struct SHeader
{
	char
		Type[4],
		Game[4],
		Platform[4];
	u32
		Version,
		NumSamples;
} SHeader;

typedef struct SMusicHeader
{
	char
		Type[4],
		Game[4],
		Platform[4];
	u32
		Version,
		NumInstrs,
		InstrsOffset,
		NumSongs,
		SongsOffset;
} SMusicHeader;

typedef struct SSample
{
	u8
		ROMtype,
		RAMtype;
	u16
		Freq,
		Chunks,
		Size,
		Loop1,
		Loop2;
	u32
		Data;
} SSample;

typedef enum
{
	STATE_OFF,
	STATE_ATTACK,
	STATE_DECAY,
	STATE_SUSTAIN,
	STATE_RELEASE
} EState;

typedef struct SChannel
{
	volatile EState
		State;
	s32
		Id;
	void
		(*Routine)( struct SChannel *channel, s32 *buffer, s32 count );
	SSample
		*Sample;
	u8
		*Data;
	u32
		Rate,
		Offset,
		Flags,
		Chunks,
		BufSize,
		Cache;
	s32
		Time,
		Base,
		Note,
		Pitch,
		Volume,
		FullVol,
		ScaleVol,
		Attack,
		Decay,
		Sustain,
		Release,
		BalanceL,
		BalanceR,
		VibDepth,
		VibNote,
		VibNoteAdd,
		VibScale,
		VibScaleAdd;
	s16
		Buffer[368];
} SChannel;


extern void
	GSM2_Init();

extern int
	GSM2_Play( s32 *buffer, s32 size, s32 lvol, s32 rvol );

extern s32
	Filtering;

u32
	*GSM2file,
	GSM2size;


volatile s32
	AudioCheck = FALSE,
	AudioOverruns = 0,
	AudioBusy = 0,
	AudioMaxCmds = 0;


static OSMesgQueue
	AudioMessageQ;

static OSMesg
	DummyMessage,
	AudioMessageBuf;

static OSThread
	AudioThread;

u64
	AudioThreadStack[STACK_SIZE/8];

static volatile u8
	Halted = FALSE,
	Done = FALSE;

static s16
	*AudioBuffers[2];

static s32
	*MixBuffer;

static s32
	MusicVolume = AUDIO_MAX_VOLUME;

static SChannel
	Channels[MAX_CHANNELS];

static s32
	PitchTable[12+1] =
	{
		65536/2,
		69432/2,
		73561/2,
		77935/2,
		82570/2,
		87480/2,
		92681/2,
		98193/2,
		104031/2,
		110217/2,
		116771/2,
		123715/2,
		131072/2
	};


extern volatile u32
	VBLticks;

s32
	ALines[2][2] = { 0, 0, 0, 0 };


/*******************************************************************/

extern OSPiHandle
	*cartRomHandle;


static OSMesgQueue
	DMAqueue;

static OSMesg
	DMAmesg;


static void SetupDMA()
{
	osCreateMesgQueue( &DMAqueue, &DMAmesg, 1 );
}


void DMAfromROM( u8 *src, u8 *dst, s32 size )
{
#if 0

	register s32
		i;
	register u8
		*to = (u8 *)dst;

	for (i=0; i<size; i+=4)
		osEPiReadIo( cartRomHandle, (u32)&src[i], (u32 *)&to[i] );

#else

	OSIoMesg
		io_mesg;

	osInvalDCache( dst, size );

	io_mesg.hdr.pri = OS_MESG_PRI_HIGH;
	io_mesg.hdr.retQueue = &DMAqueue;
	io_mesg.dramAddr = (void *)dst;
	io_mesg.devAddr = (u32)src;
	io_mesg.size = size;

	osEPiStartDma( cartRomHandle, &io_mesg, OS_READ );

	osRecvMesg( &DMAqueue, &DMAmesg, OS_MESG_BLOCK );

#endif
}


/*******************************************************************/

extern void
	DecodeAdpcm( u8 *psrc, s16 *pdst, int len );


static u64
	ADPCMsrc[192/8];


static void PlayADPCM( SChannel *channel, s32 *buffer, s32 count )
{
	static s32
		prev = 0;
	SSample
		*sample;
	s32
		i,
		size,
		next,
		volume;

	volume = (channel->FullVol >> 16);
	sample = channel->Sample;

	while (count)
	{
		if (!channel->BufSize)
		{
			if (!channel->Chunks)
			{
				if (channel->Flags & FLAG_REPEAT)
				{
					channel->Data = (u8 *)sample->Data;
					channel->Chunks = sample->Chunks;
				}
				else
				{
					channel->State = STATE_OFF;
					return;
				}
			}

			DMAfromROM( channel->Data, (u8 *)ADPCMsrc, 192 );

			channel->BufSize = *(u16 *)ADPCMsrc;
if (channel->BufSize > 368)
	FatalError( "bad adpcm size", __FILE__, __LINE__ );
			DecodeAdpcm( (u8 *)((u32)ADPCMsrc + 2), channel->Buffer, channel->BufSize );
			channel->Offset = 0;

			channel->Data += 192;
			channel->Chunks--;
		}


		size = channel->BufSize * 2;
		if (size > count)
			size = count;

		for (i=0; i<(size/2); i++)
		{
			next = channel->Buffer[channel->Offset];
			channel->Offset++;

			next = (next * volume) >> 7;

			prev += next;
			prev >>= 1;

			*buffer++ += prev;
			*buffer++ += prev;
			*buffer++ += next;
			*buffer++ += next;

			prev = next;
		}

		channel->BufSize -= size/2;
		count -= size;
	}
}


static void PlayRamADPCM( SChannel *channel, s32 *buffer, s32 count )
{
	SSample
		*sample;
	s32
		i,
		s,
		vol,
		size;

	sample = channel->Sample;
	vol = (channel->FullVol >> 16);

	while (count)
	{
		size = channel->Time;

		if (!size)
		{
			if (!channel->Chunks)
			{
				channel->State = STATE_OFF;
				return;
			}

			size = *(u16 *)channel->Data;
			DecodeAdpcm( &channel->Data[2], channel->Buffer, size );
			channel->Data += 192;
			channel->Chunks--;
			channel->Time = size;
			channel->Offset = 0;
		}

		if (size > count)
			size = count;

		if (sample->Freq == 22050)//channel->Rate == (1<<16))
		{
			for (i=0; i<size; i++)
			{
				s = channel->Buffer[channel->Offset];
				channel->Offset++;

				s = (s * vol) >> 7;

				*buffer++ += s;
				*buffer++ += s;
			}

			channel->Time -= size;
		}
		else
		{
			// Assume it's 11khz...

			for (i=0; i<size; i++)
			{
				if (!(i & 1))
				{
					s = channel->Buffer[channel->Offset];
					channel->Offset++;
				}

				s = (s * vol) >> 7;

				*buffer++ += s;
				*buffer++ += s;
			}

			if (size < 2)
				channel->Time -= size;
			else
				channel->Time -= size >> 1;
		}

		count -= size;
	}
}


/*******************************************************************/

static void PlayGSM2( SChannel *channel, s32 *buffer, s32 count )
{
	if (GSM2_Play( buffer, count, channel->BalanceL, channel->BalanceR ))
		channel->State = STATE_OFF;
}


static void PlaySimple( SChannel *channel, s32 *buffer, s32 count )
{
	SSample
		*sample;
	register s32
		s,
		lvol,
		rvol,
		volume;
	register s16
		*data;
	register u32
		offset;

	sample = channel->Sample;


	if (channel->State == STATE_RELEASE)
	{
		channel->State = STATE_OFF;
		return;
	}


	data = (s16 *)sample->Data;

	volume = (channel->FullVol * channel->ScaleVol) >> 7;
	lvol = (volume * channel->BalanceL) >> 16;
	lvol = (volume * channel->BalanceR) >> 16;


	offset = (channel->Offset >> 16);
	if ((offset + count) >= sample->Size)
		count = sample->Size - offset;

	while (count)
	{
		s = data[offset];

		*buffer++ += s;//(s * lvol) >> (7+7);
		*buffer++ += s;//(s * rvol) >> (7+7);


		offset++;
		count--;
	}


	if (offset >= sample->Size)
		channel->State = STATE_OFF;
	else
		channel->Offset = offset << 16;
}


static u32 CacheBuffer( SChannel *channel, u32 offset )
{
	u32
		len,
		chunk;

	if (offset < channel->Cache || offset >= (channel->Cache + 368))
	{
		chunk = offset / 368;
		channel->Cache = chunk * 368;

		Copy32s( &channel->Data[chunk*192], ADPCMsrc, 192/4 );

		len = *(u16 *)ADPCMsrc;
		DecodeAdpcm( (u8 *)((u32)ADPCMsrc + 2), channel->Buffer, len );
	}

	return( offset - channel->Cache );
}


static void PlayComplex( SChannel *channel, s32 *buffer, s32 count )
{
	SSample
		*sample;
	s32
		s,
		vol,
		volume,
		volend,
		voladd,
		vibrato;
	s16
		*data;

	sample = channel->Sample;
	data = channel->Buffer;//(s16 *)sample->Data;
	volume = channel->Volume;

	if (channel->VibDepth)
	{
		vibrato = channel->VibNote * channel->VibScale;
		vibrato >>= (16+12)-11;

		channel->VibScale += channel->VibScaleAdd;
		if (channel->VibScale > (1<<16))
			channel->VibScale = (1<<16);

		channel->VibNote += channel->VibNoteAdd;
		if (channel->VibNote >= +channel->VibDepth ||
				channel->VibNote <= -channel->VibDepth)
			channel->VibNoteAdd = -channel->VibNoteAdd;
	}
	else
		vibrato = 0;


	{
		s32
			note,
			frac,
			pitch,
			shift = 0;

		note = channel->Note - channel->Base;
		note <<= 11;
		note += channel->Pitch;
		note += vibrato;
		frac = note & 0x7ff;
		note >>= 11;

		if (note > 0)
		{
			while (note >= 12)
			{
				shift++;
				note -= 12;
			}
			pitch = PitchTable[note];
			pitch += ((PitchTable[note+1] - pitch) * frac) >> 11;
			channel->Rate = sample->Freq * pitch / 22050;
			channel->Rate <<= 1+shift;
		}
		else if (note < 0)
		{
			note = 12 + note;
			while (note < 0)
			{
				shift++;
				note += 12;
			}
			pitch = PitchTable[note];
			pitch += ((PitchTable[note+1] - pitch) * frac) >> 11;
			channel->Rate = sample->Freq * pitch / 22050;
			channel->Rate >>= shift;
		}
		else
			channel->Rate = (sample->Freq << 16) / 22050;

		if (!channel->Rate)
			channel->Rate = 1<<16;
	}


	switch (channel->State)
	{
		case STATE_ATTACK:
			channel->Volume += channel->Attack;
			channel->Time--;

			if (!channel->Time)
			{
				if (channel->Decay)
				{
					channel->State = STATE_DECAY;
					channel->Volume = channel->FullVol;
					channel->Time = channel->Decay;
					vol = (channel->FullVol * channel->Sustain) >> 7;
					channel->Decay = (channel->Volume - vol) / channel->Time;
				}
				else
				{
					channel->State = STATE_SUSTAIN;
					channel->Volume = (channel->FullVol * channel->Sustain) >> 7;
				}
			}
			break;

		case STATE_DECAY:
			channel->Volume -= channel->Decay;
			channel->Time--;

			if (!channel->Time)
			{
				channel->State = STATE_SUSTAIN;
				channel->Volume = (channel->FullVol * channel->Sustain) >> 7;
			}
			break;

		case STATE_RELEASE:
			if (sample->Loop2 && sample->Loop2 != sample->Size)
				break;

			channel->Volume -= channel->Release;
			channel->Time--;

			if (!channel->Time || !volume)
			{
				channel->State = STATE_OFF;
//				return;
			}
			break;

		case STATE_SUSTAIN:
			volend = channel->Volume;
			voladd = channel->FullVol;
			break;
	}

	volend = channel->Volume;
	volend = (volend * channel->ScaleVol) >> 7;
	volume = (volume * channel->ScaleVol) >> 7;
	voladd = (volend - volume) / count;

	while (count > 0)
	{
		u32
			o;
		s32
			l,
			s1,
			s2;

		l = channel->Offset & 0xffff;

#if 0
		s = data[channel->Offset >> 16];
#else
		o = CacheBuffer( channel, channel->Offset >> 16 );
		s = data[o];
#endif

		if (channel->Offset & 0xffff)
		{
			o = (channel->Offset >> 16) + 1;

			s1 = s;
#if 1
			o = CacheBuffer( channel, o );
#endif
			s2 = data[o];

			s2 = (s2 * l) >> 16;
			l = (1 << 16) - l;
			s1 = (s1 * l) >> 16;
			s = s1 + s2;
		}

		vol = (volume >> 16);
		volume += voladd;

		s *= vol;

		*buffer++ += (s * channel->BalanceL) >> (7+7);
		*buffer++ += (s * channel->BalanceR) >> (7+7);


		channel->Offset += channel->Rate;

		if (sample->Loop2)
		{
			if (channel->State != STATE_RELEASE || sample->Loop2 == sample->Size)
			{
				if ((channel->Offset >> 16) >= sample->Loop2)
				{
					channel->Offset -= (sample->Loop2 << 16);
					channel->Offset += (sample->Loop1 << 16);
				}
			}
		}

		if ((channel->Offset >> 16) >= sample->Size)
		{
			channel->State = STATE_OFF;
			return;
		}

		count--;
	}
}


/*******************************************************************/

#define LIST_SIZE 16


typedef enum
{
	CMD_NONE,
	CMD_SFX_PLAY,
	CMD_SFX_STOP,
	CMD_SFX_VOLUME,
	CMD_SFX_BALANCE,
	CMD_SFX_REPEAT,
	CMD_SFX_PAUSE,
	CMD_MUSIC_PLAY,
	CMD_MUSIC_STOP,
	CMD_MUSIC_PAUSE,
	CMD_STOP_ALL
} ECommand;

typedef struct SSfxPlay
{
	void
		*Bank;
	u32
		Index;
} SSfxPlay;

typedef struct SSfxVolume
{
	u8
		Volume;
} SSfxVolume;

typedef struct SSfxBalance
{
	u8
		Left,
		Right;
} SSfxBalance;

typedef struct SSfxRepeat
{
	s32
		Repeat;
} SSfxRepeat;

typedef struct SSfxPause
{
	s32
		Pause;
} SSfxPause;

typedef struct SMusicPlay
{
	void
		*Song;
	u32
		Index;
} SMusicPlay;

typedef struct SMusicPause
{
	s32
		Pause;
} SMusicPause;

typedef struct SCommand
{
	ECommand
		Command;
	s32
		Id;
	union
	{
		SSfxPlay		SfxPlay;
		SSfxVolume	SfxVolume;
		SSfxBalance	SfxBalance;
		SSfxRepeat	SfxRepeat;
		SSfxPause		SfxPause;
		SMusicPlay	MusicPlay;
		SMusicPause	MusicPause;
	} Data;
} SCommand;

typedef struct SList
{
	volatile s32
		Ready;
	SCommand
		Commands[LIST_SIZE];
} SList;


static s32
	ExtUnique = 0,
	IntUnique = 0,
	ReadList = 0,
	WriteList = 0,
	CmdCount;

static SCommand
	*CmdPtr = NULL;

static SList
	CmdLists[2];


/*******************************************************************/

static s8 SfxPlay( const s32 id, void *bank, s32 index )
{
	SChannel
		*channel = Channels;
	SSample
		*sample;
	s32
		i;

	if (!bank)
		return( -1 );

	sample = (SSample *)((u32)bank + (5*4) + (index * sizeof( SSample )));
	if (!sample->Data)
		return( -1 );


	for (i=0; i<MAX_CHANNELS; i++)
	{
		if (channel->State == STATE_OFF)
		{
			channel->Id = id;

			channel->Sample = sample;
			channel->Offset = 0;
			channel->Rate = 1<<16;
			channel->Flags = 0;

			channel->Base = 0;
			channel->Note = 0;
			channel->Pitch = 0;

			channel->BalanceL = 128;
			channel->BalanceR = 128;

			channel->State = STATE_SUSTAIN;

			channel->Attack = 0;
			channel->Decay = 0;
			channel->Sustain = 128;
			channel->Release = 0;

			channel->FullVol = 128<<16;
			channel->ScaleVol = 128;

			channel->Volume = 128<<16;

			channel->Time = 0;

			channel->VibDepth = 0;

			channel->Cache = (1<<30);

			if (sample->ROMtype == TYPE_GSM)
			{
				GSM2_Init();
				GSM2file = (u32 *)sample->Data;
				GSM2size = sample->Chunks;
				channel->Routine = PlayGSM2;
			}
			else if (sample->RAMtype == TYPE_ADPCM)
			{
				channel->Data = (u8 *)sample->Data;
				channel->Time = 0;
				channel->Chunks = sample->Chunks;
				channel->Routine = PlayRamADPCM;
			}
			else if (sample->RAMtype == TYPE_NONE)
			{
				channel->Data = (u8 *)sample->Data;
				channel->BufSize = 0;
				channel->Chunks = sample->Chunks;
				channel->Routine = PlayADPCM;
			}
			else
				channel->Routine = PlaySimple;

			return( i );
		}

		channel++;
	}

	return( -1 );
}


static void SfxStop( const s32 id )
{
	SChannel
		*channel = Channels;
	s32
		i;

	for (i=0; i<MAX_CHANNELS; i++)
	{
		if (channel->Id == id && channel->State != STATE_OFF)
		{
			channel->State = STATE_OFF;
			return;
		}

		channel++;
	}
}


static void SfxRelease( const s32 id )
{
	SChannel
		*channel = Channels;
	s32
		i;

	for (i=0; i<MAX_CHANNELS; i++)
	{
		if (channel->Id == id && channel->State != STATE_OFF && channel->State != STATE_RELEASE)
		{
			channel->State = STATE_RELEASE;

			if (channel->Release)
			{
				channel->Time = channel->Release;
				channel->Release = channel->Volume / channel->Time;
			}
			else
			{
				channel->Release = 0;
				channel->Time = 4*60;
			}

			return;
		}

		channel++;
	}
}


static void SfxBalance( const s32 id, u8 left, u8 right )
{
	SChannel
		*channel = Channels;
	s32
		i;

	for (i=0; i<MAX_CHANNELS; i++)
	{
		if (channel->Id == id)
		{
			channel->BalanceL = left;
			channel->BalanceR = right;

			return;
		}

		channel++;
	}
}


static void SfxVolume( const s32 id, u8 volume )
{
	SChannel
		*channel = Channels;
	s32
		i;

	for (i=0; i<MAX_CHANNELS; i++)
	{
		if (channel->Id == id)
		{
			channel->FullVol = (s32)volume << 16;
			return;
		}

		channel++;
	}
}


static void SfxScaleVol( const s32 id, u8 volume )
{
	SChannel
		*channel = Channels;
	s32
		i;

	for (i=0; i<MAX_CHANNELS; i++)
	{
		if (channel->Id == id)
		{
			channel->ScaleVol = volume;
			return;
		}

		channel++;
	}
}


static void SfxPitch( const s32 id, s32 pitch )
{
	SChannel
		*channel = Channels;
	s32
		i;

	for (i=0; i<MAX_CHANNELS; i++)
	{
		if (channel->Id == id)
		{
			channel->Routine = PlayComplex;

			channel->Pitch = pitch;
			return;
		}

		channel++;
	}
}


static void SfxRepeat( const s32 id, s32 repeat )
{
	SChannel
		*channel = Channels;
	s32
		i;

	for (i=0; i<MAX_CHANNELS; i++)
	{
		if (channel->Id == id)
		{
			if (repeat)
				channel->Flags |= FLAG_REPEAT;
			else
				channel->Flags &= ~FLAG_REPEAT;

			return;
		}

		channel++;
	}
}


static void SfxPause( const s32 id, s32 pause )
{
	SChannel
		*channel = Channels;
	s32
		i;

	for (i=0; i<MAX_CHANNELS; i++)
	{
		if (channel->Id == id)
		{
			if (pause)
				channel->Flags |= FLAG_PAUSE;
			else
				channel->Flags &= ~FLAG_PAUSE;

			return;
		}

		channel++;
	}
}


static void SetVolume( const s32 c, const u8 volume )
{
	Channels[c].FullVol = (s32)volume << 16;
}


static void SetBalance( const s32 c, u8 left, u8 right )
{
	SChannel
		*channel;

	channel = &Channels[c];

	channel->BalanceL = left;
	channel->BalanceR = right;
}


static void SetADSR( const s32 c, u8 attack, u8 decay, u8 sustain, u8 release )
{
	SChannel
		*channel;
	s32
		i,
		vol;

	channel = &Channels[c];

	channel->Routine = PlayComplex;

	channel->Attack = attack;
	channel->Decay = decay;
	channel->Sustain = sustain;
	channel->Release = release;

	if (attack)
	{
		channel->State = STATE_ATTACK;
		channel->Time = attack;
		channel->Attack = channel->FullVol / attack;
		channel->Volume = 0;
	}
	else
	{
		if (decay)
		{
			channel->State = STATE_DECAY;
			channel->Time = decay;
			channel->Volume = channel->FullVol;
			vol = (channel->FullVol * channel->Sustain) >> 7;
			channel->Decay = (channel->FullVol - vol) / decay;
		}
		else
			channel->Volume = (channel->FullVol * channel->Sustain) >> 7;
	}
}


static void SetBase( const s32 c, u8 base )
{
	SChannel
		*channel;

	channel = &Channels[c];

	channel->Routine = PlayComplex;
	channel->Base = base;
}


static void SetNote( const s32 c, s32 note )
{
	SChannel
		*channel;

	channel = &Channels[c];

	channel->Routine = PlayComplex;
	channel->Note = note;
}


static void SetVibrato( const s32 c, u8 ramp, u8 rate, s16 depth )
{
	SChannel
		*channel;

	channel = &Channels[c];

	if (depth)
	{
		channel->Routine = PlayComplex;

		channel->VibDepth = depth;

		rate /= 2;
		if (!rate)
			rate = 1;

		channel->VibNote = 0;
		channel->VibNoteAdd = channel->VibDepth / rate;

		if (ramp)
		{
			channel->VibScale = 0;
			channel->VibScaleAdd = (1<<16) / ramp;
		}
		else
		{
			channel->VibScale = (1<<16);
			channel->VibScaleAdd = 0;
		}
	}
}


/*******************************************************************/

// Music handler...

#define MAX_TRACKS 16


typedef enum
{
	EVENT_NOTE_ON,
	EVENT_NOTE_OFF,
	EVENT_PITCH_BEND,
	EVENT_SCALE_VOL
} EEvent;

typedef struct
{
	u8
		Sample,
		Note;
	u8
		BalanceL,
		BalanceR;
	u8
		Attack,
		Decay,
		Sustain,
		Release;
	u8
		VibRamp,
		VibRate;
	s16
		VibDepth;
} SInstr;

typedef struct
{
	s32
		Id;
	s16
		Group,
		Channel;
} STrack;


static void
	*MusicBank = NULL,
	*MusicSong = NULL;

static SInstr
	*Instruments;

static u16
	MusicTime = 0,
	MusicPaused = FALSE;

static u8
	*MusicPtr = NULL,
	GroupVols[16];

static STrack
	Tracks[MAX_TRACKS];


static void ProcessMusic()
{
	u16
		slot;
	u8
		count,
		event,
		group;

	if (MusicPtr && !MusicPaused)
	{
		slot = *(u16 *)MusicPtr;

		if (slot == 0xffff)
		{
			MusicTime = 0;
			MusicPtr = NULL;
			return;
		}

		if (slot == MusicTime)
		{
			count = MusicPtr[2];
			MusicPtr += 3;

			while (count)
			{
				event = *MusicPtr++;
				group = event >> 4;
				event &= 0x0f;

				switch (event)
				{
					case EVENT_NOTE_ON:
						{
							SInstr
								*instr;
							u32
								inum,
								tnum;
							s32
								c;

							tnum = MusicPtr[0];
							inum = MusicPtr[1];
							instr = &Instruments[inum];

							SfxStop( Tracks[tnum].Id );

							IntUnique--;
							Tracks[tnum].Id = IntUnique;
							Tracks[tnum].Group = group;

							c = SfxPlay( IntUnique, MusicBank, instr->Sample );
							if (c >= 0)
							{
								SetVolume( c, (MusicVolume * MusicPtr[3]) >> (7+2) );
								SetBalance( c, instr->BalanceL, instr->BalanceR );
								SetADSR( c, instr->Attack, instr->Decay, instr->Sustain, instr->Release );
								SetBase( c, instr->Note );
								SetNote( c, MusicPtr[2] );
								SetVibrato( c, instr->VibRamp, instr->VibRate, instr->VibDepth );
								SfxScaleVol( IntUnique, GroupVols[group] );
							}
						}
						MusicPtr += 4;
						break;

					case EVENT_NOTE_OFF:
						{
							u32
								tnum;

							tnum = MusicPtr[0];
							SfxRelease( Tracks[tnum].Id );
						}
						MusicPtr += 1;
						break;

					case EVENT_PITCH_BEND:
						{
							s16
								i,
								pitch;

							pitch = (s8)MusicPtr[0];
							pitch = (pitch << 8) | MusicPtr[1];

							for (i=0; i<MAX_TRACKS; i++)
							{
								if (Tracks[i].Group == group)
									SfxPitch( Tracks[i].Id, pitch );
							}
						}
						MusicPtr += 2;
						break;

					case EVENT_SCALE_VOL:
						GroupVols[group] = MusicPtr[0];
						{
							s32
								i;

							for (i=0; i<MAX_TRACKS; i++)
							{
								if (Tracks[i].Group == group)
									SfxScaleVol( Tracks[i].Id, MusicPtr[0] );
							}
						}
						MusicPtr += 1;
						break;
				}

				count--;
			}

			MusicPtr = (u8 *)(((u32)MusicPtr + 1) & ~1);
		}

		MusicTime++;
	}
}


static void MusicPause( s32 pause )
{
	STrack
		*track = Tracks;
	s32
		i;

	MusicPaused = pause;

	for (i=0; i<MAX_TRACKS; i++)
	{
		SfxPause( track->Id, pause );

		track++;
	}
}


/*******************************************************************/

static void ProcessCommands()
{
	SMusicHeader
		*header;
	SCommand
		*cmd;
	s32
		count;

	if (CmdLists[ReadList].Ready)
	{
		cmd = &CmdLists[ReadList].Commands[0];
		count = LIST_SIZE;

		while (count && cmd->Command != CMD_NONE)
		{
			switch (cmd->Command)
			{
				case CMD_SFX_PLAY:
					{
						SSfxPlay
							*data;

						data = &cmd->Data.SfxPlay;
						SfxPlay( cmd->Id, data->Bank, data->Index );
					}
					break;

				case CMD_SFX_STOP:
					{
						SfxStop( cmd->Id );
					}
					break;

				case CMD_SFX_VOLUME:
					{
						SSfxVolume
							*data;

						data = &cmd->Data.SfxVolume;
						SfxVolume( cmd->Id, data->Volume );
					}
					break;

				case CMD_SFX_BALANCE:
					{
						SSfxBalance
							*data;

						data = &cmd->Data.SfxBalance;
						SfxBalance( cmd->Id, data->Left, data->Right );
					}
					break;

				case CMD_SFX_REPEAT:
					{
						SSfxRepeat
							*data;

						data = &cmd->Data.SfxRepeat;
						SfxRepeat( cmd->Id, data->Repeat );
					}
					break;

				case CMD_SFX_PAUSE:
					{
						SSfxPause
							*data;

						data = &cmd->Data.SfxPause;
						SfxPause( cmd->Id, data->Pause );
					}
					break;

				case CMD_MUSIC_PLAY:
					{
						SMusicPlay
							*data;
						s32
							i;

						for (i=0; i<MAX_TRACKS; i++)
							SfxStop( Tracks[i].Id );

						data = &cmd->Data.MusicPlay;
						MusicSong = data->Song;

						for (i=0; i<16; i++)
							GroupVols[i] = 128;

						for (i=0; i<MAX_TRACKS; i++)
						{
							Tracks[i].Id = 0;
							Tracks[i].Group = -1;
							Tracks[i].Channel = -1;
						}

						header = (SMusicHeader *)MusicSong;

						i = header->InstrsOffset;
						Instruments = (SInstr *)((u32)MusicSong + i);

						i = header->SongsOffset;
						i += data->Index * 4;
						i = *(u32 *)((u32)MusicSong + i);
						i += (256/32) * 4;
						MusicPtr = (u8 *)((u32)MusicSong + i);

						MusicTime = 0;
					}
					break;

				case CMD_MUSIC_STOP:
					{
						s32
							i;

						for (i=0; i<MAX_TRACKS; i++)
							SfxStop( Tracks[i].Id );

						MusicPtr = NULL;
						MusicTime = 0;
					}
					break;

				case CMD_MUSIC_PAUSE:
					{
						SMusicPause
							*data;

						data = &cmd->Data.MusicPause;
						MusicPause( data->Pause );
					}
					break;

				case CMD_STOP_ALL:
					{
						s32
							i;

						for (i=0; i<MAX_CHANNELS; i++)
							SfxStop( Channels[i].Id );

						MusicPtr = NULL;
						MusicTime = 0;
					}
					break;
			}

			cmd++;
			count--;
		}

		CmdLists[ReadList].Ready = FALSE;
		ReadList = 1 - ReadList;
	}
}


static void AudioProc( void *arg )
{
	s16
		*aptr;
	s32
		i,
		s,
		freq,
		size,
		*mptr,
		which = 0,
		samples;
	s32
		tick;

	SetupDMA();

	osCreateMesgQueue( &AudioMessageQ, &AudioMessageBuf, 1 );
	osSetEventMesg( OS_EVENT_AI, &AudioMessageQ, DummyMessage );

	freq = osAiSetFrequency( 22050 );
	size = (freq * 2 * 2) / 60;
	size += 7;
	size &= ~7;

	samples = size / (2*2);

	AudioBuffers[0] = MemMalloc( size, MEM_ANY );
	AudioBuffers[1] = MemMalloc( size, MEM_ANY );

	Set32s( AudioBuffers[0], 0, size/4 );
	Set32s( AudioBuffers[1], 0, size/4 );

	osAiSetNextBuffer( AudioBuffers[0], size );
	osAiSetNextBuffer( AudioBuffers[1], size );

	MixBuffer = MemMalloc( samples*2*4, MEM_ANY );

	while (!Halted)
	{
AudioCheck = 0;

		if (osRecvMesg( &AudioMessageQ, NULL, OS_MESG_NOBLOCK ) >= 0)
			AudioOverruns++;
		else
			osRecvMesg( &AudioMessageQ, NULL, OS_MESG_BLOCK );

AudioCheck = 1;

tick = VBLticks;
		ALines[tick&1][0] = osViGetCurrentLine();


AudioCheck = 2;

		Set32s( MixBuffer, 0, samples*2 );
AudioCheck = 3;

		ProcessCommands();
AudioCheck = 4;

		ProcessMusic();
AudioCheck = 5;


		AudioBusy = 0;

		for (i=0; i<MAX_CHANNELS; i++)
		{
			if (Channels[i].State != STATE_OFF)
			{
				AudioBusy++;

				if (!(Channels[i].Flags & FLAG_PAUSE))
{
	if ((u32)Channels[i].Routine == (u32)PlayADPCM ||
			(u32)Channels[i].Routine == (u32)PlayADPCM ||
			(u32)Channels[i].Routine == (u32)PlayRamADPCM ||
			(u32)Channels[i].Routine == (u32)PlayGSM2 ||
			(u32)Channels[i].Routine == (u32)PlaySimple ||
			(u32)Channels[i].Routine == (u32)PlayComplex)
	{
	}
	else
			FatalError( "bad channel routine", __FILE__, __LINE__ );

					Channels[i].Routine( &Channels[i], MixBuffer, samples );
}
			}
		}

AudioCheck = 6;

		aptr = AudioBuffers[which];
		mptr = MixBuffer;

		for (i=0; i<(samples*2); i++)
		{
			s = *mptr++;
			if (s < -32768)
				s = -32768;
			else if (s > 32767)
				s = 32767;

			*aptr++ = (s16)s;
		}

AudioCheck = 7;


		osWritebackDCache( AudioBuffers[which], size );
AudioCheck = 8;
		osAiSetNextBuffer( AudioBuffers[which], size );
AudioCheck = 9;
		which = 1 - which;


i = osViGetCurrentLine();
if (tick != VBLticks)
	i += 525;
		ALines[tick&1][1] = i;
	}


	Set32s( AudioBuffers[0], 0, size/4 );
	Set32s( AudioBuffers[1], 0, size/4 );
	osWritebackDCache( AudioBuffers[0], size );
	osWritebackDCache( AudioBuffers[1], size );

	Done = TRUE;
}


void *Audio_LoadBank( char *filename )
{
	FH
		fh;
	SHeader
		header;
	SSample
		*sample;
	void
		*bank;
	s16
		*data;
	s32
		len,
		size,
		count;
	DIR_INFO_T
		*info;
	u32
		i;

	info = FileRomInfo( filename );


	fh = FileOpen( filename, "rb" );

	FileRead( &header, sizeof( SHeader ), 1, fh );

if (header.Version != 2)
{
	FileClose( fh );
	return( NULL );
}

	size = sizeof( SHeader ) + (header.NumSamples * sizeof( SSample ));
	bank = MemMalloc( size, MEM_ANY );

	FileSeek( fh, 0, SEEK_SET );
	FileRead( bank, size, 1, fh );

	sample = (SSample *)((u32)bank + sizeof( SHeader ));

	for (i=0; i<header.NumSamples; i++)
	{
		if (sample->ROMtype == TYPE_ADPCM)
		{
			switch (sample->RAMtype)
			{
				case TYPE_NONE:
					sample->Data += info->romOffset;
					break;

				case TYPE_PCM:
					FileSeek( fh, sample->Data, SEEK_SET );

					size = sample->Size * 2;
					data = MemMalloc( size+2, MEM_ANY );

					count = sample->Chunks;
					size = 0;

					while (count)
					{
						FileRead( ADPCMsrc, 192, 1, fh );

						len = *(u16 *)ADPCMsrc;
						DecodeAdpcm( (u8 *)((u32)ADPCMsrc + 2), &data[size], len );
						size += len;
						count--;
					}

					data[size] = 0;
					sample->Data = (u32)data;
					break;

				case TYPE_ADPCM:
					FileSeek( fh, sample->Data, SEEK_SET );

					size = sample->Chunks * 192;
					data = MemMalloc( size, MEM_ANY );

					FileRead( data, size, 1, fh );
					sample->Data = (u32)data;
					break;

				default:
					sample->Data = 0;
			}
		}
		else if (sample->ROMtype == TYPE_GSM)
		{
			sample->Data += info->romOffset;
		}
		else
			sample->Data = 0;

		sample++;
	}

	FileClose( fh );

	return( bank );
}


void Audio_FreeBank( void *bank )
{
	SHeader
		*header;
	SSample
		*sample;
	s32
		i;

	if (bank)
	{
		header = (SHeader *)bank;
		sample = (SSample *)((u32)bank + sizeof( SHeader ));

		for (i=0; i<header->NumSamples; i++)
		{
			if (sample->RAMtype != TYPE_NONE)
				MemFree( (void *)sample->Data );

			sample++;
		}

		MemFree( bank );
	}
}


static void *LoadMusicBank( char *filename, u32 *used )
{
	FH
		fh;
	SHeader
		header;
	SSample
		*sample;
	void
		*bank;
	s16
		*data;
	s32
		len,
		size,
		count;
	DIR_INFO_T
		*info;
	u32
		i;
s32
	pre_size;

	info = FileRomInfo( filename );


	fh = FileOpen( filename, "rb" );

	FileRead( &header, sizeof( SHeader ), 1, fh );

if (header.Version != 2)
{
	FileClose( fh );
	return( NULL );
}

	size = sizeof( SHeader ) + (header.NumSamples * sizeof( SSample ));
	bank = MemMalloc( size, MEM_ANY );

	FileSeek( fh, 0, SEEK_SET );
	FileRead( bank, size, 1, fh );

	sample = (SSample *)((u32)bank + sizeof( SHeader ));

	for (i=0; i<header.NumSamples; i++)
	{
		if (used[i/32] & (1 << (i & 31)))
		{
			if (sample->ROMtype == TYPE_ADPCM)
			{
				switch (sample->RAMtype)
				{
					case TYPE_NONE:
						sample->Data += info->romOffset;
						break;

					case TYPE_PCM:
						FileSeek( fh, sample->Data, SEEK_SET );

						size = sample->Size * 2;
						data = MemMalloc( size+2, MEM_ANY );
pre_size = size;

						count = sample->Chunks;
						size = 0;

						while (count)
						{
							FileRead( ADPCMsrc, 192, 1, fh );

							len = *(u16 *)ADPCMsrc;
if (len > 368)
	FatalError( "ADPCM size too large!", __FILE__, __LINE__ );
							DecodeAdpcm( (u8 *)((u32)ADPCMsrc + 2), &data[size], len );
							size += len;
							count--;
						}
if (pre_size != (size*2))
	FatalError( "bad decoded size!", __FILE__, __LINE__ );

						data[size] = 0;
						sample->Data = (u32)data;
						break;

					case TYPE_ADPCM:
						FileSeek( fh, sample->Data, SEEK_SET );

						size = sample->Chunks * 192;
						data = MemMalloc( size, MEM_ANY );

						FileRead( data, size, 1, fh );
						sample->Data = (u32)data;
						break;

					default:
						sample->Data = 0;
				}
			}
			else if (sample->ROMtype == TYPE_GSM)
			{
				sample->Data += info->romOffset;
			}
			else
				sample->Data = 0;
		}
		else
		{
			sample->Data = 0;
			sample->RAMtype = TYPE_NONE;
		}

		sample++;
	}

	FileClose( fh );

	return( bank );
}


void *Audio_LoadMusic( char *musicname, char *bankname, const u32 index )
{
	FH
		fh;
	SMusicHeader
		*header;
	u32
		*used,
		offset;
	void
		*mem;
	s32
		size;

	fh = FileOpen( musicname, "rb" );
	FileSeek( fh, 0, SEEK_END );
	size = FileTell( fh );
	mem = MemMalloc( size, MEM_ANY );
	FileSeek( fh, 0, SEEK_SET );
	FileRead( mem, size, 1, fh );
	FileClose( fh );


	header = (SMusicHeader *)mem;
	offset = header->SongsOffset;
	used = (u32 *)((u32)mem + offset);
	offset = used[index];
	used = (u32 *)((u32)mem + offset);

	MusicBank = LoadMusicBank( bankname, used );


	return( mem );
}


void Audio_FreeMusic( void *music )
{
	if (music)
	{
		Audio_FreeBank( MusicBank );
		MemFree( music );
	}
}


s32 Audio_MusicActive()
{
	s32
		i;

	if (MusicPtr)
		return( TRUE );

	for (i=0; i<MAX_TRACKS; i++)
	{
		if (Audio_SfxActive( Tracks[i].Id ))
			return( TRUE );
	}

	return( FALSE );
}


s32 Audio_SfxPlay( void *bank, const u32 index, const u8 volume )
{
	SSfxPlay
		*play;
	SSfxVolume
		*vol;

	ExtUnique++;


	if (CmdCount <= (LIST_SIZE-2))
	{
		CmdPtr->Command = CMD_SFX_PLAY;
		CmdPtr->Id = ExtUnique;

		play = &CmdPtr->Data.SfxPlay;
		play->Bank = bank;
		play->Index = index;

		CmdPtr++;


		CmdPtr->Command = CMD_SFX_VOLUME;
		CmdPtr->Id = ExtUnique;

		vol = &CmdPtr->Data.SfxVolume;
		vol->Volume = volume;

		CmdPtr++;
	}

	CmdCount += 2;


	return( ExtUnique );
}


void Audio_SfxStop( const s32 id )
{
	if (CmdCount < LIST_SIZE)
	{
		CmdPtr->Command = CMD_SFX_STOP;
		CmdPtr->Id = id;

		CmdPtr++;
	}

	CmdCount++;
}


void Audio_SfxVolume( const s32 id, const u8 volume )
{
	SSfxVolume
		*data;

	if (CmdCount < LIST_SIZE)
	{
		CmdPtr->Command = CMD_SFX_VOLUME;
		CmdPtr->Id = id;

		data = &CmdPtr->Data.SfxVolume;
		data->Volume = volume;

		CmdPtr++;
	}

	CmdCount++;
}


void Audio_SfxBalance( const s32 id, const u8 left, const u8 right )
{
	SSfxBalance
		*data;

	if (CmdCount < LIST_SIZE)
	{
		CmdPtr->Command = CMD_SFX_BALANCE;
		CmdPtr->Id = id;

		data = &CmdPtr->Data.SfxBalance;
		data->Left = left;
		data->Right = right;

		CmdPtr++;
	}

	CmdCount++;
}


void Audio_SfxRepeat( const s32 id, const s32 repeat )
{
	SSfxRepeat
		*data;

	if (CmdCount < LIST_SIZE)
	{
		CmdPtr->Command = CMD_SFX_REPEAT;
		CmdPtr->Id = id;

		data = &CmdPtr->Data.SfxRepeat;
		data->Repeat = repeat;

		CmdPtr++;
	}

	CmdCount++;
}


void Audio_SfxPause( const s32 id, const s32 pause )
{
	SSfxPause
		*data;

	if (CmdCount < LIST_SIZE)
	{
		CmdPtr->Command = CMD_SFX_PAUSE;
		CmdPtr->Id = id;

		data = &CmdPtr->Data.SfxPause;
		data->Pause = pause;

		CmdPtr++;
	}

	CmdCount++;
}


void Audio_MusicPlay( void *music, const u32 index )
{
	SMusicPlay
		*data;

	if (CmdCount < LIST_SIZE)
	{
		CmdPtr->Command = CMD_MUSIC_PLAY;

		data = &CmdPtr->Data.MusicPlay;
		data->Song = music;
		data->Index = index;

		CmdPtr++;
	}

	CmdCount++;
}


void Audio_MusicVolume( const u8 volume )
{
	SChannel
		*channel;
	s32
		i,
		c,
		scale;

	if (MusicVolume)
		scale = (volume << 7) / MusicVolume;
	else
		scale = volume;

	for (i=0; i<MAX_TRACKS; i++)
	{
		channel = Channels;

		for (c=0; c<MAX_CHANNELS; c++)
		{
			if (channel->Id == Tracks[i].Id)
			{
				channel->FullVol = (channel->FullVol * scale) >> 7;
				channel->Volume = (channel->Volume * scale) >> 7;
			}

			channel++;
		}
	}

	MusicVolume = volume;
}


void Audio_MusicStop()
{
	if (CmdCount < LIST_SIZE)
	{
		CmdPtr->Command = CMD_MUSIC_STOP;

		CmdPtr++;
	}

	CmdCount++;
}


void Audio_MusicPause( const s32 pause )
{
	SMusicPause
		*data;

	if (CmdCount < LIST_SIZE)
	{
		CmdPtr->Command = CMD_MUSIC_PAUSE;

		data = &CmdPtr->Data.MusicPause;
		data->Pause = pause;

		CmdPtr++;
	}

	CmdCount++;
}


void Audio_StopAll()
{
	if (CmdCount < LIST_SIZE)
	{
		CmdPtr->Command = CMD_STOP_ALL;

		CmdPtr++;
	}

	CmdCount++;
}


// Immediate functions...

s32 Audio_SfxActive( const u32 id )
{
	s32
		i;

	for (i=0; i<MAX_CHANNELS; i++)
	{
		if (Channels[i].Id == id)
		{
			if (Channels[i].State == STATE_OFF)
				return( FALSE );
			else
				return( TRUE );
		}
	}

	return( FALSE );
}


void Audio_Process()
{
	s32
		i;

if (CmdCount > AudioMaxCmds)
	AudioMaxCmds = CmdCount;


	CmdLists[WriteList].Ready = TRUE;			// Signal to the audio thread that this list is ready.

	WriteList = 1 - WriteList;			// Change to the other list.

	while (CmdLists[WriteList].Ready);			// Wait for it to be clear.

	for (i=0; i<LIST_SIZE; i++)
		CmdLists[WriteList].Commands[i].Command = CMD_NONE;

	CmdPtr = &CmdLists[WriteList].Commands[0];
	CmdCount = 0;
}


void Audio_Init()
{
	s32
		i,
		j;

	GSM2_Init();


	for (j=0; j<2; j++)
	{
		CmdLists[j].Ready = FALSE;

		for (i=0; i<LIST_SIZE; i++)
			CmdLists[j].Commands[i].Command = CMD_NONE;
	}

	CmdPtr = &CmdLists[WriteList].Commands[0];
	CmdCount = 0;


	for (i=0; i<MAX_CHANNELS; i++)
	{
		Channels[i].State = STATE_OFF;
	}


	for (i=0; i<MAX_TRACKS; i++)
	{
		Tracks[i].Id = 0;
		Tracks[i].Group = -1;
		Tracks[i].Channel = -1;
	}

	MusicPtr = NULL;
	MusicTime = 0;


Stack_Setup( AudioThreadStack, STACK_SIZE );
	osCreateThread( &AudioThread, THREAD_AUDIO, AudioProc, NULL, AudioThreadStack+(STACK_SIZE/8), THREAD_AUDIO );
	osStartThread( &AudioThread );
}


void Audio_Deinit()
{
	Halted = TRUE;

	while (!Done);

	osDestroyThread( &AudioThread );

	MemFree( MixBuffer );
	MemFree( AudioBuffers[1] );
	MemFree( AudioBuffers[0] );
}
