ft2-clone

Fasttracker 2 clone
Log | Files | Refs | README | LICENSE

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:
Msrc/ft2_audio.c | 33+++++++++++++++++++++------------
Msrc/ft2_audio.h | 2+-
Msrc/ft2_checkboxes.c | 2+-
Msrc/ft2_config.c | 72+++++++++++++++++++++++++++++++++++++++++-------------------------------
Msrc/ft2_config.h | 21+++------------------
Msrc/ft2_header.h | 2+-
Msrc/ft2_inst_ed.c | 44+++++++++++++++++++++++---------------------
Msrc/ft2_main.c | 2+-
Msrc/ft2_pushbuttons.c | 4++--
Msrc/ft2_radiobuttons.c | 31++++++++++++++++---------------
Msrc/ft2_radiobuttons.h | 3++-
Msrc/ft2_replayer.c | 83+++++++++++++++++--------------------------------------------------------------
Msrc/ft2_replayer.h | 10++++------
Msrc/ft2_sampling.c | 4++--
Msrc/ft2_scrollbars.c | 4++--
Msrc/mixer/ft2_cubic_spline.c | 17+++++++++++++----
Msrc/mixer/ft2_cubic_spline.h | 4----
Asrc/mixer/ft2_gaussian.c | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mixer/ft2_gaussian.h | 17+++++++++++++++++
Msrc/mixer/ft2_mix.c | 576++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/mixer/ft2_mix.h | 14++++++++++++++
Msrc/mixer/ft2_mix_macros.h | 34+++++++++++++++++++++++++++++++++-
Msrc/mixer/ft2_silence_mix.c | 13++++++++-----
Msrc/mixer/ft2_windowed_sinc.c | 101++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Msrc/mixer/ft2_windowed_sinc.h | 18++++++++----------
Msrc/modloaders/ft2_load_s3m.c | 2+-
Msrc/modloaders/ft2_load_stm.c | 2+-
Msrc/smploaders/ft2_load_aiff.c | 13++++++-------
Msrc/smploaders/ft2_load_flac.c | 6+++---
Msrc/smploaders/ft2_load_iff.c | 2+-
Msrc/smploaders/ft2_load_wav.c | 2+-
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;