Generate a sinusoid with gen~

Max provides an object called gen~ that opens up a new patching window in which you can program audio at the sample level. You build an audio network inside the gen~ window with objects that are quite similar to MSP objects, but within the gen~ window there is only audio, no non-audio events. What you build in gen~ is represented equivalently in a special text language called GenExpr in an adjacent “Codebox” window. This gives the programmer access to text-based sample-level programming which is then compiled as an audio object that runs just like other MSP objects.

Here is an example of generating a sinusoidal tone from scratch using gen~.


generatesinusoid.maxpat

And here is what the contents of the gen~ object look like, with the GenExpr code.


sinusoid in gen~

Notice that the objects in gen~ are similar to MSP objects but a) they don’t need the tilde ~ because everything is audio, b) some objects are different, like counter instead of count~, c) some macros exist, such as SAMPLERATE, d) inlets are specified by in objects, which only receive audio signal, and e) parameters defined within gen~ can have their value changed by named messages from the main patch received in the first inlet. (The in object is not really needed in this gen~ patch because there’s no audio input.)

Synthesize a sinusoid in MSP

Synthesizing a sinusoid in MSP is a fairly trivial matter, because the cycle~ object does most of the work for you. It produces a full-amplitude waveform at the requested frequency ƒ and with the requested phase offset φ.

Technically, it’s not really calculating a sinusoid sample-by-sample. It fills a wavetable (an array) with one cycle of the cosine waveform, and then reads through it over and over, changing its increment per sample according to what’s needed to produce the desired frequency, based on the wavetable length and the sampling rate.

Amplification—turning the amplitude A up or down—is achieved by multiplication of the full-amplitude wave. Amplification greater than 1 (or less than -1) will cause clipping of the wave.


sinusoid.maxpat

Using the full path when opening a file

This example consists of a .zip archive of files. Together they demonstrate the idea of organizing one’s sound files in a specific folder, and then using the full path to that folder to ensure finding the sound files when you want to play them. It takes advantage of (and demonstrates) an abstraction for providing a full path name; that abstraction is saved with the name “providepath” and used as an object in the “*main*” patch.

The *main* patch has been set (in the Patcher Inspector) to open in Presentation mode. Try it out, then uncheck Presentation in the View menu to see the full patch.


tones.zip

A umenu popup menu object contains the names of the three sound files contained in the “sounds” folder. Each soundfile is a few cycles of a steady sinusoidal tone. When played once, each file makes a brief 100-millisecond beep, but when the Loop toggle is on, the file will be played in a continuous loop and will only stop when looping is turned off.

Note the use of the $1 argument in the message box. This signifies a replaceable argument. Whatever is the first item in the incoming message (in this case either ‘1’ or ‘0’) is put in place of the $1 before the message is sent out. In this way, the toggle will produce either a ‘loop 1’ message or a ‘loop 0’ message to turn looping on or off in the sfplay~ object.

When the patch is opened, loadbang automatically turns MSP on, sets the gain~ volume to full on, and chooses the desired default file from the umenu. That filename gets combined with its full path in the “providepath” subpatch (abstraction), and that full name is used as the argument of an ‘open’ message to sfplay~. Thus, the file is loaded and ready to play, waiting for the user to click on the Go button.

One object left unexplained is deferlow. This object takes whatever message it receives, and waits until all other tasks have been completed before it passes the message on. The reason it’s used here is because there are really two loadbang objects in this program—one in the main patch and one in the “providepath” subpatch. (You can double-click on the “providepath” object to see its contents.) The order of the two loadbang objects’ outputs is unspecified. So in order to make sure that the path name and folder name are acquired before the filename is specified, we defer execution of the umenu selection till after the ‘path’ message has been sent to thispatcher in the subpatch. That way the file name from umenu will successfully be combined with the path constructed in the subpatch.

Providing a full path name

To ensure finding a file (regardless of any File Preferences… settings in Max) you may need to provide the entire path to the file in the hierarchical file system: volume name, folder name(s), and file name. This example shows how you can construct such a full path. The example assumes that you have a folder named “sounds” in the same directory as your patch, but you can edit the patch and modify that name to be anything you want. It’s an abstraction that can be used to construct a full path for a file, based on the file name received in the inlet.


providepath.maxpat

The combine object is for constructing a single symbol (a single string of characters) from multiple items received in its inlets. The number of inlets it will have depends on the number of typed-in arguments—three in this case. The arguments specify the default strings for each of those items, but those will be replaced by whatever comes in the corresponding inlet. Whereas most objects are triggered by a message received in the left inlet,  the combine object is rather special in that it lets the programmer choose which will the the triggering inlet(s) that will cause output. The inlets are numbered starting from 0, so setting the ‘triggers’ attribute to 2 in this case means that nothing will be sent out except when a message is received in the right inlet.

The ‘path’ message to the thispatcher object causes the full path of the current patch to be sent out the right outlet. Thus, the loadbang object triggers the “sounds” folder name to be sent in inlet 1 and the path to that folder to be sent in inlet 0. The combine object is then ready to make a full path out of any filename that comes in inlet 2 (from the main patch).

The error object

When Max detects a bug or a problem in your patch, it posts an error message in the Max window, such as “sfplay~: cant find file <filename>”. If you want to handle that error in your patch somehow, you can use the error object, which can sends those error messages out its outlet, and you can then use that message however you want (show it to the user, trigger other actions, etc.).

You can turn error watching on or off for each error object, and you can use the route object to detect certain kinds of errors. For simplicity, this example does nothing more than display the error message by sending it in the right inlet of a message box, but it does show the basic usage of the object.

errorobjectbasics.maxpat

How to send a Max patch by email

You can send a Max patch (or any selected objects from it) to someone else as plain text in an email (or a post like this) by following these steps.

1. In Max, while in Edit mode, select the objects you want to include (or type Command-A to Select All).

2. Choose ‘Copy Compressed’ from the Edit menu.

3. Go to the program where you want to include your patch and choose ‘Paste’ from the Edit menu.

The text that you paste in will look something like the text shown at the bottom of this post. When you receive such text from someone else, to turn it back into a Max patch, follow these steps.

1. Copy the weird-looking text, including the lines that say

———-begin_max5_patcher———
and
———–end_max5_patcher———–

but not the lines that contain the HTML tags ‘<pre><code>’ and ‘</pre></code>’.

2. Go to Max and choose ‘New From Clipboard’ from the File menu.

Try it yourself. This will be a common way for you to send and receive Max patches.

<pre><code>
———-begin_max5_patcher———-
560.3oc0V0taaBCE82vSgk2eyRMNfIr8q8BrWfopIGvP7DXihc1RaU6y97GP
VZKPJsQSsHA1b8E6y4dO9ZtKL.tQdfoffu.9AHH3tvf.mIqgft2CfMzC40Tk
yMXtrogIzvE9wzrCZmcgbIPVBn6K3RP9VpPvpU8d0R04a4hpetikq8qFFgVh
V.hHqcM3DaC1XCbc2GwKbSrbyu9LNtelJkBsf1vbC8scbZc+Hh8MbQMS6fYz
+LJ2q6shNYRT7acSRDdIxZ89vP6iEuwvfrkI.TfRtWT.J40rIBAqhVlYuHF9
ihcgADdxv.9iRXvKCjhqjkkSDAhhczFicMoqlj7n2ajmcaAM+gonGwqwW6nW
bR+ygnWDYPRfGlDuBz1vTJZEaPE6Dbf3ElXGSVk54yHTXtYngIWzHYnNydO0
2zx7.DBAWeAUtJMcm9JkV1ddca2tVeVdLca1GqRWlxVs0zaLc5pkelhXYDK4
W6E5IwSEIReuEIzxppIIW+FX7wL73riLCNDMlVlaxKuJ4rf8GCHdtZtzlLe.
feAjLwc1Sp4PozTbR53LM4hrMe0L2lq3UB6BbZuMTQE7HDUze267m9tyQO6s
cM2XWhLiXHIxaA90GGrcyDrlKd5uD4hZV6ONCXNkeWd+R1EY.QGwSASo4Bpl
KEm3isP+INskWTvDmJra3EsRiPnCCiHGlCjPuDHg9uAI64DmESIyCRl+cHYg
qtrsgjc7s2HVIWdn9DIW38g+Eb7fmdI
———–end_max5_patcher———–
</code></pre>