mirror of https://github.com/stella-emu/stella.git
added WAV playing and adapted KidVid code accordingly
This commit is contained in:
parent
162921b9f3
commit
ba6b32aa21
|
@ -72,6 +72,10 @@ SoundSDL2::~SoundSDL2()
|
|||
{
|
||||
ASSERT_MAIN_THREAD;
|
||||
|
||||
stopWav();
|
||||
if(myWavDevice)
|
||||
SDL_CloseAudioDevice(myWavDevice);
|
||||
|
||||
if (!myIsInitializedFlag) return;
|
||||
|
||||
SDL_CloseAudioDevice(myDevice);
|
||||
|
@ -194,6 +198,7 @@ void SoundSDL2::open(shared_ptr<AudioQueue> audioQueue,
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void SoundSDL2::close()
|
||||
{
|
||||
stopWav();
|
||||
if(!myIsInitializedFlag) return;
|
||||
|
||||
mute(true);
|
||||
|
@ -209,6 +214,8 @@ bool SoundSDL2::mute(bool state)
|
|||
const bool oldstate = SDL_GetAudioDeviceStatus(myDevice) == SDL_AUDIO_PAUSED;
|
||||
if(myIsInitializedFlag)
|
||||
SDL_PauseAudioDevice(myDevice, state ? 1 : 0);
|
||||
if(myWavDevice)
|
||||
SDL_PauseAudioDevice(myWavDevice, state ? 1 : 0);
|
||||
|
||||
return oldstate;
|
||||
}
|
||||
|
@ -395,4 +402,57 @@ void SoundSDL2::callback(void* udata, uInt8* stream, int 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
|
||||
|
|
|
@ -107,6 +107,29 @@ class SoundSDL2 : public Sound
|
|||
*/
|
||||
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:
|
||||
/**
|
||||
This method is called to query the audio devices.
|
||||
|
@ -161,6 +184,9 @@ class SoundSDL2 : public Sound
|
|||
|
||||
AudioSettings& myAudioSettings;
|
||||
|
||||
SDL_AudioDeviceID myWavDevice{0};
|
||||
uInt8* myWavBuffer{nullptr};
|
||||
|
||||
string myAboutString;
|
||||
|
||||
private:
|
||||
|
|
|
@ -1028,7 +1028,8 @@ unique_ptr<Controller> Console::getControllerPort(const Controller::Type type,
|
|||
break;
|
||||
|
||||
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;
|
||||
|
||||
case Controller::Type::MindLink:
|
||||
|
|
|
@ -16,14 +16,16 @@
|
|||
//============================================================================
|
||||
|
||||
#include "Event.hxx"
|
||||
#include "Sound.hxx"
|
||||
#include "KidVid.hxx"
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
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),
|
||||
myEnabled{myJack == Jack::Right},
|
||||
myBaseDir{baseDir}
|
||||
myBaseDir{baseDir},
|
||||
mySound{sound}
|
||||
{
|
||||
// Right now, there are only two games that use the KidVid
|
||||
if(romMd5 == "ee6665683ebdb539e89ba620981cb0f6")
|
||||
|
@ -59,7 +61,8 @@ void KidVid::update()
|
|||
if(myEvent.get(Event::ConsoleReset))
|
||||
{
|
||||
myTape = 0; // rewind Kid Vid tape
|
||||
closeSampleFiles();
|
||||
myFilesFound = mySongPlaying = false;
|
||||
mySound.stopWav();
|
||||
}
|
||||
if(!myTape)
|
||||
{
|
||||
|
@ -71,9 +74,8 @@ void KidVid::update()
|
|||
myTape = myGame == BBears ? 4 : 1; // Berenstain Bears or Smurfs Save The Day?
|
||||
if(myTape)
|
||||
{
|
||||
//cerr << "myTape = " << myTape << endl;0
|
||||
myIdx = myGame == BBears ? BlockBits : 0;
|
||||
myBlockIdx = BlockBits;
|
||||
myIdx = myGame == BBears ? NumBlockBits : 0;
|
||||
myBlockIdx = NumBlockBits;
|
||||
myBlock = 0;
|
||||
openSampleFiles();
|
||||
}
|
||||
|
@ -92,49 +94,58 @@ void KidVid::update()
|
|||
if(!myBlockIdx)
|
||||
{
|
||||
if(!myBlock)
|
||||
myIdx = ((myTape * 6) + 12 - Blocks) * 8; //KVData00-KVData=12
|
||||
myIdx = ((myTape * 6) + 12 - NumBlocks) * 8; //KVData00-KVData=12
|
||||
else
|
||||
{
|
||||
if(myGame == Smurfs)
|
||||
{
|
||||
if(myBlock >= ourBlocks[myTape - 1])
|
||||
myIdx = 42 * 8; //KVData80-KVData=42
|
||||
else
|
||||
{
|
||||
myIdx = 36 * 8;//KVPause-KVData=36
|
||||
setNextSong();
|
||||
}
|
||||
}
|
||||
const uInt32 lastBlock = myGame == Smurfs
|
||||
? ourBlocks[myTape - 1]
|
||||
: ourBlocks[myTape + 2 - 1];
|
||||
if(myBlock >= lastBlock)
|
||||
myIdx = 42 * 8; //KVData80-KVData=42
|
||||
else
|
||||
{
|
||||
if(myBlock >= ourBlocks[myTape + 2 - 1])
|
||||
myIdx = 42 * 8; //KVData80-KVData=42
|
||||
else
|
||||
{
|
||||
myIdx = 36 * 8;//KVPause-KVData=36
|
||||
setNextSong();
|
||||
}
|
||||
myIdx = 36 * 8;//KVPause-KVData=36
|
||||
setNextSong();
|
||||
}
|
||||
}
|
||||
++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()
|
||||
{
|
||||
#ifdef KID_TAPE
|
||||
static constexpr const char* fileNames[6] = {
|
||||
"KVS3.WAV", "KVS1.WAV", "KVS2.WAV",
|
||||
"KVB3.WAV", "KVB1.WAV", "KVB2.WAV"
|
||||
};
|
||||
static constexpr uInt32 startSong[6] = {
|
||||
static constexpr uInt32 firstSongPointer[6] = {
|
||||
44 + 38,
|
||||
0,
|
||||
44,
|
||||
|
@ -143,114 +154,62 @@ void KidVid::openSampleFiles()
|
|||
44 + 38 + 42 + 62
|
||||
};
|
||||
|
||||
if(!myFilesOpened)
|
||||
if(!myFilesFound)
|
||||
{
|
||||
int i = myGame == Smurfs ? myTape - 1 : myTape + 2;
|
||||
if(myTape == 4) i = 3;
|
||||
|
||||
mySampleFile.open(myBaseDir + fileNames[i], std::ios::binary);
|
||||
if(mySampleFile.is_open())
|
||||
{
|
||||
cerr << "opened file: " << fileNames[i] << endl;
|
||||
mySharedSampleFile.open(myBaseDir + "KVSHARED.WAV", std::ios::binary);
|
||||
if(!mySharedSampleFile.is_open())
|
||||
mySampleFile.close();
|
||||
else
|
||||
{
|
||||
cerr << "opened file: " << "kvshared.wav" << endl;
|
||||
mySampleFile.seekg(45);
|
||||
myFilesOpened = true;
|
||||
}
|
||||
}
|
||||
mySongCounter = 0;
|
||||
myFilePointer = startSong[i];
|
||||
mySampleFile = myBaseDir + fileNames[i];
|
||||
|
||||
std::ifstream f1, f2;
|
||||
f1.open(mySampleFile, std::ios::binary);
|
||||
f2.open(myBaseDir + "KVSHARED.WAV", std::ios::binary);
|
||||
|
||||
myFilesFound = f1.is_open() && f2.is_open();
|
||||
|
||||
if(myFilesFound)
|
||||
cerr << "found file: " << fileNames[i] << endl
|
||||
<< "found file: " << "KVSHARED.WAV" << endl;
|
||||
|
||||
mySongLength = 0;
|
||||
mySongPointer = firstSongPointer[i];
|
||||
}
|
||||
#endif
|
||||
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()
|
||||
{
|
||||
#ifdef KID_TAPE
|
||||
if(myFilesOpened)
|
||||
if(myFilesFound)
|
||||
{
|
||||
//cerr << endl << std::dec << mySongCounter << ", " << myFilePointer << endl;
|
||||
myBeep = (ourSongPositions[myFilePointer] & 0x80) == 0;
|
||||
myBeep = (ourSongPositions[mySongPointer] & 0x80) == 0;
|
||||
|
||||
const uInt8 temp = ourSongPositions[myFilePointer] & 0x7f;
|
||||
mySharedData = (temp < 10);
|
||||
mySongCounter = ourSongStart[temp+1] - ourSongStart[temp];
|
||||
const uInt8 temp = ourSongPositions[mySongPointer] & 0x7f;
|
||||
mySongLength = ourSongStart[temp+1] - ourSongStart[temp];
|
||||
|
||||
if(mySharedData)
|
||||
mySharedSampleFile.seekg(ourSongStart[temp]);
|
||||
else
|
||||
mySampleFile.seekg(ourSongStart[temp]);
|
||||
// Play the WAV file
|
||||
const string fileName = (temp < 10) ? myBaseDir + "KVSHARED.WAV" : mySampleFile;
|
||||
mySound.playWav(fileName.c_str(), ourSongStart[temp], mySongLength);
|
||||
|
||||
++myFilePointer;
|
||||
myTapeBusy = true;
|
||||
mySongPlaying = myTapeBusy = true;
|
||||
++mySongPointer;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
myBeep = true;
|
||||
myTapeBusy = true;
|
||||
mySongCounter = 10 * 80*262; /* delay needed for Harmony without tape */
|
||||
mySongLength = 80; /* delay needed for Harmony without tape */
|
||||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void KidVid::getNextSampleByte()
|
||||
{
|
||||
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 = {
|
||||
const std::array<uInt8, KidVid::NumBlocks> KidVid::ourBlocks = {
|
||||
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) */
|
||||
};
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
const std::array<uInt8, KidVid::BlockBits> KidVid::ourData = {
|
||||
const std::array<uInt8, KidVid::NumBlockBits> KidVid::ourData = {
|
||||
/* KVData44 */
|
||||
0x7b, // 0111 1011b ; (1)0
|
||||
0x1e, // 0001 1110b ; 1
|
||||
|
@ -409,7 +368,7 @@ const std::array<uInt32, KidVid::SongStartSize> KidVid::ourSongStart = {
|
|||
3720920,
|
||||
|
||||
/* kvb1 */
|
||||
44, /* 3 */
|
||||
44, /* 3 */ // can be one too late!
|
||||
592749, /* 5 */
|
||||
936142, /* 2 */
|
||||
1465343, /* 4 */
|
||||
|
|
|
@ -18,9 +18,8 @@
|
|||
#ifndef KIDVID_HXX
|
||||
#define KIDVID_HXX
|
||||
|
||||
//#define KID_TAPE
|
||||
|
||||
class Event;
|
||||
class Sound;
|
||||
|
||||
#include "bspf.hxx"
|
||||
#include "Control.hxx"
|
||||
|
@ -48,7 +47,7 @@ class KidVid : public Controller
|
|||
@param romMd5 The md5 of the ROM using this controller
|
||||
*/
|
||||
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;
|
||||
|
||||
public:
|
||||
|
@ -76,21 +75,16 @@ class KidVid : public Controller
|
|||
private:
|
||||
// Open/close a WAV sample file
|
||||
void openSampleFiles();
|
||||
void closeSampleFiles();
|
||||
|
||||
// Jump to next song in the sequence
|
||||
void setNextSong();
|
||||
|
||||
// Generate next sample byte
|
||||
// TODO - rework this, perhaps send directly to sound class
|
||||
void getNextSampleByte();
|
||||
|
||||
private:
|
||||
static constexpr uInt32
|
||||
Smurfs = 0x44,
|
||||
BBears = 0x48,
|
||||
Blocks = 6, // number of bytes / block
|
||||
BlockBits = Blocks*8, // number of bits / block
|
||||
Smurfs = 0x44,
|
||||
BBears = 0x48,
|
||||
NumBlocks = 6, // number of bytes / block
|
||||
NumBlockBits = NumBlocks*8, // number of bits / block
|
||||
SongPosSize = 44+38+42+62+80+62,
|
||||
SongStartSize = 104
|
||||
;
|
||||
|
@ -100,28 +94,27 @@ class KidVid : public Controller
|
|||
bool myEnabled{false};
|
||||
|
||||
string myBaseDir;
|
||||
#ifdef KID_TAPE
|
||||
// The file streams for the WAV files
|
||||
std::ifstream mySampleFile, mySharedSampleFile;
|
||||
// Indicates if sample files have been successfully opened
|
||||
bool myFilesOpened{false};
|
||||
Sound& mySound;
|
||||
|
||||
uInt32 myFilePointer{0};
|
||||
bool mySharedData{false};
|
||||
uInt8 mySampleByte{0};
|
||||
#endif
|
||||
// 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};
|
||||
|
||||
uInt32 mySongCounter{0};
|
||||
bool mySongPlaying{false};
|
||||
uInt32 mySongLength{0};
|
||||
bool myBeep{false};
|
||||
uInt32 myGame{0}, myTape{0};
|
||||
uInt32 myIdx{0}, myBlock{0}, myBlockIdx{0};
|
||||
|
||||
// Number of blocks and data on tape
|
||||
static const std::array<uInt8, Blocks> ourBlocks;
|
||||
static const std::array<uInt8, BlockBits> ourData;
|
||||
static const std::array<uInt8, NumBlocks> ourBlocks;
|
||||
static const std::array<uInt8, NumBlockBits> ourData;
|
||||
|
||||
static const std::array<uInt8, SongPosSize> ourSongPositions;
|
||||
static const std::array<uInt32, SongStartSize> ourSongStart;
|
||||
|
|
|
@ -23,6 +23,7 @@ class AudioQueue;
|
|||
class EmulationTiming;
|
||||
|
||||
#include "bspf.hxx"
|
||||
#include "Variant.hxx"
|
||||
|
||||
/**
|
||||
This class is an abstract base class for the various sound objects.
|
||||
|
@ -104,6 +105,29 @@ class Sound
|
|||
*/
|
||||
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:
|
||||
/**
|
||||
This method is called to query the audio devices.
|
||||
|
@ -112,6 +136,7 @@ class Sound
|
|||
*/
|
||||
virtual void queryHardware(VariantList& devices) = 0;
|
||||
|
||||
|
||||
protected:
|
||||
// The OSystem for this sound object
|
||||
OSystem& myOSystem;
|
||||
|
|
Loading…
Reference in New Issue