/*
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 <ctype.h>
#include "kernel.h"
#include "synth.h"

//      Veneer based on studying the DDE guide &
//      http://www.riscos.com/support/developers/c/cpasm.html

// Placed inside s.dspif (DSP interface)

DWORD buffer_fill(void* param, signed short* buffer_base, signed short* buffer_end, DWORD flags)
{
    int clearBuffer = 0;
    int i,j;
    BUFFER_TYPE32 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
    {
        signed short *ptr = buffer_base;
        channelBuffer.m_size = lengthbuffer;
        channelBuffer.m_dataLength = 0;

        for (j = 0; j < MAX_POLY; ++j)
        {
            BUFFER_TYPE32 *buffer = &channelBuffer;
            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          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;
            dco.m_sample.m_loop = channel->m_dco.m_sample.m_loop;
            dco.m_sample.m_loopBack = channel->m_dco.m_sample.m_loopBack;
            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)];
            }

            if (channel->m_adsr.m_stage < channel->m_adsr.m_endState)
            for (i = 0; (i < buffer->m_size); ++i)
            {
                {
                    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 << 4);
                    }

                    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 ++;
                            }
                            // Sample loop
                            if (dco.m_sample.m_loop)
                            {
                                if (playposition >= dco.m_sample.m_loop)
                                {
                                    dco.m_sample_lookup_msw = dco.m_sample.m_loopBack >> 16;
                                    dco.m_osc_lookup =
                                       (dco.m_sample.m_loopBack & 0xffff) << 14;
                                }
                            }

                            if (playposition >= dco.m_sample.m_dataLength)
                            {
                                dco.m_osc_lookup = 0;

                                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 raise- 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)
                            {
                                unsigned int tmp = channel->m_prng >> 31;

                                if (tmp & 1)
                                {
                                    signal &= SWITCH_BASE_AMP;
                                }
                                else
                                {
                                    signal &= SWITCH_TOP_AMP;
                                }
                            }
                            else
                            {
                                unsigned int tmp = channel->m_prng >> 31;

                                if (tmp & 1)
                                {
                                    signal = SWITCH_BASE_AMP;
                                }
                                else
                                {
                                    signal = SWITCH_TOP_AMP;
                                }
                            }
                        }
                    }

                    // 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;
                        int target = channel->m_adsr.m_targetADSR[channel->m_adsr.m_stage];
                        int delta = 0;
                        channel->m_adsr.m_stageCounter++;

                        // Temporary variable to simplify code
                        delta = channel->m_adsr.m_stepADSR[channel->m_adsr.m_stage];
                        if (
                            ((target != 0) && (
                                    ((delta > 0) && (amplifier >= target)) ||
                                    ((delta < 0) && (amplifier <= target))
                            )) ||
                            (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 (target != 0)
                            amplifier = target;
                        }

                        if (cont)
                        {
                            int wasSigned = channel->m_adsr.m_accumulator[channel->m_adsr.m_stage] < 0;
                            // Compute delta based on accumulators
                            channel->m_adsr.m_accumulator[channel->m_adsr.m_stage] += channel->m_adsr.m_stepADSR[channel->m_adsr.m_stage];
                            delta = channel->m_adsr.m_accumulator[channel->m_adsr.m_stage] >> 16;
                            channel->m_adsr.m_accumulator[channel->m_adsr.m_stage] =  channel->m_adsr.m_accumulator[channel->m_adsr.m_stage] & 0xffff;
                            if (wasSigned)
                              channel->m_adsr.m_accumulator[channel->m_adsr.m_stage] *= -1;

                            amplifier = (amplifier + delta);
                            if (amplifier > 0xff)
                            amplifier = 0xff;
                            if (amplifier < 0)
                            amplifier = 0;

                            if (channel->m_adsr.m_flags & ADSR_FLAG_PITCH)
                            {
                                tone = (tone + delta);
                                if (tone > 364)
                                tone = 364;
                                if (tone < 0)
                                tone = 0;

                            }

                            if (channel->m_adsr.m_flags & ADSR_FLAG_FILTER)
                            {
                                lpf.cutoff = (lpf.cutoff + delta);
                                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 + (delta * 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: Pan - i.e. channel buffer will be stereo
        {
            int i = 0;

            int volume = synth_channel->m_masterVolume;
            int avg = 0;
            while ((ptr <= (buffer_end-2)) && (i < watermark))
            {
                int level = (channelBuffer.m_data[i] * volume);

                if (!clearBuffer)
                {
                    ptr[0] = level >> 8;
                    ptr++;
                    ptr[0] = level >> 8;
                    ptr++;
                }
                else
                {
                    ptr[0] += level >> 8;
                    ptr++;
                    ptr[0] += level >> 8;
                    ptr++;
                }
                i++;
            }
        }
    }

    // The caller copies this to R3
    return flags | 1;
}

