Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions include/scratchcpp/iengine.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
3 changes: 3 additions & 0 deletions src/audio/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ endif()

target_sources(scratchcpp-audio
PUBLIC
iaudioengine.h
iaudioplayer.h
iaudiooutput.h
audiooutput.cpp
Expand All @@ -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
Expand Down
21 changes: 21 additions & 0 deletions src/audio/iaudioengine.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: Apache-2.0

#pragma once

#include <memory>

namespace libscratchcpp
{

class IAudioEngine
{
public:
virtual ~IAudioEngine() { }

static IAudioEngine *instance();

virtual float volume() const = 0;
virtual void setVolume(float volume) = 0;
};

} // namespace libscratchcpp
30 changes: 26 additions & 4 deletions src/audio/internal/audioengine.cpp
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
// SPDX-License-Identifier: Apache-2.0

#include <iostream>
#include <miniaudio.h>

#include "audioengine.h"

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()
Expand All @@ -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;
Expand Down
16 changes: 13 additions & 3 deletions src/audio/internal/audioengine.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,37 @@
#pragma once

#include <memory>
#include <miniaudio.h>

#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();

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
26 changes: 26 additions & 0 deletions src/audio/internal/audioenginestub.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
24 changes: 24 additions & 0 deletions src/audio/internal/audioenginestub.h
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions src/audio/internal/audioplayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <iostream>
#include <cassert>
#include <miniaudio.h>

#include "audioplayer.h"
#include "audioengine.h"
Expand Down
14 changes: 13 additions & 1 deletion src/engine/internal/engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -46,7 +47,8 @@ const std::unordered_map<Engine::HatType, bool> Engine::m_hatEdgeActivated = {
Engine::Engine() :
m_defaultTimer(std::make_unique<Timer>()),
m_timer(m_defaultTimer.get()),
m_clock(Clock::instance().get())
m_clock(Clock::instance().get()),
m_audioEngine(IAudioEngine::instance())
{
}

Expand Down Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions src/engine/internal/engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ namespace libscratchcpp

class Entity;
class IClock;
class IAudioEngine;

class Engine : public IEngine
{
Expand All @@ -41,7 +42,10 @@ class Engine : public IEngine
void stopTarget(Target *target, VirtualMachine *exceptScript) override;
void initClone(std::shared_ptr<Sprite> clone) override;
void deinitClone(std::shared_ptr<Sprite> clone) override;

void stopSounds() override;
virtual double globalVolume() const override;
virtual void setGlobalVolume(double volume) override;

void updateMonitors() override;
void step() override;
Expand Down Expand Up @@ -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
Expand Down
18 changes: 18 additions & 0 deletions test/audio/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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
Expand Down
31 changes: 31 additions & 0 deletions test/audio/audioengine_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#ifdef LIBSCRATCHCPP_AUDIO_SUPPORT
#include <audio/internal/audioengine.h>
#else
#include <audio/internal/audioenginestub.h>
#endif

#include "../common.h"

using namespace libscratchcpp;

TEST(AudioEngineTest, Instance)
{
IAudioEngine *instance = IAudioEngine::instance();
ASSERT_TRUE(instance);

#ifdef LIBSCRATCHCPP_AUDIO_SUPPORT
ASSERT_TRUE(dynamic_cast<AudioEngine *>(instance));
#else
ASSERT_TRUE(dynamic_cast<AudioEngineStub *>(instance));
#endif
}

TEST(AudioEngineTest, Volume)
{
IAudioEngine *engine = IAudioEngine::instance();
ASSERT_TRUE(engine);
ASSERT_EQ(engine->volume(), 1.0f);

engine->setVolume(0.86f);
ASSERT_EQ(engine->volume(), 0.86f);
}
19 changes: 19 additions & 0 deletions test/engine/engine_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <scratch/sound_p.h>
#include <timermock.h>
#include <clockmock.h>
#include <audioenginemock.h>
#include <audioinputmock.h>
#include <audiooutputmock.h>
#include <audioplayermock.h>
Expand Down Expand Up @@ -318,6 +319,24 @@ TEST(EngineTest, StopSounds)
SoundPrivate::audioOutput = nullptr;
}

TEST(EngineTest, GlobalVolume)
{
Engine engine;
ASSERT_EQ(engine.globalVolume(), 100);

engine.setGlobalVolume(58.3);
ASSERT_EQ(std::round(engine.globalVolume() * 100) / 100, 58.3);

AudioEngineMock audioEngine;
engine.m_audioEngine = &audioEngine;

EXPECT_CALL(audioEngine, volume()).WillOnce(Return(0.275));
ASSERT_EQ(engine.globalVolume(), 27.5);

EXPECT_CALL(audioEngine, setVolume(0.9236));
engine.setGlobalVolume(92.36);
}

TEST(EngineTest, Step)
{
Project p("step.sb3");
Expand Down
13 changes: 13 additions & 0 deletions test/mocks/audioenginemock.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#pragma once

#include <audio/iaudioengine.h>
#include <gmock/gmock.h>

using namespace libscratchcpp;

class AudioEngineMock : public IAudioEngine
{
public:
MOCK_METHOD(float, volume, (), (const, override));
MOCK_METHOD(void, setVolume, (float), (override));
};
3 changes: 3 additions & 0 deletions test/mocks/enginemock.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ class EngineMock : public IEngine
MOCK_METHOD(void, stopTarget, (Target *, VirtualMachine *), (override));
MOCK_METHOD(void, initClone, (std::shared_ptr<Sprite>), (override));
MOCK_METHOD(void, deinitClone, (std::shared_ptr<Sprite>), (override));

MOCK_METHOD(void, stopSounds, (), (override));
MOCK_METHOD(double, globalVolume, (), (const, override));
MOCK_METHOD(void, setGlobalVolume, (double), (override));

MOCK_METHOD(void, updateMonitors, (), (override));
MOCK_METHOD(void, step, (), (override));
Expand Down