1/**************************************************************************/
2/* audio_stream_ogg_vorbis.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "audio_stream_ogg_vorbis.h"
32
33#include "core/io/file_access.h"
34#include "core/variant/typed_array.h"
35
36#include "modules/vorbis/resource_importer_ogg_vorbis.h"
37#include <ogg/ogg.h>
38
39int AudioStreamPlaybackOggVorbis::_mix_internal(AudioFrame *p_buffer, int p_frames) {
40 ERR_FAIL_COND_V(!ready, 0);
41
42 if (!active) {
43 return 0;
44 }
45
46 int todo = p_frames;
47
48 int beat_length_frames = -1;
49 bool beat_loop = vorbis_stream->has_loop();
50 if (beat_loop && vorbis_stream->get_bpm() > 0 && vorbis_stream->get_beat_count() > 0) {
51 beat_length_frames = vorbis_stream->get_beat_count() * vorbis_data->get_sampling_rate() * 60 / vorbis_stream->get_bpm();
52 }
53
54 while (todo > 0 && active) {
55 AudioFrame *buffer = p_buffer;
56 buffer += p_frames - todo;
57
58 int to_mix = todo;
59 if (beat_length_frames >= 0 && (beat_length_frames - (int)frames_mixed) < to_mix) {
60 to_mix = MAX(0, beat_length_frames - (int)frames_mixed);
61 }
62
63 int mixed = _mix_frames_vorbis(buffer, to_mix);
64 ERR_FAIL_COND_V(mixed < 0, 0);
65 todo -= mixed;
66 frames_mixed += mixed;
67
68 if (loop_fade_remaining < FADE_SIZE) {
69 int to_fade = loop_fade_remaining + MIN(FADE_SIZE - loop_fade_remaining, mixed);
70 for (int i = loop_fade_remaining; i < to_fade; i++) {
71 buffer[i - loop_fade_remaining] += loop_fade[i] * (float(FADE_SIZE - i) / float(FADE_SIZE));
72 }
73 loop_fade_remaining = to_fade;
74 }
75
76 if (beat_length_frames >= 0) {
77 /**
78 * Length determined by beat length
79 * This code is commented out because, in practice, it is preferred that the fade
80 * is done by the transitioner and this stream just goes on until it ends while fading out.
81 *
82 * End fade implementation is left here for reference in case at some point this feature
83 * is desired.
84
85 if (!beat_loop && (int)frames_mixed > beat_length_frames - FADE_SIZE) {
86 print_line("beat length fade/after mix?");
87 //No loop, just fade and finish
88 for (int i = 0; i < mixed; i++) {
89 int idx = frames_mixed + i - mixed;
90 buffer[i] *= 1.0 - float(MAX(0, (idx - (beat_length_frames - FADE_SIZE)))) / float(FADE_SIZE);
91 }
92 if ((int)frames_mixed == beat_length_frames) {
93 for (int i = p_frames - todo; i < p_frames; i++) {
94 p_buffer[i] = AudioFrame(0, 0);
95 }
96 active = false;
97 break;
98 }
99 } else
100 **/
101
102 if (beat_loop && beat_length_frames <= (int)frames_mixed) {
103 // End of file when doing beat-based looping. <= used instead of == because importer editing
104 if (!have_packets_left && !have_samples_left) {
105 //Nothing remaining, so do nothing.
106 loop_fade_remaining = FADE_SIZE;
107 } else {
108 // Add some loop fade;
109 int faded_mix = _mix_frames_vorbis(loop_fade, FADE_SIZE);
110
111 for (int i = faded_mix; i < FADE_SIZE; i++) {
112 // In case lesss was mixed, pad with zeros
113 loop_fade[i] = AudioFrame(0, 0);
114 }
115 loop_fade_remaining = 0;
116 }
117
118 seek(vorbis_stream->loop_offset);
119 loops++;
120 // We still have buffer to fill, start from this element in the next iteration.
121 continue;
122 }
123 }
124
125 if (!have_packets_left && !have_samples_left) {
126 // Actual end of file!
127 bool is_not_empty = mixed > 0 || vorbis_stream->get_length() > 0;
128 if (vorbis_stream->loop && is_not_empty) {
129 //loop
130
131 seek(vorbis_stream->loop_offset);
132 loops++;
133 // We still have buffer to fill, start from this element in the next iteration.
134
135 } else {
136 for (int i = p_frames - todo; i < p_frames; i++) {
137 p_buffer[i] = AudioFrame(0, 0);
138 }
139 active = false;
140 }
141 }
142 }
143 return p_frames - todo;
144}
145
146int AudioStreamPlaybackOggVorbis::_mix_frames_vorbis(AudioFrame *p_buffer, int p_frames) {
147 ERR_FAIL_COND_V(!ready, 0);
148 if (!have_samples_left) {
149 ogg_packet *packet = nullptr;
150 int err;
151
152 if (!vorbis_data_playback->next_ogg_packet(&packet)) {
153 have_packets_left = false;
154 WARN_PRINT("ran out of packets in stream");
155 return -1;
156 }
157
158 err = vorbis_synthesis(&block, packet);
159 ERR_FAIL_COND_V_MSG(err != 0, 0, "Error during vorbis synthesis " + itos(err));
160
161 err = vorbis_synthesis_blockin(&dsp_state, &block);
162 ERR_FAIL_COND_V_MSG(err != 0, 0, "Error during vorbis block processing " + itos(err));
163
164 have_packets_left = !packet->e_o_s;
165 }
166
167 float **pcm; // Accessed with pcm[channel_idx][sample_idx].
168
169 int frames = vorbis_synthesis_pcmout(&dsp_state, &pcm);
170 if (frames > p_frames) {
171 frames = p_frames;
172 have_samples_left = true;
173 } else {
174 have_samples_left = false;
175 }
176
177 if (info.channels > 1) {
178 for (int frame = 0; frame < frames; frame++) {
179 p_buffer[frame].l = pcm[0][frame];
180 p_buffer[frame].r = pcm[1][frame];
181 }
182 } else {
183 for (int frame = 0; frame < frames; frame++) {
184 p_buffer[frame].l = pcm[0][frame];
185 p_buffer[frame].r = pcm[0][frame];
186 }
187 }
188 vorbis_synthesis_read(&dsp_state, frames);
189 return frames;
190}
191
192float AudioStreamPlaybackOggVorbis::get_stream_sampling_rate() {
193 return vorbis_data->get_sampling_rate();
194}
195
196bool AudioStreamPlaybackOggVorbis::_alloc_vorbis() {
197 vorbis_info_init(&info);
198 info_is_allocated = true;
199 vorbis_comment_init(&comment);
200 comment_is_allocated = true;
201
202 ERR_FAIL_COND_V(vorbis_data.is_null(), false);
203 vorbis_data_playback = vorbis_data->instantiate_playback();
204
205 ogg_packet *packet;
206 int err;
207
208 for (int i = 0; i < 3; i++) {
209 if (!vorbis_data_playback->next_ogg_packet(&packet)) {
210 WARN_PRINT("Not enough packets to parse header");
211 return false;
212 }
213
214 err = vorbis_synthesis_headerin(&info, &comment, packet);
215 ERR_FAIL_COND_V_MSG(err != 0, false, "Error parsing header");
216 }
217
218 err = vorbis_synthesis_init(&dsp_state, &info);
219 ERR_FAIL_COND_V_MSG(err != 0, false, "Error initializing dsp state");
220 dsp_state_is_allocated = true;
221
222 err = vorbis_block_init(&dsp_state, &block);
223 ERR_FAIL_COND_V_MSG(err != 0, false, "Error initializing block");
224 block_is_allocated = true;
225
226 ready = true;
227
228 return true;
229}
230
231void AudioStreamPlaybackOggVorbis::start(double p_from_pos) {
232 ERR_FAIL_COND(!ready);
233 loop_fade_remaining = FADE_SIZE;
234 active = true;
235 seek(p_from_pos);
236 loops = 0;
237 begin_resample();
238}
239
240void AudioStreamPlaybackOggVorbis::stop() {
241 active = false;
242}
243
244bool AudioStreamPlaybackOggVorbis::is_playing() const {
245 return active;
246}
247
248int AudioStreamPlaybackOggVorbis::get_loop_count() const {
249 return loops;
250}
251
252double AudioStreamPlaybackOggVorbis::get_playback_position() const {
253 return double(frames_mixed) / (double)vorbis_data->get_sampling_rate();
254}
255
256void AudioStreamPlaybackOggVorbis::tag_used_streams() {
257 vorbis_stream->tag_used(get_playback_position());
258}
259
260void AudioStreamPlaybackOggVorbis::seek(double p_time) {
261 ERR_FAIL_COND(!ready);
262 ERR_FAIL_COND(vorbis_stream.is_null());
263 if (!active) {
264 return;
265 }
266
267 vorbis_synthesis_restart(&dsp_state);
268
269 if (p_time >= vorbis_stream->get_length()) {
270 p_time = 0;
271 }
272 frames_mixed = uint32_t(vorbis_data->get_sampling_rate() * p_time);
273
274 const int64_t desired_sample = p_time * get_stream_sampling_rate();
275
276 if (!vorbis_data_playback->seek_page(desired_sample)) {
277 WARN_PRINT("seek failed");
278 return;
279 }
280
281 ogg_packet *packet;
282 if (!vorbis_data_playback->next_ogg_packet(&packet)) {
283 WARN_PRINT_ONCE("seeking beyond limits");
284 return;
285 }
286
287 // The granule position of the page we're seeking through.
288 int64_t granule_pos = 0;
289
290 int headers_remaining = 0;
291 int samples_in_page = 0;
292 int err;
293 while (true) {
294 if (vorbis_synthesis_idheader(packet)) {
295 headers_remaining = 3;
296 }
297 if (!headers_remaining) {
298 err = vorbis_synthesis(&block, packet);
299 ERR_FAIL_COND_MSG(err != 0, "Error during vorbis synthesis " + itos(err));
300
301 err = vorbis_synthesis_blockin(&dsp_state, &block);
302 ERR_FAIL_COND_MSG(err != 0, "Error during vorbis block processing " + itos(err));
303
304 int samples_out = vorbis_synthesis_pcmout(&dsp_state, nullptr);
305 err = vorbis_synthesis_read(&dsp_state, samples_out);
306 ERR_FAIL_COND_MSG(err != 0, "Error during vorbis read updating " + itos(err));
307
308 samples_in_page += samples_out;
309
310 } else {
311 headers_remaining--;
312 }
313 if (packet->granulepos != -1 && headers_remaining == 0) {
314 // This indicates the end of the page.
315 granule_pos = packet->granulepos;
316 break;
317 }
318 if (packet->e_o_s) {
319 break;
320 }
321 if (!vorbis_data_playback->next_ogg_packet(&packet)) {
322 // We should get an e_o_s flag before this happens.
323 WARN_PRINT("Vorbis file ended without warning.");
324 break;
325 }
326 }
327
328 int64_t samples_to_burn = samples_in_page - (granule_pos - desired_sample);
329
330 if (samples_to_burn > samples_in_page) {
331 WARN_PRINT_ONCE("Burning more samples than we have in this page. Check seek algorithm.");
332 } else if (samples_to_burn < 0) {
333 WARN_PRINT_ONCE("Burning negative samples doesn't make sense. Check seek algorithm.");
334 }
335
336 // Seek again, this time we'll burn a specific number of samples instead of all of them.
337 if (!vorbis_data_playback->seek_page(desired_sample)) {
338 WARN_PRINT("seek failed");
339 return;
340 }
341
342 if (!vorbis_data_playback->next_ogg_packet(&packet)) {
343 WARN_PRINT_ONCE("seeking beyond limits");
344 return;
345 }
346 vorbis_synthesis_restart(&dsp_state);
347
348 while (true) {
349 if (vorbis_synthesis_idheader(packet)) {
350 headers_remaining = 3;
351 }
352 if (!headers_remaining) {
353 err = vorbis_synthesis(&block, packet);
354 ERR_FAIL_COND_MSG(err != 0, "Error during vorbis synthesis " + itos(err));
355
356 err = vorbis_synthesis_blockin(&dsp_state, &block);
357 ERR_FAIL_COND_MSG(err != 0, "Error during vorbis block processing " + itos(err));
358
359 int samples_out = vorbis_synthesis_pcmout(&dsp_state, nullptr);
360 int read_samples = samples_to_burn > samples_out ? samples_out : samples_to_burn;
361 err = vorbis_synthesis_read(&dsp_state, samples_out);
362 ERR_FAIL_COND_MSG(err != 0, "Error during vorbis read updating " + itos(err));
363 samples_to_burn -= read_samples;
364
365 if (samples_to_burn <= 0) {
366 break;
367 }
368 } else {
369 headers_remaining--;
370 }
371 if (packet->granulepos != -1 && headers_remaining == 0) {
372 // This indicates the end of the page.
373 break;
374 }
375 if (packet->e_o_s) {
376 break;
377 }
378 if (!vorbis_data_playback->next_ogg_packet(&packet)) {
379 // We should get an e_o_s flag before this happens.
380 WARN_PRINT("Vorbis file ended without warning.");
381 break;
382 }
383 }
384}
385
386AudioStreamPlaybackOggVorbis::~AudioStreamPlaybackOggVorbis() {
387 if (block_is_allocated) {
388 vorbis_block_clear(&block);
389 }
390 if (dsp_state_is_allocated) {
391 vorbis_dsp_clear(&dsp_state);
392 }
393 if (comment_is_allocated) {
394 vorbis_comment_clear(&comment);
395 }
396 if (info_is_allocated) {
397 vorbis_info_clear(&info);
398 }
399}
400
401Ref<AudioStreamPlayback> AudioStreamOggVorbis::instantiate_playback() {
402 Ref<AudioStreamPlaybackOggVorbis> ovs;
403
404 ERR_FAIL_COND_V(packet_sequence.is_null(), nullptr);
405
406 ovs.instantiate();
407 ovs->vorbis_stream = Ref<AudioStreamOggVorbis>(this);
408 ovs->vorbis_data = packet_sequence;
409 ovs->frames_mixed = 0;
410 ovs->active = false;
411 ovs->loops = 0;
412 if (ovs->_alloc_vorbis()) {
413 return ovs;
414 }
415 // Failed to allocate data structures.
416 return nullptr;
417}
418
419String AudioStreamOggVorbis::get_stream_name() const {
420 return ""; //return stream_name;
421}
422
423void AudioStreamOggVorbis::maybe_update_info() {
424 ERR_FAIL_COND(packet_sequence.is_null());
425
426 vorbis_info info;
427 vorbis_comment comment;
428 int err;
429
430 vorbis_info_init(&info);
431 vorbis_comment_init(&comment);
432
433 Ref<OggPacketSequencePlayback> packet_sequence_playback = packet_sequence->instantiate_playback();
434
435 for (int i = 0; i < 3; i++) {
436 ogg_packet *packet;
437 if (!packet_sequence_playback->next_ogg_packet(&packet)) {
438 WARN_PRINT("Failed to get header packet");
439 break;
440 }
441 if (i == 0) {
442 packet->b_o_s = 1;
443
444 ERR_FAIL_COND(!vorbis_synthesis_idheader(packet));
445 }
446
447 err = vorbis_synthesis_headerin(&info, &comment, packet);
448 ERR_FAIL_COND_MSG(err != 0, "Error parsing header packet " + itos(i) + ": " + itos(err));
449 }
450
451 packet_sequence->set_sampling_rate(info.rate);
452
453 vorbis_comment_clear(&comment);
454 vorbis_info_clear(&info);
455}
456
457void AudioStreamOggVorbis::set_packet_sequence(Ref<OggPacketSequence> p_packet_sequence) {
458 packet_sequence = p_packet_sequence;
459 if (packet_sequence.is_valid()) {
460 maybe_update_info();
461 }
462}
463
464Ref<OggPacketSequence> AudioStreamOggVorbis::get_packet_sequence() const {
465 return packet_sequence;
466}
467
468void AudioStreamOggVorbis::set_loop(bool p_enable) {
469 loop = p_enable;
470}
471
472bool AudioStreamOggVorbis::has_loop() const {
473 return loop;
474}
475
476void AudioStreamOggVorbis::set_loop_offset(double p_seconds) {
477 loop_offset = p_seconds;
478}
479
480double AudioStreamOggVorbis::get_loop_offset() const {
481 return loop_offset;
482}
483
484double AudioStreamOggVorbis::get_length() const {
485 ERR_FAIL_COND_V(packet_sequence.is_null(), 0);
486 return packet_sequence->get_length();
487}
488
489void AudioStreamOggVorbis::set_bpm(double p_bpm) {
490 ERR_FAIL_COND(p_bpm < 0);
491 bpm = p_bpm;
492 emit_changed();
493}
494
495double AudioStreamOggVorbis::get_bpm() const {
496 return bpm;
497}
498
499void AudioStreamOggVorbis::set_beat_count(int p_beat_count) {
500 ERR_FAIL_COND(p_beat_count < 0);
501 beat_count = p_beat_count;
502 emit_changed();
503}
504
505int AudioStreamOggVorbis::get_beat_count() const {
506 return beat_count;
507}
508
509void AudioStreamOggVorbis::set_bar_beats(int p_bar_beats) {
510 ERR_FAIL_COND(p_bar_beats < 2);
511 bar_beats = p_bar_beats;
512 emit_changed();
513}
514
515int AudioStreamOggVorbis::get_bar_beats() const {
516 return bar_beats;
517}
518
519bool AudioStreamOggVorbis::is_monophonic() const {
520 return false;
521}
522
523void AudioStreamOggVorbis::_bind_methods() {
524 ClassDB::bind_static_method("AudioStreamOggVorbis", D_METHOD("load_from_buffer", "buffer"), &AudioStreamOggVorbis::load_from_buffer);
525 ClassDB::bind_static_method("AudioStreamOggVorbis", D_METHOD("load_from_file", "path"), &AudioStreamOggVorbis::load_from_file);
526
527 ClassDB::bind_method(D_METHOD("set_packet_sequence", "packet_sequence"), &AudioStreamOggVorbis::set_packet_sequence);
528 ClassDB::bind_method(D_METHOD("get_packet_sequence"), &AudioStreamOggVorbis::get_packet_sequence);
529
530 ClassDB::bind_method(D_METHOD("set_loop", "enable"), &AudioStreamOggVorbis::set_loop);
531 ClassDB::bind_method(D_METHOD("has_loop"), &AudioStreamOggVorbis::has_loop);
532
533 ClassDB::bind_method(D_METHOD("set_loop_offset", "seconds"), &AudioStreamOggVorbis::set_loop_offset);
534 ClassDB::bind_method(D_METHOD("get_loop_offset"), &AudioStreamOggVorbis::get_loop_offset);
535
536 ClassDB::bind_method(D_METHOD("set_bpm", "bpm"), &AudioStreamOggVorbis::set_bpm);
537 ClassDB::bind_method(D_METHOD("get_bpm"), &AudioStreamOggVorbis::get_bpm);
538
539 ClassDB::bind_method(D_METHOD("set_beat_count", "count"), &AudioStreamOggVorbis::set_beat_count);
540 ClassDB::bind_method(D_METHOD("get_beat_count"), &AudioStreamOggVorbis::get_beat_count);
541
542 ClassDB::bind_method(D_METHOD("set_bar_beats", "count"), &AudioStreamOggVorbis::set_bar_beats);
543 ClassDB::bind_method(D_METHOD("get_bar_beats"), &AudioStreamOggVorbis::get_bar_beats);
544
545 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "packet_sequence", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_packet_sequence", "get_packet_sequence");
546 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bpm", PROPERTY_HINT_RANGE, "0,400,0.01,or_greater"), "set_bpm", "get_bpm");
547 ADD_PROPERTY(PropertyInfo(Variant::INT, "beat_count", PROPERTY_HINT_RANGE, "0,512,1,or_greater"), "set_beat_count", "get_beat_count");
548 ADD_PROPERTY(PropertyInfo(Variant::INT, "bar_beats", PROPERTY_HINT_RANGE, "2,32,1,or_greater"), "set_bar_beats", "get_bar_beats");
549 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop");
550 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "loop_offset"), "set_loop_offset", "get_loop_offset");
551}
552
553AudioStreamOggVorbis::AudioStreamOggVorbis() {}
554
555AudioStreamOggVorbis::~AudioStreamOggVorbis() {}
556
557Ref<AudioStreamOggVorbis> AudioStreamOggVorbis::load_from_buffer(const Vector<uint8_t> &file_data) {
558 return ResourceImporterOggVorbis::load_from_buffer(file_data);
559}
560
561Ref<AudioStreamOggVorbis> AudioStreamOggVorbis::load_from_file(const String &p_path) {
562 return ResourceImporterOggVorbis::load_from_file(p_path);
563}
564