/*
   Risc os Digital Signal Processor
   (C) 2017 A.V.Bartram

  THIS SOFTWARE IS PROVIDED  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  IN NO EVENT SHALL A.V.Bartram BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL
  ,SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO
  ),PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS
  ;OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "kernel.h"
#include "synth.h"

// I may need a veneer here that uses assembler and invokes the main
// C function via a BL call.
// This may be because shared sound needs me to update R3

// I think I have no stack. So code is all required to be inlined
// This is more optimal in any case as I am not confident on relying
// on compiler code optimisation inlining in Norcroft.
void buffer_fill(void* param, signed short* buffer_base, signed short* buffer_end,
                 DWORD flags, DWORD sample_freq, DWORD sample_period,
                 DWORD step,  DWORD volume,      void* fill_ptr)
{
  int clearBuffer = 0;
  int i,j;
  BUFFER_TYPE channelBuffer;
  RISC_SYNTH *synth_channel = (RISC_SYNTH*)param;
  int watermark = 0;
  int fxwatermark = 0;
  size_t lengthbuffer = (size_t)(buffer_end - buffer_base) / 2;

  channelBuffer.m_data = synth_channel->m_fillBuffer;

  // First, check if we need to clear the buffer or add to it
  if (flags & 1)
    clearBuffer =1;

  if (lengthbuffer > 44100)
  {
    if (!clearBuffer)
    {
      signed short *ptr = buffer_base;
      while (ptr <= (buffer_end-2))
      {
        ptr[0] = 0;
        ptr++;
        ptr[1] = 0;
        ptr++;
      }
    }
  }
  else
  {
  //  int debug = 0;
    signed short *ptr = buffer_base;
    channelBuffer.m_size = lengthbuffer;
    channelBuffer.m_dataLength = 0;


    for (j = 0; j < MAX_POLY; ++j)
    {
      BUFFER_TYPE *buffer = &channelBuffer; // May need to put this in param too
      BUFFER_TYPE *xorchannel = NULL;
      BUFFER_TYPE xorimpl;
      signed short *xornext =  ((RISC_SYNTH*)param)->m_lastChannel[(j&1)];
      SYNTH_CHANNEL *channel = ((RISC_SYNTH*)param)->m_channel+j;

	unsigned int length = 0;
	unsigned int i;
	int          signal = 0;
	DCO          dco;
	LPF          lpf;
	// int          osc_throttle = 0;

	int          res = 0;
	int          signali = 0;

	int          resUp = 0;
	int          amplifier = 0;
	int          wave = 0;
	int          tone = 0;
	int          pulse_width = 0;

	// Local variables may be faster than memory referenced
	lpf.pole1 = channel->m_lpf.pole1;
	lpf.pole2 = channel->m_lpf.pole2;
	lpf.cutoff = channel->m_lpf.cutoff;
	lpf.resonance = channel->m_lpf.resonance;
	dco.m_osc_lookup = channel->m_dco.m_osc_lookup;
	dco.m_sample_lookup_msw = channel->m_dco.m_sample_lookup_msw;
	dco.m_sample = channel->m_dco.m_sample;
	amplifier = channel->m_amp;
	wave = channel->m_wave;
	tone = channel->m_tone;
	pulse_width = channel->m_pulse_width;
	// Should only compute if cutoff changes as div is expensive
	resUp = 1 << ((256-lpf.cutoff)>>6);

    if ((channel->m_mode & CHANNEL_MODE_XOR) && (j>0))
    {
      xorchannel = &xorimpl;
      xorimpl.m_data =((RISC_SYNTH*)param)->m_lastChannel[~(j&1)];

      // Writes to j&1
    }

    if (channel->m_adsr.m_stage < channel->m_adsr.m_endState)
	for (i = 0; (i < buffer->m_size); ++i)
	{
		//osc_throttle = (++osc_throttle) & 3;
		// last signal is copied forward 4 times. Creates high harmonics via stepping.

		//if (osc_throttle == 0)
		{
			 unsigned int osc_control = dco.m_osc_lookup >> 14 ; // Currently fractions are in 1/16384ths..

            // All oscillators are by default slightly detuned to create
            // simple phase effect.
			if ((channel->m_mode & CHANNEL_MODE_SYNC) == 0)
			{
  	          dco.m_osc_lookup += (j << 8);
            }

			signal = 0; // signal change start
			if (wave == 0)
			{
				if (dco.m_sample.m_data != NULL)
				{
				    int playposition = (dco.m_sample_lookup_msw << 16) +
				                       (dco.m_osc_lookup >> 14);
	    	        dco.m_osc_lookup = dco.m_osc_lookup + synth_channel->sampleFreqTable[tone];
                    if (dco.m_osc_lookup > 1073741824 )
                    {
                      dco.m_osc_lookup -= 1073741824;
                      dco.m_sample_lookup_msw ++;
                    }
					if (playposition >= dco.m_sample.m_dataLength)
					{
						dco.m_osc_lookup = 0;
						// terminate. No looping at present
						channel->m_adsr.m_stage = channel->m_adsr.m_endState;
						break;
					}

		   		    signal = dco.m_sample.m_data[playposition];

				}
			}
			else
			{
				dco.m_osc_lookup = dco.m_osc_lookup + synth_channel->tonetable[tone];

				if (dco.m_osc_lookup >= OSC_SIZE)
					dco.m_osc_lookup -= OSC_SIZE; // Modulus wil be expensive unless it is base 2

				// pulse / square
				if (wave & 0x1f)
				{
					signal = osc_control < pulse_width ? SWITCH_TOP_AMP : SWITCH_BASE_AMP;
				}
				// saw wave
				if (wave & WAVEFORM_SAW)
				{
					if ((wave & 0xdf)&&signal!=0)
						signal &= osc_control - HALF_SAMPLE_RATE;
					else
						signal = osc_control - HALF_SAMPLE_RATE;
				}

				// triangle wave
				if (wave & WAVEFORM_TRIANGLE)
				{
					if ((wave & 0xbf)&&signal!=0)
					{
						int s = osc_control < HALF_SAMPLE_RATE ? osc_control : SAMPLE_RATE - osc_control;
						s -= QUARTER_SAMPLE_RATE;
						signal &= s;
					}
					else
					{
						signal = osc_control < HALF_SAMPLE_RATE ? osc_control : SAMPLE_RATE - osc_control;
						signal -= QUARTER_SAMPLE_RATE;
						signal = signal * 2; // triangle is quieter - normal rae- to +11k.
					}
				}
				// modulatable noise (logarithmic)
				if (wave & WAVEFORM_NOISE)
				{
					// allow LCG prng to repeat last value mix into the signal
					// 41860090 == run every time
					//   218267 == lowest frequency
					channel->m_noise_throttle += synth_channel->tonetable[tone];
					if (channel->m_noise_throttle >= synth_channel->tonetable[MAX_TONE-1])
					{
						channel->m_noise_throttle -= synth_channel->tonetable[MAX_TONE-1];
						// Trivial linear congruential generator.
						// Using the sample constants as Borland C for now
						channel->m_prng = (22695477 * channel->m_prng) + 1;
						// prng is a 2^32 number, so as usual Modulus is automatic.
					 }


					 if ((wave & 0x7f)&&signal!=0)
					 {
					   // I think the issue here is that it defaulted
					   // to a signed value and that this was 64 bit signed
					   // value - which would have overflowed.

				//		signal &= channel->m_prng < 2147483648 ? SWITCH_BASE_AMP : SWITCH_TOP_AMP ; // reduce amplitude to 25% if another wave is playing
    /*					else*/
                        unsigned int tmp = channel->m_prng >> 31;

                        if (tmp & 1)
                        {
                          signal &= SWITCH_BASE_AMP;
                        }
                        else
                        {
                          signal &= SWITCH_TOP_AMP;
                        }
                    }
                    else
					 {
				//		signal &= channel->m_prng < 2147483648 ? SWITCH_BASE_AMP : SWITCH_TOP_AMP ; // reduce amplitude to 25% if another wave is playing
    /*					else*/
                        unsigned int tmp = channel->m_prng >> 31;

                        if (tmp & 1)
                        {
                          signal = SWITCH_BASE_AMP;
                        }
                        else
                        {
                          signal = SWITCH_TOP_AMP;
                        }
                    }
					//	signal = channel->m_prng < 2147483648 ? SWITCH_BASE_AMP : SWITCH_TOP_AMP ; // reduce amplitude to 25% if another wave is playing



				  }

			 }

// tmp debug
         // }


			// Channel can be ring-modulated (via simple XOR) with adjacent channel i.e. 1 with channel 2, 3 with 4, 5 with 6 and 7 with 8
			if (xorchannel)
			{
				 signal = signal ^ xorchannel->m_data[i];
			}
			if (signal < 0)
			{
				signal = signal * -1;
				signal = (signal * amplifier) >> 8; // Amplifier is 0 to 256
				signal = signal * -1;
			}
			else
			{
				signal = (signal * amplifier) >> 8; // Amplifier is 0 to 256
			}
		}
		//

		// Envelopes - pitch, amp or filter
		channel->centisecond++;
		if (channel->centisecond == CENTISECOND)
		{
			channel->centisecond = 0;
			// I now have 2 envelopes that can modulate more than 1 source
			// The 3 stage env can loop and modulate any value including amplitude.

			if (channel->m_adr.m_stage < channel->m_adr.m_endState)
			{
				int cont = 1;

				// ADR supports a speed parameter
				if (channel->m_adr.m_counterSpeed > 1)
				{
					channel->m_adr.m_speedCounter++;
					if (channel->m_adr.m_speedCounter <= channel->m_adr.m_counterSpeed)
						cont = 0;
					else
						channel->m_adr.m_speedCounter = 0;
				}

				if (cont)
				{
					channel->m_adr.m_stageCounter++;
					if (channel->m_adr.m_stageCounter >= channel->m_adr.m_numADSR[channel->m_adr.m_stage])
					{
						channel->m_adr.m_stageCounter = 0;
						channel->m_adr.m_stage++;
						if (channel->m_adr.m_stage == channel->m_adr.m_endState)
						{
							if (channel->m_adr.m_flags & ASR_FLAG_LOOP)
							{
								channel->m_adr.m_stage = 0;
							}
							else
							{
								cont = 0;
							}
						}
					}
				}

				if (cont)
				{
					if (channel->m_adr.m_flags & ASR_FLAG_PITCH)
					{
						tone = (tone + channel->m_adr.m_stepADSR[channel->m_adr.m_stage]);
						if (tone >= 364)
						  tone -= 364;
                        if (tone < 0)
                          tone += 364;
					}

					if (channel->m_adr.m_flags & ASR_FLAG_AMP)
					{
						amplifier = (amplifier + channel->m_adr.m_stepADSR[channel->m_adr.m_stage]);
						if (amplifier >= 0xff)
						  amplifier -= 0xff;
                        if (amplifier < 0)
                          amplifier += 0xff;
					}

					if (channel->m_adr.m_flags & ASR_FLAG_FILTER)
					{
						lpf.cutoff = (lpf.cutoff + channel->m_adr.m_stepADSR[channel->m_adr.m_stage]);
						if (lpf.cutoff >= FILTER_LIMITER)
						  lpf.cutoff = FILTER_LIMITER;
                        if (lpf.cutoff < 0)
                          lpf.cutoff = 0;

						resUp = 1 << ((256-lpf.cutoff)>>6);
					}

					if (channel->m_adr.m_flags & ASR_FLAG_PWM)
					{
						pulse_width = (pulse_width + (channel->m_adr.m_stepADSR[channel->m_adr.m_stage] * 10));
						if (pulse_width >= PWM_LIMITER)
						  pulse_width -= PWM_LIMITER;
                        if (pulse_width < 0)
                          pulse_width += PWM_LIMITER;
					}
				}
			}

			// adsr
			if (channel->m_adsr.m_stage < channel->m_adsr.m_endState)
			{
				int cont = 1;
				channel->m_adsr.m_stageCounter++;
				if (channel->m_adsr.m_stageCounter >= channel->m_adsr.m_numADSR[channel->m_adsr.m_stage])
				{
					channel->m_adsr.m_stageCounter = 0;
					channel->m_adsr.m_stage++;
					if (channel->m_adsr.m_stage == channel->m_adsr.m_endState)
						cont = 0;
				}

				if (cont)
				{
					amplifier = (amplifier + channel->m_adsr.m_stepADSR[channel->m_adsr.m_stage]);
					if (amplifier > 0xff)
						amplifier = 0xff;
					if (amplifier < 0)
						amplifier = 0;


					if (channel->m_adsr.m_flags & ADSR_FLAG_PITCH)
					{
						tone = (tone + channel->m_adsr.m_stepADSR[channel->m_adsr.m_stage]);
						if (tone > 364)
							tone = 364;
						if (tone < 0)
							tone = 0;

					}

					if (channel->m_adsr.m_flags & ADSR_FLAG_FILTER)
					{
						lpf.cutoff = (lpf.cutoff + channel->m_adsr.m_stepADSR[channel->m_adsr.m_stage]);
						if (lpf.cutoff > FILTER_LIMITER)
							lpf.cutoff = FILTER_LIMITER;
						if (lpf.cutoff < 0)
							lpf.cutoff = 0;
						resUp = 1 << ((256-lpf.cutoff)>>6);
					}

					if (channel->m_adsr.m_flags & ADSR_FLAG_PWM)
					{
						pulse_width = (pulse_width + (channel->m_adsr.m_stepADSR[channel->m_adsr.m_stage] * 10));
						if (pulse_width > PWM_LIMITER)
							pulse_width = PWM_LIMITER;
						if (pulse_width < 0)
							pulse_width = 0;
					}
				}
			}
			else
			{
				length = i;
				break;
			}
		}

		// resup - resonance feedback needs to increase as filter reduces feedback when cutoff is higher.
		// 20 cutoff - *32   5   4     256 - 20 = 236
		// 50 cutoff - *32
		/// 100 cutoff *16   4   7                256
		//  150 cutoff *4
		// 200 cutoff 2     1                     56
		// 255 cutoff 1     0  8                   0
		// Low pass filter
		res = 12 - lpf.resonance;                                        // could move to 256th fractions for pole2 *resup i.e. more points on logscale
		signali = lpf.resonance == 0 ? (signal << 4) : (signal << 4) - ((lpf.pole2*resUp) >> res); // res is logarithmic
		// So as ARM v6 has no integer div - and we want the remainder of the pie for the input pole e.g. 7/8ths and 1/8th cutoff of signal
		// can restate so that the pole is 64 minus the cutoff  shift right 6 i.e. div 64 i.e. 63/64 and 1/64 input
		// This is basically equivalant as the pole is playing catchup - but we have a more linear scale
		// could increase this to 256 (for example)
		// In general div instructions are usually expensive to calculate on any CPU.
  // n.b. 2016 : I could increase the resolution here by 4 bits. Internally filter
  //             range would be 2^12 or 4096 values.
		lpf.pole1 = (lpf.pole1 * (256 - lpf.cutoff) >> 8) + ((signali * lpf.cutoff) >> 8);
		lpf.pole2 = (lpf.pole2 * (256 - lpf.cutoff) >> 8) + ((lpf.pole1 * lpf.cutoff) >> 8);

        {
          int rout = lpf.pole2 >> 4;

          if ((channel->m_mode & CHANNEL_MODE_MUTE) == 0)
          {
            if (watermark == 0)
  	     	      buffer->m_data[i] =  rout;
  	        else
                buffer->m_data[i] +=  rout;

            if (synth_channel->m_effectChannel[j])
            {
              if (fxwatermark == 0)
                synth_channel->m_effectBuffer[i] = rout;
              else
                synth_channel->m_effectBuffer[i] += rout;

            }
          }
          xornext[i] = rout;
        }
        length = i+1;

	 } // end loop to populate channel buffer


	channel->m_lpf.pole1 = lpf.pole1;
	channel->m_lpf.pole2 = lpf.pole2;
	channel->m_lpf.cutoff = lpf.cutoff;
	channel->m_lpf.resonance = lpf.resonance;
	channel->m_dco.m_osc_lookup = dco.m_osc_lookup;
	channel->m_dco.m_sample_lookup_msw = dco.m_sample_lookup_msw;
	channel->m_amp = amplifier;
	channel->m_tone = tone;
	channel->m_pulse_width = pulse_width;

    if ((channel->m_mode & CHANNEL_MODE_MUTE) == 0)
    {
      if (length>buffer->m_dataLength )
      {
        watermark = length;
        buffer->m_dataLength = length;
      }
    }

    if ((synth_channel->m_effectChannel[j]) && (length > fxwatermark))
      fxwatermark = length;

    } // end channel loop

    // Now process effect buffer with effects algorithms and mix
    // into main buffer

    {
      int i = 0;
      if (synth_channel->m_effects.m_mode != 0)
      {

        for (i = 0; i < channelBuffer.m_size; ++i)
        {
          // If no data generated then write silence
          // Assume buffers are in sync
          if (i >= fxwatermark)
            synth_channel->m_effectBuffer[i] = 0;
          if (i >= watermark)
            channelBuffer.m_data[i] = 0;
          if (synth_channel->m_effects.m_mode & EFFECT_MODE_DELAY)
          {
             EFFECT_DLINE *dline = &(synth_channel->m_effects.m_delayLine);
             U32           p = dline->m_play;
             U32           r = dline->m_record;

		       dline->m_line[r] = synth_channel->m_effectBuffer[i] +
							  ((dline->m_line[p] * dline->m_feedback) >> 8);

			  // For now parallel effects rather than serial
              // TODO: Put the effects buffer output into channel 0
              //       The effects output can then be panned seperately
		      channelBuffer.m_data[i] += dline->m_line[p];
		      p++;
		      p = p&0xffff;
		      r++;
		      r = r&0xffff;
		      dline->m_play = p;
		      dline->m_record = r;

          }
        }
        watermark = channelBuffer.m_size;

      }
    }

    // Copy the synthesiser output to stereo shared sound output.
    // TODO: Master volume.
    // TODO: Pan - i.e. channel buffer will be stereo
   {
      int i = 0;

      if (!clearBuffer)
      {
        while ((ptr <= (buffer_end-2)) && (i < watermark))
        {
          ptr[0] = channelBuffer.m_data[i];
          ptr++;
          ptr[0] = channelBuffer.m_data[i];
          ptr++;
          i++;
        }
      }
      else
      {
        while ((ptr <= (buffer_end-2)) && (i < watermark))
        {
          ptr[0] += channelBuffer.m_data[i];
          ptr++;
          ptr[0] += channelBuffer.m_data[i];
          ptr++;
          i++;
        }
      }
   }
    }

/*    __asm
    {
       MOV R3, #1
    }*/
}

