extends Node3D # This Scene is meant for playing back sound groups. For simple, singular sound effects use stock Godot nodes, this is meant to play a random sound among a group with variations. signal SoundPlayer_finished const SFX_dir = "res://Assets/SFX" # all sound clips must reside somewhere in this directory @export_file("*01.wav") var SoundClip := SFX_dir + "/" + "Test_01.wav" @export var AutoPlay := false @export var MinimumRandomDistance := 0.35 # gives optimal playback repetition for sound clip groups of different sizes. @export var PlayUntilEnd := false # determines if the play() function is allowed to sop a previously started sound @export var MinDelay := 0.0 # determines how many seconds must pass before the sound can be triggered again @export var PitchScale := 1.0 @export var RandomizePitch := 0.0 @export var Voice_Count := 1 var min_distance = 0 # this determines how ofte na sound is allowed to play (any Nth time) this is calculated automatically based on maximum_repetition var clips = [] # holds loaded sound stream resources var recently_played = [] # holds indexes of recently played var ready_to_play = true # used as a semaphor for MinDelay var voices = [] var voice = 0 var debug = false func _ready(): var files = [] var dir = Directory.new() dir.open(SFX_dir) dir.list_dir_begin() if debug: print("SoundClip: ", SoundClip) # determine the sound group name part #var group = SoundClip.left(SoundClip.rfind('_') -2).right(SoundClip.rfind('/') + 1) #var group = SoundClip.left(SoundClip.rfind('_')).right(SoundClip.rfind('/')) var group = SoundClip.trim_prefix("res://").trim_suffix('_01.wav').get_file() if false: # temporarily disabled, needs work # determine the sound layer name part var layer = SoundClip.right(SoundClip.rfind('01') + 1) layer = layer.right(layer.rfind('_') -1) layer = layer.left(layer.rfind('.') - 2) if layer == "1": # sound without a layer defined will return "1", so let's take that as a "no layers defined in this sound" layer = "" else: # if the layers was specified the group will be incorrectly including the variant number, let's fix that group = group.left(group.rfind('_')) if debug: print("group: ", group) #print("layer: ", layer) while true: var file = dir.get_next() #print(file) if file == "": break elif not file.begins_with(".") and file.begins_with(group) and file.ends_with(".wav"): files.append(file) # temporarily disabled, not sure what this does # if layer.length() == 0: # no layer specified? # files.append(file) # if debug: # print("no layer specified - adding ", file) # elif file.find(layer) > -1: # chek if the file name contains the layer string # files.append(file) # if debug: # print("layer matches - adding ", file) dir.list_dir_end() if debug: print("files in list: \n", files) for f in files: var res_file = SFX_dir + '/' + f var clip = load(res_file) clips.append(clip) if debug: print("loading ", res_file, "; result: ", clip) var clip_count = clips.size() if MinimumRandomDistance: min_distance = floor(clip_count * MinimumRandomDistance) else: min_distance = 0 if debug: print("Clips: ", len(clips)) print("min_distance: ", min_distance) # prepare voices - TODO: this does not work! as aworkaround I've duplicated the secondary AudioStreamPlayer3D manually if Voice_Count > 1: for i in range(1, Voice_Count): var new_voice = $AudioStreamPlayer3D.duplicate() add_child(new_voice) for i in get_children(): voices.append(i) if debug: print("voices: ", voices) if AutoPlay: play() func pick_random(): assert(len(clips) > 0, "SoundPlayer has no clips to choose from") return randi() % len(clips) @rpc(call_local, any_peer, reliable) func set_unit_db(value): $AudioStreamPlayer3D.unit_db = value @rpc(call_local, any_peer, reliable) func play(): # temporary if RandomizePitch != 0: $AudioStreamPlayer3D.pitch_scale = PitchScale + randf_range(-RandomizePitch /2, RandomizePitch/2) else: $AudioStreamPlayer3D.pitch_scale = PitchScale $AudioStreamPlayer3D.play() return # the functionality fo this node is disabled until the Godot not bundling wave files in exports issue is solved var player = voices[voice] voice = (voice + 1) % voices.size() if debug: print("playing ", name, " on voice ", voice) if PlayUntilEnd: if player.playing: return 1 if MinDelay > 0: if not ready_to_play: return 2 var i = pick_random() while recently_played.has(i): i = pick_random() #print("i: ", i) recently_played.append(i) if len(recently_played) > min_distance: recently_played.remove(0) if debug: print("random pick: ", clips[i]) player.stream = clips[i] if RandomizePitch != 0: player.pitch_scale = PitchScale + randf_range(-RandomizePitch /2, RandomizePitch/2) else: player.pitch_scale = PitchScale player.play() #ready_to_play = false #await get_tree().create_timer(MinDelay) ready_to_play = true #$Timer.start(MinDelay) # TODO implement final randomization algorithm # Called every frame. 'delta' is the elapsed time since the previous frame. #func _process(delta): # pass func _on_AudioStreamPlayer3D_finished(): emit_signal(&"SoundPlayer_finished")