shithub: ft2-clone

Download patch

ref: 108c333ecdfd8287e12f39f858347db64d33f2f7
parent: aaa773b416d6de796d233caffb15fa78ad8b2a90
author: Olav Sørensen <[email protected]>
date: Tue Mar 31 17:40:07 EDT 2020

Pushed v1.15 code

- More improvements to S3M loader. Fixes "satellite one.s3m" and other S3Ms.
- Up/down pushbutton delay has been increased even more, to prevent accidentally
  skipping too much.
- Some other small miscellaneous changes not worth of a mention

--- a/src/ft2_about.c
+++ b/src/ft2_about.c
@@ -9,7 +9,7 @@
 
 // ported from original FT2 code
 
-#define NUM_STARS 512
+#define NUM_STARS 1000
 #define ABOUT_SCREEN_W 626
 #define ABOUT_SCREEN_H 167
 #define ABOUT_LOGO_W 449
@@ -53,7 +53,7 @@
 	randSeed *= 134775813;
 	randSeed += 1;
 
-	r = (int32_t)(((int64_t)randSeed * l) >> 32);
+	r = ((int64_t)randSeed * l) >> 32;
 	return r;
 }
 
@@ -68,13 +68,13 @@
 	sc = sin32767[a.z >> 6];
 	cc = cos32767[a.z >> 6];
 
-	mat->x.x = ((ca * cc) >> 16) + (((sc * ((sa * sb) >> 16)) >> 16) << 1);
+	mat->x.x = ((ca * cc) >> 16) + ((sc * ((sa * sb) >> 16)) >> (16-1));
 	mat->y.x = (sa * cb) >> 16;
-	mat->z.x = (((cc * ((sa * sb) >> 16)) >> 16) << 1) - ((ca * sc) >> 16);
+	mat->z.x = ((cc * ((sa * sb) >> 16)) >> (16-1)) - ((ca * sc) >> 16);
 
-	mat->x.y = (((sc * ((ca * sb) >> 16)) >> 16) << 1) - ((sa * cc) >> 16);
+	mat->x.y = ((sc * ((ca * sb) >> 16)) >> (16-1)) - ((sa * cc) >> 16);
 	mat->y.y = (ca * cb) >> 16;
-	mat->z.y = ((sa * sc) >> 16) + (((cc * ((ca * sb) >> 16)) >> 16) << 1);
+	mat->z.y = ((sa * sc) >> 16) + ((cc * ((ca * sb) >> 16)) >> (16-1));
 
 	mat->x.z = (cb * sc) >> 16;
 	mat->y.z = 0 - (sb >> 1);
@@ -167,8 +167,8 @@
 static void realStars(void)
 {
 	uint8_t col;
-	int16_t x, y, z, xx, xy, xz, yx, yy, yz, zx, zy, zz;
-	int32_t screenBufferPos;
+	int16_t z, xx, xy, xz, yx, yy, yz, zx, zy, zz;
+	int32_t x, y, zMul, screenBufferPos;
 	vector_t *star;
 
 	xx = starmat.x.x; xy = starmat.x.y; xz = starmat.x.z;
@@ -190,26 +190,27 @@
 		star = &starcrd[i];
 		star->z += hastighet;
 
-		z = ((xz * star->x) >> 16) + ((yz * star->y) >> 16) + ((zz * star->z) >> 16);
+		z = ((xz * star->x) + (yz * star->y) + (zz * star->z)) >> 16;
 		z += 9000;
-		if (z <= 100)
-			continue;
+		if (z <= 100) continue;
+		zMul = 0xFFFFFFFF / z; // 8bitbubsy: optimization
+		
+		y = ((xy * star->x) + (yy * star->y) + (zy * star->z)) >> (16-7);
+		y = ((int64_t)y * zMul) >> 32;
+		y += (2+ABOUT_SCREEN_H)/2;
+		if ((uint32_t)y > 2+ABOUT_SCREEN_H) continue;
 
-		y = ((xy * star->x) >> 16) + ((yy * star->y) >> 16) + ((zy * star->z) >> 16);
-		y = (int16_t)((y << 7) / z) + 84;
-		if ((uint16_t)y >= 173-6)
-			continue;
+		x = ((xx * star->x) + (yx * star->y) + (zx * star->z)) >> (16-7);
+		x += x >> 2; // x *= 1.25
+		x = ((int64_t)x * zMul) >> 32;
+		x += (2+ABOUT_SCREEN_W)/2;
+		if ((uint32_t)x > 2+ABOUT_SCREEN_W) continue;
 
-		x = ((xx * star->x) >> 16) + ((yx * star->y) >> 16) + ((zx * star->z) >> 16);
-		x = (int16_t)((((x >> 2) + x) << 7) / z) + (320-8);
-		if ((uint16_t)x >= 640-16)
-			continue;
-
 		// render star pixel if the pixel under it is the background
-		screenBufferPos = ((y + 4) * SCREEN_W) + (x + 4);
+		screenBufferPos = ((uint32_t)y * SCREEN_W) + (uint32_t)x;
 		if ((video.frameBuffer[screenBufferPos] >> 24) == PAL_BCKGRND)
 		{
-			col = ((uint8_t)~(z >> 8) >> 3) - (22 - 8);
+			col = ((uint8_t)~(z >> 8) >> 3) - 14;
 			if (col < 24)
 			{
 				video.frameBuffer[screenBufferPos] = video.palette[starColConv[col]] & 0x00FFFFFF;
--- a/src/ft2_audio.c
+++ b/src/ft2_audio.c
@@ -20,8 +20,8 @@
 static int8_t pmpCountDiv, pmpChannels = 2;
 static uint16_t smpBuffSize;
 static int32_t masterVol, oldAudioFreq, speedVal, pmpLeft, randSeed = INITIAL_DITHER_SEED;
-static int32_t prngStateL, prngStateR;
-static uint32_t tickTimeLen, tickTimeLenFrac, oldSFrq, oldSFrqRev = 0xFFFFFFFF;
+static int32_t prngStateL, prngStateR, oldPeriod, oldSFrq, oldSFrqRev;
+static uint32_t tickTimeLen, tickTimeLenFrac;
 static float fAudioAmpMul;
 static voice_t voice[MAX_VOICES * 2];
 static void (*sendAudSamplesFunc)(uint8_t *, uint32_t, uint8_t); // "send mixed samples" routines
@@ -31,8 +31,9 @@
 
 volatile bool pattQueueReading, pattQueueClearing, chQueueReading, chQueueClearing;
 
-void resetOldRevFreqs(void)
+void resetCachedMixerVars(void)
 {
+	oldPeriod = -1;
 	oldSFrq = 0;
 	oldSFrqRev = 0xFFFFFFFF;
 }
@@ -369,11 +370,9 @@
 		// frequency change
 		if (status & IS_Period)
 		{
-			v->SFrq = getFrequenceValue(ch->finalPeriod);
-
-			if (v->SFrq != oldSFrq) // this value will very often be the same as before
+			if (ch->finalPeriod != oldPeriod) // this value will very often be the same as before
 			{
-				oldSFrq = v->SFrq;
+				oldSFrq = getFrequenceValue(ch->finalPeriod);
 
 				oldSFrqRev = 0xFFFFFFFF;
 				if (oldSFrq != 0)
@@ -380,6 +379,7 @@
 					oldSFrqRev /= oldSFrq;
 			}
 
+			v->SFrq = oldSFrq;
 			v->SFrqRev = oldSFrqRev;
 		}
 
--- a/src/ft2_audio.h
+++ b/src/ft2_audio.h
@@ -45,8 +45,10 @@
 	bool backwards, isFadeOutVoice;
 	uint8_t SPan;
 	uint16_t SVol;
-	int32_t SLVol1, SRVol1, SLVol2, SRVol2, SLVolIP, SRVolIP, SVolIPLen, SPos, SLen, SRepS, SRepL;
-	uint32_t SPosDec, SFrq, SFrqRev;
+	int32_t SLVol1, SRVol1, SLVol2, SRVol2, SLVolIP, SRVolIP, SVolIPLen;
+	int32_t SPos, SLen, SRepS, SRepL, SFrq, SFrqRev;
+	uint32_t SPosDec;
+
 	void (*mixRoutine)(void *, int32_t); // function pointer to mix routine
 } voice_t;
 
@@ -79,7 +81,7 @@
 
 extern volatile bool pattQueueReading, pattQueueClearing, chQueueReading, chQueueClearing;
 
-void resetOldRevFreqs(void);
+void resetCachedMixerVars(void);
 int32_t pattQueueReadSize(void);
 int32_t pattQueueWriteSize(void);
 bool pattQueuePush(pattSyncData_t t);
--- a/src/ft2_checkboxes.c
+++ b/src/ft2_checkboxes.c
@@ -79,7 +79,7 @@
 
 	// ------ CONFIG CHECKBOXES ------
 	//x,   y,   w,   h,  funcOnUp
-	{   3,  91,  76, 12, cbToggleAutoSaveConfig },
+	{   3,  91,  77, 12, cbToggleAutoSaveConfig },
 	{ 389, 132,  90, 12, cbConfigInterpolation },
 	{ 389, 145, 107, 12, cbConfigVolRamp },
 	{ 389, 158 , 84, 12, cbConfigDither },
--- a/src/ft2_config.c
+++ b/src/ft2_config.c
@@ -1106,14 +1106,14 @@
 	showPushButton(PB_CONFIG_SAVE);
 	showPushButton(PB_CONFIG_EXIT);
 
-	textOutShadow(5,    4, PAL_FORGRND, PAL_DSKTOP2, "Configuration:");
-	textOutShadow(22,  20, PAL_FORGRND, PAL_DSKTOP2, "I/O devices");
-	textOutShadow(22,  36, PAL_FORGRND, PAL_DSKTOP2, "Layout");
-	textOutShadow(22,  52, PAL_FORGRND, PAL_DSKTOP2, "Miscellaneous");
+	textOutShadow(4,   4, PAL_FORGRND, PAL_DSKTOP2, "Configuration:");
+	textOutShadow(21, 19, PAL_FORGRND, PAL_DSKTOP2, "I/O devices");
+	textOutShadow(21, 35, PAL_FORGRND, PAL_DSKTOP2, "Layout");
+	textOutShadow(21, 51, PAL_FORGRND, PAL_DSKTOP2, "Miscellaneous");
 #ifdef HAS_MIDI
-	textOutShadow(22,  68, PAL_FORGRND, PAL_DSKTOP2, "MIDI input");
+	textOutShadow(21, 67, PAL_FORGRND, PAL_DSKTOP2, "MIDI input");
 #endif
-	textOutShadow(19,  92, PAL_FORGRND, PAL_DSKTOP2, "Auto save");
+	textOutShadow(20, 93, PAL_FORGRND, PAL_DSKTOP2, "Auto save");
 
 	switch (editor.currConfigScreen)
 	{
--- a/src/ft2_diskop.c
+++ b/src/ft2_diskop.c
@@ -104,7 +104,7 @@
 	if (stat(fileNameU, &st) != 0)
 		return 0;
 
-	fSize = (int64_t)(st.st_size);
+	fSize = (int64_t)st.st_size;
 #endif
 	if (fSize < 0)
 		fSize = 0;
--- a/src/ft2_header.h
+++ b/src/ft2_header.h
@@ -12,7 +12,7 @@
 #endif
 #include "ft2_replayer.h"
 
-#define PROG_VER_STR "1.14"
+#define PROG_VER_STR "1.15"
 
 // do NOT change these! It will only mess things up...
 
--- a/src/ft2_help.c
+++ b/src/ft2_help.c
@@ -417,13 +417,13 @@
 
 	showScrollBar(SB_HELP_SCROLL);
 
-	textOutShadow(4,   3, PAL_FORGRND, PAL_DSKTOP2, "Subjects:");
-	textOutShadow(20, 17, PAL_FORGRND, PAL_DSKTOP2, "Features");
-	textOutShadow(20, 32, PAL_FORGRND, PAL_DSKTOP2, "Effects");
-	textOutShadow(20, 47, PAL_FORGRND, PAL_DSKTOP2, "Keyboard");
-	textOutShadow(20, 62, PAL_FORGRND, PAL_DSKTOP2, "How to use FT2");
-	textOutShadow(20, 77, PAL_FORGRND, PAL_DSKTOP2, "Problems/FAQ");
-	textOutShadow(20, 92, PAL_FORGRND, PAL_DSKTOP2, "Known bugs");
+	textOutShadow(4,   4, PAL_FORGRND, PAL_DSKTOP2, "Subjects:");
+	textOutShadow(21, 19, PAL_FORGRND, PAL_DSKTOP2, "Features");
+	textOutShadow(21, 35, PAL_FORGRND, PAL_DSKTOP2, "Effects");
+	textOutShadow(21, 51, PAL_FORGRND, PAL_DSKTOP2, "Keyboard");
+	textOutShadow(21, 67, PAL_FORGRND, PAL_DSKTOP2, "How to use FT2");
+	textOutShadow(21, 83, PAL_FORGRND, PAL_DSKTOP2, "Problems/FAQ");
+	textOutShadow(21, 99, PAL_FORGRND, PAL_DSKTOP2, "Known bugs");
 
 	writeHelp();
 }
--- a/src/ft2_inst_ed.c
+++ b/src/ft2_inst_ed.c
@@ -397,7 +397,7 @@
 	instrTyp *ins = getCurDispInstr();
 
 	sprintf(str, "%02d", ins->envVRepS);
-	textOutFixed(382, 234, PAL_FORGRND, PAL_DESKTOP, str);
+	textOutFixed(382, 233, PAL_FORGRND, PAL_DESKTOP, str);
 }
 
 static void drawVolEnvRepE(void)
@@ -415,7 +415,7 @@
 	instrTyp *ins = getCurDispInstr();
 
 	sprintf(str, "%02d", ins->envPSust);
-	textOutFixed(382, 294, PAL_FORGRND, PAL_DESKTOP, str);
+	textOutFixed(382, 293, PAL_FORGRND, PAL_DESKTOP, str);
 }
 
 static void drawPanEnvRepS(void)
@@ -424,7 +424,7 @@
 	instrTyp *ins = getCurDispInstr();
 
 	sprintf(str, "%02d", ins->envPRepS);
-	textOutFixed(382, 321, PAL_FORGRND, PAL_DESKTOP, str);
+	textOutFixed(382, 320, PAL_FORGRND, PAL_DESKTOP, str);
 }
 
 static void drawPanEnvRepE(void)
@@ -433,7 +433,7 @@
 	instrTyp *ins = getCurDispInstr();
 
 	sprintf(str, "%02d", ins->envPRepE);
-	textOutFixed(382, 335, PAL_FORGRND, PAL_DESKTOP, str);
+	textOutFixed(382, 334, PAL_FORGRND, PAL_DESKTOP, str);
 }
 
 static void drawVolume(void)
@@ -445,7 +445,7 @@
 	else
 		s = &instr[editor.curInstr]->samp[editor.curSmp];
 
-	hexOutBg(505, 178, PAL_FORGRND, PAL_DESKTOP, s->vol, 2);
+	hexOutBg(505, 177, PAL_FORGRND, PAL_DESKTOP, s->vol, 2);
 }
 
 static void drawPanning(void)
@@ -457,7 +457,7 @@
 	else
 		s = &instr[editor.curInstr]->samp[editor.curSmp];
 
-	hexOutBg(505, 192, PAL_FORGRND, PAL_DESKTOP, s->pan, 2);
+	hexOutBg(505, 191, PAL_FORGRND, PAL_DESKTOP, s->pan, 2);
 }
 
 static void drawFineTune(void)
@@ -2262,17 +2262,17 @@
 
 	textOutShadow(20,  176, PAL_FORGRND, PAL_DSKTOP2, "Volume envelope:");
 	textOutShadow(153, 176, PAL_FORGRND, PAL_DSKTOP2, "Predef.");
-	textOutShadow(358, 193, PAL_FORGRND, PAL_DSKTOP2, "Sustain:");
+	textOutShadow(358, 194, PAL_FORGRND, PAL_DSKTOP2, "Sustain:");
 	textOutShadow(342, 206, PAL_FORGRND, PAL_DSKTOP2, "Point");
 	textOutShadow(358, 219, PAL_FORGRND, PAL_DSKTOP2, "Env.loop:");
-	textOutShadow(342, 234, PAL_FORGRND, PAL_DSKTOP2, "Start");
+	textOutShadow(342, 233, PAL_FORGRND, PAL_DSKTOP2, "Start");
 	textOutShadow(342, 247, PAL_FORGRND, PAL_DSKTOP2, "End");
 	textOutShadow(20,  263, PAL_FORGRND, PAL_DSKTOP2, "Panning envelope:");
 	textOutShadow(152, 263, PAL_FORGRND, PAL_DSKTOP2, "Predef.");
-	textOutShadow(358, 280, PAL_FORGRND, PAL_DSKTOP2, "Sustain:");
+	textOutShadow(358, 281, PAL_FORGRND, PAL_DSKTOP2, "Sustain:");
 	textOutShadow(342, 293, PAL_FORGRND, PAL_DSKTOP2, "Point");
 	textOutShadow(358, 306, PAL_FORGRND, PAL_DSKTOP2, "Env.loop:");
-	textOutShadow(342, 321, PAL_FORGRND, PAL_DSKTOP2, "Start");
+	textOutShadow(342, 320, PAL_FORGRND, PAL_DSKTOP2, "Start");
 	textOutShadow(342, 334, PAL_FORGRND, PAL_DSKTOP2, "End");
 	textOutShadow(443, 177, PAL_FORGRND, PAL_DSKTOP2, "Volume");
 	textOutShadow(443, 191, PAL_FORGRND, PAL_DSKTOP2, "Panning");
@@ -2609,8 +2609,8 @@
 	textOutShadow(4,   96,  PAL_FORGRND, PAL_DSKTOP2, "Instrument Editor Extension:");
 	textOutShadow(20,  114, PAL_FORGRND, PAL_DSKTOP2, "Instrument MIDI enable");
 	textOutShadow(189, 114, PAL_FORGRND, PAL_DSKTOP2, "Mute computer");
-	textOutShadow(4,   133, PAL_FORGRND, PAL_DSKTOP2, "MIDI transmit channel");
-	textOutShadow(4,   147, PAL_FORGRND, PAL_DSKTOP2, "MIDI program");
+	textOutShadow(4,   132, PAL_FORGRND, PAL_DSKTOP2, "MIDI transmit channel");
+	textOutShadow(4,   146, PAL_FORGRND, PAL_DSKTOP2, "MIDI program");
 	textOutShadow(4,   160, PAL_FORGRND, PAL_DSKTOP2, "Bender range (halftones)");
 
 	if (ins == NULL)
@@ -3268,9 +3268,9 @@
 				{
 					s->typ &= ~32;
 
-					s->len /= 2;
-					s->repL /= 2;
-					s->repS /= 2;
+					s->len >>= 1;
+					s->repL >>= 1;
+					s->repS >>= 1;
 
 					newPtr = (int8_t *)realloc(s->origPek, s->len + LOOP_FIX_LEN);
 					if (newPtr != NULL)
@@ -3348,11 +3348,11 @@
 				{
 					ins->vibSweep = ih_PATWave.vibSweep;
 
-					ins->vibRate = (ih_PATWave.vibRate + 2) / 4;
+					ins->vibRate = (ih_PATWave.vibRate + 2) >> 2;
 					if (ins->vibRate > 0x3F)
 						ins->vibRate = 0x3F;
 
-					ins->vibDepth = (ih_PATWave.vibDepth + 1) / 2;
+					ins->vibDepth = (ih_PATWave.vibDepth + 1) >> 1;
 					if (ins->vibDepth > 0x0F)
 						ins->vibDepth = 0x0F;
 				}
--- a/src/ft2_mix.c
+++ b/src/ft2_mix.c
@@ -36,13 +36,10 @@
 
 static void mix8bNoLoop(voice_t *v, uint32_t numSamples)
 {
-	const int8_t *CDA_LinearAdr;
-	bool mixInMono;
-	int32_t realPos, sample, *audioMixL, *audioMixR, CDA_BytesLeft;
-	register const int8_t *smpPtr;
-	register int32_t CDA_LVol, CDA_RVol;
-	register uint32_t pos, delta;
-	uint32_t i, samplesToMix;
+	const int8_t *CDA_LinearAdr, *smpPtr;
+	int32_t realPos, sample, *audioMixL, *audioMixR;
+	int32_t CDA_BytesLeft, CDA_LVol, CDA_RVol, i, samplesToMix;
+	uint32_t pos;
 
 	GET_VOL
 
@@ -103,13 +100,10 @@
 
 static void mix8bLoop(voice_t *v, uint32_t numSamples)
 {
-	const int8_t *CDA_LinearAdr;
-	bool mixInMono;
-	int32_t realPos, sample, *audioMixL, *audioMixR, CDA_BytesLeft;
-	register const int8_t *smpPtr;
-	register int32_t CDA_LVol, CDA_RVol;
-	register uint32_t pos, delta;
-	uint32_t i, samplesToMix;
+	const int8_t *CDA_LinearAdr, *smpPtr;;
+	int32_t realPos, sample, *audioMixL, *audioMixR;
+	int32_t CDA_BytesLeft, CDA_LVol, CDA_RVol, i, samplesToMix;
+	uint32_t pos;
 
 	GET_VOL
 
@@ -170,13 +164,10 @@
 
 static void mix8bBidiLoop(voice_t *v, uint32_t numSamples)
 {
-	bool mixInMono;
-	const int8_t *CDA_LinearAdr, *CDA_LinAdrRev;
-	int32_t realPos, sample, *audioMixL, *audioMixR, CDA_BytesLeft;
-	register const int8_t *smpPtr;
-	register int32_t CDA_LVol, CDA_RVol, CDA_IPValL, CDA_IPValH;
-	register uint32_t pos;
-	uint32_t delta, i, samplesToMix;
+	const int8_t *CDA_LinearAdr, *CDA_LinAdrRev, *smpPtr;
+	int32_t realPos, sample, *audioMixL, *audioMixR;
+	int32_t CDA_BytesLeft, CDA_LVol, CDA_RVol, i, samplesToMix;
+	uint32_t pos, delta;
 
 	GET_VOL
 
@@ -237,13 +228,10 @@
 
 static void mix8bNoLoopIntrp(voice_t *v, uint32_t numSamples)
 {
-	const int8_t *CDA_LinearAdr;
-	bool mixInMono;
-	int32_t realPos, sample, sample2, *audioMixL, *audioMixR, CDA_BytesLeft;
-	register const int8_t *smpPtr;
-	register int32_t CDA_LVol, CDA_RVol;
-	register uint32_t pos, delta;
-	uint32_t i, samplesToMix;
+	const int8_t *CDA_LinearAdr, *smpPtr;
+	int32_t realPos, sample, sample2, *audioMixL, *audioMixR;
+	int32_t CDA_BytesLeft, CDA_LVol, CDA_RVol, i, samplesToMix;
+	uint32_t pos;
 #ifndef LERPMIX
 	int32_t sample3, sample4;
 #endif
@@ -307,13 +295,10 @@
 
 static void mix8bLoopIntrp(voice_t *v, uint32_t numSamples)
 {
-	const int8_t *CDA_LinearAdr;
-	bool mixInMono;
-	int32_t realPos, sample, sample2, *audioMixL, *audioMixR, CDA_BytesLeft;
-	register const int8_t *smpPtr;
-	register int32_t CDA_LVol, CDA_RVol;
-	register uint32_t pos, delta;
-	uint32_t i, samplesToMix;
+	const int8_t *CDA_LinearAdr, *smpPtr;
+	int32_t realPos, sample, sample2, *audioMixL, *audioMixR;
+	int32_t CDA_BytesLeft, CDA_LVol, CDA_RVol, i, samplesToMix;
+	uint32_t pos;
 #ifndef LERPMIX
 	int32_t sample3, sample4;
 #endif
@@ -377,13 +362,10 @@
 
 static void mix8bBidiLoopIntrp(voice_t *v, uint32_t numSamples)
 {
-	bool mixInMono;
-	const int8_t *CDA_LinearAdr, *CDA_LinAdrRev;
-	int32_t realPos, sample, sample2, *audioMixL, *audioMixR, CDA_BytesLeft;
-	register const int8_t *smpPtr;
-	register int32_t CDA_LVol, CDA_RVol, CDA_IPValL, CDA_IPValH;
-	register uint32_t pos;
-	uint32_t delta, i, samplesToMix;
+	const int8_t *CDA_LinearAdr, *CDA_LinAdrRev, *smpPtr;
+	int32_t realPos, sample, sample2, *audioMixL, *audioMixR;
+	int32_t CDA_BytesLeft, CDA_LVol, CDA_RVol, i, samplesToMix;
+	uint32_t pos, delta;
 #ifndef LERPMIX
 	int32_t sample3, sample4;
 #endif
@@ -448,13 +430,10 @@
 
 static void mix8bRampNoLoop(voice_t *v, uint32_t numSamples)
 {
-	const int8_t *CDA_LinearAdr;
-	bool mixInMono;
-	int32_t realPos, sample, *audioMixL, *audioMixR, CDA_BytesLeft, CDA_LVolIP, CDA_RVolIP;
-	register const int8_t *smpPtr;
-	register int32_t CDA_LVol, CDA_RVol;
-	register uint32_t pos, delta;
-	uint32_t i, samplesToMix;
+	const int8_t *CDA_LinearAdr, *smpPtr;
+	int32_t realPos, sample, *audioMixL, *audioMixR, CDA_BytesLeft;
+	int32_t CDA_LVolIP, CDA_RVolIP, CDA_LVol, CDA_RVol, i, samplesToMix;
+	uint32_t pos;
 
 	if ((v->SLVol1 | v->SRVol1 | v->SLVol2 | v->SRVol2) == 0)
 	{
@@ -522,13 +501,10 @@
 
 static void mix8bRampLoop(voice_t *v, uint32_t numSamples)
 {
-	const int8_t *CDA_LinearAdr;
-	bool mixInMono;
-	int32_t realPos, sample, *audioMixL, *audioMixR, CDA_BytesLeft, CDA_LVolIP, CDA_RVolIP;
-	register const int8_t *smpPtr;
-	register int32_t CDA_LVol, CDA_RVol;
-	register uint32_t pos, delta;
-	uint32_t i, samplesToMix;
+	const int8_t *CDA_LinearAdr, *smpPtr;;
+	int32_t realPos, sample, *audioMixL, *audioMixR, CDA_BytesLeft;
+	int32_t CDA_LVolIP, CDA_RVolIP, CDA_LVol, CDA_RVol, i, samplesToMix;
+	uint32_t pos;
 
 	if ((v->SLVol1 | v->SRVol1 | v->SLVol2 | v->SRVol2) == 0)
 	{
@@ -596,13 +572,10 @@
 
 static void mix8bRampBidiLoop(voice_t *v, uint32_t numSamples)
 {
-	bool mixInMono;
-	const int8_t *CDA_LinearAdr, *CDA_LinAdrRev;
-	int32_t realPos, sample, *audioMixL, *audioMixR, CDA_BytesLeft, CDA_LVolIP, CDA_RVolIP;
-	register const int8_t *smpPtr;
-	register int32_t CDA_LVol, CDA_RVol, CDA_IPValL, CDA_IPValH;
-	register uint32_t pos;
-	uint32_t delta, i, samplesToMix;
+	const int8_t *CDA_LinearAdr, *CDA_LinAdrRev, *smpPtr;
+	int32_t realPos, sample, *audioMixL, *audioMixR, CDA_BytesLeft;
+	int32_t CDA_LVolIP, CDA_RVolIP, CDA_LVol, CDA_RVol, i, samplesToMix;
+	uint32_t pos, delta;
 
 	if ((v->SLVol1 | v->SRVol1 | v->SLVol2 | v->SRVol2) == 0)
 	{
@@ -671,13 +644,10 @@
 
 static void mix8bRampNoLoopIntrp(voice_t *v, uint32_t numSamples)
 {
-	const int8_t *CDA_LinearAdr;
-	bool mixInMono;
-	int32_t realPos, sample, sample2, *audioMixL, *audioMixR, CDA_BytesLeft, CDA_LVolIP, CDA_RVolIP;
-	register const int8_t *smpPtr;
-	register int32_t CDA_LVol, CDA_RVol;
-	register uint32_t pos, delta;
-	uint32_t i, samplesToMix;
+	const int8_t *CDA_LinearAdr, *smpPtr;
+	int32_t realPos, sample, sample2, *audioMixL, *audioMixR, CDA_BytesLeft;
+	int32_t CDA_LVolIP, CDA_RVolIP, CDA_LVol, CDA_RVol, i, samplesToMix;
+	uint32_t pos;
 #ifndef LERPMIX
 	int32_t sample3, sample4;
 #endif
@@ -748,13 +718,10 @@
 
 static void mix8bRampLoopIntrp(voice_t *v, uint32_t numSamples)
 {
-	const int8_t *CDA_LinearAdr;
-	bool mixInMono;
-	int32_t realPos, sample, sample2, *audioMixL, *audioMixR, CDA_BytesLeft, CDA_LVolIP, CDA_RVolIP;
-	register const int8_t *smpPtr;
-	register int32_t CDA_LVol, CDA_RVol;
-	register uint32_t pos, delta;
-	uint32_t i, samplesToMix;
+	const int8_t *CDA_LinearAdr, *smpPtr;
+	int32_t realPos, sample, sample2, *audioMixL, *audioMixR, CDA_BytesLeft;
+	int32_t CDA_LVolIP, CDA_RVolIP, CDA_LVol, CDA_RVol, i, samplesToMix;
+	uint32_t pos;
 #ifndef LERPMIX
 	int32_t sample3, sample4;
 #endif
@@ -825,13 +792,10 @@
 
 static void mix8bRampBidiLoopIntrp(voice_t *v, uint32_t numSamples)
 {
-	bool mixInMono;
-	const int8_t *CDA_LinearAdr, *CDA_LinAdrRev;
-	int32_t realPos, sample, sample2, *audioMixL, *audioMixR, CDA_BytesLeft, CDA_LVolIP, CDA_RVolIP;
-	register const int8_t *smpPtr;
-	register int32_t CDA_LVol, CDA_RVol, CDA_IPValL, CDA_IPValH;
-	register uint32_t pos;
-	uint32_t delta, i, samplesToMix;
+	const int8_t *CDA_LinearAdr, *CDA_LinAdrRev, *smpPtr;
+	int32_t realPos, sample, sample2, *audioMixL, *audioMixR, CDA_BytesLeft;
+	int32_t CDA_LVolIP, CDA_RVolIP, CDA_LVol, CDA_RVol, i, samplesToMix;
+	uint32_t pos, delta;
 #ifndef LERPMIX
 	int32_t sample3, sample4;
 #endif
@@ -908,13 +872,10 @@
 
 static void mix16bNoLoop(voice_t *v, uint32_t numSamples)
 {
-	bool mixInMono;
-	const int16_t *CDA_LinearAdr;
-	int32_t realPos, sample, *audioMixL, *audioMixR, CDA_BytesLeft;
-	register const int16_t *smpPtr;
-	register int32_t CDA_LVol, CDA_RVol;
-	register uint32_t pos, delta;
-	uint32_t i, samplesToMix;
+	const int16_t *CDA_LinearAdr, *smpPtr;
+	int32_t realPos, sample, *audioMixL, *audioMixR;
+	int32_t CDA_BytesLeft, CDA_LVol, CDA_RVol, i, samplesToMix;
+	uint32_t pos;
 
 	GET_VOL
 
@@ -975,13 +936,10 @@
 
 static void mix16bLoop(voice_t *v, uint32_t numSamples)
 {
-	bool mixInMono;
-	const int16_t *CDA_LinearAdr;
-	int32_t realPos, sample, *audioMixL, *audioMixR, CDA_BytesLeft;
-	register const int16_t *smpPtr;
-	register int32_t CDA_LVol, CDA_RVol;
-	register uint32_t pos, delta;
-	uint32_t i, samplesToMix;
+	const int16_t *CDA_LinearAdr, *smpPtr;
+	int32_t realPos, sample, *audioMixL, *audioMixR;
+	int32_t CDA_BytesLeft, CDA_LVol, CDA_RVol, i, samplesToMix;
+	uint32_t pos;
 
 	GET_VOL
 
@@ -1042,13 +1000,10 @@
 
 static void mix16bBidiLoop(voice_t *v, uint32_t numSamples)
 {
-	bool mixInMono;
-	const int16_t *CDA_LinearAdr, *CDA_LinAdrRev;
-	int32_t realPos, sample, *audioMixL, *audioMixR, CDA_BytesLeft;
-	register const int16_t *smpPtr;
-	register int32_t CDA_LVol, CDA_RVol, CDA_IPValL, CDA_IPValH;
-	register uint32_t pos;
-	uint32_t delta, i, samplesToMix;
+	const int16_t *CDA_LinearAdr, *CDA_LinAdrRev, *smpPtr;
+	int32_t realPos, sample, *audioMixL, *audioMixR;
+	int32_t  CDA_BytesLeft, CDA_LVol, CDA_RVol, i, samplesToMix;
+	uint32_t pos, delta;
 
 	GET_VOL
 
@@ -1110,13 +1065,10 @@
 
 static void mix16bNoLoopIntrp(voice_t *v, uint32_t numSamples)
 {
-	bool mixInMono;
-	const int16_t *CDA_LinearAdr;
-	int32_t realPos, sample, sample2, *audioMixL, *audioMixR, CDA_BytesLeft;
-	register const int16_t *smpPtr;
-	register int32_t CDA_LVol, CDA_RVol;
-	register uint32_t pos, delta;
-	uint32_t i, samplesToMix;
+	const int16_t *CDA_LinearAdr, *smpPtr;
+	int32_t realPos, sample, sample2, *audioMixL, *audioMixR;
+	int32_t CDA_BytesLeft, CDA_LVol, CDA_RVol, i, samplesToMix;
+	uint32_t pos;
 #ifndef LERPMIX
 	int32_t sample3, sample4;
 #endif
@@ -1180,13 +1132,10 @@
 
 static void mix16bLoopIntrp(voice_t *v, uint32_t numSamples)
 {
-	bool mixInMono;
-	const int16_t *CDA_LinearAdr;
-	int32_t realPos, sample, sample2, *audioMixL, *audioMixR, CDA_BytesLeft;
-	register const int16_t *smpPtr;
-	register int32_t CDA_LVol, CDA_RVol;
-	register uint32_t pos, delta;
-	uint32_t i, samplesToMix;
+	const int16_t *CDA_LinearAdr, *smpPtr;
+	int32_t realPos, sample, sample2, *audioMixL, *audioMixR;
+	int32_t CDA_BytesLeft, CDA_LVol, CDA_RVol, i, samplesToMix;
+	uint32_t pos;
 #ifndef LERPMIX
 	int32_t sample3, sample4;
 #endif
@@ -1250,13 +1199,10 @@
 
 static void mix16bBidiLoopIntrp(voice_t *v, uint32_t numSamples)
 {
-	bool mixInMono;
-	const int16_t *CDA_LinearAdr, *CDA_LinAdrRev;
-	int32_t realPos, sample, sample2, *audioMixL, *audioMixR, CDA_BytesLeft;
-	register const int16_t *smpPtr;
-	register int32_t CDA_LVol, CDA_RVol, CDA_IPValL, CDA_IPValH;
-	register uint32_t pos;
-	uint32_t delta, i, samplesToMix;
+	const int16_t *CDA_LinearAdr, *CDA_LinAdrRev, *smpPtr;
+	int32_t realPos, sample, sample2, *audioMixL, *audioMixR;
+	int32_t CDA_BytesLeft, CDA_LVol, CDA_RVol, i, samplesToMix;
+	uint32_t pos, delta;
 #ifndef LERPMIX
 	int32_t sample3, sample4;
 #endif
@@ -1321,13 +1267,10 @@
 
 static void mix16bRampNoLoop(voice_t *v, uint32_t numSamples)
 {
-	bool mixInMono;
-	const int16_t *CDA_LinearAdr;
-	int32_t realPos, sample, *audioMixL, *audioMixR, CDA_BytesLeft, CDA_LVolIP, CDA_RVolIP;
-	register const int16_t *smpPtr;
-	register int32_t CDA_LVol, CDA_RVol;
-	register uint32_t pos, delta;
-	uint32_t i, samplesToMix;
+	const int16_t *CDA_LinearAdr, *smpPtr;
+	int32_t realPos, sample, *audioMixL, *audioMixR, CDA_BytesLeft;
+	int32_t CDA_LVolIP, CDA_RVolIP, CDA_LVol, CDA_RVol, i, samplesToMix;
+	uint32_t pos;
 
 	if ((v->SLVol1 | v->SRVol1 | v->SLVol2 | v->SRVol2) == 0)
 	{
@@ -1395,13 +1338,10 @@
 
 static void mix16bRampLoop(voice_t *v, uint32_t numSamples)
 {
-	bool mixInMono;
-	const int16_t *CDA_LinearAdr;
-	int32_t realPos, sample, *audioMixL, *audioMixR, CDA_BytesLeft, CDA_LVolIP, CDA_RVolIP;
-	register const int16_t *smpPtr;
-	register int32_t CDA_LVol, CDA_RVol;
-	register uint32_t pos, delta;
-	uint32_t i, samplesToMix;
+	const int16_t *CDA_LinearAdr, *smpPtr;
+	int32_t realPos, sample, *audioMixL, *audioMixR, CDA_BytesLeft;
+	int32_t CDA_LVolIP, CDA_RVolIP, CDA_LVol, CDA_RVol, i, samplesToMix;
+	uint32_t pos;
 
 	if ((v->SLVol1 | v->SRVol1 | v->SLVol2 | v->SRVol2) == 0)
 	{
@@ -1469,13 +1409,10 @@
 
 static void mix16bRampBidiLoop(voice_t *v, uint32_t numSamples)
 {
-	bool mixInMono;
-	const int16_t *CDA_LinearAdr, *CDA_LinAdrRev;
-	int32_t realPos, sample, *audioMixL, *audioMixR, CDA_BytesLeft, CDA_LVolIP, CDA_RVolIP;
-	register const int16_t *smpPtr;
-	register int32_t CDA_LVol, CDA_RVol, CDA_IPValL, CDA_IPValH;
-	register uint32_t pos;
-	uint32_t delta, i, samplesToMix;
+	const int16_t *CDA_LinearAdr, *CDA_LinAdrRev, *smpPtr;
+	int32_t realPos, sample, *audioMixL, *audioMixR, CDA_BytesLeft;
+	int32_t CDA_LVolIP, CDA_RVolIP, CDA_LVol, CDA_RVol, i, samplesToMix;
+	uint32_t pos, delta;
 
 	if ((v->SLVol1 | v->SRVol1 | v->SLVol2 | v->SRVol2) == 0)
 	{
@@ -1544,13 +1481,10 @@
 
 static void mix16bRampNoLoopIntrp(voice_t *v, uint32_t numSamples)
 {
-	bool mixInMono;
-	const int16_t *CDA_LinearAdr;
-	int32_t realPos, sample, sample2, *audioMixL, *audioMixR, CDA_BytesLeft, CDA_LVolIP, CDA_RVolIP;
-	register const int16_t *smpPtr;
-	register int32_t  CDA_LVol, CDA_RVol;
-	register uint32_t pos, delta;
-	uint32_t i, samplesToMix;
+	const int16_t *CDA_LinearAdr, *smpPtr;
+	int32_t realPos, sample, sample2, *audioMixL, *audioMixR, CDA_BytesLeft;
+	int32_t CDA_LVolIP, CDA_RVolIP, CDA_LVol, CDA_RVol, i, samplesToMix;
+	uint32_t pos;
 #ifndef LERPMIX
 	int32_t sample3, sample4;
 #endif
@@ -1621,13 +1555,10 @@
 
 static void mix16bRampLoopIntrp(voice_t *v, uint32_t numSamples)
 {
-	bool mixInMono;
-	const int16_t *CDA_LinearAdr;
-	int32_t realPos, sample, sample2, *audioMixL, *audioMixR, CDA_BytesLeft, CDA_LVolIP, CDA_RVolIP;
-	register const int16_t *smpPtr;
-	register int32_t CDA_LVol, CDA_RVol;
-	register uint32_t pos, delta;
-	uint32_t i, samplesToMix;
+	const int16_t *CDA_LinearAdr, *smpPtr;
+	int32_t realPos, sample, sample2, *audioMixL, *audioMixR, CDA_BytesLeft;
+	int32_t CDA_LVolIP, CDA_RVolIP, CDA_LVol, CDA_RVol, i, samplesToMix;
+	uint32_t pos;
 #ifndef LERPMIX
 	int32_t sample3, sample4;
 #endif
@@ -1698,13 +1629,10 @@
 
 static void mix16bRampBidiLoopIntrp(voice_t *v, uint32_t numSamples)
 {
-	bool mixInMono;
-	const int16_t *CDA_LinearAdr, *CDA_LinAdrRev;
-	int32_t realPos, sample, sample2, *audioMixL, *audioMixR, CDA_BytesLeft, CDA_LVolIP, CDA_RVolIP;
-	register const int16_t *smpPtr;
-	register int32_t CDA_LVol, CDA_RVol, CDA_IPValL, CDA_IPValH;
-	register uint32_t pos;
-	uint32_t delta, i, samplesToMix;
+	const int16_t *CDA_LinearAdr, *CDA_LinAdrRev, *smpPtr;
+	int32_t realPos, sample, sample2, *audioMixL, *audioMixR, CDA_BytesLeft;
+	int32_t CDA_LVolIP, CDA_RVolIP, CDA_LVol, CDA_RVol, i, samplesToMix;
+	uint32_t pos, delta;
 #ifndef LERPMIX
 	int32_t sample3, sample4;
 #endif
--- a/src/ft2_mix_macros.h
+++ b/src/ft2_mix_macros.h
@@ -14,21 +14,24 @@
 	v->SLVol2 = CDA_LVol; \
 	v->SRVol2 = CDA_RVol; \
 
-#define GET_DELTA delta = v->SFrq;
+#define GET_DELTA \
+	const uint32_t delta = v->SFrq;
 
 #define GET_MIXER_VARS \
+	const int32_t SFrqRev = v->SFrqRev; \
 	audioMixL = audio.mixBufferL; \
 	audioMixR = audio.mixBufferR; \
-	mixInMono = (CDA_LVol == CDA_RVol); \
+	const bool mixInMono = (CDA_LVol == CDA_RVol); \
 	realPos = v->SPos; \
 	pos = v->SPosDec; \
 
 #define GET_MIXER_VARS_RAMP \
+	const int32_t SFrqRev = v->SFrqRev; \
 	audioMixL = audio.mixBufferL; \
 	audioMixR = audio.mixBufferR; \
 	CDA_LVolIP = v->SLVolIP; \
 	CDA_RVolIP = v->SRVolIP; \
-	mixInMono = (v->SLVol2 == v->SRVol2) && (CDA_LVolIP == CDA_RVolIP); \
+	const bool mixInMono = (v->SLVol2 == v->SRVol2) && (CDA_LVolIP == CDA_RVolIP); \
 	realPos = v->SPos; \
 	pos = v->SPosDec; \
 
@@ -240,12 +243,15 @@
 /* ----------------------------------------------------------------------- */
 
 #define LIMIT_MIX_NUM \
-	i = (v->SLen - 1) - realPos; \
-	if (i > 65535) \
-		i = 65535; \
+	samplesToMix = (v->SLen - 1) - realPos; \
+	if (samplesToMix > 65535) \
+		samplesToMix = 65535; \
 	\
-	samplesToMix = (((((uint64_t)i << 16) | (pos ^ 0xFFFF)) * v->SFrqRev) >> 32) + 1; \
-	if (samplesToMix > (uint32_t)CDA_BytesLeft) \
+	samplesToMix = (samplesToMix << 16) | (pos ^ 0xFFFF); \
+	samplesToMix = ((int64_t)samplesToMix * SFrqRev) >> 32; \
+	samplesToMix++; \
+	\
+	if (samplesToMix > CDA_BytesLeft) \
 		samplesToMix = CDA_BytesLeft; \
 
 #define LIMIT_MIX_NUM_RAMP \
@@ -262,7 +268,7 @@
 	} \
 	else \
 	{ \
-		if (samplesToMix > (uint32_t)v->SVolIPLen) \
+		if (samplesToMix > v->SVolIPLen) \
 			samplesToMix = v->SVolIPLen; \
 		\
 		v->SVolIPLen -= samplesToMix; \
@@ -272,7 +278,6 @@
 	if (v->backwards) \
 	{ \
 		delta = 0 - v->SFrq; \
-		CDA_IPValH = (int32_t)delta >> 16; \
 		assert(realPos >= v->SRepS && realPos < v->SLen); \
 		realPos = ~realPos; \
 		smpPtr = CDA_LinAdrRev + realPos; \
@@ -281,19 +286,18 @@
 	else \
 	{ \
 		delta = v->SFrq; \
-		CDA_IPValH = delta >> 16; \
 		assert(realPos >= 0 && realPos < v->SLen); \
 		smpPtr = CDA_LinearAdr + realPos; \
 	} \
 	\
-	CDA_IPValL = delta & 0xFFFF; \
+	const int32_t CDA_IPValH = (int32_t)delta >> 16; \
+	const int32_t CDA_IPValL = delta & 0xFFFF; \
 
 #define END_BIDI \
 	if (v->backwards) \
 	{ \
 		pos ^= 0xFFFF; \
-		realPos = (int32_t)(smpPtr - CDA_LinAdrRev); \
-		realPos = ~realPos; \
+		realPos = ~(int32_t)(smpPtr - CDA_LinAdrRev); \
 	} \
 	else \
 	{ \
--- a/src/ft2_module_loader.c
+++ b/src/ft2_module_loader.c
@@ -184,7 +184,7 @@
 
 	if (IS_ID("M.K.", id) || IS_ID("M!K!", id) || IS_ID("NSMS", id) || IS_ID("LARD", id) || IS_ID("PATT", id))
 	{
-		modFormat = FORMAT_MK; // ProTracker or compatible	
+		modFormat = FORMAT_MK; // ProTracker or compatible
 	}
 	else if (id[1] == 'C' && id[2] == 'H' && id[3] == 'N')
 	{
@@ -200,11 +200,6 @@
 	{
 		modFormat = FORMAT_FLT; // StarTrekker (4-channel modules only)
 	}
-	else if (IS_ID("FLT8", id))
-	{
-		modFormat = FORMAT_FLT; // StarTrekker (4-channel modules only)
-		*numChannels = 8;
-	}
 	else if (IS_ID("N.T.", id))
 	{
 		modFormat = FORMAT_NT; // NoiseTracker
@@ -364,23 +359,23 @@
 		*/
 		modFormat = FORMAT_STK;
 
-		if (h_MOD15.repS == 0)
-			h_MOD15.repS = 120;
+		if (songTmp.repS == 0)
+			songTmp.repS = 120;
 
-		// jjk55.mod by Jesper Kyd has a bogus STK tempo value that should be ignored
+		// jjk55.mod by Jesper Kyd has a bogus STK tempo value that should be ignored (hackish!)
 		if (!strcmp("jjk55", h_MOD31.name))
-			h_MOD15.repS = 120;
+			songTmp.repS = 120;
 
 		// The "restart pos" field in STK is the inital tempo (must be converted to BPM first)
-		if (h_MOD15.repS != 120) // 120 is a special case and means 50Hz (125BPM)
+		if (songTmp.repS != 120) // 120 is a special case and means 50Hz (125BPM)
 		{
-			if (h_MOD15.repS > 220)
-				h_MOD15.repS = 220;
+			if (songTmp.repS > 220)
+				songTmp.repS = 220;
 
 			// convert UST tempo to BPM
 			uint16_t ciaPeriod = (240 - songTmp.repS) * 122;
 			double dHz = 709379.0 / ciaPeriod;
-			int32_t BPM = (int32_t)((dHz * (125.0 / 50.0)) + 0.5);
+			int32_t BPM = (int32_t)((dHz * 2.5) + 0.5);
 
 			songTmp.speed = (uint16_t)BPM;
 		}
@@ -450,7 +445,7 @@
 				ton->effTyp = bytes[2] & 0x0F;
 				ton->eff = bytes[3];
 
-				if (mightBeSTK)
+				if (modFormat == FORMAT_STK)
 				{
 					if (ton->effTyp == 0xC || ton->effTyp == 0xD || ton->effTyp == 0xE)
 					{
@@ -519,7 +514,7 @@
 	}
 
 	// pattern command conversion for non-PT formats
-	if (modFormat == FORMAT_STK || modFormat == FORMAT_FT2 || modFormat == FORMAT_NT || modFormat == FORMAT_HMNT || modFormat == FORMAT_FLT)
+	if (modFormat == FORMAT_STK || modFormat == FORMAT_NT || modFormat == FORMAT_HMNT || modFormat == FORMAT_FLT)
 	{
 		for (a = 0; a < b; a++)
 		{
@@ -690,7 +685,7 @@
 		/* For Ultimate SoundTracker modules, only the loop area of a looped sample is played.
 		** Skip loading of eventual data present before loop start.
 		*/
-		if (modFormat == FORMAT_STK && (s->repS > 0 && s->repL < s->len))
+		if (modFormat == FORMAT_STK && s->repS > 0 && s->repL < s->len)
 		{
 			s->len -= s->repS;
 			fseek(f, s->repS, SEEK_CUR);
@@ -705,7 +700,7 @@
 		}
 
 		// clear repL and repS on non-looping samples...
-		if ((s->typ & 3) == 0)
+		if (s->typ == 0)
 		{
 			s->repL = 0;
 			s->repS = 0;
@@ -735,18 +730,15 @@
 	uint16_t hz = 50;
 
 	hz -= ((slowdowns[tempo >> 4] * (tempo & 15)) >> 4); // can and will underflow
-
-	bpm = (int32_t)((hz * 2.5) + 0.5);
+	bpm = (hz << 1) + (hz >> 1); // BPM = hz * 2.5
 	return (uint8_t)CLAMP(bpm, 32, 255); // result can be slightly off, but close enough...
 }
 
 static bool loadMusicSTM(FILE *f, uint32_t fileLength, bool fromExternalThread)
 {
-	bool check3xx;
-	uint8_t typ, tmp8, tempo;
+	uint8_t typ, tempo;
 	int16_t i, j, k, ai, ap, tmp;
 	uint16_t a;
-	int32_t len;
 	tonTyp *ton;
 	sampleTyp *s;
 	songSTMHeaderTyp h_STM;
@@ -887,19 +879,6 @@
 					ton->eff = 0;
 				}
 
-				/* Remove any EDx with no note.
-				** SDx with no note in ST3 = does nothing
-				** EDx with no note in FT2 = still retriggers
-				*/
-				if (ton->effTyp == 0xE && (ton->eff & 0xF0) == 0xD0)
-				{
-					if (ton->ton == 0 || ton->ton == 97)
-					{
-						ton->eff = 0;
-						ton->effTyp = 0;
-					}
-				}
-
 				if (ton->effTyp > 35)
 				{
 					ton->effTyp = 0;
@@ -986,77 +965,6 @@
 		}
 	}
 
-	// non-FT2: fix overflown 9xx and illegal 3xx
-
-	for (i = 0; i < ap; i++)
-	{
-		if (pattTmp[i] == NULL)
-			continue;
-
-		for (k = 0; k < songTmp.antChn; k++)
-		{
-			check3xx = false;
-			for (j = 0; j < 64; j++)
-			{
-				ton = &pattTmp[i][(j * MAX_VOICES) + k];
-
-				if (ton->ton > 0 && ton->ton < 97 && ton->effTyp != 0x3)
-					check3xx = true;
-
-				if (ton->ton > 0 && ton->ton < 97 && ton->effTyp == 0x3)
-					check3xx = false;
-
-				if (check3xx && ton->effTyp == 0x3)
-				{
-					if (ton->ton == 0 || ton->ton == 97)
-					{
-						ton->effTyp = 0;
-						ton->eff = 0;
-					}
-				}
-
-				if (ton->effTyp == 0x9 && ton->eff > 0)
-				{
-					if (ton->instr != 0 && ton->instr <= ai)
-					{
-						s = &instrTmp[ton->instr]->samp[0];
-						len = s->len;
-
-						tmp8 = 0;
-						if (len > 0)
-						{
-							tmp8 = ton->eff;
-
-							int32_t newLen = len >> 8;
-							if (tmp8 >= newLen)
-							{
-								if (newLen < 1)
-									tmp8 = 0;
-								else
-									tmp8 = (uint8_t)(newLen - 1);
-							}
-						}
-
-						if (tmp8 > 0)
-						{
-							ton->eff = tmp8;
-						}
-						else
-						{
-							ton->effTyp = 0;
-							ton->eff = 0;
-						}
-					}
-					else
-					{
-						ton->effTyp = 0;
-						ton->eff = 0;
-					}
-				}
-			}
-		}
-	}
-
 	fclose(f);
 
 	moduleLoaded = true;
@@ -1102,14 +1010,14 @@
 static bool loadMusicS3M(FILE *f, uint32_t dataLength, bool fromExternalThread)
 {
 	int8_t *tmpSmp;
-	bool check3xx, illegalUxx;
+	bool illegalUxx;
 	uint8_t ha[2048];
-	uint8_t s3mLastDEff[32], s3mLastEEff[32], s3mLastFEff[32];
-	uint8_t s3mLastSEff[32], s3mLastJEff[32], s3mLastGInstr[32], typ;
+	uint8_t alastnfo[32], alastefx[32], alastvibnfo[32], s3mLastGInstr[32];
+	uint8_t typ;
 	int16_t ai, ap, ver, ii, kk, tmp;
 	uint16_t ptnOfs[256];
 	int32_t i, j, k, len;
-	tonTyp ton, *pattTon;
+	tonTyp ton;
 	sampleTyp *s;
 	songS3MHeaderTyp h_S3M;
 	songS3MinstrHeaderTyp h_S3MInstr;
@@ -1143,14 +1051,8 @@
 		goto s3mLoadError;
 	}
 
-	// count real song table entries
-	songTmp.len = 256;
-	while (songTmp.len > 0 && songTmp.songTab[songTmp.len-1] == 255)
-		songTmp.len--;
+	songTmp.len = h_S3M.songTabLen;
 
-	if (songTmp.len == 256)
-		songTmp.len = 255;
-
 	// remove pattern separators (254)
 	k = 0;
 	j = 0;
@@ -1165,11 +1067,20 @@
 	if (k <= songTmp.len)
 		songTmp.len -= (uint16_t)k;
 	else
-		songTmp.len = 0;
+		songTmp.len = 1;
+
+	for (i = 1; i < songTmp.len; i++)
+	{
+		if (songTmp.songTab[i] == 255)
+		{
+			songTmp.len = (uint16_t)i;
+			break;
+		}
+	}
 	
 	// clear unused song table entries
 	if (songTmp.len < 255)
-		memset(&songTmp.songTab[songTmp.len], 0, 256 - songTmp.len);
+		memset(&songTmp.songTab[songTmp.len], 0, 255-songTmp.len);
 
 	songTmp.speed = h_S3M.defTempo;
 	if (songTmp.speed < 32)
@@ -1226,11 +1137,9 @@
 		if (ptnOfs[i]  == 0)
 			continue; // empty pattern
 
-		memset(s3mLastDEff, 0, sizeof (s3mLastDEff));
-		memset(s3mLastEEff, 0, sizeof (s3mLastEEff));
-		memset(s3mLastFEff, 0, sizeof (s3mLastFEff));
-		memset(s3mLastSEff, 0, sizeof (s3mLastSEff));
-		memset(s3mLastJEff, 0, sizeof (s3mLastJEff));
+		memset(alastnfo, 0, sizeof (alastnfo));
+		memset(alastefx, 0, sizeof (alastefx));
+		memset(alastvibnfo, 0, sizeof (alastvibnfo));
 		memset(s3mLastGInstr, 0, sizeof (s3mLastGInstr));
 
 		fseek(f, ptnOfs[i] << 4, SEEK_SET);
@@ -1295,7 +1204,7 @@
 						}
 					}
 
-					// volume
+					// volume column
 					if (typ & 64)
 					{
 						ton.vol = pattBuff[k++];
@@ -1312,28 +1221,42 @@
 						ton.effTyp = pattBuff[k++];
 						ton.eff = pattBuff[k++];
 
-						if (ton.eff == 0)
+						if (ton.eff > 0)
 						{
-							if (ton.effTyp == 4)
+							alastnfo[ii] = ton.eff;
+							if (ton.effTyp == 8 || ton.effTyp == 21)
+								alastvibnfo[ii] = ton.eff; // H/U
+						}
+
+						// in ST3, a lot of effects directly share the same memory!
+						if (ton.eff == 0 && ton.effTyp != 7) // G
+						{
+							uint8_t efx = ton.effTyp;
+							if (efx == 8 || efx == 21) // H/U
+								ton.eff = alastvibnfo[ii];
+							else if ((efx >= 4 && efx <= 12) || (efx >= 17 && efx <= 19)) // D/E/F/I/J/K/L/Q/R/S
+								ton.eff = alastnfo[ii];
+
+							/* If effect data is zero and effect type was the same as last one, clear out
+							** data if it's not J or S (those have no memory in the equivalent XM effects).
+							** Also goes for extra fine pitch slides and fine volume slides,
+							** since they get converted to other effects.
+							*/
+							if (efx == alastefx[ii] && ton.effTyp != 10 && ton.effTyp != 19) // J/S
 							{
-								if ((s3mLastDEff[ii] & 0xF0) == 0xF0 || (s3mLastDEff[ii] & 0x0F) == 0x0F)
-									ton.eff = s3mLastDEff[ii];
+								uint8_t nfo = ton.eff;
+								bool extraFinePitchSlides = (efx == 5 || efx == 6) && ((nfo & 0xF0) == 0xE0);
+								bool fineVolSlides = (efx == 4 || efx == 11) &&
+								     ((nfo > 0xF0) || (((nfo & 0xF) == 0xF) && ((nfo & 0xF0) > 0)));
+
+								if (!extraFinePitchSlides && !fineVolSlides)
+									ton.eff = 0;
 							}
-							else if (ton.effTyp == 5) ton.eff = s3mLastEEff[ii];
-							else if (ton.effTyp == 6) ton.eff = s3mLastFEff[ii];
-							else if (ton.effTyp == 10) ton.eff = s3mLastJEff[ii];
-							else if (ton.effTyp == 19) ton.eff = s3mLastSEff[ii];
 						}
-						
-						if (ton.eff != 0)
-						{
-							     if (ton.effTyp == 4) s3mLastDEff[ii] = ton.eff;
-							else if (ton.effTyp == 5) s3mLastEEff[ii] = ton.eff;
-							else if (ton.effTyp == 6) s3mLastFEff[ii] = ton.eff;
-							else if (ton.effTyp == 10) s3mLastJEff[ii] = ton.eff;
-							else if (ton.effTyp == 19) s3mLastSEff[ii] = ton.eff;
-						}
 
+						if (ton.effTyp > 0)
+							alastefx[ii] = ton.effTyp;
+
 						switch (ton.effTyp)
 						{
 							case 1: // A
@@ -1351,14 +1274,12 @@
 							case 3: ton.effTyp = 0xD; break; // C
 							case 4: // D
 							{
-								     if ((ton.eff & 0xF0) == 0) ton.effTyp = 0xA;
-								else if ((ton.eff & 0x0F) == 0) ton.effTyp = 0xA;
-								else if ((ton.eff & 0xF0) == 0xF0)
+								if (ton.eff > 0xF0) // fine slide up
 								{
 									ton.effTyp = 0xE;
-									ton.eff = 0xB0 | (ton.eff & 15);
+									ton.eff = 0xB0 | (ton.eff & 0xF);
 								}
-								else if ((ton.eff & 0x0F) == 0x0F)
+								else if ((ton.eff & 0x0F) == 0x0F && (ton.eff & 0xF0) > 0) // fine slide down
 								{
 									ton.effTyp = 0xE;
 									ton.eff = 0xA0 | (ton.eff >> 4);
@@ -1366,10 +1287,8 @@
 								else
 								{
 									ton.effTyp = 0xA;
-									if (ton.eff & 0x0F)
+									if (ton.eff & 0x0F) // on D/K, last nybble has first priority in ST3
 										ton.eff &= 0x0F;
-									else
-										ton.eff &= 0xF0;
 								}
 							}
 							break;
@@ -1379,6 +1298,7 @@
 							{
 								if ((ton.eff & 0xF0) >= 0xE0)
 								{
+									// convert to fine slide
 									if ((ton.eff & 0xF0) == 0xE0)
 										tmp = 0x21;
 									else
@@ -1392,9 +1312,15 @@
 										ton.eff |= 0x10;
 
 									ton.effTyp = (uint8_t)tmp;
+
+									if (ton.effTyp == 0x21 && ton.eff == 0)
+									{
+										ton.effTyp = 0;
+									}
 								}
 								else
 								{
+									// convert to normal 1xx/2xx slide
 									ton.effTyp = 7 - ton.effTyp;
 								}
 							}
@@ -1402,11 +1328,40 @@
 
 							case 7: // G
 							{
+								ton.effTyp = 0x03;
+
 								// fix illegal slides (to new instruments)
 								if (ton.instr != 0 && ton.instr != s3mLastGInstr[ii])
 									ton.instr = s3mLastGInstr[ii];
+							}
+							break;
 
-								ton.effTyp = 0x03;
+							case 11: // K
+							{
+								if (ton.eff > 0xF0) // fine slide up
+								{
+									ton.effTyp = 0xE;
+									ton.eff = 0xB0 | (ton.eff & 0xF);
+
+									// if volume column is unoccupied, set to vibrato
+									if (ton.vol == 0)
+										ton.vol = 0xB0;
+								}
+								else if ((ton.eff & 0x0F) == 0x0F && (ton.eff & 0xF0) > 0) // fine slide down
+								{
+									ton.effTyp = 0xE;
+									ton.eff = 0xA0 | (ton.eff >> 4);
+
+									// if volume column is unoccupied, set to vibrato
+									if (ton.vol == 0)
+										ton.vol = 0xB0;
+								}
+								else
+								{
+									ton.effTyp = 0x6;
+									if (ton.eff & 0x0F) // on D/K, last nybble has first priority in ST3
+										ton.eff &= 0x0F;
+								}
 							}
 							break;
 
@@ -1413,7 +1368,6 @@
 							case 8: ton.effTyp = 0x04; break; // H
 							case 9: ton.effTyp = 0x1D; break; // I
 							case 10: ton.effTyp = 0x00; break; // J
-							case 11: ton.effTyp = 0x06; break; // K
 							case 12: ton.effTyp = 0x05; break; // L
 							case 15: ton.effTyp = 0x09; break; // O
 							case 17: ton.effTyp = 0x1B; break; // Q
@@ -1429,10 +1383,37 @@
 								else if (tmp == 0x2) ton.eff |= 0x50;
 								else if (tmp == 0x3) ton.eff |= 0x40;
 								else if (tmp == 0x4) ton.eff |= 0x70;
-								// we ignore S8x (set 4-bit pan) becuase it's not compatible with FT2 panning
+								// ignore S8x becuase it's not compatible with FT2 panning
 								else if (tmp == 0xB) ton.eff |= 0x60;
-								else if (tmp == 0xC) ton.eff |= 0xC0;
-								else if (tmp == 0xD) ton.eff |= 0xD0;
+								else if (tmp == 0xC) // Note Cut
+								{
+									ton.eff |= 0xC0;
+									if (ton.eff == 0xC0)
+									{
+										// EC0 does nothing in ST3 but cuts voice in FT2, remove effect
+										ton.effTyp = 0;
+										ton.eff = 0;
+									}
+								}
+								else if (tmp == 0xD) // Note Delay
+								{
+									ton.eff |= 0xD0;
+									if (ton.ton == 0 || ton.ton == 97)
+									{
+										// EDx without a note does nothing in ST3 but retrigs in FT2, remove effect
+										ton.effTyp = 0;
+										ton.eff = 0;
+									}
+									else if (ton.eff == 0xD0)
+									{
+										// ED0 prevents note/smp/vol from updating in ST3, remove everything
+										ton.ton = 0;
+										ton.instr = 0;
+										ton.vol = 0;
+										ton.effTyp = 0;
+										ton.eff = 0;
+									}
+								}
 								else if (tmp == 0xE) ton.eff |= 0xE0;
 								else if (tmp == 0xF) ton.eff |= 0xF0;
 								else
@@ -1454,40 +1435,18 @@
 							}
 							break;
 
-							case 21: // U (fine vibrato, doesn't exist in FT2, do a poor conversion to normal vibrato)
+							case 22: // V
 							{
-								if ((ton.eff & 0x0F) != 0)
+								ton.effTyp = 0x10;
+								if (ton.eff > 0x40)
 								{
-									ton.eff = (ton.eff & 0xF0) | (((ton.eff & 15) + 1) >> 2); // divide depth by 4
-									if ((ton.eff & 0x0F) == 0) // depth too low, remove effect
-									{
-										illegalUxx = true;
-										ton.effTyp = 0;
-										ton.eff = 0;
-									}
-									else
-									{
-										illegalUxx = false;
-										ton.effTyp = 0x04;
-									}
+									// Vxx > 0x40 does nothing in ST3
+									ton.effTyp = 0;
+									ton.eff = 0;
 								}
-								else
-								{
-									if (!illegalUxx)
-									{
-										ton.effTyp = 0x04;
-									}
-									else
-									{
-										ton.effTyp = 0;
-										ton.eff = 0;
-									}
-								}
 							}
 							break;
 
-							case 22: ton.effTyp = 0x10; break; // V
-
 							default:
 							{
 								ton.effTyp = 0;
@@ -1500,40 +1459,6 @@
 					if (ton.instr != 0 && ton.effTyp != 0x3)
 						s3mLastGInstr[ii] = ton.instr;
 
-					// EDx with no note does nothing in ST3 but retrigs in FT2, remove effect
-					if (ton.effTyp == 0xE && (ton.eff & 0xF0) == 0xD0)
-					{
-						if (ton.ton == 0 || ton.ton == 97)
-						{
-							ton.effTyp = 0;
-							ton.eff = 0;
-						}
-					}
-
-					// EDx with a zero will prevent note/instr/vol from updating in ST3, remove everything
-					if (ton.effTyp == 0xE && ton.eff == 0xD0)
-					{
-						ton.ton = 0;
-						ton.instr = 0;
-						ton.vol = 0;
-						ton.effTyp = 0;
-						ton.eff = 0;
-					}
-
-					// ECx with a zero does nothing in ST3 but cuts voice in FT2, remove effect
-					if (ton.effTyp == 0xE && ton.eff == 0xC0)
-					{
-						ton.effTyp = 0;
-						ton.eff = 0;
-					}
-
-					// Vxx with a value higher than 64 (0x40) does nothing in ST3, remove effect
-					if (ton.effTyp == 0x10 && ton.eff > 0x40)
-					{
-						ton.effTyp = 0;
-						ton.eff = 0;
-					}
-
 					if (ton.effTyp > 35)
 					{
 						ton.effTyp = 0;
@@ -1610,10 +1535,10 @@
 				bool is16Bit = (h_S3MInstr.flags >> 2) & 1;
 			
 				if (is16Bit) // 16-bit
-					len *= 2;
+					len <<= 1;
 
 				if (stereoSample) // stereo
-					len *= 2;
+					len <<= 1;
 
 				tmpSmp = (int8_t *)malloc(len + LOOP_FIX_LEN);
 				if (tmpSmp == NULL)
@@ -1683,9 +1608,9 @@
 						s->origPek = tmpSmp;
 						s->pek = s->origPek + SMP_DAT_OFFSET;
 
-						s->len *= 2;
-						s->repS *= 2;
-						s->repL *= 2;
+						s->len <<= 1;
+						s->repS <<= 1;
+						s->repL <<= 1;
 					}
 					else
 					{
@@ -1712,74 +1637,6 @@
 		}
 	}
 
-	// non-FT2: fix overflown 9xx and illegal 3xx slides
-
-	for (i = 0; i < ap; i++)
-	{
-		if (pattTmp[i] == NULL)
-			continue;
-
-		for (k = 0; k < songTmp.antChn; k++)
-		{
-			check3xx = false;
-			for (j = 0; j < 64; j++)
-			{
-				pattTon = &pattTmp[i][(j * MAX_VOICES) + k];
-
-				// fix illegal 3xx slides
-
-				if (pattTon->ton > 0 && pattTon->ton < 97)
-					check3xx = pattTon->effTyp != 0x3;
-
-				if (check3xx && pattTon->effTyp == 0x3)
-				{
-					if (pattTon->ton == 0 || pattTon->ton == 97)
-					{
-						pattTon->effTyp = 0;
-						pattTon->eff = 0;
-					}
-				}
-
-				/* In ST3 in GUS mode, an overflowed sample offset behaves like this:
-				** - Non-looped sample: Cut voice
-				** - Looped sample: Wrap around loop point
-				**
-				** What we do here is to change the sample offset value to point to
-				** the wrapped sample loop position. This may be off by up to 256 bytes
-				** though...
-				*/
-
-				if (pattTon->effTyp == 0x9 && pattTon->eff > 0 && pattTon->instr > 0 && pattTon->instr <= ai && ai <= 128)
-				{
-					instrTyp *ins = instrTmp[pattTon->instr];
-					if (ins == NULL)
-						continue; // empty instrument (sample)
-
-					s = &ins->samp[0];
-					if (s->len > 0 && (s->typ & 1)) // only handle non-empty looping samples
-					{
-						uint32_t loopEnd = s->repS + s->repL;
-						uint32_t offset = pattTon->eff * 256;
-
-						if (offset >= loopEnd)
-						{
-							if (s->repL >= 2)
-								offset = s->repS + ((offset - loopEnd) % s->repL);
-							else
-								offset = s->repS;
-
-							offset = (offset + (1 << 7)) >> 8; // convert to rounded sample offset value
-							if (offset > 255)
-								offset = 255;
-
-							pattTon->eff = (uint8_t)offset;
-						}
-					}
-				}
-			}
-		}
-	}
-
 	fclose(f);
 
 	songTmp.antChn = countS3MChannels(ap);
@@ -2566,30 +2423,37 @@
 	// support non-even channel numbers
 	if (song.antChn & 1)
 	{
-		if (++song.antChn > MAX_VOICES)
+		song.antChn++;
+		if (song.antChn > MAX_VOICES)
 			song.antChn = MAX_VOICES;
 	}
 
-	if (song.repS > song.len)
+	if (song.len == 0)
+		song.len = 1;
+
+	if (song.repS >= song.len)
 		song.repS = 0;
 
 	song.globVol = 64;
-	song.timer = 1;
 
 	setScrollBarEnd(SB_POS_ED, (song.len - 1) + 5);
 	setScrollBarPos(SB_POS_ED, 0, false);
 
 	resetChannels();
-	setPos(0, 0, false);
+	setPos(0, 0, true);
 	setSpeed(song.speed);
 
 	editor.tmpPattern = editor.editPattern; // set kludge variable
 	editor.speed = song.speed;
 	editor.tempo = song.tempo;
-	editor.timer = 1;
+	editor.timer = song.timer;
 	editor.globalVol = song.globVol;
 
-	setFrqTab((loadedFormat == FORMAT_XM) ? linearFreqTable : false);
+	if (loadedFormat == FORMAT_XM)
+		setFrqTab(linearFreqTable);
+	else
+		setFrqTab(false);
+
 	unlockMixerCallback();
 
 	editor.currVolEnvPoint = 0;
@@ -2602,8 +2466,9 @@
 	updateChanNums();
 	resetWavRenderer();
 	clearPattMark();
-	song.musicTime = 0;
 	resetTrimSizes();
+
+	song.musicTime = 0;
 
 	diskOpSetFilename(DISKOP_ITEM_MODULE, editor.tmpFilenameU);
 
--- a/src/ft2_nibbles.c
+++ b/src/ft2_nibbles.c
@@ -265,7 +265,7 @@
 {
 	if (editor.NI_Play)
 	{
-		okBox(0, "System message", "The highscore table is not available during play.");
+		okBox(0, "Nibbles message", "The highscore table is not available during play.");
 		return;
 	}
 
@@ -781,7 +781,7 @@
 {
 	if (editor.NI_Play)
 	{
-		if (okBox(2, "Nibbles request", "Restart the current game of Nibbles?") != 1)
+		if (okBox(2, "Nibbles request", "Restart current game of Nibbles?") != 1)
 			return;
 	}
 
--- a/src/ft2_pattern_draw.c
+++ b/src/ft2_pattern_draw.c
@@ -307,7 +307,7 @@
 
 static void drawChannelNumbering(uint16_t yPos)
 {
-#define CH_NUM_XPOS 29
+#define CH_NUM_XPOS 30
 
 	uint16_t xPos = CH_NUM_XPOS;
 	int32_t ch = editor.ui.channelOffset + 1;
--- a/src/ft2_pattern_ed.c
+++ b/src/ft2_pattern_ed.c
@@ -183,10 +183,10 @@
 	textOutShadow(  4, 126, PAL_FORGRND, PAL_DSKTOP2, "New number");
 	textOutShadow(129,  96, PAL_FORGRND, PAL_DSKTOP2, "Masking enable");
 	textOutShadow(114, 109, PAL_FORGRND, PAL_DSKTOP2, "Note");
-	textOutShadow(114, 121, PAL_FORGRND, PAL_DSKTOP2, "Instrument number");
-	textOutShadow(114, 134, PAL_FORGRND, PAL_DSKTOP2, "Volume column");
-	textOutShadow(114, 147, PAL_FORGRND, PAL_DSKTOP2, "Effect digit 1");
-	textOutShadow(114, 160, PAL_FORGRND, PAL_DSKTOP2, "Effect digit 2,3");
+	textOutShadow(114, 122, PAL_FORGRND, PAL_DSKTOP2, "Instrument number");
+	textOutShadow(114, 135, PAL_FORGRND, PAL_DSKTOP2, "Volume column");
+	textOutShadow(114, 148, PAL_FORGRND, PAL_DSKTOP2, "Effect digit 1");
+	textOutShadow(114, 161, PAL_FORGRND, PAL_DSKTOP2, "Effect digit 2,3");
 
 	charOutShadow(239, 95, PAL_FORGRND, PAL_DSKTOP2, 'C');
 	charOutShadow(258, 95, PAL_FORGRND, PAL_DSKTOP2, 'P');
@@ -680,8 +680,8 @@
 
 	textOutShadow(116,  5, PAL_FORGRND, PAL_DSKTOP2, "Sng.len.");
 	textOutShadow(116, 19, PAL_FORGRND, PAL_DSKTOP2, "Repst.");
-	textOutShadow(222, 40, PAL_FORGRND, PAL_DSKTOP2, "Ptn.");
-	textOutShadow(305, 40, PAL_FORGRND, PAL_DSKTOP2, "Ln.");
+	textOutShadow(222, 39, PAL_FORGRND, PAL_DSKTOP2, "Ptn.");
+	textOutShadow(305, 39, PAL_FORGRND, PAL_DSKTOP2, "Ln.");
 
 	editor.ui.instrSwitcherShown = true;
 	showInstrumentSwitcher();
@@ -2150,12 +2150,12 @@
 	if (editor.ui.extended)
 	{
 		x = 165;
-		y = 6;
+		y = 5;
 	}
 	else
 	{
 		x = 59;
-		y = 53;
+		y = 52;
 	}
 
 	hexOutBg(x, y, PAL_FORGRND, PAL_DESKTOP, (uint8_t)song.len, 2);
@@ -2168,12 +2168,12 @@
 	if (editor.ui.extended)
 	{
 		x = 165;
-		y = 20;
+		y = 19;
 	}
 	else
 	{
 		x = 59;
-		y = 65;
+		y = 64;
 	}
 
 	hexOutBg(x, y, PAL_FORGRND, PAL_DESKTOP, (uint8_t)song.repS, 2);
@@ -2207,8 +2207,8 @@
 
 	if (editor.ui.extended)
 	{
-		x = 251;
-		y = 40;
+		x = 252;
+		y = 39;
 	}
 	else
 	{
@@ -2226,7 +2226,7 @@
 	if (editor.ui.extended)
 	{
 		x = 326;
-		y = 40;
+		y = 39;
 	}
 	else
 	{
--- a/src/ft2_pushbuttons.c
+++ b/src/ft2_pushbuttons.c
@@ -146,13 +146,13 @@
 	  
 	// ------ ABOUT SCREEN PUSHBUTTONS ------
 	//x, y,   w,  h,  p, d, text #1, text #2, funcOnDown, funcOnUp
-	{ 5, 153, 59, 16, 0, 0, "Exit",  NULL,    NULL,       exitAboutScreen },
+	{ 4, 153, 59, 16, 0, 0, "Exit",  NULL,    NULL,       exitAboutScreen },
 
 	// ------ HELP SCREEN PUSHBUTTONS ------
 	//x,   y,   w,  h,  p, d, text #1,           text #2, funcOnDown,     funcOnUp
 	{   3, 155, 59, 16, 0, 0, "Exit",            NULL,    NULL,           exitHelpScreen },
-	{ 611,   2, 18, 13, 1, 2, ARROW_UP_STRING,   NULL,    helpScrollUp,   NULL },
-	{ 611, 158, 18, 13, 1, 2, ARROW_DOWN_STRING, NULL,    helpScrollDown, NULL },
+	{ 611,   2, 18, 13, 1, 3, ARROW_UP_STRING,   NULL,    helpScrollUp,   NULL },
+	{ 611, 158, 18, 13, 1, 3, ARROW_DOWN_STRING, NULL,    helpScrollDown, NULL },
 
 	// ------ PATTERN EDITOR PUSHBUTTONS ------
 	//x,   y,   w,  h,  p, d, text #1,            text #2, funcOnDown,         funcOnUp
@@ -196,8 +196,8 @@
 
 	// ------ SAMPLE EDITOR PUSHBUTTONS ------
 	//x,   y,   w,  h,  p, d, text #1,            text #2, funcOnDown,            funcOnUp
-	{   3, 331, 23, 13, 0, 0, ARROW_LEFT_STRING,  NULL,    scrollSampleDataLeft,  NULL },
-	{ 606, 331, 23, 13, 0, 0, ARROW_RIGHT_STRING, NULL,    scrollSampleDataRight, NULL },
+	{   3, 331, 23, 13, 1, 3, ARROW_LEFT_STRING,  NULL,    scrollSampleDataLeft,  NULL },
+	{ 606, 331, 23, 13, 1, 3, ARROW_RIGHT_STRING, NULL,    scrollSampleDataRight, NULL },
 	{  38, 356, 18, 13, 1, 4, ARROW_UP_STRING,    NULL,    sampPlayNoteUp,        NULL },
 	{  38, 368, 18, 13, 1, 4, ARROW_DOWN_STRING,  NULL,    sampPlayNoteDown,      NULL },
 	{   3, 382, 53, 16, 0, 0, "Stop",             NULL,    NULL,                  smpEdStop},
@@ -338,10 +338,10 @@
 	{ 113, 155, 93, 16, 0, 0, editor.ui.fullscreenButtonText, NULL,    NULL,                toggleFullScreen },
 	{ 370, 121, 18, 13, 1, 4, ARROW_UP_STRING,                NULL,    configQuantizeUp,    NULL },
 	{ 387, 121, 18, 13, 1, 4, ARROW_DOWN_STRING,              NULL,    configQuantizeDown,  NULL },
-	{ 594, 107, 18, 13, 1, 4, ARROW_UP_STRING,                NULL,    configMIDIChnUp,     NULL },
-	{ 611, 107, 18, 13, 1, 4, ARROW_DOWN_STRING,              NULL,    configMIDIChnDown,   NULL },
-	{ 594, 121, 18, 13, 1, 4, ARROW_UP_STRING,                NULL,    configMIDITransUp,   NULL },
-	{ 611, 121, 18, 13, 1, 4, ARROW_DOWN_STRING,              NULL,    configMIDITransDown, NULL },
+	{ 594, 106, 18, 13, 1, 4, ARROW_UP_STRING,                NULL,    configMIDIChnUp,     NULL },
+	{ 611, 106, 18, 13, 1, 4, ARROW_DOWN_STRING,              NULL,    configMIDIChnDown,   NULL },
+	{ 594, 120, 18, 13, 1, 4, ARROW_UP_STRING,                NULL,    configMIDITransUp,   NULL },
+	{ 611, 120, 18, 13, 1, 4, ARROW_DOWN_STRING,              NULL,    configMIDITransDown, NULL },
 	{ 556, 158, 22, 13, 1, 4, ARROW_LEFT_STRING,              NULL,    configMIDISensDown,  NULL },
 	{ 607, 158, 22, 13, 1, 4, ARROW_RIGHT_STRING,             NULL,    configMIDISensUp,    NULL },
 
@@ -377,8 +377,8 @@
 	{ 134,   2, 31, 13, 0, 0, "../",             NULL,    NULL,             pbDiskOpParent },
 	{ 134,  16, 31, 12, 0, 0, "/",               NULL,    NULL,             pbDiskOpRoot },
 #endif
-	{ 335,   2, 18, 13, 0, 0, ARROW_UP_STRING,   NULL,    pbDiskOpListUp,   NULL },
-	{ 335, 158, 18, 13, 0, 0, ARROW_DOWN_STRING, NULL,    pbDiskOpListDown, NULL },
+	{ 335,   2, 18, 13, 1, 3, ARROW_UP_STRING,   NULL,    pbDiskOpListUp,   NULL },
+	{ 335, 158, 18, 13, 1, 3, ARROW_DOWN_STRING, NULL,    pbDiskOpListDown, NULL },
 
 	// ------ WAV RENDERER PUSHBUTTONS ------
 	//x,   y,   w,  h,  p, d, text #1,           text #2, funcOnDown,         funcOnUp
@@ -539,10 +539,10 @@
 	{
 		// button delay stuff
 		if (mouse.rightButtonPressed)
-			buttonDelay = pushButton->delayFrames / 2;
-		else if (pushButton->preDelay == 2 && (!mouse.firstTimePressingButton && ++tmpCounter >= 20)) // special mode
+			buttonDelay = pushButton->delayFrames >> 1;
+		else if (pushButton->preDelay == 2 && (!mouse.firstTimePressingButton && ++tmpCounter >= 50)) // special mode
 			buttonDelay = 0;
-		else 
+		else
 			buttonDelay = pushButton->delayFrames;
 
 		// main repeat delay
--- a/src/ft2_pushbuttons.h
+++ b/src/ft2_pushbuttons.h
@@ -342,7 +342,7 @@
 };
 
 // amount of frames to wait
-#define BUTTON_DOWN_DELAY 16
+#define BUTTON_DOWN_DELAY 25
 
 // font #1/#2 special characters (used for buttons)
 #define ARROW_UP_STRING "\x05"
--- a/src/ft2_radiobuttons.c
+++ b/src/ft2_radiobuttons.c
@@ -30,12 +30,12 @@
 
 	// ------ HELP SCREEN RADIOBUTTONS ------
 	//x, y,   w,  group,         funcOnUp
-	{ 5, 16,  68, RB_GROUP_HELP, rbHelpFeatures },
-	{ 5, 31,  59, RB_GROUP_HELP, rbHelpEffects },
-	{ 5, 46,  70, RB_GROUP_HELP, rbHelpKeyboard },
-	{ 5, 61, 108, RB_GROUP_HELP, rbHelpHowToUseFT2 },
-	{ 5, 76, 100, RB_GROUP_HELP, rbHelpFAQ },
-	{ 5, 91,  85, RB_GROUP_HELP, rbHelpKnownBugs },
+	{ 5, 18,  69, RB_GROUP_HELP, rbHelpFeatures },
+	{ 5, 34,  60, RB_GROUP_HELP, rbHelpEffects },
+	{ 5, 50,  71, RB_GROUP_HELP, rbHelpKeyboard },
+	{ 5, 66, 109, RB_GROUP_HELP, rbHelpHowToUseFT2 },
+	{ 5, 82, 101, RB_GROUP_HELP, rbHelpFAQ },
+	{ 5, 98,  86, RB_GROUP_HELP, rbHelpKnownBugs },
 
 	// ------ NIBBLES SCREEN RADIOBUTTONS ------
 	//x, y,   w,   group,                       funcOnUp
@@ -63,12 +63,11 @@
 
 	// ------ CONFIG SCREEN LEFT RADIOBUTTONS ------
 	//x, y,  w,  group,                  funcOnUp
-	{ 4, 19, 87, RB_GROUP_CONFIG_SELECT, rbConfigIODevices },
-	{ 4, 35, 59, RB_GROUP_CONFIG_SELECT, rbConfigLayout },
-	{ 4, 51, 99, RB_GROUP_CONFIG_SELECT, rbConfigMiscellaneous },
-
+	{ 5, 18, 85, RB_GROUP_CONFIG_SELECT, rbConfigIODevices },
+	{ 5, 34, 57, RB_GROUP_CONFIG_SELECT, rbConfigLayout },
+	{ 5, 50, 97, RB_GROUP_CONFIG_SELECT, rbConfigMiscellaneous },
 #ifdef HAS_MIDI
-	{ 4, 67, 74, RB_GROUP_CONFIG_SELECT, rbConfigMidiInput },
+	{ 5, 66, 72, RB_GROUP_CONFIG_SELECT, rbConfigMidiInput },
 #endif
 
 	// ------ CONFIG AUDIO ------
--- a/src/ft2_replayer.c
+++ b/src/ft2_replayer.c
@@ -26,8 +26,7 @@
 */
 
 static bool bxxOverflow;
-static uint16_t oldPeriod;
-static uint32_t oldRate, frequenceDivFactor, frequenceMulFactor;
+static int32_t oldPeriod, oldRate, frequenceDivFactor, frequenceMulFactor;
 static tonTyp nilPatternLine;
 
 // globally accessed
@@ -230,6 +229,8 @@
 		note2Period = amigaPeriods;
 	}
 
+	resetCachedFrequencyVars();
+
 	// update "frequency table" radiobutton, if it's shown
 	if (editor.ui.configScreenShown && editor.currConfigScreen == CONFIG_SCREEN_IO_DEVICES)
 		setConfigIORadioButtonStates();
@@ -367,7 +368,7 @@
 		indexQuotient = index / 768;
 		indexRemainder = index % 768;
 
-		rate = ((uint64_t)logTab[indexRemainder] * frequenceMulFactor) >> LOG_TABLE_BITS;
+		rate = ((int64_t)logTab[indexRemainder] * frequenceMulFactor) >> LOG_TABLE_BITS;
 
 		shift = (14 - indexQuotient) & 0x1F;
 		if (shift != 0)
@@ -384,13 +385,13 @@
 	return rate;
 }
 
-void resetOldRates(void)
+void resetCachedFrequencyVars(void)
 {
-	oldPeriod = 0;
+	oldPeriod = -1;
 	oldRate = 0;
 
-	resetOldScopeRates();
-	resetOldRevFreqs();
+	resetCachedScopeVars();
+	resetCachedMixerVars();
 }
 
 static void startTone(uint8_t ton, uint8_t effTyp, uint8_t eff, stmTyp *ch)
@@ -1320,10 +1321,10 @@
 			** Also, vol envelope range is now 0..16384 instead of being shifted to 0..64
 			*/
 
-			uint32_t vol1 = song.globVol * ch->outVol * ch->fadeOutAmp; // 0..64 * 0..64 * 0..32768 = 0..134217728
-			uint32_t vol2 = envVal << 7; // 0..16384 * 2^7 = 0..2097152
+			int32_t vol1 = song.globVol * ch->outVol * ch->fadeOutAmp; // 0..64 * 0..64 * 0..32768 = 0..134217728
+			int32_t vol2 = envVal << 7; // 0..16384 * 2^7 = 0..2097152
 
-			vol = ((uint64_t)vol1 * vol2) >> 32; // 0..65536
+			vol = ((int64_t)vol1 * vol2) >> 32; // 0..65536
 
 			ch->status |= IS_Vol;
 		}
@@ -1330,7 +1331,7 @@
 		else
 		{
 			// calculate with four times more precision (finalVol = 0..65535)
-			vol = (song.globVol * ch->outVol * ch->fadeOutAmp) >> 11; // 0..64 * 0..64 * 0..32768 = 0..65536
+			vol = ((song.globVol * ch->outVol * ch->fadeOutAmp) + (1 << 10)) >> 11; // 0..64 * 0..64 * 0..32768 -> 0..65536 (rounded)
 		}
 
 		if (vol > 65535)
@@ -3058,7 +3059,7 @@
 
 	stopAllScopes();
 	resetAudioDither();
-	resetOldRates();
+	resetCachedFrequencyVars();
 
 	// wait for scope thread to finish, so that we know pointers aren't deprecated
 	while (editor.scopeThreadMutex);
--- a/src/ft2_replayer.h
+++ b/src/ft2_replayer.h
@@ -241,7 +241,7 @@
 void fixSampleName(int16_t nr); // removes spaces from right side of ins/smp names
 
 void calcReplayRate(int32_t rate);
-void resetOldRates(void);
+void resetCachedFrequencyVars(void);
 void tuneSample(sampleTyp *s, int32_t midCFreq);
 uint32_t getFrequenceValue(uint16_t period);
 
--- a/src/ft2_sample_ed.c
+++ b/src/ft2_sample_ed.c
@@ -1013,7 +1013,7 @@
 
 	viewSizeSamples = smpEd_ViewSize;
 	if (s->typ & 16)
-		viewSizeSamples /= 2;
+		viewSizeSamples >>= 1;
 
 	if (viewSizeSamples <= SAMPLE_AREA_WIDTH)
 	{
@@ -1319,18 +1319,13 @@
 		return;
 
 	if (mouse.rightButtonPressed)
-	{
 		scrollAmount = smpEd_ViewSize / 14; // rounded from 16 (70Hz)
-		if (scrollAmount < 1)
-			scrollAmount = 1;
-	}
 	else
-	{
 		scrollAmount = smpEd_ViewSize / 27; // rounded from 32 (70Hz)
-		if (scrollAmount < 1)
-			scrollAmount = 1;
-	}
 
+	if (scrollAmount < 1)
+		scrollAmount = 1;
+
 	smpEd_ScrPos -= scrollAmount;
 	if (smpEd_ScrPos < 0)
 		smpEd_ScrPos = 0;
@@ -1351,18 +1346,13 @@
 		return;
 
 	if (mouse.rightButtonPressed)
-	{
 		scrollAmount = smpEd_ViewSize / 14; // was 16 (70Hz->60Hz)
-		if (scrollAmount < 1)
-			scrollAmount = 1;
-	}
 	else
-	{
 		scrollAmount = smpEd_ViewSize / 27; // was 32 (70Hz->60Hz)
-		if (scrollAmount < 1)
-			scrollAmount = 1;
-	}
 
+	if (scrollAmount < 1)
+		scrollAmount = 1;
+
 	smpEd_ScrPos += scrollAmount;
 	if (smpEd_ScrPos+smpEd_ViewSize > sampleLen)
 		smpEd_ScrPos = sampleLen - smpEd_ViewSize;
@@ -2694,18 +2684,15 @@
 	if (s == NULL || s->pek == NULL || s->len <= 0)
 		return;
 
-	bool hasLoop = s->typ & 3;
-	int32_t loopEnd = s->repS + s->repL;
+	if (okBox(1, "System request", "Minimize sample?") != 1)
+		return;
 
-	if (!hasLoop || (loopEnd >= s->len || loopEnd == 0))
+	bool hasLoop = s->typ & 3;
+	if (hasLoop && s->len > s->repS+s->repL && s->repL < s->len)
 	{
-		okBox(0, "System message", "Sample is already minimized.");
-	}
-	else
-	{
 		lockMixerCallback();
 
-		s->len = loopEnd;
+		s->len = s->repS + s->repL;
 
 		newPtr = (int8_t *)realloc(s->origPek, s->len + LOOP_FIX_LEN);
 		if (newPtr != NULL)
@@ -2714,6 +2701,8 @@
 			s->pek = s->origPek + SMP_DAT_OFFSET;
 		}
 
+		// Note: we don't need to make a call to fixSample()
+
 		unlockMixerCallback();
 
 		updateSampleEditorSample();
@@ -2897,9 +2886,9 @@
 	textOutShadow(371, 369, PAL_FORGRND, PAL_DSKTOP2, "Forward");
 	textOutShadow(371, 386, PAL_FORGRND, PAL_DSKTOP2, "Pingpong");
 	textOutShadow(446, 369, PAL_FORGRND, PAL_DSKTOP2, "8-bit");
-	textOutShadow(445, 385, PAL_FORGRND, PAL_DSKTOP2, "16-bit");
-	textOutShadow(488, 349, PAL_FORGRND, PAL_DSKTOP2, "Display");
-	textOutShadow(488, 361, PAL_FORGRND, PAL_DSKTOP2, "Length");
+	textOutShadow(445, 384, PAL_FORGRND, PAL_DSKTOP2, "16-bit");
+	textOutShadow(488, 350, PAL_FORGRND, PAL_DSKTOP2, "Display");
+	textOutShadow(488, 362, PAL_FORGRND, PAL_DSKTOP2, "Length");
 	textOutShadow(488, 375, PAL_FORGRND, PAL_DSKTOP2, "Repeat");
 	textOutShadow(488, 387, PAL_FORGRND, PAL_DSKTOP2, "Replen.");
 
@@ -3233,7 +3222,7 @@
 			int32_t y = lastDrawY - vl;
 			uint32_t x = lastDrawX - p;
 
-			uint32_t xMul = 0xFFFFFFFF;
+			int32_t xMul = 0xFFFFFFFF;
 			if (x != 0)
 				xMul /= x;
 
@@ -3276,7 +3265,7 @@
 			int32_t y = lastDrawY - vl;
 			uint32_t x = lastDrawX - p;
 
-			uint32_t xMul = 0xFFFFFFFF;
+			int32_t xMul = 0xFFFFFFFF;
 			if (x != 0)
 				xMul /= x;
 
@@ -3430,10 +3419,10 @@
 
 	textOutShadow( 4,  96, PAL_FORGRND, PAL_DSKTOP2, "Rng.:");
 	charOutShadow(91,  95, PAL_FORGRND, PAL_DSKTOP2, '-');
-	textOutShadow( 4, 109, PAL_FORGRND, PAL_DSKTOP2, "Range size");
-	textOutShadow( 4, 123, PAL_FORGRND, PAL_DSKTOP2, "Copy buf. size");
+	textOutShadow( 4, 110, PAL_FORGRND, PAL_DSKTOP2, "Range size");
+	textOutShadow( 4, 124, PAL_FORGRND, PAL_DSKTOP2, "Copy buf. size");
 
-	textOutShadow(162,  95, PAL_FORGRND, PAL_DSKTOP2, "Src.instr.");
+	textOutShadow(162,  96, PAL_FORGRND, PAL_DSKTOP2, "Src.instr.");
 	textOutShadow(245,  96, PAL_FORGRND, PAL_DSKTOP2, "smp.");
 	textOutShadow(162, 109, PAL_FORGRND, PAL_DSKTOP2, "Dest.instr.");
 	textOutShadow(245, 109, PAL_FORGRND, PAL_DSKTOP2, "smp.");
--- a/src/ft2_sample_ed_features.c
+++ b/src/ft2_sample_ed_features.c
@@ -121,7 +121,7 @@
 	p1 = s->pek;
 
 	// don't use the potentially clamped newLen value here
-	delta64 = ((uint64_t)s->len << 32) / (uint64_t)(s->len * dLenMul); // 32.32 fixed point delta
+	delta64 = ((int64_t)s->len << 32) / (int64_t)(s->len * dLenMul); // 32.32 fixed point delta
 
 	posFrac64 = 0; // 32.32 fixed point position.fraction
 
@@ -660,8 +660,8 @@
 	hLine(x + 2,     y + h - 3, w - 4, PAL_BUTTON1);
 
 	textOutShadow(177, 226, PAL_FORGRND, PAL_BUTTON2, "Number of echoes");
-	textOutShadow(177, 239, PAL_FORGRND, PAL_BUTTON2, "Echo distance");
-	textOutShadow(177, 253, PAL_FORGRND, PAL_BUTTON2, "Fade out");
+	textOutShadow(177, 240, PAL_FORGRND, PAL_BUTTON2, "Echo distance");
+	textOutShadow(177, 254, PAL_FORGRND, PAL_BUTTON2, "Fade out");
 	textOutShadow(192, 270, PAL_FORGRND, PAL_BUTTON2, "Add memory to sample");
 
 	assert(echo_nEcho <= 1024);
@@ -1510,7 +1510,7 @@
 	hLine(x + 2,     y + h - 3, w - 4, PAL_BUTTON1);
 
 	textOutShadow(172, 236, PAL_FORGRND, PAL_BUTTON2, "Start volume");
-	textOutShadow(172, 249, PAL_FORGRND, PAL_BUTTON2, "End volume");
+	textOutShadow(172, 250, PAL_FORGRND, PAL_BUTTON2, "End volume");
 	charOutShadow(282, 236, PAL_FORGRND, PAL_BUTTON2, '%');
 	charOutShadow(282, 250, PAL_FORGRND, PAL_BUTTON2, '%');
 
--- a/src/ft2_scopes.c
+++ b/src/ft2_scopes.c
@@ -48,9 +48,9 @@
 
 lastChInstr_t lastChInstr[MAX_VOICES]; // global
 
-void resetOldScopeRates(void)
+void resetCachedScopeVars(void)
 {
-	oldVoiceDelta = 0;
+	oldVoiceDelta = 0xFFFFFFFF;
 	oldSFrq = 0;
 }
 
@@ -126,6 +126,7 @@
 
 static void drawScopeNumber(uint16_t scopeXOffs, uint16_t scopeYOffs, uint8_t channel, bool outline)
 {
+	scopeXOffs++;
 	scopeYOffs++;
 	channel++;
 
@@ -313,7 +314,7 @@
 	return false;
 }
 
-static void scopeTrigger(uint8_t ch, sampleTyp *s, int32_t playOffset)
+static void scopeTrigger(int32_t ch, sampleTyp *s, int32_t playOffset)
 {
 	bool sampleIs16Bit;
 	uint8_t loopType;
@@ -522,7 +523,7 @@
 
 		// draw rec. symbol (if enabled)
 		if (config.multiRecChn[i])
-			blit(scopeXOffs, scopeYOffs + 31, bmp.scopeRec, 13, 4);
+			blit(scopeXOffs + 1, scopeYOffs + 31, bmp.scopeRec, 13, 4);
 
 		scopeXOffs += scopeDrawLen + 3; // align x to next scope
 	}
@@ -572,7 +573,7 @@
 			if (instr[ch->instrNr] != NULL)
 			{
 				smpPtr = &instr[ch->instrNr]->samp[ch->sampleNr];
-				scopeTrigger((uint8_t)i, smpPtr, ch->smpStartPos);
+				scopeTrigger(i, smpPtr, ch->smpStartPos);
 
 				// set stuff used by Smp. Ed. for sampling position line
 
--- a/src/ft2_scopes.h
+++ b/src/ft2_scopes.h
@@ -4,7 +4,7 @@
 #include <stdbool.h>
 #include "ft2_header.h"
 
-void resetOldScopeRates(void);
+void resetCachedScopeVars(void);
 int32_t getSamplePosition(uint8_t ch);
 void stopAllScopes(void);
 void refreshScopes(void);
--- a/vs2019_project/ft2-clone/ft2-clone.vcxproj
+++ b/vs2019_project/ft2-clone/ft2-clone.vcxproj
@@ -278,6 +278,7 @@
       <RuntimeTypeInfo>false</RuntimeTypeInfo>
       <OpenMPSupport>false</OpenMPSupport>
       <LanguageStandard>stdcpplatest</LanguageStandard>
+      <OmitFramePointers>false</OmitFramePointers>
     </ClCompile>
     <Link>
       <AdditionalLibraryDirectories>