// for finding memory leaks in debug mode with Visual Studio#if defined _DEBUG && defined _MSC_VER#include <crtdbg.h>#endif #include <stdint.h>#include <stdio.h>#include <math.h>#include "ft2_header.h"#include "ft2_config.h"#include "ft2_gui.h"#include "ft2_video.h"#include "ft2_pattern_ed.h"#include "ft2_sample_ed.h"#include "ft2_inst_ed.h"#include "ft2_diskop.h"#include "ft2_midi.h"#include "scopes/ft2_scopes.h"#include "ft2_mouse.h"#include "ft2_sample_loader.h"#include "ft2_tables.h"#include "ft2_structs.h"#include "mixer/ft2_windowed_sinc.h" static double dLogTab[4*12*16], dExp2MulTab[32];static bool bxxOverflow;static note_t nilPatternLine[MAX_CHANNELS]; typedef void (*volColumnEfxRoutine)(channel_t *ch);typedef void (*volColumnEfxRoutine2)(channel_t *ch, uint8_t *volColumnData);typedef void (*efxRoutine)(channel_t *ch, uint8_t param); // globally accessedint8_t playMode = 0;bool songPlaying = false, audioPaused = false, musicPaused = false;volatile bool replayerBusy = false;const uint16_t *note2Period = NULL;int16_t patternNumRows[MAX_PATTERNS];channel_t channel[MAX_CHANNELS];song_t song;instr_t *instr[128+4];note_t *pattern[MAX_PATTERNS];// ---------------------------------- void fixString(char *str, int32_t lastChrPos) // removes leading spaces and 0x1A chars{ for (int32_t i = lastChrPos; i >= 0; i--) { if (str[i] == ' ' || str[i] == 0x1A) str[i] = '\0'; else if (str[i] != '\0') break; } str[lastChrPos+1] = '\0';} void fixSongName(void){ fixString(song.name, 19);} void fixInstrAndSampleNames(int16_t insNum){ fixString(song.instrName[insNum], 21); if (instr[insNum] != NULL) { sample_t *s = instr[insNum]->smp; for (int32_t i = 0; i < MAX_SMP_PER_INST; i++, s++) fixString(s->name, 21); }} void resetChannels(void){ const bool audioWasntLocked = !audio.locked; if (audioWasntLocked) lockAudio(); memset(channel, 0, sizeof (channel)); channel_t *ch = channel; for (int32_t i = 0; i < MAX_CHANNELS; i++, ch++) { ch->instrPtr = instr[0]; ch->status = IS_Vol; ch->oldPan = 128; ch->outPan = 128; ch->finalPan = 128; ch->channelOff = !editor.chnMode[i]; // set channel mute flag from global mute flag } if (audioWasntLocked) unlockAudio();} void setSongModifiedFlag(void){ song.isModified = true; editor.updateWindowTitle = true;} void removeSongModifiedFlag(void){ song.isModified = false; 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){ #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); if (errorCurr <= errorNext) break; // current is the closest period++; break; // current+1 is the closest } } s->finetune = ((period & 31) - 16) << 3; s->relativeNote = (int8_t)(((period & ~31) >> 4) - NOTE_C4);} void setPatternLen(uint16_t pattNum, int16_t numRows){ assert(pattNum < MAX_PATTERNS); if ((numRows < 1 || numRows > MAX_PATT_LEN) || numRows == patternNumRows[pattNum]) return; const bool audioWasntLocked = !audio.locked; if (audioWasntLocked) lockAudio(); patternNumRows[pattNum] = numRows; if (pattern[pattNum] != NULL) killPatternIfUnused(pattNum); // non-FT2 security song.pattDelTime = 0; song.pattDelTime2 = 0; song.pBreakFlag = false; song.posJumpFlag = false; song.pBreakPos = 0; song.currNumRows = numRows; if (song.row >= song.currNumRows) { song.row = song.currNumRows - 1; editor.row = song.row; } checkMarkLimits(); if (audioWasntLocked) unlockAudio(); ui.updatePatternEditor = true; ui.updatePosSections = true;} int16_t getUsedSamples(int16_t smpNum){ if (instr[smpNum] == NULL) return 0; instr_t *ins = instr[smpNum]; int16_t i = 16 - 1; while (i >= 0 && ins->smp[i].dataPtr == NULL && ins->smp[i].name[0] == '\0') i--; /* Yes, 'i' can be -1 here, and will be set to at least 0 ** because of ins->ta values. Possibly an FT2 bug... */ for (int16_t j = 0; j < 96; j++) { if (ins->note2SampleLUT[j] > i) i = ins->note2SampleLUT[j]; } return i+1;} int16_t getRealUsedSamples(int16_t smpNum){ if (instr[smpNum] == NULL) return 0; int8_t i = 16 - 1; while (i >= 0 && instr[smpNum]->smp[i].dataPtr == NULL) i--; return i+1;} double dLinearPeriod2Hz(int32_t period){ period &= 0xFFFF; // just in case (actual period range is 0..65535) if (period == 0) return 0.0; // in FT2, a period of 0 results in 0Hz const uint32_t invPeriod = ((12 * 192 * 4) - period) & 0xFFFF; // mask needed for FT2 period overflow quirk const uint32_t quotient = invPeriod / (12 * 16 * 4); const uint32_t remainder = invPeriod % (12 * 16 * 4); return dLogTab[remainder] * dExp2MulTab[(14-quotient) & 31]; // x = y >> ((14-quotient) & 31);} double dAmigaPeriod2Hz(int32_t period){ period &= 0xFFFF; // just in case (actual period range is 0..65535) if (period == 0) return 0.0; // in FT2, a period of 0 results in 0Hz return (double)(8363 * 1712) / period;} double dPeriod2Hz(int32_t period){ return audio.linearPeriodsFlag ? dLinearPeriod2Hz(period) : dAmigaPeriod2Hz(period);} // returns *exact* FT2 C-4 voice rate (depending on finetune, relativeNote and linear/Amiga period mode)double getSampleC4Rate(sample_t *s){ int32_t note = NOTE_C4 + s->relativeNote; if (note < 0) return -1; // shouldn't happen (just in case...) if (note >= (10*12)-1) return -1; // B-9 (after relativeTone add) = illegal! (won't play in replayer) const int32_t C4Period = (note << 4) + (((int8_t)s->finetune >> 3) + 16); const int32_t period = audio.linearPeriodsFlag ? linearPeriods[C4Period] : amigaPeriods[C4Period]; return dPeriod2Hz(period);} void setLinearPeriods(bool linearPeriodsFlag){ pauseAudio(); audio.linearPeriodsFlag = linearPeriodsFlag; if (audio.linearPeriodsFlag) note2Period = linearPeriods; else note2Period = amigaPeriods; resumeAudio(); if (ui.configScreenShown && editor.currConfigScreen == CONFIG_SCREEN_AUDIO) { // update "frequency slides" radiobutton, if it's shown setConfigAudioRadioButtonStates(); } // update mid-C freq. in instr. editor (it can slightly differ between Amiga/linear) if (ui.instEditorShown) drawC4Rate();} static void retrigVolume(channel_t *ch){ ch->realVol = ch->oldVol; ch->outVol = ch->oldVol; ch->outPan = ch->oldPan; ch->status |= IS_Vol + IS_Pan + IS_QuickVol;} static void retrigEnvelopeVibrato(channel_t *ch){ if (!(ch->waveCtrl & 0x04)) ch->vibPos = 0; if (!(ch->waveCtrl & 0x40)) ch->tremPos = 0; ch->retrigCnt = 0; ch->tremorPos = 0; instr_t *ins = ch->instrPtr; assert(ins != NULL); if (ins->volEnvFlags & ENV_ENABLED) { ch->volEnvTick = 65535; ch->volEnvPos = 0; } if (ins->panEnvFlags & ENV_ENABLED) { ch->panEnvTick = 65535; ch->panEnvPos = 0; } ch->keyOff = false; ch->fadeoutSpeed = ins->fadeout; // FT2 doesn't check if fadeout is more than 4095! ch->fadeoutVol = 32768; if (ins->autoVibDepth > 0) { ch->autoVibPos = 0; if (ins->autoVibSweep > 0) { ch->autoVibAmp = 0; ch->autoVibSweep = (ins->autoVibDepth << 8) / ins->autoVibSweep; } else { ch->autoVibAmp = ins->autoVibDepth << 8; ch->autoVibSweep = 0; } }} void keyOff(channel_t *ch){ ch->keyOff = true; instr_t *ins = ch->instrPtr; assert(ins != NULL); if (ins->volEnvFlags & ENV_ENABLED) { if (ch->volEnvTick >= (uint16_t)ins->volEnvPoints[ch->volEnvPos][0]) ch->volEnvTick = ins->volEnvPoints[ch->volEnvPos][0] - 1; } else { ch->realVol = 0; ch->outVol = 0; ch->status |= IS_Vol + IS_QuickVol; } if (!(ins->panEnvFlags & ENV_ENABLED)) // What..? Probably an FT2 bug. { if (ch->panEnvTick >= (uint16_t)ins->panEnvPoints[ch->panEnvPos][0]) ch->panEnvTick = ins->panEnvPoints[ch->panEnvPos][0] - 1; }} void calcReplayerLogTab(void) // for linear period -> hz calculation{ for (int32_t i = 0; i < 32; i++) dExp2MulTab[i] = 1.0 / exp2(i); // 1/(2^i) for (int32_t i = 0; i < 4*12*16; i++) dLogTab[i] = (8363.0 * 256.0) * exp2(i / (4.0 * 12.0 * 16.0));} void calcReplayerVars(int32_t audioFreq){ assert(audioFreq > 0); if (audioFreq <= 0) return; audio.dHz2MixDeltaMul = (double)MIXER_FRAC_SCALE / audioFreq; audio.quickVolRampSamples = (uint32_t)round(audioFreq / (double)FT2_QUICKRAMP_SAMPLES); for (int32_t bpm = MIN_BPM; bpm <= MAX_BPM; bpm++) { const int32_t i = bpm - MIN_BPM; // index for tables const double dBpmHz = bpm / 2.5; const double dSamplesPerTick = audioFreq / dBpmHz; 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); audio.tickTimeIntTab[i] = (uint32_t)dTimeInt; audio.tickTimeFracTab[i] = (uint64_t)((dTimeFrac * TICK_TIME_FRAC_SCALE) + 0.5); // rounded }} // for piano in Instr. Ed. (values outside 0..95 can happen)int32_t getPianoKey(uint16_t period, int8_t finetune, int8_t relativeNote) { assert(note2Period != NULL); if (period > note2Period[0]) return -1; // outside left piano edge finetune = ((int8_t)finetune >> 3) + 16; // -128..127 -> 0..31 int32_t hiPeriod = 10*12*16; int32_t loPeriod = 0; for (int32_t i = 0; i < 7; i++) { const int32_t tmpPeriod = (((loPeriod + hiPeriod) >> 1) & ~15) + finetune; int32_t lookUp = tmpPeriod - 16; if (lookUp < 0) lookUp = 0; if (period >= note2Period[lookUp]) hiPeriod = (tmpPeriod - finetune) & ~15; else loPeriod = (tmpPeriod - finetune) & ~15; } return (loPeriod >> 4) - relativeNote;} static void startTone(uint8_t note, uint8_t efx, uint8_t efxData, channel_t *ch){ if (note == NOTE_OFF) { keyOff(ch); return; } // if we came from Rxy (retrig), we didn't check note (Ton) yet if (note == 0) { note = ch->noteNum; if (note == 0) return; // if still no note, exit from routine } ch->noteNum = note; assert(ch->instrNum <= 130); instr_t *ins = instr[ch->instrNum]; if (ins == NULL) ins = instr[0]; // empty instruments use this placeholder instrument ch->instrPtr = ins; ch->mute = ins->mute; if (note > 96) // non-FT2 security (should never happen because I clamp in the patt. loader now) note = 96; ch->smpNum = ins->note2SampleLUT[note-1] & 0xF; // FT2 doesn't AND here, but let's do it for safety sample_t *s = &ins->smp[ch->smpNum]; ch->smpPtr = s;Lines 1–500 of 3391. Load more
// for finding memory leaks in debug mode with Visual Studio #if defined _DEBUG && defined _MSC_VER #include <crtdbg.h> #endif #include <stdint.h> #include <stdio.h> #include <math.h> #include "ft2_header.h" #include "ft2_config.h" #include "ft2_gui.h" #include "ft2_video.h" #include "ft2_pattern_ed.h" #include "ft2_sample_ed.h" #include "ft2_inst_ed.h" #include "ft2_diskop.h" #include "ft2_midi.h" #include "scopes/ft2_scopes.h" #include "ft2_mouse.h" #include "ft2_sample_loader.h" #include "ft2_tables.h" #include "ft2_structs.h" #include "mixer/ft2_windowed_sinc.h" static double dLogTab[4*12*16], dExp2MulTab[32]; static bool bxxOverflow; static note_t nilPatternLine[MAX_CHANNELS]; typedef void (*volColumnEfxRoutine)(channel_t *ch); typedef void (*volColumnEfxRoutine2)(channel_t *ch, uint8_t *volColumnData); typedef void (*efxRoutine)(channel_t *ch, uint8_t param); // globally accessed int8_t playMode = 0; bool songPlaying = false, audioPaused = false, musicPaused = false; volatile bool replayerBusy = false; const uint16_t *note2Period = NULL; int16_t patternNumRows[MAX_PATTERNS]; channel_t channel[MAX_CHANNELS]; song_t song; instr_t *instr[128+4]; note_t *pattern[MAX_PATTERNS]; // ---------------------------------- void fixString(char *str, int32_t lastChrPos) // removes leading spaces and 0x1A chars { for (int32_t i = lastChrPos; i >= 0; i--) { if (str[i] == ' ' || str[i] == 0x1A) str[i] = '\0'; else if (str[i] != '\0') break; } str[lastChrPos+1] = '\0'; } void fixSongName(void) { fixString(song.name, 19); } void fixInstrAndSampleNames(int16_t insNum) { fixString(song.instrName[insNum], 21); if (instr[insNum] != NULL) { sample_t *s = instr[insNum]->smp; for (int32_t i = 0; i < MAX_SMP_PER_INST; i++, s++) fixString(s->name, 21); } } void resetChannels(void) { const bool audioWasntLocked = !audio.locked; if (audioWasntLocked) lockAudio(); memset(channel, 0, sizeof (channel)); channel_t *ch = channel; for (int32_t i = 0; i < MAX_CHANNELS; i++, ch++) { ch->instrPtr = instr[0]; ch->status = IS_Vol; ch->oldPan = 128; ch->outPan = 128; ch->finalPan = 128; ch->channelOff = !editor.chnMode[i]; // set channel mute flag from global mute flag } if (audioWasntLocked) unlockAudio(); } void setSongModifiedFlag(void) { song.isModified = true; editor.updateWindowTitle = true; } void removeSongModifiedFlag(void) { song.isModified = false; 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) { #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); if (errorCurr <= errorNext) break; // current is the closest period++; break; // current+1 is the closest } } s->finetune = ((period & 31) - 16) << 3; s->relativeNote = (int8_t)(((period & ~31) >> 4) - NOTE_C4); } void setPatternLen(uint16_t pattNum, int16_t numRows) { assert(pattNum < MAX_PATTERNS); if ((numRows < 1 || numRows > MAX_PATT_LEN) || numRows == patternNumRows[pattNum]) return; const bool audioWasntLocked = !audio.locked; if (audioWasntLocked) lockAudio(); patternNumRows[pattNum] = numRows; if (pattern[pattNum] != NULL) killPatternIfUnused(pattNum); // non-FT2 security song.pattDelTime = 0; song.pattDelTime2 = 0; song.pBreakFlag = false; song.posJumpFlag = false; song.pBreakPos = 0; song.currNumRows = numRows; if (song.row >= song.currNumRows) { song.row = song.currNumRows - 1; editor.row = song.row; } checkMarkLimits(); if (audioWasntLocked) unlockAudio(); ui.updatePatternEditor = true; ui.updatePosSections = true; } int16_t getUsedSamples(int16_t smpNum) { if (instr[smpNum] == NULL) return 0; instr_t *ins = instr[smpNum]; int16_t i = 16 - 1; while (i >= 0 && ins->smp[i].dataPtr == NULL && ins->smp[i].name[0] == '\0') i--; /* Yes, 'i' can be -1 here, and will be set to at least 0 ** because of ins->ta values. Possibly an FT2 bug... */ for (int16_t j = 0; j < 96; j++) { if (ins->note2SampleLUT[j] > i) i = ins->note2SampleLUT[j]; } return i+1; } int16_t getRealUsedSamples(int16_t smpNum) { if (instr[smpNum] == NULL) return 0; int8_t i = 16 - 1; while (i >= 0 && instr[smpNum]->smp[i].dataPtr == NULL) i--; return i+1; } double dLinearPeriod2Hz(int32_t period) { period &= 0xFFFF; // just in case (actual period range is 0..65535) if (period == 0) return 0.0; // in FT2, a period of 0 results in 0Hz const uint32_t invPeriod = ((12 * 192 * 4) - period) & 0xFFFF; // mask needed for FT2 period overflow quirk const uint32_t quotient = invPeriod / (12 * 16 * 4); const uint32_t remainder = invPeriod % (12 * 16 * 4); return dLogTab[remainder] * dExp2MulTab[(14-quotient) & 31]; // x = y >> ((14-quotient) & 31); } double dAmigaPeriod2Hz(int32_t period) { period &= 0xFFFF; // just in case (actual period range is 0..65535) if (period == 0) return 0.0; // in FT2, a period of 0 results in 0Hz return (double)(8363 * 1712) / period; } double dPeriod2Hz(int32_t period) { return audio.linearPeriodsFlag ? dLinearPeriod2Hz(period) : dAmigaPeriod2Hz(period); } // returns *exact* FT2 C-4 voice rate (depending on finetune, relativeNote and linear/Amiga period mode) double getSampleC4Rate(sample_t *s) { int32_t note = NOTE_C4 + s->relativeNote; if (note < 0) return -1; // shouldn't happen (just in case...) if (note >= (10*12)-1) return -1; // B-9 (after relativeTone add) = illegal! (won't play in replayer) const int32_t C4Period = (note << 4) + (((int8_t)s->finetune >> 3) + 16); const int32_t period = audio.linearPeriodsFlag ? linearPeriods[C4Period] : amigaPeriods[C4Period]; return dPeriod2Hz(period); } void setLinearPeriods(bool linearPeriodsFlag) { pauseAudio(); audio.linearPeriodsFlag = linearPeriodsFlag; if (audio.linearPeriodsFlag) note2Period = linearPeriods; else note2Period = amigaPeriods; resumeAudio(); if (ui.configScreenShown && editor.currConfigScreen == CONFIG_SCREEN_AUDIO) { // update "frequency slides" radiobutton, if it's shown setConfigAudioRadioButtonStates(); } // update mid-C freq. in instr. editor (it can slightly differ between Amiga/linear) if (ui.instEditorShown) drawC4Rate(); } static void retrigVolume(channel_t *ch) { ch->realVol = ch->oldVol; ch->outVol = ch->oldVol; ch->outPan = ch->oldPan; ch->status |= IS_Vol + IS_Pan + IS_QuickVol; } static void retrigEnvelopeVibrato(channel_t *ch) { if (!(ch->waveCtrl & 0x04)) ch->vibPos = 0; if (!(ch->waveCtrl & 0x40)) ch->tremPos = 0; ch->retrigCnt = 0; ch->tremorPos = 0; instr_t *ins = ch->instrPtr; assert(ins != NULL); if (ins->volEnvFlags & ENV_ENABLED) { ch->volEnvTick = 65535; ch->volEnvPos = 0; } if (ins->panEnvFlags & ENV_ENABLED) { ch->panEnvTick = 65535; ch->panEnvPos = 0; } ch->keyOff = false; ch->fadeoutSpeed = ins->fadeout; // FT2 doesn't check if fadeout is more than 4095! ch->fadeoutVol = 32768; if (ins->autoVibDepth > 0) { ch->autoVibPos = 0; if (ins->autoVibSweep > 0) { ch->autoVibAmp = 0; ch->autoVibSweep = (ins->autoVibDepth << 8) / ins->autoVibSweep; } else { ch->autoVibAmp = ins->autoVibDepth << 8; ch->autoVibSweep = 0; } } } void keyOff(channel_t *ch) { ch->keyOff = true; instr_t *ins = ch->instrPtr; assert(ins != NULL); if (ins->volEnvFlags & ENV_ENABLED) { if (ch->volEnvTick >= (uint16_t)ins->volEnvPoints[ch->volEnvPos][0]) ch->volEnvTick = ins->volEnvPoints[ch->volEnvPos][0] - 1; } else { ch->realVol = 0; ch->outVol = 0; ch->status |= IS_Vol + IS_QuickVol; } if (!(ins->panEnvFlags & ENV_ENABLED)) // What..? Probably an FT2 bug. { if (ch->panEnvTick >= (uint16_t)ins->panEnvPoints[ch->panEnvPos][0]) ch->panEnvTick = ins->panEnvPoints[ch->panEnvPos][0] - 1; } } void calcReplayerLogTab(void) // for linear period -> hz calculation { for (int32_t i = 0; i < 32; i++) dExp2MulTab[i] = 1.0 / exp2(i); // 1/(2^i) for (int32_t i = 0; i < 4*12*16; i++) dLogTab[i] = (8363.0 * 256.0) * exp2(i / (4.0 * 12.0 * 16.0)); } void calcReplayerVars(int32_t audioFreq) { assert(audioFreq > 0); if (audioFreq <= 0) return; audio.dHz2MixDeltaMul = (double)MIXER_FRAC_SCALE / audioFreq; audio.quickVolRampSamples = (uint32_t)round(audioFreq / (double)FT2_QUICKRAMP_SAMPLES); for (int32_t bpm = MIN_BPM; bpm <= MAX_BPM; bpm++) { const int32_t i = bpm - MIN_BPM; // index for tables const double dBpmHz = bpm / 2.5; const double dSamplesPerTick = audioFreq / dBpmHz; 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); audio.tickTimeIntTab[i] = (uint32_t)dTimeInt; audio.tickTimeFracTab[i] = (uint64_t)((dTimeFrac * TICK_TIME_FRAC_SCALE) + 0.5); // rounded } } // for piano in Instr. Ed. (values outside 0..95 can happen) int32_t getPianoKey(uint16_t period, int8_t finetune, int8_t relativeNote) { assert(note2Period != NULL); if (period > note2Period[0]) return -1; // outside left piano edge finetune = ((int8_t)finetune >> 3) + 16; // -128..127 -> 0..31 int32_t hiPeriod = 10*12*16; int32_t loPeriod = 0; for (int32_t i = 0; i < 7; i++) { const int32_t tmpPeriod = (((loPeriod + hiPeriod) >> 1) & ~15) + finetune; int32_t lookUp = tmpPeriod - 16; if (lookUp < 0) lookUp = 0; if (period >= note2Period[lookUp]) hiPeriod = (tmpPeriod - finetune) & ~15; else loPeriod = (tmpPeriod - finetune) & ~15; } return (loPeriod >> 4) - relativeNote; } static void startTone(uint8_t note, uint8_t efx, uint8_t efxData, channel_t *ch) { if (note == NOTE_OFF) { keyOff(ch); return; } // if we came from Rxy (retrig), we didn't check note (Ton) yet if (note == 0) { note = ch->noteNum; if (note == 0) return; // if still no note, exit from routine } ch->noteNum = note; assert(ch->instrNum <= 130); instr_t *ins = instr[ch->instrNum]; if (ins == NULL) ins = instr[0]; // empty instruments use this placeholder instrument ch->instrPtr = ins; ch->mute = ins->mute; if (note > 96) // non-FT2 security (should never happen because I clamp in the patt. loader now) note = 96; ch->smpNum = ins->note2SampleLUT[note-1] & 0xF; // FT2 doesn't AND here, but let's do it for safety sample_t *s = &ins->smp[ch->smpNum]; ch->smpPtr = s;