enhanced jitter (resolves #316)

This commit is contained in:
Thomas Jentzsch 2022-06-05 15:14:51 +02:00
parent eb6d854599
commit 3beb5599e2
28 changed files with 367 additions and 150 deletions

View File

@ -45,6 +45,8 @@
* Added another oddball TIA glitch option for score mode color. * Added another oddball TIA glitch option for score mode color.
* Enhanced TV jitter emulation (TODO: doc).
* Enhanced support for CDFJ+ bankswitching type. * Enhanced support for CDFJ+ bankswitching type.
* Added 0FA0 bankswitching for Fotomania ROMs. * Added 0FA0 bankswitching for Fotomania ROMs.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

@ -1982,6 +1982,18 @@
<td>Cmd + J</td> <td>Cmd + J</td>
</tr> </tr>
<tr>
<td><i>Decrease</i> TV jitter sensitivity</td>
<td>Shift-Control-Alt + J</td>
<td>Shift-Control-Cmd + J</td>
</tr>
<tr>
<td><i>Increase</i> TV jitter sensitivity</td>
<td>Control-Alt + J</td>
<td>Control-Cmd + J</td>
</tr>
<tr> <tr>
<td><i>Decrease</i> TV jitter roll time</td> <td><i>Decrease</i> TV jitter roll time</td>
<td>Shift-Control + J</td> <td>Shift-Control + J</td>
@ -3812,10 +3824,13 @@
<td><pre>-&lt;plr.|dev.&gt;tv.jitter &lt;1|0&gt;</pre></td> <td><pre>-&lt;plr.|dev.&gt;tv.jitter &lt;1|0&gt;</pre></td>
<td>Enable TV jitter/roll effect, when there are too many or too few scanlines <td>Enable TV jitter/roll effect, when there are too many or too few scanlines
per frame.</td> per frame.</td>
</tr><tr>
<td><pre>-&lt;plr.|dev.&gt;tv.jitter_sense &lt;1 - 10&gt;</pre></td>
<td>When TV jitter/roll effect is enabled, determines the sensitivy to varying frame timings.</td>
</tr><tr> </tr><tr>
<td><pre>-&lt;plr.|dev.&gt;tv.jitter_recovery &lt;1 - 20&gt;</pre></td> <td><pre>-&lt;plr.|dev.&gt;tv.jitter_recovery &lt;1 - 20&gt;</pre></td>
<td>When TV jitter/roll effect is enabled, determines how long to delay recovery <td>When TV jitter/roll effect is enabled, determines the recovery time for screen rolling
time (recovery spread over multiple frames).</td> (recovery spread over multiple frames).</td>
</tr><tr> </tr><tr>
<td><pre>-&lt;plr.|dev.&gt;colorloss &lt;1|0&gt;</pre></td> <td><pre>-&lt;plr.|dev.&gt;colorloss &lt;1|0&gt;</pre></td>
<td>Enable/disable the PAL color-loss effect.</td> <td>Enable/disable the PAL color-loss effect.</td>
@ -4677,6 +4692,7 @@
<table border="1" cellpadding="4"> <table border="1" cellpadding="4">
<tr><th>Item</th><th>Brief description</th><th>For more information,<br>see <a href="#CommandLine">Command Line</a></th></tr> <tr><th>Item</th><th>Brief description</th><th>For more information,<br>see <a href="#CommandLine">Command Line</a></th></tr>
<tr><td>Jitter/roll effect</td><td>Emulate screen roll with inconsistent scanline count</td><td>-plr.tv.jitter<br/>-dev.tv.jitter</td></tr> <tr><td>Jitter/roll effect</td><td>Emulate screen roll with inconsistent scanline count</td><td>-plr.tv.jitter<br/>-dev.tv.jitter</td></tr>
<tr><td>(Jitter/roll) Sensitivity</td><td>Determines sensitivity for screen rolling</td><td><span style="white-space:nowrap">-plr.tv.jitter_sense<br/>-dev.tv.jitter_sense</span></td></tr>
<tr><td>(Jitter/roll) Recovery</td><td>Determines recovery time for screen rolling</td><td><span style="white-space:nowrap">-plr.tv.jitter_recovery<br/>-dev.tv.jitter_recovery</span></td></tr> <tr><td>(Jitter/roll) Recovery</td><td>Determines recovery time for screen rolling</td><td><span style="white-space:nowrap">-plr.tv.jitter_recovery<br/>-dev.tv.jitter_recovery</span></td></tr>
<tr><td>PAL color-loss</td><td>Use PAL color-loss effect</td><td>-plr.colorloss<br/>-dev.colorloss</td></tr> <tr><td>PAL color-loss</td><td>Use PAL color-loss effect</td><td>-plr.colorloss<br/>-dev.colorloss</td></tr>
<tr><td>Debug colors</td><td>Use fixed debug colors</td><td>-plr.debugcolors<br/>-dev.debugcolors</td></tr> <tr><td>Debug colors</td><td>Use fixed debug colors</td><td>-plr.debugcolors<br/>-dev.debugcolors</td></tr>

View File

@ -78,6 +78,7 @@ void DevSettingsHandler::loadSettings(SettingsSet set)
myColorLoss[set] = settings.getBool(prefix + "colorloss"); myColorLoss[set] = settings.getBool(prefix + "colorloss");
// Jitter // Jitter
myTVJitter[set] = settings.getBool(prefix + "tv.jitter"); myTVJitter[set] = settings.getBool(prefix + "tv.jitter");
myTVJitterSense[set] = settings.getInt(prefix + "tv.jitter_sense");
myTVJitterRec[set] = settings.getInt(prefix + "tv.jitter_recovery"); myTVJitterRec[set] = settings.getInt(prefix + "tv.jitter_recovery");
// States // States
@ -148,6 +149,7 @@ void DevSettingsHandler::saveSettings(SettingsSet set)
settings.setValue(prefix + "colorloss", myColorLoss[set]); settings.setValue(prefix + "colorloss", myColorLoss[set]);
// Jitter // Jitter
settings.setValue(prefix + "tv.jitter", myTVJitter[set]); settings.setValue(prefix + "tv.jitter", myTVJitter[set]);
settings.setValue(prefix + "tv.jitter_sense", myTVJitterSense[set]);
settings.setValue(prefix + "tv.jitter_recovery", myTVJitterRec[set]); settings.setValue(prefix + "tv.jitter_recovery", myTVJitterRec[set]);
// States // States
@ -200,6 +202,7 @@ void DevSettingsHandler::applySettings(SettingsSet set)
{ {
// TV Jitter // TV Jitter
myOSystem.console().tia().toggleJitter(myTVJitter[set] ? 1 : 0); myOSystem.console().tia().toggleJitter(myTVJitter[set] ? 1 : 0);
myOSystem.console().tia().setJitterSensitivity(myTVJitterSense[set]);
myOSystem.console().tia().setJitterRecoveryFactor(myTVJitterRec[set]); myOSystem.console().tia().setJitterRecoveryFactor(myTVJitterRec[set]);
// PAL color loss // PAL color loss
myOSystem.console().enableColorLoss(myColorLoss[set]); myOSystem.console().enableColorLoss(myColorLoss[set]);

View File

@ -55,6 +55,7 @@ class DevSettingsHandler
std::array<string, numSets> myRandomizeCPU; std::array<string, numSets> myRandomizeCPU;
std::array<bool, numSets> myColorLoss; std::array<bool, numSets> myColorLoss;
std::array<bool, numSets> myTVJitter; std::array<bool, numSets> myTVJitter;
std::array<int, numSets> myTVJitterSense;
std::array<int, numSets> myTVJitterRec; std::array<int, numSets> myTVJitterRec;
std::array<bool, numSets> myDebugColors; std::array<bool, numSets> myDebugColors;
std::array<bool, numSets> myUndrivenPins; std::array<bool, numSets> myUndrivenPins;

View File

@ -700,8 +700,10 @@ PhysicalKeyboardHandler::DefaultCommonMapping = {
{ Event::DecreaseSpeed, KBDK_S, KBDM_SHIFT | KBDM_CTRL }, { Event::DecreaseSpeed, KBDK_S, KBDM_SHIFT | KBDM_CTRL },
{ Event::IncreaseSpeed, KBDK_S, KBDM_CTRL }, { Event::IncreaseSpeed, KBDK_S, KBDM_CTRL },
{ Event::ToggleTurbo, KBDK_T, KBDM_CTRL }, { Event::ToggleTurbo, KBDK_T, KBDM_CTRL },
{ Event::JitterDecrease, KBDK_J, KBDM_SHIFT | KBDM_CTRL }, { Event::JitterSenseDecrease, KBDK_J, KBDM_SHIFT | MOD3 | KBDM_CTRL },
{ Event::JitterIncrease, KBDK_J, KBDM_CTRL }, { Event::JitterSenseIncrease, KBDK_J, MOD3 | KBDM_CTRL },
{ Event::JitterRecDecrease, KBDK_J, KBDM_SHIFT | KBDM_CTRL },
{ Event::JitterRecIncrease, KBDK_J, KBDM_CTRL },
{ Event::ToggleDeveloperSet, KBDK_D, MOD3 }, { Event::ToggleDeveloperSet, KBDK_D, MOD3 },
{ Event::ToggleJitter, KBDK_J, MOD3 }, { Event::ToggleJitter, KBDK_J, MOD3 },
{ Event::ToggleFrameStats, KBDK_L, MOD3 }, { Event::ToggleFrameStats, KBDK_L, MOD3 },

View File

@ -361,8 +361,10 @@ NLOHMANN_JSON_SERIALIZE_ENUM(Event::Type, {
{Event::TogglePhosphor, "TogglePhosphor"}, {Event::TogglePhosphor, "TogglePhosphor"},
{Event::ToggleDeveloperSet, "ToggleDeveloperSet"}, {Event::ToggleDeveloperSet, "ToggleDeveloperSet"},
{Event::ToggleInter, "ToggleInter"}, {Event::ToggleInter, "ToggleInter"},
{Event::JitterDecrease, "JitterDecrease"}, {Event::JitterSenseDecrease, "JitterSenseDecrease"},
{Event::JitterIncrease, "JitterIncrease"}, {Event::JitterSenseIncrease, "JitterSenseIncrease"},
{Event::JitterRecDecrease, "JitterRecDecrease"},
{Event::JitterRecIncrease, "JitterRecIncrease"},
{Event::ToggleJitter, "ToggleJitter"}, {Event::ToggleJitter, "ToggleJitter"},
{Event::VolumeDecrease, "VolumeDecrease"}, {Event::VolumeDecrease, "VolumeDecrease"},
{Event::VolumeIncrease, "VolumeIncrease"}, {Event::VolumeIncrease, "VolumeIncrease"},

View File

@ -1253,7 +1253,40 @@ void Console::toggleJitter(bool toggle) const
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Console::changeJitter(int direction) const void Console::changeJitterSense(int direction) const
{
const string prefix = myOSystem.settings().getBool("dev.settings") ? "dev." : "plr.";
int sensitivity = myOSystem.settings().getInt(prefix + "tv.jitter_sense");
const bool enabled = direction ? sensitivity + direction > 0 : myTIA->toggleJitter(3);
// if disabled, enable before first before increasing recovery
if(!myTIA->toggleJitter(3))
direction = 0;
sensitivity = BSPF::clamp(static_cast<Int32>(sensitivity + direction),
JitterEmulation::MIN_SENSITIVITY, JitterEmulation::MAX_SENSITIVITY);
myOSystem.settings().setValue(prefix + "tv.jitter", enabled);
if(enabled)
{
ostringstream val;
myTIA->toggleJitter(1);
myTIA->setJitterSensitivity(sensitivity);
myOSystem.settings().setValue(prefix + "tv.jitter_sense", sensitivity);
val << sensitivity;
myOSystem.frameBuffer().showGaugeMessage("TV jitter sensitivity", val.str(), sensitivity,
0, JitterEmulation::MAX_SENSITIVITY);
}
else
{
myTIA->toggleJitter(0);
myOSystem.frameBuffer().showTextMessage("TV scanline jitter disabled");
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Console::changeJitterRecovery(int direction) const
{ {
const string prefix = myOSystem.settings().getBool("dev.settings") ? "dev." : "plr."; const string prefix = myOSystem.settings().getBool("dev.settings") ? "dev." : "plr.";
int recovery = myOSystem.settings().getInt(prefix + "tv.jitter_recovery"); int recovery = myOSystem.settings().getInt(prefix + "tv.jitter_recovery");
@ -1263,7 +1296,8 @@ void Console::changeJitter(int direction) const
if(!myTIA->toggleJitter(3)) if(!myTIA->toggleJitter(3))
direction = 0; direction = 0;
recovery = BSPF::clamp(recovery + direction, 1, 20); recovery = BSPF::clamp(static_cast<Int32>(recovery + direction),
JitterEmulation::MIN_RECOVERY, JitterEmulation::MAX_RECOVERY);
myOSystem.settings().setValue(prefix + "tv.jitter", enabled); myOSystem.settings().setValue(prefix + "tv.jitter", enabled);
if(enabled) if(enabled)
@ -1274,7 +1308,8 @@ void Console::changeJitter(int direction) const
myTIA->setJitterRecoveryFactor(recovery); myTIA->setJitterRecoveryFactor(recovery);
myOSystem.settings().setValue(prefix + "tv.jitter_recovery", recovery); myOSystem.settings().setValue(prefix + "tv.jitter_recovery", recovery);
val << recovery; val << recovery;
myOSystem.frameBuffer().showGaugeMessage("TV jitter roll", val.str(), recovery, 0, 20); myOSystem.frameBuffer().showGaugeMessage("TV jitter roll", val.str(), recovery,
0, JitterEmulation::MAX_RECOVERY);
} }
else else
{ {

View File

@ -357,10 +357,15 @@ class Console : public Serializable, public ConsoleIO
*/ */
void toggleJitter(bool toggle = true) const; void toggleJitter(bool toggle = true) const;
/**
Changes the TIA 'scanline jitter' sensitivity.
*/
void changeJitterSense(int direction = +1) const;
/** /**
Changes the TIA 'scanline jitter' revcovery rate. Changes the TIA 'scanline jitter' revcovery rate.
*/ */
void changeJitter(int direction = +1) const; void changeJitterRecovery(int direction = +1) const;
/** /**
* Update vcenter * Update vcenter

View File

@ -116,7 +116,8 @@ class Event
ScanlinesDecrease, ScanlinesIncrease, ScanlinesDecrease, ScanlinesIncrease,
PreviousScanlineMask, NextScanlineMask, PreviousScanlineMask, NextScanlineMask,
PhosphorDecrease, PhosphorIncrease, TogglePhosphor, ToggleInter, PhosphorDecrease, PhosphorIncrease, TogglePhosphor, ToggleInter,
ToggleDeveloperSet, JitterDecrease, JitterIncrease, ToggleJitter, ToggleDeveloperSet, JitterRecDecrease, JitterRecIncrease,
JitterSenseDecrease, JitterSenseIncrease, ToggleJitter,
VolumeDecrease, VolumeIncrease, SoundToggle, VolumeDecrease, VolumeIncrease, SoundToggle,

View File

@ -976,23 +976,39 @@ void EventHandler::handleEvent(Event::Type event, Int32 value, bool repeated)
if(pressed && !repeated) if(pressed && !repeated)
{ {
myOSystem.console().toggleJitter(); myOSystem.console().toggleJitter();
myGlobalKeyHandler->setSetting(GlobalKeyHandler::Setting::JITTER); myGlobalKeyHandler->setSetting(GlobalKeyHandler::Setting::JITTER_SENSE);
} }
return; return;
case Event::JitterDecrease: case Event::JitterSenseDecrease:
if(pressed) if(pressed)
{ {
myOSystem.console().changeJitter(-1); myOSystem.console().changeJitterSense(-1);
myGlobalKeyHandler->setSetting(GlobalKeyHandler::Setting::JITTER); myGlobalKeyHandler->setSetting(GlobalKeyHandler::Setting::JITTER_SENSE);
} }
return; return;
case Event::JitterIncrease: case Event::JitterSenseIncrease:
if(pressed) if(pressed)
{ {
myOSystem.console().changeJitter(+1); myOSystem.console().changeJitterSense(+1);
myGlobalKeyHandler->setSetting(GlobalKeyHandler::Setting::JITTER); myGlobalKeyHandler->setSetting(GlobalKeyHandler::Setting::JITTER_SENSE);
}
return;
case Event::JitterRecDecrease:
if(pressed)
{
myOSystem.console().changeJitterRecovery(-1);
myGlobalKeyHandler->setSetting(GlobalKeyHandler::Setting::JITTER_REC);
}
return;
case Event::JitterRecIncrease:
if(pressed)
{
myOSystem.console().changeJitterRecovery(+1);
myGlobalKeyHandler->setSetting(GlobalKeyHandler::Setting::JITTER_REC);
} }
return; return;
@ -3041,8 +3057,10 @@ EventHandler::EmulActionList EventHandler::ourEmulActionList = { {
{ Event::ToggleFixedColors, "Toggle TIA 'Fixed Debug Colors' mode", "" }, { Event::ToggleFixedColors, "Toggle TIA 'Fixed Debug Colors' mode", "" },
{ Event::ToggleColorLoss, "Toggle PAL color-loss effect", "" }, { Event::ToggleColorLoss, "Toggle PAL color-loss effect", "" },
{ Event::ToggleJitter, "Toggle TV scanline 'Jitter' effect", "" }, { Event::ToggleJitter, "Toggle TV scanline 'Jitter' effect", "" },
{ Event::JitterDecrease, "Decrease TV 'Jitter' roll", "" }, { Event::JitterSenseDecrease, "Decrease TV 'Jitter' sensitivity", "" },
{ Event::JitterIncrease, "Increase TV 'Jitter' roll", "" }, { Event::JitterSenseIncrease, "Increase TV 'Jitter' sensitivity", "" },
{ Event::JitterRecDecrease, "Decrease TV 'Jitter' roll", "" },
{ Event::JitterRecIncrease, "Increase TV 'Jitter' roll", "" },
// Combo // Combo
{ Event::Combo1, "Combo 1", "" }, { Event::Combo1, "Combo 1", "" },
@ -3230,5 +3248,6 @@ const Event::EventSet EventHandler::DebugEvents = {
Event::ToggleBLCollision, Event::ToggleBLBit, Event::TogglePFCollision, Event::TogglePFBit, Event::ToggleBLCollision, Event::ToggleBLBit, Event::TogglePFCollision, Event::TogglePFBit,
Event::ToggleCollisions, Event::ToggleBits, Event::ToggleFixedColors, Event::ToggleCollisions, Event::ToggleBits, Event::ToggleFixedColors,
Event::ToggleColorLoss, Event::ToggleColorLoss,
Event::ToggleJitter, Event::JitterDecrease,Event::JitterIncrease, Event::ToggleJitter, Event::JitterSenseDecrease,Event::JitterSenseIncrease,
Event::JitterRecDecrease,Event::JitterRecIncrease,
}; };

View File

@ -524,7 +524,7 @@ class EventHandler
#else #else
REFRESH_SIZE = 0, REFRESH_SIZE = 0,
#endif #endif
EMUL_ACTIONLIST_SIZE = 230 + PNG_SIZE + COMBO_SIZE + REFRESH_SIZE, EMUL_ACTIONLIST_SIZE = 232 + PNG_SIZE + COMBO_SIZE + REFRESH_SIZE,
MENU_ACTIONLIST_SIZE = 19 MENU_ACTIONLIST_SIZE = 19
; ;

View File

@ -436,7 +436,8 @@ GlobalKeyHandler::SettingData GlobalKeyHandler::getSettingData(const Setting set
{Setting::ALL_CX, {false, std::bind(&Console::toggleCollisions, &myOSystem.console(), _1)}}, // debug, not persisted {Setting::ALL_CX, {false, std::bind(&Console::toggleCollisions, &myOSystem.console(), _1)}}, // debug, not persisted
{Setting::FIXED_COL, {false, std::bind(&Console::toggleFixedColors, &myOSystem.console(), _1)}}, // debug, not persisted {Setting::FIXED_COL, {false, std::bind(&Console::toggleFixedColors, &myOSystem.console(), _1)}}, // debug, not persisted
{Setting::COLOR_LOSS, {false, std::bind(&Console::toggleColorLoss, &myOSystem.console(), _1)}}, {Setting::COLOR_LOSS, {false, std::bind(&Console::toggleColorLoss, &myOSystem.console(), _1)}},
{Setting::JITTER, {true, std::bind(&Console::changeJitter, &myOSystem.console(), _1)}}, {Setting::JITTER_SENSE, {true, std::bind(&Console::changeJitterSense, &myOSystem.console(), _1)}},
{Setting::JITTER_REC, {true, std::bind(&Console::changeJitterRecovery, &myOSystem.console(), _1)}},
// *** Following functions are not used when cycling settings, but for "direct only" hotkeys *** // *** Following functions are not used when cycling settings, but for "direct only" hotkeys ***
{Setting::STATE, {true, std::bind(&StateManager::changeState, &myOSystem.state(), _1)}}, // temporary, not persisted {Setting::STATE, {true, std::bind(&StateManager::changeState, &myOSystem.state(), _1)}}, // temporary, not persisted
{Setting::PALETTE_ATTRIBUTE, {true, std::bind(&PaletteHandler::changeCurrentAdjustable, &myOSystem.frameBuffer().tiaSurface().paletteHandler(), _1)}}, {Setting::PALETTE_ATTRIBUTE, {true, std::bind(&PaletteHandler::changeCurrentAdjustable, &myOSystem.frameBuffer().tiaSurface().paletteHandler(), _1)}},

View File

@ -118,7 +118,8 @@ class GlobalKeyHandler
ALL_CX, ALL_CX,
FIXED_COL, FIXED_COL,
COLOR_LOSS, COLOR_LOSS,
JITTER, JITTER_SENSE,
JITTER_REC,
// *** Only used via direct hotkeys *** // *** Only used via direct hotkeys ***
STATE, STATE,
PALETTE_ATTRIBUTE, PALETTE_ATTRIBUTE,
@ -131,7 +132,7 @@ class GlobalKeyHandler
START_INPUT_ADJ = DIGITAL_DEADZONE, START_INPUT_ADJ = DIGITAL_DEADZONE,
END_INPUT_ADJ = MOUSE_RANGE, END_INPUT_ADJ = MOUSE_RANGE,
START_DEBUG_ADJ = DEVELOPER, START_DEBUG_ADJ = DEVELOPER,
END_DEBUG_ADJ = JITTER, END_DEBUG_ADJ = JITTER_REC,
}; };
public: public:

View File

@ -43,9 +43,9 @@ void MindLink::update()
myEvent.get(Event::MouseButtonRightValue)) myEvent.get(Event::MouseButtonRightValue))
myMindlinkPos = myMindlinkPos | TRIGGER_VALUE; // starts game, calibration and reverse myMindlinkPos = myMindlinkPos | TRIGGER_VALUE; // starts game, calibration and reverse
#ifdef DEBUG_BUILD //#ifdef DEBUG_BUILD
cerr << std::hex << myMindlinkPos << endl; // cerr << std::hex << myMindlinkPos << endl;
#endif //#endif
myMindlinkShift = 1; // start transfer with least significant bit myMindlinkShift = 1; // start transfer with least significant bit
nextMindlinkBit(); nextMindlinkBit();

View File

@ -25,6 +25,7 @@
#include "PaletteHandler.hxx" #include "PaletteHandler.hxx"
#include "Joystick.hxx" #include "Joystick.hxx"
#include "Paddles.hxx" #include "Paddles.hxx"
#include "JitterEmulation.hxx"
#ifdef DEBUGGER_SUPPORT #ifdef DEBUGGER_SUPPORT
#include "DebuggerDialog.hxx" #include "DebuggerDialog.hxx"
@ -225,7 +226,8 @@ Settings::Settings()
setPermanent("plr.tiarandom", "false"); setPermanent("plr.tiarandom", "false");
setPermanent("plr.colorloss", "false"); setPermanent("plr.colorloss", "false");
setPermanent("plr.tv.jitter", "true"); setPermanent("plr.tv.jitter", "true");
setPermanent("plr.tv.jitter_recovery", "10"); setPermanent("plr.tv.jitter_sense", JitterEmulation::PLR_SENSITIVITY);
setPermanent("plr.tv.jitter_recovery", JitterEmulation::PLR_RECOVERY);
setPermanent("plr.debugcolors", "false"); setPermanent("plr.debugcolors", "false");
setPermanent("plr.console", "2600"); // 7800 setPermanent("plr.console", "2600"); // 7800
setPermanent("plr.timemachine", true); setPermanent("plr.timemachine", true);
@ -245,7 +247,8 @@ Settings::Settings()
setPermanent("dev.tiarandom", "true"); setPermanent("dev.tiarandom", "true");
setPermanent("dev.colorloss", "true"); setPermanent("dev.colorloss", "true");
setPermanent("dev.tv.jitter", "true"); setPermanent("dev.tv.jitter", "true");
setPermanent("dev.tv.jitter_recovery", "2"); setPermanent("dev.tv.jitter_sense", JitterEmulation::DEV_SENSITIVITY);
setPermanent("dev.tv.jitter_recovery", JitterEmulation::DEV_RECOVERY);
setPermanent("dev.debugcolors", "false"); setPermanent("dev.debugcolors", "false");
setPermanent("dev.tiadriven", "true"); setPermanent("dev.tiadriven", "true");
setPermanent("dev.console", "2600"); // 7800 setPermanent("dev.console", "2600"); // 7800
@ -336,8 +339,13 @@ void Settings::validate()
i = getInt("tv.filter"); i = getInt("tv.filter");
if(i < 0 || i > 5) setValue("tv.filter", "0"); if(i < 0 || i > 5) setValue("tv.filter", "0");
i = getInt("dev.tv.jitter_sense");
if(i < JitterEmulation::MIN_SENSITIVITY || i > JitterEmulation::MAX_SENSITIVITY)
setValue("dev.tv.jitter_sense", JitterEmulation::DEV_SENSITIVITY);
i = getInt("dev.tv.jitter_recovery"); i = getInt("dev.tv.jitter_recovery");
if(i < 1 || i > 20) setValue("dev.tv.jitter_recovery", "2"); if(i < JitterEmulation::MIN_RECOVERY || i > JitterEmulation::MAX_RECOVERY)
setValue("dev.tv.jitter_recovery", JitterEmulation::DEV_RECOVERY);
int size = getInt("dev.tm.size"); int size = getInt("dev.tm.size");
if(size < 20 || size > 1000) if(size < 20 || size > 1000)
@ -355,8 +363,12 @@ void Settings::validate()
i = getInt("dev.tm.horizon"); i = getInt("dev.tm.horizon");
if(i < 0 || i > 6) setValue("dev.tm.horizon", 1);*/ if(i < 0 || i > 6) setValue("dev.tm.horizon", 1);*/
i = getInt("plr.tv.jitter_sense");
if(i < JitterEmulation::MIN_SENSITIVITY || i > JitterEmulation::MAX_SENSITIVITY)
setValue("plr.tv.jitter_sense", JitterEmulation::PLR_SENSITIVITY);
i = getInt("plr.tv.jitter_recovery"); i = getInt("plr.tv.jitter_recovery");
if(i < 1 || i > 20) setValue("plr.tv.jitter_recovery", "10"); if(i < 1 || i > 20) setValue("plr.tv.jitter_recovery", JitterEmulation::PLR_RECOVERY);
size = getInt("plr.tm.size"); size = getInt("plr.tm.size");
if(size < 20 || size > 1000) if(size < 20 || size > 1000)
@ -713,6 +725,7 @@ void Settings::usage() const
<< " -plr.debugcolors <1|0> Enable debug colors\n" << " -plr.debugcolors <1|0> Enable debug colors\n"
<< " -plr.colorloss <1|0> Enable PAL color-loss effect\n" << " -plr.colorloss <1|0> Enable PAL color-loss effect\n"
<< " -plr.tv.jitter <1|0> Enable TV jitter effect\n" << " -plr.tv.jitter <1|0> Enable TV jitter effect\n"
<< " -plr.tv.jitter_sense <1-10> Set TV jitter effect sensitivity\n"
<< " -plr.tv.jitter_recovery <1-20> Set recovery time for TV jitter effect\n" << " -plr.tv.jitter_recovery <1-20> Set recovery time for TV jitter effect\n"
<< " -plr.extaccess <1|0> Enable messages for external access\n" << " -plr.extaccess <1|0> Enable messages for external access\n"
<< endl << endl
@ -729,8 +742,9 @@ void Settings::usage() const
<< " -dev.debugcolors <1|0> Enable debug colors\n" << " -dev.debugcolors <1|0> Enable debug colors\n"
<< " -dev.colorloss <1|0> Enable PAL color-loss effect\n" << " -dev.colorloss <1|0> Enable PAL color-loss effect\n"
<< " -dev.tv.jitter <1|0> Enable TV jitter effect\n" << " -dev.tv.jitter <1|0> Enable TV jitter effect\n"
<< " -dev.tv.jitter_sense <1-10> Set TV jitter effect sensitivity\n"
<< " -dev.tv.jitter_recovery <1-20> Set recovery time for TV jitter effect\n" << " -dev.tv.jitter_recovery <1-20> Set recovery time for TV jitter effect\n"
<< " -dev.tiadriven <1|0> Drive unused TIA pins randomly on a\n" << " -dev.tiadriven <1|0> Drive unqused TIA pins randomly on a\n"
<< " read/peek\n" << " read/peek\n"
#ifdef DEBUGGER_SUPPORT #ifdef DEBUGGER_SUPPORT
<< " -dev.rwportbreak <1|0> Debugger breaks on reads from write ports\n" << " -dev.rwportbreak <1|0> Debugger breaks on reads from write ports\n"

View File

@ -104,7 +104,8 @@ void TIA::setFrameManager(AbstractFrameManager* frameManager)
); );
myFrameManager->enableJitter(myEnableJitter); myFrameManager->enableJitter(myEnableJitter);
myFrameManager->setJitterFactor(myJitterFactor); myFrameManager->setJitterSensitivity(myJitterSensitivity);
myFrameManager->setJitterRecovery(myJitterRecovery);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -544,7 +545,7 @@ bool TIA::poke(uInt16 address, uInt8 value)
break; break;
case VSYNC: case VSYNC:
myFrameManager->setVsync(value & 0x02); myFrameManager->setVsync(value & 0x02, mySystem->cycles());
myShadowRegisters[address] = value; myShadowRegisters[address] = value;
break; break;
@ -985,7 +986,8 @@ void TIA::applyDeveloperSettings()
myTIAPinsDriven = devSettings ? mySettings.getBool("dev.tiadriven") : false; myTIAPinsDriven = devSettings ? mySettings.getBool("dev.tiadriven") : false;
myEnableJitter = mySettings.getBool(devSettings ? "dev.tv.jitter" : "plr.tv.jitter"); myEnableJitter = mySettings.getBool(devSettings ? "dev.tv.jitter" : "plr.tv.jitter");
myJitterFactor = mySettings.getInt(devSettings ? "dev.tv.jitter_recovery" : "plr.tv.jitter_recovery"); myJitterSensitivity = mySettings.getInt(devSettings ? "dev.tv.jitter_sense" : "plr.tv.jitter_sense");
myJitterRecovery = mySettings.getInt(devSettings ? "dev.tv.jitter_recovery" : "plr.tv.jitter_recovery");
if(myFrameManager) if(myFrameManager)
enableColorLoss(mySettings.getBool(devSettings ? "dev.colorloss" : "plr.colorloss")); enableColorLoss(mySettings.getBool(devSettings ? "dev.colorloss" : "plr.colorloss"));
@ -1281,12 +1283,20 @@ bool TIA::toggleJitter(uInt8 mode)
return myEnableJitter; return myEnableJitter;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIA::setJitterSensitivity(Int32 sensitivity)
{
myJitterSensitivity = sensitivity;
if (myFrameManager) myFrameManager->setJitterSensitivity(sensitivity);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIA::setJitterRecoveryFactor(Int32 factor) void TIA::setJitterRecoveryFactor(Int32 factor)
{ {
myJitterFactor = factor; myJitterRecovery = factor;
if (myFrameManager) myFrameManager->setJitterFactor(myJitterFactor); if (myFrameManager) myFrameManager->setJitterRecovery(myJitterRecovery);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -441,6 +441,7 @@ class TIA : public Device
@return Whether the mode was enabled or disabled @return Whether the mode was enabled or disabled
*/ */
bool toggleJitter(uInt8 mode = 2); bool toggleJitter(uInt8 mode = 2);
void setJitterSensitivity(Int32 sensitivity);
void setJitterRecoveryFactor(Int32 factor); void setJitterRecoveryFactor(Int32 factor);
/** /**
@ -977,7 +978,8 @@ class TIA : public Device
* The frame manager can change during our lifetime, so we buffer those two. * The frame manager can change during our lifetime, so we buffer those two.
*/ */
bool myEnableJitter{false}; bool myEnableJitter{false};
uInt8 myJitterFactor{0}; uInt8 myJitterSensitivity{0};
uInt8 myJitterRecovery{0};
static constexpr uInt16 static constexpr uInt16
TIA_SIZE = 0x40, TIA_MASK = TIA_SIZE - 1, TIA_SIZE = 0x40, TIA_MASK = TIA_SIZE - 1,

View File

@ -72,13 +72,13 @@ void AbstractFrameManager::setVblank(bool vblank)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void AbstractFrameManager::setVsync(bool vsync) void AbstractFrameManager::setVsync(bool vsync, uInt64 cycles)
{ {
if (vsync == myVsync) return; if (vsync == myVsync) return;
myVsync = vsync; myVsync = vsync;
onSetVsync(); onSetVsync(cycles);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -67,7 +67,7 @@ class AbstractFrameManager : public Serializable
/** /**
* Called by TIA on VSYNC writes. * Called by TIA on VSYNC writes.
*/ */
void setVsync(bool vsync); void setVsync(bool vsync, uInt64 cycles);
/** /**
* Should the TIA render its frame? This is buffered in a flag for * Should the TIA render its frame? This is buffered in a flag for
@ -123,10 +123,15 @@ class AbstractFrameManager : public Serializable
// required. All of these are irrelevant if nothing is displayed (during // required. All of these are irrelevant if nothing is displayed (during
// autodetect). // autodetect).
/**
* The jitter sensitivity determines jitter simulation sensitivity to unstable video signals.
*/
virtual void setJitterSensitivity(uInt8 sensitivity) {}
/** /**
* The jitter factor determines the time jitter simulation takes to recover. * The jitter factor determines the time jitter simulation takes to recover.
*/ */
virtual void setJitterFactor(uInt8 factor) {} virtual void setJitterRecovery(uInt8 factor) {}
/** /**
* Is jitter simulation enabled? * Is jitter simulation enabled?
@ -207,7 +212,7 @@ class AbstractFrameManager : public Serializable
/** /**
* Called if vsync changes. * Called if vsync changes.
*/ */
virtual void onSetVsync() {} virtual void onSetVsync(uInt64 cycles) {}
/** /**
* Called if the next line is signalled, after the internal bookkeeping has * Called if the next line is signalled, after the internal bookkeeping has

View File

@ -39,7 +39,7 @@ void FrameLayoutDetector::onReset()
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameLayoutDetector::onSetVsync() void FrameLayoutDetector::onSetVsync(uInt64)
{ {
if (myVsync) if (myVsync)
setState(State::waitForVsyncEnd); setState(State::waitForVsyncEnd);

View File

@ -44,7 +44,7 @@ class FrameLayoutDetector: public AbstractFrameManager
/** /**
* Hook into vsync changes. * Hook into vsync changes.
*/ */
void onSetVsync() override; void onSetVsync(uInt64) override;
/** /**
* Hook into reset. * Hook into reset.

View File

@ -114,10 +114,16 @@ void FrameManager::setAdjustVSize(Int32 adjustVSize)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameManager::onSetVsync() void FrameManager::onSetVsync(uInt64 cycles)
{ {
if (myState == State::waitForVsyncEnd) setState(State::waitForFrameStart); if (myState == State::waitForVsyncEnd) {
else setState(State::waitForVsyncEnd); myVsyncEnd = cycles;
setState(State::waitForFrameStart);
}
else {
myVsyncStart = cycles;
setState(State::waitForVsyncEnd);
}
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -133,7 +139,7 @@ void FrameManager::setState(FrameManager::State state)
notifyFrameComplete(); notifyFrameComplete();
if (myTotalFrames > Metrics::initialGarbageFrames) if (myTotalFrames > Metrics::initialGarbageFrames)
myJitterEmulation.frameComplete(myCurrentFrameFinalLines); myJitterEmulation.frameComplete(myCurrentFrameFinalLines, myVsyncEnd - myVsyncStart);
notifyFrameStart(); notifyFrameStart();

View File

@ -47,8 +47,9 @@ class FrameManager: public AbstractFrameManager {
FrameManager(); FrameManager();
public: public:
void setJitterSensitivity(uInt8 sensitivity) override { myJitterEmulation.setSensitivity(sensitivity); }
void setJitterFactor(uInt8 factor) override { myJitterEmulation.setJitterFactor(factor); } void setJitterRecovery(uInt8 factor) override { myJitterEmulation.setRecovery(factor); }
bool jitterEnabled() const override { return myJitterEnabled; } bool jitterEnabled() const override { return myJitterEnabled; }
@ -78,7 +79,7 @@ class FrameManager: public AbstractFrameManager {
void setLayout(FrameLayout mode) override { layout(mode); } void setLayout(FrameLayout mode) override { layout(mode); }
void onSetVsync() override; void onSetVsync(uInt64 cycles) override;
void onNextLine() override; void onNextLine() override;
@ -122,6 +123,8 @@ class FrameManager: public AbstractFrameManager {
Int32 myMaxVcenter{0}; Int32 myMaxVcenter{0};
Int32 myVSizeAdjust{0}; Int32 myVSizeAdjust{0};
uInt64 myVsyncStart{0};
uInt64 myVsyncEnd{0};
bool myJitterEnabled{false}; bool myJitterEnabled{false};
JitterEmulation myJitterEmulation; JitterEmulation myJitterEmulation;

View File

@ -26,52 +26,103 @@ JitterEmulation::JitterEmulation()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void JitterEmulation::reset() void JitterEmulation::reset()
{ {
myLastFrameScanlines = 0; setSensitivity(mySensitivity);
myStableFrameFinalLines = -1;
myStableFrames = 0;
myStabilizationCounter = 0;
myDestabilizationCounter = 0;
myJitter = 0;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void JitterEmulation::frameComplete(uInt32 scanlineCount) void JitterEmulation::setSensitivity(Int32 sensitivity)
{ {
if (static_cast<Int32>(scanlineCount) != myStableFrameFinalLines) { myLastFrameScanlines = myLastFrameVsyncCycles = myUnstableCount = myJitter = 0;
if (myDestabilizationCounter++ > Metrics::framesUntilDestabilization) myStableFrameFinalLines = -1; mySensitivity = BSPF::clamp(sensitivity, MIN_SENSITIVITY, MAX_SENSITIVITY);
if (scanlineCount == myLastFrameScanlines) { const float factor = pow(static_cast<float>(mySensitivity - MIN_SENSITIVITY) / (MAX_SENSITIVITY - MIN_SENSITIVITY), 1.5);
if (++myStabilizationCounter >= Metrics::framesForStableHeight) { myScanlineDelta = round(MAX_SCANLINE_DELTA - (MAX_SCANLINE_DELTA - MIN_SCANLINE_DELTA) * factor);
if (myStableFrameFinalLines > 0) updateJitter(scanlineCount - myStableFrameFinalLines); myVsyncCycles = round(MIN_VSYNC_CYCLES + (MAX_VSYNC_CYCLES - MIN_VSYNC_CYCLES) * factor);
myVsyncDelta1 = round(MAX_VSYNC_DELTA_1 - (MAX_VSYNC_DELTA_1 - MIN_VSYNC_DELTA_1) * factor);
#ifdef VSYNC_LINE_JITTER
myVsyncDelta2 = round(MIN_VSYNC_DELTA_2 + (MAX_VSYNC_DELTA_2 - MIN_VSYNC_DELTA_2) * factor);
#endif
myUnstableFrames = round(MAX_UNSTABLE_FRAMES - (MAX_UNSTABLE_FRAMES - MIN_UNSTABLE_FRAMES) * factor);
myJitterLines = round(MIN_JITTER_LINES + (MAX_JITTER_LINES - MIN_JITTER_LINES) * factor);
myVsyncLines = round(MIN_VSYNC_LINES + (MAX_VSYNC_LINES - MIN_VSYNC_LINES) * factor);
}
myStableFrameFinalLines = scanlineCount; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
myDestabilizationCounter = 0; void JitterEmulation::frameComplete(Int32 scanlineCount, Int32 vsyncCycles)
{
#ifdef DEBUG_BUILD
const int vsyncLines = round((vsyncCycles - 2) / 76.0);
cerr << "TV jitter " << myJitter << " - " << scanlineCount << ", " << vsyncCycles << ", " << vsyncLines << endl;
#endif
// Check if current frame size is stable compared to previous frame
const bool scanlinesStable = scanlineCount == myLastFrameScanlines;
const bool vsyncCyclesStable = vsyncCycles >= myVsyncCycles;
// Handle inconsistency of vsync cycles and around half lines
#ifdef VSYNC_LINE_JITTER
const Int32 minLines = round((vsyncCycles - 2 - myVsyncDelta2) / 76.0);
const Int32 maxLines = round((vsyncCycles - 2 + myVsyncDelta2) / 76.0);
const Int32 minLastLines = round((myLastFrameVsyncCycles - 2 - myVsyncDelta2) / 76.0);
const Int32 maxLastLines = round((myLastFrameVsyncCycles - 2 + myVsyncDelta2) / 76.0);
const bool vsyncLinesStable = abs(vsyncCycles - myLastFrameVsyncCycles) < myVsyncDelta1
&& minLines == maxLastLines && maxLines == minLastLines;
#else
const bool vsyncLinesStable = abs(vsyncCycles - myLastFrameVsyncCycles) < myVsyncDelta1;
#endif
if(!scanlinesStable || !vsyncCyclesStable || !vsyncLinesStable)
{
if(++myUnstableCount >= myUnstableFrames)
{
if(!scanlinesStable)
{
const Int32 scanlineDifference = scanlineCount - myLastFrameScanlines;
if(abs(scanlineDifference) < myScanlineDelta
&& abs(myJitter) < static_cast<Int32>(myRandom.next() % myJitterLines))
{
// Repeated invalid frames cause randomly repeated jitter
myJitter = std::max(std::min(scanlineDifference, myJitterLines), -myYStart);
}
} }
if(!vsyncCyclesStable)
{
// If VSYNC length is too low, the frame rolls permanently down, speed depending on missing cycles
const Int32 jitter = std::max(
std::min<Int32>(round(scanlineCount * (1 - static_cast<float>(vsyncCycles) / myVsyncCycles)),
myJitterLines),
myJitterRecovery + 1); // Roll at least one scanline
myJitter -= jitter;
// Limit jitter to -myYstart..scanlineCount
if(myJitter < -myYStart)
myJitter += 262;
}
if(!vsyncLinesStable)
{
#ifdef VSYNC_LINE_JITTER
myJitter += minLines > maxLastLines ? myVsyncLines : -myVsyncLines;
#else
myJitter += vsyncCycles > myLastFrameVsyncCycles ? myVsyncLines : -myVsyncLines;
#endif
}
myJitter = std::max(myJitter, -myYStart);
} }
else myStabilizationCounter = 0;
} }
else myDestabilizationCounter = 0; else
{
myUnstableCount = 0;
// Only recover during stable frames
if(myJitter > 0)
myJitter = std::max(myJitter - myJitterRecovery, 0);
else if(myJitter < 0)
myJitter = std::min(myJitter + myJitterRecovery, 0);
}
myLastFrameScanlines = scanlineCount; myLastFrameScanlines = scanlineCount;
myLastFrameVsyncCycles = vsyncCycles;
if (myJitter > 0) myJitter = std::max(myJitter - myJitterFactor, 0);
if (myJitter < 0) myJitter = std::min(myJitter + myJitterFactor, 0);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void JitterEmulation::updateJitter(Int32 scanlineDifference)
{
if (static_cast<uInt32>(abs(scanlineDifference)) < Metrics::minDeltaForJitter) return;
Int32 jitter = std::min<Int32>(scanlineDifference, Metrics::maxJitter);
jitter = std::max<Int32>(jitter, -myYStart);
if (jitter > 0) jitter += myJitterFactor;
if (jitter < 0) jitter -= myJitterFactor;
if (abs(jitter) > abs(myJitter)) myJitter = jitter;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -79,14 +130,13 @@ bool JitterEmulation::save(Serializer& out) const
{ {
try try
{ {
out.putInt(myLastFrameScanlines); out.putInt(mySensitivity);
out.putInt(myStableFrameFinalLines); out.putInt(myJitterRecovery);
out.putInt(myStableFrames);
out.putInt(myStabilizationCounter);
out.putInt(myDestabilizationCounter);
out.putInt(myJitter);
out.putInt(myJitterFactor);
out.putInt(myYStart); out.putInt(myYStart);
out.putInt(myLastFrameScanlines);
out.putInt(myLastFrameVsyncCycles);
out.putInt(myUnstableCount);
out.putInt(myJitter);
} }
catch(...) catch(...)
{ {
@ -103,14 +153,13 @@ bool JitterEmulation::load(Serializer& in)
{ {
try try
{ {
myLastFrameScanlines = in.getInt(); mySensitivity = in.getInt();
myStableFrameFinalLines = in.getInt(); myJitterRecovery = in.getInt();
myStableFrames = in.getInt();
myStabilizationCounter = in.getInt();
myDestabilizationCounter = in.getInt();
myJitter = in.getInt();
myJitterFactor = in.getInt();
myYStart = in.getInt(); myYStart = in.getInt();
myLastFrameScanlines = in.getInt();
myLastFrameVsyncCycles = in.getInt();
myUnstableCount = in.getInt();
myJitter = in.getInt();
} }
catch (...) catch (...)
{ {
@ -118,6 +167,7 @@ bool JitterEmulation::load(Serializer& in)
return false; return false;
} }
setSensitivity(mySensitivity);
return true; return true;
} }

View File

@ -18,27 +18,38 @@
#ifndef TIA_JITTER_EMULATION #ifndef TIA_JITTER_EMULATION
#define TIA_JITTER_EMULATION #define TIA_JITTER_EMULATION
//#define VSYNC_LINE_JITTER // makes e.g. Alien jitter, but not on my TV (TJ)
#include "bspf.hxx" #include "bspf.hxx"
#include "Serializable.hxx" #include "Serializable.hxx"
#include "Random.hxx"
class JitterEmulation: public Serializable class JitterEmulation : public Serializable
{ {
public: public:
JitterEmulation(); JitterEmulation();
public: public:
// sensitivity:
static constexpr Int32 MIN_SENSITIVITY = 1;
static constexpr Int32 MAX_SENSITIVITY = 10;
static constexpr Int32 PLR_SENSITIVITY = 3;
static constexpr Int32 DEV_SENSITIVITY = 8;
// roll speed:
static constexpr Int32 MIN_RECOVERY = 1;
static constexpr Int32 MAX_RECOVERY = 20;
static constexpr Int32 PLR_RECOVERY = 10;
static constexpr Int32 DEV_RECOVERY = 2;
public:
void reset(); void reset();
void setSensitivity(Int32 sensitivity);
void setRecovery(Int32 recoveryFactor) { myJitterRecovery = recoveryFactor; }
void setYStart(Int32 ystart) { myYStart = ystart; }
void frameComplete(uInt32 scanlineCount); void frameComplete(Int32 scanlineCount, Int32 vsyncCycles);
void setJitterFactor(Int32 factor) { myJitterFactor = factor; }
Int32 jitter() const { return myJitter; } Int32 jitter() const { return myJitter; }
void setYStart(uInt32 ystart) { myYStart = ystart; }
/** /**
* Save state. * Save state.
*/ */
@ -50,36 +61,48 @@ class JitterEmulation: public Serializable
bool load(Serializer& in) override; bool load(Serializer& in) override;
private: private:
// varying scanlines:
void updateJitter(Int32 scanlineDifference); static constexpr Int32 MIN_SCANLINE_DELTA = 1; // was: 3
static constexpr Int32 MAX_SCANLINE_DELTA = 5;
// varying VSYNC timing:
static constexpr Int32 MIN_VSYNC_CYCLES = 76 * 3 / 4;
static constexpr Int32 MAX_VSYNC_CYCLES = 76 * 3;
static constexpr Int32 MIN_VSYNC_DELTA_1 = 1;
static constexpr Int32 MAX_VSYNC_DELTA_1 = 76 / 3;
#ifdef VSYNC_LINE_JITTER
static constexpr Int32 MIN_VSYNC_DELTA_2 = 1;
static constexpr Int32 MAX_VSYNC_DELTA_2 = 10;
#endif
// threshold for jitter:
static constexpr Int32 MIN_UNSTABLE_FRAMES = 1; // was: 10
static constexpr Int32 MAX_UNSTABLE_FRAMES = 10;
// resulting jitter:
static constexpr Int32 MIN_JITTER_LINES = 1; // was: 50
static constexpr Int32 MAX_JITTER_LINES = 200;
static constexpr Int32 MIN_VSYNC_LINES = 1;
static constexpr Int32 MAX_VSYNC_LINES = 5;
private: private:
Random myRandom;
uInt32 myLastFrameScanlines{0}; Int32 myLastFrameScanlines{0};
Int32 myLastFrameVsyncCycles{0};
Int32 myStableFrameFinalLines{-1}; Int32 myUnstableCount{0};
uInt32 myStableFrames{0};
uInt32 myStabilizationCounter{0};
uInt32 myDestabilizationCounter{0};
Int32 myJitter{0}; Int32 myJitter{0};
Int32 myJitterRecovery{0};
Int32 myJitterFactor{0}; Int32 myYStart{0};
Int32 mySensitivity{MIN_SENSITIVITY};
uInt32 myYStart{0}; Int32 myScanlineDelta{MAX_SCANLINE_DELTA};
Int32 myVsyncCycles{MIN_VSYNC_CYCLES};
enum Metrics: uInt32 { Int32 myVsyncDelta1{MAX_VSYNC_DELTA_1};
framesForStableHeight = 2, #ifdef VSYNC_LINE_JITTER
framesUntilDestabilization = 10, Int32 myVsyncDelta2{MIN_VSYNC_DELTA_2};
minDeltaForJitter = 3, #endif
maxJitter = 50 Int32 myUnstableFrames{MAX_UNSTABLE_FRAMES};
}; Int32 myJitterLines{MIN_JITTER_LINES};
Int32 myVsyncLines{MIN_VSYNC_LINES};
private: private:
JitterEmulation(const JitterEmulation&) = delete; JitterEmulation(const JitterEmulation&) = delete;
JitterEmulation(JitterEmulation&&) = delete; JitterEmulation(JitterEmulation&&) = delete;
JitterEmulation& operator=(const JitterEmulation&) = delete; JitterEmulation& operator=(const JitterEmulation&) = delete;

View File

@ -33,6 +33,7 @@
#include "Font.hxx" #include "Font.hxx"
#include "Console.hxx" #include "Console.hxx"
#include "TIA.hxx" #include "TIA.hxx"
#include "JitterEmulation.hxx"
#include "OSystem.hxx" #include "OSystem.hxx"
#include "EventHandler.hxx" #include "EventHandler.hxx"
#include "StateManager.hxx" #include "StateManager.hxx"
@ -372,21 +373,33 @@ void DeveloperDialog::addVideoTab(const GUI::Font& font)
// TV jitter effect // TV jitter effect
myTVJitterWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 1, ypos + 1, myTVJitterWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 1, ypos + 1,
"Jitter/roll effect", kTVJitter); "Jitter/roll effect", kTVJitter);
ypos += lineHeight + VGAP;
myTVJitterWidget->setToolTip("Enable to emulate TV loss of sync.", Event::ToggleJitter); myTVJitterWidget->setToolTip("Enable to emulate TV loss of sync.", Event::ToggleJitter);
wid.push_back(myTVJitterWidget); wid.push_back(myTVJitterWidget);
myTVJitterSenseWidget = new SliderWidget(myTab, font,
myTVJitterWidget->getLeft() + CheckboxWidget::prefixSize(font), ypos - 1,
fontWidth * 9, lineHeight,
"Sensitivity ", 0, 0, fontWidth * 2);
myTVJitterSenseWidget->setMinValue(JitterEmulation::MIN_SENSITIVITY);
myTVJitterSenseWidget->setMaxValue(JitterEmulation::MAX_SENSITIVITY);
myTVJitterSenseWidget->setTickmarkIntervals(3);
myTVJitterSenseWidget->setToolTip("Define sensitivity to unstable frames.",
Event::JitterSenseDecrease, Event::JitterSenseIncrease);
wid.push_back(myTVJitterSenseWidget);
myTVJitterRecWidget = new SliderWidget(myTab, font, myTVJitterRecWidget = new SliderWidget(myTab, font,
myTVJitterWidget->getRight() + fontWidth * 3, ypos - 1, myTVJitterSenseWidget->getRight() + fontWidth * 2, ypos - 1,
"Recovery ", 0, kTVJitterChanged); fontWidth * 9, lineHeight,
myTVJitterRecWidget->setMinValue(1); myTVJitterRecWidget->setMaxValue(20); "Recovery ", 0, 0, fontWidth * 2);
myTVJitterRecWidget->setMinValue(JitterEmulation::MIN_RECOVERY);
myTVJitterRecWidget->setMaxValue(JitterEmulation::MAX_RECOVERY);
myTVJitterRecWidget->setTickmarkIntervals(5); myTVJitterRecWidget->setTickmarkIntervals(5);
myTVJitterRecWidget->setToolTip("Define speed of sync recovery.", myTVJitterRecWidget->setToolTip("Define speed of sync recovery.",
Event::JitterDecrease, Event::JitterIncrease); Event::JitterRecDecrease, Event::JitterRecIncrease);
wid.push_back(myTVJitterRecWidget); wid.push_back(myTVJitterRecWidget);
myTVJitterRecLabelWidget = new StaticTextWidget(myTab, font, ypos += lineHeight + VGAP * 2;
myTVJitterRecWidget->getRight() + 4,
myTVJitterRecWidget->getTop() + 2,
5 * fontWidth, fontHeight);
ypos += lineHeight + VGAP;
myColorLossWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 1, ypos + 1, myColorLossWidget = new CheckboxWidget(myTab, font, HBORDER + INDENT * 1, ypos + 1,
"PAL color-loss"); "PAL color-loss");
@ -728,6 +741,7 @@ void DeveloperDialog::getWidgetStates(SettingsSet set)
myColorLoss[set] = myColorLossWidget->getState(); myColorLoss[set] = myColorLossWidget->getState();
// Jitter // Jitter
myTVJitter[set] = myTVJitterWidget->getState(); myTVJitter[set] = myTVJitterWidget->getState();
myTVJitterSense[set] = myTVJitterSenseWidget->getValue();
myTVJitterRec[set] = myTVJitterRecWidget->getValue(); myTVJitterRec[set] = myTVJitterRecWidget->getValue();
// States // States
@ -777,9 +791,10 @@ void DeveloperDialog::setWidgetStates(SettingsSet set)
myColorLossWidget->setState(myColorLoss[set]); myColorLossWidget->setState(myColorLoss[set]);
// Jitter // Jitter
myTVJitterWidget->setState(myTVJitter[set]); myTVJitterWidget->setState(myTVJitter[set]);
myTVJitterSenseWidget->setValue(myTVJitterSense[set]);
myTVJitterRecWidget->setValue(myTVJitterRec[set]); myTVJitterRecWidget->setValue(myTVJitterRec[set]);
handleTVJitterChange(myTVJitterWidget->getState()); handleTVJitterChange();
// States // States
myTimeMachineWidget->setState(myTimeMachine[set]); myTimeMachineWidget->setState(myTimeMachine[set]);
@ -931,6 +946,9 @@ void DeveloperDialog::setDefaults()
case 2: // Video case 2: // Video
// Jitter // Jitter
myTVJitter[set] = true; myTVJitter[set] = true;
myTVJitterSense[set] = devSettings
? JitterEmulation::DEV_SENSITIVITY
: JitterEmulation::PLR_SENSITIVITY;
myTVJitterRec[set] = devSettings ? 2 : 10; myTVJitterRec[set] = devSettings ? 2 : 10;
// PAL color-loss effect // PAL color-loss effect
myColorLoss[set] = devSettings ? true : false; myColorLoss[set] = devSettings ? true : false;
@ -997,11 +1015,7 @@ void DeveloperDialog::handleCommand(CommandSender* sender, int cmd, int data, in
break; break;
case kTVJitter: case kTVJitter:
handleTVJitterChange(myTVJitterWidget->getState()); handleTVJitterChange();
break;
case kTVJitterChanged:
myTVJitterRecLabelWidget->setValue(myTVJitterRecWidget->getValue());
break; break;
case kTimeMachine: case kTimeMachine:
@ -1101,10 +1115,12 @@ void DeveloperDialog::handleSettings(bool devSettings)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DeveloperDialog::handleTVJitterChange(bool enable) void DeveloperDialog::handleTVJitterChange()
{ {
bool enable = myTVJitterWidget->getState();
myTVJitterSenseWidget->setEnabled(enable);
myTVJitterRecWidget->setEnabled(enable); myTVJitterRecWidget->setEnabled(enable);
myTVJitterRecLabelWidget->setEnabled(enable);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -60,7 +60,6 @@ class DeveloperDialog : public Dialog, DevSettingsHandler
kConsole = 'DVco', kConsole = 'DVco',
kTIAType = 'DVtt', kTIAType = 'DVtt',
kTVJitter = 'DVjt', kTVJitter = 'DVjt',
kTVJitterChanged = 'DVjr',
kTimeMachine = 'DTtm', kTimeMachine = 'DTtm',
kSizeChanged = 'DTsz', kSizeChanged = 'DTsz',
kUncompressedChanged = 'DTuc', kUncompressedChanged = 'DTuc',
@ -125,6 +124,7 @@ class DeveloperDialog : public Dialog, DevSettingsHandler
CheckboxWidget* myTVJitterWidget{nullptr}; CheckboxWidget* myTVJitterWidget{nullptr};
SliderWidget* myTVJitterRecWidget{nullptr}; SliderWidget* myTVJitterRecWidget{nullptr};
StaticTextWidget* myTVJitterRecLabelWidget{nullptr}; StaticTextWidget* myTVJitterRecLabelWidget{nullptr};
SliderWidget* myTVJitterSenseWidget{nullptr};
CheckboxWidget* myColorLossWidget{nullptr}; CheckboxWidget* myColorLossWidget{nullptr};
CheckboxWidget* myDebugColorsWidget{nullptr}; CheckboxWidget* myDebugColorsWidget{nullptr};
std::array<PopUpWidget*, DEBUG_COLORS> myDbgColour{nullptr}; std::array<PopUpWidget*, DEBUG_COLORS> myDbgColour{nullptr};
@ -160,7 +160,7 @@ class DeveloperDialog : public Dialog, DevSettingsHandler
void setWidgetStates(SettingsSet set); void setWidgetStates(SettingsSet set);
void handleSettings(bool devSettings); void handleSettings(bool devSettings);
void handleTVJitterChange(bool enable); void handleTVJitterChange();
void handleConsole(); void handleConsole();
void handleTia(); void handleTia();