3 a simple external: counter

Now we want to realize a simple counter as an external. A “bang”-trigger outputs the counter-value on the outlet and afterwards increases the counter-value by 1.

This class is similar to the previous one, but the data space is extended by a variable “counter” and the result is written as a message to an outlet instead of a string to the standard error.

3.1 object-variables

Of course, a counter needs a state-variable to store the actual counter-value.

State-variables that belong to class instances belong to the data space.

typedef struct _counter {  
  t_object  x_obj;  
  t_int i_count;  
} t_counter;

The integer variable i_count stores the counter-value.

3.2 object-arguments

It is quite useful for a counter, if a initial value can be defined by the user. Therefore this initial value should be passed to the object at creation-time.

void counter_setup(void) {  
  counter_class = class_new(gensym("counter"),  
        (t_newmethod)counter_new,  
        0, sizeof(t_counter),  
        CLASS_DEFAULT,  
        A_DEFFLOAT, 0);  
 
  class_addbang(counter_class, counter_bang);  
}

So we have an additional argument in the function class_new: A_DEFFLOAT tells Pd, that the object needs one argument of the type t_floatarg. If no argument is passed, this will default to “0”.

3.3 constructor

The constructor has some new tasks. On the one hand, a variable value has to be initialised, on the other hand, an outlet for the object has to be created.

void *counter_new(t_floatarg f)  
{  
  t_counter *x = (t_counter *)pd_new(counter_class);  
 
  x->i_count=f;  
  outlet_new(&x->x_obj, &s_float);  
 
  return (void *)x;  
}

The constructor-method has one argument of type t_floatarg as declared in the setup-routine by class_new. This argument is used to initialise the counter.

A new outlet is created with the function outlet_new. The first argument is a pointer to the interna of the object the new outlet is created for.

The second argument is a symbolic description of the outlet-type. Since out counter should output numeric values it is of type “float”.

outlet_new returns a pointer to the new outlet and saves this very pointer in the t_object-variable x_obj.ob_outlet. If only one outlet is used, the pointer need not additionally be stored in the data space. If more than one outlets are used, the pointers have to be stored in the data space, because the t_object-variable can only hold one outlet pointer.

3.4 the counter method

When triggered, the counter value should be sent to the outlet and afterwards be incremented by 1.

void counter_bang(t_counter *x)  
{  
  t_float f=x->i_count;  
  x->i_count++;  
  outlet_float(x->x_obj.ob_outlet, f);  
}

The function outlet_float sends a floating-point-value (second argument) to the outlet that is specified by the first argument.

We first store the counter in a floating point-buffer. Afterwards the counter is incremented and not before that the buffer variable is sent to the outlet.

What appears to be unnecessary on the first glance, makes sense after further inspection: The buffer variable has been realized as t_float, since outlet_float expects a floating point-value and a typecast is inevitable.

If the counter value was sent to the outlet before being incremented, this could result in an unwanted (though well defined) behaviour: If the counter-outlet directly triggered its own inlet, the counter-method would be called although the counter value was not yet incremented. Normally this is not what we want.

The same (correct) result could of course be obtained with a single line, but this would obscure the reentrant-problem.

3.5 the code: counter

#include "m_pd.h"  
 
static t_class *counter_class;  
 
typedef struct _counter {  
  t_object  x_obj;  
  t_int i_count;  
} t_counter;  
 
void counter_bang(t_counter *x)  
{  
  t_float f=x->i_count;  
  x->i_count++;  
  outlet_float(x->x_obj.ob_outlet, f);  
}  
 
void *counter_new(t_floatarg f)  
{  
  t_counter *x = (t_counter *)pd_new(counter_class);  
 
  x->i_count=f;  
  outlet_new(&x->x_obj, &s_float);  
 
  return (void *)x;  
}  
 
void counter_setup(void) {  
  counter_class = class_new(gensym("counter"),  
        (t_newmethod)counter_new,  
        0, sizeof(t_counter),  
        CLASS_DEFAULT,  
        A_DEFFLOAT, 0);  
 
  class_addbang(counter_class, counter_bang);  
}