Pārlūkot izejas kodu

Merge pull request #100307 from akien-mga/mp3-load_from_file

Add AudioStreamMP3 `load_from_file`/`load_from_buffer` and harmonize other audio streams
Rémi Verschelde 8 mēneši atpakaļ
vecāks
revīzija
d5b73e2742

+ 5 - 4
doc/classes/AudioStreamWAV.xml

@@ -13,11 +13,11 @@
 	<methods>
 	<methods>
 		<method name="load_from_buffer" qualifiers="static">
 		<method name="load_from_buffer" qualifiers="static">
 			<return type="AudioStreamWAV" />
 			<return type="AudioStreamWAV" />
-			<param index="0" name="buffer" type="PackedByteArray" />
+			<param index="0" name="stream_data" type="PackedByteArray" />
 			<param index="1" name="options" type="Dictionary" default="{}" />
 			<param index="1" name="options" type="Dictionary" default="{}" />
 			<description>
 			<description>
-				Creates a new [AudioStreamWAV] instance from the given buffer. The keys and values of [param options] match the properties of [ResourceImporterWAV].
-				The usage of [param options] is identical to [method AudioStreamWAV.load_from_file].
+				Creates a new [AudioStreamWAV] instance from the given buffer. The buffer must contain WAV data.
+				The keys and values of [param options] match the properties of [ResourceImporterWAV]. The usage of [param options] is identical to [method AudioStreamWAV.load_from_file].
 			</description>
 			</description>
 		</method>
 		</method>
 		<method name="load_from_file" qualifiers="static">
 		<method name="load_from_file" qualifiers="static">
@@ -25,7 +25,8 @@
 			<param index="0" name="path" type="String" />
 			<param index="0" name="path" type="String" />
 			<param index="1" name="options" type="Dictionary" default="{}" />
 			<param index="1" name="options" type="Dictionary" default="{}" />
 			<description>
 			<description>
-				Creates a new [AudioStreamWAV] instance from the given file path. The keys and values of [param options] match the properties of [ResourceImporterWAV].
+				Creates a new [AudioStreamWAV] instance from the given file path. The file must be in WAV format.
+				The keys and values of [param options] match the properties of [ResourceImporterWAV].
 				[b]Example:[/b] Load the first file dropped as a WAV and play it:
 				[b]Example:[/b] Load the first file dropped as a WAV and play it:
 				[codeblock]
 				[codeblock]
 				@onready var audio_player = $AudioStreamPlayer
 				@onready var audio_player = $AudioStreamPlayer

+ 17 - 0
modules/minimp3/audio_stream_mp3.cpp

@@ -312,7 +312,24 @@ Ref<AudioSample> AudioStreamMP3::generate_sample() const {
 	return sample;
 	return sample;
 }
 }
 
 
+Ref<AudioStreamMP3> AudioStreamMP3::load_from_buffer(const Vector<uint8_t> &p_stream_data) {
+	Ref<AudioStreamMP3> mp3_stream;
+	mp3_stream.instantiate();
+	mp3_stream->set_data(p_stream_data);
+	ERR_FAIL_COND_V_MSG(mp3_stream->get_data().is_empty(), Ref<AudioStreamMP3>(), "MP3 decoding failed. Check that your data is a valid MP3 audio stream.");
+	return mp3_stream;
+}
+
+Ref<AudioStreamMP3> AudioStreamMP3::load_from_file(const String &p_path) {
+	const Vector<uint8_t> stream_data = FileAccess::get_file_as_bytes(p_path);
+	ERR_FAIL_COND_V_MSG(stream_data.is_empty(), Ref<AudioStreamMP3>(), vformat("Cannot open file '%s'.", p_path));
+	return load_from_buffer(stream_data);
+}
+
 void AudioStreamMP3::_bind_methods() {
 void AudioStreamMP3::_bind_methods() {
+	ClassDB::bind_static_method("AudioStreamMP3", D_METHOD("load_from_buffer", "stream_data"), &AudioStreamMP3::load_from_buffer);
+	ClassDB::bind_static_method("AudioStreamMP3", D_METHOD("load_from_file", "path"), &AudioStreamMP3::load_from_file);
+
 	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);
 
 

+ 3 - 0
modules/minimp3/audio_stream_mp3.h

@@ -113,6 +113,9 @@ protected:
 	static void _bind_methods();
 	static void _bind_methods();
 
 
 public:
 public:
+	static Ref<AudioStreamMP3> load_from_buffer(const Vector<uint8_t> &p_stream_data);
+	static Ref<AudioStreamMP3> load_from_file(const String &p_path);
+
 	void set_loop(bool p_enable);
 	void set_loop(bool p_enable);
 	virtual bool has_loop() const override;
 	virtual bool has_loop() const override;
 
 

+ 17 - 0
modules/minimp3/doc_classes/AudioStreamMP3.xml

@@ -5,9 +5,26 @@
 	</brief_description>
 	</brief_description>
 	<description>
 	<description>
 		MP3 audio stream driver. See [member data] if you want to load an MP3 file at run-time.
 		MP3 audio stream driver. See [member data] if you want to load an MP3 file at run-time.
+		[b]Note:[/b] This class can optionally support legacy MP1 and MP2 formats, provided that the engine is compiled with the [code]minimp3_extra_formats=yes[/code] SCons option. These extra formats are not enabled by default.
 	</description>
 	</description>
 	<tutorials>
 	<tutorials>
 	</tutorials>
 	</tutorials>
+	<methods>
+		<method name="load_from_buffer" qualifiers="static">
+			<return type="AudioStreamMP3" />
+			<param index="0" name="stream_data" type="PackedByteArray" />
+			<description>
+				Creates a new [AudioStreamMP3] instance from the given buffer. The buffer must contain MP3 data.
+			</description>
+		</method>
+		<method name="load_from_file" qualifiers="static">
+			<return type="AudioStreamMP3" />
+			<param index="0" name="path" type="String" />
+			<description>
+				Creates a new [AudioStreamMP3] instance from the given file path. The file must be in MP3 format.
+			</description>
+		</method>
+	</methods>
 	<members>
 	<members>
 		<member name="bar_beats" type="int" setter="set_bar_beats" getter="get_bar_beats" default="4">
 		<member name="bar_beats" type="int" setter="set_bar_beats" getter="get_bar_beats" default="4">
 		</member>
 		</member>

+ 4 - 23
modules/minimp3/resource_importer_mp3.cpp

@@ -85,35 +85,15 @@ void ResourceImporterMP3::get_import_options(const String &p_path, List<ImportOp
 bool ResourceImporterMP3::has_advanced_options() const {
 bool ResourceImporterMP3::has_advanced_options() const {
 	return true;
 	return true;
 }
 }
+
 void ResourceImporterMP3::show_advanced_options(const String &p_path) {
 void ResourceImporterMP3::show_advanced_options(const String &p_path) {
-	Ref<AudioStreamMP3> mp3_stream = import_mp3(p_path);
+	Ref<AudioStreamMP3> mp3_stream = AudioStreamMP3::load_from_file(p_path);
 	if (mp3_stream.is_valid()) {
 	if (mp3_stream.is_valid()) {
 		AudioStreamImportSettingsDialog::get_singleton()->edit(p_path, "mp3", mp3_stream);
 		AudioStreamImportSettingsDialog::get_singleton()->edit(p_path, "mp3", mp3_stream);
 	}
 	}
 }
 }
 #endif
 #endif
 
 
-Ref<AudioStreamMP3> ResourceImporterMP3::import_mp3(const String &p_path) {
-	Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
-	ERR_FAIL_COND_V(f.is_null(), Ref<AudioStreamMP3>());
-
-	uint64_t len = f->get_length();
-
-	Vector<uint8_t> data;
-	data.resize(len);
-	uint8_t *w = data.ptrw();
-
-	f->get_buffer(w, len);
-
-	Ref<AudioStreamMP3> mp3_stream;
-	mp3_stream.instantiate();
-
-	mp3_stream->set_data(data);
-	ERR_FAIL_COND_V(mp3_stream->get_data().is_empty(), Ref<AudioStreamMP3>());
-
-	return mp3_stream;
-}
-
 Error ResourceImporterMP3::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
 Error ResourceImporterMP3::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
 	bool loop = p_options["loop"];
 	bool loop = p_options["loop"];
 	float loop_offset = p_options["loop_offset"];
 	float loop_offset = p_options["loop_offset"];
@@ -121,10 +101,11 @@ Error ResourceImporterMP3::import(ResourceUID::ID p_source_id, const String &p_s
 	float beat_count = p_options["beat_count"];
 	float beat_count = p_options["beat_count"];
 	float bar_beats = p_options["bar_beats"];
 	float bar_beats = p_options["bar_beats"];
 
 
-	Ref<AudioStreamMP3> mp3_stream = import_mp3(p_source_file);
+	Ref<AudioStreamMP3> mp3_stream = AudioStreamMP3::load_from_file(p_source_file);
 	if (mp3_stream.is_null()) {
 	if (mp3_stream.is_null()) {
 		return ERR_CANT_OPEN;
 		return ERR_CANT_OPEN;
 	}
 	}
+
 	mp3_stream->set_loop(loop);
 	mp3_stream->set_loop(loop);
 	mp3_stream->set_loop_offset(loop_offset);
 	mp3_stream->set_loop_offset(loop_offset);
 	mp3_stream->set_bpm(bpm);
 	mp3_stream->set_bpm(bpm);

+ 0 - 1
modules/minimp3/resource_importer_mp3.h

@@ -55,7 +55,6 @@ public:
 	virtual bool has_advanced_options() const override;
 	virtual bool has_advanced_options() const override;
 	virtual void show_advanced_options(const String &p_path) override;
 	virtual void show_advanced_options(const String &p_path) override;
 #endif
 #endif
-	static Ref<AudioStreamMP3> import_mp3(const String &p_path);
 
 
 	virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
 	virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
 
 

+ 127 - 11
modules/vorbis/audio_stream_ogg_vorbis.cpp

@@ -30,8 +30,6 @@
 
 
 #include "audio_stream_ogg_vorbis.h"
 #include "audio_stream_ogg_vorbis.h"
 
 
-#include "modules/vorbis/resource_importer_ogg_vorbis.h"
-
 #include <ogg/ogg.h>
 #include <ogg/ogg.h>
 
 
 int AudioStreamPlaybackOggVorbis::_mix_internal(AudioFrame *p_buffer, int p_frames) {
 int AudioStreamPlaybackOggVorbis::_mix_internal(AudioFrame *p_buffer, int p_frames) {
@@ -546,8 +544,134 @@ Ref<AudioSample> AudioStreamOggVorbis::generate_sample() const {
 	return sample;
 	return sample;
 }
 }
 
 
+Ref<AudioStreamOggVorbis> AudioStreamOggVorbis::load_from_buffer(const Vector<uint8_t> &p_stream_data) {
+	Ref<AudioStreamOggVorbis> ogg_vorbis_stream;
+	ogg_vorbis_stream.instantiate();
+
+	Ref<OggPacketSequence> ogg_packet_sequence;
+	ogg_packet_sequence.instantiate();
+
+	ogg_stream_state stream_state;
+	ogg_sync_state sync_state;
+	ogg_page page;
+	ogg_packet packet;
+	bool initialized_stream = false;
+
+	ogg_sync_init(&sync_state);
+	const long OGG_SYNC_BUFFER_SIZE = 8192;
+	int err;
+	size_t cursor = 0;
+	size_t packet_count = 0;
+	bool done = false;
+	while (!done) {
+		err = ogg_sync_check(&sync_state);
+		ERR_FAIL_COND_V_MSG(err != 0, Ref<AudioStreamOggVorbis>(), "Ogg sync error " + itos(err));
+		while (ogg_sync_pageout(&sync_state, &page) != 1) {
+			if (cursor >= size_t(p_stream_data.size())) {
+				done = true;
+				break;
+			}
+			err = ogg_sync_check(&sync_state);
+			ERR_FAIL_COND_V_MSG(err != 0, Ref<AudioStreamOggVorbis>(), "Ogg sync error " + itos(err));
+			char *sync_buf = ogg_sync_buffer(&sync_state, OGG_SYNC_BUFFER_SIZE);
+			err = ogg_sync_check(&sync_state);
+			ERR_FAIL_COND_V_MSG(err != 0, Ref<AudioStreamOggVorbis>(), "Ogg sync error " + itos(err));
+			size_t copy_size = p_stream_data.size() - cursor;
+			if (copy_size > OGG_SYNC_BUFFER_SIZE) {
+				copy_size = OGG_SYNC_BUFFER_SIZE;
+			}
+			memcpy(sync_buf, &p_stream_data[cursor], copy_size);
+			ogg_sync_wrote(&sync_state, copy_size);
+			cursor += copy_size;
+			err = ogg_sync_check(&sync_state);
+			ERR_FAIL_COND_V_MSG(err != 0, Ref<AudioStreamOggVorbis>(), "Ogg sync error " + itos(err));
+		}
+		if (done) {
+			break;
+		}
+		err = ogg_sync_check(&sync_state);
+		ERR_FAIL_COND_V_MSG(err != 0, Ref<AudioStreamOggVorbis>(), "Ogg sync error " + itos(err));
+
+		// Have a page now.
+		if (!initialized_stream) {
+			if (ogg_stream_init(&stream_state, ogg_page_serialno(&page))) {
+				ERR_FAIL_V_MSG(Ref<AudioStreamOggVorbis>(), "Failed allocating memory for Ogg Vorbis stream.");
+			}
+			initialized_stream = true;
+		}
+		ogg_stream_pagein(&stream_state, &page);
+		err = ogg_stream_check(&stream_state);
+		ERR_FAIL_COND_V_MSG(err != 0, Ref<AudioStreamOggVorbis>(), "Ogg stream error " + itos(err));
+		int desync_iters = 0;
+
+		RBMap<uint64_t, Vector<Vector<uint8_t>>> sorted_packets;
+		int64_t granule_pos = 0;
+
+		while (true) {
+			err = ogg_stream_packetout(&stream_state, &packet);
+			if (err == -1) {
+				// According to the docs this is usually recoverable, but don't sit here spinning forever.
+				desync_iters++;
+				WARN_PRINT_ONCE("Desync during ogg import.");
+				ERR_FAIL_COND_V_MSG(desync_iters > 100, Ref<AudioStreamOggVorbis>(), "Packet sync issue during Ogg import");
+				continue;
+			} else if (err == 0) {
+				// Not enough data to fully reconstruct a packet. Go on to the next page.
+				break;
+			}
+			if (packet_count == 0 && vorbis_synthesis_idheader(&packet) == 0) {
+				print_verbose("Found a non-vorbis-header packet in a header position");
+				// Clearly this logical stream is not a vorbis stream, so destroy it and try again with the next page.
+				if (initialized_stream) {
+					ogg_stream_clear(&stream_state);
+					initialized_stream = false;
+				}
+				break;
+			}
+			if (packet.granulepos > granule_pos) {
+				granule_pos = packet.granulepos;
+			}
+
+			if (packet.bytes > 0) {
+				PackedByteArray data;
+				data.resize(packet.bytes);
+				memcpy(data.ptrw(), packet.packet, packet.bytes);
+				sorted_packets[granule_pos].push_back(data);
+				packet_count++;
+			}
+		}
+		Vector<Vector<uint8_t>> packet_data;
+		for (const KeyValue<uint64_t, Vector<Vector<uint8_t>>> &pair : sorted_packets) {
+			for (const Vector<uint8_t> &packets : pair.value) {
+				packet_data.push_back(packets);
+			}
+		}
+		if (initialized_stream && packet_data.size() > 0) {
+			ogg_packet_sequence->push_page(ogg_page_granulepos(&page), packet_data);
+		}
+	}
+	if (initialized_stream) {
+		ogg_stream_clear(&stream_state);
+	}
+	ogg_sync_clear(&sync_state);
+
+	if (ogg_packet_sequence->get_packet_granule_positions().is_empty()) {
+		ERR_FAIL_V_MSG(Ref<AudioStreamOggVorbis>(), "Ogg Vorbis decoding failed. Check that your data is a valid Ogg Vorbis audio stream.");
+	}
+
+	ogg_vorbis_stream->set_packet_sequence(ogg_packet_sequence);
+
+	return ogg_vorbis_stream;
+}
+
+Ref<AudioStreamOggVorbis> AudioStreamOggVorbis::load_from_file(const String &p_path) {
+	const Vector<uint8_t> stream_data = FileAccess::get_file_as_bytes(p_path);
+	ERR_FAIL_COND_V_MSG(stream_data.is_empty(), Ref<AudioStreamOggVorbis>(), vformat("Cannot open file '%s'.", p_path));
+	return load_from_buffer(stream_data);
+}
+
 void AudioStreamOggVorbis::_bind_methods() {
 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_buffer", "stream_data"), &AudioStreamOggVorbis::load_from_buffer);
 	ClassDB::bind_static_method("AudioStreamOggVorbis", D_METHOD("load_from_file", "path"), &AudioStreamOggVorbis::load_from_file);
 	ClassDB::bind_static_method("AudioStreamOggVorbis", D_METHOD("load_from_file", "path"), &AudioStreamOggVorbis::load_from_file);
 
 
 	ClassDB::bind_method(D_METHOD("set_packet_sequence", "packet_sequence"), &AudioStreamOggVorbis::set_packet_sequence);
 	ClassDB::bind_method(D_METHOD("set_packet_sequence", "packet_sequence"), &AudioStreamOggVorbis::set_packet_sequence);
@@ -579,11 +703,3 @@ void AudioStreamOggVorbis::_bind_methods() {
 AudioStreamOggVorbis::AudioStreamOggVorbis() {}
 AudioStreamOggVorbis::AudioStreamOggVorbis() {}
 
 
 AudioStreamOggVorbis::~AudioStreamOggVorbis() {}
 AudioStreamOggVorbis::~AudioStreamOggVorbis() {}
-
-Ref<AudioStreamOggVorbis> AudioStreamOggVorbis::load_from_buffer(const Vector<uint8_t> &file_data) {
-	return ResourceImporterOggVorbis::load_from_buffer(file_data);
-}
-
-Ref<AudioStreamOggVorbis> AudioStreamOggVorbis::load_from_file(const String &p_path) {
-	return ResourceImporterOggVorbis::load_from_file(p_path);
-}

+ 2 - 1
modules/vorbis/audio_stream_ogg_vorbis.h

@@ -140,7 +140,8 @@ protected:
 
 
 public:
 public:
 	static Ref<AudioStreamOggVorbis> load_from_file(const String &p_path);
 	static Ref<AudioStreamOggVorbis> load_from_file(const String &p_path);
-	static Ref<AudioStreamOggVorbis> load_from_buffer(const Vector<uint8_t> &file_data);
+	static Ref<AudioStreamOggVorbis> load_from_buffer(const Vector<uint8_t> &p_stream_data);
+
 	void set_loop(bool p_enable);
 	void set_loop(bool p_enable);
 	virtual bool has_loop() const override;
 	virtual bool has_loop() const override;
 
 

+ 3 - 3
modules/vorbis/doc_classes/AudioStreamOggVorbis.xml

@@ -12,16 +12,16 @@
 	<methods>
 	<methods>
 		<method name="load_from_buffer" qualifiers="static">
 		<method name="load_from_buffer" qualifiers="static">
 			<return type="AudioStreamOggVorbis" />
 			<return type="AudioStreamOggVorbis" />
-			<param index="0" name="buffer" type="PackedByteArray" />
+			<param index="0" name="stream_data" type="PackedByteArray" />
 			<description>
 			<description>
-				Creates a new AudioStreamOggVorbis instance from the given buffer. The buffer must contain Ogg Vorbis data.
+				Creates a new [AudioStreamOggVorbis] instance from the given buffer. The buffer must contain Ogg Vorbis data.
 			</description>
 			</description>
 		</method>
 		</method>
 		<method name="load_from_file" qualifiers="static">
 		<method name="load_from_file" qualifiers="static">
 			<return type="AudioStreamOggVorbis" />
 			<return type="AudioStreamOggVorbis" />
 			<param index="0" name="path" type="String" />
 			<param index="0" name="path" type="String" />
 			<description>
 			<description>
-				Creates a new AudioStreamOggVorbis instance from the given file path. The file must be in Ogg Vorbis format.
+				Creates a new [AudioStreamOggVorbis] instance from the given file path. The file must be in Ogg Vorbis format.
 			</description>
 			</description>
 		</method>
 		</method>
 	</methods>
 	</methods>

+ 5 - 5
modules/vorbis/doc_classes/ResourceImporterOggVorbis.xml

@@ -12,18 +12,18 @@
 		<link title="Importing audio samples">$DOCS_URL/tutorials/assets_pipeline/importing_audio_samples.html</link>
 		<link title="Importing audio samples">$DOCS_URL/tutorials/assets_pipeline/importing_audio_samples.html</link>
 	</tutorials>
 	</tutorials>
 	<methods>
 	<methods>
-		<method name="load_from_buffer" qualifiers="static">
+		<method name="load_from_buffer" qualifiers="static" deprecated="Use [method AudioStreamOggVorbis.load_from_buffer] instead.">
 			<return type="AudioStreamOggVorbis" />
 			<return type="AudioStreamOggVorbis" />
-			<param index="0" name="buffer" type="PackedByteArray" />
+			<param index="0" name="stream_data" type="PackedByteArray" />
 			<description>
 			<description>
-				This method loads audio data from a PackedByteArray buffer into an AudioStreamOggVorbis object.
+				Creates a new [AudioStreamOggVorbis] instance from the given buffer. The buffer must contain Ogg Vorbis data.
 			</description>
 			</description>
 		</method>
 		</method>
-		<method name="load_from_file" qualifiers="static">
+		<method name="load_from_file" qualifiers="static" deprecated="Use [method AudioStreamOggVorbis.load_from_file] instead.">
 			<return type="AudioStreamOggVorbis" />
 			<return type="AudioStreamOggVorbis" />
 			<param index="0" name="path" type="String" />
 			<param index="0" name="path" type="String" />
 			<description>
 			<description>
-				This method loads audio data from a file into an AudioStreamOggVorbis object. The file path is provided as a string.
+				Creates a new [AudioStreamOggVorbis] instance from the given file path. The file must be in Ogg Vorbis format.
 			</description>
 			</description>
 		</method>
 		</method>
 	</methods>
 	</methods>

+ 14 - 128
modules/vorbis/resource_importer_ogg_vorbis.cpp

@@ -81,13 +81,12 @@ void ResourceImporterOggVorbis::get_import_options(const String &p_path, List<Im
 }
 }
 
 
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
-
 bool ResourceImporterOggVorbis::has_advanced_options() const {
 bool ResourceImporterOggVorbis::has_advanced_options() const {
 	return true;
 	return true;
 }
 }
 
 
 void ResourceImporterOggVorbis::show_advanced_options(const String &p_path) {
 void ResourceImporterOggVorbis::show_advanced_options(const String &p_path) {
-	Ref<AudioStreamOggVorbis> ogg_stream = load_from_file(p_path);
+	Ref<AudioStreamOggVorbis> ogg_stream = AudioStreamOggVorbis::load_from_file(p_path);
 	if (ogg_stream.is_valid()) {
 	if (ogg_stream.is_valid()) {
 		AudioStreamImportSettingsDialog::get_singleton()->edit(p_path, "oggvorbisstr", ogg_stream);
 		AudioStreamImportSettingsDialog::get_singleton()->edit(p_path, "oggvorbisstr", ogg_stream);
 	}
 	}
@@ -101,7 +100,7 @@ Error ResourceImporterOggVorbis::import(ResourceUID::ID p_source_id, const Strin
 	int beat_count = p_options["beat_count"];
 	int beat_count = p_options["beat_count"];
 	int bar_beats = p_options["bar_beats"];
 	int bar_beats = p_options["bar_beats"];
 
 
-	Ref<AudioStreamOggVorbis> ogg_vorbis_stream = load_from_file(p_source_file);
+	Ref<AudioStreamOggVorbis> ogg_vorbis_stream = AudioStreamOggVorbis::load_from_file(p_source_file);
 	if (ogg_vorbis_stream.is_null()) {
 	if (ogg_vorbis_stream.is_null()) {
 		return ERR_CANT_OPEN;
 		return ERR_CANT_OPEN;
 	}
 	}
@@ -115,135 +114,22 @@ Error ResourceImporterOggVorbis::import(ResourceUID::ID p_source_id, const Strin
 	return ResourceSaver::save(ogg_vorbis_stream, p_save_path + ".oggvorbisstr");
 	return ResourceSaver::save(ogg_vorbis_stream, p_save_path + ".oggvorbisstr");
 }
 }
 
 
-ResourceImporterOggVorbis::ResourceImporterOggVorbis() {
+#ifndef DISABLE_DEPRECATED
+Ref<AudioStreamOggVorbis> ResourceImporterOggVorbis::load_from_buffer(const Vector<uint8_t> &p_stream_data) {
+	return AudioStreamOggVorbis::load_from_buffer(p_stream_data);
 }
 }
 
 
-void ResourceImporterOggVorbis::_bind_methods() {
-	ClassDB::bind_static_method("ResourceImporterOggVorbis", D_METHOD("load_from_buffer", "buffer"), &ResourceImporterOggVorbis::load_from_buffer);
-	ClassDB::bind_static_method("ResourceImporterOggVorbis", D_METHOD("load_from_file", "path"), &ResourceImporterOggVorbis::load_from_file);
+Ref<AudioStreamOggVorbis> ResourceImporterOggVorbis::load_from_file(const String &p_path) {
+	return AudioStreamOggVorbis::load_from_file(p_path);
 }
 }
+#endif
 
 
-Ref<AudioStreamOggVorbis> ResourceImporterOggVorbis::load_from_buffer(const Vector<uint8_t> &file_data) {
-	Ref<AudioStreamOggVorbis> ogg_vorbis_stream;
-	ogg_vorbis_stream.instantiate();
-
-	Ref<OggPacketSequence> ogg_packet_sequence;
-	ogg_packet_sequence.instantiate();
-
-	ogg_stream_state stream_state;
-	ogg_sync_state sync_state;
-	ogg_page page;
-	ogg_packet packet;
-	bool initialized_stream = false;
-
-	ogg_sync_init(&sync_state);
-	int err;
-	size_t cursor = 0;
-	size_t packet_count = 0;
-	bool done = false;
-	while (!done) {
-		err = ogg_sync_check(&sync_state);
-		ERR_FAIL_COND_V_MSG(err != 0, Ref<AudioStreamOggVorbis>(), "Ogg sync error " + itos(err));
-		while (ogg_sync_pageout(&sync_state, &page) != 1) {
-			if (cursor >= size_t(file_data.size())) {
-				done = true;
-				break;
-			}
-			err = ogg_sync_check(&sync_state);
-			ERR_FAIL_COND_V_MSG(err != 0, Ref<AudioStreamOggVorbis>(), "Ogg sync error " + itos(err));
-			char *sync_buf = ogg_sync_buffer(&sync_state, OGG_SYNC_BUFFER_SIZE);
-			err = ogg_sync_check(&sync_state);
-			ERR_FAIL_COND_V_MSG(err != 0, Ref<AudioStreamOggVorbis>(), "Ogg sync error " + itos(err));
-			size_t copy_size = file_data.size() - cursor;
-			if (copy_size > OGG_SYNC_BUFFER_SIZE) {
-				copy_size = OGG_SYNC_BUFFER_SIZE;
-			}
-			memcpy(sync_buf, &file_data[cursor], copy_size);
-			ogg_sync_wrote(&sync_state, copy_size);
-			cursor += copy_size;
-			err = ogg_sync_check(&sync_state);
-			ERR_FAIL_COND_V_MSG(err != 0, Ref<AudioStreamOggVorbis>(), "Ogg sync error " + itos(err));
-		}
-		if (done) {
-			break;
-		}
-		err = ogg_sync_check(&sync_state);
-		ERR_FAIL_COND_V_MSG(err != 0, Ref<AudioStreamOggVorbis>(), "Ogg sync error " + itos(err));
-
-		// Have a page now.
-		if (!initialized_stream) {
-			if (ogg_stream_init(&stream_state, ogg_page_serialno(&page))) {
-				ERR_FAIL_V_MSG(Ref<AudioStreamOggVorbis>(), "Failed allocating memory for Ogg Vorbis stream.");
-			}
-			initialized_stream = true;
-		}
-		ogg_stream_pagein(&stream_state, &page);
-		err = ogg_stream_check(&stream_state);
-		ERR_FAIL_COND_V_MSG(err != 0, Ref<AudioStreamOggVorbis>(), "Ogg stream error " + itos(err));
-		int desync_iters = 0;
-
-		RBMap<uint64_t, Vector<Vector<uint8_t>>> sorted_packets;
-		int64_t granule_pos = 0;
-
-		while (true) {
-			err = ogg_stream_packetout(&stream_state, &packet);
-			if (err == -1) {
-				// According to the docs this is usually recoverable, but don't sit here spinning forever.
-				desync_iters++;
-				WARN_PRINT_ONCE("Desync during ogg import.");
-				ERR_FAIL_COND_V_MSG(desync_iters > 100, Ref<AudioStreamOggVorbis>(), "Packet sync issue during Ogg import");
-				continue;
-			} else if (err == 0) {
-				// Not enough data to fully reconstruct a packet. Go on to the next page.
-				break;
-			}
-			if (packet_count == 0 && vorbis_synthesis_idheader(&packet) == 0) {
-				print_verbose("Found a non-vorbis-header packet in a header position");
-				// Clearly this logical stream is not a vorbis stream, so destroy it and try again with the next page.
-				if (initialized_stream) {
-					ogg_stream_clear(&stream_state);
-					initialized_stream = false;
-				}
-				break;
-			}
-			if (packet.granulepos > granule_pos) {
-				granule_pos = packet.granulepos;
-			}
-
-			if (packet.bytes > 0) {
-				PackedByteArray data;
-				data.resize(packet.bytes);
-				memcpy(data.ptrw(), packet.packet, packet.bytes);
-				sorted_packets[granule_pos].push_back(data);
-				packet_count++;
-			}
-		}
-		Vector<Vector<uint8_t>> packet_data;
-		for (const KeyValue<uint64_t, Vector<Vector<uint8_t>>> &pair : sorted_packets) {
-			for (const Vector<uint8_t> &packets : pair.value) {
-				packet_data.push_back(packets);
-			}
-		}
-		if (initialized_stream && packet_data.size() > 0) {
-			ogg_packet_sequence->push_page(ogg_page_granulepos(&page), packet_data);
-		}
-	}
-	if (initialized_stream) {
-		ogg_stream_clear(&stream_state);
-	}
-	ogg_sync_clear(&sync_state);
-
-	if (ogg_packet_sequence->get_packet_granule_positions().is_empty()) {
-		ERR_FAIL_V_MSG(Ref<AudioStreamOggVorbis>(), "Ogg Vorbis decoding failed. Check that your data is a valid Ogg Vorbis audio stream.");
-	}
-
-	ogg_vorbis_stream->set_packet_sequence(ogg_packet_sequence);
-
-	return ogg_vorbis_stream;
+void ResourceImporterOggVorbis::_bind_methods() {
+#ifndef DISABLE_DEPRECATED
+	ClassDB::bind_static_method("ResourceImporterOggVorbis", D_METHOD("load_from_buffer", "stream_data"), &ResourceImporterOggVorbis::load_from_buffer);
+	ClassDB::bind_static_method("ResourceImporterOggVorbis", D_METHOD("load_from_file", "path"), &ResourceImporterOggVorbis::load_from_file);
+#endif
 }
 }
 
 
-Ref<AudioStreamOggVorbis> ResourceImporterOggVorbis::load_from_file(const String &p_path) {
-	Vector<uint8_t> file_data = FileAccess::get_file_as_bytes(p_path);
-	ERR_FAIL_COND_V_MSG(file_data.is_empty(), Ref<AudioStreamOggVorbis>(), "Cannot open file '" + p_path + "'.");
-	return load_from_buffer(file_data);
+ResourceImporterOggVorbis::ResourceImporterOggVorbis() {
 }
 }

+ 4 - 5
modules/vorbis/resource_importer_ogg_vorbis.h

@@ -38,10 +38,6 @@
 class ResourceImporterOggVorbis : public ResourceImporter {
 class ResourceImporterOggVorbis : public ResourceImporter {
 	GDCLASS(ResourceImporterOggVorbis, ResourceImporter);
 	GDCLASS(ResourceImporterOggVorbis, ResourceImporter);
 
 
-	enum {
-		OGG_SYNC_BUFFER_SIZE = 8192,
-	};
-
 protected:
 protected:
 	static void _bind_methods();
 	static void _bind_methods();
 
 
@@ -51,8 +47,11 @@ public:
 	virtual void show_advanced_options(const String &p_path) override;
 	virtual void show_advanced_options(const String &p_path) override;
 #endif
 #endif
 
 
+#ifndef DISABLE_DEPRECATED
 	static Ref<AudioStreamOggVorbis> load_from_file(const String &p_path);
 	static Ref<AudioStreamOggVorbis> load_from_file(const String &p_path);
-	static Ref<AudioStreamOggVorbis> load_from_buffer(const Vector<uint8_t> &file_data);
+	static Ref<AudioStreamOggVorbis> load_from_buffer(const Vector<uint8_t> &p_stream_data);
+#endif
+
 	virtual void get_recognized_extensions(List<String> *p_extensions) const override;
 	virtual void get_recognized_extensions(List<String> *p_extensions) const override;
 	virtual String get_save_extension() const override;
 	virtual String get_save_extension() const override;
 	virtual String get_resource_type() const override;
 	virtual String get_resource_type() const override;

+ 51 - 51
scene/resources/audio_stream_wav.cpp

@@ -723,58 +723,12 @@ Ref<AudioSample> AudioStreamWAV::generate_sample() const {
 	return sample;
 	return sample;
 }
 }
 
 
-void AudioStreamWAV::_bind_methods() {
-	ClassDB::bind_static_method("AudioStreamWAV", D_METHOD("load_from_file", "path", "options"), &AudioStreamWAV::load_from_file, DEFVAL(Dictionary()));
-	ClassDB::bind_static_method("AudioStreamWAV", D_METHOD("load_from_buffer", "buffer", "options"), &AudioStreamWAV::load_from_buffer, DEFVAL(Dictionary()));
-
-	ClassDB::bind_method(D_METHOD("set_data", "data"), &AudioStreamWAV::set_data);
-	ClassDB::bind_method(D_METHOD("get_data"), &AudioStreamWAV::get_data);
-
-	ClassDB::bind_method(D_METHOD("set_format", "format"), &AudioStreamWAV::set_format);
-	ClassDB::bind_method(D_METHOD("get_format"), &AudioStreamWAV::get_format);
-
-	ClassDB::bind_method(D_METHOD("set_loop_mode", "loop_mode"), &AudioStreamWAV::set_loop_mode);
-	ClassDB::bind_method(D_METHOD("get_loop_mode"), &AudioStreamWAV::get_loop_mode);
-
-	ClassDB::bind_method(D_METHOD("set_loop_begin", "loop_begin"), &AudioStreamWAV::set_loop_begin);
-	ClassDB::bind_method(D_METHOD("get_loop_begin"), &AudioStreamWAV::get_loop_begin);
-
-	ClassDB::bind_method(D_METHOD("set_loop_end", "loop_end"), &AudioStreamWAV::set_loop_end);
-	ClassDB::bind_method(D_METHOD("get_loop_end"), &AudioStreamWAV::get_loop_end);
-
-	ClassDB::bind_method(D_METHOD("set_mix_rate", "mix_rate"), &AudioStreamWAV::set_mix_rate);
-	ClassDB::bind_method(D_METHOD("get_mix_rate"), &AudioStreamWAV::get_mix_rate);
-
-	ClassDB::bind_method(D_METHOD("set_stereo", "stereo"), &AudioStreamWAV::set_stereo);
-	ClassDB::bind_method(D_METHOD("is_stereo"), &AudioStreamWAV::is_stereo);
-
-	ClassDB::bind_method(D_METHOD("save_to_wav", "path"), &AudioStreamWAV::save_to_wav);
-
-	ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_data", "get_data");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "format", PROPERTY_HINT_ENUM, "8-Bit,16-Bit,IMA ADPCM,Quite OK Audio"), "set_format", "get_format");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_mode", PROPERTY_HINT_ENUM, "Disabled,Forward,Ping-Pong,Backward"), "set_loop_mode", "get_loop_mode");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_begin"), "set_loop_begin", "get_loop_begin");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_end"), "set_loop_end", "get_loop_end");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "mix_rate"), "set_mix_rate", "get_mix_rate");
-	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stereo"), "set_stereo", "is_stereo");
-
-	BIND_ENUM_CONSTANT(FORMAT_8_BITS);
-	BIND_ENUM_CONSTANT(FORMAT_16_BITS);
-	BIND_ENUM_CONSTANT(FORMAT_IMA_ADPCM);
-	BIND_ENUM_CONSTANT(FORMAT_QOA);
-
-	BIND_ENUM_CONSTANT(LOOP_DISABLED);
-	BIND_ENUM_CONSTANT(LOOP_FORWARD);
-	BIND_ENUM_CONSTANT(LOOP_PINGPONG);
-	BIND_ENUM_CONSTANT(LOOP_BACKWARD);
-}
-
-Ref<AudioStreamWAV> AudioStreamWAV::load_from_buffer(const Vector<uint8_t> &p_file_data, const Dictionary &p_options) {
+Ref<AudioStreamWAV> AudioStreamWAV::load_from_buffer(const Vector<uint8_t> &p_stream_data, const Dictionary &p_options) {
 	// /* STEP 1, READ WAVE FILE */
 	// /* STEP 1, READ WAVE FILE */
 
 
 	Ref<FileAccessMemory> file;
 	Ref<FileAccessMemory> file;
 	file.instantiate();
 	file.instantiate();
-	Error err = file->open_custom(p_file_data.ptr(), p_file_data.size());
+	Error err = file->open_custom(p_stream_data.ptr(), p_stream_data.size());
 	ERR_FAIL_COND_V_MSG(err != OK, Ref<AudioStreamWAV>(), "Cannot create memfile for WAV file buffer.");
 	ERR_FAIL_COND_V_MSG(err != OK, Ref<AudioStreamWAV>(), "Cannot create memfile for WAV file buffer.");
 
 
 	/* CHECK RIFF */
 	/* CHECK RIFF */
@@ -1223,9 +1177,55 @@ Ref<AudioStreamWAV> AudioStreamWAV::load_from_buffer(const Vector<uint8_t> &p_fi
 }
 }
 
 
 Ref<AudioStreamWAV> AudioStreamWAV::load_from_file(const String &p_path, const Dictionary &p_options) {
 Ref<AudioStreamWAV> AudioStreamWAV::load_from_file(const String &p_path, const Dictionary &p_options) {
-	Vector<uint8_t> file_data = FileAccess::get_file_as_bytes(p_path);
-	ERR_FAIL_COND_V_MSG(file_data.is_empty(), Ref<AudioStreamWAV>(), vformat("Cannot open file '%s'.", p_path));
-	return load_from_buffer(file_data, p_options);
+	const Vector<uint8_t> stream_data = FileAccess::get_file_as_bytes(p_path);
+	ERR_FAIL_COND_V_MSG(stream_data.is_empty(), Ref<AudioStreamWAV>(), vformat("Cannot open file '%s'.", p_path));
+	return load_from_buffer(stream_data, p_options);
+}
+
+void AudioStreamWAV::_bind_methods() {
+	ClassDB::bind_static_method("AudioStreamWAV", D_METHOD("load_from_buffer", "stream_data", "options"), &AudioStreamWAV::load_from_buffer, DEFVAL(Dictionary()));
+	ClassDB::bind_static_method("AudioStreamWAV", D_METHOD("load_from_file", "path", "options"), &AudioStreamWAV::load_from_file, DEFVAL(Dictionary()));
+
+	ClassDB::bind_method(D_METHOD("set_data", "data"), &AudioStreamWAV::set_data);
+	ClassDB::bind_method(D_METHOD("get_data"), &AudioStreamWAV::get_data);
+
+	ClassDB::bind_method(D_METHOD("set_format", "format"), &AudioStreamWAV::set_format);
+	ClassDB::bind_method(D_METHOD("get_format"), &AudioStreamWAV::get_format);
+
+	ClassDB::bind_method(D_METHOD("set_loop_mode", "loop_mode"), &AudioStreamWAV::set_loop_mode);
+	ClassDB::bind_method(D_METHOD("get_loop_mode"), &AudioStreamWAV::get_loop_mode);
+
+	ClassDB::bind_method(D_METHOD("set_loop_begin", "loop_begin"), &AudioStreamWAV::set_loop_begin);
+	ClassDB::bind_method(D_METHOD("get_loop_begin"), &AudioStreamWAV::get_loop_begin);
+
+	ClassDB::bind_method(D_METHOD("set_loop_end", "loop_end"), &AudioStreamWAV::set_loop_end);
+	ClassDB::bind_method(D_METHOD("get_loop_end"), &AudioStreamWAV::get_loop_end);
+
+	ClassDB::bind_method(D_METHOD("set_mix_rate", "mix_rate"), &AudioStreamWAV::set_mix_rate);
+	ClassDB::bind_method(D_METHOD("get_mix_rate"), &AudioStreamWAV::get_mix_rate);
+
+	ClassDB::bind_method(D_METHOD("set_stereo", "stereo"), &AudioStreamWAV::set_stereo);
+	ClassDB::bind_method(D_METHOD("is_stereo"), &AudioStreamWAV::is_stereo);
+
+	ClassDB::bind_method(D_METHOD("save_to_wav", "path"), &AudioStreamWAV::save_to_wav);
+
+	ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_data", "get_data");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "format", PROPERTY_HINT_ENUM, "8-Bit,16-Bit,IMA ADPCM,Quite OK Audio"), "set_format", "get_format");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_mode", PROPERTY_HINT_ENUM, "Disabled,Forward,Ping-Pong,Backward"), "set_loop_mode", "get_loop_mode");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_begin"), "set_loop_begin", "get_loop_begin");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_end"), "set_loop_end", "get_loop_end");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "mix_rate"), "set_mix_rate", "get_mix_rate");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stereo"), "set_stereo", "is_stereo");
+
+	BIND_ENUM_CONSTANT(FORMAT_8_BITS);
+	BIND_ENUM_CONSTANT(FORMAT_16_BITS);
+	BIND_ENUM_CONSTANT(FORMAT_IMA_ADPCM);
+	BIND_ENUM_CONSTANT(FORMAT_QOA);
+
+	BIND_ENUM_CONSTANT(LOOP_DISABLED);
+	BIND_ENUM_CONSTANT(LOOP_FORWARD);
+	BIND_ENUM_CONSTANT(LOOP_PINGPONG);
+	BIND_ENUM_CONSTANT(LOOP_BACKWARD);
 }
 }
 
 
 AudioStreamWAV::AudioStreamWAV() {}
 AudioStreamWAV::AudioStreamWAV() {}

+ 2 - 1
scene/resources/audio_stream_wav.h

@@ -32,6 +32,7 @@
 #define AUDIO_STREAM_WAV_H
 #define AUDIO_STREAM_WAV_H
 
 
 #include "servers/audio/audio_stream.h"
 #include "servers/audio/audio_stream.h"
+
 #include "thirdparty/misc/qoa.h"
 #include "thirdparty/misc/qoa.h"
 
 
 class AudioStreamWAV;
 class AudioStreamWAV;
@@ -141,8 +142,8 @@ protected:
 	static void _bind_methods();
 	static void _bind_methods();
 
 
 public:
 public:
+	static Ref<AudioStreamWAV> load_from_buffer(const Vector<uint8_t> &p_stream_data, const Dictionary &p_options);
 	static Ref<AudioStreamWAV> load_from_file(const String &p_path, const Dictionary &p_options);
 	static Ref<AudioStreamWAV> load_from_file(const String &p_path, const Dictionary &p_options);
-	static Ref<AudioStreamWAV> load_from_buffer(const Vector<uint8_t> &p_file_data, const Dictionary &p_options);
 
 
 	void set_format(Format p_format);
 	void set_format(Format p_format);
 	Format get_format() const;
 	Format get_format() const;