Updated the sound system (#18724)

* sdaf

* sdaf

* sdfa

* sadf

* sfda

* gfd

* reduce thrusters volume

* sdafsadsdaf

* sdfa

* Reduced some sound ranges and made some/more not ignore walls for loops

* health analyzers too

* ivdrip adjustment

* most tools now use play_tool_sound to have the sound played, reduced range for it
This commit is contained in:
Fluffy
2024-03-25 20:40:36 +00:00
committed by GitHub
parent 9faac2d684
commit 1b40dbce82
174 changed files with 1251 additions and 817 deletions
+203 -90
View File
@@ -1,136 +1,249 @@
/*
output_atoms (list of atoms) The destination(s) for the sounds
mid_sounds (list or soundfile) Since this can be either a list or a single soundfile you can have random sounds. May contain further lists but must contain a soundfile at the end.
mid_length (num) The length to wait between playing mid_sounds
start_sound (soundfile) Played before starting the mid_sounds loop
start_length (num) How long to wait before starting the main loop after playing start_sound
end_sound (soundfile) The sound played after the main loop has concluded
chance (num) Chance per loop to play a mid_sound
volume (num) Sound output volume
max_loops (num) The max amount of loops to run for.
direct (bool) If true plays directly to provided atoms instead of from them
*/
/**
* A datum for sounds that need to loop, with a high amount of configurability.
*/
/datum/looping_sound
///A `/list` of `/atom`, destinations for the sound
var/list/atom/output_atoms = list()
/**
* A list with soundfiles, or a soundfile, to play for the intermediate steps
*
* Must contain at least one sound
*/
/// (list or soundfile) Since this can be either a list or a single soundfile you can have random sounds. May contain further lists but must contain a soundfile at the end.
var/mid_sounds
///The length to wait between playing mid_sounds
/// The length of time to wait between playing mid_sounds.
var/mid_length
///Volume for the start sound
/// Amount of time to add/take away from the mid length, randomly
var/mid_length_vary = 0
/// If we should always play each sound once per loop of all sounds. Weights here only really effect order, and could be disgarded
var/each_once = FALSE
/// Whether if the sounds should be played in order or not. Defaults to FALSE.
var/in_order = FALSE
/// Override for volume of start sound.
var/start_volume
///A soundfile, played before starting the mid_sounds loop
/// (soundfile) Played before starting the mid_sounds loop.
var/start_sound
///How long to wait before starting the main loop after playing start_sound
/// How long to wait before starting the main loop after playing start_sound.
var/start_length
///Volume for the end sound
/// Override for volume of end sound.
var/end_volume
///A soundfile, the sound played after the main loop has concluded
/// (soundfile) The sound played after the main loop has concluded.
var/end_sound
///Chance per loop to play a mid_sound
/// Chance per loop to play a mid_sound.
var/chance
///Sound output volume
/// Sound output volume.
var/volume = 100
///Whether or not the sounds will vary in pitch when played
/// Whether or not the sounds will vary in pitch when played.
var/vary = FALSE
///The max amount of loops to run for
/// The max amount of loops to run for.
var/max_loops
/// The extra range of the sound in tiles, defaults to 0.
var/extra_range = 0
/// How much the sound will be affected by falloff per tile.
var/falloff_exponent
/// The falloff distance of the sound,
var/falloff_distance
/// Are the sounds affected by pressure? Defaults to TRUE.
var/pressure_affected = TRUE
/// Are the sounds subject to reverb? Defaults to TRUE.
var/use_reverb = TRUE
/// Are we ignoring walls? Defaults to TRUE.
var/ignore_walls = TRUE
// State stuff
/// The source of the sound, or the recipient of the sound.
var/atom/parent
/// The ID of the timer that's used to loop the sounds.
var/timer_id
/// Has the looping started yet?
var/loop_started = FALSE
/// If we're using cut_mid, this is the list we cut from
var/list/cut_list
///The index of the current song we're playing in the mid_sounds list, only used if in_order is used
///This is immediately set to 1, so we start the index at 0
var/audio_index = 0
// Args
/// Do we skip the starting sounds?
var/skip_starting_sounds = FALSE
/// If true, plays directly to provided atoms instead of from them.
var/direct
/// Sound channel to play on, random if not provided
var/sound_channel
///The extra range of the sound in tiles, defaults to 0.
var/extra_range
var/falloff
///The ID of the timer that's used to loop the sounds.
var/timerid
/datum/looping_sound/New(list/_output_atoms=list(), start_immediately=FALSE, _direct=FALSE)
/datum/looping_sound/New(_parent, start_immediately = FALSE, _direct = FALSE, _skip_starting_sounds = FALSE)
if(!mid_sounds)
crash_with("A looping sound datum was created without sounds to play.")
WARNING("A looping sound datum was created without sounds to play.")
return
output_atoms |= _output_atoms
set_parent(_parent)
direct = _direct
skip_starting_sounds = _skip_starting_sounds
if(start_immediately)
start()
/datum/looping_sound/Destroy()
stop()
output_atoms = null
stop(TRUE)
return ..()
/datum/looping_sound/proc/start(atom/add_thing)
if(add_thing)
output_atoms |= add_thing
if(timerid)
/**
* The proc to actually kickstart the whole sound sequence. This is what you should call to start the `looping_sound`.
*
* Arguments:
* * on_behalf_of - The new object to set as a parent.
*/
/datum/looping_sound/proc/start(on_behalf_of)
if(on_behalf_of)
set_parent(on_behalf_of)
if(timer_id)
return
on_start()
/datum/looping_sound/proc/stop(atom/remove_thing)
if(remove_thing)
output_atoms -= remove_thing
if(!timerid)
/**
* The proc to call to stop the sound loop.
*
* Arguments:
* * null_parent - Whether or not we should set the parent to null (useful when destroying the `looping_sound` itself). Defaults to FALSE.
*/
/datum/looping_sound/proc/stop(null_parent = FALSE)
stop_current()
if(null_parent)
set_parent(null)
if(!timer_id)
return
on_stop()
deltimer(timerid)
timerid = null
deltimer(timer_id, SSsound_loops)
timer_id = null
loop_started = FALSE
/datum/looping_sound/proc/sound_loop(starttime)
if(max_loops && (world.time >= starttime + mid_length * max_loops))
/// The proc that handles starting the actual core sound loop.
/datum/looping_sound/proc/start_sound_loop()
loop_started = TRUE
sound_loop()
timer_id = addtimer(CALLBACK(src, PROC_REF(sound_loop), world.time), mid_length, TIMER_CLIENT_TIME | TIMER_STOPPABLE | TIMER_LOOP | TIMER_DELETE_ME, SSsound_loops)
/**
* A simple proc handling the looping of the sound itself.
*
* Arguments:
* * start_time - The time at which the `mid_sounds` started being played (so we know when to stop looping).
*/
/datum/looping_sound/proc/sound_loop(start_time)
if(max_loops && world.time >= start_time + mid_length * max_loops)
stop()
return
// If we have a timer, we're varying mid length, and this is happening while we're runnin mid_sounds
if(timer_id && mid_length_vary && start_time)
updatetimedelay(timer_id, mid_length + rand(-mid_length_vary, mid_length_vary), timer_subsystem = SSsound_loops)
if(!chance || prob(chance))
play(get_sound(starttime))
if(!timerid)
timerid = addtimer(CALLBACK(src, PROC_REF(sound_loop), world.time), mid_length, TIMER_STOPPABLE | TIMER_LOOP)
play(get_sound())
/**
* Applies a new mid length to the sound
*/
/datum/looping_sound/proc/set_mid_length(new_mid)
mid_length = new_mid
if(!timer_id)
return
updatetimedelay(timer_id, mid_length + rand(-mid_length_vary, mid_length_vary), timer_subsystem = SSsound_loops)
/**
* The proc that handles actually playing the sound.
*
* Arguments:
* * soundfile - The soundfile we want to play.
* * volume_override - The volume we want to play the sound at, overriding the `volume` variable.
*/
/datum/looping_sound/proc/play(soundfile, volume_override)
var/list/atoms_cache = output_atoms
var/sound/S = sound(soundfile)
// if(direct)
// S.channel = open_sound_channel() someone could probably make this work but you could probably just delete this with no reprecussions.
// S.volume = volume
for(var/i in 1 to atoms_cache.len)
var/atom/thing = atoms_cache[i]
// if(direct)
// SEND_SOUND(thing, S)
// else
playsound(thing, S, volume_override || volume, vary, extra_range, falloff, required_preferences = ASFX_AMBIENCE) // you can turn it off, i guess.
var/sound/sound_to_play = sound(soundfile)
if(direct)
sound_to_play.channel = sound_channel || SSsounds.random_available_channel()
sound_to_play.volume = volume_override || volume //Use volume as fallback if theres no override
SEND_SOUND(parent, sound_to_play)
else
playsound(
parent,
sound_to_play,
volume,
vary,
extra_range,
falloff_exponent = falloff_exponent,
pressure_affected = pressure_affected,
ignore_walls = ignore_walls,
falloff_distance = falloff_distance,
use_reverb = use_reverb
)
/datum/looping_sound/proc/get_sound(starttime, _mid_sounds)
. = _mid_sounds || mid_sounds
/// Returns the sound we should now be playing.
/datum/looping_sound/proc/get_sound(_mid_sounds)
var/list/play_from = _mid_sounds || mid_sounds
if(!each_once)
. = play_from
while(!isfile(.) && !isnull(.))
. = pick_weight(.)
return .
if(in_order)
. = play_from
audio_index++
if(audio_index > length(play_from))
audio_index = 1
return .[audio_index]
if(!length(cut_list))
cut_list = shuffle(play_from.Copy())
var/list/tree = list()
. = cut_list
while(!isfile(.) && !isnull(.))
. = pickweight(.)
// Tree is a list of lists containign files
// If an entry in the tree goes to 0 length, we cut it from the list
tree += list(.)
. = pick_weight(.)
if(!isfile(.))
return
// Remove the sound file
tree[length(tree)] -= .
// Walk the tree bottom up, remove any lists that are empty
// Don't do anything for the topmost list, cause we do not care
for(var/i in length(tree) to 2 step -1)
var/list/branch = tree[i]
if(length(branch))
break
tree[i - 1] -= list(branch) // Remove the empty list
return .
/// A proc that's there to handle delaying the main sounds if there's a start_sound, and simply starting the sound loop in general.
/datum/looping_sound/proc/on_start()
var/start_wait = 0
if(start_sound)
if(start_sound && !skip_starting_sounds)
play(start_sound, start_volume)
start_wait = start_length
addtimer(CALLBACK(src, PROC_REF(sound_loop)), start_wait)
timer_id = addtimer(CALLBACK(src, PROC_REF(start_sound_loop)), start_wait, TIMER_CLIENT_TIME | TIMER_DELETE_ME | TIMER_STOPPABLE, SSsound_loops)
/// Stops sound playing on current channel, if specified
/datum/looping_sound/proc/stop_current()
if(!sound_channel || !ismob(parent))
return
var/mob/mob_parent = parent
mob_parent.stop_sound_channel(sound_channel)
/// Simple proc that's executed when the looping sound is stopped, so that the `end_sound` can be played, if there's one.
/datum/looping_sound/proc/on_stop()
if(end_sound)
if(end_sound && loop_started)
play(end_sound, end_volume)
/// A simple proc to change who our parent is set to, also handling registering and unregistering the QDELETING signals on the parent.
/datum/looping_sound/proc/set_parent(new_parent)
if(parent)
UnregisterSignal(parent, COMSIG_QDELETING)
parent = new_parent
if(parent)
RegisterSignal(parent, COMSIG_QDELETING, PROC_REF(handle_parent_del))
/// A simple proc that lets us know whether the sounds are currently active or not.
/datum/looping_sound/proc/is_active()
return !!timer_id
/// A simple proc to handle the deletion of the parent, so that it does not force it to hard-delete.
/datum/looping_sound/proc/handle_parent_del(datum/source)
SIGNAL_HANDLER
set_parent(null)
+13 -5
View File
@@ -5,6 +5,7 @@
mid_length = 10
end_sound = 'sound/machines/shower/shower_end.ogg'
volume = 20
extra_range = SHORT_RANGE_SOUND_EXTRARANGE
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -12,8 +13,8 @@
mid_sounds = list('sound/machines/sm/loops/calm.ogg' = 1)
mid_length = 60
volume = 40
extra_range = 10
falloff = 4
ignore_walls = FALSE
falloff_distance = 4
vary = TRUE
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -32,6 +33,7 @@
mid_sounds = list('sound/machines/grill/grillsizzle.ogg' = 1)
mid_length = 18
volume = 50
extra_range = SHORT_RANGE_SOUND_EXTRARANGE
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -42,7 +44,8 @@
mid_length = 13
end_sound = 'sound/machines/oven/oven_loop_end.ogg'
volume = 100
falloff = 4
falloff_distance = 4
extra_range = SHORT_RANGE_SOUND_EXTRARANGE
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -53,6 +56,7 @@
mid_length = 10
end_sound = 'sound/machines/microwave/microwave-end.ogg'
volume = 30
extra_range = SHORT_RANGE_SOUND_EXTRARANGE
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -67,7 +71,8 @@
'sound/machines/tcomms/tcomms_mid7.ogg' = 1
)
mid_length = 1.8 SECONDS
extra_range = -4.5
extra_range = SHORT_RANGE_SOUND_EXTRARANGE
ignore_walls = FALSE
volume = 10
// mid_length = 1.8 SECONDS
@@ -87,7 +92,8 @@
end_sound = 'sound/machines/computer/computer_end.ogg'
end_volume = 10
volume = 1
extra_range = -5.5
extra_range = SILENCED_SOUND_EXTRARANGE
ignore_walls = FALSE
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -96,6 +102,7 @@
mid_length = 1.8 SECONDS
extra_range = 10
volume = 70
ignore_walls = FALSE
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -103,5 +110,6 @@
mid_sounds = list('sound/machines/firealarm/FireAlarm1.ogg' = 1,'sound/machines/firealarm/FireAlarm2.ogg' = 1,'sound/machines/firealarm/FireAlarm3.ogg' = 1,'sound/machines/firealarm/FireAlarm4.ogg' = 1)
mid_length = 2.4 SECONDS
volume = 75
ignore_walls = FALSE
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+1 -1
View File
@@ -250,7 +250,7 @@ GLOBAL_DATUM_INIT(sound_player, /singleton/sound_player, new)
/datum/sound_token/proc/PrivGetEnvironment(listener)
var/area/A = get_area(listener)
return A && PrivIsValidEnvironment(A.sound_env) ? A.sound_env : sound.environment
return A && PrivIsValidEnvironment(A.sound_environment) ? A.sound_environment : sound.environment
/datum/sound_token/proc/PrivIsValidEnvironment(environment)
if(islist(environment) && length(environment) != 23)