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:
@ -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].
|
Creates a new [AudioStreamWAV] instance from the given buffer. The buffer must contain WAV data.
|
||||||
The usage of [param options] is identical to [method AudioStreamWAV.load_from_file].
|
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
|
||||||
|
|||||||
@ -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);
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
|
||||||
}
|
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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_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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<AudioStreamOggVorbis> ResourceImporterOggVorbis::load_from_file(const String &p_path) {
|
Ref<AudioStreamOggVorbis> ResourceImporterOggVorbis::load_from_file(const String &p_path) {
|
||||||
Vector<uint8_t> file_data = FileAccess::get_file_as_bytes(p_path);
|
return AudioStreamOggVorbis::load_from_file(p_path);
|
||||||
ERR_FAIL_COND_V_MSG(file_data.is_empty(), Ref<AudioStreamOggVorbis>(), "Cannot open file '" + p_path + "'.");
|
}
|
||||||
return load_from_buffer(file_data);
|
#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() {
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -723,58 +723,12 @@ Ref<AudioSample> AudioStreamWAV::generate_sample() const {
|
|||||||
return sample;
|
return sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioStreamWAV::_bind_methods() {
|
Ref<AudioStreamWAV> AudioStreamWAV::load_from_buffer(const Vector<uint8_t> &p_stream_data, const Dictionary &p_options) {
|
||||||
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) {
|
|
||||||
// /* 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);
|
const Vector<uint8_t> stream_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));
|
ERR_FAIL_COND_V_MSG(stream_data.is_empty(), Ref<AudioStreamWAV>(), vformat("Cannot open file '%s'.", p_path));
|
||||||
return load_from_buffer(file_data, p_options);
|
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() {}
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user