msp external problem


    Aug 23 2006 | 12:47 am
    Hi,
    Having written a few max objects in C, I've started my first msp
    object. I am working with Visual Studio .NET 2003 under Windows XP. I
    am using Max 4.5.5.
    To take some first steps, I copied the sample plussz~ Visual Studio
    project (which compiles and works fine), and exchanged the code in
    plussz~.c for the code at the end of the e-mail. I have found that if
    the line
    x->ltestvar = 0;
    in the amstring_new function is removed, the external works fine,
    passing the audio through unchanged. But with this line, it does not
    work. If you try to place the external in a patch it does not give the
    object any inputs or outputs. However, it does not say 'No such
    object' in the Max window.
    Does anyone have any idea why this might be the case?
    Thanks,
    Aengus.
    #include "ext.h"
    #include "z_dsp.h"
    void* amstring_class;
    typedef struct _amstring
    {
    t_pxobject* x_obj;
    long ltestvar;
    } t_amstring;
    void amstring_dsp(t_amstring *x, t_signal **sp, short *count);
    t_int *amstring_perform(t_int *w);
    void* amstring_new(long delay, float feedback);
    void main(void)
    {
    setup((t_messlist **)&amstring_class, (method)amstring_new, (method)dsp_free,
    (short)sizeof(t_amstring), 0L, A_DEFLONG, A_DEFFLOAT, A_NOTHING);
    addmess((method)amstring_dsp, "dsp", A_CANT, 0);
    dsp_initclass(); // must call this function for MSP object classes
    }
    void amstring_dsp(t_amstring *x, t_signal **sp, short *count)
    {
    dsp_add(amstring_perform, 4, sp[0]->s_vec, sp[1]->s_vec, x, sp[0]->s_n);
    }
    t_int *amstring_perform(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]);
    while (--n) *++out = *++in; // copy input to output
    return (w+5);
    }
    void* amstring_new(long delay_length, float feedback_gain)
    {
    t_amstring* x = (t_amstring *)newobject(amstring_class);
    dsp_setup((t_pxobject *)x, 1);
    outlet_new((t_pxobject *)x, "signal");
    x->ltestvar = 0; // external works if this line is removed
    return(x);
    }
    ____________________
    www.am-process.org

    • Aug 23 2006 | 1:04 am
      Hi Aengus,
      Look here:
      typedef struct _amstring
      {
      t_pxobject* x_obj;
      long ltestvar;
      } t_amstring;
      I don't think x_obj should be a pointer to a t_pxobject, but rather an
      actual t_pxobject instance. so change it to
      typedef struct _amstring
      {
      t_pxobject x_obj;
      long ltestvar;
      } t_amstring;
      and see if that works out.
      Ben
    • Aug 23 2006 | 1:04 am
      It looks like you have half of a pre-dec/incr approach to passing your vectors
      (which I recommend avoiding), which could potentially lead to a crash. Perhaps
      your DSP line should instead be:
      while (n--) *out++ = *in++; // copy input to output
      Good luck,
      Eric
    • Aug 23 2006 | 9:57 am
      Thanks to you both.
      That pointer which was the culprit might have taken a long time to
      find. Usually a typo like that causes a problem compiling (so you find
      them soon after you make them), so I guess I was somewhat unlucky to
      get as far as I did with that mistake in there.
      I was a little uncomfortable with the line which passes the vectors
      myself. I had copied it straight from the plussz~ example, just to get
      something working.
      Cheers,
      Aengus.
      On 8/23/06, Eric Lyon wrote:
      > It looks like you have half of a pre-dec/incr approach to passing your vectors
      > (which I recommend avoiding), which could potentially lead to a crash. Perhaps
      > your DSP line should instead be:
      >
      > while (n--) *out++ = *in++; // copy input to output
      >
      > Good luck,
      >
      > Eric
      >
      >
      > Quoting Aengus Martin :
      >
      > > Hi,
      > >
      > > Having written a few max objects in C, I've started my first msp
      > > object. I am working with Visual Studio .NET 2003 under Windows XP. I
      > > am using Max 4.5.5.
      > >
      > > To take some first steps, I copied the sample plussz~ Visual Studio
      > > project (which compiles and works fine), and exchanged the code in
      > > plussz~.c for the code at the end of the e-mail. I have found that if
      > > the line
      > >
      > > x->ltestvar = 0;
      > >
      > > in the amstring_new function is removed, the external works fine,
      > > passing the audio through unchanged. But with this line, it does not
      > > work. If you try to place the external in a patch it does not give the
      > > object any inputs or outputs. However, it does not say 'No such
      > > object' in the Max window.
      > >
      > > Does anyone have any idea why this might be the case?
      > >
      > > Thanks,
      > >
      > > Aengus.
      > >
      > >
      > > #include "ext.h"
      > > #include "z_dsp.h"
      > >
      > > void* amstring_class;
      > >
      > > typedef struct _amstring
      > > {
      > > t_pxobject* x_obj;
      > > long ltestvar;
      > > } t_amstring;
      > >
      > > void amstring_dsp(t_amstring *x, t_signal **sp, short *count);
      > > t_int *amstring_perform(t_int *w);
      > > void* amstring_new(long delay, float feedback);
      > >
      > > void main(void)
      > > {
      > > setup((t_messlist **)&amstring_class, (method)amstring_new,
      > > (method)dsp_free,
      > > (short)sizeof(t_amstring), 0L, A_DEFLONG, A_DEFFLOAT, A_NOTHING);
      > > addmess((method)amstring_dsp, "dsp", A_CANT, 0);
      > > dsp_initclass(); // must call this function for MSP object classes
      > > }
      > >
      > > void amstring_dsp(t_amstring *x, t_signal **sp, short *count)
      > > {
      > > dsp_add(amstring_perform, 4, sp[0]->s_vec, sp[1]->s_vec, x, sp[0]->s_n);
      > > }
      > >
      > > t_int *amstring_perform(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]);
      > >
      > > while (--n) *++out = *++in; // copy input to output
      > >
      > > return (w+5);
      > > }
      > >
      > > void* amstring_new(long delay_length, float feedback_gain)
      > > {
      > > t_amstring* x = (t_amstring *)newobject(amstring_class);
      > > dsp_setup((t_pxobject *)x, 1);
      > > outlet_new((t_pxobject *)x, "signal");
      > >
      > > x->ltestvar = 0; // external works if this line is removed
      > >
      > > return(x);
      > > }
      > >
      > >
      > > ____________________
      > > www.am-process.org
      > >
      >
      >
      >
      >
      --
      ____________________
      www.am-process.org
    • Aug 23 2006 | 11:30 am
      Both Ben and Eric are right, but there's more.
      > typedef struct _amstring
      > {
      > t_pxobject* x_obj;
      recto: t_pxobject x_obj;
      The SDK underscores that the first member of your object must be the
      whole entire t_pxobject struct, not a pointer.
      > long ltestvar;
      > } t_amstring;
      >
      > void amstring_dsp(t_amstring *x, t_signal **sp, short *count);
      > t_int *amstring_perform(t_int *w);
      > void* amstring_new(long delay, float feedback);
      >
      > void main(void)
      > {
      > setup((t_messlist **)&amstring_class, (method)amstring_new,
      > (method)dsp_free,
      > (short)sizeof(t_amstring), 0L, A_DEFLONG, A_DEFFLOAT, A_NOTHING);
      Consider yourself lucky that Max isn't already croaking here. In your
      declaration, sizeof(t_amstring) is 8, but the smallest possible Max
      object must be at least 16 bytes in size.
      > addmess((method)amstring_dsp, "dsp", A_CANT, 0);
      > dsp_initclass(); // must call this function for MSP object classes
      > }
      >
      > void amstring_dsp(t_amstring *x, t_signal **sp, short *count)
      > {
      > dsp_add(amstring_perform, 4, sp[0]->s_vec, sp[1]->s_vec, x, sp[0]-
      > >s_n);
      > }
      The parameters to dsp_add() must match what you're doing in your
      perform method, which they don't. At this stage of the game, you're
      better off to leave the dsp_add() call as is and modify your perform
      method. See below.
      >
      > t_int *amstring_perform(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]);
      >
      > while (--n) *++out = *++in; // copy input to output
      recto: while (n--) *out++ = *in++;
      Some of the SDK examples and documentation use predec and/or preinc
      instead of the postfix ops, but look *very* carefully at how they
      call dsp_add()! Objects that do this sort of stuff need to tweak the
      dsp_add() parameters to compensate for the fact that the main loop
      is "one too few" and that the read/write vectors "start one too
      early". This is easily overlooked when reading the SDK examples.
      Some processors can, indeed, shave a few cycles off by using predec
      instead of postdec. But there are other, less error-prone ways of
      doing this, such as:
      do { *out++ = *in++; } while (n-- > 0);
      This requires that the initial value for n be guaranteed to be
      strictly positive, which is more than a reasonable presumption for
      the size of an MSP signal vector.
      In either case, I find
      while (n-- > 0)
      far more readable for the loop condition, and most compilers will
      generate identical machine code with or without the explicit less-
      than-zero. But check your disassembly code if it worries you.
      > return (w+5);
      > }
      >
      > void* amstring_new(long delay_length, float feedback_gain)
      > {
      > t_amstring* x = (t_amstring *)newobject(amstring_class);
      > dsp_setup((t_pxobject *)x, 1);
      > outlet_new((t_pxobject *)x, "signal");
      >
      > x->ltestvar = 0; // external works if this line is removed
      This is writing four bytes at offset 4 into the t_pxobject struct
      that newobject() and dsp_setup() so carefully initialized. Max
      finally crashes.
      The offset at 4 bytes is actually the o_magic member of the basic
      t_object struct at the heart of every Max object. The Max engine
      checks this member at certain points to make sure that valid objects
      are being passed around. Overwriting this value behind Max' back is a
      good way to wreck havoc. As you have seen.
      >
      > return(x);
      > }
      This is just a style thing, but I don't know why people insist upon
      writing return statements as if they were functions. The parentheses
      are completely superfluous. They don't actually hurt anything; so
      don't worry, just be aware that they're superfluous.-
      return x;
      -- P
      -------------- 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|
    • Aug 27 2006 | 5:05 am
      Hi,
      Peter, thanks for the pointing those issues out- I think I've
      addressed all of them now.
      In fact the external seems to work exactly as I want it to except for
      one new problem which I don't understand at all. I've included a
      patcher in the email and a windows binary of the external is attached.
      It may not be appropriate to attach a binary like that, but it is tiny
      and it should not be necessary to use it to understand the issue- only
      to demonstrate it.
      The patcher contains a click~ object, which sends audio along two
      signal chains, each of which contains an instance of my am-string~
      object. One chain ends at dac~ 1 and the other at dac~ 2. The only
      difference between the two is that the *~ 0.9 in the right hand one is
      connected to another *~ object as well as to an instance of the
      am-string~ object. This extra *~ object is not connected to anything.
      If you click the bang, which sends a click down both signal chains,
      there is only output on the right hand chain. This is the one which
      has the extra *~ object which, by my understanding of the way max
      works, should not have any effect on anything.
      Can someone please explain? (I'll post the current source code for the
      object if it'd help)
      Thanks,
      Aengus.
      max v2;
      On 8/23/06, Peter Castine wrote:
      > Both Ben and Eric are right, but there's more.
      >
      > > typedef struct _amstring
      > > {
      > > t_pxobject* x_obj;
      > recto: t_pxobject x_obj;
      >
      > The SDK underscores that the first member of your object must be the
      > whole entire t_pxobject struct, not a pointer.
      >
      > > long ltestvar;
      > > } t_amstring;
      > >
      > > void amstring_dsp(t_amstring *x, t_signal **sp, short *count);
      > > t_int *amstring_perform(t_int *w);
      > > void* amstring_new(long delay, float feedback);
      > >
      > > void main(void)
      > > {
      > > setup((t_messlist **)&amstring_class, (method)amstring_new,
      > > (method)dsp_free,
      > > (short)sizeof(t_amstring), 0L, A_DEFLONG, A_DEFFLOAT, A_NOTHING);
      >
      > Consider yourself lucky that Max isn't already croaking here. In your
      > declaration, sizeof(t_amstring) is 8, but the smallest possible Max
      > object must be at least 16 bytes in size.
      >
      > > addmess((method)amstring_dsp, "dsp", A_CANT, 0);
      > > dsp_initclass(); // must call this function for MSP object classes
      > > }
      > >
      > > void amstring_dsp(t_amstring *x, t_signal **sp, short *count)
      > > {
      > > dsp_add(amstring_perform, 4, sp[0]->s_vec, sp[1]->s_vec, x, sp[0]-
      > > >s_n);
      > > }
      >
      > The parameters to dsp_add() must match what you're doing in your
      > perform method, which they don't. At this stage of the game, you're
      > better off to leave the dsp_add() call as is and modify your perform
      > method. See below.
      >
      > >
      > > t_int *amstring_perform(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]);
      > >
      > > while (--n) *++out = *++in; // copy input to output
      >
      > recto: while (n--) *out++ = *in++;
      >
      > Some of the SDK examples and documentation use predec and/or preinc
      > instead of the postfix ops, but look *very* carefully at how they
      > call dsp_add()! Objects that do this sort of stuff need to tweak the
      > dsp_add() parameters to compensate for the fact that the main loop
      > is "one too few" and that the read/write vectors "start one too
      > early". This is easily overlooked when reading the SDK examples.
      >
      > Some processors can, indeed, shave a few cycles off by using predec
      > instead of postdec. But there are other, less error-prone ways of
      > doing this, such as:
      >
      > do { *out++ = *in++; } while (n-- > 0);
      >
      > This requires that the initial value for n be guaranteed to be
      > strictly positive, which is more than a reasonable presumption for
      > the size of an MSP signal vector.
      >
      > In either case, I find
      >
      > while (n-- > 0)
      >
      > far more readable for the loop condition, and most compilers will
      > generate identical machine code with or without the explicit less-
      > than-zero. But check your disassembly code if it worries you.
      >
      > > return (w+5);
      > > }
      > >
      > > void* amstring_new(long delay_length, float feedback_gain)
      > > {
      > > t_amstring* x = (t_amstring *)newobject(amstring_class);
      > > dsp_setup((t_pxobject *)x, 1);
      > > outlet_new((t_pxobject *)x, "signal");
      > >
      > > x->ltestvar = 0; // external works if this line is removed
      >
      > This is writing four bytes at offset 4 into the t_pxobject struct
      > that newobject() and dsp_setup() so carefully initialized. Max
      > finally crashes.
      >
      > The offset at 4 bytes is actually the o_magic member of the basic
      > t_object struct at the heart of every Max object. The Max engine
      > checks this member at certain points to make sure that valid objects
      > are being passed around. Overwriting this value behind Max' back is a
      > good way to wreck havoc. As you have seen.
      >
      > >
      > > return(x);
      > > }
      >
      > This is just a style thing, but I don't know why people insist upon
      > writing return statements as if they were functions. The parentheses
      > are completely superfluous. They don't actually hurt anything; so
      > don't worry, just be aware that they're superfluous.-
      >
      > return x;
      >
      >
      > -- P
      >
      >
      >
      > -------------- 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
      >
      >
      >
      --
      ____________________
      www.am-process.org