Skip to content

Commit f6e7df7

Browse files
committed
Stop waiting after waiting threads/sounds are stopped
1 parent 28a82f7 commit f6e7df7

File tree

3 files changed

+117
-9
lines changed

3 files changed

+117
-9
lines changed

src/blocks/soundblocks.cpp

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22

33
#include <scratchcpp/iengine.h>
44
#include <scratchcpp/compiler.h>
5+
#include <scratchcpp/compilerconstant.h>
56
#include <scratchcpp/target.h>
7+
#include <scratchcpp/thread.h>
8+
#include <scratchcpp/value.h>
9+
#include <scratchcpp/executioncontext.h>
610
#include <algorithm>
711

812
#include "soundblocks.h"
@@ -30,22 +34,38 @@ void SoundBlocks::registerBlocks(IEngine *engine)
3034
engine->addCompileFunction(this, "sound_playuntildone", &compilePlayUntilDone);
3135
}
3236

37+
void SoundBlocks::onInit(IEngine *engine)
38+
{
39+
engine->threadAboutToStop().connect([](Thread *thread) {
40+
Target *target = thread->target();
41+
const auto &sounds = target->sounds();
42+
43+
for (auto sound : sounds) {
44+
if (sound->owner() == thread) {
45+
sound->stop();
46+
break;
47+
}
48+
}
49+
});
50+
}
51+
3352
CompilerValue *SoundBlocks::compilePlay(Compiler *compiler)
3453
{
3554
auto sound = compiler->addInput("SOUND_MENU");
36-
compiler->addTargetFunctionCall("sound_play", Compiler::StaticType::Pointer, { Compiler::StaticType::Unknown }, { sound });
55+
auto storeOwner = compiler->addConstValue(false);
56+
compiler->addFunctionCallWithCtx("sound_play", Compiler::StaticType::Pointer, { Compiler::StaticType::Unknown, Compiler::StaticType::Bool }, { sound, storeOwner });
3757
return nullptr;
3858
}
3959

4060
CompilerValue *SoundBlocks::compilePlayUntilDone(Compiler *compiler)
4161
{
4262
auto sound = compiler->addInput("SOUND_MENU");
43-
auto soundPtr = compiler->addTargetFunctionCall("sound_play", Compiler::StaticType::Pointer, { Compiler::StaticType::Unknown }, { sound });
63+
auto storeOwner = compiler->addConstValue(true);
64+
auto soundPtr = compiler->addFunctionCallWithCtx("sound_play", Compiler::StaticType::Pointer, { Compiler::StaticType::Unknown, Compiler::StaticType::Bool }, { sound, storeOwner });
4465

4566
compiler->beginLoopCondition();
46-
auto numType = Compiler::StaticType::Number;
47-
auto playing = compiler->addFunctionCall("sound_is_playing", Compiler::StaticType::Bool, { Compiler::StaticType::Pointer }, { soundPtr });
48-
compiler->beginWhileLoop(playing);
67+
auto waiting = compiler->addFunctionCallWithCtx("sound_is_waiting", Compiler::StaticType::Bool, { Compiler::StaticType::Pointer }, { soundPtr });
68+
compiler->beginWhileLoop(waiting);
4969
compiler->endLoop();
5070

5171
return nullptr;
@@ -93,23 +113,25 @@ int sound_get_index(Target *target, const ValueData *sound)
93113
return -1;
94114
}
95115

96-
extern "C" Sound *sound_play(Target *target, const ValueData *soundName)
116+
extern "C" Sound *sound_play(ExecutionContext *ctx, const ValueData *soundName, bool storeOwner)
97117
{
118+
Thread *thread = ctx->thread();
119+
Target *target = thread->target();
98120
int index = sound_get_index(target, soundName);
99121
auto sound = target->soundAt(index);
100122

101123
if (sound) {
102-
sound->start();
124+
sound->start(storeOwner ? thread : nullptr);
103125
return sound.get();
104126
}
105127

106128
return nullptr;
107129
}
108130

109-
extern "C" bool sound_is_playing(Sound *sound)
131+
extern "C" bool sound_is_waiting(ExecutionContext *ctx, Sound *sound)
110132
{
111133
if (!sound)
112134
return false;
113135

114-
return sound->isPlaying();
136+
return sound->owner() == ctx->thread() && sound->isPlaying();
115137
}

src/blocks/soundblocks.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class SoundBlocks : public IExtension
1717
Rgb color() const override;
1818

1919
void registerBlocks(IEngine *engine) override;
20+
void onInit(IEngine *engine) override;
2021

2122
private:
2223
static CompilerValue *compilePlay(Compiler *compiler);

test/blocks/sound_blocks_test.cpp

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class SoundBlocksTest : public testing::Test
2626
m_extension = std::make_unique<SoundBlocks>();
2727
m_engine = m_project.engine().get();
2828
m_extension->registerBlocks(m_engine);
29+
m_extension->onInit(m_engine);
2930

3031
SoundPrivate::audioOutput = &m_outputMock;
3132
}
@@ -428,6 +429,90 @@ TEST_F(SoundBlocksTest, PlayUntilDone_SoundName)
428429
ASSERT_TRUE(thread.isFinished());
429430
}
430431

432+
TEST_F(SoundBlocksTest, PlayUntilDone_KillWaitingThread)
433+
{
434+
auto sprite = std::make_shared<Sprite>();
435+
auto player1 = std::make_shared<AudioPlayerMock>();
436+
auto player2 = std::make_shared<AudioPlayerMock>();
437+
auto player3 = std::make_shared<AudioPlayerMock>();
438+
439+
EXPECT_CALL(m_outputMock, createAudioPlayer()).WillOnce(Return(player1));
440+
sprite->addSound(std::make_shared<Sound>("pop", "a", "wav"));
441+
442+
EXPECT_CALL(m_outputMock, createAudioPlayer()).WillOnce(Return(player2));
443+
auto sound2 = std::make_shared<Sound>("meow", "b", "wav");
444+
sprite->addSound(sound2);
445+
446+
EXPECT_CALL(m_outputMock, createAudioPlayer()).WillOnce(Return(player3));
447+
sprite->addSound(std::make_shared<Sound>("test", "c", "mp3"));
448+
449+
ScriptBuilder builder(m_extension.get(), m_engine, sprite);
450+
builder.addBlock("sound_playuntildone");
451+
builder.addDropdownInput("SOUND_MENU", "meow");
452+
auto block = builder.currentBlock();
453+
454+
Compiler compiler(&m_engineMock, sprite.get());
455+
auto code = compiler.compile(block);
456+
Script script(sprite.get(), block, &m_engineMock);
457+
script.setCode(code);
458+
Thread thread(sprite.get(), &m_engineMock, &script);
459+
460+
EXPECT_CALL(*player1, start).Times(0);
461+
EXPECT_CALL(*player2, start());
462+
EXPECT_CALL(*player3, start).Times(0);
463+
EXPECT_CALL(*player2, isPlaying()).WillRepeatedly(Return(true));
464+
thread.run();
465+
ASSERT_FALSE(thread.isFinished());
466+
ASSERT_EQ(sound2->owner(), &thread);
467+
468+
EXPECT_CALL(*player2, stop());
469+
m_engine->threadAboutToStop()(&thread);
470+
thread.kill();
471+
ASSERT_EQ(sound2->owner(), nullptr);
472+
}
473+
474+
TEST_F(SoundBlocksTest, PlayUntilDone_StopWaitingSound)
475+
{
476+
auto sprite = std::make_shared<Sprite>();
477+
auto player1 = std::make_shared<AudioPlayerMock>();
478+
auto player2 = std::make_shared<AudioPlayerMock>();
479+
auto player3 = std::make_shared<AudioPlayerMock>();
480+
481+
EXPECT_CALL(m_outputMock, createAudioPlayer()).WillOnce(Return(player1));
482+
sprite->addSound(std::make_shared<Sound>("pop", "a", "wav"));
483+
484+
EXPECT_CALL(m_outputMock, createAudioPlayer()).WillOnce(Return(player2));
485+
auto sound2 = std::make_shared<Sound>("meow", "b", "wav");
486+
sprite->addSound(sound2);
487+
488+
EXPECT_CALL(m_outputMock, createAudioPlayer()).WillOnce(Return(player3));
489+
sprite->addSound(std::make_shared<Sound>("test", "c", "mp3"));
490+
491+
ScriptBuilder builder(m_extension.get(), m_engine, sprite);
492+
builder.addBlock("sound_playuntildone");
493+
builder.addDropdownInput("SOUND_MENU", "meow");
494+
auto block = builder.currentBlock();
495+
496+
Compiler compiler(&m_engineMock, sprite.get());
497+
auto code = compiler.compile(block);
498+
Script script(sprite.get(), block, &m_engineMock);
499+
script.setCode(code);
500+
Thread thread(sprite.get(), &m_engineMock, &script);
501+
502+
EXPECT_CALL(*player1, start).Times(0);
503+
EXPECT_CALL(*player2, start());
504+
EXPECT_CALL(*player3, start).Times(0);
505+
EXPECT_CALL(*player2, isPlaying()).WillRepeatedly(Return(true));
506+
thread.run();
507+
ASSERT_FALSE(thread.isFinished());
508+
ASSERT_EQ(sound2->owner(), &thread);
509+
510+
sound2->stop();
511+
EXPECT_CALL(*player2, isPlaying()).Times(0);
512+
thread.run();
513+
ASSERT_TRUE(thread.isFinished());
514+
}
515+
431516
TEST_F(SoundBlocksTest, PlayUntilDone_NumberIndex)
432517
{
433518
auto sprite = std::make_shared<Sprite>();

0 commit comments

Comments
 (0)