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

Subsections

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:

  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 up previous contents
Next: a signal-external: pan~ Up: HOWTO write an External Previous: a simple external: counter   Contents
IOhannes m zmoelnig 2001-09-13