mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-28 07:38:05 +01:00
Sound Optimizations and Debug Tools (#88517)
## About The Pull Request I was digging through sound code trying to add a few misc `.ogg`'s when I noticed a horrifying revelation. Sound calculations are broken as fuck. At first I didn't think it was a big deal until I remembered this comment in another PR:  I asked for the profile logs: ``` Profile results (total time) Proc Name Self CPU Total CPU Real Time Overtime Calls ---------------------------------------------------------------------------------------------- --------- --------- --------- --------- --------- /proc/playsound 4.771 26.841 26.902 0.347 153938 /mob/proc/playsound_local 9.511 15.689 15.891 0.724 429391 /mob/living/say 0.125 19.064 19.945 0.000 2550 /mob/living/carbon/updatehealth 0.878 8.305 8.341 0.013 49847 /mob/living/carbon/proc/handle_organs 1.486 9.263 9.300 0.000 70977 /obj/item/organ/internal/on_life 0.415 0.455 0.561 0.000 340312 /mob/living/carbon/Life 0.758 30.085 30.123 0.000 71933 /mob/living/proc/Life 5.509 39.404 39.512 0.000 401951 ``` Yeah, that is _really_ bad. Especially since it is a hot proc that is used half a million times. Optimizing this would help a decent amount. So now you are probably wondering, what exactly is the problem?! Let's take a look. https://www.desmos.com/calculator/sqdfl8ipgf Sounds are being broadcast on a large radius which is typically about ~17 tiles. Since the volume of a sound is reduced by distance, the further away the sound is, the quieter it becomes. You would think, hey that's fine! Sounds become inaudible at the 17 tile distance limit right? Except no, they generally become inaudible about 2/3rds of the way there. (~10 tile range) Sound volume is between 1-100. When the sound volume is lower than 3, it becomes (pretty much) inaudible. This means most of the sounds that are being spammed in game everywhere, you can't even hear!!! To fix this we used two major optimizations: 1. Add a `SOUND_AUDIBLE_VOLUME_MIN` to ignore any sound lower than this volume and calculate an audible distance 2. Use the new audible distance to drastically shorten the tile range of mob listeners using `get_hearers_in_view()` To give you an idea of how much better this is: Sounds that had a range of 17x17 (289 turfs) are now 12x12 (144 turfs). There is also large range machinery like the SM that has a 40x40 (1600 turfs) that is now 30x30 (900 turfs). It's quite an improvement. I also went and added a debugging tool to the admin/debug equipment set that can be found in the debug box. It is a pair of earmuffs that when worn has any sounds sent to the mob via a `to_chat` message displaying the sounds max range, distance from player, volume, and sound name. It is highly recommend to walk while wearing the earmuffs since walking stops your mob from emitting footstep sounds. ## Why It's Good For The Game The speed of sound is now faster than ever. Please don't break the sound barrier. ## Changelog 🆑 refactor: Sound has been heavily optimized and will now ignore low volume sounds from far away. admin: Add debugging sound earmuffs to admin equipment inside the debug box. Wear them to determine a sounds max range, distance, volume, and sound name. Highly recommended to walk otherwise you will get spammed with footstep sounds. /🆑
This commit is contained in:
@@ -17,6 +17,44 @@
|
||||
|
||||
#define MAX_INSTRUMENT_CHANNELS (128 * 6)
|
||||
|
||||
/// This is the lowest volume that can be used by playsound otherwise it gets ignored
|
||||
/// Most sounds around 10 volume can barely be heard. Almost all sounds at 5 volume or below are inaudible
|
||||
/// This is to prevent sound being spammed at really low volumes due to distance calculations
|
||||
/// Recommend setting this to anywhere from 10-3 (or 0 to disable any sound minimum volume restrictions)
|
||||
/// Ex. For a 70 volume sound, 17 tile range, 3 exponent, 2 falloff_distance:
|
||||
/// Setting SOUND_AUDIBLE_VOLUME_MIN to 0 for the above will result in 17x17 radius (289 turfs)
|
||||
/// Setting SOUND_AUDIBLE_VOLUME_MIN to 5 for the above will result in 14x14 radius (196 turfs)
|
||||
/// Setting SOUND_AUDIBLE_VOLUME_MIN to 10 for the above will result in 11x11 radius (121 turfs)
|
||||
#define SOUND_AUDIBLE_VOLUME_MIN 3
|
||||
|
||||
/* Calculates the max distance of a sound based on audible volume
|
||||
*
|
||||
* Note - you should NEVER pass in a volume that is lower than SOUND_AUDIBLE_VOLUME_MIN otherwise distance will be insanely large (like +250,000)
|
||||
*
|
||||
* Arguments:
|
||||
* * volume: The initial volume of the sound being played
|
||||
* * max_distance: The range of the sound in tiles (technically not real max distance since the furthest areas gets pruned due to SOUND_AUDIBLE_VOLUME_MIN)
|
||||
* * falloff_distance: Distance at which falloff begins. Sound is at peak volume (in regards to falloff) aslong as it is in this range.
|
||||
* * falloff_exponent: Rate of falloff for the audio. Higher means quicker drop to low volume. Should generally be over 1 to indicate a quick dive to 0 rather than a slow dive.
|
||||
* Returns: The max distance of a sound based on audible volume range
|
||||
*/
|
||||
#define CALCULATE_MAX_SOUND_AUDIBLE_DISTANCE(volume, max_distance, falloff_distance, falloff_exponent)\
|
||||
floor(((((-(max(max_distance - falloff_distance, 0) ** (1 / falloff_exponent)) / volume) * (SOUND_AUDIBLE_VOLUME_MIN - volume)) ** falloff_exponent) + falloff_distance))
|
||||
|
||||
/* Calculates the volume of a sound based on distance
|
||||
*
|
||||
* https://www.desmos.com/calculator/sqdfl8ipgf
|
||||
*
|
||||
* Arguments:
|
||||
* * volume: The initial volume of the sound being played
|
||||
* * distance: How far away the sound is in tiles from the source
|
||||
* * falloff_distance: Distance at which falloff begins. Sound is at peak volume (in regards to falloff) aslong as it is in this range.
|
||||
* * falloff_exponent: Rate of falloff for the audio. Higher means quicker drop to low volume. Should generally be over 1 to indicate a quick dive to 0 rather than a slow dive.
|
||||
* Returns: The max distance of a sound based on audible volume range
|
||||
*/
|
||||
#define CALCULATE_SOUND_VOLUME(volume, distance, max_distance, falloff_distance, falloff_exponent)\
|
||||
((max(distance - falloff_distance, 0) ** (1 / falloff_exponent)) / ((max(max_distance, distance) - falloff_distance) ** (1 / falloff_exponent)) * volume)
|
||||
|
||||
///Default range of a sound.
|
||||
#define SOUND_RANGE 17
|
||||
#define MEDIUM_RANGE_SOUND_EXTRARANGE -5
|
||||
|
||||
@@ -921,6 +921,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
|
||||
// Debug traits
|
||||
/// This object has light debugging tools attached to it
|
||||
#define TRAIT_LIGHTING_DEBUGGED "lighting_debugged"
|
||||
/// This object has sound debugging tools attached to it
|
||||
#define TRAIT_SOUND_DEBUGGED "sound_debugged"
|
||||
|
||||
/// Gives you the Shifty Eyes quirk, rarely making people who examine you think you examined them back even when you didn't
|
||||
#define TRAIT_SHIFTY_EYES "shifty_eyes"
|
||||
|
||||
@@ -492,6 +492,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
|
||||
"TRAIT_SNOB" = TRAIT_SNOB,
|
||||
"TRAIT_SOFTSPOKEN" = TRAIT_SOFTSPOKEN,
|
||||
"TRAIT_SOOTHED_THROAT" = TRAIT_SOOTHED_THROAT,
|
||||
"TRAIT_SOUND_DEBUGGED" = TRAIT_SOUND_DEBUGGED,
|
||||
"TRAIT_SPACEWALK" = TRAIT_SPACEWALK,
|
||||
"TRAIT_SPARRING" = TRAIT_SPARRING,
|
||||
"TRAIT_SPEAKS_CLEARLY" = TRAIT_SPEAKS_CLEARLY,
|
||||
|
||||
@@ -82,6 +82,7 @@
|
||||
/obj/item/storage/box/material=1,
|
||||
/obj/item/uplink/debug=1,
|
||||
/obj/item/uplink/nuclear/debug=1,
|
||||
/obj/item/clothing/ears/earmuffs/debug = 1,
|
||||
)
|
||||
generate_items_inside(items_inside,src)
|
||||
|
||||
|
||||
+17
-9
@@ -51,6 +51,9 @@
|
||||
if (!turf_source || !soundin || !vol)
|
||||
return
|
||||
|
||||
if(vol < SOUND_AUDIBLE_VOLUME_MIN) // never let sound go below SOUND_AUDIBLE_VOLUME_MIN or bad things will happen
|
||||
return
|
||||
|
||||
//allocate a channel if necessary now so its the same for everyone
|
||||
channel = channel || SSsounds.random_available_channel()
|
||||
|
||||
@@ -64,8 +67,9 @@
|
||||
var/turf/above_turf = GET_TURF_ABOVE(turf_source)
|
||||
var/turf/below_turf = GET_TURF_BELOW(turf_source)
|
||||
|
||||
if(ignore_walls)
|
||||
var/audible_distance = CALCULATE_MAX_SOUND_AUDIBLE_DISTANCE(vol, maxdistance, falloff_distance, falloff_exponent)
|
||||
|
||||
if(ignore_walls)
|
||||
if(above_turf && istransparentturf(above_turf))
|
||||
listeners += SSmobs.clients_by_zlevel[above_turf.z]
|
||||
|
||||
@@ -73,16 +77,16 @@
|
||||
listeners += SSmobs.clients_by_zlevel[below_turf.z]
|
||||
|
||||
else //these sounds don't carry through walls
|
||||
listeners = get_hearers_in_view(maxdistance, turf_source)
|
||||
listeners = get_hearers_in_view(audible_distance, turf_source)
|
||||
|
||||
if(above_turf && istransparentturf(above_turf))
|
||||
listeners += get_hearers_in_view(maxdistance, above_turf)
|
||||
listeners += get_hearers_in_view(audible_distance, above_turf)
|
||||
|
||||
if(below_turf && istransparentturf(turf_source))
|
||||
listeners += get_hearers_in_view(maxdistance, below_turf)
|
||||
listeners += get_hearers_in_view(audible_distance, below_turf)
|
||||
|
||||
for(var/mob/listening_mob in listeners | SSmobs.dead_players_by_zlevel[source_z])//observers always hear through walls
|
||||
if(get_dist(listening_mob, turf_source) <= maxdistance)
|
||||
if(get_dist(listening_mob, turf_source) <= audible_distance)
|
||||
listening_mob.playsound_local(turf_source, soundin, vol, vary, frequency, falloff_exponent, channel, pressure_affected, S, maxdistance, falloff_distance, 1, use_reverb)
|
||||
. += listening_mob
|
||||
|
||||
@@ -122,15 +126,16 @@
|
||||
else
|
||||
sound_to_use.frequency = get_rand_frequency()
|
||||
|
||||
var/distance = 0
|
||||
|
||||
if(isturf(turf_source))
|
||||
var/turf/turf_loc = get_turf(src)
|
||||
|
||||
//sound volume falloff with distance
|
||||
var/distance = get_dist(turf_loc, turf_source) * distance_multiplier
|
||||
distance = get_dist(turf_loc, turf_source) * distance_multiplier
|
||||
|
||||
if(max_distance) //If theres no max_distance we're not a 3D sound, so no falloff.
|
||||
sound_to_use.volume -= (max(distance - falloff_distance, 0) ** (1 / falloff_exponent)) / ((max(max_distance, distance) - falloff_distance) ** (1 / falloff_exponent)) * sound_to_use.volume
|
||||
//https://www.desmos.com/calculator/sqdfl8ipgf
|
||||
sound_to_use.volume -= CALCULATE_SOUND_VOLUME(vol, distance, max_distance, falloff_distance, falloff_exponent)
|
||||
|
||||
if(pressure_affected)
|
||||
//Atmosphere affects sound
|
||||
@@ -151,7 +156,7 @@
|
||||
sound_to_use.volume *= pressure_factor
|
||||
//End Atmosphere affecting sound
|
||||
|
||||
if(sound_to_use.volume <= 0)
|
||||
if(sound_to_use.volume < SOUND_AUDIBLE_VOLUME_MIN)
|
||||
return //No sound
|
||||
|
||||
var/dx = turf_source.x - turf_loc.x // Hearing from the right/left
|
||||
@@ -176,6 +181,9 @@
|
||||
sound_to_use.echo[3] = 0 //Room setting, 0 means normal reverb
|
||||
sound_to_use.echo[4] = 0 //RoomHF setting, 0 means normal reverb.
|
||||
|
||||
if(HAS_TRAIT(src, TRAIT_SOUND_DEBUGGED))
|
||||
to_chat(src, span_admin("Max Range-[max_distance] Distance-[distance] Vol-[round(sound_to_use.volume, 0.01)] Sound-[sound_to_use.file]"))
|
||||
|
||||
SEND_SOUND(src, sound_to_use)
|
||||
|
||||
/proc/sound_to_playing_players(soundin, volume = 100, vary = FALSE, frequency = 0, channel = 0, pressure_affected = FALSE, sound/S)
|
||||
|
||||
@@ -27,3 +27,8 @@
|
||||
AddElement(/datum/element/earhealing)
|
||||
AddComponent(/datum/component/wearertargeting/earprotection, list(ITEM_SLOT_EARS))
|
||||
AddComponent(/datum/component/adjust_fishing_difficulty, -2)
|
||||
|
||||
/obj/item/clothing/ears/earmuffs/debug
|
||||
name = "debug earmuffs"
|
||||
desc = "Wearing these sends a chat message for every sound played. Walking to ignore footsteps is highly recommended."
|
||||
clothing_traits = list(TRAIT_SOUND_DEBUGGED)
|
||||
|
||||
Reference in New Issue
Block a user