/***************************************************************************
 *                                                                         *
 *   LinuxSampler - modular, streaming capable sampler                     *
 *                                                                         *
 *   Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck   *
 *   Copyright (C) 2005 - 2025 Christian Schoenebeck                       *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the Free Software           *
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston,                 *
 *   MA  02111-1307  USA                                                   *
 ***************************************************************************/

// Note: the assembly code is currently disabled, as it doesn't fit into
// the new synthesis core introduced by LS 0.4.0

#ifndef __LS_RESAMPLER_H__
#define __LS_RESAMPLER_H__

#include "../../common/global_private.h"

#ifndef USE_LINEAR_INTERPOLATION
# define USE_LINEAR_INTERPOLATION   0  ///< we set this to 0 by default for using cubic interpolation instead (slightly slower, but much better audio quality than linear interpolation)
#endif
#if USE_LINEAR_INTERPOLATION
# warning Linear interpolation was configured as resampling algorithm, which is deprecated!
#endif

namespace LinuxSampler {

    /** @brief Stereo sample point
     *
     * Encapsulates one stereo sample point, thus signal value for one
     * sample point for left and right channel.
     */
    struct stereo_sample_t {
        float left;
        float right;
    };

    /** @brief Resampler Template
     *
     * This template provides pure C++ and MMX/SSE assembly implementations
     * for linear and cubic interpolation for pitching a mono or stereo
     * input signal.
     */
    template<bool INTERPOLATE,bool BITDEPTH24>
    class Resampler {
        public:
            inline static float GetNextSampleMonoCPP(sample_t* __restrict pSrc, double* __restrict Pos, float& Pitch) {
                if (INTERPOLATE) return Interpolate1StepMonoCPP(pSrc, Pos, Pitch);
                else { // no pitch, so no interpolation necessary
                    int pos_int = (int) *Pos;
                    *Pos += 1.0;
                    return pSrc [pos_int];
                }
            }

            inline static stereo_sample_t GetNextSampleStereoCPP(sample_t* __restrict pSrc, double* __restrict Pos, float& Pitch) {
                if (INTERPOLATE) return Interpolate1StepStereoCPP(pSrc, Pos, Pitch);
                else { // no pitch, so no interpolation necessary
                    int pos_int = (int) *Pos;
                    pos_int <<= 1;
                    *Pos += 1.0;
                    stereo_sample_t samplePoint;
                    samplePoint.left  = pSrc[pos_int];
                    samplePoint.right = pSrc[pos_int+1];
                    return samplePoint;
                }
            }

        protected:

            inline static int32_t getSample(sample_t* __restrict src, int pos) {
                if (BITDEPTH24) {
                    pos *= 3;
                    #if WORDS_BIGENDIAN
                    unsigned char* p = (unsigned char*)src;
                    return p[pos] << 8 | p[pos + 1] << 16 | p[pos + 2] << 24;
                    #else
                    // 24bit read optimization: 
                    // a misaligned 32bit read and subquent 8 bit shift is faster (on x86) than reading 3 single bytes and shifting them
                    return (*((int32_t *)(&((char *)(src))[pos])))<<8;
                    #endif
                } else {
                    return src[pos];
                }
            }

            inline static float Interpolate1StepMonoCPP(sample_t* __restrict pSrc, double* __restrict Pos, float& Pitch) {
                int   pos_int   = (int) *Pos;     // integer position
                float pos_fract = *Pos - pos_int; // fractional part of position

                #if USE_LINEAR_INTERPOLATION
                    int x1 = getSample(pSrc, pos_int);
                    int x2 = getSample(pSrc, pos_int + 1);
                    float samplePoint  = (x1 + pos_fract * (x2 - x1));
                #else // polynomial interpolation
                    float xm1 = getSample(pSrc, pos_int);
                    float x0  = getSample(pSrc, pos_int + 1);
                    float x1  = getSample(pSrc, pos_int + 2);
                    float x2  = getSample(pSrc, pos_int + 3);
                    float a   = (3.0f * (x0 - x1) - xm1 + x2) * 0.5f;
                    float b   = 2.0f * x1 + xm1 - (5.0f * x0 + x2) * 0.5f;
                    float c   = (x1 - xm1) * 0.5f;
                    float samplePoint =  (((a * pos_fract) + b) * pos_fract + c) * pos_fract + x0;
                #endif // USE_LINEAR_INTERPOLATION

                *Pos += Pitch;
                return samplePoint;
            }

            inline static stereo_sample_t Interpolate1StepStereoCPP(sample_t* __restrict pSrc, double* __restrict Pos, float& Pitch) {
                int   pos_int   = (int) *Pos;  // integer position
                float pos_fract = *Pos - pos_int;     // fractional part of position
                pos_int <<= 1;

                stereo_sample_t samplePoint;

                #if USE_LINEAR_INTERPOLATION
                    // left channel
                    int x1 = getSample(pSrc, pos_int);
                    int x2 = getSample(pSrc, pos_int + 2);
                    samplePoint.left  = (x1 + pos_fract * (x2 - x1));
                    // right channel
                    x1 = getSample(pSrc, pos_int + 1);
                    x2 = getSample(pSrc, pos_int + 3);
                    samplePoint.right = (x1 + pos_fract * (x2 - x1));
                #else // polynomial interpolation
                    // calculate left channel
                    float xm1 = getSample(pSrc, pos_int);
                    float x0  = getSample(pSrc, pos_int + 2);
                    float x1  = getSample(pSrc, pos_int + 4);
                    float x2  = getSample(pSrc, pos_int + 6);
                    float a   = (3.0f * (x0 - x1) - xm1 + x2) * 0.5f;
                    float b   = 2.0f * x1 + xm1 - (5.0f * x0 + x2) * 0.5f;
                    float c   = (x1 - xm1) * 0.5f;
                    samplePoint.left = (((a * pos_fract) + b) * pos_fract + c) * pos_fract + x0;

                    //calculate right channel
                    xm1 = getSample(pSrc, pos_int + 1);
                    x0  = getSample(pSrc, pos_int + 3);
                    x1  = getSample(pSrc, pos_int + 5);
                    x2  = getSample(pSrc, pos_int + 7);
                    a   = (3.0f * (x0 - x1) - xm1 + x2) * 0.5f;
                    b   = 2.0f * x1 + xm1 - (5.0f * x0 + x2) * 0.5f;
                    c   = (x1 - xm1) * 0.5f;
                    samplePoint.right =  (((a * pos_fract) + b) * pos_fract + c) * pos_fract + x0;
                #endif // USE_LINEAR_INTERPOLATION

                *Pos += Pitch;
                return samplePoint;
            }
    };

} // namespace LinuxSampler

#endif // __LS_RESAMPLER_H__
