/* Simple fixed-point linear resampler by BearOso*/

#ifndef __LINEAR_RESAMPLER_H
#define __LINEAR_RESAMPLER_H

#include "resampler.h"
#include "snes9x.h"

static const int    f_prec = 15;
static const uint32 f__one = (1 << f_prec);

#define lerp(t, a, b) (((((b) - (a)) * (t)) >> f_prec) + (a))

class LinearResampler : public Resampler
{
    protected:
        uint32 f__r_step;
        uint32 f__inv_r_step;
        uint32 f__r_frac;
        int    r_left, r_right;

    public:
        LinearResampler (int num_samples) : Resampler (num_samples)
        {
            f__r_frac = 0;
        }

        ~LinearResampler ()
        {
        }

        void
        time_ratio (double ratio)
        {
            if (ratio == 0.0)
                ratio = 1.0;
            f__r_step = (uint32) (ratio * f__one);
            f__inv_r_step = (uint32) (f__one / ratio);
            clear ();
        }

        void
        clear (void)
        {
            ring_buffer::clear ();
            f__r_frac = 0;
            r_left = 0;
            r_right = 0;
        }

        void
        read (short *data, int num_samples)
        {
            int i_position = start >> 1;
            short *internal_buffer = (short *) buffer;
            int o_position = 0;
            int consumed = 0;
            int max_samples = (buffer_size >> 1);

            while (o_position < num_samples && consumed < buffer_size)
            {
                if (f__r_step == f__one)
                {
                    data[o_position] = internal_buffer[i_position];
                    data[o_position + 1] = internal_buffer[i_position + 1];

                    o_position += 2;
                    i_position += 2;
                    if (i_position >= max_samples)
                        i_position -= max_samples;
                    consumed += 2;

                    continue;
                }

                while (f__r_frac <= f__one  && o_position < num_samples)
                {
                    data[o_position]     = lerp (f__r_frac,
                                                 r_left,
                                                 internal_buffer[i_position]);
                    data[o_position + 1] = lerp (f__r_frac,
                                                 r_right,
                                                 internal_buffer[i_position + 1]);

                    o_position += 2;

                    f__r_frac += f__r_step;
                }

                if (f__r_frac > f__one)
                {
                    f__r_frac -= f__one;
                    r_left = internal_buffer[i_position];
                    r_right = internal_buffer[i_position + 1];
                    i_position += 2;
                    if (i_position >= max_samples)
                        i_position -= max_samples;
                    consumed += 2;
                }
            }

            size -= consumed << 1;
            start += consumed << 1;
            if (start >= buffer_size)
                start -= buffer_size;
        }

        inline int
        avail (void)
        {
            return (((size >> 2) * f__inv_r_step) - ((f__r_frac * f__inv_r_step) >> f_prec)) >> (f_prec - 1);
        }
};

#endif /* __LINEAR_RESAMPLER_H */