Skip to content

Commit 5f21681

Browse files
committed
Implement sound_play block
1 parent 72b5f74 commit 5f21681

File tree

3 files changed

+265
-0
lines changed

3 files changed

+265
-0
lines changed

src/blocks/soundblocks.cpp

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
#include <scratchcpp/iengine.h>
44
#include <scratchcpp/compiler.h>
55
#include <scratchcpp/target.h>
6+
#include <scratchcpp/input.h>
7+
#include <scratchcpp/sound.h>
68

79
#include "soundblocks.h"
810

@@ -16,14 +18,49 @@ std::string SoundBlocks::name() const
1618
void SoundBlocks::registerBlocks(IEngine *engine)
1719
{
1820
// Blocks
21+
engine->addCompileFunction(this, "sound_play", &compilePlay);
1922
engine->addCompileFunction(this, "sound_changevolumeby", &compileChangeVolumeBy);
2023
engine->addCompileFunction(this, "sound_setvolumeto", &compileSetVolumeTo);
2124
engine->addCompileFunction(this, "sound_volume", &compileVolume);
2225

2326
// Inputs
27+
engine->addInput(this, "SOUND_MENU", SOUND_MENU);
2428
engine->addInput(this, "VOLUME", VOLUME);
2529
}
2630

31+
void SoundBlocks::compilePlay(Compiler *compiler)
32+
{
33+
Target *target = compiler->target();
34+
assert(target);
35+
36+
if (!target)
37+
return;
38+
39+
Input *input = compiler->input(SOUND_MENU);
40+
41+
if (input->type() != Input::Type::ObscuredShadow) {
42+
assert(input->pointsToDropdownMenu());
43+
std::string value = input->selectedMenuItem();
44+
45+
int index = target->findSound(value);
46+
47+
if (index == -1) {
48+
Value v(value);
49+
50+
if (v.type() == Value::Type::Integer) {
51+
compiler->addConstValue(v.toLong() - 1);
52+
compiler->addFunctionCall(&playByIndex);
53+
}
54+
} else {
55+
compiler->addConstValue(index);
56+
compiler->addFunctionCall(&playByIndex);
57+
}
58+
} else {
59+
compiler->addInput(input);
60+
compiler->addFunctionCall(&play);
61+
}
62+
}
63+
2764
void SoundBlocks::compileChangeVolumeBy(Compiler *compiler)
2865
{
2966
compiler->addInput(VOLUME);
@@ -41,6 +78,56 @@ void SoundBlocks::compileVolume(Compiler *compiler)
4178
compiler->addFunctionCall(&volume);
4279
}
4380

81+
Sound *SoundBlocks::getSoundByIndex(Target *target, long index)
82+
{
83+
long soundCount = target->sounds().size();
84+
85+
if (index < 0 || index >= soundCount) {
86+
if (index < 0)
87+
index = std::fmod(soundCount + std::fmod(index, -soundCount), soundCount);
88+
else
89+
index = std::fmod(index, soundCount);
90+
}
91+
92+
return target->soundAt(index).get();
93+
}
94+
95+
unsigned int SoundBlocks::play(VirtualMachine *vm)
96+
{
97+
Target *target = vm->target();
98+
assert(target);
99+
const Value *name = vm->getInput(0, 1);
100+
101+
if (target) {
102+
Sound *sound = target->soundAt(target->findSound(name->toString())).get();
103+
104+
if (sound)
105+
sound->start();
106+
else if (name->type() == Value::Type::Integer) {
107+
sound = getSoundByIndex(target, name->toLong() - 1);
108+
109+
if (sound)
110+
sound->start();
111+
}
112+
}
113+
114+
return 1;
115+
}
116+
117+
unsigned int SoundBlocks::playByIndex(VirtualMachine *vm)
118+
{
119+
Target *target = vm->target();
120+
assert(target);
121+
122+
if (target) {
123+
auto sound = getSoundByIndex(target, vm->getInput(0, 1)->toInt());
124+
125+
if (sound)
126+
sound->start();
127+
}
128+
return 1;
129+
}
130+
44131
unsigned int SoundBlocks::changeVolumeBy(VirtualMachine *vm)
45132
{
46133
if (Target *target = vm->target())

src/blocks/soundblocks.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,16 @@
77
namespace libscratchcpp
88
{
99

10+
class Sound;
11+
class Target;
12+
1013
/*! \brief The SoundBlocks class contains the implementation of sound blocks. */
1114
class SoundBlocks : public IBlockSection
1215
{
1316
public:
1417
enum Inputs
1518
{
19+
SOUND_MENU,
1620
VOLUME
1721
};
1822

@@ -30,10 +34,15 @@ class SoundBlocks : public IBlockSection
3034

3135
void registerBlocks(IEngine *engine) override;
3236

37+
static void compilePlay(Compiler *compiler);
3338
static void compileChangeVolumeBy(Compiler *compiler);
3439
static void compileSetVolumeTo(Compiler *compiler);
3540
static void compileVolume(Compiler *compiler);
3641

42+
static Sound *getSoundByIndex(Target *target, long index);
43+
static unsigned int play(VirtualMachine *vm);
44+
static unsigned int playByIndex(VirtualMachine *vm);
45+
3746
static unsigned int changeVolumeBy(VirtualMachine *vm);
3847
static unsigned int setVolumeTo(VirtualMachine *vm);
3948
static unsigned int volume(VirtualMachine *vm);

test/blocks/sound_blocks_test.cpp

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33
#include <scratchcpp/input.h>
44
#include <scratchcpp/field.h>
55
#include <scratchcpp/target.h>
6+
#include <scratchcpp/sound.h>
7+
#include <scratch/sound_p.h>
68
#include <enginemock.h>
9+
#include <audioplayerfactorymock.h>
10+
#include <audioplayermock.h>
711

812
#include "../common.h"
913
#include "blocks/soundblocks.h"
@@ -94,16 +98,181 @@ TEST_F(SoundBlocksTest, CategoryVisible)
9498
TEST_F(SoundBlocksTest, RegisterBlocks)
9599
{
96100
// Blocks
101+
EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "sound_play", &SoundBlocks::compilePlay));
97102
EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "sound_changevolumeby", &SoundBlocks::compileChangeVolumeBy));
98103
EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "sound_setvolumeto", &SoundBlocks::compileSetVolumeTo));
99104
EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "sound_volume", &SoundBlocks::compileVolume));
100105

101106
// Inputs
107+
EXPECT_CALL(m_engineMock, addInput(m_section.get(), "SOUND_MENU", SoundBlocks::SOUND_MENU));
102108
EXPECT_CALL(m_engineMock, addInput(m_section.get(), "VOLUME", SoundBlocks::VOLUME));
103109

104110
m_section->registerBlocks(&m_engineMock);
105111
}
106112

113+
TEST_F(SoundBlocksTest, Play)
114+
{
115+
AudioPlayerFactoryMock factory;
116+
auto player1 = std::make_shared<AudioPlayerMock>();
117+
auto player2 = std::make_shared<AudioPlayerMock>();
118+
SoundPrivate::playerFactory = &factory;
119+
EXPECT_CALL(factory, create()).WillOnce(Return(player1)).WillOnce(Return(player2));
120+
EXPECT_CALL(*player1, setVolume);
121+
EXPECT_CALL(*player2, setVolume);
122+
Target target;
123+
target.addSound(std::make_shared<Sound>("test", "", ""));
124+
target.addSound(std::make_shared<Sound>("some sound", "", ""));
125+
Compiler compiler(&m_engineMock, &target);
126+
127+
// start sound (some sound)
128+
auto block1 = std::make_shared<Block>("a", "sound_play");
129+
addDropdownInput(block1, "SOUND_MENU", SoundBlocks::SOUND_MENU, "some sound");
130+
131+
// start sound (1)
132+
auto block2 = std::make_shared<Block>("b", "sound_play");
133+
addDropdownInput(block2, "SOUND_MENU", SoundBlocks::SOUND_MENU, "1");
134+
135+
// start sound (5)
136+
auto block3 = std::make_shared<Block>("c", "sound_play");
137+
addDropdownInput(block3, "SOUND_MENU", SoundBlocks::SOUND_MENU, "5");
138+
139+
// start sound (-3)
140+
auto block4 = std::make_shared<Block>("d", "sound_play");
141+
addDropdownInput(block4, "SOUND_MENU", SoundBlocks::SOUND_MENU, "-3");
142+
143+
// start sound (nonexistent sound)
144+
auto block5 = std::make_shared<Block>("e", "sound_play");
145+
addDropdownInput(block5, "SOUND_MENU", SoundBlocks::SOUND_MENU, "nonexistent sound");
146+
147+
// start sound (null block)
148+
auto block6 = std::make_shared<Block>("f", "sound_play");
149+
addDropdownInput(block6, "SOUND_MENU", SoundBlocks::SOUND_MENU, "", createNullBlock("g"));
150+
151+
compiler.init();
152+
153+
EXPECT_CALL(m_engineMock, functionIndex(&SoundBlocks::playByIndex)).WillOnce(Return(2));
154+
compiler.setBlock(block1);
155+
SoundBlocks::compilePlay(&compiler);
156+
157+
EXPECT_CALL(m_engineMock, functionIndex(&SoundBlocks::playByIndex)).WillOnce(Return(2));
158+
compiler.setBlock(block2);
159+
SoundBlocks::compilePlay(&compiler);
160+
161+
EXPECT_CALL(m_engineMock, functionIndex(&SoundBlocks::playByIndex)).WillOnce(Return(2));
162+
compiler.setBlock(block3);
163+
SoundBlocks::compilePlay(&compiler);
164+
165+
EXPECT_CALL(m_engineMock, functionIndex(&SoundBlocks::playByIndex)).WillOnce(Return(2));
166+
compiler.setBlock(block4);
167+
SoundBlocks::compilePlay(&compiler);
168+
169+
EXPECT_CALL(m_engineMock, functionIndex(&SoundBlocks::playByIndex)).Times(0);
170+
EXPECT_CALL(m_engineMock, functionIndex(&SoundBlocks::play)).Times(0);
171+
compiler.setBlock(block5);
172+
SoundBlocks::compilePlay(&compiler);
173+
174+
EXPECT_CALL(m_engineMock, functionIndex(&SoundBlocks::play)).WillOnce(Return(3));
175+
compiler.setBlock(block6);
176+
SoundBlocks::compilePlay(&compiler);
177+
178+
compiler.end();
179+
180+
ASSERT_EQ(
181+
compiler.bytecode(),
182+
std::vector<unsigned int>({ vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 2, vm::OP_CONST, 1, vm::OP_EXEC, 2, vm::OP_CONST, 2, vm::OP_EXEC, 2, vm::OP_CONST, 3, vm::OP_EXEC, 2,
183+
vm::OP_NULL, vm::OP_EXEC, 3, vm::OP_HALT }));
184+
ASSERT_EQ(compiler.constValues(), std::vector<Value>({ 1, 0, 4, -4 }));
185+
186+
SoundPrivate::playerFactory = nullptr;
187+
}
188+
189+
TEST_F(SoundBlocksTest, PlayImpl)
190+
{
191+
static unsigned int bytecode1[] = { vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 0, vm::OP_HALT };
192+
static unsigned int bytecode2[] = { vm::OP_START, vm::OP_CONST, 1, vm::OP_EXEC, 0, vm::OP_HALT };
193+
static unsigned int bytecode3[] = { vm::OP_START, vm::OP_CONST, 2, vm::OP_EXEC, 0, vm::OP_HALT };
194+
static unsigned int bytecode4[] = { vm::OP_START, vm::OP_CONST, 3, vm::OP_EXEC, 1, vm::OP_HALT };
195+
static unsigned int bytecode5[] = { vm::OP_START, vm::OP_CONST, 4, vm::OP_EXEC, 1, vm::OP_HALT };
196+
static unsigned int bytecode6[] = { vm::OP_START, vm::OP_CONST, 5, vm::OP_EXEC, 1, vm::OP_HALT };
197+
static unsigned int bytecode7[] = { vm::OP_START, vm::OP_CONST, 6, vm::OP_EXEC, 1, vm::OP_HALT };
198+
static unsigned int bytecode8[] = { vm::OP_START, vm::OP_CONST, 7, vm::OP_EXEC, 1, vm::OP_HALT };
199+
static BlockFunc functions[] = { &SoundBlocks::playByIndex, &SoundBlocks::play };
200+
static Value constValues[] = { 2, 5, -1, "test", "nonexistent", "1", "4", "-3" };
201+
202+
AudioPlayerFactoryMock factory;
203+
auto player1 = std::make_shared<AudioPlayerMock>();
204+
auto player2 = std::make_shared<AudioPlayerMock>();
205+
auto player3 = std::make_shared<AudioPlayerMock>();
206+
SoundPrivate::playerFactory = &factory;
207+
EXPECT_CALL(factory, create()).WillOnce(Return(player1)).WillOnce(Return(player2)).WillOnce(Return(player3));
208+
EXPECT_CALL(*player1, setVolume);
209+
EXPECT_CALL(*player2, setVolume);
210+
EXPECT_CALL(*player3, setVolume);
211+
Target target;
212+
target.addSound(std::make_shared<Sound>("some sound", "", ""));
213+
target.addSound(std::make_shared<Sound>("test", "", ""));
214+
target.addSound(std::make_shared<Sound>("another sound", "", ""));
215+
216+
VirtualMachine vm(&target, nullptr, nullptr);
217+
vm.setFunctions(functions);
218+
vm.setConstValues(constValues);
219+
220+
EXPECT_CALL(*player3, start());
221+
vm.setBytecode(bytecode1);
222+
vm.run();
223+
224+
ASSERT_EQ(vm.registerCount(), 0);
225+
226+
EXPECT_CALL(*player3, start());
227+
vm.setBytecode(bytecode2);
228+
vm.run();
229+
230+
ASSERT_EQ(vm.registerCount(), 0);
231+
232+
EXPECT_CALL(*player3, start());
233+
vm.reset();
234+
vm.setBytecode(bytecode3);
235+
vm.run();
236+
237+
ASSERT_EQ(vm.registerCount(), 0);
238+
239+
EXPECT_CALL(*player2, start());
240+
vm.reset();
241+
vm.setBytecode(bytecode4);
242+
vm.run();
243+
244+
ASSERT_EQ(vm.registerCount(), 0);
245+
246+
vm.reset();
247+
vm.setBytecode(bytecode5);
248+
vm.run();
249+
250+
ASSERT_EQ(vm.registerCount(), 0);
251+
252+
EXPECT_CALL(*player1, start());
253+
vm.reset();
254+
vm.setBytecode(bytecode6);
255+
vm.run();
256+
257+
ASSERT_EQ(vm.registerCount(), 0);
258+
259+
EXPECT_CALL(*player1, start());
260+
vm.reset();
261+
vm.setBytecode(bytecode7);
262+
vm.run();
263+
264+
ASSERT_EQ(vm.registerCount(), 0);
265+
266+
EXPECT_CALL(*player3, start());
267+
vm.reset();
268+
vm.setBytecode(bytecode8);
269+
vm.run();
270+
271+
ASSERT_EQ(vm.registerCount(), 0);
272+
273+
SoundPrivate::playerFactory = nullptr;
274+
}
275+
107276
TEST_F(SoundBlocksTest, ChangeVolumeBy)
108277
{
109278
Compiler compiler(&m_engineMock);

0 commit comments

Comments
 (0)