/*
	syn123_int: internal header for libsyn123

	copyright 2018-2020 by the mpg123 project,
	licensed under the terms of the LGPL 2.1
	see COPYING and AUTHORS files in distribution or http://mpg123.org

	initially written by Thomas Orgis
*/

#ifndef _MPG123_SYN123_INT_H_
#define _MPG123_SYN123_INT_H_

#define _ISOC99_SOURCE

#include "config.h"
#include "intsym.h"
#include "compat.h"
#include "abi_align.h"
/* export DLL symbols */
#if defined(WIN32) && defined(DYNAMIC_BUILD)
#define BUILD_MPG123_DLL
#endif
#define SYN123_NO_LARGEFUNC
#include "syn123.h"

// Generally, a number of samples we work on in one go to
// allow the compiler to know our loops.
// An enum is the best integer constant you can define in plain C.
// This sets an upper limit to the number of channels in some functions.
enum { bufblock = 512 };

struct syn123_wave
{
	enum syn123_wave_id id;
	int backwards; /* TRUE or FALSE */
	double freq; /* actual frequency */
	double phase; /* current phase */
};

struct syn123_sweep
{
	struct syn123_wave wave;
	double f1, f2; // begin/end frequencies (or logarithms of the same)
	enum syn123_sweep_id id;
	size_t i; // position counter
	size_t d; // duration
	size_t post; // amount of samples after sweep to finish period
	double endphase; // phase for continuing, just after sweep end
};

// Only a forward declaration of the resampler state. An instance
// is allocated and a pointer stored if it is configured. The
// resampler is pretty disjunct from the other parts of syn123.
// Not sure if synergies will emerge eventually.
struct resample_data;

struct filter_chain
{
	int mixenc;   // double or float
	int channels;
	size_t count; // of filters
	size_t maxcount; // storage allocated for that many, to avoid
	                 // realloc in syn123_drop_filter()
	// Only one type is configured!
	struct d_filter *df; // double precision filters
	struct f_filter *ff; // single precision filters
};

struct syn123_struct
{
	// Temporary storage in internal precision.
	// This is a set of two to accomodate x and y=function(x, y).
	// Working in blocks reduces function call overhead and gives
	// chance of vectorization.
	// This may also be used as buffer for data with output encoding,
	// exploiting the fact that double is the biggest data type we
	// handle, also with the biggest alignment.
	double workbuf[2][bufblock];
	struct mpg123_fmt fmt;
	int dither; // if dithering is activated for the handle
	int do_dither; // flag for recursive calls of syn123_conv()
	uint32_t dither_seed;
	// Pointer to a generator function that writes a bit of samples
	// into workbuf[1], possibly using workbuf[0] internally.
	// Given count of samples <= bufblock!
	void (*generator)(syn123_handle*, int);
	// Generator configuration.
	// wave generator
	size_t wave_count;
	struct syn123_wave* waves;
	// pink noise, maybe others: simple structs that can be
	// simply free()d
	void* handle;
	uint32_t seed; // random seed for some RNGs
	// Extraction of initially-computed waveform from buffer.
	void *buf;      // period buffer
	size_t bufs;    // allocated size of buffer in bytes
	size_t maxbuf;  // maximum period buffer size in bytes
	size_t samples; // samples (PCM frames) in period buffer
	size_t offset;  // offset in buffer for extraction helper
	struct resample_data *rd; // resampler data, if initialized
	struct filter_chain fc;
};

#ifndef NO_SMIN
static size_t smin(size_t a, size_t b)
{
	return a < b ? a : b;
}
#endif

#ifndef NO_SMAX
static size_t smax(size_t a, size_t b)
{
	return a > b ? a : b;
}
#endif

#ifndef NO_GROW_BUF
// Grow period buffer to at least given size.
// Content is not preserved.
static void grow_buf(syn123_handle *sh, size_t bytes)
{
	if(sh->bufs >= bytes)
		return;
	if(sh->buf)
		free(sh->buf);
	sh->buf = NULL;
	if(bytes && bytes <= sh->maxbuf)
		sh->buf = malloc(bytes);
	sh->bufs = sh->buf ? bytes : 0;
}
#endif

#ifdef FILL_PERIOD
static int fill_period(syn123_handle *sh)
{
	sh->samples = 0;
	if(!sh->maxbuf)
		return SYN123_OK;
	size_t samplesize = MPG123_SAMPLESIZE(sh->fmt.encoding);
	size_t buffer_samples = sh->maxbuf/samplesize;
	grow_buf(sh, buffer_samples*samplesize);
	if(buffer_samples > sh->bufs/samplesize)
		return SYN123_DOOM;
	int outchannels = sh->fmt.channels;
	sh->fmt.channels = 1;
	size_t buffer_bytes = syn123_read(sh, sh->buf, buffer_samples*samplesize);
	sh->fmt.channels = outchannels;
	if(buffer_bytes != buffer_samples*samplesize)
		return SYN123_WEIRD;
	sh->samples = buffer_samples;
	return SYN123_OK;
}
#endif

#ifdef RAND_XORSHIFT32
// Borrowing the random number algorithm from the libmpg123 dither code.
// xorshift random number generator
// See http://www.jstatsoft.org/v08/i14/paper on XOR shift random number generators.
static float rand_xorshift32(uint32_t *seed)
{
	union
	{
		uint32_t i;
		float f;
	} fi;
	
	fi.i = *seed;
	fi.i ^= (fi.i<<13);
	fi.i ^= (fi.i>>17);
	fi.i ^= (fi.i<<5);
	*seed = fi.i;
	/* scale the number to [-0.5, 0.5] */
#ifdef IEEE_FLOAT
	fi.i = (fi.i>>9)|0x3f800000;
	fi.f -= 1.5f;
#else
	fi.f = (double)fi.i / 4294967295.0;
	fi.f -= 0.5f;
#endif
	return fi.f;
}
#endif

#endif
