/***************************************************************************

    Capcom CPS-3 Sound Hardware

***************************************************************************/
#include "driver.h"
#include "streams.h"
#include "includes/cps3.h"

#define CPS3_VOICES		16

static sound_stream *cps3_stream;

typedef struct _cps3_voice_
{
	UINT32 regs[8];
	UINT32 pos;
	UINT16 frac;
} cps3_voice;

struct
{
	cps3_voice voice[CPS3_VOICES];
	UINT16     key;
} chip;

static void cps3_stream_update(void *param, stream_sample_t **inputs, stream_sample_t **buffer, int length)
{
	int i;
	INT8 *base = (INT8*)memory_region(REGION_USER5);

	/* Clear the buffers */
	memset(buffer[0], 0, length*sizeof(*buffer[0]));
	memset(buffer[1], 0, length*sizeof(*buffer[1]));

	for (i = 0; i < CPS3_VOICES; i ++)
	{
		if (chip.key & (1 << i))
		{
			int j;

			/* TODO */
			#define SWAP(a) ((a >> 16) | ((a & 0xffff) << 16))

			cps3_voice *vptr = &chip.voice[i];

			UINT32 start = vptr->regs[1];
			UINT32 end   = vptr->regs[5];
			UINT32 loop  = (vptr->regs[3] & 0xffff) + ((vptr->regs[4] & 0xffff) << 16);
			UINT32 step  = (vptr->regs[3] >> 16);

			INT16 vol_l = (vptr->regs[7] & 0xffff);
			INT16 vol_r = ((vptr->regs[7] >> 16) & 0xffff);

			UINT32 pos = vptr->pos;
			UINT16 frac = vptr->frac;

			/* TODO */
			start = SWAP(start) - 0x400000;
			end = SWAP(end) - 0x400000;
			loop -= 0x400000;

			/* Go through the buffer and add voice contributions */
			for (j = 0; j < length; j ++)
			{
				INT32 sample;

				pos += (frac >> 12);
				frac &= 0xfff;


				if (start + pos >= end)
				{
					if (vptr->regs[2])
					{
						pos = loop - start;
					}
					else
					{
						chip.key &= ~(1 << i);
						break;
					}
				}

				sample = base[BYTE4_XOR_LE(start + pos)];
				frac += step;

				buffer[0][j] += (sample * (vol_l >> 8));
				buffer[1][j] += (sample * (vol_r >> 8));
			}

			vptr->pos = pos;
			vptr->frac = frac;
		}
	}

}

void *cps3_sh_start(int clock, const struct CustomSound_interface *config)
{
	/* Allocate the stream */
	cps3_stream = stream_create(0, 2, clock / 384, NULL, cps3_stream_update);

	memset(&chip, 0, sizeof(chip));

	return auto_malloc(1);
}

WRITE32_HANDLER( cps3_sound_w )
{
	stream_update(cps3_stream);

	if (offset < 0x80)
	{
		COMBINE_DATA(&chip.voice[offset / 8].regs[offset & 7]);
	}
	else if (offset == 0x80)
	{
		int i;
		UINT16 key = data >> 16;

		for (i = 0; i < CPS3_VOICES; i++)
		{
			// Key off -> Key on
			if ((key & (1 << i)) && !(chip.key & (1 << i)))
			{
				chip.voice[i].frac = 0;
				chip.voice[i].pos = 0;
			}
		}
		chip.key = key;
	}
	else
	{
		printf("Sound [%x] %x\n", offset, data);
	}
}

READ32_HANDLER( cps3_sound_r )
{
	stream_update(cps3_stream);

	if (offset < 0x80)
	{
		return chip.voice[offset / 8].regs[offset & 7] & ~mem_mask;
	}
	else if (offset == 0x80)
	{
		return chip.key << 16;
	}
	else
	{
		printf("Unk sound read : %x\n", offset);
		return 0;
	}
}
