shithub: pt2-clone

Download patch

ref: 6e65b8e483985689039b0008236ecb7a85c49fd8
parent: 507f40cc04600763992ee8bf86c3a73a8d9e9770
author: Olav Sørensen <olav.sorensen@live.no>
date: Thu Apr 29 19:00:08 EDT 2021

Use 64-bit fixed-point logic instead of floating-point for samplesPerTick

--- a/src/pt2_audio.c
+++ b/src/pt2_audio.c
@@ -645,7 +645,7 @@
 	int32_t samplesLeft = len >> 2;
 	while (samplesLeft > 0)
 	{
-		if (audio.dTickSampleCounter <= 0.0)
+		if (audio.tickSampleCounter64 <= 0)
 		{
 			// new replayer tick
 
@@ -655,10 +655,10 @@
 				fillVisualsSyncBuffer();
 			}
 
-			audio.dTickSampleCounter += audio.dSamplesPerTick;
+			audio.tickSampleCounter64 += audio.samplesPerTick64;
 		}
 
-		const int32_t remainingTick = (int32_t)ceil(audio.dTickSampleCounter);
+		const int32_t remainingTick = (audio.tickSampleCounter64 + UINT32_MAX) >> 32; // ceil rounding (upwards)
 
 		int32_t samplesToMix = samplesLeft;
 		if (samplesToMix > remainingTick)
@@ -668,7 +668,7 @@
 		streamOut += samplesToMix<<1;
 
 		samplesLeft -= samplesToMix;
-		audio.dTickSampleCounter -= samplesToMix;
+		audio.tickSampleCounter64 -= (int64_t)samplesToMix << 32;
 	}
 
 	(void)userdata;
@@ -852,17 +852,24 @@
 {
 	for (int32_t bpm = 32; bpm <= 255; bpm++)
 	{
-		double dHz;
+		double dBpmHz;
 		
 		if (vblankTimingFlag)
-			dHz = AMIGA_PAL_VBLANK_HZ;
+			dBpmHz = AMIGA_PAL_VBLANK_HZ;
 		else
-			dHz = ciaBpm2Hz(bpm);
+			dBpmHz = ciaBpm2Hz(bpm);
 
-		audio.bpmTable[bpm-32] = audio.outputRate / dHz;
-		audio.bpmTable28kHz[bpm-32] = PAT2SMP_HI_FREQ / dHz; // PAT2SMP hi quality
-		audio.bpmTable22kHz[bpm-32] = PAT2SMP_LO_FREQ / dHz; // PAT2SMP low quality
-		audio.bpmTableMod2Wav[bpm-32] = MOD2WAV_FREQ / dHz; // MOD2WAV
+		const double dSamplesPerTick = audio.outputRate / dBpmHz;
+		const double dSamplesPerTick28kHz = PAT2SMP_HI_FREQ / dBpmHz; // PAT2SMP hi quality
+		const double dSamplesPerTick22kHz = PAT2SMP_LO_FREQ / dBpmHz; // PAT2SMP low quality
+		const double dSamplesPerTickMod2Wav = MOD2WAV_FREQ / dBpmHz; // MOD2WAV
+
+		// convert to rounded 32.32 fixed-point
+		const int32_t i = bpm-32;
+		audio.bpmTable[i] = (int64_t)((dSamplesPerTick * (UINT32_MAX+1.0)) + 0.5);
+		audio.bpmTable28kHz[i] = (int64_t)((dSamplesPerTick28kHz * (UINT32_MAX+1.0)) + 0.5);
+		audio.bpmTable22kHz[i] = (int64_t)((dSamplesPerTick22kHz * (UINT32_MAX+1.0)) + 0.5);
+		audio.bpmTableMod2Wav[i] = (int64_t)((dSamplesPerTickMod2Wav * (UINT32_MAX+1.0)) + 0.5);
 	}
 }
 
@@ -939,9 +946,9 @@
 	updateReplayerTimingMode();
 
 	const int32_t lowestBPM = 32;
-	const int32_t pat2SmpMaxSamples = (int32_t)ceil(audio.bpmTable22kHz[lowestBPM-32]);
-	const int32_t mod2WavMaxSamples = (int32_t)ceil(audio.bpmTableMod2Wav[lowestBPM-32]);
-	const int32_t renderMaxSamples = (int32_t)ceil(audio.bpmTable[lowestBPM-32]);
+	const int32_t pat2SmpMaxSamples = (audio.bpmTable22kHz[lowestBPM-32] + (1LL + 31)) >> 32; // ceil (rounded upwards)
+	const int32_t mod2WavMaxSamples = (audio.bpmTableMod2Wav[lowestBPM-32] + (1LL + 31)) >> 32; // ceil (rounded upwards)
+	const int32_t renderMaxSamples = (audio.bpmTable[lowestBPM-32] + (1LL + 31)) >> 32; // ceil (rounded upwards)
 
 	const int32_t maxSamplesToMix = MAX(pat2SmpMaxSamples, MAX(mod2WavMaxSamples, renderMaxSamples));
 
@@ -964,8 +971,8 @@
 	ledFilterEnabled = false;
 	calculateFilterCoeffs();
 
-	audio.dSamplesPerTick = audio.bpmTable[125-32]; // BPM 125
-	audio.dTickSampleCounter = 0.0;
+	audio.samplesPerTick64 = audio.bpmTable[125-32]; // BPM 125
+	audio.tickSampleCounter64 = 0; // zero tick sample counter so that it will instantly initiate a tick
 
 	calcAudioLatencyVars(audio.audioBufferSize, audio.outputRate);
 
--- a/src/pt2_audio.h
+++ b/src/pt2_audio.h
@@ -9,11 +9,11 @@
 	volatile bool locked, isSampling;
 
 	bool forceMixerOff;
-	double bpmTable[256-32], bpmTable28kHz[256-32], bpmTable22kHz[256-32], bpmTableMod2Wav[256-32];
+	
 	uint32_t outputRate, audioBufferSize;
+	int64_t tickSampleCounter64, samplesPerTick64, samplesPerTick64Tab[256-32];
+	int64_t bpmTable[256-32], bpmTable28kHz[256-32], bpmTable22kHz[256-32], bpmTableMod2Wav[256-32]; // 32.32 fixed-point
 	double dPeriodToDeltaDiv;
-
-	double dSamplesPerTick, dTickSampleCounter;
 
 	// for audio sampling
 	bool rescanAudioDevicesSupported;
--- a/src/pt2_mod2wav.c
+++ b/src/pt2_mod2wav.c
@@ -17,10 +17,11 @@
 
 #define TICKS_PER_RENDER_CHUNK 64
 
-void storeTempVariables(void); // pt_modplayer.c
-bool intMusic(void); // pt_modplayer.c
+// pt_modplayer.c
+void storeTempVariables(void);
+bool intMusic(void);
+// ---------------------
 
-static volatile bool wavRenderingDone;
 static int16_t *mod2WavBuffer;
 
 static void calcMod2WavTotalRows(void);
@@ -38,11 +39,9 @@
 	if (MOD2WAV_FREQ != audio.outputRate)
 		recalcFilterCoeffs(MOD2WAV_FREQ);
 
-	wavRenderingDone = false;
-
 	uint32_t sampleCounter = 0;
 	uint8_t tickCounter = 8;
-	double dTickSampleCounter = 0.0;
+	int64_t tickSampleCounter64 = 0;
 
 	bool renderDone = false;
 	while (!renderDone)
@@ -53,26 +52,24 @@
 		int16_t *ptr16 = mod2WavBuffer;
 		for (uint32_t i = 0; i < TICKS_PER_RENDER_CHUNK; i++)
 		{
-			if (!editor.isWAVRendering || wavRenderingDone || editor.abortMod2Wav || !editor.songPlaying)
+			if (!editor.isWAVRendering || renderDone || editor.abortMod2Wav || !editor.songPlaying)
 			{
 				renderDone = true;
 				break;
 			}
 
-			if (dTickSampleCounter <= 0.0)
+			if (tickSampleCounter64 <= 0) // new replayer tick
 			{
-				// new replayer tick
-
 				if (!intMusic())
-					wavRenderingDone = true;
+					renderDone = true; // this tick is the last tick
 
-				dTickSampleCounter += audio.dSamplesPerTick;
+				tickSampleCounter64 += audio.samplesPerTick64;
 			}
 
-			int32_t remainingTick = (int32_t)ceil(dTickSampleCounter);
+			int32_t remainingTick = (tickSampleCounter64 + UINT32_MAX) >> 32; // ceil (rounded upwards)
 
 			outputAudio(ptr16, remainingTick);
-			dTickSampleCounter -= remainingTick;
+			tickSampleCounter64 -= (int64_t)remainingTick << 32;
 
 			remainingTick *= 2; // stereo
 			samplesInChunk += remainingTick;
@@ -167,7 +164,8 @@
 	}
 
 	const int32_t lowestBPM = 32;
-	const int32_t maxSamplesToMix = (int32_t)ceil(TICKS_PER_RENDER_CHUNK * audio.bpmTableMod2Wav[lowestBPM-32]); // stereo
+	const int64_t maxSamplesToMix64 = audio.bpmTableMod2Wav[lowestBPM-32];
+	const int32_t maxSamplesToMix = ((TICKS_PER_RENDER_CHUNK * maxSamplesToMix64) + (1LL << 31)) >> 32; // ceil (rounded upwards)
 
 	mod2WavBuffer = (int16_t *)malloc(maxSamplesToMix * (2 * sizeof (int16_t)));
 	if (mod2WavBuffer == NULL)
--- a/src/pt2_pat2smp.c
+++ b/src/pt2_pat2smp.c
@@ -52,26 +52,27 @@
 	modSetTempo(song->currBPM, true);
 	editor.pat2SmpPos = 0;
 
-	double dTickSampleCounter = 0.0;
+	int64_t tickSampleCounter64 = 0;
 
 	editor.smpRenderingDone = false;
 	while (!editor.smpRenderingDone && editor.songPlaying)
 	{
-		if (dTickSampleCounter <= 0.0)
+		if (tickSampleCounter64 <= 0) // new replayer tick
 		{
-			// new replayer tick
-
 			if (!intMusic())
-				editor.smpRenderingDone = true;
+				editor.smpRenderingDone = true; // this tick is the last tick
 
-			dTickSampleCounter += audio.dSamplesPerTick;
+			tickSampleCounter64 += audio.samplesPerTick64;
 		}
 
-		const int32_t remainingTick = (int32_t)ceil(dTickSampleCounter);
+		int32_t remainingTick = (tickSampleCounter64 + UINT32_MAX) >> 32; // ceil (rounded upwards)
 		outputAudio(NULL, remainingTick);
-		dTickSampleCounter -= remainingTick;
+		tickSampleCounter64 -= (int64_t)remainingTick << 32;
 	}
 	editor.isSMPRendering = false;
+
+	//const double dSamplesPerTick = audio.samplesPerTick64 / (UINT32_MAX+1.0);
+
 	resetSong();
 
 	int32_t renderLength = editor.pat2SmpPos;
@@ -99,7 +100,7 @@
 	for (int32_t i = 0; i < renderLength; i++)
 	{
 		const int32_t smp = (const int32_t)round(editor.dPat2SmpBuf[i] * dAmp);
-		assert(smp >= -128 && smp <= 127); // shouldn't happen according to dAmp (but just in case)
+		assert(smp >= -128 && smp <= 127); // shouldn't happen according to dAmp
 		smpPtr[i] = (int8_t)smp;
 	}