Jamoma API  0.6.0.a19
j.meter~.cpp
Go to the documentation of this file.
1 /** @file
2  *
3  * @ingroup implementationMaxExternalsDSP
4  *
5  * @brief j.meter~ : Audio signal level meter GUI.
6  *
7  * @details This external is deprecated, use live.meter~ instead.
8  *
9  * @authors Tim Place, Thomas Grill, Trond Lossius
10  *
11  * @copyright © 2005 by Timothy Place @n
12  * This code is licensed under the terms of the "New BSD License" @n
13  * http://creativecommons.org/licenses/BSD/
14  */
15 
16 
17 #include "ext.h"
18 #include "ext_obex.h"
19 #include "ext_user.h"
20 #include "ext_common.h"
21 #include "jpatcher_api.h" // jpatcher_api.h must come before z_dsp.h (in Jamoma.h)
22 #include "jgraphics.h"
23 //#include "Jamoma.h"
24 //#include "z_dsp.h"
25 //#include "TTDSP.h"
26 #include "TTClassWrapperMax.h"
27 
28 
29 // Constants
30 const double kPollIntervalDefault = 150;
31 const double kPollIntervalMinimum = 50;
32 const double kPollIntervalMaximum = 5000;
33 const double kHeightDefault = 13;
34 const double kHeightMinimum = 1;
35 const double kHeightMaximum = 400;
36 const double kWidthDefault = 80;
37 const double kWidthMinimum = 1;
38 const double kWidthMaximum = 1200;
39 const double kOrientationVertical = 1;
40 const double kOrientationHorizontal = 0;
41 const double kMinimumChangeForRedraw = 0.00001;
42 
43 
44 // Enumerations
45 enum {
46  ORIENTATION_AUTOMATIC = 0,
47  ORIENTATION_HORIZONTAL = 1,
48  ORIENTATION_VERTICAL = 2
49 };
50 
51 
52 // Instance definition
53 typedef struct _meter{
54  t_pxjbox obj;
55  t_symbol* attrMode; // TODO: options are 'fast' and 'pretty' -- fast mode doesn't scale properly when zooming a patcher, etc. but it's faster
56  long attrDefeat; // disable the meter
57  char attrNumChannels; // TODO: number of audio channels to display
58  float attrInterval; // TODO: make the polling interval dynamic
59  t_jrgba attrBgColor; ///< Background color
60  t_jrgba attrBorderColor;
61  long attrOrientation; // the orientation mode
62 
63  long effectOrientation; // the effective orientation of the object (0: vertical, 1: horizontal)
64  TTFloat32 envelope; // the result of the amplitude analysis [0.0, 1.0]
65  TTFloat64 newEnvelope;
66  TTFloat32 peak; // the loudest sample since the last reset
67  t_jsurface* gradientSurface; ///< precalculated and drawn gradient for the size of this instance
68  t_rect gradientRect;
69  void* clock;
70  void *outlet; ///< Outlet
71 } t_meter;
72 
73 
74 // prototypes
75 void* meter_new(t_symbol *s, long argc, t_atom *argv);
76 void meter_free(t_meter *x);
77 t_max_err meter_notify(t_meter *x, t_symbol *s, t_symbol *msg, void *sender, void *data);
78 void meter_assist(t_meter *x, void *b, long m, long a, char *s);
79 void meter_bang(t_meter *x);
80 void meter_int(t_meter *x, long value);
81 void meter_float(t_meter *x, double value);
82 void meter_set(t_meter *x, double value);
83 void meter_clock(t_meter *x);
84 t_int* meter_perform(t_int *w);
85 void meter_perform64(t_meter *x, t_object *dsp64, double **ins, long numins, double **outs, long numouts, long sampleframes, long flags, void *userparam);
86 void meter_dsp(t_meter *x, t_signal **sp, short *count);
87 void meter_dsp64(t_meter *x, t_object *dsp64, short *count, double samplerate, long maxvectorsize, long flags);
88 void meter_paint(t_meter *x, t_object *view);
89 void meter_dopaint_horizontal(t_meter *x, t_object *view);
90 void meter_dopaint_vertical(t_meter *x, t_object *view);
91 void* meter_oksize(t_meter *x, t_rect *newrect);
92 void meterEffectOrientation(t_meter* x);
93 void meterCacheSurface(t_meter* x);
94 
95 // globals
96 static t_class* s_meter_class;
97 
98 
99 // implementation
100 #if 0
101 #pragma mark -
102 #pragma mark Class Definition
103 #endif // 0
104 
105 int C74_EXPORT main(void)
106 {
107  t_class *c = class_new("j.meter~", (method)meter_new, (method)meter_free, sizeof(t_meter), (method)NULL, A_GIMME, 0L);
108 
109  c->c_flags |= CLASS_FLAG_NEWDICTIONARY; // use dictionary constructor
110  jbox_initclass(c, 0);
111  class_dspinitjbox(c);
112 
113 // jamoma_init();
114  common_symbols_init();
115 
116  class_addmethod(c, (method)meter_bang, "bang", 0);
117  class_addmethod(c, (method)meter_int, "int", A_LONG, 0);
118  class_addmethod(c, (method)meter_float, "float", A_FLOAT, 0);
119  class_addmethod(c, (method)meter_set, "set", A_FLOAT, 0);
120  class_addmethod(c, (method)meter_dsp, "dsp", A_CANT, 0);
121  class_addmethod(c, (method)meter_dsp64, "dsp64", A_CANT, 0);
122  class_addmethod(c, (method)meter_paint, "paint", A_CANT, 0);
123  class_addmethod(c, (method)meter_oksize, "oksize", A_CANT, 0);
124  class_addmethod(c, (method)meter_bang, "mousedown", A_CANT, 0);
125  class_addmethod(c, (method)meter_notify, "notify", A_CANT, 0);
126  class_addmethod(c, (method)meter_assist, "assist", A_CANT, 0);
127 
128 // CLASS_ATTR_FLOAT(c, "interval", 0, t_meter, attrInterval);
129 // CLASS_ATTR_FILTER_CLIP(c, "interval", kPollIntervalMinimum, kPollIntervalMaximum);
130 // CLASS_ATTR_DEFAULT_SAVE(c, "interval", 0, "150");
131 // CLASS_ATTR_LABEL(c, "interval", 0, "Refresh Interval in Milliseconds");
132 
133 // CLASS_ATTR_RGBA(c, "bordercolor", 0, t_meter, attrBorderColor);
134 // CLASS_ATTR_STYLE_LABEL(c, "bordercolor", 0, "rgba", "Border Color");
135 // CLASS_ATTR_DEFAULT_SAVE_PAINT(c, "bordercolor", 0, "0.2 0.2 0.2 1.");
136 
137  CLASS_ATTR_LONG(c, "orientation", 0, t_meter, attrOrientation);
138  CLASS_ATTR_LABEL(c, "orientation", 0, "Orientation");
139  CLASS_ATTR_CATEGORY(c, "orientation", 0,"Appearance");
140  CLASS_ATTR_ENUMINDEX(c, "orientation", 0, "Automatic Horizontal Vertical");
141  CLASS_ATTR_DEFAULT_SAVE_PAINT(c, "orientation", 0,"0");
142 
143  CLASS_ATTR_RGBA(c, "bgcolor", 0, t_meter, attrBgColor);
144  CLASS_ATTR_DEFAULTNAME_SAVE_PAINT(c, "bgcolor", 0, "0.1 0.1 0.1 1.0");
145  CLASS_ATTR_STYLE(c, "bgcolor", 0, "rgba");
146  CLASS_ATTR_LABEL(c, "bgcolor", 0, "Background Color");
147 
148  CLASS_ATTR_LONG(c, "defeat", 0, t_meter, attrDefeat);
149  CLASS_ATTR_LABEL(c, "defeat", 0, "defeat");
150  CLASS_ATTR_DEFAULT(c, "defeat", 0, "0");
151  CLASS_ATTR_SAVE(c, "defeat", 0);
152  CLASS_ATTR_STYLE(c, "defeat", 0, "onoff");
153  CLASS_ATTR_CATEGORY(c, "defeat", 0,"Behavior");
154 
155  CLASS_ATTR_DEFAULT(c, "patching_rect", 0, "0. 0. 100. 12.");
156  CLASS_ATTR_MIN(c, "patching_size", 0, "1. 1.");
157 
158 // CLASS_ATTR_DEFAULT_SAVE_PAINT(c, "bgcolor", 0, "0.1 0.1 0.1 1.0");
159 
160  class_register(CLASS_BOX, c);
161  s_meter_class = c;
162  return 0;
163 }
164 
165 
166 
167 
168 #if 0
169 #pragma mark -
170 #pragma mark Life Cycle
171 #endif // 0
172 
173 void *meter_new(t_symbol *s, long argc, t_atom *argv)
174 {
175  t_meter* x;
176  t_jbox* box;
177  long flags;
178 
179  t_dictionary *d=NULL;
180 
181  if (!(d=object_dictionaryarg(argc,argv)))
182  return NULL;
183 
184  x = (t_meter *)object_alloc(s_meter_class);
185 
186  flags = 0
187  | JBOX_DRAWFIRSTIN
188  | JBOX_NODRAWBOX
189  | JBOX_DRAWINLAST
190  // | JBOX_TRANSPARENT
191  // | JBOX_NOGROW
192  // | JBOX_GROWY
193  | JBOX_GROWBOTH
194  // | JBOX_HILITE
195  // | JBOX_BACKGROUND
196  // | JBOX_DRAWBACKGROUND
197  // | JBOX_NOFLOATINSPECTOR
198  // | JBOX_TEXTFIELD
199  // | JBOX_MOUSEDRAGDELTA
200  // | JBOX_TEXTFIELD
201  ;
202 
203  box = (t_jbox *)x;
204  jbox_new(box, flags, argc, argv);
205  x->obj.z_box.b_firstin = (t_object*)x;
206  dsp_setupjbox((t_pxjbox *)x, 1);
207  x->clock = clock_new(x, (method)meter_clock);
208  meterEffectOrientation(x);
209  x->outlet = outlet_new(x, 0); //adding a outlet
210  attr_dictionary_process(x,d);
211  jbox_ready((t_jbox *)x);
212  return x;
213 }
214 
215 
216 void meter_free(t_meter *x)
217 {
218  dsp_freejbox((t_pxjbox *)x);
219  jgraphics_surface_destroy(x->gradientSurface);
220  object_free((t_object *)x->clock);
221  jbox_free((t_jbox *)x);
222 }
223 
224 
225 #if 0
226 #pragma mark -
227 #pragma mark Methods
228 #endif // 0
229 
230 t_max_err meter_notify(t_meter *x, t_symbol *s, t_symbol *msg, void *sender, void *data)
231 {
232  t_symbol* name;
233 
234  if (msg == _sym_attr_modified) {
235 
236  name = (t_symbol *)object_method((t_object *)data, _sym_getname);
237 
238  if (name == _sym_bgcolor)
239  jbox_redraw((t_jbox*)x);
240  if (name == _sym_patching_rect || name == gensym("orientation"))
241  meterEffectOrientation(x);
242  if (name == gensym("defeat")) {
243  if (sys_getdspstate()) { // if dsp is on & defeat is off then we schedule another tick
244  if (x->attrDefeat == 0)
245  clock_delay(x->clock, kPollIntervalDefault);
246  }
247  }
248  }
249  return jbox_notify((t_jbox*)x, s, msg, sender, data);
250 }
251 
252 
253 // Method for Assistance Messages
254 void meter_assist(t_meter *x, void *b, long msg, long arg, char *dst)
255 {
256  if (msg==1) // Inlet
257  strcpy(dst, "Input");
258  else if (msg==2) { // Outlets
259  switch(arg) {
260  case 0: strcpy(dst, "Output"); break;
261  //case 1: strcpy(dst, "Attribute Stuff"); break;
262  }
263  }
264 }
265 
266 
267 // Method: bang - clear the peak hold and redraw
268 void meter_bang(t_meter *x)
269 {
270  x->peak = 0;
271  jbox_redraw((t_jbox*)x);
272 }
273 
274 
275 void meter_int(t_meter *x, long value)
276 {
277  meter_float(x,(double)value);
278 }
279 
280 void meter_float(t_meter *x, double value)
281 {
282  x->envelope = value;
283  outlet_float(x->outlet, x->envelope);
284  jbox_redraw((t_jbox*)x);
285 }
286 
287 
288 // Method: set - update and redraw, but no output
289 void meter_set(t_meter *x, double value)
290 {
291  x->envelope = value;
292  jbox_redraw((t_jbox*)x);
293 
294 }
295 
296 
297 
298 void meter_clock(t_meter *x)
299 {
300  double delta = fabs(x->newEnvelope - x->envelope);
301  x->envelope = x->newEnvelope;
302  x->newEnvelope = 0;
303  // Only re-draw if there was a change of some significance
304  if (delta > kMinimumChangeForRedraw)
305  jbox_redraw((t_jbox *)x);
306 
307  outlet_float(x->outlet, x->envelope);
308  if (sys_getdspstate()) { // if dsp is on then we schedule another tick
309  if (x->attrDefeat == 0)
310  clock_delay(x->clock, kPollIntervalDefault);
311  }
312 }
313 
314 
315 #if 0
316 #pragma mark -
317 #pragma mark Signal Processing
318 #endif // 0
319 
320 t_int *meter_perform(t_int *w)
321 {
322  t_meter *x = (t_meter *)(w[1]);
323  t_float *input = (float *)(w[2]);
324  long n = (long)(w[3]);
325  float currentvalue;
326 
327  if (x->obj.z_disabled)
328  goto out;
329 
330  while (n--) {
331  currentvalue = ((*input) < 0)?-(*input):*input; // get the current sample's absolute value
332  if (currentvalue > x->newEnvelope) // if it's a new peak amplitude...
333  x->newEnvelope = currentvalue;
334  input++; // increment pointer in the vector
335  }
336 out:
337  return w+4;
338 }
339 
340 
341 
342 void meter_perform64(t_meter *x, t_object *dsp64, double **ins, long numins, double **outs, long numouts, long sampleframes, long flags, void *userparam)
343 {
344  TTFloat64 *in = ins[0]; // we get audio for each inlet of the object from the **ins argument
345  int n = sampleframes;
346  double currentvalue;
347 
348  while (n--) {
349  currentvalue = ((*in) < 0)?-(*in):*in; // get the current sample's absolute value
350  if (currentvalue > x->newEnvelope) // if it's a new peak amplitude...
351  x->newEnvelope = currentvalue;
352  in++; // increment pointer in the vector
353  }
354 }
355 
356 
357 void meter_dsp(t_meter *x, t_signal **sp, short *count)
358 {
359  if (count[0]) {
360  dsp_add(meter_perform, 3, x, sp[0]->s_vec, sp[0]->s_n);
361  clock_delay(x->clock, kPollIntervalDefault); // start the clock
362  x->peak = 0;
363  }
364 }
365 
366 
367 void meter_dsp64(t_meter *x, t_object *dsp64, short *count, double samplerate, long maxvectorsize, long flags)
368 {
369  if (count[0]) {
370  object_method(dsp64, gensym("dsp_add64"), x, meter_perform64, 0, NULL);
371  //dsp_add64(dsp64, (t_object*)x, (t_perfroutine64)meter_perform64, 0, NULL);
372  clock_delay(x->clock, kPollIntervalDefault); // start the clock
373  x->peak = 0;
374  }
375 }
376 
377 
378 #if 0
379 #pragma mark -
380 #pragma mark User Interface
381 #endif // 0
382 
383 void *meter_oksize(t_meter *x, t_rect *newrect)
384 {
385  TTLimit(newrect->width, kWidthMinimum, kWidthMaximum);
386  TTLimit(newrect->height, kHeightMinimum, kHeightMaximum);
387  meterCacheSurface(x); // Now draw the gradient and cache it in our surface
388  return (void*)1;
389 }
390 
391 // To set the effective orientation
392 void meterEffectOrientation(t_meter* x) {
393 
394  t_jbox* box = (t_jbox*)x;
395  long lastEffectOrientation = x->effectOrientation;
396 
397  switch(x->attrOrientation) {
398  case 0 : // automatic mode
399  x->effectOrientation = box->b_patching_rect.width > box->b_patching_rect.height;
400  break;
401 
402  case 1 : // horizontal mode
403  x->effectOrientation = 1;
404  break;
405 
406  case 2 : // vertical mode
407  x->effectOrientation = 0;
408  break;
409 
410  default :
411  break;
412  }
413  if (x->effectOrientation != lastEffectOrientation)
414  jbox_redraw((t_jbox*)x);
415 }
416 
417 
418 void meterCacheSurface(t_meter* x)
419 {
420  t_jrgba color;
421  t_jbox* box = (t_jbox*)x;
422  int i, j;
423 
424  x->gradientRect.x = 0;
425  x->gradientRect.y = 0;
426  // horizontal mode
427  if (x->effectOrientation) {
428  x->gradientRect.width = box->b_patching_rect.width * 0.96;
429  x->gradientRect.height = box->b_patching_rect.height;
430  }
431  // vertical mode
432  else {
433  x->gradientRect.width = box->b_patching_rect.width;
434  x->gradientRect.height = box->b_patching_rect.height * 0.96;
435  }
436 
437  if (x->gradientSurface)
438  jgraphics_surface_destroy(x->gradientSurface);
439 
440  x->gradientSurface = jgraphics_image_surface_create(JGRAPHICS_FORMAT_ARGB32, x->gradientRect.width, x->gradientRect.height);
441 
442  color.red = 0.0;
443  color.green = 1.0;
444  color.blue = 0.0;
445  color.alpha = 1.0;
446 
447  // horizontal mode
448  if (x->effectOrientation) {
449  for (i=0; i < x->gradientRect.width; i++) {
450  color.red = i / x->gradientRect.width;
451  for (j=0; j < x->gradientRect.height; j++)
452  jgraphics_image_surface_set_pixel(x->gradientSurface, i, j, color);
453  }
454  }
455  else {
456  for (j=0; j < x->gradientRect.height; j++) {
457  color.red = 1. - (j / x->gradientRect.height);
458  for (i=0; i < x->gradientRect.width; i++)
459  jgraphics_image_surface_set_pixel(x->gradientSurface, i, j, color);
460  }
461  }
462 }
463 
464 
465 void meter_paint(t_meter *x, t_object *view)
466 {
467  if (x->effectOrientation)
468  meter_dopaint_horizontal(x,view);
469  else
470  meter_dopaint_vertical(x,view);
471 }
472 
473 
474 void meter_dopaint_horizontal(t_meter *x, t_object *view) {
475 
476  t_rect rect;
477  t_jgraphics* g;
478  double level = TTClip(x->envelope, 0.0f, 1.0f);
479  double position;
480  double peakPosition;
481  t_jrgba c;
482 
483  if (level > 0.0)
484  level = pow(level, kTTGainMidiPowerInv); // this is taken from the midi conversion in the Gain Dataspace
485 
486  g = (t_jgraphics*) patcherview_get_jgraphics(view); // obtain graphics context
487  jbox_get_rect_for_view((t_object *)x, view, &rect); // this is the box rectangle -- but we draw relative to 0 0, and thus only care about width & height
488  rect.x = 0;
489  rect.y = 0;
490  position = rect.width * level * 0.96;
491  peakPosition = rect.width * x->peak * 0.96;
492 
493  if (level > x->peak)
494  x->peak = level;
495 
496  // TODO: Can we export this from the kernel???
497  // jgraphics_image_surface_draw_fast(g, x->gradientSurface);
498  jgraphics_image_surface_draw(g, x->gradientSurface, x->gradientRect, rect);
499 
500  jgraphics_set_source_jrgba(g, &x->attrBgColor);
501  jgraphics_rectangle_fill_fast(g, position, 0, rect.width-position, rect.height);
502 
503  if (x->envelope > 1.0 || x->peak > 1.0) {
504  c.red = 1.0;
505  c.green = c.blue = 0.0;
506  c.alpha = 1.0;
507  jgraphics_set_source_jrgba(g, &c);
508  jgraphics_rectangle_fill_fast(g, rect.width - (rect.width * .04), 0, rect.width * .04, rect.height);
509  }
510  else {
511  c.red = peakPosition / x->gradientRect.width;
512  c.green = 1.0;
513  c.blue = 0.0;
514  c.alpha = 1.0;
515  jgraphics_set_source_jrgba(g, &c);
516  // TODO: Can we export this from the kernel???
517  // jgraphics_line_draw_fast(g, rect.width * level * 0.96, 0, rect.width * level * 0.96, rect.height, 1.0);
518  jgraphics_move_to(g, peakPosition, 0.0);
519  jgraphics_line_to(g, peakPosition, rect.height);
520  jgraphics_set_line_width(g, 1.0);
521  jgraphics_stroke(g);
522  }
523 }
524 
525 void meter_dopaint_vertical(t_meter *x, t_object *view) {
526 
527  t_rect rect;
528  t_jgraphics* g;
529  double level = TTClip(x->envelope, 0.0f, 1.0f);
530  double position;
531  double peakPosition;
532  t_jrgba c;
533 
534  if (level > 0.0)
535  level = pow(level, kTTGainMidiPowerInv); // this is taken from the midi conversion in the Gain Dataspace
536 
537  g = (t_jgraphics*) patcherview_get_jgraphics(view); // obtain graphics context
538  jbox_get_rect_for_view((t_object *)x, view, &rect); // this is the box rectangle -- but we draw relative to 0 0, and thus only care about width & height
539  rect.x = 0;
540  rect.y = 0;
541  position = rect.height * level * 0.96;
542  peakPosition = rect.height * x->peak * 0.96;
543 
544  if (level > x->peak)
545  x->peak = level;
546 
547  // TODO: Can we export this from the kernel???
548  // jgraphics_image_surface_draw_fast(g, x->gradientSurface);
549  jgraphics_image_surface_draw(g, x->gradientSurface, x->gradientRect, rect);
550 
551  jgraphics_set_source_jrgba(g, &x->attrBgColor);
552  jgraphics_rectangle_fill_fast(g, 0, 0, rect.width, rect.height-position);
553 
554  if (x->envelope > 1.0 || x->peak > 1.0) {
555  c.red = 1.0;
556  c.green = c.blue = 0.0;
557  c.alpha = 1.0;
558  jgraphics_set_source_jrgba(g, &c);
559  jgraphics_rectangle_fill_fast(g, 0, 0, rect.width, rect.height * .04);
560  }
561  else {
562  c.red = peakPosition / x->gradientRect.height;
563  c.green = 1.0;
564  c.blue = 0.0;
565  c.alpha = 1.0;
566  jgraphics_set_source_jrgba(g, &c);
567  // TODO: Can we export this from the kernel???
568  // jgraphics_line_draw_fast(g, rect.width * level * 0.96, 0, rect.width * level * 0.96, rect.height, 1.0);
569  jgraphics_move_to(g, 0.0, rect.height - peakPosition);
570  jgraphics_line_to(g, rect.width, rect.height - peakPosition);
571  jgraphics_set_line_width(g, 1.0);
572  jgraphics_stroke(g);
573  }
574 }
575 
int C74_EXPORT main(void)
Set up this class as a Max external the first time an object of this kind is instantiated.
Definition: j.meter~.cpp:105
double TTFloat64
64 bit floating point number
Definition: TTBase.h:188
TTFOUNDATION_EXPORT const TTFloat64 kTTGainMidiPowerInv
Invverse power constant used when calculating MID gain.
Definition: TTBase.cpp:35
float TTFloat32
32 bit floating point number
Definition: TTBase.h:187