diff --git a/release/macos/protracker.ini b/release/macos/protracker.ini index f19bf35..05efeaa 100644 --- a/release/macos/protracker.ini +++ b/release/macos/protracker.ini @@ -80,6 +80,12 @@ VSYNCOFF=FALSE HWMOUSE=TRUE [GENERAL SETTINGS] +; Don't show downsample dialog after loading a sample whose frequency is >22kHz. +; Syntax: TRUE or FALSE +; Default value: FALSE +; +NO_DWNSMP_ON_SMP_LOAD=FALSE + ; Hide last modification dates in Disk Op. to get longer dir/file names ; Syntax: TRUE or FALSE ; Default value: FALSE diff --git a/release/other/protracker.ini b/release/other/protracker.ini index f19bf35..05efeaa 100644 --- a/release/other/protracker.ini +++ b/release/other/protracker.ini @@ -80,6 +80,12 @@ VSYNCOFF=FALSE HWMOUSE=TRUE [GENERAL SETTINGS] +; Don't show downsample dialog after loading a sample whose frequency is >22kHz. +; Syntax: TRUE or FALSE +; Default value: FALSE +; +NO_DWNSMP_ON_SMP_LOAD=FALSE + ; Hide last modification dates in Disk Op. to get longer dir/file names ; Syntax: TRUE or FALSE ; Default value: FALSE diff --git a/release/win32/protracker.ini b/release/win32/protracker.ini index f19bf35..05efeaa 100644 --- a/release/win32/protracker.ini +++ b/release/win32/protracker.ini @@ -80,6 +80,12 @@ VSYNCOFF=FALSE HWMOUSE=TRUE [GENERAL SETTINGS] +; Don't show downsample dialog after loading a sample whose frequency is >22kHz. +; Syntax: TRUE or FALSE +; Default value: FALSE +; +NO_DWNSMP_ON_SMP_LOAD=FALSE + ; Hide last modification dates in Disk Op. to get longer dir/file names ; Syntax: TRUE or FALSE ; Default value: FALSE diff --git a/release/win64/protracker.ini b/release/win64/protracker.ini index f19bf35..05efeaa 100644 --- a/release/win64/protracker.ini +++ b/release/win64/protracker.ini @@ -80,6 +80,12 @@ VSYNCOFF=FALSE HWMOUSE=TRUE [GENERAL SETTINGS] +; Don't show downsample dialog after loading a sample whose frequency is >22kHz. +; Syntax: TRUE or FALSE +; Default value: FALSE +; +NO_DWNSMP_ON_SMP_LOAD=FALSE + ; Hide last modification dates in Disk Op. to get longer dir/file names ; Syntax: TRUE or FALSE ; Default value: FALSE diff --git a/src/pt2_audio.c b/src/pt2_audio.c index 7663f7f..5e8183e 100644 --- a/src/pt2_audio.c +++ b/src/pt2_audio.c @@ -277,13 +277,12 @@ void paulaSetPeriod(int32_t ch, uint16_t period) v->dOldVoiceDeltaMul = 1.0 / v->dOldVoiceDelta; } - v->dNewDelta = v->dOldVoiceDelta; - if (v->dLastDelta == 0.0) // for BLEP - v->dLastDelta = v->dNewDelta; - - v->dNewDeltaMul = v->dOldVoiceDeltaMul; - if (v->dLastDeltaMul == 0.0) // for BLEP - v->dLastDeltaMul = v->dNewDeltaMul; + v->AUD_PER_delta = v->dOldVoiceDelta; + + // set BLEP stuff + v->dDeltaMul = v->dOldVoiceDeltaMul; + if (v->dLastDelta == 0.0) + v->dLastDelta = v->AUD_PER_delta; } void paulaSetVolume(int32_t ch, uint16_t vol) @@ -299,7 +298,7 @@ void paulaSetVolume(int32_t ch, uint16_t vol) // ------------------------ // multiplying by this also scales the sample from -128..127 -> -1.0 .. ~0.99 - v->dScaledVolume = realVol * (1.0 / (128.0 * 64.0)); + v->AUD_VOL = realVol * (1.0 / (128.0 * 64.0)); if (editor.songPlaying) { @@ -316,26 +315,12 @@ void paulaSetLength(int32_t ch, uint16_t len) { paulaVoice_t *v = &paula[ch]; - int32_t realLength = len; - if (realLength == 0) - { - realLength = 1+65535; - /* Confirmed behavior on real Amiga. We have room for this - ** even at the last sample slot, so it will never overflow! - ** - ** PS: I don't really know if it's possible for ProTracker to - ** set a Paula length of 0, but I fully support this Paula - ** behavior just in case. - */ - } - - realLength <<= 1; // we work with bytes, not words + v->AUD_LEN = len; - v->newLength = realLength; if (editor.songPlaying) v->syncFlags |= SET_SCOPE_LENGTH; else - scope[ch].newLength = realLength; + scope[ch].newLength = len*2; } void paulaSetData(int32_t ch, const int8_t *src) @@ -345,7 +330,8 @@ void paulaSetData(int32_t ch, const int8_t *src) if (src == NULL) src = &song->sampleData[RESERVED_SAMPLE_OFFSET]; // 128K reserved sample - v->newData = src; + v->AUD_LC = src; + if (editor.songPlaying) v->syncFlags |= SET_SCOPE_DATA; else @@ -368,41 +354,40 @@ void paulaStartDMA(int32_t ch) { paulaVoice_t *v = &paula[ch]; - const int8_t *dat = v->newData; + const int8_t *dat = v->AUD_LC; if (dat == NULL) dat = &song->sampleData[RESERVED_SAMPLE_OFFSET]; // 128K reserved sample - int32_t length = v->newLength; // in bytes, not words - if (length == 0) - length = 1+65535; + /* This is not really accurate to what happens on Paula + ** during DMA start, but it's good enough. + */ - v->dPhase = v->dLastPhase = 0.0; - v->pos = 0; - v->data = dat; - v->length = length; - v->dDelta = v->dLastDelta = v->dNewDelta; - v->dDeltaMul = v->dLastDeltaMul = v->dNewDeltaMul; + v->dDelta = v->AUD_PER_delta; + v->location = v->AUD_LC; + v->lengthCounter = v->AUD_LEN; - /* Read first sample data point into cache now. - ** - ** (multiplying by dScaledVolume will also change the scale - ** from -128..127 to -1.0 .. ~0.99.) - */ - v->dCachedSamplePoint = v->data[0] * v->dScaledVolume; + v->dSample = 0.0; + v->sampleCounter = 0; // read new DMA data samples ASAP + + // set BLEP stuff + v->dLastPhase = 0.0; + v->dLastDelta = v->dDelta; + v->dBlepOffset = 0.0; + v->dPhase = 0.0; v->DMA_active = true; if (editor.songPlaying) { v->syncTriggerData = dat; - v->syncTriggerLength = length; + v->syncTriggerLength = v->AUD_LEN * 2; v->syncFlags |= TRIGGER_SCOPE; } else { scope_t *s = &scope[ch]; s->newData = dat; - s->newLength = length; + s->newLength = v->AUD_LEN * 2; scopeTrigger(ch); } } @@ -448,20 +433,17 @@ void mixChannels(int32_t numSamples) ** is temporarily forced offline, and its voice pointers are ** cleared to prevent expired pointer addresses. */ - if (!v->DMA_active || v->data == NULL) + if (!v->DMA_active || v->location == NULL || v->AUD_LC == NULL) continue; double *dMixBuf = dMixBufSelect[i]; // what output channel to mix into (L, R, R, L) for (int32_t j = 0; j < numSamples; j++) { - double dSmp = v->dCachedSamplePoint; + double dSmp = v->dSample; if (dSmp != bSmp->dLastValue) { if (v->dLastDelta > v->dLastPhase) - { - // v->dLastDeltaMul is (1.0 / v->dLastDelta) (pre-computed for speed, div -> mul) - blepAdd(bSmp, v->dLastPhase * v->dLastDeltaMul, bSmp->dLastValue - dSmp); - } + blepAdd(bSmp, v->dBlepOffset, bSmp->dLastValue - dSmp); bSmp->dLastValue = dSmp; } @@ -476,33 +458,40 @@ void mixChannels(int32_t numSamples) { v->dPhase -= 1.0; - // Paula only updates period (delta) during sample fetching - v->dDelta = v->dNewDelta; - v->dDeltaMul = v->dNewDeltaMul; - // -------------------------------------------------------- - - v->dLastPhase = v->dPhase; - v->dLastDelta = v->dDelta; - v->dLastDeltaMul = v->dDeltaMul; + // Paula only updates period (delta) during period refetching (this stage) + v->dDelta = v->AUD_PER_delta; - if (++v->pos >= v->length) + if (v->sampleCounter == 0) { - v->pos = 0; - - // re-fetch new Paula register values now - v->length = v->newLength; - v->data = v->newData; + // it's time to read new samples from DMA + + if (--v->lengthCounter == 0) + { + v->lengthCounter = v->AUD_LEN; + v->location = v->AUD_LC; + } + + // fill DMA data buffer + v->AUD_DAT[0] = *v->location++; + v->AUD_DAT[1] = *v->location++; + v->sampleCounter = 2; } - /* Read sample into cache now. - ** Also change volume here as well. It has recently been - ** discovered that Paula only updates its volume during period - ** fetching (when it's reading the next sample point). - ** - ** (multiplying by dScaledVolume will also change the scale - ** from -128..127 to -1.0 .. ~0.99.) + /* Pre-compute current sample point. + ** Output volume is only read from AUDxVOL at this stage, + ** and we don't emulate volume PWM anyway, so we can + ** pre-multiply by volume at this point. */ - v->dCachedSamplePoint = v->data[v->pos] * v->dScaledVolume; + v->dSample = v->AUD_DAT[0] * v->AUD_VOL; // -128..127 * 0.0 .. 1.0 + + // progress AUD_DAT buffer + v->AUD_DAT[0] = v->AUD_DAT[1]; + v->sampleCounter--; + + // setup BLEP stuff + v->dBlepOffset = v->dPhase * v->dDeltaMul; + v->dLastPhase = v->dPhase; + v->dLastDelta = v->dDelta; } } } @@ -881,7 +870,6 @@ void outputAudio(int16_t *target, int32_t numSamples) } } } - } } @@ -910,8 +898,8 @@ static void fillVisualsSyncBuffer(void) s->period = v->syncPeriod; s->triggerData = v->syncTriggerData; s->triggerLength = v->syncTriggerLength; - s->newData = v->newData; - s->newLength = v->newLength; + s->newData = v->AUD_LC; + s->newLength = v->AUD_LEN * 2; s->vuVolume = c->syncVuVolume; s->analyzerVolume = c->syncAnalyzerVolume; s->analyzerPeriod = c->syncAnalyzerPeriod; diff --git a/src/pt2_audio.h b/src/pt2_audio.h index b4579f7..d569f5b 100644 --- a/src/pt2_audio.h +++ b/src/pt2_audio.h @@ -27,11 +27,21 @@ typedef struct voice_t { volatile bool DMA_active; - const int8_t *data, *newData; - int32_t length, newLength, pos; + // internal values (don't modify directly!) + int8_t AUD_DAT[2]; // DMA data buffer + const int8_t* location; // current location + uint16_t lengthCounter; // current length + int32_t sampleCounter; // how many bytes left in AUD_DAT + double dSample; // current sample point - double dDelta, dDeltaMul, dPhase, dLastDelta, dLastDeltaMul, dLastPhase; - double dCachedSamplePoint, dScaledVolume, dNewDelta, dNewDeltaMul; + // registers modified by Paula functions + const int8_t* AUD_LC; // location + uint16_t AUD_LEN; // length (in words) + double AUD_PER_delta; // delta + double AUD_VOL; // volume + + double dBlepOffset, dDelta, dPhase, dLastDelta, dLastPhase; + double dScaledVolume, dDeltaMul; // period cache int32_t oldPeriod; diff --git a/src/pt2_config.c b/src/pt2_config.c index 36d2be4..9978408 100644 --- a/src/pt2_config.c +++ b/src/pt2_config.c @@ -42,6 +42,7 @@ void loadConfig(void) FILE *f; // set default config values first + config.noDownsampleOnSmpLoad = false; config.disableE8xEffect = false; config.fullScreenStretch = false; config.pattDots = false; @@ -193,6 +194,13 @@ static bool loadProTrackerDotIni(FILE *f) continue; } + // NO_DWNSMP_ON_SMP_LOAD (no dialog for 2x downsample after >22kHz sample load) + else if (!_strnicmp(configLine, "NO_DWNSMP_ON_SMP_LOAD=", 22)) + { + if (!_strnicmp(&configLine[22], "TRUE", 4)) config.noDownsampleOnSmpLoad = true; + else if (!_strnicmp(&configLine[22], "FALSE", 5)) config.noDownsampleOnSmpLoad = false; + } + // DISABLE_E8X (Karplus-Strong command) else if (!_strnicmp(configLine, "DISABLE_E8X=", 12)) { diff --git a/src/pt2_config.h b/src/pt2_config.h index 0d4e7c3..852b75d 100644 --- a/src/pt2_config.h +++ b/src/pt2_config.h @@ -15,7 +15,7 @@ typedef struct config_t char *defModulesDir, *defSamplesDir; bool waveformCenterLine, pattDots, compoMode, autoCloseDiskOp, hideDiskOpDates, hwMouse; bool transDel, fullScreenStretch, vsyncOff, modDot, blankZeroFlag, realVuMeters, rememberPlayMode; - bool startInFullscreen, integerScaling, disableE8xEffect; + bool startInFullscreen, integerScaling, disableE8xEffect, noDownsampleOnSmpLoad; int8_t stereoSeparation, videoScaleFactor, accidental; uint8_t pixelFilter, filterModel; uint16_t quantizeValue; diff --git a/src/pt2_header.h b/src/pt2_header.h index 3d88869..1c8635c 100644 --- a/src/pt2_header.h +++ b/src/pt2_header.h @@ -14,7 +14,7 @@ #include "pt2_unicode.h" #include "pt2_palette.h" -#define PROG_VER_STR "1.34" +#define PROG_VER_STR "1.35" #ifdef _WIN32 #define DIR_DELIMITER '\\' diff --git a/src/pt2_sample_loader.c b/src/pt2_sample_loader.c index 802323e..80a3d0e 100644 --- a/src/pt2_sample_loader.c +++ b/src/pt2_sample_loader.c @@ -239,7 +239,7 @@ bool loadWAVSample(UNICHAR *fileName, char *entryName, int8_t forceDownSampling) return false; } - if (sampleRate > 22050) + if (sampleRate > 22050 && !config.noDownsampleOnSmpLoad) { if (forceDownSampling == -1) { @@ -884,7 +884,7 @@ bool loadIFFSample(UNICHAR *fileName, char *entryName, int8_t forceDownSampling) return false; } - if (sampleRate > 22050) + if (sampleRate > 22050 && !config.noDownsampleOnSmpLoad) { if (forceDownSampling == -1) { @@ -1322,7 +1322,7 @@ bool loadAIFFSample(UNICHAR *fileName, char *entryName, int8_t forceDownSampling return false; } - if (sampleRate > 22050) + if (sampleRate > 22050 && !config.noDownsampleOnSmpLoad) { if (forceDownSampling == -1) { diff --git a/src/pt2_visuals.c b/src/pt2_visuals.c index c085219..8566385 100644 --- a/src/pt2_visuals.c +++ b/src/pt2_visuals.c @@ -342,7 +342,7 @@ void showDownsampleAskDialog(void) renderBigAskDialog(); textOutTight(133, 49, "THE SAMPLE'S FREQUENCY IS", video.palette[PAL_BACKGRD]); - textOutTight(178, 57, "ABOVE 22KHZ.", video.palette[PAL_BACKGRD]); + textOutTight(154, 57, "HIGH (ABOVE 22KHZ).", video.palette[PAL_BACKGRD]); textOutTight(133, 65, "DO YOU WANT TO DOWNSAMPLE", video.palette[PAL_BACKGRD]); textOutTight(156, 73, "BEFORE LOADING IT?", video.palette[PAL_BACKGRD]); } @@ -2304,7 +2304,10 @@ void toggleFullScreen(void) SDL_SetWindowFullscreen(video.window, 0); SDL_RenderSetLogicalSize(video.renderer, SCREEN_W, SCREEN_H); SDL_SetWindowSize(video.window, SCREEN_W * config.videoScaleFactor, SCREEN_H * config.videoScaleFactor); - SDL_SetWindowPosition(video.window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); + + // this is not sensible on a multi-monitor setup + //SDL_SetWindowPosition(video.window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); + SDL_SetWindowGrab(video.window, SDL_FALSE); } diff --git a/vs2019_project/pt2-clone/protracker.ini b/vs2019_project/pt2-clone/protracker.ini index f19bf35..05efeaa 100644 --- a/vs2019_project/pt2-clone/protracker.ini +++ b/vs2019_project/pt2-clone/protracker.ini @@ -80,6 +80,12 @@ VSYNCOFF=FALSE HWMOUSE=TRUE [GENERAL SETTINGS] +; Don't show downsample dialog after loading a sample whose frequency is >22kHz. +; Syntax: TRUE or FALSE +; Default value: FALSE +; +NO_DWNSMP_ON_SMP_LOAD=FALSE + ; Hide last modification dates in Disk Op. to get longer dir/file names ; Syntax: TRUE or FALSE ; Default value: FALSE