Max 7 - 64bit vs 32bit dsp method / perform routine

Matthias's icon

Hi all,
I'm at the stage of slowly getting the hang of external development, but I'm still swimming. So here's what bothers me:

At the point of time the addendum section of Eric Lyon's book "Designing Audio Objects for Max/MSP and Pd" was published and I started developing Max externals, you basically had to include two separate dsp methods. Each of these called the respective perform routine.

So basically you either called the myexternal_perform or the myexternal_perform64 routines with dsp_add(arguments) in order to cover both 64bit and 32bit versions.

So far so good.

BUT:
In the section "Appendix: Updating Externals for Max 6" of the current SDK, Cycling '74 state the following:
"Several projects, including the simplemsp~ example, demonstrate how to support both 64-bit audio processing in Max 6 and 32-bit audio processing for compatibility with Max 5."

When I now review the code of the simplemsp~ example (obtained from the latest SDK release), I see one single "dsp64" method, which in turn calls the object_method sending the "dsp_add64" message. So I guess the above quote is a legacy statement which has not been updated.

But am I now correct in assuming that the current code of the simplemsp~ example is both 32bit and 64bit compatible in Max 7 down to Max 6.1, but not Max 6 or 5, since it usess that completely different approach with the object_method?

I am not targeting Max versions below 6.1, but I really wish to cover both 64bit and 32bit correctly. What do I have to do?

I'd be really grateful if someone could shed some light on this confusing topic.

Best,
-Matthias

Matthias's icon

So, I just answered part of the question myself.

When I call my 64bit perform routine with the object_method, the object won't load when I run Max in 32bit mode:object_method(dsp64, gensym("dsp_add64"), x, myobject_perform64, 0, NULL);

When I call my 64bit perform routine with dsp_add64(arguments), the object loads in Max 32bit mode, but Max will crash after a while:dsp_add64(dsp64, (t_object*)x, (t_perfroutine64)myobject_perform64, 0, NULL);

If I try to cover both 32bit and 64bit by using dsp_add64(arguments) and dsp_add(arguments) in their respective dsp method, I get a compiler warning that dsp_add(arguments) is deprecated.

I am really confused, so here's my question rephrased:
What is the preferred method to build an external object that will load in both 32bit and 64bit Max versions?

Thank you in advance!
-Matthias

Venetian's icon

you can do a simple test, print out some statement like

object_post((t_object*)x, "64bit routine called.");

I tended to find, the max5 routine is never called. I *think* the perform64 routine is called in 32 bit Max.
so I'm tending towards suggesting
_dsp and _perform are legacy methods, left in 'in case you want Max 5', but you could pretty much use the new _dsp64 and perform64 routines.

however, others will know better.

Matthias's icon

Thank you for your comment..time is limited at the moment. I'll do some tests and try to figure out how to do this properly.

Or maybe one of Cycling '74 would be willing to comment if it is at all possible to call a 32 bit dsp method / perform routine with the latest SDK without getting the deprecation warning?

Best,
-Matthias

Emmanuel Jourdan's icon

Since Max 6.1 (it doesn't matter if Max is set to run in 32 or 64 bits), when the dsp chain is started, Max look for a dsp64 method, if not found, it falls back to the dsp method (aka the old 32 bits floating point values) and adds a wrapper so everything works properly.

HTH,
ej

Matthias's icon

Hi Emmanuel

Thank you for your clarification. I slowly start to understand how Max calls the DSP method. However, I seem to have a different problem. I've moved the discussion over to the Max/MSP facebook group, where we're discussing what's wrong with my build settings.

I don't seem to be able to compile as a universal binary that can be loaded by both 32 bit and 64 bit Max.

Best,
-Matthias

Matthias's icon

Just a quick update on my findings:

The reason why I was not able to compile as a universal binary was because I messed up the build settings when renaming the Xcode project. When you rename your project from the project tree on the left, Xcode asks to update the build settings. Just deselect every item in the list and all should be fine.

Now my only problem is that my external crashes Max 32 bit right in the New Instance routine. This doesn't happen in Max 64 bit, so there seems to be something fishy with the memory allocation (I allocate some memory to a number of 2D arrays in the object structure, so I'm sure I'm missing something on that front).

Thanks for all your feedback.
-Matthias

Emmanuel Jourdan's icon

If it's a "large" amount of memory that you need to allocate, you should just put a pointer in your object structure and allocate properly in the new method (and free, in the free method).

Matthias's icon

Emmanuel, I have a number of array pointers in the object structure. And it's actually just regular arrays, not any tables or anything fancy; mistake from my side.

Here's an extract from my object structure, with all the arrays I allocate memory for:
typedef struct _cmbufferwin {
    t_pxobject obj;
...
    double *object_inlets;
    double *grain_params;
    double *randomized;
    double *testvalues;
    short *busy;
    long *grainpos;
    long *start;
    long *t_length;
    long *gr_length;
    double *pan_left;
    double *pan_right;
    double *gain;
...
} t_cmbufferwin;

In the new-instance routine, I allocate memory for each pointer like this (just as an example):
x->busy = (short *)sysmem_newptrclear((MAXGRAINS) * sizeof(short *));
    if (x->busy == NULL) {
        object_error((t_object *)x, "out of memory");
        return NULL;
    }

The size of MAXGRAINS is defined as 128. The in the free function, I free the memory for each pointer like this:
sysmem_freeptr(x->busy);

I am playing around with debugging at the moment, it seems that Max crashes as soon as I try to write some init values after allocating the memory in the new-instance routine, but I can't yet figure out what exactly causes the crash.

I also did some calculations with sizeof, and I end up with a value of < 5 MB for all arrays combined, so I can't be exceeding the 4 GB memory limit for 32 bit.

All works fine in 64 bit, by the way ;-)

Can you comment on this, Emmanuel?

Best
-Matthias

Jeremy's icon

x->busy = (short *)sysmem_newptrclear((MAXGRAINS) * sizeof(short *));

is wrong

x->busy = (short *)sysmem_newptrclear((MAXGRAINS) * sizeof(short));

is correct.

In 32-bit mode, your double* are 32-bits wide. Which means that those arrays will be 1/2 the size you are expecting, so when you begin to fill them with 64-bit-wide values, you will start trashing memory.

Jeremy

Matthias's icon

Jeremy,
Mind --> blown!!!

Thank you so, so very much! I will try this asap, and report back on my findings.

Best,
-Matthias

Matthias's icon

Jeremy,
That was it! The external no longer crashes Max in 32 bit!! Thank you so much again!
Best,
Matthias

Jeremy's icon

Awesome. Thanks for letting us know!