(max) simple synths – envelope control

The next set of patches demonstrate how to apply amplitude envelopes in Max. Again, all patchers have extensive comments, although I do take some redundant (previously used) comments out as patchers progress. Do take a look at patchers in order for full explanations.

All patchers are in the max examples folder on box.

basic midi-controlled synth with adsr~ envelope

<BasicSynthADSR.maxpat>

adsr~ is probably the simplest envelope to use in Max, both in its familiarity to the user and the way it responds to control messages. adsr~ starts when it receives a non-zero number (and scales it’s max amplitude based on the number), and triggers its release when it receives a 0. You can use key velocity to not only start the envelope and trigger the release, you can use velocity to scale amplitude. You must, however, convert MIDI velocity to a 0. – 1. range, which you can do by dividing the velocity by 127. Note that the argument to the divide object ( / ) has a decimal point after it (127.). That decimal point tells float to perform floating-point math and output a floating-point number. If you don’t specify a float as the argument, you get a truncated integer for your answer. For example, 64/127 gives an integer result of 0, because the integer output truncates anything after the decimal. It doesn’t round.

The basic synth also has some performance controls. The mod wheel (CC#1) is being used to control the portamento time, the time in ms that it takes to change from one note to another. CC#7, usually defined as channel volume control in MIDI, is being used to control the output gain.

basic midi-controlled synth with adsr~ envelope and selector~

<BasicSynthADSRselector.maxpat>

This example adds a selector~ object, a type of audio gate/router that takes multiple inputs and lets you choose which input signal is sent to the output. Five oscillators are being sent to selector~. Using the umenu to the left of the selector~ object you can choose the waveform you want to send to the output. Selector~ is looking for the number corresponding to the input to pass, starting with 0 = all closed, 1 = saw~, 2 = cycle~(sine), etc. umenu is passing the location of the item selected – not the text of the item chosen. You need to make sure you set up the umenu to have the items appear in the order of the oscillator connections, with the first item (“off”) keeping all inputs closed.

basic midi-controlled synth with function

<BasicSynthLineFunction.maxpat>

The function object provides another way to provide envelope control of a signal. function is a graphic, breakpoint function control editor. You click to add a break point and shift-click to delete a breakpoint. The function can have as many breakpoints and line segments as you like. It sends target/time pairs in line~ format out its second outlet. You send that to line~, and you can use key velocity to scale it after the line~. The important thing to note is that the velocity scaling should be done at control rate, sent to the right inlet of a *~ object, while the output of line~ should go to the left inlet of the same *~.

bang messages trigger the function to output its values, which can be triggered by non-zero key velocities (note on velocities). You can set the overall length of the y-axis (functioning as time in ms) with the setdomain message.

basic midi-controlled synth with function and sustain

<BasicSynthLineFunctionSustain.maxpat>

The function object also has the ability to define sustain points. Function will output target/time values up to a sustain point, and the it will wait until it receives the “next” message to continue. Use cmd-click to define a sustain point. The dot will appear to have a halo, or outer ring. Cmd-click again to remove sustain from a point.

The patcher has been modified so that it looks at incoming key velocity via a select 0 object. Non-zero input will be passed out the right outlet, triggering the button and starting the function up to the sustain point. Note off velocities of 0 will be selected, sent out the left outlet to the button, and trigger the next message.

A couple of things to note. The way this patcher responds to note on and off velocities, with the next message specifically, only works if there is one (and only) one sustain point defined in the function window. If there are no sustain points, the next message will retrigger the function output. The second thing is that since you have a sustain point in your envelope, which could last an indeterminate amount of time, the domain length (changed via the setdomain message) works a little bit differently. You only need to have enough domain time to include the combined attack, initial decay, and release times (of an ADSR envelope), or the combined segment times. Setdomain will not equal how long the note lasts, since that is a function of performance.

adding a preset

<BasicSynthLineSustainPreset.maxpat>

It would be useful to be able to store envelope functions as part of a preset. I’ve given you an example with function. It would be fairly easy to add it to the ADSR patcher if you wanted. All I want to store are function settings (the envelope shapes) and the duration of the function (that goes into the setdomain message). I’ve connected the left outlet of preset into the inlets of those two objects.

I’ve also changed the way velocity is scaled. To get an exponential map of velocity to loudness, I mapped velocity to dB (-70. – 0.), and then converted it with dbtoa (dB to amplitude). A dB value of -70. gives a floating-point representation of amplitude of 0. An amplitude of 1. equals 0 dB.


Comments

Leave a Reply