added WAV playing and adapted KidVid code accordingly

This commit is contained in:
Thomas Jentzsch 2022-09-03 16:54:33 +02:00
parent 162921b9f3
commit ba6b32aa21
6 changed files with 200 additions and 136 deletions

View File

@ -72,6 +72,10 @@ SoundSDL2::~SoundSDL2()
{ {
ASSERT_MAIN_THREAD; ASSERT_MAIN_THREAD;
stopWav();
if(myWavDevice)
SDL_CloseAudioDevice(myWavDevice);
if (!myIsInitializedFlag) return; if (!myIsInitializedFlag) return;
SDL_CloseAudioDevice(myDevice); SDL_CloseAudioDevice(myDevice);
@ -194,6 +198,7 @@ void SoundSDL2::open(shared_ptr<AudioQueue> audioQueue,
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL2::close() void SoundSDL2::close()
{ {
stopWav();
if(!myIsInitializedFlag) return; if(!myIsInitializedFlag) return;
mute(true); mute(true);
@ -209,6 +214,8 @@ bool SoundSDL2::mute(bool state)
const bool oldstate = SDL_GetAudioDeviceStatus(myDevice) == SDL_AUDIO_PAUSED; const bool oldstate = SDL_GetAudioDeviceStatus(myDevice) == SDL_AUDIO_PAUSED;
if(myIsInitializedFlag) if(myIsInitializedFlag)
SDL_PauseAudioDevice(myDevice, state ? 1 : 0); SDL_PauseAudioDevice(myDevice, state ? 1 : 0);
if(myWavDevice)
SDL_PauseAudioDevice(myWavDevice, state ? 1 : 0);
return oldstate; return oldstate;
} }
@ -395,4 +402,57 @@ void SoundSDL2::callback(void* udata, uInt8* stream, int len)
SDL_memset(stream, 0, len); SDL_memset(stream, 0, len);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool SoundSDL2::playWav(const char* fileName, uInt32 position, uInt32 length)
{
// ToDos:
// - volume (requires callback using SDL_MixAudio)
// - (un)mute
SDL_AudioSpec wavSpec;
uInt32 wavLength;
// Stop any playing WAVs
stopWav();
// Load WAV file
SDL_AudioSpec* result = SDL_LoadWAV(fileName, &wavSpec, &myWavBuffer, &wavLength);
if(result == NULL || position > wavLength)
return false;
length = length
? std::min(length, wavLength - position)
: wavLength;
// Open audio device
const char* device = myDeviceId ? myDevices.at(myDeviceId).first.c_str() : nullptr;
myWavDevice = SDL_OpenAudioDevice(device, 0, &wavSpec, NULL, 0);
if(!myWavDevice)
return false;
// Play audio
int success = SDL_QueueAudio(myWavDevice, myWavBuffer + position, length);
SDL_PauseAudioDevice(myWavDevice, 0);
return success == 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL2::stopWav()
{
if(myWavBuffer)
{
// Clean up
SDL_CloseAudioDevice(myWavDevice);
SDL_FreeWAV(myWavBuffer);
myWavBuffer = nullptr;
}
}
uInt32 SoundSDL2::wavSize() const
{
return myWavBuffer ? SDL_GetQueuedAudioSize(myWavDevice) : 0;
}
#endif // SOUND_SUPPORT #endif // SOUND_SUPPORT

View File

@ -107,6 +107,29 @@ class SoundSDL2 : public Sound
*/ */
string about() const override; string about() const override;
/**
Play a WAV file.
@param fileName The name of the WAV file
@param position The position to start playing
@param length The played length
@return True, if the WAV file can be played
*/
bool playWav(const char* fileName, uInt32 position = 0, uInt32 length = 0) override;
/**
Stop any currently playing WAV file.
*/
void stopWav() override;
/**
Get the size of the WAV file which remains to be played.
@return The remaining number of bytes
*/
uInt32 wavSize() const override;
protected: protected:
/** /**
This method is called to query the audio devices. This method is called to query the audio devices.
@ -161,6 +184,9 @@ class SoundSDL2 : public Sound
AudioSettings& myAudioSettings; AudioSettings& myAudioSettings;
SDL_AudioDeviceID myWavDevice{0};
uInt8* myWavBuffer{nullptr};
string myAboutString; string myAboutString;
private: private:

View File

@ -1028,7 +1028,8 @@ unique_ptr<Controller> Console::getControllerPort(const Controller::Type type,
break; break;
case Controller::Type::KidVid: case Controller::Type::KidVid:
controller = make_unique<KidVid>(port, myEvent, *mySystem, myOSystem.baseDir().getPath(), romMd5); controller = make_unique<KidVid>(port, myEvent, *mySystem, myOSystem.baseDir().getPath(),
myOSystem.sound(), romMd5);
break; break;
case Controller::Type::MindLink: case Controller::Type::MindLink:

View File

@ -16,14 +16,16 @@
//============================================================================ //============================================================================
#include "Event.hxx" #include "Event.hxx"
#include "Sound.hxx"
#include "KidVid.hxx" #include "KidVid.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
KidVid::KidVid(Jack jack, const Event& event, const System& system, KidVid::KidVid(Jack jack, const Event& event, const System& system,
const string& baseDir, const string& romMd5) const string& baseDir, Sound& sound, const string& romMd5)
: Controller(jack, event, system, Controller::Type::KidVid), : Controller(jack, event, system, Controller::Type::KidVid),
myEnabled{myJack == Jack::Right}, myEnabled{myJack == Jack::Right},
myBaseDir{baseDir} myBaseDir{baseDir},
mySound{sound}
{ {
// Right now, there are only two games that use the KidVid // Right now, there are only two games that use the KidVid
if(romMd5 == "ee6665683ebdb539e89ba620981cb0f6") if(romMd5 == "ee6665683ebdb539e89ba620981cb0f6")
@ -59,7 +61,8 @@ void KidVid::update()
if(myEvent.get(Event::ConsoleReset)) if(myEvent.get(Event::ConsoleReset))
{ {
myTape = 0; // rewind Kid Vid tape myTape = 0; // rewind Kid Vid tape
closeSampleFiles(); myFilesFound = mySongPlaying = false;
mySound.stopWav();
} }
if(!myTape) if(!myTape)
{ {
@ -71,9 +74,8 @@ void KidVid::update()
myTape = myGame == BBears ? 4 : 1; // Berenstain Bears or Smurfs Save The Day? myTape = myGame == BBears ? 4 : 1; // Berenstain Bears or Smurfs Save The Day?
if(myTape) if(myTape)
{ {
//cerr << "myTape = " << myTape << endl;0 myIdx = myGame == BBears ? NumBlockBits : 0;
myIdx = myGame == BBears ? BlockBits : 0; myBlockIdx = NumBlockBits;
myBlockIdx = BlockBits;
myBlock = 0; myBlock = 0;
openSampleFiles(); openSampleFiles();
} }
@ -92,49 +94,58 @@ void KidVid::update()
if(!myBlockIdx) if(!myBlockIdx)
{ {
if(!myBlock) if(!myBlock)
myIdx = ((myTape * 6) + 12 - Blocks) * 8; //KVData00-KVData=12 myIdx = ((myTape * 6) + 12 - NumBlocks) * 8; //KVData00-KVData=12
else else
{ {
if(myGame == Smurfs) const uInt32 lastBlock = myGame == Smurfs
{ ? ourBlocks[myTape - 1]
if(myBlock >= ourBlocks[myTape - 1]) : ourBlocks[myTape + 2 - 1];
myIdx = 42 * 8; //KVData80-KVData=42 if(myBlock >= lastBlock)
else myIdx = 42 * 8; //KVData80-KVData=42
{
myIdx = 36 * 8;//KVPause-KVData=36
setNextSong();
}
}
else else
{ {
if(myBlock >= ourBlocks[myTape + 2 - 1]) myIdx = 36 * 8;//KVPause-KVData=36
myIdx = 42 * 8; //KVData80-KVData=42 setNextSong();
else
{
myIdx = 36 * 8;//KVPause-KVData=36
setNextSong();
}
} }
} }
++myBlock; ++myBlock;
myBlockIdx = BlockBits; myBlockIdx = NumBlockBits;
}
}
if(myFilesFound)
{
if(mySongPlaying)
{
myTapeBusy = (mySound.wavSize() > 262 * 48) || !myBeep;
// Check for end of played sample
if(mySound.wavSize() == 0)
{
mySongPlaying = false;
myTapeBusy = !myBeep;
if(!myBeep)
setNextSong();
}
}
}
else
{
if(mySongLength)
{
--mySongLength;
myTapeBusy = (mySongLength > 48);
} }
} }
// emulate playing the songs (but MUCH faster!)
// TODO: this has to be done by an Audio class
for(int i = mySongCounter / 8; i >= 0; --i)
getNextSampleByte();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void KidVid::openSampleFiles() void KidVid::openSampleFiles()
{ {
#ifdef KID_TAPE
static constexpr const char* fileNames[6] = { static constexpr const char* fileNames[6] = {
"KVS3.WAV", "KVS1.WAV", "KVS2.WAV", "KVS3.WAV", "KVS1.WAV", "KVS2.WAV",
"KVB3.WAV", "KVB1.WAV", "KVB2.WAV" "KVB3.WAV", "KVB1.WAV", "KVB2.WAV"
}; };
static constexpr uInt32 startSong[6] = { static constexpr uInt32 firstSongPointer[6] = {
44 + 38, 44 + 38,
0, 0,
44, 44,
@ -143,114 +154,62 @@ void KidVid::openSampleFiles()
44 + 38 + 42 + 62 44 + 38 + 42 + 62
}; };
if(!myFilesOpened) if(!myFilesFound)
{ {
int i = myGame == Smurfs ? myTape - 1 : myTape + 2; int i = myGame == Smurfs ? myTape - 1 : myTape + 2;
if(myTape == 4) i = 3; if(myTape == 4) i = 3;
mySampleFile.open(myBaseDir + fileNames[i], std::ios::binary); mySampleFile = myBaseDir + fileNames[i];
if(mySampleFile.is_open())
{ std::ifstream f1, f2;
cerr << "opened file: " << fileNames[i] << endl; f1.open(mySampleFile, std::ios::binary);
mySharedSampleFile.open(myBaseDir + "KVSHARED.WAV", std::ios::binary); f2.open(myBaseDir + "KVSHARED.WAV", std::ios::binary);
if(!mySharedSampleFile.is_open())
mySampleFile.close(); myFilesFound = f1.is_open() && f2.is_open();
else
{ if(myFilesFound)
cerr << "opened file: " << "kvshared.wav" << endl; cerr << "found file: " << fileNames[i] << endl
mySampleFile.seekg(45); << "found file: " << "KVSHARED.WAV" << endl;
myFilesOpened = true;
} mySongLength = 0;
} mySongPointer = firstSongPointer[i];
mySongCounter = 0;
myFilePointer = startSong[i];
} }
#endif
myTapeBusy = false; myTapeBusy = false;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void KidVid::closeSampleFiles()
{
#ifdef KID_TAPE
if(mySampleFile.is_open())
mySampleFile.close();
if(mySharedSampleFile.is_open())
mySharedSampleFile.close();
myFilesOpened = false;
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void KidVid::setNextSong() void KidVid::setNextSong()
{ {
#ifdef KID_TAPE if(myFilesFound)
if(myFilesOpened)
{ {
//cerr << endl << std::dec << mySongCounter << ", " << myFilePointer << endl; myBeep = (ourSongPositions[mySongPointer] & 0x80) == 0;
myBeep = (ourSongPositions[myFilePointer] & 0x80) == 0;
const uInt8 temp = ourSongPositions[myFilePointer] & 0x7f; const uInt8 temp = ourSongPositions[mySongPointer] & 0x7f;
mySharedData = (temp < 10); mySongLength = ourSongStart[temp+1] - ourSongStart[temp];
mySongCounter = ourSongStart[temp+1] - ourSongStart[temp];
if(mySharedData) // Play the WAV file
mySharedSampleFile.seekg(ourSongStart[temp]); const string fileName = (temp < 10) ? myBaseDir + "KVSHARED.WAV" : mySampleFile;
else mySound.playWav(fileName.c_str(), ourSongStart[temp], mySongLength);
mySampleFile.seekg(ourSongStart[temp]);
++myFilePointer; mySongPlaying = myTapeBusy = true;
myTapeBusy = true; ++mySongPointer;
} }
else else
#endif
{ {
myBeep = true; myBeep = true;
myTapeBusy = true; myTapeBusy = true;
mySongCounter = 10 * 80*262; /* delay needed for Harmony without tape */ mySongLength = 80; /* delay needed for Harmony without tape */
} }
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void KidVid::getNextSampleByte() const std::array<uInt8, KidVid::NumBlocks> KidVid::ourBlocks = {
{
static bool oddeven = false;
if(mySongCounter == 0)
{
#ifdef KID_TAPE
mySampleByte = 0x80;
#endif
}
else
{
oddeven = !oddeven;
if(oddeven)
{
mySongCounter--;
myTapeBusy = (mySongCounter > 262 * 48) || !myBeep;
#ifdef KID_TAPE
mySampleByte = myFilesOpened
? (mySharedData ? mySharedSampleFile.get() : mySampleFile.get())
: 0x80;
#endif
if(!myBeep && (mySongCounter == 0))
setNextSong();
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const std::array<uInt8, KidVid::Blocks> KidVid::ourBlocks = {
2+40, 2+21, 2+35, /* Smurfs tapes 3, 1, 2 */ 2+40, 2+21, 2+35, /* Smurfs tapes 3, 1, 2 */
42+60, 42+78, 42+60 /* BBears tapes 1, 2, 3 (40 extra blocks for intro) */ 42+60, 42+78, 42+60 /* BBears tapes 1, 2, 3 (40 extra blocks for intro) */
}; };
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const std::array<uInt8, KidVid::BlockBits> KidVid::ourData = { const std::array<uInt8, KidVid::NumBlockBits> KidVid::ourData = {
/* KVData44 */ /* KVData44 */
0x7b, // 0111 1011b ; (1)0 0x7b, // 0111 1011b ; (1)0
0x1e, // 0001 1110b ; 1 0x1e, // 0001 1110b ; 1
@ -409,7 +368,7 @@ const std::array<uInt32, KidVid::SongStartSize> KidVid::ourSongStart = {
3720920, 3720920,
/* kvb1 */ /* kvb1 */
44, /* 3 */ 44, /* 3 */ // can be one too late!
592749, /* 5 */ 592749, /* 5 */
936142, /* 2 */ 936142, /* 2 */
1465343, /* 4 */ 1465343, /* 4 */

View File

@ -18,9 +18,8 @@
#ifndef KIDVID_HXX #ifndef KIDVID_HXX
#define KIDVID_HXX #define KIDVID_HXX
//#define KID_TAPE
class Event; class Event;
class Sound;
#include "bspf.hxx" #include "bspf.hxx"
#include "Control.hxx" #include "Control.hxx"
@ -48,7 +47,7 @@ class KidVid : public Controller
@param romMd5 The md5 of the ROM using this controller @param romMd5 The md5 of the ROM using this controller
*/ */
KidVid(Jack jack, const Event& event, const System& system, KidVid(Jack jack, const Event& event, const System& system,
const string& baseDir, const string& romMd5); const string& baseDir, Sound& sound, const string& romMd5);
~KidVid() override = default; ~KidVid() override = default;
public: public:
@ -76,21 +75,16 @@ class KidVid : public Controller
private: private:
// Open/close a WAV sample file // Open/close a WAV sample file
void openSampleFiles(); void openSampleFiles();
void closeSampleFiles();
// Jump to next song in the sequence // Jump to next song in the sequence
void setNextSong(); void setNextSong();
// Generate next sample byte
// TODO - rework this, perhaps send directly to sound class
void getNextSampleByte();
private: private:
static constexpr uInt32 static constexpr uInt32
Smurfs = 0x44, Smurfs = 0x44,
BBears = 0x48, BBears = 0x48,
Blocks = 6, // number of bytes / block NumBlocks = 6, // number of bytes / block
BlockBits = Blocks*8, // number of bits / block NumBlockBits = NumBlocks*8, // number of bits / block
SongPosSize = 44+38+42+62+80+62, SongPosSize = 44+38+42+62+80+62,
SongStartSize = 104 SongStartSize = 104
; ;
@ -100,28 +94,27 @@ class KidVid : public Controller
bool myEnabled{false}; bool myEnabled{false};
string myBaseDir; string myBaseDir;
#ifdef KID_TAPE Sound& mySound;
// The file streams for the WAV files
std::ifstream mySampleFile, mySharedSampleFile;
// Indicates if sample files have been successfully opened
bool myFilesOpened{false};
uInt32 myFilePointer{0}; // Path and name of the current sample file
bool mySharedData{false}; string mySampleFile;
uInt8 mySampleByte{0}; // Indicates if the sample files have been found
#endif bool myFilesFound{false};
uInt32 mySongPointer{0};
// Is the tape currently 'busy' / in use? // Is the tape currently 'busy' / in use?
bool myTapeBusy{false}; bool myTapeBusy{false};
uInt32 mySongCounter{0}; bool mySongPlaying{false};
uInt32 mySongLength{0};
bool myBeep{false}; bool myBeep{false};
uInt32 myGame{0}, myTape{0}; uInt32 myGame{0}, myTape{0};
uInt32 myIdx{0}, myBlock{0}, myBlockIdx{0}; uInt32 myIdx{0}, myBlock{0}, myBlockIdx{0};
// Number of blocks and data on tape // Number of blocks and data on tape
static const std::array<uInt8, Blocks> ourBlocks; static const std::array<uInt8, NumBlocks> ourBlocks;
static const std::array<uInt8, BlockBits> ourData; static const std::array<uInt8, NumBlockBits> ourData;
static const std::array<uInt8, SongPosSize> ourSongPositions; static const std::array<uInt8, SongPosSize> ourSongPositions;
static const std::array<uInt32, SongStartSize> ourSongStart; static const std::array<uInt32, SongStartSize> ourSongStart;

View File

@ -23,6 +23,7 @@ class AudioQueue;
class EmulationTiming; class EmulationTiming;
#include "bspf.hxx" #include "bspf.hxx"
#include "Variant.hxx"
/** /**
This class is an abstract base class for the various sound objects. This class is an abstract base class for the various sound objects.
@ -104,6 +105,29 @@ class Sound
*/ */
const VariantList& supportedDevices() const {return myDevices;} const VariantList& supportedDevices() const {return myDevices;}
/**
Play a WAV file.
@param fileName The name of the WAV file
@param position The position to start playing
@param length The played length
@return True, if the WAV file can be played
*/
virtual bool playWav(const char* fileName, uInt32 position = 0, uInt32 length = 0) = 0;
/**
Stop any currently playing WAV file.
*/
virtual void stopWav() = 0;
/**
Get the size of the WAV file which remains to be played.
@return The remaining number of bytes
*/
virtual uInt32 wavSize() const = 0;
protected: protected:
/** /**
This method is called to query the audio devices. This method is called to query the audio devices.
@ -112,6 +136,7 @@ class Sound
*/ */
virtual void queryHardware(VariantList& devices) = 0; virtual void queryHardware(VariantList& devices) = 0;
protected: protected:
// The OSystem for this sound object // The OSystem for this sound object
OSystem& myOSystem; OSystem& myOSystem;