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

/**********************************************************************
Each line (also called division) has information which controls every
tick within the division. The ModTrack routine processes one division,
setting information for processing by ModTick. The division processing
occurs once and only once before all ticks are processed.
**********************************************************************/

void ModTrack(moduleinfo * m)
{
	////////////////////////////////////////////////////////////
	// The current division information is calculated from
	// scratch for each call. I could have stored the line
	// position and implemented special cases for all the
	// track, line and tick changes. However the overhead
	// isnt significant and this solution is easier to read.
	////////////////////////////////////////////////////////////
	unsigned char * notes;
	notes = m->rawpattern;
	notes += m->rawtable[m->songposition] * m->numchannels * 256;
	notes += m->lineposition * m->numchannels * 4;

	////////////////////////////////////////////////////////////
	// The notes pointer now references the first note. We
	// loop through all possible channels, advancing notes
	// each time to reference the next note. The variable i
	// will index various arrays which the contents of notes
	// modifies.
	////////////////////////////////////////////////////////////
	for (int i = 0; i < m->numchannels; i++, notes += 4)
	{
		////////////////////////////////////////////////////////////
		// The note information gets extracted from the four bytes
		// referenced by the notes pointer.  The effect command is
		// broken into the 4-bit type and the 8-bit argument.  The
		// sample number ranges from 1-31 while the C arrays index
		// from 0-30 requiring the subtraction. The period requires
		// no modification (thank god).
		////////////////////////////////////////////////////////////
		int sample = (notes[0] << 0L & 0x0f0) | (notes[2] >> 4L & 0x00f) - 1;
		int period = (notes[0] << 8L & 0xf00) | (notes[1] >> 0L & 0x0ff);
		int effect = (notes[2] & 0xf);
		int effarg = (notes[3]);

		////////////////////////////////////////////////////////////
		// This debugging info helps me greatly during the coding.
		// You might like including this debugging stuff anyway.
		// Its well synchronised with the output and provides
		// decent information about whats happening.
		////////////////////////////////////////////////////////////
#ifdef DEBUG
		printf("%2d %3d %3x%s", sample, period, effect << 8 | effarg,
			(i == m->numchannels-1) ? "\n" : " | ");
#endif

		////////////////////////////////////////////////////////////
		// When no sample is present but a period exists the old
		// sample gets retriggered. No sample is present when the
		// (decremented) sample value equals -1. A period is here
		// when the period number isnt 0.
		////////////////////////////////////////////////////////////
		if (sample == -1 && period != 0)
			sample = m->channelsample[i];

		////////////////////////////////////////////////////////////
		// When a sample is present but no period exists the old
		// period gets retriggered. No period is present when the
		// period value equals 0 (infinite frequency!). A sample
		// is present when the value isnt -1.
		////////////////////////////////////////////////////////////
		if (sample != -1 && period == 0)
			period = m->channelperiod[i];

		////////////////////////////////////////////////////////////
		// This next section implements the note-trigger code.
		// A trigger occurs when either sample or period have
		// meaningful values.  The previous two sections have
		// loaded meaningful values into both variables so I
		// just check the period value.
		//
		// Unfortunately effects can use the sample and period
		// slots for storing extra effect arguments. Currently
		// I know that effect 3 (PortamentoWithVolumeSlide) and
		// effect 5 (VibratoWithVolumeSlide) do this. Checking
		// for these values here prevents incorrect retriggers.
		////////////////////////////////////////////////////////////
		if (period != 0 && effect != 3 && effect != 5)
		{
			////////////////////////////////////////////////////////////
			// The default values are loaded into the current channel.
			// Note the period slide values are not modified here: by
			// design. The sample and period values are stored for any
			// later retriggers.  The repeat point is stored with the
			// absolute address rather than the relative offset. This
			// makes later mixing routines slightly simpler.
			////////////////////////////////////////////////////////////
			m->channelvolumeslide[i] = 0;
			m->channelposition[i] = 0;
			m->channelsample[i] = sample;
			m->channelperiod[i] = period;
			m->channelnoise[i] = m->samplenoise[sample];
			m->channelfntune[i] = m->samplefntune[sample];
			m->channelvolume[i] = m->samplevolume[sample];
			m->channellength[i] = m->samplelength[sample];
			m->channelreplen[i] = m->samplereplen[sample];
			m->channelreppnt[i] = m->samplenoise[sample];
			m->channelreppnt[i] += m->samplereppnt[sample];
		}

		////////////////////////////////////////////////////////////
		// Period slides occur for one division only. Retriggered
		// notes dont slide by default. This means with the start
		// of each division all period slides are halted. The ex-
		// ception is effect 5 (PortamentoWithVolumeSlide) which
		// allows the old period slide to continue.
		//
		// The period slide limits are also reset here. The code
		// was being repeated elsewhere, but should apply to all
		// retriggered notes.  The amiga hardware cannot slide a
		// period lower than 113 (B3) or higher than 856 (C1).
		////////////////////////////////////////////////////////////
		if (effect != 5)
		{
			m->channelperiodslide[i] = 0;
			m->channelperiodmin[i] = 113;
			m->channelperiodmax[i] = 856;
		}

		////////////////////////////////////////////////////////////
		// Here comes the gutsy part! All implemented effects are
		// implemented through one huge switch.  I tried function
		// arrays but didnt see their usefulness. The increase in
		// speed wasnt significant. The resulting code was fairly
		// ugly. Plus I needed to pass the effarg, period, sample
		// and moduleinfo pointer to each function. YUK!
		////////////////////////////////////////////////////////////
		switch (effect)
		{
			////////////////////////////////////////////////////////////
			// Effect 1 (Portamento Slide Up) will cause the sample
			// frequency to increase linearly during this up-coming
			// division. This means the period decreases linearly.
			////////////////////////////////////////////////////////////
			case 1:
				m->channelperiodslide[i] = -effarg;
				break;
			////////////////////////////////////////////////////////////
			// Effect 2 (Portamento Slide Down) will cause the sample
			// frequency to decrease linearly during this up-coming
			// division. This means the period increases linearly.
			////////////////////////////////////////////////////////////
			case 2:
				m->channelperiodslide[i] = effarg;
				break;
			////////////////////////////////////////////////////////////
			// Effect 3 (Portamento Slide To Note) will cause the
			// sample to slide up or down. The slide direction is
			// determined from the supplied period value. Sliding
			// wont pass through the supplied period value or the
			// aforementioned minimum/maximum Amiga limits.
			////////////////////////////////////////////////////////////
			case 3:
				if (m->channelperiod[i] < period)
				{
					m->channelperiodslide[i] = effarg;
					m->channelperiodmax[i] = (period > 856) ? 856 : period;
				}
				else
				{
					m->channelperiodslide[i] = -effarg;
					m->channelperiodmin[i] = (period < 113) ? 113 : period;
				}
				break;
			////////////////////////////////////////////////////////////
			// The following three effects are all very similarly
			// behaved.  Effect 5 (Portamento With Volume Slide),
			// effect 6 (Vibrato With Volume Slide) and effect 10
			// (Volume Slide) differ with what they don't change.
			// For instance effect 5 doesnt change period slides.
			//
			// The volume slide is determined from the two nybbles
			// from effarg. The high nybble means slide the volume
			// up, the low nybble means slide the volume down. The
			// two effects 5 & 6 technically should fail when both
			// are non-zero. Effect 10 takes the high nybble first
			// which is more flexible so all three work like this.
			////////////////////////////////////////////////////////////
			case 5:
			case 6:
			case 10:
				if (effarg & 0xf0)
					m->channelvolumeslide[i] = +(effarg >> 4);
				else
					m->channelvolumeslide[i] = -(effarg & 0x0f);
				break;
			////////////////////////////////////////////////////////////
			// Effect 9 (Set Sample Offset) modifies the samples start
			// position. For triggered notes (i.e. triggered this same
			// division) this simply means increasing the channelnoise
			// pointer. The value first gets multiplied by 256, as the
			// MOD specifications state. This then gives the offset in
			// words so another multiplication of 2 is required.
			//
			// Untriggered notes are handled specially. Specifically an
			// untriggered note gets retriggered here without changing
			// the channel volume. -- I havent implemented this yet --
			////////////////////////////////////////////////////////////
			case 9:
				m->channelnoise[i] += effarg * 512;
				break;
			////////////////////////////////////////////////////////////
			// Effect 11 (Position Jump) will change the current song
			// position. It resets the line position to the beginning.
			// Note this whole function is implemented inside one loop
			// so I load -1 instead of 0. The loops postbody increment
			// will correctly place the lineposition at 0.
			////////////////////////////////////////////////////////////
			case 11:
				m->songposition = effarg;
				m->lineposition = -1;
				break;
			////////////////////////////////////////////////////////////
			// Effect 12 (Set Volume) will change the current channels
			// volume. Theres some confusion as to whether this volume
			// change carries across future notes. Anybody know?
			////////////////////////////////////////////////////////////
			case 12:
				m->channelvolume[i] = effarg;
				break;
			////////////////////////////////////////////////////////////
			// Effect 13 (Pattern Break) will stop the current track,
			// advance the song position, then move the line position
			// to the specified effect argument. Just to be difficult
			// the effarg nybbles are stored in bcd instead of simple
			// hex. Sigh.... The new line position is stored with the
			// -1 decrement discussed in effect 11.
			////////////////////////////////////////////////////////////
			case 13:
				m->songposition++;
				m->lineposition = (effarg >> 4) * 10;
				m->lineposition = (effarg & 0x0f) + m->lineposition - 1;
				break;
			////////////////////////////////////////////////////////////
			// Effect 14 (Miscellaneous) stores all the effects they
			// couldnt shove into the 16 slots. It requires further
			// switching based upon the effect argument.  IMHO they
			// should have used an 8-bit period value which indexed
			// a 256 entry frequency table. Then had 16-bit effects
			// which would make the whole format neater. But I cant
			// rock the boat now...
			////////////////////////////////////////////////////////////
			case 14:
				////////////////////////////////////////////////////////////
				// The high nybble of effect 14 contains the different
				// subeffects. The lower nybble contains the subeffect
				// argument.
				////////////////////////////////////////////////////////////
				switch (effarg >> 4)
				{
					////////////////////////////////////////////////////////
					// Effect 14-1 (Fineslide Up) causes the current
					// channels period to change.  It occurs now and
					// only once. It isnt really sliding, but that's
					// what it's called. Remember moving frequencies
					// up moves periods down. The period mustnt slide
					// below B3 (113).
					////////////////////////////////////////////////////////
					case 1:
						m->channelperiod[i] -= effarg & 0x0f;
						if (m->channelperiod[i] < 113)
							m->channelperiod[i] = 113;
						break;
					////////////////////////////////////////////////////////
					// Effect 14-2 (Fineslide Down) causes the current
					// channels period to change.  It occurs now and
					// only once. It isnt really sliding, but that's
					// what it's called. Remember moving frequencies
					// down moves periods up. The period mustnt slide
					// above C1 (856).
					////////////////////////////////////////////////////////
					case 2:
						m->channelperiod[i] += effarg & 0x0f;
						if (m->channelperiod[i] > 856)
							m->channelperiod[i] = 856;
						break;
					////////////////////////////////////////////////////////
					// Effect 14-5 (Set Finetune) will change the current
					// channels finetune value. It defaults to the sample
					// finetune value given when first triggered.
					////////////////////////////////////////////////////////
					case 5:
						m->channelfntune[i] = effarg & 0x0f;
						break;
					////////////////////////////////////////////////////////
					// Effect 14-10 (Fine Volume Slide Up) changes the
					// current channels volume. It occurs now and only
					// once; it isnt really sliding. The volume mustnt
					// slide above the maximum volume 64.
					////////////////////////////////////////////////////////
					case 10:
						m->channelvolume[i] += effarg & 0x0f;
						if (m->channelperiod[i] > 64)
							m->channelperiod[i] = 64;
						break;
					////////////////////////////////////////////////////////
					// Effect 14-11 (Fine Volume Slide Down) changes the
					// current channels volume. It occurs now and only
					// once; it isnt really sliding. The volume mustnt
					// slide below the minimum volume 0.
					////////////////////////////////////////////////////////
					case 11:
						m->channelvolume[i] -= effarg & 0x0f;
						if (m->channelperiod[i] < 0)
							m->channelperiod[i] = 0;
						break;
				}
				break;
			////////////////////////////////////////////////////////////
			// Effect 15 (Set Speed) controls the rate at which new
			// divisions are invoked. There are two speed values in
			// MOD songs,  ticks-per-division and beats-per-minute.
			// One beat equals four divisions.
			//
			// Speed values of zero technically mean there arent any
			// ticks per division. In reality theres always one tick
			// per division but tick-based effects (ie period slide)
			// dont work. I incorrectly treat speed 0 as speed 1.
			//
			// Speed values between 1-31 set the ticks per division.
			// Higher numbers cause period/volume slides to happen
			// faster without changing how often divisions retrigger.
			// This number gets used inside ModPlay.
			//
			// Speed values between 32-255 set the beats per minute.
			// Lower numbers slow down how often division retrigger
			// and also affect period/volume slides. This number gets
			// used inside ModTick.
			////////////////////////////////////////////////////////////
			case 15:
				if (effarg == 0)
					m->ticksperline = 1;
				else if (effarg < 32)
					m->ticksperline = effarg;
				else
					m->beatsperminute = effarg;
				break;
		}
	}
}
