Skip to content

Commit

Permalink
Pushed v1.24 code
Browse files Browse the repository at this point in the history
- BPM is now slightly more accurate to CIA specifications, and also more accurate in vblank (VBL) timing mode using the true Amiga PAL vblank rate. NOTE: We're speaking tiny variations in BPM here...
- Vblank (VBL) timing mode: Fixed a bug with setting a speed of FF (255) on the first row. The tick duration would be about twice as long...
- Small code cleanup
  • Loading branch information
8bitbubsy committed Oct 4, 2020
1 parent 7e106b7 commit 585a044
Show file tree
Hide file tree
Showing 10 changed files with 124 additions and 73 deletions.
97 changes: 68 additions & 29 deletions src/pt2_audio.c
Expand Up @@ -116,19 +116,6 @@ void setSyncTickTimeLen(uint32_t timeLen, uint32_t timeLenFrac)
tickTimeLenFrac = timeLenFrac;
}

static void generateBpmTables(void)
{
for (int32_t i = 32; i <= 255; i++)
{
const double dBpmHz = i / 2.5;

audio.bpmTab[i-32] = audio.outputRate / dBpmHz;
audio.bpmTab28kHz[i-32] = PAT2SMP_HI_FREQ / dBpmHz; // PAT2SMP hi quality
audio.bpmTab22kHz[i-32] = PAT2SMP_LO_FREQ / dBpmHz; // PAT2SMP low quality
audio.bpmTabMod2Wav[i-32] = MOD2WAV_FREQ / dBpmHz; // MOD2WAV
}
}

void lockAudio(void)
{
if (dev != 0)
Expand Down Expand Up @@ -788,6 +775,69 @@ void mixerCalcVoicePans(uint8_t stereoSeparation) // 0..100 (percentage)
setVoicePan(3, panL);
}

static double ciaBpm2Hz(int32_t bpm)
{
if (bpm == 0)
return 0.0;

const uint32_t ciaPeriod = 1773447 / bpm; // yes, PT truncates here
return (double)CIA_PAL_CLK / ciaPeriod;
}

static void generateBpmTables(bool vblankTimingFlag)
{
for (int32_t bpm = 32; bpm <= 255; bpm++)
{
double dHz;

if (vblankTimingFlag)
dHz = AMIGA_PAL_VBLANK_HZ;
else
dHz = ciaBpm2Hz(bpm);

audio.bpmTable[bpm-32] = audio.outputRate / dHz;
audio.bpmTable28kHz[bpm-32] = PAT2SMP_HI_FREQ / dHz; // PAT2SMP hi quality
audio.bpmTable22kHz[bpm-32] = PAT2SMP_LO_FREQ / dHz; // PAT2SMP low quality
audio.bpmTableMod2Wav[bpm-32] = MOD2WAV_FREQ / dHz; // MOD2WAV
}
}

static void generateTickLengthTable(bool vblankTimingFlag)
{
for (int32_t bpm = 32; bpm <= 255; bpm++)
{
double dHz;

if (vblankTimingFlag)
dHz = AMIGA_PAL_VBLANK_HZ;
else
dHz = ciaBpm2Hz(bpm);

// BPM -> Hz -> tick length for performance counter (syncing visuals to audio)
double dTimeInt;
double dTimeFrac = modf(editor.dPerfFreq / dHz, &dTimeInt);
const int32_t timeInt = (int32_t)dTimeInt;

dTimeFrac = floor((UINT32_MAX+1.0) * dTimeFrac); // fractional part (scaled to 0..2^32-1)

audio.tickLengthTable[bpm-32] = ((uint64_t)timeInt << 32) | (uint32_t)dTimeFrac;
}
}

void updateReplayerTimingMode(void)
{
const bool audioWasntLocked = !audio.locked;
if (audioWasntLocked)
lockAudio();

const bool vblankTimingMode = (editor.timingMode == TEMPO_MODE_VBLANK);
generateBpmTables(vblankTimingMode);
generateTickLengthTable(vblankTimingMode);

if (audioWasntLocked)
unlockAudio();
}

bool setupAudio(void)
{
SDL_AudioSpec want, have;
Expand Down Expand Up @@ -822,12 +872,12 @@ bool setupAudio(void)
audio.audioBufferSize = have.samples;
audio.dPeriodToDeltaDiv = (double)PAULA_PAL_CLK / audio.outputRate;

generateBpmTables();
updateReplayerTimingMode();

const int32_t lowestBPM = 32;
const int32_t pat2SmpMaxSamples = (int32_t)ceil(audio.bpmTab22kHz[lowestBPM-32]);
const int32_t mod2WavMaxSamples = (int32_t)ceil(audio.bpmTabMod2Wav[lowestBPM-32]);
const int32_t renderMaxSamples = (int32_t)ceil(audio.bpmTab[lowestBPM-32]);
const int32_t pat2SmpMaxSamples = (int32_t)ceil(audio.bpmTable22kHz[lowestBPM-32]);
const int32_t mod2WavMaxSamples = (int32_t)ceil(audio.bpmTableMod2Wav[lowestBPM-32]);
const int32_t renderMaxSamples = (int32_t)ceil(audio.bpmTable[lowestBPM-32]);

const int32_t maxSamplesToMix = MAX(pat2SmpMaxSamples, MAX(mod2WavMaxSamples, renderMaxSamples));

Expand All @@ -850,24 +900,13 @@ bool setupAudio(void)
ledFilterEnabled = false;
calculateFilterCoeffs();

audio.dSamplesPerTick = audio.bpmTab[125-32]; // BPM 125
audio.dSamplesPerTick = audio.bpmTable[125-32]; // BPM 125
audio.dTickSampleCounter = 0.0;

calcAudioLatencyVars(audio.audioBufferSize, audio.outputRate);

for (int32_t i = 32; i <= 255; i++)
{
const double dBpmHz = i / 2.5;

// 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 *= UINT32_MAX+1.0; // fractional part (scaled to 0..2^32-1)

audio.tickTimeLengthTab[i-32] = ((uint64_t)timeInt << 32) | (uint32_t)dTimeFrac;
}

audio.resetSyncTickTimeFlag = true;
SDL_PauseAudioDevice(dev, false);
Expand Down
6 changes: 4 additions & 2 deletions src/pt2_audio.h
Expand Up @@ -9,7 +9,7 @@ typedef struct audio_t
volatile bool locked, isSampling;

bool forceMixerOff;
double bpmTab[256-32], bpmTab28kHz[256-32], bpmTab22kHz[256-32], bpmTabMod2Wav[256-32];
double bpmTable[256-32], bpmTable28kHz[256-32], bpmTable22kHz[256-32], bpmTableMod2Wav[256-32];
uint32_t outputRate, audioBufferSize;
double dPeriodToDeltaDiv;

Expand All @@ -20,7 +20,7 @@ typedef struct audio_t

// for audio/video syncing
bool resetSyncTickTimeFlag;
uint64_t tickTimeLengthTab[224];
uint64_t tickLengthTable[224];
} audio_t;

typedef struct voice_t
Expand All @@ -39,6 +39,8 @@ typedef struct voice_t
const int8_t *syncTriggerData;
} paulaVoice_t;

void updateReplayerTimingMode(void);

void setSyncTickTimeLen(uint32_t timeLen, uint32_t timeLenFrac);
void resetCachedMixerPeriod(void);
void resetAudioDithering(void);
Expand Down
6 changes: 3 additions & 3 deletions src/pt2_header.h
Expand Up @@ -14,7 +14,7 @@
#include "pt2_unicode.h"
#include "pt2_palette.h"

#define PROG_VER_STR "1.23"
#define PROG_VER_STR "1.24"

#ifdef _WIN32
#define DIR_DELIMITER '\\'
Expand Down Expand Up @@ -225,8 +225,8 @@ void modStop(void);
void doStopIt(bool resetPlayMode);
void playPattern(int8_t startRow);
void modPlay(int16_t patt, int16_t order, int8_t row);
void modSetSpeed(uint8_t speed);
void modSetTempo(uint16_t bpm, bool doLockAudio);
void modSetSpeed(int32_t speed);
void modSetTempo(int32_t bpm, bool doLockAudio);
void modFree(void);
bool setupAudio(void);
void audioClose(void);
Expand Down
18 changes: 12 additions & 6 deletions src/pt2_keyboard.c
Expand Up @@ -699,17 +699,26 @@ void keyDownHandler(SDL_Scancode scancode, SDL_Keycode keycode)
{
if (keyb.leftCtrlPressed)
{
const bool audioWasntLocked = !audio.locked;
if (audioWasntLocked)
lockAudio();

editor.timingMode ^= 1;
updateReplayerTimingMode();

if (editor.timingMode == TEMPO_MODE_VBLANK)
{
editor.oldTempo = song->currBPM;
modSetTempo(125, true);
modSetTempo(125, false);
}
else
{
modSetTempo(editor.oldTempo, true);
modSetTempo(editor.oldTempo, false);
}

if (audioWasntLocked)
unlockAudio();

ui.updateSongTiming = true;
}
else if (keyb.shiftPressed)
Expand Down Expand Up @@ -3452,10 +3461,7 @@ void handleKeyRepeat(SDL_Scancode scancode)
break;
}

// repeat keys at 49.92Hz (Amiga PAL) rate
const uint64_t keyRepeatDelta = (uint64_t)(((UINT32_MAX+1.0) * (AMIGA_PAL_VBLANK_HZ / (double)VBLANK_HZ)) + 0.5);

keyb.repeatFrac += keyRepeatDelta; // 32.32 fixed-point counter
keyb.repeatFrac += keyb.repeatDelta; // 32.32 fixed-point counter
if (keyb.repeatFrac > 0xFFFFFFFF)
{
keyb.repeatFrac &= 0xFFFFFFFF;
Expand Down
4 changes: 4 additions & 0 deletions src/pt2_main.c
Expand Up @@ -471,6 +471,10 @@ static bool initializeVars(void)

editor.repeatKeyFlag = (SDL_GetModState() & KMOD_CAPS) ? true : false;

// set key repeat rate to 49.9204Hz (Amiga PAL vblank rate)
const double dVblankHzRatio = AMIGA_PAL_VBLANK_HZ / (double)VBLANK_HZ;
keyb.repeatDelta = (uint64_t)floor((UINT32_MAX+1.0) * dVblankHzRatio);

strcpy(editor.mixText, "MIX 01+02 TO 03");

// allocate some memory
Expand Down
3 changes: 2 additions & 1 deletion src/pt2_mod2wav.c
Expand Up @@ -157,7 +157,8 @@ bool renderToWav(char *fileName, bool checkIfFileExist)
return false;
}

const int32_t maxSamplesToMix = (int32_t)ceil(TICKS_PER_RENDER_CHUNK * audio.bpmTabMod2Wav[32-32]); // BPM 32, stereo
const int32_t lowestBPM = 32;
const int32_t maxSamplesToMix = (int32_t)ceil(TICKS_PER_RENDER_CHUNK * audio.bpmTableMod2Wav[lowestBPM-32]); // stereo

mod2WavBuffer = (int16_t *)malloc(maxSamplesToMix * (2 * sizeof (int16_t)));
if (mod2WavBuffer == NULL)
Expand Down
1 change: 1 addition & 0 deletions src/pt2_module_loader.c
Expand Up @@ -946,6 +946,7 @@ void setupLoadedMod(void)
updateWindowTitle(MOD_NOT_MODIFIED);

editor.timingMode = TEMPO_MODE_CIA;
updateReplayerTimingMode();

modSetSpeed(6);
modSetTempo(song->header.initialTempo, false); // 125 for normal MODs, custom value for certain STK/UST MODs
Expand Down
4 changes: 2 additions & 2 deletions src/pt2_mouse.c
Expand Up @@ -1048,7 +1048,7 @@ void sampleRepeatLengthDownButton(bool fast)

void tempoUpButton(void)
{
int16_t val;
int32_t val;

if (editor.timingMode == TEMPO_MODE_VBLANK)
return;
Expand All @@ -1069,7 +1069,7 @@ void tempoUpButton(void)

void tempoDownButton(void)
{
int16_t val;
int32_t val;

if (editor.timingMode == TEMPO_MODE_VBLANK)
return;
Expand Down

0 comments on commit 585a044

Please sign in to comment.