Skip to content

Commit

Permalink
Comments/Type Hints. Child feminine pack
Browse files Browse the repository at this point in the history
  • Loading branch information
rrealmuto committed Dec 24, 2024
1 parent 278c939 commit c6c0aab
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 66 deletions.
10 changes: 5 additions & 5 deletions Cosmetics.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from Plandomizer import InvalidFileException
from Utils import data_path
from version import __version__
from Voices import VOICE_PACK_AGE, _patch_voice_pack, child_link_sfx, adult_link_sfx
from Voices import VOICE_PACK_AGE, patch_voice_pack, child_link_sfx, adult_link_sfx

if TYPE_CHECKING:
from Rom import Rom
Expand Down Expand Up @@ -937,16 +937,16 @@ def patch_voices(rom: Rom, settings: Settings, log: CosmeticsLog, symbols: dict[
# Write the setting to the log
log.sfx[log_key] = voice_setting

def patch_voice_pack(rom: Rom, settings: Settings, log: CosmeticsLog, symbols: dict[str, int]):
def patch_voice_packs(rom: Rom, settings: Settings, log: CosmeticsLog, symbols: dict[str, int]):
if settings.sfx_link_adult == 'Silent':
patch_silent_voice(rom, VOICE_PACK_AGE.ADULT, log)
elif settings.sfx_link_adult != 'Default':
_patch_voice_pack(rom, VOICE_PACK_AGE.ADULT, settings.sfx_link_adult, settings)
patch_voice_pack(rom, VOICE_PACK_AGE.ADULT, settings.sfx_link_adult, settings)

if settings.sfx_link_child == 'Silent':
patch_silent_voice(rom, VOICE_PACK_AGE.ADULT, log)
elif settings.sfx_link_child != 'Default':
_patch_voice_pack(rom, VOICE_PACK_AGE.CHILD, settings.sfx_link_child, settings)
patch_voice_pack(rom, VOICE_PACK_AGE.CHILD, settings.sfx_link_child, settings)

def patch_music_changes(rom: Rom, settings: Settings, log: CosmeticsLog, symbols: dict[str, int]) -> None:
# Music tempo changes
Expand Down Expand Up @@ -1234,7 +1234,7 @@ def patch_song_names(rom: Rom, settings: Settings, log: CosmeticsLog, symbols: d
# 8.2.22
patch_sets[0x1F073FE3] = {
"patches": patch_sets[0x1F073FE2]["patches"] + [
patch_voice_pack,
patch_voice_packs,
],
"symbols": {
**patch_sets[0x1F073FE2]["symbols"]
Expand Down
87 changes: 26 additions & 61 deletions Voices.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from math import ceil
import random
import sys
from typing import BinaryIO
from Audiobank import *
from Rom import Rom
from Settings import Settings
Expand Down Expand Up @@ -30,37 +31,44 @@
SFX_TYPE_CHOOSE_RAND = 0x02
SFX_TYPE_PLAY_ORDERED = 0x03


def calculate_ticks(numFrames, sampleRate):
# Calculate the ticks variable to be used when overwriting
# SFX sequences
def calculate_ticks(numFrames, sampleRate) -> int:
duration = float(numFrames) / float(sampleRate)
# seconds = ticks / (120 * 48) * 60
numTicks = int(duration * (120 * 48) / 60) + 1
numTicks |= 0x8000 # for aseq VAR format
return numTicks

def adult_sfx_patch_death(rom: Rom, numFrames: int, sampleRate: int):
# SFX patch functions
# Used to patch SFX that use multiple sequenced samples with a single audio file
# Return a list of tuples of the form (address, [patch_data])
# Where address is the offset into the SEQ0 file
# and patch_data is a list of bytes to patch at that address

def adult_sfx_patch_death(rom: Rom, numFrames: int, sampleRate: int) -> tuple[int, list[int]]:
# increase length. Patch out the last 2 notes w/ 0xFF
numTicks = calculate_ticks(numFrames, sampleRate)
tick_bytes = numTicks.to_bytes(2, 'big')
return [(0x6265, [0x4D] + list(tick_bytes) + [0x64] + [0xFF]*5)]

def adult_sfx_patch_sneeze(rom: Rom, numFrames: int, sampleRate: int):
def adult_sfx_patch_sneeze(rom: Rom, numFrames: int, sampleRate: int) -> tuple[int, list[int]]:
# Increase duration of first note and make it 100 volume. Patch out the last 2 notes
numTicks = calculate_ticks(numFrames, sampleRate)
tick_bytes = numTicks.to_bytes(2, 'big')
return [
(0x628D, [0x50] + list(tick_bytes) + [0x64] + [0xFF]*7)
]

def adult_sfx_patch_sweat(rom: Rom, numFrames: int, sampleRate: int):
def adult_sfx_patch_sweat(rom: Rom, numFrames: int, sampleRate: int) -> tuple[int, list[int]]:
numTicks = calculate_ticks(numFrames, sampleRate)
tick_bytes = numTicks.to_bytes(2, 'big')
return [
(0x629F, [0x53] + list(tick_bytes) + [0x64] +
[0xFF]*7)
]

def adult_sfx_patch_stretch(rom: Rom, numFrames: int, sampleRate: int):
def adult_sfx_patch_stretch(rom: Rom, numFrames: int, sampleRate: int) -> tuple[int, list[int]]:
numTicks = calculate_ticks(numFrames, sampleRate)
tick_bytes = numTicks.to_bytes(2, 'big')
return [
Expand Down Expand Up @@ -209,8 +217,6 @@ def child_sfx_patch_stretch(rom: Rom, numFrames: int, sampleRate: int):
return [
(0x6488, [0x4A] + list(tick_bytes) + [0x64] + [0xFF]*6)
]
# Replace the first


child_sfx_id_map = {
0x6820: { # Child Link Attacks
Expand Down Expand Up @@ -405,15 +411,10 @@ class VOICE_PACK_AGE(Enum):
# sfx_id_map - the sfx_id -> bank map to use selected by age
# pak_sounds - a dictionary mapping for the entire voice pack - sfx_id to a list of tuples containing the file's name and the raw data from the file
# age that this pak is for
def process_pak_sfx_by_id(pak_sfx_id: int, sfx_id_map, pak_sounds, age):
def process_pak_sfx_by_id(pak_sfx_id: int, sfx_id_map, pak_sounds, age) -> tuple[str, int, list[int], bytearray, int, int, function]:
to_add = []
# Try adult and child SFX ID
# age_shift = -0x20 if age == VOICE_PACK_AGE.ADULT else 0x20 # For a pak we're using as adult, also try using child SFX IDs and vice-versa

# Check if the sfx_id is in the mapping for this age. If not try to shift it to the other age
# This should allow a pack designed for a single age to work for another age
# And if a pack has both ages in it, will only patch the current age on this pass
# sfx_id = pak_sfx_id if pak_sfx_id in sfx_id_map.keys() else pak_sfx_id + age_shift

# Check if the sfx_id is in the mapping for this age.
sfx_id = pak_sfx_id
if sfx_id in sfx_id_map.keys():
pak_opts = pak_sounds[pak_sfx_id] # Options provided in the pack
Expand Down Expand Up @@ -466,13 +467,7 @@ def process_pak_sfx_by_id(pak_sfx_id: int, sfx_id_map, pak_sounds, age):
raise Exception("Unsupported sfx type")
return to_add

def get_sfx_id(sfx_list: list[tuple[str,int]], sfx_name: str):
for name, sfx_id in sfx_list:
if sfx_name == name:
return sfx_id
return -1

def _patch_voice_pack(rom: Rom, age: VOICE_PACK_AGE, voice_pack: str, settings: Settings):
def patch_voice_pack(rom: Rom, age: VOICE_PACK_AGE, voice_pack: str, settings: Settings) -> None:
# Don't allow custom voice packs when generating patch files
if settings.generating_patch_file:
return
Expand Down Expand Up @@ -507,7 +502,6 @@ def _patch_voice_pack(rom: Rom, age: VOICE_PACK_AGE, voice_pack: str, settings:
pak_sounds = pak.read_all_sounds()
for pak_sfx_id in pak_sounds.keys():
sfxs.extend(process_pak_sfx_by_id(pak_sfx_id, sfx_id_map, pak_sounds, age))


# New ZOOTR voice pack file
# Support mapping sounds either via SFX_ID like ML64 does
Expand Down Expand Up @@ -605,7 +599,6 @@ def _patch_voice_pack(rom: Rom, age: VOICE_PACK_AGE, voice_pack: str, settings:
bank.bank_data[sfx.sfx_offset:sfx.sfx_offset+0x08] = sfx.get_bytes()
bank.bank_data[sfx.sample.loop_addr:sfx.sample.loop_addr+len(loopBytes)] = loopBytes


dma_entry = rom.dma[AUDIOSEQ_DMADATA_INDEX]
# Need to read the Audioseq table to find the start of sequence 0
seq0_table_entry = rom.read_bytes(0xB89AE0, 0x10)
Expand All @@ -615,14 +608,13 @@ def _patch_voice_pack(rom: Rom, age: VOICE_PACK_AGE, voice_pack: str, settings:
patches = patch(rom, numSampleFrames, sampleRate)
for addr, patch_bytes in patches:
rom.write_bytes(dma_entry.start + seq0_offset + addr, patch_bytes)

return

# Processes a single sound file into ADPCM data ready to be patched into the ROM
# file_name: the name of the file, used to determine how to process it
# file: a file-like object that will be read to process the file
# returns: tuple of the form (soundData, numSampleFrames, sampleRate)
def process_sound_file(file_name: str, file, trim: bool = False) -> tuple[bytearray, int, int]:
def process_sound_file(file_name: str, file: BinaryIO, trim: bool = False) -> tuple[bytearray, int, int]:
# Check if this is a file format that sf supports
filename, ext = os.path.splitext(file_name)
if ext.strip('.').upper() in sf.available_formats():
Expand All @@ -633,12 +625,12 @@ def process_sound_file(file_name: str, file, trim: bool = False) -> tuple[bytear
soundData, numSampleFrames, sampleRate = process_bin_file(file)
else:
raise Exception(f"Unsupported file format {ext} in custom voice pack.")

return soundData, numSampleFrames, sampleRate


# Read an audio file using the soundfile python library
def process_soundfile_file(f, trim=False):
def process_soundfile_file(f: BinaryIO, trim=False) -> tuple[bytes, int, int]:
data, sampleRate = sf.read(f)
if data.ndim == 2 and data.shape[1] == 2:
# Convert stereo to mono by averaging the two channels
Expand All @@ -656,17 +648,16 @@ def process_soundfile_file(f, trim=False):
soundData = adpcm_encode(frames, len(data)) # Encode the raw samples
return soundData, numSampleFrames, sampleRate

# Used for patching SFX that have already been stripped into raw binary ready to patch into the ROM
def process_bin_file(f):
# Used for patching SFX AIFC files that have already been stripped into raw binary ready to patch into the ROM
# Assume a vanilla sampling rate of 20000
def process_bin_file(f: BinaryIO) -> tuple[bytes, int, int]:
soundData = f.read()
numSampleFrames = int(len(soundData) * 3 / 2)
numSampleFrames = int(len(soundData) * 16 / 9)
sampleRate = 20000
return (soundData, numSampleFrames, sampleRate)


# Pretty basic aifc file parser. Extracts the already encoded .aifc data metadata from the file
def process_aifc_file(f):
def process_aifc_file(f: BinaryIO) -> tuple[bytes, int, int]:
# Open the .aifc file
index = 0
# Read data from the .aifc file
Expand Down Expand Up @@ -742,30 +733,4 @@ def process_aifc_file(f):
soundData = data[8:8 + dataLen]
return soundData, numSampleFrames, sampleRate

def rename_old_files(dir: str, age: VOICE_PACK_AGE):
# list the contents of the directory

files : list[str] = os.listdir(dir)
sfxlist = adult_link_sfx if age == VOICE_PACK_AGE.ADULT else child_link_sfx
for file in files:
if file.startswith("00-") and (file.endswith(".aifc")):
# Rename this file
split = file.split("_")
split = split[0].split("-")
bank = split[0]
oldsfxid = int(split[1],16)
# Get the name from the table
for sfxname, sfxid in sfxlist:
# Rename the file to sfxname
if oldsfxid == sfxid:
old_path = os.path.join(dir,file)
new_path = os.path.join(dir,sfxname+ ".aifc")
print("Renaming " + old_path)
os.rename(old_path, new_path)
break

if __name__ == "__main__":
rom = Rom("ZOOTDEC.z64")
rename_old_files("data/Voices/Child/Feminine_New", VOICE_PACK_AGE.CHILD)
#_patch_voice_pack(rom, "Mario", VOICE_PACK_AGE.ADULT)

Binary file added data/Voices/Child/FeminineWav/FeminineWav.zip
Binary file not shown.

0 comments on commit c6c0aab

Please sign in to comment.