initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled

This commit is contained in:
2025-09-16 20:46:46 -04:00
commit 9d30169a8d
13378 changed files with 7050105 additions and 0 deletions

69
modules/vorbis/SCsub Normal file
View File

@@ -0,0 +1,69 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
env_vorbis = env_modules.Clone()
# Thirdparty source files
thirdparty_obj = []
if env["builtin_libvorbis"]:
thirdparty_dir = "#thirdparty/libvorbis/"
thirdparty_sources = [
# "barkmel.c",
"bitrate.c",
"block.c",
"codebook.c",
"envelope.c",
"floor0.c",
"floor1.c",
"info.c",
"lookup.c",
"lpc.c",
"lsp.c",
"mapping0.c",
"mdct.c",
"psy.c",
# "psytune.c",
"registry.c",
"res0.c",
"sharedbook.c",
"smallft.c",
"synthesis.c",
# "tone.c",
"vorbisfile.c",
"window.c",
]
if env.editor_build:
thirdparty_sources += [
"analysis.c",
"vorbisenc.c",
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
env_vorbis.Prepend(CPPEXTPATH=[thirdparty_dir])
# also requires libogg
if env["builtin_libogg"]:
env_vorbis.Prepend(CPPEXTPATH=["#thirdparty/libogg"])
env_thirdparty = env_vorbis.Clone()
env_thirdparty.disable_warnings()
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
env.modules_sources += thirdparty_obj
# Godot source files
module_obj = []
env_vorbis.add_source_files(module_obj, "*.cpp")
env.modules_sources += module_obj
# Needed to force rebuilding the module files when the thirdparty library is updated.
env.Depends(module_obj, thirdparty_obj)

View File

@@ -0,0 +1,734 @@
/**************************************************************************/
/* audio_stream_ogg_vorbis.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "audio_stream_ogg_vorbis.h"
#include <ogg/ogg.h>
int AudioStreamPlaybackOggVorbis::_mix_internal(AudioFrame *p_buffer, int p_frames) {
ERR_FAIL_COND_V(!ready, 0);
if (!active) {
return 0;
}
int todo = p_frames;
int beat_length_frames = -1;
bool use_loop = looping_override ? looping : vorbis_stream->loop;
if (use_loop && vorbis_stream->get_bpm() > 0 && vorbis_stream->get_beat_count() > 0) {
beat_length_frames = vorbis_stream->get_beat_count() * vorbis_data->get_sampling_rate() * 60 / vorbis_stream->get_bpm();
}
while (todo > 0 && active) {
AudioFrame *buffer = p_buffer;
buffer += p_frames - todo;
int to_mix = todo;
if (beat_length_frames >= 0 && (beat_length_frames - (int)frames_mixed) < to_mix) {
to_mix = MAX(0, beat_length_frames - (int)frames_mixed);
}
int mixed = _mix_frames_vorbis(buffer, to_mix);
ERR_FAIL_COND_V(mixed < 0, 0);
todo -= mixed;
frames_mixed += mixed;
if (loop_fade_remaining < FADE_SIZE) {
int to_fade = loop_fade_remaining + MIN(FADE_SIZE - loop_fade_remaining, mixed);
for (int i = loop_fade_remaining; i < to_fade; i++) {
buffer[i - loop_fade_remaining] += loop_fade[i] * (float(FADE_SIZE - i) / float(FADE_SIZE));
}
loop_fade_remaining = to_fade;
}
if (beat_length_frames >= 0) {
/**
* Length determined by beat length
* This code is commented out because, in practice, it is preferred that the fade
* is done by the transitioner and this stream just goes on until it ends while fading out.
*
* End fade implementation is left here for reference in case at some point this feature
* is desired.
if (!beat_loop && (int)frames_mixed > beat_length_frames - FADE_SIZE) {
print_line("beat length fade/after mix?");
//No loop, just fade and finish
for (int i = 0; i < mixed; i++) {
int idx = frames_mixed + i - mixed;
buffer[i] *= 1.0 - float(MAX(0, (idx - (beat_length_frames - FADE_SIZE)))) / float(FADE_SIZE);
}
if ((int)frames_mixed == beat_length_frames) {
for (int i = p_frames - todo; i < p_frames; i++) {
p_buffer[i] = AudioFrame(0, 0);
}
active = false;
break;
}
} else
**/
if (use_loop && beat_length_frames <= (int)frames_mixed) {
// End of file when doing beat-based looping. <= used instead of == because importer editing
if (!have_packets_left && !have_samples_left) {
//Nothing remaining, so do nothing.
loop_fade_remaining = FADE_SIZE;
} else {
// Add some loop fade;
int faded_mix = _mix_frames_vorbis(loop_fade, FADE_SIZE);
for (int i = faded_mix; i < FADE_SIZE; i++) {
// In case lesss was mixed, pad with zeros
loop_fade[i] = AudioFrame(0, 0);
}
loop_fade_remaining = 0;
}
seek(vorbis_stream->loop_offset);
loops++;
// We still have buffer to fill, start from this element in the next iteration.
continue;
}
}
if (!have_packets_left && !have_samples_left) {
// Actual end of file!
bool is_not_empty = mixed > 0 || vorbis_stream->get_length() > 0;
if (use_loop && is_not_empty) {
//loop
seek(vorbis_stream->loop_offset);
loops++;
// We still have buffer to fill, start from this element in the next iteration.
} else {
for (int i = p_frames - todo; i < p_frames; i++) {
p_buffer[i] = AudioFrame(0, 0);
}
active = false;
}
}
}
return p_frames - todo;
}
int AudioStreamPlaybackOggVorbis::_mix_frames_vorbis(AudioFrame *p_buffer, int p_frames) {
ERR_FAIL_COND_V(!ready, p_frames);
if (!have_samples_left) {
ogg_packet *packet = nullptr;
int err;
if (!vorbis_data_playback->next_ogg_packet(&packet)) {
have_packets_left = false;
WARN_PRINT("ran out of packets in stream");
return -1;
}
err = vorbis_synthesis(&block, packet);
ERR_FAIL_COND_V_MSG(err != 0, p_frames, "Error during vorbis synthesis " + itos(err));
err = vorbis_synthesis_blockin(&dsp_state, &block);
ERR_FAIL_COND_V_MSG(err != 0, p_frames, "Error during vorbis block processing " + itos(err));
have_packets_left = !packet->e_o_s;
}
float **pcm; // Accessed with pcm[channel_idx][sample_idx].
int frames = vorbis_synthesis_pcmout(&dsp_state, &pcm);
if (frames > p_frames) {
frames = p_frames;
have_samples_left = true;
} else {
have_samples_left = false;
}
if (info.channels > 1) {
for (int frame = 0; frame < frames; frame++) {
p_buffer[frame].left = pcm[0][frame];
p_buffer[frame].right = pcm[1][frame];
}
} else {
for (int frame = 0; frame < frames; frame++) {
p_buffer[frame].left = pcm[0][frame];
p_buffer[frame].right = pcm[0][frame];
}
}
vorbis_synthesis_read(&dsp_state, frames);
return frames;
}
float AudioStreamPlaybackOggVorbis::get_stream_sampling_rate() {
return vorbis_data->get_sampling_rate();
}
bool AudioStreamPlaybackOggVorbis::_alloc_vorbis() {
vorbis_info_init(&info);
info_is_allocated = true;
vorbis_comment_init(&comment);
comment_is_allocated = true;
ERR_FAIL_COND_V(vorbis_data.is_null(), false);
vorbis_data_playback = vorbis_data->instantiate_playback();
ogg_packet *packet;
int err;
for (int i = 0; i < 3; i++) {
if (!vorbis_data_playback->next_ogg_packet(&packet)) {
WARN_PRINT("Not enough packets to parse header");
return false;
}
err = vorbis_synthesis_headerin(&info, &comment, packet);
ERR_FAIL_COND_V_MSG(err != 0, false, "Error parsing header");
}
err = vorbis_synthesis_init(&dsp_state, &info);
ERR_FAIL_COND_V_MSG(err != 0, false, "Error initializing dsp state");
dsp_state_is_allocated = true;
err = vorbis_block_init(&dsp_state, &block);
ERR_FAIL_COND_V_MSG(err != 0, false, "Error initializing block");
block_is_allocated = true;
ready = true;
return true;
}
void AudioStreamPlaybackOggVorbis::start(double p_from_pos) {
ERR_FAIL_COND(!ready);
loop_fade_remaining = FADE_SIZE;
active = true;
seek(p_from_pos);
loops = 0;
begin_resample();
}
void AudioStreamPlaybackOggVorbis::stop() {
active = false;
}
bool AudioStreamPlaybackOggVorbis::is_playing() const {
return active;
}
int AudioStreamPlaybackOggVorbis::get_loop_count() const {
return loops;
}
double AudioStreamPlaybackOggVorbis::get_playback_position() const {
return double(frames_mixed) / (double)vorbis_data->get_sampling_rate();
}
void AudioStreamPlaybackOggVorbis::tag_used_streams() {
vorbis_stream->tag_used(get_playback_position());
}
void AudioStreamPlaybackOggVorbis::set_parameter(const StringName &p_name, const Variant &p_value) {
if (p_name == SNAME("looping")) {
if (p_value == Variant()) {
looping_override = false;
looping = false;
} else {
looping_override = true;
looping = p_value;
}
}
}
Variant AudioStreamPlaybackOggVorbis::get_parameter(const StringName &p_name) const {
if (looping_override && p_name == SNAME("looping")) {
return looping;
}
return Variant();
}
void AudioStreamPlaybackOggVorbis::seek(double p_time) {
ERR_FAIL_COND(!ready);
ERR_FAIL_COND(vorbis_stream.is_null());
if (!active) {
return;
}
if (p_time >= vorbis_stream->get_length()) {
p_time = 0;
}
frames_mixed = uint32_t(vorbis_data->get_sampling_rate() * p_time);
const int64_t desired_sample = p_time * get_stream_sampling_rate();
if (!vorbis_data_playback->seek_page(desired_sample)) {
WARN_PRINT("seek failed");
return;
}
// We want to start decoding before the page that we expect the sample to be in (the sample may
// be part of a partial packet across page boundaries). Otherwise, the decoder may not have
// synchronized before reaching the sample.
int64_t start_page_number = vorbis_data_playback->get_page_number() - 1;
if (start_page_number < 0) {
start_page_number = 0;
}
while (true) {
ogg_packet *packet;
int err;
// We start at an unknown granule position.
int64_t granule_pos = -1;
// Decode data until we get to the desired sample or notice that we have read past it.
vorbis_data_playback->set_page_number(start_page_number);
vorbis_synthesis_restart(&dsp_state);
while (true) {
if (!vorbis_data_playback->next_ogg_packet(&packet)) {
WARN_PRINT_ONCE("Seeking beyond limits");
return;
}
err = vorbis_synthesis(&block, packet);
if (err != OV_ENOTAUDIO) {
ERR_FAIL_COND_MSG(err != 0, "Error during vorbis synthesis " + itos(err) + ".");
err = vorbis_synthesis_blockin(&dsp_state, &block);
ERR_FAIL_COND_MSG(err != 0, "Error during vorbis block processing " + itos(err) + ".");
int samples_out = vorbis_synthesis_pcmout(&dsp_state, nullptr);
if (granule_pos < 0) {
// We don't know where we are yet, so just keep on decoding.
err = vorbis_synthesis_read(&dsp_state, samples_out);
ERR_FAIL_COND_MSG(err != 0, "Error during vorbis read updating " + itos(err) + ".");
} else if (granule_pos + samples_out >= desired_sample) {
// Our sample is in this block. Skip the beginning of the block up to the sample, then
// return.
int skip_samples = (int)(desired_sample - granule_pos);
err = vorbis_synthesis_read(&dsp_state, skip_samples);
ERR_FAIL_COND_MSG(err != 0, "Error during vorbis read updating " + itos(err) + ".");
have_samples_left = skip_samples < samples_out;
have_packets_left = !packet->e_o_s;
return;
} else {
// Our sample is not in this block. Skip it.
err = vorbis_synthesis_read(&dsp_state, samples_out);
ERR_FAIL_COND_MSG(err != 0, "Error during vorbis read updating " + itos(err) + ".");
granule_pos += samples_out;
}
}
if (packet->granulepos != -1) {
// We found an update to our granule position.
granule_pos = packet->granulepos;
if (granule_pos > desired_sample) {
// We've read past our sample. We need to start on an earlier page.
if (start_page_number == 0) {
// We didn't find the sample even reading from the beginning.
have_samples_left = false;
have_packets_left = !packet->e_o_s;
return;
}
start_page_number--;
break;
}
}
if (packet->e_o_s) {
// We've reached the end of the stream and didn't find our sample.
have_samples_left = false;
have_packets_left = false;
return;
}
}
}
}
void AudioStreamPlaybackOggVorbis::set_is_sample(bool p_is_sample) {
_is_sample = p_is_sample;
}
bool AudioStreamPlaybackOggVorbis::get_is_sample() const {
return _is_sample;
}
Ref<AudioSamplePlayback> AudioStreamPlaybackOggVorbis::get_sample_playback() const {
return sample_playback;
}
void AudioStreamPlaybackOggVorbis::set_sample_playback(const Ref<AudioSamplePlayback> &p_playback) {
sample_playback = p_playback;
if (sample_playback.is_valid()) {
sample_playback->stream_playback = Ref<AudioStreamPlayback>(this);
}
}
AudioStreamPlaybackOggVorbis::~AudioStreamPlaybackOggVorbis() {
if (block_is_allocated) {
vorbis_block_clear(&block);
}
if (dsp_state_is_allocated) {
vorbis_dsp_clear(&dsp_state);
}
if (comment_is_allocated) {
vorbis_comment_clear(&comment);
}
if (info_is_allocated) {
vorbis_info_clear(&info);
}
}
Ref<AudioStreamPlayback> AudioStreamOggVorbis::instantiate_playback() {
Ref<AudioStreamPlaybackOggVorbis> ovs;
ERR_FAIL_COND_V(packet_sequence.is_null(), nullptr);
ovs.instantiate();
ovs->vorbis_stream = Ref<AudioStreamOggVorbis>(this);
ovs->vorbis_data = packet_sequence;
ovs->frames_mixed = 0;
ovs->active = false;
ovs->loops = 0;
if (ovs->_alloc_vorbis()) {
return ovs;
}
// Failed to allocate data structures.
return nullptr;
}
String AudioStreamOggVorbis::get_stream_name() const {
return ""; //return stream_name;
}
void AudioStreamOggVorbis::maybe_update_info() {
ERR_FAIL_COND(packet_sequence.is_null());
vorbis_info info;
vorbis_comment comment;
int err;
vorbis_info_init(&info);
vorbis_comment_init(&comment);
Ref<OggPacketSequencePlayback> packet_sequence_playback = packet_sequence->instantiate_playback();
for (int i = 0; i < 3; i++) {
ogg_packet *packet;
if (!packet_sequence_playback->next_ogg_packet(&packet)) {
WARN_PRINT("Failed to get header packet");
break;
}
if (i == 0) {
packet->b_o_s = 1;
ERR_FAIL_COND(!vorbis_synthesis_idheader(packet));
}
err = vorbis_synthesis_headerin(&info, &comment, packet);
ERR_FAIL_COND_MSG(err != 0, "Error parsing header packet " + itos(i) + ": " + itos(err));
}
Dictionary dictionary;
for (int i = 0; i < comment.comments; i++) {
String c = String::utf8(comment.user_comments[i]);
int equals = c.find_char('=');
if (equals == -1) {
WARN_PRINT("Invalid comment in Ogg Vorbis file.");
continue;
}
String tag = c.substr(0, equals);
String tag_value = c.substr(equals + 1);
dictionary[tag.to_lower()] = tag_value;
}
tags = dictionary;
packet_sequence->set_sampling_rate(info.rate);
vorbis_comment_clear(&comment);
vorbis_info_clear(&info);
}
void AudioStreamOggVorbis::set_packet_sequence(Ref<OggPacketSequence> p_packet_sequence) {
packet_sequence = p_packet_sequence;
if (packet_sequence.is_valid()) {
maybe_update_info();
}
}
Ref<OggPacketSequence> AudioStreamOggVorbis::get_packet_sequence() const {
return packet_sequence;
}
void AudioStreamOggVorbis::set_loop(bool p_enable) {
loop = p_enable;
}
bool AudioStreamOggVorbis::has_loop() const {
return loop;
}
void AudioStreamOggVorbis::set_loop_offset(double p_seconds) {
loop_offset = p_seconds;
}
double AudioStreamOggVorbis::get_loop_offset() const {
return loop_offset;
}
double AudioStreamOggVorbis::get_length() const {
ERR_FAIL_COND_V(packet_sequence.is_null(), 0);
return packet_sequence->get_length();
}
void AudioStreamOggVorbis::set_bpm(double p_bpm) {
ERR_FAIL_COND(p_bpm < 0);
bpm = p_bpm;
emit_changed();
}
double AudioStreamOggVorbis::get_bpm() const {
return bpm;
}
void AudioStreamOggVorbis::set_beat_count(int p_beat_count) {
ERR_FAIL_COND(p_beat_count < 0);
beat_count = p_beat_count;
emit_changed();
}
int AudioStreamOggVorbis::get_beat_count() const {
return beat_count;
}
void AudioStreamOggVorbis::set_bar_beats(int p_bar_beats) {
ERR_FAIL_COND(p_bar_beats < 2);
bar_beats = p_bar_beats;
emit_changed();
}
int AudioStreamOggVorbis::get_bar_beats() const {
return bar_beats;
}
void AudioStreamOggVorbis::set_tags(const Dictionary &p_tags) {
tags = p_tags;
}
Dictionary AudioStreamOggVorbis::get_tags() const {
return tags;
}
bool AudioStreamOggVorbis::is_monophonic() const {
return false;
}
void AudioStreamOggVorbis::get_parameter_list(List<Parameter> *r_parameters) {
r_parameters->push_back(Parameter(PropertyInfo(Variant::BOOL, "looping", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_CHECKABLE), Variant()));
}
Ref<AudioSample> AudioStreamOggVorbis::generate_sample() const {
Ref<AudioSample> sample;
sample.instantiate();
sample->stream = this;
sample->loop_mode = loop
? AudioSample::LoopMode::LOOP_FORWARD
: AudioSample::LoopMode::LOOP_DISABLED;
sample->loop_begin = loop_offset;
sample->loop_end = 0;
return sample;
}
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", "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);
ClassDB::bind_method(D_METHOD("get_packet_sequence"), &AudioStreamOggVorbis::get_packet_sequence);
ClassDB::bind_method(D_METHOD("set_loop", "enable"), &AudioStreamOggVorbis::set_loop);
ClassDB::bind_method(D_METHOD("has_loop"), &AudioStreamOggVorbis::has_loop);
ClassDB::bind_method(D_METHOD("set_loop_offset", "seconds"), &AudioStreamOggVorbis::set_loop_offset);
ClassDB::bind_method(D_METHOD("get_loop_offset"), &AudioStreamOggVorbis::get_loop_offset);
ClassDB::bind_method(D_METHOD("set_bpm", "bpm"), &AudioStreamOggVorbis::set_bpm);
ClassDB::bind_method(D_METHOD("get_bpm"), &AudioStreamOggVorbis::get_bpm);
ClassDB::bind_method(D_METHOD("set_beat_count", "count"), &AudioStreamOggVorbis::set_beat_count);
ClassDB::bind_method(D_METHOD("get_beat_count"), &AudioStreamOggVorbis::get_beat_count);
ClassDB::bind_method(D_METHOD("set_bar_beats", "count"), &AudioStreamOggVorbis::set_bar_beats);
ClassDB::bind_method(D_METHOD("get_bar_beats"), &AudioStreamOggVorbis::get_bar_beats);
ClassDB::bind_method(D_METHOD("set_tags", "tags"), &AudioStreamOggVorbis::set_tags);
ClassDB::bind_method(D_METHOD("get_tags"), &AudioStreamOggVorbis::get_tags);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "packet_sequence", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_packet_sequence", "get_packet_sequence");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bpm", PROPERTY_HINT_RANGE, "0,400,0.01,or_greater"), "set_bpm", "get_bpm");
ADD_PROPERTY(PropertyInfo(Variant::INT, "beat_count", PROPERTY_HINT_RANGE, "0,512,1,or_greater"), "set_beat_count", "get_beat_count");
ADD_PROPERTY(PropertyInfo(Variant::INT, "bar_beats", PROPERTY_HINT_RANGE, "2,32,1,or_greater"), "set_bar_beats", "get_bar_beats");
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "tags", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_tags", "get_tags");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "loop_offset"), "set_loop_offset", "get_loop_offset");
}
AudioStreamOggVorbis::AudioStreamOggVorbis() {}
AudioStreamOggVorbis::~AudioStreamOggVorbis() {}

View File

@@ -0,0 +1,182 @@
/**************************************************************************/
/* audio_stream_ogg_vorbis.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "core/variant/variant.h"
#include "servers/audio/audio_stream.h"
#include "modules/ogg/ogg_packet_sequence.h"
#include <vorbis/codec.h>
class AudioStreamOggVorbis;
class AudioStreamPlaybackOggVorbis : public AudioStreamPlaybackResampled {
GDCLASS(AudioStreamPlaybackOggVorbis, AudioStreamPlaybackResampled);
uint32_t frames_mixed = 0;
bool active = false;
bool looping_override = false;
bool looping = false;
int loops = 0;
enum {
FADE_SIZE = 256
};
AudioFrame loop_fade[FADE_SIZE];
int loop_fade_remaining = FADE_SIZE;
vorbis_info info;
vorbis_comment comment;
vorbis_dsp_state dsp_state;
vorbis_block block;
bool info_is_allocated = false;
bool comment_is_allocated = false;
bool dsp_state_is_allocated = false;
bool block_is_allocated = false;
bool ready = false;
bool have_samples_left = false;
bool have_packets_left = false;
friend class AudioStreamOggVorbis;
Ref<OggPacketSequence> vorbis_data;
Ref<OggPacketSequencePlayback> vorbis_data_playback;
Ref<AudioStreamOggVorbis> vorbis_stream;
bool _is_sample = false;
Ref<AudioSamplePlayback> sample_playback;
int _mix_frames(AudioFrame *p_buffer, int p_frames);
int _mix_frames_vorbis(AudioFrame *p_buffer, int p_frames);
// Allocates vorbis data structures. Returns true upon success, false on failure.
bool _alloc_vorbis();
protected:
virtual int _mix_internal(AudioFrame *p_buffer, int p_frames) override;
virtual float get_stream_sampling_rate() override;
public:
virtual void start(double p_from_pos = 0.0) override;
virtual void stop() override;
virtual bool is_playing() const override;
virtual int get_loop_count() const override; //times it looped
virtual double get_playback_position() const override;
virtual void seek(double p_time) override;
virtual void tag_used_streams() override;
virtual void set_parameter(const StringName &p_name, const Variant &p_value) override;
virtual Variant get_parameter(const StringName &p_name) const override;
virtual void set_is_sample(bool p_is_sample) override;
virtual bool get_is_sample() const override;
virtual Ref<AudioSamplePlayback> get_sample_playback() const override;
virtual void set_sample_playback(const Ref<AudioSamplePlayback> &p_playback) override;
AudioStreamPlaybackOggVorbis() {}
~AudioStreamPlaybackOggVorbis();
};
class AudioStreamOggVorbis : public AudioStream {
GDCLASS(AudioStreamOggVorbis, AudioStream);
OBJ_SAVE_TYPE(AudioStream); // Saves derived classes with common type so they can be interchanged.
RES_BASE_EXTENSION("oggvorbisstr");
friend class AudioStreamPlaybackOggVorbis;
int channels = 1;
double length = 0.0;
bool loop = false;
double loop_offset = 0.0;
// Performs a seek to the beginning of the stream, should not be called during playback!
// Also causes allocation and deallocation.
void maybe_update_info();
Ref<OggPacketSequence> packet_sequence;
double bpm = 0;
int beat_count = 0;
int bar_beats = 4;
Dictionary tags;
protected:
static void _bind_methods();
public:
static Ref<AudioStreamOggVorbis> load_from_file(const String &p_path);
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;
void set_loop_offset(double p_seconds);
double get_loop_offset() const;
void set_bpm(double p_bpm);
virtual double get_bpm() const override;
void set_beat_count(int p_beat_count);
virtual int get_beat_count() const override;
void set_bar_beats(int p_bar_beats);
virtual int get_bar_beats() const override;
void set_tags(const Dictionary &p_tags);
virtual Dictionary get_tags() const override;
virtual Ref<AudioStreamPlayback> instantiate_playback() override;
virtual String get_stream_name() const override;
void set_packet_sequence(Ref<OggPacketSequence> p_packet_sequence);
Ref<OggPacketSequence> get_packet_sequence() const;
virtual double get_length() const override; //if supported, otherwise return 0
virtual bool is_monophonic() const override;
virtual void get_parameter_list(List<Parameter> *r_parameters) override;
virtual bool can_be_sampled() const override {
return true;
}
virtual Ref<AudioSample> generate_sample() const override;
AudioStreamOggVorbis();
virtual ~AudioStreamOggVorbis();
};

19
modules/vorbis/config.py Normal file
View File

@@ -0,0 +1,19 @@
def can_build(env, platform):
env.module_add_dependencies("vorbis", ["ogg"])
return True
def configure(env):
pass
def get_doc_classes():
return [
"AudioStreamOggVorbis",
"AudioStreamPlaybackOggVorbis",
"ResourceImporterOggVorbis",
]
def get_doc_path():
return "doc_classes"

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AudioStreamOggVorbis" inherits="AudioStream" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
A class representing an Ogg Vorbis audio stream.
</brief_description>
<description>
The AudioStreamOggVorbis class is a specialized [AudioStream] for handling Ogg Vorbis file formats. It offers functionality for loading and playing back Ogg Vorbis files, as well as managing looping and other playback properties. This class is part of the audio stream system, which also supports WAV files through the [AudioStreamWAV] class.
</description>
<tutorials>
<link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link>
</tutorials>
<methods>
<method name="load_from_buffer" qualifiers="static">
<return type="AudioStreamOggVorbis" />
<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.
</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.
</description>
</method>
</methods>
<members>
<member name="bar_beats" type="int" setter="set_bar_beats" getter="get_bar_beats" default="4">
</member>
<member name="beat_count" type="int" setter="set_beat_count" getter="get_beat_count" default="0">
</member>
<member name="bpm" type="float" setter="set_bpm" getter="get_bpm" default="0.0">
</member>
<member name="loop" type="bool" setter="set_loop" getter="has_loop" default="false">
If [code]true[/code], the audio will play again from the specified [member loop_offset] once it is done playing. Useful for ambient sounds and background music.
</member>
<member name="loop_offset" type="float" setter="set_loop_offset" getter="get_loop_offset" default="0.0">
Time in seconds at which the stream starts after being looped.
</member>
<member name="packet_sequence" type="OggPacketSequence" setter="set_packet_sequence" getter="get_packet_sequence">
Contains the raw Ogg data for this stream.
</member>
<member name="tags" type="Dictionary" setter="set_tags" getter="get_tags" default="{}">
Contains user-defined tags if found in the Ogg Vorbis data.
Commonly used tags include [code]title[/code], [code]artist[/code], [code]album[/code], [code]tracknumber[/code], and [code]date[/code] ([code]date[/code] does not have a standard date format).
[b]Note:[/b] No tag is [i]guaranteed[/i] to be present in every file, so make sure to account for the keys not always existing.
</member>
</members>
</class>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AudioStreamPlaybackOggVorbis" inherits="AudioStreamPlaybackResampled" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
</brief_description>
<description>
</description>
<tutorials>
</tutorials>
</class>

View File

@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="ResourceImporterOggVorbis" inherits="ResourceImporter" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Imports an Ogg Vorbis audio file for playback.
</brief_description>
<description>
Ogg Vorbis is a lossy audio format, with better audio quality compared to [ResourceImporterMP3] at a given bitrate.
In most cases, it's recommended to use Ogg Vorbis over MP3. However, if you're using an MP3 sound source with no higher quality source available, then it's recommended to use the MP3 file directly to avoid double lossy compression.
Ogg Vorbis requires more CPU to decode than [ResourceImporterWAV]. If you need to play a lot of simultaneous sounds, it's recommended to use WAV for those sounds instead, especially if targeting low-end devices.
</description>
<tutorials>
<link title="Importing audio samples">$DOCS_URL/tutorials/assets_pipeline/importing_audio_samples.html</link>
</tutorials>
<methods>
<method name="load_from_buffer" qualifiers="static" deprecated="Use [method AudioStreamOggVorbis.load_from_buffer] instead.">
<return type="AudioStreamOggVorbis" />
<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.
</description>
</method>
<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>
Creates a new [AudioStreamOggVorbis] instance from the given file path. The file must be in Ogg Vorbis format.
</description>
</method>
</methods>
<members>
<member name="bar_beats" type="int" setter="" getter="" default="4">
The number of bars within a single beat in the audio track. This is only relevant for music that wishes to make use of interactive music functionality, not sound effects.
A more convenient editor for [member bar_beats] is provided in the [b]Advanced Import Settings[/b] dialog, as it lets you preview your changes without having to reimport the audio.
</member>
<member name="beat_count" type="int" setter="" getter="" default="0">
The beat count of the audio track. This is only relevant for music that wishes to make use of interactive music functionality, not sound effects.
A more convenient editor for [member beat_count] is provided in the [b]Advanced Import Settings[/b] dialog, as it lets you preview your changes without having to reimport the audio.
</member>
<member name="bpm" type="float" setter="" getter="" default="0">
The beats per minute of the audio track. This should match the BPM measure that was used to compose the track. This is only relevant for music that wishes to make use of interactive music functionality, not sound effects.
A more convenient editor for [member bpm] is provided in the [b]Advanced Import Settings[/b] dialog, as it lets you preview your changes without having to reimport the audio.
</member>
<member name="loop" type="bool" setter="" getter="" default="false">
If enabled, the audio will begin playing at the beginning after playback ends by reaching the end of the audio.
[b]Note:[/b] In [AudioStreamPlayer], the [signal AudioStreamPlayer.finished] signal won't be emitted for looping audio when it reaches the end of the audio file, as the audio will keep playing indefinitely.
</member>
<member name="loop_offset" type="float" setter="" getter="" default="0">
Determines where audio will start to loop after playback reaches the end of the audio. This can be used to only loop a part of the audio file, which is useful for some ambient sounds or music. The value is determined in seconds relative to the beginning of the audio. A value of [code]0.0[/code] will loop the entire audio file.
Only has an effect if [member loop] is [code]true[/code].
A more convenient editor for [member loop_offset] is provided in the [b]Advanced Import Settings[/b] dialog, as it lets you preview your changes without having to reimport the audio.
</member>
</members>
</class>

View File

@@ -0,0 +1,62 @@
/**************************************************************************/
/* register_types.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "register_types.h"
#include "audio_stream_ogg_vorbis.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_node.h"
#include "resource_importer_ogg_vorbis.h"
static void _editor_init() {
Ref<ResourceImporterOggVorbis> ogg_vorbis_importer;
ogg_vorbis_importer.instantiate();
ResourceFormatImporter::get_singleton()->add_importer(ogg_vorbis_importer);
}
#endif
void initialize_vorbis_module(ModuleInitializationLevel p_level) {
if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) {
GDREGISTER_CLASS(AudioStreamOggVorbis);
GDREGISTER_CLASS(AudioStreamPlaybackOggVorbis);
}
#ifdef TOOLS_ENABLED
if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) {
GDREGISTER_CLASS(ResourceImporterOggVorbis);
EditorNode::add_init_callback(_editor_init);
}
#endif
}
void uninitialize_vorbis_module(ModuleInitializationLevel p_level) {
}

View File

@@ -0,0 +1,36 @@
/**************************************************************************/
/* register_types.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "modules/register_module_types.h"
void initialize_vorbis_module(ModuleInitializationLevel p_level);
void uninitialize_vorbis_module(ModuleInitializationLevel p_level);

View File

@@ -0,0 +1,135 @@
/**************************************************************************/
/* resource_importer_ogg_vorbis.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "resource_importer_ogg_vorbis.h"
#include "core/io/file_access.h"
#include "core/io/resource_saver.h"
#ifdef TOOLS_ENABLED
#include "editor/import/audio_stream_import_settings.h"
#endif
#include <ogg/ogg.h>
#include <vorbis/codec.h>
String ResourceImporterOggVorbis::get_importer_name() const {
return "oggvorbisstr";
}
String ResourceImporterOggVorbis::get_visible_name() const {
return "Ogg Vorbis";
}
void ResourceImporterOggVorbis::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("ogg");
}
String ResourceImporterOggVorbis::get_save_extension() const {
return "oggvorbisstr";
}
String ResourceImporterOggVorbis::get_resource_type() const {
return "AudioStreamOggVorbis";
}
bool ResourceImporterOggVorbis::get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
return true;
}
int ResourceImporterOggVorbis::get_preset_count() const {
return 0;
}
String ResourceImporterOggVorbis::get_preset_name(int p_idx) const {
return String();
}
void ResourceImporterOggVorbis::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const {
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "loop"), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "loop_offset"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "bpm", PROPERTY_HINT_RANGE, "0,400,0.01,or_greater"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "beat_count", PROPERTY_HINT_RANGE, "0,512,or_greater"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "bar_beats", PROPERTY_HINT_RANGE, "2,32,or_greater"), 4));
}
#ifdef TOOLS_ENABLED
bool ResourceImporterOggVorbis::has_advanced_options() const {
return true;
}
void ResourceImporterOggVorbis::show_advanced_options(const String &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);
}
}
#endif
Error ResourceImporterOggVorbis::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"];
double loop_offset = p_options["loop_offset"];
double bpm = p_options["bpm"];
int beat_count = p_options["beat_count"];
int bar_beats = p_options["bar_beats"];
Ref<AudioStreamOggVorbis> ogg_vorbis_stream = AudioStreamOggVorbis::load_from_file(p_source_file);
if (ogg_vorbis_stream.is_null()) {
return ERR_CANT_OPEN;
}
ogg_vorbis_stream->set_loop(loop);
ogg_vorbis_stream->set_loop_offset(loop_offset);
ogg_vorbis_stream->set_bpm(bpm);
ogg_vorbis_stream->set_beat_count(beat_count);
ogg_vorbis_stream->set_bar_beats(bar_beats);
return ResourceSaver::save(ogg_vorbis_stream, p_save_path + ".oggvorbisstr");
}
#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) {
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

@@ -0,0 +1,69 @@
/**************************************************************************/
/* resource_importer_ogg_vorbis.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "audio_stream_ogg_vorbis.h"
#include "core/io/resource_importer.h"
class ResourceImporterOggVorbis : public ResourceImporter {
GDCLASS(ResourceImporterOggVorbis, ResourceImporter);
protected:
static void _bind_methods();
public:
#ifdef TOOLS_ENABLED
virtual bool has_advanced_options() const override;
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> &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;
virtual String get_importer_name() const override;
virtual String get_visible_name() const override;
virtual int get_preset_count() const override;
virtual String get_preset_name(int p_idx) const override;
virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override;
virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const 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;
virtual bool can_import_threaded() const override { return true; }
ResourceImporterOggVorbis();
};