diff options
Diffstat (limited to 'servers/audio/audio_stream.cpp')
-rw-r--r-- | servers/audio/audio_stream.cpp | 526 |
1 files changed, 454 insertions, 72 deletions
diff --git a/servers/audio/audio_stream.cpp b/servers/audio/audio_stream.cpp index e1b391b823..80485845c9 100644 --- a/servers/audio/audio_stream.cpp +++ b/servers/audio/audio_stream.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -76,13 +76,17 @@ void AudioStreamPlayback::seek(float p_time) { int AudioStreamPlayback::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) { int ret; - if (GDVIRTUAL_CALL(_mix, p_buffer, p_rate_scale, p_frames, ret)) { + if (GDVIRTUAL_REQUIRED_CALL(_mix, p_buffer, p_rate_scale, p_frames, ret)) { return ret; } - WARN_PRINT_ONCE("AudioStreamPlayback::mix unimplemented!"); + 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,10 +95,11 @@ 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); } ////////////////////////////// -void AudioStreamPlaybackResampled::_begin_resample() { +void AudioStreamPlaybackResampled::begin_resample() { //clear cubic interpolation history internal_buffer[0] = AudioFrame(0.0, 0.0); internal_buffer[1] = AudioFrame(0.0, 0.0); @@ -105,15 +110,40 @@ void AudioStreamPlaybackResampled::_begin_resample() { mix_offset = 0; } +int AudioStreamPlaybackResampled::_mix_internal(AudioFrame *p_buffer, int p_frames) { + int ret; + if (GDVIRTUAL_REQUIRED_CALL(_mix_resampled, p_buffer, p_frames, ret)) { + return ret; + } + + return 0; +} +float AudioStreamPlaybackResampled::get_stream_sampling_rate() { + float ret; + if (GDVIRTUAL_REQUIRED_CALL(_get_stream_sampling_rate, ret)) { + return ret; + } + + return 0; +} + +void AudioStreamPlaybackResampled::_bind_methods() { + ClassDB::bind_method(D_METHOD("begin_resample"), &AudioStreamPlaybackResampled::begin_resample); + + GDVIRTUAL_BIND(_mix_resampled, "dst_buffer", "frame_count"); + GDVIRTUAL_BIND(_get_stream_sampling_rate); +} + int AudioStreamPlaybackResampled::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) { float target_rate = AudioServer::get_singleton()->get_mix_rate(); float playback_speed_scale = AudioServer::get_singleton()->get_playback_speed_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. @@ -123,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; } @@ -143,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!"); @@ -189,16 +215,82 @@ float AudioStream::get_length() const { return 0; } +bool AudioStream::is_monophonic() const { + bool ret; + if (GDVIRTUAL_CALL(_is_monophonic, ret)) { + return ret; + } + 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); - GDVIRTUAL_BIND(_instance_playback); + ClassDB::bind_method(D_METHOD("is_monophonic"), &AudioStream::is_monophonic); + 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(); @@ -221,6 +313,10 @@ float AudioStreamMicrophone::get_length() const { return 0; } +bool AudioStreamMicrophone::is_monophonic() const { + return true; +} + void AudioStreamMicrophone::_bind_methods() { } @@ -300,7 +396,7 @@ void AudioStreamPlaybackMicrophone::start(float p_from_pos) { if (AudioDriver::get_singleton()->capture_start() == OK) { active = true; - _begin_resample(); + begin_resample(); } } @@ -327,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(); @@ -337,92 +437,366 @@ AudioStreamPlaybackMicrophone::AudioStreamPlaybackMicrophone() { //////////////////////////////// -void AudioStreamRandomPitch::set_audio_stream(const Ref<AudioStream> &p_audio_stream) { - audio_stream = p_audio_stream; - if (audio_stream.is_valid()) { - for (Set<AudioStreamPlaybackRandomPitch *>::Element *E = playbacks.front(); E; E = E->next()) { - E->get()->playback = audio_stream->instance_playback(); - } +void AudioStreamRandomizer::add_stream(int p_index) { + if (p_index < 0) { + p_index = audio_stream_pool.size(); } + ERR_FAIL_COND(p_index > audio_stream_pool.size()); + PoolEntry entry{ nullptr, 1.0f }; + audio_stream_pool.insert(p_index, entry); + emit_signal(SNAME("changed")); + 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_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]); + // If 'from' is strictly after 'to' we need to increment the index by one because of the insertion. + if (p_index_from > p_index_to) { + p_index_from++; + } + audio_stream_pool.remove_at(p_index_from); + emit_signal(SNAME("changed")); + notify_property_list_changed(); +} + +void AudioStreamRandomizer::remove_stream(int p_index) { + 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_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_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_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_INDEX_V(p_index, audio_stream_pool.size(), 0); + return audio_stream_pool[p_index].weight; } -Ref<AudioStream> AudioStreamRandomPitch::get_audio_stream() const { - return audio_stream; +void AudioStreamRandomizer::set_streams_count(int p_count) { + audio_stream_pool.resize(p_count); } -void AudioStreamRandomPitch::set_random_pitch(float p_pitch) { +int AudioStreamRandomizer::get_streams_count() const { + return audio_stream_pool.size(); +} + +void AudioStreamRandomizer::set_random_pitch(float p_pitch) { if (p_pitch < 1) { p_pitch = 1; } - random_pitch = p_pitch; + random_pitch_scale = p_pitch; +} + +float AudioStreamRandomizer::get_random_pitch() const { + return random_pitch_scale; +} + +void AudioStreamRandomizer::set_random_volume_offset_db(float p_volume_offset_db) { + if (p_volume_offset_db < 0) { + p_volume_offset_db = 0; + } + random_volume_offset_db = p_volume_offset_db; +} + +float AudioStreamRandomizer::get_random_volume_offset_db() const { + return random_volume_offset_db; } -float AudioStreamRandomPitch::get_random_pitch() const { - return random_pitch; +void AudioStreamRandomizer::set_playback_mode(PlaybackMode p_playback_mode) { + playback_mode = p_playback_mode; } -Ref<AudioStreamPlayback> AudioStreamRandomPitch::instance_playback() { - Ref<AudioStreamPlaybackRandomPitch> playback; +AudioStreamRandomizer::PlaybackMode AudioStreamRandomizer::get_playback_mode() const { + return playback_mode; +} + +Ref<AudioStreamPlayback> AudioStreamRandomizer::instance_playback_random() { + Ref<AudioStreamPlaybackRandomizer> playback; playback.instantiate(); - if (audio_stream.is_valid()) { - playback->playback = audio_stream->instance_playback(); + playbacks.insert(playback.ptr()); + playback->randomizer = Ref<AudioStreamRandomizer>((AudioStreamRandomizer *)this); + + double total_weight = 0; + Vector<PoolEntry> local_pool; + for (const PoolEntry &entry : audio_stream_pool) { + if (entry.stream.is_valid() && entry.weight > 0) { + local_pool.push_back(entry); + total_weight += entry.weight; + } + } + if (local_pool.is_empty()) { + return playback; + } + double chosen_cumulative_weight = Math::random(0.0, total_weight); + double cumulative_weight = 0; + for (PoolEntry &entry : local_pool) { + cumulative_weight += entry.weight; + if (cumulative_weight > chosen_cumulative_weight) { + playback->playback = entry.stream->instantiate_playback(); + last_playback = entry.stream; + 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->instantiate_playback(); + } + return playback; +} + +Ref<AudioStreamPlayback> AudioStreamRandomizer::instance_playback_no_repeats() { + Ref<AudioStreamPlaybackRandomizer> playback; + + double total_weight = 0; + Vector<PoolEntry> local_pool; + for (const PoolEntry &entry : audio_stream_pool) { + if (entry.stream == last_playback) { + continue; + } + if (entry.stream.is_valid() && entry.weight > 0) { + local_pool.push_back(entry); + total_weight += entry.weight; + } + } + if (local_pool.is_empty()) { + playback = instance_playback_random(); + WARN_PRINT("Playback stream pool is too small to prevent repeats."); + return playback; } + playback.instantiate(); playbacks.insert(playback.ptr()); - playback->random_pitch = Ref<AudioStreamRandomPitch>((AudioStreamRandomPitch *)this); + playback->randomizer = Ref<AudioStreamRandomizer>((AudioStreamRandomizer *)this); + double chosen_cumulative_weight = Math::random(0.0, total_weight); + double cumulative_weight = 0; + for (PoolEntry &entry : local_pool) { + cumulative_weight += entry.weight; + if (cumulative_weight > chosen_cumulative_weight) { + last_playback = entry.stream; + 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->instantiate_playback(); + } return playback; } -String AudioStreamRandomPitch::get_stream_name() const { - if (audio_stream.is_valid()) { - return "Random: " + audio_stream->get_name(); +Ref<AudioStreamPlayback> AudioStreamRandomizer::instance_playback_sequential() { + Ref<AudioStreamPlaybackRandomizer> playback; + playback.instantiate(); + playbacks.insert(playback.ptr()); + playback->randomizer = Ref<AudioStreamRandomizer>((AudioStreamRandomizer *)this); + + Vector<Ref<AudioStream>> local_pool; + for (const PoolEntry &entry : audio_stream_pool) { + if (entry.stream.is_null()) { + continue; + } + if (local_pool.find(entry.stream) != -1) { + WARN_PRINT("Duplicate stream in sequential playback pool"); + continue; + } + local_pool.push_back(entry.stream); + } + if (local_pool.is_empty()) { + return playback; + } + bool found_last_stream = false; + for (Ref<AudioStream> &entry : local_pool) { + if (found_last_stream) { + last_playback = entry; + playback->playback = entry->instantiate_playback(); + break; + } + if (entry == last_playback) { + found_last_stream = true; + } } - return "RandomPitch"; + if (playback->playback.is_null()) { + // Wrap around + last_playback = local_pool[0]; + playback->playback = local_pool.write[0]->instantiate_playback(); + } + return playback; } -float AudioStreamRandomPitch::get_length() const { - if (audio_stream.is_valid()) { - return audio_stream->get_length(); +Ref<AudioStreamPlayback> AudioStreamRandomizer::instantiate_playback() { + switch (playback_mode) { + case PLAYBACK_RANDOM: + return instance_playback_random(); + case PLAYBACK_RANDOM_NO_REPEATS: + return instance_playback_no_repeats(); + case PLAYBACK_SEQUENTIAL: + return instance_playback_sequential(); + default: + ERR_FAIL_V_MSG(nullptr, "Unhandled playback mode."); } +} +String AudioStreamRandomizer::get_stream_name() const { + return "Randomizer"; +} + +float AudioStreamRandomizer::get_length() const { return 0; } -void AudioStreamRandomPitch::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_audio_stream", "stream"), &AudioStreamRandomPitch::set_audio_stream); - ClassDB::bind_method(D_METHOD("get_audio_stream"), &AudioStreamRandomPitch::get_audio_stream); +bool AudioStreamRandomizer::is_monophonic() const { + for (const PoolEntry &entry : audio_stream_pool) { + if (entry.stream.is_valid() && entry.stream->is_monophonic()) { + return true; + } + } + return false; +} - ClassDB::bind_method(D_METHOD("set_random_pitch", "scale"), &AudioStreamRandomPitch::set_random_pitch); - ClassDB::bind_method(D_METHOD("get_random_pitch"), &AudioStreamRandomPitch::get_random_pitch); +bool AudioStreamRandomizer::_get(const StringName &p_name, Variant &r_ret) const { + if (AudioStream::_get(p_name, r_ret)) { + return true; + } + Vector<String> components = String(p_name).split("/", true, 2); + if (components.size() == 2 && components[0].begins_with("stream_") && components[0].trim_prefix("stream_").is_valid_int()) { + int index = components[0].trim_prefix("stream_").to_int(); + if (index < 0 || index >= (int)audio_stream_pool.size()) { + return false; + } - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "audio_stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_audio_stream", "get_audio_stream"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "random_pitch", PROPERTY_HINT_RANGE, "1,16,0.01"), "set_random_pitch", "get_random_pitch"); + if (components[1] == "stream") { + r_ret = get_stream(index); + return true; + } else if (components[1] == "weight") { + r_ret = get_stream_probability_weight(index); + return true; + } else { + return false; + } + } + return false; } -AudioStreamRandomPitch::AudioStreamRandomPitch() { - random_pitch = 1.1; +bool AudioStreamRandomizer::_set(const StringName &p_name, const Variant &p_value) { + if (AudioStream::_set(p_name, p_value)) { + return true; + } + Vector<String> components = String(p_name).split("/", true, 2); + if (components.size() == 2 && components[0].begins_with("stream_") && components[0].trim_prefix("stream_").is_valid_int()) { + int index = components[0].trim_prefix("stream_").to_int(); + if (index < 0 || index >= (int)audio_stream_pool.size()) { + return false; + } + + if (components[1] == "stream") { + set_stream(index, p_value); + return true; + } else if (components[1] == "weight") { + set_stream_probability_weight(index, p_value); + return true; + } else { + return false; + } + } + return false; } -void AudioStreamPlaybackRandomPitch::start(float p_from_pos) { +void AudioStreamRandomizer::_get_property_list(List<PropertyInfo> *p_list) const { + AudioStream::_get_property_list(p_list); // Define the trivial scalar properties. + p_list->push_back(PropertyInfo(Variant::NIL, "Streams", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); + for (int i = 0; i < audio_stream_pool.size(); i++) { + p_list->push_back(PropertyInfo(Variant::OBJECT, vformat("stream_%d/stream", i), PROPERTY_HINT_RESOURCE_TYPE, "AudioStream")); + p_list->push_back(PropertyInfo(Variant::FLOAT, vformat("stream_%d/weight", i), PROPERTY_HINT_RANGE, "0,100,0.001,or_greater")); + } +} + +void AudioStreamRandomizer::_bind_methods() { + ClassDB::bind_method(D_METHOD("add_stream", "index"), &AudioStreamRandomizer::add_stream); + ClassDB::bind_method(D_METHOD("move_stream", "index_from", "index_to"), &AudioStreamRandomizer::move_stream); + ClassDB::bind_method(D_METHOD("remove_stream", "index"), &AudioStreamRandomizer::remove_stream); + + ClassDB::bind_method(D_METHOD("set_stream", "index", "stream"), &AudioStreamRandomizer::set_stream); + ClassDB::bind_method(D_METHOD("get_stream", "index"), &AudioStreamRandomizer::get_stream); + ClassDB::bind_method(D_METHOD("set_stream_probability_weight", "index", "weight"), &AudioStreamRandomizer::set_stream_probability_weight); + ClassDB::bind_method(D_METHOD("get_stream_probability_weight", "index"), &AudioStreamRandomizer::get_stream_probability_weight); + + ClassDB::bind_method(D_METHOD("set_streams_count", "count"), &AudioStreamRandomizer::set_streams_count); + ClassDB::bind_method(D_METHOD("get_streams_count"), &AudioStreamRandomizer::get_streams_count); + + ClassDB::bind_method(D_METHOD("set_random_pitch", "scale"), &AudioStreamRandomizer::set_random_pitch); + ClassDB::bind_method(D_METHOD("get_random_pitch"), &AudioStreamRandomizer::get_random_pitch); + + ClassDB::bind_method(D_METHOD("set_random_volume_offset_db", "db_offset"), &AudioStreamRandomizer::set_random_volume_offset_db); + ClassDB::bind_method(D_METHOD("get_random_volume_offset_db"), &AudioStreamRandomizer::get_random_volume_offset_db); + + ClassDB::bind_method(D_METHOD("set_playback_mode", "mode"), &AudioStreamRandomizer::set_playback_mode); + ClassDB::bind_method(D_METHOD("get_playback_mode"), &AudioStreamRandomizer::get_playback_mode); + + ADD_ARRAY("streams", "stream_"); + 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.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() {} + +void AudioStreamPlaybackRandomizer::start(float p_from_pos) { playing = playback; - float range_from = 1.0 / random_pitch->random_pitch; - float range_to = random_pitch->random_pitch; + { + float range_from = 1.0 / randomizer->random_pitch_scale; + float range_to = randomizer->random_pitch_scale; - pitch_scale = range_from + Math::randf() * (range_to - range_from); + pitch_scale = range_from + Math::randf() * (range_to - range_from); + } + { + float range_from = -randomizer->random_volume_offset_db; + float range_to = randomizer->random_volume_offset_db; + + float volume_offset_db = range_from + Math::randf() * (range_to - range_from); + volume_scale = Math::db2linear(volume_offset_db); + } if (playing.is_valid()) { playing->start(p_from_pos); } } -void AudioStreamPlaybackRandomPitch::stop() { +void AudioStreamPlaybackRandomizer::stop() { if (playing.is_valid()) { playing->stop(); - ; } } -bool AudioStreamPlaybackRandomPitch::is_playing() const { +bool AudioStreamPlaybackRandomizer::is_playing() const { if (playing.is_valid()) { return playing->is_playing(); } @@ -430,7 +804,7 @@ bool AudioStreamPlaybackRandomPitch::is_playing() const { return false; } -int AudioStreamPlaybackRandomPitch::get_loop_count() const { +int AudioStreamPlaybackRandomizer::get_loop_count() const { if (playing.is_valid()) { return playing->get_loop_count(); } @@ -438,7 +812,7 @@ int AudioStreamPlaybackRandomPitch::get_loop_count() const { return 0; } -float AudioStreamPlaybackRandomPitch::get_playback_position() const { +float AudioStreamPlaybackRandomizer::get_playback_position() const { if (playing.is_valid()) { return playing->get_playback_position(); } @@ -446,13 +820,21 @@ float AudioStreamPlaybackRandomPitch::get_playback_position() const { return 0; } -void AudioStreamPlaybackRandomPitch::seek(float p_time) { +void AudioStreamPlaybackRandomizer::seek(float p_time) { if (playing.is_valid()) { playing->seek(p_time); } } -int AudioStreamPlaybackRandomPitch::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) { +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); } else { @@ -463,7 +845,7 @@ int AudioStreamPlaybackRandomPitch::mix(AudioFrame *p_buffer, float p_rate_scale } } -AudioStreamPlaybackRandomPitch::~AudioStreamPlaybackRandomPitch() { - random_pitch->playbacks.erase(this); +AudioStreamPlaybackRandomizer::~AudioStreamPlaybackRandomizer() { + randomizer->playbacks.erase(this); } ///////////////////////////////////////////// |