ref: 1e5706a2419f55dc66900ecf6783a9a17172ee50
parent: 065a79216e2697245dc175423fe4a85f27106ede
author: Olav Sørensen <olav.sorensen@live.no>
date: Mon Jun 15 17:39:32 EDT 2020
Small scope/mixer delta calculation rework Eliminated the need for a 512kB table, and also did some small code cleanup.
--- a/.gitignore
+++ b/.gitignore
@@ -18,3 +18,5 @@
*.cod
vs2019_project/ft2-clone/Debug/ft2-clone.vcxproj.FileListAbsolute.txt
*.opendb
+*.db-shm
+*.db-wal
--- a/src/ft2_audio.c
+++ b/src/ft2_audio.c
@@ -18,14 +18,6 @@
#define INITIAL_DITHER_SEED 0x12345000
-// globalized
-audio_t audio;
-pattSyncData_t *pattSyncEntry;
-chSyncData_t *chSyncEntry;
-chSync_t chSync;
-pattSync_t pattSync;
-volatile bool pattQueueClearing, chQueueClearing;
-
static int8_t pmpCountDiv, pmpChannels = 2;
static uint16_t smpBuffSize;
static int32_t masterVol, oldAudioFreq, pmpLeft, randSeed = INITIAL_DITHER_SEED;
@@ -35,19 +27,29 @@
static voice_t voice[MAX_VOICES * 2];
static void (*sendAudSamplesFunc)(uint8_t *, uint32_t, uint8_t); // "send mixed samples" routines
-#if !defined __amd64__ && !defined _WIN64
static int32_t oldPeriod;
+#if !defined __amd64__ && !defined _WIN64
static uint32_t oldSFrq, oldSFrqRev;
+#else
+static uint64_t oldSFrq;
#endif
-#if !defined __amd64__ && !defined _WIN64
+// globalized
+audio_t audio;
+pattSyncData_t *pattSyncEntry;
+chSyncData_t *chSyncEntry;
+chSync_t chSync;
+pattSync_t pattSync;
+volatile bool pattQueueClearing, chQueueClearing;
+
void resetCachedMixerVars(void)
{
oldPeriod = -1;
oldSFrq = 0;
+#if !defined __amd64__ && !defined _WIN64
oldSFrqRev = 0xFFFFFFFF;
-}
#endif
+}
void stopVoice(int32_t i)
{
@@ -368,22 +370,23 @@
if (status & IS_Period)
{
-#if defined __amd64__ || defined _WIN64
- v->SFrq = getFrequenceValue(ch->finalPeriod);
-#else
- // use cached values to prevent a 32-bit divsion all the time
- if (ch->finalPeriod != oldPeriod)
+ // use cached values if possible
+
+ const uint16_t period = ch->finalPeriod;
+ if (period != oldPeriod)
{
- oldPeriod = ch->finalPeriod;
+ oldPeriod = period;
+ oldSFrq = getMixerDelta(period);
- oldSFrq = getFrequenceValue(ch->finalPeriod);
-
+#if !defined __amd64__ && !defined _WIN64
oldSFrqRev = 0xFFFFFFFF;
if (oldSFrq != 0)
oldSFrqRev /= oldSFrq;
+#endif
}
v->SFrq = oldSFrq;
+#if !defined __amd64__ && !defined _WIN64
v->SFrqRev = oldSFrqRev;
#endif
}
--- a/src/ft2_audio.h
+++ b/src/ft2_audio.h
@@ -54,7 +54,7 @@
uint32_t freq;
uint32_t audLatencyPerfValInt, audLatencyPerfValFrac, speedVal, musicTimeSpeedVal;
uint64_t tickTime64, tickTime64Frac, tickTimeLengthTab[MAX_BPM+1];
- double dAudioLatencyMs, dSpeedValMul, dPianoDeltaMul;
+ double dAudioLatencyMs;
SDL_AudioDeviceID dev;
uint32_t wantFreq, haveFreq, wantSamples, haveSamples, wantChannels, haveChannels;
} audio_t;
@@ -104,21 +104,7 @@
chSyncData_t data[SYNC_QUEUE_LEN + 1];
} chSync_t;
-// in ft2_audio.c
-extern audio_t audio;
-extern pattSyncData_t *pattSyncEntry;
-extern chSyncData_t *chSyncEntry;
-extern chSync_t chSync;
-extern pattSync_t pattSync;
-
-extern volatile bool pattQueueClearing, chQueueClearing;
-
-#if !defined __amd64__ && !defined _WIN64
void resetCachedMixerVars(void);
-#endif
-
-void calcAudioTables(void);
-
int32_t pattQueueReadSize(void);
int32_t pattQueueWriteSize(void);
bool pattQueuePush(pattSyncData_t t);
@@ -153,3 +139,12 @@
void mix_SaveIPVolumes(void);
void mix_UpdateChannelVolPanFrq(void);
uint32_t mixReplayerTickToBuffer(uint8_t *stream, uint8_t bitDepth);
+
+// in ft2_audio.c
+extern audio_t audio;
+extern pattSyncData_t *pattSyncEntry;
+extern chSyncData_t *chSyncEntry;
+extern chSync_t chSync;
+extern pattSync_t pattSync;
+
+extern volatile bool pattQueueClearing, chQueueClearing;
--- a/src/ft2_header.h
+++ b/src/ft2_header.h
@@ -12,7 +12,7 @@
#endif
#include "ft2_replayer.h"
-#define PROG_VER_STR "1.25"
+#define PROG_VER_STR "1.26"
// do NOT change these! It will only mess things up...
--- a/src/ft2_main.c
+++ b/src/ft2_main.c
@@ -312,7 +312,7 @@
audio.linearFreqTable = true;
- calcAudioTables();
+ calcReplayerLogTab();
}
static void cleanUpAndExit(void) // never call this inside the main loop!
--- a/src/ft2_replayer.c
+++ b/src/ft2_replayer.c
@@ -26,20 +26,10 @@
** If something looks to be off, it probably isn't!
*/
-/* Tables for pre-calculated stuff on run time and when changing freq. and/or linear/amiga mode.
-** FT2 obviously didn't have such big tables.
-*/
-
+// non-FT2 precalced stuff
static uint32_t musicTimeTab[MAX_BPM+1];
-static uint64_t period2ScopeDeltaTab[65536];
-static double dLogTab[768], dLogTabMul[32], dAudioRateFactor;
+static double dPeriod2HzTab[65536], dLogTab[768], dAudioRateFactor;
-#if defined _WIN64 || defined __amd64__
-static uint64_t period2DeltaTab[65536];
-#else
-static uint32_t period2DeltaTab[65536];
-#endif
-
static bool bxxOverflow;
static tonTyp nilPatternLine;
@@ -141,8 +131,8 @@
return;
}
- double dFreq = log2(midCFreq * (1.0 / 8363.0)) * (12.0 * 128.0);
- int32_t linearFreq = (int32_t)(dFreq + 0.5);
+ double dFreq = log2(midCFreq / 8363.0) * (12.0 * 128.0);
+ int32_t linearFreq = (int32_t)(dFreq + 0.5); // rounded
s->fine = ((linearFreq + 128) & 255) - 128;
int32_t relTon = (linearFreq - s->fine) >> 7;
@@ -220,61 +210,29 @@
return i+1;
}
-// called every time you change linear/amiga mode and mixing frequency
-static void calcPeriod2DeltaTables(void)
+static void calcPeriod2HzTab(void) // called every time you change linear/amiga period mode
{
- int32_t baseDelta;
- uint32_t i;
+ dPeriod2HzTab[0] = 0.0; // in FT2, a period of 0 yields 0Hz
- period2DeltaTab[0] = 0; // in FT2, a period of 0 is converted to a delta of 0
-
- const double dScopeRateFactor = SCOPE_FRAC_SCALE / (double)SCOPE_HZ;
-
if (audio.linearFreqTable)
{
// linear periods
- for (i = 1; i < 65536; i++)
+ for (int32_t i = 1; i < 65536; i++)
{
const uint16_t invPeriod = (12 * 192 * 4) - (uint16_t)i; // this intentionally overflows uint16_t to be accurate to FT2
const int32_t octave = invPeriod / 768;
const int32_t period = invPeriod % 768;
- const int32_t shift = (14 - octave) & 0x1F; // 100% accurate to FT2!
+ const int32_t bitshift = (14 - octave) & 0x1F; // 100% accurate to FT2
- const double dHz = dLogTab[period] * dLogTabMul[shift];
-
-#if defined _WIN64 || defined __amd64__
- period2DeltaTab[i] = (uint64_t)((dHz * dAudioRateFactor) + 0.5);
-#else
- period2DeltaTab[i] = (uint32_t)((dHz * dAudioRateFactor) + 0.5);
-#endif
- period2ScopeDeltaTab[i] = (uint64_t)((dHz * dScopeRateFactor) + 0.5);
+ dPeriod2HzTab[i] = dLogTab[period] / (1UL << bitshift);
}
}
else
{
// Amiga periods
- for (i = 1; i < 65536; i++)
- {
- double dHz = (8363.0 * 1712.0) / i;
-
-#if defined _WIN64 || defined __amd64__
- period2DeltaTab[i] = (uint64_t)((dHz * dAudioRateFactor) + 0.5);
-#else
- period2DeltaTab[i] = (uint32_t)((dHz * dAudioRateFactor) + 0.5);
-#endif
- period2ScopeDeltaTab[i] = (uint64_t)((dHz * dScopeRateFactor) + 0.5);
- }
+ for (int32_t i = 1; i < 65536; i++)
+ dPeriod2HzTab[i] = (8363.0 * 1712.0) / i;
}
-
- // for piano in Instr. Ed.
-
- // (this delta is small enough to fit in int32_t even with 32.32 deltas)
- if (audio.linearFreqTable)
- baseDelta = (int32_t)period2DeltaTab[7680];
- else
- baseDelta = (int32_t)period2DeltaTab[1712*16];
-
- audio.dPianoDeltaMul = 1.0 / baseDelta;
}
void setFrqTab(bool linear)
@@ -288,7 +246,7 @@
else
note2Period = amigaPeriods;
- calcPeriod2DeltaTables();
+ calcPeriod2HzTab();
resumeAudio();
@@ -302,7 +260,7 @@
ch->realVol = ch->oldVol;
ch->outVol = ch->oldVol;
ch->outPan = ch->oldPan;
- ch->status |= (IS_Vol + IS_Pan + IS_QuickVol);
+ ch->status |= IS_Vol + IS_Pan + IS_QuickVol;
}
static void retrigEnvelopeVibrato(stmTyp *ch)
@@ -376,26 +334,18 @@
{
ch->realVol = 0;
ch->outVol = 0;
- ch->status |= (IS_Vol + IS_QuickVol);
+ ch->status |= IS_Vol + IS_QuickVol;
}
}
-void calcAudioTables(void)
+void calcReplayerLogTab(void)
{
- int32_t i;
-
- for (i = 0; i < 768; i++)
+ for (int32_t i = 0; i < 768; i++)
dLogTab[i] = exp2(i / 768.0) * (8363.0 * 256.0);
-
- dLogTabMul[0] = 1.0;
- for (i = 1; i < 32; i++)
- dLogTabMul[i] = exp2(-i);
}
void calcReplayRate(int32_t audioFreq)
{
- int32_t i;
-
if (audioFreq == 0)
return;
@@ -403,77 +353,66 @@
audio.quickVolSizeVal = audioFreq / 200; // FT2 truncates here
audio.rampQuickVolMul = (int32_t)(((UINT32_MAX + 1.0) / audio.quickVolSizeVal) + 0.5);
- audio.dSpeedValMul = editor.dPerfFreq / audioFreq; // for audio/video sync
/* Calculate tables to prevent floating point operations on systems that
** might have a slow FPU. This is quite hackish and not really needed,
- ** but it doesn't take up THAT much RAM anyway.
+ ** but it doesn't take up a lot of RAM, so why not.
*/
- // calculate table used to count replayer time (displayed as hours/minutes/seconds)
- const double dMul = (UINT32_MAX + 1.0) / audioFreq;
-
audio.speedValTab[0] = 0;
musicTimeTab[0] = UINT32_MAX;
- audio.rampSpeedValMulTab[0] = UINT32_MAX;
- audio.tickTimeLengthTab[0] = (uint64_t)UINT32_MAX << 32;
+ audio.tickTimeLengthTab[0] = UINT64_MAX;
+ audio.rampSpeedValMulTab[0] = INT32_MAX;
- const double dTickTimeLenMul = audio.dSpeedValMul * (UINT32_MAX + 1.0);
- for (i = 1; i <= MAX_BPM; i++)
- {
- int32_t samplesPerTick = (int32_t)(((audioFreq * 2.5) / i) + 0.5); // rounded
+ const double dMul1 = (UINT32_MAX + 1.0) / audioFreq;
+ const double dMul2 = (editor.dPerfFreq / audioFreq) * (UINT32_MAX + 1.0);
+ for (int32_t i = 1; i <= MAX_BPM; i++)
+ {
+ const int32_t samplesPerTick = (int32_t)(((audioFreq * 2.5) / i) + 0.5); // rounded
audio.speedValTab[i] = samplesPerTick;
// used for song playback counter (hh:mm:ss)
- musicTimeTab[i] = (uint32_t)((samplesPerTick * dMul) + 0.5);
+ musicTimeTab[i] = (uint32_t)((samplesPerTick * dMul1) + 0.5);
// number of samples per tick -> tick length for performance counter (syncing visuals to audio)
- audio.tickTimeLengthTab[i] = (uint64_t)(samplesPerTick * dTickTimeLenMul);
+ audio.tickTimeLengthTab[i] = (uint64_t)(samplesPerTick * dMul2);
// for calculating volume ramp length for "tick" ramps
audio.rampSpeedValMulTab[i] = (int32_t)(((UINT32_MAX + 1.0) / samplesPerTick) + 0.5);
}
+}
- calcPeriod2DeltaTables();
+double period2Hz(uint16_t period)
+{
+ return dPeriod2HzTab[period];
}
#if defined _WIN64 || defined __amd64__
-uint64_t getFrequenceValue(uint16_t period)
+int64_t getMixerDelta(uint16_t period)
{
- return period2DeltaTab[period];
+ return (int64_t)((dPeriod2HzTab[period] * dAudioRateFactor) + 0.5); // Hz -> rounded fixed-point mixer delta
}
#else
-uint32_t getFrequenceValue(uint16_t period)
+int32_t getMixerDelta(uint16_t period)
{
- return period2DeltaTab[period];
+ return (int32_t)((dPeriod2HzTab[period] * dAudioRateFactor) + 0.5); // Hz -> rounded fixed-point mixer delta
}
#endif
int32_t getPianoKey(uint16_t period, int32_t finetune, int32_t relativeNote) // for piano in Instr. Ed.
{
-#if defined _WIN64 || defined __amd64__
- uint64_t delta = period2DeltaTab[period];
-#else
- uint32_t delta = period2DeltaTab[period];
-#endif
-
finetune >>= 3; // FT2 does this in the replayer internally, so the actual range is -16..15
- const double dNote = (log2(delta * audio.dPianoDeltaMul) * 12.0) - (finetune * (1.0 / 16.0));
- int32_t note = (int32_t)(dNote + 0.5);
+ const double dRelativeHz = dPeriod2HzTab[period] * (1.0 / (8363.0 / 16.0));
+ const double dNote = (log2(dRelativeHz) * 12.0) - (finetune * (1.0 / 16.0));
- note -= relativeNote;
+ const int32_t note = (int32_t)(dNote + 0.5) - relativeNote; // rounded
- // "note" is now the raw piano key number, unaffected by finetune/relativeNote
+ // "note" is now the raw piano key number, unaffected by finetune and relativeNote
return note;
}
-uint64_t getScopeFrequenceValue(uint16_t period)
-{
- return period2ScopeDeltaTab[period];
-}
-
static void startTone(uint8_t ton, uint8_t effTyp, uint8_t eff, stmTyp *ch)
{
uint8_t smp;
@@ -538,7 +477,7 @@
}
}
- ch->status |= (IS_Period + IS_Vol + IS_Pan + IS_NyTon + IS_QuickVol);
+ ch->status |= IS_Period + IS_Vol + IS_Pan + IS_NyTon + IS_QuickVol;
if (effTyp == 9)
{
@@ -787,7 +726,7 @@
{
ch->realVol = 0;
ch->outVol = 0;
- ch->status |= (IS_Vol + IS_QuickVol);
+ ch->status |= IS_Vol + IS_QuickVol;
}
}
@@ -978,7 +917,7 @@
ch->outVol = volKol;
ch->realVol = volKol;
- ch->status |= (IS_Vol + IS_QuickVol);
+ ch->status |= IS_Vol + IS_QuickVol;
}
// fine volume slide down
@@ -1035,7 +974,7 @@
ch->realVol = 64;
ch->outVol = ch->realVol;
- ch->status |= (IS_Vol + IS_QuickVol);
+ ch->status |= IS_Vol + IS_QuickVol;
return;
}
@@ -1939,7 +1878,7 @@
{
ch->outVol = 0;
ch->realVol = 0;
- ch->status |= (IS_Vol + IS_QuickVol);
+ ch->status |= IS_Vol + IS_QuickVol;
}
}
@@ -2064,7 +2003,7 @@
ch->tremorPos = tremorSign | tremorData;
ch->outVol = (tremorSign == 0x80) ? ch->realVol : 0;
- ch->status |= (IS_Vol + IS_QuickVol);
+ ch->status |= IS_Vol + IS_QuickVol;
}
}
@@ -2779,6 +2718,7 @@
audio.linearFreqTable = true;
note2Period = linearPeriods;
+ calcPeriod2HzTab();
setPos(0, 0, true);
@@ -3114,9 +3054,8 @@
stopAllScopes();
resetAudioDither();
-#if !defined __amd64__ && !defined _WIN64
resetCachedMixerVars();
-#endif
+ resetCachedScopeVars();
// wait for scope thread to finish, so that we know pointers aren't deprecated
while (editor.scopeThreadMutex);
--- a/src/ft2_replayer.h
+++ b/src/ft2_replayer.h
@@ -237,18 +237,18 @@
void fixSongName(void); // removes spaces from right side of song name
void fixSampleName(int16_t nr); // removes spaces from right side of ins/smp names
-
void calcReplayRate(int32_t rate);
void tuneSample(sampleTyp *s, int32_t midCFreq);
+void calcReplayerLogTab(void);
+double period2Hz(uint16_t period);
#if defined _WIN64 || defined __amd64__
-uint64_t getFrequenceValue(uint16_t period);
+int64_t getMixerDelta(uint16_t period);
#else
-uint32_t getFrequenceValue(uint16_t period);
+int32_t getMixerDelta(uint16_t period);
#endif
-uint64_t getScopeFrequenceValue(uint16_t period);
int32_t getPianoKey(uint16_t period, int32_t finetune, int32_t relativeNote); // for piano in Instr. Ed.
bool allocateInstr(int16_t nr);
--- a/src/ft2_scopedraw.c
+++ b/src/ft2_scopedraw.c
@@ -101,13 +101,13 @@
#define SCOPE_UPDATE_DRAWPOS \
scopeDrawFrac += scopeDrawDelta; \
- scopeDrawPos += scopeDrawFrac >> 16; \
- scopeDrawFrac &= 0xFFFF; \
+ scopeDrawPos += scopeDrawFrac >> SCOPE_DRAW_FRAC_BITS; \
+ scopeDrawFrac &= SCOPE_DRAW_FRAC_MASK; \
#define SCOPE_UPDATE_DRAWPOS_PINGPONG \
scopeDrawFrac += scopeDrawDelta; \
- scopeDrawPos += (scopeDrawFrac >> 16) * drawPosDir; \
- scopeDrawFrac &= 0xFFFF; \
+ scopeDrawPos += (scopeDrawFrac >> SCOPE_DRAW_FRAC_BITS) * drawPosDir; \
+ scopeDrawFrac &= SCOPE_DRAW_FRAC_MASK; \
#define SCOPE_DRAW_SMP \
video.frameBuffer[((lineY - sample) * SCREEN_W) + x] = scopePixelColor;
--- a/src/ft2_scopedraw.h
+++ b/src/ft2_scopedraw.h
@@ -3,6 +3,6 @@
#include <stdint.h>
#include "ft2_scopes.h"
-typedef void (*scopeDrawRoutine)(scope_t *, uint32_t, uint32_t, uint32_t);
+typedef void (*scopeDrawRoutine)(const scope_t *, uint32_t, uint32_t, uint32_t);
extern const scopeDrawRoutine scopeDrawRoutineTable[12]; // ft2_scopedraw.c
--- a/src/ft2_scopes.c
+++ b/src/ft2_scopes.c
@@ -41,8 +41,9 @@
} scopeState_t;
static volatile bool scopesUpdatingFlag, scopesDisplayingFlag;
-static uint32_t scopeTimeLen, scopeTimeLenFrac;
-static uint64_t timeNext64, timeNext64Frac;
+static int32_t oldPeriod;
+static uint32_t oldDFrq, scopeTimeLen, scopeTimeLenFrac;
+static uint64_t oldSFrq, timeNext64, timeNext64Frac;
static volatile scope_t scope[MAX_VOICES];
static SDL_Thread *scopeThread;
static uint8_t *scopeMuteBMP_Ptrs[16];
@@ -49,6 +50,13 @@
lastChInstr_t lastChInstr[MAX_VOICES]; // global
+void resetCachedScopeVars(void)
+{
+ oldPeriod = -1;
+ oldSFrq = 0;
+ oldDFrq = 0;
+}
+
int32_t getSamplePosition(uint8_t ch)
{
volatile bool active, sample16Bit;
@@ -202,7 +210,7 @@
void refreshScopes(void)
{
- for (int16_t i = 0; i < MAX_VOICES; i++)
+ for (int32_t i = 0; i < MAX_VOICES; i++)
scope[i].wasCleared = false;
}
@@ -292,7 +300,7 @@
if (mouse.x >= x && mouse.x < x+scopeLens[i])
break;
- x += scopeLens[i] + 3;
+ x += scopeLens[i]+3;
}
if (i == chansPerRow)
@@ -309,7 +317,7 @@
return false;
}
-static void scopeTrigger(int32_t ch, sampleTyp *s, int32_t playOffset)
+static void scopeTrigger(int32_t ch, const sampleTyp *s, int32_t playOffset)
{
bool sampleIs16Bit;
uint8_t loopType;
@@ -387,15 +395,13 @@
static void updateScopes(void)
{
int32_t loopOverflowVal;
- volatile scope_t *sc;
- scope_t tempState;
scopesUpdatingFlag = true;
- sc = scope;
+ volatile scope_t *sc = scope;
for (int32_t i = 0; i < song.antChn; i++, sc++)
{
- tempState = *sc; // cache it
+ scope_t tempState = *sc; // cache it
if (!tempState.active)
continue; // scope is not active, no need
@@ -402,17 +408,15 @@
// scope position update
tempState.SPosDec += tempState.SFrq;
-
const uint32_t posAdd = tempState.SPosDec >> SCOPE_FRAC_BITS;
+ tempState.SPosDec &= SCOPE_FRAC_MASK;
+
if (tempState.backwards)
tempState.SPos -= posAdd;
else
tempState.SPos += posAdd;
- tempState.SPosDec &= SCOPE_FRAC_MASK;
-
// handle loop wrapping or sample end
-
if (tempState.backwards && tempState.SPos < tempState.SRepS) // sampling backwards (definitely pingpong loop)
{
tempState.backwards = false; // change direction to forwards
@@ -460,9 +464,7 @@
const uint16_t *scopeLens;
uint16_t scopeXOffs, scopeYOffs, scopeDrawLen;
int32_t chansPerRow;
- volatile scope_t *sc;
- scope_t s;
-
+
scopesDisplayingFlag = true;
chansPerRow = (uint32_t)song.antChn >> 1;
@@ -482,18 +484,16 @@
}
scopeDrawLen = scopeLens[i];
-
if (!editor.chnMode[i]) // scope muted (mute graphics blit()'ed elsewhere)
{
- scopeXOffs += scopeDrawLen + 3; // align x to next scope
+ scopeXOffs += scopeDrawLen+3; // align x to next scope
continue;
}
- s = scope[i]; // cache scope to lower thread race condition issues
+ const scope_t s = scope[i]; // cache scope to lower thread race condition issues
if (s.active && s.SVol > 0 && !audio.locked)
{
// scope is active
-
scope[i].wasCleared = false;
// clear scope background
@@ -506,8 +506,7 @@
else
{
// scope is inactive
-
- sc = &scope[i];
+ volatile scope_t *sc = &scope[i];
if (!sc->wasCleared)
{
// clear scope background
@@ -528,7 +527,7 @@
if (config.multiRecChn[i])
blit(scopeXOffs + 1, scopeYOffs + 31, bmp.scopeRec, 13, 4);
- scopeXOffs += scopeDrawLen + 3; // align x to next scope
+ scopeXOffs += scopeDrawLen+3; // align x to next scope
}
scopesDisplayingFlag = false;
@@ -537,7 +536,7 @@
void drawScopeFramework(void)
{
drawFramework(0, 92, 291, 81, FRAMEWORK_TYPE1);
- for (uint8_t i = 0; i < song.antChn; i++)
+ for (int32_t i = 0; i < song.antChn; i++)
redrawScope(i);
}
@@ -544,11 +543,9 @@
void handleScopesFromChQueue(chSyncData_t *chSyncData, uint8_t *scopeUpdateStatus)
{
uint8_t status;
- syncedChannel_t *ch;
- volatile scope_t *sc;
- sc = scope;
- ch = chSyncData->channels;
+ volatile scope_t *sc = scope;
+ syncedChannel_t *ch = chSyncData->channels;
for (int32_t i = 0; i < song.antChn; i++, sc++, ch++)
{
status = scopeUpdateStatus[i];
@@ -558,8 +555,23 @@
if (status & IS_Period)
{
- sc->SFrq = getScopeFrequenceValue(ch->finalPeriod);
- sc->DFrq = (uint32_t)(sc->SFrq >> (SCOPE_FRAC_BITS - 10)); // amount of samples to skip after drawing a pixel
+ // use cached values if possible
+
+ const uint16_t period = ch->finalPeriod;
+ if (period != oldPeriod)
+ {
+ oldPeriod = period;
+ const double dHz = period2Hz(period);
+
+ const double dScopeRateFactor = SCOPE_FRAC_SCALE / (double)SCOPE_HZ;
+ oldSFrq = (int64_t)((dHz * dScopeRateFactor) + 0.5); // Hz -> rounded fixed-point delta
+
+ const double dRelativeHz = dHz * (1.0 / (8363.0 / 2.0));
+ oldDFrq = (int32_t)((dRelativeHz * SCOPE_DRAW_FRAC_SCALE) + 0.5); // Hz -> rounded fixed-point draw delta
+ }
+
+ sc->SFrq = oldSFrq;
+ sc->DFrq = oldDFrq;
}
if (status & IS_NyTon)
--- a/src/ft2_scopes.h
+++ b/src/ft2_scopes.h
@@ -9,6 +9,12 @@
#define SCOPE_FRAC_SCALE (1ULL << SCOPE_FRAC_BITS)
#define SCOPE_FRAC_MASK (SCOPE_FRAC_SCALE-1)
+// just about max safe bits, don't mess with it!
+#define SCOPE_DRAW_FRAC_BITS 20
+#define SCOPE_DRAW_FRAC_SCALE (1UL << SCOPE_DRAW_FRAC_BITS)
+#define SCOPE_DRAW_FRAC_MASK (SCOPE_DRAW_FRAC_SCALE-1)
+
+void resetCachedScopeVars(void);
int32_t getSamplePosition(uint8_t ch);
void stopAllScopes(void);
void refreshScopes(void);
--- a/vs2019_project/ft2-clone/ft2-clone.vcxproj.filters
+++ b/vs2019_project/ft2-clone/ft2-clone.vcxproj.filters
@@ -22,7 +22,6 @@
<ClCompile Include="..\..\src\ft2_unicode.c" />
<ClCompile Include="..\..\src\ft2_midi.c" />
<ClCompile Include="..\..\src\ft2_wav_renderer.c" />
- <ClCompile Include="..\..\src\ft2_about.c" />
<ClCompile Include="..\..\src\ft2_trim.c" />
<ClCompile Include="..\..\src\ft2_checkboxes.c" />
<ClCompile Include="..\..\src\ft2_pushbuttons.c" />
@@ -77,6 +76,7 @@
<Filter>graphics</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ft2_structs.c" />
+ <ClCompile Include="..\..\src\ft2_about.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\src\ft2_audio.h">