pix_frei0r.cpp 16.2 KB
Newer Older
1
2
3
4
5
6
7
8
////////////////////////////////////////////////////////
//
// GEM - Graphics Environment for Multimedia
//
// zmoelnig@iem.kug.ac.at
//
// Implementation file
//
zmoelnig's avatar
zmoelnig committed
9
//    Copyright (c) 2011-2011 IOhannes m zmölnig. forum::für::umläute. IEM. zmoelnig@iem.at
10
11
12
13
14
15
//    For information on usage and redistribution, and for a DISCLAIMER OF ALL
//    WARRANTIES, see the file, "GEM.LICENSE.TERMS" in this distribution.
//
/////////////////////////////////////////////////////////

#include "pix_frei0r.h"
16
17
#include "Gem/Exception.h"
#include "Gem/Loaders.h"
18

19
#include "Gem/Dylib.h"
20
#include "RTE/RTE.h"
21

22
#include "Gem/Properties.h"
23

24
#include <iostream>
25
#include <stdio.h>
26

27
28
#include <map>

29
30
31
32
33
34
35
36
37
38
39
40
#ifdef _WIN32
# include <io.h>
# include <windows.h>
# define snprintf _snprintf
# define close _close

/*
 * Apple used to use CFBundle's to load FF plugins
 * currently this only crashes (on OSX-10.4 and OSX-10.5)
 * we therefore use dlopen() on OSX as well
 */
#elif defined __APPLE__ && 0
41
# include <mach-o/dyld.h>
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# include <unistd.h>
#else
# define DL_OPEN
# include <dlfcn.h>
# include <unistd.h>
#endif /* __APPLE__ */

#include <string.h>


#ifndef HAVE_STRNLEN
#define strnlen f0r_strnlen
static size_t f0r_strnlen(const char* str, size_t maxlen) {
  size_t len=0;
  if(NULL==str)return len;
  while(*str++ && len<maxlen)len++;

  return len;
}
#endif


zmoelnig's avatar
zmoelnig committed
64
65
class pix_frei0r::F0RPlugin {
public:
66
67
  bool init(void) {
    if(!f0r_init)return false;
68

69
70
71
72
73
74
75
76
77
78
79
80
    if(!f0r_get_plugin_info)return false;
    if(!f0r_get_param_info)return false;
    if(!f0r_construct)return false;
    if(!f0r_destruct)return false;
    if(!f0r_set_param_value)return false;
    if(!f0r_get_param_value)return false;
    if(!f0r_deinit)return false;

    int err=0;

    if(f0r_init)
      err=f0r_init();
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
81
82
83
84
    if (err<0) {
      ::error("[pix_frei0r] failed to initialize plugin");
      return false;
    }
85
86
87
88
89
90

    f0r_plugin_info_t info;
    f0r_get_plugin_info(&info);
    m_name = info.name;
    m_author = info.author;
    m_type = info.plugin_type;
zmoelnig's avatar
zmoelnig committed
91
92
93
94
95
96
97
98
99
    switch(m_type) {
    case (F0R_PLUGIN_TYPE_SOURCE):
    case (F0R_PLUGIN_TYPE_FILTER):
      break;
    default:
      ::error("[pix_frei0r] only supports sources/filters, no mixers!");
      return false;
    }

zmoelnig's avatar
zmoelnig committed
100
101
102
#ifdef __GNUC__
# warning check color type
#endif
103
    m_color = info.color_model;
zmoelnig's avatar
zmoelnig committed
104
105
106
#ifdef __GNUC__
# warning check compatibility
#endif
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
    m_frei0rVersion = info.frei0r_version;
    m_majorVersion = info.major_version;
    m_minorVersion = info.minor_version;
    m_explanation = info.explanation;

    ::post("%s by %s", info.name, info.author);
    ::post("%d:: %s", m_type, info.explanation);


    if(!f0r_update)return false;
    // if(!f0r_update2)return false;

    int numparameters = info.num_params;
    int i=0;
    m_parameterNames.clear();
zmoelnig's avatar
zmoelnig committed
122
    m_parameterTypes.clear();
123
124
    m_parameter.clear();

zmoelnig's avatar
zmoelnig committed
125
126
127
128
    // dummy parameter (so we start at 1)
    m_parameterNames.push_back("");
    m_parameterTypes.push_back(0);

129
130
131
132
    for(i=0; i<numparameters; i++) {
      f0r_param_info_t pinfo;
      f0r_get_param_info(&pinfo, i);
      m_parameterNames.push_back(pinfo.name);
zmoelnig's avatar
zmoelnig committed
133
      m_parameterTypes.push_back(pinfo.type);
134

zmoelnig's avatar
zmoelnig committed
135
      ::post("parm%02d[%s]: %s", i+1, pinfo.name, pinfo.explanation);
136
137
138
139
    }

    return true;
  }
zmoelnig's avatar
zmoelnig committed
140
141
142
143
144
  void deinit(void) {
    destruct();
    if(f0r_deinit)
      f0r_deinit();
  }
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159

  unsigned int m_width, m_height;

  bool construct(unsigned int width, unsigned int height) {
    destruct();
    m_instance=f0r_construct(width, height);
    m_width=width;
    m_height=height;
    return (m_instance!=NULL);
  }
  void destruct(void) {
    if(m_instance)
      f0r_destruct(m_instance);
    m_instance=NULL;
  }
160

161
162
163
164
165
166
167
168
169
170
171
172
173
  f0r_instance_t m_instance;

  std::string m_name;
  std::string m_author;
  int 	m_type;
  int 	m_color;
  int 	m_frei0rVersion;
  int 	m_majorVersion;
  int 	m_minorVersion;
  std::string m_explanation;

  gem::Properties m_parameter;
  std::vector<std::string>m_parameterNames;
zmoelnig's avatar
zmoelnig committed
174
  std::vector<int>m_parameterTypes;
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201

typedef int (*t_f0r_init)(void);
typedef void (*t_f0r_get_plugin_info)(f0r_plugin_info_t* pluginInfo);
typedef void (*t_f0r_get_param_info)(f0r_param_info_t* info, int param_index);
typedef f0r_instance_t (*t_f0r_construct)(unsigned int width, unsigned int height);
typedef void (*t_f0r_destruct)(f0r_instance_t instance);
typedef void (*t_f0r_set_param_value)(f0r_instance_t instance, f0r_param_t param, int param_index);
typedef void (*t_f0r_get_param_value)(f0r_instance_t instance, f0r_param_t param, int param_index);
typedef void (*t_f0r_update) (f0r_instance_t instance, double time, const uint32_t* inframe, uint32_t* outframe);
typedef void (*t_f0r_update2)(f0r_instance_t instance, double time, const uint32_t* inframe1, const uint32_t* inframe2, const uint32_t* inframe3, uint32_t* outframe);
typedef int (*t_f0r_deinit)(void);


  t_f0r_init f0r_init;
  t_f0r_get_plugin_info f0r_get_plugin_info;
  t_f0r_get_param_info f0r_get_param_info;
  t_f0r_construct f0r_construct;
  t_f0r_destruct f0r_destruct;
  t_f0r_set_param_value f0r_set_param_value;
  t_f0r_get_param_value f0r_get_param_value;
  t_f0r_update f0r_update;
  t_f0r_update2 f0r_update2;
  t_f0r_deinit f0r_deinit;


  void close(void) {
    destruct();
202
203
204
205
    if(f0r_deinit) {
      int err=f0r_deinit();
      if(err)::error("[%s] f0r_deinit() failed with %d", m_name.c_str(), err);
    }
206
207
  }

208
  F0RPlugin(std::string name) :
209
210
    m_width(0), m_height(0),
    m_instance(NULL),
211
212
213
    m_name(""), m_author(""),
    m_type(0), m_color(0),
    m_frei0rVersion(0), m_majorVersion(0), m_minorVersion(0),
214
    m_explanation(""),
215
    m_dylib(name)
216
217
218
219
220
221
222
223
224
225
226
227
  {
    f0r_init           =reinterpret_cast<t_f0r_init           >(m_dylib.proc("f0r_init"));
    f0r_get_plugin_info=reinterpret_cast<t_f0r_get_plugin_info>(m_dylib.proc("f0r_get_plugin_info"));
    f0r_get_param_info =reinterpret_cast<t_f0r_get_param_info >(m_dylib.proc("f0r_get_param_info"));
    f0r_construct      =reinterpret_cast<t_f0r_construct      >(m_dylib.proc("f0r_construct"));
    f0r_destruct       =reinterpret_cast<t_f0r_destruct       >(m_dylib.proc("f0r_destruct"));
    f0r_set_param_value=reinterpret_cast<t_f0r_set_param_value>(m_dylib.proc("f0r_set_param_value"));
    f0r_get_param_value=reinterpret_cast<t_f0r_get_param_value>(m_dylib.proc("f0r_get_param_value"));
    f0r_update         =reinterpret_cast<t_f0r_update         >(m_dylib.proc("f0r_update"));
    f0r_update2        =reinterpret_cast<t_f0r_update2        >(m_dylib.proc("f0r_update2"));
    f0r_deinit         =reinterpret_cast<t_f0r_deinit         >(m_dylib.proc("f0r_deinit"));

zmoelnig's avatar
zmoelnig committed
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
    if(!init()) {
      deinit();
      throw(GemException("couldn't instantiate frei0r plugin"));
    }
  }

  bool set(unsigned int key, bool value) {
    if(!m_instance)return false;
    f0r_param_bool v=value;
    f0r_set_param_value(m_instance, &v, key);
    return true;
  }
  bool set(unsigned int key, double value) {
    if(!m_instance)return false;
    f0r_param_double v=value;
    f0r_set_param_value(m_instance, &v, key);
    return true;
  }
  bool set(unsigned int key, double x, double y) {
    if(!m_instance)return false;
    f0r_param_position v;
    v.x=x;
    v.y=y;
    f0r_set_param_value(m_instance, &v, key);
    return true;
  }
  bool set(unsigned int key, double r, double g, double b) {
    if(!m_instance)return false;
    f0r_param_color v;
    v.r=r;
    v.g=g;
    v.b=b;
    f0r_set_param_value(m_instance, &v, key);
    return true;
  }
  bool set(unsigned int key, std::string s) {
    if(!m_instance)return false;
265
    f0r_param_string v=const_cast<f0r_param_string>(s.c_str());
zmoelnig's avatar
zmoelnig committed
266
267
    f0r_set_param_value(m_instance, &v, key);
    return true;
268
269
  }

zmoelnig's avatar
zmoelnig committed
270

271
272
273
274
275
276
  bool process(double time, imageStruct&input, imageStruct&output) {
    if(!m_instance || m_width!=input.xsize || m_height!=input.ysize)
      construct(input.xsize, input.ysize);

    if(!m_instance)return false;

277
278
    f0r_update(m_instance, time,
	       reinterpret_cast<const uint32_t*>(input.data),
279
280
281
282
283
284
285
286
	       reinterpret_cast<uint32_t*>(output.data));

    return true;
  }

  GemDylib m_dylib;
};

287
static std::map<const t_symbol*, std::string>s_class2filename;
288

289
CPPEXTERN_NEW_WITH_ONE_ARG(pix_frei0r,  t_symbol *, A_DEFSYM);
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305

/////////////////////////////////////////////////////////
//
// pix_frei0r
//
/////////////////////////////////////////////////////////
// Constructor
//
/////////////////////////////////////////////////////////

pix_frei0r :: pix_frei0r(t_symbol*s)
  : m_plugin(NULL)
  , m_canopen(false)
{
  //  throw(GemException("Gem has been compiled without Frei0r-support!"));
  int can_rgba=0;
306
  m_image.setCsizeByFormat(GL_RGBA_GEM);
307

308
309
310
311
  if(!s || s==&s_) {
    m_canopen=true;
    return;
  }
312
313
  std::string pluginname = s->s_name;
  std::string filename = pluginname;
314
315
  if(s_class2filename.find(s) != s_class2filename.end()) {
    filename=s_class2filename[s];
316
    //::post("using cached filename %s", filename.c_str());
317
318
319
320
321
322
    try {
      m_plugin = new F0RPlugin(filename);
    } catch (GemException&e) {
      // ignore the error, and keep trying
      m_plugin = 0;
    }
323
  }
324
325
326
327
328
  if (0 == m_plugin) {
    gem::RTE::RTE*rte=gem::RTE::RTE::getRuntimeEnvironment();
    if(rte) {
      filename=rte->findFile(pluginname, GemDylib::getDefaultExtension(), getCanvas());
    }
329

330
331
    m_plugin = new F0RPlugin(filename);
  }
332

zmoelnig's avatar
zmoelnig committed
333
334
335
336
  unsigned int numparams = m_plugin->m_parameterNames.size();
  char tempVt[5];

  unsigned int i;
zmoelnig's avatar
zmoelnig committed
337
  for(i=1; i<numparams; i++) {
zmoelnig's avatar
zmoelnig committed
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
    snprintf(tempVt, 5, "#%d", i);
    tempVt[4]=0;
    unsigned int parmType=0;
    t_symbol*s_inletType;
    parmType=m_plugin->m_parameterTypes[i];

    switch(parmType) {
    case(F0R_PARAM_BOOL):
    case(F0R_PARAM_DOUBLE):
      s_inletType=gensym("float");
    break;
    case(F0R_PARAM_COLOR):
    case(F0R_PARAM_POSITION):
      s_inletType=gensym("list");
    break;
    case(F0R_PARAM_STRING):
      s_inletType=gensym("symbol");
      break;
    default:
      s_inletType=&s_;
    }
    m_inlet.push_back(inlet_new(this->x_obj, &this->x_obj->ob_pd, s_inletType, gensym(tempVt)));
  }
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
}

/////////////////////////////////////////////////////////
// Destructor
//
/////////////////////////////////////////////////////////
pix_frei0r :: ~pix_frei0r()
{
  while(!m_inlet.empty()) {
    t_inlet*in=m_inlet.back();
    if(in)inlet_free(in);
    m_inlet.pop_back();
  }
  closeMess();
}

void pix_frei0r :: closeMess()
{
  if(m_plugin){
    delete m_plugin;
  }
  m_plugin=NULL;
}

void pix_frei0r :: openMess(t_symbol*s)
{
  if(!m_canopen) {
    error("this instance cannot dynamically change the plugin");
    return;
  }

  std::string pluginname = s->s_name;
393

394
395
396
  if(m_plugin) {
    delete m_plugin;
  }
397
398
  m_plugin=NULL;
  try {
399
400
401
402
403
404
    std::string filename = pluginname;
    gem::RTE::RTE*rte=gem::RTE::RTE::getRuntimeEnvironment();
    if(rte) {
      filename=rte->findFile(pluginname, GemDylib::getDefaultExtension(), getCanvas());
    }
    m_plugin = new F0RPlugin(filename);
405
406
407
  } catch (GemException&x) {
    error("%s", x.what());
  }
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435

  if(NULL==m_plugin) {
    error("unable to open '%s'", pluginname.c_str());
    return;
  }
}

/////////////////////////////////////////////////////////
// processImage
//
/////////////////////////////////////////////////////////
void pix_frei0r :: processRGBAImage(imageStruct &image)
{
  static double time=0;
  if(!m_plugin)return;

  m_image.xsize=image.xsize;
  m_image.ysize=image.ysize;
  m_image.reallocate();

  m_plugin->process(time, image, m_image);
  time++;

  image.data   = m_image.data;
  image.notowned = true;
  image.setCsizeByFormat(m_image.format);
}

zmoelnig's avatar
zmoelnig committed
436
437
438
439
440
void pix_frei0r :: parmMess(const std::string key, int argc, t_atom *argv){
  if(!m_plugin) {
    error("no plugin present! forgetting parameter....");
    return;
  }
zmoelnig's avatar
zmoelnig committed
441
  unsigned int i=0;
zmoelnig's avatar
zmoelnig committed
442
443
444
445
446
447
448
  for(i=0; i<m_plugin->m_parameterNames.size(); i++) {
    if(key==m_plugin->m_parameterNames[i]) {
      parmMess(i, argc, argv);
      return;
    }
  }
  error("unknown parameter '%s'", key.c_str());
449
450
451
}


zmoelnig's avatar
zmoelnig committed
452
void pix_frei0r :: parmMess(int key, int argc, t_atom *argv){
zmoelnig's avatar
zmoelnig committed
453
  unsigned int realkey=0;
zmoelnig's avatar
zmoelnig committed
454
455
456
457
  if(!m_plugin) {
    error("no plugin present! forgetting parameter....");
    return;
  }
zmoelnig's avatar
zmoelnig committed
458
459
  if(key<=0) {
    error("parameterIDs must be >0");
zmoelnig's avatar
zmoelnig committed
460
    return;
zmoelnig's avatar
zmoelnig committed
461
462
  } else {
    realkey=key-1;
zmoelnig's avatar
zmoelnig committed
463
  }
464
  if(static_cast<unsigned int>(key)>=m_plugin->m_parameterNames.size()) {
zmoelnig's avatar
zmoelnig committed
465
466
467
468
469
470
471
472
    error("parameterID out of bounds");
    return;
  }

  int type=m_plugin->m_parameterTypes[key];

  double r, g, b;
  double x, y;
473

zmoelnig's avatar
zmoelnig committed
474
475
476
477
478
479
480
  const char*name=m_plugin->m_parameterNames[key].c_str();
  switch(type) {
  case(F0R_PARAM_BOOL):
    if(argc!=1) {
      error("param#%02d('%s') is of type BOOL: need exactly 1 argument", key, name);
      return;
    }
zmoelnig's avatar
zmoelnig committed
481
    m_plugin->set(realkey, (atom_getfloat(argv)>0.5));
zmoelnig's avatar
zmoelnig committed
482
483
484
485
486
487
    break;
  case(F0R_PARAM_DOUBLE):
    if(argc!=1) {
      error("param#%02d('%s') is of type DOUBLE: need exactly 1 argument", key, name);
      return;
    }
zmoelnig's avatar
zmoelnig committed
488
    m_plugin->set(realkey, static_cast<double>(atom_getfloat(argv)));
zmoelnig's avatar
zmoelnig committed
489
490
491
492
493
494
495
496
497
    break;
  case(F0R_PARAM_COLOR):
    if(argc!=3) {
      error("param#%02d('%s') is of type COLOR: need exactly 3 arguments", key, name);
      return;
    }
    r=atom_getfloat(argv+0);
    g=atom_getfloat(argv+1);
    b=atom_getfloat(argv+2);
zmoelnig's avatar
zmoelnig committed
498
    m_plugin->set(realkey, r, g, b);
zmoelnig's avatar
zmoelnig committed
499
500
501
502
503
504
505
506
    break;
  case(F0R_PARAM_POSITION):
    if(argc!=2) {
      error("param#%02d('%s') is of type POSITION: need exactly 2 arguments", key, name);
      return;
    }
    x=atom_getfloat(argv+0);
    y=atom_getfloat(argv+1);
zmoelnig's avatar
zmoelnig committed
507
    m_plugin->set(realkey, x, y);
zmoelnig's avatar
zmoelnig committed
508
509
510
511
512
513
    break;
  case(F0R_PARAM_STRING):
    if(argc!=1) {
      error("param#%02d('%s') is of type STRING: need exactly 1 argument", key, name);
      return;
    }
zmoelnig's avatar
zmoelnig committed
514
    m_plugin->set(realkey, std::string(atom_getsymbol(argv)->s_name));
zmoelnig's avatar
zmoelnig committed
515
516
517
518
519
520
    break;
  default:
    error("param#%02d('%s') is of UNKNOWN type", key, name);
    break;
  }

521
522
523
524
525
}

static const int offset_pix_=strlen("pix_");

static void*frei0r_loader_new(t_symbol*s, int argc, t_atom*argv) {
526
  if(!s){
527
    ::verbose(2, "frei0r_loader: no name given");
528
529
530
    return 0;
  }
  ::verbose(2, "frei0r_loader: %s",s->s_name);
531
  try{
532
533
534
535
536
537
538
539
    Obj_header *obj = new (pd_new(pix_frei0r_class),(void *)NULL) Obj_header;
    char*realname=s->s_name+offset_pix_; /* strip of the leading 'pix_' */
    CPPExtern::m_holder = &obj->pd_obj;
    CPPExtern::m_holdname=s->s_name;
    obj->data = new pix_frei0r(gensym(realname));
    CPPExtern::m_holder = NULL;
    CPPExtern::m_holdname=NULL;
    return(obj);
540
  } catch (GemException&e) {
541
542
    ::verbose(2, "frei0r_loader: failed! (%s)", e.what());
    return 0;
543
544
545
  }
  return 0;
}
546
bool pix_frei0r :: loader(const t_canvas*canvas, const std::string&classname, const std::string&path) {
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
547
  if(strncmp("pix_", classname.c_str(), offset_pix_)) {
548
    return false;
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
549
  }
550
  std::string pluginname = classname.substr(offset_pix_);
551
552
553
  std::string filename = pluginname;
  gem::RTE::RTE*rte=gem::RTE::RTE::getRuntimeEnvironment();
  if(rte) {
554
555
556
557
    if (path.empty())
      filename=rte->findFile(pluginname, GemDylib::getDefaultExtension(), canvas);
    else
      filename=rte->findFile(path+"/"+pluginname, GemDylib::getDefaultExtension(), canvas);
558
  }
559
560
  pix_frei0r::F0RPlugin*plugin=NULL;
  try {
561
    plugin=new F0RPlugin(filename);
562
  } catch (GemException&e) {
563
    ::verbose(2, "frei0r_loader: failed!! (%s)", e.what());
564
565
    return false;
  }
566

567
568
  if(plugin!=NULL) {
    delete plugin;
569
570
571
    /* cache the filename that loads this plugin */
    s_class2filename[gensym(pluginname.c_str())]=filename;
    /* register a new class */
572
573
574
575
576
577
    class_addcreator(reinterpret_cast<t_newmethod>(frei0r_loader_new), gensym(classname.c_str()), A_GIMME, 0);
    return true;
  }
  return false;
}

578
static int frei0r_loader(const t_canvas *canvas, const char *classname, const char *path) {
579
  return pix_frei0r::loader(canvas, classname, path?path:"");
580
}
581
582
583
584
585
586
587
588
589

/////////////////////////////////////////////////////////
// static member function
//
/////////////////////////////////////////////////////////

void pix_frei0r :: obj_setupCallback(t_class *classPtr)
{
  class_addanything(classPtr, reinterpret_cast<t_method>(&pix_frei0r::parmCallback));
590
  class_addmethod  (classPtr, reinterpret_cast<t_method>(&pix_frei0r::openCallback), gensym("load"), A_SYMBOL, A_NULL);
591
592
593
594
595
596
  gem_register_loader(frei0r_loader);
}

void pix_frei0r :: parmCallback(void *data, t_symbol*s, int argc, t_atom*argv){
  if('#'==s->s_name[0]) {
    int i = atoi(s->s_name+1);
zmoelnig's avatar
zmoelnig committed
597
    GetMyClass(data)->parmMess(i, argc, argv);
598
  } else {
zmoelnig's avatar
zmoelnig committed
599
    GetMyClass(data)->parmMess(std::string(s->s_name), argc, argv);
600
601
602
603
604
605
  }
}

void pix_frei0r :: openCallback(void *data, t_symbol*name){
  GetMyClass(data)->openMess(name);
}