commit 544e74e4829b1856bd7a60de24317af9f461fdb5
parent 7f215f857b4adc6c13ecd36713737c8861816e20
Author: Olav Sørensen <olav.sorensen@live.no>
Date: Sun, 3 Nov 2024 19:30:39 +0100
Mega-commit (unfortunately) for v1.87
Diffstat:
31 files changed, 958 insertions(+), 252 deletions(-)
diff --git a/src/ft2_audio.c b/src/ft2_audio.c
@@ -177,6 +177,7 @@ void setMixerBPM(int32_t bpm)
audio.samplesPerTickInt = audio.samplesPerTickIntTab[i];
audio.samplesPerTickFrac = audio.samplesPerTickFracTab[i];
+ audio.fSamplesPerTickIntMul = (float)(1.0 / (double)audio.samplesPerTickInt);
// for audio/video sync timestamp
tickTimeLenInt = audio.tickTimeIntTab[i];
@@ -261,10 +262,8 @@ static void voiceUpdateVolumes(int32_t i, uint8_t status)
const float fVolumeRDiff = 0.0f - f->fCurrVolumeR;
f->volumeRampLength = audio.quickVolRampSamples; // 5ms
- const float fVolumeRampLength = (float)(int32_t)f->volumeRampLength;
-
- f->fVolumeLDelta = fVolumeLDiff / fVolumeRampLength;
- f->fVolumeRDelta = fVolumeRDiff / fVolumeRampLength;
+ f->fVolumeLDelta = fVolumeLDiff * audio.fQuickVolRampSamplesMul;
+ f->fVolumeRDelta = fVolumeRDiff * audio.fQuickVolRampSamplesMul;
f->isFadeOutVoice = true;
}
@@ -282,12 +281,20 @@ static void voiceUpdateVolumes(int32_t i, uint8_t status)
const float fVolumeLDiff = v->fTargetVolumeL - v->fCurrVolumeL;
const float fVolumeRDiff = v->fTargetVolumeR - v->fCurrVolumeR;
- // IS_QuickVol = 5ms, otherwise the duration of a tick
- v->volumeRampLength = (status & IS_QuickVol) ? audio.quickVolRampSamples : audio.samplesPerTickInt;
- const float fVolumeRampLength = (float)(int32_t)v->volumeRampLength;
+ float fRampLengthMul;
+ if (status & IS_QuickVol) // duration of 5ms
+ {
+ v->volumeRampLength = audio.quickVolRampSamples;
+ fRampLengthMul = audio.fQuickVolRampSamplesMul;
+ }
+ else // duration of a tick
+ {
+ v->volumeRampLength = audio.samplesPerTickInt;
+ fRampLengthMul = audio.fSamplesPerTickIntMul;
+ }
- v->fVolumeLDelta = fVolumeLDiff / fVolumeRampLength;
- v->fVolumeRDelta = fVolumeRDiff / fVolumeRampLength;
+ v->fVolumeLDelta = fVolumeLDiff * fRampLengthMul;
+ v->fVolumeRDelta = fVolumeRDiff * fRampLengthMul;
}
}
@@ -340,7 +347,7 @@ static void voiceTrigger(int32_t ch, sample_t *s, int32_t position)
return;
}
- v->mixFuncOffset = ((int32_t)sample16Bit * 15) + (audio.interpolationType * 3) + loopType;
+ v->mixFuncOffset = ((int32_t)sample16Bit * 18) + (audio.interpolationType * 3) + loopType;
v->active = true;
}
@@ -462,6 +469,8 @@ static void doChannelMixing(int32_t bufferPosition, int32_t samplesToMix)
voice_t *v = voice; // normal voices
voice_t *r = &voice[MAX_CHANNELS]; // volume ramp fadeout-voices
+ const int32_t mixOffsetBias = 3 * NUM_INTERPOLATORS * 2; // 3 = loop types (off/fwd/bidi), 2 = bit depths (8-bit/16-bit)
+
for (int32_t i = 0; i < song.numChannels; i++, v++, r++)
{
if (v->active)
@@ -470,11 +479,11 @@ static void doChannelMixing(int32_t bufferPosition, int32_t samplesToMix)
if (!volRampFlag && v->fCurrVolumeL == 0.0f && v->fCurrVolumeR == 0.0f)
silenceMixRoutine(v, samplesToMix);
else
- mixFuncTab[((int32_t)volRampFlag * (3*5*2)) + v->mixFuncOffset](v, bufferPosition, samplesToMix);
+ mixFuncTab[((int32_t)volRampFlag * mixOffsetBias) + v->mixFuncOffset](v, bufferPosition, samplesToMix);
}
if (r->active) // volume ramp fadeout-voice
- mixFuncTab[(3*5*2) + r->mixFuncOffset](r, bufferPosition, samplesToMix);
+ mixFuncTab[mixOffsetBias + r->mixFuncOffset](r, bufferPosition, samplesToMix);
}
}
diff --git a/src/ft2_audio.h b/src/ft2_audio.h
@@ -49,7 +49,7 @@ typedef struct audio_t
uint64_t tickTime64, tickTime64Frac;
- float *fMixBufferL, *fMixBufferR;
+ float *fMixBufferL, *fMixBufferR, fQuickVolRampSamplesMul, fSamplesPerTickIntMul;
double dHz2MixDeltaMul;
SDL_AudioDeviceID dev;
diff --git a/src/ft2_checkboxes.c b/src/ft2_checkboxes.c
@@ -82,7 +82,7 @@ checkBox_t checkBoxes[NUM_CHECKBOXES] =
// ------ CONFIG CHECKBOXES ------
//x, y, w, h, funcOnUp
{ 3, 91, 77, 12, cbToggleAutoSaveConfig },
- { 508, 158, 107, 12, cbConfigVolRamp },
+ { 512, 158, 107, 12, cbConfigVolRamp },
{ 113, 14, 108, 12, cbConfigPattStretch },
{ 113, 27, 117, 12, cbConfigHexCount },
{ 113, 40, 81, 12, cbConfigAccidential },
diff --git a/src/ft2_config.c b/src/ft2_config.c
@@ -139,7 +139,7 @@ static void loadConfigFromBuffer(bool defaults)
config.recMIDIVolSens = CLAMP(config.recMIDIVolSens, 0, 200);
config.recMIDIChn = CLAMP(config.recMIDIChn, 1, 16);
- if (config.interpolation > 4)
+ if (config.interpolation >= NUM_INTERPOLATORS)
config.interpolation = INTERPOLATION_SINC8; // default (sinc, 8 point)
if (config.recTrueInsert > 1)
@@ -837,6 +837,8 @@ void setConfigAudioRadioButtonStates(void) // accessed by other .c files
tmpID = RB_CONFIG_AUDIO_INTRP_SINC16;
else if (config.interpolation == INTERPOLATION_CUBIC)
tmpID = RB_CONFIG_AUDIO_INTRP_CUBIC;
+ else if (config.interpolation == INTERPOLATION_GAUSSIAN)
+ tmpID = RB_CONFIG_AUDIO_INTRP_GAUSSIAN;
else
tmpID = RB_CONFIG_AUDIO_INTRP_SINC8; // default case
@@ -1126,13 +1128,13 @@ void showConfigScreen(void)
drawFramework(110, 0, 276, 87, FRAMEWORK_TYPE1);
drawFramework(110, 87, 276, 86, FRAMEWORK_TYPE1);
- drawFramework(386, 0, 119, 58, FRAMEWORK_TYPE1);
- drawFramework(386, 58, 119, 44, FRAMEWORK_TYPE1);
- drawFramework(386, 102, 119, 71, FRAMEWORK_TYPE1);
+ drawFramework(386, 0, 123, 58, FRAMEWORK_TYPE1);
+ drawFramework(386, 58, 123, 29, FRAMEWORK_TYPE1);
+ drawFramework(386, 87, 123, 86, FRAMEWORK_TYPE1);
- drawFramework(505, 0, 127, 58, FRAMEWORK_TYPE1);
- drawFramework(505, 102, 127, 71, FRAMEWORK_TYPE1);
- drawFramework(505, 58, 127, 44, FRAMEWORK_TYPE1);
+ drawFramework(509, 0, 123, 58, FRAMEWORK_TYPE1);
+ drawFramework(509, 102, 123, 71, FRAMEWORK_TYPE1);
+ drawFramework(509, 58, 123, 44, FRAMEWORK_TYPE1);
drawFramework(112, 16, AUDIO_SELECTORS_BOX_WIDTH+4, 69, FRAMEWORK_TYPE2);
drawFramework(112, 103, AUDIO_SELECTORS_BOX_WIDTH+4, 47, FRAMEWORK_TYPE2);
@@ -1161,33 +1163,34 @@ void showConfigScreen(void)
textOutShadow(336, 157, PAL_FORGRND, PAL_DSKTOP2, "96.0kHz");
textOutShadow(390, 3, PAL_FORGRND, PAL_DSKTOP2, "Audio buffer size:");
- textOutShadow(406, 17, PAL_FORGRND, PAL_DSKTOP2, "Small");
- textOutShadow(406, 31, PAL_FORGRND, PAL_DSKTOP2, "Medium (default)");
- textOutShadow(406, 45, PAL_FORGRND, PAL_DSKTOP2, "Large");
+ textOutShadow(405, 17, PAL_FORGRND, PAL_DSKTOP2, "Small");
+ textOutShadow(405, 31, PAL_FORGRND, PAL_DSKTOP2, "Medium (default)");
+ textOutShadow(405, 45, PAL_FORGRND, PAL_DSKTOP2, "Large");
textOutShadow(390, 61, PAL_FORGRND, PAL_DSKTOP2, "Audio bit depth:");
- textOutShadow(406, 75, PAL_FORGRND, PAL_DSKTOP2, "16-bit");
- textOutShadow(406, 89, PAL_FORGRND, PAL_DSKTOP2, "32-bit (float)");
-
- textOutShadow(406, 105, PAL_FORGRND, PAL_DSKTOP2, "No interpolation");
- textOutShadow(406, 119, PAL_FORGRND, PAL_DSKTOP2, "Linear (FT2)");
- textOutShadow(406, 133, PAL_FORGRND, PAL_DSKTOP2, "Cubic spline");
- textOutShadow(406, 147, PAL_FORGRND, PAL_DSKTOP2, "Sinc (8 point)");
- textOutShadow(406, 161, PAL_FORGRND, PAL_DSKTOP2, "Sinc (16 point)");
-
- textOutShadow(509, 3, PAL_FORGRND, PAL_DSKTOP2, "Audio output rate:");
- textOutShadow(525, 17, PAL_FORGRND, PAL_DSKTOP2, "44100Hz");
- textOutShadow(525, 31, PAL_FORGRND, PAL_DSKTOP2, "48000Hz");
- textOutShadow(525, 45, PAL_FORGRND, PAL_DSKTOP2, "96000Hz");
-
- textOutShadow(509, 61, PAL_FORGRND, PAL_DSKTOP2, "Frequency slides:");
- textOutShadow(525, 75, PAL_FORGRND, PAL_DSKTOP2, "Amiga");
- textOutShadow(525, 89, PAL_FORGRND, PAL_DSKTOP2, "Linear (default)");
-
- textOutShadow(509, 105, PAL_FORGRND, PAL_DSKTOP2, "Amplification:");
+ textOutShadow(405, 74, PAL_FORGRND, PAL_DSKTOP2, "16-bit");
+ textOutShadow(468, 74, PAL_FORGRND, PAL_DSKTOP2, "32-bit");
+
+ textOutShadow(405, 91, PAL_FORGRND, PAL_DSKTOP2, "No interpolation");
+ textOutShadow(405, 105, PAL_FORGRND, PAL_DSKTOP2, "Linear (FT2)");
+ textOutShadow(405, 119, PAL_FORGRND, PAL_DSKTOP2, "Gaussian (SNES)");
+ textOutShadow(405, 133, PAL_FORGRND, PAL_DSKTOP2, "Cubic Hermite");
+ textOutShadow(405, 147, PAL_FORGRND, PAL_DSKTOP2, "Sinc (8 point)");
+ textOutShadow(405, 161, PAL_FORGRND, PAL_DSKTOP2, "Sinc (16 point)");
+
+ textOutShadow(513, 3, PAL_FORGRND, PAL_DSKTOP2, "Audio output rate:");
+ textOutShadow(528, 17, PAL_FORGRND, PAL_DSKTOP2, "44100Hz");
+ textOutShadow(528, 31, PAL_FORGRND, PAL_DSKTOP2, "48000Hz");
+ textOutShadow(528, 45, PAL_FORGRND, PAL_DSKTOP2, "96000Hz");
+
+ textOutShadow(513, 61, PAL_FORGRND, PAL_DSKTOP2, "Frequency slides:");
+ textOutShadow(528, 75, PAL_FORGRND, PAL_DSKTOP2, "Amiga");
+ textOutShadow(528, 89, PAL_FORGRND, PAL_DSKTOP2, "Linear (default)");
+
+ textOutShadow(513, 105, PAL_FORGRND, PAL_DSKTOP2, "Amplification:");
charOutShadow(621, 105, PAL_FORGRND, PAL_DSKTOP2, 'x');
- textOutShadow(509, 133, PAL_FORGRND, PAL_DSKTOP2, "Master volume:");
- textOutShadow(525, 160, PAL_FORGRND, PAL_DSKTOP2, "Volume ramping");
+ textOutShadow(513, 133, PAL_FORGRND, PAL_DSKTOP2, "Master volume:");
+ textOutShadow(529, 160, PAL_FORGRND, PAL_DSKTOP2, "Volume ramping");
setConfigAudioRadioButtonStates();
setConfigAudioCheckButtonStates();
@@ -1625,6 +1628,13 @@ void rbConfigAudioIntrpLinear(void)
checkRadioButton(RB_CONFIG_AUDIO_INTRP_LINEAR);
}
+void rbConfigAudioIntrpGaussian(void)
+{
+ config.interpolation = INTERPOLATION_GAUSSIAN;
+ audioSetInterpolationType(config.interpolation);
+ checkRadioButton(RB_CONFIG_AUDIO_INTRP_GAUSSIAN);
+}
+
void rbConfigAudioIntrpCubic(void)
{
config.interpolation = INTERPOLATION_CUBIC;
diff --git a/src/ft2_config.h b/src/ft2_config.h
@@ -20,14 +20,6 @@ enum
CONFIG_HIDE_ERRORS = 0,
CONFIG_SHOW_ERRORS = 1,
- // don't change the order of these! (yes, it looks off)
- INTERPOLATION_DISABLED = 0,
- INTERPOLATION_SINC8 = 1,
- INTERPOLATION_LINEAR = 2,
- INTERPOLATION_SINC16 = 3,
- INTERPOLATION_CUBIC = 4,
- // ------
-
MOUSE_IDLE_SHAPE_NICE = 0,
MOUSE_IDLE_SHAPE_UGLY = 1,
MOUSE_IDLE_SHAPE_AWFUL = 2,
@@ -55,22 +47,14 @@ enum
PAL_JUNGLE = 5,
PAL_LITHE_DARK = 6,
PAL_ROSE = 7,
- PAL_DARK_MODE = 8,
+ PAL_DARK_MODE = 8, // default
PAL_VIOLENT = 9,
- PAL_WHY_COLORS = 10, // default
+ PAL_WHY_COLORS = 10,
PAL_USER_DEFINED = 11,
FILESORT_EXT = 0,
FILESORT_NAME = 1,
- ONE_PLAYER = 0,
- TWO_PLAYERS = 1,
-
- DIFFICULTY_NOVICE = 0,
- DIFFICULTY_AVERAGE = 1,
- DIFFICULTY_PRO = 2,
- DIFFICULTY_MANIAC = 3,
-
DONT_SHOW_IMPORT_WARNING_FLAG = 64,
DONT_SHOW_NOT_YET_APPLIED_WARNING_FLAG = 32,
@@ -221,6 +205,7 @@ void rbConfigAudio16Bit(void);
void rbConfigAudio32BitFloat(void);
void rbConfigAudioIntrpDisabled(void);
void rbConfigAudioIntrpLinear(void);
+void rbConfigAudioIntrpGaussian(void);
void rbConfigAudioIntrpCubic(void);
void rbConfigAudioIntrp8PointSinc(void);
void rbConfigAudioIntrp16PointSinc(void);
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.86"
+#define PROG_VER_STR "1.87"
// do NOT change these! It will only mess things up...
diff --git a/src/ft2_inst_ed.c b/src/ft2_inst_ed.c
@@ -55,13 +55,13 @@ typedef struct patWaveHdr_t
uint8_t fractions;
int32_t sampleLength, loopStart, loopEnd;
uint16_t sampleRate;
- int32_t lowFrq, highFreq, rootFrq;
- int16_t finetune;
- uint8_t panning, envRate[6], envOfs[6], tremSweep, tremRate;
+ int32_t lowFreq, highFreq, rootFreq;
+ int16_t tune; // -512 to +512, EXCLUDING 0 cause it is a multiplier. 512 is one octave off, and 1 is a neutral value
+ uint8_t balance, envRate[6], envOfs[6], tremSweep, tremRate;
uint8_t tremDepth, vibSweep, vibRate, vibDepth, flags;
- int16_t junk1;
- uint16_t junk2;
- char junk3[36];
+ int16_t scaleFreq;
+ uint16_t scaleFactor;
+ char reserved[36];
}
#ifdef __GNUC__
__attribute__ ((packed))
@@ -3092,12 +3092,10 @@ void saveInstr(UNICHAR *filenameU, int16_t insNum)
SDL_DetachThread(thread);
}
-static int16_t getPATNote(int32_t freq)
+static int16_t getPATNote(uint32_t freq)
{
- const double dNote = (log2(freq / 440000.0) * 12.0) + 57.0;
- const int32_t note = (const int32_t)(dNote + 0.5); // rounded
-
- return (int16_t)note;
+ const double dNote = (log2((freq / 1000.0) / 440.0) * 12.0) + 9.0;
+ return (int16_t)round(NOTE_C4 + dNote);
}
static int32_t SDLCALL loadInstrThread(void *ptr)
@@ -3314,8 +3312,8 @@ static int32_t SDLCALL loadInstrThread(void *ptr)
{
// PAT - Gravis Ultrasound patch
- if (pat_h.numSamples == 0)
- pat_h.numSamples = 1; // to some patch makers, 0 means 1
+ if (pat_h.numSamples == 0) // 0 samples really means 1 (for quirky .PAT files)
+ pat_h.numSamples = 1;
if (pat_h.layers > 1 || pat_h.numSamples > MAX_SMP_PER_INST)
{
@@ -3375,8 +3373,8 @@ static int32_t SDLCALL loadInstrThread(void *ptr)
if (i == 0)
{
ins->autoVibSweep = patWave_h.vibSweep;
- ins->autoVibRate = (patWave_h.vibRate + 2) >> 2; // rounded
- ins->autoVibDepth = (patWave_h.vibDepth + 1) >> 1; // rounded
+ ins->autoVibRate = (patWave_h.vibRate + 1) >> 2;
+ ins->autoVibDepth = (patWave_h.vibDepth+1) >> 1;
}
s = &instr[editor.curInstr]->smp[i];
@@ -3392,7 +3390,7 @@ static int32_t SDLCALL loadInstrThread(void *ptr)
s->flags |= LOOP_FWD;
}
- s->panning = ((patWave_h.panning << 4) & 0xF0) | (patWave_h.panning & 0xF);
+ s->panning = ((patWave_h.balance << 4) & 0xF0) | (patWave_h.balance & 0xF); // 0..15 -> 0..255
s->loopStart = patWave_h.loopStart;
s->loopLength = patWave_h.loopEnd - patWave_h.loopStart;
@@ -3402,13 +3400,17 @@ static int32_t SDLCALL loadInstrThread(void *ptr)
s->loopLength >>= 1;
}
- const double dFreq = (1.0 + (patWave_h.finetune / 512.0)) * patWave_h.sampleRate;
- tuneSample(s, (int32_t)(dFreq + 0.5), audio.linearPeriodsFlag);
+ double dC4Freq = patWave_h.sampleRate;
+ if (patWave_h.scaleFactor > 0)
+ {
+ const double dMidiC4Freq = 261.6255653005986347; // 440*2^(-9/12)
+ const double dRatio = dMidiC4Freq / (patWave_h.rootFreq / 1000.0);
+ dC4Freq *= dRatio;
+ }
- a = getPATNote(patWave_h.rootFrq) - (12 * 3);
- s->relativeNote -= (uint8_t)a;
+ setSampleC4Hz(s, dC4Freq);
- a = getPATNote(patWave_h.lowFrq);
+ a = getPATNote(patWave_h.lowFreq);
b = getPATNote(patWave_h.highFreq);
a = CLAMP(a, 0, 95);
diff --git a/src/ft2_main.c b/src/ft2_main.c
@@ -146,7 +146,7 @@ int main(int argc, char *argv[])
#ifdef __APPLE__
osxSetDirToProgramDirFromArgs(argv);
#endif
- if (!setupExecutablePath() || !loadBMPs() || !calcCubicSplineTable() || !calcWindowedSincTables())
+ if (!setupExecutablePath() || !loadBMPs() || !calcGaussianTable() || !calcCubicSplineTable() || !calcWindowedSincTables())
{
cleanUpAndExit();
return 1;
diff --git a/src/ft2_pushbuttons.c b/src/ft2_pushbuttons.c
@@ -317,9 +317,9 @@ pushButton_t pushButtons[NUM_PUSHBUTTONS] =
{ 365, 72, 18, 13, 1, 4, ARROW_DOWN_STRING, NULL, scrollAudOutputDevListDown, NULL },
{ 365, 103, 18, 13, 1, 4, ARROW_UP_STRING, NULL, scrollAudInputDevListUp, NULL },
{ 365, 137, 18, 13, 1, 4, ARROW_DOWN_STRING, NULL, scrollAudInputDevListDown, NULL },
- { 508, 117, 21, 13, 1, 4, ARROW_LEFT_STRING, NULL, configAmpDown, NULL },
+ { 512, 117, 21, 13, 1, 4, ARROW_LEFT_STRING, NULL, configAmpDown, NULL },
{ 608, 117, 21, 13, 1, 4, ARROW_RIGHT_STRING, NULL, configAmpUp, NULL },
- { 508, 143, 21, 13, 1, 0, ARROW_LEFT_STRING, NULL, configMasterVolDown, NULL },
+ { 512, 143, 21, 13, 1, 0, ARROW_LEFT_STRING, NULL, configMasterVolDown, NULL },
{ 608, 143, 21, 13, 1, 0, ARROW_RIGHT_STRING, NULL, configMasterVolUp, NULL },
// ------ CONFIG LAYOUT PUSHBUTTONS ------
diff --git a/src/ft2_radiobuttons.c b/src/ft2_radiobuttons.c
@@ -75,28 +75,29 @@ radioButton_t radioButtons[NUM_RADIOBUTTONS] =
// audio buffer size
//x, y, w, group, funcOnUp
- { 390, 16, 46, RB_GROUP_CONFIG_SOUND_BUFF_SIZE, rbConfigAudioBuffSmall },
- { 390, 30, 113, RB_GROUP_CONFIG_SOUND_BUFF_SIZE, rbConfigAudioBuffMedium },
- { 390, 44, 50, RB_GROUP_CONFIG_SOUND_BUFF_SIZE, rbConfigAudioBuffLarge },
+ { 390, 16, 45, RB_GROUP_CONFIG_SOUND_BUFF_SIZE, rbConfigAudioBuffSmall },
+ { 390, 30, 112, RB_GROUP_CONFIG_SOUND_BUFF_SIZE, rbConfigAudioBuffMedium },
+ { 390, 44, 49, RB_GROUP_CONFIG_SOUND_BUFF_SIZE, rbConfigAudioBuffLarge },
// audio bit depth
//x, y, w, group, funcOnUp
- { 390, 74, 52, RB_GROUP_CONFIG_AUDIO_BIT_DEPTH, rbConfigAudio16Bit },
- { 390, 88, 93, RB_GROUP_CONFIG_AUDIO_BIT_DEPTH, rbConfigAudio32BitFloat },
+ { 390, 73, 51, RB_GROUP_CONFIG_AUDIO_BIT_DEPTH, rbConfigAudio16Bit },
+ { 453, 73, 51, RB_GROUP_CONFIG_AUDIO_BIT_DEPTH, rbConfigAudio32BitFloat },
// audio interpolation
//x, y, w, group, funcOnUp
- { 390, 104, 109, RB_GROUP_CONFIG_AUDIO_INTERPOLATION, rbConfigAudioIntrpDisabled },
- { 390, 118, 91, RB_GROUP_CONFIG_AUDIO_INTERPOLATION, rbConfigAudioIntrpLinear },
- { 390, 132, 86, RB_GROUP_CONFIG_AUDIO_INTERPOLATION, rbConfigAudioIntrpCubic },
- { 390, 146, 95, RB_GROUP_CONFIG_AUDIO_INTERPOLATION, rbConfigAudioIntrp8PointSinc },
- { 390, 160, 102, RB_GROUP_CONFIG_AUDIO_INTERPOLATION, rbConfigAudioIntrp16PointSinc },
+ { 390, 90, 108, RB_GROUP_CONFIG_AUDIO_INTERPOLATION, rbConfigAudioIntrpDisabled },
+ { 390, 104, 90, RB_GROUP_CONFIG_AUDIO_INTERPOLATION, rbConfigAudioIntrpLinear },
+ { 390, 118, 115, RB_GROUP_CONFIG_AUDIO_INTERPOLATION, rbConfigAudioIntrpGaussian },
+ { 390, 132, 95, RB_GROUP_CONFIG_AUDIO_INTERPOLATION, rbConfigAudioIntrpCubic },
+ { 390, 146, 94, RB_GROUP_CONFIG_AUDIO_INTERPOLATION, rbConfigAudioIntrp8PointSinc },
+ { 390, 160, 101, RB_GROUP_CONFIG_AUDIO_INTERPOLATION, rbConfigAudioIntrp16PointSinc },
// audio output frequency
//x, y, w, group, funcOnUp
- { 509, 16, 66, RB_GROUP_CONFIG_AUDIO_FREQ, rbConfigAudio44kHz },
- { 509, 30, 66, RB_GROUP_CONFIG_AUDIO_FREQ, rbConfigAudio48kHz },
- { 509, 44, 66, RB_GROUP_CONFIG_AUDIO_FREQ, rbConfigAudio96kHz },
+ { 513, 16, 65, RB_GROUP_CONFIG_AUDIO_FREQ, rbConfigAudio44kHz },
+ { 513, 30, 65, RB_GROUP_CONFIG_AUDIO_FREQ, rbConfigAudio48kHz },
+ { 513, 44, 65, RB_GROUP_CONFIG_AUDIO_FREQ, rbConfigAudio96kHz },
// audio input frequency
//x, y, w, group, funcOnUp
@@ -106,8 +107,8 @@ radioButton_t radioButtons[NUM_RADIOBUTTONS] =
// frequency slides
//x, y, w, group, funcOnUp
- { 509, 74, 50, RB_GROUP_CONFIG_FREQ_SLIDES, rbConfigFreqSlidesAmiga },
- { 509, 88, 108, RB_GROUP_CONFIG_FREQ_SLIDES, rbConfigFreqSlidesLinear },
+ { 513, 74, 49, RB_GROUP_CONFIG_FREQ_SLIDES, rbConfigFreqSlidesAmiga },
+ { 513, 88, 107, RB_GROUP_CONFIG_FREQ_SLIDES, rbConfigFreqSlidesLinear },
// ------ CONFIG LAYOUT ------
diff --git a/src/ft2_radiobuttons.h b/src/ft2_radiobuttons.h
@@ -19,7 +19,7 @@ enum // RADIOBUTTONS
RB_NIBBLES_NOVICE,
RB_NIBBLES_AVERAGE,
RB_NIBBLES_PRO,
- RB_NIBBLES_MANIAC,
+ RB_NIBBLES_TRITON,
// SAMPLER
RB_SAMPLE_NO_LOOP,
@@ -57,6 +57,7 @@ enum // RADIOBUTTONS
// AUDIO INTERPOLATION
RB_CONFIG_AUDIO_INTRP_DISABLED,
RB_CONFIG_AUDIO_INTRP_LINEAR,
+ RB_CONFIG_AUDIO_INTRP_GAUSSIAN,
RB_CONFIG_AUDIO_INTRP_CUBIC,
RB_CONFIG_AUDIO_INTRP_SINC8,
RB_CONFIG_AUDIO_INTRP_SINC16,
diff --git a/src/ft2_replayer.c b/src/ft2_replayer.c
@@ -27,6 +27,7 @@
#include "ft2_sample_loader.h"
#include "ft2_tables.h"
#include "ft2_structs.h"
+#include "mixer/ft2_gaussian.h"
#include "mixer/ft2_cubic_spline.h"
#include "mixer/ft2_windowed_sinc.h"
@@ -139,73 +140,23 @@ void removeSongModifiedFlag(void)
editor.updateWindowTitle = true;
}
-// used on external sample load and during sample loading (on some module formats)
-void tuneSample(sample_t *s, const int32_t midCFreq, bool linearPeriodsFlag)
+void setSampleC4Hz(sample_t *s, double dC4Hz)
{
- #define MIN_PERIOD (0)
- #define MAX_PERIOD (((10*12*16)-1)-1) /* -1 (because of bugged amigaPeriods table values) */
-
- double (*dGetHzFromPeriod)(int32_t) = linearPeriodsFlag ? dLinearPeriod2Hz : dAmigaPeriod2Hz;
- const uint16_t *periodTab = linearPeriodsFlag ? linearPeriods : amigaPeriods;
-
- if (midCFreq <= 0 || periodTab == NULL)
- {
- s->finetune = s->relativeNote = 0;
- return;
- }
-
- // handle frequency boundaries first...
-
- if (midCFreq <= (int32_t)dGetHzFromPeriod(periodTab[MIN_PERIOD]))
- {
- s->finetune = -128;
- s->relativeNote = -48;
- return;
- }
-
- if (midCFreq >= (int32_t)dGetHzFromPeriod(periodTab[MAX_PERIOD]))
- {
- s->finetune = 127;
- s->relativeNote = 71;
- return;
- }
-
- // check if midCFreq is matching any of the non-finetuned note frequencies (C-0..B-9)
-
- for (int8_t i = 0; i < 10*12; i++)
- {
- if (midCFreq == (int32_t)dGetHzFromPeriod(periodTab[16 + (i<<4)]))
- {
- s->finetune = 0;
- s->relativeNote = i - NOTE_C4;
- return;
- }
- }
-
- // find closest frequency in period table
-
- int32_t period = MAX_PERIOD;
- for (; period >= MIN_PERIOD; period--)
- {
- const int32_t curr = (int32_t)dGetHzFromPeriod(periodTab[period]);
- if (midCFreq == curr)
- break;
-
- if (midCFreq > curr)
- {
- const int32_t next = (int32_t)dGetHzFromPeriod(periodTab[period+1]);
- const int32_t errorCurr = ABS(curr-midCFreq);
- const int32_t errorNext = ABS(next-midCFreq);
+ /* Sets a sample's relative note and finetune according to input C-4 rate.
+ **
+ ** Note:
+ ** This algorithm uses only 5 finetune bits (like FT2 internally),
+ ** so that the resulting finetune is the same when loading it in a
+ ** tracker that does support the full 8 finetune bits.
+ */
- if (errorCurr <= errorNext)
- break; // current is the closest
+ const double dC4PeriodOffset = (NOTE_C4 * 16) + 16;
+ int32_t period = (int32_t)round(dC4PeriodOffset + (log2(dC4Hz / C4_FREQ) * 12.0 * 16.0));
- period++;
- break; // current+1 is the closest
- }
- }
+ // Hi-limit is A#9 at highest finetune. B-9 is bugged in FT2, don't include it.
+ period = CLAMP(period, 0, (12 * 16 * 10) - 1);
- s->finetune = ((period & 31) - 16) << 3;
+ s->finetune = ((period & 31) - 16) << 3; // 0..31 -> -128..120
s->relativeNote = (int8_t)(((period & ~31) >> 4) - NOTE_C4);
}
@@ -452,7 +403,8 @@ void calcReplayerVars(int32_t audioFreq)
return;
audio.dHz2MixDeltaMul = (double)MIXER_FRAC_SCALE / audioFreq;
- audio.quickVolRampSamples = (uint32_t)round(audioFreq / (double)FT2_QUICKRAMP_SAMPLES);
+ audio.quickVolRampSamples = (uint32_t)round(audioFreq / (1000.0 / FT2_QUICK_VOLRAMP_MILLISECONDS));
+ audio.fQuickVolRampSamplesMul = (float)(1.0 / (double)audio.quickVolRampSamples);
for (int32_t bpm = MIN_BPM; bpm <= MAX_BPM; bpm++)
{
@@ -477,7 +429,7 @@ 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)
+int32_t getPianoKey(uint16_t period, int8_t finetune, int8_t relativeNote)
{
assert(note2Period != NULL);
if (period > note2Period[0])
@@ -2838,6 +2790,7 @@ void closeReplayer(void)
instr[131] = NULL;
}
+ freeGaussianTable();
freeCubicSplineTable();
freeWindowedSincTables();
}
diff --git a/src/ft2_replayer.h b/src/ft2_replayer.h
@@ -3,6 +3,7 @@
#include <stdint.h>
#include <stdbool.h>
#include "ft2_unicode.h"
+#include "mixer/ft2_gaussian.h"
#include "mixer/ft2_cubic_spline.h"
#include "mixer/ft2_windowed_sinc.h"
@@ -47,7 +48,7 @@ enum
#define C4_FREQ 8363
#define NOTE_C4 (4*12)
#define NOTE_OFF 97
-#define MAX_NOTES (10*12*16+16)
+#define MAX_NOTES ((10*12*16)+16)
#define MAX_PATTERNS 256
#define MAX_PATT_LEN 256
#define MAX_INST 128
@@ -57,7 +58,7 @@ enum
#define INSTR_HEADER_SIZE 263
#define INSTR_XI_HEADER_SIZE 298
#define MAX_SAMPLE_LEN 0x3FFFFFFF
-#define FT2_QUICKRAMP_SAMPLES 200
+#define FT2_QUICK_VOLRAMP_MILLISECONDS 5
#define PROG_NAME_STR "Fasttracker II clone"
enum // sample flags
@@ -287,10 +288,7 @@ void fixSongName(void);
void fixInstrAndSampleNames(int16_t insNum);
void calcReplayerVars(int32_t rate);
-
-// used on external sample load and during sample loading in some module formats
-void tuneSample(sample_t *s, const int32_t midCFreq, bool linearPeriodsFlag);
-
+void setSampleC4Hz(sample_t *s, double dC4Hz);
void calcReplayerLogTab(void); // for linear period -> hz calculation
double dLinearPeriod2Hz(int32_t period);
diff --git a/src/ft2_sampling.c b/src/ft2_sampling.c
@@ -383,14 +383,14 @@ void startSampling(void)
smpL = &instr[editor.curInstr]->smp[editor.curSmp];
freeSample(editor.curInstr, editor.curSmp); // also sets pan to 128 and vol to 64
- tuneSample(smpL, samplingRate, audio.linearPeriodsFlag);
+ setSampleC4Hz(smpL, samplingRate);
smpL->flags |= SAMPLE_16BIT;
if (sampleInStereo)
{
smpR = &instr[editor.curInstr]->smp[editor.curSmp+1];
freeSample(editor.curInstr, editor.curSmp+1); // also sets pan to 128 and vol to 64
- tuneSample(smpR, samplingRate, audio.linearPeriodsFlag);
+ setSampleC4Hz(smpR, samplingRate);
smpR->flags |= SAMPLE_16BIT;
strcpy(smpL->name, "Left sample");
diff --git a/src/ft2_scrollbars.c b/src/ft2_scrollbars.c
@@ -87,8 +87,8 @@ scrollBar_t scrollBars[NUM_SCROLLBARS] =
//x, y, w, h, type, style funcOnDown
{ 365, 29, 18, 43, SCROLLBAR_VERTICAL, SCROLLBAR_DYNAMIC_THUMB_SIZE, sbAudOutputSetPos },
{ 365, 116, 18, 21, SCROLLBAR_VERTICAL, SCROLLBAR_DYNAMIC_THUMB_SIZE, sbAudInputSetPos },
- { 529, 117, 79, 13, SCROLLBAR_HORIZONTAL, SCROLLBAR_FIXED_THUMB_SIZE, sbAmp },
- { 529, 143, 79, 13, SCROLLBAR_HORIZONTAL, SCROLLBAR_FIXED_THUMB_SIZE, sbMasterVol },
+ { 533, 117, 75, 13, SCROLLBAR_HORIZONTAL, SCROLLBAR_FIXED_THUMB_SIZE, sbAmp },
+ { 533, 143, 75, 13, SCROLLBAR_HORIZONTAL, SCROLLBAR_FIXED_THUMB_SIZE, sbMasterVol },
// ------ CONFIG LAYOUT SCROLLBARS ------
//x, y, w, h, type, style funcOnDown
diff --git a/src/mixer/ft2_cubic_spline.c b/src/mixer/ft2_cubic_spline.c
@@ -1,3 +1,7 @@
+/*
+** Cubic Hermite spline (Catmull-Rom) interpolation LUT generator
+*/
+
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
@@ -22,10 +26,15 @@ bool calcCubicSplineTable(void)
const double x2 = x1 * x1; // x^2
const double x3 = x2 * x1; // x^3
- *fPtr++ = (float)(-0.5 * x3 + 1.0 * x2 - 0.5 * x1);
- *fPtr++ = (float)( 1.5 * x3 - 2.5 * x2 + 1.0);
- *fPtr++ = (float)(-1.5 * x3 + 2.0 * x2 + 0.5 * x1);
- *fPtr++ = (float)( 0.5 * x3 - 0.5 * x2);
+ const double t1 = -0.5 * x3 + x2 - 0.5 * x1;
+ const double t2 = 1.5 * x3 - 2.5 * x2 + 1.0;
+ const double t3 = -1.5 * x3 + 2.0 * x2 + 0.5 * x1;
+ const double t4 = 0.5 * x3 - 0.5 * x2;
+
+ *fPtr++ = (float)t1;
+ *fPtr++ = (float)t2;
+ *fPtr++ = (float)t3;
+ *fPtr++ = (float)t4;
}
return true;
diff --git a/src/mixer/ft2_cubic_spline.h b/src/mixer/ft2_cubic_spline.h
@@ -6,12 +6,8 @@
#define CUBIC_SPLINE_TAPS 4
#define CUBIC_SPLINE_WIDTH_BITS 2 // log2(CUBIC_SPLINE_TAPS)
-
-// 8192 is a good compromise
#define CUBIC_SPLINE_PHASES 8192
#define CUBIC_SPLINE_PHASES_BITS 13 // log2(CUBIC_SPLINE_PHASES)
-
-// do not change these!
#define CUBIC_SPLINE_FSHIFT (MIXER_FRAC_BITS-(CUBIC_SPLINE_PHASES_BITS+CUBIC_SPLINE_WIDTH_BITS))
#define CUBIC_SPLINE_FMASK ((CUBIC_SPLINE_TAPS*CUBIC_SPLINE_PHASES)-CUBIC_SPLINE_TAPS)
diff --git a/src/mixer/ft2_gaussian.c b/src/mixer/ft2_gaussian.c
@@ -0,0 +1,72 @@
+/*
+** Super Nintendo SPC700 interpolation LUT generator.
+** This has long believed to have a Gaussian curve, but it doesn't.
+**
+** Based on code by Mednafen and nocash:
+** https://forums.nesdev.org/viewtopic.php?t=10586
+**
+*/
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include "ft2_gaussian.h"
+#include "../ft2_video.h" // showErrorMsgBox()
+
+#define MY_PI 3.14159265358979323846264338327950288
+
+float *fGaussianLUT = NULL; // globalized
+
+bool calcGaussianTable(void)
+{
+ fGaussianLUT = (float *)malloc(GAUSSIAN_TAPS*GAUSSIAN_PHASES * sizeof (float));
+ if (fGaussianLUT == NULL)
+ {
+ showErrorMsgBox("Not enough memory!");
+ return false;
+ }
+
+ float *fPtr = fGaussianLUT;
+ for (int32_t i = 0; i < GAUSSIAN_PHASES; i++)
+ {
+ const int32_t i1 = GAUSSIAN_PHASES + i;
+ const int32_t i2 = i;
+ const int32_t i3 = (GAUSSIAN_PHASES-1) - i;
+ const int32_t i4 = ((GAUSSIAN_PHASES*2)-1) - i;
+
+ const double x1 = (0.5 + i1) * (1.0 / ((GAUSSIAN_PHASES*4)-1));
+ const double x2 = (0.5 + i2) * (1.0 / ((GAUSSIAN_PHASES*4)-1));
+ const double x3 = (0.5 + i3) * (1.0 / ((GAUSSIAN_PHASES*4)-1));
+ const double x4 = (0.5 + i4) * (1.0 / ((GAUSSIAN_PHASES*4)-1));
+
+ // Blackman window
+ const double w1 = (0.42 + (0.50 * cos(2.0 * MY_PI * x1)) + (0.08 * cos(4.0 * MY_PI * x1))) / x1;
+ const double w2 = (0.42 + (0.50 * cos(2.0 * MY_PI * x2)) + (0.08 * cos(4.0 * MY_PI * x2))) / x2;
+ const double w3 = (0.42 + (0.50 * cos(2.0 * MY_PI * x3)) + (0.08 * cos(4.0 * MY_PI * x3))) / x3;
+ const double w4 = (0.42 + (0.50 * cos(2.0 * MY_PI * x4)) + (0.08 * cos(4.0 * MY_PI * x4))) / x4;
+
+ const double t1 = sin(1.28 * MY_PI * x1) * w1;
+ const double t2 = sin(1.28 * MY_PI * x2) * w2;
+ const double t3 = sin(1.28 * MY_PI * x3) * w3;
+ const double t4 = sin(1.28 * MY_PI * x4) * w4;
+
+ // normalization value (assures unity gain when summing taps)
+ const double dScale = 1.0 / (t1 + t2 + t3 + t4);
+
+ *fPtr++ = (float)(t1 * dScale);
+ *fPtr++ = (float)(t2 * dScale);
+ *fPtr++ = (float)(t3 * dScale);
+ *fPtr++ = (float)(t4 * dScale);
+ }
+
+ return true;
+}
+
+void freeGaussianTable(void)
+{
+ if (fGaussianLUT != NULL)
+ {
+ free(fGaussianLUT);
+ fGaussianLUT = NULL;
+ }
+}
diff --git a/src/mixer/ft2_gaussian.h b/src/mixer/ft2_gaussian.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "ft2_mix.h" // MIXER_FRAC_BITS
+
+#define GAUSSIAN_TAPS 4
+#define GAUSSIAN_WIDTH_BITS 2 // log2(GAUSSIAN_TAPS)
+#define GAUSSIAN_PHASES 8192
+#define GAUSSIAN_PHASES_BITS 13 // log2(GAUSSIAN_PHASES)
+#define GAUSSIAN_FSHIFT (MIXER_FRAC_BITS-(GAUSSIAN_PHASES_BITS+GAUSSIAN_WIDTH_BITS))
+#define GAUSSIAN_FMASK ((GAUSSIAN_TAPS*GAUSSIAN_PHASES)-GAUSSIAN_TAPS)
+
+extern float *fGaussianLUT;
+
+bool calcGaussianTable(void);
+void freeGaussianTable(void);
diff --git a/src/mixer/ft2_mix.c b/src/mixer/ft2_mix.c
@@ -1,5 +1,6 @@
#include <stdint.h>
#include <stdbool.h>
+#include "../ft2_header.h" // CLAMP16()
#include "ft2_mix.h"
#include "ft2_mix_macros.h"
@@ -819,6 +820,135 @@ static void mix8bBidiLoopCIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamp
SET_BACK_MIXER_POS
}
+static void mix8bNoLoopGIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+{
+ const int8_t *base, *smpPtr;
+ float fSample, *fMixBufferL, *fMixBufferR;
+ int32_t position;
+ uint32_t i, samplesToMix, samplesLeft;
+ uint64_t positionFrac;
+
+ GET_VOL
+ GET_MIXER_VARS
+ SET_BASE8
+
+ samplesLeft = numSamples;
+ while (samplesLeft > 0)
+ {
+ LIMIT_MIX_NUM
+ samplesLeft -= samplesToMix;
+
+ for (i = 0; i < (samplesToMix & 3); i++)
+ {
+ RENDER_8BIT_SMP_GINTRP
+ INC_POS
+ }
+ samplesToMix >>= 2;
+ for (i = 0; i < samplesToMix; i++)
+ {
+ RENDER_8BIT_SMP_GINTRP
+ INC_POS
+ RENDER_8BIT_SMP_GINTRP
+ INC_POS
+ RENDER_8BIT_SMP_GINTRP
+ INC_POS
+ RENDER_8BIT_SMP_GINTRP
+ INC_POS
+ }
+
+ HANDLE_SAMPLE_END
+ }
+
+ SET_BACK_MIXER_POS
+}
+
+static void mix8bLoopGIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+{
+ const int8_t *base, *smpPtr;
+ float fSample, *fMixBufferL, *fMixBufferR;
+ int32_t position;
+ uint32_t i, samplesToMix, samplesLeft;
+ uint64_t positionFrac;
+
+ GET_VOL
+ GET_MIXER_VARS
+ SET_BASE8
+
+ samplesLeft = numSamples;
+ while (samplesLeft > 0)
+ {
+ LIMIT_MIX_NUM
+ samplesLeft -= samplesToMix;
+
+ for (i = 0; i < (samplesToMix & 3); i++)
+ {
+ RENDER_8BIT_SMP_GINTRP
+ INC_POS
+ }
+ samplesToMix >>= 2;
+ for (i = 0; i < samplesToMix; i++)
+ {
+ RENDER_8BIT_SMP_GINTRP
+ INC_POS
+ RENDER_8BIT_SMP_GINTRP
+ INC_POS
+ RENDER_8BIT_SMP_GINTRP
+ INC_POS
+ RENDER_8BIT_SMP_GINTRP
+ INC_POS
+ }
+
+ WRAP_LOOP
+ }
+
+ SET_BACK_MIXER_POS
+}
+
+static void mix8bBidiLoopGIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+{
+ const int8_t *base, *revBase, *smpPtr;
+ float fSample, *fMixBufferL, *fMixBufferR;
+ int32_t position;
+ uint32_t i, samplesToMix, samplesLeft;
+ uint64_t positionFrac, tmpDelta;
+
+ GET_VOL
+ GET_MIXER_VARS
+ SET_BASE8_BIDI
+
+ samplesLeft = numSamples;
+ while (samplesLeft > 0)
+ {
+ LIMIT_MIX_NUM
+ samplesLeft -= samplesToMix;
+
+ START_BIDI
+ for (i = 0; i < (samplesToMix & 3); i++)
+ {
+ RENDER_8BIT_SMP_GINTRP
+ INC_POS_BIDI
+ }
+ samplesToMix >>= 2;
+ for (i = 0; i < samplesToMix; i++)
+ {
+ RENDER_8BIT_SMP_GINTRP
+ INC_POS_BIDI
+ RENDER_8BIT_SMP_GINTRP
+ INC_POS_BIDI
+ RENDER_8BIT_SMP_GINTRP
+ INC_POS_BIDI
+ RENDER_8BIT_SMP_GINTRP
+ INC_POS_BIDI
+ }
+ END_BIDI
+
+ WRAP_BIDI_LOOP
+ }
+
+ SET_BACK_MIXER_POS
+}
+
+
static void mix8bRampNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
{
const int8_t *base, *smpPtr;
@@ -1759,6 +1889,158 @@ static void mix8bRampBidiLoopCIntrp(voice_t *v, uint32_t bufferPos, uint32_t num
SET_BACK_MIXER_POS
}
+static void mix8bRampNoLoopGIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+{
+ const int8_t *base, *smpPtr;
+ float fSample, *fMixBufferL, *fMixBufferR;
+ int32_t position;
+ float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR;
+ uint32_t i, samplesToMix, samplesLeft;
+ uint64_t positionFrac;
+
+ GET_VOL_RAMP
+ GET_MIXER_VARS_RAMP
+ SET_BASE8
+
+ samplesLeft = numSamples;
+ while (samplesLeft > 0)
+ {
+ LIMIT_MIX_NUM
+ LIMIT_MIX_NUM_RAMP
+ samplesLeft -= samplesToMix;
+
+ for (i = 0; i < (samplesToMix & 3); i++)
+ {
+ RENDER_8BIT_SMP_GINTRP
+ VOLUME_RAMPING
+ INC_POS
+ }
+ samplesToMix >>= 2;
+ for (i = 0; i < samplesToMix; i++)
+ {
+ RENDER_8BIT_SMP_GINTRP
+ VOLUME_RAMPING
+ INC_POS
+ RENDER_8BIT_SMP_GINTRP
+ VOLUME_RAMPING
+ INC_POS
+ RENDER_8BIT_SMP_GINTRP
+ VOLUME_RAMPING
+ INC_POS
+ RENDER_8BIT_SMP_GINTRP
+ VOLUME_RAMPING
+ INC_POS
+ }
+
+ HANDLE_SAMPLE_END
+ }
+
+ SET_VOL_BACK
+ SET_BACK_MIXER_POS
+}
+
+static void mix8bRampLoopGIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+{
+ const int8_t *base, *smpPtr;
+ float fSample, *fMixBufferL, *fMixBufferR;
+ int32_t position;
+ float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR;
+ uint32_t i, samplesToMix, samplesLeft;
+ uint64_t positionFrac;
+
+ GET_VOL_RAMP
+ GET_MIXER_VARS_RAMP
+ SET_BASE8
+
+ samplesLeft = numSamples;
+ while (samplesLeft > 0)
+ {
+ LIMIT_MIX_NUM
+ LIMIT_MIX_NUM_RAMP
+ samplesLeft -= samplesToMix;
+
+ for (i = 0; i < (samplesToMix & 3); i++)
+ {
+ RENDER_8BIT_SMP_GINTRP
+ VOLUME_RAMPING
+ INC_POS
+ }
+ samplesToMix >>= 2;
+ for (i = 0; i < samplesToMix; i++)
+ {
+ RENDER_8BIT_SMP_GINTRP
+ VOLUME_RAMPING
+ INC_POS
+ RENDER_8BIT_SMP_GINTRP
+ VOLUME_RAMPING
+ INC_POS
+ RENDER_8BIT_SMP_GINTRP
+ VOLUME_RAMPING
+ INC_POS
+ RENDER_8BIT_SMP_GINTRP
+ VOLUME_RAMPING
+ INC_POS
+ }
+
+ WRAP_LOOP
+ }
+
+ SET_VOL_BACK
+ SET_BACK_MIXER_POS
+}
+
+static void mix8bRampBidiLoopGIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+{
+ const int8_t *base, *revBase, *smpPtr;
+ float fSample, *fMixBufferL, *fMixBufferR;
+ int32_t position;
+ float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR;
+ uint32_t i, samplesToMix, samplesLeft;
+ uint64_t positionFrac, tmpDelta;
+
+ GET_VOL_RAMP
+ GET_MIXER_VARS_RAMP
+ SET_BASE8_BIDI
+
+ samplesLeft = numSamples;
+ while (samplesLeft > 0)
+ {
+ LIMIT_MIX_NUM
+ LIMIT_MIX_NUM_RAMP
+ samplesLeft -= samplesToMix;
+
+ START_BIDI
+ for (i = 0; i < (samplesToMix & 3); i++)
+ {
+ RENDER_8BIT_SMP_GINTRP
+ VOLUME_RAMPING
+ INC_POS_BIDI
+ }
+ samplesToMix >>= 2;
+ for (i = 0; i < samplesToMix; i++)
+ {
+ RENDER_8BIT_SMP_GINTRP
+ VOLUME_RAMPING
+ INC_POS_BIDI
+ RENDER_8BIT_SMP_GINTRP
+ VOLUME_RAMPING
+ INC_POS_BIDI
+ RENDER_8BIT_SMP_GINTRP
+ VOLUME_RAMPING
+ INC_POS_BIDI
+ RENDER_8BIT_SMP_GINTRP
+ VOLUME_RAMPING
+ INC_POS_BIDI
+ }
+ END_BIDI
+
+ WRAP_BIDI_LOOP
+ }
+
+ SET_VOL_BACK
+ SET_BACK_MIXER_POS
+}
+
/* ----------------------------------------------------------------------- */
/* 16-BIT MIXING ROUTINES */
/* ----------------------------------------------------------------------- */
@@ -2553,6 +2835,134 @@ static void mix16bBidiLoopCIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSam
SET_BACK_MIXER_POS
}
+static void mix16bNoLoopGIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+{
+ const int16_t *base, *smpPtr;
+ float fSample, *fMixBufferL, *fMixBufferR;
+ int32_t position;
+ uint32_t i, samplesToMix, samplesLeft;
+ uint64_t positionFrac;
+
+ GET_VOL
+ GET_MIXER_VARS
+ SET_BASE16
+
+ samplesLeft = numSamples;
+ while (samplesLeft > 0)
+ {
+ LIMIT_MIX_NUM
+ samplesLeft -= samplesToMix;
+
+ for (i = 0; i < (samplesToMix & 3); i++)
+ {
+ RENDER_16BIT_SMP_GINTRP
+ INC_POS
+ }
+ samplesToMix >>= 2;
+ for (i = 0; i < samplesToMix; i++)
+ {
+ RENDER_16BIT_SMP_GINTRP
+ INC_POS
+ RENDER_16BIT_SMP_GINTRP
+ INC_POS
+ RENDER_16BIT_SMP_GINTRP
+ INC_POS
+ RENDER_16BIT_SMP_GINTRP
+ INC_POS
+ }
+
+ HANDLE_SAMPLE_END
+ }
+
+ SET_BACK_MIXER_POS
+}
+
+static void mix16bLoopGIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+{
+ const int16_t *base, *smpPtr;
+ float fSample, *fMixBufferL, *fMixBufferR;
+ int32_t position;
+ uint32_t i, samplesToMix, samplesLeft;
+ uint64_t positionFrac;
+
+ GET_VOL
+ GET_MIXER_VARS
+ SET_BASE16
+
+ samplesLeft = numSamples;
+ while (samplesLeft > 0)
+ {
+ LIMIT_MIX_NUM
+ samplesLeft -= samplesToMix;
+
+ for (i = 0; i < (samplesToMix & 3); i++)
+ {
+ RENDER_16BIT_SMP_GINTRP
+ INC_POS
+ }
+ samplesToMix >>= 2;
+ for (i = 0; i < samplesToMix; i++)
+ {
+ RENDER_16BIT_SMP_GINTRP
+ INC_POS
+ RENDER_16BIT_SMP_GINTRP
+ INC_POS
+ RENDER_16BIT_SMP_GINTRP
+ INC_POS
+ RENDER_16BIT_SMP_GINTRP
+ INC_POS
+ }
+
+ WRAP_LOOP
+ }
+
+ SET_BACK_MIXER_POS
+}
+
+static void mix16bBidiLoopGIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+{
+ const int16_t *base, *revBase, *smpPtr;
+ float fSample, *fMixBufferL, *fMixBufferR;
+ int32_t position;
+ uint32_t i, samplesToMix, samplesLeft;
+ uint64_t positionFrac, tmpDelta;
+
+ GET_VOL
+ GET_MIXER_VARS
+ SET_BASE16_BIDI
+
+ samplesLeft = numSamples;
+ while (samplesLeft > 0)
+ {
+ LIMIT_MIX_NUM
+ samplesLeft -= samplesToMix;
+
+ START_BIDI
+ for (i = 0; i < (samplesToMix & 3); i++)
+ {
+ RENDER_16BIT_SMP_GINTRP
+ INC_POS_BIDI
+ }
+ samplesToMix >>= 2;
+ for (i = 0; i < samplesToMix; i++)
+ {
+ RENDER_16BIT_SMP_GINTRP
+ INC_POS_BIDI
+ RENDER_16BIT_SMP_GINTRP
+ INC_POS_BIDI
+ RENDER_16BIT_SMP_GINTRP
+ INC_POS_BIDI
+ RENDER_16BIT_SMP_GINTRP
+ INC_POS_BIDI
+ }
+ END_BIDI
+
+ WRAP_BIDI_LOOP
+ }
+
+ SET_BACK_MIXER_POS
+}
+
static void mix16bRampNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
{
const int16_t *base, *smpPtr;
@@ -3493,6 +3903,158 @@ static void mix16bRampBidiLoopCIntrp(voice_t *v, uint32_t bufferPos, uint32_t nu
SET_BACK_MIXER_POS
}
+static void mix16bRampNoLoopGIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+{
+ const int16_t *base, *smpPtr;
+ float fSample, *fMixBufferL, *fMixBufferR;
+ int32_t position;
+ float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR;
+ uint32_t i, samplesToMix, samplesLeft;
+ uint64_t positionFrac;
+
+ GET_VOL_RAMP
+ GET_MIXER_VARS_RAMP
+ SET_BASE16
+
+ samplesLeft = numSamples;
+ while (samplesLeft > 0)
+ {
+ LIMIT_MIX_NUM
+ LIMIT_MIX_NUM_RAMP
+ samplesLeft -= samplesToMix;
+
+ for (i = 0; i < (samplesToMix & 3); i++)
+ {
+ RENDER_16BIT_SMP_GINTRP
+ VOLUME_RAMPING
+ INC_POS
+ }
+ samplesToMix >>= 2;
+ for (i = 0; i < samplesToMix; i++)
+ {
+ RENDER_16BIT_SMP_GINTRP
+ VOLUME_RAMPING
+ INC_POS
+ RENDER_16BIT_SMP_GINTRP
+ VOLUME_RAMPING
+ INC_POS
+ RENDER_16BIT_SMP_GINTRP
+ VOLUME_RAMPING
+ INC_POS
+ RENDER_16BIT_SMP_GINTRP
+ VOLUME_RAMPING
+ INC_POS
+ }
+
+ HANDLE_SAMPLE_END
+ }
+
+ SET_VOL_BACK
+ SET_BACK_MIXER_POS
+}
+
+static void mix16bRampLoopGIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+{
+ const int16_t *base, *smpPtr;
+ float fSample, *fMixBufferL, *fMixBufferR;
+ int32_t position;
+ float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR;
+ uint32_t i, samplesToMix, samplesLeft;
+ uint64_t positionFrac;
+
+ GET_VOL_RAMP
+ GET_MIXER_VARS_RAMP
+ SET_BASE16
+
+ samplesLeft = numSamples;
+ while (samplesLeft > 0)
+ {
+ LIMIT_MIX_NUM
+ LIMIT_MIX_NUM_RAMP
+ samplesLeft -= samplesToMix;
+
+ for (i = 0; i < (samplesToMix & 3); i++)
+ {
+ RENDER_16BIT_SMP_GINTRP
+ VOLUME_RAMPING
+ INC_POS
+ }
+ samplesToMix >>= 2;
+ for (i = 0; i < samplesToMix; i++)
+ {
+ RENDER_16BIT_SMP_GINTRP
+ VOLUME_RAMPING
+ INC_POS
+ RENDER_16BIT_SMP_GINTRP
+ VOLUME_RAMPING
+ INC_POS
+ RENDER_16BIT_SMP_GINTRP
+ VOLUME_RAMPING
+ INC_POS
+ RENDER_16BIT_SMP_GINTRP
+ VOLUME_RAMPING
+ INC_POS
+ }
+
+ WRAP_LOOP
+ }
+
+ SET_VOL_BACK
+ SET_BACK_MIXER_POS
+}
+
+static void mix16bRampBidiLoopGIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
+{
+ const int16_t *base, *revBase, *smpPtr;
+ float fSample, *fMixBufferL, *fMixBufferR;
+ int32_t position;
+ float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR;
+ uint32_t i, samplesToMix, samplesLeft;
+ uint64_t positionFrac, tmpDelta;
+
+ GET_VOL_RAMP
+ GET_MIXER_VARS_RAMP
+ SET_BASE16_BIDI
+
+ samplesLeft = numSamples;
+ while (samplesLeft > 0)
+ {
+ LIMIT_MIX_NUM
+ LIMIT_MIX_NUM_RAMP
+ samplesLeft -= samplesToMix;
+
+ START_BIDI
+ for (i = 0; i < (samplesToMix & 3); i++)
+ {
+ RENDER_16BIT_SMP_GINTRP
+ VOLUME_RAMPING
+ INC_POS_BIDI
+ }
+ samplesToMix >>= 2;
+ for (i = 0; i < samplesToMix; i++)
+ {
+ RENDER_16BIT_SMP_GINTRP
+ VOLUME_RAMPING
+ INC_POS_BIDI
+ RENDER_16BIT_SMP_GINTRP
+ VOLUME_RAMPING
+ INC_POS_BIDI
+ RENDER_16BIT_SMP_GINTRP
+ VOLUME_RAMPING
+ INC_POS_BIDI
+ RENDER_16BIT_SMP_GINTRP
+ VOLUME_RAMPING
+ INC_POS_BIDI
+ }
+ END_BIDI
+
+ WRAP_BIDI_LOOP
+ }
+
+ SET_VOL_BACK
+ SET_BACK_MIXER_POS
+}
+
// -----------------------------------------------------------------------
const mixFunc mixFuncTab[] =
@@ -3515,6 +4077,9 @@ const mixFunc mixFuncTab[] =
(mixFunc)mix8bNoLoopCIntrp,
(mixFunc)mix8bLoopCIntrp,
(mixFunc)mix8bBidiLoopCIntrp,
+ (mixFunc)mix8bNoLoopGIntrp,
+ (mixFunc)mix8bLoopGIntrp,
+ (mixFunc)mix8bBidiLoopGIntrp,
// 16-bit
(mixFunc)mix16bNoLoop,
@@ -3532,6 +4097,9 @@ const mixFunc mixFuncTab[] =
(mixFunc)mix16bNoLoopCIntrp,
(mixFunc)mix16bLoopCIntrp,
(mixFunc)mix16bBidiLoopCIntrp,
+ (mixFunc)mix16bNoLoopGIntrp,
+ (mixFunc)mix16bLoopGIntrp,
+ (mixFunc)mix16bBidiLoopGIntrp,
// volume ramping
@@ -3551,6 +4119,9 @@ const mixFunc mixFuncTab[] =
(mixFunc)mix8bRampNoLoopCIntrp,
(mixFunc)mix8bRampLoopCIntrp,
(mixFunc)mix8bRampBidiLoopCIntrp,
+ (mixFunc)mix8bRampNoLoopGIntrp,
+ (mixFunc)mix8bRampLoopGIntrp,
+ (mixFunc)mix8bRampBidiLoopGIntrp,
// 16-bit
(mixFunc)mix16bRampNoLoop,
@@ -3567,5 +4138,8 @@ const mixFunc mixFuncTab[] =
(mixFunc)mix16bRampBidiLoopS16Intrp,
(mixFunc)mix16bRampNoLoopCIntrp,
(mixFunc)mix16bRampLoopCIntrp,
- (mixFunc)mix16bRampBidiLoopCIntrp
+ (mixFunc)mix16bRampBidiLoopCIntrp,
+ (mixFunc)mix16bRampNoLoopGIntrp,
+ (mixFunc)mix16bRampLoopGIntrp,
+ (mixFunc)mix16bRampBidiLoopGIntrp
};
diff --git a/src/mixer/ft2_mix.h b/src/mixer/ft2_mix.h
@@ -2,6 +2,20 @@
#include <stdint.h>
+enum
+{
+ // don't change the order of these! (yes, it looks off)
+ INTERPOLATION_DISABLED = 0,
+ INTERPOLATION_SINC8 = 1,
+ INTERPOLATION_LINEAR = 2,
+ INTERPOLATION_SINC16 = 3,
+ INTERPOLATION_CUBIC = 4,
+ INTERPOLATION_GAUSSIAN = 5,
+ // ------
+
+ NUM_INTERPOLATORS,
+};
+
#define MAX_TAPS 16
#define MAX_LEFT_TAPS ((MAX_TAPS/2)-1)
#define MAX_RIGHT_TAPS (MAX_TAPS/2)
diff --git a/src/mixer/ft2_mix_macros.h b/src/mixer/ft2_mix_macros.h
@@ -1,6 +1,8 @@
#pragma once
#include "../ft2_audio.h"
+#include "ft2_cubic_spline.h"
+#include "ft2_gaussian.h"
#include "ft2_windowed_sinc.h"
/* ----------------------------------------------------------------------- */
@@ -79,7 +81,7 @@
fVolumeR += fVolumeRDelta;
/* ----------------------------------------------------------------------- */
-/* NO INTERPOLATION */
+/* NO INTERPOLATION (NEAREST NEIGHBOR) */
/* ----------------------------------------------------------------------- */
#define RENDER_8BIT_SMP \
@@ -167,6 +169,36 @@
*fMixBufferR++ += fSample * fVolumeR;
/* ----------------------------------------------------------------------- */
+/* GAUSSIAN INTERPOLATION */
+/* ----------------------------------------------------------------------- */
+
+// through LUT: mixer/ft2_gaussian.c
+
+/* It may look like we are potentially going out of bounds while looking up the sample points,
+** but the sample data is actually padded on the right side, where correct tap are stored according
+** to loop mode (or no loop).
+*/
+
+#define GAUSSIAN_INTERPOLATION(s, f, scale) \
+{ \
+ const float *t = fGaussianLUT + (((uint32_t)(f) >> GAUSSIAN_FSHIFT) & GAUSSIAN_FMASK); \
+ fSample = ((s[0] * t[0]) + \
+ (s[1] * t[1]) + \
+ (s[2] * t[2]) + \
+ (s[3] * t[3])) * (1.0f / scale); \
+}
+
+#define RENDER_8BIT_SMP_GINTRP \
+ GAUSSIAN_INTERPOLATION(smpPtr, positionFrac, 128.0f) \
+ *fMixBufferL++ += fSample * fVolumeL; \
+ *fMixBufferR++ += fSample * fVolumeR;
+
+#define RENDER_16BIT_SMP_GINTRP \
+ GAUSSIAN_INTERPOLATION(smpPtr, positionFrac, 32768.0f) \
+ *fMixBufferL++ += fSample * fVolumeL; \
+ *fMixBufferR++ += fSample * fVolumeR;
+
+/* ----------------------------------------------------------------------- */
/* WINDOWED-SINC INTERPOLATION */
/* ----------------------------------------------------------------------- */
diff --git a/src/mixer/ft2_silence_mix.c b/src/mixer/ft2_silence_mix.c
@@ -1,3 +1,4 @@
+#include <assert.h>
#include <stdint.h>
#include "../ft2_audio.h"
@@ -5,13 +6,13 @@
void silenceMixRoutine(voice_t *v, int32_t numSamples)
{
- const uint64_t samplesToMix = (uint64_t)v->delta * (uint32_t)numSamples; // fixed-point
+ assert((uint32_t)numSamples <= UINT16_MAX);
- const uint32_t samples = (uint32_t)(samplesToMix >> MIXER_FRAC_BITS);
- const uint64_t samplesFrac = (samplesToMix & MIXER_FRAC_MASK) + v->positionFrac;
+ const uint32_t samplesInt = (uint32_t)(v->delta >> MIXER_FRAC_BITS) * (uint32_t)numSamples;
+ const uint64_t samplesFrac = (uint64_t)(v->delta & MIXER_FRAC_SCALE) * (uint32_t)numSamples;
- uint32_t position = v->position + samples + (uint32_t)(samplesFrac >> MIXER_FRAC_BITS);
- uint64_t positionFrac = samplesFrac & MIXER_FRAC_MASK;
+ uint32_t position = v->position + samplesInt + (uint32_t)(samplesFrac >> MIXER_FRAC_BITS);
+ uint32_t positionFrac = samplesFrac & MIXER_FRAC_MASK;
if (position < (uint32_t)v->sampleEnd) // we haven't reached the sample's end yet
{
@@ -39,6 +40,8 @@ void silenceMixRoutine(voice_t *v, int32_t numSamples)
{
if (v->loopLength >= 2)
{
+ // wrap as forward loop (position is inverted if sampling backwards, when needed)
+
const uint32_t overflow = position - v->sampleEnd;
const uint32_t cycles = overflow / v->loopLength;
const uint32_t phase = overflow % v->loopLength;
diff --git a/src/mixer/ft2_windowed_sinc.c b/src/mixer/ft2_windowed_sinc.c
@@ -1,5 +1,8 @@
-/* The code in this file is based on code from the OpenMPT project,
-** which shares the same coding license as this project.
+/*
+** Windowed-sinc (Kaiser window) w/ low-pass interpolation LUT generator
+**
+** This was originally based on OpenMPT's source code,
+** but it has been heavily modified.
*/
#include <stdint.h>
@@ -38,36 +41,38 @@ static double besselI0(double z)
return s;
}
-static void getSinc(uint32_t numTaps, float *fLUTPtr, const double beta, const double cutoff)
+static void generateSincLUT(float *fOutput, uint32_t filterWidth, uint32_t numPhases, const double beta, const double lpCutoff)
{
const double I0Beta = besselI0(beta);
- const double kPi = MY_PI * cutoff;
- const double xMul = 1.0 / ((numTaps / 2) * (numTaps / 2));
+ const double kPi = MY_PI * lpCutoff;
+ const double iMul = 1.0 / numPhases;
+ const double xMul = 1.0 / ((filterWidth / 2) * (filterWidth / 2));
- const uint32_t length = numTaps * SINC_PHASES;
- const uint32_t tapBits = (uint32_t)log2(numTaps);
- const uint32_t tapsMinus1 = numTaps - 1;
- const int32_t midPoint = (numTaps / 2) * SINC_PHASES;
+ const uint32_t length = filterWidth * numPhases;
+ const uint32_t tapBits = (uint32_t)log2(filterWidth);
+ const uint32_t phasesBits = (uint32_t)log2(numPhases);
+ const uint32_t tapsMinus1 = filterWidth - 1;
+ const int32_t midPoint = (filterWidth / 2) * numPhases;
for (uint32_t i = 0; i < length; i++)
{
- const int32_t ix = ((tapsMinus1 - (i & tapsMinus1)) << SINC_PHASES_BITS) + (i >> tapBits);
+ const int32_t ix = ((tapsMinus1 - (i & tapsMinus1)) << phasesBits) + (i >> tapBits);
double dSinc = 1.0;
if (ix != midPoint)
{
- const double x = (ix - midPoint) * (1.0 / SINC_PHASES);
+ const double x = (ix - midPoint) * iMul;
const double xPi = x * kPi;
- // sinc with Kaiser window
+ // Kaiser window
dSinc = (sin(xPi) * besselI0(beta * sqrt(1.0 - (x * x * xMul)))) / (I0Beta * xPi);
}
- fLUTPtr[i] = (float)(dSinc * cutoff);
+ fOutput[i] = (float)(dSinc * lpCutoff);
}
}
-static double dBToKaiserBeta(double dB)
+static double decibelsToKaiserBeta(double dB)
{
if (dB < 21.0)
return 0.0;
@@ -77,15 +82,19 @@ static double dBToKaiserBeta(double dB)
return 0.1102 * (dB - 8.7);
}
-bool calcWindowedSincTables(void)
+static double rippleToDecibels(double ripple)
{
- fKaiserSinc_8 = (float *)malloc(SINC1_TAPS*SINC_PHASES * sizeof (float));
- fDownSample1_8 = (float *)malloc(SINC1_TAPS*SINC_PHASES * sizeof (float));
- fDownSample2_8 = (float *)malloc(SINC1_TAPS*SINC_PHASES * sizeof (float));
+ return 20.0 * log10(ripple);
+}
- fKaiserSinc_16 = (float *)malloc(SINC2_TAPS*SINC_PHASES * sizeof (float));
- fDownSample1_16 = (float *)malloc(SINC2_TAPS*SINC_PHASES * sizeof (float));
- fDownSample2_16 = (float *)malloc(SINC2_TAPS*SINC_PHASES * sizeof (float));
+bool calcWindowedSincTables(void)
+{
+ fKaiserSinc_8 = (float *)malloc(SINC1_TAPS*SINC1_PHASES * sizeof (float));
+ fDownSample1_8 = (float *)malloc(SINC1_TAPS*SINC1_PHASES * sizeof (float));
+ fDownSample2_8 = (float *)malloc(SINC1_TAPS*SINC1_PHASES * sizeof (float));
+ fKaiserSinc_16 = (float *)malloc(SINC2_TAPS*SINC2_PHASES * sizeof (float));
+ fDownSample1_16 = (float *)malloc(SINC2_TAPS*SINC2_PHASES * sizeof (float));
+ fDownSample2_16 = (float *)malloc(SINC2_TAPS*SINC2_PHASES * sizeof (float));
if (fKaiserSinc_8 == NULL || fDownSample1_8 == NULL || fDownSample2_8 == NULL ||
fKaiserSinc_16 == NULL || fDownSample1_16 == NULL || fDownSample2_16 == NULL)
@@ -94,23 +103,47 @@ bool calcWindowedSincTables(void)
return false;
}
- sincDownsample1Ratio = (uint64_t)(1.1875 * MIXER_FRAC_SCALE);
- sincDownsample2Ratio = (uint64_t)(1.5 * MIXER_FRAC_SCALE);
-
- // sidelobe attenuation (Kaiser beta)
- const double b0 = dBToKaiserBeta(96.15645);
- const double b1 = dBToKaiserBeta(85.83249);
- const double b2 = dBToKaiserBeta(72.22088);
+ // resampling ratios for picking suitable downsample LUT
+ const double ratio1 = 1.1875; // fKaiserSinc if <=
+ const double ratio2 = 1.5; // fDownSample1 if <=, fDownSample2 if >
+
+ sincDownsample1Ratio = (uint64_t)(ratio1 * MIXER_FRAC_SCALE);
+ sincDownsample2Ratio = (uint64_t)(ratio2 * MIXER_FRAC_SCALE);
+
+ /* Max ripple (to be converted into Kaiser beta parameter)
+ **
+ ** NOTE:
+ ** These may not be the correct values. Proper calculation
+ ** is needed, but is beyond my knowledge.
+ */
+ const double maxRipple1 = 1 << 16;
+ const double maxRipple2 = 1 << 14;
+ const double maxRipple3 = 1 << 12;
+
+ // convert max ripple into sidelode attenuation (dB)
+ double db1 = rippleToDecibels(maxRipple1);
+ double db2 = rippleToDecibels(maxRipple2);
+ double db3 = rippleToDecibels(maxRipple3);
+
+ // convert sidelobe attenuation (dB) into Kaiser beta
+ const double b1 = decibelsToKaiserBeta(db1);
+ const double b2 = decibelsToKaiserBeta(db2);
+ const double b3 = decibelsToKaiserBeta(db3);
+
+ // low-pass cutoffs (could maybe use some further tweaking)
+ const double c1 = 1.000;
+ const double c2 = 0.500;
+ const double c3 = 0.425;
// 8 point
- getSinc(SINC1_TAPS, fKaiserSinc_8, b0, 1.0);
- getSinc(SINC1_TAPS, fDownSample1_8, b1, 0.5);
- getSinc(SINC1_TAPS, fDownSample2_8, b2, 0.425);
+ generateSincLUT(fKaiserSinc_8, SINC1_TAPS, SINC1_PHASES, b1, c1);
+ generateSincLUT(fDownSample1_8, SINC1_TAPS, SINC1_PHASES, b2, c2);
+ generateSincLUT(fDownSample2_8, SINC1_TAPS, SINC1_PHASES, b3, c3);
// 16 point
- getSinc(SINC2_TAPS, fKaiserSinc_16, b0, 1.0);
- getSinc(SINC2_TAPS, fDownSample1_16, b1, 0.5);
- getSinc(SINC2_TAPS, fDownSample2_16, b2, 0.425);
+ generateSincLUT(fKaiserSinc_16, SINC2_TAPS, SINC2_PHASES, b1, c1);
+ generateSincLUT(fDownSample1_16, SINC2_TAPS, SINC2_PHASES, b2, c2);
+ generateSincLUT(fDownSample2_16, SINC2_TAPS, SINC2_PHASES, b3, c3);
return true;
}
diff --git a/src/mixer/ft2_windowed_sinc.h b/src/mixer/ft2_windowed_sinc.h
@@ -4,21 +4,19 @@
#include <stdbool.h>
#include "ft2_mix.h" // MIXER_FRAC_BITS
-// 8192 is a good compromise
-#define SINC_PHASES 8192
-#define SINC_PHASES_BITS 13 // log2(SINC_PHASES)
-
-// do not change these!
-
#define SINC1_TAPS 8
#define SINC8_WIDTH_BITS 3 // log2(SINC1_TAPS)
-#define SINC8_FSHIFT (MIXER_FRAC_BITS-(SINC_PHASES_BITS+SINC8_WIDTH_BITS))
-#define SINC8_FMASK ((SINC1_TAPS*SINC_PHASES)-SINC1_TAPS)
+#define SINC1_PHASES 8192
+#define SINC1_PHASES_BITS 13 // log2(SINC1_PHASES)
+#define SINC8_FSHIFT (MIXER_FRAC_BITS-(SINC1_PHASES_BITS+SINC8_WIDTH_BITS))
+#define SINC8_FMASK ((SINC1_TAPS*SINC1_PHASES)-SINC1_TAPS)
#define SINC2_TAPS 16
#define SINC16_WIDTH_BITS 4 // log2(SINC2_TAPS)
-#define SINC16_FSHIFT (MIXER_FRAC_BITS-(SINC_PHASES_BITS+SINC16_WIDTH_BITS))
-#define SINC16_FMASK ((SINC2_TAPS*SINC_PHASES)-SINC2_TAPS)
+#define SINC2_PHASES 8192
+#define SINC2_PHASES_BITS 13 // log2(SINC2_PHASES)
+#define SINC16_FSHIFT (MIXER_FRAC_BITS-(SINC2_PHASES_BITS+SINC16_WIDTH_BITS))
+#define SINC16_FMASK ((SINC2_TAPS*SINC2_PHASES)-SINC2_TAPS)
extern float *fKaiserSinc_8, *fDownSample1_8, *fDownSample2_8;
extern float *fKaiserSinc_16, *fDownSample1_16, *fDownSample2_16;
diff --git a/src/modloaders/ft2_load_s3m.c b/src/modloaders/ft2_load_s3m.c
@@ -563,7 +563,7 @@ bool loadS3M(FILE *f, uint32_t filesize)
s->loopStart = smpHdr.loopStart;
s->loopLength = smpHdr.loopEnd - smpHdr.loopStart;
- tuneSample(s, smpHdr.midCFreq, tmpLinearPeriodsFlag);
+ setSampleC4Hz(s, smpHdr.midCFreq);
if (sample16Bit)
{
diff --git a/src/modloaders/ft2_load_stm.c b/src/modloaders/ft2_load_stm.c
@@ -212,7 +212,7 @@ bool loadSTM(FILE *f, uint32_t filesize)
s->loopLength = hdr.smp[i].loopEnd - hdr.smp[i].loopStart;
memcpy(s->name, hdr.smp[i].name, 12);
- tuneSample(s, hdr.smp[i].midCFreq, tmpLinearPeriodsFlag);
+ setSampleC4Hz(s, hdr.smp[i].midCFreq);
if (s->loopStart < s->length && hdr.smp[i].loopEnd > s->loopStart && hdr.smp[i].loopEnd != 0xFFFF)
{
diff --git a/src/smploaders/ft2_load_aiff.c b/src/smploaders/ft2_load_aiff.c
@@ -15,7 +15,7 @@
#include "../ft2_sysreqs.h"
#include "../ft2_sample_loader.h"
-static uint32_t getAIFFSampleRate(uint8_t *in);
+static double getAIFFSampleRate(uint8_t *in);
static bool aiffIsStereo(FILE *f); // only ran on files that are confirmed to be AIFFs
bool loadAIFF(FILE *f, uint32_t filesize)
@@ -128,7 +128,7 @@ bool loadAIFF(FILE *f, uint32_t filesize)
return false;
}
- uint32_t sampleRate = getAIFFSampleRate(sampleRateBytes);
+ double dSampleRate = getAIFFSampleRate(sampleRateBytes);
// sample data chunk
@@ -578,14 +578,14 @@ bool loadAIFF(FILE *f, uint32_t filesize)
s->volume = 64;
s->panning = 128;
- tuneSample(s, sampleRate, audio.linearPeriodsFlag);
+ setSampleC4Hz(s, dSampleRate);
return true;
}
-static uint32_t getAIFFSampleRate(uint8_t *in)
+static double getAIFFSampleRate(uint8_t *in)
{
- /* 80-bit IEEE-754 to unsigned 32-bit integer (rounded).
+ /* 80-bit IEEE-754 to unsigned double-precision float.
** Sign bit is ignored.
*/
@@ -598,8 +598,7 @@ static uint32_t getAIFFSampleRate(uint8_t *in)
double dExp = exp15 - EXP_BIAS;
double dMantissa = mantissa63 / (INT64_MAX+1.0);
- double dResult = (1.0 + dMantissa) * exp2(dExp);
- return (uint32_t)round(dResult);
+ return (1.0 + dMantissa) * exp2(dExp);
}
static bool aiffIsStereo(FILE *f) // only ran on files that are confirmed to be AIFFs
diff --git a/src/smploaders/ft2_load_flac.c b/src/smploaders/ft2_load_flac.c
@@ -94,7 +94,7 @@ bool loadFLAC(FILE *f, uint32_t filesize)
FLAC__stream_decoder_finish(decoder);
FLAC__stream_decoder_delete(decoder);
- tuneSample(s, sampleRate, audio.linearPeriodsFlag);
+ setSampleC4Hz(s, sampleRate);
return true;
@@ -378,7 +378,7 @@ static FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *
{
int16_t *dst16 = (int16_t *)s->dataPtr + samplesRead;
for (uint32_t i = 0; i < blockSize; i++)
- dst16[i] = (int16_t)((src32_L[i] + src32_R[i]) >> (16+1));
+ dst16[i] = (int16_t)((src32_L[i] + src32_R[i]) >> ((24-16)+1));
}
break;
@@ -411,7 +411,7 @@ static FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *
{
int16_t *dst16 = (int16_t *)s->dataPtr + samplesRead;
for (uint32_t i = 0; i < blockSize; i++)
- dst16[i] = (int16_t)(src32[i] >> 8);
+ dst16[i] = (int16_t)(src32[i] >> (24-16));
}
break;
diff --git a/src/smploaders/ft2_load_iff.c b/src/smploaders/ft2_load_iff.c
@@ -143,7 +143,7 @@ bool loadIFF(FILE *f, uint32_t filesize)
s->volume = (uint8_t)volume;
s->panning = 128;
- tuneSample(s, sampleRate, audio.linearPeriodsFlag);
+ setSampleC4Hz(s, sampleRate);
// set name
if (namePtr != 0 && nameLen > 0)
diff --git a/src/smploaders/ft2_load_wav.c b/src/smploaders/ft2_load_wav.c
@@ -592,7 +592,7 @@ bool loadWAV(FILE *f, uint32_t filesize)
bool sample16Bit = !!(s->flags & SAMPLE_16BIT);
reallocateSmpData(s, sampleLength, sample16Bit); // readjust memory needed
- tuneSample(s, sampleRate, audio.linearPeriodsFlag);
+ setSampleC4Hz(s, sampleRate);
s->volume = 64;
s->panning = 128;