Skip to content

Palinopsia v1.2.5 : data and rhythm

Choose a tag to compare

@github-actions github-actions released this 10 Oct 07:16
· 256 commits to master since this release

Palinopsia v1.2.5 : data and rhythm.

A release about marks, steps and the picture read as data. Lowercase is a new source
of machine-cut marks that land on a step and hold, and Signal is a new Sonify voice
that walks the picture with read heads and fires a micro-grain at every bright cell it
crosses : point the one at the other and the marks play themselves. Every layer gains a
Vary of its own that a modulator can fire, a layer's racks can be re-dealt one at a
time, and everything you fire now blinks so you can see it act.

Underneath, the instrument stopped paying for something it should never have paid for.
It reads its own picture back every frame (for the vision features, Sonify's taps and
the depth input), and on a computer whose graphics chip shares memory with the processor
each of those reads stopped the whole pipeline for about 17 ms. Opening the Performance
tab turned one of them on, and the show then spent up to two thirds of every second
waiting inside the graphics driver, with nothing in the app to show it. The reads are queued now
and collected a frame or two later, and the picture never waits for them. The picture itself is
untouched : the same passes, the same formats, the same resolution. (The light output and the
/opsia/av/mark-signal strip still read synchronously : both are off unless you use them.)

Highlights

  • Lowercase, a new source. Eight figures of machine-cut marks : a data matrix whose
    cells subdivide into one to three narrow bars, blocks, bars, ticks, a scan rule, a
    lattice, sprocket ladders down the edges, and one white slab with bars cut out of it.
    Everything lands on a step and holds; nothing sweeps. Every deal is a fresh draw, or,
    with scroll, the same one slid a cell along : the field read as a stream of data. It pulses on its own, up to 120
    steps a second, or plays any rhythm you bind to its gate (a Euclid, a Spastic, an
    audio transient). cells runs to 512 : the grids turn to crisp 1-bit static and the
    slab becomes a barcode. A sidechain reads another layer one sample per cell, so a
    film arrives as a 1-bit halftone of cells on this clock. One accent color marks a
    regular lattice of cells, and the previous step trails behind as a filmed projection
    does.
  • Signal, a new Sonify voice, before Chord. Read heads walk the picture on a clock and
    every cell they cross that is brighter than a threshold fires one micro-grain : pip,
    ping, damped, noise, click, fm, square, tri or pink, from a 0.2 ms tick to a 500 ms
    ring. Nothing here is a stored rhythm; the picture is the pattern. One to four heads
    each read their own band, and spread pulls their sweeps out of step into a
    polymeter that drifts back. Pitch comes from the place along the bar, with a snap
    from free frequency to the scale whose middle is microtonal. Every grain lands on its
    exact sample rather than on the audio block, a fixed moment after its step, which is
    what keeps a fast grid a grid; synced, it runs on the beat clock the Sonify sequencers
    share.
  • A Vary for each layer, under its Speed : the global Vary aimed at one layer alone,
    with its own amount. Its button is a modulation target that fires as the modulator
    crosses its middle, so a pattern can re-vary a layer; an auto-fire stays out of undo.
    Each fire is a variant around where the layer stood, so a modulator can fire it all
    night without the layer wandering off.
  • Re-deal one rack from a layer's right-click menu : the effects of source A, of
    source B, or the layer's own, leaving the sources and the other racks alone. Dealt
    racks also reach four effects now (they stopped at two, and a source rack came out
    empty about half the time).
  • You see what fires. Every Vary, the Randomize button and the return-to-default
    buttons blink when they act, whether by a click, a modulator, a MIDI pad or a key, each
    in the color of its kind of action.
  • The picture readout is free. Measured on an 8 GB Apple-silicon Mac Mini, a blank
    1920x1080 session, a 50 Hz display, the Performance tab open, three runs each : 41.9
    to 50.0 fps before, a steady 50.0 after.
    The app's own meter reads 1.4 ms of
    processor time per frame instead of 14.2
    , and the worst single frame 5.5 ms instead
    of 42.8
    . Blocking readback time fell from 560 ms per second to 0.1.
  • The frame rate reads against your display : 50 fps / 50 Hz in the Performance
    tab and in the corner of the preview. The render is locked to the display's refresh
    rate, so that rate is the ceiling, not 60, and 24 fps on a display set to 24 Hz is
    every frame drawn. The red warning and the frame budget follow the display too, frames
    the GPU backlog guard drops are counted as skips, and the GPU and VRAM meters
    work on a Mac
    .
  • Esc closes a fullscreen output. A plain fullscreen output on one display could
    only be left with the window shortcut (Cmd+W, Ctrl+W); a tap of Esc now closes it. On
    a projector the control window is not behind, Esc hands focus back to the controls
    instead and the show goes on, and an installation keeps its 1.5 s hold, so a brushed
    key never ends a show. And on a Mac, screen capture says how to
    grant its permission
    instead of showing a grid of black thumbnails.

Install

Platform File
Windows Palinopsia-1.2.5-win-x64.exe (installer) or Palinopsia-1.2.5-portable.exe
macOS (Apple Silicon) Palinopsia-1.2.5-mac-arm64.dmg
Linux Palinopsia-1.2.5-linux-x86_64.AppImage or Palinopsia-1.2.5-linux-amd64.deb

Builds are unsigned :

  • Windows : SmartScreen may warn, click More info → Run anyway.
  • macOS (Apple Silicon) : not notarized. Drag Palinopsia into Applications first
    (run from the disk image or Downloads, macOS runs a hidden temporary copy), then run
    xattr -cr /Applications/Palinopsia.app once in Terminal, or open it and click Open
    Anyway
    in System Settings → Privacy & Security. The first launch asks for the local
    network, and for the camera and the microphone when you use them : accept, or OSC, NDI
    and light output stay silent. Screen capture is the one permission macOS never prompts
    for : switch Palinopsia on under Privacy & Security → Screen & System Audio Recording,
    then start it again.
  • Linux : chmod +x the AppImage, then run it.

Sessions saved with v1.2.0 open and play unchanged : a layer without a Vary amount reads
the default, and a Sonify patch without Signal reads it as off.

Intel Macs (x64) : this release ships Apple Silicon only. On an Intel Mac, build from
source (npm install, then npm run build:mac).

For developers

  • Sonify voices are stored by index. A session saves each voice's mixer channel, DJ
    filter and sequencer mask by position, and a MIDI on/off binding is
    sonify:voice:i. Signal is therefore APPENDED as voice 9 (NVOICE 10, a ten-entry
    mixFilter, older nine-entry arrays padded on load), and VOICE_ORDER in
    SonifyPage.tsx only shows it before Chord. A new voice goes at the end, always.
  • Adding a Sonify voice : the two steps that are easy to miss. Claim its tap in the
    tick's need[] : a voice missing there runs only while ANOTHER voice reads the same
    tap, which a test hides whenever Spectra is on (it is by default, on tap 0). And
    nothing in setSonify : it now merges every voice block with its default, so a
    session saved before a voice existed loads with that voice off instead of crashing
    the Sonify page on a missing block.
  • Signal's scan runs on the main thread, so it reads sonifyModValues itself : the
    simple-mod overlay ships modulated values to the worklet only, and a scan reading its
    base config would ignore every modulator. Onsets carry a per-grain sample delay into
    their render block (wait) instead of firing at the block's start.
  • pushSideTex (Compositor) is the counterpart of pushAudioTex : any ISF source
    that declares sideTex receives its slot's sidechain, keyed on the declaration, so a
    new source that reads a layer needs no registration. No layer picked unfeeds the
    input, which then reads the bridge's transparent texel : weigh a sidechain by its
    alpha and "none" means none.
  • A modulatable trigger : the layer vary target is an EVENT, not a value. The
    modulation path folds every driver of a layer into one value a frame (the highest),
    and flushVaryEdges tests it once : one fire per upward crossing of the middle,
    re-armed below 0.4, never twice inside 60 ms, and none on the first frame a binding
    is seen. It hands the fire out through setVaryTrigger, which App runs inside
    runSilently; runSilently commits a pending user edit first.
  • <Blink> (components/Blink.tsx) shows a fired action : given a count it blinks
    when the count changes (actions a modulator, a pad or a key can fire : varyPulse,
    randPulse, transient store state outside the composition) and only for a change since
    it mounted, and without one it blinks on its own button's click, drawn over the page
    so it outlives a button that disables or hides itself.
  • The three rack-size distributions are one constant each in randomize.ts
    (SOURCE_A_RACK, SOURCE_B_RACK, LAYER_RACK) : they used to be nine copies across
    every randomizer.
  • src/renderer/src/engine/gridReadback.ts is the small-grid counterpart to
    frameCapture.ts : pixel-pack buffers, fenceSync plus a zero-timeout
    clientWaitSync, the newest finished grid wins, and kick() skips rather than waits
    when every slot is in flight. It flushes only when the window is NOT painting : a
    painting window submits the work itself, and a submit into a full command queue blocks.
    A ring hands each landed grid out ONCE : two consumers of one source share a read
    (readSonifyGrid keeps each source's grid for the frame), and dropInFlight() is for a
    consumer back from a pause.
  • The cost of a synchronous readback on a shared-memory GPU is the pipeline drain, not
    the bytes : 1x1, 32x32 and 256x256 all measured the same. Size is not the knob.
  • PerfStats carries hz, the refresh rate of the window's display, and display:hz
    returns just that rate. Rates print through formatHz() (60, not 60.00; real
    fractional rates such as 59.94 keep their decimals).
  • kiosk:active reports whether THIS session runs as an installation; kiosk:config
    reports the stored setting, which stays on after an exit.

Full details in CHANGELOG.md.