ref: d3ed7576858d869b5a5c3ae5061162f2aa6765df
parent: 8b9bbe660ea1dad5f1f8e695e752ed24293533ec
author: Olav Sørensen <[email protected]>
date: Mon May 4 18:17:47 EDT 2020
Pushed v1.13 code - Windows bugfix: Left Amiga key (Win-key) didn't work! - Windows: Removed a broken Num Lock-specific hack. Seems like it didn't work anymore in later SDL2 versions. This means that the drumpad feature is pretty useless... - Bugfix: Saving samples as .IFF would result in broken .IFF files - Bugfix: Don't allow sample modifications in "sample zero" mode - Bugfix: The "numpad 0" key wasn't behaving the way it should - Bugfix: The "numpad enter" key wasn't behaving the way it should if the current sample was set to 16 (0010). - Code cleanup. Moved certain chunks to new files, renamed modEntry struct etc.
--- /dev/null
+++ b/release/other/known bugs.txt
@@ -1,0 +1,7 @@
+Windows (and probably Linux?):
+- The num lock key (which is used to set the current sample to 1 or 17) will
+ also toggle the num lock state, which means that some of the numpad keys
+ work like arrow keys instead. This makes the drumpad function useless! I
+ used to have a Win32 API routine to fix this, but it seems to not work
+ anymore after some SDL2 version, so I removed it. SDL2 seems to handle num
+ lock internally these days, which ruins it.
--- /dev/null
+++ b/release/win32/known bugs.txt
@@ -1,0 +1,7 @@
+Windows (and probably Linux?):
+- The num lock key (which is used to set the current sample to 1 or 17) will
+ also toggle the num lock state, which means that some of the numpad keys
+ work like arrow keys instead. This makes the drumpad function useless! I
+ used to have a Win32 API routine to fix this, but it seems to not work
+ anymore after some SDL2 version, so I removed it. SDL2 seems to handle num
+ lock internally these days, which ruins it.
--- /dev/null
+++ b/release/win64/known bugs.txt
@@ -1,0 +1,7 @@
+Windows (and probably Linux?):
+- The num lock key (which is used to set the current sample to 1 or 17) will
+ also toggle the num lock state, which means that some of the numpad keys
+ work like arrow keys instead. This makes the drumpad function useless! I
+ used to have a Win32 API routine to fix this, but it seems to not work
+ anymore after some SDL2 version, so I removed it. SDL2 seems to handle num
+ lock internally these days, which ruins it.
--- a/src/pt2_audio.c
+++ b/src/pt2_audio.c
@@ -319,10 +319,10 @@
for (int32_t i = 0; i < AMIGA_VOICES; i++)
{
- ch = &modEntry->channels[i];
+ ch = &song->channels[i];
if (ch->n_samplenum == editor.currSample)
{
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
paulaSetData(i, ch->n_start + s->loopStart);
paulaSetLength(i, s->loopLength >> 1);
}
@@ -473,13 +473,13 @@
moduleSample_t *s;
scopeChannelExt_t *se, tmp;
- smp = modEntry->channels[ch].n_samplenum;
+ smp = song->channels[ch].n_samplenum;
assert(smp <= 30);
- s = &modEntry->samples[smp];
+ s = &song->samples[smp];
// set voice data
if (src == NULL)
- src = &modEntry->sampleData[RESERVED_SAMPLE_OFFSET]; // dummy sample
+ src = &song->sampleData[RESERVED_SAMPLE_OFFSET]; // dummy sample
paula[ch].newData = src;
@@ -513,7 +513,7 @@
dat = v->newData;
if (dat == NULL)
- dat = &modEntry->sampleData[RESERVED_SAMPLE_OFFSET]; // dummy sample
+ dat = &song->sampleData[RESERVED_SAMPLE_OFFSET]; // dummy sample
length = v->newLength;
if (length < 2)
@@ -533,7 +533,7 @@
dat = se->newData;
if (dat == NULL)
- dat = &modEntry->sampleData[RESERVED_SAMPLE_OFFSET]; // dummy sample
+ dat = &song->sampleData[RESERVED_SAMPLE_OFFSET]; // dummy sample
s.length = length;
s.data = dat;
@@ -1138,7 +1138,7 @@
uint32_t maxSamplesToMix;
if (MOD2WAV_FREQ > audio.outputRate)
- maxSamplesToMix = audio.bpmTabMod2Wav[32-32]; // BPM 32
+ maxSamplesToMix = audio.bpmTabMod2Wav[32-32]; // BPM 32
else
maxSamplesToMix = audio.bpmTab[32-32]; // BPM 32
--- /dev/null
+++ b/src/pt2_bmp.c
@@ -1,0 +1,290 @@
+// for finding memory leaks in debug mode with Visual Studio
+#if defined _DEBUG && defined _MSC_VER
+#include <crtdbg.h>
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "pt2_palette.h"
+#include "pt2_structs.h"
+#include "pt2_helpers.h"
+#include "pt2_bmp.h"
+#include "pt2_tables.h"
+
+uint32_t *aboutScreenBMP = NULL, *clearDialogBMP = NULL;
+uint32_t *diskOpScreenBMP = NULL, *editOpModeCharsBMP = NULL, *mod2wavBMP = NULL;
+uint32_t *editOpScreen1BMP = NULL, *editOpScreen2BMP = NULL, *samplerVolumeBMP = NULL;
+uint32_t *editOpScreen3BMP = NULL, *editOpScreen4BMP = NULL, *spectrumVisualsBMP = NULL;
+uint32_t *muteButtonsBMP = NULL, *posEdBMP = NULL, *samplerFiltersBMP = NULL;
+uint32_t *samplerScreenBMP = NULL, *pat2SmpDialogBMP = NULL, *trackerFrameBMP = NULL;
+uint32_t *yesNoDialogBMP = NULL, *bigYesNoDialogBMP = NULL;
+
+void createBitmaps(void)
+{
+ uint8_t r8, g8, b8, r8_2, g8_2, b8_2;
+ uint16_t pixel12;
+ uint32_t i, j, x, y, pixel24;
+
+ pixel24 = video.palette[PAL_PATCURSOR];
+ for (y = 0; y < 14; y++)
+ {
+ // top two rows have a lighter color
+ if (y < 2)
+ {
+ r8 = R24(pixel24);
+ g8 = G24(pixel24);
+ b8 = B24(pixel24);
+
+ if (r8 <= 0xFF-0x33)
+ r8 += 0x33;
+ else
+ r8 = 0xFF;
+
+ if (g8 <= 0xFF-0x33)
+ g8 += 0x33;
+ else
+ g8 = 0xFF;
+
+ if (b8 <= 0xFF-0x33)
+ b8 += 0x33;
+ else
+ b8 = 0xFF;
+
+ for (x = 0; x < 11; x++)
+ patternCursorBMP[(y * 11) + x] = RGB24(r8, g8, b8);
+ }
+
+ // sides (same color)
+ if (y >= 2 && y <= 12)
+ {
+ patternCursorBMP[(y * 11) + 0] = pixel24;
+
+ for (x = 1; x < 10; x++)
+ patternCursorBMP[(y * 11) + x] = video.palette[PAL_COLORKEY];
+
+ patternCursorBMP[(y * 11) + 10] = pixel24;
+ }
+
+ // bottom two rows have a darker color
+ if (y > 11)
+ {
+ r8 = R24(pixel24);
+ g8 = G24(pixel24);
+ b8 = B24(pixel24);
+
+ if (r8 >= 0x33)
+ r8 -= 0x33;
+ else
+ r8 = 0x00;
+
+ if (g8 >= 0x33)
+ g8 -= 0x33;
+ else
+ g8 = 0x00;
+
+ if (b8 >= 0x33)
+ b8 -= 0x33;
+ else
+ b8 = 0x00;
+
+ for (x = 0; x < 11; x++)
+ patternCursorBMP[(y * 11) + x] = RGB24(r8, g8, b8);
+ }
+ }
+
+ // create spectrum analyzer bar graphics
+ for (i = 0; i < 36; i++)
+ spectrumAnaBMP[i] = RGB12_to_RGB24(analyzerColors[35-i]);
+
+ // create VU-Meter bar graphics
+ for (i = 0; i < 48; i++)
+ {
+ pixel12 = vuMeterColors[47-i];
+
+ r8_2 = r8 = R12_to_R24(pixel12);
+ g8_2 = g8 = G12_to_G24(pixel12);
+ b8_2 = b8 = B12_to_B24(pixel12);
+
+ // brighter pixels on the left side
+
+ if (r8_2 <= 0xFF-0x33)
+ r8_2 += 0x33;
+ else
+ r8_2 = 0xFF;
+
+ if (g8_2 <= 0xFF-0x33)
+ g8_2 += 0x33;
+ else
+ g8_2 = 0xFF;
+
+ if (b8_2 <= 0xFF-0x33)
+ b8_2 += 0x33;
+ else
+ b8_2 = 0xFF;
+
+ pixel24 = RGB24(r8_2, g8_2, b8_2);
+
+ vuMeterBMP[(i * 10) + 0] = pixel24;
+ vuMeterBMP[(i * 10) + 1] = pixel24;
+
+ // main pixels
+ for (j = 2; j < 8; j++)
+ vuMeterBMP[(i * 10) + j] = RGB24(r8, g8, b8);
+
+ // darker pixels on the right side
+ r8_2 = r8;
+ g8_2 = g8;
+ b8_2 = b8;
+
+ if (r8_2 >= 0x33)
+ r8_2 -= 0x33;
+ else
+ r8_2 = 0x00;
+
+ if (g8_2 >= 0x33)
+ g8_2 -= 0x33;
+ else
+ g8_2 = 0x00;
+
+ if (b8_2 >= 0x33)
+ b8_2 -= 0x33;
+ else
+ b8_2 = 0x00;
+
+ pixel24 = RGB24(r8_2, g8_2, b8_2);
+
+ vuMeterBMP[(i * 10) + 8] = pixel24;
+ vuMeterBMP[(i * 10) + 9] = pixel24;
+ }
+}
+
+void freeBMPs(void)
+{
+ if (trackerFrameBMP != NULL) free(trackerFrameBMP);
+ if (samplerScreenBMP != NULL) free(samplerScreenBMP);
+ if (samplerVolumeBMP != NULL) free(samplerVolumeBMP);
+ if (samplerFiltersBMP != NULL) free(samplerFiltersBMP);
+ if (clearDialogBMP != NULL) free(clearDialogBMP);
+ if (diskOpScreenBMP != NULL) free(diskOpScreenBMP);
+ if (mod2wavBMP != NULL) free(mod2wavBMP);
+ if (posEdBMP != NULL) free(posEdBMP);
+ if (spectrumVisualsBMP != NULL) free(spectrumVisualsBMP);
+ if (yesNoDialogBMP != NULL) free(yesNoDialogBMP);
+ if (bigYesNoDialogBMP != NULL) free(bigYesNoDialogBMP);
+ if (pat2SmpDialogBMP != NULL) free(pat2SmpDialogBMP);
+ if (editOpScreen1BMP != NULL) free(editOpScreen1BMP);
+ if (editOpScreen2BMP != NULL) free(editOpScreen2BMP);
+ if (editOpScreen3BMP != NULL) free(editOpScreen3BMP);
+ if (editOpScreen4BMP != NULL) free(editOpScreen4BMP);
+ if (aboutScreenBMP != NULL) free(aboutScreenBMP);
+ if (muteButtonsBMP != NULL) free(muteButtonsBMP);
+ if (editOpModeCharsBMP != NULL) free(editOpModeCharsBMP);
+}
+
+uint32_t *unpackBMP(const uint8_t *src, uint32_t packedLen)
+{
+ const uint8_t *packSrc;
+ uint8_t *tmpBuffer, *packDst, byteIn;
+ int16_t count;
+ uint32_t *dst, decodedLength, i;
+
+ // RLE decode
+ decodedLength = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
+
+ // 2-bit to 8-bit conversion
+ dst = (uint32_t *)malloc((decodedLength * 4) * sizeof (int32_t));
+ if (dst == NULL)
+ return NULL;
+
+ tmpBuffer = (uint8_t *)malloc(decodedLength + 512); // some margin is needed, the packer is buggy
+ if (tmpBuffer == NULL)
+ {
+ free(dst);
+ return NULL;
+ }
+
+ packSrc = src + 4;
+ packDst = tmpBuffer;
+
+ i = packedLen - 4;
+ while (i > 0)
+ {
+ byteIn = *packSrc++;
+ if (byteIn == 0xCC) // compactor code
+ {
+ count = *packSrc++;
+ byteIn = *packSrc++;
+
+ while (count-- >= 0)
+ *packDst++ = byteIn;
+
+ i -= 2;
+ }
+ else
+ {
+ *packDst++ = byteIn;
+ }
+
+ i--;
+ }
+
+ for (i = 0; i < decodedLength; i++)
+ {
+ byteIn = (tmpBuffer[i] & 0xC0) >> 6;
+ assert(byteIn < PALETTE_NUM);
+ dst[(i * 4) + 0] = video.palette[byteIn];
+
+ byteIn = (tmpBuffer[i] & 0x30) >> 4;
+ assert(byteIn < PALETTE_NUM);
+ dst[(i * 4) + 1] = video.palette[byteIn];
+
+ byteIn = (tmpBuffer[i] & 0x0C) >> 2;
+ assert(byteIn < PALETTE_NUM);
+ dst[(i * 4) + 2] = video.palette[byteIn];
+
+ byteIn = (tmpBuffer[i] & 0x03) >> 0;
+ assert(byteIn < PALETTE_NUM);
+ dst[(i * 4) + 3] = video.palette[byteIn];
+ }
+
+ free(tmpBuffer);
+ return dst;
+}
+
+bool unpackBMPs(void)
+{
+ trackerFrameBMP = unpackBMP(trackerFramePackedBMP, sizeof (trackerFramePackedBMP));
+ samplerScreenBMP = unpackBMP(samplerScreenPackedBMP, sizeof (samplerScreenPackedBMP));
+ samplerVolumeBMP = unpackBMP(samplerVolumePackedBMP, sizeof (samplerVolumePackedBMP));
+ samplerFiltersBMP = unpackBMP(samplerFiltersPackedBMP, sizeof (samplerFiltersPackedBMP));
+ clearDialogBMP = unpackBMP(clearDialogPackedBMP, sizeof (clearDialogPackedBMP));
+ diskOpScreenBMP = unpackBMP(diskOpScreenPackedBMP, sizeof (diskOpScreenPackedBMP));
+ mod2wavBMP = unpackBMP(mod2wavPackedBMP, sizeof (mod2wavPackedBMP));
+ posEdBMP = unpackBMP(posEdPackedBMP, sizeof (posEdPackedBMP));
+ spectrumVisualsBMP = unpackBMP(spectrumVisualsPackedBMP, sizeof (spectrumVisualsPackedBMP));
+ yesNoDialogBMP = unpackBMP(yesNoDialogPackedBMP, sizeof (yesNoDialogPackedBMP));
+ bigYesNoDialogBMP = unpackBMP(bigYesNoDialogPackedBMP, sizeof (bigYesNoDialogPackedBMP));
+ pat2SmpDialogBMP = unpackBMP(pat2SmpDialogPackedBMP, sizeof (pat2SmpDialogPackedBMP));
+ editOpScreen1BMP = unpackBMP(editOpScreen1PackedBMP, sizeof (editOpScreen1PackedBMP));
+ editOpScreen2BMP = unpackBMP(editOpScreen2PackedBMP, sizeof (editOpScreen2PackedBMP));
+ editOpScreen3BMP = unpackBMP(editOpScreen3PackedBMP, sizeof (editOpScreen3PackedBMP));
+ editOpScreen4BMP = unpackBMP(editOpScreen4PackedBMP, sizeof (editOpScreen4PackedBMP));
+ aboutScreenBMP = unpackBMP(aboutScreenPackedBMP, sizeof (aboutScreenPackedBMP));
+ muteButtonsBMP = unpackBMP(muteButtonsPackedBMP, sizeof (muteButtonsPackedBMP));
+ editOpModeCharsBMP = unpackBMP(editOpModeCharsPackedBMP, sizeof (editOpModeCharsPackedBMP));
+
+ if (trackerFrameBMP == NULL || samplerScreenBMP == NULL || samplerVolumeBMP == NULL ||
+ clearDialogBMP == NULL || diskOpScreenBMP == NULL || mod2wavBMP == NULL ||
+ posEdBMP == NULL || spectrumVisualsBMP == NULL || yesNoDialogBMP == NULL ||
+ editOpScreen1BMP == NULL || editOpScreen2BMP == NULL || editOpScreen3BMP == NULL ||
+ editOpScreen4BMP == NULL || aboutScreenBMP == NULL || muteButtonsBMP == NULL ||
+ editOpModeCharsBMP == NULL || samplerFiltersBMP == NULL || yesNoDialogBMP == NULL ||
+ bigYesNoDialogBMP == NULL)
+ {
+ showErrorMsgBox("Out of memory!");
+ return false; // BMPs are free'd in cleanUp()
+ }
+
+ createBitmaps();
+ return true;
+}
--- /dev/null
+++ b/src/pt2_bmp.h
@@ -1,0 +1,69 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#define EDOP_MODE_BMP_A_OFS ((7 * 6) * 0)
+#define EDOP_MODE_BMP_C_OFS ((7 * 6) * 1)
+#define EDOP_MODE_BMP_H_OFS ((7 * 6) * 2)
+#define EDOP_MODE_BMP_N_OFS ((7 * 6) * 3)
+#define EDOP_MODE_BMP_O_OFS ((7 * 6) * 4)
+#define EDOP_MODE_BMP_P_OFS ((7 * 6) * 5)
+#define EDOP_MODE_BMP_S_OFS ((7 * 6) * 6)
+#define EDOP_MODE_BMP_T_OFS ((7 * 6) * 7)
+
+// GFX
+extern uint32_t iconBMP[1024];
+extern const uint8_t mousePointerBMP[256];
+extern const uint8_t fontBMP[6096];
+
+// PACKED GFX
+extern const uint8_t aboutScreenPackedBMP[1684];
+extern const uint8_t clearDialogPackedBMP[525];
+extern const uint8_t diskOpScreenPackedBMP[1898];
+extern const uint8_t editOpModeCharsPackedBMP[88];
+extern const uint8_t editOpScreen1PackedBMP[1481];
+extern const uint8_t editOpScreen2PackedBMP[1502];
+extern const uint8_t editOpScreen3PackedBMP[1736];
+extern const uint8_t editOpScreen4PackedBMP[1713];
+extern const uint8_t mod2wavPackedBMP[607];
+extern const uint8_t muteButtonsPackedBMP[46];
+extern const uint8_t posEdPackedBMP[1375];
+extern const uint8_t samplerVolumePackedBMP[706];
+extern const uint8_t samplerFiltersPackedBMP[933];
+extern const uint8_t samplerScreenPackedBMP[3056];
+extern const uint8_t spectrumVisualsPackedBMP[2217];
+extern const uint8_t trackerFramePackedBMP[8486];
+extern const uint8_t yesNoDialogPackedBMP[476];
+extern const uint8_t bigYesNoDialogPackedBMP[472];
+extern const uint8_t pat2SmpDialogPackedBMP[520];
+
+// these are filled/normalized on init, so no const
+extern uint32_t vuMeterBMP[480];
+extern uint32_t loopPinsBMP[512];
+extern uint32_t samplingPosBMP[64];
+extern uint32_t spectrumAnaBMP[36];
+extern uint32_t patternCursorBMP[154];
+extern uint32_t *editOpScreen1BMP;
+extern uint32_t *editOpScreen2BMP;
+extern uint32_t *editOpScreen3BMP;
+extern uint32_t *editOpScreen4BMP;
+extern uint32_t *yesNoDialogBMP;
+extern uint32_t *bigYesNoDialogBMP;
+extern uint32_t *spectrumVisualsBMP;
+extern uint32_t *posEdBMP;
+extern uint32_t *mod2wavBMP;
+extern uint32_t *diskOpScreenBMP;
+extern uint32_t *clearDialogBMP;
+extern uint32_t *samplerVolumeBMP;
+extern uint32_t *samplerFiltersBMP;
+extern uint32_t *samplerScreenBMP;
+extern uint32_t *trackerFrameBMP;
+extern uint32_t *aboutScreenBMP;
+extern uint32_t *muteButtonsBMP;
+extern uint32_t *editOpModeCharsBMP;
+extern uint32_t *pat2SmpDialogBMP;
+
+bool unpackBMPs(void);
+void createBitmaps(void);
+void freeBMPs(void);
--- a/src/pt2_config.c
+++ b/src/pt2_config.c
@@ -13,13 +13,12 @@
#include <unistd.h>
#include <limits.h>
#endif
-#include "pt2_helpers.h"
#include "pt2_header.h"
+#include "pt2_helpers.h"
#include "pt2_config.h"
#include "pt2_tables.h"
#include "pt2_audio.h"
#include "pt2_diskop.h"
-#include "pt2_config.h"
#include "pt2_textout.h"
#include "pt2_sampler.h"
--- a/src/pt2_diskop.c
+++ b/src/pt2_diskop.c
@@ -29,7 +29,7 @@
#include "pt2_diskop.h"
#include "pt2_tables.h"
#include "pt2_palette.h"
-#include "pt2_modloader.h"
+#include "pt2_module_loader.h"
#include "pt2_audio.h"
#include "pt2_sampler.h"
#include "pt2_config.h"
@@ -36,7 +36,7 @@
#include "pt2_helpers.h"
#include "pt2_keyboard.h"
#include "pt2_visuals.h"
-#include "pt2_sampleloader.h"
+#include "pt2_sample_loader.h"
typedef struct fileEntry_t
{
@@ -68,6 +68,16 @@
static UNICHAR pathTmp[PATH_MAX + 2];
static fileEntry_t *diskOpEntry;
+void addSampleFileExt(char *fileName)
+{
+ switch (diskop.smpSaveType)
+ {
+ case DISKOP_SMP_WAV: strcat(fileName, ".wav"); break;
+ case DISKOP_SMP_IFF: strcat(fileName, ".iff"); break;
+ default: case DISKOP_SMP_RAW: break;
+ }
+}
+
static fileEntry_t *bufferCreateEmptyDir(void) // special case: creates a dir entry with a ".." directory
{
fileEntry_t *dirEntry;
@@ -861,7 +871,6 @@
{
uint8_t oldMode, oldPlayMode;
UNICHAR *filePath;
- module_t *tempMod;
// if we clicked on an empty space, return...
if (diskOpEntryIsEmpty(fileEntryRow))
@@ -878,7 +887,7 @@
{
if (diskop.mode == DISKOP_MODE_MOD)
{
- if (songModifiedCheck && modEntry->modified)
+ if (songModifiedCheck && song->modified)
{
oldFileEntryRow = fileEntryRow;
showSongUnsavedAskBox(ASK_DISCARD_SONG);
@@ -885,8 +894,8 @@
return;
}
- tempMod = modLoad(filePath);
- if (tempMod != NULL)
+ module_t *newSong = modLoad(filePath);
+ if (newSong != NULL)
{
oldMode = editor.currMode;
oldPlayMode = editor.playMode;
@@ -894,9 +903,9 @@
modStop();
modFree();
- modEntry = tempMod;
+ song = newSong;
setupNewMod();
- modEntry->moduleLoaded = true;
+ song->loaded = true;
statusAllRight();
--- a/src/pt2_diskop.h
+++ b/src/pt2_diskop.h
@@ -11,6 +11,7 @@
#define DISKOP_LINES 10
+void addSampleFileExt(char *fileName);
void diskOpShowSelectText(void);
void diskOpLoadFile(uint32_t fileEntryRow, bool songModifiedCheck);
void diskOpLoadFile2(void);
--- a/src/pt2_edit.c
+++ b/src/pt2_edit.c
@@ -19,7 +19,6 @@
#include "pt2_textout.h"
#include "pt2_tables.h"
#include "pt2_audio.h"
-#include "pt2_helpers.h"
#include "pt2_palette.h"
#include "pt2_diskop.h"
#include "pt2_mouse.h"
@@ -28,6 +27,7 @@
#include "pt2_keyboard.h"
#include "pt2_scopes.h"
#include "pt2_structs.h"
+#include "pt2_config.h"
const int8_t scancode2NoteLo[52] = // "USB usage page standard" order
{
@@ -151,7 +151,7 @@
if (ui.editObject == PTB_SONGNAME)
{
for (i = 0; i < 20; i++)
- modEntry->head.moduleTitle[i] = (char)tolower(modEntry->head.moduleTitle[i]);
+ song->header.name[i] = (char)tolower(song->header.name[i]);
}
pointerSetPreviousMode();
@@ -163,7 +163,7 @@
{
// set back GUI text pointers and update values (if requested)
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
switch (ui.editObject)
{
case PTB_SA_FIL_LP_CUTOFF:
@@ -247,8 +247,8 @@
if (updateValue)
{
editor.samplePos = ui.tmpDisp16;
- if (editor.samplePos > modEntry->samples[editor.currSample].length)
- editor.samplePos = modEntry->samples[editor.currSample].length;
+ if (editor.samplePos > song->samples[editor.currSample].length)
+ editor.samplePos = song->samples[editor.currSample].length;
ui.updatePosText = true;
}
@@ -336,11 +336,11 @@
case PTB_PE_PATT:
{
- posEdPos = modEntry->currOrder;
- if (posEdPos > modEntry->head.orderCount-1)
- posEdPos = modEntry->head.orderCount-1;
+ posEdPos = song->currOrder;
+ if (posEdPos > song->header.numOrders-1)
+ posEdPos = song->header.numOrders-1;
- editor.currPosEdPattDisp = &modEntry->head.order[posEdPos];
+ editor.currPosEdPattDisp = &song->header.order[posEdPos];
if (updateValue)
{
@@ -347,7 +347,7 @@
if (ui.tmpDisp16 > MAX_PATTERNS-1)
ui.tmpDisp16 = MAX_PATTERNS-1;
- modEntry->head.order[posEdPos] = ui.tmpDisp16;
+ song->header.order[posEdPos] = ui.tmpDisp16;
updateWindowTitle(MOD_IS_MODIFIED);
@@ -362,7 +362,7 @@
case PTB_POSS:
{
- editor.currPosDisp = &modEntry->currOrder;
+ editor.currPosDisp = &song->currOrder;
if (updateValue)
{
@@ -370,10 +370,10 @@
if (tmp16 > 126)
tmp16 = 126;
- if (modEntry->currOrder != tmp16)
+ if (song->currOrder != tmp16)
{
- modEntry->currOrder = tmp16;
- editor.currPatternDisp = &modEntry->head.order[modEntry->currOrder];
+ song->currOrder = tmp16;
+ editor.currPatternDisp = &song->header.order[song->currOrder];
if (ui.posEdScreenShown)
ui.updatePosEd = true;
@@ -387,7 +387,7 @@
case PTB_PATTERNS:
{
- editor.currPatternDisp = &modEntry->head.order[modEntry->currOrder];
+ editor.currPatternDisp = &song->header.order[song->currOrder];
if (updateValue)
{
@@ -395,9 +395,9 @@
if (tmp16 > MAX_PATTERNS-1)
tmp16 = MAX_PATTERNS-1;
- if (modEntry->head.order[modEntry->currOrder] != tmp16)
+ if (song->header.order[song->currOrder] != tmp16)
{
- modEntry->head.order[modEntry->currOrder] = tmp16;
+ song->header.order[song->currOrder] = tmp16;
updateWindowTitle(MOD_IS_MODIFIED);
@@ -413,21 +413,21 @@
case PTB_LENGTHS:
{
- editor.currLengthDisp = &modEntry->head.orderCount;
+ editor.currLengthDisp = &song->header.numOrders;
if (updateValue)
{
tmp16 = CLAMP(ui.tmpDisp16, 1, 127);
- if (modEntry->head.orderCount != tmp16)
+ if (song->header.numOrders != tmp16)
{
- modEntry->head.orderCount = tmp16;
+ song->header.numOrders = tmp16;
- posEdPos = modEntry->currOrder;
- if (posEdPos > modEntry->head.orderCount-1)
- posEdPos = modEntry->head.orderCount-1;
+ posEdPos = song->currOrder;
+ if (posEdPos > song->header.numOrders-1)
+ posEdPos = song->header.numOrders-1;
- editor.currPosEdPattDisp = &modEntry->head.order[posEdPos];
+ editor.currPosEdPattDisp = &song->header.order[posEdPos];
if (ui.posEdScreenShown)
ui.updatePosEd = true;
@@ -442,11 +442,11 @@
case PTB_PATTDATA:
{
- editor.currEditPatternDisp = &modEntry->currPattern;
+ editor.currEditPatternDisp = &song->currPattern;
if (updateValue)
{
- if (modEntry->currPattern != ui.tmpDisp16)
+ if (song->currPattern != ui.tmpDisp16)
{
setPattern(ui.tmpDisp16);
ui.updatePatternData = true;
@@ -692,7 +692,7 @@
if (handleSpecialKeys(scancode))
{
if (editor.currMode != MODE_RECORD)
- modSetPos(DONT_SET_ORDER, (modEntry->currRow + editor.editMoveAdd) & 0x3F);
+ modSetPos(DONT_SET_ORDER, (song->currRow + editor.editMoveAdd) & 0x3F);
return;
}
@@ -736,7 +736,7 @@
key += hexKey;
}
- note = &modEntry->patterns[modEntry->currPattern][(modEntry->currRow * AMIGA_VOICES) + cursor.channel];
+ note = &song->patterns[song->currPattern][(song->currRow * AMIGA_VOICES) + cursor.channel];
switch (cursor.mode)
{
@@ -747,7 +747,7 @@
note->sample = (uint8_t)((note->sample % 0x10) | (key << 4));
if (editor.currMode != MODE_RECORD)
- modSetPos(DONT_SET_ORDER, (modEntry->currRow + editor.editMoveAdd) & 0x3F);
+ modSetPos(DONT_SET_ORDER, (song->currRow + editor.editMoveAdd) & 0x3F);
updateWindowTitle(MOD_IS_MODIFIED);
}
@@ -761,7 +761,7 @@
note->sample = (uint8_t)((note->sample & 16) | key);
if (editor.currMode != MODE_RECORD)
- modSetPos(DONT_SET_ORDER, (modEntry->currRow + editor.editMoveAdd) & 0x3F);
+ modSetPos(DONT_SET_ORDER, (song->currRow + editor.editMoveAdd) & 0x3F);
updateWindowTitle(MOD_IS_MODIFIED);
}
@@ -775,7 +775,7 @@
note->command = (uint8_t)key;
if (editor.currMode != MODE_RECORD)
- modSetPos(DONT_SET_ORDER, (modEntry->currRow + editor.editMoveAdd) & 0x3F);
+ modSetPos(DONT_SET_ORDER, (song->currRow + editor.editMoveAdd) & 0x3F);
updateWindowTitle(MOD_IS_MODIFIED);
}
@@ -789,7 +789,7 @@
note->param = (uint8_t)((note->param % 0x10) | (key << 4));
if (editor.currMode != MODE_RECORD)
- modSetPos(DONT_SET_ORDER, (modEntry->currRow + editor.editMoveAdd) & 0x3F);
+ modSetPos(DONT_SET_ORDER, (song->currRow + editor.editMoveAdd) & 0x3F);
updateWindowTitle(MOD_IS_MODIFIED);
}
@@ -803,7 +803,7 @@
note->param = (uint8_t)((note->param & 0xF0) | key);
if (editor.currMode != MODE_RECORD)
- modSetPos(DONT_SET_ORDER, (modEntry->currRow + editor.editMoveAdd) & 0x3F);
+ modSetPos(DONT_SET_ORDER, (song->currRow + editor.editMoveAdd) & 0x3F);
updateWindowTitle(MOD_IS_MODIFIED);
}
@@ -820,7 +820,7 @@
{
if (editor.currMode == MODE_EDIT || editor.currMode == MODE_RECORD)
{
- note = &modEntry->patterns[modEntry->currPattern][(modEntry->currRow * AMIGA_VOICES) + cursor.channel];
+ note = &song->patterns[song->currPattern][(song->currRow * AMIGA_VOICES) + cursor.channel];
if (!keyb.leftAltPressed)
{
@@ -835,7 +835,7 @@
}
if (editor.currMode != MODE_RECORD)
- modSetPos(DONT_SET_ORDER, (modEntry->currRow + editor.editMoveAdd) & 0x3F);
+ modSetPos(DONT_SET_ORDER, (song->currRow + editor.editMoveAdd) & 0x3F);
updateWindowTitle(MOD_IS_MODIFIED);
}
@@ -858,9 +858,9 @@
if (!keyb.leftAltPressed)
return false;
- patt = modEntry->patterns[modEntry->currPattern];
- note = &patt[(modEntry->currRow * AMIGA_VOICES) + cursor.channel];
- prevNote = &patt[(((modEntry->currRow - 1) & 0x3F) * AMIGA_VOICES) + cursor.channel];
+ patt = song->patterns[song->currPattern];
+ note = &patt[(song->currRow * AMIGA_VOICES) + cursor.channel];
+ prevNote = &patt[(((song->currRow - 1) & 0x3F) * AMIGA_VOICES) + cursor.channel];
if (scancode >= SDL_SCANCODE_1 && scancode <= SDL_SCANCODE_0)
{
@@ -918,13 +918,13 @@
ch = cursor.channel;
assert(ch < AMIGA_VOICES);
- chn = &modEntry->channels[ch];
- note = &modEntry->patterns[modEntry->currPattern][(quantizeCheck(modEntry->currRow) * AMIGA_VOICES) + ch];
+ chn = &song->channels[ch];
+ note = &song->patterns[song->currPattern][(quantizeCheck(song->currRow) * AMIGA_VOICES) + ch];
noteVal = normalMode ? keyToNote(scancode) : pNoteTable[editor.currSample];
if (noteVal >= 0)
{
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
tempPeriod = periodTable[((s->fineTune & 0xF) * 37) + noteVal];
cleanPeriod = periodTable[noteVal];
@@ -939,9 +939,9 @@
chn->n_samplenum = editor.currSample;
chn->n_volume = s->volume;
chn->n_period = tempPeriod;
- chn->n_start = &modEntry->sampleData[s->offset];
+ chn->n_start = &song->sampleData[s->offset];
chn->n_length = (s->loopStart > 0) ? (s->loopStart + s->loopLength) >> 1 : s->length >> 1;
- chn->n_loopstart = &modEntry->sampleData[s->offset + s->loopStart];
+ chn->n_loopstart = &song->sampleData[s->offset + s->loopStart];
chn->n_replen = s->loopLength >> 1;
if (chn->n_length == 0)
@@ -980,7 +980,7 @@
}
if (editor.currMode != MODE_RECORD)
- modSetPos(DONT_SET_ORDER, (modEntry->currRow + editor.editMoveAdd) & 0x3F);
+ modSetPos(DONT_SET_ORDER, (song->currRow + editor.editMoveAdd) & 0x3F);
updateWindowTitle(MOD_IS_MODIFIED);
}
@@ -1004,7 +1004,7 @@
note->sample = 0;
if (editor.currMode != MODE_RECORD)
- modSetPos(DONT_SET_ORDER, (modEntry->currRow + editor.editMoveAdd) & 0x3F);
+ modSetPos(DONT_SET_ORDER, (song->currRow + editor.editMoveAdd) & 0x3F);
updateWindowTitle(MOD_IS_MODIFIED);
}
@@ -1014,9 +1014,11 @@
uint8_t quantizeCheck(uint8_t row)
{
- uint8_t tempRow, quantize;
+ assert(song != NULL);
+ if (song == NULL)
+ return row;
- quantize = (uint8_t)config.quantizeValue;
+ const uint8_t quantize = (uint8_t)config.quantizeValue;
editor.didQuantize = false;
if (editor.currMode == MODE_RECORD)
@@ -1027,7 +1029,7 @@
}
else if (quantize == 1)
{
- if (editor.modTick > editor.modSpeed/2)
+ if (song->tick > song->speed>>1)
{
row = (row + 1) & 0x3F;
editor.didQuantize = true;
@@ -1035,7 +1037,7 @@
}
else
{
- tempRow = ((((quantize / 2) + row) & 0x3F) / quantize) * quantize;
+ uint8_t tempRow = ((((quantize >> 1) + row) & 0x3F) / quantize) * quantize;
if (tempRow > row)
editor.didQuantize = true;
@@ -1048,7 +1050,7 @@
void saveUndo(void)
{
- memcpy(editor.undoBuffer, modEntry->patterns[modEntry->currPattern], sizeof (note_t) * (AMIGA_VOICES * MOD_ROWS));
+ memcpy(editor.undoBuffer, song->patterns[song->currPattern], sizeof (note_t) * (AMIGA_VOICES * MOD_ROWS));
}
void undoLastChange(void)
@@ -1058,8 +1060,8 @@
for (uint16_t i = 0; i < MOD_ROWS*AMIGA_VOICES; i++)
{
data = editor.undoBuffer[i];
- editor.undoBuffer[i] = modEntry->patterns[modEntry->currPattern][i];
- modEntry->patterns[modEntry->currPattern][i] = data;
+ editor.undoBuffer[i] = song->patterns[song->currPattern][i];
+ song->patterns[song->currPattern][i] = data;
}
updateWindowTitle(MOD_IS_MODIFIED);
@@ -1084,8 +1086,8 @@
return;
}
- smpTo = &modEntry->samples[editor.sampleTo - 1];
- smpFrom = &modEntry->samples[editor.sampleFrom - 1];
+ smpTo = &song->samples[editor.sampleTo - 1];
+ smpFrom = &song->samples[editor.sampleFrom - 1];
turnOffVoices();
@@ -1101,7 +1103,7 @@
smpTo->loopLengthDisp = &smpTo->loopLength;
// copy sample data
- memcpy(&modEntry->sampleData[smpTo->offset], &modEntry->sampleData[smpFrom->offset], MAX_SAMPLE_LEN);
+ memcpy(&song->sampleData[smpTo->offset], &song->sampleData[smpFrom->offset], MAX_SAMPLE_LEN);
updateCurrSample();
ui.updateSongSize = true;
@@ -1113,7 +1115,7 @@
{
for (i = 0; i < MOD_ROWS; i++)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + cursor.channel];
+ noteSrc = &song->patterns[song->currPattern][(i * AMIGA_VOICES) + cursor.channel];
if (noteSrc->sample == editor.sampleFrom)
noteSrc->sample = editor.sampleTo;
}
@@ -1124,7 +1126,7 @@
{
for (uint8_t j = 0; j < MOD_ROWS; j++)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(j * AMIGA_VOICES) + i];
+ noteSrc = &song->patterns[song->currPattern][(j * AMIGA_VOICES) + i];
if (noteSrc->sample == editor.sampleFrom)
noteSrc->sample = editor.sampleTo;
}
@@ -1158,8 +1160,8 @@
return;
}
- smpTo = &modEntry->samples[editor.sampleTo-1];
- smpFrom = &modEntry->samples[editor.sampleFrom-1];
+ smpTo = &song->samples[editor.sampleTo-1];
+ smpFrom = &song->samples[editor.sampleFrom-1];
turnOffVoices();
@@ -1186,9 +1188,9 @@
// swap sample data
for (i = 0; i < MAX_SAMPLE_LEN; i++)
{
- smp = modEntry->sampleData[smpFrom->offset+i];
- modEntry->sampleData[smpFrom->offset+i] = modEntry->sampleData[smpTo->offset+i];
- modEntry->sampleData[smpTo->offset+i] = smp;
+ smp = song->sampleData[smpFrom->offset+i];
+ song->sampleData[smpFrom->offset+i] = song->sampleData[smpTo->offset+i];
+ song->sampleData[smpTo->offset+i] = smp;
}
editor.sampleZero = false;
@@ -1202,7 +1204,7 @@
{
for (i = 0; i < MOD_ROWS; i++)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + cursor.channel];
+ noteSrc = &song->patterns[song->currPattern][(i * AMIGA_VOICES) + cursor.channel];
if (noteSrc->sample == editor.sampleFrom) noteSrc->sample = editor.sampleTo;
else if (noteSrc->sample == editor.sampleTo) noteSrc->sample = editor.sampleFrom;
@@ -1214,7 +1216,7 @@
{
for (uint8_t j = 0; j < MOD_ROWS; j++)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(j * AMIGA_VOICES) + i];
+ noteSrc = &song->patterns[song->currPattern][(j * AMIGA_VOICES) + i];
if (noteSrc->sample == editor.sampleFrom) noteSrc->sample = editor.sampleTo;
else if (noteSrc->sample == editor.sampleTo) noteSrc->sample = editor.sampleFrom;
@@ -1241,7 +1243,7 @@
{
for (i = 0; i < MOD_ROWS; i++)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + cursor.channel];
+ noteSrc = &song->patterns[song->currPattern][(i * AMIGA_VOICES) + cursor.channel];
if (noteSrc->sample == editor.currSample+1)
{
noteSrc->period = 0;
@@ -1257,7 +1259,7 @@
{
for (uint8_t j = 0; j < MOD_ROWS; j++)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(j * AMIGA_VOICES) + i];
+ noteSrc = &song->patterns[song->currPattern][(j * AMIGA_VOICES) + i];
if (noteSrc->sample == editor.currSample+1)
{
noteSrc->period = 0;
@@ -1289,7 +1291,7 @@
saveUndo();
for (uint8_t i = from; i <= to; i++)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + cursor.channel];
+ noteSrc = &song->patterns[song->currPattern][(i * AMIGA_VOICES) + cursor.channel];
if (!sampleAllFlag && noteSrc->sample != editor.currSample+1)
continue;
@@ -1342,7 +1344,7 @@
saveUndo();
for (uint8_t i = from; i <= to; i++)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + cursor.channel];
+ noteSrc = &song->patterns[song->currPattern][(i * AMIGA_VOICES) + cursor.channel];
if (!sampleAllFlag && noteSrc->sample != editor.currSample+1)
continue;
@@ -1397,7 +1399,7 @@
saveUndo();
for (uint8_t i = from; i <= to; i++)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + cursor.channel];
+ noteSrc = &song->patterns[song->currPattern][(i * AMIGA_VOICES) + cursor.channel];
if (!sampleAllFlag && noteSrc->sample != editor.currSample+1)
continue;
@@ -1456,7 +1458,7 @@
saveUndo();
for (uint8_t i = from; i <= to; i++)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + cursor.channel];
+ noteSrc = &song->patterns[song->currPattern][(i * AMIGA_VOICES) + cursor.channel];
if (!sampleAllFlag && noteSrc->sample != editor.currSample+1)
continue;
@@ -1502,7 +1504,7 @@
{
for (uint8_t j = 0; j < MOD_ROWS; j++)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(j * AMIGA_VOICES) + i];
+ noteSrc = &song->patterns[song->currPattern][(j * AMIGA_VOICES) + i];
if (!sampleAllFlag && noteSrc->sample != editor.currSample+1)
continue;
@@ -1551,7 +1553,7 @@
{
for (uint8_t j = 0; j < MOD_ROWS; j++)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(j * AMIGA_VOICES) + i];
+ noteSrc = &song->patterns[song->currPattern][(j * AMIGA_VOICES) + i];
if (!sampleAllFlag && noteSrc->sample != editor.currSample+1)
continue;
@@ -1600,7 +1602,7 @@
{
for (uint8_t j = 0; j < MOD_ROWS; j++)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(j * AMIGA_VOICES) + i];
+ noteSrc = &song->patterns[song->currPattern][(j * AMIGA_VOICES) + i];
if (!sampleAllFlag && noteSrc->sample != editor.currSample+1)
continue;
@@ -1647,7 +1649,7 @@
{
for (uint8_t j = 0; j < MOD_ROWS; j++)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(j * AMIGA_VOICES) + i];
+ noteSrc = &song->patterns[song->currPattern][(j * AMIGA_VOICES) + i];
if (!sampleAllFlag && noteSrc->sample != editor.currSample+1)
continue;
--- a/src/pt2_header.h
+++ b/src/pt2_header.h
@@ -6,7 +6,7 @@
#include <assert.h>
#ifdef _WIN32
#define WIN32_MEAN_AND_LEAN
-#include <windows.h>
+#include <windows.h> // MAX_PATH
#else
#include <limits.h> // PATH_MAX
#endif
@@ -14,7 +14,7 @@
#include "pt2_unicode.h"
#include "pt2_palette.h"
-#define PROG_VER_STR "1.12"
+#define PROG_VER_STR "1.13"
#ifdef _WIN32
#define DIR_DELIMITER '\\'
@@ -21,11 +21,6 @@
#define PATH_MAX MAX_PATH
#else
#define DIR_DELIMITER '/'
-#endif
-
-#include "pt2_config.h" // this must be included after PATH_MAX definition
-
-#ifndef _WIN32
#define _stricmp strcasecmp
#define _strnicmp strncasecmp
#endif
@@ -37,12 +32,14 @@
#define SCREEN_H 255
/* "60Hz" ranges everywhere from 59..61Hz depending on the monitor, so with
-** no vsync we will get stuttering because the rate is not perfect... */
+** no vsync we will get stuttering because the rate is not perfect...
+*/
#define VBLANK_HZ 60
/* Scopes are clocked at 64Hz instead of 60Hz to prevent +/- interference
** from monitors not being exactly 60Hz (and unstable non-vsync mode).
-** Sadly the scopes might midly flicker from this. */
+** Sadly the scopes might midly flicker from this.
+*/
#define SCOPE_HZ 64
#define AMIGA_PAL_VBLANK_HZ 50
--- a/src/pt2_helpers.c
+++ b/src/pt2_helpers.c
@@ -20,6 +20,7 @@
#include "pt2_tables.h"
#include "pt2_palette.h"
#include "pt2_structs.h"
+#include "pt2_config.h"
// used for Windows usleep() implementation
#ifdef _WIN32
@@ -120,25 +121,25 @@
char titleTemp[128];
if (modified)
- modEntry->modified = true;
+ song->modified = true;
else
- modEntry->modified = false;
+ song->modified = false;
- if (modEntry->head.moduleTitle[0] != '\0')
+ if (song->header.name[0] != '\0')
{
if (modified)
{
if (config.modDot)
- sprintf(titleTemp, "ProTracker 2 clone v%s - \"mod.%s\" (unsaved)", PROG_VER_STR, modEntry->head.moduleTitle);
+ sprintf(titleTemp, "ProTracker 2 clone v%s - \"mod.%s\" (unsaved)", PROG_VER_STR, song->header.name);
else
- sprintf(titleTemp, "ProTracker 2 clone v%s - \"%s.mod\" (unsaved)", PROG_VER_STR, modEntry->head.moduleTitle);
+ sprintf(titleTemp, "ProTracker 2 clone v%s - \"%s.mod\" (unsaved)", PROG_VER_STR, song->header.name);
}
else
{
if (config.modDot)
- sprintf(titleTemp, "ProTracker 2 clone v%s - \"mod.%s\"", PROG_VER_STR, modEntry->head.moduleTitle);
+ sprintf(titleTemp, "ProTracker 2 clone v%s - \"mod.%s\"", PROG_VER_STR, song->header.name);
else
- sprintf(titleTemp, "ProTracker 2 clone v%s - \"%s.mod\"", PROG_VER_STR, modEntry->head.moduleTitle);
+ sprintf(titleTemp, "ProTracker 2 clone v%s - \"%s.mod\"", PROG_VER_STR, song->header.name);
}
}
else
@@ -168,7 +169,7 @@
int32_t len;
moduleSample_t *s;
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
if (editor.chordLengthMin)
{
--- a/src/pt2_keyboard.c
+++ b/src/pt2_keyboard.c
@@ -23,9 +23,11 @@
#include "pt2_audio.h"
#include "pt2_keyboard.h"
#include "pt2_tables.h"
-#include "pt2_modloader.h"
+#include "pt2_module_loader.h"
+#include "pt2_module_saver.h"
#include "pt2_mouse.h"
#include "pt2_unicode.h"
+#include "pt2_config.h"
#if defined _WIN32 && !defined _DEBUG
extern bool windowsKeyIsDown;
@@ -52,10 +54,8 @@
void readKeyModifiers(void)
{
- uint32_t modState;
+ const SDL_Keymod modState = SDL_GetModState();
- modState = SDL_GetModState();
-
keyb.leftCtrlPressed = (modState & KMOD_LCTRL) ? true : false;
keyb.leftAltPressed = (modState & KMOD_LALT) ? true : false;
keyb.shiftPressed = (modState & (KMOD_LSHIFT + KMOD_RSHIFT)) ? true : false;
@@ -64,100 +64,75 @@
keyb.leftCommandPressed = (modState & KMOD_LGUI) ? true : false;
#endif
-#if defined _WIN32 && !defined _DEBUG // Windows: handled in lowLevelKeyboardProc
+#if defined _WIN32 && !defined _DEBUG
+ keyb.leftAmigaPressed = windowsKeyIsDown; // Windows: handled in lowLevelKeyboardProc
+#else
keyb.leftAmigaPressed = (modState & KMOD_LGUI) ? true : false;
#endif
}
#if defined _WIN32 && !defined _DEBUG
-/* For taking control over windows key and numlock on keyboard if app has focus.
+/* For taking control over windows key if the program has focus.
** Warning: Don't do this in debug mode, it will completely ruin the keyboard input
-** latency when the debugger is breaking.
+** latency (in the OS in general) when the debugger is breaking.
*/
LRESULT CALLBACK lowLevelKeyboardProc(int32_t nCode, WPARAM wParam, LPARAM lParam)
{
- bool bEatKeystroke;
- KBDLLHOOKSTRUCT *p;
SDL_Event inputEvent;
+ SDL_Window *window = video.window;
- if (nCode < 0 || nCode != HC_ACTION) // do not process message
+ if (window == NULL || nCode < 0 || nCode != HC_ACTION) // do not process message
return CallNextHookEx(g_hKeyboardHook, nCode, wParam, lParam);
- bEatKeystroke = false;
- p = (KBDLLHOOKSTRUCT *)lParam;
-
+ KBDLLHOOKSTRUCT *p = (KBDLLHOOKSTRUCT *)lParam;
switch (wParam)
{
case WM_KEYUP:
case WM_KEYDOWN:
{
- bEatKeystroke = (SDL_GetWindowFlags(video.window) & SDL_WINDOW_INPUT_FOCUS) && (p->vkCode == VK_LWIN || p->vkCode == VK_NUMLOCK);
+ const bool windowHasFocus = SDL_GetWindowFlags(window) & SDL_WINDOW_INPUT_FOCUS;
- if (bEatKeystroke)
- {
- if (wParam == WM_KEYDOWN)
- {
- if (p->vkCode == VK_NUMLOCK)
- {
- memset(&inputEvent, 0, sizeof (SDL_Event));
- inputEvent.type = SDL_KEYDOWN;
- inputEvent.key.type = SDL_KEYDOWN;
- inputEvent.key.state = 1;
- inputEvent.key.keysym.scancode = (SDL_Scancode)69;
- inputEvent.key.keysym.mod = KMOD_NUM;
- inputEvent.key.keysym.scancode = SDL_SCANCODE_NUMLOCKCLEAR;
+ const bool bEatKeystroke = windowHasFocus && p->vkCode == VK_LWIN;
+ if (!bEatKeystroke)
+ return CallNextHookEx(g_hKeyboardHook, nCode, wParam, lParam);
- SDL_PushEvent(&inputEvent);
- }
- else if (!windowsKeyIsDown)
- {
- windowsKeyIsDown = true;
- keyb.leftAmigaPressed = true;
+ memset(&inputEvent, 0, sizeof (SDL_Event));
- memset(&inputEvent, 0, sizeof (SDL_Event));
- inputEvent.type = SDL_KEYDOWN;
- inputEvent.key.type = SDL_KEYDOWN;
- inputEvent.key.state = 1;
- inputEvent.key.keysym.scancode = (SDL_Scancode)91;
- inputEvent.key.keysym.scancode = SDL_SCANCODE_LGUI;
+ const bool keyDown = (wParam == WM_KEYDOWN);
+ if (keyDown)
+ {
+ if (windowsKeyIsDown)
+ break; // Windows-key is already down (XXX: Do we need this check?)
- SDL_PushEvent(&inputEvent);
- }
- }
- else if (wParam == WM_KEYUP)
- {
- if (p->vkCode == VK_NUMLOCK)
- {
- memset(&inputEvent, 0, sizeof (SDL_Event));
- inputEvent.type = SDL_KEYUP;
- inputEvent.key.type = SDL_KEYUP;
- inputEvent.key.keysym.scancode = (SDL_Scancode)69;
- inputEvent.key.keysym.scancode = SDL_SCANCODE_NUMLOCKCLEAR;
+ inputEvent.type = SDL_KEYDOWN;
+ inputEvent.key.type = SDL_KEYDOWN;
+ inputEvent.key.state = SDL_PRESSED;
- SDL_PushEvent(&inputEvent);
- }
- else
- {
- windowsKeyIsDown = false;
- keyb.leftAmigaPressed = false;
+ windowsKeyIsDown = true;
+ }
+ else
+ {
+ inputEvent.type = SDL_KEYUP;
+ inputEvent.key.type = SDL_KEYUP;
+ inputEvent.key.state = SDL_RELEASED;
- memset(&inputEvent, 0, sizeof (SDL_Event));
- inputEvent.type = SDL_KEYUP;
- inputEvent.key.type = SDL_KEYUP;
- inputEvent.key.keysym.scancode = (SDL_Scancode)91;
- inputEvent.key.keysym.scancode = SDL_SCANCODE_LGUI;
-
- SDL_PushEvent(&inputEvent);
- }
- }
+ windowsKeyIsDown = false;
}
- break;
+
+ inputEvent.key.keysym.sym = SDLK_LGUI;
+ inputEvent.key.keysym.scancode = SDL_SCANCODE_LGUI;
+ inputEvent.key.keysym.mod = SDL_GetModState();
+ inputEvent.key.timestamp = SDL_GetTicks();
+ inputEvent.key.windowID = SDL_GetWindowID(window);
+
+ SDL_PushEvent(&inputEvent);
}
+ break;
default: break;
}
- return bEatKeystroke ? true : CallNextHookEx(g_hKeyboardHook, nCode, wParam, lParam);
+ return true;
}
#endif
@@ -489,7 +464,7 @@
if (keyb.leftAltPressed)
{
if (handleSpecialKeys(scancode) && editor.currMode != MODE_RECORD)
- modSetPos(DONT_SET_ORDER, (modEntry->currRow + editor.editMoveAdd) & 0x3F);
+ modSetPos(DONT_SET_ORDER, (song->currRow + editor.editMoveAdd) & 0x3F);
}
else
{
@@ -523,7 +498,7 @@
if (!ui.askScreenShown)
{
editor.playMode = PLAY_MODE_NORMAL;
- modPlay(DONT_SET_PATTERN, modEntry->currOrder, DONT_SET_ROW);
+ modPlay(DONT_SET_PATTERN, song->currOrder, DONT_SET_ROW);
editor.currMode = MODE_PLAY;
pointerSetMode(POINTER_MODE_PLAY, DO_CARRY);
statusAllRight();
@@ -541,7 +516,7 @@
if (!ui.askScreenShown)
{
editor.playMode = PLAY_MODE_PATTERN;
- modPlay(modEntry->currPattern, DONT_SET_ORDER, DONT_SET_ROW);
+ modPlay(song->currPattern, DONT_SET_ORDER, DONT_SET_ROW);
editor.currMode = MODE_PLAY;
pointerSetMode(POINTER_MODE_PLAY, DO_CARRY);
statusAllRight();
@@ -555,7 +530,7 @@
if (!ui.samplerScreenShown && !ui.askScreenShown)
{
editor.playMode = PLAY_MODE_PATTERN;
- modPlay(modEntry->currPattern, DONT_SET_ORDER, DONT_SET_ROW);
+ modPlay(song->currPattern, DONT_SET_ORDER, DONT_SET_ROW);
editor.currMode = MODE_RECORD;
pointerSetMode(POINTER_MODE_EDIT, DO_CARRY);
statusAllRight();
@@ -614,10 +589,10 @@
{
if (ui.posEdScreenShown)
{
- if (modEntry->currOrder > 0)
+ if (song->currOrder > 0)
{
- if (modEntry->currOrder-(POSED_LIST_SIZE-1) > 0)
- modSetPos(modEntry->currOrder-(POSED_LIST_SIZE-1), DONT_SET_ROW);
+ if (song->currOrder-(POSED_LIST_SIZE-1) > 0)
+ modSetPos(song->currOrder-(POSED_LIST_SIZE-1), DONT_SET_ROW);
else
modSetPos(0, DONT_SET_ROW);
}
@@ -634,12 +609,12 @@
{
if (editor.currMode == MODE_IDLE || editor.currMode == MODE_EDIT)
{
- if (modEntry->currRow == 63)
- modSetPos(DONT_SET_ORDER, modEntry->currRow - 15);
- else if (modEntry->currRow == 15)
+ if (song->currRow == 63)
+ modSetPos(DONT_SET_ORDER, song->currRow - 15);
+ else if (song->currRow == 15)
modSetPos(DONT_SET_ORDER, 0); // 15-16 would turn into -1, which is "DON'T SET ROW" flag
else
- modSetPos(DONT_SET_ORDER, modEntry->currRow - 16);
+ modSetPos(DONT_SET_ORDER, song->currRow - 16);
}
}
@@ -655,12 +630,12 @@
{
if (ui.posEdScreenShown)
{
- if (modEntry->currOrder != modEntry->head.orderCount-1)
+ if (song->currOrder != song->header.numOrders-1)
{
- if (modEntry->currOrder+(POSED_LIST_SIZE-1) <= modEntry->head.orderCount-1)
- modSetPos(modEntry->currOrder+(POSED_LIST_SIZE-1), DONT_SET_ROW);
+ if (song->currOrder+(POSED_LIST_SIZE-1) <= song->header.numOrders-1)
+ modSetPos(song->currOrder+(POSED_LIST_SIZE-1), DONT_SET_ROW);
else
- modSetPos(modEntry->head.orderCount - 1, DONT_SET_ROW);
+ modSetPos(song->header.numOrders - 1, DONT_SET_ROW);
}
}
else if (ui.diskOpScreenShown)
@@ -677,7 +652,7 @@
else
{
if (editor.currMode == MODE_IDLE || editor.currMode == MODE_EDIT)
- modSetPos(DONT_SET_ORDER, modEntry->currRow + 16);
+ modSetPos(DONT_SET_ORDER, song->currRow + 16);
}
if (!keyb.repeatKey)
@@ -692,7 +667,7 @@
{
if (ui.posEdScreenShown)
{
- if (modEntry->currOrder > 0)
+ if (song->currOrder > 0)
modSetPos(0, DONT_SET_ROW);
}
else if (ui.diskOpScreenShown)
@@ -715,7 +690,7 @@
{
if (ui.posEdScreenShown)
{
- modSetPos(modEntry->head.orderCount - 1, DONT_SET_ROW);
+ modSetPos(song->header.numOrders - 1, DONT_SET_ROW);
}
else if (ui.diskOpScreenShown)
{
@@ -749,7 +724,7 @@
editor.timingMode ^= 1;
if (editor.timingMode == TEMPO_MODE_VBLANK)
{
- editor.oldTempo = modEntry->currBPM;
+ editor.oldTempo = song->currBPM;
modSetTempo(125);
}
else
@@ -787,17 +762,17 @@
saveUndo();
if (keyb.leftAltPressed && !keyb.leftCtrlPressed)
{
- if (modEntry->currRow < 63)
+ if (song->currRow < 63)
{
for (i = 0; i < AMIGA_VOICES; i++)
{
- for (j = 62; j >= modEntry->currRow; j--)
+ for (j = 62; j >= song->currRow; j--)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(j * AMIGA_VOICES) + i];
- modEntry->patterns[modEntry->currPattern][((j + 1) * AMIGA_VOICES) + i] = *noteSrc;
+ noteSrc = &song->patterns[song->currPattern][(j * AMIGA_VOICES) + i];
+ song->patterns[song->currPattern][((j + 1) * AMIGA_VOICES) + i] = *noteSrc;
}
- noteDst = &modEntry->patterns[modEntry->currPattern][((j + 1) * AMIGA_VOICES) + i];
+ noteDst = &song->patterns[song->currPattern][((j + 1) * AMIGA_VOICES) + i];
noteDst->period = 0;
noteDst->sample = 0;
@@ -805,7 +780,7 @@
noteDst->param = 0;
}
- modEntry->currRow++;
+ song->currRow++;
updateWindowTitle(MOD_IS_MODIFIED);
ui.updatePatternData = true;
@@ -813,12 +788,12 @@
}
else
{
- if (modEntry->currRow < 63)
+ if (song->currRow < 63)
{
- for (i = 62; i >= modEntry->currRow; i--)
+ for (i = 62; i >= song->currRow; i--)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][((i + 0) * AMIGA_VOICES) + cursor.channel];
- noteDst = &modEntry->patterns[modEntry->currPattern][((i + 1) * AMIGA_VOICES) + cursor.channel];
+ noteSrc = &song->patterns[song->currPattern][((i + 0) * AMIGA_VOICES) + cursor.channel];
+ noteDst = &song->patterns[song->currPattern][((i + 1) * AMIGA_VOICES) + cursor.channel];
if (keyb.leftCtrlPressed)
{
@@ -831,7 +806,7 @@
}
}
- noteDst = &modEntry->patterns[modEntry->currPattern][((i + 1) * AMIGA_VOICES) + cursor.channel];
+ noteDst = &song->patterns[song->currPattern][((i + 1) * AMIGA_VOICES) + cursor.channel];
if (!keyb.leftCtrlPressed)
{
@@ -842,7 +817,7 @@
noteDst->command = 0;
noteDst->param = 0;
- modEntry->currRow++;
+ song->currRow++;
updateWindowTitle(MOD_IS_MODIFIED);
ui.updatePatternData = true;
@@ -855,7 +830,7 @@
editor.stepPlayBackwards = false;
doStopIt(true);
- playPattern(modEntry->currRow);
+ playPattern(song->currRow);
}
}
}
@@ -910,7 +885,7 @@
noteDst = editor.trackBuffer;
for (i = 0; i < MOD_ROWS; i++)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + cursor.channel];
+ noteSrc = &song->patterns[song->currPattern][(i * AMIGA_VOICES) + cursor.channel];
*noteDst++ = *noteSrc;
noteSrc->period = 0;
@@ -927,10 +902,10 @@
// cut pattern and put in buffer
saveUndo();
- memcpy(editor.patternBuffer, modEntry->patterns[modEntry->currPattern],
+ memcpy(editor.patternBuffer, song->patterns[song->currPattern],
sizeof (note_t) * (AMIGA_VOICES * MOD_ROWS));
- memset(modEntry->patterns[modEntry->currPattern], 0,
+ memset(song->patterns[song->currPattern], 0,
sizeof (note_t) * (AMIGA_VOICES * MOD_ROWS));
updateWindowTitle(MOD_IS_MODIFIED);
@@ -944,7 +919,7 @@
noteDst = editor.cmdsBuffer;
for (i = 0; i < MOD_ROWS; i++)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + cursor.channel];
+ noteSrc = &song->patterns[song->currPattern][(i * AMIGA_VOICES) + cursor.channel];
*noteDst++ = *noteSrc;
noteSrc->command = 0;
@@ -972,13 +947,13 @@
noteDst = editor.trackBuffer;
for (i = 0; i < MOD_ROWS; i++)
- *noteDst++ = modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + cursor.channel];
+ *noteDst++ = song->patterns[song->currPattern][(i * AMIGA_VOICES) + cursor.channel];
}
else if (keyb.leftAltPressed)
{
// copy pattern to buffer
- memcpy(editor.patternBuffer, modEntry->patterns[modEntry->currPattern],
+ memcpy(editor.patternBuffer, song->patterns[song->currPattern],
sizeof (note_t) * (AMIGA_VOICES * MOD_ROWS));
}
else if (keyb.leftCtrlPressed)
@@ -988,7 +963,7 @@
noteDst = editor.cmdsBuffer;
for (i = 0; i < MOD_ROWS; i++)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + cursor.channel];
+ noteSrc = &song->patterns[song->currPattern][(i * AMIGA_VOICES) + cursor.channel];
noteDst->command = noteSrc->command;
noteDst->param = noteSrc->param;
@@ -1014,7 +989,7 @@
noteSrc = editor.trackBuffer;
for (i = 0; i < MOD_ROWS; i++)
- modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + cursor.channel] = *noteSrc++;
+ song->patterns[song->currPattern][(i * AMIGA_VOICES) + cursor.channel] = *noteSrc++;
updateWindowTitle(MOD_IS_MODIFIED);
ui.updatePatternData = true;
@@ -1024,7 +999,7 @@
// paste pattern buffer to pattern
saveUndo();
- memcpy(modEntry->patterns[modEntry->currPattern],
+ memcpy(song->patterns[song->currPattern],
editor.patternBuffer, sizeof (note_t) * (AMIGA_VOICES * MOD_ROWS));
updateWindowTitle(MOD_IS_MODIFIED);
@@ -1038,7 +1013,7 @@
noteSrc = editor.cmdsBuffer;
for (i = 0; i < MOD_ROWS; i++)
{
- noteDst = &modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + cursor.channel];
+ noteDst = &song->patterns[song->currPattern][(i * AMIGA_VOICES) + cursor.channel];
noteDst->command = noteSrc->command;
noteDst->param = noteSrc->param;
@@ -1056,7 +1031,7 @@
{
if (keyb.shiftPressed)
{
- editor.f6Pos = modEntry->currRow;
+ editor.f6Pos = song->currRow;
displayMsg("POSITION SET");
}
else
@@ -1064,7 +1039,7 @@
if (keyb.leftAltPressed)
{
editor.playMode = PLAY_MODE_PATTERN;
- modPlay(modEntry->currPattern, DONT_SET_ORDER, editor.f6Pos);
+ modPlay(song->currPattern, DONT_SET_ORDER, editor.f6Pos);
editor.currMode = MODE_PLAY;
pointerSetMode(POINTER_MODE_PLAY, DO_CARRY);
@@ -1075,7 +1050,7 @@
if (!ui.samplerScreenShown)
{
editor.playMode = PLAY_MODE_PATTERN;
- modPlay(modEntry->currPattern, DONT_SET_ORDER, editor.f6Pos);
+ modPlay(song->currPattern, DONT_SET_ORDER, editor.f6Pos);
editor.currMode = MODE_RECORD;
pointerSetMode(POINTER_MODE_EDIT, DO_CARRY);
@@ -1085,7 +1060,7 @@
else if (keyb.leftAmigaPressed)
{
editor.playMode = PLAY_MODE_NORMAL;
- modPlay(DONT_SET_PATTERN, modEntry->currOrder, editor.f6Pos);
+ modPlay(DONT_SET_PATTERN, song->currOrder, editor.f6Pos);
editor.currMode = MODE_PLAY;
pointerSetMode(POINTER_MODE_PLAY, DO_CARRY);
@@ -1103,7 +1078,7 @@
{
if (keyb.shiftPressed)
{
- editor.f7Pos = modEntry->currRow;
+ editor.f7Pos = song->currRow;
displayMsg("POSITION SET");
}
else
@@ -1111,7 +1086,7 @@
if (keyb.leftAltPressed)
{
editor.playMode = PLAY_MODE_PATTERN;
- modPlay(modEntry->currPattern, DONT_SET_ORDER, editor.f7Pos);
+ modPlay(song->currPattern, DONT_SET_ORDER, editor.f7Pos);
editor.currMode = MODE_PLAY;
pointerSetMode(POINTER_MODE_PLAY, DO_CARRY);
@@ -1122,7 +1097,7 @@
if (!ui.samplerScreenShown)
{
editor.playMode = PLAY_MODE_PATTERN;
- modPlay(modEntry->currPattern, DONT_SET_ORDER, editor.f7Pos);
+ modPlay(song->currPattern, DONT_SET_ORDER, editor.f7Pos);
editor.currMode = MODE_RECORD;
pointerSetMode(POINTER_MODE_EDIT, DO_CARRY);
@@ -1132,7 +1107,7 @@
else if (keyb.leftAmigaPressed)
{
editor.playMode = PLAY_MODE_NORMAL;
- modPlay(DONT_SET_PATTERN, modEntry->currOrder, editor.f7Pos);
+ modPlay(DONT_SET_PATTERN, song->currOrder, editor.f7Pos);
editor.currMode = MODE_PLAY;
pointerSetMode(POINTER_MODE_PLAY, DO_CARRY);
@@ -1150,7 +1125,7 @@
{
if (keyb.shiftPressed)
{
- editor.f8Pos = modEntry->currRow;
+ editor.f8Pos = song->currRow;
displayMsg("POSITION SET");
}
else
@@ -1158,7 +1133,7 @@
if (keyb.leftAltPressed)
{
editor.playMode = PLAY_MODE_PATTERN;
- modPlay(modEntry->currPattern, DONT_SET_ORDER, editor.f8Pos);
+ modPlay(song->currPattern, DONT_SET_ORDER, editor.f8Pos);
editor.currMode = MODE_PLAY;
pointerSetMode(POINTER_MODE_PLAY, DO_CARRY);
@@ -1169,7 +1144,7 @@
if (!ui.samplerScreenShown)
{
editor.playMode = PLAY_MODE_PATTERN;
- modPlay(modEntry->currPattern, DONT_SET_ORDER, editor.f8Pos);
+ modPlay(song->currPattern, DONT_SET_ORDER, editor.f8Pos);
editor.currMode = MODE_RECORD;
pointerSetMode(POINTER_MODE_EDIT, DO_CARRY);
@@ -1179,7 +1154,7 @@
else if (keyb.leftAmigaPressed)
{
editor.playMode = PLAY_MODE_NORMAL;
- modPlay(DONT_SET_PATTERN, modEntry->currOrder, editor.f8Pos);
+ modPlay(DONT_SET_PATTERN, song->currOrder, editor.f8Pos);
editor.currMode = MODE_PLAY;
pointerSetMode(POINTER_MODE_PLAY, DO_CARRY);
@@ -1197,7 +1172,7 @@
{
if (keyb.shiftPressed)
{
- editor.f9Pos = modEntry->currRow;
+ editor.f9Pos = song->currRow;
displayMsg("POSITION SET");
}
else
@@ -1205,7 +1180,7 @@
if (keyb.leftAltPressed)
{
editor.playMode = PLAY_MODE_PATTERN;
- modPlay(modEntry->currPattern, DONT_SET_ORDER, editor.f9Pos);
+ modPlay(song->currPattern, DONT_SET_ORDER, editor.f9Pos);
editor.currMode = MODE_PLAY;
pointerSetMode(POINTER_MODE_PLAY, DO_CARRY);
@@ -1216,7 +1191,7 @@
if (!ui.samplerScreenShown)
{
editor.playMode = PLAY_MODE_PATTERN;
- modPlay(modEntry->currPattern, DONT_SET_ORDER, editor.f9Pos);
+ modPlay(song->currPattern, DONT_SET_ORDER, editor.f9Pos);
editor.currMode = MODE_RECORD;
pointerSetMode(POINTER_MODE_EDIT, DO_CARRY);
@@ -1226,7 +1201,7 @@
else if (keyb.leftAmigaPressed)
{
editor.playMode = PLAY_MODE_NORMAL;
- modPlay(DONT_SET_PATTERN, modEntry->currOrder, editor.f9Pos);
+ modPlay(DONT_SET_PATTERN, song->currOrder, editor.f9Pos);
editor.currMode = MODE_PLAY;
pointerSetMode(POINTER_MODE_PLAY, DO_CARRY);
@@ -1244,7 +1219,7 @@
{
if (keyb.shiftPressed)
{
- editor.f10Pos = modEntry->currRow;
+ editor.f10Pos = song->currRow;
displayMsg("POSITION SET");
}
else
@@ -1252,7 +1227,7 @@
if (keyb.leftAltPressed)
{
editor.playMode = PLAY_MODE_PATTERN;
- modPlay(modEntry->currPattern, DONT_SET_ORDER, editor.f10Pos);
+ modPlay(song->currPattern, DONT_SET_ORDER, editor.f10Pos);
editor.currMode = MODE_PLAY;
pointerSetMode(POINTER_MODE_PLAY, DO_CARRY);
@@ -1263,7 +1238,7 @@
if (!ui.samplerScreenShown)
{
editor.playMode = PLAY_MODE_PATTERN;
- modPlay(modEntry->currPattern, DONT_SET_ORDER, editor.f10Pos);
+ modPlay(song->currPattern, DONT_SET_ORDER, editor.f10Pos);
editor.currMode = MODE_RECORD;
pointerSetMode(POINTER_MODE_EDIT, DO_CARRY);
@@ -1273,7 +1248,7 @@
else if (keyb.leftAmigaPressed)
{
editor.playMode = PLAY_MODE_NORMAL;
- modPlay(DONT_SET_PATTERN, modEntry->currOrder, editor.f10Pos);
+ modPlay(DONT_SET_PATTERN, song->currOrder, editor.f10Pos);
editor.currMode = MODE_PLAY;
pointerSetMode(POINTER_MODE_PLAY, DO_CARRY);
@@ -1316,7 +1291,7 @@
}
else if (keyb.shiftPressed)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(modEntry->currRow * AMIGA_VOICES) + cursor.channel];
+ noteSrc = &song->patterns[song->currPattern][(song->currRow * AMIGA_VOICES) + cursor.channel];
editor.effectMacros[9] = (noteSrc->command << 8) | noteSrc->param;
displayMsg("COMMAND STORED!");
}
@@ -1341,7 +1316,7 @@
}
else if (keyb.shiftPressed)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(modEntry->currRow * AMIGA_VOICES) + cursor.channel];
+ noteSrc = &song->patterns[song->currPattern][(song->currRow * AMIGA_VOICES) + cursor.channel];
editor.effectMacros[0] = (noteSrc->command << 8) | noteSrc->param;
displayMsg("COMMAND STORED!");
}
@@ -1370,7 +1345,7 @@
}
else if (keyb.shiftPressed)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(modEntry->currRow * AMIGA_VOICES) + cursor.channel];
+ noteSrc = &song->patterns[song->currPattern][(song->currRow * AMIGA_VOICES) + cursor.channel];
editor.effectMacros[1] = (noteSrc->command << 8) | noteSrc->param;
displayMsg("COMMAND STORED!");
}
@@ -1399,7 +1374,7 @@
}
else if (keyb.shiftPressed)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(modEntry->currRow * AMIGA_VOICES) + cursor.channel];
+ noteSrc = &song->patterns[song->currPattern][(song->currRow * AMIGA_VOICES) + cursor.channel];
editor.effectMacros[2] = (noteSrc->command << 8) | noteSrc->param;
displayMsg("COMMAND STORED!");
}
@@ -1428,7 +1403,7 @@
}
else if (keyb.shiftPressed)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(modEntry->currRow * AMIGA_VOICES) + cursor.channel];
+ noteSrc = &song->patterns[song->currPattern][(song->currRow * AMIGA_VOICES) + cursor.channel];
editor.effectMacros[3] = (noteSrc->command << 8) | noteSrc->param;
displayMsg("COMMAND STORED!");
}
@@ -1453,7 +1428,7 @@
}
else if (keyb.shiftPressed)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(modEntry->currRow * AMIGA_VOICES) + cursor.channel];
+ noteSrc = &song->patterns[song->currPattern][(song->currRow * AMIGA_VOICES) + cursor.channel];
editor.effectMacros[4] = (noteSrc->command << 8) | noteSrc->param;
displayMsg("COMMAND STORED!");
}
@@ -1474,7 +1449,7 @@
}
else if (keyb.shiftPressed)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(modEntry->currRow * AMIGA_VOICES) + cursor.channel];
+ noteSrc = &song->patterns[song->currPattern][(song->currRow * AMIGA_VOICES) + cursor.channel];
editor.effectMacros[5] = (noteSrc->command << 8) | noteSrc->param;
displayMsg("COMMAND STORED!");
}
@@ -1495,7 +1470,7 @@
}
else if (keyb.shiftPressed)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(modEntry->currRow * AMIGA_VOICES) + cursor.channel];
+ noteSrc = &song->patterns[song->currPattern][(song->currRow * AMIGA_VOICES) + cursor.channel];
editor.effectMacros[6] = (noteSrc->command << 8) | noteSrc->param;
displayMsg("COMMAND STORED!");
}
@@ -1516,7 +1491,7 @@
}
else if (keyb.shiftPressed)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(modEntry->currRow * AMIGA_VOICES) + cursor.channel];
+ noteSrc = &song->patterns[song->currPattern][(song->currRow * AMIGA_VOICES) + cursor.channel];
editor.effectMacros[7] = (noteSrc->command << 8) | noteSrc->param;
displayMsg("COMMAND STORED!");
}
@@ -1537,7 +1512,7 @@
}
else if (keyb.shiftPressed)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(modEntry->currRow * AMIGA_VOICES) + cursor.channel];
+ noteSrc = &song->patterns[song->currPattern][(song->currRow * AMIGA_VOICES) + cursor.channel];
editor.effectMacros[8] = (noteSrc->command << 8) | noteSrc->param;
displayMsg("COMMAND STORED!");
}
@@ -1550,7 +1525,17 @@
case SDL_SCANCODE_KP_0:
{
- editor.sampleZero = true;
+ if (editor.hiLowInstr >= 0x10)
+ {
+ editor.sampleZero = false;
+ editor.currSample = 0x10-1;
+ }
+ else
+ {
+ editor.sampleZero = true;
+ editor.currSample = 0x00;
+ }
+
updateCurrSample();
}
break;
@@ -1558,7 +1543,7 @@
case SDL_SCANCODE_KP_1:
{
editor.sampleZero = false;
- editor.currSample = editor.keypadSampleOffset + 12;
+ editor.currSample = editor.hiLowInstr + 12;
updateCurrSample();
if (keyb.leftAltPressed && editor.pNoteFlag > 0)
@@ -1577,7 +1562,7 @@
case SDL_SCANCODE_KP_2:
{
editor.sampleZero = false;
- editor.currSample = editor.keypadSampleOffset + 13;
+ editor.currSample = editor.hiLowInstr + 13;
updateCurrSample();
if (keyb.leftAltPressed && editor.pNoteFlag > 0)
@@ -1596,7 +1581,7 @@
case SDL_SCANCODE_KP_3:
{
editor.sampleZero = false;
- editor.currSample = editor.keypadSampleOffset + 14;
+ editor.currSample = editor.hiLowInstr + 14;
updateCurrSample();
if (keyb.leftAltPressed && editor.pNoteFlag > 0)
@@ -1615,7 +1600,7 @@
case SDL_SCANCODE_KP_4:
{
editor.sampleZero = false;
- editor.currSample = editor.keypadSampleOffset + 8;
+ editor.currSample = editor.hiLowInstr + 8;
updateCurrSample();
if (keyb.leftAltPressed && editor.pNoteFlag > 0)
@@ -1634,7 +1619,7 @@
case SDL_SCANCODE_KP_5:
{
editor.sampleZero = false;
- editor.currSample = editor.keypadSampleOffset + 9;
+ editor.currSample = editor.hiLowInstr + 9;
updateCurrSample();
if (keyb.leftAltPressed && editor.pNoteFlag > 0)
@@ -1653,7 +1638,7 @@
case SDL_SCANCODE_KP_6:
{
editor.sampleZero = false;
- editor.currSample = editor.keypadSampleOffset + 10;
+ editor.currSample = editor.hiLowInstr + 10;
updateCurrSample();
if (keyb.leftAltPressed && editor.pNoteFlag > 0)
@@ -1672,7 +1657,7 @@
case SDL_SCANCODE_KP_7:
{
editor.sampleZero = false;
- editor.currSample = editor.keypadSampleOffset + 4;
+ editor.currSample = editor.hiLowInstr + 4;
updateCurrSample();
if (keyb.leftAltPressed && editor.pNoteFlag > 0)
@@ -1691,7 +1676,7 @@
case SDL_SCANCODE_KP_8:
{
editor.sampleZero = false;
- editor.currSample = editor.keypadSampleOffset + 5;
+ editor.currSample = editor.hiLowInstr + 5;
updateCurrSample();
if (keyb.leftAltPressed && editor.pNoteFlag > 0)
@@ -1710,7 +1695,7 @@
case SDL_SCANCODE_KP_9:
{
editor.sampleZero = false;
- editor.currSample = editor.keypadSampleOffset + 6;
+ editor.currSample = editor.hiLowInstr + 6;
updateCurrSample();
if (keyb.leftAltPressed && editor.pNoteFlag > 0)
@@ -1737,24 +1722,24 @@
}
else
{
- editor.sampleZero = false;
+ editor.hiLowInstr ^= 0x10;
- editor.currSample++;
- if (editor.currSample >= 0x10)
+ if (editor.sampleZero)
{
- editor.keypadSampleOffset = 0x00;
-
- editor.currSample -= 0x10;
- if (editor.currSample < 0x01)
- editor.currSample = 0x01;
+ editor.currSample = 15;
+ editor.sampleZero = false;
}
else
{
- editor.currSample += 0x10;
- editor.keypadSampleOffset = 0x10;
+ editor.currSample ^= 0x10;
}
- editor.currSample--;
+ if (editor.currSample == 31) // kludge if sample was 15 (0010 in UI) before key press
+ {
+ editor.currSample = 15;
+ editor.sampleZero ^= 1;
+ }
+
updateCurrSample();
if (keyb.leftAltPressed && editor.pNoteFlag > 0)
{
@@ -1776,9 +1761,9 @@
// the Amiga numpad has one more key, so we need to use this key for two sample numbers...
if (editor.keypadToggle8CFlag)
- editor.currSample = editor.keypadSampleOffset + (0x0C - 1);
+ editor.currSample = editor.hiLowInstr + (0x0C - 1);
else
- editor.currSample = editor.keypadSampleOffset + (0x08 - 1);
+ editor.currSample = editor.hiLowInstr + (0x08 - 1);
updateCurrSample();
if (keyb.leftAltPressed && editor.pNoteFlag > 0)
@@ -1791,7 +1776,7 @@
case SDL_SCANCODE_KP_MINUS:
{
editor.sampleZero = false;
- editor.currSample = editor.keypadSampleOffset + 3;
+ editor.currSample = editor.hiLowInstr + 3;
updateCurrSample();
if (keyb.leftAltPressed && editor.pNoteFlag > 0)
@@ -1810,7 +1795,7 @@
case SDL_SCANCODE_KP_MULTIPLY:
{
editor.sampleZero = false;
- editor.currSample = editor.keypadSampleOffset + 2;
+ editor.currSample = editor.hiLowInstr + 2;
updateCurrSample();
if (keyb.leftAltPressed && editor.pNoteFlag > 0)
@@ -1829,7 +1814,7 @@
case SDL_SCANCODE_KP_DIVIDE:
{
editor.sampleZero = false;
- editor.currSample = editor.keypadSampleOffset + 1;
+ editor.currSample = editor.hiLowInstr + 1;
updateCurrSample();
if (keyb.leftAltPressed && editor.pNoteFlag > 0)
@@ -1848,7 +1833,7 @@
case SDL_SCANCODE_NUMLOCKCLEAR:
{
editor.sampleZero = false;
- editor.currSample = editor.keypadSampleOffset + 0;
+ editor.currSample = editor.hiLowInstr + 0;
updateCurrSample();
if (keyb.leftAltPressed && editor.pNoteFlag > 0)
@@ -1903,12 +1888,12 @@
}
else if (ui.posEdScreenShown)
{
- if (modEntry->currOrder != modEntry->head.orderCount-1)
+ if (song->currOrder != song->header.numOrders-1)
{
- if (++modEntry->currOrder > modEntry->head.orderCount-1)
- modEntry->currOrder = modEntry->head.orderCount-1;
+ if (++song->currOrder > song->header.numOrders-1)
+ song->currOrder = song->header.numOrders-1;
- modSetPos(modEntry->currOrder, DONT_SET_ROW);
+ modSetPos(song->currOrder, DONT_SET_ROW);
ui.updatePosEd = true;
}
@@ -1921,7 +1906,7 @@
else if (!ui.samplerScreenShown)
{
if (editor.currMode != MODE_PLAY && editor.currMode != MODE_RECORD)
- modSetPos(DONT_SET_ORDER, (modEntry->currRow + 1) & 0x3F);
+ modSetPos(DONT_SET_ORDER, (song->currRow + 1) & 0x3F);
keyb.repeatKey = true;
}
@@ -1952,9 +1937,9 @@
}
else if (ui.posEdScreenShown)
{
- if (modEntry->currOrder > 0)
+ if (song->currOrder > 0)
{
- modSetPos(modEntry->currOrder - 1, DONT_SET_ROW);
+ modSetPos(song->currOrder - 1, DONT_SET_ROW);
ui.updatePosEd = true;
}
@@ -1967,7 +1952,7 @@
else if (!ui.samplerScreenShown)
{
if ((editor.currMode != MODE_PLAY) && (editor.currMode != MODE_RECORD))
- modSetPos(DONT_SET_ORDER, (modEntry->currRow - 1) & 0x3F);
+ modSetPos(DONT_SET_ORDER, (song->currRow - 1) & 0x3F);
keyb.repeatKey = true;
}
@@ -1990,9 +1975,9 @@
}
else if (keyb.shiftPressed)
{
- if (modEntry->currOrder > 0)
+ if (song->currOrder > 0)
{
- modSetPos(modEntry->currOrder - 1, DONT_SET_ROW);
+ modSetPos(song->currOrder - 1, DONT_SET_ROW);
if (editor.repeatKeyFlag)
{
keyb.delayKey = true;
@@ -2033,9 +2018,9 @@
}
else if (keyb.shiftPressed)
{
- if (modEntry->currOrder < 126)
+ if (song->currOrder < 126)
{
- modSetPos(modEntry->currOrder + 1, DONT_SET_ROW);
+ modSetPos(song->currOrder + 1, DONT_SET_ROW);
if (editor.repeatKeyFlag)
{
keyb.delayKey = true;
@@ -2115,8 +2100,8 @@
else
{
editor.blockMarkFlag = true;
- editor.blockFromPos = modEntry->currRow;
- editor.blockToPos = modEntry->currRow;
+ editor.blockFromPos = song->currRow;
+ editor.blockToPos = song->currRow;
}
ui.updateStatusText = true;
@@ -2123,10 +2108,10 @@
}
else if (keyb.leftAltPressed)
{
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
if (s->length == 0)
{
- displayErrorMsg("SAMPLE IS EMPTY");
+ statusSampleIsEmpty();
break;
}
@@ -2166,7 +2151,7 @@
editor.blockBufferFlag = true;
for (i = 0; i < MOD_ROWS; i++)
- editor.blockBuffer[i] = modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + cursor.channel];
+ editor.blockBuffer[i] = song->patterns[song->currPattern][(i * AMIGA_VOICES) + cursor.channel];
if (editor.blockFromPos > editor.blockToPos)
{
@@ -2258,16 +2243,16 @@
{
saveUndo();
- j = modEntry->currRow + 1;
+ j = song->currRow + 1;
while (j < MOD_ROWS)
{
for (i = 62; i >= j; i--)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + cursor.channel];
- modEntry->patterns[modEntry->currPattern][((i + 1) * AMIGA_VOICES) + cursor.channel] = *noteSrc;
+ noteSrc = &song->patterns[song->currPattern][(i * AMIGA_VOICES) + cursor.channel];
+ song->patterns[song->currPattern][((i + 1) * AMIGA_VOICES) + cursor.channel] = *noteSrc;
}
- noteDst = &modEntry->patterns[modEntry->currPattern][((i + 1) * AMIGA_VOICES) + cursor.channel];
+ noteDst = &song->patterns[song->currPattern][((i + 1) * AMIGA_VOICES) + cursor.channel];
noteDst->period = 0;
noteDst->sample = 0;
noteDst->command = 0;
@@ -2310,10 +2295,10 @@
}
else if (keyb.leftAltPressed)
{
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
if (s->length == 0)
{
- displayErrorMsg("SAMPLE IS EMPTY");
+ statusSampleIsEmpty();
break;
}
@@ -2385,14 +2370,14 @@
return;
}
- if (modEntry->currRow < 63)
+ if (song->currRow < 63)
{
for (i = 0; i <= editor.buffToPos-editor.buffFromPos; i++)
{
- for (j = 62; j >= modEntry->currRow; j--)
+ for (j = 62; j >= song->currRow; j--)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(j * AMIGA_VOICES) + cursor.channel];
- modEntry->patterns[modEntry->currPattern][((j + 1) * AMIGA_VOICES) + cursor.channel] = *noteSrc;
+ noteSrc = &song->patterns[song->currPattern][(j * AMIGA_VOICES) + cursor.channel];
+ song->patterns[song->currPattern][((j + 1) * AMIGA_VOICES) + cursor.channel] = *noteSrc;
}
}
}
@@ -2400,18 +2385,18 @@
saveUndo();
for (i = 0; i <= editor.buffToPos-editor.buffFromPos; i++)
{
- if (modEntry->currRow+i > 63)
+ if (song->currRow+i > 63)
break;
- modEntry->patterns[modEntry->currPattern][((modEntry->currRow + i) * AMIGA_VOICES) + cursor.channel]
+ song->patterns[song->currPattern][((song->currRow + i) * AMIGA_VOICES) + cursor.channel]
= editor.blockBuffer[editor.buffFromPos + i];
}
if (!keyb.shiftPressed)
{
- modEntry->currRow += i & 0xFF;
- if (modEntry->currRow > 63)
- modEntry->currRow = 0;
+ song->currRow += i & 0xFF;
+ if (song->currRow > 63)
+ song->currRow = 0;
}
updateWindowTitle(MOD_IS_MODIFIED);
@@ -2442,8 +2427,8 @@
saveUndo();
i = editor.buffFromPos;
- j = modEntry->currRow;
- patt = modEntry->patterns[modEntry->currPattern];
+ j = song->currRow;
+ patt = song->patterns[song->currPattern];
while (true)
{
noteDst = &patt[(j * AMIGA_VOICES) + cursor.channel];
@@ -2467,9 +2452,9 @@
if (!keyb.shiftPressed)
{
- modEntry->currRow += (editor.buffToPos-editor.buffFromPos) + 1;
- if (modEntry->currRow > 63)
- modEntry->currRow = 0;
+ song->currRow += (editor.buffToPos-editor.buffFromPos) + 1;
+ if (song->currRow > 63)
+ song->currRow = 0;
}
updateWindowTitle(MOD_IS_MODIFIED);
@@ -2488,7 +2473,7 @@
{
for (i = 0; i < MOD_ROWS; i++)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + cursor.channel];
+ noteSrc = &song->patterns[song->currPattern][(i * AMIGA_VOICES) + cursor.channel];
if (noteSrc->sample == editor.currSample+1)
{
noteSrc->period = 0;
@@ -2505,13 +2490,13 @@
{
saveUndo();
- i = modEntry->currRow;
+ i = song->currRow;
if (keyb.shiftPressed)
{
// kill to start
while (i >= 0)
{
- noteDst = &modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + cursor.channel];
+ noteDst = &song->patterns[song->currPattern][(i * AMIGA_VOICES) + cursor.channel];
noteDst->period = 0;
noteDst->sample = 0;
noteDst->command = 0;
@@ -2525,7 +2510,7 @@
// kill to end
while (i < MOD_ROWS)
{
- noteDst = &modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + cursor.channel];
+ noteDst = &song->patterns[song->currPattern][(i * AMIGA_VOICES) + cursor.channel];
noteDst->period = 0;
noteDst->sample = 0;
noteDst->command = 0;
@@ -2593,7 +2578,7 @@
if (keyb.leftCtrlPressed)
{
editor.blockMarkFlag = true;
- modEntry->currRow = editor.blockToPos;
+ song->currRow = editor.blockToPos;
}
else
{
@@ -2610,17 +2595,17 @@
saveUndo();
- j = modEntry->currRow + 1;
+ j = song->currRow + 1;
while (j < MOD_ROWS)
{
for (i = j; i < MOD_ROWS-1; i++)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][((i + 1) * AMIGA_VOICES) + cursor.channel];
- modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + cursor.channel] = *noteSrc;
+ noteSrc = &song->patterns[song->currPattern][((i + 1) * AMIGA_VOICES) + cursor.channel];
+ song->patterns[song->currPattern][(i * AMIGA_VOICES) + cursor.channel] = *noteSrc;
}
// clear newly made row on very bottom
- noteDst = &modEntry->patterns[modEntry->currPattern][(63 * AMIGA_VOICES) + cursor.channel];
+ noteDst = &song->patterns[song->currPattern][(63 * AMIGA_VOICES) + cursor.channel];
noteDst->period = 0;
noteDst->sample = 0;
noteDst->command = 0;
@@ -2652,8 +2637,8 @@
saveUndo();
i = editor.buffFromPos;
- j = modEntry->currRow;
- patt = modEntry->patterns[modEntry->currPattern];
+ j = song->currRow;
+ patt = song->patterns[song->currPattern];
while (true)
{
noteDst = &patt[(j * AMIGA_VOICES) + cursor.channel];
@@ -2668,9 +2653,9 @@
if (!keyb.shiftPressed)
{
- modEntry->currRow += (editor.buffToPos-editor.buffFromPos) + 1;
- if (modEntry->currRow > 63)
- modEntry->currRow = 0;
+ song->currRow += (editor.buffToPos-editor.buffFromPos) + 1;
+ if (song->currRow > 63)
+ song->currRow = 0;
}
updateWindowTitle(MOD_IS_MODIFIED);
@@ -2869,8 +2854,8 @@
saveUndo();
i = editor.buffFromPos;
- j = modEntry->currRow;
- patt = modEntry->patterns[modEntry->currPattern];
+ j = song->currRow;
+ patt = song->patterns[song->currPattern];
while (true)
{
noteDst = &patt[(j * AMIGA_VOICES) + cursor.channel];
@@ -2894,9 +2879,9 @@
if (!keyb.shiftPressed)
{
- modEntry->currRow += (editor.buffToPos-editor.buffFromPos) + 1;
- if (modEntry->currRow > 63)
- modEntry->currRow = 0;
+ song->currRow += (editor.buffToPos-editor.buffFromPos) + 1;
+ if (song->currRow > 63)
+ song->currRow = 0;
}
updateWindowTitle(MOD_IS_MODIFIED);
@@ -2934,7 +2919,7 @@
editor.blockBufferFlag = true;
for (i = 0; i < MOD_ROWS; i++)
- editor.blockBuffer[i] = modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + cursor.channel];
+ editor.blockBuffer[i] = song->patterns[song->currPattern][(i * AMIGA_VOICES) + cursor.channel];
if (editor.blockFromPos > editor.blockToPos)
{
@@ -2949,7 +2934,7 @@
for (i = editor.buffFromPos; i <= editor.buffToPos; i++)
{
- noteDst = &modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + cursor.channel];
+ noteDst = &song->patterns[song->currPattern][(i * AMIGA_VOICES) + cursor.channel];
noteDst->period = 0;
noteDst->sample = 0;
noteDst->command = 0;
@@ -3002,8 +2987,8 @@
while (blockFrom < blockTo)
{
- noteDst = &modEntry->patterns[modEntry->currPattern][(blockFrom * AMIGA_VOICES) + cursor.channel];
- noteSrc = &modEntry->patterns[modEntry->currPattern][(blockTo * AMIGA_VOICES) + cursor.channel];
+ noteDst = &song->patterns[song->currPattern][(blockFrom * AMIGA_VOICES) + cursor.channel];
+ noteSrc = &song->patterns[song->currPattern][(blockTo * AMIGA_VOICES) + cursor.channel];
noteTmp = *noteDst;
*noteDst = *noteSrc;
@@ -3056,7 +3041,7 @@
for (i = 0; i < AMIGA_VOICES; i++)
{
- ch = &modEntry->channels[i];
+ ch = &song->channels[i];
ch->n_wavecontrol = 0;
ch->n_glissfunk = 0;
ch->n_finetune = 0;
@@ -3167,8 +3152,8 @@
if (ui.posEdScreenShown)
{
- if (modEntry->currOrder-(POSED_LIST_SIZE-1) > 0)
- modSetPos(modEntry->currOrder-(POSED_LIST_SIZE-1), DONT_SET_ROW);
+ if (song->currOrder-(POSED_LIST_SIZE-1) > 0)
+ modSetPos(song->currOrder-(POSED_LIST_SIZE-1), DONT_SET_ROW);
else
modSetPos(0, DONT_SET_ROW);
}
@@ -3185,12 +3170,12 @@
}
else if (editor.currMode == MODE_IDLE || editor.currMode == MODE_EDIT)
{
- if (modEntry->currRow == 63)
- modSetPos(DONT_SET_ORDER, modEntry->currRow - 15);
- else if (modEntry->currRow == 15)
+ if (song->currRow == 63)
+ modSetPos(DONT_SET_ORDER, song->currRow - 15);
+ else if (song->currRow == 15)
modSetPos(DONT_SET_ORDER, 0); // 15-16 would turn into -1, which is "DON'T SET ROW" flag
else
- modSetPos(DONT_SET_ORDER, modEntry->currRow - 16);
+ modSetPos(DONT_SET_ORDER, song->currRow - 16);
}
}
}
@@ -3204,10 +3189,10 @@
if (ui.posEdScreenShown)
{
- if (modEntry->currOrder+(POSED_LIST_SIZE-1) <= modEntry->head.orderCount-1)
- modSetPos(modEntry->currOrder+(POSED_LIST_SIZE-1), DONT_SET_ROW);
+ if (song->currOrder+(POSED_LIST_SIZE-1) <= song->header.numOrders-1)
+ modSetPos(song->currOrder+(POSED_LIST_SIZE-1), DONT_SET_ROW);
else
- modSetPos(modEntry->head.orderCount - 1, DONT_SET_ROW);
+ modSetPos(song->header.numOrders - 1, DONT_SET_ROW);
}
else if (ui.diskOpScreenShown)
{
@@ -3222,7 +3207,7 @@
}
else if (editor.currMode == MODE_IDLE || editor.currMode == MODE_EDIT)
{
- modSetPos(DONT_SET_ORDER, modEntry->currRow + 16);
+ modSetPos(DONT_SET_ORDER, song->currRow + 16);
}
}
}
@@ -3253,8 +3238,8 @@
if (keyb.repeatCounter >= 6)
{
keyb.repeatCounter = 0;
- if (modEntry->currOrder > 0)
- modSetPos(modEntry->currOrder - 1, DONT_SET_ROW);
+ if (song->currOrder > 0)
+ modSetPos(song->currOrder - 1, DONT_SET_ROW);
}
}
else if (keyb.leftAltPressed)
@@ -3303,8 +3288,8 @@
if (keyb.repeatCounter >= 6)
{
keyb.repeatCounter = 0;
- if (modEntry->currOrder < 126)
- modSetPos(modEntry->currOrder + 1, DONT_SET_ROW);
+ if (song->currOrder < 126)
+ modSetPos(song->currOrder + 1, DONT_SET_ROW);
}
}
else if (keyb.leftAltPressed)
@@ -3351,9 +3336,9 @@
if (keyb.repeatCounter >= 3)
{
keyb.repeatCounter = 0;
- if (modEntry->currOrder > 0)
+ if (song->currOrder > 0)
{
- modSetPos(modEntry->currOrder - 1, DONT_SET_ROW);
+ modSetPos(song->currOrder - 1, DONT_SET_ROW);
ui.updatePosEd = true;
}
}
@@ -3371,7 +3356,7 @@
if (keyb.repeatCounter >= repeatNum)
{
keyb.repeatCounter = 0;
- modSetPos(DONT_SET_ORDER, (modEntry->currRow - 1) & 0x3F);
+ modSetPos(DONT_SET_ORDER, (song->currRow - 1) & 0x3F);
}
}
}
@@ -3405,12 +3390,12 @@
{
keyb.repeatCounter = 0;
- if (modEntry->currOrder != modEntry->head.orderCount-1)
+ if (song->currOrder != song->header.numOrders-1)
{
- if (++modEntry->currOrder > modEntry->head.orderCount-1)
- modEntry->currOrder = modEntry->head.orderCount-1;
+ if (++song->currOrder > song->header.numOrders-1)
+ song->currOrder = song->header.numOrders-1;
- modSetPos(modEntry->currOrder, DONT_SET_ROW);
+ modSetPos(song->currOrder, DONT_SET_ROW);
ui.updatePosEd = true;
}
}
@@ -3428,7 +3413,7 @@
if (keyb.repeatCounter >= repeatNum)
{
keyb.repeatCounter = 0;
- modSetPos(DONT_SET_ORDER, (modEntry->currRow + 1) & 0x3F);
+ modSetPos(DONT_SET_ORDER, (song->currRow + 1) & 0x3F);
}
}
}
@@ -3692,10 +3677,10 @@
{
for (i = 0; i < MOD_ROWS; i++)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + cursor.channel];
- noteTmp = modEntry->patterns[modEntry->currPattern][i * AMIGA_VOICES];
+ noteSrc = &song->patterns[song->currPattern][(i * AMIGA_VOICES) + cursor.channel];
+ noteTmp = song->patterns[song->currPattern][i * AMIGA_VOICES];
- modEntry->patterns[modEntry->currPattern][i * AMIGA_VOICES] = *noteSrc;
+ song->patterns[song->currPattern][i * AMIGA_VOICES] = *noteSrc;
*noteSrc = noteTmp;
}
@@ -3710,10 +3695,10 @@
{
for (i = 0; i < MOD_ROWS; i++)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + cursor.channel];
- noteTmp = modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + 1];
+ noteSrc = &song->patterns[song->currPattern][(i * AMIGA_VOICES) + cursor.channel];
+ noteTmp = song->patterns[song->currPattern][(i * AMIGA_VOICES) + 1];
- modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + 1] = *noteSrc;
+ song->patterns[song->currPattern][(i * AMIGA_VOICES) + 1] = *noteSrc;
*noteSrc = noteTmp;
}
@@ -3728,10 +3713,10 @@
{
for (i = 0; i < MOD_ROWS; i++)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + cursor.channel];
- noteTmp = modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + 2];
+ noteSrc = &song->patterns[song->currPattern][(i * AMIGA_VOICES) + cursor.channel];
+ noteTmp = song->patterns[song->currPattern][(i * AMIGA_VOICES) + 2];
- modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + 2] = *noteSrc;
+ song->patterns[song->currPattern][(i * AMIGA_VOICES) + 2] = *noteSrc;
*noteSrc = noteTmp;
}
@@ -3746,10 +3731,10 @@
{
for (i = 0; i < MOD_ROWS; i++)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + cursor.channel];
- noteTmp = modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + 3];
+ noteSrc = &song->patterns[song->currPattern][(i * AMIGA_VOICES) + cursor.channel];
+ noteTmp = song->patterns[song->currPattern][(i * AMIGA_VOICES) + 3];
- modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + 3] = *noteSrc;
+ song->patterns[song->currPattern][(i * AMIGA_VOICES) + 3] = *noteSrc;
*noteSrc = noteTmp;
}
@@ -4227,18 +4212,18 @@
saveUndo();
if (keyb.leftAltPressed && !keyb.leftCtrlPressed)
{
- if (modEntry->currRow > 0)
+ if (song->currRow > 0)
{
for (i = 0; i < AMIGA_VOICES; i++)
{
- for (j = (modEntry->currRow - 1); j < MOD_ROWS; j++)
+ for (j = (song->currRow - 1); j < MOD_ROWS; j++)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][((j + 1) * AMIGA_VOICES) + i];
- modEntry->patterns[modEntry->currPattern][(j * AMIGA_VOICES) + i] = *noteSrc;
+ noteSrc = &song->patterns[song->currPattern][((j + 1) * AMIGA_VOICES) + i];
+ song->patterns[song->currPattern][(j * AMIGA_VOICES) + i] = *noteSrc;
}
// clear newly made row on very bottom
- noteDst = &modEntry->patterns[modEntry->currPattern][(63 * AMIGA_VOICES) + i];
+ noteDst = &song->patterns[song->currPattern][(63 * AMIGA_VOICES) + i];
noteDst->period = 0;
noteDst->sample = 0;
noteDst->command = 0;
@@ -4245,18 +4230,18 @@
noteDst->param = 0;
}
- modEntry->currRow--;
+ song->currRow--;
ui.updatePatternData = true;
}
}
else
{
- if (modEntry->currRow > 0)
+ if (song->currRow > 0)
{
- for (i = modEntry->currRow-1; i < MOD_ROWS-1; i++)
+ for (i = song->currRow-1; i < MOD_ROWS-1; i++)
{
- noteSrc = &modEntry->patterns[modEntry->currPattern][((i + 1) * AMIGA_VOICES) + cursor.channel];
- noteDst = &modEntry->patterns[modEntry->currPattern][(i * AMIGA_VOICES) + cursor.channel];
+ noteSrc = &song->patterns[song->currPattern][((i + 1) * AMIGA_VOICES) + cursor.channel];
+ noteDst = &song->patterns[song->currPattern][(i * AMIGA_VOICES) + cursor.channel];
if (keyb.leftCtrlPressed)
{
@@ -4270,13 +4255,13 @@
}
// clear newly made row on very bottom
- noteDst = &modEntry->patterns[modEntry->currPattern][(63 * AMIGA_VOICES) + cursor.channel];
+ noteDst = &song->patterns[song->currPattern][(63 * AMIGA_VOICES) + cursor.channel];
noteDst->period = 0;
noteDst->sample = 0;
noteDst->command = 0;
noteDst->param = 0;
- modEntry->currRow--;
+ song->currRow--;
ui.updatePatternData = true;
}
}
@@ -4287,7 +4272,7 @@
editor.stepPlayBackwards = true;
doStopIt(true);
- playPattern((modEntry->currRow - 1) & 0x3F);
+ playPattern((song->currRow - 1) & 0x3F);
}
}
}
--- a/src/pt2_main.c
+++ b/src/pt2_main.c
@@ -28,17 +28,19 @@
#include "pt2_config.h"
#include "pt2_visuals.h"
#include "pt2_edit.h"
-#include "pt2_modloader.h"
-#include "pt2_sampleloader.h"
+#include "pt2_module_loader.h"
+#include "pt2_module_saver.h"
+#include "pt2_sample_loader.h"
#include "pt2_unicode.h"
#include "pt2_scopes.h"
#include "pt2_audio.h"
+#include "pt2_bmp.h"
#define CRASH_TEXT "Oh no!\nThe ProTracker 2 clone has crashed...\n\nA backup .mod was hopefully " \
"saved to the current module directory.\n\nPlease report this bug if you can.\n" \
"Try to mention what you did before the crash happened."
-module_t *modEntry = NULL; // globalized
+module_t *song = NULL; // globalized
static bool backupMadeAfterCrash;
@@ -198,6 +200,7 @@
#ifdef _WIN32
+ // Windows: the Win key has to be taken over to work like the Amiga key
#ifndef _DEBUG
windowsKeyIsDown = false;
g_hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, lowLevelKeyboardProc, GetModuleHandle(NULL), 0);
@@ -244,8 +247,8 @@
setupSprites();
setupPerfFreq();
- modEntry = createNewMod();
- if (modEntry == NULL)
+ song = createNewMod();
+ if (song == NULL)
{
cleanUp();
SDL_Quit();
@@ -273,7 +276,7 @@
loadModFromArg(argv[1]);
// play song
- if (modEntry->moduleLoaded)
+ if (song->loaded)
{
editor.playMode = PLAY_MODE_NORMAL;
modPlay(DONT_SET_PATTERN, 0, DONT_SET_ROW);
@@ -282,10 +285,9 @@
statusAllRight();
}
}
- else
+ else if (!editor.configFound)
{
- if (!editor.configFound)
- displayErrorMsg("CONFIG NOT FOUND!");
+ displayErrorMsg("CONFIG NOT FOUND!");
}
displayMainScreen();
@@ -448,8 +450,6 @@
editor.repeatKeyFlag = (SDL_GetModState() & KMOD_CAPS) ? true : false;
- modEntry = NULL;
-
strcpy(editor.mixText, "MIX 01+02 TO 03");
// allocate some memory
@@ -538,7 +538,7 @@
static void handleSigTerm(void)
{
- if (modEntry->modified)
+ if (song->modified)
{
resetAllScreens();
@@ -862,8 +862,9 @@
static void cleanUp(void) // never call this inside the main loop!
{
- audioClose();
+ modStop();
modFree();
+ audioClose();
deAllocSamplerVars();
freeDiskOpMem();
freeDiskOpEntryMem();
--- a/src/pt2_mod2wav.c
+++ b/src/pt2_mod2wav.c
@@ -178,7 +178,7 @@
editor.abortMod2Wav = false;
- modSetTempo(modEntry->currBPM); // update BPM with MOD2WAV audio output rate
+ modSetTempo(song->currBPM); // update BPM with MOD2WAV audio output rate
editor.mod2WavThread = SDL_CreateThread(mod2WavThreadFunc, NULL, fOut);
if (editor.mod2WavThread != NULL)
@@ -217,12 +217,12 @@
memset(n_pattpos, 0, sizeof (n_pattpos));
memset(n_loopcount, 0, sizeof (n_loopcount));
- modEntry->rowsCounter = 0;
- modEntry->rowsInTotal = 0;
+ song->rowsCounter = 0;
+ song->rowsInTotal = 0;
modRow = 0;
modOrder = 0;
- modPattern = modEntry->head.order[0];
+ modPattern = song->header.order[0];
pBreakPosition = 0;
posJumpAssert = false;
pBreakFlag = false;
@@ -235,7 +235,7 @@
for (ch = 0; ch < AMIGA_VOICES; ch++)
{
- note = &modEntry->patterns[modPattern][(modRow * AMIGA_VOICES) + ch];
+ note = &song->patterns[modPattern][(modRow * AMIGA_VOICES) + ch];
if (note->command == 0x0B) // Bxx - Position Jump
{
modOrder = note->param - 1;
@@ -284,7 +284,7 @@
}
modRow++;
- modEntry->rowsInTotal++;
+ song->rowsInTotal++;
if (pBreakFlag)
{
@@ -300,7 +300,7 @@
posJumpAssert = false;
modOrder = (modOrder + 1) & 0x7F;
- if (modOrder >= modEntry->head.orderCount)
+ if (modOrder >= song->header.numOrders)
{
modOrder = 0;
calcingRows = false;
@@ -307,7 +307,7 @@
break;
}
- modPattern = modEntry->head.order[modOrder];
+ modPattern = song->header.order[modOrder];
if (modPattern > MAX_PATTERNS-1)
modPattern = MAX_PATTERNS-1;
}
--- a/src/pt2_modloader.c
+++ /dev/null
@@ -1,1459 +1,0 @@
-// for finding memory leaks in debug mode with Visual Studio
-#if defined _DEBUG && defined _MSC_VER
-#include <crtdbg.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <stdbool.h>
-#include <ctype.h> // tolower()
-#ifdef _WIN32
-#include <io.h>
-#else
-#include <unistd.h>
-#endif
-#include <fcntl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include "pt2_mouse.h"
-#include "pt2_header.h"
-#include "pt2_sampler.h"
-#include "pt2_textout.h"
-#include "pt2_audio.h"
-#include "pt2_helpers.h"
-#include "pt2_visuals.h"
-#include "pt2_unicode.h"
-#include "pt2_modloader.h"
-#include "pt2_sampleloader.h"
-#
-typedef struct mem_t
-{
- bool _eof;
- uint8_t *_ptr, *_base;
- uint32_t _cnt, _bufsiz;
-} MEMFILE;
-
-static bool oldAutoPlay;
-static char oldFullPath[(PATH_MAX * 2) + 2];
-static uint32_t oldFullPathLen;
-static module_t *tempMod;
-
-static MEMFILE *mopen(const uint8_t *src, uint32_t length);
-static void mclose(MEMFILE **buf);
-static int32_t mgetc(MEMFILE *buf);
-static size_t mread(void *buffer, size_t size, size_t count, MEMFILE *buf);
-static void mseek(MEMFILE *buf, int32_t offset, int32_t whence);
-static void mrewind(MEMFILE *buf);
-static uint8_t ppdecrunch(uint8_t *src, uint8_t *dst, uint8_t *offsetLens, uint32_t srcLen, uint32_t dstLen, uint8_t skipBits);
-
-void showSongUnsavedAskBox(int8_t askScreenType)
-{
- ui.askScreenShown = true;
- ui.askScreenType = askScreenType;
-
- pointerSetMode(POINTER_MODE_MSG1, NO_CARRY);
- setStatusMessage("SONG IS UNSAVED !", NO_CARRY);
- renderAskDialog();
-}
-
-bool modSave(char *fileName)
-{
- int16_t tempPatternCount;
- int32_t i;
- uint32_t tempLoopLength, tempLoopStart, j, k;
- note_t tmp;
- FILE *fmodule;
-
- tempPatternCount = 0;
-
- fmodule = fopen(fileName, "wb");
- if (fmodule == NULL)
- {
- displayErrorMsg("FILE I/O ERROR !");
- return false;
- }
-
- for (i = 0; i < 20; i++)
- fputc(tolower(modEntry->head.moduleTitle[i]), fmodule);
-
- for (i = 0; i < MOD_SAMPLES; i++)
- {
- for (j = 0; j < 22; j++)
- fputc(tolower(modEntry->samples[i].text[j]), fmodule);
-
- fputc(modEntry->samples[i].length >> 9, fmodule);
- fputc(modEntry->samples[i].length >> 1, fmodule);
- fputc(modEntry->samples[i].fineTune & 0x0F, fmodule);
- fputc((modEntry->samples[i].volume > 64) ? 64 : modEntry->samples[i].volume, fmodule);
-
- tempLoopLength = modEntry->samples[i].loopLength;
- if (tempLoopLength < 2)
- tempLoopLength = 2;
-
- tempLoopStart = modEntry->samples[i].loopStart;
- if (tempLoopLength == 2)
- tempLoopStart = 0;
-
- fputc(tempLoopStart >> 9, fmodule);
- fputc(tempLoopStart >> 1, fmodule);
- fputc(tempLoopLength >> 9, fmodule);
- fputc(tempLoopLength >> 1, fmodule);
- }
-
- fputc(modEntry->head.orderCount & 0x00FF, fmodule);
- fputc(0x7F, fmodule); // ProTracker puts 0x7F at this place (restart pos/BPM in other trackers)
-
- for (i = 0; i < MOD_ORDERS; i++)
- fputc(modEntry->head.order[i] & 0xFF, fmodule);
-
- tempPatternCount = 0;
- for (i = 0; i < MOD_ORDERS; i++)
- {
- if (tempPatternCount < modEntry->head.order[i])
- tempPatternCount = modEntry->head.order[i];
- }
-
- if (++tempPatternCount > MAX_PATTERNS)
- tempPatternCount = MAX_PATTERNS;
-
- fwrite((tempPatternCount <= 64) ? "M.K." : "M!K!", 1, 4, fmodule);
-
- for (i = 0; i < tempPatternCount; i++)
- {
- for (j = 0; j < MOD_ROWS; j++)
- {
- for (k = 0; k < AMIGA_VOICES; k++)
- {
- tmp = modEntry->patterns[i][(j * AMIGA_VOICES) + k];
-
- fputc((tmp.sample & 0xF0) | ((tmp.period >> 8) & 0x0F), fmodule);
- fputc(tmp.period & 0xFF, fmodule);
- fputc(((tmp.sample << 4) & 0xF0) | (tmp.command & 0x0F), fmodule);
- fputc(tmp.param, fmodule);
- }
- }
- }
-
- for (i = 0; i < MOD_SAMPLES; i++)
- {
- // Amiga ProTracker stuck "BEEP" sample fix
- if (modEntry->samples[i].length >= 2 && modEntry->samples[i].loopStart+modEntry->samples[i].loopLength == 2)
- {
- fputc(0, fmodule);
- fputc(0, fmodule);
-
- k = modEntry->samples[i].length;
- for (j = 2; j < k; j++)
- fputc(modEntry->sampleData[modEntry->samples[i].offset+j], fmodule);
- }
- else
- {
- fwrite(&modEntry->sampleData[MAX_SAMPLE_LEN * i], 1, modEntry->samples[i].length, fmodule);
- }
- }
-
- fclose(fmodule);
-
- displayMsg("MODULE SAVED !");
- setMsgPointer();
-
- diskop.cached = false;
- if (ui.diskOpScreenShown)
- ui.updateDiskOpFileList = true;
-
- updateWindowTitle(MOD_NOT_MODIFIED);
- return true;
-}
-
-#define IS_ID(s, b) !strncmp(s, b, 4)
-
-static uint8_t getModType(uint8_t *numChannels, const char *id)
-{
- *numChannels = 4;
-
- if (IS_ID("M.K.", id) || IS_ID("M!K!", id) || IS_ID("NSMS", id) || IS_ID("LARD", id) || IS_ID("PATT", id))
- {
- return FORMAT_MK; // ProTracker (or compatible)
- }
- else if (IS_ID("FLT4", id))
- {
- return FORMAT_FLT; // Startrekker (4 channels)
- }
- else if (IS_ID("N.T.", id))
- {
- return FORMAT_NT; // NoiseTracker
- }
- else if (IS_ID("M&K!", id) || IS_ID("FEST", id))
- {
- return FORMAT_HMNT; // His Master's NoiseTracker
- }
- else if (id[1] == 'C' && id[2] == 'H' && id[3] == 'N')
- {
- *numChannels = id[0] - '0';
- return FORMAT_FT2; // Fasttracker II 1..9 channels (or other trackers)
- }
-
- return FORMAT_UNKNOWN; // may be The Ultimate Soundtracker (set to FORMAT_STK later)
-}
-
-// converts zeroes to spaces in a string, up until the last zero found
-static void fixZeroesInString(char *str, uint32_t maxLength)
-{
- int32_t i;
-
- for (i = maxLength-1; i >= 0; i--)
- {
- if (str[i] != '\0')
- break;
- }
-
- // convert zeroes to spaces
- if (i > 0)
- {
- for (int32_t j = 0; j < i; j++)
- {
- if (str[j] == '\0')
- str[j] = ' ';
- }
- }
-}
-
-static uint8_t *unpackPPModule(FILE *f, uint32_t *filesize)
-{
- uint8_t *modBuffer, ppCrunchData[4], *ppBuffer;
- uint32_t ppPackLen, ppUnpackLen;
-
- ppPackLen = *filesize;
- if ((ppPackLen & 3) || ppPackLen <= 12)
- {
- displayErrorMsg("POWERPACKER ERROR");
- return NULL;
- }
-
- ppBuffer = (uint8_t *)malloc(ppPackLen);
- if (ppBuffer == NULL)
- {
- statusOutOfMemory();
- return NULL;
- }
-
- fseek(f, ppPackLen-4, SEEK_SET);
-
- ppCrunchData[0] = (uint8_t)fgetc(f);
- ppCrunchData[1] = (uint8_t)fgetc(f);
- ppCrunchData[2] = (uint8_t)fgetc(f);
- ppCrunchData[3] = (uint8_t)fgetc(f);
-
- ppUnpackLen = (ppCrunchData[0] << 16) | (ppCrunchData[1] << 8) | ppCrunchData[2];
-
- modBuffer = (uint8_t *)malloc(ppUnpackLen);
- if (modBuffer == NULL)
- {
- free(ppBuffer);
- statusOutOfMemory();
- return NULL;
- }
-
- rewind(f);
- fread(ppBuffer, 1, ppPackLen, f);
- fclose(f);
-
- if (!ppdecrunch(ppBuffer+8, modBuffer, ppBuffer+4, ppPackLen-12, ppUnpackLen, ppCrunchData[3]))
- {
- free(ppBuffer);
- displayErrorMsg("POWERPACKER ERROR");
- return NULL;
- }
-
- free(ppBuffer);
- *filesize = ppUnpackLen;
- return modBuffer;
-}
-
-module_t *modLoad(UNICHAR *fileName)
-{
- bool mightBeSTK, lateSTKVerFlag, veryLateSTKVerFlag;
- char modID[4], tmpChar;
- int8_t numSamples;
- uint8_t bytes[4], restartPos, modFormat;
- uint8_t *modBuffer, numChannels;
- int32_t i, j, k, loopStart, loopLength, loopOverflowVal, numPatterns;
- uint32_t powerPackerID, filesize;
- FILE *f;
- MEMFILE *m;
- module_t *newMod;
- moduleSample_t *s;
- note_t *note;
-
- veryLateSTKVerFlag = false; // "DFJ SoundTracker III" and later
- lateSTKVerFlag = false; // "TJC SoundTracker II" and later
- mightBeSTK = false;
-
- m = NULL;
- f = NULL;
- modBuffer = NULL;
-
- newMod = (module_t *)calloc(1, sizeof (module_t));
- if (newMod == NULL)
- {
- statusOutOfMemory();
- goto modLoadError;
- }
-
- f = UNICHAR_FOPEN(fileName, "rb");
- if (f == NULL)
- {
- displayErrorMsg("FILE I/O ERROR !");
- goto modLoadError;
- }
-
- fseek(f, 0, SEEK_END);
- filesize = ftell(f);
- rewind(f);
-
- // check if mod is a powerpacker mod
- fread(&powerPackerID, 4, 1, f);
- if (powerPackerID == 0x30325850) // "PX20"
- {
- displayErrorMsg("ENCRYPTED MOD !");
- goto modLoadError;
- }
- else if (powerPackerID == 0x30325050) // "PP20"
- {
- modBuffer = unpackPPModule(f, &filesize);
- if (modBuffer == NULL)
- goto modLoadError; // error msg is set in unpackPPModule()
- }
- else
- {
- modBuffer = (uint8_t *)malloc(filesize);
- if (modBuffer == NULL)
- {
- statusOutOfMemory();
- goto modLoadError;
- }
-
- fseek(f, 0, SEEK_SET);
- fread(modBuffer, 1, filesize, f);
- fclose(f);
- }
-
- // smallest and biggest possible PT .MOD
- if (filesize < 2108 || filesize > 4195326)
- {
- displayErrorMsg("NOT A MOD FILE !");
- goto modLoadError;
- }
-
- // Use MEMFILE functions on module buffer (similar to FILE functions)
-
- m = mopen(modBuffer, filesize);
- if (m == NULL)
- {
- displayErrorMsg("FILE I/O ERROR !");
- goto modLoadError;
- }
-
- // check magic ID
- memset(modID, 0, 4); // in case mread fails
- mseek(m, 1080, SEEK_SET);
- mread(modID, 1, 4, m);
-
- modFormat = getModType(&numChannels, modID);
- if (numChannels == 0 || numChannels > AMIGA_VOICES)
- {
- displayErrorMsg("UNSUPPORTED MOD !");
- goto modLoadError;
- }
-
- if (modFormat == FORMAT_UNKNOWN)
- mightBeSTK = true;
-
- mrewind(m);
- mread(newMod->head.moduleTitle, 1, 20, m);
- newMod->head.moduleTitle[20] = '\0';
-
- // convert illegal song name characters to space
- for (i = 0; i < 20; i++)
- {
- tmpChar = newMod->head.moduleTitle[i];
- if ((tmpChar < ' ' || tmpChar > '~') && tmpChar != '\0')
- tmpChar = ' ';
-
- newMod->head.moduleTitle[i] = (char)tolower(tmpChar);
- }
-
- fixZeroesInString(newMod->head.moduleTitle, 20);
-
- // read sample headers
- s = newMod->samples;
- for (i = 0; i < MOD_SAMPLES; i++, s++)
- {
- if (mightBeSTK && i >= 15) // skip reading sample headers past sample slot 15 in STK/UST modules
- {
- s->loopLength = 2; // this be set though
- continue;
- }
-
- mread(s->text, 1, 22, m);
- s->text[22] = '\0';
-
- if (modFormat == FORMAT_HMNT)
- {
- // most of "His Master's Noisetracker" songs have junk sample names, so let's wipe it.
- memset(s->text, 0, 22);
- }
- else
- {
- // convert illegal sample name characters to space
- for (j = 0; j < 22; j++)
- {
- tmpChar = s->text[j];
- if ((tmpChar < ' ' || tmpChar > '~') && tmpChar != '\0')
- tmpChar = ' ';
-
- s->text[j] = (char)tolower(tmpChar);
- }
-
- fixZeroesInString(s->text, 22);
- }
-
- s->length = (uint16_t)(((mgetc(m) << 8) | mgetc(m)) * 2);
-
- /* Only late versions of Ultimate SoundTracker could have samples larger than 9999 bytes.
- ** If found, we know for sure that this is a late STK module.
- */
- if (mightBeSTK && s->length > 9999)
- lateSTKVerFlag = true;
-
- if (modFormat == FORMAT_HMNT) // finetune in "His Master's NoiseTracker" is different
- s->fineTune = (uint8_t)((-mgetc(m) & 0x1F) / 2); // one more bit of precision, + inverted
- else
- s->fineTune = (uint8_t)mgetc(m) & 0xF;
-
- if (mightBeSTK)
- s->fineTune = 0; // this is high byte of volume in STK/UST (has no finetune), set to zero
-
- s->volume = (int8_t)mgetc(m);
- if ((uint8_t)s->volume > 64)
- s->volume = 64;
-
- loopStart = ((mgetc(m) << 8) | mgetc(m)) * 2;
- loopLength = ((mgetc(m) << 8) | mgetc(m)) * 2;
-
- if (loopLength < 2)
- loopLength = 2; // fixes empty samples in .MODs saved from FT2
-
- // we don't support samples bigger than 65534 bytes, disable uncompatible loops
- if (loopStart > MAX_SAMPLE_LEN || loopStart+loopLength > MAX_SAMPLE_LEN)
- {
- s->loopStart = 0;
- s->loopLength = 2;
- }
- else
- {
- s->loopStart = (uint16_t)loopStart;
- s->loopLength = (uint16_t)loopLength;
- }
-
- // in The Ultimate SoundTracker, sample loop start is in bytes, not words
- if (mightBeSTK)
- s->loopStart /= 2;
-
- // fix for poorly converted STK (< v2.5) -> PT/NT modules (FIXME: Worth keeping or not?)
- if (!mightBeSTK && s->loopLength > 2 && s->loopStart+s->loopLength > s->length)
- {
- if ((s->loopStart/2) + s->loopLength <= s->length)
- s->loopStart /= 2;
- }
- }
-
- newMod->head.orderCount = (uint8_t)mgetc(m);
-
- if (modFormat == FORMAT_MK && newMod->head.orderCount == 129)
- newMod->head.orderCount = 127; // fixes a specific copy of beatwave.mod
-
- if (newMod->head.orderCount > 129)
- {
- displayErrorMsg("NOT A MOD FILE !");
- goto modLoadError;
- }
-
- if (newMod->head.orderCount == 0)
- {
- displayErrorMsg("NOT A MOD FILE !");
- goto modLoadError;
- }
-
- restartPos = (uint8_t)mgetc(m);
- if (mightBeSTK && restartPos > 220)
- {
- displayErrorMsg("NOT A MOD FILE !");
- goto modLoadError;
- }
-
- newMod->head.initialTempo = 125;
- if (mightBeSTK)
- {
- /* If we're still here at this point and the mightBeSTK flag is set,
- ** then it's most likely a proper The Ultimate SoundTracker (STK/UST) module.
- */
- modFormat = FORMAT_STK;
-
- if (restartPos == 0)
- restartPos = 120;
-
- // jjk55.mod by Jesper Kyd has a bogus STK tempo value that should be ignored
- if (!strcmp("jjk55", newMod->head.moduleTitle))
- restartPos = 120;
-
- // the "restart pos" field in STK is the inital tempo (must be converted to BPM first)
- if (restartPos != 120) // 120 is a special case and means 50Hz (125BPM)
- {
- if (restartPos > 220)
- restartPos = 220;
-
- // convert UST tempo to BPM
- uint16_t ciaPeriod = (240 - restartPos) * 122;
- double dHz = (double)CIA_PAL_CLK / ciaPeriod;
- int32_t BPM = (int32_t)((dHz * (125.0 / 50.0)) + 0.5);
-
- newMod->head.initialTempo = (uint16_t)BPM;
- }
- }
-
- // read orders and count number of patterns
- numPatterns = 0;
- for (i = 0; i < MOD_ORDERS; i++)
- {
- newMod->head.order[i] = (int16_t)mgetc(m);
- if (newMod->head.order[i] > numPatterns)
- numPatterns = newMod->head.order[i];
- }
- numPatterns++;
-
- if (numPatterns > MAX_PATTERNS)
- {
- displayErrorMsg("UNSUPPORTED MOD !");
- goto modLoadError;
- }
-
- // skip magic ID (The Ultimate SoundTracker MODs doesn't have it)
- if (modFormat != FORMAT_STK)
- mseek(m, 4, SEEK_CUR);
-
- // allocate 100 patterns
- for (i = 0; i < MAX_PATTERNS; i++)
- {
- newMod->patterns[i] = (note_t *)calloc(MOD_ROWS * AMIGA_VOICES, sizeof (note_t));
- if (newMod->patterns[i] == NULL)
- {
- statusOutOfMemory();
- goto modLoadError;
- }
- }
-
- // load pattern data
- for (i = 0; i < numPatterns; i++)
- {
- note = newMod->patterns[i];
- for (j = 0; j < MOD_ROWS; j++)
- {
- for (k = 0; k < numChannels; k++, note++)
- {
- mread(bytes, 1, 4, m);
-
- note->period = ((bytes[0] & 0x0F) << 8) | bytes[1];
- note->sample = (bytes[0] & 0xF0) | (bytes[2] >> 4);
- note->command = bytes[2] & 0x0F;
- note->param = bytes[3];
-
- if (modFormat == FORMAT_STK)
- {
- if (note->command == 0xC || note->command == 0xD || note->command == 0xE)
- {
- // "TJC SoundTracker II" and later
- lateSTKVerFlag = true;
- }
-
- if (note->command == 0xF)
- {
- // "DFJ SoundTracker III" and later
- lateSTKVerFlag = true;
- veryLateSTKVerFlag = true;
- }
- }
- }
-
- if (numChannels < AMIGA_VOICES)
- note += AMIGA_VOICES-numChannels;
- }
- }
-
- // pattern command conversion for non-PT formats
- if (modFormat == FORMAT_STK || modFormat == FORMAT_FT2 || modFormat == FORMAT_NT || modFormat == FORMAT_HMNT || modFormat == FORMAT_FLT)
- {
- for (i = 0; i < numPatterns; i++)
- {
- note = newMod->patterns[i];
- for (j = 0; j < MOD_ROWS*4; j++, note++)
- {
- if (modFormat == FORMAT_NT || modFormat == FORMAT_HMNT)
- {
- // any Dxx == D00 in NT/HMNT
- if (note->command == 0xD)
- note->param = 0;
-
- // effect F with param 0x00 does nothing in NT/HMNT
- if (note->command == 0xF && note->param == 0)
- note->command = 0;
- }
- else if (modFormat == FORMAT_FLT) // Startrekker (4 channels)
- {
- if (note->command == 0xE) // remove unsupported "assembly macros" command
- {
- note->command = 0;
- note->param = 0;
- }
-
- // Startrekker is always in vblank mode, and limits speed to 0x1F
- if (note->command == 0xF && note->param > 0x1F)
- note->param = 0x1F;
- }
- else if (modFormat == FORMAT_STK)
- {
- // convert STK effects to PT effects
-
- if (!lateSTKVerFlag)
- {
- // old SoundTracker 1.x commands
-
- if (note->command == 1)
- {
- // arpeggio
- note->command = 0;
- }
- else if (note->command == 2)
- {
- // pitch slide
- if (note->param & 0xF0)
- {
- // pitch slide down
- note->command = 2;
- note->param >>= 4;
- }
- else if (note->param & 0x0F)
- {
- // pitch slide up
- note->command = 1;
- }
- }
- }
- else
- {
- // "DFJ SoundTracker II" or later
-
- // TODO: This needs more detection and is NOT correct!
- if (note->command == 0xD)
- {
- if (veryLateSTKVerFlag) // "DFJ SoundTracker III" or later
- {
- // pattern break w/ no param (param must be cleared to fix some songs)
- note->param = 0;
- }
- else
- {
- // volume slide
- note->command = 0xA;
- }
- }
- }
-
- // effect F with param 0x00 does nothing in UST/STK (I think?)
- if (note->command == 0xF && note->param == 0)
- note->command = 0;
- }
-
- // remove sample-trashing effects that were only present in ProTracker
-
- // remove E8x (Karplus-Strong in ProTracker)
- if (note->command == 0xE && (note->param >> 4) == 0x8)
- {
- note->command = 0;
- note->param = 0;
- }
-
- // remove EFx (Invert Loop in ProTracker)
- if (note->command == 0xE && (note->param >> 4) == 0xF)
- {
- note->command = 0;
- note->param = 0;
- }
- }
- }
- }
-
- // allocate sample data (+2 sample slots for overflow safety (Paula and scopes))
- newMod->sampleData = (int8_t *)calloc(MOD_SAMPLES + 2, MAX_SAMPLE_LEN);
- if (newMod->sampleData == NULL)
- {
- statusOutOfMemory();
- goto modLoadError;
- }
-
- // set sample data offsets (sample data = one huge buffer to rule them all)
- for (i = 0; i < MOD_SAMPLES; i++)
- newMod->samples[i].offset = MAX_SAMPLE_LEN * i;
-
- // load sample data
- numSamples = (modFormat == FORMAT_STK) ? 15 : 31;
- s = newMod->samples;
- for (i = 0; i < numSamples; i++, s++)
- {
- uint32_t bytesToSkip = 0;
-
- /* For Ultimate SoundTracker modules, only the loop area of a looped sample is played.
- ** Skip loading of eventual data present before loop start.
- */
- if (modFormat == FORMAT_STK && s->loopStart > 0 && s->loopLength < s->length)
- {
- s->length -= s->loopStart;
- mseek(m, s->loopStart, SEEK_CUR);
- s->loopStart = 0;
- }
-
- /* We don't support loading samples bigger than 65534 bytes in our PT2 clone,
- ** so clamp length and skip overflown data.
- */
- if (s->length > MAX_SAMPLE_LEN)
- {
- s->length = MAX_SAMPLE_LEN;
- bytesToSkip = s->length - MAX_SAMPLE_LEN;
- }
-
- // For Ultimate SoundTracker modules, don't load sample data after loop end
- uint16_t loopEnd = s->loopStart + s->loopLength;
- if (modFormat == FORMAT_STK && loopEnd > 2 && s->length > loopEnd)
- {
- bytesToSkip += s->length-loopEnd;
- s->length = loopEnd;
- }
-
- mread(&newMod->sampleData[s->offset], 1, s->length, m);
-
- if (bytesToSkip > 0)
- mseek(m, bytesToSkip, SEEK_CUR);
-
- // clear first two bytes of non-looping samples to prevent beep after sample has been played
- if (s->length >= 2 && loopEnd <= 2)
- {
- newMod->sampleData[s->offset+0] = 0;
- newMod->sampleData[s->offset+1] = 0;
- }
-
- // some modules are broken like this, adjust sample length if possible (this is ok if we have room)
- if (s->length > 0 && s->loopLength > 2 && s->loopStart+s->loopLength > s->length)
- {
- loopOverflowVal = (s->loopStart + s->loopLength) - s->length;
- if (s->length+loopOverflowVal <= MAX_SAMPLE_LEN)
- {
- s->length = (uint16_t)(s->length + loopOverflowVal); // this is safe, we're allocating 65534 bytes per sample slot
- }
- else
- {
- s->loopStart = 0;
- s->loopLength = 2;
- }
- }
- }
-
- mclose(&m);
- free(modBuffer);
-
- for (i = 0; i < AMIGA_VOICES; i++)
- newMod->channels[i].n_chanindex = (int8_t)i;
-
- return newMod;
-
-modLoadError:
- if (m != NULL)
- mclose(&m);
-
- if (modBuffer != NULL)
- free(modBuffer);
-
- if (newMod != NULL)
- {
- for (i = 0; i < MAX_PATTERNS; i++)
- {
- if (newMod->patterns[i] != NULL)
- free(newMod->patterns[i]);
- }
-
- free(newMod);
- }
-
- return NULL;
-}
-
-bool saveModule(bool checkIfFileExist, bool giveNewFreeFilename)
-{
- char fileName[128], tmpBuffer[64];
- uint16_t i;
- struct stat statBuffer;
-
- memset(fileName, 0, sizeof (fileName));
-
- if (config.modDot)
- {
- // extension.filename
- if (*modEntry->head.moduleTitle == '\0')
- {
- strcat(fileName, "mod.untitled");
- }
- else
- {
- strcat(fileName, "mod.");
- for (i = 4; i < 20+4; i++)
- {
- fileName[i] = (char)tolower(modEntry->head.moduleTitle[i-4]);
- if (fileName[i] == '\0') break;
- sanitizeFilenameChar(&fileName[i]);
- }
- }
- }
- else
- {
- // filename.extension
- if (*modEntry->head.moduleTitle == '\0')
- {
- strcat(fileName, "untitled.mod");
- }
- else
- {
- for (i = 0; i < 20; i++)
- {
- fileName[i] = (char)tolower(modEntry->head.moduleTitle[i]);
- if (fileName[i] == '\0') break;
- sanitizeFilenameChar(&fileName[i]);
- }
- strcat(fileName, ".mod");
- }
- }
-
- if (giveNewFreeFilename && stat(fileName, &statBuffer) == 0)
- {
- for (uint16_t j = 1; j <= 9999; j++)
- {
- memset(fileName, 0, sizeof (fileName));
- if (config.modDot)
- {
- // extension.filename
- if (*modEntry->head.moduleTitle == '\0')
- {
- sprintf(fileName, "mod.untitled-%d", j);
- }
- else
- {
- for (i = 0; i < 20; i++)
- {
- tmpBuffer[i] = (char)tolower(modEntry->head.moduleTitle[i]);
- if (tmpBuffer[i] == '\0') break;
- sanitizeFilenameChar(&tmpBuffer[i]);
- }
- sprintf(fileName, "mod.%s-%d", tmpBuffer, j);
- }
- }
- else
- {
- // filename.extension
- if (*modEntry->head.moduleTitle == '\0')
- {
- sprintf(fileName, "untitled-%d.mod", j);
- }
- else
- {
- for (i = 0; i < 20; i++)
- {
- tmpBuffer[i] = (char)tolower(modEntry->head.moduleTitle[i]);
- if (tmpBuffer[i] == '\0') break;
- sanitizeFilenameChar(&tmpBuffer[i]);
- }
- sprintf(fileName, "%s-%d.mod", tmpBuffer, j);
- }
- }
-
- if (stat(fileName, &statBuffer) != 0)
- break;
- }
- }
-
- if (checkIfFileExist)
- {
- if (stat(fileName, &statBuffer) == 0)
- {
- ui.askScreenShown = true;
- ui.askScreenType = ASK_SAVEMOD_OVERWRITE;
- pointerSetMode(POINTER_MODE_MSG1, NO_CARRY);
- setStatusMessage("OVERWRITE FILE ?", NO_CARRY);
- renderAskDialog();
- return -1;
- }
- }
-
- if (ui.askScreenShown)
- {
- ui.answerNo = false;
- ui.answerYes = false;
- ui.askScreenShown = false;
- }
-
- return modSave(fileName);
-}
-
-static MEMFILE *mopen(const uint8_t *src, uint32_t length)
-{
- MEMFILE *b;
-
- if (src == NULL || length == 0)
- return NULL;
-
- b = (MEMFILE *)malloc(sizeof (MEMFILE));
- if (b == NULL)
- return NULL;
-
- b->_base = (uint8_t *)src;
- b->_ptr = (uint8_t *)src;
- b->_cnt = length;
- b->_bufsiz = length;
- b->_eof = false;
-
- return b;
-}
-
-static void mclose(MEMFILE **buf)
-{
- if (*buf != NULL)
- {
- free(*buf);
- *buf = NULL;
- }
-}
-
-static int32_t mgetc(MEMFILE *buf)
-{
- int32_t b;
-
- if (buf == NULL || buf->_ptr == NULL || buf->_cnt <= 0)
- return 0;
-
- b = *buf->_ptr;
-
- buf->_cnt--;
- buf->_ptr++;
-
- if (buf->_cnt <= 0)
- {
- buf->_ptr = buf->_base + buf->_bufsiz;
- buf->_cnt = 0;
- buf->_eof = true;
- }
-
- return (int32_t)b;
-}
-
-static size_t mread(void *buffer, size_t size, size_t count, MEMFILE *buf)
-{
- int32_t pcnt;
- size_t wrcnt;
-
- if (buf == NULL || buf->_ptr == NULL)
- return 0;
-
- wrcnt = size * count;
- if (size == 0 || buf->_eof)
- return 0;
-
- pcnt = (buf->_cnt > (uint32_t)wrcnt) ? (uint32_t)wrcnt : buf->_cnt;
- memcpy(buffer, buf->_ptr, pcnt);
-
- buf->_cnt -= pcnt;
- buf->_ptr += pcnt;
-
- if (buf->_cnt <= 0)
- {
- buf->_ptr = buf->_base + buf->_bufsiz;
- buf->_cnt = 0;
- buf->_eof = true;
- }
-
- return pcnt / size;
-}
-
-static void mseek(MEMFILE *buf, int32_t offset, int32_t whence)
-{
- if (buf == NULL)
- return;
-
- if (buf->_base)
- {
- switch (whence)
- {
- case SEEK_SET: buf->_ptr = buf->_base + offset; break;
- case SEEK_CUR: buf->_ptr += offset; break;
- case SEEK_END: buf->_ptr = buf->_base + buf->_bufsiz + offset; break;
- default: break;
- }
-
- buf->_eof = false;
- if (buf->_ptr >= buf->_base+buf->_bufsiz)
- {
- buf->_ptr = buf->_base + buf->_bufsiz;
- buf->_eof = true;
- }
-
- buf->_cnt = (uint32_t)((buf->_base + buf->_bufsiz) - buf->_ptr);
- }
-}
-
-static void mrewind(MEMFILE *buf)
-{
- mseek(buf, 0, SEEK_SET);
-}
-
-/* PowerPacker unpack code taken from Heikki Orsila's amigadepack. Seems to have no license,
-** so I'll assume it fits into BSD 3-Clause. If not, feel free to contact me at my email
-** address found at the bottom of 16-bits.org.
-**
-** Modified by 8bitbubsy (me).
-*/
-
-#define PP_READ_BITS(nbits, var) \
- bitCnt = (nbits); \
- while (bitsLeft < bitCnt) \
- { \
- if (bufSrc < src) \
- return false; \
- bitBuffer |= ((*--bufSrc) << bitsLeft); \
- bitsLeft += 8; \
- } \
- (var) = 0; \
- bitsLeft -= bitCnt; \
- while (bitCnt--) \
- { \
- (var) = ((var) << 1) | (bitBuffer & 1); \
- bitBuffer >>= 1; \
- } \
-
-static uint8_t ppdecrunch(uint8_t *src, uint8_t *dst, uint8_t *offsetLens, uint32_t srcLen, uint32_t dstLen, uint8_t skipBits)
-{
- uint8_t *bufSrc, *dstEnd, *out, bitsLeft, bitCnt;
- uint32_t x, todo, offBits, offset, written, bitBuffer;
-
- if (src == NULL || dst == NULL || offsetLens == NULL)
- return false;
-
- bitsLeft = 0;
- bitBuffer = 0;
- written = 0;
- bufSrc = src + srcLen;
- out = dst + dstLen;
- dstEnd = out;
-
- PP_READ_BITS(skipBits, x);
- while (written < dstLen)
- {
- PP_READ_BITS(1, x);
- if (x == 0)
- {
- todo = 1;
-
- do
- {
- PP_READ_BITS(2, x);
- todo += x;
- }
- while (x == 3);
-
- while (todo--)
- {
- PP_READ_BITS(8, x);
- if (out <= dst)
- return false;
-
- *--out = (uint8_t)x;
- written++;
- }
-
- if (written == dstLen)
- break;
- }
-
- PP_READ_BITS(2, x);
- offBits = offsetLens[x];
- todo = x + 2;
-
- if (x == 3)
- {
- PP_READ_BITS(1, x);
- if (x == 0) offBits = 7;
-
- PP_READ_BITS((uint8_t)offBits, offset);
- do
- {
- PP_READ_BITS(3, x);
- todo += x;
- }
- while (x == 7);
- }
- else
- {
- PP_READ_BITS((uint8_t)offBits, offset);
- }
-
- if (out+offset >= dstEnd)
- return false;
-
- while (todo--)
- {
- x = out[offset];
- if (out <= dst)
- return false;
-
- *--out = (uint8_t)x;
- written++;
- }
- }
-
- return true;
-}
-
-void setupNewMod(void)
-{
- int8_t i;
-
- // setup GUI text pointers
- for (i = 0; i < MOD_SAMPLES; i++)
- {
- modEntry->samples[i].volumeDisp = &modEntry->samples[i].volume;
- modEntry->samples[i].lengthDisp = &modEntry->samples[i].length;
- modEntry->samples[i].loopStartDisp = &modEntry->samples[i].loopStart;
- modEntry->samples[i].loopLengthDisp = &modEntry->samples[i].loopLength;
-
- fillSampleRedoBuffer(i);
- }
-
- modSetPos(0, 0);
- modSetPattern(0); // set pattern to 00 instead of first order's pattern
-
- editor.currEditPatternDisp = &modEntry->currPattern;
- editor.currPosDisp = &modEntry->currOrder;
- editor.currPatternDisp = &modEntry->head.order[0];
- editor.currPosEdPattDisp = &modEntry->head.order[0];
- editor.currLengthDisp = &modEntry->head.orderCount;
-
- // calculate MOD size
- ui.updateSongSize = true;
-
- editor.muted[0] = false;
- editor.muted[1] = false;
- editor.muted[2] = false;
- editor.muted[3] = false;
-
- editor.editMoveAdd = 1;
- editor.currSample = 0;
- editor.musicTime = 0;
- editor.modLoaded = true;
- editor.blockMarkFlag = false;
- editor.sampleZero = false;
- editor.keypadSampleOffset = 0;
-
- setLEDFilter(false); // real PT doesn't do this, but that's insane
-
- updateWindowTitle(MOD_NOT_MODIFIED);
-
- editor.timingMode = TEMPO_MODE_CIA;
-
- modSetSpeed(6);
- modSetTempo(modEntry->head.initialTempo); // 125 for normal MODs, custom value for certain STK/UST MODs
-
- updateCurrSample();
- editor.samplePos = 0;
- updateSamplePos();
-}
-
-void loadModFromArg(char *arg)
-{
- uint32_t filenameLen;
- UNICHAR *filenameU;
-
- ui.introScreenShown = false;
- statusAllRight();
-
- filenameLen = (uint32_t)strlen(arg);
-
- filenameU = (UNICHAR *)calloc(filenameLen + 2, sizeof (UNICHAR));
- if (filenameU == NULL)
- {
- statusOutOfMemory();
- return;
- }
-
-#ifdef _WIN32
- MultiByteToWideChar(CP_UTF8, 0, arg, -1, filenameU, filenameLen);
-#else
- strcpy(filenameU, arg);
-#endif
-
- tempMod = modLoad(filenameU);
- if (tempMod != NULL)
- {
- modEntry->moduleLoaded = false;
- modFree();
- modEntry = tempMod;
- setupNewMod();
- modEntry->moduleLoaded = true;
- }
- else
- {
- editor.errorMsgActive = true;
- editor.errorMsgBlock = true;
- editor.errorMsgCounter = 0;
-
- // status/error message is set in the mod loader
- setErrPointer();
- }
-
- free(filenameU);
-}
-
-static bool testExtension(char *ext, uint8_t extLen, char *fullPath)
-{
- // checks for EXT.filename and filename.EXT
- char *fileName, begStr[8], endStr[8];
- uint32_t fileNameLen;
-
- extLen++; // add one to length (dot)
-
- fileName = strrchr(fullPath, DIR_DELIMITER);
- if (fileName != NULL)
- fileName++;
- else
- fileName = fullPath;
-
- fileNameLen = (uint32_t)strlen(fileName);
- if (fileNameLen >= extLen)
- {
- sprintf(begStr, "%s.", ext);
- if (!_strnicmp(begStr, fileName, extLen))
- return true;
-
- sprintf(endStr, ".%s", ext);
- if (!_strnicmp(endStr, fileName + (fileNameLen - extLen), extLen))
- return true;
- }
-
- return false;
-}
-
-void loadDroppedFile(char *fullPath, uint32_t fullPathLen, bool autoPlay, bool songModifiedCheck)
-{
- bool isMod;
- char *fileName, *ansiName;
- uint8_t oldMode, oldPlayMode;
- UNICHAR *fullPathU;
-
- // don't allow drag n' drop if the tracker is busy
- if (ui.pointerMode == POINTER_MODE_MSG1 || diskop.isFilling ||
- editor.isWAVRendering || ui.samplerFiltersBoxShown || ui.samplerVolBoxShown)
- {
- return;
- }
-
- ansiName = (char *)calloc(fullPathLen + 10, sizeof (char));
- if (ansiName == NULL)
- {
- statusOutOfMemory();
- return;
- }
-
- fullPathU = (UNICHAR *)calloc(fullPathLen + 2, sizeof (UNICHAR));
- if (fullPathU == NULL)
- {
- statusOutOfMemory();
- return;
- }
-
-#ifdef _WIN32
- MultiByteToWideChar(CP_UTF8, 0, fullPath, -1, fullPathU, fullPathLen);
-#else
- strcpy(fullPathU, fullPath);
-#endif
-
- unicharToAnsi(ansiName, fullPathU, fullPathLen);
-
- // make a new pointer point to filename (strip path)
- fileName = strrchr(ansiName, DIR_DELIMITER);
- if (fileName != NULL)
- fileName++;
- else
- fileName = ansiName;
-
- // check if the file extension is a module (FIXME: check module by content instead..?)
- isMod = false;
- if (testExtension("MOD", 3, fileName)) isMod = true;
- else if (testExtension("M15", 3, fileName)) isMod = true;
- else if (testExtension("STK", 3, fileName)) isMod = true;
- else if (testExtension("NST", 3, fileName)) isMod = true;
- else if (testExtension("UST", 3, fileName)) isMod = true;
- else if (testExtension("PP", 2, fileName)) isMod = true;
- else if (testExtension("NT", 2, fileName)) isMod = true;
-
- if (isMod)
- {
- if (songModifiedCheck && modEntry->modified)
- {
- free(ansiName);
- free(fullPathU);
-
- memcpy(oldFullPath, fullPath, fullPathLen);
- oldFullPath[fullPathLen+0] = 0;
- oldFullPath[fullPathLen+1] = 0;
-
- oldFullPathLen = fullPathLen;
- oldAutoPlay = autoPlay;
-
- // de-minimize window and set focus so that the user sees the message box
- SDL_RestoreWindow(video.window);
- SDL_RaiseWindow(video.window);
-
- showSongUnsavedAskBox(ASK_DISCARD_SONG_DRAGNDROP);
- return;
- }
-
- tempMod = modLoad(fullPathU);
- if (tempMod != NULL)
- {
- oldMode = editor.currMode;
- oldPlayMode = editor.playMode;
-
- modStop();
- modFree();
-
- modEntry = tempMod;
- setupNewMod();
- modEntry->moduleLoaded = true;
-
- statusAllRight();
-
- if (autoPlay)
- {
- editor.playMode = PLAY_MODE_NORMAL;
- editor.currMode = MODE_PLAY;
-
- // start normal playback
- modPlay(DONT_SET_PATTERN, 0, 0);
-
- pointerSetMode(POINTER_MODE_PLAY, DO_CARRY);
- }
- else if (oldMode == MODE_PLAY || oldMode == MODE_RECORD)
- {
- // use last mode
- editor.playMode = oldPlayMode;
- if (oldPlayMode == PLAY_MODE_PATTERN || oldMode == MODE_RECORD)
- modPlay(0, 0, 0);
- else
- modPlay(DONT_SET_PATTERN, 0, 0);
- editor.currMode = oldMode;
-
- if (oldMode == MODE_RECORD)
- pointerSetMode(POINTER_MODE_RECORD, DO_CARRY);
- else
- pointerSetMode(POINTER_MODE_PLAY, DO_CARRY);
- }
- else
- {
- // stop playback
- editor.playMode = PLAY_MODE_NORMAL;
- editor.currMode = MODE_IDLE;
- editor.songPlaying = false;
- pointerSetMode(POINTER_MODE_IDLE, DO_CARRY);
- }
-
- displayMainScreen();
- }
- else
- {
- editor.errorMsgActive = true;
- editor.errorMsgBlock = true;
- editor.errorMsgCounter = 0;
- setErrPointer(); // status/error message is set in the mod loader
- }
- }
- else
- {
- loadSample(fullPathU, fileName);
- }
-
- free(ansiName);
- free(fullPathU);
-}
-
-void loadDroppedFile2(void)
-{
- loadDroppedFile(oldFullPath, oldFullPathLen, oldAutoPlay, false);
-}
-
-module_t *createNewMod(void)
-{
- uint8_t i;
- module_t *newMod;
-
- newMod = (module_t *)calloc(1, sizeof (module_t));
- if (newMod == NULL)
- goto oom;
-
- for (i = 0; i < MAX_PATTERNS; i++)
- {
- newMod->patterns[i] = (note_t *)calloc(1, MOD_ROWS * sizeof (note_t) * AMIGA_VOICES);
- if (newMod->patterns[i] == NULL)
- goto oom;
- }
-
- // +2 sample slots for overflow safety (Paula and scopes)
- newMod->sampleData = (int8_t *)calloc(MOD_SAMPLES + 2, MAX_SAMPLE_LEN);
- if (newMod->sampleData == NULL)
- goto oom;
-
- newMod->head.orderCount = 1;
-
- for (i = 0; i < MOD_SAMPLES; i++)
- {
- newMod->samples[i].offset = MAX_SAMPLE_LEN * i;
- newMod->samples[i].loopLength = 2;
-
- // setup GUI text pointers
- newMod->samples[i].volumeDisp = &newMod->samples[i].volume;
- newMod->samples[i].lengthDisp = &newMod->samples[i].length;
- newMod->samples[i].loopStartDisp = &newMod->samples[i].loopStart;
- newMod->samples[i].loopLengthDisp = &newMod->samples[i].loopLength;
- }
-
- for (i = 0; i < AMIGA_VOICES; i++)
- newMod->channels[i].n_chanindex = i;
-
- // setup GUI text pointers
- editor.currEditPatternDisp = &newMod->currPattern;
- editor.currPosDisp = &newMod->currOrder;
- editor.currPatternDisp = &newMod->head.order[0];
- editor.currPosEdPattDisp = &newMod->head.order[0];
- editor.currLengthDisp = &newMod->head.orderCount;
-
- ui.updateSongSize = true;
- return newMod;
-
-oom:
- showErrorMsgBox("Out of memory!");
- return NULL;
-}
--- a/src/pt2_modloader.h
+++ /dev/null
@@ -1,17 +1,0 @@
-#pragma once
-
-#include <stdint.h>
-#include <stdbool.h>
-#include "pt2_header.h"
-#include "pt2_unicode.h"
-#include "pt2_structs.h"
-
-void showSongUnsavedAskBox(int8_t askScreenType);
-void loadModFromArg(char *arg);
-void loadDroppedFile(char *fullPath, uint32_t fullPathLen, bool autoPlay, bool songModifiedCheck);
-void loadDroppedFile2(void);
-module_t *createNewMod(void);
-bool saveModule(bool checkIfFileExist, bool giveNewFreeFilename);
-bool modSave(char *fileName);
-module_t *modLoad(UNICHAR *fileName);
-void setupNewMod(void);
--- a/src/pt2_modplayer.c
+++ /dev/null
@@ -1,1549 +1,0 @@
-/* Very accurate C port of ProTracker 2.3D's replayer by 8bitbubsy, slightly modified.
-** Earlier versions of the PT clone used a completely different and less accurate replayer.
-*/
-
-// for finding memory leaks in debug mode with Visual Studio
-#if defined _DEBUG && defined _MSC_VER
-#include <crtdbg.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <stdbool.h>
-#include <math.h>
-#include "pt2_header.h"
-#include "pt2_audio.h"
-#include "pt2_helpers.h"
-#include "pt2_palette.h"
-#include "pt2_tables.h"
-#include "pt2_modloader.h"
-#include "pt2_config.h"
-#include "pt2_sampler.h"
-#include "pt2_visuals.h"
-#include "pt2_textout.h"
-#include "pt2_scopes.h"
-
-static bool posJumpAssert, pBreakFlag, updateUIPositions, modHasBeenPlayed;
-static int8_t pBreakPosition, oldRow, modPattern;
-static uint8_t pattDelTime, setBPMFlag, lowMask = 0xFF, pattDelTime2, oldSpeed;
-static int16_t modOrder, oldPattern, oldOrder;
-static uint16_t modBPM, oldBPM;
-
-static const int8_t vuMeterHeights[65] =
-{
- 0, 0, 1, 2, 2, 3, 4, 5,
- 5, 6, 7, 8, 8, 9, 10, 11,
- 11, 12, 13, 14, 14, 15, 16, 17,
- 17, 18, 19, 20, 20, 21, 22, 23,
- 23, 24, 25, 26, 26, 27, 28, 29,
- 29, 30, 31, 32, 32, 33, 34, 35,
- 35, 36, 37, 38, 38, 39, 40, 41,
- 41, 42, 43, 44, 44, 45, 46, 47,
- 47
-};
-
-static const uint8_t funkTable[16] = // EFx (FunkRepeat/InvertLoop)
-{
- 0x00, 0x05, 0x06, 0x07, 0x08, 0x0A, 0x0B, 0x0D,
- 0x10, 0x13, 0x16, 0x1A, 0x20, 0x2B, 0x40, 0x80
-};
-
-void modSetSpeed(uint8_t speed)
-{
- editor.modSpeed = speed;
- modEntry->currSpeed = speed;
- editor.modTick = 0;
-}
-
-void doStopIt(bool resetPlayMode)
-{
- moduleChannel_t *c;
- uint8_t i;
-
- editor.songPlaying = false;
-
- resetCachedMixerPeriod();
-
- pattDelTime = 0;
- pattDelTime2 = 0;
-
- if (resetPlayMode)
- {
- editor.playMode = PLAY_MODE_NORMAL;
- editor.currMode = MODE_IDLE;
-
- pointerSetMode(POINTER_MODE_IDLE, DO_CARRY);
- }
-
- for (i = 0; i < AMIGA_VOICES; i++)
- {
- c = &modEntry->channels[i];
- c->n_wavecontrol = 0;
- c->n_glissfunk = 0;
- c->n_finetune = 0;
- c->n_loopcount = 0;
- }
-}
-
-void setPattern(int16_t pattern)
-{
- if (pattern > MAX_PATTERNS-1)
- pattern = MAX_PATTERNS-1;
-
- modEntry->currPattern = modPattern = (int8_t)pattern;
-}
-
-void storeTempVariables(void) // this one is accessed in other files, so non-static
-{
- oldBPM = modEntry->currBPM;
- oldRow = modEntry->currRow;
- oldOrder = modEntry->currOrder;
- oldSpeed = modEntry->currSpeed;
- oldPattern = modEntry->currPattern;
-}
-
-static void setVUMeterHeight(moduleChannel_t *ch)
-{
- uint8_t vol;
-
- if (editor.muted[ch->n_chanindex])
- return;
-
- vol = ch->n_volume;
- if ((ch->n_cmd & 0xF00) == 0xC00) // handle Cxx effect
- vol = ch->n_cmd & 0xFF;
-
- if (vol > 64)
- vol = 64;
-
- editor.vuMeterVolumes[ch->n_chanindex] = vuMeterHeights[vol];
-}
-
-static void updateFunk(moduleChannel_t *ch)
-{
- int8_t funkspeed;
-
- funkspeed = ch->n_glissfunk >> 4;
- if (funkspeed == 0)
- return;
-
- ch->n_funkoffset += funkTable[funkspeed];
- if (ch->n_funkoffset >= 128)
- {
- ch->n_funkoffset = 0;
-
- if (ch->n_loopstart != NULL && ch->n_wavestart != NULL) // SAFETY BUG FIX
- {
- if (++ch->n_wavestart >= ch->n_loopstart+ch->n_replen)
- ch->n_wavestart = ch->n_loopstart;
-
- *ch->n_wavestart = -1 - *ch->n_wavestart;
- }
- }
-}
-
-static void setGlissControl(moduleChannel_t *ch)
-{
- ch->n_glissfunk = (ch->n_glissfunk & 0xF0) | (ch->n_cmd & 0x0F);
-}
-
-static void setVibratoControl(moduleChannel_t *ch)
-{
- ch->n_wavecontrol = (ch->n_wavecontrol & 0xF0) | (ch->n_cmd & 0x0F);
-}
-
-static void setFineTune(moduleChannel_t *ch)
-{
- ch->n_finetune = ch->n_cmd & 0xF;
-}
-
-static void jumpLoop(moduleChannel_t *ch)
-{
- uint8_t tempParam;
-
- if (editor.modTick != 0)
- return;
-
- if ((ch->n_cmd & 0xF) == 0)
- {
- ch->n_pattpos = modEntry->row;
- }
- else
- {
- if (ch->n_loopcount == 0)
- {
- ch->n_loopcount = ch->n_cmd & 0xF;
- }
- else
- {
- if (--ch->n_loopcount == 0)
- return;
- }
-
- pBreakPosition = ch->n_pattpos;
- pBreakFlag = 1;
-
- if (editor.isWAVRendering)
- {
- for (tempParam = pBreakPosition; tempParam <= modEntry->row; tempParam++)
- editor.rowVisitTable[(modOrder * MOD_ROWS) + tempParam] = false;
- }
- }
-}
-
-static void setTremoloControl(moduleChannel_t *ch)
-{
- ch->n_wavecontrol = ((ch->n_cmd & 0xF) << 4) | (ch->n_wavecontrol & 0xF);
-}
-
-static void karplusStrong(moduleChannel_t *ch)
-{
- (void)ch; // this effect is *horrible* and never used, I'm not implementing it.
-}
-
-static void doRetrg(moduleChannel_t *ch)
-{
- paulaSetData(ch->n_chanindex, ch->n_start); // n_start is increased on 9xx
- paulaSetLength(ch->n_chanindex, ch->n_length);
- paulaSetPeriod(ch->n_chanindex, ch->n_period);
- paulaStartDMA(ch->n_chanindex);
-
- // these take effect after the current DMA cycle is done
- paulaSetData(ch->n_chanindex, ch->n_loopstart);
- paulaSetLength(ch->n_chanindex, ch->n_replen);
-
- updateSpectrumAnalyzer(ch->n_volume, ch->n_period);
- setVUMeterHeight(ch);
-}
-
-static void retrigNote(moduleChannel_t *ch)
-{
- if ((ch->n_cmd & 0xF) > 0)
- {
- if (editor.modTick == 0 && (ch->n_note & 0xFFF) > 0)
- return;
-
- if (editor.modTick % (ch->n_cmd & 0xF) == 0)
- doRetrg(ch);
- }
-}
-
-static void volumeSlide(moduleChannel_t *ch)
-{
- uint8_t cmd = ch->n_cmd & 0xFF;
-
- if ((cmd & 0xF0) == 0)
- {
- ch->n_volume -= cmd & 0x0F;
- if (ch->n_volume < 0)
- ch->n_volume = 0;
- }
- else
- {
- ch->n_volume += cmd >> 4;
- if (ch->n_volume > 64)
- ch->n_volume = 64;
- }
-}
-
-static void volumeFineUp(moduleChannel_t *ch)
-{
- if (editor.modTick == 0)
- {
- ch->n_volume += ch->n_cmd & 0xF;
- if (ch->n_volume > 64)
- ch->n_volume = 64;
- }
-}
-
-static void volumeFineDown(moduleChannel_t *ch)
-{
- if (editor.modTick == 0)
- {
- ch->n_volume -= ch->n_cmd & 0xF;
- if (ch->n_volume < 0)
- ch->n_volume = 0;
- }
-}
-
-static void noteCut(moduleChannel_t *ch)
-{
- if (editor.modTick == (ch->n_cmd & 0xF))
- ch->n_volume = 0;
-}
-
-static void noteDelay(moduleChannel_t *ch)
-{
- if (editor.modTick == (ch->n_cmd & 0xF) && (ch->n_note & 0xFFF) > 0)
- doRetrg(ch);
-}
-
-static void patternDelay(moduleChannel_t *ch)
-{
- if (editor.modTick == 0 && pattDelTime2 == 0)
- pattDelTime = (ch->n_cmd & 0xF) + 1;
-}
-
-static void funkIt(moduleChannel_t *ch)
-{
- if (editor.modTick == 0)
- {
- ch->n_glissfunk = ((ch->n_cmd & 0xF) << 4) | (ch->n_glissfunk & 0xF);
- if ((ch->n_glissfunk & 0xF0) > 0)
- updateFunk(ch);
- }
-}
-
-static void positionJump(moduleChannel_t *ch)
-{
- modOrder = (ch->n_cmd & 0xFF) - 1; // 0xFF (B00) jumps to pat 0
- pBreakPosition = 0;
- posJumpAssert = true;
-}
-
-static void volumeChange(moduleChannel_t *ch)
-{
- ch->n_volume = ch->n_cmd & 0xFF;
- if ((uint8_t)ch->n_volume > 64)
- ch->n_volume = 64;
-}
-
-static void patternBreak(moduleChannel_t *ch)
-{
- pBreakPosition = (((ch->n_cmd & 0xF0) >> 4) * 10) + (ch->n_cmd & 0x0F);
- if ((uint8_t)pBreakPosition > 63)
- pBreakPosition = 0;
-
- posJumpAssert = true;
-}
-
-static void setSpeed(moduleChannel_t *ch)
-{
- if ((ch->n_cmd & 0xFF) > 0)
- {
- editor.modTick = 0;
-
- if (editor.timingMode == TEMPO_MODE_VBLANK || (ch->n_cmd & 0xFF) < 32)
- modSetSpeed(ch->n_cmd & 0xFF);
- else
- setBPMFlag = ch->n_cmd & 0xFF; // CIA doesn't refresh its registers until the next interrupt, so change it later
- }
- else
- {
- editor.songPlaying = false;
- editor.playMode = PLAY_MODE_NORMAL;
- editor.currMode = MODE_IDLE;
-
- pointerSetMode(POINTER_MODE_IDLE, DO_CARRY);
- }
-}
-
-static void arpeggio(moduleChannel_t *ch)
-{
- uint8_t arpTick, arpNote;
- const int16_t *periods;
-
- assert(editor.modTick < 32);
- arpTick = arpTickTable[editor.modTick]; // 0, 1, 2
-
- if (arpTick == 1)
- {
- arpNote = (uint8_t)(ch->n_cmd >> 4);
- }
- else if (arpTick == 2)
- {
- arpNote = ch->n_cmd & 0xF;
- }
- else // arpTick 0
- {
- paulaSetPeriod(ch->n_chanindex, ch->n_period);
- return;
- }
-
- /* 8bitbubsy: If the finetune is -1, this can overflow up to
- ** 15 words outside of the table. The table is padded with
- ** the correct overflow values to allow this to safely happen
- ** and sound correct at the same time.
- */
- periods = &periodTable[ch->n_finetune * 37];
- for (int32_t baseNote = 0; baseNote < 37; baseNote++)
- {
- if (ch->n_period >= periods[baseNote])
- {
- paulaSetPeriod(ch->n_chanindex, periods[baseNote+arpNote]);
- break;
- }
- }
-}
-
-static void portaUp(moduleChannel_t *ch)
-{
- ch->n_period -= (ch->n_cmd & 0xFF) & lowMask;
- lowMask = 0xFF;
-
- if ((ch->n_period & 0xFFF) < 113)
- ch->n_period = (ch->n_period & 0xF000) | 113;
-
- paulaSetPeriod(ch->n_chanindex, ch->n_period & 0xFFF);
-}
-
-static void portaDown(moduleChannel_t *ch)
-{
- ch->n_period += (ch->n_cmd & 0xFF) & lowMask;
- lowMask = 0xFF;
-
- if ((ch->n_period & 0xFFF) > 856)
- ch->n_period = (ch->n_period & 0xF000) | 856;
-
- paulaSetPeriod(ch->n_chanindex, ch->n_period & 0xFFF);
-}
-
-static void filterOnOff(moduleChannel_t *ch)
-{
- setLEDFilter(!(ch->n_cmd & 1));
-}
-
-static void finePortaUp(moduleChannel_t *ch)
-{
- if (editor.modTick == 0)
- {
- lowMask = 0xF;
- portaUp(ch);
- }
-}
-
-static void finePortaDown(moduleChannel_t *ch)
-{
- if (editor.modTick == 0)
- {
- lowMask = 0xF;
- portaDown(ch);
- }
-}
-
-static void setTonePorta(moduleChannel_t *ch)
-{
- uint8_t i;
- const int16_t *portaPointer;
- uint16_t note;
-
- note = ch->n_note & 0xFFF;
- portaPointer = &periodTable[ch->n_finetune * 37];
-
- i = 0;
- while (true)
- {
- // portaPointer[36] = 0, so i=36 is safe
- if (note >= portaPointer[i])
- break;
-
- if (++i >= 37)
- {
- i = 35;
- break;
- }
- }
-
- if ((ch->n_finetune & 8) && i > 0)
- i--;
-
- ch->n_wantedperiod = portaPointer[i];
- ch->n_toneportdirec = 0;
-
- if (ch->n_period == ch->n_wantedperiod) ch->n_wantedperiod = 0;
- else if (ch->n_period > ch->n_wantedperiod) ch->n_toneportdirec = 1;
-}
-
-static void tonePortNoChange(moduleChannel_t *ch)
-{
- uint8_t i;
- const int16_t *portaPointer;
-
- if (ch->n_wantedperiod <= 0)
- return;
-
- if (ch->n_toneportdirec > 0)
- {
- ch->n_period -= ch->n_toneportspeed;
- if (ch->n_period <= ch->n_wantedperiod)
- {
- ch->n_period = ch->n_wantedperiod;
- ch->n_wantedperiod = 0;
- }
- }
- else
- {
- ch->n_period += ch->n_toneportspeed;
- if (ch->n_period >= ch->n_wantedperiod)
- {
- ch->n_period = ch->n_wantedperiod;
- ch->n_wantedperiod = 0;
- }
- }
-
- if ((ch->n_glissfunk & 0xF) == 0)
- {
- paulaSetPeriod(ch->n_chanindex, ch->n_period);
- }
- else
- {
- portaPointer = &periodTable[ch->n_finetune * 37];
-
- i = 0;
- while (true)
- {
- // portaPointer[36] = 0, so i=36 is safe
- if (ch->n_period >= portaPointer[i])
- break;
-
- if (++i >= 37)
- {
- i = 35;
- break;
- }
- }
-
- paulaSetPeriod(ch->n_chanindex, portaPointer[i]);
- }
-}
-
-static void tonePortamento(moduleChannel_t *ch)
-{
- if ((ch->n_cmd & 0xFF) > 0)
- {
- ch->n_toneportspeed = ch->n_cmd & 0xFF;
- ch->n_cmd &= 0xFF00;
- }
-
- tonePortNoChange(ch);
-}
-
-static void vibratoNoChange(moduleChannel_t *ch)
-{
- uint8_t vibratoTemp;
- int16_t vibratoData;
-
- vibratoTemp = (ch->n_vibratopos / 4) & 31;
- vibratoData = ch->n_wavecontrol & 3;
-
- if (vibratoData == 0)
- {
- vibratoData = vibratoTable[vibratoTemp];
- }
- else
- {
- if (vibratoData == 1)
- {
- if (ch->n_vibratopos < 0)
- vibratoData = 255 - (vibratoTemp * 8);
- else
- vibratoData = vibratoTemp * 8;
- }
- else
- {
- vibratoData = 255;
- }
- }
-
- vibratoData = (vibratoData * (ch->n_vibratocmd & 0xF)) / 128;
-
- if (ch->n_vibratopos < 0)
- vibratoData = ch->n_period - vibratoData;
- else
- vibratoData = ch->n_period + vibratoData;
-
- paulaSetPeriod(ch->n_chanindex, vibratoData);
-
- ch->n_vibratopos += ((ch->n_vibratocmd >> 4) * 4);
-}
-
-static void vibrato(moduleChannel_t *ch)
-{
- if ((ch->n_cmd & 0xFF) > 0)
- {
- if ((ch->n_cmd & 0x0F) > 0)
- ch->n_vibratocmd = (ch->n_vibratocmd & 0xF0) | (ch->n_cmd & 0x0F);
-
- if ((ch->n_cmd & 0xF0) > 0)
- ch->n_vibratocmd = (ch->n_cmd & 0xF0) | (ch->n_vibratocmd & 0x0F);
- }
-
- vibratoNoChange(ch);
-}
-
-static void tonePlusVolSlide(moduleChannel_t *ch)
-{
- tonePortNoChange(ch);
- volumeSlide(ch);
-}
-
-static void vibratoPlusVolSlide(moduleChannel_t *ch)
-{
- vibratoNoChange(ch);
- volumeSlide(ch);
-}
-
-static void tremolo(moduleChannel_t *ch)
-{
- int8_t tremoloTemp;
- int16_t tremoloData;
-
- if ((ch->n_cmd & 0xFF) > 0)
- {
- if ((ch->n_cmd & 0x0F) > 0)
- ch->n_tremolocmd = (ch->n_tremolocmd & 0xF0) | (ch->n_cmd & 0x0F);
-
- if ((ch->n_cmd & 0xF0) > 0)
- ch->n_tremolocmd = (ch->n_cmd & 0xF0) | (ch->n_tremolocmd & 0x0F);
- }
-
- tremoloTemp = (ch->n_tremolopos / 4) & 31;
- tremoloData = (ch->n_wavecontrol >> 4) & 3;
-
- if (!tremoloData)
- {
- tremoloData = vibratoTable[tremoloTemp];
- }
- else
- {
- if (tremoloData == 1)
- {
- if (ch->n_vibratopos < 0) // PT bug, should've been n_tremolopos
- tremoloData = 255 - (tremoloTemp * 8);
- else
- tremoloData = tremoloTemp * 8;
- }
- else
- {
- tremoloData = 255;
- }
- }
-
- tremoloData = (tremoloData * (ch->n_tremolocmd & 0xF)) / 64;
-
- if (ch->n_tremolopos < 0)
- {
- tremoloData = ch->n_volume - tremoloData;
- if (tremoloData < 0)
- tremoloData = 0;
- }
- else
- {
- tremoloData = ch->n_volume + tremoloData;
- if (tremoloData > 64)
- tremoloData = 64;
- }
-
- paulaSetVolume(ch->n_chanindex, tremoloData);
-
- ch->n_tremolopos += (ch->n_tremolocmd >> 4) * 4;
-}
-
-static void sampleOffset(moduleChannel_t *ch)
-{
- uint16_t newOffset;
-
- if ((ch->n_cmd & 0xFF) > 0)
- ch->n_sampleoffset = ch->n_cmd & 0xFF;
-
- newOffset = ch->n_sampleoffset << 7;
-
- if ((int16_t)newOffset < (int16_t)ch->n_length)
- {
- ch->n_length -= newOffset;
- ch->n_start += newOffset*2;
- }
- else
- {
- ch->n_length = 1;
- }
-}
-
-static void E_Commands(moduleChannel_t *ch)
-{
- uint8_t cmd;
-
- cmd = (ch->n_cmd & 0xF0) >> 4;
- switch (cmd)
- {
- case 0x0: filterOnOff(ch); break;
- case 0x1: finePortaUp(ch); break;
- case 0x2: finePortaDown(ch); break;
- case 0x3: setGlissControl(ch); break;
- case 0x4: setVibratoControl(ch); break;
- case 0x5: setFineTune(ch); break;
- case 0x6: jumpLoop(ch); break;
- case 0x7: setTremoloControl(ch); break;
- case 0x8: karplusStrong(ch); break;
- default: break;
- }
-
- if (editor.muted[ch->n_chanindex])
- return;
-
- switch (cmd)
- {
- case 0x9: retrigNote(ch); break;
- case 0xA: volumeFineUp(ch); break;
- case 0xB: volumeFineDown(ch); break;
- case 0xC: noteCut(ch); break;
- case 0xD: noteDelay(ch); break;
- case 0xE: patternDelay(ch); break;
- case 0xF: funkIt(ch); break;
- default: break;
- }
-}
-
-static void checkMoreEffects(moduleChannel_t *ch)
-{
- switch ((ch->n_cmd & 0xF00) >> 8)
- {
- case 0x9: sampleOffset(ch); break;
- case 0xB: positionJump(ch); break;
-
- case 0xC:
- {
- if (!editor.muted[ch->n_chanindex])
- volumeChange(ch);
- }
- break;
-
- case 0xD: patternBreak(ch); break;
- case 0xE: E_Commands(ch); break;
- case 0xF: setSpeed(ch); break;
-
- default:
- {
- if (!editor.muted[ch->n_chanindex])
- paulaSetPeriod(ch->n_chanindex, ch->n_period);
- }
- break;
- }
-}
-
-static void checkEffects(moduleChannel_t *ch)
-{
- uint8_t effect;
-
- if (editor.muted[ch->n_chanindex])
- return;
-
- updateFunk(ch);
-
- effect = (ch->n_cmd & 0xF00) >> 8;
-
- if ((ch->n_cmd & 0xFFF) > 0)
- {
- switch (effect)
- {
- case 0x0: arpeggio(ch); break;
- case 0x1: portaUp(ch); break;
- case 0x2: portaDown(ch); break;
- case 0x3: tonePortamento(ch); break;
- case 0x4: vibrato(ch); break;
- case 0x5: tonePlusVolSlide(ch); break;
- case 0x6: vibratoPlusVolSlide(ch); break;
- case 0xE: E_Commands(ch); break;
-
- case 0x7:
- {
- paulaSetPeriod(ch->n_chanindex, ch->n_period);
- tremolo(ch);
- }
- break;
-
- case 0xA:
- {
- paulaSetPeriod(ch->n_chanindex, ch->n_period);
- volumeSlide(ch);
- }
- break;
-
- default: paulaSetPeriod(ch->n_chanindex, ch->n_period); break;
- }
- }
-
- if (effect != 0x7)
- paulaSetVolume(ch->n_chanindex, ch->n_volume);
-}
-
-static void setPeriod(moduleChannel_t *ch)
-{
- uint8_t i;
- uint16_t note;
-
- note = ch->n_note & 0xFFF;
- for (i = 0; i < 37; i++)
- {
- // periodTable[36] = 0, so i=36 is safe
- if (note >= periodTable[i])
- break;
- }
-
- // BUG: yes it's 'safe' if i=37 because of padding at the end of period table
- ch->n_period = periodTable[(ch->n_finetune * 37) + i];
-
- if ((ch->n_cmd & 0xFF0) != 0xED0) // no note delay
- {
- if ((ch->n_wavecontrol & 0x04) == 0) ch->n_vibratopos = 0;
- if ((ch->n_wavecontrol & 0x40) == 0) ch->n_tremolopos = 0;
-
- paulaSetLength(ch->n_chanindex, ch->n_length);
- paulaSetData(ch->n_chanindex, ch->n_start);
-
- if (ch->n_start == NULL)
- {
- ch->n_loopstart = NULL;
- paulaSetLength(ch->n_chanindex, 1);
- ch->n_replen = 1;
- }
-
- paulaSetPeriod(ch->n_chanindex, ch->n_period);
-
- if (!editor.muted[ch->n_chanindex])
- {
- paulaStartDMA(ch->n_chanindex);
- updateSpectrumAnalyzer(ch->n_volume, ch->n_period);
- setVUMeterHeight(ch);
- }
- else
- {
- paulaStopDMA(ch->n_chanindex);
- }
- }
-
- checkMoreEffects(ch);
-}
-
-static void checkMetronome(moduleChannel_t *ch, note_t *note)
-{
- if (editor.metroFlag && editor.metroChannel > 0)
- {
- if (ch->n_chanindex == editor.metroChannel-1 && (modEntry->row % editor.metroSpeed) == 0)
- {
- note->sample = 0x1F;
- note->period = (((modEntry->row / editor.metroSpeed) % editor.metroSpeed) == 0) ? 160 : 214;
- }
- }
-}
-
-static void playVoice(moduleChannel_t *ch)
-{
- uint8_t cmd;
- moduleSample_t *s;
- note_t note;
-
- if (ch->n_note == 0 && ch->n_cmd == 0)
- paulaSetPeriod(ch->n_chanindex, ch->n_period);
-
- note = modEntry->patterns[modPattern][(modEntry->row * AMIGA_VOICES) + ch->n_chanindex];
- checkMetronome(ch, ¬e);
-
- ch->n_note = note.period;
- ch->n_cmd = (note.command << 8) | note.param;
-
- if (note.sample >= 1 && note.sample <= 31) // SAFETY BUG FIX: don't handle sample-numbers >31
- {
- ch->n_samplenum = note.sample - 1;
- s = &modEntry->samples[ch->n_samplenum];
-
- ch->n_start = &modEntry->sampleData[s->offset];
- ch->n_finetune = s->fineTune;
- ch->n_volume = s->volume;
- ch->n_length = s->length / 2;
- ch->n_replen = s->loopLength / 2;
-
- if (s->loopStart > 0)
- {
- ch->n_loopstart = ch->n_start + s->loopStart;
- ch->n_wavestart = ch->n_loopstart;
- ch->n_length = (s->loopStart / 2) + ch->n_replen;
- }
- else
- {
- ch->n_loopstart = ch->n_start;
- ch->n_wavestart = ch->n_start;
- }
-
- // non-PT2 quirk
- if (ch->n_length == 0)
- ch->n_loopstart = ch->n_wavestart = &modEntry->sampleData[RESERVED_SAMPLE_OFFSET]; // dummy sample
- }
-
- if ((ch->n_note & 0xFFF) > 0)
- {
- if ((ch->n_cmd & 0xFF0) == 0xE50) // set finetune
- {
- setFineTune(ch);
- setPeriod(ch);
- }
- else
- {
- cmd = (ch->n_cmd & 0xF00) >> 8;
- if (cmd == 3 || cmd == 5)
- {
- setVUMeterHeight(ch);
- setTonePorta(ch);
- checkMoreEffects(ch);
- }
- else if (cmd == 9)
- {
- checkMoreEffects(ch);
- setPeriod(ch);
- }
- else
- {
- setPeriod(ch);
- }
- }
- }
- else
- {
- checkMoreEffects(ch);
- }
-}
-
-static void nextPosition(void)
-{
- modEntry->row = pBreakPosition;
- pBreakPosition = 0;
- posJumpAssert = false;
-
- if (editor.playMode != PLAY_MODE_PATTERN ||
- (editor.currMode == MODE_RECORD && editor.recordMode != RECORD_PATT))
- {
- if (editor.stepPlayEnabled)
- {
- doStopIt(true);
-
- editor.stepPlayEnabled = false;
- editor.stepPlayBackwards = false;
-
- if (!editor.isWAVRendering && !editor.isSMPRendering)
- modEntry->currRow = modEntry->row;
-
- return;
- }
-
- modOrder = (modOrder + 1) & 0x7F;
- if (modOrder >= modEntry->head.orderCount)
- {
- modOrder = 0;
- modHasBeenPlayed = true;
-
- if (config.compoMode) // stop song for music competitions playing
- {
- doStopIt(true);
- turnOffVoices();
-
- modEntry->currOrder = 0;
- modEntry->currRow = modEntry->row = 0;
- modEntry->currPattern = modPattern = (int8_t)modEntry->head.order[0];
-
- editor.currPatternDisp = &modEntry->currPattern;
- editor.currPosEdPattDisp = &modEntry->currPattern;
- editor.currPatternDisp = &modEntry->currPattern;
- editor.currPosEdPattDisp = &modEntry->currPattern;
-
- if (ui.posEdScreenShown)
- ui.updatePosEd = true;
-
- ui.updateSongPos = true;
- ui.updateSongPattern = true;
- ui.updateCurrPattText = true;
- }
- }
-
- modPattern = (int8_t)modEntry->head.order[modOrder];
- if (modPattern > MAX_PATTERNS-1)
- modPattern = MAX_PATTERNS-1;
-
- updateUIPositions = true;
- }
-}
-
-bool intMusic(void)
-{
- uint8_t i;
- uint16_t *patt;
- moduleChannel_t *c;
-
- if (modBPM > 0)
- editor.musicTime += (65536 / modBPM); // for playback counter
-
- if (updateUIPositions)
- {
- updateUIPositions = false;
-
- if (!editor.isWAVRendering && !editor.isSMPRendering)
- {
- if (editor.playMode != PLAY_MODE_PATTERN)
- {
- modEntry->currOrder = modOrder;
- modEntry->currPattern = modPattern;
-
- patt = &modEntry->head.order[modOrder];
- editor.currPatternDisp = patt;
- editor.currPosEdPattDisp = patt;
- editor.currPatternDisp = patt;
- editor.currPosEdPattDisp = patt;
-
- if (ui.posEdScreenShown)
- ui.updatePosEd = true;
-
- ui.updateSongPos = true;
- ui.updateSongPattern = true;
- ui.updateCurrPattText = true;
- }
- }
- }
-
- // PT quirk: CIA refreshes its timer values on the next interrupt, so do the real tempo change here
- if (setBPMFlag != 0)
- {
- modSetTempo(setBPMFlag);
- setBPMFlag = 0;
- }
-
- if (editor.isWAVRendering && editor.modTick == 0)
- editor.rowVisitTable[(modOrder * MOD_ROWS) + modEntry->row] = true;
-
- if (!editor.stepPlayEnabled)
- editor.modTick++;
-
- if (editor.modTick >= editor.modSpeed || editor.stepPlayEnabled)
- {
- editor.modTick = 0;
-
- if (pattDelTime2 == 0)
- {
- for (i = 0; i < AMIGA_VOICES; i++)
- {
- c = &modEntry->channels[i];
-
- playVoice(c);
- paulaSetVolume(i, c->n_volume);
-
- // these take effect after the current DMA cycle is done
- paulaSetData(i, c->n_loopstart);
- paulaSetLength(i, c->n_replen);
- }
- }
- else
- {
- for (i = 0; i < AMIGA_VOICES; i++)
- checkEffects(&modEntry->channels[i]);
- }
-
- if (!editor.isWAVRendering && !editor.isSMPRendering)
- {
- modEntry->currRow = modEntry->row;
- ui.updatePatternData = true;
- }
-
- if (!editor.stepPlayBackwards)
- {
- modEntry->row++;
- modEntry->rowsCounter++;
- }
-
- if (pattDelTime > 0)
- {
- pattDelTime2 = pattDelTime;
- pattDelTime = 0;
- }
-
- if (pattDelTime2 > 0)
- {
- if (--pattDelTime2 > 0)
- modEntry->row--;
- }
-
- if (pBreakFlag)
- {
- modEntry->row = pBreakPosition;
- pBreakPosition = 0;
- pBreakFlag = false;
- }
-
- if (editor.blockMarkFlag)
- ui.updateStatusText = true;
-
- if (editor.stepPlayEnabled)
- {
- doStopIt(true);
-
- modEntry->currRow = modEntry->row & 0x3F;
- ui.updatePatternData = true;
-
- editor.stepPlayEnabled = false;
- editor.stepPlayBackwards = false;
- ui.updatePatternData = true;
-
- return true;
- }
-
- if (modEntry->row >= MOD_ROWS || posJumpAssert)
- {
- if (editor.isSMPRendering)
- modHasBeenPlayed = true;
-
- nextPosition();
- }
-
- if (editor.isWAVRendering && !pattDelTime2 && editor.rowVisitTable[(modOrder * MOD_ROWS) + modEntry->row])
- modHasBeenPlayed = true;
- }
- else
- {
- for (i = 0; i < AMIGA_VOICES; i++)
- checkEffects(&modEntry->channels[i]);
-
- if (posJumpAssert)
- nextPosition();
- }
-
- if ((editor.isSMPRendering || editor.isWAVRendering) && modHasBeenPlayed && editor.modTick == editor.modSpeed-1)
- {
- modHasBeenPlayed = false;
- return false;
- }
-
- return true;
-}
-
-void modSetPattern(uint8_t pattern)
-{
- modPattern = pattern;
- modEntry->currPattern = modPattern;
- ui.updateCurrPattText = true;
-}
-
-void modSetPos(int16_t order, int16_t row)
-{
- int16_t posEdPos;
-
- if (row != -1)
- {
- row = CLAMP(row, 0, 63);
-
- editor.modTick = 0;
- modEntry->row = (int8_t)row;
- modEntry->currRow = (int8_t)row;
- }
-
- if (order != -1)
- {
- if (order >= 0)
- {
- modOrder = order;
- modEntry->currOrder = order;
- ui.updateSongPos = true;
-
- if (editor.currMode == MODE_PLAY && editor.playMode == PLAY_MODE_NORMAL)
- {
- modPattern = (int8_t)modEntry->head.order[order];
- if (modPattern > MAX_PATTERNS-1)
- modPattern = MAX_PATTERNS-1;
-
- modEntry->currPattern = modPattern;
- ui.updateCurrPattText = true;
- }
-
- ui.updateSongPattern = true;
- editor.currPatternDisp = &modEntry->head.order[modOrder];
-
- posEdPos = modEntry->currOrder;
- if (posEdPos > modEntry->head.orderCount-1)
- posEdPos = modEntry->head.orderCount-1;
-
- editor.currPosEdPattDisp = &modEntry->head.order[posEdPos];
-
- if (ui.posEdScreenShown)
- ui.updatePosEd = true;
- }
- }
-
- ui.updatePatternData = true;
-
- if (editor.blockMarkFlag)
- ui.updateStatusText = true;
-}
-
-void modSetTempo(uint16_t bpm)
-{
- uint32_t smpsPerTick;
-
- if (bpm < 32)
- return;
-
- const bool audioWasntLocked = !audio.locked;
- if (audioWasntLocked)
- lockAudio();
-
- modBPM = bpm;
- if (!editor.isSMPRendering && !editor.isWAVRendering)
- {
- modEntry->currBPM = bpm;
- ui.updateSongBPM = true;
- }
-
- bpm -= 32; // 32..255 -> 0..223
-
- if (editor.isSMPRendering)
- smpsPerTick = editor.pat2SmpHQ ? audio.bpmTab28kHz[bpm] : audio.bpmTab22kHz[bpm];
- else if (editor.isWAVRendering)
- smpsPerTick = audio.bpmTabMod2Wav[bpm];
- else
- smpsPerTick = audio.bpmTab[bpm];
-
- mixerSetSamplesPerTick(smpsPerTick);
-
- if (audioWasntLocked)
- unlockAudio();
-}
-
-void modStop(void)
-{
- moduleChannel_t *ch;
-
- editor.songPlaying = false;
- turnOffVoices();
-
- for (uint8_t i = 0; i < AMIGA_VOICES; i++)
- {
- ch = &modEntry->channels[i];
-
- ch->n_wavecontrol = 0;
- ch->n_glissfunk = 0;
- ch->n_finetune = 0;
- ch->n_loopcount = 0;
- }
-
- pBreakFlag = false;
- pattDelTime = 0;
- pattDelTime2 = 0;
- pBreakPosition = 0;
- posJumpAssert = false;
- modHasBeenPlayed = true;
-}
-
-void playPattern(int8_t startRow)
-{
- modEntry->row = startRow & 0x3F;
- modEntry->currRow = modEntry->row;
- editor.modTick = 0;
- editor.playMode = PLAY_MODE_PATTERN;
- editor.currMode = MODE_PLAY;
- editor.didQuantize = false;
-
- if (!editor.stepPlayEnabled)
- pointerSetMode(POINTER_MODE_PLAY, DO_CARRY);
-
- editor.songPlaying = true;
- mixerClearSampleCounter();
-}
-
-void incPatt(void)
-{
- if (++modPattern > MAX_PATTERNS-1)
- modPattern = 0;
-
- modEntry->currPattern = modPattern;
-
- ui.updatePatternData = true;
- ui.updateCurrPattText = true;
-}
-
-void decPatt(void)
-{
- if (--modPattern < 0)
- modPattern = MAX_PATTERNS - 1;
-
- modEntry->currPattern = modPattern;
-
- ui.updatePatternData = true;
- ui.updateCurrPattText = true;
-}
-
-void modPlay(int16_t patt, int16_t order, int8_t row)
-{
- uint8_t oldPlayMode, oldMode;
-
- doStopIt(false);
- turnOffVoices();
- mixerClearSampleCounter();
-
- if (row != -1)
- {
- if (row >= 0 && row <= 63)
- {
- modEntry->row = row;
- modEntry->currRow = row;
- }
- }
- else
- {
- modEntry->row = 0;
- modEntry->currRow = 0;
- }
-
- if (editor.playMode != PLAY_MODE_PATTERN)
- {
- if (modOrder >= modEntry->head.orderCount)
- {
- modOrder = 0;
- modEntry->currOrder = 0;
- }
-
- if (order >= 0 && order < modEntry->head.orderCount)
- {
- modOrder = order;
- modEntry->currOrder = order;
- }
-
- if (order >= modEntry->head.orderCount)
- {
- modOrder = 0;
- modEntry->currOrder = 0;
- }
- }
-
- if (patt >= 0 && patt <= MAX_PATTERNS-1)
- modEntry->currPattern = modPattern = (int8_t)patt;
- else
- modEntry->currPattern = modPattern = (int8_t)modEntry->head.order[modOrder];
-
- editor.currPatternDisp = &modEntry->head.order[modOrder];
- editor.currPosEdPattDisp = &modEntry->head.order[modOrder];
-
- oldPlayMode = editor.playMode;
- oldMode = editor.currMode;
-
- editor.playMode = oldPlayMode;
- editor.currMode = oldMode;
-
- editor.modTick = editor.modSpeed;
- modHasBeenPlayed = false;
- editor.songPlaying = true;
- editor.didQuantize = false;
- editor.musicTime = 0;
-
- if (!editor.isSMPRendering && !editor.isWAVRendering)
- {
- ui.updateSongPos = true;
- ui.updatePatternData = true;
- ui.updateSongPattern = true;
- ui.updateCurrPattText = true;
- }
-}
-
-void clearSong(void)
-{
- uint8_t i;
- moduleChannel_t *ch;
-
- if (modEntry != NULL)
- {
- memset(modEntry->head.order, 0, sizeof (modEntry->head.order));
- memset(modEntry->head.moduleTitle, 0, sizeof (modEntry->head.moduleTitle));
-
- editor.muted[0] = false;
- editor.muted[1] = false;
- editor.muted[2] = false;
- editor.muted[3] = false;
-
- editor.f6Pos = 0;
- editor.f7Pos = 16;
- editor.f8Pos = 32;
- editor.f9Pos = 48;
- editor.f10Pos = 63;
-
- editor.musicTime = 0;
-
- editor.metroFlag = false;
- editor.currSample = 0;
- editor.editMoveAdd = 1;
- editor.blockMarkFlag = false;
- editor.swapChannelFlag = false;
-
- modEntry->head.orderCount = 1;
-
- for (i = 0; i < MAX_PATTERNS; i++)
- memset(modEntry->patterns[i], 0, (MOD_ROWS * AMIGA_VOICES) * sizeof (note_t));
-
- for (i = 0; i < AMIGA_VOICES; i++)
- {
- ch = &modEntry->channels[i];
-
- ch->n_wavecontrol = 0;
- ch->n_glissfunk = 0;
- ch->n_finetune = 0;
- ch->n_loopcount = 0;
- }
-
- modSetPos(0, 0); // this also refreshes pattern data
-
- modEntry->currOrder = 0;
- modEntry->currPattern = 0;
- editor.currPatternDisp = &modEntry->head.order[0];
- editor.currPosEdPattDisp = &modEntry->head.order[0];
-
- modSetTempo(editor.initialTempo);
- modSetSpeed(editor.initialSpeed);
-
- setLEDFilter(false); // real PT doesn't do this there, but that's insane
- updateCurrSample();
-
- ui.updateSongSize = true;
- renderMuteButtons();
- updateWindowTitle(MOD_IS_MODIFIED);
- }
-}
-
-void clearSamples(void)
-{
- moduleSample_t *s;
-
- if (modEntry == NULL)
- return;
-
- for (uint8_t i = 0; i < MOD_SAMPLES; i++)
- {
- s = &modEntry->samples[i];
-
- s->fineTune = 0;
- s->length = 0;
- s->loopLength = 2;
- s->loopStart = 0;
- s->volume = 0;
-
- memset(s->text, 0, sizeof (s->text));
- }
-
- memset(modEntry->sampleData, 0, (MOD_SAMPLES + 1) * MAX_SAMPLE_LEN);
-
- editor.currSample = 0;
- editor.keypadSampleOffset = 0;
- editor.sampleZero = false;
- ui.editOpScreenShown = false;
- ui.aboutScreenShown = false;
- editor.blockMarkFlag = false;
-
- editor.samplePos = 0;
- updateCurrSample();
-
- updateWindowTitle(MOD_IS_MODIFIED);
-}
-
-void clearAll(void)
-{
- if (modEntry != NULL)
- {
- clearSamples();
- clearSong();
- }
-}
-
-void modFree(void)
-{
- uint8_t i;
-
- if (modEntry == NULL)
- return; // not allocated
-
- const bool audioWasntLocked = !audio.locked;
- if (audioWasntLocked)
- lockAudio();
-
- turnOffVoices();
-
- for (i = 0; i < MAX_PATTERNS; i++)
- {
- if (modEntry->patterns[i] != NULL)
- free(modEntry->patterns[i]);
- }
-
- if (modEntry->sampleData != NULL)
- free(modEntry->sampleData);
-
- free(modEntry);
- modEntry = NULL;
-
- if (audioWasntLocked)
- unlockAudio();
-}
-
-void restartSong(void) // for the beginning of MOD2WAV/PAT2SMP
-{
- if (editor.songPlaying)
- modStop();
-
- editor.playMode = PLAY_MODE_NORMAL;
- editor.blockMarkFlag = false;
- audio.forceMixerOff = true;
-
- modEntry->row = 0;
- modEntry->currRow = 0;
- modEntry->rowsCounter = 0;
-
- memset(editor.rowVisitTable, 0, MOD_ORDERS * MOD_ROWS); // for MOD2WAV
-
- if (editor.isSMPRendering)
- {
- modPlay(DONT_SET_PATTERN, DONT_SET_ORDER, DONT_SET_ROW);
- }
- else
- {
- modEntry->currSpeed = 6;
- modEntry->currBPM = 125;
- modSetSpeed(6);
- modSetTempo(125);
-
- modPlay(DONT_SET_PATTERN, 0, 0);
- }
-}
-
-// this function is meant for the end of MOD2WAV/PAT2SMP
-void resetSong(void) // only call this after storeTempVariables() has been called!
-{
- modStop();
-
- editor.songPlaying = false;
- editor.playMode = PLAY_MODE_NORMAL;
- editor.currMode = MODE_IDLE;
-
- turnOffVoices();
-
- memset((int8_t *)editor.vuMeterVolumes,0, sizeof (editor.vuMeterVolumes));
- memset((int8_t *)editor.realVuMeterVolumes, 0, sizeof (editor.realVuMeterVolumes));
- memset((int8_t *)editor.spectrumVolumes, 0, sizeof (editor.spectrumVolumes));
-
- memset(modEntry->channels, 0, sizeof (modEntry->channels));
- for (uint8_t i = 0; i < AMIGA_VOICES; i++)
- modEntry->channels[i].n_chanindex = i;
-
- modOrder = oldOrder;
- modPattern = (int8_t)oldPattern;
-
- modEntry->row = oldRow;
- modEntry->currRow = oldRow;
- modEntry->currBPM = oldBPM;
- modEntry->currOrder = oldOrder;
- modEntry->currPattern = oldPattern;
-
- editor.currPosDisp = &modEntry->currOrder;
- editor.currEditPatternDisp = &modEntry->currPattern;
- editor.currPatternDisp = &modEntry->head.order[modEntry->currOrder];
- editor.currPosEdPattDisp = &modEntry->head.order[modEntry->currOrder];
-
- modSetSpeed(oldSpeed);
- modSetTempo(oldBPM);
-
- doStopIt(true);
-
- editor.modTick = 0;
- modHasBeenPlayed = false;
- audio.forceMixerOff = false;
-}
--- /dev/null
+++ b/src/pt2_module_loader.c
@@ -1,0 +1,1234 @@
+// for finding memory leaks in debug mode with Visual Studio
+#if defined _DEBUG && defined _MSC_VER
+#include <crtdbg.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#ifdef _WIN32
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "pt2_mouse.h"
+#include "pt2_header.h"
+#include "pt2_sampler.h"
+#include "pt2_textout.h"
+#include "pt2_audio.h"
+#include "pt2_helpers.h"
+#include "pt2_visuals.h"
+#include "pt2_unicode.h"
+#include "pt2_module_loader.h"
+#include "pt2_sample_loader.h"
+#include "pt2_config.h"
+
+typedef struct mem_t
+{
+ bool _eof;
+ uint8_t *_ptr, *_base;
+ uint32_t _cnt, _bufsiz;
+} MEMFILE;
+
+static bool oldAutoPlay;
+static char oldFullPath[(PATH_MAX * 2) + 2];
+static uint32_t oldFullPathLen;
+
+static MEMFILE *mopen(const uint8_t *src, uint32_t length);
+static void mclose(MEMFILE **buf);
+static int32_t mgetc(MEMFILE *buf);
+static size_t mread(void *buffer, size_t size, size_t count, MEMFILE *buf);
+static void mseek(MEMFILE *buf, int32_t offset, int32_t whence);
+static void mrewind(MEMFILE *buf);
+static uint8_t ppdecrunch(uint8_t *src, uint8_t *dst, uint8_t *offsetLens, uint32_t srcLen, uint32_t dstLen, uint8_t skipBits);
+
+void showSongUnsavedAskBox(int8_t askScreenType)
+{
+ ui.askScreenShown = true;
+ ui.askScreenType = askScreenType;
+
+ pointerSetMode(POINTER_MODE_MSG1, NO_CARRY);
+ setStatusMessage("SONG IS UNSAVED !", NO_CARRY);
+ renderAskDialog();
+}
+
+#define IS_ID(s, b) !strncmp(s, b, 4)
+
+static uint8_t getModType(uint8_t *numChannels, const char *id)
+{
+ *numChannels = 4;
+
+ if (IS_ID("M.K.", id) || IS_ID("M!K!", id) || IS_ID("NSMS", id) || IS_ID("LARD", id) || IS_ID("PATT", id))
+ {
+ return FORMAT_MK; // ProTracker (or compatible)
+ }
+ else if (IS_ID("FLT4", id))
+ {
+ return FORMAT_FLT; // Startrekker (4 channels)
+ }
+ else if (IS_ID("N.T.", id))
+ {
+ return FORMAT_NT; // NoiseTracker
+ }
+ else if (IS_ID("M&K!", id) || IS_ID("FEST", id))
+ {
+ return FORMAT_HMNT; // His Master's NoiseTracker
+ }
+ else if (id[1] == 'C' && id[2] == 'H' && id[3] == 'N')
+ {
+ *numChannels = id[0] - '0';
+ return FORMAT_FT2; // Fasttracker II 1..9 channels (or other trackers)
+ }
+
+ return FORMAT_UNKNOWN; // may be The Ultimate Soundtracker (set to FORMAT_STK later)
+}
+
+// converts zeroes to spaces in a string, up until the last zero found
+static void fixZeroesInString(char *str, uint32_t maxLength)
+{
+ int32_t i;
+
+ for (i = maxLength-1; i >= 0; i--)
+ {
+ if (str[i] != '\0')
+ break;
+ }
+
+ // convert zeroes to spaces
+ if (i > 0)
+ {
+ for (int32_t j = 0; j < i; j++)
+ {
+ if (str[j] == '\0')
+ str[j] = ' ';
+ }
+ }
+}
+
+static uint8_t *unpackPPModule(FILE *f, uint32_t *filesize)
+{
+ uint8_t *modBuffer, ppCrunchData[4], *ppBuffer;
+ uint32_t ppPackLen, ppUnpackLen;
+
+ ppPackLen = *filesize;
+ if ((ppPackLen & 3) || ppPackLen <= 12)
+ {
+ displayErrorMsg("POWERPACKER ERROR");
+ return NULL;
+ }
+
+ ppBuffer = (uint8_t *)malloc(ppPackLen);
+ if (ppBuffer == NULL)
+ {
+ statusOutOfMemory();
+ return NULL;
+ }
+
+ fseek(f, ppPackLen-4, SEEK_SET);
+
+ ppCrunchData[0] = (uint8_t)fgetc(f);
+ ppCrunchData[1] = (uint8_t)fgetc(f);
+ ppCrunchData[2] = (uint8_t)fgetc(f);
+ ppCrunchData[3] = (uint8_t)fgetc(f);
+
+ ppUnpackLen = (ppCrunchData[0] << 16) | (ppCrunchData[1] << 8) | ppCrunchData[2];
+
+ modBuffer = (uint8_t *)malloc(ppUnpackLen);
+ if (modBuffer == NULL)
+ {
+ free(ppBuffer);
+ statusOutOfMemory();
+ return NULL;
+ }
+
+ rewind(f);
+ fread(ppBuffer, 1, ppPackLen, f);
+ fclose(f);
+
+ if (!ppdecrunch(ppBuffer+8, modBuffer, ppBuffer+4, ppPackLen-12, ppUnpackLen, ppCrunchData[3]))
+ {
+ free(ppBuffer);
+ displayErrorMsg("POWERPACKER ERROR");
+ return NULL;
+ }
+
+ free(ppBuffer);
+ *filesize = ppUnpackLen;
+ return modBuffer;
+}
+
+module_t *modLoad(UNICHAR *fileName)
+{
+ bool mightBeSTK, lateSTKVerFlag, veryLateSTKVerFlag;
+ char modID[4], tmpChar;
+ int8_t numSamples;
+ uint8_t bytes[4], restartPos, modFormat;
+ uint8_t *modBuffer, numChannels;
+ int32_t i, j, k, loopStart, loopLength, loopOverflowVal, numPatterns;
+ uint32_t powerPackerID, filesize;
+ FILE *f;
+ MEMFILE *m;
+ module_t *newMod;
+ moduleSample_t *s;
+ note_t *note;
+
+ veryLateSTKVerFlag = false; // "DFJ SoundTracker III" and later
+ lateSTKVerFlag = false; // "TJC SoundTracker II" and later
+ mightBeSTK = false;
+
+ m = NULL;
+ f = NULL;
+ modBuffer = NULL;
+
+ newMod = (module_t *)calloc(1, sizeof (module_t));
+ if (newMod == NULL)
+ {
+ statusOutOfMemory();
+ goto modLoadError;
+ }
+
+ f = UNICHAR_FOPEN(fileName, "rb");
+ if (f == NULL)
+ {
+ displayErrorMsg("FILE I/O ERROR !");
+ goto modLoadError;
+ }
+
+ fseek(f, 0, SEEK_END);
+ filesize = ftell(f);
+ rewind(f);
+
+ // check if mod is a powerpacker mod
+ fread(&powerPackerID, 4, 1, f);
+ if (powerPackerID == 0x30325850) // "PX20"
+ {
+ displayErrorMsg("ENCRYPTED MOD !");
+ goto modLoadError;
+ }
+ else if (powerPackerID == 0x30325050) // "PP20"
+ {
+ modBuffer = unpackPPModule(f, &filesize);
+ if (modBuffer == NULL)
+ goto modLoadError; // error msg is set in unpackPPModule()
+ }
+ else
+ {
+ modBuffer = (uint8_t *)malloc(filesize);
+ if (modBuffer == NULL)
+ {
+ statusOutOfMemory();
+ goto modLoadError;
+ }
+
+ fseek(f, 0, SEEK_SET);
+ fread(modBuffer, 1, filesize, f);
+ fclose(f);
+ }
+
+ // smallest and biggest possible PT .MOD
+ if (filesize < 2108 || filesize > 4195326)
+ {
+ displayErrorMsg("NOT A MOD FILE !");
+ goto modLoadError;
+ }
+
+ // Use MEMFILE functions on module buffer (similar to FILE functions)
+
+ m = mopen(modBuffer, filesize);
+ if (m == NULL)
+ {
+ displayErrorMsg("FILE I/O ERROR !");
+ goto modLoadError;
+ }
+
+ // check magic ID
+ memset(modID, 0, 4); // in case mread fails
+ mseek(m, 1080, SEEK_SET);
+ mread(modID, 1, 4, m);
+
+ modFormat = getModType(&numChannels, modID);
+ if (numChannels == 0 || numChannels > AMIGA_VOICES)
+ {
+ displayErrorMsg("UNSUPPORTED MOD !");
+ goto modLoadError;
+ }
+
+ if (modFormat == FORMAT_UNKNOWN)
+ mightBeSTK = true;
+
+ mrewind(m);
+ mread(newMod->header.name, 1, 20, m);
+ newMod->header.name[20] = '\0';
+
+ // convert illegal song name characters to space
+ for (i = 0; i < 20; i++)
+ {
+ tmpChar = newMod->header.name[i];
+ if ((tmpChar < ' ' || tmpChar > '~') && tmpChar != '\0')
+ tmpChar = ' ';
+
+ newMod->header.name[i] = tmpChar;
+ }
+
+ fixZeroesInString(newMod->header.name, 20);
+
+ // read sample headers
+ s = newMod->samples;
+ for (i = 0; i < MOD_SAMPLES; i++, s++)
+ {
+ if (mightBeSTK && i >= 15) // skip reading sample headers past sample slot 15 in STK/UST modules
+ {
+ s->loopLength = 2; // this be set though
+ continue;
+ }
+
+ mread(s->text, 1, 22, m);
+ s->text[22] = '\0';
+
+ if (modFormat == FORMAT_HMNT)
+ {
+ // most of "His Master's Noisetracker" songs have junk sample names, so let's wipe it.
+ memset(s->text, 0, 22);
+ }
+ else
+ {
+ // convert illegal sample name characters to space
+ for (j = 0; j < 22; j++)
+ {
+ tmpChar = s->text[j];
+ if ((tmpChar < ' ' || tmpChar > '~') && tmpChar != '\0')
+ tmpChar = ' ';
+
+ s->text[j] = tmpChar;
+ }
+
+ fixZeroesInString(s->text, 22);
+ }
+
+ s->length = (uint16_t)(((mgetc(m) << 8) | mgetc(m)) * 2);
+
+ /* Only late versions of Ultimate SoundTracker could have samples larger than 9999 bytes.
+ ** If found, we know for sure that this is a late STK module.
+ */
+ if (mightBeSTK && s->length > 9999)
+ lateSTKVerFlag = true;
+
+ if (modFormat == FORMAT_HMNT) // finetune in "His Master's NoiseTracker" is different
+ s->fineTune = (uint8_t)((-mgetc(m) & 0x1F) / 2); // one more bit of precision, + inverted
+ else
+ s->fineTune = (uint8_t)mgetc(m) & 0xF;
+
+ if (mightBeSTK)
+ s->fineTune = 0; // this is high byte of volume in STK/UST (has no finetune), set to zero
+
+ s->volume = (int8_t)mgetc(m);
+ if ((uint8_t)s->volume > 64)
+ s->volume = 64;
+
+ loopStart = ((mgetc(m) << 8) | mgetc(m)) * 2;
+ loopLength = ((mgetc(m) << 8) | mgetc(m)) * 2;
+
+ if (loopLength < 2)
+ loopLength = 2; // fixes empty samples in .MODs saved from FT2
+
+ // we don't support samples bigger than 65534 bytes, disable uncompatible loops
+ if (loopStart > MAX_SAMPLE_LEN || loopStart+loopLength > MAX_SAMPLE_LEN)
+ {
+ s->loopStart = 0;
+ s->loopLength = 2;
+ }
+ else
+ {
+ s->loopStart = (uint16_t)loopStart;
+ s->loopLength = (uint16_t)loopLength;
+ }
+
+ // in The Ultimate SoundTracker, sample loop start is in bytes, not words
+ if (mightBeSTK)
+ s->loopStart /= 2;
+
+ // fix for poorly converted STK (< v2.5) -> PT/NT modules (FIXME: Worth keeping or not?)
+ if (!mightBeSTK && s->loopLength > 2 && s->loopStart+s->loopLength > s->length)
+ {
+ if ((s->loopStart/2) + s->loopLength <= s->length)
+ s->loopStart /= 2;
+ }
+ }
+
+ newMod->header.numOrders = (uint8_t)mgetc(m);
+
+ if (modFormat == FORMAT_MK && newMod->header.numOrders == 129)
+ newMod->header.numOrders = 127; // fixes a specific copy of beatwave.mod
+
+ if (newMod->header.numOrders > 129)
+ {
+ displayErrorMsg("NOT A MOD FILE !");
+ goto modLoadError;
+ }
+
+ if (newMod->header.numOrders == 0)
+ {
+ displayErrorMsg("NOT A MOD FILE !");
+ goto modLoadError;
+ }
+
+ restartPos = (uint8_t)mgetc(m);
+ if (mightBeSTK && restartPos > 220)
+ {
+ displayErrorMsg("NOT A MOD FILE !");
+ goto modLoadError;
+ }
+
+ newMod->header.initialTempo = 125;
+ if (mightBeSTK)
+ {
+ /* If we're still here at this point and the mightBeSTK flag is set,
+ ** then it's most likely a proper The Ultimate SoundTracker (STK/UST) module.
+ */
+ modFormat = FORMAT_STK;
+
+ if (restartPos == 0)
+ restartPos = 120;
+
+ // jjk55.mod by Jesper Kyd has a bogus STK tempo value that should be ignored
+ if (!strcmp("jjk55", newMod->header.name))
+ restartPos = 120;
+
+ // the "restart pos" field in STK is the inital tempo (must be converted to BPM first)
+ if (restartPos != 120) // 120 is a special case and means 50Hz (125BPM)
+ {
+ if (restartPos > 220)
+ restartPos = 220;
+
+ // convert UST tempo to BPM
+ uint16_t ciaPeriod = (240 - restartPos) * 122;
+ double dHz = (double)CIA_PAL_CLK / ciaPeriod;
+ int32_t BPM = (int32_t)((dHz * (125.0 / 50.0)) + 0.5);
+
+ newMod->header.initialTempo = (uint16_t)BPM;
+ }
+ }
+
+ // read orders and count number of patterns
+ numPatterns = 0;
+ for (i = 0; i < MOD_ORDERS; i++)
+ {
+ newMod->header.order[i] = (int16_t)mgetc(m);
+ if (newMod->header.order[i] > numPatterns)
+ numPatterns = newMod->header.order[i];
+ }
+ numPatterns++;
+
+ if (numPatterns > MAX_PATTERNS)
+ {
+ displayErrorMsg("UNSUPPORTED MOD !");
+ goto modLoadError;
+ }
+
+ // skip magic ID (The Ultimate SoundTracker MODs doesn't have it)
+ if (modFormat != FORMAT_STK)
+ mseek(m, 4, SEEK_CUR);
+
+ // allocate 100 patterns
+ for (i = 0; i < MAX_PATTERNS; i++)
+ {
+ newMod->patterns[i] = (note_t *)calloc(MOD_ROWS * AMIGA_VOICES, sizeof (note_t));
+ if (newMod->patterns[i] == NULL)
+ {
+ statusOutOfMemory();
+ goto modLoadError;
+ }
+ }
+
+ // load pattern data
+ for (i = 0; i < numPatterns; i++)
+ {
+ note = newMod->patterns[i];
+ for (j = 0; j < MOD_ROWS; j++)
+ {
+ for (k = 0; k < numChannels; k++, note++)
+ {
+ mread(bytes, 1, 4, m);
+
+ note->period = ((bytes[0] & 0x0F) << 8) | bytes[1];
+ note->sample = (bytes[0] & 0xF0) | (bytes[2] >> 4);
+ note->command = bytes[2] & 0x0F;
+ note->param = bytes[3];
+
+ if (modFormat == FORMAT_STK)
+ {
+ if (note->command == 0xC || note->command == 0xD || note->command == 0xE)
+ {
+ // "TJC SoundTracker II" and later
+ lateSTKVerFlag = true;
+ }
+
+ if (note->command == 0xF)
+ {
+ // "DFJ SoundTracker III" and later
+ lateSTKVerFlag = true;
+ veryLateSTKVerFlag = true;
+ }
+ }
+ }
+
+ if (numChannels < AMIGA_VOICES)
+ note += AMIGA_VOICES-numChannels;
+ }
+ }
+
+ // pattern command conversion for non-PT formats
+ if (modFormat == FORMAT_STK || modFormat == FORMAT_FT2 || modFormat == FORMAT_NT || modFormat == FORMAT_HMNT || modFormat == FORMAT_FLT)
+ {
+ for (i = 0; i < numPatterns; i++)
+ {
+ note = newMod->patterns[i];
+ for (j = 0; j < MOD_ROWS*4; j++, note++)
+ {
+ if (modFormat == FORMAT_NT || modFormat == FORMAT_HMNT)
+ {
+ // any Dxx == D00 in NT/HMNT
+ if (note->command == 0xD)
+ note->param = 0;
+
+ // effect F with param 0x00 does nothing in NT/HMNT
+ if (note->command == 0xF && note->param == 0)
+ note->command = 0;
+ }
+ else if (modFormat == FORMAT_FLT) // Startrekker (4 channels)
+ {
+ if (note->command == 0xE) // remove unsupported "assembly macros" command
+ {
+ note->command = 0;
+ note->param = 0;
+ }
+
+ // Startrekker is always in vblank mode, and limits speed to 0x1F
+ if (note->command == 0xF && note->param > 0x1F)
+ note->param = 0x1F;
+ }
+ else if (modFormat == FORMAT_STK)
+ {
+ // convert STK effects to PT effects
+
+ if (!lateSTKVerFlag)
+ {
+ // old SoundTracker 1.x commands
+
+ if (note->command == 1)
+ {
+ // arpeggio
+ note->command = 0;
+ }
+ else if (note->command == 2)
+ {
+ // pitch slide
+ if (note->param & 0xF0)
+ {
+ // pitch slide down
+ note->command = 2;
+ note->param >>= 4;
+ }
+ else if (note->param & 0x0F)
+ {
+ // pitch slide up
+ note->command = 1;
+ }
+ }
+ }
+ else
+ {
+ // "DFJ SoundTracker II" or later
+
+ // TODO: This needs more detection and is NOT correct!
+ if (note->command == 0xD)
+ {
+ if (veryLateSTKVerFlag) // "DFJ SoundTracker III" or later
+ {
+ // pattern break w/ no param (param must be cleared to fix some songs)
+ note->param = 0;
+ }
+ else
+ {
+ // volume slide
+ note->command = 0xA;
+ }
+ }
+ }
+
+ // effect F with param 0x00 does nothing in UST/STK (I think?)
+ if (note->command == 0xF && note->param == 0)
+ note->command = 0;
+ }
+
+ // remove sample-trashing effects that were only present in ProTracker
+
+ // remove E8x (Karplus-Strong in ProTracker)
+ if (note->command == 0xE && (note->param >> 4) == 0x8)
+ {
+ note->command = 0;
+ note->param = 0;
+ }
+
+ // remove EFx (Invert Loop in ProTracker)
+ if (note->command == 0xE && (note->param >> 4) == 0xF)
+ {
+ note->command = 0;
+ note->param = 0;
+ }
+ }
+ }
+ }
+
+ // allocate sample data (+2 sample slots for overflow safety (Paula and scopes))
+ newMod->sampleData = (int8_t *)calloc(MOD_SAMPLES + 2, MAX_SAMPLE_LEN);
+ if (newMod->sampleData == NULL)
+ {
+ statusOutOfMemory();
+ goto modLoadError;
+ }
+
+ // set sample data offsets (sample data = one huge buffer to rule them all)
+ for (i = 0; i < MOD_SAMPLES; i++)
+ newMod->samples[i].offset = MAX_SAMPLE_LEN * i;
+
+ // load sample data
+ numSamples = (modFormat == FORMAT_STK) ? 15 : 31;
+ s = newMod->samples;
+ for (i = 0; i < numSamples; i++, s++)
+ {
+ uint32_t bytesToSkip = 0;
+
+ /* For Ultimate SoundTracker modules, only the loop area of a looped sample is played.
+ ** Skip loading of eventual data present before loop start.
+ */
+ if (modFormat == FORMAT_STK && s->loopStart > 0 && s->loopLength < s->length)
+ {
+ s->length -= s->loopStart;
+ mseek(m, s->loopStart, SEEK_CUR);
+ s->loopStart = 0;
+ }
+
+ /* We don't support loading samples bigger than 65534 bytes in our PT2 clone,
+ ** so clamp length and skip overflown data.
+ */
+ if (s->length > MAX_SAMPLE_LEN)
+ {
+ s->length = MAX_SAMPLE_LEN;
+ bytesToSkip = s->length - MAX_SAMPLE_LEN;
+ }
+
+ // For Ultimate SoundTracker modules, don't load sample data after loop end
+ uint16_t loopEnd = s->loopStart + s->loopLength;
+ if (modFormat == FORMAT_STK && loopEnd > 2 && s->length > loopEnd)
+ {
+ bytesToSkip += s->length-loopEnd;
+ s->length = loopEnd;
+ }
+
+ mread(&newMod->sampleData[s->offset], 1, s->length, m);
+
+ if (bytesToSkip > 0)
+ mseek(m, bytesToSkip, SEEK_CUR);
+
+ // clear first two bytes of non-looping samples to prevent beep after sample has been played
+ if (s->length >= 2 && loopEnd <= 2)
+ {
+ newMod->sampleData[s->offset+0] = 0;
+ newMod->sampleData[s->offset+1] = 0;
+ }
+
+ // some modules are broken like this, adjust sample length if possible (this is ok if we have room)
+ if (s->length > 0 && s->loopLength > 2 && s->loopStart+s->loopLength > s->length)
+ {
+ loopOverflowVal = (s->loopStart + s->loopLength) - s->length;
+ if (s->length+loopOverflowVal <= MAX_SAMPLE_LEN)
+ {
+ s->length = (uint16_t)(s->length + loopOverflowVal); // this is safe, we're allocating 65534 bytes per sample slot
+ }
+ else
+ {
+ s->loopStart = 0;
+ s->loopLength = 2;
+ }
+ }
+ }
+
+ mclose(&m);
+ free(modBuffer);
+
+ for (i = 0; i < AMIGA_VOICES; i++)
+ newMod->channels[i].n_chanindex = (int8_t)i;
+
+ return newMod;
+
+modLoadError:
+ if (m != NULL)
+ mclose(&m);
+
+ if (modBuffer != NULL)
+ free(modBuffer);
+
+ if (newMod != NULL)
+ {
+ for (i = 0; i < MAX_PATTERNS; i++)
+ {
+ if (newMod->patterns[i] != NULL)
+ free(newMod->patterns[i]);
+ }
+
+ free(newMod);
+ }
+
+ return NULL;
+}
+
+static MEMFILE *mopen(const uint8_t *src, uint32_t length)
+{
+ MEMFILE *b;
+
+ if (src == NULL || length == 0)
+ return NULL;
+
+ b = (MEMFILE *)malloc(sizeof (MEMFILE));
+ if (b == NULL)
+ return NULL;
+
+ b->_base = (uint8_t *)src;
+ b->_ptr = (uint8_t *)src;
+ b->_cnt = length;
+ b->_bufsiz = length;
+ b->_eof = false;
+
+ return b;
+}
+
+static void mclose(MEMFILE **buf)
+{
+ if (*buf != NULL)
+ {
+ free(*buf);
+ *buf = NULL;
+ }
+}
+
+static int32_t mgetc(MEMFILE *buf)
+{
+ int32_t b;
+
+ if (buf == NULL || buf->_ptr == NULL || buf->_cnt <= 0)
+ return 0;
+
+ b = *buf->_ptr;
+
+ buf->_cnt--;
+ buf->_ptr++;
+
+ if (buf->_cnt <= 0)
+ {
+ buf->_ptr = buf->_base + buf->_bufsiz;
+ buf->_cnt = 0;
+ buf->_eof = true;
+ }
+
+ return (int32_t)b;
+}
+
+static size_t mread(void *buffer, size_t size, size_t count, MEMFILE *buf)
+{
+ int32_t pcnt;
+ size_t wrcnt;
+
+ if (buf == NULL || buf->_ptr == NULL)
+ return 0;
+
+ wrcnt = size * count;
+ if (size == 0 || buf->_eof)
+ return 0;
+
+ pcnt = (buf->_cnt > (uint32_t)wrcnt) ? (uint32_t)wrcnt : buf->_cnt;
+ memcpy(buffer, buf->_ptr, pcnt);
+
+ buf->_cnt -= pcnt;
+ buf->_ptr += pcnt;
+
+ if (buf->_cnt <= 0)
+ {
+ buf->_ptr = buf->_base + buf->_bufsiz;
+ buf->_cnt = 0;
+ buf->_eof = true;
+ }
+
+ return pcnt / size;
+}
+
+static void mseek(MEMFILE *buf, int32_t offset, int32_t whence)
+{
+ if (buf == NULL)
+ return;
+
+ if (buf->_base)
+ {
+ switch (whence)
+ {
+ case SEEK_SET: buf->_ptr = buf->_base + offset; break;
+ case SEEK_CUR: buf->_ptr += offset; break;
+ case SEEK_END: buf->_ptr = buf->_base + buf->_bufsiz + offset; break;
+ default: break;
+ }
+
+ buf->_eof = false;
+ if (buf->_ptr >= buf->_base+buf->_bufsiz)
+ {
+ buf->_ptr = buf->_base + buf->_bufsiz;
+ buf->_eof = true;
+ }
+
+ buf->_cnt = (uint32_t)((buf->_base + buf->_bufsiz) - buf->_ptr);
+ }
+}
+
+static void mrewind(MEMFILE *buf)
+{
+ mseek(buf, 0, SEEK_SET);
+}
+
+/* PowerPacker unpack code taken from Heikki Orsila's amigadepack. Seems to have no license,
+** so I'll assume it fits into BSD 3-Clause. If not, feel free to contact me at my email
+** address found at the bottom of 16-bits.org.
+**
+** Modified by 8bitbubsy (me).
+*/
+
+#define PP_READ_BITS(nbits, var) \
+ bitCnt = (nbits); \
+ while (bitsLeft < bitCnt) \
+ { \
+ if (bufSrc < src) \
+ return false; \
+ bitBuffer |= ((*--bufSrc) << bitsLeft); \
+ bitsLeft += 8; \
+ } \
+ (var) = 0; \
+ bitsLeft -= bitCnt; \
+ while (bitCnt--) \
+ { \
+ (var) = ((var) << 1) | (bitBuffer & 1); \
+ bitBuffer >>= 1; \
+ } \
+
+static uint8_t ppdecrunch(uint8_t *src, uint8_t *dst, uint8_t *offsetLens, uint32_t srcLen, uint32_t dstLen, uint8_t skipBits)
+{
+ uint8_t *bufSrc, *dstEnd, *out, bitsLeft, bitCnt;
+ uint32_t x, todo, offBits, offset, written, bitBuffer;
+
+ if (src == NULL || dst == NULL || offsetLens == NULL)
+ return false;
+
+ bitsLeft = 0;
+ bitBuffer = 0;
+ written = 0;
+ bufSrc = src + srcLen;
+ out = dst + dstLen;
+ dstEnd = out;
+
+ PP_READ_BITS(skipBits, x);
+ while (written < dstLen)
+ {
+ PP_READ_BITS(1, x);
+ if (x == 0)
+ {
+ todo = 1;
+
+ do
+ {
+ PP_READ_BITS(2, x);
+ todo += x;
+ }
+ while (x == 3);
+
+ while (todo--)
+ {
+ PP_READ_BITS(8, x);
+ if (out <= dst)
+ return false;
+
+ *--out = (uint8_t)x;
+ written++;
+ }
+
+ if (written == dstLen)
+ break;
+ }
+
+ PP_READ_BITS(2, x);
+ offBits = offsetLens[x];
+ todo = x + 2;
+
+ if (x == 3)
+ {
+ PP_READ_BITS(1, x);
+ if (x == 0) offBits = 7;
+
+ PP_READ_BITS((uint8_t)offBits, offset);
+ do
+ {
+ PP_READ_BITS(3, x);
+ todo += x;
+ }
+ while (x == 7);
+ }
+ else
+ {
+ PP_READ_BITS((uint8_t)offBits, offset);
+ }
+
+ if (out+offset >= dstEnd)
+ return false;
+
+ while (todo--)
+ {
+ x = out[offset];
+ if (out <= dst)
+ return false;
+
+ *--out = (uint8_t)x;
+ written++;
+ }
+ }
+
+ return true;
+}
+
+void setupNewMod(void)
+{
+ int8_t i;
+
+ // setup GUI text pointers
+ for (i = 0; i < MOD_SAMPLES; i++)
+ {
+ song->samples[i].volumeDisp = &song->samples[i].volume;
+ song->samples[i].lengthDisp = &song->samples[i].length;
+ song->samples[i].loopStartDisp = &song->samples[i].loopStart;
+ song->samples[i].loopLengthDisp = &song->samples[i].loopLength;
+
+ fillSampleRedoBuffer(i);
+ }
+
+ modSetPos(0, 0);
+ modSetPattern(0); // set pattern to 00 instead of first order's pattern
+
+ editor.currEditPatternDisp = &song->currPattern;
+ editor.currPosDisp = &song->currOrder;
+ editor.currPatternDisp = &song->header.order[0];
+ editor.currPosEdPattDisp = &song->header.order[0];
+ editor.currLengthDisp = &song->header.numOrders;
+
+ // calculate MOD size
+ ui.updateSongSize = true;
+
+ editor.muted[0] = false;
+ editor.muted[1] = false;
+ editor.muted[2] = false;
+ editor.muted[3] = false;
+
+ editor.editMoveAdd = 1;
+ editor.currSample = 0;
+ editor.musicTime = 0;
+ editor.modLoaded = true;
+ editor.blockMarkFlag = false;
+ editor.sampleZero = false;
+ editor.hiLowInstr = 0;
+
+ setLEDFilter(false); // real PT doesn't do this, but that's insane
+
+ updateWindowTitle(MOD_NOT_MODIFIED);
+
+ editor.timingMode = TEMPO_MODE_CIA;
+
+ modSetSpeed(6);
+ modSetTempo(song->header.initialTempo); // 125 for normal MODs, custom value for certain STK/UST MODs
+
+ updateCurrSample();
+ editor.samplePos = 0;
+ updateSamplePos();
+}
+
+void loadModFromArg(char *arg)
+{
+ uint32_t filenameLen;
+ UNICHAR *filenameU;
+
+ ui.introScreenShown = false;
+ statusAllRight();
+
+ filenameLen = (uint32_t)strlen(arg);
+
+ filenameU = (UNICHAR *)calloc(filenameLen + 2, sizeof (UNICHAR));
+ if (filenameU == NULL)
+ {
+ statusOutOfMemory();
+ return;
+ }
+
+#ifdef _WIN32
+ MultiByteToWideChar(CP_UTF8, 0, arg, -1, filenameU, filenameLen);
+#else
+ strcpy(filenameU, arg);
+#endif
+
+ module_t *tempSong = modLoad(filenameU);
+ if (tempSong != NULL)
+ {
+ song->loaded = false;
+ modFree();
+ song = tempSong;
+ setupNewMod();
+ song->loaded = true;
+ }
+ else
+ {
+ editor.errorMsgActive = true;
+ editor.errorMsgBlock = true;
+ editor.errorMsgCounter = 0;
+
+ // status/error message is set in the mod loader
+ setErrPointer();
+ }
+
+ free(filenameU);
+}
+
+static bool testExtension(char *ext, uint8_t extLen, char *fullPath)
+{
+ // checks for EXT.filename and filename.EXT
+ char *fileName, begStr[8], endStr[8];
+ uint32_t fileNameLen;
+
+ extLen++; // add one to length (dot)
+
+ fileName = strrchr(fullPath, DIR_DELIMITER);
+ if (fileName != NULL)
+ fileName++;
+ else
+ fileName = fullPath;
+
+ fileNameLen = (uint32_t)strlen(fileName);
+ if (fileNameLen >= extLen)
+ {
+ sprintf(begStr, "%s.", ext);
+ if (!_strnicmp(begStr, fileName, extLen))
+ return true;
+
+ sprintf(endStr, ".%s", ext);
+ if (!_strnicmp(endStr, fileName + (fileNameLen - extLen), extLen))
+ return true;
+ }
+
+ return false;
+}
+
+void loadDroppedFile(char *fullPath, uint32_t fullPathLen, bool autoPlay, bool songModifiedCheck)
+{
+ bool isMod;
+ char *fileName, *ansiName;
+ uint8_t oldMode, oldPlayMode;
+ UNICHAR *fullPathU;
+
+ // don't allow drag n' drop if the tracker is busy
+ if (ui.pointerMode == POINTER_MODE_MSG1 || diskop.isFilling ||
+ editor.isWAVRendering || ui.samplerFiltersBoxShown || ui.samplerVolBoxShown)
+ {
+ return;
+ }
+
+ ansiName = (char *)calloc(fullPathLen + 10, sizeof (char));
+ if (ansiName == NULL)
+ {
+ statusOutOfMemory();
+ return;
+ }
+
+ fullPathU = (UNICHAR *)calloc(fullPathLen + 2, sizeof (UNICHAR));
+ if (fullPathU == NULL)
+ {
+ statusOutOfMemory();
+ return;
+ }
+
+#ifdef _WIN32
+ MultiByteToWideChar(CP_UTF8, 0, fullPath, -1, fullPathU, fullPathLen);
+#else
+ strcpy(fullPathU, fullPath);
+#endif
+
+ unicharToAnsi(ansiName, fullPathU, fullPathLen);
+
+ // make a new pointer point to filename (strip path)
+ fileName = strrchr(ansiName, DIR_DELIMITER);
+ if (fileName != NULL)
+ fileName++;
+ else
+ fileName = ansiName;
+
+ // check if the file extension is a module (FIXME: check module by content instead..?)
+ isMod = false;
+ if (testExtension("MOD", 3, fileName)) isMod = true;
+ else if (testExtension("M15", 3, fileName)) isMod = true;
+ else if (testExtension("STK", 3, fileName)) isMod = true;
+ else if (testExtension("NST", 3, fileName)) isMod = true;
+ else if (testExtension("UST", 3, fileName)) isMod = true;
+ else if (testExtension("PP", 2, fileName)) isMod = true;
+ else if (testExtension("NT", 2, fileName)) isMod = true;
+
+ if (isMod)
+ {
+ if (songModifiedCheck && song->modified)
+ {
+ free(ansiName);
+ free(fullPathU);
+
+ memcpy(oldFullPath, fullPath, fullPathLen);
+ oldFullPath[fullPathLen+0] = 0;
+ oldFullPath[fullPathLen+1] = 0;
+
+ oldFullPathLen = fullPathLen;
+ oldAutoPlay = autoPlay;
+
+ // de-minimize window and set focus so that the user sees the message box
+ SDL_RestoreWindow(video.window);
+ SDL_RaiseWindow(video.window);
+
+ showSongUnsavedAskBox(ASK_DISCARD_SONG_DRAGNDROP);
+ return;
+ }
+
+ module_t *tempSong = modLoad(fullPathU);
+ if (tempSong != NULL)
+ {
+ oldMode = editor.currMode;
+ oldPlayMode = editor.playMode;
+
+ modStop();
+ modFree();
+
+ song = tempSong;
+ setupNewMod();
+ song->loaded = true;
+
+ statusAllRight();
+
+ if (autoPlay)
+ {
+ editor.playMode = PLAY_MODE_NORMAL;
+ editor.currMode = MODE_PLAY;
+
+ // start normal playback
+ modPlay(DONT_SET_PATTERN, 0, 0);
+
+ pointerSetMode(POINTER_MODE_PLAY, DO_CARRY);
+ }
+ else if (oldMode == MODE_PLAY || oldMode == MODE_RECORD)
+ {
+ // use last mode
+ editor.playMode = oldPlayMode;
+ if (oldPlayMode == PLAY_MODE_PATTERN || oldMode == MODE_RECORD)
+ modPlay(0, 0, 0);
+ else
+ modPlay(DONT_SET_PATTERN, 0, 0);
+ editor.currMode = oldMode;
+
+ if (oldMode == MODE_RECORD)
+ pointerSetMode(POINTER_MODE_RECORD, DO_CARRY);
+ else
+ pointerSetMode(POINTER_MODE_PLAY, DO_CARRY);
+ }
+ else
+ {
+ // stop playback
+ editor.playMode = PLAY_MODE_NORMAL;
+ editor.currMode = MODE_IDLE;
+ editor.songPlaying = false;
+ pointerSetMode(POINTER_MODE_IDLE, DO_CARRY);
+ }
+
+ displayMainScreen();
+ }
+ else
+ {
+ editor.errorMsgActive = true;
+ editor.errorMsgBlock = true;
+ editor.errorMsgCounter = 0;
+ setErrPointer(); // status/error message is set in the mod loader
+ }
+ }
+ else
+ {
+ loadSample(fullPathU, fileName);
+ }
+
+ free(ansiName);
+ free(fullPathU);
+}
+
+void loadDroppedFile2(void)
+{
+ loadDroppedFile(oldFullPath, oldFullPathLen, oldAutoPlay, false);
+}
+
+module_t *createNewMod(void)
+{
+ uint8_t i;
+ module_t *newMod;
+
+ newMod = (module_t *)calloc(1, sizeof (module_t));
+ if (newMod == NULL)
+ goto oom;
+
+ for (i = 0; i < MAX_PATTERNS; i++)
+ {
+ newMod->patterns[i] = (note_t *)calloc(1, MOD_ROWS * sizeof (note_t) * AMIGA_VOICES);
+ if (newMod->patterns[i] == NULL)
+ goto oom;
+ }
+
+ // +2 sample slots for overflow safety (Paula and scopes)
+ newMod->sampleData = (int8_t *)calloc(MOD_SAMPLES + 2, MAX_SAMPLE_LEN);
+ if (newMod->sampleData == NULL)
+ goto oom;
+
+ newMod->header.numOrders = 1;
+
+ for (i = 0; i < MOD_SAMPLES; i++)
+ {
+ newMod->samples[i].offset = MAX_SAMPLE_LEN * i;
+ newMod->samples[i].loopLength = 2;
+
+ // setup GUI text pointers
+ newMod->samples[i].volumeDisp = &newMod->samples[i].volume;
+ newMod->samples[i].lengthDisp = &newMod->samples[i].length;
+ newMod->samples[i].loopStartDisp = &newMod->samples[i].loopStart;
+ newMod->samples[i].loopLengthDisp = &newMod->samples[i].loopLength;
+ }
+
+ for (i = 0; i < AMIGA_VOICES; i++)
+ newMod->channels[i].n_chanindex = i;
+
+ // setup GUI text pointers
+ editor.currEditPatternDisp = &newMod->currPattern;
+ editor.currPosDisp = &newMod->currOrder;
+ editor.currPatternDisp = &newMod->header.order[0];
+ editor.currPosEdPattDisp = &newMod->header.order[0];
+ editor.currLengthDisp = &newMod->header.numOrders;
+
+ ui.updateSongSize = true;
+ return newMod;
+
+oom:
+ showErrorMsgBox("Out of memory!");
+ return NULL;
+}
--- /dev/null
+++ b/src/pt2_module_loader.h
@@ -1,0 +1,15 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "pt2_header.h"
+#include "pt2_unicode.h"
+#include "pt2_structs.h"
+
+void showSongUnsavedAskBox(int8_t askScreenType);
+void loadModFromArg(char *arg);
+void loadDroppedFile(char *fullPath, uint32_t fullPathLen, bool autoPlay, bool songModifiedCheck);
+void loadDroppedFile2(void);
+module_t *createNewMod(void);
+module_t *modLoad(UNICHAR *fileName);
+void setupNewMod(void);
--- /dev/null
+++ b/src/pt2_module_saver.c
@@ -1,0 +1,235 @@
+// for finding memory leaks in debug mode with Visual Studio
+#if defined _DEBUG && defined _MSC_VER
+#include <crtdbg.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <sys/stat.h>
+#include "pt2_mouse.h"
+#include "pt2_header.h"
+#include "pt2_textout.h"
+#include "pt2_helpers.h"
+#include "pt2_visuals.h"
+#include "pt2_sampler.h"
+#include "pt2_config.h"
+
+bool modSave(char *fileName)
+{
+ int32_t i, j;
+ FILE *f;
+
+ f = fopen(fileName, "wb");
+ if (f == NULL)
+ {
+ displayErrorMsg("FILE I/O ERROR !");
+ return false;
+ }
+
+ fwrite(song->header.name, 1, 20, f);
+
+ for (i = 0; i < MOD_SAMPLES; i++)
+ {
+ moduleSample_t *s = &song->samples[i];
+
+ fwrite(s->text, 1, 22, f);
+
+ uint16_t length = SWAP16(s->length >> 1);
+ fwrite(&length, sizeof (int16_t), 1, f);
+
+ fputc(s->fineTune & 0xF, f);
+ fputc(((uint8_t)s->volume > 64) ? 64 : s->volume, f);
+
+ uint16_t loopStart = s->loopStart;
+ uint16_t loopLength = s->loopLength;
+
+ if (loopLength < 2)
+ loopLength = 2;
+
+ if (loopStart+loopLength <= 2 || loopStart+loopLength > s->length)
+ {
+ loopStart = 0;
+ loopLength = 2;
+ }
+
+ loopLength = SWAP16(loopLength >> 1);
+ loopStart = SWAP16(loopStart >> 1);
+
+ fwrite(&loopStart, sizeof (int16_t), 1, f);
+ fwrite(&loopLength, sizeof (int16_t), 1, f);
+ }
+
+ fputc((uint8_t)song->header.numOrders, f);
+ fputc(0x7F, f); // ProTracker puts 0x7F at this place (restartPos/tempo in other trackers)
+
+ for (i = 0; i < MOD_ORDERS; i++)
+ fputc((uint8_t)song->header.order[i], f);
+
+ int32_t numPatterns = 0;
+ for (i = 0; i < MOD_ORDERS; i++)
+ {
+ if (song->header.order[i] > numPatterns)
+ numPatterns = song->header.order[i];
+ }
+
+ numPatterns++;
+ if (numPatterns > MAX_PATTERNS)
+ numPatterns = MAX_PATTERNS;
+
+ fwrite((numPatterns <= 64) ? "M.K." : "M!K!", 1, 4, f);
+
+ for (i = 0; i < numPatterns; i++)
+ {
+ note_t *note = song->patterns[i];
+ for (j = 0; j < MOD_ROWS * AMIGA_VOICES; j++, note++)
+ {
+ fputc((note->sample & 0xF0) | (note->period >> 8), f);
+ fputc(note->period & 0xFF, f);
+ fputc(((note->sample << 4) & 0xF0) | (note->command & 0x0F), f);
+ fputc(note->param, f);
+ }
+ }
+
+ for (i = 0; i < MOD_SAMPLES; i++)
+ {
+ moduleSample_t *s = &song->samples[i];
+ const int8_t *smpPtr8 = &song->sampleData[s->offset];
+
+ // clear first two bytes of non-looping samples (prevents stuck "BEEEEEP")
+ if (s->length >= 2 && s->loopStart+s->loopLength == 2)
+ {
+ fputc(0, f);
+ fputc(0, f);
+
+ fwrite(&smpPtr8[2], 1, s->length-2, f);
+ }
+ else
+ {
+ fwrite(smpPtr8, 1, s->length, f);
+ }
+ }
+
+ fclose(f);
+
+ displayMsg("MODULE SAVED !");
+ setMsgPointer();
+
+ diskop.cached = false;
+ if (ui.diskOpScreenShown)
+ ui.updateDiskOpFileList = true;
+
+ updateWindowTitle(MOD_NOT_MODIFIED);
+ return true;
+}
+
+bool saveModule(bool checkIfFileExist, bool giveNewFreeFilename)
+{
+ char fileName[128], tmpBuffer[64];
+ int32_t i, j;
+ struct stat statBuffer;
+
+ if (config.modDot)
+ {
+ // extension.filename
+ if (song->header.name[0] == '\0')
+ {
+ strcat(fileName, "mod.untitled");
+ }
+ else
+ {
+ strcat(fileName, "mod.");
+ for (i = 4; i < 20+4; i++)
+ {
+ fileName[i] = (char)tolower(song->header.name[i-4]);
+ if (fileName[i] == '\0') break;
+ sanitizeFilenameChar(&fileName[i]);
+ }
+ }
+ }
+ else
+ {
+ // filename.extension
+ if (song->header.name[0] == '\0')
+ {
+ strcat(fileName, "untitled.mod");
+ }
+ else
+ {
+ for (i = 0; i < 20; i++)
+ {
+ fileName[i] = (char)tolower(song->header.name[i]);
+ if (fileName[i] == '\0') break;
+ sanitizeFilenameChar(&fileName[i]);
+ }
+ strcat(fileName, ".mod");
+ }
+ }
+
+ if (giveNewFreeFilename && stat(fileName, &statBuffer) == 0)
+ {
+ for (i = 1; i <= 999; i++)
+ {
+ if (config.modDot)
+ {
+ // extension.filename
+ if (song->header.name[0] == '\0')
+ {
+ sprintf(fileName, "mod.untitled-%d", i);
+ }
+ else
+ {
+ for (j = 0; j < 20; j++)
+ {
+ tmpBuffer[j] = (char)tolower(song->header.name[j]);
+ if (tmpBuffer[j] == '\0') break;
+ sanitizeFilenameChar(&tmpBuffer[j]);
+ }
+ sprintf(fileName, "mod.%s-%d", tmpBuffer, i);
+ }
+ }
+ else
+ {
+ // filename.extension
+ if (song->header.name[0] == '\0')
+ {
+ sprintf(fileName, "untitled-%d.mod", i);
+ }
+ else
+ {
+ for (j = 0; j < 20; j++)
+ {
+ tmpBuffer[j] = (char)tolower(song->header.name[j]);
+ if (tmpBuffer[j] == '\0') break;
+ sanitizeFilenameChar(&tmpBuffer[j]);
+ }
+ sprintf(fileName, "%s-%d.mod", tmpBuffer, i);
+ }
+ }
+
+ if (stat(fileName, &statBuffer) != 0)
+ break; // this filename can be used
+ }
+ }
+
+ if (checkIfFileExist && stat(fileName, &statBuffer) == 0)
+ {
+ ui.askScreenShown = true;
+ ui.askScreenType = ASK_SAVEMOD_OVERWRITE;
+ pointerSetMode(POINTER_MODE_MSG1, NO_CARRY);
+ setStatusMessage("OVERWRITE FILE ?", NO_CARRY);
+ renderAskDialog();
+ return -1;
+ }
+
+ if (ui.askScreenShown)
+ {
+ ui.answerNo = false;
+ ui.answerYes = false;
+ ui.askScreenShown = false;
+ }
+
+ return modSave(fileName);
+}
+
--- /dev/null
+++ b/src/pt2_module_saver.h
@@ -1,0 +1,6 @@
+#pragma once
+
+#include <stdbool.h>
+
+bool saveModule(bool checkIfFileExist, bool giveNewFreeFilename);
+bool modSave(char *fileName);
--- a/src/pt2_mouse.c
+++ b/src/pt2_mouse.c
@@ -15,14 +15,16 @@
#include "pt2_palette.h"
#include "pt2_diskop.h"
#include "pt2_sampler.h"
-#include "pt2_modloader.h"
+#include "pt2_module_loader.h"
#include "pt2_edit.h"
-#include "pt2_sampleloader.h"
+#include "pt2_sample_loader.h"
#include "pt2_visuals.h"
#include "pt2_tables.h"
#include "pt2_audio.h"
#include "pt2_textout.h"
#include "pt2_keyboard.h"
+#include "pt2_config.h"
+#include "pt2_bmp.h"
/* TODO: Move irrelevant routines outta here! Disgusting design!
** Keep in mind that this was programmed in my early programming days...
@@ -574,8 +576,8 @@
}
}
- if (editor.samplePos > modEntry->samples[editor.currSample].length)
- editor.samplePos = modEntry->samples[editor.currSample].length;
+ if (editor.samplePos > song->samples[editor.currSample].length)
+ editor.samplePos = song->samples[editor.currSample].length;
ui.updatePosText = true;
}
@@ -712,12 +714,12 @@
void sampleFineTuneUpButton(void)
{
- int8_t finetune = modEntry->samples[editor.currSample].fineTune & 0xF;
+ int8_t finetune = song->samples[editor.currSample].fineTune & 0xF;
if (finetune != 7)
- modEntry->samples[editor.currSample].fineTune = (finetune + 1) & 0xF;
+ song->samples[editor.currSample].fineTune = (finetune + 1) & 0xF;
if (mouse.rightButtonPressed)
- modEntry->samples[editor.currSample].fineTune = 0;
+ song->samples[editor.currSample].fineTune = 0;
recalcChordLength();
ui.updateCurrSampleFineTune = true;
@@ -725,12 +727,12 @@
void sampleFineTuneDownButton(void)
{
- int8_t finetune = modEntry->samples[editor.currSample].fineTune & 0xF;
+ int8_t finetune = song->samples[editor.currSample].fineTune & 0xF;
if (finetune != 8)
- modEntry->samples[editor.currSample].fineTune = (finetune - 1) & 0xF;
+ song->samples[editor.currSample].fineTune = (finetune - 1) & 0xF;
if (mouse.rightButtonPressed)
- modEntry->samples[editor.currSample].fineTune = 0;
+ song->samples[editor.currSample].fineTune = 0;
recalcChordLength();
ui.updateCurrSampleFineTune = true;
@@ -738,7 +740,7 @@
void sampleVolumeUpButton(void)
{
- int8_t val = modEntry->samples[editor.currSample].volume;
+ int8_t val = song->samples[editor.currSample].volume;
if (mouse.rightButtonPressed)
val += 16;
@@ -748,13 +750,13 @@
if (val > 64)
val = 64;
- modEntry->samples[editor.currSample].volume = (uint8_t)val;
+ song->samples[editor.currSample].volume = (uint8_t)val;
ui.updateCurrSampleVolume = true;
}
void sampleVolumeDownButton(void)
{
- int8_t val = modEntry->samples[editor.currSample].volume;
+ int8_t val = song->samples[editor.currSample].volume;
if (mouse.rightButtonPressed)
val -= 16;
@@ -764,7 +766,7 @@
if (val < 0)
val = 0;
- modEntry->samples[editor.currSample].volume = (uint8_t)val;
+ song->samples[editor.currSample].volume = (uint8_t)val;
ui.updateCurrSampleVolume = true;
}
@@ -772,10 +774,10 @@
{
int32_t val;
- if (modEntry->samples[editor.currSample].length == MAX_SAMPLE_LEN)
+ if (song->samples[editor.currSample].length == MAX_SAMPLE_LEN)
return;
- val = modEntry->samples[editor.currSample].length;
+ val = song->samples[editor.currSample].length;
if (mouse.rightButtonPressed)
{
if (fast)
@@ -794,7 +796,7 @@
if (val > MAX_SAMPLE_LEN)
val = MAX_SAMPLE_LEN;
- modEntry->samples[editor.currSample].length = (uint16_t)val;
+ song->samples[editor.currSample].length = (uint16_t)val;
ui.updateCurrSampleLength = true;
}
@@ -803,7 +805,7 @@
int32_t val;
moduleSample_t *s;
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
if (s->loopStart+s->loopLength > 2)
{
if (s->length == s->loopStart+s->loopLength)
@@ -815,7 +817,7 @@
return;
}
- val = modEntry->samples[editor.currSample].length;
+ val = song->samples[editor.currSample].length;
if (mouse.rightButtonPressed)
{
if (fast)
@@ -849,13 +851,13 @@
{
int32_t val, loopLen, len;
- val = modEntry->samples[editor.currSample].loopStart;
- loopLen = modEntry->samples[editor.currSample].loopLength;
- len = modEntry->samples[editor.currSample].length;
+ val = song->samples[editor.currSample].loopStart;
+ loopLen = song->samples[editor.currSample].loopLength;
+ len = song->samples[editor.currSample].length;
if (len == 0)
{
- modEntry->samples[editor.currSample].loopStart = 0;
+ song->samples[editor.currSample].loopStart = 0;
return;
}
@@ -877,7 +879,7 @@
if (val > len-loopLen)
val = len-loopLen;
- modEntry->samples[editor.currSample].loopStart = (uint16_t)val;
+ song->samples[editor.currSample].loopStart = (uint16_t)val;
ui.updateCurrSampleRepeat = true;
mixerUpdateLoops();
@@ -893,12 +895,12 @@
{
int32_t val, len;
- val = modEntry->samples[editor.currSample].loopStart;
- len = modEntry->samples[editor.currSample].length;
+ val = song->samples[editor.currSample].loopStart;
+ len = song->samples[editor.currSample].length;
if (len == 0)
{
- modEntry->samples[editor.currSample].loopStart = 0;
+ song->samples[editor.currSample].loopStart = 0;
return;
}
@@ -920,7 +922,7 @@
if (val < 0)
val = 0;
- modEntry->samples[editor.currSample].loopStart = (uint16_t)val;
+ song->samples[editor.currSample].loopStart = (uint16_t)val;
ui.updateCurrSampleRepeat = true;
mixerUpdateLoops();
@@ -936,13 +938,13 @@
{
int32_t val, loopStart, len;
- val = modEntry->samples[editor.currSample].loopLength;
- loopStart = modEntry->samples[editor.currSample].loopStart;
- len = modEntry->samples[editor.currSample].length;
+ val = song->samples[editor.currSample].loopLength;
+ loopStart = song->samples[editor.currSample].loopStart;
+ len = song->samples[editor.currSample].length;
if (len == 0)
{
- modEntry->samples[editor.currSample].loopLength = 2;
+ song->samples[editor.currSample].loopLength = 2;
return;
}
@@ -964,7 +966,7 @@
if (val > len-loopStart)
val = len-loopStart;
- modEntry->samples[editor.currSample].loopLength = (uint16_t)val;
+ song->samples[editor.currSample].loopLength = (uint16_t)val;
ui.updateCurrSampleReplen = true;
mixerUpdateLoops();
@@ -980,12 +982,12 @@
{
int32_t val, len;
- val = modEntry->samples[editor.currSample].loopLength;
- len = modEntry->samples[editor.currSample].length;
+ val = song->samples[editor.currSample].loopLength;
+ len = song->samples[editor.currSample].length;
if (len == 0)
{
- modEntry->samples[editor.currSample].loopLength = 2;
+ song->samples[editor.currSample].loopLength = 2;
return;
}
@@ -1007,7 +1009,7 @@
if (val < 2)
val = 2;
- modEntry->samples[editor.currSample].loopLength = (uint16_t)val;
+ song->samples[editor.currSample].loopLength = (uint16_t)val;
ui.updateCurrSampleReplen = true;
mixerUpdateLoops();
@@ -1026,7 +1028,7 @@
if (editor.timingMode == TEMPO_MODE_VBLANK)
return;
- val = modEntry->currBPM;
+ val = song->currBPM;
if (mouse.rightButtonPressed)
val += 10;
else
@@ -1035,8 +1037,8 @@
if (val > 255)
val = 255;
- modEntry->currBPM = val;
- modSetTempo(modEntry->currBPM);
+ song->currBPM = val;
+ modSetTempo(song->currBPM);
ui.updateSongBPM = true;
}
@@ -1047,7 +1049,7 @@
if (editor.timingMode == TEMPO_MODE_VBLANK)
return;
- val = modEntry->currBPM;
+ val = song->currBPM;
if (mouse.rightButtonPressed)
val -= 10;
else
@@ -1056,8 +1058,8 @@
if (val < 32)
val = 32;
- modEntry->currBPM = val;
- modSetTempo(modEntry->currBPM);
+ song->currBPM = val;
+ modSetTempo(song->currBPM);
ui.updateSongBPM = true;
}
@@ -1065,7 +1067,7 @@
{
int16_t val;
- val = modEntry->head.orderCount;
+ val = song->header.numOrders;
if (mouse.rightButtonPressed)
val += 10;
else
@@ -1074,19 +1076,19 @@
if (val > 128)
val = 128;
- modEntry->head.orderCount = (uint8_t)val;
+ song->header.numOrders = (uint8_t)val;
- val = modEntry->currOrder;
- if (val > modEntry->head.orderCount-1)
- val = modEntry->head.orderCount-1;
+ val = song->currOrder;
+ if (val > song->header.numOrders-1)
+ val = song->header.numOrders-1;
- editor.currPosEdPattDisp = &modEntry->head.order[val];
+ editor.currPosEdPattDisp = &song->header.order[val];
ui.updateSongLength = true;
}
void songLengthDownButton(void)
{
- int16_t val = modEntry->head.orderCount;
+ int16_t val = song->header.numOrders;
if (mouse.rightButtonPressed)
val -= 10;
@@ -1096,19 +1098,19 @@
if (val < 1)
val = 1;
- modEntry->head.orderCount = (uint8_t)val;
+ song->header.numOrders = (uint8_t)val;
- val = modEntry->currOrder;
- if (val > modEntry->head.orderCount-1)
- val = modEntry->head.orderCount-1;
+ val = song->currOrder;
+ if (val > song->header.numOrders-1)
+ val = song->header.numOrders-1;
- editor.currPosEdPattDisp = &modEntry->head.order[val];
+ editor.currPosEdPattDisp = &song->header.order[val];
ui.updateSongLength = true;
}
void patternUpButton(void)
{
- int16_t val = modEntry->head.order[modEntry->currOrder];
+ int16_t val = song->header.order[song->currOrder];
if (mouse.rightButtonPressed)
val += 10;
@@ -1118,7 +1120,7 @@
if (val > MAX_PATTERNS-1)
val = MAX_PATTERNS-1;
- modEntry->head.order[modEntry->currOrder] = (uint8_t)val;
+ song->header.order[song->currOrder] = (uint8_t)val;
if (ui.posEdScreenShown)
ui.updatePosEd = true;
@@ -1128,7 +1130,7 @@
void patternDownButton(void)
{
- int16_t val = modEntry->head.order[modEntry->currOrder];
+ int16_t val = song->header.order[song->currOrder];
if (mouse.rightButtonPressed)
val -= 10;
@@ -1138,7 +1140,7 @@
if (val < 0)
val = 0;
- modEntry->head.order[modEntry->currOrder] = (uint8_t)val;
+ song->header.order[song->currOrder] = (uint8_t)val;
if (ui.posEdScreenShown)
ui.updatePosEd = true;
@@ -1148,7 +1150,7 @@
void positionUpButton(void)
{
- int16_t val = modEntry->currOrder;
+ int16_t val = song->currOrder;
if (mouse.rightButtonPressed)
val += 10;
@@ -1163,7 +1165,7 @@
void positionDownButton(void)
{
- int16_t val = modEntry->currOrder;
+ int16_t val = song->currOrder;
if (mouse.rightButtonPressed)
val -= 10;
@@ -1321,14 +1323,20 @@
// NORMALIZE
if (mouse.x >= 101 && mouse.x <= 143)
{
- s = &modEntry->samples[editor.currSample];
+ if (editor.sampleZero)
+ {
+ statusNotSampleZero();
+ return;
+ }
+
+ s = &song->samples[editor.currSample];
if (s->length == 0)
{
- displayErrorMsg("SAMPLE IS EMPTY");
+ statusSampleIsEmpty();
return;
}
- sampleData = &modEntry->sampleData[s->offset];
+ sampleData = &song->sampleData[s->offset];
if (editor.markStartOfs != -1)
{
sampleData += editor.markStartOfs;
@@ -1424,10 +1432,16 @@
// RAMP
else if (mouse.x >= 72 && mouse.x <= 100)
{
- s = &modEntry->samples[editor.currSample];
+ if (editor.sampleZero)
+ {
+ statusNotSampleZero();
+ return;
+ }
+
+ s = &song->samples[editor.currSample];
if (s->length == 0)
{
- displayErrorMsg("SAMPLE IS EMPTY");
+ statusSampleIsEmpty();
return;
}
@@ -1438,7 +1452,7 @@
return;
}
- sampleData = &modEntry->sampleData[s->offset];
+ sampleData = &song->sampleData[s->offset];
if (editor.markStartOfs != -1)
{
sampleData += editor.markStartOfs;
@@ -1674,14 +1688,20 @@
// UNDO
if (mouse.x >= 65 && mouse.x <= 75 && mouse.y >= 154 && mouse.y <= 184)
{
- s = &modEntry->samples[editor.currSample];
+ if (editor.sampleZero)
+ {
+ statusNotSampleZero();
+ return;
+ }
+
+ s = &song->samples[editor.currSample];
if (s->length == 0)
{
- displayErrorMsg("SAMPLE IS EMPTY");
+ statusSampleIsEmpty();
}
else
{
- memcpy(&modEntry->sampleData[s->offset], editor.tempSample, MAX_SAMPLE_LEN);
+ memcpy(&song->sampleData[s->offset], editor.tempSample, MAX_SAMPLE_LEN);
redrawSample();
updateWindowTitle(MOD_IS_MODIFIED);
renderSamplerFiltersBox();
@@ -2053,9 +2073,9 @@
ui.updateDiskOpFileList = true;
}
}
- else if (ui.posEdScreenShown && modEntry->currOrder > 0)
+ else if (ui.posEdScreenShown && song->currOrder > 0)
{
- modSetPos(modEntry->currOrder - 1, DONT_SET_ROW);
+ modSetPos(song->currOrder - 1, DONT_SET_ROW);
}
}
else if (ui.samplerScreenShown)
@@ -2062,9 +2082,9 @@
{
samplerZoomInMouseWheel(); // lower part of screen
}
- else if (!editor.songPlaying && modEntry->currRow > 0)
+ else if (!editor.songPlaying && song->currRow > 0)
{
- modSetPos(DONT_SET_ORDER, modEntry->currRow - 1); // pattern data
+ modSetPos(DONT_SET_ORDER, song->currRow - 1); // pattern data
}
}
@@ -2084,9 +2104,9 @@
ui.updateDiskOpFileList = true;
}
}
- else if (ui.posEdScreenShown && modEntry->currOrder < modEntry->head.orderCount-1)
+ else if (ui.posEdScreenShown && song->currOrder < song->header.numOrders-1)
{
- modSetPos(modEntry->currOrder + 1, DONT_SET_ROW);
+ modSetPos(song->currOrder + 1, DONT_SET_ROW);
}
}
else if (ui.samplerScreenShown)
@@ -2093,9 +2113,9 @@
{
samplerZoomOutMouseWheel(); // lower part of screen
}
- else if (!editor.songPlaying && modEntry->currRow < MOD_ROWS)
+ else if (!editor.songPlaying && song->currRow < MOD_ROWS)
{
- modSetPos(DONT_SET_ORDER, modEntry->currRow + 1); // pattern data
+ modSetPos(DONT_SET_ORDER, song->currRow + 1); // pattern data
}
}
@@ -2371,7 +2391,7 @@
if (editor.songPlaying)
sprintf(pat2SmpText, "ROW 00 TO SMP %02X?", editor.currSample + 1);
else
- sprintf(pat2SmpText, "ROW %02d TO SMP %02X?", modEntry->currRow, editor.currSample + 1);
+ sprintf(pat2SmpText, "ROW %02d TO SMP %02X?", song->currRow, editor.currSample + 1);
setStatusMessage(pat2SmpText, NO_CARRY);
renderAskDialog();
@@ -2591,10 +2611,16 @@
}
else
{
- s = &modEntry->samples[editor.currSample];
+ if (editor.sampleZero)
+ {
+ statusNotSampleZero();
+ break;
+ }
+
+ s = &song->samples[editor.currSample];
if (s->length == 0)
{
- displayErrorMsg("SAMPLE IS EMPTY");
+ statusSampleIsEmpty();
break;
}
@@ -2611,10 +2637,10 @@
return true;
}
- memcpy(ptr8_1, &modEntry->sampleData[s->offset], MAX_SAMPLE_LEN);
+ memcpy(ptr8_1, &song->sampleData[s->offset], MAX_SAMPLE_LEN);
- ptr8_2 = &modEntry->sampleData[s->offset+editor.samplePos];
- ptr8_3 = &modEntry->sampleData[s->offset+s->length-1];
+ ptr8_2 = &song->sampleData[s->offset+editor.samplePos];
+ ptr8_3 = &song->sampleData[s->offset+s->length-1];
ptr8_4 = ptr8_1;
editor.modulateOffset = 0;
@@ -2637,12 +2663,12 @@
{
editor.modulatePos += editor.modulateSpeed;
- modTmp = (editor.modulatePos / 4096) & 0xFF;
- modDat = vibratoTable[modTmp & 0x1F] / 4;
+ modTmp = (editor.modulatePos >> 12) & 0xFF;
+ modDat = vibratoTable[modTmp & 0x1F] >> 2;
modPos = ((modTmp & 32) ? (editor.modulateOffset - modDat) : (editor.modulateOffset + modDat)) + 2048;
editor.modulateOffset = modPos;
- modPos /= 2048;
+ modPos >>= 11;
modPos = CLAMP(modPos, 0, s->length - 1);
ptr8_1 = &ptr8_4[modPos];
}
@@ -2661,10 +2687,16 @@
case PTB_EO_ECHO:
{
- s = &modEntry->samples[editor.currSample];
+ if (editor.sampleZero)
+ {
+ statusNotSampleZero();
+ break;
+ }
+
+ s = &song->samples[editor.currSample];
if (s->length == 0)
{
- displayErrorMsg("SAMPLE IS EMPTY");
+ statusSampleIsEmpty();
break;
}
@@ -2680,8 +2712,8 @@
break;
}
- ptr8_1 = &modEntry->sampleData[s->offset+editor.samplePos];
- ptr8_2 = &modEntry->sampleData[s->offset];
+ ptr8_1 = &song->sampleData[s->offset+editor.samplePos];
+ ptr8_2 = &song->sampleData[s->offset];
ptr8_3 = ptr8_2;
editor.modulateOffset = 0;
@@ -2702,12 +2734,12 @@
{
editor.modulatePos += editor.modulateSpeed;
- modTmp = (editor.modulatePos / 4096) & 0xFF;
- modDat = vibratoTable[modTmp & 0x1F] / 4;
+ modTmp = (editor.modulatePos >> 12) & 0xFF;
+ modDat = vibratoTable[modTmp & 0x1F] >> 2;
modPos = ((modTmp & 32) ? (editor.modulateOffset - modDat) : (editor.modulateOffset + modDat)) + 2048;
editor.modulateOffset = modPos;
- modPos /= 2048;
+ modPos >>= 11;
modPos = CLAMP(modPos, 0, s->length - 1);
ptr8_2 = &ptr8_3[modPos];
}
@@ -2756,10 +2788,16 @@
case PTB_EO_BOOST: // this is actually treble increase
{
- s = &modEntry->samples[editor.currSample];
+ if (editor.sampleZero)
+ {
+ statusNotSampleZero();
+ break;
+ }
+
+ s = &song->samples[editor.currSample];
if (s->length == 0)
{
- displayErrorMsg("SAMPLE IS EMPTY");
+ statusSampleIsEmpty();
break;
}
@@ -2773,10 +2811,16 @@
case PTB_EO_FILTER: // this is actually treble decrease
{
- s = &modEntry->samples[editor.currSample];
+ if (editor.sampleZero)
+ {
+ statusNotSampleZero();
+ break;
+ }
+
+ s = &song->samples[editor.currSample];
if (s->length == 0)
{
- displayErrorMsg("SAMPLE IS EMPTY");
+ statusSampleIsEmpty();
break;
}
@@ -2800,10 +2844,16 @@
case PTB_EO_MOD:
{
- s = &modEntry->samples[editor.currSample];
+ if (editor.sampleZero)
+ {
+ statusNotSampleZero();
+ break;
+ }
+
+ s = &song->samples[editor.currSample];
if (s->length == 0)
{
- displayErrorMsg("SAMPLE IS EMPTY");
+ statusSampleIsEmpty();
break;
}
@@ -2813,7 +2863,7 @@
break;
}
- ptr8_1 = &modEntry->sampleData[s->offset];
+ ptr8_1 = &song->sampleData[s->offset];
ptr8_3 = (int8_t *)malloc(MAX_SAMPLE_LEN);
if (ptr8_3 == NULL)
@@ -2835,13 +2885,13 @@
editor.modulatePos += editor.modulateSpeed;
- modTmp = (editor.modulatePos / 4096) & 0xFF;
- modDat = vibratoTable[modTmp & 0x1F] / 4;
+ modTmp = (editor.modulatePos >> 12) & 0xFF;
+ modDat = vibratoTable[modTmp & 0x1F] >> 2;
modPos = ((modTmp & 32) ? (editor.modulateOffset - modDat) : (editor.modulateOffset + modDat)) + 2048;
editor.modulateOffset = modPos;
- modPos /= 2048;
+ modPos >>= 11;
modPos = CLAMP(modPos, 0, s->length - 1);
ptr8_2 = &ptr8_3[modPos];
}
@@ -2861,16 +2911,22 @@
case PTB_EO_X_FADE:
{
- s = &modEntry->samples[editor.currSample];
+ if (editor.sampleZero)
+ {
+ statusNotSampleZero();
+ break;
+ }
+ s = &song->samples[editor.currSample];
+
if (s->length == 0)
{
- displayErrorMsg("SAMPLE IS EMPTY");
+ statusSampleIsEmpty();
break;
}
- ptr8_1 = &modEntry->sampleData[s->offset];
- ptr8_2 = &modEntry->sampleData[s->offset+s->length-1];
+ ptr8_1 = &song->sampleData[s->offset];
+ ptr8_2 = &song->sampleData[s->offset+s->length-1];
do
{
@@ -2896,22 +2952,28 @@
case PTB_EO_BACKWD:
{
- s = &modEntry->samples[editor.currSample];
+ if (editor.sampleZero)
+ {
+ statusNotSampleZero();
+ break;
+ }
+
+ s = &song->samples[editor.currSample];
if (s->length == 0)
{
- displayErrorMsg("SAMPLE IS EMPTY");
+ statusSampleIsEmpty();
break;
}
if (editor.markStartOfs != -1 && editor.markStartOfs != editor.markEndOfs && editor.markEndOfs != 0)
{
- ptr8_1 = &modEntry->sampleData[s->offset+editor.markStartOfs];
- ptr8_2 = &modEntry->sampleData[s->offset+editor.markEndOfs-1];
+ ptr8_1 = &song->sampleData[s->offset+editor.markStartOfs];
+ ptr8_2 = &song->sampleData[s->offset+editor.markEndOfs-1];
}
else
{
- ptr8_1 = &modEntry->sampleData[s->offset];
- ptr8_2 = &modEntry->sampleData[s->offset+s->length-1];
+ ptr8_1 = &song->sampleData[s->offset];
+ ptr8_2 = &song->sampleData[s->offset+s->length-1];
}
do
@@ -2932,10 +2994,16 @@
case PTB_EO_CB:
{
- s = &modEntry->samples[editor.currSample];
+ if (editor.sampleZero)
+ {
+ statusNotSampleZero();
+ break;
+ }
+
+ s = &song->samples[editor.currSample];
if (s->length == 0)
{
- displayErrorMsg("SAMPLE IS EMPTY");
+ statusSampleIsEmpty();
break;
}
@@ -2953,8 +3021,8 @@
turnOffVoices();
- memcpy(&modEntry->sampleData[s->offset], &modEntry->sampleData[s->offset + editor.samplePos], MAX_SAMPLE_LEN - editor.samplePos);
- memset(&modEntry->sampleData[s->offset + (MAX_SAMPLE_LEN - editor.samplePos)], 0, editor.samplePos);
+ memcpy(&song->sampleData[s->offset], &song->sampleData[s->offset + editor.samplePos], MAX_SAMPLE_LEN - editor.samplePos);
+ memset(&song->sampleData[s->offset + (MAX_SAMPLE_LEN - editor.samplePos)], 0, editor.samplePos);
if (editor.samplePos > s->loopStart)
{
@@ -2986,10 +3054,16 @@
// fade up
case PTB_EO_FU:
{
- s = &modEntry->samples[editor.currSample];
+ if (editor.sampleZero)
+ {
+ statusNotSampleZero();
+ break;
+ }
+
+ s = &song->samples[editor.currSample];
if (s->length == 0)
{
- displayErrorMsg("SAMPLE IS EMPTY");
+ statusSampleIsEmpty();
break;
}
@@ -3001,7 +3075,7 @@
double dSamplePosMul = 1.0 / editor.samplePos;
- ptr8_1 = &modEntry->sampleData[s->offset];
+ ptr8_1 = &song->sampleData[s->offset];
for (j = 0; j < editor.samplePos; j++)
{
dSmp = ((*ptr8_1) * j) * dSamplePosMul;
@@ -3021,10 +3095,16 @@
// fade down
case PTB_EO_FD:
{
- s = &modEntry->samples[editor.currSample];
+ if (editor.sampleZero)
+ {
+ statusNotSampleZero();
+ break;
+ }
+
+ s = &song->samples[editor.currSample];
if (s->length == 0)
{
- displayErrorMsg("SAMPLE IS EMPTY");
+ statusSampleIsEmpty();
break;
}
@@ -3040,13 +3120,17 @@
double dSampleMul = 1.0 / tmp32;
- ptr8_1 = &modEntry->sampleData[s->offset+s->length-1];
+ ptr8_1 = &song->sampleData[s->offset+s->length-1];
+
+ int32_t idx = 0;
for (j = editor.samplePos; j < s->length; j++)
{
- dSmp = ((*ptr8_1) * (j - editor.samplePos)) * dSampleMul;
+ dSmp = ((*ptr8_1) * idx) * dSampleMul;
smp32 = (int32_t)dSmp;
CLAMP8(smp32);
*ptr8_1-- = (int8_t)smp32;
+
+ idx++;
}
fixSampleBeep(s);
@@ -3059,10 +3143,10 @@
case PTB_EO_UPSAMP:
{
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
if (s->length == 0)
{
- displayErrorMsg("SAMPLE IS EMPTY");
+ statusSampleIsEmpty();
break;
}
@@ -3076,10 +3160,10 @@
case PTB_EO_DNSAMP:
{
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
if (s->length == 0)
{
- displayErrorMsg("SAMPLE IS EMPTY");
+ statusSampleIsEmpty();
break;
}
@@ -3112,21 +3196,27 @@
case PTB_EO_VOL:
{
- s = &modEntry->samples[editor.currSample];
+ if (editor.sampleZero)
+ {
+ statusNotSampleZero();
+ break;
+ }
+
+ s = &song->samples[editor.currSample];
if (s->length == 0)
{
- displayErrorMsg("SAMPLE IS EMPTY");
+ statusSampleIsEmpty();
break;
}
if (editor.sampleVol != 100)
{
- ptr8_1 = &modEntry->sampleData[modEntry->samples[editor.currSample].offset];
- double dSampleMul = editor.sampleVol / 100.0;
+ ptr8_1 = &song->sampleData[s->offset];
+ int32_t sampleMul = (((1UL << 19) * editor.sampleVol) + 50) / 100;
for (j = 0; j < s->length; j++)
{
- tmp16 = (int16_t)(ptr8_1[j] * dSampleMul);
+ tmp16 = (ptr8_1[j] * sampleMul) >> 19;
CLAMP8(tmp16);
ptr8_1[j] = (int8_t)tmp16;
}
@@ -3442,7 +3532,7 @@
case PTB_EO_LENGTH:
{
- if (modEntry->samples[editor.currSample].loopLength == 2 && modEntry->samples[editor.currSample].loopStart == 0)
+ if (song->samples[editor.currSample].loopLength == 2 && song->samples[editor.currSample].loopStart == 0)
{
editor.chordLengthMin = mouse.rightButtonPressed ? true : false;
recalcChordLength();
@@ -3497,11 +3587,11 @@
{
if (editor.currMode == MODE_IDLE || editor.currMode == MODE_EDIT)
{
- ui.tmpDisp16 = modEntry->currOrder;
- if (ui.tmpDisp16 > modEntry->head.orderCount-1)
- ui.tmpDisp16 = modEntry->head.orderCount-1;
+ ui.tmpDisp16 = song->currOrder;
+ if (ui.tmpDisp16 > song->header.numOrders-1)
+ ui.tmpDisp16 = song->header.numOrders-1;
- ui.tmpDisp16 = modEntry->head.order[ui.tmpDisp16];
+ ui.tmpDisp16 = song->header.order[ui.tmpDisp16];
editor.currPosEdPattDisp = &ui.tmpDisp16;
ui.numPtr16 = &ui.tmpDisp16;
ui.numLen = 2;
@@ -3513,7 +3603,7 @@
case PTB_PE_SCROLLTOP:
{
- if (modEntry->currOrder != 0)
+ if (song->currOrder != 0)
modSetPos(0, DONT_SET_ROW);
}
break;
@@ -3520,22 +3610,22 @@
case PTB_PE_SCROLLUP:
{
- if (modEntry->currOrder > 0)
- modSetPos(modEntry->currOrder - 1, DONT_SET_ROW);
+ if (song->currOrder > 0)
+ modSetPos(song->currOrder - 1, DONT_SET_ROW);
}
break;
case PTB_PE_SCROLLDOWN:
{
- if (modEntry->currOrder < modEntry->head.orderCount-1)
- modSetPos(modEntry->currOrder + 1, DONT_SET_ROW);
+ if (song->currOrder < song->header.numOrders-1)
+ modSetPos(song->currOrder + 1, DONT_SET_ROW);
}
break;
case PTB_PE_SCROLLBOT:
{
- if (modEntry->currOrder != modEntry->head.orderCount-1)
- modSetPos(modEntry->head.orderCount - 1, DONT_SET_ROW);
+ if (song->currOrder != song->header.numOrders-1)
+ modSetPos(song->header.numOrders - 1, DONT_SET_ROW);
}
break;
@@ -3569,8 +3659,8 @@
{
if (mouse.rightButtonPressed)
{
- modEntry->currOrder = 0;
- editor.currPatternDisp = &modEntry->head.order[modEntry->currOrder];
+ song->currOrder = 0;
+ editor.currPatternDisp = &song->header.order[song->currOrder];
if (ui.posEdScreenShown)
ui.updatePosEd = true;
@@ -3577,7 +3667,7 @@
}
else
{
- ui.tmpDisp16 = modEntry->currOrder;
+ ui.tmpDisp16 = song->currOrder;
editor.currPosDisp = &ui.tmpDisp16;
ui.numPtr16 = &ui.tmpDisp16;
ui.numLen = 3;
@@ -3594,7 +3684,7 @@
{
if (mouse.rightButtonPressed)
{
- modEntry->head.order[modEntry->currOrder] = 0;
+ song->header.order[song->currOrder] = 0;
ui.updateSongSize = true;
updateWindowTitle(MOD_IS_MODIFIED);
@@ -3604,7 +3694,7 @@
}
else
{
- ui.tmpDisp16 = modEntry->head.order[modEntry->currOrder];
+ ui.tmpDisp16 = song->header.order[song->currOrder];
editor.currPatternDisp = &ui.tmpDisp16;
ui.numPtr16 = &ui.tmpDisp16;
ui.numLen = 2;
@@ -3621,13 +3711,13 @@
{
if (mouse.rightButtonPressed)
{
- modEntry->head.orderCount = 1;
+ song->header.numOrders = 1;
- tmp16 = modEntry->currOrder;
- if (tmp16 > modEntry->head.orderCount-1)
- tmp16 = modEntry->head.orderCount-1;
+ tmp16 = song->currOrder;
+ if (tmp16 > song->header.numOrders-1)
+ tmp16 = song->header.numOrders-1;
- editor.currPosEdPattDisp = &modEntry->head.order[tmp16];
+ editor.currPosEdPattDisp = &song->header.order[tmp16];
ui.updateSongSize = true;
updateWindowTitle(MOD_IS_MODIFIED);
@@ -3637,7 +3727,7 @@
}
else
{
- ui.tmpDisp16 = modEntry->head.orderCount;
+ ui.tmpDisp16 = song->header.numOrders;
editor.currLengthDisp = &ui.tmpDisp16;
ui.numPtr16 = &ui.tmpDisp16;
ui.numLen = 3;
@@ -3653,7 +3743,7 @@
{
if (!ui.introScreenShown && (editor.currMode == MODE_IDLE || editor.currMode == MODE_EDIT || editor.playMode != PLAY_MODE_NORMAL))
{
- ui.tmpDisp16 = modEntry->currPattern;
+ ui.tmpDisp16 = song->currPattern;
editor.currEditPatternDisp = &ui.tmpDisp16;
ui.numPtr16 = &ui.tmpDisp16;
ui.numLen = 2;
@@ -3665,7 +3755,12 @@
case PTB_SAMPLES:
{
- editor.sampleZero = false;
+ if (editor.sampleZero)
+ {
+ editor.sampleZero = false;
+ ui.updateCurrSampleNum = true;
+ }
+
ui.tmpDisp8 = editor.currSample;
editor.currSampleDisp = &ui.tmpDisp8;
ui.numPtr8 = &ui.tmpDisp8;
@@ -3678,14 +3773,20 @@
case PTB_SVOLUMES:
{
+ if (editor.sampleZero)
+ {
+ statusNotSampleZero();
+ break;
+ }
+
if (mouse.rightButtonPressed)
{
- modEntry->samples[editor.currSample].volume = 0;
+ song->samples[editor.currSample].volume = 0;
}
else
{
- ui.tmpDisp8 = modEntry->samples[editor.currSample].volume;
- modEntry->samples[editor.currSample].volumeDisp = &ui.tmpDisp8;
+ ui.tmpDisp8 = song->samples[editor.currSample].volume;
+ song->samples[editor.currSample].volumeDisp = &ui.tmpDisp8;
ui.numPtr8 = &ui.tmpDisp8;
ui.numLen = 2;
ui.numBits = 8;
@@ -3697,9 +3798,15 @@
case PTB_SLENGTHS:
{
+ if (editor.sampleZero)
+ {
+ statusNotSampleZero();
+ break;
+ }
+
if (mouse.rightButtonPressed)
{
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
turnOffVoices();
@@ -3721,8 +3828,8 @@
}
else
{
- ui.tmpDisp16 = modEntry->samples[editor.currSample].length;
- modEntry->samples[editor.currSample].lengthDisp = &ui.tmpDisp16;
+ ui.tmpDisp16 = song->samples[editor.currSample].length;
+ song->samples[editor.currSample].lengthDisp = &ui.tmpDisp16;
ui.numPtr16 = &ui.tmpDisp16;
ui.numLen = 4;
ui.numBits = 16;
@@ -3734,9 +3841,15 @@
case PTB_SREPEATS:
{
+ if (editor.sampleZero)
+ {
+ statusNotSampleZero();
+ break;
+ }
+
if (mouse.rightButtonPressed)
{
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
s->loopStart = 0;
if (s->length >= s->loopLength)
@@ -3761,8 +3874,8 @@
}
else
{
- ui.tmpDisp16 = modEntry->samples[editor.currSample].loopStart;
- modEntry->samples[editor.currSample].loopStartDisp = &ui.tmpDisp16;
+ ui.tmpDisp16 = song->samples[editor.currSample].loopStart;
+ song->samples[editor.currSample].loopStartDisp = &ui.tmpDisp16;
ui.numPtr16 = &ui.tmpDisp16;
ui.numLen = 4;
ui.numBits = 16;
@@ -3774,9 +3887,15 @@
case PTB_SREPLENS:
{
+ if (editor.sampleZero)
+ {
+ statusNotSampleZero();
+ break;
+ }
+
if (mouse.rightButtonPressed)
{
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
s->loopLength = 0;
if (s->length >= s->loopStart)
@@ -3804,8 +3923,8 @@
}
else
{
- ui.tmpDisp16 = modEntry->samples[editor.currSample].loopLength;
- modEntry->samples[editor.currSample].loopLengthDisp = &ui.tmpDisp16;
+ ui.tmpDisp16 = song->samples[editor.currSample].loopLength;
+ song->samples[editor.currSample].loopLengthDisp = &ui.tmpDisp16;
ui.numPtr16 = &ui.tmpDisp16;
ui.numLen = 4;
ui.numBits = 16;
@@ -4025,15 +4144,15 @@
case PTB_POSINS:
{
- if ((editor.currMode == MODE_IDLE || editor.currMode == MODE_EDIT) && modEntry->head.orderCount < 128)
+ if ((editor.currMode == MODE_IDLE || editor.currMode == MODE_EDIT) && song->header.numOrders < 128)
{
- for (i = 0; i < 127-modEntry->currOrder; i++)
- modEntry->head.order[127-i] = modEntry->head.order[(127-i)-1];
- modEntry->head.order[modEntry->currOrder] = 0;
+ for (i = 0; i < 127-song->currOrder; i++)
+ song->header.order[127-i] = song->header.order[(127-i)-1];
+ song->header.order[song->currOrder] = 0;
- modEntry->head.orderCount++;
- if (modEntry->currOrder > modEntry->head.orderCount-1)
- editor.currPosEdPattDisp = &modEntry->head.order[modEntry->head.orderCount-1];
+ song->header.numOrders++;
+ if (song->currOrder > song->header.numOrders-1)
+ editor.currPosEdPattDisp = &song->header.order[song->header.numOrders-1];
updateWindowTitle(MOD_IS_MODIFIED);
@@ -4049,15 +4168,15 @@
case PTB_POSDEL:
{
- if ((editor.currMode == MODE_IDLE || editor.currMode == MODE_EDIT) && modEntry->head.orderCount > 1)
+ if ((editor.currMode == MODE_IDLE || editor.currMode == MODE_EDIT) && song->header.numOrders > 1)
{
- for (i = 0; i < 128-modEntry->currOrder; i++)
- modEntry->head.order[modEntry->currOrder+i] = modEntry->head.order[modEntry->currOrder+i+1];
- modEntry->head.order[127] = 0;
+ for (i = 0; i < 128-song->currOrder; i++)
+ song->header.order[song->currOrder+i] = song->header.order[song->currOrder+i+1];
+ song->header.order[127] = 0;
- modEntry->head.orderCount--;
- if (modEntry->currOrder > modEntry->head.orderCount-1)
- editor.currPosEdPattDisp = &modEntry->head.order[modEntry->head.orderCount-1];
+ song->header.numOrders--;
+ if (song->currOrder > song->header.numOrders-1)
+ editor.currPosEdPattDisp = &song->header.order[song->header.numOrders-1];
updateWindowTitle(MOD_IS_MODIFIED);
@@ -4112,14 +4231,14 @@
{
if (mouse.rightButtonPressed)
{
- memset(modEntry->head.moduleTitle, 0, sizeof (modEntry->head.moduleTitle));
+ memset(song->header.name, 0, sizeof (song->header.name));
ui.updateSongName = true;
updateWindowTitle(MOD_IS_MODIFIED);
}
else
{
- ui.showTextPtr = modEntry->head.moduleTitle;
- ui.textEndPtr = modEntry->head.moduleTitle + 19;
+ ui.showTextPtr = song->header.name;
+ ui.textEndPtr = song->header.name + 19;
ui.textLength = 20;
ui.editTextPos = 4133; // (y * 40) + x
ui.dstOffset = NULL;
@@ -4133,14 +4252,14 @@
{
if (mouse.rightButtonPressed)
{
- memset(modEntry->samples[editor.currSample].text, 0, sizeof (modEntry->samples[editor.currSample].text));
+ memset(song->samples[editor.currSample].text, 0, sizeof (song->samples[editor.currSample].text));
ui.updateCurrSampleName = true;
updateWindowTitle(MOD_IS_MODIFIED);
}
else
{
- ui.showTextPtr = modEntry->samples[editor.currSample].text;
- ui.textEndPtr = modEntry->samples[editor.currSample].text + 21;
+ ui.showTextPtr = song->samples[editor.currSample].text;
+ ui.textEndPtr = song->samples[editor.currSample].text + 21;
ui.textLength = 22;
ui.editTextPos = 4573; // (y * 40) + x
ui.dstOffset = NULL;
@@ -4376,9 +4495,9 @@
editor.playMode = PLAY_MODE_NORMAL;
if (mouse.rightButtonPressed)
- modPlay(DONT_SET_PATTERN, modEntry->currOrder, modEntry->currRow);
+ modPlay(DONT_SET_PATTERN, song->currOrder, song->currRow);
else
- modPlay(DONT_SET_PATTERN, modEntry->currOrder, DONT_SET_ROW);
+ modPlay(DONT_SET_PATTERN, song->currOrder, DONT_SET_ROW);
editor.currMode = MODE_PLAY;
pointerSetMode(POINTER_MODE_PLAY, DO_CARRY);
@@ -4391,9 +4510,9 @@
editor.playMode = PLAY_MODE_PATTERN;
if (mouse.rightButtonPressed)
- modPlay(modEntry->currPattern, DONT_SET_ORDER, modEntry->currRow);
+ modPlay(song->currPattern, DONT_SET_ORDER, song->currRow);
else
- modPlay(modEntry->currPattern, DONT_SET_ORDER, DONT_SET_ROW);
+ modPlay(song->currPattern, DONT_SET_ORDER, DONT_SET_ROW);
editor.currMode = MODE_PLAY;
pointerSetMode(POINTER_MODE_PLAY, DO_CARRY);
@@ -4421,9 +4540,9 @@
editor.playMode = PLAY_MODE_PATTERN;
if (mouse.rightButtonPressed)
- modPlay(modEntry->currPattern, DONT_SET_ORDER, modEntry->currRow);
+ modPlay(song->currPattern, DONT_SET_ORDER, song->currRow);
else
- modPlay(modEntry->currPattern, DONT_SET_ORDER, DONT_SET_ROW);
+ modPlay(song->currPattern, DONT_SET_ORDER, DONT_SET_ROW);
editor.currMode = MODE_RECORD;
pointerSetMode(POINTER_MODE_EDIT, DO_CARRY);
@@ -4523,7 +4642,7 @@
case PTB_FTUNEU:
{
- if ((modEntry->samples[editor.currSample].fineTune & 0xF) != 7)
+ if (!editor.sampleZero && (song->samples[editor.currSample].fineTune & 0xF) != 7)
{
sampleFineTuneUpButton();
updateWindowTitle(MOD_IS_MODIFIED);
@@ -4533,11 +4652,10 @@
case PTB_FTUNED:
{
- if ((modEntry->samples[editor.currSample].fineTune & 0xF) != 8)
+ if (!editor.sampleZero && (song->samples[editor.currSample].fineTune & 0xF) != 8)
{
sampleFineTuneDownButton();
updateWindowTitle(MOD_IS_MODIFIED);
-
}
}
break;
@@ -4544,7 +4662,7 @@
case PTB_SVOLUMEU:
{
- if (modEntry->samples[editor.currSample].volume < 64)
+ if (!editor.sampleZero && song->samples[editor.currSample].volume < 64)
{
sampleVolumeUpButton();
updateWindowTitle(MOD_IS_MODIFIED);
@@ -4554,7 +4672,7 @@
case PTB_SVOLUMED:
{
- if (modEntry->samples[editor.currSample].volume > 0)
+ if (!editor.sampleZero && song->samples[editor.currSample].volume > 0)
{
sampleVolumeDownButton();
updateWindowTitle(MOD_IS_MODIFIED);
@@ -4564,7 +4682,7 @@
case PTB_SLENGTHU:
{
- if (modEntry->samples[editor.currSample].length < MAX_SAMPLE_LEN)
+ if (!editor.sampleZero && song->samples[editor.currSample].length < MAX_SAMPLE_LEN)
{
sampleLengthUpButton(INCREMENT_SLOW);
updateWindowTitle(MOD_IS_MODIFIED);
@@ -4574,7 +4692,7 @@
case PTB_SLENGTHD:
{
- if (modEntry->samples[editor.currSample].length > 0)
+ if (!editor.sampleZero && song->samples[editor.currSample].length > 0)
{
sampleLengthDownButton(INCREMENT_SLOW);
updateWindowTitle(MOD_IS_MODIFIED);
@@ -4584,37 +4702,49 @@
case PTB_SREPEATU:
{
- oldVal = modEntry->samples[editor.currSample].loopStart;
- sampleRepeatUpButton(INCREMENT_SLOW);
- if (modEntry->samples[editor.currSample].loopStart != oldVal)
- updateWindowTitle(MOD_IS_MODIFIED);
+ if (!editor.sampleZero)
+ {
+ oldVal = song->samples[editor.currSample].loopStart;
+ sampleRepeatUpButton(INCREMENT_SLOW);
+ if (song->samples[editor.currSample].loopStart != oldVal)
+ updateWindowTitle(MOD_IS_MODIFIED);
+ }
}
break;
case PTB_SREPEATD:
{
- oldVal = modEntry->samples[editor.currSample].loopStart;
- sampleRepeatDownButton(INCREMENT_SLOW);
- if (modEntry->samples[editor.currSample].loopStart != oldVal)
- updateWindowTitle(MOD_IS_MODIFIED);
+ if (!editor.sampleZero)
+ {
+ oldVal = song->samples[editor.currSample].loopStart;
+ sampleRepeatDownButton(INCREMENT_SLOW);
+ if (song->samples[editor.currSample].loopStart != oldVal)
+ updateWindowTitle(MOD_IS_MODIFIED);
+ }
}
break;
case PTB_SREPLENU:
{
- oldVal = modEntry->samples[editor.currSample].loopLength;
- sampleRepeatLengthUpButton(INCREMENT_SLOW);
- if (modEntry->samples[editor.currSample].loopLength != oldVal)
- updateWindowTitle(MOD_IS_MODIFIED);
+ if (!editor.sampleZero)
+ {
+ oldVal = song->samples[editor.currSample].loopLength;
+ sampleRepeatLengthUpButton(INCREMENT_SLOW);
+ if (song->samples[editor.currSample].loopLength != oldVal)
+ updateWindowTitle(MOD_IS_MODIFIED);
+ }
}
break;
case PTB_SREPLEND:
{
- oldVal = modEntry->samples[editor.currSample].loopLength;
- sampleRepeatLengthDownButton(INCREMENT_SLOW);
- if (modEntry->samples[editor.currSample].loopLength != oldVal)
- updateWindowTitle(MOD_IS_MODIFIED);
+ if (!editor.sampleZero)
+ {
+ oldVal = song->samples[editor.currSample].loopLength;
+ sampleRepeatLengthDownButton(INCREMENT_SLOW);
+ if (song->samples[editor.currSample].loopLength != oldVal)
+ updateWindowTitle(MOD_IS_MODIFIED);
+ }
}
break;
@@ -4623,7 +4753,7 @@
case PTB_LENGTHU:
{
- if (modEntry->head.orderCount < 128)
+ if (song->header.numOrders < 128)
{
songLengthUpButton();
updateWindowTitle(MOD_IS_MODIFIED);
@@ -4633,7 +4763,7 @@
case PTB_LENGTHD:
{
- if (modEntry->head.orderCount > 1)
+ if (song->header.numOrders > 1)
{
songLengthDownButton();
updateWindowTitle(MOD_IS_MODIFIED);
@@ -4643,7 +4773,7 @@
case PTB_PATTERNU:
{
- if (modEntry->head.order[modEntry->currOrder] < 99)
+ if (song->header.order[song->currOrder] < 99)
{
patternUpButton();
updateWindowTitle(MOD_IS_MODIFIED);
@@ -4653,7 +4783,7 @@
case PTB_PATTERND:
{
- if (modEntry->head.order[modEntry->currOrder] > 0)
+ if (song->header.order[song->currOrder] > 0)
{
patternDownButton();
updateWindowTitle(MOD_IS_MODIFIED);
@@ -5083,8 +5213,8 @@
if (mouse.repeatCounter >= 2)
{
mouse.repeatCounter = 0;
- if (modEntry->currOrder > 0)
- modSetPos(modEntry->currOrder - 1, DONT_SET_ROW);
+ if (song->currOrder > 0)
+ modSetPos(song->currOrder - 1, DONT_SET_ROW);
}
}
break;
@@ -5094,8 +5224,8 @@
if (mouse.repeatCounter >= 2)
{
mouse.repeatCounter = 0;
- if (modEntry->currOrder < modEntry->head.orderCount-1)
- modSetPos(modEntry->currOrder + 1, DONT_SET_ROW);
+ if (song->currOrder < song->header.numOrders-1)
+ modSetPos(song->currOrder + 1, DONT_SET_ROW);
}
}
break;
--- a/src/pt2_palette.c
+++ b/src/pt2_palette.c
@@ -3,6 +3,7 @@
void setDefaultPalette(void)
{
+ // default ProTracker palette
video.palette[PAL_BACKGRD] = 0x000000;
video.palette[PAL_BORDER] = 0xBBBBBB;
video.palette[PAL_GENBKG] = 0x888888;
@@ -17,5 +18,6 @@
video.palette[PAL_MOUSE_1] = 0x444444;
video.palette[PAL_MOUSE_2] = 0x777777;
video.palette[PAL_MOUSE_3] = 0xAAAAAA;
+
video.palette[PAL_COLORKEY] = 0xC0FFEE;
}
--- a/src/pt2_palette.h
+++ b/src/pt2_palette.h
@@ -11,7 +11,6 @@
PAL_PATCURSOR = 5,
PAL_GENTXT = 6,
PAL_PATTXT = 7,
- // -----------------------------
PAL_SAMPLLINE = 8,
PAL_LOOPPIN = 9,
PAL_TEXTMARK = 10,
--- a/src/pt2_pat2smp.c
+++ b/src/pt2_pat2smp.c
@@ -23,6 +23,12 @@
ui.pat2SmpDialogShown = false;
+ if (editor.sampleZero)
+ {
+ statusNotSampleZero();
+ return;
+ }
+
editor.pat2SmpBuf = (int16_t *)malloc(MAX_SAMPLE_LEN * sizeof (int16_t));
if (editor.pat2SmpBuf == NULL)
{
@@ -30,19 +36,19 @@
return;
}
- int8_t oldRow = editor.songPlaying ? 0 : modEntry->currRow;
+ int8_t oldRow = editor.songPlaying ? 0 : song->currRow;
uint32_t oldSamplesPerTick = samplesPerTick;
editor.isSMPRendering = true; // this must be set before restartSong()
storeTempVariables();
restartSong();
- modEntry->row = oldRow;
- modEntry->currRow = modEntry->row;
+ song->row = oldRow;
+ song->currRow = song->row;
editor.blockMarkFlag = false;
pointerSetMode(POINTER_MODE_MSG2, NO_CARRY);
setStatusMessage("RENDERING...", NO_CARRY);
- modSetTempo(modEntry->currBPM);
+ modSetTempo(song->currBPM);
editor.pat2SmpPos = 0;
editor.smpRenderingDone = false;
@@ -57,21 +63,21 @@
resetSong();
// set back old row and samplesPerTick
- modEntry->row = oldRow;
- modEntry->currRow = modEntry->row;
+ song->row = oldRow;
+ song->currRow = song->row;
mixerSetSamplesPerTick(oldSamplesPerTick);
normalize16bitSigned(editor.pat2SmpBuf, MIN(editor.pat2SmpPos, MAX_SAMPLE_LEN));
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
// quantize to 8-bit
for (int32_t i = 0; i < editor.pat2SmpPos; i++)
- modEntry->sampleData[s->offset+i] = editor.pat2SmpBuf[i] >> 8;
+ song->sampleData[s->offset+i] = editor.pat2SmpBuf[i] >> 8;
// clear the rest of the sample
if (editor.pat2SmpPos < MAX_SAMPLE_LEN)
- memset(&modEntry->sampleData[s->offset+editor.pat2SmpPos], 0, MAX_SAMPLE_LEN - editor.pat2SmpPos);
+ memset(&song->sampleData[s->offset+editor.pat2SmpPos], 0, MAX_SAMPLE_LEN - editor.pat2SmpPos);
free(editor.pat2SmpBuf);
--- /dev/null
+++ b/src/pt2_pattern_viewer.c
@@ -1,0 +1,251 @@
+#include <stdint.h>
+#include <stdbool.h>
+#include "pt2_header.h"
+#include "pt2_palette.h"
+#include "pt2_tables.h"
+#include "pt2_textout.h"
+#include "pt2_structs.h"
+#include "pt2_config.h"
+
+#define MIDDLE_ROW 7
+#define VISIBLE_ROWS 15
+
+static const char *emptyRowNum = " ";
+static const char *emptyRowData = " ";
+static const char emptyDottedEffect[4] = { 0x02, 0x02, 0x02, 0x00 };
+static const char emptyDottedSample[3] = { 0x02, 0x02, 0x00 };
+
+static int32_t periodToNote(int32_t period) // 0 = no note, 1 = illegal note, 2..37 = note
+{
+ int32_t beg, end, tableVal;
+
+ if (period == 0)
+ return 0;
+
+ beg = 0;
+ end = 36 - 1;
+
+ // do binary search
+ while (beg <= end)
+ {
+ const int32_t mid = (beg + end) >> 1;
+
+ tableVal = periodTable[mid];
+ if (period == tableVal)
+ return 2+mid;
+
+ if (period < tableVal)
+ beg = mid+1;
+ else
+ end = mid-1;
+ }
+
+ return 1; // illegal note
+}
+
+static void drawPatternNormal(void)
+{
+ const char **noteNames;
+ char smpChar;
+ int32_t row, j, x, y;
+ note_t *patt, *note;
+
+ if (config.accidental)
+ noteNames = (const char **)noteNames2;
+ else
+ noteNames = (const char **)noteNames1;
+
+ patt = song->patterns[song->currPattern];
+ row = song->currRow - MIDDLE_ROW;
+ y = 140;
+
+ for (int32_t i = 0; i < VISIBLE_ROWS; i++, y += 7, row++)
+ {
+ if (row < 0 || row >= MOD_ROWS)
+ {
+ // clear empty rows outside of pattern data
+ textOutBg(8, y, emptyRowNum, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
+ textOutBg(32+(0*72), y, emptyRowData, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
+ textOutBg(32+(1*72), y, emptyRowData, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
+ textOutBg(32+(2*72), y, emptyRowData, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
+ textOutBg(32+(3*72), y, emptyRowData, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
+ }
+ else
+ {
+ if (i == MIDDLE_ROW) // middle row has twice as tall glyphs
+ {
+ y++;
+ printTwoDecimalsBigBg(8, y, row, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
+
+ note = patt + (row << 2);
+ x = 32;
+ for (j = 0; j < AMIGA_VOICES; j++, note++)
+ {
+ textOutBigBg(x, y, noteNames[periodToNote(note->period)], video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
+ x += 8*3;
+
+ smpChar = (config.blankZeroFlag && !(note->sample & 0xF0)) ? ' ' : hexTable[note->sample >> 4];
+ charOutBigBg(x, y, smpChar, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
+ x += 8;
+
+ printOneHexBigBg(x, y, note->sample & 0x0F, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
+ x += 8;
+
+ printOneHexBigBg(x, y, note->command, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
+ x += 8;
+
+ printTwoHexBigBg(x, y, note->param, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
+ x += (8*2)+8;
+ }
+ y += 6;
+ }
+ else // non-middle rows
+ {
+ printTwoDecimalsBg(8, y, row, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
+
+ note = patt + (row << 2);
+ x = 32;
+ for (j = 0; j < AMIGA_VOICES; j++, note++)
+ {
+ textOutBg(x, y, noteNames[periodToNote(note->period)], video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
+ x += 8*3;
+
+ smpChar = (config.blankZeroFlag && !(note->sample & 0xF0)) ? ' ' : hexTable[note->sample >> 4];
+ charOutBg(x, y, smpChar, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
+ x += 8;
+
+ printOneHexBg(x , y, note->sample & 0x0F, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
+ x += 8;
+
+ printOneHexBg(x, y, note->command, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
+ x += 8;
+
+ printTwoHexBg(x, y, note->param, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
+ x += (8*2)+8;
+ }
+ }
+ }
+ }
+}
+
+static void drawPatternDotted(void)
+{
+ char smpChar;
+ const char **noteNames;
+ int32_t row, j, x, y;
+ note_t *patt, *note;
+
+ if (config.accidental)
+ noteNames = (const char **)noteNames4;
+ else
+ noteNames = (const char **)noteNames3;
+
+ patt = song->patterns[song->currPattern];
+ row = song->currRow - MIDDLE_ROW;
+ y = 140;
+
+ for (int32_t i = 0; i < VISIBLE_ROWS; i++, y += 7, row++)
+ {
+ if (row < 0 || row >= MOD_ROWS)
+ {
+ // clear empty rows outside of pattern data
+ textOutBg(8, y, emptyRowNum, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
+ textOutBg(32+(0*72), y, emptyRowData, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
+ textOutBg(32+(1*72), y, emptyRowData, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
+ textOutBg(32+(2*72), y, emptyRowData, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
+ textOutBg(32+(3*72), y, emptyRowData, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
+ }
+ else
+ {
+ if (i == MIDDLE_ROW) // middle row has twice as tall glyphs
+ {
+ y++;
+ printTwoDecimalsBigBg(8, y, row, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
+
+ note = patt + (row << 2);
+ x = 32;
+ for (j = 0; j < AMIGA_VOICES; j++, note++)
+ {
+ textOutBigBg(x, y, noteNames[periodToNote(note->period)], video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
+ x += 8*3;
+
+ if (note->sample == 0)
+ {
+ textOutBigBg(x, y, emptyDottedSample, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
+ x += 8*2;
+ }
+ else
+ {
+ smpChar = (note->sample & 0xF0) ? hexTable[note->sample >> 4] : 0x02;
+ charOutBigBg(x, y, smpChar, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
+ x += 8;
+ printOneHexBigBg(x, y, note->sample & 0x0F, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
+ x += 8;
+ }
+
+ if (note->command == 0 && note->param == 0)
+ {
+ textOutBigBg(x, y, emptyDottedEffect, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
+ x += (8*3)+8;
+ }
+ else
+ {
+ printOneHexBigBg(x, y, note->command, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
+ x += 8;
+ printTwoHexBigBg(x, y, note->param, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
+ x += (8*2)+8;
+ }
+ }
+ y += 6;
+ }
+ else // non-middle rows
+ {
+ printTwoDecimalsBg(8, y, row, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
+
+ // pattern data
+ note = patt + (row << 2);
+ x = 32;
+ for (j = 0; j < AMIGA_VOICES; j++, note++)
+ {
+ textOutBg(x, y, noteNames[periodToNote(note->period)], video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
+ x += 8*3;
+
+ if (note->sample == 0)
+ {
+ textOutBg(x, y, emptyDottedSample, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
+ x += 8*2;
+ }
+ else
+ {
+ smpChar = (note->sample & 0xF0) ? hexTable[note->sample >> 4] : 0x02;
+ charOutBg(x, y, smpChar, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
+ x += 8;
+ printOneHexBg(x, y, note->sample & 0x0F, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
+ x += 8;
+ }
+
+ if (note->command == 0 && note->param == 0)
+ {
+ textOutBg(x, y, emptyDottedEffect, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
+ x += (8*3)+8;
+ }
+ else
+ {
+ printOneHexBg(x, y, note->command, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
+ x += 8;
+ printTwoHexBg(x, y, note->param, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
+ x += (8*2)+8;
+ }
+ }
+ }
+ }
+ }
+}
+
+void redrawPattern(void)
+{
+ if (config.pattDots)
+ drawPatternDotted();
+ else
+ drawPatternNormal();
+}
--- /dev/null
+++ b/src/pt2_pattern_viewer.h
@@ -1,0 +1,3 @@
+#pragma once
+
+void redrawPattern(void);
--- a/src/pt2_patternviewer.c
+++ /dev/null
@@ -1,250 +1,0 @@
-#include <stdint.h>
-#include <stdbool.h>
-#include "pt2_header.h"
-#include "pt2_palette.h"
-#include "pt2_tables.h"
-#include "pt2_textout.h"
-#include "pt2_structs.h"
-
-#define MIDDLE_ROW 7
-#define VISIBLE_ROWS 15
-
-static const char *emptyRowNum = " ";
-static const char *emptyRowData = " ";
-static const char emptyDottedEffect[4] = { 0x02, 0x02, 0x02, 0x00 };
-static const char emptyDottedSample[3] = { 0x02, 0x02, 0x00 };
-
-static int32_t periodToNote(int32_t period) // 0 = no note, 1 = illegal note, 2..37 = note
-{
- int32_t beg, end, tableVal;
-
- if (period == 0)
- return 0;
-
- beg = 0;
- end = 36 - 1;
-
- // do binary search
- while (beg <= end)
- {
- const int32_t mid = (beg + end) >> 1;
-
- tableVal = periodTable[mid];
- if (period == tableVal)
- return 2+mid;
-
- if (period < tableVal)
- beg = mid+1;
- else
- end = mid-1;
- }
-
- return 1; // illegal note
-}
-
-static void drawPatternNormal(void)
-{
- const char **noteNames;
- char smpChar;
- int32_t row, j, x, y;
- note_t *patt, *note;
-
- if (config.accidental)
- noteNames = (const char **)noteNames2;
- else
- noteNames = (const char **)noteNames1;
-
- patt = modEntry->patterns[modEntry->currPattern];
- row = modEntry->currRow - MIDDLE_ROW;
- y = 140;
-
- for (int32_t i = 0; i < VISIBLE_ROWS; i++, y += 7, row++)
- {
- if (row < 0 || row >= MOD_ROWS)
- {
- // clear empty rows outside of pattern data
- textOutBg(8, y, emptyRowNum, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
- textOutBg(32+(0*72), y, emptyRowData, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
- textOutBg(32+(1*72), y, emptyRowData, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
- textOutBg(32+(2*72), y, emptyRowData, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
- textOutBg(32+(3*72), y, emptyRowData, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
- }
- else
- {
- if (i == MIDDLE_ROW) // middle row has twice as tall glyphs
- {
- y++;
- printTwoDecimalsBigBg(8, y, row, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
-
- note = patt + (row << 2);
- x = 32;
- for (j = 0; j < AMIGA_VOICES; j++, note++)
- {
- textOutBigBg(x, y, noteNames[periodToNote(note->period)], video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
- x += 8*3;
-
- smpChar = (config.blankZeroFlag && !(note->sample & 0xF0)) ? ' ' : hexTable[note->sample >> 4];
- charOutBigBg(x, y, smpChar, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
- x += 8;
-
- printOneHexBigBg(x, y, note->sample & 0x0F, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
- x += 8;
-
- printOneHexBigBg(x, y, note->command, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
- x += 8;
-
- printTwoHexBigBg(x, y, note->param, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
- x += (8*2)+8;
- }
- y += 6;
- }
- else // non-middle rows
- {
- printTwoDecimalsBg(8, y, row, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
-
- note = patt + (row << 2);
- x = 32;
- for (j = 0; j < AMIGA_VOICES; j++, note++)
- {
- textOutBg(x, y, noteNames[periodToNote(note->period)], video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
- x += 8*3;
-
- smpChar = (config.blankZeroFlag && !(note->sample & 0xF0)) ? ' ' : hexTable[note->sample >> 4];
- charOutBg(x, y, smpChar, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
- x += 8;
-
- printOneHexBg(x , y, note->sample & 0x0F, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
- x += 8;
-
- printOneHexBg(x, y, note->command, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
- x += 8;
-
- printTwoHexBg(x, y, note->param, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
- x += (8*2)+8;
- }
- }
- }
- }
-}
-
-static void drawPatternDotted(void)
-{
- char smpChar;
- const char **noteNames;
- int32_t row, j, x, y;
- note_t *patt, *note;
-
- if (config.accidental)
- noteNames = (const char **)noteNames4;
- else
- noteNames = (const char **)noteNames3;
-
- patt = modEntry->patterns[modEntry->currPattern];
- row = modEntry->currRow - MIDDLE_ROW;
- y = 140;
-
- for (int32_t i = 0; i < VISIBLE_ROWS; i++, y += 7, row++)
- {
- if (row < 0 || row >= MOD_ROWS)
- {
- // clear empty rows outside of pattern data
- textOutBg(8, y, emptyRowNum, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
- textOutBg(32+(0*72), y, emptyRowData, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
- textOutBg(32+(1*72), y, emptyRowData, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
- textOutBg(32+(2*72), y, emptyRowData, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
- textOutBg(32+(3*72), y, emptyRowData, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
- }
- else
- {
- if (i == MIDDLE_ROW) // middle row has twice as tall glyphs
- {
- y++;
- printTwoDecimalsBigBg(8, y, row, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
-
- note = patt + (row << 2);
- x = 32;
- for (j = 0; j < AMIGA_VOICES; j++, note++)
- {
- textOutBigBg(x, y, noteNames[periodToNote(note->period)], video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
- x += 8*3;
-
- if (note->sample == 0)
- {
- textOutBigBg(x, y, emptyDottedSample, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
- x += 8*2;
- }
- else
- {
- smpChar = (note->sample & 0xF0) ? hexTable[note->sample >> 4] : 0x02;
- charOutBigBg(x, y, smpChar, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
- x += 8;
- printOneHexBigBg(x, y, note->sample & 0x0F, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
- x += 8;
- }
-
- if (note->command == 0 && note->param == 0)
- {
- textOutBigBg(x, y, emptyDottedEffect, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
- x += (8*3)+8;
- }
- else
- {
- printOneHexBigBg(x, y, note->command, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
- x += 8;
- printTwoHexBigBg(x, y, note->param, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
- x += (8*2)+8;
- }
- }
- y += 6;
- }
- else // non-middle rows
- {
- printTwoDecimalsBg(8, y, row, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
-
- // pattern data
- note = patt + (row << 2);
- x = 32;
- for (j = 0; j < AMIGA_VOICES; j++, note++)
- {
- textOutBg(x, y, noteNames[periodToNote(note->period)], video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
- x += 8*3;
-
- if (note->sample == 0)
- {
- textOutBg(x, y, emptyDottedSample, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
- x += 8*2;
- }
- else
- {
- smpChar = (note->sample & 0xF0) ? hexTable[note->sample >> 4] : 0x02;
- charOutBg(x, y, smpChar, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
- x += 8;
- printOneHexBg(x, y, note->sample & 0x0F, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
- x += 8;
- }
-
- if (note->command == 0 && note->param == 0)
- {
- textOutBg(x, y, emptyDottedEffect, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
- x += (8*3)+8;
- }
- else
- {
- printOneHexBg(x, y, note->command, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
- x += 8;
- printTwoHexBg(x, y, note->param, video.palette[PAL_PATTXT], video.palette[PAL_BACKGRD]);
- x += (8*2)+8;
- }
- }
- }
- }
- }
-}
-
-void redrawPattern(void)
-{
- if (config.pattDots)
- drawPatternDotted();
- else
- drawPatternNormal();
-}
--- a/src/pt2_patternviewer.h
+++ /dev/null
@@ -1,3 +1,0 @@
-#pragma once
-
-void redrawPattern(void);
--- /dev/null
+++ b/src/pt2_replayer.c
@@ -1,0 +1,1549 @@
+// C port of ProTracker 2.3D's replayer by 8bitbubsy, slightly modified.
+
+// for finding memory leaks in debug mode with Visual Studio
+#if defined _DEBUG && defined _MSC_VER
+#include <crtdbg.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <math.h>
+#include "pt2_header.h"
+#include "pt2_audio.h"
+#include "pt2_helpers.h"
+#include "pt2_palette.h"
+#include "pt2_tables.h"
+#include "pt2_module_loader.h"
+#include "pt2_config.h"
+#include "pt2_sampler.h"
+#include "pt2_visuals.h"
+#include "pt2_textout.h"
+#include "pt2_scopes.h"
+
+static bool posJumpAssert, pBreakFlag, updateUIPositions, modHasBeenPlayed;
+static int8_t pBreakPosition, oldRow, modPattern;
+static uint8_t pattDelTime, setBPMFlag, lowMask = 0xFF, pattDelTime2, oldSpeed;
+static int16_t modOrder, oldPattern, oldOrder;
+static uint16_t modBPM, oldBPM;
+
+static const int8_t vuMeterHeights[65] =
+{
+ 0, 0, 1, 2, 2, 3, 4, 5,
+ 5, 6, 7, 8, 8, 9, 10, 11,
+ 11, 12, 13, 14, 14, 15, 16, 17,
+ 17, 18, 19, 20, 20, 21, 22, 23,
+ 23, 24, 25, 26, 26, 27, 28, 29,
+ 29, 30, 31, 32, 32, 33, 34, 35,
+ 35, 36, 37, 38, 38, 39, 40, 41,
+ 41, 42, 43, 44, 44, 45, 46, 47,
+ 47
+};
+
+static const uint8_t funkTable[16] = // EFx (FunkRepeat/InvertLoop)
+{
+ 0x00, 0x05, 0x06, 0x07, 0x08, 0x0A, 0x0B, 0x0D,
+ 0x10, 0x13, 0x16, 0x1A, 0x20, 0x2B, 0x40, 0x80
+};
+
+void modSetSpeed(uint8_t speed)
+{
+ song->speed = speed;
+ song->currSpeed = speed;
+ song->tick = 0;
+}
+
+void doStopIt(bool resetPlayMode)
+{
+ editor.songPlaying = false;
+
+ resetCachedMixerPeriod();
+
+ pattDelTime = 0;
+ pattDelTime2 = 0;
+
+ if (resetPlayMode)
+ {
+ editor.playMode = PLAY_MODE_NORMAL;
+ editor.currMode = MODE_IDLE;
+
+ pointerSetMode(POINTER_MODE_IDLE, DO_CARRY);
+ }
+
+ for (int32_t i = 0; i < AMIGA_VOICES; i++)
+ {
+ moduleChannel_t *c = &song->channels[i];
+
+ c->n_wavecontrol = 0;
+ c->n_glissfunk = 0;
+ c->n_finetune = 0;
+ c->n_loopcount = 0;
+ }
+}
+
+void setPattern(int16_t pattern)
+{
+ if (pattern > MAX_PATTERNS-1)
+ pattern = MAX_PATTERNS-1;
+
+ song->currPattern = modPattern = (int8_t)pattern;
+}
+
+void storeTempVariables(void) // this one is accessed in other files, so non-static
+{
+ oldBPM = song->currBPM;
+ oldRow = song->currRow;
+ oldOrder = song->currOrder;
+ oldSpeed = song->currSpeed;
+ oldPattern = song->currPattern;
+}
+
+static void setVUMeterHeight(moduleChannel_t *ch)
+{
+ uint8_t vol;
+
+ if (editor.muted[ch->n_chanindex])
+ return;
+
+ vol = ch->n_volume;
+ if ((ch->n_cmd & 0xF00) == 0xC00) // handle Cxx effect
+ vol = ch->n_cmd & 0xFF;
+
+ if (vol > 64)
+ vol = 64;
+
+ editor.vuMeterVolumes[ch->n_chanindex] = vuMeterHeights[vol];
+}
+
+static void updateFunk(moduleChannel_t *ch)
+{
+ const int8_t funkSpeed = ch->n_glissfunk >> 4;
+ if (funkSpeed == 0)
+ return;
+
+ ch->n_funkoffset += funkTable[funkSpeed];
+ if (ch->n_funkoffset >= 128)
+ {
+ ch->n_funkoffset = 0;
+
+ if (ch->n_loopstart != NULL && ch->n_wavestart != NULL) // non-PT2 bug fix
+ {
+ if (++ch->n_wavestart >= ch->n_loopstart+ch->n_replen)
+ ch->n_wavestart = ch->n_loopstart;
+
+ *ch->n_wavestart = -1 - *ch->n_wavestart;
+ }
+ }
+}
+
+static void setGlissControl(moduleChannel_t *ch)
+{
+ ch->n_glissfunk = (ch->n_glissfunk & 0xF0) | (ch->n_cmd & 0x0F);
+}
+
+static void setVibratoControl(moduleChannel_t *ch)
+{
+ ch->n_wavecontrol = (ch->n_wavecontrol & 0xF0) | (ch->n_cmd & 0x0F);
+}
+
+static void setFineTune(moduleChannel_t *ch)
+{
+ ch->n_finetune = ch->n_cmd & 0xF;
+}
+
+static void jumpLoop(moduleChannel_t *ch)
+{
+ uint8_t tempParam;
+
+ if (song->tick != 0)
+ return;
+
+ if ((ch->n_cmd & 0xF) == 0)
+ {
+ ch->n_pattpos = song->row;
+ }
+ else
+ {
+ if (ch->n_loopcount == 0)
+ {
+ ch->n_loopcount = ch->n_cmd & 0xF;
+ }
+ else if (--ch->n_loopcount == 0)
+ {
+ return;
+ }
+
+ pBreakPosition = ch->n_pattpos;
+ pBreakFlag = true;
+
+ // stuff used for MOD2WAV to determine if the song has reached its end
+ if (editor.isWAVRendering)
+ {
+ for (tempParam = pBreakPosition; tempParam <= song->row; tempParam++)
+ editor.rowVisitTable[(modOrder * MOD_ROWS) + tempParam] = false;
+ }
+ }
+}
+
+static void setTremoloControl(moduleChannel_t *ch)
+{
+ ch->n_wavecontrol = ((ch->n_cmd & 0xF) << 4) | (ch->n_wavecontrol & 0xF);
+}
+
+static void karplusStrong(moduleChannel_t *ch)
+{
+ /* This effect is definitely the least used PT effect there is!
+ ** It trashes (filters) the sample data.
+ ** The reason I'm not implementing it is because a lot of songs used
+ ** E8x for syncing to demos/intros, and because I have never ever
+ ** seen this effect being used intentionally.
+ */
+
+ (void)ch;
+}
+
+static void doRetrg(moduleChannel_t *ch)
+{
+ paulaSetData(ch->n_chanindex, ch->n_start); // n_start is increased on 9xx
+ paulaSetLength(ch->n_chanindex, ch->n_length);
+ paulaSetPeriod(ch->n_chanindex, ch->n_period);
+ paulaStartDMA(ch->n_chanindex);
+
+ // these take effect after the current DMA cycle is done
+ paulaSetData(ch->n_chanindex, ch->n_loopstart);
+ paulaSetLength(ch->n_chanindex, ch->n_replen);
+
+ updateSpectrumAnalyzer(ch->n_volume, ch->n_period);
+ setVUMeterHeight(ch);
+}
+
+static void retrigNote(moduleChannel_t *ch)
+{
+ if ((ch->n_cmd & 0xF) > 0)
+ {
+ if (song->tick == 0 && (ch->n_note & 0xFFF) > 0)
+ return;
+
+ if (song->tick % (ch->n_cmd & 0xF) == 0)
+ doRetrg(ch);
+ }
+}
+
+static void volumeSlide(moduleChannel_t *ch)
+{
+ uint8_t cmd = ch->n_cmd & 0xFF;
+
+ if ((cmd & 0xF0) == 0)
+ {
+ ch->n_volume -= cmd & 0x0F;
+ if (ch->n_volume < 0)
+ ch->n_volume = 0;
+ }
+ else
+ {
+ ch->n_volume += cmd >> 4;
+ if (ch->n_volume > 64)
+ ch->n_volume = 64;
+ }
+}
+
+static void volumeFineUp(moduleChannel_t *ch)
+{
+ if (song->tick == 0)
+ {
+ ch->n_volume += ch->n_cmd & 0xF;
+ if (ch->n_volume > 64)
+ ch->n_volume = 64;
+ }
+}
+
+static void volumeFineDown(moduleChannel_t *ch)
+{
+ if (song->tick == 0)
+ {
+ ch->n_volume -= ch->n_cmd & 0xF;
+ if (ch->n_volume < 0)
+ ch->n_volume = 0;
+ }
+}
+
+static void noteCut(moduleChannel_t *ch)
+{
+ if (song->tick == (ch->n_cmd & 0xF))
+ ch->n_volume = 0;
+}
+
+static void noteDelay(moduleChannel_t *ch)
+{
+ if (song->tick == (ch->n_cmd & 0xF) && (ch->n_note & 0xFFF) > 0)
+ doRetrg(ch);
+}
+
+static void patternDelay(moduleChannel_t *ch)
+{
+ if (song->tick == 0 && pattDelTime2 == 0)
+ pattDelTime = (ch->n_cmd & 0xF) + 1;
+}
+
+static void funkIt(moduleChannel_t *ch)
+{
+ if (song->tick == 0)
+ {
+ ch->n_glissfunk = ((ch->n_cmd & 0xF) << 4) | (ch->n_glissfunk & 0xF);
+ if ((ch->n_glissfunk & 0xF0) > 0)
+ updateFunk(ch);
+ }
+}
+
+static void positionJump(moduleChannel_t *ch)
+{
+ modOrder = (ch->n_cmd & 0xFF) - 1; // 0xFF (B00) jumps to pat 0
+ pBreakPosition = 0;
+ posJumpAssert = true;
+}
+
+static void volumeChange(moduleChannel_t *ch)
+{
+ ch->n_volume = ch->n_cmd & 0xFF;
+ if ((uint8_t)ch->n_volume > 64)
+ ch->n_volume = 64;
+}
+
+static void patternBreak(moduleChannel_t *ch)
+{
+ pBreakPosition = (((ch->n_cmd & 0xF0) >> 4) * 10) + (ch->n_cmd & 0x0F);
+ if ((uint8_t)pBreakPosition > 63)
+ pBreakPosition = 0;
+
+ posJumpAssert = true;
+}
+
+static void setSpeed(moduleChannel_t *ch)
+{
+ if ((ch->n_cmd & 0xFF) > 0)
+ {
+ song->tick = 0;
+
+ if (editor.timingMode == TEMPO_MODE_VBLANK || (ch->n_cmd & 0xFF) < 32)
+ modSetSpeed(ch->n_cmd & 0xFF);
+ else
+ setBPMFlag = ch->n_cmd & 0xFF; // CIA doesn't refresh its registers until the next interrupt, so change it later
+ }
+ else
+ {
+ editor.songPlaying = false;
+ editor.playMode = PLAY_MODE_NORMAL;
+ editor.currMode = MODE_IDLE;
+
+ pointerSetMode(POINTER_MODE_IDLE, DO_CARRY);
+ }
+}
+
+static void arpeggio(moduleChannel_t *ch)
+{
+ uint8_t arpTick, arpNote;
+ const int16_t *periods;
+
+ assert(song->tick < 32);
+ arpTick = arpTickTable[song->tick]; // 0, 1, 2
+
+ if (arpTick == 1)
+ {
+ arpNote = (uint8_t)(ch->n_cmd >> 4);
+ }
+ else if (arpTick == 2)
+ {
+ arpNote = ch->n_cmd & 0xF;
+ }
+ else // arpTick 0
+ {
+ paulaSetPeriod(ch->n_chanindex, ch->n_period);
+ return;
+ }
+
+ /* 8bitbubsy: If the finetune is -1, this can overflow up to
+ ** 15 words outside of the table. The table is padded with
+ ** the correct overflow values to allow this to safely happen
+ ** and sound correct at the same time.
+ */
+ periods = &periodTable[ch->n_finetune * 37];
+ for (int32_t baseNote = 0; baseNote < 37; baseNote++)
+ {
+ if (ch->n_period >= periods[baseNote])
+ {
+ paulaSetPeriod(ch->n_chanindex, periods[baseNote+arpNote]);
+ break;
+ }
+ }
+}
+
+static void portaUp(moduleChannel_t *ch)
+{
+ ch->n_period -= (ch->n_cmd & 0xFF) & lowMask;
+ lowMask = 0xFF;
+
+ if ((ch->n_period & 0xFFF) < 113)
+ ch->n_period = (ch->n_period & 0xF000) | 113;
+
+ paulaSetPeriod(ch->n_chanindex, ch->n_period & 0xFFF);
+}
+
+static void portaDown(moduleChannel_t *ch)
+{
+ ch->n_period += (ch->n_cmd & 0xFF) & lowMask;
+ lowMask = 0xFF;
+
+ if ((ch->n_period & 0xFFF) > 856)
+ ch->n_period = (ch->n_period & 0xF000) | 856;
+
+ paulaSetPeriod(ch->n_chanindex, ch->n_period & 0xFFF);
+}
+
+static void filterOnOff(moduleChannel_t *ch)
+{
+ setLEDFilter(!(ch->n_cmd & 1));
+}
+
+static void finePortaUp(moduleChannel_t *ch)
+{
+ if (song->tick == 0)
+ {
+ lowMask = 0xF;
+ portaUp(ch);
+ }
+}
+
+static void finePortaDown(moduleChannel_t *ch)
+{
+ if (song->tick == 0)
+ {
+ lowMask = 0xF;
+ portaDown(ch);
+ }
+}
+
+static void setTonePorta(moduleChannel_t *ch)
+{
+ uint8_t i;
+ const int16_t *portaPointer;
+ uint16_t note;
+
+ note = ch->n_note & 0xFFF;
+ portaPointer = &periodTable[ch->n_finetune * 37];
+
+ i = 0;
+ while (true)
+ {
+ // portaPointer[36] = 0, so i=36 is safe
+ if (note >= portaPointer[i])
+ break;
+
+ if (++i >= 37)
+ {
+ i = 35;
+ break;
+ }
+ }
+
+ if ((ch->n_finetune & 8) && i > 0)
+ i--;
+
+ ch->n_wantedperiod = portaPointer[i];
+ ch->n_toneportdirec = 0;
+
+ if (ch->n_period == ch->n_wantedperiod) ch->n_wantedperiod = 0;
+ else if (ch->n_period > ch->n_wantedperiod) ch->n_toneportdirec = 1;
+}
+
+static void tonePortNoChange(moduleChannel_t *ch)
+{
+ uint8_t i;
+ const int16_t *portaPointer;
+
+ if (ch->n_wantedperiod <= 0)
+ return;
+
+ if (ch->n_toneportdirec > 0)
+ {
+ ch->n_period -= ch->n_toneportspeed;
+ if (ch->n_period <= ch->n_wantedperiod)
+ {
+ ch->n_period = ch->n_wantedperiod;
+ ch->n_wantedperiod = 0;
+ }
+ }
+ else
+ {
+ ch->n_period += ch->n_toneportspeed;
+ if (ch->n_period >= ch->n_wantedperiod)
+ {
+ ch->n_period = ch->n_wantedperiod;
+ ch->n_wantedperiod = 0;
+ }
+ }
+
+ if ((ch->n_glissfunk & 0xF) == 0)
+ {
+ paulaSetPeriod(ch->n_chanindex, ch->n_period);
+ }
+ else
+ {
+ portaPointer = &periodTable[ch->n_finetune * 37];
+
+ i = 0;
+ while (true)
+ {
+ // portaPointer[36] = 0, so i=36 is safe
+ if (ch->n_period >= portaPointer[i])
+ break;
+
+ if (++i >= 37)
+ {
+ i = 35;
+ break;
+ }
+ }
+
+ paulaSetPeriod(ch->n_chanindex, portaPointer[i]);
+ }
+}
+
+static void tonePortamento(moduleChannel_t *ch)
+{
+ if ((ch->n_cmd & 0xFF) > 0)
+ {
+ ch->n_toneportspeed = ch->n_cmd & 0xFF;
+ ch->n_cmd &= 0xFF00;
+ }
+
+ tonePortNoChange(ch);
+}
+
+static void vibratoNoChange(moduleChannel_t *ch)
+{
+ uint8_t vibratoTemp;
+ int16_t vibratoData;
+
+ vibratoTemp = (ch->n_vibratopos / 4) & 31;
+ vibratoData = ch->n_wavecontrol & 3;
+
+ if (vibratoData == 0)
+ {
+ vibratoData = vibratoTable[vibratoTemp];
+ }
+ else
+ {
+ if (vibratoData == 1)
+ {
+ if (ch->n_vibratopos < 0)
+ vibratoData = 255 - (vibratoTemp * 8);
+ else
+ vibratoData = vibratoTemp * 8;
+ }
+ else
+ {
+ vibratoData = 255;
+ }
+ }
+
+ vibratoData = (vibratoData * (ch->n_vibratocmd & 0xF)) / 128;
+
+ if (ch->n_vibratopos < 0)
+ vibratoData = ch->n_period - vibratoData;
+ else
+ vibratoData = ch->n_period + vibratoData;
+
+ paulaSetPeriod(ch->n_chanindex, vibratoData);
+
+ ch->n_vibratopos += ((ch->n_vibratocmd >> 4) * 4);
+}
+
+static void vibrato(moduleChannel_t *ch)
+{
+ if ((ch->n_cmd & 0xFF) > 0)
+ {
+ if ((ch->n_cmd & 0x0F) > 0)
+ ch->n_vibratocmd = (ch->n_vibratocmd & 0xF0) | (ch->n_cmd & 0x0F);
+
+ if ((ch->n_cmd & 0xF0) > 0)
+ ch->n_vibratocmd = (ch->n_cmd & 0xF0) | (ch->n_vibratocmd & 0x0F);
+ }
+
+ vibratoNoChange(ch);
+}
+
+static void tonePlusVolSlide(moduleChannel_t *ch)
+{
+ tonePortNoChange(ch);
+ volumeSlide(ch);
+}
+
+static void vibratoPlusVolSlide(moduleChannel_t *ch)
+{
+ vibratoNoChange(ch);
+ volumeSlide(ch);
+}
+
+static void tremolo(moduleChannel_t *ch)
+{
+ int8_t tremoloTemp;
+ int16_t tremoloData;
+
+ if ((ch->n_cmd & 0xFF) > 0)
+ {
+ if ((ch->n_cmd & 0x0F) > 0)
+ ch->n_tremolocmd = (ch->n_tremolocmd & 0xF0) | (ch->n_cmd & 0x0F);
+
+ if ((ch->n_cmd & 0xF0) > 0)
+ ch->n_tremolocmd = (ch->n_cmd & 0xF0) | (ch->n_tremolocmd & 0x0F);
+ }
+
+ tremoloTemp = (ch->n_tremolopos / 4) & 31;
+ tremoloData = (ch->n_wavecontrol >> 4) & 3;
+
+ if (!tremoloData)
+ {
+ tremoloData = vibratoTable[tremoloTemp];
+ }
+ else
+ {
+ if (tremoloData == 1)
+ {
+ if (ch->n_vibratopos < 0) // PT bug, should've been n_tremolopos
+ tremoloData = 255 - (tremoloTemp * 8);
+ else
+ tremoloData = tremoloTemp * 8;
+ }
+ else
+ {
+ tremoloData = 255;
+ }
+ }
+
+ tremoloData = (tremoloData * (ch->n_tremolocmd & 0xF)) / 64;
+
+ if (ch->n_tremolopos < 0)
+ {
+ tremoloData = ch->n_volume - tremoloData;
+ if (tremoloData < 0)
+ tremoloData = 0;
+ }
+ else
+ {
+ tremoloData = ch->n_volume + tremoloData;
+ if (tremoloData > 64)
+ tremoloData = 64;
+ }
+
+ paulaSetVolume(ch->n_chanindex, tremoloData);
+
+ ch->n_tremolopos += (ch->n_tremolocmd >> 4) * 4;
+}
+
+static void sampleOffset(moduleChannel_t *ch)
+{
+ uint16_t newOffset;
+
+ if ((ch->n_cmd & 0xFF) > 0)
+ ch->n_sampleoffset = ch->n_cmd & 0xFF;
+
+ newOffset = ch->n_sampleoffset << 7;
+
+ if ((int16_t)newOffset < (int16_t)ch->n_length)
+ {
+ ch->n_length -= newOffset;
+ ch->n_start += newOffset*2;
+ }
+ else
+ {
+ ch->n_length = 1;
+ }
+}
+
+static void E_Commands(moduleChannel_t *ch)
+{
+ uint8_t cmd;
+
+ cmd = (ch->n_cmd & 0xF0) >> 4;
+ switch (cmd)
+ {
+ case 0x0: filterOnOff(ch); break;
+ case 0x1: finePortaUp(ch); break;
+ case 0x2: finePortaDown(ch); break;
+ case 0x3: setGlissControl(ch); break;
+ case 0x4: setVibratoControl(ch); break;
+ case 0x5: setFineTune(ch); break;
+ case 0x6: jumpLoop(ch); break;
+ case 0x7: setTremoloControl(ch); break;
+ case 0x8: karplusStrong(ch); break;
+ default: break;
+ }
+
+ if (editor.muted[ch->n_chanindex])
+ return;
+
+ switch (cmd)
+ {
+ case 0x9: retrigNote(ch); break;
+ case 0xA: volumeFineUp(ch); break;
+ case 0xB: volumeFineDown(ch); break;
+ case 0xC: noteCut(ch); break;
+ case 0xD: noteDelay(ch); break;
+ case 0xE: patternDelay(ch); break;
+ case 0xF: funkIt(ch); break;
+ default: break;
+ }
+}
+
+static void checkMoreEffects(moduleChannel_t *ch)
+{
+ switch ((ch->n_cmd & 0xF00) >> 8)
+ {
+ case 0x9: sampleOffset(ch); break;
+ case 0xB: positionJump(ch); break;
+
+ case 0xC:
+ {
+ if (!editor.muted[ch->n_chanindex])
+ volumeChange(ch);
+ }
+ break;
+
+ case 0xD: patternBreak(ch); break;
+ case 0xE: E_Commands(ch); break;
+ case 0xF: setSpeed(ch); break;
+
+ default:
+ {
+ if (!editor.muted[ch->n_chanindex])
+ paulaSetPeriod(ch->n_chanindex, ch->n_period);
+ }
+ break;
+ }
+}
+
+static void checkEffects(moduleChannel_t *ch)
+{
+ uint8_t effect;
+
+ if (editor.muted[ch->n_chanindex])
+ return;
+
+ updateFunk(ch);
+
+ effect = (ch->n_cmd & 0xF00) >> 8;
+
+ if ((ch->n_cmd & 0xFFF) > 0)
+ {
+ switch (effect)
+ {
+ case 0x0: arpeggio(ch); break;
+ case 0x1: portaUp(ch); break;
+ case 0x2: portaDown(ch); break;
+ case 0x3: tonePortamento(ch); break;
+ case 0x4: vibrato(ch); break;
+ case 0x5: tonePlusVolSlide(ch); break;
+ case 0x6: vibratoPlusVolSlide(ch); break;
+ case 0xE: E_Commands(ch); break;
+
+ case 0x7:
+ {
+ paulaSetPeriod(ch->n_chanindex, ch->n_period);
+ tremolo(ch);
+ }
+ break;
+
+ case 0xA:
+ {
+ paulaSetPeriod(ch->n_chanindex, ch->n_period);
+ volumeSlide(ch);
+ }
+ break;
+
+ default: paulaSetPeriod(ch->n_chanindex, ch->n_period); break;
+ }
+ }
+
+ if (effect != 0x7)
+ paulaSetVolume(ch->n_chanindex, ch->n_volume);
+}
+
+static void setPeriod(moduleChannel_t *ch)
+{
+ uint8_t i;
+ uint16_t note;
+
+ note = ch->n_note & 0xFFF;
+ for (i = 0; i < 37; i++)
+ {
+ // periodTable[36] = 0, so i=36 is safe
+ if (note >= periodTable[i])
+ break;
+ }
+
+ // BUG: yes it's 'safe' if i=37 because of padding at the end of period table
+ ch->n_period = periodTable[(ch->n_finetune * 37) + i];
+
+ if ((ch->n_cmd & 0xFF0) != 0xED0) // no note delay
+ {
+ if ((ch->n_wavecontrol & 0x04) == 0) ch->n_vibratopos = 0;
+ if ((ch->n_wavecontrol & 0x40) == 0) ch->n_tremolopos = 0;
+
+ paulaSetLength(ch->n_chanindex, ch->n_length);
+ paulaSetData(ch->n_chanindex, ch->n_start);
+
+ if (ch->n_start == NULL)
+ {
+ ch->n_loopstart = NULL;
+ paulaSetLength(ch->n_chanindex, 1);
+ ch->n_replen = 1;
+ }
+
+ paulaSetPeriod(ch->n_chanindex, ch->n_period);
+
+ if (!editor.muted[ch->n_chanindex])
+ {
+ paulaStartDMA(ch->n_chanindex);
+ updateSpectrumAnalyzer(ch->n_volume, ch->n_period);
+ setVUMeterHeight(ch);
+ }
+ else
+ {
+ paulaStopDMA(ch->n_chanindex);
+ }
+ }
+
+ checkMoreEffects(ch);
+}
+
+static void checkMetronome(moduleChannel_t *ch, note_t *note)
+{
+ if (editor.metroFlag && editor.metroChannel > 0)
+ {
+ if (ch->n_chanindex == editor.metroChannel-1 && (song->row % editor.metroSpeed) == 0)
+ {
+ note->sample = 0x1F;
+ note->period = (((song->row / editor.metroSpeed) % editor.metroSpeed) == 0) ? 160 : 214;
+ }
+ }
+}
+
+static void playVoice(moduleChannel_t *ch)
+{
+ uint8_t cmd;
+ moduleSample_t *s;
+ note_t note;
+
+ if (ch->n_note == 0 && ch->n_cmd == 0)
+ paulaSetPeriod(ch->n_chanindex, ch->n_period);
+
+ note = song->patterns[modPattern][(song->row * AMIGA_VOICES) + ch->n_chanindex];
+ checkMetronome(ch, ¬e);
+
+ ch->n_note = note.period;
+ ch->n_cmd = (note.command << 8) | note.param;
+
+ if (note.sample >= 1 && note.sample <= 31) // SAFETY BUG FIX: don't handle sample-numbers >31
+ {
+ ch->n_samplenum = note.sample - 1;
+ s = &song->samples[ch->n_samplenum];
+
+ ch->n_start = &song->sampleData[s->offset];
+ ch->n_finetune = s->fineTune;
+ ch->n_volume = s->volume;
+ ch->n_length = s->length / 2;
+ ch->n_replen = s->loopLength / 2;
+
+ if (s->loopStart > 0)
+ {
+ ch->n_loopstart = ch->n_start + s->loopStart;
+ ch->n_wavestart = ch->n_loopstart;
+ ch->n_length = (s->loopStart / 2) + ch->n_replen;
+ }
+ else
+ {
+ ch->n_loopstart = ch->n_start;
+ ch->n_wavestart = ch->n_start;
+ }
+
+ // non-PT2 quirk
+ if (ch->n_length == 0)
+ ch->n_loopstart = ch->n_wavestart = &song->sampleData[RESERVED_SAMPLE_OFFSET]; // dummy sample
+ }
+
+ if ((ch->n_note & 0xFFF) > 0)
+ {
+ if ((ch->n_cmd & 0xFF0) == 0xE50) // set finetune
+ {
+ setFineTune(ch);
+ setPeriod(ch);
+ }
+ else
+ {
+ cmd = (ch->n_cmd & 0xF00) >> 8;
+ if (cmd == 3 || cmd == 5)
+ {
+ setVUMeterHeight(ch);
+ setTonePorta(ch);
+ checkMoreEffects(ch);
+ }
+ else if (cmd == 9)
+ {
+ checkMoreEffects(ch);
+ setPeriod(ch);
+ }
+ else
+ {
+ setPeriod(ch);
+ }
+ }
+ }
+ else
+ {
+ checkMoreEffects(ch);
+ }
+}
+
+static void nextPosition(void)
+{
+ song->row = pBreakPosition;
+ pBreakPosition = 0;
+ posJumpAssert = false;
+
+ if (editor.playMode != PLAY_MODE_PATTERN ||
+ (editor.currMode == MODE_RECORD && editor.recordMode != RECORD_PATT))
+ {
+ if (editor.stepPlayEnabled)
+ {
+ doStopIt(true);
+
+ editor.stepPlayEnabled = false;
+ editor.stepPlayBackwards = false;
+
+ if (!editor.isWAVRendering && !editor.isSMPRendering)
+ song->currRow = song->row;
+
+ return;
+ }
+
+ modOrder = (modOrder + 1) & 0x7F;
+ if (modOrder >= song->header.numOrders)
+ {
+ modOrder = 0;
+ modHasBeenPlayed = true;
+
+ if (config.compoMode) // stop song for music competitions playing
+ {
+ doStopIt(true);
+ turnOffVoices();
+
+ song->currOrder = 0;
+ song->currRow = song->row = 0;
+ song->currPattern = modPattern = (int8_t)song->header.order[0];
+
+ editor.currPatternDisp = &song->currPattern;
+ editor.currPosEdPattDisp = &song->currPattern;
+ editor.currPatternDisp = &song->currPattern;
+ editor.currPosEdPattDisp = &song->currPattern;
+
+ if (ui.posEdScreenShown)
+ ui.updatePosEd = true;
+
+ ui.updateSongPos = true;
+ ui.updateSongPattern = true;
+ ui.updateCurrPattText = true;
+ }
+ }
+
+ modPattern = (int8_t)song->header.order[modOrder];
+ if (modPattern > MAX_PATTERNS-1)
+ modPattern = MAX_PATTERNS-1;
+
+ updateUIPositions = true;
+ }
+}
+
+bool intMusic(void)
+{
+ uint8_t i;
+ uint16_t *patt;
+ moduleChannel_t *c;
+
+ if (modBPM > 0)
+ editor.musicTime += (65536 / modBPM); // for playback counter
+
+ if (updateUIPositions)
+ {
+ updateUIPositions = false;
+
+ if (!editor.isWAVRendering && !editor.isSMPRendering)
+ {
+ if (editor.playMode != PLAY_MODE_PATTERN)
+ {
+ song->currOrder = modOrder;
+ song->currPattern = modPattern;
+
+ patt = &song->header.order[modOrder];
+ editor.currPatternDisp = patt;
+ editor.currPosEdPattDisp = patt;
+ editor.currPatternDisp = patt;
+ editor.currPosEdPattDisp = patt;
+
+ if (ui.posEdScreenShown)
+ ui.updatePosEd = true;
+
+ ui.updateSongPos = true;
+ ui.updateSongPattern = true;
+ ui.updateCurrPattText = true;
+ }
+ }
+ }
+
+ // PT quirk: CIA refreshes its timer values on the next interrupt, so do the real tempo change here
+ if (setBPMFlag != 0)
+ {
+ modSetTempo(setBPMFlag);
+ setBPMFlag = 0;
+ }
+
+ if (editor.isWAVRendering && song->tick == 0)
+ editor.rowVisitTable[(modOrder * MOD_ROWS) + song->row] = true;
+
+ if (!editor.stepPlayEnabled)
+ song->tick++;
+
+ if (song->tick >= song->speed || editor.stepPlayEnabled)
+ {
+ song->tick = 0;
+
+ if (pattDelTime2 == 0)
+ {
+ for (i = 0; i < AMIGA_VOICES; i++)
+ {
+ c = &song->channels[i];
+
+ playVoice(c);
+ paulaSetVolume(i, c->n_volume);
+
+ // these take effect after the current DMA cycle is done
+ paulaSetData(i, c->n_loopstart);
+ paulaSetLength(i, c->n_replen);
+ }
+ }
+ else
+ {
+ for (i = 0; i < AMIGA_VOICES; i++)
+ checkEffects(&song->channels[i]);
+ }
+
+ if (!editor.isWAVRendering && !editor.isSMPRendering)
+ {
+ song->currRow = song->row;
+ ui.updatePatternData = true;
+ }
+
+ if (!editor.stepPlayBackwards)
+ {
+ song->row++;
+ song->rowsCounter++;
+ }
+
+ if (pattDelTime > 0)
+ {
+ pattDelTime2 = pattDelTime;
+ pattDelTime = 0;
+ }
+
+ if (pattDelTime2 > 0)
+ {
+ if (--pattDelTime2 > 0)
+ song->row--;
+ }
+
+ if (pBreakFlag)
+ {
+ song->row = pBreakPosition;
+ pBreakPosition = 0;
+ pBreakFlag = false;
+ }
+
+ if (editor.blockMarkFlag)
+ ui.updateStatusText = true;
+
+ if (editor.stepPlayEnabled)
+ {
+ doStopIt(true);
+
+ song->currRow = song->row & 0x3F;
+ ui.updatePatternData = true;
+
+ editor.stepPlayEnabled = false;
+ editor.stepPlayBackwards = false;
+ ui.updatePatternData = true;
+
+ return true;
+ }
+
+ if (song->row >= MOD_ROWS || posJumpAssert)
+ {
+ if (editor.isSMPRendering)
+ modHasBeenPlayed = true;
+
+ nextPosition();
+ }
+
+ if (editor.isWAVRendering && !pattDelTime2 && editor.rowVisitTable[(modOrder * MOD_ROWS) + song->row])
+ modHasBeenPlayed = true;
+ }
+ else
+ {
+ for (i = 0; i < AMIGA_VOICES; i++)
+ checkEffects(&song->channels[i]);
+
+ if (posJumpAssert)
+ nextPosition();
+ }
+
+ if ((editor.isSMPRendering || editor.isWAVRendering) && modHasBeenPlayed && song->tick == song->speed-1)
+ {
+ modHasBeenPlayed = false;
+ return false;
+ }
+
+ return true;
+}
+
+void modSetPattern(uint8_t pattern)
+{
+ modPattern = pattern;
+ song->currPattern = modPattern;
+ ui.updateCurrPattText = true;
+}
+
+void modSetPos(int16_t order, int16_t row)
+{
+ int16_t posEdPos;
+
+ if (row != -1)
+ {
+ row = CLAMP(row, 0, 63);
+
+ song->tick = 0;
+ song->row = (int8_t)row;
+ song->currRow = (int8_t)row;
+ }
+
+ if (order != -1)
+ {
+ if (order >= 0)
+ {
+ modOrder = order;
+ song->currOrder = order;
+ ui.updateSongPos = true;
+
+ if (editor.currMode == MODE_PLAY && editor.playMode == PLAY_MODE_NORMAL)
+ {
+ modPattern = (int8_t)song->header.order[order];
+ if (modPattern > MAX_PATTERNS-1)
+ modPattern = MAX_PATTERNS-1;
+
+ song->currPattern = modPattern;
+ ui.updateCurrPattText = true;
+ }
+
+ ui.updateSongPattern = true;
+ editor.currPatternDisp = &song->header.order[modOrder];
+
+ posEdPos = song->currOrder;
+ if (posEdPos > song->header.numOrders-1)
+ posEdPos = song->header.numOrders-1;
+
+ editor.currPosEdPattDisp = &song->header.order[posEdPos];
+
+ if (ui.posEdScreenShown)
+ ui.updatePosEd = true;
+ }
+ }
+
+ ui.updatePatternData = true;
+
+ if (editor.blockMarkFlag)
+ ui.updateStatusText = true;
+}
+
+void modSetTempo(uint16_t bpm)
+{
+ uint32_t smpsPerTick;
+
+ if (bpm < 32)
+ return;
+
+ const bool audioWasntLocked = !audio.locked;
+ if (audioWasntLocked)
+ lockAudio();
+
+ modBPM = bpm;
+ if (!editor.isSMPRendering && !editor.isWAVRendering)
+ {
+ song->currBPM = bpm;
+ ui.updateSongBPM = true;
+ }
+
+ bpm -= 32; // 32..255 -> 0..223
+
+ if (editor.isSMPRendering)
+ smpsPerTick = editor.pat2SmpHQ ? audio.bpmTab28kHz[bpm] : audio.bpmTab22kHz[bpm];
+ else if (editor.isWAVRendering)
+ smpsPerTick = audio.bpmTabMod2Wav[bpm];
+ else
+ smpsPerTick = audio.bpmTab[bpm];
+
+ mixerSetSamplesPerTick(smpsPerTick);
+
+ if (audioWasntLocked)
+ unlockAudio();
+}
+
+void modStop(void)
+{
+ editor.songPlaying = false;
+ turnOffVoices();
+
+ for (int32_t i = 0; i < AMIGA_VOICES; i++)
+ {
+ moduleChannel_t *c = &song->channels[i];
+
+ c->n_wavecontrol = 0;
+ c->n_glissfunk = 0;
+ c->n_finetune = 0;
+ c->n_loopcount = 0;
+ }
+
+ pBreakFlag = false;
+ pattDelTime = 0;
+ pattDelTime2 = 0;
+ pBreakPosition = 0;
+ posJumpAssert = false;
+ modHasBeenPlayed = true;
+}
+
+void playPattern(int8_t startRow)
+{
+ if (!editor.stepPlayEnabled)
+ pointerSetMode(POINTER_MODE_PLAY, DO_CARRY);
+
+ mixerClearSampleCounter();
+
+ song->currRow = song->row = startRow & 0x3F;
+ song->tick = song->speed;
+
+ editor.playMode = PLAY_MODE_PATTERN;
+ editor.currMode = MODE_PLAY;
+ editor.didQuantize = false;
+ editor.songPlaying = true;
+}
+
+void incPatt(void)
+{
+ modPattern++;
+ if (modPattern > MAX_PATTERNS-1)
+ modPattern = 0;
+
+ song->currPattern = modPattern;
+
+ ui.updatePatternData = true;
+ ui.updateCurrPattText = true;
+}
+
+void decPatt(void)
+{
+ modPattern--;
+ if (modPattern < 0)
+ modPattern = MAX_PATTERNS - 1;
+
+ song->currPattern = modPattern;
+
+ ui.updatePatternData = true;
+ ui.updateCurrPattText = true;
+}
+
+void modPlay(int16_t patt, int16_t order, int8_t row)
+{
+ uint8_t oldPlayMode, oldMode;
+
+ doStopIt(false);
+ turnOffVoices();
+ mixerClearSampleCounter();
+
+ if (row != -1)
+ {
+ if (row >= 0 && row <= 63)
+ {
+ song->row = row;
+ song->currRow = row;
+ }
+ }
+ else
+ {
+ song->row = 0;
+ song->currRow = 0;
+ }
+
+ if (editor.playMode != PLAY_MODE_PATTERN)
+ {
+ if (modOrder >= song->header.numOrders)
+ {
+ modOrder = 0;
+ song->currOrder = 0;
+ }
+
+ if (order >= 0 && order < song->header.numOrders)
+ {
+ modOrder = order;
+ song->currOrder = order;
+ }
+
+ if (order >= song->header.numOrders)
+ {
+ modOrder = 0;
+ song->currOrder = 0;
+ }
+ }
+
+ if (patt >= 0 && patt <= MAX_PATTERNS-1)
+ song->currPattern = modPattern = (int8_t)patt;
+ else
+ song->currPattern = modPattern = (int8_t)song->header.order[modOrder];
+
+ editor.currPatternDisp = &song->header.order[modOrder];
+ editor.currPosEdPattDisp = &song->header.order[modOrder];
+
+ oldPlayMode = editor.playMode;
+ oldMode = editor.currMode;
+
+ editor.playMode = oldPlayMode;
+ editor.currMode = oldMode;
+
+ song->tick = song->speed;
+ modHasBeenPlayed = false;
+ editor.songPlaying = true;
+ editor.didQuantize = false;
+ editor.musicTime = 0;
+
+ if (!editor.isSMPRendering && !editor.isWAVRendering)
+ {
+ ui.updateSongPos = true;
+ ui.updatePatternData = true;
+ ui.updateSongPattern = true;
+ ui.updateCurrPattText = true;
+ }
+}
+
+void clearSong(void)
+{
+ uint8_t i;
+ moduleChannel_t *ch;
+
+ assert(song != NULL);
+ if (song == NULL)
+ return;
+
+ memset(song->header.order, 0, sizeof (song->header.order));
+ memset(song->header.name, 0, sizeof (song->header.name));
+
+ editor.muted[0] = false;
+ editor.muted[1] = false;
+ editor.muted[2] = false;
+ editor.muted[3] = false;
+
+ editor.f6Pos = 0;
+ editor.f7Pos = 16;
+ editor.f8Pos = 32;
+ editor.f9Pos = 48;
+ editor.f10Pos = 63;
+
+ editor.musicTime = 0;
+
+ editor.metroFlag = false;
+ editor.currSample = 0;
+ editor.editMoveAdd = 1;
+ editor.blockMarkFlag = false;
+ editor.swapChannelFlag = false;
+
+ song->header.numOrders = 1;
+
+ for (i = 0; i < MAX_PATTERNS; i++)
+ memset(song->patterns[i], 0, (MOD_ROWS * AMIGA_VOICES) * sizeof (note_t));
+
+ for (i = 0; i < AMIGA_VOICES; i++)
+ {
+ ch = &song->channels[i];
+
+ ch->n_wavecontrol = 0;
+ ch->n_glissfunk = 0;
+ ch->n_finetune = 0;
+ ch->n_loopcount = 0;
+ }
+
+ modSetPos(0, 0); // this also refreshes pattern data
+
+ song->currOrder = 0;
+ song->currPattern = 0;
+ editor.currPatternDisp = &song->header.order[0];
+ editor.currPosEdPattDisp = &song->header.order[0];
+
+ modSetTempo(editor.initialTempo);
+ modSetSpeed(editor.initialSpeed);
+
+ setLEDFilter(false); // real PT doesn't do this there, but that's insane
+ updateCurrSample();
+
+ ui.updateSongSize = true;
+ renderMuteButtons();
+ updateWindowTitle(MOD_IS_MODIFIED);
+}
+
+void clearSamples(void)
+{
+ moduleSample_t *s;
+
+ assert(song != NULL);
+ if (song == NULL)
+ return;
+
+ for (uint8_t i = 0; i < MOD_SAMPLES; i++)
+ {
+ s = &song->samples[i];
+
+ s->fineTune = 0;
+ s->length = 0;
+ s->loopLength = 2;
+ s->loopStart = 0;
+ s->volume = 0;
+
+ memset(s->text, 0, sizeof (s->text));
+ }
+
+ memset(song->sampleData, 0, (MOD_SAMPLES + 1) * MAX_SAMPLE_LEN);
+
+ editor.currSample = 0;
+ editor.hiLowInstr = 0;
+ editor.sampleZero = false;
+ ui.editOpScreenShown = false;
+ ui.aboutScreenShown = false;
+ editor.blockMarkFlag = false;
+
+ editor.samplePos = 0;
+ updateCurrSample();
+
+ updateWindowTitle(MOD_IS_MODIFIED);
+}
+
+void clearAll(void)
+{
+ clearSamples();
+ clearSong();
+}
+
+void modFree(void)
+{
+ uint8_t i;
+
+ if (song == NULL)
+ return; // not allocated
+
+ const bool audioWasntLocked = !audio.locked;
+ if (audioWasntLocked)
+ lockAudio();
+
+ turnOffVoices();
+
+ for (i = 0; i < MAX_PATTERNS; i++)
+ {
+ if (song->patterns[i] != NULL)
+ free(song->patterns[i]);
+ }
+
+ if (song->sampleData != NULL)
+ free(song->sampleData);
+
+ free(song);
+ song = NULL;
+
+ if (audioWasntLocked)
+ unlockAudio();
+}
+
+void restartSong(void) // for the beginning of MOD2WAV/PAT2SMP
+{
+ if (editor.songPlaying)
+ modStop();
+
+ editor.playMode = PLAY_MODE_NORMAL;
+ editor.blockMarkFlag = false;
+ audio.forceMixerOff = true;
+
+ song->row = 0;
+ song->currRow = 0;
+ song->rowsCounter = 0;
+
+ memset(editor.rowVisitTable, 0, MOD_ORDERS * MOD_ROWS); // for MOD2WAV
+
+ if (editor.isSMPRendering)
+ {
+ modPlay(DONT_SET_PATTERN, DONT_SET_ORDER, DONT_SET_ROW);
+ }
+ else
+ {
+ song->currSpeed = 6;
+ song->currBPM = 125;
+ modSetSpeed(6);
+ modSetTempo(125);
+
+ modPlay(DONT_SET_PATTERN, 0, 0);
+ }
+}
+
+// this function is meant for the end of MOD2WAV/PAT2SMP
+void resetSong(void) // only call this after storeTempVariables() has been called!
+{
+ modStop();
+
+ editor.songPlaying = false;
+ editor.playMode = PLAY_MODE_NORMAL;
+ editor.currMode = MODE_IDLE;
+
+ turnOffVoices();
+
+ memset((int8_t *)editor.vuMeterVolumes,0, sizeof (editor.vuMeterVolumes));
+ memset((int8_t *)editor.realVuMeterVolumes, 0, sizeof (editor.realVuMeterVolumes));
+ memset((int8_t *)editor.spectrumVolumes, 0, sizeof (editor.spectrumVolumes));
+
+ memset(song->channels, 0, sizeof (song->channels));
+ for (uint8_t i = 0; i < AMIGA_VOICES; i++)
+ song->channels[i].n_chanindex = i;
+
+ modOrder = oldOrder;
+ modPattern = (int8_t)oldPattern;
+
+ song->row = oldRow;
+ song->currRow = oldRow;
+ song->currBPM = oldBPM;
+ song->currOrder = oldOrder;
+ song->currPattern = oldPattern;
+
+ editor.currPosDisp = &song->currOrder;
+ editor.currEditPatternDisp = &song->currPattern;
+ editor.currPatternDisp = &song->header.order[song->currOrder];
+ editor.currPosEdPattDisp = &song->header.order[song->currOrder];
+
+ modSetSpeed(oldSpeed);
+ modSetTempo(oldBPM);
+
+ doStopIt(true);
+
+ song->tick = 0;
+ modHasBeenPlayed = false;
+ audio.forceMixerOff = false;
+}
--- /dev/null
+++ b/src/pt2_sample_loader.c
@@ -1,0 +1,1773 @@
+// for finding memory leaks in debug mode with Visual Studio
+#if defined _DEBUG && defined _MSC_VER
+#include <crtdbg.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <ctype.h> // tolower()
+#include "pt2_header.h"
+#include "pt2_textout.h"
+#include "pt2_mouse.h"
+#include "pt2_structs.h"
+#include "pt2_sampler.h" // fixSampleBeep()
+#include "pt2_audio.h"
+#include "pt2_visuals.h"
+#include "pt2_helpers.h"
+#include "pt2_unicode.h"
+#include "pt2_config.h"
+
+#define DOWNSAMPLE_CUTOFF_FACTOR 4.0
+
+enum
+{
+ WAV_FORMAT_PCM = 0x0001,
+ WAV_FORMAT_IEEE_FLOAT = 0x0003
+};
+
+static bool loadWAVSample(UNICHAR *fileName, char *entryName, int8_t forceDownSampling);
+static bool loadIFFSample(UNICHAR *fileName, char *entryName);
+static bool loadRAWSample(UNICHAR *fileName, char *entryName);
+static bool loadAIFFSample(UNICHAR *fileName, char *entryName, int8_t forceDownSampling);
+
+static bool loadedFileWasAIFF;
+
+static bool lowPassSample8Bit(int8_t *buffer, int32_t length, int32_t sampleFrequency, double cutoff)
+{
+ rcFilter_t filter;
+
+ if (buffer == NULL || length == 0 || cutoff == 0.0)
+ return false;
+
+ calcRCFilterCoeffs(sampleFrequency, cutoff, &filter);
+ clearRCFilterState(&filter);
+
+ for (int32_t i = 0; i < length; i++)
+ {
+ int32_t sample;
+ double dSample;
+
+ RCLowPassFilterMono(&filter, buffer[i], &dSample);
+ sample = (int32_t)dSample;
+
+ buffer[i] = (int8_t)CLAMP(sample, INT8_MIN, INT8_MAX);
+ }
+
+ return true;
+}
+
+static bool lowPassSample8BitUnsigned(uint8_t *buffer, int32_t length, int32_t sampleFrequency, double cutoff)
+{
+ rcFilter_t filter;
+
+ if (buffer == NULL || length == 0 || cutoff == 0.0)
+ return false;
+
+ calcRCFilterCoeffs(sampleFrequency, cutoff, &filter);
+ clearRCFilterState(&filter);
+
+ for (int32_t i = 0; i < length; i++)
+ {
+ int32_t sample;
+ double dSample;
+
+ RCLowPassFilterMono(&filter, buffer[i] - 128, &dSample);
+ sample = (int32_t)dSample;
+
+ sample = CLAMP(sample, INT8_MIN, INT8_MAX);
+ buffer[i] = (uint8_t)(sample + 128);
+ }
+
+ return true;
+}
+
+static bool lowPassSample16Bit(int16_t *buffer, int32_t length, int32_t sampleFrequency, double cutoff)
+{
+ rcFilter_t filter;
+
+ if (buffer == NULL || length == 0 || cutoff == 0.0)
+ return false;
+
+ calcRCFilterCoeffs(sampleFrequency, cutoff, &filter);
+ clearRCFilterState(&filter);
+
+ for (int32_t i = 0; i < length; i++)
+ {
+ int32_t sample;
+ double dSample;
+
+ RCLowPassFilterMono(&filter, buffer[i], &dSample);
+ sample = (int32_t)dSample;
+
+ buffer[i] = (int16_t)CLAMP(sample, INT16_MIN, INT16_MAX);
+ }
+
+ return true;
+}
+
+static bool lowPassSample32Bit(int32_t *buffer, int32_t length, int32_t sampleFrequency, double cutoff)
+{
+ rcFilter_t filter;
+
+ if (buffer == NULL || length == 0 || cutoff == 0.0)
+ return false;
+
+ calcRCFilterCoeffs(sampleFrequency, cutoff, &filter);
+ clearRCFilterState(&filter);
+
+ for (int32_t i = 0; i < length; i++)
+ {
+ int64_t sample;
+ double dSample;
+
+ RCLowPassFilterMono(&filter, buffer[i], &dSample);
+ sample = (int32_t)dSample;
+
+ buffer[i] = (int32_t)CLAMP(sample, INT32_MIN, INT32_MAX);
+ }
+
+ return true;
+}
+
+static bool lowPassSampleFloat(float *buffer, int32_t length, int32_t sampleFrequency, double cutoff)
+{
+ rcFilter_t filter;
+
+ if (buffer == NULL || length == 0 || cutoff == 0.0)
+ return false;
+
+ calcRCFilterCoeffs(sampleFrequency, cutoff, &filter);
+ clearRCFilterState(&filter);
+
+ for (int32_t i = 0; i < length; i++)
+ {
+ double dSample;
+
+ RCLowPassFilterMono(&filter, buffer[i], &dSample);
+ buffer[i] = (float)dSample;
+ }
+
+ return true;
+}
+
+static bool lowPassSampleDouble(double *buffer, int32_t length, int32_t sampleFrequency, double cutoff)
+{
+ rcFilter_t filter;
+
+ if (buffer == NULL || length == 0 || cutoff == 0.0)
+ return false;
+
+ calcRCFilterCoeffs(sampleFrequency, cutoff, &filter);
+ clearRCFilterState(&filter);
+
+ for (int32_t i = 0; i < length; i++)
+ {
+ double dSample;
+ RCLowPassFilterMono(&filter, buffer[i], &dSample);
+
+ buffer[i] = dSample;
+ }
+
+ return true;
+}
+
+void extLoadWAVOrAIFFSampleCallback(bool downsample)
+{
+ if (loadedFileWasAIFF)
+ loadAIFFSample(editor.fileNameTmpU, editor.entryNameTmp, downsample);
+ else
+ loadWAVSample(editor.fileNameTmpU, editor.entryNameTmp, downsample);
+}
+
+bool loadWAVSample(UNICHAR *fileName, char *entryName, int8_t forceDownSampling)
+{
+ bool wavSampleNameFound;
+ uint8_t *audioDataU8;
+ int16_t *audioDataS16, tempVol, smp16;
+ uint16_t audioFormat, numChannels, bitsPerSample;
+ int32_t *audioDataS32, smp32;
+ uint32_t *audioDataU32, i, nameLen, chunkID, chunkSize;
+ uint32_t sampleLength, sampleRate, filesize, loopFlags;
+ uint32_t loopStart, loopEnd, dataPtr, dataLen, fmtPtr, endOfChunk, bytesRead;
+ uint32_t fmtLen, inamPtr, inamLen, smplPtr, smplLen, xtraPtr, xtraLen;
+ float *fAudioDataFloat, fSmp;
+ double *dAudioDataDouble, dSmp;
+ FILE *f;
+ moduleSample_t *s;
+
+ loadedFileWasAIFF = false;
+
+ // zero out chunk pointers and lengths
+ fmtPtr = 0; fmtLen = 0;
+ dataPtr = 0; dataLen = 0;
+ inamPtr = 0; inamLen = 0;
+ xtraPtr = 0; xtraLen = 0;
+ smplPtr = 0; smplLen = 0;
+
+ wavSampleNameFound = false;
+
+ s = &song->samples[editor.currSample];
+
+ if (forceDownSampling == -1)
+ {
+ // these two *must* be fully wiped, for outputting reasons
+ memset(editor.fileNameTmpU, 0, PATH_MAX);
+ memset(editor.entryNameTmp, 0, PATH_MAX);
+ UNICHAR_STRCPY(editor.fileNameTmpU, fileName);
+ strcpy(editor.entryNameTmp, entryName);
+ }
+
+ f = UNICHAR_FOPEN(fileName, "rb");
+ if (f == NULL)
+ {
+ displayErrorMsg("FILE I/O ERROR !");
+ return false;
+ }
+
+ fseek(f, 0, SEEK_END);
+ filesize = ftell(f);
+ if (filesize == 0)
+ {
+ fclose(f);
+
+ displayErrorMsg("NOT A WAV !");
+ return false;
+ }
+
+ // look for wanted chunks and set up pointers + lengths
+ fseek(f, 12, SEEK_SET);
+
+ bytesRead = 0;
+ while (!feof(f) && bytesRead < filesize-12)
+ {
+ fread(&chunkID, 4, 1, f); if (feof(f)) break;
+ fread(&chunkSize, 4, 1, f); if (feof(f)) break;
+
+ endOfChunk = (ftell(f) + chunkSize) + (chunkSize & 1);
+ switch (chunkID)
+ {
+ case 0x20746D66: // "fmt "
+ {
+ fmtPtr = ftell(f);
+ fmtLen = chunkSize;
+ }
+ break;
+
+ case 0x61746164: // "data"
+ {
+ dataPtr = ftell(f);
+ dataLen = chunkSize;
+ }
+ break;
+
+ case 0x5453494C: // "LIST"
+ {
+ if (chunkSize >= 4)
+ {
+ fread(&chunkID, 4, 1, f);
+ if (chunkID == 0x4F464E49) // "INFO"
+ {
+ bytesRead = 0;
+ while (!feof(f) && (bytesRead < chunkSize))
+ {
+ fread(&chunkID, 4, 1, f);
+ fread(&chunkSize, 4, 1, f);
+
+ switch (chunkID)
+ {
+ case 0x4D414E49: // "INAM"
+ {
+ inamPtr = ftell(f);
+ inamLen = chunkSize;
+ }
+ break;
+
+ default: break;
+ }
+
+ bytesRead += (chunkSize + (chunkSize & 1));
+ }
+ }
+ }
+ }
+ break;
+
+ case 0x61727478: // "xtra"
+ {
+ xtraPtr = ftell(f);
+ xtraLen = chunkSize;
+ }
+ break;
+
+ case 0x6C706D73: // "smpl"
+ {
+ smplPtr = ftell(f);
+ smplLen = chunkSize;
+ }
+ break;
+
+ default: break;
+ }
+
+ bytesRead += chunkSize + (chunkSize & 1);
+ fseek(f, endOfChunk, SEEK_SET);
+ }
+
+ // we need at least "fmt " and "data" - check if we found them sanely
+ if ((fmtPtr == 0 || fmtLen < 16) || (dataPtr == 0 || dataLen == 0))
+ {
+ fclose(f);
+ displayErrorMsg("NOT A WAV !");
+ return false;
+ }
+
+ // ---- READ "fmt " CHUNK ----
+ fseek(f, fmtPtr, SEEK_SET);
+ fread(&audioFormat, 2, 1, f);
+ fread(&numChannels, 2, 1, f);
+ fread(&sampleRate, 4, 1, f);
+ fseek(f, 6, SEEK_CUR);
+ fread(&bitsPerSample, 2, 1, f);
+ sampleLength = dataLen;
+ // ---------------------------
+
+ if (sampleRate == 0 || sampleLength == 0 || sampleLength >= filesize*(bitsPerSample/8))
+ {
+ fclose(f);
+ displayErrorMsg("WAV CORRUPT !");
+ return false;
+ }
+
+ if (audioFormat != WAV_FORMAT_PCM && audioFormat != WAV_FORMAT_IEEE_FLOAT)
+ {
+ fclose(f);
+ displayErrorMsg("WAV UNSUPPORTED !");
+ return false;
+ }
+
+ if ((numChannels == 0) || (numChannels > 2))
+ {
+ fclose(f);
+ displayErrorMsg("WAV UNSUPPORTED !");
+ return false;
+ }
+
+ if (audioFormat == WAV_FORMAT_IEEE_FLOAT && bitsPerSample != 32 && bitsPerSample != 64)
+ {
+ fclose(f);
+ displayErrorMsg("WAV UNSUPPORTED !");
+ return false;
+ }
+
+ if (bitsPerSample != 8 && bitsPerSample != 16 && bitsPerSample != 24 && bitsPerSample != 32 && bitsPerSample != 64)
+ {
+ fclose(f);
+ displayErrorMsg("WAV UNSUPPORTED !");
+ return false;
+ }
+
+ if (sampleRate > 22050)
+ {
+ if (forceDownSampling == -1)
+ {
+ showDownsampleAskDialog();
+ fclose(f);
+ return true;
+ }
+ }
+ else
+ {
+ forceDownSampling = false;
+ }
+
+ // ---- READ SAMPLE DATA ----
+ fseek(f, dataPtr, SEEK_SET);
+
+ if (bitsPerSample == 8) // 8-BIT INTEGER SAMPLE
+ {
+ if (sampleLength > MAX_SAMPLE_LEN*4)
+ sampleLength = MAX_SAMPLE_LEN*4;
+
+ audioDataU8 = (uint8_t *)malloc(sampleLength * sizeof (uint8_t));
+ if (audioDataU8 == NULL)
+ {
+ fclose(f);
+ statusOutOfMemory();
+ return false;
+ }
+
+ // read sample data
+ if (fread(audioDataU8, 1, sampleLength, f) != sampleLength)
+ {
+ fclose(f);
+ free(audioDataU8);
+ displayErrorMsg("I/O ERROR !");
+ return false;
+ }
+
+ // convert from stereo to mono (if needed)
+ if (numChannels == 2)
+ {
+ sampleLength >>= 1;
+ for (i = 0; i < sampleLength-1; i++) // add right channel to left channel
+ {
+ smp16 = (audioDataU8[(i << 1) + 0] - 128) + (audioDataU8[(i << 1) + 1] - 128);
+ smp16 = 128 + (smp16 >> 1);
+ audioDataU8[i] = (uint8_t)smp16;
+ }
+ }
+
+ // 2x downsampling - remove every other sample (if needed)
+ if (forceDownSampling)
+ {
+ if (config.sampleLowpass)
+ lowPassSample8BitUnsigned(audioDataU8, sampleLength, sampleRate, sampleRate / DOWNSAMPLE_CUTOFF_FACTOR);
+
+ sampleLength >>= 1;
+ for (i = 1; i < sampleLength; i++)
+ audioDataU8[i] = audioDataU8[i << 1];
+ }
+
+ if (sampleLength > MAX_SAMPLE_LEN)
+ sampleLength = MAX_SAMPLE_LEN;
+
+ turnOffVoices();
+ for (i = 0; i < MAX_SAMPLE_LEN; i++)
+ {
+ if (i <= (sampleLength & 0xFFFFFFFE))
+ song->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = audioDataU8[i] - 128;
+ else
+ song->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = 0; // clear rest of sample
+ }
+
+ free(audioDataU8);
+ }
+ else if (bitsPerSample == 16) // 16-BIT INTEGER SAMPLE
+ {
+ sampleLength >>= 1;
+ if (sampleLength > MAX_SAMPLE_LEN*4)
+ sampleLength = MAX_SAMPLE_LEN*4;
+
+ audioDataS16 = (int16_t *)malloc(sampleLength * sizeof (int16_t));
+ if (audioDataS16 == NULL)
+ {
+ fclose(f);
+ statusOutOfMemory();
+ return false;
+ }
+
+ // read sample data
+ if (fread(audioDataS16, 2, sampleLength, f) != sampleLength)
+ {
+ fclose(f);
+ free(audioDataS16);
+ displayErrorMsg("I/O ERROR !");
+ return false;
+ }
+
+ // convert from stereo to mono (if needed)
+ if (numChannels == 2)
+ {
+ sampleLength >>= 1;
+ for (i = 0; i < sampleLength-1; i++) // add right channel to left channel
+ {
+ smp32 = (audioDataS16[(i << 1) + 0] + audioDataS16[(i << 1) + 1]) >> 1;
+ audioDataS16[i] = (int16_t)smp32;
+ }
+ }
+
+ // 2x downsampling - remove every other sample (if needed)
+ if (forceDownSampling)
+ {
+ if (config.sampleLowpass)
+ lowPassSample16Bit(audioDataS16, sampleLength, sampleRate, sampleRate / DOWNSAMPLE_CUTOFF_FACTOR);
+
+ sampleLength >>= 1;
+ for (i = 1; i < sampleLength; i++)
+ audioDataS16[i] = audioDataS16[i << 1];
+ }
+
+ if (sampleLength > MAX_SAMPLE_LEN)
+ sampleLength = MAX_SAMPLE_LEN;
+
+ normalize16bitSigned(audioDataS16, sampleLength);
+
+ turnOffVoices();
+ for (i = 0; i < MAX_SAMPLE_LEN; i++)
+ {
+ if (i <= (sampleLength & 0xFFFFFFFE))
+ song->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = audioDataS16[i] >> 8;
+ else
+ song->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = 0; // clear rest of sample
+ }
+
+ free(audioDataS16);
+ }
+ else if (bitsPerSample == 24) // 24-BIT INTEGER SAMPLE
+ {
+ sampleLength /= 3;
+ if (sampleLength > MAX_SAMPLE_LEN*4)
+ sampleLength = MAX_SAMPLE_LEN*4;
+
+ audioDataS32 = (int32_t *)malloc(sampleLength * sizeof (int32_t));
+ if (audioDataS32 == NULL)
+ {
+ fclose(f);
+ statusOutOfMemory();
+ return false;
+ }
+
+ // read sample data
+ audioDataU8 = (uint8_t *)audioDataS32;
+ for (i = 0; i < sampleLength; i++)
+ {
+ audioDataU8[0] = 0;
+ fread(&audioDataU8[1], 3, 1, f);
+ audioDataU8 += sizeof (int32_t);
+ }
+
+ // convert from stereo to mono (if needed)
+ if (numChannels == 2)
+ {
+ sampleLength >>= 1;
+ for (i = 0; i < sampleLength-1; i++) // add right channel to left channel
+ {
+ int64_t smp = ((int64_t)audioDataS32[(i << 1) + 0] + audioDataS32[(i << 1) + 1]) >> 1;
+ audioDataS32[i] = (int32_t)smp;
+ }
+ }
+
+ // 2x downsampling - remove every other sample (if needed)
+ if (forceDownSampling)
+ {
+ if (config.sampleLowpass)
+ lowPassSample32Bit(audioDataS32, sampleLength, sampleRate, sampleRate / DOWNSAMPLE_CUTOFF_FACTOR);
+
+ sampleLength >>= 1;
+ for (i = 1; i < sampleLength; i++)
+ audioDataS32[i] = audioDataS32[i << 1];
+ }
+
+ if (sampleLength > MAX_SAMPLE_LEN)
+ sampleLength = MAX_SAMPLE_LEN;
+
+ normalize32bitSigned(audioDataS32, sampleLength);
+
+ turnOffVoices();
+ for (i = 0; i < MAX_SAMPLE_LEN; i++)
+ {
+ if (i <= (sampleLength & 0xFFFFFFFE))
+ song->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = (int8_t)(audioDataS32[i] >> 24);
+ else
+ song->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = 0; // clear rest of sample
+ }
+
+ free(audioDataS32);
+ }
+ else if (audioFormat == WAV_FORMAT_PCM && bitsPerSample == 32) // 32-BIT INTEGER SAMPLE
+ {
+ sampleLength >>= 2;
+ if (sampleLength > MAX_SAMPLE_LEN*4)
+ sampleLength = MAX_SAMPLE_LEN*4;
+
+ audioDataS32 = (int32_t *)malloc(sampleLength * sizeof (int32_t));
+ if (audioDataS32 == NULL)
+ {
+ fclose(f);
+ statusOutOfMemory();
+ return false;
+ }
+
+ // read sample data
+ if (fread(audioDataS32, 4, sampleLength, f) != sampleLength)
+ {
+ fclose(f);
+ free(audioDataS32);
+ displayErrorMsg("I/O ERROR !");
+ return false;
+ }
+
+ // convert from stereo to mono (if needed)
+ if (numChannels == 2)
+ {
+ sampleLength >>= 1;
+ for (i = 0; i < sampleLength-1; i++) // add right channel to left channel
+ {
+ int64_t smp = ((int64_t)audioDataS32[(i << 1) + 0] + audioDataS32[(i << 1) + 1]) >> 1;
+ audioDataS32[i] = (int32_t)smp;
+ }
+ }
+
+ // 2x downsampling - remove every other sample (if needed)
+ if (forceDownSampling)
+ {
+ if (config.sampleLowpass)
+ lowPassSample32Bit(audioDataS32, sampleLength, sampleRate, sampleRate / DOWNSAMPLE_CUTOFF_FACTOR);
+
+ sampleLength >>= 1;
+ for (i = 1; i < sampleLength; i++)
+ audioDataS32[i] = audioDataS32[i << 1];
+ }
+
+ if (sampleLength > MAX_SAMPLE_LEN)
+ sampleLength = MAX_SAMPLE_LEN;
+
+ normalize32bitSigned(audioDataS32, sampleLength);
+
+ turnOffVoices();
+ for (i = 0; i < MAX_SAMPLE_LEN; i++)
+ {
+ if (i <= (sampleLength & 0xFFFFFFFE))
+ song->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = (int8_t)(audioDataS32[i] >> 24);
+ else
+ song->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = 0; // clear rest of sample
+ }
+
+ free(audioDataS32);
+ }
+ else if (audioFormat == WAV_FORMAT_IEEE_FLOAT && bitsPerSample == 32) // 32-BIT FLOATING POINT SAMPLE
+ {
+ sampleLength >>= 2;
+ if (sampleLength > MAX_SAMPLE_LEN*4)
+ sampleLength = MAX_SAMPLE_LEN*4;
+
+ audioDataU32 = (uint32_t *)malloc(sampleLength * sizeof (uint32_t));
+ if (audioDataU32 == NULL)
+ {
+ fclose(f);
+ statusOutOfMemory();
+ return false;
+ }
+
+ // read sample data
+ if (fread(audioDataU32, 4, sampleLength, f) != sampleLength)
+ {
+ fclose(f);
+ free(audioDataU32);
+ displayErrorMsg("I/O ERROR !");
+ return false;
+ }
+
+ fAudioDataFloat = (float *)audioDataU32;
+
+ // convert from stereo to mono (if needed)
+ if (numChannels == 2)
+ {
+ sampleLength >>= 1;
+ for (i = 0; i < sampleLength-1; i++) // add right channel to left channel
+ {
+ fSmp = (fAudioDataFloat[(i * 2) + 0] + fAudioDataFloat[(i * 2) + 1]) * 0.5f;
+ fAudioDataFloat[i] = fSmp;
+ }
+ }
+
+ // 2x downsampling - remove every other sample (if needed)
+ if (forceDownSampling)
+ {
+ if (config.sampleLowpass)
+ lowPassSampleFloat(fAudioDataFloat, sampleLength, sampleRate, sampleRate / DOWNSAMPLE_CUTOFF_FACTOR);
+
+ sampleLength >>= 1;
+ for (i = 1; i < sampleLength; i++)
+ fAudioDataFloat[i] = fAudioDataFloat[i << 1];
+ }
+
+ if (sampleLength > MAX_SAMPLE_LEN)
+ sampleLength = MAX_SAMPLE_LEN;
+
+ normalize8bitFloatSigned(fAudioDataFloat, sampleLength);
+
+ turnOffVoices();
+ for (i = 0; i < MAX_SAMPLE_LEN; i++)
+ {
+ if (i <= (sampleLength & 0xFFFFFFFE))
+ {
+ smp32 = (int32_t)fAudioDataFloat[i];
+ CLAMP8(smp32);
+ song->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = (int8_t)smp32;
+ }
+ else
+ {
+ song->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = 0; // clear rest of sample
+ }
+ }
+
+ free(audioDataU32);
+ }
+ else if (audioFormat == WAV_FORMAT_IEEE_FLOAT && bitsPerSample == 64) // 64-BIT FLOATING POINT SAMPLE
+ {
+ sampleLength >>= 3;
+ if (sampleLength > MAX_SAMPLE_LEN*4)
+ sampleLength = MAX_SAMPLE_LEN*4;
+
+ audioDataU32 = (uint32_t *)malloc(sampleLength * (sizeof (uint32_t) * 2));
+ if (audioDataU32 == NULL)
+ {
+ fclose(f);
+ statusOutOfMemory();
+ return false;
+ }
+
+ // read sample data
+ if (fread(audioDataU32, 8, sampleLength, f) != sampleLength)
+ {
+ fclose(f);
+ free(audioDataU32);
+ displayErrorMsg("I/O ERROR !");
+ return false;
+ }
+
+ dAudioDataDouble = (double *)audioDataU32;
+
+ // convert from stereo to mono (if needed)
+ if (numChannels == 2)
+ {
+ sampleLength >>= 1;
+ for (i = 0; i < sampleLength-1; i++) // add right channel to left channel
+ {
+ dSmp = (dAudioDataDouble[(i * 2) + 0] + dAudioDataDouble[(i * 2) + 1]) * 0.5;
+ dAudioDataDouble[i] = dSmp;
+ }
+ }
+
+ // 2x downsampling - remove every other sample (if needed)
+ if (forceDownSampling)
+ {
+ if (config.sampleLowpass)
+ lowPassSampleDouble(dAudioDataDouble, sampleLength, sampleRate, sampleRate / DOWNSAMPLE_CUTOFF_FACTOR);
+
+ sampleLength >>= 1;
+ for (i = 1; i < sampleLength; i++)
+ dAudioDataDouble[i] = dAudioDataDouble[i << 1];
+ }
+
+ if (sampleLength > MAX_SAMPLE_LEN)
+ sampleLength = MAX_SAMPLE_LEN;
+
+ normalize8bitDoubleSigned(dAudioDataDouble, sampleLength);
+
+ turnOffVoices();
+ for (i = 0; i < MAX_SAMPLE_LEN; i++)
+ {
+ if (i <= (sampleLength & 0xFFFFFFFE))
+ {
+ smp32 = (int32_t)dAudioDataDouble[i];
+ CLAMP8(smp32);
+ song->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = (int8_t)smp32;
+ }
+ else
+ {
+ song->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = 0; // clear rest of sample
+ }
+ }
+
+ free(audioDataU32);
+ }
+
+ // set sample length
+ if (sampleLength & 1)
+ {
+ if (++sampleLength > MAX_SAMPLE_LEN)
+ sampleLength = MAX_SAMPLE_LEN;
+ }
+
+ s->length = (uint16_t)sampleLength;
+ s->fineTune = 0;
+ s->volume = 64;
+ s->loopStart = 0;
+ s->loopLength = 2;
+
+ // ---- READ "smpl" chunk ----
+ if (smplPtr != 0 && smplLen > 52)
+ {
+ fseek(f, smplPtr + 28, SEEK_SET); // seek to first wanted byte
+
+ fread(&loopFlags, 4, 1, f);
+ fseek(f, 12, SEEK_CUR);
+ fread(&loopStart, 4, 1, f);
+ fread(&loopEnd, 4, 1, f);
+ loopEnd++;
+
+ if (forceDownSampling)
+ {
+ // we already downsampled 2x, so we're half the original length
+ loopStart >>= 1;
+ loopEnd >>= 1;
+ }
+
+ loopStart &= 0xFFFFFFFE;
+ loopEnd &= 0xFFFFFFFE;
+
+ if (loopFlags)
+ {
+ if (loopStart+(loopEnd-loopStart) <= s->length)
+ {
+ s->loopStart = (uint16_t)loopStart;
+ s->loopLength = (uint16_t)(loopEnd - loopStart);
+
+ if (s->loopLength < 2)
+ {
+ s->loopStart = 0;
+ s->loopLength = 2;
+ }
+ }
+ }
+ }
+ // ---------------------------
+
+ // ---- READ "xtra" chunk ----
+ if (xtraPtr != 0 && xtraLen >= 8)
+ {
+ fseek(f, xtraPtr + 4, SEEK_SET); // seek to first wanted byte
+
+ // volume (0..256)
+ fseek(f, 2, SEEK_CUR);
+ fread(&tempVol, 2, 1, f);
+ if (tempVol > 256)
+ tempVol = 256;
+
+ tempVol >>= 2; // 0..256 -> 0..64
+
+ s->volume = (int8_t)tempVol;
+ }
+ // ---------------------------
+
+ // ---- READ "INAM" chunk ----
+ if (inamPtr != 0 && inamLen > 0)
+ {
+ fseek(f, inamPtr, SEEK_SET); // seek to first wanted byte
+
+ for (i = 0; i < 21; i++)
+ {
+ if (i < inamLen)
+ s->text[i] = (char)tolower(fgetc(f));
+ else
+ s->text[i] = '\0';
+ }
+
+ s->text[21] = '\0';
+ s->text[22] = '\0';
+
+ wavSampleNameFound = true;
+ }
+ // ---------------------------
+
+ fclose(f);
+
+ // copy over sample name
+ if (!wavSampleNameFound)
+ {
+ nameLen = (uint32_t)strlen(entryName);
+ for (i = 0; i < 21; i++)
+ s->text[i] = (i < nameLen) ? (char)tolower(entryName[i]) : '\0';
+
+ s->text[21] = '\0';
+ s->text[22] = '\0';
+ }
+
+ // remove .wav from end of sample name (if present)
+ nameLen = (uint32_t)strlen(s->text);
+ if (nameLen >= 4 && !_strnicmp(&s->text[nameLen-4], ".WAV", 4))
+ memset(&s->text[nameLen-4], '\0', 4);
+
+ editor.sampleZero = false;
+ editor.samplePos = 0;
+
+ fixSampleBeep(s);
+ updateCurrSample();
+
+ updateWindowTitle(MOD_IS_MODIFIED);
+ return true;
+}
+
+bool loadIFFSample(UNICHAR *fileName, char *entryName)
+{
+ bool nameFound, is16Bit;
+ char tmpCharBuf[23];
+ int8_t *sampleData;
+ int16_t sample16, *ptr16;
+ int32_t filesize;
+ uint32_t i, sampleLength, sampleLoopStart, sampleLoopLength;
+ uint32_t sampleVolume, blockName, blockSize;
+ uint32_t vhdrPtr, vhdrLen, bodyPtr, bodyLen, namePtr, nameLen;
+ FILE *f;
+ moduleSample_t *s;
+
+ s = &song->samples[editor.currSample];
+
+ vhdrPtr = 0; vhdrLen = 0;
+ bodyPtr = 0; bodyLen = 0;
+ namePtr = 0; nameLen = 0;
+
+ f = UNICHAR_FOPEN(fileName, "rb");
+ if (f == NULL)
+ {
+ displayErrorMsg("FILE I/O ERROR !");
+ return false;
+ }
+
+ fseek(f, 0, SEEK_END);
+ filesize = ftell(f);
+ if (filesize == 0)
+ {
+ displayErrorMsg("IFF IS CORRUPT !");
+ return false;
+ }
+
+ fseek(f, 8, SEEK_SET);
+ fread(tmpCharBuf, 1, 4, f);
+ is16Bit = !strncmp(tmpCharBuf, "16SV", 4);
+
+ sampleLength = 0;
+ nameFound = false;
+ sampleVolume = 65536; // max volume
+
+ fseek(f, 12, SEEK_SET);
+ while (!feof(f) && ftell(f) < filesize-12)
+ {
+ fread(&blockName, 4, 1, f); if (feof(f)) break;
+ fread(&blockSize, 4, 1, f); if (feof(f)) break;
+
+ blockName = SWAP32(blockName);
+ blockSize = SWAP32(blockSize);
+
+ switch (blockName)
+ {
+ case 0x56484452: // VHDR
+ {
+ vhdrPtr = ftell(f);
+ vhdrLen = blockSize;
+ }
+ break;
+
+ case 0x4E414D45: // NAME
+ {
+ namePtr = ftell(f);
+ nameLen = blockSize;
+ }
+ break;
+
+ case 0x424F4459: // BODY
+ {
+ bodyPtr = ftell(f);
+ bodyLen = blockSize;
+ }
+ break;
+
+ default: break;
+ }
+
+ fseek(f, blockSize + (blockSize & 1), SEEK_CUR);
+ }
+
+ if (vhdrPtr == 0 || vhdrLen < 20 || bodyPtr == 0)
+ {
+ fclose(f);
+ displayErrorMsg("NOT A VALID IFF !");
+ return false;
+ }
+
+ // kludge for some really strange IFFs
+ if (bodyLen == 0)
+ bodyLen = filesize - bodyPtr;
+
+ if (bodyPtr+bodyLen > (uint32_t)filesize)
+ bodyLen = filesize - bodyPtr;
+
+ fseek(f, vhdrPtr, SEEK_SET);
+ fread(&sampleLoopStart, 4, 1, f); sampleLoopStart = SWAP32(sampleLoopStart);
+ fread(&sampleLoopLength, 4, 1, f); sampleLoopLength = SWAP32(sampleLoopLength);
+
+ fseek(f, 4 + 2 + 1, SEEK_CUR);
+
+ if (fgetc(f) != 0) // sample type
+ {
+ fclose(f);
+ displayErrorMsg("UNSUPPORTED IFF !");
+ return false;
+ }
+
+ fread(&sampleVolume, 4, 1, f); sampleVolume = SWAP32(sampleVolume);
+ if (sampleVolume > 65536)
+ sampleVolume = 65536;
+
+ sampleVolume = (sampleVolume + 512) / 1024; // rounded
+ if (sampleVolume > 64)
+ sampleVolume = 64;
+
+ sampleLength = bodyLen;
+ if (is16Bit)
+ {
+ if (sampleLength > MAX_SAMPLE_LEN*2)
+ sampleLength = MAX_SAMPLE_LEN*2;
+ }
+ else
+ {
+ if (sampleLength > MAX_SAMPLE_LEN)
+ sampleLength = MAX_SAMPLE_LEN;
+ }
+
+ if (sampleLength == 0)
+ {
+ fclose(f);
+ displayErrorMsg("NOT A VALID IFF !");
+ return false;
+ }
+
+ sampleData = (int8_t *)malloc(sampleLength);
+ if (sampleData == NULL)
+ {
+ fclose(f);
+ statusOutOfMemory();
+ return false;
+ }
+
+ if (is16Bit)
+ {
+ sampleLength >>= 1;
+ sampleLoopStart >>= 1;
+ sampleLoopLength >>= 1;
+ }
+
+ sampleLength &= 0xFFFFFFFE;
+ sampleLoopStart &= 0xFFFFFFFE;
+ sampleLoopLength &= 0xFFFFFFFE;
+
+ if (sampleLength > MAX_SAMPLE_LEN)
+ sampleLength = MAX_SAMPLE_LEN;
+
+ if (sampleLoopLength < 2)
+ {
+ sampleLoopStart = 0;
+ sampleLoopLength = 2;
+ }
+
+ if (sampleLoopStart >= MAX_SAMPLE_LEN || sampleLoopLength > MAX_SAMPLE_LEN)
+ {
+ sampleLoopStart= 0;
+ sampleLoopLength = 2;
+ }
+
+ if (sampleLoopStart+sampleLoopLength > sampleLength)
+ {
+ sampleLoopStart = 0;
+ sampleLoopLength = 2;
+ }
+
+ if (sampleLoopStart > sampleLength-2)
+ {
+ sampleLoopStart = 0;
+ sampleLoopLength = 2;
+ }
+
+ turnOffVoices();
+
+ fseek(f, bodyPtr, SEEK_SET);
+ if (is16Bit) // FT2 specific 16SV format (little-endian samples)
+ {
+ fread(sampleData, 1, sampleLength << 1, f);
+
+ ptr16 = (int16_t *)sampleData;
+ for (i = 0; i < sampleLength; i++)
+ {
+ sample16 = ptr16[i];
+ song->sampleData[s->offset+i] = sample16 >> 8;
+ }
+ }
+ else
+ {
+ fread(sampleData, 1, sampleLength, f);
+ memcpy(&song->sampleData[s->offset], sampleData, sampleLength);
+ }
+
+ if (sampleLength < MAX_SAMPLE_LEN) // clear rest of sample data
+ memset(&song->sampleData[s->offset + sampleLength], 0, MAX_SAMPLE_LEN - sampleLength);
+
+ free(sampleData);
+
+ // set sample attributes
+ s->volume = (int8_t)sampleVolume;
+ s->fineTune = 0;
+ s->length = (uint16_t)sampleLength;
+ s->loopStart = (uint16_t)sampleLoopStart;
+ s->loopLength = (uint16_t)sampleLoopLength;
+
+ // read name
+ if (namePtr != 0 && nameLen > 0)
+ {
+ fseek(f, namePtr, SEEK_SET);
+ memset(tmpCharBuf, 0, sizeof (tmpCharBuf));
+
+ if (nameLen > 21)
+ {
+ fread(tmpCharBuf, 1, 21, f);
+ fseek(f, nameLen - 21, SEEK_CUR);
+ }
+ else
+ {
+ fread(tmpCharBuf, 1, nameLen, f);
+ }
+
+ nameFound = true;
+ }
+
+ fclose(f);
+
+ // copy over sample name
+ memset(s->text, '\0', sizeof (s->text));
+
+ if (nameFound)
+ {
+ nameLen = (uint32_t)strlen(tmpCharBuf);
+ if (nameLen > 21)
+ nameLen = 21;
+
+ for (i = 0; i < nameLen; i++)
+ s->text[i] = (char)tolower(tmpCharBuf[i]);
+ }
+ else
+ {
+ nameLen = (uint32_t)strlen(entryName);
+ if (nameLen > 21)
+ nameLen = 21;
+
+ for (i = 0; i < nameLen; i++)
+ s->text[i] = (char)tolower(entryName[i]);
+ }
+
+ // remove .iff from end of sample name (if present)
+ nameLen = (uint32_t)strlen(s->text);
+ if (nameLen >= 4 && !strncmp(&s->text[nameLen-4], ".IFF", 4))
+ memset(&s->text[nameLen-4], '\0', 4);
+
+ editor.sampleZero = false;
+ editor.samplePos = 0;
+
+ fixSampleBeep(s);
+ updateCurrSample();
+
+ updateWindowTitle(MOD_IS_MODIFIED);
+ return false;
+}
+
+bool loadRAWSample(UNICHAR *fileName, char *entryName)
+{
+ uint8_t i;
+ uint32_t nameLen, fileSize;
+ FILE *f;
+ moduleSample_t *s;
+
+ s = &song->samples[editor.currSample];
+
+ f = UNICHAR_FOPEN(fileName, "rb");
+ if (f == NULL)
+ {
+ displayErrorMsg("FILE I/O ERROR !");
+ return false;
+ }
+
+ fseek(f, 0, SEEK_END);
+ fileSize = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+ fileSize &= 0xFFFFFFFE;
+ if (fileSize > MAX_SAMPLE_LEN)
+ fileSize = MAX_SAMPLE_LEN;
+
+ turnOffVoices();
+
+ fread(&song->sampleData[s->offset], 1, fileSize, f);
+ fclose(f);
+
+ if (fileSize < MAX_SAMPLE_LEN)
+ memset(&song->sampleData[s->offset + fileSize], 0, MAX_SAMPLE_LEN - fileSize);
+
+ // set sample attributes
+ s->volume = 64;
+ s->fineTune = 0;
+ s->length = (uint16_t)fileSize;
+ s->loopStart = 0;
+ s->loopLength = 2;
+
+ // copy over sample name
+ nameLen = (uint32_t)strlen(entryName);
+ for (i = 0; i < 21; i++)
+ s->text[i] = (i < nameLen) ? (char)tolower(entryName[i]) : '\0';
+
+ s->text[21] = '\0';
+ s->text[22] = '\0';
+
+ editor.sampleZero = false;
+ editor.samplePos = 0;
+
+ fixSampleBeep(s);
+ updateCurrSample();
+
+ updateWindowTitle(MOD_IS_MODIFIED);
+ return true;
+}
+
+static int32_t getAIFFRate(uint8_t *in)
+{
+ int32_t exp;
+ uint32_t lo, hi;
+ double dOut;
+
+ exp = (int32_t)(((in[0] & 0x7F) << 8) | in[1]);
+ lo = (in[2] << 24) | (in[3] << 16) | (in[4] << 8) | in[5];
+ hi = (in[6] << 24) | (in[7] << 16) | (in[8] << 8) | in[9];
+
+ if (exp == 0 && lo == 0 && hi == 0)
+ return 0;
+
+ exp -= 16383;
+
+ dOut = ldexp(lo, -31 + exp) + ldexp(hi, -63 + exp);
+ return (int32_t)(dOut + 0.5);
+}
+
+bool loadAIFFSample(UNICHAR *fileName, char *entryName, int8_t forceDownSampling)
+{
+ bool unsigned8bit;
+ char compType[4];
+ int8_t *audioDataS8;
+ uint8_t *audioDataU8, sampleRateBytes[10];
+ int16_t *audioDataS16, smp16;
+ uint16_t bitDepth, numChannels;
+ int32_t filesize, *audioDataS32, smp32;
+ uint32_t nameLen, i, offset, sampleRate, sampleLength, blockName, blockSize;
+ uint32_t commPtr, commLen, ssndPtr, ssndLen;
+ FILE *f;
+ moduleSample_t *s;
+
+ unsigned8bit = false;
+ loadedFileWasAIFF = true;
+
+ if (forceDownSampling == -1)
+ {
+ // these two *must* be fully wiped, for outputting reasons
+ memset(editor.fileNameTmpU, 0, PATH_MAX);
+ memset(editor.entryNameTmp, 0, PATH_MAX);
+ UNICHAR_STRCPY(editor.fileNameTmpU, fileName);
+ strcpy(editor.entryNameTmp, entryName);
+ }
+
+ s = &song->samples[editor.currSample];
+
+ commPtr = 0; commLen = 0;
+ ssndPtr = 0; ssndLen = 0;
+
+ f = UNICHAR_FOPEN(fileName, "rb");
+ if (f == NULL)
+ {
+ displayErrorMsg("FILE I/O ERROR !");
+ return false;
+ }
+
+ fseek(f, 0, SEEK_END);
+ filesize = ftell(f);
+ if (filesize == 0)
+ {
+ displayErrorMsg("AIFF IS CORRUPT !");
+ return false;
+ }
+
+ fseek(f, 12, SEEK_SET);
+ while (!feof(f) && ftell(f) < filesize-12)
+ {
+ fread(&blockName, 4, 1, f); if (feof(f)) break;
+ fread(&blockSize, 4, 1, f); if (feof(f)) break;
+
+ blockName = SWAP32(blockName);
+ blockSize = SWAP32(blockSize);
+
+ switch (blockName)
+ {
+ case 0x434F4D4D: // "COMM"
+ {
+ commPtr = ftell(f);
+ commLen = blockSize;
+ }
+ break;
+
+ case 0x53534E44: // "SSND"
+ {
+ ssndPtr = ftell(f);
+ ssndLen = blockSize;
+ }
+ break;
+
+ default: break;
+ }
+
+ fseek(f, blockSize + (blockSize & 1), SEEK_CUR);
+ }
+
+ if (commPtr == 0 || commLen < 18 || ssndPtr == 0)
+ {
+ fclose(f);
+ displayErrorMsg("NOT A VALID AIFF!");
+ return false;
+ }
+
+ // kludge for some really strange AIFFs
+ if (ssndLen == 0)
+ ssndLen = filesize - ssndPtr;
+
+ if (ssndPtr+ssndLen > (uint32_t)filesize)
+ ssndLen = filesize - ssndPtr;
+
+ fseek(f, commPtr, SEEK_SET);
+ fread(&numChannels, 2, 1, f); numChannels = SWAP16(numChannels);
+ fseek(f, 4, SEEK_CUR);
+ fread(&bitDepth, 2, 1, f); bitDepth = SWAP16(bitDepth);
+ fread(sampleRateBytes, 1, 10, f);
+
+ fseek(f, 4 + 2 + 1, SEEK_CUR);
+
+ if (numChannels != 1 && numChannels != 2) // sample type
+ {
+ fclose(f);
+ displayErrorMsg("UNSUPPORTED AIFF!");
+ return false;
+ }
+
+ if (bitDepth != 8 && bitDepth != 16 && bitDepth != 24 && bitDepth != 32)
+ {
+ fclose(f);
+ displayErrorMsg("UNSUPPORTED AIFF!");
+ return false;
+ }
+
+ // read compression type (if present)
+ if (commLen > 18)
+ {
+ fread(&compType, 1, 4, f);
+ if (memcmp(compType, "NONE", 4))
+ {
+ fclose(f);
+ displayErrorMsg("UNSUPPORTED AIFF!");
+ return false;
+ }
+ }
+
+ sampleRate = getAIFFRate(sampleRateBytes);
+
+ // sample data chunk
+
+ fseek(f, ssndPtr, SEEK_SET);
+
+ fread(&offset, 4, 1, f);
+ if (offset > 0)
+ {
+ fclose(f);
+ displayErrorMsg("UNSUPPORTED AIFF!");
+ return false;
+ }
+
+ fseek(f, 4, SEEK_CUR);
+
+ ssndLen -= 8; // don't include offset and blockSize datas
+
+ sampleLength = ssndLen;
+ if (sampleLength == 0)
+ {
+ fclose(f);
+ displayErrorMsg("NOT A VALID AIFF!");
+ return false;
+ }
+
+ if (sampleRate > 22050)
+ {
+ if (forceDownSampling == -1)
+ {
+ showDownsampleAskDialog();
+ fclose(f);
+ return true;
+ }
+ }
+ else
+ {
+ forceDownSampling = false;
+ }
+
+ if (bitDepth == 8) // 8-BIT INTEGER SAMPLE
+ {
+ if (sampleLength > MAX_SAMPLE_LEN*4)
+ sampleLength = MAX_SAMPLE_LEN*4;
+
+ audioDataS8 = (int8_t *)malloc(sampleLength * sizeof (int8_t));
+ if (audioDataS8 == NULL)
+ {
+ fclose(f);
+ statusOutOfMemory();
+ return false;
+ }
+
+ // read sample data
+ if (fread(audioDataS8, 1, sampleLength, f) != sampleLength)
+ {
+ fclose(f);
+ free(audioDataS8);
+ displayErrorMsg("I/O ERROR !");
+ return false;
+ }
+
+ if (unsigned8bit)
+ {
+ for (i = 0; i < sampleLength; i++)
+ audioDataS8[i] ^= 0x80;
+ }
+
+ // convert from stereo to mono (if needed)
+ if (numChannels == 2)
+ {
+ sampleLength >>= 1;
+ for (i = 0; i < sampleLength-1; i++) // add right channel to left channel
+ {
+ smp16 = (audioDataS8[(i * 2) + 0] + audioDataS8[(i * 2) + 1]) >> 1;
+ audioDataS8[i] = (uint8_t)smp16;
+ }
+ }
+
+ // 2x downsampling - remove every other sample (if needed)
+ if (forceDownSampling)
+ {
+ if (config.sampleLowpass)
+ lowPassSample8Bit(audioDataS8, sampleLength, sampleRate, sampleRate / DOWNSAMPLE_CUTOFF_FACTOR);
+
+ sampleLength >>= 1;
+ for (i = 1; i < sampleLength; i++)
+ audioDataS8[i] = audioDataS8[i << 1];
+ }
+
+ if (sampleLength > MAX_SAMPLE_LEN)
+ sampleLength = MAX_SAMPLE_LEN;
+
+ turnOffVoices();
+ for (i = 0; i < MAX_SAMPLE_LEN; i++)
+ {
+ if (i <= (sampleLength & 0xFFFFFFFE))
+ song->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = audioDataS8[i];
+ else
+ song->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = 0; // clear rest of sample
+ }
+
+ free(audioDataS8);
+ }
+ else if (bitDepth == 16) // 16-BIT INTEGER SAMPLE
+ {
+ sampleLength >>= 1;
+ if (sampleLength > MAX_SAMPLE_LEN*4)
+ sampleLength = MAX_SAMPLE_LEN*4;
+
+ audioDataS16 = (int16_t *)malloc(sampleLength * sizeof (int16_t));
+ if (audioDataS16 == NULL)
+ {
+ fclose(f);
+ statusOutOfMemory();
+ return false;
+ }
+
+ // read sample data
+ if (fread(audioDataS16, 2, sampleLength, f) != sampleLength)
+ {
+ fclose(f);
+ free(audioDataS16);
+ displayErrorMsg("I/O ERROR !");
+ return false;
+ }
+
+ // fix endianness
+ for (i = 0; i < sampleLength; i++)
+ audioDataS16[i] = SWAP16(audioDataS16[i]);
+
+ // convert from stereo to mono (if needed)
+ if (numChannels == 2)
+ {
+ sampleLength >>= 1;
+ for (i = 0; i < sampleLength-1; i++) // add right channel to left channel
+ {
+ smp32 = (audioDataS16[(i << 1) + 0] + audioDataS16[(i << 1) + 1]) >> 1;
+ audioDataS16[i] = (int16_t)(smp32);
+ }
+ }
+
+ // 2x downsampling - remove every other sample (if needed)
+ if (forceDownSampling)
+ {
+ if (config.sampleLowpass)
+ lowPassSample16Bit(audioDataS16, sampleLength, sampleRate, sampleRate / DOWNSAMPLE_CUTOFF_FACTOR);
+
+ sampleLength >>= 1;
+ for (i = 1; i < sampleLength; i++)
+ audioDataS16[i] = audioDataS16[i << 1];
+ }
+
+ if (sampleLength > MAX_SAMPLE_LEN)
+ sampleLength = MAX_SAMPLE_LEN;
+
+ normalize16bitSigned(audioDataS16, sampleLength);
+
+ turnOffVoices();
+ for (i = 0; i < MAX_SAMPLE_LEN; i++)
+ {
+ if (i <= (sampleLength & 0xFFFFFFFE))
+ song->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = audioDataS16[i] >> 8;
+ else
+ song->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = 0; // clear rest of sample
+ }
+
+ free(audioDataS16);
+ }
+ else if (bitDepth == 24) // 24-BIT INTEGER SAMPLE
+ {
+ sampleLength /= 3;
+ if (sampleLength > MAX_SAMPLE_LEN*4)
+ sampleLength = MAX_SAMPLE_LEN*4;
+
+ audioDataS32 = (int32_t *)malloc(sampleLength * sizeof (int32_t));
+ if (audioDataS32 == NULL)
+ {
+ fclose(f);
+ statusOutOfMemory();
+ return false;
+ }
+
+ // read sample data
+ if (fread(&audioDataS32[sampleLength >> 2], 3, sampleLength, f) != sampleLength)
+ {
+ fclose(f);
+ free(audioDataS32);
+ displayErrorMsg("I/O ERROR !");
+ return false;
+ }
+
+ // convert to 32-bit
+ audioDataU8 = (uint8_t *)audioDataS32 + sampleLength;
+ for (i = 0; i < sampleLength; i++)
+ {
+ audioDataS32[i] = (audioDataU8[0] << 24) | (audioDataU8[1] << 16) | (audioDataU8[2] << 8);
+ audioDataU8 += 3;
+ }
+
+ // convert from stereo to mono (if needed)
+ if (numChannels == 2)
+ {
+ sampleLength >>= 1;
+ for (i = 0; i < sampleLength-1; i++) // add right channel to left channel
+ {
+ int64_t smp = ((int64_t)audioDataS32[(i << 1) + 0] + audioDataS32[(i << 1) + 1]) >> 1;
+ audioDataS32[i] = (int32_t)smp;
+ }
+ }
+
+ // 2x downsampling - remove every other sample (if needed)
+ if (forceDownSampling)
+ {
+ if (config.sampleLowpass)
+ lowPassSample32Bit(audioDataS32, sampleLength, sampleRate, sampleRate / DOWNSAMPLE_CUTOFF_FACTOR);
+
+ sampleLength >>= 1;
+ for (i = 1; i < sampleLength; i++)
+ audioDataS32[i] = audioDataS32[i << 1];
+ }
+
+ if (sampleLength > MAX_SAMPLE_LEN)
+ sampleLength = MAX_SAMPLE_LEN;
+
+ normalize32bitSigned(audioDataS32, sampleLength);
+
+ turnOffVoices();
+ for (i = 0; i < MAX_SAMPLE_LEN; i++)
+ {
+ if (i <= (sampleLength & 0xFFFFFFFE))
+ song->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = (int8_t)(audioDataS32[i] >> 24);
+ else
+ song->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = 0; // clear rest of sample
+ }
+
+ free(audioDataS32);
+ }
+ else if (bitDepth == 32) // 32-BIT INTEGER SAMPLE
+ {
+ sampleLength >>= 2;
+ if (sampleLength > MAX_SAMPLE_LEN*4)
+ sampleLength = MAX_SAMPLE_LEN*4;
+
+ audioDataS32 = (int32_t *)malloc(sampleLength * sizeof (int32_t));
+ if (audioDataS32 == NULL)
+ {
+ fclose(f);
+ statusOutOfMemory();
+ return false;
+ }
+
+ // read sample data
+ if (fread(audioDataS32, 4, sampleLength, f) != sampleLength)
+ {
+ fclose(f);
+ free(audioDataS32);
+ displayErrorMsg("I/O ERROR !");
+ return false;
+ }
+
+ // fix endianness
+ for (i = 0; i < sampleLength; i++)
+ audioDataS32[i] = SWAP32(audioDataS32[i]);
+
+ // convert from stereo to mono (if needed)
+ if (numChannels == 2)
+ {
+ sampleLength >>= 1;
+ for (i = 0; i < sampleLength-1; i++) // add right channel to left channel
+ {
+ int64_t smp = ((int64_t)audioDataS32[(i << 1) + 0] + audioDataS32[(i << 1) + 1]) >> 1;
+ audioDataS32[i] = (int32_t)smp;
+ }
+ }
+
+ // 2x downsampling - remove every other sample (if needed)
+ if (forceDownSampling)
+ {
+ if (config.sampleLowpass)
+ lowPassSample32Bit(audioDataS32, sampleLength, sampleRate, sampleRate / DOWNSAMPLE_CUTOFF_FACTOR);
+
+ sampleLength >>= 1;
+ for (i = 1; i < sampleLength; i++)
+ audioDataS32[i] = audioDataS32[i << 1];
+ }
+
+ if (sampleLength > MAX_SAMPLE_LEN)
+ sampleLength = MAX_SAMPLE_LEN;
+
+ normalize32bitSigned(audioDataS32, sampleLength);
+
+ turnOffVoices();
+ for (i = 0; i < MAX_SAMPLE_LEN; i++)
+ {
+ if (i <= (sampleLength & 0xFFFFFFFE))
+ song->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = (int8_t)(audioDataS32[i] >> 24);
+ else
+ song->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = 0; // clear rest of sample
+ }
+
+ free(audioDataS32);
+ }
+
+ // set sample length
+ if (sampleLength & 1)
+ {
+ if (++sampleLength > MAX_SAMPLE_LEN)
+ sampleLength = MAX_SAMPLE_LEN;
+ }
+
+ s->length = (uint16_t)sampleLength;
+ s->fineTune = 0;
+ s->volume = 64;
+ s->loopStart = 0;
+ s->loopLength = 2;
+
+ fclose(f);
+
+ // copy over sample name
+ nameLen = (uint32_t)strlen(entryName);
+ for (i = 0; i < 21; i++)
+ s->text[i] = (i < nameLen) ? (char)tolower(entryName[i]) : '\0';
+
+ s->text[21] = '\0';
+ s->text[22] = '\0';
+
+ // remove .aiff from end of sample name (if present)
+ nameLen = (uint32_t)strlen(s->text);
+ if (nameLen >= 5 && !_strnicmp(&s->text[nameLen-5], ".AIFF", 5))
+ memset(&s->text[nameLen-5], '\0', 5);
+
+ editor.sampleZero = false;
+ editor.samplePos = 0;
+
+ fixSampleBeep(s);
+ updateCurrSample();
+
+ updateWindowTitle(MOD_IS_MODIFIED);
+ return true;
+}
+
+bool loadSample(UNICHAR *fileName, char *entryName)
+{
+ uint32_t fileSize, ID;
+ FILE *f;
+
+ if (editor.sampleZero)
+ {
+ statusNotSampleZero();
+ return false;
+ }
+
+ f = UNICHAR_FOPEN(fileName, "rb");
+ if (f == NULL)
+ {
+ displayErrorMsg("FILE I/O ERROR !");
+ return false;
+ }
+
+ fseek(f, 0, SEEK_END);
+ fileSize = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+ // first, check heades before we eventually load as RAW
+ if (fileSize > 16)
+ {
+ fread(&ID, 4, 1, f);
+
+ // check if it's actually a WAV sample
+ if (ID == 0x46464952) // "RIFF"
+ {
+ fseek(f, 4, SEEK_CUR);
+ fread(&ID, 4, 1, f);
+
+ if (ID == 0x45564157) // "WAVE"
+ {
+ fread(&ID, 4, 1, f);
+ if (ID == 0x20746D66) // "fmt "
+ {
+ fclose(f);
+ return loadWAVSample(fileName, entryName, -1);
+ }
+ }
+ }
+ else if (ID == 0x4D524F46) // "FORM"
+ {
+ fseek(f, 4, SEEK_CUR);
+ fread(&ID, 4, 1, f);
+
+ // check if it's an Amiga IFF sample
+ if (ID == 0x58565338 || ID == 0x56533631) // "8SVX" (normal) and "16SV" (FT2 sample)
+ {
+ fclose(f);
+ return loadIFFSample(fileName, entryName);
+ }
+
+ // check if it's an AIFF sample
+ else if (ID == 0x46464941) // "AIFF"
+ {
+ fclose(f);
+ return loadAIFFSample(fileName, entryName, -1);
+ }
+
+ else if (ID == 0x43464941) // "AIFC" (compressed AIFF)
+ {
+ fclose(f);
+ displayErrorMsg("UNSUPPORTED AIFF!");
+ return false;
+ }
+ }
+ }
+
+ // nope, continue loading as RAW
+ fclose(f);
+
+ return loadRAWSample(fileName, entryName);
+}
--- /dev/null
+++ b/src/pt2_sample_loader.h
@@ -1,0 +1,8 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "pt2_unicode.h"
+
+void extLoadWAVOrAIFFSampleCallback(bool downsample);
+bool loadSample(UNICHAR *fileName, char *entryName);
--- /dev/null
+++ b/src/pt2_sample_saver.c
@@ -1,0 +1,288 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <ctype.h> // tolower()
+#include <sys/stat.h>
+#include "pt2_header.h"
+#include "pt2_structs.h"
+#include "pt2_textout.h"
+#include "pt2_mouse.h"
+#include "pt2_visuals.h"
+#include "pt2_helpers.h"
+#include "pt2_diskop.h"
+
+#define PLAYBACK_FREQ 16574 /* C-3 */
+
+static void removeSampleFileExt(char *text) // for sample saver
+{
+ uint32_t fileExtPos;
+ uint32_t filenameLength;
+
+ if (text == NULL || text[0] == '\0')
+ return;
+
+ filenameLength = (uint32_t)strlen(text);
+ if (filenameLength < 5)
+ return;
+
+ // remove .wav/.iff/from end of sample name (if present)
+ fileExtPos = filenameLength - 4;
+ if (fileExtPos > 0 && (!strncmp(&text[fileExtPos], ".wav", 4) || !strncmp(&text[fileExtPos], ".iff", 4)))
+ text[fileExtPos] = '\0';
+}
+
+static void iffWriteChunkHeader(FILE *f, char *chunkName, uint32_t chunkLen)
+{
+ fwrite(chunkName, sizeof (int32_t), 1, f);
+ chunkLen = SWAP32(chunkLen);
+ fwrite(&chunkLen, sizeof (int32_t), 1, f);
+}
+
+
+static void iffWriteUint32(FILE *f, uint32_t value)
+{
+ value = SWAP32(value);
+ fwrite(&value, sizeof (int32_t), 1, f);
+}
+
+static void iffWriteUint16(FILE *f, uint16_t value)
+{
+ value = SWAP16(value);
+ fwrite(&value, sizeof (int16_t), 1, f);
+}
+
+static void iffWriteUint8(FILE *f, const uint8_t value)
+{
+ fwrite(&value, sizeof (int8_t), 1, f);
+}
+
+static void iffWriteChunkData(FILE *f, const void *data, size_t length)
+{
+ fwrite(data, sizeof (int8_t), length, f);
+ if (length & 1) fputc(0, f); // write pad byte if chunk size is uneven
+}
+
+bool saveSample(bool checkIfFileExist, bool giveNewFreeFilename)
+{
+ char fileName[128], tmpBuffer[64];
+ uint32_t i, j, chunkLen;
+ FILE *f;
+ struct stat statBuffer;
+ wavHeader_t wavHeader;
+ samplerChunk_t samplerChunk;
+ mptExtraChunk_t mptExtraChunk;
+
+ const moduleSample_t *s = &song->samples[editor.currSample];
+
+ if (s->length == 0)
+ {
+ statusSampleIsEmpty();
+ return false;
+ }
+
+ // get sample filename
+ if (s->text[0] == '\0')
+ {
+ strcpy(fileName, "untitled");
+ }
+ else
+ {
+ for (i = 0; i < 22; i++)
+ {
+ tmpBuffer[i] = (char)tolower(song->samples[editor.currSample].text[i]);
+ if (tmpBuffer[i] == '\0') break;
+ sanitizeFilenameChar(&tmpBuffer[i]);
+ }
+
+ strcpy(fileName, tmpBuffer);
+ }
+
+ removeSampleFileExt(fileName);
+ addSampleFileExt(fileName);
+
+ // if the user picked "no" to overwriting the file, generate a new filename
+ if (giveNewFreeFilename && stat(fileName, &statBuffer) == 0)
+ {
+ for (j = 1; j <= 999; j++)
+ {
+ if (s->text[0] == '\0')
+ {
+ sprintf(fileName, "untitled-%d", j);
+ }
+ else
+ {
+ for (i = 0; i < 22; i++)
+ {
+ tmpBuffer[i] = (char)tolower(song->samples[editor.currSample].text[i]);
+ if (tmpBuffer[i] == '\0') break;
+ sanitizeFilenameChar(&tmpBuffer[i]);
+ }
+
+ removeSampleFileExt(tmpBuffer);
+ sprintf(fileName, "%s-%d", tmpBuffer, j);
+ }
+
+ addSampleFileExt(fileName);
+
+ if (stat(fileName, &statBuffer) != 0)
+ break; // this filename can be used
+ }
+ }
+
+ // check if we need to overwrite file...
+
+ if (checkIfFileExist && stat(fileName, &statBuffer) == 0)
+ {
+ ui.askScreenShown = true;
+ ui.askScreenType = ASK_SAVESMP_OVERWRITE;
+ pointerSetMode(POINTER_MODE_MSG1, NO_CARRY);
+ setStatusMessage("OVERWRITE FILE ?", NO_CARRY);
+ renderAskDialog();
+ return -1;
+ }
+
+ if (ui.askScreenShown)
+ {
+ ui.answerNo = false;
+ ui.answerYes = false;
+ ui.askScreenShown = false;
+ }
+
+ f = fopen(fileName, "wb");
+ if (f == NULL)
+ {
+ displayErrorMsg("FILE I/O ERROR !");
+ return false;
+ }
+
+ const int8_t *sampleData = &song->sampleData[s->offset];
+ const uint32_t sampleLength = s->length;
+ const uint32_t loopStart = s->loopStart & 0xFFFE;
+ const uint32_t loopLength = s->loopLength & 0xFFFE;
+
+ switch (diskop.smpSaveType)
+ {
+ default:
+ case DISKOP_SMP_WAV:
+ {
+ wavHeader.format = 0x45564157; // "WAVE"
+ wavHeader.chunkID = 0x46464952; // "RIFF"
+ wavHeader.subchunk1ID = 0x20746D66; // "fmt "
+ wavHeader.subchunk2ID = 0x61746164; // "data"
+ wavHeader.subchunk1Size = 16;
+ wavHeader.subchunk2Size = sampleLength;
+ wavHeader.chunkSize = 36 + wavHeader.subchunk2Size;
+ wavHeader.audioFormat = 1;
+ wavHeader.numChannels = 1;
+ wavHeader.bitsPerSample = 8;
+ wavHeader.sampleRate = PLAYBACK_FREQ;
+ wavHeader.byteRate = wavHeader.sampleRate * wavHeader.numChannels * wavHeader.bitsPerSample / 8;
+ wavHeader.blockAlign = wavHeader.numChannels * wavHeader.bitsPerSample / 8;
+
+ // set "sampler" chunk if loop is enabled
+ if (loopStart+loopLength > 2) // loop enabled?
+ {
+ wavHeader.chunkSize += sizeof (samplerChunk_t);
+ memset(&samplerChunk, 0, sizeof (samplerChunk_t));
+ samplerChunk.chunkID = 0x6C706D73; // "smpl"
+ samplerChunk.chunkSize = 60;
+ samplerChunk.dwSamplePeriod = 1000000000 / PLAYBACK_FREQ;
+ samplerChunk.dwMIDIUnityNote = 60; // 60 = MIDI middle-C
+ samplerChunk.cSampleLoops = 1;
+ samplerChunk.loop.dwStart = loopStart;
+ samplerChunk.loop.dwEnd = (loopStart + loopLength) - 1;
+ }
+
+ // set ModPlug Tracker chunk (used for sample volume only in this case)
+ wavHeader.chunkSize += sizeof (mptExtraChunk);
+ memset(&mptExtraChunk, 0, sizeof (mptExtraChunk));
+ mptExtraChunk.chunkID = 0x61727478; // "xtra"
+ mptExtraChunk.chunkSize = sizeof (mptExtraChunk) - 4 - 4;
+ mptExtraChunk.defaultPan = 128; // 0..255
+ mptExtraChunk.defaultVolume = s->volume * 4; // 0..256
+ mptExtraChunk.globalVolume = 64; // 0..64
+
+ fwrite(&wavHeader, sizeof (wavHeader_t), 1, f);
+
+ for (i = 0; i < sampleLength; i++)
+ fputc((uint8_t)(sampleData[i] + 128), f);
+
+ if (sampleLength & 1)
+ fputc(0, f); // pad align byte
+
+ if (loopStart+loopLength > 2) // loop enabled?
+ fwrite(&samplerChunk, sizeof (samplerChunk), 1, f);
+
+ fwrite(&mptExtraChunk, sizeof (mptExtraChunk), 1, f);
+ }
+ break;
+
+ case DISKOP_SMP_IFF:
+ {
+ // "FORM" chunk
+ iffWriteChunkHeader(f, "FORM", 0); // "FORM" chunk size is overwritten later
+ iffWriteUint32(f, 0x38535658); // "8SVX"
+
+ // "VHDR" chunk
+ iffWriteChunkHeader(f, "VHDR", 20);
+
+ if (loopStart+loopLength > 2) // loop enabled?
+ {
+ iffWriteUint32(f, loopStart); // oneShotHiSamples
+ iffWriteUint32(f, loopLength); // repeatHiSamples
+ }
+ else
+ {
+ iffWriteUint32(f, 0); // oneShotHiSamples
+ iffWriteUint32(f, 0); // repeatHiSamples
+ }
+
+ iffWriteUint32(f, 0); // samplesPerHiCycle
+ iffWriteUint16(f, PLAYBACK_FREQ); // samplesPerSec
+ iffWriteUint8(f, 1); // ctOctave (number of samples)
+ iffWriteUint8(f, 0); // sCompression
+ iffWriteUint32(f, s->volume * 1024); // volume (max: 65536/0x10000)
+
+ // "NAME" chunk
+ chunkLen = (uint32_t)strlen(s->text);
+ if (chunkLen > 0)
+ {
+ iffWriteChunkHeader(f, "NAME", chunkLen);
+ iffWriteChunkData(f, s->text, chunkLen);
+ }
+
+ // "ANNO" chunk (we put the program name here)
+ const char annoStr[] = "ProTracker 2 clone";
+ chunkLen = sizeof (annoStr) - 1;
+ iffWriteChunkHeader(f, "ANNO", chunkLen);
+ iffWriteChunkData(f, annoStr, chunkLen);
+
+ // "BODY" chunk
+ chunkLen = sampleLength;
+ iffWriteChunkHeader(f, "BODY", chunkLen);
+ iffWriteChunkData(f, sampleData, chunkLen);
+
+ // go back and fill in "FORM" chunk size
+ chunkLen = ftell(f) - 8;
+ fseek(f, 4, SEEK_SET);
+ iffWriteUint32(f, chunkLen);
+ }
+ break;
+
+ case DISKOP_SMP_RAW:
+ fwrite(sampleData, 1, sampleLength, f);
+ break;
+ }
+
+ fclose(f);
+
+ displayMsg("SAMPLE SAVED !");
+ setMsgPointer();
+
+ diskop.cached = false;
+ if (ui.diskOpScreenShown)
+ ui.updateDiskOpFileList = true;
+
+ return true;
+}
--- /dev/null
+++ b/src/pt2_sample_saver.h
@@ -1,0 +1,5 @@
+#pragma once
+
+#include <stdbool.h>
+
+bool saveSample(bool checkIfFileExist, bool giveNewFreeFilename);
--- a/src/pt2_sampleloader.c
+++ /dev/null
@@ -1,2025 +1,0 @@
-// for finding memory leaks in debug mode with Visual Studio
-#if defined _DEBUG && defined _MSC_VER
-#include <crtdbg.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <stdbool.h>
-#include <ctype.h> // tolower()/toupper()
-#ifdef _WIN32
-#include <io.h>
-#else
-#include <unistd.h>
-#endif
-#include <fcntl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include "pt2_header.h"
-#include "pt2_textout.h"
-#include "pt2_mouse.h"
-#include "pt2_sampler.h"
-#include "pt2_audio.h"
-#include "pt2_sampleloader.h"
-#include "pt2_visuals.h"
-#include "pt2_helpers.h"
-#include "pt2_unicode.h"
-
-#define DOWNSAMPLE_CUTOFF_FACTOR 4.0
-
-enum
-{
- WAV_FORMAT_PCM = 0x0001,
- WAV_FORMAT_IEEE_FLOAT = 0x0003
-};
-
-static bool loadWAVSample(UNICHAR *fileName, char *entryName, int8_t forceDownSampling);
-static bool loadIFFSample(UNICHAR *fileName, char *entryName);
-static bool loadRAWSample(UNICHAR *fileName, char *entryName);
-static bool loadAIFFSample(UNICHAR *fileName, char *entryName, int8_t forceDownSampling);
-
-static bool loadedFileWasAIFF;
-
-static bool lowPassSample8Bit(int8_t *buffer, int32_t length, int32_t sampleFrequency, double cutoff)
-{
- rcFilter_t filter;
-
- if (buffer == NULL || length == 0 || cutoff == 0.0)
- return false;
-
- calcRCFilterCoeffs(sampleFrequency, cutoff, &filter);
- clearRCFilterState(&filter);
-
- for (int32_t i = 0; i < length; i++)
- {
- int32_t sample;
- double dSample;
-
- RCLowPassFilterMono(&filter, buffer[i], &dSample);
- sample = (int32_t)dSample;
-
- buffer[i] = (int8_t)CLAMP(sample, INT8_MIN, INT8_MAX);
- }
-
- return true;
-}
-
-static bool lowPassSample8BitUnsigned(uint8_t *buffer, int32_t length, int32_t sampleFrequency, double cutoff)
-{
- rcFilter_t filter;
-
- if (buffer == NULL || length == 0 || cutoff == 0.0)
- return false;
-
- calcRCFilterCoeffs(sampleFrequency, cutoff, &filter);
- clearRCFilterState(&filter);
-
- for (int32_t i = 0; i < length; i++)
- {
- int32_t sample;
- double dSample;
-
- RCLowPassFilterMono(&filter, buffer[i] - 128, &dSample);
- sample = (int32_t)dSample;
-
- sample = CLAMP(sample, INT8_MIN, INT8_MAX);
- buffer[i] = (uint8_t)(sample + 128);
- }
-
- return true;
-}
-
-static bool lowPassSample16Bit(int16_t *buffer, int32_t length, int32_t sampleFrequency, double cutoff)
-{
- rcFilter_t filter;
-
- if (buffer == NULL || length == 0 || cutoff == 0.0)
- return false;
-
- calcRCFilterCoeffs(sampleFrequency, cutoff, &filter);
- clearRCFilterState(&filter);
-
- for (int32_t i = 0; i < length; i++)
- {
- int32_t sample;
- double dSample;
-
- RCLowPassFilterMono(&filter, buffer[i], &dSample);
- sample = (int32_t)dSample;
-
- buffer[i] = (int16_t)CLAMP(sample, INT16_MIN, INT16_MAX);
- }
-
- return true;
-}
-
-static bool lowPassSample32Bit(int32_t *buffer, int32_t length, int32_t sampleFrequency, double cutoff)
-{
- rcFilter_t filter;
-
- if (buffer == NULL || length == 0 || cutoff == 0.0)
- return false;
-
- calcRCFilterCoeffs(sampleFrequency, cutoff, &filter);
- clearRCFilterState(&filter);
-
- for (int32_t i = 0; i < length; i++)
- {
- int64_t sample;
- double dSample;
-
- RCLowPassFilterMono(&filter, buffer[i], &dSample);
- sample = (int32_t)dSample;
-
- buffer[i] = (int32_t)CLAMP(sample, INT32_MIN, INT32_MAX);
- }
-
- return true;
-}
-
-static bool lowPassSampleFloat(float *buffer, int32_t length, int32_t sampleFrequency, double cutoff)
-{
- rcFilter_t filter;
-
- if (buffer == NULL || length == 0 || cutoff == 0.0)
- return false;
-
- calcRCFilterCoeffs(sampleFrequency, cutoff, &filter);
- clearRCFilterState(&filter);
-
- for (int32_t i = 0; i < length; i++)
- {
- double dSample;
-
- RCLowPassFilterMono(&filter, buffer[i], &dSample);
- buffer[i] = (float)dSample;
- }
-
- return true;
-}
-
-static bool lowPassSampleDouble(double *buffer, int32_t length, int32_t sampleFrequency, double cutoff)
-{
- rcFilter_t filter;
-
- if (buffer == NULL || length == 0 || cutoff == 0.0)
- return false;
-
- calcRCFilterCoeffs(sampleFrequency, cutoff, &filter);
- clearRCFilterState(&filter);
-
- for (int32_t i = 0; i < length; i++)
- {
- double dSample;
- RCLowPassFilterMono(&filter, buffer[i], &dSample);
-
- buffer[i] = dSample;
- }
-
- return true;
-}
-
-void extLoadWAVOrAIFFSampleCallback(bool downsample)
-{
- if (loadedFileWasAIFF)
- loadAIFFSample(editor.fileNameTmpU, editor.entryNameTmp, downsample);
- else
- loadWAVSample(editor.fileNameTmpU, editor.entryNameTmp, downsample);
-}
-
-bool loadWAVSample(UNICHAR *fileName, char *entryName, int8_t forceDownSampling)
-{
- bool wavSampleNameFound;
- uint8_t *audioDataU8;
- int16_t *audioDataS16, tempVol, smp16;
- uint16_t audioFormat, numChannels, bitsPerSample;
- int32_t *audioDataS32, smp32;
- uint32_t *audioDataU32, i, nameLen, chunkID, chunkSize;
- uint32_t sampleLength, sampleRate, filesize, loopFlags;
- uint32_t loopStart, loopEnd, dataPtr, dataLen, fmtPtr, endOfChunk, bytesRead;
- uint32_t fmtLen, inamPtr, inamLen, smplPtr, smplLen, xtraPtr, xtraLen;
- float *fAudioDataFloat, fSmp;
- double *dAudioDataDouble, dSmp;
- FILE *f;
- moduleSample_t *s;
-
- loadedFileWasAIFF = false;
-
- // zero out chunk pointers and lengths
- fmtPtr = 0; fmtLen = 0;
- dataPtr = 0; dataLen = 0;
- inamPtr = 0; inamLen = 0;
- xtraPtr = 0; xtraLen = 0;
- smplPtr = 0; smplLen = 0;
-
- wavSampleNameFound = false;
-
- s = &modEntry->samples[editor.currSample];
-
- if (forceDownSampling == -1)
- {
- // these two *must* be fully wiped, for outputting reasons
- memset(editor.fileNameTmpU, 0, PATH_MAX);
- memset(editor.entryNameTmp, 0, PATH_MAX);
- UNICHAR_STRCPY(editor.fileNameTmpU, fileName);
- strcpy(editor.entryNameTmp, entryName);
- }
-
- f = UNICHAR_FOPEN(fileName, "rb");
- if (f == NULL)
- {
- displayErrorMsg("FILE I/O ERROR !");
- return false;
- }
-
- fseek(f, 0, SEEK_END);
- filesize = ftell(f);
- if (filesize == 0)
- {
- fclose(f);
-
- displayErrorMsg("NOT A WAV !");
- return false;
- }
-
- // look for wanted chunks and set up pointers + lengths
- fseek(f, 12, SEEK_SET);
-
- bytesRead = 0;
- while (!feof(f) && bytesRead < filesize-12)
- {
- fread(&chunkID, 4, 1, f); if (feof(f)) break;
- fread(&chunkSize, 4, 1, f); if (feof(f)) break;
-
- endOfChunk = (ftell(f) + chunkSize) + (chunkSize & 1);
- switch (chunkID)
- {
- case 0x20746D66: // "fmt "
- {
- fmtPtr = ftell(f);
- fmtLen = chunkSize;
- }
- break;
-
- case 0x61746164: // "data"
- {
- dataPtr = ftell(f);
- dataLen = chunkSize;
- }
- break;
-
- case 0x5453494C: // "LIST"
- {
- if (chunkSize >= 4)
- {
- fread(&chunkID, 4, 1, f);
- if (chunkID == 0x4F464E49) // "INFO"
- {
- bytesRead = 0;
- while (!feof(f) && (bytesRead < chunkSize))
- {
- fread(&chunkID, 4, 1, f);
- fread(&chunkSize, 4, 1, f);
-
- switch (chunkID)
- {
- case 0x4D414E49: // "INAM"
- {
- inamPtr = ftell(f);
- inamLen = chunkSize;
- }
- break;
-
- default: break;
- }
-
- bytesRead += (chunkSize + (chunkSize & 1));
- }
- }
- }
- }
- break;
-
- case 0x61727478: // "xtra"
- {
- xtraPtr = ftell(f);
- xtraLen = chunkSize;
- }
- break;
-
- case 0x6C706D73: // "smpl"
- {
- smplPtr = ftell(f);
- smplLen = chunkSize;
- }
- break;
-
- default: break;
- }
-
- bytesRead += chunkSize + (chunkSize & 1);
- fseek(f, endOfChunk, SEEK_SET);
- }
-
- // we need at least "fmt " and "data" - check if we found them sanely
- if ((fmtPtr == 0 || fmtLen < 16) || (dataPtr == 0 || dataLen == 0))
- {
- fclose(f);
- displayErrorMsg("NOT A WAV !");
- return false;
- }
-
- // ---- READ "fmt " CHUNK ----
- fseek(f, fmtPtr, SEEK_SET);
- fread(&audioFormat, 2, 1, f);
- fread(&numChannels, 2, 1, f);
- fread(&sampleRate, 4, 1, f);
- fseek(f, 6, SEEK_CUR);
- fread(&bitsPerSample, 2, 1, f);
- sampleLength = dataLen;
- // ---------------------------
-
- if (sampleRate == 0 || sampleLength == 0 || sampleLength >= filesize*(bitsPerSample/8))
- {
- fclose(f);
- displayErrorMsg("WAV CORRUPT !");
- return false;
- }
-
- if (audioFormat != WAV_FORMAT_PCM && audioFormat != WAV_FORMAT_IEEE_FLOAT)
- {
- fclose(f);
- displayErrorMsg("WAV UNSUPPORTED !");
- return false;
- }
-
- if ((numChannels == 0) || (numChannels > 2))
- {
- fclose(f);
- displayErrorMsg("WAV UNSUPPORTED !");
- return false;
- }
-
- if (audioFormat == WAV_FORMAT_IEEE_FLOAT && bitsPerSample != 32 && bitsPerSample != 64)
- {
- fclose(f);
- displayErrorMsg("WAV UNSUPPORTED !");
- return false;
- }
-
- if (bitsPerSample != 8 && bitsPerSample != 16 && bitsPerSample != 24 && bitsPerSample != 32 && bitsPerSample != 64)
- {
- fclose(f);
- displayErrorMsg("WAV UNSUPPORTED !");
- return false;
- }
-
- if (sampleRate > 22050)
- {
- if (forceDownSampling == -1)
- {
- showDownsampleAskDialog();
- fclose(f);
- return true;
- }
- }
- else
- {
- forceDownSampling = false;
- }
-
- // ---- READ SAMPLE DATA ----
- fseek(f, dataPtr, SEEK_SET);
-
- if (bitsPerSample == 8) // 8-BIT INTEGER SAMPLE
- {
- if (sampleLength > MAX_SAMPLE_LEN*4)
- sampleLength = MAX_SAMPLE_LEN*4;
-
- audioDataU8 = (uint8_t *)malloc(sampleLength * sizeof (uint8_t));
- if (audioDataU8 == NULL)
- {
- fclose(f);
- statusOutOfMemory();
- return false;
- }
-
- // read sample data
- if (fread(audioDataU8, 1, sampleLength, f) != sampleLength)
- {
- fclose(f);
- free(audioDataU8);
- displayErrorMsg("I/O ERROR !");
- return false;
- }
-
- // convert from stereo to mono (if needed)
- if (numChannels == 2)
- {
- sampleLength >>= 1;
- for (i = 0; i < sampleLength-1; i++) // add right channel to left channel
- {
- smp16 = (audioDataU8[(i << 1) + 0] - 128) + (audioDataU8[(i << 1) + 1] - 128);
- smp16 = 128 + (smp16 >> 1);
- audioDataU8[i] = (uint8_t)smp16;
- }
- }
-
- // 2x downsampling - remove every other sample (if needed)
- if (forceDownSampling)
- {
- if (config.sampleLowpass)
- lowPassSample8BitUnsigned(audioDataU8, sampleLength, sampleRate, sampleRate / DOWNSAMPLE_CUTOFF_FACTOR);
-
- sampleLength >>= 1;
- for (i = 1; i < sampleLength; i++)
- audioDataU8[i] = audioDataU8[i << 1];
- }
-
- if (sampleLength > MAX_SAMPLE_LEN)
- sampleLength = MAX_SAMPLE_LEN;
-
- turnOffVoices();
- for (i = 0; i < MAX_SAMPLE_LEN; i++)
- {
- if (i <= (sampleLength & 0xFFFFFFFE))
- modEntry->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = audioDataU8[i] - 128;
- else
- modEntry->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = 0; // clear rest of sample
- }
-
- free(audioDataU8);
- }
- else if (bitsPerSample == 16) // 16-BIT INTEGER SAMPLE
- {
- sampleLength >>= 1;
- if (sampleLength > MAX_SAMPLE_LEN*4)
- sampleLength = MAX_SAMPLE_LEN*4;
-
- audioDataS16 = (int16_t *)malloc(sampleLength * sizeof (int16_t));
- if (audioDataS16 == NULL)
- {
- fclose(f);
- statusOutOfMemory();
- return false;
- }
-
- // read sample data
- if (fread(audioDataS16, 2, sampleLength, f) != sampleLength)
- {
- fclose(f);
- free(audioDataS16);
- displayErrorMsg("I/O ERROR !");
- return false;
- }
-
- // convert from stereo to mono (if needed)
- if (numChannels == 2)
- {
- sampleLength >>= 1;
- for (i = 0; i < sampleLength-1; i++) // add right channel to left channel
- {
- smp32 = (audioDataS16[(i << 1) + 0] + audioDataS16[(i << 1) + 1]) >> 1;
- audioDataS16[i] = (int16_t)smp32;
- }
- }
-
- // 2x downsampling - remove every other sample (if needed)
- if (forceDownSampling)
- {
- if (config.sampleLowpass)
- lowPassSample16Bit(audioDataS16, sampleLength, sampleRate, sampleRate / DOWNSAMPLE_CUTOFF_FACTOR);
-
- sampleLength >>= 1;
- for (i = 1; i < sampleLength; i++)
- audioDataS16[i] = audioDataS16[i << 1];
- }
-
- if (sampleLength > MAX_SAMPLE_LEN)
- sampleLength = MAX_SAMPLE_LEN;
-
- normalize16bitSigned(audioDataS16, sampleLength);
-
- turnOffVoices();
- for (i = 0; i < MAX_SAMPLE_LEN; i++)
- {
- if (i <= (sampleLength & 0xFFFFFFFE))
- modEntry->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = audioDataS16[i] >> 8;
- else
- modEntry->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = 0; // clear rest of sample
- }
-
- free(audioDataS16);
- }
- else if (bitsPerSample == 24) // 24-BIT INTEGER SAMPLE
- {
- sampleLength /= 3;
- if (sampleLength > MAX_SAMPLE_LEN*4)
- sampleLength = MAX_SAMPLE_LEN*4;
-
- audioDataS32 = (int32_t *)malloc(sampleLength * sizeof (int32_t));
- if (audioDataS32 == NULL)
- {
- fclose(f);
- statusOutOfMemory();
- return false;
- }
-
- // read sample data
- audioDataU8 = (uint8_t *)audioDataS32;
- for (i = 0; i < sampleLength; i++)
- {
- audioDataU8[0] = 0;
- fread(&audioDataU8[1], 3, 1, f);
- audioDataU8 += sizeof (int32_t);
- }
-
- // convert from stereo to mono (if needed)
- if (numChannels == 2)
- {
- sampleLength >>= 1;
- for (i = 0; i < sampleLength-1; i++) // add right channel to left channel
- {
- int64_t smp = ((int64_t)audioDataS32[(i << 1) + 0] + audioDataS32[(i << 1) + 1]) >> 1;
- audioDataS32[i] = (int32_t)smp;
- }
- }
-
- // 2x downsampling - remove every other sample (if needed)
- if (forceDownSampling)
- {
- if (config.sampleLowpass)
- lowPassSample32Bit(audioDataS32, sampleLength, sampleRate, sampleRate / DOWNSAMPLE_CUTOFF_FACTOR);
-
- sampleLength >>= 1;
- for (i = 1; i < sampleLength; i++)
- audioDataS32[i] = audioDataS32[i << 1];
- }
-
- if (sampleLength > MAX_SAMPLE_LEN)
- sampleLength = MAX_SAMPLE_LEN;
-
- normalize32bitSigned(audioDataS32, sampleLength);
-
- turnOffVoices();
- for (i = 0; i < MAX_SAMPLE_LEN; i++)
- {
- if (i <= (sampleLength & 0xFFFFFFFE))
- modEntry->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = (int8_t)(audioDataS32[i] >> 24);
- else
- modEntry->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = 0; // clear rest of sample
- }
-
- free(audioDataS32);
- }
- else if (audioFormat == WAV_FORMAT_PCM && bitsPerSample == 32) // 32-BIT INTEGER SAMPLE
- {
- sampleLength >>= 2;
- if (sampleLength > MAX_SAMPLE_LEN*4)
- sampleLength = MAX_SAMPLE_LEN*4;
-
- audioDataS32 = (int32_t *)malloc(sampleLength * sizeof (int32_t));
- if (audioDataS32 == NULL)
- {
- fclose(f);
- statusOutOfMemory();
- return false;
- }
-
- // read sample data
- if (fread(audioDataS32, 4, sampleLength, f) != sampleLength)
- {
- fclose(f);
- free(audioDataS32);
- displayErrorMsg("I/O ERROR !");
- return false;
- }
-
- // convert from stereo to mono (if needed)
- if (numChannels == 2)
- {
- sampleLength >>= 1;
- for (i = 0; i < sampleLength-1; i++) // add right channel to left channel
- {
- int64_t smp = ((int64_t)audioDataS32[(i << 1) + 0] + audioDataS32[(i << 1) + 1]) >> 1;
- audioDataS32[i] = (int32_t)smp;
- }
- }
-
- // 2x downsampling - remove every other sample (if needed)
- if (forceDownSampling)
- {
- if (config.sampleLowpass)
- lowPassSample32Bit(audioDataS32, sampleLength, sampleRate, sampleRate / DOWNSAMPLE_CUTOFF_FACTOR);
-
- sampleLength >>= 1;
- for (i = 1; i < sampleLength; i++)
- audioDataS32[i] = audioDataS32[i << 1];
- }
-
- if (sampleLength > MAX_SAMPLE_LEN)
- sampleLength = MAX_SAMPLE_LEN;
-
- normalize32bitSigned(audioDataS32, sampleLength);
-
- turnOffVoices();
- for (i = 0; i < MAX_SAMPLE_LEN; i++)
- {
- if (i <= (sampleLength & 0xFFFFFFFE))
- modEntry->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = (int8_t)(audioDataS32[i] >> 24);
- else
- modEntry->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = 0; // clear rest of sample
- }
-
- free(audioDataS32);
- }
- else if (audioFormat == WAV_FORMAT_IEEE_FLOAT && bitsPerSample == 32) // 32-BIT FLOATING POINT SAMPLE
- {
- sampleLength >>= 2;
- if (sampleLength > MAX_SAMPLE_LEN*4)
- sampleLength = MAX_SAMPLE_LEN*4;
-
- audioDataU32 = (uint32_t *)malloc(sampleLength * sizeof (uint32_t));
- if (audioDataU32 == NULL)
- {
- fclose(f);
- statusOutOfMemory();
- return false;
- }
-
- // read sample data
- if (fread(audioDataU32, 4, sampleLength, f) != sampleLength)
- {
- fclose(f);
- free(audioDataU32);
- displayErrorMsg("I/O ERROR !");
- return false;
- }
-
- fAudioDataFloat = (float *)audioDataU32;
-
- // convert from stereo to mono (if needed)
- if (numChannels == 2)
- {
- sampleLength >>= 1;
- for (i = 0; i < sampleLength-1; i++) // add right channel to left channel
- {
- fSmp = (fAudioDataFloat[(i * 2) + 0] + fAudioDataFloat[(i * 2) + 1]) * 0.5f;
- fAudioDataFloat[i] = fSmp;
- }
- }
-
- // 2x downsampling - remove every other sample (if needed)
- if (forceDownSampling)
- {
- if (config.sampleLowpass)
- lowPassSampleFloat(fAudioDataFloat, sampleLength, sampleRate, sampleRate / DOWNSAMPLE_CUTOFF_FACTOR);
-
- sampleLength >>= 1;
- for (i = 1; i < sampleLength; i++)
- fAudioDataFloat[i] = fAudioDataFloat[i << 1];
- }
-
- if (sampleLength > MAX_SAMPLE_LEN)
- sampleLength = MAX_SAMPLE_LEN;
-
- normalize8bitFloatSigned(fAudioDataFloat, sampleLength);
-
- turnOffVoices();
- for (i = 0; i < MAX_SAMPLE_LEN; i++)
- {
- if (i <= (sampleLength & 0xFFFFFFFE))
- {
- smp32 = (int32_t)fAudioDataFloat[i];
- CLAMP8(smp32);
- modEntry->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = (int8_t)smp32;
- }
- else
- {
- modEntry->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = 0; // clear rest of sample
- }
- }
-
- free(audioDataU32);
- }
- else if (audioFormat == WAV_FORMAT_IEEE_FLOAT && bitsPerSample == 64) // 64-BIT FLOATING POINT SAMPLE
- {
- sampleLength >>= 3;
- if (sampleLength > MAX_SAMPLE_LEN*4)
- sampleLength = MAX_SAMPLE_LEN*4;
-
- audioDataU32 = (uint32_t *)malloc(sampleLength * (sizeof (uint32_t) * 2));
- if (audioDataU32 == NULL)
- {
- fclose(f);
- statusOutOfMemory();
- return false;
- }
-
- // read sample data
- if (fread(audioDataU32, 8, sampleLength, f) != sampleLength)
- {
- fclose(f);
- free(audioDataU32);
- displayErrorMsg("I/O ERROR !");
- return false;
- }
-
- dAudioDataDouble = (double *)audioDataU32;
-
- // convert from stereo to mono (if needed)
- if (numChannels == 2)
- {
- sampleLength >>= 1;
- for (i = 0; i < sampleLength-1; i++) // add right channel to left channel
- {
- dSmp = (dAudioDataDouble[(i * 2) + 0] + dAudioDataDouble[(i * 2) + 1]) * 0.5;
- dAudioDataDouble[i] = dSmp;
- }
- }
-
- // 2x downsampling - remove every other sample (if needed)
- if (forceDownSampling)
- {
- if (config.sampleLowpass)
- lowPassSampleDouble(dAudioDataDouble, sampleLength, sampleRate, sampleRate / DOWNSAMPLE_CUTOFF_FACTOR);
-
- sampleLength >>= 1;
- for (i = 1; i < sampleLength; i++)
- dAudioDataDouble[i] = dAudioDataDouble[i << 1];
- }
-
- if (sampleLength > MAX_SAMPLE_LEN)
- sampleLength = MAX_SAMPLE_LEN;
-
- normalize8bitDoubleSigned(dAudioDataDouble, sampleLength);
-
- turnOffVoices();
- for (i = 0; i < MAX_SAMPLE_LEN; i++)
- {
- if (i <= (sampleLength & 0xFFFFFFFE))
- {
- smp32 = (int32_t)dAudioDataDouble[i];
- CLAMP8(smp32);
- modEntry->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = (int8_t)smp32;
- }
- else
- {
- modEntry->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = 0; // clear rest of sample
- }
- }
-
- free(audioDataU32);
- }
-
- // set sample length
- if (sampleLength & 1)
- {
- if (++sampleLength > MAX_SAMPLE_LEN)
- sampleLength = MAX_SAMPLE_LEN;
- }
-
- s->length = (uint16_t)sampleLength;
- s->fineTune = 0;
- s->volume = 64;
- s->loopStart = 0;
- s->loopLength = 2;
-
- // ---- READ "smpl" chunk ----
- if (smplPtr != 0 && smplLen > 52)
- {
- fseek(f, smplPtr + 28, SEEK_SET); // seek to first wanted byte
-
- fread(&loopFlags, 4, 1, f);
- fseek(f, 12, SEEK_CUR);
- fread(&loopStart, 4, 1, f);
- fread(&loopEnd, 4, 1, f);
- loopEnd++;
-
- if (forceDownSampling)
- {
- // we already downsampled 2x, so we're half the original length
- loopStart >>= 1;
- loopEnd >>= 1;
- }
-
- loopStart &= 0xFFFFFFFE;
- loopEnd &= 0xFFFFFFFE;
-
- if (loopFlags)
- {
- if (loopStart+(loopEnd-loopStart) <= s->length)
- {
- s->loopStart = (uint16_t)loopStart;
- s->loopLength = (uint16_t)(loopEnd - loopStart);
-
- if (s->loopLength < 2)
- {
- s->loopStart = 0;
- s->loopLength = 2;
- }
- }
- }
- }
- // ---------------------------
-
- // ---- READ "xtra" chunk ----
- if (xtraPtr != 0 && xtraLen >= 8)
- {
- fseek(f, xtraPtr + 4, SEEK_SET); // seek to first wanted byte
-
- // volume (0..256)
- fseek(f, 2, SEEK_CUR);
- fread(&tempVol, 2, 1, f);
- if (tempVol > 256)
- tempVol = 256;
-
- s->volume = (int8_t)(tempVol >> 2);
- }
- // ---------------------------
-
- // ---- READ "INAM" chunk ----
- if (inamPtr != 0 && inamLen > 0)
- {
- fseek(f, inamPtr, SEEK_SET); // seek to first wanted byte
-
- for (i = 0; i < 21; i++)
- {
- if (i < inamLen)
- s->text[i] = (char)toupper(fgetc(f));
- else
- s->text[i] = '\0';
- }
-
- s->text[21] = '\0';
- s->text[22] = '\0';
-
- wavSampleNameFound = true;
- }
- // ---------------------------
-
- fclose(f);
-
- // copy over sample name
- if (!wavSampleNameFound)
- {
- nameLen = (uint32_t)strlen(entryName);
- for (i = 0; i < 21; i++)
- s->text[i] = (i < nameLen) ? (char)toupper(entryName[i]) : '\0';
-
- s->text[21] = '\0';
- s->text[22] = '\0';
- }
-
- // remove .wav from end of sample name (if present)
- nameLen = (uint32_t)strlen(s->text);
- if (nameLen >= 4 && !_strnicmp(&s->text[nameLen-4], ".WAV", 4))
- memset(&s->text[nameLen-4], '\0', 4);
-
- editor.sampleZero = false;
- editor.samplePos = 0;
-
- fixSampleBeep(s);
- updateCurrSample();
-
- updateWindowTitle(MOD_IS_MODIFIED);
- return true;
-}
-
-bool loadIFFSample(UNICHAR *fileName, char *entryName)
-{
- bool nameFound, is16Bit;
- char tmpCharBuf[23];
- int8_t *sampleData;
- int16_t sample16, *ptr16;
- int32_t filesize;
- uint32_t i, sampleLength, sampleLoopStart, sampleLoopLength;
- uint32_t sampleVolume, blockName, blockSize;
- uint32_t vhdrPtr, vhdrLen, bodyPtr, bodyLen, namePtr, nameLen;
- FILE *f;
- moduleSample_t *s;
-
- s = &modEntry->samples[editor.currSample];
-
- vhdrPtr = 0; vhdrLen = 0;
- bodyPtr = 0; bodyLen = 0;
- namePtr = 0; nameLen = 0;
-
- f = UNICHAR_FOPEN(fileName, "rb");
- if (f == NULL)
- {
- displayErrorMsg("FILE I/O ERROR !");
- return false;
- }
-
- fseek(f, 0, SEEK_END);
- filesize = ftell(f);
- if (filesize == 0)
- {
- displayErrorMsg("IFF IS CORRUPT !");
- return false;
- }
-
- fseek(f, 8, SEEK_SET);
- fread(tmpCharBuf, 1, 4, f);
- is16Bit = !strncmp(tmpCharBuf, "16SV", 4);
-
- sampleLength = 0;
- nameFound = false;
- sampleVolume = 65536; // max volume
-
- fseek(f, 12, SEEK_SET);
- while (!feof(f) && ftell(f) < filesize-12)
- {
- fread(&blockName, 4, 1, f); if (feof(f)) break;
- fread(&blockSize, 4, 1, f); if (feof(f)) break;
-
- blockName = SWAP32(blockName);
- blockSize = SWAP32(blockSize);
-
- switch (blockName)
- {
- case 0x56484452: // VHDR
- {
- vhdrPtr = ftell(f);
- vhdrLen = blockSize;
- }
- break;
-
- case 0x4E414D45: // NAME
- {
- namePtr = ftell(f);
- nameLen = blockSize;
- }
- break;
-
- case 0x424F4459: // BODY
- {
- bodyPtr = ftell(f);
- bodyLen = blockSize;
- }
- break;
-
- default: break;
- }
-
- fseek(f, blockSize + (blockSize & 1), SEEK_CUR);
- }
-
- if (vhdrPtr == 0 || vhdrLen < 20 || bodyPtr == 0)
- {
- fclose(f);
- displayErrorMsg("NOT A VALID IFF !");
- return false;
- }
-
- // kludge for some really strange IFFs
- if (bodyLen == 0)
- bodyLen = filesize - bodyPtr;
-
- if ((bodyPtr + bodyLen) > (uint32_t)filesize)
- bodyLen = filesize - bodyPtr;
-
- fseek(f, vhdrPtr, SEEK_SET);
- fread(&sampleLoopStart, 4, 1, f); sampleLoopStart = SWAP32(sampleLoopStart);
- fread(&sampleLoopLength, 4, 1, f); sampleLoopLength = SWAP32(sampleLoopLength);
-
- fseek(f, 4 + 2 + 1, SEEK_CUR);
-
- if (fgetc(f) != 0) // sample type
- {
- fclose(f);
- displayErrorMsg("UNSUPPORTED IFF !");
- return false;
- }
-
- fread(&sampleVolume, 4, 1, f); sampleVolume = SWAP32(sampleVolume);
- if (sampleVolume > 65536)
- sampleVolume = 65536;
-
- sampleVolume = (sampleVolume + 512) / 1024; // rounded
- if (sampleVolume > 64)
- sampleVolume = 64;
-
- sampleLength = bodyLen;
- if (is16Bit)
- {
- if (sampleLength > MAX_SAMPLE_LEN*2)
- sampleLength = MAX_SAMPLE_LEN*2;
- }
- else
- {
- if (sampleLength > MAX_SAMPLE_LEN)
- sampleLength = MAX_SAMPLE_LEN;
- }
-
- if (sampleLength == 0)
- {
- fclose(f);
- displayErrorMsg("NOT A VALID IFF !");
- return false;
- }
-
- sampleData = (int8_t *)malloc(sampleLength);
- if (sampleData == NULL)
- {
- fclose(f);
- statusOutOfMemory();
- return false;
- }
-
- if (is16Bit)
- {
- sampleLength >>= 1;
- sampleLoopStart >>= 1;
- sampleLoopLength >>= 1;
- }
-
- sampleLength &= 0xFFFFFFFE;
- sampleLoopStart &= 0xFFFFFFFE;
- sampleLoopLength &= 0xFFFFFFFE;
-
- if (sampleLength > MAX_SAMPLE_LEN)
- sampleLength = MAX_SAMPLE_LEN;
-
- if (sampleLoopLength < 2)
- {
- sampleLoopStart = 0;
- sampleLoopLength = 2;
- }
-
- if (sampleLoopStart >= MAX_SAMPLE_LEN || sampleLoopLength > MAX_SAMPLE_LEN)
- {
- sampleLoopStart= 0;
- sampleLoopLength = 2;
- }
-
- if (sampleLoopStart+sampleLoopLength > sampleLength)
- {
- sampleLoopStart = 0;
- sampleLoopLength = 2;
- }
-
- if (sampleLoopStart > sampleLength-2)
- {
- sampleLoopStart = 0;
- sampleLoopLength = 2;
- }
-
- turnOffVoices();
-
- fseek(f, bodyPtr, SEEK_SET);
- if (is16Bit) // FT2 specific 16SV format (little-endian samples)
- {
- fread(sampleData, 1, sampleLength << 1, f);
-
- ptr16 = (int16_t *)sampleData;
- for (i = 0; i < sampleLength; i++)
- {
- sample16 = ptr16[i];
- modEntry->sampleData[s->offset+i] = sample16 >> 8;
- }
- }
- else
- {
- fread(sampleData, 1, sampleLength, f);
- memcpy(&modEntry->sampleData[s->offset], sampleData, sampleLength);
- }
-
- if (sampleLength < MAX_SAMPLE_LEN) // clear rest of sample data
- memset(&modEntry->sampleData[s->offset + sampleLength], 0, MAX_SAMPLE_LEN - sampleLength);
-
- free(sampleData);
-
- // set sample attributes
- s->volume = (int8_t)sampleVolume;
- s->fineTune = 0;
- s->length = (uint16_t)sampleLength;
- s->loopStart = (uint16_t)sampleLoopStart;
- s->loopLength = (uint16_t)sampleLoopLength;
-
- // read name
- if (namePtr != 0 && nameLen > 0)
- {
- fseek(f, namePtr, SEEK_SET);
- memset(tmpCharBuf, 0, sizeof (tmpCharBuf));
-
- if (nameLen > 21)
- {
- fread(tmpCharBuf, 1, 21, f);
- fseek(f, nameLen - 21, SEEK_CUR);
- }
- else
- {
- fread(tmpCharBuf, 1, nameLen, f);
- }
-
- nameFound = true;
- }
-
- fclose(f);
-
- // copy over sample name
- memset(s->text, '\0', sizeof (s->text));
-
- if (nameFound)
- {
- nameLen = (uint32_t)strlen(tmpCharBuf);
- if (nameLen > 21)
- nameLen = 21;
-
- for (i = 0; i < nameLen; i++)
- s->text[i] = (char)toupper(tmpCharBuf[i]);
- }
- else
- {
- nameLen = (uint32_t)strlen(entryName);
- if (nameLen > 21)
- nameLen = 21;
-
- for (i = 0; i < nameLen; i++)
- s->text[i] = (char)toupper(entryName[i]);
- }
-
- // remove .iff from end of sample name (if present)
- nameLen = (uint32_t)strlen(s->text);
- if (nameLen >= 4 && !strncmp(&s->text[nameLen-4], ".IFF", 4))
- memset(&s->text[nameLen-4], '\0', 4);
-
- editor.sampleZero = false;
- editor.samplePos = 0;
-
- fixSampleBeep(s);
- updateCurrSample();
-
- updateWindowTitle(MOD_IS_MODIFIED);
- return false;
-}
-
-bool loadRAWSample(UNICHAR *fileName, char *entryName)
-{
- uint8_t i;
- uint32_t nameLen, fileSize;
- FILE *f;
- moduleSample_t *s;
-
- s = &modEntry->samples[editor.currSample];
-
- f = UNICHAR_FOPEN(fileName, "rb");
- if (f == NULL)
- {
- displayErrorMsg("FILE I/O ERROR !");
- return false;
- }
-
- fseek(f, 0, SEEK_END);
- fileSize = ftell(f);
- fseek(f, 0, SEEK_SET);
-
- fileSize &= 0xFFFFFFFE;
- if (fileSize > MAX_SAMPLE_LEN)
- fileSize = MAX_SAMPLE_LEN;
-
- turnOffVoices();
-
- fread(&modEntry->sampleData[s->offset], 1, fileSize, f);
- fclose(f);
-
- if (fileSize < MAX_SAMPLE_LEN)
- memset(&modEntry->sampleData[s->offset + fileSize], 0, MAX_SAMPLE_LEN - fileSize);
-
- // set sample attributes
- s->volume = 64;
- s->fineTune = 0;
- s->length = (uint16_t)fileSize;
- s->loopStart = 0;
- s->loopLength = 2;
-
- // copy over sample name
- nameLen = (uint32_t)strlen(entryName);
- for (i = 0; i < 21; i++)
- s->text[i] = (i < nameLen) ? (char)toupper(entryName[i]) : '\0';
-
- s->text[21] = '\0';
- s->text[22] = '\0';
-
- editor.sampleZero = false;
- editor.samplePos = 0;
-
- fixSampleBeep(s);
- updateCurrSample();
-
- updateWindowTitle(MOD_IS_MODIFIED);
- return true;
-}
-
-static int32_t getAIFFRate(uint8_t *in)
-{
- int32_t exp;
- uint32_t lo, hi;
- double dOut;
-
- exp = (int32_t)(((in[0] & 0x7F) << 8) | in[1]);
- lo = (in[2] << 24) | (in[3] << 16) | (in[4] << 8) | in[5];
- hi = (in[6] << 24) | (in[7] << 16) | (in[8] << 8) | in[9];
-
- if (exp == 0 && lo == 0 && hi == 0)
- return 0;
-
- exp -= 16383;
-
- dOut = ldexp(lo, -31 + exp) + ldexp(hi, -63 + exp);
- return (int32_t)(dOut + 0.5);
-}
-
-bool loadAIFFSample(UNICHAR *fileName, char *entryName, int8_t forceDownSampling)
-{
- bool unsigned8bit;
- char compType[4];
- int8_t *audioDataS8;
- uint8_t *audioDataU8, sampleRateBytes[10];
- int16_t *audioDataS16, smp16;
- uint16_t bitDepth, numChannels;
- int32_t filesize, *audioDataS32, smp32;
- uint32_t nameLen, i, offset, sampleRate, sampleLength, blockName, blockSize;
- uint32_t commPtr, commLen, ssndPtr, ssndLen;
- FILE *f;
- moduleSample_t *s;
-
- unsigned8bit = false;
- loadedFileWasAIFF = true;
-
- if (forceDownSampling == -1)
- {
- // these two *must* be fully wiped, for outputting reasons
- memset(editor.fileNameTmpU, 0, PATH_MAX);
- memset(editor.entryNameTmp, 0, PATH_MAX);
- UNICHAR_STRCPY(editor.fileNameTmpU, fileName);
- strcpy(editor.entryNameTmp, entryName);
- }
-
- s = &modEntry->samples[editor.currSample];
-
- commPtr = 0; commLen = 0;
- ssndPtr = 0; ssndLen = 0;
-
- f = UNICHAR_FOPEN(fileName, "rb");
- if (f == NULL)
- {
- displayErrorMsg("FILE I/O ERROR !");
- return false;
- }
-
- fseek(f, 0, SEEK_END);
- filesize = ftell(f);
- if (filesize == 0)
- {
- displayErrorMsg("AIFF IS CORRUPT !");
- return false;
- }
-
- fseek(f, 12, SEEK_SET);
- while (!feof(f) && ftell(f) < filesize-12)
- {
- fread(&blockName, 4, 1, f); if (feof(f)) break;
- fread(&blockSize, 4, 1, f); if (feof(f)) break;
-
- blockName = SWAP32(blockName);
- blockSize = SWAP32(blockSize);
-
- switch (blockName)
- {
- case 0x434F4D4D: // "COMM"
- {
- commPtr = ftell(f);
- commLen = blockSize;
- }
- break;
-
- case 0x53534E44: // "SSND"
- {
- ssndPtr = ftell(f);
- ssndLen = blockSize;
- }
- break;
-
- default: break;
- }
-
- fseek(f, blockSize + (blockSize & 1), SEEK_CUR);
- }
-
- if (commPtr == 0 || commLen < 18 || ssndPtr == 0)
- {
- fclose(f);
- displayErrorMsg("NOT A VALID AIFF!");
- return false;
- }
-
- // kludge for some really strange AIFFs
- if (ssndLen == 0)
- ssndLen = filesize - ssndPtr;
-
- if (ssndPtr+ssndLen > (uint32_t)filesize)
- ssndLen = filesize - ssndPtr;
-
- fseek(f, commPtr, SEEK_SET);
- fread(&numChannels, 2, 1, f); numChannels = SWAP16(numChannels);
- fseek(f, 4, SEEK_CUR);
- fread(&bitDepth, 2, 1, f); bitDepth = SWAP16(bitDepth);
- fread(sampleRateBytes, 1, 10, f);
-
- fseek(f, 4 + 2 + 1, SEEK_CUR);
-
- if (numChannels != 1 && numChannels != 2) // sample type
- {
- fclose(f);
- displayErrorMsg("UNSUPPORTED AIFF!");
- return false;
- }
-
- if (bitDepth != 8 && bitDepth != 16 && bitDepth != 24 && bitDepth != 32)
- {
- fclose(f);
- displayErrorMsg("UNSUPPORTED AIFF!");
- return false;
- }
-
- // read compression type (if present)
- if (commLen > 18)
- {
- fread(&compType, 1, 4, f);
- if (memcmp(compType, "NONE", 4))
- {
- fclose(f);
- displayErrorMsg("UNSUPPORTED AIFF!");
- return false;
- }
- }
-
- sampleRate = getAIFFRate(sampleRateBytes);
-
- // sample data chunk
-
- fseek(f, ssndPtr, SEEK_SET);
-
- fread(&offset, 4, 1, f);
- if (offset > 0)
- {
- fclose(f);
- displayErrorMsg("UNSUPPORTED AIFF!");
- return false;
- }
-
- fseek(f, 4, SEEK_CUR);
-
- ssndLen -= 8; // don't include offset and blockSize datas
-
- sampleLength = ssndLen;
- if (sampleLength == 0)
- {
- fclose(f);
- displayErrorMsg("NOT A VALID AIFF!");
- return false;
- }
-
- if (sampleRate > 22050)
- {
- if (forceDownSampling == -1)
- {
- showDownsampleAskDialog();
- fclose(f);
- return true;
- }
- }
- else
- {
- forceDownSampling = false;
- }
-
- if (bitDepth == 8) // 8-BIT INTEGER SAMPLE
- {
- if (sampleLength > MAX_SAMPLE_LEN*4)
- sampleLength = MAX_SAMPLE_LEN*4;
-
- audioDataS8 = (int8_t *)malloc(sampleLength * sizeof (int8_t));
- if (audioDataS8 == NULL)
- {
- fclose(f);
- statusOutOfMemory();
- return false;
- }
-
- // read sample data
- if (fread(audioDataS8, 1, sampleLength, f) != sampleLength)
- {
- fclose(f);
- free(audioDataS8);
- displayErrorMsg("I/O ERROR !");
- return false;
- }
-
- if (unsigned8bit)
- {
- for (i = 0; i < sampleLength; i++)
- audioDataS8[i] ^= 0x80;
- }
-
- // convert from stereo to mono (if needed)
- if (numChannels == 2)
- {
- sampleLength >>= 1;
- for (i = 0; i < sampleLength-1; i++) // add right channel to left channel
- {
- smp16 = (audioDataS8[(i * 2) + 0] + audioDataS8[(i * 2) + 1]) >> 1;
- audioDataS8[i] = (uint8_t)smp16;
- }
- }
-
- // 2x downsampling - remove every other sample (if needed)
- if (forceDownSampling)
- {
- if (config.sampleLowpass)
- lowPassSample8Bit(audioDataS8, sampleLength, sampleRate, sampleRate / DOWNSAMPLE_CUTOFF_FACTOR);
-
- sampleLength >>= 1;
- for (i = 1; i < sampleLength; i++)
- audioDataS8[i] = audioDataS8[i << 1];
- }
-
- if (sampleLength > MAX_SAMPLE_LEN)
- sampleLength = MAX_SAMPLE_LEN;
-
- turnOffVoices();
- for (i = 0; i < MAX_SAMPLE_LEN; i++)
- {
- if (i <= (sampleLength & 0xFFFFFFFE))
- modEntry->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = audioDataS8[i];
- else
- modEntry->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = 0; // clear rest of sample
- }
-
- free(audioDataS8);
- }
- else if (bitDepth == 16) // 16-BIT INTEGER SAMPLE
- {
- sampleLength >>= 1;
- if (sampleLength > MAX_SAMPLE_LEN*4)
- sampleLength = MAX_SAMPLE_LEN*4;
-
- audioDataS16 = (int16_t *)malloc(sampleLength * sizeof (int16_t));
- if (audioDataS16 == NULL)
- {
- fclose(f);
- statusOutOfMemory();
- return false;
- }
-
- // read sample data
- if (fread(audioDataS16, 2, sampleLength, f) != sampleLength)
- {
- fclose(f);
- free(audioDataS16);
- displayErrorMsg("I/O ERROR !");
- return false;
- }
-
- // fix endianness
- for (i = 0; i < sampleLength; i++)
- audioDataS16[i] = SWAP16(audioDataS16[i]);
-
- // convert from stereo to mono (if needed)
- if (numChannels == 2)
- {
- sampleLength >>= 1;
- for (i = 0; i < sampleLength-1; i++) // add right channel to left channel
- {
- smp32 = (audioDataS16[(i << 1) + 0] + audioDataS16[(i << 1) + 1]) >> 1;
- audioDataS16[i] = (int16_t)(smp32);
- }
- }
-
- // 2x downsampling - remove every other sample (if needed)
- if (forceDownSampling)
- {
- if (config.sampleLowpass)
- lowPassSample16Bit(audioDataS16, sampleLength, sampleRate, sampleRate / DOWNSAMPLE_CUTOFF_FACTOR);
-
- sampleLength >>= 1;
- for (i = 1; i < sampleLength; i++)
- audioDataS16[i] = audioDataS16[i << 1];
- }
-
- if (sampleLength > MAX_SAMPLE_LEN)
- sampleLength = MAX_SAMPLE_LEN;
-
- normalize16bitSigned(audioDataS16, sampleLength);
-
- turnOffVoices();
- for (i = 0; i < MAX_SAMPLE_LEN; i++)
- {
- if (i <= (sampleLength & 0xFFFFFFFE))
- modEntry->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = audioDataS16[i] >> 8;
- else
- modEntry->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = 0; // clear rest of sample
- }
-
- free(audioDataS16);
- }
- else if (bitDepth == 24) // 24-BIT INTEGER SAMPLE
- {
- sampleLength /= 3;
- if (sampleLength > MAX_SAMPLE_LEN*4)
- sampleLength = MAX_SAMPLE_LEN*4;
-
- audioDataS32 = (int32_t *)malloc(sampleLength * sizeof (int32_t));
- if (audioDataS32 == NULL)
- {
- fclose(f);
- statusOutOfMemory();
- return false;
- }
-
- // read sample data
- if (fread(&audioDataS32[sampleLength >> 2], 3, sampleLength, f) != sampleLength)
- {
- fclose(f);
- free(audioDataS32);
- displayErrorMsg("I/O ERROR !");
- return false;
- }
-
- // convert to 32-bit
- audioDataU8 = (uint8_t *)audioDataS32 + sampleLength;
- for (i = 0; i < sampleLength; i++)
- {
- audioDataS32[i] = (audioDataU8[0] << 24) | (audioDataU8[1] << 16) | (audioDataU8[2] << 8);
- audioDataU8 += 3;
- }
-
- // convert from stereo to mono (if needed)
- if (numChannels == 2)
- {
- sampleLength >>= 1;
- for (i = 0; i < sampleLength-1; i++) // add right channel to left channel
- {
- int64_t smp = ((int64_t)audioDataS32[(i << 1) + 0] + audioDataS32[(i << 1) + 1]) >> 1;
- audioDataS32[i] = (int32_t)smp;
- }
- }
-
- // 2x downsampling - remove every other sample (if needed)
- if (forceDownSampling)
- {
- if (config.sampleLowpass)
- lowPassSample32Bit(audioDataS32, sampleLength, sampleRate, sampleRate / DOWNSAMPLE_CUTOFF_FACTOR);
-
- sampleLength >>= 1;
- for (i = 1; i < sampleLength; i++)
- audioDataS32[i] = audioDataS32[i << 1];
- }
-
- if (sampleLength > MAX_SAMPLE_LEN)
- sampleLength = MAX_SAMPLE_LEN;
-
- normalize32bitSigned(audioDataS32, sampleLength);
-
- turnOffVoices();
- for (i = 0; i < MAX_SAMPLE_LEN; i++)
- {
- if (i <= (sampleLength & 0xFFFFFFFE))
- modEntry->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = (int8_t)(audioDataS32[i] >> 24);
- else
- modEntry->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = 0; // clear rest of sample
- }
-
- free(audioDataS32);
- }
- else if (bitDepth == 32) // 32-BIT INTEGER SAMPLE
- {
- sampleLength >>= 2;
- if (sampleLength > MAX_SAMPLE_LEN*4)
- sampleLength = MAX_SAMPLE_LEN*4;
-
- audioDataS32 = (int32_t *)malloc(sampleLength * sizeof (int32_t));
- if (audioDataS32 == NULL)
- {
- fclose(f);
- statusOutOfMemory();
- return false;
- }
-
- // read sample data
- if (fread(audioDataS32, 4, sampleLength, f) != sampleLength)
- {
- fclose(f);
- free(audioDataS32);
- displayErrorMsg("I/O ERROR !");
- return false;
- }
-
- // fix endianness
- for (i = 0; i < sampleLength; i++)
- audioDataS32[i] = SWAP32(audioDataS32[i]);
-
- // convert from stereo to mono (if needed)
- if (numChannels == 2)
- {
- sampleLength >>= 1;
- for (i = 0; i < sampleLength-1; i++) // add right channel to left channel
- {
- int64_t smp = ((int64_t)audioDataS32[(i << 1) + 0] + audioDataS32[(i << 1) + 1]) >> 1;
- audioDataS32[i] = (int32_t)smp;
- }
- }
-
- // 2x downsampling - remove every other sample (if needed)
- if (forceDownSampling)
- {
- if (config.sampleLowpass)
- lowPassSample32Bit(audioDataS32, sampleLength, sampleRate, sampleRate / DOWNSAMPLE_CUTOFF_FACTOR);
-
- sampleLength >>= 1;
- for (i = 1; i < sampleLength; i++)
- audioDataS32[i] = audioDataS32[i << 1];
- }
-
- if (sampleLength > MAX_SAMPLE_LEN)
- sampleLength = MAX_SAMPLE_LEN;
-
- normalize32bitSigned(audioDataS32, sampleLength);
-
- turnOffVoices();
- for (i = 0; i < MAX_SAMPLE_LEN; i++)
- {
- if (i <= (sampleLength & 0xFFFFFFFE))
- modEntry->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = (int8_t)(audioDataS32[i] >> 24);
- else
- modEntry->sampleData[(editor.currSample * MAX_SAMPLE_LEN) + i] = 0; // clear rest of sample
- }
-
- free(audioDataS32);
- }
-
- // set sample length
- if (sampleLength & 1)
- {
- if (++sampleLength > MAX_SAMPLE_LEN)
- sampleLength = MAX_SAMPLE_LEN;
- }
-
- s->length = (uint16_t)sampleLength;
- s->fineTune = 0;
- s->volume = 64;
- s->loopStart = 0;
- s->loopLength = 2;
-
- fclose(f);
-
- // copy over sample name
- nameLen = (uint32_t)strlen(entryName);
- for (i = 0; i < 21; i++)
- s->text[i] = (i < nameLen) ? (char)toupper(entryName[i]) : '\0';
-
- s->text[21] = '\0';
- s->text[22] = '\0';
-
- // remove .aiff from end of sample name (if present)
- nameLen = (uint32_t)strlen(s->text);
- if (nameLen >= 5 && !_strnicmp(&s->text[nameLen-5], ".AIFF", 5))
- memset(&s->text[nameLen-5], '\0', 5);
-
- editor.sampleZero = false;
- editor.samplePos = 0;
-
- fixSampleBeep(s);
- updateCurrSample();
-
- updateWindowTitle(MOD_IS_MODIFIED);
- return true;
-}
-
-bool loadSample(UNICHAR *fileName, char *entryName)
-{
- uint32_t fileSize, ID;
- FILE *f;
-
- f = UNICHAR_FOPEN(fileName, "rb");
- if (f == NULL)
- {
- displayErrorMsg("FILE I/O ERROR !");
- return false;
- }
-
- fseek(f, 0, SEEK_END);
- fileSize = ftell(f);
- fseek(f, 0, SEEK_SET);
-
- // first, check heades before we eventually load as RAW
- if (fileSize > 16)
- {
- fread(&ID, 4, 1, f);
-
- // check if it's actually a WAV sample
- if (ID == 0x46464952) // "RIFF"
- {
- fseek(f, 4, SEEK_CUR);
- fread(&ID, 4, 1, f);
-
- if (ID == 0x45564157) // "WAVE"
- {
- fread(&ID, 4, 1, f);
- if (ID == 0x20746D66) // "fmt "
- {
- fclose(f);
- return loadWAVSample(fileName, entryName, -1);
- }
- }
- }
- else if (ID == 0x4D524F46) // "FORM"
- {
- fseek(f, 4, SEEK_CUR);
- fread(&ID, 4, 1, f);
-
- // check if it's an Amiga IFF sample
- if (ID == 0x58565338 || ID == 0x56533631) // "8SVX" (normal) and "16SV" (FT2 sample)
- {
- fclose(f);
- return loadIFFSample(fileName, entryName);
- }
-
- // check if it's an AIFF sample
- else if (ID == 0x46464941) // "AIFF"
- {
- fclose(f);
- return loadAIFFSample(fileName, entryName, -1);
- }
-
- else if (ID == 0x43464941) // "AIFC" (compressed AIFF)
- {
- fclose(f);
- displayErrorMsg("UNSUPPORTED AIFF!");
- return false;
- }
- }
- }
-
- // nope, continue loading as RAW
- fclose(f);
-
- return loadRAWSample(fileName, entryName);
-}
-
-static void removeWavIffExt(char *text) // for sample saver
-{
- uint32_t fileExtPos;
- uint32_t filenameLength;
-
- filenameLength = (uint32_t)strlen(text);
- if (filenameLength < 5)
- return;
-
- // remove .wav/.iff/from end of sample name (if present)
- fileExtPos = filenameLength - 4;
- if (fileExtPos > 0 && (!strncmp(&text[fileExtPos], ".wav", 4) || !strncmp(&text[fileExtPos], ".iff", 4)))
- text[fileExtPos] = '\0';
-}
-
-bool saveSample(bool checkIfFileExist, bool giveNewFreeFilename)
-{
- char fileName[128], tmpBuffer[64];
- uint8_t smp;
- uint16_t j;
- int32_t i;
- uint32_t sampleSize, iffSize, iffSampleSize;
- uint32_t loopStart, loopLen, tmp32;
- FILE *f;
- struct stat statBuffer;
- moduleSample_t *s;
- wavHeader_t wavHeader;
- samplerChunk_t samplerChunk;
-
- if (modEntry->samples[editor.currSample].length == 0)
- {
- displayErrorMsg("SAMPLE IS EMPTY");
- return false;
- }
-
- memset(fileName, 0, sizeof (fileName));
-
- if (*modEntry->samples[editor.currSample].text == '\0')
- {
- strcpy(fileName, "untitled");
- }
- else
- {
- for (i = 0; i < 22; i++)
- {
- tmpBuffer[i] = (char)tolower(modEntry->samples[editor.currSample].text[i]);
- if (tmpBuffer[i] == '\0') break;
- sanitizeFilenameChar(&tmpBuffer[i]);
- }
-
- strcpy(fileName, tmpBuffer);
- }
-
- removeWavIffExt(fileName);
-
- switch (diskop.smpSaveType)
- {
- case DISKOP_SMP_WAV: strcat(fileName, ".wav"); break;
- case DISKOP_SMP_IFF: strcat(fileName, ".iff"); break;
- case DISKOP_SMP_RAW: break;
- default: return false; // make compiler happy
- }
-
- if (giveNewFreeFilename)
- {
- if (stat(fileName, &statBuffer) == 0)
- {
- for (j = 1; j <= 9999; j++) // This number should satisfy all! ;)
- {
- memset(fileName, 0, sizeof (fileName));
-
- if (*modEntry->samples[editor.currSample].text == '\0')
- {
- sprintf(fileName, "untitled-%d", j);
- }
- else
- {
- for (i = 0; i < 22; i++)
- {
- tmpBuffer[i] = (char)tolower(modEntry->samples[editor.currSample].text[i]);
- if (tmpBuffer[i] == '\0') break;
- sanitizeFilenameChar(&tmpBuffer[i]);
- }
-
- removeWavIffExt(tmpBuffer);
- sprintf(fileName, "%s-%d", tmpBuffer, j);
- }
-
- switch (diskop.smpSaveType)
- {
- default: case DISKOP_SMP_WAV: strcat(fileName, ".wav"); break;
- case DISKOP_SMP_IFF: strcat(fileName, ".iff"); break;
- case DISKOP_SMP_RAW: break;
- }
-
- if (stat(fileName, &statBuffer) != 0)
- break;
- }
- }
- }
-
- if (checkIfFileExist && stat(fileName, &statBuffer) == 0)
- {
- ui.askScreenShown = true;
- ui.askScreenType = ASK_SAVESMP_OVERWRITE;
- pointerSetMode(POINTER_MODE_MSG1, NO_CARRY);
- setStatusMessage("OVERWRITE FILE ?", NO_CARRY);
- renderAskDialog();
- return -1;
- }
-
- if (ui.askScreenShown)
- {
- ui.answerNo = false;
- ui.answerYes = false;
- ui.askScreenShown = false;
- }
-
- f = fopen(fileName, "wb");
- if (f == NULL)
- {
- displayErrorMsg("FILE I/O ERROR !");
- return false;
- }
-
- sampleSize = modEntry->samples[editor.currSample].length;
-
- switch (diskop.smpSaveType)
- {
- default:
- case DISKOP_SMP_WAV:
- {
- s = &modEntry->samples[editor.currSample];
-
- wavHeader.format = 0x45564157; // "WAVE"
- wavHeader.chunkID = 0x46464952; // "RIFF"
- wavHeader.subchunk1ID = 0x20746D66; // "fmt "
- wavHeader.subchunk2ID = 0x61746164; // "data"
- wavHeader.subchunk1Size = 16;
- wavHeader.subchunk2Size = sampleSize;
- wavHeader.chunkSize = wavHeader.subchunk2Size + 36;
- wavHeader.audioFormat = 1;
- wavHeader.numChannels = 1;
- wavHeader.bitsPerSample = 8;
- wavHeader.sampleRate = 16574;
- wavHeader.byteRate = wavHeader.sampleRate * wavHeader.numChannels * wavHeader.bitsPerSample / 8;
- wavHeader.blockAlign = wavHeader.numChannels * wavHeader.bitsPerSample / 8;
-
- if (s->loopLength > 2)
- {
- wavHeader.chunkSize += sizeof (samplerChunk_t);
- memset(&samplerChunk, 0, sizeof (samplerChunk_t));
- samplerChunk.chunkID = 0x6C706D73; // "smpl"
- samplerChunk.chunkSize = 60;
- samplerChunk.dwSamplePeriod = 1000000000 / 16574;
- samplerChunk.dwMIDIUnityNote = 60; // 60 = C-4
- samplerChunk.cSampleLoops = 1;
- samplerChunk.loop.dwStart = (uint32_t)s->loopStart;
- samplerChunk.loop.dwEnd = (uint32_t)((s->loopStart + s->loopLength) - 1);
- }
-
- fwrite(&wavHeader, sizeof (wavHeader_t), 1, f);
-
- for (i = 0; i < (int32_t)sampleSize; i++)
- {
- smp = modEntry->sampleData[modEntry->samples[editor.currSample].offset + i] + 128;
- fputc(smp, f);
- }
-
- if (sampleSize & 1)
- fputc(0, f); // pad align byte
-
- if (s->loopLength > 2)
- fwrite(&samplerChunk, sizeof (samplerChunk), 1, f);
- }
- break;
-
- case DISKOP_SMP_IFF:
- {
- // dwords are big-endian in IFF
- loopStart = modEntry->samples[editor.currSample].loopStart & 0xFFFFFFFE;
- loopLen = modEntry->samples[editor.currSample].loopLength & 0xFFFFFFFE;
-
- loopStart = SWAP32(loopStart);
- loopLen = SWAP32(loopLen);
- iffSize = SWAP32(sampleSize + 100);
- iffSampleSize = SWAP32(sampleSize);
-
- fputc(0x46, f);fputc(0x4F, f);fputc(0x52, f);fputc(0x4D, f); // "FORM"
- fwrite(&iffSize, 4, 1, f);
-
- fputc(0x38, f);fputc(0x53, f);fputc(0x56, f);fputc(0x58, f); // "8SVX"
- fputc(0x56, f);fputc(0x48, f);fputc(0x44, f);fputc(0x52, f); // "VHDR"
- fputc(0x00, f);fputc(0x00, f);fputc(0x00, f);fputc(0x14, f); // 0x00000014
-
- if (modEntry->samples[editor.currSample].loopLength > 2)
- {
- fwrite(&loopStart, 4, 1, f);
- fwrite(&loopLen, 4, 1, f);
- }
- else
- {
- fwrite(&iffSampleSize, 4, 1, f);
- fputc(0x00, f);fputc(0x00, f);fputc(0x00, f);fputc(0x00, f); // 0x00000000
- }
-
- fputc(0x00, f);fputc(0x00, f);fputc(0x00, f);fputc(0x00, f); // 0x00000000
-
- fputc(0x41, f);fputc(0x56, f); // 16726 (rate)
- fputc(0x01, f);fputc(0x00, f); // numSamples and compression
-
- tmp32 = modEntry->samples[editor.currSample].volume * 1024;
- tmp32 = SWAP32(tmp32);
- fwrite(&tmp32, 4, 1, f);
-
- fputc(0x4E, f);fputc(0x41, f);fputc(0x4D, f);fputc(0x45, f); // "NAME"
- fputc(0x00, f);fputc(0x00, f);fputc(0x00, f);fputc(0x16, f); // 0x00000016
-
- for (i = 0; i < 22; i++)
- fputc(tolower(modEntry->samples[editor.currSample].text[i]), f);
-
- fputc(0x41, f);fputc(0x4E, f);fputc(0x4E, f);fputc(0x4F, f); // "ANNO"
- fputc(0x00, f);fputc(0x00, f);fputc(0x00, f);fputc(0x15, f); // 0x00000012
- fprintf(f, "ProTracker 2 clone");
- fputc(0x00, f); // even padding
-
- fputc(0x42, f);fputc(0x4F, f);fputc(0x44, f);fputc(0x59, f); // "BODY"
- fwrite(&iffSampleSize, 4, 1, f);
- fwrite(modEntry->sampleData + modEntry->samples[editor.currSample].offset, 1, sampleSize, f);
-
- // shouldn't happen, but in just case: safety even padding
- if (sampleSize & 1)
- fputc(0x00, f);
- }
- break;
-
- case DISKOP_SMP_RAW:
- fwrite(modEntry->sampleData + modEntry->samples[editor.currSample].offset, 1, sampleSize, f);
- break;
- }
-
- fclose(f);
-
- diskop.cached = false;
- if (ui.diskOpScreenShown)
- ui.updateDiskOpFileList = true;
-
- displayMsg("SAMPLE SAVED !");
- setMsgPointer();
-
- return true;
-}
--- a/src/pt2_sampleloader.h
+++ /dev/null
@@ -1,9 +1,0 @@
-#pragma once
-
-#include <stdint.h>
-#include <stdbool.h>
-#include "pt2_unicode.h"
-
-void extLoadWAVOrAIFFSampleCallback(bool downSample);
-bool saveSample(bool checkIfFileExist, bool giveNewFreeFilename);
-bool loadSample(UNICHAR *fileName, char *entryName);
--- a/src/pt2_sampler.c
+++ b/src/pt2_sampler.c
@@ -19,6 +19,8 @@
#include "pt2_scopes.h"
#include "pt2_sampler.h"
#include "pt2_structs.h"
+#include "pt2_config.h"
+#include "pt2_bmp.h"
#define CENTER_LINE_COLOR 0x303030
#define MARK_COLOR_1 0x666666 /* inverted background */
@@ -49,8 +51,14 @@
void upSample(void)
{
- moduleSample_t *s = &modEntry->samples[editor.currSample];
+ if (editor.sampleZero)
+ {
+ statusNotSampleZero();
+ return;
+ }
+ moduleSample_t *s = &song->samples[editor.currSample];
+
uint32_t newLength = (s->length >> 1) & 0xFFFE;
if (newLength < 2)
return;
@@ -58,7 +66,7 @@
turnOffVoices();
// upsample
- int8_t *ptr8 = &modEntry->sampleData[s->offset];
+ int8_t *ptr8 = &song->sampleData[s->offset];
for (uint32_t i = 0; i < newLength; i++)
ptr8[i] = ptr8[i << 1];
@@ -85,8 +93,14 @@
void downSample(void)
{
- moduleSample_t *s = &modEntry->samples[editor.currSample];
+ if (editor.sampleZero)
+ {
+ statusNotSampleZero();
+ return;
+ }
+ moduleSample_t *s = &song->samples[editor.currSample];
+
uint32_t newLength = s->length << 1;
if (newLength > MAX_SAMPLE_LEN)
newLength = MAX_SAMPLE_LEN;
@@ -95,7 +109,7 @@
// downsample
- int8_t *ptr8 = &modEntry->sampleData[s->offset];
+ int8_t *ptr8 = &song->sampleData[s->offset];
int8_t *ptr8_2 = ptr8 - 1;
for (int32_t i = s->length-1; i > 0; i--)
{
@@ -153,8 +167,8 @@
{
if (s->length >= 2 && s->loopStart+s->loopLength <= 2)
{
- modEntry->sampleData[s->offset+0] = 0;
- modEntry->sampleData[s->offset+1] = 0;
+ song->sampleData[s->offset+0] = 0;
+ song->sampleData[s->offset+1] = 0;
}
}
@@ -165,7 +179,7 @@
assert(editor.currSample >= 0 && editor.currSample <= 30);
if (editor.currSample >= 0 && editor.currSample <= 30)
{
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
if (editor.samplePos > s->length)
editor.samplePos = s->length;
@@ -181,8 +195,8 @@
assert(editor.currSample >= 0 && editor.currSample <= 30);
if (editor.currSample >= 0 && editor.currSample <= 30)
{
- s = &modEntry->samples[editor.currSample];
- memcpy(editor.tempSample, &modEntry->sampleData[s->offset], s->length);
+ s = &song->samples[editor.currSample];
+ memcpy(editor.tempSample, &song->sampleData[s->offset], s->length);
}
}
@@ -376,7 +390,7 @@
uint32_t *dstPtr, pixel;
moduleSample_t *s;
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
// clear sample data background
@@ -421,7 +435,7 @@
oldMin = y1;
oldMax = y1;
- smpPtr = &modEntry->sampleData[s->offset];
+ smpPtr = &song->sampleData[s->offset];
for (x = 0; x < SAMPLE_AREA_WIDTH; x++)
{
smpIdx = scr2SmpPos(x);
@@ -518,10 +532,10 @@
sampler.samOffset = 0;
updateSamOffset();
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
if (s->length > 0)
{
- sampler.samStart = &modEntry->sampleData[s->offset];
+ sampler.samStart = &song->sampleData[s->offset];
sampler.samDisplay = s->length;
sampler.samLength = s->length;
}
@@ -554,6 +568,12 @@
assert(editor.currSample >= 0 && editor.currSample <= 30);
+ if (editor.sampleZero)
+ {
+ statusNotSampleZero();
+ return;
+ }
+
if (cutOff == 0)
{
displayErrorMsg("CUTOFF CAN'T BE 0");
@@ -560,10 +580,10 @@
return;
}
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
if (s->length == 0)
{
- displayErrorMsg("SAMPLE IS EMPTY");
+ statusSampleIsEmpty();
return;
}
@@ -609,7 +629,7 @@
// copy over sample data to double buffer
for (i = 0; i < s->length; i++)
- dSampleData[i] = modEntry->sampleData[s->offset+i];
+ dSampleData[i] = song->sampleData[s->offset+i];
clearRCFilterState(&filterHi);
if (to <= s->length)
@@ -625,7 +645,7 @@
{
smp32 = (int32_t)dSampleData[i];
CLAMP8(smp32);
- modEntry->sampleData[s->offset + i] = (int8_t)smp32;
+ song->sampleData[s->offset + i] = (int8_t)smp32;
}
free(dSampleData);
@@ -642,6 +662,12 @@
moduleSample_t *s;
rcFilter_t filterLo;
+ if (editor.sampleZero)
+ {
+ statusNotSampleZero();
+ return;
+ }
+
assert(editor.currSample >= 0 && editor.currSample <= 30);
if (cutOff == 0)
@@ -650,10 +676,10 @@
return;
}
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
if (s->length == 0)
{
- displayErrorMsg("SAMPLE IS EMPTY");
+ statusSampleIsEmpty();
return;
}
@@ -699,7 +725,7 @@
// copy over sample data to double buffer
for (i = 0; i < s->length; i++)
- dSampleData[i] = modEntry->sampleData[s->offset+i];
+ dSampleData[i] = song->sampleData[s->offset+i];
clearRCFilterState(&filterLo);
if (to <= s->length)
@@ -715,7 +741,7 @@
{
smp32 = (int32_t)dSampleData[i];
CLAMP8(smp32);
- modEntry->sampleData[s->offset + i] = (int8_t)smp32;
+ song->sampleData[s->offset + i] = (int8_t)smp32;
}
free(dSampleData);
@@ -727,24 +753,30 @@
void redoSampleData(int8_t sample)
{
+ if (editor.sampleZero)
+ {
+ statusNotSampleZero();
+ return;
+ }
+
moduleSample_t *s;
assert(sample >= 0 && sample <= 30);
- s = &modEntry->samples[sample];
+ s = &song->samples[sample];
turnOffVoices();
if (editor.smpRedoBuffer[sample] != NULL && editor.smpRedoLengths[sample] > 0)
{
- memcpy(&modEntry->sampleData[s->offset], editor.smpRedoBuffer[sample], editor.smpRedoLengths[sample]);
+ memcpy(&song->sampleData[s->offset], editor.smpRedoBuffer[sample], editor.smpRedoLengths[sample]);
if (editor.smpRedoLengths[sample] < MAX_SAMPLE_LEN)
- memset(&modEntry->sampleData[s->offset + editor.smpRedoLengths[sample]], 0, MAX_SAMPLE_LEN - editor.smpRedoLengths[sample]);
+ memset(&song->sampleData[s->offset + editor.smpRedoLengths[sample]], 0, MAX_SAMPLE_LEN - editor.smpRedoLengths[sample]);
}
else
{
- memset(&modEntry->sampleData[s->offset], 0, MAX_SAMPLE_LEN);
+ memset(&song->sampleData[s->offset], 0, MAX_SAMPLE_LEN);
}
s->fineTune = editor.smpRedoFinetunes[sample];
@@ -774,7 +806,7 @@
assert(sample >= 0 && sample <= 30);
- s = &modEntry->samples[sample];
+ s = &song->samples[sample];
if (editor.smpRedoBuffer[sample] != NULL)
{
@@ -792,7 +824,7 @@
{
editor.smpRedoBuffer[sample] = (int8_t *)malloc(s->length);
if (editor.smpRedoBuffer[sample] != NULL)
- memcpy(editor.smpRedoBuffer[sample], &modEntry->sampleData[s->offset], s->length);
+ memcpy(editor.smpRedoBuffer[sample], &song->sampleData[s->offset], s->length);
}
}
@@ -821,7 +853,7 @@
sampler.blankSample = NULL;
}
- for (uint8_t i = 0; i < MOD_SAMPLES; i++)
+ for (int32_t i = 0; i < MOD_SAMPLES; i++)
{
if (editor.smpRedoBuffer[i] != NULL)
{
@@ -837,16 +869,22 @@
int32_t smp32, i, from, to, offset;
moduleSample_t *s;
+ if (editor.sampleZero)
+ {
+ statusNotSampleZero();
+ return;
+ }
+
assert(editor.currSample >= 0 && editor.currSample <= 30);
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
if (s->length == 0)
{
- displayErrorMsg("SAMPLE IS EMPTY");
+ statusSampleIsEmpty();
return;
}
- smpDat = &modEntry->sampleData[s->offset];
+ smpDat = &song->sampleData[s->offset];
from = 0;
to = s->length;
@@ -921,6 +959,12 @@
sampleMixer_t mixCh[4], *v;
moduleSample_t *s;
+ if (editor.sampleZero)
+ {
+ statusNotSampleZero();
+ return;
+ }
+
assert(editor.currSample >= 0 && editor.currSample <= 30);
assert(editor.tuningNote <= 35);
@@ -930,9 +974,9 @@
return;
}
- if (modEntry->samples[editor.currSample].length == 0)
+ if (song->samples[editor.currSample].length == 0)
{
- displayErrorMsg("SAMPLE IS EMPTY");
+ statusSampleIsEmpty();
return;
}
@@ -981,11 +1025,11 @@
// setup some variables
- smpLoopStart = modEntry->samples[editor.currSample].loopStart;
- smpLoopLength = modEntry->samples[editor.currSample].loopLength;
+ smpLoopStart = song->samples[editor.currSample].loopStart;
+ smpLoopLength = song->samples[editor.currSample].loopLength;
smpLoopFlag = (smpLoopStart + smpLoopLength) > 2;
- smpEnd = smpLoopFlag ? (smpLoopStart + smpLoopLength) : modEntry->samples[editor.currSample].length;
- smpData = &modEntry->sampleData[modEntry->samples[editor.currSample].offset];
+ smpEnd = smpLoopFlag ? (smpLoopStart + smpLoopLength) : song->samples[editor.currSample].length;
+ smpData = &song->sampleData[song->samples[editor.currSample].offset];
if (editor.newOldFlag == 0)
{
@@ -993,7 +1037,7 @@
for (i = 0; i < MOD_SAMPLES; i++)
{
- if (modEntry->samples[i].length == 0)
+ if (song->samples[i].length == 0)
break;
}
@@ -1003,11 +1047,11 @@
return;
}
- smpFinetune = modEntry->samples[editor.currSample].fineTune;
- smpVolume = modEntry->samples[editor.currSample].volume;
- memcpy(smpText, modEntry->samples[editor.currSample].text, sizeof (smpText));
+ smpFinetune = song->samples[editor.currSample].fineTune;
+ smpVolume = song->samples[editor.currSample].volume;
+ memcpy(smpText, song->samples[editor.currSample].text, sizeof (smpText));
- s = &modEntry->samples[i];
+ s = &song->samples[i];
s->fineTune = smpFinetune;
s->volume = smpVolume;
@@ -1017,7 +1061,7 @@
else
{
// overwrite current sample
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
}
mixData = (int32_t *)calloc(MAX_SAMPLE_LEN, sizeof (int32_t));
@@ -1121,10 +1165,10 @@
// normalize gain and quantize to 8-bit
for (i = 0; i < s->length; i++)
- modEntry->sampleData[s->offset + i] = (int8_t)(mixData[i] >> 24);
+ song->sampleData[s->offset + i] = (int8_t)(mixData[i] >> 24);
if (s->length < MAX_SAMPLE_LEN)
- memset(&modEntry->sampleData[s->offset + s->length], 0, MAX_SAMPLE_LEN - s->length);
+ memset(&song->sampleData[s->offset + s->length], 0, MAX_SAMPLE_LEN - s->length);
// we're done
@@ -1146,13 +1190,19 @@
uint32_t posFrac, delta;
moduleSample_t *s;
+ if (editor.sampleZero)
+ {
+ statusNotSampleZero();
+ return;
+ }
+
assert(editor.currSample >= 0 && editor.currSample <= 30);
assert(editor.tuningNote <= 35 && editor.resampleNote <= 35);
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
if (s->length == 0)
{
- displayErrorMsg("SAMPLE IS EMPTY");
+ statusSampleIsEmpty();
return;
}
@@ -1159,7 +1209,7 @@
// setup resampling variables
readPos = 0;
writePos = 0;
- writeData = &modEntry->sampleData[s->offset];
+ writeData = &song->sampleData[s->offset];
refPeriod = periodTable[editor.tuningNote];
newPeriod = periodTable[(37 * (s->fineTune & 0xF)) + editor.resampleNote];
readLength = s->length;
@@ -1297,9 +1347,9 @@
return;
}
- s1 = &modEntry->samples[--smpFrom1];
- s2 = &modEntry->samples[--smpFrom2];
- s3 = &modEntry->samples[--smpTo];
+ s1 = &song->samples[--smpFrom1];
+ s2 = &song->samples[--smpFrom2];
+ s3 = &song->samples[--smpTo];
if (s1->length == 0 || s2->length == 0)
{
@@ -1309,14 +1359,14 @@
if (s1->length > s2->length)
{
- fromPtr1 = &modEntry->sampleData[s1->offset];
- fromPtr2 = &modEntry->sampleData[s2->offset];
+ fromPtr1 = &song->sampleData[s1->offset];
+ fromPtr2 = &song->sampleData[s2->offset];
mixLength = s1->length;
}
else
{
- fromPtr1 = &modEntry->sampleData[s2->offset];
- fromPtr2 = &modEntry->sampleData[s1->offset];
+ fromPtr1 = &song->sampleData[s2->offset];
+ fromPtr2 = &song->sampleData[s1->offset];
mixLength = s2->length;
}
@@ -1339,9 +1389,9 @@
mixPtr[i] = (int8_t)tmp16;
}
- memcpy(&modEntry->sampleData[s3->offset], mixPtr, mixLength);
+ memcpy(&song->sampleData[s3->offset], mixPtr, mixLength);
if (mixLength < MAX_SAMPLE_LEN)
- memset(&modEntry->sampleData[s3->offset + mixLength], 0, MAX_SAMPLE_LEN - mixLength);
+ memset(&song->sampleData[s3->offset + mixLength], 0, MAX_SAMPLE_LEN - mixLength);
free(mixPtr);
@@ -1369,11 +1419,11 @@
assert(sample >= 0 && sample <= 30);
- s = &modEntry->samples[sample];
+ s = &song->samples[sample];
if (s->length == 0)
return; // don't display warning/show warning pointer, it is done elsewhere
- smpDat = &modEntry->sampleData[s->offset];
+ smpDat = &song->sampleData[s->offset];
from = 0;
to = s->length;
@@ -1425,11 +1475,11 @@
assert(sample >= 0 && sample <= 30);
- s = &modEntry->samples[sample];
+ s = &song->samples[sample];
if (s->length == 0)
return; // don't display warning/show warning pointer, it is done elsewhere
- smpDat = &modEntry->sampleData[s->offset];
+ smpDat = &song->sampleData[s->offset];
from = 1;
to = s->length;
@@ -1482,7 +1532,7 @@
if (editor.tuningNote > 35)
editor.tuningNote = 35;
- modEntry->channels[editor.tuningChan].n_volume = 64; // we need this for the scopes
+ song->channels[editor.tuningChan].n_volume = 64; // we need this for the scopes
paulaSetPeriod(editor.tuningChan, periodTable[editor.tuningNote]);
paulaSetVolume(editor.tuningChan, 64);
@@ -1506,7 +1556,7 @@
assert(editor.currSample >= 0 && editor.currSample <= 30);
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
if (s->length == 0)
{
invertRange();
@@ -1540,7 +1590,7 @@
assert(editor.currSample >= 0 && editor.currSample <= 30);
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
if (s->length == 0)
{
invertRange();
@@ -1578,7 +1628,7 @@
assert(editor.currSample >= 0 && editor.currSample <= 30);
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
if (s->length == 0)
{
invertRange();
@@ -1609,6 +1659,12 @@
{
moduleSample_t *s;
+ if (editor.sampleZero)
+ {
+ statusNotSampleZero();
+ return;
+ }
+
assert(editor.currSample >= 0 && editor.currSample <= 30);
if (editor.markStartOfs == -1)
@@ -1623,10 +1679,10 @@
return;
}
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
if (s->length == 0)
{
- displayErrorMsg("SAMPLE IS EMPTY");
+ statusSampleIsEmpty();
return;
}
@@ -1638,7 +1694,7 @@
return;
}
- memcpy(sampler.copyBuf, &modEntry->sampleData[s->offset+editor.markStartOfs], sampler.copyBufSize);
+ memcpy(sampler.copyBuf, &song->sampleData[s->offset+editor.markStartOfs], sampler.copyBufSize);
}
void samplerSamDelete(uint8_t cut)
@@ -1647,6 +1703,12 @@
int32_t val32, sampleLength, copyLength, markEnd, markStart;
moduleSample_t *s;
+ if (editor.sampleZero)
+ {
+ statusNotSampleZero();
+ return;
+ }
+
assert(editor.currSample >= 0 && editor.currSample <= 30);
if (editor.markStartOfs == -1)
@@ -1664,12 +1726,12 @@
if (cut)
samplerSamCopy();
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
sampleLength = s->length;
if (sampleLength == 0)
{
- displayErrorMsg("SAMPLE IS EMPTY");
+ statusSampleIsEmpty();
return;
}
@@ -1678,7 +1740,7 @@
// if whole sample is marked, wipe it
if (editor.markEndOfs-editor.markStartOfs >= sampleLength)
{
- memset(&modEntry->sampleData[s->offset], 0, MAX_SAMPLE_LEN);
+ memset(&song->sampleData[s->offset], 0, MAX_SAMPLE_LEN);
invertRange();
editor.markStartOfs = -1;
@@ -1718,17 +1780,17 @@
}
// copy start part
- memcpy(tmpBuf, &modEntry->sampleData[s->offset], editor.markStartOfs);
+ memcpy(tmpBuf, &song->sampleData[s->offset], editor.markStartOfs);
// copy end part
if (sampleLength-markEnd > 0)
- memcpy(&tmpBuf[editor.markStartOfs], &modEntry->sampleData[s->offset+markEnd], sampleLength - markEnd);
+ memcpy(&tmpBuf[editor.markStartOfs], &song->sampleData[s->offset+markEnd], sampleLength - markEnd);
// nuke sample data and copy over the result
- memcpy(&modEntry->sampleData[s->offset], tmpBuf, copyLength);
+ memcpy(&song->sampleData[s->offset], tmpBuf, copyLength);
if (copyLength < MAX_SAMPLE_LEN)
- memset(&modEntry->sampleData[s->offset+copyLength], 0, MAX_SAMPLE_LEN - copyLength);
+ memset(&song->sampleData[s->offset+copyLength], 0, MAX_SAMPLE_LEN - copyLength);
free(tmpBuf);
@@ -1833,6 +1895,12 @@
uint32_t readPos;
moduleSample_t *s;
+ if (editor.sampleZero)
+ {
+ statusNotSampleZero();
+ return;
+ }
+
assert(editor.currSample >= 0 && editor.currSample <= 30);
if (sampler.copyBuf == NULL || sampler.copyBufSize == 0)
@@ -1841,7 +1909,7 @@
return;
}
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
if (s->length > 0 && editor.markStartOfs == -1)
{
displayErrorMsg("SET CURSOR POS");
@@ -1872,7 +1940,7 @@
// copy start part
if (markStart > 0)
{
- memcpy(&tmpBuf[readPos], &modEntry->sampleData[s->offset], markStart);
+ memcpy(&tmpBuf[readPos], &song->sampleData[s->offset], markStart);
readPos += markStart;
}
@@ -1885,7 +1953,7 @@
readPos += sampler.copyBufSize;
if (s->length-markStart > 0)
- memcpy(&tmpBuf[readPos], &modEntry->sampleData[s->offset+markStart], s->length - markStart);
+ memcpy(&tmpBuf[readPos], &song->sampleData[s->offset+markStart], s->length - markStart);
}
int32_t newLength = (s->length + sampler.copyBufSize) & 0xFFFFFFFE;
@@ -1940,9 +2008,9 @@
}
}
- memcpy(&modEntry->sampleData[s->offset], tmpBuf, s->length);
+ memcpy(&song->sampleData[s->offset], tmpBuf, s->length);
if (s->length < MAX_SAMPLE_LEN)
- memset(&modEntry->sampleData[s->offset+s->length], 0, MAX_SAMPLE_LEN - s->length);
+ memset(&song->sampleData[s->offset+s->length], 0, MAX_SAMPLE_LEN - s->length);
free(tmpBuf);
@@ -1973,8 +2041,8 @@
assert(chn < AMIGA_VOICES);
assert(editor.currPlayNote <= 35);
- s = &modEntry->samples[editor.currSample];
- ch = &modEntry->channels[chn];
+ s = &song->samples[editor.currSample];
+ ch = &song->channels[chn];
ch->n_samplenum = editor.currSample;
ch->n_volume = s->volume;
@@ -1982,16 +2050,16 @@
if (playWaveformFlag)
{
- ch->n_start = &modEntry->sampleData[s->offset];
+ ch->n_start = &song->sampleData[s->offset];
ch->n_length = (s->loopStart > 0) ? (uint32_t)(s->loopStart + s->loopLength) / 2 : s->length / 2;
- ch->n_loopstart = &modEntry->sampleData[s->offset + s->loopStart];
+ ch->n_loopstart = &song->sampleData[s->offset + s->loopStart];
ch->n_replen = s->loopLength / 2;
}
else
{
- ch->n_start = &modEntry->sampleData[s->offset + startOffset];
+ ch->n_start = &song->sampleData[s->offset + startOffset];
ch->n_length = (uint16_t)((uint32_t)(endOffset - startOffset) >> 1);
- ch->n_loopstart = &modEntry->sampleData[s->offset];
+ ch->n_loopstart = &song->sampleData[s->offset];
ch->n_replen = 1;
}
@@ -2066,7 +2134,7 @@
assert(editor.currSample >= 0 && editor.currSample <= 30);
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
if (s->loopStart+s->loopLength > 2)
{
if (sampler.samDisplay > 0)
@@ -2119,7 +2187,7 @@
{
int32_t tmpDisplay, tmpOffset;
- if (modEntry->samples[editor.currSample].length == 0 || sampler.samDisplay <= 2)
+ if (song->samples[editor.currSample].length == 0 || sampler.samDisplay <= 2)
return;
if (step < 1)
@@ -2151,7 +2219,7 @@
{
int32_t tmpDisplay, tmpOffset;
- if (modEntry->samples[editor.currSample].length == 0 || sampler.samDisplay == sampler.samLength)
+ if (song->samples[editor.currSample].length == 0 || sampler.samDisplay == sampler.samLength)
return;
if (step < 1)
@@ -2205,7 +2273,7 @@
assert(editor.currSample >= 0 && editor.currSample <= 30);
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
if (s->length == 0)
{
invertRange();
@@ -2226,10 +2294,10 @@
assert(editor.currSample >= 0 && editor.currSample <= 30);
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
if (s->length == 0)
{
- displayErrorMsg("SAMPLE IS EMPTY");
+ statusSampleIsEmpty();
return;
}
@@ -2403,8 +2471,14 @@
int32_t mx, my, tmp32, p, vl, tvl, r, rl, rvl, start, end;
moduleSample_t *s;
+ if (editor.sampleZero)
+ {
+ statusNotSampleZero();
+ return;
+ }
+
assert(editor.currSample >= 0 && editor.currSample <= 30);
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
if (s->length == 0)
{
@@ -2461,7 +2535,7 @@
vl = tmp32;
}
- ptr8 = &modEntry->sampleData[s->offset];
+ ptr8 = &song->sampleData[s->offset];
start = p;
if (start < 0)
@@ -2515,7 +2589,7 @@
if (!mouseButtonHeld)
{
- if (mouse.y < 142)
+ if (!editor.sampleZero && mouse.y < 142)
{
if (mouse.x >= sampler.loopStartPos && mouse.x <= sampler.loopStartPos+3)
{
@@ -2538,7 +2612,7 @@
mouseX = CLAMP(mouse.x, 0, SCREEN_W+8); // allow some extra pixels outside of the screen
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
if (ui.leftLoopPinMoving)
{
@@ -2582,7 +2656,7 @@
{
sampler.lastMouseX = mouseX;
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
tmpPos = (scr2SmpPos(mouseX - 4) - s->loopStart) & 0xFFFFFFFE;
tmpPos = CLAMP(tmpPos, 2, MAX_SAMPLE_LEN);
@@ -2602,7 +2676,7 @@
if (!mouseButtonHeld)
{
- if (mouseX < 3 || mouseX >= SCREEN_W)
+ if (mouseX < 0 || mouseX >= SCREEN_W)
return;
ui.sampleMarkingPos = (int16_t)mouseX;
@@ -2693,9 +2767,15 @@
{
moduleSample_t *s;
+ if (editor.sampleZero)
+ {
+ statusNotSampleZero();
+ return;
+ }
+
assert(editor.currSample >= 0 && editor.currSample <= 30);
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
if (s->length < 2)
return;
@@ -2792,7 +2872,7 @@
for (i = 0; i < AMIGA_VOICES; i++)
{
- if (modEntry->channels[i].n_samplenum == editor.currSample && !editor.muted[i])
+ if (song->channels[i].n_samplenum == editor.currSample && !editor.muted[i])
{
pos = getSampleReadPos(i, editor.currSample);
if (pos >= 0)
--- a/src/pt2_scopes.c
+++ b/src/pt2_scopes.c
@@ -17,6 +17,7 @@
#include "pt2_palette.h"
#include "pt2_tables.h"
#include "pt2_structs.h"
+#include "pt2_config.h"
// this uses code that is not entirely thread safe, but I have never had any issues so far...
@@ -43,13 +44,13 @@
if (scopeExt[ch].active && pos >= 2)
{
- s = &modEntry->samples[smpNum];
+ s = &song->samples[smpNum];
/* Get real sampling position regardless of where the scope data points to
** sc->data changes during loop, offset and so on, so this has to be done
** (sadly, because it's really hackish).
*/
- pos = (int32_t)(&data[pos] - &modEntry->sampleData[s->offset]);
+ pos = (int32_t)(&data[pos] - &song->sampleData[s->offset]);
if (pos >= s->length)
return -1;
@@ -149,7 +150,7 @@
if (samplesToScan > 512) // don't waste cycles on reading a ton of samples
samplesToScan = 512;
- volume = modEntry->channels[i].n_volume;
+ volume = song->channels[i].n_volume;
if (se->active && tmpScope.data != NULL && volume != 0 && tmpScope.length > 0)
{
@@ -227,7 +228,7 @@
tmpScope = *sc;
didSwapData = se->didSwapData;
- volume = -modEntry->channels[i].n_volume; // invert volume
+ volume = -song->channels[i].n_volume; // invert volume
// render scope
if (se->active && tmpScope.data != NULL && volume != 0 && tmpScope.length > 0)
--- a/src/pt2_structs.h
+++ b/src/pt2_structs.h
@@ -32,6 +32,13 @@
uint32_t dwSMPTEFormat, dwSMPTEOffset, cSampleLoops, cbSamplerData;
sampleLoop_t loop;
} samplerChunk_t;
+
+typedef struct mptExtraChunk_t
+{
+ uint32_t chunkID, chunkSize, flags;
+ uint16_t defaultPan, defaultVolume, globalVolume, reserved;
+ uint8_t vibratoType, vibratoSweep, vibratoDepth, vibratoRate;
+} mptExtraChunk_t;
// -----------------------------------------
typedef struct note_t
@@ -42,8 +49,8 @@
typedef struct moduleHeader_t
{
- char moduleTitle[20 + 1];
- uint16_t order[MOD_ORDERS], orderCount;
+ char name[20 + 1];
+ uint16_t order[MOD_ORDERS], numOrders;
uint16_t initialTempo; // used for STK/UST modules after module is loaded
} moduleHeader_t;
@@ -71,14 +78,25 @@
typedef struct module_t
{
- int8_t *sampleData, currRow, modified, row;
- uint8_t currSpeed, moduleLoaded;
- uint16_t currOrder, currPattern, currBPM;
- uint32_t rowsCounter, rowsInTotal;
- moduleHeader_t head;
+ bool loaded, modified;
+ int8_t *sampleData;
+
+ volatile uint8_t tick, speed;
+
+ int8_t row; // used for different things, so must not be internal to replayer
+
+ moduleHeader_t header;
moduleSample_t samples[MOD_SAMPLES];
moduleChannel_t channels[AMIGA_VOICES];
note_t *patterns[MAX_PATTERNS];
+
+ // for pattern viewer
+ int8_t currRow;
+ uint8_t currSpeed;
+ uint16_t currOrder, currPattern, currBPM;
+
+ // for MOD2WAV progress bar
+ uint32_t rowsCounter, rowsInTotal;
} module_t;
typedef struct keyb_t
@@ -125,7 +143,6 @@
volatile int8_t vuMeterVolumes[AMIGA_VOICES], spectrumVolumes[SPECTRUM_BAR_NUM];
volatile int8_t *sampleFromDisp, *sampleToDisp, *currSampleDisp, realVuMeterVolumes[AMIGA_VOICES];
volatile bool songPlaying, programRunning, isWAVRendering, isSMPRendering, smpRenderingDone;
- volatile uint8_t modTick, modSpeed;
volatile uint16_t *quantizeValueDisp, *metroSpeedDisp, *metroChannelDisp, *sampleVolDisp;
volatile uint16_t *vol1Disp, *vol2Disp, *currEditPatternDisp, *currPosDisp, *currPatternDisp;
volatile uint16_t *currPosEdPattDisp, *currLengthDisp, *lpCutOffDisp, *hpCutOffDisp;
@@ -144,7 +161,7 @@
int8_t smpRedoFinetunes[MOD_SAMPLES], smpRedoVolumes[MOD_SAMPLES], multiModeNext[4], trackPattFlag;
int8_t *smpRedoBuffer[MOD_SAMPLES], *tempSample, currSample, recordMode, sampleFrom, sampleTo, autoInsSlot;
- int8_t keypadSampleOffset, note1, note2, note3, note4, oldNote1, oldNote2, oldNote3, oldNote4;
+ int8_t hiLowInstr, note1, note2, note3, note4, oldNote1, oldNote2, oldNote3, oldNote4;
uint8_t playMode, currMode, tuningChan, tuningVol, errorMsgCounter, buffFromPos, buffToPos;
uint8_t blockFromPos, blockToPos, timingMode, f6Pos, f7Pos, f8Pos, f9Pos, f10Pos, keyOctave, pNoteFlag;
uint8_t tuningNote, resampleNote, initialTempo, initialSpeed, editMoveAdd;
@@ -240,4 +257,4 @@
extern cursor_t cursor;
extern ui_t ui;
-extern module_t *modEntry; // pt_main.c
+extern module_t *song; // pt_main.c
--- a/src/pt2_tables.c
+++ b/src/pt2_tables.c
@@ -1,15 +1,6 @@
-#include <SDL2/SDL.h>
-#include <stdio.h>
#include <stdint.h>
+#include <stdbool.h>
#include "pt2_mouse.h"
-
-uint32_t *aboutScreenBMP = NULL, *clearDialogBMP = NULL;
-uint32_t *diskOpScreenBMP = NULL, *editOpModeCharsBMP = NULL, *mod2wavBMP = NULL;
-uint32_t *editOpScreen1BMP = NULL, *editOpScreen2BMP = NULL, *samplerVolumeBMP = NULL;
-uint32_t *editOpScreen3BMP = NULL, *editOpScreen4BMP = NULL, *spectrumVisualsBMP = NULL;
-uint32_t *muteButtonsBMP = NULL, *posEdBMP = NULL, *samplerFiltersBMP = NULL;
-uint32_t *samplerScreenBMP = NULL, *pat2SmpDialogBMP = NULL, *trackerFrameBMP = NULL;
-uint32_t *yesNoDialogBMP = NULL, *bigYesNoDialogBMP = NULL;
const char hexTable[16] =
{
--- a/src/pt2_tables.h
+++ b/src/pt2_tables.h
@@ -16,62 +16,10 @@
extern const int16_t periodTable[(37*16)+15];
extern int8_t pNoteTable[32];
-// GFX
-extern uint32_t iconBMP[1024];
-extern const uint8_t mousePointerBMP[256];
-extern const uint8_t fontBMP[6096];
-
-// PACKED GFX
-extern const uint8_t aboutScreenPackedBMP[1684];
-extern const uint8_t clearDialogPackedBMP[525];
-extern const uint8_t diskOpScreenPackedBMP[1898];
-extern const uint8_t editOpModeCharsPackedBMP[88];
-extern const uint8_t editOpScreen1PackedBMP[1481];
-extern const uint8_t editOpScreen2PackedBMP[1502];
-extern const uint8_t editOpScreen3PackedBMP[1736];
-extern const uint8_t editOpScreen4PackedBMP[1713];
-extern const uint8_t mod2wavPackedBMP[607];
-extern const uint8_t muteButtonsPackedBMP[46];
-extern const uint8_t posEdPackedBMP[1375];
-extern const uint8_t samplerVolumePackedBMP[706];
-extern const uint8_t samplerFiltersPackedBMP[933];
-extern const uint8_t samplerScreenPackedBMP[3056];
-extern const uint8_t spectrumVisualsPackedBMP[2217];
-extern const uint8_t trackerFramePackedBMP[8486];
-extern const uint8_t yesNoDialogPackedBMP[476];
-extern const uint8_t bigYesNoDialogPackedBMP[472];
-extern const uint8_t pat2SmpDialogPackedBMP[520];
-
// changable by config file
extern uint16_t analyzerColors[36];
extern uint16_t vuMeterColors[48];
-// these are filled/normalized on init, so no const
-extern uint32_t vuMeterBMP[480];
-extern uint32_t loopPinsBMP[512];
-extern uint32_t samplingPosBMP[64];
-extern uint32_t spectrumAnaBMP[36];
-extern uint32_t patternCursorBMP[154];
-extern uint32_t *editOpScreen1BMP;
-extern uint32_t *editOpScreen2BMP;
-extern uint32_t *editOpScreen3BMP;
-extern uint32_t *editOpScreen4BMP;
-extern uint32_t *yesNoDialogBMP;
-extern uint32_t *bigYesNoDialogBMP;
-extern uint32_t *spectrumVisualsBMP;
-extern uint32_t *posEdBMP;
-extern uint32_t *mod2wavBMP;
-extern uint32_t *diskOpScreenBMP;
-extern uint32_t *clearDialogBMP;
-extern uint32_t *samplerVolumeBMP;
-extern uint32_t *samplerFiltersBMP;
-extern uint32_t *samplerScreenBMP;
-extern uint32_t *trackerFrameBMP;
-extern uint32_t *aboutScreenBMP;
-extern uint32_t *muteButtonsBMP;
-extern uint32_t *editOpModeCharsBMP;
-extern uint32_t *pat2SmpDialogBMP;
-
// button tables taken from the ptplay project + modified
// MODIFY THESE EVERY TIME YOU REMOVE/ADD A BUTTON!
@@ -103,12 +51,3 @@
extern const guiButton_t bEditOp3[];
extern const guiButton_t bEditOp4[];
extern const guiButton_t bSampler[];
-
-#define EDOP_MODE_BMP_A_OFS ((7 * 6) * 0)
-#define EDOP_MODE_BMP_C_OFS ((7 * 6) * 1)
-#define EDOP_MODE_BMP_H_OFS ((7 * 6) * 2)
-#define EDOP_MODE_BMP_N_OFS ((7 * 6) * 3)
-#define EDOP_MODE_BMP_O_OFS ((7 * 6) * 4)
-#define EDOP_MODE_BMP_P_OFS ((7 * 6) * 5)
-#define EDOP_MODE_BMP_S_OFS ((7 * 6) * 6)
-#define EDOP_MODE_BMP_T_OFS ((7 * 6) * 7)
--- a/src/pt2_textout.c
+++ b/src/pt2_textout.c
@@ -7,6 +7,7 @@
#include "pt2_palette.h"
#include "pt2_visuals.h"
#include "pt2_structs.h"
+#include "pt2_bmp.h"
void charOut(uint32_t xPos, uint32_t yPos, char ch, uint32_t color)
{
@@ -32,38 +33,6 @@
}
}
-void charOut2(uint32_t xPos, uint32_t yPos, char ch)
-{
- const uint8_t *srcPtr;
- uint32_t *dstPtr1, *dstPtr2;
-
- if (ch == '\0' || ch == ' ')
- return;
-
- srcPtr = &fontBMP[(ch & 0x7F) << 3];
- dstPtr1 = &video.frameBuffer[(yPos * SCREEN_W) + xPos];
- dstPtr2 = dstPtr1 + (SCREEN_W+1);
-
- const uint32_t color1 = video.palette[PAL_BORDER];
- const uint32_t color2 = video.palette[PAL_GENBKG2];
-
- for (int32_t y = 0; y < FONT_CHAR_H; y++)
- {
- for (int32_t x = 0; x < FONT_CHAR_W; x++)
- {
- if (srcPtr[x])
- {
- dstPtr2[x] = color2;
- dstPtr1[x] = color1;
- }
- }
-
- srcPtr += 127*FONT_CHAR_W;
- dstPtr1 += SCREEN_W;
- dstPtr2 += SCREEN_W;
- }
-}
-
void charOutBg(uint32_t xPos, uint32_t yPos, char ch, uint32_t fgColor, uint32_t bgColor)
{
const uint8_t *srcPtr;
@@ -155,18 +124,6 @@
while (*text != '\0')
{
charOut(x, yPos, *text++, color);
- x += FONT_CHAR_W;
- }
-}
-
-void textOut2(uint32_t xPos, uint32_t yPos, const char *text)
-{
- assert(text != NULL);
-
- uint32_t x = xPos;
- while (*text != '\0')
- {
- charOut2(x, yPos, *text++);
x += FONT_CHAR_W;
}
}
--- a/src/pt2_textout.h
+++ b/src/pt2_textout.h
@@ -4,12 +4,10 @@
#include <stdbool.h>
void charOut(uint32_t xPos, uint32_t yPos, char ch, uint32_t color);
-void charOut2(uint32_t xPos, uint32_t yPos, char ch);
void charOutBg(uint32_t xPos, uint32_t yPos, char ch, uint32_t fgColor, uint32_t bgColor);
void charOutBig(uint32_t xPos, uint32_t yPos, char ch, uint32_t color);
void charOutBigBg(uint32_t xPos, uint32_t yPos, char ch, uint32_t fgColor, uint32_t bgColor);
void textOut(uint32_t xPos, uint32_t yPos, const char *text, uint32_t color);
-void textOut2(uint32_t xPos, uint32_t yPos, const char *text);
void textOutTight(uint32_t xPos, uint32_t yPos, const char *text, uint32_t color);
void textOutBg(uint32_t xPos, uint32_t yPos, const char *text, uint32_t fgColor, uint32_t bgColor);
void textOutBig(uint32_t xPos, uint32_t yPos, const char *text, uint32_t color);
--- a/src/pt2_visuals.c
+++ b/src/pt2_visuals.c
@@ -26,17 +26,20 @@
#include "pt2_helpers.h"
#include "pt2_textout.h"
#include "pt2_tables.h"
-#include "pt2_modloader.h"
-#include "pt2_sampleloader.h"
-#include "pt2_patternviewer.h"
+#include "pt2_module_loader.h"
+#include "pt2_module_saver.h"
+#include "pt2_sample_loader.h"
+#include "pt2_sample_saver.h"
+#include "pt2_pattern_viewer.h"
#include "pt2_sampler.h"
#include "pt2_diskop.h"
#include "pt2_visuals.h"
-#include "pt2_helpers.h"
#include "pt2_scopes.h"
#include "pt2_edit.h"
#include "pt2_pat2smp.h"
#include "pt2_mod2wav.h"
+#include "pt2_config.h"
+#include "pt2_bmp.h"
typedef struct sprite_t
{
@@ -77,6 +80,20 @@
displayErrorMsg("OUT OF MEMORY !!!");
}
+void statusSampleIsEmpty(void)
+{
+ displayErrorMsg("SAMPLE IS EMPTY");
+}
+
+void statusNotSampleZero(void)
+{
+ /* This rather confusing error message actually means that
+ ** you can't load a sample to sample slot #0 (which isn't a
+ ** real sample slot).
+ */
+ displayErrorMsg("NOT SAMPLE 0 !");
+}
+
void setupPerfFreq(void)
{
uint64_t perfFreq64;
@@ -339,7 +356,7 @@
if (ui.diskOpScreenShown)
return;
- currSample = &modEntry->samples[editor.currSample];
+ currSample = &song->samples[editor.currSample];
if (ui.updateSongPos)
{
@@ -441,7 +458,7 @@
textOut(88, 127, "MARK BLOCK", video.palette[PAL_GENTXT]);
charOut(192, 127, '-', video.palette[PAL_GENTXT]);
- editor.blockToPos = modEntry->currRow;
+ editor.blockToPos = song->currRow;
if (editor.blockFromPos >= editor.blockToPos)
{
printTwoDecimals(176, 127, editor.blockToPos, video.palette[PAL_GENTXT]);
@@ -463,7 +480,7 @@
{
ui.updateSongBPM = false;
if (!ui.samplerScreenShown)
- printThreeDecimalsBg(32, 123, modEntry->currBPM, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
+ printThreeDecimalsBg(32, 123, song->currBPM, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
}
if (ui.updateCurrPattText)
@@ -542,7 +559,7 @@
ui.updateSongName = false;
for (x = 0; x < 20; x++)
{
- tempChar = modEntry->head.moduleTitle[x];
+ tempChar = song->header.name[x];
if (tempChar == '\0')
tempChar = '_';
@@ -553,7 +570,7 @@
if (ui.updateCurrSampleName)
{
ui.updateCurrSampleName = false;
- currSample = &modEntry->samples[editor.currSample];
+ currSample = &song->samples[editor.currSample];
for (x = 0; x < 22; x++)
{
@@ -575,13 +592,13 @@
// calculate module length
uint32_t totalSampleDataSize = 0;
for (i = 0; i < MOD_SAMPLES; i++)
- totalSampleDataSize += modEntry->samples[i].length;
+ totalSampleDataSize += song->samples[i].length;
uint32_t totalPatterns = 0;
for (i = 0; i < MOD_ORDERS; i++)
{
- if (modEntry->head.order[i] > totalPatterns)
- totalPatterns = modEntry->head.order[i];
+ if (song->header.order[i] > totalPatterns)
+ totalPatterns = song->header.order[i];
}
uint32_t moduleSize = 2108 + (totalPatterns * 1024) + totalSampleDataSize;
@@ -618,7 +635,7 @@
return;
assert(editor.currSample >= 0 && editor.currSample <= 30);
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
// update 9xx offset
if (mouse.y >= 138 && mouse.y <= 201 && mouse.x >= 3 && mouse.x <= 316)
@@ -899,9 +916,9 @@
{
bgPixel = video.palette[PAL_BACKGRD];
- posEdPosition = modEntry->currOrder;
- if (posEdPosition > modEntry->head.orderCount-1)
- posEdPosition = modEntry->head.orderCount-1;
+ posEdPosition = song->currOrder;
+ if (posEdPosition > song->header.numOrders-1)
+ posEdPosition = song->header.numOrders-1;
// top five
for (y = 0; y < 5; y++)
@@ -909,7 +926,7 @@
if (posEdPosition-(5-y) >= 0)
{
printThreeDecimalsBg(128, 23+(y*6), posEdPosition-(5-y), video.palette[PAL_QADSCP], video.palette[PAL_BACKGRD]);
- printTwoDecimalsBg(160, 23+(y*6), modEntry->head.order[posEdPosition-(5-y)], video.palette[PAL_QADSCP], video.palette[PAL_BACKGRD]);
+ printTwoDecimalsBg(160, 23+(y*6), song->header.order[posEdPosition-(5-y)], video.palette[PAL_QADSCP], video.palette[PAL_BACKGRD]);
}
else
{
@@ -931,10 +948,10 @@
// bottom six
for (y = 0; y < 6; y++)
{
- if (posEdPosition+y < modEntry->head.orderCount-1)
+ if (posEdPosition+y < song->header.numOrders-1)
{
printThreeDecimalsBg(128, 59+(y*6), posEdPosition+(y+1), video.palette[PAL_QADSCP], video.palette[PAL_BACKGRD]);
- printTwoDecimalsBg(160, 59+(y*6), modEntry->head.order[posEdPosition+(y+1)], video.palette[PAL_QADSCP], video.palette[PAL_BACKGRD]);
+ printTwoDecimalsBg(160, 59+(y*6), song->header.order[posEdPosition+(y+1)], video.palette[PAL_QADSCP], video.palette[PAL_BACKGRD]);
}
else
{
@@ -1449,7 +1466,7 @@
}
editor.isWAVRendering = false;
- modSetTempo(modEntry->currBPM); // update BPM with normal audio output rate
+ modSetTempo(song->currBPM); // update BPM with normal audio output rate
displayMainScreen();
}
else
@@ -1456,7 +1473,7 @@
{
// render progress bar
- percent = (uint8_t)((modEntry->rowsCounter * 100) / modEntry->rowsInTotal);
+ percent = (uint8_t)((song->rowsCounter * 100) / song->rowsInTotal);
if (percent > 100)
percent = 100;
@@ -1591,7 +1608,7 @@
textOutBg(168, 91, " ", video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
charOut(198, 91, ':', video.palette[PAL_GENBKG]);
- if (modEntry->samples[editor.currSample].loopLength > 2 || modEntry->samples[editor.currSample].loopStart >= 2)
+ if (song->samples[editor.currSample].loopLength > 2 || song->samples[editor.currSample].loopStart >= 2)
{
textOut(168, 91, "LOOP", video.palette[PAL_GENTXT]);
}
@@ -1830,7 +1847,7 @@
for (i = 0; i < MOD_SAMPLES; i++)
{
editor.currSample = (int8_t)i;
- if (modEntry->samples[i].length > 2)
+ if (song->samples[i].length > 2)
saveSample(DONT_CHECK_IF_FILE_EXIST, GIVE_NEW_FILENAME);
}
editor.currSample = oldSample;
@@ -1893,8 +1910,14 @@
{
restoreStatusAndMousePointer();
+ if (editor.sampleZero)
+ {
+ statusNotSampleZero();
+ break;
+ }
+
turnOffVoices();
- s = &modEntry->samples[editor.currSample];
+ s = &song->samples[editor.currSample];
s->fineTune = 0;
s->volume = 0;
@@ -1903,7 +1926,7 @@
s->loopLength = 2;
memset(s->text, 0, sizeof (s->text));
- memset(&modEntry->sampleData[(editor.currSample * MAX_SAMPLE_LEN)], 0, MAX_SAMPLE_LEN);
+ memset(&song->sampleData[(editor.currSample * MAX_SAMPLE_LEN)], 0, MAX_SAMPLE_LEN);
editor.samplePos = 0;
updateCurrSample();
@@ -1932,11 +1955,11 @@
{
memset(fileName, 0, sizeof (fileName));
- if (modEntry->head.moduleTitle[0] != '\0')
+ if (song->header.name[0] != '\0')
{
for (i = 0; i < 20; i++)
{
- fileName[i] = (char)tolower(modEntry->head.moduleTitle[i]);
+ fileName[i] = (char)tolower(song->header.name[i]);
if (fileName[i] == '\0') break;
sanitizeFilenameChar(&fileName[i]);
}
@@ -1956,11 +1979,11 @@
{
memset(fileName, 0, sizeof (fileName));
- if (modEntry->head.moduleTitle[0] != '\0')
+ if (song->header.name[0] != '\0')
{
for (i = 0; i < 20; i++)
{
- fileName[i] = (char)(tolower(modEntry->head.moduleTitle[i]));
+ fileName[i] = (char)(tolower(song->header.name[i]));
if (fileName[i] == '\0') break;
sanitizeFilenameChar(&fileName[i]);
}
@@ -2015,276 +2038,6 @@
}
removeAskDialog();
-}
-
-void createBitmaps(void)
-{
- uint8_t r8, g8, b8, r8_2, g8_2, b8_2;
- uint16_t pixel12;
- uint32_t i, j, x, y, pixel24;
-
- pixel24 = video.palette[PAL_PATCURSOR];
- for (y = 0; y < 14; y++)
- {
- // top two rows have a lighter color
- if (y < 2)
- {
- r8 = R24(pixel24);
- g8 = G24(pixel24);
- b8 = B24(pixel24);
-
- if (r8 <= 0xFF-0x33)
- r8 += 0x33;
- else
- r8 = 0xFF;
-
- if (g8 <= 0xFF-0x33)
- g8 += 0x33;
- else
- g8 = 0xFF;
-
- if (b8 <= 0xFF-0x33)
- b8 += 0x33;
- else
- b8 = 0xFF;
-
- for (x = 0; x < 11; x++)
- patternCursorBMP[(y * 11) + x] = RGB24(r8, g8, b8);
- }
-
- // sides (same color)
- if (y >= 2 && y <= 12)
- {
- patternCursorBMP[(y * 11) + 0] = pixel24;
-
- for (x = 1; x < 10; x++)
- patternCursorBMP[(y * 11) + x] = video.palette[PAL_COLORKEY];
-
- patternCursorBMP[(y * 11) + 10] = pixel24;
- }
-
- // bottom two rows have a darker color
- if (y > 11)
- {
- r8 = R24(pixel24);
- g8 = G24(pixel24);
- b8 = B24(pixel24);
-
- if (r8 >= 0x33)
- r8 -= 0x33;
- else
- r8 = 0x00;
-
- if (g8 >= 0x33)
- g8 -= 0x33;
- else
- g8 = 0x00;
-
- if (b8 >= 0x33)
- b8 -= 0x33;
- else
- b8 = 0x00;
-
- for (x = 0; x < 11; x++)
- patternCursorBMP[(y * 11) + x] = RGB24(r8, g8, b8);
- }
- }
-
- // create spectrum analyzer bar graphics
- for (i = 0; i < 36; i++)
- spectrumAnaBMP[i] = RGB12_to_RGB24(analyzerColors[35-i]);
-
- // create VU-Meter bar graphics
- for (i = 0; i < 48; i++)
- {
- pixel12 = vuMeterColors[47-i];
-
- r8_2 = r8 = R12_to_R24(pixel12);
- g8_2 = g8 = G12_to_G24(pixel12);
- b8_2 = b8 = B12_to_B24(pixel12);
-
- // brighter pixels on the left side
-
- if (r8_2 <= 0xFF-0x33)
- r8_2 += 0x33;
- else
- r8_2 = 0xFF;
-
- if (g8_2 <= 0xFF-0x33)
- g8_2 += 0x33;
- else
- g8_2 = 0xFF;
-
- if (b8_2 <= 0xFF-0x33)
- b8_2 += 0x33;
- else
- b8_2 = 0xFF;
-
- pixel24 = RGB24(r8_2, g8_2, b8_2);
-
- vuMeterBMP[(i * 10) + 0] = pixel24;
- vuMeterBMP[(i * 10) + 1] = pixel24;
-
- // main pixels
- for (j = 2; j < 8; j++)
- vuMeterBMP[(i * 10) + j] = RGB24(r8, g8, b8);
-
- // darker pixels on the right side
- r8_2 = r8;
- g8_2 = g8;
- b8_2 = b8;
-
- if (r8_2 >= 0x33)
- r8_2 -= 0x33;
- else
- r8_2 = 0x00;
-
- if (g8_2 >= 0x33)
- g8_2 -= 0x33;
- else
- g8_2 = 0x00;
-
- if (b8_2 >= 0x33)
- b8_2 -= 0x33;
- else
- b8_2 = 0x00;
-
- pixel24 = RGB24(r8_2, g8_2, b8_2);
-
- vuMeterBMP[(i * 10) + 8] = pixel24;
- vuMeterBMP[(i * 10) + 9] = pixel24;
- }
-}
-
-void freeBMPs(void)
-{
- if (trackerFrameBMP != NULL) free(trackerFrameBMP);
- if (samplerScreenBMP != NULL) free(samplerScreenBMP);
- if (samplerVolumeBMP != NULL) free(samplerVolumeBMP);
- if (samplerFiltersBMP != NULL) free(samplerFiltersBMP);
- if (clearDialogBMP != NULL) free(clearDialogBMP);
- if (diskOpScreenBMP != NULL) free(diskOpScreenBMP);
- if (mod2wavBMP != NULL) free(mod2wavBMP);
- if (posEdBMP != NULL) free(posEdBMP);
- if (spectrumVisualsBMP != NULL) free(spectrumVisualsBMP);
- if (yesNoDialogBMP != NULL) free(yesNoDialogBMP);
- if (bigYesNoDialogBMP != NULL) free(bigYesNoDialogBMP);
- if (pat2SmpDialogBMP != NULL) free(pat2SmpDialogBMP);
- if (editOpScreen1BMP != NULL) free(editOpScreen1BMP);
- if (editOpScreen2BMP != NULL) free(editOpScreen2BMP);
- if (editOpScreen3BMP != NULL) free(editOpScreen3BMP);
- if (editOpScreen4BMP != NULL) free(editOpScreen4BMP);
- if (aboutScreenBMP != NULL) free(aboutScreenBMP);
- if (muteButtonsBMP != NULL) free(muteButtonsBMP);
- if (editOpModeCharsBMP != NULL) free(editOpModeCharsBMP);
-}
-
-uint32_t *unpackBMP(const uint8_t *src, uint32_t packedLen)
-{
- const uint8_t *packSrc;
- uint8_t *tmpBuffer, *packDst, byteIn;
- int16_t count;
- uint32_t *dst, decodedLength, i;
-
- // RLE decode
- decodedLength = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
-
- // 2-bit to 8-bit conversion
- dst = (uint32_t *)malloc((decodedLength * 4) * sizeof (int32_t));
- if (dst == NULL)
- return NULL;
-
- tmpBuffer = (uint8_t *)malloc(decodedLength + 512); // some margin is needed, the packer is buggy
- if (tmpBuffer == NULL)
- {
- free(dst);
- return NULL;
- }
-
- packSrc = src + 4;
- packDst = tmpBuffer;
-
- i = packedLen - 4;
- while (i > 0)
- {
- byteIn = *packSrc++;
- if (byteIn == 0xCC) // compactor code
- {
- count = *packSrc++;
- byteIn = *packSrc++;
-
- while (count-- >= 0)
- *packDst++ = byteIn;
-
- i -= 2;
- }
- else
- {
- *packDst++ = byteIn;
- }
-
- i--;
- }
-
- for (i = 0; i < decodedLength; i++)
- {
- byteIn = (tmpBuffer[i] & 0xC0) >> 6;
- assert(byteIn < PALETTE_NUM);
- dst[(i * 4) + 0] = video.palette[byteIn];
-
- byteIn = (tmpBuffer[i] & 0x30) >> 4;
- assert(byteIn < PALETTE_NUM);
- dst[(i * 4) + 1] = video.palette[byteIn];
-
- byteIn = (tmpBuffer[i] & 0x0C) >> 2;
- assert(byteIn < PALETTE_NUM);
- dst[(i * 4) + 2] = video.palette[byteIn];
-
- byteIn = (tmpBuffer[i] & 0x03) >> 0;
- assert(byteIn < PALETTE_NUM);
- dst[(i * 4) + 3] = video.palette[byteIn];
- }
-
- free(tmpBuffer);
- return dst;
-}
-
-bool unpackBMPs(void)
-{
- trackerFrameBMP = unpackBMP(trackerFramePackedBMP, sizeof (trackerFramePackedBMP));
- samplerScreenBMP = unpackBMP(samplerScreenPackedBMP, sizeof (samplerScreenPackedBMP));
- samplerVolumeBMP = unpackBMP(samplerVolumePackedBMP, sizeof (samplerVolumePackedBMP));
- samplerFiltersBMP = unpackBMP(samplerFiltersPackedBMP, sizeof (samplerFiltersPackedBMP));
- clearDialogBMP = unpackBMP(clearDialogPackedBMP, sizeof (clearDialogPackedBMP));
- diskOpScreenBMP = unpackBMP(diskOpScreenPackedBMP, sizeof (diskOpScreenPackedBMP));
- mod2wavBMP = unpackBMP(mod2wavPackedBMP, sizeof (mod2wavPackedBMP));
- posEdBMP = unpackBMP(posEdPackedBMP, sizeof (posEdPackedBMP));
- spectrumVisualsBMP = unpackBMP(spectrumVisualsPackedBMP, sizeof (spectrumVisualsPackedBMP));
- yesNoDialogBMP = unpackBMP(yesNoDialogPackedBMP, sizeof (yesNoDialogPackedBMP));
- bigYesNoDialogBMP = unpackBMP(bigYesNoDialogPackedBMP, sizeof (bigYesNoDialogPackedBMP));
- pat2SmpDialogBMP = unpackBMP(pat2SmpDialogPackedBMP, sizeof (pat2SmpDialogPackedBMP));
- editOpScreen1BMP = unpackBMP(editOpScreen1PackedBMP, sizeof (editOpScreen1PackedBMP));
- editOpScreen2BMP = unpackBMP(editOpScreen2PackedBMP, sizeof (editOpScreen2PackedBMP));
- editOpScreen3BMP = unpackBMP(editOpScreen3PackedBMP, sizeof (editOpScreen3PackedBMP));
- editOpScreen4BMP = unpackBMP(editOpScreen4PackedBMP, sizeof (editOpScreen4PackedBMP));
- aboutScreenBMP = unpackBMP(aboutScreenPackedBMP, sizeof (aboutScreenPackedBMP));
- muteButtonsBMP = unpackBMP(muteButtonsPackedBMP, sizeof (muteButtonsPackedBMP));
- editOpModeCharsBMP = unpackBMP(editOpModeCharsPackedBMP, sizeof (editOpModeCharsPackedBMP));
-
- if (trackerFrameBMP == NULL || samplerScreenBMP == NULL || samplerVolumeBMP == NULL ||
- clearDialogBMP == NULL || diskOpScreenBMP == NULL || mod2wavBMP == NULL ||
- posEdBMP == NULL || spectrumVisualsBMP == NULL || yesNoDialogBMP == NULL ||
- editOpScreen1BMP == NULL || editOpScreen2BMP == NULL || editOpScreen3BMP == NULL ||
- editOpScreen4BMP == NULL || aboutScreenBMP == NULL || muteButtonsBMP == NULL ||
- editOpModeCharsBMP == NULL || samplerFiltersBMP == NULL || yesNoDialogBMP == NULL ||
- bigYesNoDialogBMP == NULL)
- {
- showErrorMsgBox("Out of memory!");
- return false; // BMPs are free'd in cleanUp()
- }
-
- createBitmaps();
- return true;
}
void videoClose(void)
--- a/src/pt2_visuals.h
+++ b/src/pt2_visuals.h
@@ -20,11 +20,13 @@
void statusAllRight(void);
void statusOutOfMemory(void);
+void statusSampleIsEmpty(void);
+void statusNotSampleZero(void);
+
void setupPerfFreq(void);
void setupWaitVBL(void);
void waitVBL(void);
void resetAllScreens(void);
-void freeBMPs(void);
void handleAskNo(void);
void handleAskYes(void);
bool setupVideo(void);
@@ -38,8 +40,7 @@
void updateDiskOp(void);
void toggleFullScreen(void);
void videoClose(void);
-bool unpackBMPs(void);
-void createBitmaps(void);
+
void displayMainScreen(void);
void renderAskDialog(void);
void renderBigAskDialog(void);
--- a/vs2019_project/pt2-clone/pt2-clone.vcxproj
+++ b/vs2019_project/pt2-clone/pt2-clone.vcxproj
@@ -273,6 +273,7 @@
<ItemGroup>
<ClInclude Include="..\..\src\pt2_audio.h" />
<ClInclude Include="..\..\src\pt2_blep.h" />
+ <ClInclude Include="..\..\src\pt2_bmp.h" />
<ClInclude Include="..\..\src\pt2_config.h" />
<ClInclude Include="..\..\src\pt2_diskop.h" />
<ClInclude Include="..\..\src\pt2_edit.h" />
@@ -280,13 +281,15 @@
<ClInclude Include="..\..\src\pt2_helpers.h" />
<ClInclude Include="..\..\src\pt2_keyboard.h" />
<ClInclude Include="..\..\src\pt2_mod2wav.h" />
- <ClInclude Include="..\..\src\pt2_modloader.h" />
+ <ClInclude Include="..\..\src\pt2_module_loader.h" />
+ <ClInclude Include="..\..\src\pt2_module_saver.h" />
<ClInclude Include="..\..\src\pt2_mouse.h" />
<ClInclude Include="..\..\src\pt2_palette.h" />
<ClInclude Include="..\..\src\pt2_pat2smp.h" />
- <ClInclude Include="..\..\src\pt2_patternviewer.h" />
- <ClInclude Include="..\..\src\pt2_sampleloader.h" />
+ <ClInclude Include="..\..\src\pt2_pattern_viewer.h" />
+ <ClInclude Include="..\..\src\pt2_sample_loader.h" />
<ClInclude Include="..\..\src\pt2_sampler.h" />
+ <ClInclude Include="..\..\src\pt2_sample_saver.h" />
<ClInclude Include="..\..\src\pt2_scopes.h" />
<ClInclude Include="..\..\src\pt2_structs.h" />
<ClInclude Include="..\..\src\pt2_tables.h" />
@@ -317,6 +320,7 @@
<EnableEnhancedInstructionSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
</ClCompile>
<ClCompile Include="..\..\src\pt2_blep.c" />
+ <ClCompile Include="..\..\src\pt2_bmp.c" />
<ClCompile Include="..\..\src\pt2_config.c" />
<ClCompile Include="..\..\src\pt2_diskop.c" />
<ClCompile Include="..\..\src\pt2_edit.c" />
@@ -324,14 +328,16 @@
<ClCompile Include="..\..\src\pt2_keyboard.c" />
<ClCompile Include="..\..\src\pt2_main.c" />
<ClCompile Include="..\..\src\pt2_mod2wav.c" />
- <ClCompile Include="..\..\src\pt2_modloader.c" />
- <ClCompile Include="..\..\src\pt2_modplayer.c" />
+ <ClCompile Include="..\..\src\pt2_module_loader.c" />
+ <ClCompile Include="..\..\src\pt2_replayer.c" />
+ <ClCompile Include="..\..\src\pt2_module_saver.c" />
<ClCompile Include="..\..\src\pt2_mouse.c" />
<ClCompile Include="..\..\src\pt2_palette.c" />
<ClCompile Include="..\..\src\pt2_pat2smp.c" />
- <ClCompile Include="..\..\src\pt2_patternviewer.c" />
- <ClCompile Include="..\..\src\pt2_sampleloader.c" />
+ <ClCompile Include="..\..\src\pt2_pattern_viewer.c" />
+ <ClCompile Include="..\..\src\pt2_sample_loader.c" />
<ClCompile Include="..\..\src\pt2_sampler.c" />
+ <ClCompile Include="..\..\src\pt2_sample_saver.c" />
<ClCompile Include="..\..\src\pt2_scopes.c" />
<ClCompile Include="..\..\src\pt2_structs.c" />
<ClCompile Include="..\..\src\pt2_tables.c" />
--- a/vs2019_project/pt2-clone/pt2-clone.vcxproj.filters
+++ b/vs2019_project/pt2-clone/pt2-clone.vcxproj.filters
@@ -33,9 +33,6 @@
<ClInclude Include="..\..\src\pt2_keyboard.h">
<Filter>headers</Filter>
</ClInclude>
- <ClInclude Include="..\..\src\pt2_modloader.h">
- <Filter>headers</Filter>
- </ClInclude>
<ClInclude Include="..\..\src\pt2_mouse.h">
<Filter>headers</Filter>
</ClInclude>
@@ -42,12 +39,6 @@
<ClInclude Include="..\..\src\pt2_palette.h">
<Filter>headers</Filter>
</ClInclude>
- <ClInclude Include="..\..\src\pt2_patternviewer.h">
- <Filter>headers</Filter>
- </ClInclude>
- <ClInclude Include="..\..\src\pt2_sampleloader.h">
- <Filter>headers</Filter>
- </ClInclude>
<ClInclude Include="..\..\src\pt2_sampler.h">
<Filter>headers</Filter>
</ClInclude>
@@ -75,6 +66,24 @@
<ClInclude Include="..\..\src\pt2_structs.h">
<Filter>headers</Filter>
</ClInclude>
+ <ClInclude Include="..\..\src\pt2_bmp.h">
+ <Filter>headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\pt2_sample_loader.h">
+ <Filter>headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\pt2_sample_saver.h">
+ <Filter>headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\pt2_module_loader.h">
+ <Filter>headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\pt2_pattern_viewer.h">
+ <Filter>headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\pt2_module_saver.h">
+ <Filter>headers</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\src\pt2_audio.c" />
@@ -85,12 +94,8 @@
<ClCompile Include="..\..\src\pt2_helpers.c" />
<ClCompile Include="..\..\src\pt2_keyboard.c" />
<ClCompile Include="..\..\src\pt2_main.c" />
- <ClCompile Include="..\..\src\pt2_modloader.c" />
- <ClCompile Include="..\..\src\pt2_modplayer.c" />
<ClCompile Include="..\..\src\pt2_mouse.c" />
<ClCompile Include="..\..\src\pt2_palette.c" />
- <ClCompile Include="..\..\src\pt2_patternviewer.c" />
- <ClCompile Include="..\..\src\pt2_sampleloader.c" />
<ClCompile Include="..\..\src\pt2_sampler.c" />
<ClCompile Include="..\..\src\pt2_scopes.c" />
<ClCompile Include="..\..\src\pt2_tables.c" />
@@ -154,6 +159,13 @@
<ClCompile Include="..\..\src\pt2_mod2wav.c" />
<ClCompile Include="..\..\src\pt2_pat2smp.c" />
<ClCompile Include="..\..\src\pt2_structs.c" />
+ <ClCompile Include="..\..\src\pt2_bmp.c" />
+ <ClCompile Include="..\..\src\pt2_sample_saver.c" />
+ <ClCompile Include="..\..\src\pt2_sample_loader.c" />
+ <ClCompile Include="..\..\src\pt2_module_loader.c" />
+ <ClCompile Include="..\..\src\pt2_pattern_viewer.c" />
+ <ClCompile Include="..\..\src\pt2_module_saver.c" />
+ <ClCompile Include="..\..\src\pt2_replayer.c" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\..\src\pt2-clone.rc" />