Hi all,

I've taken a quick stab at trying to port John Gibson's spectdelay~
external from Max to Pd and got stuck at the following error:

load_object: Symbol "spectdelay_tilde_setup" not found

I have spectdelay_tilde_setup in the code, it is format-wise (TTBOMYK)
the same as other signal-based externals. At any rate, I am stuck and
unsure how to proceed. Please advise.

build script:
gcc -DPD -g -I/usr/local/include/pdl2ork -I../Spectacle -o spectdelay~.o
-c spectdelay~.cpp
gcc -o spectdelay~.pd_linux spectdelay~.o -Wall -W -Wstrict-prototypes
-Wno-unused -Wno-parentheses -Wno-switch -O6 -funroll-loops
-fomit-frame-pointer -fno-strict-aliasing -fPIC -DUNIX
-Wl,--export-dynamic-shared ../Spectacle/Spectacle.o 
../Spectacle/SpectacleBase.o  ../genlib/Odelay.o ../genlib/Obucket.o 
../genlib/FFTReal.o ../genlib/Offt.o

source file is attached. Bunch of .o files are independent/shared .o
files that have been built as follows:
gcc -c -o Ooscil.o Ooscil.cpp

Any ideas as to why is this happening?

TIA


/*
 *  Copyright (C) 2008 John Gibson
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License, version 2, as
 *  published by the Free Software Foundation.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 */

// Documentation, in ref manual format
//
// Arguments
//
//    int      Optional. FFT length (power of 2, usually 1024)
//             [if missing, fftlen=1024, windowlen=2048, overlap=2, maxdel=6]
//
//    int      Optional. Window length (power of 2, usually FFT length * 2)
//             [if missing, windowlen=2048, overlap=2, maxdel=6]
//
//    int      Optional. FFT window overlap (positive power of 2)
//             1: no overlap, 2: hopsize=FFTlen/2, 4: hopsize=FFTlen/4, etc.
//             2 or 4 is usually fine; 1 is fluttery; 4 gives fewer artifacts;
//             higher overlaps use more CPU; 
//             [if missing, overlap=2, maxdel=6]
//
//    float    Optional. Maximum delay time (seconds)
//             [if missing, maxdel=6]
//
// Input
//
//    signal   Audio signal in left inlet to be processed by jg.spectdelay~.
//
//    binmap   In left inlet: The word binmap, followed by a list of ints,
//             which gives the number of adjacent FFT bins to affect for each
//             of the elements of the EQ table. The binmap table and the EQ
//             table must be the same size. Giving the word binmap alone
//             cancels the use of a binmap table. For example, the message
//             "binmap 1 2 10 20 50" assumes a previous eq message of five 
//             floats and treats the first of those floats as the gain of the 
//             lowest FFT bin, the second as the gain of the next two FFT bins,
//             the third as the gain of the next ten FFT bins, the fourth as
//             the gain of the next 20 FFT bins, and the fifth as the gain of
//             the next 50 FFT bins (and any leftover bins above these).
//
//     clear   In left inlet: The word clear. This fills all the spectral delay
//             lines with zero, resulting in silence.
//
//    dbinmap  In left inlet: The word dbinmap, followed by a list of ints,
//             which gives the number of adjacent FFT bins to affect for each
//             of the elements of the delay time and feedback tables. The delay
//             binmap table and these tables must be the same size. Giving the
//             word dbinmap alone cancels the use of a delay binmap table. The
//             mapping scheme functions in the way described for the binmap
//             message.
//
//    drange   In left inlet: The word drange, followed by two floats (min
//             and max), which define the frequency range within which the delay
//             time (del) and feedback (feed) tables function. Giving 0 for max
//             sets the maximum frequency to Nyquist. By default, the range is
//             0 Hz to Nyquist. Note that this range is ignored when using the
//             binmap table.
//
//        dt   In left inlet: The word dt, followed by a list of floats, which
//             define the delay times (in seconds) for individual frequency
//             bands. Effect depends on delay frequency range or dbinmap table.
//             If the list contains just one number, then that is applied to
//             all bands.
//
//        dx   In left inlet: The word dx, followed by an index (int) and 
//             delay time (float) to replace the value at that index in the
//             currently defined delay time table. The table must already have
//             at least index + 1 values.
//
//        eq   In left inlet: The word eq, followed by a list of floats,
//             which define the amplitude scaling for individual frequency
//             bands, in decibels. (0 dB means no change, + dB boost, - dB cut.)
//             Effect depends on EQ frequency range or binmap table. If the list
//             contains just one number, then that is applied to all bands.
//
//        ex   In left inlet: The word ex, followed by an index (int) and EQ
//             amplitude in dB (float) to replace the value at that index in the
//             currently defined EQ table. The table must already have
//             at least index + 1 values.
//
//        fb   In left inlet: The word fb, followed by a list of floats, which
//             define the delay feedback multiplier (-1 to 1, inclusive) for
//             individual frequency bands. Effect depends on delay frequency
//             range or dbinmap table. If the list contains just one number,
//             then that is applied to all bands. If the feedback list has more
//             than one item, the size of the list must match the size of the
//             delay time (dt) list. If changing both list sizes, change the
//             delay time list first.
//
//        fx   In left inlet: The word fx, followed by an index (int) and 
//             delay feedback (float) to replace the value at that index in the
//             currently defined delay feedback table. The table must already
//             have at least index + 1 values.
//
//       fft   In left inlet: The word fft, followed by three ints, gives
//             the FFT length, window length, and overlap (respectively).
//             (See explanation above in Arguments secion.)
//             Note: sending this message will halt processing while object
//             reconfigures itself and will cause an audio glitch.
//
//      hold   In left inlet: The word hold, followed by a 0 or a 1.  If 0,
//             play normally.  If 1, stop accepting input and allow the sound
//             already in delay lines to recirculate.  (If instead you let the
//             input decay to zeros, without holding, you may hear the delay
//             lines resonate pitches corresponding to the fundamental frequency
//             of analysis and its harmonic partials.)
//
//    maxdel   In left inlet: The word maxdel, followed by a float giving the
//             maximum number of seconds for the delay lines.
//
//    posteq   In left inlet: The word posteq, followed by a 0 or a 1.  If 0,
//             EQ is applied before delay.  If 1, EQ is applied after delay,
//             which means it affects sound that's feeding back through the
//             delay lines.
//
//     range   In left inlet: The word range, followed by two floats (min
//             and max), which define the frequency range within which the EQ
//             table functions. Giving 0 for max sets the maximum frequency to
//             Nyquist. By default, the range is 0 Hz to Nyquist. Note that this
//             range is ignored when using the binmap table.
//
// NOTE: It takes a certain, fixed amount of time for the sound to enter
// jg.spectdelay~ and emerge processed. If you want to delay your dry signal so
// that it synchronizes with the processed signal, delay it by the window length
// (i.e., the second argument to jg.spectdelay~).

// Changes
//
// v0.9		- fixed crashes seen on Snow Leopard, due to uninitialized pointers. 


//extern "C" {
	//#include "ext.h"gedi
	//#include "z_dsp.h"
	//#include "ext_strings.h"
	//#include "/home/ico/Downloads/PureData/pure-data/pd/src/m_pd.h"
//}

//#include <math.h>
//#include <stdio.h>
//#include <stdlib.h>
//#include <string.h>

#include "Spectacle.h"
#include "/home/ico/Downloads/PureData/pure-data/pd/src/m_pd.h"

// conform out-of-range values passed to dt, dx, fb, and fx
#define CONFORM_INPUT

const int kDefaultFFTLen = 1024;
const int kDefaultWindowLen = 2048;
const int kDefaultOverlap = 2;
const float kDefaultMaxDelTime = 6.0f;
const float kMinMaxDelTime = 0.1f;
const float kMaxMaxDelTime = 20.0f;
const int kMaxTableLen = 512;

//void *spectdelay_class;

static t_class *spectdelay_class;

typedef struct _spectdelay
{
	// header
	t_object x_obj;
    
	// variables specific to this object
	int fftlen, windowlen, overlap;
	float srate, maxdeltime;
	Spectacle *spectdelay, *newspectdelay;
	float eqtable[kMaxTableLen];
	float dttable[kMaxTableLen];
	float fbtable[kMaxTableLen];
	int eqbinmap[kMaxTableLen];
	int delaybinmap[kMaxTableLen];
	int eqtablen, dttablen, fbtablen, eqbinmaplen, delaybinmaplen;
	float minfreq, maxfreq, dminfreq, dmaxfreq;
	bool in_connected, hold, posteq;
} t_spectdelay;

// prototypes
void *spectdelay_new(long fftlen, long windowlen, long overlap, 
	float maxdeltime);
void spectdelay_free(t_spectdelay *x);
void spectdelay_dsp(t_spectdelay *x, t_signal **sp, short *count); 
t_int *spectdelay_perform(t_int *w);
void spectdelay_assist(t_spectdelay *x, void *b, long m, long a, char *s);
// our message handlers
void spectdelay_binmap_msg(t_spectdelay *x, t_symbol *s, short argc, t_atom *argv);
void spectdelay_clear_msg(t_spectdelay *x, t_symbol *s, short argc, t_atom *argv);
void spectdelay_dbinmap_msg(t_spectdelay *x, t_symbol *s, short argc, t_atom *argv);
void spectdelay_drange_msg(t_spectdelay *x, t_symbol *s, short argc, t_atom *argv);
void spectdelay_dt_msg(t_spectdelay *x, t_symbol *s, short argc, t_atom *argv);
void spectdelay_dx_msg(t_spectdelay *x, t_symbol *s, short argc, t_atom *argv);
void spectdelay_eq_msg(t_spectdelay *x, t_symbol *s, short argc, t_atom *argv);
void spectdelay_ex_msg(t_spectdelay *x, t_symbol *s, short argc, t_atom *argv);
void spectdelay_fb_msg(t_spectdelay *x, t_symbol *s, short argc, t_atom *argv);
void spectdelay_fx_msg(t_spectdelay *x, t_symbol *s, short argc, t_atom *argv);
void spectdelay_fft_msg(t_spectdelay *x, t_symbol *s, short argc, t_atom *argv);
void spectdelay_hold_msg(t_spectdelay *x, t_symbol *s, short argc, t_atom *argv);
void spectdelay_posteq_msg(t_spectdelay *x, t_symbol *s, short argc, t_atom *argv);
void spectdelay_maxdel_msg(t_spectdelay *x, t_symbol *s, short argc, t_atom *argv);
void spectdelay_range_msg(t_spectdelay *x, t_symbol *s, short argc, t_atom *argv);


// utilities

inline float _fclamp(float min, float val, float max)
{
	return (val < min) ? min : ((val > max) ? max : val);
}

inline long _atom_getlong(int index, short argc, t_atom *argv)
{
	/*if (argv[index].a_type == A_LONG)
		return argv[index].a_w.w_long;
	else*/ if (argv[index].a_type == A_FLOAT)
		return (long) argv[index].a_w.w_float;
	else return 0L;
}

inline float _atom_getfloat(int index, short argc, t_atom *argv)
{
	/*if (argv[index].a_type == A_LONG)
		return (float) argv[index].a_w.w_long;
	else */ if (argv[index].a_type == A_FLOAT)
		return argv[index].a_w.w_float;
	else return 0.0;
}


// Called the first time object is inserted or read from a file
// during a Max/MSP run.  It is *not* called on subsequent object
// creation during a run.

//extern "C" int main(void)
void spectdelay_tilde_setup(void)
{
	// The three optional A_DEFLONG arguments give the FFT length, window length
	// and window overlap.
	//setup((struct messlist **) &spectdelay_class, (method) spectdelay_new,
	//			(method) spectdelay_free, (short) sizeof(t_spectdelay), 0L,
	//			A_DEFLONG, A_DEFLONG, A_DEFLONG, A_DEFFLOAT, 0);
	spectdelay_class = class_new(gensym("spectdelay~"), (t_newmethod)
				spectdelay_new, (t_method) spectdelay_free, (short) sizeof
				(t_spectdelay), 0, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0);
	//CLASS_MAINSIGNALIN(spectdelay_class, t_spectdelay, x_f);

	// standard messages; don't change these
	class_addmethod(spectdelay_class, (t_method)spectdelay_dsp, gensym("dsp"), A_CANT, 0);
	//class_addmethod(spectdelay_class, spectdelay_assist, gensym("assist"), 0);

	// our own messages
	class_addmethod(spectdelay_class, (t_method)spectdelay_binmap_msg, gensym("binmap"), A_GIMME, 0);
	class_addmethod(spectdelay_class, (t_method)spectdelay_clear_msg, gensym("clear"), A_GIMME, 0);
	class_addmethod(spectdelay_class, (t_method)spectdelay_dbinmap_msg, gensym("dbinmap"), A_GIMME, 0);
	class_addmethod(spectdelay_class, (t_method)spectdelay_drange_msg, gensym("drange"), A_GIMME, 0);
	class_addmethod(spectdelay_class, (t_method)spectdelay_dt_msg, gensym("dt"), A_GIMME, 0);
	class_addmethod(spectdelay_class, (t_method)spectdelay_dx_msg, gensym("dx"), A_GIMME, 0);
	class_addmethod(spectdelay_class, (t_method)spectdelay_eq_msg, gensym("eq"), A_GIMME, 0);
	class_addmethod(spectdelay_class, (t_method)spectdelay_ex_msg, gensym("ex"), A_GIMME, 0);
	class_addmethod(spectdelay_class, (t_method)spectdelay_fb_msg, gensym("fb"), A_GIMME, 0);
	class_addmethod(spectdelay_class, (t_method)spectdelay_fx_msg, gensym("fx"), A_GIMME, 0);
	class_addmethod(spectdelay_class, (t_method)spectdelay_fft_msg, gensym("fft"), A_GIMME, 0);
	class_addmethod(spectdelay_class, (t_method)spectdelay_hold_msg, gensym("hold"), A_GIMME, 0);
	class_addmethod(spectdelay_class, (t_method)spectdelay_posteq_msg, gensym("posteq"), A_GIMME, 0);
	class_addmethod(spectdelay_class, (t_method)spectdelay_maxdel_msg, gensym("maxdel"), A_GIMME, 0);
	class_addmethod(spectdelay_class, (t_method)spectdelay_range_msg, gensym("range"), A_GIMME, 0);

	//dsp_initclass();

	//post("jg.spectdelay~ v0.9 - Copyright (C) 2008-2010 by John Gibson\nPd port by Ivica Ico Bukvic 2010");
}


// Called when the object is created.  Note that every time the user edits
// arguments, Max calls _free and then _new to recreate the object.

void *spectdelay_new(long fftlen, long windowlen, long overlap,
	float maxdeltime)
{
	// create our object
	t_spectdelay *x = (t_spectdelay *)pd_new(spectdelay_class);

	// setup up audio signal inputs and outputs
	//dsp_setup((t_object *) x, 1);
	inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
	//outlet_new((t_object *) x, "signal");
	outlet_new(&x->x_obj, gensym("signal"));

	// Normally, an out vector points to the same memory as an in
	// vector, and code must be designed to write an output sample
	// only after reading the corresponding input sample.  If this
	// is not possible, uncomment the following line, which makes the
	// in and out buffers independent.
	// x->x_obj.z_misc = Z_NO_INPLACE;

	// init our struct fields
	x->fftlen = fftlen ? fftlen : kDefaultFFTLen;
	x->windowlen = windowlen ? windowlen : kDefaultWindowLen;
	x->overlap = overlap ? overlap : kDefaultOverlap;
	x->maxdeltime = maxdeltime ? maxdeltime : kDefaultMaxDelTime;
	x->maxdeltime = _fclamp(kMinMaxDelTime, x->maxdeltime, kMaxMaxDelTime);
	x->srate = sys_getsr();		// possibly overridden in spectdelay_dsp
	if (x->srate == 0.0)
		x->srate = 44100.0;
	x->spectdelay = NULL;
	float *del = x->dttable;
	float *eq = x->eqtable;
	float *feed = x->fbtable;
	int *bm = x->eqbinmap;
	int *dbm = x->delaybinmap;
	for (int i = 0; i < kMaxTableLen; i++) {
		*del++ = 0.0;
		*eq++ = 0.0;
		*feed++ = 0.0;
		*bm++ = 0;
		*dbm++ = 0;
	}
	x->dttablen = x->eqtablen = x->fbtablen = 0;
	x->eqbinmaplen = x->delaybinmaplen = 0;
	x->dminfreq = x->dmaxfreq = x->minfreq = x->maxfreq = 0.0;
	x->in_connected = false;
	x->hold = false;
	x->posteq = false;
	x->newspectdelay = NULL;

	x->spectdelay = new Spectacle();
	x->spectdelay->init(x->fftlen, x->windowlen, x->overlap, x->srate,
	                                                        x->maxdeltime);

	return x;
}


// Called when an object is destroyed

void spectdelay_free(t_spectdelay *x)
{
	// Must call this *before* you free other resources!
	//dsp_free((t_object *) x);
	
	delete x->spectdelay;
	delete x->newspectdelay;	// in case this happens to be alive
}


// Called every time audio is started.  Even when audio is running, if the
// user changes anything (like deletes a patch cord), audio will be turned off
// and then on again, calling this func.
// This adds the "perform" method to the DSP chain, and also tells us
// where the audio vectors are and how big they are.

#define NUM_DSP_ADD_ARGS 4

void spectdelay_dsp(t_spectdelay *x, t_signal **sp, short *count)
{
	int i;

	// set sample rate vars, in case srate is different from global rate
	const float newsrate = sp[0]->s_sr;
	if (newsrate != x->srate) {
		x->srate = newsrate;
		x->spectdelay->set_srate(x->srate);
	}

	// check to see if there are signals connected to the input
	x->in_connected = (bool) count[0];

	t_int** dsp_add_args = (t_int**)getbytes(sizeof(t_int)*(NUM_DSP_ADD_ARGS));
	dsp_add_args[0] = (t_int*)x;			// pointer to self
	dsp_add_args[1] = (t_int*)sp[0]->s_n;	// vector size

	for (i=0;i < NUM_DSP_ADD_ARGS-2;i++)
	    dsp_add_args[2 + i] = (t_int*)sp[i]->s_vec;

	//dsp_add_args[2] = (t_int*)sp[0]->s_vec;	// input vector
	//dsp_add_args[3] = (t_int*)sp[1]->s_vec;	// output vector

	// add vectors to signal chain
	dsp_addv(spectdelay_perform, NUM_DSP_ADD_ARGS, (t_int*)dsp_add_args);
	freebytes(dsp_add_args,sizeof(t_int)*NUM_DSP_ADD_ARGS);
}


// Called to process or generate a block of samples

t_int *spectdelay_perform(t_int *w)
{
	t_spectdelay *x = (t_spectdelay *) w[1];

	// Don't do anything else if patcher is "muted"
	if (/*x->x_obj.z_disabled ||*/ x->in_connected)
	{

		// in and out vectors
		float *in = (float *) w[2];
		float *out = (float *) w[3];

		// number of samples per vector
		const int vsize = (int) w[4];

		// If spectdelay_fft_msg has created a replacement Spectacle, use it.
		if (x->newspectdelay) {
			Spectacle *tmp = x->spectdelay;
			x->spectdelay = x->newspectdelay;
			x->newspectdelay = NULL;
			delete tmp;
		}

		// process a block of samples
		x->spectdelay->run(in, out, vsize);

	}

	// return a pointer to the next object in the signal chain.
	return w + NUM_DSP_ADD_ARGS + 1;
}	


// Called when user mouses over inlets/outlets, which shows help blurbs.

void spectdelay_assist(t_spectdelay *x, void *b, long m, long a, char *s)
{
	/*if (m == ASSIST_INLET) {
		if (a == 0) sprintf(s, "input (signal), messages");
	}
	else {
		if (a == 0) sprintf(s, "output (signal)");
	}*/
}


// === our message handlers ====================================================

void spectdelay_binmap_msg(t_spectdelay *x, t_symbol *s, short argc, t_atom *argv)
{
	if (argc == 0) {
		post("jg.spectdelay~: cancelling EQ binmap table");
		for (int i = 0; i < x->eqbinmaplen; i++)
			x->eqbinmap[i] = 0;
		x->eqbinmaplen = 0;
		x->spectdelay->set_binmap_table(NULL, 0);
		return;
	}
	else if (argc > kMaxTableLen) {
		post("jg.spectdelay~ warning: EQ binmap list can have no more than "
				"%d values", kMaxTableLen);
		argc = kMaxTableLen;
	}
	if (x->eqtablen != 1 && argc != x->eqtablen) {
		error("jg.spectdelay~: EQ binmap list must have same number of values "
				"as the eq list");
		return;
	}
	x->eqbinmaplen = argc;

	int *tab = (int *) &x->eqbinmap;
	for (int i = 0; i < argc; i++)
		tab[i] = _atom_getlong(i, argc, argv);

	x->spectdelay->set_binmap_table(x->eqbinmap, x->eqbinmaplen);

	const float nyquist = x->srate / 2;
	if (x->minfreq != 0 || (x->maxfreq != 0 && x->maxfreq != nyquist))
		post("jg.spectdelay~ warning: use of EQ binmap table ignores min "
           "and max EQ frequencies");
}


void spectdelay_clear_msg(t_spectdelay *x, t_symbol *s, short argc, t_atom *argv)
{
   x->spectdelay->clear();
}


void spectdelay_dbinmap_msg(t_spectdelay *x, t_symbol *s, short argc, t_atom *argv)
{
	if (argc == 0) {
		post("jg.spectdelay~: cancelling delay binmap table");
		for (int i = 0; i < x->delaybinmaplen; i++)
			x->delaybinmap[i] = 0;
		x->delaybinmaplen = 0;
		x->spectdelay->set_delay_binmap_table(NULL, 0);
		return;
	}
	else if (argc > kMaxTableLen) {
		post("jg.spectdelay~ warning: delay binmap list can have no more "
           "than %d values", kMaxTableLen);
		argc = kMaxTableLen;
	}
	if ((x->dttablen != 1 && argc != x->dttablen)
	    || (x->fbtablen != 1 && argc != x->fbtablen)) {
		error("jg.spectdelay~: delay binmap list must have same number of values "
            "as the delay time and feedback lists");
		return;
	}
	x->delaybinmaplen = argc;

	int *tab = (int *) &x->delaybinmap;
	for (int i = 0; i < argc; i++)
		tab[i] = _atom_getlong(i, argc, argv);

	x->spectdelay->set_delay_binmap_table(x->delaybinmap, x->delaybinmaplen);

	const float nyquist = x->srate / 2;
	if (x->dminfreq != 0 || (x->dmaxfreq != 0 && x->dmaxfreq != nyquist))
		post("jg.spectdelay~ warning: use of delay binmap table ignores min "
           "and max delay frequencies");
}


void spectdelay_drange_msg(t_spectdelay *x, t_symbol *s, short argc, t_atom *argv)
{
	if (argc != 2) {
		error("jg.spectdelay~: drange min max (delay time frequency range "
				"in Hz)");
		return;
	}
	const float nyquist = x->srate / 2;
	float max = _atom_getfloat(1, argc, argv);
	if (max == 0)
		max = nyquist;
	else
		max = _fclamp(0.0, max, nyquist);
	float min = _fclamp(0.0, _atom_getfloat(0, argc, argv), max);

	// changing range is expensive, so avoid when params are same
	if (min != x->dminfreq || max != x->dmaxfreq) {
		x->dminfreq = min;
		x->dmaxfreq = max;
		x->spectdelay->set_delay_freqrange(min, max);
	}
}


void spectdelay_dt_msg(t_spectdelay *x, t_symbol *s, short argc, t_atom *argv)
{
	if (argc < 1) {
		error("jg.spectdelay~: delay time list must have at least 1 value");
		return;
	}
	if (argc > kMaxTableLen) {
		post("jg.spectdelay~ warning: delay time list can have no more than %d "
				"values", kMaxTableLen);
		argc = kMaxTableLen;
	}
	x->dttablen = argc;

	float *tab = (float *) &x->dttable;
#ifdef CONFORM_INPUT
	const float maxdeltime = x->maxdeltime;
	for (int i = 0; i < argc; i++)
		tab[i] = _fclamp(0.0f, _atom_getfloat(i, argc, argv), maxdeltime);
#else
	for (int i = 0; i < argc; i++)
		tab[i] = _atom_getfloat(i, argc, argv);
#endif

	x->spectdelay->set_deltable(x->dttable, x->dttablen);
}


void spectdelay_dx_msg(t_spectdelay *x, t_symbol *s, short argc, t_atom *argv)
{
	if (argc != 2) {
		error("jg.spectdelay~: dx index value");
		return;
	}
	const int index = _atom_getlong(0, argc, argv);
	if (index >= x->dttablen) {
		error("jg.spectdelay~: dx index out of range");
		return;
	}
#ifdef CONFORM_INPUT
	float value = _fclamp(0.0f, _atom_getfloat(1, argc, argv), x->maxdeltime);
#else
	float value = _atom_getfloat(1, argc, argv);
#endif
	x->dttable[index] = value;	// update array read by Spectacle object
}


void spectdelay_eq_msg(t_spectdelay *x, t_symbol *s, short argc, t_atom *argv)
{
	if (argc < 1) {
		error("jg.spectdelay~: eq list must have at least 1 value");
		return;
	}
	if (argc > kMaxTableLen) {
		post("jg.spectdelay~ warning: eq list can have no more than %d values",
				kMaxTableLen);
		argc = kMaxTableLen;
	}
	x->eqtablen = argc;

	float *tab = (float *) &x->eqtable;
	for (int i = 0; i < argc; i++)
		tab[i] = _atom_getfloat(i, argc, argv);

	x->spectdelay->set_eqtable(x->eqtable, x->eqtablen);
}


void spectdelay_ex_msg(t_spectdelay *x, t_symbol *s, short argc, t_atom *argv)
{
	if (argc != 2) {
		error("jg.spectdelay~: ex index value");
		return;
	}
	const int index = _atom_getlong(0, argc, argv);
	if (index >= x->eqtablen) {
		error("jg.spectdelay~: ex index out of range");
		return;
	}
	float value = _atom_getfloat(1, argc, argv);
	x->eqtable[index] = value;	// update array read by Spectacle object
}


void spectdelay_fb_msg(t_spectdelay *x, t_symbol *s, short argc, t_atom *argv)
{
	if (argc < 1) {
		error("jg.spectdelay~: feedback list must have at least 1 value");
		return;
	}
	if (argc > kMaxTableLen) {
		post("jg.spectdelay~ warning: feedback list can have no more than "
				"%d values", kMaxTableLen);
		argc = kMaxTableLen;
	}
	x->fbtablen = argc;

	if (x->fbtablen != 1 && x->dttablen != 1 &&
	                                            x->fbtablen != x->dttablen) {
		error("jg.spectdelay~: feedback list must have same number of values as "
				"delay time list (%d)", x->dttablen);
		return;
	}

	float *tab = (float *) &x->fbtable;
#ifdef CONFORM_INPUT
	for (int i = 0; i < argc; i++)
		tab[i] = _fclamp(-1.0f, _atom_getfloat(i, argc, argv), 1.0f);
#else
	for (int i = 0; i < argc; i++)
		tab[i] = _atom_getfloat(i, argc, argv);
#endif

	x->spectdelay->set_feedtable(x->fbtable, x->fbtablen);
}


void spectdelay_fx_msg(t_spectdelay *x, t_symbol *s, short argc, t_atom *argv)
{
	if (argc != 2) {
		error("jg.spectdelay~: fx index value");
		return;
	}
	const int index = _atom_getlong(0, argc, argv);
	if (index >= x->fbtablen) {
		error("jg.spectdelay~: fx index out of range");
		return;
	}
#ifdef CONFORM_INPUT
	float value = _fclamp(-1.0f, _atom_getfloat(1, argc, argv), 1.0f);
#else
	float value = _atom_getfloat(1, argc, argv);
#endif
	x->fbtable[index] = value;	// update array read by Spectacle object
}


void spectdelay_fft_msg(t_spectdelay *x, t_symbol *s, short argc, t_atom *argv)
{
	if (argc != 3) {
		error("jg.spectdelay~: fft fftlen windowlen overlap");
		return;
	}
	int fftlen = _atom_getlong(0, argc, argv);
	int windowlen = _atom_getlong(1, argc, argv);
	int overlap = _atom_getlong(2, argc, argv);

	// If any of these params is updated, must rebuild object, restoring all
	// non-default settings.
	if (fftlen != x->fftlen || windowlen != x->windowlen
			|| overlap != x->overlap) {
		Spectacle *newsp = new Spectacle();
		newsp->init(fftlen, windowlen, overlap, x->srate, x->maxdeltime);
		newsp->set_eqtable(x->eqtable, x->eqtablen);
		newsp->set_deltable(x->dttable, x->dttablen);
		newsp->set_feedtable(x->fbtable, x->fbtablen);
		newsp->set_freqrange(x->minfreq, x->maxfreq);
		newsp->set_delay_freqrange(x->dminfreq, x->dmaxfreq);
		newsp->set_binmap_table(x->eqbinmap, x->eqbinmaplen);
		newsp->set_delay_binmap_table(x->delaybinmap, x->delaybinmaplen);
		newsp->set_hold(x->hold);
		newsp->set_posteq(x->posteq);
		x->fftlen = fftlen;
		x->windowlen = windowlen;
		x->overlap = overlap;

		// The perform function is running in a different thread, and it
      // accesses the Spectacle object.  We tell perform to delete the old
		// object and swap in the new one, when it's ready.  If we were to
		// do it here, perform could try to use a deleted Spectacle.
      x->newspectdelay = newsp;
	}
}


void spectdelay_hold_msg(t_spectdelay *x, t_symbol *s, short argc, t_atom *argv)
{
	if (argc != 1) {
		error("jg.spectdelay~: hold 0 or 1");
		return;
	}
   x->hold = (bool) _atom_getlong(0, argc, argv);
   x->spectdelay->set_hold(x->hold);
}


void spectdelay_posteq_msg(t_spectdelay *x, t_symbol *s, short argc, t_atom *argv)
{
	if (argc != 1) {
		error("jg.spectdelay~: posteq 0 or 1");
		return;
	}
   x->posteq = (bool) _atom_getlong(0, argc, argv);
   x->spectdelay->set_posteq(x->posteq);
}


void spectdelay_maxdel_msg(t_spectdelay *x, t_symbol *s, short argc, t_atom *argv)
{
	if (argc != 1) {
		error("jg.spectdelay~: maxdel time (max delay time in seconds)");
		return;
	}
	float maxdeltime = _fclamp(kMinMaxDelTime, _atom_getfloat(0, argc, argv),
	                                                          kMaxMaxDelTime);
	if (maxdeltime != x->maxdeltime) {
		if (maxdeltime < x->maxdeltime) {
			// pin existing delay times to new maximum
			for (int i = 0; i < x->dttablen; i++) {
				if (x->dttable[i] > maxdeltime)
					x->dttable[i] = maxdeltime;
			}
		}
		x->spectdelay->set_maxdeltime(maxdeltime);
		x->maxdeltime = maxdeltime;
	}
}


void spectdelay_range_msg(t_spectdelay *x, t_symbol *s, short argc, t_atom *argv)
{
	if (argc != 2) {
		error("jg.spectdelay~: range min max (EQ frequency range in Hz)");
		return;
	}
	const float nyquist = x->srate / 2;
	float max = _atom_getfloat(1, argc, argv);
	if (max == 0)
		max = nyquist;
	else
		max = _fclamp(0.0, max, nyquist);
	float min = _fclamp(0.0, _atom_getfloat(0, argc, argv), max);

	// changing range is expensive, so avoid when params are same
	if (min != x->minfreq || max != x->maxfreq) {
		x->minfreq = min;
		x->maxfreq = max;
		x->spectdelay->set_freqrange(min, max);
	}
}


_______________________________________________
Pd-dev mailing list
[email protected]
http://lists.puredata.info/listinfo/pd-dev

Reply via email to