Digital Music Programming II: bang




This lab demonstrate how to create an object with one inlet and one outlet. Compile the following program in CodeWarrior and try it out in Max/MSP (you should have already done this in the previous lab). For this lab, you should study the example program and understand the basic components used in creating a Max external object. Read pages 3-6 of Ich's Tutorial (see the Reading List).

bang.c (code with comments)

#include "ext.h"

typedef struct {
   t_object max_data;  
   void*    output;    
} MyObject;

void *object_data;

void   main             (void);
void*  create_object    (void);
void   InputBang        (MyObject* mo);

void main(void) {
   setup((t_messlist**)&object_data, (method)create_object,
         NULL, (short)sizeof(MyObject), NULL, A_NOTHING);
   addbang((method)InputBang); 
}

void* create_object(void) {
   MyObject *mo;                            
   mo = (MyObject*)newobject(object_data);  
   mo->output = bangout(mo);                
   return mo;                               
}

void InputBang(MyObject* mo) {
   outlet_bang(mo->output);
}

Notice that there are three colored sections in the bang.c source code. Each color represents a particular part of the program:

  1. black -- basic setup instructions. Included files, global variables, function definitions, and data type definitions belong in this section of the code.
  2. red -- initialization functions which will be called when the object is first created in a patcher window in Max.
  3. blue -- behavior functions which will be run when input messages are received in the object.

More elaborate explanations of each section are given below:

Setup Section

    The setup section consists usually of four pieces:

    1. include files
    #include "ext.h"
    Max object must include the file ext.h. This file contains all of the functions that Max gives you to communicate with it. View the file ext.h to get a sense for what is inside of it. Other header files may be included if needed for defining other library or system functions.

    2. object's data structure
    Your program communicates with Max/MSP via a data structure, which in this case is:
    
    typedef struct {
       t_object max_data;
       void*    output;
    } MyObject;
        

    The only requirement for the data structure which you must follow is that the first variable in the struct must be the data type t_object. The name of the t_object is not important and can be anything you give it. In this case the t_object's variable name is max_data. This variable holds information that Max uses to communicate between itself and your object. The t_object variable must come first in your data struct because Max will communicate only with the t_object field of your data struct, and will ignore/does not know the rest of the data struct variables. If the t_object data structure were not first in the struct, then Max would become confused because it does have any other information about your data structure. Try moving it from the first position in the data struct, and see what Max does when you try to use it.

    After the t_object variable comes all of your variables for storing information about the object. In this case there is only one variable: void* output. This variable is used to communicate with the outlet at the bottom of the object. It is setup in the function create_object. It is used in this program in the InputBang function when the Max function outlet_bang is called.

    3. global variables
    void *object_data;
    There is one global data pointer in your object which will eventually point to storage for your data struct. You do not point to it directly, but rather allow the setup() function in main() to do that for you. The name of the pointer in this case is object_data. This name is arbitrary and can be anything you like. The name of the pointer must match the first parameter name in the setup function call in the main function.

    The data type of the pointer must be void* because it will be used to point to several data types. In your object, you will use it as a MyObject pointer. Max will use it as a t_object and t_messlist pointer.

    4. function declarations
    void   main             (void);
    void*  create_object    (void);
    void   InputBang        (MyObject* mo);
    
    The last part of the setup section contains a brief description of every function in the following sections. The compiler needs to know about the functions before using them in the rest of the program. These descriptions of the functions are called the function declarations. You are declaring that you will be defining these functions in the rest of the code. Notice that there are three functions declared here: main, create_object, and InputBang. The only function name which is significant to Max communication is main.

Initialization Section

    There are two functions in the initialization section of the program code: main and create_object. These functions can come in any order in the program.

    The main function's name is special. This is the first function which will be called when the object is used. Step 8h. in the previous lab is where you told CodeWarrior to put this function at the start of the object when it compiles it, so this function name must match the one you give in step 8h of the previous lab.

    Here is the code for the main function with numbers to the left of the code indicating the line numbers:

    
    1.   void main(void) {
    2.      setup((t_messlist**)&object_data, (method)create_object,
    3.            NULL, (short)sizeof(MyObject), NULL, A_NOTHING);
    4.      addbang((method)InputBang); 
    5.   }
    

    The name of the create_object function is arbitrary, but must match the name of the function pointer given as the second parameter in the setup function called in main. The create_object function will create the data storage for the object and initialize all of its data elements.

    Here is the code for the create_object function with numbers to the left of the code indicating the line numbers:

    
    1   void* create_object(void) {
    2      MyObject *mo;                            
    3      mo = (MyObject*)newobject(object_data);  
    4      mo->output = bangout(mo);                
    5      return mo;                               
    6   }

    line one of the program indicates that the initialization function does not need any default parameters when the object is created. It is possible to have several default parameters which will be explained in further labs. In this case, there are no inputs, so the input parameters list is (void).

    The create_object function must return a void pointer. The function will be creating storage space for your object using the struct you defined in the setup section of the code. Eventually the setup function call in the main function will be taking this pointer and assigning it to your global pointer object_data.

    Line 2 of the create_object function creates a temporary pointer for use inside the function. The pointer must be to your data struct, which in this case is MyObject. Note that you can give any name to your data structure other than MyObject.

    Line 3 of the create_object function does a very significant thing: it creates storage space in the computer's memory to store your object data structured (called MyObject).

Behavior Section

    There is one function in the behavior section called InputBang. This function is called whenever a bang is received by the object. To tell the compiler that this function is to be called whenever a bang enters the object, the line

       addbang((method)InputBang);
    
    is necessary in the main function.

    There is one line in the InputBang function:

       outlet_bang(mo->output);
    
    This line sends a bang to the outlet of the object, which is stored in the variable mo->output.