How to make a new external

RTFM

For documentation on the C coding part of writing externals, especially including the API that Cycling 74 provides for writing externals, the Max/MSP SDK comes with a large PDF file called WritingExternals. As of May 20, 2008 (after Max 5 was released), the most recent version of this document is from the 4.5.5 SDK, which is checked into SVN at max/SDK/4.5.5-MacSDK/WritingExternals.pdf. There are also a good number of example objects.

There is also a helpful tutorial by Ichiro Fujinaga.

Have the programming tools

See [cnmat:node/3258|Required Development Tools] and [cnmat:node/2970|Configuring Subversion and Related Software].

Have a working copy

See CNMAT's Subversion documentation for details, but the short story is that you can check out CNMAT's entire source code world to your current directory with this command:

svn checkout svn+ssh://128.32.122.217/svn/cnmat/trunk

Choose a Subversion-compatible name for your new external

No weird foreign characters (accents, umlauts, n-with-a-tilde, etc. No space characters. No Unix-unfriendly punctuation like *, (, ), ?, $, !, etc.

Choose/Make a place for your new external in the directory structure

There are [cnmat:node/277|subdirectories for different categories of Max and MSP externals]. If your new external fits one of the existing categories, go to the corresponding directory in your working copy and make a new subdirectory with subversion. For example, to add a new machine-learning Max external named kernel-ridge-regression, do this:

cd location/of/my/working/copy/max/externals/machine-learning
svn mkdir kernel-ridge-regression

If your new external doens't fit any existing category, use "svn mkdir" to make directory for a new category, update the [cnmat:node/277|Subversion Directory Structure documentation] to describe your new category, and then make the subdirectory for your new external as above.

Start from Existing C Code (and Makefile)

Now find an existing external that already works, preferably one whose code will be a reasonable starting point for your new external. Here are some suggested starting points:

  • externals/misc/printit shows how to handle every kind of Max event that can come in
  • The code for index~ in the SDK shows how to access data in a buffer~
  • externals/SDIF/SDIF-buffer shows what you have to link to use the <stdio.h> functions like fopen(), fread(), etc.
  • externals/SDIF/SDIF-tuples shows how to turn the name of an SDIF-buffer into a pointer to the actual data
  • Any external that is available for multiple platforms at [cnmat:downloads|our downloads page] can be presumed to have all the right IDE projects, etc., for cross-platform building

You might want to ask Subversion which objects have been updated recently, on the assumption that more-recently-checked-in objects will be more likely to follow CNMAT's most recent coding guidelines. Go to the top-level max directory and type this svn command:

     svn log -v | more

This will give you all the log messages, in reverse chronological order, for everything within the max directory. The "-v" flag asks for verbose, which is important because that way svn tells you which files were changed in each revision.

Get the Makefile (which should be trivial, just including the global per-object Makefile) and the C code (which will include an example Name/Value comment block, the version.h mechanism, etc) from an existing project.

Copy the old .c file along with the old Makefile to the new directory for this external, change the .c file's name to match the name of your new external, and "add" everything in Subversion:

cd directory/for/this/new/external 
cp directory/for/the/old/external/old-external-name.c .
mv old-external-name.c new-external-name.c 
cp directory/for/the/old/external/makefile  .   
svn add new-external-name.c  makefile

Now set the "keep revision number in the source code up to date" property on the .c file, and then commit everything to the repository:

svn propset svn:keywords "LastChangedRevision" new-external-name.c

svn commit

Make the Xcode Project

Follow the instructions on converting from CodeWarrior to Xcode, but ignore the parts about already having a Codewarrior project.

Update the Makefile so the object will build on Windows

Now that we're using GCC/make/cygwin instead of an annoying graphical IDE to build Windows externals, you don't have to go through the tedious process of making a "Windows project" to build an object for Windows. Most objects will build for Windows just by saying "make win" in the proper directory on a Windows box.

The exception is objects that require source code other than what's in [myobjectname].c, for example, SDIF or OSC externals that link to SDIF or OSC code in a different part of the svn hierarchy. For these you will have to add some lines to the Makefile telling the compiler and linker where to find the additional files. For example, here's the Makefile for the SDIF-buffer object:

MORE_SOURCE += ../../../utility-library/search-path/open-sdif-file.c
MORE_SOURCE += ../../../../sdif/lib/sdif-buf.c
MORE_SOURCE += ../../../../sdif/lib/sdif-mem.c
MORE_SOURCE += ../../../../sdif/lib/sdif.c
include ../../../makefile.per-object

It's essential that you refer to other directories via relative paths within the Subversion directory structure. If you used an absolute path name to wherever your working directory happens to be, then this would break for anybody else, or if you tried to build from a different working directory.

Any directories referred to in these MORE_SOURCE lines will also be part of the search path for #include files. If you need to #include a file but your project doesn't use any of the .c files in that directory, you can add it like this:

CFLAGS += -I../SDIF-buffer

The last thing you might need to add is additional (system) libraries to be linked. The only current example that needs this is OpenSoundControl, which uses the following Makefile line to get the correct version of the htonl() procedure:

LDFLAGS += -lws2_32

(Optional, only if you want to build for CFM) Make the CodeWarrior Project

The tedious part is making the CodeWarrior project, because the name of the external is embedded in lots of project settings that you'll never find as you click your way through endless settings panels and dialog boxes. That's why Cycling '74 supplies the XML project templates:

  1. Create a new subdirectory (with svn mkdir) at the appropriate place in CNMAT's Subversion directory structure.
  2. CNMAT's versions of Cycling 74's XML project templates live (of course) in svn, in max/project-templates
  3. Pick the one corresponding to the type of external you're writing. Max or MSP? Does it need <math.h> functions? Does it do graphics?
  4. Make a copy of that XML file in the (new) directory for this project. (Actually, you can put your copy anywhere, since you're not going to check in this XML file.)
  5. Open the XML file in CodeWarrior, search for "C74OBJECTNAME" and replace all instances with the name of your object. Save the new version.
  6. Under CodeWarrior's "File" menu, choose "Import Project...", then in the resulting dialog box, go to the XML file you just saved.
  7. In the new dialog box, save the new file as [name_of_the_external].mcp, in the directory for this new external.
  8. Do "svn add [name_of_the_external].mcp" to put the CodeWarrior project file in SVN. (Actually, it doesn't really go to SVN until you "svn commit").
  9. Once that works you can throw away the XML file.