flext.cpp 8.28 KB
Newer Older
Thomas Grill's avatar
Thomas Grill committed
1 2
/*
flext - C++ layer for Max and Pure Data externals
thomas's avatar
thomas committed
3

4
Copyright (c) 2001-2017 Thomas Grill (gr@grrrr.org)
thomas's avatar
thomas committed
5
For information on usage and redistribution, and for a DISCLAIMER OF ALL
Thomas Grill's avatar
Thomas Grill committed
6
WARRANTIES, see the file, "license.txt," in this distribution.
thomas's avatar
thomas committed
7 8 9 10 11 12
*/

/*! \file flext.cpp
    \brief Implementation of the flext base class.
*/
 
Thomas Grill's avatar
Thomas Grill committed
13 14 15
#ifndef __FLEXT_CPP
#define __FLEXT_CPP

thomas's avatar
thomas committed
16 17
#include "flext.h"
#include "flinternal.h"
18
#include "fldsp.h"
thomas's avatar
thomas committed
19
#include <cstring>
thomas's avatar
thomas committed
20

21 22
#include "flpushns.h"

thomas's avatar
thomas committed
23 24
// === flext_base ============================================

25
FLEXT_TEMPIMPL(const t_symbol *FLEXT_CLASSDEF(flext_base))::curtag = NULL;
thomas's avatar
thomas committed
26

27
FLEXT_TEMPIMPL(FLEXT_CLASSDEF(flext_base))::FLEXT_CLASSDEF(flext_base)()
thomas's avatar
thomas committed
28 29
    : incnt(0),outcnt(0)
    , insigs(0),outsigs(0)
thomas's avatar
thomas committed
30
#if FLEXT_SYS == FLEXT_SYS_PD || FLEXT_SYS == FLEXT_SYS_MAX
thomas's avatar
thomas committed
31
    ,outlets(NULL),inlets(NULL)
thomas's avatar
thomas committed
32
#endif
thomas's avatar
thomas committed
33
#if FLEXT_SYS == FLEXT_SYS_MAX
thomas's avatar
thomas committed
34
    ,indesc(NULL),outdesc(NULL)
thomas's avatar
thomas committed
35
#endif
thomas's avatar
thomas committed
36
{
thomas's avatar
thomas committed
37 38
    FLEXT_LOG1("%s - flext logging is on",thisName());

39
    methhead = NULL;
thomas's avatar
thomas committed
40 41
    bindhead = NULL;

thomas's avatar
thomas committed
42
    if(HasAttributes()) {
thomas's avatar
thomas committed
43 44 45 46 47
        // initialize when attribute processing is enabled
        attrhead = new ItemCont;
        attrdata = new AttrDataCont;
    }
    else {
thomas's avatar
thomas committed
48
        attrhead = NULL;
thomas's avatar
thomas committed
49 50
        attrdata = NULL;
    }
thomas's avatar
thomas committed
51 52
}

53 54 55 56 57 58 59 60 61
/*! This virtual function is called after the object has been created, that is, 
    after the constructor has been processed. 
    It creates the inlets and outlets and the message and attribute lists.
    \note You can override it in your own class, but be sure to call it, 
    \note otherwise no inlets/outlets will be created
    \note All inlet, outlets, method and attribute declarations must be made before a call to Init!
    \remark Creation of inlets/outlets can't be done upon declaration, as Max/MSP needs creation
    \remark in reverse.
*/
62
FLEXT_TEMPIMPL(bool FLEXT_CLASSDEF(flext_base))::Init()
63 64 65 66 67 68
{
    bool ok = flext_obj::Init();

    if(ok) ok = InitInlets() && InitOutlets();

    if(ok) {
69 70 71 72 73 74
#if FLEXT_SYS == FLEXT_SYS_MAX
		// according to the Max/MSP SDK this should be prior to any inlet creation, BUT
		// that doesn't seem to be true... multiple signal ins and additional inlets don't seem to work then      
		if(NeedDSP()) dsp_setup(thisHdr(),CntInSig()); // signal inlets   
#endif

75 76 77 78 79 80 81 82 83 84 85 86 87
        if(HasAttributes() && m_holdaargc && m_holdaargv) {
            // initialize creation attributes
            ok = InitAttrib(m_holdaargc,m_holdaargv);
        }
    }

    return ok;
}


/*! This virtual function is called before the destructor.
    We do this because here we can still call virtual methods.
*/
88
FLEXT_TEMPIMPL(void FLEXT_CLASSDEF(flext_base))::Exit()
thomas's avatar
thomas committed
89
{
90 91 92 93 94
#if FLEXT_SYS == FLEXT_SYS_MAX
    // according to David Z. one should do that first...
	if(NeedDSP()) dsp_free(thisHdr());
#endif

95 96 97 98 99
#if FLEXT_SYS == FLEXT_SYS_PD && !defined(FLEXT_NOATTREDIT)
    // attribute editor window may still be open -> close it
    gfxstub_deleteforkey(thisHdr());
#endif

thomas's avatar
thomas committed
100
#ifdef FLEXT_THREADS
thomas's avatar
thomas committed
101
    StopThreads();
thomas's avatar
thomas committed
102 103
#endif

thomas's avatar
thomas committed
104 105
    // send remaining pending messages for this object
    QFlush(this);
thomas's avatar
thomas committed
106

thomas's avatar
thomas committed
107 108
    // delete message lists
    if(bindhead) delete bindhead;  // ATTENTION: the object must free all memory associated to bindings itself
109
    if(methhead) delete methhead;
thomas's avatar
thomas committed
110 111 112
    if(attrhead) delete attrhead;
    if(attrdata) delete attrdata;
    
thomas's avatar
thomas committed
113
#if FLEXT_SYS == FLEXT_SYS_PD || FLEXT_SYS == FLEXT_SYS_MAX
thomas's avatar
thomas committed
114
    if(outlets) delete[] outlets;
thomas's avatar
thomas committed
115

thomas's avatar
thomas committed
116
    if(inlets) {
117 118 119
        FLEXT_ASSERT(incnt > 1);
        for(int ix = 1; ix < incnt; ++ix)
            if(inlets[ix-1]) {
thomas's avatar
thomas committed
120
                // release proxy object
thomas's avatar
thomas committed
121
#if FLEXT_SYS == FLEXT_SYS_PD
122
                pd_free(&inlets[ix-1]->obj.ob_pd);
thomas's avatar
thomas committed
123
#elif FLEXT_SYS == FLEXT_SYS_MAX
124
                freeobject((object *)inlets[ix-1]);
thomas's avatar
thomas committed
125
#endif
thomas's avatar
thomas committed
126 127 128
            }
        delete[] inlets;
    }
thomas's avatar
thomas committed
129
#endif
thomas's avatar
thomas committed
130

thomas's avatar
thomas committed
131
#if FLEXT_SYS == FLEXT_SYS_MAX
thomas's avatar
thomas committed
132 133 134 135 136 137 138 139
    if(indesc) {
        for(int i = 0; i < incnt; ++i) if(indesc[i]) delete[] indesc[i];
        delete[] indesc;
    }
    if(outdesc) {
        for(int i = 0; i < outcnt; ++i) if(outdesc[i]) delete[] outdesc[i];
        delete[] outdesc;
    }
thomas's avatar
thomas committed
140 141
#endif

142
    flext_obj::Exit();
thomas's avatar
thomas committed
143 144
}

145

146
FLEXT_TEMPIMPL(void FLEXT_CLASSDEF(flext_base))::AddMessageMethods(t_class *c,bool dsp,bool dspin)
thomas's avatar
thomas committed
147
{
thomas's avatar
thomas committed
148
    add_loadbang(c,cb_loadbang);
149

thomas's avatar
thomas committed
150
#if FLEXT_SYS == FLEXT_SYS_PD
151
    class_addmethod(c,(t_method)cb_click,gensym(const_cast<char *>("click")),A_FLOAT,A_FLOAT,A_FLOAT,A_FLOAT,A_FLOAT,A_NULL);
thomas's avatar
thomas committed
152
#elif FLEXT_SYS == FLEXT_SYS_MAX
thomas's avatar
thomas committed
153
    add_assist(c,cb_assist);
154
    add_dblclick(c,cb_click);
thomas's avatar
thomas committed
155
#endif
thomas's avatar
thomas committed
156

157
    SetProxies(c,dsp);
158
    StartQueue();
159 160
    
    if(dsp) {
161
#if FLEXT_SYS == FLEXT_SYS_MAX
martin hermant's avatar
martin hermant committed
162 163 164
#if MSP64
        add_dsp64(c,cb_dsp64);
#else
165
        add_dsp(c,cb_dsp);
martin hermant's avatar
martin hermant committed
166
#endif
167
        dsp_initclass();
168
#elif FLEXT_SYS == FLEXT_SYS_PD
169 170
        if(dspin)
            CLASS_MAINSIGNALIN(c,flext_hdr,defsig); // float messages going into the left inlet are converted to signal
171
        add_dsp(c,cb_dsp);
172 173 174
#else
#error Platform not supported!
#endif
175
    }
176 177
}

178

179 180 181
/*! Set up proxy classes and basic methods at class creation time
    This ensures that they are processed before the registered flext messages
*/
182
FLEXT_TEMPIMPL(void FLEXT_CLASSDEF(flext_base))::Setup(t_classid id)
183 184 185 186 187 188
{
    t_class *c = getClass(id);

#if FLEXT_SYS == FLEXT_SYS_MAX
	if(!IsLib(id))
#endif
thomas's avatar
thomas committed
189
        AddMessageMethods(c,IsDSP(id),HasDSPIn(id));
190

191
    if(HasAttributes(id)) {
thomas's avatar
thomas committed
192 193
        AddMethod(id,0,"getattributes",cb_ListAttrib);
        AddMethod(id,0,"getmethods",cb_ListMethods);
194 195

#if FLEXT_SYS == FLEXT_SYS_PD && !defined(FLEXT_NOATTREDIT)
thomas's avatar
thomas committed
196
        AddMethod(id,0,"attributedialog",cb_AttrDialog);
197
#endif
thomas's avatar
thomas committed
198
    }
thomas's avatar
thomas committed
199

thomas's avatar
thomas committed
200 201 202
#if FLEXT_SYS == FLEXT_SYS_PD
    SetGfx(id);
#endif
thomas's avatar
thomas committed
203 204
}

205
FLEXT_TEMPIMPL(void FLEXT_CLASSDEF(flext_base))::cb_loadbang(flext_hdr *c)
thomas's avatar
thomas committed
206 207 208 209
{ 
    Locker lock(c);
    thisObject(c)->CbLoadbang(); 
}   
thomas's avatar
thomas committed
210

211 212
FLEXT_TEMPIMPL(void FLEXT_CLASSDEF(flext_base))::m_loadbang() {}
FLEXT_TEMPIMPL(void FLEXT_CLASSDEF(flext_base))::CbLoadbang() { m_loadbang(); }
thomas's avatar
thomas committed
213

214
FLEXT_TEMPIMPL(void FLEXT_CLASSDEF(flext_base))::CbClick() {}
thomas's avatar
thomas committed
215

216
#if FLEXT_SYS == FLEXT_SYS_PD
217
FLEXT_TEMPIMPL(void FLEXT_CLASSDEF(flext_base))::cb_click(flext_hdr *c,t_floatarg xpos,t_floatarg ypos,t_floatarg shift,t_floatarg ctrl,t_floatarg alt)
thomas's avatar
thomas committed
218
{
thomas's avatar
thomas committed
219 220 221 222
    if(shift) {
        Locker lock(c);
        thisObject(c)->CbClick();
    }
223 224 225
}
#endif

thomas's avatar
thomas committed
226
#if FLEXT_SYS == FLEXT_SYS_MAX
227
FLEXT_TEMPIMPL(void FLEXT_CLASSDEF(flext_base))::cb_click(flext_hdr *c, Point pt, short mods)
228
{
thomas's avatar
thomas committed
229
    Locker lock(c);
230
    thisObject(c)->CbClick();
231 232
}

233
FLEXT_TEMPIMPL(void FLEXT_CLASSDEF(flext_base))::cb_assist(flext_hdr *c,void * /*b*/,long msg,long arg,char *s)
234
{ 
thomas's avatar
thomas committed
235
    Locker lock(c);
236 237
    flext_base *th = thisObject(c); 

thomas's avatar
thomas committed
238 239
    switch(msg) {
    case 1: //ASSIST_INLET:
240
        if(arg < th->incnt && th->indesc[arg]) strcpy(s,th->indesc[arg]);
thomas's avatar
thomas committed
241 242
        break;
    case 2: //ASSIST_OUTLET:
243 244 245
        if(arg < th->outcnt) {
            if(th->outdesc[arg]) strcpy(s,th->outdesc[arg]);
        }
thomas's avatar
thomas committed
246
        else
thomas's avatar
thomas committed
247
            if(arg == th->outcnt && th->HasAttributes()) strcpy(s,"Attributes");
thomas's avatar
thomas committed
248 249
        break;
    }
thomas's avatar
thomas committed
250
}
251
#endif
252

martin hermant's avatar
martin hermant committed
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279

#if MSP64
FLEXT_TEMPIMPL(void FLEXT_CLASSDEF(flext_base))::cb_dsp64(flext_hdr *c, t_object *dsp64, short *count, double samplerate, long maxvectorsize, long flags)
{
    Locker lock(c);
    flext_base *bobj = thisObject(c);
	
#if FLEXT_SYS == FLEXT_SYS_MAX
	// we must extra-check here if it is really a DSP object
	// obviously, for objects that are part of a library, one dsp_initclass enables DSP for all
	if(!bobj->IsDSP()) return;
#endif
    
	flext_dsp *obj;
#ifdef FLEXT_DEBUG
    obj = dynamic_cast<flext_dsp *>(bobj);
#else
    obj = static_cast<flext_dsp *>(bobj);
#endif
    
    FLEXT_ASSERT(obj);
	obj->SetupDsp64(c,dsp64,count,samplerate,maxvectorsize,flags);
    
}

#else

280
#if FLEXT_SYS == FLEXT_SYS_MAX
281
FLEXT_TEMPIMPL(void FLEXT_CLASSDEF(flext_base))::cb_dsp(flext_hdr *c,t_signal **sp,short *count)
282
#else
283
FLEXT_TEMPIMPL(void FLEXT_CLASSDEF(flext_base))::cb_dsp(flext_hdr *c,t_signal **sp)
284 285
#endif
{ 
thomas's avatar
thomas committed
286
    Locker lock(c);
287 288 289 290 291 292 293 294 295 296
    flext_base *bobj = thisObject(c); 
	
#if FLEXT_SYS == FLEXT_SYS_MAX
	// we must extra-check here if it is really a DSP object
	// obviously, for objects that are part of a library, one dsp_initclass enables DSP for all
	if(!bobj->IsDSP()) return;
#endif

	flext_dsp *obj;
#ifdef FLEXT_DEBUG
thomas's avatar
thomas committed
297
    obj = dynamic_cast<flext_dsp *>(bobj);
298 299 300 301
#else
    obj = static_cast<flext_dsp *>(bobj); 
#endif

thomas's avatar
thomas committed
302
    FLEXT_ASSERT(obj);
303 304
	obj->SetupDsp(sp);
}
martin hermant's avatar
martin hermant committed
305 306 307 308 309
#endif




310

311
FLEXT_TEMPIMPL(bool FLEXT_CLASSDEF(flext_base))::CbIdle() { return 0; }
312 313

#include "flpopns.h"
Thomas Grill's avatar
Thomas Grill committed
314 315 316 317

#endif // __FLEXT_CPP