Forums > Dev

msp external problem update

August 29, 2006 | 12:57 pm

Hi Again,

Please have a look at the code below.

I include my dsp, perform and new funcions, as well as my struct
definition below, in the hope that someone can spot something I
haven’t. There are two versions of the perform function. If I use the
first (amstring_performtest) the external behaves strangely as
described, whereas if I use the second (amstring_performtest2) the
external behaves normally- no extra objects needed to make it work.
Some lines of code in the functions may seem purposeless, but this is
because of other parts taken out in an effort to simplify the problem.

It seems that when no sound is produced from my external, it is
because no sound is going in; the input vector is entirely made up of
zeros unless the extra object is also connected to the object which
supplies a signal to my external (as described in my last post). Now
of course it must be something in my object that is causing this to
happen, but despite staring and comparing to the SDK samples and
trying various modifications for hours, I cannot find out what the
cause of the problem is.

Thanks for any suggestions or theories,

Cheers,

Aengus.

——————————————————————————–

/*
* struct definition
*/
typedef struct _amstring
{
t_pxobject x_obj;
long lMaxDelayLength; // length of buffer
long lDelayLength;
double fFeedbackGain;
long lPointer; // read/write position marker
double dK; // filter coefficient
t_float fLastFilterOut;
t_float* pfBuffer;
double dSr; // sample rate
double dCutoff; //
long numnonzero;
} t_amstring;

/*
* handle the ‘dsp’ message
*/
void amstring_dsp(t_amstring *x, t_signal **sp, short *count)
{
// [ prepare for dsp ]
x->dSr = sp[0]->s_sr; // store sample rate in object in case filter
cutoff is changed while audio is on.
change_cutoff(x, x->dCutoff); // recalculate filter coefficient in
case sample rate has changed.

// post("disabled state %ld",x->x_obj.z_disabled);

// [ initialise memory to zero ]
for(int i=0;ilMaxDelayLength;i++) x->pfBuffer[i] = 0.0; // delay buffer
x->fLastFilterOut = 0.0; // y[n-1] for lp filter

// [ perform func., 4 args, in vector, out vector, pointer to obj,
vector length. ]
dsp_add(amstring_performtest, 4, sp[0]->s_vec, sp[1]->s_vec, x, sp[0]->s_n);
}

/*
* perform
*/
t_int *amstring_performtest(t_int* w) // 4 args. in memory pointed to by w.
{
t_float *in = (t_float *)(w[1]);
t_float *out = (t_float *)(w[2]);
t_amstring *x = (t_amstring *)(w[3]);
int n = (int)(w[4]);

// [ copy values from object into local variables for efficiency ]
long p = x->lPointer;
double fb = x->fFeedbackGain;
long dl = x->lDelayLength;
t_float* buf = x->pfBuffer;
t_float prev = x->fLastFilterOut;
double fc = x->dK;
t_float test;

// [ efficient way of doing loop ]
while(n–) // this will go around the loop n times.
{
*out++ = buf[p];
buf[p] = *in++;
p = (p+1)%(dl); // increment read/write marker modulo delay length
}

x->lPointer = p;

return (w+5);
}

t_int *amstring_performtest2(t_int* w) // 4 args. in memory pointed to by w.
{
t_float *in = (t_float *)(w[1]);
t_float *out = (t_float *)(w[2]);
t_amstring *x = (t_amstring *)(w[3]);
int n = (int)(w[4]);

// [ copy values from object into local variables for efficiency ]
long p = x->lPointer;
double fb = x->fFeedbackGain;
long dl = x->lDelayLength;
t_float* buf = x->pfBuffer;
t_float prev = x->fLastFilterOut;
double fc = x->dK;
t_float test;

// [ efficient way of doing loop ]
while(n–) // this will go around the loop n times.
{
*out++ = *in++;
}

return (w+5);
}

/*
* instance creation function
*/
void* amstring_new(long maxdelay, long delay, double feedback)
{
t_amstring* x = (t_amstring *)newobject(amstring_class);
dsp_setup((t_pxobject *)x, 1);

// [ set parameters ]
if(maxdelay > 0 ) x->lMaxDelayLength = maxdelay;
else x->lMaxDelayLength = DEFAULT_MAX_DELAY;
if(delay > 0 && delay < = maxdelay) x->lDelayLength = delay;
else x->lDelayLength = x->lMaxDelayLength;
if(feedback < = MAX_FB) x->fFeedbackGain = feedback;
else x->fFeedbackGain = 0.0;

// [ create buffer and initialise to 0.0 ]
x->pfBuffer = new t_float[x->lMaxDelayLength];
for(int i=0;ilMaxDelayLength;i++) x->pfBuffer[i] = 0.0;

// [ initialise y[n-1] for filter ]
x->fLastFilterOut = 0.0;

// [ initialise to start reading from and writing to first element ]
x->lPointer=0;

// [ initialise filter-related variables ]
x->dSr = sys_getsr();
x->dCutoff = x->dSr / 2.0; // default initial cutoff.
x->dK = calcK(x->dCutoff,x->dSr); // initialise filter coefficient

// [ create additional inlets ]
floatin(x,1); // change the cutoff freq of the filter (rightmost inlet)
floatin(x,2); // change the feedback (2nd from right inlet)
intin(x,3); // change the delay length

// [ create outlet ]
outlet_new((t_object *)x, "signal");

x->numnonzero = 0;

return x;
}

——————————————————————————–


August 29, 2006 | 4:06 pm

You may be encountering the MSP/Pd shared buffer feature: by default buffer memory is shared, so that it is quite possible that *in and *out refer to the same memory. Therefore in the below code you would be writing over your input with the first line.

{
*out++ = buf[p];
buf[p] = *in++;
p = (p+1)%(dl); // increment read/write marker modulo delay length
}

One solution is to set the Z_NO_INPLACE mask. (read SDK docs for more about this.) Alternatively, read input before writing output:

{

buf[p] = *in++;
*out++ = buf[p];
p = (p+1)%(dl); // increment read/write marker modulo delay length
}

hTh,

Eric


August 29, 2006 | 9:24 pm

On 29-Aug-2006, at 18:06, Eric Lyon wrote:

> {
> *out++ = buf[p];
> buf[p] = *in++;
> p = (p+1)%(dl); // increment read/write marker modulo
> delay length
> }
>
> One solution is to set the Z_NO_INPLACE mask. (read SDK docs for
> more about this.) Alternatively, read input before writing output:
>
> {
>
> buf[p] = *in++;
> *out++ = buf[p];
> p = (p+1)%(dl); // increment read/write marker modulo
> delay length
> }

Z_NO_INPLACE is the easy correct solution. The suggested improvement,
although it looks good, has a logic bug: buf[] is a buffer array, but
the ‘corrected’ code is overwriting the buffer before the the
buffered value can be used.

To (try) to make things clearer, the suggestion could be written as

*out++ = buf[p] = *in++;

which is definitely a different animal from Aengus’ code.

The safest way to write perform loops is to read the current *in to a
local variable, do whatever processing you want to do, and then write
to *out. So:

{
float temp = *in++;
*out++ = buf[p];
buf[p] = temp;

}

About

p = (p + 1) % (d1);

Modulo is a relatively expensive operation, every bit as expensive as
integer divide. On PPC it’s a bit more expensive because there is no
intrinsic mod operator on the chip. Don’t worry about this until
you’ve got the code actually working (correctness trumps efficiency).
But when you’ve got the code working and if performance is an issue,
you should consider replacing the statement with something like

if (++p >= d1) p = 0;

There’s an even better approach that gets the if-statement out of the
loop, but it’s a little tricky. James McCartney wrote a nice article
on this for the Music-DSP list, if there’s interest I’ll try to dig
it up.

– Peter

————– http://www.bek.no/~pcastine/Litter/ ————-
Peter Castine +–> Litter Power & Litter Bundle for Jitter
Universal Binaries on the way
iCE: Sequencing, Recording &
Interface Building for |home | chez nous|
Max/MSP Extremely cool |bei uns | i nostri|
http://www.dspaudio.com/ http://www.castine.de


Viewing 3 posts - 1 through 3 (of 3 total)