Fixed WAV playback; KidVid games work again.

This commit is contained in:
Stephen Anthony 2025-05-31 18:46:54 -02:30
parent 9eac43823d
commit 6868abec68
2 changed files with 100 additions and 135 deletions

View File

@ -71,6 +71,7 @@ SoundSDL::~SoundSDL()
return;
SDL_DestroyAudioStream(myStream);
SDL_CloseAudioDevice(myDevice);
SDL_QuitSubSystem(SDL_INIT_AUDIO);
}
@ -79,17 +80,7 @@ bool SoundSDL::openDevice()
{
ASSERT_MAIN_THREAD;
mySpec = { SDL_AUDIO_F32, 2, static_cast<int>(myAudioSettings.sampleRate()) };
if(myIsInitializedFlag)
{
SDL_DestroyAudioStream(myStream);
myStream = nullptr;
}
myStream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK,
&mySpec, audioCallback, this);
if(myStream == nullptr)
auto SOUND_ERROR = [this]() -> bool
{
ostringstream buf;
@ -98,7 +89,27 @@ bool SoundSDL::openDevice()
Logger::error(buf.view());
return myIsInitializedFlag = false;
};
if(myIsInitializedFlag)
{
SDL_DestroyAudioStream(myStream);
myStream = nullptr;
}
mySpec = { SDL_AUDIO_F32, 2, static_cast<int>(myAudioSettings.sampleRate()) };
myDevice = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &mySpec);
if(myDevice == 0)
return SOUND_ERROR();
myStream = SDL_CreateAudioStream(&mySpec, nullptr);
if(!myStream)
return SOUND_ERROR();
if(!SDL_BindAudioStream(myDevice, myStream))
return SOUND_ERROR();
if(!SDL_SetAudioStreamGetCallback(myStream, audioCallback, this))
return SOUND_ERROR();
return myIsInitializedFlag = true;
}
@ -131,7 +142,7 @@ void SoundSDL::open(shared_ptr<AudioQueue> audioQueue,
myCurrentFragment = nullptr;
myAudioQueue->ignoreOverflows(!myAudioSettings.enabled());
// myWavHandler.setSpeed(262 * 60 * 2. / myEmulationTiming->audioSampleRate());
myWavHandler.setSpeed(262 * 60 * 2. / myEmulationTiming->audioSampleRate());
// Do we need to re-open the sound device?
// Only do this when absolutely necessary
@ -191,7 +202,7 @@ bool SoundSDL::pause(bool enable)
if(enable) SDL_PauseAudioStreamDevice(myStream);
else SDL_ResumeAudioStreamDevice(myStream);
// myWavHandler.pause(enable);
myWavHandler.pause(enable);
}
return wasPaused;
}
@ -206,7 +217,7 @@ void SoundSDL::setVolume(uInt32 volume, bool persist)
: 0.F;
SDL_SetAudioStreamGain(myStream, myVolumeFactor);
// myWavHandler.setVolumeFactor(myVolumeFactor);
myWavHandler.setVolumeFactor(myVolumeFactor);
if(persist)
myAudioSettings.setVolume(volume);
@ -359,32 +370,27 @@ void SoundSDL::audioCallback(void* object, SDL_AudioStream* stream,
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool SoundSDL::playWav(const string& fileName, uInt32 position, uInt32 length)
{
#if 0
return myWavHandler.play(fileName, position, length);
#endif
return false;
if(myStream)
return myWavHandler.play(SDL_GetAudioStreamDevice(myStream),
fileName, position, length);
else
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::stopWav()
{
#if 0
myWavHandler.stop();
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 SoundSDL::wavSize() const
{
#if 0
return myWavHandler.size();
#endif
return 0;
}
#if 0
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool SoundSDL::WavHandlerSDL::play(
bool SoundSDL::WavHandler::play(SDL_AudioDeviceID device,
const string& fileName, uInt32 position, uInt32 length)
{
// Load WAV file
@ -396,132 +402,94 @@ bool SoundSDL::WavHandlerSDL::play(
myBuffer = nullptr;
}
SDL_zero(mySpec);
if(SDL_LoadWAV(fileName.c_str(), &mySpec, &myBuffer, &myLength) == nullptr)
if(!SDL_LoadWAV(fileName.c_str(), &mySpec, &myBuffer, &myLength))
return false;
// Set the callback function
mySpec.callback = callback;
mySpec.userdata = this;
}
if(position > myLength)
return false;
myFilename = fileName;
if(myStream)
SDL_UnbindAudioStream(myStream);
myStream = SDL_CreateAudioStream(&mySpec, nullptr);
if(myStream == nullptr)
return false;
if(!SDL_BindAudioStream(device, myStream))
return false;
if(!SDL_SetAudioStreamGetCallback(myStream, WavHandler::wavCallback, this))
return false;
SDL_SetAudioStreamGain(myStream, myVolumeFactor);
myFilename = fileName;
myRemaining = length
? std::min(length, myLength - position)
: myLength;
myPos = myBuffer + position;
// Open audio device
if(!myDevice)
{
myDevice = SDL_OpenAudioDevice(device, 0, &mySpec, nullptr, 0);
if(!myDevice)
return false;
// Play audio
pause(false);
}
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::WavHandlerSDL::stop()
void SoundSDL::WavHandler::stop()
{
if(myBuffer)
{
// Clean up
myRemaining = 0;
SDL_CloseAudioDevice(myDevice); myDevice = 0;
SDL_UnbindAudioStream(myStream); myStream = nullptr;
SDL_free(myBuffer); myBuffer = nullptr;
}
if(myCvtBuffer)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::WavHandler::setVolumeFactor(float volumeFactor)
{
myVolumeFactor = volumeFactor;
if(myStream)
SDL_SetAudioStreamGain(myStream, myVolumeFactor);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::WavHandler::pause(bool state) const
{
if(myStream)
{
myCvtBuffer.reset();
myCvtBufferSize = 0;
if(state) SDL_PauseAudioStreamDevice(myStream);
else SDL_ResumeAudioStreamDevice(myStream);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::WavHandlerSDL::processWav(uInt8* stream, uInt32 len)
void SoundSDL::WavHandler::wavCallback(void* object, SDL_AudioStream* stream,
int additional_amt, int)
{
SDL_memset(stream, mySpec.silence, len);
if(myRemaining)
auto* self = static_cast<WavHandler*>(object);
uInt32 len = static_cast<uInt32>(additional_amt);
uInt32& remaining = self->myRemaining;
if(remaining)
{
if(mySpeed != 1.0)
{
const int origLen = len;
len = std::round(len / mySpeed);
const int newFreq =
std::round(static_cast<double>(mySpec.freq) * origLen / len);
if(self->mySpeed != 1.0)
len = std::round(len / self->mySpeed);
if(len > myRemaining) // NOLINT(readability-use-std-min-max)
len = myRemaining;
if(len > remaining) // NOLINT(readability-use-std-min-max)
len = remaining;
SDL_AudioCVT cvt;
SDL_BuildAudioCVT(&cvt, mySpec.format, mySpec.channels, mySpec.freq,
mySpec.format, mySpec.channels, newFreq);
SDL_assert(cvt.needed); // Obviously, this one is always needed.
cvt.len = len * mySpec.channels; // Mono 8 bit sample frames
SDL_PutAudioStreamData(stream, self->myPos, len);
if(!myCvtBuffer || std::cmp_less(myCvtBufferSize, cvt.len * cvt.len_mult))
{
myCvtBufferSize = cvt.len * cvt.len_mult;
myCvtBuffer = make_unique<uInt8[]>(myCvtBufferSize);
}
cvt.buf = myCvtBuffer.get();
// Read original data into conversion buffer
SDL_memcpy(cvt.buf, myPos, cvt.len);
SDL_ConvertAudio(&cvt);
// Mix volume adjusted WAV data into silent buffer
SDL_MixAudioFormat(stream, cvt.buf, mySpec.format, cvt.len_cvt,
SDL_MIX_MAXVOLUME * SoundSDL::myVolumeFactor);
}
else
{
if(len > myRemaining) // NOLINT(readability-use-std-min-max)
len = myRemaining;
// Mix volume adjusted WAV data into silent buffer
SDL_MixAudioFormat(stream, myPos, mySpec.format, len,
SDL_MIX_MAXVOLUME * myVolumeFactor);
}
myPos += len;
myRemaining -= len;
self->myPos += len;
remaining -= len;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::WavHandlerSDL::wavCallback(void* object, SDL_AudioStream* stream,
int additional_amt, int total_amt)
SoundSDL::WavHandler::~WavHandler()
{
static_cast<WavHandlerSDL*>(object)->processWav(
stream, static_cast<uInt32>(len));
}
ASSERT_MAIN_THREAD;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundSDL::WavHandlerSDL::~WavHandlerSDL()
{
if(myDevice)
{
SDL_CloseAudioDevice(myDevice);
SDL_FreeWAV(myBuffer);
}
if(myBuffer)
SDL_free(myBuffer);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::WavHandlerSDL::pause(bool state) const
{
if(myDevice)
if (state ? 1 : 0) {
SDL_PauseAudioDevice(myDevice);
}
else {
SDL_ResumeAudioDevice(myDevice);
}
}
#endif
#endif // SOUND_SUPPORT

View File

@ -152,7 +152,8 @@ class SoundSDL : public Sound
// Audio specification structure
SDL_AudioSpec mySpec{};
// Audio stream, which handles all interaction with SDL sound backend
// Audio device and stream, which handles all interaction with SDL sound backend
SDL_AudioDeviceID myDevice{};
SDL_AudioStream* myStream{nullptr};
// Audio buffer, passed to the audio callback and filled from the resampler
@ -166,56 +167,52 @@ class SoundSDL : public Sound
Int16* myCurrentFragment{nullptr};
bool myUnderrun{false};
float myVolumeFactor{0.F}; // Current volume level (0.F - 1.F)
float myVolumeFactor{1.F}; // Current volume level (0.F - 1.F)
string myAboutString;
#if 0
/**
This class implements WAV file playback using the SDL sound API.
*/
class WavHandlerSDL
class WavHandler
{
public:
explicit WavHandlerSDL() = default;
~WavHandlerSDL();
explicit WavHandler() = default;
~WavHandler();
bool play(const string& fileName, uInt32 position, uInt32 length);
bool play(SDL_AudioDeviceID device, const string& fileName,
uInt32 position, uInt32 length);
void stop();
uInt32 size() const { return myBuffer ? myRemaining : 0; }
void setSpeed(double speed) { mySpeed = speed; }
void setVolume(double volumeFactor) { mySpeed = speed; }
void setVolumeFactor(float volumeFactor);
void pause(bool state) const;
private:
string myFilename;
uInt32 myLength{0};
SDL_AudioStream* myStream{nullptr};
SDL_AudioSpec mySpec{};
uInt8* myBuffer{nullptr};
uInt32 myLength{0};
double mySpeed{1.0};
float myVolumeFactor{0.F}; // Current volume level (0 - 100)
float myVolumeFactor{1.F}; // Current volume level (0.F - 1.F)
unique_ptr<uInt8[]> myCvtBuffer;
uInt32 myCvtBufferSize{0};
SDL_AudioSpec mySpec{}; // audio output format
uInt8* myPos{nullptr}; // pointer to the audio buffer to be played
uInt32 myRemaining{0}; // remaining length of the sample we have to play
private:
// Callback function invoked by the SDL Audio library when it needs data
void processWav(uInt8* stream, uInt32 len);
static void wavCallback(void* object, SDL_AudioStream* stream,
int additonal_amt, int total_amt);
int additional_amt, int);
// Following constructors and assignment operators not supported
WavHandlerSDL(const WavHandlerSDL&) = delete;
WavHandlerSDL(WavHandlerSDL&&) = delete;
WavHandlerSDL& operator=(const WavHandlerSDL&) = delete;
WavHandlerSDL& operator=(WavHandlerSDL&&) = delete;
WavHandler(const WavHandler&) = delete;
WavHandler(WavHandler&&) = delete;
WavHandler& operator=(const WavHandler&) = delete;
WavHandler& operator=(WavHandler&&) = delete;
};
WavHandlerSDL myWavHandler;
#endif
WavHandler myWavHandler;
private:
// Callback functions invoked by the SDL Audio library when it needs data