Forums > Dev

msp external problem

August 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);
}

____________________
http://www.am-process.org


August 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


August 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


August 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);
> > }
> >
> >
> > ____________________
> > http://www.am-process.org
> >
>
>
>
>


____________________
http://www.am-process.org


August 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|
http://www.dspaudio.com/ http://www.castine.de


August 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;
#N vpatcher 384 172 896 638;
#P origin 0 -4;
#P window setfont "Sans Serif" 9.;
#P newex 317 189 27 9109513 *~;
#P newex 226 343 33 9109513 *~ 0.3;
#P newex 173 343 33 9109513 *~ 0.3;
#P newex 289 162 33 9109513 *~ 0.9;
#P user meter~ 334 301 414 314 50 0 168 0 103 103 103 255 153 0 255 0
0 217 217 0 153 186 0 12 3 3 3 3;
#P message 393 232 28 9109513 4000;
#P newex 393 211 45 9109513 loadbang;
#P message 251 211 39 9109513 params;
#P newex 300 252 104 9109513 am-string~ 600 165 0.8;
#P user meter~ 19 301 99 314 50 0 168 0 103 103 103 255 153 0 255 0 0
217 217 0 153 186 0 12 3 3 3 3;
#P message 175 232 28 9109513 4000;
#P newex 175 211 45 9109513 loadbang;
#P newex 110 162 33 9109513 *~ 0.9;
#P user meter~ 258 105 338 118 50 0 168 0 103 103 103 255 153 0 255 0
0 217 217 0 153 186 0 12 3 3 3 3;
#P button 206 31 33 0;
#P newex 206 67 32 9109513 click~;
#P message 56 211 39 9109513 params;
#P newex 82 252 104 9109513 am-string~ 600 165 0.8;
#P newex 186 391 44 9109513 dac~ 1 2;
#P toggle 138 349 19 0;
#P connect 2 0 10 0;
#P connect 3 0 2 0;
#P connect 7 0 2 0;
#P connect 4 0 7 0;
#P connect 2 0 17 0;
#P connect 8 0 9 0;
#P connect 9 0 2 3;
#P connect 0 0 1 0;
#P connect 17 0 1 0;
#P connect 5 0 4 0;
#P connect 18 0 1 1;
#P connect 11 0 18 0;
#P connect 4 0 6 0;
#P connect 4 0 16 0;
#P connect 16 0 11 0;
#P connect 12 0 11 0;
#P connect 16 0 19 0;
#P connect 11 0 15 0;
#P connect 13 0 14 0;
#P connect 14 0 11 3;
#P pop;

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


____________________
http://www.am-process.org


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