"you africans, please listen to me as africans
and you non-africans, listen to me with open mind"
- read this readme
- there's some exapmles of the basic ideas here: https://github.com/tomara-x/quartz/discussions/categories/e
- drag and drop examples from the assets folder into a quartz window (type
sa
thenII
to display info) - if anything is confusing, please start a discussion: https://github.com/tomara-x/quartz/discussions
to build from source:
- install rust: https://www.rust-lang.org/tools/install
- install bevy dependencies: https://bevyengine.org/learn/quick-start/getting-started/setup/#installing-os-dependencies
- on linux also install libjack-dev
- clone quartz
git clone https://github.com/tomara-x/quartz.git
- build it
cd quartz
cargo run --release
development happens on the main
branch
alternatively you can download stable releases from: https://github.com/tomara-x/quartz/releases
there's an experimental wasm build here: https://tomara-x.github.io/quartz/ (no audio input, no file loading/saving, and no OSC)
wasm building
uncomment the commented out dependencies in cargo.toml, then:
RUSTFLAGS=--cfg=web_sys_unstable_apis cargo build --profile wasm-release --target wasm32-unknown-unknown
wasm-bindgen --out-name quartz --out-dir target --target web target/wasm32-unknown-unknown/wasm-release/quartz.wasm
more details: https://github.com/bevyengine/bevy/tree/main/examples#wasm
when you open quartz, it will be an empty window. there's 3 modes:
- edit: (default) interact with entities and execute commands (press
e
oresc
) - draw: draw new circles (press
d
) - connect: connect circles (press
c
)- target: target an entity from another (hold
t
in connect mode)
- target: target an entity from another (hold
hold space
, then drag to pan the view, or scroll to zoom in and out
first, define your terms:
- circle: an object that you create in draw mode (they're regular polygons)
- hole: a connection object. these always come in (black hole - white hole) pairs
- entity: i'll use that to refer to any object (circle or hole)
in addition to the shared properties that both circles and holes have (position, color, radius, vertices) a circle holds other things:
- a number (just a float)
- an op string: defining what that circle does
- for example:
sum
,toggle
,screenshot
,lowpass()
- for example:
- an array of numbers: for different uses (don't worry it's empty by default, thus allocating no memory)
- an array of target entities that this circle controls in some way (empty by default too)
- an order number: defining if/when that circle is processed
- an audio node (defined by the op string) (empty by default)
there are 2 types of commands:
- return-terminated
(type it then press enter) (you can separate them with
;
to run more than one at once)
scene saving/loading
:e {file name}
edit (open) a scene file (in the assets path) (no spaces):w {file name}
write (save) a scene file (same)
:w moth.cute // saves the current scene as the file "assets/moth.cute" (OVERWRITES)
:e soup.fun // opens the file "assets/soup.fun" if it's there
(dragging and dropping scene files into a window also works)
set values
:set n [id] {float}
set num value:set r [id] {float}
set radius (use rx or ry to set those independently):set x [id] {float}
set x position:set y [id] {float}
set y position:set z [id] {float}
set z position (this controls depth. what's in front of what):set h [id] {float}
set hue value [0...360]:set s [id] {float}
set saturation [0...1]:set l [id] {float}
set lightness [0...1]:set a [id] {float}
set alpha [0...1]:set v [id] {float}
set number of vertices (3 or higher):set o [id] {float}
set rotation [-pi...pi] (:set rot
and:set rotation
also work):set op [id] {string}
set op (use shortcuto
):set ord[der] [id] {float}
set order (use[
and]
to increment/decrement order):set arr[ay] [id] {float float ...}
set the array (space separated):set tar[gets] {id id ...}
set targets (if nothing is selected, the first entity gets the rest of the list as its targets):tsel {id}
target selected (:tsel 4v2
sets selected entities as targets of entity 4v2):push {float}/{id}
push a number to the array, or an id to the targets array:lt [id] {link type}
set holes' link type (use shortcutl
)
:set n 4v0 42 // will set the num of entity 4v0 to 42
:set n 42 // will set the num values of selected entities to 42
audio device selection
:od {index} {index} [sample rate] [buffer size]
set the output audio device. first index is the host, second is the device index (use the commandsah
andao
to get those) if sample rate and buffer size aren't given, the device defaults will be used:id {index} {index} [sample rate] [buffer size]
set the input audio device
other
:nl
set the maximum number of nodes a connective op (*
,>>
, etc) will allow (default 500) (saved in scene file):reset_bloom
if you change bloom settings to the point where you can't see what's happening, reset them:dv {float}
set default number of vertices of drawn circles:dc {float} [float] [float] float]
set default color of drawn circles (h s l a):ht {id}
toggle open a white hole (by id):q
exit (don't combine with other commands using;
)
note: using the std constants in the commands works. e.g. :set op dc(-PI)
, :set n TAU
- immediate commands (these execute when you finish typing them)
drag modes
(what happens when dragging selected entities, or when arrow keys are pressed)
- exclusive:
ee
drag nothing (default)et
drag translation (move entity)er
drag radiusen
drag numbereh
drag huees
drag saturationel
drag lightnessea
drag alphaeo
drag rotationev
drag vertices
- add a drag mode: (to drag multiple properties at the same time)
Et
add translationEr
add radiusEn
add numEh
add hueEs
add saturationEl
add lightnessEa
add alphaEo
add rotationEv
add vertices
shortcuts
o
shortcut for:set op
l
shortcut for:lt
info texts
II
spawn info texts for selected entitiesIC
clear info textsID
show/hide entity id in visible info texts
inspect commands
(information about the selected entities)
ii
entity id'sin
number valuesira
radius valuesix
x positioniy
y positioniz
z positionihu
hue valueis
saturationil
lightnessial
alphaiv
verticesiro
rotationior
orderiop
opiar
arrayiho
holesit
targetsiL
hole link typeiO
white hole open status
audio node info
ni
number of inputsno
number of outputsnp
info about the node
audio hosts/devices
ah
list available audio hostsao
list output devicesai
list input devices
selection
sa
select allsA
deselect allsc
select all circlessC
deselect circlessh
select all holessH
deselect holessv
select visible entities (in view)sV
deselect visible entitiessg
select holes of the selected circlesst
select targets of the selected circles<delete>
delete selected entitiesyy
copy selection to clipboardp
paste copied
note: when drag-selecting, holding alt
will only select circles (ignores holes), holding ctrl
will only select holes (ignores circles), and holding shift
will add to the selection
visibility
vc
toggle circle visibilityvb
toggle black hole visibilityvw
toggle white hole visibilityva
toggle arrow visibilityvv
show all
other
<F11>
toggle fullscreenht
toggle white hole open statusF
freeze the command line (pressesc
,Enter
, orBackspace
to reactivate it)quartz
shhh!version
print versionawa
awawawa
any connection links 2 circles together in some way. the black hole is taking some data from the source circle, and the white hole is getting that data and feeding it to the sink circle. the link type determines what that data is.
n
or-1
: numr
or-2
: radiusx
or-3
: x positiony
or-4
: y positionz
or-5
: z positionh
or-6
: hues
or-7
: saturationl
or-8
: lightnessa
or-9
: alpha-10
: order (only withdistro
)v
or-11
: verticeso
or-12
: rotationA
or-13
: arrayT
or-14
: targets- positive numbers: used to denote "input number x"
0
usually means audio node (or nothing) a0 -> 0
connection does nothing (except for specific ops), but when connecting audio nodes, the connection is usually0 -> x
(x is positive)
use }
/{
to increment/decrement link types. or use l
with selected holes to set a specific link type
every circle has an order (0 or higher). things in order 0 do nothing.
each frame, every circle with a positive order gets processed (does whatever its op defines) this processing happens.. you guessed it, in order
we process things breadth-first. so when a circle is processed, all its inputs must have their data ready (they processed in this frame) to make sure that's the case, their order has to be lower than that of the circle reading their data...
lower order processes first, and the higher the order, the later that circle processes (within the same frame)
unless...
a circle has an array of "targets" those are just entity id's. so it's like a "pointer" to another circles or hole. think of it as a one-way wireless connection.
some ops make a circle do things to its targets. like process
, del_targets
, spin_target
, distro
, reorder
, (see ops for full list)
(they allow some things that aren't easy through normal processing. since circles read their input when they process, while targets are written to when the controller circle is processed instead)
targets
process
- this circle will process its targets in the order they appear in the targets array. it doesn't matter what order those targets are. even if they're at order 0 (it's preferable they are at 0 so you don't cause unexpected things). so for every frame a circle with a
process
op is processed, it processes all of its targets in order. - you can't nest them. so if a process has another process in its targets, that won't process the second one (to avoid blowing up computers)
- this circle will process its targets in the order they appear in the targets array. it doesn't matter what order those targets are. even if they're at order 0 (it's preferable they are at 0 so you don't cause unexpected things). so for every frame a circle with a
select_target
- input:
n -> 1
- select the targets when input is non-zero, deselect them when it's zero
- input:
open_target
- inputs:
n -> 1
- open target white holes when input is non-zero
- inputs:
close_target
- inputs:
n -> 1
- close target white holes when input is non-zero
- inputs:
open_nth
- inputs:
n -> 1
- open nth target once if it's a white hole
- inputs:
del_target
- inputs:
n -> 1
- delete targets and clear targets array when input is non-zero
- inputs:
spin_target
- inputs:
n
,n -> 1
- rotate targets around self by self
n
when inputn
is non-zero
- inputs:
reorder
- inputs:
n -> 1
- set target circles' order to input
n
- inputs:
spawn
- inputs:
n -> 1
- spawn a new circle similar to self when input is non-zero. the new circle is added to this circle's targets. only the color, vertices, and transform (ish) are copied (z depth is increased with each one)
- inputs:
distro
- inputs:
A -> n/r/x/y/z/r/o/v/h/s/l/a/-10
(any number of those) - distribute values from input array among targets
- inputs:
connect_target
- inputs:
n -> 1
, [T -> 2
] - remove holes from targets array, then connect each target circle to the next. if array contains 2 numbers they will be used as the connection type (otherwise
0 -> 0
) if second input is provided, the white holes created will be added as targets to that circle
- inputs:
isolate_target
- inputs:
n -> 1
- delete all connections target has when input is non-zero
- inputs:
target_lt
- inputs:
n -> 1
- for hole targets, set their link type to input num
- inputs:
repeat
- inputs:
n
,T -> 1
- repeat input targets array n times
- inputs:
arrays
zip
- inputs:
A -> 1
,A -> 2
- zip array 1 and array 2
- inputs:
unzip
- inputs:
A -> 1
- unzip input array (one side remains in input array, the other side is in self)
- inputs:
push
- inputs:
n -> 1
- push input num to self's array
- inputs:
pop
- inputs:
n -> 1
- pop the last number in the array and set self's num to it when input is non-zero
- inputs:
len
- inputs:
A -> 1
- length of input array
- inputs:
append
- inputs:
A -> 1
- copy input array and append it to the end of self's array
- inputs:
slice
- inputs:
n
,A -> 1
- slice input array at index
n
, [0..n] remain in input array, [n..len] are moved to self's array
- inputs:
resize
- inputs:
n -> 1
- resize self's array, discards numbers when shrinking, and adds zeros when growing
- inputs:
contains
- inputs:
A -> 1
,n -> 2
- outputs 1 when input array contains input num, 0 otherwise
- inputs:
set
- inputs:
n -> 1
,n -> 2
- first input is index, second is value. sets the value of the given index of self's array
- inputs:
get
- inputs:
A -> 1
,n -> 2
- get the value at index of the input array
- inputs:
collect
- inputs:
n -> {non-negative}
(any number of those) - collect all connected nums and create an array of them in order (in self)
- inputs:
settings
clear_color
- when color changes (drag h/s/l), sets the background color (the clear color)
draw_verts
- when vertices change, set the default drawing vertices for future circles
draw_color
- when color changes, set the default drawing color
highlight_color
- when color changes, set the highlight color (the outline around selected entities)
indicator_color
- when color changes, set the color of the selecting/drawing/connecting indicator
connection_color
- when color changes, set the color of connection arrows
connection_width
- when this circle's num changes, set the width of the connection arrows
command_color
- when color changes, set color of the command line text
text_size
- when this circle's num changes, set the font size of info texts
tonemapping
- inputs:
n -> 1
- input num sets the tonemapping mode. 0 =
None
, 1 =Reinhard
, 2 =ReinhardLuminance
, 3 =AcesFitted
, 4 =AgX
, 5 =SomewhatBoringDisplayTransform
, 6 =TonyMcMapface
, 7 =BlenderFilmic
(default: 6 tony)
- inputs:
bloom
- control bloom parameters
- inputs:
n -> 1
: intensity (default: 0.5)n -> 2
: low frequency boost (default: 0.6)n -> 3
: low frequency boost curvature (default: 0.4)n -> 4
: high pass frequency (default: 1)n -> 5
: composite mode (if n > 0Additive
elseEnergyConserving
) (default: additive)n -> 6
: prefilter threshold (default: 0)n -> 7
: prefilter threshold softness (default: 0)
all of these except for the tonemapping and bloom settings are saved inside the scene file (so deleting the circle after changing that setting is fine) but for persistent change to bloom/tonemapping you have to leave the circles with the input values attached to them in the scene
utils
cam
n -> 1
camera x positionn -> 2
camera y positionn -> 3
camera z position (can be useful if you're playing with extremes in depth)n -> 4
camera rotationn -> 5
zoom
update_rate
- inputs:
n -> 1
,n -> 2
- by default quartz will respond (as fast as possible) to any mouse input/movement, or keyboard input, or if the refresh duration has elapsed. that duration is by default 1/60 of a second (60fps) when the window is in focus, and 30fps when out of focus. first input is the refresh rate (in hz) for focused mode, second input is unfocused rate
- inputs:
command
- inputs:
0 -> 1
(op string to first input) - when the white hole is open set the command line text to the string of the input circle
- inputs:
screenshot
- inputs:
n -> 1
- when input num is non-zero, take a screenshot and save it as screenshots/{time in ms since 1970}.png (make sure that folder exists)
- inputs:
osc
- set the settings of osc sender and receiver
n -> 1
receiver port (needs to be specified for receiving to work)0 -> 2
op string of the input sets the host ip (ip to send to) (defaults to 127.0.0.1 (the machine itself))n -> 3
sender port (defaults to 1729)
osc_r_{osc address}
- receive osc messages into the array of this circle. the
osc
op must be present in this patch and is processing for this to work. the osc messages must be sent to the given osc address and contain floats. you can receive from multiple addresses - e.g.
osc_r /gyroscope
,osc_r /touch1 /touch3
- receive osc messages into the array of this circle. the
osc_s_{osc address}
- inputs:
A -> 1
- send the input array as an osc message with the given address (to the host and port set by the
osc
op) - e.g.
osc_s /space
- inputs:
for more info about osc: https://opensoundcontrol.stanford.edu/spec-1_0.html
input
mouse
- array stores mouse position (in world coordinates) [x, y]
lmb_pressed
- num = 1 if left mouse button is pressed, 0 otherwise
mmb_pressed
- num = 1 if middle mouse button is pressed, 0 otherwise
rmb_pressed
- num = 1 if right mouse button is pressed, 0 otherwise
butt
- num = 1 when clicked, 0 otherwise
toggle
- num = 1 when clicked, 0 when clicked again (kinda)
key
- pressed keyboard keys are added to this circle's array and removed when released. for keys corresponding to an ascii character that's their decimal ascii code, for other keys it's an arbitrary convention that i put together in 5 minutes:
Control
: 128,Shift
: 129,Alt
: 130,Super
: 131,Fn
: 132CapsLock
: 133,NumLock
: 134,ScrollLock
: 135End
: 136,Home
: 137,PageUp
: 138,PageDown
: 139Insert
: 140,ContextMenu
: 141ArrowUp
: 200,ArrowDown
: 201,ArrowLeft
: 202,ArrowRight
: 203F1
: -1,F2
: -2 ..F12
: -12
- pressed keyboard keys are added to this circle's array and removed when released. for keys corresponding to an ascii character that's their decimal ascii code, for other keys it's an arbitrary convention that i put together in 5 minutes:
pressed_{one or more characters}
- e.g.
pressed Hi
this circle's num will be set to 1 when eitherH
ori
is pressed, zero otherwise
- e.g.
data management xD
apply
- inputs:
0 -> 1
(input audio node),A -> 2
(input array) - process the input array as input to the given audio node (array length must match the number of input channels the node has) output of the node is written to this circle's array (process one audio frame)
- inputs:
render
- inputs:
n
,0 -> 1
(input node),n -> 2
(trigger) - render n samples from the given audio node into the array when the second input is non-zero (node must have 0 ins, and 1 out)
- inputs:
store
- inputs:
n -> 1
- store the input num into self's num, but doesn't open the white holes reading nums like usual
- inputs:
num_push
- inputs:
n -> 1
- output this circle's num (open all white holes reading it) when the input num in non-zero
- inputs:
sum
- inputs:
n -> 1
(any number of those) - convenience op for adding numbers together
- inputs:
product
- inputs:
n -> 1
(any number of those) - multiply numbers together
- inputs:
audio node management
refer to the fundsp readme, and docs for more details (sometimes)
SUM
- inputs:
0 -> {non-negative}
(any number of those),n
(repetitions) - sum given nodes together. their number of outputs must match, their inputs are stacked together in the order they appear in connections
- inputs:
*
PRO
- inputs:
0 -> {non-negative}
(any number of those),n
(repetitions) - multiply given nodes together. their number of outputs must match, their inputs are stacked together in the order they appear in connections
- inputs:
-
SUB
- inputs:
0 -> 1
,0 -> 2
- node 1 - node 2 (number of outputs of those nodes must match)
- inputs:
>>
PIP
- inputs:
0 -> {non-negative}
(any number of those),n
(repetitions) - pipe nodes though each other. if outputs of node 1 matches inputs of node 2 they're piped together, and so on
- inputs:
|
STA
- inputs:
0 -> {non-negative}
(any number of those),n
(repetitions) - stack inputs and outputs of given nodes
- inputs:
&
BUS
- inputs:
0 -> {non-negative}
(any number of those),n
(repetitions) - bus given nodes together. number of inputs and outputs must match. input is passed through each node and output from them is mixed at output
- inputs:
^
BRA
- inputs:
0 -> {non-negative}
(any number of those),n
(repetitions) - branch given nodes together (same inputs are passed to each node, but their outputs are kept separate)
- inputs:
!
THR
- inputs:
0 -> 1
- pass extra inputs through
- inputs:
branch()
- inputs:
A -> 1
,0 -> 2
- create as many nodes as the input array has values, replacing the "#" in the second input's op with each value, all branched together. e.g. array: [1, 2, 3] and op string "lowpass(1729, #)" creates the node
lowpass(1729, 1) ^ lowpass(1729, 2) ^ lowpass(1729, 3)
- inputs:
bus()
- same as branch() but bus nodes together instead
pipe()
- same as branch() but pipe nodes together
stack()
- same as branch() but stack nodes
sum()
- same as branch() but sum
product()
- same as branch() but
swap(usize, usize)
(non-negative numbers)- inputs:
0 -> 1
- swap the node without resetting the graph. arity of input node must match the ins/outs specified in the op string for the swapping to work. e.g.
swap(2,1)
will accept nodes with 2 inputs and 1 output and swaps them in place
- inputs:
out()
dac()
- inputs:
0 -> 1
- output given node to speakers (node must have 1 or 2 outputs)
- inputs:
in()
adc()
- node with 2 outputs corresponding to the quartz input device (mic input and the like)
var()
- node: 0 ins, 1 out
- create a shared variable audio node. its output is the value of this circle's num. must have an order >= 1
monitor()
- node: 1 in, 1 out (it passes audio through)
- create a monitor node. sets the value of this circle's num to the latest sample that passed through this node. must have an order >= 1
timer()
- when stacked with another node, this will maintain the current time of that node in this circle's number. must have an order >= 1
get()
- node: 1 in (index), 1 out (value)
- copies this circle's array into node so it can be indexed at audio-rate. input is index, output is the value at that index
quantize()
- inputs:
A -> 1
(array of steps to quantize to. must have at least 2 different values) - node: 1 in, 1 out
- quantize input to the nearest value in the given steps
- inputs:
feedback()
- inputs:
0 -> 1
(input node), [n -> 2
] (optional delay) - mixes outputs of given node back into its inputs (number of node ins/outs must match)
- node: ins and outs are the same as the input node
- inputs:
kr()
- inputs:
n
,0 -> 1
(input node) - node: 0 ins, 1 out
- tick the input node once every n samples (input node must have 0 ins and 1 out)
- inputs:
sr()
- inputs:
n
,0 -> 1
(input node) - set the sample rate for the input node
- inputs:
reset()
- inputs:
n
,0 -> 1
(input node (must have 0 ins, and 1 out)) - node: 0 ins, 1 out
- process the input node, but reset it every n seconds (rounded to nearest sample)
- inputs:
reset_v()
- inputs:
0 -> 1
(input node (must have 0 ins, and 1 out)) - node: 1 in, 1 out
- process the input node but reset it every n seconds. n is specified by the input to this node
- inputs:
trig_reset()
- inputs:
0 -> 1
(input node (must have 0 ins, and 1 out)) - node: 1 in, 1 out
- reset the given node whenever the input is non-zero
- inputs:
seq()
- inputs:
0 -> {non-negative}
(any number of those) - node: 4 ins (trig, node index, delay, duration), 1 out (output from sequenced nodes)
- sequences the given nodes and mixes their outputs at output (valid input nodes must have no inputs, and only one output). for every sample trig is non-zero, add an event for the node at index with the given delay and duration (in seconds, rounded to nearest sample)
- indexes are collected. e.g. if circle has three connections:
0 -> 1
0 -> 5
0 -> 8
this is gonna be a sequencer node that accepts indexes 0, 1, and 2. the node at 1 has index 0, node at 5 has index 1, and node at 8 has index 2. and only valid nodes are added.
- inputs:
select()
- inputs:
0 -> {non-negative}
(any number of those) - node: 1 in (index of selected node), 1 out (output from that node)
- create a node that switches between input nodes based on index
- inputs:
wave()
- inputs:
A -> 1
- node: 0 ins, 1 out
- create a wave player from the input array
- inputs:
audio nodes
refer to the fundsp readme, and docs for more details (in some cases)
sources
sine([float])
e.g.sine(440)
has no inputs, and outputs a sine wave at 440Hz.sine()
takes 1 input (frequency) and outputs sine wavesaw([float])
(same)square([float])
(same)triangle([float])
(same)organ([float])
(same)hammond([float])
(same)soft_saw([float])
(same)dsf_saw([float])
dsf_saw()
takes 2 inputs (frequency, and roughness [0...1]),dsf_saw(0.5)
takes only a freq input.dsf_square([float])
(same)pulse()
pulse wave oscillator (frequency, and duty cycle [0...1])brown()
brown noisepink()
pink noisewhite()
noise()
white noisezero()
silenceimpulse()
one sample impulselorenz()
rossler()
constant(float)
dc(float)
pluck(float, float, float)
(frequency, gain per sec, high freq damping) input is string excitation signalmls([float])
ramp()
ramp from 0 to 1 at input freq (phasor)
filters
allpole([float])
delay in samples. if not provided the node takes 2 inputs (signal, delay)pinkpass()
allpass([float], [float])
if 1 param is given, that's the q, and the node takes 2 input channels (signal, and hz) if 2 are given, that's the hz and q and the node only takes input signalbandpass([float], [float])
(same as allpass)bandrez([float], [float])
(same)bell([float, float], [float])
if 2 params are given, they're (q, gain), if 3 are give, they're (hz, q, gain), if none, the node takes 4 channels (input, hz, q, gain)biquad(float, float, float, float, float)
butterpass([float])
e.g.butterpass()
takes 2 inputs (signal, and hz).butterpass(1729)
takes 1 inputdcblock([float])
if no param is provided, the cutoff is 10Hzfir(float [float], [float], ...)
(up to 10 weights)fir3(float)
param is gain at nyquistfollow(float, [float])
attack and release response timeshighpass([float], [float])
(same as allpass)highpole([float])
(same as butterpass)highshelf([float, float], [float])
(same as bell)lowpass([float], [float])
(same as allpass)lowpole([float])
(same as butterpass)lowrez([float], [float])
(same as allpass)lowshelf([float, float], [float])
(same as bell)moog([float], [float])
(same as allpass)morph([float, float, float])
(hz, q, morph [-1...1] (-1 = lowpass, 0 = peak, 1 = highpass)) if not provided, the node takes 4 inputs (signal, hz, q, morph)notch([float], [float])
(same as allpass)peak([float], [float])
(same)resonator([float, float])
(hz, bandwidth) if not provided the node takes 3 inputs (signal, hz, bandwidth)
channels
sink()
eats an input channelpass()
takes an input channel and passes it unchangedchan(float, float,...)
shortcut for stacking pass/sink nodes. e.g.chan(0,1,0,1,2)
is the same assink() | pass() | sink() | pass() | pass()
(non-zero is pass)pan([float])
e.g.pan(0)
pan input (mono to stereo)pan()
takes 2 inputs (signal, and pan [-1...1])join(float)
float can be [2...8] e.g.join(8)
takes 8 inputs and averages them into 1 outputsplit(float)
float can be [2...8]split(8)
takes 1 input and copies it into 8 outputsreverse(float)
float can be [2...8] reverse the order of channels
envelopes (all subsampled at ~2 ms)
adsr(float, float, float, float)
xd([float])
(this is just anexp(-t*input)
)xD([float], [float])
e.g.xD()
takes 2 inputs (time and curvature)xD(5)
takes 1 input specifying the decay time with a curvature of 5.xD(10, 0.5)
is a decay over 10 seconds with a curvature of 0.5.ar([float, float], [float, float])
if there are no params it takes 4 inputs, if there are 2 params they are the curvature of attack and release and the node takes 2 inputs specifying the times, if there are 4 params they are (attack time, attack curvature, release time, release curvature)
other
tick()
one sample delayshift_reg()
2 ins (trigger signal, input signal), 8 outs (outputs of the shift register)meter(peak/rms, float)
e.g.meter(rms, 0.5)
rms(peak, 2)
chorus(float, float, float, float)
(seed, separation, variation, mod frequency)declick([float])
e.g.declick()
10ms fade in,declick(2)
2 second fade indelay(float)
e.g.delay(2)
2 second delayhold(float, [float])
e.g.hold(0.5)
takes 2 inputs (signal, and sampling frequency) with variability 0.5,hold(150, 0)
takes one input and samples it at 150Hz with variability 0limiter(float, float)
look ahead limiter. first param is attack time, second is release time (in seconds)limiter_stereo(float, float)
(same)reverb_stereo(float, [float], [float])
(room size, reverberation time, damping) when damping isn't provided it defaults to 1, time defaults to 5tap(float, float)
(min delay time, max delay time)tap_linear(float, float)
(same)samp_delay(float)
argument is max delay time (in samples), node takes 2 inputs (signal, delay time in samples)
math
add(float, [float], [float], ...)
(up to 8 params)sub(float, [float], [float], ...)
(same)mul(float, [float], [float], ...)
(same)div(float, [float], [float], ...)
(same)rotate(float, float)
t()
time since the node started processing (subsampled every ~2 ms)rise()
one sample trigger when there's a rise in inputfall()
same but fall>([float])
e.g.>()
takes 2 inputs and compares them.>(3)
takes one input and compares against 3<([float])
(same from this one...)==([float])
!=([float])
>=([float])
<=([float])
min([float])
max([float])
pow([float])
mod([float])
rem([float])
log([float])
bitand([float])
bitor([float])
bitxor([float])
shl([float])
shr([float])
(.. all the way to this one)clip([float, float])
e.g.clip()
takes 1 input and clips to [-1...1],clip(-5, 5)
clips to [-5...5]wrap(float, [float])
wrap between 2 numbers (or between 0 and x if only one number is given)mirror(float, float)
mirror (wave fold) between two valueslerp([float, float])
e.g.lerp()
takes 3 inputs (a, b, t)lerp(3,5)
takes one input (t)lerp11([float, float])
(same..)delerp([float, float])
delerp11([float, float])
xerp([float, float])
xerp11([float, float])
dexerp([float, float])
dexerp11([float, float])
(.. same)abs()
signum()
floor()
fract()
ceil()
round()
sqrt()
exp()
exp2()
exp10()
exp_m1()
ln_1p()
ln()
log2()
log10()
sin()
cos()
tan()
asin()
acos()
atan()
sinh()
cosh()
tanh()
asinh()
acosh()
atanh()
atan2()
hypot()
rfft(n)
n is a power of two between 2 and 32768 (inclusive)ifft(n)
samepol()
car()
deg()
rad()
recip()
squared()
cubed()
dissonance()
dissonance_max()
db_amp()
amp_db()
a_weight()
m_weight()
spline()
spline_mono()
soft_sign()
soft_exp()
soft_mix()
smooth3()
smooth5()
smooth7()
smooth9()
uparc()
downarc()
sine_ease()
sin_hz()
cos_hz()
sqr_hz()
tri_hz()
semitone_ratio()
rnd()
rnd2()
spline_noise()
fractal_noise()
i hope that everyone will become friends
- tools / dependencies:
- rust https://github.com/rust-lang/rust
- bevy https://github.com/bevyengine/bevy
- bevy_pancam https://github.com/johanhelsing/bevy_pancam
- bevy-inspector-egui https://github.com/jakobhellermann/bevy-inspector-egui/
- fundsp https://github.com/SamiPerttu/fundsp
- cpal https://github.com/rustaudio/cpal
- copypasta https://github.com/alacritty/copypasta
- serde https://github.com/serde-rs/serde
- rosc https://github.com/klingtnet/rosc
- crossbeam https://github.com/crossbeam-rs/crossbeam
- wasm-bindgen https://github.com/rustwasm/wasm-bindgen
- bevy_github_ci_template https://github.com/bevyengine/bevy_github_ci_template
- tracy https://github.com/wolfpld/tracy
- gnuplot http://gnuplot.info/
- calc https://github.com/lcn2/calc
- vim https://github.com/vim/vim
- void linux https://voidlinux.org/
- learning / inspiration / used for a while:
- modulus salomonis regis https://github.com/AriaSalvatrice/AriaModules
- network https://github.com/JustMog/Mog-VCV
- csound https://csound.com
- faust https://faust.grame.fr
- plugdata https://github.com/plugdata-team/plugdata
- bevy-cheatbook: https://github.com/bevy-cheatbook/bevy-cheatbook
- bevy best practices https://github.com/tbillington/bevy_best_practices
- knyst https://github.com/ErikNatanael/knyst
- shadplay https://github.com/alphastrata/shadplay
- rust-ants-colony-simulation https://github.com/bones-ai/rust-ants-colony-simulation
- bevy_fundsp https://github.com/harudagondi/bevy_fundsp
- bevy_kira_audio https://github.com/NiklasEi/bevy_kira_audio
- bevy_mod_picking https://github.com/aevyrie/bevy_mod_picking/
- bevy_vector_shapes https://github.com/james-j-obrien/bevy_vector_shapes
- anyhow https://github.com/dtolnay/anyhow
- assert_no_alloc https://github.com/Windfisch/rust-assert-no-alloc
- bevy_mod_osc https://github.com/funatsufumiya/bevy_mod_osc
quartz is free and open source. all code in this repository is dual-licensed under either:
- MIT License (LICENSE-MIT or http://opensource.org/licenses/MIT)
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
at your option.
unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
https://www.youtube.com/playlist?list=PLW3qKRjtGsGZC7V4eKU_tNwszVZAYvKow