partikkel

partikkel — Granular synthesizer with "per grain" control over many of its parameters. Has a sync input to sychronize its internal grain scheduler clock to an external clock source.

Description

partikkel was conceived after reading Curtis Roads' book "Microsound", and the goal was to create an opcode that was capable of all time-domain varieties of granular synthesis described in this book. The idea being that most of the techniques only differ in parameter values, and by having a single opcode that can do all varieties of granular synthesis makes it possible to interpolate between techniques. Granular synthesis is sometimes dubbed particle synthesis, and it was thought apt to name the opcode partikkel to distinguish it from other granular opcodes.

Some of the input parameters to partikkel is table numbers, pointing to tables where values for the "per grain" parameter changes are stored. partikkel can use single-cycle or complex (e.g. sampled sound) waveforms as source waveforms for grains. Each grain consists of a mix of 4 source waveforms. Individual tuning of the base frequency can be done for each of the 4 source waveforms. Frequency modulation inside each grain is enabled via an auxillary audio input (awavfm). Trainlet synthesis is available, and trainlets can be mixed with wavetable based grains. Up to 8 separate audio outputs can be used.

Syntax

a1 [, a2, a3, a4, a5, a6, a7, a8] partikkel agrainfreq, \
              kdistribution, idisttab, async, kenv2amt, ienv2tab, ienv_attack, \
              ienv_decay, ksustain_amount, ka_d_ratio, kduration, kamp, igainmasks, \
              kwavfreq, ksweepshape, iwavfreqstarttab, iwavfreqendtab, awavfm, \
              ifmamptab, kfmenv, icosine, ktraincps, knumpartials, kchroma, \
              ichannelmasks, krandommask, kwaveform1, kwaveform2, kwaveform3, \
              kwaveform4, iwaveamptab, asamplepos1, asamplepos2, asamplepos3, \
              asamplepos4, kwavekey1, kwavekey2, kwavekey3, kwavekey4, imax_grains \
              [, iopcode_id]

Initialization

idisttab -- function table number, distribution for random grain displacements over time. The table values are interpreted as "displacement amount" scaled by 1/grainrate. This means that a value of 0.5 in the table will displace a grain by half the grainrate period. The table values are read randomly, and scaled by kdistribution. For realistic stochastic results, it is advisable not to use a too small table size, as this limits the amount of possible displacement values. This can also be utilized for other purposes, e.g. using quantized displacement values to work with controlled time displacement from the periodic grain rate. If kdistribution is negative, the table values will be read sequentially. A default table might be selected by using -1 as the ftable number, for idisttab the default uses a zero distribution (no displacement).

ienv_attack -- function table number, attack shape of grain. Needs extended guard point. A default table might be selected by using -1 as the ftable number, for ienv_attack the default uses a square window (no enveloping).

ienv_decay -- function table number, decay shape of grain. Needs extended guard point. A default table might be selected by using -1 as the ftable number, for ienv_decay the default uses a square window (no enveloping).

ienv2tab -- function table number, additional envelope applied to grain, done after attack and decay envelopes. Can be used e.g. for fof formant synthesis. Needs extended guard point. A default table might be selected by using -1 as the ftable number, for ienv2tab the default uses a square window (no enveloping).

icosine -- function table number, must contain a cosine, used for trainlets. Table size should be at least 2048 for good quality trainlets.

igainmasks -- function table number, gain per grain. The sequence of values in the table is as follows: index 0 is used as a loop start point in reading the values, index 1 is used as a loop end point. Remaining indices contain gain values (normally in range 0 - 1, but other values are allowed, negative values will invert phase of waveform inside grain) for a sequence of grains, these are read at grain rate enabling exact patterns of "gain per grain". The loop start and end points are zero based with an origin at index 2, e.g. a loop start value of 0 and loop end value of 3 will read indices 2,3,4,5 in a loop at grain rate. A default table might be selected by using -1 as the ftable number, for igainmasks the default disables gain masking (all grains are given a gain masking value of 1).

ichannelmasks -- function table number, see igainmasks for a description of how the values in the table are read. Range is 0 to N, where N is the number of output channels minus 1. A value of zero will send the grain to audio output 1 from the opcode. Fractional values are allowed, e.g. a value of 3.5 will mix the grain equally to outputs 4 and 5. The user is responsible for keeping the values in range, no range checking is done. The opcode will crash with out of range values. A default table might be selected by using -1 as the ftable number, for ichannelmasks the default disables channel masking (all grains are given a channel masking value of 0 and are sent to partikkel audio out 1).

iwavfreqstarttab -- function table number, see igainmasks for a description of how the values in the table are read. Start frequency multiplicator for each grain. Pitch will glide from start frequency to end frequency following a line or curve as set by ksweepshape. A default table might be selected by using -1 as the ftable number, for iwavfreqstarttab the default uses a multiplicator of 1, disabling any start frequency modification.

iwavfreqendtab -- function table number, see iwavfreqstarttab. End frequency multiplicator for each grain. A default table might be selected by using -1 as the ftable number, for iwavfreqendtab the default uses a multiplicator of 1, disabling any end frequency modification.

ifmamptab -- function table number, see igainmasks for a description of how the values in the table are read. Frequency modulation index per grain. The signal awavfm will be multiplied by values read from this table. A default table might be selected by using -1 as the ftable number, for ifmamptab the default uses 1 as the index multiplicator, enabling fm for all grains.

iwaveamptab -- function table number, the indices are read in a similar way to what is used for igainmasks. Index 0 is used as a loop start point, and index 1 is used as a loop end point. The rest of the indices are read in groups of 5, where each value represent a gain value for each of the 4 source waveforms, and the 5th value represent trainlet amplitude. A default table might be selected by using -1 as the ftable number, for iwaveamptab the default uses an equal mix of all 4 source waveforms (each with an amplitude of 0.5) and setting trainlet amp to zero.

Computation of trainlets can be CPU intensive, and setting ktrainamp to zero will skip most of the trainlet computations. Trainlets will be normalized to peak (ktrainamp), compensating for amplitude variations caused by variations in kpartials and kchroma.

imax_grains -- maximum number of grains per k-period. Estimating a large value should not affect performance, exceeding this value will lead to "oldest grains" being deleted.

iopcode_id -- the opcode id, linking an instance of partikkel to an instance of partikkelsync, the linked partikkelsync will output trigger pulses synchronized to partikkel's grain maker scheduler. The default value is zero, which means no connection to any partikkelsync instances.

Performance

xgrainfreq -- number of grains per second. A value of zero is allowed, and this will defer all grain scheduling to the sync input.

async -- sync input. Input values are added to the phase value of the internal grain maker clock, enabling tempo synchronization with an external clock source. As this is an a-rate signal, inputs are usually pulses of length 1/sr. Using such pulses, the internal phase value can be "nudged" up or down, enabling soft or hard synchronization. Negative input values decrements the internal phase, while positive values in the range 0 to 1 increments the internal phase. An input value of 1 will always make partikkel generate a grain. If the value remains at 1, the internal grain scheduler clock will pause but any currently playing grains will still play to end.

kdistribution -- periodic or stochastic distribution of grains, 0 = periodic. Normal range 0 to 1, but higher values can be used for the classic stochastic grain distribution effect. If kdistribution is negative, the result is deterministic time displacement as described by idisttab.

kenv2amt -- amount of enveloping for the secondary envelope for each grain. Range 0 to 1, where 0 is no secondary enveloping (square window), a value of 0.5 will use an interpolation between a square window and the shape set by ienv2tab.

ksustain_amount -- sustain time as fraction of grain duration. I.e. balance between enveloped time(attack+decay) and sustain level time. The sustain level is taken from the last value of the ienv_attack ftable.

ka_d_ratio -- balance between attack time and decay time. For example, with ksustain_amount set to 0.5 and ka_d_ratio set to 0.5, the attack envelope of each grain will take 25% of the grain duration, full amplitude (sustain) will be held for 50% of the grain duration, and the decay envelope will take the remaining 25% of the grain duration.

kduration -- grain duration in milliseconds.

kamp -- amplitude scaling of the opcode's output. Multiplied by per grain amplitude read from igainmasks.

kwavfreq -- transposition scaling. Multiplied with start and end transposition values read from iwavfreqstarttab and iwavfreqendtab.

ksweepshape -- transposition sweep shape, controls the curvature of the transposition sweep. Range 0 to 1. Low values will hold the transposition at the start value longer and then drop to the end value quickly, high values will drop to the end value quickly. A value of 0.5 will give a linear sweep. A value of exactly 0 will bypass sweep and only use the start frequency, while a value of exactly 1 will bypass sweep and only use the end frequency. The sweep generator might be slightly inaccurate in hitting the end frequency when using a steep curve and very long grains.

awavfm -- audio input for frequency modulation inside grain.

kfmenv -- function table number, envelope for FM modulator signal enabling the modulation index to change over the duration of a grain.

ktraincps -- trainlet fundamental frequency.

knumpartials -- number of partials in trainlets.

kchroma -- chroma of trainlets. A value of 1 give equal amplitude to each partial, higher values will reduce the amplitude of lower partials while strengthening the amplitude of the higher partials.

krandommask -- random masking (muting) of individual grains. Range 0 to 1, where a value of 0 will give no masking (all grains are played), and a value of 1 will mute all grains.

kwaveform1 -- table number for source waveform 1.

kwaveform2 -- table number for source waveform 2.

kwaveform3 -- table number for source waveform 3.

kwaveform4 -- table number for source waveform 4.

asamplepos1 -- start position for reading source waveform 1.

asamplepos2 -- start position for reading source waveform 2.

asamplepos3 -- start position for reading source waveform 3.

asamplepos4 -- start position for reading source waveform 4.

kwavekey1 -- original key of source waveform 1. Can be used to transpose each source waveform independently.

kwavekey2 -- as kwavekey1, but for source waveform 2.

kwavekey3 -- as kwavekey1, but for source waveform 3.

kwavekey4 -- as kwavekey1, but for source waveform 4.

Examples

Here is an example of the partikkel opcode. It uses the file PartikkelExample1.csd.

Example 303. Example of the partikkel opcode.

See the sections Real-time Audio and Command Line Flags for more information on using command line flags.

<CsoundSynthesizer>
<CsOptions>
; Select audio/midi flags here according to platform
; Audio out  
-odac           ;;;RT audio 
; For Non-realtime ouput leave only the line below:
; -o partikkel.wav -W ;;; for file output any platform
</CsOptions>
<CsInstruments>

sr = 44100
ksmps = 20
nchnls = 2

; Example by Joachim Heintz and Oeyvind Brandtsegg 2008

giCosine	ftgen	0, 0, 8193, 9, 1, 1, 90		; cosine
giDisttab	ftgen	0, 0, 32768, 7, 0, 32768, 1	; for kdistribution
giFile		ftgen	0, 0, 0, 1, "fox.wav", 0, 0, 0	; soundfile for source waveform
giWin		ftgen	0, 0, 4096, 20, 9, 1		; grain envelope
giPan		ftgen	0, 0, 32768, -21, 1		; for panning (random values between 0 and 1)


; *************************************************
; partikkel example, processing of soundfile
; uses the file "fox.wav" 
; *************************************************
		instr 1

/*score parameters*/
ispeed			= p4		; 1 = original speed 
igrainrate		= p5		; grain rate
igrainsize		= p6		; grain size in ms
icent			= p7		; transposition in cent
iposrand		= p8		; time position randomness (offset) of the pointer in ms
icentrand		= p9		; transposition randomness in cents
ipan			= p10		; panning narrow (0) to wide (1)
idist			= p11		; grain distribution (0=periodic, 1=scattered)

/*get length of source wave file, needed for both transposition and time pointer*/
ifilen			tableng	giFile
ifildur			= ifilen / sr

/*sync input (disabled)*/
async			= 0		

/*grain envelope*/
kenv2amt		= 1		; use only secondary envelope
ienv2tab 		= giWin		; grain (secondary) envelope
ienv_attack		= -1 		; default attack envelope (flat)
ienv_decay		= -1 		; default decay envelope (flat)
ksustain_amount		= 0.5		; no meaning in this case (use only secondary envelope, ienv2tab)
ka_d_ratio		= 0.5 		; no meaning in this case (use only secondary envelope, ienv2tab)

/*amplitude*/
kamp			= 0.4*0dbfs	; grain amplitude
igainmasks		= -1		; (default) no gain masking

/*transposition*/
kcentrand		rand icentrand	; random transposition
iorig			= 1 / ifildur	; original pitch
kwavfreq		= iorig * cent(icent + kcentrand)

/*other pitch related (disabled)*/
ksweepshape		= 0		; no frequency sweep
iwavfreqstarttab 	= -1		; default frequency sweep start
iwavfreqendtab		= -1		; default frequency sweep end
awavfm			= 0		; no FM input
ifmamptab		= -1		; default FM scaling (=1)
kfmenv			= -1		; default FM envelope (flat)

/*trainlet related (disabled)*/
icosine			= giCosine	; cosine ftable
kTrainCps		= igrainrate	; set trainlet cps equal to grain rate for single-cycle trainlet in each grain
knumpartials		= 1		; number of partials in trainlet
kchroma			= 1		; balance of partials in trainlet

/*panning, using channel masks*/
imid			= .5; center
ileftmost		= imid - ipan/2
irightmost		= imid + ipan/2
giPanthis		ftgen	0, 0, 32768, -24, giPan, ileftmost, irightmost	; rescales giPan according to ipan
			tableiw		0, 0, giPanthis				; change index 0 ...
			tableiw		32766, 1, giPanthis			; ... and 1 for ichannelmasks
ichannelmasks		= giPanthis		; ftable for panning

/*random gain masking (disabled)*/
krandommask		= 0	

/*source waveforms*/
kwaveform1		= giFile	; source waveform
kwaveform2		= giFile	; all 4 sources are the same
kwaveform3		= giFile
kwaveform4		= giFile
iwaveamptab		= -1		; (default) equal mix of source waveforms and no amplitude for trainlets

/*time pointer*/
afilposphas		phasor ispeed / ifildur
/*generate random deviation of the time pointer*/
iposrandsec		= iposrand / 1000	; ms -> sec
iposrand		= iposrandsec / ifildur	; phase values (0-1)
krndpos			linrand	 iposrand	; random offset in phase values
/*add random deviation to the time pointer*/
asamplepos1		= afilposphas + krndpos; resulting phase values (0-1)
asamplepos2		= asamplepos1
asamplepos3		= asamplepos1	
asamplepos4		= asamplepos1	

/*original key for each source waveform*/
kwavekey1		= 1
kwavekey2		= kwavekey1	
kwavekey3		= kwavekey1
kwavekey4		= kwavekey1

/* maximum number of grains per k-period*/
imax_grains		= 100		

aL, aR		partikkel igrainrate, idist, giDisttab, async, kenv2amt, ienv2tab, \
		ienv_attack, ienv_decay, ksustain_amount, ka_d_ratio, igrainsize, kamp, igainmasks, \
		kwavfreq, ksweepshape, iwavfreqstarttab, iwavfreqendtab, awavfm, \
		ifmamptab, kfmenv, icosine, kTrainCps, knumpartials, \
		kchroma, ichannelmasks, krandommask, kwaveform1, kwaveform2, kwaveform3, kwaveform4, \
		iwaveamptab, asamplepos1, asamplepos2, asamplepos3, asamplepos4, \
		kwavekey1, kwavekey2, kwavekey3, kwavekey4, imax_grains

		outs			aL, aR

endin


</CsInstruments>
<CsScore>
;i1	st	dur	speed	grate	gsize	cent	posrnd	cntrnd	pan	dist
i1	0	2.757	1	200	15	0	0	0	0	0
s
i1	0	2.757	1	200	15	400	0	0	0	0
s
i1	0	2.757	1	15	450	400	0	0	0	0
s
i1	0	2.757	1	15	450	400	0	0	0	0.4
s
i1	0	2.757	1	200	15	0	400	0	0	1
s
i1	0	5.514	.5	200	20	0	0	600	.5	1
s
i1	0	11.028	.25	200	15	0	1000	400	1	1


</CsScore>
</CsoundSynthesizer>


Here is another example of the partikkel opcode. It uses the file partikkel_softsync.csd.

Example 304. Example with soft sync of two partikkel generators.

<CsoundSynthesizer>
<CsOptions>
; Select audio/midi flags here according to platform
; Audio out  
-odac           ;;;RT audio 
; For Non-realtime ouput leave only the line below:
; -o partikkel_softsync.wav -W ;;; for file output any platform
</CsOptions>
<CsInstruments>

sr = 44100
ksmps = 20
nchnls = 2

; Example by Oeyvind Brandtsegg 2007, revised 2008

giSine		ftgen	0, 0, 65537, 10, 1
giCosine	ftgen	0, 0, 8193, 9, 1, 1, 90
giSigmoRise	ftgen		0, 0, 8193, 19, 0.5, 1, 270, 1			; rising sigmoid 
giSigmoFall	ftgen		0, 0, 8193, 19, 0.5, 1, 90, 1			; falling sigmoid 

; *************************************************
; example of soft synchronization of two partikkel instances
; *************************************************
	instr 1


/*score parameters*/
igrainrate	= p4		; grain rate
igrainsize	= p5		; grain size in ms
igrainFreq	= p6		; fundamental frequency of source waveform
iosc2Dev	= p7		; partikkel instance 2 grain rate deviation factor
iMaxSync	= p8		; max soft sync amount (increasing to this value during length of note)

/*overall envelope*/
iattack		= 0.001
idecay		= 0.2
isustain	= 0.7
irelease	= 0.2
amp		linsegr	0, iattack, 1, idecay, isustain, 1, isustain, irelease, 0

kgrainfreq	= igrainrate		; grains per second
kdistribution	= 0			; periodic grain distribution
idisttab	= -1			; (default) flat distribution used
                                        ; for grain distribution
async		= 0			; no sync input
kenv2amt	= 0			; no secondary enveloping
ienv2tab	= -1			; default secondary envelope (flat)
ienv_attack	= giSigmoRise		; default attack envelope (flat)
ienv_decay	= giSigmoFall		; default decay envelope (flat)
ksustain_amount	= 0.3			; time (in fraction of grain dur) at
                                        ; sustain level for each grain
ka_d_ratio	= 0.2 			; balance between attack and decay time
kduration	= igrainsize		; set grain duration in ms
kamp		= 0.2*0dbfs 		; amp
igainmasks	= -1			; (default) no gain masking
kwavfreq	= igrainFreq		; fundamental frequency of source waveform
ksweepshape	= 0			; shape of frequency sweep (0=no sweep)
iwavfreqstarttab = -1			; default frequency sweep start
                                        ; (value in table = 1, which give
                                        ; no frequency modification)
iwavfreqendtab	= -1			; default frequency sweep end
                                        ; (value in table = 1, which give
                                        ; no frequency modification)
awavfm		= 0			; no FM input
ifmamptab	= -1			; default FM scaling (=1)
kfmenv		= -1			; default FM envelope (flat)
icosine		= giCosine		; cosine ftable
kTrainCps	= kgrainfreq		; set trainlet cps equal to grain
                                        ; rate for single-cycle trainlet in
                                        ; each grain
knumpartials	= 3			; number of partials in trainlet
kchroma		= 1			; balance of partials in trainlet
ichannelmasks	= -1			; (default) no channel masking,
                                        ; all grains to output 1
krandommask	= 0			; no random grain masking
kwaveform1	= giSine		; source waveforms
kwaveform2	= giSine		;
kwaveform3	= giSine		;
kwaveform4	= giSine		;
iwaveamptab	= -1			; mix of 4 source waveforms and
                                        ; trainlets (set to default)
asamplepos1	= 0			; phase offset for reading source waveform
asamplepos2	= 0			;
asamplepos3	= 0			;
asamplepos4	= 0			;
kwavekey1	= 1			; original key for source waveform
kwavekey2	= 1			;
kwavekey3	= 1			;
kwavekey4	= 1			;
imax_grains	= 100			; max grains per k period
iopcode_id	= 1			; id of opcode, linking partikkel
                                        ; to partikkelsync

a1  partikkel kgrainfreq, kdistribution, idisttab, async, kenv2amt, \
       ienv2tab,ienv_attack, ienv_decay, ksustain_amount, ka_d_ratio, \
       kduration, kamp, igainmasks, kwavfreq, ksweepshape, \
       iwavfreqstarttab, iwavfreqendtab, awavfm, ifmamptab, kfmenv, \
       icosine, kTrainCps, knumpartials, kchroma, ichannelmasks, \
       krandommask, kwaveform1, kwaveform2, kwaveform3, kwaveform4, \
       iwaveamptab, asamplepos1, asamplepos2, asamplepos3, asamplepos4, \
       kwavekey1, kwavekey2, kwavekey3, kwavekey4, imax_grains, iopcode_id

async1		partikkelsync	iopcode_id   ; clock pulse output of the 
                                             ; partikkel instance above
ksyncGravity 	line 0, p3, iMaxSync	     ; strength of synchronization
aphase2		init 0					
asyncPolarity	limit (int(aphase2*2)*2)-1, -1, 1
; use the phase of partikkelsync instance 2 to find sync 
; polarity for partikkel instance 2.
; If the phase of instance 2 is less than 0.5, we want to
; nudge it down when synchronizing,
; and if the phase is > 0.5 we want to nudge it upwards.
async1		= async1*ksyncGravity*asyncPolarity  ; prepare sync signal
                                                  ; with polarity and strength

kgrainfreq2	= igrainrate * iosc2Dev		; grains per second for second partikkel instance
iopcode_id2	= 2
a2 partikkel kgrainfreq2, kdistribution, idisttab, async1, kenv2amt, \
       ienv2tab, ienv_attack, ienv_decay, ksustain_amount, ka_d_ratio, \
       kduration, kamp, igainmasks, kwavfreq, ksweepshape, \
       iwavfreqstarttab, iwavfreqendtab, awavfm, ifmamptab, kfmenv, \
       icosine, kTrainCps, knumpartials, kchroma, ichannelmasks, \
       krandommask, kwaveform1, kwaveform2, kwaveform3, kwaveform4, \
       iwaveamptab, asamplepos1, asamplepos2, asamplepos3, \ 
       asamplepos4, kwavekey1, kwavekey2, kwavekey3, kwavekey4, \
       imax_grains, iopcode_id2

async2, aphase2	partikkelsync	iopcode_id2
; clock pulse and phase 
; output of the partikkel instance above,
; we will only use the phase

outs	a1*amp, a2*amp

endin

</CsInstruments>
<CsScore>

/*score parameters
igrainrate	= p4		; grain rate
igrainsize	= p5		; grain size in ms
igrainFreq	= p6		; frequency of source wave within grain
iosc2Dev	= p7		; partikkel instance 2 grain rate deviation factor
iMaxSync	= p8		; max soft sync amount (increasing to this value during length of note)
*/
;		GrRate	GrSize	GrFund	Osc2Dev	MaxSync

i1 0 	10	2	20	880	1.3	0.3
s
i1 0 	10	5	20	440	0.8	0.3	
s
i1 0 	6	55	15	660	1.8	0.45
s
i1 0 	6	110	10	440	0.6	0.6	
s
i1 0 	6	220	3	660	2.6	0.45
s
i1 0 	6	220	3	660	2.1	0.45
s
i1 0 	6	440	3	660	0.8	0.22
s

e

e
</CsScore>
</CsoundSynthesizer>


See Also

fof, fof2, fog, grain, grain2, grain3, granule, sndwarp, sndwarpst, syncgrain, syncloop, partikkelsync

Credits

Author: Thom Johansen
Author: Torgeir Strand Henriksen
Author: Oeyvind Brandtsegg
April 2007

Examples written by Joachim Heintz and Oeyvind Brandtsegg.

New in version 5.06