Repository navigation
v0.17.2
Upgrade note: a lighting show with an invalid color: or level value now fails to parse instead
of silently dropping it (#538). Run validate_lighting over your shows before upgrading.
Fixed
- Quieter lint on padding cues and beat-rounded endings (#541):
replace-overlapno longer
reports a later effect that restates the one it overlaps -- the same effect with the same
parameters, not fading in -- since nothing changes on stage; padding and anchor cues were most of
that warning's output on real shows.past-end-of-songnow lets an effect run up to one engine
frame (25ms) past the audio before reporting it, so a last effect sized in beats that ends a few
milliseconds late is not flagged. - An invalid color is a parse error, not silently dropped (#538): a
color:value that did
not parse was discarded without a word -- astaticset no colour and rendered as if it were not
there, acycleran on its other colours -- andvalidate_lightingreported the show clean. It
is now a parse error naming the value. The same goes for a level that is not a number or a
percentage (dimmer/intensityand colour channels onstatic,pulselevels,dimmerstart
and end,rainbowsaturation and brightness), which used to leave the effect at its default.
Every effect parse error now also names its line. - An effect lasts to its own end, not a microsecond short (#536): an effect was retired once
it was within a microsecond of its end, so one ending on the downbeat the next cue starts on left
the rig dark for that microsecond, andevaluate_showreported dark there while the effect's own
duration said it was still running. Effects now end exactly at their end. - Back-to-back steps meet exactly; no nanosecond dark windows (#534): an effect lasting to
the next downbeat ended at its rounded start plus its rounded length, a nanosecond off the next
cue, and looped sequences crept a nanosecond later every pass.analyze_showreported each gap
as a dark window -- 37 in Battle Scarred, about 200 across the Isenmor set -- burying any real
one. Beat durations are now positions, like measure durations, snapped to a millionth of a beat
so the end lands exactly on the next cue; loop lengths are snapped the same way.analyze_show
and the replace-overlap lint ignore anything shorter than a millisecond. - A random chase is random on every lap (#532):
pattern: randomshuffled the fixtures once
per effect and repeated that order on every lap, so the same brick lit on the same beat every
bar -- a fixed loop, not a sparkle. Each lap is now a fresh shuffle, seeded by the cue time and
the lap so a given moment always renders the same way, and a lap never starts on the fixture the
previous one ended on. hold_timein measures is as long asdurationin measures: ahold_timecounted
"playback measures" on its own path, stepping through tempo changes at their unshifted bars and
ignoring gradual transitions, so in an offset show or a ramp it disagreed with the same length
written asdurationand with the cue grid. It is now resolved exactly asdurationis.- Durations inside a sequence follow the tempo the sequence plays at (#529): a sequence's cue
times were rescaled to the tempo where it is expanded, but its effects'beats/measures
durations stayed resolved at the tempo the sequence was parsed at. In Esaweg (110 BPM at the top,
sequences played at 160) the verse's8measureschases lasted 17.4 s instead of 12 s and
overlapped the next half, and half-beat flashes ran 40% long. Musical durations are now kept as
written and resolved again when the sequence is expanded, at the expanded cue's time and offset;
durations in seconds are left as they are. - A measure-long effect inside a tempo ramp lasts the whole bar (#526):
measuresdurations
were integrated as step changes, ignoring gradual transitions, soduration: 1measureon the
downbeat of a ritardando bar ended at the pre-ramp tempo -- 0.6 s short in Jotunheim, a blackout
before the final chord -- while the next bar's cue fired where the ramp put it. A measure
duration is now the measure position at its start plus the count, converted back through the
same walk cue positions use, so durations and cue times agree inside ramps. offsetshifts the tempo thatbeats/measuresspeeds run at (#525): anoffset N measuresmoved cue times, but the engine read tempo-aware speeds and frequencies against the
unshifted tempo map, so a beat-based chase changed tempoNmeasures early -- Esaweg's chase ran
at 160 for a bar before the music did. Each effect now carries its cue's offset, and its rate is
read against the tempo map shifted by it. Abeats/measuresrate is also taken from the tempo
at the instant rather than the length of the next N beats, which blended in an upcoming tempo
change for N beats before it.- A profile update no longer wipes the sections it does not mention (#523): a whole-profile
update -- MCP and gRPCupdate_profile, and the web UI's profile saves -- deserialized the body
on its own, so every section it left out came back empty. Adding a controller that way wrote
trigger: ~,status_events: ~andnotifications: ~, and the audio trigger silently
stopped. A section the body omits now keeps its current value and one sent asnullis
removed; the web UI sendsnullwhen a section or the hostname is removed. A profile update
that empties a populated section is logged as a warning. - A strobe no longer keeps strobing after it ends: the engine only wrote the channels an
effect drove that frame, and DMX holds whatever it was last sent, so when a hardware strobe
expired, was cleared, or was cut off by the next song, the fixture's strobe channel stayed at
its last rate. The next look strobed in hardware until something drove the strobe again — an
Esaweg ending at 10 Hz strobed straight through the gap and into the following song. A strobe
channel driven on one frame and not the next is now sent off. write_song_lightingputs every new show underlighting/(#516): a song with an old,
unreferencedshow.lightbeside itssong.yamlhad the new show written over that stray at
the song root, while every other new show went to<song>/lighting/. An unreferenced
root-level file in a directory-layout song is no longer reused; referenced files, drafts under
lighting/, and the flat-layout pre-created file still are.- 12/8 click tracks report the tempo the show is written in (#515):
song_detailscounted
one beat per click, so a 12/8 song clicked on the dotted quarter reported 112 for a 169 tempo
block, and a bar of eighth clicks showed up as its own 240 BPM segment. When the song or one of
its shows declares a meter, each bar's clicks are now read against that bar's length, giving
quarter-note BPM (bpm_unit: "quarter";"click"when there is no meter to read against). A
short, jittery stretch at a section change is folded into the neighbour whose tempo it is
nearer rather than the longer one, so the change is reported on the bar it happens. - A fading
replacestatic crossfades with the bed instead of dipping to black (#521): an
up_time/down_timeon areplacestatic scaled the static's own colour toward zero and
then replaced the layers beneath with it, so a lift over a coloured bed went bed → black →
colour → black → bed. A partial replace now moves from the value beneath toward its colour by
the fade amount; over nothing it still fades from black, andscreen/multiply/add/
overlaystatics fade their own contribution as before. - A chase lifts the bed instead of blacking it out when the blend says so (#518): on an
RGB-only fixture the chase wrote its mask into a multiplier whatever theblend_mode, so a
screen,addoroverlaychase lit one fixture and sent every other one to zero — the
documented pass-through never happened. The lifting modes now write the mask into the colour
channels: the active fixture brightens toward white, the inactive ones keep the bed.multiply
andreplacestill gate (the classic running light).overlayis treated asscreen, since
with a zero mask it would darken a dim bed, which no chase means. - A looping song's lights no longer thrash after the first pass (#517): the DMX loop restarted
its timeline the moment the last cue fired and measured song time from a clock that kept
counting across passes, so a show whose cues sit near the top restarted ~40 times a second
from the loop point on. Passes now restart on the audio's own boundaries and count song time
from their start. repeats: Nfollowed by a comment parses (#514): the number rule admitted implicit
whitespace, which includes comments, so the comment became part of the count.- Writing a file another user owns no longer fails on the mode (#513): the in-place rewrite
re-applied the destination's own mode, whichfchmodrefuses to a non-owner. The mode is left
alone when it already matches, and a refused change is logged rather than failing the write. - Named playlists resolve without
playlists_dir(#512):read_playlistand
write_playlistwith anamenow default toplaylists/beside the config, as the loader
already did, instead of refusing.