1 // ------------------------------------------------------------------------
2 // eca-control-base.cpp: Base class providing basic functionality
3 // for controlling the ecasound library
4 // Copyright (C) 1999-2004,2006,2008,2009,2012 Kai Vehmanen
5 //
6 // Attributes:
7 // eca-style-version: 3 (see Ecasound Programmer's Guide)
8 //
9 // This program is free software; you can redistribute it and/or modify
10 // it under the terms of the GNU General Public License as published by
11 // the Free Software Foundation; either version 2 of the License, or
12 // (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with this program; if not, write to the Free Software
21 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 // ------------------------------------------------------------------------
23
24 #include <string>
25 #include <vector>
26
27 #include <pthread.h>
28 #include <signal.h>
29 #include <unistd.h>
30
31 #include <kvu_dbc.h>
32 #include <kvu_utils.h>
33 #include <kvu_numtostr.h>
34 #include <kvu_message_item.h>
35 #include <kvu_value_queue.h>
36
37 #include "eca-engine.h"
38 #include "eca-session.h"
39 #include "eca-chainsetup.h"
40 #include "eca-resources.h"
41 #include "eca-control.h"
42
43 #include "eca-error.h"
44 #include "eca-logger.h"
45
46 /**
47 * Import namespaces
48 */
49 using std::list;
50 using std::string;
51 using std::vector;
52
53 /**
54 * Definitions for member functions
55 */
56
57 /**
58 * Helper function for starting the slave thread.
59 */
start_normal_thread(void * ptr)60 void* ECA_CONTROL::start_normal_thread(void *ptr)
61 {
62 ECA_CONTROL* ctrl_base = static_cast<ECA_CONTROL*>(ptr);
63 ctrl_base->engine_pid_rep = getpid();
64 DBC_CHECK(ctrl_base->engine_pid_rep >= 0);
65
66 ECA_LOG_MSG(ECA_LOGGER::system_objects, "Engine thread started with pid: " + kvu_numtostr(ctrl_base->engine_pid_rep));
67
68 ctrl_base->run_engine();
69
70 ECA_LOG_MSG(ECA_LOGGER::system_objects,
71 "Engine thread " + kvu_numtostr(ctrl_base->engine_pid_rep) + " will exit.\n");
72 ctrl_base->engine_pid_rep = -1;
73
74 return 0;
75 }
76
77 /**
78 * Initializes the engine
79 *
80 * @pre is_connected() == true
81 * @pre is_engine_created() != true
82 */
engine_start(void)83 void ECA_CONTROL::engine_start(void)
84 {
85 // --------
86 DBC_REQUIRE(is_connected() == true);
87 DBC_REQUIRE(is_engine_created() != true);
88 // --------
89
90 start_engine_sub(false);
91 }
92
93 /**
94 * Start the processing engine
95 *
96 * @pre is_connected() == true
97 * @pre is_running() != true
98 * @post is_engine_created() == true
99 *
100 * @return negative on error, zero on success
101 */
start(void)102 int ECA_CONTROL::start(void)
103 {
104 // --------
105 DBC_REQUIRE(is_connected() == true);
106 DBC_REQUIRE(is_running() != true);
107 // --------
108
109 int result = 0;
110
111 ECA_LOG_MSG(ECA_LOGGER::subsystems, "Controller/Processing started");
112
113 if (is_engine_created() != true) {
114 /* request_batchmode=false */
115 start_engine_sub(false);
116 }
117
118 if (is_engine_created() != true) {
119 ECA_LOG_MSG(ECA_LOGGER::info, "Can't start processing: couldn't start engine.");
120 result = -1;
121 }
122 else {
123 engine_repp->command(ECA_ENGINE::ep_start, 0.0);
124 }
125
126 // --------
127 DBC_ENSURE(result != 0 || is_engine_created() == true);
128 // --------
129
130 return result;
131 }
132
133 /**
134 * Starts the processing engine and blocks until
135 * processing is finished.
136 *
137 * @param batchmode if true, runs until finished/stopped state is reached, and
138 * then returns; if false, will run infinitely
139 *
140 * @pre is_connected() == true
141 * @pre is_running() != true
142 * @post is_finished() == true ||
143 * processing_started == true && is_running() != true ||
144 * processing_started != true &&
145 * (is_engine_created() != true ||
146 * is_engine_created() == true &&
147 * engine_repp->status() != ECA_ENGINE::engine_status_stopped))
148 *
149 * @return negative on error, zero on success
150 */
run(bool batchmode)151 int ECA_CONTROL::run(bool batchmode)
152 {
153 // --------
154 DBC_REQUIRE(is_connected() == true);
155 DBC_REQUIRE(is_running() != true);
156 // --------
157
158 ECA_LOG_MSG(ECA_LOGGER::subsystems, "Controller/Starting batch processing");
159
160 bool processing_started = false;
161 int result = -1;
162
163 if (is_engine_created() != true) {
164 /* request_batchmode=true */
165 start_engine_sub(batchmode);
166 }
167
168 if (is_engine_created() != true) {
169 ECA_LOG_MSG(ECA_LOGGER::info, "Can't start processing: couldn't start the engine. (2)");
170 }
171 else {
172 engine_repp->command(ECA_ENGINE::ep_start, 0.0);
173
174 DBC_CHECK(is_finished() != true);
175
176 result = 0;
177
178 /* run until processing is finished; in batchmode run forever (or
179 * until error occurs) */
180 while(is_finished() != true || batchmode != true) {
181
182 /* sleep for 250ms */
183 kvu_sleep(0, 250000000);
184
185 if (processing_started != true) {
186 if (is_running() == true ||
187 is_finished() == true ||
188 engine_exited_rep.get() == 1) {
189 /* make a note that engine state changed to 'running' */
190 processing_started = true;
191 }
192 else if (is_engine_created() == true) {
193 if (engine_repp->status() == ECA_ENGINE::engine_status_error) {
194 /* not running, so status() is either 'not_ready' or 'error' */
195 ECA_LOG_MSG(ECA_LOGGER::info, "Can't start processing: engine startup failed. (3)");
196 result = -2;
197 break;
198 }
199 /* other valid state alternatives: */
200 DBC_CHECK(engine_repp->status() == ECA_ENGINE::engine_status_stopped ||
201 engine_repp->status() == ECA_ENGINE::engine_status_notready);
202 }
203 else {
204 /* ECA_CONTROL_BASE destructor has been run and
205 * engine_repp is now 0 (--> is_engine_created() != true) */
206 break;
207 }
208 }
209 else {
210 /* engine was started successfully (processing_started == true) */
211 if (is_running() != true) {
212 /* operation successfully completed, exit from run() unless
213 * infinite operation is requested (batchmode) */
214 if (batchmode == true) break;
215 }
216 }
217 }
218 }
219
220 if (last_exec_res_rep < 0) {
221 /* error occured during processing */
222 result = -3;
223 }
224
225 ECA_LOG_MSG(ECA_LOGGER::subsystems,
226 std::string("Controller/Batch processing finished (")
227 + kvu_numtostr(result) + ")");
228
229 // --------
230 DBC_ENSURE(is_finished() == true ||
231 (processing_started == true && is_running()) != true ||
232 (processing_started != true &&
233 (is_engine_created() != true ||
234 (is_engine_created() == true &&
235 engine_repp->status() != ECA_ENGINE::engine_status_stopped))));
236 // --------
237
238 return result;
239 }
240
241 /**
242 * Stops the processing engine.
243 *
244 * @see stop_on_condition()
245 *
246 * @pre is_engine_created() == true
247 * @post is_running() == false
248 */
stop(void)249 void ECA_CONTROL::stop(void)
250 {
251 // --------
252 DBC_REQUIRE(is_engine_created() == true);
253 // --------
254
255 ECA_LOG_MSG(ECA_LOGGER::subsystems, "Controller/Processing stopped");
256 engine_repp->command(ECA_ENGINE::ep_stop, 0.0);
257
258 // --------
259 // ensure:
260 // assert(is_running() == false);
261 // -- there's a small timeout so assertion cannot be checked
262 // --------
263 }
264
265 /**
266 * Stop the processing engine using thread-to-thread condition
267 * signaling.
268 *
269 * @pre is_engine_created() == true
270 * @post is_running() == false
271 */
stop_on_condition(void)272 void ECA_CONTROL::stop_on_condition(void)
273 {
274 // --------
275 DBC_REQUIRE(is_engine_created() == true);
276 // --------
277
278 ECA_LOG_MSG(ECA_LOGGER::subsystems, "Controller/Processing stopped (cond)");
279 engine_repp->command(ECA_ENGINE::ep_stop, 0.0);
280 ECA_LOG_MSG(ECA_LOGGER::system_objects, "Received stop-cond");
281
282 // --
283 // blocks until engine has stopped (or 5 sec has passed);
284 engine_repp->wait_for_stop(5);
285
286 // --------
287 DBC_ENSURE(is_running() == false);
288 // --------
289 }
290
291 /**
292 * Stops the processing engine.
293 * Call will block until engine has terminated.
294 */
quit(void)295 void ECA_CONTROL::quit(void) { close_engine(); }
296
297 /**
298 * Stops the processing engine. A thread-safe variant of
299 * quit(). Call will not block.
300 *
301 */
quit_async(void)302 void ECA_CONTROL::quit_async(void)
303 {
304 if (is_engine_ready_for_commands() != true)
305 return;
306
307 engine_repp->command(ECA_ENGINE::ep_exit, 0.0);
308 }
309
310 /**
311 * Starts the processing engine.
312 *
313 * @pre is_connected() == true
314 * @pre is_engine_ready_for_commands() != true
315 */
start_engine_sub(bool batchmode)316 void ECA_CONTROL::start_engine_sub(bool batchmode)
317 {
318 // --------
319 DBC_REQUIRE(is_connected() == true);
320 DBC_REQUIRE(is_engine_ready_for_commands() != true);
321 // --------
322
323 DBC_CHECK(engine_exited_rep.get() != 1);
324
325 unsigned int p = session_repp->connected_chainsetup_repp->first_selected_chain();
326 if (p < session_repp->connected_chainsetup_repp->chains.size())
327 session_repp->connected_chainsetup_repp->selected_chain_index_rep = p;
328
329 if (engine_repp)
330 close_engine();
331
332 DBC_CHECK(is_engine_created() != true);
333 engine_repp = new ECA_ENGINE (session_repp->connected_chainsetup_repp);
334 DBC_CHECK(is_engine_created() == true);
335
336 /* to relay the batchmode parameter to created new thread */
337 req_batchmode_rep = batchmode;
338
339 pthread_attr_t th_attr;
340 pthread_attr_init(&th_attr);
341 int retcode_rep = pthread_create(&th_cqueue_rep,
342 &th_attr,
343 start_normal_thread,
344 static_cast<void *>(this));
345 if (retcode_rep != 0) {
346 ECA_LOG_MSG(ECA_LOGGER::info, "WARNING: Unable to create a new thread for engine.");
347 ECA_ENGINE *engine_tmp = engine_repp;
348 engine_repp = 0;
349 delete engine_tmp;
350 }
351
352 DBC_ENSURE(is_engine_created() == true);
353 }
354
355 /**
356 * Routine used for launching the engine.
357 */
run_engine(void)358 void ECA_CONTROL::run_engine(void)
359 {
360 last_exec_res_rep = 0;
361 last_exec_res_rep = engine_repp->exec(req_batchmode_rep);
362 engine_exited_rep.set(1);
363 }
364
365 /**
366 * Closes the processing engine.
367 *
368 * ensure:
369 * is_engine_created() != true
370 * is_engine_ready_for_commands() != true
371 */
close_engine(void)372 void ECA_CONTROL::close_engine(void)
373 {
374 if (is_engine_created() != true) return;
375
376 engine_repp->command(ECA_ENGINE::ep_exit, 0.0);
377
378 ECA_LOG_MSG(ECA_LOGGER::system_objects, "Waiting for engine thread to exit.");
379 if (joining_rep != true) {
380 joining_rep = true;
381 int res = pthread_join(th_cqueue_rep,NULL);
382 joining_rep = false;
383 ECA_LOG_MSG(ECA_LOGGER::system_objects,
384 "pthread_join returned: "
385 + kvu_numtostr(res));
386 }
387 else {
388 DBC_CHECK(engine_pid_rep >= 0);
389 int i;
390 for (i = 0; i < 30; i++) {
391
392 if (engine_exited_rep.get() ==1)
393 break;
394
395 /* 100ms sleep */
396 kvu_sleep(0, 100000000);
397 }
398 ECA_LOG_MSG(ECA_LOGGER::info, "WARNING: engine is stuck, sending SIGKILL.");
399 DBC_CHECK(engine_pid_rep >= 0);
400 /* note: we use SIGKILL as SIGTERM, SIGINT et al are blocked and
401 * handled by the watchdog thread */
402 pthread_kill(th_cqueue_rep, SIGKILL);
403 }
404
405 if (engine_exited_rep.get() == 1) {
406 ECA_LOG_MSG(ECA_LOGGER::system_objects, "Engine thread has exited successfully.");
407 delete engine_repp;
408 engine_repp = 0;
409 engine_exited_rep.set(0);
410 }
411 else {
412 ECA_LOG_MSG(ECA_LOGGER::info, "WARNING: Problems while shutting down the engine!");
413 }
414
415 // ---
416 DBC_ENSURE(is_engine_created() != true);
417 DBC_ENSURE(is_engine_ready_for_commands() != true);
418 // ---
419 }
420
421 /**
422 * Is currently selected chainsetup valid?
423 *
424 * @pre is_selected()
425 */
is_valid(void) const426 bool ECA_CONTROL::is_valid(void) const
427 {
428 // --------
429 DBC_REQUIRE(is_selected());
430 // --------
431
432 /* use is_valid_for_connection() instead of is_valid() to
433 * report any detected errors via the logging subsystem */
434 return selected_chainsetup_repp->is_valid_for_connection(true);
435 }
436
437 /**
438 * Returns true if active chainsetup exists and is connected.
439 */
is_connected(void) const440 bool ECA_CONTROL::is_connected(void) const
441 {
442 if (session_repp->connected_chainsetup_repp == 0) {
443 return false;
444 }
445
446 return (session_repp->connected_chainsetup_repp->is_valid() &&
447 session_repp->connected_chainsetup_repp->is_enabled());
448 }
449
450 /**
451 * Returns true if some chainsetup is selected.
452 */
is_selected(void) const453 bool ECA_CONTROL::is_selected(void) const { return selected_chainsetup_repp != 0; }
454
455 /**
456 * Returns true if processing engine is running.
457 */
is_running(void) const458 bool ECA_CONTROL::is_running(void) const { return (is_engine_created() == true && engine_repp->status() == ECA_ENGINE::engine_status_running); }
459
460 /**
461 * Returns true if engine has finished processing. Engine state is
462 * either "finished" or "error".
463 */
is_finished(void) const464 bool ECA_CONTROL::is_finished(void) const
465 {
466 return (is_engine_created() == true &&
467 (engine_repp->status() == ECA_ENGINE::engine_status_finished ||
468 engine_repp->status() == ECA_ENGINE::engine_status_error));
469 }
470
resource_value(const string & key) const471 string ECA_CONTROL::resource_value(const string& key) const
472 {
473 ECA_RESOURCES ecarc;
474 return ecarc.resource(key);
475 }
476
477 /**
478 * Returns the length of the selected chainsetup (in samples).
479 *
480 * @pre is_selected() == true
481 */
length_in_samples(void) const482 SAMPLE_SPECS::sample_pos_t ECA_CONTROL::length_in_samples(void) const
483 {
484 // --------
485 DBC_REQUIRE(is_selected());
486 // --------
487
488 SAMPLE_SPECS::sample_pos_t cslen = 0;
489 if (selected_chainsetup_repp->length_set() == true) {
490 cslen = selected_chainsetup_repp->length_in_samples();
491 }
492 if (selected_chainsetup_repp->max_length_set() == true) {
493 cslen = selected_chainsetup_repp->max_length_in_samples();
494 }
495
496 return cslen;
497 }
498
499 /**
500 * Returns the length of the selected chainsetup (in seconds).
501 *
502 * @pre is_selected() == true
503 */
length_in_seconds_exact(void) const504 double ECA_CONTROL::length_in_seconds_exact(void) const
505 {
506 // --------
507 DBC_REQUIRE(is_selected());
508 // --------
509
510 double cslen = 0.0f;
511 if (selected_chainsetup_repp->length_set() == true) {
512 cslen = selected_chainsetup_repp->length_in_seconds_exact();
513 }
514 if (selected_chainsetup_repp->max_length_set() == true) {
515 cslen = selected_chainsetup_repp->max_length_in_seconds_exact();
516 }
517
518 return cslen;
519 }
520
521 /**
522 * Returns the current position of the selected chainsetup (in samples).
523 *
524 * @pre is_selected() == true
525 */
position_in_samples(void) const526 SAMPLE_SPECS::sample_pos_t ECA_CONTROL::position_in_samples(void) const
527 {
528 // --------
529 DBC_REQUIRE(is_selected());
530 // --------
531
532 return selected_chainsetup_repp->position_in_samples();
533 }
534
535 /**
536 * Returns the current position of the selected chainsetup (in seconds).
537 *
538 * @pre is_selected() == true
539 */
position_in_seconds_exact(void) const540 double ECA_CONTROL::position_in_seconds_exact(void) const
541 {
542 // --------
543 DBC_REQUIRE(is_selected());
544 // --------
545
546 return selected_chainsetup_repp->position_in_seconds_exact();
547 }
548
549 /**
550 * Returns true if engine object has been created.
551 * If true, the engine object is available for use, but
552 * the related engine thread is not necessarily running.
553 *
554 * @see is_engine_ready_for_commands()
555 */
is_engine_created(void) const556 bool ECA_CONTROL::is_engine_created(void) const
557 {
558 return (engine_repp != 0);
559 }
560
561 /**
562 * Returns true if engine is running and ready to receive
563 * control commands via the message queue.
564 *
565 * In practise running means that the engine thread
566 * has been created and it is running the exec() method
567 * of the engine.
568 *
569 * @see is_engine_created()
570 */
is_engine_ready_for_commands(void) const571 bool ECA_CONTROL::is_engine_ready_for_commands(void) const
572 {
573 bool started = is_engine_created();
574
575 if (started != true)
576 return false;
577
578 /* note: has been started, but run_engine() has returned */
579 if (engine_pid_rep < 0)
580 return false;
581
582 DBC_CHECK(engine_repp != 0);
583 if (engine_repp->status() ==
584 ECA_ENGINE::engine_status_notready)
585 return false;
586
587 return true;
588 }
589
590 /**
591 * Return info about engine status.
592 */
engine_status(void) const593 string ECA_CONTROL::engine_status(void) const
594 {
595 if (is_engine_created() == true) {
596 switch(engine_repp->status()) {
597 case ECA_ENGINE::engine_status_running:
598 {
599 return "running";
600 }
601 case ECA_ENGINE::engine_status_stopped:
602 {
603 return "stopped";
604 }
605 case ECA_ENGINE::engine_status_finished:
606 {
607 return "finished";
608 }
609 case ECA_ENGINE::engine_status_error:
610 {
611 return "error";
612 }
613 case ECA_ENGINE::engine_status_notready:
614 {
615 return "not ready";
616 }
617 default:
618 {
619 return "unknown status";
620 }
621 }
622 }
623 return "not started";
624 }
625
set_last_string(const list<string> & s)626 void ECA_CONTROL::set_last_string(const list<string>& s)
627 {
628 string s_rep;
629
630 DBC_CHECK(s_rep.size() == 0);
631 list<string>::const_iterator p = s.begin();
632 while(p != s.end()) {
633 s_rep += *p;
634 ++p;
635 if (p != s.end()) s_rep += "\n";
636 }
637 set_last_string(s_rep);
638 }
639
set_last_string_list(const vector<string> & s)640 void ECA_CONTROL::set_last_string_list(const vector<string>& s)
641 {
642 last_retval_rep.type = eci_return_value::retval_string_list;
643 last_retval_rep.string_list_val = s;
644 }
645
set_last_string(const string & s)646 void ECA_CONTROL::set_last_string(const string& s)
647 {
648 last_retval_rep.type = eci_return_value::retval_string;
649 last_retval_rep.string_val = s;
650 }
651
set_last_float(double v)652 void ECA_CONTROL::set_last_float(double v)
653 {
654 last_retval_rep.type = eci_return_value::retval_float;
655 last_retval_rep.m.float_val = v;
656 }
657
set_last_integer(int v)658 void ECA_CONTROL::set_last_integer(int v)
659 {
660 last_retval_rep.type = eci_return_value::retval_integer;
661 last_retval_rep.m.int_val = v;
662 }
663
set_last_long_integer(long int v)664 void ECA_CONTROL::set_last_long_integer(long int v)
665 {
666 last_retval_rep.type = eci_return_value::retval_long_integer;
667 last_retval_rep.m.long_int_val = v;
668 }
669
set_last_error(const string & s)670 void ECA_CONTROL::set_last_error(const string& s)
671 {
672 last_retval_rep.type = eci_return_value::retval_error;
673 last_retval_rep.string_val = s;
674 }
675
last_error(void) const676 string ECA_CONTROL::last_error(void) const
677 {
678 if (last_retval_rep.type == eci_return_value::retval_error)
679 return last_retval_rep.string_val;
680
681 return string();
682 }
683
clear_last_values(void)684 void ECA_CONTROL::clear_last_values(void)
685 {
686 ECA_CONTROL_MAIN::clear_return_value(&last_retval_rep);
687 }
688
set_float_to_string_precision(int precision)689 void ECA_CONTROL::set_float_to_string_precision(int precision)
690 {
691 float_to_string_precision_rep = precision;
692 }
float_to_string(double n) const693 std::string ECA_CONTROL::float_to_string(double n) const
694 {
695 return kvu_numtostr(n, float_to_string_precision_rep);
696 }
697