2018-05-04 22:47:48 +00:00
|
|
|
//============================================================================
|
|
|
|
//
|
|
|
|
// 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-2018 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 "EmulationTiming.hxx"
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
constexpr uInt32 AUDIO_HALF_FRAMES_PER_FRAGMENT = 1;
|
2018-05-12 14:21:58 +00:00
|
|
|
constexpr uInt32 QUEUE_CAPACITY_EXTRA_FRAGMENTS = 1;
|
|
|
|
constexpr uInt32 PREBUFFER_EXTRA_FRAGMENT_COUNT = 2;
|
|
|
|
|
|
|
|
uInt32 discreteDivCeil(uInt32 n, uInt32 d)
|
|
|
|
{
|
|
|
|
return n / d + ((n % d == 0) ? 0 : 1);
|
|
|
|
}
|
2018-05-04 22:47:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2018-05-06 21:45:21 +00:00
|
|
|
EmulationTiming::EmulationTiming(FrameLayout frameLayout) :
|
|
|
|
myFrameLayout(frameLayout),
|
|
|
|
myPlaybackRate(44100),
|
|
|
|
myPlaybackPeriod(512)
|
|
|
|
{}
|
2018-05-04 22:47:48 +00:00
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2018-05-06 21:45:21 +00:00
|
|
|
void EmulationTiming::updateFrameLayout(FrameLayout frameLayout)
|
|
|
|
{
|
|
|
|
myFrameLayout = frameLayout;
|
2018-05-04 22:47:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2018-05-06 21:45:21 +00:00
|
|
|
void EmulationTiming::updatePlaybackRate(uInt32 playbackRate)
|
|
|
|
{
|
|
|
|
myPlaybackRate = playbackRate;
|
2018-05-04 22:47:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2018-05-06 21:45:21 +00:00
|
|
|
void EmulationTiming::updatePlaybackPeriod(uInt32 playbackPeriod)
|
|
|
|
{
|
|
|
|
myPlaybackPeriod = playbackPeriod;
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
uInt32 EmulationTiming::maxCyclesPerTimeslice() const
|
|
|
|
{
|
|
|
|
return 2 * cyclesPerFrame();
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
uInt32 EmulationTiming::minCyclesPerTimeslice() const
|
|
|
|
{
|
2018-05-04 22:47:48 +00:00
|
|
|
return cyclesPerFrame() / 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2018-05-06 21:45:21 +00:00
|
|
|
uInt32 EmulationTiming::linesPerFrame() const
|
|
|
|
{
|
|
|
|
switch (myFrameLayout) {
|
2018-05-04 22:47:48 +00:00
|
|
|
case FrameLayout::ntsc:
|
|
|
|
return 262;
|
|
|
|
|
|
|
|
case FrameLayout::pal:
|
|
|
|
return 312;
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw runtime_error("invalid frame layout");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2018-05-06 21:45:21 +00:00
|
|
|
uInt32 EmulationTiming::cyclesPerFrame() const
|
|
|
|
{
|
2018-05-04 22:47:48 +00:00
|
|
|
return 76 * linesPerFrame();
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2018-05-06 21:45:21 +00:00
|
|
|
uInt32 EmulationTiming::framesPerSecond() const
|
|
|
|
{
|
|
|
|
switch (myFrameLayout) {
|
2018-05-04 22:47:48 +00:00
|
|
|
case FrameLayout::ntsc:
|
|
|
|
return 60;
|
|
|
|
|
|
|
|
case FrameLayout::pal:
|
|
|
|
return 50;
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw runtime_error("invalid frame layout");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2018-05-06 21:45:21 +00:00
|
|
|
uInt32 EmulationTiming::cyclesPerSecond() const
|
|
|
|
{
|
2018-05-04 22:47:48 +00:00
|
|
|
return cyclesPerFrame() * framesPerSecond();
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2018-05-06 21:45:21 +00:00
|
|
|
uInt32 EmulationTiming::audioFragmentSize() const
|
|
|
|
{
|
2018-05-04 22:47:48 +00:00
|
|
|
return AUDIO_HALF_FRAMES_PER_FRAGMENT * linesPerFrame();
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2018-05-06 21:45:21 +00:00
|
|
|
uInt32 EmulationTiming::audioSampleRate() const
|
|
|
|
{
|
2018-05-04 22:47:48 +00:00
|
|
|
return 2 * linesPerFrame() * framesPerSecond();
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2018-05-06 21:45:21 +00:00
|
|
|
uInt32 EmulationTiming::audioQueueCapacity() const
|
|
|
|
{
|
2018-05-12 14:21:58 +00:00
|
|
|
uInt32 minCapacity = discreteDivCeil(maxCyclesPerTimeslice() * audioSampleRate(), audioFragmentSize() * cyclesPerSecond());
|
2018-05-04 22:47:48 +00:00
|
|
|
|
2018-05-12 14:21:58 +00:00
|
|
|
return std::max(prebufferFragmentCount(), minCapacity) + QUEUE_CAPACITY_EXTRA_FRAGMENTS;
|
2018-05-04 22:47:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2018-05-06 21:45:21 +00:00
|
|
|
uInt32 EmulationTiming::prebufferFragmentCount() const
|
|
|
|
{
|
2018-05-12 14:21:58 +00:00
|
|
|
return discreteDivCeil(myPlaybackPeriod * audioSampleRate(), audioFragmentSize() * myPlaybackRate) + PREBUFFER_EXTRA_FRAGMENT_COUNT;
|
2018-05-04 22:47:48 +00:00
|
|
|
}
|