v0.53.0: Kaleidostrobe, the 2002 iTunes visualizer, as the fourth style
Reverse-engineered from its PowerPC binary and checked against the original running under emulation: textures byte for byte and every triangle, over 3,600 frames. 25 fps like the plugin; its colour maps and wave images are extracted from its own code; tests/kaleidostrobe_golden.npz was recorded from the original.
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
"""Drive the emulated plugin through VisualPluginHandler like iTunes did."""
|
||||
import struct, sys
|
||||
sys.path.insert(0, __file__.rsplit('/', 1)[0])
|
||||
from emu import KS
|
||||
|
||||
def fourcc(s): return struct.unpack('>I', s.encode())[0]
|
||||
|
||||
HANDLER = '_VisualPluginHandler'
|
||||
|
||||
|
||||
class Plugin:
|
||||
def __init__(self, seed=1, rect=(0, 0, 480, 640), ticks=1000):
|
||||
k = self.k = KS(seed)
|
||||
k.ticks = ticks
|
||||
k.port_rect = rect
|
||||
self.msg = k.alloc(0x100)
|
||||
self.port = k.alloc(0x100)
|
||||
self.render = k.alloc(0x802)
|
||||
# init
|
||||
k.call(HANDLER, fourcc('init'), self.msg, 0)
|
||||
self.pd = k.r32(self.msg + 0x18)
|
||||
# show: port, rect, options
|
||||
k.w32(self.msg, self.port)
|
||||
k.u.mem_write(self.msg + 4, struct.pack('>4h', *rect))
|
||||
k.w32(self.msg + 0xc, 0)
|
||||
k.call(HANDLER, fourcc('show'), self.msg, self.pd)
|
||||
# play
|
||||
k.w32(self.msg, 0); k.w32(self.msg + 4, 0)
|
||||
k.call(HANDLER, fourcc('vply'), self.msg, self.pd)
|
||||
|
||||
def frame(self, wave=None, spec=None, dt_us=40001, dticks=None):
|
||||
"""One 'vrnd'. wave/spec: 2x512 uint8 lists (or None for silence)."""
|
||||
k = self.k
|
||||
k.micro += dt_us
|
||||
k.ticks += dticks if dticks is not None else round(dt_us * 60 / 1e6)
|
||||
data = bytearray(0x802)
|
||||
data[0] = 2
|
||||
data[0x401] = 2
|
||||
if wave is not None:
|
||||
data[1:0x401] = bytes(wave[0]) + bytes(wave[1])
|
||||
else:
|
||||
data[1:0x401] = bytes([128]) * 1024
|
||||
if spec is not None:
|
||||
data[0x402:0x802] = bytes(spec[0]) + bytes(spec[1])
|
||||
k.u.mem_write(self.render, bytes(data))
|
||||
k.w32(self.msg, self.render)
|
||||
k.w32(self.msg + 4, 0)
|
||||
start = len(k.gl)
|
||||
k.call(HANDLER, fourcc('vrnd'), self.msg, self.pd)
|
||||
return k.gl[start:]
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import random
|
||||
p = Plugin()
|
||||
print('pd', hex(p.pd), 'gl calls during show:', len(p.k.gl))
|
||||
for name, args in p.k.gl[:40]:
|
||||
print(' ', name, args)
|
||||
rnd = random.Random(0)
|
||||
for i in range(3):
|
||||
wave = [[rnd.randrange(64, 192) for _ in range(512)] for _ in range(2)]
|
||||
spec = [[rnd.randrange(0, 200) for _ in range(512)] for _ in range(2)]
|
||||
calls = p.frame(wave, spec)
|
||||
print('frame', i, len(calls))
|
||||
from collections import Counter
|
||||
print(Counter(n for n, a in calls))
|
||||
for c in calls[:60]:
|
||||
print(' ', c)
|
||||
Reference in New Issue
Block a user