#include <stdio.h>
#include <string.h>
#include "GlobHdr.h"
#include "Mem.h"
#include "FSongCtrl.h"
#include "Effects.h"
#include "FChannel.h"
#include "FController.h"
#include "FNotes.h"
#include "FTables.h"

#define ramp_pshift	3
#define ramp_parts	(1<<ramp_pshift) // 8
#define ramp_tshift	16
#define ramp_time	(1<<ramp_tshift) // = 256 us

static void Song_ClearAllNotes(ISong* pSong)
{
	// Reset all channels note pointers to zero
	Channels_ClearNotes(&pSong->channels[0], song_max_channels);
	Notes_ClearAllNotes(&pSong->Notes);
}

static void Song_RefreshNotes(ISong* pSong)
{
	// Reset all channels note pointers to zero
	Channels_ClearNotes(&pSong->channels[0], song_max_channels);
	Notes_Refresh(&pSong->Notes);
}

/*
 * Clear contollers and set limits. For default values, use set defaults
 */
static void Song_InitControllers(ISong* pSong)
{
	Channel* pChannel;
	int i;

	// Global volume controller
	memset(&pSong->Volume, 0, sizeof(pSong->Volume));
	pSong->Volume.value.max = 256;

	// Tempo controller
	memset(&pSong->Tempo, 0, sizeof(pSong->Tempo));
	pSong->Tempo.value.min = pSong->Hdr.MinTempo;
	pSong->Tempo.value.max = pSong->Hdr.MaxTempo;

	// Speed controller
	memset(&pSong->Speed, 0, sizeof(pSong->Speed));
	pSong->Speed.value.min = pSong->Hdr.MinSpeed;
	pSong->Speed.value.max = pSong->Hdr.MaxSpeed;

	// Clear channels info
	memset(&pSong->channels, 0, sizeof(pSong->channels));

	// Limit pitch range (in 1/256 semitones)
	if (pSong->Hdr.MinPitch)
		pSong->Notes.MinPitch = pSong->Hdr.MinPitch << 8;
	else
		pSong->Notes.MinPitch = 1 << 8;
	if (pSong->Hdr.MaxPitch)
		pSong->Notes.MaxPitch = pSong->Hdr.MaxPitch << 8;
	else
		pSong->Notes.MaxPitch = 232 << 8;

	// Set Channels Volume, Panning, vibrato types
	for ( pChannel = &pSong->channels[0]
	    , i = 0
	    ; i < pSong->Hdr.Channels
	    ; i++, pChannel++
	    )
	{
		pChannel->id = i;
		// Note volume controller
		pChannel->note_volume.value.max = 256;
		// Column volume controller
		pChannel->channel_volume.value.max = 256;
		// Panning controller
		pChannel->panning.value.max = 256;
		// Limit pitch range, for frac allow high or negative values (for 669)
		if (pSong->Hdr.Flags & Song_Flag_Linear)
		{
			pChannel->note_pitch.value.min = pSong->Notes.MinPitch;
			if (pSong->Hdr.Flags & Song_Flag_CutOnHighPitch)
				pChannel->note_pitch.value.max = 0x7F000000; // allow it to go over limit
			else
				pChannel->note_pitch.value.max = pSong->Notes.MaxPitch;
		}
		else if (pSong->Hdr.Flags & Song_Flag_PitchAsFraction)
		{
			if (pSong->Hdr.MinPitch)
				pChannel->note_pitch.value.min = Convert_ToneToFrac(pSong->Notes.MinPitch);
			else
				pChannel->note_pitch.value.min = (int32_t) 0x81000000; // allow negative pitch
			if (pSong->Hdr.Flags & Song_Flag_CutOnHighPitch)
				pChannel->note_pitch.value.max = 0x7F000000; // allow it to go over limit
			else if (pSong->Hdr.MaxPitch)
				pChannel->note_pitch.value.max = Convert_ToneToFrac(pSong->Notes.MaxPitch);
			else
				pChannel->note_pitch.value.max = 0x7F000000; // allow it to go over limit
		}
		else
		{
		 	// Period, inverted limits
		 	pChannel->note_pitch.value.max = Convert_ToneToPeriod(pSong->Notes.MinPitch);
			if (pSong->Hdr.Flags & Song_Flag_CutOnHighPitch)
				pChannel->note_pitch.value.min = 0x1; // allow it to go over limit
			else
				pChannel->note_pitch.value.min = Convert_ToneToPeriod(pSong->Notes.MaxPitch);
		}
	}
}

void Song_SetPreAmp(ISong* pSong)
{
	if (!pSong->ConfigAmp)
	{
		pSong->PreAmp = (1 << 16);
		if (pSong->Seq.maxstreams)
			pSong->PreAmp /= pSong->Seq.maxstreams;
	}
	else if (pSong->ConfigAmp == 1)
		pSong->PreAmp = Get_PreAmp(pSong->Seq.maxstreams);
	else
		pSong->PreAmp = pSong->ConfigAmp;
}

static void Song_SetDefaults(ISong* pSong)
{
	const SubSong* pSubSong = (pSong->PlayRange.Pattern < 0) ? pSong->SubSongPtr : &pSong->Hdr.BaseSong;
	int i;

	// Transfer song global info.
	// Do not touch tempo/speed unless at the beginning of very first section.
	// The author is supposed to set its own tempo/speed for each section
	// but when he forgets, I noticed that it is usally better to keep
	// the values used in the previous section than attempting to reset tempo.
	// Note also that we do not use the sequence Tempos, we may be called
	// by the function which builds them.
	if (!pSong->SeqPos)
	{
		pSong->Tempo.value.start =
		pSong->Tempo.value.final = pSubSong->Defaults.Tempo;
		pSong->Speed.value.start =
		pSong->Speed.value.final = pSubSong->Defaults.Speed;
	}

	pSong->Volume.value.start =
	pSong->Volume.value.final = pSubSong->Defaults.Volume;

	// Set Channels Volume & Panning
	Channel* pChannel = &pSong->channels[0];
	const ChannelDefault* pDef = &pSubSong->Defaults.ChDef[0];

	for (i = pSong->Hdr.Channels; i > 0; i--, pChannel++, pDef++)
	{
		// Set muted channels
		if (!(pSong->Config & glb_flag_IgnoreMuteInfoOnLoading))
			if (pDef->Mute) pChannel->flags |= ch_flags_mute;

		// Volume, panning
		pChannel->channel_volume.value.start =
		pChannel->channel_volume.value.final = pDef->Volume;
		pChannel->panning.value.start =
		pChannel->panning.value.final = pDef->Panning;
		// Vibratos
		pChannel->note_volume.vibrato_type =
		pChannel->channel_volume.vibrato_type = pSubSong->Defaults.TremoloType;
		pChannel->note_volume.vibrato_speed =
		pChannel->channel_volume.vibrato_speed = pSubSong->Defaults.TremoloSpeed;
		pChannel->note_volume.vibrato_strength =
		pChannel->channel_volume.vibrato_strength = pSubSong->Defaults.TremoloStrength;
		pChannel->panning.vibrato_type = pSubSong->Defaults.PanbrelloType;
		pChannel->panning.vibrato_speed = pSubSong->Defaults.PanbrelloSpeed;
		pChannel->panning.vibrato_strength = pSubSong->Defaults.PanbrelloStrength;
		pChannel->note_pitch.vibrato_type = pSubSong->Defaults.VibratoType;
		pChannel->note_pitch.vibrato_speed = pSubSong->Defaults.VibratoSpeed;
		pChannel->note_pitch.vibrato_strength = pSubSong->Defaults.VibratoStrength;
		pChannel->note_volume.vibrato_depth =
		pChannel->channel_volume.vibrato_depth =
		pChannel->panning.vibrato_depth =
		pChannel->note_pitch.vibrato_depth = 0;
		// Autoslides
		pChannel->note_volume.autoslide =
		pChannel->channel_volume.autoslide =
		pChannel->panning.autoslide =
		pChannel->note_pitch.autoslide = cmd_up;
		// Tremor
		pChannel->tremor_ontime =
		pChannel->tremor_offtime = 0;
		pChannel->tremor_pos = (pSubSong->Defaults.TremorOn) ? 0x80 : 0; // On phase
	}
}

static void Song_ReadCurrentPattern(ISong* pSong, bool resetLoops)
{
	const SubSong* pSubSong = pSong->SubSongPtr;
	int32_t pos = pSong->SeqPos;
	int i, patt_nr;


	if (pSong->PlayRange.Pattern >= 0)
	{
		// Pattern play
		pSong->TimeIndex = 0;
		patt_nr = pSong->PlayRange.Pattern;
	}
	else
	{
		// Sequence play, skip dummy entries
		while(true)
		{
			if (pos >= pSubSong->SeqLen)
				pos = 0;
			// Test dummy entries
			if (pSubSong->pSeqs[pos] < -1)
				pos++;
			else if (pSubSong->pSeqs[pos] == -1)
			{
				if (pSong->Config & glb_flag_IgnoreSeqEndMark)
					 pos++;
				else pos = 0;
			}
			else break;
		}
		pSong->SeqPos = pos;

		// Read time index
		pSong->TimeIndex = pSubSong->pTimeIndexes[pSong->SeqPos];

		patt_nr = pSubSong->pSeqs[pSong->SeqPos];
	}

	// Read pattern info for given pattern nr
	const Pattern* pPat = &pSong->patterns[patt_nr];
	pSong->Pattern.Rows = pPat->Rows;
	pSong->Pattern.Ptr = pPat->Ptr;

	// Reset loops info
	pSong->LoopsCount = 0;
	if (resetLoops)
	{
		ILoop def = {0,0,-1}; // count = 0, pos = 0, last completed loop = -1
		for (i = 0; i < swrk_max_loops; i++)
			pSong->loops[i] = def;
	}
	else
	{
		for (i = 0; i < swrk_max_loops; i++)
			pSong->loops[i].last = -1;
	}

	memset(pSong->MarkRow, 0, sizeof(pSong->MarkRow));
}

static void Song_ReadRow(ISong* pSong)
{
	Channel* pChannel;
	int val, i;

	pSong->MarkRow[pSong->RowPos] = 1;

	// Step 0 - Set row ptr from pattern ptr and row pos

	const uint8_t* ptr = pSong->Pattern.Ptr;

	for (i = 0; i < pSong->RowPos; i++)
		ptr += 2 + ptr[0] + (ptr[1] << 8);

	pSong->RowPtr = ptr;

	// Step 1 - Reset effects ptr

	// Reset row repeats
	pSong->Repeats = 1;
	// Find end of row
	ptr += 2;
	const uint8_t* ptr_end = ptr + ptr[-2] + (ptr[-1] << 8);
	// Store start of global effects
	if (ptr >= ptr_end)
	{
		pSong->EffectsPtr = 0;
	}
	else
	{
		pSong->EffectsPtr = ptr;
		// Skip global effects
		while((val = *ptr++) != 0)
		{
			ptr += 2*(val >> 4);
		}
	}

	for ( pChannel = &pSong->channels[0]
	    , i = pSong->Hdr.Channels
	    ; i > 0
	    ; i--, pChannel++
	    )
	{
		pChannel->p_new_effects = NULL;
		pChannel->delay =
		pChannel->new_note_id =
		pChannel->new_inst_id = 0;
		pChannel->flags &= ch_flags_reset;
		if (pSong->Hdr.Flags & Song_Flag_LimitTremorToRow)
			pChannel->flags &= ~ch_flags_zero_volume;
	}

	// Step 2 - Assign effects ptr and delays from row ptr
	while (ptr < ptr_end)
	{
		// Read column number
		val = *ptr++;
		pChannel = &pSong->channels[val & 0x3f];
		if (val & 0x40)
			pChannel->new_note_id = *ptr++;
		if (val & 0x80)
			pChannel->new_inst_id = *ptr++;
		pChannel->p_new_effects = ptr;
		// Find end of effects list
		while((val = *ptr++) != 0)
		{
			int ctrl = val & 0xf;
			int count = val >> 4;

			// Locate note portamento and note delay
			switch (ctrl)
			{
				case 0: // Instrument controller
				{
					// Find delay effect
					while (--count >= 0)
					{
						int eff = *ptr++;
						val = *ptr++;
						if ((eff & 0xf0) == cmd_sample_note_delay)
							pChannel->delay = val + ((eff & 0xf) << 8);
 					}
				}
				break;
				case 1: // Pitch controller
				{
					// find portamento effect
					while (--count >= 0)
					{
						int eff = *ptr;
						ptr += 2;
						if ((eff & 0xf0) == cmd_portamento)
							pChannel->flags |= ch_flags_portamento;
					}
				}
				break;
				case 5: // Pitch controller 2
				{
					// find auto portamento effect
					while (--count >= 0)
					{
						int eff = *ptr;
						ptr += 2;
						if ((eff & 0xf0) == (cmd_pitch_auto_portamento & 0xf0))
							pChannel->flags |= ch_flags_portamento;
					}
				}
				break;
				default:
					ptr += 2*count;
			}
		}
	}
}

static void Process_GlbEffects(ISong* pSong)
{
	// Step 1 - Prepare controllers

	// Reset non permanent controllers variations
	pSong->Volume.value.delta =
	pSong->Tempo.value.delta =
	pSong->Speed.value.delta = 0;

	// Reset frames to 0 on row start due to speed effects
	if (pSong->FrameExtCount == 0)
		pSong->Frames = 0;

	// Step 2 - Process Effects

	const uint8_t* ptr = pSong->EffectsPtr;

	if (ptr)
	{
		int val;

		while ((val = *ptr++) != 0)
		{
			int count = val >> 4;
			int ctrl = val & 0xf;
			int eff;

			switch(ctrl)
			{
				case ctrls_glb_tempo: // Tempo controller
				{
					ptr = TempoCtrl
							(&pSong->Tempo
							, pSong
							, pSong->FrameExtCount
							, count
							, ptr
							);
				}
				break;
				case ctrls_glb_frames: // Frames controller
				{
					while (--count >= 0)
					{
						eff = *ptr++;
						val = *ptr++;
						val += (eff & 0xf) << 8;

						// Only on very first frame of row
						if (pSong->FrameExtCount != 0)
							continue;

						switch(eff & 0xf0)
						{
							case gcmd_frames_set:
							{
								if (val > 0)
								{
									pSong->Speed.value.start =
									pSong->Speed.value.final = val;
								}
							}
							break;
							case gcmd_frames_up:
							{
								pSong->Speed.value.start += val;
							}
							break;
							case gcmd_frames_down:
							{
								pSong->Speed.value.start -= val;
							}
							break;
							case gcmd_frames_repeat:
							{
								pSong->Repeats += val;
							}
							break;
							case gcmd_frames_delay:
							{
								pSong->Frames += val;
							}
							break;
						}
					}
				}
				break;
				case ctrls_glb_volume: // Volume controller
				{
					ptr = VolumeCtrl
							(&pSong->Volume
							, NULL
							, pSong->FrameExtCount
							, count
							, ptr
							);
				}
				break;
				default:
					ptr += 2*count;
			}
		}
	}

	// Step 3 - Apply Effects Results

	// Step 3.1 - Apply Tempo
	Controller* pCtrl = &pSong->Tempo;
	int tempo;

	if (pSong->Status & swrk_status_tempoisbpm)
	{
		const int BPM_Dividers[] =
			{ 24 // 60 sec / 2.5 Hz
			,  3 // 60 sec / 20 Hz
			, 15 // 60 sec / 4 Hz
			};

		// BPM mode
		pCtrl->value.min = 1;
		pCtrl->value.max = 256;
		tempo = Controller_CheckValue(pCtrl);
		if (pSong->TicksPerBeat == 0)
			 tempo *= 8;
		else tempo *= pSong->TicksPerBeat;
		tempo = tempo / BPM_Dividers[pSong->Hdr.TempoBase];
	}
	else
	{
		// Tempo Mode
		pCtrl->value.min = pSong->Hdr.MinTempo;
		pCtrl->value.max = pSong->Hdr.MaxTempo;
		tempo = Controller_CheckValue(pCtrl);
	}
	pSong->CurrentTempo = tempo;
	pSong->TempoTime = Get_TempoTime(tempo, pSong->Hdr.TempoBase);
	if (pSong->TempoTime > ramp_time*ramp_parts*3)
		 pSong->Status &= ~swrk_status_noramp;
	else pSong->Status |= swrk_status_noramp;

	// Step 3.2 - Apply Speed
	pSong->CurrentSpeed = Controller_CheckValue(&pSong->Speed);
	// add speed to frames on row start
	if (pSong->FrameExtCount == 0)
		pSong->Frames += pSong->CurrentSpeed;

	// Step 3.3 - Apply Volume
	pSong->Notes.Volume = Controller_CheckValue(&pSong->Volume);
}

static void Song_PreProcessRow(ISong* pSong)
{
	const SubSong* pSubSong = pSong->SubSongPtr;
	Channel* pChannel;

	//---------
	// Step 1.  Backup old note info for Volume Ramping
	uint32_t count = pSong->Notes.ActiveNotes;
	Note** ppNote = &pSong->Notes.ActiveNotesArray[0];
	for (;count > 0; count--)
	{
		Note* pNote = *ppNote++;
		// Only volume may change in this phase
		// for notes that are going to be cut
		pNote->old_volume = pNote->new_volume;
		pNote->old_panning = pNote->new_panning;
		pNote->old_frequency = pNote->new_frequency;
	}

	//---------
	// Step 2.  PreProcess a new pattern row

	if (!pSong->FrameExtCount)
	{
		// Step 2.1 Restore defaults?
		// - when song loops to start of section, all defaults
		// - saved tempos/speed at entry of each order, to help SetPosition
		//   keep the correct timing,

		if (!pSong->RowPos && (pSong->PlayRange.Pattern < 0))
		{
			if (pSong->SectionsPtr
			&&  (pSong->SectionsPtr[pSubSong->pSecIndexes[pSong->SeqPos]-1].StartPos == pSong->SeqPos))
			{
				Song_SetDefaults(pSong);
				Song_ClearAllNotes(pSong);
			}

			uint32_t tempo = pSubSong->pSecTempos[pSong->SeqPos];
			if (tempo)
			{
				pSong->Tempo.value.start =
				pSong->Tempo.value.final = tempo >> 8;
				pSong->Speed.value.start =
				pSong->Speed.value.final = tempo & 0xff;
			}
		}

		// Step 2.2 Decode the new pattern row

		Song_ReadRow(pSong);
	}

	//---------
	// Step 3.  PreProcess channels effects

	for ( count = pSong->Hdr.Channels
	    , pChannel = &pSong->channels[0]
	    ; count > 0
	    ; count --, pChannel++
	    )
	{
		Channel_PreProcess(pSong, pChannel);
	}
}

static void Song_ProcessRow(ISong* pSong)
{
	Channel* pChannel;
	int count;

	//---------
	// Step 1.  Remove all playing notes terminated by the mixer
	Song_RefreshNotes(pSong);

	//---------
	// Step 2.  Process globals effects

	Process_GlbEffects(pSong);

	//---------
	// Step 3.  Process channels effects

	for ( count = pSong->Hdr.Channels
	    , pChannel = &pSong->channels[0]
	    ; count > 0
	    ; count --, pChannel++
	    )
	{
		Channel_Process(pSong, pChannel);
	}

	//---------
	// Step 4.  Process notes and build note list

	_kernel_swi_regs r;
	r.r[0] = 253;
	CSeq_Configure(Glb, &r);
	if (r.r[1] & seq_status_SMLASupport)
		pSong->Notes.MixFrequency = CSeq_GetPlayingFrequency(Glb);
	else
		pSong->Notes.MixFrequency = 0;

	Notes_BuildActiveNotesList(&pSong->Notes);

	//---------
	// Step 5.  Update new notes for Volume Ramping

	for ( count = pSong->Hdr.Channels
	    , pChannel = &pSong->channels[0]
	    ; count > 0
	    ; count --, pChannel++
	    )
	{
		if (pChannel->flags & ch_flags_triggernote)
		{
			pChannel->flags &= ~ch_flags_triggernote;
			Note* pNote = pChannel->pnote;

			if (pNote)
			{
				pNote->old_panning = pNote->panning;
				pNote->old_volume = 0;
				pNote->old_frequency = pNote->frequency;
			}
		}
	}

	//---------
	// Step 6.  Set PreAmp

	if (pSong->Seq.maxstreams < pSong->Notes.ActiveNotes)
	{
		pSong->Seq.maxstreams = pSong->Notes.ActiveNotes;
		Song_SetPreAmp(pSong);

		CSeq_SetMixScale(Glb);
	}

	//---------
	// Step 7.  Update stream panning and volume

	count = pSong->Notes.ActiveNotes;
	Note** ppNote = &pSong->Notes.ActiveNotesArray[0];

	if (count > 0)
	{
		// Amplification
		uint32_t svolume = (pSong->PreAmp * pSong->Seq.volume) >> 8;
		svolume = (svolume * pSong->Hdr.VolumeShift) >> 8;

		// Use recommanded scale volume ?
		if (pSong->Hdr.Compatibility & song_compat_usescalevolume)
			svolume = (svolume * pSong->Hdr.pGlb->emulvolume) >> 8;

		for (;count > 0; count--)
		{
			Note* pNote = *ppNote++;
			// Update new values.
			// Update streaming values for when ramping is disabled.
			// Update panning, beware we cannot ramp to surround.
			if ((pNote->old_panning > 256) || (pNote->panning > 256))
				pNote->old_panning = pNote->panning;
			pNote->stream.panning =
			pNote->new_panning = pNote->panning;
			// Update volume
			uint32_t volume = MulH16(svolume, pNote->volume);
			// Take into account muted channels (also beware of ramping)
			if (pNote->pchannel->flags & ch_flags_mute)
				pNote->old_volume = volume = 0;
			pNote->stream.volume =
			pNote->new_volume = volume;
			// Update frequency
			pNote->stream.frequency =
			pNote->new_frequency = pNote->frequency;
		}
	}
}

static void Song_NextFrame(ISong* pSong)
{
	const SubSong* pSubSong = pSong->SubSongPtr;

	pSong->SeqsChange &= ~(swrk_change_row|swrk_change_pattern);

	// Step 0 - New frame - Update row only if completed
	pSong->FrameExtCount++;
	pSong->FrameCount++;
	if (pSong->FrameCount < pSong->Frames)
		return;

	pSong->FrameCount = 0;
	pSong->RepeatCount++;
	if (pSong->RepeatCount < pSong->Repeats)
		return;

	pSong->RepeatCount = 0;

	// Step 1 - New row must be decided

	pSong->FrameExtCount = 0;
	pSong->SeqsChange |= swrk_change_row;

	// Step 1.a - Get restart pos

	uint32_t restartpos = 0;

	if (pSong->PlayRange.Pattern < 0)
	{
		if (pSong->PlayRange.Start >= 0)
			restartpos = pSong->PlayRange.Start;
		else
		{
			restartpos = pSubSong->RestartPos;

			if (pSong->Config & glb_flag_IgnoreSeqEndLoop)
			{
				if (restartpos >= (pSubSong->SeqLen - 1))
					restartpos = 0;
			}
		}
	}

	// Step 1.b - Process row effects to find out new row &| new seq pos

	int rowpos = pSong->RowPos;
	int seqpos = pSong->SeqPos;

	// No active loop
	pSong->LoopsActiveNr = -1;

	// Find effects list
	const uint8_t* ptr = pSong->EffectsPtr;
	if (ptr)
	{
		int ctrl, count, eff, val;

		while ((ctrl = *ptr++) != 0)
		{
			count = ctrl >> 4;
			if ((ctrl & 0xf) == ctrls_glb_pos)
			{
				while (--count >= 0)
				{
					eff = *ptr++;
					val = *ptr++;
					val += (eff & 0xf) << 8;

					switch(eff & 0xf0)
					{
						case gcmd_pos_loop:
						{
							uint32_t lcount = val >> 8;
							val &= 0xff;
							ILoop* ploop = &pSong->LoopsInfoPtr[val];
							// if counter = 0, set loop start pos
							if (!lcount)
							{
								if (ploop->row != pSong->RowPos)
								{
									ploop->last = -1;
									ploop->row = pSong->RowPos;
									ploop->count = 0;
								}
							}
							else
							{
								// val <> 0, end pos, loop is active ?
								if (ploop->count > 0)
								{
									// End pos active, continue looping?
									if (--ploop->count)
									{
										pSong->LoopsActiveNr = val;
										break;
									}
									// No, some trackers move loop start to next row
									if (pSong->Hdr.Flags & Song_Flag_MoveLoopAfterLoop)
									{
										ploop->row = pSong->RowPos + 1;
										break;
									}
									// Register last completed loop for infinite loops detection
									ploop->last = pSong->RowPos;
								}
								else
								{
									// No active loop, activate loop?
									// Current loop start must not be after current line
									if (ploop->row > pSong->RowPos)
										break;
									// Activate loop
									ploop->count = lcount;
									pSong->LoopsActiveNr = val;
								}
							}
						}
						break;
						case gcmd_pos_jump:
						{
							// Are we on last seq pos?
							if ((pSong->PlayRange.Pattern < 0)
							&&  (val == (pSubSong->SeqLen -1))
							&&  (val == pSong->SeqPos))
							{
								// Yes, we may possibly ignore jumps to last pos
								if (pSong->Config & glb_flag_IgnoreSeqEndLoop)
								{
									if (restartpos == (pSubSong->SeqLen -1))
										val = 0;
									else
										val = restartpos;
								}
							}
							// Move to first row in new pattern
							// unless row already forced by another command
							seqpos = val;
							if (!(pSong->SeqsChange & swrk_change_pattern))
								rowpos = -1; // cf goto NoCmd
							pSong->SeqsChange |= swrk_change_pattern;
						}
						break;
						case gcmd_pos_break:
						{
							rowpos = val - 1; // cf goto NoCmd
							// Move to next order
							// unless order already forced by another command
							if (!(pSong->SeqsChange & swrk_change_pattern))
								seqpos++;
							pSong->SeqsChange |= swrk_change_pattern;
						}
						break;
						case gcmd_pos_linejump:
						{
							// Just some insurance to avoid infinite loops
							// in song build length
							if (pSong->RowPos >= val)
								pSong->SeqsChange |= swrk_change_pattern;
							rowpos = val - 1; // cf goto NoCmd
						}
						break;
					}
				}
			}
			else
				ptr += 2*count;
		}
	}

	rowpos++;

	// Step 1.c - Process active loop

	// Active loop in current pattern has priority
	if (pSong->LoopsActiveNr != -1)
	{
		// get loop info
		ILoop* ploop = &pSong->LoopsInfoPtr[pSong->LoopsActiveNr];

		// Infinite loop ? (i.e two loops in channel with same loop start)
		// Assume so at first and treat as pattern change
		pSong->SeqsChange |= swrk_change_pattern;
		// If last completed loop in (-1, current row)
		// simple loop, treat as jump within pattern
		if ((ploop->last < 0)
		||  (ploop->last == pSong->RowPos))
			pSong->SeqsChange &= ~swrk_change_pattern;

		// Ignore loops in last seq pos?
		if ((pSong->PlayRange.Pattern < 0)
		||  (pSong->SeqPos != (pSubSong->SeqLen -1))
		|| !(pSong->Config & glb_flag_IgnoreSeqEndLoop))
		{
			// During song duration calculation
			// limit loop count to avoid near endless loops
			if ((pSong->Status & swrk_status_playing)
			||  (++pSong->LoopsCount < 512))
			{
				// Jump to loop within current pattern
				rowpos = ploop->row; // Loop start pos
				seqpos = pSong->SeqPos; // Reload current seq pos
			}
		}
	}

	// Step d - New seq pos - set new pattern info

Update_SeqInfo:
	// If we flagged a change from sequence position but we are still
	// on same position on a yet unplayed row ignore it
	if ((pSong->SeqsChange & swrk_change_pattern)
	&&  (pSong->SeqPos == seqpos)
	&&  (rowpos < pSong->Pattern.Rows)
	&&  (pSong->MarkRow[rowpos] == 0))
		pSong->SeqsChange &= ~swrk_change_pattern;

	// Still in same seq pos ?
	if ((seqpos != pSong->SeqPos)
	||  (pSong->SeqsChange & swrk_change_pattern))
	{
		// Keep pos within sequence limits
		if (seqpos < 0) seqpos = 0;
		// Update position when playing a sequence
		if (pSong->PlayRange.Pattern < 0)
		{
			// Playing sequence
			if (pSong->PlayRange.Start >= 0)
			{
				// Playing range
				if (seqpos > pSong->PlayRange.Last)
					seqpos = pSong->PlayRange.Start;
			}
			else
			{
				// Normal play
				if (seqpos >= pSubSong->SeqLen) seqpos = restartpos;

				// Use ignore restart pos flag as always jump back to start of section
				if ((pSong->Config & glb_flag_IgnoreSeqRestartPos)
				&&  (pSubSong->pTimeIndexes[seqpos] <= pSubSong->pTimeIndexes[pSong->SeqPos]))
					seqpos = pSong->SectionsPtr[pSubSong->pSecIndexes[pSong->SeqPos] - 1].StartPos;
			}
			pSong->SeqPos = seqpos;
		}

		pSong->SeqsChange |= swrk_change_pattern;
		// Read new pattern info
		Song_ReadCurrentPattern(pSong, (pSong->Hdr.Flags & Song_Flag_ResetsLoopsOnPatternChange) != 0);
	}

	// Step e - New seq pos if row off limits - set new pattern info

	// Test if row pos within rows limits
	if (rowpos >= pSong->Pattern.Rows)
	{
		// Move to next seq pos
		rowpos = 0;
		seqpos++;
		goto Update_SeqInfo;
	}

	// Step f - Set new row info, store frame info
 	pSong->RowPos = rowpos;
}

/**
 * In  - duration, planned fill duration in 1/256 us
 *
 * Out - fill duration limited by stream change
 */

int Song_Changer(ISong* pSong, int duration)
{
	int time, ramp;

	// Check if song is playing
	if (pSong->Status & swrk_status_playing)
	{
		// Time to update channels info?
		if (pSong->FrameTime >= pSong->TempoTime)
		{
			pSong->FrameTime -= pSong->TempoTime;
			pSong->TimeIndex += pSong->TempoTime >> 10;

			// Go to next frame
			time = pSong->TimeIndex;
			Song_NextFrame(pSong);

			Song_PreProcessRow(pSong);

			// Remind frame to be decoded
			pSong->Status |= swrk_status_processrow;

			// Process forced pauses
			// Pause after frame change?
			if ((pSong->Status & swrk_status_pauseatframe)
			// Pause after row change?
			||  (   (pSong->SeqsChange & swrk_change_row)
			     && (pSong->Status & swrk_status_pauseatrow))
			// Pause after pattern change?
			||  (   (pSong->SeqsChange & swrk_change_pattern)
			     && (   (pSong->Status & swrk_status_pauseatpattern)
			        // Pause at song end?
			         || (   (pSong->Status & swrk_status_pauseatend)
			             && (pSong->TimeIndex < time)))))
			{
				pSong->Status &= ~swrk_status_playing;
				pSong->Status |= swrk_status_paused;
				return duration;
			}
		}

		// Read ramping duration to apply
		if (Glb->flags & glb_flag_VolumeRamping)
			ramp = ramp_time*ramp_parts;
		else
			ramp = 0;
		// Sometimes frames are to short for ramping
		if (pSong->Status & swrk_status_noramp)
			ramp = 0;

		// Delay end of current frame for ramping of notes cuts
		if (pSong->FrameTime >= ramp)
		{
			// Frame Time >= Cut Ramp Duration
			// Decode frame if not yet done
			if (pSong->Status & swrk_status_processrow)
			{
				Song_ProcessRow(pSong);
				pSong->Status &= ~swrk_status_processrow;
			}
		}

		// Limit to time remaining till next frame
		time = pSong->TempoTime - pSong->FrameTime;
		if (duration > time)
			duration = time;

		// Ramping tests
		time = (ramp << 1) - pSong->FrameTime;
		if (time > 0)
		{
			// Frame Time < 2 Ramps Duration (ramping of note cuts,
			// then ramping of new notes)
			// Limit buffer to ramping parts
			// divide in n areas
			uint32_t utime = time << (32 - ramp_tshift);
			utime >>= (32 - ramp_tshift);
			if (!utime) utime = ramp_time;

			if (duration > utime)
				duration = utime;
		}
	}

	return duration;
}

/*---------------
 * ISong_Lister
 *
 * In  - R0  fill duration in 1/256 us
 *
 * Out - R0  streams ptr
 *       R1  nr of streams
 */

void Song_Lister(_kernel_swi_regs* r, ISong* pSong)
{
	int duration, time;

	// Check if song is playing
	if (!(pSong->Status & swrk_status_playing))
	{
		// return empty list
		r->r[1] = 0;

		return;
	}
	time = pSong->FrameTime;
	pSong->FrameTime += duration = r->r[0];
	r->r[0] = (int) &pSong->Notes.ActiveNotesArray[0];
	r->r[1] = pSong->Notes.ActiveNotes;
	if (!r->r[1]) return;

	// Ramping?
	// No, return
	if (!(Glb->flags & glb_flag_VolumeRamping)
	||  (pSong->Status & swrk_status_noramp))
		return;

	// ramping (2 times: ramping cut and ramping slide)
	if (time >= ramp_parts*ramp_time*2)
		return;
	if (time >= ramp_parts*ramp_time)
		time -= ramp_parts*ramp_time;

	time = ramp_parts - 1 - (time >> ramp_tshift); // / time of 1 ramp part

	uint32_t count = pSong->Notes.ActiveNotes;
	Note** ppNote = &pSong->Notes.ActiveNotesArray[0];
	for (;count > 0; count--)
	{
		Note* pNote = *ppNote++;
		// process volume
		int val = pNote->old_volume - pNote->new_volume;
		val = (val * time) >> ramp_pshift;
		pNote->stream.volume = pNote->new_volume + val;
		// process frequency
		val = pNote->old_frequency - pNote->new_frequency;
		val = (val * time) >> ramp_pshift;
		pNote->stream.frequency = pNote->new_frequency + val;
		// process panning
		if (pNote->new_panning <= 256)
		{
			val = pNote->old_panning - pNote->new_panning;
			val = (val * time) >> ramp_pshift;
			pNote->stream.panning = pNote->new_panning + val;
		}
	}
}

/**
 * In  - Sequence Position [0, nr of orders[
 */
void Song_SetPosition(ISong* pSong, const SubSong* pSubSong, uint32_t seq_nr)
{
	int32_t pos = seq_nr;

#ifndef MAKEABS
	// forbid buffer filling in the middle of our changes
	int ioff = _kernel_irqs_disabled();
	if (!ioff) _kernel_irqs_off();
#endif

	// Update sequence info only when playing a sequence
	if (pSong->PlayRange.Pattern < 0)
	{
		if (pSong->SubSongPtr != pSubSong)
		{
			pSong->SubSongPtr = pSubSong;
			pSong->SeqPos = 0;
		}

		// Handle dummy entries in sequence

		// Song_ReadCurrentPattern will skip dummy entries
		// which means when trying to perform Pos -= 1
		// we fall again on Pos. So, in this case we move back
		// to previous pos without dummy value.
		if (pos < pSong->SeqPos)
		{
			// Dummy sequence pos?
			if (pSong->PlayRange.Last >= 0)
			{
				while (pSubSong->pSeqs[pos] < 0)
				{
					pos--;
					if (pos < pSong->PlayRange.Start)  pos = pSong->PlayRange.Last;
				}
			}
			else
			{
				while (pSubSong->pSeqs[pos] < 0)
				{
					pos--;
					if (pos < 0) pos = pSubSong->SeqLen - 1;
				}
			}
		}
		pSong->SeqPos = pos;
	}

	// Init pos variables
	pSong->FrameCount =
	pSong->FrameExtCount =
	pSong->RepeatCount =
	pSong->FrameTime =
	pSong->RowPos = 0;

	// Cut every note played
	Song_ClearAllNotes(pSong);

	// Read first row of first pattern
	Song_ReadCurrentPattern(pSong, true);
	Song_PreProcessRow(pSong);
	//Remind frame to be decoded
	pSong->Status |= swrk_status_processrow;

#ifndef MAKEABS
	// Reallow buffer filling
	if (!ioff) _kernel_irqs_on();
#endif
}

/**
 * Builds time index, sections and PreAmp
 */
static const _kernel_oserror* Song_BuildLength(ISong* pSong)
{
	const _kernel_oserror* err;
	SubSong* pSubSong;
	int i, seq;

	pSong->Hdr.Sections = 0;
	pSong->SongTime = 0;

	// Sanity checks
	if (!pSong->Hdr.Patterns
	||  !pSong->Hdr.Channels)
		return NULL;

	// Clear sections marks, set special mark for special values in sequence
	seq = 0;
	for (pSubSong = &pSong->Hdr.BaseSong; pSubSong; pSubSong = pSubSong->pNextSubSong)
	{
		for (i = 0; i < pSubSong->SeqLen; i++)
		{
			if (pSubSong->pSeqs[i] < 0)
				pSubSong->pSecIndexes[i] = -1;
			else
			{
				pSubSong->pSecIndexes[i] = 0;
				seq++;
			}
		}
	}
	if (!seq) return NULL;

	// Init pattern processor
	pSong->Seq.maxstreams = pSong->Hdr.Channels;

	// Loop while there are unprocessed positions in sequence
	uint32_t time = 0;
	uint32_t pos = 0;

	// First pass for 1 section of every subsong
	for (pSubSong = &pSong->Hdr.BaseSong; pSubSong; pSubSong = pSubSong->pNextSubSong)
	{
		//
		// 1. Skip processed positions in sequence
		//

		for (pos = 0; pos < pSubSong->SeqLen; pos++)
		{
			if (pSubSong->pSecIndexes[pos] == 0)
				break;
		}

		// Done?
		if (pos >= pSubSong->SeqLen)
			continue;

		//
		// 2. New section, loop till we reach a processed position
		//

		// New section
		pSong->Hdr.Sections++;
		// set start position for pattern processor
		pSong->SubSongPtr = pSubSong;
		pSong->SeqPos = pos;
		pSong->RowPos = 0;
		pSong->FrameCount =
		pSong->FrameExtCount =
		pSong->RepeatCount =
		pSong->FrameTime = 0;
		Song_SetDefaults(pSong);
		Song_ReadCurrentPattern(pSong, true);

		//
		// Process sequence till we loop on an already timed position
		//
		while(true)
		{
			// Stop if we reached a processed position
			if (pSubSong->pSecIndexes[pos])
				break;

			// Store section/time
			pSubSong->pSecIndexes[pos] = pSong->Hdr.Sections;
			pSubSong->pTimeIndexes[pos] = time;

			// store tempo/speed
			pSubSong->pSecTempos[pos] = ((pSong->Tempo.value.start) << 8)
			                           + pSong->Speed.value.start;

			//
			// Process frames till we reach a new sequence position
			//
			while(true)
			{
				// Decode new row?
				if (!pSong->FrameExtCount)
					Song_ReadRow(pSong);

				// Process globals
				Process_GlbEffects(pSong);

				// Add frame duration to elapsed time
				time += pSong->TempoTime >> 10;

				// Move to next frame
				Song_NextFrame(pSong);

				// Check if a change from sequence position occured
				if (pSong->SeqsChange & swrk_change_pattern)
					break;
			}
			pos = pSong->SeqPos;
		}
	}

	// Find extra sections of sub-song, we must allocate a new sub-song for them
	for (pSubSong = &pSong->Hdr.BaseSong; pSubSong; pSubSong = pSubSong->pNextSubSong)
	{
		while(true)
		{
			//
			// 1. Skip processed positions in sequence
			//

			for (pos = 0; pos < pSubSong->SeqLen; pos++)
			{
				if (pSubSong->pSecIndexes[pos] == 0)
					break;
			}

			// Done?
			if (pos >= pSubSong->SeqLen)
				break;

			//
			// 2. New section, loop till we reach a processed position
			//

			pSong->Hdr.Sections++;
			// set start position for pattern processor
			pSong->SubSongPtr = pSubSong;
			pSong->SeqPos = pos;
			pSong->RowPos = 0;
			pSong->FrameCount =
			pSong->FrameExtCount =
			pSong->RepeatCount =
			pSong->FrameTime = 0;
			Song_SetDefaults(pSong);
			Song_ReadCurrentPattern(pSong, true);

			//
			// Process sequence till we loop on an already timed position
			//
			while(true)
			{
				// Stop if we reached a processed position
				if (pSubSong->pSecIndexes[pos])
					break;

				// Store section/time
				pSubSong->pSecIndexes[pos] = pSong->Hdr.Sections;
				pSubSong->pTimeIndexes[pos] = time;

				// store tempo/speed
				pSubSong->pSecTempos[pos] = ((pSong->Tempo.value.start) << 8)
				                           + pSong->Speed.value.start;

				//
				// Process frames till we reach a new sequence position
				//
				while(true)
				{
					// Decode new row?
					if (!pSong->FrameExtCount)
						Song_ReadRow(pSong);

					// Process globals
					Process_GlbEffects(pSong);

					// Add frame duration to elapsed time
					time += pSong->TempoTime >> 10;

					// Move to next frame
					Song_NextFrame(pSong);

					// Check if a change from sequence position occured
					if (pSong->SeqsChange & swrk_change_pattern)
						break;
				}
				pos = pSong->SeqPos;
			}
		}
	}

	// Store time index
	pSong->SongTime = time;
	err = CMem_Alloc(pSong->Hdr.pGlb, (void**) &pSong->SectionsPtr, sizeof(ISection)*pSong->Hdr.Sections);
	if (err) return err;

	// Build list of sections
	for (i = 1; i <= pSong->Hdr.Sections; i++)
	{
		pSong->SectionsPtr[i - 1].pSubSong = NULL;

		for (pSubSong = &pSong->Hdr.BaseSong; pSubSong; pSubSong = pSubSong->pNextSubSong)
		{
			for (seq = 0; seq < pSubSong->SeqLen; seq++)
			{
				if (pSubSong->pSecIndexes[seq] == i)
				{
					pSong->SectionsPtr[i - 1].pSubSong = pSubSong;
					pSong->SectionsPtr[i - 1].StartPos = seq;
					break;
				}
			}

			if (pSong->SectionsPtr[i - 1].pSubSong != NULL)
				break;
		}
	}

	// Set PreAmp
	Song_SetPreAmp(pSong);

	// Reinit variables
	Song_InitControllers(pSong);
	Song_SetPosition(pSong, &pSong->Hdr.BaseSong, 0);

	return NULL;
}

extern void ASM_Song_Lister(void);
extern void ASM_Song_Changer(void);

const _kernel_oserror* Song_InitForPlay(ISong* pSong)
{
	const _kernel_oserror*  err = NULL;
	// Transfer song global info
	pSong->Seq.lister = ASM_Song_Lister;
	pSong->Seq.changer = ASM_Song_Changer;
	pSong->Seq.R12 = pSong;
	pSong->Seq.maxstreams = 0;

	pSong->Notes.Flags = 0;
	if (pSong->Hdr.Flags & Song_Flag_ITEnvelopes)
		pSong->Notes.Flags |= hnotes_flag_ITenvelopes;
	if (!(pSong->Hdr.Flags & Song_Flag_CutOnHighPitch))
		pSong->Notes.Flags |= hnotes_flag_nocutonhighpitch;
	if (pSong->Hdr.Flags & Song_Flag_XMFadeout)
		pSong->Notes.Flags |= hnotes_flag_XMFadeout;
	pSong->Notes.Polyphony = hnotes_max_notes;
	pSong->Notes.MaxNotesPerChannel = pSong->Hdr.MaxNotesPerChannel;

	Song_InitControllers(pSong);
	Song_ClearAllNotes(pSong);
	// Ignore flags altering replay length
	uint32_t config = pSong->Config;
	pSong->Config &= ~(glb_flag_IgnoreSeqEndMark|glb_flag_IgnoreSeqEndLoop|glb_flag_IgnoreSeqRestartPos);
	err = Song_BuildLength(pSong);
	// Restore flags
	pSong->Config = config;

	return err;
}
