ref: 01ad3966b1dd62e332cb5e8187a876e54db7577c
parent: 0d68fc457a2d78765f9173876e52b1da84e4db6e
author: Olav Sørensen <olav.sorensen@live.no>
date: Thu Aug 12 15:12:17 EDT 2021
Fixed: Paula/Scope delta cache didn't work properly
--- a/src/pt2_audio.c
+++ b/src/pt2_audio.c
@@ -43,10 +43,10 @@
static volatile uint8_t filterModel;
static int8_t defStereoSep;
static bool amigaPanFlag;
-static int32_t oldPeriod = -1, randSeed = INITIAL_DITHER_SEED;
+static int32_t randSeed = INITIAL_DITHER_SEED;
static uint32_t audLatencyPerfValInt, audLatencyPerfValFrac;
static uint64_t tickTime64, tickTime64Frac;
-static double *dMixBufferL, *dMixBufferR, *dMixBufferLUnaligned, *dMixBufferRUnaligned, dOldVoiceDelta, dOldVoiceDeltaMul;
+static double *dMixBufferL, *dMixBufferR, *dMixBufferLUnaligned, *dMixBufferRUnaligned;
static double dPrngStateL, dPrngStateR, dLState[2], dRState[2];
static blep_t blep[AMIGA_VOICES], blepVol[AMIGA_VOICES];
static rcFilter_t filterLoA500, filterHiA500, filterHiA1200;
@@ -203,7 +203,13 @@
void resetCachedMixerPeriod(void)
{
- oldPeriod = -1;
+ paulaVoice_t *v = paula;
+ for (int32_t i = 0; i < AMIGA_VOICES; i++, v++)
+ {
+ v->oldPeriod = -1;
+ v->dOldVoiceDelta = 0.0;
+ v->dOldVoiceDeltaMul = 1.0;
+ }
}
// the following routines are only called from the mixer thread.
@@ -229,10 +235,10 @@
scopeSetPeriod(ch, realPeriod);
}
- // if the new period was the same as the previous period, use cached deltas
- if (realPeriod != oldPeriod)
+ // if the new period was the same as the previous period, use cached delta
+ if (realPeriod != v->oldPeriod)
{
- oldPeriod = realPeriod;
+ v->oldPeriod = realPeriod;
// this period is not cached, calculate mixer deltas
@@ -244,17 +250,23 @@
else
dPeriodToDeltaDiv = audio.dPeriodToDeltaDiv;
- // cache these
- dOldVoiceDelta = dPeriodToDeltaDiv / realPeriod;
- dOldVoiceDeltaMul = 1.0 / dOldVoiceDelta; // for BLEP synthesis
+ v->dOldVoiceDelta = dPeriodToDeltaDiv / realPeriod;
+
+ // for BLEP synthesis (prevents division in inner mix loop)
+ v->dOldVoiceDeltaMul = 1.0 / v->dOldVoiceDelta;
}
- v->dDelta = dOldVoiceDelta;
+ v->dDelta = v->dOldVoiceDelta;
// for BLEP synthesis
- v->dDeltaMul = dOldVoiceDeltaMul;
- if (v->dLastDelta == 0.0) v->dLastDelta = v->dDelta;
- if (v->dLastDeltaMul == 0.0) v->dLastDeltaMul = v->dDeltaMul;
+ v->dDeltaMul = v->dOldVoiceDeltaMul;
+
+ if (v->dLastDelta == 0.0)
+ v->dLastDelta = v->dDelta;
+
+ if (v->dLastDeltaMul == 0.0)
+ v->dLastDeltaMul = v->dDeltaMul;
+ // ------------------
}
void paulaSetVolume(int32_t ch, uint16_t vol)
@@ -263,10 +275,11 @@
int32_t realVol = vol;
- // confirmed behavior on real Amiga
+ // this is what WinUAE does, so I assume it's what Paula does too
realVol &= 127;
if (realVol > 64)
realVol = 64;
+ // ----------------
v->dVolume = realVol * (1.0 / 64.0);
@@ -283,6 +296,8 @@
void paulaSetLength(int32_t ch, uint16_t len)
{
+ paulaVoice_t *v = &paula[ch];
+
int32_t realLength = len;
if (realLength == 0)
{
@@ -298,9 +313,9 @@
realLength <<= 1; // we work with bytes, not words
- paula[ch].newLength = realLength;
+ v->newLength = realLength;
if (editor.songPlaying)
- paula[ch].syncFlags |= SET_SCOPE_LENGTH;
+ v->syncFlags |= SET_SCOPE_LENGTH;
else
scope[ch].newLength = realLength;
}
@@ -307,12 +322,14 @@
void paulaSetData(int32_t ch, const int8_t *src)
{
+ paulaVoice_t *v = &paula[ch];
+
if (src == NULL)
src = &song->sampleData[RESERVED_SAMPLE_OFFSET]; // 128K reserved sample
- paula[ch].newData = src;
+ v->newData = src;
if (editor.songPlaying)
- paula[ch].syncFlags |= SET_SCOPE_DATA;
+ v->syncFlags |= SET_SCOPE_DATA;
else
scope[ch].newData = src;
}
@@ -319,10 +336,12 @@
void paulaStopDMA(int32_t ch)
{
- paula[ch].active = false;
+ paulaVoice_t *v = &paula[ch];
+ v->active = false;
+
if (editor.songPlaying)
- paula[ch].syncFlags |= STOP_SCOPE;
+ v->syncFlags |= STOP_SCOPE;
else
scope[ch].active = false;
}
@@ -329,21 +348,15 @@
void paulaStartDMA(int32_t ch)
{
- const int8_t *dat;
- int32_t length;
- paulaVoice_t *v;
+ paulaVoice_t *v = &paula[ch];
- // trigger voice
-
- v = &paula[ch];
-
- dat = v->newData;
+ const int8_t *dat = v->newData;
if (dat == NULL)
dat = &song->sampleData[RESERVED_SAMPLE_OFFSET]; // 128K reserved sample
- length = v->newLength; // in bytes, not words
- if (length < 2)
- length = 2; // for safety
+ int32_t length = v->newLength; // in bytes, not words
+ if (length == 0)
+ length = 1+65535;
v->dPhase = 0.0;
v->pos = 0;
@@ -359,8 +372,9 @@
}
else
{
- scope[ch].newData = dat;
- scope[ch].newLength = length;
+ scope_t *s = &scope[ch];
+ s->newData = dat;
+ s->newLength = length;
scopeTrigger(ch);
}
}
@@ -976,6 +990,7 @@
calcAudioLatencyVars(audio.audioBufferSize, audio.outputRate);
+ resetCachedMixerPeriod();
resetAudioDownsamplingStates();
audio.resetSyncTickTimeFlag = true;
SDL_PauseAudioDevice(dev, false);
--- a/src/pt2_audio.h
+++ b/src/pt2_audio.h
@@ -31,6 +31,10 @@
int32_t length, newLength, pos;
double dVolume, dDelta, dDeltaMul, dPhase, dLastDelta, dLastDeltaMul, dLastPhase, dPanL, dPanR;
+ // period cache
+ int32_t oldPeriod;
+ double dOldVoiceDelta, dOldVoiceDeltaMul;
+
// used for pt2_sync.c
uint8_t syncFlags;
uint8_t syncVolume;
--- a/src/pt2_scopes.c
+++ b/src/pt2_scopes.c
@@ -15,10 +15,8 @@
// this uses code that is not entirely thread safe, but I have never had any issues so far...
static volatile bool scopesUpdatingFlag, scopesDisplayingFlag;
-static int32_t oldPeriod = -1;
static uint32_t scopeTimeLen, scopeTimeLenFrac;
static uint64_t timeNext64, timeNext64Frac;
-static double dOldScopeDelta;
static SDL_Thread *scopeThread;
scope_t scope[AMIGA_VOICES]; // global
@@ -25,8 +23,12 @@
void resetCachedScopePeriod(void)
{
- oldPeriod = -1;
- dOldScopeDelta = 0.0;
+ scope_t *s = scope;
+ for (int32_t i = 0; i < AMIGA_VOICES; i++, s++)
+ {
+ s->oldPeriod = -1;
+ s->dOldScopeDelta = 0.0;
+ }
}
// this is quite hackish, but fixes sample swapping issues
@@ -95,15 +97,18 @@
void scopeSetPeriod(int32_t ch, int32_t period)
{
- // if the new period was the same as the previous period, use cached deltas
- if (period != oldPeriod)
+ volatile scope_t *s = &scope[ch];
+
+ // if the new period was the same as the previous period, use cached delta
+ if (period != s->oldPeriod)
{
- oldPeriod = period;
+ s->oldPeriod = period;
+
const double dPeriodToScopeDeltaDiv = PAULA_PAL_CLK / (double)SCOPE_HZ;
- dOldScopeDelta = dPeriodToScopeDeltaDiv / period;
+ s->dOldScopeDelta = dPeriodToScopeDeltaDiv / period;
}
- scope[ch].dDelta = dOldScopeDelta;
+ s->dDelta = s->dOldScopeDelta;
}
void scopeTrigger(int32_t ch)
@@ -370,6 +375,8 @@
// fractional part (scaled to 0..2^32-1)
dFrac *= UINT32_MAX+1.0;
scopeTimeLenFrac = (uint32_t)dFrac;
+
+ resetCachedScopePeriod();
scopeThread = SDL_CreateThread(scopeThreadFunc, NULL, NULL);
if (scopeThread == NULL)
--- a/src/pt2_scopes.h
+++ b/src/pt2_scopes.h
@@ -12,6 +12,10 @@
uint8_t volume;
int32_t length, pos;
+ // cache
+ int32_t oldPeriod;
+ double dOldScopeDelta;
+
double dDelta, dPhase;
const int8_t *newData;
int32_t newLength;