mirror of https://github.com/stella-emu/stella.git
203 lines
5.6 KiB
C++
203 lines
5.6 KiB
C++
//============================================================================
|
|
//
|
|
// SSSS tt lll lll
|
|
// SS SS tt ll ll
|
|
// SS tttttt eeee ll ll aaaa
|
|
// SSSS tt ee ee ll ll aa
|
|
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
|
// SS SS tt ee ll ll aa aa
|
|
// SSSS ttt eeeee llll llll aaaaa
|
|
//
|
|
// Copyright (c) 1995-2020 by Bradford W. Mott, Stephen Anthony
|
|
// and the Stella Team
|
|
//
|
|
// See the file "License.txt" for information on usage and redistribution of
|
|
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
|
//============================================================================
|
|
|
|
#include <cmath>
|
|
|
|
#include "EmulationTiming.hxx"
|
|
|
|
namespace {
|
|
constexpr uInt32 AUDIO_HALF_FRAMES_PER_FRAGMENT = 1;
|
|
|
|
uInt32 discreteDivCeil(uInt32 n, uInt32 d)
|
|
{
|
|
return n / d + ((n % d == 0) ? 0 : 1);
|
|
}
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
EmulationTiming::EmulationTiming(FrameLayout frameLayout, ConsoleTiming consoleTiming) :
|
|
myFrameLayout(frameLayout),
|
|
myConsoleTiming(consoleTiming)
|
|
{
|
|
recalculate();
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
EmulationTiming& EmulationTiming::updateFrameLayout(FrameLayout frameLayout)
|
|
{
|
|
myFrameLayout = frameLayout;
|
|
recalculate();
|
|
|
|
return *this;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
EmulationTiming& EmulationTiming::updateConsoleTiming(ConsoleTiming consoleTiming)
|
|
{
|
|
myConsoleTiming = consoleTiming;
|
|
recalculate();
|
|
|
|
return *this;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
EmulationTiming& EmulationTiming::updatePlaybackRate(uInt32 playbackRate)
|
|
{
|
|
myPlaybackRate = playbackRate;
|
|
recalculate();
|
|
|
|
return *this;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
EmulationTiming& EmulationTiming::updatePlaybackPeriod(uInt32 playbackPeriod)
|
|
{
|
|
myPlaybackPeriod = playbackPeriod;
|
|
recalculate();
|
|
|
|
return *this;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
EmulationTiming& EmulationTiming::updateAudioQueueExtraFragments(uInt32 audioQueueExtraFragments)
|
|
{
|
|
myAudioQueueExtraFragments = audioQueueExtraFragments;
|
|
recalculate();
|
|
|
|
return *this;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
EmulationTiming& EmulationTiming::updateAudioQueueHeadroom(uInt32 audioQueueHeadroom)
|
|
{
|
|
myAudioQueueHeadroom = audioQueueHeadroom;
|
|
recalculate();
|
|
|
|
return *this;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
EmulationTiming& EmulationTiming::updateSpeedFactor(float speedFactor)
|
|
{
|
|
mySpeedFactor = static_cast<double>(speedFactor);
|
|
recalculate();
|
|
|
|
return *this;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
uInt32 EmulationTiming::maxCyclesPerTimeslice() const
|
|
{
|
|
return myMaxCyclesPerTimeslice;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
uInt32 EmulationTiming::minCyclesPerTimeslice() const
|
|
{
|
|
return myMinCyclesPerTimeslice;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
uInt32 EmulationTiming::linesPerFrame() const
|
|
{
|
|
return myLinesPerFrame;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
uInt32 EmulationTiming::cyclesPerFrame() const
|
|
{
|
|
return myCyclesPerFrame;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
uInt32 EmulationTiming::cyclesPerSecond() const
|
|
{
|
|
return myCyclesPerSecond;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
uInt32 EmulationTiming::audioFragmentSize() const
|
|
{
|
|
return myAudioFragmentSize;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
uInt32 EmulationTiming::audioSampleRate() const
|
|
{
|
|
return myAudioSampleRate;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
uInt32 EmulationTiming::audioQueueCapacity() const
|
|
{
|
|
return myAudioQueueCapacity;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
uInt32 EmulationTiming::prebufferFragmentCount() const
|
|
{
|
|
return myPrebufferFragmentCount;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EmulationTiming::recalculate()
|
|
{
|
|
switch (myFrameLayout) {
|
|
case FrameLayout::ntsc:
|
|
myLinesPerFrame = 262;
|
|
break;
|
|
|
|
case FrameLayout::pal:
|
|
myLinesPerFrame = 312;
|
|
break;
|
|
|
|
default:
|
|
throw runtime_error("invalid frame layout");
|
|
}
|
|
|
|
switch (myConsoleTiming) {
|
|
case ConsoleTiming::ntsc:
|
|
myAudioSampleRate = uInt32(round(mySpeedFactor * 262 * 76 * 60) / 38);
|
|
break;
|
|
|
|
case ConsoleTiming::pal:
|
|
case ConsoleTiming::secam:
|
|
myAudioSampleRate = uInt32(round(mySpeedFactor * 312 * 76 * 50) / 38);
|
|
break;
|
|
|
|
default:
|
|
throw runtime_error("invalid console timing");
|
|
}
|
|
|
|
myCyclesPerSecond = myAudioSampleRate * 38;
|
|
|
|
myCyclesPerFrame = 76 * myLinesPerFrame;
|
|
myMaxCyclesPerTimeslice = uInt32(round(mySpeedFactor * myCyclesPerFrame * 2));
|
|
myMinCyclesPerTimeslice = uInt32(round(mySpeedFactor * myCyclesPerFrame / 2));
|
|
myAudioFragmentSize = uInt32(round(mySpeedFactor * AUDIO_HALF_FRAMES_PER_FRAGMENT * myLinesPerFrame));
|
|
|
|
myPrebufferFragmentCount = discreteDivCeil(
|
|
myPlaybackPeriod * myAudioSampleRate,
|
|
myAudioFragmentSize * myPlaybackRate
|
|
) + myAudioQueueHeadroom;
|
|
|
|
myAudioQueueCapacity = std::max(
|
|
myPrebufferFragmentCount,
|
|
discreteDivCeil(myMaxCyclesPerTimeslice * myAudioSampleRate, myAudioFragmentSize * myCyclesPerSecond)
|
|
) + myAudioQueueExtraFragments;
|
|
}
|