Tutorials

gen~ for Beginners, Part 5: The Codebox Operator

I’m sure that it comes as no surprise to you that I’d say that the community of Max users isn’t a monolithic batch of patching fanatics – the community can be divided in any number of ways. We can think of audio programming versus visual programming, performance versus installation, Max versus Max for Live users, and even further segment user communities from there. For this gen~ tutorial, I’d like to talk about another way to segment the Max community in ways that we don’t often talk about: graphic versus text-based coding.

All the tutorials in this series: Part 1, Part 2, Part 3, Part 4, Part 5, Part 6, Part 7
Follow-up video tutorials: Working with Abstractions, Debugging and Signal-Rate Processing

Some Max users learned Javascript at around the same time as they took piano or violin lessons, and many of them remember the first time they asked their teachers or posted to the Forum asking about “where the do( ) loop object was.” Other Max users took to Max because graphic programming just made sense to them right away, and rarely felt the need to either enter or re-enter the realms of wiring code (Author’s note: I am one of those people who once wrote code and hardly ever feel the urge or need to do it now that I own Max).

One of the truly interesting things about the Gen universe in Max is what it offers both groups of users the ability to create efficient and idiosyncratic Max tools by patching with operators, writing code, or combining both approaches and choosing the one that makes the most intuitive or practical sense.

That’s what the codebox operator in gen~ is all about. In this installment of our gen~ for beginners series, I’d like to introduce you to the codebox operator. As before, one of my goals is the reduction of anxiety – and, perhaps more than any other part of the gen~ patching world - the codebox operator tends to be a source of discomfort for some beginners. I think that discomfort takes two forms:

  1. Some Max users who are uncomfortable or unfamiliar with text-based coding may worry that using the codebox operator is more efficient than the operator-based approach that they’re becoming more comfortable with (now that we’re 5 tutorials into the series). I think that’s because we find ourselves in a culture that privileges text-based coding, and perhaps because beginning users see others opting for codebox operators from the start and assume that it must always be more efficient.

  2. Max users who are comfortable writing command-line code soon realize that working with the codebox operator means learning a slightly different bit of programming than they may be accustomed to – GenExpr. For them, the questions involve figuring out what’s different about syntax inside the codebox operator.

To codebox Or Not To codebox, That Is The Question…

Let’s start with the first – and probably the most important bit of anxiety reduction for some beginning gen~ users:

There is absolutely no difference in terms of performance between using operators inside your gen~ patch as compared to using a codebox operator or using the GenExpr language in an expr operator to do the same thing.

A Gen patcher contains and describes the calculations a Gen object performs. The gen~ object compiles the contents of patcher into a language called GenExpr, which the object turns into target code necessary to perform its calculations (that GenExpr target code is what you see in any gen~ patcher’s Code tab). From this, the gen~ object then generates and compiles native CPU machine code on-the-fly. Everything – operator-based patching or codebox operators - gets merged into a single representation. The bottom line? Use what you’re comfortable with, or the approach that is most convenient for the problem you want to solve.

Apart from your own codeophilia or codeophobia, when might you want to (or need) to use a codebox operator in your gen~ patching?

The reasons for using regular operator-based patching over the use of the codebox operator have to do with the editing environment itself, as much as anything else.

  • Working with operators lets you take advantage of autocomplete features when patching.

  • You can easily add assistance on operator inlets and outlets. (You’ll particularly notice this once you try going back to that bit of codeboxing you forgot to comment well and try to figure out what the third inlet did – believe me).

  • You can call the Max reference resources by option-clicking (Macintosh) or alt-clicking (Windows).

  • Although this won’t be true for all of your friends, operator-based patching may be more accessible to most Max users when sharing your patches.

That said, there are some things for which the codebox operator is incredibly useful. There are things which you cannot do without a codebox object:

  • The codebox operator is useful if you want to perform certain kinds of programming operations – things like for( ), or while( ), or if( ). It’s not just useful – it’s almost always the only way to include those procedural operations.

  • The codebox operator gives you specific and explicit control of the order or reads and writes for things like delays and operations on data and buffer operators. While you can sometimes achieve this with standard gen~ patching, there will be some cases where you want to guarantee a specific order to operations. The codebox operator is your friend here.

  • The codebox operator lets you create libraries of functions you can re-use. In the Max world, this is a little bit like having your own folder full of abstractions.

In the course of this series, I've been corresponding with my friend and colleague Graham Wakefield - the father of the gen~ object. The subject of working with the codebox operator came up, and his comments about it surprised me a little - I guess I'd assumed that someone like Graham - who codes with the same ease I bring to making errors in my Max patching - would naturally use the codebox operator for everything. Not really, as it turns out:

I have to say, every time I start a new work in gen~ I start with patching, not with a codebox; and I dive into codebox only where I need to.

Partly this is because I’m expecting I might add this to the Gen examples folder, but it's also the case that I just find it easier to think some things as dataflow first. Also, honestly I can never remember the inlets and attributes of buffer and delay operators, so the UI help is really appreciated.

Sometimes, my patcher will end up being mostly patching with a few codebox operators here and there as needed. But sometimes there’s no way to avoid doing the whole thing in a giant codebox. That's nearly always because I need to do some parallel operations (loops) or non-samplerate conditionals (via if()).

Let's take a first foray into some codeboxing.

Your First Codebox

To add a codebox operator to your gen~ patcher, click in a blank space in your unlocked patcher window, type "n" (new) to create a new object box with a cursor, and type in the word 'codebox,' followed by a return. When you hit return, a new codebox operator will be created.

As is the case when creating a new gen~ object, the new codebox operator contains an example of GenExpr code by default. The contents of the generic codebox operator bear a very strong resemblance to what appears inside of a gen~ object when you first create it: an example of adding two input values and outputting the sum (of course, you'd need to add two in operators and an out operator to create a precise copy of the generic gen~ starter patch - the codebox isn't connected to the outside world.

We've spent previous installments of our gen~ for Beginners tutorials looking at similarities and differences, so let's turn out attention to figuring out what it is you type into the text window of a codebox operator.

The contents of a codebox operator are in GenExpr - the internal language used by all Gen patchers. When you type GenExpr text into a codebox operator, the object analyzes the expressions you type and automatically constructs the appropriate number of inlets and outlets so that patch cords can be connected to the computations described inside.

If you'd like a complete guide to the GenExpr (along with an overview of its use in a codebox operator), type GenExpr into the text box in the upper right-hand corner of your patcher window and hit return. The Documentation Browser will open, and you can click on the Guides tab to the left to be routed to the GenExpr overview. It's full of useful information.

The GenExpr Motherlode

So, what are the equivalent GenExpr commands to the Gen operators you use when doing visual programming? Since you've been following this tutorial series, you already know the answer to this question!

In our first gen~ for Beginners tutorial, we showed you the Code icon in the gen~ patcher window. We explained that clicking on the Code tab’s icon displayed the actual code that Gen operators use to produce the machine code compiled as you work.

This is a feature you can use when you start codeboxing. If you have a question about what the syntax of any Gen operator looks like in GenExpr, here's a quick and dirty technique to get your answer:

  1. Create an instance of the operator you want to check on (in this example, we'll use the tan operator).

  2. If you've got an out operator in your patch, connect the operator up. If not, add one and connect it.

  3. Select the operator you want to check on.

  4. Click on the Code icon at the right-hand side of your patcher window to open the Code View tab. The operator you've selected will be highlighted.

A Simple Codebox Example

Let's look again at the contents of the vanilla codebox operator:

In this simple example, we can see how the codebox object's inlets (in1 and in2) and outlets are identified and created, how its outlets (out1) are identified and created (Note that we're talking about the inlets and outlets to the codebox object itself - to connect it to the outside world, we'll still need to add standard in and out Gen operators in our patch).

We can also see that the line of code has a semicolon at its end (every line in a multiline codebox that isn't a comment needs to end with that semicolon).

With that in mind, let's create a codebox-based piece of gen~ patching.

There are several interesting sources for DSP code out there on the Web that you can use. I'm going to grab a simple waveshaper effect patch from the www.musicdsp.org website. You can find it here:

This looks pretty straightforward, but what's the range for the "output" supposed to be? Oh, right - it's listed as between -1.0 and 1.0 in the notes. We know that what they're calling x is the input signal and that f(x) is a description of how you calculate the output. With that in mind, we can make a few minor modifications to the code to "port" it to our codebox. Here's the result:

You'll notice a slight change from the original code here - that line where the amount is declared. When the original code was put in, it turned out that this bit of code tended to blow up loudly and nastily with values outside of the -1.0 - 1.0 range, so I added the clamp object (yes, it does the same thing as the Gen clamp operator) to constrain the input range tosave your ears. You're welcome.

When we type the code into our codebox operator, we don't see any errors in the Max window. That ought to mean we're good to go. Let's add a little patching to our gen~ patcher and listen to our waveshaper.

Note: This patch is included as part of the download for this tutorial that you can find at the end of the tutorial, but you'll also find a copy of it as part of the gen~.overdrive.maxpat patch in your Gen examples folder, along with another codebox-based gen~ patcher ported from www.musicdsp.org.

Working With Parameters

You can make use of the Gen param operator in your codebox patching, too - parameters declared in GenExpr behave just like param operators in a patch - you add a param operator to your gen~ patcher, and then reference it by name as part of the code you type into the codebox operator. You'll remember that we do want to limit the range of inputs for our parameter for reasons of your hearing, as we did last time. How can we do this using a codebox operator? If we were working the standard Gen param operator, we'd use the @min and @max attributes.

In this example, we've changed our patch so that the amount in our waveshaper is a standard Gen parameter. Here's what the result looks like:

One thing you'll need to remember — and you can see it in the example above — is that you need to add those parameters at the top of your codebox text (i.e. before any declarations) - they need to be there because they're operating at the same level as object box Gen operators in your patcher.

You'll also notice that the inside of our gen~ patcher doesn't include a param operator, as you might expect. As long as the Param line is added to the codebox, it's as though you'd added a param operator. The line Param amount(0., min=-1,max=1); also lets us set a default value of zero for the parameter, and specify those min and max values to save your hearing - just as though we'd added a param amount @default 0. @min -1 @max 1 operator to our gen~ patcher.

To finish up this edition of our beginner's tutorial, here's another parameter-based example of one of the things you'll need a codebox operator to achieve: wrapping or folding an input waveform based upon its threshold value. It's one of those places where an if... else... construction is just the thing.

This example shows another approach to working with parameters in a gen~ patcher. Instead of adding a Param-style line in the codebox operator, we've added named param operators to the gen~ patcher window as we might normally do in non-codeboxed patch and then used those parameters in our codebox code by name (mode and threshold).

Note, too, that we've set a variable (wrapfold_out) at the beginning of our codebox code, which lets us use that variable in the if... else portion of the code and output it when we're done.

In Part 6, we'll look at working with buffers and data operators, history, and functions. See you then!

gen5_tutorial_patches.zip
application/zip 10.31 KB
Download the patches used in this tutorial


by Gregory Taylor on 2018年4月17日 20:28

Creative Commons License
Andrew's icon

Andrew

4月 19 2018 | 10:22 午後

Great tutorial, thanks!

Francesco Leonardo Bossi's icon

Francesco Leonardo Bossi

5月 09 2018 | 9:48 午後

excellent, thank you.

tsambou's icon

tsambou

11月 10 2019 | 2:40 午後

Is the variable "wrapfold_out = 0" something like a static variable, which only initializes once? How is that the case? I thought the whole code was running at sample rate so it would every sample set the variable back to zero. Is it only because it's declared at the top of the codebox?

Also, why do you use a semicolumn ";" at the end of the if statement?
if (condition)
{
// code
}; <--------------------- semicolumn?

Scott Pincus's icon

Scott Pincus

12月 22 2022 | 3:20 午前

@tsambou, wrapfold_out = 0 is initializing a local variable to the codebox. It gets reinitialized on every sample as it should so we start with a blank slate on every sample, thus making room for the current sample. It is possible for memory to get very messy if you don't do that, but depends on the coding language/ complier convention.
The semi colon "completes" the if conditional code block and is required. Leaving that out is a common coding error for many languages. If you leave it out the complier would try and use anything you type after as part of the if block, even if you don't type anything else after. Basically , it tells the complier that this block ends here. Look up coding "end of line" for each language you use or keep eye out for what is used. It can often be different for each language.

Todd Harrop's icon

Todd Harrop

2月 23 2024 | 8:38 午前

I think the semicolon after the 'if' statement's closing brace is unnecessary and that the compiler interprets it as an empty line (ending with the semicolon), so it's innocuous. If an 'if' statement has no pair of curly braces then, of course, a single statement following the 'if' condition requires a semicolon. In tutorials six and seven there are examples of 'if' statements in GenExpr without semicolons after the closing braces. I haven't tested it yet myself in GenExpr, but it should only take a moment for anyone to confirm for themselves.