Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/scratchcpp/iengine.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ class LIBSCRATCHCPP_EXPORT IEngine
/*! Automatically called from clones that are being deleted. */
virtual void deinitClone(std::shared_ptr<Sprite> clone) = 0;

/*! Stops all currently playing sounds. */
virtual void stopSounds() = 0;

/*! Steps all currently running threads. Use this to implement a custom event loop. */
virtual void step() = 0;

Expand Down
23 changes: 18 additions & 5 deletions src/audio/audioplayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,23 +51,36 @@ bool AudioPlayer::load(unsigned int size, const void *data, unsigned long sample
}

m_loaded = true;
ma_sound_set_volume(m_sound, m_volume);
return true;
}

void AudioPlayer::setVolume(float volume)
{
if (!AudioEngine::initialized())
m_volume = volume;

if (!m_loaded)
return;

ma_sound_set_volume(m_sound, volume);
}

void AudioPlayer::start()
{
if (!AudioEngine::initialized())
if (!m_loaded)
return;

ma_result result = ma_sound_start(m_sound);
if (isPlaying())
stop();

ma_result result = ma_sound_seek_to_pcm_frame(m_sound, 0);

if (result != MA_SUCCESS) {
std::cerr << "Failed to seek to PCM frame 0." << std::endl;
m_started = false;
}

result = ma_sound_start(m_sound);

if (result != MA_SUCCESS) {
std::cerr << "Failed to start sound." << std::endl;
Expand All @@ -78,7 +91,7 @@ void AudioPlayer::start()

void AudioPlayer::stop()
{
if (!AudioEngine::initialized())
if (!m_loaded)
return;

ma_result result = ma_sound_stop(m_sound);
Expand All @@ -90,7 +103,7 @@ void AudioPlayer::stop()

bool AudioPlayer::isPlaying() const
{
if (!AudioEngine::initialized())
if (!m_loaded)
return false;

return m_started && !m_sound->atEnd;
Expand Down
1 change: 1 addition & 0 deletions src/audio/audioplayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class AudioPlayer : public IAudioPlayer
ma_sound *m_sound;
bool m_loaded = false;
bool m_started = false;
float m_volume = 1;
};

} // namespace libscratchcpp
4 changes: 2 additions & 2 deletions src/blocks/looksblocks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -685,8 +685,8 @@ unsigned int LooksBlocks::size(VirtualMachine *vm)

void LooksBlocks::setCostumeByIndex(Target *target, long index)
{
// TODO: Remove this (#248)
std::size_t costumeCount = target->costumes().size();
long costumeCount = target->costumes().size();

if (index < 0 || index >= costumeCount) {
if (index < 0)
index = std::fmod(costumeCount + std::fmod(index, -costumeCount), costumeCount);
Expand Down
229 changes: 229 additions & 0 deletions src/blocks/soundblocks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include <scratchcpp/iengine.h>
#include <scratchcpp/compiler.h>
#include <scratchcpp/target.h>
#include <scratchcpp/input.h>
#include <scratchcpp/sound.h>

#include "soundblocks.h"

Expand All @@ -16,14 +18,86 @@ std::string SoundBlocks::name() const
void SoundBlocks::registerBlocks(IEngine *engine)
{
// Blocks
engine->addCompileFunction(this, "sound_play", &compilePlay);
engine->addCompileFunction(this, "sound_playuntildone", &compilePlayUntilDone);
engine->addCompileFunction(this, "sound_stopallsounds", &compileStopAllSounds);
engine->addCompileFunction(this, "sound_changevolumeby", &compileChangeVolumeBy);
engine->addCompileFunction(this, "sound_setvolumeto", &compileSetVolumeTo);
engine->addCompileFunction(this, "sound_volume", &compileVolume);

// Inputs
engine->addInput(this, "SOUND_MENU", SOUND_MENU);
engine->addInput(this, "VOLUME", VOLUME);
}

bool SoundBlocks::compilePlayCommon(Compiler *compiler, bool untilDone, bool *byIndex)
{
Target *target = compiler->target();
assert(target);

if (!target)
return false;

Input *input = compiler->input(SOUND_MENU);

if (input->type() != Input::Type::ObscuredShadow) {
assert(input->pointsToDropdownMenu());
std::string value = input->selectedMenuItem();

int index = target->findSound(value);

if (index == -1) {
Value v(value);

if (v.type() == Value::Type::Integer) {
compiler->addConstValue(v.toLong() - 1);
compiler->addFunctionCall(untilDone ? &playByIndexUntilDone : &playByIndex);

if (byIndex)
*byIndex = true;

return true;
}
} else {
compiler->addConstValue(index);
compiler->addFunctionCall(untilDone ? &playByIndexUntilDone : &playByIndex);

if (byIndex)
*byIndex = true;

return true;
}
} else {
compiler->addInput(input);
compiler->addFunctionCall(untilDone ? &playUntilDone : &play);

if (byIndex)
*byIndex = false;

return true;
}

return false;
}

void SoundBlocks::compilePlay(Compiler *compiler)
{
compilePlayCommon(compiler, false);
}

void SoundBlocks::compilePlayUntilDone(Compiler *compiler)
{
bool byIndex = false;

if (compilePlayCommon(compiler, true, &byIndex))
compiler->addFunctionCall(byIndex ? &checkSoundByIndex : &checkSound);
}

void SoundBlocks::compileStopAllSounds(Compiler *compiler)
{
compiler->addFunctionCall(&stopAllSounds);
}

void SoundBlocks::compileChangeVolumeBy(Compiler *compiler)
{
compiler->addInput(VOLUME);
Expand All @@ -41,6 +115,161 @@ void SoundBlocks::compileVolume(Compiler *compiler)
compiler->addFunctionCall(&volume);
}

Sound *SoundBlocks::getSoundByIndex(Target *target, long index)
{
long soundCount = target->sounds().size();

if (index < 0 || index >= soundCount) {
if (index < 0)
index = std::fmod(soundCount + std::fmod(index, -soundCount), soundCount);
else
index = std::fmod(index, soundCount);
}

return target->soundAt(index).get();
}

Sound *SoundBlocks::playCommon(VirtualMachine *vm)
{
Target *target = vm->target();
assert(target);
const Value *name = vm->getInput(0, 1);

if (target) {
Sound *sound = target->soundAt(target->findSound(name->toString())).get();

if (sound) {
sound->start();
return sound;
}

else if (name->type() == Value::Type::Integer) {
sound = getSoundByIndex(target, name->toLong() - 1);

if (sound) {
sound->start();
return sound;
}
}
}

return nullptr;
}

Sound *SoundBlocks::playByIndexCommon(VirtualMachine *vm)
{
Target *target = vm->target();
assert(target);

if (target) {
Sound *sound = getSoundByIndex(target, vm->getInput(0, 1)->toInt());

if (sound) {
sound->start();
return sound;
}
}

return nullptr;
}

unsigned int SoundBlocks::play(VirtualMachine *vm)
{
Sound *sound = playCommon(vm);

if (sound)
m_waitingSounds.erase(sound);

return 1;
}

unsigned int SoundBlocks::playByIndex(VirtualMachine *vm)
{
Sound *sound = playByIndexCommon(vm);

if (sound)
m_waitingSounds.erase(sound);

return 1;
}

unsigned int SoundBlocks::playUntilDone(VirtualMachine *vm)
{
Sound *sound = playCommon(vm);

if (sound)
m_waitingSounds[sound] = vm;

return 0; // leave the register for checkSound()
}

unsigned int SoundBlocks::playByIndexUntilDone(VirtualMachine *vm)
{
Sound *sound = playByIndexCommon(vm);

if (sound)
m_waitingSounds[sound] = vm;

return 0; // leave the register for checkSoundByIndex()
}

unsigned int SoundBlocks::checkSound(VirtualMachine *vm)
{
Target *target = vm->target();
assert(target);
const Value *name = vm->getInput(0, 1);

if (target) {
Sound *sound = target->soundAt(target->findSound(name->toString())).get();

if (!sound && name->type() == Value::Type::Integer)
sound = getSoundByIndex(target, name->toLong() - 1);

if (sound) {
auto it = m_waitingSounds.find(sound);

if (it != m_waitingSounds.cend() && it->second == vm) {
if (sound->isPlaying())
vm->stop(true, true, true);
else
m_waitingSounds.erase(sound);
}
}
}

return 1;
}

unsigned int SoundBlocks::checkSoundByIndex(VirtualMachine *vm)
{
Target *target = vm->target();
assert(target);

if (target) {
auto sound = getSoundByIndex(target, vm->getInput(0, 1)->toInt());

if (sound) {
auto it = m_waitingSounds.find(sound);

if (it != m_waitingSounds.cend() && it->second == vm) {
if (sound->isPlaying())
vm->stop(true, true, true);
else
m_waitingSounds.erase(sound);
}
}
}

return 1;
}

unsigned int SoundBlocks::stopAllSounds(VirtualMachine *vm)
{
vm->engine()->stopSounds();
m_waitingSounds.clear();
return 0;
}

unsigned int SoundBlocks::changeVolumeBy(VirtualMachine *vm)
{
if (Target *target = vm->target())
Expand Down
Loading