PbindFx works like a normal Pbind of event type 'note' in most regards, but with the additional option to define a number of effects. Their order and parameters can also be defined with patterns which allows a great flexibility: for each event an arbitrary multichannel effect graph can be applied, mixing sequential and parallel arrangement ad libitum. This requires a relatively high amount of resource management: bus allocation, routing and node ordering as well as delayed cleanup have to be done for each event. All necessary bookkeeping is done automatically, for some critical parameters though it's the user's responsibility to pass meaningful values (e.g. cleanupDelay for reverb has to be defined sufficiently high, otherwise reverb synth and audio bus might be freed before reverb has ended). There is always a tradeoff between flexibility and processing effort, if you won't change fx parameters on a per-event base or you won't reorder your effect arrangement, then you might prefer playing effects from and to predefined buses and control them otherwise, e.g. per LFOs (additionally possible also with PbindFx) or dedicated setting streams, see PmonoPar and PpolyPar. However with the strategy of effects bound to buses you have the same effect arrangements and parameters concerning all source signals sent to the same bus, more variation with such setups needs more (pre-)definition of buses, whereas with PbindFx overlapping events can be processed with different effect arrangements and parameters with no explicit effort. One possible application of PbindFx is applying effects per grain, for the project kitchen_studies I documented the source code of a fixed media piece in six parts, using this technique.
WARNING: As bus allocation is done dynamically per event, there is a circumvented, but still potential danger of creating feedback loops. To prevent this, additional "zero synths" are started with bus-reading fx synths, playing a zero signal with ReplaceOut to the buses in question, they are placed before those fx / source synth(s), which will play there too. For all test examples, even with deliberately bad values, zero synths turned out to be an effective way to block unwanted input signals and feedback, as they last as long as fx / source synths (they even overlap them a bit). However, with extraordinary parameter values for timing, improperly defined fx / source synths, sloppy audio bus mapping and / or parallel actions that affect resource management globally, feedback, as in any situation of heavy bus repatching, can not be totally excluded. Be aware of that, avoid high levels and be careful with headphones !
Creates a new PbindFx object.
Arguments:
pbindData |
SequenceableCollection of Pbind's key/value pairs or event pattern. Passing a list saves explicitely typing Pbind, but passing an event pattern is more flexible, as it allows replacement (Ex.7), event pattern filtering (Ex.3a) and similar operations.
specific keys:
- \fxOrder – For each event it can be given in three ways, for single effects and sequential ordering it may be
- an Integer, or
- a SequenceableCollection of Integers, indicating the effect order. Effect counting starts with 1, 0 means no effect (default). E.g. if three effects are passed to fxData, [1], [2], [3], [1, 2], [1, 3], [2, 3], [2, 1], [3, 1], [3, 2], [1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1], would represent all possible effect orders with each effect applied once. Passing single Integers is equivalent to passing them in an array. Data concerning one event can also be given as
- a Ref object containing an IdentityDictionary, representing the effect graph. Keys of the dictionary – single Integers – are identified with effects of fxData, values of the dictionary might be single Integers or SequenceableCollections of Integers, indicating a branching of effects (whenever you want a branching of effects or routing two different effects to a third one, the input must be given as a ref'd IdentityDictionary). Within the effect graph an explicit direct out can be denoted with the Symbol \o, 0 represents the source. The graph must be acyclic, a check is performed. fxOrder can also be a Pattern, sequencing effect orders of the forms (1) - (3). See Principle of operation and Ex.10a, Ex.10b, Ex.10c.
- \cleanupDelay – Number or Pattern to generate Numbers, taken as seconds. Its meaning differs, depending if source synth (passed by \instrument) has a gated or fixed length envelope. If a gate control is encountered in the SynthDef description, a gated envelope is assumed. In the latter case this arg determines the earliest time after release when cleanup may start. With a fixed-length envelope it determines the earliest time after synth start when cleanup may start. With effects defined, cleanupDelays of source and effects are summarized. Defaults to the class variable defaultSourceCleanupDelay which defaults to 0.3. Typically you would take the releaseTime of the source synth's / synthdef's gated envelope, or the maximum overall length of the fixed-length envelope, each plus a small delta.
- \cleanupClock – The clock on which freeing of audio buses is scheduled with SkipJack objects. As the clock should survive CmdPeriod its permanent flag must be set to true. Per default the SystemClock is used, but for certain cases, e.g. granulation you might want to pass a TempoClock with higher queueSize, see examples below.
- \cleanupDt – Number or Pattern to generate Numbers, taken as seconds. Determines the delta time for cleanup with SkipJack objects. In those intervals it is checked whether event-specific cleanup delaytimes have been reached and cleanup (freeing buses) should be performed. Defaults to the class variable defaultCleanupDt which defaults to 0.2. With higher values more SkipJack objects have to be scheduled at the same time, although with fewer activity.
- \freePerGroup – Boolean, defaults to false. Determines if effects and zero synths of the chain should be freed separately or removed at the end of the chain by freeing the enclosing group. With more effects and longer cleanupDelays the latter leads to a larger number of parallel synths and might be wasteful thus. On the other hand it can be a useful option with short events and cleanupDelays, e.g. in case of granulation. See below timing scheme for a more detailled description.
- \otherBusArgs – Takes SequenceableCollection of Symbols, defaults to nil. For all passed symbols the source synth is enabled to read from and write to external buses, which are passed to an arg of that name, LocalIn and LocalOut are allowed anyway. Per default a restrictive check of synth I/O ugens and matching is performed in order to prevent unintended reading from resp. writing to buses. With this option you can allow reading and writing in a controlled way, see Ex.6a and Ex.6b.
|
... fxData |
SequenceableCollection of Pbind's key/value pairs defining effect sequencing or event pattern. Passing a list saves explicitely typing Pbind, but passing an event pattern is more flexible, as it allows replacement (Ex.7), event pattern filtering (Ex.3a) and similar operations.
specific keys:
- \fx – Symbol or Pattern to generate Symbols. Determines the effect synthdef.
- \cleanupDelay – Number or Pattern to generate Numbers, taken as seconds. Determines the earliest time after effect node delay when cleanup, including freeing the effect synth, may start. With effects defined, cleanupDelays of source and effects are summarized. Defaults to the class variable defaultFxCleanupDelay which defaults to 0.05. Typically you would take the maximum delay of the effect synth plus a small delta.
- \otherBusArgs – Takes SequenceableCollection of Symbols, defaults to nil. For all passed symbols the fx synth is enabled to read from and write to external buses, which are passed to an arg of that name, LocalIn and LocalOut are allowed anyway. Per default a restrictive check of synth I/O ugens and matching is performed in order to prevent unintended reading from resp. writing to buses. With this option you can allow reading in a controlled way, see Ex.6a and Ex.6b.
|
Get and set the value of this class variable. Defaults to 0.3.
Get and set the value of this class variable. Defaults to 0.2.
Get and set the value of this class variable. Defaults to 0.05.
This can typically be achieved by passing an array [i, j] to \fxOrder, where i and j denote arbitrary unequal positive effect numbers (numbers smaller or equal than fxData's size).
Fig. 1
Effects FX#1 and FX#2 read from buses BUS#1 and BUS#2 which are reserved for the duration of the event plus a cleanup time. BUS#1 and BUS#2 get their data from source synth SRC resp. FX#1. "Zero synths" Z#1 and Z#2 are placed before them in node order, they are lasting as long as (in fact a bit longer than) FX#1 and FX#2, playing zero signals to BUS#1 and BUS#2 with ReplaceOut and cancelling out possible residual signals on those buses, thus blocking feedback. BUS#1 and BUS#2 might be multichannel buses, in that case a number of mono zero synths is established for each Z#i. Two dedicated groups are generated for each event: FXGRP is the container for all event-generated synths and SRCGRP is placed at its head. Z#1 is placed at the head of SRCGRP, other zero synths and effects are sequentially placed upwards from the tail of FXGRP, interlaced in the way described below. Finally source synth(s) are placed at tail of SRCGRP (the group passed further to the note event).
node ordering for n = 2
Fig. 2
You can also pass a group (or a pattern of groups) to PbindFx, in that case FXGRP is related to the group depending on \addAction (default \addToHead):
general node ordering with n sequentially applied effects for n > 2 group GRP passed to PbindFx with key \group, default addAction \addToHead
Fig. 3
- GRP:
- FXGRP:
- SRCGRP:
- Z#2
- FX#1
...
...
- Z#n
- FX#n-1
- FX#n
Note that effect order is arbitrary per event, determined by key \fxOrder, in these examples FX#i denote the order in the regarded event, not the order in which the effects are passed to PbindFx. The whole process is implemented via dedicated event type 'pbindFx', an extension of event type 'note', which is chosen with PbindFx by default. The following schemes refer to server-side timing, additional lang-side offset might be passed with lag (in seconds) to ensure proper timing for sequences combining delayed and non-delayed effects e.g. including occasional echo as in several of the examples below.
Fig. 4
- t#0: start SRC, Z#i, FX#i and groups with server-side ordering as described in routing scheme, in fact Z#i are started slightly earlier.
- t#1: begin of SRC envelope release period, caused by a release message (set gate arg to 0)
- t#2: end of SRC envelope release period, probably end of source synth (doneAction = 2)
- t#3: supposed latest end of source synth due to given cleanupDelay of source, added to t#1 (cleanupDelay might be passed as a constant maximum release time for all events)
- t#4:
- ~freePerGroup == false (default): freeing of FX#1 and Z#1, the latter a bit later
- ~freePerGroup == true: do nothing
- t#5: free FXGRP, thus also FX#2 and Z#2.
For longer effect chains with n effects the case distinction of t#4 applies to all pairs Z#i / FX#i for 1 < i < n: with ~freePerGroup == true they all are freed at last by freeing the group.
Fig. 5
- t#0: start SRC, Z#i, FX#i and groups with server-side ordering as described in routing scheme, in fact Z#i are started slightly earlier.
- t#1: begin of SRC envelope release period, caused by the synth itself (no setting gate to 0)
- t#2: end of SRC envelope release period, probably end of source synth (doneAction = 2)
- t#3: supposed latest end of source synth due to given cleanupDelay of source, added to t#0 (cleanupDelay might be passed as a constant maximum envelope length for all events)
- t#4 - t#5: as with gated envelope
The most simple case of applying two effects in parallel can be triggered by passing `(0: [1, 2]) to fxOrder. Effect numbers occurring in arrays of dictionary values cause a routing from these effects to out. Symbol \o (for destination out) can also be passed explicitely. Branching like this requires the use of a split synth which routes the output to the demanded multitude of fx buses. A suitable predefined split synthdef is chosen according to the number of branches and the channel number of the signal to be splitted – currently both is limited by 8, however synths, which are only playing to out directly, might have an arbitrary number of output channels. Different to sequential fx processing, both fx bus zero synths have to be added before the source, additional zero synth(s) for the split bus are prepended.
Fig. 6
The following effect graph can be established by fxOrder `(0: [1, 2, 3], 2: [4, \o], 3: 2), split synths, zero synths (for fx buses and split buses) are omitted in this scheme. For each (acyclic) fx graph a topological order is calculated (here it could e.g. be [0,1,3,2,4]) and the node ordering, including FXGRP and SRCGRP, similar to the sequential case, is derived accordingly. Some small differences, especially concerning the cleanup delay times, have to be taken into account, these details are omitted here.
Fig. 7
- Source instrument and effect SynthDefs must be known to SynthDescLib.global (e.g. by creating SynthDefs with methods 'add' or 'store')
- Fx SynthDefs must be defined with arg 'out' for the outbus index, arg 'in' for inbus index and use In.ar(in, ...) within the SynthDef.It's up to the user whether to define the effect with dry/wet mix option. Effect synths don't need to have an envelope, their freeing is handled by the event type function.
- Effect chains must be defined properly in terms of in/out channel number. For each event the bus matching of the effect chain is checked, following these conventions:
- For source and fx SynthDefs there must be only one out ugen using 'out' as bus arg, LocalOut is allowed, other out ugens can be admitted by \otherBusArgs.
- Source SynthDefs must not have in ugens, except LocalIn or they are admitted by \otherBusArgs.
- Fxs must read from buses with ugen In.ar(in, ...) refering to the bus arg 'in', there must not be other in ugens within the fx SynthDef except LocalIn or they are admitted by \otherBusArgs, see Ex.6a and Ex.6b.
- Number of out channels of preceeding source / fx synth must not be greater than the number of the following fx synth's in channels, this is checked for all pairs of an fx graph.
- Mismatches of above points lead to errors.
- Checks of (3) are also based on info in SynthDescLib.global. It is possible to replace SynthDefs on the fly (Ex.7a, Ex.7b), in that case I'd recommend to use also methods 'add' or 'store' – e.g. in case of using 'send' for redefinition no bus matching check is performed and a possibly wrong routing would be undetected.
- As shown in above graphics the source synth's cleanupDelay is interpretated differentely with gated and non-gated (fixed-length) envelopes. This is done automatically by looking for a 'gate' arg in the SynthDef's SynthDesc. It's the users responsibility to use the conventional arg 'gate' for release in the SynthDef and omit a 'gate' arg with SynthDefs employing fixed-length envelopes.
- As with normal event patterns the source Pbind / Pbind pairs may be defined with arrays to produce multiple synths per event, then the whole source signal is routed to the fx graph (see Ex.2b, Ex.2c and others). fxData pairs can also be defined with arrays, which causes parallel processing per node ("implicit parallelism"), see Ex.4a. For applying different fxs to parallel events (resp. chords) use parallel PbindFxs (Ex.3a, Ex.3b). It is of course also possible that fx synths have array args and fx patterns are passing them with the double-bracketing convention used for these cases, see Event_patterns_and_array_args.
- Source instrument and effect SynthDefs are allowed to have args of same name. There is no problem as key/value pairs are stored in separate lists/events.
- PbindFx is implemented per automatically chosen effect type 'pbindFx', which employs event type 'note', thus an event type must neither be passed to pbindData nor to fxData.
- The value conversion framework can be used for fxData, e.g. by passing \midinote instead of \freq or \db instead of \amp as with Pbind, see Ex.9. Other than with Pbind, freq and amp event defaults are not passed to fx synths, if no such values are passed via fxData. In that case default values of fx synths are taken.
- While playing PbindFx you should not play with allocation affecting private buses. Freeing buses is done by SkipJack objects, so you should not forcefully stop all SkipJack objects while playing PbindFx.
- Keys \group and \addAction may be passed, they determine how the group enclosing event-generated synths is related to the passed group, see scheme Principle of operation.
- Zero synths are using the SynthDefs 'pbindFx_zero' and 'pbindFx_splitZero' which are written to disk at startup time, as well as split synths pbindFx_split_axb for a = 2,...,8 and b = 1,...,8. Of course these SynthDefs shouldn't be deleted or exchanged.
- You might encounter the error message "Meta_Bus:audio: failed to get an audio bus allocated." As audio buses for effect chains are allocated and freed per event a higher number of private audio buses is likely to be required (more than default 128). You might also encounter the error message "exception in real time: alloc failed, increase server's memory allocation (e.g. via ServerOptions)". Hence it's recommended to set the concerned server options before (re-)booting and working with PbindFx, e.g.:
- You might encounter the warning "Scheduler queue is full." Per default delayed freeing of buses is scheduled on SystemClock, which defaults to queueSize 1024. In case of granulation and/or large cleanup delays it's recommended to pass a cleanup clock with sufficiently large queueSize via pbindData, its permanent flag must be set to true, e.g.:
Note that this clock keeps on running and survives CmdPeriod, you should explicitely stop it if you don't need it anymore, hence it should be stored in a variable. In case you haven't done that you can still stop all TempoClocks and remove them from CmdPeriod with
- Cleanup delays for source and effects (or, if not passed, their default values) should be sufficiently large (see description of \cleanupDelay ), otherwise effects and audio buses might be freed too early and signals cut.
- Some effects, like echo, introduce a delay. If sequencing mixes delayed events with non-delayed events, entries of the latter have to be delayed accordingly to preserve correct timing, this can be done by setting an offset in seconds with \lag. See examples with echo below.
SynthDefs from Ex. 1a plus SynthDef variation with adsr args
PbindFx using spat and echo effects. Especially relevant keys: \fxOrder which determines fx sequencing and \cleanupDelay for proper releaseTimes of source and effects.
SynthDefs from Ex.1a, see also extended server resources defined there.
If more than one synth per event is produced (here by key 'midinote'), the effect chain is applied to all of them, see Ex.3 for applying different effects to parallel synths.
SynthDefs from Ex.1a, see also extended server resources defined there.
Additional use of rests, reverb added. Here the reverb usage is deliberately wasteful, see Ex.2d for an alternative. The use of Pn + Pshuf (or equivalently Pshufn) gives balanced random variation for several key streams.
SynthDefs from Ex.1a, see also extended server resources defined there.
If effects have a long cleanup delay, you will get a possibly large number of overlapping effect chains. E.g. in Ex.2c many reverb synths can be there in parallel, the decayTime is controlled by Pwhite(3.0, 10), so it might well be that reverbs with decayTimes 7.95, 8, and 8.1 are instantiated in parallel, which doesn't make much difference and is quite wasteful. Reverb is often placed at the last position of the effect chain, so a more efficient approach would be the following: do all effect sequencing without reverb with PbindFx and pipe the overall out to a permanently running reverb.
SynthDefs from Ex.1a, see also extended server resources defined there.
This can be done with parallel PbindFxs.
Here the option of passing a source Pbind instead of a list of Pbind pairs can be used.
SynthDefs from Ex.1a, see also extended server resources defined there.
Equivalent to Ex.3a, but might look more straight, as fxOrder pair already generated with PbindFx Function.
SynthDefs from Ex.1a, see also extended server resources defined there.
Tempo control works as with Pbind and can be influenced by a number of parameters. As cleanup parameters of PbindFx are passed in seconds, tempo control can be done independent from making cleanup time changes (though you might do so as well). The use of a dedicated TempoClock as master tempo control is a good idea, setting tempo for individual streams can be done with 'stretch'. The following example employs a PbindFx generator Function, the tempo of streams can be controlled individually and generally.
SynthDefs from Ex.1a, see also extended server resources defined there.
This means the use of buses, which are not internally used by PbindFx's event Function. As shown in Ex.2d it can be useful to route PbindFx's out to an external reverb. Vice versa data can be read from external buses, from source synths as well as from fx synths.
Audio routing benefits from the possibility to pass groups to PbindFx. Then all temporary groups, generated during playing the PbindFx, are enclosed by it and node order can be clearly defined. For audio bus routing better use In.ar than mapping with asMap, that way matching of ins and outs of fx chains is checked and it avoids issues with using asMap and stopping the external source (occuring at least in SC 3.6.6).
Spat SynthDef from Ex.1a, see also extended server resources defined there.
Spat SynthDef from Ex.1a, see also extended server resources defined there.
Control the amount / mix of an effect continously.
Spat SynthDef from Ex.1a, see also extended server resources defined there.
Replacement can affect certain key streams only or whole source resp. fx patterns, the latter can be done with using the option of passing source / fx patterns instead of lists.
This can be done with Pbind + Pdefn or Pbind + PL, a specific possibility with PbindFx is the replacement of \fxOrder.
SynthDefs from Ex.1a, see also extended server resources defined there.
SynthDefs from Ex.1a, see also extended server resources defined there.
SynthDefs from Ex.1a, see also extended server resources defined there.
Control of fx params with VarGui, control of fx sequencing by code.
SynthDefs from Ex.1a, see also extended server resources defined there.
Effects can produce their own characteristic frequencies. For this it can be practical to use Event's value conversion framework.
SynthDefs from Ex.1a, see also extended server resources defined there.
Here source is routed to echo #1 and echo #2 in parallel, echo #1 (fx index 2) is a "classical" echo whereas echo #2 (fx index 3), due to short echoDelta, results in an additional frequency. The output of echo #2 is routed to a wah-wah, echo #1 directly to out.
SynthDefs from Ex.1a, see also extended server resources defined there.
A generalized modulating effect node has two ins: carrier and modulator. Fx convention of PbindFx demands one single In ugen per fx synth, but two ins can simply be handled by a 2-channel In ugen and hard-panned input signals.
Spat SynthDef from Ex.1a, see also extended server resources defined there.
SynthDefs from Ex. 10b, spat SynthDef from Ex.1a, see also extended server resources defined there.