/****************************************************************************
 * FCE Ultra
 * Nintendo Wii/Gamecube Port
 *
 * Tantric 2008-2009
 * eke-eke October 2008
 *
 * gcaudio.cpp
 *
 * Audio driver
 ****************************************************************************/

#include <gccore.h>
#include <string.h>
#include <asndlib.h>

#define SAMPLERATE 48000

static u8 ConfigRequested = 0;
static u8 soundbuffer[2][3840] ATTRIBUTE_ALIGN(32);
static u8 mixbuffer[16000];
static int mixhead = 0;
static int mixtail = 0;
static int whichab = 0;
static int IsPlaying = 0;

/****************************************************************************
 * MixerCollect
 *
 * Collects sound samples from mixbuffer and puts them into outbuffer
 * Makes sure to align them to 32 bytes for AUDIO_InitDMA
 ***************************************************************************/
static int MixerCollect( u8 *outbuffer, int len )
{
	u32 *dst = (u32 *)outbuffer;
	u32 *src = (u32 *)mixbuffer;
	int done = 0;

	// Always clear output buffer
	memset(outbuffer, 0, len);

	while ( ( mixtail != mixhead ) && ( done < len ) )
	{
		*dst++ = src[mixtail++];
		if (mixtail == 4000) mixtail = 0;
		done += 4;
	}

	// Realign to 32 bytes for DMA
	mixtail -= ((done&0x1f) >> 2);
	if (mixtail < 0)
		mixtail += 4000;
	done &= ~0x1f;
	if (!done)
		return len >> 1;

	return done;
}

/****************************************************************************
 * AudioSwitchBuffers
 *
 * Manages which buffer is played next
 ***************************************************************************/
static void AudioSwitchBuffers()
{
	if ( !ConfigRequested )
	{
		int len = MixerCollect( soundbuffer[whichab], 3840 );
		DCFlushRange(soundbuffer[whichab], len);
		AUDIO_InitDMA((u32)soundbuffer[whichab], len);
		AUDIO_StartDMA();
		whichab ^= 1;
		IsPlaying = 1;
	}
	else IsPlaying = 0;
}

/****************************************************************************
 * InitialiseAudio
 *
 * Initializes sound system on first load of emulator
 ***************************************************************************/
void InitialiseAudio()
{
	#ifdef NO_SOUND
	AUDIO_Init (NULL);
	AUDIO_RegisterDMACallback(AudioSwitchBuffers);
	#else
	ASND_Init();
	#endif
	memset(soundbuffer, 0, 3840*2);
	memset(mixbuffer, 0, 16000);
}

/****************************************************************************
 * ResetAudio
 *
 * Reset audio output when loading a new game
 ***************************************************************************/
void ResetAudio()
{
	memset(soundbuffer, 0, 3840*2);
	memset(mixbuffer, 0, 16000);
	mixhead = mixtail = 0;
}

/****************************************************************************
 * SwitchAudioMode
 *
 * Switches between menu sound and emulator sound
 ***************************************************************************/
void
SwitchAudioMode(int mode)
{
	if(mode == 0) // emulator
	{
		#ifndef NO_SOUND
		ASND_Pause(1);
		AUDIO_StopDMA();
		AUDIO_RegisterDMACallback(AudioSwitchBuffers);
		#endif
	}
	else // menu
	{
		IsPlaying = 0;
		#ifndef NO_SOUND
		ASND_Init();
		ASND_Pause(0);
		#else
		AUDIO_StopDMA();
		#endif
	}
}

/****************************************************************************
 * ShutdownAudio
 *
 * Shuts down audio subsystem. Useful to avoid unpleasant sounds if a
 * crash occurs during shutdown.
 ***************************************************************************/
void ShutdownAudio()
{
	AUDIO_StopDMA();
}

/****************************************************************************
 * PlaySound
 *
 * Puts incoming mono samples into mixbuffer
 * Splits mono samples into two channels (stereo)
 ****************************************************************************/
void PlaySound( int *Buffer, int count )
{
	int i;
	u16 sample;
	u32 *dst = (u32 *)mixbuffer;

	for( i = 0; i < count; i++ )
	{
		sample = Buffer[i] & 0xffff;
		dst[mixhead++] = sample | ( sample << 16);
		if (mixhead == 4000)
			mixhead = 0;
	}

	// Restart Sound Processing if stopped
	if (IsPlaying == 0)
	{
		AudioSwitchBuffers ();
	}
}
