commit 972dbd93232a868c586c79121db648fb0c397d33
parent 65356a7a59258839e2c91869a43fc76e547c3025
Author: Olav Sørensen <olav.sorensen@live.no>
Date: Fri, 20 Dec 2024 21:59:55 +0100
Scopes interpolation fix
Diffstat:
4 files changed, 84 insertions(+), 35 deletions(-)
diff --git a/src/ft2_audio.c b/src/ft2_audio.c
@@ -380,8 +380,8 @@ void updateVoices(void)
{
v->fVolume = ch->fFinalVol;
- // set scope volume
- const int32_t scopeVolume = (int32_t)((SCOPE_HEIGHT * ch->fFinalVol) + 0.5f); // rounded
+ // scale volume for scopes (0..128)
+ const int32_t scopeVolume = (int32_t)((ch->fFinalVol * 128.0f) + 0.5f); // rounded
v->scopeVolume = (uint8_t)scopeVolume;
}
diff --git a/src/scopes/ft2_scope_macros.h b/src/scopes/ft2_scope_macros.h
@@ -10,7 +10,7 @@
/* ----------------------------------------------------------------------- */
#define SCOPE_REGS_NO_LOOP \
- const int32_t volume = s->volume; \
+ const int32_t volume = s->volume * SCOPE_HEIGHT; \
const int32_t sampleEnd = s->sampleEnd; \
const uint64_t delta = s->drawDelta; \
const uint32_t color = video.palette[PAL_PATTEXT]; \
@@ -20,7 +20,7 @@
uint64_t positionFrac = 0;
#define SCOPE_REGS_LOOP \
- const int32_t volume = s->volume; \
+ const int32_t volume = s->volume * SCOPE_HEIGHT; \
const int32_t sampleEnd = s->sampleEnd; \
const int32_t loopStart = s->loopStart; \
const int32_t loopLength = s->loopLength; \
@@ -32,7 +32,7 @@
uint64_t positionFrac = 0;
#define SCOPE_REGS_BIDI \
- const int32_t volume = s->volume; \
+ const int32_t volume = s->volume * SCOPE_HEIGHT; \
const int32_t sampleEnd = s->sampleEnd; \
const int32_t loopStart = s->loopStart; \
const int32_t loopLength = s->loopLength; \
@@ -74,19 +74,38 @@
const int32_t f = (frac) >> (SCOPE_FRAC_BITS-15); \
sample = (s8[0] << 8) + ((((s8[1] - s8[0]) << 8) * f) >> 15); \
} \
- else \
+ else /* interpolate scopes using 6-tap cubic B-spline */ \
{ \
const float *t = fScopeIntrpLUT + (((frac) >> (SCOPE_FRAC_BITS-SCOPE_INTRP_PHASES_BITS)) * SCOPE_INTRP_TAPS); \
\
- /* This has a delay of 2 samples, but that's acceptable for a tracker scope. */ \
- /* Not having to look-up previous samples significantly reduces the */ \
- /* logic needed in the scopes. */ \
- float fSample = (s8[0] * t[0]) + \
- (s8[1] * t[1]) + \
- (s8[2] * t[2]) + \
- (s8[3] * t[3]) + \
- (s8[4] * t[4]) + \
- (s8[5] * t[5]); \
+ /* get correct negative tap sample points */ \
+ int32_t p1 = pos - 2; \
+ int32_t p2 = pos - 1; \
+ float fSample; \
+ if (s->loopType != LOOP_DISABLED && s->hasLooped && (int32_t)pos-2 < (int32_t)s->loopStart) \
+ { \
+ const int32_t overflow1 = (int32_t)s->loopStart - p1; \
+ const int32_t overflow2 = (int32_t)s->loopStart - p2; \
+ if (s->loopType == LOOP_BIDI) /* direction is always backwards at this point */ \
+ { \
+ p1 = s->loopStart + overflow1; \
+ if (overflow2 > 0) \
+ p2 = s->loopStart + overflow2; \
+ } \
+ else \
+ { \
+ p1 = s->loopEnd - overflow1; \
+ if (overflow2 > 0) \
+ p2 = s->loopEnd - overflow2; \
+ } \
+ } \
+ \
+ fSample = (s->base8[p1] * t[0]) + \
+ (s->base8[p2] * t[1]) + \
+ ( s8[0] * t[2]) + \
+ ( s8[1] * t[3]) + \
+ ( s8[2] * t[4]) + \
+ ( s8[3] * t[5]); \
sample = (int32_t)(fSample * 256.0f); \
}
@@ -101,30 +120,50 @@
const int32_t f = (frac) >> (SCOPE_FRAC_BITS-15); \
sample = s16[0] + (((s16[1] - s16[0]) * f) >> 15); \
} \
- else \
+ else /* interpolate scopes using 6-tap cubic B-spline */ \
{ \
const float *t = fScopeIntrpLUT + (((frac) >> (SCOPE_FRAC_BITS-SCOPE_INTRP_PHASES_BITS)) * SCOPE_INTRP_TAPS); \
\
- /* This has a delay of 2 samples, but that's acceptable for a tracker scope. */ \
- /* Not having to look-up previous samples significantly reduces the */ \
- /* logic needed in the scopes. */ \
- float fSample = (s16[0] * t[0]) + \
- (s16[1] * t[1]) + \
- (s16[2] * t[2]) + \
- (s16[3] * t[3]) + \
- (s16[4] * t[4]) + \
- (s16[5] * t[5]); \
+ /* get correct negative tap sample points */ \
+ int32_t p1 = pos - 2; \
+ int32_t p2 = pos - 1; \
+ float fSample; \
+ if (s->loopType != LOOP_DISABLED && s->hasLooped && (int32_t)pos-2 < (int32_t)s->loopStart) \
+ { \
+ const int32_t overflow1 = (int32_t)s->loopStart - p1; \
+ const int32_t overflow2 = (int32_t)s->loopStart - p2; \
+ if (s->loopType == LOOP_BIDI) /* direction is always backwards at this point */ \
+ { \
+ p1 = s->loopStart + overflow1; \
+ if (overflow2 > 0) \
+ p2 = s->loopStart + overflow2; \
+ } \
+ else \
+ { \
+ p1 = s->loopEnd - overflow1; \
+ if (overflow2 > 0) \
+ p2 = s->loopEnd - overflow2; \
+ } \
+ } \
+ \
+ fSample = (s->base16[p1] * t[0]) + \
+ (s->base16[p2] * t[1]) + \
+ ( s16[0] * t[2]) + \
+ ( s16[1] * t[3]) + \
+ ( s16[2] * t[4]) + \
+ ( s16[3] * t[5]); \
+ \
sample = (int32_t)fSample; \
}
#define SCOPE_GET_SMP8 \
if (s->active) \
- sample = (s->base8[position] * volume) >> 8; \
+ sample = (s->base8[position] * volume) >> (8+7); \
else \
sample = 0;
#define SCOPE_GET_SMP16 \
if (s->active) \
- sample = (s->base16[position] * volume) >> 16; \
+ sample = (s->base16[position] * volume) >> (16+7); \
else \
sample = 0;
@@ -132,7 +171,7 @@
if (s->active) \
{ \
GET_BIDI_POSITION \
- sample = (s->base8[actualPos] * volume) >> 8; \
+ sample = (s->base8[actualPos] * volume) >> (8+7); \
} \
else \
{ \
@@ -143,7 +182,7 @@
if (s->active) \
{ \
GET_BIDI_POSITION \
- sample = (s->base16[actualPos] * volume) >> 16; \
+ sample = (s->base16[actualPos] * volume) >> (16+7); \
} \
else \
{ \
@@ -154,7 +193,7 @@
if (s->active) \
{ \
INTERPOLATE_SMP8(position, (uint32_t)positionFrac) \
- sample = (sample * volume) >> 16; \
+ sample = (sample * volume) >> (16+7); \
} \
else \
{ \
@@ -165,7 +204,7 @@
if (s->active) \
{ \
INTERPOLATE_SMP16(position, (uint32_t)positionFrac) \
- sample = (sample * volume) >> 16; \
+ sample = (sample * volume) >> (16+7); \
} \
else \
{ \
@@ -183,7 +222,7 @@
{ \
GET_BIDI_POSITION \
INTERPOLATE_SMP8(actualPos, samplingBackwards ? ((uint32_t)positionFrac ^ UINT32_MAX) : (uint32_t)positionFrac) \
- sample = (sample * volume) >> 16; \
+ sample = (sample * volume) >> (16+7); \
} \
else \
{ \
@@ -195,7 +234,7 @@
{ \
GET_BIDI_POSITION \
INTERPOLATE_SMP16(actualPos, samplingBackwards ? ((uint32_t)positionFrac ^ UINT32_MAX) : (uint32_t)positionFrac) \
- sample = (sample * volume) >> 16; \
+ sample = (sample * volume) >> (16+7); \
} \
else \
{ \
@@ -246,6 +285,8 @@
position = loopStart + ((position - sampleEnd) % loopLength); \
else \
position = loopStart; \
+ \
+ s->hasLooped = true; \
}
#define SCOPE_HANDLE_POS_BIDI \
@@ -263,4 +304,6 @@
{ \
position = loopStart; \
} \
+ \
+ s->hasLooped = true; \
}
diff --git a/src/scopes/ft2_scopes.c b/src/scopes/ft2_scopes.c
@@ -314,10 +314,12 @@ static void scopeTrigger(int32_t ch, const sample_t *s, int32_t playOffset)
tempState.sample16Bit = sample16Bit;
tempState.loopType = loopType;
+ tempState.hasLooped = false;
tempState.samplingBackwards = false;
tempState.sampleEnd = (loopType == LOOP_OFF) ? length : loopEnd;
tempState.loopStart = loopStart;
tempState.loopLength = loopLength;
+ tempState.loopEnd = loopEnd;
tempState.position = playOffset;
tempState.positionFrac = 0;
@@ -376,6 +378,8 @@ static void updateScopes(void)
{
s.position = s.loopStart;
}
+
+ s.hasLooped = true;
}
else if (s.loopType == LOOP_FORWARD)
{
@@ -383,6 +387,8 @@ static void updateScopes(void)
s.position = s.loopStart + ((s.position - s.sampleEnd) % s.loopLength);
else
s.position = s.loopStart;
+
+ s.hasLooped = true;
}
else // no loop
{
diff --git a/src/scopes/ft2_scopes.h b/src/scopes/ft2_scopes.h
@@ -35,9 +35,9 @@ typedef struct scope_t
volatile bool active;
const int8_t *base8;
const int16_t *base16;
- bool wasCleared, sample16Bit, samplingBackwards;
+ bool wasCleared, sample16Bit, samplingBackwards, hasLooped;
uint8_t loopType;
- int32_t volume, loopStart, loopLength, sampleEnd, position;
+ int32_t volume, loopStart, loopLength, loopEnd, sampleEnd, position;
uint64_t delta, drawDelta, positionFrac;
} scope_t;