shithub: ft2-clone

Download patch

ref: 007b8a42742ad0f220568db820b8d91a3c9ce2ec
parent: ba70ad4ef096963531b39cfbf682d03a3e001358
author: Olav Sørensen <olav.sorensen@live.no>
date: Wed Mar 18 07:34:59 EDT 2020

Pushed v1.13 code

- Fixed crash when loading some very specific S3M modules
- Linux: Fixed CMakeLists.txt to work on Arch Linux
- Some other small miscellaneous fixes not worth of a mention

--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -5,23 +5,25 @@
 find_package(SDL2 REQUIRED)
 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${ft2-clone_SOURCE_DIR}/release/other/")
 
-
 file(GLOB ft2-clone_SRC
     "${ft2-clone_SOURCE_DIR}/src/rtmidi/*.cpp"
     "${ft2-clone_SOURCE_DIR}/src/*.c"
-    "${ft2-clone_SOURCE_DIR}/src/gfxdata/*.c"   
+    "${ft2-clone_SOURCE_DIR}/src/gfxdata/*.c"
 )
 
-
 add_executable(ft2-clone ${ft2-clone_SRC})
 
 target_include_directories(ft2-clone SYSTEM
     PRIVATE ${SDL2_INCLUDE_DIRS})
 
-target_link_libraries(ft2-clone 
+if("${SDL2_LIBRARIES}" STREQUAL "")
+    message(WARNING "SDL2_LIBRARIES wasn't set, manually setting to SDL2::SDL2")
+    set(SDL2_LIBRARIES "SDL2::SDL2")
+endif()
+
+target_link_libraries(ft2-clone
     PRIVATE m asound pthread ${SDL2_LIBRARIES})
 target_compile_definitions(ft2-clone PRIVATE __LINUX_ALSA__)
 
-
 install(TARGETS ft2-clone
-        RUNTIME DESTINATION bin )
\ No newline at end of file
+    RUNTIME DESTINATION bin )
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
 BSD 3-Clause License
 
-Copyright (c) 2019-2020, Olav Sørensen
+Copyright (c) 2016-2020, Olav Sørensen
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
--- a/release/LICENSES.txt
+++ b/release/LICENSES.txt
@@ -2,7 +2,7 @@
 ---------------------- Source code (BSD 3-clause license) ----------------------
 --------------------------------------------------------------------------------
 
-Copyright (c) 2016-2020, Olav S�rensen
+Copyright (c) 2016-2020, Olav Sørensen
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -19,7 +19,7 @@
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL OLAV S�RENSEN BE LIABLE FOR ANY
+DISCLAIMED. IN NO EVENT SHALL OLAV SØRENSEN BE LIABLE FOR ANY
 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
@@ -85,7 +85,7 @@
 
 
 --------------------------------------------------------------------------------
-- Graphics, except program icon, MIDI logo and new FT2 logo: by Magnus H�gdahl -
+- Graphics, except program icon, MIDI logo and new FT2 logo: by Magnus Högdahl -
 --------------------------------------------------------------------------------
 
 Attribution-NonCommercial-ShareAlike 4.0 International
@@ -510,7 +510,7 @@
 Creative Commons is not a party to its public
 licenses. Notwithstanding, Creative Commons may elect to apply one of
 its public licenses to material it publishes and in those instances
-will be considered the �Licensor.� The text of the Creative Commons
+will be considered the “Licensor.” The text of the Creative Commons
 public licenses is dedicated to the public domain under the CC0 Public
 Domain Dedication. Except for the limited purpose of indicating that
 material is shared under a Creative Commons public license or as
--- a/src/ft2_audio.h
+++ b/src/ft2_audio.h
@@ -33,7 +33,7 @@
 	uint32_t freq;
 	uint32_t audLatencyPerfValInt, audLatencyPerfValFrac;
 	uint64_t tickTime64, tickTime64Frac;
-	double dAudioLatencyMs, dSpeedValMul, dScopeFreqMul;
+	double dAudioLatencyMs, dSpeedValMul, dScopeFreqMul, dPianoDeltaMul;
 	SDL_AudioDeviceID dev;
 	uint32_t wantFreq, haveFreq, wantSamples, haveSamples, wantChannels, haveChannels;
 } audio;
--- a/src/ft2_header.h
+++ b/src/ft2_header.h
@@ -12,7 +12,7 @@
 #endif
 #include "ft2_replayer.h"
 
-#define PROG_VER_STR "1.12"
+#define PROG_VER_STR "1.13"
 
 // do NOT change these! It will only mess things up...
 
--- a/src/ft2_inst_ed.c
+++ b/src/ft2_inst_ed.c
@@ -1701,39 +1701,31 @@
 
 /* 8bitbubsy: This is my new version of FT2's buggy getNote().
 ** It's used to convert a channel's period into a piano key number.
+** Or in this case, a voice's frequency into a piano key number.
 **
-** It's probably slower in "Amiga frequencies" mode, but at least it doesn't
-** have weird overflow/underflow patterns.
+** It's probably slower than the original version, but this one is
+** 100% accurate in all (quirky) situations.
 **
 ** Warning: This function intentionally doesn't clamp the output value!
 */
-static int32_t getPianoKey(int32_t period, int32_t finetune, int32_t relativeTone, bool linearFrequencies)
+static int32_t getPianoKey(int32_t voiceDelta, int32_t finetune, int32_t relativeNote)
 {
-	int32_t note;
+	finetune >>= 3; // FT2 does this in the replayer internally (-128..127 -> -16..15)
 
-	finetune >>= 3; // FT2 does this in the replayer internally
+	double dTmp = voiceDelta * audio.dPianoDeltaMul;
+	dTmp = (log2(dTmp) * 12.0) - (finetune * (1.0 / 16.0));
+	int32_t note = (int32_t)(dTmp + 0.5); // rounded
 
-	if (linearFrequencies)
-	{
-		period = ((10 * 12 * 16 * 4) - period) - (finetune << 2);
-		note = (period + (1 << 5)) >> 6; // rounded
-	}
-	else
-	{
-		double dNote = (log2(period * (1.0 / (1712.0 * 16.0))) * -12.0) - (finetune * (1.0 / 16.0));
-		note = (int32_t)(dNote + 0.5); // rounded
-	}
+	note -= relativeNote;
+	// "note" is now the raw piano key number, unaffected by finetune/relativeNote
 
-	note -= relativeTone;
 	return note;
 }
 
-void drawPiano(void) // draw piano in idle mode (jamming keys)
+void drawPiano(chSyncData_t *chSyncData)
 {
-	bool keyDown, newStatus[96];
-	uint8_t key, octave;
+	bool newStatus[96];
 	int32_t note;
-	stmTyp *ch;
 
 	memset(newStatus, 0, sizeof (newStatus));
 
@@ -1740,69 +1732,43 @@
 	// find active notes
 	if (editor.curInstr > 0)
 	{
-		for (uint8_t i = 0; i < song.antChn; i++)
+		if (chSyncData != NULL) // song is playing, use replayer channel state
 		{
-			ch = &stm[i];
-			if (ch->instrNr == editor.curInstr && ch->envSustainActive)
+			syncedChannel_t *c = chSyncData->channels;
+			for (int32_t i = 0; i < song.antChn; i++, c++)
 			{
-				note = getPianoKey(ch->finalPeriod, ch->fineTune, ch->relTonNr, linearFrqTab);
-				if (note >= 0 && note <= 95)
-					newStatus[note] = true;
+				if (c->instrNr == editor.curInstr && c->envSustainActive)
+				{
+					note = getPianoKey(c->voiceDelta, c->fineTune, c->relTonNr);
+					if (note >= 0 && note <= 95)
+						newStatus[note] = true;
+				}
 			}
 		}
-	}
-
-	// draw keys
-	for (uint8_t i = 0; i < 96; i++)
-	{
-		keyDown = newStatus[i];
-		if (pianoKeyStatus[i] ^ keyDown)
+		else // song is not playing (jamming from keyboard/MIDI)
 		{
-			key = noteTab1[i];
-			octave = noteTab2[i];
-
-			if (keyIsBlackTab[key])
-				drawBlackPianoKey(key, octave, keyDown);
-			else
-				drawWhitePianoKey(key, octave, keyDown);
-
-			pianoKeyStatus[i] = keyDown;
-		}
-	}
-}
-
-void drawPianoReplayer(chSyncData_t *chSyncData) // draw piano with synced replayer state (song playing)
-{
-	bool keyDown, newStatus[96];
-	uint8_t key, octave;
-	int32_t note;
-	syncedChannel_t *ch;
-
-	memset(newStatus, 0, sizeof (newStatus));
-
-	// find active notes
-	if (editor.curInstr > 0)
-	{
-		for (uint8_t i = 0; i < song.antChn; i++)
-		{
-			ch = &chSyncData->channels[i];
-			if (ch->instrNr == editor.curInstr && ch->envSustainActive)
+			stmTyp *c = stm;
+			for (int32_t i = 0; i < song.antChn; i++, c++)
 			{
-				note = getPianoKey(ch->finalPeriod, ch->fineTune, ch->relTonNr, linearFrqTab);
-				if (note >= 0 && note <= 95)
-					newStatus[note] = true;
+				if (c->instrNr == editor.curInstr && c->envSustainActive)
+				{
+					int32_t voiceDelta = getFrequenceValue(c->finalPeriod);
+					note = getPianoKey(voiceDelta, c->fineTune, c->relTonNr);
+					if (note >= 0 && note <= 95)
+						newStatus[note] = true;
+				}
 			}
 		}
 	}
 
 	// draw keys
-	for (uint8_t i = 0; i < 96; i++)
+	for (int32_t i = 0; i < 96; i++)
 	{
-		keyDown = newStatus[i];
+		bool keyDown = newStatus[i];
 		if (pianoKeyStatus[i] ^ keyDown)
 		{
-			key = noteTab1[i];
-			octave = noteTab2[i];
+			uint8_t key = noteTab1[i];
+			uint8_t octave = noteTab2[i];
 
 			if (keyIsBlackTab[key])
 				drawBlackPianoKey(key, octave, keyDown);
@@ -1839,10 +1805,10 @@
 
 	// get coefficients
 	dx = x2 - x1;
-	ax = ABS(dx) * 2;
+	ax = ABS(dx) << 1;
 	sx = SGN(dx);
 	dy = y2 - y1;
-	ay = ABS(dy) * 2;
+	ay = ABS(dy) << 1;
 	sy = SGN(dy);
 	x  = x1;
 	y  = y1;
@@ -1857,7 +1823,7 @@
 	// draw line
 	if (ax > ay)
 	{
-		d = ay - (ax / 2);
+		d = ay - (ax >> 1);
 
 		while (true)
 		{
@@ -1886,7 +1852,7 @@
 	}
 	else
 	{
-		d = ax - (ay / 2);
+		d = ax - (ay >> 1);
 
 		while (true)
 		{
--- a/src/ft2_inst_ed.h
+++ b/src/ft2_inst_ed.h
@@ -90,8 +90,7 @@
 void cbPEnv(void);
 void cbPEnvSus(void);
 void cbPEnvLoop(void);
-void drawPiano(void);
-void drawPianoReplayer(chSyncData_t *chSyncData);
+void drawPiano(chSyncData_t *chSyncData);
 bool testInstrVolEnvMouseDown(bool mouseButtonDown);
 bool testInstrPanEnvMouseDown(bool mouseButtonDown);
 bool testPianoKeysMouseDown(bool mouseButtonDown);
--- a/src/ft2_module_loader.c
+++ b/src/ft2_module_loader.c
@@ -2616,7 +2616,7 @@
 	showBottomScreen(); // redraw bottom screen (also redraws pattern editor)
 
 	if (editor.ui.instEditorShown)
-		drawPiano(); // redraw piano now (since if playing = wait for next tick update)
+		drawPiano(NULL); // redraw piano now (since if playing = wait for next tick update)
 
 	removeSongModifiedFlag();
 
--- a/src/ft2_replayer.c
+++ b/src/ft2_replayer.c
@@ -26,7 +26,7 @@
 */
 
 static bool bxxOverflow;
-static int32_t oldPeriod;
+static uint16_t oldPeriod;
 static uint32_t oldRate, frequenceDivFactor, frequenceMulFactor;
 static tonTyp nilPatternLine;
 
@@ -342,13 +342,17 @@
 
 	// for audio/video sync
 	audio.dSpeedValMul = (1.0 / rate) * editor.dPerfFreq;
+
+	uint32_t deltaBase = frequenceDivFactor / (1712 * 16); // exact 16.16 delta base for this audio rate
+	audio.dPianoDeltaMul = 1.0 / deltaBase; // for piano in Instr. Ed.
 }
 
 // 100% FT2-accurate routine, do not touch!
-uint32_t getFrequenceValue(int32_t period)
+uint32_t getFrequenceValue(uint16_t period)
 {
 	uint8_t shift;
-	int32_t index, indexQuotient, indexRemainder;
+	uint16_t index;
+	int32_t indexQuotient, indexRemainder;
 	uint32_t rate;
 
 	if (period == 0)
@@ -439,21 +443,17 @@
 	ch->oldPan = s->pan;
 
 	if (effTyp == 0x0E && (eff & 0xF0) == 0x50)
-		ch->fineTune = ((eff & 0x0F) * 16) - 128; // result is now -128 .. 127
+		ch->fineTune = ((eff & 0x0F) << 4) - 128; // result is now -128..127
 	else
 		ch->fineTune = s->fine;
 
-	if (ton > 0)
+	if (ton != 0)
 	{
-		tmpTon = ((ton - 1) * 16) + (((ch->fineTune >> 3) + 16) & 0xFF);
-		if (tmpTon < MAX_NOTES) // should always happen, but FT2 does this check
+		tmpTon = ((ton - 1) << 4) + (((ch->fineTune >> 3) + 16) & 0xFF);
+		if (tmpTon < MAX_NOTES)
 		{
 			assert(note2Period != NULL);
-			ch->realPeriod = note2Period[tmpTon];
-
-			ch->outPeriod = ch->realPeriod;
-			ch->midiCurPeriod = ch->realPeriod;
-			ch->midiPortaPeriod = ch->realPeriod;
+			ch->outPeriod = ch->realPeriod = note2Period[tmpTon];
 		}
 	}
 
@@ -2837,15 +2837,19 @@
 	assert(stmm < MAX_VOICES && inst < MAX_INST && ton <= 97);
 	ch = &stm[stmm];
 
+	// FT2 bugfix: Don't play tone if certain requirements are not met
 	if (ton != 97)
 	{
-		if (ton < 1 || ton > 96)
+		if (ton == 0 || ton > 96)
 			return;
 
-		s = &ins->samp[ins->ta[ton-1] & 0x0F];
-		if (s->pek == NULL || s->len == 0 || ton+s->relTon <= 0 || ton+s->relTon >= 12*10)
+		s = &ins->samp[ins->ta[ton-1] & 0xF];
+
+		int16_t newTon = (int16_t)ton + s->relTon;
+		if (s->pek == NULL || s->len == 0 || newTon <= 0 || newTon >= 12*10)
 			return;
 	}
+	// -------------------
 
 	lockAudio();
 
--- a/src/ft2_replayer.h
+++ b/src/ft2_replayer.h
@@ -201,8 +201,7 @@
 	uint8_t portaUpSpeed, portaDownSpeed, retrigSpeed, retrigCnt, retrigVol;
 	uint8_t volKolVol, tonNr, envPPos, eVibPos, envVPos, realVol, oldVol, outVol;
 	uint8_t oldPan, outPan, finalPan;
-	int16_t midiCurChannel, midiCurTone, midiCurVibDepth, midiCurPeriod, midiCurPitch;
-	int16_t midiBend, midiPortaPeriod, midiPitch, realPeriod, envVIPValue, envPIPValue;
+	int16_t midiPitch, realPeriod, envVIPValue, envPIPValue;
 	uint16_t finalVol, outPeriod, finalPeriod, tonTyp, wantPeriod, portaSpeed;
 	uint16_t envVCnt, envVAmp, envPCnt, envPAmp, eVibAmp, eVibSweep;
 	uint16_t fadeOutAmp, fadeOutSpeed, midiVibDepth;
@@ -244,7 +243,7 @@
 void calcReplayRate(int32_t rate);
 void resetOldRates(void);
 void tuneSample(sampleTyp *s, int32_t midCFreq);
-uint32_t getFrequenceValue(int32_t period);
+uint32_t getFrequenceValue(uint16_t period);
 
 bool allocateInstr(int16_t nr);
 void freeInstr(int32_t nr);
--- a/src/ft2_scopes.c
+++ b/src/ft2_scopes.c
@@ -378,9 +378,10 @@
 
 	/* Update live scope now.
 	** In theory it -can- be written to in the middle of a cached read,
-    ** then the read thread writes its own non-updated cached copy back and
+	** then the read thread writes its own non-updated cached copy back and
 	** the trigger never happens. So far I have never seen it happen,
-	** so it's probably very rare. Yes, this is not good coding... */
+	** so it's probably very rare. Yes, this is not good coding...
+	*/
 	*sc = tempState;
 }
 
@@ -558,7 +559,7 @@
 			if (ch->voiceDelta != oldVoiceDelta)
 			{
 				oldVoiceDelta = ch->voiceDelta;
-				oldSFrq = (int32_t)((oldVoiceDelta * audio.dScopeFreqMul) + 0.5); // rounded
+				oldSFrq = (int32_t)(((int32_t)oldVoiceDelta * audio.dScopeFreqMul) + 0.5); // rounded
 			}
 
 			sc->SFrq = oldSFrq;
--- a/src/ft2_video.c
+++ b/src/ft2_video.c
@@ -1108,7 +1108,7 @@
 			if (editor.ui.instEditorShown)
 			{
 				if (chSyncEntry != NULL)
-					drawPianoReplayer(chSyncEntry);
+					drawPiano(chSyncEntry);
 			}
 		}
 
@@ -1157,7 +1157,7 @@
 	}
 	else if (editor.ui.instEditorShown)
 	{
-		drawPiano();
+		drawPiano(NULL);
 	}
 
 	// handle pattern data updates