pix_record.cpp 11.3 KB
Newer Older
cclepper's avatar
cclepper committed
1
2
3
4
/*
 *  pix_record.cpp
 *
 */
5
#include "Gem/GemConfig.h"
cclepper's avatar
cclepper committed
6
7
#include "pix_record.h"

8
#include "Gem/State.h"
9
#include "Gem/Exception.h"
10

11
12
13
#include "plugins/PluginFactory.h"

#include <map>
14
#include <algorithm>
15

16
CPPEXTERN_NEW_WITH_GIMME(pix_record);
cclepper's avatar
cclepper committed
17

zmoelnig's avatar
zmoelnig committed
18
19
class pix_record :: PIMPL {
public:
20
21
22
23
  PIMPL(void) {};
  ~PIMPL(void) {};

  struct codechandle {
24
    codechandle(gem::plugins::record*h, const std::string c):handle(h), codec(c) {}
25

26
    gem::plugins::record*handle;
zmoelnig's avatar
zmoelnig committed
27
    std::string codec;
28
  };
zmoelnig's avatar
zmoelnig committed
29
  std::map<std::string, std::vector<codechandle> >m_codechandle;
30
31
  std::vector<std::string>m_codecs;

32
  void addCodecHandle(gem::plugins::record*handle, const std::string codec) {
33
34
35
#ifdef __GNUC__
# warning better handling of duplicate codecs
#endif
zmoelnig's avatar
zmoelnig committed
36
37
    /* FIXME: we should generate a unique codec-ID, e.g. "<handlename>:<codec>" */
    m_codechandle[codec].push_back(codechandle(handle, codec));
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
    m_codecs.push_back(codec);
  }
  void clearCodecHandle(void) {
    m_codecs.clear();
    m_codechandle.clear();
  }

  static gem::any atom2any(t_atom*ap) {
    gem::any result;
    if(ap) {
      switch(ap->a_type) {
      case A_FLOAT:
	result=atom_getfloat(ap);
	break;
      case A_SYMBOL:
	result=atom_getsymbol(ap)->s_name;
	break;
      default:
	result=ap->a_w.w_gpointer;
      }
    }
    return result;
  }
  static void addProperties(gem::Properties&props, int argc, t_atom*argv)
  {
    if(!argc)return;
64

65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
    if(argv->a_type != A_SYMBOL) {
      ::error("no key given...");
      return;
    }
    std::string key=std::string(atom_getsymbol(argv)->s_name);
    std::vector<gem::any> values;
    argc--; argv++;
    while(argc-->0) {
      values.push_back(atom2any(argv++));
    }
    switch(values.size()) {
    default:
      props.set(key, values);
      break;
    case 1:
      props.set(key, values[0]);
      break;
82
    case 0:
83
84
85
86
87
88
89
      {
	gem::any dummy;
	props.set(key, dummy);
      }
      break;
    }
  }
90

91
92
};

93
/////////////////////////////////////////////////////////
cclepper's avatar
cclepper committed
94
95
96
97
98
99
100
//
// pix_record
//
/////////////////////////////////////////////////////////
// Constructor
//
/////////////////////////////////////////////////////////
zmoelnig's avatar
zmoelnig committed
101
pix_record :: pix_record(int argc, t_atom *argv):
102
103
  m_banged(false), m_automatic(true),
  m_outNumFrames(NULL), m_outInfo(NULL),
zmoelnig's avatar
zmoelnig committed
104
  m_currentFrame(-1),
zmoelnig's avatar
zmoelnig committed
105
  m_maxFrames(0),
106
107
108
  m_recording(false),
  m_handle(NULL),
  m_pimpl(new PIMPL())
cclepper's avatar
cclepper committed
109
{
110
111
  if (argc != 0){
    error("ignoring arugments");
cclepper's avatar
cclepper committed
112
  }
cclepper's avatar
cclepper committed
113
  m_outNumFrames = outlet_new(this->x_obj, 0);
114
  m_outInfo      = outlet_new(this->x_obj, 0);
cclepper's avatar
cclepper committed
115

116

IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
117
  m_handle=gem::plugins::record::getInstance();
118
  getCodecList();
cclepper's avatar
cclepper committed
119
120
121
122
123
124
125
126
}

/////////////////////////////////////////////////////////
// Destructor
//
/////////////////////////////////////////////////////////
pix_record :: ~pix_record()
{
127
  if(m_handle)delete m_handle;
128
129
  outlet_free(m_outNumFrames);
  outlet_free(m_outInfo);
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163

  if(m_pimpl)delete m_pimpl;
}


/////////////////////////////////////////////////////////
// add backends
//
/////////////////////////////////////////////////////////
bool pix_record :: addHandle( std::vector<std::string>available, std::string ID)
{
  unsigned int i=0;
  int count=0;

  std::vector<std::string>id;
  if(!ID.empty()) {
    // if requested 'cid' is in 'available' add it to the list of 'id's
    if(std::find(available.begin(), available.end(), ID)!=available.end()) {
      id.push_back(ID);
    } else {
      // request for an unavailable ID
      verbose(2, "backend '%s' unavailable", ID.c_str());
      return false;
    }
  } else {
    // no 'ID' given: add all available IDs
    id=available;
  }

  for(i=0; i<id.size(); i++) {
    std::string key=id[i];
    verbose(2, "trying to add '%s' as backend", key.c_str());
    if(std::find(m_ids.begin(), m_ids.end(), key)==m_ids.end()) {
      // not yet added, do so now!
164
      gem::plugins::record         *handle=NULL;
zmoelnig's avatar
zmoelnig committed
165
      startpost("backend #%d='%s'\t", m_allhandles.size(), key.c_str());
166
      try {
167
        handle=gem::PluginFactory<gem::plugins::record>::getInstance(key);
168
      } catch (GemException&ex) {
169
170
        startpost("(%s) ", ex.what());
        handle=NULL;
171
      }
172
      if(NULL==handle) {
173
174
        post("<--- DISABLED");
        break;
175
176
177
178
      }
      endpost();

      m_ids.push_back(key);
zmoelnig's avatar
zmoelnig committed
179
      m_allhandles.push_back(handle);
180
      count++;
zmoelnig's avatar
zmoelnig committed
181
      verbose(2, "added backend#%d '%s' @ 0x%x", m_allhandles.size()-1, key.c_str(), handle);
182
183
184
185
186
187
188
189
190
191
192
    }
  }

  return (count>0);
}

//
// stops recording into the movie
//
void pix_record :: startRecording()
{
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
193
194
  if(!m_handle)return;

zmoelnig's avatar
zmoelnig committed
195
196
197
198
199
  if(m_filename.empty()) {
    error("start recording requested with no prior open");
    return;
  }

200
201
  // find a handle for the current settings (filename, codec, props)
  const std::string codec=m_codec;
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
202
203
204
  stopRecording();


zmoelnig's avatar
zmoelnig committed
205
  m_currentFrame = 0;
206
  unsigned int i=0;
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
207
208
  // do not re-set the codec, if there is no need...
  /* m_handle->setCodec(codec); */
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
209
  if(m_handle->start(m_filename, m_props)) {
210
211
    m_filename=std::string("");
    m_recording=true;
zmoelnig's avatar
zmoelnig committed
212
213
  } else {
    post("unable to open '%s'", m_filename.c_str());
214
  }
cclepper's avatar
cclepper committed
215
216
217
}

//
218
// stops recording into the movie
cclepper's avatar
cclepper committed
219
220
221
//
void pix_record :: stopRecording()
{
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
222
223
  if(!m_handle)return;

224
  if(m_recording) {
225
    m_handle->stop();
226
    m_currentFrame = 0;
227
    outlet_float(m_outNumFrames,m_currentFrame);
228
    verbose(1, "movie written");
229
  }
cclepper's avatar
cclepper committed
230

231
  m_recording=false;
cclepper's avatar
cclepper committed
232
233
234
235
236
237
238
239
240
}


/////////////////////////////////////////////////////////
// render
//
/////////////////////////////////////////////////////////
void pix_record :: render(GemState *state)
{
241
  if(!m_handle || !m_recording)return;
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
242

243
  //check if state exists
zmoelnig's avatar
zmoelnig committed
244
245
  if(!state)return;
  pixBlock*img=NULL;
246
  state->get(GemState::_PIX, img);
zmoelnig's avatar
zmoelnig committed
247
248

  if(!img || !img->image.data){
zmoelnig's avatar
zmoelnig committed
249
250
    return;
  }
251

252
253
  if(m_banged||m_automatic){
    //      if(m_maxFrames != 0 && m_currentFrame >= m_maxFrames) m_recordStop = 1;
zmoelnig's avatar
zmoelnig committed
254
    bool success=m_handle->write(&img->image);
255
256
257
258
    m_banged=false;

    if(success) {
      m_currentFrame++;
zmoelnig's avatar
zmoelnig committed
259
      outlet_float(m_outNumFrames,m_currentFrame);
260
261
    } else {
      stopRecording();
zmoelnig's avatar
zmoelnig committed
262
263
    }
  }
cclepper's avatar
cclepper committed
264
265
266
}

/////////////////////////////////////////////////////////
267
// Properties
cclepper's avatar
cclepper committed
268
269
//
/////////////////////////////////////////////////////////
270
void pix_record :: enumPropertiesMess()
cclepper's avatar
cclepper committed
271
{
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
272
  if(!m_handle)return;
273

IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
  gem::Properties props;
  if(!m_handle->enumProperties(props))
    return;

  int ac=0;
  t_atom ap[3];
  std::vector<std::string>keys=props.keys();

  SETFLOAT(ap+0, keys.size());
  outlet_anything(m_outInfo, gensym("numprops"), 1, ap);

  unsigned int i=0;
  for(i=0; i<keys.size(); i++) {
    ac=2;
    std::string key=keys[i];
    SETSYMBOL(ap+0, gensym(key.c_str()));
    switch(props.type(key)) {
    case gem::Properties::NONE:
      SETSYMBOL(ap+1, gensym("Bang"));
      break;
    case gem::Properties::DOUBLE: {
      double d=-1;
      SETSYMBOL(ap+1, gensym("Float"));
      /* LATER: get and show ranges */
      if(props.get(key, d)) {
	ac=3;
	SETFLOAT(ap+2, d);
301
      }
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
302
303
304
305
306
307
308
309
    }
      break;
    case gem::Properties::STRING: {
      SETSYMBOL(ap+1, gensym("Symbol"));
      std::string s;
      if(props.get(key, s)) {
	ac=3;
	SETSYMBOL(ap+2, gensym(s.c_str()));
310
      }
311
    }
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
312
313
314
315
316
317
      break;
    default:
      SETSYMBOL(ap+1, gensym("unknown"));
      break;
    }
    outlet_anything(m_outInfo, gensym("property"), ac, ap);
318
  }
cclepper's avatar
cclepper committed
319
}
320
void pix_record :: setPropertiesMess(t_symbol*s, int argc, t_atom*argv)
321
322
323
{
  PIMPL::addProperties(m_props, argc, argv);
}
cclepper's avatar
cclepper committed
324

325
void pix_record :: clearPropertiesMess()
cclepper's avatar
cclepper committed
326
{
327
  m_props.clear();
cclepper's avatar
cclepper committed
328
329
}

330
331


332
333
334
335
336
337
/////////////////////////////////////////////////////////
// dialogMess
//
/////////////////////////////////////////////////////////
void pix_record :: dialogMess()
{
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
338
339
340
341
  if(!m_handle)return;

  if(!m_handle->dialog()){
    error("unable to open settings dialog");
342
343
  }
}
344
/////////////////////////////////////////////////////////
345
// recordMess
346
347
348
349
350
//
/////////////////////////////////////////////////////////
void pix_record :: recordMess(bool on)
{
  if (on) {
351
    startRecording();
352
  }else{
353
    stopRecording();
354
355
  }
}
356
357
358
359
360
361
362

/////////////////////////////////////////////////////////
// spits out a list of installed codecs and stores them
//
/////////////////////////////////////////////////////////
void pix_record :: getCodecList()
{
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
  if(!m_handle)return;

  std::vector<std::string>codecs=m_handle->getCodecs();

  unsigned int i;
  for(i=0; i<codecs.size(); i++) {
    const std::string codecname=codecs[i];
    const std::string descr=m_handle->getCodecDescription(codecname);
    t_atom ap[3];

    verbose(2, "codec%d: '%s': %s", i, codecname.c_str(), (descr.empty()?"":descr.c_str()));
    SETFLOAT (ap+0, static_cast<t_float>(i));
    SETSYMBOL(ap+1, gensym(codecname.c_str()));
    SETSYMBOL(ap+2, gensym(descr.c_str()));
    outlet_anything(m_outInfo, gensym("codec"), 3, ap);
378
  }
379
380
381
382
383
384
385
}


/////////////////////////////////////////////////////////
// deals with the name of a codec
//
/////////////////////////////////////////////////////////
386
void pix_record :: codecMess(t_atom *argv)
387
{
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
388
389
  if(!m_handle)return;

390
#ifdef __GNUC__
zmoelnig's avatar
zmoelnig committed
391
#warning codecMess is a mess
392
#endif
zmoelnig's avatar
zmoelnig committed
393
394
395
396
397
398
399
400
401
402
403
404
405
  /*
   * allow setting of codec without handle
   */

  /*
   * codecMess should do the following:
   *  find "valid" handles (those that support the given codec)
   *  copy all valid handles from m_allhandles to m_handles
   *  query all valid handles for settable properties
   *
   * if a special codec is given (e.g. none at all), all handles are valid
   */

406
407
408
409
  std::string sid;

  if (A_SYMBOL==argv->a_type) {
    sid=std::string(atom_getsymbol(argv)->s_name);
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
410
  } else if (A_FLOAT==argv->a_type) {
411
    int id=atom_getint(argv);
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
412
    std::vector<std::string>codecs=m_handle->getCodecs();
413
    if(id>0 && ((unsigned int)id)<codecs.size())
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
414
415
416
417
      sid=codecs[id];
    else {
      error("invalid codec# %d (0..%d)", id, codecs.size());
      return;
418
419
    }
  }
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
420
  if(m_handle->setCodec(sid)) {
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
421
    m_codec=sid;
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
422
    verbose(1, "successfully set codec '%s'", sid.c_str());
423
  } else {
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
424
425
    error("couldn't find a valid backend for codec '%s'", sid.c_str());
    return;
426
  }
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
427
  enumPropertiesMess();
428
429
}

430
void pix_record :: fileMess(t_symbol*s, int argc, t_atom *argv)
cclepper's avatar
cclepper committed
431
{
zmoelnig's avatar
zmoelnig committed
432
  /* LATER let the record()-handles chose whether they accept an open request
433
   * and then try other handles (if available)
434
   * this would allow us to use this object for streaming, virtual output devices,...
435
   */
436
437
  if(argc) {
    m_filename=std::string(atom_getsymbol(argv)->s_name);
cclepper's avatar
cclepper committed
438
439
440
441
442
443
444
445
446
  }
}

/////////////////////////////////////////////////////////
// static member functions
//
/////////////////////////////////////////////////////////
void pix_record :: obj_setupCallback(t_class *classPtr)
{
447
  CPPEXTERN_MSG (classPtr, "file", fileMess);
cclepper's avatar
cclepper committed
448

449
450
  CPPEXTERN_MSG1(classPtr, "auto", autoMess, bool);
  CPPEXTERN_MSG0(classPtr, "bang", bangMess);
451
452
453
  CPPEXTERN_MSG1(classPtr, "record", recordMess, bool);
  CPPEXTERN_MSG0(classPtr, "dialog", dialogMess);
  CPPEXTERN_MSG0(classPtr, "codeclist", getCodecList);
zmoelnig's avatar
zmoelnig committed
454
  class_addmethod(classPtr, reinterpret_cast<t_method>(&pix_record::codecMessCallback),
455
		  gensym("codec"), A_GIMME, A_NULL);
456

457
  CPPEXTERN_MSG0(classPtr, "proplist", enumPropertiesMess);
458
  CPPEXTERN_MSG0(classPtr, "enumProps", enumPropertiesMess);
459
  CPPEXTERN_MSG (classPtr, "set", setPropertiesMess);
cclepper's avatar
cclepper committed
460

461
  CPPEXTERN_MSG0(classPtr, "clearProps", clearPropertiesMess);
462
  CPPEXTERN_MSG0(classPtr, "clearprops", clearPropertiesMess);
cclepper's avatar
cclepper committed
463
464
}

465
void pix_record :: bangMess(void)
466
{
467
  m_banged=true;
468
}
469
void pix_record :: autoMess(bool on)
470
{
471
  m_automatic=on;
472
473
474
475
}

void pix_record :: codecMessCallback(void *data, t_symbol *s, int argc, t_atom *argv)
{
476
477
  if(argc)
    GetMyClass(data)->codecMess(argv);
478
}