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;
--
⑨