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;
}