From 5645456537d71449ec7e4965f944b4675920631a Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Fri, 8 Apr 2022 23:48:28 +0100 Subject: [PATCH] Support MP3 playback --- 3rdParty/SDL_audiolib/CMakeLists.txt | 2 +- Source/sound.cpp | 51 ++++++++++++++++++++++------ Source/utils/soundsample.cpp | 37 ++++++++++++++------ Source/utils/soundsample.h | 12 ++++--- 4 files changed, 76 insertions(+), 26 deletions(-) diff --git a/3rdParty/SDL_audiolib/CMakeLists.txt b/3rdParty/SDL_audiolib/CMakeLists.txt index 0cafb82fe..039f9babe 100644 --- a/3rdParty/SDL_audiolib/CMakeLists.txt +++ b/3rdParty/SDL_audiolib/CMakeLists.txt @@ -27,7 +27,7 @@ set(USE_DEC_FLUIDSYNTH OFF) set(USE_DEC_BASSMIDI OFF) set(USE_DEC_WILDMIDI OFF) set(USE_DEC_ADLMIDI OFF) -set(USE_DEC_DRMP3 OFF) +set(USE_DEC_DRMP3 ON) include(FetchContent) FetchContent_Declare(SDL_audiolib diff --git a/Source/sound.cpp b/Source/sound.cpp index 2467aa86d..efb552b59 100644 --- a/Source/sound.cpp +++ b/Source/sound.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -45,7 +46,22 @@ std::optional music; std::unique_ptr musicBuffer; #endif -void LoadMusic(SDL_RWops *handle) +std::unique_ptr CreateDecoder(bool isMp3) +{ + if (isMp3) + return std::make_unique(); + return std::make_unique(); +} + +std::string GetMp3Path(const char *path) +{ + std::string mp3Path = path; + const std::string::size_type dot = mp3Path.find_last_of('.'); + mp3Path.replace(dot + 1, mp3Path.size() - (dot + 1), "mp3"); + return mp3Path; +} + +void LoadMusic(SDL_RWops *handle, bool isMp3) { #ifdef DISABLE_STREAMING_MUSIC size_t bytestoread = SDL_RWsize(handle); @@ -55,7 +71,7 @@ void LoadMusic(SDL_RWops *handle) handle = SDL_RWFromConstMem(musicBuffer.get(), bytestoread); #endif - music.emplace(handle, std::make_unique(), + music.emplace(handle, CreateDecoder(isMp3), std::make_unique(*sgOptions.Audio.resamplingQuality), /*closeRw=*/true); } @@ -151,28 +167,36 @@ void snd_play_snd(TSnd *pSnd, int lVolume, int lPan) std::unique_ptr sound_file_load(const char *path, bool stream) { - auto snd = std::make_unique(); snd->start_tc = SDL_GetTicks() - 80 - 1; #ifndef STREAM_ALL_AUDIO if (stream) { #endif - if (snd->DSB.SetChunkStream(path) != 0) { - ErrSdl(); + if (snd->DSB.SetChunkStream(GetMp3Path(path), /*isMp3=*/true, /*logErrors=*/false) != 0) { + SDL_ClearError(); + if (snd->DSB.SetChunkStream(path, /*isMp3=*/false, /*logErrors=*/true) != 0) { + ErrSdl(); + } } #ifndef STREAM_ALL_AUDIO } else { - SDL_RWops *file = OpenAsset(path); + bool isMp3 = true; + SDL_RWops *file = OpenAsset(GetMp3Path(path).c_str()); if (file == nullptr) { - ErrDlg("OpenAsset failed", path, __FILE__, __LINE__); + SDL_ClearError(); + isMp3 = false; + file = OpenAsset(path); + if (file == nullptr) { + ErrDlg("OpenAsset failed", path, __FILE__, __LINE__); + } } size_t dwBytes = SDL_RWsize(file); auto waveFile = MakeArraySharedPtr(dwBytes); if (SDL_RWread(file, waveFile.get(), dwBytes, 1) == 0) { ErrDlg("Failed to read file", fmt::format("{}: {}", path, SDL_GetError()), __FILE__, __LINE__); } - int error = snd->DSB.SetChunk(waveFile, dwBytes); + int error = snd->DSB.SetChunk(waveFile, dwBytes, isMp3); SDL_RWclose(file); if (error != 0) { ErrSdl(); @@ -245,9 +269,16 @@ void music_start(uint8_t nTrack) #else const bool threadsafe = true; #endif - SDL_RWops *handle = OpenAsset(trackPath, threadsafe); + bool isMp3 = true; + SDL_RWops *handle = OpenAsset(GetMp3Path(trackPath).c_str()); + if (handle == nullptr) { + SDL_ClearError(); + handle = OpenAsset(trackPath, threadsafe); + isMp3 = false; + } + if (handle != nullptr) { - LoadMusic(handle); + LoadMusic(handle, isMp3); if (!music->open()) { LogError(LogCategory::Audio, "Aulib::Stream::open (from music_start): {}", SDL_GetError()); CleanupMusic(); diff --git a/Source/utils/soundsample.cpp b/Source/utils/soundsample.cpp index 50ea6450f..6b9c721e6 100644 --- a/Source/utils/soundsample.cpp +++ b/Source/utils/soundsample.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -56,6 +57,19 @@ float PanLogToLinear(int logPan) return copysign(1.F - factor, static_cast(logPan)); } +std::unique_ptr CreateDecoder(bool isMp3) +{ + if (isMp3) + return std::make_unique(); + return std::make_unique(); +} + +std::unique_ptr CreateStream(SDL_RWops *handle, bool isMp3) +{ + return std::make_unique(handle, CreateDecoder(isMp3), + std::make_unique(*sgOptions.Audio.resamplingQuality), /*closeRw=*/true); +} + } // namespace float VolumeLogToLinear(int logVolume, int logMin, int logMax) @@ -113,28 +127,30 @@ void SoundSample::Stop() stream_->stop(); } -int SoundSample::SetChunkStream(std::string filePath) +int SoundSample::SetChunkStream(std::string filePath, bool isMp3, bool logErrors) { - file_path_ = std::move(filePath); - SDL_RWops *handle = OpenAsset(file_path_.c_str(), /*threadsafe=*/true); + SDL_RWops *handle = OpenAsset(filePath.c_str(), /*threadsafe=*/true); if (handle == nullptr) { - LogError(LogCategory::Audio, "OpenAsset failed (from SoundSample::SetChunkStream): {}", SDL_GetError()); + if (logErrors) + LogError(LogCategory::Audio, "OpenAsset failed (from SoundSample::SetChunkStream): {}", SDL_GetError()); return -1; } - - stream_ = std::make_unique(handle, std::make_unique(), - std::make_unique(*sgOptions.Audio.resamplingQuality), /*closeRw=*/true); + file_path_ = std::move(filePath); + isMp3_ = isMp3; + stream_ = CreateStream(handle, isMp3_); if (!stream_->open()) { stream_ = nullptr; - LogError(LogCategory::Audio, "Aulib::Stream::open (from SoundSample::SetChunkStream): {}", SDL_GetError()); + if (logErrors) + LogError(LogCategory::Audio, "Aulib::Stream::open (from SoundSample::SetChunkStream): {}", SDL_GetError()); return -1; } return 0; } #ifndef STREAM_ALL_AUDIO -int SoundSample::SetChunk(ArraySharedPtr fileData, std::size_t dwBytes) +int SoundSample::SetChunk(ArraySharedPtr fileData, std::size_t dwBytes, bool isMp3) { + isMp3_ = isMp3; file_data_ = std::move(fileData); file_data_size_ = dwBytes; SDL_RWops *buf = SDL_RWFromConstMem(file_data_.get(), dwBytes); @@ -142,8 +158,7 @@ int SoundSample::SetChunk(ArraySharedPtr fileData, std::size_t dwB return -1; } - stream_ = std::make_unique(buf, std::make_unique(), - std::make_unique(*sgOptions.Audio.resamplingQuality), /*closeRw=*/true); + stream_ = CreateStream(buf, isMp3_); if (!stream_->open()) { stream_ = nullptr; file_data_ = nullptr; diff --git a/Source/utils/soundsample.h b/Source/utils/soundsample.h index 398654919..d3c27c436 100644 --- a/Source/utils/soundsample.h +++ b/Source/utils/soundsample.h @@ -29,7 +29,9 @@ public: bool IsPlaying(); void Play(int logSoundVolume, int logUserVolume, int logPan); void Stop(); - int SetChunkStream(std::string filePath); + + // Returns 0 on success. + int SetChunkStream(std::string filePath, bool isMp3, bool logErrors = true); void SetFinishCallback(Aulib::Stream::Callback &&callback) { @@ -43,7 +45,7 @@ public: * @param dwBytes Length of buffer * @return 0 on success, -1 otherwise */ - int SetChunk(ArraySharedPtr fileData, std::size_t dwBytes); + int SetChunk(ArraySharedPtr fileData, std::size_t dwBytes, bool isMp3); #endif #ifndef STREAM_ALL_AUDIO @@ -59,8 +61,8 @@ public: return SetChunkStream(other.file_path_); #else if (other.IsStreaming()) - return SetChunkStream(other.file_path_); - return SetChunk(other.file_data_, other.file_data_size_); + return SetChunkStream(other.file_path_, isMp3_); + return SetChunk(other.file_data_, other.file_data_size_, isMp3_); #endif } @@ -76,6 +78,8 @@ private: // Set for streaming audio to allow for duplicating it: std::string file_path_; + bool isMp3_; + std::unique_ptr stream_; };