1
0
Fork 0
mirror of https://github.com/zeldaret/oot.git synced 2025-07-17 21:35:11 +00:00

[Audio 2/?] Extract audio samples to wav (#2020)

* [Audio 2/?] Extract audio samples to wav

Co-authored-by: zelda2774 <69368340+zelda2774@users.noreply.github.com>

* How

* Hopefully fix warning I don't get locally

* Pad default sample filenames, comment on the vadpcm frame encoder functions, other suggested changes

* Small tweaks to above

* Remove some obsolete code

---------

Co-authored-by: zelda2774 <69368340+zelda2774@users.noreply.github.com>
This commit is contained in:
Tharo 2024-08-09 03:39:18 +01:00 committed by GitHub
parent 1021c482af
commit ef329e633a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 4317 additions and 112 deletions

View file

@ -4,17 +4,18 @@
# Extract audio files
#
import os
import os, shutil, time
from dataclasses import dataclass
from enum import auto, Enum
from multiprocessing.pool import ThreadPool
from typing import Dict, List, Tuple, Union
from xml.etree import ElementTree
from xml.etree.ElementTree import Element
from .audio_tables import AudioCodeTable, AudioCodeTableEntry, AudioStorageMedium
from .audiotable import AudioTableFile
from .audiotable import AudioTableData, AudioTableFile, AudioTableSample
from .audiobank_file import AudiobankFile
from .util import align, debugm, error, incbin
from .util import align, debugm, error, incbin, program_get
class MMLVersion(Enum):
OOT = auto()
@ -114,13 +115,34 @@ def collect_soundfonts(audiobank_seg : memoryview, extracted_dir : str, version_
return soundfonts
def extract_samplebank(extracted_dir : str, sample_banks : List[Union[AudioTableFile, int]], bank : AudioTableFile,
write_xml : bool):
def aifc_extract_one_sample(base_path : str, sample : AudioTableSample):
aifc_path = f"{base_path}/aifc/{sample.filename}"
ext_compressed = sample.codec_file_extension_compressed()
ext_decompressed = sample.codec_file_extension_decompressed()
wav_path = f"{base_path}/{sample.filename.replace(ext_compressed, ext_decompressed)}"
# export to AIFC
sample.to_file(aifc_path)
# decode to AIFF/WAV
program_get(f"{SAMPLECONV_PATH} --matching pcm16 {aifc_path} {wav_path}")
def aifc_extract_one_bin(base_path : str, sample : AudioTableData):
# export to BIN
if BASEROM_DEBUG:
sample.to_file(f"{base_path}/aifc/{sample.filename}")
# copy to correct location
shutil.copyfile(f"{base_path}/aifc/{sample.filename}", f"{base_path}/{sample.filename}")
else:
sample.to_file(f"{base_path}/{sample.filename}")
def extract_samplebank(pool : ThreadPool, extracted_dir : str, sample_banks : List[Union[AudioTableFile, int]],
bank : AudioTableFile, write_xml : bool):
# deal with remaining gaps, have to blob them unless we can find an exact match in another bank
bank.finalize_coverage(sample_banks)
# assign names
bank.assign_names()
base_path = f"{extracted_dir}/assets/audio/samples/{bank.name}"
# write xml
with open(f"{extracted_dir}/assets/audio/samplebanks/{bank.file_name}.xml", "w") as outfile:
outfile.write(bank.to_xml(f"assets/audio/samples/{bank.name}"))
@ -129,6 +151,31 @@ def extract_samplebank(extracted_dir : str, sample_banks : List[Union[AudioTable
if write_xml:
bank.write_extraction_xml(f"assets/xml/audio/samplebanks/{bank.file_name}.xml")
# write sample sand blobs
os.makedirs(f"{base_path}/aifc", exist_ok=True)
aifc_samples = [sample for sample in bank.samples_final if isinstance(sample, AudioTableSample)]
bin_samples = [sample for sample in bank.samples_final if not isinstance(sample, AudioTableSample)]
t_start = time.time()
# we assume the number of bin samples are very small and don't multiprocess it
for sample in bin_samples:
aifc_extract_one_bin(base_path, sample)
# multiprocess aifc extraction + decompression
async_results = [pool.apply_async(aifc_extract_one_sample, args=(base_path, sample)) for sample in aifc_samples]
# block until done
[res.get() for res in async_results]
dt = time.time() - t_start
print(f"Samplebank {bank.name} extraction took {dt:.3f}s")
# drop aifc dir if not in debug mode
if not BASEROM_DEBUG:
shutil.rmtree(f"{base_path}/aifc")
def extract_audio_for_version(version_info : GameVersionInfo, extracted_dir : str, read_xml : bool, write_xml : bool):
print("Setting up...")
@ -234,13 +281,17 @@ def extract_audio_for_version(version_info : GameVersionInfo, extracted_dir : st
print("Extracting samplebanks...")
# Check that the sampleconv binary is available
assert os.path.isfile(SAMPLECONV_PATH) , "Compile sampleconv"
os.makedirs(f"{extracted_dir}/assets/audio/samplebanks", exist_ok=True)
if write_xml:
os.makedirs(f"assets/xml/audio/samplebanks", exist_ok=True)
for bank in sample_banks:
if isinstance(bank, AudioTableFile):
extract_samplebank(extracted_dir, sample_banks, bank, write_xml)
with ThreadPool(processes=os.cpu_count()) as pool:
for bank in sample_banks:
if isinstance(bank, AudioTableFile):
extract_samplebank(pool, extracted_dir, sample_banks, bank, write_xml)
# ==================================================================================================================
# Extract soundfonts