ref: 898c82f5468058e6d8fdb7d99c6ce9720046871b
parent: a2f19de82ff56c52d445e88a802bee4df1c161e5
author: Olav Sørensen <olav.sorensen@live.no>
date: Sat Feb 8 09:41:55 EST 2020
Pushed v1.08 code - Critical bugfix: Saved instruments (.xi) would end up being broken! - Linux bugfix: Loading a song by passing it to the executable's argument from a terminal wouldn't work in most cases... - macOS/Linux bugfix: Don't show ".." directory when you are in root - Code fix: We don't want our main instrument/sample structs to be packed, only the ones used during saving/loading of songs/instruments. This doesn't change the behavior of the FT2 clone, but it prevents unaligned pointer access in the replayer and other routines. - Small optimizations to pattern data rendering, those routines are quite slow! - Updated HOW-TO-COMPILE.txt
--- a/HOW-TO-COMPILE.txt
+++ b/HOW-TO-COMPILE.txt
@@ -22,9 +22,12 @@
chmod +x make-linux.sh (only needed once)
./make-linux.sh
+ Note: If you don't have libstdc++ and/or can't compile rtmidi, try running
+ make-linux-nomidi.sh instead.
+
Note: If you want faster audio mixing (for SLOW devices), pass -DLERPMIX
- to the GCC command line (edit make-linux.sh). This will yield slightly
- more blurry sound when interpolation is activated, though...
+ to the GCC command line (edit make-linux.sh/make-linux-nomidi.sh). This
+ will lower the resampling interpolation quality, though...
Known issues: Audio recording (sampling) can update VERY slowly or not work at
all... I have no idea why, it works really well on Windows/maCOS.
--- a/src/ft2_diskop.c
+++ b/src/ft2_diskop.c
@@ -83,7 +83,7 @@
static void setDiskOpItem(uint8_t item);
-int32_t getFileSize(UNICHAR *fileName) // returning -1 = filesize over 2GB
+int32_t getFileSize(UNICHAR *fileNameU) // returning -1 = filesize over 2GB
{
#ifdef _WIN32
FILE *f;
@@ -93,7 +93,7 @@
int64_t fSize;
#ifdef _WIN32
- f = UNICHAR_FOPEN(fileName, "rb");
+ f = UNICHAR_FOPEN(fileNameU, "rb");
if (f == NULL)
return 0;
@@ -101,7 +101,7 @@
fSize = _ftelli64(f);
fclose(f);
#else
- if (stat(fileName, &st) != 0)
+ if (stat(fileNameU, &st) != 0)
return 0;
fSize = (int64_t)(st.st_size);
@@ -273,10 +273,10 @@
FReq_PatCurPathU = (UNICHAR *)calloc(PATH_MAX + 2, sizeof (UNICHAR));
FReq_TrkCurPathU = (UNICHAR *)calloc(PATH_MAX + 2, sizeof (UNICHAR));
- if (modTmpFName == NULL || insTmpFName == NULL || smpTmpFName == NULL ||
- patTmpFName == NULL || trkTmpFName == NULL || FReq_NameTemp == NULL ||
- FReq_ModCurPathU == NULL || FReq_InsCurPathU == NULL || FReq_SmpCurPathU == NULL ||
- FReq_PatCurPathU == NULL || FReq_TrkCurPathU == NULL)
+ if (modTmpFName == NULL || insTmpFName == NULL || smpTmpFName == NULL || patTmpFName == NULL ||
+ trkTmpFName == NULL || FReq_NameTemp == NULL || FReq_ModCurPathU == NULL ||
+ FReq_InsCurPathU == NULL || FReq_SmpCurPathU == NULL || FReq_PatCurPathU == NULL ||
+ FReq_TrkCurPathU == NULL)
{
// allocated memory is free'd lateron
showErrorMsgBox("Not enough memory!");
@@ -349,9 +349,9 @@
SHFILEOPSTRUCTW shfo;
memset(&shfo, 0, sizeof (shfo));
- shfo.wFunc = FO_DELETE;
+ shfo.wFunc = FO_DELETE;
shfo.fFlags = FOF_SILENT | FOF_NOERRORUI | FOF_NOCONFIRMATION;
- shfo.pFrom = strU;
+ shfo.pFrom = strU;
return (SHFileOperationW(&shfo) == 0);
}
@@ -568,12 +568,12 @@
{
int32_t i, len;
- if (p == NULL)
- return (p);
+ if (p == NULL || p[0] == '\0')
+ return p;
len = (int32_t)strlen(p);
if (len < 2 || p[len-1] == DIR_DELIMITER)
- return (p);
+ return p;
// search for last directory delimiter
for (i = len - 1; i >= 0; i--)
@@ -583,7 +583,7 @@
}
if (i != 0)
- p += i + 1; // we found a directory delimiter - skip to the last one
+ p += i+1; // we found a directory delimiter - skip to the last one
return p;
}
@@ -594,7 +594,7 @@
const char illegalChars[] = "\\/:*?\"<>|";
char *ptr;
- if (src == NULL || *src == '\0')
+ if (src == NULL || src[0] == '\0')
return;
// convert illegal characters to space (for making a filename the OS will accept)
@@ -624,7 +624,6 @@
default:
case DISKOP_ITEM_MODULE:
{
- memset(modTmpFName, 0, PATH_MAX + 1);
strcpy(modTmpFName, filename);
updateCurrSongFilename(); // for window title
@@ -633,31 +632,19 @@
break;
case DISKOP_ITEM_INSTR:
- {
- memset(insTmpFName, 0, PATH_MAX + 1);
strcpy(insTmpFName, filename);
- }
break;
case DISKOP_ITEM_SAMPLE:
- {
- memset(smpTmpFName, 0, PATH_MAX + 1);
strcpy(smpTmpFName, filename);
- }
break;
case DISKOP_ITEM_PATTERN:
- {
- memset(patTmpFName, 0, PATH_MAX + 1);
strcpy(patTmpFName, filename);
- }
break;
case DISKOP_ITEM_TRACK:
- {
- memset(trkTmpFName, 0, PATH_MAX + 1);
strcpy(trkTmpFName, filename);
- }
break;
}
@@ -715,10 +702,21 @@
}
break;
- case DISKOP_ITEM_INSTR: loadInstr(filenameU); break;
- case DISKOP_ITEM_SAMPLE: loadSample(filenameU, editor.curSmp, false); break;
- case DISKOP_ITEM_PATTERN: loadPattern(filenameU); break;
- case DISKOP_ITEM_TRACK: loadTrack(filenameU); break;
+ case DISKOP_ITEM_INSTR:
+ loadInstr(filenameU);
+ break;
+
+ case DISKOP_ITEM_SAMPLE:
+ loadSample(filenameU, editor.curSmp, false);
+ break;
+
+ case DISKOP_ITEM_PATTERN:
+ loadPattern(filenameU);
+ break;
+
+ case DISKOP_ITEM_TRACK:
+ loadTrack(filenameU);
+ break;
}
}
@@ -1205,45 +1203,40 @@
name = unicharToCp437(nameU, false);
if (name == NULL)
return true;
+
+ if (name[0] == '\0')
+ goto skipEntry;
nameLen = (int32_t)strlen(name);
- if (nameLen == 0)
- {
- free(name);
- return true;
- }
// skip ".name" dirs/files
if (nameLen >= 2 && name[0] == '.' && name[1] != '.')
- {
- free(name);
- return true;
- }
+ goto skipEntry;
if (isDir)
{
// skip '.' directory
if (nameLen == 1 && name[0] == '.')
+ goto skipEntry;
+
+ // macOS/Linux: skip '..' directory if we're in root
+#ifndef _WIN32
+ if (nameLen == 2 && name[0] == '.' && name[1] == '.')
{
- free(name);
- return true;
+ if (FReq_CurPathU[0] == '/' && FReq_CurPathU[1] == '\0')
+ goto skipEntry;
}
+#endif
}
else if (!FReq_ShowAllFiles)
{
extOffset = getExtOffset(name, nameLen);
if (extOffset == -1)
- {
- free(name);
- return true;
- }
+ goto skipEntry;
extLen = (int32_t)strlen(&name[extOffset]);
if (extLen < 3 || extLen > 5)
- {
- free(name);
- return true; // no possibly known extensions to filter out
- }
+ goto skipEntry; // no possibly known extensions to filter out
extPtr = &name[extOffset];
@@ -1256,10 +1249,7 @@
if (extLen == 3)
{
if (_stricmp(".xm", extPtr) && _stricmp(".ft", extPtr))
- {
- free(name);
- return true; // skip, none of the extensions above
- }
+ goto skipEntry; // skip, none of the extensions above
}
else if (extLen == 4)
{
@@ -1267,14 +1257,12 @@
_stricmp(".s3m", extPtr) && _stricmp(".stm", extPtr) &&
_stricmp(".fst", extPtr) && _stricmp(".wav", extPtr))
{
- free(name);
- return true; // skip, none of the extensions above
+ goto skipEntry; // skip, none of the extensions above
}
}
else
{
- free(name);
- return true;
+ goto skipEntry;
}
}
break;
@@ -1284,10 +1272,7 @@
if (extLen == 3)
{
if (_stricmp(".xi", extPtr))
- {
- free(name);
- return true; // skip, none of the extensions above
- }
+ goto skipEntry; // skip, none of the extensions above
}
else if (extLen == 4)
{
@@ -1296,22 +1281,17 @@
_stricmp(".smp", extPtr) && _stricmp(".sam", extPtr) &&
_stricmp(".aif", extPtr) && _stricmp(".pat", extPtr))
{
- free(name);
- return true; // skip, none of the extensions above
+ goto skipEntry; // skip, none of the extensions above
}
}
else if (extLen == 5)
{
if (_stricmp(".aiff", extPtr))
- {
- free(name);
- return true; // skip, not the extension above
- }
+ goto skipEntry; // skip, not the extension above
}
else
{
- free(name);
- return true;
+ goto skipEntry;
}
}
break;
@@ -1325,22 +1305,17 @@
_stricmp(".smp", extPtr) && _stricmp(".sam", extPtr) &&
_stricmp(".aif", extPtr))
{
- free(name);
- return true; // skip, none of the extensions above
+ goto skipEntry; // skip, none of the extensions above
}
}
else if (extLen == 5)
{
if (_stricmp(".aiff", extPtr))
- {
- free(name);
- return true; // skip, not the extension above
- }
+ goto skipEntry; // skip, not the extension above
}
else
{
- free(name);
- return true;
+ goto skipEntry;
}
}
break;
@@ -1350,15 +1325,11 @@
if (extLen == 3)
{
if (_stricmp(".xp", extPtr))
- {
- free(name);
- return true; // skip, not the extension above
- }
+ goto skipEntry; // skip, not the extension above
}
else
{
- free(name);
- return true;
+ goto skipEntry;
}
}
break;
@@ -1368,15 +1339,11 @@
if (extLen == 3)
{
if (_stricmp(".xt", extPtr))
- {
- free(name);
- return true; // skip, not the extension above
- }
+ goto skipEntry; // skip, not the extension above
}
else
{
- free(name);
- return true;
+ goto skipEntry;
}
}
break;
@@ -1385,6 +1352,10 @@
free(name);
return false; // "Show All Files" mode is enabled, don't skip entry
+
+skipEntry:
+ free(name);
+ return true;
}
static int8_t findFirst(DirRec *searchRec)
@@ -1927,6 +1898,8 @@
// free old buffer
freeDirRecBuffer();
+ UNICHAR_GETCWD(FReq_CurPathU, PATH_MAX);
+
// read first file
lastFindFileFlag = findFirst(&tmpBuffer);
if (lastFindFileFlag != LFF_DONE && lastFindFileFlag != LFF_SKIP)
@@ -1989,7 +1962,6 @@
okBoxThreadSafe(0, "System message", "Not enough memory!");
}
- UNICHAR_GETCWD(FReq_CurPathU, PATH_MAX);
editor.diskOpReadDone = true;
setMouseBusy(false);
--- a/src/ft2_diskop.h
+++ b/src/ft2_diskop.h
@@ -21,7 +21,7 @@
SMP_SAVE_MODE_WAV = 2
};
-int32_t getFileSize(UNICHAR *fileName);
+int32_t getFileSize(UNICHAR *fileNameU);
uint8_t getDiskOpItem(void);
void updateCurrSongFilename(void); // for window title
char *getCurrSongFilename(void); // for window title
--- a/src/ft2_gui.c
+++ b/src/ft2_gui.c
@@ -49,7 +49,8 @@
if (mouse.lastUsedObjectID == OBJECT_ID_NONE)
{
/* if last object ID is OBJECT_ID_NONE, check if we moved the
- ** sample data loop pins, and unstuck them if so */
+ ** sample data loop pins, and unstuck them if so
+ */
if (editor.ui.leftLoopPinMoving)
{
@@ -311,11 +312,11 @@
void charOut(uint16_t xPos, uint16_t yPos, uint8_t paletteIndex, char chr)
{
const uint8_t *srcPtr;
- uint32_t *dstPtr, pixVal;
+ uint32_t *dstPtr, pixVal, tmp;
assert(xPos < SCREEN_W && yPos < SCREEN_H);
- chr &= 0x7F;
+ chr &= 0x7F; // this is important to get the nordic glyphs in the font
if (chr == ' ')
return;
@@ -327,8 +328,10 @@
{
for (uint32_t x = 0; x < FONT1_CHAR_W; x++)
{
- if (srcPtr[x])
- dstPtr[x] = pixVal;
+ // carefully written like this to generate conditional move instructions (font data is hard to predict)
+ tmp = dstPtr[x];
+ if (srcPtr[x] != 0) tmp = pixVal;
+ dstPtr[x] = tmp;
}
srcPtr += FONT1_WIDTH;
@@ -343,7 +346,7 @@
assert(xPos < SCREEN_W && yPos < SCREEN_H);
- chr &= 0x7F;
+ chr &= 0x7F; // this is important to get the nordic glyphs in the font
if (chr == ' ')
return;
@@ -355,8 +358,8 @@
for (uint32_t y = 0; y < FONT1_CHAR_H; y++)
{
- for (uint32_t x = 0; x < 7; x++)
- dstPtr[x] = srcPtr[x] ? fg : bg;
+ for (uint32_t x = 0; x < FONT1_CHAR_W-1; x++)
+ dstPtr[x] = srcPtr[x] ? fg : bg; // compiles nicely into conditional move instructions
srcPtr += FONT1_WIDTH;
dstPtr += SCREEN_W;
@@ -376,32 +379,37 @@
void charOutShadow(uint16_t xPos, uint16_t yPos, uint8_t paletteIndex, uint8_t shadowPaletteIndex, char chr)
{
const uint8_t *srcPtr;
- uint32_t *dstPtr, pixVal1, pixVal2;
+ uint32_t *dstPtr1, *dstPtr2, pixVal1, pixVal2, tmp;
assert(xPos < SCREEN_W && yPos < SCREEN_H);
- chr &= 0x7F;
+ chr &= 0x7F; // this is important to get the nordic glyphs in the font
if (chr == ' ')
return;
pixVal1 = video.palette[paletteIndex];
pixVal2 = video.palette[shadowPaletteIndex];
- srcPtr = &font1Data[chr * FONT1_CHAR_W];
- dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos];
+ srcPtr = &font1Data[chr * FONT1_CHAR_W];
+ dstPtr1 = &video.frameBuffer[(yPos * SCREEN_W) + xPos];
+ dstPtr2 = dstPtr1 + (SCREEN_W+1);
for (uint32_t y = 0; y < FONT1_CHAR_H; y++)
{
for (uint32_t x = 0; x < FONT1_CHAR_W; x++)
{
- if (srcPtr[x])
- {
- dstPtr[x+(SCREEN_W+1)] = pixVal2;
- dstPtr[x] = pixVal1;
- }
+ // carefully written like this to generate conditional move instructions (font data is hard to predict)
+ tmp = dstPtr2[x];
+ if (srcPtr[x] != 0) tmp = pixVal2;
+ dstPtr2[x] = tmp;
+
+ tmp = dstPtr1[x];
+ if (srcPtr[x] != 0) tmp = pixVal1;
+ dstPtr1[x] = tmp;
}
srcPtr += FONT1_WIDTH;
- dstPtr += SCREEN_W;
+ dstPtr1 += SCREEN_W;
+ dstPtr2 += SCREEN_W;
}
}
@@ -409,7 +417,7 @@
{
const uint8_t *srcPtr;
uint16_t width;
- uint32_t *dstPtr, pixVal;
+ uint32_t *dstPtr, pixVal, tmp;
assert(xPos < SCREEN_W && yPos < SCREEN_H);
@@ -416,7 +424,7 @@
if (xPos > clipX)
return;
- chr &= 0x7F;
+ chr &= 0x7F; // this is important to get the nordic glyphs in the font
if (chr == ' ')
return;
@@ -432,8 +440,10 @@
{
for (uint32_t x = 0; x < width; x++)
{
- if (srcPtr[x])
- dstPtr[x] = pixVal;
+ // carefully written like this to generate conditional move instructions (font data is hard to predict)
+ tmp = dstPtr[x];
+ if (srcPtr[x] != 0) tmp = pixVal;
+ dstPtr[x] = tmp;
}
srcPtr += FONT1_WIDTH;
@@ -444,11 +454,11 @@
void bigCharOut(uint16_t xPos, uint16_t yPos, uint8_t paletteIndex, char chr)
{
const uint8_t *srcPtr;
- uint32_t *dstPtr, pixVal;
+ uint32_t *dstPtr, pixVal, tmp;
assert(xPos < SCREEN_W && yPos < SCREEN_H);
- chr &= 0x7F;
+ chr &= 0x7F; // this is important to get the nordic glyphs in the font
if (chr == ' ')
return;
@@ -460,8 +470,10 @@
{
for (uint32_t x = 0; x < FONT2_CHAR_W; x++)
{
- if (srcPtr[x])
- dstPtr[x] = pixVal;
+ // carefully written like this to generate conditional move instructions (font data is hard to predict)
+ tmp = dstPtr[x];
+ if (srcPtr[x] != 0) tmp = pixVal;
+ dstPtr[x] = tmp;
}
srcPtr += FONT2_WIDTH;
@@ -472,32 +484,37 @@
static void bigCharOutShadow(uint16_t xPos, uint16_t yPos, uint8_t paletteIndex, uint8_t shadowPaletteIndex, char chr)
{
const uint8_t *srcPtr;
- uint32_t *dstPtr, pixVal1, pixVal2;
+ uint32_t *dstPtr1, *dstPtr2, pixVal1, pixVal2, tmp;
assert(xPos < SCREEN_W && yPos < SCREEN_H);
- chr &= 0x7F;
+ chr &= 0x7F; // this is important to get the nordic glyphs in the font
if (chr == ' ')
return;
pixVal1 = video.palette[paletteIndex];
pixVal2 = video.palette[shadowPaletteIndex];
- srcPtr = &font2Data[chr * FONT2_CHAR_W];
- dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos];
+ srcPtr = &font2Data[chr * FONT2_CHAR_W];
+ dstPtr1 = &video.frameBuffer[(yPos * SCREEN_W) + xPos];
+ dstPtr2 = dstPtr1 + (SCREEN_W+1);
for (uint32_t y = 0; y < FONT2_CHAR_H; y++)
{
for (uint32_t x = 0; x < FONT2_CHAR_W; x++)
{
- if (srcPtr[x])
- {
- dstPtr[x+(SCREEN_W+1)] = pixVal2;
- dstPtr[x] = pixVal1;
- }
+ // carefully written like this to generate conditional move instructions (font data is hard to predict)
+ tmp = dstPtr2[x];
+ if (srcPtr[x] != 0) tmp = pixVal2;
+ dstPtr2[x] = tmp;
+
+ tmp = dstPtr1[x];
+ if (srcPtr[x] != 0) tmp = pixVal1;
+ dstPtr1[x] = tmp;
}
srcPtr += FONT2_WIDTH;
- dstPtr += SCREEN_W;
+ dstPtr1 += SCREEN_W;
+ dstPtr2 += SCREEN_W;
}
}
@@ -632,7 +649,7 @@
void hexOut(uint16_t xPos, uint16_t yPos, uint8_t paletteIndex, uint32_t val, uint8_t numDigits)
{
const uint8_t *srcPtr;
- uint32_t *dstPtr, pixVal;
+ uint32_t *dstPtr, pixVal, tmp;
assert(xPos < SCREEN_W && yPos < SCREEN_H);
@@ -648,8 +665,10 @@
{
for (uint32_t x = 0; x < FONT6_CHAR_W; x++)
{
- if (srcPtr[x])
- dstPtr[x] = pixVal;
+ // carefully written like this to generate conditional move instructions (font data is hard to predict)
+ tmp = dstPtr[x];
+ if (srcPtr[x] != 0) tmp = pixVal;
+ dstPtr[x] = tmp;
}
srcPtr += FONT6_WIDTH;
@@ -680,7 +699,7 @@
for (uint32_t y = 0; y < FONT6_CHAR_H; y++)
{
for (uint32_t x = 0; x < FONT6_CHAR_W; x++)
- dstPtr[x] = srcPtr[x] ? fg : bg;
+ dstPtr[x] = srcPtr[x] ? fg : bg; // compiles nicely into conditional move instructions
srcPtr += FONT6_WIDTH;
dstPtr += SCREEN_W;
@@ -935,13 +954,13 @@
void showTopLeftMainScreen(bool restoreScreens)
{
- editor.ui.diskOpShown = false;
+ editor.ui.diskOpShown = false;
editor.ui.sampleEditorExtShown = false;
- editor.ui.instEditorExtShown = false;
- editor.ui.transposeShown = false;
- editor.ui.advEditShown = false;
- editor.ui.wavRendererShown = false;
- editor.ui.trimScreenShown = false;
+ editor.ui.instEditorExtShown = false;
+ editor.ui.transposeShown = false;
+ editor.ui.advEditShown = false;
+ editor.ui.wavRendererShown = false;
+ editor.ui.trimScreenShown = false;
editor.ui.scopesShown = true;
if (restoreScreens)
@@ -949,13 +968,13 @@
switch (editor.ui.oldTopLeftScreen)
{
default: break;
- case 1: editor.ui.diskOpShown = true; break;
+ case 1: editor.ui.diskOpShown = true; break;
case 2: editor.ui.sampleEditorExtShown = true; break;
- case 3: editor.ui.instEditorExtShown = true; break;
- case 4: editor.ui.transposeShown = true; break;
- case 5: editor.ui.advEditShown = true; break;
- case 6: editor.ui.wavRendererShown = true; break;
- case 7: editor.ui.trimScreenShown = true; break;
+ case 3: editor.ui.instEditorExtShown = true; break;
+ case 4: editor.ui.transposeShown = true; break;
+ case 5: editor.ui.advEditShown = true; break;
+ case 6: editor.ui.wavRendererShown = true; break;
+ case 7: editor.ui.trimScreenShown = true; break;
}
if (editor.ui.oldTopLeftScreen > 0)
@@ -1205,10 +1224,22 @@
{
editor.ui.scopesShown = false;
- if (editor.ui.aboutScreenShown) showAboutScreen();
- else if (editor.ui.configScreenShown) showConfigScreen();
- else if (editor.ui.helpScreenShown) showHelpScreen();
- else if (editor.ui.nibblesShown) showNibblesScreen();
+ if (editor.ui.aboutScreenShown)
+ {
+ showAboutScreen();
+ }
+ else if (editor.ui.configScreenShown)
+ {
+ showConfigScreen();
+ }
+ else if (editor.ui.helpScreenShown)
+ {
+ showHelpScreen();
+ }
+ else if (editor.ui.nibblesShown)
+ {
+ showNibblesScreen();
+ }
else
{
showTopLeftMainScreen(restoreScreens); // updates editor.ui.scopesShown
--- a/src/ft2_header.h
+++ b/src/ft2_header.h
@@ -12,7 +12,7 @@
#endif
#include "ft2_replayer.h"
-#define PROG_VER_STR "1.07"
+#define PROG_VER_STR "1.08"
// do NOT change these! It will only mess things up...
@@ -122,6 +122,7 @@
UNICHAR *tmpFilenameU, *tmpInstrFilenameU; // used by saving/loading threads
UNICHAR *configFileLocation, *audioDevConfigFileLocation, *midiConfigFileLocation;
+ volatile bool mainLoopOngoing;
volatile bool busy, scopeThreadMutex, programRunning, wavIsRendering, wavReachedEndFlag;
volatile bool updateCurSmp, updateCurInstr, diskOpReadDir, diskOpReadDone, updateWindowTitle;
volatile uint8_t loadMusicEvent;
--- a/src/ft2_inst_ed.c
+++ b/src/ft2_inst_ed.c
@@ -18,6 +18,7 @@
#include "ft2_video.h"
#include "ft2_sample_loader.h"
#include "ft2_diskop.h"
+#include "ft2_module_loader.h"
#ifdef _MSC_VER
#pragma pack(push)
@@ -71,7 +72,7 @@
uint8_t ta[96];
int16_t envVP[12][2], envPP[12][2];
uint8_t envVPAnt, envPPAnt, envVSust, envVRepS, envVRepE, envPSust, envPRepS;
- uint8_t envPRepE, envVtyp, envPtyp, vibTyp, vibSweep, vibDepth, vibRate;
+ uint8_t envPRepE, envVTyp, envPTyp, vibTyp, vibSweep, vibDepth, vibRate;
uint16_t fadeOut;
uint8_t midiOn, midiChannel;
int16_t midiProgram, midiBend;
@@ -2004,7 +2005,7 @@
le = -1;
}
- curEnvP = ins->envVP;
+ curEnvP = ins->envVP;
selected = editor.currVolEnvPoint;
}
else
@@ -2026,7 +2027,7 @@
le = -1;
}
- curEnvP = ins->envPP;
+ curEnvP = ins->envPP;
selected = editor.currPanEnvPoint;
}
@@ -2933,11 +2934,13 @@
static int32_t SDLCALL saveInstrThread(void *ptr)
{
int16_t n;
+ int32_t i;
size_t result;
FILE *f;
instrXIHeaderTyp ih;
- sampleTyp *srcSmp;
- sampleHeaderTyp *dstSmpHdr;
+ instrTyp *ins;
+ sampleTyp *s;
+ sampleHeaderTyp *dst;
(void)ptr;
@@ -2948,7 +2951,7 @@
}
n = getUsedSamples(saveInstrNr);
- if (n == 0)
+ if (n == 0 || instr[saveInstrNr] == NULL)
{
okBoxThreadSafe(0, "System message", "Instrument is empty!");
return false;
@@ -2961,7 +2964,8 @@
return false;
}
- memset(&ih, 0, sizeof (ih));
+ memset(&ih, 0, sizeof (ih)); // important, also clears reserved stuff
+
memcpy(ih.sig, "Extended Instrument: ", 21);
memset(ih.name, ' ', 22);
memcpy(ih.name, song.instrName[saveInstrNr], strlen(song.instrName[saveInstrNr]));
@@ -2969,17 +2973,53 @@
memcpy(ih.progName, PROG_NAME_STR, 20);
ih.ver = 0x0102;
- memcpy(ih.ta, &instr[saveInstrNr], INSTR_SIZE);
+ // copy over instrument struct data to instrument header
+ ins = instr[saveInstrNr];
+ memcpy(ih.ta, ins->ta, 96);
+ memcpy(ih.envVP, ins->envVP, 12*2*sizeof(int16_t));
+ memcpy(ih.envPP, ins->envPP, 12*2*sizeof(int16_t));
+ ih.envVPAnt = ins->envVPAnt;
+ ih.envPPAnt = ins->envPPAnt;
+ ih.envVSust = ins->envVSust;
+ ih.envVRepS = ins->envVRepS;
+ ih.envVRepE = ins->envVRepE;
+ ih.envPSust = ins->envPSust;
+ ih.envPRepS = ins->envPRepS;
+ ih.envPRepE = ins->envPRepE;
+ ih.envVTyp = ins->envVTyp;
+ ih.envPTyp = ins->envPTyp;
+ ih.vibTyp = ins->vibTyp;
+ ih.vibSweep = ins->vibSweep;
+ ih.vibDepth = ins->vibDepth;
+ ih.vibRate = ins->vibRate;
+ ih.fadeOut = ins->fadeOut;
+ ih.midiOn = ins->midiOn ? 1 : 0;
+ ih.midiChannel = ins->midiChannel;
+ ih.midiProgram = ins->midiProgram;
+ ih.midiBend = ins->midiBend;
+ ih.mute = ins->mute ? 1 : 0;
ih.antSamp = n;
- for (int16_t i = 0; i < n; i++)
+ // copy over sample struct datas to sample headers
+ for (i = 0; i < n; i++)
{
- srcSmp = &instr[saveInstrNr]->samp[i];
- dstSmpHdr = &ih.samp[i];
+ s = &instr[saveInstrNr]->samp[i];
+ dst = &ih.samp[i];
- memcpy(&dstSmpHdr->len, &srcSmp->len, 12+4+2 + strlen(srcSmp->name));
- if (srcSmp->pek == NULL)
- dstSmpHdr->len = 0;
+ dst->len = s->len;
+ dst->repS = s->repS;
+ dst->repL = s->repL;
+ dst->vol = s->vol;
+ dst->fine = s->fine;
+ dst->typ = s->typ;
+ dst->pan = s->pan;
+ dst->relTon = s->relTon;
+
+ dst->nameLen = (uint8_t)strlen(s->name);
+ memcpy(dst->name, s->name, 22);
+
+ if (s->pek == NULL)
+ dst->len = 0;
}
result = fwrite(&ih, INSTR_XI_HEADER_SIZE + (ih.antSamp * sizeof (sampleHeaderTyp)), 1, f);
@@ -2991,20 +3031,20 @@
}
pauseAudio();
- for (int16_t i = 0; i < n; i++)
+ for (i = 0; i < n; i++)
{
- srcSmp = &instr[saveInstrNr]->samp[i];
- if (srcSmp->pek != NULL)
+ s = &instr[saveInstrNr]->samp[i];
+ if (s->pek != NULL && s->len > 0)
{
- restoreSample(srcSmp);
- samp2Delta(srcSmp->pek, srcSmp->len, srcSmp->typ);
+ restoreSample(s);
+ samp2Delta(s->pek, s->len, s->typ);
- result = fwrite(srcSmp->pek, 1, srcSmp->len, f);
+ result = fwrite(s->pek, 1, s->len, f);
- delta2Samp(srcSmp->pek, srcSmp->len, srcSmp->typ);
- fixSample(srcSmp);
+ delta2Samp(s->pek, s->len, s->typ);
+ fixSample(s);
- if (result != (size_t)srcSmp->len) // write not OK
+ if (result != (size_t)s->len) // write not OK
{
resumeAudio();
fclose(f);
@@ -3044,10 +3084,7 @@
static int16_t getPATNote(int32_t freq)
{
- double dFreq;
-
- dFreq = ((log(freq / (440.0 * 1000.0)) / M_LN2) * 12.0) + 48.0 + 9.0;
- return (int16_t)round(dFreq);
+ return (int16_t)round(((log(freq / (440.0 * 1000.0)) / M_LN2) * 12.0) + 48.0 + 9.0);
}
static int32_t SDLCALL loadInstrThread(void *ptr)
@@ -3055,11 +3092,13 @@
bool stereoWarning;
int8_t *newPtr;
int16_t a, b;
+ int32_t i, j;
double dFreq;
FILE *f;
instrXIHeaderTyp ih;
instrPATHeaderTyp ih_PAT;
instrPATWaveHeaderTyp ih_PATWave;
+ sampleHeaderTyp *src;
sampleTyp *s;
instrTyp *ins;
@@ -3093,14 +3132,16 @@
goto loadDone;
}
- if (ih.ver == 0x0101)
+ if (ih.ver == 0x0101) // not even FT2.01 can save old v1.01 .XI files, so I have no way to verify this.
{
- fseek(f, -2 - 15 - 1 - 2, SEEK_CUR);
+ fseek(f, -20, SEEK_CUR);
ih.antSamp = ih.midiProgram;
- memset(&ih.midiProgram, 0, 2+15+1+2);
+ ih.midiProgram = 0;
+ ih.midiBend = 0;
+ ih.mute = false;
}
- if (ih.antSamp > 16)
+ if (ih.antSamp > MAX_SMP_PER_INST)
{
okBoxThreadSafe(0, "System message", "Incompatible instrument!");
goto loadDone;
@@ -3122,44 +3163,67 @@
goto loadDone;
}
+ // copy instrument header elements to our instrument struct
+
+ ins = instr[editor.curInstr];
+ memcpy(ins->ta, ih.ta, 96);
+ memcpy(ins->envVP, ih.envVP, 12*2*sizeof(int16_t));
+ memcpy(ins->envPP, ih.envPP, 12*2*sizeof(int16_t));
+ ins->envVPAnt = ih.envVPAnt;
+ ins->envPPAnt = ih.envPPAnt;
+ ins->envVSust = ih.envVSust;
+ ins->envVRepS = ih.envVRepS;
+ ins->envVRepE = ih.envVRepE;
+ ins->envPSust = ih.envPSust;
+ ins->envPRepS = ih.envPRepS;
+ ins->envPRepE = ih.envPRepE;
+ ins->envVTyp = ih.envVTyp;
+ ins->envPTyp = ih.envPTyp;
+ ins->vibTyp = ih.vibTyp;
+ ins->vibSweep = ih.vibSweep;
+ ins->vibDepth = ih.vibDepth;
+ ins->vibRate = ih.vibRate;
+ ins->fadeOut = ih.fadeOut;
+ ins->midiOn = (ih.midiOn > 0) ? true : false;
+ ins->midiChannel = ih.midiChannel;
+ ins->midiProgram = ih.midiProgram;
+ ins->midiBend = ih.midiBend;
+ ins->mute = (ih.mute > 0) ? true : false;
+ ins->antSamp = ih.antSamp; // used in loadInstrSample()
+
// sanitize stuff for broken/unsupported instruments
- ih.midiProgram = CLAMP(ih.midiProgram, 0, 127);
- ih.midiBend = CLAMP(ih.midiBend, 0, 36);
+ ins->midiProgram = CLAMP(ins->midiProgram, 0, 127);
+ ins->midiBend = CLAMP(ins->midiBend, 0, 36);
- if (ih.midiChannel > 15) ih.midiChannel = 15;
- if (ih.mute != 1) ih.mute = 0;
- if (ih.midiOn!= 1) ih.midiOn = 0;
- if (ih.vibDepth > 0x0F) ih.vibDepth = 0x0F;
- if (ih.vibRate > 0x3F) ih.vibRate = 0x3F;
- if (ih.vibTyp > 3) ih.vibTyp = 0;
+ if (ins->midiChannel > 15) ins->midiChannel = 15;
+ if (ins->vibDepth > 0x0F) ins->vibDepth = 0x0F;
+ if (ins->vibRate > 0x3F) ins->vibRate = 0x3F;
+ if (ins->vibTyp > 3) ins->vibTyp = 0;
- for (int16_t i = 0; i < 96; i++)
+ for (i = 0; i < 96; i++)
{
- if (ih.ta[i] > 15)
- ih.ta[i] = 15;
+ if (ins->ta[i] > 15)
+ ins->ta[i] = 15;
}
- if (ih.envVPAnt > 12) ih.envVPAnt = 12;
- if (ih.envVRepS > 11) ih.envVRepS = 11;
- if (ih.envVRepE > 11) ih.envVRepE = 11;
- if (ih.envVSust > 11) ih.envVSust = 11;
- if (ih.envPPAnt > 12) ih.envPPAnt = 12;
- if (ih.envPRepS > 11) ih.envPRepS = 11;
- if (ih.envPRepE > 11) ih.envPRepE = 11;
- if (ih.envPSust > 11) ih.envPSust = 11;
+ if (ins->envVPAnt > 12) ins->envVPAnt = 12;
+ if (ins->envVRepS > 11) ins->envVRepS = 11;
+ if (ins->envVRepE > 11) ins->envVRepE = 11;
+ if (ins->envVSust > 11) ins->envVSust = 11;
+ if (ins->envPPAnt > 12) ins->envPPAnt = 12;
+ if (ins->envPRepS > 11) ins->envPRepS = 11;
+ if (ins->envPRepE > 11) ins->envPRepE = 11;
+ if (ins->envPSust > 11) ins->envPSust = 11;
- for (int16_t i = 0; i < 12; i++)
+ for (i = 0; i < 12; i++)
{
- if ((uint16_t)ih.envVP[i][0] > 32767) ih.envVP[i][0] = 32767;
- if ((uint16_t)ih.envPP[i][0] > 32767) ih.envPP[i][0] = 32767;
- if ((uint16_t)ih.envVP[i][1] > 64) ih.envVP[i][1] = 64;
- if ((uint16_t)ih.envPP[i][1] > 63) ih.envPP[i][1] = 63;
+ if ((uint16_t)ins->envVP[i][0] > 32767) ins->envVP[i][0] = 32767;
+ if ((uint16_t)ins->envPP[i][0] > 32767) ins->envPP[i][0] = 32767;
+ if ((uint16_t)ins->envVP[i][1] > 64) ins->envVP[i][1] = 64;
+ if ((uint16_t)ins->envPP[i][1] > 63) ins->envPP[i][1] = 63;
+
}
- // ----------------------------------------
-
- memcpy(instr[editor.curInstr]->ta, ih.ta, INSTR_SIZE);
-
if (fread(ih.samp, sizeof (sampleHeaderTyp) * ih.antSamp, 1, f) != 1)
{
freeInstr(editor.curInstr);
@@ -3168,23 +3232,50 @@
goto loadDone;
}
- for (int16_t i = 0; i < ih.antSamp; i++)
- memcpy(&instr[editor.curInstr]->samp[i], &ih.samp[i], 12 + 4 + 24);
+ for (i = 0; i < ih.antSamp; i++)
+ {
+ s = &instr[editor.curInstr]->samp[i];
+ src = &ih.samp[i];
+
+ // copy sample header elements to our sample struct
+
+ s->len = src->len;
+ s->repS = src->repS;
+ s->repL = src->repL;
+ s->vol = src->vol;
+ s->fine = src->fine;
+ s->typ = src->typ;
+ s->pan = src->pan;
+ s->relTon = src->relTon;
+ memcpy(s->name, src->name, 22);
+ s->name[22] = '\0';
+
+ // dst->pek is set up later
+
+ // trim off spaces at end of name
+ for (j = 21; j >= 0; j--)
+ {
+ if (s->name[j] == ' ' || s->name[j] == 0x1A)
+ s->name[j] = '\0';
+ else
+ break;
+ }
+
+ // sanitize stuff broken/unsupported samples
+ if (s->vol > 64)
+ s->vol = 64;
+
+ s->relTon = CLAMP(s->relTon, -48, 71);
+ }
}
- for (int16_t i = 0; i < ih.antSamp; i++)
+ for (i = 0; i < ih.antSamp; i++)
{
s = &instr[editor.curInstr]->samp[i];
- // sanitize stuff for malicious modules
- if (s->vol > 64)
- s->vol = 64;
-
- s->relTon = CLAMP(s->relTon, -48, 71);
-
- // if a sample has both forward loop and pingpong loop set, make it pingpong loop only (FT2 behavior)
+ // if a sample has both forward loop and pingpong loop set, make it pingpong loop only (FT2 mixer behavior)
if ((s->typ & 3) == 3)
- s->typ = 2;
+ s->typ &= 0xFE;
if (s->len > 0)
{
@@ -3223,6 +3314,7 @@
stereoWarning = true;
}
+ checkSampleRepeat(s);
fixSample(s);
}
}
@@ -3256,7 +3348,7 @@
memset(song.instrName[editor.curInstr], 0, 22 + 1);
memcpy(song.instrName[editor.curInstr], ih_PAT.instrName, 16);
- for (int16_t i = 0; i < ih_PAT.antSamp; i++)
+ for (i = 0; i < ih_PAT.antSamp; i++)
{
s = &instr[editor.curInstr]->samp[i];
ins = instr[editor.curInstr];
@@ -3281,8 +3373,14 @@
if (i == 0)
{
ins->vibSweep = ih_PATWave.vibSweep;
- ins->vibRate = ih_PATWave.vibRate / 2; if (ins->vibRate > 0x3F) ins->vibRate = 0x3F;
- ins->vibDepth = ih_PATWave.vibDepth / 2; if (ins->vibDepth > 0x0F) ins->vibDepth = 0x0F;
+
+ ins->vibRate = ih_PATWave.vibRate / 2;
+ if (ins->vibRate > 0x3F)
+ ins->vibRate = 0x3F;
+
+ ins->vibDepth = ih_PATWave.vibDepth / 2;
+ if (ins->vibDepth > 0x0F)
+ ins->vibDepth = 0x0F;
}
s = &instr[editor.curInstr]->samp[i];
@@ -3298,7 +3396,7 @@
s->typ |= 1; // forward loop
}
- s->pan = (ih_PATWave.pan == 8) ? 128 : (ih_PATWave.pan * 17); // FT2 does <<4 here, I don't like that!
+ s->pan = ih_PATWave.pan << 4;
s->len = ih_PATWave.waveSize;
s->repS = ih_PATWave.repS;
@@ -3323,13 +3421,16 @@
dFreq = round((1.0 + ih_PATWave.fineTune / 512.0) * ih_PATWave.sampleRate);
tuneSample(s, (int32_t)dFreq);
- s->relTon -= (int8_t)(getPATNote(ih_PATWave.rootFrq) - (12 * 3));
- s->relTon = CLAMP(s->relTon, -48, 71);
+ s->relTon -= (int8_t)(getPATNote(ih_PATWave.rootFrq) - (12*3));
+ s->relTon = CLAMP(s->relTon, -48, 71);
- a = getPATNote(ih_PATWave.lowFrq); a = CLAMP(a, 0, 95);
- b = getPATNote(ih_PATWave.highFreq); b = CLAMP(b, 0, 95);
+ a = getPATNote(ih_PATWave.lowFrq);
+ a = CLAMP(a, 0, 95);
- for (int16_t j = a; j <= b; j++)
+ b = getPATNote(ih_PATWave.highFreq);
+ b = CLAMP(b, 0, 95);
+
+ for (j = a; j <= b; j++)
ins->ta[j] = (uint8_t)i;
if (fread(s->pek, ih_PATWave.waveSize, 1, f) != 1)
--- a/src/ft2_main.c
+++ b/src/ft2_main.c
@@ -221,6 +221,7 @@
setupWaitVBL();
handleModuleLoadFromArg(argc, argv);
+ editor.mainLoopOngoing = true;
while (editor.programRunning)
{
beginFPSCounter();
--- a/src/ft2_module_loader.c
+++ b/src/ft2_module_loader.c
@@ -10,7 +10,6 @@
#include <unistd.h>
#endif
#include "ft2_header.h"
-#include "ft2_audio.h"
#include "ft2_config.h"
#include "ft2_scopes.h"
#include "ft2_trim.h"
@@ -22,7 +21,6 @@
#include "ft2_diskop.h"
#include "ft2_sample_loader.h"
#include "ft2_mouse.h"
-#include "ft2_scopes.h"
#include "ft2_midi.h"
#include "ft2_events.h"
#include "ft2_video.h"
@@ -129,12 +127,13 @@
static void setupLoadedModule(void);
static void freeTmpModule(void);
static bool loadInstrHeader(FILE *f, uint16_t i);
-static void checkSampleRepeat(sampleTyp *s);
static bool loadInstrSample(FILE *f, uint16_t i);
void unpackPatt(uint8_t *dst, uint16_t inn, uint16_t len, uint8_t antChn);
static bool tmpPatternEmpty(uint16_t nr);
static bool loadPatterns(FILE *f, uint16_t antPtn);
+void checkSampleRepeat(sampleTyp *s);
+
// ft2_replayer.c
extern const char modSig[32][5];
extern const uint16_t amigaPeriod[12*8];
@@ -162,7 +161,7 @@
return true;
}
-static bool loadMusicMOD(FILE *f, uint32_t fileLength)
+static bool loadMusicMOD(FILE *f, uint32_t fileLength, bool fromExternalThread)
{
char ID[16];
bool modIsUST, modIsFEST, modIsNT;
@@ -173,7 +172,10 @@
sampleTyp *s;
songMOD31HeaderTyp h_MOD31;
songMOD15HeaderTyp h_MOD15;
+ int16_t (*showMsg)(int16_t, const char *, const char *);
+ showMsg = fromExternalThread ? okBoxThreadSafe : okBox;
+
// start loading MOD
loadedFormat = FORMAT_MOD;
@@ -187,7 +189,7 @@
// since .mod is the last format tested, check if the file is an .it module (Impulse Tracker)
if (!memcmp(ID, "IMPM", 4) && bytes[0] == 0)
{
- okBoxThreadSafe(0, "System message", "Error: Impulse Tracker modules are not supported!");
+ showMsg(0, "System message", "Error: Impulse Tracker modules are not supported!");
goto modLoadError;
}
@@ -194,19 +196,19 @@
// check if the file to load is a WAV, if so reject it
if (!memcmp(ID, "RIFF", 4) && !memcmp(&ID[8], "WAVEfmt", 7))
{
- okBoxThreadSafe(0, "System message", "Error: Can't load a .wav file as a module!");
+ showMsg(0, "System message", "Error: Can't load a .wav file as a module!");
goto modLoadError;
}
if (fileLength < 1596 || fileLength > 20842494) // minimum and maximum possible size for an FT2 .mod
{
- okBoxThreadSafe(0, "System message", "Error: This file is either not a module, or is not supported.");
+ showMsg(0, "System message", "Error: This file is either not a module, or is not supported.");
goto modLoadError;
}
if (fread(&h_MOD31, 1, sizeof (h_MOD31), f) != sizeof (h_MOD31))
{
- okBoxThreadSafe(0, "System message", "Error: This file is either not a module, or is not supported.");
+ showMsg(0, "System message", "Error: This file is either not a module, or is not supported.");
goto modLoadError;
}
@@ -253,7 +255,7 @@
// unsupported MOD
if (j == -1)
{
- okBoxThreadSafe(0, "System message", "Error: This file is either not a module, or is not supported.");
+ showMsg(0, "System message", "Error: This file is either not a module, or is not supported.");
goto modLoadError;
}
@@ -262,7 +264,7 @@
modIsUST = false;
if (fileLength < sizeof (h_MOD31))
{
- okBoxThreadSafe(0, "System message", "Error: This file is either not a module, or is not supported.");
+ showMsg(0, "System message", "Error: This file is either not a module, or is not supported.");
goto modLoadError;
}
@@ -277,7 +279,7 @@
modIsUST = true;
if (fileLength < sizeof (h_MOD15))
{
- okBoxThreadSafe(0, "System message", "Error: This file is either not a module, or is not supported.");
+ showMsg(0, "System message", "Error: This file is either not a module, or is not supported.");
goto modLoadError;
}
@@ -284,7 +286,7 @@
fseek(f, 0, SEEK_SET);
if (fread(&h_MOD15, 1, sizeof (h_MOD15), f) != sizeof (h_MOD15))
{
- okBoxThreadSafe(0, "System message", "Error: This file is either not a module, or is not supported.");
+ showMsg(0, "System message", "Error: This file is either not a module, or is not supported.");
goto modLoadError;
}
@@ -297,7 +299,7 @@
if (songTmp.antChn == 0 || songTmp.len < 1)
{
- okBoxThreadSafe(0, "System message", "Error: This file is either not a module, or is not supported.");
+ showMsg(0, "System message", "Error: This file is either not a module, or is not supported.");
goto modLoadError;
}
@@ -306,7 +308,7 @@
if (songTmp.len > 128 || (modIsUST && (songTmp.repS == 0 || songTmp.repS > 220)))
{
- okBoxThreadSafe(0, "System message", "Error: This file is either not a module, or is not supported.");
+ showMsg(0, "System message", "Error: This file is either not a module, or is not supported.");
goto modLoadError;
}
@@ -352,7 +354,7 @@
pattTmp[a] = (tonTyp *)calloc((MAX_PATT_LEN * TRACK_WIDTH) + 16, 1);
if (pattTmp[a] == NULL)
{
- okBoxThreadSafe(0, "System message", "Not enough memory!");
+ showMsg(0, "System message", "Not enough memory!");
goto modLoadError;
}
@@ -365,7 +367,7 @@
if (fread(bytes, 1, 4, f) != 4)
{
- okBoxThreadSafe(0, "System message", "Error: This file is either not a module, or is not supported.");
+ showMsg(0, "System message", "Error: This file is either not a module, or is not supported.");
goto modLoadError;
}
@@ -472,7 +474,7 @@
if (!allocateTmpInstr(1 + a))
{
- okBoxThreadSafe(0, "System message", "Not enough memory!");
+ showMsg(0, "System message", "Not enough memory!");
goto modLoadError;
}
@@ -485,7 +487,7 @@
s->pek = (int8_t *)malloc(s->len + LOOP_FIX_LEN);
if (s->pek == NULL)
{
- okBoxThreadSafe(0, "System message", "Not enough memory!");
+ showMsg(0, "System message", "Not enough memory!");
goto modLoadError;
}
@@ -604,7 +606,7 @@
return (uint8_t)CLAMP(bpm, 32, 255); // result can be slightly off, but close enough...
}
-static bool loadMusicSTM(FILE *f, uint32_t fileLength)
+static bool loadMusicSTM(FILE *f, uint32_t fileLength, bool fromExternalThread)
{
bool check3xx;
uint8_t typ, tmp8, tempo;
@@ -614,18 +616,21 @@
tonTyp *ton;
sampleTyp *s;
songSTMHeaderTyp h_STM;
+ int16_t (*showMsg)(int16_t, const char *, const char *);
+ showMsg = fromExternalThread ? okBoxThreadSafe : okBox;
+
rewind(f);
// start loading STM
if (fread(&h_STM, 1, sizeof (h_STM), f) != sizeof (h_STM))
- return loadMusicMOD(f, fileLength); // file is not a .stm, try to load as .mod
+ return loadMusicMOD(f, fileLength, fromExternalThread); // file is not a .stm, try to load as .mod
if (memcmp(h_STM.sig, "!Scream!", 8) && memcmp(h_STM.sig, "BMOD2STM", 8) &&
memcmp(h_STM.sig, "WUZAMOD!", 8) && memcmp(h_STM.sig, "SWavePro", 8))
{
- return loadMusicMOD(f, fileLength); // file is not a .stm, try to load as .mod
+ return loadMusicMOD(f, fileLength, fromExternalThread); // file is not a .stm, try to load as .mod
}
loadedFormat = FORMAT_STM;
@@ -632,7 +637,7 @@
if (h_STM.verMinor == 0 || h_STM.typ != 2)
{
- okBoxThreadSafe(0, "System message", "Error loading .stm: Incompatible module!");
+ showMsg(0, "System message", "Error loading .stm: Incompatible module!");
goto stmLoadError;
}
@@ -677,7 +682,7 @@
pattTmp[i] = (tonTyp *)calloc((MAX_PATT_LEN * TRACK_WIDTH) + 16, 1);
if (pattTmp[i] == NULL)
{
- okBoxThreadSafe(0, "System message", "Not enough memory!");
+ showMsg(0, "System message", "Not enough memory!");
goto stmLoadError;
}
@@ -684,7 +689,7 @@
pattLensTmp[i] = 64;
if (fread(pattBuff, 64 * 4 * 4, 1, f) != 1)
{
- okBoxThreadSafe(0, "System message", "General I/O error during loading!");
+ showMsg(0, "System message", "General I/O error during loading!");
goto stmLoadError;
}
@@ -805,7 +810,7 @@
s->pek = (int8_t *)malloc(h_STM.instr[i].len + LOOP_FIX_LEN);
if (s->pek == NULL)
{
- okBoxThreadSafe(0, "System message", "Not enough memory!");
+ showMsg(0, "System message", "Not enough memory!");
goto stmLoadError;
}
@@ -835,7 +840,7 @@
if (fread(s->pek, s->len, 1, f) != 1)
{
- okBoxThreadSafe(0, "System message", "General I/O error during loading! Possibly corrupt module?");
+ showMsg(0, "System message", "General I/O error during loading! Possibly corrupt module?");
goto stmLoadError;
}
@@ -954,7 +959,7 @@
return channels;
}
-static bool loadMusicS3M(FILE *f, uint32_t dataLength)
+static bool loadMusicS3M(FILE *f, uint32_t dataLength, bool fromExternalThread)
{
int8_t *tmpSmp;
bool check3xx, illegalUxx;
@@ -968,7 +973,10 @@
sampleTyp *s;
songS3MHeaderTyp h_S3M;
songS3MinstrHeaderTyp h_S3MInstr;
+ int16_t (*showMsg)(int16_t, const char *, const char *);
+ showMsg = fromExternalThread ? okBoxThreadSafe : okBox;
+
stereoSamplesWarn = false;
rewind(f);
@@ -976,10 +984,10 @@
// start loading S3M
if (fread(&h_S3M, 1, sizeof (h_S3M), f) != sizeof (h_S3M))
- return loadMusicSTM(f, dataLength); // not a .s3m, try loading as .stm
+ return loadMusicSTM(f, dataLength, fromExternalThread); // not a .s3m, try loading as .stm
if (memcmp(h_S3M.id, "SCRM", 4))
- return loadMusicSTM(f, dataLength); // not a .s3m, try loading as .stm
+ return loadMusicSTM(f, dataLength, fromExternalThread); // not a .s3m, try loading as .stm
loadedFormat = FORMAT_S3M;
@@ -986,7 +994,7 @@
if (h_S3M.antInstr > MAX_INST || h_S3M.songTabLen > 256 || h_S3M.antPatt > 256 ||
h_S3M.typ != 16 || h_S3M.ver < 1 || h_S3M.ver > 2)
{
- okBoxThreadSafe(0, "System message", "Error loading .s3m: Incompatible module!");
+ showMsg(0, "System message", "Error loading .s3m: Incompatible module!");
goto s3mLoadError;
}
@@ -993,7 +1001,7 @@
memset(songTmp.songTab, 255, sizeof (songTmp.songTab));
if (fread(songTmp.songTab, h_S3M.songTabLen, 1, f) != 1)
{
- okBoxThreadSafe(0, "System message", "General I/O error during loading! Is the file in use?");
+ showMsg(0, "System message", "General I/O error during loading! Is the file in use?");
goto s3mLoadError;
}
@@ -1060,13 +1068,13 @@
if (fread(ha, ai + ai, 1, f) != 1)
{
- okBoxThreadSafe(0, "System message", "General I/O error during loading! Is the file in use?");
+ showMsg(0, "System message", "General I/O error during loading! Is the file in use?");
goto s3mLoadError;
}
if (fread(ptnOfs, ap + ap, 1, f) != 1)
{
- okBoxThreadSafe(0, "System message", "General I/O error during loading! Is the file in use?");
+ showMsg(0, "System message", "General I/O error during loading! Is the file in use?");
goto s3mLoadError;
}
@@ -1093,7 +1101,7 @@
if (fread(&j, 2, 1, f) != 1)
{
- okBoxThreadSafe(0, "System message", "General I/O error during loading! Is the file in use?");
+ showMsg(0, "System message", "General I/O error during loading! Is the file in use?");
goto s3mLoadError;
}
@@ -1102,7 +1110,7 @@
pattTmp[i] = (tonTyp *)calloc((MAX_PATT_LEN * TRACK_WIDTH) + 16, 1);
if (pattTmp[i] == NULL)
{
- okBoxThreadSafe(0, "System message", "Not enough memory!");
+ showMsg(0, "System message", "Not enough memory!");
goto s3mLoadError;
}
@@ -1109,7 +1117,7 @@
pattLensTmp[i] = 64;
if (fread(pattBuff, j, 1, f) != 1)
{
- okBoxThreadSafe(0, "System message", "General I/O error during loading! Is the file in use?");
+ showMsg(0, "System message", "General I/O error during loading! Is the file in use?");
goto s3mLoadError;
}
@@ -1394,7 +1402,7 @@
if (fread(&h_S3MInstr, 1, sizeof (h_S3MInstr), f) != sizeof (h_S3MInstr))
{
- okBoxThreadSafe(0, "System message", "Not enough memory!");
+ showMsg(0, "System message", "Not enough memory!");
goto s3mLoadError;
}
@@ -1412,7 +1420,7 @@
if (h_S3MInstr.typ > 1)
{
- okBoxThreadSafe(0, "System message", "Error loading .s3m: Incompatible module!");
+ showMsg(0, "System message", "Error loading .s3m: Incompatible module!");
goto s3mLoadError;
}
else if (h_S3MInstr.typ == 1)
@@ -1419,7 +1427,7 @@
{
if ((h_S3MInstr.flags & (255-1-2-4)) != 0 || h_S3MInstr.pack != 0)
{
- okBoxThreadSafe(0, "System message", "Error loading .s3m: Incompatible module!");
+ showMsg(0, "System message", "Error loading .s3m: Incompatible module!");
goto s3mLoadError;
}
else if (h_S3MInstr.memSeg > 0 && h_S3MInstr.len > 0)
@@ -1426,7 +1434,7 @@
{
if (!allocateTmpInstr(1 + i))
{
- okBoxThreadSafe(0, "System message", "Not enough memory!");
+ showMsg(0, "System message", "Not enough memory!");
goto s3mLoadError;
}
@@ -1447,7 +1455,7 @@
tmpSmp = (int8_t *)malloc(len + LOOP_FIX_LEN);
if (tmpSmp == NULL)
{
- okBoxThreadSafe(0, "System message", "Not enough memory!");
+ showMsg(0, "System message", "Not enough memory!");
goto s3mLoadError;
}
@@ -1489,7 +1497,7 @@
if (fread(tmpSmp, len, 1, f) != 1)
{
free(tmpSmp);
- okBoxThreadSafe(0, "System message", "General I/O error during loading! Is the file in use?");
+ showMsg(0, "System message", "General I/O error during loading! Is the file in use?");
goto s3mLoadError;
}
@@ -1503,7 +1511,7 @@
if (s->pek == NULL)
{
free(tmpSmp);
- okBoxThreadSafe(0, "System message", "Not enough memory!");
+ showMsg(0, "System message", "Not enough memory!");
goto s3mLoadError;
}
@@ -1521,7 +1529,7 @@
if (s->pek == NULL)
{
free(tmpSmp);
- okBoxThreadSafe(0, "System message", "Not enough memory!");
+ showMsg(0, "System message", "Not enough memory!");
goto s3mLoadError;
}
@@ -1537,7 +1545,7 @@
}
if (stereoSamplesWarn)
- okBoxThreadSafe(0, "System message", "Stereo samples were found and will be converted to mono.");
+ showMsg(0, "System message", "Stereo samples were found and will be converted to mono.");
// non-FT2: fix overflown 9xx and illegal 3xx slides
@@ -1611,7 +1619,7 @@
songTmp.antChn = countS3MChannels(ap);
if (!(config.dontShowAgainFlags & DONT_SHOW_S3M_LOAD_WARNING_FLAG))
- okBoxThreadSafe(6, "System message", "Warning: S3M channel panning is not compatible with FT2!");
+ showMsg(6, "System message", "Warning: S3M channel panning is not compatible with FT2!");
moduleLoaded = true;
return true;
@@ -1623,7 +1631,7 @@
return false;
}
-static int32_t SDLCALL loadMusicThread(void *ptr)
+bool doLoadMusic(bool fromExternalThread)
{
char tmpText[128];
int16_t k;
@@ -1631,8 +1639,9 @@
uint32_t filelength;
songHeaderTyp h;
FILE *f;
+ int16_t (*showMsg)(int16_t, const char *, const char *);
- (void)ptr;
+ showMsg = fromExternalThread ? okBoxThreadSafe : okBox;
stereoSamplesWarn = false;
linearFreqTable = false;
@@ -1639,7 +1648,7 @@
if (editor.tmpFilenameU == NULL)
{
- okBoxThreadSafe(0, "System message", "General I/O error during loading! Is the file in use?");
+ showMsg(0, "System message", "Generic memory fault during loading!");
moduleFailedToLoad = true;
return false;
}
@@ -1647,7 +1656,7 @@
f = UNICHAR_FOPEN(editor.tmpFilenameU, "rb");
if (f == NULL)
{
- okBoxThreadSafe(0, "System message", "General I/O error during loading! Is the file in use?");
+ showMsg(0, "System message", "General I/O error during loading! Is the file in use? Does it exist?");
moduleFailedToLoad = true;
return false;
}
@@ -1658,10 +1667,10 @@
// start loading
if (fread(&h, 1, sizeof (h), f) != sizeof (h))
- return loadMusicS3M(f, filelength); // not a .xm file, try to load as .s3m
+ return loadMusicS3M(f, filelength, fromExternalThread); // not a .xm file, try to load as .s3m
if (memcmp(h.sig, "Extended Module: ", 17))
- return loadMusicS3M(f, filelength); // not a .xm file, try to load as .s3m
+ return loadMusicS3M(f, filelength, fromExternalThread); // not a .xm file, try to load as .s3m
loadedFormat = FORMAT_XM;
@@ -1671,7 +1680,7 @@
sprintf(tmpText, "Error loading .xm: Unsupported XM version (v%1d.%1d%1d)",
'0' + (((h.ver >> 8) & 0x0F) % 10), '0' + (((h.ver >> 4) & 0x0F)) % 10, '0' + ((h.ver & 0x0F)) % 10);
- okBoxThreadSafe(0, "System message", tmpText);
+ showMsg(0, "System message", tmpText);
moduleFailedToLoad = true;
return false;
@@ -1679,29 +1688,29 @@
if (h.len > MAX_ORDERS)
{
- okBoxThreadSafe(0, "System message", "Error loading .xm: The song has more than 256 orders!");
+ showMsg(0, "System message", "Error loading .xm: The song has more than 256 orders!");
goto xmLoadError;
}
if (h.antPtn > MAX_PATTERNS)
{
- okBoxThreadSafe(0, "System message", "Error loading .xm: The song has more than 256 patterns!");
+ showMsg(0, "System message", "Error loading .xm: The song has more than 256 patterns!");
goto xmLoadError;
}
if (h.antChn == 0 || h.antChn > MAX_VOICES)
{
- okBoxThreadSafe(0, "System message", "Error loading .xm: Incompatible amount of channels!");
+ showMsg(0, "System message", "Error loading .xm: Incompatible amount of channels!");
goto xmLoadError;
}
if (h.antInstrs > MAX_INST)
- okBoxThreadSafe(0, "System message", "This module has over 128 instruments! Only the first 128 will be loaded.");
+ showMsg(0, "System message", "This module has over 128 instruments! Only the first 128 will be loaded.");
fseek(f, 60 + h.headerSize, SEEK_SET);
if (filelength != 336 && feof(f)) // 336 in length at this point = empty XM
{
- okBoxThreadSafe(0, "System message", "Error loading .xm: The module is empty!");
+ showMsg(0, "System message", "Error loading .xm: The module is empty!");
goto xmLoadError;
}
@@ -1747,7 +1756,7 @@
{
if (!loadInstrHeader(f, i))
{
- okBoxThreadSafe(0, "System message", "Error loading .xm: Either a corrupt or a non-supported module!");
+ showMsg(0, "System message", "Error loading .xm: Either a corrupt or a non-supported module!");
goto xmLoadError;
}
}
@@ -1762,7 +1771,7 @@
{
if (!loadInstrSample(f, i))
{
- okBoxThreadSafe(0, "System message", "Not enough memory!");
+ showMsg(0, "System message", "Not enough memory!");
goto xmLoadError;
}
}
@@ -1781,13 +1790,13 @@
{
if (!loadInstrHeader(f, i))
{
- okBoxThreadSafe(0, "System message", "Error loading .xm: Either a corrupt or a non-supported module!");
+ showMsg(0, "System message", "Error loading .xm: Either a corrupt or a non-supported module!");
goto xmLoadError;
}
if (!loadInstrSample(f, i))
{
- okBoxThreadSafe(0, "System message", "Not enough memory!");
+ showMsg(0, "System message", "Not enough memory!");
goto xmLoadError;
}
}
@@ -1794,7 +1803,7 @@
}
if (stereoSamplesWarn)
- okBoxThreadSafe(0, "System message", "Stereo samples were found and will be converted to mono.");
+ showMsg(0, "System message", "Stereo samples were found and will be converted to mono.");
fclose(f);
@@ -1808,6 +1817,12 @@
return false;
}
+static int32_t SDLCALL loadMusicThread(void *ptr)
+{
+ (void)ptr;
+ return doLoadMusic(true);
+}
+
void loadMusic(UNICHAR *filenameU)
{
if (musicIsLoading)
@@ -1844,13 +1859,13 @@
SDL_DetachThread(thread);
}
-bool loadMusicUnthreaded(UNICHAR *filenameU) // for development testing
+bool loadMusicUnthreaded(UNICHAR *filenameU, bool autoPlay)
{
- if (editor.tmpFilenameU == NULL)
+ if (filenameU == NULL || editor.tmpFilenameU == NULL)
return false;
// clear deprecated pointers from possible last loading session (super important)
- memset(pattTmp, 0, sizeof (pattTmp));
+ memset(pattTmp, 0, sizeof (pattTmp));
memset(instrTmp, 0, sizeof (instrTmp));
// prevent stuck instrument names from previous module
@@ -1861,12 +1876,15 @@
UNICHAR_STRCPY(editor.tmpFilenameU, filenameU);
- loadMusicThread(NULL);
editor.loadMusicEvent = EVENT_NONE;
+ doLoadMusic(false);
if (moduleLoaded)
{
setupLoadedModule();
+ if (autoPlay)
+ startPlaying(PLAYMODE_SONG, 0);
+
return true;
}
@@ -1910,6 +1928,8 @@
uint8_t j;
uint32_t readSize;
instrHeaderTyp ih;
+ instrTyp *ins;
+ sampleHeaderTyp *src;
sampleTyp *s;
memset(&ih, 0, INSTR_HEADER_SIZE);
@@ -1921,7 +1941,7 @@
readSize = INSTR_HEADER_SIZE;
// load instrument data into temp buffer
- fread(ih.name, readSize - 4, 1, f); // -4 = skip ih.instrSize
+ fread(ih.name, readSize-4, 1, f); // -4 = skip ih.instrSize
// FT2 bugfix: skip instrument header data if instrSize is above INSTR_HEADER_SIZE
if (ih.instrSize > INSTR_HEADER_SIZE)
@@ -1952,45 +1972,66 @@
if (!allocateTmpInstr(i))
return false;
+ // copy instrument header elements to our instrument struct
+
+ ins = instrTmp[i];
+ memcpy(ins->ta, ih.ta, 96);
+ memcpy(ins->envVP, ih.envVP, 12*2*sizeof(int16_t));
+ memcpy(ins->envPP, ih.envPP, 12*2*sizeof(int16_t));
+ ins->envVPAnt = ih.envVPAnt;
+ ins->envPPAnt = ih.envPPAnt;
+ ins->envVSust = ih.envVSust;
+ ins->envVRepS = ih.envVRepS;
+ ins->envVRepE = ih.envVRepE;
+ ins->envPSust = ih.envPSust;
+ ins->envPRepS = ih.envPRepS;
+ ins->envPRepE = ih.envPRepE;
+ ins->envVTyp = ih.envVTyp;
+ ins->envPTyp = ih.envPTyp;
+ ins->vibTyp = ih.vibTyp;
+ ins->vibSweep = ih.vibSweep;
+ ins->vibDepth = ih.vibDepth;
+ ins->vibRate = ih.vibRate;
+ ins->fadeOut = ih.fadeOut;
+ ins->midiOn = (ih.midiOn > 0) ? true : false;
+ ins->midiChannel = ih.midiChannel;
+ ins->midiProgram = ih.midiProgram;
+ ins->midiBend = ih.midiBend;
+ ins->mute = (ih.mute > 0) ? true : false;
+ ins->antSamp = ih.antSamp; // used in loadInstrSample()
+
// sanitize stuff for broken/unsupported instruments
- ih.midiProgram = CLAMP(ih.midiProgram, 0, 127);
- ih.midiBend = CLAMP(ih.midiBend, 0, 36);
+ ins->midiProgram = CLAMP(ins->midiProgram, 0, 127);
+ ins->midiBend = CLAMP(ins->midiBend, 0, 36);
- if (ih.midiChannel > 15) ih.midiChannel = 15;
- if (ih.mute != 1) ih.mute = 0;
- if (ih.midiOn != 1) ih.midiOn = 0;
- if (ih.vibDepth > 0x0F) ih.vibDepth = 0x0F;
- if (ih.vibRate > 0x3F) ih.vibRate = 0x3F;
- if (ih.vibTyp > 3) ih.vibTyp = 0;
+ if (ins->midiChannel > 15) ins->midiChannel = 15;
+ if (ins->vibDepth > 0x0F) ins->vibDepth = 0x0F;
+ if (ins->vibRate > 0x3F) ins->vibRate = 0x3F;
+ if (ins->vibTyp > 3) ins->vibTyp = 0;
for (j = 0; j < 96; j++)
{
- if (ih.ta[j] > 15)
- ih.ta[j] = 15;
+ if (ins->ta[j] > 15)
+ ins->ta[j] = 15;
}
- if (ih.envVPAnt > 12) ih.envVPAnt = 12;
- if (ih.envVRepS > 11) ih.envVRepS = 11;
- if (ih.envVRepE > 11) ih.envVRepE = 11;
- if (ih.envVSust > 11) ih.envVSust = 11;
- if (ih.envPPAnt > 12) ih.envPPAnt = 12;
- if (ih.envPRepS > 11) ih.envPRepS = 11;
- if (ih.envPRepE > 11) ih.envPRepE = 11;
- if (ih.envPSust > 11) ih.envPSust = 11;
+ if (ins->envVPAnt > 12) ins->envVPAnt = 12;
+ if (ins->envVRepS > 11) ins->envVRepS = 11;
+ if (ins->envVRepE > 11) ins->envVRepE = 11;
+ if (ins->envVSust > 11) ins->envVSust = 11;
+ if (ins->envPPAnt > 12) ins->envPPAnt = 12;
+ if (ins->envPRepS > 11) ins->envPRepS = 11;
+ if (ins->envPRepE > 11) ins->envPRepE = 11;
+ if (ins->envPSust > 11) ins->envPSust = 11;
for (j = 0; j < 12; j++)
{
- if ((uint16_t)ih.envVP[j][0] > 32767) ih.envVP[j][0] = 32767;
- if ((uint16_t)ih.envPP[j][0] > 32767) ih.envPP[j][0] = 32767;
- if ((uint16_t)ih.envVP[j][1] > 64) ih.envVP[j][1] = 64;
- if ((uint16_t)ih.envPP[j][1] > 63) ih.envPP[j][1] = 63;
+ if ((uint16_t)ins->envVP[j][0] > 32767) ins->envVP[j][0] = 32767;
+ if ((uint16_t)ins->envPP[j][0] > 32767) ins->envPP[j][0] = 32767;
+ if ((uint16_t)ins->envVP[j][1] > 64) ins->envVP[j][1] = 64;
+ if ((uint16_t)ins->envPP[j][1] > 63) ins->envPP[j][1] = 63;
}
- // ----------------------------------------
-
- // copy over final instrument data from temp buffer
- memcpy(instrTmp[i], ih.ta, INSTR_SIZE);
- instrTmp[i]->antSamp = ih.antSamp;
}
if (fread(ih.samp, ih.antSamp * sizeof (sampleHeaderTyp), 1, f) != 1)
@@ -2001,9 +2042,23 @@
for (j = 0; j < ih.antSamp; j++)
{
s = &instrTmp[i]->samp[j];
- memcpy(s, &ih.samp[j], 12+4+24);
- // s->pek is set up later
+ src = &ih.samp[j];
+ // copy sample header elements to our sample struct
+
+ s->len = src->len;
+ s->repS = src->repS;
+ s->repL = src->repL;
+ s->vol = src->vol;
+ s->fine = src->fine;
+ s->typ = src->typ;
+ s->pan = src->pan;
+ s->relTon = src->relTon;
+ memcpy(s->name, src->name, 22);
+ s->name[22] = '\0';
+
+ // dst->pek is set up later
+
// trim off spaces at end of name
for (k = 21; k >= 0; k--)
{
@@ -2013,7 +2068,7 @@
break;
}
- // sanitize stuff for malicious modules
+ // sanitize stuff broken/unsupported samples
if (s->vol > 64)
s->vol = 64;
@@ -2025,7 +2080,7 @@
return true;
}
-static void checkSampleRepeat(sampleTyp *s)
+void checkSampleRepeat(sampleTyp *s)
{
if (s->repS < 0) s->repS = 0;
if (s->repL < 0) s->repL = 0;
@@ -2049,7 +2104,7 @@
{
s = &instrTmp[i]->samp[j];
- // if a sample has both forward loop and pingpong loop set, make it pingpong loop only (FT2 behavior)
+ // if a sample has both forward loop and pingpong loop set, make it pingpong loop only (FT2 mixer behavior)
if ((s->typ & 3) == 3)
s->typ &= 0xFE;
@@ -2414,11 +2469,11 @@
{
int32_t filesize;
uint32_t filenameLen;
- UNICHAR *filenameU, tmpPathU[PATH_MAX + 2];
+ UNICHAR *filenameU, tmpPathU[PATH_MAX+2];
// this is crude, we always expect only one parameter, and that it is the module.
- if (argc != 2)
+ if (argc != 2 || argv[1] == NULL || argv[1][0] == '\0')
return false;
#ifdef __APPLE__
@@ -2428,7 +2483,7 @@
filenameLen = (uint32_t)strlen(argv[1]);
- filenameU = (UNICHAR *)calloc((filenameLen + 1), sizeof (UNICHAR));
+ filenameU = (UNICHAR *)calloc(filenameLen+1, sizeof (UNICHAR));
if (filenameU == NULL)
{
okBox(0, "System message", "Not enough memory!");
@@ -2444,34 +2499,28 @@
// store old path
UNICHAR_GETCWD(tmpPathU, PATH_MAX);
- // set binary path
+ // set path to where the main executable is
UNICHAR_CHDIR(editor.binaryPathU);
filesize = getFileSize(filenameU);
-
- if (filesize == -1) // >2GB
+ if (filesize == -1 || filesize >= 512L*1024*1024) // >=2GB or >=512MB
{
- okBox(0, "System message", "The file is too big and can't be loaded (over 2GB).");
- goto argLoadErr;
- }
+ okBox(0, "System message", "Error: The module is too big to be loaded!");
+ /* This is not really true, but let's add this check to prevent accidentally
+ ** passing really big files to the program. And how often do you really
+ ** see a >=512MB .XM/.S3M module?
+ */
- if (filesize >= 128L*1024*1024) // 128MB
- {
- if (okBox(2, "System request", "Are you sure you want to load such a big file?") != 1)
- goto argLoadErr;
+ free(filenameU);
+ UNICHAR_CHDIR(tmpPathU); // set old path back
+ return false;
}
- editor.loadMusicEvent = EVENT_LOADMUSIC_ARGV;
- loadMusic(filenameU);
-
- UNICHAR_CHDIR(tmpPathU); // set old path back
- free(filenameU);
- return true;
+ bool result = loadMusicUnthreaded(filenameU, true);
-argLoadErr:
- UNICHAR_CHDIR(tmpPathU); // set old path back
free(filenameU);
- return false;
+ UNICHAR_CHDIR(tmpPathU); // set old path back
+ return result;
}
void loadDroppedFile(char *fullPathUTF8, bool songModifiedCheck)
--- a/src/ft2_module_loader.h
+++ b/src/ft2_module_loader.h
@@ -5,9 +5,10 @@
#include "ft2_unicode.h"
void loadMusic(UNICHAR *filenameU);
-bool loadMusicUnthreaded(UNICHAR *filenameU); // for development testing
+bool loadMusicUnthreaded(UNICHAR *filenameU, bool autoPlay);
bool handleModuleLoadFromArg(int argc, char **argv);
void loadDroppedFile(char *fullPathUTF8, bool songModifiedCheck);
void handleLoadMusicEvents(void);
void clearUnusedChannels(tonTyp *p, int16_t pattLen, uint8_t antChn);
void unpackPatt(uint8_t *dst, uint16_t inn, uint16_t len, uint8_t antChn);
+void checkSampleRepeat(sampleTyp *s);
--- a/src/ft2_module_saver.c
+++ b/src/ft2_module_saver.c
@@ -13,7 +13,8 @@
#include "ft2_module_loader.h"
/* These savers are directly ported, so they should act identical to FT2
-** except for some very minor changes. */
+** except for some very minor changes.
+*/
static SDL_Thread *thread;
@@ -31,9 +32,10 @@
size_t result;
songHeaderTyp h;
patternHeaderTyp ph;
+ instrTyp *ins;
instrHeaderTyp ih;
- sampleTyp *srcSmp;
- sampleHeaderTyp *dstSmp;
+ sampleTyp *s;
+ sampleHeaderTyp *dst;
FILE *f;
f = UNICHAR_FOPEN(filenameU, "wb");
@@ -135,6 +137,8 @@
}
}
+ memset(&ih, 0, sizeof (ih)); // important, clears reserved stuff
+
for (i = 1; i <= ai; i++)
{
if (instr[i] == NULL)
@@ -153,19 +157,55 @@
if (a > 0)
{
- memcpy(ih.ta, instr[j], INSTR_SIZE);
+ ins = instr[j];
+
+ memcpy(ih.ta, ins->ta, 96);
+ memcpy(ih.envVP, ins->envVP, 12*2*sizeof(int16_t));
+ memcpy(ih.envPP, ins->envPP, 12*2*sizeof(int16_t));
+ ih.envVPAnt = ins->envVPAnt;
+ ih.envPPAnt = ins->envPPAnt;
+ ih.envVSust = ins->envVSust;
+ ih.envVRepS = ins->envVRepS;
+ ih.envVRepE = ins->envVRepE;
+ ih.envPSust = ins->envPSust;
+ ih.envPRepS = ins->envPRepS;
+ ih.envPRepE = ins->envPRepE;
+ ih.envVTyp = ins->envVTyp;
+ ih.envPTyp = ins->envPTyp;
+ ih.vibTyp = ins->vibTyp;
+ ih.vibSweep = ins->vibSweep;
+ ih.vibDepth = ins->vibDepth;
+ ih.vibRate = ins->vibRate;
+ ih.fadeOut = ins->fadeOut;
+ ih.midiOn = ins->midiOn ? 1 : 0;
+ ih.midiChannel = ins->midiChannel;
+ ih.midiProgram = ins->midiProgram;
+ ih.midiBend = ins->midiBend;
+ ih.mute = ins->mute ? 1 : 0;
ih.instrSize = INSTR_HEADER_SIZE;
- for (k = 1; k <= a; k++)
+ for (k = 0; k < a; k++)
{
- srcSmp = &instr[j]->samp[k-1];
- dstSmp = &ih.samp[k-1];
+ s = &instr[j]->samp[k];
+ dst = &ih.samp[k];
- memset(dstSmp->name, ' ', 22);
+ dst->len = s->len;
+ dst->repS = s->repS;
+ dst->repL = s->repL;
+ dst->vol = s->vol;
+ dst->fine = s->fine;
+ dst->typ = s->typ;
+ dst->pan = s->pan;
+ dst->relTon = s->relTon;
- memcpy(dstSmp, srcSmp, 12+4+2 + strlen(srcSmp->name));
- if (srcSmp->pek == NULL)
- dstSmp->len = 0;
+ uint8_t nameLen = (uint8_t)strlen(s->name);
+
+ dst->nameLen = nameLen;
+ memset(dst->name, ' ', 22);
+ memcpy(dst->name, s->name, nameLen);
+
+ if (s->pek == NULL)
+ dst->len = 0;
}
}
else
@@ -182,18 +222,18 @@
for (k = 1; k <= a; k++)
{
- srcSmp = &instr[j]->samp[k-1];
- if (srcSmp->pek != NULL)
+ s = &instr[j]->samp[k-1];
+ if (s->pek != NULL)
{
- restoreSample(srcSmp);
- samp2Delta(srcSmp->pek, srcSmp->len, srcSmp->typ);
+ restoreSample(s);
+ samp2Delta(s->pek, s->len, s->typ);
- result = fwrite(srcSmp->pek, 1, srcSmp->len, f);
+ result = fwrite(s->pek, 1, s->len, f);
- delta2Samp(srcSmp->pek, srcSmp->len, srcSmp->typ);
- fixSample(srcSmp);
+ delta2Samp(s->pek, s->len, s->typ);
+ fixSample(s);
- if (result != (size_t)srcSmp->len) // write not OK
+ if (result != (size_t)s->len) // write not OK
{
fclose(f);
okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!");
--- a/src/ft2_pattern_draw.c
+++ b/src/ft2_pattern_draw.c
@@ -560,18 +560,18 @@
for (uint32_t y = 0; y < FONT4_CHAR_H; y++)
{
- for (uint32_t x1 = 0; x1 < FONT4_CHAR_W; x1++)
+ for (uint32_t x = 0; x < FONT4_CHAR_W; x++)
{
- if (src1Ptr[x1])
+ if (src1Ptr[x])
{
- dst1Ptr[x1] = pixVal; // left side
- dst2Ptr[x1] = pixVal; // right side
+ dst1Ptr[x] = pixVal; // left side
+ dst2Ptr[x] = pixVal; // right side
}
- if (src2Ptr[x1])
+ if (src2Ptr[x])
{
- dst1Ptr[FONT4_CHAR_W + x1] = pixVal; // left side
- dst2Ptr[FONT4_CHAR_W + x1] = pixVal; // right side
+ dst1Ptr[FONT4_CHAR_W+x] = pixVal; // left side
+ dst2Ptr[FONT4_CHAR_W+x] = pixVal; // right side
}
}
@@ -898,6 +898,7 @@
noteTextColors[0] = video.palette[PAL_PATTEXT]; // not selected
noteTextColors[1] = video.palette[PAL_FORGRND]; // selected
+ // increment pattern data pointer by horizontal scrollbar offset/channel
if (pattPtr != NULL)
pattPtr += editor.ui.channelOffset;
@@ -976,7 +977,7 @@
void pattTwoHexOut(uint32_t xPos, uint32_t yPos, uint8_t val, uint32_t color)
{
const uint8_t *ch1Ptr, *ch2Ptr;
- uint32_t *dstPtr;
+ uint32_t *dstPtr, tmp;
ch1Ptr = &font4Ptr[(val >> 4) * FONT4_CHAR_W];
ch2Ptr = &font4Ptr[(val & 0x0F) * FONT4_CHAR_W];
@@ -986,8 +987,14 @@
{
for (uint32_t x = 0; x < FONT4_CHAR_W; x++)
{
- if (ch1Ptr[x]) dstPtr[x] = color;
- if (ch2Ptr[x]) dstPtr[FONT4_CHAR_W + x] = color;
+ // carefully written like this to generate conditional move instructions (font data is hard to predict)
+ tmp = dstPtr[x];
+ if (ch1Ptr[x] != 0) tmp = color;
+ dstPtr[x] = tmp;
+
+ tmp = dstPtr[FONT4_CHAR_W+x];
+ if (ch2Ptr[x] != 0) tmp = color;
+ dstPtr[FONT4_CHAR_W+x] = tmp;
}
ch1Ptr += FONT4_WIDTH;
@@ -999,7 +1006,7 @@
static void pattCharOut(uint32_t xPos, uint32_t yPos, uint8_t chr, uint8_t fontType, uint32_t color)
{
const uint8_t *srcPtr;
- uint32_t x, y, *dstPtr;
+ uint32_t x, y, *dstPtr, tmp;
dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos];
@@ -1010,8 +1017,10 @@
{
for (x = 0; x < FONT3_CHAR_W; x++)
{
- if (srcPtr[x])
- dstPtr[x] = color;
+ // carefully written like this to generate conditional move instructions (font data is hard to predict)
+ tmp = dstPtr[x];
+ if (srcPtr[x] != 0) tmp = color;
+ dstPtr[x] = tmp;
}
srcPtr += FONT3_WIDTH;
@@ -1025,8 +1034,10 @@
{
for (x = 0; x < FONT4_CHAR_W; x++)
{
- if (srcPtr[x])
- dstPtr[x] = color;
+ // carefully written like this to generate conditional move instructions (font data is hard to predict)
+ tmp = dstPtr[x];
+ if (srcPtr[x] != 0) tmp = color;
+ dstPtr[x] = tmp;
}
srcPtr += FONT4_WIDTH;
@@ -1040,8 +1051,10 @@
{
for (x = 0; x < FONT5_CHAR_W; x++)
{
- if (srcPtr[x])
- dstPtr[x] = color;
+ // carefully written like this to generate conditional move instructions (font data is hard to predict)
+ tmp = dstPtr[x];
+ if (srcPtr[x] != 0) tmp = color;
+ dstPtr[x] = tmp;
}
srcPtr += FONT5_WIDTH;
@@ -1055,8 +1068,10 @@
{
for (x = 0; x < FONT7_CHAR_W; x++)
{
- if (srcPtr[x])
- dstPtr[x] = color;
+ // carefully written like this to generate conditional move instructions (font data is hard to predict)
+ tmp = dstPtr[x];
+ if (srcPtr[x] != 0) tmp = color;
+ dstPtr[x] = tmp;
}
srcPtr += FONT7_WIDTH;
@@ -1068,7 +1083,7 @@
static void drawEmptyNoteSmall(uint32_t xPos, uint32_t yPos, uint32_t color)
{
const uint8_t *srcPtr;
- uint32_t *dstPtr;
+ uint32_t *dstPtr, tmp;
srcPtr = &font7Data[18 * FONT7_CHAR_W];
dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos];
@@ -1077,8 +1092,10 @@
{
for (uint32_t x = 0; x < FONT7_CHAR_W*3; x++)
{
- if (srcPtr[x])
- dstPtr[x] = color;
+ // carefully written like this to generate conditional move instructions (font data is hard to predict)
+ tmp = dstPtr[x];
+ if (srcPtr[x] != 0) tmp = color;
+ dstPtr[x] = tmp;
}
srcPtr += FONT7_WIDTH;
@@ -1089,7 +1106,7 @@
static void drawKeyOffSmall(uint32_t xPos, uint32_t yPos, uint32_t color)
{
const uint8_t *srcPtr;
- uint32_t *dstPtr;
+ uint32_t *dstPtr, tmp;
srcPtr = &font7Data[21 * FONT7_CHAR_W];
dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + (xPos + 2)];
@@ -1098,8 +1115,10 @@
{
for (uint32_t x = 0; x < FONT7_CHAR_W*2; x++)
{
- if (srcPtr[x])
- dstPtr[x] = color;
+ // carefully written like this to generate conditional move instructions (font data is hard to predict)
+ tmp = dstPtr[x];
+ if (srcPtr[x] != 0) tmp = color;
+ dstPtr[x] = tmp;
}
srcPtr += FONT7_WIDTH;
@@ -1111,7 +1130,7 @@
{
const uint8_t *ch1Ptr, *ch2Ptr, *ch3Ptr;
uint8_t note;
- uint32_t *dstPtr, char1, char2, char3;
+ uint32_t *dstPtr, char1, char2, char3, tmp;
assert(ton >= 1 && ton <= 97);
@@ -1140,9 +1159,18 @@
{
for (uint32_t x = 0; x < FONT7_CHAR_W; x++)
{
- if (ch1Ptr[x]) dstPtr[ (FONT7_CHAR_W * 0) + x] = color;
- if (ch2Ptr[x]) dstPtr[ (FONT7_CHAR_W * 1) + x] = color;
- if (ch3Ptr[x]) dstPtr[((FONT7_CHAR_W * 2) - 2) + x] = color;
+ // carefully written like this to generate conditional move instructions (font data is hard to predict)
+ tmp = dstPtr[x];
+ if (ch1Ptr[x] != 0) tmp = color;
+ dstPtr[x] = tmp;
+
+ tmp = dstPtr[FONT7_CHAR_W+x];
+ if (ch2Ptr[x] != 0) tmp = color;
+ dstPtr[FONT7_CHAR_W+x] = tmp;
+
+ tmp = dstPtr[((FONT7_CHAR_W*2)-2)+x]; // -2 to get correct alignment for ending glyph
+ if (ch3Ptr[x] != 0) tmp = color;
+ dstPtr[((FONT7_CHAR_W*2)-2)+x] = tmp;
}
ch1Ptr += FONT7_WIDTH;
@@ -1155,7 +1183,7 @@
static void drawEmptyNoteMedium(uint32_t xPos, uint32_t yPos, uint32_t color)
{
const uint8_t *srcPtr;
- uint32_t *dstPtr;
+ uint32_t *dstPtr, tmp;
dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos];
srcPtr = &font4Ptr[43 * FONT4_CHAR_W];
@@ -1164,8 +1192,10 @@
{
for (uint32_t x = 0; x < FONT4_CHAR_W*3; x++)
{
- if (srcPtr[x])
- dstPtr[x] = color;
+ // carefully written like this to generate conditional move instructions (font data is hard to predict)
+ tmp = dstPtr[x];
+ if (srcPtr[x] != 0) tmp = color;
+ dstPtr[x] = tmp;
}
srcPtr += FONT4_WIDTH;
@@ -1176,7 +1206,7 @@
static void drawKeyOffMedium(uint32_t xPos, uint32_t yPos, uint32_t color)
{
const uint8_t *srcPtr;
- uint32_t *dstPtr;
+ uint32_t *dstPtr, tmp;
srcPtr = &font4Ptr[40 * FONT4_CHAR_W];
dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos];
@@ -1185,8 +1215,10 @@
{
for (uint32_t x = 0; x < FONT4_CHAR_W*3; x++)
{
- if (srcPtr[x])
- dstPtr[x] = color;
+ // carefully written like this to generate conditional move instructions (font data is hard to predict)
+ tmp = dstPtr[x];
+ if (srcPtr[x] != 0) tmp = color;
+ dstPtr[x] = tmp;
}
srcPtr += FONT4_WIDTH;
@@ -1197,7 +1229,7 @@
static void drawNoteMedium(uint32_t xPos, uint32_t yPos, int32_t ton, uint32_t color)
{
const uint8_t *ch1Ptr, *ch2Ptr, *ch3Ptr;
- uint32_t note, *dstPtr, char1, char2, char3;
+ uint32_t note, *dstPtr, char1, char2, char3, tmp;
ton--;
@@ -1224,9 +1256,18 @@
{
for (uint32_t x = 0; x < FONT4_CHAR_W; x++)
{
- if (ch1Ptr[x]) dstPtr[(FONT4_CHAR_W * 0) + x] = color;
- if (ch2Ptr[x]) dstPtr[(FONT4_CHAR_W * 1) + x] = color;
- if (ch3Ptr[x]) dstPtr[(FONT4_CHAR_W * 2) + x] = color;
+ // carefully written like this to generate conditional move instructions (font data is hard to predict)
+ tmp = dstPtr[x];
+ if (ch1Ptr[x] != 0) tmp = color;
+ dstPtr[x] = tmp;
+
+ tmp = dstPtr[FONT4_CHAR_W+x];
+ if (ch2Ptr[x] != 0) tmp = color;
+ dstPtr[FONT4_CHAR_W+x] = tmp;
+
+ tmp = dstPtr[(FONT4_CHAR_W*2)+x];
+ if (ch3Ptr[x] != 0) tmp = color;
+ dstPtr[(FONT4_CHAR_W*2)+x] = tmp;
}
ch1Ptr += FONT4_WIDTH;
@@ -1239,7 +1280,7 @@
static void drawEmptyNoteBig(uint32_t xPos, uint32_t yPos, uint32_t color)
{
const uint8_t *srcPtr;
- uint32_t *dstPtr;
+ uint32_t *dstPtr, tmp;
srcPtr = &font4Ptr[67 * FONT4_CHAR_W];
dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos];
@@ -1248,8 +1289,10 @@
{
for (uint32_t x = 0; x < FONT4_CHAR_W*6; x++)
{
- if (srcPtr[x])
- dstPtr[x] = color;
+ // carefully written like this to generate conditional move instructions (font data is hard to predict)
+ tmp = dstPtr[x];
+ if (srcPtr[x] != 0) tmp = color;
+ dstPtr[x] = tmp;
}
srcPtr += FONT4_WIDTH;
@@ -1260,7 +1303,7 @@
static void drawKeyOffBig(uint32_t xPos, uint32_t yPos, uint32_t color)
{
const uint8_t *srcPtr;
- uint32_t *dstPtr;
+ uint32_t *dstPtr, tmp;
srcPtr = &font4Data[61 * FONT4_CHAR_W];
dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + xPos];
@@ -1269,8 +1312,10 @@
{
for (uint32_t x = 0; x < FONT4_CHAR_W*6; x++)
{
- if (srcPtr[x])
- dstPtr[x] = color;
+ // carefully written like this to generate conditional move instructions (font data is hard to predict)
+ tmp = dstPtr[x];
+ if (srcPtr[x] != 0) tmp = color;
+ dstPtr[x] = tmp;
}
srcPtr += FONT4_WIDTH;
@@ -1282,7 +1327,7 @@
{
const uint8_t *ch1Ptr, *ch2Ptr, *ch3Ptr;
uint8_t note;
- uint32_t *dstPtr, char1, char2, char3;
+ uint32_t *dstPtr, char1, char2, char3, tmp;
ton--;
@@ -1309,9 +1354,18 @@
{
for (uint32_t x = 0; x < FONT5_CHAR_W; x++)
{
- if (ch1Ptr[x]) dstPtr[(FONT5_CHAR_W * 0) + x] = color;
- if (ch2Ptr[x]) dstPtr[(FONT5_CHAR_W * 1) + x] = color;
- if (ch3Ptr[x]) dstPtr[(FONT5_CHAR_W * 2) + x] = color;
+ // carefully written like this to generate conditional move instructions (font data is hard to predict)
+ tmp = dstPtr[x];
+ if (ch1Ptr[x] != 0) tmp = color;
+ dstPtr[x] = tmp;
+
+ tmp = dstPtr[FONT5_CHAR_W+x];
+ if (ch2Ptr[x] != 0) tmp = color;
+ dstPtr[FONT5_CHAR_W+x] = tmp;
+
+ tmp = dstPtr[(FONT5_CHAR_W*2)+x];
+ if (ch3Ptr[x] != 0) tmp = color;
+ dstPtr[(FONT5_CHAR_W*2)+x] = tmp;
}
ch1Ptr += FONT5_WIDTH;
--- a/src/ft2_pattern_ed.c
+++ b/src/ft2_pattern_ed.c
@@ -59,7 +59,8 @@
* do that to avoid out of bondary row look-up between out-of-sync replayer
* state and tracker state (yes it used to happen, rarely). We're not wasting
* too much RAM for a modern computer anyway. Worst case: 256 allocated
- * patterns would be ~10MB. */
+ * patterns would be ~10MB.
+ */
patt[nr] = (tonTyp *)calloc((MAX_PATT_LEN * TRACK_WIDTH) + 16, 1);
if (patt[nr] == NULL)
@@ -891,7 +892,7 @@
{
if (mouse.x < 29)
{
- scrollBarScrollUp(SB_CHAN_SCROLL, 1);
+ scrollBarScrollUp(SB_CHAN_SCROLL, 1);
forceMarking = true;
}
else if (mouse.x > 604)
@@ -1517,6 +1518,7 @@
if (editor.ui.pattChanScrollShown)
{
assert(song.antChn > editor.ui.numChannelsShown);
+
if (channel >= editor.ui.channelOffset+editor.ui.numChannelsShown)
scrollBarScrollDown(SB_CHAN_SCROLL, (channel - (editor.ui.channelOffset + editor.ui.numChannelsShown)) + 1);
else if (channel < editor.ui.channelOffset)
@@ -2307,12 +2309,12 @@
if (logoType == 0)
{
pushButtons[PB_LOGO].bitmapUnpressed = &ft2LogoBadges[(154 * 32) * 0];
- pushButtons[PB_LOGO].bitmapPressed = &ft2LogoBadges[(154 * 32) * 1];
+ pushButtons[PB_LOGO].bitmapPressed = &ft2LogoBadges[(154 * 32) * 1];
}
else
{
pushButtons[PB_LOGO].bitmapUnpressed = &ft2LogoBadges[(154 * 32) * 2];
- pushButtons[PB_LOGO].bitmapPressed = &ft2LogoBadges[(154 * 32) * 3];
+ pushButtons[PB_LOGO].bitmapPressed = &ft2LogoBadges[(154 * 32) * 3];
}
drawPushButton(PB_LOGO);
@@ -2325,12 +2327,12 @@
if (badgeType == 0)
{
pushButtons[PB_BADGE].bitmapUnpressed = &ft2InfoBadges[(25 * 32) * 0];
- pushButtons[PB_BADGE].bitmapPressed = &ft2InfoBadges[(25 * 32) * 1];
+ pushButtons[PB_BADGE].bitmapPressed = &ft2InfoBadges[(25 * 32) * 1];
}
else
{
pushButtons[PB_BADGE].bitmapUnpressed = &ft2InfoBadges[(25 * 32) * 2];
- pushButtons[PB_BADGE].bitmapPressed = &ft2InfoBadges[(25 * 32) * 3];
+ pushButtons[PB_BADGE].bitmapPressed = &ft2InfoBadges[(25 * 32) * 3];
}
drawPushButton(PB_BADGE);
@@ -2701,7 +2703,7 @@
song.songPos = 0;
song.globVol = 64;
- memset(song.name, 0, sizeof (song.name));
+ memset(song.name, 0, sizeof (song.name));
memset(song.songTab, 0, sizeof (song.songTab));
// zero all pattern data and reset pattern lengths
@@ -2916,7 +2918,7 @@
editor.pattPos = song.pattPos;
editor.ui.updatePatternEditor = true;
- editor.ui.updatePosSections = true;
+ editor.ui.updatePosSections = true;
unlockMixerCallback();
setSongModifiedFlag();
--- a/src/ft2_replayer.c
+++ b/src/ft2_replayer.c
@@ -419,7 +419,7 @@
ch->instrSeg = ins;
ch->mute = ins->mute;
- if (ton > 96) // non-FT2 security (should never happen because I clamp in the patt loaders now)
+ if (ton > 96) // non-FT2 security (should never happen because I clamp in the patt. loader now)
ton = 96;
smp = ins->ta[ton-1] & 0xF;
@@ -1114,7 +1114,7 @@
ch->eff = p->eff;
ch->tonTyp = (p->instr << 8) | p->ton;
- if (ch->stOff == 1)
+ if (ch->stOff)
{
checkMoreEffects(ch);
return;
@@ -1233,7 +1233,7 @@
}
}
- if (ch->mute != 1)
+ if (!ch->mute)
{
// *** VOLUME ENVELOPE ***
envVal = 0;
--- a/src/ft2_replayer.h
+++ b/src/ft2_replayer.h
@@ -34,8 +34,8 @@
// DO NOT TOUCH!
#define MIN_BPM 32
-#define TRACK_WIDTH (5 * MAX_VOICES)
#define MAX_VOICES 32
+#define TRACK_WIDTH (5 * MAX_VOICES)
#define MAX_NOTES ((12 * 10 * 16) + 16)
#define MAX_PATTERNS 256
#define MAX_PATT_LEN 256
@@ -43,7 +43,6 @@
#define MAX_SMP_PER_INST 16
#define MAX_ORDERS 256
#define STD_ENV_SIZE ((6*2*12*2*2) + (6*8*2) + (6*5*2) + (6*2*2))
-#define INSTR_SIZE 232
#define INSTR_HEADER_SIZE 263
#define INSTR_XI_HEADER_SIZE 298
#define MAX_SAMPLE_LEN 0x3FFFFFFF
@@ -123,7 +122,7 @@
int8_t fine;
uint8_t typ, pan;
int8_t relTon;
- uint8_t reserved;
+ uint8_t nameLen;
char name[22];
}
#ifdef __GNUC__
@@ -157,34 +156,27 @@
#endif
instrHeaderTyp;
-typedef struct sampleTyp_t // DO NOT TOUCH!
-{
- int32_t len, repS, repL;
- uint8_t vol;
- int8_t fine;
- uint8_t typ, pan;
- int8_t relTon;
- uint8_t reserved;
- char name[22 + 1]; // +1 for tracker NUL termination, not present in sample header
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif
- // stuff from now on can be touched
- int8_t *pek;
- uint8_t fixed;
+typedef struct sampleTyp_t
+{
+ char name[22+1];
+ bool fixed;
+ int8_t fine, relTon, *pek;
+ uint8_t vol, typ, pan;
int16_t fixedSmp1;
#ifndef LERPMIX
int16_t fixedSmp2;
#endif
- int32_t fixedPos;
-}
-#ifdef __GNUC__
-__attribute__ ((packed))
-#endif
-sampleTyp;
+ int32_t fixedPos, len, repS, repL;
+} sampleTyp;
-typedef struct instrTyp_t // DO NOT TOUCH!
+typedef struct instrTyp_t
{
- uint8_t ta[96];
- int16_t envVP[12][2], envPP[12][2];
+ bool midiOn, mute;
+ uint8_t midiChannel, ta[96];
uint8_t envVPAnt, envPPAnt;
uint8_t envVSust, envVRepS, envVRepE;
uint8_t envPSust, envPRepS, envPRepE;
@@ -191,26 +183,18 @@
uint8_t envVTyp, envPTyp;
uint8_t vibTyp, vibSweep, vibDepth, vibRate;
uint16_t fadeOut;
- uint8_t midiOn, midiChannel;
- int16_t midiProgram, midiBend;
- uint8_t mute, reserved[15];
- int16_t antSamp;
+ int16_t envVP[12][2], envPP[12][2], midiProgram, midiBend;
+ int16_t antSamp; // used by loader only
sampleTyp samp[16];
-}
-#ifdef __GNUC__
-__attribute__ ((packed))
-#endif
-instrTyp;
-#ifdef _MSC_VER
-#pragma pack(pop)
-#endif
+} instrTyp;
typedef struct stmTyp_t
{
+ bool envSustainActive, stOff, mute;
volatile uint8_t status, tmpStatus;
int8_t relTonNr, fineTune;
- uint8_t sampleNr, instrNr, stOff, effTyp, eff, smpOffset, tremorSave, tremorPos;
- uint8_t globVolSlideSpeed, panningSlideSpeed, mute, waveCtrl, portaDir;
+ uint8_t sampleNr, instrNr, effTyp, eff, smpOffset, tremorSave, tremorPos;
+ uint8_t globVolSlideSpeed, panningSlideSpeed, waveCtrl, portaDir;
uint8_t glissFunk, vibPos, tremPos, vibSpeed, vibDepth, tremSpeed, tremDepth;
uint8_t pattPos, loopCnt, volSlideSpeed, fVolSlideUpSpeed, fVolSlideDownSpeed;
uint8_t fPortaUpSpeed, fPortaDownSpeed, ePortaUpSpeed, ePortaDownSpeed;
@@ -217,7 +201,6 @@
uint8_t portaUpSpeed, portaDownSpeed, retrigSpeed, retrigCnt, retrigVol;
uint8_t volKolVol, tonNr, envPPos, eVibPos, envVPos, realVol, oldVol, outVol;
uint8_t oldPan, outPan, finalPan;
- bool envSustainActive;
int16_t midiCurChannel, midiCurTone, midiCurVibDepth, midiCurPeriod, midiCurPitch;
int16_t midiBend, midiPortaPeriod, midiPitch, realPeriod, envVIPValue, envPIPValue;
uint16_t finalVol, outPeriod, finalPeriod, tonTyp, wantPeriod, portaSpeed;
@@ -230,15 +213,13 @@
typedef struct songTyp_t
{
- uint8_t antChn, pattDelTime, pattDelTime2, pBreakPos, songTab[MAX_ORDERS];
bool pBreakFlag, posJumpFlag, isModified;
+ char name[20+1], instrName[1+MAX_INST][22+1];
+ uint8_t curReplayerTimer, curReplayerPattPos, curReplayerSongPos, curReplayerPattNr; // used for audio/video sync queue
+ uint8_t antChn, pattDelTime, pattDelTime2, pBreakPos, songTab[MAX_ORDERS];
int16_t songPos, pattNr, pattPos, pattLen;
uint16_t len, repS, speed, tempo, globVol, timer, ver, initialTempo;
- char name[20 + 1], instrName[1 + MAX_INST][22 + 1];
uint32_t musicTime;
-
- // used for audio/video sync queue
- uint8_t curReplayerTimer, curReplayerPattPos, curReplayerSongPos, curReplayerPattNr;
} songTyp;
typedef struct tonTyp_t
--- a/src/ft2_sysreqs.c
+++ b/src/ft2_sysreqs.c
@@ -612,6 +612,9 @@
// WARNING: This routine must NOT be called from the main input/video thread!
int16_t okBoxThreadSafe(int16_t typ, const char *headline, const char *text)
{
+ if (!editor.mainLoopOngoing)
+ return 0; // main loop was not even started yet, bail out.
+
// block multiple calls before they are completed (for safety)
while (okBoxData.active)
SDL_Delay(1000 / VBLANK_HZ);
--- a/src/ft2_wav_renderer.c
+++ b/src/ft2_wav_renderer.c
@@ -156,7 +156,6 @@
static bool dump_Init(uint32_t frq, int16_t amp, int16_t songPos)
{
- uint8_t i, oldMuteFlags[MAX_VOICES];
uint32_t maxSamplesPerTick, sampleSize;
maxSamplesPerTick = ((frq * 5) / 2) / MIN_BPM; // absolute max samples per tidck
@@ -173,16 +172,7 @@
playMode = PLAYMODE_SONG;
songPlaying = true;
- // store mute states
- for (i = 0; i < MAX_VOICES; i++)
- oldMuteFlags[i] = stm[i].stOff;
-
resetChannels();
-
- // restore mute states
- for (i = 0; i < MAX_VOICES; i++)
- stm[i].stOff = oldMuteFlags[i];
-
setNewAudioFreq(frq);
setAudioAmp(amp, config.masterVol, (WDBitDepth == 32));
--- a/vs2019_project/ft2-clone/ft2-clone.vcxproj
+++ b/vs2019_project/ft2-clone/ft2-clone.vcxproj
@@ -26,13 +26,11 @@
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
- <UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
- <UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>MultiByte</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v142</PlatformToolset>