summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJuan Linietsky <reduzio@gmail.com>2023-12-23 17:30:32 +0100
committerJuan Linietsky <reduzio@gmail.com>2024-01-16 10:31:52 +0100
commita40fe168667cbb6f9542d421b5c8e4c0a83ad1e4 (patch)
treecede30e382732ac0716518c867c5e108f0f41734
parent13a0d6e9b253654f5cc2a44f3d0b3cae10440443 (diff)
downloadredot-engine-a40fe168667cbb6f9542d421b5c8e4c0a83ad1e4.tar.gz
Implement audio stream playback parameters.
Implements a way for audio stream playback to be configured via parameters directly in the edited AudioStreamPlayer[2D/3D]. Currently, configuring the playback stream is not possible (or is sometimes hacky as the user has to obtain the currently played stream, which is not always immediately available). This PR only implements this new feature to control looping in stream playback instances (a commonly requested feature, which was lost in the transition from Godot 2 to Godot 3). But the idea is that it can do a lot more: * If effects are bundled to the stream, control per playback instance parameters such as cutoff or resoance, or any other exposed effect parameter per playback instance. * For the upcoming interactive music PR (#64488), this exposes an easy way to change the active clip, which was not possible before. * For the upcoming parametrizable audio support (https://github.com/godotengine/godot-proposals/issues/3394) this allows editing and animating audio graph parameters. In any case, this PR is required to complete #64488. Update modules/vorbis/audio_stream_ogg_vorbis.h Co-authored-by: A Thousand Ships <96648715+AThousandShips@users.noreply.github.com> Update modules/minimp3/audio_stream_mp3.h Co-authored-by: A Thousand Ships <96648715+AThousandShips@users.noreply.github.com> Update modules/minimp3/audio_stream_mp3.h Co-authored-by: A Thousand Ships <96648715+AThousandShips@users.noreply.github.com> Update modules/vorbis/audio_stream_ogg_vorbis.h Co-authored-by: A Thousand Ships <96648715+AThousandShips@users.noreply.github.com> Update doc/classes/AudioStream.xml Co-authored-by: A Thousand Ships <96648715+AThousandShips@users.noreply.github.com>
-rw-r--r--doc/classes/AudioStream.xml13
-rw-r--r--doc/classes/AudioStreamPlayback.xml15
-rw-r--r--modules/minimp3/audio_stream_mp3.cpp29
-rw-r--r--modules/minimp3/audio_stream_mp3.h7
-rw-r--r--modules/vorbis/audio_stream_ogg_vorbis.cpp32
-rw-r--r--modules/vorbis/audio_stream_ogg_vorbis.h7
-rw-r--r--scene/2d/audio_stream_player_2d.cpp69
-rw-r--r--scene/2d/audio_stream_player_2d.h12
-rw-r--r--scene/3d/audio_stream_player_3d.cpp68
-rw-r--r--scene/3d/audio_stream_player_3d.h12
-rw-r--r--scene/audio/audio_stream_player.cpp68
-rw-r--r--scene/audio/audio_stream_player.h12
-rw-r--r--servers/audio/audio_stream.cpp25
-rw-r--r--servers/audio/audio_stream.h19
14 files changed, 382 insertions, 6 deletions
diff --git a/doc/classes/AudioStream.xml b/doc/classes/AudioStream.xml
index 12e09b235f..6e30775fee 100644
--- a/doc/classes/AudioStream.xml
+++ b/doc/classes/AudioStream.xml
@@ -28,6 +28,12 @@
<description>
</description>
</method>
+ <method name="_get_parameter_list" qualifiers="virtual const">
+ <return type="Dictionary[]" />
+ <description>
+ Return the controllable parameters of this stream. This array contains dictionaries with a property info description format (see [method Object.get_property_list]). Additionally, the default value for this parameter must be added tho each dictionary in "default_value" field.
+ </description>
+ </method>
<method name="_get_stream_name" qualifiers="virtual const">
<return type="String" />
<description>
@@ -62,4 +68,11 @@
</description>
</method>
</methods>
+ <signals>
+ <signal name="parameter_list_changed">
+ <description>
+ Signal to be emitted to notify when the parameter list changed.
+ </description>
+ </signal>
+ </signals>
</class>
diff --git a/doc/classes/AudioStreamPlayback.xml b/doc/classes/AudioStreamPlayback.xml
index 7692690b5e..a090989194 100644
--- a/doc/classes/AudioStreamPlayback.xml
+++ b/doc/classes/AudioStreamPlayback.xml
@@ -15,6 +15,13 @@
<description>
</description>
</method>
+ <method name="_get_parameter" qualifiers="virtual const">
+ <return type="Variant" />
+ <param index="0" name="name" type="StringName" />
+ <description>
+ Return the current value of a playback parameter by name (see [method AudioStream._get_parameter_list]).
+ </description>
+ </method>
<method name="_get_playback_position" qualifiers="virtual const">
<return type="float" />
<description>
@@ -39,6 +46,14 @@
<description>
</description>
</method>
+ <method name="_set_parameter" qualifiers="virtual">
+ <return type="void" />
+ <param index="0" name="name" type="StringName" />
+ <param index="1" name="value" type="Variant" />
+ <description>
+ Set the current value of a playback parameter by name (see [method AudioStream._get_parameter_list]).
+ </description>
+ </method>
<method name="_start" qualifiers="virtual">
<return type="void" />
<param index="0" name="from_pos" type="float" />
diff --git a/modules/minimp3/audio_stream_mp3.cpp b/modules/minimp3/audio_stream_mp3.cpp
index 4efa4d329e..a46a1c93b5 100644
--- a/modules/minimp3/audio_stream_mp3.cpp
+++ b/modules/minimp3/audio_stream_mp3.cpp
@@ -46,7 +46,9 @@ int AudioStreamPlaybackMP3::_mix_internal(AudioFrame *p_buffer, int p_frames) {
int frames_mixed_this_step = p_frames;
int beat_length_frames = -1;
- bool beat_loop = mp3_stream->has_loop() && mp3_stream->get_bpm() > 0 && mp3_stream->get_beat_count() > 0;
+ bool use_loop = looping_override ? looping : mp3_stream->loop;
+
+ bool beat_loop = use_loop && mp3_stream->get_bpm() > 0 && mp3_stream->get_beat_count() > 0;
if (beat_loop) {
beat_length_frames = mp3_stream->get_beat_count() * mp3_stream->sample_rate * 60 / mp3_stream->get_bpm();
}
@@ -82,7 +84,7 @@ int AudioStreamPlaybackMP3::_mix_internal(AudioFrame *p_buffer, int p_frames) {
else {
//EOF
- if (mp3_stream->loop) {
+ if (use_loop) {
seek(mp3_stream->loop_offset);
loops++;
} else {
@@ -143,6 +145,25 @@ void AudioStreamPlaybackMP3::tag_used_streams() {
mp3_stream->tag_used(get_playback_position());
}
+void AudioStreamPlaybackMP3::set_parameter(const StringName &p_name, const Variant &p_value) {
+ if (p_name == SNAME("looping")) {
+ if (p_value == Variant()) {
+ looping_override = false;
+ looping = false;
+ } else {
+ looping_override = true;
+ looping = p_value;
+ }
+ }
+}
+
+Variant AudioStreamPlaybackMP3::get_parameter(const StringName &p_name) const {
+ if (looping_override && p_name == SNAME("looping")) {
+ return looping;
+ }
+ return Variant();
+}
+
AudioStreamPlaybackMP3::~AudioStreamPlaybackMP3() {
if (mp3d) {
mp3dec_ex_close(mp3d);
@@ -232,6 +253,10 @@ bool AudioStreamMP3::is_monophonic() const {
return false;
}
+void AudioStreamMP3::get_parameter_list(List<Parameter> *r_parameters) {
+ r_parameters->push_back(Parameter(PropertyInfo(Variant::BOOL, "looping", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_CHECKABLE), Variant()));
+}
+
void AudioStreamMP3::set_bpm(double p_bpm) {
ERR_FAIL_COND(p_bpm < 0);
bpm = p_bpm;
diff --git a/modules/minimp3/audio_stream_mp3.h b/modules/minimp3/audio_stream_mp3.h
index 30760703e3..7d85e0a321 100644
--- a/modules/minimp3/audio_stream_mp3.h
+++ b/modules/minimp3/audio_stream_mp3.h
@@ -47,6 +47,8 @@ class AudioStreamPlaybackMP3 : public AudioStreamPlaybackResampled {
AudioFrame loop_fade[FADE_SIZE];
int loop_fade_remaining = FADE_SIZE;
+ bool looping_override = false;
+ bool looping = false;
mp3dec_ex_t *mp3d = nullptr;
uint32_t frames_mixed = 0;
bool active = false;
@@ -72,6 +74,9 @@ public:
virtual void tag_used_streams() override;
+ virtual void set_parameter(const StringName &p_name, const Variant &p_value) override;
+ virtual Variant get_parameter(const StringName &p_name) const override;
+
AudioStreamPlaybackMP3() {}
~AudioStreamPlaybackMP3();
};
@@ -126,6 +131,8 @@ public:
virtual bool is_monophonic() const override;
+ virtual void get_parameter_list(List<Parameter> *r_parameters) override;
+
AudioStreamMP3();
virtual ~AudioStreamMP3();
};
diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp
index 7ec0b697bf..e6003f35df 100644
--- a/modules/vorbis/audio_stream_ogg_vorbis.cpp
+++ b/modules/vorbis/audio_stream_ogg_vorbis.cpp
@@ -46,8 +46,9 @@ int AudioStreamPlaybackOggVorbis::_mix_internal(AudioFrame *p_buffer, int p_fram
int todo = p_frames;
int beat_length_frames = -1;
- bool beat_loop = vorbis_stream->has_loop();
- if (beat_loop && vorbis_stream->get_bpm() > 0 && vorbis_stream->get_beat_count() > 0) {
+ bool use_loop = looping_override ? looping : vorbis_stream->loop;
+
+ if (use_loop && vorbis_stream->get_bpm() > 0 && vorbis_stream->get_beat_count() > 0) {
beat_length_frames = vorbis_stream->get_beat_count() * vorbis_data->get_sampling_rate() * 60 / vorbis_stream->get_bpm();
}
@@ -99,7 +100,7 @@ int AudioStreamPlaybackOggVorbis::_mix_internal(AudioFrame *p_buffer, int p_fram
} else
**/
- if (beat_loop && beat_length_frames <= (int)frames_mixed) {
+ if (use_loop && beat_length_frames <= (int)frames_mixed) {
// End of file when doing beat-based looping. <= used instead of == because importer editing
if (!have_packets_left && !have_samples_left) {
//Nothing remaining, so do nothing.
@@ -125,7 +126,7 @@ int AudioStreamPlaybackOggVorbis::_mix_internal(AudioFrame *p_buffer, int p_fram
if (!have_packets_left && !have_samples_left) {
// Actual end of file!
bool is_not_empty = mixed > 0 || vorbis_stream->get_length() > 0;
- if (vorbis_stream->loop && is_not_empty) {
+ if (use_loop && is_not_empty) {
//loop
seek(vorbis_stream->loop_offset);
@@ -257,6 +258,25 @@ void AudioStreamPlaybackOggVorbis::tag_used_streams() {
vorbis_stream->tag_used(get_playback_position());
}
+void AudioStreamPlaybackOggVorbis::set_parameter(const StringName &p_name, const Variant &p_value) {
+ if (p_name == SNAME("looping")) {
+ if (p_value == Variant()) {
+ looping_override = false;
+ looping = false;
+ } else {
+ looping_override = true;
+ looping = p_value;
+ }
+ }
+}
+
+Variant AudioStreamPlaybackOggVorbis::get_parameter(const StringName &p_name) const {
+ if (looping_override && p_name == SNAME("looping")) {
+ return looping;
+ }
+ return Variant();
+}
+
void AudioStreamPlaybackOggVorbis::seek(double p_time) {
ERR_FAIL_COND(!ready);
ERR_FAIL_COND(vorbis_stream.is_null());
@@ -493,6 +513,10 @@ bool AudioStreamOggVorbis::is_monophonic() const {
return false;
}
+void AudioStreamOggVorbis::get_parameter_list(List<Parameter> *r_parameters) {
+ r_parameters->push_back(Parameter(PropertyInfo(Variant::BOOL, "looping", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_CHECKABLE), Variant()));
+}
+
void AudioStreamOggVorbis::_bind_methods() {
ClassDB::bind_static_method("AudioStreamOggVorbis", D_METHOD("load_from_buffer", "buffer"), &AudioStreamOggVorbis::load_from_buffer);
ClassDB::bind_static_method("AudioStreamOggVorbis", D_METHOD("load_from_file", "path"), &AudioStreamOggVorbis::load_from_file);
diff --git a/modules/vorbis/audio_stream_ogg_vorbis.h b/modules/vorbis/audio_stream_ogg_vorbis.h
index 6abaeaaebd..64a7815b57 100644
--- a/modules/vorbis/audio_stream_ogg_vorbis.h
+++ b/modules/vorbis/audio_stream_ogg_vorbis.h
@@ -44,6 +44,8 @@ class AudioStreamPlaybackOggVorbis : public AudioStreamPlaybackResampled {
uint32_t frames_mixed = 0;
bool active = false;
+ bool looping_override = false;
+ bool looping = false;
int loops = 0;
enum {
@@ -95,6 +97,9 @@ public:
virtual void tag_used_streams() override;
+ virtual void set_parameter(const StringName &p_name, const Variant &p_value) override;
+ virtual Variant get_parameter(const StringName &p_name) const override;
+
AudioStreamPlaybackOggVorbis() {}
~AudioStreamPlaybackOggVorbis();
};
@@ -152,6 +157,8 @@ public:
virtual bool is_monophonic() const override;
+ virtual void get_parameter_list(List<Parameter> *r_parameters) override;
+
AudioStreamOggVorbis();
virtual ~AudioStreamOggVorbis();
};
diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp
index afc5748ac5..a99964c0e0 100644
--- a/scene/2d/audio_stream_player_2d.cpp
+++ b/scene/2d/audio_stream_player_2d.cpp
@@ -36,6 +36,8 @@
#include "scene/main/window.h"
#include "scene/resources/world_2d.h"
+#define PARAM_PREFIX "parameters/"
+
void AudioStreamPlayer2D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
@@ -222,9 +224,36 @@ void AudioStreamPlayer2D::_update_panning() {
last_mix_count = AudioServer::get_singleton()->get_mix_count();
}
+void AudioStreamPlayer2D::_update_stream_parameters() {
+ if (stream.is_null()) {
+ return;
+ }
+
+ List<AudioStream::Parameter> parameters;
+ stream->get_parameter_list(&parameters);
+ for (const AudioStream::Parameter &K : parameters) {
+ const PropertyInfo &pi = K.property;
+ StringName key = PARAM_PREFIX + pi.name;
+ if (!playback_parameters.has(key)) {
+ ParameterData pd;
+ pd.path = pi.name;
+ pd.value = K.default_value;
+ playback_parameters.insert(key, pd);
+ }
+ }
+}
+
void AudioStreamPlayer2D::set_stream(Ref<AudioStream> p_stream) {
+ if (stream.is_valid()) {
+ stream->disconnect(SNAME("parameter_list_changed"), callable_mp(this, &AudioStreamPlayer2D::_update_stream_parameters));
+ }
stop();
stream = p_stream;
+ _update_stream_parameters();
+ if (stream.is_valid()) {
+ stream->connect(SNAME("parameter_list_changed"), callable_mp(this, &AudioStreamPlayer2D::_update_stream_parameters));
+ }
+ notify_property_list_changed();
}
Ref<AudioStream> AudioStreamPlayer2D::get_stream() const {
@@ -262,6 +291,10 @@ void AudioStreamPlayer2D::play(float p_from_pos) {
Ref<AudioStreamPlayback> stream_playback = stream->instantiate_playback();
ERR_FAIL_COND_MSG(stream_playback.is_null(), "Failed to instantiate playback.");
+ for (const KeyValue<StringName, ParameterData> &K : playback_parameters) {
+ stream_playback->set_parameter(K.value.path, K.value.value);
+ }
+
stream_playbacks.push_back(stream_playback);
setplayback = stream_playback;
setplay.set(p_from_pos);
@@ -430,6 +463,42 @@ void AudioStreamPlayer2D::_on_bus_renamed(int p_bus_index, const StringName &p_o
notify_property_list_changed();
}
+bool AudioStreamPlayer2D::_set(const StringName &p_name, const Variant &p_value) {
+ HashMap<StringName, ParameterData>::Iterator I = playback_parameters.find(p_name);
+ if (!I) {
+ return false;
+ }
+ ParameterData &pd = I->value;
+ pd.value = p_value;
+ for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ playback->set_parameter(pd.path, pd.value);
+ }
+ return true;
+}
+
+bool AudioStreamPlayer2D::_get(const StringName &p_name, Variant &r_ret) const {
+ HashMap<StringName, ParameterData>::ConstIterator I = playback_parameters.find(p_name);
+ if (!I) {
+ return false;
+ }
+
+ r_ret = I->value.value;
+ return true;
+}
+
+void AudioStreamPlayer2D::_get_property_list(List<PropertyInfo> *p_list) const {
+ if (stream.is_null()) {
+ return;
+ }
+ List<AudioStream::Parameter> parameters;
+ stream->get_parameter_list(&parameters);
+ for (const AudioStream::Parameter &K : parameters) {
+ PropertyInfo pi = K.property;
+ pi.name = PARAM_PREFIX + pi.name;
+ p_list->push_back(pi);
+ }
+}
+
void AudioStreamPlayer2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_stream", "stream"), &AudioStreamPlayer2D::set_stream);
ClassDB::bind_method(D_METHOD("get_stream"), &AudioStreamPlayer2D::get_stream);
diff --git a/scene/2d/audio_stream_player_2d.h b/scene/2d/audio_stream_player_2d.h
index 0766f2f77d..267d6a625b 100644
--- a/scene/2d/audio_stream_player_2d.h
+++ b/scene/2d/audio_stream_player_2d.h
@@ -89,11 +89,23 @@ private:
float panning_strength = 1.0f;
float cached_global_panning_strength = 0.5f;
+ struct ParameterData {
+ StringName path;
+ Variant value;
+ };
+
+ HashMap<StringName, ParameterData> playback_parameters;
+ void _update_stream_parameters();
+
protected:
void _validate_property(PropertyInfo &p_property) const;
void _notification(int p_what);
static void _bind_methods();
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
public:
void set_stream(Ref<AudioStream> p_stream);
Ref<AudioStream> get_stream() const;
diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp
index 3971e615a1..bfdbd14cc9 100644
--- a/scene/3d/audio_stream_player_3d.cpp
+++ b/scene/3d/audio_stream_player_3d.cpp
@@ -37,6 +37,8 @@
#include "scene/main/viewport.h"
#include "scene/scene_string_names.h"
+#define PARAM_PREFIX "parameters/"
+
// Based on "A Novel Multichannel Panning Method for Standard and Arbitrary Loudspeaker Configurations" by Ramy Sadek and Chris Kyriakakis (2004)
// Speaker-Placement Correction Amplitude Panning (SPCAP)
class Spcap {
@@ -525,9 +527,35 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() {
return output_volume_vector;
}
+void AudioStreamPlayer3D::_update_stream_parameters() {
+ if (stream.is_null()) {
+ return;
+ }
+ List<AudioStream::Parameter> parameters;
+ stream->get_parameter_list(&parameters);
+ for (const AudioStream::Parameter &K : parameters) {
+ const PropertyInfo &pi = K.property;
+ StringName key = PARAM_PREFIX + pi.name;
+ if (!playback_parameters.has(key)) {
+ ParameterData pd;
+ pd.path = pi.name;
+ pd.value = K.default_value;
+ playback_parameters.insert(key, pd);
+ }
+ }
+}
+
void AudioStreamPlayer3D::set_stream(Ref<AudioStream> p_stream) {
+ if (stream.is_valid()) {
+ stream->disconnect(SNAME("parameter_list_changed"), callable_mp(this, &AudioStreamPlayer3D::_update_stream_parameters));
+ }
stop();
stream = p_stream;
+ _update_stream_parameters();
+ if (stream.is_valid()) {
+ stream->connect(SNAME("parameter_list_changed"), callable_mp(this, &AudioStreamPlayer3D::_update_stream_parameters));
+ }
+ notify_property_list_changed();
}
Ref<AudioStream> AudioStreamPlayer3D::get_stream() const {
@@ -579,6 +607,10 @@ void AudioStreamPlayer3D::play(float p_from_pos) {
Ref<AudioStreamPlayback> stream_playback = stream->instantiate_playback();
ERR_FAIL_COND_MSG(stream_playback.is_null(), "Failed to instantiate playback.");
+ for (const KeyValue<StringName, ParameterData> &K : playback_parameters) {
+ stream_playback->set_parameter(K.value.path, K.value.value);
+ }
+
stream_playbacks.push_back(stream_playback);
setplayback = stream_playback;
setplay.set(p_from_pos);
@@ -818,6 +850,42 @@ void AudioStreamPlayer3D::_on_bus_renamed(int p_bus_index, const StringName &p_o
notify_property_list_changed();
}
+bool AudioStreamPlayer3D::_set(const StringName &p_name, const Variant &p_value) {
+ HashMap<StringName, ParameterData>::Iterator I = playback_parameters.find(p_name);
+ if (!I) {
+ return false;
+ }
+ ParameterData &pd = I->value;
+ pd.value = p_value;
+ for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ playback->set_parameter(pd.path, pd.value);
+ }
+ return true;
+}
+
+bool AudioStreamPlayer3D::_get(const StringName &p_name, Variant &r_ret) const {
+ HashMap<StringName, ParameterData>::ConstIterator I = playback_parameters.find(p_name);
+ if (!I) {
+ return false;
+ }
+
+ r_ret = I->value.value;
+ return true;
+}
+
+void AudioStreamPlayer3D::_get_property_list(List<PropertyInfo> *p_list) const {
+ if (stream.is_null()) {
+ return;
+ }
+ List<AudioStream::Parameter> parameters;
+ stream->get_parameter_list(&parameters);
+ for (const AudioStream::Parameter &K : parameters) {
+ PropertyInfo pi = K.property;
+ pi.name = PARAM_PREFIX + pi.name;
+ p_list->push_back(pi);
+ }
+}
+
void AudioStreamPlayer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_stream", "stream"), &AudioStreamPlayer3D::set_stream);
ClassDB::bind_method(D_METHOD("get_stream"), &AudioStreamPlayer3D::get_stream);
diff --git a/scene/3d/audio_stream_player_3d.h b/scene/3d/audio_stream_player_3d.h
index f20170e63b..facded1b9c 100644
--- a/scene/3d/audio_stream_player_3d.h
+++ b/scene/3d/audio_stream_player_3d.h
@@ -121,11 +121,23 @@ private:
float panning_strength = 1.0f;
float cached_global_panning_strength = 0.5f;
+ struct ParameterData {
+ StringName path;
+ Variant value;
+ };
+
+ HashMap<StringName, ParameterData> playback_parameters;
+ void _update_stream_parameters();
+
protected:
void _validate_property(PropertyInfo &p_property) const;
void _notification(int p_what);
static void _bind_methods();
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
public:
void set_stream(Ref<AudioStream> p_stream);
Ref<AudioStream> get_stream() const;
diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp
index f3c986f9b3..bd4731d8dd 100644
--- a/scene/audio/audio_stream_player.cpp
+++ b/scene/audio/audio_stream_player.cpp
@@ -34,6 +34,8 @@
#include "core/math/audio_frame.h"
#include "servers/audio_server.h"
+#define PARAM_PREFIX "parameters/"
+
void AudioStreamPlayer::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
@@ -88,9 +90,71 @@ void AudioStreamPlayer::_notification(int p_what) {
}
}
+void AudioStreamPlayer::_update_stream_parameters() {
+ if (stream.is_null()) {
+ return;
+ }
+ List<AudioStream::Parameter> parameters;
+ stream->get_parameter_list(&parameters);
+ for (const AudioStream::Parameter &K : parameters) {
+ const PropertyInfo &pi = K.property;
+ StringName key = PARAM_PREFIX + pi.name;
+ if (!playback_parameters.has(key)) {
+ ParameterData pd;
+ pd.path = pi.name;
+ pd.value = K.default_value;
+ playback_parameters.insert(key, pd);
+ }
+ }
+}
+
void AudioStreamPlayer::set_stream(Ref<AudioStream> p_stream) {
+ if (stream.is_valid()) {
+ stream->disconnect(SNAME("parameter_list_changed"), callable_mp(this, &AudioStreamPlayer::_update_stream_parameters));
+ }
stop();
stream = p_stream;
+ _update_stream_parameters();
+ if (stream.is_valid()) {
+ stream->connect(SNAME("parameter_list_changed"), callable_mp(this, &AudioStreamPlayer::_update_stream_parameters));
+ }
+ notify_property_list_changed();
+}
+
+bool AudioStreamPlayer::_set(const StringName &p_name, const Variant &p_value) {
+ HashMap<StringName, ParameterData>::Iterator I = playback_parameters.find(p_name);
+ if (!I) {
+ return false;
+ }
+ ParameterData &pd = I->value;
+ pd.value = p_value;
+ for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ playback->set_parameter(pd.path, pd.value);
+ }
+ return true;
+}
+
+bool AudioStreamPlayer::_get(const StringName &p_name, Variant &r_ret) const {
+ HashMap<StringName, ParameterData>::ConstIterator I = playback_parameters.find(p_name);
+ if (!I) {
+ return false;
+ }
+
+ r_ret = I->value.value;
+ return true;
+}
+
+void AudioStreamPlayer::_get_property_list(List<PropertyInfo> *p_list) const {
+ if (stream.is_null()) {
+ return;
+ }
+ List<AudioStream::Parameter> parameters;
+ stream->get_parameter_list(&parameters);
+ for (const AudioStream::Parameter &K : parameters) {
+ PropertyInfo pi = K.property;
+ pi.name = PARAM_PREFIX + pi.name;
+ p_list->push_back(pi);
+ }
}
Ref<AudioStream> AudioStreamPlayer::get_stream() const {
@@ -144,6 +208,10 @@ void AudioStreamPlayer::play(float p_from_pos) {
Ref<AudioStreamPlayback> stream_playback = stream->instantiate_playback();
ERR_FAIL_COND_MSG(stream_playback.is_null(), "Failed to instantiate playback.");
+ for (const KeyValue<StringName, ParameterData> &K : playback_parameters) {
+ stream_playback->set_parameter(K.value.path, K.value.value);
+ }
+
AudioServer::get_singleton()->start_playback_stream(stream_playback, bus, _get_volume_vector(), p_from_pos, pitch_scale);
stream_playbacks.push_back(stream_playback);
active.set();
diff --git a/scene/audio/audio_stream_player.h b/scene/audio/audio_stream_player.h
index 9dbdccdc69..404d6fbebf 100644
--- a/scene/audio/audio_stream_player.h
+++ b/scene/audio/audio_stream_player.h
@@ -68,11 +68,23 @@ private:
Vector<AudioFrame> _get_volume_vector();
+ struct ParameterData {
+ StringName path;
+ Variant value;
+ };
+
+ HashMap<StringName, ParameterData> playback_parameters;
+ void _update_stream_parameters();
+
protected:
void _validate_property(PropertyInfo &p_property) const;
void _notification(int p_what);
static void _bind_methods();
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
public:
void set_stream(Ref<AudioStream> p_stream);
Ref<AudioStream> get_stream() const;
diff --git a/servers/audio/audio_stream.cpp b/servers/audio/audio_stream.cpp
index 65d88a0eba..3e3a7d2381 100644
--- a/servers/audio/audio_stream.cpp
+++ b/servers/audio/audio_stream.cpp
@@ -80,6 +80,16 @@ void AudioStreamPlayback::tag_used_streams() {
GDVIRTUAL_CALL(_tag_used_streams);
}
+void AudioStreamPlayback::set_parameter(const StringName &p_name, const Variant &p_value) {
+ GDVIRTUAL_CALL(_set_parameter, p_name, p_value);
+}
+
+Variant AudioStreamPlayback::get_parameter(const StringName &p_name) const {
+ Variant ret;
+ GDVIRTUAL_CALL(_get_parameter, p_name, ret);
+ return ret;
+}
+
void AudioStreamPlayback::_bind_methods() {
GDVIRTUAL_BIND(_start, "from_pos")
GDVIRTUAL_BIND(_stop)
@@ -89,6 +99,8 @@ void AudioStreamPlayback::_bind_methods() {
GDVIRTUAL_BIND(_seek, "position")
GDVIRTUAL_BIND(_mix, "buffer", "rate_scale", "frames");
GDVIRTUAL_BIND(_tag_used_streams);
+ GDVIRTUAL_BIND(_set_parameter, "name", "value");
+ GDVIRTUAL_BIND(_get_parameter, "name");
}
//////////////////////////////
@@ -249,6 +261,16 @@ float AudioStream::get_tagged_frame_offset(int p_index) const {
return tagged_offsets[p_index];
}
+void AudioStream::get_parameter_list(List<Parameter> *r_parameters) {
+ TypedArray<Dictionary> ret;
+ GDVIRTUAL_CALL(_get_parameter_list, ret);
+ for (int i = 0; i < ret.size(); i++) {
+ Dictionary d = ret[i];
+ ERR_CONTINUE(!d.has("default_value"));
+ r_parameters->push_back(Parameter(PropertyInfo::from_dict(d), d["default_value"]));
+ }
+}
+
void AudioStream::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_length"), &AudioStream::get_length);
ClassDB::bind_method(D_METHOD("is_monophonic"), &AudioStream::is_monophonic);
@@ -259,6 +281,9 @@ void AudioStream::_bind_methods() {
GDVIRTUAL_BIND(_is_monophonic);
GDVIRTUAL_BIND(_get_bpm)
GDVIRTUAL_BIND(_get_beat_count)
+ GDVIRTUAL_BIND(_get_parameter_list)
+
+ ADD_SIGNAL(MethodInfo("parameter_list_changed"));
}
////////////////////////////////
diff --git a/servers/audio/audio_stream.h b/servers/audio/audio_stream.h
index 015e89fc8e..f8123fbe15 100644
--- a/servers/audio/audio_stream.h
+++ b/servers/audio/audio_stream.h
@@ -38,6 +38,7 @@
#include "core/object/gdvirtual.gen.inc"
#include "core/variant/native_ptr.h"
+#include "core/variant/typed_array.h"
class AudioStream;
@@ -54,6 +55,9 @@ protected:
GDVIRTUAL1(_seek, double)
GDVIRTUAL3R(int, _mix, GDExtensionPtr<AudioFrame>, float, int)
GDVIRTUAL0(_tag_used_streams)
+ GDVIRTUAL2(_set_parameter, const StringName &, const Variant &)
+ GDVIRTUAL1RC(Variant, _get_parameter, const StringName &)
+
public:
virtual void start(double p_from_pos = 0.0);
virtual void stop();
@@ -66,6 +70,9 @@ public:
virtual void tag_used_streams();
+ virtual void set_parameter(const StringName &p_name, const Variant &p_value);
+ virtual Variant get_parameter(const StringName &p_name) const;
+
virtual int mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames);
};
@@ -124,6 +131,7 @@ protected:
GDVIRTUAL0RC(bool, _has_loop)
GDVIRTUAL0RC(int, _get_bar_beats)
GDVIRTUAL0RC(int, _get_beat_count)
+ GDVIRTUAL0RC(TypedArray<Dictionary>, _get_parameter_list)
public:
virtual Ref<AudioStreamPlayback> instantiate_playback();
@@ -141,6 +149,17 @@ public:
uint64_t get_tagged_frame() const;
uint32_t get_tagged_frame_count() const;
float get_tagged_frame_offset(int p_index) const;
+
+ struct Parameter {
+ PropertyInfo property;
+ Variant default_value;
+ Parameter(const PropertyInfo &p_info = PropertyInfo(), const Variant &p_default_value = Variant()) {
+ property = p_info;
+ default_value = p_default_value;
+ }
+ };
+
+ virtual void get_parameter_list(List<Parameter> *r_parameters);
};
// Microphone