Next: a signal-external: pan~ Up: HOWTO write an External Previous: a simple external: counter Contents
Subsections

- extended dataspace
- extension of the class
- construction of in- and outlets
- extended methodspace
- the code: `counter`

# a complex external: `counter`

The simple counter of the previous chapter can easily be extended to more complexity. It might be quite usefull to be able to reset the counter to an initial value, to set upper and lower boudaries and to control the step-width. Each overrun should send a ``bang''-Message to a second outlet and reset the counter to the initial value.

## extended dataspace

    typedef struct _counter {
      t_object  x_obj;
      t_int i_count;
      t_float step;
      t_int i_down, i_up;
      t_outlet *f_out, *b_out;
    } t_counter;

The dataspace has been extended to hold variables for stepwidth and upper and lower boundaries. Furthermore pointers for two outlets have been added.

## extension of the class

The new classobjects should have methods for different messages, like ``set'' and ``reset''. Therefore the methodspace has to be extended too.

      counter_class = class_new(gensym("counter"),
            (t_newmethod)counter_new,
            0, sizeof(t_counter),
            CLASS_DEFAULT,
            A_GIMME, 0);

The classgenerator `class_new` has been extended by the argument `A_GIMME`. This enables a dynamic number of arguments to be passed at the instantiation of the object.

      class_addmethod(counter_class,
            (t_method)counter_reset,
            gensym("reset"), 0);

`class_addmethod` adds a method for an arbitrary selector to an class.

The first argument is the class the method (second argument) will be added to.

The third argument is the symbolic selector that should be associated with the method.

The remaining ``0''-terminated arguments describe the list of atoms that follows the selector.

      class_addmethod(counter_class,
            (t_method)counter_set, gensym("set"),
            A_DEFFLOAT, 0);
      class_addmethod(counter_class,
            (t_method)counter_bound, gensym("bound"),
            A_DEFFLOAT, A_DEFFLOAT, 0);

A method for ``set'' followed by a numerical value is added, as well as a method for the selector ``bound'' followed by two numerical values.

      class_sethelpsymbol(counter_class, gensym("help-counter"));

If a pd-object is right-clicked, a help-patch describing the object-class can be opened. By default, this patch is located in the directory ``doc/5.reference/'' and is named like the symbolic classname.

An alternative help-patch can be defined with the `class_sethelpsymbol`-command.

## construction of in- and outlets

When creating the object, several arguments should be passed by the user.

    void *counter_new(t_symbol *s, int argc, t_atom *argv)

Because of the declaration of arguments in the `class_new`-function with `A_GIMME`, the constructor has following arguments:

`t_symbol *s` | the symbolic name,
| that was used for object creation
`int argc` | the numer of arguments passed to the object
`t_atom *argv` | a pointer to a list of `argc` atoms

      t_float f1=0, f2=0;

      x->step=1;
      switch(argc){
      default:
      case 3:
        x->step=atom_getfloat(argv+2);
      case 2:
        f2=atom_getfloat(argv+1);
      case 1:
        f1=atom_getfloat(argv);
        break;
      case 0:
      }
      if (argc<2)f2=f1;
      x->i_down = (f1<f2)?f1:f2;
      x->i_up   = (f1>f2)?f1:f2;

      x->i_count=x->i_down;

If three arguments are passed, these should be the lower boundary, the upper boundary and the step width.

If only two arguments are passed, the step-width defaults to ``1''. If only one argument is passed, this should be the initial value of the counter with step-width of ``1''.

      inlet_new(&x->x_obj, &x->x_obj.ob_pd,
            gensym("list"), gensym("bound"));

The function `inlet_new` creates a new ``active'' inlet. ``Active'' means, that a class-method is called each time a message is sent to an ``active'' inlet.

Due to the software-architecture, the first inlet is always ``active''.

The first two arguments of the `inlet_new`-function are pointers to the interna of the object and to the graphical presentation of the object.

The symbolic selector that is specified by the third argument is to be substituted by another symbolic selector (fourth argument) for this inlet.

Because of this substitution of selectors, a message on a certain right inlet can be treated as a message with a certain selector on the leftmost inlet.

This means:

- The substituting selector has to be declared by `class_addmethod` in the setup-routine.
- It is possible to simulate a certain right inlet, by sending a message with this inlet's selector to the leftmost inlet.
- It is not possible to add methods for more than one selector to a right inlet. Particularly it is not possible to add a universal method for arbitrary selectors to a right inlet.

      floatinlet_new(&x->x_obj, &x->step);

`floatinlet_new` generates a new ``passive'' inlet for numerical values. ``Passive'' inlets allow parts of the dataspace-memory to be written directly from outside. Therefore it is not possible to check for illegal inputs.

The first argument is a pointer to the internal infrastructure of the object. The second argument is the address in the dataspace-memory, where other objects can write too.

``Passive'' inlets can be created for pointers, symbolic or numerical (floatingpoint3) values.

      x->f_out = outlet_new(&x->x_obj, &s_float);
      x->b_out = outlet_new(&x->x_obj, &s_bang);

The pointers returned by `outlet_new` have to be saved in the classdataspace to be used later by the outlet-routines.

The order of the generation of inlets and outlets is important, since it corresponds to the order of inlets and outlets in the graphical representation of the object.

## extended methodspace

The method for the ``bang''-message has to fullfill the more complex tasks.

    void counter_bang(t_counter *x)
    {
      t_float f=x->i_count;
      t_int step = x->step;
      x->i_count+=step;
      if (x->i_down-x->i_up) {
        if ((step>0) && (x->i_count > x->i_up)) {
          x->i_count = x->i_down;
          outlet_bang(x->b_out);
        } else if (x->i_count < x->i_down) {
          x->i_count = x->i_up;
          outlet_bang(x->b_out);
        }
      }
      outlet_float(x->f_out, f);
    }

Each outlet is identified by the `outlet_...`-functions via the pointer to this outlets.

The remaining methods still have to be implemented:

    void counter_reset(t_counter *x)
    {
      x->i_count = x->i_down;
    }

    void counter_set(t_counter *x, t_floatarg f)
    {
      x->i_count = f;
    }

    void counter_bound(t_counter *x, t_floatarg f1, t_floatarg f2)
    {
      x->i_down = (f1<f2)?f1:f2;
      x->i_up   = (f1>f2)?f1:f2;
    }

## the code: `counter`

    #include "m_pd.h"

    static t_class *counter_class;

    typedef struct _counter {
      t_object  x_obj;
      t_int i_count;
      t_float step;
      t_int i_down, i_up;
      t_outlet *f_out, *b_out;
    } t_counter;

    void counter_bang(t_counter *x)
    {
      t_float f=x->i_count;
      t_int step = x->step;
      x->i_count+=step;

      if (x->i_down-x->i_up) {
        if ((step>0) && (x->i_count > x->i_up)) {
          x->i_count = x->i_down;
          outlet_bang(x->b_out);
        } else if (x->i_count < x->i_down) {
          x->i_count = x->i_up;
          outlet_bang(x->b_out);
        }
      }

      outlet_float(x->f_out, f);
    }

    void counter_reset(t_counter *x)
    {
      x->i_count = x->i_down;
    }

    void counter_set(t_counter *x, t_floatarg f)
    {
      x->i_count = f;
    }

    void counter_bound(t_counter *x, t_floatarg f1, t_floatarg f2)
    {
      x->i_down = (f1<f2)?f1:f2;
      x->i_up   = (f1>f2)?f1:f2;
    }

    void *counter_new(t_symbol *s, int argc, t_atom *argv)
    {
      t_counter *x = (t_counter *)pd_new(counter_class);
      t_float f1=0, f2=0;

      x->step=1;
      switch(argc){
      default:
      case 3:
        x->step=atom_getfloat(argv+2);
      case 2:
        f2=atom_getfloat(argv+1);
      case 1:
        f1=atom_getfloat(argv);
        break;
      case 0:
      }
      if (argc<2)f2=f1;

      x->i_down = (f1<f2)?f1:f2;
      x->i_up   = (f1>f2)?f1:f2;

      x->i_count=x->i_down;

      inlet_new(&x->x_obj, &x->x_obj.ob_pd,
            gensym("list"), gensym("bound"));
      floatinlet_new(&x->x_obj, &x->step);

      x->f_out = outlet_new(&x->x_obj, &s_float);
      x->b_out = outlet_new(&x->x_obj, &s_bang);

      return (void *)x;
    }

    void counter_setup(void) {
      counter_class = class_new(gensym("counter"),
            (t_newmethod)counter_new,
            0, sizeof(t_counter),
            CLASS_DEFAULT,
            A_GIMME, 0);

      class_addbang  (counter_class, counter_bang);
      class_addmethod(counter_class,
            (t_method)counter_reset, gensym("reset"), 0);
      class_addmethod(counter_class,
            (t_method)counter_set, gensym("set"),
            A_DEFFLOAT, 0);
      class_addmethod(counter_class,
            (t_method)counter_bound, gensym("bound"),
            A_DEFFLOAT, A_DEFFLOAT, 0);

      class_sethelpsymbol(counter_class, gensym("help-counter"));
    }

Next: a signal-external: pan~ Up: HOWTO write an External Previous: a simple external: counter Contents