// synthv1_wave.h
//
/****************************************************************************
   Copyright (C) 2012-2023, rncbc aka Rui Nuno Capela. All rights reserved.

   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.,
   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

*****************************************************************************/

#ifndef __synthv1_wave_h
#define __synthv1_wave_h

#include <cstdint>


// forward decls.
class synthv1_wave_sched;


//-------------------------------------------------------------------------
// synthv1_wave - smoothed (integrating oversampled) wave table.
//

class synthv1_wave
{
public:

	// shape.
	enum Shape { Pulse = 0, Saw, Sine, Rand, Noise };

	// ctor.
	synthv1_wave(uint32_t nsize = 4096, uint16_t nover = 24, uint16_t ntabs = 8);

	// dtor.
	~synthv1_wave();

	// properties.
	Shape shape() const
		{ return m_shape; }
	float width() const
		{ return m_width; }
	bool bandl() const
		{ return m_bandl; }

	// sample rate.
	void setSampleRate(float srate)
		{ m_srate = srate; }
	float sampleRate() const
		{ return m_srate; }

	// table size (in frames)
	uint32_t size() const
		{ return m_nsize; }

	// phase-zero (for slave reset)
	float phase0() const
		{ return m_phase0; }

	// init.
	void reset(Shape shape, float width, bool bandl = false);
	// init.sync.
	void reset_sync();

	// init.test
	void reset_test(Shape shape, float width, bool bandl = false)
	{
		if (shape != m_shape || width != m_width
			|| (m_ntabs > 0 && bandl != m_bandl))
			reset(shape, width, bandl);
	}

	// phasor.
	struct Phase
	{
		Phase() { reset(); }

		float    phase;
		float    ftab;
		uint16_t itab;

		Phase   *slave;
		float    slave_phase0;

		void reset()
		{
			phase = ftab = slave_phase0 = 0.0f;
			itab  = 0;
			slave = 0;
		}
	};

	// begin.
	float start(Phase& phase, float pshift = 0.0f, float freq = 0.0f) const
	{
		update(phase, freq);

		phase.phase = m_phase0 + pshift;
		if (phase.phase >= 1.0f)
			phase.phase -= 1.0f;

		return sample(phase, freq);
	}

	// iterate.
	float sample(Phase& phase, float freq) const
	{
		const float index = phase.phase * float(m_nsize);
		const uint32_t i = uint32_t(index);
		const float alpha = index - float(i);

		phase.phase += freq / m_srate;
		if (phase.phase >= 1.0f) {
			phase.phase -= 1.0f;
			if (phase.slave)
				phase.slave->phase = phase.slave_phase0;
		}

		if (phase.itab < m_ntabs) {
			const float x0 = interp(i, phase.itab, alpha);
			const float x1 = interp(i, phase.itab + 1, alpha);
			return x0 + phase.ftab * (x1 - x0);
		} else {
			return interp(i, phase.itab, alpha);
		}
	}

	// interpolate.
	float interp(uint32_t i, uint16_t itab, float alpha) const
	{
		float *frames = m_tables[itab];

		const float x0 = frames[i];
		const float x1 = frames[i + 1];
#if 0	// cubic interp.
		const float x2 = frames[i + 2];
		const float x3 = frames[i + 3];

		const float c1 = (x2 - x0) * 0.5f;
		const float b1 = (x1 - x2);
		const float b2 = (c1 + b1);
		const float c3 = (x3 - x1) * 0.5f + b2 + b1;
		const float c2 = (c3 + b2);

		return (((c3 * alpha) - c2) * alpha + c1) * alpha + x1;
#else	// linear interp.
		return x0 + alpha * (x1 - x0);
#endif
	}

	// absolute value.
	float value(float phase) const
	{
		phase += m_phase0;
		if (phase >= 1.0f)
			phase -= 1.0f;

		return m_tables[m_ntabs][uint32_t(phase * float(m_nsize))];
	}

	// post-iter.
	void update(Phase& phase, float freq) const
	{
		if (freq < m_min_freq) {
			phase.itab  = m_ntabs;
			phase.ftab  = 0.0f;
		} else if (freq < m_max_freq) {
			phase.ftab  = fast_log2f(m_max_freq / freq);
			phase.itab  = uint16_t(phase.ftab);
			phase.ftab -= float(phase.itab);
		} else {
			phase.itab  = 0;
			phase.ftab  = 0.0f;
		}
	}

protected:

	// fast log2f approximation.
	static inline float fast_log2f ( float x )
	{
		union { float f; uint32_t i; } u;
		u.f = x;
		return (u.i * 1.192092896e-7f) - 126.943612f;
	}

	// init pulse tables.
	void reset_pulse();
	// init pulse partial table.
	void reset_pulse_part(uint16_t itab);

	// init saw tables.
	void reset_saw();
	// init saw partial table.
	void reset_saw_part(uint16_t itab);

	// init sine tables.
	void reset_sine();
	// init sine partial table.
	void reset_sine_part(uint16_t itab);

	// init random tables.
	void reset_rand();
	// init random partial table.
	void reset_rand_part(uint16_t itab);

	// init noise tables.
	void reset_noise();
	// init noise partial table.
	void reset_noise_part(uint16_t itab);

	// post-processors.
	void reset_filter(uint16_t itab);
	void reset_normalize(uint16_t itab);
	void reset_interp(uint16_t itab);

	// Hal Chamberlain's pseudo-random linear congruential method.
	float pseudo_randf ()
	{
		m_srand = (m_srand * 196314165) + 907633515;
		return m_srand / float(INT32_MAX) - 1.0f;
	}

private:

	uint32_t m_nsize;
	uint16_t m_nover;
	uint16_t m_ntabs;

	Shape    m_shape;
	float    m_width;
	bool     m_bandl;

	float    m_srate;
	float  **m_tables;
	float    m_phase0;

	uint32_t m_srand;

	float    m_min_freq;
	float    m_max_freq;

	synthv1_wave_sched *m_sched;
};


//-------------------------------------------------------------------------
// synthv1_wave_lf - hard/non-smoothed wave table (eg. LFO).
//

class synthv1_wave_lf : public synthv1_wave
{
public:

	// ctor.
	synthv1_wave_lf(uint32_t nsize = 1024)
		: synthv1_wave(nsize, 0, 0) {}
};


//-------------------------------------------------------------------------
// synthv1_oscillator - wave table oscillator
//

class synthv1_oscillator
{
public:

	// ctor.
	synthv1_oscillator(synthv1_wave *wave = 0) { reset(wave); }

	// wave and phase accessors.
	void reset(synthv1_wave *wave)
		{ m_wave = wave; m_phase.reset(); }

	synthv1_wave *wave() const
		{ return m_wave; }

	// begin.
	float start(float pshift = 0.0f, float freq = 0.0f)
		{ return m_wave->start(m_phase, pshift, freq); }

	// iterate.
	float sample(float freq)
		{ return m_wave->sample(m_phase, freq); }

	// post-iter.
	void update(float freq)
		{ m_wave->update(m_phase, freq); }

	// hard-sync slave.
	void sync(synthv1_oscillator *slave)
	{
		if (slave && slave->m_wave) {
			m_phase.slave = &(slave->m_phase);
			m_phase.slave_phase0 = (slave->m_wave)->phase0();
		} else {
			m_phase.slave = 0;
			m_phase.slave_phase0 = 0.0f;
		}
	}

	// phase-shift accessor.
	float pshift() const
	{
		const float pshift = m_wave->phase0() + m_phase.phase;
		return (pshift >= 1.0f ? pshift - 1.0f : pshift);
	}

private:

	synthv1_wave       *m_wave;
	synthv1_wave::Phase m_phase;
};


#endif	// __synthv1_wave_h

// end of synthv1_wave.h
