shithub: ft²

Download patch

ref: a685b2642191830c45e3b99ed79716e423758d82
parent: b3226cae30afa0b3a43ebcca5919b2cd02555a1e
author: Olav Sørensen <olav.sorensen@live.no>
date: Sun Feb 16 11:24:22 EST 2025

New mixer interpolator + small tracker scope changes

--- a/src/ft2_audio.c
+++ b/src/ft2_audio.c
@@ -367,11 +367,8 @@
 
 		if (status & IS_Vol)
 		{
-			v->fVolume = ch->fFinalVol;
-
-			// set scope volume (scaled)
-			const int32_t scopeVolume = (int32_t)((ch->fFinalVol * (SCOPE_HEIGHT*(1<<2))) + 0.5f); // rounded
-			v->scopeVolume = (uint8_t)scopeVolume;
+			v->fVolume = ch->fFinalVol; // 0.0f .. 1.0f
+			v->scopeVolume = (uint8_t)((ch->fFinalVol * 255.0f) + 0.5f); // 0..255, rounded
 		}
 
 		if (status & IS_Pan)
--- a/src/ft2_audio.h
+++ b/src/ft2_audio.h
@@ -61,7 +61,7 @@
 	const int8_t *base8, *revBase8;
 	const int16_t *base16, *revBase16;
 	bool active, samplingBackwards, isFadeOutVoice, hasLooped;
-	uint8_t mixFuncOffset, panning, loopType, scopeVolume;
+	uint8_t scopeVolume, mixFuncOffset, panning, loopType;
 	int32_t position, sampleEnd, loopStart, loopLength;
 	uint32_t volumeRampLength;
 	uint64_t positionFrac, delta, scopeDelta;
--- a/src/ft2_config.c
+++ b/src/ft2_config.c
@@ -835,10 +835,10 @@
 		tmpID = RB_CONFIG_AUDIO_INTRP_LINEAR;
 	else if (config.interpolation == INTERPOLATION_SINC16)
 		tmpID = RB_CONFIG_AUDIO_INTRP_SINC16;
-	else if (config.interpolation == INTERPOLATION_CUBIC4)
-		tmpID = RB_CONFIG_AUDIO_INTRP_CUBIC4;
-	else if (config.interpolation == INTERPOLATION_CUBIC6)
-		tmpID = RB_CONFIG_AUDIO_INTRP_CUBIC6;
+	else if (config.interpolation == INTERPOLATION_CUBIC)
+		tmpID = RB_CONFIG_AUDIO_INTRP_CUBIC;
+	else if (config.interpolation == INTERPOLATION_QUADRATIC)
+		tmpID = RB_CONFIG_AUDIO_INTRP_QUADRATIC;
 	else
 		tmpID = RB_CONFIG_AUDIO_INTRP_SINC8; // default case
 
@@ -1173,8 +1173,8 @@
 
 			textOutShadow(405,  91, PAL_FORGRND, PAL_DSKTOP2, "No interpolation");
 			textOutShadow(405, 105, PAL_FORGRND, PAL_DSKTOP2, "Linear (FT2)");
-			textOutShadow(405, 119, PAL_FORGRND, PAL_DSKTOP2, "Cubic (4 point)");
-			textOutShadow(405, 133, PAL_FORGRND, PAL_DSKTOP2, "Cubic (6 point)");
+			textOutShadow(405, 119, PAL_FORGRND, PAL_DSKTOP2, "Quadratic spline");
+			textOutShadow(405, 133, PAL_FORGRND, PAL_DSKTOP2, "Cubic spline");
 			textOutShadow(405, 147, PAL_FORGRND, PAL_DSKTOP2, "Sinc (8 point)");
 			textOutShadow(405, 161, PAL_FORGRND, PAL_DSKTOP2, "Sinc (16 point)");
 
@@ -1629,18 +1629,18 @@
 	audioSetInterpolationType(config.interpolation);
 	checkRadioButton(RB_CONFIG_AUDIO_INTRP_LINEAR);
 }
-void rbConfigAudioIntrpCubic4(void)
+void rbConfigAudioIntrpCubic(void)
 {
-	config.interpolation = INTERPOLATION_CUBIC4;
+	config.interpolation = INTERPOLATION_CUBIC;
 	audioSetInterpolationType(config.interpolation);
-	checkRadioButton(RB_CONFIG_AUDIO_INTRP_CUBIC4);
+	checkRadioButton(RB_CONFIG_AUDIO_INTRP_CUBIC);
 }
 
-void rbConfigAudioIntrpCubic6(void)
+void rbConfigAudioIntrpQuadratic(void)
 {
-	config.interpolation = INTERPOLATION_CUBIC6;
+	config.interpolation = INTERPOLATION_QUADRATIC;
 	audioSetInterpolationType(config.interpolation);
-	checkRadioButton(RB_CONFIG_AUDIO_INTRP_CUBIC6);
+	checkRadioButton(RB_CONFIG_AUDIO_INTRP_QUADRATIC);
 }
 
 void rbConfigAudioIntrpSinc8(void)
--- a/src/ft2_config.h
+++ b/src/ft2_config.h
@@ -205,8 +205,8 @@
 void rbConfigAudio32BitFloat(void);
 void rbConfigAudioIntrpDisabled(void);
 void rbConfigAudioIntrpLinear(void);
-void rbConfigAudioIntrpCubic4(void);
-void rbConfigAudioIntrpCubic6(void);
+void rbConfigAudioIntrpQuadratic(void);
+void rbConfigAudioIntrpCubic(void);
 void rbConfigAudioIntrpSinc8(void);
 void rbConfigAudioIntrpSinc16(void);
 void rbConfigAudio44kHz(void);
--- a/src/ft2_main.c
+++ b/src/ft2_main.c
@@ -147,7 +147,7 @@
 #ifdef __APPLE__
 	osxSetDirToProgramDirFromArgs(argv);
 #endif
-	if (!setupExecutablePath() || !loadBMPs() || !setupCubicSplineTables() || !setupWindowedSincTables())
+	if (!setupExecutablePath() || !loadBMPs() || !setupQuadraticSplineTable() || !setupCubicSplineTable() || !setupWindowedSincTables())
 	{
 		cleanUpAndExit();
 		return 1;
--- a/src/ft2_radiobuttons.c
+++ b/src/ft2_radiobuttons.c
@@ -88,8 +88,8 @@
 	//x,   y,   w,   group,                               funcOnUp
 	{ 390,  90, 108, RB_GROUP_CONFIG_AUDIO_INTERPOLATION, rbConfigAudioIntrpDisabled },
 	{ 390, 104,  90, RB_GROUP_CONFIG_AUDIO_INTERPOLATION, rbConfigAudioIntrpLinear },
-	{ 390, 118, 101, RB_GROUP_CONFIG_AUDIO_INTERPOLATION, rbConfigAudioIntrpCubic4 },
-	{ 390, 132, 101, RB_GROUP_CONFIG_AUDIO_INTERPOLATION, rbConfigAudioIntrpCubic6 },
+	{ 390, 118, 109, RB_GROUP_CONFIG_AUDIO_INTERPOLATION, rbConfigAudioIntrpQuadratic },
+	{ 390, 132,  85, RB_GROUP_CONFIG_AUDIO_INTERPOLATION, rbConfigAudioIntrpCubic },
 	{ 390, 146,  94, RB_GROUP_CONFIG_AUDIO_INTERPOLATION, rbConfigAudioIntrpSinc8 },
 	{ 390, 160, 101, RB_GROUP_CONFIG_AUDIO_INTERPOLATION, rbConfigAudioIntrpSinc16 },
 
--- a/src/ft2_radiobuttons.h
+++ b/src/ft2_radiobuttons.h
@@ -57,8 +57,8 @@
 	// AUDIO INTERPOLATION
 	RB_CONFIG_AUDIO_INTRP_DISABLED,
 	RB_CONFIG_AUDIO_INTRP_LINEAR,
-	RB_CONFIG_AUDIO_INTRP_CUBIC4,
-	RB_CONFIG_AUDIO_INTRP_CUBIC6,
+	RB_CONFIG_AUDIO_INTRP_QUADRATIC,
+	RB_CONFIG_AUDIO_INTRP_CUBIC,
 	RB_CONFIG_AUDIO_INTRP_SINC8,
 	RB_CONFIG_AUDIO_INTRP_SINC16,
 
--- a/src/ft2_replayer.c
+++ b/src/ft2_replayer.c
@@ -2789,7 +2789,8 @@
 		instr[131] = NULL;
 	}
 
-	freeCubicSplineTables();
+	freeQuadraticSplineTable();
+	freeCubicSplineTable();
 	freeWindowedSincTables();
 }
 
--- a/src/ft2_replayer.h
+++ b/src/ft2_replayer.h
@@ -3,6 +3,7 @@
 #include <stdint.h>
 #include <stdbool.h>
 #include "ft2_unicode.h"
+#include "mixer/ft2_quadratic_spline.h"
 #include "mixer/ft2_cubic_spline.h"
 #include "mixer/ft2_windowed_sinc.h"
 
--- a/src/mixer/ft2_cubic_spline.c
+++ b/src/mixer/ft2_cubic_spline.c
@@ -1,6 +1,4 @@
-/*
-** Cubic Hermite spline (Catmull-Rom) interpolation LUT generator
-*/
+// 4-point cubic Hermite spline (Catmull-Rom) interpolation LUT generator
 
 #include <stdint.h>
 #include <stdbool.h>
@@ -8,33 +6,28 @@
 #include "ft2_cubic_spline.h"
 #include "../ft2_video.h" // showErrorMsgBox()
 
-float *f4PointCubicSplineLUT = NULL, *f6PointCubicSplineLUT = NULL; // globalized
+float *fCubicSplineLUT = NULL; // globalized
 
-bool setupCubicSplineTables(void)
+bool setupCubicSplineTable(void)
 {
-	float *fPtr;
-
-	f4PointCubicSplineLUT = (float *)malloc(4 * CUBIC4P_SPLINE_PHASES * sizeof (float));
-	f6PointCubicSplineLUT = (float *)malloc(6 * CUBIC6P_SPLINE_PHASES * sizeof (float));
-
-	if (f4PointCubicSplineLUT == NULL || f6PointCubicSplineLUT == NULL)
+	fCubicSplineLUT = (float *)malloc(CUBIC_SPLINE_WIDTH * CUBIC_SPLINE_PHASES * sizeof (float));
+	if (fCubicSplineLUT == NULL)
 	{
 		showErrorMsgBox("Not enough memory!");
 		return false;
 	}
 
-	// 4-point Cubic Hermite (Catmull-Rom)
-	fPtr = f4PointCubicSplineLUT;
-	for (int32_t i = 0; i < CUBIC4P_SPLINE_PHASES; i++)
+	float *fPtr = fCubicSplineLUT;
+	for (int32_t i = 0; i < CUBIC_SPLINE_PHASES; i++)
 	{
-		const double x1 = i * (1.0 / CUBIC4P_SPLINE_PHASES);
+		const double x1 = i * (1.0 / CUBIC_SPLINE_PHASES);
 		const double x2 = x1 * x1; // x^2
 		const double x3 = x2 * x1; // x^3
 
-		double t1 = (-(1.0/2.0) * x3) + ( (    1.0) * x2) + (-(1.0/2.0) * x1);
-		double t2 = ( (3.0/2.0) * x3) + (-(5.0/2.0) * x2) + 1.0;
-		double t3 = (-(3.0/2.0) * x3) + ( (    2.0) * x2) + ( (1.0/2.0) * x1);
-		double t4 = ( (1.0/2.0) * x3) + (-(1.0/2.0) * x2);
+		double t1 = (-0.5 * x3) + ( 1.0 * x2) + (-0.5 * x1);
+		double t2 = ( 1.5 * x3) + (-2.5 * x2) + 1.0;
+		double t3 = (-1.5 * x3) + ( 2.0 * x2) + ( 0.5 * x1);
+		double t4 = ( 0.5 * x3) + (-0.5 * x2);
 
 		*fPtr++ = (float)t1;
 		*fPtr++ = (float)t2;
@@ -42,11 +35,11 @@
 		*fPtr++ = (float)t4;
 	}
 
+	/*
 	// 6-point Cubic Hermite (Catmull-Rom)
-	fPtr = f6PointCubicSplineLUT;
-	for (int32_t i = 0; i < CUBIC6P_SPLINE_PHASES; i++)
+	for (int32_t i = 0; i < CUBIC_SPLINE_PHASES; i++)
 	{
-		const double x1 = i * (1.0 / CUBIC6P_SPLINE_PHASES);
+		const double x1 = i * (1.0 / CUBIC_SPLINE_PHASES);
 		const double x2 = x1 * x1; // x^2
 		const double x3 = x2 * x1; // x^3
 
@@ -64,21 +57,16 @@
 		*fPtr++ = (float)t5;
 		*fPtr++ = (float)t6;
 	}
+	*/
 
 	return true;
 }
 
-void freeCubicSplineTables(void)
+void freeCubicSplineTable(void)
 {
-	if (f4PointCubicSplineLUT != NULL)
+	if (fCubicSplineLUT != NULL)
 	{
-		free(f4PointCubicSplineLUT);
-		f4PointCubicSplineLUT = NULL;
-	}
-
-	if (f6PointCubicSplineLUT != NULL)
-	{
-		free(f6PointCubicSplineLUT);
-		f6PointCubicSplineLUT = NULL;
+		free(fCubicSplineLUT);
+		fCubicSplineLUT = NULL;
 	}
 }
--- a/src/mixer/ft2_cubic_spline.h
+++ b/src/mixer/ft2_cubic_spline.h
@@ -4,19 +4,14 @@
 #include <stdbool.h>
 #include "ft2_mix.h" // MIXER_FRAC_BITS
 
-#define CUBIC4P_SPLINE_WIDTH 4
-#define CUBIC4P_SPLINE_WIDTH_BITS 2 /* log2(CUBIC4P_SPLINE_WIDTH */
-#define CUBIC4P_SPLINE_PHASES 8192
-#define CUBIC4P_SPLINE_PHASES_BITS 13 // log2(CUBIC4P_SPLINE_PHASES)
-#define CUBIC4P_SPLINE_FRACSHIFT (MIXER_FRAC_BITS-(CUBIC4P_SPLINE_PHASES_BITS+CUBIC4P_SPLINE_WIDTH_BITS))
-#define CUBIC4P_SPLINE_FRACMASK ((CUBIC4P_SPLINE_WIDTH*CUBIC4P_SPLINE_PHASES)-CUBIC4P_SPLINE_WIDTH)
+#define CUBIC_SPLINE_WIDTH 4
+#define CUBIC_SPLINE_WIDTH_BITS 2 /* log2(CUBIC_SPLINE_WIDTH) */
+#define CUBIC_SPLINE_PHASES 8192
+#define CUBIC_SPLINE_PHASES_BITS 13 // log2(CUBIC_SPLINE_PHASES)
+#define CUBIC_SPLINE_FRACSHIFT (MIXER_FRAC_BITS-(CUBIC_SPLINE_PHASES_BITS+CUBIC_SPLINE_WIDTH_BITS))
+#define CUBIC_SPLINE_FRACMASK ((CUBIC_SPLINE_WIDTH*CUBIC_SPLINE_PHASES)-CUBIC_SPLINE_WIDTH)
 
-#define CUBIC6P_SPLINE_WIDTH 6
-#define CUBIC6P_SPLINE_PHASES 8192
-#define CUBIC6P_SPLINE_PHASES_BITS 13 // log2(CUBIC6P_SPLINE_PHASES)
-#define CUBIC6P_SPLINE_FRACSHIFT (MIXER_FRAC_BITS-CUBIC6P_SPLINE_PHASES_BITS)
+extern float *fCubicSplineLUT;
 
-extern float *f4PointCubicSplineLUT, *f6PointCubicSplineLUT;
-
-bool setupCubicSplineTables(void);
-void freeCubicSplineTables(void);
+bool setupCubicSplineTable(void);
+void freeCubicSplineTable(void);
--- a/src/mixer/ft2_mix.c
+++ b/src/mixer/ft2_mix.c
@@ -9,7 +9,7 @@
 **       (Note: Mixing macros can be found in ft2_mix_macros.h)
 **
 ** Specifications:
-** - Interpolation: None, 2-tap linear, 4-tap/6-tap cubic spline, 8-tap/16-tap windowed-sinc
+** - Interpolation: None, 2-tap linear, 3-tap quadratic spline, 4-tap cubic spline, 8-tap/16-tap windowed-sinc
 ** - FT2-styled linear volume ramping (can be turned off)
 ** - 32.32 fixed-point precision for resampling delta/position
 ** - 32-bit floating-point precision for mixing and interpolation
@@ -641,7 +641,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix8bNoLoopC4PIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+static void mix8bNoLoopCIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
 	float fSample, *fMixBufferL, *fMixBufferR;
@@ -661,19 +661,19 @@
 
 		for (i = 0; i < (samplesToMix & 3); i++)
 		{
-			RENDER_8BIT_SMP_C4PINTRP
+			RENDER_8BIT_SMP_CINTRP
 			INC_POS
 		}
 		samplesToMix >>= 2;
 		for (i = 0; i < samplesToMix; i++)
 		{
-			RENDER_8BIT_SMP_C4PINTRP
+			RENDER_8BIT_SMP_CINTRP
 			INC_POS
-			RENDER_8BIT_SMP_C4PINTRP
+			RENDER_8BIT_SMP_CINTRP
 			INC_POS
-			RENDER_8BIT_SMP_C4PINTRP
+			RENDER_8BIT_SMP_CINTRP
 			INC_POS
-			RENDER_8BIT_SMP_C4PINTRP
+			RENDER_8BIT_SMP_CINTRP
 			INC_POS
 		}
 
@@ -683,7 +683,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix8bLoopC4PIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+static void mix8bLoopCIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
 	int8_t *smpTapPtr;
@@ -707,19 +707,19 @@
 		{
 			for (i = 0; i < (samplesToMix & 3); i++)
 			{
-				RENDER_8BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_8BIT_SMP_CINTRP_TAP_FIX
 				INC_POS
 			}
 			samplesToMix >>= 2;
 			for (i = 0; i < samplesToMix; i++)
 			{
-				RENDER_8BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_8BIT_SMP_CINTRP_TAP_FIX
 				INC_POS
-				RENDER_8BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_8BIT_SMP_CINTRP_TAP_FIX
 				INC_POS
-				RENDER_8BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_8BIT_SMP_CINTRP_TAP_FIX
 				INC_POS
-				RENDER_8BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_8BIT_SMP_CINTRP_TAP_FIX
 				INC_POS
 			}
 		}
@@ -727,19 +727,19 @@
 		{
 			for (i = 0; i < (samplesToMix & 3); i++)
 			{
-				RENDER_8BIT_SMP_C4PINTRP
+				RENDER_8BIT_SMP_CINTRP
 				INC_POS
 			}
 			samplesToMix >>= 2;
 			for (i = 0; i < samplesToMix; i++)
 			{
-				RENDER_8BIT_SMP_C4PINTRP
+				RENDER_8BIT_SMP_CINTRP
 				INC_POS
-				RENDER_8BIT_SMP_C4PINTRP
+				RENDER_8BIT_SMP_CINTRP
 				INC_POS
-				RENDER_8BIT_SMP_C4PINTRP
+				RENDER_8BIT_SMP_CINTRP
 				INC_POS
-				RENDER_8BIT_SMP_C4PINTRP
+				RENDER_8BIT_SMP_CINTRP
 				INC_POS
 			}
 		}
@@ -750,7 +750,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix8bBidiLoopC4PIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+static void mix8bBidiLoopCIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *revBase, *smpPtr;
 	int8_t *smpTapPtr;
@@ -775,19 +775,19 @@
 		{
 			for (i = 0; i < (samplesToMix & 3); i++)
 			{
-				RENDER_8BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_8BIT_SMP_CINTRP_TAP_FIX
 				INC_POS_BIDI
 			}
 			samplesToMix >>= 2;
 			for (i = 0; i < samplesToMix; i++)
 			{
-				RENDER_8BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_8BIT_SMP_CINTRP_TAP_FIX
 				INC_POS_BIDI
-				RENDER_8BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_8BIT_SMP_CINTRP_TAP_FIX
 				INC_POS_BIDI
-				RENDER_8BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_8BIT_SMP_CINTRP_TAP_FIX
 				INC_POS_BIDI
-				RENDER_8BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_8BIT_SMP_CINTRP_TAP_FIX
 				INC_POS_BIDI
 			}
 		}
@@ -795,19 +795,19 @@
 		{
 			for (i = 0; i < (samplesToMix & 3); i++)
 			{
-				RENDER_8BIT_SMP_C4PINTRP
+				RENDER_8BIT_SMP_CINTRP
 				INC_POS_BIDI
 			}
 			samplesToMix >>= 2;
 			for (i = 0; i < samplesToMix; i++)
 			{
-				RENDER_8BIT_SMP_C4PINTRP
+				RENDER_8BIT_SMP_CINTRP
 				INC_POS_BIDI
-				RENDER_8BIT_SMP_C4PINTRP
+				RENDER_8BIT_SMP_CINTRP
 				INC_POS_BIDI
-				RENDER_8BIT_SMP_C4PINTRP
+				RENDER_8BIT_SMP_CINTRP
 				INC_POS_BIDI
-				RENDER_8BIT_SMP_C4PINTRP
+				RENDER_8BIT_SMP_CINTRP
 				INC_POS_BIDI
 			}
 		}
@@ -819,7 +819,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix8bNoLoopC6PIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+static void mix8bNoLoopQIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
 	float fSample, *fMixBufferL, *fMixBufferR;
@@ -839,19 +839,19 @@
 
 		for (i = 0; i < (samplesToMix & 3); i++)
 		{
-			RENDER_8BIT_SMP_C6PINTRP
+			RENDER_8BIT_SMP_QINTRP
 			INC_POS
 		}
 		samplesToMix >>= 2;
 		for (i = 0; i < samplesToMix; i++)
 		{
-			RENDER_8BIT_SMP_C6PINTRP
+			RENDER_8BIT_SMP_QINTRP
 			INC_POS
-			RENDER_8BIT_SMP_C6PINTRP
+			RENDER_8BIT_SMP_QINTRP
 			INC_POS
-			RENDER_8BIT_SMP_C6PINTRP
+			RENDER_8BIT_SMP_QINTRP
 			INC_POS
-			RENDER_8BIT_SMP_C6PINTRP
+			RENDER_8BIT_SMP_QINTRP
 			INC_POS
 		}
 
@@ -861,10 +861,9 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix8bLoopC6PIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+static void mix8bLoopQIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
-	int8_t *smpTapPtr;
 	float fSample, *fMixBufferL, *fMixBufferR;
 	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
@@ -873,7 +872,6 @@
 	GET_VOL
 	GET_MIXER_VARS
 	SET_BASE8
-	PREPARE_TAP_FIX8
 
 	samplesLeft = numSamples;
 	while (samplesLeft > 0)
@@ -881,45 +879,22 @@
 		LIMIT_MIX_NUM
 		samplesLeft -= samplesToMix;
 
-		if (v->hasLooped) // the negative interpolation taps need a special case after the sample has looped once
+		for (i = 0; i < (samplesToMix & 3); i++)
 		{
-			for (i = 0; i < (samplesToMix & 3); i++)
-			{
-				RENDER_8BIT_SMP_C6PINTRP_TAP_FIX
-				INC_POS
-			}
-			samplesToMix >>= 2;
-			for (i = 0; i < samplesToMix; i++)
-			{
-				RENDER_8BIT_SMP_C6PINTRP_TAP_FIX
-				INC_POS
-				RENDER_8BIT_SMP_C6PINTRP_TAP_FIX
-				INC_POS
-				RENDER_8BIT_SMP_C6PINTRP_TAP_FIX
-				INC_POS
-				RENDER_8BIT_SMP_C6PINTRP_TAP_FIX
-				INC_POS
-			}
+			RENDER_8BIT_SMP_QINTRP
+			INC_POS
 		}
-		else
+		samplesToMix >>= 2;
+		for (i = 0; i < samplesToMix; i++)
 		{
-			for (i = 0; i < (samplesToMix & 3); i++)
-			{
-				RENDER_8BIT_SMP_C6PINTRP
-				INC_POS
-			}
-			samplesToMix >>= 2;
-			for (i = 0; i < samplesToMix; i++)
-			{
-				RENDER_8BIT_SMP_C6PINTRP
-				INC_POS
-				RENDER_8BIT_SMP_C6PINTRP
-				INC_POS
-				RENDER_8BIT_SMP_C6PINTRP
-				INC_POS
-				RENDER_8BIT_SMP_C6PINTRP
-				INC_POS
-			}
+			RENDER_8BIT_SMP_QINTRP
+			INC_POS
+			RENDER_8BIT_SMP_QINTRP
+			INC_POS
+			RENDER_8BIT_SMP_QINTRP
+			INC_POS
+			RENDER_8BIT_SMP_QINTRP
+			INC_POS
 		}
 
 		WRAP_LOOP
@@ -928,10 +903,9 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix8bBidiLoopC6PIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+static void mix8bBidiLoopQIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *revBase, *smpPtr;
-	int8_t *smpTapPtr;
 	float fSample, *fMixBufferL, *fMixBufferR;
 	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
@@ -940,7 +914,6 @@
 	GET_VOL
 	GET_MIXER_VARS
 	SET_BASE8_BIDI
-	PREPARE_TAP_FIX8
 
 	samplesLeft = numSamples;
 	while (samplesLeft > 0)
@@ -949,45 +922,22 @@
 		samplesLeft -= samplesToMix;
 
 		START_BIDI
-		if (v->hasLooped) // the negative interpolation taps need a special case after the sample has looped once
+		for (i = 0; i < (samplesToMix & 3); i++)
 		{
-			for (i = 0; i < (samplesToMix & 3); i++)
-			{
-				RENDER_8BIT_SMP_C6PINTRP_TAP_FIX
-				INC_POS_BIDI
-			}
-			samplesToMix >>= 2;
-			for (i = 0; i < samplesToMix; i++)
-			{
-				RENDER_8BIT_SMP_C6PINTRP_TAP_FIX
-				INC_POS_BIDI
-				RENDER_8BIT_SMP_C6PINTRP_TAP_FIX
-				INC_POS_BIDI
-				RENDER_8BIT_SMP_C6PINTRP_TAP_FIX
-				INC_POS_BIDI
-				RENDER_8BIT_SMP_C6PINTRP_TAP_FIX
-				INC_POS_BIDI
-			}
+			RENDER_8BIT_SMP_QINTRP
+			INC_POS_BIDI
 		}
-		else
+		samplesToMix >>= 2;
+		for (i = 0; i < samplesToMix; i++)
 		{
-			for (i = 0; i < (samplesToMix & 3); i++)
-			{
-				RENDER_8BIT_SMP_C6PINTRP
-				INC_POS_BIDI
-			}
-			samplesToMix >>= 2;
-			for (i = 0; i < samplesToMix; i++)
-			{
-				RENDER_8BIT_SMP_C6PINTRP
-				INC_POS_BIDI
-				RENDER_8BIT_SMP_C6PINTRP
-				INC_POS_BIDI
-				RENDER_8BIT_SMP_C6PINTRP
-				INC_POS_BIDI
-				RENDER_8BIT_SMP_C6PINTRP
-				INC_POS_BIDI
-			}
+			RENDER_8BIT_SMP_QINTRP
+			INC_POS_BIDI
+			RENDER_8BIT_SMP_QINTRP
+			INC_POS_BIDI
+			RENDER_8BIT_SMP_QINTRP
+			INC_POS_BIDI
+			RENDER_8BIT_SMP_QINTRP
+			INC_POS_BIDI
 		}
 		END_BIDI
 
@@ -1726,7 +1676,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix8bRampNoLoopC4PIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+static void mix8bRampNoLoopCIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
 	float fSample, *fMixBufferL, *fMixBufferR;
@@ -1748,7 +1698,7 @@
 
 		for (i = 0; i < (samplesToMix & 3); i++)
 		{
-			RENDER_8BIT_SMP_C4PINTRP
+			RENDER_8BIT_SMP_CINTRP
 			VOLUME_RAMPING
 			INC_POS
 		}
@@ -1755,16 +1705,16 @@
 		samplesToMix >>= 2;
 		for (i = 0; i < samplesToMix; i++)
 		{
-			RENDER_8BIT_SMP_C4PINTRP
+			RENDER_8BIT_SMP_CINTRP
 			VOLUME_RAMPING
 			INC_POS
-			RENDER_8BIT_SMP_C4PINTRP
+			RENDER_8BIT_SMP_CINTRP
 			VOLUME_RAMPING
 			INC_POS
-			RENDER_8BIT_SMP_C4PINTRP
+			RENDER_8BIT_SMP_CINTRP
 			VOLUME_RAMPING
 			INC_POS
-			RENDER_8BIT_SMP_C4PINTRP
+			RENDER_8BIT_SMP_CINTRP
 			VOLUME_RAMPING
 			INC_POS
 		}
@@ -1776,7 +1726,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix8bRampLoopC4PIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+static void mix8bRampLoopCIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
 	int8_t *smpTapPtr;
@@ -1802,7 +1752,7 @@
 		{
 			for (i = 0; i < (samplesToMix & 3); i++)
 			{
-				RENDER_8BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_8BIT_SMP_CINTRP_TAP_FIX
 				VOLUME_RAMPING
 				INC_POS
 			}
@@ -1809,16 +1759,16 @@
 			samplesToMix >>= 2;
 			for (i = 0; i < samplesToMix; i++)
 			{
-				RENDER_8BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_8BIT_SMP_CINTRP_TAP_FIX
 				VOLUME_RAMPING
 				INC_POS
-				RENDER_8BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_8BIT_SMP_CINTRP_TAP_FIX
 				VOLUME_RAMPING
 				INC_POS
-				RENDER_8BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_8BIT_SMP_CINTRP_TAP_FIX
 				VOLUME_RAMPING
 				INC_POS
-				RENDER_8BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_8BIT_SMP_CINTRP_TAP_FIX
 				VOLUME_RAMPING
 				INC_POS
 			}
@@ -1827,7 +1777,7 @@
 		{
 			for (i = 0; i < (samplesToMix & 3); i++)
 			{
-				RENDER_8BIT_SMP_C4PINTRP
+				RENDER_8BIT_SMP_CINTRP
 				VOLUME_RAMPING
 				INC_POS
 			}
@@ -1834,16 +1784,16 @@
 			samplesToMix >>= 2;
 			for (i = 0; i < samplesToMix; i++)
 			{
-				RENDER_8BIT_SMP_C4PINTRP
+				RENDER_8BIT_SMP_CINTRP
 				VOLUME_RAMPING
 				INC_POS
-				RENDER_8BIT_SMP_C4PINTRP
+				RENDER_8BIT_SMP_CINTRP
 				VOLUME_RAMPING
 				INC_POS
-				RENDER_8BIT_SMP_C4PINTRP
+				RENDER_8BIT_SMP_CINTRP
 				VOLUME_RAMPING
 				INC_POS
-				RENDER_8BIT_SMP_C4PINTRP
+				RENDER_8BIT_SMP_CINTRP
 				VOLUME_RAMPING
 				INC_POS
 			}
@@ -1856,7 +1806,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix8bRampBidiLoopC4PIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+static void mix8bRampBidiLoopCIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *revBase, *smpPtr;
 	int8_t *smpTapPtr;
@@ -1883,7 +1833,7 @@
 		{
 			for (i = 0; i < (samplesToMix & 3); i++)
 			{
-				RENDER_8BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_8BIT_SMP_CINTRP_TAP_FIX
 				VOLUME_RAMPING
 				INC_POS_BIDI
 			}
@@ -1890,16 +1840,16 @@
 			samplesToMix >>= 2;
 			for (i = 0; i < samplesToMix; i++)
 			{
-				RENDER_8BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_8BIT_SMP_CINTRP_TAP_FIX
 				VOLUME_RAMPING
 				INC_POS_BIDI
-				RENDER_8BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_8BIT_SMP_CINTRP_TAP_FIX
 				VOLUME_RAMPING
 				INC_POS_BIDI
-				RENDER_8BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_8BIT_SMP_CINTRP_TAP_FIX
 				VOLUME_RAMPING
 				INC_POS_BIDI
-				RENDER_8BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_8BIT_SMP_CINTRP_TAP_FIX
 				VOLUME_RAMPING
 				INC_POS_BIDI
 			}
@@ -1908,7 +1858,7 @@
 		{
 			for (i = 0; i < (samplesToMix & 3); i++)
 			{
-				RENDER_8BIT_SMP_C4PINTRP
+				RENDER_8BIT_SMP_CINTRP
 				VOLUME_RAMPING
 				INC_POS_BIDI
 			}
@@ -1915,16 +1865,16 @@
 			samplesToMix >>= 2;
 			for (i = 0; i < samplesToMix; i++)
 			{
-				RENDER_8BIT_SMP_C4PINTRP
+				RENDER_8BIT_SMP_CINTRP
 				VOLUME_RAMPING
 				INC_POS_BIDI
-				RENDER_8BIT_SMP_C4PINTRP
+				RENDER_8BIT_SMP_CINTRP
 				VOLUME_RAMPING
 				INC_POS_BIDI
-				RENDER_8BIT_SMP_C4PINTRP
+				RENDER_8BIT_SMP_CINTRP
 				VOLUME_RAMPING
 				INC_POS_BIDI
-				RENDER_8BIT_SMP_C4PINTRP
+				RENDER_8BIT_SMP_CINTRP
 				VOLUME_RAMPING
 				INC_POS_BIDI
 			}
@@ -1938,7 +1888,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix8bRampNoLoopC6PIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+static void mix8bRampNoLoopQIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
 	float fSample, *fMixBufferL, *fMixBufferR;
@@ -1960,7 +1910,7 @@
 
 		for (i = 0; i < (samplesToMix & 3); i++)
 		{
-			RENDER_8BIT_SMP_C6PINTRP
+			RENDER_8BIT_SMP_QINTRP
 			VOLUME_RAMPING
 			INC_POS
 		}
@@ -1967,16 +1917,16 @@
 		samplesToMix >>= 2;
 		for (i = 0; i < samplesToMix; i++)
 		{
-			RENDER_8BIT_SMP_C6PINTRP
+			RENDER_8BIT_SMP_QINTRP
 			VOLUME_RAMPING
 			INC_POS
-			RENDER_8BIT_SMP_C6PINTRP
+			RENDER_8BIT_SMP_QINTRP
 			VOLUME_RAMPING
 			INC_POS
-			RENDER_8BIT_SMP_C6PINTRP
+			RENDER_8BIT_SMP_QINTRP
 			VOLUME_RAMPING
 			INC_POS
-			RENDER_8BIT_SMP_C6PINTRP
+			RENDER_8BIT_SMP_QINTRP
 			VOLUME_RAMPING
 			INC_POS
 		}
@@ -1988,10 +1938,9 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix8bRampLoopC6PIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+static void mix8bRampLoopQIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
-	int8_t *smpTapPtr;
 	float fSample, *fMixBufferL, *fMixBufferR;
 	int32_t position;
 	float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR;
@@ -2001,7 +1950,6 @@
 	GET_VOL_RAMP
 	GET_MIXER_VARS_RAMP
 	SET_BASE8
-	PREPARE_TAP_FIX8
 
 	samplesLeft = numSamples;
 	while (samplesLeft > 0)
@@ -2010,55 +1958,27 @@
 		LIMIT_MIX_NUM_RAMP
 		samplesLeft -= samplesToMix;
 		
-		if (v->hasLooped) // the negative interpolation taps need a special case after the sample has looped once
+		for (i = 0; i < (samplesToMix & 3); i++)
 		{
-			for (i = 0; i < (samplesToMix & 3); i++)
-			{
-				RENDER_8BIT_SMP_C6PINTRP_TAP_FIX
-				VOLUME_RAMPING
-				INC_POS
-			}
-			samplesToMix >>= 2;
-			for (i = 0; i < samplesToMix; i++)
-			{
-				RENDER_8BIT_SMP_C6PINTRP_TAP_FIX
-				VOLUME_RAMPING
-				INC_POS
-				RENDER_8BIT_SMP_C6PINTRP_TAP_FIX
-				VOLUME_RAMPING
-				INC_POS
-				RENDER_8BIT_SMP_C6PINTRP_TAP_FIX
-				VOLUME_RAMPING
-				INC_POS
-				RENDER_8BIT_SMP_C6PINTRP_TAP_FIX
-				VOLUME_RAMPING
-				INC_POS
-			}
+			RENDER_8BIT_SMP_QINTRP
+			VOLUME_RAMPING
+			INC_POS
 		}
-		else
+		samplesToMix >>= 2;
+		for (i = 0; i < samplesToMix; i++)
 		{
-			for (i = 0; i < (samplesToMix & 3); i++)
-			{
-				RENDER_8BIT_SMP_C6PINTRP
-				VOLUME_RAMPING
-				INC_POS
-			}
-			samplesToMix >>= 2;
-			for (i = 0; i < samplesToMix; i++)
-			{
-				RENDER_8BIT_SMP_C6PINTRP
-				VOLUME_RAMPING
-				INC_POS
-				RENDER_8BIT_SMP_C6PINTRP
-				VOLUME_RAMPING
-				INC_POS
-				RENDER_8BIT_SMP_C6PINTRP
-				VOLUME_RAMPING
-				INC_POS
-				RENDER_8BIT_SMP_C6PINTRP
-				VOLUME_RAMPING
-				INC_POS
-			}
+			RENDER_8BIT_SMP_QINTRP
+			VOLUME_RAMPING
+			INC_POS
+			RENDER_8BIT_SMP_QINTRP
+			VOLUME_RAMPING
+			INC_POS
+			RENDER_8BIT_SMP_QINTRP
+			VOLUME_RAMPING
+			INC_POS
+			RENDER_8BIT_SMP_QINTRP
+			VOLUME_RAMPING
+			INC_POS
 		}
 
 		WRAP_LOOP
@@ -2068,10 +1988,9 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix8bRampBidiLoopC6PIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+static void mix8bRampBidiLoopQIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *revBase, *smpPtr;
-	int8_t *smpTapPtr;
 	float fSample, *fMixBufferL, *fMixBufferR;
 	int32_t position;
 	float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR;
@@ -2081,7 +2000,6 @@
 	GET_VOL_RAMP
 	GET_MIXER_VARS_RAMP
 	SET_BASE8_BIDI
-	PREPARE_TAP_FIX8
 
 	samplesLeft = numSamples;
 	while (samplesLeft > 0)
@@ -2091,55 +2009,27 @@
 		samplesLeft -= samplesToMix;
 
 		START_BIDI
-		if (v->hasLooped) // the negative interpolation taps need a special case after the sample has looped once
+		for (i = 0; i < (samplesToMix & 3); i++)
 		{
-			for (i = 0; i < (samplesToMix & 3); i++)
-			{
-				RENDER_8BIT_SMP_C6PINTRP_TAP_FIX
-				VOLUME_RAMPING
-				INC_POS_BIDI
-			}
-			samplesToMix >>= 2;
-			for (i = 0; i < samplesToMix; i++)
-			{
-				RENDER_8BIT_SMP_C6PINTRP_TAP_FIX
-				VOLUME_RAMPING
-				INC_POS_BIDI
-				RENDER_8BIT_SMP_C6PINTRP_TAP_FIX
-				VOLUME_RAMPING
-				INC_POS_BIDI
-				RENDER_8BIT_SMP_C6PINTRP_TAP_FIX
-				VOLUME_RAMPING
-				INC_POS_BIDI
-				RENDER_8BIT_SMP_C6PINTRP_TAP_FIX
-				VOLUME_RAMPING
-				INC_POS_BIDI
-			}
+			RENDER_8BIT_SMP_QINTRP
+			VOLUME_RAMPING
+			INC_POS_BIDI
 		}
-		else
+		samplesToMix >>= 2;
+		for (i = 0; i < samplesToMix; i++)
 		{
-			for (i = 0; i < (samplesToMix & 3); i++)
-			{
-				RENDER_8BIT_SMP_C6PINTRP
-				VOLUME_RAMPING
-				INC_POS_BIDI
-			}
-			samplesToMix >>= 2;
-			for (i = 0; i < samplesToMix; i++)
-			{
-				RENDER_8BIT_SMP_C6PINTRP
-				VOLUME_RAMPING
-				INC_POS_BIDI
-				RENDER_8BIT_SMP_C6PINTRP
-				VOLUME_RAMPING
-				INC_POS_BIDI
-				RENDER_8BIT_SMP_C6PINTRP
-				VOLUME_RAMPING
-				INC_POS_BIDI
-				RENDER_8BIT_SMP_C6PINTRP
-				VOLUME_RAMPING
-				INC_POS_BIDI
-			}
+			RENDER_8BIT_SMP_QINTRP
+			VOLUME_RAMPING
+			INC_POS_BIDI
+			RENDER_8BIT_SMP_QINTRP
+			VOLUME_RAMPING
+			INC_POS_BIDI
+			RENDER_8BIT_SMP_QINTRP
+			VOLUME_RAMPING
+			INC_POS_BIDI
+			RENDER_8BIT_SMP_QINTRP
+			VOLUME_RAMPING
+			INC_POS_BIDI
 		}
 		END_BIDI
 
@@ -2766,7 +2656,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix16bNoLoopC4PIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+static void mix16bNoLoopCIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
 	float fSample, *fMixBufferL, *fMixBufferR;
@@ -2786,19 +2676,19 @@
 
 		for (i = 0; i < (samplesToMix & 3); i++)
 		{
-			RENDER_16BIT_SMP_C4PINTRP
+			RENDER_16BIT_SMP_CINTRP
 			INC_POS
 		}
 		samplesToMix >>= 2;
 		for (i = 0; i < samplesToMix; i++)
 		{
-			RENDER_16BIT_SMP_C4PINTRP
+			RENDER_16BIT_SMP_CINTRP
 			INC_POS
-			RENDER_16BIT_SMP_C4PINTRP
+			RENDER_16BIT_SMP_CINTRP
 			INC_POS
-			RENDER_16BIT_SMP_C4PINTRP
+			RENDER_16BIT_SMP_CINTRP
 			INC_POS
-			RENDER_16BIT_SMP_C4PINTRP
+			RENDER_16BIT_SMP_CINTRP
 			INC_POS
 		}
 
@@ -2808,7 +2698,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix16bLoopC4PIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+static void mix16bLoopCIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
 	int16_t *smpTapPtr;
@@ -2832,19 +2722,19 @@
 		{
 			for (i = 0; i < (samplesToMix & 3); i++)
 			{
-				RENDER_16BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_16BIT_SMP_CINTRP_TAP_FIX
 				INC_POS
 			}
 			samplesToMix >>= 2;
 			for (i = 0; i < samplesToMix; i++)
 			{
-				RENDER_16BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_16BIT_SMP_CINTRP_TAP_FIX
 				INC_POS
-				RENDER_16BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_16BIT_SMP_CINTRP_TAP_FIX
 				INC_POS
-				RENDER_16BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_16BIT_SMP_CINTRP_TAP_FIX
 				INC_POS
-				RENDER_16BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_16BIT_SMP_CINTRP_TAP_FIX
 				INC_POS
 			}
 		}
@@ -2852,19 +2742,19 @@
 		{
 			for (i = 0; i < (samplesToMix & 3); i++)
 			{
-				RENDER_16BIT_SMP_C4PINTRP
+				RENDER_16BIT_SMP_CINTRP
 				INC_POS
 			}
 			samplesToMix >>= 2;
 			for (i = 0; i < samplesToMix; i++)
 			{
-				RENDER_16BIT_SMP_C4PINTRP
+				RENDER_16BIT_SMP_CINTRP
 				INC_POS
-				RENDER_16BIT_SMP_C4PINTRP
+				RENDER_16BIT_SMP_CINTRP
 				INC_POS
-				RENDER_16BIT_SMP_C4PINTRP
+				RENDER_16BIT_SMP_CINTRP
 				INC_POS
-				RENDER_16BIT_SMP_C4PINTRP
+				RENDER_16BIT_SMP_CINTRP
 				INC_POS
 			}
 		}
@@ -2875,7 +2765,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix16bBidiLoopC4PIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+static void mix16bBidiLoopCIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *revBase, *smpPtr;
 	int16_t *smpTapPtr;
@@ -2900,19 +2790,19 @@
 		{
 			for (i = 0; i < (samplesToMix & 3); i++)
 			{
-				RENDER_16BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_16BIT_SMP_CINTRP_TAP_FIX
 				INC_POS_BIDI
 			}
 			samplesToMix >>= 2;
 			for (i = 0; i < samplesToMix; i++)
 			{
-				RENDER_16BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_16BIT_SMP_CINTRP_TAP_FIX
 				INC_POS_BIDI
-				RENDER_16BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_16BIT_SMP_CINTRP_TAP_FIX
 				INC_POS_BIDI
-				RENDER_16BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_16BIT_SMP_CINTRP_TAP_FIX
 				INC_POS_BIDI
-				RENDER_16BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_16BIT_SMP_CINTRP_TAP_FIX
 				INC_POS_BIDI
 			}
 		}
@@ -2920,19 +2810,19 @@
 		{
 			for (i = 0; i < (samplesToMix & 3); i++)
 			{
-				RENDER_16BIT_SMP_C4PINTRP
+				RENDER_16BIT_SMP_CINTRP
 				INC_POS_BIDI
 			}
 			samplesToMix >>= 2;
 			for (i = 0; i < samplesToMix; i++)
 			{
-				RENDER_16BIT_SMP_C4PINTRP
+				RENDER_16BIT_SMP_CINTRP
 				INC_POS_BIDI
-				RENDER_16BIT_SMP_C4PINTRP
+				RENDER_16BIT_SMP_CINTRP
 				INC_POS_BIDI
-				RENDER_16BIT_SMP_C4PINTRP
+				RENDER_16BIT_SMP_CINTRP
 				INC_POS_BIDI
-				RENDER_16BIT_SMP_C4PINTRP
+				RENDER_16BIT_SMP_CINTRP
 				INC_POS_BIDI
 			}
 		}
@@ -2944,7 +2834,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix16bNoLoopC6PIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+static void mix16bNoLoopQIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
 	float fSample, *fMixBufferL, *fMixBufferR;
@@ -2964,19 +2854,19 @@
 
 		for (i = 0; i < (samplesToMix & 3); i++)
 		{
-			RENDER_16BIT_SMP_C6PINTRP
+			RENDER_16BIT_SMP_QINTRP
 			INC_POS
 		}
 		samplesToMix >>= 2;
 		for (i = 0; i < samplesToMix; i++)
 		{
-			RENDER_16BIT_SMP_C6PINTRP
+			RENDER_16BIT_SMP_QINTRP
 			INC_POS
-			RENDER_16BIT_SMP_C6PINTRP
+			RENDER_16BIT_SMP_QINTRP
 			INC_POS
-			RENDER_16BIT_SMP_C6PINTRP
+			RENDER_16BIT_SMP_QINTRP
 			INC_POS
-			RENDER_16BIT_SMP_C6PINTRP
+			RENDER_16BIT_SMP_QINTRP
 			INC_POS
 		}
 
@@ -2986,10 +2876,9 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix16bLoopC6PIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+static void mix16bLoopQIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
-	int16_t *smpTapPtr;
 	float fSample, *fMixBufferL, *fMixBufferR;
 	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
@@ -2998,7 +2887,6 @@
 	GET_VOL
 	GET_MIXER_VARS
 	SET_BASE16
-	PREPARE_TAP_FIX16
 
 	samplesLeft = numSamples;
 	while (samplesLeft > 0)
@@ -3006,47 +2894,24 @@
 		LIMIT_MIX_NUM
 		samplesLeft -= samplesToMix;
 
-		if (v->hasLooped) // the negative interpolation taps need a special case after the sample has looped once
+		for (i = 0; i < (samplesToMix & 3); i++)
 		{
-			for (i = 0; i < (samplesToMix & 3); i++)
-			{
-				RENDER_16BIT_SMP_C6PINTRP_TAP_FIX
-				INC_POS
-			}
-			samplesToMix >>= 2;
-			for (i = 0; i < samplesToMix; i++)
-			{
-				RENDER_16BIT_SMP_C6PINTRP_TAP_FIX
-				INC_POS
-				RENDER_16BIT_SMP_C6PINTRP_TAP_FIX
-				INC_POS
-				RENDER_16BIT_SMP_C6PINTRP_TAP_FIX
-				INC_POS
-				RENDER_16BIT_SMP_C6PINTRP_TAP_FIX
-				INC_POS
-			}
+			RENDER_16BIT_SMP_QINTRP
+			INC_POS
 		}
-		else
+		samplesToMix >>= 2;
+		for (i = 0; i < samplesToMix; i++)
 		{
-			for (i = 0; i < (samplesToMix & 3); i++)
-			{
-				RENDER_16BIT_SMP_C6PINTRP
-				INC_POS
-			}
-			samplesToMix >>= 2;
-			for (i = 0; i < samplesToMix; i++)
-			{
-				RENDER_16BIT_SMP_C6PINTRP
-				INC_POS
-				RENDER_16BIT_SMP_C6PINTRP
-				INC_POS
-				RENDER_16BIT_SMP_C6PINTRP
-				INC_POS
-				RENDER_16BIT_SMP_C6PINTRP
-				INC_POS
-			}
+			RENDER_16BIT_SMP_QINTRP
+			INC_POS
+			RENDER_16BIT_SMP_QINTRP
+			INC_POS
+			RENDER_16BIT_SMP_QINTRP
+			INC_POS
+			RENDER_16BIT_SMP_QINTRP
+			INC_POS
 		}
-
+		
 		WRAP_LOOP
 	}
 
@@ -3053,10 +2918,9 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix16bBidiLoopC6PIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+static void mix16bBidiLoopQIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *revBase, *smpPtr;
-	int16_t *smpTapPtr;
 	float fSample, *fMixBufferL, *fMixBufferR;
 	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
@@ -3065,7 +2929,6 @@
 	GET_VOL
 	GET_MIXER_VARS
 	SET_BASE16_BIDI
-	PREPARE_TAP_FIX16
 
 	samplesLeft = numSamples;
 	while (samplesLeft > 0)
@@ -3074,45 +2937,22 @@
 		samplesLeft -= samplesToMix;
 
 		START_BIDI
-		if (v->hasLooped) // the negative interpolation taps need a special case after the sample has looped once
+		for (i = 0; i < (samplesToMix & 3); i++)
 		{
-			for (i = 0; i < (samplesToMix & 3); i++)
-			{
-				RENDER_16BIT_SMP_C6PINTRP_TAP_FIX
-				INC_POS_BIDI
-			}
-			samplesToMix >>= 2;
-			for (i = 0; i < samplesToMix; i++)
-			{
-				RENDER_16BIT_SMP_C6PINTRP_TAP_FIX
-				INC_POS_BIDI
-				RENDER_16BIT_SMP_C6PINTRP_TAP_FIX
-				INC_POS_BIDI
-				RENDER_16BIT_SMP_C6PINTRP_TAP_FIX
-				INC_POS_BIDI
-				RENDER_16BIT_SMP_C6PINTRP_TAP_FIX
-				INC_POS_BIDI
-			}
+			RENDER_16BIT_SMP_QINTRP
+			INC_POS_BIDI
 		}
-		else
+		samplesToMix >>= 2;
+		for (i = 0; i < samplesToMix; i++)
 		{
-			for (i = 0; i < (samplesToMix & 3); i++)
-			{
-				RENDER_16BIT_SMP_C6PINTRP
-				INC_POS_BIDI
-			}
-			samplesToMix >>= 2;
-			for (i = 0; i < samplesToMix; i++)
-			{
-				RENDER_16BIT_SMP_C6PINTRP
-				INC_POS_BIDI
-				RENDER_16BIT_SMP_C6PINTRP
-				INC_POS_BIDI
-				RENDER_16BIT_SMP_C6PINTRP
-				INC_POS_BIDI
-				RENDER_16BIT_SMP_C6PINTRP
-				INC_POS_BIDI
-			}
+			RENDER_16BIT_SMP_QINTRP
+			INC_POS_BIDI
+			RENDER_16BIT_SMP_QINTRP
+			INC_POS_BIDI
+			RENDER_16BIT_SMP_QINTRP
+			INC_POS_BIDI
+			RENDER_16BIT_SMP_QINTRP
+			INC_POS_BIDI
 		}
 		END_BIDI
 
@@ -3850,7 +3690,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix16bRampNoLoopC4PIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+static void mix16bRampNoLoopCIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
 	float fSample, *fMixBufferL, *fMixBufferR;
@@ -3872,7 +3712,7 @@
 
 		for (i = 0; i < (samplesToMix & 3); i++)
 		{
-			RENDER_16BIT_SMP_C4PINTRP
+			RENDER_16BIT_SMP_CINTRP
 			VOLUME_RAMPING
 			INC_POS
 		}
@@ -3879,16 +3719,16 @@
 		samplesToMix >>= 2;
 		for (i = 0; i < samplesToMix; i++)
 		{
-			RENDER_16BIT_SMP_C4PINTRP
+			RENDER_16BIT_SMP_CINTRP
 			VOLUME_RAMPING
 			INC_POS
-			RENDER_16BIT_SMP_C4PINTRP
+			RENDER_16BIT_SMP_CINTRP
 			VOLUME_RAMPING
 			INC_POS
-			RENDER_16BIT_SMP_C4PINTRP
+			RENDER_16BIT_SMP_CINTRP
 			VOLUME_RAMPING
 			INC_POS
-			RENDER_16BIT_SMP_C4PINTRP
+			RENDER_16BIT_SMP_CINTRP
 			VOLUME_RAMPING
 			INC_POS
 		}
@@ -3900,7 +3740,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix16bRampLoopC4PIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+static void mix16bRampLoopCIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
 	int16_t *smpTapPtr;
@@ -3926,7 +3766,7 @@
 		{
 			for (i = 0; i < (samplesToMix & 3); i++)
 			{
-				RENDER_16BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_16BIT_SMP_CINTRP_TAP_FIX
 				VOLUME_RAMPING
 				INC_POS
 			}
@@ -3933,16 +3773,16 @@
 			samplesToMix >>= 2;
 			for (i = 0; i < samplesToMix; i++)
 			{
-				RENDER_16BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_16BIT_SMP_CINTRP_TAP_FIX
 				VOLUME_RAMPING
 				INC_POS
-				RENDER_16BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_16BIT_SMP_CINTRP_TAP_FIX
 				VOLUME_RAMPING
 				INC_POS
-				RENDER_16BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_16BIT_SMP_CINTRP_TAP_FIX
 				VOLUME_RAMPING
 				INC_POS
-				RENDER_16BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_16BIT_SMP_CINTRP_TAP_FIX
 				VOLUME_RAMPING
 				INC_POS
 			}
@@ -3951,7 +3791,7 @@
 		{
 			for (i = 0; i < (samplesToMix & 3); i++)
 			{
-				RENDER_16BIT_SMP_C4PINTRP
+				RENDER_16BIT_SMP_CINTRP
 				VOLUME_RAMPING
 				INC_POS
 			}
@@ -3958,16 +3798,16 @@
 			samplesToMix >>= 2;
 			for (i = 0; i < samplesToMix; i++)
 			{
-				RENDER_16BIT_SMP_C4PINTRP
+				RENDER_16BIT_SMP_CINTRP
 				VOLUME_RAMPING
 				INC_POS
-				RENDER_16BIT_SMP_C4PINTRP
+				RENDER_16BIT_SMP_CINTRP
 				VOLUME_RAMPING
 				INC_POS
-				RENDER_16BIT_SMP_C4PINTRP
+				RENDER_16BIT_SMP_CINTRP
 				VOLUME_RAMPING
 				INC_POS
-				RENDER_16BIT_SMP_C4PINTRP
+				RENDER_16BIT_SMP_CINTRP
 				VOLUME_RAMPING
 				INC_POS
 			}
@@ -3980,7 +3820,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix16bRampBidiLoopC4PIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+static void mix16bRampBidiLoopCIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *revBase, *smpPtr;
 	int16_t *smpTapPtr;
@@ -4007,7 +3847,7 @@
 		{
 			for (i = 0; i < (samplesToMix & 3); i++)
 			{
-				RENDER_16BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_16BIT_SMP_CINTRP_TAP_FIX
 				VOLUME_RAMPING
 				INC_POS_BIDI
 			}
@@ -4014,16 +3854,16 @@
 			samplesToMix >>= 2;
 			for (i = 0; i < samplesToMix; i++)
 			{
-				RENDER_16BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_16BIT_SMP_CINTRP_TAP_FIX
 				VOLUME_RAMPING
 				INC_POS_BIDI
-				RENDER_16BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_16BIT_SMP_CINTRP_TAP_FIX
 				VOLUME_RAMPING
 				INC_POS_BIDI
-				RENDER_16BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_16BIT_SMP_CINTRP_TAP_FIX
 				VOLUME_RAMPING
 				INC_POS_BIDI
-				RENDER_16BIT_SMP_C4PINTRP_TAP_FIX
+				RENDER_16BIT_SMP_CINTRP_TAP_FIX
 				VOLUME_RAMPING
 				INC_POS_BIDI
 			}
@@ -4032,7 +3872,7 @@
 		{
 			for (i = 0; i < (samplesToMix & 3); i++)
 			{
-				RENDER_16BIT_SMP_C4PINTRP
+				RENDER_16BIT_SMP_CINTRP
 				VOLUME_RAMPING
 				INC_POS_BIDI
 			}
@@ -4039,16 +3879,16 @@
 			samplesToMix >>= 2;
 			for (i = 0; i < samplesToMix; i++)
 			{
-				RENDER_16BIT_SMP_C4PINTRP
+				RENDER_16BIT_SMP_CINTRP
 				VOLUME_RAMPING
 				INC_POS_BIDI
-				RENDER_16BIT_SMP_C4PINTRP
+				RENDER_16BIT_SMP_CINTRP
 				VOLUME_RAMPING
 				INC_POS_BIDI
-				RENDER_16BIT_SMP_C4PINTRP
+				RENDER_16BIT_SMP_CINTRP
 				VOLUME_RAMPING
 				INC_POS_BIDI
-				RENDER_16BIT_SMP_C4PINTRP
+				RENDER_16BIT_SMP_CINTRP
 				VOLUME_RAMPING
 				INC_POS_BIDI
 			}
@@ -4062,7 +3902,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix16bRampNoLoopC6PIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+static void mix16bRampNoLoopQIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
 	float fSample, *fMixBufferL, *fMixBufferR;
@@ -4084,7 +3924,7 @@
 
 		for (i = 0; i < (samplesToMix & 3); i++)
 		{
-			RENDER_16BIT_SMP_C6PINTRP
+			RENDER_16BIT_SMP_QINTRP
 			VOLUME_RAMPING
 			INC_POS
 		}
@@ -4091,16 +3931,16 @@
 		samplesToMix >>= 2;
 		for (i = 0; i < samplesToMix; i++)
 		{
-			RENDER_16BIT_SMP_C6PINTRP
+			RENDER_16BIT_SMP_QINTRP
 			VOLUME_RAMPING
 			INC_POS
-			RENDER_16BIT_SMP_C6PINTRP
+			RENDER_16BIT_SMP_QINTRP
 			VOLUME_RAMPING
 			INC_POS
-			RENDER_16BIT_SMP_C6PINTRP
+			RENDER_16BIT_SMP_QINTRP
 			VOLUME_RAMPING
 			INC_POS
-			RENDER_16BIT_SMP_C6PINTRP
+			RENDER_16BIT_SMP_QINTRP
 			VOLUME_RAMPING
 			INC_POS
 		}
@@ -4112,10 +3952,9 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix16bRampLoopC6PIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+static void mix16bRampLoopQIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
-	int16_t *smpTapPtr;
 	float fSample, *fMixBufferL, *fMixBufferR;
 	int32_t position;
 	float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR;
@@ -4125,7 +3964,6 @@
 	GET_VOL_RAMP
 	GET_MIXER_VARS_RAMP
 	SET_BASE16
-	PREPARE_TAP_FIX16
 
 	samplesLeft = numSamples;
 	while (samplesLeft > 0)
@@ -4134,55 +3972,27 @@
 		LIMIT_MIX_NUM_RAMP
 		samplesLeft -= samplesToMix;
 
-		if (v->hasLooped) // the negative interpolation taps need a special case after the sample has looped once
+		for (i = 0; i < (samplesToMix & 3); i++)
 		{
-			for (i = 0; i < (samplesToMix & 3); i++)
-			{
-				RENDER_16BIT_SMP_C6PINTRP_TAP_FIX
-				VOLUME_RAMPING
-				INC_POS
-			}
-			samplesToMix >>= 2;
-			for (i = 0; i < samplesToMix; i++)
-			{
-				RENDER_16BIT_SMP_C6PINTRP_TAP_FIX
-				VOLUME_RAMPING
-				INC_POS
-				RENDER_16BIT_SMP_C6PINTRP_TAP_FIX
-				VOLUME_RAMPING
-				INC_POS
-				RENDER_16BIT_SMP_C6PINTRP_TAP_FIX
-				VOLUME_RAMPING
-				INC_POS
-				RENDER_16BIT_SMP_C6PINTRP_TAP_FIX
-				VOLUME_RAMPING
-				INC_POS
-			}
+			RENDER_16BIT_SMP_QINTRP
+			VOLUME_RAMPING
+			INC_POS
 		}
-		else
+		samplesToMix >>= 2;
+		for (i = 0; i < samplesToMix; i++)
 		{
-			for (i = 0; i < (samplesToMix & 3); i++)
-			{
-				RENDER_16BIT_SMP_C6PINTRP
-				VOLUME_RAMPING
-				INC_POS
-			}
-			samplesToMix >>= 2;
-			for (i = 0; i < samplesToMix; i++)
-			{
-				RENDER_16BIT_SMP_C6PINTRP
-				VOLUME_RAMPING
-				INC_POS
-				RENDER_16BIT_SMP_C6PINTRP
-				VOLUME_RAMPING
-				INC_POS
-				RENDER_16BIT_SMP_C6PINTRP
-				VOLUME_RAMPING
-				INC_POS
-				RENDER_16BIT_SMP_C6PINTRP
-				VOLUME_RAMPING
-				INC_POS
-			}
+			RENDER_16BIT_SMP_QINTRP
+			VOLUME_RAMPING
+			INC_POS
+			RENDER_16BIT_SMP_QINTRP
+			VOLUME_RAMPING
+			INC_POS
+			RENDER_16BIT_SMP_QINTRP
+			VOLUME_RAMPING
+			INC_POS
+			RENDER_16BIT_SMP_QINTRP
+			VOLUME_RAMPING
+			INC_POS
 		}
 
 		WRAP_LOOP
@@ -4192,10 +4002,9 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix16bRampBidiLoopC6PIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+static void mix16bRampBidiLoopQIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *revBase, *smpPtr;
-	int16_t *smpTapPtr;
 	float fSample, *fMixBufferL, *fMixBufferR;
 	int32_t position;
 	float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR;
@@ -4205,7 +4014,6 @@
 	GET_VOL_RAMP
 	GET_MIXER_VARS_RAMP
 	SET_BASE16_BIDI
-	PREPARE_TAP_FIX16
 
 	samplesLeft = numSamples;
 	while (samplesLeft > 0)
@@ -4215,57 +4023,28 @@
 		samplesLeft -= samplesToMix;
 
 		START_BIDI
-		if (v->hasLooped) // the negative interpolation taps need a special case after the sample has looped once
+		for (i = 0; i < (samplesToMix & 3); i++)
 		{
-			for (i = 0; i < (samplesToMix & 3); i++)
-			{
-				RENDER_16BIT_SMP_C6PINTRP_TAP_FIX
-				VOLUME_RAMPING
-				INC_POS_BIDI
-			}
-			samplesToMix >>= 2;
-			for (i = 0; i < samplesToMix; i++)
-			{
-				RENDER_16BIT_SMP_C6PINTRP_TAP_FIX
-				VOLUME_RAMPING
-				INC_POS_BIDI
-				RENDER_16BIT_SMP_C6PINTRP_TAP_FIX
-				VOLUME_RAMPING
-				INC_POS_BIDI
-				RENDER_16BIT_SMP_C6PINTRP_TAP_FIX
-				VOLUME_RAMPING
-				INC_POS_BIDI
-				RENDER_16BIT_SMP_C6PINTRP_TAP_FIX
-				VOLUME_RAMPING
-				INC_POS_BIDI
-			}
+			RENDER_16BIT_SMP_QINTRP
+			VOLUME_RAMPING
+			INC_POS_BIDI
 		}
-		else
+		samplesToMix >>= 2;
+		for (i = 0; i < samplesToMix; i++)
 		{
-			for (i = 0; i < (samplesToMix & 3); i++)
-			{
-				RENDER_16BIT_SMP_C6PINTRP
-				VOLUME_RAMPING
-				INC_POS_BIDI
-			}
-			samplesToMix >>= 2;
-			for (i = 0; i < samplesToMix; i++)
-			{
-				RENDER_16BIT_SMP_C6PINTRP
-				VOLUME_RAMPING
-				INC_POS_BIDI
-				RENDER_16BIT_SMP_C6PINTRP
-				VOLUME_RAMPING
-				INC_POS_BIDI
-				RENDER_16BIT_SMP_C6PINTRP
-				VOLUME_RAMPING
-				INC_POS_BIDI
-				RENDER_16BIT_SMP_C6PINTRP
-				VOLUME_RAMPING
-				INC_POS_BIDI
-			}
+			RENDER_16BIT_SMP_QINTRP
+			VOLUME_RAMPING
+			INC_POS_BIDI
+			RENDER_16BIT_SMP_QINTRP
+			VOLUME_RAMPING
+			INC_POS_BIDI
+			RENDER_16BIT_SMP_QINTRP
+			VOLUME_RAMPING
+			INC_POS_BIDI
+			RENDER_16BIT_SMP_QINTRP
+			VOLUME_RAMPING
+			INC_POS_BIDI
 		}
-
 		END_BIDI
 
 		WRAP_BIDI_LOOP
@@ -4294,12 +4073,12 @@
 	(mixFunc)mix8bNoLoopS16Intrp,
 	(mixFunc)mix8bLoopS16Intrp,
 	(mixFunc)mix8bBidiLoopS16Intrp,
-	(mixFunc)mix8bNoLoopC4PIntrp,
-	(mixFunc)mix8bLoopC4PIntrp,
-	(mixFunc)mix8bBidiLoopC4PIntrp,
-	(mixFunc)mix8bNoLoopC6PIntrp,
-	(mixFunc)mix8bLoopC6PIntrp,
-	(mixFunc)mix8bBidiLoopC6PIntrp,
+	(mixFunc)mix8bNoLoopCIntrp,
+	(mixFunc)mix8bLoopCIntrp,
+	(mixFunc)mix8bBidiLoopCIntrp,
+	(mixFunc)mix8bNoLoopQIntrp,
+	(mixFunc)mix8bLoopQIntrp,
+	(mixFunc)mix8bBidiLoopQIntrp,
 
 	// 16-bit
 	(mixFunc)mix16bNoLoop,
@@ -4314,12 +4093,12 @@
 	(mixFunc)mix16bNoLoopS16Intrp,
 	(mixFunc)mix16bLoopS16Intrp,
 	(mixFunc)mix16bBidiLoopS16Intrp,
-	(mixFunc)mix16bNoLoopC4PIntrp,
-	(mixFunc)mix16bLoopC4PIntrp,
-	(mixFunc)mix16bBidiLoopC4PIntrp,
-	(mixFunc)mix16bNoLoopC6PIntrp,
-	(mixFunc)mix16bLoopC6PIntrp,
-	(mixFunc)mix16bBidiLoopC6PIntrp,
+	(mixFunc)mix16bNoLoopCIntrp,
+	(mixFunc)mix16bLoopCIntrp,
+	(mixFunc)mix16bBidiLoopCIntrp,
+	(mixFunc)mix16bNoLoopQIntrp,
+	(mixFunc)mix16bLoopQIntrp,
+	(mixFunc)mix16bBidiLoopQIntrp,
 
 	// volume ramping
 
@@ -4336,12 +4115,12 @@
 	(mixFunc)mix8bRampNoLoopS16Intrp,
 	(mixFunc)mix8bRampLoopS16Intrp,
 	(mixFunc)mix8bRampBidiLoopS16Intrp,
-	(mixFunc)mix8bRampNoLoopC4PIntrp,
-	(mixFunc)mix8bRampLoopC4PIntrp,
-	(mixFunc)mix8bRampBidiLoopC4PIntrp,
-	(mixFunc)mix8bRampNoLoopC6PIntrp,
-	(mixFunc)mix8bRampLoopC6PIntrp,
-	(mixFunc)mix8bRampBidiLoopC6PIntrp,
+	(mixFunc)mix8bRampNoLoopCIntrp,
+	(mixFunc)mix8bRampLoopCIntrp,
+	(mixFunc)mix8bRampBidiLoopCIntrp,
+	(mixFunc)mix8bRampNoLoopQIntrp,
+	(mixFunc)mix8bRampLoopQIntrp,
+	(mixFunc)mix8bRampBidiLoopQIntrp,
 
 	// 16-bit
 	(mixFunc)mix16bRampNoLoop,
@@ -4356,10 +4135,10 @@
 	(mixFunc)mix16bRampNoLoopS16Intrp,
 	(mixFunc)mix16bRampLoopS16Intrp,
 	(mixFunc)mix16bRampBidiLoopS16Intrp,
-	(mixFunc)mix16bRampNoLoopC4PIntrp,
-	(mixFunc)mix16bRampLoopC4PIntrp,
-	(mixFunc)mix16bRampBidiLoopC4PIntrp,
-	(mixFunc)mix16bRampNoLoopC6PIntrp,
-	(mixFunc)mix16bRampLoopC6PIntrp,
-	(mixFunc)mix16bRampBidiLoopC6PIntrp
+	(mixFunc)mix16bRampNoLoopCIntrp,
+	(mixFunc)mix16bRampLoopCIntrp,
+	(mixFunc)mix16bRampBidiLoopCIntrp,
+	(mixFunc)mix16bRampNoLoopQIntrp,
+	(mixFunc)mix16bRampLoopQIntrp,
+	(mixFunc)mix16bRampBidiLoopQIntrp
 };
--- a/src/mixer/ft2_mix.h
+++ b/src/mixer/ft2_mix.h
@@ -5,12 +5,12 @@
 enum
 {
 	// don't change the order of these! (yes, it looks weird)
-	INTERPOLATION_DISABLED = 0,
-	INTERPOLATION_SINC8    = 1,
-	INTERPOLATION_LINEAR   = 2,
-	INTERPOLATION_SINC16   = 3,
-	INTERPOLATION_CUBIC4   = 4,
-	INTERPOLATION_CUBIC6   = 5,
+	INTERPOLATION_DISABLED  = 0,
+	INTERPOLATION_SINC8     = 1,
+	INTERPOLATION_LINEAR    = 2,
+	INTERPOLATION_SINC16    = 3,
+	INTERPOLATION_CUBIC     = 4,
+	INTERPOLATION_QUADRATIC = 5,
 	// ------
 
 	NUM_INTERPOLATORS,
--- a/src/mixer/ft2_mix_macros.h
+++ b/src/mixer/ft2_mix_macros.h
@@ -1,6 +1,7 @@
 #pragma once
 
 #include "../ft2_audio.h"
+#include "ft2_quadratic_spline.h"
 #include "ft2_cubic_spline.h"
 #include "ft2_windowed_sinc.h"
 
@@ -79,6 +80,13 @@
 	fVolumeL += fVolumeLDelta; \
 	fVolumeR += fVolumeRDelta;
 
+/* It may look like we are potentially going out of bounds while looking up the sample points,
+** but the sample data is actually padded on both the left (negative) and right side, where correct tap
+** samples are stored according to loop mode (or no loop).
+**
+** There is also a second special case for the left edge (negative taps) after the sample has looped once.
+*/
+
 /* ----------------------------------------------------------------------- */
 /*                  NO INTERPOLATION (NEAREST NEIGHBOR)                    */
 /* ----------------------------------------------------------------------- */
@@ -93,14 +101,11 @@
 	*fMixBufferL++ += fSample * fVolumeL; \
 	*fMixBufferR++ += fSample * fVolumeR;
 
+
 /* ----------------------------------------------------------------------- */
 /*                          LINEAR INTERPOLATION                           */
 /* ----------------------------------------------------------------------- */
 
-/* It may look like we are potentially going out of bounds while looking up the sample points,
-** but the sample data has a fixed sample after the end (sampleEnd/loopEnd).
-*/
-
 #define LINEAR_INTERPOLATION(s, f, scale) \
 { \
 	const int32_t frac = (uint32_t)(f) >> 1; /* uint32 -> int32 range, faster int->float conv. (x86/x86_64) */ \
@@ -118,22 +123,41 @@
 	*fMixBufferL++ += fSample * fVolumeL; \
 	*fMixBufferR++ += fSample * fVolumeR;
 
+
 /* ----------------------------------------------------------------------- */
+/*                     QUADRATIC SPLINE INTERPOLATION                      */
+/* ----------------------------------------------------------------------- */
+
+// through LUT: mixer/ft2_quadratic_spline.c
+
+#define QUADRATIC_SPLINE_INTERPOLATION(s, f, scale) \
+{ \
+	const float *t = fQuadraticSplineLUT + (((uint32_t)(f) >> QUADRATIC_SPLINE_FRACSHIFT) * QUADRATIC_SPLINE_WIDTH); \
+	fSample = ((s[0] * t[0]) + \
+	           (s[1] * t[1]) + \
+	           (s[2] * t[2])) * (1.0f / scale); \
+}
+
+#define RENDER_8BIT_SMP_QINTRP \
+	QUADRATIC_SPLINE_INTERPOLATION(smpPtr, positionFrac, 128) \
+	*fMixBufferL++ += fSample * fVolumeL; \
+	*fMixBufferR++ += fSample * fVolumeR;
+
+#define RENDER_16BIT_SMP_QINTRP \
+	QUADRATIC_SPLINE_INTERPOLATION(smpPtr, positionFrac, 32768) \
+	*fMixBufferL++ += fSample * fVolumeL; \
+	*fMixBufferR++ += fSample * fVolumeR;
+
+
+/* ----------------------------------------------------------------------- */
 /*                       CUBIC SPLINE INTERPOLATION                        */
 /* ----------------------------------------------------------------------- */
 
 // through LUT: mixer/ft2_cubic_spline.c
 
-/* It may look like we are potentially going out of bounds while looking up the sample points,
-** but the sample data is actually padded on both the left (negative) and right side, where correct tap
-** samples are stored according to loop mode (or no loop).
-**
-** There is also a second special case for the left edge (negative taps) after the sample has looped once.
-*/
-
-#define CUBIC4P_SPLINE_INTERPOLATION(s, f, scale) \
+#define CUBIC_SPLINE_INTERPOLATION(s, f, scale) \
 { \
-	const float *t = f4PointCubicSplineLUT + (((uint32_t)(f) >> CUBIC4P_SPLINE_FRACSHIFT) & CUBIC4P_SPLINE_FRACMASK); \
+	const float *t = fCubicSplineLUT + (((uint32_t)(f) >> CUBIC_SPLINE_FRACSHIFT) & CUBIC_SPLINE_FRACMASK); \
 	fSample = ((s[-1] * t[0]) + \
 	           ( s[0] * t[1]) + \
 	           ( s[1] * t[2]) + \
@@ -140,66 +164,34 @@
 	           ( s[2] * t[3])) * (1.0f / scale); \
 }
 
-#define CUBIC6P_SPLINE_INTERPOLATION(s, f, scale) \
-{ \
-	const float *t = f6PointCubicSplineLUT + (((uint32_t)(f) >> CUBIC6P_SPLINE_FRACSHIFT) * CUBIC6P_SPLINE_WIDTH); \
-	fSample = ((s[-2] * t[0]) + \
-	           (s[-1] * t[1]) + \
-	           ( s[0] * t[2]) + \
-	           ( s[1] * t[3]) + \
-	           ( s[2] * t[4]) + \
-	           ( s[3] * t[5])) * (1.0f / scale); \
-}
-
-#define RENDER_8BIT_SMP_C4PINTRP \
-	CUBIC4P_SPLINE_INTERPOLATION(smpPtr, positionFrac, 128) \
+#define RENDER_8BIT_SMP_CINTRP \
+	CUBIC_SPLINE_INTERPOLATION(smpPtr, positionFrac, 128) \
 	*fMixBufferL++ += fSample * fVolumeL; \
 	*fMixBufferR++ += fSample * fVolumeR;
 
-#define RENDER_16BIT_SMP_C4PINTRP \
-	CUBIC4P_SPLINE_INTERPOLATION(smpPtr, positionFrac, 32768) \
+#define RENDER_16BIT_SMP_CINTRP \
+	CUBIC_SPLINE_INTERPOLATION(smpPtr, positionFrac, 32768) \
 	*fMixBufferL++ += fSample * fVolumeL; \
 	*fMixBufferR++ += fSample * fVolumeR;
 
-#define RENDER_8BIT_SMP_C6PINTRP \
-	CUBIC6P_SPLINE_INTERPOLATION(smpPtr, positionFrac, 128) \
-	*fMixBufferL++ += fSample * fVolumeL; \
-	*fMixBufferR++ += fSample * fVolumeR;
 
-#define RENDER_16BIT_SMP_C6PINTRP \
-	CUBIC6P_SPLINE_INTERPOLATION(smpPtr, positionFrac, 32768) \
-	*fMixBufferL++ += fSample * fVolumeL; \
-	*fMixBufferR++ += fSample * fVolumeR;
-
 /* Special left-edge case mixers to get proper tap data after one loop cycle.
 ** These are only used on looped samples.
 */
 
-#define RENDER_8BIT_SMP_C4PINTRP_TAP_FIX  \
+#define RENDER_8BIT_SMP_CINTRP_TAP_FIX  \
 	smpTapPtr = (smpPtr <= leftEdgePtr) ? (int8_t *)&v->leftEdgeTaps8[(int32_t)(smpPtr-loopStartPtr)] : (int8_t *)smpPtr; \
-	CUBIC4P_SPLINE_INTERPOLATION(smpTapPtr, positionFrac, 128) \
+	CUBIC_SPLINE_INTERPOLATION(smpTapPtr, positionFrac, 128) \
 	*fMixBufferL++ += fSample * fVolumeL; \
 	*fMixBufferR++ += fSample * fVolumeR;
 
-#define RENDER_16BIT_SMP_C4PINTRP_TAP_FIX \
+#define RENDER_16BIT_SMP_CINTRP_TAP_FIX \
 	smpTapPtr = (smpPtr <= leftEdgePtr) ? (int16_t *)&v->leftEdgeTaps16[(int32_t)(smpPtr-loopStartPtr)] : (int16_t *)smpPtr; \
-	CUBIC4P_SPLINE_INTERPOLATION(smpTapPtr, positionFrac, 32768) \
+	CUBIC_SPLINE_INTERPOLATION(smpTapPtr, positionFrac, 32768) \
 	*fMixBufferL++ += fSample * fVolumeL; \
 	*fMixBufferR++ += fSample * fVolumeR;
 
-#define RENDER_8BIT_SMP_C6PINTRP_TAP_FIX  \
-	smpTapPtr = (smpPtr <= leftEdgePtr) ? (int8_t *)&v->leftEdgeTaps8[(int32_t)(smpPtr-loopStartPtr)] : (int8_t *)smpPtr; \
-	CUBIC6P_SPLINE_INTERPOLATION(smpTapPtr, positionFrac, 128) \
-	*fMixBufferL++ += fSample * fVolumeL; \
-	*fMixBufferR++ += fSample * fVolumeR;
 
-#define RENDER_16BIT_SMP_C6PINTRP_TAP_FIX \
-	smpTapPtr = (smpPtr <= leftEdgePtr) ? (int16_t *)&v->leftEdgeTaps16[(int32_t)(smpPtr-loopStartPtr)] : (int16_t *)smpPtr; \
-	CUBIC6P_SPLINE_INTERPOLATION(smpTapPtr, positionFrac, 32768) \
-	*fMixBufferL++ += fSample * fVolumeL; \
-	*fMixBufferR++ += fSample * fVolumeR;
-
-
 /* ----------------------------------------------------------------------- */
 /*                       WINDOWED-SINC INTERPOLATION                       */
 /* ----------------------------------------------------------------------- */
@@ -206,13 +198,6 @@
 
 // through LUTs: mixer/ft2_windowed_sinc.c
 
-/* It may look like we are potentially going out of bounds while looking up the sample points,
-** but the sample data is actually padded on both the left (negative) and right side, where correct tap
-** samples are stored according to loop mode (or no loop).
-**
-** There is also a second special case for the left edge (negative taps) after the sample has looped once.
-*/
-
 #define WINDOWED_SINC8_INTERPOLATION(s, f, scale) \
 { \
 	const float *t = v->fSincLUT + (((uint32_t)(f) >> SINC1_FRACSHIFT) & SINC1_FRACMASK); \
@@ -294,6 +279,7 @@
 	WINDOWED_SINC16_INTERPOLATION(smpTapPtr, positionFrac, 32768) \
 	*fMixBufferL++ += fSample * fVolumeL; \
 	*fMixBufferR++ += fSample * fVolumeR;
+
 
 /* ----------------------------------------------------------------------- */
 /*                      SAMPLES-TO-MIX LIMITING MACROS                     */
--- /dev/null
+++ b/src/mixer/ft2_quadratic_spline.c
@@ -1,0 +1,45 @@
+// 3-point quadratic spline interpolation LUT generator
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include "ft2_quadratic_spline.h"
+#include "../ft2_video.h" // showErrorMsgBox()
+
+float *fQuadraticSplineLUT = NULL; // globalized
+
+bool setupQuadraticSplineTable(void)
+{
+	fQuadraticSplineLUT = (float *)malloc(QUADRATIC_SPLINE_WIDTH * QUADRATIC_SPLINE_PHASES * sizeof (float));
+	if (fQuadraticSplineLUT == NULL)
+	{
+		showErrorMsgBox("Not enough memory!");
+		return false;
+	}
+
+	float *fPtr = fQuadraticSplineLUT;
+	for (int32_t i = 0; i < QUADRATIC_SPLINE_PHASES; i++)
+	{
+		const double x1 = i * (1.0 / QUADRATIC_SPLINE_PHASES);
+		const double x2 = x1 * x1; // x^2
+
+		double t1 = ( 0.5 * x2) + (-1.5 * x1) + 1.0;
+		double t2 = (-1.0 * x2) + ( 2.0 * x1);
+		double t3 = ( 0.5 * x2) + (-0.5 * x1);
+
+		*fPtr++ = (float)t1;
+		*fPtr++ = (float)t2;
+		*fPtr++ = (float)t3;
+	}
+
+	return true;
+}
+
+void freeQuadraticSplineTable(void)
+{
+	if (fQuadraticSplineLUT != NULL)
+	{
+		free(fQuadraticSplineLUT);
+		fQuadraticSplineLUT = NULL;
+	}
+}
--- /dev/null
+++ b/src/mixer/ft2_quadratic_spline.h
@@ -1,0 +1,15 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "ft2_mix.h" // MIXER_FRAC_BITS
+
+#define QUADRATIC_SPLINE_WIDTH 3
+#define QUADRATIC_SPLINE_PHASES 8192
+#define QUADRATIC_SPLINE_PHASES_BITS 13 // log2(QUADRATIC_SPLINE_PHASES)
+#define QUADRATIC_SPLINE_FRACSHIFT (MIXER_FRAC_BITS-QUADRATIC_SPLINE_PHASES_BITS)
+
+extern float *fQuadraticSplineLUT;
+
+bool setupQuadraticSplineTable(void);
+void freeQuadraticSplineTable(void);
--- a/src/scopes/ft2_scope_macros.h
+++ b/src/scopes/ft2_scope_macros.h
@@ -60,24 +60,20 @@
 } \
 
 #define CUBIC_SMP8(frac) \
-	const int16_t *t = scopeIntrpLUT + (((frac) >> (SCOPE_FRAC_BITS-SCOPE_INTRP_PHASES_BITS)) * SCOPE_INTRP_WIDTH); \
+	const int16_t *t = scopeIntrpLUT + (((frac) >> (SCOPE_FRAC_BITS-SCOPE_INTRP_PHASES_BITS)) << SCOPE_INTRP_WIDTH_BITS); \
 	\
-	sample = ((s8[-2] * t[0]) + \
-	          (s8[-1] * t[1]) + \
-	          ( s8[0] * t[2]) + \
-	          ( s8[1] * t[3]) + \
-	          ( s8[2] * t[4]) + \
-	          ( s8[3] * t[5])) >> (SCOPE_INTRP_SCALE_BITS-8);
+	sample = ((s8[-1] * t[0]) + \
+	          ( s8[0] * t[1]) + \
+	          ( s8[1] * t[2]) + \
+	          ( s8[2] * t[3])) >> (SCOPE_INTRP_SCALE_BITS-8);
 
 #define CUBIC_SMP16(frac) \
-	const int16_t *t = scopeIntrpLUT + (((frac) >> (SCOPE_FRAC_BITS-SCOPE_INTRP_PHASES_BITS)) * SCOPE_INTRP_WIDTH); \
+	const int16_t *t = scopeIntrpLUT + (((frac) >> (SCOPE_FRAC_BITS-SCOPE_INTRP_PHASES_BITS)) << SCOPE_INTRP_WIDTH_BITS); \
 	\
-	sample = ((s16[-2] * t[0]) + \
-	          (s16[-1] * t[1]) + \
-	          ( s16[0] * t[2]) + \
-	          ( s16[1] * t[3]) + \
-	          ( s16[2] * t[4]) + \
-	          ( s16[3] * t[5])) >> SCOPE_INTRP_SCALE_BITS;
+	sample = ((s16[-1] * t[0]) + \
+	          ( s16[0] * t[1]) + \
+	          ( s16[1] * t[2]) + \
+	          ( s16[2] * t[3])) >> SCOPE_INTRP_SCALE_BITS;
 
 #define CUBIC_INTERPOLATION8(frac) \
 { \
@@ -113,7 +109,8 @@
 		LINEAR_INTERPOLATION8(frac) \
 	else \
 		CUBIC_INTERPOLATION8(frac) \
-	sample = (sample * s->volume) >> (16+2);
+	sample = (int32_t)roundf((float)sample * s->fVolume); \
+	if (sample > (SCOPE_HEIGHT/2)-1) sample = (SCOPE_HEIGHT/2)-1; /* upper-clamp needed */
 
 #define INTERPOLATE_SMP16(pos, frac) \
 	const int16_t *s16 = s->base16 + pos; \
@@ -123,7 +120,8 @@
 		LINEAR_INTERPOLATION16(frac) \
 	else \
 		CUBIC_INTERPOLATION16(frac) \
-	sample = (sample * s->volume) >> (16+2);
+	sample = (int32_t)roundf((float)sample * s->fVolume); \
+	if (sample > (SCOPE_HEIGHT/2)-1) sample = (SCOPE_HEIGHT/2)-1; /* upper-clamp needed */
 
 #define INTERPOLATE_SMP8_LOOP(pos, frac) \
 	const int8_t *s8 = s->base8 + pos; \
@@ -133,7 +131,8 @@
 		LINEAR_INTERPOLATION8(frac) \
 	else \
 		CUBIC_INTERPOLATION8_LOOP(pos, frac) \
-	sample = (sample * s->volume) >> (16+2);
+	sample = (int32_t)roundf((float)sample * s->fVolume); \
+	if (sample > (SCOPE_HEIGHT/2)-1) sample = (SCOPE_HEIGHT/2)-1; /* upper-clamp needed */
 
 #define INTERPOLATE_SMP16_LOOP(pos, frac) \
 	const int16_t *s16 = s->base16 + pos; \
@@ -143,25 +142,37 @@
 		LINEAR_INTERPOLATION16(frac) \
 	else \
 		CUBIC_INTERPOLATION16_LOOP(pos, frac) \
-	sample = (sample * s->volume) >> (16+2);
+	sample = (int32_t)roundf((float)sample * s->fVolume); \
+	if (sample > (SCOPE_HEIGHT/2)-1) sample = (SCOPE_HEIGHT/2)-1; /* upper-clamp needed */
 
 #define SCOPE_GET_SMP8 \
 	if (s->active) \
-		sample = (s->base8[position] * s->volume) >> (8+2); \
+	{ \
+		sample = (int32_t)roundf((float)(s->base8[position] << 8) * s->fVolume); \
+		if (sample > (SCOPE_HEIGHT/2)-1) sample = (SCOPE_HEIGHT/2)-1; /* upper-clamp needed */ \
+	} \
 	else \
-		sample = 0;
+	{ \
+		sample = 0; \
+	}
 
 #define SCOPE_GET_SMP16 \
 	if (s->active) \
-		sample = (s->base16[position] * s->volume) >> (16+2); \
+	{ \
+		sample = (int32_t)roundf((float)s->base16[position] * s->fVolume); \
+		if (sample > (SCOPE_HEIGHT/2)-1) sample = (SCOPE_HEIGHT/2)-1; /* upper-clamp needed */ \
+	} \
 	else \
-		sample = 0;
+	{ \
+		sample = 0; \
+	}
 
 #define SCOPE_GET_SMP8_BIDI \
 	if (s->active) \
 	{ \
 		GET_BIDI_POSITION \
-		sample = (s->base8[actualPos] * s->volume) >> (8+2); \
+		sample = (int32_t)roundf((float)(s->base8[actualPos] << 8) * s->fVolume); \
+		if (sample > (SCOPE_HEIGHT/2)-1) sample = (SCOPE_HEIGHT/2)-1; /* upper-clamp needed */ \
 	} \
 	else \
 	{ \
@@ -172,7 +183,8 @@
 	if (s->active) \
 	{ \
 		GET_BIDI_POSITION \
-		sample = (s->base16[actualPos] * s->volume) >> (16+2); \
+		sample = (int32_t)roundf((float)s->base16[actualPos] * s->fVolume); \
+		if (sample > (SCOPE_HEIGHT/2)-1) sample = (SCOPE_HEIGHT/2)-1; /* upper-clamp needed */ \
 	} \
 	else \
 	{ \
--- a/src/scopes/ft2_scopedraw.c
+++ b/src/scopes/ft2_scopedraw.c
@@ -22,11 +22,11 @@
 
 	/* Several tests have been done to figure out what interpolation method is most suitable
 	** for the tracker scopes. After testing linear, cubic, Gaussian and windowed-sinc
-	** interpolation, I have come to the conclusion that 6-point cubic B-spline is the best.
+	** interpolation, I have come to the conclusion that 4-point cubic B-spline is the best.
 	** This interpolation method also has no overshoot.
 	*/
 
-	// 6-point cubic B-spline (no overshoot)
+	// 4-point cubic B-spline (no overshoot)
 
 	int16_t *ptr16 = scopeIntrpLUT;
 	for (int32_t i = 0; i < SCOPE_INTRP_PHASES; i++)
@@ -34,23 +34,17 @@
 		const double x1 = i * (1.0 / SCOPE_INTRP_PHASES);
 		const double x2 = x1 * x1; // x^2
 		const double x3 = x2 * x1; // x^3
-		const double x4 = x3 * x1; // x^4
-		const double x5 = x4 * x1; // x^5
 
-		double t1 = (-(1.0/120.0) * x5) + ( (1.0/24.0) * x4) + (-(1.0/12.0) * x3) + ( (1.0/12.0) * x2) + (-(1.0/24.0) * x1) + ( 1.0/120.0);
-		double t2 = ( (1.0/ 24.0) * x5) + (-(1.0/ 6.0) * x4) + ( (1.0/ 6.0) * x3) + ( (1.0/ 6.0) * x2) + (-(5.0/12.0) * x1) + (13.0/ 60.0);
-		double t3 = (-(1.0/ 12.0) * x5) + ( (1.0/ 4.0) * x4) +                      (-(1.0/ 2.0) * x2)                      + (11.0/ 20.0);
-		double t4 = ( (1.0/ 12.0) * x5) + (-(1.0/ 6.0) * x4) + (-(1.0/ 6.0) * x3) + ( (1.0/ 6.0) * x2) + ( (5.0/12.0) * x1) + (13.0/ 60.0);
-		double t5 = (-(1.0/ 24.0) * x5) + ( (1.0/24.0) * x4) + ( (1.0/12.0) * x3) + ( (1.0/12.0) * x2) + ( (1.0/24.0) * x1) + ( 1.0/120.0);
-		double t6 =   (1.0/120.0) * x5;
+		double t1 = (-(1.0/6.0) * x3) + ( (1.0/2.0) * x2) + (-(1.0/2.0) * x1) + (1.0/6.0);
+		double t2 = ( (1.0/2.0) * x3) + (     -1.0  * x2)                     + (2.0/3.0);
+		double t3 = (-(1.0/2.0) * x3) + ( (1.0/2.0) * x2) + ( (1.0/2.0) * x1) + (1.0/6.0);
+		double t4 =   (1.0/6.0) * x3;
 
-		// important: truncate, do not round (would cause scope overflow)
-		*ptr16++ = (int16_t)(t1 * SCOPE_INTRP_SCALE);
-		*ptr16++ = (int16_t)(t2 * SCOPE_INTRP_SCALE);
-		*ptr16++ = (int16_t)(t3 * SCOPE_INTRP_SCALE);
-		*ptr16++ = (int16_t)(t4 * SCOPE_INTRP_SCALE);
-		*ptr16++ = (int16_t)(t5 * SCOPE_INTRP_SCALE);
-		*ptr16++ = (int16_t)(t6 * SCOPE_INTRP_SCALE);
+		// rounding here would make the scopes clip, but we clamp the scopes for another reason anyway
+		*ptr16++ = (int16_t)round(t1 * SCOPE_INTRP_SCALE);
+		*ptr16++ = (int16_t)round(t2 * SCOPE_INTRP_SCALE);
+		*ptr16++ = (int16_t)round(t3 * SCOPE_INTRP_SCALE);
+		*ptr16++ = (int16_t)round(t4 * SCOPE_INTRP_SCALE);
 	}
 
 	return true;
--- a/src/scopes/ft2_scopes.c
+++ b/src/scopes/ft2_scopes.c
@@ -428,7 +428,7 @@
 		}
 
 		volatile scope_t s = scope[i]; // cache scope to lower thread race condition issues
-		if (s.active && s.volume > 0 && !audio.locked)
+		if (s.active && s.fVolume > 0.0f && !audio.locked)
 		{
 			// scope is active
 			scope[i].wasCleared = false;
@@ -489,7 +489,7 @@
 		const uint8_t status = scopeUpdateStatus[i];
 
 		if (status & IS_Vol)
-			sc->volume = ch->scopeVolume;
+			sc->fVolume = ch->scopeVolume * (1.0f / (255.0f / (SCOPE_HEIGHT/2) * 32768.0f));
 
 		if (status & IS_Period)
 			sc->delta = (uint64_t)(dPeriod2Hz(ch->period) * (SCOPE_FRAC_SCALE / (double)SCOPE_HZ));
--- a/src/scopes/ft2_scopes.h
+++ b/src/scopes/ft2_scopes.h
@@ -17,7 +17,8 @@
 #define SCOPE_FRAC_SCALE ((int64_t)1 << SCOPE_FRAC_BITS)
 #define SCOPE_FRAC_MASK (SCOPE_FRAC_SCALE-1)
 
-#define SCOPE_INTRP_WIDTH 6
+#define SCOPE_INTRP_WIDTH 4
+#define SCOPE_INTRP_WIDTH_BITS 2 /* log2(SCOPE_INTRP_WIDTH) */
 #define SCOPE_INTRP_SCALE 32768
 #define SCOPE_INTRP_SCALE_BITS 15 /* log2(SCOPE_INTRP_SCALE) */
 #define SCOPE_INTRP_PHASES 512 /* plentiful for FT2-styled scopes */
@@ -39,8 +40,9 @@
 	const int16_t *base16;
 	bool wasCleared, sample16Bit, samplingBackwards, hasLooped;
 	uint8_t loopType;
-	int32_t volume, loopStart, loopLength, loopEnd, sampleEnd, position;
+	int32_t loopStart, loopLength, loopEnd, sampleEnd, position;
 	uint64_t delta, drawDelta, positionFrac;
+	float fVolume;
 
 	// if (loopEnabled && hasLooped && samplingPos <= loopStart+MAX_LEFT_TAPS) readFixedTapsFromThisPointer();
 	const int8_t *leftEdgeTaps8;
--- a/vs2019_project/ft2-clone/ft2-clone.vcxproj
+++ b/vs2019_project/ft2-clone/ft2-clone.vcxproj
@@ -357,6 +357,7 @@
     <ClCompile Include="..\..\src\libflac\window.c" />
     <ClCompile Include="..\..\src\libflac\windows_unicode_filenames.c" />
     <ClCompile Include="..\..\src\mixer\ft2_cubic_spline.c" />
+    <ClCompile Include="..\..\src\mixer\ft2_quadratic_spline.c" />
     <ClCompile Include="..\..\src\mixer\ft2_windowed_sinc.c" />
     <ClCompile Include="..\..\src\mixer\ft2_mix.c" />
     <ClCompile Include="..\..\src\mixer\ft2_silence_mix.c" />
@@ -433,6 +434,7 @@
     <ClInclude Include="..\..\src\ft2_video.h" />
     <ClInclude Include="..\..\src\ft2_wav_renderer.h" />
     <ClInclude Include="..\..\src\mixer\ft2_cubic_spline.h" />
+    <ClInclude Include="..\..\src\mixer\ft2_quadratic_spline.h" />
     <ClInclude Include="..\..\src\mixer\ft2_windowed_sinc.h" />
     <ClInclude Include="..\..\src\mixer\ft2_mix.h" />
     <ClInclude Include="..\..\src\mixer\ft2_mix_macros.h" />
--- a/vs2019_project/ft2-clone/ft2-clone.vcxproj.filters
+++ b/vs2019_project/ft2-clone/ft2-clone.vcxproj.filters
@@ -173,6 +173,9 @@
     <ClCompile Include="..\..\src\modloaders\ft2_load_it.c">
       <Filter>modloaders</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\src\mixer\ft2_quadratic_spline.c">
+      <Filter>mixer</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\src\rtmidi\RtMidi.h">
@@ -330,6 +333,9 @@
     </ClInclude>
     <ClInclude Include="..\..\src\ft2_unicode.h">
       <Filter>headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\mixer\ft2_quadratic_spline.h">
+      <Filter>mixer</Filter>
     </ClInclude>
   </ItemGroup>
   <ItemGroup>
--