ft2-clone

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

commit 8490b5e87db6bbc93bd070f42e25390ab6a79a37
parent 4e0757151e527a1716e4d2b7a3c2a235eca9361a
Author: Olav Sørensen <olav.sorensen@live.no>
Date:   Thu,  2 Mar 2023 18:39:41 +0100

BPM and audio->video sync precision improvement

Diffstat:
Msrc/ft2_audio.c | 64++++++++++++++++++++++++++++++++++------------------------------
Msrc/ft2_audio.h | 22+++++++++++++++++++---
Msrc/ft2_header.h | 2+-
Msrc/ft2_pattern_ed.c | 7++++---
Msrc/ft2_replayer.c | 26+++++++++++++++++---------
Msrc/ft2_replayer.h | 4+++-
Msrc/ft2_tables.c | 104+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Msrc/ft2_tables.h | 2+-
Msrc/ft2_wav_renderer.c | 32++++++++++++--------------------
Msrc/ft2_wav_renderer.h | 2--
10 files changed, 153 insertions(+), 112 deletions(-)

diff --git a/src/ft2_audio.c b/src/ft2_audio.c @@ -29,7 +29,8 @@ static int8_t pmpCountDiv, pmpChannels = 2; static uint16_t smpBuffSize; -static uint32_t oldAudioFreq, tickTimeLen, tickTimeLenFrac, randSeed = INITIAL_DITHER_SEED; +static uint32_t oldAudioFreq, tickTimeLenInt, randSeed = INITIAL_DITHER_SEED; +static uint64_t tickTimeLenFrac; static float fAudioNormalizeMul, fPanningTab[256+1]; static double dAudioNormalizeMul, dPrngStateL, dPrngStateR; static voice_t voice[MAX_CHANNELS * 2]; @@ -181,11 +182,11 @@ void setMixerBPM(int32_t bpm) int32_t i = bpm - MIN_BPM; - audio.samplesPerTick64 = audio.samplesPerTick64Tab[i]; // fixed-point - audio.samplesPerTick = (audio.samplesPerTick64 + (1LL << 31)) >> 32; // rounded + audio.samplesPerTickInt = audio.samplesPerTickIntTab[i]; + audio.samplesPerTickFrac = audio.samplesPerTickFracTab[i]; // for audio/video sync timestamp - tickTimeLen = audio.tickTimeTab[i]; + tickTimeLenInt = audio.tickTimeIntTab[i]; tickTimeLenFrac = audio.tickTimeFracTab[i]; // for calculating volume ramp length for tick-length ramps @@ -287,7 +288,7 @@ static void voiceUpdateVolumes(int32_t i, uint8_t status) { v->fVolumeLDelta = fVolumeLTarget * audio.fRampTickMul; v->fVolumeRDelta = fVolumeRTarget * audio.fRampTickMul; - v->volumeRampLength = audio.samplesPerTick; + v->volumeRampLength = audio.samplesPerTickInt; } } } @@ -598,7 +599,6 @@ static void doChannelMixing(int32_t bufferPosition, int32_t samplesToMix) // used for song-to-WAV renderer void mixReplayerTickToBuffer(uint32_t samplesToMix, uint8_t *stream, uint8_t bitDepth) { - assert(samplesToMix <= MAX_WAV_RENDER_SAMPLES_PER_TICK); doChannelMixing(0, samplesToMix); // normalize mix buffer and send to audio stream @@ -919,11 +919,12 @@ static void fillVisualsSyncBuffer(void) chSyncData.timestamp = audio.tickTime64; chQueuePush(chSyncData); - audio.tickTime64 += tickTimeLen; + audio.tickTime64 += tickTimeLenInt; + audio.tickTime64Frac += tickTimeLenFrac; - if (audio.tickTime64Frac > UINT32_MAX) + if (audio.tickTime64Frac >= TICK_TIME_FRAC_SCALE) { - audio.tickTime64Frac &= UINT32_MAX; + audio.tickTime64Frac &= TICK_TIME_FRAC_MASK; audio.tickTime64++; } } @@ -937,14 +938,12 @@ static void SDLCALL audioCallback(void *userdata, Uint8 *stream, int len) if (len <= 0) return; - assert(len <= MAX_WAV_RENDER_SAMPLES_PER_TICK); - int32_t bufferPosition = 0; - int32_t samplesLeft = len; + uint32_t samplesLeft = len; while (samplesLeft > 0) { - if (audio.tickSampleCounter64 <= 0) // new replayer tick + if (audio.tickSampleCounter == 0) // new replayer tick { replayerBusy = true; @@ -955,22 +954,27 @@ static void SDLCALL audioCallback(void *userdata, Uint8 *stream, int len) updateVoices(); fillVisualsSyncBuffer(); - audio.tickSampleCounter64 += audio.samplesPerTick64; + audio.tickSampleCounter = audio.samplesPerTickInt; + + audio.tickSampleCounterFrac += audio.samplesPerTickFrac; + if (audio.tickSampleCounterFrac >= BPM_FRAC_SCALE) + { + audio.tickSampleCounterFrac &= BPM_FRAC_MASK; + audio.tickSampleCounter++; + } replayerBusy = false; } - const int32_t remainingTick = (audio.tickSampleCounter64 + UINT32_MAX) >> 32; // ceil (rounded upwards) - - int32_t samplesToMix = samplesLeft; - if (samplesToMix > remainingTick) - samplesToMix = remainingTick; + uint32_t samplesToMix = samplesLeft; + if (samplesToMix > audio.tickSampleCounter) + samplesToMix = audio.tickSampleCounter; doChannelMixing(bufferPosition, samplesToMix); - bufferPosition += samplesToMix; + + audio.tickSampleCounter -= samplesToMix; samplesLeft -= samplesToMix; - audio.tickSampleCounter64 -= (int64_t)samplesToMix << 32; } // normalize mix buffer and send to audio stream @@ -981,8 +985,11 @@ static void SDLCALL audioCallback(void *userdata, Uint8 *stream, int len) static bool setupAudioBuffers(void) { - audio.fMixBufferL = (float *)calloc(MAX_WAV_RENDER_SAMPLES_PER_TICK, sizeof (float)); - audio.fMixBufferR = (float *)calloc(MAX_WAV_RENDER_SAMPLES_PER_TICK, sizeof (float)); + const int32_t maxAudioFreq = MAX(MAX_AUDIO_FREQ, MAX_WAV_RENDER_FREQ); + int32_t maxSamplesPerTick = (int32_t)ceil(maxAudioFreq / (MIN_BPM / 2.5)) + 1; + + audio.fMixBufferL = (float *)calloc(maxSamplesPerTick, sizeof (float)); + audio.fMixBufferR = (float *)calloc(maxSamplesPerTick, sizeof (float)); if (audio.fMixBufferL == NULL || audio.fMixBufferR == NULL) return false; @@ -1040,12 +1047,8 @@ static void calcAudioLatencyVars(int32_t audioBufferSize, int32_t audioFreq) double dFrac = modf(dAudioLatencySecs * editor.dPerfFreq, &dInt); - // integer part - audio.audLatencyPerfValInt = (int32_t)dInt; - - // fractional part (scaled to 0..2^32-1) - dFrac *= UINT32_MAX+1.0; - audio.audLatencyPerfValFrac = (uint32_t)dFrac; + audio.audLatencyPerfValInt = (uint32_t)dInt; + audio.audLatencyPerfValFrac = (uint64_t)((dFrac * TICK_TIME_FRAC_SCALE) + 0.5); audio.dAudioLatencyMs = dAudioLatencySecs * 1000.0; } @@ -1185,7 +1188,8 @@ bool setupAudio(bool showErrorMsg) stopAllScopes(); - audio.tickSampleCounter64 = 0; // zero tick sample counter so that it will instantly initiate a tick + audio.tickSampleCounter = 0; // zero tick sample counter so that it will instantly initiate a tick + audio.tickSampleCounterFrac = 0; calcReplayerVars(audio.freq); diff --git a/src/ft2_audio.h b/src/ft2_audio.h @@ -24,6 +24,16 @@ enum #define MAX_AUDIO_DEVICES 99 +// more bits makes little sense here + +#define BPM_FRAC_BITS 52 +#define BPM_FRAC_SCALE (1ULL << BPM_FRAC_BITS) +#define BPM_FRAC_MASK (BPM_FRAC_SCALE-1) + +#define TICK_TIME_FRAC_BITS 52 +#define TICK_TIME_FRAC_SCALE (1ULL << TICK_TIME_FRAC_BITS) +#define TICK_TIME_FRAC_MASK (TICK_TIME_FRAC_SCALE-1) + // for audio/video sync queue. (2^n-1 - don't change this! Queue buffer is already BIG in size) #define SYNC_QUEUE_LEN 4095 @@ -35,10 +45,16 @@ typedef struct audio_t bool linearPeriodsFlag, rescanAudioDevicesSupported; volatile uint8_t interpolationType; int32_t quickVolRampSamples, inputDeviceNum, outputDeviceNum, lastWorkingAudioFreq, lastWorkingAudioBits; - uint32_t tickTimeTab[(MAX_BPM-MIN_BPM)+1], tickTimeFracTab[(MAX_BPM-MIN_BPM)+1]; - int64_t tickSampleCounter64, samplesPerTick64, samplesPerTick64Tab[(MAX_BPM-MIN_BPM)+1]; - uint32_t freq, audLatencyPerfValInt, audLatencyPerfValFrac, samplesPerTick, samplesPerTickFrac, musicTimeSpeedVal; + uint32_t freq, musicTimeSpeedVal; + + uint32_t tickSampleCounter, samplesPerTickInt, samplesPerTickIntTab[(MAX_BPM-MIN_BPM)+1]; + uint64_t tickSampleCounterFrac, samplesPerTickFrac, samplesPerTickFracTab[(MAX_BPM-MIN_BPM)+1]; + + uint32_t audLatencyPerfValInt, tickTimeIntTab[(MAX_BPM-MIN_BPM)+1]; + uint64_t audLatencyPerfValFrac, tickTimeFracTab[(MAX_BPM-MIN_BPM)+1]; + uint64_t tickTime64, tickTime64Frac; + float fRampQuickVolMul, fRampTickMul, fRampTickMulTab[(MAX_BPM-MIN_BPM)+1]; float *fMixBufferL, *fMixBufferR; double dHz2MixDeltaMul, dAudioLatencyMs; 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.63" +#define PROG_VER_STR "1.64" // do NOT change these! It will only mess things up... diff --git a/src/ft2_pattern_ed.c b/src/ft2_pattern_ed.c @@ -2135,7 +2135,9 @@ void drawIDAdd(void) void resetPlaybackTime(void) { - song.musicTime64 = 0; + song.playbackSeconds = 0; + song.playbackSecondsFrac = 0; + last_TimeH = 0; last_TimeM = 0; last_TimeS = 0; @@ -2145,8 +2147,7 @@ void drawPlaybackTime(void) { if (songPlaying) { - const uint32_t ms1024 = song.musicTime64 >> 32; // milliseconds (scaled from 1000 to 1024) - uint32_t seconds = ms1024 >> 10; + uint32_t seconds = song.playbackSeconds; last_TimeH = seconds / 3600; seconds -= last_TimeH * 3600; diff --git a/src/ft2_replayer.c b/src/ft2_replayer.c @@ -424,18 +424,18 @@ void calcReplayerVars(int32_t audioFreq) const double dBpmHz = bpm / 2.5; const double dSamplesPerTick = audioFreq / dBpmHz; - // convert to 32.32 fixed-point - audio.samplesPerTick64Tab[i] = (int64_t)((dSamplesPerTick * (UINT32_MAX+1.0)) + 0.5); // rounded + double dSamplesPerTickInt; + double dSamplesPerTickFrac = modf(dSamplesPerTick, &dSamplesPerTickInt); + + audio.samplesPerTickIntTab[i] = (uint32_t)dSamplesPerTickInt; + audio.samplesPerTickFracTab[i] = (uint64_t)((dSamplesPerTickFrac * BPM_FRAC_SCALE) + 0.5); // rounded // BPM Hz -> tick length for performance counter (syncing visuals to audio) double dTimeInt; double dTimeFrac = modf(editor.dPerfFreq / dBpmHz, &dTimeInt); - const int32_t timeInt = (int32_t)dTimeInt; - - dTimeFrac = floor((UINT32_MAX+1.0) * dTimeFrac); // fractional part (scaled to 0..2^32-1) - audio.tickTimeTab[i] = (uint32_t)timeInt; - audio.tickTimeFracTab[i] = (uint32_t)dTimeFrac; + audio.tickTimeIntTab[i] = (uint32_t)dTimeInt; + audio.tickTimeFracTab[i] = (uint64_t)((dTimeFrac * TICK_TIME_FRAC_SCALE) + 0.5); // for calculating volume ramp length for tick-lenghted ramps const int32_t samplesPerTickRounded = (int32_t)(dSamplesPerTick + 0.5); // must be rounded @@ -2254,7 +2254,14 @@ void tickReplayer(void) // periodically called from audio callback // for song playback counter (hh:mm:ss) if (song.BPM >= MIN_BPM && song.BPM <= MAX_BPM) - song.musicTime64 += musicTimeTab64[song.BPM-MIN_BPM]; + { + song.playbackSecondsFrac += musicTimeTab52[song.BPM-MIN_BPM]; + if (song.playbackSecondsFrac >= 1ULL << 52) + { + song.playbackSecondsFrac &= (1ULL << 52)-1; + song.playbackSeconds++; + } + } bool tickZero = false; if (--song.tick == 0) @@ -2855,7 +2862,8 @@ void startPlaying(int8_t mode, int16_t row) if (song.speed == 0) song.speed = song.initialSpeed; - audio.tickSampleCounter64 = 0; // zero tick sample counter so that it will instantly initiate a tick + audio.tickSampleCounter = 0; // zero tick sample counter so that it will instantly initiate a tick + audio.tickSampleCounterFrac = 0; unlockMixerCallback(); diff --git a/src/ft2_replayer.h b/src/ft2_replayer.h @@ -274,7 +274,9 @@ typedef struct song_t int16_t songPos, pattNum, row, currNumRows; uint16_t songLength, songLoopStart, BPM, speed, initialSpeed, globalVolume, tick; int32_t numChannels; - uint64_t musicTime64; + + uint32_t playbackSeconds; + uint64_t playbackSecondsFrac; } song_t; double getSampleC4Rate(sample_t *s); diff --git a/src/ft2_tables.c b/src/ft2_tables.c @@ -872,50 +872,70 @@ const uint8_t defConfigData[CONFIG_FILE_SIZE] = /* ----------------------------------------------------------------------- */ /* +** For playback time counter. +** ** for (int32_t bpm = 32; bpm <= 255; bpm++) ** { -** const double dBpmMs1024 = 1024.0 / (bpm / 2.5); // milliseconds (scaled from 1000 to 1024) -** uint64_t x = (uint64_t)floor((UINT32_MAX+1.0) * dBpmMs1024); +** const double dBpmHz = bpm / 2.5; +** uint64_t musicTimeTab48[i] = (uint64_t)round((1ULL << 52) / dBpmHz); ** } */ -const uint64_t musicTimeTab64[(MAX_BPM-MIN_BPM)+1] = -{ - 0x5000000000,0x4D9364D936,0x4B4B4B4B4B,0x4924924924,0x471C71C71C,0x45306EB3E4, - 0x435E50D794,0x41A41A41A4,0x4000000000,0x3E7063E706,0x3CF3CF3CF3,0x3B88EE23B8, - 0x3A2E8BA2E8,0x38E38E38E3,0x37A6F4DE9B,0x3677D46CEF,0x3555555555,0x343EB1A1F5, - 0x3333333333,0x3232323232,0x313B13B13B,0x304D4873EC,0x2F684BDA12,0x2E8BA2E8BA, - 0x2DB6DB6DB6,0x2CE98B3A62,0x2C234F72C2,0x2B63CBEEA4,0x2AAAAAAAAA,0x29F79B4758, - 0x294A5294A5,0x28A28A28A2,0x2800000000,0x2762762762,0x26C9B26C9B,0x26357E16EC, - 0x25A5A5A5A5,0x2519F89467,0x2492492492,0x240E6C2B44,0x238E38E38E,0x231188C462, - 0x22983759F2,0x2222222222,0x21AF286BCA,0x213F2B3884,0x20D20D20D2,0x2067B23A54, - 0x2000000000,0x1F9ADD3C0C,0x1F3831F383,0x1ED7E75346,0x1E79E79E79,0x1E1E1E1E1E, - 0x1DC47711DC,0x1D6CDFA1D6,0x1D1745D174,0x1CC398730E,0x1C71C71C71,0x1C21C21C21, - 0x1BD37A6F4D,0x1B86E1B86E,0x1B3BEA3677,0x1AF286BCA1,0x1AAAAAAAAA,0x1A6449E59B, - 0x1A1F58D0FA,0x19DBCC4867,0x1999999999,0x1958B67EBB,0x1919191919,0x18DAB7EC1D, - 0x189D89D89D,0x1861861861,0x1826A439F6,0x17ECDC1CB5,0x17B425ED09,0x177C7A20E1, - 0x1745D1745D,0x171024E6A1,0x16DB6DB6DB,0x16A7A5616A,0x1674C59D31,0x1642C8590B, - 0x1611A7B961,0x15E15E15E1,0x15B1E5F752,0x15833A1583,0x1555555555,0x152832C6E0, - 0x14FBCDA3AC,0x14D0214D02,0x14A5294A52,0x147AE147AE,0x1451451451,0x142850A142, - 0x1400000000,0x13D84F613D,0x13B13B13B1,0x138ABF82EE,0x1364D9364D,0x133F84CFE1, - 0x131ABF0B76,0x12F684BDA1,0x12D2D2D2D2,0x12AFA64E7B,0x128CFC4A33,0x126AD1F4F3, - 0x1249249249,0x1227F179A5,0x12073615A2,0x11E6EFE35B,0x11C71C71C7,0x11A7B9611A, - 0x1188C46231,0x116A3B35FC,0x114C1BACF9,0x112E63A6A8,0x1111111111,0x10F421E843, - 0x10D79435E5,0x10BB6610BB,0x109F959C42,0x1084210842,0x1069069069,0x104E447BEC, - 0x1033D91D2A,0x1019C2D14E,0x1000000000,0x0FE68F1B07,0x0FCD6E9E06,0x0FB49D0E22, - 0x0F9C18F9C1,0x0F83E0F83E,0x0F6BF3A9A3,0x0F544FB66B,0x0F3CF3CF3C,0x0F25DEACAF, - 0x0F0F0F0F0F,0x0EF883BE20,0x0EE23B88EE,0x0ECC35458C,0x0EB66FD0EB,0x0EA0EA0EA0, - 0x0E8BA2E8BA,0x0E76994F8C,0x0E61CC3987,0x0E4D3AA30A,0x0E38E38E38,0x0E24C602D4, - 0x0E10E10E10,0x0DFD33C272,0x0DE9BD37A6,0x0DD67C8A60,0x0DC370DC37,0x0DB0995382, - 0x0D9DF51B3B,0x0D8B8362E0,0x0D79435E50,0x0D673445B2,0x0D55555555,0x0D43A5CD98, - 0x0D3224F2CD,0x0D20D20D20,0x0D0FAC687D,0x0CFEB35477,0x0CEDE62433,0x0CDD442E4F, - 0x0CCCCCCCCC,0x0CBC7F5CF9,0x0CAC5B3F5D,0x0C9C5FD7A5,0x0C8C8C8C8C,0x0C7CE0C7CE, - 0x0C6D5BF60E,0x0C5DFD86CD,0x0C4EC4EC4E,0x0C3FB19B8F,0x0C30C30C30,0x0C21F8B86A, - 0x0C13521CFB,0x0C04CEB916,0x0BF66E0E5A,0x0BE82FA0BE,0x0BDA12F684,0x0BCC17982F, - 0x0BBE3D1070,0x0BB082EC20,0x0BA2E8BA2E,0x0B956E0B95,0x0B88127350,0x0B7AD58650, - 0x0B6DB6DB6D,0x0B60B60B60,0x0B53D2B0B5,0x0B470C67C0,0x0B3A62CE98,0x0B2DD58507, - 0x0B21642C85,0x0B150E682C,0x0B08D3DCB0,0x0AFCB43057,0x0AF0AF0AF0,0x0AE4C415C9, - 0x0AD8F2FBA9,0x0ACD3B68C6,0x0AC19D0AC1,0x0AB617909A,0x0AAAAAAAAA,0x0A9F560A9F, - 0x0A94196370,0x0A88F46959,0x0A7DE6D1D6,0x0A72F05397,0x0A6810A681,0x0A5D4783A0, - 0x0A5294A529,0x0A47F7C66C,0x0A3D70A3D7,0x0A32FEFAE6,0x0A28A28A28,0x0A1E5B1133, - 0x0A142850A1,0x0A0A0A0A0A +const uint64_t musicTimeTab52[(MAX_BPM-MIN_BPM)+1] = +{ + 0x1400000000000,0x1364D9364D936,0x12D2D2D2D2D2D,0x1249249249249, + 0x11C71C71C71C7,0x114C1BACF914C,0x10D79435E50D8,0x1069069069069, + 0x1000000000000,0x0F9C18F9C18FA,0x0F3CF3CF3CF3D,0x0EE23B88EE23C, + 0x0E8BA2E8BA2E9,0x0E38E38E38E39,0x0DE9BD37A6F4E,0x0D9DF51B3BEA3, + 0x0D55555555555,0x0D0FAC687D634,0x0CCCCCCCCCCCD,0x0C8C8C8C8C8C9, + 0x0C4EC4EC4EC4F,0x0C13521CFB2B8,0x0BDA12F684BDA,0x0BA2E8BA2E8BA, + 0x0B6DB6DB6DB6E,0x0B3A62CE98B3A,0x0B08D3DCB08D4,0x0AD8F2FBA9387, + 0x0AAAAAAAAAAAB,0x0A7DE6D1D6086,0x0A5294A5294A5,0x0A28A28A28A29, + 0x0A00000000000,0x09D89D89D89D9,0x09B26C9B26C9B,0x098D5F85BB395, + 0x0969696969697,0x09467E2519F89,0x0924924924925,0x09039B0AD1207, + 0x08E38E38E38E4,0x08C46231188C4,0x08A60DD67C8A6,0x0888888888889, + 0x086BCA1AF286C,0x084FCACE213F3,0x0834834834835,0x0819EC8E95103, + 0x0800000000000,0x07E6B74F03291,0x07CE0C7CE0C7D,0x07B5F9D4D1BC2, + 0x079E79E79E79E,0x0787878787878,0x07711DC47711E,0x075B37E875B38, + 0x0745D1745D174,0x0730E61CC3987,0x071C71C71C71C,0x0708708708708, + 0x06F4DE9BD37A7,0x06E1B86E1B86E,0x06CEFA8D9DF52,0x06BCA1AF286BD, + 0x06AAAAAAAAAAB,0x0699127966ED8,0x0687D6343EB1A,0x0676F31219DBD, + 0x0666666666666,0x06562D9FAEE42,0x0646464646464,0x0636ADFB0774D, + 0x0627627627627,0x0618618618618,0x0609A90E7D95C,0x05FB37072D754, + 0x05ED097B425ED,0x05DF1E88385DF,0x05D1745D1745D,0x05C40939A85C4, + 0x05B6DB6DB6DB7,0x05A9E9585A9E9,0x059D31674C59D,0x0590B21642C86, + 0x058469EE5846A,0x0578578578578,0x056C797DD49C3,0x0560CE8560CE8, + 0x0555555555555,0x054A0CB1B810F,0x053EF368EB043,0x0534085340853, + 0x05294A5294A53,0x051EB851EB852,0x0514514514514,0x050A142850A14, + 0x0500000000000,0x04F613D84F614,0x04EC4EC4EC4EC,0x04E2AFE0BB9A6, + 0x04D9364D9364E,0x04CFE133F84D0,0x04C6AFC2DD9CB,0x04BDA12F684BE, + 0x04B4B4B4B4B4B,0x04ABE9939ED50,0x04A33F128CFC5,0x049AB47D3CC6F, + 0x0492492492492,0x0489FC5E694E1,0x0481CD8568904,0x0479BBF8D6D34, + 0x0471C71C71C72,0x0469EE58469EE,0x046231188C462,0x045A8ECD7F211, + 0x045306EB3E453,0x044B98E9AA181,0x0444444444444,0x043D087A10F42, + 0x0435E50D79436,0x042ED9842ED98,0x0427E567109F9,0x0421084210842, + 0x041A41A41A41A,0x0413911EFB1BC,0x040CF6474A882,0x040670B453B93, + 0x0400000000000,0x03F9A3C6C1FCD,0x03F35BA781949,0x03ED274388A35, + 0x03E7063E7063E,0x03E0F83E0F83E,0x03DAFCEA68DE1,0x03D513ED9AD39, + 0x03CF3CF3CF3CF,0x03C977AB2BEDD,0x03C3C3C3C3C3C,0x03BE20EF883BE, + 0x03B88EE23B88F,0x03B30D5163250,0x03AD9BF43AD9C,0x03A83A83A83A8, + 0x03A2E8BA2E8BA,0x039DA653E312D,0x0398730E61CC4,0x03934EA8C280B, + 0x038E38E38E38E,0x03893180B509E,0x0384384384384,0x037F4CF09CAD7, + 0x037A6F4DE9BD3,0x03759F2298376,0x0370DC370DC37,0x036C2654E0837, + 0x03677D46CEFA9,0x0362E0D8B8363,0x035E50D79435E,0x0359CD116C90C, + 0x0355555555555,0x0350E97366228,0x034C893CB376C,0x0348348348348, + 0x0343EB1A1F58D,0x033FACD51DE37,0x033B79890CEDE,0x0337510B93F09, + 0x0333333333333,0x032F1FD73E687,0x032B16CFD7721,0x032717F5E94CF, + 0x0323232323232,0x031F3831F3832,0x031B56FD83BA7,0x03177F61B352E, + 0x0313B13B13B14,0x030FEC66E3D3E,0x030C30C30C30C,0x03087E2E1AB12, + 0x0304D4873ECAE,0x030133AE45B58,0x02FD9B8396BAA,0x02FA0BE82FA0C, + 0x02F684BDA12F7,0x02F305E60BCC1,0x02EF8F441C2F0,0x02EC20BB082EC, + 0x02E8BA2E8BA2F,0x02E55B82E55B8,0x02E2049CD42E2,0x02DEB5619416F, + 0x02DB6DB6DB6DB,0x02D82D82D82D8,0x02D4F4AC2D4F5,0x02D1C319F0362, + 0x02CE98B3A62CF,0x02CB756141F4D,0x02C8590B21643,0x02C5439A0B151, + 0x02C234F72C235,0x02BF2D0C15F97,0x02BC2BC2BC2BC,0x02B9310572621, + 0x02B63CBEEA4E2,0x02B34EDA31B01,0x02B06742B0674,0x02AD85E4268F9, + 0x02AAAAAAAAAAB,0x02A7D582A7D58,0x02A50658DC087,0x02A23D1A56630, + 0x029F79B475822,0x029CBC14E5E0A,0x029A0429A042A,0x029751E0E8297, + 0x0294A5294A529,0x0291FDF19B3EB,0x028F5C28F5C29,0x028CBFBEB9A02, + 0x028A28A28A28A,0x028796C44CE6B,0x02850A142850A,0x0282828282828 }; diff --git a/src/ft2_tables.h b/src/ft2_tables.h @@ -49,4 +49,4 @@ extern const uint16_t scopeLenTab[16][32]; extern const uint8_t defConfigData[CONFIG_FILE_SIZE]; -extern const uint64_t musicTimeTab64[(MAX_BPM-MIN_BPM)+1]; +extern const uint64_t musicTimeTab52[(MAX_BPM-MIN_BPM)+1]; diff --git a/src/ft2_wav_renderer.c b/src/ft2_wav_renderer.c @@ -170,11 +170,11 @@ void exitWavRenderer(void) static bool dump_Init(uint32_t frq, int16_t amp, int16_t songPos) { - const int32_t maxSamplesPerTick = (const int32_t)ceil((frq * 2.5) / MIN_BPM); // absolute max samples per tick - uint32_t sampleSize = (WDBitDepth / 8) * 2; // 2 channels + int32_t bytesPerSample = (WDBitDepth / 8) * 2; // 2 channels + int32_t maxSamplesPerTick = (int32_t)ceil(frq / (MIN_BPM / 2.5)) + 1; // *2 for stereo - wavRenderBuffer = (uint8_t *)malloc((TICKS_PER_RENDER_CHUNK * maxSamplesPerTick) * sampleSize); + wavRenderBuffer = (uint8_t *)malloc((TICKS_PER_RENDER_CHUNK * maxSamplesPerTick) * bytesPerSample); if (wavRenderBuffer == NULL) return false; @@ -261,7 +261,7 @@ static bool dump_EndOfTune(int16_t endSongPos) { bool returnValue = (editor.wavReachedEndFlag && song.row == 0 && song.tick == 1) || (song.speed == 0); - // 8bitbubsy: FT2 bugfix for EEx (pattern delay) on first row of a pattern + // FT2 bugfix for EEx (pattern delay) on first row of a pattern if (song.pattDelTime2 > 0) returnValue = false; @@ -321,7 +321,7 @@ static int32_t SDLCALL renderWavThread(void *ptr) uint32_t sampleCounter = 0; bool overflow = false, renderDone = false; uint8_t tickCounter = UPDATE_VISUALS_AT_TICK; - int64_t tickSampleCounter64 = 0; + uint64_t tickSamplesFrac = 0; uint64_t bytesInFile = sizeof (wavHeader_t); @@ -340,29 +340,21 @@ static int32_t SDLCALL renderWavThread(void *ptr) break; } - int32_t tickSamples; + dump_TickReplayer(); + uint32_t tickSamples = audio.samplesPerTickInt; - if (useLegacyBPM) - { - dump_TickReplayer(); - tickSamples = audio.samplesPerTick64 >> 32; // truncate - } - else + if (!useLegacyBPM) { - if (tickSampleCounter64 <= 0) // new replayer tick + tickSamplesFrac += audio.samplesPerTickFrac; + if (tickSamplesFrac >= BPM_FRAC_SCALE) { - dump_TickReplayer(); - tickSampleCounter64 += audio.samplesPerTick64; + tickSamplesFrac &= BPM_FRAC_MASK; + tickSamples++; } - - tickSamples = (tickSampleCounter64 + UINT32_MAX) >> 32; // ceil (rounded upwards) } mixReplayerTickToBuffer(tickSamples, ptr8, WDBitDepth); - if (!useLegacyBPM) - tickSampleCounter64 -= (int64_t)tickSamples << 32; - tickSamples *= 2; // stereo samplesInChunk += tickSamples; sampleCounter += tickSamples; diff --git a/src/ft2_wav_renderer.h b/src/ft2_wav_renderer.h @@ -12,8 +12,6 @@ #define MAX_WAV_RENDER_FREQ 48000 #endif -#define MAX_WAV_RENDER_SAMPLES_PER_TICK (((MAX_WAV_RENDER_FREQ * 5) / 2) / MIN_BPM) - void cbToggleWavRenderBPMMode(void); void updateWavRendererSettings(void); void drawWavRenderer(void);