Skip to content

Commit e922390

Browse files
authored
Merge pull request #543 from scratchcpp/global_volume_api
Add global volume API
2 parents bba47fc + cdcc467 commit e922390

File tree

15 files changed

+222
-8
lines changed

15 files changed

+222
-8
lines changed

include/scratchcpp/iengine.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,12 @@ class LIBSCRATCHCPP_EXPORT IEngine
8787
/*! Stops all currently playing sounds. */
8888
virtual void stopSounds() = 0;
8989

90+
/*! Returns the global volume of all sounds (in %). */
91+
virtual double globalVolume() const = 0;
92+
93+
/*! Sets the global volume of all sounds (in %). */
94+
virtual void setGlobalVolume(double volume) = 0;
95+
9096
/*! Updates the values of stage monitors. */
9197
virtual void updateMonitors() = 0;
9298

src/audio/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ endif()
1515

1616
target_sources(scratchcpp-audio
1717
PUBLIC
18+
iaudioengine.h
1819
iaudioplayer.h
1920
iaudiooutput.h
2021
audiooutput.cpp
@@ -38,6 +39,8 @@ if (LIBSCRATCHCPP_AUDIO_SUPPORT)
3839
else()
3940
target_sources(scratchcpp-audio
4041
PUBLIC
42+
internal/audioenginestub.cpp
43+
internal/audioenginestub.h
4144
internal/audioplayerstub.cpp
4245
internal/audioplayerstub.h
4346
internal/audioloudnessstub.cpp

src/audio/iaudioengine.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#pragma once
4+
5+
#include <memory>
6+
7+
namespace libscratchcpp
8+
{
9+
10+
class IAudioEngine
11+
{
12+
public:
13+
virtual ~IAudioEngine() { }
14+
15+
static IAudioEngine *instance();
16+
17+
virtual float volume() const = 0;
18+
virtual void setVolume(float volume) = 0;
19+
};
20+
21+
} // namespace libscratchcpp

src/audio/internal/audioengine.cpp

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,25 @@
11
// SPDX-License-Identifier: Apache-2.0
22

33
#include <iostream>
4+
#include <miniaudio.h>
45

56
#include "audioengine.h"
67

78
using namespace libscratchcpp;
89

910
AudioEngine AudioEngine::instance;
1011

12+
IAudioEngine *IAudioEngine::instance()
13+
{
14+
return &AudioEngine::instance;
15+
}
16+
1117
ma_engine *AudioEngine::engine()
1218
{
1319
if (!instance.m_initialized)
1420
instance.init();
1521

16-
return instance.m_initialized ? &instance.m_engine : nullptr;
22+
return instance.m_initialized ? instance.m_engine : nullptr;
1723
}
1824

1925
bool AudioEngine::initialized()
@@ -24,20 +30,36 @@ bool AudioEngine::initialized()
2430
return instance.m_initialized;
2531
}
2632

33+
float AudioEngine::volume() const
34+
{
35+
return m_volume;
36+
}
37+
38+
void AudioEngine::setVolume(float volume)
39+
{
40+
m_volume = volume;
41+
42+
if (m_initialized)
43+
ma_engine_set_volume(m_engine, volume);
44+
}
45+
2746
AudioEngine::AudioEngine()
2847
{
2948
}
3049

3150
AudioEngine::~AudioEngine()
3251
{
33-
if (m_initialized)
34-
ma_engine_uninit(&m_engine);
52+
if (m_initialized && m_engine) {
53+
ma_engine_uninit(m_engine);
54+
delete m_engine;
55+
}
3556
}
3657

3758
void AudioEngine::init()
3859
{
3960
ma_result result;
40-
result = ma_engine_init(NULL, &m_engine);
61+
m_engine = new ma_engine;
62+
result = ma_engine_init(NULL, m_engine);
4163

4264
if (result != MA_SUCCESS) {
4365
std::cerr << "Failed to initialize audio engine." << std::endl;

src/audio/internal/audioengine.h

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,37 @@
33
#pragma once
44

55
#include <memory>
6-
#include <miniaudio.h>
6+
7+
#include "../iaudioengine.h"
8+
9+
struct ma_engine;
710

811
namespace libscratchcpp
912
{
1013

1114
// This is a singleton which initializes and uninitializes the miniaudio engine
12-
class AudioEngine
15+
class AudioEngine : public IAudioEngine
1316
{
1417
public:
18+
friend class IAudioEngine;
19+
1520
static ma_engine *engine();
1621
static bool initialized();
1722

23+
float volume() const override;
24+
void setVolume(float volume) override;
25+
1826
private:
1927
AudioEngine();
2028
~AudioEngine();
2129

2230
void init();
2331

2432
static AudioEngine instance;
25-
ma_engine m_engine;
33+
34+
ma_engine *m_engine = nullptr;
2635
bool m_initialized = false;
36+
float m_volume = 1.0f;
2737
};
2838

2939
} // namespace libscratchcpp
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#include "audioenginestub.h"
4+
5+
using namespace libscratchcpp;
6+
7+
AudioEngineStub AudioEngineStub::instance;
8+
9+
IAudioEngine *IAudioEngine::instance()
10+
{
11+
return &AudioEngineStub::instance;
12+
}
13+
14+
AudioEngineStub::AudioEngineStub()
15+
{
16+
}
17+
18+
float AudioEngineStub::volume() const
19+
{
20+
return m_volume;
21+
}
22+
23+
void AudioEngineStub::setVolume(float volume)
24+
{
25+
m_volume = volume;
26+
}

src/audio/internal/audioenginestub.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#pragma once
4+
5+
#include "../iaudioengine.h"
6+
7+
namespace libscratchcpp
8+
{
9+
10+
class AudioEngineStub : public IAudioEngine
11+
{
12+
public:
13+
friend class IAudioEngine;
14+
AudioEngineStub();
15+
16+
float volume() const override;
17+
void setVolume(float volume) override;
18+
19+
private:
20+
static AudioEngineStub instance;
21+
float m_volume = 1.0f;
22+
};
23+
24+
} // namespace libscratchcpp

src/audio/internal/audioplayer.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include <iostream>
44
#include <cassert>
5+
#include <miniaudio.h>
56

67
#include "audioplayer.h"
78
#include "audioengine.h"

src/engine/internal/engine.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "blocksectioncontainer.h"
2727
#include "timer.h"
2828
#include "clock.h"
29+
#include "audio/iaudioengine.h"
2930
#include "blocks/standardblocks.h"
3031
#include "blocks/variableblocks.h"
3132
#include "blocks/listblocks.h"
@@ -46,7 +47,8 @@ const std::unordered_map<Engine::HatType, bool> Engine::m_hatEdgeActivated = {
4647
Engine::Engine() :
4748
m_defaultTimer(std::make_unique<Timer>()),
4849
m_timer(m_defaultTimer.get()),
49-
m_clock(Clock::instance().get())
50+
m_clock(Clock::instance().get()),
51+
m_audioEngine(IAudioEngine::instance())
5052
{
5153
}
5254

@@ -469,6 +471,16 @@ void Engine::stopSounds()
469471
}
470472
}
471473

474+
double Engine::globalVolume() const
475+
{
476+
return m_audioEngine->volume() * 100;
477+
}
478+
479+
void Engine::setGlobalVolume(double volume)
480+
{
481+
m_audioEngine->setVolume(volume / 100);
482+
}
483+
472484
void Engine::updateMonitors()
473485
{
474486
// Execute the "script" of each visible monitor

src/engine/internal/engine.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ namespace libscratchcpp
1919

2020
class Entity;
2121
class IClock;
22+
class IAudioEngine;
2223

2324
class Engine : public IEngine
2425
{
@@ -41,7 +42,10 @@ class Engine : public IEngine
4142
void stopTarget(Target *target, VirtualMachine *exceptScript) override;
4243
void initClone(std::shared_ptr<Sprite> clone) override;
4344
void deinitClone(std::shared_ptr<Sprite> clone) override;
45+
4446
void stopSounds() override;
47+
virtual double globalVolume() const override;
48+
virtual void setGlobalVolume(double volume) override;
4549

4650
void updateMonitors() override;
4751
void step() override;
@@ -163,6 +167,7 @@ class Engine : public IEngine
163167
void setUserAgent(const std::string &agent) override;
164168

165169
IClock *m_clock = nullptr;
170+
IAudioEngine *m_audioEngine = nullptr;
166171

167172
private:
168173
enum class HatType

test/audio/CMakeLists.txt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,21 @@
1+
# audioengine_test
2+
add_executable(
3+
audioengine_test
4+
audioengine_test.cpp
5+
)
6+
7+
target_link_libraries(
8+
audioengine_test
9+
GTest::gtest_main
10+
scratchcpp
11+
)
12+
13+
gtest_discover_tests(audioengine_test)
14+
15+
if (LIBSCRATCHCPP_AUDIO_SUPPORT)
16+
target_compile_definitions(audioengine_test PRIVATE LIBSCRATCHCPP_AUDIO_SUPPORT)
17+
endif()
18+
119
# audiooutput_test
220
add_executable(
321
audiooutput_test

test/audio/audioengine_test.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#ifdef LIBSCRATCHCPP_AUDIO_SUPPORT
2+
#include <audio/internal/audioengine.h>
3+
#else
4+
#include <audio/internal/audioenginestub.h>
5+
#endif
6+
7+
#include "../common.h"
8+
9+
using namespace libscratchcpp;
10+
11+
TEST(AudioEngineTest, Instance)
12+
{
13+
IAudioEngine *instance = IAudioEngine::instance();
14+
ASSERT_TRUE(instance);
15+
16+
#ifdef LIBSCRATCHCPP_AUDIO_SUPPORT
17+
ASSERT_TRUE(dynamic_cast<AudioEngine *>(instance));
18+
#else
19+
ASSERT_TRUE(dynamic_cast<AudioEngineStub *>(instance));
20+
#endif
21+
}
22+
23+
TEST(AudioEngineTest, Volume)
24+
{
25+
IAudioEngine *engine = IAudioEngine::instance();
26+
ASSERT_TRUE(engine);
27+
ASSERT_EQ(engine->volume(), 1.0f);
28+
29+
engine->setVolume(0.86f);
30+
ASSERT_EQ(engine->volume(), 0.86f);
31+
}

test/engine/engine_test.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <scratch/sound_p.h>
1515
#include <timermock.h>
1616
#include <clockmock.h>
17+
#include <audioenginemock.h>
1718
#include <audioinputmock.h>
1819
#include <audiooutputmock.h>
1920
#include <audioplayermock.h>
@@ -318,6 +319,24 @@ TEST(EngineTest, StopSounds)
318319
SoundPrivate::audioOutput = nullptr;
319320
}
320321

322+
TEST(EngineTest, GlobalVolume)
323+
{
324+
Engine engine;
325+
ASSERT_EQ(engine.globalVolume(), 100);
326+
327+
engine.setGlobalVolume(58.3);
328+
ASSERT_EQ(std::round(engine.globalVolume() * 100) / 100, 58.3);
329+
330+
AudioEngineMock audioEngine;
331+
engine.m_audioEngine = &audioEngine;
332+
333+
EXPECT_CALL(audioEngine, volume()).WillOnce(Return(0.275));
334+
ASSERT_EQ(engine.globalVolume(), 27.5);
335+
336+
EXPECT_CALL(audioEngine, setVolume(0.9236));
337+
engine.setGlobalVolume(92.36);
338+
}
339+
321340
TEST(EngineTest, Step)
322341
{
323342
Project p("step.sb3");

test/mocks/audioenginemock.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#pragma once
2+
3+
#include <audio/iaudioengine.h>
4+
#include <gmock/gmock.h>
5+
6+
using namespace libscratchcpp;
7+
8+
class AudioEngineMock : public IAudioEngine
9+
{
10+
public:
11+
MOCK_METHOD(float, volume, (), (const, override));
12+
MOCK_METHOD(void, setVolume, (float), (override));
13+
};

test/mocks/enginemock.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ class EngineMock : public IEngine
2424
MOCK_METHOD(void, stopTarget, (Target *, VirtualMachine *), (override));
2525
MOCK_METHOD(void, initClone, (std::shared_ptr<Sprite>), (override));
2626
MOCK_METHOD(void, deinitClone, (std::shared_ptr<Sprite>), (override));
27+
2728
MOCK_METHOD(void, stopSounds, (), (override));
29+
MOCK_METHOD(double, globalVolume, (), (const, override));
30+
MOCK_METHOD(void, setGlobalVolume, (double), (override));
2831

2932
MOCK_METHOD(void, updateMonitors, (), (override));
3033
MOCK_METHOD(void, step, (), (override));

0 commit comments

Comments
 (0)