Adapting the teabox~ external for Max7-64 bits - help seeked

Roald Baudoux's icon

Hello,

In order to jump to the 64-bits world, I am getting rid of my last 32-bits externals. The very last one I have is teabox~  from Electrotap.

The source XCode project is available from GitHub.

1. Adapting the environment.
The project on GitHub is from 2012. That's the Max 6 era, with the latest MacOS X version at that time being Mountain Lion and probably some computers still running Tiger.

So I have replaced contents of the "74Objects.xconfig" file with contents from Max 7.1 SDK's "maxmspsdk.xconfig".
The project's info.plist file was also replaced by the one coming from the new SDK.

I have also removed all files from the "External Frameworks and Libraries" project's folder as they are not needed anymore, right?

The commonsyms.cpp file did not seem to be mandatory anymore, so I removed it too.

2. Creating 64-bits methods.
I have created code while looking at the one from the simplemsp~ example coming from SDK 6.0.4 (containing both 32-bits and 64-bits routines).

These prototypes have been added:
void teabox_dsp64(t_teabox *x, t_object *dsp64, short *count, double samplerate, long maxvectorsize, long flags); // ADDED - DSP Method - 64 bits
void teabox_perform64(t_teabox *x, t_object *dsp64, double **ins, long numins, double **outs, long numouts, long sampleframes, long flags, void *userparam);
void teabox_free(t_teabox *x);

3. Fixing deprecated code
One can add "C74_NO_DEPRECATION = 1" in the line beginning with "GCC_PREPROCESSOR_DEFINITIONS =" within the .xconfig file to bypass the error messages while compiling. However I have rather tried to replace all the deprecated code wherever I could.

The main function is now introduced with int C74_EXPORT main() instead of int main(void).

code like addmess((method)teabox_dsp,         "dsp", A_CANT, 0); has been replaced by class_addmethod(c, (method)teabox_dsp,         "dsp", A_CANT, 0);

I couldn't find the right replacement code for t_teabox *x = (t_teabox *)newobject(teabox_class);

Also, the 32-bits code dsp_add is deprecated too but I suppose there's no alternative for 32-bits compatibility (which I want to preserve at the moment).

So finally I have tried compiling with the deprecation messages disabled. The compiler ran fine but with the following warnings:
(null): Directory not found for option '-L/Users/admin/Documents/Max 7/Packages/max-sdk-7.1.0/source/3rdParties/teabox~/portaudio'
(null): Directory not found for option '-F/Users/admin/Documents/Max 7/Packages/max-sdk-7.1.0/source/3rdParties/teabox~/../../objectivemax/SDKs/Max5/c74support/max-includes'
(null): Directory not found for option '-F/Users/admin/Documents/Max 7/Packages/max-sdk-7.1.0/source/3rdParties/teabox~/../../objectivemax/SDKs/Max5/c74support/msp-includes'
(null): Directory not found for option '-F/Users/admin/Documents/Max 7/Packages/max-sdk-7.1.0/source/3rdParties/teabox~/../../../objectivemax/SDKs/Max5/c74support/max-includes'
(null): Directory not found for option '-F/Users/admin/Documents/Max 7/Packages/max-sdk-7.1.0/source/3rdParties/teabox~/../../../objectivemax/SDKs/Max5/c74support/msp-includes'

Outcome: the new object makes Max hanging indefinitely at startup. :-(

4. Miscellaneous
All current "new style" objects seem to include the ext_obex.h header but it's not included in the original code. Is it required or not?

Any idea to improve this code would be welcome. Thank you in advance. The updated project has been joined as a .zip file to this message.

teabox64-attempt-1.zip
zip
Roald Baudoux's icon

This one seems to function correctly both for 32- and 64-bits modes:

/*
* Teabox demuxer object for Max/MSP
* Written by Tim Place
* Copyright © 2004 by Electrotap L.L.C.
* Updated 2016-06-15 by Roald Baudoux for Max 7/64-bits compatibility
*

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

*
*/

#include "ext.h"                // Required for all Max External Objects
#include "ext_strings.h"        // Used for the assistance strings
#include "ext_obex.h" // required for "new" style objects - required here ???
#include "z_dsp.h"                // Required for all MSP externals

static t_class *teabox_class;                // Required. Global pointing to this class

// Data Structure for this object
typedef struct _teabox{
    t_pxobject         x_obj;
    char            counter;        // used for keeping track of the current sensor number
    float            data[9];        // one container for the data from each sensor
    float            hw_version;        // version number (major.minor) of the connected Teabox
    float            last_value;        // used for error correction in the perform method
    void            *outlet;        // outlet for status and version messages
} t_teabox;

// Prototypes for methods: need a method for each incoming message type:
t_int *teabox_perform(t_int *w); // An MSP Perform (signal) Method
void teabox_dsp(t_teabox *x, t_signal **sp, short *count); // DSP Method - 32 bits
void teabox_assist(t_teabox *x, void *b, long m, long a, char *s); // Assistance Method
void *teabox_new(void); // New Object Creation Method
void teabox_getversion(t_teabox *x);
void teabox_getstatus(t_teabox *x);
// ADDED
void teabox_dsp64(t_teabox *x, t_object *dsp64, short *count, double samplerate, long maxvectorsize, long flags); // ADDED - DSP Method - 64 bits
void teabox_perform64(t_teabox *x, t_object *dsp64, double **ins, long numins, double **outs, long numouts, long sampleframes, long flags, void *userparam);
void teabox_free(t_teabox *x);

/************************************************************************************/
// Main() Function

int C74_EXPORT main()                // main recieves a copy of the Max function macros table
{
    //setup((t_messlist **)&teabox_class, (method)teabox_new, (method)dsp_free, (short)sizeof(t_teabox), 0L, 0);
teabox_class = class_new("teabox~", (method)teabox_new, (method)dsp_free, sizeof(t_teabox), 0, 0);

    class_addmethod(teabox_class, (method)teabox_dsp,         "dsp", A_CANT, 0); // Bind method "teabox_dsp" to the DSP call from MSP - 32 bits
class_addmethod(teabox_class, (method)teabox_dsp64,        "dsp64",    A_CANT, 0); // Bind method "teabox_dsp" to the DSP call from MSP - 64 bits
    class_addmethod(teabox_class, (method)teabox_assist,        "assist", A_CANT, 0);    // Bind method "teabox_assist" to assistance calls
    class_addmethod(teabox_class, (method)teabox_getversion,     "getversion", 0);
    class_addmethod(teabox_class, (method)teabox_getstatus,    "getstatus", 0);

class_dspinit(teabox_class); // Setup object's class to work with MSP
    class_register(CLASS_BOX, teabox_class);

post("teabox~ external for Teabox sensor box - by Tim Place/Electrotap - 64 bits update by Roald Baudoux 15-06-2016");
}

/************************************************************************************/
// Object Creation Method

//void *teabox_new(void)
void *teabox_new()
{
    short i;

    //t_teabox *x = (t_teabox *)newobject(teabox_class);
t_teabox *x = (t_teabox *)object_alloc(teabox_class);

    dsp_setup((t_pxobject *)x,1);

    x->outlet = outlet_new(x, 0L);                // create the status/version outlet
    for(i=0; i
        outlet_new((t_object *)x, "signal");    // Create new signal outlet
        x->data[i] = 0;                            // Init this element of the data array
    }

    x->counter = 0;                                // init member values
    x->hw_version = 0;

    return(x);                                    // return the pointer to our instance
}

/************************************************************************************/
// Methods bound to input/inlets

// Method for Assistance Messages
void teabox_assist(t_teabox *x, void *b, long msg, long arg, char *dst)
{
    if (msg == 1)        // Inlets
        strcpy (dst, "(signal) Input");
    else if (msg == 2) {    // Outlets
        switch (arg) {
            case 0: strcpy(dst, "(signal) Demultiplexed Sensor Signal 1"); break;
            case 1: strcpy(dst, "(signal) Demultiplexed Sensor Signal 2"); break;
            case 2: strcpy(dst, "(signal) Demultiplexed Sensor Signal 3"); break;
            case 3: strcpy(dst, "(signal) Demultiplexed Sensor Signal 4"); break;
            case 4: strcpy(dst, "(signal) Demultiplexed Sensor Signal 5"); break;
            case 5: strcpy(dst, "(signal) Demultiplexed Sensor Signal 6"); break;
            case 6: strcpy(dst, "(signal) Demultiplexed Sensor Signal 7"); break;
            case 7: strcpy(dst, "(signal) Demultiplexed Sensor Signal 8"); break;
            case 8: strcpy(dst, "(signal) Digital Sensors, Encoded as INT"); break;
            case 9: strcpy(dst, "(attributes) dumpout"); break;
        }
    }
}

// Method for Posting Hardware Version
void teabox_getversion(t_teabox *x)
{
    short         version_major, version_minor;
    char        version_string[64];
    t_symbol    *version_symbol;
    t_atom        version_atom;

    version_major = (int)((x->hw_version * 4095.0) + 0.49) >> 8;
    version_minor = (int)((x->hw_version * 4095.0) + 0.49) & 255;

    //post("Teabox Firmware Version: %i.%i", version_major, version_minor);
    sprintf(version_string, "%i.%i", version_major, version_minor);
    version_symbol = gensym(version_string);
    //SETSYM(&version_atom, version_symbol); // OLDCODE
atom_setsym(&version_atom, version_symbol); // NEWCODE

    outlet_anything(x->outlet, gensym("version"), 1, &version_atom);
}

// Method for Posting Status
void teabox_getstatus(t_teabox *x)
{
    t_atom    status_atom;

    if(x->hw_version){
        atom_setlong(&status_atom, 1); // SETLONG-> atom_setlong
    }
    else{
        atom_setlong(&status_atom, 0); // SETLONG-> atom_setlong
    }
    outlet_anything(x->outlet, gensym("status"), 1, &status_atom);
}

// NOT CALLED!, we use dsp_free for a generic free function
void teabox_free(t_teabox *x)
{
    ;
}

// Perform (signal) Method - delay is a constant (not a signal) - 32 bits
t_int *teabox_perform(t_int *w)
{
    t_teabox *x = (t_teabox *)(w[1]);
    t_float *in = (t_float *)(w[2]);        // input
    t_float *out1 = (t_float *)(w[3]);        // sensor outputs [1-8]...
    t_float *out2 = (t_float *)(w[4]);
    t_float *out3 = (t_float *)(w[5]);
    t_float *out4 = (t_float *)(w[6]);
    t_float *out5 = (t_float *)(w[7]);
    t_float *out6 = (t_float *)(w[8]);
    t_float *out7 = (t_float *)(w[9]);
    t_float *out8 = (t_float *)(w[10]);
    t_float *out9 = (t_float *)(w[11]);        // toggle output [9]
    int n = (int)(w[12]);
    float    value;
    long    bitmask;

    if (x->x_obj.z_disabled) goto out;

    while(n--){

        // INPUT SECTION
        value = *in++;

        if(value < 0.0 || x->counter > 9){            // If the sample is the start flag...
            if(x->last_value < 0.0)                    // Actually - if all 16 toggles on the Teabox digital inputs
                x->data[8] = x->last_value;            //    are high, it will look identical to the start flag - so
                                                    // so we compensate for that here.
            x->counter = 0;
        }
        else if(x->counter == 0){                    // if the sample is hardware version number...
            x->hw_version = value * 8.0;
            x->counter++;
        }
        else{
            x->data[x->counter - 1] = value * 8.0;    // Normalize the range
            x->counter++;
        }

        // POST-PROCESS TOGGLE INPUT BITMASK
        if(x->data[8] < 0){
            bitmask = x->data[8] * 32768;            // 4096 = 32768 / 8 (we already multiplied by 8)
            bitmask ^= -32768;
            bitmask = 32768 + (bitmask);            // 2^3
        }
        else
            bitmask = x->data[8] * 4096;            // 4096 = 32768 / 8 (we already multiplied by 8)

        // OUTPUT SECTION - PIPE THE STORED DATA OUT THE OUTLETS
        *out1++ = x->data[0];
        *out2++ = x->data[1];
        *out3++ = x->data[2];
        *out4++ = x->data[3];
        *out5++ = x->data[4];
        *out6++ = x->data[5];
        *out7++ = x->data[6];
        *out8++ = x->data[7];
        *out9++ = (float)bitmask;                    // Contains the 16-bits of digital inputs

        x->last_value = value;                        // store the input value for the next time around
    }
out:
    return (w+13);
}

// DSP Method - 32 bits
void teabox_dsp(t_teabox *x, t_signal **sp, short *count)
{
dsp_add(teabox_perform, 12, x,
    sp[0]->s_vec,
    sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec, sp[4]->s_vec, sp[5]->s_vec, sp[6]->s_vec, sp[7]->s_vec, sp[8]->s_vec, sp[9]->s_vec,
    sp[0]->s_n);
teabox_getstatus(x);                            // automatically report the status when the dsp is turned on
}

// Perform (signal) Method - 64 bits
void teabox_perform64(t_teabox *x, t_object *dsp64, double **ins, long numins, double **outs, long numouts, long sampleframes, long flags, void *userparam)
{
t_double *in = ins[0];        // input
    t_double *out1 = outs[0];        // sensor outputs [1-8]...
    t_double *out2 = outs[1];
    t_double *out3 = outs[2];
    t_double *out4 = outs[3];
    t_double *out5 = outs[4];
    t_double *out6 = outs[5];
    t_double *out7 = outs[6];
    t_double *out8 = outs[7];
    t_double *out9 = outs[8];    // toggle output [9]
int n = sampleframes;
double value;
long bitmask;

    while(n--){

        // INPUT SECTION
        value = *in++;

        if(value < 0.0 || x->counter > 9){            // If the sample is the start flag...
            if(x->last_value < 0.0)                    // Actually - if all 16 toggles on the Teabox digital inputs
                x->data[8] = x->last_value;            //    are high, it will look identical to the start flag - so
// so we compensate for that here.
            x->counter = 0;
        }
        else if(x->counter == 0){                    // if the sample is hardware version number...
            x->hw_version = value * 8.0;
            x->counter++;
        }
        else{
            x->data[x->counter - 1] = value * 8.0;    // Normalize the range
            x->counter++;
        }

        // POST-PROCESS TOGGLE INPUT BITMASK
        if(x->data[8] < 0){
            bitmask = x->data[8] * 32768;            // 4096 = 32768 / 8 (we already multiplied by 8)
            bitmask ^= -32768;
            bitmask = 32768 + (bitmask);            // 2^3
        }
        else
            bitmask = x->data[8] * 4096;            // 4096 = 32768 / 8 (we already multiplied by 8)

        // OUTPUT SECTION - PIPE THE STORED DATA OUT THE OUTLETS
        *out1++ = x->data[0];
        *out2++ = x->data[1];
        *out3++ = x->data[2];
        *out4++ = x->data[3];
        *out5++ = x->data[4];
        *out6++ = x->data[5];
        *out7++ = x->data[6];
        *out8++ = x->data[7];
        *out9++ = (double)bitmask;                    // Contains the 16-bits of digital inputs

        x->last_value = value;                        // store the input value for the next time around
    }

}

// DSP Method - 64 Bits
void teabox_dsp64(t_teabox *x, t_object *dsp64, short *count, double samplerate, long maxvectorsize, long flags)
{
    object_method(dsp64, gensym("dsp_add64"), x, teabox_perform64, 0, NULL);
}