From e46cb3e1a7b2d7d7854bc9e31b3da125488c8d90 Mon Sep 17 00:00:00 2001 From: Lefucjusz Date: Tue, 27 Feb 2024 15:11:42 +0100 Subject: [PATCH] [BH-1888] Fix device freezing when changing volume intensively Fix of the issue that intensive changing volume in Relaxation app would result in device freezing after some time of such activity. --- harmony_changelog.md | 1 + module-audio/Audio/decoder/DecoderFLAC.cpp | 22 +++++++--- module-audio/Audio/decoder/DecoderMP3.cpp | 19 +++++--- module-audio/Audio/decoder/DecoderMP3.hpp | 8 ++-- module-audio/Audio/decoder/DecoderWAV.cpp | 51 ++++++++++++++++------ module-audio/Audio/decoder/DecoderWAV.hpp | 25 +++++++++++ 6 files changed, 98 insertions(+), 28 deletions(-) diff --git a/harmony_changelog.md b/harmony_changelog.md index e52e4765276b5c6e4b447785a7fd0fb18c0a0ed7..efd870989f69463f6af24277361edfd8f8c6a07d 100644 --- a/harmony_changelog.md +++ b/harmony_changelog.md @@ -4,6 +4,7 @@ ### Fixed * Fixed BIWIN eMMC memory errors +* Fixed freeze after changing Relaxation volume intensively ### Added * Added new 32px and 170px fonts diff --git a/module-audio/Audio/decoder/DecoderFLAC.cpp b/module-audio/Audio/decoder/DecoderFLAC.cpp index 5e83831bb167c81e06969f874259abac8646da28..46ba71650c8ad92558ee1b1e7bc8417cfe6ce234 100644 --- a/module-audio/Audio/decoder/DecoderFLAC.cpp +++ b/module-audio/Audio/decoder/DecoderFLAC.cpp @@ -10,6 +10,7 @@ #define DR_FLAC_NO_OGG #define DR_FLAC_NO_CRC #define DR_FLAC_NO_SIMD + #include namespace audio @@ -38,19 +39,21 @@ namespace audio drflac_close(flac); } - std::int32_t DecoderFLAC::decode(std::uint32_t samplesToRead, int16_t *pcmData) + std::int32_t DecoderFLAC::decode(std::uint32_t samplesToRead, std::int16_t *pcmData) { - if (!fileExists(fd)) { - LOG_WARN("File '%s' was deleted during playback!", filePath.c_str()); - return fileDeletedRetCode; - } - const auto samplesRead = drflac_read_pcm_frames_s16(flac, samplesToRead / channelCount, reinterpret_cast(pcmData)); if (samplesRead > 0) { /* Calculate frame duration in seconds */ position += static_cast(samplesRead) / static_cast(sampleRate); } + else if (!fileExists(fd)) { + /* Unfortunately this second check of file existence is needed + * to verify whether lack of new samples was caused by EOF or by + * deletion of the file. */ + LOG_WARN("File '%s' was deleted during playback!", filePath.c_str()); + return fileDeletedRetCode; + } return samplesRead * channelCount; } @@ -79,6 +82,13 @@ namespace audio std::size_t DecoderFLAC::drflacRead(void *pUserData, void *pBufferOut, std::size_t bytesToRead) { const auto decoderContext = reinterpret_cast(pUserData); + + /* Check if the file exists - std::fread happily returns bytesToRead if + * requested to read from deleted file, what causes decoding library + * to enter infinite loop of reading. */ + if (!fileExists(decoderContext->fd)) { + return 0; + } return std::fread(pBufferOut, 1, bytesToRead, decoderContext->fd); } diff --git a/module-audio/Audio/decoder/DecoderMP3.cpp b/module-audio/Audio/decoder/DecoderMP3.cpp index 3f25159e9ca096ba5208ba42e63ec9e4c58c40ef..917cebbf98e5fd09e79eb970d703f927b2b5e0fc 100644 --- a/module-audio/Audio/decoder/DecoderMP3.cpp +++ b/module-audio/Audio/decoder/DecoderMP3.cpp @@ -96,23 +96,32 @@ namespace audio std::int32_t DecoderMP3::decode(std::uint32_t samplesToRead, std::int16_t *pcmData) { - if (!fileExists(fd)) { - LOG_WARN("File '%s' was deleted during playback!", filePath.c_str()); - return fileDeletedRetCode; - } - const auto samplesRead = drmp3_read_pcm_frames_s16( mp3.get(), samplesToRead / channelCount, reinterpret_cast(pcmData)); if (samplesRead > 0) { /* Calculate frame duration in seconds */ position += static_cast(samplesRead) / static_cast(sampleRate); } + else if (!fileExists(fd)) { + /* Unfortunately this second check of file existence is needed + * to verify whether lack of new samples was caused by EOF or by + * deletion of the file. */ + LOG_WARN("File '%s' was deleted during playback!", filePath.c_str()); + return fileDeletedRetCode; + } return samplesRead * channelCount; } std::size_t DecoderMP3::drmp3Read(void *pUserData, void *pBufferOut, std::size_t bytesToRead) { const auto decoderContext = reinterpret_cast(pUserData); + + /* Check if the file exists - std::fread happily returns bytesToRead if + * requested to read from deleted file, what causes decoding library + * to enter infinite loop of reading. */ + if (!fileExists(decoderContext->fd)) { + return 0; + } return std::fread(pBufferOut, 1, bytesToRead, decoderContext->fd); } diff --git a/module-audio/Audio/decoder/DecoderMP3.hpp b/module-audio/Audio/decoder/DecoderMP3.hpp index 10ca12d61b1b191e4bb4bdd8968ba21d188aa62a..099dd99b374fec223582773e3bd6757413522764 100644 --- a/module-audio/Audio/decoder/DecoderMP3.hpp +++ b/module-audio/Audio/decoder/DecoderMP3.hpp @@ -23,7 +23,7 @@ namespace audio // Callback for when data needs to be read from the client. // - // pUserData [in] The user data that was passed to drflac_open() and family. + // pUserData [in] The user data that was passed to drmp3_init() and family. // pBufferOut [out] The output buffer. // bytesToRead [in] The number of bytes to read. // @@ -35,15 +35,15 @@ namespace audio // Callback for when data needs to be seeked. // - // pUserData [in] The user data that was passed to drflac_open() and family. + // pUserData [in] The user data that was passed to drmp3_init() and family. // offset [in] The number of bytes to move, relative to the origin. Will never be negative. // origin [in] The origin of the seek - the current position or the start of the stream. // // Returns whether the seek was successful. // // The offset will never be negative. Whether it is relative to the beginning or current position is - // determined by the "origin" parameter which will be either drflac_seek_origin_start or - // drflac_seek_origin_current. + // determined by the "origin" parameter which will be either drmp3_seek_origin_start or + // drmp3_seek_origin_current. static drmp3_bool32 drmp3Seek(void *pUserData, int offset, drmp3_seek_origin origin); }; } // namespace audio diff --git a/module-audio/Audio/decoder/DecoderWAV.cpp b/module-audio/Audio/decoder/DecoderWAV.cpp index 7c7bfb10608f5a53ffa72c5947d3effaaab9396c..0dc09cd9ad005f11baabaf2236cd7bbeda5f32a0 100644 --- a/module-audio/Audio/decoder/DecoderWAV.cpp +++ b/module-audio/Audio/decoder/DecoderWAV.cpp @@ -7,13 +7,15 @@ #include #define DR_WAV_IMPLEMENTATION +#define DR_MP3_NO_STDIO + #include namespace audio { DecoderWAV::DecoderWAV(const std::string &filePath) : Decoder(filePath), wav(std::make_unique()) { - if (drwav_init_file(wav.get(), filePath.c_str(), nullptr) == DRWAV_FALSE) { + if (drwav_init(wav.get(), drwavRead, drwavSeek, this, nullptr) == DRWAV_FALSE) { LOG_ERROR("Unable to init WAV decoder"); return; } @@ -32,16 +34,23 @@ namespace audio } } - std::int32_t DecoderWAV::decode(std::uint32_t samplesToRead, std::int16_t *pcmData) + void DecoderWAV::setPosition(float pos) { if (!isInitialized) { LOG_ERROR("WAV decoder not initialized"); - return 0; + return; } + drwav_seek_to_pcm_frame(wav.get(), wav->totalPCMFrameCount * pos); - if (!fileExists(fd)) { - LOG_WARN("File '%s' was deleted during playback!", filePath.c_str()); - return fileDeletedRetCode; + /* Calculate new position */ + position = static_cast(wav->totalPCMFrameCount) * pos / static_cast(sampleRate); + } + + std::int32_t DecoderWAV::decode(std::uint32_t samplesToRead, std::int16_t *pcmData) + { + if (!isInitialized) { + LOG_ERROR("WAV decoder not initialized"); + return 0; } const auto samplesRead = drwav_read_pcm_frames_s16(wav.get(), samplesToRead / channelCount, pcmData); @@ -49,18 +58,34 @@ namespace audio /* Calculate frame duration in seconds */ position += static_cast(samplesRead) / static_cast(sampleRate); } + else if (!fileExists(fd)) { + /* Unfortunately this second check of file existence is needed + * to verify whether lack of new samples was caused by EOF or by + * deletion of the file. */ + LOG_WARN("File '%s' was deleted during playback!", filePath.c_str()); + return fileDeletedRetCode; + } return samplesRead * channelCount; } - void DecoderWAV::setPosition(float pos) + std::size_t DecoderWAV::drwavRead(void *pUserData, void *pBufferOut, std::size_t bytesToRead) { - if (!isInitialized) { - LOG_ERROR("WAV decoder not initialized"); - return; + const auto decoderContext = reinterpret_cast(pUserData); + + /* Check if the file exists - std::fread happily returns bytesToRead if + * requested to read from deleted file, what causes decoding library + * to enter infinite loop of reading. */ + if (!fileExists(decoderContext->fd)) { + return 0; } - drwav_seek_to_pcm_frame(wav.get(), wav->totalPCMFrameCount * pos); + return std::fread(pBufferOut, 1, bytesToRead, decoderContext->fd); + } - /* Calculate new position */ - position = static_cast(wav->totalPCMFrameCount) * pos / static_cast(sampleRate); + drwav_bool32 DecoderWAV::drwavSeek(void *pUserData, int offset, drwav_seek_origin origin) + { + const auto decoderContext = reinterpret_cast(pUserData); + const auto seekError = + std::fseek(decoderContext->fd, offset, origin == drwav_seek_origin_start ? SEEK_SET : SEEK_CUR); + return (seekError == 0) ? DRWAV_TRUE : DRWAV_FALSE; } } // namespace audio diff --git a/module-audio/Audio/decoder/DecoderWAV.hpp b/module-audio/Audio/decoder/DecoderWAV.hpp index 8b40da80f5811be2f6b14be94b0c3de1af86ca1e..d164b253832eb8ba7ea9b14744492b1756059c32 100644 --- a/module-audio/Audio/decoder/DecoderWAV.hpp +++ b/module-audio/Audio/decoder/DecoderWAV.hpp @@ -20,5 +20,30 @@ namespace audio private: std::unique_ptr wav; + + // Callback for when data needs to be read from the client. + // + // pUserData [in] The user data that was passed to drwav_open() and family. + // pBufferOut [out] The output buffer. + // bytesToRead [in] The number of bytes to read. + // + // Returns the number of bytes actually read. + // + // A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback + // until either the entire bytesToRead is filled or you have reached the end of the stream. + static std::size_t drwavRead(void *pUserData, void *pBufferOut, std::size_t bytesToRead); + + // Callback for when data needs to be seeked. + // + // pUserData [in] The user data that was passed to drwav_init() and family. + // offset [in] The number of bytes to move, relative to the origin. Will never be negative. + // origin [in] The origin of the seek - the current position or the start of the stream. + // + // Returns whether the seek was successful. + // + // The offset will never be negative. Whether it is relative to the beginning or current position is + // determined by the "origin" parameter which will be either drwav_seek_origin_start or + // drwav_seek_origin_current. + static drwav_bool32 drwavSeek(void *pUserData, int offset, drwav_seek_origin origin); }; } // namespace audio