Code Search for Developers
 
 
  

jcom.meter~.cpp from Jamoma at Krugle


Show jcom.meter~.cpp syntax highlighted

/* 
 * jcom.meter~
 * External for Jamoma: signal level meter
 * By Tim Place, Copyright © 2005
 * Modifications for Compiling on Windows by Thomas Grill, 2005
 * 
 * License: This code is licensed under the terms of the GNU LGPL
 * http://www.gnu.org/licenses/lgpl.html 
 */

// define 
#ifdef WIN_VERSION
#define USE_QTML
#endif

#include "ext.h"				// Max Header
#include "ext_user.h"
#include "ext_common.h"			// includes the MIN macro
#include "z_dsp.h"				// MSP Header
#include "ext_strings.h"		// String Functions
#include "commonsyms.h"			// Common symbols used by the Max 4.5 API
#include "ext_obex.h"			// Max Object Extensions (attributes) Header
#include <math.h>

#ifdef USE_QTML
#include "ext_qtstubs.h"
#define patcher_setport(x) (XQT_patcher_setport(x))
#define box_nodraw(x) (XQT_box_nodraw(x))
#define box_enddraw(x) (XQT_box_enddraw(x))
#define SetPort(gp) (XQT_patcher_restoreport(gp))
#define patcher_restoreport(gp) (XQT_patcher_restoreport(gp))
#endif

// Macros and Constants
#define RES_ID			10120		// ID of our SICN resources
#define MAXWIDTH 		1024L		// maximum width and height of user object in pixels
#define MAXHEIGHT		512L		//		(defines maximum offscreen canvas allocation)
#define MINWIDTH 		20L			// minimum width and height
#define MINHEIGHT		2L			//		...
#define DEFWIDTH 		100			// default width and height
#define DEFHEIGHT		10			//		...
#define POLL_INTERVAL	150			// metro time

// Data Structure for our object
typedef struct {
	t_pxbox		my_box;				// required box structure for all audio ui externs
	void		*obex;				// max 4.5 object extensions
	void		*qelem;				// queue element to defer drawing commands
	void		*clock;				// clock for polling the audio analysis results
	
	Rect 		rect;				// for comparing with x_box.b_rect in the update() method
	
	float		envelope;			// the result of the amplitude analysis [0.0, 1.0]
	float		peak;				// the loudest sample since the last reset
	short		peak_position;
	short		peak_level;
	
	long		attr_defeat;		// turn off the meters
} t_meter;

// Prototypes for our methods:
void *meter_new(t_symbol *s, short argc, t_atom *argv);					// Object life...
void *meter_menu(void *p, long x, long y, long font);
void meter_free(t_meter *x);
void meter_assist(t_meter *x, void *b, long msg, long arg, char *dst);	// Max Methods...
void meter_bang(t_meter *x);
void meter_float(t_meter *x, double value);
void meter_int(t_meter *x, long value);
void meter_tick(t_meter *x);
t_int *meter_perform(t_int *w);
void meter_dsp(t_meter *x, t_signal **sp, short *count);
void meter_update(t_meter *x);											// UI Methods...
void meter_click(t_meter *x, Point pt, short modifiers);
void meter_psave(t_meter *x, void *w);
void meter_qfn(t_meter *x);												// Draw Routines...
void meter_draw(t_meter *x);
t_max_err attr_set_defeat(t_meter *x, void *attr, long argc, t_atom *argv);

// Globals
t_class *meter_class;

/************************************************************************************/
// Main() Function

int main(void)
{	
	long attrflags = 0;
	t_class *c;
	t_object *attr;
	
	common_symbols_init();

	c = class_new("jcom.meter~",(method)meter_new, (method)meter_free, (short)sizeof(t_meter), (method)meter_menu, A_GIMME, 0);
	class_obexoffset_set(c, calcoffset(t_meter, obex));

	class_addmethod(c, (method)meter_click,		"click", A_CANT, 0L);
 	class_addmethod(c, (method)meter_update, 	"update", A_CANT, 0L);
 	class_addmethod(c, (method)meter_psave,		"psave", A_CANT, 0L);	
	class_addmethod(c, (method)meter_assist, 	"assist", A_CANT, 0L); 
	class_addmethod(c, (method)meter_bang, 		"bang", 0L);
	class_addmethod(c, (method)meter_float,		"float", A_FLOAT, 0L);
	class_addmethod(c, (method)meter_int,		"int", A_LONG, 0L);
 	class_addmethod(c, (method)meter_dsp, 		"dsp", A_CANT, 0L);		

	attr = attr_offset_new("defeat", _sym_long, attrflags,
		(method)0L,(method)attr_set_defeat, calcoffset(t_meter, attr_defeat));
	class_addattr(c, attr);

	class_dspinitbox(c);									// Setup object's class to work with MSP

	// Finalize our class
	class_register(CLASS_BOX, c);
	meter_class = c;
	return 0;
}


/************************************************************************************/
// Object Life

// when the UI external is read-in from a patcher file...
void *meter_new(t_symbol *s, short argc, t_atom *argv)
{
	t_meter *x;
	void *patcher;
	long x_coord, y_coord, width, height;
	//short bw, bh, flags;
	short flags;
	
	x = (t_meter*)object_alloc(meter_class);
	if(x){
		dsp_setupbox((t_pxbox *)x, 1);
	
		patcher = argv[0].a_w.w_obj;					// patcher
		x_coord = argv[1].a_w.w_long;					// x coord
		y_coord = argv[2].a_w.w_long;					// y coord
		width = argv[3].a_w.w_long;						// width
		height = argv[4].a_w.w_long;					// height
			
		width = CLIP(width, MINWIDTH, MAXWIDTH); 		// constrain to min and max size
		height = CLIP(height, MINHEIGHT, MAXHEIGHT);	// constrain to min and max size
		
		// set flags with which our t_box struct will be initialized:
		//	F_DRAWFIRSTIN - Draw the first inlet
		//	F_NODRAWBOX - don't draw the box
		//	F_GROWBOTH - can grow independently in both width and height
		//	F_DRAWINLAST - draws inlets after box draws
		//	F_SAVVY - tells Max any queue function you make calls box_enddraw()
		flags = F_DRAWFIRSTIN | F_NODRAWBOX | F_GROWBOTH | F_DRAWINLAST | F_SAVVY;
	
		// now actually initialize the t_box structure
		box_new((t_box *)x, (t_patcher *)patcher, flags, x_coord, y_coord, x_coord + width, y_coord + height);

		// Reassign the box's firstin field to point to our new object
		x->my_box.z_box.b_firstin = (void *)x;
		
		// Cache rect for comparisons when the user decides to re-size the object
		x->rect = x->my_box.z_box.b_rect;

		// Create our queue element for defering calls to the draw function
		x->qelem = qelem_new(x, (method)meter_qfn);

		// Create clock routine for polling the audio result
		x->clock = clock_new(x, (method)meter_tick);

		// Set defaults
		x->envelope = 0;
		x->peak = 0;
		
		// Finish it up...
		box_ready((t_box *)x);
	}
	else
		error("jcom.meter~ - could not create instance");
	return(x);
}


// if the user selects our object from the toolbar/menu, we supply the _new method with default values
void *meter_menu(void *p, long x, long y, long font)
{
	t_atom argv[5];		// reduced to 5 on 24 nov 2004
	
	SETOBJ(argv, (t_object *)p);				// patcher
	SETLONG(argv+1, x);							// x coord
	SETLONG(argv+2, y);							// y coord
	SETLONG(argv+3, DEFWIDTH);					// width
	SETLONG(argv+4, DEFHEIGHT);					// height
	
	return meter_new(gensym("jcom.meter~"), 5, argv);
}


// delete
void meter_free(t_meter *x)
{
	dsp_freebox((t_pxbox *)x);			// a version of dsp_free() for ui objects
	qelem_free(x->qelem);				// delete our qelem
	freeobject((t_object *)x->clock);	// delete our clock
	box_free((t_box *)x);				// free the ui box
}


/************************************************************************************/
// Methods bound to input/inlets

// Method for Assistance Messages
void meter_assist(t_meter *x, void *b, long msg, long arg, char *dst)
{
	if(msg==1) 						// Inlet
		strcpy(dst, "Input");
	else if(msg==2){ 				// Outlets
		switch(arg){
			case 0: strcpy(dst, "Output"); break;
			//case 1: strcpy(dst, "Attribute Stuff"); break;
 		}
 	}		
}


// Method: bang - clear the peak hold and redraw
void meter_bang(t_meter *x)
{
	x->peak = 0;	// reset the peak hold
	qelem_set(x->qelem);
}


// Method: float - can be used instead of a signal
void meter_float(t_meter *x, double value)
{
	x->envelope = value;
	qelem_set(x->qelem);
}

// cast to float...
void meter_int(t_meter *x, long value)
{
	meter_float(x, (double)value);
}

// Attribute: defeat
t_max_err attr_set_defeat(t_meter *x, void *attr, long argc, t_atom *argv)
{
	x->attr_defeat = atom_getlong(argv);
	if(x->attr_defeat == 0) 
		clock_delay(x->clock, POLL_INTERVAL); 	// start the clock

	return MAX_ERR_NONE;
	#pragma unused(attr)
}


// Method: triggered by our clock
void meter_tick(t_meter *x)
{
	qelem_set(x->qelem); 							// draw the meters

	if(sys_getdspstate()) {							// if dsp is on then we schedule another tick
		if(x->attr_defeat == 0)
			clock_delay(x->clock, POLL_INTERVAL); 	// schedule the clock
	}
}


// Method: perform signal processing
t_int *meter_perform(t_int *w)
{
	t_meter		*x = (t_meter *)(w[1]);
	t_float		*input = (float *)(w[2]);
	long 		n = (long)(w[3]);
	float 		currentvalue;

	while(n--){
		currentvalue = ((*input) < 0)?-(*input):*input; // get the current sample's absolute value
		if(currentvalue > x->envelope) 					// if it's a new peak amplitude...
			x->envelope = currentvalue;
		input++; 										// increment pointer in the vector
	}
	return (w+4);
}		


// Method: compile dsp chain
void meter_dsp(t_meter *x, t_signal **sp, short *count)
{		
	if(count[0]){
		dsp_add(meter_perform, 3, x, sp[0]->s_vec, sp[0]->s_n);
		clock_delay(x->clock, POLL_INTERVAL); 			// start the clock
		x->peak = 0;
	}
}


/************************************************************************************/
// Required UI Object Methods

// Sent when our object needs to be redrawn
//	(this is already at low priority and does not need to be deferred)
void meter_update(t_meter *x)
{
	short width_old = x->rect.right - x->rect.left;
	short height_old = x->rect.bottom - x->rect.top;
	short width_new = x->my_box.z_box.b_rect.right - x->my_box.z_box.b_rect.left;
	short height_new = x->my_box.z_box.b_rect.bottom - x->my_box.z_box.b_rect.top;

	if(height_new != height_old || width_new != width_old) {
		width_new = CLIP(width_new, MINWIDTH, MAXWIDTH); // constrain to min and max size
		height_new = CLIP(height_new, MINHEIGHT, MAXHEIGHT);
		box_size(&x->my_box, width_new, height_new);	// this function actually resizes out t_box structure
		x->rect = x->my_box.z_box.b_rect;
		
		//xgui_allocoffscreen(x); // (existing offsceen is disposed inside)
	}

	GrafPtr	gp = patcher_setport(x->my_box.z_box.b_patcher);
    meter_draw(x);
    patcher_restoreport(gp); 
}


// If the user clicks on the object
void meter_click(t_meter *x, Point pt, short modifiers)
{ 
	meter_bang(x);		// reset the peak hold
}


// Save our UI object's location and appearance with the patcher...
void meter_psave(t_meter *x, void *w)
{
	Rect r = x->my_box.z_box.b_rect;
	t_atom argv[16];
	short inc = 0;
	
	SETSYM(argv,gensym("#P"));
	if (x->my_box.z_box.b_hidden) {	// i.e. if it's hidden when the patcher is locked
		SETSYM(argv+1,gensym("hidden"));
		inc = 1;
	}
	SETSYM(argv+1+inc, gensym("user"));
	SETSYM(argv+2+inc, ob_sym(x));
	SETLONG(argv+3+inc, r.left);			// x
	SETLONG(argv+4+inc, r.top);				// y
	SETLONG(argv+5+inc, r.right - r.left);	// width
	SETLONG(argv+6+inc, r.bottom - r.top);	// height
	
	binbuf_insert(w, 0L, 7+inc, argv);
}


/************************************************************************************/
// Drawing Routines

// The deferred routine called by our Qelem
void meter_qfn(t_meter *x)
{
	GrafPtr gp = patcher_setport(x->my_box.z_box.b_patcher);

	if(gp){				// if the pointer is valid...
		if(!box_nodraw((t_box *)x)){
			meter_draw(x);
			box_enddraw((t_box *) x);
		}
	}
	SetPort(gp);
}


// The actual drawing routine
void meter_draw(t_meter *x)   
{ 
	GrafPtr		curPort;
	GDHandle	curDevice; 
	RGBColor	frgb, old_color; 
	Rect		rect_ui = x->my_box.z_box.b_rect;
	Rect		rect_temp;
	short		i; 
	short		width_ui = x->my_box.z_box.b_rect.right - x->my_box.z_box.b_rect.left;
	short		height_ui = x->my_box.z_box.b_rect.bottom - x->my_box.z_box.b_rect.top;
	short		width_green = 0.96 * width_ui;		// 96% of total width
	short		width_red = width_ui - width_green;	// 4% of total width
	short		left = x->my_box.z_box.b_rect.left;
	float		level;
	short		position;

	GetGWorld((CGrafPtr *)&curPort, &curDevice);
	GetForeColor(&old_color);
	PenMode(srcCopy);

	frgb.blue = 0;
	frgb.green = 65535;

	rect_temp.top = x->my_box.z_box.b_rect.top;
	rect_temp.bottom = x->my_box.z_box.b_rect.bottom;

	level = CLIP(x->envelope, 0.0, 1.0);		// get the amplitude
	
	if(level >= 1.0){
		x->peak = 1.0;
		// Draw Green
		for(i=0; i<width_green; i++){
			rect_temp.left = left + i;
			rect_temp.right = rect_temp.left + 1;

			frgb.red = (i * 65535) / width_green;	
			RGBForeColor(&frgb);
			PaintRect(&rect_temp);
		}

		rect_temp.left = left + i;
		rect_temp.right = left + width_ui;

		// Draw Red
		frgb.green = 0;
		RGBForeColor(&frgb);
		PaintRect(&rect_temp);

		goto out;
	}
	if(level >= 0.0) {
		level = CLIP(20. * (log10(level)), -96.0, 0.0);	// convert to decibels
		level = -(96 - (exp((level) * 0.0344014267) * 96));
		position = (width_green) + (level * 0.0104166667 * width_green);

		// Draw Green
		for(i=0; i<position; i++){
			frgb.red = (i * 65535) / width_green;	
			RGBForeColor(&frgb);
			MoveTo(left+i, rect_temp.top);
			Line(0, height_ui);
		}
		rect_temp.left = left + i;
		rect_temp.right = left + width_ui;

		// Draw Gray
		frgb.red = 8000;
		frgb.green = 8000;
		frgb.blue = 8000;
		RGBForeColor(&frgb);
		PaintRect(&rect_temp);
	}
	else{
		// Draw Gray
		frgb.red = 8000;
		frgb.green = 8000;
		frgb.blue = 8000;
		RGBForeColor(&frgb);
		PaintRect(&rect_ui);
	}
	
	// Peak Hold
	if(x->envelope > x->peak){
		x->peak = x->envelope;
		x->peak_position = position;
		x->peak_level = level;
	}

	if((x->peak > 0) && (x->peak <1)){	// Green Range
		rect_temp.left = x->peak_position + left;
		rect_temp.right = rect_temp.left + 1;
		frgb.green = 65535;
		frgb.red = x->peak_level * 5653.5;
		RGBForeColor(&frgb);
		PaintRect(&rect_temp);
	}
	else if(x->peak > 0){				// Red Range
		rect_temp.left = left + width_green;
		rect_temp.right = x->my_box.z_box.b_rect.right;
		frgb.red = 65535;
		frgb.green = 0;
		frgb.blue = 0;
		RGBForeColor(&frgb);
		PaintRect(&rect_temp);
	}
	
out:
	x->envelope = 0;								// reset the amplitude tracker

	PenNormal();
	RGBForeColor(&old_color);	
}







See more files for this project here

Jamoma

Jamoma is a flexible framework for the creation of modules in Max, MSP, and Jitter

Project homepage: http://sourceforge.net/projects/jamoma
Programming language(s): C++,JavaScript,XML
License: other

  jcom.meter~.xcodeproj/
    project.pbxproj
  COMPILING.txt
  MacHeadersACAF.h
  jcom.meter~.cpp
  jcom.meter~.def
  jcom.meter~.rsrc.zip
  jcom.meter~.sln
  jcom.meter~.vcproj
  jcom_meter.qtr
  jcom_meter.qtr.txt
  jcom_meter.r