/*

    TiMidity -- Experimental MIDI to WAVE converter
    Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>

    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., 675 Mass Ave, Cambridge, MA 02139, USA.

    resample.c
*/

#include "pent_include.h"

#ifdef USE_TIMIDITY_MIDI

#include <cmath>
#include <cstdio>
#include <cstdlib>

#include "timidity.h"
#include "timidity_common.h"
#include "timidity_instrum.h"
#include "timidity_playmidi.h"
#include "timidity_output.h"
#include "timidity_controls.h"
#include "timidity_tables.h"
#include "timidity_resample.h"

#ifdef NS_TIMIDITY
namespace NS_TIMIDITY {
#endif

#ifdef LINEAR_INTERPOLATION
# if defined(LOOKUP_HACK) && defined(LOOKUP_INTERPOLATION)
#   define RESAMPLATION \
		v1=src[ofs>>FRACTION_BITS];\
		v2=src[(ofs>>FRACTION_BITS)+1];\
		*dest++ = static_cast<sample_t>(v1 + (iplookup[(((v2-v1)<<5) & 0x03FE0) | \
					((ofs & FRACTION_MASK) >> (FRACTION_BITS-5))]));
# else
#   define RESAMPLATION \
		v1=src[ofs>>FRACTION_BITS];\
		v2=src[(ofs>>FRACTION_BITS)+1];\
		*dest++ = static_cast<sample_t>(v1 + (((v2-v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS));
# endif
#  define INTERPVARS sample_t v1;	\
                     sample_t v2
#else
/* Earplugs recommended for maximum listening enjoyment */
#  define RESAMPLATION *dest++=src[ofs>>FRACTION_BITS];
#  define INTERPVARS
#endif

#define FINALINTERP if (ofs == le) *dest++=src[ofs>>FRACTION_BITS];
/* So it isn't interpolation. At least it's final. */

extern sample_t *resample_buffer;

/*************** resampling with fixed increment *****************/

static sample_t *rs_plain(int v, sint32 *countptr)
{
	/* Play sample until end, then free the voice. */

	Voice *vp=&voice[v];
	sample_t *dest=resample_buffer;
	sample_t *src=vp->sample->data;
	sint32 ofs=vp->sample_offset;
	sint32 incr=vp->sample_increment;
	sint32 le=vp->sample->data_length;
	sint32 count=*countptr;

#ifdef PRECALC_LOOPS
	sint32 i;

	if (incr<0) incr = -incr; /* In case we're coming out of a bidir loop */

	/* Precalc how many times we should go through the loop.
	 NOTE: Assumes that incr > 0 and that ofs <= le */
	i = (le - ofs) / incr + 1;

	if (i > count)
	{
		i = count;
		count = 0;
	}
	else count -= i;

	while (i--)
	{
		INTERPVARS;
		RESAMPLATION;
		ofs += incr;
	}

	if (ofs >= le)
	{
		FINALINTERP;
		vp->status=VOICE_FREE;
		ctl->note(v);
		*countptr-=count+1;
	}

#else /* PRECALC_LOOPS */
	while (count--)
	{
		INTERPVARS;
		RESAMPLATION;
		ofs += incr;
		if (ofs >= le)
		{
			FINALINTERP;
			vp->status=VOICE_FREE;
			ctl->note(v);
			*countptr-=count+1;
			break;
		}
	}
#endif /* PRECALC_LOOPS */

	vp->sample_offset=ofs; /* Update offset */
	return resample_buffer;
}

static sample_t *rs_loop(Voice *vp, sint32 count)
{
	/* Play sample until end-of-loop, skip back and continue. */

	sint32 ofs=vp->sample_offset;
	sint32 incr=vp->sample_increment;
	sint32 le=vp->sample->loop_end;
	sint32 ll=le - vp->sample->loop_start;
	sample_t *dest=resample_buffer;
	sample_t *src=vp->sample->data;

#ifdef PRECALC_LOOPS
	while (count)
	{
		if (ofs >= le)
			/* NOTE: Assumes that ll > incr and that incr > 0. */
			ofs -= ll;
		/* Precalc how many times we should go through the loop */
		sint32 i = (le - ofs) / incr + 1;
		if (i > count)
		{
			i = count;
			count = 0;
		}
		else count -= i;
		while (i--)
		{
			INTERPVARS;
			RESAMPLATION;
			ofs += incr;
		}
	}
#else
	while (count--)
	{
		INTERPVARS;
		RESAMPLATION;
		ofs += incr;
		if (ofs>=le)
			ofs -= ll; /* Hopefully the loop is longer than an increment. */
	}
#endif

	vp->sample_offset=ofs; /* Update offset */
	return resample_buffer;
}

static sample_t *rs_bidir(Voice *vp, sint32 count)
{
	sint32 ofs=vp->sample_offset;
	sint32 incr=vp->sample_increment;
	sint32 le=vp->sample->loop_end;
	sint32 ls=vp->sample->loop_start;
	sample_t *dest=resample_buffer;
	sample_t *src=vp->sample->data;

#ifdef PRECALC_LOOPS
	sint32 le2 = le<<1;
	sint32 ls2 = ls<<1;
	/* Play normally until inside the loop region */

	if (ofs <= ls)
	{
		/* NOTE: Assumes that incr > 0, which is NOT always the case
		 when doing bidirectional looping.  I have yet to see a case
		 where both ofs <= ls AND incr < 0, however. */
		sint32 i = (ls - ofs) / incr + 1;
		if (i > count)
		{
			i = count;
			count = 0;
		}
		else count -= i;
		while (i--)
		{
			INTERPVARS;
			RESAMPLATION;
			ofs += incr;
		}
	}

	/* Then do the bidirectional looping */

	while(count)
	{
		/* Precalc how many times we should go through the loop */
		sint32 i = ((incr > 0 ? le : ls) - ofs) / incr + 1;
		if (i > count)
		{
			i = count;
			count = 0;
		}
		else count -= i;
		while (i--)
		{
			INTERPVARS;
			RESAMPLATION;
			ofs += incr;
		}
		if (ofs>=le)
		{
			/* fold the overshoot back in */
			ofs = le2 - ofs;
			incr *= 1;
		}
		else if (ofs <= ls)
		{
			ofs = ls2 - ofs;
			incr *= -1;
		}
	}

#else /* PRECALC_LOOPS */
	/* Play normally until inside the loop region */

	if (ofs < ls)
	{
		while (count--)
		{
			INTERPVARS;
			RESAMPLATION;
			ofs += incr;
			if (ofs>=ls)
				break;
		}
	}

	/* Then do the bidirectional looping */

	if (count>0)
		while (count--)
	{
		INTERPVARS;
		RESAMPLATION;
		ofs += incr;
		if (ofs>=le)
		{
			/* fold the overshoot back in */
			ofs = le - (ofs - le);
			incr = -incr;
		}
		else if (ofs <= ls)
		{
			ofs = ls + (ls - ofs);
			incr = -incr;
		}
	}
#endif /* PRECALC_LOOPS */
	vp->sample_increment=incr;
	vp->sample_offset=ofs; /* Update offset */
	return resample_buffer;
}

/*********************** vibrato versions ***************************/

/* We only need to compute one half of the vibrato sine cycle */
static int vib_phase_to_inc_ptr(int phase)
{
	if (phase < VIBRATO_SAMPLE_INCREMENTS/2)
		return VIBRATO_SAMPLE_INCREMENTS/2-1-phase;
	else if (phase >= 3*VIBRATO_SAMPLE_INCREMENTS/2)
		return 5*VIBRATO_SAMPLE_INCREMENTS/2-1-phase;
	else
		return phase-VIBRATO_SAMPLE_INCREMENTS/2;
}

static sint32 update_vibrato(Voice *vp, int sign)
{
	if (vp->vibrato_phase++ >= 2*VIBRATO_SAMPLE_INCREMENTS-1)
		vp->vibrato_phase=0;
	int phase=vib_phase_to_inc_ptr(vp->vibrato_phase);

	if (vp->vibrato_sample_increment[phase])
	{
		if (sign)
			return -vp->vibrato_sample_increment[phase];
		else
			return vp->vibrato_sample_increment[phase];
	}

	/* Need to compute this sample increment. */

	sint32 depth=vp->sample->vibrato_depth<<7;

	if (vp->vibrato_sweep)
	{
		/* Need to update sweep */
		vp->vibrato_sweep_position += vp->vibrato_sweep;
		if (vp->vibrato_sweep_position >= (1<<SWEEP_SHIFT))
			vp->vibrato_sweep=0;
		else
		{
			/* Adjust depth */
			depth *= vp->vibrato_sweep_position;
			depth >>= SWEEP_SHIFT;
		}
	}

	double a = FSCALE((static_cast<double>(vp->sample->sample_rate) *
	            static_cast<double>(vp->frequency)) /
	           (static_cast<double>(vp->sample->root_freq) *
	            static_cast<double>(play_mode->rate)),
	           FRACTION_BITS);

	int pb=static_cast<int>((sine(vp->vibrato_phase * 
	               (SINE_CYCLE_LENGTH/(2*VIBRATO_SAMPLE_INCREMENTS)))
	          * static_cast<double>(depth) * VIBRATO_AMPLITUDE_TUNING));

	if (pb<0)
	{
		pb=-pb;
		a /= bend_fine[(pb>>5) & 0xFF] * bend_coarse[pb>>13];
	}
	else
		a *= bend_fine[(pb>>5) & 0xFF] * bend_coarse[pb>>13];

	/* If the sweep's over, we can store the newly computed sample_increment */
	if (!vp->vibrato_sweep)
		vp->vibrato_sample_increment[phase]=static_cast<sint32>(a);

	if (sign)
		a = -a; /* need to preserve the loop direction */

	return static_cast<sint32>(a);
}

static sample_t *rs_vib_plain(int v, sint32 *countptr)
{
	/* Play sample until end, then free the voice. */

	Voice *vp=&voice[v];
	sample_t *dest=resample_buffer;
	sample_t *src=vp->sample->data;
	sint32 le=vp->sample->data_length;
	sint32 ofs=vp->sample_offset;
	sint32 incr=vp->sample_increment;
	sint32 count=*countptr;
	int cc=vp->vibrato_control_counter;

	/* This has never been tested */

	if (incr<0) incr = -incr; /* In case we're coming out of a bidir loop */

	while (count--)
	{
		if (!cc--)
		{
			cc=vp->vibrato_control_ratio;
			incr=update_vibrato(vp, 0);
		}
		INTERPVARS;
		RESAMPLATION;
		ofs += incr;
		if (ofs >= le)
		{
			FINALINTERP;
			vp->status=VOICE_FREE;
			ctl->note(v);
			*countptr-=count+1;
			break;
		}
	}

	vp->vibrato_control_counter=cc;
	vp->sample_increment=incr;
	vp->sample_offset=ofs; /* Update offset */
	return resample_buffer;
}

static sample_t *rs_vib_loop(Voice *vp, sint32 count)
{
	/* Play sample until end-of-loop, skip back and continue. */

	sint32 ofs=vp->sample_offset;
	sint32 incr=vp->sample_increment;
	sint32 le=vp->sample->loop_end;
	sint32 ll=le - vp->sample->loop_start;
	sample_t *dest=resample_buffer;
	sample_t *src=vp->sample->data;
	int cc=vp->vibrato_control_counter;

#ifdef PRECALC_LOOPS
	int vibflag=0;

	while (count)
	{
		/* Hopefully the loop is longer than an increment */
		if(ofs >= le)
			ofs -= ll;
		/* Precalc how many times to go through the loop, taking
		 the vibrato control ratio into account this time. */
		sint32 i = (le - ofs) / incr + 1;
		if(i > count) i = count;
		if(i > cc)
		{
			i = cc;
			vibflag = 1;
		}
		else cc -= i;
		count -= i;
		while(i--)
		{
			INTERPVARS;
			RESAMPLATION;
			ofs += incr;
		}
		if(vibflag)
		{
			cc = vp->vibrato_control_ratio;
			incr = update_vibrato(vp, 0);
			vibflag = 0;
		}
	}

#else /* PRECALC_LOOPS */
	while (count--)
	{
		if (!cc--)
		{
			cc=vp->vibrato_control_ratio;
			incr=update_vibrato(vp, 0);
		}
		INTERPVARS;
		RESAMPLATION;
		ofs += incr;
		if (ofs>=le)
			ofs -= ll; /* Hopefully the loop is longer than an increment. */
	}
#endif /* PRECALC_LOOPS */

	vp->vibrato_control_counter=cc;
	vp->sample_increment=incr;
	vp->sample_offset=ofs; /* Update offset */
	return resample_buffer;
}

static sample_t *rs_vib_bidir(Voice *vp, sint32 count)
{
	sint32 ofs=vp->sample_offset;
	sint32 incr=vp->sample_increment;
	sint32 le=vp->sample->loop_end;
	sint32 ls=vp->sample->loop_start;
	sample_t *dest=resample_buffer;
	sample_t *src=vp->sample->data;
	int cc=vp->vibrato_control_counter;

#ifdef PRECALC_LOOPS
	sint32 le2=le<<1;
	sint32 ls2=ls<<1;
	int vibflag = 0;

	/* Play normally until inside the loop region */
	while (count && (ofs <= ls))
	{
		sint32 i = (ls - ofs) / incr + 1;
		if (i > count) i = count;
		if (i > cc)
		{
			i = cc;
			vibflag = 1;
		}
		else cc -= i;
		count -= i;
		while (i--)
		{
			INTERPVARS;
			RESAMPLATION;
			ofs += incr;
		}
		if (vibflag)
		{
			cc = vp->vibrato_control_ratio;
			incr = update_vibrato(vp, 0);
			vibflag = 0;
		}
	}

	/* Then do the bidirectional looping */

	while (count)
	{
		/* Precalc how many times we should go through the loop */
		sint32 i = ((incr > 0 ? le : ls) - ofs) / incr + 1;
		if(i > count) i = count;
		if(i > cc)
		{
			i = cc;
			vibflag = 1;
		}
		else cc -= i;
		count -= i;
		while (i--)
		{
			INTERPVARS;
			RESAMPLATION;
			ofs += incr;
		}
		if (vibflag)
		{
			cc = vp->vibrato_control_ratio;
			incr = update_vibrato(vp, (incr < 0));
			vibflag = 0;
		}
		if (ofs >= le)
		{
			/* fold the overshoot back in */
			ofs = le2 - ofs;
			incr *= -1;
		}
		else if (ofs <= ls)
		{
			ofs = ls2 - ofs;
			incr *= -1;
		}
	}

#else /* PRECALC_LOOPS */
	/* Play normally until inside the loop region */

	if (ofs < ls)
	{
		while (count--)
		{
			if (!cc--)
			{
				cc=vp->vibrato_control_ratio;
				incr=update_vibrato(vp, 0);
			}
			INTERPVARS;
			RESAMPLATION;
			ofs += incr;
			if (ofs>=ls)
				break;
		}
	}

	/* Then do the bidirectional looping */

	if (count>0)
		while (count--)
	{
		if (!cc--)
		{
			cc=vp->vibrato_control_ratio;
			incr=update_vibrato(vp, (incr < 0));
		}
		INTERPVARS;
		RESAMPLATION;
		ofs += incr;
		if (ofs>=le)
		{
			/* fold the overshoot back in */
			ofs = le - (ofs - le);
			incr = -incr;
		}
		else if (ofs <= ls)
		{
			ofs = ls + (ls - ofs);
			incr = -incr;
		}
	}
#endif /* PRECALC_LOOPS */

	vp->vibrato_control_counter=cc;
	vp->sample_increment=incr;
	vp->sample_offset=ofs; /* Update offset */
	return resample_buffer;
}

sample_t *resample_voice(int v, sint32 *countptr)
{
	Voice *vp=&voice[v];

	if (!(vp->sample->sample_rate))
	{
		/* Pre-resampled data -- just update the offset and check if
		 we're out of data. */
		sint32 ofs=vp->sample_offset >> FRACTION_BITS; /* Kind of silly to use
		 FRACTION_BITS here... */
		if (*countptr >= (vp->sample->data_length>>FRACTION_BITS) - ofs)
		{
			/* Note finished. Free the voice. */
			vp->status = VOICE_FREE;
			ctl->note(v);

			/* Let the caller know how much data we had left */
			*countptr = (vp->sample->data_length>>FRACTION_BITS) - ofs;
		}
		else
			vp->sample_offset += *countptr << FRACTION_BITS;

		return vp->sample->data+ofs;
	}

	/* Need to resample. Use the proper function. */
	uint8 modes=vp->sample->modes;

	if (vp->vibrato_control_ratio)
	{
		if ((modes & MODES_LOOPING) &&
		    ((modes & MODES_ENVELOPE) ||
		     (vp->status==VOICE_ON || vp->status==VOICE_SUSTAINED)))
		{
			if (modes & MODES_PINGPONG)
				return rs_vib_bidir(vp, *countptr);
			else
				return rs_vib_loop(vp, *countptr);
		}
		else
			return rs_vib_plain(v, countptr);
	}
	else
	{
		if ((modes & MODES_LOOPING) &&
		    ((modes & MODES_ENVELOPE) ||
		     (vp->status==VOICE_ON || vp->status==VOICE_SUSTAINED)))
		{
			if (modes & MODES_PINGPONG)
				return rs_bidir(vp, *countptr);
			else
				return rs_loop(vp, *countptr);
		}
		else
			return rs_plain(v, countptr);
	}
}

void pre_resample(Sample * sp)
{
	sint16 *src = sp->data;
	static const char note_name[12][3] =
	{
		"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
	};

	ctl->cmsg(CMSG_INFO, VERB_NOISY, " * pre-resampling for note %d (%s%d)",
	          sp->note_to_use,
	          note_name[sp->note_to_use % 12], (sp->note_to_use & 0x7F) / 12);

	double a = (static_cast<double>(sp->sample_rate) * freq_table[static_cast<int>(sp->note_to_use)]) /
		(static_cast<double>(sp->root_freq) * play_mode->rate);
	if (a <= 0) return;
	auto newlen = static_cast<sint32>(sp->data_length / a);
	if (newlen < 0 || (newlen >> FRACTION_BITS) > MAX_SAMPLE_SIZE) return;
	auto *newdata = safe_Malloc<sint16>(newlen >> FRACTION_BITS);
	sint16 *dest = newdata;

	sint32 count = (newlen >> FRACTION_BITS) - 1;
	sint32 incr = (sp->data_length - (1 << FRACTION_BITS)) / count;
	sint32 ofs = incr;

	if (--count)
		*dest++ = src[0];

	/* Since we're pre-processing and this doesn't have to be done in
	 real-time, we go ahead and do the full sliding cubic interpolation. */
	while (--count)
	{
		sint16 *vptr = src + (ofs >> FRACTION_BITS);
		sint16 v1 = (vptr == src) ? *vptr : *(vptr - 1);
		sint16 v2 = *vptr;
		sint16 v3 = *(vptr + 1);
		sint16 v4 = *(vptr + 2);
		double xdiff = FSCALENEG(ofs & FRACTION_MASK, FRACTION_BITS);
		*dest++ = static_cast<sint16>(v2 + (xdiff / 6.0) * (-2 * v1 - 3 * v2 + 6 * v3 - v4 +
		                 xdiff * (3 * (v1 - 2 * v2 + v3) + xdiff * (-v1 + 3 * (v2 - v3) + v4))));
		ofs += incr;
	}

	if (ofs & FRACTION_MASK)
	{
		sint16 v1 = src[ofs >> FRACTION_BITS];
		sint16 v2 = src[(ofs >> FRACTION_BITS) + 1];
		*dest++ = static_cast<uint16>(v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS));
	}
	else
		*dest++ = src[ofs >> FRACTION_BITS];

	sp->data_length = newlen;
	sp->loop_start = static_cast<sint32>(sp->loop_start / a);
	sp->loop_end = static_cast<sint32>(sp->loop_end / a);
	free(sp->data);
	sp->data = newdata;
	sp->sample_rate = 0;
}

#ifdef NS_TIMIDITY
}
#endif

#endif //USE_TIMIDITY_MIDI
