Brenta Engine 1.2
Loading...
Searching...
No Matches
audio.cpp
1// SPDX-License-Identifier: MIT
2// Author: Giovanni Santini
3// Mail: giovanni.santini@proton.me
4// Github: @San7o
5
6#include <brenta/audio.hpp>
7#include <brenta/logger.hpp>
8
9using namespace brenta;
10
11//
12// Static variables
13//
14
15std::vector<std::tuple<types::sound_id_t, std::string,
16 types::stream_id_t>> audio::init_sounds;
17std::vector<std::pair<types::stream_id_t, float>> audio::init_streams;
18std::unordered_map<types::sound_id_t, types::sound_t> audio::sounds;
19std::unordered_map<types::stream_id_t, types::stream_t> audio::streams;
20ma_engine audio::engine;
21bool audio::initialized = false;
22const std::string audio::subsystem_name = "audio";
23
24//
25// Subsystem interface
26//
27
28std::expected<void, subsystem::error> audio::initialize()
29{
30 if (this->is_initialized()) return {};
31 ma_result result;
32 result = ma_engine_init(NULL, &audio::engine);
33 if (result != MA_SUCCESS)
34 {
35 ERROR("{}: error initializing engine: {}",
36 audio::subsystem_name, ma_result_description(result));
37 return std::unexpected("Initializing audio backend");
38 }
39
40 for (auto& f : audio::init_sounds)
41 {
42 if (!audio::load(std::get<0>(f), std::get<1>(f), std::get<2>(f)).has_value())
43 return std::unexpected("Loading audio " + get<0>(f));
44 }
45
46 for (auto& s : audio::init_streams)
47 {
48 if (!audio::create_stream(s.first).has_value())
49 return std::unexpected("Creating stream " + s.first);
50 if (!audio::stream_set_volume(s.first, s.second).has_value())
51 return std::unexpected("Setting volume for stream " + s.first);
52 }
53
54 if (!audio::get_stream("default"))
55 {
56 if (!audio::create_stream("default").has_value())
57 return std::unexpected("Creating stream default");
58 }
59
60 audio::initialized = true;
61 INFO("{}: initialized", audio::subsystem_name);
62 return {};
63}
64
65std::expected<void, subsystem::error> audio::terminate()
66{
67 if (!this->is_initialized()) return {};
68
69 for (auto sound : audio::sounds)
70 ma_sound_uninit(&sound.second);
71
72 for (auto stream : audio::streams)
73 ma_sound_group_uninit(&stream.second);
74
75 ma_engine_uninit(&audio::engine);
76
77 audio::initialized = false;
78 INFO("{}: termianted", audio::subsystem_name);
79 return {};
80}
81
82std::string audio::name()
83{
84 return audio::subsystem_name;
85}
86
88{
89 return audio::initialized;
90}
91
92//
93// Member functions
94//
95
96audio &audio::instance()
97{
98 static audio _audio;
99 return _audio;
100}
101
102std::expected<void, audio::error>
103audio::load(const types::sound_id_t &sound_id,
104 const std::string &path,
105 const types::stream_id_t &stream_id)
106{
107 types::stream_t *stream = audio::get_stream(stream_id);
108 if (!stream)
109 {
110 audio::create_stream(stream_id);
111 stream = audio::get_stream(stream_id);
112 if (!stream)
113 {
114 ERROR("{}: error stream {} not found",
115 audio::subsystem_name, stream_id);
116 return std::unexpected(audio::error::stream_not_found);
117 }
118 }
119
120 types::sound_t sound = {};
121 audio::sounds.insert({sound_id, sound});
122 if (ma_sound_init_from_file(&audio::engine, path.c_str(), 0, stream, NULL,
123 &audio::sounds.at(sound_id)) != MA_SUCCESS)
124 {
125 ERROR("{}: error loading sound {} from path {}",
126 audio::subsystem_name, sound_id, path);
127 return std::unexpected(audio::error::init_from_file);
128 }
129
130 INFO("{}: loaded sound {} from {} in stream {}",
131 audio::subsystem_name, sound_id, path, stream_id);
132 return {};
133}
134
135std::expected<void, audio::error>
136audio::play(const types::sound_id_t &id)
137{
138 types::sound_t *sound = &audio::sounds.at(id);
139 if (!sound)
140 {
141 ERROR("{}: sound with id {} not found",
142 audio::subsystem_name, id);
143 return std::unexpected(audio::error::sound_not_found);
144 }
145 ma_sound_start(sound);
146 return {};
147}
148
149std::expected<void, audio::error>
150audio::create_stream(const types::stream_id_t &id)
151{
152 types::stream_t *stream = audio::get_stream(id);
153 if (stream) return {};
154
155 types::stream_t s = {};
156 audio::streams.insert({id, s});
157 stream = &audio::streams.at(id);
158 if (ma_sound_group_init(&audio::engine, 0, NULL, stream)
159 != MA_SUCCESS)
160 {
161 ERROR("{}: error creating audio stream {}",
162 audio::subsystem_name, id);
163 return std::unexpected(audio::error::stream_init);
164 }
165
166 INFO("{}: stream {} created", audio::subsystem_name, id);
167 return {};
168}
169
170types::stream_t *audio::get_stream(const types::stream_id_t &id)
171{
172 if (audio::streams.find(id) == audio::streams.end())
173 {
174 return nullptr;
175 }
176 return &audio::streams.at(id);
177}
178
179std::expected<void, audio::error>
180audio::stream_set_volume(const types::stream_id_t &id, float volume)
181{
182 types::stream_t *stream = audio::get_stream(id);
183 if (!stream)
184 {
185 ERROR("{}: could not set volume: Audio stream {} not found",
186 audio::subsystem_name, id);
187 return std::unexpected(audio::error::stream_not_found);
188 }
189
190 ma_sound_group_set_volume(stream, volume);
191
192 INFO("{}: volume for stream {} set to {}",
193 audio::subsystem_name, id, volume);
194 return {};
195}
196
197std::expected<void, audio::error>
198audio::stream_stop(const types::stream_id_t &id)
199{
200 types::stream_t *stream = audio::get_stream(id);
201 if (stream == nullptr)
202 {
203 ERROR("{}: could not pause stream: stream {} not found",
204 audio::subsystem_name, id);
205 return std::unexpected(audio::error::stream_not_found);
206 }
207
208 if (ma_sound_group_stop(stream) != MA_SUCCESS)
209 {
210 ERROR("{}: error stopping stream {}",
211 audio::subsystem_name, id);
212 return std::unexpected(audio::error::stream_stop);
213 }
214
215 INFO("{}: stream {} stopped", audio::subsystem_name, id);
216 return {};
217}
218
219std::expected<void, audio::error>
220audio::stream_start(const types::stream_id_t &id)
221{
222 auto stream = audio::get_stream(id);
223 if (stream == nullptr)
224 {
225 ERROR("{}: could not start stream: Audio stream {} not found",
226 audio::subsystem_name, id);
227 return std::unexpected(audio::error::stream_not_found);
228 }
229
230 if (ma_sound_group_start(stream) != MA_SUCCESS)
231 {
232 ERROR("{}: error starting stream {}", audio::subsystem_name, id);
233 return std::unexpected(audio::error::stream_start);
234 }
235
236 INFO("{}: stream {} started", audio::subsystem_name, id);
237 return {};
238}
239
240//
241// Builder
242//
243
245audio::builder::sound(const types::sound_id_t &sound_id,
246 const std::string &path,
247 const types::stream_id_t &stream_id)
248{
249 this->init_sounds.push_back(std::make_tuple(sound_id, path, stream_id));
250 return *this;
251}
252
254audio::builder::stream(const types::stream_id_t &id,
255 float volume)
256{
257 this->init_streams.push_back(std::make_pair(id, volume));
258 return *this;
259}
260
261subsystem &audio::builder::build()
262{
263 audio::init_sounds = this->init_sounds;
264 audio::init_streams = this->init_streams;
265 return audio::instance();
266}
Audio subsystem.
Definition audio.hpp:44
std::expected< void, subsystem::error > terminate() override
Terminate the audio system.
Definition audio.cpp:65
static std::unordered_map< types::sound_id_t, types::sound_t > sounds
Map of sound files.
Definition audio.hpp:53
std::expected< void, subsystem::error > initialize() override
Initialize the audio subsystem.
Definition audio.cpp:28
static std::expected< void, audio::error > load(const types::sound_id_t &sound_id, const std::string &path, const types::stream_id_t &stream_id="default")
Load a sound from path on a stream.
Definition audio.cpp:103
bool is_initialized() override
Returns true if the subsystem is initialized.
Definition audio.cpp:87
static std::unordered_map< types::stream_id_t, types::stream_t > streams
Map of audio streams.
Definition audio.hpp:62
static std::expected< void, audio::error > stream_set_volume(const types::stream_id_t &id, float volume)
Set the volume of a stream.
Definition audio.cpp:180
static std::expected< void, audio::error > play(const types::sound_id_t &id)
Play a sound on its stream.
Definition audio.cpp:136
std::string name() override
Returns the name of the sybsystem.
Definition audio.cpp:82
Subsystem interface.
Definition subsystem.hpp:22