mirror of
https://github.com/godotengine/godot
synced 2024-09-05 18:49:46 +00:00
Add polyphony to Audio Stream Player nodes
This commit is contained in:
parent
ec70295c01
commit
0e3cab41eb
|
@ -28,12 +28,23 @@
|
||||||
<description>
|
<description>
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
|
<method name="_is_monophonic" qualifiers="virtual const">
|
||||||
|
<return type="bool" />
|
||||||
|
<description>
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
<method name="get_length" qualifiers="const">
|
<method name="get_length" qualifiers="const">
|
||||||
<return type="float" />
|
<return type="float" />
|
||||||
<description>
|
<description>
|
||||||
Returns the length of the audio stream in seconds.
|
Returns the length of the audio stream in seconds.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
|
<method name="is_monophonic" qualifiers="const">
|
||||||
|
<return type="bool" />
|
||||||
|
<description>
|
||||||
|
Returns true if this audio stream only supports monophonic playback, or false if the audio stream supports polyphony.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
</methods>
|
</methods>
|
||||||
<constants>
|
<constants>
|
||||||
</constants>
|
</constants>
|
||||||
|
|
|
@ -56,6 +56,9 @@
|
||||||
<member name="bus" type="StringName" setter="set_bus" getter="get_bus" default="&"Master"">
|
<member name="bus" type="StringName" setter="set_bus" getter="get_bus" default="&"Master"">
|
||||||
Bus on which this audio is playing.
|
Bus on which this audio is playing.
|
||||||
</member>
|
</member>
|
||||||
|
<member name="max_polyphony" type="int" setter="set_max_polyphony" getter="get_max_polyphony" default="1">
|
||||||
|
The maximum number of sounds this node can play at the same time. Playing additional sounds after this value is reached will cut off the oldest sounds.
|
||||||
|
</member>
|
||||||
<member name="mix_target" type="int" setter="set_mix_target" getter="get_mix_target" enum="AudioStreamPlayer.MixTarget" default="0">
|
<member name="mix_target" type="int" setter="set_mix_target" getter="get_mix_target" enum="AudioStreamPlayer.MixTarget" default="0">
|
||||||
If the audio configuration has more than two speakers, this sets the target channels. See [enum MixTarget] constants.
|
If the audio configuration has more than two speakers, this sets the target channels. See [enum MixTarget] constants.
|
||||||
</member>
|
</member>
|
||||||
|
|
|
@ -61,6 +61,9 @@
|
||||||
<member name="max_distance" type="float" setter="set_max_distance" getter="get_max_distance" default="2000.0">
|
<member name="max_distance" type="float" setter="set_max_distance" getter="get_max_distance" default="2000.0">
|
||||||
Maximum distance from which audio is still hearable.
|
Maximum distance from which audio is still hearable.
|
||||||
</member>
|
</member>
|
||||||
|
<member name="max_polyphony" type="int" setter="set_max_polyphony" getter="get_max_polyphony" default="1">
|
||||||
|
The maximum number of sounds this node can play at the same time. Playing additional sounds after this value is reached will cut off the oldest sounds.
|
||||||
|
</member>
|
||||||
<member name="pitch_scale" type="float" setter="set_pitch_scale" getter="get_pitch_scale" default="1.0">
|
<member name="pitch_scale" type="float" setter="set_pitch_scale" getter="get_pitch_scale" default="1.0">
|
||||||
The pitch and the tempo of the audio, as a multiplier of the audio sample's sample rate.
|
The pitch and the tempo of the audio, as a multiplier of the audio sample's sample rate.
|
||||||
</member>
|
</member>
|
||||||
|
|
|
@ -83,6 +83,9 @@
|
||||||
<member name="max_distance" type="float" setter="set_max_distance" getter="get_max_distance" default="0.0">
|
<member name="max_distance" type="float" setter="set_max_distance" getter="get_max_distance" default="0.0">
|
||||||
Sets the distance from which the [member out_of_range_mode] takes effect. Has no effect if set to 0.
|
Sets the distance from which the [member out_of_range_mode] takes effect. Has no effect if set to 0.
|
||||||
</member>
|
</member>
|
||||||
|
<member name="max_polyphony" type="int" setter="set_max_polyphony" getter="get_max_polyphony" default="1">
|
||||||
|
The maximum number of sounds this node can play at the same time. Playing additional sounds after this value is reached will cut off the oldest sounds.
|
||||||
|
</member>
|
||||||
<member name="out_of_range_mode" type="int" setter="set_out_of_range_mode" getter="get_out_of_range_mode" enum="AudioStreamPlayer3D.OutOfRangeMode" default="0">
|
<member name="out_of_range_mode" type="int" setter="set_out_of_range_mode" getter="get_out_of_range_mode" enum="AudioStreamPlayer3D.OutOfRangeMode" default="0">
|
||||||
Decides if audio should pause when source is outside of [member max_distance] range.
|
Decides if audio should pause when source is outside of [member max_distance] range.
|
||||||
</member>
|
</member>
|
||||||
|
|
|
@ -214,6 +214,10 @@ float AudioStreamMP3::get_length() const {
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AudioStreamMP3::is_monophonic() const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void AudioStreamMP3::_bind_methods() {
|
void AudioStreamMP3::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("set_data", "data"), &AudioStreamMP3::set_data);
|
ClassDB::bind_method(D_METHOD("set_data", "data"), &AudioStreamMP3::set_data);
|
||||||
ClassDB::bind_method(D_METHOD("get_data"), &AudioStreamMP3::get_data);
|
ClassDB::bind_method(D_METHOD("get_data"), &AudioStreamMP3::get_data);
|
||||||
|
|
|
@ -103,6 +103,8 @@ public:
|
||||||
|
|
||||||
virtual float get_length() const override;
|
virtual float get_length() const override;
|
||||||
|
|
||||||
|
virtual bool is_monophonic() const override;
|
||||||
|
|
||||||
AudioStreamMP3();
|
AudioStreamMP3();
|
||||||
virtual ~AudioStreamMP3();
|
virtual ~AudioStreamMP3();
|
||||||
};
|
};
|
||||||
|
|
|
@ -252,6 +252,10 @@ float AudioStreamOGGVorbis::get_length() const {
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AudioStreamOGGVorbis::is_monophonic() const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void AudioStreamOGGVorbis::_bind_methods() {
|
void AudioStreamOGGVorbis::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("set_data", "data"), &AudioStreamOGGVorbis::set_data);
|
ClassDB::bind_method(D_METHOD("set_data", "data"), &AudioStreamOGGVorbis::set_data);
|
||||||
ClassDB::bind_method(D_METHOD("get_data"), &AudioStreamOGGVorbis::get_data);
|
ClassDB::bind_method(D_METHOD("get_data"), &AudioStreamOGGVorbis::get_data);
|
||||||
|
|
|
@ -105,6 +105,8 @@ public:
|
||||||
|
|
||||||
virtual float get_length() const override; //if supported, otherwise return 0
|
virtual float get_length() const override; //if supported, otherwise return 0
|
||||||
|
|
||||||
|
virtual bool is_monophonic() const override;
|
||||||
|
|
||||||
AudioStreamOGGVorbis();
|
AudioStreamOGGVorbis();
|
||||||
virtual ~AudioStreamOGGVorbis();
|
virtual ~AudioStreamOGGVorbis();
|
||||||
};
|
};
|
||||||
|
|
|
@ -59,33 +59,47 @@ void AudioStreamPlayer2D::_notification(int p_what) {
|
||||||
|
|
||||||
if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
|
if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
|
||||||
//update anything related to position first, if possible of course
|
//update anything related to position first, if possible of course
|
||||||
|
if (setplay.get() > 0 || (active.is_set() && last_mix_count != AudioServer::get_singleton()->get_mix_count())) {
|
||||||
if (!stream_playback.is_valid()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (setplay.get() >= 0 || (active.is_set() && last_mix_count != AudioServer::get_singleton()->get_mix_count())) {
|
|
||||||
_update_panning();
|
_update_panning();
|
||||||
if (setplay.get() >= 0) {
|
}
|
||||||
active.set();
|
|
||||||
AudioServer::get_singleton()->start_playback_stream(stream_playback, _get_actual_bus(), volume_vector, setplay.get());
|
if (setplay.get() >= 0 && stream.is_valid()) {
|
||||||
setplay.set(-1);
|
active.set();
|
||||||
|
Ref<AudioStreamPlayback> new_playback = stream->instance_playback();
|
||||||
|
ERR_FAIL_COND_MSG(new_playback.is_null(), "Failed to instantiate playback.");
|
||||||
|
AudioServer::get_singleton()->start_playback_stream(new_playback, _get_actual_bus(), volume_vector, setplay.get());
|
||||||
|
stream_playbacks.push_back(new_playback);
|
||||||
|
setplay.set(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!stream_playbacks.is_empty() && active.is_set()) {
|
||||||
|
// Stop playing if no longer active.
|
||||||
|
Vector<Ref<AudioStreamPlayback>> playbacks_to_remove;
|
||||||
|
for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
|
||||||
|
if (playback.is_valid() && !AudioServer::get_singleton()->is_playback_active(playback) && !AudioServer::get_singleton()->is_playback_paused(playback)) {
|
||||||
|
emit_signal(SNAME("finished"));
|
||||||
|
playbacks_to_remove.push_back(playback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Now go through and remove playbacks that have finished. Removing elements from a Vector in a range based for is asking for trouble.
|
||||||
|
for (Ref<AudioStreamPlayback> &playback : playbacks_to_remove) {
|
||||||
|
stream_playbacks.erase(playback);
|
||||||
|
}
|
||||||
|
if (!playbacks_to_remove.is_empty() && stream_playbacks.is_empty()) {
|
||||||
|
// This node is no longer actively playing audio.
|
||||||
|
active.clear();
|
||||||
|
set_physics_process_internal(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop playing if no longer active.
|
while (stream_playbacks.size() > max_polyphony) {
|
||||||
if (active.is_set() && !AudioServer::get_singleton()->is_playback_active(stream_playback)) {
|
AudioServer::get_singleton()->stop_playback_stream(stream_playbacks[0]);
|
||||||
active.clear();
|
stream_playbacks.remove(0);
|
||||||
set_physics_process_internal(false);
|
|
||||||
emit_signal(SNAME("finished"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StringName AudioStreamPlayer2D::_get_actual_bus() {
|
StringName AudioStreamPlayer2D::_get_actual_bus() {
|
||||||
if (!stream_playback.is_valid()) {
|
|
||||||
return SNAME("Master");
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector2 global_pos = get_global_position();
|
Vector2 global_pos = get_global_position();
|
||||||
|
|
||||||
//check if any area is diverting sound into a bus
|
//check if any area is diverting sound into a bus
|
||||||
|
@ -113,12 +127,10 @@ StringName AudioStreamPlayer2D::_get_actual_bus() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioStreamPlayer2D::_update_panning() {
|
void AudioStreamPlayer2D::_update_panning() {
|
||||||
if (!stream_playback.is_valid()) {
|
if (!active.is_set() || stream.is_null()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
last_mix_count = AudioServer::get_singleton()->get_mix_count();
|
|
||||||
|
|
||||||
Ref<World2D> world_2d = get_world_2d();
|
Ref<World2D> world_2d = get_world_2d();
|
||||||
ERR_FAIL_COND(world_2d.is_null());
|
ERR_FAIL_COND(world_2d.is_null());
|
||||||
|
|
||||||
|
@ -164,28 +176,20 @@ void AudioStreamPlayer2D::_update_panning() {
|
||||||
volume_vector.write[0] = AudioFrame(l, r) * multiplier;
|
volume_vector.write[0] = AudioFrame(l, r) * multiplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioServer::get_singleton()->set_playback_bus_exclusive(stream_playback, _get_actual_bus(), volume_vector);
|
for (const Ref<AudioStreamPlayback> &playback : stream_playbacks) {
|
||||||
|
AudioServer::get_singleton()->set_playback_bus_exclusive(playback, _get_actual_bus(), volume_vector);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
|
||||||
|
AudioServer::get_singleton()->set_playback_pitch_scale(playback, pitch_scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
last_mix_count = AudioServer::get_singleton()->get_mix_count();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioStreamPlayer2D::set_stream(Ref<AudioStream> p_stream) {
|
void AudioStreamPlayer2D::set_stream(Ref<AudioStream> p_stream) {
|
||||||
if (stream_playback.is_valid()) {
|
stop();
|
||||||
stop();
|
stream = p_stream;
|
||||||
}
|
|
||||||
|
|
||||||
stream_playback.unref();
|
|
||||||
stream.unref();
|
|
||||||
if (p_stream.is_valid()) {
|
|
||||||
stream_playback = p_stream->instance_playback();
|
|
||||||
if (stream_playback.is_valid()) {
|
|
||||||
stream = p_stream;
|
|
||||||
} else {
|
|
||||||
stream.unref();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p_stream.is_valid() && stream_playback.is_null()) {
|
|
||||||
stream.unref();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<AudioStream> AudioStreamPlayer2D::get_stream() const {
|
Ref<AudioStream> AudioStreamPlayer2D::get_stream() const {
|
||||||
|
@ -203,8 +207,8 @@ float AudioStreamPlayer2D::get_volume_db() const {
|
||||||
void AudioStreamPlayer2D::set_pitch_scale(float p_pitch_scale) {
|
void AudioStreamPlayer2D::set_pitch_scale(float p_pitch_scale) {
|
||||||
ERR_FAIL_COND(p_pitch_scale <= 0.0);
|
ERR_FAIL_COND(p_pitch_scale <= 0.0);
|
||||||
pitch_scale = p_pitch_scale;
|
pitch_scale = p_pitch_scale;
|
||||||
if (stream_playback.is_valid()) {
|
for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
|
||||||
AudioServer::get_singleton()->set_playback_pitch_scale(stream_playback, p_pitch_scale);
|
AudioServer::get_singleton()->set_playback_pitch_scale(playback, p_pitch_scale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,44 +217,50 @@ float AudioStreamPlayer2D::get_pitch_scale() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioStreamPlayer2D::play(float p_from_pos) {
|
void AudioStreamPlayer2D::play(float p_from_pos) {
|
||||||
stop();
|
if (stream.is_null()) {
|
||||||
if (stream.is_valid()) {
|
return;
|
||||||
stream_playback = stream->instance_playback();
|
|
||||||
}
|
}
|
||||||
if (stream_playback.is_valid()) {
|
ERR_FAIL_COND_MSG(!is_inside_tree(), "Playback can only happen when a node is inside the scene tree");
|
||||||
setplay.set(p_from_pos);
|
if (stream->is_monophonic() && is_playing()) {
|
||||||
set_physics_process_internal(true);
|
stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setplay.set(p_from_pos);
|
||||||
|
active.set();
|
||||||
|
set_physics_process_internal(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioStreamPlayer2D::seek(float p_seconds) {
|
void AudioStreamPlayer2D::seek(float p_seconds) {
|
||||||
if (stream_playback.is_valid() && active.is_set()) {
|
if (is_playing()) {
|
||||||
|
stop();
|
||||||
play(p_seconds);
|
play(p_seconds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioStreamPlayer2D::stop() {
|
void AudioStreamPlayer2D::stop() {
|
||||||
if (stream_playback.is_valid()) {
|
setplay.set(-1);
|
||||||
active.clear();
|
for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
|
||||||
AudioServer::get_singleton()->stop_playback_stream(stream_playback);
|
AudioServer::get_singleton()->stop_playback_stream(playback);
|
||||||
set_physics_process_internal(false);
|
|
||||||
setplay.set(-1);
|
|
||||||
}
|
}
|
||||||
|
stream_playbacks.clear();
|
||||||
|
active.clear();
|
||||||
|
set_physics_process_internal(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioStreamPlayer2D::is_playing() const {
|
bool AudioStreamPlayer2D::is_playing() const {
|
||||||
if (stream_playback.is_valid()) {
|
for (const Ref<AudioStreamPlayback> &playback : stream_playbacks) {
|
||||||
return AudioServer::get_singleton()->is_playback_active(stream_playback);
|
if (AudioServer::get_singleton()->is_playback_active(playback)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
float AudioStreamPlayer2D::get_playback_position() {
|
float AudioStreamPlayer2D::get_playback_position() {
|
||||||
if (stream_playback.is_valid()) {
|
// Return the playback position of the most recently started playback stream.
|
||||||
return AudioServer::get_singleton()->get_playback_position(stream_playback);
|
if (!stream_playbacks.is_empty()) {
|
||||||
|
return AudioServer::get_singleton()->get_playback_position(stream_playbacks[stream_playbacks.size() - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,11 +294,7 @@ void AudioStreamPlayer2D::_set_playing(bool p_enable) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioStreamPlayer2D::_is_active() const {
|
bool AudioStreamPlayer2D::_is_active() const {
|
||||||
if (stream_playback.is_valid()) {
|
return active.is_set();
|
||||||
// TODO make sure this doesn't change any behavior w.r.t. pauses. Is a paused stream active?
|
|
||||||
return AudioServer::get_singleton()->is_playback_active(stream_playback);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioStreamPlayer2D::_validate_property(PropertyInfo &property) const {
|
void AudioStreamPlayer2D::_validate_property(PropertyInfo &property) const {
|
||||||
|
@ -336,21 +342,35 @@ uint32_t AudioStreamPlayer2D::get_area_mask() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioStreamPlayer2D::set_stream_paused(bool p_pause) {
|
void AudioStreamPlayer2D::set_stream_paused(bool p_pause) {
|
||||||
// TODO this does not have perfect recall, fix that maybe? If the stream isn't set, we can't persist this bool.
|
// TODO this does not have perfect recall, fix that maybe? If there are zero playbacks registered with the AudioServer, this bool isn't persisted.
|
||||||
if (stream_playback.is_valid()) {
|
for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
|
||||||
AudioServer::get_singleton()->set_playback_paused(stream_playback, p_pause);
|
AudioServer::get_singleton()->set_playback_paused(playback, p_pause);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioStreamPlayer2D::get_stream_paused() const {
|
bool AudioStreamPlayer2D::get_stream_paused() const {
|
||||||
if (stream_playback.is_valid()) {
|
// There's currently no way to pause some playback streams but not others. Check the first and don't bother looking at the rest.
|
||||||
return AudioServer::get_singleton()->is_playback_paused(stream_playback);
|
if (!stream_playbacks.is_empty()) {
|
||||||
|
return AudioServer::get_singleton()->is_playback_paused(stream_playbacks[0]);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<AudioStreamPlayback> AudioStreamPlayer2D::get_stream_playback() {
|
Ref<AudioStreamPlayback> AudioStreamPlayer2D::get_stream_playback() {
|
||||||
return stream_playback;
|
if (!stream_playbacks.is_empty()) {
|
||||||
|
return stream_playbacks[stream_playbacks.size() - 1];
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioStreamPlayer2D::set_max_polyphony(int p_max_polyphony) {
|
||||||
|
if (p_max_polyphony > 0) {
|
||||||
|
max_polyphony = p_max_polyphony;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int AudioStreamPlayer2D::get_max_polyphony() const {
|
||||||
|
return max_polyphony;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioStreamPlayer2D::_bind_methods() {
|
void AudioStreamPlayer2D::_bind_methods() {
|
||||||
|
@ -391,6 +411,9 @@ void AudioStreamPlayer2D::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("set_stream_paused", "pause"), &AudioStreamPlayer2D::set_stream_paused);
|
ClassDB::bind_method(D_METHOD("set_stream_paused", "pause"), &AudioStreamPlayer2D::set_stream_paused);
|
||||||
ClassDB::bind_method(D_METHOD("get_stream_paused"), &AudioStreamPlayer2D::get_stream_paused);
|
ClassDB::bind_method(D_METHOD("get_stream_paused"), &AudioStreamPlayer2D::get_stream_paused);
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("set_max_polyphony", "max_polyphony"), &AudioStreamPlayer2D::set_max_polyphony);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_max_polyphony"), &AudioStreamPlayer2D::get_max_polyphony);
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer2D::get_stream_playback);
|
ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer2D::get_stream_playback);
|
||||||
|
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream");
|
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream");
|
||||||
|
@ -401,6 +424,7 @@ void AudioStreamPlayer2D::_bind_methods() {
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stream_paused", PROPERTY_HINT_NONE, ""), "set_stream_paused", "get_stream_paused");
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stream_paused", PROPERTY_HINT_NONE, ""), "set_stream_paused", "get_stream_paused");
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_distance", PROPERTY_HINT_RANGE, "1,4096,1,or_greater,exp"), "set_max_distance", "get_max_distance");
|
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_distance", PROPERTY_HINT_RANGE, "1,4096,1,or_greater,exp"), "set_max_distance", "get_max_distance");
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_attenuation", "get_attenuation");
|
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_attenuation", "get_attenuation");
|
||||||
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_polyphony", PROPERTY_HINT_NONE, ""), "set_max_polyphony", "get_max_polyphony");
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus");
|
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus");
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "area_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_area_mask", "get_area_mask");
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "area_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_area_mask", "get_area_mask");
|
||||||
|
|
||||||
|
|
|
@ -51,10 +51,10 @@ private:
|
||||||
Viewport *viewport = nullptr; //pointer only used for reference to previous mix
|
Viewport *viewport = nullptr; //pointer only used for reference to previous mix
|
||||||
};
|
};
|
||||||
|
|
||||||
Ref<AudioStreamPlayback> stream_playback;
|
Vector<Ref<AudioStreamPlayback>> stream_playbacks;
|
||||||
Ref<AudioStream> stream;
|
Ref<AudioStream> stream;
|
||||||
|
|
||||||
SafeFlag active;
|
SafeFlag active{ false };
|
||||||
SafeNumeric<float> setplay{ -1.0 };
|
SafeNumeric<float> setplay{ -1.0 };
|
||||||
|
|
||||||
Vector<AudioFrame> volume_vector;
|
Vector<AudioFrame> volume_vector;
|
||||||
|
@ -64,7 +64,8 @@ private:
|
||||||
float volume_db = 0.0;
|
float volume_db = 0.0;
|
||||||
float pitch_scale = 1.0;
|
float pitch_scale = 1.0;
|
||||||
bool autoplay = false;
|
bool autoplay = false;
|
||||||
StringName default_bus = "Master";
|
StringName default_bus = SNAME("Master");
|
||||||
|
int max_polyphony = 1;
|
||||||
|
|
||||||
void _set_playing(bool p_enable);
|
void _set_playing(bool p_enable);
|
||||||
bool _is_active() const;
|
bool _is_active() const;
|
||||||
|
@ -119,6 +120,9 @@ public:
|
||||||
void set_stream_paused(bool p_pause);
|
void set_stream_paused(bool p_pause);
|
||||||
bool get_stream_paused() const;
|
bool get_stream_paused() const;
|
||||||
|
|
||||||
|
void set_max_polyphony(int p_max_polyphony);
|
||||||
|
int get_max_polyphony() const;
|
||||||
|
|
||||||
Ref<AudioStreamPlayback> get_stream_playback();
|
Ref<AudioStreamPlayback> get_stream_playback();
|
||||||
|
|
||||||
AudioStreamPlayer2D();
|
AudioStreamPlayer2D();
|
||||||
|
|
|
@ -271,28 +271,45 @@ void AudioStreamPlayer3D::_notification(int p_what) {
|
||||||
|
|
||||||
if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
|
if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
|
||||||
//update anything related to position first, if possible of course
|
//update anything related to position first, if possible of course
|
||||||
|
Vector<AudioFrame> volume_vector;
|
||||||
if (!stream_playback.is_valid()) {
|
if (setplay.get() > 0 || (active.is_set() && last_mix_count != AudioServer::get_singleton()->get_mix_count())) {
|
||||||
return;
|
volume_vector = _update_panning();
|
||||||
}
|
}
|
||||||
//start playing if requested
|
|
||||||
|
|
||||||
if (setplay.get() >= 0) {
|
if (setplay.get() >= 0 && stream.is_valid()) {
|
||||||
Vector<AudioFrame> volume_vector = _update_panning();
|
|
||||||
AudioServer::get_singleton()->start_playback_stream(stream_playback, _get_actual_bus(), volume_vector, setplay.get());
|
|
||||||
active.set();
|
active.set();
|
||||||
|
Ref<AudioStreamPlayback> new_playback = stream->instance_playback();
|
||||||
|
ERR_FAIL_COND_MSG(new_playback.is_null(), "Failed to instantiate playback.");
|
||||||
|
Map<StringName, Vector<AudioFrame>> bus_map;
|
||||||
|
bus_map[_get_actual_bus()] = volume_vector;
|
||||||
|
AudioServer::get_singleton()->start_playback_stream(new_playback, bus_map, setplay.get(), linear_attenuation, attenuation_filter_cutoff_hz, actual_pitch_scale);
|
||||||
|
stream_playbacks.push_back(new_playback);
|
||||||
setplay.set(-1);
|
setplay.set(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (active.is_set() && last_mix_count != AudioServer::get_singleton()->get_mix_count()) {
|
if (!stream_playbacks.is_empty() && active.is_set()) {
|
||||||
_update_panning();
|
// Stop playing if no longer active.
|
||||||
last_mix_count = AudioServer::get_singleton()->get_mix_count();
|
Vector<Ref<AudioStreamPlayback>> playbacks_to_remove;
|
||||||
|
for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
|
||||||
|
if (playback.is_valid() && !AudioServer::get_singleton()->is_playback_active(playback) && !AudioServer::get_singleton()->is_playback_paused(playback)) {
|
||||||
|
emit_signal(SNAME("finished"));
|
||||||
|
playbacks_to_remove.push_back(playback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Now go through and remove playbacks that have finished. Removing elements from a Vector in a range based for is asking for trouble.
|
||||||
|
for (Ref<AudioStreamPlayback> &playback : playbacks_to_remove) {
|
||||||
|
stream_playbacks.erase(playback);
|
||||||
|
}
|
||||||
|
if (!playbacks_to_remove.is_empty() && stream_playbacks.is_empty()) {
|
||||||
|
// This node is no longer actively playing audio.
|
||||||
|
active.clear();
|
||||||
|
set_physics_process_internal(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop playing if no longer active.
|
while (stream_playbacks.size() > max_polyphony) {
|
||||||
if (!active.is_set()) {
|
AudioServer::get_singleton()->stop_playback_stream(stream_playbacks[0]);
|
||||||
set_physics_process_internal(false);
|
stream_playbacks.remove(0);
|
||||||
emit_signal(SNAME("finished"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -330,9 +347,6 @@ Area3D *AudioStreamPlayer3D::_get_overriding_area() {
|
||||||
}
|
}
|
||||||
|
|
||||||
StringName AudioStreamPlayer3D::_get_actual_bus() {
|
StringName AudioStreamPlayer3D::_get_actual_bus() {
|
||||||
if (!stream_playback.is_valid()) {
|
|
||||||
return SNAME("Master");
|
|
||||||
}
|
|
||||||
Area3D *overriding_area = _get_overriding_area();
|
Area3D *overriding_area = _get_overriding_area();
|
||||||
if (overriding_area && overriding_area->is_overriding_audio_bus() && !overriding_area->is_using_reverb_bus()) {
|
if (overriding_area && overriding_area->is_overriding_audio_bus() && !overriding_area->is_using_reverb_bus()) {
|
||||||
return overriding_area->get_audio_bus_name();
|
return overriding_area->get_audio_bus_name();
|
||||||
|
@ -347,7 +361,9 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() {
|
||||||
frame = AudioFrame(0, 0);
|
frame = AudioFrame(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
ERR_FAIL_COND_V(stream_playback.is_null(), output_volume_vector);
|
if (!active.is_set() || stream.is_null()) {
|
||||||
|
return output_volume_vector;
|
||||||
|
}
|
||||||
|
|
||||||
Vector3 linear_velocity;
|
Vector3 linear_velocity;
|
||||||
|
|
||||||
|
@ -422,7 +438,10 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioServer::get_singleton()->set_playback_highshelf_params(stream_playback, Math::db2linear(db_att), attenuation_filter_cutoff_hz);
|
linear_attenuation = Math::db2linear(db_att);
|
||||||
|
for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
|
||||||
|
AudioServer::get_singleton()->set_playback_highshelf_params(playback, linear_attenuation, attenuation_filter_cutoff_hz);
|
||||||
|
}
|
||||||
//TODO: The lower the second parameter (tightness) the more the sound will "enclose" the listener (more undirected / playing from
|
//TODO: The lower the second parameter (tightness) the more the sound will "enclose" the listener (more undirected / playing from
|
||||||
// speakers not facing the source) - this could be made distance dependent.
|
// speakers not facing the source) - this could be made distance dependent.
|
||||||
_calc_output_vol(local_pos.normalized(), 4.0, output_volume_vector);
|
_calc_output_vol(local_pos.normalized(), 4.0, output_volume_vector);
|
||||||
|
@ -447,7 +466,10 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() {
|
||||||
} else {
|
} else {
|
||||||
bus_volumes[bus] = output_volume_vector;
|
bus_volumes[bus] = output_volume_vector;
|
||||||
}
|
}
|
||||||
AudioServer::get_singleton()->set_playback_bus_volumes_linear(stream_playback, bus_volumes);
|
|
||||||
|
for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
|
||||||
|
AudioServer::get_singleton()->set_playback_bus_volumes_linear(playback, bus_volumes);
|
||||||
|
}
|
||||||
|
|
||||||
if (doppler_tracking != DOPPLER_TRACKING_DISABLED) {
|
if (doppler_tracking != DOPPLER_TRACKING_DISABLED) {
|
||||||
Vector3 listener_velocity;
|
Vector3 listener_velocity;
|
||||||
|
@ -458,9 +480,7 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() {
|
||||||
|
|
||||||
Vector3 local_velocity = listener_node->get_global_transform().orthonormalized().basis.xform_inv(linear_velocity - listener_velocity);
|
Vector3 local_velocity = listener_node->get_global_transform().orthonormalized().basis.xform_inv(linear_velocity - listener_velocity);
|
||||||
|
|
||||||
if (local_velocity == Vector3()) {
|
if (local_velocity != Vector3()) {
|
||||||
AudioServer::get_singleton()->set_playback_pitch_scale(stream_playback, pitch_scale);
|
|
||||||
} else {
|
|
||||||
float approaching = local_pos.normalized().dot(local_velocity.normalized());
|
float approaching = local_pos.normalized().dot(local_velocity.normalized());
|
||||||
float velocity = local_velocity.length();
|
float velocity = local_velocity.length();
|
||||||
float speed_of_sound = 343.0;
|
float speed_of_sound = 343.0;
|
||||||
|
@ -468,34 +488,23 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() {
|
||||||
float doppler_pitch_scale = pitch_scale * speed_of_sound / (speed_of_sound + velocity * approaching);
|
float doppler_pitch_scale = pitch_scale * speed_of_sound / (speed_of_sound + velocity * approaching);
|
||||||
doppler_pitch_scale = CLAMP(doppler_pitch_scale, (1 / 8.0), 8.0); //avoid crazy stuff
|
doppler_pitch_scale = CLAMP(doppler_pitch_scale, (1 / 8.0), 8.0); //avoid crazy stuff
|
||||||
|
|
||||||
AudioServer::get_singleton()->set_playback_pitch_scale(stream_playback, doppler_pitch_scale);
|
actual_pitch_scale = doppler_pitch_scale;
|
||||||
|
} else {
|
||||||
|
actual_pitch_scale = pitch_scale;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
AudioServer::get_singleton()->set_playback_pitch_scale(stream_playback, pitch_scale);
|
actual_pitch_scale = pitch_scale;
|
||||||
|
}
|
||||||
|
for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
|
||||||
|
AudioServer::get_singleton()->set_playback_pitch_scale(playback, actual_pitch_scale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return output_volume_vector;
|
return output_volume_vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioStreamPlayer3D::set_stream(Ref<AudioStream> p_stream) {
|
void AudioStreamPlayer3D::set_stream(Ref<AudioStream> p_stream) {
|
||||||
if (stream_playback.is_valid()) {
|
stop();
|
||||||
stop();
|
stream = p_stream;
|
||||||
stream_playback.unref();
|
|
||||||
stream.unref();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p_stream.is_valid()) {
|
|
||||||
stream_playback = p_stream->instance_playback();
|
|
||||||
if (stream_playback.is_valid()) {
|
|
||||||
stream = p_stream;
|
|
||||||
} else {
|
|
||||||
stream.unref();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p_stream.is_valid() && stream_playback.is_null()) {
|
|
||||||
stream.unref();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<AudioStream> AudioStreamPlayer3D::get_stream() const {
|
Ref<AudioStream> AudioStreamPlayer3D::get_stream() const {
|
||||||
|
@ -536,40 +545,47 @@ float AudioStreamPlayer3D::get_pitch_scale() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioStreamPlayer3D::play(float p_from_pos) {
|
void AudioStreamPlayer3D::play(float p_from_pos) {
|
||||||
if (stream_playback.is_valid()) {
|
if (stream.is_null()) {
|
||||||
setplay.set(p_from_pos);
|
return;
|
||||||
set_physics_process_internal(true);
|
|
||||||
}
|
}
|
||||||
|
ERR_FAIL_COND_MSG(!is_inside_tree(), "Playback can only happen when a node is inside the scene tree");
|
||||||
|
if (stream->is_monophonic() && is_playing()) {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
setplay.set(p_from_pos);
|
||||||
|
active.set();
|
||||||
|
set_physics_process_internal(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioStreamPlayer3D::seek(float p_seconds) {
|
void AudioStreamPlayer3D::seek(float p_seconds) {
|
||||||
if (stream_playback.is_valid() && active.is_set()) {
|
stop();
|
||||||
play(p_seconds);
|
play(p_seconds);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioStreamPlayer3D::stop() {
|
void AudioStreamPlayer3D::stop() {
|
||||||
if (stream_playback.is_valid()) {
|
setplay.set(-1);
|
||||||
active.clear();
|
for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
|
||||||
AudioServer::get_singleton()->stop_playback_stream(stream_playback);
|
AudioServer::get_singleton()->stop_playback_stream(playback);
|
||||||
set_physics_process_internal(false);
|
|
||||||
setplay.set(-1);
|
|
||||||
}
|
}
|
||||||
|
stream_playbacks.clear();
|
||||||
|
active.clear();
|
||||||
|
set_physics_process_internal(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioStreamPlayer3D::is_playing() const {
|
bool AudioStreamPlayer3D::is_playing() const {
|
||||||
if (stream_playback.is_valid()) {
|
for (const Ref<AudioStreamPlayback> &playback : stream_playbacks) {
|
||||||
return active.is_set() || setplay.get() >= 0;
|
if (AudioServer::get_singleton()->is_playback_active(playback)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
float AudioStreamPlayer3D::get_playback_position() {
|
float AudioStreamPlayer3D::get_playback_position() {
|
||||||
if (stream_playback.is_valid()) {
|
// Return the playback position of the most recently started playback stream.
|
||||||
return AudioServer::get_singleton()->get_playback_position(stream_playback);
|
if (!stream_playbacks.is_empty()) {
|
||||||
|
return AudioServer::get_singleton()->get_playback_position(stream_playbacks[stream_playbacks.size() - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -729,20 +745,35 @@ AudioStreamPlayer3D::DopplerTracking AudioStreamPlayer3D::get_doppler_tracking()
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioStreamPlayer3D::set_stream_paused(bool p_pause) {
|
void AudioStreamPlayer3D::set_stream_paused(bool p_pause) {
|
||||||
if (stream_playback.is_valid()) {
|
// TODO this does not have perfect recall, fix that maybe? If there are zero playbacks registered with the AudioServer, this bool isn't persisted.
|
||||||
AudioServer::get_singleton()->set_playback_paused(stream_playback, p_pause);
|
for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
|
||||||
|
AudioServer::get_singleton()->set_playback_paused(playback, p_pause);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioStreamPlayer3D::get_stream_paused() const {
|
bool AudioStreamPlayer3D::get_stream_paused() const {
|
||||||
if (stream_playback.is_valid()) {
|
// There's currently no way to pause some playback streams but not others. Check the first and don't bother looking at the rest.
|
||||||
return AudioServer::get_singleton()->is_playback_paused(stream_playback);
|
if (!stream_playbacks.is_empty()) {
|
||||||
|
return AudioServer::get_singleton()->is_playback_paused(stream_playbacks[0]);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<AudioStreamPlayback> AudioStreamPlayer3D::get_stream_playback() {
|
Ref<AudioStreamPlayback> AudioStreamPlayer3D::get_stream_playback() {
|
||||||
return stream_playback;
|
if (!stream_playbacks.is_empty()) {
|
||||||
|
return stream_playbacks[stream_playbacks.size() - 1];
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioStreamPlayer3D::set_max_polyphony(int p_max_polyphony) {
|
||||||
|
if (p_max_polyphony > 0) {
|
||||||
|
max_polyphony = p_max_polyphony;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int AudioStreamPlayer3D::get_max_polyphony() const {
|
||||||
|
return max_polyphony;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioStreamPlayer3D::_bind_methods() {
|
void AudioStreamPlayer3D::_bind_methods() {
|
||||||
|
@ -810,6 +841,9 @@ void AudioStreamPlayer3D::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("set_stream_paused", "pause"), &AudioStreamPlayer3D::set_stream_paused);
|
ClassDB::bind_method(D_METHOD("set_stream_paused", "pause"), &AudioStreamPlayer3D::set_stream_paused);
|
||||||
ClassDB::bind_method(D_METHOD("get_stream_paused"), &AudioStreamPlayer3D::get_stream_paused);
|
ClassDB::bind_method(D_METHOD("get_stream_paused"), &AudioStreamPlayer3D::get_stream_paused);
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("set_max_polyphony", "max_polyphony"), &AudioStreamPlayer3D::set_max_polyphony);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_max_polyphony"), &AudioStreamPlayer3D::get_max_polyphony);
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer3D::get_stream_playback);
|
ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer3D::get_stream_playback);
|
||||||
|
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream");
|
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream");
|
||||||
|
@ -823,6 +857,7 @@ void AudioStreamPlayer3D::_bind_methods() {
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stream_paused", PROPERTY_HINT_NONE, ""), "set_stream_paused", "get_stream_paused");
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stream_paused", PROPERTY_HINT_NONE, ""), "set_stream_paused", "get_stream_paused");
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_distance", PROPERTY_HINT_RANGE, "0,4096,1,or_greater,exp"), "set_max_distance", "get_max_distance");
|
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_distance", PROPERTY_HINT_RANGE, "0,4096,1,or_greater,exp"), "set_max_distance", "get_max_distance");
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "out_of_range_mode", PROPERTY_HINT_ENUM, "Mix,Pause"), "set_out_of_range_mode", "get_out_of_range_mode");
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "out_of_range_mode", PROPERTY_HINT_ENUM, "Mix,Pause"), "set_out_of_range_mode", "get_out_of_range_mode");
|
||||||
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_polyphony", PROPERTY_HINT_NONE, ""), "set_max_polyphony", "get_max_polyphony");
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus");
|
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus");
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "area_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_area_mask", "get_area_mask");
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "area_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_area_mask", "get_area_mask");
|
||||||
ADD_GROUP("Emission Angle", "emission_angle");
|
ADD_GROUP("Emission Angle", "emission_angle");
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#ifndef AUDIO_STREAM_PLAYER_3D_H
|
#ifndef AUDIO_STREAM_PLAYER_3D_H
|
||||||
#define AUDIO_STREAM_PLAYER_3D_H
|
#define AUDIO_STREAM_PLAYER_3D_H
|
||||||
|
|
||||||
|
#include "core/os/mutex.h"
|
||||||
#include "scene/3d/area_3d.h"
|
#include "scene/3d/area_3d.h"
|
||||||
#include "scene/3d/node_3d.h"
|
#include "scene/3d/node_3d.h"
|
||||||
#include "scene/3d/velocity_tracker_3d.h"
|
#include "scene/3d/velocity_tracker_3d.h"
|
||||||
|
@ -68,10 +69,10 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ref<AudioStreamPlayback> stream_playback;
|
Vector<Ref<AudioStreamPlayback>> stream_playbacks;
|
||||||
Ref<AudioStream> stream;
|
Ref<AudioStream> stream;
|
||||||
|
|
||||||
SafeFlag active;
|
SafeFlag active{ false };
|
||||||
SafeNumeric<float> setplay{ -1.0 };
|
SafeNumeric<float> setplay{ -1.0 };
|
||||||
|
|
||||||
AttenuationModel attenuation_model = ATTENUATION_INVERSE_DISTANCE;
|
AttenuationModel attenuation_model = ATTENUATION_INVERSE_DISTANCE;
|
||||||
|
@ -79,8 +80,11 @@ private:
|
||||||
float unit_size = 10.0;
|
float unit_size = 10.0;
|
||||||
float max_db = 3.0;
|
float max_db = 3.0;
|
||||||
float pitch_scale = 1.0;
|
float pitch_scale = 1.0;
|
||||||
|
// Internally used to take doppler tracking into account.
|
||||||
|
float actual_pitch_scale = 1.0;
|
||||||
bool autoplay = false;
|
bool autoplay = false;
|
||||||
StringName bus = "Master";
|
StringName bus = SNAME("Master");
|
||||||
|
int max_polyphony = 1;
|
||||||
|
|
||||||
uint64_t last_mix_count = -1;
|
uint64_t last_mix_count = -1;
|
||||||
|
|
||||||
|
@ -106,6 +110,8 @@ private:
|
||||||
float attenuation_filter_cutoff_hz = 5000.0;
|
float attenuation_filter_cutoff_hz = 5000.0;
|
||||||
float attenuation_filter_db = -24.0;
|
float attenuation_filter_db = -24.0;
|
||||||
|
|
||||||
|
float linear_attenuation = 0;
|
||||||
|
|
||||||
float max_distance = 0.0;
|
float max_distance = 0.0;
|
||||||
|
|
||||||
Ref<VelocityTracker3D> velocity_tracker;
|
Ref<VelocityTracker3D> velocity_tracker;
|
||||||
|
@ -146,6 +152,9 @@ public:
|
||||||
void set_bus(const StringName &p_bus);
|
void set_bus(const StringName &p_bus);
|
||||||
StringName get_bus() const;
|
StringName get_bus() const;
|
||||||
|
|
||||||
|
void set_max_polyphony(int p_max_polyphony);
|
||||||
|
int get_max_polyphony() const;
|
||||||
|
|
||||||
void set_autoplay(bool p_enable);
|
void set_autoplay(bool p_enable);
|
||||||
bool is_autoplay_enabled();
|
bool is_autoplay_enabled();
|
||||||
|
|
||||||
|
|
|
@ -42,17 +42,29 @@ void AudioStreamPlayer::_notification(int p_what) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p_what == NOTIFICATION_INTERNAL_PROCESS) {
|
if (p_what == NOTIFICATION_INTERNAL_PROCESS) {
|
||||||
if (stream_playback.is_valid() && active.is_set() && !AudioServer::get_singleton()->is_playback_active(stream_playback)) {
|
Vector<Ref<AudioStreamPlayback>> playbacks_to_remove;
|
||||||
|
for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
|
||||||
|
if (playback.is_valid() && !AudioServer::get_singleton()->is_playback_active(playback) && !AudioServer::get_singleton()->is_playback_paused(playback)) {
|
||||||
|
emit_signal(SNAME("finished"));
|
||||||
|
playbacks_to_remove.push_back(playback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Now go through and remove playbacks that have finished. Removing elements from a Vector in a range based for is asking for trouble.
|
||||||
|
for (Ref<AudioStreamPlayback> &playback : playbacks_to_remove) {
|
||||||
|
stream_playbacks.erase(playback);
|
||||||
|
}
|
||||||
|
if (!playbacks_to_remove.is_empty() && stream_playbacks.is_empty()) {
|
||||||
|
// This node is no longer actively playing audio.
|
||||||
active.clear();
|
active.clear();
|
||||||
set_process_internal(false);
|
set_process_internal(false);
|
||||||
emit_signal(SNAME("finished"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p_what == NOTIFICATION_EXIT_TREE) {
|
if (p_what == NOTIFICATION_EXIT_TREE) {
|
||||||
if (stream_playback.is_valid()) {
|
for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
|
||||||
AudioServer::get_singleton()->stop_playback_stream(stream_playback);
|
AudioServer::get_singleton()->stop_playback_stream(playback);
|
||||||
}
|
}
|
||||||
|
stream_playbacks.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p_what == NOTIFICATION_PAUSED) {
|
if (p_what == NOTIFICATION_PAUSED) {
|
||||||
|
@ -68,24 +80,8 @@ void AudioStreamPlayer::_notification(int p_what) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioStreamPlayer::set_stream(Ref<AudioStream> p_stream) {
|
void AudioStreamPlayer::set_stream(Ref<AudioStream> p_stream) {
|
||||||
if (stream_playback.is_valid()) {
|
stop();
|
||||||
stop();
|
stream = p_stream;
|
||||||
stream_playback.unref();
|
|
||||||
stream.unref();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p_stream.is_valid()) {
|
|
||||||
stream_playback = p_stream->instance_playback();
|
|
||||||
if (stream_playback.is_valid()) {
|
|
||||||
stream = p_stream;
|
|
||||||
} else {
|
|
||||||
stream.unref();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p_stream.is_valid() && stream_playback.is_null()) {
|
|
||||||
stream.unref();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<AudioStream> AudioStreamPlayer::get_stream() const {
|
Ref<AudioStream> AudioStreamPlayer::get_stream() const {
|
||||||
|
@ -95,7 +91,10 @@ Ref<AudioStream> AudioStreamPlayer::get_stream() const {
|
||||||
void AudioStreamPlayer::set_volume_db(float p_volume) {
|
void AudioStreamPlayer::set_volume_db(float p_volume) {
|
||||||
volume_db = p_volume;
|
volume_db = p_volume;
|
||||||
|
|
||||||
AudioServer::get_singleton()->set_playback_all_bus_volumes_linear(stream_playback, _get_volume_vector());
|
Vector<AudioFrame> volume_vector = _get_volume_vector();
|
||||||
|
for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
|
||||||
|
AudioServer::get_singleton()->set_playback_all_bus_volumes_linear(playback, volume_vector);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float AudioStreamPlayer::get_volume_db() const {
|
float AudioStreamPlayer::get_volume_db() const {
|
||||||
|
@ -106,57 +105,83 @@ void AudioStreamPlayer::set_pitch_scale(float p_pitch_scale) {
|
||||||
ERR_FAIL_COND(p_pitch_scale <= 0.0);
|
ERR_FAIL_COND(p_pitch_scale <= 0.0);
|
||||||
pitch_scale = p_pitch_scale;
|
pitch_scale = p_pitch_scale;
|
||||||
|
|
||||||
AudioServer::get_singleton()->set_playback_pitch_scale(stream_playback, pitch_scale);
|
for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
|
||||||
|
AudioServer::get_singleton()->set_playback_pitch_scale(playback, pitch_scale);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float AudioStreamPlayer::get_pitch_scale() const {
|
float AudioStreamPlayer::get_pitch_scale() const {
|
||||||
return pitch_scale;
|
return pitch_scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioStreamPlayer::play(float p_from_pos) {
|
void AudioStreamPlayer::set_max_polyphony(int p_max_polyphony) {
|
||||||
stop();
|
if (p_max_polyphony > 0) {
|
||||||
if (stream.is_valid()) {
|
max_polyphony = p_max_polyphony;
|
||||||
stream_playback = stream->instance_playback();
|
|
||||||
}
|
}
|
||||||
if (stream_playback.is_valid()) {
|
}
|
||||||
AudioServer::get_singleton()->start_playback_stream(stream_playback, bus, _get_volume_vector(), p_from_pos);
|
|
||||||
active.set();
|
int AudioStreamPlayer::get_max_polyphony() const {
|
||||||
|
return max_polyphony;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioStreamPlayer::play(float p_from_pos) {
|
||||||
|
if (stream.is_null()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ERR_FAIL_COND_MSG(!is_inside_tree(), "Playback can only happen when a node is inside the scene tree");
|
||||||
|
if (stream->is_monophonic() && is_playing()) {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
Ref<AudioStreamPlayback> stream_playback = stream->instance_playback();
|
||||||
|
ERR_FAIL_COND_MSG(stream_playback.is_null(), "Failed to instantiate playback.");
|
||||||
|
|
||||||
|
AudioServer::get_singleton()->start_playback_stream(stream_playback, bus, _get_volume_vector(), p_from_pos);
|
||||||
|
stream_playbacks.push_back(stream_playback);
|
||||||
|
active.set();
|
||||||
|
set_process_internal(true);
|
||||||
|
while (stream_playbacks.size() > max_polyphony) {
|
||||||
|
AudioServer::get_singleton()->stop_playback_stream(stream_playbacks[0]);
|
||||||
|
stream_playbacks.remove(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioStreamPlayer::seek(float p_seconds) {
|
void AudioStreamPlayer::seek(float p_seconds) {
|
||||||
if (stream_playback.is_valid() && active.is_set()) {
|
if (is_playing()) {
|
||||||
|
stop();
|
||||||
play(p_seconds);
|
play(p_seconds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioStreamPlayer::stop() {
|
void AudioStreamPlayer::stop() {
|
||||||
if (stream_playback.is_valid()) {
|
for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
|
||||||
active.clear();
|
AudioServer::get_singleton()->stop_playback_stream(playback);
|
||||||
AudioServer::get_singleton()->stop_playback_stream(stream_playback);
|
|
||||||
}
|
}
|
||||||
|
stream_playbacks.clear();
|
||||||
|
active.clear();
|
||||||
|
set_process_internal(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioStreamPlayer::is_playing() const {
|
bool AudioStreamPlayer::is_playing() const {
|
||||||
if (stream_playback.is_valid()) {
|
for (const Ref<AudioStreamPlayback> &playback : stream_playbacks) {
|
||||||
return AudioServer::get_singleton()->is_playback_active(stream_playback);
|
if (AudioServer::get_singleton()->is_playback_active(playback)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
float AudioStreamPlayer::get_playback_position() {
|
float AudioStreamPlayer::get_playback_position() {
|
||||||
if (stream_playback.is_valid()) {
|
// Return the playback position of the most recently started playback stream.
|
||||||
return AudioServer::get_singleton()->get_playback_position(stream_playback);
|
if (!stream_playbacks.is_empty()) {
|
||||||
|
return AudioServer::get_singleton()->get_playback_position(stream_playbacks[stream_playbacks.size() - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioStreamPlayer::set_bus(const StringName &p_bus) {
|
void AudioStreamPlayer::set_bus(const StringName &p_bus) {
|
||||||
bus = p_bus;
|
bus = p_bus;
|
||||||
if (stream_playback.is_valid()) {
|
for (const Ref<AudioStreamPlayback> &playback : stream_playbacks) {
|
||||||
AudioServer::get_singleton()->set_playback_bus_exclusive(stream_playback, p_bus, _get_volume_vector());
|
AudioServer::get_singleton()->set_playback_bus_exclusive(playback, p_bus, _get_volume_vector());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,7 +191,7 @@ StringName AudioStreamPlayer::get_bus() const {
|
||||||
return bus;
|
return bus;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "Master";
|
return SNAME("Master");
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioStreamPlayer::set_autoplay(bool p_enable) {
|
void AudioStreamPlayer::set_autoplay(bool p_enable) {
|
||||||
|
@ -194,22 +219,25 @@ void AudioStreamPlayer::_set_playing(bool p_enable) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioStreamPlayer::_is_active() const {
|
bool AudioStreamPlayer::_is_active() const {
|
||||||
if (stream_playback.is_valid()) {
|
for (const Ref<AudioStreamPlayback> &playback : stream_playbacks) {
|
||||||
return AudioServer::get_singleton()->is_playback_active(stream_playback);
|
if (AudioServer::get_singleton()->is_playback_active(playback)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioStreamPlayer::set_stream_paused(bool p_pause) {
|
void AudioStreamPlayer::set_stream_paused(bool p_pause) {
|
||||||
// TODO this does not have perfect recall, fix that maybe? If the stream isn't set, we can't persist this bool.
|
// TODO this does not have perfect recall, fix that maybe? If there are zero playbacks registered with the AudioServer, this bool isn't persisted.
|
||||||
if (stream_playback.is_valid()) {
|
for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
|
||||||
AudioServer::get_singleton()->set_playback_paused(stream_playback, p_pause);
|
AudioServer::get_singleton()->set_playback_paused(playback, p_pause);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioStreamPlayer::get_stream_paused() const {
|
bool AudioStreamPlayer::get_stream_paused() const {
|
||||||
if (stream_playback.is_valid()) {
|
// There's currently no way to pause some playback streams but not others. Check the first and don't bother looking at the rest.
|
||||||
return AudioServer::get_singleton()->is_playback_paused(stream_playback);
|
if (!stream_playbacks.is_empty()) {
|
||||||
|
return AudioServer::get_singleton()->is_playback_paused(stream_playbacks[0]);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -271,7 +299,10 @@ void AudioStreamPlayer::_bus_layout_changed() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<AudioStreamPlayback> AudioStreamPlayer::get_stream_playback() {
|
Ref<AudioStreamPlayback> AudioStreamPlayer::get_stream_playback() {
|
||||||
return stream_playback;
|
if (!stream_playbacks.is_empty()) {
|
||||||
|
return stream_playbacks[stream_playbacks.size() - 1];
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioStreamPlayer::_bind_methods() {
|
void AudioStreamPlayer::_bind_methods() {
|
||||||
|
@ -306,6 +337,9 @@ void AudioStreamPlayer::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("set_stream_paused", "pause"), &AudioStreamPlayer::set_stream_paused);
|
ClassDB::bind_method(D_METHOD("set_stream_paused", "pause"), &AudioStreamPlayer::set_stream_paused);
|
||||||
ClassDB::bind_method(D_METHOD("get_stream_paused"), &AudioStreamPlayer::get_stream_paused);
|
ClassDB::bind_method(D_METHOD("get_stream_paused"), &AudioStreamPlayer::get_stream_paused);
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("set_max_polyphony", "max_polyphony"), &AudioStreamPlayer::set_max_polyphony);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_max_polyphony"), &AudioStreamPlayer::get_max_polyphony);
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer::get_stream_playback);
|
ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer::get_stream_playback);
|
||||||
|
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream");
|
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream");
|
||||||
|
@ -315,6 +349,7 @@ void AudioStreamPlayer::_bind_methods() {
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled");
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled");
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stream_paused", PROPERTY_HINT_NONE, ""), "set_stream_paused", "get_stream_paused");
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stream_paused", PROPERTY_HINT_NONE, ""), "set_stream_paused", "get_stream_paused");
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "mix_target", PROPERTY_HINT_ENUM, "Stereo,Surround,Center"), "set_mix_target", "get_mix_target");
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "mix_target", PROPERTY_HINT_ENUM, "Stereo,Surround,Center"), "set_mix_target", "get_mix_target");
|
||||||
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_polyphony", PROPERTY_HINT_NONE, ""), "set_max_polyphony", "get_max_polyphony");
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus");
|
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus");
|
||||||
|
|
||||||
ADD_SIGNAL(MethodInfo("finished"));
|
ADD_SIGNAL(MethodInfo("finished"));
|
||||||
|
|
|
@ -46,7 +46,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ref<AudioStreamPlayback> stream_playback;
|
Vector<Ref<AudioStreamPlayback>> stream_playbacks;
|
||||||
Ref<AudioStream> stream;
|
Ref<AudioStream> stream;
|
||||||
|
|
||||||
SafeFlag active;
|
SafeFlag active;
|
||||||
|
@ -54,7 +54,8 @@ private:
|
||||||
float pitch_scale = 1.0;
|
float pitch_scale = 1.0;
|
||||||
float volume_db = 0.0;
|
float volume_db = 0.0;
|
||||||
bool autoplay = false;
|
bool autoplay = false;
|
||||||
StringName bus = "Master";
|
StringName bus = SNAME("Master");
|
||||||
|
int max_polyphony = 1;
|
||||||
|
|
||||||
MixTarget mix_target = MIX_TARGET_STEREO;
|
MixTarget mix_target = MIX_TARGET_STEREO;
|
||||||
|
|
||||||
|
@ -85,6 +86,9 @@ public:
|
||||||
void set_pitch_scale(float p_pitch_scale);
|
void set_pitch_scale(float p_pitch_scale);
|
||||||
float get_pitch_scale() const;
|
float get_pitch_scale() const;
|
||||||
|
|
||||||
|
void set_max_polyphony(int p_max_polyphony);
|
||||||
|
int get_max_polyphony() const;
|
||||||
|
|
||||||
void play(float p_from_pos = 0.0);
|
void play(float p_from_pos = 0.0);
|
||||||
void seek(float p_seconds);
|
void seek(float p_seconds);
|
||||||
void stop();
|
void stop();
|
||||||
|
|
|
@ -480,6 +480,10 @@ float AudioStreamSample::get_length() const {
|
||||||
return float(len) / mix_rate;
|
return float(len) / mix_rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AudioStreamSample::is_monophonic() const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void AudioStreamSample::set_data(const Vector<uint8_t> &p_data) {
|
void AudioStreamSample::set_data(const Vector<uint8_t> &p_data) {
|
||||||
AudioServer::get_singleton()->lock();
|
AudioServer::get_singleton()->lock();
|
||||||
if (data) {
|
if (data) {
|
||||||
|
|
|
@ -136,6 +136,8 @@ public:
|
||||||
|
|
||||||
virtual float get_length() const override; //if supported, otherwise return 0
|
virtual float get_length() const override; //if supported, otherwise return 0
|
||||||
|
|
||||||
|
virtual bool is_monophonic() const override;
|
||||||
|
|
||||||
void set_data(const Vector<uint8_t> &p_data);
|
void set_data(const Vector<uint8_t> &p_data);
|
||||||
Vector<uint8_t> get_data() const;
|
Vector<uint8_t> get_data() const;
|
||||||
|
|
||||||
|
|
|
@ -189,11 +189,21 @@ float AudioStream::get_length() const {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AudioStream::is_monophonic() const {
|
||||||
|
bool ret;
|
||||||
|
if (GDVIRTUAL_CALL(_is_monophonic, ret)) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
GDVIRTUAL_BIND(_instance_playback);
|
GDVIRTUAL_BIND(_instance_playback);
|
||||||
GDVIRTUAL_BIND(_get_stream_name);
|
GDVIRTUAL_BIND(_get_stream_name);
|
||||||
GDVIRTUAL_BIND(_get_length);
|
GDVIRTUAL_BIND(_get_length);
|
||||||
|
GDVIRTUAL_BIND(_is_monophonic);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
|
@ -221,6 +231,10 @@ float AudioStreamMicrophone::get_length() const {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AudioStreamMicrophone::is_monophonic() const {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void AudioStreamMicrophone::_bind_methods() {
|
void AudioStreamMicrophone::_bind_methods() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,6 +402,14 @@ float AudioStreamRandomPitch::get_length() const {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AudioStreamRandomPitch::is_monophonic() const {
|
||||||
|
if (audio_stream.is_valid()) {
|
||||||
|
return audio_stream->is_monophonic();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; // It doesn't really matter what we return here, but no sense instancing a many playbacks of a null stream.
|
||||||
|
}
|
||||||
|
|
||||||
void AudioStreamRandomPitch::_bind_methods() {
|
void AudioStreamRandomPitch::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("set_audio_stream", "stream"), &AudioStreamRandomPitch::set_audio_stream);
|
ClassDB::bind_method(D_METHOD("set_audio_stream", "stream"), &AudioStreamRandomPitch::set_audio_stream);
|
||||||
ClassDB::bind_method(D_METHOD("get_audio_stream"), &AudioStreamRandomPitch::get_audio_stream);
|
ClassDB::bind_method(D_METHOD("get_audio_stream"), &AudioStreamRandomPitch::get_audio_stream);
|
||||||
|
|
|
@ -102,12 +102,14 @@ protected:
|
||||||
GDVIRTUAL0RC(Ref<AudioStreamPlayback>, _instance_playback)
|
GDVIRTUAL0RC(Ref<AudioStreamPlayback>, _instance_playback)
|
||||||
GDVIRTUAL0RC(String, _get_stream_name)
|
GDVIRTUAL0RC(String, _get_stream_name)
|
||||||
GDVIRTUAL0RC(float, _get_length)
|
GDVIRTUAL0RC(float, _get_length)
|
||||||
|
GDVIRTUAL0RC(bool, _is_monophonic)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual Ref<AudioStreamPlayback> instance_playback();
|
virtual Ref<AudioStreamPlayback> instance_playback();
|
||||||
virtual String get_stream_name() const;
|
virtual String get_stream_name() const;
|
||||||
|
|
||||||
virtual float get_length() const;
|
virtual float get_length() const;
|
||||||
|
virtual bool is_monophonic() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Microphone
|
// Microphone
|
||||||
|
@ -129,6 +131,8 @@ public:
|
||||||
|
|
||||||
virtual float get_length() const override; //if supported, otherwise return 0
|
virtual float get_length() const override; //if supported, otherwise return 0
|
||||||
|
|
||||||
|
virtual bool is_monophonic() const override;
|
||||||
|
|
||||||
AudioStreamMicrophone();
|
AudioStreamMicrophone();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -187,6 +191,7 @@ public:
|
||||||
virtual String get_stream_name() const override;
|
virtual String get_stream_name() const override;
|
||||||
|
|
||||||
virtual float get_length() const override; //if supported, otherwise return 0
|
virtual float get_length() const override; //if supported, otherwise return 0
|
||||||
|
virtual bool is_monophonic() const override;
|
||||||
|
|
||||||
AudioStreamRandomPitch();
|
AudioStreamRandomPitch();
|
||||||
};
|
};
|
||||||
|
|
|
@ -64,6 +64,10 @@ float AudioStreamGenerator::get_length() const {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AudioStreamGenerator::is_monophonic() const {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void AudioStreamGenerator::_bind_methods() {
|
void AudioStreamGenerator::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("set_mix_rate", "hz"), &AudioStreamGenerator::set_mix_rate);
|
ClassDB::bind_method(D_METHOD("set_mix_rate", "hz"), &AudioStreamGenerator::set_mix_rate);
|
||||||
ClassDB::bind_method(D_METHOD("get_mix_rate"), &AudioStreamGenerator::get_mix_rate);
|
ClassDB::bind_method(D_METHOD("get_mix_rate"), &AudioStreamGenerator::get_mix_rate);
|
||||||
|
|
|
@ -54,6 +54,7 @@ public:
|
||||||
virtual String get_stream_name() const override;
|
virtual String get_stream_name() const override;
|
||||||
|
|
||||||
virtual float get_length() const override;
|
virtual float get_length() const override;
|
||||||
|
virtual bool is_monophonic() const override;
|
||||||
AudioStreamGenerator();
|
AudioStreamGenerator();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -363,10 +363,10 @@ void AudioServer::_mix_step() {
|
||||||
if (mixed_frames != buffer_size) {
|
if (mixed_frames != buffer_size) {
|
||||||
// We know we have at least the size of our lookahead buffer for fade-out purposes.
|
// We know we have at least the size of our lookahead buffer for fade-out purposes.
|
||||||
|
|
||||||
float fadeout_base = 0.87;
|
float fadeout_base = 0.94;
|
||||||
float fadeout_coefficient = 1;
|
float fadeout_coefficient = 1;
|
||||||
static_assert(LOOKAHEAD_BUFFER_SIZE == 32, "Update fadeout_base and comment here if you change LOOKAHEAD_BUFFER_SIZE.");
|
static_assert(LOOKAHEAD_BUFFER_SIZE == 64, "Update fadeout_base and comment here if you change LOOKAHEAD_BUFFER_SIZE.");
|
||||||
// 0.87 ^ 32 = 0.0116. There might still be a pop but it'll be way better than if we didn't do this.
|
// 0.94 ^ 64 = 0.01906. There might still be a pop but it'll be way better than if we didn't do this.
|
||||||
for (unsigned int idx = mixed_frames; idx < buffer_size; idx++) {
|
for (unsigned int idx = mixed_frames; idx < buffer_size; idx++) {
|
||||||
fadeout_coefficient *= fadeout_base;
|
fadeout_coefficient *= fadeout_base;
|
||||||
buf[idx] *= fadeout_coefficient;
|
buf[idx] *= fadeout_coefficient;
|
||||||
|
@ -381,29 +381,24 @@ void AudioServer::_mix_step() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ERR_FAIL_COND(playback->bus_details.load() == nullptr);
|
AudioStreamPlaybackBusDetails *ptr = playback->bus_details.load();
|
||||||
|
ERR_FAIL_COND(ptr == nullptr);
|
||||||
// By putting null into the bus details pointers, we're taking ownership of their memory for the duration of this mix.
|
// By putting null into the bus details pointers, we're taking ownership of their memory for the duration of this mix.
|
||||||
AudioStreamPlaybackBusDetails *bus_details = nullptr;
|
AudioStreamPlaybackBusDetails bus_details = *ptr;
|
||||||
{
|
|
||||||
std::atomic<AudioStreamPlaybackBusDetails *> bus_details_atomic = nullptr;
|
|
||||||
bus_details = playback->bus_details.exchange(bus_details_atomic);
|
|
||||||
}
|
|
||||||
ERR_FAIL_COND(bus_details == nullptr);
|
|
||||||
AudioStreamPlaybackBusDetails *prev_bus_details = playback->prev_bus_details;
|
|
||||||
|
|
||||||
// Mix to any active buses.
|
// Mix to any active buses.
|
||||||
for (int idx = 0; idx < MAX_BUSES_PER_PLAYBACK; idx++) {
|
for (int idx = 0; idx < MAX_BUSES_PER_PLAYBACK; idx++) {
|
||||||
if (!bus_details->bus_active[idx]) {
|
if (!bus_details.bus_active[idx]) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int bus_idx = thread_find_bus_index(bus_details->bus[idx]);
|
int bus_idx = thread_find_bus_index(bus_details.bus[idx]);
|
||||||
|
|
||||||
int prev_bus_idx = -1;
|
int prev_bus_idx = -1;
|
||||||
for (int search_idx = 0; search_idx < MAX_BUSES_PER_PLAYBACK; search_idx++) {
|
for (int search_idx = 0; search_idx < MAX_BUSES_PER_PLAYBACK; search_idx++) {
|
||||||
if (!prev_bus_details->bus_active[search_idx]) {
|
if (!playback->prev_bus_details->bus_active[search_idx]) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (prev_bus_details->bus[search_idx].hash() == bus_details->bus[idx].hash()) {
|
if (playback->prev_bus_details->bus[search_idx].hash() == bus_details.bus[idx].hash()) {
|
||||||
prev_bus_idx = search_idx;
|
prev_bus_idx = search_idx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -411,13 +406,13 @@ void AudioServer::_mix_step() {
|
||||||
for (int channel_idx = 0; channel_idx < channel_count; channel_idx++) {
|
for (int channel_idx = 0; channel_idx < channel_count; channel_idx++) {
|
||||||
AudioFrame *channel_buf = thread_get_channel_mix_buffer(bus_idx, channel_idx);
|
AudioFrame *channel_buf = thread_get_channel_mix_buffer(bus_idx, channel_idx);
|
||||||
if (fading_out) {
|
if (fading_out) {
|
||||||
bus_details->volume[idx][channel_idx] = AudioFrame(0, 0);
|
bus_details.volume[idx][channel_idx] = AudioFrame(0, 0);
|
||||||
}
|
}
|
||||||
AudioFrame channel_vol = bus_details->volume[idx][channel_idx];
|
AudioFrame channel_vol = bus_details.volume[idx][channel_idx];
|
||||||
|
|
||||||
AudioFrame prev_channel_vol = AudioFrame(0, 0);
|
AudioFrame prev_channel_vol = AudioFrame(0, 0);
|
||||||
if (prev_bus_idx != -1) {
|
if (prev_bus_idx != -1) {
|
||||||
prev_channel_vol = prev_bus_details->volume[prev_bus_idx][channel_idx];
|
prev_channel_vol = playback->prev_bus_details->volume[prev_bus_idx][channel_idx];
|
||||||
}
|
}
|
||||||
_mix_step_for_channel(channel_buf, buf, prev_channel_vol, channel_vol, playback->attenuation_filter_cutoff_hz.get(), playback->highshelf_gain.get(), &playback->filter_process[channel_idx * 2], &playback->filter_process[channel_idx * 2 + 1]);
|
_mix_step_for_channel(channel_buf, buf, prev_channel_vol, channel_vol, playback->attenuation_filter_cutoff_hz.get(), playback->highshelf_gain.get(), &playback->filter_process[channel_idx * 2], &playback->filter_process[channel_idx * 2 + 1]);
|
||||||
}
|
}
|
||||||
|
@ -425,14 +420,14 @@ void AudioServer::_mix_step() {
|
||||||
|
|
||||||
// Now go through and fade-out any buses that were being played to previously that we missed by going through current data.
|
// Now go through and fade-out any buses that were being played to previously that we missed by going through current data.
|
||||||
for (int idx = 0; idx < MAX_BUSES_PER_PLAYBACK; idx++) {
|
for (int idx = 0; idx < MAX_BUSES_PER_PLAYBACK; idx++) {
|
||||||
if (!prev_bus_details->bus_active[idx]) {
|
if (!playback->prev_bus_details->bus_active[idx]) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int bus_idx = thread_find_bus_index(prev_bus_details->bus[idx]);
|
int bus_idx = thread_find_bus_index(playback->prev_bus_details->bus[idx]);
|
||||||
|
|
||||||
int current_bus_idx = -1;
|
int current_bus_idx = -1;
|
||||||
for (int search_idx = 0; search_idx < MAX_BUSES_PER_PLAYBACK; search_idx++) {
|
for (int search_idx = 0; search_idx < MAX_BUSES_PER_PLAYBACK; search_idx++) {
|
||||||
if (bus_details->bus[search_idx] == prev_bus_details->bus[idx]) {
|
if (bus_details.bus[search_idx] == playback->prev_bus_details->bus[idx]) {
|
||||||
current_bus_idx = search_idx;
|
current_bus_idx = search_idx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -443,24 +438,17 @@ void AudioServer::_mix_step() {
|
||||||
|
|
||||||
for (int channel_idx = 0; channel_idx < channel_count; channel_idx++) {
|
for (int channel_idx = 0; channel_idx < channel_count; channel_idx++) {
|
||||||
AudioFrame *channel_buf = thread_get_channel_mix_buffer(bus_idx, channel_idx);
|
AudioFrame *channel_buf = thread_get_channel_mix_buffer(bus_idx, channel_idx);
|
||||||
AudioFrame prev_channel_vol = prev_bus_details->volume[idx][channel_idx];
|
AudioFrame prev_channel_vol = playback->prev_bus_details->volume[idx][channel_idx];
|
||||||
// Fade out to silence
|
// Fade out to silence
|
||||||
_mix_step_for_channel(channel_buf, buf, prev_channel_vol, AudioFrame(0, 0), playback->attenuation_filter_cutoff_hz.get(), playback->highshelf_gain.get(), &playback->filter_process[channel_idx * 2], &playback->filter_process[channel_idx * 2 + 1]);
|
_mix_step_for_channel(channel_buf, buf, prev_channel_vol, AudioFrame(0, 0), playback->attenuation_filter_cutoff_hz.get(), playback->highshelf_gain.get(), &playback->filter_process[channel_idx * 2], &playback->filter_process[channel_idx * 2 + 1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the bus details we mixed with to the previous bus details to maintain volume ramps.
|
// Copy the bus details we mixed with to the previous bus details to maintain volume ramps.
|
||||||
std::copy(std::begin(bus_details->bus_active), std::end(bus_details->bus_active), std::begin(prev_bus_details->bus_active));
|
std::copy(std::begin(bus_details.bus_active), std::end(bus_details.bus_active), std::begin(playback->prev_bus_details->bus_active));
|
||||||
std::copy(std::begin(bus_details->bus), std::end(bus_details->bus), std::begin(prev_bus_details->bus));
|
std::copy(std::begin(bus_details.bus), std::end(bus_details.bus), std::begin(playback->prev_bus_details->bus));
|
||||||
for (int bus_idx = 0; bus_idx < MAX_BUSES_PER_PLAYBACK; bus_idx++) {
|
for (int bus_idx = 0; bus_idx < MAX_BUSES_PER_PLAYBACK; bus_idx++) {
|
||||||
std::copy(std::begin(bus_details->volume[bus_idx]), std::end(bus_details->volume[bus_idx]), std::begin(prev_bus_details->volume[bus_idx]));
|
std::copy(std::begin(bus_details.volume[bus_idx]), std::end(bus_details.volume[bus_idx]), std::begin(playback->prev_bus_details->volume[bus_idx]));
|
||||||
}
|
|
||||||
|
|
||||||
AudioStreamPlaybackBusDetails *bus_details_expected = nullptr;
|
|
||||||
// Only put the bus details pointer back if it hasn't been updated already.
|
|
||||||
if (!playback->bus_details.compare_exchange_strong(/* expected= */ bus_details_expected, /* new= */ bus_details)) {
|
|
||||||
// If it *has* been updated already, queue the old one for deletion.
|
|
||||||
bus_details_graveyard.insert(bus_details);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (playback->state.load()) {
|
switch (playback->state.load()) {
|
||||||
|
@ -1132,7 +1120,7 @@ void AudioServer::start_playback_stream(Ref<AudioStreamPlayback> p_playback, Str
|
||||||
start_playback_stream(p_playback, map, p_start_time);
|
start_playback_stream(p_playback, map, p_start_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioServer::start_playback_stream(Ref<AudioStreamPlayback> p_playback, Map<StringName, Vector<AudioFrame>> p_bus_volumes, float p_start_time) {
|
void AudioServer::start_playback_stream(Ref<AudioStreamPlayback> p_playback, Map<StringName, Vector<AudioFrame>> p_bus_volumes, float p_start_time, float p_highshelf_gain, float p_attenuation_cutoff_hz, float p_pitch_scale) {
|
||||||
ERR_FAIL_COND(p_playback.is_null());
|
ERR_FAIL_COND(p_playback.is_null());
|
||||||
|
|
||||||
AudioStreamPlaybackListNode *playback_node = new AudioStreamPlaybackListNode();
|
AudioStreamPlaybackListNode *playback_node = new AudioStreamPlaybackListNode();
|
||||||
|
@ -1154,10 +1142,9 @@ void AudioServer::start_playback_stream(Ref<AudioStreamPlayback> p_playback, Map
|
||||||
playback_node->bus_details = new_bus_details;
|
playback_node->bus_details = new_bus_details;
|
||||||
playback_node->prev_bus_details = new AudioStreamPlaybackBusDetails();
|
playback_node->prev_bus_details = new AudioStreamPlaybackBusDetails();
|
||||||
|
|
||||||
playback_node->setseek.set(-1);
|
playback_node->pitch_scale.set(p_pitch_scale);
|
||||||
playback_node->pitch_scale.set(1);
|
playback_node->highshelf_gain.set(p_highshelf_gain);
|
||||||
playback_node->highshelf_gain.set(0);
|
playback_node->attenuation_filter_cutoff_hz.set(p_attenuation_cutoff_hz);
|
||||||
playback_node->attenuation_filter_cutoff_hz.set(0);
|
|
||||||
|
|
||||||
memset(playback_node->prev_bus_details->volume, 0, sizeof(playback_node->prev_bus_details->volume));
|
memset(playback_node->prev_bus_details->volume, 0, sizeof(playback_node->prev_bus_details->volume));
|
||||||
|
|
||||||
|
@ -1181,6 +1168,9 @@ void AudioServer::stop_playback_stream(Ref<AudioStreamPlayback> p_playback) {
|
||||||
AudioStreamPlaybackListNode::PlaybackState new_state, old_state;
|
AudioStreamPlaybackListNode::PlaybackState new_state, old_state;
|
||||||
do {
|
do {
|
||||||
old_state = playback_node->state.load();
|
old_state = playback_node->state.load();
|
||||||
|
if (old_state == AudioStreamPlaybackListNode::AWAITING_DELETION) {
|
||||||
|
break; // Don't fade out again.
|
||||||
|
}
|
||||||
new_state = AudioStreamPlaybackListNode::FADE_OUT_TO_DELETION;
|
new_state = AudioStreamPlaybackListNode::FADE_OUT_TO_DELETION;
|
||||||
|
|
||||||
} while (!playback_node->state.compare_exchange_strong(old_state, new_state));
|
} while (!playback_node->state.compare_exchange_strong(old_state, new_state));
|
||||||
|
@ -1206,6 +1196,9 @@ void AudioServer::set_playback_bus_volumes_linear(Ref<AudioStreamPlayback> p_pla
|
||||||
|
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
for (KeyValue<StringName, Vector<AudioFrame>> pair : p_bus_volumes) {
|
for (KeyValue<StringName, Vector<AudioFrame>> pair : p_bus_volumes) {
|
||||||
|
if (idx >= MAX_BUSES_PER_PLAYBACK) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
ERR_FAIL_COND(pair.value.size() < channel_count);
|
ERR_FAIL_COND(pair.value.size() < channel_count);
|
||||||
ERR_FAIL_COND(pair.value.size() != MAX_CHANNELS_PER_BUS);
|
ERR_FAIL_COND(pair.value.size() != MAX_CHANNELS_PER_BUS);
|
||||||
|
|
||||||
|
@ -1214,6 +1207,7 @@ void AudioServer::set_playback_bus_volumes_linear(Ref<AudioStreamPlayback> p_pla
|
||||||
for (int channel_idx = 0; channel_idx < MAX_CHANNELS_PER_BUS; channel_idx++) {
|
for (int channel_idx = 0; channel_idx < MAX_CHANNELS_PER_BUS; channel_idx++) {
|
||||||
new_bus_details->volume[idx][channel_idx] = pair.value[channel_idx];
|
new_bus_details->volume[idx][channel_idx] = pair.value[channel_idx];
|
||||||
}
|
}
|
||||||
|
idx++;
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
@ -1260,17 +1254,18 @@ void AudioServer::set_playback_paused(Ref<AudioStreamPlayback> p_playback, bool
|
||||||
if (!playback_node) {
|
if (!playback_node) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!p_paused && playback_node->state == AudioStreamPlaybackListNode::PLAYING) {
|
|
||||||
return; // No-op.
|
|
||||||
}
|
|
||||||
if (p_paused && (playback_node->state == AudioStreamPlaybackListNode::PAUSED || playback_node->state == AudioStreamPlaybackListNode::FADE_OUT_TO_PAUSE)) {
|
|
||||||
return; // No-op.
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioStreamPlaybackListNode::PlaybackState new_state, old_state;
|
AudioStreamPlaybackListNode::PlaybackState new_state, old_state;
|
||||||
do {
|
do {
|
||||||
old_state = playback_node->state.load();
|
old_state = playback_node->state.load();
|
||||||
new_state = p_paused ? AudioStreamPlaybackListNode::FADE_OUT_TO_PAUSE : AudioStreamPlaybackListNode::PLAYING;
|
new_state = p_paused ? AudioStreamPlaybackListNode::FADE_OUT_TO_PAUSE : AudioStreamPlaybackListNode::PLAYING;
|
||||||
|
if (!p_paused && old_state == AudioStreamPlaybackListNode::PLAYING) {
|
||||||
|
return; // No-op.
|
||||||
|
}
|
||||||
|
if (p_paused && (old_state == AudioStreamPlaybackListNode::PAUSED || old_state == AudioStreamPlaybackListNode::FADE_OUT_TO_PAUSE)) {
|
||||||
|
return; // No-op.
|
||||||
|
}
|
||||||
|
|
||||||
} while (!playback_node->state.compare_exchange_strong(old_state, new_state));
|
} while (!playback_node->state.compare_exchange_strong(old_state, new_state));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1444,10 +1439,15 @@ void AudioServer::update() {
|
||||||
update_callback_list.maybe_cleanup();
|
update_callback_list.maybe_cleanup();
|
||||||
listener_changed_callback_list.maybe_cleanup();
|
listener_changed_callback_list.maybe_cleanup();
|
||||||
playback_list.maybe_cleanup();
|
playback_list.maybe_cleanup();
|
||||||
|
for (AudioStreamPlaybackBusDetails *bus_details : bus_details_graveyard_frame_old) {
|
||||||
|
bus_details_graveyard_frame_old.erase(bus_details, [](AudioStreamPlaybackBusDetails *d) { delete d; });
|
||||||
|
}
|
||||||
for (AudioStreamPlaybackBusDetails *bus_details : bus_details_graveyard) {
|
for (AudioStreamPlaybackBusDetails *bus_details : bus_details_graveyard) {
|
||||||
bus_details_graveyard.erase(bus_details, [](AudioStreamPlaybackBusDetails *d) { delete d; });
|
bus_details_graveyard_frame_old.insert(bus_details);
|
||||||
|
bus_details_graveyard.erase(bus_details);
|
||||||
}
|
}
|
||||||
bus_details_graveyard.maybe_cleanup();
|
bus_details_graveyard.maybe_cleanup();
|
||||||
|
bus_details_graveyard_frame_old.maybe_cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioServer::load_default_bus_layout() {
|
void AudioServer::load_default_bus_layout() {
|
||||||
|
|
|
@ -163,7 +163,7 @@ public:
|
||||||
AUDIO_DATA_INVALID_ID = -1,
|
AUDIO_DATA_INVALID_ID = -1,
|
||||||
MAX_CHANNELS_PER_BUS = 4,
|
MAX_CHANNELS_PER_BUS = 4,
|
||||||
MAX_BUSES_PER_PLAYBACK = 6,
|
MAX_BUSES_PER_PLAYBACK = 6,
|
||||||
LOOKAHEAD_BUFFER_SIZE = 32,
|
LOOKAHEAD_BUFFER_SIZE = 64,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef void (*AudioCallback)(void *p_userdata);
|
typedef void (*AudioCallback)(void *p_userdata);
|
||||||
|
@ -262,6 +262,9 @@ private:
|
||||||
SafeList<AudioStreamPlaybackListNode *> playback_list;
|
SafeList<AudioStreamPlaybackListNode *> playback_list;
|
||||||
SafeList<AudioStreamPlaybackBusDetails *> bus_details_graveyard;
|
SafeList<AudioStreamPlaybackBusDetails *> bus_details_graveyard;
|
||||||
|
|
||||||
|
// TODO document if this is necessary.
|
||||||
|
SafeList<AudioStreamPlaybackBusDetails *> bus_details_graveyard_frame_old;
|
||||||
|
|
||||||
Vector<Vector<AudioFrame>> temp_buffer; //temp_buffer for each level
|
Vector<Vector<AudioFrame>> temp_buffer; //temp_buffer for each level
|
||||||
Vector<AudioFrame> mix_buffer;
|
Vector<AudioFrame> mix_buffer;
|
||||||
Vector<Bus *> buses;
|
Vector<Bus *> buses;
|
||||||
|
@ -364,8 +367,10 @@ public:
|
||||||
void set_playback_speed_scale(float p_scale);
|
void set_playback_speed_scale(float p_scale);
|
||||||
float get_playback_speed_scale() const;
|
float get_playback_speed_scale() const;
|
||||||
|
|
||||||
|
// Convenience method.
|
||||||
void start_playback_stream(Ref<AudioStreamPlayback> p_playback, StringName p_bus, Vector<AudioFrame> p_volume_db_vector, float p_start_time = 0);
|
void start_playback_stream(Ref<AudioStreamPlayback> p_playback, StringName p_bus, Vector<AudioFrame> p_volume_db_vector, float p_start_time = 0);
|
||||||
void start_playback_stream(Ref<AudioStreamPlayback> p_playback, Map<StringName, Vector<AudioFrame>> p_bus_volumes, float p_start_time = 0);
|
// Expose all parameters.
|
||||||
|
void start_playback_stream(Ref<AudioStreamPlayback> p_playback, Map<StringName, Vector<AudioFrame>> p_bus_volumes, float p_start_time = 0, float p_highshelf_gain = 0, float p_attenuation_cutoff_hz = 0, float p_pitch_scale = 1);
|
||||||
void stop_playback_stream(Ref<AudioStreamPlayback> p_playback);
|
void stop_playback_stream(Ref<AudioStreamPlayback> p_playback);
|
||||||
|
|
||||||
void set_playback_bus_exclusive(Ref<AudioStreamPlayback> p_playback, StringName p_bus, Vector<AudioFrame> p_volumes);
|
void set_playback_bus_exclusive(Ref<AudioStreamPlayback> p_playback, StringName p_bus, Vector<AudioFrame> p_volumes);
|
||||||
|
|
Loading…
Reference in a new issue