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>
This commit is contained in:
Juan Linietsky 2023-12-23 17:30:32 +01:00
parent 13a0d6e9b2
commit a40fe16866
14 changed files with 382 additions and 6 deletions

View file

@ -28,6 +28,12 @@
<description> <description>
</description> </description>
</method> </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"> <method name="_get_stream_name" qualifiers="virtual const">
<return type="String" /> <return type="String" />
<description> <description>
@ -62,4 +68,11 @@
</description> </description>
</method> </method>
</methods> </methods>
<signals>
<signal name="parameter_list_changed">
<description>
Signal to be emitted to notify when the parameter list changed.
</description>
</signal>
</signals>
</class> </class>

View file

@ -15,6 +15,13 @@
<description> <description>
</description> </description>
</method> </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"> <method name="_get_playback_position" qualifiers="virtual const">
<return type="float" /> <return type="float" />
<description> <description>
@ -39,6 +46,14 @@
<description> <description>
</description> </description>
</method> </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"> <method name="_start" qualifiers="virtual">
<return type="void" /> <return type="void" />
<param index="0" name="from_pos" type="float" /> <param index="0" name="from_pos" type="float" />

View file

@ -46,7 +46,9 @@ int AudioStreamPlaybackMP3::_mix_internal(AudioFrame *p_buffer, int p_frames) {
int frames_mixed_this_step = p_frames; int frames_mixed_this_step = p_frames;
int beat_length_frames = -1; 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) { if (beat_loop) {
beat_length_frames = mp3_stream->get_beat_count() * mp3_stream->sample_rate * 60 / mp3_stream->get_bpm(); 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 { else {
//EOF //EOF
if (mp3_stream->loop) { if (use_loop) {
seek(mp3_stream->loop_offset); seek(mp3_stream->loop_offset);
loops++; loops++;
} else { } else {
@ -143,6 +145,25 @@ void AudioStreamPlaybackMP3::tag_used_streams() {
mp3_stream->tag_used(get_playback_position()); 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() { AudioStreamPlaybackMP3::~AudioStreamPlaybackMP3() {
if (mp3d) { if (mp3d) {
mp3dec_ex_close(mp3d); mp3dec_ex_close(mp3d);
@ -232,6 +253,10 @@ bool AudioStreamMP3::is_monophonic() const {
return false; 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) { void AudioStreamMP3::set_bpm(double p_bpm) {
ERR_FAIL_COND(p_bpm < 0); ERR_FAIL_COND(p_bpm < 0);
bpm = p_bpm; bpm = p_bpm;

View file

@ -47,6 +47,8 @@ class AudioStreamPlaybackMP3 : public AudioStreamPlaybackResampled {
AudioFrame loop_fade[FADE_SIZE]; AudioFrame loop_fade[FADE_SIZE];
int loop_fade_remaining = FADE_SIZE; int loop_fade_remaining = FADE_SIZE;
bool looping_override = false;
bool looping = false;
mp3dec_ex_t *mp3d = nullptr; mp3dec_ex_t *mp3d = nullptr;
uint32_t frames_mixed = 0; uint32_t frames_mixed = 0;
bool active = false; bool active = false;
@ -72,6 +74,9 @@ public:
virtual void tag_used_streams() override; 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() {}
~AudioStreamPlaybackMP3(); ~AudioStreamPlaybackMP3();
}; };
@ -126,6 +131,8 @@ public:
virtual bool is_monophonic() const override; virtual bool is_monophonic() const override;
virtual void get_parameter_list(List<Parameter> *r_parameters) override;
AudioStreamMP3(); AudioStreamMP3();
virtual ~AudioStreamMP3(); virtual ~AudioStreamMP3();
}; };

View file

@ -46,8 +46,9 @@ int AudioStreamPlaybackOggVorbis::_mix_internal(AudioFrame *p_buffer, int p_fram
int todo = p_frames; int todo = p_frames;
int beat_length_frames = -1; int beat_length_frames = -1;
bool beat_loop = vorbis_stream->has_loop(); bool use_loop = looping_override ? looping : vorbis_stream->loop;
if (beat_loop && vorbis_stream->get_bpm() > 0 && vorbis_stream->get_beat_count() > 0) {
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(); 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 } 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 // End of file when doing beat-based looping. <= used instead of == because importer editing
if (!have_packets_left && !have_samples_left) { if (!have_packets_left && !have_samples_left) {
//Nothing remaining, so do nothing. //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) { if (!have_packets_left && !have_samples_left) {
// Actual end of file! // Actual end of file!
bool is_not_empty = mixed > 0 || vorbis_stream->get_length() > 0; 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 //loop
seek(vorbis_stream->loop_offset); seek(vorbis_stream->loop_offset);
@ -257,6 +258,25 @@ void AudioStreamPlaybackOggVorbis::tag_used_streams() {
vorbis_stream->tag_used(get_playback_position()); 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) { void AudioStreamPlaybackOggVorbis::seek(double p_time) {
ERR_FAIL_COND(!ready); ERR_FAIL_COND(!ready);
ERR_FAIL_COND(vorbis_stream.is_null()); ERR_FAIL_COND(vorbis_stream.is_null());
@ -493,6 +513,10 @@ bool AudioStreamOggVorbis::is_monophonic() const {
return false; 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() { 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_buffer", "buffer"), &AudioStreamOggVorbis::load_from_buffer);
ClassDB::bind_static_method("AudioStreamOggVorbis", D_METHOD("load_from_file", "path"), &AudioStreamOggVorbis::load_from_file); ClassDB::bind_static_method("AudioStreamOggVorbis", D_METHOD("load_from_file", "path"), &AudioStreamOggVorbis::load_from_file);

View file

@ -44,6 +44,8 @@ class AudioStreamPlaybackOggVorbis : public AudioStreamPlaybackResampled {
uint32_t frames_mixed = 0; uint32_t frames_mixed = 0;
bool active = false; bool active = false;
bool looping_override = false;
bool looping = false;
int loops = 0; int loops = 0;
enum { enum {
@ -95,6 +97,9 @@ public:
virtual void tag_used_streams() override; 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() {}
~AudioStreamPlaybackOggVorbis(); ~AudioStreamPlaybackOggVorbis();
}; };
@ -152,6 +157,8 @@ public:
virtual bool is_monophonic() const override; virtual bool is_monophonic() const override;
virtual void get_parameter_list(List<Parameter> *r_parameters) override;
AudioStreamOggVorbis(); AudioStreamOggVorbis();
virtual ~AudioStreamOggVorbis(); virtual ~AudioStreamOggVorbis();
}; };

View file

@ -36,6 +36,8 @@
#include "scene/main/window.h" #include "scene/main/window.h"
#include "scene/resources/world_2d.h" #include "scene/resources/world_2d.h"
#define PARAM_PREFIX "parameters/"
void AudioStreamPlayer2D::_notification(int p_what) { void AudioStreamPlayer2D::_notification(int p_what) {
switch (p_what) { switch (p_what) {
case NOTIFICATION_ENTER_TREE: { case NOTIFICATION_ENTER_TREE: {
@ -222,9 +224,36 @@ void AudioStreamPlayer2D::_update_panning() {
last_mix_count = AudioServer::get_singleton()->get_mix_count(); 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) { 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(); stop();
stream = p_stream; 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 { Ref<AudioStream> AudioStreamPlayer2D::get_stream() const {
@ -262,6 +291,10 @@ void AudioStreamPlayer2D::play(float p_from_pos) {
Ref<AudioStreamPlayback> stream_playback = stream->instantiate_playback(); Ref<AudioStreamPlayback> stream_playback = stream->instantiate_playback();
ERR_FAIL_COND_MSG(stream_playback.is_null(), "Failed to 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); stream_playbacks.push_back(stream_playback);
setplayback = stream_playback; setplayback = stream_playback;
setplay.set(p_from_pos); 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(); 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() { void AudioStreamPlayer2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_stream", "stream"), &AudioStreamPlayer2D::set_stream); ClassDB::bind_method(D_METHOD("set_stream", "stream"), &AudioStreamPlayer2D::set_stream);
ClassDB::bind_method(D_METHOD("get_stream"), &AudioStreamPlayer2D::get_stream); ClassDB::bind_method(D_METHOD("get_stream"), &AudioStreamPlayer2D::get_stream);

View file

@ -89,11 +89,23 @@ private:
float panning_strength = 1.0f; float panning_strength = 1.0f;
float cached_global_panning_strength = 0.5f; float cached_global_panning_strength = 0.5f;
struct ParameterData {
StringName path;
Variant value;
};
HashMap<StringName, ParameterData> playback_parameters;
void _update_stream_parameters();
protected: protected:
void _validate_property(PropertyInfo &p_property) const; void _validate_property(PropertyInfo &p_property) const;
void _notification(int p_what); void _notification(int p_what);
static void _bind_methods(); 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: public:
void set_stream(Ref<AudioStream> p_stream); void set_stream(Ref<AudioStream> p_stream);
Ref<AudioStream> get_stream() const; Ref<AudioStream> get_stream() const;

View file

@ -37,6 +37,8 @@
#include "scene/main/viewport.h" #include "scene/main/viewport.h"
#include "scene/scene_string_names.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) // 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) // Speaker-Placement Correction Amplitude Panning (SPCAP)
class Spcap { class Spcap {
@ -525,9 +527,35 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() {
return output_volume_vector; 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) { 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(); stop();
stream = p_stream; 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 { Ref<AudioStream> AudioStreamPlayer3D::get_stream() const {
@ -579,6 +607,10 @@ void AudioStreamPlayer3D::play(float p_from_pos) {
Ref<AudioStreamPlayback> stream_playback = stream->instantiate_playback(); Ref<AudioStreamPlayback> stream_playback = stream->instantiate_playback();
ERR_FAIL_COND_MSG(stream_playback.is_null(), "Failed to 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); stream_playbacks.push_back(stream_playback);
setplayback = stream_playback; setplayback = stream_playback;
setplay.set(p_from_pos); 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(); 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() { void AudioStreamPlayer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_stream", "stream"), &AudioStreamPlayer3D::set_stream); ClassDB::bind_method(D_METHOD("set_stream", "stream"), &AudioStreamPlayer3D::set_stream);
ClassDB::bind_method(D_METHOD("get_stream"), &AudioStreamPlayer3D::get_stream); ClassDB::bind_method(D_METHOD("get_stream"), &AudioStreamPlayer3D::get_stream);

View file

@ -121,11 +121,23 @@ private:
float panning_strength = 1.0f; float panning_strength = 1.0f;
float cached_global_panning_strength = 0.5f; float cached_global_panning_strength = 0.5f;
struct ParameterData {
StringName path;
Variant value;
};
HashMap<StringName, ParameterData> playback_parameters;
void _update_stream_parameters();
protected: protected:
void _validate_property(PropertyInfo &p_property) const; void _validate_property(PropertyInfo &p_property) const;
void _notification(int p_what); void _notification(int p_what);
static void _bind_methods(); 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: public:
void set_stream(Ref<AudioStream> p_stream); void set_stream(Ref<AudioStream> p_stream);
Ref<AudioStream> get_stream() const; Ref<AudioStream> get_stream() const;

View file

@ -34,6 +34,8 @@
#include "core/math/audio_frame.h" #include "core/math/audio_frame.h"
#include "servers/audio_server.h" #include "servers/audio_server.h"
#define PARAM_PREFIX "parameters/"
void AudioStreamPlayer::_notification(int p_what) { void AudioStreamPlayer::_notification(int p_what) {
switch (p_what) { switch (p_what) {
case NOTIFICATION_ENTER_TREE: { 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) { 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(); stop();
stream = p_stream; 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 { Ref<AudioStream> AudioStreamPlayer::get_stream() const {
@ -144,6 +208,10 @@ void AudioStreamPlayer::play(float p_from_pos) {
Ref<AudioStreamPlayback> stream_playback = stream->instantiate_playback(); Ref<AudioStreamPlayback> stream_playback = stream->instantiate_playback();
ERR_FAIL_COND_MSG(stream_playback.is_null(), "Failed to 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); AudioServer::get_singleton()->start_playback_stream(stream_playback, bus, _get_volume_vector(), p_from_pos, pitch_scale);
stream_playbacks.push_back(stream_playback); stream_playbacks.push_back(stream_playback);
active.set(); active.set();

View file

@ -68,11 +68,23 @@ private:
Vector<AudioFrame> _get_volume_vector(); Vector<AudioFrame> _get_volume_vector();
struct ParameterData {
StringName path;
Variant value;
};
HashMap<StringName, ParameterData> playback_parameters;
void _update_stream_parameters();
protected: protected:
void _validate_property(PropertyInfo &p_property) const; void _validate_property(PropertyInfo &p_property) const;
void _notification(int p_what); void _notification(int p_what);
static void _bind_methods(); 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: public:
void set_stream(Ref<AudioStream> p_stream); void set_stream(Ref<AudioStream> p_stream);
Ref<AudioStream> get_stream() const; Ref<AudioStream> get_stream() const;

View file

@ -80,6 +80,16 @@ void AudioStreamPlayback::tag_used_streams() {
GDVIRTUAL_CALL(_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() { void AudioStreamPlayback::_bind_methods() {
GDVIRTUAL_BIND(_start, "from_pos") GDVIRTUAL_BIND(_start, "from_pos")
GDVIRTUAL_BIND(_stop) GDVIRTUAL_BIND(_stop)
@ -89,6 +99,8 @@ void AudioStreamPlayback::_bind_methods() {
GDVIRTUAL_BIND(_seek, "position") GDVIRTUAL_BIND(_seek, "position")
GDVIRTUAL_BIND(_mix, "buffer", "rate_scale", "frames"); GDVIRTUAL_BIND(_mix, "buffer", "rate_scale", "frames");
GDVIRTUAL_BIND(_tag_used_streams); 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]; 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() { void AudioStream::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_length"), &AudioStream::get_length); ClassDB::bind_method(D_METHOD("get_length"), &AudioStream::get_length);
ClassDB::bind_method(D_METHOD("is_monophonic"), &AudioStream::is_monophonic); ClassDB::bind_method(D_METHOD("is_monophonic"), &AudioStream::is_monophonic);
@ -259,6 +281,9 @@ void AudioStream::_bind_methods() {
GDVIRTUAL_BIND(_is_monophonic); GDVIRTUAL_BIND(_is_monophonic);
GDVIRTUAL_BIND(_get_bpm) GDVIRTUAL_BIND(_get_bpm)
GDVIRTUAL_BIND(_get_beat_count) GDVIRTUAL_BIND(_get_beat_count)
GDVIRTUAL_BIND(_get_parameter_list)
ADD_SIGNAL(MethodInfo("parameter_list_changed"));
} }
//////////////////////////////// ////////////////////////////////

View file

@ -38,6 +38,7 @@
#include "core/object/gdvirtual.gen.inc" #include "core/object/gdvirtual.gen.inc"
#include "core/variant/native_ptr.h" #include "core/variant/native_ptr.h"
#include "core/variant/typed_array.h"
class AudioStream; class AudioStream;
@ -54,6 +55,9 @@ protected:
GDVIRTUAL1(_seek, double) GDVIRTUAL1(_seek, double)
GDVIRTUAL3R(int, _mix, GDExtensionPtr<AudioFrame>, float, int) GDVIRTUAL3R(int, _mix, GDExtensionPtr<AudioFrame>, float, int)
GDVIRTUAL0(_tag_used_streams) GDVIRTUAL0(_tag_used_streams)
GDVIRTUAL2(_set_parameter, const StringName &, const Variant &)
GDVIRTUAL1RC(Variant, _get_parameter, const StringName &)
public: public:
virtual void start(double p_from_pos = 0.0); virtual void start(double p_from_pos = 0.0);
virtual void stop(); virtual void stop();
@ -66,6 +70,9 @@ public:
virtual void tag_used_streams(); 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); virtual int mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames);
}; };
@ -124,6 +131,7 @@ protected:
GDVIRTUAL0RC(bool, _has_loop) GDVIRTUAL0RC(bool, _has_loop)
GDVIRTUAL0RC(int, _get_bar_beats) GDVIRTUAL0RC(int, _get_bar_beats)
GDVIRTUAL0RC(int, _get_beat_count) GDVIRTUAL0RC(int, _get_beat_count)
GDVIRTUAL0RC(TypedArray<Dictionary>, _get_parameter_list)
public: public:
virtual Ref<AudioStreamPlayback> instantiate_playback(); virtual Ref<AudioStreamPlayback> instantiate_playback();
@ -141,6 +149,17 @@ public:
uint64_t get_tagged_frame() const; uint64_t get_tagged_frame() const;
uint32_t get_tagged_frame_count() const; uint32_t get_tagged_frame_count() const;
float get_tagged_frame_offset(int p_index) 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 // Microphone