diff --git a/doc/classes/AudioStreamWAV.xml b/doc/classes/AudioStreamWAV.xml index e3685ce949a4..abb7f674f7c5 100644 --- a/doc/classes/AudioStreamWAV.xml +++ b/doc/classes/AudioStreamWAV.xml @@ -11,6 +11,12 @@ $DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html + + + + Creates a placeholder version of this resource ([PlaceholderAudioStream]). + + diff --git a/doc/classes/PlaceholderAudioStream.xml b/doc/classes/PlaceholderAudioStream.xml new file mode 100644 index 000000000000..a802c305adaf --- /dev/null +++ b/doc/classes/PlaceholderAudioStream.xml @@ -0,0 +1,46 @@ + + + + Placeholder class for WAV, Ogg Vorbis or MP3 audio. + + + This class is used when loading a project that uses [AudioStreamWAV], [AudioStreamOggVorbis] or [AudioStreamMP3], if the project was exported in dedicated server mode. In this mode, only the resource reference is kept, with no actual audio data. This allows reducing the exported PCK's size significantly. + [b]Note:[/b] This is not intended to be used as an actual resource for audio playback. + + + + + + The audio stream's length in seconds. + + + The loop start point (in number of samples, relative to the beginning of the stream). + + + The loop end point (in number of samples, relative to the beginning of the stream). + + + The loop mode. + + + Contains user-defined tags if found in the WAV or Ogg Vorbis data. + Commonly used tags include [code]title[/code], [code]artist[/code], [code]album[/code], [code]tracknumber[/code], and [code]date[/code] ([code]date[/code] does not have a standard date format). + [b]Note:[/b] No tag is [i]guaranteed[/i] to be present in every file, so make sure to account for the keys not always existing. + [b]Note:[/b] Only WAV files using a [code]LIST[/code] chunk with an identifier of [code]INFO[/code] to encode the tags are currently supported. + + + + + Audio does not loop. + + + Audio loops the data between [member loop_begin] and [member loop_end], playing forward only. + + + Audio loops the data between [member loop_begin] and [member loop_end], playing back and forth. + + + Audio loops the data between [member loop_begin] and [member loop_end], playing backward only. + + + diff --git a/modules/minimp3/audio_stream_mp3.cpp b/modules/minimp3/audio_stream_mp3.cpp index 6b955f3a118a..92efa8079fbb 100644 --- a/modules/minimp3/audio_stream_mp3.cpp +++ b/modules/minimp3/audio_stream_mp3.cpp @@ -34,6 +34,7 @@ #include "audio_stream_mp3.h" #include "core/io/file_access.h" +#include "scene/resources/placeholder_audio_stream.h" int AudioStreamPlaybackMP3::_mix_internal(AudioFrame *p_buffer, int p_frames) { if (!active) { @@ -327,6 +328,14 @@ Ref AudioStreamMP3::load_from_file(const String &p_path) { return load_from_buffer(stream_data); } +Ref AudioStreamMP3::create_placeholder() const { + Ref placeholder; + placeholder.instantiate(); + placeholder->set_length(get_length()); + placeholder->set_tags(get_tags()); + return placeholder; +} + void AudioStreamMP3::_bind_methods() { ClassDB::bind_static_method("AudioStreamMP3", D_METHOD("load_from_buffer", "stream_data"), &AudioStreamMP3::load_from_buffer); ClassDB::bind_static_method("AudioStreamMP3", D_METHOD("load_from_file", "path"), &AudioStreamMP3::load_from_file); @@ -349,6 +358,8 @@ void AudioStreamMP3::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bar_beats", "count"), &AudioStreamMP3::set_bar_beats); ClassDB::bind_method(D_METHOD("get_bar_beats"), &AudioStreamMP3::get_bar_beats); + ClassDB::bind_method(D_METHOD("create_placeholder"), &AudioStreamMP3::create_placeholder); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_data", "get_data"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bpm", PROPERTY_HINT_RANGE, "0,400,0.01,or_greater"), "set_bpm", "get_bpm"); ADD_PROPERTY(PropertyInfo(Variant::INT, "beat_count", PROPERTY_HINT_RANGE, "0,512,1,or_greater"), "set_beat_count", "get_beat_count"); diff --git a/modules/minimp3/audio_stream_mp3.h b/modules/minimp3/audio_stream_mp3.h index 9612e6a3b038..ee20e8683059 100644 --- a/modules/minimp3/audio_stream_mp3.h +++ b/modules/minimp3/audio_stream_mp3.h @@ -147,6 +147,8 @@ class AudioStreamMP3 : public AudioStream { virtual void get_parameter_list(List *r_parameters) override; + virtual Ref create_placeholder() const; + AudioStreamMP3(); virtual ~AudioStreamMP3(); }; diff --git a/modules/minimp3/doc_classes/AudioStreamMP3.xml b/modules/minimp3/doc_classes/AudioStreamMP3.xml index 6eb665b04891..420a95dd88de 100644 --- a/modules/minimp3/doc_classes/AudioStreamMP3.xml +++ b/modules/minimp3/doc_classes/AudioStreamMP3.xml @@ -10,6 +10,12 @@ + + + + Creates a placeholder version of this resource ([PlaceholderAudioStream]). + + diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp index 62c7e73956a1..efe460f4c056 100644 --- a/modules/vorbis/audio_stream_ogg_vorbis.cpp +++ b/modules/vorbis/audio_stream_ogg_vorbis.cpp @@ -30,6 +30,7 @@ #include "audio_stream_ogg_vorbis.h" #include "core/io/file_access.h" +#include "scene/resources/placeholder_audio_stream.h" #include "core/templates/rb_map.h" @@ -703,6 +704,14 @@ Ref AudioStreamOggVorbis::load_from_file(const String &p_p return load_from_buffer(stream_data); } +Ref AudioStreamOggVorbis::create_placeholder() const { + Ref placeholder; + placeholder.instantiate(); + placeholder->set_length(get_length()); + placeholder->set_tags(get_tags()); + return placeholder; +} + void AudioStreamOggVorbis::_bind_methods() { ClassDB::bind_static_method("AudioStreamOggVorbis", D_METHOD("load_from_buffer", "stream_data"), &AudioStreamOggVorbis::load_from_buffer); ClassDB::bind_static_method("AudioStreamOggVorbis", D_METHOD("load_from_file", "path"), &AudioStreamOggVorbis::load_from_file); @@ -728,6 +737,8 @@ void AudioStreamOggVorbis::_bind_methods() { ClassDB::bind_method(D_METHOD("set_tags", "tags"), &AudioStreamOggVorbis::set_tags); ClassDB::bind_method(D_METHOD("get_tags"), &AudioStreamOggVorbis::get_tags); + ClassDB::bind_method(D_METHOD("create_placeholder"), &AudioStreamOggVorbis::create_placeholder); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "packet_sequence", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_packet_sequence", "get_packet_sequence"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bpm", PROPERTY_HINT_RANGE, "0,400,0.01,or_greater"), "set_bpm", "get_bpm"); ADD_PROPERTY(PropertyInfo(Variant::INT, "beat_count", PROPERTY_HINT_RANGE, "0,512,1,or_greater"), "set_beat_count", "get_beat_count"); diff --git a/modules/vorbis/audio_stream_ogg_vorbis.h b/modules/vorbis/audio_stream_ogg_vorbis.h index e14df33490ae..6e906da83053 100644 --- a/modules/vorbis/audio_stream_ogg_vorbis.h +++ b/modules/vorbis/audio_stream_ogg_vorbis.h @@ -38,6 +38,7 @@ #include class AudioStreamOggVorbis; +class PlaceholderAudioStream; class AudioStreamPlaybackOggVorbis : public AudioStreamPlaybackResampled { GDCLASS(AudioStreamPlaybackOggVorbis, AudioStreamPlaybackResampled); @@ -177,6 +178,8 @@ class AudioStreamOggVorbis : public AudioStream { } virtual Ref generate_sample() const override; + virtual Ref create_placeholder() const; + AudioStreamOggVorbis(); virtual ~AudioStreamOggVorbis(); }; diff --git a/modules/vorbis/doc_classes/AudioStreamOggVorbis.xml b/modules/vorbis/doc_classes/AudioStreamOggVorbis.xml index 5f144baa319b..1d8aea288fca 100644 --- a/modules/vorbis/doc_classes/AudioStreamOggVorbis.xml +++ b/modules/vorbis/doc_classes/AudioStreamOggVorbis.xml @@ -10,6 +10,12 @@ $DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html + + + + Creates a placeholder version of this resource ([PlaceholderAudioStream]). + + diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 57135e034836..ce7c8a60948b 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -138,6 +138,7 @@ #include "scene/resources/dpi_texture.h" #include "scene/resources/packed_scene.h" #include "scene/resources/particle_process_material.h" +#include "scene/resources/placeholder_audio_stream.h" #include "scene/resources/placeholder_textures.h" #include "scene/resources/portable_compressed_texture.h" #include "scene/resources/resource_format_text.h" @@ -1116,6 +1117,7 @@ void register_scene_types() { GDREGISTER_CLASS(AudioStreamPlayer); GDREGISTER_CLASS(AudioStreamWAV); GDREGISTER_CLASS(AudioStreamPolyphonic); + GDREGISTER_CLASS(PlaceholderAudioStream); GDREGISTER_ABSTRACT_CLASS(AudioStreamPlaybackPolyphonic); OS::get_singleton()->yield(); // may take time to init diff --git a/scene/resources/audio_stream_wav.cpp b/scene/resources/audio_stream_wav.cpp index 949ab8236d51..ec608cc9bf71 100644 --- a/scene/resources/audio_stream_wav.cpp +++ b/scene/resources/audio_stream_wav.cpp @@ -32,6 +32,7 @@ #include "core/io/file_access_memory.h" #include "core/io/marshalls.h" +#include "scene/resources/placeholder_audio_stream.h" const float TRIM_DB_LIMIT = -50; const int TRIM_FADE_OUT_FRAMES = 500; @@ -1204,6 +1205,14 @@ Ref AudioStreamWAV::load_from_file(const String &p_path, const D return load_from_buffer(stream_data, p_options); } +Ref AudioStreamWAV::create_placeholder() const { + Ref placeholder; + placeholder.instantiate(); + placeholder->set_length(get_length()); + placeholder->set_tags(get_tags()); + return placeholder; +} + void AudioStreamWAV::_bind_methods() { ClassDB::bind_static_method("AudioStreamWAV", D_METHOD("load_from_buffer", "stream_data", "options"), &AudioStreamWAV::load_from_buffer, DEFVAL(Dictionary())); ClassDB::bind_static_method("AudioStreamWAV", D_METHOD("load_from_file", "path", "options"), &AudioStreamWAV::load_from_file, DEFVAL(Dictionary())); @@ -1234,6 +1243,8 @@ void AudioStreamWAV::_bind_methods() { ClassDB::bind_method(D_METHOD("save_to_wav", "path"), &AudioStreamWAV::save_to_wav); + ClassDB::bind_method(D_METHOD("create_placeholder"), &AudioStreamWAV::create_placeholder); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_data", "get_data"); ADD_PROPERTY(PropertyInfo(Variant::INT, "format", PROPERTY_HINT_ENUM, "8-Bit,16-Bit,IMA ADPCM,Quite OK Audio"), "set_format", "get_format"); ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_mode", PROPERTY_HINT_ENUM, "Disabled,Forward,Ping-Pong,Backward"), "set_loop_mode", "get_loop_mode"); diff --git a/scene/resources/audio_stream_wav.h b/scene/resources/audio_stream_wav.h index fd67d7b14b6e..ec26b248778b 100644 --- a/scene/resources/audio_stream_wav.h +++ b/scene/resources/audio_stream_wav.h @@ -171,6 +171,8 @@ class AudioStreamWAV : public AudioStream { } virtual Ref generate_sample() const override; + virtual Ref create_placeholder() const; + static void _compress_ima_adpcm(const Vector &p_data, Vector &r_dst_data) { static const int16_t _ima_adpcm_step_table[89] = { 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, diff --git a/scene/resources/placeholder_audio_stream.cpp b/scene/resources/placeholder_audio_stream.cpp new file mode 100644 index 000000000000..b4aeea869600 --- /dev/null +++ b/scene/resources/placeholder_audio_stream.cpp @@ -0,0 +1,298 @@ +/**************************************************************************/ +/* placeholder_audio_stream.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "placeholder_audio_stream.h" + +void PlaceholderAudioStreamPlayback::start(double p_from_pos) { + seek(p_from_pos); + sign = 1; + active = true; +} + +void PlaceholderAudioStreamPlayback::stop() { + active = false; +} + +bool PlaceholderAudioStreamPlayback::is_playing() const { + return active; +} + +int PlaceholderAudioStreamPlayback::get_loop_count() const { + return 0; +} + +double PlaceholderAudioStreamPlayback::get_playback_position() const { + return double(offset); +} + +void PlaceholderAudioStreamPlayback::seek(double p_time) { + double max = base->get_length(); + if (p_time < 0) { + p_time = 0; + } else if (p_time >= max) { + p_time = max - 0.001; + } + + offset = int64_t(p_time); +} + +void PlaceholderAudioStreamPlayback::tag_used_streams() { + base->tag_used(get_playback_position()); +} + +void PlaceholderAudioStreamPlayback::set_is_sample(bool p_is_sample) { + _is_sample = p_is_sample; +} + +bool PlaceholderAudioStreamPlayback::get_is_sample() const { + return _is_sample; +} + +Ref PlaceholderAudioStreamPlayback::get_sample_playback() const { + return sample_playback; +} + +void PlaceholderAudioStreamPlayback::set_sample_playback(const Ref &p_playback) { + sample_playback = p_playback; + if (sample_playback.is_valid()) { + sample_playback->stream_playback = Ref(this); + } +} + +int PlaceholderAudioStreamPlayback::_mix_internal(AudioFrame *p_buffer, int p_frames) { + if (!active) { + for (int i = 0; i < p_frames; i++) { + p_buffer[i] = AudioFrame(0, 0); + } + return 0; + } + + uint64_t len = base->get_length() * 1000; // TODO: Calculate length correctly. + + int64_t loop_begin = base->loop_begin; + int64_t loop_end = base->loop_end; + int64_t begin_limit = (base->loop_mode != PlaceholderAudioStream::LOOP_DISABLED) ? loop_begin : 0; + int64_t end_limit = (base->loop_mode != PlaceholderAudioStream::LOOP_DISABLED) ? loop_end : len - 1; + + int32_t todo = p_frames; + + if (base->loop_mode == PlaceholderAudioStream::LOOP_BACKWARD) { + sign = -1; + } + + int8_t increment = sign; + + //looping + + PlaceholderAudioStream::LoopMode loop_format = base->loop_mode; + + /* audio data */ + + AudioFrame *dst_buff = p_buffer; + + while (todo > 0) { + int64_t limit = 0; + int32_t target = 0, aux = 0; + + /** LOOP CHECKING **/ + + if (increment < 0) { + /* going backwards */ + + if (loop_format != PlaceholderAudioStream::LOOP_DISABLED && offset < loop_begin) { + /* loopstart reached */ + if (loop_format == PlaceholderAudioStream::LOOP_PINGPONG) { + /* bounce ping pong */ + offset = loop_begin + (loop_begin - offset); + increment = -increment; + sign *= -1; + } else { + /* go to loop-end */ + offset = loop_end - (loop_begin - offset); + } + } else { + /* check for sample not reaching beginning */ + if (offset < 0) { + active = false; + break; + } + } + } else { + /* going forward */ + if (loop_format != PlaceholderAudioStream::LOOP_DISABLED && offset >= loop_end) { + /* loopend reached */ + + if (loop_format == PlaceholderAudioStream::LOOP_PINGPONG) { + /* bounce ping pong */ + offset = loop_end - (offset - loop_end); + increment = -increment; + sign *= -1; + } else { + /* go to loop-begin */ + + offset = loop_begin + (offset - loop_end); + } + } else { + /* no loop, check for end of sample */ + if ((uint64_t)offset >= len) { + active = false; + break; + } + } + } + + /** MIXCOUNT COMPUTING **/ + + /* next possible limit (looppoints or sample begin/end */ + limit = (increment < 0) ? begin_limit : end_limit; + + /* compute what is shorter, the todo or the limit? */ + aux = (limit - offset) / increment + 1; + target = (aux < todo) ? aux : todo; /* mix target is the shorter buffer */ + + /* check just in case */ + if (target <= 0) { + active = false; + break; + } + + todo -= target; + + dst_buff += target; + } + + if (todo) { + int mixed_frames = p_frames - todo; + //bit was missing from mix + int todo_ofs = p_frames - todo; + for (int i = todo_ofs; i < p_frames; i++) { + p_buffer[i] = AudioFrame(0, 0); + } + return mixed_frames; + } + return p_frames; +} + +int PlaceholderAudioStreamPlayback::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) { + constexpr int INTERNAL_BUFFER_LEN = 128; + int mixed_frames_total = -1; + + int i; + for (i = 0; i < p_frames; i++) { + AudioFrame af; + _mix_internal(&af, INTERNAL_BUFFER_LEN); + } + if (mixed_frames_total == -1 && i == p_frames) { + mixed_frames_total = p_frames; + } + return mixed_frames_total; +} + +/////////// + +void PlaceholderAudioStream::set_length(double p_length) { + length = p_length; +} + +double PlaceholderAudioStream::get_length() const { + return length; +} + +void PlaceholderAudioStream::set_loop_mode(const LoopMode p_loop_mode) { + loop_mode = p_loop_mode; +} + +PlaceholderAudioStream::LoopMode PlaceholderAudioStream::get_loop_mode() const { + return loop_mode; +} + +void PlaceholderAudioStream::set_loop_begin(int p_frame) { + loop_begin = p_frame; +} + +int PlaceholderAudioStream::get_loop_begin() const { + return loop_begin; +} + +void PlaceholderAudioStream::set_loop_end(int p_frame) { + loop_end = p_frame; +} + +int PlaceholderAudioStream::get_loop_end() const { + return loop_end; +} + +void PlaceholderAudioStream::set_tags(const Dictionary &p_tags) { + tags = p_tags; +} + +Dictionary PlaceholderAudioStream::get_tags() const { + return tags; +} + +Ref PlaceholderAudioStream::instantiate_playback() { + Ref sample; + sample.instantiate(); + sample->base = Ref(this); + return sample; +} + +void PlaceholderAudioStream::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_length"), &PlaceholderAudioStream::set_length); + + ClassDB::bind_method(D_METHOD("set_loop_mode"), &PlaceholderAudioStream::set_loop_mode); + ClassDB::bind_method(D_METHOD("get_loop_mode"), &PlaceholderAudioStream::get_loop_mode); + + ClassDB::bind_method(D_METHOD("set_loop_begin", "loop_begin"), &PlaceholderAudioStream::set_loop_begin); + ClassDB::bind_method(D_METHOD("get_loop_begin"), &PlaceholderAudioStream::get_loop_begin); + + ClassDB::bind_method(D_METHOD("set_loop_end", "loop_end"), &PlaceholderAudioStream::set_loop_end); + ClassDB::bind_method(D_METHOD("get_loop_end"), &PlaceholderAudioStream::get_loop_end); + + ClassDB::bind_method(D_METHOD("set_tags"), &PlaceholderAudioStream::set_tags); + ClassDB::bind_method(D_METHOD("get_tags"), &PlaceholderAudioStream::get_tags); + + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_RANGE, "0.0,100.0,0.001,or_greater,suffix:s"), "set_length", "get_length"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_mode", PROPERTY_HINT_ENUM, "Disabled,Forward,Ping-Pong,Backward"), "set_loop_mode", "get_loop_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_begin"), "set_loop_begin", "get_loop_begin"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_end"), "set_loop_end", "get_loop_end"); + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "tags", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_tags", "get_tags"); + + BIND_ENUM_CONSTANT(LOOP_DISABLED); + BIND_ENUM_CONSTANT(LOOP_FORWARD); + BIND_ENUM_CONSTANT(LOOP_PINGPONG); + BIND_ENUM_CONSTANT(LOOP_BACKWARD); +} + +PlaceholderAudioStream::PlaceholderAudioStream() { +} + +PlaceholderAudioStream::~PlaceholderAudioStream() { +} diff --git a/scene/resources/placeholder_audio_stream.h b/scene/resources/placeholder_audio_stream.h new file mode 100644 index 000000000000..6c3a0a3c4196 --- /dev/null +++ b/scene/resources/placeholder_audio_stream.h @@ -0,0 +1,119 @@ +/**************************************************************************/ +/* placeholder_audio_stream.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#pragma once + +#include "servers/audio/audio_stream.h" + +class PlaceholderAudioStream; + +class PlaceholderAudioStreamPlayback : public AudioStreamPlayback { + GDCLASS(PlaceholderAudioStreamPlayback, AudioStreamPlayback); + + int64_t offset = 0; + int8_t sign = 1; + bool active = false; + Ref base; + + friend class PlaceholderAudioStream; + + bool _is_sample = false; + Ref sample_playback; + + virtual int _mix_internal(AudioFrame *p_buffer, int p_frames); + +public: + virtual void start(double p_from_pos = 0.0) override; + virtual void stop() override; + virtual bool is_playing() const override; + + virtual int get_loop_count() const override; + + virtual double get_playback_position() const override; + virtual void seek(double p_time) override; + + virtual void tag_used_streams() override; + + virtual void set_is_sample(bool p_is_sample) override; + virtual bool get_is_sample() const override; + virtual Ref get_sample_playback() const override; + virtual void set_sample_playback(const Ref &p_playback) override; + + virtual int mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) override; +}; + +/////////// + +class PlaceholderAudioStream : public AudioStream { + GDCLASS(PlaceholderAudioStream, AudioStream) + +public: + enum LoopMode { + LOOP_DISABLED, + LOOP_FORWARD, + LOOP_PINGPONG, + LOOP_BACKWARD + }; + +private: + double length = 0.0; + LoopMode loop_mode = LOOP_DISABLED; + int64_t loop_begin = 0; + int64_t loop_end = 0; + Dictionary tags; + + friend class PlaceholderAudioStreamPlayback; + +protected: + static void _bind_methods(); + +public: + void set_length(double p_length); + double get_length() const override; + + void set_loop_mode(LoopMode p_loop_mode); + LoopMode get_loop_mode() const; + + void set_loop_begin(int p_frame); + int get_loop_begin() const; + + void set_loop_end(int p_frame); + int get_loop_end() const; + + void set_tags(const Dictionary &p_tags); + Dictionary get_tags() const override; + + virtual Ref instantiate_playback() override; + + PlaceholderAudioStream(); + ~PlaceholderAudioStream(); +}; + +VARIANT_ENUM_CAST(PlaceholderAudioStream::LoopMode);