1
1
// SPDX-License-Identifier: Apache-2.0
2
2
3
+ #include < scratchcpp/iengine.h>
4
+ #include < scratchcpp/compiler.h>
5
+ #include < scratchcpp/compilerconstant.h>
6
+ #include < scratchcpp/field.h>
7
+ #include < scratchcpp/target.h>
8
+ #include < scratchcpp/thread.h>
9
+ #include < scratchcpp/value.h>
10
+ #include < scratchcpp/executioncontext.h>
11
+ #include < algorithm>
12
+
3
13
#include " soundblocks.h"
4
14
5
15
using namespace libscratchcpp ;
@@ -21,4 +31,231 @@ Rgb SoundBlocks::color() const
21
31
22
32
void SoundBlocks::registerBlocks (IEngine *engine)
23
33
{
34
+ engine->addCompileFunction (this , " sound_play" , &compilePlay);
35
+ engine->addCompileFunction (this , " sound_playuntildone" , &compilePlayUntilDone);
36
+ engine->addCompileFunction (this , " sound_stopallsounds" , &compileStopAllSounds);
37
+ engine->addCompileFunction (this , " sound_seteffectto" , &compileSetEffectTo);
38
+ engine->addCompileFunction (this , " sound_changeeffectby" , &compileChangeEffectBy);
39
+ engine->addCompileFunction (this , " sound_cleareffects" , &compileClearEffects);
40
+ engine->addCompileFunction (this , " sound_changevolumeby" , &compileChangeVolumeBy);
41
+ engine->addCompileFunction (this , " sound_setvolumeto" , &compileSetVolumeTo);
42
+ engine->addCompileFunction (this , " sound_volume" , &compileVolume);
43
+ }
44
+
45
+ void SoundBlocks::onInit (IEngine *engine)
46
+ {
47
+ engine->threadAboutToStop ().connect ([](Thread *thread) {
48
+ Target *target = thread->target ();
49
+ const auto &sounds = target->sounds ();
50
+
51
+ for (auto sound : sounds) {
52
+ if (sound->owner () == thread) {
53
+ sound->stop ();
54
+ break ;
55
+ }
56
+ }
57
+ });
58
+ }
59
+
60
+ CompilerValue *SoundBlocks::compilePlay (Compiler *compiler)
61
+ {
62
+ auto sound = compiler->addInput (" SOUND_MENU" );
63
+ auto storeOwner = compiler->addConstValue (false );
64
+ compiler->addFunctionCallWithCtx (" sound_play" , Compiler::StaticType::Pointer, { Compiler::StaticType::Unknown, Compiler::StaticType::Bool }, { sound, storeOwner });
65
+ return nullptr ;
66
+ }
67
+
68
+ CompilerValue *SoundBlocks::compilePlayUntilDone (Compiler *compiler)
69
+ {
70
+ auto sound = compiler->addInput (" SOUND_MENU" );
71
+ auto storeOwner = compiler->addConstValue (true );
72
+ auto soundPtr = compiler->addFunctionCallWithCtx (" sound_play" , Compiler::StaticType::Pointer, { Compiler::StaticType::Unknown, Compiler::StaticType::Bool }, { sound, storeOwner });
73
+
74
+ compiler->beginLoopCondition ();
75
+ auto waiting = compiler->addFunctionCallWithCtx (" sound_is_waiting" , Compiler::StaticType::Bool, { Compiler::StaticType::Pointer }, { soundPtr });
76
+ compiler->beginWhileLoop (waiting);
77
+ compiler->endLoop ();
78
+
79
+ return nullptr ;
80
+ }
81
+
82
+ CompilerValue *SoundBlocks::compileStopAllSounds (Compiler *compiler)
83
+ {
84
+ compiler->addFunctionCallWithCtx (" sound_stopallsounds" );
85
+ return nullptr ;
86
+ }
87
+
88
+ CompilerValue *SoundBlocks::compileSetEffectTo (Compiler *compiler)
89
+ {
90
+ Field *field = compiler->field (" EFFECT" );
91
+
92
+ if (!field)
93
+ return nullptr ;
94
+
95
+ const std::string &option = field->value ().toString ();
96
+
97
+ if (option == " PITCH" ) {
98
+ auto value = compiler->addInput (" VALUE" );
99
+ compiler->addTargetFunctionCall (" sound_set_pitch_effect" , Compiler::StaticType::Void, { Compiler::StaticType::Number }, { value });
100
+ } else if (option == " PAN" ) {
101
+ auto value = compiler->addInput (" VALUE" );
102
+ compiler->addTargetFunctionCall (" sound_set_pan_effect" , Compiler::StaticType::Void, { Compiler::StaticType::Number }, { value });
103
+ }
104
+
105
+ return nullptr ;
106
+ }
107
+
108
+ CompilerValue *SoundBlocks::compileChangeEffectBy (Compiler *compiler)
109
+ {
110
+ Field *field = compiler->field (" EFFECT" );
111
+
112
+ if (!field)
113
+ return nullptr ;
114
+
115
+ const std::string &option = field->value ().toString ();
116
+
117
+ if (option == " PITCH" ) {
118
+ auto value = compiler->addInput (" VALUE" );
119
+ compiler->addTargetFunctionCall (" sound_change_pitch_effect" , Compiler::StaticType::Void, { Compiler::StaticType::Number }, { value });
120
+ } else if (option == " PAN" ) {
121
+ auto value = compiler->addInput (" VALUE" );
122
+ compiler->addTargetFunctionCall (" sound_change_pan_effect" , Compiler::StaticType::Void, { Compiler::StaticType::Number }, { value });
123
+ }
124
+
125
+ return nullptr ;
126
+ }
127
+
128
+ CompilerValue *SoundBlocks::compileClearEffects (Compiler *compiler)
129
+ {
130
+ compiler->addTargetFunctionCall (" sound_cleareffects" );
131
+ return nullptr ;
132
+ }
133
+
134
+ CompilerValue *SoundBlocks::compileChangeVolumeBy (Compiler *compiler)
135
+ {
136
+ auto volume = compiler->addInput (" VOLUME" );
137
+ compiler->addTargetFunctionCall (" sound_changevolumeby" , Compiler::StaticType::Void, { Compiler::StaticType::Number }, { volume });
138
+ return nullptr ;
139
+ }
140
+
141
+ CompilerValue *SoundBlocks::compileSetVolumeTo (Compiler *compiler)
142
+ {
143
+ auto volume = compiler->addInput (" VOLUME" );
144
+ compiler->addTargetFunctionCall (" sound_setvolumeto" , Compiler::StaticType::Void, { Compiler::StaticType::Number }, { volume });
145
+ return nullptr ;
146
+ }
147
+
148
+ CompilerValue *SoundBlocks::compileVolume (Compiler *compiler)
149
+ {
150
+ return compiler->addTargetFunctionCall (" sound_volume" , Compiler::StaticType::Number);
151
+ }
152
+
153
+ int sound_wrap_clamp_index (Target *target, int index)
154
+ {
155
+ const long soundCount = target->sounds ().size ();
156
+
157
+ if (index < 0 )
158
+ return (soundCount + index % (-soundCount)) % soundCount;
159
+ else if (index >= soundCount)
160
+ return index % soundCount;
161
+ else
162
+ return index ;
163
+ }
164
+
165
+ int sound_get_index (Target *target, const ValueData *sound)
166
+ {
167
+ if (!value_isString (sound)) {
168
+ // Numbers should be treated as sound indices
169
+ if (value_isNaN (sound) || value_isInfinity (sound) || value_isNegativeInfinity (sound))
170
+ return -1 ;
171
+ else
172
+ return sound_wrap_clamp_index (target, value_toLong (sound) - 1 );
173
+ } else {
174
+ // Strings should be treated as sound names, where possible
175
+ // TODO: Use UTF-16 in Target
176
+ // StringPtr *nameStr = value_toStringPtr(sound);
177
+ std::string nameStr;
178
+ value_toString (sound, &nameStr);
179
+ const int soundIndex = target->findSound (nameStr);
180
+
181
+ auto it = std::find_if (nameStr.begin (), nameStr.end (), [](char c) { return !std::isspace (c); });
182
+ bool isWhiteSpace = (it == nameStr.end ());
183
+
184
+ if (soundIndex != -1 ) {
185
+ return soundIndex;
186
+ // Try to cast the string to a number (and treat it as a costume index)
187
+ // Pure whitespace should not be treated as a number
188
+ } else if (value_isValidNumber (sound) && !isWhiteSpace)
189
+ return sound_wrap_clamp_index (target, value_toLong (sound) - 1 );
190
+ }
191
+
192
+ return -1 ;
193
+ }
194
+
195
+ extern " C" Sound *sound_play (ExecutionContext *ctx, const ValueData *soundName, bool storeOwner)
196
+ {
197
+ Thread *thread = ctx->thread ();
198
+ Target *target = thread->target ();
199
+ int index = sound_get_index (target, soundName);
200
+ auto sound = target->soundAt (index );
201
+
202
+ if (sound) {
203
+ sound->start (storeOwner ? thread : nullptr );
204
+ return sound.get ();
205
+ }
206
+
207
+ return nullptr ;
208
+ }
209
+
210
+ extern " C" bool sound_is_waiting (ExecutionContext *ctx, Sound *sound)
211
+ {
212
+ if (!sound)
213
+ return false ;
214
+
215
+ return sound->owner () == ctx->thread () && sound->isPlaying ();
216
+ }
217
+
218
+ extern " C" void sound_stopallsounds (ExecutionContext *ctx)
219
+ {
220
+ ctx->engine ()->stopSounds ();
221
+ }
222
+
223
+ extern " C" void sound_set_pitch_effect (Target *target, double value)
224
+ {
225
+ target->setSoundEffectValue (Sound::Effect::Pitch, value);
226
+ }
227
+
228
+ extern " C" void sound_set_pan_effect (Target *target, double value)
229
+ {
230
+ target->setSoundEffectValue (Sound::Effect::Pan, value);
231
+ }
232
+
233
+ extern " C" void sound_change_pitch_effect (Target *target, double value)
234
+ {
235
+ target->setSoundEffectValue (Sound::Effect::Pitch, target->soundEffectValue (Sound::Effect::Pitch) + value);
236
+ }
237
+
238
+ extern " C" void sound_change_pan_effect (Target *target, double value)
239
+ {
240
+ target->setSoundEffectValue (Sound::Effect::Pan, target->soundEffectValue (Sound::Effect::Pan) + value);
241
+ }
242
+
243
+ extern " C" void sound_cleareffects (Target *target)
244
+ {
245
+ target->clearSoundEffects ();
246
+ }
247
+
248
+ extern " C" void sound_changevolumeby (Target *target, double volume)
249
+ {
250
+ target->setVolume (target->volume () + volume);
251
+ }
252
+
253
+ extern " C" void sound_setvolumeto (Target *target, double volume)
254
+ {
255
+ target->setVolume (volume);
256
+ }
257
+
258
+ extern " C" double sound_volume (Target *target)
259
+ {
260
+ return target->volume ();
24
261
}
0 commit comments