Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add loop_mode to AudioStreamPlayer & AudioStreamPlayback #65797

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions doc/classes/AudioStreamPlayback.xml
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,24 @@
</description>
</method>
</methods>
<constants>
<constant name="LOOP_DETECT" value="0" enum="LoopMode">
If supported, looping is automatically detected depending on the associated [AudioStream].
</constant>
<constant name="LOOP_DISABLED" value="1" enum="LoopMode">
The [AudioStream] does not loop.
</constant>
<constant name="LOOP_FORWARD" value="2" enum="LoopMode">
If supported, the [AudioStream] loops when it reaches the end.
</constant>
<constant name="LOOP_PINGPONG" value="3" enum="LoopMode">
If supported, the [AudioStream] switches between playing forward and backwards, after reaching either sides.
</constant>
<constant name="LOOP_BACKWARD" value="4" enum="LoopMode">
If supported, the [AudioStream] plays backwards and loops when it reaches the beginning.
</constant>
<constant name="LOOP_MAX" value="5" enum="LoopMode">
Represents the size of the [enum LoopMode] enum.
</constant>
</constants>
</class>
4 changes: 4 additions & 0 deletions doc/classes/AudioStreamPlayer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@
Bus on which this audio is playing.
[b]Note:[/b] When setting this property, keep in mind that no validation is performed to see if the given name matches an existing bus. This is because audio bus layouts might be loaded after this property is set. If this given name can't be resolved at runtime, it will fall back to [code]"Master"[/code].
</member>
<member name="loop_mode" type="int" setter="set_loop_mode" getter="get_loop_mode" enum="AudioStreamPlayback.LoopMode" default="0">
The way the audio should loop. If set to [constant AudioStreamPlayback.LOOP_DETECT], it is inferred from the [member stream]. See [enum AudioStreamPlayback.LoopMode] for possible modes.
[b]Note:[/b] Not all [AudioStream]s support all looping modes.
</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>
Expand Down
4 changes: 4 additions & 0 deletions doc/classes/AudioStreamPlayer2D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@
Bus on which this audio is playing.
[b]Note:[/b] When setting this property, keep in mind that no validation is performed to see if the given name matches an existing bus. This is because audio bus layouts might be loaded after this property is set. If this given name can't be resolved at runtime, it will fall back to [code]"Master"[/code].
</member>
<member name="loop_mode" type="int" setter="set_loop_mode" getter="get_loop_mode" enum="AudioStreamPlayback.LoopMode" default="0">
The way the audio should loop. If set to [constant AudioStreamPlayback.LOOP_DETECT], it is inferred from the [member stream]. See [enum AudioStreamPlayback.LoopMode] for possible values.
[b]Note:[/b] Not all [AudioStream]s support all looping modes.
</member>
<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.
</member>
Expand Down
4 changes: 4 additions & 0 deletions doc/classes/AudioStreamPlayer3D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@
<member name="emission_angle_filter_attenuation_db" type="float" setter="set_emission_angle_filter_attenuation_db" getter="get_emission_angle_filter_attenuation_db" default="-12.0">
Dampens audio if camera is outside of [member emission_angle_degrees] and [member emission_angle_enabled] is set by this factor, in decibels.
</member>
<member name="loop_mode" type="int" setter="set_loop_mode" getter="get_loop_mode" enum="AudioStreamPlayback.LoopMode" default="0">
The way the audio should loop. If set to [constant AudioStreamPlayback.LOOP_DETECT], it is inferred from the [member stream]. See [enum AudioStreamPlayback.LoopMode] for possible values.
[b]Note:[/b] Not all [AudioStream]s support all looping modes.
</member>
<member name="max_db" type="float" setter="set_max_db" getter="get_max_db" default="3.0">
Sets the absolute maximum of the soundlevel, in decibels.
</member>
Expand Down
3 changes: 2 additions & 1 deletion editor/import/resource_importer_wav.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s
}
}

// Detect loop mode from file (default).
if (import_loop_mode == 0 && chunkID[0] == 's' && chunkID[1] == 'm' && chunkID[2] == 'p' && chunkID[3] == 'l') {
// Loop point info!

Expand Down Expand Up @@ -431,7 +432,7 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s
}

if (import_loop_mode >= 2) {
loop_mode = (AudioStreamWAV::LoopMode)(import_loop_mode - 1);
loop_mode = (AudioStreamWAV::LoopMode)(import_loop_mode - 1); // Not counting "Detect from WAV" option.
loop_begin = p_options["edit/loop_begin"];
loop_end = p_options["edit/loop_end"];
// Wrap around to max frames, so `-1` can be used to select the end, etc.
Expand Down
20 changes: 19 additions & 1 deletion modules/minimp3/audio_stream_mp3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ 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 beat_loop = loop_mode == LOOP_FORWARD && 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();
}
Expand Down Expand Up @@ -119,6 +119,17 @@ bool AudioStreamPlaybackMP3::is_playing() const {
return active;
}

void AudioStreamPlaybackMP3::set_loop_mode(const LoopMode p_loop_mode) {
if (p_loop_mode == LOOP_PINGPONG || p_loop_mode == LOOP_BACKWARD) {
WARN_PRINT("AudioStreamPlaybackMP3 does not support Ping-Pong or Backward Loop Mode! Looping forward.");
loop_mode = LOOP_FORWARD;
} else if (p_loop_mode == LOOP_DETECT) {
loop_mode = mp3_stream->has_loop() ? LOOP_FORWARD : LOOP_DISABLED;
} else {
loop_mode = p_loop_mode;
}
}

int AudioStreamPlaybackMP3::get_loop_count() const {
return loops;
}
Expand Down Expand Up @@ -229,6 +240,13 @@ float AudioStreamMP3::get_length() const {
return length;
}

Vector<AudioStreamPlayback::LoopMode> AudioStreamMP3::get_unsupported_loop_modes() const {
Vector<AudioStreamPlayback::LoopMode> unsupported_modes;
unsupported_modes.append(AudioStreamPlayback::LoopMode::LOOP_BACKWARD);
unsupported_modes.append(AudioStreamPlayback::LoopMode::LOOP_PINGPONG);
return unsupported_modes;
};

bool AudioStreamMP3::is_monophonic() const {
return false;
}
Expand Down
4 changes: 4 additions & 0 deletions modules/minimp3/audio_stream_mp3.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class AudioStreamPlaybackMP3 : public AudioStreamPlaybackResampled {
virtual void stop() override;
virtual bool is_playing() const override;

virtual void set_loop_mode(const LoopMode p_loop_mode) override;
virtual int get_loop_count() const override; //times it looped

virtual float get_playback_position() const override;
Expand Down Expand Up @@ -126,6 +127,9 @@ class AudioStreamMP3 : public AudioStream {

virtual bool is_monophonic() const override;

// MP3 does not support LOOP_BACKWARD and LOOP_PINGPONG.
virtual Vector<AudioStreamPlayback::LoopMode> get_unsupported_loop_modes() const override;

AudioStreamMP3();
virtual ~AudioStreamMP3();
};
Expand Down
20 changes: 19 additions & 1 deletion modules/vorbis/audio_stream_ogg_vorbis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ 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();
bool beat_loop = loop_mode == LOOP_FORWARD;
if (beat_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();
}
Expand Down Expand Up @@ -240,6 +240,17 @@ bool AudioStreamPlaybackOggVorbis::is_playing() const {
return active;
}

void AudioStreamPlaybackOggVorbis::set_loop_mode(const LoopMode p_loop_mode) {
if (p_loop_mode == LOOP_PINGPONG || p_loop_mode == LOOP_BACKWARD) {
WARN_PRINT("AudioStreamPlaybackOggVorbis does not support Ping-Pong or Backward Loop Mode! Looping forward.");
loop_mode = LOOP_FORWARD;
} else if (p_loop_mode == LOOP_DETECT) {
loop_mode = vorbis_stream->has_loop() ? LOOP_FORWARD : LOOP_DISABLED;
} else {
loop_mode = p_loop_mode;
}
}

int AudioStreamPlaybackOggVorbis::get_loop_count() const {
return loops;
}
Expand Down Expand Up @@ -509,6 +520,13 @@ bool AudioStreamOggVorbis::is_monophonic() const {
return false;
}

Vector<AudioStreamPlayback::LoopMode> AudioStreamOggVorbis::get_unsupported_loop_modes() const {
Vector<AudioStreamPlayback::LoopMode> unsupported_modes;
unsupported_modes.append(AudioStreamPlayback::LoopMode::LOOP_BACKWARD);
unsupported_modes.append(AudioStreamPlayback::LoopMode::LOOP_PINGPONG);
return unsupported_modes;
}

void AudioStreamOggVorbis::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_packet_sequence", "packet_sequence"), &AudioStreamOggVorbis::set_packet_sequence);
ClassDB::bind_method(D_METHOD("get_packet_sequence"), &AudioStreamOggVorbis::get_packet_sequence);
Expand Down
4 changes: 4 additions & 0 deletions modules/vorbis/audio_stream_ogg_vorbis.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ class AudioStreamPlaybackOggVorbis : public AudioStreamPlaybackResampled {
virtual void stop() override;
virtual bool is_playing() const override;

virtual void set_loop_mode(const LoopMode p_loop_mode) override;
virtual int get_loop_count() const override; //times it looped

virtual float get_playback_position() const override;
Expand Down Expand Up @@ -149,6 +150,9 @@ class AudioStreamOggVorbis : public AudioStream {

virtual bool is_monophonic() const override;

// OGG does not support LOOP_BACKWARD and LOOP_PINGPONG.
virtual Vector<AudioStreamPlayback::LoopMode> get_unsupported_loop_modes() const override;

AudioStreamOggVorbis();
virtual ~AudioStreamOggVorbis();
};
Expand Down
55 changes: 55 additions & 0 deletions scene/2d/audio_stream_player_2d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ void AudioStreamPlayer2D::_notification(int p_what) {
active.set();
Ref<AudioStreamPlayback> new_playback = stream->instantiate_playback();
ERR_FAIL_COND_MSG(new_playback.is_null(), "Failed to instantiate playback.");
new_playback->set_loop_mode(loop_mode);

AudioServer::get_singleton()->start_playback_stream(new_playback, _get_actual_bus(), volume_vector, setplay.get(), pitch_scale);
stream_playbacks.push_back(new_playback);
setplay.set(-1);
Expand Down Expand Up @@ -221,6 +223,9 @@ void AudioStreamPlayer2D::_update_panning() {
void AudioStreamPlayer2D::set_stream(Ref<AudioStream> p_stream) {
stop();
stream = p_stream;

update_configuration_warnings();
notify_property_list_changed();
}

Ref<AudioStream> AudioStreamPlayer2D::get_stream() const {
Expand Down Expand Up @@ -295,6 +300,25 @@ float AudioStreamPlayer2D::get_playback_position() {
return 0;
}

void AudioStreamPlayer2D::set_loop_mode(const AudioStreamPlayback::LoopMode p_loop_mode) {
ERR_FAIL_INDEX(0, AudioStreamPlayback::LoopMode::LOOP_MAX);
if (loop_mode == p_loop_mode) {
return;
}

loop_mode = p_loop_mode;

for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
playback->set_loop_mode(loop_mode);
}

update_configuration_warnings();
}

AudioStreamPlayback::LoopMode AudioStreamPlayer2D::get_loop_mode() const {
return loop_mode;
}

void AudioStreamPlayer2D::set_bus(const StringName &p_bus) {
default_bus = p_bus; // This will be pushed to the audio server during the next physics timestep, which is fast enough.
}
Expand Down Expand Up @@ -341,6 +365,33 @@ void AudioStreamPlayer2D::_validate_property(PropertyInfo &p_property) const {

p_property.hint_string = options;
}
if (p_property.name == "loop_mode" && stream.is_valid()) {
Vector<AudioStreamPlayback::LoopMode> unsupported_modes = stream->get_unsupported_loop_modes();
for (int i = 0; i < AudioStreamPlayback::LoopMode::LOOP_MAX; i++) {
if (unsupported_modes.has(static_cast<AudioStreamPlayback::LoopMode>(i))) {
String slice = p_property.hint_string.get_slice(",", i);
p_property.hint_string = p_property.hint_string.replace(slice, slice + " " + RTR("(Not Supported)"));
}
}
}
}

TypedArray<String> AudioStreamPlayer2D::get_configuration_warnings() const {
TypedArray<String> warnings = Node2D::get_configuration_warnings();

if (stream.is_null()) {
return warnings;
}

Vector<AudioStreamPlayback::LoopMode> unsupported_modes = stream->get_unsupported_loop_modes();
if (unsupported_modes.has(loop_mode)) {
PropertyInfo info;
ClassDB::get_property_info(get_class_name(), "loop_mode", &info);
const String readable_name = info.hint_string.get_slice(",", loop_mode);
warnings.push_back(vformat(RTR("%s does not support \"%s\" loop mode.\nAs such, the stream may not play as intended.\nConsider switching to another loop mode."), stream->get_class_name(), readable_name));
}

return warnings;
}

void AudioStreamPlayer2D::_bus_layout_changed() {
Expand Down Expand Up @@ -430,6 +481,9 @@ void AudioStreamPlayer2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_playing"), &AudioStreamPlayer2D::is_playing);
ClassDB::bind_method(D_METHOD("get_playback_position"), &AudioStreamPlayer2D::get_playback_position);

ClassDB::bind_method(D_METHOD("set_loop_mode", "loop_mode"), &AudioStreamPlayer2D::set_loop_mode);
ClassDB::bind_method(D_METHOD("get_loop_mode"), &AudioStreamPlayer2D::get_loop_mode);

ClassDB::bind_method(D_METHOD("set_bus", "bus"), &AudioStreamPlayer2D::set_bus);
ClassDB::bind_method(D_METHOD("get_bus"), &AudioStreamPlayer2D::get_bus);

Expand Down Expand Up @@ -465,6 +519,7 @@ void AudioStreamPlayer2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_playing", "is_playing");
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::INT, "loop_mode", PROPERTY_HINT_ENUM, "Detect,Disabled,Forward,Ping-Pong,Backward"), "set_loop_mode", "get_loop_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_distance", PROPERTY_HINT_RANGE, "1,4096,1,or_greater,exp,suffix:px"), "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::INT, "max_polyphony", PROPERTY_HINT_NONE, ""), "set_max_polyphony", "get_max_polyphony");
Expand Down
7 changes: 7 additions & 0 deletions scene/2d/audio_stream_player_2d.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ class AudioStreamPlayer2D : public Node2D {
StringName default_bus = SNAME("Master");
int max_polyphony = 1;

AudioStreamPlayback::LoopMode loop_mode = AudioStreamPlayback::LOOP_DETECT;

void _set_playing(bool p_enable);
bool _is_active() const;

Expand Down Expand Up @@ -105,6 +107,9 @@ class AudioStreamPlayer2D : public Node2D {
bool is_playing() const;
float get_playback_position();

void set_loop_mode(const AudioStreamPlayback::LoopMode p_loop_mode);
AudioStreamPlayback::LoopMode get_loop_mode() const;

void set_bus(const StringName &p_bus);
StringName get_bus() const;

Expand All @@ -131,6 +136,8 @@ class AudioStreamPlayer2D : public Node2D {

Ref<AudioStreamPlayback> get_stream_playback();

TypedArray<String> get_configuration_warnings() const override;

AudioStreamPlayer2D();
~AudioStreamPlayer2D();
};
Expand Down
Loading