aboutsummaryrefslogtreecommitdiff
path: root/doc/tutorials/externals-howto/example3/counter.c
blob: 5fa8caef98e31dd01c7b17e19fbd8f2528ea2b17 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
/*
 * HOWTO write an External for Pure data
 * (c) 2001-2006 IOhannes m zmölnig zmoelnig[AT]iem.at
 *
 * this is the source-code for the third example in the HOWTO
 * it creates an object that increments and outputs a counter
 * whenever it gets banged.
 * the counter value can be "set" to a special value, or "reset" to a default
 * an upper and lower boundary can be specified: whenever the counter crosses
 * such boundary a "bang" is emitted at the 2nd outlet and the counter value wraps
 *
 * for legal issues please see the file LICENSE.txt
 */


/**
 * include the interface to Pd 
 */
#include "m_pd.h"


/**
 * define a new "class" 
 */
static t_class *counter_class;


/**
 * this is the dataspace of our new object
 * the first element is the mandatory "t_object"
 * then we have all sort of variables for the
 * actual counter value, the step-size and the counting boundaries
 * finally we have 2 "t_outlet" elements so we can send data
 * to a "named" outlet.
 */
typedef struct _counter {
  t_object  x_obj;         /* mandatory t_object */
  t_int i_count;           /* the current counter value */
  t_float step;            /* step size; 
                            * this is "float" because of the passive inlet we are using */
  t_int i_down, i_up;      /* lower and upper boundary */
  t_outlet *f_out, *b_out; /* outlets */
} t_counter;


/**
 * this method is called whenever a "bang" is sent to the object
 */
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;
      /* we crossed the upper boundary, so we send a bang out of 
       * the 2nd outlet (which is x->b_out)
       */
      outlet_bang(x->b_out);
    } else if (x->i_count < x->i_down) {
      x->i_count = x->i_up;
      outlet_bang(x->b_out);
    }
  }
  /* output the current counter value at the 1st outlet (which is x->f_out) */
  outlet_float(x->f_out, f);
}


/**
 * this is called whenever a "reset" message is sent to the inlet of the object
 * since the "reset" message has no arguments (as declared in counter_setup())
 * we only get a reference to the class-dataspace
 */
void counter_reset(t_counter *x)
{
  x->i_count = x->i_down;
}


/**
 * this is called whenever a "set" message is sent to the inlet of the object
 * since the "set" message has one floating-point argument (as declared in counter_setup())
 * we get a reference to the class-dataspace and the value 
 */
void counter_set(t_counter *x, t_floatarg f)
{
  x->i_count = f;
}


/**
 * this is called whenever a "bound" message is sent to the inlet of the object
 * note that in counter_new(), we rewrite a list to the 2nd inlet 
 * to a "bound" message to the 1st inlet
 */
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;
}


/**
 * this is the "constructor" of the class
 * we expect a variable number of arguments to this object
 * symbol "s" is the name of the object itself
 * the arguments are given as a t_atom array of argc elements.
 */
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;

  /* depending on the number of arguments we interprete them differently */
  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:
    break;
  }
  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;

  /* create a new active inlet for this object
   * a message with the selector "list" that is sent
   * to this inlet (it is the 2nd inlet from left),
   * will be appear to be the same message but with the selector "bound"
   * at the 1st inlet.
   * the method for "bound" messages is given in counter_setup()
   */
  inlet_new(&x->x_obj, &x->x_obj.ob_pd,
        gensym("list"), gensym("bound"));

  /* create a passive inlet inlet (it will be the 2rd inlet from left)
   * whenever a floating point number is sent to this inlet,
   * its value will be immediately stored in "x->step"
   * no function will be called
   */
  floatinlet_new(&x->x_obj, &x->step);

  /* create a new outlet which will output floats
   * we store a reference to this outlet in x->f_out
   * so we are able to send data to this very outlet
   */
  x->f_out = outlet_new(&x->x_obj, &s_float);
  /* create a new outlet which will output bangs */
  x->b_out = outlet_new(&x->x_obj, &s_bang);

  return (void *)x;
}


/**
 * define the function-space of the class
 */
void counter_setup(void) {
  counter_class = class_new(gensym("counter"),
                            (t_newmethod)counter_new,
                            0, sizeof(t_counter),
                            CLASS_DEFAULT, 
                            A_GIMME, /* an arbitrary number of arguments 
                                      * which are of arbitrary type */
                            0);

  /* call a function when a "bang" message appears on the first inlet */
  class_addbang  (counter_class, counter_bang);

  /* call a function when a "reset" message (without arguments) appears on the first inlet */
  class_addmethod(counter_class,
        (t_method)counter_reset, gensym("reset"), 0);

  /* call a function when a "set" message with one float-argument (defaults to 0)
   * appears on the first inlet */
  class_addmethod(counter_class, 
        (t_method)counter_set, gensym("set"),
        A_DEFFLOAT, 0);

  /* call a function when a "bound" message with 2 float-argument (both default to 0)
   * appears on the first inlet
   * this is used for "list" messages which appear on the 2nd inlet
   * the magic is done in counter_new()
   */
  class_addmethod(counter_class,
        (t_method)counter_bound, gensym("bound"),
        A_DEFFLOAT, A_DEFFLOAT, 0);

  /* set the name of the help-patch to "help-counter"(.pd) */
  class_sethelpsymbol(counter_class, gensym("help-counter"));
}