From a647b2ba7f87a0d448581072d5470d7c167b3fa9 Mon Sep 17 00:00:00 2001 From: Thomas Jentzsch Date: Tue, 13 Sep 2022 15:24:19 +0200 Subject: [PATCH] implemented gapless playback for WAV files (KidVid) --- src/common/SoundSDL2.cxx | 54 ++++++++++++++++++++++------------------ src/common/SoundSDL2.hxx | 6 +++-- src/emucore/KidVid.cxx | 30 +++++++++++++--------- src/emucore/KidVid.hxx | 3 ++- src/emucore/Sound.hxx | 4 +-- 5 files changed, 56 insertions(+), 41 deletions(-) diff --git a/src/common/SoundSDL2.cxx b/src/common/SoundSDL2.cxx index 679a7e641..ab2fb21e0 100644 --- a/src/common/SoundSDL2.cxx +++ b/src/common/SoundSDL2.cxx @@ -401,39 +401,44 @@ void SoundSDL2::callback(void* udata, uInt8* stream, int len) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool SoundSDL2::playWav(const string& fileName, uInt32 position, uInt32 length) +bool SoundSDL2::playWav(const string& fileName, const uInt32 position, + const uInt32 length) { - uInt32 wavLength{0}; - - // Stop any playing WAVs - stopWav(); - // Load WAV file - auto* result = SDL_LoadWAV(fileName.c_str(), &myWavSpec, &myWavBuffer, &wavLength); - if(result == nullptr || position > wavLength) + if(fileName != myWavFilename || myWavBuffer == nullptr) + { + if(myWavBuffer) + { + SDL_FreeWAV(myWavBuffer); + myWavBuffer = nullptr; + } + if(SDL_LoadWAV(fileName.c_str(), &myWavSpec, &myWavBuffer, &myWavLength) == nullptr) + return false; + // Set the callback function + myWavSpec.callback = wavCallback; + myWavSpec.userdata = nullptr; + } + if(position > myWavLength) return false; - length = length - ? std::min(length, wavLength - position) - : wavLength; - - // Set the callback function - myWavSpec.callback = wavCallback; - myWavSpec.userdata = nullptr; + myWavFilename = fileName; + myWavLen = length + ? std::min(length, myWavLength - position) + : myWavLength; myWavPos = myWavBuffer + position; - myWavLen = length; // Open audio device - const char* device = myDeviceId ? myDevices.at(myDeviceId).first.c_str() : nullptr; - - myWavDevice = SDL_OpenAudioDevice(device, 0, &myWavSpec, nullptr, 0); if(!myWavDevice) - return false; - - // Play audio - SDL_PauseAudioDevice(myWavDevice, 0); + { + const char* device = myDeviceId ? myDevices.at(myDeviceId).first.c_str() : nullptr; + myWavDevice = SDL_OpenAudioDevice(device, 0, &myWavSpec, nullptr, 0); + if(!myWavDevice) + return false; + // Play audio + SDL_PauseAudioDevice(myWavDevice, 0); + } return true; } @@ -443,9 +448,10 @@ void SoundSDL2::stopWav() if(myWavBuffer) { // Clean up + myWavLen = 0; SDL_CloseAudioDevice(myWavDevice); + myWavDevice = 0; SDL_FreeWAV(myWavBuffer); - myWavBuffer = nullptr; } } diff --git a/src/common/SoundSDL2.hxx b/src/common/SoundSDL2.hxx index b2b9b4ee6..60868b2f9 100644 --- a/src/common/SoundSDL2.hxx +++ b/src/common/SoundSDL2.hxx @@ -116,8 +116,8 @@ class SoundSDL2 : public Sound @return True, if the WAV file can be played */ - bool playWav(const string& fileName, uInt32 position = 0, - uInt32 length = 0) override; + bool playWav(const string& fileName, const uInt32 position = 0, + const uInt32 length = 0) override; /** Stop any currently playing WAV file. @@ -186,6 +186,8 @@ class SoundSDL2 : public Sound AudioSettings& myAudioSettings; // WAV file sound variables + string myWavFilename{EmptyString}; + uInt32 myWavLength{0}; SDL_AudioDeviceID myWavDevice{0}; uInt8* myWavBuffer{nullptr}; diff --git a/src/emucore/KidVid.cxx b/src/emucore/KidVid.cxx index c06043eb9..085e01349 100644 --- a/src/emucore/KidVid.cxx +++ b/src/emucore/KidVid.cxx @@ -68,7 +68,7 @@ void KidVid::update() { // Continue playing song after state load const uInt8 temp = ourSongPositions[mySongPointer - 1] & 0x7f; - const uInt32 songLength = ourSongStart[temp + 1] - ourSongStart[temp]; + const uInt32 songLength = ourSongStart[temp + 1] - ourSongStart[temp] - (262 * ClickFrames); // Play the remaining WAV file const string& fileName = myOSystem.baseDir().getPath() + ((temp < 10) ? "KVSHARED.WAV" : getFileName()); @@ -140,6 +140,10 @@ void KidVid::update() { setPin(DigitalPin::Four, (ourData[myIdx >> 3] << (myIdx & 0x07)) & 0x80); + #ifdef DEBUG_BUILD + cerr << (DigitalPin::Four, (ourData[myIdx >> 3] << (myIdx & 0x07)) & 0x80 ? "X" : "."); + #endif + // increase to next bit ++myIdx; --myBlockIdx; @@ -172,7 +176,7 @@ void KidVid::update() if(mySongPlaying) { mySongLength = myOSystem.sound().wavSize(); - myTapeBusy = (mySongLength > 262 * ClickFrames) || !myBeep; + myTapeBusy = (mySongLength > 262 * TapeFrames) || !myBeep; // Check for end of played sample if(mySongLength == 0) { @@ -188,7 +192,7 @@ void KidVid::update() if(mySongLength) { --mySongLength; - myTapeBusy = (mySongLength > ClickFrames); + myTapeBusy = (mySongLength > TapeFrames); } } } @@ -259,6 +263,7 @@ void KidVid::openSampleFiles() 44 + 38 + 42 + 62 }; +#ifdef SOUND_SUPPORT if(!myFilesFound) { int i = myGame == Game::Smurfs ? myTape - 1 : myTape + 2; @@ -267,13 +272,14 @@ void KidVid::openSampleFiles() myFilesFound = FSNode(myOSystem.baseDir().getPath() + getFileName()).exists() && FSNode(myOSystem.baseDir().getPath() + "KVSHARED.WAV").exists(); -#ifdef DEBUG_BUILD + #ifdef DEBUG_BUILD if(myFilesFound) cerr << endl << "found file: " << getFileName() << endl << "found file: " << "KVSHARED.WAV" << endl; -#endif + #endif +#endif mySongLength = 0; mySongPointer = firstSongPointer[i]; } @@ -288,7 +294,7 @@ void KidVid::setNextSong() myBeep = (ourSongPositions[mySongPointer] & 0x80) == 0; const uInt8 temp = ourSongPositions[mySongPointer] & 0x7f; - mySongLength = ourSongStart[temp + 1] - ourSongStart[temp]; + mySongLength = ourSongStart[temp + 1] - ourSongStart[temp] - (262 * ClickFrames); // Play the WAV file const string& fileName = (temp < 10) ? "KVSHARED.WAV" : getFileName(); @@ -298,9 +304,9 @@ void KidVid::setNextSong() msg << "Read song #" << mySongPointer << " (" << fileName << ")"; myCallback(msg.str(), false); -#ifdef DEBUG_BUILD + #ifdef DEBUG_BUILD cerr << fileName << ": " << (ourSongPositions[mySongPointer] & 0x7f) << endl; -#endif + #endif mySongPlaying = myTapeBusy = true; ++mySongPointer; @@ -309,7 +315,7 @@ void KidVid::setNextSong() { myBeep = true; myTapeBusy = true; - mySongLength = 80; /* delay needed for Harmony without tape */ + mySongLength = TapeFrames + 32; /* delay needed for Harmony without tape */ } } @@ -438,14 +444,14 @@ const std::array KidVid::ourSongStart = { 3059116, /* kvs1 */ - 44, /* Harmony into 1 */ - 164685, /* falling notes (into 2) */ + 44, /* Harmony intro 1 */ + 164685, /* falling notes (intro 2) */ 395182, /* instructions */ 750335, /* high notes are high */ 962016, /* my hat's off to you */ 1204273, /* 1 2 3 do re mi */ 1538258, /* Harmony */ - 1801683, /* concratulations (all of the Smurfs voted) */ + 1801683, /* congratulations (all of the Smurfs voted) */ 2086276, /* line or space */ 2399093, /* hooray */ 2589606, /* hear yeeh */ diff --git a/src/emucore/KidVid.hxx b/src/emucore/KidVid.hxx index b4c9cbacd..583660473 100644 --- a/src/emucore/KidVid.hxx +++ b/src/emucore/KidVid.hxx @@ -111,7 +111,8 @@ class KidVid : public Controller NumBlockBits = NumBlocks * 8, // number of bits / block SongPosSize = 44 + 38 + 42 + 62 + 80 + 62, SongStartSize = 104, - ClickFrames = 48 // eliminate click noise at song end + TapeFrames = 60, + ClickFrames = 3 // eliminate click noise at song end ; // Whether the KidVid device is enabled (only for games that it diff --git a/src/emucore/Sound.hxx b/src/emucore/Sound.hxx index 902fb80a5..f06aa710b 100644 --- a/src/emucore/Sound.hxx +++ b/src/emucore/Sound.hxx @@ -114,8 +114,8 @@ class Sound @return True, if the WAV file can be played */ - virtual bool playWav(const string& fileName, uInt32 position = 0, - uInt32 length = 0) { return false; } + virtual bool playWav(const string& fileName, const uInt32 position = 0, + const uInt32 length = 0) { return false; } /** Stop any currently playing WAV file.