summaryrefslogtreecommitdiff
path: root/pokemontools/audio.py
diff options
context:
space:
mode:
Diffstat (limited to 'pokemontools/audio.py')
-rw-r--r--pokemontools/audio.py83
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()