mirror of
https://github.com/KrisKennaway/ii-sound.git
synced 2024-06-25 22:29:29 +00:00
Add partial support for 4-cycle NOP; NOP opcode pairs
Count and report on the number of speaker toggles/sec during encoding
This commit is contained in:
parent
089591737f
commit
a1a1f33c21
|
@ -101,7 +101,8 @@ def frame_horizon(frame_offset: int, lookahead_steps: int):
|
|||
return frame_offset
|
||||
|
||||
|
||||
def audio_bytestream(data: numpy.ndarray, step: int, lookahead_steps: int):
|
||||
def audio_bytestream(data: numpy.ndarray, step: int, lookahead_steps: int,
|
||||
sample_rate: int):
|
||||
"""Computes optimal sequence of player opcodes to reproduce audio data."""
|
||||
|
||||
dlen = len(data)
|
||||
|
@ -117,6 +118,7 @@ def audio_bytestream(data: numpy.ndarray, step: int, lookahead_steps: int):
|
|||
position = 0.0
|
||||
voltage = -1.0
|
||||
|
||||
toggles = 0
|
||||
all_partial_positions = {}
|
||||
# Precompute partial_positions so we don't skew ETA during encoding.
|
||||
for i in range(2048):
|
||||
|
@ -178,6 +180,7 @@ def audio_bytestream(data: numpy.ndarray, step: int, lookahead_steps: int):
|
|||
opcode = candidate_opcodes[opcode_idx][0]
|
||||
opcode_length = opcodes.cycle_length(opcode)
|
||||
opcode_counts[opcode] += 1
|
||||
toggles += opcodes.TOGGLES[opcode]
|
||||
|
||||
# Apply this opcode to evolve the speaker position
|
||||
delta_powers, partial_positions, last_voltage = \
|
||||
|
@ -200,6 +203,8 @@ def audio_bytestream(data: numpy.ndarray, step: int, lookahead_steps: int):
|
|||
yield opcodes.Opcode.EXIT
|
||||
eta.done()
|
||||
print("Total error %f" % total_err)
|
||||
toggles_per_sec = toggles / dlen * sample_rate
|
||||
print("%d speaker toggles/sec" % toggles_per_sec)
|
||||
|
||||
print("Opcodes used:")
|
||||
for v, k in sorted(list(opcode_counts.items()), key=lambda kv: kv[1],
|
||||
|
@ -240,7 +245,8 @@ def main(argv):
|
|||
|
||||
with open(out, "wb+") as f:
|
||||
for opcode in audio_bytestream(
|
||||
preprocess(serve_file, sample_rate), step, lookahead_steps):
|
||||
preprocess(serve_file, sample_rate), step, lookahead_steps,
|
||||
sample_rate):
|
||||
f.write(bytes([opcode.value]))
|
||||
|
||||
|
||||
|
|
|
@ -12,19 +12,20 @@ class Opcode(enum.Enum):
|
|||
INC = 5
|
||||
INCX = 6
|
||||
JMP_INDIRECT = 7
|
||||
NOPNOP = 8
|
||||
|
||||
|
||||
# Byte length of opcodes
|
||||
OPCODE_LEN = {
|
||||
Opcode.NOP: 1, Opcode.NOP3: 2, Opcode.STA: 3, Opcode.STAX: 3,
|
||||
Opcode.INC: 3, Opcode.INCX: 3, Opcode.JMP_INDIRECT: 3
|
||||
Opcode.INC: 3, Opcode.INCX: 3, Opcode.JMP_INDIRECT: 3, Opcode.NOPNOP: 2
|
||||
}
|
||||
|
||||
ASM = {
|
||||
Opcode.NOP: "NOP", Opcode.NOP3: "STA zpdummy",
|
||||
Opcode.STA: "STA $C030", Opcode.STAX: "STA $C030,X",
|
||||
Opcode.INC: "INC $C030", Opcode.INCX: "INC $C030,X",
|
||||
Opcode.JMP_INDIRECT: "JMP (WDATA)"
|
||||
Opcode.JMP_INDIRECT: "JMP (WDATA)", Opcode.NOPNOP: "NOP NOP"
|
||||
}
|
||||
|
||||
# Applied speaker voltages resulting from executing opcode
|
||||
|
@ -37,19 +38,24 @@ VOLTAGES = {
|
|||
Opcode.NOP3: [1, 1, 1],
|
||||
# TODO: support 6502 cycle counts as well
|
||||
Opcode.JMP_INDIRECT: [1, 1, 1, 1, 1, 1],
|
||||
Opcode.NOPNOP: [1, 1, 1, 1],
|
||||
}
|
||||
|
||||
|
||||
def voltage_sequence(
|
||||
opcodes: Iterable[Opcode], starting_voltage=1.0) -> numpy.ndarray:
|
||||
opcodes: Iterable[Opcode], starting_voltage=1.0
|
||||
) -> Tuple[numpy.float32, int]:
|
||||
"""Voltage sequence for sequence of opcodes."""
|
||||
out = []
|
||||
v = starting_voltage
|
||||
toggles = 0
|
||||
for op in opcodes:
|
||||
for nv in v * numpy.array(VOLTAGES[op]):
|
||||
if v != nv:
|
||||
toggles += 1
|
||||
v = nv
|
||||
out.append(v)
|
||||
return numpy.array(out, dtype=numpy.float64)
|
||||
return tuple(numpy.array(out, dtype=numpy.float32)), toggles
|
||||
|
||||
|
||||
def all_opcodes(
|
||||
|
@ -72,27 +78,28 @@ def all_opcodes(
|
|||
|
||||
|
||||
def generate_player(player_ops: List[Tuple[Opcode]], opcode_filename: str,
|
||||
player_filename: str, truncate_to_page=True):
|
||||
player_filename: str):
|
||||
num_bytes = 0
|
||||
seqs = {}
|
||||
num_op = 0
|
||||
offset = 0
|
||||
unique_opcodes = {}
|
||||
toggles = {}
|
||||
done = False
|
||||
with open(player_filename, "w+") as f:
|
||||
for i, k in enumerate(player_ops):
|
||||
is_unique = False
|
||||
new_unique = []
|
||||
player_op = []
|
||||
player_op_len = 0
|
||||
|
||||
new_offset = offset
|
||||
for j, o in enumerate(k):
|
||||
seq = tuple(voltage_sequence(k[j:], 1.0))
|
||||
seq, tog = voltage_sequence(k[j:], 1.0)
|
||||
dup = seqs.setdefault(seq, i)
|
||||
if dup == i:
|
||||
player_op.append("tick_%02x: ; voltages %s" % (
|
||||
new_offset, seq))
|
||||
is_unique = True
|
||||
unique_opcodes[new_offset] = seq
|
||||
new_unique.append((new_offset, seq, tog))
|
||||
player_op.append(" %s" % ASM[o])
|
||||
player_op_len += OPCODE_LEN[o]
|
||||
new_offset += OPCODE_LEN[o]
|
||||
|
@ -100,13 +107,17 @@ def generate_player(player_ops: List[Tuple[Opcode]], opcode_filename: str,
|
|||
|
||||
# If at least one of the partial opcode sequences was not
|
||||
# a dup, then add it to the player
|
||||
if is_unique:
|
||||
if (num_bytes + player_op_len) >= 252:
|
||||
if new_unique:
|
||||
# Reserve 9 bytes for EXIT and SLOWPATH
|
||||
if (num_bytes + player_op_len) > (256 - 9):
|
||||
print("Out of space, truncating.")
|
||||
break
|
||||
num_op += 1
|
||||
f.write("\n".join(player_op))
|
||||
num_bytes += player_op_len
|
||||
for op_offset, seq, tog in new_unique:
|
||||
unique_opcodes[op_offset] = seq
|
||||
toggles[op_offset] = tog
|
||||
offset = new_offset
|
||||
|
||||
f.write("; %d bytes\n" % num_bytes)
|
||||
|
@ -126,6 +137,13 @@ def generate_player(player_ops: List[Tuple[Opcode]], opcode_filename: str,
|
|||
"\n" % (o, v))
|
||||
f.write("}\n")
|
||||
|
||||
f.write("\n\nTOGGLES = {\n")
|
||||
for o, v in toggles.items():
|
||||
f.write(
|
||||
" Opcode.TICK_%02x: %d,\n" % (o, v)
|
||||
)
|
||||
f.write("}\n")
|
||||
|
||||
|
||||
def all_opcode_combinations(
|
||||
max_cycles: int, opcodes: List[Opcode], start_opcodes: List[Opcode]
|
||||
|
@ -151,6 +169,7 @@ if __name__ == "__main__":
|
|||
opcodes=[
|
||||
Opcode.NOP,
|
||||
Opcode.NOP3,
|
||||
# Opcode.NOPNOP,
|
||||
Opcode.STA,
|
||||
# Opcode.INC,
|
||||
# Opcode.INCX
|
||||
|
|
Loading…
Reference in New Issue
Block a user