mirror of https://github.com/stella-emu/stella.git
Support variable emulation speed.
This commit is contained in:
parent
674e5f01c0
commit
2b23c81126
|
@ -58,6 +58,7 @@
|
||||||
"cstring": "cpp",
|
"cstring": "cpp",
|
||||||
"iostream": "cpp",
|
"iostream": "cpp",
|
||||||
"cstdint": "cpp",
|
"cstdint": "cpp",
|
||||||
"ostream": "cpp"
|
"ostream": "cpp",
|
||||||
|
"__memory": "cpp"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -563,7 +563,8 @@ void Console::initializeAudio()
|
||||||
.updatePlaybackRate(myOSystem.sound().getSampleRate())
|
.updatePlaybackRate(myOSystem.sound().getSampleRate())
|
||||||
.updatePlaybackPeriod(myOSystem.sound().getFragmentSize())
|
.updatePlaybackPeriod(myOSystem.sound().getFragmentSize())
|
||||||
.updateAudioQueueExtraFragments(myAudioSettings.bufferSize())
|
.updateAudioQueueExtraFragments(myAudioSettings.bufferSize())
|
||||||
.updateAudioQueueHeadroom(myAudioSettings.headroom());
|
.updateAudioQueueHeadroom(myAudioSettings.headroom())
|
||||||
|
.updateSpeedFactor(myOSystem.settings().getFloat("speed"));
|
||||||
|
|
||||||
(cout << "sample rate: " << myOSystem.sound().getSampleRate() << std::endl).flush();
|
(cout << "sample rate: " << myOSystem.sound().getSampleRate() << std::endl).flush();
|
||||||
(cout << "fragment size: " << myOSystem.sound().getFragmentSize() << std::endl).flush();
|
(cout << "fragment size: " << myOSystem.sound().getFragmentSize() << std::endl).flush();
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||||
//============================================================================
|
//============================================================================
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
#include "EmulationTiming.hxx"
|
#include "EmulationTiming.hxx"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -32,13 +34,18 @@ EmulationTiming::EmulationTiming(FrameLayout frameLayout) :
|
||||||
myPlaybackRate(44100),
|
myPlaybackRate(44100),
|
||||||
myPlaybackPeriod(512),
|
myPlaybackPeriod(512),
|
||||||
myAudioQueueExtraFragments(1),
|
myAudioQueueExtraFragments(1),
|
||||||
myAudioQueueHeadroom(2)
|
myAudioQueueHeadroom(2),
|
||||||
{}
|
mySpeedFactor(1)
|
||||||
|
{
|
||||||
|
recalculate();
|
||||||
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
EmulationTiming& EmulationTiming::updateFrameLayout(FrameLayout frameLayout)
|
EmulationTiming& EmulationTiming::updateFrameLayout(FrameLayout frameLayout)
|
||||||
{
|
{
|
||||||
myFrameLayout = frameLayout;
|
myFrameLayout = frameLayout;
|
||||||
|
recalculate();
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,6 +53,8 @@ EmulationTiming& EmulationTiming::updateFrameLayout(FrameLayout frameLayout)
|
||||||
EmulationTiming& EmulationTiming::updatePlaybackRate(uInt32 playbackRate)
|
EmulationTiming& EmulationTiming::updatePlaybackRate(uInt32 playbackRate)
|
||||||
{
|
{
|
||||||
myPlaybackRate = playbackRate;
|
myPlaybackRate = playbackRate;
|
||||||
|
recalculate();
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,6 +62,8 @@ EmulationTiming& EmulationTiming::updatePlaybackRate(uInt32 playbackRate)
|
||||||
EmulationTiming& EmulationTiming::updatePlaybackPeriod(uInt32 playbackPeriod)
|
EmulationTiming& EmulationTiming::updatePlaybackPeriod(uInt32 playbackPeriod)
|
||||||
{
|
{
|
||||||
myPlaybackPeriod = playbackPeriod;
|
myPlaybackPeriod = playbackPeriod;
|
||||||
|
recalculate();
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,6 +71,8 @@ EmulationTiming& EmulationTiming::updatePlaybackPeriod(uInt32 playbackPeriod)
|
||||||
EmulationTiming& EmulationTiming::updateAudioQueueExtraFragments(uInt32 audioQueueExtraFragments)
|
EmulationTiming& EmulationTiming::updateAudioQueueExtraFragments(uInt32 audioQueueExtraFragments)
|
||||||
{
|
{
|
||||||
myAudioQueueExtraFragments = audioQueueExtraFragments;
|
myAudioQueueExtraFragments = audioQueueExtraFragments;
|
||||||
|
recalculate();
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,85 +80,123 @@ EmulationTiming& EmulationTiming::updateAudioQueueExtraFragments(uInt32 audioQue
|
||||||
EmulationTiming& EmulationTiming::updateAudioQueueHeadroom(uInt32 audioQueueHeadroom)
|
EmulationTiming& EmulationTiming::updateAudioQueueHeadroom(uInt32 audioQueueHeadroom)
|
||||||
{
|
{
|
||||||
myAudioQueueHeadroom = audioQueueHeadroom;
|
myAudioQueueHeadroom = audioQueueHeadroom;
|
||||||
|
recalculate();
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
EmulationTiming& EmulationTiming::updateSpeedFactor(float speedFactor)
|
||||||
|
{
|
||||||
|
mySpeedFactor = speedFactor;
|
||||||
|
recalculate();
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
uInt32 EmulationTiming::maxCyclesPerTimeslice() const
|
uInt32 EmulationTiming::maxCyclesPerTimeslice() const
|
||||||
{
|
{
|
||||||
return 2 * cyclesPerFrame();
|
return myMaxCyclesPerTimeslice;
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
uInt32 EmulationTiming::minCyclesPerTimeslice() const
|
uInt32 EmulationTiming::minCyclesPerTimeslice() const
|
||||||
{
|
{
|
||||||
return cyclesPerFrame() / 2;
|
return myMinCyclesPerTimeslice;
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
uInt32 EmulationTiming::linesPerFrame() const
|
uInt32 EmulationTiming::linesPerFrame() const
|
||||||
{
|
{
|
||||||
switch (myFrameLayout) {
|
return myLinesPerFrame;
|
||||||
case FrameLayout::ntsc:
|
|
||||||
return 262;
|
|
||||||
|
|
||||||
case FrameLayout::pal:
|
|
||||||
return 312;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw runtime_error("invalid frame layout");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
uInt32 EmulationTiming::cyclesPerFrame() const
|
uInt32 EmulationTiming::cyclesPerFrame() const
|
||||||
{
|
{
|
||||||
return 76 * linesPerFrame();
|
return myCyclesPerFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
uInt32 EmulationTiming::framesPerSecond() const
|
uInt32 EmulationTiming::framesPerSecond() const
|
||||||
{
|
{
|
||||||
switch (myFrameLayout) {
|
return myFramesPerSecond;
|
||||||
case FrameLayout::ntsc:
|
|
||||||
return 60;
|
|
||||||
|
|
||||||
case FrameLayout::pal:
|
|
||||||
return 50;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw runtime_error("invalid frame layout");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
uInt32 EmulationTiming::cyclesPerSecond() const
|
uInt32 EmulationTiming::cyclesPerSecond() const
|
||||||
{
|
{
|
||||||
return cyclesPerFrame() * framesPerSecond();
|
return myCyclesPerSecond;
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
uInt32 EmulationTiming::audioFragmentSize() const
|
uInt32 EmulationTiming::audioFragmentSize() const
|
||||||
{
|
{
|
||||||
return AUDIO_HALF_FRAMES_PER_FRAGMENT * linesPerFrame();
|
return myAudioFragmentSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
uInt32 EmulationTiming::audioSampleRate() const
|
uInt32 EmulationTiming::audioSampleRate() const
|
||||||
{
|
{
|
||||||
return 2 * linesPerFrame() * framesPerSecond();
|
return myAudioSampleRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
uInt32 EmulationTiming::audioQueueCapacity() const
|
uInt32 EmulationTiming::audioQueueCapacity() const
|
||||||
{
|
{
|
||||||
uInt32 minCapacity = discreteDivCeil(maxCyclesPerTimeslice() * audioSampleRate(), audioFragmentSize() * cyclesPerSecond());
|
return myAudioQueueCapacity;
|
||||||
|
|
||||||
return std::max(prebufferFragmentCount(), minCapacity) + myAudioQueueExtraFragments;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
uInt32 EmulationTiming::prebufferFragmentCount() const
|
uInt32 EmulationTiming::prebufferFragmentCount() const
|
||||||
{
|
{
|
||||||
return discreteDivCeil(myPlaybackPeriod * audioSampleRate(), audioFragmentSize() * myPlaybackRate) + myAudioQueueHeadroom;
|
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 (myFrameLayout) {
|
||||||
|
case FrameLayout::ntsc:
|
||||||
|
myFramesPerSecond = round(mySpeedFactor * 60);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FrameLayout::pal:
|
||||||
|
myFramesPerSecond = round(mySpeedFactor * 50);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw runtime_error("invalid frame layout");
|
||||||
|
}
|
||||||
|
|
||||||
|
myCyclesPerFrame = 76 * myLinesPerFrame;
|
||||||
|
myMaxCyclesPerTimeslice = round(mySpeedFactor * myCyclesPerFrame * 2);
|
||||||
|
myMinCyclesPerTimeslice = round(mySpeedFactor * myCyclesPerFrame / 2);
|
||||||
|
myCyclesPerSecond = myCyclesPerFrame * myFramesPerSecond;
|
||||||
|
myAudioFragmentSize = round(mySpeedFactor * AUDIO_HALF_FRAMES_PER_FRAGMENT * myLinesPerFrame);
|
||||||
|
myAudioSampleRate = 2 * myLinesPerFrame * myFramesPerSecond;
|
||||||
|
|
||||||
|
myPrebufferFragmentCount = discreteDivCeil(
|
||||||
|
myPlaybackPeriod * myAudioSampleRate,
|
||||||
|
myAudioFragmentSize * myPlaybackRate
|
||||||
|
) + myAudioQueueHeadroom;
|
||||||
|
|
||||||
|
myAudioQueueCapacity = std::max(
|
||||||
|
myPrebufferFragmentCount,
|
||||||
|
discreteDivCeil(myMaxCyclesPerTimeslice * myAudioSampleRate, myAudioFragmentSize * myCyclesPerSecond)
|
||||||
|
) + myAudioQueueExtraFragments;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,8 @@ class EmulationTiming {
|
||||||
|
|
||||||
EmulationTiming& updateAudioQueueHeadroom(uInt32 audioQueueHeadroom);
|
EmulationTiming& updateAudioQueueHeadroom(uInt32 audioQueueHeadroom);
|
||||||
|
|
||||||
|
EmulationTiming& updateSpeedFactor(float speedFactor);
|
||||||
|
|
||||||
uInt32 maxCyclesPerTimeslice() const;
|
uInt32 maxCyclesPerTimeslice() const;
|
||||||
|
|
||||||
uInt32 minCyclesPerTimeslice() const;
|
uInt32 minCyclesPerTimeslice() const;
|
||||||
|
@ -56,17 +58,32 @@ class EmulationTiming {
|
||||||
|
|
||||||
uInt32 prebufferFragmentCount() const;
|
uInt32 prebufferFragmentCount() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void recalculate();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
FrameLayout myFrameLayout;
|
FrameLayout myFrameLayout;
|
||||||
|
|
||||||
uInt32 myPlaybackRate;
|
uInt32 myPlaybackRate;
|
||||||
|
|
||||||
uInt32 myPlaybackPeriod;
|
uInt32 myPlaybackPeriod;
|
||||||
|
|
||||||
uInt32 myAudioQueueExtraFragments;
|
uInt32 myAudioQueueExtraFragments;
|
||||||
uInt32 myAudioQueueHeadroom;
|
uInt32 myAudioQueueHeadroom;
|
||||||
|
|
||||||
|
uInt32 myMaxCyclesPerTimeslice;
|
||||||
|
uInt32 myMinCyclesPerTimeslice;
|
||||||
|
uInt32 myLinesPerFrame;
|
||||||
|
uInt32 myCyclesPerFrame;
|
||||||
|
uInt32 myFramesPerSecond;
|
||||||
|
uInt32 myCyclesPerSecond;
|
||||||
|
uInt32 myAudioFragmentSize;
|
||||||
|
uInt32 myAudioSampleRate;
|
||||||
|
uInt32 myAudioQueueCapacity;
|
||||||
|
uInt32 myPrebufferFragmentCount;
|
||||||
|
|
||||||
|
float mySpeedFactor;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
EmulationTiming(const EmulationTiming&) = delete;
|
EmulationTiming(const EmulationTiming&) = delete;
|
||||||
|
|
|
@ -35,7 +35,7 @@ Settings::Settings(OSystem& osystem)
|
||||||
{
|
{
|
||||||
// Video-related options
|
// Video-related options
|
||||||
setInternal("video", "");
|
setInternal("video", "");
|
||||||
setInternal("framerate", "0");
|
setInternal("speed", "1.0");
|
||||||
setInternal("vsync", "true");
|
setInternal("vsync", "true");
|
||||||
setInternal("fullscreen", "false");
|
setInternal("fullscreen", "false");
|
||||||
setInternal("center", "false");
|
setInternal("center", "false");
|
||||||
|
@ -303,6 +303,10 @@ void Settings::validate()
|
||||||
{
|
{
|
||||||
string s;
|
string s;
|
||||||
int i;
|
int i;
|
||||||
|
float f;
|
||||||
|
|
||||||
|
f = getFloat("speed");
|
||||||
|
if (f <= 0) setInternal("speed", "1.0");
|
||||||
|
|
||||||
s = getString("timing");
|
s = getString("timing");
|
||||||
if(s != "sleep" && s != "busy") setInternal("timing", "sleep");
|
if(s != "sleep" && s != "busy") setInternal("timing", "sleep");
|
||||||
|
@ -439,7 +443,7 @@ void Settings::usage() const
|
||||||
<< " -palette <standard| Use the specified color palette\n"
|
<< " -palette <standard| Use the specified color palette\n"
|
||||||
<< " z26|\n"
|
<< " z26|\n"
|
||||||
<< " user>\n"
|
<< " user>\n"
|
||||||
<< " -framerate <number> Display the given number of frames per second (0 to auto-calculate)\n"
|
<< " -speed <number> Run emulation at the given speed\n"
|
||||||
<< " -timing <sleep|busy> Use the given type of wait between frames\n"
|
<< " -timing <sleep|busy> Use the given type of wait between frames\n"
|
||||||
<< " -uimessages <1|0> Show onscreen UI messages for different events\n"
|
<< " -uimessages <1|0> Show onscreen UI messages for different events\n"
|
||||||
<< endl
|
<< endl
|
||||||
|
|
Loading…
Reference in New Issue