//	InterestingNoise.cpp - Class used to generate interesting-sounding noise.
//  ----------------------------------------------------------------------------
//	This file is part of 'NiallsAVLib', base code for any kind of audiovisual
//	apps.
//	Copyright (C) 2012  Niall Moody
//	
//	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 3 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, see <http://www.gnu.org/licenses/>.
//	----------------------------------------------------------------------------

#include "InterestingNoise.h"
#include "MiscHelper.h"
#include "Random.h"

#include <cmath>

//------------------------------------------------------------------------------
InterestingNoise::InterestingNoise():
fadeIn(0.0f),
attenuate(false),
attVal(1.0f),
level(1.0f),
whineFade(0.0f),
whineDir(0.0f)
{
	float tempf = expf(-2.0f*NIALL_PI*880.0f/44100.0f);

	filter.freq = 880.0f;
	filter.a0 = 1.0f - tempf;
	filter.b1 = -tempf;
	filter.lastOut = 0.0f;

	nextFreq = 220.0f + (Random::getGlobal().getRandomFloat() * 1680.0f);

	whine.buf0 = 0.0f;
	whine.buf1 = 0.0f;
}

//------------------------------------------------------------------------------
InterestingNoise::~InterestingNoise()
{

}

//------------------------------------------------------------------------------
void InterestingNoise::getAudio(float *buffer, int numSamples)
{
	int i;
	float tempf;
	float whineOut;

	//Generate audio.
	for(i=0;i<numSamples;++i)
	{
		tempf = (Random::getGlobal().getRandomFloat() * 2.0f) - 1.0f;

		if(whineFade > 0.0f)
		{
			whine.buf0 = whine.buf0 + whine.freq * (tempf - whine.buf0 + whine.coeff * (whine.buf0 - whine.buf1));
			whine.buf1 = whine.buf1 + whine.freq * (whine.buf0 - whine.buf1);
			whineOut = tanhf(whine.buf1) * whineFade;
		}
		else
			whineOut = 0.0f;

		if(attenuate && (attVal > 0.5f))
			attVal -= 0.0001f;
		else if(!attenuate && (attVal < 1.0f))
			attVal += 0.0001f;

		tempf = (filter.a0 * tempf) - (filter.b1 * filter.lastOut);
		filter.lastOut = tempf;

		tempf += whineOut;

		tempf *= 0.25f * level * attVal * fadeIn;
		buffer[(i*2)] += tempf;
		buffer[(i*2)+1] += tempf;

		if(fadeIn < 1.0f)
			fadeIn += 0.00001f;
		if(whineDir > 0.0f)
		{
			whineFade += whineDir;
			if(whineFade > 1.0f)
				whineDir = -whineDir;
		}
		else if(whineDir < 0.0f)
		{
			whineFade += whineDir;
			if(whineFade < 0.0f)
			{
				whineFade = 0.0f;
				whineDir = 0.0f;
			}
		}
	}

	//Update filter coeffs.
	if(fabsf(nextFreq - filter.freq) < 1.0f)
		nextFreq = 220.0f + (Random::getGlobal().getRandomFloat() * 880.0f);

	filter.freq += (nextFreq - filter.freq) * 0.2f;
	tempf = expf(-2.0f*NIALL_PI*filter.freq/44100.0f);
	filter.a0 = 1.0f - tempf;
	filter.b1 = -tempf;

	//Update whine.
	if(whineDir == 0.0f)
	{
		if(Random::getGlobal().getRandomFloat() > 0.95f)
		{
			whineDir = 0.000002f + (Random::getGlobal().getRandomFloat() * 0.000003f);

			whine.freq = (3360.0f + (Random::getGlobal().getRandomFloat() * 7000.0f))/22050.0f;
			whine.coeff = 0.99f + 0.99f/(1.0f - whine.freq);
		}
	}
}

//------------------------------------------------------------------------------
void InterestingNoise::setAttenuate(bool val)
{
	attenuate = val;
}

//------------------------------------------------------------------------------
void InterestingNoise::setLevel(float val)
{
	level = val;
}
