71 lines
2.7 KiB
GDScript
71 lines
2.7 KiB
GDScript
extends SceneTree
|
|
|
|
const OUTPUT_DIRECTORY := "res://sound/dialogue/animalese/placeholder"
|
|
const SAMPLE_RATE: int = 22050
|
|
const SAMPLE_SECONDS: float = 0.055
|
|
const CHARACTERS := "abcdefghijklmnopqrstuvwxyz"
|
|
const VOWELS := "aeiou"
|
|
|
|
|
|
func _initialize() -> void:
|
|
var absolute_directory := ProjectSettings.globalize_path(OUTPUT_DIRECTORY)
|
|
var directory_error: Error = DirAccess.make_dir_recursive_absolute(
|
|
absolute_directory
|
|
)
|
|
if directory_error != OK:
|
|
push_error("Could not create animalese sample directory.")
|
|
quit(1)
|
|
return
|
|
for character_index: int in range(CHARACTERS.length()):
|
|
var character := CHARACTERS.substr(character_index, 1)
|
|
_save_sample(character, character_index)
|
|
_save_sample("fallback", CHARACTERS.length())
|
|
quit()
|
|
|
|
|
|
func _save_sample(sample_name: String, sample_index: int) -> void:
|
|
var stream := _build_sample(sample_name, sample_index)
|
|
var output_path := "%s/%s" % [OUTPUT_DIRECTORY, sample_name]
|
|
var save_error: Error = stream.save_to_wav(output_path)
|
|
if save_error != OK:
|
|
push_error("Could not save animalese sample: %s" % sample_name)
|
|
|
|
|
|
func _build_sample(sample_name: String, sample_index: int) -> AudioStreamWAV:
|
|
var frame_count := roundi(SAMPLE_RATE * SAMPLE_SECONDS)
|
|
var pcm := PackedByteArray()
|
|
pcm.resize(frame_count * 2)
|
|
var base_frequency := 245.0 + float(sample_index % 7) * 18.0
|
|
var formant_one := 620.0 + float(sample_index % 5) * 85.0
|
|
var formant_two := 1320.0 + float(sample_index % 6) * 115.0
|
|
var is_vowel := VOWELS.contains(sample_name)
|
|
var noise_seed := sample_index * 7919 + 104729
|
|
for frame_index: int in range(frame_count):
|
|
var time := float(frame_index) / SAMPLE_RATE
|
|
var attack := minf(time / 0.004, 1.0)
|
|
var release := minf((SAMPLE_SECONDS - time) / 0.014, 1.0)
|
|
var envelope := maxf(minf(attack, release), 0.0)
|
|
noise_seed = int(
|
|
(noise_seed * 1103515245 + 12345) & 0x7fffffff
|
|
)
|
|
var noise := float(noise_seed % 65536) / 32767.5 - 1.0
|
|
var voiced := (
|
|
sin(TAU * base_frequency * time) * 0.42
|
|
+ sin(TAU * base_frequency * 2.0 * time) * 0.16
|
|
)
|
|
var formants := (
|
|
sin(TAU * formant_one * time) * 0.18
|
|
+ sin(TAU * formant_two * time) * 0.10
|
|
)
|
|
var consonant_noise := noise * (0.08 if is_vowel else 0.24)
|
|
var value := envelope * (voiced + formants + consonant_noise) * 0.72
|
|
var signed_sample := clampi(roundi(value * 32767.0), -32768, 32767)
|
|
var encoded_sample := signed_sample if signed_sample >= 0 else signed_sample + 65536
|
|
pcm[frame_index * 2] = encoded_sample & 0xff
|
|
pcm[frame_index * 2 + 1] = (encoded_sample >> 8) & 0xff
|
|
var stream := AudioStreamWAV.new()
|
|
stream.set("format", AudioStreamWAV.FORMAT_16_BITS)
|
|
stream.mix_rate = SAMPLE_RATE
|
|
stream.stereo = false
|
|
stream.data = pcm
|
|
return stream
|