#include <stdio.h>
#include "module.h"

/**********************************************************************
The SumChannels function samples all the channels, preparing the
combined sum. Each channel gets modified such that further calls
will produce further sums.  Channel repeats and volumes are also
handled within this function. The function is inline to decrease
computational time.
**********************************************************************/

static inline int SumChannels(moduleinfo * m)
{
	////////////////////////////////////////////////////////////
	// All active channels are looped through. The sum variable
	// initially contains zero. Sample data for each channel is
	// calculated and added onto the sum. Adding two waves will
	// effectively mix the waves together.  Adding all channels
	// produces the next piece of sound data.
	////////////////////////////////////////////////////////////
	for (int i = 0, sum = 0; i < m->numchannels; i++)
	{
		////////////////////////////////////////////////////////////
		// The position into the sample data is stored as one 24.8
		// fixed point number. We extract the position and compare
		// against the sample data length.  If the length has been
		// exceeded a repeat is required.  Else the sample will be
		// advanced normally.
		////////////////////////////////////////////////////////////
		int pos = m->channelposition[i] >> 8;
		if (pos >= m->channellength[i])
		{
			////////////////////////////////////////////////////////////
			// A sample repeat has occured. The position becomes equal
			// to the dda increment.  To explain, the channel position
			// has already passed the sample length so sample data for
			// the channel comes from the first position 0. Because we
			// have now used the first position we dont want to use it
			// again next time, so the position gets incremented along.
			//
			// The channel length and noise pointer become equal to the
			// precalculated repeat length and repeat noise pointer. In
			// this way succesive repeats occur with the same data.
			//
			// The channel data now comes from the first position within
			// the noise data, this being the repeat start. The value is
			// modified by the VolumeMap array discussed elsewhere.
			////////////////////////////////////////////////////////////
			m->channelposition[i] = m->channelddainc[i];
			m->channellength[i] = m->channelreplen[i];
			m->channelnoise[i] = m->channelreppnt[i];
			sum += VolumeMap[m->channelvolume[i]][m->channelnoise[i][0]];
		}
		else
		{
			////////////////////////////////////////////////////////////
			// A sample repeat has not occured. The channel position
			// is incremented to allow for "frequencies" with sample
			// data. The current position (not the newly incremented
			// one) determines the single noise byte.  The VolumeMap
			// changes the channel byte before incrementing the sum.
			////////////////////////////////////////////////////////////
			m->channelposition[i] += m->channelddainc[i];
			sum += VolumeMap[m->channelvolume[i]][m->channelnoise[i][pos]];
		}
	}
	return sum;
}

void ModTick(moduleinfo * m)
{
	////////////////////////////////////////////////////////////
	// The dda increment is determined once every tick. "My,
	// God," cry the optimisers "That means 200 unneccesary
	// divisions per second!". "Piffle" says I.  The period
	// possibly changes every tick so recalculating the dda
	// increment is safer and neater. I find the speed loss
	// insignificant. Note the special handling for periods
	// equal to zero.
	////////////////////////////////////////////////////////////
	for (int i = 0; i < m->numchannels; i++)
	{
		if (m->channelperiod[i] != 0)
		{
			m->channelddainc[i] = m->periodmodifier[m->channelfntune[i]];
			m->channelddainc[i] /= m->channelperiod[i];
		}
		else
			m->channelddainc[i] = 0;
	}

	////////////////////////////////////////////////////////////
	// A tick-worth of bytes is produced and blitted thru the
	// sound device. The MixBuffer array stores 4096 prepared
	// bytes for one single SoundSend call. This improves the
	// overall performance. Note that upon exit any remaining
	// prepared bytes are also sent. The OutputMap array does
	// the sum division mentioned elsewhere.
	////////////////////////////////////////////////////////////
	int bytes = samplerate * 60 / m->beatsperminute / 24;
	for (i = 0; i < bytes; i++)
	{
		MixBuffer[i % 4096] = OutputMap[SumChannels(m)];
		if (i % 4096 == 4095)
			SoundSend(MixBuffer, 4096);
	}
	SoundSend(MixBuffer, bytes % 4096);

	////////////////////////////////////////////////////////////
	// Each channel has its period value slid. The period cant
	// slide outside the minimum/maximum range. Though usually
	// this range lies between 113--856 the SlideToNote effect
	// can change one end of the scale.
	////////////////////////////////////////////////////////////
	for (i = 0; i < m->numchannels; i++)
	{
		m->channelperiod[i] += m->channelperiodslide[i];
		if (m->channelperiod[i] > m->channelperiodmax[i])
			m->channelperiod[i] = m->channelperiodmax[i];
		if (m->channelperiod[i] < m->channelperiodmin[i])
			m->channelperiod[i] = m->channelperiodmin[i];
	}

	////////////////////////////////////////////////////////////
	// Each channel has its volume value slid. The volume cant
	// slide outside the minimum/maximum range. The bounds are
	// always 0 and 64. Why not 63? It would make the checking
	// one AND statement and make array lookup more efficient.
	// Yet another misdesign for the MOD format <grumble>.
	////////////////////////////////////////////////////////////
	for (i = 0; i < m->numchannels; i++)
	{
		m->channelvolume[i] += m->channelvolumeslide[i];
		if (m->channelvolume[i] > 64)
			m->channelvolume[i] = 64;
		if (m->channelvolume[i] < 0)
			m->channelvolume[i] = 0;
	}
}
