shithub: ft²

Download patch

ref: f71a1d5fc0085f357a6c5f12a72fad5926fc7ab5
parent: 0cc01d0968f47593885846ac23878a89df22ba32
author: Olav Sørensen <olav.sorensen@live.no>
date: Mon Nov 22 17:49:28 EST 2021

Fixed small bug in instrument editor piano (+ small cleanup)

--- a/src/ft2_header.h
+++ b/src/ft2_header.h
@@ -12,7 +12,7 @@
 #endif
 #include "ft2_replayer.h"
 
-#define PROG_VER_STR "1.47"
+#define PROG_VER_STR "1.48"
 
 // do NOT change these! It will only mess things up...
 
@@ -56,11 +56,11 @@
 #define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
 
 #define DROUND(x) \
-	if (x < 0.0) x -= 0.5; \
+	     if (x < 0.0) x -= 0.5; \
 	else if (x > 0.0) x += 0.5
 
 #define FROUND(x) \
-	if (x < 0.0f) x -= 0.5f; \
+	     if (x < 0.0f) x -= 0.5f; \
 	else if (x > 0.0f) x += 0.5f
 
 // fast 32-bit -> 8-bit clamp
--- a/src/ft2_replayer.c
+++ b/src/ft2_replayer.c
@@ -22,12 +22,7 @@
 #include "ft2_structs.h"
 #include "mixer/ft2_windowed_sinc.h"
 
-/* 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!
-*/
-
-static double dLogTab[768], dExp2MulTab[32];
+static double dLogTab[4*12*16], dExp2MulTab[32];
 static bool bxxOverflow;
 static note_t nilPatternLine[MAX_CHANNELS];
 
@@ -43,7 +38,7 @@
 int16_t patternNumRows[MAX_PATTERNS];
 channel_t channel[MAX_CHANNELS];
 song_t song;
-instr_t *instr[132];
+instr_t *instr[128+4];
 note_t *pattern[MAX_PATTERNS];
 // ----------------------------------
 
@@ -111,10 +106,9 @@
 	editor.updateWindowTitle = true;
 }
 
-// used on external sample load and during sample loading in some module formats
+// used on external sample load and during sample loading (on some module formats)
 void tuneSample(sample_t *s, const int32_t midCFreq, bool linearPeriodsFlag)
 {
-	#define NOTE_C4 (4*12)
 	#define MIN_PERIOD (0)
 	#define MAX_PERIOD (((10*12*16)-1)-1) /* -1 (because of bugged amigaPeriods table values) */
 
@@ -257,13 +251,15 @@
 
 double dLinearPeriod2Hz(int32_t period)
 {
+	period &= 0xFFFF; // just in case (actual period range is 0..65535)
+
 	if (period == 0)
 		return 0.0; // in FT2, a period of 0 results in 0Hz
 
-	const uint32_t invPeriod = ((12 * 192 * 4) - period) & 0xFFFF; // mask needed for FT2 frequency quirk
+	const uint32_t invPeriod = ((12 * 192 * 4) - period) & 0xFFFF; // mask needed for FT2 period overflow quirk
 
-	const uint32_t quotient = invPeriod / 768;
-	const uint32_t remainder = invPeriod % 768;
+	const uint32_t quotient  = invPeriod / (12 * 16 * 4);
+	const uint32_t remainder = invPeriod % (12 * 16 * 4);
 
 	return dLogTab[remainder] * dExp2MulTab[(14-quotient) & 31]; // x = y >> ((14-quotient) & 31);
 }
@@ -270,6 +266,8 @@
 
 double dAmigaPeriod2Hz(int32_t period)
 {
+	period &= 0xFFFF; // just in case (actual period range is 0..65535)
+
 	if (period == 0)
 		return 0.0; // in FT2, a period of 0 results in 0Hz
 
@@ -284,13 +282,16 @@
 // returns *exact* FT2 C-4 voice rate (depending on finetune, relativeNote and linear/Amiga period mode)
 double getSampleC4Rate(sample_t *s)
 {
-	int32_t note = (96/2) + s->relativeNote;
+	int32_t note = NOTE_C4 + s->relativeNote;
+	if (note < 0)
+		return -1; // shouldn't happen (just in case...)
+
 	if (note >= (10*12)-1)
-		return -1; // B-9 (from relativeTone) = illegal! (won't play in replayer)
+		return -1; // B-9 (after relativeTone add) = illegal! (won't play in replayer)
 
 	const int32_t C4Period = (note << 4) + (((int8_t)s->finetune >> 3) + 16);
 
-	const uint16_t period = audio.linearPeriodsFlag ? linearPeriods[C4Period] : amigaPeriods[C4Period];
+	const int32_t period = audio.linearPeriodsFlag ? linearPeriods[C4Period] : amigaPeriods[C4Period];
 	return dPeriod2Hz(period);
 }
 
@@ -328,7 +329,7 @@
 
 static void retrigEnvelopeVibrato(channel_t *ch)
 {
-	if (!(ch->waveCtrl & 0x04)) ch->vibPos = 0;
+	if (!(ch->waveCtrl & 0x04)) ch->vibPos  = 0;
 	if (!(ch->waveCtrl & 0x40)) ch->tremPos = 0;
 
 	ch->retrigCnt = 0;
@@ -397,13 +398,13 @@
 	}
 }
 
-void calcReplayerLogTab(void)
+void calcReplayerLogTab(void) // for linear period -> hz calculation
 {
 	for (int32_t i = 0; i < 32; i++)
 		dExp2MulTab[i] = 1.0 / exp2(i); // 1/(2^i)
 
-	for (int32_t i = 0; i < 768; i++)
-		dLogTab[i] = 8363.0 * 256.0 * exp2(i / 768.0);
+	for (int32_t i = 0; i < 4*12*16; i++)
+		dLogTab[i] = 8363.0 * exp2(i / 768.0) * 256.0;
 }
 
 void calcReplayerVars(int32_t audioFreq)
@@ -413,7 +414,7 @@
 		return;
 
 	audio.dHz2MixDeltaMul = (double)MIXER_FRAC_SCALE / audioFreq;
-	audio.quickVolRampSamples = (int32_t)round(audioFreq / 200.0);
+	audio.quickVolRampSamples = (int32_t)round(audioFreq / (double)FT2_QUICKRAMP_SAMPLES);
 	audio.fRampQuickVolMul = (float)(1.0 / audio.quickVolRampSamples);
 
 	for (int32_t bpm = MIN_BPM; bpm <= MAX_BPM; bpm++)
@@ -442,28 +443,16 @@
 	}
 }
 
-int32_t getPianoKey(uint16_t period, int8_t finetune, int8_t relativeNote) // for piano in Instr. Ed.
+// for piano in Instr. Ed. (values outside 0..95 can happen)
+int32_t getPianoKey(uint16_t period, int8_t finetune, int8_t relativeNote) 
 {
 	assert(note2Period != NULL);
-
 	if (period > note2Period[0])
-		return -1; // outside lower range on piano
+		return -1; // outside left piano edge
 
-	if (audio.linearPeriodsFlag)
-	{
-		int32_t note = ((10*12*16*4+64) - (period + 16*2)) >> 2;
-		note -= ((int8_t)finetune >> 3) + 16;
-		note = ((note >> 4) + 1) - relativeNote;
+	finetune = ((int8_t)finetune >> 3) + 16; // -128..127 -> 0..31
 
-		return note;
-	}
-
-	/* Amiga periods require a slightly slower method...
-	** This is not 100% accurate for all periods, but should be faster
-	** than using log2() and floating-point arithmetics.
-	*/
-	finetune = ((int8_t)finetune >> 3) + 16;
-	int32_t hiPeriod = (10*12*16)+16;
+	int32_t hiPeriod = 10*12*16;
 	int32_t loPeriod = 0;
 
 	for (int32_t i = 0; i < 7; i++)
@@ -470,7 +459,7 @@
 	{
 		const int32_t tmpPeriod = (((loPeriod + hiPeriod) >> 1) & ~15) + finetune;
 
-		int32_t lookUp = tmpPeriod - 8;
+		int32_t lookUp = tmpPeriod - 16;
 		if (lookUp < 0)
 			lookUp = 0;
 
@@ -480,11 +469,7 @@
 			loPeriod = (tmpPeriod - finetune) & ~15;
 	}
 
-	int32_t note = loPeriod;
-	note >>= 4;
-	note -= relativeNote;
-
-	return note;
+	return (loPeriod >> 4) - relativeNote;
 }
 
 static void startTone(uint8_t note, uint8_t efx, uint8_t efxData, channel_t *ch)
@@ -523,7 +508,7 @@
 	ch->relativeNote = s->relativeNote;
 
 	note += ch->relativeNote;
-	if (note >= 10*12)
+	if (note >= 10*12) // unsigned check (also handles note < 0)
 		return;
 
 	ch->oldVol = s->volume;
@@ -536,12 +521,10 @@
 
 	if (note != 0)
 	{
-		const uint16_t tmpTon = ((note-1) << 4) + (((int8_t)ch->finetune >> 3) + 16); // 0..1935
-		if (tmpTon < MAX_NOTES) // tmpTon is *always* below MAX_NOTES here, so this check is not really needed
-		{
-			assert(note2Period != NULL);
-			ch->outPeriod = ch->realPeriod = note2Period[tmpTon];
-		}
+		const uint16_t noteIndex = ((note-1) << 4) + (((int8_t)ch->finetune >> 3) + 16); // 0..1920
+
+		assert(note2Period != NULL);
+		ch->outPeriod = ch->realPeriod = note2Period[noteIndex];
 	}
 
 	ch->status |= IS_Period + IS_Vol + IS_Pan + IS_Trigger + IS_QuickVol;
@@ -787,6 +770,8 @@
 	instr_t *ins = ch->instrPtr;
 	assert(ins != NULL);
 
+	// (envelope precision has been updated from x.8fp to x.16fp)
+
 	// *** VOLUME ENVELOPE ***
 	if (ins->volEnvFlags & ENV_ENABLED)
 	{
@@ -854,7 +839,7 @@
 	}
 
 	// *** PANNING ENVELOPE ***
-	if (ins->volEnvFlags & ENV_SUSTAIN) // What..? Probably an FT2 bug!
+	if (ins->volEnvFlags & ENV_SUSTAIN) // What..? An FT2 bug!?
 	{
 		ch->panEnvTick = param-1;
 
@@ -1424,6 +1409,8 @@
 
 	if (!ch->mute)
 	{
+		// (envelope precision has been updated from x.8fp to x.16fp)
+
 		// *** VOLUME ENVELOPE ***
 		envVal = 0;
 		if (ins->volEnvFlags & ENV_ENABLED)
@@ -1712,7 +1699,7 @@
 
 		int32_t lookUp = tmpPeriod - 8;
 		if (lookUp < 0)
-			lookUp = 0; // safety fix (C-0 w/ finetune <= -65). This buggy read seems to return 0 in FT2 (TODO: Verify...)
+			lookUp = 0; // safety fix (C-0 w/ f.tune <= -65). This seems to result in 0 in FT2 (TODO: verify)
 
 		if (period >= note2Period[lookUp])
 			hiPeriod = (tmpPeriod - fineTune) & ~15;
--- a/src/ft2_replayer.h
+++ b/src/ft2_replayer.h
@@ -46,6 +46,7 @@
 #define TRACK_WIDTH (5 * MAX_CHANNELS)
 #define MAX_FRQ 32000
 #define C4_FREQ 8363
+#define NOTE_C4 (4*12)
 #define NOTE_OFF 97
 #define MAX_NOTES (10*12*16+16)
 #define MAX_PATTERNS 256
@@ -57,6 +58,7 @@
 #define INSTR_HEADER_SIZE 263
 #define INSTR_XI_HEADER_SIZE 298
 #define MAX_SAMPLE_LEN 0x3FFFFFFF
+#define FT2_QUICKRAMP_SAMPLES 200
 #define PROG_NAME_STR "Fasttracker II clone"
 
 enum // sample flags
@@ -289,7 +291,7 @@
 // used on external sample load and during sample loading in some module formats
 void tuneSample(sample_t *s, const int32_t midCFreq, bool linearPeriodsFlag);
 
-void calcReplayerLogTab(void);
+void calcReplayerLogTab(void); // for linear period -> hz calculation
 
 double dLinearPeriod2Hz(int32_t period);
 double dAmigaPeriod2Hz(int32_t period);
@@ -352,5 +354,5 @@
 extern int16_t patternNumRows[MAX_PATTERNS];
 extern channel_t channel[MAX_CHANNELS];
 extern song_t song;
-extern instr_t *instr[132];
+extern instr_t *instr[128+4];
 extern note_t *pattern[MAX_PATTERNS];