-
Notifications
You must be signed in to change notification settings - Fork 2
Home
A Max/MSP external object for multi-channel, multi-voice, multi-transposition granular synthesis
Michael Edwards
www.michael-edwards.org
object arguments
left inlet
other inlets
This Max/MSP object performs time granulation of sampled sound in real-time.
Input in the form of a fixed, pre-recorded sample buffer or an incoming signal of user-specified length is used to randomly select segments of sound (grains) whose durations (grain length) may be modified on-the-fly. The grains are (amplitude-) enveloped and reconfigured each time a precursor comes to an end.
The granulation is performed in a user-specified number of streams (also known as (aka) voices or layers): the more streams, the denser and thicker but also the smoother the effect. An increase in streams also implies an increase in processing time and is the main factor affecting real-time performance.
The streams may be subject to transposition whereby either a single transposition offset is given and/or a list of semitone values sent to the object forms the set from which a randomly-chosen value is selected for each grain of sound output by the object.
Finally, each grain is routed to a randomly-chosen outlet (the number of which is also user specified) allowing mapping onto an arbitrary number of output channels.
N.B. Most data passed to the object only has an effect when the DSP is on. If you're getting strange behaviour/crashes, make sure the DSP is on before you start interacting with the object.
The object takes the following arguments:
- The number of simultaneous streams (aka voices, layers) of grains ; this is limited only by your computer's CPU power.
- The number of output channels; this will determine the number of signal outlets the object has, i.e. there will be one for each channel you request.
As with most Max/MSP objects, various data can be sent to the left-most inlet:
-
A bang toggles play/stop (aka on/off) state.
-
An "on" message turns the granulator on.
-
An "off" message turns it off: both on and off (as well as bangs) work gracefully, i.e. with a fade up/down to avoid a click. The length of the fade is the same as the grains' ramp length.
-
A list sets the transpositions in semitones (default none, limited to 256) that each grain will then choose randomly from. Given enough voices, sending the list 0,3,7, for example, will result in an audible minor triad (assuming that the input is pitched material). Note that transpositions may be negative and floating point (i.e. microtonal).
-
An OctaveSize and/or OctaveDivisions message will change the temperament of the transpositions (once new ones are received). Temperament will remain equal but you can move away from the default temperament of 12 semitones in an octave of twice the frequency. E.g. OctaveSize 3 OctaveDivisions 13 would create a tempered Bohlen-Pierce scale.
-
A "set x" message sets the buffer (aka sample table, e.g. "set granbuf") to be granulated, or the length of the buffer for live granulation (e.g. "set ms1000"): If a number preceded by "ms" is given, this will be the buffer length (in milliseconds) used to store incoming signal samples (e.g. ms5000 for 5 seconds), otherwise the name given is assumed to be that of an already existing sample buffer object. Changing the buffer on the fly will probably result in a click in the output so it's best to switch off the granulator first, change the buffer, then switch it back on. N.B. for live buffers, this message only sets the length of the allocated buffer that will be used: to actually allocate memory for a live buffer use the MaxLiveBufferMS message (see below). The minimum size for a live buffer is 6ms. The default is a 1000ms live buffer.
NB In September 2013 this object was updated to the Max API 6.1.3. This meant fairly hefty changes to make the object work in the now 64 bit float (double) MSP environment. Most changes reflect the new buffer accessing framework in MSP. An MSP buffer~ is still 32 bit (for backward compatibility) so if granulating a static buffer~, its samples are first copied over and promoted to 64 bit by mdeGranular~. This will mean that if you change the contents of the buffer~ there will be no difference to the granulator's output until you reuse the set message again. See also the note about MaxLiveBufferMS below.
-
A "setms x" message essentially does the same as a 'set ms...' message but allows you to avoid the 'ms' prefix onto buffer sizes; so 'set ms2000' == 'setms 2000'
-
A "BufferGrainRamp x y z" message is useful for setting the buffer (live or static), grain length, and ramp length simultaneously. When you set these independently, checks are made to ensure you do nothing that would cause a crash (e.g. try to put impose a ramp that's too long for the grain length, or a grain length that's too long for the buffer size). Because of this it's sometimes difficult to know which you should set first: the required order depends on the current and desired states. So using this method you can do all three at once and have no execution order problems.
-
A "MaxLiveBufferMS x" message sets the maximum length of the live buffer (x, milliseconds). This should generally be set to the maximum you will need for your whole performance (to avoid allocating memory on the fly). In particular, because switching off the granulator is not immediate (there is a ramp down to avoid clicks), you should definitely wait after doing so (one second should be enough) and before sending this message otherwise memory that has been de-allocated might be read thus causing a crash. Default = 10 seconds.
NB Since the update to Max API 6.1.3 and the 64-bit MSP environment, the internal buffer is now used for copying static MSP buffer~ samples over from 32-bit to 64-bit, hence the MaxLiveBufferMS size must also accommodate the largest MSP buffer~ you'll want to granulate in a performance.
-
Signal sent to the left inlet will be used for live granulation or ignored if the granulator is currently using another buffer (as determined by the "set" message, see above).
-
A "livestop" message stops recording the incoming signal so that granulation proceeds with what is already in the buffer (a granular form of sample and hold).
-
A "livestart" message starts recording incoming signal again.
-
A "print" message prints the state of the granulator to the Max window (for debugging purposes).
-
A "DoGrainDelays" message will make each voice (aka layer of grains) pause slightly (randomly between 0 and 200% of the grain length) when starting the next grain; this should smooth out the granulation process and avoid all grains starting/stopping at almost the same time. This is done automatically when the granulator is started and is particularly useful when switching on-the-fly from very short grain lengths to very long grains lengths.
-
A "SmoothMode" message is related to DoGrainDelays but here the delays are no longer random. Instead they're spread out evenly--(and with no grain length deviation--over a grain length e.g. if there are 5 active voices, each one will wait 1/5 of a grain length before restarting. NB if ActiveVoices is changed, this will not retrigger so 'smoothness' may be interrupted. This function may also cause a click in output so it is envisaged that the object is off or the amp is at 0. Bear in mind that the smoothness of the result is still dependent on the material being granulated. Also consider that if you're using a live buffer, then its length must be at least a little greater than twice the grain length (or more if transposing upwards) because we avoid the portion of the live buffer that will be written into whilst the grain is playing.
-
A "Portion position% width%" message sets the start/end points in the buffer by passing values in percentages: position% will set the middle point between start (0) and end (100), as determined by width% (0-100). These default to 0 and 100 respectively.
-
For convenience, a "PortionPosition x" message sets the position only of the Portion algorithm. Similarly "PortionWidth x" sets the width only. In each case the other parameter remains at its previous value.
-
A "RampLenMS x" message (where x is the new ramp length in milliseconds) will change the ramp length. This is only possible when the granulator is off (and has finished it's fade out). The default ramp length is 10ms and the minimum is 0.5ms.
-
A "RampType x" message will set the type of ramp up and down; x can be one of the following standard windows (all case sensitive): HANN (or HANNING: the default, a typical bell-shaped window), TRAPEZOID (straight line ramp), RECTANGULAR (no ramp at all), WELCH, PARZEN , BARTLETT, HAMMING, BLACKMAN2, BLACKMAN3, BLACKMAN4, EXPONENTIAL, KAISER, CAUCHY, POISSON, RIEMANN, GAUSSIAN, TUKEY.
-
A "MaxVoices x" message will set the maximum number of voices (layers) running. This may also cause a click in output so it's best sent when the granulator is off.
-
An "ActiveVoices x" message will set the number of the maximum voices that are actually playing. This may be safely changed on the fly. Changing this will not instantly affect the output however as the grains are only deactivated once they have got to the end of their current window.
-
An "ActiveChannels x" message will set the number of output channels used. This should of course be less than the number of output channels set as the object argument.
-
A Warnings (1 or 0, default 1) message will toggle between printing useful warnings or not to the Max window i.e. "Warnings 0" means the object should remain silent no matter what you tell it to do and how much sense that makes (and whether it ignores you or not).
The rest of the inlets, numbered from left to right, are as follows. The messages which can be sent to the leftmost inlet to accomplish the same thing are in square brackets and are case sensitive.
-
Transposition offset in semitones (default none): This may be fractional; it is added to the list of transpositions sent to the left inlet (see above), or simply transposes all the grains if no transposition list is given.
[TranspositionOffsetST] -
Grain length in milliseconds (default 50).
[GrainLengthMS] -
Grain length deviation (as a percentage) of the grain length (plus or minus, default 10%); i.e. if anything other than 0, the grain length is randomised by an amount between plus or minus the deviation percentage.
[GrainLengthDeviation] -
The minimum start point in the input buffer (fixed or live) in milliseconds (default 0). The actual start point is a function of the grain length, and the start and end points (see below) and is randomly chosen to fit anywhere within these points.
[SamplesStartMS] -
The maximum end point in the buffer in millisecs (the default is the end of the buffer). N.B if end < start then the buffer will be granulated backwards. If the difference between these two is too small for the ramp up and ramp down to be accomplished, no grains will be output. [SamplesEndMS]
-
The "density" of the grains (as a percentage). 100% means a grain will start immediately every time the previous one ends, 50% means one in two grains (determined randomly) will produce no output. (Default 100%).
[ Density] -
Grain amplitude (>=0 and <= 100, default 0.5). This is a straight amplitude scaler for each grain, i.e. no account is taken of how many voices there are. N.B. when using MIDI faders, the 7-bit step between values is often large enough to create a slight but audible click in output. This is corrected in mdeGranular~ by interpolating from the last to the new amplitude. The side-effect is that new amplitudes will not be accepted for an "audio tick's worth" of samples. [GrainAmp]