Skip to content

Commit f311ce2

Browse files
committed
Sound: Implement pitch effect
1 parent ca1fc7e commit f311ce2

File tree

3 files changed

+66
-0
lines changed

3 files changed

+66
-0
lines changed

include/scratchcpp/sound.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ class SoundPrivate;
1616
class LIBSCRATCHCPP_EXPORT Sound : public Asset
1717
{
1818
public:
19+
enum class Effect
20+
{
21+
Pitch
22+
};
23+
1924
Sound(const std::string &name, const std::string &id, const std::string &format);
2025
Sound(const Sound &) = delete;
2126
virtual ~Sound() { }
@@ -27,6 +32,7 @@ class LIBSCRATCHCPP_EXPORT Sound : public Asset
2732
void setSampleCount(int newSampleCount);
2833

2934
virtual void setVolume(double volume);
35+
virtual void setEffect(Effect effect, double value);
3036

3137
virtual void start();
3238
virtual void stop();

src/scratch/sound.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,18 @@
33
#include <scratchcpp/sound.h>
44
#include <scratchcpp/sprite.h>
55
#include <iostream>
6+
#include <unordered_map>
7+
#include <algorithm>
8+
#include <cmath>
69

710
#include "sound_p.h"
811

912
using namespace libscratchcpp;
1013

14+
static std::unordered_map<Sound::Effect, std::pair<double, double>> EFFECT_RANGE = {
15+
{ Sound::Effect::Pitch, { -360, 360 } } // -3 to 3 octaves
16+
};
17+
1118
/*! Constructs Sound. */
1219
Sound::Sound(const std::string &name, const std::string &id, const std::string &format) :
1320
Asset(name, id, format),
@@ -45,6 +52,25 @@ void Sound::setVolume(double volume)
4552
impl->player->setVolume(volume / 100);
4653
}
4754

55+
/*! Sets the value of the given sound effect. */
56+
void Sound::setEffect(Effect effect, double value)
57+
{
58+
auto it = EFFECT_RANGE.find(effect);
59+
60+
if (it == EFFECT_RANGE.cend())
61+
return;
62+
63+
value = std::clamp(value, it->second.first, it->second.second);
64+
65+
switch (effect) {
66+
case Effect::Pitch:
67+
// Convert from linear
68+
const double root = std::pow(2, 1 / 12.0);
69+
impl->player->setPitch(std::pow(root, value / 10));
70+
break;
71+
}
72+
}
73+
4874
/*! Starts the playback of the sound. */
4975
void Sound::start()
5076
{
@@ -88,6 +114,7 @@ std::shared_ptr<Sound> Sound::clone() const
88114
sound->setSampleCount(sampleCount());
89115
sound->impl->cloneRoot = root;
90116
sound->impl->player->setVolume(impl->player->volume());
117+
sound->impl->player->setPitch(impl->player->pitch());
91118

92119
if (root->impl->player->isLoaded()) {
93120
sound->impl->player->loadCopy(root->impl->player.get());

test/assets/sound_test.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,33 @@ TEST_F(SoundTest, SetVolume)
7979
sound.setVolume(56.04);
8080
}
8181

82+
TEST_F(SoundTest, SetEffect)
83+
{
84+
Sound sound("sound1", "a", "wav");
85+
86+
// Pitch
87+
EXPECT_CALL(*m_player, setPitch(0.125));
88+
sound.setEffect(Sound::Effect::Pitch, -400);
89+
90+
EXPECT_CALL(*m_player, setPitch(0.125));
91+
sound.setEffect(Sound::Effect::Pitch, -360);
92+
93+
EXPECT_CALL(*m_player, setPitch(0.5));
94+
sound.setEffect(Sound::Effect::Pitch, -120);
95+
96+
EXPECT_CALL(*m_player, setPitch(1));
97+
sound.setEffect(Sound::Effect::Pitch, 0);
98+
99+
EXPECT_CALL(*m_player, setPitch(2));
100+
sound.setEffect(Sound::Effect::Pitch, 120);
101+
102+
EXPECT_CALL(*m_player, setPitch(8));
103+
sound.setEffect(Sound::Effect::Pitch, 360);
104+
105+
EXPECT_CALL(*m_player, setPitch(8));
106+
sound.setEffect(Sound::Effect::Pitch, 400);
107+
}
108+
82109
TEST_F(SoundTest, Start)
83110
{
84111
Sound sound("sound1", "a", "wav");
@@ -137,6 +164,8 @@ TEST_F(SoundTest, Clone)
137164
EXPECT_CALL(*clonePlayer, loadCopy(m_player.get())).WillOnce(Return(true));
138165
EXPECT_CALL(*m_player, volume()).WillOnce(Return(0.45));
139166
EXPECT_CALL(*clonePlayer, setVolume(0.45));
167+
EXPECT_CALL(*m_player, pitch()).WillOnce(Return(1.25));
168+
EXPECT_CALL(*clonePlayer, setPitch(1.25));
140169
EXPECT_CALL(*clonePlayer, isLoaded()).WillOnce(Return(true));
141170
auto clone = sound->clone();
142171
ASSERT_TRUE(clone);
@@ -152,6 +181,8 @@ TEST_F(SoundTest, Clone)
152181
EXPECT_CALL(*cloneClonePlayer, loadCopy(m_player.get())).WillOnce(Return(true));
153182
EXPECT_CALL(*clonePlayer, volume()).WillOnce(Return(0.62));
154183
EXPECT_CALL(*cloneClonePlayer, setVolume(0.62));
184+
EXPECT_CALL(*clonePlayer, pitch()).WillOnce(Return(0.5));
185+
EXPECT_CALL(*cloneClonePlayer, setPitch(0.5));
155186
EXPECT_CALL(*cloneClonePlayer, isLoaded()).WillOnce(Return(true));
156187
auto cloneClone = clone->clone();
157188
ASSERT_TRUE(cloneClone);
@@ -162,6 +193,8 @@ TEST_F(SoundTest, Clone)
162193
EXPECT_CALL(*anotherClonePlayer, loadCopy).Times(0);
163194
EXPECT_CALL(*clonePlayer, volume()).WillOnce(Return(0.62));
164195
EXPECT_CALL(*anotherClonePlayer, setVolume(0.62));
196+
EXPECT_CALL(*clonePlayer, pitch()).WillOnce(Return(2));
197+
EXPECT_CALL(*anotherClonePlayer, setPitch(2));
165198
EXPECT_CALL(*anotherClonePlayer, isLoaded()).Times(0);
166199
auto anotherClone = clone->clone();
167200
ASSERT_TRUE(anotherClone);

0 commit comments

Comments
 (0)