shithub: ft²

Download patch

ref: 824199e016e6ad8747489995fb518d43726ee8d7
parent: 4a95aceff6b745d0dcfc13185c16e682267f7316
author: Olav Sørensen <olav.sorensen@live.no>
date: Fri Mar 24 13:17:45 EDT 2023

Code cleanup + voice volume precision change

--- a/src/ft2_audio.c
+++ b/src/ft2_audio.c
@@ -14,11 +14,9 @@
 #include "ft2_wav_renderer.h"
 #include "ft2_tables.h"
 #include "ft2_structs.h"
-// --------------------------------
 #include "mixer/ft2_mix.h"
 #include "mixer/ft2_center_mix.h"
 #include "mixer/ft2_silence_mix.h"
-// --------------------------------
 
 // hide POSIX warnings
 #ifdef _MSC_VER
@@ -27,14 +25,11 @@
 
 #define INITIAL_DITHER_SEED 0x12345000
 
-static int8_t pmpCountDiv, pmpChannels = 2;
-static uint16_t smpBuffSize;
+static int32_t smpShiftValue;
 static uint32_t oldAudioFreq, tickTimeLenInt, randSeed = INITIAL_DITHER_SEED;
 static uint64_t tickTimeLenFrac;
-static float fAudioNormalizeMul, fPanningTab[256+1];
-static double dAudioNormalizeMul, dPrngStateL, dPrngStateR;
+static double dAudioNormalizeMul, dPanningTab[256+1], dPrngStateL, dPrngStateR;
 static voice_t voice[MAX_CHANNELS * 2];
-static void (*sendAudSamplesFunc)(uint8_t *, uint32_t, uint8_t); // "send mixed samples" routines
 
 // globalized
 audio_t audio;
@@ -121,7 +116,6 @@
 		dAmp *= 32768.0;
 
 	dAudioNormalizeMul = dAmp;
-	fAudioNormalizeMul = (float)dAmp;
 }
 
 void decreaseMasterVol(void)
@@ -206,9 +200,9 @@
 
 void calcPanningTable(void)
 {
-	// same formula as FT2's panning table (with 0.0f..1.0f range)
+	// same formula as FT2's panning table (with 0.0..1.0 range)
 	for (int32_t i = 0; i <= 256; i++)
-		fPanningTab[i] = sqrtf(i / 256.0f);
+		dPanningTab[i] = sqrt(i / 256.0);
 }
 
 static void voiceUpdateVolumes(int32_t i, uint8_t status)
@@ -215,8 +209,8 @@
 {
 	voice_t *v = &voice[i];
 
-	const float fVolumeL = v->fVolume * fPanningTab[256-v->panning];
-	const float fVolumeR = v->fVolume * fPanningTab[    v->panning];
+	const float fVolumeL = (float)(v->dVolume * dPanningTab[256-v->panning]);
+	const float fVolumeR = (float)(v->dVolume * dPanningTab[    v->panning]);
 
 	if (!audio.volumeRampingFlag)
 	{
@@ -244,29 +238,23 @@
 			const float fVolumeLTarget = -f->fVolumeL;
 			const float fVolumeRTarget = -f->fVolumeR;
 
-			f->volumeRampLength = audio.quickVolRampSamples;
-			f->fVolumeLDelta = fVolumeLTarget / (int32_t)f->volumeRampLength;
-			f->fVolumeRDelta = fVolumeRTarget / (int32_t)f->volumeRampLength;
+			f->volumeRampLength = audio.quickVolRampSamples; // 5ms
+			const float fVolumeRampLength = (float)(int32_t)f->volumeRampLength;
 
+			f->fVolumeLDelta = fVolumeLTarget / fVolumeRampLength;
+			f->fVolumeRDelta = fVolumeRTarget / fVolumeRampLength;
+
 			f->isFadeOutVoice = true;
 		}
 
 		// make current voice fade in from zero when it starts
-		v->fVolumeL = 0.0f;
-		v->fVolumeR = 0.0f;
+		v->fVolumeL = v->fVolumeR = 0.0f;
 	}
 
-	// ramp volume changes
-
-	/* FT2 has two internal volume ramping lengths:
-	** IS_QuickVol: 5ms
-	** Normal: The duration of a tick (samplesPerTick)
-	*/
-
 	// if destination volume and current volume is the same (and we have no sample trigger), don't do ramp
 	if (fVolumeL == v->fVolumeL && fVolumeR == v->fVolumeR && !(status & IS_Trigger))
 	{
-		v->volumeRampLength = 0; // there is no volume change
+		v->volumeRampLength = 0;
 	}
 	else
 	{
@@ -273,18 +261,12 @@
 		const float fVolumeLTarget = fVolumeL - v->fVolumeL;
 		const float fVolumeRTarget = fVolumeR - v->fVolumeR;
 
-		if (status & IS_QuickVol)
-		{
-			v->volumeRampLength = audio.quickVolRampSamples;
-			v->fVolumeLDelta = fVolumeLTarget / (int32_t)v->volumeRampLength;
-			v->fVolumeRDelta = fVolumeRTarget / (int32_t)v->volumeRampLength;
-		}
-		else
-		{
-			v->volumeRampLength = audio.samplesPerTickInt;
-			v->fVolumeLDelta = fVolumeLTarget / (int32_t)v->volumeRampLength;
-			v->fVolumeRDelta = fVolumeRTarget / (int32_t)v->volumeRampLength;
-		}
+		// IS_QuickVol = 5ms, otherwise the duration of a tick
+		v->volumeRampLength = (status & IS_QuickVol) ? audio.quickVolRampSamples : audio.samplesPerTickInt;
+		const float fVolumeRampLength = (float)(int32_t)v->volumeRampLength;
+
+		v->fVolumeLDelta = fVolumeLTarget / fVolumeRampLength;
+		v->fVolumeRDelta = fVolumeRTarget / fVolumeRampLength;
 	}
 }
 
@@ -367,9 +349,10 @@
 
 		if (status & IS_Vol)
 		{
-			v->fVolume = ch->fFinalVol;
+			v->dVolume = ch->dFinalVol;
 
-			const int32_t scopeVolume = (int32_t)((SCOPE_HEIGHT * ch->fFinalVol) + 0.5f); // rounded
+			// set scope volume
+			const int32_t scopeVolume = (int32_t)((SCOPE_HEIGHT * ch->dFinalVol) + 0.5); // rounded
 			v->scopeVolume = (uint8_t)scopeVolume;
 		}
 
@@ -435,7 +418,7 @@
 	return (int32_t)randSeed;
 }
 
-static void sendSamples16BitDitherStereo(uint8_t *stream, uint32_t sampleBlockLength, uint8_t numAudioChannels)
+static void sendSamples16BitDitherStereo(uint8_t *stream, uint32_t sampleBlockLength)
 {
 	int32_t out32;
 	double dOut, dPrng;
@@ -465,94 +448,30 @@
 		audio.fMixBufferL[i] = 0.0f;
 		audio.fMixBufferR[i] = 0.0f;
 	}
-
-	(void)numAudioChannels;
 }
 
-static void sendSamples16BitDitherMultiChan(uint8_t *stream, uint32_t sampleBlockLength, uint8_t numAudioChannels)
+static void sendSamples32BitStereo(uint8_t *stream, uint32_t sampleBlockLength)
 {
-	int32_t out32;
-	double dOut, dPrng;
-
-	int16_t *streamPointer16 = (int16_t *)stream;
+	double dOut;
+	float *fStreamPointer32 = (float *)stream;
 	for (uint32_t i = 0; i < sampleBlockLength; i++)
 	{
-		// left channel - 1-bit triangular dithering
-		dPrng = random32() * (0.5 / INT32_MAX); // -0.5 .. 0.5
+		// left channel
 		dOut = (double)audio.fMixBufferL[i] * dAudioNormalizeMul;
-		dOut = (dOut + dPrng) - dPrngStateL;
-		dPrngStateL = dPrng;
-		out32 = (int32_t)dOut;
-		CLAMP16(out32);
-		*streamPointer16++ = (int16_t)out32;
+		dOut = CLAMP(dOut, -1.0, 1.0);
+		*fStreamPointer32++ = (float)dOut;
 
-		// right channel - 1-bit triangular dithering
-		dPrng = random32() * (0.5 / INT32_MAX); // -0.5 .. 0.5
+		// right channel
 		dOut = (double)audio.fMixBufferR[i] * dAudioNormalizeMul;
-		dOut = (dOut + dPrng) - dPrngStateR;
-		dPrngStateR = dPrng;
-		out32 = (int32_t)dOut;
-		CLAMP16(out32);
-		*streamPointer16++ = (int16_t)out32;
+		dOut = CLAMP(dOut, -1.0, 1.0);
+		*fStreamPointer32++ = (float)dOut;
 
 		// clear what we read from the mixing buffer
 		audio.fMixBufferL[i] = 0.0f;
 		audio.fMixBufferR[i] = 0.0f;
-
-		// send zeroes to the rest of the channels
-		for (uint32_t j = 2; j < numAudioChannels; j++)
-			*streamPointer16++ = 0;
 	}
 }
 
-static void sendSamples32BitStereo(uint8_t *stream, uint32_t sampleBlockLength, uint8_t numAudioChannels)
-{
-	float fOut, *fStreamPointer32 = (float *)stream;
-	for (uint32_t i = 0; i < sampleBlockLength; i++)
-	{
-		// left channel
-		fOut = audio.fMixBufferL[i] * fAudioNormalizeMul;
-		fOut = CLAMP(fOut, -1.0f, 1.0f);
-		*fStreamPointer32++ = fOut;
-
-		// right channel
-		fOut = audio.fMixBufferR[i] * fAudioNormalizeMul;
-		fOut = CLAMP(fOut, -1.0f, 1.0f);
-		*fStreamPointer32++ = fOut;
-
-		// clear what we read from the mixing buffer
-		audio.fMixBufferL[i] = 0.0f;
-		audio.fMixBufferR[i] = 0.0f;
-	}
-
-	(void)numAudioChannels;
-}
-
-static void sendSamples32BitMultiChan(uint8_t *stream, uint32_t sampleBlockLength, uint8_t numAudioChannels)
-{
-	float fOut, *fStreamPointer32 = (float *)stream;
-	for (uint32_t i = 0; i < sampleBlockLength; i++)
-	{
-		// left channel
-		fOut = audio.fMixBufferL[i] * fAudioNormalizeMul;
-		fOut = CLAMP(fOut, -1.0f, 1.0f);
-		*fStreamPointer32++ = fOut;
-
-		// right channel
-		fOut = audio.fMixBufferR[i] * fAudioNormalizeMul;
-		fOut = CLAMP(fOut, -1.0f, 1.0f);
-		*fStreamPointer32++ = fOut;
-
-		// clear what we read from the mixing buffer
-		audio.fMixBufferL[i] = 0.0f;
-		audio.fMixBufferR[i] = 0.0f;
-
-		// send zeroes to the rest of the channels
-		for (uint32_t j = 2; j < numAudioChannels; j++)
-			*fStreamPointer32++ = 0.0f;
-	}
-}
-
 static void doChannelMixing(int32_t bufferPosition, int32_t samplesToMix)
 {
 	voice_t *v = voice; // normal voices
@@ -598,9 +517,9 @@
 
 	// normalize mix buffer and send to audio stream
 	if (bitDepth == 16)
-		sendSamples16BitDitherStereo(stream, samplesToMix, 2);
+		sendSamples16BitDitherStereo(stream, samplesToMix);
 	else
-		sendSamples32BitStereo(stream, samplesToMix, 2);
+		sendSamples32BitStereo(stream, samplesToMix);
 }
 
 int32_t pattQueueReadSize(void)
@@ -903,7 +822,7 @@
 		c->smpStartPos = s->smpStartPos;
 
 		c->pianoNoteNum = 255; // no piano key
-		if (songPlaying && (c->status & IS_Period) && s->envSustainActive)
+		if (songPlaying && (c->status & IS_Period) && !s->keyOff)
 		{
 			const int32_t note = getPianoKey(s->finalPeriod, s->finetune, s->relativeNote);
 			if (note >= 0 && note <= 95)
@@ -929,7 +848,7 @@
 	if (editor.wavIsRendering)
 		return;
 
-	len /= pmpCountDiv; // bytes -> samples
+	len >>= smpShiftValue; // bytes -> samples
 	if (len <= 0)
 		return;
 
@@ -972,8 +891,10 @@
 		samplesLeft -= samplesToMix;
 	}
 
-	// normalize mix buffer and send to audio stream
-	sendAudSamplesFunc(stream, len, pmpChannels);
+	if (config.specialFlags & BITDEPTH_16)
+		sendSamples16BitDitherStereo(stream, len);
+	else
+		sendSamples32BitStereo(stream, len);
 
 	(void)userdata;
 }
@@ -1007,30 +928,6 @@
 	}
 }
 
-void updateSendAudSamplesRoutine(bool lockMixer)
-{
-	if (lockMixer)
-		lockMixerCallback();
-
-	if (config.specialFlags & BITDEPTH_16)
-	{
-		if (pmpChannels > 2)
-			sendAudSamplesFunc = sendSamples16BitDitherMultiChan;
-		else
-			sendAudSamplesFunc = sendSamples16BitDitherStereo;
-	}
-	else
-	{
-		if (pmpChannels > 2)
-			sendAudSamplesFunc = sendSamples32BitMultiChan;
-		else
-			sendAudSamplesFunc = sendSamples32BitStereo;
-	}
-
-	if (lockMixer)
-		unlockMixerCallback();
-}
-
 static void calcAudioLatencyVars(int32_t audioBufferSize, int32_t audioFreq)
 {
 	double dInt;
@@ -1079,20 +976,16 @@
 
 	audio.wantFreq = config.audioFreq;
 	audio.wantSamples = configAudioBufSize;
-	audio.wantChannels = 2;
 
 	// set up audio device
 	memset(&want, 0, sizeof (want));
-
-	// these three may change after opening a device, but our mixer is dealing with it
 	want.freq = config.audioFreq;
 	want.format = (config.specialFlags & BITDEPTH_32) ? AUDIO_F32 : AUDIO_S16;
 	want.channels = 2;
-	// -------------------------------------------------------------------------------
 	want.callback = audioCallback;
 	want.samples  = configAudioBufSize;
 
-	audio.dev = SDL_OpenAudioDevice(audio.currOutputDevice, 0, &want, &have, SDL_AUDIO_ALLOW_ANY_CHANGE); // prevent SDL2 from resampling
+	audio.dev = SDL_OpenAudioDevice(audio.currOutputDevice, 0, &want, &have, SDL_AUDIO_ALLOW_ANY_CHANGE);
 	if (audio.dev == 0)
 	{
 		if (showErrorMsg)
@@ -1105,18 +998,26 @@
 	if (have.format != AUDIO_S16 && have.format != AUDIO_F32)
 	{
 		if (showErrorMsg)
-			showErrorMsgBox("Couldn't open audio device:\nThe program doesn't support an SDL_AudioFormat of '%d' (not 16-bit or 32-bit float).",
-				(uint32_t)have.format);
+			showErrorMsgBox("Couldn't open audio device:\nThis program only supports 16-bit or 32-bit float audio streams. Sorry!");
 
 		closeAudio();
 		return false;
 	}
 
-	// test if the received audio rate is compatible
+	// test if the received audio stream is compatible
 
+	if (have.channels != 2)
+	{
+		if (showErrorMsg)
+			showErrorMsgBox("Couldn't open audio device:\nThis program only supports stereo audio streams. Sorry!");
+
+		closeAudio();
+		return false;
+	}
+
 #if CPU_64BIT
 	if (have.freq != 44100 && have.freq != 48000 && have.freq != 96000 && have.freq != 192000)
-#else
+#else // 32-bit CPUs only support .16fp resampling precision. Not sensible with high rates.
 	if (have.freq != 44100 && have.freq != 48000)
 #endif
 	{
@@ -1151,19 +1052,11 @@
 
 	audio.haveFreq = have.freq;
 	audio.haveSamples = have.samples;
-	audio.haveChannels = have.channels;
+	config.audioFreq = audio.freq = have.freq;
 
-	// set a few variables
-
-	config.audioFreq = have.freq;
-	audio.freq = have.freq;
-	smpBuffSize = have.samples;
-
 	calcAudioLatencyVars(have.samples, have.freq);
+	smpShiftValue = (newBitDepth == 16) ? 2 : 3;
 
-	pmpChannels = have.channels;
-	pmpCountDiv = pmpChannels * ((newBitDepth == 16) ? sizeof (int16_t) : sizeof (float));
-
 	// make a copy of the new known working audio settings
 
 	audio.lastWorkingAudioFreq = config.audioFreq;
@@ -1183,8 +1076,8 @@
 
 	stopAllScopes();
 
-	audio.tickSampleCounter = 0; // zero tick sample counter so that it will instantly initiate a tick
-	audio.tickSampleCounterFrac = 0;
+	// zero tick sample counter so that it will instantly initiate a tick
+	audio.tickSampleCounterFrac  = audio.tickSampleCounter = 0;
 
 	calcReplayerVars(audio.freq);
 
@@ -1193,9 +1086,7 @@
 
 	setMixerBPM(song.BPM); // this is important
 
-	updateSendAudSamplesRoutine(false);
 	audio.resetSyncTickTimeFlag = true;
-
 	return true;
 }
 
--- a/src/ft2_audio.h
+++ b/src/ft2_audio.h
@@ -59,7 +59,7 @@
 	double dHz2MixDeltaMul, dAudioLatencyMs;
 
 	SDL_AudioDeviceID dev;
-	uint32_t wantFreq, haveFreq, wantSamples, haveSamples, wantChannels, haveChannels;
+	uint32_t wantFreq, haveFreq, wantSamples, haveSamples;
 } audio_t;
 
 typedef struct
@@ -78,7 +78,8 @@
 	const int16_t *leftEdgeTaps16;
 
 	const float *fSincLUT;
-	float fVolume, fVolumeL, fVolumeR, fVolumeLDelta, fVolumeRDelta, fVolumeLTarget, fVolumeRTarget;
+	double dVolume;
+	float fVolumeL, fVolumeR, fVolumeLDelta, fVolumeRDelta, fVolumeLTarget, fVolumeRTarget;
 } voice_t;
 
 #ifdef _MSC_VER
@@ -153,7 +154,6 @@
 void unlockAudio(void);
 void lockMixerCallback(void);
 void unlockMixerCallback(void);
-void updateSendAudSamplesRoutine(bool lockMixer);
 void resetRampVolumes(void);
 void updateVoices(void);
 void mixReplayerTickToBuffer(uint32_t samplesToMix, uint8_t *stream, uint8_t bitDepth);
--- a/src/ft2_header.h
+++ b/src/ft2_header.h
@@ -12,7 +12,7 @@
 #endif
 #include "ft2_replayer.h"
 
-#define PROG_VER_STR "1.65"
+#define PROG_VER_STR "1.66"
 
 // do NOT change these! It will only mess things up...
 
--- a/src/ft2_inst_ed.c
+++ b/src/ft2_inst_ed.c
@@ -131,9 +131,9 @@
 	ins->midiBend = CLAMP(ins->midiBend, 0, 36);
 
 	if (ins->midiChannel > 15) ins->midiChannel = 15;
-	if (ins->vibDepth > 0x0F) ins->vibDepth = 0x0F;
-	if (ins->vibRate > 0x3F) ins->vibRate = 0x3F;
-	if (ins->vibType > 3) ins->vibType = 0;
+	if (ins->autoVibDepth > 0x0F) ins->autoVibDepth = 0x0F;
+	if (ins->autoVibRate > 0x3F) ins->autoVibRate = 0x3F;
+	if (ins->autoVibType > 3) ins->autoVibType = 0;
 
 	for (int32_t i = 0; i < 96; i++)
 	{
@@ -537,17 +537,17 @@
 
 static void drawVibSpeed(void)
 {
-	hexOutBg(505, 236, PAL_FORGRND, PAL_DESKTOP, getCurDispInstr()->vibRate, 2);
+	hexOutBg(505, 236, PAL_FORGRND, PAL_DESKTOP, getCurDispInstr()->autoVibRate, 2);
 }
 
 static void drawVibDepth(void)
 {
-	hexOutBg(512, 250, PAL_FORGRND, PAL_DESKTOP, getCurDispInstr()->vibDepth, 1);
+	hexOutBg(512, 250, PAL_FORGRND, PAL_DESKTOP, getCurDispInstr()->autoVibDepth, 1);
 }
 
 static void drawVibSweep(void)
 {
-	hexOutBg(505, 264, PAL_FORGRND, PAL_DESKTOP, getCurDispInstr()->vibSweep, 2);
+	hexOutBg(505, 264, PAL_FORGRND, PAL_DESKTOP, getCurDispInstr()->autoVibSweep, 2);
 }
 
 static void drawRelativeNote(void)
@@ -598,10 +598,10 @@
 	ins->volEnvLoopEnd = (uint8_t)config.stdVolEnvLoopEnd[num];
 	ins->volEnvLength = (uint8_t)config.stdVolEnvLength[num];
 	ins->volEnvFlags = (uint8_t)config.stdVolEnvFlags[num];
-	ins->vibRate = (uint8_t)config.stdVibRate[num];
-	ins->vibDepth = (uint8_t)config.stdVibDepth[num];
-	ins->vibSweep = (uint8_t)config.stdVibSweep[num];
-	ins->vibType = (uint8_t)config.stdVibType[num];
+	ins->autoVibRate = (uint8_t)config.stdVibRate[num];
+	ins->autoVibDepth = (uint8_t)config.stdVibDepth[num];
+	ins->autoVibSweep = (uint8_t)config.stdVibSweep[num];
+	ins->autoVibType = (uint8_t)config.stdVibType[num];
 
 	memcpy(ins->volEnvPoints, config.stdEnvPoints[num][0], sizeof (int16_t) * 12 * 2);
 
@@ -641,10 +641,10 @@
 		config.stdVolEnvLoopEnd[num] = ins->volEnvLoopEnd;
 		config.stdVolEnvLength[num] = ins->volEnvLength;
 		config.stdVolEnvFlags[num] = ins->volEnvFlags;
-		config.stdVibRate[num] = ins->vibRate;
-		config.stdVibDepth[num] = ins->vibDepth;
-		config.stdVibSweep[num] = ins->vibSweep;
-		config.stdVibType[num] = ins->vibType;
+		config.stdVibRate[num] = ins->autoVibRate;
+		config.stdVibDepth[num] = ins->autoVibDepth;
+		config.stdVibSweep[num] = ins->autoVibSweep;
+		config.stdVibType[num] = ins->autoVibType;
 
 		memcpy(config.stdEnvPoints[num][0], ins->volEnvPoints, sizeof (int16_t) * 12 * 2);
 	}
@@ -673,10 +673,10 @@
 		config.stdPanEnvLoopEnd[num] = ins->panEnvLoopEnd;
 		config.stdPanEnvLength[num] = ins->panEnvLength;
 		config.stdPanEnvFlags[num] = ins->panEnvFlags;
-		config.stdVibRate[num] = ins->vibRate;
-		config.stdVibDepth[num] = ins->vibDepth;
-		config.stdVibSweep[num] = ins->vibSweep;
-		config.stdVibType[num] = ins->vibType;
+		config.stdVibRate[num] = ins->autoVibRate;
+		config.stdVibDepth[num] = ins->autoVibDepth;
+		config.stdVibSweep[num] = ins->autoVibSweep;
+		config.stdVibType[num] = ins->autoVibType;
 
 		memcpy(config.stdEnvPoints[num][1], ins->panEnvPoints, sizeof (int16_t) * 12 * 2);
 	}
@@ -1376,9 +1376,9 @@
 		return;
 	}
 
-	if (ins->vibRate != (uint8_t)pos)
+	if (ins->autoVibRate != (uint8_t)pos)
 	{
-		ins->vibRate = (uint8_t)pos;
+		ins->autoVibRate = (uint8_t)pos;
 		drawVibSpeed();
 		setSongModifiedFlag();
 	}
@@ -1393,9 +1393,9 @@
 		return;
 	}
 
-	if (ins->vibDepth != (uint8_t)pos)
+	if (ins->autoVibDepth != (uint8_t)pos)
 	{
-		ins->vibDepth = (uint8_t)pos;
+		ins->autoVibDepth = (uint8_t)pos;
 		drawVibDepth();
 		setSongModifiedFlag();
 	}
@@ -1410,9 +1410,9 @@
 		return;
 	}
 
-	if (ins->vibSweep != (uint8_t)pos)
+	if (ins->autoVibSweep != (uint8_t)pos)
 	{
-		ins->vibSweep = (uint8_t)pos;
+		ins->autoVibSweep = (uint8_t)pos;
 		drawVibSweep();
 		setSongModifiedFlag();
 	}
@@ -1423,7 +1423,7 @@
 	if (instr[editor.curInstr] == NULL || editor.curInstr == 0)
 		return;
 
-	instr[editor.curInstr]->vibType = 0;
+	instr[editor.curInstr]->autoVibType = 0;
 
 	uncheckRadioButtonGroup(RB_GROUP_INST_WAVEFORM);
 	radioButtons[RB_INST_WAVE_SINE].state = RADIOBUTTON_CHECKED;
@@ -1436,7 +1436,7 @@
 	if (instr[editor.curInstr] == NULL || editor.curInstr == 0)
 		return;
 
-	instr[editor.curInstr]->vibType = 1;
+	instr[editor.curInstr]->autoVibType = 1;
 
 	uncheckRadioButtonGroup(RB_GROUP_INST_WAVEFORM);
 	radioButtons[RB_INST_WAVE_SQUARE].state = RADIOBUTTON_CHECKED;
@@ -1449,7 +1449,7 @@
 	if (instr[editor.curInstr] == NULL || editor.curInstr == 0)
 		return;
 
-	instr[editor.curInstr]->vibType = 2;
+	instr[editor.curInstr]->autoVibType = 2;
 
 	uncheckRadioButtonGroup(RB_GROUP_INST_WAVEFORM);
 	radioButtons[RB_INST_WAVE_RAMP_DOWN].state = RADIOBUTTON_CHECKED;
@@ -1462,7 +1462,7 @@
 	if (instr[editor.curInstr] == NULL || editor.curInstr == 0)
 		return;
 
-	instr[editor.curInstr]->vibType = 3;
+	instr[editor.curInstr]->autoVibType = 3;
 
 	uncheckRadioButtonGroup(RB_GROUP_INST_WAVEFORM);
 	radioButtons[RB_INST_WAVE_RAMP_UP].state = RADIOBUTTON_CHECKED;
@@ -1702,7 +1702,7 @@
 			channel_t *c = channel;
 			for (int32_t i = 0; i < song.numChannels; i++, c++)
 			{
-				if (c->instrNum == editor.curInstr && c->envSustainActive)
+				if (c->instrNum == editor.curInstr && !c->keyOff)
 				{
 					const int32_t note = getPianoKey(c->finalPeriod, c->finetune, c->relativeNote);
 					if (note >= 0 && note <= 95)
@@ -2214,14 +2214,14 @@
 	setScrollBarPos(SB_INST_PAN, s->panning, false);
 	setScrollBarPos(SB_INST_FTUNE, 128 + s->finetune, false);
 	setScrollBarPos(SB_INST_FADEOUT, ins->fadeout, false);
-	setScrollBarPos(SB_INST_VIBSPEED, ins->vibRate, false);
-	setScrollBarPos(SB_INST_VIBDEPTH, ins->vibDepth, false);
-	setScrollBarPos(SB_INST_VIBSWEEP, ins->vibSweep, false);
+	setScrollBarPos(SB_INST_VIBSPEED, ins->autoVibRate, false);
+	setScrollBarPos(SB_INST_VIBDEPTH, ins->autoVibDepth, false);
+	setScrollBarPos(SB_INST_VIBSWEEP, ins->autoVibSweep, false);
 
 	// set radio buttons
 
 	uncheckRadioButtonGroup(RB_GROUP_INST_WAVEFORM);
-	switch (ins->vibType)
+	switch (ins->autoVibType)
 	{
 		default:
 		case 0: tmpID = RB_INST_WAVE_SINE;      break;
@@ -2972,10 +2972,10 @@
 	ih.panEnvLoopEnd = ins->panEnvLoopEnd;
 	ih.volEnvFlags = ins->volEnvFlags;
 	ih.panEnvFlags = ins->panEnvFlags;
-	ih.vibType = ins->vibType;
-	ih.vibSweep = ins->vibSweep;
-	ih.vibDepth = ins->vibDepth;
-	ih.vibRate = ins->vibRate;
+	ih.vibType = ins->autoVibType;
+	ih.vibSweep = ins->autoVibSweep;
+	ih.vibDepth = ins->autoVibDepth;
+	ih.vibRate = ins->autoVibRate;
 	ih.fadeout = ins->fadeout;
 	ih.midiOn = ins->midiOn ? 1 : 0;
 	ih.midiChannel = ins->midiChannel;
@@ -3177,10 +3177,10 @@
 			ins->panEnvLoopEnd = xi_h.panEnvLoopEnd;
 			ins->volEnvFlags = xi_h.volEnvFlags;
 			ins->panEnvFlags = xi_h.panEnvFlags;
-			ins->vibType = xi_h.vibType;
-			ins->vibSweep = xi_h.vibSweep;
-			ins->vibDepth = xi_h.vibDepth;
-			ins->vibRate = xi_h.vibRate;
+			ins->autoVibType = xi_h.vibType;
+			ins->autoVibSweep = xi_h.vibSweep;
+			ins->autoVibDepth = xi_h.vibDepth;
+			ins->autoVibRate = xi_h.vibRate;
 			ins->fadeout = xi_h.fadeout;
 			ins->midiOn = (xi_h.midiOn == 1) ? true : false;
 			ins->midiChannel = xi_h.midiChannel;
@@ -3364,9 +3364,9 @@
 
 				if (i == 0)
 				{
-					ins->vibSweep = patWave_h.vibSweep;
-					ins->vibRate = (patWave_h.vibRate + 2) >> 2; // rounded
-					ins->vibDepth = (patWave_h.vibDepth + 1) >> 1; // rounded
+					ins->autoVibSweep = patWave_h.vibSweep;
+					ins->autoVibRate = (patWave_h.vibRate + 2) >> 2; // rounded
+					ins->autoVibDepth = (patWave_h.vibDepth + 1) >> 1; // rounded
 				}
 
 				s = &instr[editor.curInstr]->smp[i];
--- a/src/ft2_module_saver.c
+++ b/src/ft2_module_saver.c
@@ -187,10 +187,10 @@
 			ih.panEnvLoopEnd = ins->panEnvLoopEnd;
 			ih.volEnvFlags = ins->volEnvFlags;
 			ih.panEnvFlags = ins->panEnvFlags;
-			ih.vibType = ins->vibType;
-			ih.vibSweep = ins->vibSweep;
-			ih.vibDepth = ins->vibDepth;
-			ih.vibRate = ins->vibRate;
+			ih.vibType = ins->autoVibType;
+			ih.vibSweep = ins->autoVibSweep;
+			ih.vibDepth = ins->autoVibDepth;
+			ih.vibRate = ins->autoVibRate;
 			ih.fadeout = ins->fadeout;
 			ih.midiOn = ins->midiOn ? 1 : 0;
 			ih.midiChannel = ins->midiChannel;
@@ -363,7 +363,7 @@
 
 		if (j == 1)
 		{
-			if (ins->fadeout != 0 || ins->volEnvFlags != 0 || ins->panEnvFlags != 0 || ins->vibRate > 0 ||
+			if (ins->fadeout != 0 || ins->volEnvFlags != 0 || ins->panEnvFlags != 0 || ins->autoVibRate > 0 ||
 				GET_LOOPTYPE(smp->flags) == LOOP_BIDI || smp->relativeNote != 0 || ins->midiOn)
 			{
 				test = true;
--- a/src/ft2_replayer.c
+++ b/src/ft2_replayer.c
@@ -335,8 +335,6 @@
 	ch->retrigCnt = 0;
 	ch->tremorPos = 0;
 
-	ch->envSustainActive = true;
-
 	instr_t *ins = ch->instrPtr;
 	assert(ins != NULL);
 
@@ -352,22 +350,22 @@
 		ch->panEnvPos = 0;
 	}
 
+	ch->keyOff = false;
 	ch->fadeoutSpeed = ins->fadeout; // FT2 doesn't check if fadeout is more than 4095!
 	ch->fadeoutVol = 32768;
 
-	if (ins->vibDepth > 0)
+	if (ins->autoVibDepth > 0)
 	{
-		ch->eVibPos = 0;
-
-		if (ins->vibSweep > 0)
+		ch->autoVibPos = 0;
+		if (ins->autoVibSweep > 0)
 		{
-			ch->eVibAmp = 0;
-			ch->eVibSweep = (ins->vibDepth << 8) / ins->vibSweep;
+			ch->autoVibAmp = 0;
+			ch->autoVibSweep = (ins->autoVibDepth << 8) / ins->autoVibSweep;
 		}
 		else
 		{
-			ch->eVibAmp = ins->vibDepth << 8;
-			ch->eVibSweep = 0;
+			ch->autoVibAmp = ins->autoVibDepth << 8;
+			ch->autoVibSweep = 0;
 		}
 	}
 }
@@ -374,7 +372,7 @@
 
 void keyOff(channel_t *ch)
 {
-	ch->envSustainActive = false;
+	ch->keyOff = true;
 
 	instr_t *ins = ch->instrPtr;
 	assert(ins != NULL);
@@ -571,8 +569,8 @@
 	ch->fPortaDownSpeed = param;
 
 	ch->realPeriod += param << 2;
-	if ((int16_t)ch->realPeriod > MAX_FRQ-1) // FT2 bug, should've been unsigned comparison
-		ch->realPeriod = MAX_FRQ-1;
+	if ((int16_t)ch->realPeriod >= 32000) // FT2 bug, should've been unsigned comparison
+		ch->realPeriod = 32000-1;
 
 	ch->outPeriod = ch->realPeriod;
 	ch->status |= IS_Period;
@@ -1128,8 +1126,8 @@
 		uint16_t newPeriod = ch->realPeriod;
 
 		newPeriod += param;
-		if ((int16_t)newPeriod > MAX_FRQ-1) // FT2 bug, should've been unsigned comparison
-			newPeriod = MAX_FRQ-1;
+		if ((int16_t)newPeriod >= 32000) // FT2 bug, should've been unsigned comparison
+			newPeriod = 32000-1;
 
 		ch->outPeriod = ch->realPeriod = newPeriod;
 		ch->status |= IS_Period;
@@ -1377,29 +1375,26 @@
 {
 	bool envInterpolateFlag, envDidInterpolate;
 	uint8_t envPos;
-	int16_t autoVibVal;
-	uint16_t autoVibAmp;
 	int32_t envVal;
-	float fVol;
+	double dVol;
 
 	instr_t *ins = ch->instrPtr;
 	assert(ins != NULL);
 
 	// *** FADEOUT ***
-	if (!ch->envSustainActive)
+	if (ch->keyOff)
 	{
-		ch->status |= IS_Vol;
+		ch->status |= IS_Vol; // always update volume, even if fadeout has reached 0
 
-		// unsigned clamp + reset
-		if (ch->fadeoutVol >= ch->fadeoutSpeed)
+		if (ch->fadeoutSpeed > 0) // 0..4095
 		{
 			ch->fadeoutVol -= ch->fadeoutSpeed;
+			if (ch->fadeoutVol <= 0)
+			{
+				ch->fadeoutVol = 0;
+				ch->fadeoutSpeed = 0;
+			}
 		}
-		else
-		{
-			ch->fadeoutVol = 0;
-			ch->fadeoutSpeed = 0;
-		}
 	}
 
 	if (!ch->mute)
@@ -1424,7 +1419,7 @@
 
 					if (envPos == ins->volEnvLoopEnd)
 					{
-						if (!(ins->volEnvFlags & ENV_SUSTAIN) || envPos != ins->volEnvSustain || ch->envSustainActive)
+						if (!(ins->volEnvFlags & ENV_SUSTAIN) || envPos != ins->volEnvSustain || !ch->keyOff)
 						{
 							envPos = ins->volEnvLoopStart;
 							ch->volEnvTick = ins->volEnvPoints[envPos][0];
@@ -1438,7 +1433,7 @@
 				if (envPos < ins->volEnvLength)
 				{
 					envInterpolateFlag = true;
-					if ((ins->volEnvFlags & ENV_SUSTAIN) && ch->envSustainActive)
+					if ((ins->volEnvFlags & ENV_SUSTAIN) && !ch->keyOff)
 					{
 						if (envPos-1 == ins->volEnvSustain)
 						{
@@ -1488,8 +1483,8 @@
 
 			const int32_t vol = song.globalVolume * ch->outVol * ch->fadeoutVol;
 
-			fVol = vol * (1.0f / (64.0f * 64.0f * 32768.0f));
-			fVol *= (int32_t)envVal * (1.0f / (64.0f * (1 << 16))); // volume envelope value
+			dVol = vol * (1.0 / (64.0 * 64.0 * 32768.0));
+			dVol *= envVal * (1.0 / (64.0 * (1 << 16))); // volume envelope value
 
 			ch->status |= IS_Vol; // update mixer vol every tick when vol envelope is enabled
 		}
@@ -1496,17 +1491,18 @@
 		else
 		{
 			const int32_t vol = song.globalVolume * ch->outVol * ch->fadeoutVol;
-			fVol = vol * (1.0f / (64.0f * 64.0f * 32768.0f));
+
+			dVol = vol * (1.0 / (64.0 * 64.0 * 32768.0));
 		}
 
 		/* FT2 doesn't clamp here, but it's actually important if you
 		** move envelope points with the mouse while playing the instrument.
 		*/
-		ch->fFinalVol = CLAMP(fVol, 0.0f, 1.0f);
+		ch->dFinalVol = CLAMP(dVol, 0.0, 1.0);
 	}
 	else
 	{
-		ch->fFinalVol = 0.0f;
+		ch->dFinalVol = 0.0;
 	}
 
 	// *** PANNING ENVELOPE ***
@@ -1528,7 +1524,7 @@
 
 				if (envPos == ins->panEnvLoopEnd)
 				{
-					if (!(ins->panEnvFlags & ENV_SUSTAIN) || envPos != ins->panEnvSustain || ch->envSustainActive)
+					if (!(ins->panEnvFlags & ENV_SUSTAIN) || envPos != ins->panEnvSustain || !ch->keyOff)
 					{
 						envPos = ins->panEnvLoopStart;
 
@@ -1543,7 +1539,7 @@
 			if (envPos < ins->panEnvLength)
 			{
 				envInterpolateFlag = true;
-				if ((ins->panEnvFlags & ENV_SUSTAIN) && ch->envSustainActive)
+				if ((ins->panEnvFlags & ENV_SUSTAIN) && !ch->keyOff)
 				{
 					if (envPos-1 == ins->panEnvSustain)
 					{
@@ -1606,51 +1602,53 @@
 
 	// *** AUTO VIBRATO ***
 #ifdef HAS_MIDI
-	if (ch->midiVibDepth > 0 || ins->vibDepth > 0)
+	if (ch->midiVibDepth > 0 || ins->autoVibDepth > 0)
 #else
 	if (ins->vibDepth > 0)
 #endif
 	{
-		if (ch->eVibSweep > 0)
+		uint16_t autoVibAmp;
+
+		if (ch->autoVibSweep > 0)
 		{
-			autoVibAmp = ch->eVibSweep;
-			if (ch->envSustainActive)
+			autoVibAmp = ch->autoVibSweep;
+			if (!ch->keyOff)
 			{
-				autoVibAmp += ch->eVibAmp;
-				if ((autoVibAmp >> 8) > ins->vibDepth)
+				autoVibAmp += ch->autoVibAmp;
+				if ((autoVibAmp >> 8) > ins->autoVibDepth)
 				{
-					autoVibAmp = ins->vibDepth << 8;
-					ch->eVibSweep = 0;
+					autoVibAmp = ins->autoVibDepth << 8;
+					ch->autoVibSweep = 0;
 				}
 
-				ch->eVibAmp = autoVibAmp;
+				ch->autoVibAmp = autoVibAmp;
 			}
 		}
 		else
 		{
-			autoVibAmp = ch->eVibAmp;
+			autoVibAmp = ch->autoVibAmp;
 		}
 
 #ifdef HAS_MIDI
 		// non-FT2 hack to make modulation wheel work when auto vibrato rate is zero
-		if (ch->midiVibDepth > 0 && ins->vibRate == 0)
-			ins->vibRate = 0x20;
+		if (ch->midiVibDepth > 0 && ins->autoVibRate == 0)
+			ins->autoVibRate = 0x20;
 
 		autoVibAmp += ch->midiVibDepth;
 #endif
-		ch->eVibPos += ins->vibRate;
+		ch->autoVibPos += ins->autoVibRate;
 
-		     if (ins->vibType == 1) autoVibVal = (ch->eVibPos > 127) ? 64 : -64; // square
-		else if (ins->vibType == 2) autoVibVal = (((ch->eVibPos >> 1) + 64) & 127) - 64; // ramp up
-		else if (ins->vibType == 3) autoVibVal = ((-(ch->eVibPos >> 1) + 64) & 127) - 64; // ramp down
-		else autoVibVal = vibSineTab[ch->eVibPos]; // sine
+		int16_t autoVibVal;
+		     if (ins->autoVibType == 1) autoVibVal = (ch->autoVibPos > 127) ? 64 : -64; // square
+		else if (ins->autoVibType == 2) autoVibVal = (((ch->autoVibPos >> 1) + 64) & 127) - 64; // ramp up
+		else if (ins->autoVibType == 3) autoVibVal = ((-(ch->autoVibPos >> 1) + 64) & 127) - 64; // ramp down
+		else autoVibVal = autoVibSineTab[ch->autoVibPos]; // sine
 
-		autoVibVal <<= 2;
-		uint16_t tmpPeriod = (autoVibVal * (int16_t)autoVibAmp) >> 16;
+		autoVibVal = (autoVibVal * (int16_t)autoVibAmp) >> (6+8);
 
-		tmpPeriod += ch->outPeriod;
-		if (tmpPeriod > MAX_FRQ-1)
-			tmpPeriod = 0; // yes, FT2 does this (!)
+		uint16_t tmpPeriod = ch->outPeriod + autoVibVal;
+		if (tmpPeriod >= 32000) // unsigned comparison
+			tmpPeriod = 0;
 
 #ifdef HAS_MIDI
 		if (midi.enable)
@@ -1787,8 +1785,8 @@
 	ch->portaDownSpeed = param;
 
 	ch->realPeriod += param << 2;
-	if ((int16_t)ch->realPeriod > MAX_FRQ-1) // FT2 bug, should've been unsigned comparison
-		ch->realPeriod = MAX_FRQ-1;
+	if ((int16_t)ch->realPeriod >= 32000) // FT2 bug, should've been unsigned comparison
+		ch->realPeriod = 32000-1;
 
 	ch->outPeriod = ch->realPeriod;
 	ch->status |= IS_Period;
@@ -2590,10 +2588,10 @@
 		ins->volEnvLoopStart = (uint8_t)config.stdVolEnvLoopStart[i];
 		ins->volEnvLoopEnd = (uint8_t)config.stdVolEnvLoopEnd[i];
 		ins->fadeout = config.stdFadeout[i];
-		ins->vibRate = (uint8_t)config.stdVibRate[i];
-		ins->vibDepth = (uint8_t)config.stdVibDepth[i];
-		ins->vibSweep = (uint8_t)config.stdVibSweep[i];
-		ins->vibType = (uint8_t)config.stdVibType[i];
+		ins->autoVibRate = (uint8_t)config.stdVibRate[i];
+		ins->autoVibDepth = (uint8_t)config.stdVibDepth[i];
+		ins->autoVibSweep = (uint8_t)config.stdVibSweep[i];
+		ins->autoVibType = (uint8_t)config.stdVibType[i];
 		ins->volEnvFlags = (uint8_t)config.stdVolEnvFlags[i];
 	}
 
@@ -2632,10 +2630,10 @@
 	ins->panEnvFlags = 0;
 
 	ins->fadeout = 0;
-	ins->vibRate = 0;
-	ins->vibDepth = 0;
-	ins->vibSweep = 0;
-	ins->vibType = 0;
+	ins->autoVibRate = 0;
+	ins->autoVibDepth = 0;
+	ins->autoVibSweep = 0;
+	ins->autoVibType = 0;
 
 	resumeMusic();
 }
@@ -2857,8 +2855,8 @@
 	if (song.speed == 0)
 		song.speed = song.initialSpeed;
 
-	audio.tickSampleCounter = 0; // zero tick sample counter so that it will instantly initiate a tick
-	audio.tickSampleCounterFrac = 0;
+	// zero tick sample counter so that it will instantly initiate a tick
+	audio.tickSampleCounterFrac = audio.tickSampleCounter = 0;
 
 	unlockMixerCallback();
 
@@ -3104,7 +3102,7 @@
 		ch->realVol = 0;
 		ch->outVol = 0;
 		ch->oldVol = 0;
-		ch->fFinalVol = 0.0f;
+		ch->dFinalVol = 0.0;
 		ch->oldPan = 128;
 		ch->outPan = 128;
 		ch->finalPan = 128;
--- a/src/ft2_replayer.h
+++ b/src/ft2_replayer.h
@@ -44,7 +44,6 @@
 #define MAX_SPEED 31
 #define MAX_CHANNELS 32
 #define TRACK_WIDTH (5 * MAX_CHANNELS)
-#define MAX_FRQ 32000
 #define C4_FREQ 8363
 #define NOTE_C4 (4*12)
 #define NOTE_OFF 97
@@ -232,7 +231,7 @@
 	uint8_t volEnvSustain, volEnvLoopStart, volEnvLoopEnd;
 	uint8_t panEnvSustain, panEnvLoopStart, panEnvLoopEnd;
 	uint8_t volEnvFlags, panEnvFlags;
-	uint8_t vibType, vibSweep, vibDepth, vibRate;
+	uint8_t autoVibType, autoVibSweep, autoVibDepth, autoVibRate;
 	uint16_t fadeout;
 	int16_t volEnvPoints[12][2], panEnvPoints[12][2], midiProgram, midiBend;
 	int16_t numSamples; // used by loader only
@@ -241,7 +240,7 @@
 
 typedef struct channel_t
 {
-	bool envSustainActive, channelOff, mute;
+	bool keyOff, channelOff, mute;
 	volatile uint8_t status, tmpStatus;
 	int8_t relativeNote, finetune;
 	uint8_t smpNum, instrNum, efxData, efx, smpOffset, tremorSave, tremorPos;
@@ -250,16 +249,17 @@
 	uint8_t jumpToRow, patLoopCounter, volSlideSpeed, fVolSlideUpSpeed, fVolSlideDownSpeed;
 	uint8_t fPortaUpSpeed, fPortaDownSpeed, ePortaUpSpeed, ePortaDownSpeed;
 	uint8_t portaUpSpeed, portaDownSpeed, retrigSpeed, retrigCnt, retrigVol;
-	uint8_t volColumnVol, noteNum, panEnvPos, eVibPos, volEnvPos, realVol, oldVol, outVol;
+	uint8_t volColumnVol, noteNum, panEnvPos, autoVibPos, volEnvPos, realVol, oldVol, outVol;
 	uint8_t oldPan, outPan, finalPan;
 	int16_t midiPitch;
 	uint16_t outPeriod, realPeriod, finalPeriod, noteData, wantPeriod, portaSpeed;
-	uint16_t volEnvTick, panEnvTick, eVibAmp, eVibSweep;
-	uint16_t fadeoutVol, fadeoutSpeed, midiVibDepth;
+	uint16_t volEnvTick, panEnvTick, autoVibAmp, autoVibSweep;
+	uint16_t midiVibDepth;
+	int32_t fadeoutVol, fadeoutSpeed;
 	int32_t volEnvDelta, panEnvDelta, volEnvValue, panEnvValue;
 	int32_t oldFinalPeriod, smpStartPos;
 
-	float fFinalVol; // 0.0f .. 1.0f
+	double dFinalVol; // 0.0 .. 1.0
 
 	sample_t *smpPtr;
 	instr_t *instrPtr;
--- a/src/ft2_sample_saver.c
+++ b/src/ft2_sample_saver.c
@@ -421,10 +421,10 @@
 		mptExtraChunk.defaultPan = smp->panning; // 0..255
 		mptExtraChunk.defaultVolume = smp->volume * 4; // 0..256
 		mptExtraChunk.globalVolume = 64; // 0..64
-		mptExtraChunk.vibratoType = ins->vibType; // 0..3    0 = sine, 1 = square, 2 = ramp up, 3 = ramp down
-		mptExtraChunk.vibratoSweep = ins->vibSweep; // 0..255
-		mptExtraChunk.vibratoDepth = ins->vibDepth; // 0..15
-		mptExtraChunk.vibratoRate= ins->vibRate; // 0..63
+		mptExtraChunk.vibratoType = ins->autoVibType; // 0..3    0 = sine, 1 = square, 2 = ramp up, 3 = ramp down
+		mptExtraChunk.vibratoSweep = ins->autoVibSweep; // 0..255
+		mptExtraChunk.vibratoDepth = ins->autoVibDepth; // 0..15
+		mptExtraChunk.vibratoRate = ins->autoVibRate; // 0..63
 
 		fwrite(&mptExtraChunk, sizeof (mptExtraChunk), 1, f);
 		if (mptExtraChunk.chunkSize & 1)
--- a/src/ft2_tables.c
+++ b/src/ft2_tables.c
@@ -41,7 +41,7 @@
 	0x00,0x00,0x00,0x00,0x00,0x00,0x46,0x4F,0x52,0x4D,0x49,0x4C,0x42,0x4D,0x42,0x4D
 };
 
-const int8_t vibSineTab[256] = // for auto-vibrato
+const int8_t autoVibSineTab[256] = 
 {
 	  0,  -2,  -3,  -5,  -6,  -8,  -9, -11, -12, -14, -16, -17, -19, -20, -22, -23,
 	-24, -26, -27, -29, -30, -32, -33, -34, -36, -37, -38, -39, -41, -42, -43, -44,
--- a/src/ft2_tables.h
+++ b/src/ft2_tables.h
@@ -13,7 +13,7 @@
 extern const uint16_t ptPeriods[3 * 12];
 
 extern const uint8_t arpTab[256];
-extern const int8_t vibSineTab[256]; // for auto-vibrato
+extern const int8_t autoVibSineTab[256];
 extern const uint8_t vibTab[32];
 extern const uint16_t amigaPeriod[8 * 12];
 extern const uint16_t linearPeriods[1936];
--- a/src/ft2_video.c
+++ b/src/ft2_video.c
@@ -121,9 +121,9 @@
 	             "Frames per second: %.3f\n" \
 	             "Monitor refresh rate: %.1fHz (+/-)\n" \
 	             "59..61Hz GPU VSync used: %s\n" \
+	             "HPC frequency (timer): %.4fMHz\n" \
 	             "Audio frequency: %.1fkHz (expected %.1fkHz)\n" \
 	             "Audio buffer samples: %d (expected %d)\n" \
-	             "Audio channels: %d (expected %d)\n" \
 	             "Audio latency: %.1fms (expected %.1fms)\n" \
 	             "Render size: %dx%d (offset %d,%d)\n" \
 	             "Disp. size: %dx%d (window: %dx%d)\n" \
@@ -136,9 +136,9 @@
 	             dAvgFPS,
 	             dRefreshRate,
 	             video.vsync60HzPresent ? "yes" : "no",
-	             audio.haveFreq * (1.0 / 1000.0), audio.wantFreq * (1.0 / 1000.0),
+	             hpcFreq.freq64 / (1000.0 * 1000.0),
+	             audio.haveFreq / 1000.0, audio.wantFreq / 1000.0,
 	             audio.haveSamples, audio.wantSamples,
-	             audio.haveChannels, audio.wantChannels,
 	             dAudLatency, ((audio.wantSamples * 1000.0) / audio.wantFreq),
 	             video.renderW, video.renderH, video.renderX, video.renderY,
 	             video.displayW, video.displayH, video.windowW, video.windowH,
--- a/src/mixer/ft2_mix.h
+++ b/src/mixer/ft2_mix.h
@@ -3,6 +3,7 @@
 #include <stdint.h>
 #include "../ft2_cpu.h"
 
+// the fractional bits are hardcoded, changing these will break things!
 #if CPU_64BIT
 #define MIXER_FRAC_BITS 32
 #else
--- a/src/modloaders/ft2_load_xm.c
+++ b/src/modloaders/ft2_load_xm.c
@@ -251,10 +251,10 @@
 		ins->panEnvLoopEnd = ih.panEnvLoopEnd;
 		ins->volEnvFlags = ih.volEnvFlags;
 		ins->panEnvFlags = ih.panEnvFlags;
-		ins->vibType = ih.vibType;
-		ins->vibSweep = ih.vibSweep;
-		ins->vibDepth = ih.vibDepth;
-		ins->vibRate = ih.vibRate;
+		ins->autoVibType = ih.vibType;
+		ins->autoVibSweep = ih.vibSweep;
+		ins->autoVibDepth = ih.vibDepth;
+		ins->autoVibRate = ih.vibRate;
 		ins->fadeout = ih.fadeout;
 		ins->midiOn = (ih.midiOn == 1) ? true : false;
 		ins->midiChannel = ih.midiChannel;
--- a/src/scopes/ft2_scopes.c
+++ b/src/scopes/ft2_scopes.c
@@ -78,13 +78,13 @@
 		ch->realVol = 0;
 		ch->outVol = 0;
 		ch->oldVol = 0;
-		ch->fFinalVol = 0.0f;
+		ch->dFinalVol = 0.0;
 		ch->outPan = 128;
 		ch->oldPan = 128;
 		ch->finalPan = 128;
 		ch->status = IS_Vol;
 
-		ch->envSustainActive = false; // non-FT2 bug fix for stuck piano keys
+		ch->keyOff = true; // non-FT2 bug fix for stuck piano keys
 	}
 
 	scope[chNr].wasCleared = false;