/*  SpiralSynth
 *  Copyleft (C) 2000 David Griffiths <dave@pawfal.org>
 *
 *  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 2 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, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/ 

#include "Oscillator.h"
#include "Output.h"
#include <stdlib.h>
#include <limits.h>

static const short MAX_LEV = 10000;

Oscillator::Oscillator() : 
m_Type(SQUARE),
m_Octave(0),
m_FineFreq(1.0f),
m_PulseWidth(0.5f),
m_PortmentoSpeed(1),
m_DesiredFreq(0),
m_ModAmount(0.01f)
{
	m_CyclePos=new int[SpiralInfo::POLY];
	m_InOctave=new int[SpiralInfo::POLY];
	m_Note=new int[SpiralInfo::POLY];
	m_LastFreq=new float[SpiralInfo::POLY];
	m_Volume=new char[SpiralInfo::POLY];
	
	for (int n=0; n<SpiralInfo::POLY; n++)
	{
		m_CyclePos[n]=0;	
		m_InOctave[n]=0;
		m_Note[n]=0;
		m_LastFreq[n]=0;
		m_Volume[n]=128;
	}
}

Oscillator::~Oscillator() 
{
	delete[] m_CyclePos;
	delete[] m_InOctave;
	delete[] m_Note;
	delete[] m_LastFreq;
	delete[] m_Volume;
}

void Oscillator::NoteTrigger(int V, int octave,int s,int v) 
{
	m_InOctave[V]=octave; 
	m_Note[V]=s;
	m_Volume[V]=v;
	
	//cerr<<m_Note[v]<<" "<<s<<endl; 
}


void Oscillator::GetOutput(int V, short *data)
{
	static short count = 0, noisev;
	float Freq=0;
	float CycleLen=0;
	int PW=0, samplelen;
	
	float Volume=m_Volume[V]/128.0f;
	
	for (int n=0; n<SpiralInfo::BUFSIZE; n++)
	{
		count++;
		
		data[n]=0;
		
		if (!m_FreqModBuf)	m_DesiredFreq=NoteTable[m_Octave+m_InOctave[V]][m_Note[V]]*m_FineFreq;
		else m_DesiredFreq=(NoteTable[m_Octave+m_InOctave[V]][m_Note[V]]*m_FineFreq)+(m_FreqModBuf[n]*m_ModAmount);
		if (m_DesiredFreq==0) m_DesiredFreq=0.0001;
		
		if (m_PortmentoSpeed && m_LastFreq[V]!=m_DesiredFreq)
		{
			if (m_LastFreq[V]<m_DesiredFreq)
			{
				Freq=m_LastFreq[V]+=m_PortmentoSpeed;
			}
			else
			{
				Freq=m_LastFreq[V]-=m_PortmentoSpeed;
			}
			
			if (m_LastFreq[V]>=m_DesiredFreq-m_PortmentoSpeed &&
				m_LastFreq[V]<=m_DesiredFreq+m_PortmentoSpeed)
			{
				Freq=m_DesiredFreq;
			}			
		}
		else
		{
			Freq=m_DesiredFreq;
		}
		
		CycleLen = SpiralInfo::SAMPLERATE/Freq;

		m_LastFreq[V]=Freq;
		
		if (!m_PulseWidthModBuf) PW = (int)(m_PulseWidth * CycleLen);
		else PW = (int)((m_PulseWidth+((m_PulseWidthModBuf[n]*SpiralInfo::VALUECONV)*m_ModAmount*10.0f)) * CycleLen);

		switch (m_Type)
		{
		case SQUARE:
			// calculate square wave pattern
			m_CyclePos[V]++;
			if (m_CyclePos[V]>CycleLen) m_CyclePos[V]=0;		
			if (m_CyclePos[V]<PW) data[n]=(short)(MAX_LEV*Volume);
			else data[n]=(short)(-MAX_LEV*Volume);	
			break;
			
		case SAW:
			// get normailise position between cycle 
			
			m_CyclePos[V]++;
			if (m_CyclePos[V]>CycleLen) m_CyclePos[V]=0;		
			
			if (m_CyclePos[V]<PW) 
			{
				// before pw -1->1
				data[n] = static_cast<short>(Linear(0,PW,m_CyclePos[V],-MAX_LEV*Volume,MAX_LEV*Volume));
			}
			else 
			{
				// after pw 1->-1
				data[n] = static_cast<short>(Linear(PW,CycleLen,m_CyclePos[V],MAX_LEV*Volume,-MAX_LEV*Volume));
			}	
			
			break;
			
		case NOISE:
			m_CyclePos[V]++;
			
			//modulate the sample & hold length
			if (!m_SHModBuf) samplelen = static_cast<int>(m_SHLen*SpiralInfo::SAMPLERATE);
			else samplelen = static_cast<int>((m_SHLen+(m_SHModBuf[n]*m_ModAmount))*SpiralInfo::SAMPLERATE);
		
			// do sample & hold on the noise
			if (m_CyclePos[V]>samplelen)
			{
				noisev=(short)((MAX_LEV/2)-(rand()%MAX_LEV*Volume*2));
				m_CyclePos[V]=0;
			}
			data[n]=noisev;
			break;
		case NONE: break;
		}
		
	}
}

void Oscillator::Randomise()
{
	float t=RandFloat();
	if (t<0.33) m_Type=SQUARE;
	else if (t<0.66) m_Type=SAW;
	else m_Type=NOISE;

	m_Octave = (rand()%6)-3;
	m_FineFreq = RandFloat(0.0f,2.0f);
	m_PulseWidth = RandFloat();
	m_PortmentoSpeed = RandFloat();
	m_ModAmount = RandFloat()/0.1f;
	m_SHLen = RandFloat(0.0f,0.2f);
}

istream &operator>>(istream &s, Oscillator &o)
{
	s>>(int&)o.m_Type>>o.m_Octave>>o.m_FineFreq>>o.m_PulseWidth>>o.m_PortmentoSpeed>>
	o.m_SHLen>>o.m_ModAmount>>o.m_Volume;
	return s;
}

ostream &operator<<(ostream &s, Oscillator &o)
{
	s<<(int)o.m_Type<<" "<<o.m_Octave<<" "<<o.m_FineFreq<<" "<<o.m_PulseWidth<<" "<<
	o.m_PortmentoSpeed<<" "<<o.m_SHLen<<" "<<o.m_ModAmount<<" "<<o.m_Volume<<" ";
	return s;
}
