1
0
Fork 0

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;