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:
parent
13a0d6e9b2
commit
a40fe16866
|
@ -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>
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -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(¶meters);
|
||||
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(¶meters);
|
||||
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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(¶meters);
|
||||
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(¶meters);
|
||||
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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(¶meters);
|
||||
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(¶meters);
|
||||
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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue