Merge pull request #86473 from reduz/audio-playback-parameters
Implement audio stream playback parameters.
This commit is contained in:
commit
3df0c5be6a
|
@ -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