shithub: ft²

Download patch

ref: 93e72ec9553429bc8d7321ec7f241fe5b152a9df
parent: 98fcfa79342eab3d3659ae352c76355c28762b3f
parent: b581c2cbc55c2dc826f7564757da116fec74ced8
parent: c64b5e1941c8d269136edaaa7da5c04ced96b916
author: qwx <qwx@sciops.net>
date: Sun Dec 10 19:54:49 EST 2023

sync upstream

--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
 # ft2-clone
-Fasttracker II clone for Windows/macOS/Linux, by [8bitbubsy](https://16-bits.org).
+Fasttracker II clone for Windows/macOS/Linux
 
-Aims to be a **highly accurate** clone of the classic Fasttracker II software for MS-DOS. \
+Aims to be a highly accurate clone of the classic Fasttracker II software for MS-DOS. \
 The XM player itself has been directly ported from the original source code, for maximum accuracy. \
 The code is partly my own, partly based on the original FT2 code.
 
@@ -14,8 +14,8 @@
 If these don't work for you, you'll have to compile the code manually.
 
 # Improvements over original DOS version
-- The channel resampler/mixer uses floating-point arithmetics for less errors, and has a high-quality interpolation option (8-point windowed-sinc)
-- The sample loader supports AIFF samples and more WAV types. It will also attempt to tune the sample (finetune/rel. note) to its playback frequency on load.
+- The channel resampler/mixer uses floating-point arithmetics for less errors, and has a high-quality interpolation option (8-point and 16-point windowed-sinc)
+- The sample loader supports FLAC/AIFF samples and more WAV types than original FT2. It will also attempt to tune the sample (finetune and rel. note) to its playback frequency on load.
 - It contains a new "Trim" feature, which will remove unused stuff to potentially make the module smaller
 - Drag n' drop of modules/samples
 - The waveform display in the sample editor shows peak based data when zoomed out
@@ -37,6 +37,6 @@
 Please don't nag me about it, and try to use the Linux packages linked to from [16-bits.org](https://16-bits.org/ft2.php) instead.
 
 PS: The source code is quite hackish and hardcoded. \
-My first priority is to make an _accurate_ 1:1 clone, and not to make flexible and easily modifiable code.
+My first priority is to make an accurate clone, and not to make flexible and easily modifiable code.
 
 Big parts of the code (except GUI) are directly ported from the original FT2 source code, with permission to use a BSD 3-Clause license.
--- a/src/ft2_audio.c
+++ b/src/ft2_audio.c
@@ -27,7 +27,7 @@
 static int32_t smpShiftValue;
 static uint32_t oldAudioFreq, tickTimeLenInt;
 static uint64_t tickTimeLenFrac;
-static double dAudioNormalizeMul, dSqrtPanningTable[256+1];
+static float fAudioNormalizeMul, fSqrtPanningTable[256+1];
 static voice_t voice[MAX_CHANNELS * 2];
 
 // globalized
@@ -116,7 +116,7 @@
 	if (!bitDepth32Flag)
 		dAmp *= 32768.0;
 
-	dAudioNormalizeMul = dAmp;
+	fAudioNormalizeMul = (float)dAmp;
 }
 
 void decreaseMasterVol(void)
@@ -197,7 +197,7 @@
 	lockMixerCallback();
 	audio.interpolationType = interpolationType;
 
-	// set sinc polyphase LUT pointers
+	// set sinc LUT pointers
 	if (config.interpolation == INTERPOLATION_SINC8)
 	{
 		fKaiserSinc = fKaiserSinc_8;
@@ -218,7 +218,7 @@
 {
 	// same formula as FT2's panning table (with 0.0 .. 1.0 scale)
 	for (int32_t i = 0; i <= 256; i++)
-		dSqrtPanningTable[i] = sqrt(i / 256.0);
+		fSqrtPanningTable[i] = (float)sqrt(i / 256.0);
 }
 
 static void voiceUpdateVolumes(int32_t i, uint8_t status)
@@ -225,8 +225,8 @@
 {
 	voice_t *v = &voice[i];
 
-	v->fTargetVolumeL = (float)(v->dVolume * dSqrtPanningTable[256-v->panning]);
-	v->fTargetVolumeR = (float)(v->dVolume * dSqrtPanningTable[    v->panning]);
+	v->fTargetVolumeL = v->fVolume * fSqrtPanningTable[256-v->panning];
+	v->fTargetVolumeR = v->fVolume * fSqrtPanningTable[    v->panning];
 
 	if (!audio.volumeRampingFlag)
 	{
@@ -251,7 +251,7 @@
 
 			voice_t *f = &voice[MAX_CHANNELS+i];
 
-			*f = *v; // store current voice in respective fadeout ramp voice
+			*f = *v; // copy current voice to new fadeout-ramp voice
 
 			const float fVolumeLDiff = 0.0f - f->fCurrVolumeL;
 			const float fVolumeRDiff = 0.0f - f->fCurrVolumeR;
@@ -366,10 +366,10 @@
 
 		if (status & IS_Vol)
 		{
-			v->dVolume = ch->dFinalVol;
+			v->fVolume = ch->fFinalVol;
 
 			// set scope volume
-			const int32_t scopeVolume = (int32_t)((SCOPE_HEIGHT * ch->dFinalVol) + 0.5); // rounded
+			const int32_t scopeVolume = (int32_t)((SCOPE_HEIGHT * ch->fFinalVol) + 0.5f); // rounded
 			v->scopeVolume = (uint8_t)scopeVolume;
 		}
 
@@ -388,9 +388,9 @@
 
 				if (ch->finalPeriod == 0) // in FT2, period 0 -> delta 0
 				{
-					v->scopeDelta = 0;
 					v->oldDelta = 0;
 					v->fSincLUT = fKaiserSinc;
+					v->scopeDelta = 0;
 				}
 				else
 				{
@@ -397,7 +397,7 @@
 					const double dHz = dPeriod2Hz(ch->finalPeriod);
 					const uintCPUWord_t delta = v->oldDelta = (intCPUWord_t)((dHz * audio.dHz2MixDeltaMul) + 0.5); // Hz -> fixed-point delta (rounded)
 
-					// decide which polyphase sinc LUT to use according to resampling ratio
+					// decide which sinc LUT to use according to the resampling ratio
 					if (delta <= (uintCPUWord_t)(1.1875 * MIXER_FRAC_SCALE))
 						v->fSincLUT = fKaiserSinc;
 					else if (delta <= (uintCPUWord_t)(1.5 * MIXER_FRAC_SCALE))
@@ -419,19 +419,22 @@
 	}
 }
 
-static void sendSamples16BitStereo(uint8_t *stream, uint32_t sampleBlockLength)
+static void sendSamples16BitStereo(void *stream, uint32_t sampleBlockLength)
 {
-	int16_t *streamPointer16 = (int16_t *)stream;
+	int16_t *streamPtr16 = (int16_t *)stream;
 	for (uint32_t i = 0; i < sampleBlockLength; i++)
 	{
-		int32_t L = (int32_t)((double)audio.fMixBufferL[i] * dAudioNormalizeMul);
-		CLAMP16(L);
-		*streamPointer16++ = (int16_t)L;
+		// TODO: This could use dithering (a proper implementation, that is...)
 
-		int32_t R = (int32_t)((double)audio.fMixBufferR[i] * dAudioNormalizeMul);
+		int32_t L = (int32_t)(audio.fMixBufferL[i] * fAudioNormalizeMul);
+		int32_t R = (int32_t)(audio.fMixBufferR[i] * fAudioNormalizeMul);
+
+		CLAMP16(L);
 		CLAMP16(R);
-		*streamPointer16++ = (int16_t)R;
 
+		*streamPtr16++ = (int16_t)L;
+		*streamPtr16++ = (int16_t)R;
+
 		// clear what we read from the mixing buffer
 		audio.fMixBufferL[i] = 0.0f;
 		audio.fMixBufferR[i] = 0.0f;
@@ -438,18 +441,16 @@
 	}
 }
 
-static void sendSamples32BitFloatStereo(uint8_t *stream, uint32_t sampleBlockLength)
+static void sendSamples32BitFloatStereo(void *stream, uint32_t sampleBlockLength)
 {
-	float *fStreamPointer32 = (float *)stream;
+	float *fStreamPtr32 = (float *)stream;
 	for (uint32_t i = 0; i < sampleBlockLength; i++)
 	{
-		double dL = (double)audio.fMixBufferL[i] * dAudioNormalizeMul;
-		dL = CLAMP(dL, -1.0, 1.0);
-		*fStreamPointer32++ = (float)dL;
+		const float fL = audio.fMixBufferL[i] * fAudioNormalizeMul;
+		const float fR = audio.fMixBufferR[i] * fAudioNormalizeMul;
 
-		double dR = (double)audio.fMixBufferR[i] * dAudioNormalizeMul;
-		dR = CLAMP(dR, -1.0, 1.0);
-		*fStreamPointer32++ = (float)dR;
+		*fStreamPtr32++ = CLAMP(fL, -1.0f, 1.0f);
+		*fStreamPtr32++ = CLAMP(fR, -1.0f, 1.0f);
 
 		// clear what we read from the mixing buffer
 		audio.fMixBufferL[i] = 0.0f;
@@ -496,7 +497,7 @@
 }
 
 // used for song-to-WAV renderer
-void mixReplayerTickToBuffer(uint32_t samplesToMix, uint8_t *stream, uint8_t bitDepth)
+void mixReplayerTickToBuffer(uint32_t samplesToMix, void *stream, uint8_t bitDepth)
 {
 	doChannelMixing(0, samplesToMix);
 
--- a/src/ft2_audio.h
+++ b/src/ft2_audio.h
@@ -79,8 +79,7 @@
 	const int16_t *leftEdgeTaps16;
 
 	const float *fSincLUT;
-	double dVolume;
-	float fCurrVolumeL, fCurrVolumeR, fVolumeLDelta, fVolumeRDelta, fTargetVolumeL, fTargetVolumeR;
+	float fVolume, fCurrVolumeL, fCurrVolumeR, fVolumeLDelta, fVolumeRDelta, fTargetVolumeL, fTargetVolumeR;
 } voice_t;
 
 #if defined(_MSC_VER) || defined(__plan9__)
@@ -156,7 +155,7 @@
 void unlockMixerCallback(void);
 void resetRampVolumes(void);
 void updateVoices(void);
-void mixReplayerTickToBuffer(uint32_t samplesToMix, uint8_t *stream, uint8_t bitDepth);
+void mixReplayerTickToBuffer(uint32_t samplesToMix, void *stream, uint8_t bitDepth);
 
 // in ft2_audio.c
 extern audio_t audio;
--- a/src/ft2_header.h
+++ b/src/ft2_header.h
@@ -13,7 +13,7 @@
 #endif
 #include "ft2_replayer.h"
 
-#define PROG_VER_STR "1.73"
+#define PROG_VER_STR "1.74"
 
 // do NOT change these! It will only mess things up...
 
--- a/src/ft2_replayer.c
+++ b/src/ft2_replayer.c
@@ -70,6 +70,31 @@
 	}
 }
 
+void resetReplayerState(void)
+{
+	song.pattDelTime = song.pattDelTime2 = 0;
+	song.posJumpFlag = false;
+	song.pBreakPos = 0;
+	song.pBreakFlag = false;
+
+	channel_t *ch = channel;
+	for (int32_t i = 0; i < song.numChannels; i++, ch++)
+	{
+		ch->patternLoopStartRow = 0;
+		ch->patternLoopCounter = 0;
+	}
+	
+	// reset global volume (if song was playing)
+	if (songPlaying)
+	{
+		song.globalVolume = 64;
+
+		ch = channel;
+		for (int32_t i = 0; i < song.numChannels; i++, ch++)
+			ch->status |= IS_Vol;
+	}
+}
+
 void resetChannels(void)
 {
 	const bool audioWasntLocked = !audio.locked;
@@ -287,7 +312,7 @@
 		return -1; // shouldn't happen (just in case...)
 
 	if (note >= (10*12)-1)
-		return -1; // B-9 (after relativeTone add) = illegal! (won't play in replayer)
+		return -1; // B-9 (after relativeNote calculation) = illegal! (won't play in replayer)
 
 	const int32_t C4Period = (note << 4) + (((int8_t)s->finetune >> 3) + 16);
 
@@ -319,54 +344,61 @@
 		drawC4Rate();
 }
 
-static void retrigVolume(channel_t *ch)
+static void resetVolumes(channel_t *ch)
 {
 	ch->realVol = ch->oldVol;
 	ch->outVol = ch->oldVol;
 	ch->outPan = ch->oldPan;
+
 	ch->status |= IS_Vol + IS_Pan + IS_QuickVol;
 }
 
-static void retrigEnvelopeVibrato(channel_t *ch)
+static void triggerInstrument(channel_t *ch)
 {
-	if (!(ch->waveCtrl & 0x04)) ch->vibPos  = 0;
-	if (!(ch->waveCtrl & 0x40)) ch->tremPos = 0;
+	if (!(ch->vibTremCtrl & 0x04)) ch->vibratoPos = 0;
+	if (!(ch->vibTremCtrl & 0x40)) ch->tremoloPos = 0;
 
-	ch->retrigCnt = 0;
+	ch->noteRetrigCounter = 0;
 	ch->tremorPos = 0;
+	ch->keyOff = false;
 
 	instr_t *ins = ch->instrPtr;
-	assert(ins != NULL);
-
-	if (ins->volEnvFlags & ENV_ENABLED)
+	if (ins != NULL) // just in case
 	{
-		ch->volEnvTick = 65535;
-		ch->volEnvPos = 0;
-	}
+		// reset volume envelope
+		if (ins->volEnvFlags & ENV_ENABLED)
+		{
+			ch->volEnvTick = 65535; // will be increased to 0 on envelope handling
+			ch->volEnvPos = 0;
+		}
 
-	if (ins->panEnvFlags & ENV_ENABLED)
-	{
-		ch->panEnvTick = 65535;
-		ch->panEnvPos = 0;
-	}
+		// reset panning envelope
+		if (ins->panEnvFlags & ENV_ENABLED)
+		{
+			ch->panEnvTick = 65535; // will be increased to 0 on envelope handling
+			ch->panEnvPos = 0;
+		}
 
-	ch->keyOff = false;
-	ch->fadeoutSpeed = ins->fadeout; // FT2 doesn't check if fadeout is more than 4095!
-	ch->fadeoutVol = 32768;
+		// reset fadeout
+		ch->fadeoutSpeed = ins->fadeout; // Warning: FT2 doesn't check if fadeout is more than 4095!
+		ch->fadeoutVol = 32768;
 
-	if (ins->autoVibDepth > 0)
-	{
-		ch->autoVibPos = 0;
-		if (ins->autoVibSweep > 0)
+		// reset auto-vibrato
+		if (ins->autoVibDepth > 0)
 		{
-			ch->autoVibAmp = 0;
-			ch->autoVibSweep = (ins->autoVibDepth << 8) / ins->autoVibSweep;
+			ch->autoVibPos = 0;
+
+			if (ins->autoVibSweep > 0)
+			{
+				ch->autoVibAmp = 0;
+				ch->autoVibSweep = (ins->autoVibDepth << 8) / ins->autoVibSweep;
+			}
+			else
+			{
+				ch->autoVibAmp = ins->autoVibDepth << 8;
+				ch->autoVibSweep = 0;
+			}
 		}
-		else
-		{
-			ch->autoVibAmp = ins->autoVibDepth << 8;
-			ch->autoVibSweep = 0;
-		}
 	}
 }
 
@@ -389,7 +421,7 @@
 		ch->status |= IS_Vol + IS_QuickVol;
 	}
 
-	if (!(ins->panEnvFlags & ENV_ENABLED)) // What..? Probably an FT2 bug.
+	if (!(ins->panEnvFlags & ENV_ENABLED)) // great, another FT2 logic bug
 	{
 		if (ch->panEnvTick >= (uint16_t)ins->panEnvPoints[ch->panEnvPos][0])
 			ch->panEnvTick = ins->panEnvPoints[ch->panEnvPos][0] - 1;
@@ -465,7 +497,7 @@
 	return (loPeriod >> 4) - relativeNote;
 }
 
-static void startTone(uint8_t note, uint8_t efx, uint8_t efxData, channel_t *ch)
+static void triggerNote(uint8_t note, uint8_t efx, uint8_t efxData, channel_t *ch)
 {
 	if (note == NOTE_OFF)
 	{
@@ -491,10 +523,10 @@
 	ch->instrPtr = ins;
 	ch->mute = ins->mute;
 
-	if (note > 96) // non-FT2 security (should never happen because I clamp in the patt. loader now)
+	if (note > 96) // non-FT2 sanity check
 		note = 96;
 
-	ch->smpNum = ins->note2SampleLUT[note-1] & 0xF; // FT2 doesn't AND here, but let's do it for safety
+	ch->smpNum = ins->note2SampleLUT[note-1] & 0xF; // FT2 doesn't mask it, but let's do it anyway
 	sample_t *s = &ins->smp[ch->smpNum];
 
 	ch->smpPtr = s;
@@ -508,13 +540,13 @@
 	ch->oldPan = s->panning;
 
 	if (efx == 0xE && (efxData & 0xF0) == 0x50)
-		ch->finetune = ((efxData & 0x0F) << 4) - 128;
+		ch->finetune = ((efxData & 0x0F) * 16) - 128;
 	else
 		ch->finetune = s->finetune;
 
 	if (note != 0)
 	{
-		const uint16_t noteIndex = ((note-1) << 4) + (((int8_t)ch->finetune >> 3) + 16); // 0..1920
+		const uint16_t noteIndex = ((note-1) * 16) + (((int8_t)ch->finetune >> 3) + 16); // 0..1920
 
 		assert(note2Period != NULL);
 		ch->outPeriod = ch->realPeriod = note2Period[noteIndex];
@@ -525,9 +557,9 @@
 	if (efx == 9)
 	{
 		if (efxData > 0)
-			ch->smpOffset = ch->efxData;
+			ch->sampleOffset = ch->efxData;
 
-		ch->smpStartPos = ch->smpOffset << 8;
+		ch->smpStartPos = ch->sampleOffset << 8;
 	}
 	else
 	{
@@ -535,9 +567,9 @@
 	}
 }
 
-static void volume(channel_t *ch, uint8_t param); // volume slide
-static void vibrato2(channel_t *ch);
-static void tonePorta(channel_t *ch, uint8_t param);
+static void volSlide(channel_t *ch, uint8_t param);
+static void doVibrato(channel_t *ch);
+static void portamento(channel_t *ch, uint8_t param);
 
 static void dummy(channel_t *ch, uint8_t param)
 {
@@ -546,14 +578,14 @@
 	return;
 }
 
-static void finePortaUp(channel_t *ch, uint8_t param)
+static void finePitchSlideUp(channel_t *ch, uint8_t param)
 {
 	if (param == 0)
-		param = ch->fPortaUpSpeed;
+		param = ch->fPitchSlideUpSpeed;
 
-	ch->fPortaUpSpeed = param;
+	ch->fPitchSlideUpSpeed = param;
 
-	ch->realPeriod -= param << 2;
+	ch->realPeriod -= param * 4;
 	if ((int16_t)ch->realPeriod < 1)
 		ch->realPeriod = 1;
 
@@ -561,14 +593,14 @@
 	ch->status |= IS_Period;
 }
 
-static void finePortaDown(channel_t *ch, uint8_t param)
+static void finePitchSlideDown(channel_t *ch, uint8_t param)
 {
 	if (param == 0)
-		param = ch->fPortaDownSpeed;
+		param = ch->fPitchSlideDownSpeed;
 
-	ch->fPortaDownSpeed = param;
+	ch->fPitchSlideDownSpeed = param;
 
-	ch->realPeriod += param << 2;
+	ch->realPeriod += param * 4;
 	if ((int16_t)ch->realPeriod >= 32000) // FT2 bug, should've been unsigned comparison
 		ch->realPeriod = 32000-1;
 
@@ -576,32 +608,32 @@
 	ch->status |= IS_Period;
 }
 
-static void setGlissCtrl(channel_t *ch, uint8_t param)
+static void setPortamentoCtrl(channel_t *ch, uint8_t param)
 {
-	ch->glissFunk = param;
+	ch->portaSemitoneSlides = (param != 0);
 }
 
 static void setVibratoCtrl(channel_t *ch, uint8_t param)
 {
-	ch->waveCtrl = (ch->waveCtrl & 0xF0) | param;
+	ch->vibTremCtrl = (ch->vibTremCtrl & 0xF0) | param;
 }
 
-static void jumpLoop(channel_t *ch, uint8_t param)
+static void patternLoop(channel_t *ch, uint8_t param)
 {
 	if (param == 0)
 	{
-		ch->jumpToRow = song.row & 0xFF;
+		ch->patternLoopStartRow = song.row & 0xFF;
 	}
-	else if (ch->patLoopCounter == 0)
+	else if (ch->patternLoopCounter == 0)
 	{
-		ch->patLoopCounter = param;
+		ch->patternLoopCounter = param;
 
-		song.pBreakPos = ch->jumpToRow;
+		song.pBreakPos = ch->patternLoopStartRow;
 		song.pBreakFlag = true;
 	}
-	else if (--ch->patLoopCounter > 0)
+	else if (--ch->patternLoopCounter > 0)
 	{
-		song.pBreakPos = ch->jumpToRow;
+		song.pBreakPos = ch->patternLoopStartRow;
 		song.pBreakFlag = true;
 	}
 }
@@ -608,10 +640,10 @@
 
 static void setTremoloCtrl(channel_t *ch, uint8_t param)
 {
-	ch->waveCtrl = (param << 4) | (ch->waveCtrl & 0x0F);
+	ch->vibTremCtrl = (param << 4) | (ch->vibTremCtrl & 0x0F);
 }
 
-static void volFineUp(channel_t *ch, uint8_t param)
+static void fineVolSlideUp(channel_t *ch, uint8_t param)
 {
 	if (param == 0)
 		param = ch->fVolSlideUpSpeed;
@@ -626,7 +658,7 @@
 	ch->status |= IS_Vol;
 }
 
-static void volFineDown(channel_t *ch, uint8_t param)
+static void fineVolFineDown(channel_t *ch, uint8_t param)
 {
 	if (param == 0)
 		param = ch->fVolSlideDownSpeed;
@@ -651,7 +683,7 @@
 	}
 }
 
-static void pattDelay(channel_t *ch, uint8_t param)
+static void patternDelay(channel_t *ch, uint8_t param)
 {
 	if (song.pattDelTime2 == 0)
 		song.pattDelTime = param + 1;
@@ -662,20 +694,20 @@
 static const efxRoutine EJumpTab_TickZero[16] =
 {
 	dummy, // 0
-	finePortaUp, // 1
-	finePortaDown, // 2
-	setGlissCtrl, // 3
+	finePitchSlideUp, // 1
+	finePitchSlideDown, // 2
+	setPortamentoCtrl, // 3
 	setVibratoCtrl, // 4
 	dummy, // 5
-	jumpLoop, // 6
+	patternLoop, // 6
 	setTremoloCtrl, // 7
 	dummy, // 8
 	dummy, // 9
-	volFineUp, // A
-	volFineDown, // B
+	fineVolSlideUp, // A
+	fineVolFineDown, // B
 	noteCut0, // C
 	dummy, // D
-	pattDelay, // E
+	patternDelay, // E
 	dummy // F
 };
 
@@ -686,8 +718,8 @@
 
 	if (ch->channelOff) // channel is muted, only handle some E effects
 	{
-		     if (efx == 0x6) jumpLoop(ch, param);
-		else if (efx == 0xE) pattDelay(ch, param);
+		     if (efx == 0x6) patternLoop(ch, param);
+		else if (efx == 0xE) patternDelay(ch, param);
 
 		return;
 	}
@@ -695,7 +727,7 @@
 	EJumpTab_TickZero[efx](ch, param);
 }
 
-static void posJump(channel_t *ch, uint8_t param)
+static void positionJump(channel_t *ch, uint8_t param)
 {
 	if (playMode != PLAYMODE_PATT && playMode != PLAYMODE_RECPATT)
 	{
@@ -712,7 +744,7 @@
 	(void)ch;
 }
 
-static void pattBreak(channel_t *ch, uint8_t param)
+static void patternBreak(channel_t *ch, uint8_t param)
 {
 	param = ((param >> 4) * 10) + (param & 0x0F);
 	if (param <= 63)
@@ -758,12 +790,12 @@
 {
 	bool envUpdate;
 	int8_t point;
-	int16_t tick;
+	int32_t tick;
 
 	instr_t *ins = ch->instrPtr;
 	assert(ins != NULL);
 
-	// (envelope precision has been updated from x.8fp to x.16fp)
+	// envelope precision has been upgraded from .8fp to single-precision float
 
 	// *** VOLUME ENVELOPE ***
 	if (ins->volEnvFlags & ENV_ENABLED)
@@ -784,23 +816,25 @@
 					point--;
 
 					tick -= ins->volEnvPoints[point][0];
-					if (tick == 0)
+					if (tick == 0) // FT2 doesn't test for <= 0 here
 					{
 						envUpdate = false;
 						break;
 					}
 
-					if (ins->volEnvPoints[point+1][0] <= ins->volEnvPoints[point][0])
+					const int32_t xDiff = ins->volEnvPoints[point+1][0] - ins->volEnvPoints[point+0][0];
+					if (xDiff <= 0)
 					{
 						envUpdate = true;
 						break;
 					}
 
-					int32_t delta = (int8_t)((ins->volEnvPoints[point+1][1] - ins->volEnvPoints[point][1]) & 0xFF) << 16;
-					delta /= (ins->volEnvPoints[point+1][0]-ins->volEnvPoints[point][0]);
+					const int32_t y0 = ins->volEnvPoints[point+0][1] & 0xFF;
+					const int32_t y1 = ins->volEnvPoints[point+1][1] & 0xFF;
+					const int32_t yDiff = y1 - y0;
 
-					ch->volEnvDelta = delta;
-					ch->volEnvValue = (ch->volEnvDelta * (tick-1)) + ((int8_t)(ins->volEnvPoints[point][1] & 0xFF) << 16);
+					ch->fVolEnvDelta = (float)yDiff / (float)xDiff;
+					ch->fVolEnvValue = (float)y0 + (ch->fVolEnvDelta * (tick-1));
 
 					point++;
 
@@ -817,8 +851,8 @@
 
 		if (envUpdate)
 		{
-			ch->volEnvDelta = 0;
-			ch->volEnvValue = (int8_t)(ins->volEnvPoints[point][1] & 0xFF) << 16;
+			ch->fVolEnvDelta = 0.0f;
+			ch->fVolEnvValue = (float)(int32_t)(ins->volEnvPoints[point][1] & 0xFF);
 		}
 
 		if (point >= ins->volEnvLength)
@@ -832,7 +866,7 @@
 	}
 
 	// *** PANNING ENVELOPE ***
-	if (ins->volEnvFlags & ENV_SUSTAIN) // FT2 PLAYER BUG: Should've been ins->panEnvFlags
+	if (ins->volEnvFlags & ENV_SUSTAIN) // FT2 REPLAYER BUG: Should've been ins->panEnvFlags
 	{
 		ch->panEnvTick = param-1;
 
@@ -850,23 +884,25 @@
 					point--;
 
 					tick -= ins->panEnvPoints[point][0];
-					if (tick == 0)
+					if (tick == 0) // FT2 doesn't test for <= 0 here
 					{
 						envUpdate = false;
 						break;
 					}
 
-					if (ins->panEnvPoints[point+1][0] <= ins->panEnvPoints[point][0])
+					const int32_t xDiff = ins->panEnvPoints[point+1][0] - ins->panEnvPoints[point+0][0];
+					if (xDiff <= 0)
 					{
 						envUpdate = true;
 						break;
 					}
 
-					int32_t delta = (int8_t)((ins->panEnvPoints[point+1][1] - ins->panEnvPoints[point][1]) & 0xFF) << 16;
-					delta /= (ins->panEnvPoints[point+1][0]-ins->panEnvPoints[point][0]);
+					const int32_t y0 = ins->panEnvPoints[point+0][1] & 0xFF;
+					const int32_t y1 = ins->panEnvPoints[point+1][1] & 0xFF;
+					const int32_t yDiff = y1 - y0;
 
-					ch->panEnvDelta = delta;
-					ch->panEnvValue = (ch->panEnvDelta * (tick-1)) + ((int8_t)(ins->panEnvPoints[point][1] & 0xFF) << 16);
+					ch->fPanEnvDelta = (float)yDiff / (float)xDiff;
+					ch->fPanEnvValue = (float)y0 + (ch->fPanEnvDelta * (tick-1));
 
 					point++;
 
@@ -883,8 +919,8 @@
 
 		if (envUpdate)
 		{
-			ch->panEnvDelta = 0;
-			ch->panEnvValue = (int8_t)(ins->panEnvPoints[point][1] & 0xFF) << 16;
+			ch->fPanEnvDelta = 0.0f;
+			ch->fPanEnvValue = (float)(int32_t)(ins->panEnvPoints[point][1] & 0xFF);
 		}
 
 		if (point >= ins->panEnvLength)
@@ -911,9 +947,9 @@
 	dummy, // 8
 	dummy, // 9
 	dummy, // A
-	posJump, // B
+	positionJump, // B
 	dummy, // C
-	pattBreak, // D
+	patternBreak, // D
 	E_Effects_TickZero, // E
 	setSpeed, // F
 	setGlobalVolume, // G
@@ -947,17 +983,17 @@
 }
 
 /* -- tick-zero volume column effects --
-** 2nd parameter is used for a volume column quirk with the Rxy command (multiretrig)
+** 2nd parameter is used for a volume column quirk with the Rxy command (multiNoteRetrig)
 */
 
 static void v_SetVibSpeed(channel_t *ch, uint8_t *volColumnData)
 {
-	*volColumnData = (ch->volColumnVol & 0x0F) << 2;
+	*volColumnData = (ch->volColumnVol & 0x0F) * 4;
 	if (*volColumnData != 0)
-		ch->vibSpeed = *volColumnData;
+		ch->vibratoSpeed = *volColumnData;
 }
 
-static void v_Volume(channel_t *ch, uint8_t *volColumnData)
+static void v_SetVolume(channel_t *ch, uint8_t *volColumnData)
 {
 	*volColumnData -= 16;
 	if (*volColumnData > 64) // no idea why FT2 has this check...
@@ -967,7 +1003,7 @@
 	ch->status |= IS_Vol + IS_QuickVol;
 }
 
-static void v_FineSlideDown(channel_t *ch, uint8_t *volColumnData)
+static void v_FineVolSlideDown(channel_t *ch, uint8_t *volColumnData)
 {
 	*volColumnData = (uint8_t)(0 - (ch->volColumnVol & 0x0F)) + ch->realVol;
 	if ((int8_t)*volColumnData < 0)
@@ -977,7 +1013,7 @@
 	ch->status |= IS_Vol;
 }
 
-static void v_FineSlideUp(channel_t *ch, uint8_t *volColumnData)
+static void v_FineVolSlideUp(channel_t *ch, uint8_t *volColumnData)
 {
 	*volColumnData = (ch->volColumnVol & 0x0F) + ch->realVol;
 	if (*volColumnData > 64)
@@ -997,7 +1033,7 @@
 
 // -- non-tick-zero volume column effects --
 
-static void v_SlideDown(channel_t *ch)
+static void v_VolSlideDown(channel_t *ch)
 {
 	uint8_t newVol = (uint8_t)(0 - (ch->volColumnVol & 0x0F)) + ch->realVol;
 	if ((int8_t)newVol < 0)
@@ -1007,7 +1043,7 @@
 	ch->status |= IS_Vol;
 }
 
-static void v_SlideUp(channel_t *ch)
+static void v_VolSlideUp(channel_t *ch)
 {
 	uint8_t newVol = (ch->volColumnVol & 0x0F) + ch->realVol;
 	if (newVol > 64)
@@ -1021,14 +1057,14 @@
 {
 	const uint8_t param = ch->volColumnVol & 0xF;
 	if (param > 0)
-		ch->vibDepth = param;
+		ch->vibratoDepth = param;
 
-	vibrato2(ch);
+	doVibrato(ch);
 }
 
 static void v_PanSlideLeft(channel_t *ch)
 {
-	uint16_t tmp16 = (uint8_t)(0 - (ch->volColumnVol & 0x0F)) + ch->outPan;
+	uint16_t tmp16 = ch->outPan + (uint8_t)(0 - (ch->volColumnVol & 0x0F));
 	if (tmp16 < 256) // includes an FT2 bug: pan-slide-left of 0 = set pan to 0
 		tmp16 = 0;
 
@@ -1038,7 +1074,7 @@
 
 static void v_PanSlideRight(channel_t *ch)
 {
-	uint16_t tmp16 = (ch->volColumnVol & 0x0F) + ch->outPan;
+	uint16_t tmp16 = ch->outPan + (ch->volColumnVol & 0x0F);
 	if (tmp16 > 255)
 		tmp16 = 255;
 
@@ -1046,9 +1082,9 @@
 	ch->status |= IS_Pan;
 }
 
-static void v_TonePorta(channel_t *ch)
+static void v_Portamento(channel_t *ch)
 {
-	tonePorta(ch, 0); // the last parameter is actually not used in tonePorta()
+	portamento(ch, 0); // the last parameter is actually not used in portamento()
 }
 
 static void v_dummy(channel_t *ch)
@@ -1066,18 +1102,18 @@
 
 static const volColumnEfxRoutine VJumpTab_TickNonZero[16] =
 {
-	v_dummy,        v_dummy,         v_dummy,  v_dummy,
-	v_dummy,        v_dummy,     v_SlideDown, v_SlideUp,
+	v_dummy,        v_dummy,         v_dummy, v_dummy,
+	v_dummy,        v_dummy,  v_VolSlideDown, v_VolSlideUp,
 	v_dummy,        v_dummy,         v_dummy, v_Vibrato,
-	v_dummy, v_PanSlideLeft, v_PanSlideRight, v_TonePorta
+	v_dummy, v_PanSlideLeft, v_PanSlideRight, v_Portamento
 };
 
 static const volColumnEfxRoutine2 VJumpTab_TickZero[16] =
 {
-	       v_dummy2,      v_Volume,      v_Volume, v_Volume,
-	       v_Volume,      v_Volume,      v_dummy2, v_dummy2,
-	v_FineSlideDown, v_FineSlideUp, v_SetVibSpeed, v_dummy2,
-	       v_SetPan,      v_dummy2,      v_dummy2, v_dummy2
+	          v_dummy2,      v_SetVolume,   v_SetVolume, v_SetVolume,
+	       v_SetVolume,      v_SetVolume,      v_dummy2, v_dummy2,
+	v_FineVolSlideDown, v_FineVolSlideUp, v_SetVibSpeed, v_dummy2,
+	          v_SetPan,         v_dummy2,      v_dummy2, v_dummy2
 };
 
 static void setPan(channel_t *ch, uint8_t param)
@@ -1095,17 +1131,17 @@
 	ch->status |= IS_Vol + IS_QuickVol;
 }
 
-static void xFinePorta(channel_t *ch, uint8_t param)
+static void extraFinePitchSlide(channel_t *ch, uint8_t param)
 {
-	const uint8_t type = param >> 4;
+	const uint8_t slideType = param >> 4;
 	param &= 0x0F;
 
-	if (type == 0x1) // extra fine porta up
+	if (slideType == 1) // slide up
 	{
 		if (param == 0)
-			param = ch->ePortaUpSpeed;
+			param = ch->efPitchSlideUpSpeed;
 
-		ch->ePortaUpSpeed = param;
+		ch->efPitchSlideUpSpeed = param;
 
 		uint16_t newPeriod = ch->realPeriod;
 
@@ -1116,12 +1152,12 @@
 		ch->outPeriod = ch->realPeriod = newPeriod;
 		ch->status |= IS_Period;
 	}
-	else if (type == 0x2) // extra fine porta down
+	else if (slideType == 2) // slide down
 	{
 		if (param == 0)
-			param = ch->ePortaDownSpeed;
+			param = ch->efPitchSlideDownSpeed;
 
-		ch->ePortaDownSpeed = param;
+		ch->efPitchSlideDownSpeed = param;
 
 		uint16_t newPeriod = ch->realPeriod;
 
@@ -1134,19 +1170,20 @@
 	}
 }
 
-static void doMultiRetrig(channel_t *ch, uint8_t param) // "param" is never used (needed for efx jumptable structure)
+// "param" is never used (needed for efx jumptable structure)
+static void doMultiNoteRetrig(channel_t *ch, uint8_t param)
 {
-	uint8_t cnt = ch->retrigCnt + 1;
-	if (cnt < ch->retrigSpeed)
+	uint8_t cnt = ch->noteRetrigCounter + 1;
+	if (cnt < ch->noteRetrigSpeed)
 	{
-		ch->retrigCnt = cnt;
+		ch->noteRetrigCounter = cnt;
 		return;
 	}
 
-	ch->retrigCnt = 0;
+	ch->noteRetrigCounter = 0;
 
 	int16_t vol = ch->realVol;
-	switch (ch->retrigVol)
+	switch (ch->noteRetrigVol)
 	{
 		case 0x1: vol -= 1; break;
 		case 0x2: vol -= 2; break;
@@ -1180,35 +1217,35 @@
 		ch->outPan = (ch->volColumnVol & 0x0F) << 4;
 	}
 
-	startTone(0, 0, 0, ch);
+	triggerNote(0, 0, 0, ch);
 
 	(void)param;
 }
 
-static void multiRetrig(channel_t *ch, uint8_t param, uint8_t volumeColumnData)
+static void multiNoteRetrig(channel_t *ch, uint8_t param, uint8_t volumeColumnData)
 {
 	uint8_t tmpParam;
 
 	tmpParam = param & 0x0F;
 	if (tmpParam == 0)
-		tmpParam = ch->retrigSpeed;
+		tmpParam = ch->noteRetrigSpeed;
 
-	ch->retrigSpeed = tmpParam;
+	ch->noteRetrigSpeed = tmpParam;
 
 	tmpParam = param >> 4;
 	if (tmpParam == 0)
-		tmpParam = ch->retrigVol;
+		tmpParam = ch->noteRetrigVol;
 
-	ch->retrigVol = tmpParam;
+	ch->noteRetrigVol = tmpParam;
 
 	if (volumeColumnData == 0)
-		doMultiRetrig(ch, 0); // the second parameter is never used (needed for efx jumptable structure)
+		doMultiNoteRetrig(ch, 0); // the second parameter is never used (needed for efx jumptable structure)
 }
 
 static void handleEffects_TickZero(channel_t *ch)
 {
 	// volume column effects
-	uint8_t newVolCol = ch->volColumnVol; // manipulated by vol. column effects, then used for multiretrig check (FT2 quirk)
+	uint8_t newVolCol = ch->volColumnVol; // manipulated by vol. column effects, then used for multiNoteRetrig check (FT2 quirk)
 	VJumpTab_TickZero[ch->volColumnVol >> 4](ch, &newVolCol);
 
 	// normal effects
@@ -1218,13 +1255,13 @@
 
 	     if (ch->efx ==  8) setPan(ch, param);
 	else if (ch->efx == 12) setVol(ch, param);
-	else if (ch->efx == 27) multiRetrig(ch, param, newVolCol);
-	else if (ch->efx == 33) xFinePorta(ch, param);
+	else if (ch->efx == 27) multiNoteRetrig(ch, param, newVolCol);
+	else if (ch->efx == 33) extraFinePitchSlide(ch, param);
 
 	handleMoreEffects_TickZero(ch);
 }
 
-static void fixTonePorta(channel_t *ch, const note_t *p, uint8_t inst)
+static void preparePortamento(channel_t *ch, const note_t *p, uint8_t inst)
 {
 	if (p->note > 0)
 	{
@@ -1234,18 +1271,18 @@
 		}
 		else
 		{
-			const uint16_t note = (((p->note-1) + ch->relativeNote) << 4) + (((int8_t)ch->finetune >> 3) + 16);
+			const uint16_t note = (((p->note-1) + ch->relativeNote) * 16) + (((int8_t)ch->finetune >> 3) + 16);
 			if (note < MAX_NOTES)
 			{
 				assert(note2Period != NULL);
-				ch->wantPeriod = note2Period[note];
+				ch->portamentoTargetPeriod = note2Period[note];
 
-				if (ch->wantPeriod == ch->realPeriod)
-					ch->portaDirection = 0;
-				else if (ch->wantPeriod > ch->realPeriod)
-					ch->portaDirection = 1;
+				if (ch->portamentoTargetPeriod == ch->realPeriod)
+					ch->portamentoDirection = 0;
+				else if (ch->portamentoTargetPeriod > ch->realPeriod)
+					ch->portamentoDirection = 1;
 				else
-					ch->portaDirection = 2;
+					ch->portamentoDirection = 2;
 			}
 		}
 	}
@@ -1252,9 +1289,9 @@
 
 	if (inst > 0)
 	{
-		retrigVolume(ch);
+		resetVolumes(ch);
 		if (p->note != NOTE_OFF)
-			retrigEnvelopeVibrato(ch);
+			triggerInstrument(ch);
 	}
 }
 
@@ -1282,7 +1319,7 @@
 
 	ch->efx = p->efx;
 	ch->efxData = p->efxData;
-	ch->noteData = (p->instr << 8) | p->note;
+	ch->copyOfInstrAndNote = (p->instr << 8) | p->note;
 
 	if (ch->channelOff) // channel is muted, only handle some effects
 	{
@@ -1300,24 +1337,19 @@
 			inst = 0;
 	}
 
-	bool checkEfx = true;
-	if (p->efx == 0x0E)
-	{
-		if (p->efxData >= 0xD1 && p->efxData <= 0xDF)
-			return; // we have a note delay (ED1..EDF)
-		else if (p->efxData == 0x90)
-			checkEfx = false;
-	}
+	if (p->efx == 0x0E && p->efxData >= 0xD1 && p->efxData <= 0xDF)
+		return; // we have a note delay (ED1..EDF)
 
-	if (checkEfx)
+	// only handly effects here when no E90 (retrigger note, parameter zero)
+	if (p->efx != 0x0E || p->efxData != 0x90)
 	{
 		if ((ch->volColumnVol & 0xF0) == 0xF0) // gxx
 		{
-			const uint8_t volColumnData = ch->volColumnVol & 0x0F;
-			if (volColumnData > 0)
-				ch->portaSpeed = volColumnData << 6;
+			const uint8_t param = ch->volColumnVol & 0x0F;
+			if (param > 0)
+				ch->portamentoSpeed = (param << 4) * 4;
 
-			fixTonePorta(ch, p, inst);
+			preparePortamento(ch, p, inst);
 			handleEffects_TickZero(ch);
 			return;
 		}
@@ -1325,9 +1357,9 @@
 		if (p->efx == 3 || p->efx == 5) // 3xx or 5xx
 		{
 			if (p->efx != 5 && p->efxData != 0)
-				ch->portaSpeed = p->efxData << 2;
+				ch->portamentoSpeed = p->efxData * 4;
 
-			fixTonePorta(ch, p, inst);
+			preparePortamento(ch, p, inst);
 			handleEffects_TickZero(ch);
 			return;
 		}
@@ -1337,7 +1369,7 @@
 			keyOff(ch);
 
 			if (inst)
-				retrigVolume(ch);
+				resetVolumes(ch);
 
 			handleEffects_TickZero(ch);
 			return;
@@ -1347,8 +1379,8 @@
 		{
 			if (inst > 0)
 			{
-				retrigVolume(ch);
-				retrigEnvelopeVibrato(ch);
+				resetVolumes(ch);
+				triggerInstrument(ch);
 			}
 
 			handleEffects_TickZero(ch);
@@ -1359,33 +1391,30 @@
 	if (p->note == NOTE_OFF)
 		keyOff(ch);
 	else
-		startTone(p->note, p->efx, p->efxData, ch);
+		triggerNote(p->note, p->efx, p->efxData, ch);
 
 	if (inst > 0)
 	{
-		retrigVolume(ch);
+		resetVolumes(ch);
 		if (p->note != NOTE_OFF)
-			retrigEnvelopeVibrato(ch);
+			triggerInstrument(ch);
 	}
 
 	handleEffects_TickZero(ch);
 }
 
-static void updateChannel(channel_t *ch)
+static void updateVolPanAutoVib(channel_t *ch)
 {
 	bool envInterpolateFlag, envDidInterpolate;
 	uint8_t envPos;
-	int32_t envVal;
-	double dVol;
+	float fEnvVal, fVol;
 
 	instr_t *ins = ch->instrPtr;
 	assert(ins != NULL);
 
-	// *** FADEOUT ***
+	// *** FADEOUT ON KEY OFF ***
 	if (ch->keyOff)
 	{
-		ch->status |= IS_Vol; // always update volume, even if fadeout has reached 0
-
 		if (ch->fadeoutSpeed > 0) // 0..4095
 		{
 			ch->fadeoutVol -= ch->fadeoutSpeed;
@@ -1395,22 +1424,26 @@
 				ch->fadeoutSpeed = 0;
 			}
 		}
+
+		ch->status |= IS_Vol; // always update volume, even if fadeout has reached 0
 	}
 
 	if (!ch->mute)
 	{
-		// (envelope precision has been updated from x.8fp to x.16fp)
+		// envelope precision has been upgraded from .8fp to single-precision float
 
 		// *** VOLUME ENVELOPE ***
-		envVal = 0;
+		fEnvVal = 0.0f;
 		if (ins->volEnvFlags & ENV_ENABLED)
 		{
 			envDidInterpolate = false;
 			envPos = ch->volEnvPos;
 
-			if (++ch->volEnvTick == ins->volEnvPoints[envPos][0])
+			ch->volEnvTick++;
+
+			if (ch->volEnvTick == ins->volEnvPoints[envPos][0])
 			{
-				ch->volEnvValue = (int8_t)(ins->volEnvPoints[envPos][1] & 0xFF) << 16;
+				ch->fVolEnvValue = (float)(int32_t)(ins->volEnvPoints[envPos][1] & 0xFF);
 
 				envPos++;
 				if (ins->volEnvFlags & ENV_LOOP)
@@ -1423,7 +1456,7 @@
 						{
 							envPos = ins->volEnvLoopStart;
 							ch->volEnvTick = ins->volEnvPoints[envPos][0];
-							ch->volEnvValue = (int8_t)(ins->volEnvPoints[envPos][1] & 0xFF) << 16;
+							ch->fVolEnvValue = (float)(int32_t)(ins->volEnvPoints[envPos][1] & 0xFF);
 						}
 					}
 
@@ -1438,7 +1471,7 @@
 						if (envPos-1 == ins->volEnvSustain)
 						{
 							envPos--;
-							ch->volEnvDelta = 0;
+							ch->fVolEnvDelta = 0.0f;
 							envInterpolateFlag = false;
 						}
 					}
@@ -1447,44 +1480,49 @@
 					{
 						ch->volEnvPos = envPos;
 
-						ch->volEnvDelta = 0;
-						if (ins->volEnvPoints[envPos][0] > ins->volEnvPoints[envPos-1][0])
+						const int32_t x0 = ins->volEnvPoints[envPos-1][0];
+						const int32_t x1 = ins->volEnvPoints[envPos-0][0];
+
+						const int32_t xDiff = x1 - x0;
+						if (xDiff > 0)
 						{
-							int32_t delta = (int8_t)((ins->volEnvPoints[envPos][1] - ins->volEnvPoints[envPos-1][1]) & 0xFF) << 16;
-							delta /= (ins->volEnvPoints[envPos][0]-ins->volEnvPoints[envPos-1][0]);
-							ch->volEnvDelta = delta;
+							const int32_t y0 = ins->volEnvPoints[envPos-1][1] & 0xFF;
+							const int32_t y1 = ins->volEnvPoints[envPos-0][1] & 0xFF;
 
-							envVal = ch->volEnvValue;
+							const int32_t yDiff = y1 - y0;
+							ch->fVolEnvDelta = (float)yDiff / (float)xDiff;
+
+							fEnvVal = ch->fVolEnvValue;
 							envDidInterpolate = true;
 						}
+						else
+						{
+							ch->fVolEnvDelta = 0.0f;
+						}
 					}
 				}
 				else
 				{
-					ch->volEnvDelta = 0;
+					ch->fVolEnvDelta = 0.0f;
 				}
 			}
 
 			if (!envDidInterpolate)
 			{
-				ch->volEnvValue += ch->volEnvDelta;
+				ch->fVolEnvValue += ch->fVolEnvDelta;
 
-				envVal = ch->volEnvValue;
-				if (envVal > 64<<16)
+				fEnvVal = ch->fVolEnvValue;
+				if (fEnvVal < 0.0f || fEnvVal > 64.0f)
 				{
-					if (envVal > 128<<16)
-						envVal = 64<<16;
-					else
-						envVal = 0;
-
-					ch->volEnvDelta = 0;
+					fEnvVal = CLAMP(fEnvVal, 0.0f, 64.0f);
+					ch->fVolEnvDelta = 0.0f;
 				}
 			}
 
 			const int32_t vol = song.globalVolume * ch->outVol * ch->fadeoutVol;
 
-			dVol = vol * (1.0 / (64.0 * 64.0 * 32768.0));
-			dVol *= envVal * (1.0 / (64.0 * (1 << 16))); // volume envelope value
+			fVol = vol * (1.0f / (64.0f * 64.0f * 32768.0f));
+			fVol *= fEnvVal * (1.0f / 64.0f); // volume envelope value
 
 			ch->status |= IS_Vol; // update mixer vol every tick when vol envelope is enabled
 		}
@@ -1492,30 +1530,30 @@
 		{
 			const int32_t vol = song.globalVolume * ch->outVol * ch->fadeoutVol;
 
-			dVol = vol * (1.0 / (64.0 * 64.0 * 32768.0));
+			fVol = vol * (1.0f / (64.0f * 64.0f * 32768.0f));
 		}
 
-		/* FT2 doesn't clamp here, but it's actually important if you
-		** move envelope points with the mouse while playing the instrument.
-		*/
-		ch->dFinalVol = CLAMP(dVol, 0.0, 1.0);
+		// FT2 doesn't clamp the volume, but let's do it anyway
+		ch->fFinalVol = CLAMP(fVol, 0.0f, 1.0f);
 	}
 	else
 	{
-		ch->dFinalVol = 0.0;
+		ch->fFinalVol = 0.0f;
 	}
 
 	// *** PANNING ENVELOPE ***
 
-	envVal = 0;
+	fEnvVal = 0.0f;
 	if (ins->panEnvFlags & ENV_ENABLED)
 	{
 		envDidInterpolate = false;
 		envPos = ch->panEnvPos;
 
-		if (++ch->panEnvTick == ins->panEnvPoints[envPos][0])
+		ch->panEnvTick++;
+
+		if (ch->panEnvTick == ins->panEnvPoints[envPos][0])
 		{
-			ch->panEnvValue = (int8_t)(ins->panEnvPoints[envPos][1] & 0xFF) << 16;
+			ch->fPanEnvValue = (float)(int32_t)(ins->panEnvPoints[envPos][1] & 0xFF);
 
 			envPos++;
 			if (ins->panEnvFlags & ENV_LOOP)
@@ -1529,7 +1567,7 @@
 						envPos = ins->panEnvLoopStart;
 
 						ch->panEnvTick = ins->panEnvPoints[envPos][0];
-						ch->panEnvValue = (int8_t)(ins->panEnvPoints[envPos][1] & 0xFF) << 16;
+						ch->fPanEnvValue = (float)(int32_t)(ins->panEnvPoints[envPos][1] & 0xFF);
 					}
 				}
 
@@ -1544,7 +1582,7 @@
 					if (envPos-1 == ins->panEnvSustain)
 					{
 						envPos--;
-						ch->panEnvDelta = 0;
+						ch->fPanEnvDelta = 0.0f;
 						envInterpolateFlag = false;
 					}
 				}
@@ -1553,46 +1591,53 @@
 				{
 					ch->panEnvPos = envPos;
 
-					ch->panEnvDelta = 0;
-					if (ins->panEnvPoints[envPos][0] > ins->panEnvPoints[envPos-1][0])
+					const int32_t x0 = ins->panEnvPoints[envPos-1][0];
+					const int32_t x1 = ins->panEnvPoints[envPos-0][0];
+
+					const int32_t xDiff = x1 - x0;
+					if (xDiff > 0)
 					{
-						int32_t delta = (int8_t)((ins->panEnvPoints[envPos][1] - ins->panEnvPoints[envPos-1][1]) & 0xFF) << 16;
-						delta /= (ins->panEnvPoints[envPos][0]-ins->panEnvPoints[envPos-1][0]);
-						ch->panEnvDelta = delta;
+						const int32_t y0 = ins->panEnvPoints[envPos-1][1] & 0xFF;
+						const int32_t y1 = ins->panEnvPoints[envPos-0][1] & 0xFF;
 
-						envVal = ch->panEnvValue;
+						const int32_t yDiff = y1 - y0;
+						ch->fPanEnvDelta = (float)yDiff / (float)xDiff;
+
+						fEnvVal = ch->fPanEnvValue;
 						envDidInterpolate = true;
 					}
+					else
+					{
+						ch->fPanEnvDelta = 0.0f;
+					}
 				}
 			}
 			else
 			{
-				ch->panEnvDelta = 0;
+				ch->fPanEnvDelta = 0.0f;
 			}
 		}
 
 		if (!envDidInterpolate)
 		{
-			ch->panEnvValue += ch->panEnvDelta;
+			ch->fPanEnvValue += ch->fPanEnvDelta;
 
-			envVal = ch->panEnvValue;
-			if (envVal > 64<<16)
+			fEnvVal = ch->fPanEnvValue;
+			if (fEnvVal < 0.0f || fEnvVal > 64.0f)
 			{
-				if (envVal > 128<<16)
-					envVal = 64<<16;
-				else
-					envVal = 0;
-
-				ch->panEnvDelta = 0;
+				fEnvVal = CLAMP(fEnvVal, 0.0f, 64.0f);
+				ch->fPanEnvDelta = 0.0f;
 			}
 		}
 
-		envVal -= 32<<16; // center panning envelope value
+		fEnvVal -= 32.0f; // center panning envelope value (0..64 -> -32..32)
 
-		const int32_t pan = 128 - ABS(ch->outPan-128);
-		const int32_t panAdd = (pan * envVal) >> (16+5);
-		ch->finalPan = (uint8_t)(ch->outPan + panAdd);
+		const int32_t pan = 128 - ABS(ch->outPan - 128);
+		const float fPanAdd = (pan * fEnvVal) * (1.0f / 32.0f);
+		const int32_t newPan = (int32_t)(ch->outPan + fPanAdd); // truncate here, do not round
 
+		ch->finalPan = (uint8_t)CLAMP(newPan, 0, 255); // FT2 doesn't clamp the pan, but let's do it anyway
+
 		ch->status |= IS_Pan; // update pan every tick because pan envelope is enabled
 	}
 	else
@@ -1673,7 +1718,7 @@
 }
 
 // for arpeggio and portamento (semitone-slide mode)
-static uint16_t relocateTon(uint16_t period, uint8_t arpNote, channel_t *ch)
+static uint16_t adjustPeriodFromNote(uint16_t period, uint8_t arpNote, channel_t *ch)
 {
 	int32_t tmpPeriod;
 
@@ -1707,20 +1752,20 @@
 	return note2Period[tmpPeriod];
 }
 
-static void vibrato2(channel_t *ch)
+static void doVibrato(channel_t *ch)
 {
-	uint8_t tmpVib = (ch->vibPos >> 2) & 0x1F;
+	uint8_t tmpVib = (ch->vibratoPos >> 2) & 0x1F;
 
-	switch (ch->waveCtrl & 3)
+	switch (ch->vibTremCtrl & 3)
 	{
 		// 0: sine
-		case 0: tmpVib = vibTab[tmpVib]; break;
+		case 0: tmpVib = vibratoTab[tmpVib]; break;
 
 		// 1: ramp
 		case 1:
 		{
 			tmpVib <<= 3;
-			if ((int8_t)ch->vibPos < 0)
+			if ((int8_t)ch->vibratoPos < 0)
 				tmpVib = ~tmpVib;
 		}
 		break;
@@ -1729,22 +1774,22 @@
 		default: tmpVib = 255; break;
 	}
 
-	tmpVib = (tmpVib * ch->vibDepth) >> 5; // logical shift (unsigned calc.), not arithmetic shift
+	tmpVib = (tmpVib * ch->vibratoDepth) >> 5; // logical shift (unsigned calc.), not arithmetic shift
 
-	if ((int8_t)ch->vibPos < 0)
+	if ((int8_t)ch->vibratoPos < 0)
 		ch->outPeriod = ch->realPeriod - tmpVib;
 	else
 		ch->outPeriod = ch->realPeriod + tmpVib;
 
 	ch->status |= IS_Period;
-	ch->vibPos += ch->vibSpeed;
+	ch->vibratoPos += ch->vibratoSpeed;
 }
 
-static void arp(channel_t *ch, uint8_t param)
+static void arpeggio(channel_t *ch, uint8_t param)
 {
 	uint8_t note;
 
-	const uint8_t tick = arpTab[song.tick & 0xFF]; // non-FT2 protection (we have 248 extra overflow bytes in LUT, but not more!)
+	const uint8_t tick = arpeggioTab[song.tick & 255];
 	if (tick == 0)
 	{
 		ch->outPeriod = ch->realPeriod;
@@ -1756,20 +1801,20 @@
 		else
 			note = param & 0x0F; // tick 2
 
-		ch->outPeriod = relocateTon(ch->realPeriod, note, ch);
+		ch->outPeriod = adjustPeriodFromNote(ch->realPeriod, note, ch);
 	}
 
 	ch->status |= IS_Period;
 }
 
-static void portaUp(channel_t *ch, uint8_t param)
+static void pitchSlideUp(channel_t *ch, uint8_t param)
 {
 	if (param == 0)
-		param = ch->portaUpSpeed;
+		param = ch->pitchSlideUpSpeed;
 
-	ch->portaUpSpeed = param;
+	ch->pitchSlideUpSpeed = param;
 
-	ch->realPeriod -= param << 2;
+	ch->realPeriod -= param * 4;
 	if ((int16_t)ch->realPeriod < 1)
 		ch->realPeriod = 1;
 
@@ -1777,14 +1822,14 @@
 	ch->status |= IS_Period;
 }
 
-static void portaDown(channel_t *ch, uint8_t param)
+static void pitchSlideDown(channel_t *ch, uint8_t param)
 {
 	if (param == 0)
-		param = ch->portaDownSpeed;
+		param = ch->pitchSlideDownSpeed;
 
-	ch->portaDownSpeed = param;
+	ch->pitchSlideDownSpeed = param;
 
-	ch->realPeriod += param << 2;
+	ch->realPeriod += param * 4;
 	if ((int16_t)ch->realPeriod >= 32000) // FT2 bug, should've been unsigned comparison
 		ch->realPeriod = 32000-1;
 
@@ -1792,32 +1837,32 @@
 	ch->status |= IS_Period;
 }
 
-static void tonePorta(channel_t *ch, uint8_t param)
+static void portamento(channel_t *ch, uint8_t param)
 {
-	if (ch->portaDirection == 0)
+	if (ch->portamentoDirection == 0)
 		return;
 
-	if (ch->portaDirection > 1)
+	if (ch->portamentoDirection > 1)
 	{
-		ch->realPeriod -= ch->portaSpeed;
-		if ((int16_t)ch->realPeriod <= (int16_t)ch->wantPeriod)
+		ch->realPeriod -= ch->portamentoSpeed;
+		if ((int16_t)ch->realPeriod <= (int16_t)ch->portamentoTargetPeriod)
 		{
-			ch->portaDirection = 1;
-			ch->realPeriod = ch->wantPeriod;
+			ch->portamentoDirection = 1;
+			ch->realPeriod = ch->portamentoTargetPeriod;
 		}
 	}
 	else
 	{
-		ch->realPeriod += ch->portaSpeed;
-		if (ch->realPeriod >= ch->wantPeriod)
+		ch->realPeriod += ch->portamentoSpeed;
+		if (ch->realPeriod >= ch->portamentoTargetPeriod)
 		{
-			ch->portaDirection = 1;
-			ch->realPeriod = ch->wantPeriod;
+			ch->portamentoDirection = 1;
+			ch->realPeriod = ch->portamentoTargetPeriod;
 		}
 	}
 
-	if (ch->glissFunk) // semitone-slide flag
-		ch->outPeriod = relocateTon(ch->realPeriod, 0, ch);
+	if (ch->portaSemitoneSlides)
+		ch->outPeriod = adjustPeriodFromNote(ch->realPeriod, 0, ch);
 	else
 		ch->outPeriod = ch->realPeriod;
 
@@ -1828,34 +1873,32 @@
 
 static void vibrato(channel_t *ch, uint8_t param)
 {
-	uint8_t tmp8;
-
 	if (param > 0)
 	{
-		tmp8 = param & 0x0F;
-		if (tmp8 > 0)
-			ch->vibDepth = tmp8;
+		const uint8_t vibratoDepth = param & 0x0F;
+		if (vibratoDepth > 0)
+			ch->vibratoDepth = vibratoDepth;
 
-		tmp8 = (param & 0xF0) >> 2;
-		if (tmp8 > 0)
-			ch->vibSpeed = tmp8;
+		const uint8_t vibratoSpeed = (param & 0xF0) >> 2;
+		if (vibratoSpeed > 0)
+			ch->vibratoSpeed = vibratoSpeed;
 	}
 
-	vibrato2(ch);
+	doVibrato(ch);
 }
 
-static void tonePlusVol(channel_t *ch, uint8_t param)
+static void portamentoPlusVolSlide(channel_t *ch, uint8_t param)
 {
-	tonePorta(ch, 0); // the last parameter is actually not used in tonePorta()
-	volume(ch, param);
+	portamento(ch, 0); // the last parameter is actually not used in portamento()
+	volSlide(ch, param);
 
 	(void)param;
 }
 
-static void vibratoPlusVol(channel_t *ch, uint8_t param)
+static void vibratoPlusVolSlide(channel_t *ch, uint8_t param)
 {
-	vibrato2(ch);
-	volume(ch, param);
+	doVibrato(ch);
+	volSlide(ch, param);
 
 	(void)param;
 }
@@ -1862,32 +1905,30 @@
 
 static void tremolo(channel_t *ch, uint8_t param)
 {
-	uint8_t tmp8;
 	int16_t tremVol;
 
-	const uint8_t tmpEff = param;
-	if (tmpEff > 0)
+	if (param > 0)
 	{
-		tmp8 = tmpEff & 0x0F;
-		if (tmp8 > 0)
-			ch->tremDepth = tmp8;
+		uint8_t tremoloDepth = param & 0x0F;
+		if (tremoloDepth > 0)
+			ch->tremoloDepth = tremoloDepth;
 
-		tmp8 = (tmpEff & 0xF0) >> 2;
-		if (tmp8 > 0)
-			ch->tremSpeed = tmp8;
+		uint8_t tremoloSpeed = (param & 0xF0) >> 2;
+		if (tremoloSpeed > 0)
+			ch->tremoloSpeed = tremoloSpeed;
 	}
 
-	uint8_t tmpTrem = (ch->tremPos >> 2) & 0x1F;
-	switch ((ch->waveCtrl >> 4) & 3)
+	uint8_t tmpTrem = (ch->tremoloPos >> 2) & 0x1F;
+	switch ((ch->vibTremCtrl >> 4) & 3)
 	{
 		// 0: sine
-		case 0: tmpTrem = vibTab[tmpTrem]; break;
+		case 0: tmpTrem = vibratoTab[tmpTrem]; break;
 
 		// 1: ramp
 		case 1:
 		{
 			tmpTrem <<= 3;
-			if ((int8_t)ch->vibPos < 0) // FT2 bug, should've been ch->tremPos
+			if ((int8_t)ch->vibratoPos < 0) // FT2 bug, should've been ch->tremoloPos
 				tmpTrem = ~tmpTrem;
 		}
 		break;
@@ -1895,9 +1936,9 @@
 		// 2/3: square
 		default: tmpTrem = 255; break;
 	}
-	tmpTrem = (tmpTrem * ch->tremDepth) >> 6; // logical shift (unsigned calc.), not arithmetic shift
+	tmpTrem = (tmpTrem * ch->tremoloDepth) >> 6; // logical shift (unsigned calc.), not arithmetic shift
 
-	if ((int8_t)ch->tremPos < 0)
+	if ((int8_t)ch->tremoloPos < 0)
 	{
 		tremVol = ch->realVol - tmpTrem;
 		if (tremVol < 0)
@@ -1912,10 +1953,11 @@
 
 	ch->outVol = (uint8_t)tremVol;
 	ch->status |= IS_Vol;
-	ch->tremPos += ch->tremSpeed;
+
+	ch->tremoloPos += ch->tremoloSpeed;
 }
 
-static void volume(channel_t *ch, uint8_t param) // volume slide
+static void volSlide(channel_t *ch, uint8_t param)
 {
 	if (param == 0)
 		param = ch->volSlideSpeed;
@@ -2008,9 +2050,9 @@
 static void tremor(channel_t *ch, uint8_t param)
 {
 	if (param == 0)
-		param = ch->tremorSave;
+		param = ch->tremorParam;
 
-	ch->tremorSave = param;
+	ch->tremorParam = param;
 
 	uint8_t tremorSign = ch->tremorPos & 0x80;
 	uint8_t tremorData = ch->tremorPos & 0x7F;
@@ -2042,8 +2084,8 @@
 
 	if ((song.speed-song.tick) % param == 0)
 	{
-		startTone(0, 0, 0, ch);
-		retrigEnvelopeVibrato(ch);
+		triggerNote(0, 0, 0, ch);
+		triggerInstrument(ch);
 	}
 }
 
@@ -2060,12 +2102,14 @@
 {
 	if ((uint8_t)(song.speed-song.tick) == param)
 	{
-		startTone(ch->noteData & 0xFF, 0, 0, ch);
+		const uint8_t note = ch->copyOfInstrAndNote & 0x00FF;
+		triggerNote(note, 0, 0, ch);
 
-		if ((ch->noteData & 0xFF00) > 0)
-			retrigVolume(ch);
+		const uint8_t instrument = ch->copyOfInstrAndNote >> 8;
+		if (instrument > 0)
+			resetVolumes(ch);
 
-		retrigEnvelopeVibrato(ch);
+		triggerInstrument(ch);
 
 		if (ch->volColumnVol >= 0x10 && ch->volColumnVol <= 0x50)
 		{
@@ -2106,17 +2150,17 @@
 
 static const efxRoutine JumpTab_TickNonZero[36] =
 {
-	arp, // 0
-	portaUp, // 1
-	portaDown, // 2
-	tonePorta, // 3
+	arpeggio, // 0
+	pitchSlideUp, // 1
+	pitchSlideDown, // 2
+	portamento, // 3
 	vibrato, // 4
-	tonePlusVol, // 5
-	vibratoPlusVol, // 6
+	portamentoPlusVolSlide, // 5
+	vibratoPlusVolSlide, // 6
 	tremolo, // 7
 	dummy, // 8
 	dummy, // 9
-	volume, // A
+	volSlide, // A
 	dummy, // B
 	dummy, // C
 	dummy, // D
@@ -2133,7 +2177,7 @@
 	dummy, // O
 	panningSlide, // P
 	dummy, // Q
-	doMultiRetrig, // R
+	doMultiNoteRetrig, // R
 	dummy, // S
 	tremor, // T
 	dummy, // U
@@ -2240,7 +2284,7 @@
 	{
 		ch = channel;
 		for (i = 0; i < song.numChannels; i++, ch++)
-			updateChannel(ch);
+			updateVolPanAutoVib(ch);
 
 		return;
 	}
@@ -2282,7 +2326,7 @@
 		for (i = 0; i < song.numChannels; i++, ch++, p++)
 		{
 			getNewNote(ch, p);
-			updateChannel(ch);
+			updateVolPanAutoVib(ch);
 		}
 	}
 	else
@@ -2291,7 +2335,7 @@
 		for (i = 0; i < song.numChannels; i++, ch++)
 		{
 			handleEffects_TickNonZero(ch);
-			updateChannel(ch);
+			updateVolPanAutoVib(ch);
 		}
 	}
 
@@ -2403,6 +2447,7 @@
 		{
 			int8_t *p8L = (int8_t *)p;
 			int8_t *p8R = (int8_t *)p + length;
+
 			int8_t olds8L = 0;
 			int8_t olds8R = 0;
 
@@ -2487,10 +2532,11 @@
 		return false;
 
 	memset(p, 0, sizeof (instr_t));
-	for (int32_t i = 0; i < MAX_SMP_PER_INST; i++)
+	sample_t *s = p->smp;
+	for (int32_t i = 0; i < MAX_SMP_PER_INST; i++, s++)
 	{
-		p->smp[i].panning = 128;
-		p->smp[i].volume = 64;
+		s->panning = 128;
+		s->volume = 64;
 	}
 
 	setStdEnvelope(p, 0, 3);
@@ -2929,20 +2975,20 @@
 
 	if (insNum != 0 && note != NOTE_OFF)
 	{
-		ch->noteData = (insNum << 8) | (ch->noteData & 0xFF);
+		ch->copyOfInstrAndNote = (insNum << 8) | (ch->copyOfInstrAndNote & 0xFF);
 		ch->instrNum = insNum;
 	}
 
-	ch->noteData = (ch->noteData & 0xFF00) | note;
+	ch->copyOfInstrAndNote = (ch->copyOfInstrAndNote & 0xFF00) | note;
 	ch->efx = 0;
 	ch->efxData = 0;
 
-	startTone(note, 0, 0, ch);
+	triggerNote(note, 0, 0, ch);
 
 	if (note != NOTE_OFF)
 	{
-		retrigVolume(ch);
-		retrigEnvelopeVibrato(ch);
+		resetVolumes(ch);
+		triggerInstrument(ch);
 
 		if (vol != -1) // if jamming note keys, vol -1 = use sample's volume
 		{
@@ -2955,7 +3001,7 @@
 	ch->midiVibDepth = midiVibDepth;
 	ch->midiPitch = midiPitch;
 
-	updateChannel(ch);
+	updateVolPanAutoVib(ch);
 
 	unlockAudio();
 }
@@ -2982,15 +3028,15 @@
 	lockAudio();
 
 	ch->instrNum = 130;
-	ch->noteData = (ch->instrNum << 8) | note;
+	ch->copyOfInstrAndNote = (ch->instrNum << 8) | note;
 	ch->efx = 0;
 
-	startTone(note, 0, 0, ch);
+	triggerNote(note, 0, 0, ch);
 
 	if (note != NOTE_OFF)
 	{
-		retrigVolume(ch);
-		retrigEnvelopeVibrato(ch);
+		resetVolumes(ch);
+		triggerInstrument(ch);
 
 		ch->realVol = vol;
 		ch->outVol = vol;
@@ -3000,7 +3046,7 @@
 	ch->midiVibDepth = midiVibDepth;
 	ch->midiPitch = midiPitch;
 
-	updateChannel(ch);
+	updateVolPanAutoVib(ch);
 
 	unlockAudio();
 
@@ -3042,18 +3088,18 @@
 	int32_t samplePlayOffset = smpOffset;
 
 	ch->instrNum = 130;
-	ch->noteData = (ch->instrNum << 8) | note;
+	ch->copyOfInstrAndNote = (ch->instrNum << 8) | note;
 	ch->efx = 0;
 	ch->efxData = 0;
 
-	startTone(note, 0, 0, ch);
+	triggerNote(note, 0, 0, ch);
 
 	ch->smpStartPos = samplePlayOffset;
 
 	if (note != NOTE_OFF)
 	{
-		retrigVolume(ch);
-		retrigEnvelopeVibrato(ch);
+		resetVolumes(ch);
+		triggerInstrument(ch);
 
 		ch->realVol = vol;
 		ch->outVol = vol;
@@ -3063,7 +3109,7 @@
 	ch->midiVibDepth = midiVibDepth;
 	ch->midiPitch = midiPitch;
 
-	updateChannel(ch);
+	updateVolPanAutoVib(ch);
 
 	unlockAudio();
 
@@ -3086,7 +3132,7 @@
 		lastChInstr[i].smpNum = 255;
 		lastChInstr[i].instrNum = 255;
 
-		ch->noteData = 0;
+		ch->copyOfInstrAndNote = 0;
 		ch->relativeNote = 0;
 		ch->smpNum = 0;
 		ch->smpPtr = NULL;
@@ -3096,14 +3142,14 @@
 		ch->realVol = 0;
 		ch->outVol = 0;
 		ch->oldVol = 0;
-		ch->dFinalVol = 0.0;
+		ch->fFinalVol = 0.0f;
 		ch->oldPan = 128;
 		ch->outPan = 128;
 		ch->finalPan = 128;
-		ch->vibDepth = 0;
+		ch->vibratoDepth = 0; // clear it because it can be set from the volumn column
 		ch->midiVibDepth = 0;
 		ch->midiPitch = 0;
-		ch->portaDirection = 0; // FT2 bugfix: weird 3xx behavior if not used with note
+		ch->portamentoDirection = 0; // FT2 bugfix: weird 3xx behavior if not used with note
 
 		stopVoice(i);
 	}
@@ -3120,32 +3166,6 @@
 
 	if (audioWasntLocked)
 		unlockAudio();
-}
-
-void resetReplayerState(void)
-{
-	song.pattDelTime = song.pattDelTime2 = 0;
-	song.posJumpFlag = false;
-	song.pBreakPos = 0;
-	song.pBreakFlag = false;
-
-	// reset pattern loops (E6x)
-	channel_t *ch = channel;
-	for (int32_t i = 0; i < song.numChannels; i++, ch++)
-	{
-		ch->jumpToRow = 0;
-		ch->patLoopCounter = 0;
-	}
-	
-	// reset global volume (if song was playing)
-	if (songPlaying)
-	{
-		song.globalVolume = 64;
-
-		ch = channel;
-		for (int32_t i = 0; i < song.numChannels; i++, ch++)
-			ch->status |= IS_Vol;
-	}
 }
 
 void setNewSongPos(int32_t pos)
--- a/src/ft2_replayer.h
+++ b/src/ft2_replayer.h
@@ -241,26 +241,25 @@
 
 typedef struct channel_t
 {
-	bool keyOff, channelOff, mute;
+	bool keyOff, channelOff, mute, portaSemitoneSlides;
 	volatile uint8_t status, tmpStatus;
 	int8_t relativeNote, finetune;
-	uint8_t smpNum, instrNum, efxData, efx, smpOffset, tremorSave, tremorPos;
-	uint8_t globVolSlideSpeed, panningSlideSpeed, waveCtrl, portaDirection;
-	uint8_t glissFunk, vibPos, tremPos, vibSpeed, vibDepth, tremSpeed, tremDepth;
-	uint8_t jumpToRow, patLoopCounter, volSlideSpeed, fVolSlideUpSpeed, fVolSlideDownSpeed;
-	uint8_t fPortaUpSpeed, fPortaDownSpeed, ePortaUpSpeed, ePortaDownSpeed;
-	uint8_t portaUpSpeed, portaDownSpeed, retrigSpeed, retrigCnt, retrigVol;
+	uint8_t smpNum, instrNum, efxData, efx, sampleOffset, tremorParam, tremorPos;
+	uint8_t globVolSlideSpeed, panningSlideSpeed, vibTremCtrl, portamentoDirection;
+	uint8_t vibratoPos, tremoloPos, vibratoSpeed, vibratoDepth, tremoloSpeed, tremoloDepth;
+	uint8_t patternLoopStartRow, patternLoopCounter, volSlideSpeed, fVolSlideUpSpeed, fVolSlideDownSpeed;
+	uint8_t fPitchSlideUpSpeed, fPitchSlideDownSpeed, efPitchSlideUpSpeed, efPitchSlideDownSpeed;
+	uint8_t pitchSlideUpSpeed, pitchSlideDownSpeed, noteRetrigSpeed, noteRetrigCounter, noteRetrigVol;
 	uint8_t volColumnVol, noteNum, panEnvPos, autoVibPos, volEnvPos, realVol, oldVol, outVol;
 	uint8_t oldPan, outPan, finalPan;
 	int16_t midiPitch;
-	uint16_t outPeriod, realPeriod, finalPeriod, noteData, wantPeriod, portaSpeed;
+	uint16_t outPeriod, realPeriod, finalPeriod, copyOfInstrAndNote, portamentoTargetPeriod, portamentoSpeed;
 	uint16_t volEnvTick, panEnvTick, autoVibAmp, autoVibSweep;
 	uint16_t midiVibDepth;
 	int32_t fadeoutVol, fadeoutSpeed;
-	int32_t volEnvDelta, panEnvDelta, volEnvValue, panEnvValue;
 	int32_t oldFinalPeriod, smpStartPos;
 
-	double dFinalVol; // 0.0 .. 1.0
+	float fFinalVol, fVolEnvDelta, fPanEnvDelta, fVolEnvValue, fPanEnvValue;
 
 	sample_t *smpPtr;
 	instr_t *instrPtr;
@@ -283,7 +282,6 @@
 double getSampleC4Rate(sample_t *s);
 
 void setNewSongPos(int32_t pos);
-void resetReplayerState(void);
 
 void fixString(char *str, int32_t lastChrPos); // removes leading spaces and 0x1A chars
 void fixSongName(void);
--- a/src/ft2_tables.c
+++ b/src/ft2_tables.c
@@ -17,7 +17,7 @@
 	214,202,190,180,170,160,151,143,135,127,120,113
 };
 
-const uint8_t arpTab[256] =
+const uint8_t arpeggioTab[256] =
 {
 	0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,0,
 	
@@ -61,7 +61,7 @@
 	 24,  23,  22,  20,  19,  17,  16,  14,  12,  11,   9,   8,   6,   5,   3,   2
 };
 
-const uint8_t vibTab[32] = // for normal vibrato/tremolo
+const uint8_t vibratoTab[32] = // for normal vibrato/tremolo
 {
 	  0, 24, 49, 74, 97,120,141,161,180,197,212,224,235,244,250,253,
 	255,253,250,244,235,224,212,197,180,161,141,120, 97, 74, 49, 24
@@ -325,9 +325,10 @@
 	   36,    35,    35,    35,    35,    35,    35,    35,    35,    34,    34,    34,    34,    34,    34,    34,
 	   34,    33,    33,    33,    33,    33,    33,    33,    33,    32,    32,    32,    32,    32,    32,    32,
 	   32,    32,    32,    31,    31,    31,    31,    31,    31,    31,    31,    30,    30,    30,    30,    30,
-	   30,    30,    30,    30,    30,    29,    29,    29,    29,    29,    29,    29,    29,    29,    29,    22,
-	   16,     8,     0,    16,    32,    24,    16,     8,     0,    16,    32,    24,    16,     8,     0,     0
-	   // the last 17 values are off (but identical to FT2) because of a bug in how FT2 calculates this table
+	   30,    30,    30,    30,    30,    29,    29,    29,    29,    29,    29,    29,    29,    29,    29,
+
+	   // these last values are wrong (but identical to FT2) because of a bug in how FT2 calculates this table
+	   22, 16, 8, 0, 16, 32, 24, 16, 8, 0, 16, 32, 24, 16, 8, 0, 0
 };
 
 /* ----------------------------------------------------------------------- */
--- a/src/ft2_tables.h
+++ b/src/ft2_tables.h
@@ -13,9 +13,9 @@
 
 extern const uint16_t ptPeriods[3 * 12];
 
-extern const uint8_t arpTab[256];
+extern const uint8_t arpeggioTab[256];
 extern const int8_t autoVibSineTab[256];
-extern const uint8_t vibTab[32];
+extern const uint8_t vibratoTab[32];
 extern const uint16_t modPeriods[8 * 12];
 extern const uint16_t linearPeriods[1936];
 extern const uint16_t amigaPeriods[1936];
--- a/src/mixer/ft2_mix_macros.h
+++ b/src/mixer/ft2_mix_macros.h
@@ -384,8 +384,8 @@
 #define LIMIT_MIX_NUM_RAMP \
 	if (v->volumeRampLength == 0) \
 	{ \
-		fVolumeLDelta = 0.0; \
-		fVolumeRDelta = 0.0; \
+		fVolumeLDelta = 0.0f; \
+		fVolumeRDelta = 0.0f; \
 		\
 		if (v->isFadeOutVoice) \
 		{ \
@@ -404,7 +404,7 @@
 #define LIMIT_MIX_NUM_MONO_RAMP \
 	if (v->volumeRampLength == 0) \
 	{ \
-		fVolumeLDelta = 0.0; \
+		fVolumeLDelta = 0.0f; \
 		if (v->isFadeOutVoice) \
 		{ \
 			v->active = false; /* volume ramp fadeout-voice is done, shut it down */ \
--- a/src/scopes/ft2_scopes.c
+++ b/src/scopes/ft2_scopes.c
@@ -78,7 +78,7 @@
 		ch->realVol = 0;
 		ch->outVol = 0;
 		ch->oldVol = 0;
-		ch->dFinalVol = 0.0;
+		ch->fFinalVol = 0.0f;
 		ch->outPan = 128;
 		ch->oldPan = 128;
 		ch->finalPan = 128;