/*
* play.cc
* DIN Is Noise is copyright (c) 2006-2024 Jagannathan Sampath
* DIN Is Noise is released under GNU Public License 2.0
* For more information, please visit https://dinisnoise.org/
*/

#include "audio.h"
#include "play.h"
#include "multi_curve.h"
#include <string.h>

void play::init () {
  sol = 0;
  x = 0.0f;
  dx = 0.0f;
	vol = 0.0f;
	pdx = pvol = 0;
	alloc ();
	pdx [0] = 0.0f; pdx [n_1] = 1.0f;
	pvol [0] = 0.0f; pvol [n_1] = 1.0f;
}

void play::alloc () {
	n = aout.samples_per_channel;
	n_1 = n - 1;
	da = 1.0f / n_1;
	if (pdx) delete [] pdx;
	if (pvol) delete [] pvol;
	pdx = new float [n]; 
	pvol = new float [n]; 
	int sz = n * sizeof (float);
	memset (pdx, 0, sz);
	memset (pvol, 0, sz);
}

void play::realloc () {
	alloc ();
	fill_pitch (dx);
	fill_volume (vol);
}

play::play (solver *w) {
	init ();
	set_wave (w);
}

play::play () {
  init ();
}

play::play (const play& p) {
	sol = p.sol;
	x = p.x;
	dx = p.dx;
	vol = p.vol;
	n = p.n;
	n_1 = p.n_1;
	da = p.da;
	pdx = new float [n];
	pvol = new float [n];
	for (int i = 0; i < n; ++i) {
		pdx [i] = p.pdx [i];
		pvol [i] = p.pvol [i];
	}
}

play::~play () {
	if (pdx) delete[] pdx;
	if (pvol) delete[] pvol;
}

void play::fill_pitch (float xd) {
	for (int i = 0; i < n; ++i) pdx[i] = xd;
	dx = xd;
}

void play::fill_volume (float v) {
	for (int i = 0; i < n; ++i) pvol[i] = v;
	vol = v;
}

void play::interpolate_buffer (float* buf, float s, float e) {
	float a = 0;
	float es = e - s;
	for (int i = 0; i < n; ++i) {
		buf[i] = s + a * es;
		a += da;
	}
}

void play::set_interpolated_volume (float v) {
	interpolate_buffer (pvol, vol, v);
	vol = v;
}

void play::set_interpolated_pitch (float xd) {
	interpolate_buffer (pdx, dx, xd);
	dx = xd;
}

void play::set_interpolated_pitch (float xd, int check) {
	if ((pdx[0] == pdx[n_1]) && (pdx[0] == xd)) return; // pitch is same
	else 
		if (xd == dx) 
			for (int i = 0; i < n; ++i) pdx[i] = dx; // same as last pitch so just fill ie no interpolation needed 
		else 
			set_interpolated_pitch (xd);
}

void play::set_interpolated_volume (float v, int check) {
	if ((pvol[0]==pvol[n_1]) && (pvol[0]==v)) return; // volume is same
	else 
		if (v == vol) 
			for (int i = 0; i < n; ++i) pvol[i] = v; // same as last volume so just fill ie no interpolation needed
		else 
			set_interpolated_volume (v);
}

void play::gen_wav_and_mix (float* wav, int n) {
	(*sol) (x, pdx, n, wav);
	if (mixer.active) {
		mixer.gen_mix (aout.mix, n);
		mixer.do_mix (wav, aout.mix, aout.mixa, n);
	}
}

void play::master (float* L, float* R, float* wav, int n, float* vola)  { // master with volume array
	gen_wav_and_mix (wav, n); // mix waveforms
	operator() (L, n, wav, vola); // multiply volumes
	memcpy (R, L, aout.samples_channel_size); // copy to R
}

void play::master (float* L, float* R, float* wav, int n, float vol)  { // master with volume
	gen_wav_and_mix (wav, n); // mix waveforms
	operator() (L, n, wav, vol); // apply volume
	memcpy (R, L, aout.samples_channel_size); // copy to R
}

void play::gen_wav_fm (solver& _sol, float& _x, float* wav, float* fm, int n) { // generate waveform samples with FM 
	_sol (_x, pdx, n, fm, wav);
}

void play::gen_wav_am (float* out, float* wav, float* am, int n) { // apply AM to waveform samples
  for (int i = 0; i < n; ++i) out[i] = ((pvol[i] + am[i]) * wav[i]);
}

void play::gen_wav_fm_am_mix (float* out, int n) { // generate waveform samples, apply AM and FM and mix (if present)
	gen_wav_fm (*sol, x, aout.result, aout.fms, n);
	gen_wav_am (out, aout.result, aout.ams, n);
	if (mixer.active) {
		mixer.gen_fm (aout.result, n, aout.fms);
		gen_wav_am (aout.mix, aout.result, aout.ams, n);
		mixer.do_mix (out, aout.mix, aout.mixa, n);
	}
}

void play::gen_wav_mix (float* out, float* vola, int n) {
	gen_wav_and_mix (aout.result, n);
	operator() (out, n, aout.result, vola);
}

void play::gen_wav_mix (float* out, float vol, int n) {
	gen_wav_and_mix (aout.result, n);
	operator() (out, n, aout.result, vol);
}

void play::set_mix (multi_curve& crv, const std::string& nam) {
	mixer.setup (crv, x, dx);
  mixer.name = nam;
}
