1 // liblives.cpp
2 // LiVES (lives-exe)
3 // (c) G. Finch <salsaman@gmail.com> 2015 - 2017
4 // Released under the GPL 3 or later
5 // see file ../COPYING for licensing details
6
7 /** \file liblives.cpp
8 liblives interface
9 */
10
11 #ifndef DOXYGEN_SKIP
12
13 #include "liblives.hpp"
14
15 #include <stdlib.h>
16 #include <pthread.h>
17 #include <unistd.h>
18 #include <iostream>
19
20 extern "C" {
21 typedef int Boolean;
22 #include <libOSC/libosc.h>
23 #include <libOSC/OSC-client.h>
24
25 #include "main.h"
26 #include "lbindings.h"
27 #include "effects-weed.h"
28
29 int real_main(int argc, char *argv[], pthread_t *gtk_thread, ulong id);
30
31 bool lives_osc_cb_quit(void *context, int arglen, const void *vargs, OSCTimeTag when, void *ra);
32
33 track_rect *find_block_by_uid(lives_mt *mt, ulong uid);
34 }
35
36 static volatile bool spinning;
37 static ulong msg_id;
38 static char *private_response;
39 static pthread_mutex_t spin_mutex = PTHREAD_MUTEX_INITIALIZER;
40 static pthread_mutex_t cond_mutex = PTHREAD_MUTEX_INITIALIZER;
41 static pthread_cond_t cond_done = PTHREAD_COND_INITIALIZER;
42
private_cb(lives::_privateInfo * info,void * data)43 static bool private_cb(lives::_privateInfo *info, void *data) {
44 if (info->id == msg_id) {
45 private_response = strdup(info->response);
46 spinning = false;
47 pthread_cond_signal(&cond_done);
48 return false;
49 }
50 return true;
51 }
52
53 #endif // doxygen_skip
54
55 //////////////////////////////////////////////////
56
57 namespace lives {
58 #ifndef DOXYGEN_SKIP
59 typedef struct {
60 ulong id;
61 livesApp *app;
62 } livesAppCtx;
63
64 static list<livesAppCtx> appMgr;
65
find_instance_for_id(ulong id)66 static livesApp *find_instance_for_id(ulong id) {
67 list<livesAppCtx>::iterator it;
68 for (it = appMgr.begin(); it != appMgr.end(); it++) {
69 if ((*it).id == id) return (*it).app;
70 }
71 return NULL;
72 }
73
74 #endif
75
setEncoding(lives_char_encoding_t enc)76 void livesString::setEncoding(lives_char_encoding_t enc) {
77 m_encoding = enc;
78 }
79
encoding()80 lives_char_encoding_t livesString::encoding() {
81 return m_encoding;
82 }
83
toEncoding(lives_char_encoding_t enc)84 livesString livesString::toEncoding(lives_char_encoding_t enc) {
85 if (enc == LIVES_CHAR_ENCODING_UTF8) {
86 if (m_encoding == LIVES_CHAR_ENCODING_LOCAL8BIT) {
87 livesString str(L2U8(this->c_str()));
88 str.setEncoding(LIVES_CHAR_ENCODING_UTF8);
89 return str;
90 }
91 #ifndef IS_MINGW
92 else if (m_encoding == LIVES_CHAR_ENCODING_FILESYSTEM) {
93 livesString str(F2U8(this->c_str()));
94 str.setEncoding(LIVES_CHAR_ENCODING_UTF8);
95 return str;
96 }
97 #endif
98 } else if (enc == LIVES_CHAR_ENCODING_FILESYSTEM) {
99 #ifndef IS_MINGW
100 if (m_encoding == LIVES_CHAR_ENCODING_UTF8) {
101 livesString str(U82F(this->c_str()));
102 str.setEncoding(LIVES_CHAR_ENCODING_FILESYSTEM);
103 return str;
104 }
105 #else
106 if (m_encoding == LIVES_CHAR_ENCODING_LOCAL8BIT) {
107 livesString str(U82L(this->c_str()));
108 str.setEncoding(LIVES_CHAR_ENCODING_FILESYSTEM);
109 return str;
110 }
111 #endif
112 } else if (enc == LIVES_CHAR_ENCODING_LOCAL8BIT) {
113 if (m_encoding == LIVES_CHAR_ENCODING_UTF8) {
114 livesString str(U82L(this->c_str()));
115 str.setEncoding(LIVES_CHAR_ENCODING_LOCAL8BIT);
116 return str;
117 }
118 #ifndef IS_MINGW
119 if (m_encoding == LIVES_CHAR_ENCODING_FILESYSTEM) {
120 livesString str(F2U8(this->c_str()));
121 str.assign(U82L(str.c_str()));
122 str.setEncoding(LIVES_CHAR_ENCODING_LOCAL8BIT);
123 return str;
124 }
125 #endif
126 }
127 return *this;
128 }
129
130
init(int argc,char * oargv[])131 void livesApp::init(int argc, char *oargv[]) {
132 char **argv;
133 char progname[] = "lives-exe";
134 if (argc < 0) argc = 0;
135 argc++;
136
137 argv = (char **)malloc(argc * sizeof(char *));
138 argv[0] = strdup(progname);
139
140 for (int i = 1; i < argc; i++) {
141 argv[i] = strdup(oargv[i - 1]);
142 }
143
144 ulong id = lives_random();
145 livesAppCtx *ctx = new livesAppCtx;
146
147 pthread_t *gtk_thread = new pthread_t;
148
149 ctx->id = id;
150 ctx->app = this;
151 appMgr.push_back(*ctx);
152
153 m_set = new set(this);
154 m_player = new player(this);
155 m_effectKeyMap = new effectKeyMap(this);
156 m_multitrack = new multitrack(this);
157
158 m_deinterlace = false;
159
160 m_thread = gtk_thread;
161
162 m_id = id;
163
164 real_main(argc, argv, m_thread, m_id);
165 free(argv);
166 }
167
168
livesApp()169 livesApp::livesApp() : m_id(0l) {
170 if (appMgr.empty())
171 init(0, NULL);
172 }
173
174
livesApp(int argc,char * argv[])175 livesApp::livesApp(int argc, char *argv[]) : m_id(0l) {
176 if (appMgr.empty())
177 init(argc, argv);
178 }
179
180
~livesApp()181 livesApp::~livesApp() {
182 if (!isValid()) return;
183 idle_quit(m_thread);
184 }
185
186
isValid() const187 bool livesApp::isValid() const {
188 return this != NULL && m_id != 0l;
189 }
190
191
isPlaying() const192 bool livesApp::isPlaying() const {
193 return status() == LIVES_STATUS_PLAYING;
194 }
195
196
isReady() const197 bool livesApp::isReady() const {
198 return status() == LIVES_STATUS_READY;
199 }
200
201
getSet()202 const set &livesApp::getSet() {
203 return *m_set;
204 }
205
206
getPlayer()207 const player &livesApp::getPlayer() {
208 return *m_player;
209 }
210
211
getMultitrack()212 const multitrack &livesApp::getMultitrack() {
213 return *m_multitrack;
214 }
215
216
appendClosure(lives_callback_t cb_type,callback_f func,void * data) const217 ulong livesApp::appendClosure(lives_callback_t cb_type, callback_f func, void *data) const {
218 closure *cl = new closure;
219 cl->id = lives_random();
220 cl->object = (livesApp *)this;
221 cl->cb_type = cb_type;
222 cl->func = (callback_f)func;
223 cl->data = data;
224 while (pthread_mutex_trylock(&spin_mutex)) {
225 // lock mutex so that new callbacks cannot be added yet
226 lives_usleep(::prefs->sleep_time);
227 }
228 ((livesApp *)this)->m_closures.push_back(cl);
229 pthread_mutex_unlock(&spin_mutex);
230 return cl->id;
231 }
232
233 #ifndef DOXYGEN_SKIP
234
setClosures(closureList cl)235 void livesApp::setClosures(closureList cl) {
236 m_closures = cl;
237 }
238
239 #endif
240
addCallback(lives_callback_t cb_type,modeChanged_callback_f func,void * data) const241 ulong livesApp::addCallback(lives_callback_t cb_type, modeChanged_callback_f func, void *data) const {
242 if (cb_type != LIVES_CALLBACK_MODE_CHANGED) return 0l;
243 return appendClosure(cb_type, (callback_f)func, data);
244 }
245
addCallback(lives_callback_t cb_type,private_callback_f func,void * data) const246 ulong livesApp::addCallback(lives_callback_t cb_type, private_callback_f func, void *data) const {
247 if (cb_type != LIVES_CALLBACK_PRIVATE) return 0l;
248 return appendClosure(cb_type, (callback_f)func, data);
249 }
250
addCallback(lives_callback_t cb_type,objectDestroyed_callback_f func,void * data) const251 ulong livesApp::addCallback(lives_callback_t cb_type, objectDestroyed_callback_f func, void *data) const {
252 if (cb_type != LIVES_CALLBACK_OBJECT_DESTROYED) return 0l;
253 return appendClosure(cb_type, (callback_f)func, data);
254 }
255
addCallback(lives_callback_t cb_type,appQuit_callback_f func,void * data) const256 ulong livesApp::addCallback(lives_callback_t cb_type, appQuit_callback_f func, void *data) const {
257 if (cb_type != LIVES_CALLBACK_APP_QUIT) return 0l;
258 return appendClosure(cb_type, (callback_f)func, data);
259 }
260
removeCallback(ulong id) const261 bool livesApp::removeCallback(ulong id) const {
262 while (pthread_mutex_trylock(&spin_mutex)) {
263 // lock mutex so that new callbacks cannot be added yet
264 lives_usleep(::prefs->sleep_time);
265 }
266 closureListIterator it = ((livesApp *)this)->m_closures.begin();
267 while (it != ((livesApp *)this)->m_closures.end()) {
268 if ((*it)->id == id) {
269 delete *it;
270 ((livesApp *)this)->m_closures.erase(it);
271 pthread_mutex_unlock(&spin_mutex);
272 return true;
273 }
274 ++it;
275 }
276 pthread_mutex_unlock(&spin_mutex);
277 return false;
278 }
279
280
showInfo(livesString text,bool blocking)281 lives_dialog_response_t livesApp::showInfo(livesString text, bool blocking) {
282 lives_dialog_response_t ret = LIVES_DIALOG_RESPONSE_INVALID;
283 if (!isValid() || status() == LIVES_STATUS_NOTREADY) return ret;
284 // if blocking wait for response
285 if (blocking) {
286 spinning = true;
287 msg_id = lives_random();
288 ulong cbid = addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
289 pthread_mutex_lock(&cond_mutex);
290 if (!idle_show_info(text.toEncoding(LIVES_CHAR_ENCODING_UTF8).c_str(), blocking, msg_id)) {
291 pthread_mutex_unlock(&cond_mutex);
292 spinning = false;
293 removeCallback(cbid);
294 } else {
295 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
296 pthread_mutex_unlock(&cond_mutex);
297 if (isValid()) {
298 ret = (lives_dialog_response_t)atoi(private_response);
299 lives_free(private_response);
300 }
301 }
302 return ret;
303 }
304 if (idle_show_info(text.toEncoding(LIVES_CHAR_ENCODING_UTF8).c_str(), blocking, 0))
305 return LIVES_DIALOG_RESPONSE_NONE;
306 return ret;
307 }
308
309
chooseFileWithPreview(livesString dirname,lives_filechooser_t preview_type,livesString title)310 livesString livesApp::chooseFileWithPreview(livesString dirname, lives_filechooser_t preview_type, livesString title) {
311 livesString emptystr;
312 if (!isValid() || status() == LIVES_STATUS_NOTREADY) return emptystr;
313 if (preview_type != LIVES_FILE_CHOOSER_VIDEO_AUDIO && preview_type != LIVES_FILE_CHOOSER_AUDIO_ONLY) return emptystr;
314 spinning = true;
315 msg_id = lives_random();
316 ulong cbid = addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
317 pthread_mutex_lock(&cond_mutex);
318 if (!idle_choose_file_with_preview(dirname.toEncoding(LIVES_CHAR_ENCODING_FILESYSTEM).c_str(),
319 title.toEncoding(LIVES_CHAR_ENCODING_UTF8).c_str(),
320 preview_type, msg_id)) {
321 pthread_mutex_unlock(&cond_mutex);
322 spinning = false;
323 removeCallback(cbid);
324 } else {
325 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
326 pthread_mutex_unlock(&cond_mutex);
327 if (isValid()) {
328 // last 2 chars are " " and %d (deinterlace choice)
329 livesString str(private_response, strlen(private_response) - 2, LIVES_CHAR_ENCODING_FILESYSTEM);
330 m_deinterlace = (bool)atoi(private_response + strlen(private_response) - 2);
331 lives_free(private_response);
332 return str;
333 }
334 }
335 return emptystr;
336 }
337
338
chooseSet()339 livesString livesApp::chooseSet() {
340 livesString emptystr;
341 if (!isValid() || status() == LIVES_STATUS_NOTREADY) return emptystr;
342 spinning = true;
343 msg_id = lives_random();
344 ulong cbid = addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
345 pthread_mutex_lock(&cond_mutex);
346 if (!idle_choose_set(msg_id)) {
347 pthread_mutex_unlock(&cond_mutex);
348 spinning = false;
349 removeCallback(cbid);
350 } else {
351 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
352 pthread_mutex_unlock(&cond_mutex);
353 if (isValid()) {
354 livesString str(private_response, LIVES_CHAR_ENCODING_FILESYSTEM);
355 lives_free(private_response);
356 return str;
357 }
358 }
359 return emptystr;
360 }
361
362
availableSets()363 livesStringList livesApp::availableSets() {
364 livesStringList list;
365 if (!isValid() || status() == LIVES_STATUS_NOTREADY) return list;
366 LiVESList *setlist = get_set_list(::prefs->workdir, true), *slist = setlist;
367 while (slist != NULL) {
368 list.push_back(livesString((const char *)slist->data, LIVES_CHAR_ENCODING_UTF8));
369 lives_free(slist->data);
370 slist = slist->next;
371 }
372 lives_list_free(setlist);
373 return list;
374 }
375
376
openFile(livesString fname,bool with_audio,double stime,int frames,bool deinterlace)377 clip livesApp::openFile(livesString fname, bool with_audio, double stime, int frames, bool deinterlace) {
378 if (!isValid() || status() == LIVES_STATUS_NOTREADY) return clip();
379 if (fname.empty()) return clip();
380 spinning = true;
381 msg_id = lives_random();
382 ulong cbid = addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
383 ulong cid = 0l;
384
385 pthread_mutex_lock(&cond_mutex);
386 if (!idle_open_file(fname.toEncoding(LIVES_CHAR_ENCODING_FILESYSTEM).c_str(), stime, frames, msg_id)) {
387 pthread_mutex_unlock(&cond_mutex);
388 spinning = false;
389 removeCallback(cbid);
390 } else {
391 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
392 pthread_mutex_unlock(&cond_mutex);
393 if (isValid()) {
394 cid = strtoul(private_response, NULL, 10);
395 lives_free(private_response);
396 }
397 }
398 return clip(cid, this);
399 }
400
401
reloadSet(livesString setname)402 bool livesApp::reloadSet(livesString setname) {
403 if (!isValid() || status() == LIVES_STATUS_NOTREADY) return false;
404 spinning = true;
405 msg_id = lives_random();
406 ulong cbid = addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
407
408 pthread_mutex_lock(&cond_mutex);
409 if (!idle_reload_set(setname.toEncoding(LIVES_CHAR_ENCODING_FILESYSTEM).c_str(), msg_id)) {
410 pthread_mutex_unlock(&cond_mutex);
411 spinning = false;
412 removeCallback(cbid);
413 return false;
414 }
415 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
416 pthread_mutex_unlock(&cond_mutex);
417 if (isValid()) {
418 bool ret = (bool)atoi(private_response);
419 lives_free(private_response);
420 return ret;
421 }
422 return false;
423 }
424
425
deinterlaceOption()426 bool livesApp::deinterlaceOption() {
427 return m_deinterlace;
428 }
429
430
mode()431 lives_interface_mode_t livesApp::mode() {
432 if (!isValid() || status() == LIVES_STATUS_NOTREADY) return LIVES_INTERFACE_MODE_INVALID;
433 if (m_multitrack->isActive()) return LIVES_INTERFACE_MODE_MULTITRACK;
434 return LIVES_INTERFACE_MODE_CLIPEDIT;
435 }
436
437
setMode(lives_interface_mode_t newmode)438 lives_interface_mode_t livesApp::setMode(lives_interface_mode_t newmode) {
439 if (!isValid() || status() == LIVES_STATUS_NOTREADY) return LIVES_INTERFACE_MODE_INVALID;
440 spinning = true;
441 msg_id = lives_random();
442 ulong cbid = addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
443
444 pthread_mutex_lock(&cond_mutex);
445 if (!idle_set_if_mode(newmode, msg_id)) {
446 pthread_mutex_unlock(&cond_mutex);
447 spinning = false;
448 removeCallback(cbid);
449 return mode();
450 }
451 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
452 pthread_mutex_unlock(&cond_mutex);
453 if (isValid()) {
454 lives_free(private_response);
455 }
456 return mode();
457 }
458
459
status() const460 lives_status_t livesApp::status() const {
461 if (!isValid()) return LIVES_STATUS_INVALID;
462 if (mainw->go_away) return LIVES_STATUS_NOTREADY;
463 if (mainw->is_processing) return LIVES_STATUS_PROCESSING;
464 if ((mainw->preview || mainw->event_list != NULL) && mainw->multitrack == NULL) return LIVES_STATUS_PREVIEW;
465 if (mainw->playing_file > -1 || mainw->preview) return LIVES_STATUS_PLAYING;
466 return LIVES_STATUS_READY;
467 }
468
469
cancel()470 bool livesApp::cancel() {
471 if (!isValid()) return false;
472 if (status() != LIVES_STATUS_PROCESSING) return false;
473 bool ret = false;
474 spinning = true;
475 msg_id = lives_random();
476 ulong cbid = addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
477
478 pthread_mutex_lock(&cond_mutex);
479 if (!idle_cancel_proc(msg_id)) {
480 pthread_mutex_unlock(&cond_mutex);
481 spinning = false;
482 removeCallback(cbid);
483 return false;
484 }
485 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
486 pthread_mutex_unlock(&cond_mutex);
487 if (isValid()) {
488 ret = (bool)atoi(private_response);
489 lives_free(private_response);
490 }
491 return ret;
492 }
493
494 #ifndef DOXYGEN_SKIP
495
closures()496 closureList &livesApp::closures() {
497 return m_closures;
498 }
499
500 #endif
501
invalidate()502 void livesApp::invalidate() {
503 m_id = 0l;
504 }
505
506
interactive()507 bool livesApp::interactive() {
508 return mainw->interactive;
509 }
510
511
setInteractive(bool setting)512 bool livesApp::setInteractive(bool setting) {
513 if (!isValid() || status() == LIVES_STATUS_NOTREADY) return mainw->interactive;
514 spinning = true;
515 msg_id = lives_random();
516 ulong cbid = addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
517 pthread_mutex_lock(&cond_mutex);
518 if (!idle_set_interactive(setting, msg_id)) {
519 pthread_mutex_unlock(&cond_mutex);
520 spinning = false;
521 removeCallback(cbid);
522 }
523 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
524 pthread_mutex_unlock(&cond_mutex);
525 if (isValid()) {
526 lives_free(private_response);
527 }
528 return setting;
529 }
530
531
getEffectKeyMap()532 const effectKeyMap &livesApp::getEffectKeyMap() {
533 return *m_effectKeyMap;
534 }
535
536
537 #ifndef DOXYGEN_SKIP
setPref(const char * prefidx,bool val) const538 bool livesApp::setPref(const char *prefidx, bool val) const {
539 if (!isValid() || status() == LIVES_STATUS_NOTREADY) return false;
540 spinning = true;
541 msg_id = lives_random();
542 ulong cbid = addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
543 pthread_mutex_lock(&cond_mutex);
544 if (!idle_set_pref_bool(prefidx, val, msg_id)) {
545 pthread_mutex_unlock(&cond_mutex);
546 spinning = false;
547 removeCallback(cbid);
548 return false;
549 }
550 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
551 pthread_mutex_unlock(&cond_mutex);
552 if (isValid()) {
553 lives_free(private_response);
554 }
555 return true;
556 }
557
setPref(const char * prefidx,int val) const558 bool livesApp::setPref(const char *prefidx, int val) const {
559 if (!isValid() || status() == LIVES_STATUS_NOTREADY) return false;
560 spinning = true;
561 msg_id = lives_random();
562 ulong cbid = addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
563 pthread_mutex_lock(&cond_mutex);
564 if (!idle_set_pref_int(prefidx, val, msg_id)) {
565 pthread_mutex_unlock(&cond_mutex);
566 spinning = false;
567 removeCallback(cbid);
568 return false;
569 }
570 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
571 pthread_mutex_unlock(&cond_mutex);
572 if (isValid()) {
573 lives_free(private_response);
574 }
575 return true;
576 }
577
setPref(const char * prefidx,int bitfield,bool val) const578 bool livesApp::setPref(const char *prefidx, int bitfield, bool val) const {
579 if (!isValid() || status() == LIVES_STATUS_NOTREADY) return false;
580 spinning = true;
581 msg_id = lives_random();
582 ulong cbid = addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
583 pthread_mutex_lock(&cond_mutex);
584 if (!idle_set_pref_bitmapped(prefidx, bitfield, val, msg_id)) {
585 pthread_mutex_unlock(&cond_mutex);
586 spinning = false;
587 removeCallback(cbid);
588 return false;
589 }
590 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
591 pthread_mutex_unlock(&cond_mutex);
592 if (isValid()) {
593 lives_free(private_response);
594 }
595 return true;
596 }
597 #endif
598
599 //////////////// player ////////////////////
600
player(livesApp * lives)601 player::player(livesApp *lives) {
602 // make shared ptr
603 m_lives = lives;
604 }
605
606
isValid() const607 bool player::isValid() const {
608 return m_lives != NULL && m_lives->isValid() && m_lives->status() != LIVES_STATUS_NOTREADY;
609 }
610
611
isPlaying() const612 bool player::isPlaying() const {
613 return isValid() && m_lives->isPlaying();
614 }
615
616
isRecording() const617 bool player::isRecording() const {
618 return isValid() && mainw->record;
619 }
620
621
play() const622 bool player::play() const {
623 if (!isValid() || !m_lives->isReady()) return false;
624 return start_player();
625 }
626
627
stop() const628 bool player::stop() const {
629 if (!isPlaying()) return false;
630 spinning = true;
631 msg_id = lives_random();
632 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
633 pthread_mutex_lock(&cond_mutex);
634 if (!idle_stop_playback(msg_id)) {
635 pthread_mutex_unlock(&cond_mutex);
636 spinning = false;
637 m_lives->removeCallback(cbid);
638 return false;
639 }
640 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
641 pthread_mutex_unlock(&cond_mutex);
642 if (isValid()) {
643 bool ret = atoi(private_response);
644 lives_free(private_response);
645 return ret;
646 }
647 return false;
648 }
649
650
setSepWin(bool setting) const651 void player::setSepWin(bool setting) const {
652 if (!isValid()) return;
653 spinning = true;
654 msg_id = lives_random();
655 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
656 pthread_mutex_lock(&cond_mutex);
657 if (!idle_set_sepwin(setting, msg_id)) {
658 pthread_mutex_unlock(&cond_mutex);
659 spinning = false;
660 m_lives->removeCallback(cbid);
661 return;
662 }
663 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
664 pthread_mutex_unlock(&cond_mutex);
665 if (isValid()) {
666 lives_free(private_response);
667 }
668 return;
669 }
670
671
setFullScreen(bool setting) const672 void player::setFullScreen(bool setting) const {
673 if (!isValid()) return;
674 spinning = true;
675 msg_id = lives_random();
676 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
677 pthread_mutex_lock(&cond_mutex);
678 if (!idle_set_fullscreen(setting, msg_id)) {
679 pthread_mutex_unlock(&cond_mutex);
680 spinning = false;
681 m_lives->removeCallback(cbid);
682 return;
683 }
684 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
685 pthread_mutex_unlock(&cond_mutex);
686 if (isValid()) {
687 lives_free(private_response);
688 }
689 return;
690 }
691
692
sepWin() const693 bool player::sepWin() const {
694 if (!isValid()) return false;
695 return mainw->sep_win;
696 }
697
698
fullScreen() const699 bool player::fullScreen() const {
700 if (!isValid()) return false;
701 return mainw->fs;
702 }
703
704
setForegroundClip(clip c) const705 bool player::setForegroundClip(clip c) const {
706 if (!isPlaying()) return false;
707 return c.switchTo();
708 }
709
710
setBackgroundClip(clip c) const711 bool player::setBackgroundClip(clip c) const {
712 if (!isPlaying()) return false;
713 return c.setIsBackground();
714 }
715
716
foregroundClip() const717 clip player::foregroundClip() const {
718 if (!isPlaying()) return clip();
719 if (m_lives->m_multitrack->isActive()) return clip();
720 if (mainw->files[mainw->playing_file] != NULL) return clip(mainw->files[mainw->playing_file]->unique_id, m_lives);
721 return clip();
722 }
723
backgroundClip() const724 clip player::backgroundClip() const {
725 if (!isPlaying()) return clip();
726 if (m_lives->m_multitrack->isActive()) return clip();
727 if (mainw->files[mainw->blend_file] != NULL) return clip(mainw->files[mainw->blend_file]->unique_id, m_lives);
728 return clip();
729 }
730
setFS(bool setting) const731 void player::setFS(bool setting) const {
732 if (!isValid()) return;
733 spinning = true;
734 msg_id = lives_random();
735 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
736 pthread_mutex_lock(&cond_mutex);
737 if (!idle_set_fullscreen_sepwin(setting, msg_id)) {
738 pthread_mutex_unlock(&cond_mutex);
739 spinning = false;
740 m_lives->removeCallback(cbid);
741 return;
742 }
743 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
744 pthread_mutex_unlock(&cond_mutex);
745 if (isValid()) {
746 lives_free(private_response);
747 }
748 return;
749 }
750
751
videoPlaybackTime(bool background) const752 double player::videoPlaybackTime(bool background) const {
753 if (!isValid()) return 0.;
754
755 if (m_lives->status() == LIVES_STATUS_NOTREADY || m_lives->status() == LIVES_STATUS_PROCESSING) return 0.;
756
757 if (!m_lives->m_multitrack->isActive()) {
758 if (mainw->current_file == -1) return 0.;
759 if (mainw->playing_file > -1) {
760 if (!background) return (cfile->frameno - 1.) / cfile->fps;
761 else if (mainw->blend_file != -1 && mainw->blend_file != mainw->current_file && mainw->files[mainw->blend_file] != NULL) {
762 return mainw->files[mainw->blend_file]->frameno;
763 } else return 0.;
764 } else return cfile->pointer_time;
765 } else {
766 return lives_ruler_get_value(LIVES_RULER(mainw->multitrack->timeline));
767 }
768 }
769
770
audioPlaybackTime() const771 double player::audioPlaybackTime() const {
772 if (!isValid()) return 0.;
773
774 if (m_lives->status() != LIVES_STATUS_NOTREADY || m_lives->status() == LIVES_STATUS_PROCESSING) return 0.;
775
776 if (!m_lives->m_multitrack->isActive()) {
777 if (mainw->current_file == -1) return 0.;
778 if (mainw->playing_file > -1) return (mainw->aframeno - 1.) / cfile->fps;
779 else return cfile->pointer_time;
780 } else {
781 return lives_ruler_get_value(LIVES_RULER(mainw->multitrack->timeline));
782 }
783 }
784
785
setAudioPlaybackTime(double time) const786 double player::setAudioPlaybackTime(double time) const {
787 if (!isValid()) return 0.;
788 if (!m_lives->isPlaying()) return 0.;
789 if (!is_realtime_aplayer(::prefs->audio_player)) return 0.;
790 if (mainw->record && ::prefs->audio_src == AUDIO_SRC_EXT) return 0.;
791 if (mainw->multitrack != NULL) return 0.;
792 if (time < 0. || time > cfile->laudio_time) return 0.;
793
794 spinning = true;
795 msg_id = lives_random();
796 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
797 pthread_mutex_lock(&cond_mutex);
798 if (!idle_set_current_audio_time(time, msg_id)) {
799 pthread_mutex_unlock(&cond_mutex);
800 spinning = false;
801 m_lives->removeCallback(cbid);
802 } else {
803 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
804 pthread_mutex_unlock(&cond_mutex);
805 if (isValid()) {
806 lives_free(private_response);
807 }
808 }
809 return audioPlaybackTime();
810 }
811
812
setPlaybackStartTime(double time) const813 double player::setPlaybackStartTime(double time) const {
814 if (!isValid()) return 0.;
815 if (!m_lives->isReady()) return 0.;
816
817 spinning = true;
818 msg_id = lives_random();
819 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
820 pthread_mutex_lock(&cond_mutex);
821 if (!idle_set_current_time(time, msg_id)) {
822 pthread_mutex_unlock(&cond_mutex);
823 spinning = false;
824 m_lives->removeCallback(cbid);
825 } else {
826 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
827 pthread_mutex_unlock(&cond_mutex);
828 if (isValid()) {
829 lives_free(private_response);
830 }
831 }
832 return videoPlaybackTime();
833 }
834
835
setVideoPlaybackFrame(int frame,bool bg) const836 int player::setVideoPlaybackFrame(int frame, bool bg) const {
837 if (!isValid()) return 0;
838
839 spinning = true;
840 msg_id = lives_random();
841 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
842 pthread_mutex_lock(&cond_mutex);
843 if (!idle_set_current_frame(frame, bg, msg_id)) {
844 pthread_mutex_unlock(&cond_mutex);
845 spinning = false;
846 m_lives->removeCallback(cbid);
847 } else {
848 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
849 pthread_mutex_unlock(&cond_mutex);
850 if (isValid()) {
851 lives_free(private_response);
852 }
853 }
854 return videoPlaybackTime();
855 }
856
857
elapsedTime() const858 double player::elapsedTime() const {
859 if (!isPlaying()) return 0.;
860 return mainw->currticks / TICKS_PER_SECOND_DBL;
861 }
862
863
currentFPS() const864 double player::currentFPS() const {
865 if (!isValid()) return 0.;
866 if (mainw->current_file == -1 || cfile == NULL) return 0.;
867 if (m_lives->status() != LIVES_STATUS_PLAYING && m_lives->status() != LIVES_STATUS_READY) return 0.;
868 return cfile->pb_fps;
869 }
870
871
setCurrentFPS(double fps) const872 double player::setCurrentFPS(double fps) const {
873 if (!isPlaying()) return 0.;
874
875 spinning = true;
876 msg_id = lives_random();
877 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
878 pthread_mutex_lock(&cond_mutex);
879 if (!idle_set_current_fps(fps, msg_id)) {
880 pthread_mutex_unlock(&cond_mutex);
881 spinning = false;
882 m_lives->removeCallback(cbid);
883 } else {
884 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
885 pthread_mutex_unlock(&cond_mutex);
886 if (isValid()) {
887 lives_free(private_response);
888 }
889 }
890 return currentFPS();
891 }
892
893
currentAudioRate() const894 int player::currentAudioRate() const {
895 if (!isValid()) return 0.;
896 if (m_lives->status() != LIVES_STATUS_PLAYING && m_lives->status() != LIVES_STATUS_READY) return 0.;
897 if (mainw->current_file == -1 || cfile == NULL) return 0.;
898 return cfile->arps;
899 }
900
901
setLoopMode(lives_loop_mode_t mode) const902 lives_loop_mode_t player::setLoopMode(lives_loop_mode_t mode) const {
903 if (!isValid()) return loopMode();
904 spinning = true;
905 msg_id = lives_random();
906 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
907 pthread_mutex_lock(&cond_mutex);
908 if (!idle_set_loop_mode(mode, msg_id)) {
909 pthread_mutex_unlock(&cond_mutex);
910 spinning = false;
911 m_lives->removeCallback(cbid);
912 } else {
913 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
914 pthread_mutex_unlock(&cond_mutex);
915 if (isValid()) {
916 lives_free(private_response);
917 }
918 }
919 return loopMode();
920 }
921
922
loopMode() const923 lives_loop_mode_t player::loopMode() const {
924 unsigned int lmode = LIVES_LOOP_MODE_NONE;
925 if (!isValid()) return (lives_loop_mode_t)lmode;
926
927 if (mainw->loop) lmode |= LIVES_LOOP_MODE_FIT_AUDIO;
928 if (mainw->loop_cont) lmode |= LIVES_LOOP_MODE_CONTINUOUS;
929
930 return (lives_loop_mode_t)lmode;
931 }
932
933
setPingPong(bool setting) const934 bool player::setPingPong(bool setting) const {
935 if (!isValid()) return pingPong();
936 spinning = true;
937 msg_id = lives_random();
938 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
939 pthread_mutex_lock(&cond_mutex);
940 if (!idle_set_ping_pong(setting, msg_id)) {
941 pthread_mutex_unlock(&cond_mutex);
942 spinning = false;
943 m_lives->removeCallback(cbid);
944 } else {
945 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
946 pthread_mutex_unlock(&cond_mutex);
947 if (isValid()) {
948 lives_free(private_response);
949 }
950 }
951 return pingPong();
952 }
953
954
pingPong() const955 bool player::pingPong() const {
956 if (!isValid()) return false;
957 return mainw->ping_pong;
958 }
959
960
resyncFPS() const961 bool player::resyncFPS() const {
962 if (!isPlaying()) return false;
963 spinning = true;
964 msg_id = lives_random();
965 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
966 pthread_mutex_lock(&cond_mutex);
967 if (!idle_resync_fps(msg_id)) {
968 pthread_mutex_unlock(&cond_mutex);
969 spinning = false;
970 m_lives->removeCallback(cbid);
971 } else {
972 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
973 pthread_mutex_unlock(&cond_mutex);
974 if (isValid()) {
975 lives_free(private_response);
976 }
977 }
978 return true;
979 }
980
981
982 //////////////// set ////////////////////
983
set(livesApp * lives)984 set::set(livesApp *lives) {
985 m_lives = lives;
986 }
987
988
isValid() const989 bool set::isValid() const {
990 return m_lives != NULL && m_lives->isValid() && m_lives->status() != LIVES_STATUS_NOTREADY;
991 }
992
993
name() const994 livesString set::name() const {
995 if (!isValid()) return livesString();
996 return livesString(mainw->set_name, LIVES_CHAR_ENCODING_UTF8);
997 }
998
999
setName(livesString name) const1000 bool set::setName(livesString name) const {
1001 if (!isValid()) return false;
1002 if (strlen(mainw->set_name) > 0) return false;
1003 if (numClips() == 0) return false;
1004
1005 if (!name.empty()) {
1006 const char *new_set_name = name.toEncoding(LIVES_CHAR_ENCODING_FILESYSTEM).c_str();
1007 if (is_legal_set_name(new_set_name, TRUE)) {
1008 lives_snprintf(mainw->set_name, 128, "%s", new_set_name);
1009 return true;
1010 }
1011 return false;
1012 }
1013
1014 spinning = true;
1015 msg_id = lives_random();
1016 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1017
1018 pthread_mutex_lock(&cond_mutex);
1019 if (!idle_set_set_name(msg_id)) {
1020 pthread_mutex_unlock(&cond_mutex);
1021 spinning = false;
1022 m_lives->removeCallback(cbid);
1023 return false;
1024 }
1025 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1026 pthread_mutex_unlock(&cond_mutex);
1027 if (isValid()) {
1028 bool ret = (bool)atoi(private_response);
1029 lives_free(private_response);
1030 return ret;
1031 }
1032 return false;
1033 }
1034
1035
numClips() const1036 unsigned int set::numClips() const {
1037 if (!isValid()) return 0;
1038 (const_cast<set *>(this))->update_clip_list();
1039 return m_clips.size();
1040 }
1041
1042
nthClip(unsigned int n) const1043 clip set::nthClip(unsigned int n) const {
1044 if (!isValid()) return clip();
1045 (const_cast<set *>(this))->update_clip_list();
1046 if (n >= m_clips.size()) return clip();
1047 return clip(m_clips[n], m_lives);
1048 }
1049
1050
indexOf(clip c) const1051 int set::indexOf(clip c) const {
1052 if (!isValid()) return -1;
1053 if (!c.isValid()) return -1;
1054 if (m_clips.empty()) return -1;
1055 (const_cast<set *>(this))->update_clip_list();
1056 int i;
1057 for (i = 0; i < (int)m_clips.size(); i++) {
1058 if (m_clips[i] == c.m_uid) return i;
1059 }
1060 return -1;
1061 }
1062
1063
save(livesString name,bool force_append) const1064 bool set::save(livesString name, bool force_append) const {
1065 if (!isValid()) return FALSE;
1066 const char *cname = name.toEncoding(LIVES_CHAR_ENCODING_FILESYSTEM).c_str();
1067
1068 spinning = true;
1069 msg_id = lives_random();
1070
1071 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1072
1073 bool ret = false;
1074
1075 pthread_mutex_lock(&cond_mutex);
1076 if (!idle_save_set(cname, force_append, msg_id)) {
1077 pthread_mutex_unlock(&cond_mutex);
1078 spinning = false;
1079 m_lives->removeCallback(cbid);
1080 } else {
1081 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1082 pthread_mutex_unlock(&cond_mutex);
1083 if (isValid()) {
1084 ret = (bool)(atoi(private_response));
1085 lives_free(private_response);
1086 }
1087 }
1088 return ret;
1089 }
1090
1091
save() const1092 bool set::save() const {
1093 return save(name());
1094 }
1095
1096
update_clip_list()1097 void set::update_clip_list() {
1098 clipListIterator it = m_clips.begin();
1099 while (it != m_clips.end()) {
1100 it = m_clips.erase(it);
1101 }
1102 if (isValid()) {
1103 ulong *ids = get_unique_ids();
1104
1105 for (int i = 0; ids[i] != 0l; i++) {
1106 m_clips.push_back(ids[i]);
1107 }
1108 lives_free(ids);
1109 }
1110 }
1111
1112
1113 /////////////// clip ////////////////
1114
clip()1115 clip::clip() : m_uid(0l), m_lives(NULL) {};
1116
clip(ulong uid,livesApp * lives)1117 clip::clip(ulong uid, livesApp *lives) {
1118 m_uid = uid;
1119 m_lives = lives;
1120 }
1121
isValid() const1122 bool clip::isValid() const {
1123 return (m_lives != NULL && m_lives->isValid() && m_lives->status() != LIVES_STATUS_NOTREADY && cnum_for_uid(m_uid) != -1);
1124 }
1125
frames()1126 int clip::frames() {
1127 if (isValid()) {
1128 int cnum = cnum_for_uid(m_uid);
1129 if (cnum > -1 && mainw->files[cnum] != NULL) return mainw->files[cnum]->frames;
1130 }
1131 return 0;
1132 }
1133
width()1134 int clip::width() {
1135 if (isValid()) {
1136 int cnum = cnum_for_uid(m_uid);
1137 if (cnum > -1 && mainw->files[cnum] != NULL) return mainw->files[cnum]->hsize;
1138 }
1139 return 0;
1140 }
1141
height()1142 int clip::height() {
1143 if (isValid()) {
1144 int cnum = cnum_for_uid(m_uid);
1145 if (cnum > -1 && mainw->files[cnum] != NULL) return mainw->files[cnum]->vsize;
1146 }
1147 return 0;
1148 }
1149
FPS()1150 double clip::FPS() {
1151 if (isValid()) {
1152 int cnum = cnum_for_uid(m_uid);
1153 if (cnum > -1 && mainw->files[cnum] != NULL) return mainw->files[cnum]->fps;
1154 }
1155 return 0.;
1156 }
1157
1158
playbackFPS()1159 double clip::playbackFPS() {
1160 if (isValid()) {
1161 if (!m_lives->m_multitrack->isActive()) {
1162 int cnum = cnum_for_uid(m_uid);
1163 if (cnum > -1 && mainw->files[cnum] != NULL) return mainw->files[cnum]->pb_fps;
1164 } else return m_lives->m_multitrack->FPS();
1165 }
1166 return 0.;
1167 }
1168
1169
audioRate()1170 int clip::audioRate() {
1171 if (isValid()) {
1172 int cnum = cnum_for_uid(m_uid);
1173 if (cnum > -1 && mainw->files[cnum] != NULL) return mainw->files[cnum]->arate;
1174 }
1175 return 0;
1176 }
1177
1178
playbackAudioRate()1179 int clip::playbackAudioRate() {
1180 int arps = -1;
1181 if (isValid()) {
1182 if (!m_lives->m_multitrack->isActive()) {
1183 int cnum = cnum_for_uid(m_uid);
1184 if (cnum > -1 && mainw->files[cnum] != NULL) {
1185 arps = mainw->files[cnum]->arps;
1186 arps *= mainw->files[cnum]->pb_fps / mainw->files[cnum]->fps;
1187 }
1188 } else arps = mainw->files[mainw->multitrack->render_file]->arate;
1189 }
1190 return arps;
1191 }
1192
1193
audioLength()1194 double clip::audioLength() {
1195 if (isValid()) {
1196 int cnum = cnum_for_uid(m_uid);
1197 if (cnum > -1 && mainw->files[cnum] != NULL) return mainw->files[cnum]->laudio_time;
1198 }
1199 return 0.;
1200 }
1201
1202
audioChannels()1203 int clip::audioChannels() {
1204 if (isValid()) {
1205 int cnum = cnum_for_uid(m_uid);
1206 if (cnum > -1 && mainw->files[cnum] != NULL) return mainw->files[cnum]->achans;
1207 }
1208 return 0;
1209 }
1210
1211
audioSampleSize()1212 int clip::audioSampleSize() {
1213 if (isValid()) {
1214 int cnum = cnum_for_uid(m_uid);
1215 if (cnum > -1 && mainw->files[cnum] != NULL) return mainw->files[cnum]->asampsize;
1216 }
1217 return 0;
1218 }
1219
1220
audioSigned()1221 bool clip::audioSigned() {
1222 if (isValid()) {
1223 int cnum = cnum_for_uid(m_uid);
1224 if (cnum > -1 && mainw->files[cnum] != NULL) return !(mainw->files[cnum]->signed_endian & AFORM_UNSIGNED);
1225 }
1226 return true;
1227 }
1228
1229
audioEndian()1230 lives_endian_t clip::audioEndian() {
1231 if (isValid()) {
1232 int cnum = cnum_for_uid(m_uid);
1233 if (cnum > -1 && mainw->files[cnum] != NULL) {
1234 if (mainw->files[cnum]->signed_endian & AFORM_BIG_ENDIAN) return LIVES_BIGENDIAN;
1235 }
1236 }
1237 return LIVES_LITTLEENDIAN;
1238 }
1239
1240
name()1241 livesString clip::name() {
1242 if (isValid()) {
1243 int cnum = cnum_for_uid(m_uid);
1244 if (cnum > -1 &&
1245 mainw->files[cnum] != NULL) return livesString(get_menu_name(mainw->files[cnum], FALSE), LIVES_CHAR_ENCODING_UTF8);
1246 }
1247 return livesString();
1248 }
1249
1250
selectionStart()1251 int clip::selectionStart() {
1252 if (isValid()) {
1253 int cnum = cnum_for_uid(m_uid);
1254 if (cnum > -1 && mainw->files[cnum] != NULL) return mainw->files[cnum]->start;
1255 }
1256 return 0;
1257 }
1258
1259
selectionEnd()1260 int clip::selectionEnd() {
1261 if (isValid()) {
1262 int cnum = cnum_for_uid(m_uid);
1263 if (cnum > -1 && mainw->files[cnum] != NULL) return mainw->files[cnum]->end;
1264 }
1265 return 0;
1266 }
1267
1268
selectAll()1269 bool clip::selectAll() {
1270 if (!isValid() || m_lives->status() == LIVES_STATUS_NOTREADY) return false;
1271 spinning = true;
1272 msg_id = lives_random();
1273 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1274
1275 int cnum = cnum_for_uid(m_uid);
1276 pthread_mutex_lock(&cond_mutex);
1277 if (!idle_select_all(cnum, msg_id)) {
1278 pthread_mutex_unlock(&cond_mutex);
1279 spinning = false;
1280 m_lives->removeCallback(cbid);
1281 return false;
1282 }
1283 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1284 pthread_mutex_unlock(&cond_mutex);
1285 if (isValid()) {
1286 bool ret = (bool)atoi(private_response);
1287 lives_free(private_response);
1288 return ret;
1289 }
1290 return false;
1291 }
1292
1293
setSelectionStart(unsigned int frame)1294 bool clip::setSelectionStart(unsigned int frame) {
1295 if (!isValid()) return false;
1296 spinning = true;
1297 msg_id = lives_random();
1298 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1299
1300 int cnum = cnum_for_uid(m_uid);
1301 pthread_mutex_lock(&cond_mutex);
1302 if (!idle_select_start(cnum, frame, msg_id)) {
1303 pthread_mutex_unlock(&cond_mutex);
1304 spinning = false;
1305 m_lives->removeCallback(cbid);
1306 return false;
1307 }
1308 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1309 pthread_mutex_unlock(&cond_mutex);
1310 if (isValid()) {
1311 bool ret = (bool)atoi(private_response);
1312 lives_free(private_response);
1313 return ret;
1314 }
1315 return false;
1316 }
1317
1318
setSelectionEnd(unsigned int frame)1319 bool clip::setSelectionEnd(unsigned int frame) {
1320 if (!isValid()) return false;
1321 spinning = true;
1322 msg_id = lives_random();
1323 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1324
1325 int cnum = cnum_for_uid(m_uid);
1326 pthread_mutex_lock(&cond_mutex);
1327 if (!idle_select_end(cnum, frame, msg_id)) {
1328 pthread_mutex_unlock(&cond_mutex);
1329 spinning = false;
1330 m_lives->removeCallback(cbid);
1331 return false;
1332 }
1333 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1334 pthread_mutex_unlock(&cond_mutex);
1335 if (isValid()) {
1336 bool ret = (bool)atoi(private_response);
1337 lives_free(private_response);
1338 return ret;
1339 }
1340 return false;
1341 }
1342
1343
switchTo()1344 bool clip::switchTo() {
1345 if (!isValid()) return false;
1346 if (m_lives->m_multitrack->isActive()) return false;
1347 spinning = true;
1348 msg_id = lives_random();
1349 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1350
1351 int cnum = cnum_for_uid(m_uid);
1352 pthread_mutex_lock(&cond_mutex);
1353 if (!idle_switch_clip(1, cnum, msg_id)) {
1354 pthread_mutex_unlock(&cond_mutex);
1355 spinning = false;
1356 m_lives->removeCallback(cbid);
1357 return false;
1358 }
1359 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1360 pthread_mutex_unlock(&cond_mutex);
1361 if (isValid()) {
1362 bool ret = (bool)atoi(private_response);
1363 lives_free(private_response);
1364 return ret;
1365 }
1366 return false;
1367 }
1368
1369
setIsBackground()1370 bool clip::setIsBackground() {
1371 if (!isValid()) return false;
1372 if (m_lives->m_multitrack->isActive()) return false;
1373 spinning = true;
1374 msg_id = lives_random();
1375 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1376
1377 int cnum = cnum_for_uid(m_uid);
1378 pthread_mutex_lock(&cond_mutex);
1379 if (!idle_switch_clip(2, cnum, msg_id)) {
1380 pthread_mutex_unlock(&cond_mutex);
1381 spinning = false;
1382 m_lives->removeCallback(cbid);
1383 return false;
1384 }
1385 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1386 pthread_mutex_unlock(&cond_mutex);
1387 if (isValid()) {
1388 bool ret = (bool)atoi(private_response);
1389 lives_free(private_response);
1390 return ret;
1391 }
1392 return false;
1393 }
1394
1395 //////////////////////////////////////////////
1396
1397 //// effectKeyMap
effectKeyMap(livesApp * lives)1398 effectKeyMap::effectKeyMap(livesApp *lives) {
1399 m_lives = lives;
1400 }
1401
1402
isValid() const1403 bool effectKeyMap::isValid() const {
1404 return m_lives != NULL && m_lives->isValid() && m_lives->status() != LIVES_STATUS_NOTREADY;
1405 }
1406
1407
at(int i) const1408 effectKey effectKeyMap::at(int i) const {
1409 return (*this)[i];
1410 }
1411
1412
size() const1413 size_t effectKeyMap::size() const {
1414 if (!isValid()) return 0;
1415 return (size_t) prefs::rteKeysVirtual(*m_lives);
1416 }
1417
1418
clear() const1419 bool effectKeyMap::clear() const {
1420 if (!isValid()) return false;
1421 spinning = true;
1422 msg_id = lives_random();
1423 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1424
1425 pthread_mutex_lock(&cond_mutex);
1426 if (!idle_unmap_effects(msg_id)) {
1427 pthread_mutex_unlock(&cond_mutex);
1428 spinning = false;
1429 m_lives->removeCallback(cbid);
1430 return false;
1431 }
1432 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1433 pthread_mutex_unlock(&cond_mutex);
1434 if (isValid()) {
1435 bool ret = (bool)atoi(private_response);
1436 lives_free(private_response);
1437 return ret;
1438 }
1439 return false;
1440 }
1441
1442
1443 /////////////////////////////////////////////////
1444
1445 /// effectKey
effectKey()1446 effectKey::effectKey() {
1447 m_key = 0;
1448 }
1449
1450
effectKey(livesApp * lives,int key)1451 effectKey::effectKey(livesApp *lives, int key) {
1452 m_lives = lives;
1453 m_key = key;
1454 }
1455
1456
isValid() const1457 bool effectKey::isValid() const {
1458 return m_lives != NULL && m_lives->isValid() && m_lives->status() != LIVES_STATUS_NOTREADY &&
1459 m_key >= 1 && m_key <= prefs::rteKeysVirtual(*(m_lives));
1460 }
1461
1462
key()1463 int effectKey::key() {
1464 return m_key;
1465 }
1466
1467
numModes()1468 int effectKey::numModes() {
1469 if (!isValid()) return 0;
1470 return ::prefs->max_modes_per_key;
1471 }
1472
1473
numMappedModes()1474 int effectKey::numMappedModes() {
1475 if (!isValid()) return 0;
1476 return get_num_mapped_modes_for_key(m_key);
1477 }
1478
1479
currentMode()1480 int effectKey::currentMode() {
1481 if (!isValid()) return -1;
1482 return get_current_mode_for_key(m_key);
1483 }
1484
1485
enabled()1486 bool effectKey::enabled() {
1487 if (!isValid()) return false;
1488 return get_rte_key_is_enabled(m_key);
1489 }
1490
1491
setCurrentMode(int new_mode)1492 int effectKey::setCurrentMode(int new_mode) {
1493 if (!isValid()) return -1;
1494 if (new_mode < 0 || new_mode >= numMappedModes()) return currentMode();
1495
1496 if (new_mode == currentMode()) return currentMode();
1497
1498 spinning = true;
1499 msg_id = lives_random();
1500 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1501
1502 pthread_mutex_lock(&cond_mutex);
1503 if (!idle_fx_setmode(m_key, new_mode, msg_id)) {
1504 pthread_mutex_unlock(&cond_mutex);
1505 spinning = false;
1506 m_lives->removeCallback(cbid);
1507 return currentMode();
1508 }
1509 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1510 pthread_mutex_unlock(&cond_mutex);
1511 if (isValid()) {
1512 lives_free(private_response);
1513 }
1514 return currentMode();
1515 }
1516
1517
setEnabled(bool setting)1518 bool effectKey::setEnabled(bool setting) {
1519 if (!isValid()) return false;
1520
1521 spinning = true;
1522 msg_id = lives_random();
1523 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1524
1525 pthread_mutex_lock(&cond_mutex);
1526 if (!idle_fx_enable(m_key, setting, msg_id)) {
1527 spinning = false;
1528 m_lives->removeCallback(cbid);
1529 return enabled();
1530 }
1531 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1532 pthread_mutex_unlock(&cond_mutex);
1533 if (isValid()) {
1534 lives_free(private_response);
1535
1536 // TODO: if it was a generator, wait for playing or error
1537
1538 }
1539 return enabled();
1540 }
1541
1542
appendMapping(effect fx)1543 int effectKey::appendMapping(effect fx) {
1544 if (!isValid()) return -1;
1545
1546 if (!fx.isValid()) return -1;
1547
1548 if (fx.m_lives != m_lives) return -1;
1549
1550 if (!m_lives->isReady() && !m_lives->isPlaying()) return -1;
1551
1552 int mode = numMappedModes();
1553 if (mode == numModes()) return -1;
1554
1555 spinning = true;
1556 msg_id = lives_random();
1557 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1558
1559 pthread_mutex_lock(&cond_mutex);
1560 if (!idle_map_fx(m_key, mode, fx.m_idx, msg_id)) {
1561 pthread_mutex_unlock(&cond_mutex);
1562 spinning = false;
1563 m_lives->removeCallback(cbid);
1564 return -1;
1565 }
1566 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1567 pthread_mutex_unlock(&cond_mutex);
1568 if (isValid()) {
1569 bool ret = (bool)atoi(private_response);
1570 lives_free(private_response);
1571 if (ret) return mode;
1572 }
1573 return -1;
1574 }
1575
1576
removeMapping(int mode)1577 bool effectKey::removeMapping(int mode) {
1578 if (!isValid()) return false;
1579
1580 if (!m_lives->isReady() && !m_lives->isPlaying()) return false;
1581
1582 if (mode >= numMappedModes()) return false;
1583
1584 spinning = true;
1585 msg_id = lives_random();
1586 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1587
1588 pthread_mutex_lock(&cond_mutex);
1589 if (!idle_unmap_fx(m_key, mode, msg_id)) {
1590 pthread_mutex_unlock(&cond_mutex);
1591 spinning = false;
1592 m_lives->removeCallback(cbid);
1593 return false;
1594 }
1595 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1596 pthread_mutex_unlock(&cond_mutex);
1597 if (isValid()) {
1598 bool ret = (bool)atoi(private_response);
1599 lives_free(private_response);
1600 return ret;
1601 }
1602 return false;
1603 }
1604
1605
at(int mode)1606 effect effectKey::at(int mode) {
1607 effect e;
1608 if (!isValid()) return e;
1609 int idx = rte_keymode_get_filter_idx(m_key, mode);
1610 if (idx == -1) return e;
1611 e = effect(m_lives, idx);
1612 return e;
1613 }
1614
1615
1616 ////////////////////////////////////////////////////////
1617
effect(const livesApp & lives,livesString hashname,bool match_full)1618 effect::effect(const livesApp &lives, livesString hashname, bool match_full) {
1619 m_idx = -1;
1620 m_lives = (livesApp *)&lives;
1621 if (m_lives != NULL && m_lives->isValid() && m_lives->status() != LIVES_STATUS_NOTREADY) {
1622 m_idx = weed_get_idx_for_hashname(hashname.toEncoding(LIVES_CHAR_ENCODING_UTF8).c_str(), match_full);
1623 }
1624 }
1625
effect(const livesApp & lives,livesString package,livesString fxname,livesString author,int version)1626 effect::effect(const livesApp &lives, livesString package, livesString fxname, livesString author, int version) {
1627 m_idx = -1;
1628 m_lives = (livesApp *)&lives;
1629 if (m_lives != NULL && m_lives->isValid() && m_lives->status() != LIVES_STATUS_NOTREADY) {
1630 m_idx = get_first_fx_matched(package.toEncoding(LIVES_CHAR_ENCODING_UTF8).c_str(),
1631 fxname.toEncoding(LIVES_CHAR_ENCODING_UTF8).c_str(),
1632 author.toEncoding(LIVES_CHAR_ENCODING_UTF8).c_str(),
1633 version);
1634 }
1635 }
1636
isValid() const1637 bool effect::isValid() const {
1638 return (m_idx != -1 && m_lives != NULL && m_lives->isValid() && m_lives->status() != LIVES_STATUS_NOTREADY);
1639 }
1640
1641
effect()1642 effect::effect() : m_lives(NULL), m_idx(-1) {}
1643
effect(livesApp * lives,int idx)1644 effect::effect(livesApp *lives, int idx) : m_lives(lives), m_idx(idx) {}
1645
1646
1647 ///////////////////////////////
1648 //// block
1649
block(multitrack * m,ulong uid)1650 block::block(multitrack *m, ulong uid) : m_uid(uid) {
1651 if (m == NULL) m_lives = NULL;
1652 else m_lives = m->m_lives;
1653 }
1654
1655
block(multitrack m,int track,double time)1656 block::block(multitrack m, int track, double time) {
1657 m_lives = m.m_lives;
1658 if (!m.isActive()) m_uid = 0l;
1659 else {
1660 track_rect *tr = get_block_from_track_and_time(mainw->multitrack, track, time);
1661 if (tr == NULL) m_uid = 0l;
1662 else m_uid = tr->uid;
1663 }
1664 }
1665
1666
isValid() const1667 bool block::isValid() const {
1668 if (m_lives == NULL || !m_lives->isValid() || !m_lives->m_multitrack->isActive() || m_uid == 0l ||
1669 find_block_by_uid(mainw->multitrack, m_uid) == NULL) return false;
1670 return true;
1671 }
1672
1673
invalidate()1674 void block::invalidate() {
1675 m_uid = 0l;
1676 }
1677
1678
startTime()1679 double block::startTime() {
1680 track_rect *tr = find_block_by_uid(mainw->multitrack, m_uid);
1681 if (tr == NULL) return -1.;
1682 return (double)get_event_timecode(tr->start_event) / TICKS_PER_SECOND_DBL;
1683 }
1684
1685
length()1686 double block::length() {
1687 track_rect *tr = find_block_by_uid(mainw->multitrack, m_uid);
1688 if (tr == NULL) return -1.;
1689 return (double)get_event_timecode(tr->end_event) / TICKS_PER_SECOND_DBL + 1. / mainw->multitrack->fps -
1690 (double)get_event_timecode(tr->start_event) / TICKS_PER_SECOND_DBL;
1691 }
1692
1693
clipSource()1694 clip block::clipSource() {
1695 track_rect *tr = find_block_by_uid(mainw->multitrack, m_uid);
1696 if (tr == NULL) return clip();
1697 int cnum = get_clip_for_block(tr);
1698 if (cnum == -1) return clip();
1699 return clip(mainw->files[cnum]->unique_id, m_lives);
1700 }
1701
1702
track()1703 int block::track() {
1704 track_rect *tr = find_block_by_uid(mainw->multitrack, m_uid);
1705 if (tr == NULL) return 0;
1706 return get_track_for_block(tr);
1707 }
1708
1709
remove()1710 bool block::remove() {
1711 if (!isValid()) return false;
1712 if (!m_lives->isReady()) return false;
1713
1714 track_rect *tr = find_block_by_uid(mainw->multitrack, m_uid);
1715 if (tr == NULL) return false;
1716
1717 spinning = true;
1718 msg_id = lives_random();
1719 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1720 pthread_mutex_lock(&cond_mutex);
1721 if (!idle_remove_block(m_uid, msg_id)) {
1722 pthread_mutex_unlock(&cond_mutex);
1723 spinning = false;
1724 m_lives->removeCallback(cbid);
1725 return false;
1726 }
1727 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1728 pthread_mutex_unlock(&cond_mutex);
1729 if (isValid()) {
1730 bool ret = (bool)atoi(private_response);
1731 lives_free(private_response);
1732 if (ret) invalidate();
1733 return ret;
1734 }
1735 return false;
1736 }
1737
1738
moveTo(int track,double time)1739 bool block::moveTo(int track, double time) {
1740 if (!isValid()) return false;
1741 if (!m_lives->isReady()) return false;
1742
1743 track_rect *tr = find_block_by_uid(mainw->multitrack, m_uid);
1744 if (tr == NULL) return false;
1745
1746 spinning = true;
1747 msg_id = lives_random();
1748 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1749 pthread_mutex_lock(&cond_mutex);
1750 if (!idle_move_block(m_uid, track, time, msg_id)) {
1751 pthread_mutex_unlock(&cond_mutex);
1752 spinning = false;
1753 m_lives->removeCallback(cbid);
1754 return false;
1755 }
1756 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1757 pthread_mutex_unlock(&cond_mutex);
1758 if (isValid()) {
1759 bool ret = (bool)atoi(private_response);
1760 lives_free(private_response);
1761 if (ret) invalidate();
1762 return ret;
1763 }
1764 return false;
1765 }
1766
1767
1768 ///////////////////////////////////////////////////////////////////
1769 /// multitrack
1770
multitrack(livesApp * lives)1771 multitrack::multitrack(livesApp *lives) {
1772 m_lives = lives;
1773 }
1774
1775
isValid() const1776 bool multitrack::isValid() const {
1777 return m_lives != NULL && m_lives->m_id != 0l && m_lives->status() != LIVES_STATUS_NOTREADY;
1778 }
1779
1780
isActive() const1781 bool multitrack::isActive() const {
1782 return (isValid() && mainw->multitrack != NULL);
1783 }
1784
1785
currentTime() const1786 double multitrack::currentTime() const {
1787 if (!isActive()) return 0.;
1788 return m_lives->m_player->videoPlaybackTime();
1789 }
1790
1791
setCurrentTime(double time) const1792 double multitrack::setCurrentTime(double time) const {
1793 if (!isActive() || !m_lives->isReady()) return currentTime();
1794 return m_lives->m_player->setPlaybackStartTime(time);
1795 }
1796
1797
insertBlock(clip c,bool ign_sel,bool without_audio) const1798 block multitrack::insertBlock(clip c, bool ign_sel, bool without_audio) const {
1799 if (!isActive()) return block();
1800 if (!c.isValid()) return block();
1801 if (!m_lives->isReady()) return block();
1802
1803 int clipno = cnum_for_uid(c.m_uid);
1804
1805 spinning = true;
1806 msg_id = lives_random();
1807 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1808 pthread_mutex_lock(&cond_mutex);
1809 if (!idle_insert_block(clipno, ign_sel, !without_audio, msg_id)) {
1810 pthread_mutex_unlock(&cond_mutex);
1811 spinning = false;
1812 m_lives->removeCallback(cbid);
1813 return block();
1814 }
1815 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1816 pthread_mutex_unlock(&cond_mutex);
1817 if (isValid()) {
1818 ulong uid = strtoul(private_response, NULL, 10);
1819 lives_free(private_response);
1820 return block(const_cast<multitrack *>(this), uid);
1821 }
1822 return block();
1823 }
1824
1825
wipeLayout(bool force) const1826 livesString multitrack::wipeLayout(bool force) const {
1827 livesString emptystr;
1828 if (!isActive()) return emptystr;
1829 if (!m_lives->isReady()) return emptystr;
1830
1831 spinning = true;
1832 msg_id = lives_random();
1833 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1834
1835 pthread_mutex_lock(&cond_mutex);
1836 if (!idle_wipe_layout(force, msg_id)) {
1837 pthread_mutex_unlock(&cond_mutex);
1838 spinning = false;
1839 m_lives->removeCallback(cbid);
1840 return emptystr;
1841 }
1842 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1843 pthread_mutex_unlock(&cond_mutex);
1844 if (isValid()) {
1845 livesString str(private_response, LIVES_CHAR_ENCODING_UTF8);
1846 lives_free(private_response);
1847 return str;
1848 }
1849 return emptystr;
1850 }
1851
1852
chooseLayout() const1853 livesString multitrack::chooseLayout() const {
1854 livesString emptystr;
1855 if (!isActive()) return emptystr;
1856 if (!m_lives->isReady()) return emptystr;
1857
1858 spinning = true;
1859 msg_id = lives_random();
1860 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1861
1862 pthread_mutex_lock(&cond_mutex);
1863 if (!idle_choose_layout(msg_id)) {
1864 pthread_mutex_unlock(&cond_mutex);
1865 spinning = false;
1866 m_lives->removeCallback(cbid);
1867 return emptystr;
1868 }
1869 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1870 pthread_mutex_unlock(&cond_mutex);
1871 if (isValid()) {
1872 livesString str(private_response, LIVES_CHAR_ENCODING_UTF8);
1873 lives_free(private_response);
1874 return str;
1875 }
1876 return emptystr;
1877 }
1878
1879
availableLayouts() const1880 livesStringList multitrack::availableLayouts() const {
1881 livesStringList list;
1882 if (!isValid()) return list;
1883 LiVESList *layoutlist = mainw->current_layouts_map;
1884 while (layoutlist != NULL) {
1885 char *data = repl_workdir((const char *)layoutlist->data, FALSE);
1886 list.push_back(livesString(data, LIVES_CHAR_ENCODING_FILESYSTEM).toEncoding(LIVES_CHAR_ENCODING_UTF8));
1887 lives_free(data);
1888 layoutlist = layoutlist->next;
1889 }
1890 return list;
1891 }
1892
1893
reloadLayout(livesString layoutname) const1894 bool multitrack::reloadLayout(livesString layoutname) const {
1895 if (!isActive()) return false;
1896 if (!m_lives->isReady()) return false;
1897
1898 spinning = true;
1899 msg_id = lives_random();
1900 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1901
1902 pthread_mutex_lock(&cond_mutex);
1903 if (!idle_reload_layout(layoutname.toEncoding(LIVES_CHAR_ENCODING_FILESYSTEM).c_str(), msg_id)) {
1904 pthread_mutex_unlock(&cond_mutex);
1905 spinning = false;
1906 m_lives->removeCallback(cbid);
1907 return false;
1908 }
1909 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1910 pthread_mutex_unlock(&cond_mutex);
1911 if (isValid()) {
1912 bool ret = (bool)atoi(private_response);
1913 lives_free(private_response);
1914 return ret;
1915 }
1916 return false;
1917 }
1918
1919
saveLayout(livesString name) const1920 livesString multitrack::saveLayout(livesString name) const {
1921 livesString emptystr;
1922 if (!isActive()) return emptystr;
1923 if (!m_lives->isReady()) return emptystr;
1924
1925 spinning = true;
1926 msg_id = lives_random();
1927 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1928
1929 pthread_mutex_lock(&cond_mutex);
1930 if (!idle_save_layout(name.toEncoding(LIVES_CHAR_ENCODING_FILESYSTEM).c_str(), msg_id)) {
1931 pthread_mutex_unlock(&cond_mutex);
1932 spinning = false;
1933 m_lives->removeCallback(cbid);
1934 return emptystr;
1935 }
1936 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1937 pthread_mutex_unlock(&cond_mutex);
1938 if (isValid()) {
1939 char *lname = strdup(private_response);
1940 lives_free(private_response);
1941 return livesString(lname).toEncoding(LIVES_CHAR_ENCODING_UTF8);
1942 }
1943 return emptystr;
1944 }
1945
1946
saveLayout() const1947 livesString multitrack::saveLayout() const {
1948 livesString emptystr;
1949 if (!isActive()) return emptystr;
1950 if (!m_lives->isReady()) return emptystr;
1951
1952 spinning = true;
1953 msg_id = lives_random();
1954 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1955
1956 pthread_mutex_lock(&cond_mutex);
1957 if (!idle_save_layout(NULL, msg_id)) {
1958 pthread_mutex_unlock(&cond_mutex);
1959 spinning = false;
1960 m_lives->removeCallback(cbid);
1961 return emptystr;
1962 }
1963 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1964 pthread_mutex_unlock(&cond_mutex);
1965 if (isValid()) {
1966 char *lname = strdup(private_response);
1967 lives_free(private_response);
1968 return livesString(lname).toEncoding(LIVES_CHAR_ENCODING_UTF8);
1969 }
1970 return emptystr;
1971 }
1972
1973
render(bool with_audio,bool normalise_audio) const1974 clip multitrack::render(bool with_audio, bool normalise_audio) const {
1975 clip c;
1976 if (!isActive()) return c;
1977 if (!m_lives->isReady()) return c;
1978
1979 spinning = true;
1980 msg_id = lives_random();
1981 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1982
1983 pthread_mutex_lock(&cond_mutex);
1984 if (!idle_render_layout(with_audio, normalise_audio, msg_id)) {
1985 pthread_mutex_unlock(&cond_mutex);
1986 spinning = false;
1987 m_lives->removeCallback(cbid);
1988 return c;
1989 }
1990 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1991 pthread_mutex_unlock(&cond_mutex);
1992 if (isValid()) {
1993 ulong uid = strtoul(private_response, NULL, 10);
1994 c = clip(uid, m_lives);
1995 lives_free(private_response);
1996 }
1997 return c;
1998 }
1999
2000
autoTransition() const2001 effect multitrack::autoTransition() const {
2002 effect e;
2003 if (!m_lives->isValid() || m_lives->status() == LIVES_STATUS_NOTREADY) return e;
2004 if (::prefs->atrans_fx == -1) return e;
2005 e = effect(m_lives, ::prefs->atrans_fx);
2006 return e;
2007 }
2008
2009
setAutoTransition(effect autotrans) const2010 bool multitrack::setAutoTransition(effect autotrans) const {
2011 if (!m_lives->isValid()) return false;
2012 if (!autotrans.isValid()) return disableAutoTransition();
2013
2014 // check if is transition
2015 if (get_transition_param(get_weed_filter(autotrans.m_idx), FALSE) == -1) return false;
2016
2017 if (m_lives->status() != LIVES_STATUS_READY && m_lives->status() != LIVES_STATUS_PLAYING) return false;
2018 mt_set_autotrans(autotrans.m_idx);
2019 return true;
2020 }
2021
2022
disableAutoTransition() const2023 bool multitrack::disableAutoTransition() const {
2024 if (!m_lives->isValid()) return false;
2025 if (m_lives->status() != LIVES_STATUS_READY && m_lives->status() != LIVES_STATUS_PLAYING) return false;
2026 mt_set_autotrans(-1);
2027 return true;
2028 }
2029
2030
currentTrack() const2031 int multitrack::currentTrack() const {
2032 if (!isActive()) return 0;
2033 return mainw->multitrack->current_track;
2034 }
2035
2036
setCurrentTrack(int track) const2037 bool multitrack::setCurrentTrack(int track) const {
2038 if (m_lives->status() == LIVES_STATUS_PROCESSING) return false;
2039 if (!isActive()) return false;
2040
2041 spinning = true;
2042 msg_id = lives_random();
2043 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
2044 pthread_mutex_lock(&cond_mutex);
2045 if (!idle_mt_set_track(track, msg_id)) {
2046 pthread_mutex_unlock(&cond_mutex);
2047 spinning = false;
2048 m_lives->removeCallback(cbid);
2049 } else {
2050 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
2051 pthread_mutex_unlock(&cond_mutex);
2052 if (isValid()) {
2053 bool ret = (bool)(atoi(private_response));
2054 lives_free(private_response);
2055 return ret;
2056 }
2057 }
2058 return false;
2059 }
2060
2061
trackLabel(int track) const2062 livesString multitrack::trackLabel(int track) const {
2063 livesString emptystr;
2064 if (!isActive()) return emptystr;
2065
2066 if (mt_track_is_video(mainw->multitrack, track))
2067 return livesString(get_track_name(mainw->multitrack, track, FALSE), LIVES_CHAR_ENCODING_UTF8);
2068 if (mt_track_is_audio(mainw->multitrack, track))
2069 return livesString(get_track_name(mainw->multitrack, track, TRUE), LIVES_CHAR_ENCODING_UTF8);
2070
2071 return emptystr;
2072 }
2073
2074
FPS() const2075 double multitrack::FPS() const {
2076 if (!isActive()) return 0.;
2077 return mainw->multitrack->fps;
2078 }
2079
2080
setTrackLabel(int track,livesString label) const2081 bool multitrack::setTrackLabel(int track, livesString label) const {
2082 if (m_lives->status() == LIVES_STATUS_PROCESSING) return false;
2083 if (!isActive()) return false;
2084
2085 spinning = true;
2086 msg_id = lives_random();
2087 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
2088 pthread_mutex_lock(&cond_mutex);
2089 if (!idle_set_track_label(track, label.toEncoding(LIVES_CHAR_ENCODING_UTF8).c_str(), msg_id)) {
2090 pthread_mutex_unlock(&cond_mutex);
2091 spinning = false;
2092 m_lives->removeCallback(cbid);
2093 } else {
2094 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
2095 pthread_mutex_unlock(&cond_mutex);
2096 if (isValid()) {
2097 bool ret = (bool)(atoi(private_response));
2098 lives_free(private_response);
2099 return ret;
2100 }
2101 }
2102 return false;
2103 }
2104
2105
gravity() const2106 lives_gravity_t multitrack::gravity() const {
2107 if (!isActive()) return LIVES_GRAVITY_NORMAL;
2108 switch (mainw->multitrack->opts.grav_mode) {
2109 case GRAV_MODE_LEFT:
2110 return LIVES_GRAVITY_LEFT;
2111 case GRAV_MODE_RIGHT:
2112 return LIVES_GRAVITY_RIGHT;
2113 default:
2114 return LIVES_GRAVITY_NORMAL;
2115 }
2116 }
2117
2118
setGravity(lives_gravity_t grav) const2119 lives_gravity_t multitrack::setGravity(lives_gravity_t grav) const {
2120 if (m_lives->status() == LIVES_STATUS_PROCESSING) return gravity();
2121 if (!isActive()) return gravity();
2122
2123 spinning = true;
2124 msg_id = lives_random();
2125 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
2126 pthread_mutex_lock(&cond_mutex);
2127 if (!idle_set_gravity((int)grav, msg_id)) {
2128 pthread_mutex_unlock(&cond_mutex);
2129 spinning = false;
2130 m_lives->removeCallback(cbid);
2131 } else {
2132 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
2133 pthread_mutex_unlock(&cond_mutex);
2134 if (isValid()) {
2135 lives_free(private_response);
2136 }
2137 }
2138 return gravity();
2139 }
2140
2141
insertMode() const2142 lives_insert_mode_t multitrack::insertMode() const {
2143 if (!isActive()) return LIVES_INSERT_MODE_NORMAL;
2144 switch (mainw->multitrack->opts.insert_mode) {
2145 default:
2146 return LIVES_INSERT_MODE_NORMAL;
2147 }
2148 }
2149
2150
setInsertMode(lives_insert_mode_t mode) const2151 lives_insert_mode_t multitrack::setInsertMode(lives_insert_mode_t mode) const {
2152 if (m_lives->status() == LIVES_STATUS_PROCESSING) return insertMode();
2153 if (!isActive()) return insertMode();
2154
2155 spinning = true;
2156 msg_id = lives_random();
2157 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
2158 pthread_mutex_lock(&cond_mutex);
2159 if (!idle_set_insert_mode((int)mode, msg_id)) {
2160 pthread_mutex_unlock(&cond_mutex);
2161 spinning = false;
2162 m_lives->removeCallback(cbid);
2163 } else {
2164 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
2165 pthread_mutex_unlock(&cond_mutex);
2166 if (isValid()) {
2167 lives_free(private_response);
2168 }
2169 }
2170 return insertMode();
2171 }
2172
2173
numAudioTracks() const2174 int multitrack::numAudioTracks() const {
2175 if (!isActive()) return 0;
2176 return mainw->multitrack->opts.back_audio_tracks;
2177 }
2178
2179
numVideoTracks() const2180 int multitrack::numVideoTracks() const {
2181 if (!isActive()) return 0;
2182 return mainw->multitrack->num_video_tracks;
2183 }
2184
2185
addVideoTrack(bool in_front) const2186 int multitrack::addVideoTrack(bool in_front) const {
2187 if (!isActive()) return -1;
2188 if (m_lives->isReady()) return -1.;
2189
2190 spinning = true;
2191 msg_id = lives_random();
2192 ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
2193 pthread_mutex_lock(&cond_mutex);
2194 if (!idle_insert_vtrack(in_front, msg_id)) {
2195 pthread_mutex_unlock(&cond_mutex);
2196 spinning = false;
2197 m_lives->removeCallback(cbid);
2198 } else {
2199 while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
2200 pthread_mutex_unlock(&cond_mutex);
2201 if (isValid()) {
2202 int tnum = atoi(private_response);
2203 lives_free(private_response);
2204 return tnum;
2205 }
2206 }
2207 return -1;
2208 }
2209
2210
2211 //////////////////////////////////////////////
2212
2213 ////// prefs
2214
2215 namespace prefs {
currentVideoLoadDir(const livesApp & lives)2216 livesString currentVideoLoadDir(const livesApp &lives) {
2217 if (!lives.isValid() || lives.status() == LIVES_STATUS_NOTREADY) return livesString();
2218 return livesString(mainw->vid_load_dir, LIVES_CHAR_ENCODING_UTF8);
2219 }
2220
currentAudioDir(const livesApp & lives)2221 livesString currentAudioDir(const livesApp &lives) {
2222 if (!lives.isValid() || lives.status() == LIVES_STATUS_NOTREADY) return livesString();
2223 return livesString(mainw->audio_dir, LIVES_CHAR_ENCODING_UTF8);
2224 }
2225
tmpDir(const livesApp & lives)2226 livesString tmpDir(const livesApp &lives) {
2227 if (!lives.isValid() || lives.status() == LIVES_STATUS_NOTREADY) return livesString();
2228 return livesString(::prefs->workdir, LIVES_CHAR_ENCODING_FILESYSTEM);
2229 }
2230
audioSource(const livesApp & lives)2231 lives_audio_source_t audioSource(const livesApp &lives) {
2232 if (!lives.isValid() || lives.status() == LIVES_STATUS_NOTREADY) return LIVES_AUDIO_SOURCE_UNKNOWN;
2233 if (::prefs->audio_src == AUDIO_SRC_EXT) return LIVES_AUDIO_SOURCE_EXTERNAL;
2234 return LIVES_AUDIO_SOURCE_INTERNAL;
2235 }
2236
setAudioSource(const livesApp & lives,lives_audio_source_t asrc)2237 bool setAudioSource(const livesApp &lives, lives_audio_source_t asrc) {
2238 if (!lives.isReady()) return false;
2239 return lives.setPref(PREF_REC_EXT_AUDIO, (bool)(asrc == LIVES_AUDIO_SOURCE_EXTERNAL));
2240 }
2241
audioPlayer(const livesApp & lives)2242 lives_audio_player_t audioPlayer(const livesApp &lives) {
2243 if (!lives.isValid() || lives.status() == LIVES_STATUS_NOTREADY) return LIVES_AUDIO_PLAYER_UNKNOWN;
2244 if (::prefs->audio_player == AUD_PLAYER_SOX) return LIVES_AUDIO_PLAYER_SOX;
2245 if (::prefs->audio_player == AUD_PLAYER_JACK) return LIVES_AUDIO_PLAYER_JACK;
2246 if (::prefs->audio_player == AUD_PLAYER_PULSE) return LIVES_AUDIO_PLAYER_PULSE;
2247 return LIVES_AUDIO_PLAYER_UNKNOWN;
2248 }
2249
audioPlayerRate(const livesApp & lives)2250 int audioPlayerRate(const livesApp &lives) {
2251 if (!lives.isValid() || lives.status() == LIVES_STATUS_NOTREADY) return 0;
2252 #ifdef ENABLE_JACK
2253 if (::prefs->audio_player == AUD_PLAYER_JACK && mainw->jackd != NULL) return mainw->jackd->sample_out_rate;
2254 #endif
2255 #ifdef HAVE_PULSE_AUDIO
2256 if (::prefs->audio_player == AUD_PLAYER_PULSE && mainw->pulsed != NULL) return mainw->pulsed->out_arate;
2257 #endif
2258 return 0;
2259 }
2260
isRealtimeAudioPlayer(lives_audio_player_t player_type)2261 bool isRealtimeAudioPlayer(lives_audio_player_t player_type) {
2262 int ptype = AUD_PLAYER_NONE;
2263 if (player_type == LIVES_AUDIO_PLAYER_SOX) ptype = AUD_PLAYER_SOX;
2264 else if (player_type == LIVES_AUDIO_PLAYER_JACK) ptype = AUD_PLAYER_JACK;
2265 else if (player_type == LIVES_AUDIO_PLAYER_PULSE) ptype = AUD_PLAYER_PULSE;
2266 return is_realtime_aplayer(ptype);
2267 }
2268
rteKeysVirtual(const livesApp & lives)2269 int rteKeysVirtual(const livesApp &lives) {
2270 if (!lives.isValid() || lives.status() == LIVES_STATUS_NOTREADY) return 0;
2271 return ::prefs->rte_keys_virtual;
2272 }
2273
maxFPS(const livesApp & lives)2274 double maxFPS(const livesApp &lives) {
2275 if (!lives.isValid() || lives.status() == LIVES_STATUS_NOTREADY) return 0.;
2276 return FPS_MAX;
2277 }
2278
audioFollowsVideoChanges(const livesApp & lives)2279 bool audioFollowsVideoChanges(const livesApp &lives) {
2280 if (!lives.isValid() || lives.status() == LIVES_STATUS_NOTREADY) return false;
2281 return ::prefs->audio_opts & AUDIO_OPTS_FOLLOW_CLIPS;
2282 }
2283
audioFollowsFPSChanges(const livesApp & lives)2284 bool audioFollowsFPSChanges(const livesApp &lives) {
2285 if (!lives.isValid() || lives.status() == LIVES_STATUS_NOTREADY) return false;
2286 return ::prefs->audio_opts & AUDIO_OPTS_FOLLOW_FPS;
2287 }
2288
setAudioFollowsVideoChanges(const livesApp & lives,bool setting)2289 bool setAudioFollowsVideoChanges(const livesApp &lives, bool setting) {
2290 if (!lives.isValid() || lives.status() == LIVES_STATUS_NOTREADY) return false;
2291 return lives.setPref(PREF_AUDIO_OPTS, AUDIO_OPTS_FOLLOW_CLIPS, setting);
2292 }
2293
setAudioFollowsFPSChanges(const livesApp & lives,bool setting)2294 bool setAudioFollowsFPSChanges(const livesApp &lives, bool setting) {
2295 if (!lives.isValid() || lives.status() == LIVES_STATUS_NOTREADY) return false;
2296 return lives.setPref(PREF_AUDIO_OPTS, AUDIO_OPTS_FOLLOW_FPS, setting);
2297 }
2298
sepWinSticky(const livesApp & lives)2299 bool sepWinSticky(const livesApp &lives) {
2300 if (!lives.isValid() || lives.status() == LIVES_STATUS_NOTREADY) return false;
2301 return ::prefs->sepwin_type == SEPWIN_TYPE_STICKY;
2302 }
2303
setSepWinSticky(const livesApp & lives,bool setting)2304 bool setSepWinSticky(const livesApp &lives, bool setting) {
2305 if (!lives.isValid() || lives.status() == LIVES_STATUS_NOTREADY) return false;
2306 return lives.setPref(PREF_SEPWIN_TYPE, setting ? SEPWIN_TYPE_STICKY : SEPWIN_TYPE_NON_STICKY);
2307 }
2308
mtExitRender(const livesApp & lives)2309 bool mtExitRender(const livesApp &lives) {
2310 if (!lives.isValid() || lives.status() == LIVES_STATUS_NOTREADY) return false;
2311 return ::prefs->mt_exit_render;
2312 }
2313
setMtExitRender(const livesApp & lives,bool setting)2314 bool setMtExitRender(const livesApp &lives, bool setting) {
2315 if (!lives.isValid() || lives.status() == LIVES_STATUS_NOTREADY) return false;
2316 return lives.setPref(PREF_MT_EXIT_RENDER, setting);
2317 }
2318 }
2319 }
2320
2321
2322 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2323
2324 #ifndef DOXYGEN_SKIP
2325
binding_cb(lives_callback_t cb_type,const char * msgstring,ulong id)2326 void binding_cb(lives_callback_t cb_type, const char *msgstring, ulong id) {
2327 bool ret;
2328 lives::livesApp *lapp;
2329
2330 if (cb_type == LIVES_CALLBACK_OBJECT_DESTROYED) lapp = (lives::livesApp *)id;
2331 else lapp = lives::find_instance_for_id(id);
2332
2333 if (lapp == NULL) return;
2334
2335 pthread_mutex_lock(&spin_mutex); // lock mutex so that new callbacks cannot be added yet
2336
2337 lives::closureList cl = lapp->closures();
2338
2339 lives::closureListIterator it = cl.begin();
2340 while (it != cl.end()) {
2341 if ((*it)->cb_type == cb_type) {
2342 switch (cb_type) {
2343 case LIVES_CALLBACK_MODE_CHANGED: {
2344 lives::modeChangedInfo info;
2345 info.mode = (lives_interface_mode_t)atoi(msgstring);
2346 lives::modeChanged_callback_f fn = (lives::modeChanged_callback_f)((*it)->func);
2347 ret = (fn)((*it)->object, &info, (*it)->data);
2348 }
2349 break;
2350 case LIVES_CALLBACK_APP_QUIT: {
2351 // TODO !! test
2352 lives::appQuitInfo info;
2353 info.signum = atoi(msgstring);
2354 lives::appQuit_callback_f fn = (lives::appQuit_callback_f)((*it)->func);
2355 lapp->invalidate();
2356 ret = (fn)((*it)->object, &info, (*it)->data);
2357 spinning = false;
2358 }
2359 break;
2360 case LIVES_CALLBACK_OBJECT_DESTROYED: {
2361 lives::objectDestroyed_callback_f fn = (lives::objectDestroyed_callback_f)((*it)->func);
2362 ret = (fn)((*it)->object, (*it)->data);
2363 }
2364 break;
2365 case LIVES_CALLBACK_PRIVATE: {
2366 // private event type
2367 lives::_privateInfo info;
2368 char *endptr;
2369 info.id = strtoul(msgstring, &endptr, 10);
2370 info.response = endptr + 1;
2371 lives::private_callback_f fn = (lives::private_callback_f)((*it)->func);
2372 ret = (fn)(&info, (*it)->data);
2373 }
2374 break;
2375 default:
2376 ++it;
2377 continue;
2378 }
2379 if (!ret) {
2380 delete *it;
2381 it = cl.erase(it);
2382 lapp->setClosures(cl);
2383 continue;
2384 }
2385 }
2386 ++it;
2387 }
2388
2389 pthread_mutex_unlock(&spin_mutex);
2390 }
2391
2392 #endif // doxygen_skip
2393