ref: a1d153b3ea809d6032483d629d0681bb4743e786
parent: 37069678c8a822cf43703ae627cdaa9768431e15
author: Olav Sørensen <[email protected]>
date: Sun Feb 16 15:03:49 EST 2020
Pushed v1.06 code - Fix: Don't cut off voices when changing the sample length from the GUI - When loading a WAV/AIFF sample that has a frequency above 22kHz, show a big ask dialog with more details about what is being asked. Some users got confused about the old "2X DOWNSAMPLING ?" status text that appeared when loading some samples. - When selecting "Yes" to downsampling a sample before loading it, apply a low-pass filter first to get rid of some potential aliasing after downsampling. This can now be turned off in protracker.ini (SAMPLELOWPASS). I recommend leaving this on, as it might remove quite a bit of aliasing in samples with a lot of high frequencies, like hi-hats and cymbals. However, some sharpness might get lost. If you need more sharpness, try the "BOOST" button in "Edit Op." screen #3. The low-pass filter is only applied to samples that are going to get 2x downsampled during load. You'll get a dialog where you click yes/no on samples that has a frequency higher than 22kHz. - Only filter forwards (not backwards as well) when filtering samples in the FILTERS toolbox in the sample editor. This makes the selected cutoff more correct. - The sample marking now looks slightly nicer (different color on the center line).
--- a/release/macos/protracker.ini
+++ b/release/macos/protracker.ini
@@ -221,4 +221,18 @@
;
STEREOSEPARATION=20
+
+; Low-pass samples before getting 2x downsampled during loading (if requested)
+; Syntax: TRUE or FALSE
+; Default value: TRUE
+; Comment: Set to false if you want slightly sharper sound when loading
+; samples that are 2x downsampled (if requested).
+; Keep in mind that you might get more aliasing in the sound if you
+; disable this, and certain sounds with a lot of treble might sound
+; weird (f.ex. hi-hats and cymbals). If the sample you load have a
+; frequency below 22kHz, it will never be downsampled (and thus this
+; setting changes nothing). I recommend keeping this set to TRUE.
+;
+SAMPLELOWPASS=TRUE
+
; End of config file
\ No newline at end of file
--- a/release/other/protracker.ini
+++ b/release/other/protracker.ini
@@ -221,4 +221,18 @@
;
STEREOSEPARATION=20
+
+; Low-pass samples before getting 2x downsampled during loading (if requested)
+; Syntax: TRUE or FALSE
+; Default value: TRUE
+; Comment: Set to false if you want slightly sharper sound when loading
+; samples that are 2x downsampled (if requested).
+; Keep in mind that you might get more aliasing in the sound if you
+; disable this, and certain sounds with a lot of treble might sound
+; weird (f.ex. hi-hats and cymbals). If the sample you load have a
+; frequency below 22kHz, it will never be downsampled (and thus this
+; setting changes nothing). I recommend keeping this set to TRUE.
+;
+SAMPLELOWPASS=TRUE
+
; End of config file
\ No newline at end of file
--- a/release/win32/protracker.ini
+++ b/release/win32/protracker.ini
@@ -221,4 +221,18 @@
;
STEREOSEPARATION=20
+
+; Low-pass samples before getting 2x downsampled during loading (if requested)
+; Syntax: TRUE or FALSE
+; Default value: TRUE
+; Comment: Set to false if you want slightly sharper sound when loading
+; samples that are 2x downsampled (if requested).
+; Keep in mind that you might get more aliasing in the sound if you
+; disable this, and certain sounds with a lot of treble might sound
+; weird (f.ex. hi-hats and cymbals). If the sample you load have a
+; frequency below 22kHz, it will never be downsampled (and thus this
+; setting changes nothing). I recommend keeping this set to TRUE.
+;
+SAMPLELOWPASS=TRUE
+
; End of config file
\ No newline at end of file
--- a/release/win64/protracker.ini
+++ b/release/win64/protracker.ini
@@ -221,4 +221,18 @@
;
STEREOSEPARATION=20
+
+; Low-pass samples before getting 2x downsampled during loading (if requested)
+; Syntax: TRUE or FALSE
+; Default value: TRUE
+; Comment: Set to false if you want slightly sharper sound when loading
+; samples that are 2x downsampled (if requested).
+; Keep in mind that you might get more aliasing in the sound if you
+; disable this, and certain sounds with a lot of treble might sound
+; weird (f.ex. hi-hats and cymbals). If the sample you load have a
+; frequency below 22kHz, it will never be downsampled (and thus this
+; setting changes nothing). I recommend keeping this set to TRUE.
+;
+SAMPLELOWPASS=TRUE
+
; End of config file
\ No newline at end of file
binary files /dev/null b/src/gfx/bmp/bigyesnodialog.bmp differ
--- a/src/gfx/pt2_gfx_yes_no_dialog.c
+++ b/src/gfx/pt2_gfx_yes_no_dialog.c
@@ -35,3 +35,34 @@
0x5A,0xCC,0x17,0xAA,0xAF,0x5A,0xCC,0x17,0xAA,0xAF,0x5A,0xCC,0x17,0xAA,0xAF,0x5A,
0xCC,0x17,0xAA,0xAF,0x6F,0xCC,0x18,0xFF,0xBF,0xCC,0x18,0xFF
};
+
+// Final unpack length: 11000
+// Decoded length: 2750 (first four bytes of buffer)
+const uint8_t bigYesNoDialogPackedBMP[472] =
+{
+ 0x00,0x00,0x0A,0xBE,0xCC,0x30,0x55,0x56,0xCC,0x30,0x55,0x5B,0x5A,0xCC,0x2F,0xAA,0xAF,0x5A,0xCC,0x2F,
+ 0xAA,0xAF,0x5A,0xCC,0x2F,0xAA,0xAF,0x5A,0xCC,0x2F,0xAA,0xAF,0x5A,0xCC,0x2F,0xAA,0xAF,0x5A,0xCC,0x2F,
+ 0xAA,0xAF,0x5A,0xCC,0x2F,0xAA,0xAF,0x5A,0xCC,0x2F,0xAA,0xAF,0x5A,0xCC,0x2F,0xAA,0xAF,0x5A,0xCC,0x2F,
+ 0xAA,0xAF,0x5A,0xCC,0x2F,0xAA,0xAF,0x5A,0xCC,0x2F,0xAA,0xAF,0x5A,0xCC,0x2F,0xAA,0xAF,0x5A,0xCC,0x2F,
+ 0xAA,0xAF,0x5A,0xCC,0x2F,0xAA,0xAF,0x5A,0xCC,0x2F,0xAA,0xAF,0x5A,0xCC,0x2F,0xAA,0xAF,0x5A,0xCC,0x2F,
+ 0xAA,0xAF,0x5A,0xCC,0x2F,0xAA,0xAF,0x5A,0xCC,0x2F,0xAA,0xAF,0x5A,0xCC,0x2F,0xAA,0xAF,0x5A,0xCC,0x2F,
+ 0xAA,0xAF,0x5A,0xCC,0x2F,0xAA,0xAF,0x5A,0xCC,0x2F,0xAA,0xAF,0x5A,0xCC,0x2F,0xAA,0xAF,0x5A,0xCC,0x2F,
+ 0xAA,0xAF,0x5A,0xCC,0x2F,0xAA,0xAF,0x5A,0xCC,0x2F,0xAA,0xAF,0x5A,0xCC,0x2F,0xAA,0xAF,0x5A,0xCC,0x2F,
+ 0xAA,0xAF,0x5A,0xCC,0x2F,0xAA,0xAF,0x5A,0xCC,0x2F,0xAA,0xAF,0x5A,0xCC,0x2F,0xAA,0xAF,0x5A,0xCC,0x2F,
+ 0xAA,0xAF,0x5A,0xCC,0x2F,0xAA,0xAF,0x5A,0xCC,0x2F,0xAA,0xAF,0x5A,0xCC,0x2F,0xAA,0xAF,0x5A,0xCC,0x0C,
+ 0xAA,0xAB,0xCC,0x05,0xFF,0xCC,0x08,0xAA,0xAF,0xCC,0x03,0xFF,0xCC,0x0D,0xAA,0xAF,0x5A,0xCC,0x0C,0xAA,
+ 0xAB,0xCC,0x05,0xAA,0x6A,0xCC,0x07,0xAA,0xAE,0xCC,0x03,0xAA,0x6A,0xCC,0x0C,0xAA,0xAF,0x5A,0xCC,0x0C,
+ 0xAA,0xAB,0xCC,0x05,0xAA,0x6A,0xCC,0x07,0xAA,0xAE,0xCC,0x03,0xAA,0x6A,0xCC,0x0C,0xAA,0xAF,0x5A,0xCC,
+ 0x0C,0xAA,0xAB,0xA5,0xA5,0x95,0x56,0x95,0x5A,0x6A,0xCC,0x07,0xAA,0xAE,0x96,0x96,0x95,0x6A,0x6A,0xCC,
+ 0x0C,0xAA,0xAF,0x5A,0xCC,0x0C,0xAA,0xAB,0xA5,0xE5,0xD7,0xFF,0x5F,0xFE,0x6A,0xCC,0x07,0xAA,0xAE,0x95,
+ 0x97,0x5F,0x5A,0x6A,0xCC,0x0C,0xAA,0xAF,0x5A,0xCC,0x0C,0xAA,0xAB,0xA9,0x57,0xD5,0x5A,0x95,0x6A,0x6A,
+ 0xCC,0x07,0xAA,0xAE,0x95,0x57,0x5E,0x5E,0x6A,0xCC,0x0C,0xAA,0xAF,0x5A,0xCC,0x0C,0xAA,0xAB,0xAA,0x5F,
+ 0x97,0xFE,0xAF,0x5A,0x6A,0xCC,0x07,0xAA,0xAE,0x97,0x57,0x5E,0x5E,0x6A,0xCC,0x0C,0xAA,0xAF,0x5A,0xCC,
+ 0x0C,0xAA,0xAB,0xAA,0x5E,0x95,0x56,0x55,0x7E,0x6A,0xCC,0x07,0xAA,0xAE,0x97,0x97,0x95,0x7E,0x6A,0xCC,
+ 0x0C,0xAA,0xAF,0x5A,0xCC,0x0C,0xAA,0xAB,0xAA,0xBE,0xAF,0xFF,0xBF,0xFA,0x6A,0xCC,0x07,0xAA,0xAE,0xAF,
+ 0xAF,0xAF,0xFA,0x6A,0xCC,0x0C,0xAA,0xAF,0x5A,0xCC,0x0C,0xAA,0xAB,0xCC,0x05,0xAA,0x6A,0xCC,0x07,0xAA,
+ 0xAE,0xCC,0x03,0xAA,0x6A,0xCC,0x0C,0xAA,0xAF,0x5A,0xCC,0x0D,0xAA,0xCC,0x05,0x55,0x6A,0xCC,0x07,0xAA,
+ 0xA9,0xCC,0x03,0x55,0x6A,0xCC,0x0C,0xAA,0xAF,0x5A,0xCC,0x2F,0xAA,0xAF,0x5A,0xCC,0x2F,0xAA,0xAF,0x5A,
+ 0xCC,0x2F,0xAA,0xAF,0x6F,0xCC,0x30,0xFF,0xBF,0xCC,0x30,0xFF
+};
+
--- a/src/pt2_audio.c
+++ b/src/pt2_audio.c
@@ -34,8 +34,6 @@
#define INITIAL_DITHER_SEED 0x12345000
-#define DENORMAL_OFFSET 1e-10
-
typedef struct ledFilter_t
{
double dLed[4];
@@ -197,6 +195,12 @@
dOut[1] = filter->dBuffer[1];
}
+void lossyIntegratorMono(lossyIntegrator_t *filter, double dIn, double *dOut)
+{
+ filter->dBuffer[0] = (filter->b0 * dIn) + (filter->b1 * filter->dBuffer[0]) + DENORMAL_OFFSET;
+ *dOut = filter->dBuffer[0];
+}
+
void lossyIntegratorHighPass(lossyIntegrator_t *filter, double *dIn, double *dOut)
{
double dLow[2];
@@ -205,6 +209,15 @@
dOut[0] = dIn[0] - dLow[0]; // left channel high-pass
dOut[1] = dIn[1] - dLow[1]; // right channel high-pass
+}
+
+void lossyIntegratorHighPassMono(lossyIntegrator_t *filter, double dIn, double *dOut)
+{
+ double dLow;
+
+ lossyIntegratorMono(filter, dIn, &dLow);
+
+ *dOut = dIn - dLow;
}
/* adejr/aciddose: these sin/cos approximations both use a 0..1
--- a/src/pt2_audio.h
+++ b/src/pt2_audio.h
@@ -6,6 +6,8 @@
#include <stdint.h>
#include <stdbool.h>
+#define DENORMAL_OFFSET 1e-10
+
typedef struct lossyIntegrator_t
{
double dBuffer[2], b0, b1;
@@ -15,7 +17,9 @@
void resetAudioDithering(void);
void calcCoeffLossyIntegrator(double dSr, double dHz, lossyIntegrator_t *filter);
void lossyIntegrator(lossyIntegrator_t *filter, double *dIn, double *dOut);
+void lossyIntegratorMono(lossyIntegrator_t *filter, double dIn, double *dOut);
void lossyIntegratorHighPass(lossyIntegrator_t *filter, double *dIn, double *dOut);
+void lossyIntegratorHighPassMono(lossyIntegrator_t *filter, double dIn, double *dOut);
void normalize32bitSigned(int32_t *sampleData, uint32_t sampleLength);
void normalize16bitSigned(int16_t *sampleData, uint32_t sampleLength);
void normalize8bitFloatSigned(float *fSampleData, uint32_t sampleLength);
--- a/src/pt2_config.c
+++ b/src/pt2_config.c
@@ -21,6 +21,7 @@
#include "pt2_diskop.h"
#include "pt2_config.h"
#include "pt2_textout.h"
+#include "pt2_sampler.h"
#ifndef _WIN32
static char oldCwd[PATH_MAX];
@@ -59,6 +60,7 @@
ptConfig.autoCloseDiskOp = true;
ptConfig.vsyncOff = false;
ptConfig.hwMouse = false;
+ ptConfig.sampleLowpass = true;
#ifndef _WIN32
getcwd(oldCwd, PATH_MAX);
@@ -138,6 +140,9 @@
#ifndef _WIN32
chdir(oldCwd);
#endif
+
+ // use palette for generating sample data mark (invert) table
+ createSampleMarkTable();
}
static bool loadProTrackerDotIni(FILE *f)
@@ -188,6 +193,13 @@
{
if (!_strnicmp(&configLine[8], "TRUE", 4)) ptConfig.hwMouse = true;
else if (!_strnicmp(&configLine[8], "FALSE", 5)) ptConfig.hwMouse = false;
+ }
+
+ // SAMPLELOWPASS
+ else if (!_strnicmp(configLine, "SAMPLELOWPASS=", 14))
+ {
+ if (!_strnicmp(&configLine[14], "TRUE", 4)) ptConfig.sampleLowpass = true;
+ else if (!_strnicmp(&configLine[14], "FALSE", 5)) ptConfig.sampleLowpass = false;
}
// VSYNCOFF
--- a/src/pt2_config.h
+++ b/src/pt2_config.h
@@ -8,6 +8,7 @@
char *defModulesDir, *defSamplesDir;
bool dottedCenterFlag, pattDots, a500LowPassFilter, compoMode, autoCloseDiskOp, hideDiskOpDates, hwMouse;
bool transDel, fullScreenStretch, vsyncOff, modDot, blankZeroFlag, realVuMeters, rememberPlayMode;
+ bool sampleLowpass;
int8_t stereoSeparation, videoScaleFactor, accidental;
uint16_t quantizeValue;
uint32_t soundFrequency, soundBufferSize;
--- /dev/null
+++ b/src/pt2_filters.c
@@ -1,0 +1,146 @@
+/* These are second variants of low-pass/high-pass filters that are better than
+** the ones used in the main audio mixer. The reason we use a different ones for
+** the main audio mixer is because it makes it sound closer to real Amigas.
+**
+** These ones are used for low-pass filtering when loading samples w/ 2x downsampling.
+*/
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <math.h>
+#include "pt2_audio.h" // DENORMAL_OFFSET constant
+#include "pt2_helpers.h"
+
+typedef struct filterState_t
+{
+ double dBuffer, b0, b1;
+} filterState_t;
+
+static void calcFilterCoeffs(double dSr, double dHz, filterState_t *filter)
+{
+ filter->b0 = tan((M_PI * dHz) / dSr);
+ filter->b1 = 1.0 / (1.0 + filter->b0);
+}
+
+static double doLowpass(filterState_t *filter, double dIn)
+{
+ double dOutput;
+
+ dOutput = (filter->b0 * dIn + filter->dBuffer) * filter->b1;
+ filter->dBuffer = filter->b0 * (dIn - dOutput) + dOutput + DENORMAL_OFFSET;
+
+ return dOutput;
+}
+
+bool lowPassSample8Bit(int8_t *buffer, int32_t length, int32_t sampleFrequency, double cutoff)
+{
+ filterState_t filter;
+
+ if (buffer == NULL || length == 0 || cutoff == 0.0)
+ return false;
+
+ calcFilterCoeffs(sampleFrequency, cutoff, &filter);
+
+ filter.dBuffer = 0.0;
+ for (int32_t i = 0; i < length; i++)
+ {
+ int32_t sample;
+ sample = (int32_t)doLowpass(&filter, buffer[i]);
+ buffer[i] = (int8_t)CLAMP(sample, INT8_MIN, INT8_MAX);
+ }
+
+ return true;
+}
+
+bool lowPassSample8BitUnsigned(uint8_t *buffer, int32_t length, int32_t sampleFrequency, double cutoff)
+{
+ filterState_t filter;
+
+ if (buffer == NULL || length == 0 || cutoff == 0.0)
+ return false;
+
+ calcFilterCoeffs(sampleFrequency, cutoff, &filter);
+
+ filter.dBuffer = 0.0;
+ for (int32_t i = 0; i < length; i++)
+ {
+ int32_t sample;
+ sample = (int32_t)doLowpass(&filter, buffer[i] - 128);
+ sample = CLAMP(sample, INT8_MIN, INT8_MAX);
+ buffer[i] = (uint8_t)(sample + 128);
+ }
+
+ return true;
+}
+
+bool lowPassSample16Bit(int16_t *buffer, int32_t length, int32_t sampleFrequency, double cutoff)
+{
+ filterState_t filter;
+
+ if (buffer == NULL || length == 0 || cutoff == 0.0)
+ return false;
+
+ calcFilterCoeffs(sampleFrequency, cutoff, &filter);
+
+ filter.dBuffer = 0.0;
+ for (int32_t i = 0; i < length; i++)
+ {
+ int32_t sample;
+ sample = (int32_t)doLowpass(&filter, buffer[i]);
+ buffer[i] = (int16_t)CLAMP(sample, INT16_MIN, INT16_MAX);
+ }
+
+ return true;
+}
+
+bool lowPassSample32Bit(int32_t *buffer, int32_t length, int32_t sampleFrequency, double cutoff)
+{
+ filterState_t filter;
+
+ if (buffer == NULL || length == 0 || cutoff == 0.0)
+ return false;
+
+ calcFilterCoeffs(sampleFrequency, cutoff, &filter);
+
+ filter.dBuffer = 0.0;
+ for (int32_t i = 0; i < length; i++)
+ {
+ int64_t sample;
+ sample = (int64_t)doLowpass(&filter, buffer[i]);
+ buffer[i] = (int32_t)CLAMP(sample, INT32_MIN, INT32_MAX);
+ }
+
+ return true;
+}
+
+bool lowPassSampleFloat(float *buffer, int32_t length, int32_t sampleFrequency, double cutoff)
+{
+ filterState_t filter;
+
+ if (buffer == NULL || length == 0 || cutoff == 0.0)
+ return false;
+
+ calcFilterCoeffs(sampleFrequency, cutoff, &filter);
+
+ filter.dBuffer = 0.0;
+ for (int32_t i = 0; i < length; i++)
+ buffer[i] = (float)doLowpass(&filter, buffer[i]);
+
+ return true;
+}
+
+bool lowPassSampleDouble(double *buffer, int32_t length, int32_t sampleFrequency, double cutoff)
+{
+ filterState_t filter;
+
+ if (buffer == NULL || length == 0 || cutoff == 0.0)
+ return false;
+
+ calcFilterCoeffs(sampleFrequency, cutoff, &filter);
+
+ filter.dBuffer = 0.0;
+ for (int32_t i = 0; i < length; i++)
+ buffer[i] = doLowpass(&filter, buffer[i]);
+
+ return true;
+}
--- /dev/null
+++ b/src/pt2_filters.h
@@ -1,0 +1,31 @@
+/* These are second variants of low-pass/high-pass filters that are better than
+** the ones used in the main audio mixer. The reason we use a different one for
+** the main audio mixer is because it makes it sound closer to real Amigas.
+**
+** These ones are used for filtering samples when loading samples, or with the
+** FILTERS toolbox in the Sample Editor.
+*/
+
+#pragma once
+
+#include <stdio.h>
+#include <stdbool.h>
+
+/* 8bitbubsy: Before we downsample a loaded WAV/AIFF (>22kHz) sample by 2x, we low-pass
+** filter it.
+**
+*** I think this value ought to be 4.0 (nyquist freq. / 2), but it cuts off too much in
+** my opinion! The improvement is only noticable on samples that has quite a bit of high
+** frequencies in them to begin with.
+**
+** This is probably not how to do it, so if someone with a bit more knowledge can do this
+** in a proper way without using an external resampler library, that would be neato!
+*/
+#define DOWNSAMPLE_CUTOFF_FACTOR 4.0
+
+bool lowPassSample8Bit(int8_t *buffer, int32_t length, int32_t sampleFrequency, double cutoff);
+bool lowPassSample8BitUnsigned(uint8_t *buffer, int32_t length, int32_t sampleFrequency, double cutoff);
+bool lowPassSample16Bit(int16_t *buffer, int32_t length, int32_t sampleFrequency, double cutoff);
+bool lowPassSample32Bit(int32_t *buffer, int32_t length, int32_t sampleFrequency, double cutoff);
+bool lowPassSampleFloat(float *buffer, int32_t length, int32_t sampleFrequency, double cutoff);
+bool lowPassSampleDouble(double *buffer, int32_t length, int32_t sampleFrequency, double cutoff);
--- a/src/pt2_header.h
+++ b/src/pt2_header.h
@@ -13,7 +13,7 @@
#include <stdint.h>
#include "pt2_unicode.h"
-#define PROG_VER_STR "1.05"
+#define PROG_VER_STR "1.06"
#ifdef _WIN32
#define DIR_DELIMITER '\\'
@@ -193,7 +193,7 @@
ASK_MOD2WAV_OVERWRITE = 5,
ASK_SAVEMOD_OVERWRITE = 6,
ASK_SAVESMP_OVERWRITE = 7,
- ASK_DOWNSAMPLING = 8,
+ ASK_LOAD_DOWNSAMPLE = 8,
ASK_RESAMPLE = 9,
ASK_KILL_SAMPLE = 10,
ASK_UPSAMPLE = 11,
--- a/src/pt2_mouse.c
+++ b/src/pt2_mouse.c
@@ -771,8 +771,6 @@
if (modEntry->samples[editor.currSample].length == MAX_SAMPLE_LEN)
return;
- turnOffVoices();
-
val = modEntry->samples[editor.currSample].length;
if (input.mouse.rightButtonPressed)
{
@@ -813,8 +811,6 @@
return;
}
- turnOffVoices();
-
val = modEntry->samples[editor.currSample].length;
if (input.mouse.rightButtonPressed)
{
@@ -834,13 +830,14 @@
if (val < 0)
val = 0;
- s->length = val;
if (s->loopStart+s->loopLength > 2)
{
- if (s->length < s->loopStart+s->loopLength)
- s->length = s->loopStart+s->loopLength;
+ if (val < s->loopStart+s->loopLength)
+ val = s->loopStart+s->loopLength;
}
+ s->length = val;
+
editor.ui.updateCurrSampleLength = true;
}
@@ -2183,6 +2180,34 @@
{
handleSamplerFiltersBox();
return true;
+ }
+
+ // "downsample before loading sample" ask dialog
+ if (editor.ui.askScreenShown && editor.ui.askScreenType == ASK_LOAD_DOWNSAMPLE)
+ {
+ if (input.mouse.y >= 83 && input.mouse.y <= 93)
+ {
+ if (input.mouse.x >= 179 && input.mouse.x <= 204)
+ {
+ // YES button
+ editor.ui.askScreenShown = false;
+ editor.ui.answerNo = false;
+ editor.ui.answerYes = true;
+ handleAskYes();
+ return true;
+ }
+ else if (input.mouse.x >= 242 && input.mouse.x <= 260)
+ {
+ // NO button
+ editor.ui.askScreenShown = false;
+ editor.ui.answerNo = true;
+ editor.ui.answerYes = false;
+ handleAskNo();
+ return true;
+ }
+ }
+
+ return false;
}
// cancel note input gadgets with left/right mouse button
--- a/src/pt2_sampleloader.c
+++ b/src/pt2_sampleloader.c
@@ -25,6 +25,7 @@
#include "pt2_visuals.h"
#include "pt2_helpers.h"
#include "pt2_unicode.h"
+#include "pt2_filters.h"
enum
{
@@ -177,7 +178,7 @@
default: break;
}
- bytesRead += (chunkSize + (chunkSize & 1));
+ bytesRead += chunkSize + (chunkSize & 1);
fseek(f, endOfChunk, SEEK_SET);
}
@@ -238,11 +239,7 @@
{
if (forceDownSampling == -1)
{
- editor.ui.askScreenShown = true;
- editor.ui.askScreenType = ASK_DOWNSAMPLING;
- pointerSetMode(POINTER_MODE_MSG1, NO_CARRY);
- setStatusMessage("2X DOWNSAMPLING ?", NO_CARRY);
- renderAskDialog();
+ showDownsampleAskDialog();
fclose(f);
return true;
}
@@ -292,6 +289,9 @@
// 2x downsampling - remove every other sample (if needed)
if (forceDownSampling)
{
+ if (ptConfig.sampleLowpass)
+ lowPassSample8BitUnsigned(audioDataU8, sampleLength, sampleRate, sampleRate / DOWNSAMPLE_CUTOFF_FACTOR);
+
sampleLength /= 2;
for (i = 1; i < sampleLength; i++)
audioDataU8[i] = audioDataU8[i * 2];
@@ -348,6 +348,9 @@
// 2x downsampling - remove every other sample (if needed)
if (forceDownSampling)
{
+ if (ptConfig.sampleLowpass)
+ lowPassSample16Bit(audioDataS16, sampleLength, sampleRate, sampleRate / DOWNSAMPLE_CUTOFF_FACTOR);
+
sampleLength /= 2;
for (i = 1; i < sampleLength; i++)
audioDataS16[i] = audioDataS16[i * 2];
@@ -407,6 +410,9 @@
// 2x downsampling - remove every other sample (if needed)
if (forceDownSampling)
{
+ if (ptConfig.sampleLowpass)
+ lowPassSample32Bit(audioDataS32, sampleLength, sampleRate, sampleRate / DOWNSAMPLE_CUTOFF_FACTOR);
+
sampleLength /= 2;
for (i = 1; i < sampleLength; i++)
audioDataS32[i] = audioDataS32[i * 2];
@@ -467,6 +473,9 @@
// 2x downsampling - remove every other sample (if needed)
if (forceDownSampling)
{
+ if (ptConfig.sampleLowpass)
+ lowPassSample32Bit(audioDataS32, sampleLength, sampleRate, sampleRate / DOWNSAMPLE_CUTOFF_FACTOR);
+
sampleLength /= 2;
for (i = 1; i < sampleLength; i++)
audioDataS32[i] = audioDataS32[i * 2];
@@ -527,6 +536,9 @@
// 2x downsampling - remove every other sample (if needed)
if (forceDownSampling)
{
+ if (ptConfig.sampleLowpass)
+ lowPassSampleFloat(fAudioDataFloat, sampleLength, sampleRate, sampleRate / DOWNSAMPLE_CUTOFF_FACTOR);
+
sampleLength /= 2;
for (i = 1; i < sampleLength; i++)
fAudioDataFloat[i] = fAudioDataFloat[i * 2];
@@ -593,6 +605,9 @@
// 2x downsampling - remove every other sample (if needed)
if (forceDownSampling)
{
+ if (ptConfig.sampleLowpass)
+ lowPassSampleDouble(dAudioDataDouble, sampleLength, sampleRate, sampleRate / DOWNSAMPLE_CUTOFF_FACTOR);
+
sampleLength /= 2;
for (i = 1; i < sampleLength; i++)
dAudioDataDouble[i] = dAudioDataDouble[i * 2];
@@ -1234,11 +1249,7 @@
{
if (forceDownSampling == -1)
{
- editor.ui.askScreenShown = true;
- editor.ui.askScreenType = ASK_DOWNSAMPLING;
- pointerSetMode(POINTER_MODE_MSG1, NO_CARRY);
- setStatusMessage("2X DOWNSAMPLING ?", NO_CARRY);
- renderAskDialog();
+ showDownsampleAskDialog();
fclose(f);
return true;
}
@@ -1284,6 +1295,9 @@
// 2x downsampling - remove every other sample (if needed)
if (forceDownSampling)
{
+ if (ptConfig.sampleLowpass)
+ lowPassSample8Bit(audioDataS8, sampleLength, sampleRate, sampleRate / DOWNSAMPLE_CUTOFF_FACTOR);
+
sampleLength /= 2;
for (i = 1; i < sampleLength; i++)
audioDataS8[i] = audioDataS8[i * 2];
@@ -1344,6 +1358,9 @@
// 2x downsampling - remove every other sample (if needed)
if (forceDownSampling)
{
+ if (ptConfig.sampleLowpass)
+ lowPassSample16Bit(audioDataS16, sampleLength, sampleRate, sampleRate / DOWNSAMPLE_CUTOFF_FACTOR);
+
sampleLength /= 2;
for (i = 1; i < sampleLength; i++)
audioDataS16[i] = audioDataS16[i * 2];
@@ -1411,6 +1428,9 @@
// 2x downsampling - remove every other sample (if needed)
if (forceDownSampling)
{
+ if (ptConfig.sampleLowpass)
+ lowPassSample32Bit(audioDataS32, sampleLength, sampleRate, sampleRate / DOWNSAMPLE_CUTOFF_FACTOR);
+
sampleLength /= 2;
for (i = 1; i < sampleLength; i++)
audioDataS32[i] = audioDataS32[i * 2];
@@ -1475,6 +1495,9 @@
// 2x downsampling - remove every other sample (if needed)
if (forceDownSampling)
{
+ if (ptConfig.sampleLowpass)
+ lowPassSample32Bit(audioDataS32, sampleLength, sampleRate, sampleRate / DOWNSAMPLE_CUTOFF_FACTOR);
+
sampleLength /= 2;
for (i = 1; i < sampleLength; i++)
audioDataS32[i] = audioDataS32[i * 2];
--- a/src/pt2_sampler.c
+++ b/src/pt2_sampler.c
@@ -18,6 +18,11 @@
#include "pt2_mouse.h"
#include "pt2_scopes.h"
+#define CENTER_LINE_COLOR 0x303030
+#define MARK_COLOR_1 0x666666 /* inverted background */
+#define MARK_COLOR_2 0xCCCCCC /* inverted waveform */
+#define MARK_COLOR_3 0x7D7D7D /* inverted center line */
+
#define SAMPLE_AREA_Y_CENTER 169
#define SAMPLE_AREA_HEIGHT 64
@@ -28,6 +33,7 @@
} sampleMixer_t;
static int32_t samOffsetScaled;
+static uint32_t waveInvertTable[8];
static const int8_t tuneToneData[32] = // Tuning Tone (Sine Wave)
{
@@ -41,12 +47,26 @@
void setLoopSprites(void);
+void createSampleMarkTable(void)
+{
+ // used for invertRange() (sample data marking)
+
+ waveInvertTable[0] = 0x00000000 | palette[PAL_BACKGRD];
+ waveInvertTable[1] = 0x01000000 | palette[PAL_QADSCP];
+ waveInvertTable[2] = 0x02000000 | CENTER_LINE_COLOR;
+ waveInvertTable[3] = 0x03000000; // spacer, not used
+ waveInvertTable[4] = 0x04000000 | MARK_COLOR_1;
+ waveInvertTable[5] = 0x05000000 | MARK_COLOR_2;
+ waveInvertTable[6] = 0x06000000 | MARK_COLOR_3;
+ waveInvertTable[7] = 0x07000000; // spacer, not used
+}
+
static void updateSamOffset(void)
{
if (editor.sampler.samDisplay == 0)
samOffsetScaled = 0;
else
- samOffsetScaled = (editor.sampler.samOffset * SAMPLE_AREA_WIDTH) / editor.sampler.samDisplay;
+ samOffsetScaled = (editor.sampler.samOffset * SAMPLE_AREA_WIDTH) / editor.sampler.samDisplay; // truncate here
}
void fixSampleBeep(moduleSample_t *s)
@@ -86,9 +106,10 @@
}
}
-static void line(uint32_t *frameBuffer, int16_t line_x1, int16_t line_x2, int16_t line_y1, int16_t line_y2)
+static void sampleLine(uint32_t *frameBuffer, int16_t line_x1, int16_t line_x2, int16_t line_y1, int16_t line_y2)
{
int16_t d, x, y, ax, ay, sx, sy, dx, dy;
+ uint32_t color = 0x01000000 | palette[PAL_QADSCP];
assert(line_x1 >= 0 || line_x2 >= 0 || line_x1 < SCREEN_W || line_x2 < SCREEN_W);
assert(line_y1 >= 0 || line_y2 >= 0 || line_y1 < SCREEN_H || line_y2 < SCREEN_H);
@@ -109,7 +130,7 @@
{
assert(y >= 0 || x >= 0 || y < SCREEN_H || x < SCREEN_W);
- frameBuffer[(y * SCREEN_W) + x] = palette[PAL_QADSCP];
+ frameBuffer[(y * SCREEN_W) + x] = color;
if (x == line_x2)
break;
@@ -131,7 +152,7 @@
{
assert(y >= 0 || x >= 0 || y < SCREEN_H || x < SCREEN_W);
- frameBuffer[(y * SCREEN_W) + x] = palette[PAL_QADSCP];
+ frameBuffer[(y * SCREEN_W) + x] = color;
if (y == line_y2)
break;
@@ -150,21 +171,20 @@
static void setDragBar(void)
{
- int32_t pos32;
+ int32_t pos;
uint32_t *dstPtr, pixel, bgPixel;
- double dPos;
if (editor.sampler.samLength > 0 && editor.sampler.samDisplay != editor.sampler.samLength)
{
+ int32_t roundingBias = (uint32_t)editor.sampler.samLength / 2;
+
// update drag bar coordinates
- dPos = (editor.sampler.samOffset * 311.0) / editor.sampler.samLength;
- pos32 = (int32_t)(dPos + 0.5);
- editor.sampler.dragStart = 4 + (uint16_t)pos32;
+ pos = ((editor.sampler.samOffset * 311) + roundingBias) / editor.sampler.samLength;
+ editor.sampler.dragStart = pos + 4;
editor.sampler.dragStart = CLAMP(editor.sampler.dragStart, 4, 315);
- dPos = ((editor.sampler.samDisplay + editor.sampler.samOffset) * 311.0) / editor.sampler.samLength;
- pos32 = (int32_t)(dPos + 0.5);
- editor.sampler.dragEnd = 5 + (uint16_t)pos32;
+ pos = (((editor.sampler.samDisplay + editor.sampler.samOffset) * 311) + roundingBias) / editor.sampler.samLength;
+ editor.sampler.dragEnd = pos + 5;
editor.sampler.dragEnd = CLAMP(editor.sampler.dragEnd, 5, 316);
if (editor.sampler.dragStart > editor.sampler.dragEnd-1)
@@ -222,13 +242,12 @@
int32_t smpPos2Scr(int32_t pos) // sample pos -> screen x pos
{
- double dPos;
-
if (editor.sampler.samDisplay == 0)
return 0;
- dPos = (pos * (double)SAMPLE_AREA_WIDTH) / editor.sampler.samDisplay;
- pos = (int32_t)(dPos + 0.5);
+ uint32_t roundingBias = (uint32_t)editor.sampler.samDisplay >> 1;
+
+ pos = (((uint32_t)pos * SAMPLE_AREA_WIDTH) + roundingBias) / (uint32_t)editor.sampler.samDisplay; // rounded
pos -= samOffsetScaled;
return pos;
@@ -243,7 +262,7 @@
x = 0;
x += samOffsetScaled;
- x = (x * editor.sampler.samDisplay) / SAMPLE_AREA_WIDTH;
+ x = (uint32_t)(x * editor.sampler.samDisplay) / SAMPLE_AREA_WIDTH; // truncate here
return x;
}
@@ -291,7 +310,11 @@
// display center line
if (ptConfig.dottedCenterFlag)
- memset(&pixelBuffer[(SAMPLE_AREA_Y_CENTER * SCREEN_W) + 3], 0x373737, SAMPLE_AREA_WIDTH * sizeof (int32_t));
+ {
+ dstPtr = &pixelBuffer[(SAMPLE_AREA_Y_CENTER * SCREEN_W) + 3];
+ for (x = 0; x < SAMPLE_AREA_WIDTH; x++)
+ dstPtr[x] = 0x02000000 | CENTER_LINE_COLOR;
+ }
// render sample data
if (editor.sampler.samDisplay >= 0 && editor.sampler.samDisplay <= MAX_SAMPLE_LEN)
@@ -304,7 +327,7 @@
for (x = 1; x < SAMPLE_AREA_WIDTH; x++)
{
y2 = SAMPLE_AREA_Y_CENTER - getScaledSample(scr2SmpPos(x));
- line(pixelBuffer, x + 2, x + 3, y1, y2);
+ sampleLine(pixelBuffer, x + 2, x + 3, y1, y2);
y1 = y2;
}
}
@@ -332,11 +355,11 @@
if (x > 0)
{
- if (min > oldMax) line(pixelBuffer, x + 2, x + 3, oldMax, min);
- if (max < oldMin) line(pixelBuffer, x + 2, x + 3, oldMin, max);
+ if (min > oldMax) sampleLine(pixelBuffer, x + 2, x + 3, oldMax, min);
+ if (max < oldMin) sampleLine(pixelBuffer, x + 2, x + 3, oldMin, max);
}
- line(pixelBuffer, x + 3, x + 3, max, min);
+ sampleLine(pixelBuffer, x + 3, x + 3, max, min);
oldMin = min;
oldMax = max;
@@ -356,8 +379,8 @@
void invertRange(void)
{
- int32_t x, y, rangeLen, dstPitch, start, end;
- uint32_t *dstPtr, pixel1, pixel2;
+ int32_t x, y, rangeLen, start, end;
+ uint32_t *dstPtr;
if (editor.markStartOfs == -1)
return; // no marking
@@ -366,34 +389,22 @@
end = smpPos2Scr(editor.markEndOfs);
if (editor.sampler.samDisplay < editor.sampler.samLength && (start >= SAMPLE_AREA_WIDTH || end < 0))
- return; // range is outside of view (passed it by scrolling)
+ return; // range is outside of view
- start = CLAMP(start, 0, SAMPLE_AREA_WIDTH - 1);
- end = CLAMP(end, 0, SAMPLE_AREA_WIDTH - 1);
+ start = CLAMP(start, 0, SAMPLE_AREA_WIDTH-1);
+ end = CLAMP(end, 0, SAMPLE_AREA_WIDTH-1);
rangeLen = (end + 1) - start;
if (rangeLen < 1)
rangeLen = 1;
- dstPtr = &pixelBuffer[(138 * SCREEN_W) + (3 + start)];
- dstPitch = SCREEN_W - rangeLen;
- pixel1 = palette[PAL_BACKGRD];
- pixel2 = palette[PAL_QADSCP];
-
+ dstPtr = &pixelBuffer[(138 * SCREEN_W) + (start + 3)];
for (y = 0; y < 64; y++)
{
for (x = 0; x < rangeLen; x++)
- {
- // this is stupid...
- if (*dstPtr == pixel1) *dstPtr = 0x666666;
- else if (*dstPtr == 0x666666) *dstPtr = pixel1;
- else if (*dstPtr == 0xCCCCCC) *dstPtr = pixel2;
- else if (*dstPtr == pixel2) *dstPtr = 0xCCCCCC;
+ dstPtr[x] = waveInvertTable[((dstPtr[x] >> 24) & 7) ^ 4]; // It's magic! ptr[x]>>24 = wave/invert color number
- dstPtr++;
- }
-
- dstPtr += dstPitch;
+ dstPtr += SCREEN_W;
}
}
@@ -454,7 +465,7 @@
void highPassSample(int32_t cutOff)
{
int32_t smp32, i, from, to;
- double *dSampleData, dBaseFreq, dCutOff, dIn[2], dOut[2];
+ double *dSampleData, dBaseFreq, dCutOff;
moduleSample_t *s;
lossyIntegrator_t filterHi;
@@ -517,30 +528,13 @@
for (i = 0; i < s->length; i++)
dSampleData[i] = modEntry->sampleData[s->offset+i];
- // filter forwards
filterHi.dBuffer[0] = 0.0;
if (to <= s->length)
{
for (i = from; i < to; i++)
- {
- dIn[0] = dSampleData[i];
- lossyIntegratorHighPass(&filterHi, dIn, dOut);
- dSampleData[i] = dOut[0];
- }
+ lossyIntegratorHighPassMono(&filterHi, dSampleData[i], &dSampleData[i]);
}
- // filter backwards
- filterHi.dBuffer[0] = 0.0;
- if (to <= s->length)
- {
- for (i = to-1; i >= from; i--)
- {
- dIn[0] = dSampleData[i];
- lossyIntegratorHighPass(&filterHi, dIn, dOut);
- dSampleData[i] = dOut[0];
- }
- }
-
if (editor.normalizeFiltersFlag)
normalize8bitDoubleSigned(dSampleData, s->length);
@@ -561,7 +555,7 @@
void lowPassSample(int32_t cutOff)
{
int32_t smp32, i, from, to;
- double *dSampleData, dBaseFreq, dCutOff, dIn[2], dOut[2];
+ double *dSampleData, dBaseFreq, dCutOff;
moduleSample_t *s;
lossyIntegrator_t filterLo;
@@ -624,30 +618,13 @@
for (i = 0; i < s->length; i++)
dSampleData[i] = modEntry->sampleData[s->offset+i];
- // filter forwards
filterLo.dBuffer[0] = 0.0;
if (to <= s->length)
{
for (i = from; i < to; i++)
- {
- dIn[0] = dSampleData[i];
- lossyIntegrator(&filterLo, dIn, dOut);
- dSampleData[i] = dOut[0];
- }
+ lossyIntegratorMono(&filterLo, dSampleData[i], &dSampleData[i]);
}
- // filter backwards
- filterLo.dBuffer[0] = 0.0;
- if (to <= s->length)
- {
- for (i = to-1; i >= from; i--)
- {
- dIn[0] = dSampleData[i];
- lossyIntegrator(&filterLo, dIn, dOut);
- dSampleData[i] = dOut[0];
- }
- }
-
if (editor.normalizeFiltersFlag)
normalize8bitDoubleSigned(dSampleData, s->length);
@@ -1491,7 +1468,7 @@
}
else
{
- middlePos = editor.sampler.samOffset + (int32_t)((editor.sampler.samDisplay / 2.0) + 0.5);
+ middlePos = editor.sampler.samOffset + ((editor.sampler.samDisplay + 1) / 2);
invertRange();
if (input.keyb.shiftPressed && editor.markStartOfs != -1)
@@ -2047,8 +2024,10 @@
if (tmpDisplay < 2)
tmpDisplay = 2;
- step += (((x - (SCREEN_W / 2)) * step) / (SCREEN_W / 2));
+ const int32_t roundingBias = SCREEN_W / 4;
+ step += (((x - (SCREEN_W / 2)) * step) + roundingBias) / (SCREEN_W / 2);
+
tmpOffset = editor.sampler.samOffset + step;
if (tmpOffset < 0)
tmpOffset = 0;
@@ -2081,8 +2060,10 @@
}
else
{
- step += (((x - (SCREEN_W / 2)) * step) / (SCREEN_W / 2));
+ const int32_t roundingBias = SCREEN_W / 4;
+ step += (((x - (SCREEN_W / 2)) * step) + roundingBias) / (SCREEN_W / 2);
+
tmpOffset = editor.sampler.samOffset - step;
if (tmpOffset < 0)
tmpOffset = 0;
@@ -2100,20 +2081,17 @@
void samplerZoomInMouseWheel(void)
{
- int32_t step = (int32_t)((editor.sampler.samDisplay / 10.0f) + 0.5f);
- samplerZoomIn(step, input.mouse.x);
+ samplerZoomIn((editor.sampler.samDisplay + 5) / 10, input.mouse.x);
}
void samplerZoomOutMouseWheel(void)
{
- int32_t step = (int32_t)((editor.sampler.samDisplay / 10.0f) + 0.5f);
- samplerZoomOut(step, input.mouse.x);
+ samplerZoomOut((editor.sampler.samDisplay + 5) / 10, input.mouse.x);
}
void samplerZoomOut2x(void)
{
- int32_t step = (int32_t)((editor.sampler.samDisplay / 2.0f) + 0.5f);
- samplerZoomOut(step, SCREEN_W / 2);
+ samplerZoomOut((editor.sampler.samDisplay + 1) / 2, SCREEN_W / 2);
}
void samplerRangeAll(void)
@@ -2200,13 +2178,13 @@
if (editor.ui.forceVolDrag == 1)
{
- editor.vol1 = (int16_t)((mouseX * 200) / 60);
+ editor.vol1 = (int16_t)(((mouseX * 200) + (60/2)) / 60); // rounded
editor.ui.updateVolFromText = true;
showVolFromSlider();
}
else if (editor.ui.forceVolDrag == 2)
{
- editor.vol2 = (int16_t)((mouseX * 200) / 60);
+ editor.vol2 = (int16_t)(((mouseX * 200) + (60/2)) / 60); // rounded
editor.ui.updateVolToText = true;
showVolToSlider();
}
@@ -2280,7 +2258,7 @@
if (tmp32 < 0)
tmp32 = 0;
- tmp32 = (int32_t)(((tmp32 * editor.sampler.samLength) / 311.0) + 0.5);
+ tmp32 = (int32_t)(((tmp32 * editor.sampler.samLength) + (311/2)) / 311); // rounded
if (tmp32+editor.sampler.samDisplay <= editor.sampler.samLength)
{
if (tmp32 == editor.sampler.samOffset)
--- a/src/pt2_sampler.h
+++ b/src/pt2_sampler.h
@@ -3,6 +3,7 @@
#include <stdint.h>
#include <stdbool.h>
+void createSampleMarkTable(void);
int32_t smpPos2Scr(int32_t pos);
int32_t scr2SmpPos(int32_t x);
void fixSampleBeep(moduleSample_t *s);
--- a/src/pt2_tables.c
+++ b/src/pt2_tables.c
@@ -9,7 +9,7 @@
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;
+uint32_t *yesNoDialogBMP = NULL, *bigYesNoDialogBMP = NULL;
const uint32_t cursorColors[6][3] =
{
--- a/src/pt2_tables.h
+++ b/src/pt2_tables.h
@@ -36,6 +36,7 @@
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
@@ -53,6 +54,7 @@
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;
--- a/src/pt2_visuals.c
+++ b/src/pt2_visuals.c
@@ -214,6 +214,42 @@
}
}
+void renderBigAskDialog(void)
+{
+ const uint32_t *srcPtr;
+ uint32_t *dstPtr;
+
+ editor.ui.disablePosEd = true;
+ editor.ui.disableVisualizer = true;
+
+ // render custom big ask dialog
+
+ srcPtr = bigYesNoDialogBMP;
+ dstPtr = &pixelBuffer[(44 * SCREEN_W) + 120];
+
+ for (uint32_t y = 0; y < 55; y++)
+ {
+ memcpy(dstPtr, srcPtr, 200 * sizeof (int32_t));
+
+ srcPtr += 200;
+ dstPtr += SCREEN_W;
+ }
+}
+
+void showDownsampleAskDialog(void)
+{
+ editor.ui.askScreenShown = true;
+ editor.ui.askScreenType = ASK_LOAD_DOWNSAMPLE;
+ pointerSetMode(POINTER_MODE_MSG1, NO_CARRY);
+ setStatusMessage("PLEASE SELECT", NO_CARRY);
+ renderBigAskDialog();
+
+ textOutTight(pixelBuffer, 133, 49, "THE SAMPLE'S FREQUENCY IS", palette[PAL_BACKGRD]);
+ textOutTight(pixelBuffer, 178, 57, "ABOVE 22KHZ.", palette[PAL_BACKGRD]);
+ textOutTight(pixelBuffer, 133, 65, "DO YOU WANT TO DOWNSAMPLE", palette[PAL_BACKGRD]);
+ textOutTight(pixelBuffer, 156, 73, "BEFORE LOADING IT?", palette[PAL_BACKGRD]);
+}
+
static void fillFromVuMetersBgBuffer(void)
{
const uint32_t *srcPtr;
@@ -594,7 +630,7 @@
{
if (!editor.ui.samplerVolBoxShown && !editor.ui.samplerFiltersBoxShown && s->length > 0)
{
- tmpSampleOffset = (scr2SmpPos(input.mouse.x - 3) + 128) >> 8;
+ tmpSampleOffset = (scr2SmpPos(input.mouse.x-3) + (1 << 7)) >> 8; // rounded
tmpSampleOffset = 0x900 + CLAMP(tmpSampleOffset, 0x00, 0xFF);
if (tmpSampleOffset != editor.ui.lastSampleOffset)
@@ -674,7 +710,7 @@
{
uint32_t *dstPtr, pixel, bgPixel, sliderStart, sliderEnd;
- sliderStart = (editor.vol1 * 3) / 10;
+ sliderStart = ((editor.vol1 * 3) + 5) / 10;
sliderEnd = sliderStart + 4;
pixel = palette[PAL_QADSCP];
bgPixel = palette[PAL_BACKGRD];
@@ -698,7 +734,7 @@
{
uint32_t *dstPtr, pixel, bgPixel, sliderStart, sliderEnd;
- sliderStart = (editor.vol2 * 3) / 10;
+ sliderStart = ((editor.vol2 * 3) + 5) / 10;
sliderEnd = sliderStart + 4;
pixel = palette[PAL_QADSCP];
bgPixel = palette[PAL_BACKGRD];
@@ -1069,6 +1105,7 @@
pixel = palette[PAL_GENBKG2];
for (uint32_t x = 0; x < 7; x++)
dstPtr[x] = pixel;
+
// no need to clear the second row of pixels
editor.ui.updatePosEd = true;
@@ -1148,7 +1185,7 @@
if (y < tmpVol)
pixel = srcPtr[y];
- for (uint32_t x = 0; x < SPECTRUM_BAR_WIDTH; x++)
+ for (uint32_t x = 0; x < SPECTRUM_BAR_WIDTH; x++)
dstPtr[x] = pixel;
dstPtr += SCREEN_W;
@@ -1223,7 +1260,7 @@
// draw version string
sprintf(verString, "v%s", PROG_VER_STR);
- verStringX = 260 + (63 - ((uint32_t)strlen(verString) * (FONT_CHAR_W - 1))) / 2;
+ verStringX = 260 + (((63 - ((uint32_t)strlen(verString) * (FONT_CHAR_W - 1))) + 1) / 2);
textOutTight(pixelBuffer, verStringX, 67, verString, palette[PAL_GENBKG2]);
}
@@ -1393,7 +1430,7 @@
if (percent > 100)
percent = 100;
- barLength = (percent * 180) / 100;
+ barLength = ((percent * 180) + 50) / 100;
dstPtr = &pixelBuffer[(42 * SCREEN_W) + 70];
pixel = palette[PAL_GENBKG2];
bgPixel = palette[PAL_BORDER];
@@ -1401,8 +1438,14 @@
for (int32_t y = 0; y < 11; y++)
{
for (int32_t x = 0; x < 180; x++)
- dstPtr[x] = (x < barLength) ? pixel : bgPixel;
+ {
+ uint32_t color = bgPixel;
+ if (x < barLength)
+ color = pixel;
+ dstPtr[x] = color;
+ }
+
dstPtr += SCREEN_W;
}
@@ -1690,7 +1733,7 @@
}
break;
- case ASK_DOWNSAMPLING:
+ case ASK_LOAD_DOWNSAMPLE:
{
restoreStatusAndMousePointer();
extLoadWAVOrAIFFSampleCallback(DONT_DOWNSAMPLE);
@@ -2021,7 +2064,7 @@
}
break;
- case ASK_DOWNSAMPLING:
+ case ASK_LOAD_DOWNSAMPLE:
{
// for WAV and AIFF sample loader
restoreStatusAndMousePointer();
@@ -2273,6 +2316,7 @@
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);
@@ -2366,6 +2410,7 @@
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));
@@ -2383,7 +2428,7 @@
editOpScreen1BMP == NULL || editOpScreen2BMP == NULL || editOpScreen3BMP == NULL ||
editOpScreen4BMP == NULL || aboutScreenBMP == NULL || muteButtonsBMP == NULL ||
editOpModeCharsBMP == NULL || arrowBMP == NULL || samplerFiltersBMP == NULL ||
- yesNoDialogBMP == NULL)
+ yesNoDialogBMP == NULL || bigYesNoDialogBMP == NULL)
{
showErrorMsgBox("Out of memory!");
return false; // BMPs are free'd in cleanUp()
--- a/src/pt2_visuals.h
+++ b/src/pt2_visuals.h
@@ -42,6 +42,8 @@
void createBitmaps(void);
void displayMainScreen(void);
void renderAskDialog(void);
+void renderBigAskDialog(void);
+void showDownsampleAskDialog(void);
void renderPosEdScreen(void);
void renderDiskOpScreen(void);
void renderMuteButtons(void);
--- a/vs2019_project/pt2-clone/protracker.ini
+++ b/vs2019_project/pt2-clone/protracker.ini
@@ -221,4 +221,18 @@
;
STEREOSEPARATION=20
+
+; Low-pass samples before getting 2x downsampled during loading (if requested)
+; Syntax: TRUE or FALSE
+; Default value: TRUE
+; Comment: Set to false if you want slightly sharper sound when loading
+; samples that are 2x downsampled (if requested).
+; Keep in mind that you might get more aliasing in the sound if you
+; disable this, and certain sounds with a lot of treble might sound
+; weird (f.ex. hi-hats and cymbals). If the sample you load have a
+; frequency below 22kHz, it will never be downsampled (and thus this
+; setting changes nothing). I recommend keeping this set to TRUE.
+;
+SAMPLELOWPASS=TRUE
+
; End of config file
\ No newline at end of file
--- a/vs2019_project/pt2-clone/pt2-clone.vcxproj
+++ b/vs2019_project/pt2-clone/pt2-clone.vcxproj
@@ -284,6 +284,7 @@
<ClInclude Include="..\..\src\pt2_config.h" />
<ClInclude Include="..\..\src\pt2_diskop.h" />
<ClInclude Include="..\..\src\pt2_edit.h" />
+ <ClInclude Include="..\..\src\pt2_filters.h" />
<ClInclude Include="..\..\src\pt2_header.h" />
<ClInclude Include="..\..\src\pt2_helpers.h" />
<ClInclude Include="..\..\src\pt2_keyboard.h" />
@@ -326,6 +327,7 @@
<ClCompile Include="..\..\src\pt2_config.c" />
<ClCompile Include="..\..\src\pt2_diskop.c" />
<ClCompile Include="..\..\src\pt2_edit.c" />
+ <ClCompile Include="..\..\src\pt2_filters.c" />
<ClCompile Include="..\..\src\pt2_helpers.c" />
<ClCompile Include="..\..\src\pt2_keyboard.c" />
<ClCompile Include="..\..\src\pt2_main.c" />
--- a/vs2019_project/pt2-clone/pt2-clone.vcxproj.filters
+++ b/vs2019_project/pt2-clone/pt2-clone.vcxproj.filters
@@ -66,6 +66,9 @@
<ClInclude Include="..\..\src\pt2_visuals.h">
<Filter>headers</Filter>
</ClInclude>
+ <ClInclude Include="..\..\src\pt2_filters.h">
+ <Filter>headers</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\src\pt2_audio.c" />
@@ -145,6 +148,7 @@
<ClCompile Include="..\..\src\gfx\pt2_gfx_yes_no_dialog.c">
<Filter>gfx</Filter>
</ClCompile>
+ <ClCompile Include="..\..\src\pt2_filters.c" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\..\src\pt2-clone.rc" />