commit 380d39a7206417b80dc6468d44c8cc3f93c197d5
parent 4dd5fb98c4325b4fb674bce5dec18652fdd5b22a
Author: Olav Sørensen <olav.sorensen@live.no>
Date: Tue, 4 Mar 2025 18:31:28 +0100
New smp. ed. "effects" screen, code cleanup + more
Diffstat:
28 files changed, 1656 insertions(+), 127 deletions(-)
diff --git a/src/ft2_about.c b/src/ft2_about.c
@@ -8,6 +8,7 @@
#include "ft2_gfxdata.h"
#include "ft2_pattern_ed.h" // exitPatternEditorExtended()
#include "ft2_config.h"
+#include "ft2_random.h"
#define OLD_NUM_STARS 1000
#define NUM_STARS 1500
@@ -64,17 +65,6 @@ static oldMatrix_t oldStarMatrix;
static vector_t starPoints[NUM_STARS], starRotation;
static matrix_t starMatrix;
-// exact Turbo Pascal Random() implementation
-static int32_t Random(int32_t limit)
-{
- static uint32_t randSeed; // seed is 0 in Turbo Pascal unless Randomize() is called
-
- randSeed *= 134775813;
- randSeed++;
-
- return ((int64_t)randSeed * limit) >> 32;
-}
-
static uint32_t blendPixels(uint32_t pixelA, uint32_t pixelB, uint16_t alpha)
{
const uint16_t invAlpha = alpha ^ 0xFFFF;
@@ -333,7 +323,7 @@ void showAboutScreen(void) // called once when about screen is opened
{
oldVector_t *s = oldStarPoints;
- const int32_t type = Random(4);
+ const int32_t type = randoml(4);
switch (type)
{
// classic "space stars"
@@ -342,9 +332,9 @@ void showAboutScreen(void) // called once when about screen is opened
zSpeed = 309;
for (int32_t i = 0; i < OLD_NUM_STARS; i++, s++)
{
- s->z = (int16_t)Random(0xFFFF) - 0x8000;
- s->y = (int16_t)Random(0xFFFF) - 0x8000;
- s->x = (int16_t)Random(0xFFFF) - 0x8000;
+ s->z = (int16_t)randoml(0xFFFF) - 0x8000;
+ s->y = (int16_t)randoml(0xFFFF) - 0x8000;
+ s->x = (int16_t)randoml(0xFFFF) - 0x8000;
}
}
break;
@@ -357,17 +347,17 @@ void showAboutScreen(void) // called once when about screen is opened
{
if (i < OLD_NUM_STARS/4)
{
- s->z = (int16_t)Random(0xFFFF) - 0x8000;
- s->y = (int16_t)Random(0xFFFF) - 0x8000;
- s->x = (int16_t)Random(0xFFFF) - 0x8000;
+ s->z = (int16_t)randoml(0xFFFF) - 0x8000;
+ s->y = (int16_t)randoml(0xFFFF) - 0x8000;
+ s->x = (int16_t)randoml(0xFFFF) - 0x8000;
}
else
{
- int32_t r = Random(30000);
- int32_t n = Random(5);
- int32_t w = ((2 * Random(2)) - 1) * Sqr(Random(1000));
+ int32_t r = randoml(30000);
+ int32_t n = randoml(5);
+ int32_t w = ((2 * randoml(2)) - 1) * Sqr(randoml(1000));
double ww = (((PI * 2.0) / 5.0) * n) + (r * (1.0 / 12000.0)) + (w * (1.0 / 3000000.0));
- int32_t h = ((Sqr(r) / 30000) * (Random(10000) - 5000)) / 12000;
+ int32_t h = ((Sqr(r) / 30000) * (randoml(10000) - 5000)) / 12000;
s->x = (int16_t)(r * cos(ww));
s->y = (int16_t)(r * sin(ww));
@@ -384,8 +374,8 @@ void showAboutScreen(void) // called once when about screen is opened
zSpeed = 0;
for (int32_t i = 0; i < OLD_NUM_STARS; i++, s++)
{
- int32_t r = (int32_t)round(sqrt(Random(500) * 500));
- int32_t w = Random(3000);
+ int32_t r = (int32_t)round(sqrt(randoml(500) * 500));
+ int32_t w = randoml(3000);
double ww = ((w * 8) + r) * (1.0 / 16.0);
const int16_t z = (int16_t)round(32767.0 * cos(w * (2.0 * PI / 1024.0)));
@@ -421,9 +411,9 @@ void initAboutScreen(void)
vector_t *s = starPoints;
for (int32_t i = 0; i < NUM_STARS; i++, s++)
{
- s->x = (float)((Random(INT32_MAX) - (INT32_MAX/2)) * (1.0 / INT32_MAX));
- s->y = (float)((Random(INT32_MAX) - (INT32_MAX/2)) * (1.0 / INT32_MAX));
- s->z = (float)((Random(INT32_MAX) - (INT32_MAX/2)) * (1.0 / INT32_MAX));
+ s->x = (float)((randoml(INT32_MAX) - (INT32_MAX/2)) * (1.0 / INT32_MAX));
+ s->y = (float)((randoml(INT32_MAX) - (INT32_MAX/2)) * (1.0 / INT32_MAX));
+ s->z = (float)((randoml(INT32_MAX) - (INT32_MAX/2)) * (1.0 / INT32_MAX));
}
sinp1 = 0;
diff --git a/src/ft2_checkboxes.c b/src/ft2_checkboxes.c
@@ -15,6 +15,7 @@
#include "ft2_edit.h"
#include "ft2_bmp.h"
#include "ft2_wav_renderer.h"
+#include "ft2_smpfx.h"
#include "ft2_structs.h"
checkBox_t checkBoxes[NUM_CHECKBOXES] =
@@ -70,6 +71,10 @@ checkBox_t checkBoxes[NUM_CHECKBOXES] =
{ 3, 112, 148, 12, cbInstMidiEnable },
{ 172, 112, 103, 12, cbInstMuteComputer },
+ // ------ SAMPLE EDITOR EFFECTS CHECKBOXES ------
+ //x, y, w, h, funcOnUp
+ { 119, 384, 95, 12, cbSfxNormalization },
+
// ------ TRIM SCREEN CHECKBOXES ------
//x, y, w, h, funcOnUp
{ 3, 107, 113, 12, cbTrimUnusedPatt },
diff --git a/src/ft2_checkboxes.h b/src/ft2_checkboxes.h
@@ -42,6 +42,9 @@ enum // CHECKBOXES
CB_INST_EXT_MIDI,
CB_INST_EXT_MUTE,
+ // SAMPLE EDITOR EFFECTS
+ CB_SAMPFX_NORMALIZATION,
+
// TRIM
CB_TRIM_PATT,
CB_TRIM_INST,
diff --git a/src/ft2_edit.c b/src/ft2_edit.c
@@ -369,7 +369,7 @@ void recordNote(uint8_t noteNum, int8_t vol) // directly ported from the origina
time = INT32_MAX;
for (i = 0; i < song.numChannels; i++)
{
- if (editor.chnMode[i] && config.multiRecChn[i] && editor.keyOffTime[i] < time && editor.keyOnTab[i] == 0)
+ if (!editor.channelMuted[i] && config.multiRecChn[i] && editor.keyOffTime[i] < time && editor.keyOnTab[i] == 0)
{
c = i;
time = editor.keyOffTime[i];
diff --git a/src/ft2_header.h b/src/ft2_header.h
@@ -12,7 +12,7 @@
#endif
#include "ft2_replayer.h"
-#define PROG_VER_STR "1.94"
+#define PROG_VER_STR "1.95"
// do NOT change these! It will only mess things up...
diff --git a/src/ft2_main.c b/src/ft2_main.c
@@ -35,6 +35,7 @@
#include "ft2_bmp.h"
#include "ft2_structs.h"
#include "ft2_hpc.h"
+#include "ft2_smpfx.h"
static void initializeVars(void);
static void cleanUpAndExit(void); // never call this inside the main loop
@@ -362,6 +363,7 @@ static void cleanUpAndExit(void) // never call this inside the main loop!
freeSprites();
freeDiskOp();
clearCopyBuffer();
+ clearSampleUndo();
freeAudioDeviceSelectorBuffers();
windUpFTHelp();
freeTextBoxes();
diff --git a/src/ft2_midi.c b/src/ft2_midi.c
@@ -220,7 +220,7 @@ void recordMIDIEffect(uint8_t efx, uint8_t efxData)
note_t *p = &pattern[editor.editPattern][editor.row * MAX_CHANNELS];
for (int32_t i = 0; i < song.numChannels; i++, p++)
{
- if (config.multiRecChn[i] && editor.chnMode[i])
+ if (config.multiRecChn[i] && !editor.channelMuted[i])
{
if (!allocatePattern(editor.editPattern))
return;
diff --git a/src/ft2_module_loader.c b/src/ft2_module_loader.c
@@ -19,6 +19,7 @@
#include "ft2_gui.h"
#include "ft2_diskop.h"
#include "ft2_sample_loader.h"
+#include "ft2_smpfx.h"
#include "ft2_mouse.h"
#include "ft2_midi.h"
#include "ft2_events.h"
@@ -498,6 +499,7 @@ static void setupLoadedModule(void)
updateChanNums();
resetWavRenderer();
clearPattMark();
+ clearSampleUndo();
resetTrimSizes();
resetPlaybackTime();
diff --git a/src/ft2_pattern_draw.c b/src/ft2_pattern_draw.c
@@ -300,7 +300,7 @@ static void writePatternBlockMark(int32_t currRow, uint32_t rowHeight, const pat
static void drawChannelNumbering(uint16_t yPos)
{
uint16_t xPos = 30;
- int32_t ch = ui.channelOffset + 1;
+ uint8_t ch = ui.channelOffset + 1;
for (uint8_t i = 0; i < ui.numChannelsShown; i++)
{
@@ -310,8 +310,8 @@ static void drawChannelNumbering(uint16_t yPos)
}
else
{
- charOutOutlined(xPos, yPos, PAL_MOUSEPT, chDecTab1[ch]);
- charOutOutlined(xPos + (FONT1_CHAR_W + 1), yPos, PAL_MOUSEPT, chDecTab2[ch]);
+ charOutOutlined(xPos, yPos, PAL_MOUSEPT, '0' + (ch / 10));
+ charOutOutlined(xPos + (FONT1_CHAR_W + 1), yPos, PAL_MOUSEPT, '0' + (ch % 10));
}
ch++;
diff --git a/src/ft2_pattern_ed.c b/src/ft2_pattern_ed.c
@@ -613,6 +613,7 @@ void patternEditorExtended(void)
ui._instEditorShown = ui.instEditorShown;
ui._instEditorExtShown = ui.instEditorExtShown;
ui._sampleEditorExtShown = ui.sampleEditorExtShown;
+ ui._sampleEditorEffectsShown = ui.sampleEditorEffectsShown;
ui._patternEditorShown = ui.patternEditorShown;
ui._sampleEditorShown = ui.sampleEditorShown;
ui._advEditShown= ui.advEditShown;
@@ -703,6 +704,7 @@ void exitPatternEditorExtended(void)
ui.instEditorShown = ui._instEditorShown;
ui.instEditorExtShown = ui._instEditorExtShown;
ui.sampleEditorExtShown = ui._sampleEditorExtShown;
+ ui.sampleEditorEffectsShown = ui._sampleEditorEffectsShown;
ui.patternEditorShown = ui._patternEditorShown;
ui.sampleEditorShown = ui._sampleEditorShown;
ui.advEditShown = ui._advEditShown;
diff --git a/src/ft2_pushbuttons.c b/src/ft2_pushbuttons.c
@@ -27,6 +27,7 @@
#include "ft2_mouse.h"
#include "ft2_edit.h"
#include "ft2_sample_ed_features.h"
+#include "ft2_smpfx.h"
#include "ft2_palette.h"
#include "ft2_structs.h"
#include "ft2_bmp.h"
@@ -218,7 +219,7 @@ pushButton_t pushButtons[NUM_PUSHBUTTONS] =
{ 251, 382, 43, 16, 0, 0, "Paste", NULL, NULL, sampPaste },
{ 300, 348, 50, 16, 0, 0, "Crop", NULL, NULL, sampCrop },
{ 300, 365, 50, 16, 0, 0, "Volume", NULL, NULL, pbSampleVolume },
- { 300, 382, 50, 16, 0, 0, "X-Fade", NULL, NULL, sampXFade },
+ { 300, 382, 50, 16, 0, 0, "Effects", NULL, NULL, pbEffects },
{ 430, 348, 54, 16, 0, 0, "Exit", NULL, NULL, exitSampleEditor },
{ 594, 348, 35, 13, 0, 0, "Clr S.", NULL, NULL, clearSample },
{ 594, 360, 35, 13, 0, 0, "Min.", NULL, NULL, sampMinimize },
@@ -227,13 +228,34 @@ pushButton_t pushButtons[NUM_PUSHBUTTONS] =
{ 594, 385, 18, 13, 2, 4, ARROW_UP_STRING, NULL, sampReplenUp, NULL },
{ 611, 385, 18, 13, 2, 4, ARROW_DOWN_STRING, NULL, sampReplenDown, NULL },
+ // ------ SAMPLE EDITOR EFFECTS PUSHBUTTONS ------
+ //x, y, w, h, p, d, text #1, text #2, funcOnDown, funcOnUp
+ { 78, 350, 18, 13, 2, 2, ARROW_UP_STRING, NULL, pbSfxCyclesUp, NULL },
+ { 95, 350, 18, 13, 2, 2, ARROW_DOWN_STRING, NULL, pbSfxCyclesDown, NULL },
+ { 3, 365, 54, 16, 0, 0, "Triangle", NULL, NULL, pbSfxTriangle },
+ { 59, 365, 54, 16, 0, 0, "Saw", NULL, NULL, pbSfxSaw },
+ { 3, 382, 54, 16, 0, 0, "Sine", NULL, NULL, pbSfxSine },
+ { 59, 382, 54, 16, 0, 0, "Square", NULL, NULL, pbSfxSquare },
+ { 192, 350, 18, 13, 1, 2, ARROW_UP_STRING, NULL, pbSfxResoUp, NULL },
+ { 209, 350, 18, 13, 1, 2, ARROW_DOWN_STRING, NULL, pbSfxResoDown, NULL },
+ { 119, 365, 53, 16, 0, 0, "lp filter", NULL, NULL, pbSfxLowPass },
+ { 174, 365, 53, 16, 0, 0, "hp filter", NULL, NULL, pbSfxHighPass },
+ { 269, 350, 13, 13, 0, 0, "-", NULL, NULL, pbSfxSubBass },
+ { 281, 350, 13, 13, 0, 0, "+", NULL, NULL, pbSfxAddBass },
+ { 269, 367, 13, 13, 0, 0, "-", NULL, NULL, pbSfxSubTreble },
+ { 281, 367, 13, 13, 0, 0, "+", NULL, NULL, pbSfxAddTreble },
+ { 233, 382, 61, 16, 0, 0, "Amplitude", NULL, NULL, pbSfxSetAmp },
+ { 300, 348, 50, 16, 0, 0, "Undo", NULL, NULL, pbSfxUndo },
+ { 300, 365, 50, 16, 0, 0, "X-Fade", NULL, NULL, sampXFade },
+ { 300, 382, 50, 16, 0, 0, "Back...", NULL, NULL, hideSampleEffectsScreen },
+
// ------ SAMPLE EDITOR EXTENSION PUSHBUTTONS ------
//x, y, w, h, p, d, text #1, text #2, funcOnDown, funcOnUp
{ 3, 138, 52, 16, 0, 0, "Clr. c.bf", NULL, NULL, clearCopyBuffer },
{ 56, 138, 49, 16, 0, 0, "Sign", NULL, NULL, sampleChangeSign },
{ 106, 138, 49, 16, 0, 0, "Echo", NULL, NULL, pbSampleEcho },
{ 3, 155, 52, 16, 0, 0, "Backw.", NULL, NULL, sampleBackwards },
- { 56, 155, 49, 16, 0, 0, "B. swap", NULL, NULL, sampleByteSwap },
+ { 56, 155, 49, 16, 0, 0, "B. swap", NULL, NULL, sampleByteSwap },
{ 106, 155, 49, 16, 0, 0, "Fix DC", NULL, NULL, fixDC },
{ 161, 121, 60, 16, 0, 0, "Copy ins.", NULL, NULL, copyInstr },
{ 222, 121, 66, 16, 0, 0, "Copy smp.", NULL, NULL, copySmp },
diff --git a/src/ft2_pushbuttons.h b/src/ft2_pushbuttons.h
@@ -168,7 +168,7 @@ enum // PUSHBUTTONS
PB_SAMP_PASTE,
PB_SAMP_CROP,
PB_SAMP_VOLUME,
- PB_SAMP_XFADE,
+ PB_SAMP_EFFECTS,
PB_SAMP_EXIT,
PB_SAMP_CLEAR,
PB_SAMP_MIN,
@@ -177,6 +177,26 @@ enum // PUSHBUTTONS
PB_SAMP_REPLEN_UP,
PB_SAMP_REPLEN_DOWN,
+ // SAMPLE EDITOR EFFECTS SCREEN
+ PB_SAMPFX_CYCLES_UP,
+ PB_SAMPFX_CYCLES_DOWN,
+ PB_SAMPFX_TRIANGLE,
+ PB_SAMPFX_SAW,
+ PB_SAMPFX_SINE,
+ PB_SAMPFX_SQUARE,
+ PB_SAMPFX_RESO_UP,
+ PB_SAMPFX_RESO_DOWN,
+ PB_SAMPFX_LOWPASS,
+ PB_SAMPFX_HIGHPASS,
+ PB_SAMPFX_SUB_BASS,
+ PB_SAMPFX_SUB_TREBLE,
+ PB_SAMPFX_ADD_BASS,
+ PB_SAMPFX_ADD_TREBLE,
+ PB_SAMPFX_SET_AMP,
+ PB_SAMPFX_UNDO,
+ PB_SAMPFX_XFADE,
+ PB_SAMPFX_BACK,
+
// SAMPLE EDITOR EXTENSION
PB_SAMP_EXT_CLEAR_COPYBUF,
PB_SAMP_EXT_CONV,
diff --git a/src/ft2_random.c b/src/ft2_random.c
@@ -0,0 +1,25 @@
+#include <SDL2/SDL.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+static uint32_t randSeed;
+
+void randomize(void)
+{
+ randSeed = (uint32_t)SDL_GetTicks();
+}
+
+int32_t randoml(int32_t limit)
+{
+ randSeed *= 134775813;
+ randSeed++;
+ return ((int64_t)randSeed * (int32_t)limit) >> 32;
+}
+
+int32_t random32(void)
+{
+ randSeed *= 134775813;
+ randSeed++;
+
+ return randSeed;
+}
diff --git a/src/ft2_random.h b/src/ft2_random.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include <stdint.h>
+
+void randomize(void);
+int32_t randoml(int32_t limit);
+int32_t random32(void);
diff --git a/src/ft2_replayer.c b/src/ft2_replayer.c
@@ -42,7 +42,7 @@ typedef void (*efxRoutine)(channel_t *ch, uint8_t param);
int8_t playMode = 0;
bool songPlaying = false, audioPaused = false, musicPaused = false;
volatile bool replayerBusy = false;
-const uint16_t *note2Period = NULL;
+const uint16_t *note2PeriodLUT = NULL;
int16_t patternNumRows[MAX_PATTERNS];
channel_t channel[MAX_CHANNELS];
song_t song;
@@ -120,7 +120,7 @@ void resetChannels(void)
ch->outPan = 128;
ch->finalPan = 128;
- ch->channelOff = !editor.chnMode[i]; // set channel mute flag from global mute flag
+ ch->channelOff = editor.channelMuted[i]; // set channel mute flag from global mute flag
}
if (audioWasntLocked)
@@ -274,7 +274,7 @@ double getSampleC4Rate(sample_t *s)
const int32_t C4Period = (note << 4) + (((int8_t)s->finetune >> 3) + 16);
- const int32_t period = audio.linearPeriodsFlag ? linearPeriods[C4Period] : amigaPeriods[C4Period];
+ const int32_t period = audio.linearPeriodsFlag ? linearPeriodLUT[C4Period] : amigaPeriodLUT[C4Period];
return dPeriod2Hz(period);
}
@@ -285,9 +285,9 @@ void setLinearPeriods(bool linearPeriodsFlag)
audio.linearPeriodsFlag = linearPeriodsFlag;
if (audio.linearPeriodsFlag)
- note2Period = linearPeriods;
+ note2PeriodLUT = linearPeriodLUT;
else
- note2Period = amigaPeriods;
+ note2PeriodLUT = amigaPeriodLUT;
resumeAudio();
@@ -302,7 +302,7 @@ void setLinearPeriods(bool linearPeriodsFlag)
drawC4Rate();
}
-static void resetVolumes(channel_t *ch)
+void resetVolumes(channel_t *ch)
{
ch->realVol = ch->oldVol;
ch->outVol = ch->oldVol;
@@ -311,7 +311,7 @@ static void resetVolumes(channel_t *ch)
ch->status |= IS_Vol + IS_Pan + IS_QuickVol;
}
-static void triggerInstrument(channel_t *ch)
+void triggerInstrument(channel_t *ch)
{
if (!(ch->vibTremCtrl & 0x04)) ch->vibratoPos = 0;
if (!(ch->vibTremCtrl & 0x40)) ch->tremoloPos = 0;
@@ -430,8 +430,8 @@ void calcReplayerVars(int32_t audioFreq)
// for piano in Instr. Ed. (values outside 0..95 can happen)
int32_t getPianoKey(uint16_t period, int8_t finetune, int8_t relativeNote)
{
- assert(note2Period != NULL);
- if (period > note2Period[0])
+ assert(note2PeriodLUT != NULL);
+ if (period > note2PeriodLUT[0])
return -1; // outside left piano edge
finetune = ((int8_t)finetune >> 3) + 16; // -128..127 -> 0..31
@@ -447,7 +447,7 @@ int32_t getPianoKey(uint16_t period, int8_t finetune, int8_t relativeNote)
if (lookUp < 0)
lookUp = 0;
- if (period >= note2Period[lookUp])
+ if (period >= note2PeriodLUT[lookUp])
hiPeriod = (tmpPeriod - finetune) & ~15;
else
loPeriod = (tmpPeriod - finetune) & ~15;
@@ -456,7 +456,7 @@ int32_t getPianoKey(uint16_t period, int8_t finetune, int8_t relativeNote)
return (loPeriod >> 4) - relativeNote;
}
-static void triggerNote(uint8_t note, uint8_t efx, uint8_t efxData, channel_t *ch)
+void triggerNote(uint8_t note, uint8_t efx, uint8_t efxData, channel_t *ch)
{
if (note == NOTE_OFF)
{
@@ -507,8 +507,8 @@ static void triggerNote(uint8_t note, uint8_t efx, uint8_t efxData, channel_t *c
{
const uint16_t noteIndex = ((note-1) * 16) + (((int8_t)ch->finetune >> 3) + 16); // 0..1920
- assert(note2Period != NULL);
- ch->outPeriod = ch->realPeriod = note2Period[noteIndex];
+ assert(note2PeriodLUT != NULL);
+ ch->outPeriod = ch->realPeriod = note2PeriodLUT[noteIndex];
}
ch->status |= IS_Period + IS_Vol + IS_Pan + IS_Trigger + IS_QuickVol;
@@ -1237,8 +1237,8 @@ static void preparePortamento(channel_t *ch, const note_t *p, uint8_t inst)
const uint16_t note = (((p->note-1) + ch->relativeNote) * 16) + (((int8_t)ch->finetune >> 3) + 16);
if (note < MAX_NOTES)
{
- assert(note2Period != NULL);
- ch->portamentoTargetPeriod = note2Period[note];
+ assert(note2PeriodLUT != NULL);
+ ch->portamentoTargetPeriod = note2PeriodLUT[note];
if (ch->portamentoTargetPeriod == ch->realPeriod)
ch->portamentoDirection = 0;
@@ -1366,7 +1366,7 @@ static void getNewNote(channel_t *ch, const note_t *p)
handleEffects_TickZero(ch);
}
-static void updateVolPanAutoVib(channel_t *ch)
+void updateVolPanAutoVib(channel_t *ch)
{
bool envInterpolateFlag, envDidInterpolate;
uint8_t envPos;
@@ -1708,7 +1708,7 @@ static uint16_t adjustPeriodFromNote(uint16_t period, uint8_t arpNote, channel_t
if (lookUp < 0)
lookUp = 0; // safety fix (C-0 w/ f.tune <= -65). This seems to result in 0 in FT2 (TODO: verify)
- if (period >= note2Period[lookUp])
+ if (period >= note2PeriodLUT[lookUp])
hiPeriod = (tmpPeriod - fineTune) & ~15;
else
loPeriod = (tmpPeriod - fineTune) & ~15;
@@ -1718,7 +1718,7 @@ static uint16_t adjustPeriodFromNote(uint16_t period, uint8_t arpNote, channel_t
if (tmpPeriod >= (8*12*16+15)-1) // FT2 bug, should've been 10*12*16+16 (also notice the +2 difference)
tmpPeriod = (8*12*16+16)-1;
- return note2Period[tmpPeriod];
+ return note2PeriodLUT[tmpPeriod];
}
static void doVibrato(channel_t *ch)
@@ -2804,7 +2804,7 @@ bool setupReplayer(void)
// unmute all channels (must be done before resetChannels() call)
for (int32_t i = 0; i < MAX_CHANNELS; i++)
- editor.chnMode[i] = 1;
+ editor.channelMuted[i] = false;
resetChannels();
@@ -2814,7 +2814,7 @@ bool setupReplayer(void)
editor.speed = song.initialSpeed = song.speed = 6;
editor.globalVolume = song.globalVolume = 64;
audio.linearPeriodsFlag = true;
- note2Period = linearPeriods;
+ note2PeriodLUT = linearPeriodLUT;
calcPanningTable();
diff --git a/src/ft2_replayer.h b/src/ft2_replayer.h
@@ -295,6 +295,8 @@ double dAmigaPeriod2Hz(int32_t period);
double dPeriod2Hz(int32_t period);
int32_t getPianoKey(uint16_t period, int8_t finetune, int8_t relativeNote); // for piano in Instr. Ed.
+void triggerNote(uint8_t note, uint8_t efx, uint8_t efxData, channel_t *ch);
+void updateVolPanAutoVib(channel_t *ch);
bool allocateInstr(int16_t insNum);
void freeInstr(int32_t insNum);
@@ -324,6 +326,8 @@ void delta2Samp(int8_t *p, int32_t length, uint8_t smpFlags);
void samp2Delta(int8_t *p, int32_t length, uint8_t smpFlags);
void setPatternLen(uint16_t pattNum, int16_t numRows);
void setLinearPeriods(bool linearPeriodsFlag);
+void resetVolumes(channel_t *ch);
+void triggerInstrument(channel_t *ch);
void tickReplayer(void); // periodically called from audio callback
void resetChannels(void);
bool patternEmpty(uint16_t pattNum);
@@ -347,7 +351,7 @@ void pbRecPtn(void);
extern int8_t playMode;
extern bool songPlaying, audioPaused, musicPaused;
extern volatile bool replayerBusy;
-extern const uint16_t *note2Period;
+extern const uint16_t *note2PeriodLUT;
extern int16_t patternNumRows[MAX_PATTERNS];
extern channel_t channel[MAX_CHANNELS];
extern song_t song;
diff --git a/src/ft2_sample_ed.c b/src/ft2_sample_ed.c
@@ -27,7 +27,9 @@
#include "ft2_diskop.h"
#include "ft2_keyboard.h"
#include "ft2_structs.h"
+#include "ft2_random.h"
#include "ft2_replayer.h"
+#include "ft2_smpfx.h"
#include "mixer/ft2_windowed_sinc.h" // SINC_TAPS, SINC_NEGATIVE_TAPS
static const char sharpNote1Char[12] = { 'C', 'C', 'D', 'D', 'E', 'F', 'F', 'G', 'G', 'A', 'A', 'B' };
@@ -1436,8 +1438,7 @@ static void setSampleRange(int32_t start, int32_t end)
void updateSampleEditorSample(void)
{
- smpEd_Rx1 = 0;
- smpEd_Rx2 = 0;
+ smpEd_Rx1 = smpEd_Rx2 = 0;
smpEd_ScrPos = 0;
updateScrPos();
@@ -1493,27 +1494,30 @@ void updateSampleEditor(void)
showRadioButtonGroup(RB_GROUP_SAMPLE_LOOP);
- // draw sample play note
+ if (!ui.sampleEditorEffectsShown)
+ {
+ // draw sample play note
- const uint32_t noteNr = editor.smpEd_NoteNr - 1;
+ const uint32_t noteNr = editor.smpEd_NoteNr - 1;
- const uint32_t note = noteNr % 12;
- const uint32_t octave = noteNr / 12;
+ const uint32_t note = noteNr % 12;
+ const uint32_t octave = noteNr / 12;
- if (config.ptnAcc == 0)
- {
- noteChar1 = sharpNote1Char[note];
- noteChar2 = sharpNote2Char[note];
- }
- else
- {
- noteChar1 = flatNote1Char[note];
- noteChar2 = flatNote2Char[note];
- }
+ if (config.ptnAcc == 0)
+ {
+ noteChar1 = sharpNote1Char[note];
+ noteChar2 = sharpNote2Char[note];
+ }
+ else
+ {
+ noteChar1 = flatNote1Char[note];
+ noteChar2 = flatNote2Char[note];
+ }
- charOutBg(7, 369, PAL_FORGRND, PAL_BCKGRND, noteChar1);
- charOutBg(15, 369, PAL_FORGRND, PAL_BCKGRND, noteChar2);
- charOutBg(23, 369, PAL_FORGRND, PAL_BCKGRND, (char)('0' + octave));
+ charOutBg(7, 369, PAL_FORGRND, PAL_BCKGRND, noteChar1);
+ charOutBg(15, 369, PAL_FORGRND, PAL_BCKGRND, noteChar2);
+ charOutBg(23, 369, PAL_FORGRND, PAL_BCKGRND, (char)('0' + octave));
+ }
// draw sample display/length
@@ -2898,6 +2902,8 @@ void sampReplenDown(void)
void hideSampleEditor(void)
{
+ hideSampleEffectsScreen();
+
hidePushButton(PB_SAMP_SCROLL_LEFT);
hidePushButton(PB_SAMP_SCROLL_RIGHT);
hidePushButton(PB_SAMP_PNOTE_UP);
@@ -2917,7 +2923,7 @@ void hideSampleEditor(void)
hidePushButton(PB_SAMP_PASTE);
hidePushButton(PB_SAMP_CROP);
hidePushButton(PB_SAMP_VOLUME);
- hidePushButton(PB_SAMP_XFADE);
+ hidePushButton(PB_SAMP_EFFECTS);
hidePushButton(PB_SAMP_EXIT);
hidePushButton(PB_SAMP_CLEAR);
hidePushButton(PB_SAMP_MIN);
@@ -2995,7 +3001,7 @@ void showSampleEditor(void)
showPushButton(PB_SAMP_PASTE);
showPushButton(PB_SAMP_CROP);
showPushButton(PB_SAMP_VOLUME);
- showPushButton(PB_SAMP_XFADE);
+ showPushButton(PB_SAMP_EFFECTS);
showPushButton(PB_SAMP_EXIT);
showPushButton(PB_SAMP_CLEAR);
showPushButton(PB_SAMP_MIN);
@@ -3015,6 +3021,9 @@ void showSampleEditor(void)
updateSampleEditor();
writeSample(true);
+
+ if (ui.sampleEditorEffectsShown)
+ pbEffects();
}
void toggleSampleEditor(void)
diff --git a/src/ft2_sample_ed.h b/src/ft2_sample_ed.h
@@ -18,7 +18,6 @@ bool reallocateSmpDataPtr(smpPtr_t *sp, int32_t length, bool sample16Bit);
void setSmpDataPtr(sample_t *s, smpPtr_t *sp);
void freeSmpDataPtr(smpPtr_t *sp);
void freeSmpData(sample_t *s);
-
bool cloneSample(sample_t *src, sample_t *dst);
sample_t *getCurSample(void);
void sanitizeSample(sample_t *s);
diff --git a/src/ft2_smpfx.c b/src/ft2_smpfx.c
@@ -0,0 +1,1389 @@
+// 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 "ft2_header.h"
+#include "ft2_audio.h"
+#include "ft2_pattern_ed.h"
+#include "ft2_gui.h"
+#include "ft2_sample_ed.h"
+#include "ft2_structs.h"
+#include "ft2_replayer.h"
+
+#define RESONANCE_RANGE 99
+#define RESONANCE_MIN 0.01 /* prevent massive blow-up */
+
+enum
+{
+ REMOVE_SAMPLE_MARK = 0,
+ KEEP_SAMPLE_MARK = 1
+};
+
+static struct
+{
+ bool filled, keepSampleMark;
+ uint8_t flags, undoInstr, undoSmp;
+ uint32_t length, loopStart, loopLength;
+ int8_t *smpData8;
+ int16_t *smpData16;
+} sampleUndo;
+
+typedef struct
+{
+ double a1, a2, a3, b1, b2;
+ double inTmp[2], outTmp[2];
+} resoFilter_t;
+
+enum
+{
+ FILTER_LOWPASS = 0,
+ FILTER_HIGHPASS = 1
+};
+
+static bool normalization;
+static uint8_t lastFilterType;
+static int32_t lastLpCutoff = 2000, lastHpCutoff = 200, filterResonance, smpCycles = 1, lastWaveLength = 64, lastAmp = 75;
+
+void clearSampleUndo(void)
+{
+ if (sampleUndo.smpData8 != NULL)
+ {
+ free(sampleUndo.smpData8);
+ sampleUndo.smpData8 = NULL;
+ }
+
+ if (sampleUndo.smpData16 != NULL)
+ {
+ free(sampleUndo.smpData16);
+ sampleUndo.smpData16 = NULL;
+ }
+
+ sampleUndo.filled = false;
+ sampleUndo.keepSampleMark = false;
+}
+
+static void fillSampleUndo(bool keepSampleMark)
+{
+ sampleUndo.filled = false;
+
+ sample_t *s = getCurSample();
+ if (s != NULL && s->length > 0)
+ {
+ pauseAudio();
+ unfixSample(s);
+
+ clearSampleUndo();
+
+ sampleUndo.undoInstr = editor.curInstr;
+ sampleUndo.undoSmp = editor.curSmp;
+ sampleUndo.flags = s->flags;
+ sampleUndo.length = s->length;
+ sampleUndo.loopStart = s->loopStart;
+ sampleUndo.loopLength = s->loopLength;
+ sampleUndo.keepSampleMark = keepSampleMark;
+
+ if (s->flags & SAMPLE_16BIT)
+ {
+ sampleUndo.smpData16 = (int16_t *)malloc(s->length * sizeof (int16_t));
+ if (sampleUndo.smpData16 != NULL)
+ {
+ memcpy(sampleUndo.smpData16, s->dataPtr, s->length * sizeof (int16_t));
+ sampleUndo.filled = true;
+ }
+ }
+ else
+ {
+ sampleUndo.smpData8 = (int8_t *)malloc(s->length * sizeof (int8_t));
+ if (sampleUndo.smpData8 != NULL)
+ {
+ memcpy(sampleUndo.smpData8, s->dataPtr, s->length * sizeof (int8_t));
+ sampleUndo.filled = true;
+ }
+ }
+
+ fixSample(s);
+ resumeAudio();
+ }
+}
+
+static sample_t *setupNewSample(uint32_t length)
+{
+ pauseAudio();
+
+ if (instr[editor.curInstr] == NULL)
+ allocateInstr(editor.curInstr);
+
+ if (instr[editor.curInstr] == NULL)
+ goto Error;
+
+ sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp];
+
+ if (!reallocateSmpData(s, length, true))
+ goto Error;
+
+ s->isFixed = false;
+ s->length = length;
+ s->loopLength = s->loopStart = 0;
+ s->flags = SAMPLE_16BIT;
+
+ resumeAudio();
+ return s;
+
+Error:
+ resumeAudio();
+ return NULL;
+}
+
+void cbSfxNormalization(void)
+{
+ normalization ^= 1;
+}
+
+static void drawSampleCycles(void)
+{
+ const int16_t x = 54;
+
+ fillRect(x, 352, 7*3, 8, PAL_DESKTOP);
+
+ char str[16];
+ sprintf(str, "%03d", smpCycles);
+ textOut(x, 352, PAL_FORGRND, str);
+}
+
+void pbSfxCyclesUp(void)
+{
+ if (smpCycles < 256)
+ {
+ smpCycles++;
+ drawSampleCycles();
+ }
+}
+
+void pbSfxCyclesDown(void)
+{
+ if (smpCycles > 1)
+ {
+ smpCycles--;
+ drawSampleCycles();
+ }
+}
+
+void pbSfxTriangle(void)
+{
+ char lengthStr[5+1];
+ memset(lengthStr, '\0', sizeof (lengthStr));
+ snprintf(lengthStr, sizeof (lengthStr), "%d", lastWaveLength);
+
+ if (inputBox(1, "Enter new waveform length:", lengthStr, sizeof (lengthStr)-1) != 1)
+ return;
+
+ if (lengthStr[0] == '\0')
+ return;
+
+ lastWaveLength = (int32_t)atoi(lengthStr);
+ if (lastWaveLength <= 1 || lastWaveLength > 65536)
+ {
+ okBox(0, "System message", "Illegal range! Allowed range is 2..65535", NULL);
+ return;
+ }
+
+ fillSampleUndo(REMOVE_SAMPLE_MARK);
+
+ int32_t newLength = lastWaveLength * smpCycles;
+
+ pauseAudio();
+
+ sample_t *s = setupNewSample(newLength);
+ if (s == NULL)
+ {
+ resumeAudio();
+ okBox(0, "System message", "Not enough memory!", NULL);
+ return;
+ }
+
+ const double delta = 4.0 / lastWaveLength;
+ double phase = 0.0;
+
+ int16_t *ptr16 = (int16_t *)s->dataPtr;
+ for (int32_t i = 0; i < newLength; i++)
+ {
+ double t = phase;
+ if (t > 3.0)
+ t -= 4.0;
+ else if (t >= 1.0)
+ t = 2.0 - t;
+
+ *ptr16++ = (int16_t)(t * INT16_MAX);
+ phase = fmod(phase + delta, 4.0);
+ }
+
+ s->loopLength = newLength;
+ s->flags |= LOOP_FORWARD;
+ fixSample(s);
+ resumeAudio();
+
+ updateSampleEditorSample();
+}
+
+void pbSfxSaw(void)
+{
+ char lengthStr[5+1];
+ memset(lengthStr, '\0', sizeof (lengthStr));
+ snprintf(lengthStr, sizeof (lengthStr), "%d", lastWaveLength);
+
+ if (inputBox(1, "Enter new waveform length:", lengthStr, sizeof (lengthStr)-1) != 1)
+ return;
+
+ if (lengthStr[0] == '\0')
+ return;
+
+ lastWaveLength = (int32_t)atoi(lengthStr);
+ if (lastWaveLength <= 1 || lastWaveLength > 65536)
+ {
+ okBox(0, "System message", "Illegal range! Allowed range is 2..65535", NULL);
+ return;
+ }
+
+ fillSampleUndo(REMOVE_SAMPLE_MARK);
+
+ int32_t newLength = lastWaveLength * smpCycles;
+
+ pauseAudio();
+
+ sample_t *s = setupNewSample(newLength);
+ if (s == NULL)
+ {
+ resumeAudio();
+ okBox(0, "System message", "Not enough memory!", NULL);
+ return;
+ }
+
+ uint64_t point64 = 0;
+ uint64_t delta64 = ((uint64_t)(INT16_MAX*2) << 32ULL) / lastWaveLength;
+
+ int16_t *ptr16 = (int16_t *)s->dataPtr;
+ for (int32_t i = 0; i < newLength; i++)
+ {
+ *ptr16++ = (int16_t)(point64 >> 32);
+ point64 += delta64;
+ }
+
+ s->loopLength = newLength;
+ s->flags |= LOOP_FORWARD;
+ fixSample(s);
+ resumeAudio();
+
+ updateSampleEditorSample();
+}
+
+void pbSfxSine(void)
+{
+ char lengthStr[5+1];
+ memset(lengthStr, '\0', sizeof (lengthStr));
+ snprintf(lengthStr, sizeof (lengthStr), "%d", lastWaveLength);
+
+ if (inputBox(1, "Enter new waveform length:", lengthStr, sizeof (lengthStr)-1) != 1)
+ return;
+
+ if (lengthStr[0] == '\0')
+ return;
+
+ lastWaveLength = (int32_t)atoi(lengthStr);
+ if (lastWaveLength <= 1 || lastWaveLength > 65536)
+ {
+ okBox(0, "System message", "Illegal range! Allowed range is 2..65535", NULL);
+ return;
+ }
+
+ fillSampleUndo(REMOVE_SAMPLE_MARK);
+
+ int32_t newLength = lastWaveLength * smpCycles;
+
+ pauseAudio();
+
+ sample_t *s = setupNewSample(newLength);
+ if (s == NULL)
+ {
+ resumeAudio();
+ okBox(0, "System message", "Not enough memory!", NULL);
+ return;
+ }
+
+ const double delta = 2.0 * M_PI / lastWaveLength;
+ double phase = 0.0;
+
+ int16_t *ptr16 = (int16_t *)s->dataPtr;
+ for (int32_t i = 0; i < newLength; i++)
+ {
+ *ptr16++ = (int16_t)(INT16_MAX * sin(phase));
+ phase += delta;
+ }
+
+ s->loopLength = newLength;
+ s->flags |= LOOP_FORWARD;
+ fixSample(s);
+ resumeAudio();
+
+ updateSampleEditorSample();
+}
+
+void pbSfxSquare(void)
+{
+ char lengthStr[5+1];
+ memset(lengthStr, '\0', sizeof (lengthStr));
+ snprintf(lengthStr, sizeof (lengthStr), "%d", lastWaveLength);
+
+ if (inputBox(1, "Enter new waveform length:", lengthStr, sizeof (lengthStr)-1) != 1)
+ return;
+
+ if (lengthStr[0] == '\0')
+ return;
+
+ lastWaveLength = (int32_t)atoi(lengthStr);
+ if (lastWaveLength <= 1 || lastWaveLength > 65536)
+ {
+ okBox(0, "System message", "Illegal range! Allowed range is 2..65535", NULL);
+ return;
+ }
+
+ fillSampleUndo(REMOVE_SAMPLE_MARK);
+
+ uint32_t newLength = lastWaveLength * smpCycles;
+
+ pauseAudio();
+
+ sample_t *s = setupNewSample(newLength);
+ if (s == NULL)
+ {
+ resumeAudio();
+ okBox(0, "System message", "Not enough memory!", NULL);
+ return;
+ }
+
+ const uint32_t halfWaveLength = lastWaveLength / 2;
+
+ int16_t currValue = INT16_MAX;
+ uint32_t counter = 0;
+
+ int16_t *ptr16 = (int16_t *)s->dataPtr;
+ for (uint32_t i = 0; i < newLength; i++)
+ {
+ *ptr16++ = currValue;
+ if (++counter >= halfWaveLength)
+ {
+ counter = 0;
+ currValue = -currValue;
+ }
+ }
+
+ s->loopLength = newLength;
+ s->flags |= LOOP_FORWARD;
+ fixSample(s);
+ resumeAudio();
+
+ updateSampleEditorSample();
+}
+
+void drawFilterResonance(void)
+{
+ const int16_t x = 172;
+
+ fillRect(x, 352, 18, 12, PAL_DESKTOP);
+
+ if (filterResonance <= 0)
+ {
+ textOut(x, 352, PAL_FORGRND, "off");
+ }
+ else
+ {
+ char str[16];
+ sprintf(str, "%02d", filterResonance);
+ textOut(x+3, 352, PAL_FORGRND, str);
+ }
+}
+
+void pbSfxResoUp(void)
+{
+ if (filterResonance < RESONANCE_RANGE)
+ {
+ filterResonance++;
+ drawFilterResonance();
+ }
+}
+
+void pbSfxResoDown(void)
+{
+ if (filterResonance > 0)
+ {
+ filterResonance--;
+ drawFilterResonance();
+ }
+}
+
+#define CUTOFF_EPSILON (1E-4)
+
+static void setupResoLpFilter(sample_t *s, resoFilter_t *f, double cutoff, uint32_t resonance, bool absoluteCutoff)
+{
+ // 12dB/oct resonant low-pass filter
+
+ if (!absoluteCutoff)
+ {
+ const double sampleFreq = getSampleC4Rate(s);
+ if (cutoff >= sampleFreq/2.0)
+ cutoff = (sampleFreq/2.0) - CUTOFF_EPSILON;
+
+ cutoff /= sampleFreq;
+ }
+
+ double r = sqrt(2.0);
+ if (resonance > 0)
+ {
+ r = pow(10.0, (resonance * -24.0) / (RESONANCE_RANGE * 20.0));
+ if (r < RESONANCE_MIN)
+ r = RESONANCE_MIN;
+ }
+
+ const double c = 1.0 / tan(PI * cutoff);
+
+ f->a1 = 1.0 / (1.0 + r * c + c * c);
+ f->a2 = 2.0 * f->a1;
+ f->a3 = f->a1;
+ f->b1 = 2.0 * (1.0 - c*c) * f->a1;
+ f->b2 = (1.0 - r * c + c * c) * f->a1;
+
+ f->inTmp[0] = f->inTmp[1] = f->outTmp[0] = f->outTmp[1] = 0.0; // clear filter history
+}
+
+static void setupResoHpFilter(sample_t *s, resoFilter_t *f, double cutoff, uint32_t resonance, bool absoluteCutoff)
+{
+ // 12dB/oct resonant high-pass filter
+
+ if (!absoluteCutoff)
+ {
+ const double sampleFreq = getSampleC4Rate(s);
+ if (cutoff >= sampleFreq/2.0)
+ cutoff = (sampleFreq/2.0) - CUTOFF_EPSILON;
+
+ cutoff /= sampleFreq;
+ }
+
+ double r = sqrt(2.0);
+ if (resonance > 0)
+ {
+ r = pow(10.0, (resonance * -24.0) / (RESONANCE_RANGE * 20.0));
+ if (r < RESONANCE_MIN)
+ r = RESONANCE_MIN;
+ }
+
+ const double c = tan(PI * cutoff);
+
+ f->a1 = 1.0 / (1.0 + r * c + c * c);
+ f->a2 = -2.0 * f->a1;
+ f->a3 = f->a1;
+ f->b1 = 2.0 * (c*c - 1.0) * f->a1;
+ f->b2 = (1.0 - r * c + c * c) * f->a1;
+
+ f->inTmp[0] = f->inTmp[1] = f->outTmp[0] = f->outTmp[1] = 0.0; // clear filter history
+}
+
+static bool applyResoFilter(sample_t *s, resoFilter_t *f)
+{
+ int32_t x1, x2;
+ if (smpEd_Rx1 < smpEd_Rx2)
+ {
+ x1 = smpEd_Rx1;
+ x2 = smpEd_Rx2;
+
+ if (x2 > s->length)
+ x2 = s->length;
+
+ if (x1 < 0)
+ x1 = 0;
+
+ if (x2 <= x1)
+ return true;
+ }
+ else
+ {
+ // no mark, operate on whole sample
+ x1 = 0;
+ x2 = s->length;
+ }
+
+ const int32_t len = x2 - x1;
+
+ if (!normalization)
+ {
+ pauseAudio();
+ unfixSample(s);
+
+ if (s->flags & SAMPLE_16BIT)
+ {
+ int16_t *ptr16 = (int16_t *)s->dataPtr + x1;
+ for (int32_t i = 0; i < len; i++)
+ {
+ double out = (f->a1*ptr16[i]) + (f->a2*f->inTmp[0]) + (f->a3*f->inTmp[1]) - (f->b1*f->outTmp[0]) - (f->b2*f->outTmp[1]);
+
+ f->inTmp[1] = f->inTmp[0];
+ f->inTmp[0] = ptr16[i];
+
+ f->outTmp[1] = f->outTmp[0];
+ f->outTmp[0] = out;
+
+ ptr16[i] = (int16_t)CLAMP(out, INT16_MIN, INT16_MAX);
+ }
+ }
+ else
+ {
+ int8_t *ptr8 = (int8_t *)s->dataPtr + x1;
+ for (int32_t i = 0; i < len; i++)
+ {
+ double out = (f->a1*ptr8[i]) + (f->a2*f->inTmp[0]) + (f->a3*f->inTmp[1]) - (f->b1*f->outTmp[0]) - (f->b2*f->outTmp[1]);
+
+ f->inTmp[1] = f->inTmp[0];
+ f->inTmp[0] = ptr8[i];
+
+ f->outTmp[1] = f->outTmp[0];
+ f->outTmp[0] = out;
+
+ ptr8[i] = (int8_t)CLAMP(out, INT8_MIN, INT8_MAX);
+ }
+ }
+
+ fixSample(s);
+ resumeAudio();
+ }
+ else // normalize peak, no clipping
+ {
+ double *dSmp = (double *)malloc(len * sizeof (double));
+ if (dSmp == NULL)
+ {
+ okBox(0, "System message", "Not enough memory!", NULL);
+ return false;
+ }
+
+ pauseAudio();
+ unfixSample(s);
+
+ if (s->flags & SAMPLE_16BIT)
+ {
+ int16_t *ptr16 = (int16_t *)s->dataPtr + x1;
+ for (int32_t i = 0; i < len; i++)
+ dSmp[i] = (double)ptr16[i];
+ }
+ else
+ {
+ int8_t *ptr8 = (int8_t *)s->dataPtr + x1;
+ for (int32_t i = 0; i < len; i++)
+ dSmp[i] = (double)ptr8[i];
+ }
+
+ double peak = 0.0;
+ for (int32_t i = 0; i < len; i++)
+ {
+ const double out = (f->a1*dSmp[i]) + (f->a2*f->inTmp[0]) + (f->a3*f->inTmp[1]) - (f->b1*f->outTmp[0]) - (f->b2*f->outTmp[1]);
+
+ f->inTmp[1] = f->inTmp[0];
+ f->inTmp[0] = dSmp[i];
+
+ f->outTmp[1] = f->outTmp[0];
+ f->outTmp[0] = out;
+
+ dSmp[i] = out;
+
+ const double outAbs = fabs(out);
+ if (outAbs > peak)
+ peak = outAbs;
+ }
+
+ if (s->flags & SAMPLE_16BIT)
+ {
+ const double scale = INT16_MAX / peak;
+
+ int16_t *ptr16 = (int16_t *)s->dataPtr + x1;
+ for (int32_t i = 0; i < len; i++)
+ ptr16[i] = (int16_t)(dSmp[i] * scale);
+ }
+ else
+ {
+ const double scale = INT8_MAX / peak;
+
+ int8_t *ptr8 = (int8_t *)s->dataPtr + x1;
+ for (int32_t i = 0; i < len; i++)
+ ptr8[i] = (int8_t)(dSmp[i] * scale);
+ }
+
+ free(dSmp);
+
+ fixSample(s);
+ resumeAudio();
+ }
+
+ return true;
+}
+
+void pbSfxLowPass(void)
+{
+ resoFilter_t f;
+
+ sample_t *s = getCurSample();
+ if (s == NULL || s->dataPtr == NULL)
+ return;
+
+ char lengthStr[5+1];
+ memset(lengthStr, '\0', sizeof (lengthStr));
+ snprintf(lengthStr, sizeof (lengthStr), "%d", lastLpCutoff);
+
+ lastFilterType = FILTER_LOWPASS;
+ if (inputBox(6, "Enter low-pass filter cutoff (in Hz):", lengthStr, sizeof (lengthStr)-1) != 1)
+ return;
+
+ if (lengthStr[0] == '\0')
+ return;
+
+ lastLpCutoff = (int32_t)atoi(lengthStr);
+ if (lastLpCutoff < 1 || lastLpCutoff > 99999)
+ {
+ okBox(0, "System message", "Illegal range! Allowed range is 1..99999", NULL);
+ return;
+ }
+
+ setupResoLpFilter(s, &f, lastLpCutoff, filterResonance, false);
+ fillSampleUndo(KEEP_SAMPLE_MARK);
+ applyResoFilter(s, &f);
+ writeSample(true);
+}
+
+void pbSfxHighPass(void)
+{
+ resoFilter_t f;
+
+ sample_t *s = getCurSample();
+ if (s == NULL || s->dataPtr == NULL)
+ return;
+
+ char lengthStr[5+1];
+ memset(lengthStr, '\0', sizeof (lengthStr));
+ snprintf(lengthStr, sizeof (lengthStr), "%d", lastHpCutoff);
+
+ lastFilterType = FILTER_HIGHPASS;
+ if (inputBox(6, "Enter high-pass filter cutoff (in Hz):", lengthStr, sizeof (lengthStr)-1) != 1)
+ return;
+
+ if (lengthStr[0] == '\0')
+ return;
+
+ lastHpCutoff = (int32_t)atoi(lengthStr);
+ if (lastHpCutoff < 1 || lastHpCutoff > 99999)
+ {
+ okBox(0, "System message", "Illegal range! Allowed range is 1..99999", NULL);
+ return;
+ }
+
+ setupResoHpFilter(s, &f, lastHpCutoff, filterResonance, false);
+ fillSampleUndo(KEEP_SAMPLE_MARK);
+ applyResoFilter(s, &f);
+ writeSample(true);
+}
+
+void sfxPreviewFilter(uint32_t cutoff)
+{
+ sample_t oldSample;
+ resoFilter_t f;
+
+ sample_t *s = getCurSample();
+ if (s == NULL || s->dataPtr == NULL || s->length == 0 || cutoff < 1 || cutoff > 99999)
+ return;
+
+ int32_t x1, x2;
+ if (smpEd_Rx1 < smpEd_Rx2)
+ {
+ x1 = smpEd_Rx1;
+ x2 = smpEd_Rx2;
+
+ if (x2 > s->length)
+ x2 = s->length;
+
+ if (x1 < 0)
+ x1 = 0;
+
+ if (x2 <= x1)
+ return;
+ }
+ else
+ {
+ // no mark, operate on whole sample
+ x1 = 0;
+ x2 = s->length;
+ }
+
+ const int32_t len = x2 - x1;
+
+ pauseAudio();
+ unfixSample(s);
+ memcpy(&oldSample, s, sizeof (sample_t));
+
+ if (lastFilterType == FILTER_LOWPASS)
+ setupResoLpFilter(s, &f, cutoff, filterResonance, false);
+ else
+ setupResoHpFilter(s, &f, cutoff, filterResonance, false);
+
+ // prepare new sample
+ int8_t *sampleData;
+ if (s->flags & SAMPLE_16BIT)
+ {
+ sampleData = (int8_t *)malloc((len * sizeof (int16_t)) + SAMPLE_PAD_LENGTH);
+ if (sampleData == NULL)
+ goto Error;
+
+ memcpy(sampleData + SMP_DAT_OFFSET, (int16_t *)s->dataPtr + x1, len * sizeof (int16_t));
+ }
+ else
+ {
+ sampleData = (int8_t *)malloc((len * sizeof (int8_t)) + SAMPLE_PAD_LENGTH);
+ if (sampleData == NULL)
+ goto Error;
+
+ memcpy(sampleData + SMP_DAT_OFFSET, (int8_t *)s->dataPtr + x1, len * sizeof (int8_t));
+ }
+
+ s->origDataPtr = sampleData;
+ s->length = len;
+ s->dataPtr = s->origDataPtr + SMP_DAT_OFFSET;
+ s->loopStart = s->loopLength = 0;
+ fixSample(s);
+
+ const int32_t oldX1 = smpEd_Rx1;
+ const int32_t oldX2 = smpEd_Rx2;
+ smpEd_Rx1 = smpEd_Rx2 = 0;
+ applyResoFilter(s, &f);
+ smpEd_Rx1 = oldX1;
+ smpEd_Rx2 = oldX2;
+
+ // set up preview sample on channel 0
+ channel_t *ch = &channel[0];
+ uint8_t note = editor.smpEd_NoteNr;
+ ch->smpNum = editor.curSmp;
+ ch->instrNum = editor.curInstr;
+ ch->copyOfInstrAndNote = (ch->instrNum << 8) | note;
+ ch->efx = 0;
+ ch->smpStartPos = 0;
+ resumeAudio();
+ triggerNote(note, 0, 0, ch);
+ resetVolumes(ch);
+ triggerInstrument(ch);
+ ch->realVol = ch->outVol = ch->oldVol = 64;
+ updateVolPanAutoVib(ch);
+
+ while (ch->status & IS_Trigger); // wait for sample to latch in mixer
+ SDL_Delay(1500); // wait 1.5 seconds
+
+ // we're done, stop voice and free temporary data
+ pauseAudio();
+ free(sampleData);
+
+Error:
+ // set back old sample
+ memcpy(s, &oldSample, sizeof (sample_t));
+ fixSample(s);
+ resumeAudio();
+}
+
+void pbSfxSubBass(void)
+{
+ resoFilter_t f;
+
+ sample_t *s = getCurSample();
+ if (s == NULL || s->dataPtr == NULL)
+ return;
+
+ setupResoHpFilter(s, &f, 0.001, 0, true);
+ fillSampleUndo(KEEP_SAMPLE_MARK);
+ applyResoFilter(s, &f);
+ writeSample(true);
+}
+
+void pbSfxAddBass(void)
+{
+ resoFilter_t f;
+
+ sample_t *s = getCurSample();
+ if (s == NULL || s->dataPtr == NULL)
+ return;
+
+ int32_t x1, x2;
+ if (smpEd_Rx1 < smpEd_Rx2)
+ {
+ x1 = smpEd_Rx1;
+ x2 = smpEd_Rx2;
+
+ if (x2 > s->length)
+ x2 = s->length;
+
+ if (x1 < 0)
+ x1 = 0;
+
+ if (x2 <= x1)
+ return;
+ }
+ else
+ {
+ // no mark, operate on whole sample
+ x1 = 0;
+ x2 = s->length;
+ }
+
+ const int32_t len = x2 - x1;
+
+ setupResoLpFilter(s, &f, 0.015, 0, true);
+
+ double *dSmp = (double *)malloc(len * sizeof (double));
+ if (dSmp == NULL)
+ {
+ okBox(0, "System message", "Not enough memory!", NULL);
+ return;
+ }
+
+ fillSampleUndo(KEEP_SAMPLE_MARK);
+
+ pauseAudio();
+ unfixSample(s);
+
+ if (s->flags & SAMPLE_16BIT)
+ {
+ int16_t *ptr16 = (int16_t *)s->dataPtr + x1;
+ for (int32_t i = 0; i < len; i++)
+ dSmp[i] = (double)ptr16[i];
+ }
+ else
+ {
+ int8_t *ptr8 = (int8_t *)s->dataPtr + x1;
+ for (int32_t i = 0; i < len; i++)
+ dSmp[i] = (double)ptr8[i];
+ }
+
+ if (!normalization)
+ {
+ for (int32_t i = 0; i < len; i++)
+ {
+ double out = (f.a1*dSmp[i]) + (f.a2*f.inTmp[0]) + (f.a3*f.inTmp[1]) - (f.b1*f.outTmp[0]) - (f.b2*f.outTmp[1]);
+
+ f.inTmp[1] = f.inTmp[0];
+ f.inTmp[0] = dSmp[i];
+
+ f.outTmp[1] = f.outTmp[0];
+ f.outTmp[0] = out;
+
+ dSmp[i] = out;
+ }
+
+ if (s->flags & SAMPLE_16BIT)
+ {
+ int16_t *ptr16 = (int16_t *)s->dataPtr + x1;
+ for (int32_t i = 0; i < len; i++)
+ {
+ double out = ptr16[i] + (dSmp[i] * 0.25);
+ out = CLAMP(out, INT16_MIN, INT16_MAX);
+
+ ptr16[i] = (int16_t)out;
+ }
+ }
+ else
+ {
+ int8_t *ptr8 = (int8_t *)s->dataPtr + x1;
+ for (int32_t i = 0; i < len; i++)
+ {
+ double out = ptr8[i] + (dSmp[i] * 0.25);
+ out = CLAMP(out, INT8_MIN, INT8_MAX);
+
+ ptr8[i] = (int8_t)out;
+ }
+ }
+ }
+ else
+ {
+ if (s->flags & SAMPLE_16BIT)
+ {
+ int16_t *ptr16 = (int16_t *)s->dataPtr + x1;
+
+ double peak = 0.0;
+ for (int32_t i = 0; i < len; i++)
+ {
+ double out = (f.a1*dSmp[i]) + (f.a2*f.inTmp[0]) + (f.a3*f.inTmp[1]) - (f.b1*f.outTmp[0]) - (f.b2*f.outTmp[1]);
+
+ f.inTmp[1] = f.inTmp[0];
+ f.inTmp[0] = dSmp[i];
+
+ f.outTmp[1] = f.outTmp[0];
+ f.outTmp[0] = out;
+
+ dSmp[i] = out;
+ double bass = ptr16[i] + (out * 0.25);
+
+ const double outAbs = fabs(bass);
+ if (outAbs > peak)
+ peak = outAbs;
+ }
+
+ double scale = INT16_MAX / peak;
+ for (int32_t i = 0; i < len; i++)
+ ptr16[i] = (int16_t)((ptr16[i] + (dSmp[i] * 0.25)) * scale);
+ }
+ else
+ {
+ int8_t *ptr8 = (int8_t *)s->dataPtr + x1;
+
+ double peak = 0.0;
+ for (int32_t i = 0; i < len; i++)
+ {
+ double out = (f.a1*dSmp[i]) + (f.a2*f.inTmp[0]) + (f.a3*f.inTmp[1]) - (f.b1*f.outTmp[0]) - (f.b2*f.outTmp[1]);
+
+ f.inTmp[1] = f.inTmp[0];
+ f.inTmp[0] = dSmp[i];
+
+ f.outTmp[1] = f.outTmp[0];
+ f.outTmp[0] = out;
+
+ dSmp[i] = out;
+ double bass = ptr8[i] + (out * 0.25);
+
+ const double outAbs = fabs(bass);
+ if (outAbs > peak)
+ peak = outAbs;
+ }
+
+ double scale = INT8_MAX / peak;
+ for (int32_t i = 0; i < len; i++)
+ ptr8[i] = (int8_t)((ptr8[i] + (dSmp[i] * 0.25)) * scale);
+ }
+ }
+
+ free(dSmp);
+
+ fixSample(s);
+ resumeAudio();
+
+ writeSample(true);
+}
+
+void pbSfxSubTreble(void)
+{
+ resoFilter_t f;
+
+ sample_t *s = getCurSample();
+ if (s == NULL || s->dataPtr == NULL)
+ return;
+
+ setupResoLpFilter(s, &f, 0.33, 0, true);
+ fillSampleUndo(KEEP_SAMPLE_MARK);
+ applyResoFilter(s, &f);
+ writeSample(true);
+}
+
+void pbSfxAddTreble(void)
+{
+ resoFilter_t f;
+
+ sample_t *s = getCurSample();
+ if (s == NULL || s->dataPtr == NULL)
+ return;
+
+ int32_t x1, x2;
+ if (smpEd_Rx1 < smpEd_Rx2)
+ {
+ x1 = smpEd_Rx1;
+ x2 = smpEd_Rx2;
+
+ if (x2 > s->length)
+ x2 = s->length;
+
+ if (x1 < 0)
+ x1 = 0;
+
+ if (x2 <= x1)
+ return;
+ }
+ else
+ {
+ // no mark, operate on whole sample
+ x1 = 0;
+ x2 = s->length;
+ }
+
+ const int32_t len = x2 - x1;
+
+ setupResoHpFilter(s, &f, 0.27, 0, true);
+
+ double *dSmp = (double *)malloc(len * sizeof (double));
+ if (dSmp == NULL)
+ {
+ okBox(0, "System message", "Not enough memory!", NULL);
+ return;
+ }
+
+ fillSampleUndo(KEEP_SAMPLE_MARK);
+
+ pauseAudio();
+ unfixSample(s);
+
+ if (s->flags & SAMPLE_16BIT)
+ {
+ int16_t *ptr16 = (int16_t *)s->dataPtr + x1;
+ for (int32_t i = 0; i < len; i++)
+ dSmp[i] = (double)ptr16[i];
+ }
+ else
+ {
+ int8_t *ptr8 = (int8_t *)s->dataPtr + x1;
+ for (int32_t i = 0; i < len; i++)
+ dSmp[i] = (double)ptr8[i];
+ }
+
+ if (!normalization)
+ {
+ for (int32_t i = 0; i < len; i++)
+ {
+ double out = (f.a1*dSmp[i]) + (f.a2*f.inTmp[0]) + (f.a3*f.inTmp[1]) - (f.b1*f.outTmp[0]) - (f.b2*f.outTmp[1]);
+
+ f.inTmp[1] = f.inTmp[0];
+ f.inTmp[0] = dSmp[i];
+
+ f.outTmp[1] = f.outTmp[0];
+ f.outTmp[0] = out;
+
+ dSmp[i] = out;
+ }
+
+ if (s->flags & SAMPLE_16BIT)
+ {
+ int16_t *ptr16 = (int16_t *)s->dataPtr + x1;
+ for (int32_t i = 0; i < len; i++)
+ {
+ double out = ptr16[i] - (dSmp[i] * 0.25);
+ out = CLAMP(out, INT16_MIN, INT16_MAX);
+
+ ptr16[i] = (int16_t)out;
+ }
+ }
+ else
+ {
+ int8_t *ptr8 = (int8_t *)s->dataPtr + x1;
+ for (int32_t i = 0; i < len; i++)
+ {
+ double out = ptr8[i] - (dSmp[i] * 0.25);
+ out = CLAMP(out, INT8_MIN, INT8_MAX);
+
+ ptr8[i] = (int8_t)out;
+ }
+ }
+ }
+ else
+ {
+ if (s->flags & SAMPLE_16BIT)
+ {
+ int16_t *ptr16 = (int16_t *)s->dataPtr + x1;
+
+ double peak = 0.0;
+ for (int32_t i = 0; i < len; i++)
+ {
+ double out = (f.a1*dSmp[i]) + (f.a2*f.inTmp[0]) + (f.a3*f.inTmp[1]) - (f.b1*f.outTmp[0]) - (f.b2*f.outTmp[1]);
+
+ f.inTmp[1] = f.inTmp[0];
+ f.inTmp[0] = dSmp[i];
+
+ f.outTmp[1] = f.outTmp[0];
+ f.outTmp[0] = out;
+
+ dSmp[i] = out;
+ double treble = ptr16[i] - (out * 0.25);
+
+ const double outAbs = fabs(treble);
+ if (outAbs > peak)
+ peak = outAbs;
+ }
+
+ double scale = INT16_MAX / peak;
+ for (int32_t i = 0; i < len; i++)
+ ptr16[i] = (int16_t)((ptr16[i] - (dSmp[i] * 0.25)) * scale);
+ }
+ else
+ {
+ int8_t *ptr8 = (int8_t *)s->dataPtr + x1;
+
+ double peak = 0.0;
+ for (int32_t i = 0; i < len; i++)
+ {
+ double out = (f.a1*dSmp[i]) + (f.a2*f.inTmp[0]) + (f.a3*f.inTmp[1]) - (f.b1*f.outTmp[0]) - (f.b2*f.outTmp[1]);
+
+ f.inTmp[1] = f.inTmp[0];
+ f.inTmp[0] = dSmp[i];
+
+ f.outTmp[1] = f.outTmp[0];
+ f.outTmp[0] = out;
+
+ dSmp[i] = out;
+ double treble = ptr8[i] - (out * 0.25);
+
+ const double outAbs = fabs(treble);
+ if (outAbs > peak)
+ peak = outAbs;
+ }
+
+ double scale = INT8_MAX / peak;
+ for (int32_t i = 0; i < len; i++)
+ ptr8[i] = (int8_t)((ptr8[i] - (dSmp[i] * 0.25)) * scale);
+ }
+ }
+
+ free(dSmp);
+
+ fixSample(s);
+ resumeAudio();
+
+ writeSample(true);
+}
+
+void pbSfxSetAmp(void)
+{
+ sample_t *s = getCurSample();
+ if (s == NULL || s->dataPtr == NULL)
+ return;
+
+ int32_t x1, x2;
+ if (smpEd_Rx1 < smpEd_Rx2)
+ {
+ x1 = smpEd_Rx1;
+ x2 = smpEd_Rx2;
+
+ if (x2 > s->length)
+ x2 = s->length;
+
+ if (x1 < 0)
+ x1 = 0;
+
+ if (x2 <= x1)
+ return;
+ }
+ else
+ {
+ // no mark, operate on whole sample
+ x1 = 0;
+ x2 = s->length;
+ }
+
+ const int32_t len = x2 - x1;
+
+ char ampStr[3+1];
+ memset(ampStr, '\0', sizeof (ampStr));
+ snprintf(ampStr, sizeof (ampStr), "%d", lastAmp);
+
+ if (inputBox(1, "Change sample amplitude (in percentage, 0..999):", ampStr, sizeof (ampStr)-1) != 1)
+ return;
+
+ if (ampStr[0] == '\0')
+ return;
+
+ lastAmp = (int32_t)atoi(ampStr);
+
+ fillSampleUndo(KEEP_SAMPLE_MARK);
+
+ pauseAudio();
+ unfixSample(s);
+
+ const int32_t mul = (int32_t)round((1 << 22UL) * (lastAmp / 100.0));
+
+ if (s->flags & SAMPLE_16BIT)
+ {
+ int16_t *ptr16 = (int16_t *)s->dataPtr + x1;
+ for (int32_t i = 0; i < len; i++)
+ {
+ int32_t sample = ((int64_t)ptr16[i] * (int32_t)mul) >> 22;
+ sample = CLAMP(sample, INT16_MIN, INT16_MAX);
+ ptr16[i] = (int16_t)sample;
+ }
+ }
+ else
+ {
+ int8_t *ptr8 = (int8_t *)s->dataPtr + x1;
+ for (int32_t i = 0; i < len; i++)
+ {
+ int32_t sample = ((int64_t)ptr8[i] * (int32_t)mul) >> 22;
+ sample = CLAMP(sample, INT8_MIN, INT8_MAX);
+ ptr8[i] = (int8_t)sample;
+ }
+ }
+
+ fixSample(s);
+ resumeAudio();
+
+ writeSample(true);
+}
+
+void pbSfxUndo(void)
+{
+ if (!sampleUndo.filled || sampleUndo.undoInstr != editor.curInstr || sampleUndo.undoSmp != editor.curSmp)
+ return;
+
+ sample_t *s = getCurSample();
+ if (s == NULL || s->dataPtr == NULL)
+ return;
+
+ pauseAudio();
+
+ freeSmpData(s);
+ s->flags = sampleUndo.flags;
+ s->length = sampleUndo.length;
+ s->loopStart = sampleUndo.loopStart;
+ s->loopLength = sampleUndo.loopLength;
+
+ if (allocateSmpData(s, s->length, !!(s->flags & SAMPLE_16BIT)))
+ {
+ if (s->flags & SAMPLE_16BIT)
+ memcpy(s->dataPtr, sampleUndo.smpData16, s->length * sizeof (int16_t));
+ else
+ memcpy(s->dataPtr, sampleUndo.smpData8, s->length * sizeof (int8_t));
+
+ fixSample(s);
+ resumeAudio();
+ }
+ else
+ {
+ resumeAudio();
+ okBox(0, "System message", "Not enough memory!", NULL);
+ }
+
+ int32_t oldRx1 = smpEd_Rx1;
+ int32_t oldRx2 = smpEd_Rx2;
+
+ updateSampleEditorSample();
+
+ if (sampleUndo.keepSampleMark && oldRx1 < oldRx2)
+ {
+ smpEd_Rx1 = oldRx1;
+ smpEd_Rx2 = oldRx2;
+ writeSample(false); // redraw sample mark only
+ }
+
+ sampleUndo.keepSampleMark = false;
+ sampleUndo.filled = false;
+}
+
+void hideSampleEffectsScreen(void)
+{
+ ui.sampleEditorEffectsShown = false;
+
+ hideCheckBox(CB_SAMPFX_NORMALIZATION);
+ hidePushButton(PB_SAMPFX_CYCLES_UP);
+ hidePushButton(PB_SAMPFX_CYCLES_DOWN);
+ hidePushButton(PB_SAMPFX_TRIANGLE);
+ hidePushButton(PB_SAMPFX_SAW);
+ hidePushButton(PB_SAMPFX_SINE);
+ hidePushButton(PB_SAMPFX_SQUARE);
+ hidePushButton(PB_SAMPFX_RESO_UP);
+ hidePushButton(PB_SAMPFX_RESO_DOWN);
+ hidePushButton(PB_SAMPFX_LOWPASS);
+ hidePushButton(PB_SAMPFX_HIGHPASS);
+ hidePushButton(PB_SAMPFX_SUB_BASS);
+ hidePushButton(PB_SAMPFX_SUB_TREBLE);
+ hidePushButton(PB_SAMPFX_ADD_BASS);
+ hidePushButton(PB_SAMPFX_ADD_TREBLE);
+ hidePushButton(PB_SAMPFX_SET_AMP);
+ hidePushButton(PB_SAMPFX_UNDO);
+ hidePushButton(PB_SAMPFX_XFADE);
+ hidePushButton(PB_SAMPFX_BACK);
+
+ drawFramework(0, 346, 115, 54, FRAMEWORK_TYPE1);
+ drawFramework(115, 346, 133, 54, FRAMEWORK_TYPE1);
+ drawFramework(248, 346, 49, 54, FRAMEWORK_TYPE1);
+ drawFramework(297, 346, 56, 54, FRAMEWORK_TYPE1);
+
+ showPushButton(PB_SAMP_PNOTE_UP);
+ showPushButton(PB_SAMP_PNOTE_DOWN);
+ showPushButton(PB_SAMP_STOP);
+ showPushButton(PB_SAMP_PWAVE);
+ showPushButton(PB_SAMP_PRANGE);
+ showPushButton(PB_SAMP_PDISPLAY);
+ showPushButton(PB_SAMP_SHOW_RANGE);
+ showPushButton(PB_SAMP_RANGE_ALL);
+ showPushButton(PB_SAMP_CLR_RANGE);
+ showPushButton(PB_SAMP_ZOOM_OUT);
+ showPushButton(PB_SAMP_SHOW_ALL);
+ showPushButton(PB_SAMP_SAVE_RNG);
+ showPushButton(PB_SAMP_CUT);
+ showPushButton(PB_SAMP_COPY);
+ showPushButton(PB_SAMP_PASTE);
+ showPushButton(PB_SAMP_CROP);
+ showPushButton(PB_SAMP_VOLUME);
+ showPushButton(PB_SAMP_EFFECTS);
+
+ drawFramework(2, 366, 34, 15, FRAMEWORK_TYPE2);
+ textOutShadow(5, 352, PAL_FORGRND, PAL_DSKTOP2, "Play:");
+ updateSampleEditor();
+}
+
+void pbEffects(void)
+{
+ hidePushButton(PB_SAMP_PNOTE_UP);
+ hidePushButton(PB_SAMP_PNOTE_DOWN);
+ hidePushButton(PB_SAMP_STOP);
+ hidePushButton(PB_SAMP_PWAVE);
+ hidePushButton(PB_SAMP_PRANGE);
+ hidePushButton(PB_SAMP_PDISPLAY);
+ hidePushButton(PB_SAMP_SHOW_RANGE);
+ hidePushButton(PB_SAMP_RANGE_ALL);
+ hidePushButton(PB_SAMP_CLR_RANGE);
+ hidePushButton(PB_SAMP_ZOOM_OUT);
+ hidePushButton(PB_SAMP_SHOW_ALL);
+ hidePushButton(PB_SAMP_SAVE_RNG);
+ hidePushButton(PB_SAMP_CUT);
+ hidePushButton(PB_SAMP_COPY);
+ hidePushButton(PB_SAMP_PASTE);
+ hidePushButton(PB_SAMP_CROP);
+ hidePushButton(PB_SAMP_VOLUME);
+ hidePushButton(PB_SAMP_EFFECTS);
+
+ drawFramework(0, 346, 116, 54, FRAMEWORK_TYPE1);
+ drawFramework(116, 346, 114, 54, FRAMEWORK_TYPE1);
+ drawFramework(230, 346, 67, 54, FRAMEWORK_TYPE1);
+ drawFramework(297, 346, 56, 54, FRAMEWORK_TYPE1);
+
+ checkBoxes[CB_SAMPFX_NORMALIZATION].checked = normalization ? true : false;
+ showCheckBox(CB_SAMPFX_NORMALIZATION);
+ showPushButton(PB_SAMPFX_CYCLES_UP);
+ showPushButton(PB_SAMPFX_CYCLES_DOWN);
+ showPushButton(PB_SAMPFX_TRIANGLE);
+ showPushButton(PB_SAMPFX_SAW);
+ showPushButton(PB_SAMPFX_SINE);
+ showPushButton(PB_SAMPFX_SQUARE);
+ showPushButton(PB_SAMPFX_RESO_UP);
+ showPushButton(PB_SAMPFX_RESO_DOWN);
+ showPushButton(PB_SAMPFX_LOWPASS);
+ showPushButton(PB_SAMPFX_HIGHPASS);
+ showPushButton(PB_SAMPFX_SUB_BASS);
+ showPushButton(PB_SAMPFX_SUB_TREBLE);
+ showPushButton(PB_SAMPFX_ADD_BASS);
+ showPushButton(PB_SAMPFX_ADD_TREBLE);
+ showPushButton(PB_SAMPFX_SET_AMP);
+ showPushButton(PB_SAMPFX_UNDO);
+ showPushButton(PB_SAMPFX_XFADE);
+ showPushButton(PB_SAMPFX_BACK);
+
+ textOutShadow(4, 352, PAL_FORGRND, PAL_DSKTOP2, "Cycles:");
+ drawSampleCycles();
+
+ textOutShadow(121, 352, PAL_FORGRND, PAL_DSKTOP2, "Reson.:");
+ drawFilterResonance();
+
+ textOutShadow(135, 386, PAL_FORGRND, PAL_DSKTOP2, "Normalization");
+
+ textOutShadow(235, 352, PAL_FORGRND, PAL_DSKTOP2, "Bass");
+ textOutShadow(235, 369, PAL_FORGRND, PAL_DSKTOP2, "Treb.");
+
+ ui.sampleEditorEffectsShown = true;
+}
diff --git a/src/ft2_smpfx.h b/src/ft2_smpfx.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <stdint.h>
+#include "ft2_header.h"
+
+void clearSampleUndo(void);
+void cbSfxNormalization(void);
+void pbSfxCyclesUp(void);
+void pbSfxCyclesDown(void);
+void pbSfxTriangle(void);
+void pbSfxSaw(void);
+void pbSfxSine(void);
+void pbSfxSquare(void);
+void pbSfxResoUp(void);
+void pbSfxResoDown(void);
+void pbSfxLowPass(void);
+void pbSfxHighPass(void);
+void sfxPreviewFilter(uint32_t cutof);
+void pbSfxSubBass(void);
+void pbSfxSubTreble(void);
+void pbSfxAddBass(void);
+void pbSfxAddTreble(void);
+void pbSfxSetAmp(void);
+void pbSfxUndo(void);
+void hideSampleEffectsScreen(void);
+void pbEffects(void);
diff --git a/src/ft2_structs.h b/src/ft2_structs.h
@@ -25,7 +25,7 @@ typedef struct editor_t
bool autoPlayOnDrop, trimThreadWasDone, throwExit, editTextFlag;
bool copyMaskEnable, diskOpReadOnOpen, samplingAudioFlag, editSampleFlag;
- bool instrBankSwapped, chnMode[MAX_CHANNELS], NI_Play;
+ bool instrBankSwapped, channelMuted[MAX_CHANNELS], NI_Play;
uint8_t curPlayInstr, curPlaySmp, curSmpChannel, currPanEnvPoint, currVolEnvPoint;
uint8_t copyMask[5], pasteMask[5], transpMask[5], smpEd_NoteNr, instrBankOffset, sampleBankOffset;
@@ -56,7 +56,7 @@ typedef struct ui_t
uint8_t oldTopLeftScreen;
// bottom screens
- bool patternEditorShown, instEditorShown, sampleEditorShown, pattChanScrollShown;
+ bool patternEditorShown, instEditorShown, sampleEditorShown, sampleEditorEffectsShown, pattChanScrollShown;
bool leftLoopPinMoving, rightLoopPinMoving;
bool drawReplayerPianoFlag, drawPianoFlag, updatePatternEditor;
uint8_t channelOffset, numChannelsShown, maxVisibleChannels;
@@ -66,7 +66,7 @@ typedef struct ui_t
// backup flag for when entering/exiting extended pattern editor (TODO: this is lame and shouldn't be hardcoded)
bool _aboutScreenShown, _helpScreenShown, _configScreenShown, _diskOpShown;
bool _nibblesShown, _transposeShown, _instEditorShown;
- bool _instEditorExtShown, _sampleEditorExtShown, _patternEditorShown;
+ bool _instEditorExtShown, _sampleEditorExtShown, _sampleEditorEffectsShown, _patternEditorShown;
bool _sampleEditorShown, _advEditShown, _wavRendererShown, _trimScreenShown;
// -------------------------------------------------------------------------
} ui_t;
diff --git a/src/ft2_sysreqs.c b/src/ft2_sysreqs.c
@@ -10,6 +10,7 @@
#include "ft2_sysreqs.h"
#include "ft2_structs.h"
#include "ft2_events.h"
+#include "ft2_smpfx.h"
#define SYSTEM_REQUEST_H 67
#define SYSTEM_REQUEST_Y 249
@@ -21,7 +22,7 @@ void (*loaderMsgBox)(const char *, ...);
int16_t (*loaderSysReq)(int16_t, const char *, const char *, void (*)(void));
// ----------------
-#define NUM_SYSREQ_TYPES 6
+#define NUM_SYSREQ_TYPES 7
static char *buttonText[NUM_SYSREQ_TYPES][5] =
{
@@ -33,7 +34,8 @@ static char *buttonText[NUM_SYSREQ_TYPES][5] =
// custom dialogs
{ "All", "Song", "Instruments", "Cancel", "" }, // "song clear" dialog
{ "Read left", "Read right", "Convert", "", "" }, // "stereo sample loader" dialog
- { "Mono", "Stereo", "Cancel", "","" } // "audio sampling" dialog
+ { "Mono", "Stereo", "Cancel", "","" }, // "audio sampling" dialog
+ { "OK", "Preview", "Cancel", "","" } // sample editor effects filters
};
static SDL_Keycode shortCut[NUM_SYSREQ_TYPES][5] =
@@ -47,6 +49,7 @@ static SDL_Keycode shortCut[NUM_SYSREQ_TYPES][5] =
{ SDLK_a, SDLK_s, SDLK_i, SDLK_c, 0 }, // "song clear" dialog
{ SDLK_l, SDLK_r, SDLK_c, 0, 0 }, // "stereo sample loader" dialog
{ SDLK_m, SDLK_s, SDLK_c, 0, 0 }, // "audio sampling" dialog
+ { SDLK_o, SDLK_p, SDLK_c, 0, 0 } // sample editor effects filters
};
typedef struct quitType_t
@@ -191,7 +194,10 @@ int16_t okBox(int16_t type, const char *headline, const char *text, void (*check
SDL_Event inputEvent;
if (editor.editTextFlag)
+ {
exitTextEditing();
+ keyb.ignoreCurrKeyUp = false; // don't handle key-up kludge here
+ }
// revert "delete/rename" mouse modes (disk op.)
if (mouse.mode != MOUSE_MODE_NORMAL)
@@ -396,7 +402,10 @@ int16_t inputBox(int16_t type, const char *headline, char *edText, uint16_t maxS
SDL_Event inputEvent;
if (editor.editTextFlag)
+ {
exitTextEditing();
+ keyb.ignoreCurrKeyUp = false; // don't handle key-up kludge here
+ }
// revert "delete/rename" mouse modes (disk op.)
if (mouse.mode != MOUSE_MODE_NORMAL)
@@ -553,10 +562,19 @@ int16_t inputBox(int16_t type, const char *headline, char *edText, uint16_t maxS
{
if (shortCut[1][i] == inputEvent.key.keysym.sym)
{
- returnVal = i + 1;
- ui.sysReqShown = false;
- keyb.ignoreCurrKeyUp = true; // don't handle key up event for any keys that were pressed
- break;
+ if (type == 6 && returnVal == 2)
+ {
+ // special case for filters in sample editor "effects"
+ if (edText[0] != '\0')
+ sfxPreviewFilter(atoi(edText));
+ }
+ else
+ {
+ returnVal = i + 1;
+ ui.sysReqShown = false;
+ keyb.ignoreCurrKeyUp = true; // don't handle key up event for any keys that were pressed
+ break;
+ }
}
}
}
@@ -567,7 +585,17 @@ int16_t inputBox(int16_t type, const char *headline, char *edText, uint16_t maxS
{
returnVal = testPushButtonMouseRelease(false) + 1;
if (returnVal > 0)
- ui.sysReqShown = false;
+ {
+ if (type == 6 && returnVal == 2)
+ {
+ if (edText[0] != '\0')
+ sfxPreviewFilter(atoi(edText)); // special case for filters in sample editor "effects"
+ }
+ else
+ {
+ ui.sysReqShown = false;
+ }
+ }
mouse.lastUsedObjectID = OBJECT_ID_NONE;
mouse.lastUsedObjectType = OBJECT_NONE;
diff --git a/src/ft2_tables.c b/src/ft2_tables.c
@@ -79,7 +79,7 @@ const uint16_t modPeriods[8 * 12] = // used for .MOD loading/saving
53, 50, 47, 45, 42, 40, 37, 35, 33, 31, 30, 28
};
-const uint16_t linearPeriods[1936] = // bit-exact to FT2 table
+const uint16_t linearPeriodLUT[1936] = // bit-exact to FT2 table
{
7744, 7740, 7736, 7732, 7728, 7724, 7720, 7716, 7712, 7708, 7704, 7700, 7696, 7692, 7688, 7684,
7680, 7676, 7672, 7668, 7664, 7660, 7656, 7652, 7648, 7644, 7640, 7636, 7632, 7628, 7624, 7620,
@@ -204,7 +204,7 @@ const uint16_t linearPeriods[1936] = // bit-exact to FT2 table
64, 60, 56, 52, 48, 44, 40, 36, 32, 28, 24, 20, 16, 12, 8, 4
};
-const uint16_t amigaPeriods[1936] = // bit-exact to FT2 table
+const uint16_t amigaPeriodLUT[1936] = // bit-exact to FT2 table
{
29024, 28912, 28800, 28704, 28608, 28496, 28384, 28288, 28192, 28096, 28000, 27888, 27776, 27680, 27584, 27488,
27392, 27296, 27200, 27104, 27008, 26912, 26816, 26720, 26624, 26528, 26432, 26336, 26240, 26144, 26048, 25952,
@@ -712,23 +712,6 @@ const uint8_t pattCursorWTab[2 * 4 * 8] =
24, 4, 4, 4, 4, 4, 4, 4 // 12 columns visible
};
-// these two are for channel numbering on pattern data/scopes
-const char chDecTab1[MAX_CHANNELS+1] =
-{
- '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
- '1', '1', '1', '1', '1', '1', '1', '1', '1', '1',
- '2', '2', '2', '2', '2', '2', '2', '2', '2', '2',
- '3', '3', '3'
-};
-
-const char chDecTab2[MAX_CHANNELS+1] =
-{
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
- '0', '1', '2'
-};
-
const SDL_Keycode key2VolTab[16] =
{
SDLK_0, SDLK_1, SDLK_2, SDLK_3, SDLK_4, SDLK_MINUS, SDLK_PLUS, SDLK_d,
diff --git a/src/ft2_tables.h b/src/ft2_tables.h
@@ -16,8 +16,8 @@ extern const uint8_t arpeggioTab[256];
extern const int8_t autoVibSineTab[256];
extern const uint8_t vibratoTab[32];
extern const uint16_t modPeriods[8 * 12];
-extern const uint16_t linearPeriods[1936];
-extern const uint16_t amigaPeriods[1936];
+extern const uint16_t linearPeriodLUT[1936];
+extern const uint16_t amigaPeriodLUT[1936];
extern const char *dec2StrTab[100];
extern const char *dec3StrTab[256];
@@ -37,8 +37,6 @@ extern const pattCoord2_t pattCoord2Table[2][2][2];
extern const markCoord_t markCoordTable[2][2][2];
extern const uint8_t pattCursorXTab[2 * 4 * 8];
extern const uint8_t pattCursorWTab[2 * 4 * 8];
-extern const char chDecTab1[MAX_CHANNELS+1];
-extern const char chDecTab2[MAX_CHANNELS+1];
extern const SDL_Keycode key2VolTab[16];
extern const SDL_Keycode key2EfxTab[36];
extern const SDL_Keycode key2HexTab[16];
diff --git a/src/ft2_textboxes.c b/src/ft2_textboxes.c
@@ -722,7 +722,10 @@ bool testTextBoxMouseDown(void)
// if we were editing text and we clicked outside of a text box, exit text editing
if (editor.editTextFlag)
+ {
exitTextEditing();
+ keyb.ignoreCurrKeyUp = false; // if we exited with mouse, don't handle key-up kludge
+ }
return false;
}
diff --git a/src/scopes/ft2_scopes.c b/src/scopes/ft2_scopes.c
@@ -66,11 +66,11 @@ void stopAllScopes(void)
}
// toggle mute
-static void setChannel(int32_t chNr, bool on)
+static void setChannelMute(int32_t chNr, bool off)
{
channel_t *ch = &channel[chNr];
- ch->channelOff = !on;
+ ch->channelOff = off;
if (ch->channelOff)
{
ch->efx = 0;
@@ -104,8 +104,8 @@ static void drawScopeNumber(uint16_t scopeXOffs, uint16_t scopeYOffs, uint8_t ch
}
else
{
- charOutOutlined(scopeXOffs, scopeYOffs, PAL_MOUSEPT, chDecTab1[chNr]);
- charOutOutlined(scopeXOffs + 7, scopeYOffs, PAL_MOUSEPT, chDecTab2[chNr]);
+ charOutOutlined(scopeXOffs, scopeYOffs, PAL_MOUSEPT, '0' + (chNr / 10));
+ charOutOutlined(scopeXOffs + 7, scopeYOffs, PAL_MOUSEPT, '0' + (chNr % 10));
}
}
else
@@ -116,8 +116,8 @@ static void drawScopeNumber(uint16_t scopeXOffs, uint16_t scopeYOffs, uint8_t ch
}
else
{
- charOut(scopeXOffs, scopeYOffs, PAL_MOUSEPT, chDecTab1[chNr]);
- charOut(scopeXOffs + 7, scopeYOffs, PAL_MOUSEPT, chDecTab2[chNr]);
+ charOut(scopeXOffs, scopeYOffs, PAL_MOUSEPT, '0' + (chNr / 10));
+ charOut(scopeXOffs + 7, scopeYOffs, PAL_MOUSEPT, '0' + (chNr % 10));
}
}
}
@@ -157,7 +157,7 @@ static void redrawScope(int32_t ch)
drawFramework(x, y, scopeLen + 2, 38, FRAMEWORK_TYPE2);
// draw mute graphics if channel is muted
- if (!editor.chnMode[i])
+ if (editor.channelMuted[i])
{
const uint16_t muteGfxLen = scopeMuteBMP_Widths[chanLookup];
const uint16_t muteGfxX = x + ((scopeLen - muteGfxLen) >> 1);
@@ -191,41 +191,41 @@ static void channelMode(int32_t chn)
bool test = false;
for (i = 0; i < song.numChannels; i++)
{
- if (i != chn && !editor.chnMode[i])
+ if (i != chn && editor.channelMuted[i])
test = true;
}
if (test)
{
for (i = 0; i < song.numChannels; i++)
- editor.chnMode[i] = true;
+ editor.channelMuted[i] = false;
}
else
{
for (i = 0; i < song.numChannels; i++)
- editor.chnMode[i] = (i == chn);
+ editor.channelMuted[i] = !(i == chn);
}
}
else if (m)
{
- editor.chnMode[chn] ^= 1;
+ editor.channelMuted[chn] ^= 1;
}
else
{
- if (editor.chnMode[chn])
+ if (!editor.channelMuted[chn])
{
config.multiRecChn[chn] ^= 1;
}
else
{
config.multiRecChn[chn] = true;
- editor.chnMode[chn] = true;
+ editor.channelMuted[chn] = false;
m = true;
}
}
for (i = 0; i < song.numChannels; i++)
- setChannel(i, editor.chnMode[i]);
+ setChannelMute(i, editor.channelMuted[i]);
if (m2)
{
@@ -421,7 +421,7 @@ void drawScopes(void)
}
const uint16_t scopeDrawLen = scopeLens[i];
- if (!editor.chnMode[i]) // scope muted (mute graphics blit()'ed elsewhere)
+ if (editor.channelMuted[i]) // scope muted (mute graphics blit()'ed elsewhere)
{
scopeXOffs += scopeDrawLen+3; // align x to next scope
continue;
diff --git a/vs2019_project/ft2-clone/ft2-clone.vcxproj b/vs2019_project/ft2-clone/ft2-clone.vcxproj
@@ -319,6 +319,7 @@
<ClCompile Include="..\..\src\ft2_pattern_draw.c" />
<ClCompile Include="..\..\src\ft2_pushbuttons.c" />
<ClCompile Include="..\..\src\ft2_radiobuttons.c" />
+ <ClCompile Include="..\..\src\ft2_random.c" />
<ClCompile Include="..\..\src\ft2_sample_ed_features.c" />
<ClCompile Include="..\..\src\ft2_sampling.c" />
<ClCompile Include="..\..\src\ft2_replayer.c" />
@@ -326,6 +327,7 @@
<ClCompile Include="..\..\src\ft2_sample_loader.c" />
<ClCompile Include="..\..\src\ft2_sample_saver.c" />
<ClCompile Include="..\..\src\ft2_scrollbars.c" />
+ <ClCompile Include="..\..\src\ft2_smpfx.c" />
<ClCompile Include="..\..\src\ft2_structs.c" />
<ClCompile Include="..\..\src\ft2_sysreqs.c" />
<ClCompile Include="..\..\src\ft2_tables.c" />
@@ -417,6 +419,7 @@
<ClInclude Include="..\..\src\ft2_pattern_draw.h" />
<ClInclude Include="..\..\src\ft2_pushbuttons.h" />
<ClInclude Include="..\..\src\ft2_radiobuttons.h" />
+ <ClInclude Include="..\..\src\ft2_random.h" />
<ClInclude Include="..\..\src\ft2_sample_ed_features.h" />
<ClInclude Include="..\..\src\ft2_sampling.h" />
<ClInclude Include="..\..\src\ft2_replayer.h" />
@@ -425,6 +428,7 @@
<ClInclude Include="..\..\src\ft2_sample_saver.h" />
<ClInclude Include="..\..\src\ft2_scopedraw.h" />
<ClInclude Include="..\..\src\ft2_scrollbars.h" />
+ <ClInclude Include="..\..\src\ft2_smpfx.h" />
<ClInclude Include="..\..\src\ft2_structs.h" />
<ClInclude Include="..\..\src\ft2_sysreqs.h" />
<ClInclude Include="..\..\src\ft2_tables.h" />
diff --git a/vs2019_project/ft2-clone/ft2-clone.vcxproj.filters b/vs2019_project/ft2-clone/ft2-clone.vcxproj.filters
@@ -176,6 +176,8 @@
<ClCompile Include="..\..\src\mixer\ft2_quadratic_spline.c">
<Filter>mixer</Filter>
</ClCompile>
+ <ClCompile Include="..\..\src\ft2_random.c" />
+ <ClCompile Include="..\..\src\ft2_smpfx.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\src\rtmidi\RtMidi.h">
@@ -337,6 +339,12 @@
<ClInclude Include="..\..\src\mixer\ft2_quadratic_spline.h">
<Filter>mixer</Filter>
</ClInclude>
+ <ClInclude Include="..\..\src\ft2_random.h">
+ <Filter>headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\ft2_smpfx.h">
+ <Filter>headers</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="mixer">