Fluxa is an optional addition to fluxus which add audio synthesis and sample playback. It’s also an experimental non-deterministic synth where each ‘note’ is it’s own synth graph.
Fluxa is a framework for constructing and sequencing sound. It uses a minimal and purely functional style which is designed for livecoding. It can be broken down into two parts, the descriptions of synthesis graphs and a set of language forms for describing procedural sequences.
(Fluxa is also a kind of primitive homage to supercollider – see also rsc, which is a scheme binding to sc)Example:
(require fluxus-017/fluxa) (seq (lambda (time clock) (play time (mul (sine 440) (adsr 0 0.1 0 0))) 0.2))
Plays a sine tone with a decay of 0.1 seconds every 0.2 seconds
Fluxa has decidedly non-deterministic properties – synth lifetime is bound to some global constraints:
- A fixed number of operators, which are recycled (allocation time/space constraint)
- A maximum number of synths playing simultaneously (cpu time constraint)
What this means is that synths are stopped after a variable length of time, depending on the need for new operators. Nodes making up the synth graph may also be recycled while they are in use – resulting in interesting artefacts (which is considered a feature!)
(play time node-id)
Schedules the node id to play at the supplied time. This is for general musical use.
Plays the node id as soon as possible – mainly for testing
All these commands create and return a nodes which can be played. Parameters in the synthesis graph can be other nodes or normal number values.
A sine wave at the specified frequency
A saw wave at the specified frequency
A square wave at the specified frequency
(sample sample-filename frequency)
Loads and plays a sample – files can be relative to specified searchpaths. Samples will be loaded asynchronously, and won’t interfere with real-time audio.
(adsr attack decay sustain release)
Generates an envelope signal
(add a b) (sub a b) (mul a b) (div a b)
Remember that parameters can be nodes or number values, so you can do things like:
(play time (mul (sine 440) 0.5))
(play time (mul (sine 440) (adsr 0 0.1 0 0)))
(mooghp input-node cutoff resonance) (moogbp input-node cutoff resonance) (mooglp input-node cutoff resonance) (formant input-node cutoff resonance)
Does what is says on the tin
(eq 1 1 1)
Tweak bass, mid, treble
Change the maximum concurrent synths playing – default is a measly 10
Add a path for sample loading
The panic command – deletes all synth graphs and reinitialises all the operators – not rt safe
Fluxa provides a set of forms for sequencing.
(seq (lambda (time clock) 0.1))
The top level sequence – there can only be one of these, and all code within the supplied procedure will be called when required. The time between calls is set by the returned value of the procedure – so you can change the global timing dynamically.
The parameters time and clock are passed to the procedure – time is the float real time value in seconds, to be passed to play commands. It’s actually a little bit ahead of real time, in order to give the network messages time to get to the server. You can also mess with the time like so:
(play (+ time 0.5) ...)
Which will offset the time half a second into the future. You can also make them happen earlier – but only a little bit. Clock is an ever increasing value, which increments by one each time the procedure given to seq is called. The value of this is not important, but you can use zmod, which is simply this predefined procedure:
(define (zmod clock v) (zero? (modulo clock v)))
Which is common enough to make this shortening helpful, so:
(if (zmod clock 4) (play (mul (sine 440) (adsr 0 0.1 0 0))))
Will play a note every 4 beats.
A utility for mapping note numbers to frequencies (I think the current scale is equal temperament) [todo: sort scala loading out]
(seq (lambda (time clock) (clock-map (lambda (n) (play time (mul (sine (note n)) (adsr 0 0.1 0 0)))) clock (list 10 12 14 15)) 0.1))
clock-map maps the list to the play command each tick of the clock – the list can be used as a primitive sequence, and can obviously be used to drive much more than just the pitch.
(seq (lambda (time clock) (clock-switch clock 128 (lambda () (play time (mul (sine (note n)) (adsr 0 0.1 0 0)))) (lambda () (play time (mul (saw (note n)) (adsr 0 0.1 0 0))))) 0.1))
clock-switch switches between the procedures every 128 ticks of the clock – for higher level structure.
A osc message can be sent to the client for syncing for collaborative performances the format of the sync message is as follows:
/sync [iiii] timestamp-seconds timestamp-fraction beats-per-bar tempo
When syncing, fluxa provides you with two extra global definitions:
sync-clock : a clock which is reset when a /sync is received sync-tempo : the current requested tempo (you are free to modify or ignore it)
[note: there is a program which adds timestamps to /sync messages coming from a network, which makes collaborative sync work properly (as it doesn’t require clocks to be in sync too) email me if you want more info]
- Record execution – cyclic graphs wont work
- Permanent execution of some nodes – will fix delay/reverb