diff --git a/include/scratchcpp/iengine.h b/include/scratchcpp/iengine.h index 105b9f27..8c0870b4 100644 --- a/include/scratchcpp/iengine.h +++ b/include/scratchcpp/iengine.h @@ -87,6 +87,12 @@ class LIBSCRATCHCPP_EXPORT IEngine /*! Stops all currently playing sounds. */ virtual void stopSounds() = 0; + /*! Returns the global volume of all sounds (in %). */ + virtual double globalVolume() const = 0; + + /*! Sets the global volume of all sounds (in %). */ + virtual void setGlobalVolume(double volume) = 0; + /*! Updates the values of stage monitors. */ virtual void updateMonitors() = 0; diff --git a/src/audio/CMakeLists.txt b/src/audio/CMakeLists.txt index 5bf4e0b3..536c266b 100644 --- a/src/audio/CMakeLists.txt +++ b/src/audio/CMakeLists.txt @@ -15,6 +15,7 @@ endif() target_sources(scratchcpp-audio PUBLIC + iaudioengine.h iaudioplayer.h iaudiooutput.h audiooutput.cpp @@ -38,6 +39,8 @@ if (LIBSCRATCHCPP_AUDIO_SUPPORT) else() target_sources(scratchcpp-audio PUBLIC + internal/audioenginestub.cpp + internal/audioenginestub.h internal/audioplayerstub.cpp internal/audioplayerstub.h internal/audioloudnessstub.cpp diff --git a/src/audio/iaudioengine.h b/src/audio/iaudioengine.h new file mode 100644 index 00000000..8100334b --- /dev/null +++ b/src/audio/iaudioengine.h @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include + +namespace libscratchcpp +{ + +class IAudioEngine +{ + public: + virtual ~IAudioEngine() { } + + static IAudioEngine *instance(); + + virtual float volume() const = 0; + virtual void setVolume(float volume) = 0; +}; + +} // namespace libscratchcpp diff --git a/src/audio/internal/audioengine.cpp b/src/audio/internal/audioengine.cpp index 710a394e..1af3874d 100644 --- a/src/audio/internal/audioengine.cpp +++ b/src/audio/internal/audioengine.cpp @@ -1,6 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 #include +#include #include "audioengine.h" @@ -8,12 +9,17 @@ using namespace libscratchcpp; AudioEngine AudioEngine::instance; +IAudioEngine *IAudioEngine::instance() +{ + return &AudioEngine::instance; +} + ma_engine *AudioEngine::engine() { if (!instance.m_initialized) instance.init(); - return instance.m_initialized ? &instance.m_engine : nullptr; + return instance.m_initialized ? instance.m_engine : nullptr; } bool AudioEngine::initialized() @@ -24,20 +30,36 @@ bool AudioEngine::initialized() return instance.m_initialized; } +float AudioEngine::volume() const +{ + return m_volume; +} + +void AudioEngine::setVolume(float volume) +{ + m_volume = volume; + + if (m_initialized) + ma_engine_set_volume(m_engine, volume); +} + AudioEngine::AudioEngine() { } AudioEngine::~AudioEngine() { - if (m_initialized) - ma_engine_uninit(&m_engine); + if (m_initialized && m_engine) { + ma_engine_uninit(m_engine); + delete m_engine; + } } void AudioEngine::init() { ma_result result; - result = ma_engine_init(NULL, &m_engine); + m_engine = new ma_engine; + result = ma_engine_init(NULL, m_engine); if (result != MA_SUCCESS) { std::cerr << "Failed to initialize audio engine." << std::endl; diff --git a/src/audio/internal/audioengine.h b/src/audio/internal/audioengine.h index 49097871..02672ff3 100644 --- a/src/audio/internal/audioengine.h +++ b/src/audio/internal/audioengine.h @@ -3,18 +3,26 @@ #pragma once #include -#include + +#include "../iaudioengine.h" + +struct ma_engine; namespace libscratchcpp { // This is a singleton which initializes and uninitializes the miniaudio engine -class AudioEngine +class AudioEngine : public IAudioEngine { public: + friend class IAudioEngine; + static ma_engine *engine(); static bool initialized(); + float volume() const override; + void setVolume(float volume) override; + private: AudioEngine(); ~AudioEngine(); @@ -22,8 +30,10 @@ class AudioEngine void init(); static AudioEngine instance; - ma_engine m_engine; + + ma_engine *m_engine = nullptr; bool m_initialized = false; + float m_volume = 1.0f; }; } // namespace libscratchcpp diff --git a/src/audio/internal/audioenginestub.cpp b/src/audio/internal/audioenginestub.cpp new file mode 100644 index 00000000..83b8bd45 --- /dev/null +++ b/src/audio/internal/audioenginestub.cpp @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 + +#include "audioenginestub.h" + +using namespace libscratchcpp; + +AudioEngineStub AudioEngineStub::instance; + +IAudioEngine *IAudioEngine::instance() +{ + return &AudioEngineStub::instance; +} + +AudioEngineStub::AudioEngineStub() +{ +} + +float AudioEngineStub::volume() const +{ + return m_volume; +} + +void AudioEngineStub::setVolume(float volume) +{ + m_volume = volume; +} diff --git a/src/audio/internal/audioenginestub.h b/src/audio/internal/audioenginestub.h new file mode 100644 index 00000000..e49e076c --- /dev/null +++ b/src/audio/internal/audioenginestub.h @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "../iaudioengine.h" + +namespace libscratchcpp +{ + +class AudioEngineStub : public IAudioEngine +{ + public: + friend class IAudioEngine; + AudioEngineStub(); + + float volume() const override; + void setVolume(float volume) override; + + private: + static AudioEngineStub instance; + float m_volume = 1.0f; +}; + +} // namespace libscratchcpp diff --git a/src/audio/internal/audioplayer.cpp b/src/audio/internal/audioplayer.cpp index e7cc03a5..4bb4922d 100644 --- a/src/audio/internal/audioplayer.cpp +++ b/src/audio/internal/audioplayer.cpp @@ -2,6 +2,7 @@ #include #include +#include #include "audioplayer.h" #include "audioengine.h" diff --git a/src/engine/internal/engine.cpp b/src/engine/internal/engine.cpp index a370a68e..4c06ac5d 100644 --- a/src/engine/internal/engine.cpp +++ b/src/engine/internal/engine.cpp @@ -26,6 +26,7 @@ #include "blocksectioncontainer.h" #include "timer.h" #include "clock.h" +#include "audio/iaudioengine.h" #include "blocks/standardblocks.h" #include "blocks/variableblocks.h" #include "blocks/listblocks.h" @@ -46,7 +47,8 @@ const std::unordered_map Engine::m_hatEdgeActivated = { Engine::Engine() : m_defaultTimer(std::make_unique()), m_timer(m_defaultTimer.get()), - m_clock(Clock::instance().get()) + m_clock(Clock::instance().get()), + m_audioEngine(IAudioEngine::instance()) { } @@ -469,6 +471,16 @@ void Engine::stopSounds() } } +double Engine::globalVolume() const +{ + return m_audioEngine->volume() * 100; +} + +void Engine::setGlobalVolume(double volume) +{ + m_audioEngine->setVolume(volume / 100); +} + void Engine::updateMonitors() { // Execute the "script" of each visible monitor diff --git a/src/engine/internal/engine.h b/src/engine/internal/engine.h index 76319ac0..348ee2d2 100644 --- a/src/engine/internal/engine.h +++ b/src/engine/internal/engine.h @@ -19,6 +19,7 @@ namespace libscratchcpp class Entity; class IClock; +class IAudioEngine; class Engine : public IEngine { @@ -41,7 +42,10 @@ class Engine : public IEngine void stopTarget(Target *target, VirtualMachine *exceptScript) override; void initClone(std::shared_ptr clone) override; void deinitClone(std::shared_ptr clone) override; + void stopSounds() override; + virtual double globalVolume() const override; + virtual void setGlobalVolume(double volume) override; void updateMonitors() override; void step() override; @@ -163,6 +167,7 @@ class Engine : public IEngine void setUserAgent(const std::string &agent) override; IClock *m_clock = nullptr; + IAudioEngine *m_audioEngine = nullptr; private: enum class HatType diff --git a/test/audio/CMakeLists.txt b/test/audio/CMakeLists.txt index c4aba7b4..40897a6e 100644 --- a/test/audio/CMakeLists.txt +++ b/test/audio/CMakeLists.txt @@ -1,3 +1,21 @@ +# audioengine_test +add_executable( + audioengine_test + audioengine_test.cpp +) + +target_link_libraries( + audioengine_test + GTest::gtest_main + scratchcpp +) + +gtest_discover_tests(audioengine_test) + +if (LIBSCRATCHCPP_AUDIO_SUPPORT) + target_compile_definitions(audioengine_test PRIVATE LIBSCRATCHCPP_AUDIO_SUPPORT) +endif() + # audiooutput_test add_executable( audiooutput_test diff --git a/test/audio/audioengine_test.cpp b/test/audio/audioengine_test.cpp new file mode 100644 index 00000000..e0476858 --- /dev/null +++ b/test/audio/audioengine_test.cpp @@ -0,0 +1,31 @@ +#ifdef LIBSCRATCHCPP_AUDIO_SUPPORT +#include