All methods and concepts that can be realized with normal objectclasses can therefore be realized with signalclasses too.
Per agreement, the symbolic names of signalclasses end with a tilde ~.
The class ``pan~'' shall demonstrate, how signalclasses are written.
A signal on the left inlet is mixed with a signal on the second inlet.
Der mixing-factor between 0 and 1 is defined via a t_float-message
on a third inlet.
typedef struct _pan_tilde {
  t_object x_obj;
  t_sample f_pan;
  t_float  f;
} t_pan_tilde;
Only one variable f_pan for the mixing-factor of the panning-function is needed.
The other variable f is needed whenever a signal-inlet is needed too.
If no signal but only a float-message is present at a signal-inlet, this
variable is used to automatically convert the float to signal.
void pan_tilde_setup(void) {
  pan_tilde_class = class_new(gensym("pan~"),
        (t_newmethod)pan_tilde_new,
        0, sizeof(t_pan_tilde),
        CLASS_DEFAULT, 
        A_DEFFLOAT, 0);
  class_addmethod(pan_tilde_class,
        (t_method)pan_tilde_dsp, gensym("dsp"), 0);
  CLASS_MAINSIGNALIN(pan_tilde_class, t_pan_tilde, f);
}
A method for signal-processing has to be provided by each signalclass.
Whenever pd's audioengine is started, a message with the selector ``dsp'' is sent to each object. Each class that has a method for the ``dsp''-message is recognized as signalclass.
Signalclasses that want to provide signal-inlets have to
declare this via the CLASS_MAINSIGNALIN-macro.
This enables signals at the first (default) inlet.
If more than one signal-inlet is needed, they have to be created explicitly
in the constructor-method.
Inlets that are declared as signal-inlets cannot provide
methods for t_float-messages any longer.
The first argument of the macro is a pointer to the signalclass. The second argument is the type of the class's dataspace.
The last argument is a dummy-variable out of the dataspace that is needed
to replace non-existing signal at the signal-inlet(s) with t_float-messages.
void *pan_tilde_new(t_floatarg f)
{
  t_pan_tilde *x = (t_pan_tilde *)pd_new(pan_tilde_class);
  x->f_pan = f;
  
  inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
  floatinlet_new (&x->x_obj, &x->f_pan);
  outlet_new(&x->x_obj, &s_signal);
  return (void *)x;
}
Additional signal-inlets are added like other inlets with the routine inlet_new.
The last two arguments are references to the symbolic selector ``signal''
in the lookup-table.
Signal-outlets are also created like normal (message-)outlets, by setting the outlet-selector to ``signal''.
The ``dsp''-method has two arguments, the pointer to the class-dataspace, and a pointer to an array of signals.
The signals are arranged in the array in such way, that they are ordered in a clockwise way in the graphical representation of the object.4
void pan_tilde_dsp(t_pan_tilde *x, t_signal **sp)
{
  dsp_add(pan_tilde_perform, 5, x,
          sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n);
}
dsp_add adds a perform-routine (as declared in the first argument)
to the DSP-tree.
The second argument is the number of the following pointers to diverse variables. Which pointers to which variables are passed is not limited.
Here, sp[0] is the first in-signal, sp[1] represents the second in-signal and sp[3] points to the out-signal.
The structure t_signal contains a pointer to the
its signal-vector ().s_vec (an array of samples of type  t_sample),
and the length of this signal-vector ().s_n.
Since all signalvectors of a patch (not including it's sub-patches) are of the same length, it is sufficient to get the length of one of these vectors.
A pointer to an integer-array is passed to it.
This array contains the pointers, that were passed via dsp_add,
which must be casted back to their real type.
The perform-routine has to return a pointer to integer,
that points to the address behind the stored pointers of the routine.
This means, that the return argument equals the
argument of the perform-routine plus
the number of pointervariables (as declared as the second argument
of dsp_add) plus one.
t_int *pan_tilde_perform(t_int *w)
{
  t_pan_tilde *x = (t_pan_tilde *)(w[1]);
  t_sample  *in1 =    (t_sample *)(w[2]);
  t_sample  *in2 =    (t_sample *)(w[3]);
  t_sample  *out =    (t_sample *)(w[4]);
  int          n =           (int)(w[5]);
  t_sample f_pan = (x->f_pan<0)?0.0:(x->f_pan>1)?1.0:x->f_pan;
  while (n--) *out++ = (*in1++)*(1-f_pan)+(*in2++)*f_pan;
  return (w+6);
}
Each sample of the signalvectors is read and manipulated in the while-loop.
Optimization of the DSP-tree tries to avoid unnecessary copy-operations. Therefore it is possible, that in- and out-signal are located at the same address in the memory. In this case, the programmer has to be careful not to write into the out-signal before having read the in-signal to avoid overwriting data that is not yet saved.
#include "m_pd.h"
static t_class *pan_tilde_class;
typedef struct _pan_tilde {
  t_object  x_obj;
  t_sample f_pan;
  t_sample f;
} t_pan_tilde;
t_int *pan_tilde_perform(t_int *w)
{
  t_pan_tilde *x = (t_pan_tilde *)(w[1]);
  t_sample  *in1 =    (t_sample *)(w[2]);
  t_sample  *in2 =    (t_sample *)(w[3]);
  t_sample  *out =    (t_sample *)(w[4]);
  int          n =           (int)(w[5]);
  t_sample f_pan = (x->f_pan<0)?0.0:(x->f_pan>1)?1.0:x->f_pan;
  while (n--) *out++ = (*in1++)*(1-f_pan)+(*in2++)*f_pan;
  return (w+6);
}
void pan_tilde_dsp(t_pan_tilde *x, t_signal **sp)
{
  dsp_add(pan_tilde_perform, 5, x,
          sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n);
}
void *pan_tilde_new(t_floatarg f)
{
  t_pan_tilde *x = (t_pan_tilde *)pd_new(pan_tilde_class);
  x->f_pan = f;
  
  inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
  floatinlet_new (&x->x_obj, &x->f_pan);
  outlet_new(&x->x_obj, &s_signal);
  return (void *)x;
}
void pan_tilde_setup(void) {
  pan_tilde_class = class_new(gensym("pan~"),
        (t_newmethod)pan_tilde_new,
        0, sizeof(t_pan_tilde),
        CLASS_DEFAULT, 
        A_DEFFLOAT, 0);
  class_addmethod(pan_tilde_class,
        (t_method)pan_tilde_dsp, gensym("dsp"), 0);
  CLASS_MAINSIGNALIN(pan_tilde_class, t_pan_tilde, f);
}