shithub: ft²

Download patch

ref: 57138066d9b4374cd9ec5199a6ed488593eeecb7
parent: 23431a69a95d197ec9b2a4ab6be647c0f23d9966
author: Olav Sørensen <olav.sorensen@live.no>
date: Sun Jul 13 12:46:26 EDT 2025

Tracker scope improvements

--- a/src/ft2_audio.c
+++ b/src/ft2_audio.c
@@ -368,7 +368,7 @@
 		if (status & IS_Vol)
 		{
 			v->fVolume = ch->fFinalVol; // 0.0f .. 1.0f
-			v->scopeVolume = (uint8_t)((ch->fFinalVol * (SCOPE_HEIGHT*4.0f)) + 0.5f);
+			v->scopeVolume = (uint8_t)(v->fVolume * 255.0f);
 		}
 
 		if (status & IS_Pan)
--- a/src/ft2_header.h
+++ b/src/ft2_header.h
@@ -12,7 +12,7 @@
 #endif
 #include "ft2_replayer.h"
 
-#define PROG_VER_STR "1.96"
+#define PROG_VER_STR "1.97"
 
 // do NOT change these! It will only mess things up...
 
--- a/src/mixer/ft2_mix.c
+++ b/src/mixer/ft2_mix.c
@@ -947,7 +947,6 @@
 	SET_BACK_MIXER_POS
 }
 
-
 static void mix8bRampNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
--- a/src/scopes/ft2_scope_macros.h
+++ b/src/scopes/ft2_scope_macros.h
@@ -13,7 +13,7 @@
 	uint32_t width = x + w; \
 	int32_t sample; \
 	int32_t position = s->position; \
-	uint64_t positionFrac = 0;
+	uint64_t positionFrac = s->positionFrac;
 
 #define SCOPE_INIT_BIDI \
 	const uint32_t color = video.palette[PAL_PATTEXT]; \
@@ -20,16 +20,18 @@
 	uint32_t width = x + w; \
 	int32_t sample; \
 	int32_t actualPos, position = s->position; \
-	uint64_t positionFrac = 0; \
+	uint64_t positionFrac = s->positionFrac; \
 	bool samplingBackwards = s->samplingBackwards;
 
 #define LINED_SCOPE_INIT \
 	SCOPE_INIT \
+	float fSample; \
 	int32_t smpY1, smpY2; \
 	width--;
 
 #define LINED_SCOPE_INIT_BIDI \
 	SCOPE_INIT_BIDI \
+	float fSample; \
 	int32_t smpY1, smpY2; \
 	width--;
 
@@ -39,41 +41,41 @@
 
 #define NEAREST_NEIGHGBOR8 \
 { \
-	sample = s8[0] << 8; \
+	fSample = s8[0]; \
 } \
 
 #define LINEAR_INTERPOLATION8(frac) \
 { \
-	const int32_t f = (frac) >> (SCOPE_FRAC_BITS-15); \
-	sample = (s8[0] << 8) + ((((s8[1] - s8[0]) << 8) * f) >> 15); \
+	const float f = (frac) * (1.0f / SCOPE_FRAC_SCALE); \
+	fSample = s8[0] + ((s8[1] - s8[0]) * f); \
 } \
 
 #define NEAREST_NEIGHGBOR16 \
 { \
-	sample = s16[0]; \
+	fSample = s16[0]; \
 } \
 
 #define LINEAR_INTERPOLATION16(frac) \
 { \
-	const int32_t f = (frac) >> (SCOPE_FRAC_BITS-15); \
-	sample = s16[0] + (((s16[1] - s16[0]) * f) >> 15); \
+	const float f = (frac) * (1.0f / SCOPE_FRAC_SCALE); \
+	fSample = s16[0] + ((s16[1] - s16[0]) * f); \
 } \
 
 #define CUBIC_SMP8(frac) \
-	const int16_t *t = scopeIntrpLUT + (((frac) >> (SCOPE_FRAC_BITS-SCOPE_INTRP_PHASES_BITS)) << SCOPE_INTRP_WIDTH_BITS); \
+	const float *t = fScopeIntrpLUT + (((frac) >> (SCOPE_FRAC_BITS-SCOPE_INTRP_PHASES_BITS)) << SCOPE_INTRP_WIDTH_BITS); \
 	\
-	sample = ((s8[-1] * t[0]) + \
+	fSample = (s8[-1] * t[0]) + \
 	          ( s8[0] * t[1]) + \
 	          ( s8[1] * t[2]) + \
-	          ( s8[2] * t[3])) >> (SCOPE_INTRP_SCALE_BITS-8);
+	          ( s8[2] * t[3]);
 
 #define CUBIC_SMP16(frac) \
-	const int16_t *t = scopeIntrpLUT + (((frac) >> (SCOPE_FRAC_BITS-SCOPE_INTRP_PHASES_BITS)) << SCOPE_INTRP_WIDTH_BITS); \
+	const float *t = fScopeIntrpLUT + (((frac) >> (SCOPE_FRAC_BITS-SCOPE_INTRP_PHASES_BITS)) << SCOPE_INTRP_WIDTH_BITS); \
 	\
-	sample = ((s16[-1] * t[0]) + \
+	fSample = (s16[-1] * t[0]) + \
 	          ( s16[0] * t[1]) + \
 	          ( s16[1] * t[2]) + \
-	          ( s16[2] * t[3])) >> SCOPE_INTRP_SCALE_BITS;
+	          ( s16[2] * t[3]);
 
 #define CUBIC_INTERPOLATION8(frac) \
 { \
@@ -109,7 +111,7 @@
 		LINEAR_INTERPOLATION8(frac) \
 	else \
 		CUBIC_INTERPOLATION8(frac) \
-	sample = (sample * s->volume) >> (16+2);
+	sample = (int32_t)((fSample * s->fVolume8) - 0.5f);
 
 #define INTERPOLATE_SMP16(pos, frac) \
 	const int16_t *s16 = s->base16 + pos; \
@@ -119,7 +121,7 @@
 		LINEAR_INTERPOLATION16(frac) \
 	else \
 		CUBIC_INTERPOLATION16(frac) \
-	sample = (sample * s->volume) >> (16+2);
+	sample = (int32_t)((fSample * s->fVolume16) - 0.5f);
 
 #define INTERPOLATE_SMP8_LOOP(pos, frac) \
 	const int8_t *s8 = s->base8 + pos; \
@@ -129,7 +131,7 @@
 		LINEAR_INTERPOLATION8(frac) \
 	else \
 		CUBIC_INTERPOLATION8_LOOP(pos, frac) \
-	sample = (sample * s->volume) >> (16+2);
+	sample = (int32_t)((fSample * s->fVolume8) - 0.5f);
 
 #define INTERPOLATE_SMP16_LOOP(pos, frac) \
 	const int16_t *s16 = s->base16 + pos; \
@@ -139,17 +141,17 @@
 		LINEAR_INTERPOLATION16(frac) \
 	else \
 		CUBIC_INTERPOLATION16_LOOP(pos, frac) \
-	sample = (sample * s->volume) >> (16+2);
+	sample = (int32_t)((fSample * s->fVolume16) - 0.5f);
 
 #define SCOPE_GET_SMP8 \
 	if (s->active) \
-		sample = (s->base8[position] * s->volume) >> (8+2); \
+		sample = (int32_t)((s->base8[position] * s->fVolume8) - 0.5f); \
 	else \
 		sample = 0;
 
 #define SCOPE_GET_SMP16 \
 	if (s->active) \
-		sample = (s->base16[position] * s->volume) >> (16+2); \
+		sample = (int32_t)((s->base16[position] * s->fVolume16) - 0.5f); \
 	else \
 		sample = 0;
 
@@ -157,7 +159,7 @@
 	if (s->active) \
 	{ \
 		GET_BIDI_POSITION \
-		sample = (s->base8[actualPos] * s->volume) >> (8+2); \
+		sample = (int32_t)((s->base8[actualPos] * s->fVolume8) - 0.5f); \
 	} \
 	else \
 	{ \
@@ -168,7 +170,7 @@
 	if (s->active) \
 	{ \
 		GET_BIDI_POSITION \
-		sample = (s->base16[actualPos] * s->volume) >> (16+2); \
+		sample = (int32_t)((s->base16[actualPos] * s->fVolume16) - 0.5f); \
 	} \
 	else \
 	{ \
--- a/src/scopes/ft2_scopedraw.c
+++ b/src/scopes/ft2_scopedraw.c
@@ -10,14 +10,14 @@
 #include "ft2_scopedraw.h"
 #include "ft2_scope_macros.h"
 
-static int16_t *scopeIntrpLUT;
+static float *fScopeIntrpLUT;
 
 static void scopeLine(int32_t x1, int32_t y1, int32_t y2, const uint32_t color);
 
 bool calcScopeIntrpLUT(void)
 {
-	scopeIntrpLUT = (int16_t *)malloc(SCOPE_INTRP_WIDTH * SCOPE_INTRP_PHASES * sizeof (int16_t));
-	if (scopeIntrpLUT == NULL)
+	fScopeIntrpLUT = (float *)malloc(SCOPE_INTRP_WIDTH * SCOPE_INTRP_PHASES * sizeof (float));
+	if (fScopeIntrpLUT == NULL)
 		return false;
 
 	/* Several tests have been done to figure out what interpolation method is most suitable
@@ -27,8 +27,7 @@
 	*/
 
 	// 4-point cubic B-spline (no overshoot)
-
-	int16_t *ptr16 = scopeIntrpLUT;
+	float *fPtr = fScopeIntrpLUT;
 	for (int32_t i = 0; i < SCOPE_INTRP_PHASES; i++)
 	{
 		const double x1 = i * (1.0 / SCOPE_INTRP_PHASES);
@@ -35,16 +34,15 @@
 		const double x2 = x1 * x1; // x^2
 		const double x3 = x2 * x1; // x^3
 
-		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;
+		const double t1 = (x1 * -(1.0/2.0)) + (x2 * (1.0/2.0)) + (x3 * -(1.0/6.0)) + (1.0/6.0);
+		const double t2 =                     (x2 *     -1.0 ) + (x3 *  (1.0/2.0)) + (2.0/3.0);
+		const double t3 = (x1 *  (1.0/2.0)) + (x2 * (1.0/2.0)) + (x3 * -(1.0/2.0)) + (1.0/6.0);
+		const double t4 =                                         x3 *  (1.0/6.0);
 
-		// truncate, do not round!
-		*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);
+		*fPtr++ = (float)t1;
+		*fPtr++ = (float)t2;
+		*fPtr++ = (float)t3;
+		*fPtr++ = (float)t4;
 	}
 
 	return true;
@@ -52,10 +50,10 @@
 
 void freeScopeIntrpLUT(void)
 {
-	if (scopeIntrpLUT != NULL)
+	if (fScopeIntrpLUT != NULL)
 	{
-		free(scopeIntrpLUT);
-		scopeIntrpLUT = NULL;
+		free(fScopeIntrpLUT);
+		fScopeIntrpLUT = NULL;
 	}
 }
 
--- a/src/scopes/ft2_scopes.c
+++ b/src/scopes/ft2_scopes.c
@@ -428,14 +428,11 @@
 		}
 
 		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.fVolume16 > 0.0f && !audio.locked)
 		{
 			// scope is active
 			scope[i].wasCleared = false;
 
-			// get relative voice Hz (in relation to C4/2 rate)
-			s.drawDelta = (uint64_t)(scope[i].delta * ((double)SCOPE_HZ / ((double)C4_FREQ / 2.0)));
-
 			// clear scope background
 			clearRect(scopeXOffs, scopeYOffs, scopeDrawLen, SCOPE_HEIGHT);
 
@@ -489,10 +486,18 @@
 		const uint8_t status = scopeUpdateStatus[i];
 
 		if (status & IS_Vol)
-			sc->volume = ch->scopeVolume;
+		{
+			sc->fVolume8  = ch->scopeVolume * (((SCOPE_HEIGHT/2.0f) / 255.0f) /   128.0f);
+			sc->fVolume16 = ch->scopeVolume * (((SCOPE_HEIGHT/2.0f) / 255.0f) / 32768.0f);
+		}
 
 		if (status & IS_Period)
-			sc->delta = (uint64_t)(dPeriod2Hz(ch->period) * (SCOPE_FRAC_SCALE / (double)SCOPE_HZ));
+		{
+			const double dHz = dPeriod2Hz(ch->period);
+
+			sc->delta = (uint64_t)(dHz * (SCOPE_FRAC_SCALE / (double)SCOPE_HZ));
+			sc->drawDelta = (uint64_t)(dHz * (SCOPE_FRAC_SCALE / ((double)C4_FREQ/2.0)));
+		}
 
 		if (status & IS_Trigger)
 		{
--- a/src/scopes/ft2_scopes.h
+++ b/src/scopes/ft2_scopes.h
@@ -19,10 +19,8 @@
 
 #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 256 /* enough for the scopes */
-#define SCOPE_INTRP_PHASES_BITS 8 /* log2(SCOPE_INTRP_PHASES) */
+#define SCOPE_INTRP_PHASES 512 /* enough for the scopes */
+#define SCOPE_INTRP_PHASES_BITS 9 /* log2(SCOPE_INTRP_PHASES) */
 
 int32_t getSamplePositionFromScopes(uint8_t ch);
 void stopAllScopes(void);
@@ -40,9 +38,9 @@
 	const int16_t *base16;
 	bool wasCleared, sample16Bit, samplingBackwards, hasLooped;
 	uint8_t loopType;
-	int16_t volume;
 	int32_t loopStart, loopLength, loopEnd, sampleEnd, position;
 	uint64_t delta, drawDelta, positionFrac;
+	float fVolume8, fVolume16;
 
 	// if (loopEnabled && hasLooped && samplingPos <= loopStart+MAX_LEFT_TAPS) readFixedTapsFromThisPointer();
 	const int8_t *leftEdgeTaps8;
--