// Copyright (C) Mikko Apo (apo@iki.fi)
// The following code may be used to write free software
// if credit is given to the original author.
// Using it for anything else is not allowed without permission
// from the author.

/*

  Random number generator from Oskari Tammelin's Noise.cpp Noise generator
  FSM did the logarithmic volume slider mathematic formula

*/

/*
Revision history:
  1.03  Fixed a stupid bug. This caused the fixer to fix
		only first half of a stereo signal block.
		Cheapo fixer pro makes this machine obsolete.
  1.02  Lowered the default noise level to -168.4dB
  1.01	Optimized Tick()
  1.0	First release.
  */

#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <string.h>
#include "../mdk.h"

#define COMMAND_STRING "About..."
#define MACHINE_NAME "cheapo fixer"
#define SHORT_NAME "ch.fixer"
#define MACHINE_AUTHOR "Mikko Apo (apo@iki.fi)"
#define MAX_TRACKS		0
#define MIN_TRACKS		0
#define NUMGLOBALPARAMETERS 2
#define NUMTRACKPARAMETERS 0
#define NUMATTRIBUTES 0
#define MI__VERSION__ "1.03"

//	Parameters

CMachineParameter const paraThreshold = 
{ pt_word, "Threshold","Threshold [0 - 32768]",0,32768,0xffff,MPF_STATE,0 };

CMachineParameter const paraLevel = 
{ pt_byte, "Noise Level","Whitenoise Level",0,0x7f,0xff,MPF_STATE,0x20 };

// List of all parameters, track parameters last

CMachineParameter const *pParameters[] = 
{ &paraThreshold,&paraLevel };

#pragma pack(1)

class gvals
{
public:
	word threshold;
	byte level;
};

#pragma pack()

// Machine's info

CMachineInfo const MacInfo = 
{
	MT_EFFECT,MI_VERSION,MIF_DOES_INPUT_MIXING,MIN_TRACKS,MAX_TRACKS,
	NUMGLOBALPARAMETERS,NUMTRACKPARAMETERS,pParameters,NUMATTRIBUTES,NULL,
#ifdef _DEBUG
	MACHINE_NAME" [DEBUG]"
#else
	MACHINE_NAME
#endif
	,SHORT_NAME,MACHINE_AUTHOR,COMMAND_STRING
};


class miex : public CMDKMachineInterfaceEx
{

};

class mi : public CMDKMachineInterface
{
public:
	mi();

	virtual void Command(int const i);
	virtual void Tick();
	virtual char const *DescribeValue(int const param, int const value);

	virtual void MDKInit(CMachineDataInput * const pi);
	virtual bool MDKWork(float *psamples, int numsamples, int const mode);
	virtual bool MDKWorkStereo(float *psamples, int numsamples, int const mode);
	virtual void MDKSave(CMachineDataOutput * const po) { }

	public:
	virtual CMDKMachineInterfaceEx *GetEx() { return &ex; }
	virtual void OutputModeChanged(bool stereo) {};

	public:
	miex ex;
	gvals gval;

private:

	float valThreshold;
	int RandStat;
	float amp;
	int valLevel;
};


DLL_EXPORTS

mi::mi()
{
	GlobalVals = &gval;
}

void mi::Command(int const i)
{
	static char txt[500];
	switch(i)
	{
	case 0:
		pCB->MessageBox(MACHINE_NAME"\n\nBuild date: "__DATE__"\nVersion: "MI__VERSION__"\nCoded by: "MACHINE_AUTHOR"\nThanks to Oskari and FSM for code snippets.\n\nCheck out http://www.iki.fi/apo/buzz/\nfor more buzz stuff.\n\nExcellent skin made by Hymax.");
		break;

/*	case 1:
		{
			unsigned int c;
			int stat=0x16BA2118;
			int smallest=0x7fffffff, biggest=0xffffffff;
			int null_counter=0;
			for(c=1;c<=0xffff;c++)
			{
				stat = ((stat * 1103515245 + 12345) & 0xffff) - 0x8000;
				if(stat>biggest) biggest=stat;
				if(stat<smallest) smallest=stat;
				if(!stat) null_counter++;
			}
			sprintf(txt,"Random test:\nSmallest: 0x%x (%ld)\nBiggest: 0x%x (%ld)\nNull counter: %ld\n",smallest,smallest,biggest,biggest,null_counter);
			pCB->MessageBox(txt);
		}
*/	}
}

char const *mi::DescribeValue(int const param, int const value)
{
	static char txt[100];

	switch(param)
	{
	case 0:
		if(value)
		{
		  sprintf(txt,"%d",value);
		} else
		{
			return("0.5");
		}
		break;
	case 1:
			sprintf(txt,"%.2fdB",20*(log10((((valThreshold)?valThreshold:0.5)*pow(2.0,(1-value/127.0)*(-16))/32768))));
		break;
	}
	return txt;
}

void mi::MDKInit(CMachineDataInput * const pi)
{
	valThreshold = (float)paraThreshold.DefValue;
	valLevel = paraLevel.DefValue;
	RandStat = 0x16BA2118;	// initialize random generator
	amp=(float)((pow(2.0,(1-valLevel/127.0)*(-16))*valThreshold)/0x7fff);
}

void mi::Tick()
{
	bool changed=false;
	if (gval.threshold != paraThreshold.NoValue)
	{
		valThreshold=gval.threshold;
		changed=true;
	}
	if (gval.level != paraLevel.NoValue)
	{
		valLevel=gval.level;
		changed=true;
	}
	if(changed)
	{
	if(valThreshold)
	{
		amp = (float) ((pow(2.0,(1-valLevel/127.0)*(-16))*valThreshold)/0x7fff);
	} else
	{
		amp = (float) ((pow(2.0,(1-valLevel/127.0)*(-16))*0.5)/0x7fff);
	}
	}
} 


bool mi::MDKWorkStereo(float *psamples, int numsamples, int const mode)
{
	if ((mode==WM_WRITE)||(mode==WM_NOIO))
	{
		return false;
	}

	if (mode == WM_READ)		// <thru>
		return true;

	do 
	{
		if(fabs(*psamples)<=valThreshold)
		{
			RandStat = ((RandStat * 1103515245 + 12345) & 0xffff) - 0x8000;
			*psamples= amp*RandStat;
		}
		psamples++;
		if(fabs(*psamples)<=valThreshold)
		{
			RandStat = ((RandStat * 1103515245 + 12345) & 0xffff) - 0x8000;
			*psamples= amp*RandStat;
		}
		psamples++;
	} while(--numsamples);

	return true;
}

bool mi::MDKWork(float *psamples, int numsamples, int const mode)
{
	if ((mode==WM_WRITE)||(mode==WM_NOIO))
	{
		return false;
	}

	if (mode == WM_READ)		// <thru>
		return true;

	do 
	{
		if(fabs(*psamples)<=valThreshold)
		{
			RandStat = ((RandStat * 1103515245 + 12345) & 0xffff) - 0x8000;
			*psamples=amp*RandStat;
		}
		psamples++;
	} while(--numsamples);

	return true;
}
