feat: add selectable animalese voice sets

This commit is contained in:
Alexander Sellite 2026-08-16 12:26:05 -04:00
parent 364e7dc282
commit ce733818e2
247 changed files with 2675 additions and 302 deletions

View file

@ -14,7 +14,7 @@ independent team Woofmeow.
- Voyager: co-owner, developer, project director, creator of the game's 3D
art, and composer of the original title/ambient track
`audio/music/title/as_in_four_wolves.ogg`, world track
`audio/music/world/craft.mp3`, and animalese placeholder tones.
`audio/music/world/craft.mp3`, and the synthesized robot animalese tones.
- Endeavour: co-owner, developer, and creator of the game's 2D art, including
the original fish, environment, UI, and other artwork.
- chillnfill: contributor of original 3D models for the character bodies and
@ -23,6 +23,10 @@ independent team Woofmeow.
`chillnfill`.
- Tekgator: creator of the cooler-capacity, reel-speed, and barrier-power shop
icons.
- kat: supplied the original recorded voice sample used for the animalese
alphabet and number sounds.
- kim: supplied the recorded voice performance used for the kim animalese
alphabet and number sounds.
Voyager is also the original catalog porter credit in PortMaster metadata.
Third-party creators are credited with their individual records below. Those
@ -144,7 +148,9 @@ locations only and must never appear in scenes or resources.
| Rain ambience | `audio/ambience/rain_loop_ontario.ogg` | Converted from “Rain Loop Ontario” by Freesound user Ayton, sound 212799, CC BY 3.0. |
| Character bark call | `sound/dialogue/calls/bark.wav` | Edited from `Dog_Bark.wav` by Freesound user ivolipa, sound 328729, CC0. |
| Character meow call | `sound/dialogue/calls/meow.wav` | Edited from `Cat meow.m4a` by Freesound user Christyboy100, sound 495694, Attribution 3.0. |
| Animalese placeholder tones | `sound/dialogue/animalese/placeholder/` | Original tones composed and owned by co-owner Voyager without third-party samples. |
| Animalese alphabet and number sounds | `sound/dialogue/animalese/alphanumeric/` | Processed derivatives of the original recorded voice sample supplied by NETfishing community member kat. |
| Kim animalese alphabet and number sounds | `sound/dialogue/animalese/kim/` | Processed per-character voice samples supplied by NETfishing community member kim. |
| Robot animalese tones | `sound/dialogue/animalese/robot/` | Original synthesized tones composed and owned by co-owner Voyager without third-party samples. |
Creative Commons Zero information:
https://creativecommons.org/publicdomain/zero/1.0/. Creative Commons
@ -206,6 +212,94 @@ authored `mesh_style.png` filenames.
confirmation covering Project use, modification, distribution, commercial
release, and applicable sublicensing.
### kat animalese voice contribution record
- Requested credit: `kat`.
- Original contribution: the recorded voice sample used to produce the
animalese alphabet and number sounds.
- Repository mapping: the heavily processed per-character derivatives are in
`sound/dialogue/animalese/alphanumeric/`.
- Intake filenames: `0.wav` through `9.wav` and `a.wav` through `z.wav`.
- Source and initial repository files were compared byte-for-byte at intake.
The hashes below identify those untouched intake files; runtime files are
normalized derivatives produced by `scripts/import_animalese_voice.sh`.
- Permission record: retain the original submission and permission record
covering Project use, processing, distribution, commercial release, and
applicable sublicensing.
| Clip | SHA-256 | Clip | SHA-256 |
| --- | --- | --- | --- |
| `0.wav` | `0616a2fb035d410f24403e66c4fe46e9ff86e976126db16c56125af28bbe5ad6` | `a.wav` | `aa701de9370000e620dae16c4e3082bc79fc9692ed5260dfd5f4f2480b556e75` |
| `1.wav` | `e29191c0b4011d4237710b0ef4fe1f0ebe875bb3cf8abb62b057b7799c683dda` | `b.wav` | `149286a210d4fec9197f4c6109a066706cb890181b6663369a9941435ee859bb` |
| `2.wav` | `e4ce823498ec8440b2f870ea767b1b6185add948a3ba573ec476794fc6ab5abb` | `c.wav` | `4776bb64044e82f21755e771f31e30e18e8737533b08f04a9bd5103aa9adf923` |
| `3.wav` | `498a06196dd672f273c90802d0349f3ecc5f582a1aeb9b952ff54030bef523e0` | `d.wav` | `6bb00d354b5a621895412ae40a40f02f219976fc164b08389a6bb708a6c99e84` |
| `4.wav` | `dbb90d8e7e5cb9524cfd429d63e297548871ed6dda7c2594c5ea4e932c623754` | `e.wav` | `232edabddc6c046ba5bbb5f640cabe9c9a905c2a12cec07fc60ef657e83547c4` |
| `5.wav` | `dc6dc2946e0ebb5b346bbf4c646c72e27303f4bf8838c14038df5ce7087071f9` | `f.wav` | `f104a52c97605872de4cafab1f1c24a132051e14a9400c2098a24385525f1e15` |
| `6.wav` | `8dae2ce33aaa0d788964d0a7026d3c51738ef2b06672697675a7f2da0f9f7a59` | `g.wav` | `12915a19a9c3b0f1559a4c53734c16fedc1d82f7c24adce08608aad7da141968` |
| `7.wav` | `f5c5c855b06794a22954f3eb2b32f2caec877ce7dee27fd3153c7aa65ab1ff92` | `h.wav` | `951a2c1b074eb43c8016d592ed7d1bc63ec64ab95312d122fa8f2e717b61c1e1` |
| `8.wav` | `b31561b5e3748024daf109510d5ee955274d2ef45f7fed96882f8a4862b70e30` | `i.wav` | `1d51b902b1b667eadc7bcc569909ce927075e7b91b8a02ea811f361e3e95164c` |
| `9.wav` | `e46bb34cb045fb15bb0cbbfbd9364895521ee3c441290154a1f4ff67ffd38f2d` | `j.wav` | `9ef84e992d58349b42f642901688f49203af27dda85c5baee30b4c2cf93944c9` |
| | | `k.wav` | `65ae2072c6f55b5013dea21dd4644c2dd502963b89f5711eb22f741cee3dec09` |
| | | `l.wav` | `d87f8fcb4c217c0dbd5526705c982655e514a5645ef2391557b6c033344aa713` |
| | | `m.wav` | `1f1bc0ba3abe077febfa035e873d0e47838e880fbbd6e10164b1e58e13a5e47b` |
| | | `n.wav` | `2f422e00c4c42689e0d50adf2226c28a851960f3c460b2c8cdbbefb687175ad1` |
| | | `o.wav` | `70143fd697e1b1d1f1d27c8fc3bd59ea72e076e1679494f8022a44911752da46` |
| | | `p.wav` | `9419172ec8a595f89ac2993f4a942c554dc5a8fa64bd8a75f468718cdb2b176e` |
| | | `q.wav` | `e0117043020c0eda1d15f1315b7796774a219c1439bafc587c01e3661215abbb` |
| | | `r.wav` | `31f9a4a06975c55331b830247b39f47572622698457c191d03c92d02599b1131` |
| | | `s.wav` | `232edabddc6c046ba5bbb5f640cabe9c9a905c2a12cec07fc60ef657e83547c4` |
| | | `t.wav` | `a8b7379fa5c26462bde6e51cd74edb514aa731afa02efb5f82b42341c5a50964` |
| | | `u.wav` | `e25dec74b9f141203c92c4a8ebf296c80e031cc677d924656bef64e90a02c7a3` |
| | | `v.wav` | `643725a16a9e821a5e8a502aa43979a9f9ed73860839d28cf15b4b3fb4ff3539` |
| | | `w.wav` | `5a915415157775f00875b4985844a1cd28f36733744dbe718fdf8b714f1ccc89` |
| | | `x.wav` | `04fc26436d1524c5fb6e7f84c2a1a930aa519a57f594b73e7310c7187ac581c3` |
| | | `y.wav` | `7fbe3ee4f2a6c6432b8cbeeffe2cc422b3b960e0f148533ffcc0d42d73804f9c` |
| | | `z.wav` | `0616a2fb035d410f24403e66c4fe46e9ff86e976126db16c56125af28bbe5ad6` |
### kim animalese voice contribution record
- Requested credit: `kim`.
- Original contribution: the recorded voice performance used to produce the
kim animalese alphabet and number sounds.
- Repository mapping: the per-character samples are in
`sound/dialogue/animalese/kim/`.
- Intake filenames: `0.wav` through `9.wav` and `a.wav` through `z.wav`.
- Source and initial repository files were compared byte-for-byte at intake.
The hashes below identify those untouched intake files; runtime files are
normalized derivatives produced by `scripts/import_animalese_voice.sh`.
- Permission record: retain the original submission and permission record
covering Project use, processing, distribution, commercial release, and
applicable sublicensing.
| Clip | SHA-256 | Clip | SHA-256 |
| --- | --- | --- | --- |
| `0.wav` | `cd107c5904569c87fb972dce3853e02d126d1d1fe61b16596a2e40b6c5ffa4c1` | `a.wav` | `037dde81eaae9d11b316a0336a2cf39090810afcb8e6a3349af674808a7b6a92` |
| `1.wav` | `4e2310d7af7912d7bf88dbe0b1b864bd491b412dfb46e28e8dbf7fc77d2b839e` | `b.wav` | `09e82faa88e7be9d3d9b984288f001cf722ba9c29f7a422053c90641e2406ff4` |
| `2.wav` | `ed54c8edc36b80e696a45e4eaefe724b714fa949a01af09d66ec4b8af4538dda` | `c.wav` | `8f8bf82630511d510042918389473a6bcb0c71173dc4bbfaaef0ec64c77a8b34` |
| `3.wav` | `41f6b3e0d30384c790ca625ab049826f2cf33aaca4b3bfbbbb833bfbd84d86c9` | `d.wav` | `ccf55a87158896e233afb3c3be517b92f97af92a8d972a8fc8f1ce88a418ec7c` |
| `4.wav` | `d9345022c32fc03f382d892d2ec105a3200c787dfac5357c8c192377b01b7816` | `e.wav` | `d62935c4d90cabc1fc789eab22da1aae205383ba5063b1d923ae3165bb231b92` |
| `5.wav` | `3ea7659d6d1bdc2cf6d5e9131d983ce27ec715d5a27923f141a9e8123f413551` | `f.wav` | `4a7957d71c3e287a94f94b40f77c631e41b9b885bbd968aee60b66e789f5fb65` |
| `6.wav` | `a91e64bccc07162cefaf1c59f838e4475e8ba40efcb749e965f87903f03c2a0d` | `g.wav` | `8fe9f4aa5a3c1f61860c9ef371674e9d3ce117fe222612465d2c7531367294e8` |
| `7.wav` | `e4e2e85f88625e152ff682c45ce816e7ec3fc15eb23ed01c686528f9abfa1015` | `h.wav` | `88a758b4f1e2a90758f93c72b15804b38152e3e8cf6b0b67558823d52e0a0648` |
| `8.wav` | `da94b98ef5bcf7460d3c9ca02b651f586b51ef2809f33da639898f81253e16a4` | `i.wav` | `f272e24648bbf62b09c83743b53c94168e81a90a8d3fff2aa0ba5a727927f494` |
| `9.wav` | `34aa49af5f8414bff289cb575468bef2745b3f6d0ac3420d9eb07ec148cdd645` | `j.wav` | `f25e1585e28313f63bcad0fe3c9a0032c96a32754687e592ea4f1989edfb3411` |
| | | `k.wav` | `3c39ede36a423577d75772ba5c1b7f1a4315aac0b5dea89fa6cd7ac647f4c342` |
| | | `l.wav` | `beda782f9b1de5e82e0f7c481f3e7a5694ead7c72ccabc684a7c330234b7ad7c` |
| | | `m.wav` | `b4002b6dc895896144711cf8a763cdc93d11d9bc74b0b57fbc55831ec3dbf28d` |
| | | `n.wav` | `d5e989ab8cc755cd8676fe05ec3e5b8e433d674694188df3717e3073bd27975b` |
| | | `o.wav` | `043e5f81a29400a4b8078168c187c0e0907ea43041c96be98b2bdf646afc0f61` |
| | | `p.wav` | `da228b88d1621a09b21b3a5e5ea57710fab0a726ef9655406b43da30cd67c6d4` |
| | | `q.wav` | `83cb2e061fa8473e9205d03b353c1d8a3c6cb26913b287571205033ce95e0c99` |
| | | `r.wav` | `4d58f4337a62d0b37b5cea427a22dc05f2b1cab505d6de6fa1d7d8363ee018d4` |
| | | `s.wav` | `93a19e046764af830fc5582f8de3b35b287eea2bc3abf0b269aa0e945405d95c` |
| | | `t.wav` | `c7b1f1d967736c3b640fab88fa4522ca6bec86fc96f2980c3948788c3d9ede9c` |
| | | `u.wav` | `320a3d89c0ad22cf4249d9647bedbb882e8ca561feb651eb7534f2ea51380c54` |
| | | `v.wav` | `8ed8d00aaf3f60531680241f65f45251639080258799698e81afce996ba506af` |
| | | `w.wav` | `edad2171e9b4238a20b8c3d6675b5ab457be0af0450f37950bc6d70215f9def7` |
| | | `x.wav` | `93896a36adeaf8628c18597957e86804c98115b40f41f7ea2d139ba9bf172da8` |
| | | `y.wav` | `9bde2a148b881ef04003286351cb7801fd65276d56b00433921bf7f42aa4d1ab` |
| | | `z.wav` | `cd107c5904569c87fb972dce3853e02d126d1d1fe61b16596a2e40b6c5ffa4c1` |
### Fishing fight loop source record
- Source page: https://freesound.org/s/509902/

View file

@ -292,6 +292,23 @@ godot --headless --path . --script scripts/normalize_texture_imports.gd \
godot --headless --path . --script tests/texture_sampling_validation.gd
```
### Importing animalese voice sets
Animalese clips use one deterministic runtime level and format: -18 dBFS RMS
with a -3 dBFS peak ceiling, mono, 48 kHz, 16-bit PCM. Import a supplied set
through the normalization script rather than copying its WAV files directly:
```bash
scripts/import_animalese_voice.sh /path/to/source_clips sample_set_directory
```
The source directory may contain lowercase letter, number, and underscore WAV
filenames. The script converts every top-level WAV and writes the results under
`sound/dialogue/animalese/sample_set_directory/`. Add a new runtime set to
`player/animalese_voice_profiles.gd` after import, then run the quick validation
suite. Keep the untouched submission and its original hashes in the private
intake record; repository files are normalized derivatives.
### Bubble menus
Instance `ui/components/bubble_menu/bubble_button.tscn` for each action, or

View file

@ -77,7 +77,11 @@ func is_sender_filtered(fingerprint: String) -> bool:
)
func send_local_message(body: String) -> bool:
func send_local_message(
body: String,
voice_id: String = VoiceProfilesType.DEFAULT_ID,
sample_set_id: String = VoiceProfilesType.DEFAULT_SAMPLE_SET_ID,
) -> bool:
if (
_session == null
or not _session.is_gameplay_session_active()
@ -99,6 +103,10 @@ func send_local_message(body: String) -> bool:
"session_id": _session.get_session_id(),
"body": clean,
"sender_fingerprint": _session.get_local_identity_fingerprint(),
"voice_id": VoiceProfilesType.sanitized_id(voice_id),
"sample_set_id": VoiceProfilesType.sanitized_sample_set_id(
sample_set_id
),
}
request["sender_signature"] = _session.sign_local_action(
"chat_send", NetworkChatProtocol.signature_fields(request)
@ -482,7 +490,13 @@ func _handle_request(peer_id: int, data: Dictionary) -> void:
NetworkChatProtocol.Kind.PLAYER,
peer_id,
record.display_name,
NetworkChatProtocol.sanitize_body(data["body"])
NetworkChatProtocol.sanitize_body(data["body"]),
VoiceProfilesType.sanitized_id(str(data.get(
"voice_id", VoiceProfilesType.DEFAULT_ID
))),
VoiceProfilesType.sanitized_sample_set_id(str(data.get(
"sample_set_id", VoiceProfilesType.DEFAULT_SAMPLE_SET_ID
))),
)
message["request_id"] = request_id
message["sender_fingerprint"] = data["sender_fingerprint"]
@ -499,6 +513,8 @@ func _make_message(
peer_id: int,
display_name: String,
body: String,
voice_id: String = VoiceProfilesType.DEFAULT_ID,
sample_set_id: String = VoiceProfilesType.DEFAULT_SAMPLE_SET_ID,
) -> Dictionary:
_sequence += 1
var message := {
@ -511,6 +527,10 @@ func _make_message(
"sender_display_name": display_name.left(24),
"body": body,
"sender_fingerprint": _session.get_host_identity_fingerprint(),
"voice_id": VoiceProfilesType.sanitized_id(voice_id),
"sample_set_id": VoiceProfilesType.sanitized_sample_set_id(
sample_set_id
),
}
if kind == NetworkChatProtocol.Kind.SYSTEM:
message["sender_signature"] = _session.sign_host_action(

View file

@ -10,6 +10,7 @@ var display_name: String = "Player"
var voice_id: String = VoiceProfilesType.DEFAULT_ID
var speech_speed_id: String = VoiceProfilesType.DEFAULT_SPEED_ID
var call_id: String = VoiceProfilesType.DEFAULT_CALL_ID
var sample_set_id: String = VoiceProfilesType.DEFAULT_SAMPLE_SET_ID
var created_at_unix: int = 0
var _profile_path := ""
var _expected_hash := ""
@ -57,12 +58,19 @@ func load_or_create() -> bool:
voice_id = VoiceProfilesType.DEFAULT_ID
speech_speed_id = VoiceProfilesType.DEFAULT_SPEED_ID
call_id = VoiceProfilesType.DEFAULT_CALL_ID
sample_set_id = VoiceProfilesType.DEFAULT_SAMPLE_SET_ID
created_at_unix = int(Time.get_unix_time_from_system())
return _save_atomic()
func set_display_name(value: String) -> bool:
return set_profile_identity(value, voice_id, speech_speed_id, call_id)
return set_profile_identity(
value,
voice_id,
speech_speed_id,
call_id,
sample_set_id,
)
func set_profile_identity(
@ -70,6 +78,7 @@ func set_profile_identity(
selected_voice_id: String,
selected_speech_speed_id: String,
selected_call_id: String,
selected_sample_set_id: String = VoiceProfilesType.DEFAULT_SAMPLE_SET_ID,
) -> bool:
var clean_name: String = value.strip_edges()
if (
@ -77,22 +86,26 @@ func set_profile_identity(
or not VoiceProfilesType.is_valid(selected_voice_id)
or not VoiceProfilesType.is_valid_speed(selected_speech_speed_id)
or not VoiceProfilesType.is_valid_call(selected_call_id)
or not VoiceProfilesType.is_valid_sample_set(selected_sample_set_id)
):
return false
var previous_name: String = display_name
var previous_voice_id: String = voice_id
var previous_speech_speed_id: String = speech_speed_id
var previous_call_id: String = call_id
var previous_sample_set_id: String = sample_set_id
display_name = clean_name
voice_id = selected_voice_id
speech_speed_id = selected_speech_speed_id
call_id = selected_call_id
sample_set_id = selected_sample_set_id
if _save_atomic():
return true
display_name = previous_name
voice_id = previous_voice_id
speech_speed_id = previous_speech_speed_id
call_id = previous_call_id
sample_set_id = previous_sample_set_id
return false
@ -150,6 +163,12 @@ func _load_existing() -> bool:
call_id = VoiceProfilesType.sanitized_call_id(
str(data.get("call_id", VoiceProfilesType.DEFAULT_CALL_ID))
)
sample_set_id = VoiceProfilesType.sanitized_sample_set_id(
str(data.get(
"sample_set_id",
VoiceProfilesType.DEFAULT_SAMPLE_SET_ID,
))
)
created_at_unix = int(data["created_at_unix"])
return true
@ -162,6 +181,7 @@ func _save_atomic() -> bool:
"voice_id": voice_id,
"speech_speed_id": speech_speed_id,
"call_id": call_id,
"sample_set_id": sample_set_id,
"created_at_unix": created_at_unix,
}
var result := PortableFileGuard.write_guarded(

View file

@ -25,6 +25,7 @@ var _pending_apply: Dictionary[String, Dictionary] = {}
var _pending_voice_ids: Dictionary[String, String] = {}
var _pending_speech_speed_ids: Dictionary[String, String] = {}
var _pending_call_ids: Dictionary[String, String] = {}
var _pending_sample_set_ids: Dictionary[String, String] = {}
var _host_pending_apply: Dictionary[String, Dictionary] = {}
var _latest_check_id: String = ""
var _latest_check_name: String = ""
@ -70,6 +71,10 @@ func get_persisted_call_id() -> String:
return _preferences.call_id
func get_persisted_sample_set_id() -> String:
return _preferences.sample_set_id
func get_identity_fingerprint() -> String:
return (
_session.get_local_identity_fingerprint()
@ -151,6 +156,7 @@ func apply_profile(
voice_id: String = VoiceProfilesType.DEFAULT_ID,
speech_speed_id: String = VoiceProfilesType.DEFAULT_SPEED_ID,
call_id: String = VoiceProfilesType.DEFAULT_CALL_ID,
sample_set_id: String = VoiceProfilesType.DEFAULT_SAMPLE_SET_ID,
) -> bool:
var clean_name := display_name.strip_edges()
if (
@ -159,6 +165,7 @@ func apply_profile(
or not VoiceProfilesType.is_valid(voice_id)
or not VoiceProfilesType.is_valid_speed(speech_speed_id)
or not VoiceProfilesType.is_valid_call(call_id)
or not VoiceProfilesType.is_valid_sample_set(sample_set_id)
):
apply_finished.emit(false, "Check the player name and appearance choices.")
return false
@ -178,6 +185,7 @@ func apply_profile(
_pending_voice_ids[request_id] = voice_id
_pending_speech_speed_ids[request_id] = speech_speed_id
_pending_call_ids[request_id] = call_id
_pending_sample_set_ids[request_id] = sample_set_id
if _session == null or not _session.is_gameplay_session_active():
_apply_local_result(request_id, true, "", false, PackedStringArray())
elif _session.is_host():
@ -371,6 +379,7 @@ func _apply_local_result(
_pending_voice_ids.erase(request_id)
_pending_speech_speed_ids.erase(request_id)
_pending_call_ids.erase(request_id)
_pending_sample_set_ids.erase(request_id)
conflict_result.emit(request_id, conflict, suggestions)
apply_finished.emit(false, message)
return
@ -378,6 +387,7 @@ func _apply_local_result(
var previous_voice_id := _preferences.voice_id
var previous_speech_speed_id := _preferences.speech_speed_id
var previous_call_id := _preferences.call_id
var previous_sample_set_id := _preferences.sample_set_id
var requested_voice_id: String = _pending_voice_ids.get(
request_id,
VoiceProfilesType.DEFAULT_ID,
@ -390,16 +400,22 @@ func _apply_local_result(
request_id,
VoiceProfilesType.DEFAULT_CALL_ID,
)
var requested_sample_set_id: String = _pending_sample_set_ids.get(
request_id,
VoiceProfilesType.DEFAULT_SAMPLE_SET_ID,
)
if not _preferences.set_profile_identity(
str(request["display_name"]),
requested_voice_id,
requested_speech_speed_id,
requested_call_id,
requested_sample_set_id,
):
_pending_apply.erase(request_id)
_pending_voice_ids.erase(request_id)
_pending_speech_speed_ids.erase(request_id)
_pending_call_ids.erase(request_id)
_pending_sample_set_ids.erase(request_id)
apply_finished.emit(false, "Profile could not be saved.")
return
if not _appearance_store.save_snapshot(request["appearance"]):
@ -408,17 +424,20 @@ func _apply_local_result(
previous_voice_id,
previous_speech_speed_id,
previous_call_id,
previous_sample_set_id,
)
_pending_apply.erase(request_id)
_pending_voice_ids.erase(request_id)
_pending_speech_speed_ids.erase(request_id)
_pending_call_ids.erase(request_id)
_pending_sample_set_ids.erase(request_id)
apply_finished.emit(false, "Profile could not be saved.")
return
_pending_apply.erase(request_id)
_pending_voice_ids.erase(request_id)
_pending_speech_speed_ids.erase(request_id)
_pending_call_ids.erase(request_id)
_pending_sample_set_ids.erase(request_id)
if _session != null:
_session.set_local_appearance_snapshot(_appearance_store.get_snapshot())
if _session != null and _session.is_gameplay_session_active():
@ -600,6 +619,7 @@ func _on_session_state_changed(state: NetworkSession.State) -> void:
_pending_voice_ids.clear()
_pending_speech_speed_ids.clear()
_pending_call_ids.clear()
_pending_sample_set_ids.clear()
_host_pending_apply.clear()
_latest_check_id = ""
_latest_check_name = ""
@ -676,6 +696,7 @@ func _apply_local_voice_to_avatar() -> void:
var avatar := _spawn_service.get_avatar(local_peer_id)
if avatar != null:
avatar.apply_animalese_voice_id(_preferences.voice_id)
avatar.apply_animalese_sample_set_id(_preferences.sample_set_id)
func _new_id() -> String:

View file

@ -2,9 +2,27 @@ class_name AnimaleseVoiceProfiles
extends RefCounted
const DEFAULT_ID: String = "natural"
const DEFAULT_SAMPLE_SET_ID: String = "kat"
const DEFAULT_SPEED_ID: String = "normal"
const DEFAULT_CALL_ID: String = "meow"
const CALL_AUDIO_DIRECTORY: String = "res://sound/dialogue/calls"
const SAMPLE_SET_OPTIONS: Array[Dictionary] = [
{
"id": "kat",
"label": "kat",
"directory": "res://sound/dialogue/animalese/alphanumeric",
},
{
"id": "robot",
"label": "robot",
"directory": "res://sound/dialogue/animalese/robot",
},
{
"id": "kim",
"label": "kim",
"directory": "res://sound/dialogue/animalese/kim",
},
]
const OPTIONS: Array[Dictionary] = [
{"id": "tiny", "label": "tiny", "pitch": 1.24},
{"id": "bright", "label": "bright", "pitch": 1.10},
@ -12,6 +30,31 @@ const OPTIONS: Array[Dictionary] = [
{"id": "mellow", "label": "mellow", "pitch": 0.89},
{"id": "deep", "label": "deep", "pitch": 0.77},
]
static func is_valid_sample_set(sample_set_id: String) -> bool:
for option: Dictionary in SAMPLE_SET_OPTIONS:
if str(option.get("id", "")) == sample_set_id:
return true
return false
static func sanitized_sample_set_id(sample_set_id: String) -> String:
return (
sample_set_id
if is_valid_sample_set(sample_set_id)
else DEFAULT_SAMPLE_SET_ID
)
static func sample_directory_for(sample_set_id: String) -> String:
var resolved_id := sanitized_sample_set_id(sample_set_id)
for option: Dictionary in SAMPLE_SET_OPTIONS:
if str(option.get("id", "")) == resolved_id:
return str(option.get("directory", ""))
return ""
const SPEED_OPTIONS: Array[Dictionary] = [
{"id": "slow", "label": "slow", "characters_per_second": 20.0},
{"id": "relaxed", "label": "relaxed", "characters_per_second": 24.0},

View file

@ -37,6 +37,9 @@ const HeldItemAttachmentScene = preload(
"res://player/held_item_attachment.tscn"
)
const SprintDustTrailType = preload("res://player/sprint_dust_trail.gd")
const VoiceProfilesType = preload(
"res://player/animalese_voice_profiles.gd"
)
signal retract_visual_finished
@ -124,7 +127,8 @@ const CONTROLLER_ZOOM_TRIGGER_AXIS: JoyAxis = JOY_AXIS_TRIGGER_LEFT
var appearance_snapshot: Dictionary = (
CharacterCustomizationCatalog.default_snapshot()
)
var animalese_voice_id: String = "natural"
var animalese_voice_id: String = VoiceProfilesType.DEFAULT_ID
var animalese_sample_set_id: String = VoiceProfilesType.DEFAULT_SAMPLE_SET_ID
var active_bait_id: StringName = StringName()
var active_lure_id: StringName = StringName()
signal active_bait_changed(item_id: StringName)
@ -282,12 +286,22 @@ func _apply_presented_appearance() -> void:
func apply_animalese_voice_id(voice_id: String) -> void:
animalese_voice_id = voice_id
animalese_voice_id = VoiceProfilesType.sanitized_id(voice_id)
func get_animalese_voice_id() -> String:
return animalese_voice_id
func apply_animalese_sample_set_id(sample_set_id: String) -> void:
animalese_sample_set_id = VoiceProfilesType.sanitized_sample_set_id(
sample_set_id
)
func get_animalese_sample_set_id() -> String:
return animalese_sample_set_id
class ShowcaseCameraSnapshot:
extends RefCounted

View file

@ -1,71 +0,0 @@
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

112
scripts/import_animalese_voice.sh Executable file
View file

@ -0,0 +1,112 @@
#!/usr/bin/env bash
set -euo pipefail
readonly TARGET_RMS_DBFS="-18.0"
readonly PEAK_CEILING_DBFS="-3.0"
readonly OUTPUT_SAMPLE_RATE="48000"
usage() {
printf 'usage: %s SOURCE_DIRECTORY SAMPLE_SET_DIRECTORY\n' "$0" >&2
printf 'example: %s /path/to/voice_kim kim\n' "$0" >&2
}
if (( $# != 2 )); then
usage
exit 2
fi
if ! command -v sox >/dev/null 2>&1; then
printf 'error: sox is required to import animalese samples\n' >&2
exit 1
fi
readonly SCRIPT_DIRECTORY="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
readonly PROJECT_ROOT="$(cd -- "$SCRIPT_DIRECTORY/.." && pwd)"
readonly SOURCE_DIRECTORY="$(realpath -e -- "$1")"
readonly SAMPLE_SET_DIRECTORY="$2"
if [[ ! -d "$SOURCE_DIRECTORY" ]]; then
printf 'error: source is not a directory: %s\n' "$SOURCE_DIRECTORY" >&2
exit 1
fi
if [[ ! "$SAMPLE_SET_DIRECTORY" =~ ^[a-z0-9_-]+$ ]]; then
printf 'error: sample-set directory must use lowercase letters, numbers, underscores, or hyphens\n' >&2
exit 1
fi
readonly DESTINATION_DIRECTORY="$PROJECT_ROOT/sound/dialogue/animalese/$SAMPLE_SET_DIRECTORY"
readonly WORK_DIRECTORY="$(mktemp -d -t netfishing-animalese-import.XXXXXX)"
cleanup() {
if [[ "$WORK_DIRECTORY" == /tmp/netfishing-animalese-import.* ]]; then
find "$WORK_DIRECTORY" -depth -delete
fi
}
trap cleanup EXIT
mkdir -p -- "$WORK_DIRECTORY/converted" "$WORK_DIRECTORY/normalized"
mapfile -d '' SOURCE_FILES < <(
find "$SOURCE_DIRECTORY" -maxdepth 1 -type f -name '*.wav' -print0 \
| sort -z
)
if (( ${#SOURCE_FILES[@]} == 0 )); then
printf 'error: no .wav samples found in %s\n' "$SOURCE_DIRECTORY" >&2
exit 1
fi
for source_file in "${SOURCE_FILES[@]}"; do
filename="$(basename -- "$source_file")"
if [[ ! "$filename" =~ ^[a-z0-9_]+\.wav$ ]]; then
printf 'error: unsupported sample filename: %s\n' "$filename" >&2
exit 1
fi
converted_file="$WORK_DIRECTORY/converted/$filename"
normalized_file="$WORK_DIRECTORY/normalized/$filename"
# Measure the exact channel/rate conversion that will be shipped, then use
# the smaller of the RMS correction and peak-safe gain. Very short voice
# clips are not reliable inputs for program-loudness algorithms such as
# EBU R128, so this deterministic RMS/peak policy is intentional.
# Reserve headroom before high-quality resampling. Several supplied 8-bit
# clips sit near full scale and their interpolated waveform can otherwise
# clip before the normalization gain is calculated.
sox -D "$source_file" \
-r "$OUTPUT_SAMPLE_RATE" -c 1 -b 32 -e floating-point \
"$converted_file" gain -6 rate -v "$OUTPUT_SAMPLE_RATE"
statistics="$(sox "$converted_file" -n stat 2>&1)"
peak_amplitude="$(awk '/Maximum amplitude/ {print $3}' <<< "$statistics")"
rms_amplitude="$(awk '/RMS.*amplitude/ {print $3; exit}' <<< "$statistics")"
if ! awk -v peak="$peak_amplitude" -v rms="$rms_amplitude" \
'BEGIN {exit !(peak > 0.0 && rms > 0.0)}'; then
printf 'error: sample is silent or unreadable: %s\n' "$source_file" >&2
exit 1
fi
gain_db="$(
awk \
-v peak="$peak_amplitude" \
-v rms="$rms_amplitude" \
-v target_rms="$TARGET_RMS_DBFS" \
-v peak_ceiling="$PEAK_CEILING_DBFS" \
'BEGIN {
peak_db = 20.0 * log(peak) / log(10.0)
rms_db = 20.0 * log(rms) / log(10.0)
rms_gain = target_rms - rms_db
peak_gain = peak_ceiling - peak_db
gain = rms_gain < peak_gain ? rms_gain : peak_gain
printf "%.6f", gain
}'
)"
sox -D "$converted_file" \
-r "$OUTPUT_SAMPLE_RATE" -c 1 -b 16 -e signed-integer \
"$normalized_file" gain "$gain_db"
printf '%-16s %8s dB\n' "$filename" "$gain_db"
done
mkdir -p -- "$DESTINATION_DIRECTORY"
for normalized_file in "$WORK_DIRECTORY"/normalized/*.wav; do
cp -- "$normalized_file" "$DESTINATION_DIRECTORY/$(basename -- "$normalized_file")"
done
printf 'imported %d normalized samples to %s\n' \
"${#SOURCE_FILES[@]}" "$DESTINATION_DIRECTORY"

View file

@ -9,6 +9,7 @@ readonly TEST_TIMEOUT_SECONDS="${TEST_TIMEOUT_SECONDS:-120}"
readonly RUN_ROOT="$(mktemp -d -t netfishing-validations.XXXXXX)"
readonly -a QUICK_TESTS=(
"scripts/validate_animalese_samples.gd"
"tests/android_readiness_validation.gd"
"tests/controller_focus_presentation_validation.gd"
"tests/controller_focus_recovery_validation.gd"

View file

@ -0,0 +1,73 @@
extends SceneTree
const AnimaleseVoiceType = preload("res://ui/animalese_voice.gd")
const VoiceProfilesType = preload(
"res://player/animalese_voice_profiles.gd"
)
const EXPECTED_CHARACTERS := "abcdefghijklmnopqrstuvwxyz0123456789"
const ROBOT_CHARACTERS := "abcdefghijklmnopqrstuvwxyz"
var _failures: Array[String] = []
func _initialize() -> void:
_expect(
AnimaleseVoiceType.SUPPORTED_CHARACTERS == EXPECTED_CHARACTERS,
"animalese supports every letter and digit",
)
_expect(
VoiceProfilesType.DEFAULT_SAMPLE_SET_ID == "kat",
"kat is the default animalese voice set",
)
_expect(
VoiceProfilesType.is_valid_sample_set("robot"),
"the original tones remain available as robot",
)
_expect(
VoiceProfilesType.is_valid_sample_set("kim"),
"kim is available as an animalese voice set",
)
_validate_set("kat", EXPECTED_CHARACTERS, false)
_validate_set("robot", ROBOT_CHARACTERS, true)
_validate_set("kim", EXPECTED_CHARACTERS, false)
if _failures.is_empty():
print("Animalese sample validation: PASS")
quit(0)
return
for failure: String in _failures:
push_error(failure)
quit(1)
func _validate_set(
sample_set_id: String,
characters: String,
expects_fallback: bool,
) -> void:
var sample_directory := VoiceProfilesType.sample_directory_for(
sample_set_id
)
_expect(
not sample_directory.is_empty(),
"%s has a sample directory" % sample_set_id,
)
for character_index: int in range(characters.length()):
var character := characters.substr(character_index, 1)
var sample_path := "%s/%s.wav" % [sample_directory, character]
_expect(
ResourceLoader.exists(sample_path, "AudioStreamWAV"),
"%s sample loads for %s" % [sample_set_id, character],
)
if expects_fallback:
_expect(
ResourceLoader.exists(
"%s/fallback.wav" % sample_directory,
"AudioStreamWAV",
),
"%s supplies a fallback for digits and symbols" % sample_set_id,
)
func _expect(condition: bool, message: String) -> void:
if not condition:
_failures.append(message)

Binary file not shown.

View file

@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
uid="uid://dvajjy4bwadio"
path="res://.godot/imported/a.wav-9ab4b1a9a555e6fcf07951f399769b30.sample"
uid="uid://cbn5ed728rsm"
path="res://.godot/imported/0.wav-9378d7763e1ef87ae982ab57f6e81f2d.sample"
[deps]
source_file="res://sound/dialogue/animalese/placeholder/a.wav"
dest_files=["res://.godot/imported/a.wav-9ab4b1a9a555e6fcf07951f399769b30.sample"]
source_file="res://sound/dialogue/animalese/alphanumeric/0.wav"
dest_files=["res://.godot/imported/0.wav-9378d7763e1ef87ae982ab57f6e81f2d.sample"]
[params]

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://8ofrxxya8xo8"
path="res://.godot/imported/1.wav-759e5f8cac5cef5d0e23605844f5032c.sample"
[deps]
source_file="res://sound/dialogue/animalese/alphanumeric/1.wav"
dest_files=["res://.godot/imported/1.wav-759e5f8cac5cef5d0e23605844f5032c.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://cmxvxip3pwuun"
path="res://.godot/imported/2.wav-4b3515a4b79f8ae9ffc829c8a5e3c285.sample"
[deps]
source_file="res://sound/dialogue/animalese/alphanumeric/2.wav"
dest_files=["res://.godot/imported/2.wav-4b3515a4b79f8ae9ffc829c8a5e3c285.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://c8017waek7ew8"
path="res://.godot/imported/3.wav-ffb8f531dceaa153b492b74c4bbf7c7c.sample"
[deps]
source_file="res://sound/dialogue/animalese/alphanumeric/3.wav"
dest_files=["res://.godot/imported/3.wav-ffb8f531dceaa153b492b74c4bbf7c7c.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://bbtypop1cmrw1"
path="res://.godot/imported/4.wav-effad197373ffb9b48afe78e0fe6c95a.sample"
[deps]
source_file="res://sound/dialogue/animalese/alphanumeric/4.wav"
dest_files=["res://.godot/imported/4.wav-effad197373ffb9b48afe78e0fe6c95a.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://ddwhh6igah1ok"
path="res://.godot/imported/5.wav-2cd0baa0afa291660575e1fc431a6b91.sample"
[deps]
source_file="res://sound/dialogue/animalese/alphanumeric/5.wav"
dest_files=["res://.godot/imported/5.wav-2cd0baa0afa291660575e1fc431a6b91.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://cqsddej565w48"
path="res://.godot/imported/6.wav-8c88e3785af1748d3cc8334b825655e1.sample"
[deps]
source_file="res://sound/dialogue/animalese/alphanumeric/6.wav"
dest_files=["res://.godot/imported/6.wav-8c88e3785af1748d3cc8334b825655e1.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://de5lwv3vtq4jo"
path="res://.godot/imported/7.wav-aa6620dad62f7a8fa68ac3dfe0e8bce0.sample"
[deps]
source_file="res://sound/dialogue/animalese/alphanumeric/7.wav"
dest_files=["res://.godot/imported/7.wav-aa6620dad62f7a8fa68ac3dfe0e8bce0.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://lsklvocqcohu"
path="res://.godot/imported/8.wav-ee305634b68bbc3c852777570ad90d81.sample"
[deps]
source_file="res://sound/dialogue/animalese/alphanumeric/8.wav"
dest_files=["res://.godot/imported/8.wav-ee305634b68bbc3c852777570ad90d81.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://cuwg5n2w8trdf"
path="res://.godot/imported/9.wav-2dbc1a5726fda3c5d3e5b8a5c940e14e.sample"
[deps]
source_file="res://sound/dialogue/animalese/alphanumeric/9.wav"
dest_files=["res://.godot/imported/9.wav-2dbc1a5726fda3c5d3e5b8a5c940e14e.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

Binary file not shown.

View file

@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
uid="uid://b5al0m4de1m5x"
path="res://.godot/imported/x.wav-b1beaddcc7d6663882ddd8b86e65db56.sample"
uid="uid://dnnf2sfntra41"
path="res://.godot/imported/a.wav-07b66f5c4c337a0412ac22c096725756.sample"
[deps]
source_file="res://sound/dialogue/animalese/placeholder/x.wav"
dest_files=["res://.godot/imported/x.wav-b1beaddcc7d6663882ddd8b86e65db56.sample"]
source_file="res://sound/dialogue/animalese/alphanumeric/a.wav"
dest_files=["res://.godot/imported/a.wav-07b66f5c4c337a0412ac22c096725756.sample"]
[params]

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://cxueylrkf0358"
path="res://.godot/imported/b.wav-4eb4b2c71e0d5f50efbe3fc8793801e4.sample"
[deps]
source_file="res://sound/dialogue/animalese/alphanumeric/b.wav"
dest_files=["res://.godot/imported/b.wav-4eb4b2c71e0d5f50efbe3fc8793801e4.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://cbwe5uie8fa6k"
path="res://.godot/imported/c.wav-86a3dcc44eaabde775a8efc14c630f27.sample"
[deps]
source_file="res://sound/dialogue/animalese/alphanumeric/c.wav"
dest_files=["res://.godot/imported/c.wav-86a3dcc44eaabde775a8efc14c630f27.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://yyr410g00p5p"
path="res://.godot/imported/d.wav-5577435bb675e68970dd9de887fff671.sample"
[deps]
source_file="res://sound/dialogue/animalese/alphanumeric/d.wav"
dest_files=["res://.godot/imported/d.wav-5577435bb675e68970dd9de887fff671.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://cp6stdrpayltv"
path="res://.godot/imported/e.wav-6373ce27ee05eb89c9d510ca465a24d1.sample"
[deps]
source_file="res://sound/dialogue/animalese/alphanumeric/e.wav"
dest_files=["res://.godot/imported/e.wav-6373ce27ee05eb89c9d510ca465a24d1.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

Binary file not shown.

View file

@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
uid="uid://c0gv4cphnfcxf"
path="res://.godot/imported/f.wav-8eb31f2de6240647a0d6d2520aadcd6a.sample"
uid="uid://bc1lj4kba0fr1"
path="res://.godot/imported/f.wav-637c7bb130969a55972a1c232962cb40.sample"
[deps]
source_file="res://sound/dialogue/animalese/placeholder/f.wav"
dest_files=["res://.godot/imported/f.wav-8eb31f2de6240647a0d6d2520aadcd6a.sample"]
source_file="res://sound/dialogue/animalese/alphanumeric/f.wav"
dest_files=["res://.godot/imported/f.wav-637c7bb130969a55972a1c232962cb40.sample"]
[params]

Binary file not shown.

View file

@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
uid="uid://c7706p0fc8y7c"
path="res://.godot/imported/g.wav-45b2cbd9653a31477e3f00c9432d560c.sample"
uid="uid://6k1d81vrewm7"
path="res://.godot/imported/g.wav-73da4ecac29d03a5e0c1bb2463bcb6cb.sample"
[deps]
source_file="res://sound/dialogue/animalese/placeholder/g.wav"
dest_files=["res://.godot/imported/g.wav-45b2cbd9653a31477e3f00c9432d560c.sample"]
source_file="res://sound/dialogue/animalese/alphanumeric/g.wav"
dest_files=["res://.godot/imported/g.wav-73da4ecac29d03a5e0c1bb2463bcb6cb.sample"]
[params]

Binary file not shown.

View file

@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
uid="uid://cl66wka105xmo"
path="res://.godot/imported/h.wav-2da53a6638eebb75f0867eaf75dc24ba.sample"
uid="uid://boatg77atys5a"
path="res://.godot/imported/h.wav-44456bd4f004ee75088b13cb2069e3fb.sample"
[deps]
source_file="res://sound/dialogue/animalese/placeholder/h.wav"
dest_files=["res://.godot/imported/h.wav-2da53a6638eebb75f0867eaf75dc24ba.sample"]
source_file="res://sound/dialogue/animalese/alphanumeric/h.wav"
dest_files=["res://.godot/imported/h.wav-44456bd4f004ee75088b13cb2069e3fb.sample"]
[params]

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://cvru47ct1d7mj"
path="res://.godot/imported/i.wav-8ce67b78d17a9ac111433177c3ed850b.sample"
[deps]
source_file="res://sound/dialogue/animalese/alphanumeric/i.wav"
dest_files=["res://.godot/imported/i.wav-8ce67b78d17a9ac111433177c3ed850b.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

Binary file not shown.

View file

@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
uid="uid://jicgdbnyt6bm"
path="res://.godot/imported/i.wav-1e8d7f13980a6f2f61d73f65a2efc91c.sample"
uid="uid://dwi1wq2dyli5y"
path="res://.godot/imported/j.wav-c07aa6b0f20fa8fcc89a706a3391c11c.sample"
[deps]
source_file="res://sound/dialogue/animalese/placeholder/i.wav"
dest_files=["res://.godot/imported/i.wav-1e8d7f13980a6f2f61d73f65a2efc91c.sample"]
source_file="res://sound/dialogue/animalese/alphanumeric/j.wav"
dest_files=["res://.godot/imported/j.wav-c07aa6b0f20fa8fcc89a706a3391c11c.sample"]
[params]

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://6dkc31pv1ey1"
path="res://.godot/imported/k.wav-c6fde0aff33080ec99703bdd4fa87e80.sample"
[deps]
source_file="res://sound/dialogue/animalese/alphanumeric/k.wav"
dest_files=["res://.godot/imported/k.wav-c6fde0aff33080ec99703bdd4fa87e80.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://d2o1hk6e4bbss"
path="res://.godot/imported/l.wav-722141f795e13543172ffd960308b24c.sample"
[deps]
source_file="res://sound/dialogue/animalese/alphanumeric/l.wav"
dest_files=["res://.godot/imported/l.wav-722141f795e13543172ffd960308b24c.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

Binary file not shown.

View file

@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
uid="uid://dan4o17oreugv"
path="res://.godot/imported/m.wav-aa394c4b1d5309d859ce73a368754264.sample"
uid="uid://c588ew7t8dwse"
path="res://.godot/imported/m.wav-2ad62bfb1ad90a0667f708c5154de5dc.sample"
[deps]
source_file="res://sound/dialogue/animalese/placeholder/m.wav"
dest_files=["res://.godot/imported/m.wav-aa394c4b1d5309d859ce73a368754264.sample"]
source_file="res://sound/dialogue/animalese/alphanumeric/m.wav"
dest_files=["res://.godot/imported/m.wav-2ad62bfb1ad90a0667f708c5154de5dc.sample"]
[params]

Binary file not shown.

View file

@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
uid="uid://dxup6pw80km61"
path="res://.godot/imported/n.wav-84e9a6fa6664222868d647187a09f374.sample"
uid="uid://mc1vnfuhpuwr"
path="res://.godot/imported/n.wav-b995292a9ce9755047a5a6a6166a4a4c.sample"
[deps]
source_file="res://sound/dialogue/animalese/placeholder/n.wav"
dest_files=["res://.godot/imported/n.wav-84e9a6fa6664222868d647187a09f374.sample"]
source_file="res://sound/dialogue/animalese/alphanumeric/n.wav"
dest_files=["res://.godot/imported/n.wav-b995292a9ce9755047a5a6a6166a4a4c.sample"]
[params]

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://rkmixmwg8vgp"
path="res://.godot/imported/o.wav-696f77252311afda46e416ece590eb55.sample"
[deps]
source_file="res://sound/dialogue/animalese/alphanumeric/o.wav"
dest_files=["res://.godot/imported/o.wav-696f77252311afda46e416ece590eb55.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

Binary file not shown.

View file

@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
uid="uid://crubsdrd220eh"
path="res://.godot/imported/p.wav-c32b759c822f258d8d947827adf8127a.sample"
uid="uid://blbcb70gci0q2"
path="res://.godot/imported/p.wav-bc58581e4a5d8761e1c8b91f069f951d.sample"
[deps]
source_file="res://sound/dialogue/animalese/placeholder/p.wav"
dest_files=["res://.godot/imported/p.wav-c32b759c822f258d8d947827adf8127a.sample"]
source_file="res://sound/dialogue/animalese/alphanumeric/p.wav"
dest_files=["res://.godot/imported/p.wav-bc58581e4a5d8761e1c8b91f069f951d.sample"]
[params]

Binary file not shown.

View file

@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
uid="uid://ct0decr76agoq"
path="res://.godot/imported/q.wav-d494b927c6f2b8bfbaa44d1f81887731.sample"
uid="uid://bmu30s0vscbvg"
path="res://.godot/imported/q.wav-b9d907f73d4a59dd6641ffe35c36aaac.sample"
[deps]
source_file="res://sound/dialogue/animalese/placeholder/q.wav"
dest_files=["res://.godot/imported/q.wav-d494b927c6f2b8bfbaa44d1f81887731.sample"]
source_file="res://sound/dialogue/animalese/alphanumeric/q.wav"
dest_files=["res://.godot/imported/q.wav-b9d907f73d4a59dd6641ffe35c36aaac.sample"]
[params]

Binary file not shown.

View file

@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
uid="uid://dpq7rin4pr1n3"
path="res://.godot/imported/r.wav-d1d419e3246c0e918ff51a9dd662a742.sample"
uid="uid://bgdec44ic6msd"
path="res://.godot/imported/r.wav-d5ea52bfff9160d4d25d74dd94072208.sample"
[deps]
source_file="res://sound/dialogue/animalese/placeholder/r.wav"
dest_files=["res://.godot/imported/r.wav-d1d419e3246c0e918ff51a9dd662a742.sample"]
source_file="res://sound/dialogue/animalese/alphanumeric/r.wav"
dest_files=["res://.godot/imported/r.wav-d5ea52bfff9160d4d25d74dd94072208.sample"]
[params]

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://draie1dh0muw"
path="res://.godot/imported/s.wav-0dfb29a8b9a3bf4890d24e227cd0d0c4.sample"
[deps]
source_file="res://sound/dialogue/animalese/alphanumeric/s.wav"
dest_files=["res://.godot/imported/s.wav-0dfb29a8b9a3bf4890d24e227cd0d0c4.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://ybmvcpv7prx"
path="res://.godot/imported/t.wav-d4e7e7dbe4c04e54e9c7edb0d85af81a.sample"
[deps]
source_file="res://sound/dialogue/animalese/alphanumeric/t.wav"
dest_files=["res://.godot/imported/t.wav-d4e7e7dbe4c04e54e9c7edb0d85af81a.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

Binary file not shown.

View file

@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
uid="uid://dr0dp5v8fdb8r"
path="res://.godot/imported/t.wav-3a26c5be0f8e3007adf1daa05a6a1ea2.sample"
uid="uid://dn0hxgm1ueiv7"
path="res://.godot/imported/u.wav-b8a534783b71ec701bb0925308d513a2.sample"
[deps]
source_file="res://sound/dialogue/animalese/placeholder/t.wav"
dest_files=["res://.godot/imported/t.wav-3a26c5be0f8e3007adf1daa05a6a1ea2.sample"]
source_file="res://sound/dialogue/animalese/alphanumeric/u.wav"
dest_files=["res://.godot/imported/u.wav-b8a534783b71ec701bb0925308d513a2.sample"]
[params]

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://c5p70bkanhlua"
path="res://.godot/imported/v.wav-0dc1396f424599662d6812e2e59a3b71.sample"
[deps]
source_file="res://sound/dialogue/animalese/alphanumeric/v.wav"
dest_files=["res://.godot/imported/v.wav-0dc1396f424599662d6812e2e59a3b71.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://dr2025tcuq5lq"
path="res://.godot/imported/w.wav-caf9c9b3fa280c326a6be0c426672562.sample"
[deps]
source_file="res://sound/dialogue/animalese/alphanumeric/w.wav"
dest_files=["res://.godot/imported/w.wav-caf9c9b3fa280c326a6be0c426672562.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://c2c6lwmfogv1o"
path="res://.godot/imported/x.wav-6dec8d5d6aa916173970737d9db8f509.sample"
[deps]
source_file="res://sound/dialogue/animalese/alphanumeric/x.wav"
dest_files=["res://.godot/imported/x.wav-6dec8d5d6aa916173970737d9db8f509.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

Binary file not shown.

View file

@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
uid="uid://c6hust77yamoe"
path="res://.godot/imported/y.wav-5ef178912b2921bb7c790b12e2e82337.sample"
uid="uid://csjurthefub6u"
path="res://.godot/imported/y.wav-82e4c89a86bc31782eaca5f5ee737bb1.sample"
[deps]
source_file="res://sound/dialogue/animalese/placeholder/y.wav"
dest_files=["res://.godot/imported/y.wav-5ef178912b2921bb7c790b12e2e82337.sample"]
source_file="res://sound/dialogue/animalese/alphanumeric/y.wav"
dest_files=["res://.godot/imported/y.wav-82e4c89a86bc31782eaca5f5ee737bb1.sample"]
[params]

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://bujyv5lsxarhr"
path="res://.godot/imported/z.wav-38cc5c83dca1b80b731d307651219c08.sample"
[deps]
source_file="res://sound/dialogue/animalese/alphanumeric/z.wav"
dest_files=["res://.godot/imported/z.wav-38cc5c83dca1b80b731d307651219c08.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

Binary file not shown.

View file

@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
uid="uid://eypaqf1iaq5"
path="res://.godot/imported/k.wav-b85d045dc022b9055c25a650b2e3af2a.sample"
uid="uid://w8wvtbsovk1y"
path="res://.godot/imported/0.wav-bbd392e8755139dca852d942c97afaa6.sample"
[deps]
source_file="res://sound/dialogue/animalese/placeholder/k.wav"
dest_files=["res://.godot/imported/k.wav-b85d045dc022b9055c25a650b2e3af2a.sample"]
source_file="res://sound/dialogue/animalese/kim/0.wav"
dest_files=["res://.godot/imported/0.wav-bbd392e8755139dca852d942c97afaa6.sample"]
[params]

Binary file not shown.

View file

@ -2,13 +2,13 @@
importer="wav"
type="AudioStreamWAV"
uid="uid://c00meumj2blfa"
path="res://.godot/imported/s.wav-15abb20075aacc8d5fe95cd206ae25ce.sample"
uid="uid://cnny27n4wbk6s"
path="res://.godot/imported/1.wav-5303efd25be6d603f73c2c2d77759fce.sample"
[deps]
source_file="res://sound/dialogue/animalese/placeholder/s.wav"
dest_files=["res://.godot/imported/s.wav-15abb20075aacc8d5fe95cd206ae25ce.sample"]
source_file="res://sound/dialogue/animalese/kim/1.wav"
dest_files=["res://.godot/imported/1.wav-5303efd25be6d603f73c2c2d77759fce.sample"]
[params]

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://daw5c2h7p5yu6"
path="res://.godot/imported/2.wav-a384b25ebb56567742354475fa551c97.sample"
[deps]
source_file="res://sound/dialogue/animalese/kim/2.wav"
dest_files=["res://.godot/imported/2.wav-a384b25ebb56567742354475fa551c97.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://dn24y7on5811q"
path="res://.godot/imported/3.wav-da7f408c6ee782136a7225f6a5b4c2da.sample"
[deps]
source_file="res://sound/dialogue/animalese/kim/3.wav"
dest_files=["res://.godot/imported/3.wav-da7f408c6ee782136a7225f6a5b4c2da.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://cpbt1fduqk2bc"
path="res://.godot/imported/4.wav-710dd9e6b25e0147dec2f21cfae1a894.sample"
[deps]
source_file="res://sound/dialogue/animalese/kim/4.wav"
dest_files=["res://.godot/imported/4.wav-710dd9e6b25e0147dec2f21cfae1a894.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://chf4scjvimr1o"
path="res://.godot/imported/5.wav-7adb723973f3005425a71be181ad28e9.sample"
[deps]
source_file="res://sound/dialogue/animalese/kim/5.wav"
dest_files=["res://.godot/imported/5.wav-7adb723973f3005425a71be181ad28e9.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://phxil12i1qmt"
path="res://.godot/imported/6.wav-1303e051aae59fdf169def7624379571.sample"
[deps]
source_file="res://sound/dialogue/animalese/kim/6.wav"
dest_files=["res://.godot/imported/6.wav-1303e051aae59fdf169def7624379571.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://dn1pxla32ab23"
path="res://.godot/imported/7.wav-cb4bd630248ebe745a1646dc7060d526.sample"
[deps]
source_file="res://sound/dialogue/animalese/kim/7.wav"
dest_files=["res://.godot/imported/7.wav-cb4bd630248ebe745a1646dc7060d526.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

Some files were not shown because too many files have changed in this diff Show more