2021-09-12 02:34:08 +02:00
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.
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
2021-09-12 04:28:38 +02:00
var debug = false
2021-09-12 02:34:08 +02:00
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
2021-09-12 03:48:39 +02:00
#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 ( )
2021-09-12 03:03:52 +02:00
if false : # temporarily disabled, needs work
# determine the sound layer name part
var layer = SoundClip . right ( SoundClip . rfind ( ' 01 ' ) + 1 )
2021-09-12 03:48:39 +02:00
layer = layer . right ( layer . rfind ( ' _ ' ) - 1 )
layer = layer . left ( layer . rfind ( ' . ' ) - 2 )
2021-09-12 03:03:52 +02:00
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 ( ' _ ' ) )
2021-09-12 03:48:39 +02:00
if debug :
print ( " group: " , group )
#print("layer: ", layer)
2021-09-12 02:34:08 +02:00
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 " ) :
2021-09-12 03:03:52 +02:00
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)
2021-09-12 02:34:08 +02:00
dir . list_dir_end ( )
if debug :
print ( " files in list: \n " , files )
for f in files :
2021-09-12 03:03:52 +02:00
var res_file = SFX_dir + ' / ' + f
2021-09-12 02:34:08 +02:00
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
2021-09-12 13:17:32 +02:00
if debug :
print ( " Clips: " , len ( clips ) )
print ( " min_distance: " , min_distance )
2021-09-12 02:34:08 +02:00
# 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 :
2021-09-12 13:34:54 +02:00
play ( )
2021-09-12 02:34:08 +02:00
func pick_random ( ) :
2021-09-12 03:03:52 +02:00
assert ( len ( clips ) > 0 , " SoundPlayer has no clips to choose from " )
2021-09-12 02:34:08 +02:00
return randi ( ) % len ( clips )
@ rpc ( sync , reliable ) func play ( ) :
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 ( )
2021-09-12 13:17:32 +02:00
#ready_to_play = false
#await get_tree().create_timer(MinDelay)
2021-09-12 03:03:52 +02:00
ready_to_play = true
2021-09-12 02:34:08 +02:00
2021-09-12 03:03:52 +02:00
#$Timer.start(MinDelay)
2021-09-12 02:34:08 +02:00
# TODO implement final randomization algorithm
# Called every frame. 'delta' is the elapsed time since the previous frame.
#func _process(delta):
# pass