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 sample playback support for Web exports #91382

Merged
merged 1 commit into from
Jun 19, 2024
Merged
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
2 changes: 2 additions & 0 deletions core/config/project_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1480,6 +1480,8 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF("animation/warnings/check_angle_interpolation_type_conflicting", true);

GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "audio/buses/default_bus_layout", PROPERTY_HINT_FILE, "*.tres"), "res://default_bus_layout.tres");
GLOBAL_DEF(PropertyInfo(Variant::INT, "audio/general/default_playback_type", PROPERTY_HINT_ENUM, "Stream,Sample"), 0);
GLOBAL_DEF(PropertyInfo(Variant::INT, "audio/general/default_playback_type.web", PROPERTY_HINT_ENUM, "Stream,Sample"), 1);
GLOBAL_DEF_RST("audio/general/text_to_speech", false);
GLOBAL_DEF_RST(PropertyInfo(Variant::FLOAT, "audio/general/2d_panning_strength", PROPERTY_HINT_RANGE, "0,2,0.01"), 0.5f);
GLOBAL_DEF_RST(PropertyInfo(Variant::FLOAT, "audio/general/3d_panning_strength", PROPERTY_HINT_RANGE, "0,2,0.01"), 0.5f);
Expand Down
11 changes: 11 additions & 0 deletions doc/classes/AudioSample.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AudioSample" inherits="RefCounted" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
Base class for audio samples.
</brief_description>
<description>
Base class for audio samples.
</description>
<tutorials>
</tutorials>
</class>
11 changes: 11 additions & 0 deletions doc/classes/AudioSamplePlayback.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AudioSamplePlayback" inherits="RefCounted" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
Meta class for playing back audio samples.
</brief_description>
<description>
Meta class for playing back audio samples.
</description>
<tutorials>
</tutorials>
</class>
30 changes: 30 additions & 0 deletions doc/classes/AudioServer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,14 @@
If [code]true[/code], the bus at index [param bus_idx] is in solo mode.
</description>
</method>
<method name="is_stream_registered_as_sample" experimental="">
<return type="bool" />
<param index="0" name="stream" type="AudioStream" />
<description>
If [code]true[/code], the stream is registered as a sample. The engine will not have to register it before playing the sample.
If [code]false[/code], the stream will have to be registered before playing it. To prevent lag spikes, register the stream as sample with [method register_stream_as_sample].
</description>
</method>
<method name="lock">
<return type="void" />
<description>
Expand All @@ -198,6 +206,14 @@
Moves the bus from index [param index] to index [param to_index].
</description>
</method>
<method name="register_stream_as_sample" experimental="">
<return type="void" />
<param index="0" name="stream" type="AudioStream" />
<description>
Forces the registration of a stream as a sample.
[b]Note:[/b] Lag spikes may occur when calling this method, especially on single-threaded builds. It is suggested to call this method while loading assets, where the lag spike could be masked, instead of registering the sample right before it needs to be played.
</description>
</method>
<method name="remove_bus">
<return type="void" />
<param index="0" name="index" type="int" />
Expand Down Expand Up @@ -344,5 +360,19 @@
<constant name="SPEAKER_SURROUND_71" value="3" enum="SpeakerMode">
A 7.1 channel surround setup was detected.
</constant>
<constant name="PLAYBACK_TYPE_DEFAULT" value="0" enum="PlaybackType" experimental="">
The playback will be considered of the type declared at [member ProjectSettings.audio/general/default_playback_type].
</constant>
<constant name="PLAYBACK_TYPE_STREAM" value="1" enum="PlaybackType" experimental="">
Force the playback to be considered as a stream.
</constant>
<constant name="PLAYBACK_TYPE_SAMPLE" value="2" enum="PlaybackType" experimental="">
Force the playback to be considered as a sample. This can provide lower latency and more stable playback (with less risk of audio crackling), at the cost of having less flexibility.
[b]Note:[/b] Only currently supported on the web platform.
[b]Note:[/b] [AudioEffect]s are not supported when playback is considered as a sample.
</constant>
<constant name="PLAYBACK_TYPE_MAX" value="3" enum="PlaybackType" experimental="">
Represents the size of the [enum PlaybackType] enum.
</constant>
</constants>
</class>
18 changes: 18 additions & 0 deletions doc/classes/AudioStream.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,18 @@
Override this method to customize the returned value of [method is_monophonic]. Should return [code]true[/code] if this audio stream only supports one channel.
</description>
</method>
<method name="can_be_sampled" qualifiers="const" experimental="">
<return type="bool" />
<description>
Returns if the current [AudioStream] can be used as a sample. Only static streams can be sampled.
</description>
</method>
<method name="generate_sample" qualifiers="const" experimental="">
<return type="AudioSample" />
<description>
Generates an [AudioSample] based on the current stream.
</description>
</method>
<method name="get_length" qualifiers="const">
<return type="float" />
<description>
Expand All @@ -69,6 +81,12 @@
Returns a newly created [AudioStreamPlayback] intended to play this audio stream. Useful for when you want to extend [method _instantiate_playback] but call [method instantiate_playback] from an internally held AudioStream subresource. An example of this can be found in the source code for [code]AudioStreamRandomPitch::instantiate_playback[/code].
</description>
</method>
<method name="is_meta_stream" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if the stream is a collection of other streams, [code]false[/code] otherwise.
</description>
</method>
<method name="is_monophonic" qualifiers="const">
<return type="bool" />
<description>
Expand Down
13 changes: 13 additions & 0 deletions doc/classes/AudioStreamPlayback.xml
Original file line number Diff line number Diff line change
Expand Up @@ -79,5 +79,18 @@
Overridable method. Called whenever the audio stream is mixed if the playback is active and [method AudioServer.set_enable_tagging_used_audio_streams] has been set to [code]true[/code]. Editor plugins may use this method to "tag" the current position along the audio stream and display it in a preview.
</description>
</method>
<method name="get_sample_playback" qualifiers="const" experimental="">
<return type="AudioSamplePlayback" />
<description>
Returns the [AudioSamplePlayback] associated with this [AudioStreamPlayback] for playing back the audio sample of this stream.
</description>
</method>
<method name="set_sample_playback" experimental="">
<return type="void" />
<param index="0" name="playback_sample" type="AudioSamplePlayback" />
<description>
Associates [AudioSamplePlayback] to this [AudioStreamPlayback] for playing back the audio sample of this stream.
</description>
</method>
</methods>
</class>
4 changes: 3 additions & 1 deletion doc/classes/AudioStreamPlaybackPolyphonic.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@
<param index="1" name="from_offset" type="float" default="0" />
<param index="2" name="volume_db" type="float" default="0" />
<param index="3" name="pitch_scale" type="float" default="1.0" />
<param index="4" name="playback_type" type="int" enum="AudioServer.PlaybackType" default="0" />
<param index="5" name="bus" type="StringName" default="&amp;&quot;Master&quot;" />
<description>
Play an [AudioStream] at a given offset, volume and pitch scale. Playback starts immediately.
Play an [AudioStream] at a given offset, volume, pitch scale, playback type, and bus. Playback starts immediately.
The return value is a unique integer ID that is associated to this playback stream and which can be used to control it.
This ID becomes invalid when the stream ends (if it does not loop), when the [AudioStreamPlaybackPolyphonic] is stopped, or when [method stop_stream] is called.
This function returns [constant INVALID_ID] if the amount of streams currently playing equals [member AudioStreamPolyphonic.polyphony]. If you need a higher amount of maximum polyphony, raise this value.
Expand Down
3 changes: 3 additions & 0 deletions doc/classes/AudioStreamPlayer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@
<member name="pitch_scale" type="float" setter="set_pitch_scale" getter="get_pitch_scale" default="1.0">
The audio's pitch and tempo, as a multiplier of the [member stream]'s sample rate. A value of [code]2.0[/code] doubles the audio's pitch, while a value of [code]0.5[/code] halves the pitch.
</member>
<member name="playback_type" type="int" setter="set_playback_type" getter="get_playback_type" enum="AudioServer.PlaybackType" default="0" experimental="">
The playback type of the stream player. If set other than to the default value, it will force that playback type.
</member>
<member name="playing" type="bool" setter="_set_playing" getter="is_playing" default="false">
If [code]true[/code], this node is playing sounds. Setting this property has the same effect as [method play] and [method stop].
</member>
Expand Down
3 changes: 3 additions & 0 deletions doc/classes/AudioStreamPlayer2D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@
<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.
</member>
<member name="playback_type" type="int" setter="set_playback_type" getter="get_playback_type" enum="AudioServer.PlaybackType" default="0" experimental="">
The playback type of the stream player. If set other than to the default value, it will force that playback type.
</member>
<member name="playing" type="bool" setter="_set_playing" getter="is_playing" default="false">
If [code]true[/code], audio is playing or is queued to be played (see [method play]).
</member>
Expand Down
3 changes: 3 additions & 0 deletions doc/classes/AudioStreamPlayer3D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@
<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.
</member>
<member name="playback_type" type="int" setter="set_playback_type" getter="get_playback_type" enum="AudioServer.PlaybackType" default="0" experimental="">
The playback type of the stream player. If set other than to the default value, it will force that playback type.
</member>
<member name="playing" type="bool" setter="_set_playing" getter="is_playing" default="false">
If [code]true[/code], audio is playing or is queued to be played (see [method play]).
</member>
Expand Down
11 changes: 10 additions & 1 deletion doc/classes/ProjectSettings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@
Safer override for [member audio/driver/mix_rate] in the Web platform. Here [code]0[/code] means "let the browser choose" (since some browsers do not like forcing the mix rate).
</member>
<member name="audio/driver/output_latency" type="int" setter="" getter="" default="15">
Specifies the preferred output latency in milliseconds for audio. Lower values will result in lower audio latency at the cost of increased CPU usage. Low values may result in audible cracking on slower hardware.
Specifies the preferred output latency in milliseconds for audio. Lower values will result in lower audio latency at the cost of increased CPU usage. Low values may result in audible crackling on slower hardware.
Audio output latency may be constrained by the host operating system and audio hardware drivers. If the host can not provide the specified audio output latency then Godot will attempt to use the nearest latency allowed by the host. As such you should always use [method AudioServer.get_output_latency] to determine the actual audio output latency.
Audio output latency can be overridden using the [code]--audio-output-latency &lt;ms&gt;[/code] command line argument.
[b]Note:[/b] This setting is ignored on Android, and on all versions of Windows prior to Windows 10.
Expand All @@ -409,6 +409,15 @@
The base strength of the panning effect for all [AudioStreamPlayer3D] nodes. The panning strength can be further scaled on each Node using [member AudioStreamPlayer3D.panning_strength]. A value of [code]0.0[/code] disables stereo panning entirely, leaving only volume attenuation in place. A value of [code]1.0[/code] completely mutes one of the channels if the sound is located exactly to the left (or right) of the listener.
The default value of [code]0.5[/code] is tuned for headphones. When using speakers, you may find lower values to sound better as speakers have a lower stereo separation compared to headphones.
</member>
<member name="audio/general/default_playback_type" type="int" setter="" getter="" default="0" experimental="">
Specifies the default playback type of the platform.
The default value is set to [b]Stream[/b], as most platforms have no issues mixing streams.
</member>
<member name="audio/general/default_playback_type.web" type="int" setter="" getter="" default="1" experimental="">
Specifies the default playback type of the Web platform.
The default value is set to [b]Sample[/b] as the Web platform is not suited to mix audio streams outside of the Web Audio API, especially when exporting a single-threaded game. [b]Sample[/b] allows for lower latency on the web platform at the cost of flexibility ([AudioEffect]s are not supported).
[b]Warning:[/b] Forcing [b]Stream[/b] on the Web platform may cause high audio latency and crackling, especially when exporting a multi-threaded game.
</member>
<member name="audio/general/ios/mix_with_others" type="bool" setter="" getter="" default="false">
Sets the [url=https://developer.apple.com/documentation/avfaudio/avaudiosession/categoryoptions/1616611-mixwithothers]mixWithOthers[/url] option for the AVAudioSession on iOS. This will override the mix behavior, if the category is set to [code]Play and Record[/code], [code]Playback[/code], or [code]Multi Route[/code].
[code]Ambient[/code] always has this set per default.
Expand Down
7 changes: 7 additions & 0 deletions misc/extension_api_validation/4.2-stable.expected
Original file line number Diff line number Diff line change
Expand Up @@ -365,3 +365,10 @@ Validate extension JSON: Error: Field 'classes/Animation/methods/track_find_key/

Added optional arguments to avoid finding keys out of the animation range (GH-86661), and to handle backward seeking.
Compatibility method registered.


GH-91382
--------
Validate extension JSON: Error: Field 'classes/AudioStreamPlaybackPolyphonic/methods/play_stream/arguments': size changed value in new API, from 4 to 6.

Optional arguments added. Compatibility methods registered.
28 changes: 28 additions & 0 deletions modules/minimp3/audio_stream_mp3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,22 @@ void AudioStreamPlaybackMP3::tag_used_streams() {
mp3_stream->tag_used(get_playback_position());
}

void AudioStreamPlaybackMP3::set_is_sample(bool p_is_sample) {
_is_sample = p_is_sample;
}

bool AudioStreamPlaybackMP3::get_is_sample() const {
return _is_sample;
}

Ref<AudioSamplePlayback> AudioStreamPlaybackMP3::get_sample_playback() const {
return sample_playback;
}

void AudioStreamPlaybackMP3::set_sample_playback(const Ref<AudioSamplePlayback> &p_playback) {
sample_playback = p_playback;
}

void AudioStreamPlaybackMP3::set_parameter(const StringName &p_name, const Variant &p_value) {
if (p_name == SNAME("looping")) {
if (p_value == Variant()) {
Expand Down Expand Up @@ -287,6 +303,18 @@ int AudioStreamMP3::get_bar_beats() const {
return bar_beats;
}

Ref<AudioSample> AudioStreamMP3::generate_sample() const {
Ref<AudioSample> sample;
sample.instantiate();
sample->stream = this;
sample->loop_mode = loop
? AudioSample::LoopMode::LOOP_FORWARD
: AudioSample::LoopMode::LOOP_DISABLED;
sample->loop_begin = loop_offset;
sample->loop_end = 0;
return sample;
}

void AudioStreamMP3::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_data", "data"), &AudioStreamMP3::set_data);
ClassDB::bind_method(D_METHOD("get_data"), &AudioStreamMP3::get_data);
Expand Down
13 changes: 13 additions & 0 deletions modules/minimp3/audio_stream_mp3.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ class AudioStreamPlaybackMP3 : public AudioStreamPlaybackResampled {

Ref<AudioStreamMP3> mp3_stream;

bool _is_sample = false;
Ref<AudioSamplePlayback> sample_playback;

protected:
virtual int _mix_internal(AudioFrame *p_buffer, int p_frames) override;
virtual float get_stream_sampling_rate() override;
Expand All @@ -74,6 +77,11 @@ class AudioStreamPlaybackMP3 : public AudioStreamPlaybackResampled {

virtual void tag_used_streams() override;

virtual void set_is_sample(bool p_is_sample) override;
virtual bool get_is_sample() const override;
virtual Ref<AudioSamplePlayback> get_sample_playback() const override;
virtual void set_sample_playback(const Ref<AudioSamplePlayback> &p_playback) override;

virtual void set_parameter(const StringName &p_name, const Variant &p_value) override;
virtual Variant get_parameter(const StringName &p_name) const override;

Expand Down Expand Up @@ -131,6 +139,11 @@ class AudioStreamMP3 : public AudioStream {

virtual bool is_monophonic() const override;

virtual bool can_be_sampled() const override {
return true;
}
virtual Ref<AudioSample> generate_sample() const override;

virtual void get_parameter_list(List<Parameter> *r_parameters) override;

AudioStreamMP3();
Expand Down
28 changes: 28 additions & 0 deletions modules/vorbis/audio_stream_ogg_vorbis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,22 @@ void AudioStreamPlaybackOggVorbis::seek(double p_time) {
}
}

void AudioStreamPlaybackOggVorbis::set_is_sample(bool p_is_sample) {
_is_sample = p_is_sample;
}

bool AudioStreamPlaybackOggVorbis::get_is_sample() const {
return _is_sample;
}

Ref<AudioSamplePlayback> AudioStreamPlaybackOggVorbis::get_sample_playback() const {
return sample_playback;
}

void AudioStreamPlaybackOggVorbis::set_sample_playback(const Ref<AudioSamplePlayback> &p_playback) {
sample_playback = p_playback;
}

AudioStreamPlaybackOggVorbis::~AudioStreamPlaybackOggVorbis() {
if (block_is_allocated) {
vorbis_block_clear(&block);
Expand Down Expand Up @@ -517,6 +533,18 @@ void AudioStreamOggVorbis::get_parameter_list(List<Parameter> *r_parameters) {
r_parameters->push_back(Parameter(PropertyInfo(Variant::BOOL, "looping", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_CHECKABLE), Variant()));
}

Ref<AudioSample> AudioStreamOggVorbis::generate_sample() const {
Ref<AudioSample> sample;
sample.instantiate();
sample->stream = this;
sample->loop_mode = loop
? AudioSample::LoopMode::LOOP_FORWARD
: AudioSample::LoopMode::LOOP_DISABLED;
sample->loop_begin = loop_offset;
sample->loop_end = 0;
return sample;
}

void AudioStreamOggVorbis::_bind_methods() {
ClassDB::bind_static_method("AudioStreamOggVorbis", D_METHOD("load_from_buffer", "buffer"), &AudioStreamOggVorbis::load_from_buffer);
ClassDB::bind_static_method("AudioStreamOggVorbis", D_METHOD("load_from_file", "path"), &AudioStreamOggVorbis::load_from_file);
Expand Down
13 changes: 13 additions & 0 deletions modules/vorbis/audio_stream_ogg_vorbis.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ class AudioStreamPlaybackOggVorbis : public AudioStreamPlaybackResampled {
Ref<OggPacketSequencePlayback> vorbis_data_playback;
Ref<AudioStreamOggVorbis> vorbis_stream;

bool _is_sample = false;
Ref<AudioSamplePlayback> sample_playback;

int _mix_frames(AudioFrame *p_buffer, int p_frames);
int _mix_frames_vorbis(AudioFrame *p_buffer, int p_frames);

Expand All @@ -100,6 +103,11 @@ class AudioStreamPlaybackOggVorbis : public AudioStreamPlaybackResampled {
virtual void set_parameter(const StringName &p_name, const Variant &p_value) override;
virtual Variant get_parameter(const StringName &p_name) const override;

virtual void set_is_sample(bool p_is_sample) override;
virtual bool get_is_sample() const override;
virtual Ref<AudioSamplePlayback> get_sample_playback() const override;
virtual void set_sample_playback(const Ref<AudioSamplePlayback> &p_playback) override;

AudioStreamPlaybackOggVorbis() {}
~AudioStreamPlaybackOggVorbis();
};
Expand Down Expand Up @@ -159,6 +167,11 @@ class AudioStreamOggVorbis : public AudioStream {

virtual void get_parameter_list(List<Parameter> *r_parameters) override;

virtual bool can_be_sampled() const override {
return true;
}
virtual Ref<AudioSample> generate_sample() const override;

AudioStreamOggVorbis();
virtual ~AudioStreamOggVorbis();
};
Expand Down
Loading
Loading