ports my baystruments updates from tg (#13219)

* subsystem

* fix

* e

* update

* fix
This commit is contained in:
silicons
2020-08-25 12:04:42 -07:00
committed by GitHub
parent 8699ce2174
commit 973839dee4
4 changed files with 221 additions and 110 deletions

View File

@@ -4,16 +4,26 @@ PROCESSING_SUBSYSTEM_DEF(instruments)
init_order = INIT_ORDER_INSTRUMENTS init_order = INIT_ORDER_INSTRUMENTS
flags = SS_KEEP_TIMING flags = SS_KEEP_TIMING
priority = FIRE_PRIORITY_INSTRUMENTS priority = FIRE_PRIORITY_INSTRUMENTS
var/static/list/datum/instrument/instrument_data = list() //id = datum /// List of all instrument data, associative id = datum
var/static/list/datum/instrument/instrument_data = list()
/// List of all song datums.
var/static/list/datum/song/songs = list() var/static/list/datum/song/songs = list()
/// Max lines in songs
var/static/musician_maxlines = 600 var/static/musician_maxlines = 600
/// Max characters per line in songs
var/static/musician_maxlinechars = 300 var/static/musician_maxlinechars = 300
/// Deciseconds between hearchecks. Too high and instruments seem to lag when people are moving around in terms of who can hear it. Too low and the server lags from this.
var/static/musician_hearcheck_mindelay = 5 var/static/musician_hearcheck_mindelay = 5
/// Maximum instrument channels total instruments are allowed to use. This is so you don't have instruments deadlocking all sound channels.
var/static/max_instrument_channels = MAX_INSTRUMENT_CHANNELS var/static/max_instrument_channels = MAX_INSTRUMENT_CHANNELS
/// Current number of channels allocated for instruments
var/static/current_instrument_channels = 0 var/static/current_instrument_channels = 0
/// Single cached list for synthesizer instrument ids, so you don't have to have a new list with every synthesizer.
var/static/list/synthesizer_instrument_ids
/datum/controller/subsystem/processing/instruments/Initialize() /datum/controller/subsystem/processing/instruments/Initialize()
initialize_instrument_data() initialize_instrument_data()
synthesizer_instrument_ids = get_allowed_instrument_ids()
return ..() return ..()
/datum/controller/subsystem/processing/instruments/proc/on_song_new(datum/song/S) /datum/controller/subsystem/processing/instruments/proc/on_song_new(datum/song/S)
@@ -29,7 +39,10 @@ PROCESSING_SUBSYSTEM_DEF(instruments)
continue continue
I = new path I = new path
I.Initialize() I.Initialize()
instrument_data[I.id || "[I.type]"] = I if(!I.id)
qdel(I)
continue
instrument_data[I.id] = I
CHECK_TICK CHECK_TICK
/datum/controller/subsystem/processing/instruments/proc/get_instrument(id_or_path) /datum/controller/subsystem/processing/instruments/proc/get_instrument(id_or_path)

View File

@@ -2,6 +2,12 @@
#define MUSIC_MAXLINES 1000 #define MUSIC_MAXLINES 1000
#define MUSIC_MAXLINECHARS 300 #define MUSIC_MAXLINECHARS 300
/**
* # Song datum
*
* These are the actual backend behind instruments.
* They attach to an atom and provide the editor + playback functionality.
*/
/datum/song /datum/song
/// Name of the song /// Name of the song
var/name = "Untitled" var/name = "Untitled"
@@ -15,6 +21,9 @@
/// delay between notes in deciseconds /// delay between notes in deciseconds
var/tempo = 5 var/tempo = 5
/// How far we can be heard
var/instrument_range = 15
/// Are we currently playing? /// Are we currently playing?
var/playing = FALSE var/playing = FALSE
@@ -53,17 +62,24 @@
/////////////////// Playing variables //////////////// /////////////////// Playing variables ////////////////
/** /**
* Only used in synthesized playback - The chords we compiled. Non assoc list of lists: * Build by compile_chords()
* list(list(key1, key2, key3..., tempo_divisor), list(key1, key2..., tempo_divisor), ...) * Must be rebuilt on instrument switch.
* tempo_divisor always exists
* if key1 (and so if there's no keys) doesn't exist it's a rest
* Compilation happens when we start playing and is cleared after we finish playing. * Compilation happens when we start playing and is cleared after we finish playing.
* Format: list of chord lists, with chordlists having (key1, key2, key3, tempodiv)
*/ */
var/list/compiled_chords var/list/compiled_chords
/// Current section of a long chord we're on, so we don't need to make a billion chords, one for every unit ticklag.
var/elapsed_delay
/// Amount of delay to wait before playing the next chord
var/delay_by
/// Current chord we're on.
var/current_chord
/// Channel as text = current volume percentage but it's 0 to 100 instead of 0 to 1. /// Channel as text = current volume percentage but it's 0 to 100 instead of 0 to 1.
var/list/channels_playing = list() var/list/channels_playing = list()
/// List of channels that aren't being used, as text. This is to prevent unnecessary freeing and reallocations from SSsounds/SSinstruments. /// List of channels that aren't being used, as text. This is to prevent unnecessary freeing and reallocations from SSsounds/SSinstruments.
var/list/channels_idle = list() var/list/channels_idle = list()
/// Person playing us
var/mob/user_playing
////////////////////////////////////////////////////// //////////////////////////////////////////////////////
/// Last world.time we checked for who can hear us /// Last world.time we checked for who can hear us
@@ -72,8 +88,6 @@
var/list/hearing_mobs var/list/hearing_mobs
/// If this is enabled, some things won't be strictly cleared when they usually are (liked compiled_chords on play stop) /// If this is enabled, some things won't be strictly cleared when they usually are (liked compiled_chords on play stop)
var/debug_mode = FALSE var/debug_mode = FALSE
/// Last time we processed decay
var/last_process_decay
/// Max sound channels to occupy /// Max sound channels to occupy
var/max_sound_channels = CHANNELS_PER_INSTRUMENT var/max_sound_channels = CHANNELS_PER_INSTRUMENT
/// Current channels, so we can save a length() call. /// Current channels, so we can save a length() call.
@@ -113,7 +127,7 @@
var/cached_exponential_dropoff = 1.045 var/cached_exponential_dropoff = 1.045
///////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////
/datum/song/New(atom/parent, list/instrument_ids) /datum/song/New(atom/parent, list/instrument_ids, new_range)
SSinstruments.on_song_new(src) SSinstruments.on_song_new(src)
lines = list() lines = list()
tempo = sanitize_tempo(tempo) tempo = sanitize_tempo(tempo)
@@ -125,6 +139,8 @@
hearing_mobs = list() hearing_mobs = list()
volume = clamp(volume, min_volume, max_volume) volume = clamp(volume, min_volume, max_volume)
update_sustain() update_sustain()
if(new_range)
instrument_range = new_range
/datum/song/Destroy() /datum/song/Destroy()
stop_playing() stop_playing()
@@ -135,12 +151,15 @@
parent = null parent = null
return ..() return ..()
/**
* Checks and stores which mobs can hear us. Terminates sounds for mobs that leave our range.
*/
/datum/song/proc/do_hearcheck() /datum/song/proc/do_hearcheck()
last_hearcheck = world.time last_hearcheck = world.time
var/list/old = hearing_mobs.Copy() var/list/old = hearing_mobs.Copy()
hearing_mobs.len = 0 hearing_mobs.len = 0
var/turf/source = get_turf(parent) var/turf/source = get_turf(parent)
for(var/mob/M in get_hearers_in_view(15, source)) for(var/mob/M in get_hearers_in_view(instrument_range, source))
if(!(M?.client?.prefs?.toggles & SOUND_INSTRUMENTS)) if(!(M?.client?.prefs?.toggles & SOUND_INSTRUMENTS))
continue continue
hearing_mobs[M] = get_dist(M, source) hearing_mobs[M] = get_dist(M, source)
@@ -148,10 +167,15 @@
for(var/i in exited) for(var/i in exited)
terminate_sound_mob(i) terminate_sound_mob(i)
/// I can either be a datum, id, or path (if the instrument has no id). /**
* Sets our instrument, caching anything necessary for faster accessing. Accepts an ID, typepath, or instantiated instrument datum.
*/
/datum/song/proc/set_instrument(datum/instrument/I) /datum/song/proc/set_instrument(datum/instrument/I)
terminate_all_sounds()
var/old_legacy
if(using_instrument) if(using_instrument)
using_instrument.songs_using -= src using_instrument.songs_using -= src
old_legacy = (using_instrument.instrument_flags & INSTRUMENT_LEGACY)
using_instrument = null using_instrument = null
cached_samples = null cached_samples = null
cached_legacy_ext = null cached_legacy_ext = null
@@ -162,7 +186,7 @@
if(istype(I)) if(istype(I))
using_instrument = I using_instrument = I
I.songs_using += src I.songs_using += src
var/instrument_legacy = CHECK_BITFIELD(I.instrument_flags, INSTRUMENT_LEGACY) var/instrument_legacy = (I.instrument_flags & INSTRUMENT_LEGACY)
if(instrument_legacy) if(instrument_legacy)
cached_legacy_ext = I.legacy_instrument_ext cached_legacy_ext = I.legacy_instrument_ext
cached_legacy_dir = I.legacy_instrument_path cached_legacy_dir = I.legacy_instrument_path
@@ -170,23 +194,37 @@
else else
cached_samples = I.samples cached_samples = I.samples
legacy = FALSE legacy = FALSE
if(isnull(old_legacy) || (old_legacy != instrument_legacy))
if(playing)
compile_chords()
/// THIS IS A BLOCKING CALL. /**
* Attempts to start playing our song.
*/
/datum/song/proc/start_playing(mob/user) /datum/song/proc/start_playing(mob/user)
if(playing) if(playing)
return return
if(!using_instrument?.ready()) if(!using_instrument?.ready())
to_chat(user, "<span class='warning'>An error has occured with [src]. Please reset the instrument.</span>") to_chat(user, "<span class='warning'>An error has occured with [src]. Please reset the instrument.</span>")
return return
compile_chords()
if(!length(compiled_chords))
to_chat(user, "<span class='warning'>Song is empty.</span>")
return
playing = TRUE playing = TRUE
updateDialog() updateDialog(user_playing)
//we can not afford to runtime, since we are going to be doing sound channel reservations and if we runtime it means we have a channel allocation leak. //we can not afford to runtime, since we are going to be doing sound channel reservations and if we runtime it means we have a channel allocation leak.
//wrap the rest of the stuff to ensure stop_playing() is called. //wrap the rest of the stuff to ensure stop_playing() is called.
last_process_decay = world.time do_hearcheck()
elapsed_delay = 0
delay_by = 0
current_chord = 1
user_playing = user
START_PROCESSING(SSinstruments, src) START_PROCESSING(SSinstruments, src)
. = do_play_lines(user)
stop_playing()
/**
* Stops playing, terminating all sounds if in synthesized mode. Clears hearing_mobs.
*/
/datum/song/proc/stop_playing() /datum/song/proc/stop_playing()
if(!playing) if(!playing)
return return
@@ -196,42 +234,93 @@
STOP_PROCESSING(SSinstruments, src) STOP_PROCESSING(SSinstruments, src)
terminate_all_sounds(TRUE) terminate_all_sounds(TRUE)
hearing_mobs.len = 0 hearing_mobs.len = 0
updateDialog() user_playing = null
/// THIS IS A BLOCKING CALL. /**
/datum/song/proc/do_play_lines(user) * Processes our song.
if(!playing) */
/datum/song/proc/process_song(wait)
if(!length(compiled_chords) || should_stop_playing(user_playing))
stop_playing()
return return
do_hearcheck() var/list/chord = compiled_chords[current_chord]
if(legacy) if(++elapsed_delay >= delay_by)
do_play_lines_legacy(user) play_chord(chord)
else elapsed_delay = 0
do_play_lines_synthesized(user) delay_by = tempodiv_to_delay(chord[length(chord)])
current_chord++
if(current_chord > length(compiled_chords))
if(repeat)
repeat--
current_chord = 1
return
else
stop_playing()
return
/**
* Converts a tempodiv to ticks to elapse before playing the next chord, taking into account our tempo.
*/
/datum/song/proc/tempodiv_to_delay(tempodiv)
if(!tempodiv)
tempodiv = 1 // no division by 0. some song converters tend to use 0 for when it wants to have no div, for whatever reason.
return max(1, round((tempo/tempodiv) / world.tick_lag, 1))
/**
* Compiles chords.
*/
/datum/song/proc/compile_chords()
legacy? compile_legacy() : compile_synthesized()
/**
* Plays a chord.
*/
/datum/song/proc/play_chord(list/chord)
// last value is timing information
for(var/i in 1 to (length(chord) - 1))
legacy? playkey_legacy(chord[i][1], chord[i][2], chord[i][3], user_playing) : playkey_synth(chord[i], user_playing)
/**
* Checks if we should halt playback.
*/
/datum/song/proc/should_stop_playing(mob/user) /datum/song/proc/should_stop_playing(mob/user)
return QDELETED(parent) || !using_instrument || !playing return QDELETED(parent) || !using_instrument || !playing
/**
* Sanitizes tempo to a value that makes sense and fits the current world.tick_lag.
*/
/datum/song/proc/sanitize_tempo(new_tempo) /datum/song/proc/sanitize_tempo(new_tempo)
new_tempo = abs(new_tempo) new_tempo = abs(new_tempo)
return clamp(round(new_tempo, world.tick_lag), world.tick_lag, 5 SECONDS) return clamp(round(new_tempo, world.tick_lag), world.tick_lag, 5 SECONDS)
/**
* Gets our beats per minute based on our tempo.
*/
/datum/song/proc/get_bpm() /datum/song/proc/get_bpm()
return 600 / tempo return 600 / tempo
/**
* Sets our tempo from a beats-per-minute, sanitizing it to a valid number first.
*/
/datum/song/proc/set_bpm(bpm) /datum/song/proc/set_bpm(bpm)
tempo = sanitize_tempo(600 / bpm) tempo = sanitize_tempo(600 / bpm)
/// Updates the window for our user. Override in subtypes. /**
/datum/song/proc/updateDialog(mob/user = usr) * Updates the window for our users. Override down the line.
*/
/datum/song/proc/updateDialog(mob/user)
ui_interact(user) ui_interact(user)
/datum/song/process(wait) /datum/song/process(wait)
if(!playing) if(!playing)
return PROCESS_KILL return PROCESS_KILL
var/delay = world.time - last_process_decay // it's expected this ticks at every world.tick_lag. if it lags, do not attempt to catch up.
process_decay(delay) process_song(world.tick_lag)
last_process_decay = world.time process_decay(world.tick_lag)
/**
* Updates our cached linear/exponential falloff stuff, saving calculations down the line.
*/
/datum/song/proc/update_sustain() /datum/song/proc/update_sustain()
// Exponential is easy // Exponential is easy
cached_exponential_dropoff = sustain_exponential_dropoff cached_exponential_dropoff = sustain_exponential_dropoff
@@ -241,21 +330,33 @@
var/volume_decrease_per_decisecond = volume_diff / target_duration var/volume_decrease_per_decisecond = volume_diff / target_duration
cached_linear_dropoff = volume_decrease_per_decisecond cached_linear_dropoff = volume_decrease_per_decisecond
/**
* Setter for setting output volume.
*/
/datum/song/proc/set_volume(volume) /datum/song/proc/set_volume(volume)
src.volume = clamp(volume, max(0, min_volume), min(100, max_volume)) src.volume = clamp(volume, max(0, min_volume), min(100, max_volume))
update_sustain() update_sustain()
updateDialog() updateDialog()
/**
* Setter for setting how low the volume has to get before a note is considered "dead" and dropped
*/
/datum/song/proc/set_dropoff_volume(volume) /datum/song/proc/set_dropoff_volume(volume)
sustain_dropoff_volume = clamp(volume, INSTRUMENT_MIN_SUSTAIN_DROPOFF, 100) sustain_dropoff_volume = clamp(volume, INSTRUMENT_MIN_SUSTAIN_DROPOFF, 100)
update_sustain() update_sustain()
updateDialog() updateDialog()
/**
* Setter for setting exponential falloff factor.
*/
/datum/song/proc/set_exponential_drop_rate(drop) /datum/song/proc/set_exponential_drop_rate(drop)
sustain_exponential_dropoff = clamp(drop, INSTRUMENT_EXP_FALLOFF_MIN, INSTRUMENT_EXP_FALLOFF_MAX) sustain_exponential_dropoff = clamp(drop, INSTRUMENT_EXP_FALLOFF_MIN, INSTRUMENT_EXP_FALLOFF_MAX)
update_sustain() update_sustain()
updateDialog() updateDialog()
/**
* Setter for setting linear falloff duration.
*/
/datum/song/proc/set_linear_falloff_duration(duration) /datum/song/proc/set_linear_falloff_duration(duration)
sustain_linear_duration = clamp(duration, 0.1, INSTRUMENT_MAX_TOTAL_SUSTAIN) sustain_linear_duration = clamp(duration, 0.1, INSTRUMENT_MAX_TOTAL_SUSTAIN)
update_sustain() update_sustain()
@@ -277,10 +378,8 @@
// subtype for handheld instruments, like violin // subtype for handheld instruments, like violin
/datum/song/handheld /datum/song/handheld
/datum/song/handheld/updateDialog(mob/user = usr) /datum/song/handheld/updateDialog(mob/user)
if(user.machine != src) parent.ui_interact(user || usr)
return
parent.ui_interact(user)
/datum/song/handheld/should_stop_playing(mob/user) /datum/song/handheld/should_stop_playing(mob/user)
. = ..() . = ..()
@@ -292,10 +391,8 @@
// subtype for stationary structures, like pianos // subtype for stationary structures, like pianos
/datum/song/stationary /datum/song/stationary
/datum/song/stationary/updateDialog(mob/user = usr) /datum/song/stationary/updateDialog(mob/user)
if(user.machine != src) parent.ui_interact(user || usr)
return
parent.ui_interact(user)
/datum/song/stationary/should_stop_playing(mob/user) /datum/song/stationary/should_stop_playing(mob/user)
. = ..() . = ..()

View File

@@ -1,48 +1,52 @@
/// Playing legacy instruments - None of the "advanced" like sound reservations and decay are invoked. /**
/datum/song/proc/do_play_lines_legacy(mob/user) * Compiles our lines into "chords" with filenames for legacy playback. This makes there have to be a bit of lag at the beginning of the song, but repeats will not have to parse it again, and overall playback won't be impacted by as much lag.
while(repeat >= 0) */
var/cur_oct[7] /datum/song/proc/compile_legacy()
var/cur_acc[7] if(!length(src.lines))
for(var/i = 1 to 7) return
cur_oct[i] = 3 var/list/lines = src.lines //cache for hyepr speed!
cur_acc[i] = "n" compiled_chords = list()
var/list/octaves = list(3, 3, 3, 3, 3, 3, 3)
var/list/accents = list("n", "n", "n", "n", "n", "n", "n")
for(var/line in lines)
var/list/chords = splittext(lowertext(line), ",")
for(var/chord in chords)
var/list/compiled_chord = list()
var/tempodiv = 1
var/list/notes_tempodiv = splittext(chord, "/")
var/len = length(notes_tempodiv)
if(len >= 2)
tempodiv = text2num(notes_tempodiv[2])
if(len) //some dunkass is going to do ,,,, to make 3 rests instead of ,/1 because there's no standardization so let's be prepared for that.
var/list/notes = splittext(notes_tempodiv[1], "-")
for(var/note in notes)
if(length(note) == 0)
continue
// 1-7, A-G
var/key = text2ascii(note) - 96
if((key < 1) || (key > 7))
continue
for(var/i in 2 to length(note))
var/oct_acc = copytext(note, i, i + 1)
var/num = text2num(oct_acc)
if(!num) //it's an accidental
accents[key] = oct_acc //if they misspelled it/fucked up that's on them lmao, no safety checks.
else //octave
octaves[key] = clamp(num, octave_min, octave_max)
compiled_chord[++compiled_chord.len] = list(key, accents[key], octaves[key])
compiled_chord += tempodiv //this goes last
if(length(compiled_chord))
compiled_chords[++compiled_chords.len] = compiled_chord
for(var/line in lines) /**
for(var/beat in splittext(lowertext(line), ",")) * Proc to play a legacy note. Just plays the sound to hearing mobs (and does hearcheck if necessary), no fancy channel/sustain/management.
if(should_stop_playing(user)) *
return * Arguments:
var/list/notes = splittext(beat, "/") * * note is a number from 1-7 for A-G
if(length(notes)) //because some jack-butts are going to do ,,,, to symbolize 3 rests instead of something reasonable like ,/1. * * acc is either "b", "n", or "#"
for(var/note in splittext(notes[1], "-")) * * oct is 1-8 (or 9 for C)
if(length(note) == 0) */
continue /datum/song/proc/playkey_legacy(note, acc as text, oct, mob/user)
var/cur_note = text2ascii(note) - 96
if(cur_note < 1 || cur_note > 7)
continue
for(var/i=2 to length(note))
var/ni = copytext(note,i,i+1)
if(!text2num(ni))
if(ni == "#" || ni == "b" || ni == "n")
cur_acc[cur_note] = ni
else if(ni == "s")
cur_acc[cur_note] = "#" // so shift is never required
else
cur_oct[cur_note] = text2num(ni)
playnote_legacy(cur_note, cur_acc[cur_note], cur_oct[cur_note])
if(notes.len >= 2 && text2num(notes[2]))
sleep(sanitize_tempo(tempo / text2num(notes[2])))
else
sleep(tempo)
if(should_stop_playing(user))
return
repeat--
updateDialog()
repeat = 0
// note is a number from 1-7 for A-G
// acc is either "b", "n", or "#"
// oct is 1-8 (or 9 for C)
/datum/song/proc/playnote_legacy(note, acc as text, oct)
// handle accidental -> B<>C of E<>F // handle accidental -> B<>C of E<>F
if(acc == "b" && (note == 3 || note == 6)) // C or F if(acc == "b" && (note == 3 || note == 6)) // C or F
if(note == 3) if(note == 3)

View File

@@ -1,27 +1,7 @@
/datum/song/proc/do_play_lines_synthesized(mob/user) /**
compile_lines() * Compiles our lines into "chords" with numbers. This makes there have to be a bit of lag at the beginning of the song, but repeats will not have to parse it again, and overall playback won't be impacted by as much lag.
while(repeat >= 0) */
if(should_stop_playing(user)) /datum/song/proc/compile_synthesized()
return
var/warned = FALSE
for(var/_chord in compiled_chords)
if(should_stop_playing(user))
return
var/list/chord = _chord
var/tempodiv = chord[chord.len]
for(var/i in 1 to chord.len - 1)
var/key = chord[i]
if(!playkey_synth(key))
if(!warned)
warned = TRUE
to_chat(user, "<span class='boldwarning'>Your instrument has ran out of channels. You might be playing your song too fast or be setting sustain to too high of a value. This warning will be suppressed for the rest of this cycle.</span>")
sleep(sanitize_tempo(tempo / (tempodiv || 1)))
repeat--
updateDialog()
repeat = 0
/// C-Db2-A-A4/2,A-B#4-C/3,/4,A,A-B-C as an example
/datum/song/proc/compile_lines()
if(!length(src.lines)) if(!length(src.lines))
return return
var/list/lines = src.lines //cache for hyepr speed! var/list/lines = src.lines //cache for hyepr speed!
@@ -57,10 +37,12 @@
compiled_chord += tempodiv //this goes last compiled_chord += tempodiv //this goes last
if(length(compiled_chord)) if(length(compiled_chord))
compiled_chords[++compiled_chords.len] = compiled_chord compiled_chords[++compiled_chords.len] = compiled_chord
CHECK_TICK
return compiled_chords
/datum/song/proc/playkey_synth(key) /**
* Plays a specific numerical key from our instrument to anyone who can hear us.
* Does a hearing check if enough time has passed.
*/
/datum/song/proc/playkey_synth(key, mob/user)
if(can_noteshift) if(can_noteshift)
key = clamp(key + note_shift, key_min, key_max) key = clamp(key + note_shift, key_min, key_max)
if((world.time - MUSICIAN_HEARCHECK_MINDELAY) > last_hearcheck) if((world.time - MUSICIAN_HEARCHECK_MINDELAY) > last_hearcheck)
@@ -83,6 +65,9 @@
M.playsound_local(get_turf(parent), null, volume, FALSE, K.frequency, INSTRUMENT_DISTANCE_NO_FALLOFF, channel, null, copy, distance_multiplier = INSTRUMENT_DISTANCE_FALLOFF_BUFF) M.playsound_local(get_turf(parent), null, volume, FALSE, K.frequency, INSTRUMENT_DISTANCE_NO_FALLOFF, channel, null, copy, distance_multiplier = INSTRUMENT_DISTANCE_FALLOFF_BUFF)
// Could do environment and echo later but not for now // Could do environment and echo later but not for now
/**
* Stops all sounds we are "responsible" for. Only works in synthesized mode.
*/
/datum/song/proc/terminate_all_sounds(clear_channels = TRUE) /datum/song/proc/terminate_all_sounds(clear_channels = TRUE)
for(var/i in hearing_mobs) for(var/i in hearing_mobs)
terminate_sound_mob(i) terminate_sound_mob(i)
@@ -93,10 +78,16 @@
using_sound_channels = 0 using_sound_channels = 0
SSsounds.free_datum_channels(src) SSsounds.free_datum_channels(src)
/**
* Stops all sounds we are responsible for in a given person. Only works in synthesized mode.
*/
/datum/song/proc/terminate_sound_mob(mob/M) /datum/song/proc/terminate_sound_mob(mob/M)
for(var/channel in channels_playing) for(var/channel in channels_playing)
M.stop_sound_channel(text2num(channel)) M.stop_sound_channel(text2num(channel))
/**
* Pops a channel we have reserved so we don't have to release and re-request them from SSsounds every time we play a note. This is faster.
*/
/datum/song/proc/pop_channel() /datum/song/proc/pop_channel()
if(length(channels_idle)) //just pop one off of here if we have one available if(length(channels_idle)) //just pop one off of here if we have one available
. = text2num(channels_idle[1]) . = text2num(channels_idle[1])
@@ -108,6 +99,12 @@
if(!isnull(.)) if(!isnull(.))
using_sound_channels++ using_sound_channels++
/**
* Decays our channels and updates their volumes to mobs who can hear us.
*
* Arguments:
* * wait_ds - the deciseconds we should decay by. This is to compensate for any lag, as otherwise songs would get pretty nasty during high time dilation.
*/
/datum/song/proc/process_decay(wait_ds) /datum/song/proc/process_decay(wait_ds)
var/linear_dropoff = cached_linear_dropoff * wait_ds var/linear_dropoff = cached_linear_dropoff * wait_ds
var/exponential_dropoff = cached_exponential_dropoff ** wait_ds var/exponential_dropoff = cached_exponential_dropoff ** wait_ds