Brenta Engine 1.2
Loading...
Searching...
No Matches
miniaudio.cpp
1// SPDX-License-Identifier: MIT
2// Author: Giovanni Santini
3// Mail: giovanni.santini@proton.me
4// Github: @San7o
5
6#include <brenta/drivers/miniaudio.hpp>
7#include <brenta/logger.hpp>
8
9using namespace brenta;
10
11//
12// Static variables
13//
14
15std::expected<void, std::string> MiniaudioDriver::initialize()
16{
17 ma_result result;
18 result = ma_engine_init(NULL, &this->engine);
19 if (result != MA_SUCCESS)
20 {
21 ERROR("Miniaudio: error initializing miniaudio driver: {}",
22 ma_result_description(result));
23 return std::unexpected("Initializing miniaudio driver");
24 }
25
26 for (int i = 0; i < BRENTA_NUM_STREAMS; ++i)
27 {
28 this->stream_pool[i].in_use = false;
29 this->stream_pool[i].generation = -1;
30 this->stream_pool[i].sound_index = -1;
31 }
32
33 INFO("Miniaudio: driver initialized");
34 return {};
35}
36
37std::expected<void, std::string> MiniaudioDriver::terminate()
38{
39 ma_engine_uninit(&this->engine);
40
41 // Clean up the stram pool
42 for (int i = 0; i < BRENTA_NUM_STREAMS; ++i)
43 {
44 auto& slot = this->stream_pool[i];
45 if (slot.in_use)
46 {
47 ma_sound_uninit(&slot.handle);
48 ma_audio_buffer_uninit(&slot.local_buffer);
49 slot.in_use = false;
50 }
51 }
52
53 // Clean up the Asset Pool
54 for (auto& asset : this->sound_assets)
55 {
56 if (asset.in_use)
57 {
58 ma_audio_buffer_uninit(&asset.buffer);
59 asset.in_use = false;
60 }
61 }
62
63 this->sound_assets.clear();
64
65 INFO("Miniaudio: driver terminated safely");
66 return {};
67}
68
69//
70// Member functions
71//
72
73std::optional<MiniaudioDriver::MiniaudioStream> MiniaudioDriver::create_stream()
74{
75 MiniaudioStream stream_handle;
76 ma_result res = ma_sound_init_ex(&this->engine, NULL, &stream_handle);
77 if (res != MA_SUCCESS)
78 {
79 ERROR("Miniaudio: error creating audio stream");
80 return {};
81 }
82
83 return stream_handle;
84}
85
86std::optional<Stream>
87MiniaudioDriver::request_stream(const SoundAsset& sound)
88{
89 for (int i = 0; i < BRENTA_NUM_STREAMS; ++i)
90 {
91 if (!this->stream_pool[i].in_use)
92 {
93 auto& slot = this->stream_pool[i];
94 auto& asset = this->sound_assets[sound.get_id()];
95
96 // Miniaudio boilerplate from here
97
98 ma_format format;
99 ma_uint32 channels;
100 ma_uint32 sampleRate;
101 ma_data_source_get_data_format(&asset.buffer, &format, &channels, &sampleRate, NULL, 0);
102
103 ma_uint64 frameCount;
104 ma_data_source_get_length_in_pcm_frames(&asset.buffer, &frameCount);
105
106 void* pData = const_cast<void*>(asset.buffer.ref.pData);
107
108 // 3. Initialize the streams's loal buffer
109 ma_audio_buffer_config config =
110 ma_audio_buffer_config_init(format,
111 channels,
112 frameCount,
113 pData,
114 NULL);
115 config.sampleRate = sampleRate;
116
117 ma_result res = ma_audio_buffer_init(&config, &slot.local_buffer);
118 if (res != MA_SUCCESS)
119 {
120 ma_audio_buffer_uninit(&slot.local_buffer);
121 ERROR("Miniaudio: Failed to create local buffer in sound {}",
122 sound.get_id(), i);
123 return {};
124 }
125
126 // Initialize the sound using the local_buffer
127 res = ma_sound_init_from_data_source(&this->engine,
128 &slot.local_buffer,
129 0,
130 NULL,
131 &slot.handle);
132 if (res != MA_SUCCESS)
133 {
134 ma_audio_buffer_uninit(&slot.local_buffer);
135 ERROR("Miniaudio: Failed to bind sound {} to stream {}",
136 sound.get_id(), i);
137 return {};
138 }
139
140 DEBUG("Miniaudio: Binded sound {} to stream {}",
141 sound.get_id(), i);
142
143 //ma_sound_start(&slot.handle);
144 this->stream_pool[i].in_use = true;
145 this->stream_pool[i].sound_index = sound.get_id();
146 this->stream_pool[i].generation = asset.generation;
147 return i;
148 }
149 }
150 return {};
151}
152
153void MiniaudioDriver::release_stream(Stream stream)
154{
155 auto& slot = this->stream_pool[stream];
156
157 if (!slot.in_use)
158 return;
159
160 ma_sound_stop(&slot.handle);
161 ma_sound_uninit(&slot.handle);
162 ma_audio_buffer_uninit(&slot.local_buffer);
163
164 slot.in_use = false;
165 slot.sound_index = -1;
166 slot.generation++;
167
168 DEBUG("Miniaudio: unbinded stream {}", stream);
169}
170
171std::optional<SoundAsset>
172MiniaudioDriver::load(const std::filesystem::path& path)
173{
174 int slot_index = 0;
175 bool found;
176
177 for (unsigned int i = 0; i < this->sound_assets.size(); ++i)
178 {
179 if (!this->sound_assets[i].in_use)
180 {
181 found = true;
182 slot_index = i;
183 break;
184 }
185 }
186 if (!found)
187 {
188 slot_index = sound_assets.size();
189 this->sound_assets.emplace_back();
190 }
191
192 auto& sound_slot = this->sound_assets[slot_index];
193
194 ma_decoder tmp_decoder;
195 ma_result res =
196 ma_decoder_init_file(path.string().c_str(),
197 NULL,
198 &tmp_decoder);
199 if (res != MA_SUCCESS)
200 {
201 ERROR("Miniaudio: error loading sound decoder from path {}",
202 path.string());
203 return {};
204 }
205
206 ma_uint64 total_frames;
207 ma_decoder_get_length_in_pcm_frames(&tmp_decoder, &total_frames);
208
209 size_t bufferSize = total_frames * tmp_decoder.outputChannels
210 * ma_get_bytes_per_sample(tmp_decoder.outputFormat);
211 void* pRawData = malloc(bufferSize);
212
213 ma_uint64 frames_read;
214 ma_decoder_read_pcm_frames(&tmp_decoder, pRawData, total_frames, &frames_read);
215
216 ma_audio_buffer_config config =
217 ma_audio_buffer_config_init(tmp_decoder.outputFormat,
218 tmp_decoder.outputChannels,
219 total_frames,
220 pRawData,
221 NULL);
222 res =
223 ma_audio_buffer_init_copy(&config, &sound_slot.buffer);
224 if (res != MA_SUCCESS)
225 {
226 ERROR("Miniaudio: error loading sound buffer from path {}",
227 path.string());
228 return {};
229 }
230
231 free(pRawData);
232 ma_decoder_uninit(&tmp_decoder);
233
234 sound_slot.in_use = true;
235 sound_slot.generation++;
236
237 INFO("Miniaudio: loaded sound {} from {}",
238 slot_index, path.string());
239
240 return slot_index;
241}
242
243void MiniaudioDriver::unload(const SoundAsset& sound)
244{
245 auto id = sound.get_id();
246
247 if (id < 0 || !this->sound_assets[id].in_use)
248 return;
249
250 auto& asset_slot = this->sound_assets[id];
251
252 // Disconnect any streams currently playing this asset
253 for (int i = 0; i < BRENTA_NUM_STREAMS; ++i)
254 {
255 auto& stream = this->stream_pool[i];
256
257 if (stream.in_use && stream.sound_index == id)
258 {
259 this->release_stream(i);
260 DEBUG("Miniaudio: Force-released stream {} because asset {} was unloaded", i, id);
261 }
262 }
263
264 ma_audio_buffer_uninit(&asset_slot.buffer);
265
266 asset_slot.in_use = false;
267 asset_slot.generation++;
268 return;
269}
270
271void MiniaudioDriver::play(Stream stream)
272{
273 auto& stream_slot = this->stream_pool[stream];
274 if (!stream_slot.in_use)
275 {
276 ERROR("Miniaudio: play: cannot use stream {}", stream);
277 return;
278 }
279 if (stream_slot.generation
280 != this->sound_assets[stream_slot.sound_index].generation)
281 {
282 ERROR("Miniaudio: play: stream {} does not match driver's stream", stream);
283 return;
284 }
285
286 ma_sound_start(&stream_slot.handle);
287
288 INFO("Miniaudio: started playing on stream {}", stream);
289 return;
290}
291
292void MiniaudioDriver::stop(Stream stream)
293{
294 auto& stream_slot = this->stream_pool[stream];
295 if (!stream_slot.in_use)
296 {
297 ERROR("Miniaudio: stop: cannot use stream {}", stream);
298 return;
299 }
300 if (stream_slot.generation
301 != this->sound_assets[stream_slot.sound_index].generation)
302 {
303 ERROR("Miniaudio: stop: stream {} does not match driver's stream", stream);
304 return;
305 }
306
307 ma_sound_stop(&stream_slot.handle);
308
309 INFO("Miniaudio: stopped stream {}", stream);
310 return;
311}
312
313void MiniaudioDriver::set_volume(Stream stream, float volume)
314{
315 auto& stream_slot = this->stream_pool[stream];
316 if (!stream_slot.in_use)
317 {
318 ERROR("Miniaudio: stop: cannot use stream {}", stream);
319 return;
320 }
321 if (stream_slot.generation
322 != this->sound_assets[stream_slot.sound_index].generation)
323 {
324 ERROR("Miniaudio: stop: stream {} does not match driver's stream", stream);
325 return;
326 }
327
328 ma_sound_set_volume(&stream_slot.handle, volume);
329
330 INFO("Miniaudio: volume for stream {} set to {}", stream, volume);
331 return;
332}