Merge branch 'master' into feature/filesystem

This commit is contained in:
Stephen Anthony 2022-07-14 17:17:33 -02:30
commit 944f20a011
2 changed files with 72 additions and 92 deletions

View File

@ -72,62 +72,71 @@ FrameLayout FrameLayoutDetector::detectedLayout(bool detectPal60, bool detectNts
} }
#endif #endif
// Multiply each hue's count with its NTSC and PAL stats and aggregate results // Init the layout based on scanline analysis.
// If NTSC/PAL results differ significantly, overrule frame result FrameLayout layout = myPalFrameSum > myNtscFrameSum ? FrameLayout::pal : FrameLayout::ntsc;
FrameLayout layout = myPalFrames > myNtscFrames ? FrameLayout::pal : FrameLayout::ntsc;
constexpr std::array<double, NUM_HUES> ntscColorFactor{ if(detectPal60 || detectNtsc50)
0.00000, 0.05683, 0.06220, 0.05505, 0.06162, 0.02874, 0.03532, 0.03716, {
0.15568, 0.06471, 0.02886, 0.03224, 0.06903, 0.11478, 0.02632, 0.01675 // Multiply each hue's count with its NTSC and PAL stats and aggregate results
}; // ignore black = 0x00! // If NTSC/PAL results differ significantly, overrule frame result
constexpr std::array<double, NUM_HUES> palColorFactor{ constexpr std::array<double, NUM_HUES> ntscColorFactor{
0.00000, 0.00450, 0.09962, 0.07603, 0.06978, 0.13023, 0.09638, 0.02268, 0.00000, 0.05683, 0.06220, 0.05505, 0.06162, 0.02874, 0.03532, 0.03716,
0.02871, 0.04700, 0.02950, 0.11974, 0.03474, 0.08025, 0.00642, 0.00167 0.15568, 0.06471, 0.02886, 0.03224, 0.06903, 0.11478, 0.02632, 0.01675
}; // ignore black = 0x00! }; // ignore black = 0x00!
// Calculation weights and params (optimum based on sampled ROMs optimized for PAL-60) constexpr std::array<double, NUM_HUES> palColorFactor{
constexpr double POWER_FACTOR = 0.17; // Level the color counts (large values become less relevant) 0.00000, 0.00450, 0.09962, 0.07603, 0.06978, 0.13023, 0.09638, 0.02268,
constexpr uInt32 SUM_DIV = 20; // Skip too small counts 0.02871, 0.04700, 0.02950, 0.11974, 0.03474, 0.08025, 0.00642, 0.00167
constexpr uInt32 MIN_VALID = 3; // Minimum number of different hues with significant counts }; // ignore black = 0x00!
constexpr double SUM_FACTOR = 2.0; // Minimum sum difference which triggers a layout change // Calculation weights and params (optimum based on sampled ROMs, optimized for PAL-60)
constexpr double POWER_FACTOR = 0.17; // Level the color counts (large values become less relevant)
constexpr uInt32 SUM_DIV = 20; // Skip too small counts (could be removed)
constexpr uInt32 MIN_VALID = 3; // Minimum number of different hues with significant counts
constexpr double OVERRULE_FACTOR = 2.0; // Minimum sum difference which triggers a layout change
double ntscSum{0}, palSum{0}; double ntscColSum{ 0 }, paCollSum{ 0 };
std::array<double, NUM_HUES> hueSum{0}; std::array<double, NUM_HUES> hueSum{ 0 };
double totalHueSum = 0; double totalHueSum = 0;
uInt32 validHues = 0; uInt32 validHues = 0;
// Aggregate hues // Aggregate hues
for(int hue = 0; hue < NUM_HUES; ++hue) for(int hue = 0; hue < NUM_HUES; ++hue)
{
for(int lum = 0; lum < NUM_LUMS; ++lum)
if(hue || lum) // skip 0x00
hueSum[hue] += myColorCount[hue * NUM_LUMS + lum];
hueSum[hue] = std::pow(hueSum[hue], POWER_FACTOR);
totalHueSum += hueSum[hue];
}
// Calculate hue sums
for(int hue = 0; hue < NUM_HUES; ++hue)
{
if(hueSum[hue] > totalHueSum / SUM_DIV)
validHues++;
ntscSum += hueSum[hue] * ntscColorFactor[hue];
palSum += hueSum[hue] * palColorFactor[hue];
}
// Correct layout if enough valid hues and significant sum difference
// TODO: Use fractional scanline counts for intermediate values around 285 scanlines, e.g.
// Desert Falcon, Dumbo's Flying Circus, Dungeon, Firefox, Millipede, Popeye, RS Basketball, Star Trek, Stunt Cycle
if(validHues >= MIN_VALID)
{
if(detectPal60 && layout == FrameLayout::ntsc && ntscSum * SUM_FACTOR < palSum)
{ {
layout = FrameLayout::pal60; for(int lum = 0; lum < NUM_LUMS; ++lum)
Logger::debug("Changed layout from NTSC into PAL-60"); if(hue || lum) // skip 0x00
hueSum[hue] += myColorCount[hue * NUM_LUMS + lum];
hueSum[hue] = std::pow(hueSum[hue], POWER_FACTOR);
totalHueSum += hueSum[hue];
} }
// Note: three false positives (Berzerk, Canyon Bomber, Jawbreaker) for NTSC-50 after // Calculate hue sums
// optimizing for PAL-60 for(int hue = 0; hue < NUM_HUES; ++hue)
else if(detectNtsc50 && layout == FrameLayout::pal && palSum * SUM_FACTOR < ntscSum)
{ {
layout = FrameLayout::ntsc50; if(hueSum[hue] > totalHueSum / SUM_DIV)
Logger::debug("Changed layout from PAL into NTSC-50"); validHues++;
ntscColSum += hueSum[hue] * ntscColorFactor[hue];
paCollSum += hueSum[hue] * palColorFactor[hue];
}
// Correct the layout if there are enough valid hues and a significant color sum difference.
// The required difference depends on the significance of the scanline analyis.
if(validHues >= MIN_VALID)
{
// Use frame analysis results to scale color overrule factor from 1.0 .. OVERRULE_FACTOR
const double overRuleFactor = 1.0 + (OVERRULE_FACTOR - 1.0) * 2
* (std::max(myNtscFrameSum, myPalFrameSum) / (myNtscFrameSum + myPalFrameSum) - 0.5); // 1.0 .. OVERRULE_FACTOR
//cerr << overRuleFactor << " * PAL:" << paCollSum << "/NTSC:" << ntscColSum << endl;
if(detectPal60 && layout == FrameLayout::ntsc && ntscColSum * overRuleFactor < paCollSum)
{
layout = FrameLayout::pal60;
Logger::debug("TV format changed from NTSC to PAL-60");
}
// Note: Three false positives (Adventure, Berzerk, Canyon Bomber) for NTSC-50 after
// optimizing for PAL-60
else if(detectNtsc50 && layout == FrameLayout::pal && paCollSum * overRuleFactor < ntscColSum)
{
layout = FrameLayout::ntsc50;
Logger::debug("TV format changed from PAL to NTSC-50");
}
} }
} }
return layout; return layout;
@ -143,7 +152,7 @@ FrameLayoutDetector::FrameLayoutDetector()
void FrameLayoutDetector::onReset() void FrameLayoutDetector::onReset()
{ {
myState = State::waitForVsyncStart; myState = State::waitForVsyncStart;
myNtscFrames = myPalFrames = 0; myNtscFrameSum = myPalFrameSum = 0;
myLinesWaitingForVsyncToStart = 0; myLinesWaitingForVsyncToStart = 0;
myColorCount.fill(0); myColorCount.fill(0);
myIsRendering = true; myIsRendering = true;
@ -223,37 +232,14 @@ void FrameLayoutDetector::finalizeFrame()
if (myTotalFrames <= Metrics::initialGarbageFrames) return; if (myTotalFrames <= Metrics::initialGarbageFrames) return;
// Calculate the delta between scanline count and the sweet spot for the respective // Calculate how close a frame is to PAL and NTSC based on scanlines. An odd scanline count
// frame layouts // results into a penalty of 0.5 for PAL. The result is between 0.0 (<=262 scanlines) and
const uInt32 // 1.0 (>=312) and added to PAL and (inverted) NTSC sums.
deltaNTSC = abs(static_cast<Int32>(myCurrentFrameFinalLines) - static_cast<Int32>(frameLinesNTSC)), constexpr double ODD_PENALTY = 0.5; // guessed value :)
deltaPAL = abs(static_cast<Int32>(myCurrentFrameFinalLines) - static_cast<Int32>(frameLinesPAL)); const double palFrame = BSPF::clamp(((myCurrentFrameFinalLines % 2) ? ODD_PENALTY : 1.0)
* static_cast<double>(myCurrentFrameFinalLines - frameLinesNTSC)
// Does the scanline count fall into one of our tolerance windows? -> use it / static_cast<double>(frameLinesPAL - frameLinesNTSC), 0.0, 1.0);
if (std::min(deltaNTSC, deltaPAL) <= Metrics::tvModeDetectionTolerance) myPalFrameSum += palFrame;
layout(deltaNTSC <= deltaPAL ? FrameLayout::ntsc : FrameLayout::pal); myNtscFrameSum += 1.0 - palFrame;
else if ( //cerr << myCurrentFrameFinalLines << ", " << palFrame << ", " << myPalFrameSum << ", " << myNtscFrameSum << endl;
// If scanline count is odd and lies between the PAL and NTSC windows we assume
// it is NTSC (it would cause color loss on PAL CRTs)
(myCurrentFrameFinalLines < frameLinesPAL) &&
(myCurrentFrameFinalLines > frameLinesNTSC) &&
(myCurrentFrameFinalLines % 2)
)
layout(FrameLayout::ntsc);
else
// Take the nearest layout if all else fails
layout(deltaNTSC <= deltaPAL ? FrameLayout::ntsc : FrameLayout::pal);
switch (layout()) {
case FrameLayout::ntsc:
++myNtscFrames;
break;
case FrameLayout::pal:
++myPalFrames;
break;
default:
throw runtime_error("cannot happen");
}
} }

View File

@ -95,9 +95,6 @@ class FrameLayoutDetector: public AbstractFrameManager
// (exceeding ideal frame height) // (exceeding ideal frame height)
waitForVsync = 100, waitForVsync = 100,
// tolerance window around ideal frame size for TV mode detection
tvModeDetectionTolerance = 20,
// these frames will not be considered for detection // these frames will not be considered for detection
initialGarbageFrames = TIAConstants::initialGarbageFrames initialGarbageFrames = TIAConstants::initialGarbageFrames
}; };
@ -121,10 +118,8 @@ class FrameLayoutDetector: public AbstractFrameManager
*/ */
State myState{State::waitForVsyncStart}; State myState{State::waitForVsyncStart};
/** // The aggregated likelynesses of respective two frame layouts.
* The total number of frames detected as the respective frame layout. double myNtscFrameSum{0}, myPalFrameSum{0};
*/
uInt32 myNtscFrames{0}, myPalFrames{0};
/** /**
* We count the number of scanlines we spend waiting for vsync to be * We count the number of scanlines we spend waiting for vsync to be
@ -139,7 +134,6 @@ class FrameLayoutDetector: public AbstractFrameManager
*/ */
static constexpr int NUM_HUES = 16; static constexpr int NUM_HUES = 16;
static constexpr int NUM_LUMS = 8; static constexpr int NUM_LUMS = 8;
std::array<uInt64, NUM_HUES * NUM_LUMS> myColorCount{0}; std::array<uInt64, NUM_HUES * NUM_LUMS> myColorCount{0};
private: private: