diff --git a/code/__HELPERS/tts.dm b/code/__HELPERS/tts.dm index 9d96d3d4e3e..a20df389506 100644 --- a/code/__HELPERS/tts.dm +++ b/code/__HELPERS/tts.dm @@ -2,3 +2,9 @@ // Only allow alphanumeric characters and whitespace var/static/regex/bad_chars_regex = regex("\[^a-zA-Z0-9 ,?.!'&-]", "g") return bad_chars_regex.Replace(text, " ") + +/proc/tts_filter_encode(text, atom/movable/speaker, blips) + text = replacetext(text, "%PITCH%", SStts.pitch_enabled ? speaker.pitch : 0) + text = replacetext(text, "%FEMALE%", !!findtext(speaker.voice, "Woman")) + text = replacetext(text, "%BLIPS%", blips) + return url_encode(text) diff --git a/code/controllers/subsystem/tts.dm b/code/controllers/subsystem/tts.dm index 5bd65b63e19..4c9314c8ec9 100644 --- a/code/controllers/subsystem/tts.dm +++ b/code/controllers/subsystem/tts.dm @@ -297,8 +297,8 @@ SUBSYSTEM_DEF(tts) var/datum/http_request/request_blips = new() var/file_name = "tmp/tts/[identifier].ogg" var/file_name_blips = "tmp/tts/[identifier]_blips.ogg" - request.prepare(RUSTG_HTTP_METHOD_GET, "[CONFIG_GET(string/tts_http_url)]/tts?voice=[speaker]&identifier=[identifier]&filter=[url_encode(filter)]&pitch=[pitch]&special_filters=[url_encode(special_filters)]", json_encode(list("text" = shell_scrubbed_input)), headers, file_name, timeout_seconds = CONFIG_GET(number/tts_http_timeout_seconds)) - request_blips.prepare(RUSTG_HTTP_METHOD_GET, "[CONFIG_GET(string/tts_http_url)]/tts-blips?voice=[speaker]&identifier=[identifier]&filter=[url_encode(filter)]&pitch=[pitch]&special_filters=[url_encode(special_filters)]", json_encode(list("text" = shell_scrubbed_input)), headers, file_name_blips, timeout_seconds = CONFIG_GET(number/tts_http_timeout_seconds)) + request.prepare(RUSTG_HTTP_METHOD_GET, "[CONFIG_GET(string/tts_http_url)]/tts?voice=[speaker]&identifier=[identifier]&filter=[tts_filter_encode(filter, speaker)]&pitch=[pitch]&special_filters=[url_encode(special_filters)]", json_encode(list("text" = shell_scrubbed_input)), headers, file_name, timeout_seconds = CONFIG_GET(number/tts_http_timeout_seconds)) + request_blips.prepare(RUSTG_HTTP_METHOD_GET, "[CONFIG_GET(string/tts_http_url)]/tts-blips?voice=[speaker]&identifier=[identifier]&filter=[tts_filter_encode(filter, speaker, blips = TRUE)]&pitch=[pitch]&special_filters=[url_encode(special_filters)]", json_encode(list("text" = shell_scrubbed_input)), headers, file_name_blips, timeout_seconds = CONFIG_GET(number/tts_http_timeout_seconds)) var/datum/tts_request/current_request = new /datum/tts_request(identifier, request, request_blips, shell_scrubbed_input, target, local, language, message_range, volume_offset, listeners, pitch) var/list/player_queued_tts_messages = queued_tts_messages[target] if(!player_queued_tts_messages) diff --git a/code/game/machinery/dna_infuser/organ_sets/fish_organs.dm b/code/game/machinery/dna_infuser/organ_sets/fish_organs.dm index 0f4e3cdd0d7..ea80cbe1017 100644 --- a/code/game/machinery/dna_infuser/organ_sets/fish_organs.dm +++ b/code/game/machinery/dna_infuser/organ_sets/fish_organs.dm @@ -467,6 +467,37 @@ icon = 'icons/obj/medical/organs/infuser_organs.dmi' icon_state = "inky_tongue" actions_types = list(/datum/action/cooldown/ink_spit) + /** + * This is probably the most complex tts filter that won't require external files to be added to the tts image. + * It works as follows: + * 1. Increase the pitch of the input audio. Pitch increase is lower for higher speaker pitch and vice-versa. + * 2. Apply a mid-heavy EQ curve. + * 3. Using an oscillating target frequency: + * - Apply a low pass filter with its cutoff at the target frequency + * - Boost frequencies very close to the target frequency + */ + voice_filter = "\ + rubberband=pitch='\ + ifnot(%BLIPS%,\ + 2-(%PITCH%+if(%FEMALE%,4))/16\ + ,1)'\ + :formant=preserved,\ + highpass=f=1000:t=s:w=24,\ + equalizer=f=1200:g=15,\ + equalizer=f=4350:g=-15,\ + highshelf=f=870:g=1,\ + afftfilt=\ + real='\ + st(0,(b+0.5)/nb*sr);\ + st(1,3000+1500*sin(9.3*2*PI*pts));\ + st(2,ld(0)/ld(1));\ + re*(1-ld(2)^2+2*gauss(log(ld(2)+1)))'\ + :imag='\ + st(0,(b+0.5)/nb*sr);\ + st(1,3000+1500*sin(9.3*2*PI*pts));\ + st(2,ld(0)/ld(1));\ + im*(1-ld(2)^2+2*gauss(log(ld(2)+1)))'\ + :win_size=1024" // Seafood instead of meat, because it's a fish organ foodtype_flags = RAW | SEAFOOD | GORE