text: /***************************************************************************
  
    Attack ufo sound emulation
    based on MOS 6560 emulator by
    PeT mess@utanet.at
  
  ***************************************************************************/
  #include <math.h>
  #include "driver.h"
  #include "streams.h"
  #include "sound/custom.h"
  #include "includes/attckufo.h"
  
  /*
   * assumed model:
   * each write to a ton/noise generated starts it new
   * each generator behaves like an timer
   * when it reaches 0, the next samplevalue is given out
   */
  
  /*
   * noise channel
   * based on a document by diku0748@diku.dk (Asger Alstrup Nielsen)
   *
   * 23 bit shift register
   * initial value (0x7ffff8)
   * after shift bit 0 is set to bit 22 xor bit 17
   * dac sample bit22 bit20 bit16 bit13 bit11 bit7 bit4 bit2(lsb)
   *
   * emulation:
   * allocate buffer for 5 sec sampledata (fastest played frequency)
   * and fill this buffer in init with the required sample
   * fast turning off channel, immediate change of frequency
   */
  
  #define NOISE_BUFFER_SIZE_SEC 5
  
  
  #define TONE1_ON (attckufo_regs[0xa]&0x80)
  #define TONE2_ON (attckufo_regs[0xb]&0x80)
  #define TONE3_ON (attckufo_regs[0xc]&0x80)
  #define NOISE_ON (attckufo_regs[0xd]&0x80)
  #define VOLUME (attckufo_regs[0xe]&0x0f)
  
  #define OUTPUT_RATE		(14318181/14/32)
  
  #define TONE_FREQUENCY_MIN  ((14318181/14)/256/128)
  #define TONE1_VALUE (8*(128-((attckufo_regs[0xa]+1)&0x7f)))
  #define TONE1_FREQUENCY ((14318181/14)/32/TONE1_VALUE)
  #define TONE2_VALUE (4*(128-((attckufo_regs[0xb]+1)&0x7f)))
  #define TONE2_FREQUENCY ((14318181/14)/32/TONE2_VALUE)
  #define TONE3_VALUE (2*(128-((attckufo_regs[0xc]+1)&0x7f)))
  #define TONE3_FREQUENCY ((14318181/14)/32/TONE3_VALUE)
  #define NOISE_VALUE (32*(128-((attckufo_regs[0xd]+1)&0x7f)))
  #define NOISE_FREQUENCY ((14318181/14)/NOISE_VALUE)
  #define NOISE_FREQUENCY_MAX ((14318181/14)/32/1)
  
  static int tone1pos = 0, tone2pos = 0, tone3pos = 0,
  	tonesize, tone1samples = 1, tone2samples = 1, tone3samples = 1,
  	noisesize,		  /* number of samples */
  	noisepos = 0,         /* pos of tone */
  	noisesamples = 1;	  /* count of samples to give out per tone */
  
  static sound_stream *channel;
  static INT16 *tone;
  
  static INT8 *noise;
  
  void attckufo_soundport_w (int offset, int data)
  {
    int old = attckufo_regs[offset];
  	stream_update(channel);
  	switch (offset)
  	{
  	case 0xa:
  		attckufo_regs[offset] = data;
  		if (!(old & 0x80) && TONE1_ON)
  		{
  			tone1pos = 0;
  			tone1samples = OUTPUT_RATE / TONE1_FREQUENCY;
  			if (tone1samples == 0)
  				tone1samples = 1;
  		}
  
  		break;
  	case 0xb:
  		attckufo_regs[offset] = data;
  		if (!(old & 0x80) && TONE2_ON)
  		{
  			tone2pos = 0;
  			tone2samples = OUTPUT_RATE / TONE2_FREQUENCY;
  			if (tone2samples == 0)
  				tone2samples = 1;
  		}
  
  		break;
  	case 0xc:
  		attckufo_regs[offset] = data;
  		if (!(old & 0x80) && TONE3_ON)
  		{
  			tone3pos = 0;
  			tone3samples = OUTPUT_RATE / TONE3_FREQUENCY;
  			if (tone2samples == 0)
  				tone2samples = 1;
  		}
  
  		break;
  	case 0xd:
  		attckufo_regs[offset] = data;
  		if (NOISE_ON)
  		{
  			noisesamples = (int) ((double) NOISE_FREQUENCY_MAX * OUTPUT_RATE
  								  * NOISE_BUFFER_SIZE_SEC / NOISE_FREQUENCY);
  
  			if ((double) noisepos / noisesamples >= 1.0)
  			{
  				noisepos = 0;
  			}
  		}
  		else
  		{
  			noisepos = 0;
  		}
  		break;
  	case 0xe:
  		attckufo_regs[offset] = (old & ~0xf) | (data & 0xf);
  
  		break;
  	}
  }
  
  /************************************/
  /* Sound handler update             */
  /************************************/
  static void attckufo_update (void *param,stream_sample_t **inputs, stream_sample_t **_buffer,int length)
  {
  	int i, v;
  	stream_sample_t *buffer = _buffer[0];
  
  	for (i = 0; i < length; i++)
  	{
  		v = 0;
  		if (TONE1_ON /*||(tone1pos!=0) */ )
  		{
  			v += tone[tone1pos * tonesize / tone1samples];
  			tone1pos++;
  
  			if (tone1pos >= tone1samples)
  			{
  				tone1pos = 0;
  				tone1samples = OUTPUT_RATE / TONE1_FREQUENCY;
  				if (tone1samples == 0)
  					tone1samples = 1;
  			}
  
  		}
  		if (TONE2_ON  )
  		{
  			v += tone[tone2pos * tonesize / tone2samples];
  			tone2pos++;
  			if (tone2pos >= tone2samples)
  			{
  				tone2pos = 0;
  				tone2samples = OUTPUT_RATE / TONE2_FREQUENCY;
  				if (tone2samples == 0)
  					tone2samples = 1;
  			}
  
  		}
  		if (TONE3_ON  )
  		{
  			v += tone[tone3pos * tonesize / tone3samples];
  			tone3pos++;
  
  			if (tone3pos >= tone3samples)
  			{
  				tone3pos = 0;
  				tone3samples = OUTPUT_RATE / TONE3_FREQUENCY;
  				if (tone3samples == 0)
  					tone3samples = 1;
  			}
  
  		}
  		if (NOISE_ON)
  		{
  			v += noise[(int) ((double) noisepos * noisesize / noisesamples)];
  			noisepos++;
  			if ((double) noisepos / noisesamples >= 1.0)
  			{
  				noisepos = 0;
  			}
  		}
  		v = (v * VOLUME) << 2;
  		if (v > 32767)
  			buffer[i] = 32767;
  		else if (v < -32767)
  			buffer[i] = -32767;
  		else
  			buffer[i] = v;
  
  
  
  	}
  }
  
  /************************************/
  /* Sound handler start          */
  /************************************/
  
  
  void *attckufo_custom_start(int clock, const struct CustomSound_interface *config)
  {
  	int i;
  
  	channel = stream_create(0, 1, OUTPUT_RATE, 0, attckufo_update);
  
  
  	/* buffer for fastest played sample for 5 second
       * so we have enough data for min 5 second */
  	noisesize = NOISE_FREQUENCY_MAX * NOISE_BUFFER_SIZE_SEC;
  	noise = (INT8*) auto_malloc (noisesize * sizeof (noise[0]));
  	{
  		int noiseshift = 0x7ffff8;
  		char data;
  
  		for (i = 0; i < noisesize; i++)
  		{
  			data = 0;
  			if (noiseshift & 0x400000)
  				data |= 0x80;
  			if (noiseshift & 0x100000)
  				data |= 0x40;
  			if (noiseshift & 0x010000)
  				data |= 0x20;
  			if (noiseshift & 0x002000)
  				data |= 0x10;
  			if (noiseshift & 0x000800)
  				data |= 0x08;
  			if (noiseshift & 0x000080)
  				data |= 0x04;
  			if (noiseshift & 0x000010)
  				data |= 0x02;
  			if (noiseshift & 0x000004)
  				data |= 0x01;
  			noise[i] = data;
  			if (((noiseshift & 0x400000) == 0) != ((noiseshift & 0x002000) == 0))
  				noiseshift = (noiseshift << 1) | 1;
  			else
  				noiseshift <<= 1;
  		}
  	}
  	tonesize = OUTPUT_RATE / TONE_FREQUENCY_MIN;
  
  	tone = (INT16*) auto_malloc (tonesize * sizeof (tone[0]));
  
  	for (i = 0; i < tonesize; i++)
  	{
  		tone[i] = (INT16)(sin (2 * M_PI * i / tonesize) * 127 + 0.5);
  	}
  	return (void *) ~0;
  }
  
  