1use sva_core::DEFAULT_STORE_BYTES;
4use sva_engine::{
5 DEFAULT_BUFFER_SECS, DEFAULT_CELL_SECS, DEFAULT_FLOOR_DB, DEFAULT_FRAME_SECS,
6 DEFAULT_LEDGER_DEPTH, DEFAULT_MAX_PAIRS, DEFAULT_MAX_PEAKS, DEFAULT_MODULATION_WINDOW_SECS,
7 DEFAULT_NOVELTY_WINDOW_SECS, DEFAULT_OVERSAMPLE, DEFAULT_PERIODICITY_WINDOW_SECS,
8 DEFAULT_SAMPLE_RATE, DEFAULT_SMOOTH_SECS, PSYCHOACOUSTIC_V1, READINGS, Reads,
9 self_similarity::MAX_CELLS,
10};
11
12pub fn help_text() -> String {
15 let bits = PSYCHOACOUSTIC_V1.precision_bits;
16 let store_gb = DEFAULT_STORE_BYTES >> 30;
17 let representations = representations();
18 format!(
19 r#"USAGE:
20 sva-cli (render | analyze | lint | trace | builtins | outline | new) [arguments]
21
22DESCRIPTION:
23 A composition is a directory of node files, each one closed-form expression in
24 `t` or `f` on one line, or a score placing notes in time. sva-cli reads that
25 composition and prints what it is and what it sounds like, as JSON on stdout.
26 `render`, `lint` and `trace` read the current directory; `new` writes beside it
27 and `analyze` reads a file.
28
29SCORES:
30 A score is a `.jsonl` file: JSON Lines, one object per line, sorted by `at`;
31 `@score/drop` reads `score/drop.jsonl`, and a ref never writes `.jsonl`. A
32 file with no extension opening with `{{` refuses with the rename. Line 1 is
33 its annotation, the doc comment's four fields, which `lint` holds it to:
34 {{"annotation": "Models: ... | Neglects: ... | IO: (t) -> amplitude | Tags: ..."}}
35 Every other line is one kind, told by its keys, and may add `"comment"`:
36 event "at", "node", "deg", "len" and the node's parameters: plays it
37 mark "at", "mark": a point `mark(name)` reads in this file's times
38 comment "at", "comment": a note to the reader at a time
39 move "at", "lane", "to", and "node", "len", "shape" where written
40 A score lowers to equations, the sum of its events in written order, each
41 its node shifted to its `at`; marks, comments and moves sound nothing.
42 at a time string, "0.25b" or "0.5s", or arithmetic over times, "2b +
43 1b/3" for a triplet; a bare number has no unit and refuses. A file
44 counts every time in bars or every one in seconds. "mark(verse) -
45 2b" is a position, "mark(drop) - mark(build)" a duration.
46 node an `@ref` from the score's own directory: a sound, an equation or
47 another score. On a line placing a score `len` crops it there and
48 nothing else binds; a `node` list mixing scores and sounds refuses
49 `len`.
50 deg a whole scale degree counted from 1, the tonic: sugar for "f0":
51 "deg(n)". In a seven-row scale 8 is the tonic an octave up, 0 the
52 seventh below and -6 the tonic an octave down. A chromatic note
53 binds "f0": "deg(7)*1st"; a line writing both refuses.
54 len a time as `at` takes, bound as the node's `len`.
55 other binds the node's parameter of that name: a number, or a constant in
56 a string, "0.03s", "-6db", "900hz", "A1", "0.8*0.9", "deg(5)/2".
57 Any field but `comment` may be a list. A line listing N items is N events,
58 moves or marks, item i of every list going to the i-th and a single value to
59 all; lists of different lengths refuse. A chord is a `deg` list on one `at`:
60 {{"at": "0b", "node": "@../instruments/keys", "deg": [1, 3, 5],
61 "vel": [0.8, 0.5, 0.6]}}
62 `deg(n)` is the tonic in `variables/key` times degree n's ratio in the scale
63 `variables/scale` names, read where the line sits, in a score or in any
64 equation. A scale is one the language names, as `minor` or `dorian` (`sva-cli
65 builtins` lists each with its cents), or a file of one row per degree in cents
66 above the tonic, ascending, the last the period, named by a ref: a file
67 `scales/custom` holding 200 400 500 700 900 1100 1200 is the major scale. A
68 value reading `t` or a `@ref`, a field the node does not read, a parameter it
69 reads that nothing binds, and a `deg` with no scale refuse before any audio,
70 each naming the score's file, line and column.
71
72 A lane is one value moving through the song: a node's parameter, a move
73 naming "node" and "lane": "cutoff", or a variable under `variables/`, a move
74 naming "lane" alone: "energy", "key", "scale", "bpm" (never "meter"). A move
75 with no `len` sets the lane at `at`; with "len" and "shape", `lin` or `exp`,
76 it ramps there from the value it holds:
77 {{"at": "24b", "node": "@../instruments/saw", "lane": "cutoff",
78 "to": "9khz", "len": "8b", "shape": "exp"}}
79 {{"at": "72b", "lane": "key", "to": "key*2st", "comment": "a tone up"}}
80 {{"at": "92b", "lane": "bpm", "to": "bpm*8/9", "len": "4b", "shape": "lin"}}
81 A `to` naming the lane means its home value, the node's default or the
82 variable's file, so it follows `--set`. A lane reaches every instance of its
83 node, from a score or an equation, inside a wrapper too, unless the line or
84 an invoking equation passes that parameter, which wins. Where it holds one
85 value over an instance's span it binds that number; where it moves there the
86 instance reads it at its song time, and where the engine refuses a moving
87 value, such as `sat`'s `drive`, the move refuses as `bad-lane`: pass a
88 constant, move the lane between notes, or close the sound's tail with a crop.
89 A score's times ride the tempo curve the `bpm` lane draws; `b` in an equation
90 stays a bar of `variables/bpm`. A song places its sections at marks:
91 {{"at": "0b", "mark": "intro"}}
92 {{"at": "mark(intro)", "node": "@intro"}}
93 {{"at": "mark(intro) + 8b", "mark": "verse"}}
94 {{"at": "mark(verse)", "node": "@verse"}}
95 `concat` and `repeat` place parts on the curve too: `repeat(@score/riff-1b, 4)`.
96 An equation reading a score at `t - d`, an echo or a send, hears the song as
97 it was d earlier: its lines keep their song time, and a move however many
98 reads reach it is one move.
99 A control score is one no sounding score places, read by an equation as a
100 signal: `@score/song(t) * (1 - @score/pump(t))` ducks the song under it.
101 `sva-cli outline @score/song` prints a song's marks, tempo, lanes, events and
102 size. Lint and render warn of a line passing a parameter whose lane ramps
103 under it (`line-overrides-lane`), and of a lane no instance reads
104 (`lane-reaches-nothing`).
105
106RESERVED VARIABLES:
107 Four nodes under `variables/` are reserved: a ref reads one like any node,
108 and a bar or a degree reads one by name:
109 variables/bpm a number, the beats per minute: 120
110 variables/meter beats per bar over the note: 4/4
111 variables/key the tonic `deg` counts from, a note or hertz: C3
112 variables/scale the scale `deg` counts in, a built-in's name: major, or a
113 ref to a scale file: @../scales/custom
114 A `b` literal or a file's `-2b` span reads bpm and meter; a `deg(n)` reads key
115 and scale. Before anything renders, each read is replaced by its value, by one
116 pass for all four: a bar becomes `n*(beats*60/bpm)` seconds and `deg(n)`
117 becomes `@variables/key` times degree n's ratio, the tonic still a ref. A
118 read with no value to replace it refuses as `bad-tempo` or `bad-scale`, as
119 does a variable holding no such value, such as bpm without meter, or a
120 scale name no built-in has, which refuses listing every built-in. A stream's
121 edit to a variable, or to the scale file it names, replaces every read of it
122 again. A node elsewhere named `bpm` is an ordinary node. Any other file under
123 `variables/`, as `variables/energy` holding 0.5, is a variable of the
124 composition's own: a ref reads it, `--set` sets it, and a score's moves move
125 it, as they move the reserved ones but `meter` (SCORES).
126
127RENDER:
128 sva-cli render '<expression>' --representation <list> [--until '<condition>']
129 [--bits <n>] [--rate <hz>] [--threads <n>] [--cache <path|none>]
130 [--set <name>=<text>]... [--confirm]
131
132 Renders one expression, in the grammar a node file's body uses, and prints one
133 reading per representation under `data.representations`. `@path` reads a node
134 from the current directory and `@/abs/path` one anywhere; there is no default
135 target. Every reading states its `source` (exact or measured), the
136 conformance profile it ran under, and its rate.
137
138 The target's own ref may read an interval: `@piano4([0, 2b], f0=C4,
139 vel=0.5, release=1s)`, where `release` is the instrument's own parameter. Its ends are the language's literals (`s`, `ms`, `b`,
140 `sp`, or a bare `0`); `[a, inf)` and `[a,)` leave the end open; the other
141 arguments are the node's own named ones. The start trims the output alone:
142 history before it is still computed, so a loop or a filter carries the state
143 it had there. With no interval the render starts at 0, earlier only where a
144 crop reaches before it, and its end is open. Inside an expression a window is
145 a `crop`. A target that samples its one ref read, `sample(@piano4([0, 2b],
146 f0=C4))`, reads that ref's interval and binds and the same node values.
147
148 Every node is computed over its support met with what reads it, and nowhere
149 else: outside its support a node is exactly zero. A closed interval renders
150 exactly its length, nothing cut. An open one ends where the root's support
151 does: a crop's end, the exact underflow of an `exp`, a clamp holding a factor
152 at zero; or earlier, where a proven upper bound on the root's magnitude stays
153 under the profile's silence threshold (-120 dBFS) from a sample on. A root
154 with no such bound (a held sine, a loop) is never cut. The label's `pruned`
155 states the threshold as `db` and the root's cut, `from` the sample the render
156 is treated as silent from. A stream's term leaves `@notes` where its support
157 ends. An open interval over a root whose support never ends and is never cut
158 (a held sine, a physical solver) refuses as `render.no_end`. A short-time
159 transform reads its input whole, and refuses one with no end as
160 `engine.unbounded_extent`. A form in `f` with no dual in `t` has no samples
161 and refuses as its dual does, `cast.left_algebra`.
162
163 A render is a stream pulled to its end: a closed form, a short-time
164 transform and what either reads are computed over their whole extent first,
165 and every other node block by block. `--until '<condition>'` stops the render
166 at the first sample the condition holds at, or at the interval's end,
167 whichever is first. It is checked as each block is pulled, so no node driven
168 block by block runs past the block it holds in. A condition compares (`<`,
169 `<=`, `>`, `>=`) `t`, `envelope(t)` (the RMS over every channel of the
170 `envelope` representation's frame holding `t`, framed by its own `frame`
171 where one is asked) and literals, joined by `and`/`or`.
172
173 `--representation <list>` takes a comma list of readings and may repeat. Each
174 is a call in the language's own syntax, its options its named arguments:
175 `spectrum(peaks=8, frame=50ms)`, `ledger(depth=3, brief=1)`. An entry
176 followed by `=<path>` writes a file instead, listed under `written`:
177 `samples=<path>.wav` writes audio, encoded as `--bits` says; any other path
178 takes the reading's JSON, uncapped. A path that already holds a file refuses
179 unless `--confirm` is written. `samples=alsa:<device>` plays the samples on
180 that ALSA device (`alsa:default`, `alsa:hw:1`, `alsa:plughw:1,0`) as each
181 block is pulled, in real time, through `aplay` from alsa-utils; Linux only,
182 anywhere else it refuses as `destination.unsupported_platform`. It sends
183 32-bit floats, or integer PCM where `--bits` is 16 or under. A device that
184 cannot take the format refuses with aplay's reason, and one aplay says plays
185 another rate than the render's stops there as `destination.rate_inexact`.
186 `samples(buffer=<t>)` is how much the device holds ahead of what it plays.
187 Once it has played out, `data.played` states the device, its seconds, rate,
188 channels, format, the buffer asked and the one granted, and every underrun
189 aplay reported, with what it cannot see.
190 `lines`, `atoms`, `derivative`, `bindings`, `arguments`, and a closed form's
191 `spectrum`, `envelope` and `pitch`, read the expression and ignore the range.
192
193 A closed form's `spectrum` and `pitch` are its exact lines, so a term under a
194 crop or an envelope, which has a width and is no line, refuses them; so do
195 `spectrum(frame=)` and `envelope(frame=)`, as a closed form has no frames. To
196 measure any of them frame by frame, a reader samples the target's ref,
197 `sample(@x([0, 1s]))`, and an author writes `sample(...)` inside the node.
198 A closed form with no exact envelope, such as a crop of a decay, refuses
199 `envelope` with the reason rather than measuring it unasked; `sample(...)`
200 asks for the measured one. A measured `spectrum` is one spectrum: every
201 `frame`-long window across the range, averaged. `pitch` is framed, one entry
202 per `frame`; for the spectrum at one instant, read a range one frame long.
203 A measured `envelope` is framed too, and reads every channel: a frame's
204 `rms` is the root mean square of all its channels' samples together, its
205 `peak` the largest magnitude in any channel.
206
207 `--bits <n>` is the precision every sample is written to, from 2 to 52: the
208 point where a series is truncated, and the encoding of a `.wav`, integer PCM
209 at n bits up to 16 and 32-bit float above. `--rate <hz>` (default
210 {DEFAULT_SAMPLE_RATE}) is the one rate a render samples at: the target is read
211 at its instants, and every node at the instants its reader asks for. A
212 closed form is exact at any instant, and so is a continuous loop, one
213 constant delay at a gain under 1 over a closed form such as
214 `x + 0.5*self(t - 17ms)`, which is its series. A filter, a discrete loop, a
215 solver and `rand` step at the rate asked for, where `1sp` is one step, so an
216 `sp` count or `self[idx(t) - 1]` means one sample at whatever rate is asked.
217 `rand` draws once per step, keyed by that step's index; a key between steps
218 reads the step nearest it, ties to even.
219
220 `--set <name>=<text>` renders as if the variable `variables/<name>` held
221 `<text>`, and writes no file: `--set key=G3` transposes every degree to G3,
222 `--set scale=dorian` changes mode (a scale file is a ref written from
223 `variables/`, as the file holds it: `--set scale=@../scales/custom`), `--set
224 bpm=140` moves every bar, `--set meter=3/4` regroups them and `--set
225 energy=0.8` sets a variable of the composition's own. It sets a lane's home
226 value: a move whose `to` names it follows. It may repeat, once per variable: a
227 variable set twice refuses as `render.set_twice`, a name with no file under
228 `variables/` that is no reserved one as `render.set_unreserved`, listing the
229 variables there, and the file's path, `variables/key=G3`, as
230 `render.set_path`, naming the short form.
231
232 `--threads <n>` (default: every core the machine reports) is the most
233 threads a render computes on; `1` computes every value in turn on one.
234 Values none of the others reads, and the blocks of a value that holds no
235 state, compute apart, to the same bits on any number of threads.
236
237 A discrete loop reads its own past only by index; `self(t - d)` in one
238 refuses as `type.discrete_self_at_time`, naming what made it discrete. A
239 read `@x(k*t - d)` steps `x` at `k` times the step, every input it reads,
240 `sp` and `idx` with it, and rounds `d` to the nearest sample of that step,
241 ties to even: shifts are rounded to the nearest sample so placements share
242 one cached value; timing is exact to half a sample, and the label's
243 `moved_s` states the most any read moved. A node is computed once
244 per step however many reads ask for it. A node that holds state read at a
245 time that moves refuses as `type.stateful_warp`, naming what
246 holds its state; `@x[idx(...)]` reads its nearest step instead, as `p[idx(...)]`
247 does a signal passed in as parameter `p`, and any `idx`, as
248 `idx(t - 5ms - 2ms*sin(2*pi*t))`, is read sample by sample. An edit to a
249 stream plays from the next sample on.
250
251 A render keeps in a store on disk its target and, under it, each node of
252 samples that holds state (a filter, a discrete loop or a solver in it) or
253 that two or more nodes read. Nothing else is kept. A closed form, `sample`
254 of one and `rand` have a value at any instant and are computed where read.
255 A node without state that one node alone reads, however many times, is
256 computed again by that reader from what the node itself reads: no other
257 reader could ask for it, and without state it restarts at any sample, where
258 a filter or a loop must run from its start. So a stage worth keeping is a
259 node of its own that ends in a filter or a loop, or one two nodes read. A
260 kept value is named by what it computes: never its file's name, directory,
261 comments, spacing, or the order its named arguments are written in, so a
262 renamed or moved copy, a constant lifted into a default, an expression split
263 into a file of its own or written back inline, and two addends swapped all
264 read one entry. The next render of anything that reads the same value reads
265 it back, bit for bit, and computes nothing under it. The store is on by
266 default, at `$XDG_CACHE_HOME/sva`, else `~/.cache/sva`; `--cache <path>`
267 moves it (a `/dev/shm` path keeps it in memory) and `--cache none` turns it
268 off. It holds at most {store_gb} GB, the least recently used values going
269 first, and a store another build of sva-cli wrote is emptied when opened. A
270 render stages what it computes beside the store as it goes, and the store
271 itself is written once, when the render ends, fails, or is stopped by SIGINT
272 or SIGTERM.
273
274 `ledger` prints one row per node under the target. A row's `share` is the
275 part of its reader's own energy that row accounts for, so one reader's refs
276 sum to 1; a ref no addend isolates, such as one factor of a product, prints
277 `null`. Each ref carrying a share is collapsed once on its own, so a ledger
278 costs one collapse per attributed ref beyond the render, and `depth` bounds
279 how many, counted in refs from one file to another. Every row is computed
280 whole over the range, however late a reader reads it. `brief=1` keeps only
281 the rows that clipped, `skim=1` drops the wider fields.
282
283 `arguments` renders nothing: for every instance under the target it prints
284 each builtin call's named arguments as the numbers the call was lowered
285 with, a solver's whole parameter set with `written: false` on each default
286 it filled in, and the operand each `min`/`max` `chosen` where a call folds
287 one to a number: in named arguments and a solver's, modal bank's or
288 `noise`'s positionals. One inside a filter's or cast's positional or
289 `rand`'s key or seed is not listed. `at` spans are bytes of the instance's
290 own body, as `sva-cli outline` counts them.
291
292REPRESENTATIONS:
293 Each one a `--representation` call, its arguments in parentheses, every
294 default under DEFAULTS. `exact` is read off the written math, `measured` off
295 samples.
296
297{representations}
298
299ANALYZE:
300 sva-cli analyze <file.wav> --representation <list> [--confirm]
301
302 Runs the same readings over a whole external `.wav` at its own rate, never
303 resampled. Only the readings a buffer answers alone apply, each listed under
304 REPRESENTATIONS as `measured` or `exact off a closed form, measured off
305 samples`; the rest need the graph behind it.
306
307LINT:
308 sva-cli lint ['<expression>'] [--format <json|text>]
309
310 Checks the current directory without rendering a sample. With no target it
311 checks every file under its own rules, and types every entry point, which
312 types every file it reaches. With a target, in render's grammar, it checks
313 the files that target reaches, and prints the interval a render of it reads.
314 Either refuses a type error as `render` does.
315
316 A score's annotation is its doc comment, under the same codes, and its lines
317 no body, so the length checks pass it by; a file of two or more lines that is
318 neither a score nor a scale refuses as `multiline-body` before any check
319 runs. Each song lint types prints its warnings beside the findings.
320
321 Every check prints one `data.diagnostics` item. `warning` exits 0, `error`
322 exits non-zero, so branch on the verdict and never on whether the array is
323 empty. No flag downgrades an error.
324
325 error missing-comment no `;` comment line
326 multiline-comment more than one
327 malformed-comment not four ` | ` fields, `Models:` `Neglects:`
328 `IO: <in> -> <out>` `Tags: <tag>[, <tag>...]`
329 long-comment-block a `;` block over 1000 characters, the line-1
330 doc comment's own run exempted
331 long-expression-body a body over 10000 characters, a backstop rather
332 than a complexity budget
333 literal-sample-rate a written rate where `sp` belongs
334 arity a builtin called without an argument it reads,
335 or with one it does not, as `rand(seed=k)`
336 warning key-is-not-a-pitch `variables/key` holds neither a note name nor
337 a number of hertz
338 parameter-has-no-default
339 a node reads a parameter no `name = value` line
340 binds, so a bare `@node` cannot play
341 support-never-ends a node's support, cut where its bound falls
342 under the silence threshold, has no end for a
343 bare render to stop at
344
345TRACE:
346 sva-cli trace <node|expression>
347
348 Prints one node's position without rendering audio: what it reads (`down`, one
349 hop), everything that reads it (`up`, transitively to an entry point), each
350 beside the expression doing the reading, the node that made it discrete, and
351 the feedback loop it sits in, if any. A score is shown as its line count, its
352 first and last `at` and the nodes it plays, never the sum it lowers to; every
353 entry point's song is lowered first, each at song time 0. An interval the
354 target reads is ignored.
355
356BUILTINS:
357 sva-cli builtins
358
359 Prints the whole callable and syntactic vocabulary: every builtin with its
360 arity and named arguments, each argument's `meaning`, `unit`, the model
361 `part` it sets and whether it `moves` with `t` (a filter's cutoff, q and gain;
362 every other named argument is one number, refused when it names none), each
363 positional's meaning where the model states one, unit suffixes, the note-name
364 grammar, the scales `variables/scale` may name with their cents, reserved
365 identifiers, special call shapes, and what has no operator at all.
366
367OUTLINE:
368 sva-cli outline <expression>
369
370 Prints the parse tree the engine builds from one expression, each node with
371 the byte `span` it was written in: calls by `name` with positional and named
372 `args`, operators by `op`, refs by `path` with their `binds`, refs and `self`
373 by how they `read`, `time` or `index`, literals by `value` and `unit`, names
374 by `name`. A node the parser supplies itself, the `0` of a prefix minus or the
375 `t` of a bare `@ref`, has `written: false`. Reads no composition. Score text,
376 JSON opening with `{{`, prints `score` instead of `outline`: one item per item
377 a line lists, with its `kind`, `line`, `span`, `at` with marks resolved, and
378 its `node`, `deg`, `args`, `mark` or `lane`, `to`, `len` and `shape`.
379
380 sva-cli outline @score/song
381
382 Run in a composition, outlines that score as the song a render of it is:
383 `marks` in bars and seconds, the `tempo` curve's pieces, each lane with its
384 home and every move resolved with its placement chain, how many instances it
385 reaches and how many see it move, each placed event's position, node, pitch
386 in hertz and where each parameter comes from (`line`, `lane` or `default`),
387 and the `size`: placed events and the engine's instances beside its limit.
388
389NEW:
390 sva-cli new <name> [--idempotency-key <key>]
391
392 Writes a starter composition at ./<name>, and refuses if that directory
393 exists. `--idempotency-key <key>` records the key beside the composition, so a
394 retry under the same key succeeds identically while the tree still holds what
395 was written. Any other key, or an edited tree, refuses.
396
397EXAMPLES:
398 sva-cli new song1 && cd song1
399 sva-cli render '@master' --representation samples=/tmp/song1.wav
400 sva-cli render '@master([0, 8b])' --representation 'ledger(depth=2),loudness'
401 sva-cli render '@voice/note([0, inf), f0=C4, len=2s)' \
402 --until 'envelope(t) < -60db and t > 1s' --representation samples=/tmp/note.wav
403 sva-cli render 'sample(@chord/home)' --representation 'spectrum(peaks=8)' --rate 48000
404 sva-cli render 'sample(@voice/note([0, 2s], f0=C4))' --representation pitch
405 sva-cli render '@master' --set key=G3 --set bpm=140 --set scale=dorian \
406 --representation samples=/tmp/song1-g.wav
407 sva-cli render '@master' \
408 --representation 'samples(buffer=100ms)=alsa:default,loudness'
409 sva-cli lint
410 sva-cli lint '@master'
411 sva-cli trace score/phrase
412 sva-cli outline @score/phrase
413 sva-cli builtins
414
415OUTPUT:
416 {{"status": "success", "data": {{"target": "@master([0, 8b])", "sample_rate":
417 {DEFAULT_SAMPLE_RATE}, "bits": {bits}, "profile": "psychoacoustic-v1",
418 "interval": {{"start_secs":
419 0, "end_secs": 16}}, "label": {{...}}, "written": {{"items": [...]}},
420 "representations": {{"ledger": {{...}}}}, "diagnostics": {{"items": []}}}},
421 "meta": {{"request_id": "req_...", "timestamp": 1700000000}}}}
422
423 `interval` is null where no reading read samples. An error adds "error":
424 {{"code", "message", "details": {{"count", "codes"}}}}. Success or error,
425 every response carries every finding in full at "data": {{"diagnostics":
426 {{"items": [{{"code", "severity", "message", "location", "help"}}],
427 "pagination": {{"count", "has_more", "next_cursor"}}}}}}, empty where it found
428 none.
429
430 Every collection carries that same {{items, pagination}} pair. `count` is the
431 whole reading's, `has_more` says `items` holds less than that, and
432 `next_cursor` is the interval start, as `0.5s`, the rest is read from. A
433 reading written to a file is written whole, uncapped. This page is an
434 envelope of its own, at "data": {{"help"}}.
435
436DEFAULTS:
437 --rate <hz> the one rate a render samples at, and `1sp`.
438 Default {DEFAULT_SAMPLE_RATE}.
439 --bits <n> the precision every sample is written to. Default {bits},
440 the `psychoacoustic-v1` profile's own.
441 ledger(depth=<n>) how deep below its target a `ledger` walks.
442 Default {DEFAULT_LEDGER_DEPTH}.
443 (peaks=<n>) peaks a `spectrum` keeps, notes a `pitch`, formants a
444 `formants`, stripes and novelty peaks a `self_similarity`
445 lists per view. Default {DEFAULT_MAX_PEAKS}.
446 alias(oversample=<n>) the multiple `alias` re-renders at to hear what folded.
447 Default {DEFAULT_OVERSAMPLE}.
448 (frame=<t>) the step a framed reading advances by, in seconds.
449 Default {DEFAULT_FRAME_SECS}, except `spectrum`, which sizes
450 its own transform to the range unless it is set.
451 (floor=<db>) the amplitude, as `-40db`, under which a harmonic reading
452 or `self_similarity` drops a partial, and under which
453 `self_similarity` counts a band level as zero.
454 Default {DEFAULT_FLOOR_DB}db.
455 voice_leading(norm=<l1|l2>) how a voice leading's moves add up. Default l1.
456 dissonance(pairs=<n>) the most partial pairs one frame sums before it refuses.
457 Default {DEFAULT_MAX_PAIRS}.
458 periodicity(window=<t>) the span of onset strength one window autocorrelates,
459 in seconds, at least twice the longest period it reads.
460 Default {DEFAULT_PERIODICITY_WINDOW_SECS}.
461 periodicity(curve=<0|1>) `1` adds every window's height at every lag.
462 Default 0.
463 modulation(window=<t>) the span of band envelope one window transforms, in
464 seconds, at least twice the slowest period it reads.
465 Default {DEFAULT_MODULATION_WINDOW_SECS}.
466 modulation(bpm=<n>) a tempo, as `140`, adding each rate's period in beats.
467 Default none.
468 self_similarity(cell=<t>) the side of one matrix cell, in seconds, a whole
469 number of frames; the target may span at most
470 {MAX_CELLS} cells. Default {DEFAULT_CELL_SECS}.
471 self_similarity(smooth=<t>) the length each diagonal is averaged over
472 before stripes are picked, in seconds.
473 Default {DEFAULT_SMOOTH_SECS}.
474 self_similarity(window=<t>) the whole width of the novelty kernel, in
475 seconds. Default {DEFAULT_NOVELTY_WINDOW_SECS}.
476 self_similarity(curve=<0|1>) `1` adds every cell's chroma and band levels and
477 each view's matrix. Default 0.
478 bindings(node=@<path>) the instance whose bindings are read; required.
479 samples(buffer=<t>) how much an `alsa:` device holds ahead of what it plays,
480 in seconds. Default {DEFAULT_BUFFER_SECS}; only a device
481 has one.
482 ledger(brief, skim) `0` unless set `1`.
483 --format <json|text> how `lint` prints its findings: the envelope, or one
484 terminal line each, colored where stdout is a terminal.
485 The same objects either way. Default json.
486 --cache <path|none> where the store lives. Default `$XDG_CACHE_HOME/sva`, else
487 `~/.cache/sva`, holding at most {store_gb} GB; `none` keeps
488 no store. A store that fails fails no render: it answers
489 as without one, warned why in `diagnostics`.
490 --set <name>=<text> a variable read as `<text>` for this render.
491 Default none: each is read off its file.
492 --confirm replaces a destination that already holds a file. Without
493 it a path already taken refuses as `conflict` and nothing
494 is written.
495
496ENVIRONMENT:
497 SVA_LOG=debug `render` logs, once it ends, what it asked of each value
498 to stderr; stdout and the samples are unchanged. Any
499 other value, or none, logs nothing. One line per second
500 of output, then one per node and a total:
501 sva-cache pass hit=0 miss=190 ... reused=0 cum-hit=0.0%
502 sva-cache t=1.022s hit=1 miss=2 ... cum-hit=0.5%
503 sva-cache node hit=1 miss=0 ... reused=3 <node>
504 sva-cache total hit=1 miss=198 ... hit-rate=0.5% ...
505 `pass` is what was looked up before the first block.
506 Each value is looked up once: a `hit` was answered by
507 the store, `prefix` found a stored run up to a switch,
508 `miss` computed it, `new` held it in memory, where the
509 store takes what it keeps from. Each read of a value
510 past its first is `reused`.
511
512EXIT CODES:
513 0 success (error.code absent)
514 1 internal_error (a destination could not be written)
515 3 validation_error (bad arguments, a composition that failed to parse, or a
516 render the engine refused)
517 4 conflict (a name `new` would overwrite, or a destination already holding a
518 file, without `--confirm`)
519 24 not_found (a `.wav` file, or a node this composition does not define)
520
521VERB ALIASES:
522 validate = lint, list = builtins, create = new, show = trace, from the `cli`
523 standard's own verb list. `render` and `analyze` take a reading, which that
524 list has no word for, so they keep their own names.
525
526SEE ALSO:
527 sva-cli --version Show version information"#
528 )
529}
530
531fn representations() -> String {
533 READINGS
534 .iter()
535 .map(|row| {
536 let keys: Vec<&str> = row.takes.iter().map(|arg| arg.key()).collect();
537 let call = match keys[..] {
538 [] => row.name.to_string(),
539 _ => format!("{}({})", row.name, keys.join(", ")),
540 };
541 let source = match row.reads {
542 Reads::ClosedForm | Reads::Structure => "exact",
543 Reads::Either => "exact off a closed form, measured off samples",
544 Reads::Samples => "measured",
545 Reads::Graph => "measured, and the graph behind the samples",
546 };
547 format!(" {call:<27}{source}\n {}", row.help)
548 })
549 .collect::<Vec<_>>()
550 .join("\n")
551}