shithub: ft2-clone

Download patch

ref: fbb9816f4f3ae74ef5db5f11127123f7efbecbd0
parent: 23923f12dcb6b945dc9f54f5556bd4d7278181cd
author: Olav Sørensen <olav.sorensen@live.no>
date: Sun Aug 16 12:03:42 EDT 2020

Pushed v1.28 code

- Bugfix: Don't redraw the instrument switcher when pressing numpad keys while certain screens (about/config/help/nibbles) are open.
- Slightly increased the volume precision in the audio channel mixer. This is most likely not measurable.
- Fixed some errors in the help text
- Small code cleanup

--- a/.gitignore
+++ b/.gitignore
@@ -20,3 +20,4 @@
 *.opendb
 *.db-shm
 *.db-wal
+*.recipe
--- a/src/ft2_audio.c
+++ b/src/ft2_audio.c
@@ -27,7 +27,7 @@
 static int32_t masterVol, oldAudioFreq, randSeed = INITIAL_DITHER_SEED;
 static int32_t prngStateL, prngStateR;
 static uint32_t tickTimeLen, tickTimeLenFrac;
-static float fAudioAmpMul;
+static double dAudioAmpMul, dPanningTab[256+1];
 static voice_t voice[MAX_VOICES * 2];
 static void (*sendAudSamplesFunc)(uint8_t *, uint32_t, uint8_t); // "send mixed samples" routines
 
@@ -123,18 +123,17 @@
 	ampFactor = CLAMP(ampFactor, 1, 32);
 	master = CLAMP(master, 0, 256);
 
-	// voiceVolume = (vol(0..65535) * pan(0..65536)) >> 4
-	const float fAudioNorm = 1.0f / (((65535UL * 65536) / 16) / MAX_VOICES);
+	const double dAudioNorm = 1.0 / (1UL << 25); // 2^25 = internal voice mixing range (per voice)
 
 	if (bitDepth32Flag)
 	{
-		// 32-bit floating point
-		fAudioAmpMul = fAudioNorm * (master / 256.0f) * (ampFactor / 32.0f);
+		// 32-bit floating point mixing mode
+		dAudioAmpMul = dAudioNorm * (master / 256.0) * (ampFactor / 32.0);
 	}
 	else
 	{
-		// 16-bit integer
-		masterVol = master * ampFactor * 2048;
+		// 16-bit integer mixing mode
+		masterVol = (master * ampFactor) * 512;
 	}
 }
 
@@ -190,21 +189,23 @@
 	unlockMixerCallback();
 }
 
+void calcPanningTable(void)
+{
+	// same formula as FT2's panning table (with 0.0..1.0 range)
+	for (int32_t i = 0; i <= 256; i++)
+		dPanningTab[i] = sqrt(i * (1.0 / 256.0));
+}
+
 static void voiceUpdateVolumes(int32_t i, uint8_t status)
 {
-	int32_t volL, volR, destVolL, destVolR;
-	uint32_t vol;
-	voice_t *v, *f;
+	int32_t destVolL, destVolR;
 
-	v = &voice[i];
+	voice_t *v = &voice[i];
 
-	vol = v->SVol; // 0..4096
+	const int32_t volR = (int32_t)((v->SVol * dPanningTab[    v->SPan]) + 0.5);
+	const int32_t volL = (int32_t)((v->SVol * dPanningTab[256-v->SPan]) + 0.5);
 
-	// 0..4096 * 0..65535 = 0..268431360 (about max safe volume range)
-	volR = vol * panningTab[    v->SPan];
-	volL = vol * panningTab[256-v->SPan];
-
-	if (!audio.volumeRampingFlag)
+	if (!audio.volumeRampingFlag) // is volume ramping disabled?
 	{
 		v->SVolIPLen = 0; // reset volume ramp length
 		v->SLVol2 = volL;
@@ -222,7 +223,7 @@
 			// setup "fade out" voice (only if current voice volume>0)
 			if (v->SLVol2 > 0 || v->SRVol2 > 0)
 			{
-				f = &voice[MAX_VOICES + i];
+				voice_t *f = &voice[MAX_VOICES + i];
 
 				*f = *v; // copy voice
 
@@ -231,7 +232,7 @@
 				destVolL = -f->SLVol2;
 				destVolR = -f->SRVol2;
 
-				f->SLVolIP = ((int64_t)destVolL * audio.rampQuickVolMul) >> 32;
+				f->SLVolIP = ((int64_t)destVolL * audio.rampQuickVolMul) >> 32; // x = a / b
 				f->SRVolIP = ((int64_t)destVolR * audio.rampQuickVolMul) >> 32;
 
 				f->isFadeOutVoice = true;
@@ -262,13 +263,13 @@
 			if (status & IS_QuickVol)
 			{
 				v->SVolIPLen = audio.quickVolSizeVal;
-				v->SLVolIP = ((int64_t)destVolL * audio.rampQuickVolMul) >> 32;
+				v->SLVolIP = ((int64_t)destVolL * audio.rampQuickVolMul) >> 32; // x = a / b
 				v->SRVolIP = ((int64_t)destVolR * audio.rampQuickVolMul) >> 32;
 			}
 			else
 			{
 				v->SVolIPLen = audio.samplesPerTick;
-				v->SLVolIP = ((int64_t)destVolL * audio.rampSpeedValMul) >> 32;
+				v->SLVolIP = ((int64_t)destVolL * audio.rampSpeedValMul) >> 32; // x = a / b
 				v->SRVolIP = ((int64_t)destVolR * audio.rampSpeedValMul) >> 32;
 			}
 		}
@@ -424,12 +425,9 @@
 
 static void sendSamples16BitStereo(uint8_t *stream, uint32_t sampleBlockLength, uint8_t numAudioChannels)
 {
-	int16_t *streamPointer16;
 	int32_t out32;
 
-	(void)numAudioChannels;
-
-	streamPointer16 = (int16_t *)stream;
+	int16_t *streamPointer16 = (int16_t *)stream;
 	for (uint32_t i = 0; i < sampleBlockLength; i++)
 	{
 		// left channel
@@ -442,16 +440,16 @@
 		CLAMP16(out32);
 		*streamPointer16++ = (int16_t)out32;
 	}
+
+	(void)numAudioChannels;
 }
 
 static void sendSamples16BitMultiChan(uint8_t *stream, uint32_t sampleBlockLength, uint8_t numAudioChannels)
 {
-	int16_t *streamPointer16;
 	int32_t out32;
-	uint32_t i, j;
 
-	streamPointer16 = (int16_t *)stream;
-	for (i = 0; i < sampleBlockLength; i++)
+	int16_t *streamPointer16 = (int16_t *)stream;
+	for (uint32_t i = 0; i < sampleBlockLength; i++)
 	{
 		// left channel
 		out32 = ((int64_t)audio.mixBufferL[i] * masterVol) >> 32;
@@ -464,7 +462,7 @@
 		*streamPointer16++ = (int16_t)out32;
 
 		// send zeroes to the rest of the channels
-		for (j = 2; j < numAudioChannels; j++)
+		for (uint32_t j = 2; j < numAudioChannels; j++)
 			*streamPointer16++ = 0;
 	}
 }
@@ -471,12 +469,9 @@
 
 static void sendSamples16BitDitherStereo(uint8_t *stream, uint32_t sampleBlockLength, uint8_t numAudioChannels)
 {
-	int16_t *streamPointer16;
 	int32_t prng, out32;
 
-	(void)numAudioChannels;
-
-	streamPointer16 = (int16_t *)stream;
+	int16_t *streamPointer16 = (int16_t *)stream;
 	for (uint32_t i = 0; i < sampleBlockLength; i++)
 	{
 		// left channel - 1-bit triangular dithering
@@ -493,14 +488,15 @@
 		CLAMP16(out32);
 		*streamPointer16++ = (int16_t)out32;
 	}
+
+	(void)numAudioChannels;
 }
 
 static void sendSamples16BitDitherMultiChan(uint8_t *stream, uint32_t sampleBlockLength, uint8_t numAudioChannels)
 {
-	int16_t *streamPointer16;
 	int32_t prng, out32;
 
-	streamPointer16 = (int16_t *)stream;
+	int16_t *streamPointer16 = (int16_t *)stream;
 	for (uint32_t i = 0; i < sampleBlockLength; i++)
 	{
 		// left channel - 1-bit triangular dithering
@@ -523,47 +519,47 @@
 	}
 }
 
-static void sendSamples24BitStereo(uint8_t *stream, uint32_t sampleBlockLength, uint8_t numAudioChannels)
+static void sendSamples32BitStereo(uint8_t *stream, uint32_t sampleBlockLength, uint8_t numAudioChannels)
 {
-	float fOut, *fStreamPointer24;
+	double dOut;
 
-	(void)numAudioChannels;
-
-	fStreamPointer24 = (float *)stream;
+	float *fStreamPointer32 = (float *)stream;
 	for (uint32_t i = 0; i < sampleBlockLength; i++)
 	{
 		// left channel
-		fOut = audio.mixBufferL[i] * fAudioAmpMul;
-		fOut = CLAMP(fOut, -1.0f, 1.0f);
-		*fStreamPointer24++ = fOut;
+		dOut = audio.mixBufferL[i] * dAudioAmpMul;
+		dOut = CLAMP(dOut, -1.0, 1.0);
+		*fStreamPointer32++ = (float)dOut;
 
 		// right channel
-		fOut = audio.mixBufferR[i] * fAudioAmpMul;
-		fOut = CLAMP(fOut, -1.0f, 1.0f);
-		*fStreamPointer24++ = fOut;
+		dOut = audio.mixBufferR[i] * dAudioAmpMul;
+		dOut = CLAMP(dOut, -1.0, 1.0);
+		*fStreamPointer32++ = (float)dOut;
 	}
+
+	(void)numAudioChannels;
 }
 
-static void sendSamples24BitMultiChan(uint8_t *stream, uint32_t sampleBlockLength, uint8_t numAudioChannels)
+static void sendSamples32BitMultiChan(uint8_t *stream, uint32_t sampleBlockLength, uint8_t numAudioChannels)
 {
-	float fOut, *fStreamPointer24;
+	double dOut;
 
-	fStreamPointer24 = (float *)stream;
+	float *fStreamPointer32 = (float *)stream;
 	for (uint32_t i = 0; i < sampleBlockLength; i++)
 	{
 		// left channel
-		fOut = audio.mixBufferL[i] * fAudioAmpMul;
-		fOut = CLAMP(fOut, -1.0f, 1.0f);
-		*fStreamPointer24++ = fOut;
+		dOut = audio.mixBufferL[i] * dAudioAmpMul;
+		dOut = CLAMP(dOut, -1.0, 1.0);
+		*fStreamPointer32++ = (float)dOut;
 
 		// right channel
-		fOut = audio.mixBufferR[i] * fAudioAmpMul;
-		fOut = CLAMP(fOut, -1.0f, 1.0f);
-		*fStreamPointer24++ = fOut;
+		dOut = audio.mixBufferR[i] * dAudioAmpMul;
+		dOut = CLAMP(dOut, -1.0, 1.0);
+		*fStreamPointer32++ = (float)dOut;
 
 		// send zeroes to the rest of the channels
 		for (uint32_t j = 2; j < numAudioChannels; j++)
-			*fStreamPointer24++ = 0.0f;
+			*fStreamPointer32++ = 0.0f;
 	}
 }
 
@@ -636,7 +632,7 @@
 	}
 	else
 	{
-		sendSamples24BitStereo(stream, samplesToMix, 2);
+		sendSamples32BitStereo(stream, samplesToMix, 2);
 	}
 }
 
@@ -1061,9 +1057,9 @@
 	else
 	{
 		if (pmpChannels > 2)
-			sendAudSamplesFunc = sendSamples24BitMultiChan;
+			sendAudSamplesFunc = sendSamples32BitMultiChan;
 		else
-			sendAudSamplesFunc = sendSamples24BitStereo;
+			sendAudSamplesFunc = sendSamples32BitStereo;
 	}
 
 	if (lockMixer)
--- a/src/ft2_audio.h
+++ b/src/ft2_audio.h
@@ -65,9 +65,8 @@
 	const int16_t *SBase16, *SRevBase16;
 	bool active, backwards, isFadeOutVoice;
 	uint8_t mixFuncOffset, SPan, SLoopType;
-	uint16_t SVol;
 	int32_t SLVol1, SRVol1, SLVol2, SRVol2, SLVolIP, SRVolIP;
-	int32_t SPos, SLen, SRepS, SRepL;
+	int32_t SVol, SPos, SLen, SRepS, SRepL;
 	uint32_t SVolIPLen, SFrqRev;
 
 #if defined _WIN64 || defined __amd64__
@@ -116,6 +115,8 @@
 bool chQueuePop(void);
 chSyncData_t *chQueuePeek(void);
 uint64_t getChQueueTimestamp(void);
+
+void calcPanningTable(void);
 void setAudioAmp(int16_t ampFactor, int16_t master, bool bitDepth32Flag);
 void setNewAudioFreq(uint32_t freq);
 void setBackOldAudioFreq(void);
--- a/src/ft2_header.h
+++ b/src/ft2_header.h
@@ -12,7 +12,7 @@
 #endif
 #include "ft2_replayer.h"
 
-#define PROG_VER_STR "1.27"
+#define PROG_VER_STR "1.28"
 
 // do NOT change these! It will only mess things up...
 
--- a/src/ft2_main.c
+++ b/src/ft2_main.c
@@ -440,38 +440,36 @@
 #ifdef _WIN32
 static void disableWasapi(void)
 {
-	const char *audioDriver;
-	int32_t i, numAudioDrivers;
-
 	// disable problematic WASAPI SDL2 audio driver on Windows (causes clicks/pops sometimes...)
 
-	numAudioDrivers = SDL_GetNumAudioDrivers();
-	for (i = 0; i < numAudioDrivers; i++)
+	const int32_t numAudioDrivers = SDL_GetNumAudioDrivers();
+	if (numAudioDrivers <= 1)
+		return;
+
+	// look for directsound and enable it if found
+	for (int32_t i = 0; i < numAudioDrivers; i++)
 	{
-		audioDriver = SDL_GetAudioDriver(i);
+		const char *audioDriver = SDL_GetAudioDriver(i);
 		if (audioDriver != NULL && strcmp("directsound", audioDriver) == 0)
 		{
 			SDL_setenv("SDL_AUDIODRIVER", "directsound", true);
 			audio.rescanAudioDevicesSupported = false;
-			break;
+			return;
 		}
 	}
 
-	if (i == numAudioDrivers)
+	// directsound is not available, try winmm
+	for (int32_t i = 0; i < numAudioDrivers; i++)
 	{
-		// directsound is not available, try winmm
-		for (i = 0; i < numAudioDrivers; i++)
+		const char *audioDriver = SDL_GetAudioDriver(i);
+		if (audioDriver != NULL && strcmp("winmm", audioDriver) == 0)
 		{
-			audioDriver = SDL_GetAudioDriver(i);
-			if (audioDriver != NULL && strcmp("winmm", audioDriver) == 0)
-			{
-				SDL_setenv("SDL_AUDIODRIVER", "winmm", true);
-				audio.rescanAudioDevicesSupported = false;
-				break;
-			}
+			SDL_setenv("SDL_AUDIODRIVER", "winmm", true);
+			audio.rescanAudioDevicesSupported = false;
+			return;
 		}
 	}
 
-	// maybe we didn't find directsound or winmm, let's use wasapi after all then...
+	// we didn't find directsound or winmm, let's use wasapi after all...
 }
 #endif
--- a/src/ft2_pattern_ed.c
+++ b/src/ft2_pattern_ed.c
@@ -2305,6 +2305,9 @@
 	int8_t i;
 	int16_t y;
 
+	if (ui.aboutScreenShown || ui.configScreenShown || ui.helpScreenShown || ui.nibblesShown)
+		return; // don't redraw instrument switcher when it's not shown!
+
 	if (ui.extended) // extended pattern editor
 	{
 		//INSTRUMENTS
--- a/src/ft2_replayer.c
+++ b/src/ft2_replayer.c
@@ -21,13 +21,13 @@
 #include "ft2_tables.h"
 #include "ft2_structs.h"
 
-/* This is a *huge* mess, directly ported from the original FT2 code (and modified).
+/* This is a mess, directly ported from the original FT2 code (with some modifications).
 ** You will experience a lot of headaches if you dig into it...
 ** If something looks to be off, it probably isn't!
 */
 
 // non-FT2 precalced stuff
-static double dPeriod2HzTab[65536], dLogTab[768], dAudioRateFactor;
+static double dPeriod2HzTab[65536], dLogTab[768], d2nRevTab[32], dHz2MixDeltaMul;
 
 static bool bxxOverflow;
 static tonTyp nilPatternLine;
@@ -209,21 +209,28 @@
 	return i+1;
 }
 
-static void calcPeriod2HzTab(void) // called every time you change linear/amiga period mode
+static void calc2nRevTable(void) // for calcPeriod2HzTable()
 {
-	dPeriod2HzTab[0] = 0.0; // in FT2, a period of 0 yields 0Hz
+	d2nRevTab[0] = 1.0;
+	for (int32_t i = 0; i < 32; i++)
+		d2nRevTab[i] = 1.0 / (1UL << i);
+}
 
+static void calcPeriod2HzTable(void) // called every time "linear/amiga frequency" mode is changed
+{
+	dPeriod2HzTab[0] = 0.0; // in FT2, a period of 0 converts to 0Hz
+
 	if (audio.linearFreqTable)
 	{
 		// linear periods
 		for (int32_t i = 1; i < 65536; i++)
 		{
-			const uint16_t invPeriod = (12 * 192 * 4) - (uint16_t)i; // this intentionally overflows uint16_t to be accurate to FT2
+			const uint16_t invPeriod = (12 * 192 * 4) - (uint16_t)i; // this intentionally 16-bit-underflows to be accurate to FT2
 			const int32_t octave = invPeriod / 768;
 			const int32_t period = invPeriod % 768;
-			const int32_t bitshift = (14 - octave) & 0x1F; // 100% accurate to FT2
+			const int32_t invOct = (14 - octave) & 0x1F; // accurate to FT2
 
-			dPeriod2HzTab[i] = dLogTab[period] / (1UL << bitshift);
+			dPeriod2HzTab[i] = dLogTab[period] * d2nRevTab[invOct]; // x = y / 2^invOct
 		}
 	}
 	else
@@ -245,7 +252,7 @@
 	else
 		note2Period = amigaPeriods;
 
-	calcPeriod2HzTab();
+	calcPeriod2HzTable();
 
 	resumeAudio();
 
@@ -348,9 +355,9 @@
 	if (audioFreq == 0)
 		return;
 
-	dAudioRateFactor = (double)MIXER_FRAC_SCALE / audioFreq;
+	dHz2MixDeltaMul = (double)MIXER_FRAC_SCALE / audioFreq;
 
-	audio.quickVolSizeVal = audioFreq / 200; // FT2 truncates here
+	audio.quickVolSizeVal = (int32_t)((audioFreq / 200.0) + 0.5);
 	audio.rampQuickVolMul = (int32_t)(((UINT32_MAX + 1.0) / audio.quickVolSizeVal) + 0.5);
 
 	/* Calculate tables to prevent floating point operations on systems that
@@ -384,7 +391,7 @@
 	}
 }
 
-double period2Hz(uint16_t period)
+double dPeriod2Hz(uint16_t period)
 {
 	return dPeriod2HzTab[period];
 }
@@ -392,12 +399,12 @@
 #if defined _WIN64 || defined __amd64__
 int64_t getMixerDelta(uint16_t period)
 {
-	return (int64_t)((dPeriod2HzTab[period] * dAudioRateFactor) + 0.5); // Hz -> rounded fixed-point mixer delta
+	return (int64_t)((dPeriod2Hz(period) * dHz2MixDeltaMul) + 0.5); // Hz -> rounded fixed-point mixer delta
 }
 #else
 int32_t getMixerDelta(uint16_t period)
 {
-	return (int32_t)((dPeriod2HzTab[period] * dAudioRateFactor) + 0.5); // Hz -> rounded fixed-point mixer delta
+	return (int32_t)((dPeriod2Hz(period) * dHz2MixDeltaMul) + 0.5); // Hz -> rounded fixed-point mixer delta
 }
 #endif
 
@@ -405,7 +412,7 @@
 {
 	finetune >>= 3; // FT2 does this in the replayer internally, so the actual range is -16..15
 
-	const double dRelativeHz = dPeriod2HzTab[period] * (1.0 / (8363.0 / 16.0));
+	const double dRelativeHz = dPeriod2Hz(period) * (1.0 / (8363.0 / 16.0));
 	const double dNote = (log2(dRelativeHz) * 12.0) - (finetune * (1.0 / 16.0));
 
 	const int32_t note = (int32_t)(dNote + 0.5) - relativeNote; // rounded
@@ -1317,27 +1324,28 @@
 				}
 			}
 
-			/* calculate with 16 times more precision than FT2 (vol = 0..4096)
-			** Also, vol envelope range is now 0..16384 instead of being shifted to 0..64
-			*/
+			// original FT2 shifts the vol env. range to 0..64, but we keep its full resolution (0..16384)
 
-			int32_t vol1 = song.globVol * ch->outVol * ch->fadeOutAmp; // 0..64 * 0..64 * 0..32768 = 0..134217728
-			int32_t vol2 = envVal << 3; // 0..16384 * 2^3 = 0..131072
+			// 0..64 * 0..64 * 0..32768 = 0..134217728
+			vol = song.globVol * ch->outVol * ch->fadeOutAmp;
 
-			vol = ((int64_t)vol1 * vol2) >> 32; // 0..4096
+			// ((0..134217728 * 0..16384) + 2^10) / 2^11 = 0..1073741824 (rounded)
+			vol = (uint32_t)((((int64_t)vol * envVal) + (1UL << 10)) >> 11);
 
 			ch->status |= IS_Vol;
 		}
 		else
 		{
-			// calculate with 16 times more precision than FT2 (vol = 0..4096)
-			vol = ((song.globVol * ch->outVol * ch->fadeOutAmp) + (1 << 14)) >> 15; // 0..64 * 0..64 * 0..32768 -> 0..4096 (rounded)
+			// (0..64 * 0..64 * 0..32768) * 2^3 = 0..1073741824
+			vol = (uint32_t)(song.globVol * ch->outVol * ch->fadeOutAmp) << 3;
 		}
 
-		if (vol > 4096)
-			vol = 4096;
+		// original FT2 calculates final volume with a range of 0..256. We do it with a range of 0..1073741824 (why not)
 
-		ch->finalVol = (uint16_t)vol;
+		if (vol > (1UL<<30)) // shouldn't happen, but just in case...
+			vol = (1UL<<30);
+
+		ch->finalVol = vol;
 	}
 	else
 	{
@@ -2727,7 +2735,9 @@
 
 	audio.linearFreqTable = true;
 	note2Period = linearPeriods;
-	calcPeriod2HzTab();
+	calc2nRevTable();
+	calcPeriod2HzTable();
+	calcPanningTable();
 
 	setPos(0, 0, true);
 
--- a/src/ft2_replayer.h
+++ b/src/ft2_replayer.h
@@ -201,9 +201,10 @@
 	uint8_t volKolVol, tonNr, envPPos, eVibPos, envVPos, realVol, oldVol, outVol;
 	uint8_t oldPan, outPan, finalPan;
 	int16_t midiPitch, envVIPValue, envPIPValue;
-	uint16_t finalVol, outPeriod, realPeriod, finalPeriod, tonTyp, wantPeriod, portaSpeed;
+	uint16_t outPeriod, realPeriod, finalPeriod, tonTyp, wantPeriod, portaSpeed;
 	uint16_t envVCnt, envVAmp, envPCnt, envPAmp, eVibAmp, eVibSweep;
 	uint16_t fadeOutAmp, fadeOutSpeed, midiVibDepth;
+	uint32_t finalVol;
 	int32_t smpStartPos;
 	sampleTyp *smpPtr;
 	instrTyp *instrSeg;
@@ -231,8 +232,9 @@
 	bool envSustainActive;
 	int8_t fineTune, relTonNr;
 	uint8_t status, sampleNr, instrNr;
-	uint16_t finalPeriod, finalVol;
+	uint16_t finalPeriod;
 	int32_t smpStartPos;
+	uint32_t finalVol;
 } syncedChannel_t;
 
 void fixSongName(void); // removes spaces from right side of song name
@@ -241,7 +243,7 @@
 void tuneSample(sampleTyp *s, int32_t midCFreq);
 
 void calcReplayerLogTab(void);
-double period2Hz(uint16_t period);
+double dPeriod2Hz(uint16_t period);
 
 #if defined _WIN64 || defined __amd64__
 int64_t getMixerDelta(uint16_t period);
--- a/src/ft2_scopes.c
+++ b/src/ft2_scopes.c
@@ -58,7 +58,7 @@
 
 int32_t getSamplePosition(uint8_t ch)
 {
-	volatile bool active, sample16Bit;
+	volatile bool active, sampleIs16Bit;
 	volatile int32_t pos, len;
 	volatile scope_t *sc;
 
@@ -71,7 +71,7 @@
 	active = sc->active;
 	pos = sc->SPos;
 	len = sc->SLen;
-	sample16Bit = sc->sample16Bit;
+	sampleIs16Bit = sc->sampleIs16Bit;
 
 	if (!active || len == 0)
 		return -1;
@@ -78,7 +78,7 @@
 
 	if (pos >= 0 && pos < len)
 	{
-		if (sample16Bit)
+		if (sampleIs16Bit)
 			pos <<= 1;
 
 		return pos;
@@ -357,7 +357,7 @@
 	else
 		tempState.sampleData8 = (const int8_t *)s->pek;
 
-	tempState.sample16Bit = sampleIs16Bit;
+	tempState.sampleIs16Bit = sampleIs16Bit;
 	tempState.loopType = loopType;
 
 	tempState.backwards = false;
@@ -499,8 +499,8 @@
 			clearRect(scopeXOffs, scopeYOffs, scopeDrawLen, SCOPE_HEIGHT);
 
 			// draw scope
-			bool linedScopes = !!(config.specialFlags & LINED_SCOPES);
-			scopeDrawRoutineTable[(linedScopes * 6) + (s.sample16Bit * 3) + s.loopType](&s, scopeXOffs, scopeLineY, scopeDrawLen);
+			bool linedScopesFlag = !!(config.specialFlags & LINED_SCOPES);
+			scopeDrawRoutineTable[(linedScopesFlag * 6) + (s.sampleIs16Bit * 3) + s.loopType](&s, scopeXOffs, scopeLineY, scopeDrawLen);
 		}
 		else
 		{
@@ -550,7 +550,7 @@
 		status = scopeUpdateStatus[i];
 
 		if (status & IS_Vol)
-			sc->SVol = (int8_t)(((ch->finalVol * SCOPE_HEIGHT) + (1 << 11)) >> 12); // rounded
+			sc->SVol = (int8_t)((((ch->finalVol >> 6) * SCOPE_HEIGHT)) >> 24); // rounded
 
 		if (status & IS_Period)
 		{
@@ -560,7 +560,7 @@
 			if (period != oldPeriod)
 			{
 				oldPeriod = period;
-				const double dHz = period2Hz(period);
+				const double dHz = dPeriod2Hz(period);
 
 				const double dScopeRateFactor = SCOPE_FRAC_SCALE / (double)SCOPE_HZ;
 				oldSFrq = (int64_t)((dHz * dScopeRateFactor) + 0.5); // Hz -> rounded fixed-point delta
--- a/src/ft2_scopes.h
+++ b/src/ft2_scopes.h
@@ -30,7 +30,7 @@
 	const int8_t *sampleData8;
 	const int16_t *sampleData16;
 	int8_t SVol;
-	bool wasCleared, sample16Bit, backwards;
+	bool wasCleared, sampleIs16Bit, backwards;
 	uint8_t loopType;
 	int32_t SRepS, SRepL, SLen, SPos;
 	uint32_t DFrq;
--- a/src/ft2_tables.c
+++ b/src/ft2_tables.c
@@ -317,31 +317,6 @@
 };
 
 /* ----------------------------------------------------------------------- */
-/*                           AUDIO MIXER TABLES                            */
-/* ----------------------------------------------------------------------- */
-
-const uint16_t panningTab[257] = // bit-exact to FT2 table (except last value 65536 -> 65535)
-{
-	    0, 4096, 5793, 7094, 8192, 9159,10033,10837,11585,12288,12953,13585,14189,14768,15326,15864,
-	16384,16888,17378,17854,18318,18770,19212,19644,20066,20480,20886,21283,21674,22058,22435,22806,
-	23170,23530,23884,24232,24576,24915,25249,25580,25905,26227,26545,26859,27170,27477,27780,28081,
-	28378,28672,28963,29251,29537,29819,30099,30377,30652,30924,31194,31462,31727,31991,32252,32511,
-	32768,33023,33276,33527,33776,34024,34270,34514,34756,34996,35235,35472,35708,35942,36175,36406,
-	36636,36864,37091,37316,37540,37763,37985,38205,38424,38642,38858,39073,39287,39500,39712,39923,
-	40132,40341,40548,40755,40960,41164,41368,41570,41771,41972,42171,42369,42567,42763,42959,43154,
-	43348,43541,43733,43925,44115,44305,44494,44682,44869,45056,45242,45427,45611,45795,45977,46160,
-	46341,46522,46702,46881,47059,47237,47415,47591,47767,47942,48117,48291,48465,48637,48809,48981,
-	49152,49322,49492,49661,49830,49998,50166,50332,50499,50665,50830,50995,51159,51323,51486,51649,
-	51811,51972,52134,52294,52454,52614,52773,52932,53090,53248,53405,53562,53719,53874,54030,54185,
-	54340,54494,54647,54801,54954,55106,55258,55410,55561,55712,55862,56012,56162,56311,56459,56608,
-	56756,56903,57051,57198,57344,57490,57636,57781,57926,58071,58215,58359,58503,58646,58789,58931,
-	59073,59215,59357,59498,59639,59779,59919,60059,60199,60338,60477,60615,60753,60891,61029,61166,
-	61303,61440,61576,61712,61848,61984,62119,62254,62388,62523,62657,62790,62924,63057,63190,63323,
-	63455,63587,63719,63850,63982,64113,64243,64374,64504,64634,64763,64893,65022,65151,65279,65408,
-	65535
-};
-
-/* ----------------------------------------------------------------------- */
 /*                               GUI TABLES                                */
 /* ----------------------------------------------------------------------- */
 
--- a/src/ft2_tables.h
+++ b/src/ft2_tables.h
@@ -17,8 +17,6 @@
 extern const uint16_t linearPeriods[1936];
 extern const uint16_t amigaPeriods[1936];
 
-extern const uint16_t panningTab[257];
-
 extern const char *dec2StrTab[100];
 extern const char *dec3StrTab[256];
 
--- a/src/helpdata/FT2.HLP
+++ b/src/helpdata/FT2.HLP
@@ -898,7 +898,7 @@
 >@C001Sample editor:
 >@C002
 >@X010- When a looped sample is zoomed out in the sample editor, you could see
->@X021unwanted sample data at the loop-end point. This is because of a kludge
+>@X021unexpected sample data at the loop-end point. This is because of a kludge
 for the resampling interpolation to work faster in the audio mixer, and the
 original FT2 has the same problem. I have made it so that if you zoom in to
 see the individual sample points, it will look like normal.
@@ -907,12 +907,9 @@
 >
 >@C002- Linux: The mouse cursor graphics can be glitchy at times...
 >@X010
->@C002- The "clear sample" shortcut (shift + num-pad Del/',') only works if
->@X021num lock is off. There's no way I can fix this...
->@X010
 >@C001Video:
 >@C002
->@X010- Fullscreen mode can be unbearably slow on a Raspberry Pi (even an RPi 4)
+>@X010- Fullscreen mode can be unbearably slow on a Raspberry Pi (even on RPi 4)
 >
 >@X010- The scopes can mildly flicker depending on the waveform and pitch.
 >@X021This is because their frequency is not clocked to exactly the same rate
--- a/src/helpdata/ft2_help_data.h
+++ b/src/helpdata/ft2_help_data.h
@@ -3,9 +3,9 @@
 
 #include <stdint.h>
 
-#define HELP_DATA_LEN 27295
+#define HELP_DATA_LEN 27158
 
-const uint8_t helpData[27295] =
+const uint8_t helpData[27158] =
 {
 	0x4C,0x3B,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
@@ -2187,101 +2187,90 @@
 	0x6F,0x75,0x74,0x20,0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x73,
 	0x61,0x6D,0x70,0x6C,0x65,0x20,0x65,0x64,0x69,0x74,0x6F,0x72,
 	0x2C,0x20,0x79,0x6F,0x75,0x20,0x63,0x6F,0x75,0x6C,0x64,0x20,
-	0x73,0x65,0x65,0x4D,0x3E,0x40,0x58,0x30,0x32,0x31,0x75,0x6E,
-	0x77,0x61,0x6E,0x74,0x65,0x64,0x20,0x73,0x61,0x6D,0x70,0x6C,
-	0x65,0x20,0x64,0x61,0x74,0x61,0x20,0x61,0x74,0x20,0x74,0x68,
-	0x65,0x20,0x6C,0x6F,0x6F,0x70,0x2D,0x65,0x6E,0x64,0x20,0x70,
-	0x6F,0x69,0x6E,0x74,0x2E,0x20,0x54,0x68,0x69,0x73,0x20,0x69,
-	0x73,0x20,0x62,0x65,0x63,0x61,0x75,0x73,0x65,0x20,0x6F,0x66,
-	0x20,0x61,0x20,0x6B,0x6C,0x75,0x64,0x67,0x65,0x4B,0x66,0x6F,
-	0x72,0x20,0x74,0x68,0x65,0x20,0x72,0x65,0x73,0x61,0x6D,0x70,
-	0x6C,0x69,0x6E,0x67,0x20,0x69,0x6E,0x74,0x65,0x72,0x70,0x6F,
-	0x6C,0x61,0x74,0x69,0x6F,0x6E,0x20,0x74,0x6F,0x20,0x77,0x6F,
-	0x72,0x6B,0x20,0x66,0x61,0x73,0x74,0x65,0x72,0x20,0x69,0x6E,
-	0x20,0x74,0x68,0x65,0x20,0x61,0x75,0x64,0x69,0x6F,0x20,0x6D,
-	0x69,0x78,0x65,0x72,0x2C,0x20,0x61,0x6E,0x64,0x20,0x74,0x68,
-	0x65,0x4B,0x6F,0x72,0x69,0x67,0x69,0x6E,0x61,0x6C,0x20,0x46,
-	0x54,0x32,0x20,0x68,0x61,0x73,0x20,0x74,0x68,0x65,0x20,0x73,
-	0x61,0x6D,0x65,0x20,0x70,0x72,0x6F,0x62,0x6C,0x65,0x6D,0x2E,
-	0x20,0x49,0x20,0x68,0x61,0x76,0x65,0x20,0x6D,0x61,0x64,0x65,
-	0x20,0x69,0x74,0x20,0x73,0x6F,0x20,0x74,0x68,0x61,0x74,0x20,
-	0x69,0x66,0x20,0x79,0x6F,0x75,0x20,0x7A,0x6F,0x6F,0x6D,0x20,
-	0x69,0x6E,0x20,0x74,0x6F,0x3B,0x73,0x65,0x65,0x20,0x74,0x68,
-	0x65,0x20,0x69,0x6E,0x64,0x69,0x76,0x69,0x64,0x75,0x61,0x6C,
-	0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x70,0x6F,0x69,0x6E,
-	0x74,0x73,0x2C,0x20,0x69,0x74,0x20,0x77,0x69,0x6C,0x6C,0x20,
-	0x6C,0x6F,0x6F,0x6B,0x20,0x6C,0x69,0x6B,0x65,0x20,0x6E,0x6F,
-	0x72,0x6D,0x61,0x6C,0x2E,0x06,0x3E,0x40,0x58,0x30,0x31,0x30,
-	0x17,0x3E,0x40,0x43,0x30,0x30,0x31,0x4D,0x6F,0x75,0x73,0x65,
-	0x20,0x2F,0x20,0x6B,0x65,0x79,0x62,0x6F,0x61,0x72,0x64,0x3A,
-	0x01,0x3E,0x43,0x3E,0x40,0x43,0x30,0x30,0x32,0x2D,0x20,0x4C,
-	0x69,0x6E,0x75,0x78,0x3A,0x20,0x54,0x68,0x65,0x20,0x6D,0x6F,
-	0x75,0x73,0x65,0x20,0x63,0x75,0x72,0x73,0x6F,0x72,0x20,0x67,
-	0x72,0x61,0x70,0x68,0x69,0x63,0x73,0x20,0x63,0x61,0x6E,0x20,
-	0x62,0x65,0x20,0x67,0x6C,0x69,0x74,0x63,0x68,0x79,0x20,0x61,
-	0x74,0x20,0x74,0x69,0x6D,0x65,0x73,0x2E,0x2E,0x2E,0x06,0x3E,
-	0x40,0x58,0x30,0x31,0x30,0x4B,0x3E,0x40,0x43,0x30,0x30,0x32,
-	0x2D,0x20,0x54,0x68,0x65,0x20,0x22,0x63,0x6C,0x65,0x61,0x72,
-	0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x22,0x20,0x73,0x68,0x6F,
-	0x72,0x74,0x63,0x75,0x74,0x20,0x28,0x73,0x68,0x69,0x66,0x74,
-	0x20,0x2B,0x20,0x6E,0x75,0x6D,0x2D,0x70,0x61,0x64,0x20,0x44,
-	0x65,0x6C,0x2F,0x27,0x2C,0x27,0x29,0x20,0x6F,0x6E,0x6C,0x79,
-	0x20,0x77,0x6F,0x72,0x6B,0x73,0x20,0x69,0x66,0x37,0x3E,0x40,
-	0x58,0x30,0x32,0x31,0x6E,0x75,0x6D,0x20,0x6C,0x6F,0x63,0x6B,
-	0x20,0x69,0x73,0x20,0x6F,0x66,0x66,0x2E,0x20,0x54,0x68,0x65,
-	0x72,0x65,0x27,0x73,0x20,0x6E,0x6F,0x20,0x77,0x61,0x79,0x20,
-	0x49,0x20,0x63,0x61,0x6E,0x20,0x66,0x69,0x78,0x20,0x74,0x68,
-	0x69,0x73,0x2E,0x2E,0x2E,0x06,0x3E,0x40,0x58,0x30,0x31,0x30,
-	0x0C,0x3E,0x40,0x43,0x30,0x30,0x31,0x56,0x69,0x64,0x65,0x6F,
-	0x3A,0x06,0x3E,0x40,0x43,0x30,0x30,0x32,0x50,0x3E,0x40,0x58,
-	0x30,0x31,0x30,0x2D,0x20,0x46,0x75,0x6C,0x6C,0x73,0x63,0x72,
-	0x65,0x65,0x6E,0x20,0x6D,0x6F,0x64,0x65,0x20,0x63,0x61,0x6E,
-	0x20,0x62,0x65,0x20,0x75,0x6E,0x62,0x65,0x61,0x72,0x61,0x62,
-	0x6C,0x79,0x20,0x73,0x6C,0x6F,0x77,0x20,0x6F,0x6E,0x20,0x61,
-	0x20,0x52,0x61,0x73,0x70,0x62,0x65,0x72,0x72,0x79,0x20,0x50,
-	0x69,0x20,0x28,0x65,0x76,0x65,0x6E,0x20,0x61,0x6E,0x20,0x52,
-	0x50,0x69,0x20,0x34,0x29,0x01,0x3E,0x4A,0x3E,0x40,0x58,0x30,
-	0x31,0x30,0x2D,0x20,0x54,0x68,0x65,0x20,0x73,0x63,0x6F,0x70,
-	0x65,0x73,0x20,0x63,0x61,0x6E,0x20,0x6D,0x69,0x6C,0x64,0x6C,
-	0x79,0x20,0x66,0x6C,0x69,0x63,0x6B,0x65,0x72,0x20,0x64,0x65,
-	0x70,0x65,0x6E,0x64,0x69,0x6E,0x67,0x20,0x6F,0x6E,0x20,0x74,
-	0x68,0x65,0x20,0x77,0x61,0x76,0x65,0x66,0x6F,0x72,0x6D,0x20,
-	0x61,0x6E,0x64,0x20,0x70,0x69,0x74,0x63,0x68,0x2E,0x4D,0x3E,
-	0x40,0x58,0x30,0x32,0x31,0x54,0x68,0x69,0x73,0x20,0x69,0x73,
-	0x20,0x62,0x65,0x63,0x61,0x75,0x73,0x65,0x20,0x74,0x68,0x65,
-	0x69,0x72,0x20,0x66,0x72,0x65,0x71,0x75,0x65,0x6E,0x63,0x79,
-	0x20,0x69,0x73,0x20,0x6E,0x6F,0x74,0x20,0x63,0x6C,0x6F,0x63,
-	0x6B,0x65,0x64,0x20,0x74,0x6F,0x20,0x65,0x78,0x61,0x63,0x74,
-	0x6C,0x79,0x20,0x74,0x68,0x65,0x20,0x73,0x61,0x6D,0x65,0x20,
-	0x72,0x61,0x74,0x65,0x4D,0x3E,0x61,0x74,0x20,0x77,0x68,0x69,
-	0x63,0x68,0x20,0x74,0x68,0x65,0x20,0x73,0x63,0x6F,0x70,0x65,
-	0x73,0x20,0x61,0x72,0x65,0x20,0x72,0x65,0x6E,0x64,0x65,0x72,
-	0x65,0x64,0x2E,0x20,0x49,0x74,0x27,0x73,0x20,0x63,0x6C,0x6F,
-	0x73,0x65,0x2C,0x20,0x77,0x68,0x69,0x63,0x68,0x20,0x63,0x61,
-	0x75,0x73,0x65,0x73,0x20,0x61,0x20,0x66,0x6C,0x69,0x63,0x6B,
-	0x65,0x72,0x20,0x65,0x66,0x66,0x65,0x63,0x74,0x2E,0x01,0x3E,
-	0x52,0x3E,0x40,0x58,0x30,0x31,0x30,0x2D,0x20,0x4E,0x6F,0x74,
-	0x20,0x61,0x20,0x62,0x75,0x67,0x2C,0x20,0x62,0x75,0x74,0x20,
-	0x69,0x66,0x20,0x79,0x6F,0x75,0x72,0x20,0x6D,0x6F,0x6E,0x69,
-	0x74,0x6F,0x72,0x27,0x73,0x20,0x72,0x65,0x66,0x72,0x65,0x73,
-	0x68,0x20,0x72,0x61,0x74,0x65,0x20,0x69,0x73,0x20,0x6E,0x6F,
-	0x74,0x20,0x73,0x65,0x74,0x20,0x74,0x6F,0x20,0x36,0x30,0x48,
-	0x7A,0x20,0x28,0x6F,0x72,0x20,0x35,0x39,0x48,0x7A,0x29,0x4F,
-	0x3E,0x40,0x58,0x30,0x32,0x31,0x79,0x6F,0x75,0x20,0x6D,0x61,
-	0x79,0x20,0x65,0x78,0x70,0x65,0x72,0x69,0x65,0x6E,0x63,0x65,
-	0x20,0x76,0x69,0x73,0x75,0x61,0x6C,0x20,0x73,0x74,0x75,0x74,
-	0x74,0x65,0x72,0x69,0x6E,0x67,0x20,0x62,0x65,0x63,0x61,0x75,
-	0x73,0x65,0x20,0x56,0x53,0x79,0x6E,0x63,0x20,0x77,0x69,0x6C,
-	0x6C,0x20,0x6E,0x6F,0x74,0x20,0x62,0x65,0x20,0x75,0x73,0x65,
-	0x64,0x20,0x74,0x68,0x65,0x6E,0x2E,0x49,0x49,0x20,0x68,0x69,
-	0x67,0x68,0x6C,0x79,0x20,0x72,0x65,0x63,0x6F,0x6D,0x6D,0x65,
-	0x6E,0x64,0x20,0x72,0x75,0x6E,0x6E,0x69,0x6E,0x67,0x20,0x79,
-	0x6F,0x75,0x72,0x20,0x6D,0x6F,0x6E,0x69,0x74,0x6F,0x72,0x20,
-	0x61,0x74,0x20,0x36,0x30,0x48,0x7A,0x20,0x69,0x66,0x20,0x79,
-	0x6F,0x75,0x27,0x72,0x65,0x20,0x61,0x20,0x68,0x61,0x72,0x64,
-	0x63,0x6F,0x72,0x65,0x20,0x75,0x73,0x65,0x72,0x10,0x6F,0x66,
-	0x20,0x74,0x68,0x69,0x73,0x20,0x70,0x72,0x6F,0x67,0x72,0x61,
-	0x6D,0x2E,0x00,0x03,0x45,0x4E,0x44
+	0x73,0x65,0x65,0x4F,0x3E,0x40,0x58,0x30,0x32,0x31,0x75,0x6E,
+	0x65,0x78,0x70,0x65,0x63,0x74,0x65,0x64,0x20,0x73,0x61,0x6D,
+	0x70,0x6C,0x65,0x20,0x64,0x61,0x74,0x61,0x20,0x61,0x74,0x20,
+	0x74,0x68,0x65,0x20,0x6C,0x6F,0x6F,0x70,0x2D,0x65,0x6E,0x64,
+	0x20,0x70,0x6F,0x69,0x6E,0x74,0x2E,0x20,0x54,0x68,0x69,0x73,
+	0x20,0x69,0x73,0x20,0x62,0x65,0x63,0x61,0x75,0x73,0x65,0x20,
+	0x6F,0x66,0x20,0x61,0x20,0x6B,0x6C,0x75,0x64,0x67,0x65,0x4B,
+	0x66,0x6F,0x72,0x20,0x74,0x68,0x65,0x20,0x72,0x65,0x73,0x61,
+	0x6D,0x70,0x6C,0x69,0x6E,0x67,0x20,0x69,0x6E,0x74,0x65,0x72,
+	0x70,0x6F,0x6C,0x61,0x74,0x69,0x6F,0x6E,0x20,0x74,0x6F,0x20,
+	0x77,0x6F,0x72,0x6B,0x20,0x66,0x61,0x73,0x74,0x65,0x72,0x20,
+	0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x61,0x75,0x64,0x69,0x6F,
+	0x20,0x6D,0x69,0x78,0x65,0x72,0x2C,0x20,0x61,0x6E,0x64,0x20,
+	0x74,0x68,0x65,0x4B,0x6F,0x72,0x69,0x67,0x69,0x6E,0x61,0x6C,
+	0x20,0x46,0x54,0x32,0x20,0x68,0x61,0x73,0x20,0x74,0x68,0x65,
+	0x20,0x73,0x61,0x6D,0x65,0x20,0x70,0x72,0x6F,0x62,0x6C,0x65,
+	0x6D,0x2E,0x20,0x49,0x20,0x68,0x61,0x76,0x65,0x20,0x6D,0x61,
+	0x64,0x65,0x20,0x69,0x74,0x20,0x73,0x6F,0x20,0x74,0x68,0x61,
+	0x74,0x20,0x69,0x66,0x20,0x79,0x6F,0x75,0x20,0x7A,0x6F,0x6F,
+	0x6D,0x20,0x69,0x6E,0x20,0x74,0x6F,0x3B,0x73,0x65,0x65,0x20,
+	0x74,0x68,0x65,0x20,0x69,0x6E,0x64,0x69,0x76,0x69,0x64,0x75,
+	0x61,0x6C,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x70,0x6F,
+	0x69,0x6E,0x74,0x73,0x2C,0x20,0x69,0x74,0x20,0x77,0x69,0x6C,
+	0x6C,0x20,0x6C,0x6F,0x6F,0x6B,0x20,0x6C,0x69,0x6B,0x65,0x20,
+	0x6E,0x6F,0x72,0x6D,0x61,0x6C,0x2E,0x06,0x3E,0x40,0x58,0x30,
+	0x31,0x30,0x17,0x3E,0x40,0x43,0x30,0x30,0x31,0x4D,0x6F,0x75,
+	0x73,0x65,0x20,0x2F,0x20,0x6B,0x65,0x79,0x62,0x6F,0x61,0x72,
+	0x64,0x3A,0x01,0x3E,0x43,0x3E,0x40,0x43,0x30,0x30,0x32,0x2D,
+	0x20,0x4C,0x69,0x6E,0x75,0x78,0x3A,0x20,0x54,0x68,0x65,0x20,
+	0x6D,0x6F,0x75,0x73,0x65,0x20,0x63,0x75,0x72,0x73,0x6F,0x72,
+	0x20,0x67,0x72,0x61,0x70,0x68,0x69,0x63,0x73,0x20,0x63,0x61,
+	0x6E,0x20,0x62,0x65,0x20,0x67,0x6C,0x69,0x74,0x63,0x68,0x79,
+	0x20,0x61,0x74,0x20,0x74,0x69,0x6D,0x65,0x73,0x2E,0x2E,0x2E,
+	0x06,0x3E,0x40,0x58,0x30,0x31,0x30,0x0C,0x3E,0x40,0x43,0x30,
+	0x30,0x31,0x56,0x69,0x64,0x65,0x6F,0x3A,0x06,0x3E,0x40,0x43,
+	0x30,0x30,0x32,0x50,0x3E,0x40,0x58,0x30,0x31,0x30,0x2D,0x20,
+	0x46,0x75,0x6C,0x6C,0x73,0x63,0x72,0x65,0x65,0x6E,0x20,0x6D,
+	0x6F,0x64,0x65,0x20,0x63,0x61,0x6E,0x20,0x62,0x65,0x20,0x75,
+	0x6E,0x62,0x65,0x61,0x72,0x61,0x62,0x6C,0x79,0x20,0x73,0x6C,
+	0x6F,0x77,0x20,0x6F,0x6E,0x20,0x61,0x20,0x52,0x61,0x73,0x70,
+	0x62,0x65,0x72,0x72,0x79,0x20,0x50,0x69,0x20,0x28,0x65,0x76,
+	0x65,0x6E,0x20,0x6F,0x6E,0x20,0x52,0x50,0x69,0x20,0x34,0x29,
+	0x01,0x3E,0x4A,0x3E,0x40,0x58,0x30,0x31,0x30,0x2D,0x20,0x54,
+	0x68,0x65,0x20,0x73,0x63,0x6F,0x70,0x65,0x73,0x20,0x63,0x61,
+	0x6E,0x20,0x6D,0x69,0x6C,0x64,0x6C,0x79,0x20,0x66,0x6C,0x69,
+	0x63,0x6B,0x65,0x72,0x20,0x64,0x65,0x70,0x65,0x6E,0x64,0x69,
+	0x6E,0x67,0x20,0x6F,0x6E,0x20,0x74,0x68,0x65,0x20,0x77,0x61,
+	0x76,0x65,0x66,0x6F,0x72,0x6D,0x20,0x61,0x6E,0x64,0x20,0x70,
+	0x69,0x74,0x63,0x68,0x2E,0x4D,0x3E,0x40,0x58,0x30,0x32,0x31,
+	0x54,0x68,0x69,0x73,0x20,0x69,0x73,0x20,0x62,0x65,0x63,0x61,
+	0x75,0x73,0x65,0x20,0x74,0x68,0x65,0x69,0x72,0x20,0x66,0x72,
+	0x65,0x71,0x75,0x65,0x6E,0x63,0x79,0x20,0x69,0x73,0x20,0x6E,
+	0x6F,0x74,0x20,0x63,0x6C,0x6F,0x63,0x6B,0x65,0x64,0x20,0x74,
+	0x6F,0x20,0x65,0x78,0x61,0x63,0x74,0x6C,0x79,0x20,0x74,0x68,
+	0x65,0x20,0x73,0x61,0x6D,0x65,0x20,0x72,0x61,0x74,0x65,0x4D,
+	0x3E,0x61,0x74,0x20,0x77,0x68,0x69,0x63,0x68,0x20,0x74,0x68,
+	0x65,0x20,0x73,0x63,0x6F,0x70,0x65,0x73,0x20,0x61,0x72,0x65,
+	0x20,0x72,0x65,0x6E,0x64,0x65,0x72,0x65,0x64,0x2E,0x20,0x49,
+	0x74,0x27,0x73,0x20,0x63,0x6C,0x6F,0x73,0x65,0x2C,0x20,0x77,
+	0x68,0x69,0x63,0x68,0x20,0x63,0x61,0x75,0x73,0x65,0x73,0x20,
+	0x61,0x20,0x66,0x6C,0x69,0x63,0x6B,0x65,0x72,0x20,0x65,0x66,
+	0x66,0x65,0x63,0x74,0x2E,0x01,0x3E,0x52,0x3E,0x40,0x58,0x30,
+	0x31,0x30,0x2D,0x20,0x4E,0x6F,0x74,0x20,0x61,0x20,0x62,0x75,
+	0x67,0x2C,0x20,0x62,0x75,0x74,0x20,0x69,0x66,0x20,0x79,0x6F,
+	0x75,0x72,0x20,0x6D,0x6F,0x6E,0x69,0x74,0x6F,0x72,0x27,0x73,
+	0x20,0x72,0x65,0x66,0x72,0x65,0x73,0x68,0x20,0x72,0x61,0x74,
+	0x65,0x20,0x69,0x73,0x20,0x6E,0x6F,0x74,0x20,0x73,0x65,0x74,
+	0x20,0x74,0x6F,0x20,0x36,0x30,0x48,0x7A,0x20,0x28,0x6F,0x72,
+	0x20,0x35,0x39,0x48,0x7A,0x29,0x4F,0x3E,0x40,0x58,0x30,0x32,
+	0x31,0x79,0x6F,0x75,0x20,0x6D,0x61,0x79,0x20,0x65,0x78,0x70,
+	0x65,0x72,0x69,0x65,0x6E,0x63,0x65,0x20,0x76,0x69,0x73,0x75,
+	0x61,0x6C,0x20,0x73,0x74,0x75,0x74,0x74,0x65,0x72,0x69,0x6E,
+	0x67,0x20,0x62,0x65,0x63,0x61,0x75,0x73,0x65,0x20,0x56,0x53,
+	0x79,0x6E,0x63,0x20,0x77,0x69,0x6C,0x6C,0x20,0x6E,0x6F,0x74,
+	0x20,0x62,0x65,0x20,0x75,0x73,0x65,0x64,0x20,0x74,0x68,0x65,
+	0x6E,0x2E,0x49,0x49,0x20,0x68,0x69,0x67,0x68,0x6C,0x79,0x20,
+	0x72,0x65,0x63,0x6F,0x6D,0x6D,0x65,0x6E,0x64,0x20,0x72,0x75,
+	0x6E,0x6E,0x69,0x6E,0x67,0x20,0x79,0x6F,0x75,0x72,0x20,0x6D,
+	0x6F,0x6E,0x69,0x74,0x6F,0x72,0x20,0x61,0x74,0x20,0x36,0x30,
+	0x48,0x7A,0x20,0x69,0x66,0x20,0x79,0x6F,0x75,0x27,0x72,0x65,
+	0x20,0x61,0x20,0x68,0x61,0x72,0x64,0x63,0x6F,0x72,0x65,0x20,
+	0x75,0x73,0x65,0x72,0x10,0x6F,0x66,0x20,0x74,0x68,0x69,0x73,
+	0x20,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x2E,0x00,0x03,0x45,
+	0x4E,0x44
 };
 
 #endif
--- a/src/mixer/ft2_mix_macros.h
+++ b/src/mixer/ft2_mix_macros.h
@@ -128,13 +128,13 @@
 
 #define RENDER_8BIT_SMP \
 	assert(smpPtr >= CDA_LinearAdr && smpPtr < CDA_LinearAdr+v->SLen); \
-	sample = *smpPtr << 20; \
+	sample = *smpPtr << (12+8); \
 	*audioMixL++ += ((int64_t)sample * CDA_LVol) >> 32; \
 	*audioMixR++ += ((int64_t)sample * CDA_RVol) >> 32; \
 
 #define RENDER_8BIT_SMP_MONO \
 	assert(smpPtr >= CDA_LinearAdr && smpPtr < CDA_LinearAdr+v->SLen); \
-	sample = *smpPtr << 20; \
+	sample = *smpPtr << (12+8); \
 	sample = ((int64_t)sample * CDA_LVol) >> 32; \
 	*audioMixL++ += sample; \
 	*audioMixR++ += sample; \