Skip to content

Commit 7d0608f

Browse files
authored
Merge pull request #405 from scratchcpp/audio_api
Add audio API
2 parents a2b247d + 66c57d8 commit 7d0608f

32 files changed

+89627
-9
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ include_directories(thirdparty/spimpl)
6666
target_link_libraries(scratchcpp PRIVATE nlohmann_json::nlohmann_json)
6767
target_link_libraries(scratchcpp PRIVATE utf8cpp)
6868
target_link_libraries(scratchcpp PRIVATE zip)
69+
target_link_libraries(scratchcpp PRIVATE scratchcpp-audio)
6970

7071
if (LIBSCRATCHCPP_NETWORK_SUPPORT)
7172
include(FetchContent)

include/scratchcpp/sound.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,24 @@ 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 setVolume(double volume);
29+
30+
virtual void start();
31+
virtual void stop();
32+
33+
virtual bool isPlaying();
34+
35+
protected:
36+
void processData(unsigned int size, void *data) override;
37+
2738
private:
2839
spimpl::unique_impl_ptr<SoundPrivate> impl;
2940
};

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ add_subdirectory(blocks)
1515
add_subdirectory(engine)
1616
add_subdirectory(internal)
1717
add_subdirectory(scratch)
18+
add_subdirectory(audio)

src/audio/CMakeLists.txt

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
option(LIBSCRATCHCPP_AUDIO_SUPPORT "Audio support" ON)
2+
3+
add_library(scratchcpp-audio STATIC)
4+
set(MINIAUDIO_SRC thirdparty/miniaudio)
5+
6+
if (LIBSCRATCHCPP_AUDIO_SUPPORT)
7+
add_library(miniaudio STATIC
8+
${MINIAUDIO_SRC}/miniaudio.c
9+
${MINIAUDIO_SRC}/miniaudio.h
10+
)
11+
target_include_directories(scratchcpp-audio PUBLIC ${CMAKE_CURRENT_LIST_DIR}/${MINIAUDIO_SRC})
12+
target_link_libraries(scratchcpp-audio PRIVATE miniaudio)
13+
target_compile_definitions(scratchcpp-audio PUBLIC LIBSCRATCHCPP_AUDIO_SUPPORT)
14+
endif()
15+
16+
target_sources(scratchcpp-audio
17+
PUBLIC
18+
iaudioplayer.h
19+
iaudioplayerfactory.h
20+
audioplayerfactory.cpp
21+
audioplayerfactory.h
22+
)
23+
24+
if (LIBSCRATCHCPP_AUDIO_SUPPORT)
25+
target_sources(scratchcpp-audio
26+
PUBLIC
27+
audioengine.cpp
28+
audioengine.h
29+
audioplayer.cpp
30+
audioplayer.h
31+
)
32+
else()
33+
target_sources(scratchcpp-audio
34+
PUBLIC
35+
audioplayerstub.cpp
36+
audioplayerstub.h
37+
)
38+
endif()

src/audio/audioengine.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#include <iostream>
4+
5+
#include "audioengine.h"
6+
7+
using namespace libscratchcpp;
8+
9+
AudioEngine AudioEngine::instance;
10+
11+
ma_engine *AudioEngine::engine()
12+
{
13+
return &instance.m_engine;
14+
}
15+
16+
bool AudioEngine::initialized()
17+
{
18+
return instance.m_initialized;
19+
}
20+
21+
AudioEngine::AudioEngine()
22+
{
23+
ma_result result;
24+
result = ma_engine_init(NULL, &m_engine);
25+
26+
if (result != MA_SUCCESS) {
27+
std::cerr << "Failed to initialize audio engine." << std::endl;
28+
return;
29+
}
30+
31+
m_initialized = true;
32+
}
33+
34+
AudioEngine::~AudioEngine()
35+
{
36+
if (m_initialized)
37+
ma_engine_uninit(&m_engine);
38+
}

src/audio/audioengine.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#pragma once
4+
5+
#include <memory>
6+
#include <miniaudio.h>
7+
8+
namespace libscratchcpp
9+
{
10+
11+
// This is a singleton which initializes and uninitializes the miniaudio engine
12+
class AudioEngine
13+
{
14+
public:
15+
static ma_engine *engine();
16+
static bool initialized();
17+
18+
private:
19+
AudioEngine();
20+
~AudioEngine();
21+
22+
static AudioEngine instance;
23+
ma_engine m_engine;
24+
bool m_initialized = false;
25+
};
26+
27+
} // namespace libscratchcpp

src/audio/audioplayer.cpp

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#include <iostream>
4+
5+
#include "audioplayer.h"
6+
#include "audioengine.h"
7+
8+
using namespace libscratchcpp;
9+
10+
AudioPlayer::AudioPlayer()
11+
{
12+
m_decoder = new ma_decoder;
13+
m_sound = new ma_sound;
14+
}
15+
16+
AudioPlayer::~AudioPlayer()
17+
{
18+
if (m_loaded) {
19+
ma_sound_uninit(m_sound);
20+
ma_decoder_uninit(m_decoder);
21+
}
22+
23+
delete m_decoder;
24+
delete m_sound;
25+
}
26+
27+
bool AudioPlayer::load(unsigned int size, const void *data, unsigned long sampleRate)
28+
{
29+
if (!AudioEngine::initialized())
30+
return false;
31+
32+
ma_engine *engine = AudioEngine::engine();
33+
34+
if (!data || size == 0)
35+
return false;
36+
37+
ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 2, sampleRate);
38+
ma_result result = ma_decoder_init_memory(data, size, &config, m_decoder);
39+
40+
if (result != MA_SUCCESS) {
41+
std::cerr << "Failed to decode sound." << std::endl;
42+
return false;
43+
}
44+
45+
ma_result initResult = ma_sound_init_from_data_source(engine, m_decoder, MA_SOUND_FLAG_DECODE, NULL, m_sound);
46+
47+
if (initResult != MA_SUCCESS) {
48+
std::cerr << "Failed to init sound." << std::endl;
49+
ma_decoder_uninit(m_decoder);
50+
return false;
51+
}
52+
53+
m_loaded = true;
54+
return true;
55+
}
56+
57+
void AudioPlayer::setVolume(float volume)
58+
{
59+
if (!AudioEngine::initialized())
60+
return;
61+
62+
ma_sound_set_volume(m_sound, volume);
63+
}
64+
65+
void AudioPlayer::start()
66+
{
67+
if (!AudioEngine::initialized())
68+
return;
69+
70+
ma_result result = ma_sound_start(m_sound);
71+
72+
if (result != MA_SUCCESS) {
73+
std::cerr << "Failed to start sound." << std::endl;
74+
m_started = false;
75+
} else
76+
m_started = true;
77+
}
78+
79+
void AudioPlayer::stop()
80+
{
81+
if (!AudioEngine::initialized())
82+
return;
83+
84+
ma_result result = ma_sound_stop(m_sound);
85+
m_started = false;
86+
87+
if (result != MA_SUCCESS)
88+
std::cerr << "Failed to stop sound." << std::endl;
89+
}
90+
91+
bool AudioPlayer::isPlaying() const
92+
{
93+
if (!AudioEngine::initialized())
94+
return false;
95+
96+
return m_started && !m_sound->atEnd;
97+
}

src/audio/audioplayer.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#pragma once
4+
5+
#include "iaudioplayer.h"
6+
7+
struct ma_decoder;
8+
struct ma_sound;
9+
10+
namespace libscratchcpp
11+
{
12+
13+
class AudioPlayer : public IAudioPlayer
14+
{
15+
public:
16+
AudioPlayer();
17+
~AudioPlayer();
18+
19+
bool load(unsigned int size, const void *data, unsigned long sampleRate) override;
20+
void setVolume(float volume) override;
21+
22+
void start() override;
23+
void stop() override;
24+
25+
bool isPlaying() const override;
26+
27+
private:
28+
ma_decoder *m_decoder = nullptr;
29+
ma_sound *m_sound;
30+
bool m_loaded = false;
31+
bool m_started = false;
32+
};
33+
34+
} // namespace libscratchcpp

src/audio/audioplayerfactory.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#include "audioplayerfactory.h"
4+
5+
#ifdef LIBSCRATCHCPP_AUDIO_SUPPORT
6+
#include "audioplayer.h"
7+
#else
8+
#include "audioplayerstub.h"
9+
#endif
10+
11+
using namespace libscratchcpp;
12+
13+
std::shared_ptr<AudioPlayerFactory> AudioPlayerFactory::m_instance = std::make_shared<AudioPlayerFactory>();
14+
15+
AudioPlayerFactory::AudioPlayerFactory()
16+
{
17+
}
18+
19+
std::shared_ptr<AudioPlayerFactory> AudioPlayerFactory::instance()
20+
{
21+
return m_instance;
22+
}
23+
24+
std::shared_ptr<IAudioPlayer> AudioPlayerFactory::create() const
25+
{
26+
#ifdef LIBSCRATCHCPP_AUDIO_SUPPORT
27+
return std::make_shared<AudioPlayer>();
28+
#else
29+
return std::make_shared<AudioPlayerStub>();
30+
#endif
31+
}

src/audio/audioplayerfactory.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#pragma once
4+
5+
#include "iaudioplayerfactory.h"
6+
7+
namespace libscratchcpp
8+
{
9+
10+
class AudioPlayerFactory : public IAudioPlayerFactory
11+
{
12+
public:
13+
AudioPlayerFactory();
14+
15+
static std::shared_ptr<AudioPlayerFactory> instance();
16+
std::shared_ptr<IAudioPlayer> create() const override;
17+
18+
private:
19+
static std::shared_ptr<AudioPlayerFactory> m_instance;
20+
};
21+
22+
} // namespace libscratchcpp

src/audio/audioplayerstub.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#include "audioplayerstub.h"
4+
5+
using namespace libscratchcpp;
6+
7+
AudioPlayerStub::AudioPlayerStub()
8+
{
9+
}
10+
11+
bool AudioPlayerStub::load(unsigned int size, const void *data, unsigned long sampleRate)
12+
{
13+
return true;
14+
}
15+
16+
void AudioPlayerStub::setVolume(float volume)
17+
{
18+
}
19+
20+
void AudioPlayerStub::start()
21+
{
22+
}
23+
24+
void AudioPlayerStub::stop()
25+
{
26+
}
27+
28+
bool AudioPlayerStub::isPlaying() const
29+
{
30+
return false;
31+
}

src/audio/audioplayerstub.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#pragma once
4+
5+
#include "iaudioplayer.h"
6+
7+
namespace libscratchcpp
8+
{
9+
10+
class AudioPlayerStub : public IAudioPlayer
11+
{
12+
public:
13+
AudioPlayerStub();
14+
15+
bool load(unsigned int size, const void *data, unsigned long sampleRate) override;
16+
void setVolume(float volume) override;
17+
18+
void start() override;
19+
void stop() override;
20+
21+
bool isPlaying() const override;
22+
};
23+
24+
} // namespace libscratchcpp

0 commit comments

Comments
 (0)