diff options
Diffstat (limited to 'pokemontools/audio.py')
-rw-r--r-- | pokemontools/audio.py | 83 |
1 files changed, 62 insertions, 21 deletions
diff --git a/pokemontools/audio.py b/pokemontools/audio.py index 38fd65f..0cdfcbc 100644 --- a/pokemontools/audio.py +++ b/pokemontools/audio.py @@ -5,14 +5,13 @@ import os from math import ceil from gbz80disasm import get_global_address, get_local_address - -import crystal +from labels import line_has_label from crystal import music_classes as sound_classes - from crystal import ( Command, SingleByteParam, MultiByteParam, + PointerLabelParam, load_rom, ) @@ -23,10 +22,34 @@ import configuration conf = configuration.Config() +def is_comment(asm): + return asm.startswith(';') + +def asm_sort(asm_def): + """ + Sort key for asm lists. + + Usage: + list.sort(key=asm_sort) + sorted(list, key=asm_sort) + """ + address, asm, last_address = asm_def + return ( + address, + last_address, + not is_comment(asm), + not line_has_label(asm), + asm + ) + def sort_asms(asms): - """sort and remove duplicates from a list of tuples - format (address, asm, last_address)""" - return sorted(set(asms), key=lambda (x,y,z):(x,z,not y.startswith(';'), ':' not in y)) + """ + Sort and remove duplicates from an asm list. + + Format: [(address, asm, last_address), ...] + """ + return sorted(set(asms), key=asm_sort) + class NybbleParam: size = 0.5 @@ -43,15 +66,18 @@ class NybbleParam: def parse(self): self.nybble = (rom[self.address] >> {'lo': 0, 'hi': 4}[self.which]) & 0xf -class HiNybbleParam(NybbleParam): - which = 'hi' def to_asm(self): return '%d' % self.nybble + @staticmethod + def from_asm(value): + return value + +class HiNybbleParam(NybbleParam): + which = 'hi' + class LoNybbleParam(NybbleParam): which = 'lo' - def to_asm(self): - return '%d' % self.nybble class PitchParam(HiNybbleParam): def to_asm(self): @@ -66,14 +92,23 @@ class PitchParam(HiNybbleParam): pitch += '_' return pitch +class NoteDurationParam(LoNybbleParam): + def to_asm(self): + self.nybble += 1 + return LoNybbleParam.to_asm(self) + + @staticmethod + def from_asm(value): + value = str(int(value) - 1) + return LoNybbleParam.from_asm(value) class Note(Command): macro_name = "note" - size = 1 + size = 0 end = False param_types = { 0: {"name": "pitch", "class": PitchParam}, - 1: {"name": "duration", "class": LoNybbleParam}, + 1: {"name": "duration", "class": NoteDurationParam}, } allowed_lengths = [2] override_byte_check = True @@ -83,6 +118,7 @@ class Note(Command): self.params = [] byte = rom[self.address] current_address = self.address + size = 0 for (key, param_type) in self.param_types.items(): name = param_type["name"] class_ = param_type["class"] @@ -92,12 +128,20 @@ class Note(Command): self.params += [obj] current_address += obj.size + size += obj.size + + # can't fit bytes into nybbles + if obj.size > 0.5: + if current_address % 1: + current_address = int(ceil(current_address)) + if size % 1: + size = int(ceil(size)) self.params = dict(enumerate(self.params)) - # obj sizes were 0.5, but were working with ints + # obj sizes were 0.5, but we're working with ints current_address = int(ceil(current_address)) - self.size = int(ceil(self.size)) + self.size += int(ceil(size)) self.last_address = current_address return True @@ -105,7 +149,6 @@ class Note(Command): class Noise(Note): macro_name = "noise" - size = 0 end = False param_types = { 0: {"name": "duration", "class": LoNybbleParam}, @@ -155,7 +198,7 @@ class Channel: # label any jumps or calls for key, param in class_.param_types.items(): - if param['class'] == crystal.PointerLabelParam: + if param['class'] == PointerLabelParam: label_address = class_.params[key].parsed_address label = '%s_branch_%x' % ( self.base_label, @@ -199,10 +242,10 @@ class Channel: output = sort_asms(self.output + self.labels) text = '' for i, (address, asm, last_address) in enumerate(output): - if ':' in asm: + if line_has_label(asm): # dont print labels for empty chunks for (address_, asm_, last_address_) in output[i:]: - if ':' not in asm_: + if not line_has_label(asm_): text += '\n' + asm + '\n' break else: @@ -214,7 +257,7 @@ class Channel: for class_ in sound_classes: if class_.id == i: return class_ - if self.channel in [4, 8]: return Noise + if self.channel == 8: return Noise return Note @@ -320,7 +363,6 @@ def dump_sounds(origin, names, base_label='Sound_'): sound_at = read_bank_address_pointer(origin + i * 3) sound = Sound(sound_at, base_label + name) output = sound.to_asm(labels) + '\n' - # incbin trailing commands that didnt get picked up index = addresses.index((sound.start_address, sound.last_address)) if index + 1 < len(addresses): @@ -386,5 +428,4 @@ def generate_crystal_cry_pointers(): if __name__ == '__main__': dump_crystal_music() - dump_crystal_sfx() |