1
0
Fork 0
mirror of https://github.com/zeldaret/oot.git synced 2025-07-17 13:24:45 +00:00

[Audio 9/?] Multiversion samplebank and soundfont extraction xmls, 1.0 and 1.1 audio extraction (#2291)

* [Audio 9/?] Multiversion samplebank and soundfont extraction xmls, 1.0 and 1.1 audio extraction

* Rework multiversion samplebanks to reduce duplicates
This commit is contained in:
Tharo 2024-11-12 13:47:34 +00:00 committed by GitHub
parent d886ebe711
commit 2d454933f3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 741 additions and 585 deletions

View file

@ -5,11 +5,11 @@
#
import math, struct
from typing import Dict, Tuple
from xml.etree.ElementTree import Element
from typing import Dict, Optional
from .audio_tables import AudioCodeTableEntry
from .audiobank_structs import AudioSampleCodec, SoundFontSample, AdpcmBook, AdpcmLoop
from .extraction_xml import SampleBankExtractionDescription
from .tuning import pitch_names, note_z64_to_midi, recalc_tuning, rate_from_tuning, rank_rates_notes, BAD_FLOATS
from .util import align, error, XMLWriter, f32_to_u32
@ -207,7 +207,7 @@ class AudioTableSample(AudioTableData):
def base_note_number(self):
return note_z64_to_midi(pitch_names.index(self.base_note))
def resolve_basenote_rate(self, extraction_sample_info : Dict[int, Dict[str,str]]):
def resolve_basenote_rate(self, extraction_sample_info : Optional[Dict[str,str]]):
assert len(self.notes_rates) != 0
# rate_3ds = None
@ -285,13 +285,9 @@ class AudioTableSample(AudioTableData):
final_rate,(final_note,) = rank_rates_notes(finalists)
if extraction_sample_info is not None:
if self.start in extraction_sample_info:
entry = extraction_sample_info[self.start]
if "SampleRate" in entry and "BaseNote" in entry:
final_rate = int(entry["SampleRate"])
final_note = entry["BaseNote"]
else:
print(f"WARNING: Missing extraction xml entry for sample at offset=0x{self.start:X}")
assert "SampleRate" in extraction_sample_info and "BaseNote" in extraction_sample_info
final_rate = int(extraction_sample_info["SampleRate"])
final_note = extraction_sample_info["BaseNote"]
# print(" ",len(FINAL_NOTES_RATES), FINAL_NOTES_RATES)
# if rate_3ds is not None and len(FINAL_NOTES_RATES) == 1:
@ -385,7 +381,8 @@ class AudioTableFile:
"""
def __init__(self, bank_num : int, audiotable_seg : memoryview, table_entry : AudioCodeTableEntry,
seg_offset : int, buffer_bug : bool = False, extraction_xml : Tuple[str, Element] = None):
seg_offset : int, buffer_bug : bool = False,
extraction_desc : Optional[SampleBankExtractionDescription] = None):
self.bank_num = bank_num
self.table_entry : AudioCodeTableEntry = table_entry
self.data = self.table_entry.data(audiotable_seg, seg_offset)
@ -393,24 +390,18 @@ class AudioTableFile:
self.samples_final = None
if extraction_xml is None:
if extraction_desc is None:
self.file_name = f"SampleBank_{self.bank_num}"
self.name = f"SampleBank_{self.bank_num}"
self.extraction_sample_info_versions = []
self.extraction_sample_info = None
self.extraction_blob_info = None
else:
self.file_name = extraction_xml[0]
self.name = extraction_xml[1].attrib["Name"]
self.extraction_sample_info = {}
self.extraction_blob_info = {}
for item in extraction_xml[1]:
if item.tag == "Sample":
self.extraction_sample_info[int(item.attrib["Offset"], 16)] = item.attrib
elif item.tag == "Blob":
self.extraction_blob_info[int(item.attrib["Offset"], 16)] = item.attrib
else:
assert False
self.file_name = extraction_desc.file_name
self.name = extraction_desc.name
self.extraction_sample_info_versions = extraction_desc.sample_info_versions
self.extraction_sample_info = extraction_desc.sample_info
self.extraction_blob_info = extraction_desc.blob_info
self.pointer_indices = []
@ -461,28 +452,24 @@ class AudioTableFile:
return self.samples[offset]
def sample_name(self, sample : AudioTableSample, index : int):
if self.extraction_sample_info is not None:
if sample.start in self.extraction_sample_info:
return self.extraction_sample_info[sample.start]["Name"]
print(f"WARNING: Missing extraction xml entry for sample at offset=0x{sample.start:X}")
if self.extraction_sample_info is not None and index < len(self.extraction_sample_info):
return self.extraction_sample_info[index]["Name"]
return f"SAMPLE_{self.bank_num}_{index}"
def sample_filename(self, sample : AudioTableSample, index : int):
ext = sample.codec_file_extension_compressed()
if self.extraction_sample_info is not None:
if sample.start in self.extraction_sample_info:
return self.extraction_sample_info[sample.start]["FileName"] + ext
print(f"WARNING: Missing extraction xml entry for sample at offset=0x{sample.start:X}")
if self.extraction_sample_info is not None and index < len(self.extraction_sample_info):
return self.extraction_sample_info[index]["FileName"] + ext
npad = int(math.floor(1 + math.log10(len(self.samples)))) if len(self.samples) != 0 else 0
return f"Sample{index:0{npad}}{ext}"
def blob_filename(self, start, end):
if self.extraction_blob_info is not None:
if start in self.extraction_blob_info:
return self.extraction_blob_info[start]["Name"]
print(f"WARNING: Missing extraction xml entry for blob at offset=0x{start:X}")
def blob_filename(self, start, end, index):
if self.extraction_blob_info is not None and index < len(self.extraction_blob_info):
return self.extraction_blob_info[index]["Name"]
return f"UNACCOUNTED_{start:X}_{end:X}"
def finalize_samples(self):
@ -490,7 +477,7 @@ class AudioTableFile:
for i,sample in enumerate(self.samples_final):
sample : AudioTableSample
sample.resolve_basenote_rate(self.extraction_sample_info)
sample.resolve_basenote_rate(self.extraction_sample_info[i] if self.extraction_sample_info is not None else None)
def finalize_coverage(self, all_sample_banks):
if len(self.coverage) != 0:
@ -577,6 +564,7 @@ class AudioTableFile:
def assign_names(self):
i = 0
j = 0
for sample in self.samples_final:
if isinstance(sample, AudioTableSample):
sample : AudioTableSample
@ -587,9 +575,10 @@ class AudioTableFile:
else:
sample : AudioTableData
name = self.blob_filename(sample.start, sample.end)
name = self.blob_filename(sample.start, sample.end, j)
sample.name = name
sample.filename = f"{name}.bin"
j += 1
def to_xml(self, base_path):
xml = XMLWriter()
@ -635,33 +624,36 @@ class AudioTableFile:
xml.write_comment("This file is only for extraction of vanilla data. For other purposes see assets/audio/samplebanks/")
start = {
xml.write_start_tag("SampleBank", {
"Name" : self.name,
"Index" : self.bank_num,
}
xml.write_start_tag("SampleBank", start)
})
# Write elements from the old xml version verbatim
i = 0
for sample in self.samples_final:
for entry_name,entry_attrs,in_version in self.extraction_sample_info_versions:
xml.write_element(entry_name, entry_attrs)
i += in_version
# Write any new elements
for sample in self.samples_final[i:]:
if isinstance(sample, AudioTableSample):
sample : AudioTableSample
xml.write_element("Sample", {
attrs = {
"Name" : sample.name,
"FileName" : sample.filename.replace(sample.codec_file_extension_compressed(), ""),
"Offset" : f"0x{sample.start:06X}",
"SampleRate" : sample.sample_rate,
"BaseNote" : sample.base_note,
})
i += 1
}
xml.write_element("Sample", attrs)
else:
sample : AudioTableData
xml.write_element("Blob", {
"Name" : sample.name,
"Offset" : f"0x{sample.start:06X}",
"Size" : f"0x{sample.end - sample.start:X}",
})
attrs = {
"Name" : sample.name,
}
xml.write_element("Blob", attrs)
xml.write_end_tag()