diff --git a/src/common/SoundSDL.cxx b/src/common/SoundSDL.cxx index e1faf963c..7e6f0969d 100644 --- a/src/common/SoundSDL.cxx +++ b/src/common/SoundSDL.cxx @@ -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(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(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, 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(object); + uInt32 len = static_cast(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(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(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(object)->processWav( - stream, static_cast(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 diff --git a/src/common/SoundSDL.hxx b/src/common/SoundSDL.hxx index d76492ec2..53ca7db62 100644 --- a/src/common/SoundSDL.hxx +++ b/src/common/SoundSDL.hxx @@ -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 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