Add AudioStreamMP3 load_from_file/load_from_buffer and harmonize other audio streams

Move OggVorbis and MP3 loading code to their AudioStream class, matching how it's done for WAV.

The duplicate functions in ResourceImporterOggVorbis are now deprecated.

Co-authored-by: MaxIsJoe <34368774+MaxIsJoe@users.noreply.github.com>
This commit is contained in:
Rémi Verschelde 2024-12-12 12:25:05 +01:00
parent 0e3a5eda86
commit 4396f8fbd3
14 changed files with 257 additions and 236 deletions

View file

@ -13,11 +13,11 @@
<methods>
<method name="load_from_buffer" qualifiers="static">
<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="{}" />
<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>
</method>
<method name="load_from_file" qualifiers="static">
@ -25,7 +25,8 @@
<param index="0" name="path" type="String" />
<param index="1" name="options" type="Dictionary" default="{}" />
<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:
[codeblock]
@onready var audio_player = $AudioStreamPlayer

View file

@ -312,7 +312,24 @@ Ref<AudioSample> AudioStreamMP3::generate_sample() const {
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() {
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("get_data"), &AudioStreamMP3::get_data);

View file

@ -113,6 +113,9 @@ protected:
static void _bind_methods();
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);
virtual bool has_loop() const override;

View file

@ -5,9 +5,26 @@
</brief_description>
<description>
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>
<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>
<member name="bar_beats" type="int" setter="set_bar_beats" getter="get_bar_beats" default="4">
</member>

View file

@ -85,35 +85,15 @@ void ResourceImporterMP3::get_import_options(const String &p_path, List<ImportOp
bool ResourceImporterMP3::has_advanced_options() const {
return true;
}
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()) {
AudioStreamImportSettingsDialog::get_singleton()->edit(p_path, "mp3", mp3_stream);
}
}
#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) {
bool loop = p_options["loop"];
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 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()) {
return ERR_CANT_OPEN;
}
mp3_stream->set_loop(loop);
mp3_stream->set_loop_offset(loop_offset);
mp3_stream->set_bpm(bpm);

View file

@ -55,7 +55,6 @@ public:
virtual bool has_advanced_options() const override;
virtual void show_advanced_options(const String &p_path) override;
#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;

View file

@ -30,8 +30,6 @@
#include "audio_stream_ogg_vorbis.h"
#include "modules/vorbis/resource_importer_ogg_vorbis.h"
#include <ogg/ogg.h>
int AudioStreamPlaybackOggVorbis::_mix_internal(AudioFrame *p_buffer, int p_frames) {
@ -546,8 +544,134 @@ Ref<AudioSample> AudioStreamOggVorbis::generate_sample() const {
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() {
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_method(D_METHOD("set_packet_sequence", "packet_sequence"), &AudioStreamOggVorbis::set_packet_sequence);
@ -579,11 +703,3 @@ void AudioStreamOggVorbis::_bind_methods() {
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);
}

View file

@ -140,7 +140,8 @@ protected:
public:
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);
virtual bool has_loop() const override;

View file

@ -12,16 +12,16 @@
<methods>
<method name="load_from_buffer" qualifiers="static">
<return type="AudioStreamOggVorbis" />
<param index="0" name="buffer" type="PackedByteArray" />
<param index="0" name="stream_data" type="PackedByteArray" />
<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>
</method>
<method name="load_from_file" qualifiers="static">
<return type="AudioStreamOggVorbis" />
<param index="0" name="path" type="String" />
<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>
</method>
</methods>

View file

@ -12,18 +12,18 @@
<link title="Importing audio samples">$DOCS_URL/tutorials/assets_pipeline/importing_audio_samples.html</link>
</tutorials>
<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" />
<param index="0" name="buffer" type="PackedByteArray" />
<param index="0" name="stream_data" type="PackedByteArray" />
<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>
</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" />
<param index="0" name="path" type="String" />
<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>
</method>
</methods>

View file

@ -81,13 +81,12 @@ void ResourceImporterOggVorbis::get_import_options(const String &p_path, List<Im
}
#ifdef TOOLS_ENABLED
bool ResourceImporterOggVorbis::has_advanced_options() const {
return true;
}
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()) {
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 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()) {
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");
}
ResourceImporterOggVorbis::ResourceImporterOggVorbis() {
}
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_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;
#ifndef DISABLE_DEPRECATED
Ref<AudioStreamOggVorbis> ResourceImporterOggVorbis::load_from_buffer(const Vector<uint8_t> &p_stream_data) {
return AudioStreamOggVorbis::load_from_buffer(p_stream_data);
}
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);
return AudioStreamOggVorbis::load_from_file(p_path);
}
#endif
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
}
ResourceImporterOggVorbis::ResourceImporterOggVorbis() {
}

View file

@ -38,10 +38,6 @@
class ResourceImporterOggVorbis : public ResourceImporter {
GDCLASS(ResourceImporterOggVorbis, ResourceImporter);
enum {
OGG_SYNC_BUFFER_SIZE = 8192,
};
protected:
static void _bind_methods();
@ -51,8 +47,11 @@ public:
virtual void show_advanced_options(const String &p_path) override;
#endif
#ifndef DISABLE_DEPRECATED
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 String get_save_extension() const override;
virtual String get_resource_type() const override;

View file

@ -723,58 +723,12 @@ Ref<AudioSample> AudioStreamWAV::generate_sample() const {
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 */
Ref<FileAccessMemory> file;
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.");
/* 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) {
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() {}

View file

@ -32,6 +32,7 @@
#define AUDIO_STREAM_WAV_H
#include "servers/audio/audio_stream.h"
#include "thirdparty/misc/qoa.h"
class AudioStreamWAV;
@ -141,8 +142,8 @@ protected:
static void _bind_methods();
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_buffer(const Vector<uint8_t> &p_file_data, const Dictionary &p_options);
void set_format(Format p_format);
Format get_format() const;