Skip to content

Commit

Permalink
Add samples playback support
Browse files Browse the repository at this point in the history
  • Loading branch information
adamscott committed Jun 17, 2024
1 parent eb20a68 commit 9554d2e
Show file tree
Hide file tree
Showing 42 changed files with 2,618 additions and 45 deletions.
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>
28 changes: 28 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,13 @@
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. 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 relying on registering the sample when 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 +359,18 @@
<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.
Be careful, as not every platform supports samples.
</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 if the stream is a collection of others streams.
</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
8 changes: 8 additions & 0 deletions doc/classes/ProjectSettings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,14 @@
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 [code]Stream[/code], 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 [code]Sample[/code] as the Web platform (especially when exporting a single-threaded game) is not suited to mix audio streams outside of the Web Audio API.
</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

0 comments on commit 9554d2e

Please sign in to comment.