shithub: pt2-clone

Download patch

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;