Skip to content

Commit 8a734eb

Browse files
committed
Implement audio playback
1 parent f8a75f5 commit 8a734eb

File tree

8 files changed

+141
-3
lines changed

8 files changed

+141
-3
lines changed

include/scratchcpp/sound.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,22 @@ class LIBSCRATCHCPP_EXPORT Sound : public Asset
1717
public:
1818
Sound(const std::string &name, const std::string &id, const std::string &format);
1919
Sound(const Sound &) = delete;
20+
virtual ~Sound() { }
2021

2122
int rate() const;
2223
void setRate(int newRate);
2324

2425
int sampleCount() const;
2526
void setSampleCount(int newSampleCount);
2627

28+
virtual void start();
29+
virtual void stop();
30+
31+
virtual bool isPlaying();
32+
33+
protected:
34+
void processData(unsigned int size, void *data) override;
35+
2736
private:
2837
spimpl::unique_impl_ptr<SoundPrivate> impl;
2938
};

src/scratch/sound.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
// SPDX-License-Identifier: Apache-2.0
22

33
#include <scratchcpp/sound.h>
4+
#include <iostream>
45

56
#include "sound_p.h"
7+
#include "audio/audioplayerfactory.h"
68

79
using namespace libscratchcpp;
810

@@ -36,3 +38,24 @@ void Sound::setSampleCount(int newSampleCount)
3638
{
3739
impl->sampleCount = newSampleCount;
3840
}
41+
42+
void Sound::start()
43+
{
44+
impl->player->start();
45+
}
46+
47+
void Sound::stop()
48+
{
49+
impl->player->stop();
50+
}
51+
52+
bool Sound::isPlaying()
53+
{
54+
return impl->player->isPlaying();
55+
}
56+
57+
void Sound::processData(unsigned int size, void *data)
58+
{
59+
if (!impl->player->load(size, data, impl->rate))
60+
std::cerr << "Failed to load sound " << name() << std::endl;
61+
}

src/scratch/sound_p.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
// SPDX-License-Identifier: Apache-2.0
22

33
#include "sound_p.h"
4+
#include "audio/audioplayerfactory.h"
45

56
using namespace libscratchcpp;
67

8+
IAudioPlayerFactory *SoundPrivate::playerFactory = nullptr;
9+
710
SoundPrivate::SoundPrivate()
811
{
12+
// NOTE: playerFactory must be initialized in the constructor to avoid static initialization order fiasco
13+
if (!playerFactory)
14+
playerFactory = AudioPlayerFactory::instance().get();
15+
16+
player = playerFactory->create();
917
}

src/scratch/sound_p.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
#include <string>
66

7+
#include "audio/iaudioplayerfactory.h"
8+
#include "audio/iaudioplayer.h"
9+
710
namespace libscratchcpp
811
{
912

@@ -14,6 +17,8 @@ struct SoundPrivate
1417

1518
int rate = 0;
1619
int sampleCount = 0;
20+
static IAudioPlayerFactory *playerFactory;
21+
std::shared_ptr<IAudioPlayer> player = nullptr;
1722
};
1823

1924
} // namespace libscratchcpp

test/assets/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ add_executable(
3939
target_link_libraries(
4040
sound_test
4141
GTest::gtest_main
42+
GTest::gmock_main
4243
scratchcpp
44+
scratchcpp_mocks
4345
)
4446

4547
gtest_discover_tests(sound_test)

test/assets/sound_test.cpp

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,31 @@
11
#include <scratchcpp/sound.h>
2+
#include <scratch/sound_p.h>
3+
#include <audioplayerfactorymock.h>
4+
#include <audioplayermock.h>
25

36
#include "../common.h"
47

58
using namespace libscratchcpp;
69

7-
TEST(SoundTest, Constructors)
10+
using ::testing::Return;
11+
12+
class SoundTest : public testing::Test
13+
{
14+
public:
15+
void SetUp() override
16+
{
17+
m_player = std::make_shared<AudioPlayerMock>();
18+
SoundPrivate::playerFactory = &m_playerFactory;
19+
EXPECT_CALL(m_playerFactory, create()).WillOnce(Return(m_player));
20+
}
21+
22+
void TearDown() override { SoundPrivate::playerFactory = nullptr; }
23+
24+
AudioPlayerFactoryMock m_playerFactory;
25+
std::shared_ptr<AudioPlayerMock> m_player;
26+
};
27+
28+
TEST_F(SoundTest, Constructors)
829
{
930
Sound sound("sound1", "a", "wav");
1031
ASSERT_EQ(sound.id(), "a");
@@ -15,18 +36,59 @@ TEST(SoundTest, Constructors)
1536
ASSERT_EQ(sound.sampleCount(), 0);
1637
}
1738

18-
TEST(SoundTest, Rate)
39+
TEST_F(SoundTest, Rate)
1940
{
2041
Sound sound("sound1", "a", "wav");
2142

2243
sound.setRate(2);
2344
ASSERT_EQ(sound.rate(), 2);
2445
}
2546

26-
TEST(SoundTest, SampleCount)
47+
TEST_F(SoundTest, SampleCount)
2748
{
2849
Sound sound("sound1", "a", "wav");
2950

3051
sound.setSampleCount(10);
3152
ASSERT_EQ(sound.sampleCount(), 10);
3253
}
54+
55+
TEST_F(SoundTest, ProcessData)
56+
{
57+
Sound sound("sound1", "a", "wav");
58+
sound.setRate(44100);
59+
60+
const char *data = "abc";
61+
void *dataPtr = const_cast<void *>(static_cast<const void *>(data));
62+
63+
EXPECT_CALL(*m_player, load(3, dataPtr, 44100));
64+
sound.setData(3, dataPtr);
65+
}
66+
67+
TEST_F(SoundTest, Start)
68+
{
69+
Sound sound("sound1", "a", "wav");
70+
71+
EXPECT_CALL(*m_player, start());
72+
sound.start();
73+
}
74+
75+
TEST_F(SoundTest, Stop)
76+
{
77+
Sound sound("sound1", "a", "wav");
78+
79+
EXPECT_CALL(*m_player, stop());
80+
sound.stop();
81+
}
82+
83+
TEST_F(SoundTest, IsPlaying)
84+
{
85+
Sound sound("sound1", "a", "wav");
86+
87+
EXPECT_CALL(*m_player, isPlaying()).WillOnce(Return(true));
88+
ASSERT_TRUE(sound.isPlaying());
89+
90+
EXPECT_CALL(*m_player, isPlaying()).WillOnce(Return(false));
91+
ASSERT_FALSE(sound.isPlaying());
92+
93+
SoundPrivate::playerFactory = nullptr;
94+
}

test/mocks/audioplayerfactorymock.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#pragma once
2+
3+
#include <audio/iaudioplayerfactory.h>
4+
#include <gmock/gmock.h>
5+
6+
using namespace libscratchcpp;
7+
8+
class AudioPlayerFactoryMock : public IAudioPlayerFactory
9+
{
10+
public:
11+
MOCK_METHOD(std::shared_ptr<IAudioPlayer>, create, (), (const, override));
12+
};

test/mocks/audioplayermock.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#pragma once
2+
3+
#include <audio/iaudioplayer.h>
4+
#include <gmock/gmock.h>
5+
6+
using namespace libscratchcpp;
7+
8+
class AudioPlayerMock : public IAudioPlayer
9+
{
10+
public:
11+
MOCK_METHOD(bool, load, (unsigned int, const void *, unsigned long), (override));
12+
13+
MOCK_METHOD(void, start, (), (override));
14+
MOCK_METHOD(void, stop, (), (override));
15+
16+
MOCK_METHOD(bool, isPlaying, (), (override, const));
17+
};

0 commit comments

Comments
 (0)