mirror of https://github.com/stella-emu/stella.git
added sound to Time Machine playback
fixed playback speed updated docs
This commit is contained in:
parent
d38593a821
commit
ee8734ce14
|
@ -28,6 +28,8 @@
|
|||
|
||||
* Added dynamic tooltips to most debugger items.
|
||||
|
||||
* Added sound to Time Machine playback.
|
||||
|
||||
* Increased sample size for CDFJ+.
|
||||
|
||||
* Fixed autofire bug for trackball controllers.
|
||||
|
|
|
@ -1828,8 +1828,8 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td>Playback the <a href="#TimeMachine"><b>Time Machine</b></a> from current state (without sound, from the TM dialog only)</td>
|
||||
<td>Shift</td>
|
||||
<td>Shift</td>
|
||||
<td>Space</td>
|
||||
<td>Space</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Start/Stop playback (exist/enters the <a href="#TimeMachine"><b>Time Machine</b></a> dialog)</td>
|
||||
|
@ -2259,10 +2259,10 @@
|
|||
<tr><td>Current time</td><td>Shows the time of the currently selected state,
|
||||
relative to the first one</td></tr>
|
||||
<tr><td>'Start/Stop' button</td><td>Starts or stops the Time Machine</td></tr>
|
||||
<tr><td>'Exit' button</td><td>Exits the dialog and continues emulation, starting at current state</td></tr>
|
||||
<tr><td>'Exit' button</td><td>Exits the dialog and continues emulation from the current state</td></tr>
|
||||
<tr><td>'Rewind All' button</td><td>Navigates back to the begin of the timeline</td></tr>
|
||||
<tr><td>'Rewind One' button</td><td>Navigates back by one state</td></tr>
|
||||
<tr><td>'Playback' button</td><td>Starts playback, starting at the current state (with sound disabled)</td></tr>
|
||||
<tr><td>'Playback' button</td><td>Starts playback from the current state</td></tr>
|
||||
<tr><td>'Unwind One' button</td><td>Navigates forward by one state</td></tr>
|
||||
<tr><td>'Unwind All' button</td><td>Navigates forward to the end of the timeline</td></tr>
|
||||
<tr><td>'Save All' button</td><td>Saves all Time Machine states to disk</td></tr>
|
||||
|
@ -2288,16 +2288,18 @@
|
|||
<a href="#HighScoreGames">High Scores - supported games</a></b>) this has
|
||||
been done already.</p>
|
||||
|
||||
<p>To save a score, the High Score dialog can be opened by pressing 'Insert'
|
||||
any time while a game is played. It will provide the current variation and
|
||||
score and allow the user to add this as a new high score. Of course this
|
||||
makes most sense when a game is over. Note: In multiplayer games, only the
|
||||
score of the first player can be saved.</p>
|
||||
|
||||
<p><b>High Scores</b> dialog:</p>
|
||||
<table border="5" cellpadding="2" frame="box" rules="none">
|
||||
<tr>
|
||||
<td><img src="graphics/highscores.png"></td>
|
||||
|
||||
<p>This dialog can be opened by pressing 'Insert' any time while a game is
|
||||
played. It will provide the current variation and score and allow the user
|
||||
to add this as a new high score. Of course this makes most sense when a game
|
||||
is over. Note: In multiplayer games, only the score of the first player can be
|
||||
saved.</p>
|
||||
<p>The dialog items are explained in the following two tables.</p>
|
||||
|
||||
<td> </td>
|
||||
<td valign="top">
|
||||
<table border="1" cellpadding="4">
|
||||
<tr><th>Item</th><th>Description</th></tr>
|
||||
<tr><td>Top row</td><td>Displays the current game's name.</td></tr>
|
||||
|
@ -2325,6 +2327,11 @@
|
|||
</td></tr>
|
||||
<tr><td>Cancel</td><td>Closes the dialog without saving.</td></tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p>For details how to configure high scores definintions for a game see
|
||||
<a href="#HighScoreProps"><b>High Scores Properties</b></a></h2>.</p>
|
||||
</blockquote></br>
|
||||
|
@ -3511,7 +3518,7 @@
|
|||
<p><b>User Interface Settings</b> dialog (2 tabs):</p>
|
||||
<table border="5" cellpadding="2" frame="box" rules="none">
|
||||
<tr>
|
||||
<td><img src="graphics/options_misc.png"><br><br></td>
|
||||
<td><img src="graphics/options_misc.png"></td>
|
||||
<td> </td>
|
||||
<td valign="top">
|
||||
<table border="1" cellpadding="4">
|
||||
|
@ -3530,12 +3537,13 @@
|
|||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="graphics/options_misc_classic.png"></td>
|
||||
<td><img src="graphics/options_misc_dark.png"></td>
|
||||
<td> </td>
|
||||
<td><img src="graphics/options_misc_light.png"></td>
|
||||
<td><img src="graphics/options_misc_classic.png"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="graphics/options_misc_dark.png"></td>
|
||||
<td colspan="3"><center><img src="graphics/options_misc_light.png"></center></td>
|
||||
|
||||
</tr>
|
||||
</table>
|
||||
<br>
|
||||
|
|
|
@ -422,33 +422,28 @@ string HighScoresManager::md5Props() const
|
|||
bool HighScoresManager::scoreInvert() const
|
||||
{
|
||||
json jprops;
|
||||
|
||||
return scoreInvert(properties(jprops));
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Int32 HighScoresManager::special() const
|
||||
{
|
||||
if(!myOSystem.hasConsole())
|
||||
return NO_VALUE;
|
||||
|
||||
json jprops;
|
||||
uInt16 addr = specialAddress(properties(jprops));
|
||||
|
||||
if (addr == DEFAULT_ADDRESS)
|
||||
return NO_VALUE;
|
||||
|
||||
return special(addr, specialBCD(jprops), specialZeroBased(jprops));
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Int32 HighScoresManager::special(uInt16 addr, bool varBCD, bool zeroBased) const
|
||||
{
|
||||
if (!myOSystem.hasConsole())
|
||||
return NO_VALUE;
|
||||
|
||||
Int32 var = peek(addr);
|
||||
|
||||
if (varBCD)
|
||||
if(specialBCD(jprops))
|
||||
var = fromBCD(var);
|
||||
|
||||
var += zeroBased ? 1 : 0;
|
||||
var += specialZeroBased(jprops) ? 1 : 0;
|
||||
|
||||
return var;
|
||||
}
|
||||
|
|
|
@ -131,9 +131,6 @@ class HighScoresManager
|
|||
*/
|
||||
uInt32 numAddrBytes(Int32 digits, Int32 trailing) const;
|
||||
|
||||
|
||||
Int32 special(uInt16 addr, bool varBCD, bool zeroBased) const;
|
||||
|
||||
// Retrieve current values (using game's properties)
|
||||
Int32 numVariations() const;
|
||||
const string specialLabel() const;
|
||||
|
|
|
@ -2388,7 +2388,6 @@ void EventHandler::enterPlayBackMode()
|
|||
{
|
||||
#ifdef GUI_SUPPORT
|
||||
setState(EventHandlerState::PLAYBACK);
|
||||
myOSystem.sound().mute(true); // sound does not work in playback mode
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "Settings.hxx"
|
||||
#include "TIA.hxx"
|
||||
#include "Sound.hxx"
|
||||
#include "AudioSettings.hxx"
|
||||
#include "MediaFactory.hxx"
|
||||
|
||||
#include "FBSurface.hxx"
|
||||
|
@ -440,23 +441,24 @@ void FrameBuffer::update(UpdateMode mode)
|
|||
case EventHandlerState::PLAYBACK:
|
||||
{
|
||||
static Int32 frames = 0;
|
||||
RewindManager& r = myOSystem.state().rewindManager();
|
||||
bool success = true;
|
||||
Int64 frameCycles = 76 * std::max<Int32>(myOSystem.console().tia().scanlinesLastFrame(), 240);
|
||||
|
||||
if(--frames <= 0)
|
||||
{
|
||||
r.unwindStates(1);
|
||||
// get time between current and next state
|
||||
uInt64 startCycles = r.getCurrentCycles();
|
||||
RewindManager& r = myOSystem.state().rewindManager();
|
||||
uInt64 prevCycles = r.getCurrentCycles();
|
||||
|
||||
success = r.unwindStates(1);
|
||||
// display larger state gaps faster
|
||||
frames = std::sqrt((myOSystem.console().tia().cycles() - startCycles) / frameCycles);
|
||||
|
||||
if(success)
|
||||
r.rewindStates(1);
|
||||
Int64 frameCycles = 76 * std::max<Int32>(myOSystem.console().tia().scanlinesLastFrame(), 240);
|
||||
|
||||
// Use time between previous and current state
|
||||
// to playback larger state gaps faster
|
||||
frames = std::sqrt((r.getCurrentCycles() - prevCycles) / frameCycles);
|
||||
|
||||
// TODO: test and verify with CS
|
||||
//myOSystem.sound().mute(uInt32(frames) > myOSystem.audioSettings().headroom() / 2 + 1);
|
||||
}
|
||||
|
||||
redraw |= success;
|
||||
if(redraw)
|
||||
myTIASurface->render();
|
||||
|
|
|
@ -839,7 +839,15 @@ void OSystem::mainLoop()
|
|||
if (myEventHandler->state() == EventHandlerState::EMULATION)
|
||||
// Dispatch emulation and render frame (if applicable)
|
||||
timesliceSeconds = dispatchEmulation(emulationWorker);
|
||||
else {
|
||||
else if(myEventHandler->state() == EventHandlerState::PLAYBACK)
|
||||
{
|
||||
// Playback at emulation speed
|
||||
timesliceSeconds = static_cast<double>(myConsole->tia().scanlinesLastFrame() * 76) /
|
||||
static_cast<double>(myConsole->emulationTiming().cyclesPerSecond());
|
||||
myFrameBuffer->update();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Render the GUI with 60 Hz in all other modes
|
||||
timesliceSeconds = 1. / 60.;
|
||||
myFrameBuffer->update();
|
||||
|
|
|
@ -86,16 +86,26 @@ void Audio::phase1()
|
|||
uInt8 sample0 = myChannel0.phase1();
|
||||
uInt8 sample1 = myChannel1.phase1();
|
||||
|
||||
if (!myAudioQueue) return;
|
||||
addSample(sample0, sample1);
|
||||
#ifdef GUI_SUPPORT
|
||||
mySamples.push_back(sample0 | (sample1 << 4));
|
||||
#endif
|
||||
}
|
||||
|
||||
if (myAudioQueue->isStereo()) {
|
||||
myCurrentFragment[2*mySampleIndex] = myMixingTableIndividual[sample0];
|
||||
myCurrentFragment[2*mySampleIndex + 1] = myMixingTableIndividual[sample1];
|
||||
} else {
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Audio::addSample(uInt8 sample0, uInt8 sample1)
|
||||
{
|
||||
if(!myAudioQueue) return;
|
||||
|
||||
if(myAudioQueue->isStereo()) {
|
||||
myCurrentFragment[2 * mySampleIndex] = myMixingTableIndividual[sample0];
|
||||
myCurrentFragment[2 * mySampleIndex + 1] = myMixingTableIndividual[sample1];
|
||||
}
|
||||
else {
|
||||
myCurrentFragment[mySampleIndex] = myMixingTableSum[sample0 + sample1];
|
||||
}
|
||||
|
||||
if (++mySampleIndex == myAudioQueue->fragmentSize()) {
|
||||
if(++mySampleIndex == myAudioQueue->fragmentSize()) {
|
||||
mySampleIndex = 0;
|
||||
myCurrentFragment = myAudioQueue->enqueue(myCurrentFragment);
|
||||
}
|
||||
|
@ -125,6 +135,16 @@ bool Audio::save(Serializer& out) const
|
|||
|
||||
if (!myChannel0.save(out)) return false;
|
||||
if (!myChannel1.save(out)) return false;
|
||||
#ifdef GUI_SUPPORT
|
||||
out.putLong(uInt64(mySamples.size()));
|
||||
out.putByteArray(mySamples.data(), mySamples.size());
|
||||
|
||||
// TODO: check if this improves sound of playback for larger state gaps
|
||||
//out.putInt(mySampleIndex);
|
||||
//out.putShortArray((uInt16*)myCurrentFragment, myAudioQueue->fragmentSize());
|
||||
|
||||
mySamples.clear();
|
||||
#endif
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
|
@ -144,6 +164,24 @@ bool Audio::load(Serializer& in)
|
|||
|
||||
if (!myChannel0.load(in)) return false;
|
||||
if (!myChannel1.load(in)) return false;
|
||||
#ifdef GUI_SUPPORT
|
||||
uInt64 sampleSize = in.getLong();
|
||||
unique_ptr<uInt8[]> samples = make_unique<uInt8[]>(sampleSize);
|
||||
in.getByteArray(samples.get(), sampleSize);
|
||||
|
||||
//mySampleIndex = in.getInt();
|
||||
//in.getShortArray((uInt16*)myCurrentFragment, myAudioQueue->fragmentSize());
|
||||
|
||||
// Feed all loaded samples into the audio queue
|
||||
for(size_t i = 0; i < sampleSize; i++)
|
||||
{
|
||||
uInt8 sample = samples[i];
|
||||
uInt8 sample0 = sample & 0x0f;
|
||||
uInt8 sample1 = sample >> 4;
|
||||
|
||||
addSample(sample0, sample1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
|
|
|
@ -47,6 +47,7 @@ class Audio : public Serializable
|
|||
|
||||
private:
|
||||
void phase1();
|
||||
void addSample(uInt8 sample0, uInt8 sample1);
|
||||
|
||||
private:
|
||||
shared_ptr<AudioQueue> myAudioQueue;
|
||||
|
@ -61,6 +62,9 @@ class Audio : public Serializable
|
|||
|
||||
Int16* myCurrentFragment{nullptr};
|
||||
uInt32 mySampleIndex{0};
|
||||
#ifdef GUI_SUPPORT
|
||||
mutable ByteArray mySamples;
|
||||
#endif
|
||||
|
||||
private:
|
||||
Audio(const Audio&) = delete;
|
||||
|
|
|
@ -53,6 +53,7 @@ WhatsNewDialog::WhatsNewDialog(OSystem& osystem, DialogContainer& parent, const
|
|||
add(ypos, "added wildcard support to launcher dialog filter");
|
||||
add(ypos, "added option to search subdirectories in launcher");
|
||||
add(ypos, "added tooltips to many UI items");
|
||||
add(ypos, "added sound to Time Machine playback");
|
||||
add(ypos, "increased sample size for CDFJ+");
|
||||
add(ypos, ELLIPSIS + " (for a complete list see 'docs/Changes.txt')");
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue