summaryrefslogtreecommitdiff
path: root/servers/audio/audio_stream.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'servers/audio/audio_stream.cpp')
-rw-r--r--servers/audio/audio_stream.cpp153
1 files changed, 108 insertions, 45 deletions
diff --git a/servers/audio/audio_stream.cpp b/servers/audio/audio_stream.cpp
index 1ebd57fa7f..80485845c9 100644
--- a/servers/audio/audio_stream.cpp
+++ b/servers/audio/audio_stream.cpp
@@ -83,6 +83,10 @@ int AudioStreamPlayback::mix(AudioFrame *p_buffer, float p_rate_scale, int p_fra
return 0;
}
+void AudioStreamPlayback::tag_used_streams() {
+ GDVIRTUAL_CALL(_tag_used_streams);
+}
+
void AudioStreamPlayback::_bind_methods() {
GDVIRTUAL_BIND(_start, "from_pos")
GDVIRTUAL_BIND(_stop)
@@ -91,6 +95,7 @@ void AudioStreamPlayback::_bind_methods() {
GDVIRTUAL_BIND(_get_playback_position)
GDVIRTUAL_BIND(_seek, "position")
GDVIRTUAL_BIND(_mix, "buffer", "rate_scale", "frames");
+ GDVIRTUAL_BIND(_tag_used_streams);
}
//////////////////////////////
@@ -135,9 +140,10 @@ int AudioStreamPlaybackResampled::mix(AudioFrame *p_buffer, float p_rate_scale,
uint64_t mix_increment = uint64_t(((get_stream_sampling_rate() * p_rate_scale * playback_speed_scale) / double(target_rate)) * double(FP_LEN));
- int mixed_frames_total = p_frames;
+ int mixed_frames_total = -1;
- for (int i = 0; i < p_frames; i++) {
+ int i;
+ for (i = 0; i < p_frames; i++) {
uint32_t idx = CUBIC_INTERP_HISTORY + uint32_t(mix_offset >> FP_BITS);
//standard cubic interpolation (great quality/performance ratio)
//this used to be moved to a LUT for greater performance, but nowadays CPU speed is generally faster than memory.
@@ -147,7 +153,7 @@ int AudioStreamPlaybackResampled::mix(AudioFrame *p_buffer, float p_rate_scale,
AudioFrame y2 = internal_buffer[idx - 1];
AudioFrame y3 = internal_buffer[idx - 0];
- if (idx <= internal_buffer_end && idx >= internal_buffer_end && mixed_frames_total == p_frames) {
+ if (idx >= internal_buffer_end && mixed_frames_total == -1) {
// The internal buffer ends somewhere in this range, and we haven't yet recorded the number of good frames we have.
mixed_frames_total = i;
}
@@ -167,32 +173,28 @@ int AudioStreamPlaybackResampled::mix(AudioFrame *p_buffer, float p_rate_scale,
internal_buffer[1] = internal_buffer[INTERNAL_BUFFER_LEN + 1];
internal_buffer[2] = internal_buffer[INTERNAL_BUFFER_LEN + 2];
internal_buffer[3] = internal_buffer[INTERNAL_BUFFER_LEN + 3];
- if (is_playing()) {
- int mixed_frames = _mix_internal(internal_buffer + 4, INTERNAL_BUFFER_LEN);
- if (mixed_frames != INTERNAL_BUFFER_LEN) {
- // internal_buffer[mixed_frames] is the first frame of silence.
- internal_buffer_end = mixed_frames;
- } else {
- // The internal buffer does not contain the first frame of silence.
- internal_buffer_end = -1;
- }
+ int mixed_frames = _mix_internal(internal_buffer + 4, INTERNAL_BUFFER_LEN);
+ if (mixed_frames != INTERNAL_BUFFER_LEN) {
+ // internal_buffer[mixed_frames] is the first frame of silence.
+ internal_buffer_end = mixed_frames;
} else {
- //fill with silence, not playing
- for (int j = 0; j < INTERNAL_BUFFER_LEN; ++j) {
- internal_buffer[j + 4] = AudioFrame(0, 0);
- }
+ // The internal buffer does not contain the first frame of silence.
+ internal_buffer_end = -1;
}
mix_offset -= (INTERNAL_BUFFER_LEN << FP_BITS);
}
}
+ if (mixed_frames_total == -1 && i == p_frames) {
+ mixed_frames_total = p_frames;
+ }
return mixed_frames_total;
}
////////////////////////////////
-Ref<AudioStreamPlayback> AudioStream::instance_playback() {
+Ref<AudioStreamPlayback> AudioStream::instantiate_playback() {
Ref<AudioStreamPlayback> ret;
- if (GDVIRTUAL_CALL(_instance_playback, ret)) {
+ if (GDVIRTUAL_CALL(_instantiate_playback, ret)) {
return ret;
}
ERR_FAIL_V_MSG(Ref<AudioStreamPlayback>(), "Method must be implemented!");
@@ -221,19 +223,74 @@ bool AudioStream::is_monophonic() const {
return true;
}
+double AudioStream::get_bpm() const {
+ double ret = 0;
+ if (GDVIRTUAL_CALL(_get_bpm, ret)) {
+ return ret;
+ }
+ return 0;
+}
+
+bool AudioStream::has_loop() const {
+ bool ret = 0;
+ if (GDVIRTUAL_CALL(_has_loop, ret)) {
+ return ret;
+ }
+ return 0;
+}
+
+int AudioStream::get_bar_beats() const {
+ int ret = 0;
+ if (GDVIRTUAL_CALL(_get_bar_beats, ret)) {
+ return ret;
+ }
+ return 0;
+}
+
+int AudioStream::get_beat_count() const {
+ int ret = 0;
+ if (GDVIRTUAL_CALL(_get_beat_count, ret)) {
+ return ret;
+ }
+ return 0;
+}
+
+void AudioStream::tag_used(float p_offset) {
+ if (tagged_frame != AudioServer::get_singleton()->get_mixed_frames()) {
+ offset_count = 0;
+ tagged_frame = AudioServer::get_singleton()->get_mixed_frames();
+ }
+ if (offset_count < MAX_TAGGED_OFFSETS) {
+ tagged_offsets[offset_count++] = p_offset;
+ }
+}
+
+uint64_t AudioStream::get_tagged_frame() const {
+ return tagged_frame;
+}
+uint32_t AudioStream::get_tagged_frame_count() const {
+ return offset_count;
+}
+float AudioStream::get_tagged_frame_offset(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, MAX_TAGGED_OFFSETS, 0);
+ return tagged_offsets[p_index];
+}
+
void AudioStream::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_length"), &AudioStream::get_length);
ClassDB::bind_method(D_METHOD("is_monophonic"), &AudioStream::is_monophonic);
- ClassDB::bind_method(D_METHOD("instance_playback"), &AudioStream::instance_playback);
- GDVIRTUAL_BIND(_instance_playback);
+ ClassDB::bind_method(D_METHOD("instantiate_playback"), &AudioStream::instantiate_playback);
+ GDVIRTUAL_BIND(_instantiate_playback);
GDVIRTUAL_BIND(_get_stream_name);
GDVIRTUAL_BIND(_get_length);
GDVIRTUAL_BIND(_is_monophonic);
+ GDVIRTUAL_BIND(_get_bpm)
+ GDVIRTUAL_BIND(_get_beat_count)
}
////////////////////////////////
-Ref<AudioStreamPlayback> AudioStreamMicrophone::instance_playback() {
+Ref<AudioStreamPlayback> AudioStreamMicrophone::instantiate_playback() {
Ref<AudioStreamPlaybackMicrophone> playback;
playback.instantiate();
@@ -366,6 +423,10 @@ void AudioStreamPlaybackMicrophone::seek(float p_time) {
// Can't seek a microphone input
}
+void AudioStreamPlaybackMicrophone::tag_used_streams() {
+ microphone->tag_used(0);
+}
+
AudioStreamPlaybackMicrophone::~AudioStreamPlaybackMicrophone() {
microphone->playbacks.erase(this);
stop();
@@ -387,9 +448,11 @@ void AudioStreamRandomizer::add_stream(int p_index) {
notify_property_list_changed();
}
+// p_index_to is relative to the array prior to the removal of from.
+// Example: [0, 1, 2, 3], move(1, 3) => [0, 2, 1, 3]
void AudioStreamRandomizer::move_stream(int p_index_from, int p_index_to) {
- ERR_FAIL_COND(p_index_from < 0);
- ERR_FAIL_COND(p_index_from >= audio_stream_pool.size());
+ ERR_FAIL_INDEX(p_index_from, audio_stream_pool.size());
+ // p_index_to == audio_stream_pool.size() is valid (move to end).
ERR_FAIL_COND(p_index_to < 0);
ERR_FAIL_COND(p_index_to > audio_stream_pool.size());
audio_stream_pool.insert(p_index_to, audio_stream_pool[p_index_from]);
@@ -403,36 +466,31 @@ void AudioStreamRandomizer::move_stream(int p_index_from, int p_index_to) {
}
void AudioStreamRandomizer::remove_stream(int p_index) {
- ERR_FAIL_COND(p_index < 0);
- ERR_FAIL_COND(p_index >= audio_stream_pool.size());
+ ERR_FAIL_INDEX(p_index, audio_stream_pool.size());
audio_stream_pool.remove_at(p_index);
emit_signal(SNAME("changed"));
notify_property_list_changed();
}
void AudioStreamRandomizer::set_stream(int p_index, Ref<AudioStream> p_stream) {
- ERR_FAIL_COND(p_index < 0);
- ERR_FAIL_COND(p_index >= audio_stream_pool.size());
+ ERR_FAIL_INDEX(p_index, audio_stream_pool.size());
audio_stream_pool.write[p_index].stream = p_stream;
emit_signal(SNAME("changed"));
}
Ref<AudioStream> AudioStreamRandomizer::get_stream(int p_index) const {
- ERR_FAIL_COND_V(p_index < 0, nullptr);
- ERR_FAIL_COND_V(p_index >= audio_stream_pool.size(), nullptr);
+ ERR_FAIL_INDEX_V(p_index, audio_stream_pool.size(), nullptr);
return audio_stream_pool[p_index].stream;
}
void AudioStreamRandomizer::set_stream_probability_weight(int p_index, float p_weight) {
- ERR_FAIL_COND(p_index < 0);
- ERR_FAIL_COND(p_index >= audio_stream_pool.size());
+ ERR_FAIL_INDEX(p_index, audio_stream_pool.size());
audio_stream_pool.write[p_index].weight = p_weight;
emit_signal(SNAME("changed"));
}
float AudioStreamRandomizer::get_stream_probability_weight(int p_index) const {
- ERR_FAIL_COND_V(p_index < 0, 0);
- ERR_FAIL_COND_V(p_index >= audio_stream_pool.size(), 0);
+ ERR_FAIL_INDEX_V(p_index, audio_stream_pool.size(), 0);
return audio_stream_pool[p_index].weight;
}
@@ -496,7 +554,7 @@ Ref<AudioStreamPlayback> AudioStreamRandomizer::instance_playback_random() {
for (PoolEntry &entry : local_pool) {
cumulative_weight += entry.weight;
if (cumulative_weight > chosen_cumulative_weight) {
- playback->playback = entry.stream->instance_playback();
+ playback->playback = entry.stream->instantiate_playback();
last_playback = entry.stream;
break;
}
@@ -504,7 +562,7 @@ Ref<AudioStreamPlayback> AudioStreamRandomizer::instance_playback_random() {
if (playback->playback.is_null()) {
// This indicates a floating point error. Take the last element.
last_playback = local_pool[local_pool.size() - 1].stream;
- playback->playback = local_pool.write[local_pool.size() - 1].stream->instance_playback();
+ playback->playback = local_pool.write[local_pool.size() - 1].stream->instantiate_playback();
}
return playback;
}
@@ -538,14 +596,14 @@ Ref<AudioStreamPlayback> AudioStreamRandomizer::instance_playback_no_repeats() {
cumulative_weight += entry.weight;
if (cumulative_weight > chosen_cumulative_weight) {
last_playback = entry.stream;
- playback->playback = entry.stream->instance_playback();
+ playback->playback = entry.stream->instantiate_playback();
break;
}
}
if (playback->playback.is_null()) {
// This indicates a floating point error. Take the last element.
last_playback = local_pool[local_pool.size() - 1].stream;
- playback->playback = local_pool.write[local_pool.size() - 1].stream->instance_playback();
+ playback->playback = local_pool.write[local_pool.size() - 1].stream->instantiate_playback();
}
return playback;
}
@@ -574,7 +632,7 @@ Ref<AudioStreamPlayback> AudioStreamRandomizer::instance_playback_sequential() {
for (Ref<AudioStream> &entry : local_pool) {
if (found_last_stream) {
last_playback = entry;
- playback->playback = entry->instance_playback();
+ playback->playback = entry->instantiate_playback();
break;
}
if (entry == last_playback) {
@@ -584,12 +642,12 @@ Ref<AudioStreamPlayback> AudioStreamRandomizer::instance_playback_sequential() {
if (playback->playback.is_null()) {
// Wrap around
last_playback = local_pool[0];
- playback->playback = local_pool.write[0]->instance_playback();
+ playback->playback = local_pool.write[0]->instantiate_playback();
}
return playback;
}
-Ref<AudioStreamPlayback> AudioStreamRandomizer::instance_playback() {
+Ref<AudioStreamPlayback> AudioStreamRandomizer::instantiate_playback() {
switch (playback_mode) {
case PLAYBACK_RANDOM:
return instance_playback_random();
@@ -702,17 +760,14 @@ void AudioStreamRandomizer::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "streams_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_streams_count", "get_streams_count");
ADD_PROPERTY(PropertyInfo(Variant::INT, "playback_mode", PROPERTY_HINT_ENUM, "Random (Avoid Repeats),Random,Sequential"), "set_playback_mode", "get_playback_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "random_pitch", PROPERTY_HINT_RANGE, "1,16,0.01"), "set_random_pitch", "get_random_pitch");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "random_volume_offset_db", PROPERTY_HINT_RANGE, "0,40,0"), "set_random_volume_offset_db", "get_random_volume_offset_db");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "random_volume_offset_db", PROPERTY_HINT_RANGE, "0,40,0.01,suffix:dB"), "set_random_volume_offset_db", "get_random_volume_offset_db");
BIND_ENUM_CONSTANT(PLAYBACK_RANDOM_NO_REPEATS);
BIND_ENUM_CONSTANT(PLAYBACK_RANDOM);
BIND_ENUM_CONSTANT(PLAYBACK_SEQUENTIAL);
}
-AudioStreamRandomizer::AudioStreamRandomizer() {
- random_pitch_scale = 1.1;
- random_volume_offset_db = 5;
-}
+AudioStreamRandomizer::AudioStreamRandomizer() {}
void AudioStreamPlaybackRandomizer::start(float p_from_pos) {
playing = playback;
@@ -771,6 +826,14 @@ void AudioStreamPlaybackRandomizer::seek(float p_time) {
}
}
+void AudioStreamPlaybackRandomizer::tag_used_streams() {
+ Ref<AudioStreamPlayback> p = playing; // Thread safety
+ if (p.is_valid()) {
+ p->tag_used_streams();
+ }
+ randomizer->tag_used(0);
+}
+
int AudioStreamPlaybackRandomizer::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) {
if (playing.is_valid()) {
return playing->mix(p_buffer, p_rate_scale * pitch_scale, p_frames);