/**********************************************************************************************
 *
 *   streaming ADPCM driver
 *   by Aaron Giles
 *
 *   Library to transcode from an ADPCM source to raw PCM.
 *   Written by Buffoni Mirko in 08/06/97
 *   References: various sources and documents.
 *
 *   HJB 08/31/98
 *   modified to use an automatically selected oversampling factor
 *   for the current sample rate
 *
 *   Mish 21/7/99
 *   Updated to allow multiple OKI chips with different sample rates
 *
 *   R. Belmont 31/10/2003
 *   Updated to allow a driver to use both MSM6295s and "raw" ADPCM voices (gcpinbal)
 *   Also added some error trapping for MAME_DEBUG builds
 *
 **********************************************************************************************/


#include "mamedef.h"
//#include "emu.h"
//#include "streams.h"
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include "okim6295.h"

#define FALSE	0
#define TRUE	1

//#define MAX_SAMPLE_CHUNK	10000
#define MAX_SAMPLE_CHUNK	0x10	// that's enough for VGMPlay's update rate


/* struct describing a single playing ADPCM voice */
struct ADPCMVoice
{
	UINT8 playing;			/* 1 if we are actively playing */

	UINT32 base_offset;		/* pointer to the base memory location */
	UINT32 sample;			/* current sample number */
	UINT32 count;			/* total samples to play */

	struct adpcm_state adpcm;/* current ADPCM state */
	UINT32 volume;			/* output volume */
	UINT8 Muted;
};

typedef struct _okim6295_state okim6295_state;
struct _okim6295_state
{
	#define OKIM6295_VOICES		4
	struct ADPCMVoice voice[OKIM6295_VOICES];
	//running_device *device;
	INT32 command;
	UINT8 bank_installed;
	INT32 bank_offs;
	UINT8 pin7_state;
	//sound_stream *stream;	/* which stream are we playing on? */
	UINT32 master_clock;	/* master clock frequency */
	
	UINT32				ROMSize;
	UINT8*				ROM;
};

/* step size index shift table */
static const int index_shift[8] = { -1, -1, -1, -1, 2, 4, 6, 8 };

/* lookup table for the precomputed difference */
static int diff_lookup[49*16];

/* volume lookup table. The manual lists only 9 steps, ~3dB per step. Given the dB values,
   that seems to map to a 5-bit volume control. Any volume parameter beyond the 9th index
   results in silent playback. */
static const int volume_table[16] =
{
	0x20,	//   0 dB
	0x16,	//  -3.2 dB
	0x10,	//  -6.0 dB
	0x0b,	//  -9.2 dB
	0x08,	// -12.0 dB
	0x06,	// -14.5 dB
	0x04,	// -18.0 dB
	0x03,	// -20.5 dB
	0x02,	// -24.0 dB
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
};

/* tables computed? */
static int tables_computed = 0;

/* useful interfaces */
//const okim6295_interface okim6295_interface_pin7high = { 1 };
//const okim6295_interface okim6295_interface_pin7low = { 0 };

/* default address map */
/*static ADDRESS_MAP_START( okim6295, 0, 8 )
	AM_RANGE(0x00000, 0x3ffff) AM_ROM
ADDRESS_MAP_END*/


/*INLINE okim6295_state *get_safe_token(running_device *device)
{
	assert(device != NULL);
	assert(device->token != NULL);
	assert(device->type == SOUND);
	assert(sound_get_type(device) == SOUND_OKIM6295);
	return (okim6295_state *)device->token;
}*/


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

     compute_tables -- compute the difference tables

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

static void compute_tables(void)
{
	/* nibble to bit map */
	static const int nbl2bit[16][4] =
	{
		{ 1, 0, 0, 0}, { 1, 0, 0, 1}, { 1, 0, 1, 0}, { 1, 0, 1, 1},
		{ 1, 1, 0, 0}, { 1, 1, 0, 1}, { 1, 1, 1, 0}, { 1, 1, 1, 1},
		{-1, 0, 0, 0}, {-1, 0, 0, 1}, {-1, 0, 1, 0}, {-1, 0, 1, 1},
		{-1, 1, 0, 0}, {-1, 1, 0, 1}, {-1, 1, 1, 0}, {-1, 1, 1, 1}
	};

	int step, nib;

	/* loop over all possible steps */
	for (step = 0; step <= 48; step++)
	{
		/* compute the step value */
		int stepval = (int)floor(16.0 * pow(11.0 / 10.0, (double)step));

		/* loop over all nibbles and compute the difference */
		for (nib = 0; nib < 16; nib++)
		{
			diff_lookup[step*16 + nib] = nbl2bit[nib][0] *
				(stepval   * nbl2bit[nib][1] +
				 stepval/2 * nbl2bit[nib][2] +
				 stepval/4 * nbl2bit[nib][3] +
				 stepval/8);
		}
	}

	tables_computed = 1;
}



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

     reset_adpcm -- reset the ADPCM stream

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

void reset_adpcm(struct adpcm_state *state)
{
	/* make sure we have our tables */
	if (!tables_computed)
		compute_tables();

	/* reset the signal/step */
	state->signal = -2;
	state->step = 0;
}



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

     clock_adpcm -- clock the next ADPCM byte

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

INT16 clock_adpcm(struct adpcm_state *state, UINT8 nibble)
{
	state->signal += diff_lookup[state->step * 16 + (nibble & 15)];

	/* clamp to the maximum */
	if (state->signal > 2047)
		state->signal = 2047;
	else if (state->signal < -2048)
		state->signal = -2048;

	/* adjust the step size and clamp */
	state->step += index_shift[nibble & 7];
	if (state->step > 48)
		state->step = 48;
	else if (state->step < 0)
		state->step = 0;

	/* return the signal */
	return state->signal;
}



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

     generate_adpcm -- general ADPCM decoding routine

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

static UINT8 memory_raw_read_byte(okim6295_state *info, offs_t offset)
{
	offs_t CurOfs;
	
	CurOfs = info->bank_offs | offset;
	if (CurOfs < info->ROMSize)
		return info->ROM[CurOfs];
	else
		return 0x00;
}

static void generate_adpcm(okim6295_state *chip, struct ADPCMVoice *voice, INT16 *buffer, int samples)
{
	/* if this voice is active */
	if (voice->playing)
	{
		offs_t base = voice->base_offset;
		int sample = voice->sample;
		int count = voice->count;

		/* loop while we still have samples to generate */
		while (samples)
		{
			/* compute the new amplitude and update the current step */
			//int nibble = memory_raw_read_byte(chip->device->space(), base + sample / 2) >> (((sample & 1) << 2) ^ 4);
			UINT8 nibble = memory_raw_read_byte(chip, base + sample / 2) >> (((sample & 1) << 2) ^ 4);

			/* output to the buffer, scaling by the volume */
			/* signal in range -2048..2047, volume in range 2..32 => signal * volume / 2 in range -32768..32767 */
			*buffer++ = clock_adpcm(&voice->adpcm, nibble) * voice->volume / 2;
			samples--;

			/* next! */
			if (++sample >= count)
			{
				voice->playing = 0;
				break;
			}
		}

		/* update the parameters */
		voice->sample = sample;
	}

	/* fill the rest with silence */
	while (samples--)
		*buffer++ = 0;
}



/**********************************************************************************************
 *
 *  OKIM 6295 ADPCM chip:
 *
 *  Command bytes are sent:
 *
 *      1xxx xxxx = start of 2-byte command sequence, xxxxxxx is the sample number to trigger
 *      abcd vvvv = second half of command; one of the abcd bits is set to indicate which voice
 *                  the v bits seem to be volumed
 *
 *      0abc d000 = stop playing; one or more of the abcd bits is set to indicate which voice(s)
 *
 *  Status is read:
 *
 *      ???? abcd = one bit per voice, set to 0 if nothing is playing, or 1 if it is active
 *
***********************************************************************************************/


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

     okim6295_update -- update the sound chip so that it is in sync with CPU execution

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

//static STREAM_UPDATE( okim6295_update )
void okim6295_update(void *_chip, stream_sample_t **outputs, int samples)
{
	okim6295_state *chip = (okim6295_state *)_chip;
	int i;

	memset(outputs[0], 0, samples * sizeof(*outputs[0]));

	for (i = 0; i < OKIM6295_VOICES; i++)
	{
		struct ADPCMVoice *voice = &chip->voice[i];
		if (! voice->Muted)
		{
			stream_sample_t *buffer = outputs[0];
			INT16 sample_data[MAX_SAMPLE_CHUNK];
			int remaining = samples;

			/* loop while we have samples remaining */
			while (remaining)
			{
				int samples = (remaining > MAX_SAMPLE_CHUNK) ? MAX_SAMPLE_CHUNK : remaining;
				int samp;

				generate_adpcm(chip, voice, sample_data, samples);
				for (samp = 0; samp < samples; samp++)
					*buffer++ += sample_data[samp];

				remaining -= samples;
			}
		}
	}
	
	memcpy(outputs[1], outputs[0], samples * sizeof(*outputs[0]));
}



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

     state save support for MAME

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

/*static void adpcm_state_save_register(struct ADPCMVoice *voice, running_device *device, int index)
{
	state_save_register_device_item(device, index, voice->playing);
	state_save_register_device_item(device, index, voice->sample);
	state_save_register_device_item(device, index, voice->count);
	state_save_register_device_item(device, index, voice->adpcm.signal);
	state_save_register_device_item(device, index, voice->adpcm.step);
	state_save_register_device_item(device, index, voice->volume);
	state_save_register_device_item(device, index, voice->base_offset);
}

static STATE_POSTLOAD( okim6295_postload )
{
	running_device *device = (running_device *)param;
	okim6295_state *info = get_safe_token(device);
	okim6295_set_bank_base(device, info->bank_offs);
}

static void okim6295_state_save_register(okim6295_state *info, running_device *device)
{
	int j;

	state_save_register_device_item(device, 0, info->command);
	state_save_register_device_item(device, 0, info->bank_offs);
	for (j = 0; j < OKIM6295_VOICES; j++)
		adpcm_state_save_register(&info->voice[j], device, j);

	state_save_register_postload(device->machine, okim6295_postload, (void *)device);
}*/



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

     DEVICE_START( okim6295 ) -- start emulation of an OKIM6295-compatible chip

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

//static DEVICE_START( okim6295 )
void * device_start_okim6295(int clock)
{
	//const okim6295_interface *intf = (const okim6295_interface *)device->baseconfig().static_config;
	//okim6295_state *info = get_safe_token(device);
	okim6295_state *info;
	//int divisor = intf->pin7 ? 132 : 165;
	int divisor;
	//int voice;

	info = (okim6295_state *) calloc(1, sizeof(okim6295_state));
	
	compute_tables();

	info->command = -1;
	info->bank_installed = FALSE;
	info->bank_offs = 0;
	//info->device = device;

	//info->master_clock = device->clock;
	info->master_clock = clock & 0x7FFFFFFF;
	info->pin7_state = (clock & 0x80000000) >> 31;

	/* generate the name and create the stream */
	divisor = info->pin7_state ? 132 : 165;
	//info->stream = stream_create(device, 0, 1, device->clock/divisor, info, okim6295_update);

	// moved to device_reset
	/*// initialize the voices //
	for (voice = 0; voice < OKIM6295_VOICES; voice++)
	{
		// initialize the rest of the structure //
		info->voice[voice].volume = 0;
		reset_adpcm(&info->voice[voice].adpcm);
	}*/

	//okim6295_state_save_register(info, device);
	
	return info; //return info->master_clock / divisor;
}



void device_stop_okim6295(void *_chip)
{
	okim6295_state* chip = (okim6295_state *) _chip;
	
	free(chip->ROM);	chip->ROM = NULL;
	chip->ROMSize = 0x00;
	
	free(chip);
}

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

     DEVICE_RESET( okim6295 ) -- stop emulation of an OKIM6295-compatible chip

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

//static DEVICE_RESET( okim6295 )
void device_reset_okim6295(void *chip)
{
	okim6295_state *info = (okim6295_state *) chip;
	int voice;

	for (voice = 0; voice < OKIM6295_VOICES; voice++)
	{
		info->voice[voice].volume = 0;
		reset_adpcm(&info->voice[voice].adpcm);
		
		info->voice[voice].playing = 0;
	}
}



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

     okim6295_set_bank_base -- set the base of the bank for a given voice on a given chip

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

//void okim6295_set_bank_base(running_device *device, int base)
void okim6295_set_bank_base(okim6295_state *info, int base)
{
	//okim6295_state *info = get_safe_token(device);
	//stream_update(info->stream);

	/* if we are setting a non-zero base, and we have no bank, allocate one */
	if (!info->bank_installed && base != 0)
	{
		/* override our memory map with a bank */
		//memory_install_read_bank(device->space(), 0x00000, 0x3ffff, 0, 0, device->tag());
		info->bank_installed = TRUE;
	}

	/* if we have a bank number, set the base pointer */
	if (info->bank_installed)
	{
		info->bank_offs = base;
		//memory_set_bankptr(device->machine, device->tag(), device->region->base.u8 + base);
	}
}



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

     okim6295_set_pin7 -- adjust pin 7, which controls the internal clock division

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

static void okim6295_clock_changed(okim6295_state *info)
{
	int divisor;
	divisor = info->pin7_state ? 132 : 165;
	//stream_set_sample_rate(info->stream, info->master_clock/divisor);
}

//void okim6295_set_pin7(running_device *device, int pin7)
static void okim6295_set_pin7(okim6295_state *info, int pin7)
{
	//okim6295_state *info = get_safe_token(device);
	//int divisor = pin7 ? 132 : 165;

	info->pin7_state = pin7;
	//stream_set_sample_rate(info->stream, info->master_clock/divisor);
	okim6295_clock_changed(info);
}


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

     okim6295_status_r -- read the status port of an OKIM6295-compatible chip

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

//READ8_DEVICE_HANDLER( okim6295_r )
UINT8 okim6295_r(void *chip, offs_t offset)
{
	//okim6295_state *info = get_safe_token(device);
	okim6295_state *info = (okim6295_state *) chip;
	int i, result;

	result = 0xf0;	/* naname expects bits 4-7 to be 1 */

	/* set the bit to 1 if something is playing on a given channel */
	//stream_update(info->stream);
	for (i = 0; i < OKIM6295_VOICES; i++)
	{
		struct ADPCMVoice *voice = &info->voice[i];

		/* set the bit if it's playing */
		if (voice->playing)
			result |= 1 << i;
	}

	return result;
}



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

     okim6295_data_w -- write to the data port of an OKIM6295-compatible chip

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

//WRITE8_DEVICE_HANDLER( okim6295_w )
void okim6295_write_command(okim6295_state *info, UINT8 data)
{
	//okim6295_state *info = get_safe_token(device);

	/* if a command is pending, process the second half */
	if (info->command != -1)
	{
		int temp = data >> 4, i, start, stop;
		offs_t base;

		/* the manual explicitly says that it's not possible to start multiple voices at the same time */
		if (temp != 0 && temp != 1 && temp != 2 && temp != 4 && temp != 8)
			printf("OKI6295 start %x contact MAMEDEV\n", temp);

		/* update the stream */
		//stream_update(info->stream);

		/* determine which voice(s) (voice is set by a 1 bit in the upper 4 bits of the second byte) */
		for (i = 0; i < OKIM6295_VOICES; i++, temp >>= 1)
		{
			if (temp & 1)
			{
				struct ADPCMVoice *voice = &info->voice[i];

				/* determine the start/stop positions */
				base = info->command * 8;

				//start  = memory_raw_read_byte(device->space(), base + 0) << 16;
				start  = memory_raw_read_byte(info, base + 0) << 16;
				start |= memory_raw_read_byte(info, base + 1) << 8;
				start |= memory_raw_read_byte(info, base + 2) << 0;
				start &= 0x3ffff;

				stop  = memory_raw_read_byte(info, base + 3) << 16;
				stop |= memory_raw_read_byte(info, base + 4) << 8;
				stop |= memory_raw_read_byte(info, base + 5) << 0;
				stop &= 0x3ffff;

				/* set up the voice to play this sample */
				if (start < stop)
				{
					if (!voice->playing) /* fixes Got-cha and Steel Force */
					{
						voice->playing = 1;
						voice->base_offset = start;
						voice->sample = 0;
						voice->count = 2 * (stop - start + 1);

						/* also reset the ADPCM parameters */
						reset_adpcm(&voice->adpcm);
						voice->volume = volume_table[data & 0x0f];
					}
					else
					{
						//logerror("OKIM6295:'%s' requested to play sample %02x on non-stopped voice\n",device->tag(),info->command);
						// just displays warnings when seeking
						//logerror("OKIM6295: Voice %u requested to play sample %02x on non-stopped voice\n",i,info->command);
					}
				}
				/* invalid samples go here */
				else
				{
					//logerror("OKIM6295:'%s' requested to play invalid sample %02x\n",device->tag(),info->command);
					/*logerror("OKIM6295: Voice %u  requested to play invalid sample %02x\n",i,info->command);*/
					voice->playing = 0;
				}
			}
		}

		/* reset the command */
		info->command = -1;
	}

	/* if this is the start of a command, remember the sample number for next time */
	else if (data & 0x80)
	{
		info->command = data & 0x7f;
	}

	/* otherwise, see if this is a silence command */
	else
	{
		int temp = data >> 3, i;

		/* update the stream, then turn it off */
		//stream_update(info->stream);

		/* determine which voice(s) (voice is set by a 1 bit in bits 3-6 of the command */
		for (i = 0; i < OKIM6295_VOICES; i++, temp >>= 1)
		{
			if (temp & 1)
			{
				struct ADPCMVoice *voice = &info->voice[i];

				voice->playing = 0;
			}
		}
	}
}

void okim6295_w(void *_chip, offs_t offset, UINT8 data)
{
	okim6295_state* chip = (okim6295_state *) _chip;
	
	switch(offset)
	{
	case 0x00:
		okim6295_write_command(chip, data);
		break;
	case 0x08:
		chip->master_clock &= ~0x000000FF;
		chip->master_clock |= data <<  0;
		break;
	case 0x09:
		chip->master_clock &= ~0x0000FF00;
		chip->master_clock |= data <<  8;
		break;
	case 0x0A:
		chip->master_clock &= ~0x00FF0000;
		chip->master_clock |= data << 16;
		break;
	case 0x0B:
		chip->master_clock &= ~0xFF000000;
		chip->master_clock |= data << 24;
		okim6295_clock_changed(chip);
		break;
	case 0x0C:
		okim6295_set_pin7(chip, data);
		break;
	case 0x0F:
		okim6295_set_bank_base(chip, data << 18);
		break;
	}
	
	return;
}

void okim6295_write_rom(void *_chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength,
						const UINT8* ROMData)
{
	okim6295_state *chip = (okim6295_state *) _chip;
	
	if (chip->ROMSize != ROMSize)
	{
		chip->ROM = (UINT8*)realloc(chip->ROM, ROMSize);
		chip->ROMSize = ROMSize;
		memset(chip->ROM, 0xFF, ROMSize);
	}
	if (DataStart > ROMSize)
		return;
	if (DataStart + DataLength > ROMSize)
		DataLength = ROMSize - DataStart;
	
	memcpy(chip->ROM + DataStart, ROMData, DataLength);
}


void okim6295_set_mute_mask(void *_chip, UINT32 MuteMask)
{
	okim6295_state *chip = (okim6295_state *) _chip;
	UINT8 CurChn;
	
	for (CurChn = 0; CurChn < OKIM6295_VOICES; CurChn ++)
		chip->voice[CurChn].Muted = (MuteMask >> CurChn) & 0x01;
}
