ref: 813029d72b6b62ce7af6643860acbed73068f80e
parent: 82e421b0dd0fc4154271c390d02971674e2654cb
author: Olav Sørensen <olav.sorensen@live.no>
date: Fri Nov 21 17:49:27 EST 2025
Some different tweaks
--- a/src/ft2_audio.c
+++ b/src/ft2_audio.c
@@ -28,8 +28,7 @@
static int32_t smpShiftValue;
static uint32_t oldAudioFreq, tickTimeLenInt, randSeed = INITIAL_DITHER_SEED;
static uint64_t tickTimeLenFrac;
-static float fSqrtPanningTable[256+1];
-static double dAudioNormalizeMul, dPrngStateL, dPrngStateR;
+static float fSqrtPanningTable[256+1], fAudioNormalizeMul, fPrngStateL, fPrngStateR;
static voice_t voice[MAX_CHANNELS * 2];
// globalized
@@ -103,11 +102,11 @@
amp = CLAMP(amp, 1, 32);
masterVol = CLAMP(masterVol, 0, 256);
- double dAmp = (amp * masterVol) / (32.0 * 256.0);
+ float fAmp = (amp * masterVol) / (32.0f * 256.0f);
if (!bitDepth32Flag)
- dAmp *= 32768.0;
+ fAmp *= 32768.0f;
- dAudioNormalizeMul = dAmp;
+ fAudioNormalizeMul = fAmp;
}
void decreaseMasterVol(void)
@@ -194,17 +193,15 @@
// set sinc LUT pointers
if (config.interpolation == INTERPOLATION_SINC8)
{- fSinc_1 = fSinc8_1;
- fSinc_2 = fSinc8_2;
- fSinc_3 = fSinc8_3;
+ for (int32_t i = 0; i < SINC_KERNELS; i++)
+ fSinc[i] = fSinc8[i];
audio.sincInterpolation = true;
}
else if (config.interpolation == INTERPOLATION_SINC16)
{- fSinc_1 = fSinc16_1;
- fSinc_2 = fSinc16_2;
- fSinc_3 = fSinc16_3;
+ for (int32_t i = 0; i < SINC_KERNELS; i++)
+ fSinc[i] = fSinc16[i];
audio.sincInterpolation = true;
}
@@ -381,19 +378,16 @@
if (status & CF_UPDATE_PERIOD)
{- const double dVoiceHz = dPeriod2Hz(ch->finalPeriod);
+ v->delta = period2VoiceDelta(ch->finalPeriod);
- // set voice delta
- v->delta = (int64_t)((dVoiceHz * audio.dHz2MixDeltaMul) + 0.5); // Hz -> fixed-point delta (rounded)
if (audio.sincInterpolation)
{- // decide which sinc LUT to use according to the resampling ratio
if (v->delta <= sincRatio1)
- v->fSincLUT = fSinc_1;
+ v->fSincLUT = fSinc[0];
else if (v->delta <= sincRatio2)
- v->fSincLUT = fSinc_2;
+ v->fSincLUT = fSinc[1];
else
- v->fSincLUT = fSinc_3;
+ v->fSincLUT = fSinc[2];
}
}
@@ -405,7 +399,7 @@
void resetAudioDither(void)
{randSeed = INITIAL_DITHER_SEED;
- dPrngStateL = dPrngStateR = 0.0;
+ fPrngStateL = fPrngStateR = 0.0f;
}
static inline int32_t random32(void)
@@ -420,26 +414,26 @@
static void sendSamples16BitStereo(void *stream, uint32_t sampleBlockLength)
{int32_t out32;
- double dOut, dPrng;
+ float fOut, fPrng;
int16_t *streamPtr16 = (int16_t *)stream;
for (uint32_t i = 0; i < sampleBlockLength; i++)
{// left channel - 1-bit triangular dithering
- dPrng = random32() * (1.0 / (UINT32_MAX+1.0)); // -0.5 .. 0.5
- dOut = (double)audio.fMixBufferL[i] * dAudioNormalizeMul;
- dOut = (dOut + dPrng) - dPrngStateL;
- dPrngStateL = dPrng;
- out32 = (int32_t)dOut;
+ fPrng = (float)random32() * (1.0f / (UINT32_MAX+1.0f)); // -0.5f .. 0.5f
+ fOut = audio.fMixBufferL[i] * fAudioNormalizeMul;
+ fOut = (fOut + fPrng) - fPrngStateL;
+ fPrngStateL = fPrng;
+ out32 = (int32_t)fOut;
CLAMP16(out32);
*streamPtr16++ = (int16_t)out32;
// right channel - 1-bit triangular dithering
- dPrng = random32() * (1.0 / (UINT32_MAX+1.0)); // -0.5 .. 0.5
- dOut = (double)audio.fMixBufferR[i] * dAudioNormalizeMul;
- dOut = (dOut + dPrng) - dPrngStateR;
- dPrngStateR = dPrng;
- out32 = (int32_t)dOut;
+ fPrng = (float)random32() * (1.0f / (UINT32_MAX+1.0f)); // -0.5f .. 0.5f
+ fOut = audio.fMixBufferR[i] * fAudioNormalizeMul;
+ fOut = (fOut + fPrng) - fPrngStateR;
+ fPrngStateR = fPrng;
+ out32 = (int32_t)fOut;
CLAMP16(out32);
*streamPtr16++ = (int16_t)out32;
@@ -450,20 +444,20 @@
static void sendSamples32BitFloatStereo(void *stream, uint32_t sampleBlockLength)
{- double dOut;
+ float fOut;
float *fStreamPtr32 = (float *)stream;
for (uint32_t i = 0; i < sampleBlockLength; i++)
{// left channel
- dOut = (double)audio.fMixBufferL[i] * dAudioNormalizeMul;
- dOut = CLAMP(dOut, -1.0, 1.0);
- *fStreamPtr32++ = (float)dOut;
+ fOut = audio.fMixBufferL[i] * fAudioNormalizeMul;
+ fOut = CLAMP(fOut, -1.0f, 1.0f);
+ *fStreamPtr32++ = fOut;
// right channel
- dOut = (double)audio.fMixBufferR[i] * dAudioNormalizeMul;
- dOut = CLAMP(dOut, -1.0, 1.0);
- *fStreamPtr32++ = (float)dOut;
+ fOut = audio.fMixBufferR[i] * fAudioNormalizeMul;
+ fOut = CLAMP(fOut, -1.0f, 1.0f);
+ *fStreamPtr32++ = fOut;
// clear what we read from the mixing buffer
audio.fMixBufferL[i] = audio.fMixBufferR[i] = 0.0f;
--- a/src/ft2_audio.h
+++ b/src/ft2_audio.h
@@ -50,7 +50,6 @@
uint64_t tickTime64, tickTime64Frac;
float *fMixBufferL, *fMixBufferR, fQuickVolRampSamplesMul, fSamplesPerTickIntMul;
- double dHz2MixDeltaMul;
SDL_AudioDeviceID dev;
uint32_t wantFreq, haveFreq, wantSamples, haveSamples;
--- a/src/ft2_main.c
+++ b/src/ft2_main.c
@@ -278,6 +278,8 @@
memset(&chSync, 0, sizeof (chSync));
memset(&song, 0, sizeof (song));
+ calcMiscReplayerVars();
+
// used for scopes and sampling position line (sampler screen)
for (int32_t i = 0; i < MAX_CHANNELS; i++)
{@@ -317,7 +319,6 @@
editor.diskOpReadOnOpen = true;
audio.linearPeriodsFlag = true;
- calcReplayerLogTab();
#ifdef HAS_MIDI
midi.enable = true;
--- a/src/ft2_replayer.c
+++ b/src/ft2_replayer.c
@@ -30,6 +30,8 @@
#include "mixer/ft2_cubic_spline.h"
#include "mixer/ft2_windowed_sinc.h"
+static uint64_t logTab[4*12*16], scopeLogTab[4*12*16], scopeDrawLogTab[4*12*16];
+static uint64_t amigaPeriodDiv, scopeAmigaPeriodDiv, scopeDrawAmigaPeriodDiv;
static double dLogTab[4*12*16], dExp2MulTab[32];
static bool bxxOverflow;
static note_t nilPatternLine[MAX_CHANNELS];
@@ -232,7 +234,7 @@
return i+1;
}
-double dLinearPeriod2Hz(int32_t period)
+double dPeriod2Hz(uint32_t period)
{period &= 0xFFFF; // just in case (actual period range is 0..65535)
@@ -239,29 +241,87 @@
if (period == 0)
return 0.0; // in FT2, a period of 0 results in 0Hz
- const uint32_t invPeriod = ((12 * 192 * 4) - period) & 0xFFFF; // mask needed for FT2 period overflow quirk
+ if (audio.linearPeriodsFlag)
+ {+ const uint32_t invPeriod = ((12 * 192 * 4) - period) & 0xFFFF; // mask needed for FT2 period overflow quirk
- const uint32_t quotient = invPeriod / (12 * 16 * 4);
- const uint32_t remainder = invPeriod % (12 * 16 * 4);
+ const uint32_t quotient = invPeriod / (12 * 16 * 4);
+ const uint32_t remainder = invPeriod % (12 * 16 * 4);
- return dLogTab[remainder] * dExp2MulTab[(14-quotient) & 31]; // x = y >> ((14-quotient) & 31);
+ return dLogTab[remainder] * dExp2MulTab[(14-quotient) & 31]; // x = y >> ((14-quotient) & 31);
+ }
+ else
+ {+ return (8363.0 * 1712.0) / (int32_t)period;
+ }
}
-double dAmigaPeriod2Hz(int32_t period)
+uint64_t period2VoiceDelta(uint32_t period)
{period &= 0xFFFF; // just in case (actual period range is 0..65535)
if (period == 0)
- return 0.0; // in FT2, a period of 0 results in 0Hz
+ return 0; // in FT2, a period of 0 results in 0Hz
- return (8363.0 * 1712.0) / period;
+ if (audio.linearPeriodsFlag)
+ {+ const uint32_t invPeriod = ((12 * 192 * 4) - period) & 0xFFFF; // mask needed for FT2 period overflow quirk
+
+ const uint32_t quotient = invPeriod / (12 * 16 * 4);
+ const uint32_t remainder = invPeriod % (12 * 16 * 4);
+
+ return logTab[remainder] >> ((14-quotient) & 31);
+ }
+ else
+ {+ return amigaPeriodDiv / period;
+ }
}
-double dPeriod2Hz(int32_t period)
+uint64_t period2ScopeDelta(uint32_t period)
{- return audio.linearPeriodsFlag ? dLinearPeriod2Hz(period) : dAmigaPeriod2Hz(period);
+ period &= 0xFFFF; // just in case (actual period range is 0..65535)
+
+ if (period == 0)
+ return 0; // in FT2, a period of 0 results in 0Hz
+
+ if (audio.linearPeriodsFlag)
+ {+ const uint32_t invPeriod = ((12 * 192 * 4) - period) & 0xFFFF; // mask needed for FT2 period overflow quirk
+
+ const uint32_t quotient = invPeriod / (12 * 16 * 4);
+ const uint32_t remainder = invPeriod % (12 * 16 * 4);
+
+ return scopeLogTab[remainder] >> ((14-quotient) & 31);
+ }
+ else
+ {+ return scopeAmigaPeriodDiv / period;
+ }
}
+uint64_t period2ScopeDrawDelta(uint32_t period)
+{+ period &= 0xFFFF; // just in case (actual period range is 0..65535)
+
+ if (period == 0)
+ return 0; // in FT2, a period of 0 results in 0Hz
+
+ if (audio.linearPeriodsFlag)
+ {+ const uint32_t invPeriod = ((12 * 192 * 4) - period) & 0xFFFF; // mask needed for FT2 period overflow quirk
+
+ const uint32_t quotient = invPeriod / (12 * 16 * 4);
+ const uint32_t remainder = invPeriod % (12 * 16 * 4);
+
+ return scopeDrawLogTab[remainder] >> ((14-quotient) & 31);
+ }
+ else
+ {+ return scopeDrawAmigaPeriodDiv / period;
+ }
+}
+
// returns *exact* FT2 C-4 voice rate (depending on finetune, relativeNote and linear/Amiga period mode)
double getSampleC4Rate(sample_t *s)
{@@ -386,15 +446,6 @@
}
}
-void calcReplayerLogTab(void) // for linear period -> hz calculation
-{- for (int32_t i = 0; i < 32; i++)
- dExp2MulTab[i] = 1.0 / exp2(i); // 1/(2^i)
-
- for (int32_t i = 0; i < 4*12*16; i++)
- dLogTab[i] = (8363.0 * 256.0) * exp2(i / (4.0 * 12.0 * 16.0));
-}
-
void calcReplayerVars(int32_t audioFreq)
{assert(audioFreq > 0);
@@ -401,7 +452,12 @@
if (audioFreq <= 0)
return;
- audio.dHz2MixDeltaMul = (double)MIXER_FRAC_SCALE / audioFreq;
+ const double logTabMul = (UINT32_MAX+1.0) / audioFreq;
+ for (int32_t i = 0; i < 4*12*16; i++)
+ logTab[i] = (uint64_t)round(dLogTab[i] * logTabMul);
+
+ amigaPeriodDiv = (uint64_t)round((MIXER_FRAC_SCALE * (1712.0*8363.0)) / audioFreq);
+
audio.quickVolRampSamples = (uint32_t)round(audioFreq / (1000.0 / FT2_QUICK_VOLRAMP_MILLISECONDS));
audio.fQuickVolRampSamplesMul = (float)(1.0 / audio.quickVolRampSamples);
@@ -2805,6 +2861,22 @@
freeQuadraticSplineTable();
freeCubicSplineTable();
freeWindowedSincTables();
+}
+
+void calcMiscReplayerVars(void)
+{+ for (int32_t i = 0; i < 32; i++)
+ dExp2MulTab[i] = 1.0 / exp2(i); // 1/(2^i)
+
+ for (int32_t i = 0; i < 4*12*16; i++)
+ {+ dLogTab[i] = (8363.0 * 256.0) * exp2(i / (4.0 * 12.0 * 16.0));
+ scopeLogTab[i] = (uint64_t)round(dLogTab[i] * (SCOPE_FRAC_SCALE / SCOPE_HZ));
+ scopeDrawLogTab[i] = (uint64_t)round(dLogTab[i] * (SCOPE_FRAC_SCALE / (C4_FREQ/2.0)));
+ }
+
+ scopeAmigaPeriodDiv = (uint64_t)round((SCOPE_FRAC_SCALE * (1712.0*8363.0)) / SCOPE_HZ);
+ scopeDrawAmigaPeriodDiv = (uint64_t)round((SCOPE_FRAC_SCALE * (1712.0*8363.0)) / (C4_FREQ/2.0));
}
bool setupReplayer(void)
--- a/src/ft2_replayer.h
+++ b/src/ft2_replayer.h
@@ -288,11 +288,11 @@
void calcReplayerVars(int32_t rate);
void setSampleC4Hz(sample_t *s, double dC4Hz);
-void calcReplayerLogTab(void); // for linear period -> hz calculation
-double dLinearPeriod2Hz(int32_t period);
-double dAmigaPeriod2Hz(int32_t period);
-double dPeriod2Hz(int32_t period);
+double dPeriod2Hz(uint32_t period);
+uint64_t period2VoiceDelta(uint32_t period);
+uint64_t period2ScopeDelta(uint32_t period);
+uint64_t period2ScopeDrawDelta(uint32_t period);
int32_t getPianoKey(int32_t period, int8_t finetune, int8_t relativeNote); // for piano in Instr. Ed.
void triggerNote(uint8_t note, uint8_t efx, uint8_t efxData, channel_t *ch);
@@ -305,6 +305,7 @@
void freeAllPatterns(void);
void updateChanNums(void);
+void calcMiscReplayerVars(void);
bool setupReplayer(void);
void closeReplayer(void);
void resetMusic(void);
--- a/src/mixer/ft2_cubic_spline.c
+++ b/src/mixer/ft2_cubic_spline.c
@@ -20,19 +20,19 @@
float *fPtr = fCubicSplineLUT;
for (int32_t i = 0; i < CUBIC_SPLINE_PHASES; i++)
{- const double x1 = i * (1.0 / CUBIC_SPLINE_PHASES);
- const double x2 = x1 * x1; // x^2
- const double x3 = x2 * x1; // x^3
+ const float x1 = (float)i * (1.0f / CUBIC_SPLINE_PHASES);
+ const float x2 = x1 * x1; // x^2
+ const float x3 = x2 * x1; // x^3
- const double t1 = (x1 * -0.5) + (x2 * 1.0) + (x3 * -0.5);
- const double t2 = (x2 * -2.5) + (x3 * 1.5) + 1.0;
- const double t3 = (x1 * 0.5) + (x2 * 2.0) + (x3 * -1.5);
- const double t4 = (x2 * -0.5) + (x3 * 0.5);
+ const float t1 = (x1 * -0.5f) + (x2 * 1.0f) + (x3 * -0.5f);
+ const float t2 = (x2 * -2.5f) + (x3 * 1.5f) + 1.0f;
+ const float t3 = (x1 * 0.5f) + (x2 * 2.0f) + (x3 * -1.5f);
+ const float t4 = (x2 * -0.5f) + (x3 * 0.5f);
- *fPtr++ = (float)t1; // tap #1 at sample offset -1
- *fPtr++ = (float)t2; // tap #2 at sample offset 0 (center)
- *fPtr++ = (float)t3; // tap #3 at sample offset 1
- *fPtr++ = (float)t4; // tap #4 at sample offset 2
+ *fPtr++ = t1; // tap #1 at sample offset -1
+ *fPtr++ = t2; // tap #2 at sample offset 0 (center)
+ *fPtr++ = t3; // tap #3 at sample offset 1
+ *fPtr++ = t4; // tap #4 at sample offset 2
}
/*
@@ -43,23 +43,23 @@
/*
for (int32_t i = 0; i < CUBIC_SPLINE_PHASES; i++)
{- const double x1 = i * (1.0 / CUBIC_SPLINE_PHASES);
- const double x2 = x1 * x1; // x^2
- const double x3 = x2 * x1; // x^3
+ const float x1 = i * (1.0f / CUBIC_SPLINE_PHASES);
+ const float x2 = x1 * x1; // x^2
+ const float x3 = x2 * x1; // x^3
- const double t1 = (x1 * (1.0/12.0)) + (x2 * -(1.0/ 6.0)) + (x3 * (1.0/12.0));
- const double t2 = (x1 * -(2.0/ 3.0)) + (x2 * (5.0/ 4.0)) + (x3 * -(7.0/12.0));
- const double t3 = (x2 * -(7.0/ 3.0)) + (x3 * (4.0/ 3.0)) + 1.0;
- const double t4 = (x1 * (2.0/ 3.0)) + (x2 * (5.0/ 3.0)) + (x3 * -(4.0/ 3.0));
- const double t5 = (x1 * -(1.0/12.0)) + (x2 * -(1.0/ 2.0)) + (x3 * (7.0/12.0));
- const double t6 = (x2 * (1.0/12.0)) + (x3 * -(1.0/12.0));
+ const float t1 = (x1 * (1.0f/12.0f)) + (x2 * -(1.0f/ 6.0f)) + (x3 * (1.0f/12.0f));
+ const float t2 = (x1 * -(2.0f/ 3.0f)) + (x2 * (5.0f/ 4.0f)) + (x3 * -(7.0f/12.0f));
+ const float t3 = (x2 * -(7.0f/ 3.0f)) + (x3 * (4.0f/ 3.0f)) + 1.0f;
+ const float t4 = (x1 * (2.0f/ 3.0f)) + (x2 * (5.0f/ 3.0f)) + (x3 * -(4.0f/ 3.0f));
+ const float t5 = (x1 * -(1.0f/12.0f)) + (x2 * -(1.0f/ 2.0f)) + (x3 * (7.0f/12.0f));
+ const float t6 = (x2 * (1.0f/12.0f)) + (x3 * -(1.0f/12.0f));
- *fPtr++ = (float)t1; // tap #1 at sample offset -2
- *fPtr++ = (float)t2; // tap #2 at sample offset -1
- *fPtr++ = (float)t3; // tap #3 at sample offset 0 (center)
- *fPtr++ = (float)t4; // tap #4 at sample offset 1
- *fPtr++ = (float)t5; // tap #5 at sample offset 2
- *fPtr++ = (float)t6; // tap #6 at sample offset 3
+ *fPtr++ = t1; // tap #1 at sample offset -2
+ *fPtr++ = t2; // tap #2 at sample offset -1
+ *fPtr++ = t3; // tap #3 at sample offset 0 (center)
+ *fPtr++ = t4; // tap #4 at sample offset 1
+ *fPtr++ = t5; // tap #5 at sample offset 2
+ *fPtr++ = t6; // tap #6 at sample offset 3
}
*/
--- a/src/mixer/ft2_cubic_spline.h
+++ b/src/mixer/ft2_cubic_spline.h
@@ -6,8 +6,8 @@
#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_PHASES 4096
+#define CUBIC_SPLINE_PHASES_BITS 12 /* 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)
--- a/src/mixer/ft2_mix_macros.h
+++ b/src/mixer/ft2_mix_macros.h
@@ -200,7 +200,7 @@
#define WINDOWED_SINC8_INTERPOLATION(s, f, scale) \
{ \- const float *t = v->fSincLUT + (((uint32_t)(f) >> SINC1_FRACSHIFT) & SINC1_FRACMASK); \
+ const float *t = v->fSincLUT + (((uint32_t)(f) >> SINC8_FRACSHIFT) & SINC8_FRACMASK); \
fSample = ((s[-3] * t[0]) + \
(s[-2] * t[1]) + \
(s[-1] * t[2]) + \
@@ -213,7 +213,7 @@
#define WINDOWED_SINC16_INTERPOLATION(s, f, scale) \
{ \- const float *t = v->fSincLUT + (((uint32_t)(f) >> SINC2_FRACSHIFT) & SINC2_FRACMASK); \
+ const float *t = v->fSincLUT + (((uint32_t)(f) >> SINC16_FRACSHIFT) & SINC16_FRACMASK); \
fSample = (( s[-7] * t[0]) + \
( s[-6] * t[1]) + \
( s[-5] * t[2]) + \
--- a/src/mixer/ft2_quadratic_spline.c
+++ b/src/mixer/ft2_quadratic_spline.c
@@ -20,16 +20,16 @@
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
+ const float x1 = (float)i * (1.0f / QUADRATIC_SPLINE_PHASES);
+ const float x2 = x1 * x1; // x^2
- const double t1 = (x1 * -1.5) + (x2 * 0.5) + 1.0;
- const double t2 = (x1 * 2.0) + (x2 * -1.0);
- const double t3 = (x1 * -0.5) + (x2 * 0.5);
+ const float t1 = (x1 * -1.5f) + (x2 * 0.5f) + 1.0f;
+ const float t2 = (x1 * 2.0f) + (x2 * -1.0f);
+ const float t3 = (x1 * -0.5f) + (x2 * 0.5f);
- *fPtr++ = (float)t1; // tap #1 at sample offset 0 (center)
- *fPtr++ = (float)t2; // tap #2 at sample offset 1
- *fPtr++ = (float)t3; // tap #3 at sample offset 2
+ *fPtr++ = t1; // tap #1 at sample offset 0 (center)
+ *fPtr++ = t2; // tap #2 at sample offset 1
+ *fPtr++ = t3; // tap #3 at sample offset 2
}
return true;
--- a/src/mixer/ft2_quadratic_spline.h
+++ b/src/mixer/ft2_quadratic_spline.h
@@ -5,8 +5,8 @@
#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_PHASES 4096
+#define QUADRATIC_SPLINE_PHASES_BITS 12 /* log2(QUADRATIC_SPLINE_PHASES) */
#define QUADRATIC_SPLINE_FRACSHIFT (MIXER_FRAC_BITS-QUADRATIC_SPLINE_PHASES_BITS)
extern float *fQuadraticSplineLUT;
--- a/src/mixer/ft2_windowed_sinc.c
+++ b/src/mixer/ft2_windowed_sinc.c
@@ -4,189 +4,139 @@
#include <stdbool.h>
#include <stdlib.h>
#include <math.h>
-#include "ft2_windowed_sinc.h" // SINCx_WIDTH, SINCx_PHASES
+#include "ft2_windowed_sinc.h"
#include "../ft2_header.h" // PI
#include "../ft2_video.h" // showErrorMsgBox()
+typedef struct
+{+ float kaiserBeta, sincCutoff;
+} sincKernel_t;
+
// globalized
-float *fSinc8_1 = NULL, *fSinc8_2 = NULL, *fSinc8_3 = NULL;
-float *fSinc16_1 = NULL, *fSinc16_2 = NULL, *fSinc16_3 = NULL;
-float *fSinc_1 = NULL, *fSinc_2 = NULL, *fSinc_3 = NULL;
+float *fSinc[SINC_KERNELS], *fSinc8[SINC_KERNELS], *fSinc16[SINC_KERNELS];
uint64_t sincRatio1, sincRatio2;
-// zeroth-order modified Bessel function of the first kind (series approximation)
-static inline double besselI0(double z)
+static sincKernel_t sincKernelConfig[2][SINC_KERNELS] =
{-#define EPSILON (1E-12) /* verified: lower than this makes no change when LUT output is single-precision float */
+ /* Some notes on the Kaiser-Bessel beta parameter:
+ ** Lower beta = less treble cut off, more aliasing (narrower mainlobe, stronger sidelobe)
+ ** Higher beta = more treble cut off, less aliasing (wider mainlobe, weaker sidelobe)
+ **
+ ** The 8-point kernel should not have a beta lower than around 9.2, as it results
+ ** in audible ringing at very low resampling ratios (well below 1.0, that is).
+ */
- double s = 1.0, ds = 1.0, d = 2.0;
- const double zz = z * z;
+ { // -- settings for 8-point sinc --+ // beta, cutoff
+ { 9.2f, 1.000f }, // kernel #1+ { 8.5f, 0.750f }, // kernel #2+ { 7.3f, 0.425f } // kernel #3+ },
+ { // -- settings for 16-point sinc --+ // beta, cutoff
+ { 8.6f, 1.000f }, // kernel #1+ { 8.5f, 0.750f }, // kernel #2+ { 7.3f, 0.425f } // kernel #3+ }
+};
+
+// zeroth-order modified Bessel function of the first kind (series approximation)
+static inline float besselI0(float z)
+{+ float s = 1.0f, ds = 1.0f, d = 2.0f;
+ const float zz = z * z;
+
do
{ds *= zz / (d * d);
s += ds;
- d += 2.0;
+ d += 2.0f;
}
- while (ds > s*EPSILON);
+ while (ds > s*(1E-7f));
return s;
}
-static inline double sinc(double x)
+static inline float sinc(float x, const float cutoff)
{- if (x == 0.0)
+ if (x == 0.0f)
{- return 1.0;
- }
- else
- {- x *= PI;
- return sin(x) / x;
- }
-}
-
-static inline double sincWithCutoff(double x, const double cutoff)
-{- if (x == 0.0)
- {return cutoff;
}
else
{- x *= PI;
- return sin(cutoff * x) / x;
+ x *= (float)PI;
+ return sinf(cutoff * x) / x;
}
}
-static void generateWindowedSinc(float *fOutput, const int32_t filterWidth, const int32_t filterPhases, const double beta, const double cutoff)
+// note: numPoints must be 2^n!
+static void makeSincKernel(float *out, int32_t numPoints, int32_t numPhases, float beta, float cutoff)
{- const int32_t filterWidthBits = (int32_t)log2(filterWidth);
- const int32_t filterWidthMask = filterWidth - 1;
- const int32_t filterCenter = (filterWidth / 2) - 1;
- const double besselI0Beta = 1.0 / besselI0(beta);
- const double phaseMul = 1.0 / filterPhases;
- const double xMul = 1.0 / (filterWidth / 2);
+ const int32_t kernelLen = numPhases * numPoints;
+ const int32_t pointBits = (int32_t)log2(numPoints);
+ const int32_t pointMask = numPoints - 1;
+ const int32_t centerPoint = (numPoints / 2) - 1;
+ const float besselI0Beta = 1.0f / besselI0(beta);
+ const float phaseMul = 1.0f / numPhases;
+ const float xMul = 1.0f / (numPoints / 2);
- if (cutoff < 1.0)
+ for (int32_t i = 0; i < kernelLen; i++)
{- // windowed-sinc with frequency cutoff
- for (int32_t i = 0; i < filterPhases * filterWidth; i++)
- {- const double x = ((i & filterWidthMask) - filterCenter) - ((i >> filterWidthBits) * phaseMul);
+ const float x = (float)((i & pointMask) - centerPoint) - ((float)(i >> pointBits) * phaseMul);
- // Kaiser-Bessel window
- const double n = x * xMul;
- const double window = besselI0(beta * sqrt(1.0 - n * n)) * besselI0Beta;
+ // Kaiser-Bessel window
+ const float n = x * xMul;
+ const float window = besselI0(beta * sqrtf(1.0f - n * n)) * besselI0Beta;
- fOutput[i] = (float)(sincWithCutoff(x, cutoff) * window);
- }
+ out[i] = sinc(x, cutoff) * window;
}
- else
- {- // windowed-sinc with no frequency cutoff
- for (int32_t i = 0; i < filterPhases * filterWidth; i++)
- {- const double x = ((i & filterWidthMask) - filterCenter) - ((i >> filterWidthBits) * phaseMul);
-
- // Kaiser-Bessel window
- const double n = x * xMul;
- const double window = besselI0(beta * sqrt(1.0 - n * n)) * besselI0Beta;
-
- fOutput[i] = (float)(sinc(x) * window);
- }
- }
}
bool setupWindowedSincTables(void)
{- fSinc8_1 = (float *)malloc(SINC1_WIDTH*SINC1_PHASES * sizeof (float));
- fSinc8_2 = (float *)malloc(SINC1_WIDTH*SINC1_PHASES * sizeof (float));
- fSinc8_3 = (float *)malloc(SINC1_WIDTH*SINC1_PHASES * sizeof (float));
- fSinc16_1 = (float *)malloc(SINC2_WIDTH*SINC2_PHASES * sizeof (float));
- fSinc16_2 = (float *)malloc(SINC2_WIDTH*SINC2_PHASES * sizeof (float));
- fSinc16_3 = (float *)malloc(SINC2_WIDTH*SINC2_PHASES * sizeof (float));
-
- if (fSinc8_1 == NULL || fSinc8_2 == NULL || fSinc8_3 == NULL ||
- fSinc16_1 == NULL || fSinc16_2 == NULL || fSinc16_3 == NULL)
+ sincKernel_t *k;
+ for (int32_t i = 0; i < SINC_KERNELS; i++, k++)
{- showErrorMsgBox("Not enough memory!");- return false;
- }
+ fSinc8[i] = (float *)malloc( 8 * SINC_PHASES * sizeof (float));
+ fSinc16[i] = (float *)malloc(16 * SINC_PHASES * sizeof (float));
- // LUT-select resampling ratios
- const double ratio1 = 1.1875; // fSinc_1 if <=
- const double ratio2 = 1.5; // fSinc_2 if <=, fSinc_3 if >
+ if (fSinc8[i] == NULL || fSinc16[i] == NULL)
+ {+ showErrorMsgBox("Not enough memory!");+ return false;
+ }
- // calculate mixer delta limits for LUT-selector
- sincRatio1 = (uint64_t)(ratio1 * MIXER_FRAC_SCALE);
- sincRatio2 = (uint64_t)(ratio2 * MIXER_FRAC_SCALE);
+ k = &sincKernelConfig[0][i];
+ makeSincKernel(fSinc8[i], 8, SINC_PHASES, k->kaiserBeta, k->sincCutoff);
- /* Kaiser-Bessel beta parameter
- **
- ** Basically;
- ** Lower beta = less treble cut off, but more aliasing (shorter main lobe, more side lobe ripple)
- ** Higher beta = more treble cut off, but less aliasing (wider main lobe, less side lobe ripple)
- **
- ** There simply isn't any optimal value here, it has to be tweaked to personal preference.
- */
- const double b1 = 3.0 * M_PI; // alpha = 3.00 (beta = ~9.425)
- const double b2 = 8.5;
- const double b3 = 7.3;
+ k = &sincKernelConfig[1][i];
+ makeSincKernel(fSinc16[i], 16, SINC_PHASES, k->kaiserBeta, k->sincCutoff);
+ }
- // sinc low-pass cutoff (could maybe use some further tweaking)
- const double c1 = 1.000;
- const double c2 = 0.500;
- const double c3 = 0.425;
+ // resampling ratios for sinc kernel selection
+ sincRatio1 = (uint64_t)(1.1875 * MIXER_FRAC_SCALE); // fSinc[0] if <=
+ sincRatio2 = (uint64_t)(1.5000 * MIXER_FRAC_SCALE); // fSinc[1] if <=, else fSinc[2] if >
- // 8 point
- generateWindowedSinc(fSinc8_1, SINC1_WIDTH, SINC1_PHASES, b1, c1);
- generateWindowedSinc(fSinc8_2, SINC1_WIDTH, SINC1_PHASES, b2, c2);
- generateWindowedSinc(fSinc8_3, SINC1_WIDTH, SINC1_PHASES, b3, c3);
-
- // 16 point
- generateWindowedSinc(fSinc16_1, SINC2_WIDTH, SINC2_PHASES, b1, c1);
- generateWindowedSinc(fSinc16_2, SINC2_WIDTH, SINC2_PHASES, b2, c2);
- generateWindowedSinc(fSinc16_3, SINC2_WIDTH, SINC2_PHASES, b3, c3);
-
return true;
}
void freeWindowedSincTables(void)
{- if (fSinc8_1 != NULL)
+ for (int32_t i = 0; i < SINC_KERNELS; i++)
{- free(fSinc8_1);
- fSinc8_1 = NULL;
- }
+ if (fSinc8[i] != NULL)
+ {+ free(fSinc8[i]);
+ fSinc8[i] = NULL;
+ }
- if (fSinc8_2 != NULL)
- {- free(fSinc8_2);
- fSinc8_2 = NULL;
- }
-
- if (fSinc8_3 != NULL)
- {- free(fSinc8_3);
- fSinc8_3 = NULL;
- }
-
- if (fSinc16_1 != NULL)
- {- free(fSinc16_1);
- fSinc16_1 = NULL;
- }
-
- if (fSinc16_2 != NULL)
- {- free(fSinc16_2);
- fSinc16_2 = NULL;
- }
-
- if (fSinc16_3 != NULL)
- {- free(fSinc16_3);
- fSinc16_3 = NULL;
+ if (fSinc16[i] != NULL)
+ {+ free(fSinc16[i]);
+ fSinc16[i] = NULL;
+ }
}
}
--- a/src/mixer/ft2_windowed_sinc.h
+++ b/src/mixer/ft2_windowed_sinc.h
@@ -4,23 +4,19 @@
#include <stdbool.h>
#include "ft2_mix.h" // MIXER_FRAC_BITS
-#define SINC1_WIDTH 8
-#define SINC1_WIDTH_BITS 3 /* log2(SINC1_WIDTH) */
-#define SINC1_PHASES 8192
-#define SINC1_PHASES_BITS 13 /* log2(SINC1_PHASES) */
-#define SINC1_FRACSHIFT (MIXER_FRAC_BITS-(SINC1_PHASES_BITS+SINC1_WIDTH_BITS))
-#define SINC1_FRACMASK ((SINC1_WIDTH*SINC1_PHASES)-SINC1_WIDTH)
+#define SINC_KERNELS 3
+#define SINC_PHASES 4096
+#define SINC_PHASES_BITS 12 /* log2(SINC_PHASES) */
-#define SINC2_WIDTH 16
-#define SINC2_WIDTH_BITS 4 /* log2(SINC2_WIDTH) */
-#define SINC2_PHASES 8192
-#define SINC2_PHASES_BITS 13 /* log2(SINC2_PHASES) */
-#define SINC2_FRACSHIFT (MIXER_FRAC_BITS-(SINC2_PHASES_BITS+SINC2_WIDTH_BITS))
-#define SINC2_FRACMASK ((SINC2_WIDTH*SINC2_PHASES)-SINC2_WIDTH)
+#define SINC8_WIDTH_BITS 3 /* log2(8) */
+#define SINC8_FRACSHIFT (MIXER_FRAC_BITS-(SINC_PHASES_BITS+SINC8_WIDTH_BITS))
+#define SINC8_FRACMASK ((8*SINC_PHASES)-8)
-extern float *fSinc8_1, *fSinc8_2, *fSinc8_3;
-extern float *fSinc16_1, *fSinc16_2, *fSinc16_3;
-extern float *fSinc_1, *fSinc_2, *fSinc_3;
+#define SINC16_WIDTH_BITS 4 /* log2(16) */
+#define SINC16_FRACSHIFT (MIXER_FRAC_BITS-(SINC_PHASES_BITS+SINC16_WIDTH_BITS))
+#define SINC16_FRACMASK ((16*SINC_PHASES)-16)
+
+extern float *fSinc[SINC_KERNELS], *fSinc8[SINC_KERNELS], *fSinc16[SINC_KERNELS];
extern uint64_t sincRatio1, sincRatio2;
bool setupWindowedSincTables(void);
--- a/src/scopes/ft2_scopedraw.c
+++ b/src/scopes/ft2_scopedraw.c
@@ -31,14 +31,14 @@
int16_t *ptr16 = scopeIntrpLUT;
for (int32_t i = 0; i < SCOPE_INTRP_PHASES; i++)
{- const double x1 = i * (1.0 / SCOPE_INTRP_PHASES);
- const double x2 = x1 * x1; // x^2
- const double x3 = x2 * x1; // x^3
+ const float x1 = i * (1.0f / SCOPE_INTRP_PHASES);
+ const float x2 = x1 * x1; // x^2
+ const float x3 = x2 * x1; // x^3
- 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);
+ const float t1 = (x1 * -(1.0f/2.0f)) + (x2 * (1.0f/2.0f)) + (x3 * -(1.0f/6.0f)) + (1.0f/6.0f);
+ const float t2 = (x2 * -1.0f ) + (x3 * (1.0f/2.0f)) + (2.0f/3.0f);
+ const float t3 = (x1 * (1.0f/2.0f)) + (x2 * (1.0f/2.0f)) + (x3 * -(1.0f/2.0f)) + (1.0f/6.0f);
+ const float t4 = x3 * (1.0f/6.0f);
// truncate, do not round!
*ptr16++ = (int16_t)(t1 * SCOPE_INTRP_SCALE); // tap #1 at sample offset -1
--- a/src/scopes/ft2_scopes.c
+++ b/src/scopes/ft2_scopes.c
@@ -490,10 +490,8 @@
if (status & CF_UPDATE_PERIOD)
{- 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)));
+ sc->delta = period2ScopeDelta(ch->period);
+ sc->drawDelta = period2ScopeDrawDelta(ch->period);
}
if (status & CS_TRIGGER_VOICE)
--
⑨