// Blip_Buffer 0.4.1. http://www.slack.net/~ant/

#include <limits>
#include <numeric>
#include <cmath>
#include <cstring>
#include "Blip_Buffer.h"

/* Copyright (C) 2003-2007 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module 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 Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */

// TODO: use scoped for variables in treble_eq()

Blip_Buffer::Blip_Buffer()
{
	this->factor_ = static_cast<uint32_t>(std::numeric_limits<long>::max());
	this->buffer_.clear();
	this->buffer_size_ = 0;
	this->sample_rate_ = 0;
	this->bass_shift_ = 0;
	this->clock_rate_ = 0;
	this->bass_freq_ = 16;
	this->length_ = 0;

	// assumptions code makes about implementation-defined features
#ifndef NDEBUG
	// right shift of negative value preserves sign
	buf_t_ i = -0x7FFFFFFE;
	assert((i >> 1) == -0x3FFFFFFF);

	// casting to short truncates to 16 bits and sign-extends
	i = 0x18000;
	assert(static_cast<short>(i) == -0x8000);
#endif

	this->clear();
}

Blip_Buffer::~Blip_Buffer()
{
}

void Blip_Buffer::clear(int entire_buffer)
{
	this->offset_ = 0;
	this->reader_accum_ = 0;
	this->modified_ = nullptr;
	if (!this->buffer_.empty())
	{
		long count = entire_buffer ? this->buffer_size_ : this->samples_avail();
		memset(&this->buffer_[0], 0, (count + blip_buffer_extra_) * sizeof(buf_t_));
	}
}

void Blip_Buffer::set_sample_rate(long new_rate, int msec)
{
	// start with maximum length that resampled time can represent
	long new_size = (ULONG_MAX >> BLIP_BUFFER_ACCURACY) - blip_buffer_extra_ - 64;
	if (msec != blip_max_length)
	{
		long s = (new_rate * (msec + 1) + 999) / 1000;
		if (s < new_size)
			new_size = s;
		else
			assert(false); // fails if requested buffer length exceeds limit
	}

	if (this->buffer_size_ != new_size)
		this->buffer_.resize(new_size + blip_buffer_extra_);

	this->buffer_size_ = new_size;

	// update things based on the sample rate
	this->sample_rate_ = new_rate;
	this->length_ = new_size * 1000 / new_rate - 1;
	if (msec)
		assert(this->length_ == msec); // ensure length is same as that passed in

	// update these since they depend on sample rate
	if (this->clock_rate_)
		this->clock_rate(this->clock_rate_);
	this->bass_freq(this->bass_freq_);

	this->clear();
}

blip_resampled_time_t Blip_Buffer::clock_rate_factor(long rate) const
{
	double ratio = static_cast<double>(this->sample_rate_) / rate;
	int32_t factor = static_cast<int32_t>(std::floor(ratio * (1L << BLIP_BUFFER_ACCURACY) + 0.5));
	assert(factor > 0 || !this->sample_rate_); // fails if clock/output ratio is too large
	return static_cast<blip_resampled_time_t>(factor);
}

void Blip_Buffer::bass_freq(int freq)
{
	this->bass_freq_ = freq;
	int shift = 31;
	if (freq > 0)
	{
		shift = 13;
		long f = (freq << 16) / this->sample_rate_;
		while ((f >>= 1) && --shift);
	}
	this->bass_shift_ = shift;
}

void Blip_Buffer::end_frame(blip_time_t t)
{
	this->offset_ += t * this->factor_;
	assert(this->samples_avail() <= static_cast<long>(this->buffer_size_)); // fails if time is past end of buffer
}

long Blip_Buffer::count_samples(blip_time_t t) const
{
	auto last_sample = this->resampled_time(t) >> BLIP_BUFFER_ACCURACY;
	auto first_sample = this->offset_ >> BLIP_BUFFER_ACCURACY;
	return static_cast<long>(last_sample - first_sample);
}

blip_time_t Blip_Buffer::count_clocks(long count) const
{
	if (!this->factor_)
	{
		assert(false); // sample rate and clock rates must be set first
		return 0;
	}

	if (count > this->buffer_size_)
		count = this->buffer_size_;
	auto time = static_cast<blip_resampled_time_t>(count) << BLIP_BUFFER_ACCURACY;
	return static_cast<blip_time_t>((time - this->offset_ + this->factor_ - 1) / this->factor_);
}

void Blip_Buffer::remove_samples(long count)
{
	if (count)
	{
		this->remove_silence(count);

		// copy remaining samples to beginning and clear old samples
		long remain = this->samples_avail() + blip_buffer_extra_;
		memmove(&this->buffer_[0], &this->buffer_[count], remain * sizeof(buf_t_));
		memset(&this->buffer_[remain], 0, count * sizeof(buf_t_));
	}
}

// Blip_Synth_

Blip_Synth_Fast_::Blip_Synth_Fast_()
{
	this->buf = nullptr;
	this->last_amp = this->delta_factor = 0;
}

void Blip_Synth_Fast_::volume_unit(double new_unit)
{
	this->delta_factor = static_cast<int>(new_unit * (1L << blip_sample_bits) + 0.5);
}

#if !BLIP_BUFFER_FAST

Blip_Synth_::Blip_Synth_(short *p, int w) : impulses(p), width(w)
{
	this->volume_unit_ = 0.0;
	this->kernel_unit = 0;
	this->buf = nullptr;
	this->last_amp = this->delta_factor = 0;
}

#ifndef M_PI
static const double M_PI = 3.1415926535897932384626433832795029;
#endif

static void gen_sinc(float *out, int count, double oversample, double treble, double cutoff)
{
	if (cutoff >= 0.999)
		cutoff = 0.999;

	if (treble < -300.0)
		treble = -300.0;
	if (treble > 5.0)
		treble = 5.0;

	static const double maxh = 4096.0;
	double rolloff = std::pow(10.0, 1.0 / (maxh * 20.0) * treble / (1.0 - cutoff));
	double pow_a_n = std::pow(rolloff, maxh - maxh * cutoff);
	double to_angle = M_PI / 2 / maxh / oversample;
	for (int i = 0; i < count; ++i)
	{
		double angle = ((i - count) * 2 + 1) * to_angle;
		double c = rolloff * std::cos((maxh - 1.0) * angle) - std::cos(maxh * angle);
		double cos_nc_angle = std::cos(maxh * cutoff * angle);
		double cos_nc1_angle = std::cos((maxh * cutoff - 1.0) * angle);
		double cos_angle = std::cos(angle);

		c = c * pow_a_n - rolloff * cos_nc1_angle + cos_nc_angle;
		double d = 1.0 + rolloff * (rolloff - cos_angle - cos_angle);
		double b = 2.0 - cos_angle - cos_angle;
		double a = 1.0 - cos_angle - cos_nc_angle + cos_nc1_angle;

		out[i] = static_cast<float>((a * d + c * b) / (b * d)); // a / b + c / d
	}
}

void blip_eq_t::generate(float *out, int count) const
{
	// lower cutoff freq for narrow kernels with their wider transition band
	// (8 points->1.49, 16 points->1.15)
	double oversample = blip_res * 2.25 / count + 0.85;
	double half_rate = this->sample_rate * 0.5;
	if (this->cutoff_freq)
		oversample = half_rate / this->cutoff_freq;
	double cutoff = this->rolloff_freq * oversample / half_rate;

	gen_sinc(out, count, blip_res * oversample, this->treble, cutoff);

	// apply (half of) hamming window
	double to_fraction = M_PI / (count - 1);
	for (int i = count; i--; )
		out[i] *= 0.54f - 0.46f * static_cast<float>(std::cos(i * to_fraction));
}

void Blip_Synth_::adjust_impulse()
{
	// sum pairs for each phase and add error correction to end of first half
	int size = this->impulses_size();
	for (int p = blip_res; p-- >= blip_res / 2; )
	{
		int p2 = blip_res - 2 - p;
		long error = this->kernel_unit;
		for (int i = 1; i < size; i += blip_res)
			error -= this->impulses[i + p] + this->impulses[i + p2];
		if (p == p2)
			error /= 2; // phase = 0.5 impulse uses same half for both sides
		this->impulses[size - blip_res + p] += static_cast<short>(error);
		//printf( "error: %ld\n", error );
	}

	//for (int i = blip_res; i--; printf("\n"))
		//for (int j = 0; j < this->width / 2; ++j)
			//printf("%5ld,", this->impulses[j * blip_res + i + 1]);
}

void Blip_Synth_::treble_eq(const blip_eq_t &eq)
{
	float fimpulse[blip_res / 2 * (blip_widest_impulse_ - 1) + blip_res * 2];

	int half_size = blip_res / 2 * (this->width - 1);
	eq.generate(&fimpulse[blip_res], half_size);

	int i;

	// need mirror slightly past center for calculation
	for (i = blip_res; i--; )
		fimpulse[blip_res + half_size + i] = fimpulse[blip_res + half_size - 1 - i];

	// starts at 0
	std::fill_n(&fimpulse[0], blip_res, 0.0f);

	// find rescale factor
	double total = std::accumulate(&fimpulse[blip_res], &fimpulse[blip_res + half_size], 0.0);

	//static const double base_unit = 44800.0 - 128 * 18; // allows treble up to +0 dB
	//static const double base_unit = 37888.0; // allows treble to +5 dB
	static const double base_unit = 32768.0; // necessary for blip_unscaled to work
	double rescale = base_unit / 2 / total;
	this->kernel_unit = static_cast<long>(base_unit);

	// integrate, first difference, rescale, convert to int
	double sum = 0.0;
	double next = 0.0;
	int size = this->impulses_size();
	for (i = 0; i < size; ++i)
	{
		this->impulses[i] = static_cast<short>(std::floor((next - sum) * rescale + 0.5));
		sum += fimpulse[i];
		next += fimpulse[i + blip_res];
	}
	this->adjust_impulse();

	// volume might require rescaling
	double vol = this->volume_unit_;
	if (vol)
	{
		this->volume_unit_ = 0.0;
		this->volume_unit(vol);
	}
}

void Blip_Synth_::volume_unit(double new_unit)
{
	if (new_unit != this->volume_unit_)
	{
		// use default eq if it hasn't been set yet
		if (!this->kernel_unit)
			this->treble_eq(-8.0);

		this->volume_unit_ = new_unit;
		double factor = new_unit * (1L << blip_sample_bits) / this->kernel_unit;

		if (factor > 0.0)
		{
			int shift = 0;

			// if unit is really small, might need to attenuate kernel
			while (factor < 2.0)
			{
				++shift;
				factor *= 2.0;
			}

			if (shift)
			{
				this->kernel_unit >>= shift;
				assert(this->kernel_unit > 0); // fails if volume unit is too low

				// keep values positive to avoid round-towards-zero of sign-preserving
				// right shift for negative values
				long offset = 0x8000 + (1 << (shift - 1));
				long offset2 = 0x8000 >> shift;
				for (int i = this->impulses_size(); i--; )
					this->impulses[i] = static_cast<short>(((this->impulses[i] + offset) >> shift) - offset2);
				this->adjust_impulse();
			}
		}
		this->delta_factor = static_cast<int>(std::floor(factor + 0.5));
		//printf( "delta_factor: %d, kernel_unit: %d\n", delta_factor, kernel_unit );
	}
}
#endif

long Blip_Buffer::read_samples(blip_sample_t *out_, long max_samples, bool stereo)
{
	long count = this->samples_avail();
	if (count > max_samples)
		count = max_samples;

	if (count)
	{
		int bass = BLIP_READER_BASS(this);
		BLIP_READER_BEGIN(reader, *this);
		BLIP_READER_ADJ_(reader, count);
		auto out = &out_[count];
		int32_t offset = static_cast<int32_t>(-count);

		do
		{
			int32_t s = BLIP_READER_READ(reader);
			BLIP_READER_NEXT_IDX_(reader, bass, offset);
			BLIP_CLAMP(s, s);
			out[offset * (stereo ? 2 : 1)] = static_cast<blip_sample_t>(s);
		} while (++offset);

		BLIP_READER_END(reader, *this);

		this->remove_samples(count);
	}
	return count;
}

void Blip_Buffer::mix_samples(const blip_sample_t *in, long count)
{
	auto out = &this->buffer_[(this->offset_ >> BLIP_BUFFER_ACCURACY) + blip_widest_impulse_ / 2];

	static const int sample_shift = blip_sample_bits - 16;
	int prev = 0;
	while (count--)
	{
		int32_t s = static_cast<int32_t>(*in++) << sample_shift;
		*out += s - prev;
		prev = s;
		++out;
	}
	*out -= prev;
}