diff --git a/Changes.txt b/Changes.txt
index 7383fb052..1e0d4285c 100644
--- a/Changes.txt
+++ b/Changes.txt
@@ -25,6 +25,8 @@
* Added Joy2B+ controller support
+ * Enhanced Kid Vid support to play tape audio
+
* Added BUS bankswitching support for some older demos
* Fixed broken 7800 pause key support
diff --git a/docs/index.html b/docs/index.html
index e7acb1993..168d97e0e 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -21,7 +21,7 @@
A multi-platform Atari 2600 VCS emulator
- Release 6.7
+ Release 7.0
User's Guide
@@ -282,6 +282,7 @@
Emulates Spectravideo CompuMate system using your computer's keyboard,
including mapping of CompuMate 'Backspace', 'Space' and 'Enter' functionality to
to the actual keys on your keyboard
+ Emulates the Kid Vid Voice Module
Supports autodetection for most common controller types
Support for real Atari 2600 controllers using the
Stelladaptor and
@@ -1047,6 +1048,17 @@
+
+ Kid Vid Voice Module (can be remapped via Keyboard Controller)
+
+
+ Function | Key |
+ Start game #1 | 8 (same as Right Pad Button '1') |
+ Start game #2 | 9 (same as Right Pad Button '2') |
+ Start game #3 | 0 (same as Right Pad Button '3') |
+ Skip current song | P (same as Right Pad Button '6') |
+
+
CompuMate Controller (cannot be remapped)
@@ -1071,7 +1083,6 @@
[ | [ or Shift + 8 |
] | ] or Shift + 9 |
" | " (Shift + ') or Shift + 0 |
-
@@ -5023,7 +5034,12 @@ Ms Pac-Man (Stella extended codes):
CompuMate ¹ | Spectravideo CompuMate (if either left or right is set, CompuMate is used for both). |
Lightgun | Atari XG-1 compatible Light Gun. |
MindLink ¹ | MindLink controller. |
- KidVid | Kid Vid Voice Module, limited support (Right Keyboard controller buttons 1, 2 and 3 start the games, default mapping is 8, 9 and 0). |
+ KidVid | Kid Vid Voice Module.
+ Audio files can be downloaded e.g. from AtariAge.
+ Put unzipped .WAV files into Stella's base directory (see properties
+ or palette files below for details).
+ Note: Stella supports playing the games without audio files too.
+ |
QuadTari | QuadTari controller, limited support (see below). |
diff --git a/src/common/SoundSDL2.cxx b/src/common/SoundSDL2.cxx
index d23f1ad52..679a7e641 100644
--- a/src/common/SoundSDL2.cxx
+++ b/src/common/SoundSDL2.cxx
@@ -453,7 +453,7 @@ void SoundSDL2::stopWav()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 SoundSDL2::wavSize() const
{
- return myWavBuffer ? myWavLen /*SDL_GetQueuedAudioSize(myWavDevice)*/ : 0;
+ return myWavBuffer ? myWavLen : 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/src/common/StateManager.hxx b/src/common/StateManager.hxx
index e5b265877..6383accf2 100644
--- a/src/common/StateManager.hxx
+++ b/src/common/StateManager.hxx
@@ -18,7 +18,7 @@
#ifndef STATE_MANAGER_HXX
#define STATE_MANAGER_HXX
-#define STATE_HEADER "06070000state"
+#define STATE_HEADER "06070002state"
class OSystem;
class RewindManager;
diff --git a/src/emucore/KidVid.cxx b/src/emucore/KidVid.cxx
index adc87a140..0246b8691 100644
--- a/src/emucore/KidVid.cxx
+++ b/src/emucore/KidVid.cxx
@@ -29,9 +29,9 @@ KidVid::KidVid(Jack jack, const Event& event, const System& system,
{
// Right now, there are only two games that use the KidVid
if(romMd5 == "ee6665683ebdb539e89ba620981cb0f6")
- myGame = BBears; // Berenstain Bears
+ myGame = Game::BBears; // Berenstain Bears
else if(romMd5 == "a204cd4fb1944c86e800120706512a64")
- myGame = Smurfs; // Smurfs Save the Day
+ myGame = Game::Smurfs; // Smurfs Save the Day
else
myEnabled = false;
}
@@ -58,12 +58,29 @@ void KidVid::update()
if(!myEnabled)
return;
- if(myEvent.get(Event::ConsoleReset))
+ if(myContinueSong)
+ {
+ // Continue playing song after state load
+ const uInt8 temp = ourSongPositions[mySongPointer - 1] & 0x7f;
+ const uInt32 songLength = ourSongStart[temp + 1] - ourSongStart[temp];
+
+ // Play the remaining WAV file
+ const string& fileName = myBaseDir + ((temp < 10) ? "KVSHARED.WAV" : getFileName());
+ mySound.playWav(fileName, ourSongStart[temp] + (songLength - mySongLength), mySongLength);
+
+ myContinueSong = false;
+ }
+
+ if(myGame == Game::Smurfs && myEvent.get(Event::ConsoleReset)) // Reset does not work with BBears!
{
myTape = 0; // rewind Kid Vid tape
myFilesFound = mySongPlaying = false;
mySound.stopWav();
}
+ else if(myEvent.get(Event::RightKeyboard6))
+ {
+ mySound.stopWav();
+ }
if(!myTape)
{
if(myEvent.get(Event::RightKeyboard1))
@@ -71,10 +88,10 @@ void KidVid::update()
else if(myEvent.get(Event::RightKeyboard2))
myTape = 3;
else if(myEvent.get(Event::RightKeyboard3))
- myTape = myGame == BBears ? 4 : 1; // Berenstain Bears or Smurfs Save The Day?
+ myTape = myGame == Game::BBears ? 4 : 1; // Berenstain Bears or Smurfs Save The Day?
if(myTape)
{
- myIdx = myGame == BBears ? NumBlockBits : 0;
+ myIdx = myGame == Game::BBears ? NumBlockBits : 0; // KVData48/KVData44
myBlockIdx = NumBlockBits;
myBlock = 0;
openSampleFiles();
@@ -94,17 +111,17 @@ void KidVid::update()
if(!myBlockIdx)
{
if(!myBlock)
- myIdx = ((myTape * 6) + 12 - NumBlocks) * 8; //KVData00-KVData=12
+ myIdx = ((myTape * 6) + 12 - NumBlocks) * 8; // KVData00 - ourData = 12 (2 * 6)
else
{
- const uInt32 lastBlock = myGame == Smurfs
+ const uInt32 lastBlock = myGame == Game::Smurfs
? ourBlocks[myTape - 1]
: ourBlocks[myTape + 2 - 1];
if(myBlock >= lastBlock)
- myIdx = 42 * 8; //KVData80-KVData=42
+ myIdx = 42 * 8; // KVData80 - ourData = 42 (7 * 6)
else
{
- myIdx = 36 * 8;//KVPause-KVData=36
+ myIdx = 36 * 8; // KVPause - ourData = 36 (6 * 6)
setNextSong();
}
}
@@ -117,9 +134,10 @@ void KidVid::update()
{
if(mySongPlaying)
{
- myTapeBusy = (mySound.wavSize() > 262 * 48) || !myBeep;
+ mySongLength = mySound.wavSize();
+ myTapeBusy = (mySongLength > 262 * 48) || !myBeep;
// Check for end of played sample
- if(mySound.wavSize() == 0)
+ if(mySongLength == 0)
{
mySongPlaying = false;
myTapeBusy = !myBeep;
@@ -139,12 +157,62 @@ void KidVid::update()
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-void KidVid::openSampleFiles()
+bool KidVid::save(Serializer& out) const
+{
+ // Save WAV player state
+ out.putInt(myTape);
+ out.putBool(myFilesFound);
+ out.putBool(myTapeBusy);
+ out.putBool(myBeep);
+ out.putBool(mySongPlaying);
+ out.putInt(mySongPointer);
+ out.putInt(mySongLength);
+ // Save tape input simulation state
+ out.putInt(myIdx);
+ out.putInt(myBlockIdx);
+ out.putInt(myBlock);
+
+ return Controller::save(out);
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+bool KidVid::load(Serializer& in)
+{
+ // Load WAV player state
+ myTape = in.getInt();
+ myFilesFound = in.getBool();
+ myTapeBusy = in.getBool();
+ myBeep = in.getBool();
+ mySongPlaying = in.getBool();
+ mySongPointer = in.getInt();
+ mySongLength = in.getInt();
+ // Load tape input simulation state
+ myIdx = in.getInt();
+ myBlockIdx = in.getInt();
+ myBlock = in.getInt();
+
+ myContinueSong = myFilesFound && mySongPlaying;
+
+ return Controller::load(in);
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+const char* KidVid::getFileName() const
{
static constexpr const char* fileNames[6] = {
"KVS3.WAV", "KVS1.WAV", "KVS2.WAV",
"KVB3.WAV", "KVB1.WAV", "KVB2.WAV"
};
+
+ int i = myGame == Game::Smurfs ? myTape - 1 : myTape + 2;
+ if(myTape == 4) i = 3;
+
+ return fileNames[i];
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void KidVid::openSampleFiles()
+{
static constexpr uInt32 firstSongPointer[6] = {
44 + 38,
0,
@@ -156,13 +224,11 @@ void KidVid::openSampleFiles()
if(!myFilesFound)
{
- int i = myGame == Smurfs ? myTape - 1 : myTape + 2;
+ int i = myGame == Game::Smurfs ? myTape - 1 : myTape + 2;
if(myTape == 4) i = 3;
- mySampleFile = myBaseDir + fileNames[i];
-
std::ifstream f1, f2;
- f1.open(mySampleFile);
+ f1.open(myBaseDir + getFileName());
f2.open(myBaseDir + "KVSHARED.WAV");
myFilesFound = f1.is_open() && f2.is_open();
@@ -170,7 +236,7 @@ void KidVid::openSampleFiles()
#ifdef DEBUG_BUILD
if(myFilesFound)
cerr << endl
- << "found file: " << fileNames[i] << endl
+ << "found file: " << getFileName() << endl
<< "found file: " << "KVSHARED.WAV" << endl;
#endif
@@ -191,8 +257,8 @@ void KidVid::setNextSong()
mySongLength = ourSongStart[temp + 1] - ourSongStart[temp];
// Play the WAV file
- const string& fileName = (temp < 10) ? myBaseDir + "KVSHARED.WAV" : mySampleFile;
- mySound.playWav(fileName, ourSongStart[temp], mySongLength);
+ const string& fileName = (temp < 10) ? "KVSHARED.WAV" : getFileName();
+ mySound.playWav(myBaseDir + fileName, ourSongStart[temp], mySongLength);
#ifdef DEBUG_BUILD
cerr << fileName << ": " << (ourSongPositions[mySongPointer] & 0x7f) << endl;
#endif
@@ -216,7 +282,7 @@ const std::array KidVid::ourBlocks = {
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const std::array KidVid::ourData = {
-/* KVData44 */
+/* KVData44, Smurfs */
0x7b, // 0111 1011b ; (1)0
0x1e, // 0001 1110b ; 1
0xc6, // 1100 0110b ; 00
@@ -224,7 +290,7 @@ const std::array KidVid::ourData = {
0xec, // 1110 1100b ; 0
0x60, // 0110 0000b ; 0+
-/* KVData48 */
+/* KVData48, BBears */
0x7b, // 0111 1011b ; (1)0
0x1e, // 0001 1110b ; 1
0xc6, // 1100 0110b ; 00
diff --git a/src/emucore/KidVid.hxx b/src/emucore/KidVid.hxx
index 57743ce64..5f65711f6 100644
--- a/src/emucore/KidVid.hxx
+++ b/src/emucore/KidVid.hxx
@@ -67,12 +67,31 @@ class KidVid : public Controller
*/
void update() override;
+ /**
+ Saves the current state of this controller to the given Serializer.
+
+ @param out The serializer device to save to.
+ @return The result of the save. True on success, false on failure.
+ */
+ bool save(Serializer& out) const override;
+
+ /**
+ Loads the current state of this controller from the given Serializer.
+
+ @param in The serializer device to load from.
+ @return The result of the load. True on success, false on failure.
+ */
+ bool load(Serializer& in) override;
+
/**
Returns the name of this controller.
*/
string name() const override { return "KidVid"; }
private:
+ // Get name of the current sample file
+ const char* getFileName() const;
+
// Open/close a WAV sample file
void openSampleFiles();
@@ -80,36 +99,38 @@ class KidVid : public Controller
void setNextSong();
private:
+ enum class Game {
+ Smurfs,
+ BBears
+ };
static constexpr uInt32
- Smurfs = 0x44,
- BBears = 0x48,
- NumBlocks = 6, // number of bytes / block
- NumBlockBits = NumBlocks*8, // number of bits / block
- SongPosSize = 44+38+42+62+80+62,
+ NumBlocks = 6, // number of bytes / block
+ NumBlockBits = NumBlocks * 8, // number of bits / block
+ SongPosSize = 44 + 38 + 42 + 62 + 80 + 62,
SongStartSize = 104
;
// Whether the KidVid device is enabled (only for games that it
- // supports, and if it's plugged into the right port
+ // supports, and if it's plugged into the right port)
bool myEnabled{false};
string myBaseDir;
Sound& mySound;
- // Path and name of the current sample file
- string mySampleFile;
// Indicates if the sample files have been found
bool myFilesFound{false};
- uInt32 mySongPointer{0};
-
// Is the tape currently 'busy' / in use?
bool myTapeBusy{false};
bool mySongPlaying{false};
+ // Continue song after loading state?
+ bool myContinueSong{false};
+ uInt32 mySongPointer{0};
uInt32 mySongLength{0};
bool myBeep{false};
- uInt32 myGame{0}, myTape{0};
+ Game myGame{Game::Smurfs};
+ uInt32 myTape{0};
uInt32 myIdx{0}, myBlock{0}, myBlockIdx{0};
// Number of blocks and data on tape