/*
    AUTHOR..: Ron Lee
    DATE....: 9/2/07

    Host based implementation originally by David Rowe, 5/11/97.

         Voicetronix Voice Processing Board (VPB) Software
         Copyright (C) 1997-2007 Voicetronix www.voicetronix.com.au

         This library is free software; you can redistribute it and/or
         modify it under the terms of the GNU Lesser General Public
         License version 2.1 as published by the Free Software Foundation.

         This library 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 library; if not, write to the Free Software
         Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
         MA  02110-1301  USA
*/

#include "vt/tonegen.h"
#include <cmath>

void HostToneGen::Oscillator::Reset()
{ //{{{
	double cosw = cos(m_freq * 2 * M_PI / 8000); // 8Khz sample rate

	m_cosw2 = 2.0 * cosw;
	m_y1    = (1<<15) * powf(10.0, m_mag/20.0);
	m_y2    = m_y1 * cosw;

	//printf("osc reset: freq: %d, mag: %f, cosw2: %f, y1: %f, y2: %f\n",
	//	m_freq, m_mag, m_cosw2, m_y1, m_y2);
} //}}}

void HostToneGen::Oscillator::Set( unsigned freq, float mag )
{ //{{{
	m_freq = freq;
	m_mag  = mag;
	Reset();
} //}}}

void HostToneGen::Oscillator::GetSamples( int16_t *buf, size_t n )
{ //{{{
	if( m_freq == 0 ) return;

	// y(n) = 2cos(w) * y(n-1) - y(n-2)
	for( int16_t *end = buf + n; buf != end; ++buf )
	{
		float acc = m_cosw2 * m_y1 - m_y2;
		int   sum = *buf + lrintf(acc);

		if( __builtin_expect(sum > 32767,0) )       *buf = 32767;
		else if( __builtin_expect(sum < -32768,0) ) *buf = -32768;
		else                                        *buf = sum;

		m_y2 = m_y1;
		m_y1 = acc;
	}
} //}}}

HostToneGen::State HostToneGen::ImplStart()
{ //{{{
	Config                    &config  = GetConfig();
	const Config::Freq::List  &freqs   = config.GetNextFreqs();
	const Config::Cadence     &cadence = config.GetNextCadence();

	m_osc.clear();
	m_osc.resize( freqs.size() );

	Oscillator::List::iterator  osc = m_osc.begin();
	for(Config::Freq::List::const_iterator i = freqs.begin(),
					       e = freqs.end(); i != e; ++i, ++osc)
	{
		const Config::Freq  &freq = *i;

		//fprintf(stdout, "HostToneGen::ImplStart: freq %uHz %.3fdB\n",
		//	freq.hz, freq.db);

		if(freq.hz > FREQ_MAX) throw Exception("frequency out of range");
		if(freq.db > 0)        throw Exception("magnitude out of range");

		osc->Set( freq.hz, freq.db );
	}

	m_samplecount = 0;
	m_onsamp      = cadence.onms << 3;  // 8 samples per ms.
	m_cadencesamp = m_onsamp + (cadence.offms << 3);

	#if 0
	fprintf(stdout, "HostToneGen::ImplStart: cadence %ums on %ums off,"
			"samples on %u, total %u\n",
			cadence.onms, cadence.offms,
			(unsigned)m_onsamp, (unsigned)m_cadencesamp);
	#endif
	return m_onsamp ? ONESHOT : CONTINUOUS;
} //}}}

bool HostToneGen::MixTone( int16_t *buf, size_t samples )
{ //{{{
	if( GetState() == IDLE ) return false;

	size_t onsamp = 0;

	if( m_cadencesamp ) {
		if( m_samplecount < m_onsamp )
			onsamp = std::min(samples, m_onsamp - m_samplecount);
		m_samplecount += std::min(samples, m_cadencesamp - m_samplecount);
	} else {
		onsamp        = samples;
		m_samplecount = (size_t)-1;      // != 0
	}
	if( onsamp ) {
		for( Oscillator::List::iterator i = m_osc.begin(),
						e = m_osc.end(); i != e; ++i )
			i->GetSamples( buf, onsamp );
	}
	if( m_samplecount == m_cadencesamp ) {
		//fprintf(stdout, "MixTone: samples: %d, total: %d\n",
		//	(int)m_samplecount, (int)m_cadencesamp);
		SignalCompletion();
	}
	return true;
} //}}}

