Skip to content

Commit e68170e

Browse files
committed
Implement sound_changevolumeby block
1 parent ba7a961 commit e68170e

File tree

4 files changed

+217
-0
lines changed

4 files changed

+217
-0
lines changed

src/blocks/soundblocks.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
// SPDX-License-Identifier: Apache-2.0
22

3+
#include <scratchcpp/iengine.h>
4+
#include <scratchcpp/compiler.h>
5+
#include <scratchcpp/target.h>
6+
37
#include "soundblocks.h"
48

59
using namespace libscratchcpp;
@@ -11,4 +15,23 @@ std::string SoundBlocks::name() const
1115

1216
void SoundBlocks::registerBlocks(IEngine *engine)
1317
{
18+
// Blocks
19+
engine->addCompileFunction(this, "sound_changevolumeby", &compileChangeVolumeBy);
20+
21+
// Inputs
22+
engine->addInput(this, "VOLUME", VOLUME);
23+
}
24+
25+
void SoundBlocks::compileChangeVolumeBy(Compiler *compiler)
26+
{
27+
compiler->addInput(VOLUME);
28+
compiler->addFunctionCall(&changeVolumeBy);
29+
}
30+
31+
unsigned int SoundBlocks::changeVolumeBy(VirtualMachine *vm)
32+
{
33+
if (Target *target = vm->target())
34+
target->setVolume(target->volume() + vm->getInput(0, 1)->toDouble());
35+
36+
return 1;
1437
}

src/blocks/soundblocks.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,28 @@ namespace libscratchcpp
1111
class SoundBlocks : public IBlockSection
1212
{
1313
public:
14+
enum Inputs
15+
{
16+
VOLUME
17+
};
18+
19+
enum Fields
20+
{
21+
22+
};
23+
24+
enum FieldValues
25+
{
26+
27+
};
28+
1429
std::string name() const override;
1530

1631
void registerBlocks(IEngine *engine) override;
32+
33+
static void compileChangeVolumeBy(Compiler *compiler);
34+
35+
static unsigned int changeVolumeBy(VirtualMachine *vm);
1736
};
1837

1938
} // namespace libscratchcpp

test/blocks/CMakeLists.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,3 +141,19 @@ target_link_libraries(
141141
)
142142

143143
gtest_discover_tests(looks_blocks_test)
144+
145+
# sound_blocks_test
146+
add_executable(
147+
sound_blocks_test
148+
sound_blocks_test.cpp
149+
)
150+
151+
target_link_libraries(
152+
sound_blocks_test
153+
GTest::gtest_main
154+
GTest::gmock_main
155+
scratchcpp
156+
scratchcpp_mocks
157+
)
158+
159+
gtest_discover_tests(sound_blocks_test)

test/blocks/sound_blocks_test.cpp

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
#include <scratchcpp/compiler.h>
2+
#include <scratchcpp/block.h>
3+
#include <scratchcpp/input.h>
4+
#include <scratchcpp/field.h>
5+
#include <scratchcpp/target.h>
6+
#include <enginemock.h>
7+
8+
#include "../common.h"
9+
#include "blocks/soundblocks.h"
10+
#include "engine/internal/engine.h"
11+
12+
using namespace libscratchcpp;
13+
14+
using ::testing::Return;
15+
16+
class SoundBlocksTest : public testing::Test
17+
{
18+
public:
19+
void SetUp() override
20+
{
21+
m_section = std::make_unique<SoundBlocks>();
22+
m_section->registerBlocks(&m_engine);
23+
}
24+
25+
std::shared_ptr<Block> createNullBlock(const std::string &id)
26+
{
27+
std::shared_ptr<Block> block = std::make_shared<Block>(id, "");
28+
BlockComp func = [](Compiler *compiler) { compiler->addInstruction(vm::OP_NULL); };
29+
block->setCompileFunction(func);
30+
31+
return block;
32+
}
33+
34+
void addValueInput(std::shared_ptr<Block> block, const std::string &name, SoundBlocks::Inputs id, const Value &value) const
35+
{
36+
auto input = std::make_shared<Input>(name, Input::Type::Shadow);
37+
input->setPrimaryValue(value);
38+
input->setInputId(id);
39+
block->addInput(input);
40+
}
41+
42+
void addObscuredInput(std::shared_ptr<Block> block, const std::string &name, SoundBlocks::Inputs id, std::shared_ptr<Block> valueBlock) const
43+
{
44+
auto input = std::make_shared<Input>(name, Input::Type::ObscuredShadow);
45+
input->setValueBlock(valueBlock);
46+
input->setInputId(id);
47+
block->addInput(input);
48+
}
49+
50+
std::shared_ptr<Input> addNullInput(std::shared_ptr<Block> block, const std::string &name, SoundBlocks::Inputs id) const
51+
{
52+
auto input = std::make_shared<Input>(name, Input::Type::Shadow);
53+
input->setInputId(id);
54+
block->addInput(input);
55+
56+
return input;
57+
}
58+
59+
void addDropdownInput(std::shared_ptr<Block> block, const std::string &name, SoundBlocks::Inputs id, const std::string &selectedValue, std::shared_ptr<Block> valueBlock = nullptr) const
60+
{
61+
if (valueBlock)
62+
addObscuredInput(block, name, id, valueBlock);
63+
else {
64+
auto input = addNullInput(block, name, id);
65+
auto menu = std::make_shared<Block>(block->id() + "_menu", block->opcode() + "_menu");
66+
input->setValueBlock(menu);
67+
addDropdownField(menu, name, static_cast<SoundBlocks::Fields>(-1), selectedValue, static_cast<SoundBlocks::FieldValues>(-1));
68+
}
69+
}
70+
71+
void addDropdownField(std::shared_ptr<Block> block, const std::string &name, SoundBlocks::Fields id, const std::string &value, SoundBlocks::FieldValues valueId) const
72+
{
73+
auto field = std::make_shared<Field>(name, value);
74+
field->setFieldId(id);
75+
field->setSpecialValueId(valueId);
76+
block->addField(field);
77+
}
78+
79+
std::unique_ptr<IBlockSection> m_section;
80+
Engine m_engine;
81+
EngineMock m_engineMock;
82+
};
83+
84+
TEST_F(SoundBlocksTest, Name)
85+
{
86+
ASSERT_EQ(m_section->name(), "Sound");
87+
}
88+
89+
TEST_F(SoundBlocksTest, CategoryVisible)
90+
{
91+
ASSERT_TRUE(m_section->categoryVisible());
92+
}
93+
94+
TEST_F(SoundBlocksTest, RegisterBlocks)
95+
{
96+
// Blocks
97+
EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "sound_changevolumeby", &SoundBlocks::compileChangeVolumeBy));
98+
99+
// Inputs
100+
EXPECT_CALL(m_engineMock, addInput(m_section.get(), "VOLUME", SoundBlocks::VOLUME));
101+
102+
m_section->registerBlocks(&m_engineMock);
103+
}
104+
105+
TEST_F(SoundBlocksTest, ChangeVolumeBy)
106+
{
107+
Compiler compiler(&m_engineMock);
108+
109+
// change volume by (53.05)
110+
auto block1 = std::make_shared<Block>("a", "sound_changevolumeby");
111+
addValueInput(block1, "VOLUME", SoundBlocks::VOLUME, 53.05);
112+
113+
// change volume by (-2.13)
114+
auto block2 = std::make_shared<Block>("b", "sound_changevolumeby");
115+
addValueInput(block2, "VOLUME", SoundBlocks::VOLUME, -2.13);
116+
117+
EXPECT_CALL(m_engineMock, functionIndex(&SoundBlocks::changeVolumeBy)).Times(2).WillRepeatedly(Return(0));
118+
119+
compiler.init();
120+
121+
compiler.setBlock(block1);
122+
SoundBlocks::compileChangeVolumeBy(&compiler);
123+
124+
compiler.setBlock(block2);
125+
SoundBlocks::compileChangeVolumeBy(&compiler);
126+
127+
compiler.end();
128+
129+
ASSERT_EQ(compiler.bytecode(), std::vector<unsigned int>({ vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 0, vm::OP_CONST, 1, vm::OP_EXEC, 0, vm::OP_HALT }));
130+
ASSERT_EQ(compiler.constValues(), std::vector<Value>({ 53.05, -2.13 }));
131+
}
132+
133+
TEST_F(SoundBlocksTest, ChangeVolumeByImpl)
134+
{
135+
static unsigned int bytecode1[] = { vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 0, vm::OP_HALT };
136+
static unsigned int bytecode2[] = { vm::OP_START, vm::OP_CONST, 1, vm::OP_EXEC, 0, vm::OP_HALT };
137+
static BlockFunc functions[] = { &SoundBlocks::changeVolumeBy };
138+
static Value constValues[] = { 53.05, -2.13 };
139+
140+
Target target;
141+
target.setVolume(42.4);
142+
143+
VirtualMachine vm(&target, nullptr, nullptr);
144+
145+
vm.setBytecode(bytecode1);
146+
vm.setFunctions(functions);
147+
vm.setConstValues(constValues);
148+
vm.run();
149+
150+
ASSERT_EQ(vm.registerCount(), 0);
151+
ASSERT_EQ(std::round(target.volume() * 100) / 100, 95.45);
152+
153+
vm.reset();
154+
vm.setBytecode(bytecode2);
155+
vm.run();
156+
157+
ASSERT_EQ(vm.registerCount(), 0);
158+
ASSERT_EQ(std::round(target.volume() * 100) / 100, 93.32);
159+
}

0 commit comments

Comments
 (0)