From 93575555b61422eabd62b5a950c0935aa3694946 Mon Sep 17 00:00:00 2001 From: Miron Alexandru Date: Wed, 30 Apr 2025 01:49:10 +0300 Subject: [PATCH] Allow overriding protected methods on sfMusic and sfSoundStream --- .github/workflows/ci.yml | 1 + include/CSFML/Audio/Music.h | 51 ++++++++++++ include/CSFML/Audio/SoundStream.h | 22 ++++- src/CSFML/Audio/CMakeLists.txt | 2 + src/CSFML/Audio/Music.cpp | 22 +++++ src/CSFML/Audio/MusicStruct.cpp | 111 ++++++++++++++++++++++++++ src/CSFML/Audio/MusicStruct.hpp | 13 +++ src/CSFML/Audio/SoundStream.cpp | 7 ++ src/CSFML/Audio/SoundStreamStruct.cpp | 53 ++++++++++++ src/CSFML/Audio/SoundStreamStruct.hpp | 6 +- 10 files changed, 285 insertions(+), 3 deletions(-) create mode 100644 src/CSFML/Audio/MusicStruct.cpp create mode 100644 src/CSFML/Audio/SoundStreamStruct.cpp diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6e4f637e..7e6883eb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,6 +42,7 @@ jobs: - name: Get CMake and Ninja uses: lukka/get-cmake@latest with: + cmakeVersion: ${{ runner.os == 'Windows' && '3.25' || '3.22' }} ninjaVersion: latest - name: Install Linux Dependencies diff --git a/include/CSFML/Audio/Music.h b/include/CSFML/Audio/Music.h index b0b5e490..4c2f8991 100644 --- a/include/CSFML/Audio/Music.h +++ b/include/CSFML/Audio/Music.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,16 @@ #include +typedef bool (*sfMusicOnGetDataOriginal)(sfMusic* music, sfSoundStreamChunk* data); ///< sf::Music::onGetData callback +typedef bool (*sfMusicOnGetDataMixin)(sfMusicOnGetDataOriginal originalImpl, sfMusic* music, sfSoundStreamChunk* data); ///< sf::Music::onGetData mixin + +typedef void (*sfMusicOnSeekOriginal)(sfMusic* music, sfTime data); ///< sf::Music::onSeek callback +typedef void (*sfMusicOnSeekMixin)(sfMusicOnSeekOriginal originalImpl, sfMusic* music, sfTime timeOffset); ///< sf::Music::onSeek mixin + +typedef bool (*sfMusicOnLoopOriginal)(sfMusic* music, uint64_t* position); ///< sf::Music::onLoop callback +typedef bool (*sfMusicOnLoopMixin)(sfMusicOnLoopOriginal originalImpl, sfMusic* music, uint64_t* position); ///< sf::Music::onLoop mixin + + //////////////////////////////////////////////////////////// /// \brief Structure defining a time range /// @@ -52,6 +63,46 @@ typedef struct } sfTimeSpan; +//////////////////////////////////////////////////////////// +/// \brief Override the behaviour of requesting a new chunk +/// of audio samples from the stream source +/// +/// This function fills the chunk from the next samples +/// to read from the audio file. +/// +/// \param music Music object +/// \param mixin Method override to set, a null override restores the original behaviour +/// +//////////////////////////////////////////////////////////// +CSFML_AUDIO_API void sfMusic_setOnGetData(sfMusic* music, sfMusicOnGetDataMixin mixin); + + +//////////////////////////////////////////////////////////// +/// \brief Override the behaviour of changing the current +/// playing position in the stream source +/// +/// \param music Music object +/// \param mixin Method override to set, a null override restores the original behaviour +/// +//////////////////////////////////////////////////////////// +CSFML_AUDIO_API void sfMusic_setOnSeek(sfMusic* music, sfMusicOnSeekMixin mixin); + + +//////////////////////////////////////////////////////////// +/// \brief Override the behaviour of changing the current +/// playing position in the stream source to the loop offset +/// +/// This is called by the underlying `SoundStream` whenever it needs us to reset +/// the seek position for a loop. We then determine whether we are looping on a +/// loop point or the end-of-file, perform the seek, and return the new position. +/// +/// \param music Music object +/// \param mixin Method override to set, a null override restores the original behaviour +/// +//////////////////////////////////////////////////////////// +CSFML_AUDIO_API void sfMusic_setOnLoop(sfMusic* music, sfMusicOnLoopMixin mixin); + + //////////////////////////////////////////////////////////// /// \brief Create a new music and load it from a file /// diff --git a/include/CSFML/Audio/SoundStream.h b/include/CSFML/Audio/SoundStream.h index b927f820..07286c6f 100644 --- a/include/CSFML/Audio/SoundStream.h +++ b/include/CSFML/Audio/SoundStream.h @@ -40,20 +40,38 @@ #include +typedef bool (*sfSoundStreamOnLoopOriginal)(sfSoundStream* soundStream, uint64_t* position); ///< sf::SoundStream::onLoop callback +typedef bool (*sfSoundStreamOnLoopMixin)(sfSoundStreamOnLoopOriginal, sfSoundStream* soundStream, uint64_t* position); ///< sf::SoundStream::onLoop mixin + + //////////////////////////////////////////////////////////// /// \brief defines the data to fill by the OnGetData callback /// //////////////////////////////////////////////////////////// typedef struct { - int16_t* samples; ///< Pointer to the audio samples - unsigned int sampleCount; ///< Number of samples pointed by Samples + const int16_t* samples; ///< Pointer to the audio samples + size_t sampleCount; ///< Number of samples pointed by Samples } sfSoundStreamChunk; typedef bool (*sfSoundStreamGetDataCallback)(sfSoundStreamChunk*, void*); ///< Type of the callback used to get a sound stream data typedef void (*sfSoundStreamSeekCallback)(sfTime, void*); ///< Type of the callback used to seek in a sound stream +//////////////////////////////////////////////////////////// +/// \brief Override the behaviour of changing the current +/// playing position in the stream source to the loop offset +/// +/// This function can be overridden to allow implementation of +/// custom loop points. Otherwise, it just calls `onSeek(Time::Zero)` and returns 0. +/// +/// \param soundStream SoundStream object +/// \param mixin Method override to set, a null override restores the original behaviour +/// +//////////////////////////////////////////////////////////// +CSFML_AUDIO_API void sfSoundStream_setOnLoop(sfSoundStream* soundStream, sfSoundStreamOnLoopMixin mixin); + + //////////////////////////////////////////////////////////// /// \brief Create a new sound stream /// diff --git a/src/CSFML/Audio/CMakeLists.txt b/src/CSFML/Audio/CMakeLists.txt index eb7e4394..fff17f36 100644 --- a/src/CSFML/Audio/CMakeLists.txt +++ b/src/CSFML/Audio/CMakeLists.txt @@ -10,6 +10,7 @@ set(SRC ${INCROOT}/Listener.h ${SRCROOT}/Music.cpp ${SRCROOT}/MusicStruct.hpp + ${SRCROOT}/MusicStruct.cpp ${INCROOT}/Music.h ${SRCROOT}/Sound.cpp ${SRCROOT}/SoundStruct.hpp @@ -27,6 +28,7 @@ set(SRC ${INCROOT}/SoundSourceCone.h ${INCROOT}/SoundStatus.h ${SRCROOT}/SoundStream.cpp + ${SRCROOT}/SoundStreamStruct.cpp ${SRCROOT}/SoundStreamStruct.hpp ${INCROOT}/SoundStream.h ${INCROOT}/Types.h diff --git a/src/CSFML/Audio/Music.cpp b/src/CSFML/Audio/Music.cpp index 04e30658..f6f5f309 100644 --- a/src/CSFML/Audio/Music.cpp +++ b/src/CSFML/Audio/Music.cpp @@ -34,6 +34,28 @@ #include + +//////////////////////////////////////////////////////////// +void sfMusic_setOnGetData(sfMusic* music, sfMusicOnGetDataMixin mixin) +{ + music->OnGetDataMixin = mixin; +} + + +//////////////////////////////////////////////////////////// +void sfMusic_setOnSeek(sfMusic* music, sfMusicOnSeekMixin mixin) +{ + music->OnSeekMixin = mixin; +} + + +//////////////////////////////////////////////////////////// +void sfMusic_setOnLoop(sfMusic* music, sfMusicOnLoopMixin mixin) +{ + music->OnLoopMixin = mixin; +} + + //////////////////////////////////////////////////////////// sfMusic* sfMusic_createFromFile(const char* filename) { diff --git a/src/CSFML/Audio/MusicStruct.cpp b/src/CSFML/Audio/MusicStruct.cpp new file mode 100644 index 00000000..59a03952 --- /dev/null +++ b/src/CSFML/Audio/MusicStruct.cpp @@ -0,0 +1,111 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2024 Laurent Gomila (laurent@sfml-dev.org) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + + +//////////////////////////////////////////////////////////// +bool sfMusic::onGetDataOriginal(sfMusic* music, sfSoundStreamChunk* data) +{ + sf::SoundStream::Chunk cppData; + + cppData.samples = data->samples; + cppData.sampleCount = data->sampleCount; + + bool result = music->sf::Music::onGetData(cppData); + + data->samples = cppData.samples; + data->sampleCount = cppData.sampleCount; + + return result; +} + + +//////////////////////////////////////////////////////////// +bool sfMusic::onGetData(sf::SoundStream::Chunk& data) +{ + if (!OnGetDataMixin) + return sf::Music::onGetData(data); + + sfSoundStreamChunk cData; + + cData.samples = data.samples; + cData.sampleCount = data.sampleCount; + + bool result = OnGetDataMixin(onGetDataOriginal, this, &cData); + + data.samples = cData.samples; + data.sampleCount = cData.sampleCount; + + return result; +} + + +//////////////////////////////////////////////////////////// +void sfMusic::onSeekOriginal(sfMusic* music, sfTime timeOffset) +{ + return music->sf::Music::onSeek(sf::microseconds(timeOffset.microseconds)); +} + + +//////////////////////////////////////////////////////////// +void sfMusic::onSeek(sf::Time timeOffset) +{ + if (!OnSeekMixin) + return sf::Music::onSeek(timeOffset); + + return OnSeekMixin(onSeekOriginal, this, {timeOffset.asMicroseconds()}); +} + + +//////////////////////////////////////////////////////////// +bool sfMusic::onLoopOriginal(sfMusic* music, uint64_t* position) +{ + auto result = music->sf::Music::onLoop(); + + if (!result) + { + return false; + } + + *position = *result; + return true; +} + + +//////////////////////////////////////////////////////////// +std::optional sfMusic::onLoop() +{ + if (!OnLoopMixin) + return sf::Music::onLoop(); + + std::uint64_t data = 0; + + bool noLoop = OnLoopMixin(onLoopOriginal, this, &data); + + return noLoop ? std::nullopt : std::optional(data); +} \ No newline at end of file diff --git a/src/CSFML/Audio/MusicStruct.hpp b/src/CSFML/Audio/MusicStruct.hpp index 425f6c86..17de446b 100644 --- a/src/CSFML/Audio/MusicStruct.hpp +++ b/src/CSFML/Audio/MusicStruct.hpp @@ -27,6 +27,7 @@ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// +#include #include #include @@ -42,4 +43,16 @@ struct sfMusic : sf::Music { mutable std::vector Channels; CallbackStream Stream; + + sfMusicOnGetDataMixin OnGetDataMixin = nullptr; + static bool onGetDataOriginal(sfMusic* music, sfSoundStreamChunk* data); + bool onGetData(Chunk& data) override; + + sfMusicOnSeekMixin OnSeekMixin = nullptr; + static void onSeekOriginal(sfMusic* music, sfTime timeOffset); + void onSeek(sf::Time timeOffset) override; + + sfMusicOnLoopMixin OnLoopMixin = nullptr; + static bool onLoopOriginal(sfMusic* music, uint64_t* position); + std::optional onLoop() override; }; diff --git a/src/CSFML/Audio/SoundStream.cpp b/src/CSFML/Audio/SoundStream.cpp index 80c8cb18..5817b2cd 100644 --- a/src/CSFML/Audio/SoundStream.cpp +++ b/src/CSFML/Audio/SoundStream.cpp @@ -33,6 +33,13 @@ #include +//////////////////////////////////////////////////////////// +void sfSoundStream_setOnLoop(sfSoundStream* soundStream, sfSoundStreamOnLoopMixin mixin) +{ + soundStream->OnLoopMixin = mixin; +} + + //////////////////////////////////////////////////////////// sfSoundStream* sfSoundStream_create( sfSoundStreamGetDataCallback onGetData, diff --git a/src/CSFML/Audio/SoundStreamStruct.cpp b/src/CSFML/Audio/SoundStreamStruct.cpp new file mode 100644 index 00000000..40178db8 --- /dev/null +++ b/src/CSFML/Audio/SoundStreamStruct.cpp @@ -0,0 +1,53 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2024 Laurent Gomila (laurent@sfml-dev.org) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +bool sfSoundStream::onLoopOriginal(sfSoundStream* music, uint64_t* position) +{ + auto result = music->sf::SoundStream::onLoop(); + + if (!result) + { + return false; + } + + *position = *result; + return true; +} + +std::optional sfSoundStream::onLoop() +{ + if (!OnLoopMixin) + return sf::SoundStream::onLoop(); + + std::uint64_t data = 0; + + bool noLoop = OnLoopMixin(onLoopOriginal, this, &data); + + return noLoop ? std::nullopt : std::optional(data); +} \ No newline at end of file diff --git a/src/CSFML/Audio/SoundStreamStruct.hpp b/src/CSFML/Audio/SoundStreamStruct.hpp index eaa289ac..c62cf37b 100644 --- a/src/CSFML/Audio/SoundStreamStruct.hpp +++ b/src/CSFML/Audio/SoundStreamStruct.hpp @@ -28,6 +28,7 @@ // Headers //////////////////////////////////////////////////////////// #include +#include #include @@ -57,7 +58,6 @@ struct sfSoundStream : sf::SoundStream mutable std::vector Channels; -private: bool onGetData(Chunk& data) override { sfSoundStreamChunk chunk = {nullptr, 0}; @@ -78,6 +78,10 @@ struct sfSoundStream : sf::SoundStream } } + sfSoundStreamOnLoopMixin OnLoopMixin = nullptr; + static bool onLoopOriginal(sfSoundStream* music, uint64_t* position); + std::optional onLoop() override; + sfSoundStreamGetDataCallback myGetDataCallback; sfSoundStreamSeekCallback mySeekCallback; void* myUserData;