ref: 7442f4167c59bc041ba46fd0b0fee592a5de282f
parent: 9fca319758c1194a953ad582a9c55e0a0f873ee4
author: Olav Sørensen <[email protected]>
date: Sun Apr 19 14:10:48 EDT 2020
Pushed v1.10 code - Bugfix: note(s) displayed in SAMPLER (resample note) and Edit Op. screen #4 were wrong. - After a long talk with aciddose I have found out that I did several things wrong in the Amiga filtering. I have now removed the filter cutoff tweaking, gotten a slightly improved low-pass/high-pass routine, and also replaced the naive "LED" filter implementation with another one that is slightly more accurate (but still not perfect).
--- a/release/macos/protracker.ini
+++ b/release/macos/protracker.ini
@@ -180,10 +180,9 @@
; Audio output frequency
; Syntax: Number, in hertz
; Default value: 48000
-; Comment: Ranges from 32000 to 96000.
+; Comment: Ranges from 44100 to 192000.
; Also sets the playback frequency for WAVs made with MOD2WAV.
-; Note to coders: Don't allow lower numbers than 32000, it will
-; break the BLEP synthesis.
+;
FREQUENCY=48000
; Audio buffer size
--- a/release/other/protracker.ini
+++ b/release/other/protracker.ini
@@ -180,10 +180,9 @@
; Audio output frequency
; Syntax: Number, in hertz
; Default value: 48000
-; Comment: Ranges from 32000 to 96000.
+; Comment: Ranges from 44100 to 192000.
; Also sets the playback frequency for WAVs made with MOD2WAV.
-; Note to coders: Don't allow lower numbers than 32000, it will
-; break the BLEP synthesis.
+;
FREQUENCY=48000
; Audio buffer size
--- a/release/win32/protracker.ini
+++ b/release/win32/protracker.ini
@@ -180,10 +180,9 @@
; Audio output frequency
; Syntax: Number, in hertz
; Default value: 48000
-; Comment: Ranges from 32000 to 96000.
+; Comment: Ranges from 44100 to 192000.
; Also sets the playback frequency for WAVs made with MOD2WAV.
-; Note to coders: Don't allow lower numbers than 32000, it will
-; break the BLEP synthesis.
+;
FREQUENCY=48000
; Audio buffer size
--- a/release/win64/protracker.ini
+++ b/release/win64/protracker.ini
@@ -180,10 +180,9 @@
; Audio output frequency
; Syntax: Number, in hertz
; Default value: 48000
-; Comment: Ranges from 32000 to 96000.
+; Comment: Ranges from 44100 to 192000.
; Also sets the playback frequency for WAVs made with MOD2WAV.
-; Note to coders: Don't allow lower numbers than 32000, it will
-; break the BLEP synthesis.
+;
FREQUENCY=48000
; Audio buffer size
--- a/src/pt2_audio.c
+++ b/src/pt2_audio.c
@@ -1,5 +1,4 @@
-/* The "LED" filter and BLEP routines were coded by aciddose.
-** Low-pass filter is based on https://bel.fi/alankila/modguide/interpolate.txt */
+// the audio filters and BLEP synthesis were coded by aciddose
// for finding memory leaks in debug mode with Visual Studio
#if defined _DEBUG && defined _MSC_VER
@@ -36,14 +35,10 @@
typedef struct ledFilter_t
{
- double dLed[4];
+ double buffer[4];
+ double c, ci, feedback, bg, cg, c2;
} ledFilter_t;
-typedef struct ledFilterCoeff_t
-{
- double dLed, dLedFb;
-} ledFilterCoeff_t;
-
typedef struct voice_t
{
volatile bool active;
@@ -56,13 +51,12 @@
static int8_t defStereoSep;
static bool amigaPanFlag, wavRenderingDone;
static uint16_t ch1Pan, ch2Pan, ch3Pan, ch4Pan;
-static int32_t oldPeriod, sampleCounter, maxSamplesToMix, randSeed = INITIAL_DITHER_SEED;
+static int32_t oldPeriod, sampleCounter, randSeed = INITIAL_DITHER_SEED;
static uint32_t oldScopeDelta;
static double *dMixBufferL, *dMixBufferR, *dMixBufferLUnaligned, *dMixBufferRUnaligned, dOldVoiceDelta, dOldVoiceDeltaMul;
static double dPrngStateL, dPrngStateR;
static blep_t blep[AMIGA_VOICES], blepVol[AMIGA_VOICES];
-static lossyIntegrator_t filterLo, filterHi;
-static ledFilterCoeff_t filterLEDC;
+static rcFilter_t filterLo, filterHi;
static ledFilter_t filterLED;
static paulaVoice_t paula[AMIGA_VOICES];
static SDL_AudioDeviceID dev;
@@ -77,16 +71,13 @@
static uint16_t bpm2SmpsPerTick(uint32_t bpm, uint32_t audioFreq)
{
- uint32_t ciaVal;
- double dFreqMul;
-
if (bpm == 0)
return 0;
- ciaVal = (uint32_t)(1773447 / bpm); // yes, PT truncates here
- dFreqMul = ciaVal * (1.0 / CIA_PAL_CLK);
+ const uint32_t ciaVal = (uint32_t)(1773447 / bpm); // yes, PT truncates here
+ const double dCiaHz = (double)CIA_PAL_CLK / ciaVal;
- int32_t smpsPerTick = (int32_t)((audioFreq * dFreqMul) + 0.5);
+ int32_t smpsPerTick = (int32_t)((audioFreq / dCiaHz) + 0.5);
return (uint16_t)smpsPerTick;
}
@@ -94,133 +85,184 @@
{
for (uint32_t i = 32; i <= 255; i++)
{
- audio.bpmTab[i-32] = bpm2SmpsPerTick(i, audio.audioFreq);
+ audio.bpmTab[i-32] = bpm2SmpsPerTick(i, audio.outputRate);
audio.bpmTab28kHz[i-32] = bpm2SmpsPerTick(i, 28836);
audio.bpmTab22kHz[i-32] = bpm2SmpsPerTick(i, 22168);
}
}
+static void clearLEDFilterState(void)
+{
+ filterLED.buffer[0] = 0.0; // left channel
+ filterLED.buffer[1] = 0.0;
+ filterLED.buffer[2] = 0.0; // right channel
+ filterLED.buffer[3] = 0.0;
+}
+
void setLEDFilter(bool state)
{
editor.useLEDFilter = state;
-
if (editor.useLEDFilter)
- filterFlags |= FILTER_LED_ENABLED;
+ {
+ clearLEDFilterState();
+ filterFlags |= FILTER_LED_ENABLED;
+ }
else
+ {
filterFlags &= ~FILTER_LED_ENABLED;
+ }
}
void toggleLEDFilter(void)
{
editor.useLEDFilter ^= 1;
-
if (editor.useLEDFilter)
- filterFlags |= FILTER_LED_ENABLED;
+ {
+ clearLEDFilterState();
+ filterFlags |= FILTER_LED_ENABLED;
+ }
else
+ {
filterFlags &= ~FILTER_LED_ENABLED;
+ }
}
-static void calcCoeffLED(double dSr, double dHz, ledFilterCoeff_t *filter)
+/* Imperfect "LED" filter implementation. This may be further improved in the future.
+** Based upon ideas posted by mystran @ the kvraudio.com forum.
+**
+** This filter may not function correctly used outside the fixed-cutoff context here!
+*/
+
+static double sigmoid(double x, double coefficient)
{
- static double dFb = 0.125;
+ /* Coefficient from:
+ ** 0.0 to inf (linear)
+ ** -1.0 to -inf (linear)
+ */
+ return x / (x + coefficient) * (coefficient + 1.0);
+}
-#ifndef NO_FILTER_FINETUNING
- /* 8bitbubsy: makes the filter curve sound (and look) much closer to the real deal.
- ** This has been tested against both an A500 and A1200 (real units).
+static void calcLEDFilterCoeffs(const double sr, const double hz, const double fb, ledFilter_t *filter)
+{
+ /* tan() may produce NaN or other bad results in some cases!
+ ** It appears to work correctly with these specific coefficients.
*/
- dFb *= 0.62;
-#endif
+ const double c = (hz < (sr / 2.0)) ? tan((M_PI * hz) / sr) : 1.0;
+ const double g = 1.0 / (1.0 + c);
- if (dHz < dSr/2.0)
- filter->dLed = ((2.0 * M_PI) * dHz) / dSr;
- else
- filter->dLed = 1.0;
+ // dirty compensation
+ const double s = 0.5;
+ const double t = 0.5;
+ const double ic = c > t ? 1.0 / ((1.0 - s*t) + s*c) : 1.0;
+ const double cg = c * g;
+ const double fbg = 1.0 / (1.0 + fb * cg*cg);
- filter->dLedFb = dFb + (dFb / (1.0 - filter->dLed)); // Q ~= 1/sqrt(2) (Butterworth)
+ filter->c = c;
+ filter->ci = g;
+ filter->feedback = 2.0 * sigmoid(fb, 0.5);
+ filter->bg = fbg * filter->feedback * ic;
+ filter->cg = cg;
+ filter->c2 = c * 2.0;
}
-void calcCoeffLossyIntegrator(double dSr, double dHz, lossyIntegrator_t *filter)
+static inline void LEDFilter(ledFilter_t *f, const double *in, double *out)
{
- double dOmega = ((2.0 * M_PI) * dHz) / dSr;
- filter->b0 = 1.0 / (1.0 + (1.0 / dOmega));
- filter->b1 = 1.0 - filter->b0;
+ const double in_1 = DENORMAL_OFFSET;
+ const double in_2 = DENORMAL_OFFSET;
+
+ const double c = f->c;
+ const double g = f->ci;
+ const double cg = f->cg;
+ const double bg = f->bg;
+ const double c2 = f->c2;
+
+ double *v = f->buffer;
+
+ // left channel
+ const double estimate_L = in_2 + g*(v[1] + c*(in_1 + g*(v[0] + c*in[0])));
+ const double y0_L = v[0]*g + in[0]*cg + in_1 + estimate_L * bg;
+ const double y1_L = v[1]*g + y0_L*cg + in_2;
+
+ v[0] += c2 * (in[0] - y0_L);
+ v[1] += c2 * (y0_L - y1_L);
+ out[0] = y1_L;
+
+ // right channel
+ const double estimate_R = in_2 + g*(v[3] + c*(in_1 + g*(v[2] + c*in[1])));
+ const double y0_R = v[2]*g + in[1]*cg + in_1 + estimate_R * bg;
+ const double y1_R = v[3]*g + y0_R*cg + in_2;
+
+ v[2] += c2 * (in[1] - y0_R);
+ v[3] += c2 * (y0_R - y1_R);
+ out[1] = y1_R;
}
-static void clearLossyIntegrator(lossyIntegrator_t *filter)
+void calcRCFilterCoeffs(double dSr, double dHz, rcFilter_t *f)
{
- filter->dBuffer[0] = 0.0; // L
- filter->dBuffer[1] = 0.0; // R
+ f->c = tan((M_PI * dHz) / dSr);
+ f->c2 = f->c * 2.0;
+ f->g = 1.0 / (1.0 + f->c);
+ f->cg = f->c * f->g;
}
-static void clearLEDFilter(ledFilter_t *filter)
+void clearRCFilterState(rcFilter_t *f)
{
- filter->dLed[0] = 0.0; // L
- filter->dLed[1] = 0.0;
- filter->dLed[2] = 0.0; // R
- filter->dLed[3] = 0.0;
+ f->buffer[0] = 0.0; // left channel
+ f->buffer[1] = 0.0; // right channel
}
-static inline void lossyIntegratorLED(ledFilterCoeff_t filterC, ledFilter_t *filter, double *dIn, double *dOut)
+// aciddose: input 0 is resistor side of capacitor (low-pass), input 1 is reference side (high-pass)
+static inline double getLowpassOutput(rcFilter_t *f, const double input_0, const double input_1, const double buffer)
{
- // left channel "LED" filter
- filter->dLed[0] += filterC.dLed * (dIn[0] - filter->dLed[0])
- + filterC.dLedFb * (filter->dLed[0] - filter->dLed[1]) + DENORMAL_OFFSET;
- filter->dLed[1] += filterC.dLed * (filter->dLed[0] - filter->dLed[1]) + DENORMAL_OFFSET;
- dOut[0] = filter->dLed[1];
-
- // right channel "LED" filter
- filter->dLed[2] += filterC.dLed * (dIn[1] - filter->dLed[2])
- + filterC.dLedFb * (filter->dLed[2] - filter->dLed[3]) + DENORMAL_OFFSET;
- filter->dLed[3] += filterC.dLed * (filter->dLed[2] - filter->dLed[3]) + DENORMAL_OFFSET;
- dOut[1] = filter->dLed[3];
+ return buffer * f->g + input_0 * f->cg + input_1 * (1.0 - f->cg);
}
-void lossyIntegrator(lossyIntegrator_t *filter, double *dIn, double *dOut)
+void RCLowPassFilter(rcFilter_t *f, const double *in, double *out)
{
- /* Low-pass filter implementation taken from:
- ** https://bel.fi/alankila/modguide/interpolate.txt
- **
- ** This implementation has a less smooth cutoff curve compared to the old one, so it's
- ** maybe not the best. However, I stick to this one because it has a higher gain
- ** at the end of the curve (closer to real tested Amiga 500). It also sounds much closer when
- ** comparing whitenoise on an A500.
- */
+ double output;
- // left channel low-pass
- filter->dBuffer[0] = (filter->b0 * dIn[0]) + (filter->b1 * filter->dBuffer[0]) + DENORMAL_OFFSET;
- dOut[0] = filter->dBuffer[0];
+ // left channel RC low-pass
+ output = getLowpassOutput(f, in[0], 0.0, f->buffer[0]);
+ f->buffer[0] += (in[0] - output) * f->c2;
+ out[0] = output;
- // right channel low-pass
- filter->dBuffer[1] = (filter->b0 * dIn[1]) + (filter->b1 * filter->dBuffer[1]) + DENORMAL_OFFSET;
- dOut[1] = filter->dBuffer[1];
+ // right channel RC low-pass
+ output = getLowpassOutput(f, in[1], 0.0, f->buffer[1]);
+ f->buffer[1] += (in[1] - output) * f->c2;
+ out[1] = output;
}
-void lossyIntegratorMono(lossyIntegrator_t *filter, double dIn, double *dOut)
+void RCHighPassFilter(rcFilter_t *f, const double *in, double *out)
{
- filter->dBuffer[0] = (filter->b0 * dIn) + (filter->b1 * filter->dBuffer[0]) + DENORMAL_OFFSET;
- *dOut = filter->dBuffer[0];
-}
+ double low[2];
-void lossyIntegratorHighPass(lossyIntegrator_t *filter, double *dIn, double *dOut)
-{
- double dLow[2];
+ RCLowPassFilter(f, in, low);
- lossyIntegrator(filter, dIn, dLow);
+ out[0] = in[0] - low[0]; // left channel high-pass
+ out[1] = in[1] - low[1]; // right channel high-pass
+}
- dOut[0] = dIn[0] - dLow[0]; // left channel high-pass
- dOut[1] = dIn[1] - dLow[1]; // right channel high-pass
+/* These two are used for the filters in the SAMPLER screen, and
+** also the 2x downsampling when loading samples whose frequency
+** is above 22kHz.
+*/
+
+void RCLowPassFilterMono(rcFilter_t *f, const double in, double *out)
+{
+ double output = getLowpassOutput(f, in, 0.0, f->buffer[0]);
+ f->buffer[0] += (in - output) * f->c2;
+ *out = output;
}
-void lossyIntegratorHighPassMono(lossyIntegrator_t *filter, double dIn, double *dOut)
+void RCHighPassFilterMono(rcFilter_t *f, const double in, double *out)
{
- double dLow;
+ double low;
- lossyIntegratorMono(filter, dIn, &dLow);
-
- *dOut = dIn - dLow;
+ RCLowPassFilterMono(f, in, &low);
+ *out = in - low; // high-pass
}
-/* adejr/aciddose: these sin/cos approximations both use a 0..1
+/* aciddose: these sin/cos approximations both use a 0..1
** parameter range and have 'normalized' (1/2 = 0db) coeffs
**
** the coeffs are for LERP(x, x * x, 0.224) * sqrt(2)
@@ -275,7 +317,7 @@
{
double dPan;
- /* proper 'normalized' equal-power panning is (assuming pan left to right):
+ /* aciddose: proper 'normalized' equal-power panning is (assuming pan left to right):
** L = cos(p * pi * 1/2) * sqrt(2);
** R = sin(p * pi * 1/2) * sqrt(2);
*/
@@ -318,9 +360,9 @@
for (uint8_t i = 0; i < AMIGA_VOICES; i++)
mixerKillVoice(i);
- clearLossyIntegrator(&filterLo);
- clearLossyIntegrator(&filterHi);
- clearLEDFilter(&filterLED);
+ clearRCFilterState(&filterLo);
+ clearRCFilterState(&filterHi);
+ clearLEDFilterState();
resetAudioDithering();
@@ -346,7 +388,7 @@
v = &paula[ch];
if (period == 0)
- realPeriod = 65536; // confirmed behavior on real Amiga
+ realPeriod = 1+65535; // confirmed behavior on real Amiga
else if (period < 113)
realPeriod = 113; // confirmed behavior on real Amiga
else
@@ -494,9 +536,9 @@
void toggleA500Filters(void)
{
lockAudio();
- clearLossyIntegrator(&filterLo);
- clearLossyIntegrator(&filterHi);
- clearLEDFilter(&filterLED);
+ clearRCFilterState(&filterLo);
+ clearRCFilterState(&filterHi);
+ clearLEDFilterState();
unlockAudio();
if (filterFlags & FILTER_A500)
@@ -676,10 +718,10 @@
dOut[0] = dMixBufferL[i];
dOut[1] = dMixBufferR[i];
- // don't process any low-pass filter since the cut-off is around 28-31kHz on A1200
+ // don't process any low-pass filter since the cut-off is around 34kHz on A1200
// process high-pass filter
- lossyIntegratorHighPass(&filterHi, dOut, dOut);
+ RCHighPassFilter(&filterHi, dOut, dOut);
// normalize and flip phase (A500/A1200 has an inverted audio signal)
dOut[0] *= -(INT16_MAX / AMIGA_VOICES);
@@ -710,13 +752,13 @@
dOut[0] = dMixBufferL[i];
dOut[1] = dMixBufferR[i];
- // don't process any low-pass filter since the cut-off is around 28-31kHz on A1200
+ // don't process any low-pass filter since the cut-off is around 34kHz on A1200
// process "LED" filter
- lossyIntegratorLED(filterLEDC, &filterLED, dOut, dOut);
+ LEDFilter(&filterLED, dOut, dOut);
// process high-pass filter
- lossyIntegratorHighPass(&filterHi, dOut, dOut);
+ RCHighPassFilter(&filterHi, dOut, dOut);
// normalize and flip phase (A500/A1200 has an inverted audio signal)
dOut[0] *= -(INT16_MAX / AMIGA_VOICES);
@@ -748,14 +790,10 @@
dOut[1] = dMixBufferR[i];
// process low-pass filter
- lossyIntegrator(&filterLo, dOut, dOut);
+ RCLowPassFilter(&filterLo, dOut, dOut);
- // process "LED" filter
- if (filterFlags & FILTER_LED_ENABLED)
- lossyIntegratorLED(filterLEDC, &filterLED, dOut, dOut);
-
// process high-pass filter
- lossyIntegratorHighPass(&filterHi, dOut, dOut);
+ RCHighPassFilter(&filterHi, dOut, dOut);
dOut[0] *= -(INT16_MAX / AMIGA_VOICES);
dOut[1] *= -(INT16_MAX / AMIGA_VOICES);
@@ -786,13 +824,13 @@
dOut[1] = dMixBufferR[i];
// process low-pass filter
- lossyIntegrator(&filterLo, dOut, dOut);
+ RCLowPassFilter(&filterLo, dOut, dOut);
// process "LED" filter
- lossyIntegratorLED(filterLEDC, &filterLED, dOut, dOut);
+ LEDFilter(&filterLED, dOut, dOut);
// process high-pass filter
- lossyIntegratorHighPass(&filterHi, dOut, dOut);
+ RCHighPassFilter(&filterHi, dOut, dOut);
dOut[0] *= -(INT16_MAX / AMIGA_VOICES);
dOut[1] *= -(INT16_MAX / AMIGA_VOICES);
@@ -899,8 +937,6 @@
int16_t *out;
int32_t sampleBlock, samplesTodo;
- (void)userdata;
-
if (audio.forceMixerOff) // during MOD2WAV
{
memset(stream, 0, len);
@@ -929,12 +965,12 @@
sampleCounter = samplesPerTick;
}
}
+
+ (void)userdata;
}
static void calculateFilterCoeffs(void)
{
- double dCutOffHz;
-
/* Amiga 500 filter emulation, by aciddose
**
** First comes a static low-pass 6dB formed by the supply current
@@ -971,34 +1007,28 @@
** Under spice simulation the circuit yields -3dB = 5.2Hz.
*/
- // Amiga 500 rev6 RC low-pass filter:
- const double dLp_R = 360.0; // R321 - 360 ohm resistor
- const double dLp_C = 1e-7; // C321 - 0.1uF capacitor
- dCutOffHz = 1.0 / ((2.0 * M_PI) * dLp_R * dLp_C); // ~4420.97Hz
-#ifndef NO_FILTER_FINETUNING
- dCutOffHz += 580.0; // 8bitbubsy: finetuning to better match A500 low-pass testing
-#endif
- calcCoeffLossyIntegrator(audio.dAudioFreq, dCutOffHz, &filterLo);
+ double R, C, R1, R2, C1, C2, fc, fb;
- // Amiga Sallen-Key "LED" filter:
- const double dLed_R1 = 10000.0; // R322 - 10K ohm resistor
- const double dLed_R2 = 10000.0; // R323 - 10K ohm resistor
- const double dLed_C1 = 6.8e-9; // C322 - 6800pF capacitor
- const double dLed_C2 = 3.9e-9; // C323 - 3900pF capacitor
- dCutOffHz = 1.0 / ((2.0 * M_PI) * sqrt(dLed_R1 * dLed_R2 * dLed_C1 * dLed_C2)); // ~3090.53Hz
-#ifndef NO_FILTER_FINETUNING
- dCutOffHz -= 300.0; // 8bitbubsy: finetuning to better match A500 & A1200 "LED" filter testing
-#endif
- calcCoeffLED(audio.dAudioFreq, dCutOffHz, &filterLEDC);
+ // A500 one-pole 6db/oct static RC low-pass filter:
+ R = 360.0; // R321 (360 ohm resistor)
+ C = 1e-7; // C321 (0.1uF capacitor)
+ fc = 1.0 / (2.0 * M_PI * R * C); // ~4420.97Hz
+ calcRCFilterCoeffs(audio.outputRate, fc, &filterLo);
- // Amiga RC high-pass filter:
- const double dHp_R = 1000.0 + 390.0; // R324 - 1K ohm resistor + R325 - 390 ohm resistor
- const double dHp_C = 2.2e-5; // C334 - 22uF capacitor
- dCutOffHz = 1.0 / ((2.0 * M_PI) * dHp_R * dHp_C); // ~5.20Hz
-#ifndef NO_FILTER_FINETUNING
- dCutOffHz += 1.5; // 8bitbubsy: finetuning to better match A500 & A1200 high-pass testing
-#endif
- calcCoeffLossyIntegrator(audio.dAudioFreq, dCutOffHz, &filterHi);
+ // A500/A1200 Sallen-Key filter ("LED"):
+ R1 = 10000.0; // R322 (10K ohm resistor)
+ R2 = 10000.0; // R323 (10K ohm resistor)
+ C1 = 6.8e-9; // C322 (6800pF capacitor)
+ C2 = 3.9e-9; // C323 (3900pF capacitor)
+ fc = 1.0 / (2.0 * M_PI * sqrt(R1 * R2 * C1 * C2)); // ~3090.53Hz
+ fb = 0.125; // Fb = 0.125 : Q ~= 1/sqrt(2) (Butterworth)
+ calcLEDFilterCoeffs(audio.outputRate, fc, fb, &filterLED);
+
+ // A500/A1200 one-pole 6db/oct static RC high-pass filter:
+ R = 1000.0 + 390.0; // R324 (1K ohm resistor) + R325 (390 ohm resistor)
+ C = 2.2e-5; // C334 (22uF capacitor) (+ C324 (0.33uF capacitor) if A500)
+ fc = 1.0 / (2.0 * M_PI * R * C); // ~5.20Hz
+ calcRCFilterCoeffs(audio.outputRate, fc, &filterHi);
}
void mixerCalcVoicePans(uint8_t stereoSeparation)
@@ -1021,11 +1051,11 @@
SDL_AudioSpec want, have;
want.freq = config.soundFrequency;
+ want.samples = config.soundBufferSize;
want.format = AUDIO_S16;
want.channels = 2;
want.callback = audioCallback;
want.userdata = NULL;
- want.samples = config.soundBufferSize;
dev = SDL_OpenAudioDevice(NULL, 0, &want, &have, 0);
if (dev == 0)
@@ -1034,9 +1064,9 @@
return false;
}
- if (have.freq < 32000) // lower than this is not safe for one-step mixer w/ BLEP
+ if (have.freq < 32000) // lower than this is not safe for the BLEP synthesis in the mixer
{
- showErrorMsgBox("Unable to open audio: The audio output rate couldn't be used!");
+ showErrorMsgBox("Unable to open audio: An audio rate below 32kHz can't be used!");
return false;
}
@@ -1046,13 +1076,17 @@
return false;
}
- maxSamplesToMix = (int32_t)ceil((have.freq * 2.5) / 32.0);
+ audio.outputRate = have.freq;
+ audio.audioBufferSize = have.samples;
+ audio.dPeriodToDeltaDiv = (double)PAULA_PAL_CLK / audio.outputRate;
+ generateBpmTables();
+ const int32_t maxSamplesToMix = audio.bpmTab[0]; // BPM 32
+
dMixBufferLUnaligned = (double *)MALLOC_PAD(maxSamplesToMix * sizeof (double), 256);
dMixBufferRUnaligned = (double *)MALLOC_PAD(maxSamplesToMix * sizeof (double), 256);
+ editor.mod2WavBuffer = (int16_t *)malloc(maxSamplesToMix * sizeof (int16_t));
- editor.mod2WavBuffer = (int16_t *)malloc(sizeof (int16_t) * maxSamplesToMix);
-
if (dMixBufferLUnaligned == NULL || dMixBufferRUnaligned == NULL || editor.mod2WavBuffer == NULL)
{
showErrorMsgBox("Out of memory!");
@@ -1062,19 +1096,11 @@
dMixBufferL = (double *)ALIGN_PTR(dMixBufferLUnaligned, 256);
dMixBufferR = (double *)ALIGN_PTR(dMixBufferRUnaligned, 256);
- audio.audioBufferSize = have.samples;
- config.soundFrequency = have.freq;
- audio.audioFreq = config.soundFrequency;
- audio.dAudioFreq = (double)config.soundFrequency;
- audio.dPeriodToDeltaDiv = PAULA_PAL_CLK / audio.dAudioFreq;
-
mixerCalcVoicePans(config.stereoSeparation);
defStereoSep = config.stereoSeparation;
filterFlags = config.a500LowPassFilter ? FILTER_A500 : 0;
-
calculateFilterCoeffs();
- generateBpmTables();
samplesPerTick = 0;
sampleCounter = 0;
@@ -1149,8 +1175,6 @@
while (smpCounter > 0)
{
samplesToMix = smpCounter;
- if (samplesToMix > maxSamplesToMix)
- samplesToMix = maxSamplesToMix;
outputAudio(outStream, samplesToMix);
outStream += (uint32_t)samplesToMix * 2;
@@ -1210,7 +1234,7 @@
wavHeader.subchunk1Size = 16;
wavHeader.audioFormat = 1;
wavHeader.numChannels = 2;
- wavHeader.sampleRate = audio.audioFreq;
+ wavHeader.sampleRate = audio.outputRate;
wavHeader.bitsPerSample = 16;
wavHeader.byteRate = wavHeader.sampleRate * wavHeader.numChannels * (wavHeader.bitsPerSample / 8);
wavHeader.blockAlign = wavHeader.numChannels * (wavHeader.bitsPerSample / 8);
@@ -1353,30 +1377,23 @@
{
n_pattpos[ch] = modRow;
}
- else
+ else if (n_loopcount[ch] == 0)
{
- // this is so ugly
- if (n_loopcount[ch] == 0)
- {
- n_loopcount[ch] = pos;
+ n_loopcount[ch] = pos;
- pBreakPosition = n_pattpos[ch];
- pBreakFlag = true;
+ pBreakPosition = n_pattpos[ch];
+ pBreakFlag = true;
- for (pos = pBreakPosition; pos <= modRow; pos++)
- editor.rowVisitTable[(modOrder * MOD_ROWS) + pos] = false;
- }
- else
- {
- if (--n_loopcount[ch])
- {
- pBreakPosition = n_pattpos[ch];
- pBreakFlag = true;
+ for (pos = pBreakPosition; pos <= modRow; pos++)
+ editor.rowVisitTable[(modOrder * MOD_ROWS) + pos] = false;
+ }
+ else if (--n_loopcount[ch])
+ {
+ pBreakPosition = n_pattpos[ch];
+ pBreakFlag = true;
- for (pos = pBreakPosition; pos <= modRow; pos++)
- editor.rowVisitTable[(modOrder * MOD_ROWS) + pos] = false;
- }
- }
+ for (pos = pBreakPosition; pos <= modRow; pos++)
+ editor.rowVisitTable[(modOrder * MOD_ROWS) + pos] = false;
}
}
}
--- a/src/pt2_audio.h
+++ b/src/pt2_audio.h
@@ -6,20 +6,23 @@
#include <stdint.h>
#include <stdbool.h>
+// adding this forces the FPU to enter slow mode
#define DENORMAL_OFFSET 1e-10
-typedef struct lossyIntegrator_t
+typedef struct rcFilter_t
{
- double dBuffer[2], b0, b1;
-} lossyIntegrator_t;
+ double buffer[2];
+ double c, c2, g, cg;
+} rcFilter_t;
void resetCachedMixerPeriod(void);
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 calcRCFilterCoeffs(const double sr, const double hz, rcFilter_t *f);
+void clearRCFilterState(rcFilter_t *f);
+void RCLowPassFilter(rcFilter_t *f, const double *in, double *out);
+void RCHighPassFilter(rcFilter_t *f, const double *in, double *out);
+void RCLowPassFilterMono(rcFilter_t *f, const double in, double *out);
+void RCHighPassFilterMono(rcFilter_t *f, const double in, double *out);
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
@@ -59,7 +59,7 @@
config.soundBufferSize = 1024;
config.autoCloseDiskOp = true;
config.vsyncOff = false;
- config.hwMouse = false;
+ config.hwMouse = true;
config.sampleLowpass = true;
#ifndef _WIN32
--- a/src/pt2_edit.c
+++ b/src/pt2_edit.c
@@ -1380,7 +1380,7 @@
void trackOctaUp(bool sampleAllFlag, uint8_t from, uint8_t to)
{
- bool noteDeleted;
+ bool noteDeleted, noteChanged;
uint8_t j;
note_t *noteSrc;
@@ -1391,6 +1391,8 @@
to = j;
}
+ noteChanged = false;
+
saveUndo();
for (uint8_t i = from; i <= to; i++)
{
@@ -1401,6 +1403,8 @@
if (noteSrc->period)
{
+ uint16_t oldPeriod = noteSrc->period;
+
// period -> note
for (j = 0; j < 36; j++)
{
@@ -1422,11 +1426,17 @@
if (!noteDeleted)
noteSrc->period = periodTable[j];
+
+ if (noteSrc->period != oldPeriod)
+ noteChanged = true;
}
}
- updateWindowTitle(MOD_IS_MODIFIED);
- editor.ui.updatePatternData = true;
+ if (noteChanged)
+ {
+ updateWindowTitle(MOD_IS_MODIFIED);
+ editor.ui.updatePatternData = true;
+ }
}
void trackOctaDown(bool sampleAllFlag, uint8_t from, uint8_t to)
--- a/src/pt2_filters.c
+++ /dev/null
@@ -1,146 +1,0 @@
-/* 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;
-}
--- a/src/pt2_filters.h
+++ /dev/null
@@ -1,31 +1,0 @@
-/* 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
@@ -14,7 +14,7 @@
#include "pt2_unicode.h"
#include "pt2_palette.h"
-#define PROG_VER_STR "1.09"
+#define PROG_VER_STR "1.10"
#ifdef _WIN32
#define DIR_DELIMITER '\\'
@@ -292,8 +292,8 @@
volatile bool locked;
bool forceMixerOff;
uint16_t bpmTab[256-32], bpmTab28kHz[256-32], bpmTab22kHz[256-32];
- uint32_t audioFreq, audioBufferSize;
- double dAudioFreq, dPeriodToDeltaDiv;
+ uint32_t outputRate, audioBufferSize;
+ double dPeriodToDeltaDiv;
} audio;
struct keyb_t
--- a/src/pt2_sampleloader.c
+++ b/src/pt2_sampleloader.c
@@ -25,8 +25,9 @@
#include "pt2_visuals.h"
#include "pt2_helpers.h"
#include "pt2_unicode.h"
-#include "pt2_filters.h"
+#define DOWNSAMPLE_CUTOFF_FACTOR 4.0
+
enum
{
WAV_FORMAT_PCM = 0x0001,
@@ -39,6 +40,145 @@
static bool loadAIFFSample(UNICHAR *fileName, char *entryName, int8_t forceDownSampling);
static bool loadedFileWasAIFF;
+
+static bool lowPassSample8Bit(int8_t *buffer, int32_t length, int32_t sampleFrequency, double cutoff)
+{
+ rcFilter_t filter;
+
+ if (buffer == NULL || length == 0 || cutoff == 0.0)
+ return false;
+
+ calcRCFilterCoeffs(sampleFrequency, cutoff, &filter);
+ clearRCFilterState(&filter);
+
+ for (int32_t i = 0; i < length; i++)
+ {
+ int32_t sample;
+ double dSample;
+
+ RCLowPassFilterMono(&filter, buffer[i], &dSample);
+ sample = (int32_t)dSample;
+
+ buffer[i] = (int8_t)CLAMP(sample, INT8_MIN, INT8_MAX);
+ }
+
+ return true;
+}
+
+static bool lowPassSample8BitUnsigned(uint8_t *buffer, int32_t length, int32_t sampleFrequency, double cutoff)
+{
+ rcFilter_t filter;
+
+ if (buffer == NULL || length == 0 || cutoff == 0.0)
+ return false;
+
+ calcRCFilterCoeffs(sampleFrequency, cutoff, &filter);
+ clearRCFilterState(&filter);
+
+ for (int32_t i = 0; i < length; i++)
+ {
+ int32_t sample;
+ double dSample;
+
+ RCLowPassFilterMono(&filter, buffer[i] - 128, &dSample);
+ sample = (int32_t)dSample;
+
+ sample = CLAMP(sample, INT8_MIN, INT8_MAX);
+ buffer[i] = (uint8_t)(sample + 128);
+ }
+
+ return true;
+}
+
+static bool lowPassSample16Bit(int16_t *buffer, int32_t length, int32_t sampleFrequency, double cutoff)
+{
+ rcFilter_t filter;
+
+ if (buffer == NULL || length == 0 || cutoff == 0.0)
+ return false;
+
+ calcRCFilterCoeffs(sampleFrequency, cutoff, &filter);
+ clearRCFilterState(&filter);
+
+ for (int32_t i = 0; i < length; i++)
+ {
+ int32_t sample;
+ double dSample;
+
+ RCLowPassFilterMono(&filter, buffer[i], &dSample);
+ sample = (int32_t)dSample;
+
+ buffer[i] = (int16_t)CLAMP(sample, INT16_MIN, INT16_MAX);
+ }
+
+ return true;
+}
+
+static bool lowPassSample32Bit(int32_t *buffer, int32_t length, int32_t sampleFrequency, double cutoff)
+{
+ rcFilter_t filter;
+
+ if (buffer == NULL || length == 0 || cutoff == 0.0)
+ return false;
+
+ calcRCFilterCoeffs(sampleFrequency, cutoff, &filter);
+ clearRCFilterState(&filter);
+
+ for (int32_t i = 0; i < length; i++)
+ {
+ int64_t sample;
+ double dSample;
+
+ RCLowPassFilterMono(&filter, buffer[i], &dSample);
+ sample = (int32_t)dSample;
+
+ buffer[i] = (int32_t)CLAMP(sample, INT32_MIN, INT32_MAX);
+ }
+
+ return true;
+}
+
+static bool lowPassSampleFloat(float *buffer, int32_t length, int32_t sampleFrequency, double cutoff)
+{
+ rcFilter_t filter;
+
+ if (buffer == NULL || length == 0 || cutoff == 0.0)
+ return false;
+
+ calcRCFilterCoeffs(sampleFrequency, cutoff, &filter);
+ clearRCFilterState(&filter);
+
+ for (int32_t i = 0; i < length; i++)
+ {
+ double dSample;
+
+ RCLowPassFilterMono(&filter, buffer[i], &dSample);
+ buffer[i] = (float)dSample;
+ }
+
+ return true;
+}
+
+static bool lowPassSampleDouble(double *buffer, int32_t length, int32_t sampleFrequency, double cutoff)
+{
+ rcFilter_t filter;
+
+ if (buffer == NULL || length == 0 || cutoff == 0.0)
+ return false;
+
+ calcRCFilterCoeffs(sampleFrequency, cutoff, &filter);
+ clearRCFilterState(&filter);
+
+ for (int32_t i = 0; i < length; i++)
+ {
+ double dSample;
+ RCLowPassFilterMono(&filter, buffer[i], &dSample);
+
+ buffer[i] = dSample;
+ }
+
+ return true;
+}
void extLoadWAVOrAIFFSampleCallback(bool downsample)
{
--- a/src/pt2_sampler.c
+++ b/src/pt2_sampler.c
@@ -468,7 +468,7 @@
int32_t smp32, i, from, to;
double *dSampleData, dBaseFreq, dCutOff;
moduleSample_t *s;
- lossyIntegrator_t filterHi;
+ rcFilter_t filterHi;
assert(editor.currSample >= 0 && editor.currSample <= 30);
@@ -523,17 +523,17 @@
editor.hpCutOff = (uint16_t)dCutOff;
}
- calcCoeffLossyIntegrator(dBaseFreq, dCutOff, &filterHi);
+ calcRCFilterCoeffs(dBaseFreq, dCutOff, &filterHi);
// copy over sample data to double buffer
for (i = 0; i < s->length; i++)
dSampleData[i] = modEntry->sampleData[s->offset+i];
- filterHi.dBuffer[0] = 0.0;
+ clearRCFilterState(&filterHi);
if (to <= s->length)
{
for (i = from; i < to; i++)
- lossyIntegratorHighPassMono(&filterHi, dSampleData[i], &dSampleData[i]);
+ RCHighPassFilterMono(&filterHi, dSampleData[i], &dSampleData[i]);
}
if (editor.normalizeFiltersFlag)
@@ -558,7 +558,7 @@
int32_t smp32, i, from, to;
double *dSampleData, dBaseFreq, dCutOff;
moduleSample_t *s;
- lossyIntegrator_t filterLo;
+ rcFilter_t filterLo;
assert(editor.currSample >= 0 && editor.currSample <= 30);
@@ -613,17 +613,17 @@
editor.lpCutOff = (uint16_t)dCutOff;
}
- calcCoeffLossyIntegrator(dBaseFreq, dCutOff, &filterLo);
+ calcRCFilterCoeffs(dBaseFreq, dCutOff, &filterLo);
// copy over sample data to double buffer
for (i = 0; i < s->length; i++)
dSampleData[i] = modEntry->sampleData[s->offset+i];
- filterLo.dBuffer[0] = 0.0;
+ clearRCFilterState(&filterLo);
if (to <= s->length)
{
for (i = from; i < to; i++)
- lossyIntegratorMono(&filterLo, dSampleData[i], &dSampleData[i]);
+ RCLowPassFilterMono(&filterLo, dSampleData[i], &dSampleData[i]);
}
if (editor.normalizeFiltersFlag)
--- a/src/pt2_scopes.c
+++ b/src/pt2_scopes.c
@@ -386,7 +386,10 @@
// fractional part scaled to 0..2^32-1
dFrac *= UINT32_MAX;
- scopeTimeLenFrac = (uint32_t)(dFrac + 0.5);
+ dFrac += 0.5;
+ if (dFrac > UINT32_MAX)
+ dFrac = UINT32_MAX;
+ scopeTimeLenFrac = (uint32_t)dFrac;
scopeThread = SDL_CreateThread(scopeThreadFunc, NULL, NULL);
if (scopeThread == NULL)
--- a/src/pt2_visuals.c
+++ b/src/pt2_visuals.c
@@ -94,7 +94,10 @@
// fractional part scaled to 0..2^32-1
dFrac *= UINT32_MAX;
- editor.vblankTimeLenFrac = (uint32_t)(dFrac + 0.5);
+ dFrac += 0.5;
+ if (dFrac > UINT32_MAX)
+ dFrac = UINT32_MAX;
+ editor.vblankTimeLenFrac = (uint32_t)dFrac;
}
void setupWaitVBL(void)
@@ -653,7 +656,7 @@
{
assert(editor.resampleNote < 36);
textOutBg(288, 236,
- config.accidental ? noteNames2[editor.resampleNote] : noteNames1[editor.resampleNote],
+ config.accidental ? noteNames2[2+editor.resampleNote] : noteNames1[2+editor.resampleNote],
video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
}
}
@@ -1604,7 +1607,7 @@
if (editor.note1 > 35)
textOutBg(256, 58, "---", video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
else
- textOutBg(256, 58, config.accidental ? noteNames2[editor.note1] : noteNames1[editor.note1],
+ textOutBg(256, 58, config.accidental ? noteNames2[2+editor.note1] : noteNames1[2+editor.note1],
video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
}
@@ -1614,7 +1617,7 @@
if (editor.note2 > 35)
textOutBg(256, 69, "---", video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
else
- textOutBg(256, 69, config.accidental ? noteNames2[editor.note2] : noteNames1[editor.note2],
+ textOutBg(256, 69, config.accidental ? noteNames2[2+editor.note2] : noteNames1[2+editor.note2],
video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
}
@@ -1624,7 +1627,7 @@
if (editor.note3 > 35)
textOutBg(256, 80, "---", video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
else
- textOutBg(256, 80, config.accidental ? noteNames2[editor.note3] : noteNames1[editor.note3],
+ textOutBg(256, 80, config.accidental ? noteNames2[2+editor.note3] : noteNames1[2+editor.note3],
video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
}
@@ -1634,7 +1637,7 @@
if (editor.note4 > 35)
textOutBg(256, 91, "---", video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
else
- textOutBg(256, 91, config.accidental ? noteNames2[editor.note4] : noteNames1[editor.note4],
+ textOutBg(256, 91, config.accidental ? noteNames2[2+editor.note4] : noteNames1[2+editor.note4],
video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);
}
}
--- a/vs2019_project/pt2-clone/protracker.ini
+++ b/vs2019_project/pt2-clone/protracker.ini
@@ -180,10 +180,9 @@
; Audio output frequency
; Syntax: Number, in hertz
; Default value: 48000
-; Comment: Ranges from 32000 to 96000.
+; Comment: Ranges from 44100 to 192000.
; Also sets the playback frequency for WAVs made with MOD2WAV.
-; Note to coders: Don't allow lower numbers than 32000, it will
-; break the BLEP synthesis.
+;
FREQUENCY=48000
; Audio buffer size
--- a/vs2019_project/pt2-clone/pt2-clone.vcxproj
+++ b/vs2019_project/pt2-clone/pt2-clone.vcxproj
@@ -287,7 +287,6 @@
<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" />
@@ -329,7 +328,6 @@
<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,9 +66,6 @@
<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,7 +142,6 @@
<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" />