msp external problem update


    Aug 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 lDelayLength = delay; else x->lDelayLength = x->lMaxDelayLength; if(feedback 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; }
    --------------------------------------------------------------------------------

    • Aug 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
    • Aug 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