muvid.choreo.scene¶
The scene compiler: events + sections -> a list of drawable OBJECTS.
An Obj is one thing on screen with a life: born at t_born, dead at
t_die, a shape kind, a position, a size, a colour and a motion that
says how it moves in between. Every number is computed here, in Python, from
the event list and the treatment; the renderer only draws.
Each archetype is a function (events, section, ...) -> [Obj] in a
registry (ARCHETYPE_FNS), the same seam muvid.lyricvid.scene uses:
adding a look is adding a function and a vocabulary entry. The four shipped
ones are the closed set in muvid.choreo.spec.ARCHETYPES.
Determinism is a design rule, not a hope. The only “randomness” is
unit_hash(), a closed-form integer hash of (seed, index, ...) — so
the same audio and seed produce the same objects on any machine, a re-render
after a palette change moves nothing, and a test can pin a coordinate.
Positions are normalised (x in fractions of width, y of height, origin
top-left) and sizes are fractions of canvas height, so one scene renders at
any resolution. Velocities are in the same fractions per second.
Module Attributes
Shape classes the renderer knows how to draw. |
|
How an object moves between birth and death. |
Functions
|
Every object and backdrop for the whole song. |
|
Register an archetype under |
|
A float in |
Classes
|
What is behind the objects during |
|
Output geometry. |
|
Everything the renderer needs, and nothing it has to interpret. |
|
One drawable thing with a life. |
- class muvid.choreo.scene.Backdrop(*, start, end, kind, top, bottom)[source]¶
Bases:
objectWhat is behind the objects during
[start, end).
- class muvid.choreo.scene.Canvas(*, width=1920, height=1080, fps=30)[source]¶
Bases:
objectOutput geometry.
- class muvid.choreo.scene.ChoreoScene(*, canvas, duration, backdrops, objects, meta=<factory>)[source]¶
Bases:
objectEverything the renderer needs, and nothing it has to interpret.
- muvid.choreo.scene.KINDS = ('circle', 'rect', 'triangle', 'line', 'diamond', 'ring')¶
Shape classes the renderer knows how to draw.
- muvid.choreo.scene.MOTIONS = ('hold', 'scroll', 'pulse', 'drift', 'flicker')¶
How an object moves between birth and death.
- class muvid.choreo.scene.Obj(*, t_born, t_die, kind, x, y, size, colour, motion='hold', band='', strength=1.0, aspect=1.0, angle=0.0, vx=0.0, vy=0.0, attack_s=0.0, release_s=0.0, layer=0, seed=0)[source]¶
Bases:
objectOne drawable thing with a life.
- Parameters:
y (
float) – centre, normalised (x of width, y of height), at birth.size (
float) – the shape’s main dimension as a fraction of canvas height — diameter forcircle/ring/diamond, height forrect/triangle, length forline.aspect (
float) – width/height forrect/triangle; length/thickness forline.angle (
float) – degrees, forline.vy (
float) – velocity (fractions per second) forscroll/drift.release_s (
float) – alpha ramps at the start and end of life.seed (
int) – per-object key forflicker’s per-frame jitter.
- muvid.choreo.scene.compile_scene(treatment, analysis, *, canvas=None, seed=0)[source]¶
Every object and backdrop for the whole song. Deterministic given
seed.- Return type:
>>> from muvid.choreo.analysis import Analysis, Event, Section, Tempo >>> a = Analysis(duration=4.0, tempo=Tempo(bpm=120, beats=(0, .5, 1, 1.5), source='numpy'), ... events=(Event(t=1.0, band='low', strength=0.9), ... Event(t=2.0, band='high', strength=0.5)), ... sections=(Section(index=0, label='mid', start=0.0, end=4.0, energy_db=-10),)) >>> s = compile_scene(spec_mod.default_treatment('fischinger'), a, seed=1) >>> [(o.t_born, o.kind, o.band) for o in s.objects] [(1.0, 'circle', 'low'), (2.0, 'triangle', 'high')] >>> compile_scene(spec_mod.default_treatment('fischinger'), a, seed=1) == s True
- muvid.choreo.scene.register_archetype(name)[source]¶
Register an archetype under
name.>>> @register_archetype('doctest-demo') ... def _demo(events, **kw): return [] >>> 'doctest-demo' in ARCHETYPE_FNS True >>> del ARCHETYPE_FNS['doctest-demo']
- muvid.choreo.scene.unit_hash(seed, *keys)[source]¶
A float in
[0, 1)that is a pure function of its integer arguments.splitmix64-style mixing; no state, no platform dependence, so it is the only “random” this package allows itself.
- Return type:
>>> unit_hash(0, 1) == unit_hash(0, 1), 0.0 <= unit_hash(3, 4, 5) < 1.0 (True, True) >>> unit_hash(0, 1) != unit_hash(1, 1) True