Files
vgstation13/code/modules/bayinstruments/song.dm
mentgah 289ea2a947 Synth fixes (#37471)
* instrument fixes 1

* instrument dmi fixes

* anchored
2025-03-18 05:31:11 -05:00

210 lines
6.2 KiB
Plaintext

#define STOP_PLAY_LINES \
autorepeat = 0 ;\
playing = 0 ;\
current_line = 0 ;\
return
/datum/synthesized_song
var/list/lines = list()
var/tempo = 5
var/playing = 0
var/autorepeat = 0
var/current_line = 0
var/datum/sound_player/player // Not a physical thing
var/datum/instrument/instrument_data
var/list/free_channels = list()
var/linear_decay = 1
var/sustain_timer = 11
var/soft_coeff = 2.0
var/transposition = 0
var/octave_range_min
var/octave_range_max
/datum/synthesized_song/New(datum/sound_player/playing_object, datum/instrument/instrument)
player = playing_object
instrument_data = instrument
octave_range_min = global.musical_config.lowest_octave
octave_range_max = global.musical_config.highest_octave
instrument.create_full_sample_deviation_map()
occupy_channels()
/datum/synthesized_song/proc/sanitize_tempo(new_tempo) // Identical to datum/song
new_tempo = abs(new_tempo)
return max(round(new_tempo, world.tick_lag), world.tick_lag)
/datum/synthesized_song/proc/occupy_channels()
if (!global.musical_config.free_channels_populated)
for (var/i=1 to 1024) // Currently only 1024 channels are allowed
global.musical_config.free_channels += i
global.musical_config.free_channels_populated = 1 // Only once
for (var/i=1 to global.musical_config.channels_per_instrument)
if (global.musical_config.free_channels.len)
free_channel(pick_n_take(global.musical_config.free_channels))
/datum/synthesized_song/proc/take_any_channel()
return pick_n_take(free_channels)
/datum/synthesized_song/proc/free_channel(channel)
if (channel in free_channels) return
free_channels += channel
/datum/synthesized_song/proc/return_all_channels()
global.musical_config.free_channels |= free_channels
free_channels.Cut()
/datum/synthesized_song/proc/play_synthesized_note(note, acc, oct, duration, where, which_one)
if (oct < global.musical_config.lowest_octave || oct > global.musical_config.highest_octave) return
if (oct < octave_range_min || oct > octave_range_max) return
var/delta1 = acc == "b" ? -1 : acc == "#" ? 1 : acc == "s" ? 1 : acc == "n" ? 0 : 0
var/delta2 = 12 * oct
var/note_num = delta1+delta2+global.musical_config.nn2no[note]
if (note_num < 0 || note_num > 127)
CRASH("play_synthesized note failed because of 0..127 condition, [note], [acc], [oct]")
var/datum/sample_pair/pair = instrument_data.sample_map[global.musical_config.n2t(note_num)]
#define Q 0.083 // 1/12
var/freq = 2**(Q*pair.deviation)
var/chan = take_any_channel()
if (!chan)
if (!player.channel_overload())
playing = 0
autorepeat = 0
return
#undef Q
var/list/mob/to_play_for = player.who_to_play_for()
if (!to_play_for.len)
free_channel(chan) // I'm an idiot, fuck
return
for (var/mob/hearer in to_play_for)
play_for(hearer, pair.sample, duration, freq, chan, note_num, where, which_one)
/datum/synthesized_song/proc/play_for(mob/who, what, duration, frequency, channel, which, where, which_one)
var/sound/sound_copy = sound(what)
sound_copy.wait = 0
sound_copy.repeat = 0
sound_copy.frequency = frequency
sound_copy.channel = channel
player.apply_modifications_for(who, sound_copy, which, where, which_one)
who << sound_copy
#if DM_VERSION < 511
sound_copy.frequency = 1
#endif
var/delta_volume = player.volume / sustain_timer
var/current_volume = max(round(sound_copy.volume), 0)
var/tick = duration
while (current_volume > 0)
var/new_volume = current_volume
tick += world.tick_lag
if (delta_volume <= 0)
CRASH("Delta Volume somehow was non-positive: [delta_volume]")
if (soft_coeff <= 1)
CRASH("Soft Coeff somehow was <=1: [soft_coeff]")
if (linear_decay)
new_volume = new_volume - delta_volume
else
new_volume = new_volume / soft_coeff
var/sanitized_volume = max(round(new_volume), 0)
if (sanitized_volume == current_volume)
current_volume = new_volume
continue
current_volume = sanitized_volume
SSmusic.push_event(player, who, sound_copy, tick, current_volume)
if (current_volume <= 0)
break
#define CP(L, S) copytext(L, S, S+1)
#define IS_DIGIT(L) (L >= "0" && L <= "9" ? 1 : 0)
/datum/synthesized_song/proc/play_song(mob/user)
// This code is really fucking horrible.
player.cache_unseen_tiles()
var/list/allowed_suff = list("b", "n", "#", "s")
var/list/note_off_delta = list("a"=91, "b"=91, "c"=98, "d"=98, "e"=98, "f"=98, "g"=98)
var/list/lines_copy = lines.Copy()
spawn()
if (!lines.len)
STOP_PLAY_LINES
var/list/cur_accidentals = list("n", "n", "n", "n", "n", "n", "n")
var/list/cur_octaves = list(3, 3, 3, 3, 3, 3, 3)
current_line = 1
for (var/line in lines_copy)
var/cur_note = 1
for (var/notes in splittext(lowertext(line), ","))
var/list/components = splittext(notes, "/")
var/duration = sanitize_tempo(tempo)
if (components.len)
var/delta = components.len==2 && text2num(components[2]) ? text2num(components[2]) : 1
var/note_str = splittext(components[1], "-")
duration = sanitize_tempo(tempo / delta)
for (var/note in note_str)
if (!note) continue // wtf, empty note
var/note_sym = CP(note, 1)
var/note_off = 0
if (note_sym in note_off_delta)
note_off = text2ascii(note_sym) - note_off_delta[note_sym]
else
continue // Shitty note, move along and avoid runtimes
var/octave = cur_octaves[note_off]
var/accidental = cur_accidentals[note_off]
switch (length(note))
if (3)
accidental = CP(note, 2)
octave = CP(note, 3)
if (!(accidental in allowed_suff) || !IS_DIGIT(octave))
continue
else
octave = text2num(octave)
if (2)
if (IS_DIGIT(CP(note, 2)))
octave = text2num(CP(note, 2))
else
accidental = CP(note, 2)
if (!(accidental in allowed_suff))
continue
cur_octaves[note_off] = octave
cur_accidentals[note_off] = accidental
play_synthesized_note(note_off, accidental, octave+transposition, duration, current_line, cur_note)
if (SSmusic.is_overloaded())
STOP_PLAY_LINES
cur_note++
if (!playing || player.shouldStopPlaying(user))
STOP_PLAY_LINES
sleep(duration)
current_line++
if (autorepeat)
.()
else
STOP_PLAY_LINES
#undef CP
#undef IS_DIGIT
#undef STOP_PLAY_LINES