ref: 95112aa96abb49f3cd58750e36897236f834e4b7
parent: 60a3dd36bb82179a0768a4ebeb4f7c632cdd59bf
author: Olav Sørensen <[email protected]>
date: Sun Aug 23 16:16:28 EDT 2020
Pushed v1.30 code - Fixed an off-by-one issue when dithering is enabled in 16-bit audio mode - The WAV renderer now defaults to the same frequency/bitdepth as the ones selected in the "I/O devices" config screen
--- a/src/ft2_audio.c
+++ b/src/ft2_audio.c
@@ -24,10 +24,9 @@
static int8_t pmpCountDiv, pmpChannels = 2;
static uint16_t smpBuffSize;
-static int32_t masterVol, oldAudioFreq, randSeed = INITIAL_DITHER_SEED;
-static int32_t prngStateL, prngStateR;
+static int32_t oldAudioFreq, randSeed = INITIAL_DITHER_SEED;
static uint32_t tickTimeLen, tickTimeLenFrac;
-static double dAudioAmpMul, dPanningTab[256+1];
+static double dAudioNormalizeMul, dPanningTab[256+1], dPrngStateL, dPrngStateR;
static voice_t voice[MAX_VOICES * 2];
static void (*sendAudSamplesFunc)(uint8_t *, uint32_t, uint8_t); // "send mixed samples" routines
@@ -114,27 +113,25 @@
}
resumeAudio();
+
+ setWavRenderFrequency(audio.freq);
+ setWavRenderBitDepth((config.specialFlags & BITDEPTH_32) ? 32 : 16);
return true;
}
-// ampFactor = 1..32, masterVol = 0..256
-void setAudioAmp(int16_t ampFactor, int16_t master, bool bitDepth32Flag)
+// amp = 1..32, masterVol = 0..256
+void setAudioAmp(int16_t amp, int16_t masterVol, bool bitDepth32Flag)
{
- ampFactor = CLAMP(ampFactor, 1, 32);
- master = CLAMP(master, 0, 256);
+ amp = CLAMP(amp, 1, 32);
+ masterVol = CLAMP(masterVol, 0, 256);
- const double dAudioNorm = 1.0 / (1UL << 25); // 2^25 = internal voice mixing range (per voice)
+ const double dAmp = (amp * masterVol) / (32.0 * 256.0);
- if (bitDepth32Flag)
- {
- // 32-bit floating point mixing mode
- dAudioAmpMul = dAudioNorm * (master / 256.0) * (ampFactor / 32.0);
- }
- else
- {
- // 16-bit integer mixing mode
- masterVol = (master * ampFactor) * 512;
- }
+ int32_t normalizeBits = 25; // 2^25 = mixing bits per voice
+ if (!bitDepth32Flag)
+ normalizeBits -= 16-1; // change scale from -1.0..1.0 to signed 16-bit
+
+ dAudioNormalizeMul = dAmp / (1UL << normalizeBits);
}
void setNewAudioFreq(uint32_t freq) // for song to WAV rendering
@@ -410,8 +407,8 @@
void resetAudioDither(void)
{
randSeed = INITIAL_DITHER_SEED;
- prngStateL = 0;
- prngStateR = 0;
+ dPrngStateL = 0.0;
+ dPrngStateR = 0.0;
}
static inline uint32_t random32(void)
@@ -431,12 +428,12 @@
for (uint32_t i = 0; i < sampleBlockLength; i++)
{
// left channel
- out32 = ((int64_t)audio.mixBufferL[i] * masterVol) >> 32;
+ out32 = (int32_t)(audio.mixBufferL[i] * dAudioNormalizeMul);
CLAMP16(out32);
*streamPointer16++ = (int16_t)out32;
// right channel
- out32 = ((int64_t)audio.mixBufferR[i] * masterVol) >> 32;
+ out32 = (int32_t)(audio.mixBufferR[i] * dAudioNormalizeMul);
CLAMP16(out32);
*streamPointer16++ = (int16_t)out32;
}
@@ -452,12 +449,12 @@
for (uint32_t i = 0; i < sampleBlockLength; i++)
{
// left channel
- out32 = ((int64_t)audio.mixBufferL[i] * masterVol) >> 32;
+ out32 = (int32_t)(audio.mixBufferL[i] * dAudioNormalizeMul);
CLAMP16(out32);
*streamPointer16++ = (int16_t)out32;
// right channel
- out32 = ((int64_t)audio.mixBufferR[i] * masterVol) >> 32;
+ out32 = (int32_t)(audio.mixBufferR[i] * dAudioNormalizeMul);
CLAMP16(out32);
*streamPointer16++ = (int16_t)out32;
@@ -469,22 +466,25 @@
static void sendSamples16BitDitherStereo(uint8_t *stream, uint32_t sampleBlockLength, uint8_t numAudioChannels)
{
- int32_t prng, out32;
+ int32_t out32;
+ double dOut, dPrng;
int16_t *streamPointer16 = (int16_t *)stream;
for (uint32_t i = 0; i < sampleBlockLength; i++)
{
// left channel - 1-bit triangular dithering
- prng = random32();
- out32 = ((((int64_t)audio.mixBufferL[i] * masterVol) + prng) - prngStateL) >> 32;
- prngStateL = prng;
+ dPrng = random32() * (0.5 / INT32_MAX); // -0.5..0.5
+ dOut = ((audio.mixBufferL[i] * dAudioNormalizeMul) + dPrng) - dPrngStateL;
+ dPrngStateL = dPrng;
+ out32 = (int32_t)dOut;
CLAMP16(out32);
*streamPointer16++ = (int16_t)out32;
// right channel - 1-bit triangular dithering
- prng = random32();
- out32 = ((((int64_t)audio.mixBufferR[i] * masterVol) + prng) - prngStateR) >> 32;
- prngStateR = prng;
+ dPrng = random32() * (0.5 / INT32_MAX); // -0.5..0.5
+ dOut = ((audio.mixBufferR[i] * dAudioNormalizeMul) + dPrng) - dPrngStateR;
+ dPrngStateR = dPrng;
+ out32 = (int32_t)dOut;
CLAMP16(out32);
*streamPointer16++ = (int16_t)out32;
}
@@ -494,22 +494,25 @@
static void sendSamples16BitDitherMultiChan(uint8_t *stream, uint32_t sampleBlockLength, uint8_t numAudioChannels)
{
- int32_t prng, out32;
+ int32_t out32;
+ double dOut, dPrng;
int16_t *streamPointer16 = (int16_t *)stream;
for (uint32_t i = 0; i < sampleBlockLength; i++)
{
// left channel - 1-bit triangular dithering
- prng = random32();
- out32 = ((((int64_t)audio.mixBufferL[i] * masterVol) + prng) - prngStateL) >> 32;
- prngStateL = prng;
+ dPrng = random32() * (0.5 / INT32_MAX); // -0.5..0.5
+ dOut = ((audio.mixBufferL[i] * dAudioNormalizeMul) + dPrng) - dPrngStateL;
+ dPrngStateL = dPrng;
+ out32 = (int32_t)dOut;
CLAMP16(out32);
*streamPointer16++ = (int16_t)out32;
// right channel - 1-bit triangular dithering
- prng = random32();
- out32 = ((((int64_t)audio.mixBufferR[i] * masterVol) + prng) - prngStateR) >> 32;
- prngStateR = prng;
+ dPrng = random32() * (0.5 / INT32_MAX); // -0.5..0.5
+ dOut = ((audio.mixBufferR[i] * dAudioNormalizeMul) + dPrng) - dPrngStateR;
+ dPrngStateR = dPrng;
+ out32 = (int32_t)dOut;
CLAMP16(out32);
*streamPointer16++ = (int16_t)out32;
@@ -527,12 +530,12 @@
for (uint32_t i = 0; i < sampleBlockLength; i++)
{
// left channel
- dOut = audio.mixBufferL[i] * dAudioAmpMul;
+ dOut = audio.mixBufferL[i] * dAudioNormalizeMul;
dOut = CLAMP(dOut, -1.0, 1.0);
*fStreamPointer32++ = (float)dOut;
// right channel
- dOut = audio.mixBufferR[i] * dAudioAmpMul;
+ dOut = audio.mixBufferR[i] * dAudioNormalizeMul;
dOut = CLAMP(dOut, -1.0, 1.0);
*fStreamPointer32++ = (float)dOut;
}
@@ -548,12 +551,12 @@
for (uint32_t i = 0; i < sampleBlockLength; i++)
{
// left channel
- dOut = audio.mixBufferL[i] * dAudioAmpMul;
+ dOut = audio.mixBufferL[i] * dAudioNormalizeMul;
dOut = CLAMP(dOut, -1.0, 1.0);
*fStreamPointer32++ = (float)dOut;
// right channel
- dOut = audio.mixBufferR[i] * dAudioAmpMul;
+ dOut = audio.mixBufferR[i] * dAudioNormalizeMul;
dOut = CLAMP(dOut, -1.0, 1.0);
*fStreamPointer32++ = (float)dOut;
@@ -1247,6 +1250,9 @@
updateSendAudSamplesRoutine(false);
audio.resetSyncTickTimeFlag = true;
+
+ setWavRenderFrequency(audio.freq);
+ setWavRenderBitDepth((config.specialFlags & BITDEPTH_32) ? 32 : 16);
return true;
}
--- a/src/ft2_audio.h
+++ b/src/ft2_audio.h
@@ -117,7 +117,7 @@
uint64_t getChQueueTimestamp(void);
void calcPanningTable(void);
-void setAudioAmp(int16_t ampFactor, int16_t master, bool bitDepth32Flag);
+void setAudioAmp(int16_t amp, int16_t masterVol, bool bitDepth32Flag);
void setNewAudioFreq(uint32_t freq);
void setBackOldAudioFreq(void);
void setSpeed(uint16_t bpm);
--- a/src/ft2_header.h
+++ b/src/ft2_header.h
@@ -12,7 +12,7 @@
#endif
#include "ft2_replayer.h"
-#define PROG_VER_STR "1.29"
+#define PROG_VER_STR "1.30"
// do NOT change these! It will only mess things up...
--- a/src/ft2_replayer.c
+++ b/src/ft2_replayer.c
@@ -357,8 +357,8 @@
dHz2MixDeltaMul = (double)MIXER_FRAC_SCALE / audioFreq;
- audio.quickVolSizeVal = (int32_t)((audioFreq / 200.0) + 0.5);
- audio.rampQuickVolMul = (int32_t)(((UINT32_MAX + 1.0) / audio.quickVolSizeVal) + 0.5);
+ audio.quickVolSizeVal = (int32_t)((audioFreq / 200.0) + 0.5); // rounded
+ audio.rampQuickVolMul = (int32_t)(((UINT32_MAX + 1.0) / audio.quickVolSizeVal) + 0.5); // rounded
/* Calculate tables to prevent floating point operations on systems that
** might have a slow FPU. This is quite hackish and not really needed,
@@ -373,8 +373,6 @@
{
const double dBpmHz = i / 2.5;
const double dSamplesPerTick = audioFreq / dBpmHz;
- const int32_t samplesPerTick = (int32_t)(dSamplesPerTick + 0.5); // rounded
-
audio.dSpeedValTab[i] = dSamplesPerTick;
// BPM -> Hz -> tick length for performance counter (syncing visuals to audio)
@@ -387,7 +385,8 @@
audio.tickTimeLengthTab[i] = ((uint64_t)timeInt << 32) | (uint32_t)dTimeFrac;
// for calculating volume ramp length for "tick" ramps
- audio.rampSpeedValMulTab[i] = (int32_t)(((UINT32_MAX + 1.0) / samplesPerTick) + 0.5);
+ const int32_t samplesPerTick = (int32_t)(dSamplesPerTick + 0.5); // this has to be rounded
+ audio.rampSpeedValMulTab[i] = (int32_t)(((UINT32_MAX + 1.0) / samplesPerTick) + 0.5); // rounded
}
}
--- a/src/ft2_wav_renderer.c
+++ b/src/ft2_wav_renderer.c
@@ -39,11 +39,7 @@
static char WAV_SysReqText[192];
static uint8_t WDBitDepth = 16, WDStartPos, WDStopPos, *wavRenderBuffer;
static int16_t WDAmp;
-#if defined __amd64__ || defined _WIN64
-static uint32_t WDFrequency = 96000;
-#else
static uint32_t WDFrequency = 48000;
-#endif
static SDL_Thread *thread;
static void updateWavRenderer(void)
@@ -65,6 +61,24 @@
hexOut(237, 144, PAL_FORGRND, WDStartPos, 2);
hexOut(237, 158, PAL_FORGRND, WDStopPos, 2);
+}
+
+void setWavRenderFrequency(int32_t freq)
+{
+ WDFrequency = CLAMP(freq, MIN_WAV_RENDER_FREQ, MAX_WAV_RENDER_FREQ);
+ if (ui.wavRendererShown)
+ updateWavRenderer();
+}
+
+void setWavRenderBitDepth(uint8_t bitDepth)
+{
+ if (bitDepth == 16)
+ WDBitDepth = 16;
+ else if (bitDepth == 32)
+ WDBitDepth = 32;
+
+ if (ui.wavRendererShown)
+ updateWavRenderer();
}
void updateWavRendererSettings(void) // called when changing config.boostLevel
--- a/src/ft2_wav_renderer.h
+++ b/src/ft2_wav_renderer.h
@@ -17,6 +17,8 @@
#define MAX_WAV_RENDER_SAMPLES_PER_TICK (((MAX_WAV_RENDER_FREQ * 5) / 2) / MIN_BPM)
+void setWavRenderFrequency(int32_t freq);
+void setWavRenderBitDepth(uint8_t bitDepth);
void updateWavRendererSettings(void);
void drawWavRenderer(void);
void showWavRenderer(void);