1 // ------------------------------------------------------------------------
2 // eca-control-objects.cpp: Class for configuring libecasound objects
3 // Copyright (C) 2000-2004,2006,2008,2009,2012-2014 Kai Vehmanen
4 // Copyright (C) 2005 Stuart Allie
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 <iostream>
25 #include <fstream>
26 #include <string>
27 #include <vector>
28 #include <algorithm>
29 #include <cstdio>
30 #include <cstdlib>
31 
32 #include <kvu_dbc.h> /* DBC_* */
33 #include <kvu_value_queue.h>
34 #include <kvu_temporary_file_directory.h>
35 #include <kvu_numtostr.h>
36 #include <kvu_utils.h> /* kvu_get_argument_number() */
37 
38 #include "audioio.h"
39 #include "eca-chain.h"
40 #include "eca-chainop.h"
41 #include "eca-chainsetup.h"
42 #include "eca-control.h"
43 #include "eca-engine.h"
44 #include "eca-object-factory.h"
45 #include "eca-session.h"
46 #include "generic-controller.h"
47 
48 #include "eca-error.h"
49 #include "eca-logger.h"
50 
51 /**
52  * Import namespaces
53  */
54 using std::string;
55 
56 /**
57  * Definitions for member functions
58  */
59 
60 /**
61  * Adds a new chainsetup
62  *
63  * @param name chainsetup name
64  *
65  * require:
66  *  name != ""
67  *
68  * ensure:
69  *  selected_chainsetup() == name || (last_error().size() > 0 && no_errors != true)
70  */
add_chainsetup(const string & name)71 void ECA_CONTROL::add_chainsetup(const string& name)
72 {
73   // --------
74   DBC_REQUIRE(name != "");
75   // --------
76 
77   bool no_errors = true;
78   int count = static_cast<int>(session_repp->chainsetups_rep.size());
79   session_repp->add_chainsetup(name);
80   if (static_cast<int>(session_repp->chainsetups_rep.size()) > count) {
81     select_chainsetup(name);
82     ECA_LOG_MSG(ECA_LOGGER::info, "Added a new chainsetup with name \"" + name + "\".");
83   }
84   else {
85     set_last_error("Unable to add chainsetup with name \"" + name + "\".");
86     no_errors = false;
87   }
88 
89   // --------
90   DBC_ENSURE(selected_chainsetup() == name || (last_error().size() > 0 && no_errors != true));
91   // --------
92 }
93 
94 /**
95  * Removes chainsetup
96  *
97  * @param name chainsetup name
98  *
99  * require:
100  *  is_selected() == true
101  *  connected_chainsetup() != selected_chainsetup()
102  *
103  * ensure:
104  *  selected_chainsetup.empty() == true
105  */
remove_chainsetup(void)106 void ECA_CONTROL::remove_chainsetup(void)
107 {
108   // --------
109   DBC_REQUIRE(connected_chainsetup() != selected_chainsetup());
110   DBC_REQUIRE(is_selected() == true);
111   // --------
112 
113   ECA_LOG_MSG(ECA_LOGGER::info, "Removing chainsetup:  \"" + selected_chainsetup() + "\".");
114   session_repp->remove_chainsetup();
115   selected_chainsetup_repp = 0;
116 
117   // --------
118   DBC_ENSURE(selected_chainsetup().empty() == true);
119   // --------
120 }
121 
122 /**
123  * Loads chainsetup from file 'filename'.
124  *
125  * @param name chainsetup filename
126  *
127  * require:
128  *  filename != ""
129  *
130  * ensure:
131  *  filename exists implies loaded chainsetup == selected_chainsetup()
132  */
load_chainsetup(const string & filename)133 void ECA_CONTROL::load_chainsetup(const string& filename)
134 {
135   try {
136     session_repp->load_chainsetup(filename);
137     selected_chainsetup_repp = session_repp->selected_chainsetup_repp;
138     DBC_CHECK((selected_chainsetup_repp != 0 &&
139 	       selected_chainsetup_repp->filename() == filename) ||
140 	      selected_chainsetup_repp == 0);
141     ECA_LOG_MSG(ECA_LOGGER::info, "Loaded chainsetup from file \"" + filename + "\".");
142   }
143   catch(ECA_ERROR& e) {
144     set_last_error(e.error_section() + ": \"" + e.error_message() + "\"");
145   }
146 }
147 
148 /**
149  * Save selected chainsetup.
150  *
151  * @param filename chainsetup filename (if omitted, previously used filename will be used, if any)
152  *
153  * require:
154  *  selected_chainsetup().empty() != true
155  */
save_chainsetup(const string & filename)156 void ECA_CONTROL::save_chainsetup(const string& filename)
157 {
158   // --------
159   DBC_REQUIRE(selected_chainsetup().empty() != true);
160   // --------
161 
162   try {
163     if (filename.empty() == true)
164       session_repp->save_chainsetup();
165     else
166       session_repp->save_chainsetup(filename);
167 
168     ECA_LOG_MSG(ECA_LOGGER::info, "Saved selected chainsetup \"" + selected_chainsetup() + "\".");
169   }
170   catch(ECA_ERROR& e) {
171     set_last_error(e.error_section() + ": \"" + e.error_message() + "\"");
172   }
173 }
174 
175 /**
176  * Selects chainsetup
177  *
178  * @param name chainsetup name
179  *
180  * require:
181  *   name != ""
182  *
183  * ensure:
184  *  name == selected_chainsetup() ||
185  *  selected_chainsetup_rep == 0
186  */
select_chainsetup(const string & name)187 void ECA_CONTROL::select_chainsetup(const string& name)
188 {
189   // --------
190   DBC_REQUIRE(name != "");
191   // --------
192 
193   session_repp->select_chainsetup(name);
194   selected_chainsetup_repp = session_repp->selected_chainsetup_repp;
195   if (selected_chainsetup_repp == 0) {
196     ECA_LOG_MSG(ECA_LOGGER::info, "Chainsetup \"" + name + "\" doesn't exist!");
197     set_last_error("Chainsetup \"" + name + "\" doesn't exist!");
198   }
199   //  else { ECA_LOG_MSG(ECA_LOGGER::info, "Selected chainsetup:  \"" + selected_chainsetup() + "\"."); }
200 
201   // --------
202   DBC_ENSURE(name == selected_chainsetup() ||
203 	 is_selected() == false);
204   // --------
205 }
206 
207 /**
208  * Selects a chainsetup by index.
209  *
210  * @param index_number an integer identifier
211  *
212  * require:
213  *  index_number > 0
214  */
select_chainsetup_by_index(int index_number)215 void ECA_CONTROL::select_chainsetup_by_index(int index_number)
216 {
217   // --------
218   DBC_REQUIRE(index_number > 0);
219   // --------
220 
221   for(std::vector<ECA_CHAINSETUP*>::size_type p = 0;
222       p != session_repp->chainsetups_rep.size();
223       p++) {
224     if (index_number == static_cast<int>(p + 1)) {
225       select_chainsetup(session_repp->chainsetups_rep[p]->name());
226       break;
227     }
228   }
229 }
230 
231 /**
232  * Name of currently active chainsetup
233  */
selected_chainsetup(void) const234 string ECA_CONTROL::selected_chainsetup(void) const
235 {
236  if (selected_chainsetup_repp != 0)
237    return selected_chainsetup_repp->name();
238 
239  return "";
240 }
241 
242 /**
243  * Spawns an external editor for editing selected chainsetup
244  *
245  * require:
246  *  is_selected()
247  *  connected_chainsetup() != selected_chainsetup()
248  */
edit_chainsetup(void)249 void ECA_CONTROL::edit_chainsetup(void)
250 {
251   // --------
252   DBC_REQUIRE(selected_chainsetup().empty() != true);
253   // --------
254 
255   /* 1. check which editor to use (and exit if none
256    *    defined) */
257   string editori = "";
258   string use_getenv =
259     resource_value("ext-cmd-text-editor-use-getenv");
260 
261   if (use_getenv.size() == 0 ||
262       use_getenv == "true") {
263     /* note: as specified in ecasoundrc(5), we should
264      *       default to 'true' */
265     if (std::getenv("EDITOR") != 0) {
266       editori = std::getenv("EDITOR");
267     }
268   }
269   if (editori == "")
270     editori = resource_value("ext-cmd-text-editor");
271 
272   if (editori == "") {
273     ECA_LOG_MSG(ECA_LOGGER::info,
274 		"ERROR: Cannot edit, no text editor specified/available. See ecasoundrc(5) man page and the 'ext-cmd-text-editor' configuration variable.");
275     return;
276   }
277 
278   /* 2. detect hot-swap */
279   bool hot_swap = false;
280   bool restart = false;
281   if (connected_chainsetup() == selected_chainsetup()) {
282     hot_swap = true;
283     if (is_running()) restart = true;
284   }
285 
286   /* 3. store runtime state of the old chainsetup */
287   string origname = selected_chainsetup_repp->name();
288   string origfilename = selected_chainsetup_repp->filename();
289   SAMPLE_SPECS::sample_pos_t origpos = selected_chainsetup_repp->position_in_samples();
290 
291   /* 4. create a safely accessible temporary filename */
292   TEMPORARY_FILE_DIRECTORY tempfile_dir_rep;
293   string tmpdir ("ecasound-");
294   char* tmp_p = std::getenv("USER");
295   if (tmp_p != NULL) {
296     tmpdir += string(tmp_p);
297     tempfile_dir_rep.reserve_directory(tmpdir);
298   }
299   if (tempfile_dir_rep.is_valid() != true) {
300     ECA_LOG_MSG(ECA_LOGGER::info, "WARNING: Unable to create temporary directory \"" + tmpdir + "\".");
301     return;
302   }
303   string filename = tempfile_dir_rep.create_filename("cs-edit-tmp", ".ecs");
304 
305   /* 5. save selected chainsetup to the temp file */
306   if (hot_swap == true)
307     session_repp->connected_chainsetup_repp->set_name("cs-edit-temp");
308 
309   save_chainsetup(filename);
310 
311   if (hot_swap == true)
312     session_repp->connected_chainsetup_repp->set_name(origname);
313   else
314     remove_chainsetup();
315 
316   /* 6. fork an external text editor */
317 
318   // FIXME: we should drop priviledge-level here (at least if root)!
319   editori += " " + filename;
320   int res = ::system(editori.c_str());
321 
322   if (res == 127 || res == -1) {
323     ECA_LOG_MSG(ECA_LOGGER::info, "Can't edit; unable to open file in text editor \"" + string(editori.c_str()) + "\".");
324 
325   }
326   else {
327     /* 7. reload the edited chainsetup and reset runtime state */
328     load_chainsetup(filename);
329 
330     if (is_selected() != true) {
331       ECA_LOG_MSG(ECA_LOGGER::info,
332 		  std::string("Unable to parse chainsetup, keeping the temporary file ") + filename);
333     }
334     else {
335       remove(filename.c_str());
336       if (origfilename.empty() != true) {
337 	set_chainsetup_filename(origfilename);
338       }
339 
340       if (selected_chainsetup_repp)
341 	selected_chainsetup_repp->seek_position_in_samples(origpos);
342 
343       if (hot_swap == true) {
344 	/* 7.1 disconnect the chainsetup to be replaced */
345 	disconnect_chainsetup();
346 
347 	/* 7.2 try to connect the edited chainsetup */
348 	select_chainsetup("cs-edit-temp");
349 	if (is_valid() == true) {
350 	  connect_chainsetup(0);
351 	  /* should succeed as is_valid() is true */
352 	  DBC_CHECK(is_connected() == true);
353 	  if (is_connected() == true) {
354 	    if (restart == true) {
355 	      DBC_CHECK(is_running() != true);
356 	      start();
357 	    }
358 	  }
359 	}
360 
361 	/* 7.3 if connecting the modified chainsetup fails */
362 	if (is_connected() != true) {
363 	  ECA_LOG_MSG(ECA_LOGGER::info, "Can't connect; edited chainsetup is not valid.");
364 	}
365 
366 	/* 7.4 remove the old chainsetup */
367 	select_chainsetup(origname);
368 	remove_chainsetup();
369 	select_chainsetup("cs-edit-temp");
370 	selected_chainsetup_repp->set_name(origname);
371       }
372     }
373   }
374 }
375 
376 /**
377  * Sets processing length in seconds.
378  *
379  * require:
380  *  is_selected() == true
381  *  value >= 0
382  */
set_chainsetup_processing_length_in_seconds(double value)383 void ECA_CONTROL::set_chainsetup_processing_length_in_seconds(double value)
384 {
385   // --------
386   DBC_REQUIRE(is_selected() == true);
387   DBC_REQUIRE(connected_chainsetup() != selected_chainsetup());
388   // --------
389 
390   /* note! here we set the _maximum_ length of the chainsetup */
391   selected_chainsetup_repp->set_max_length_in_seconds(value);
392   ECA_LOG_MSG(ECA_LOGGER::info, "Set chainsetup processing length to \"" + kvu_numtostr(value) + "\" seconds.");
393 }
394 
395 /**
396  * Sets processing length in samples. If 'value' is 0,
397  * length in unspecified.
398  *
399  * require:
400  *  is_selected() == true
401  *  value >= 0
402  */
set_chainsetup_processing_length_in_samples(SAMPLE_SPECS::sample_pos_t value)403 void ECA_CONTROL::set_chainsetup_processing_length_in_samples(SAMPLE_SPECS::sample_pos_t value)
404 {
405   // --------
406   DBC_REQUIRE(is_selected() == true);
407   DBC_REQUIRE(connected_chainsetup() != selected_chainsetup());
408   // --------
409 
410   /* note! here we set the _maximum_ length of the chainsetup */
411   selected_chainsetup_repp->set_max_length_in_samples(value);
412   ECA_LOG_MSG(ECA_LOGGER::info, "Set chainsetup processing length to \"" +
413 	      kvu_numtostr(selected_chainsetup_repp->max_length_in_seconds_exact()) + "\" seconds.");
414 }
415 
416 /**
417  * Sets default open mode for audio outputs.
418  *
419  * require:
420  *  output_mode == AUDIO_IO::io_write || output_mode == AUDIO_IO::io_readwrite
421  */
set_chainsetup_output_mode(int output_mode)422 void ECA_CONTROL::set_chainsetup_output_mode(int output_mode)
423 {
424   // --------
425   DBC_REQUIRE(output_mode == AUDIO_IO::io_write || output_mode == AUDIO_IO::io_readwrite);
426   // --------
427   selected_chainsetup_repp->set_output_openmode(output_mode);
428 }
429 
430 /**
431  * Sets chainsetup buffersize (in samples).
432  *
433  * @pre is_selected() == true
434  */
set_chainsetup_buffersize(int bsize)435 void ECA_CONTROL::set_chainsetup_buffersize(int bsize)
436 {
437   // --------
438   DBC_REQUIRE(is_selected() == true);
439   // --------
440   selected_chainsetup_repp->set_buffersize(bsize);
441 }
442 
443 /**
444  * Toggles chainsetup looping
445  *
446  * require:
447  *  is_selected() == true
448  */
toggle_chainsetup_looping(void)449 void ECA_CONTROL::toggle_chainsetup_looping(void)
450 {
451  // --------
452   DBC_REQUIRE(is_selected() == true);
453   DBC_REQUIRE(connected_chainsetup() != selected_chainsetup());
454   // --------
455   if (selected_chainsetup_repp->looping_enabled()) {
456     selected_chainsetup_repp->toggle_looping(false);
457     ECA_LOG_MSG(ECA_LOGGER::info, "Disabled looping.");
458   }
459   else {
460     selected_chainsetup_repp->toggle_looping(true);
461     ECA_LOG_MSG(ECA_LOGGER::info, "Enabled looping.");
462   }
463 }
464 
465 /**
466  * Connects selected chainsetup
467  *
468  * require:
469  *  is_selected() == true
470  *  is_valid() == true
471  *
472  * ensure:
473  *  is_connected() == true || (last_error().size() > 0 && no_errors != true)
474  */
connect_chainsetup(struct eci_return_value * retval)475 void ECA_CONTROL::connect_chainsetup(struct eci_return_value *retval)
476 {
477   // --------
478   DBC_REQUIRE(is_selected());
479   DBC_REQUIRE(selected_chainsetup_repp != 0 && selected_chainsetup_repp->is_valid() == true);
480   // --------
481 
482   bool no_errors = true;
483   string errmsg;
484   if (is_connected() == true) {
485     disconnect_chainsetup();
486   }
487   try {
488     session_repp->connect_chainsetup();
489     ECA_LOG_MSG(ECA_LOGGER::subsystems, "Connected chainsetup: \"" + connected_chainsetup() + "\"");
490   }
491   catch(ECA_ERROR& e) {
492     errmsg = e.error_message();
493     no_errors = false;
494   }
495   if (is_connected() != true) {
496     set_last_error(" Connecting chainsetup failed: \"" + errmsg + "\"");
497     no_errors = false;
498   }
499 
500   fill_command_retval(retval);
501 
502   // --------
503   DBC_ENSURE(is_connected() || no_errors != true);
504   // --------
505 }
506 
507 /**
508  * Name of connected chainsetup.
509  */
connected_chainsetup(void) const510 string ECA_CONTROL::connected_chainsetup(void) const
511 {
512   if (session_repp->connected_chainsetup_repp != 0) {
513     return session_repp->connected_chainsetup_repp->name();
514   }
515 
516   return "";
517 }
518 
519 /**
520  * Disconnects activate chainsetup
521  *
522  * require:
523  *  is_connected() == true
524  *
525  * ensure:
526  *  connected_chainsetup() == ""
527  */
disconnect_chainsetup(void)528 void ECA_CONTROL::disconnect_chainsetup(void)
529 {
530   // --------
531   DBC_REQUIRE(is_connected());
532   // --------
533 
534   if (is_engine_ready_for_commands() == true) {
535     stop_on_condition();
536   }
537   if (is_engine_created()) {
538     close_engine();
539   }
540 
541   ECA_LOG_MSG(ECA_LOGGER::info, "Disconnecting chainsetup:  \"" + connected_chainsetup() + "\".");
542   session_repp->disconnect_chainsetup();
543 
544   // --------
545   DBC_ENSURE(connected_chainsetup() == "");
546   // --------
547 }
548 
549 /**
550  * Changes the chainsetup position relatively to the current position.
551  * Behaves differently depending on whether the selected
552  * chainsetup is connected or not.
553  *
554  * require:
555  *  is_selected() == true
556  */
change_chainsetup_position(double seconds)557 void ECA_CONTROL::change_chainsetup_position(double seconds)
558 {
559   // --------
560   DBC_REQUIRE(is_selected());
561   // --------
562 
563   // FIXME: check whether all audio devices support seeking,
564   //        raise an error if not (note: see other similar FIXMEs)
565 
566   if (connected_chainsetup() == selected_chainsetup() && is_engine_ready_for_commands() == true) {
567     if (seconds < 0)
568       engine_repp->command(ECA_ENGINE::ep_rewind, -seconds);
569     else
570       engine_repp->command(ECA_ENGINE::ep_forward, seconds);
571   }
572   else {
573     selected_chainsetup_repp->seek_position_in_seconds(selected_chainsetup_repp->position_in_seconds()
574 						       + seconds );
575   }
576 }
577 
578 /**
579  * Changes the chainsetup position in samples relatively to the
580  * current position. Behaves differently depending on whether
581  * the selected chainsetup is connected or not.
582  *
583  * require:
584  *  is_selected() == true
585  */
change_chainsetup_position_samples(SAMPLE_SPECS::sample_pos_t samples)586 void ECA_CONTROL::change_chainsetup_position_samples(SAMPLE_SPECS::sample_pos_t samples)
587 {
588   // --------
589   DBC_REQUIRE(is_selected());
590   // --------
591 
592   // FIXME: check whether all audio devices support seeking,
593   //        raise an error if not (note: see other similar FIXMEs)
594 
595   if (connected_chainsetup() == selected_chainsetup() && is_engine_ready_for_commands() == true) {
596     change_chainsetup_position(static_cast<double>(samples) /
597 			       selected_chainsetup_repp->samples_per_second());
598   }
599   else {
600     selected_chainsetup_repp->seek_position_in_samples(selected_chainsetup_repp->position_in_samples()
601 						       + samples );
602   }
603 }
604 
605 /**
606  * Sets the chainsetup position. Behaves differently depending on
607  * whether the selected chainsetup is connected or not.
608  *
609  * require:
610  *  is_selected() == true
611  */
set_chainsetup_position(double seconds)612 void ECA_CONTROL::set_chainsetup_position(double seconds)
613 {
614   // --------
615   DBC_REQUIRE(is_selected());
616   // --------
617 
618   // FIXME: check whether all audio devices support seeking,
619   //        raise an error if not (note: see other similar FIXMEs)
620 
621   if (connected_chainsetup() == selected_chainsetup() && is_engine_ready_for_commands() == true) {
622     engine_repp->command(ECA_ENGINE::ep_setpos, seconds);
623   }
624   else {
625     selected_chainsetup_repp->seek_position_in_seconds(seconds);
626   }
627 }
628 
629 /**
630  * Sets the chainsetup position in samples.. Behaves
631  * differently depending on whether the selected chainsetup
632  * is connected or not.
633  *
634  * require:
635  *  is_selected() == true
636  */
set_chainsetup_position_samples(SAMPLE_SPECS::sample_pos_t samples)637 void ECA_CONTROL::set_chainsetup_position_samples(SAMPLE_SPECS::sample_pos_t samples)
638 {
639   // --------
640   DBC_REQUIRE(is_selected());
641   // --------
642 
643   // FIXME: check whether all audio devices support seeking,
644   //        raise an error if not (note: see other similar FIXMEs)
645 
646   if (connected_chainsetup() == selected_chainsetup() && is_engine_ready_for_commands() == true) {
647     set_chainsetup_position(static_cast<double>(samples) /
648 			    selected_chainsetup_repp->samples_per_second());
649   }
650   else {
651     selected_chainsetup_repp->seek_position_in_samples(samples);
652   }
653 }
654 
655 /**
656  * Gets a vector of al chainsetup names.
657  */
chainsetup_names(void) const658 std::vector<string> ECA_CONTROL::chainsetup_names(void) const
659 {
660   return session_repp->chainsetup_names();
661 }
662 
663 /**
664  * Gets a pointer to selected chainsetup, or 0 if no
665  * chainsetup is selected.
666  */
get_chainsetup(void) const667 const ECA_CHAINSETUP* ECA_CONTROL::get_chainsetup(void) const
668 {
669   return selected_chainsetup_repp;
670 }
671 
672 /**
673  * Gets a pointer to connected chainsetup, or 0 if no
674  * chainsetup is selected.
675  */
get_connected_chainsetup(void) const676 const ECA_CHAINSETUP* ECA_CONTROL::get_connected_chainsetup(void) const
677 {
678   return session_repp->connected_chainsetup_repp;
679 }
680 
681 /**
682  * Gets a pointer to chainsetup with filename 'filename'.
683  */
get_chainsetup_filename(const string & filename) const684 const ECA_CHAINSETUP* ECA_CONTROL::get_chainsetup_filename(const string&
685 								   filename) const
686 {
687   std::vector<ECA_CHAINSETUP*>::const_iterator p = session_repp->chainsetups_rep.begin();
688   while(p != session_repp->chainsetups_rep.end()) {
689     if ((*p)->filename() == filename) {
690       return (*p);
691     }
692     ++p;
693     }
694   return 0;
695 }
696 
697 /**
698  * Returns current buffersize of selected chainsetup.
699  *
700  * �pre is_selected() == true
701  */
chainsetup_buffersize(void) const702 int ECA_CONTROL::chainsetup_buffersize(void) const
703 {
704   // --------
705   DBC_REQUIRE(is_selected() == true);
706   // --------
707   return selected_chainsetup_repp->buffersize();
708 }
709 
710 /**
711  * Gets chainsetup filename (used by save_chainsetup())
712  *
713  * �pre is_selected() == true
714  */
chainsetup_filename(void) const715 const string& ECA_CONTROL::chainsetup_filename(void) const
716 {
717   // --------
718   DBC_REQUIRE(is_selected() == true);
719   // --------
720   return selected_chainsetup_repp->filename();
721 }
722 
723 /**
724  * Sets chainsetup filename (used by save_chainsetup())
725  *
726  * require:
727  *  is_selected() == true &&
728  *  name.empty() != true
729  */
set_chainsetup_filename(const string & name)730 void ECA_CONTROL::set_chainsetup_filename(const string& name)
731 {
732   // --------
733   DBC_REQUIRE(is_selected() == true);
734   DBC_REQUIRE(name.empty() != true);
735   // --------
736   selected_chainsetup_repp->set_filename(name);
737 }
738 
739 /**
740  * Sets general chainsetup chainsetup parameter
741  *
742  * require:
743  *  is_selected() == true &&
744  *  name.empty() != true
745  */
set_chainsetup_parameter(const string & name)746 void ECA_CONTROL::set_chainsetup_parameter(const string& name)
747 {
748   // --------
749   DBC_REQUIRE(is_selected() == true  &&
750 	      name.empty() != true);
751   // --------
752 
753   selected_chainsetup_repp->interpret_global_option(name);
754   if (selected_chainsetup_repp->interpret_result() != true) {
755     /* for instance -f:xxx options are handle by
756      * object-specific parsing */
757     selected_chainsetup_repp->interpret_object_option(name);
758   }
759 }
760 
761 /**
762  * Sets general chainsetup chainsetup parameter
763  *
764  * require:
765  *  is_selected() == true &&
766  *  name.empty() != true
767  */
set_chainsetup_sample_format(const string & name)768 void ECA_CONTROL::set_chainsetup_sample_format(const string& name)
769 {
770   // --------
771   DBC_REQUIRE(is_selected() == true  &&
772 	 name.empty() != true);
773   // --------
774 
775   selected_chainsetup_repp->interpret_object_option("-f:" + name);
776   if (selected_chainsetup_repp->interpret_result() != true) {
777     set_last_error(selected_chainsetup_repp->interpret_result_verbose());
778   }
779 }
780 
781 /**
782  * Adds a new chain (selected chainsetup). Added chain is automatically
783  * selected.
784  *
785  * require:
786  *  is_selected() == true
787  *  connected_chainsetup() != selected_chainsetup()
788  *
789  * ensure:
790  *   selected_chains().size() > 0
791  */
add_chain(const string & name)792 void ECA_CONTROL::add_chain(const string& name)
793 {
794   // --------
795   DBC_REQUIRE(is_selected() == true);
796   DBC_REQUIRE(selected_chainsetup() != connected_chainsetup());
797   // --------
798 
799   add_chains(std::vector<string> (1, name));
800 
801   // --------
802   DBC_ENSURE(selected_chains().size() > 0);
803   // --------
804 }
805 
806 /**
807  * Adds new chains (selected chainsetup).  Added chains are automatically
808  * selected.
809  *
810  * @param names comma separated list of chain names
811  *
812  * require:
813  *  is_selected() == true
814  *  connected_chainsetup() != selected_chainsetup()
815  *
816  * ensure:
817  *   selected_chains().size() > 0
818  */
add_chains(const string & names)819 void ECA_CONTROL::add_chains(const string& names)
820 {
821   // --------
822   DBC_REQUIRE(is_selected() == true &&
823 	      (session_repp->connected_chainsetup_repp !=
824 	       session_repp->selected_chainsetup_repp));
825   // --------
826 
827   add_chains(kvu_string_to_vector(names, ','));
828 
829   // --------
830   DBC_ENSURE(selected_chains().size() > 0);
831   // --------
832 }
833 
834 /**
835  * Adds new chains (selected chainsetup). Added chains are automatically
836  * selected.
837  *
838  * @param namess vector of chain names
839  *
840  * require:
841  *  is_selected() == true
842  *  connected_chainsetup() != selected_chainsetup()
843  *
844  * ensure:
845  *   selected_chains().size() == names.size()
846  */
add_chains(const std::vector<string> & new_chains)847 void ECA_CONTROL::add_chains(const std::vector<string>& new_chains)
848 {
849   // --------
850   DBC_REQUIRE(is_selected() == true);
851   DBC_REQUIRE(connected_chainsetup() != selected_chainsetup());
852   // --------
853 
854   selected_chainsetup_repp->add_new_chains(new_chains);
855   selected_chainsetup_repp->select_chains(new_chains);
856 
857   ECA_LOG_MSG(ECA_LOGGER::info, "Added chains: " + kvu_vector_to_string(new_chains, ", ") + ".");
858 
859   // --------
860   DBC_ENSURE(selected_chains().size() == new_chains.size());
861   // --------
862 }
863 
864 /**
865  * Removes currently selected chain (selected chainsetup)
866  *
867  * require:
868  *  is_selected() == true
869  *  connected_chainsetup() != selected_chainsetup()
870  *  selected_chains().size() > 0 &&
871  *
872  * ensure:
873  *  selected_chains().size() == 0
874  */
remove_chains(void)875 void ECA_CONTROL::remove_chains(void)
876 {
877   // --------
878   DBC_REQUIRE(is_selected() == true &&
879 	      selected_chains().size() > 0 &&
880 	      (session_repp->connected_chainsetup_repp !=
881 	       session_repp->selected_chainsetup_repp));
882   // --------
883 
884   selected_chainsetup_repp->remove_chains();
885 
886   ECA_LOG_MSG(ECA_LOGGER::info, "(eca-controlled) Removed selected chains.");
887 
888   // --------
889   DBC_ENSURE(selected_chains().size() == 0);
890   // --------
891 }
892 
893 /**
894  * Selects a set of chains using index numbers. Previously
895  * selected chains are first all deselected.
896  *
897  *
898  * @param index_numbers set of integer identifiers
899  *
900  * require:
901  *   is_selected() == true
902  */
select_chains_by_index(const std::vector<int> & index_numbers)903 void ECA_CONTROL::select_chains_by_index(const std::vector<int>& index_numbers)
904 {
905   // --------
906   DBC_REQUIRE(is_selected() == true);
907   // --------
908 
909   std::vector<string> selchains;
910   for(std::vector<CHAIN*>::size_type p = 0;
911       p != selected_chainsetup_repp->chains.size();
912       p++) {
913     for(std::vector<int>::size_type q = 0;
914 	q != index_numbers.size();
915 	q++) {
916       if (index_numbers[q] == static_cast<int>(p + 1)) {
917 	selchains.push_back(selected_chainsetup_repp->chains[p]->name());
918 	break;
919       }
920     }
921   }
922   select_chains(selchains);
923 }
924 
925 /**
926  * Selects a chains (currently selected chainsetup). Previously
927  * selected chains are first all deselected.
928  *
929  * require:
930  *   is_selected() == true
931  *
932  * ensure:
933  *   selected_chains().size() == 1
934  */
select_chain(const string & chain)935 void ECA_CONTROL::select_chain(const string& chain)
936 {
937   // --------
938   DBC_REQUIRE(is_selected() == true);
939   // --------
940 
941   std::vector<string> c (1);
942   c[0] = chain;
943   selected_chainsetup_repp->select_chains(c);
944   //  ECA_LOG_MSG(ECA_LOGGER::user_objects, "Selected chain: " + chain + ".");
945 
946   // --------
947   DBC_ENSURE(selected_chains().size() == 1);
948   // --------
949 }
950 
951 
952 /**
953  * Selects chains (currently selected chainsetup). Previously
954  * selected chains are first all deselected.
955  *
956  * @param chains vector of chain names
957  *
958  * require:
959  *   is_selected() == true
960  *
961  * ensure:
962  *   selected_chains().size() > 0
963  */
select_chains(const std::vector<string> & chains)964 void ECA_CONTROL::select_chains(const std::vector<string>& chains)
965 {
966   // --------
967   DBC_REQUIRE(is_selected() == true);
968   // --------
969 
970   selected_chainsetup_repp->select_chains(chains);
971 
972   //  ECA_LOG_MSG(ECA_LOGGER::user_objects, "Selected chains: " +
973 //  		vector_to_string(chains, ", ") + ".");
974 }
975 
976 /**
977  * Deselects chains (currently selected chainsetup)
978  *
979  * @param chains vector of chain names
980  *
981  * require:
982  *   is_selected() == true
983  */
deselect_chains(const std::vector<string> & chains)984 void ECA_CONTROL::deselect_chains(const std::vector<string>& chains)
985 {
986   // --------
987   DBC_REQUIRE(is_selected() == true);
988   // --------
989 
990   std::vector<string> schains = selected_chainsetup_repp->selected_chains();
991   std::vector<string>::const_iterator p = chains.begin();
992   while(p != chains.end()) {
993     std::vector<string>::iterator o = schains.begin();
994     while(o != schains.end()) {
995       if (*p == *o) {
996 	//  ECA_LOG_MSG(ECA_LOGGER::info, "(eca-controller-objects) Deselected chain " + *o  + ".");
997 	schains.erase(o);
998       }
999       else
1000 	++o;
1001     }
1002     ++p;
1003   }
1004 
1005   selected_chainsetup_repp->select_chains(schains);
1006 }
1007 
1008 /**
1009  * Selects all chains (currently selected chainsetup)
1010  *
1011  * require:
1012  *   is_selected() == true
1013  */
select_all_chains(void)1014 void ECA_CONTROL::select_all_chains(void)
1015 {
1016   // --------
1017   DBC_REQUIRE(is_selected() == true);
1018   // --------
1019 
1020   selected_chainsetup_repp->select_all_chains();
1021 
1022   //  ECA_LOG_MSG(ECA_LOGGER::info, "Selected chains: " + vector_to_string(selected_chains(), ", ") + ".");
1023 }
1024 
1025 /**
1026  * Returns a list of selected chains (currently selected chainsetup)
1027  *
1028  * require:
1029  *  is_selected() == true
1030  */
selected_chains(void) const1031 const std::vector<string>& ECA_CONTROL::selected_chains(void) const
1032 {
1033   // --------
1034   DBC_REQUIRE(is_selected() == true);
1035   // --------
1036   return selected_chainsetup_repp->selected_chains();
1037 }
1038 
1039 /**
1040  * Gets a vector of all chain names.
1041  *
1042  * require:
1043  *  is_selected() == true
1044  */
chain_names(void) const1045 std::vector<string> ECA_CONTROL::chain_names(void) const
1046 {
1047   // --------
1048   DBC_REQUIRE(is_selected() == true);
1049   // --------
1050   return selected_chainsetup_repp->chain_names();
1051 }
1052 
1053 /**
1054  * Gets a pointer to selected chain, or 0 if no chain is selected.
1055  *
1056  * require:
1057  *  is_selected() == true
1058  *  selected_chains().size() == 1
1059  */
get_chain(void) const1060 const CHAIN* ECA_CONTROL::get_chain(void) const
1061 {
1062   // --------
1063   DBC_REQUIRE(is_selected() == true);
1064   DBC_REQUIRE(selected_chains().size() == 1);
1065   // --------
1066   return get_chain_priv();
1067 
1068 }
1069 
1070 /**
1071  * Gets a pointer to selected chain, or 0 if no chain is selected.
1072  */
get_chain_priv(void) const1073 CHAIN* ECA_CONTROL::get_chain_priv(void) const
1074 {
1075   const std::vector<string>& schains = selected_chainsetup_repp->selected_chains();
1076   std::vector<string>::const_iterator o = schains.begin();
1077   while(o != schains.end()) {
1078     for(std::vector<CHAIN*>::size_type p = 0;
1079 	p != selected_chainsetup_repp->chains.size();
1080 	p++) {
1081       if (selected_chainsetup_repp->chains[p]->name() == *o)
1082 	return selected_chainsetup_repp->chains[p];
1083     }
1084     ++o;
1085   }
1086   return 0;
1087 }
1088 
1089 /**
1090  * Clears all selected chains (all chain operators and controllers
1091  * are removed)
1092  *
1093  * @param name chain name
1094  *
1095  * require:
1096  *  is_selected() == true
1097  *  is_running() != true
1098  */
clear_chains(void)1099 void ECA_CONTROL::clear_chains(void)
1100 {
1101   // --------
1102   DBC_REQUIRE(is_selected() == true);
1103   DBC_REQUIRE(is_running() != true);
1104   // --------
1105   selected_chainsetup_repp->clear_chains();
1106 }
1107 
1108 /**
1109  * Clears all selected chains (all chain operators and controllers
1110  * are removed)
1111  *
1112  * @param name chain name
1113  *
1114  * require:
1115  *  is_selected() == true
1116  *  connected_chainsetup() != selected_chainsetup()
1117  *  selected_chains().size() == 1
1118  */
rename_chain(const string & name)1119 void ECA_CONTROL::rename_chain(const string& name)
1120 {
1121   // --------
1122   DBC_REQUIRE(is_selected() == true);
1123   DBC_REQUIRE(connected_chainsetup() != selected_chainsetup());
1124   DBC_REQUIRE(selected_chains().size() == 1);
1125   // --------
1126   selected_chainsetup_repp->rename_chain(name);
1127 }
1128 
audio_input_as_selected(void)1129 void ECA_CONTROL::audio_input_as_selected(void)
1130 {
1131   /* note, here we check that the pointer is still a valid one */
1132   if (selected_chainsetup_repp->ok_audio_object(selected_audio_input_repp) != true)
1133     selected_audio_input_repp = 0;
1134 
1135   selected_audio_object_repp = selected_audio_input_repp;
1136 }
1137 
audio_output_as_selected(void)1138 void ECA_CONTROL::audio_output_as_selected(void)
1139 {
1140   /* note, here we check that the pointer is still a valid one */
1141   if (selected_chainsetup_repp->ok_audio_object(selected_audio_output_repp) != true)
1142     selected_audio_output_repp = 0;
1143 
1144   selected_audio_object_repp = selected_audio_output_repp;
1145 }
1146 
1147 /**
1148  * Sets default audio format. This format will be used, when
1149  * adding audio inputs and outputs.
1150  *
1151  * require:
1152  *  is_selected() == true
1153  */
set_default_audio_format(const string & sfrm,int channels,long int srate,bool interleaving)1154 void ECA_CONTROL::set_default_audio_format(const string& sfrm,
1155 						   int channels,
1156 						   long int srate,
1157 						   bool interleaving)
1158 {
1159  // --------
1160   DBC_REQUIRE(is_selected() == true);
1161   // --------
1162 
1163   string format;
1164   format = "-f:";
1165   format += sfrm;
1166   format += ",";
1167   format += kvu_numtostr(channels);
1168   format += ",";
1169   format += kvu_numtostr(srate);
1170   format += ",";
1171   if (interleaving == true)
1172     format += "i";
1173   else
1174     format += "n";
1175 
1176   selected_chainsetup_repp->interpret_object_option(format);
1177   if (selected_chainsetup_repp->interpret_result() != true) {
1178     set_last_error(selected_chainsetup_repp->interpret_result_verbose());
1179   }
1180 }
1181 
1182 /**
1183  * Returns the default audio format.
1184  *
1185  *  @pre is_selected() == true
1186  */
default_audio_format(void) const1187 const ECA_AUDIO_FORMAT& ECA_CONTROL::default_audio_format(void) const
1188 {
1189   // --
1190   DBC_REQUIRE(is_selected() == true);
1191   // --
1192   return selected_chainsetup_repp->default_audio_format();
1193 }
1194 
1195 /**
1196  * Sets default audio format. This format will be used, when
1197  * adding audio inputs and outputs.
1198  *
1199  * require:
1200  *  is_selected() == true
1201  */
set_default_audio_format(const ECA_AUDIO_FORMAT & format)1202 void ECA_CONTROL::set_default_audio_format(const ECA_AUDIO_FORMAT& format)
1203 {
1204  // --------
1205   DBC_REQUIRE(is_selected() == true);
1206   // --------
1207 
1208   set_default_audio_format(format.format_string(),
1209 			   static_cast<int>(format.channels()),
1210 			   static_cast<long int>(format.samples_per_second()),
1211 			   format.interleaved_channels());
1212 }
1213 
priv_select_audio_object(const std::vector<AUDIO_IO * > & objects,const std::string & name)1214 static AUDIO_IO* priv_select_audio_object(const std::vector<AUDIO_IO*>& objects, const std::string& name)
1215 {
1216   AUDIO_IO* result = 0;
1217 
1218   /* NOTE: ugly, but needed to maintain compability with older 2.4.x
1219    *       releases that allowed double-quoted filenames to ai/ao-select */
1220   std::string stripped_name;
1221   if (name.size() > 0 &&
1222       name[0] == '"') {
1223     stripped_name = name;
1224     kvu_string_strip_outer_quotes(&stripped_name, '"');
1225   }
1226 
1227   std::vector<AUDIO_IO*>::size_type p = 0;
1228   for(p = 0; p != objects.size(); p++) {
1229     if (objects[p]->label() == name ||
1230 	objects[p]->label() == stripped_name) {
1231       result = objects[p];
1232       /* note: in case 'name' is not unique, the first matching
1233        *       instance is selected */
1234       break;
1235     }
1236   }
1237 
1238   return result;
1239 }
1240 
1241 /**
1242  * Selects an audio input
1243  *
1244  * require:
1245  *  is_selected() == true
1246  */
select_audio_input(const string & name)1247 void ECA_CONTROL::select_audio_input(const string& name)
1248 {
1249   // --------
1250   DBC_REQUIRE(is_selected() == true);
1251   // --------
1252 
1253   selected_audio_input_repp =
1254     priv_select_audio_object(selected_chainsetup_repp->inputs, name);
1255 }
1256 
1257 /**
1258  * Selects an audio output
1259  *
1260  * require:
1261  *  is_selected() == true
1262  */
select_audio_output(const string & name)1263 void ECA_CONTROL::select_audio_output(const string& name)
1264 {
1265   // --------
1266   DBC_REQUIRE(is_selected() == true);
1267   // --------
1268 
1269   selected_audio_output_repp =
1270     priv_select_audio_object(selected_chainsetup_repp->outputs, name);
1271 }
1272 
1273 /**
1274  * Selects an audio input by index.
1275  *
1276  * @pre is_selected() == true
1277  * @pre index_number > 0
1278  */
select_audio_input_by_index(int index_number)1279 void ECA_CONTROL::select_audio_input_by_index(int index_number)
1280 {
1281   // --------
1282   DBC_REQUIRE(is_selected() == true);
1283   DBC_REQUIRE(index_number > 0);
1284   // --------
1285 
1286   selected_audio_input_repp = 0;
1287 
1288   if (index_number <= static_cast<int>(selected_chainsetup_repp->inputs.size()))
1289     selected_audio_input_repp = selected_chainsetup_repp->inputs[index_number-1];
1290 }
1291 
1292 /**
1293  * Selects an audio output by index.
1294  *
1295  * @pre is_selected() == true
1296  * @pre index_number > 0
1297  */
select_audio_output_by_index(int index_number)1298 void ECA_CONTROL::select_audio_output_by_index(int index_number)
1299 {
1300   // --------
1301   DBC_REQUIRE(is_selected() == true);
1302   DBC_REQUIRE(index_number > 0);
1303   // --------
1304 
1305   selected_audio_output_repp = 0;
1306 
1307   if (index_number <= static_cast<int>(selected_chainsetup_repp->outputs.size()))
1308     selected_audio_output_repp = selected_chainsetup_repp->outputs[index_number-1];
1309 }
1310 
1311 /**
1312  * Gets audio format information of the object given as argument.
1313  * Note! To get audio format information, audio objects need
1314  * to be opened. Because of this, object argument cannot be given
1315  * as a const pointer.
1316  */
get_audio_format(AUDIO_IO * aobj) const1317 ECA_AUDIO_FORMAT ECA_CONTROL::get_audio_format(AUDIO_IO* aobj) const
1318 {
1319   // --------
1320   DBC_REQUIRE(is_selected() == true);
1321   DBC_REQUIRE(aobj != 0);
1322   // --------
1323 
1324   bool was_open = true;
1325   if (aobj->is_open() == false) {
1326     was_open = false;
1327     try {
1328       aobj->open();
1329     }
1330     catch(AUDIO_IO::SETUP_ERROR&) {
1331       // FIXME: what to do here?
1332     }
1333   }
1334   ECA_AUDIO_FORMAT t (aobj->channels(),
1335 		      aobj->samples_per_second(),
1336 		      aobj->sample_format(),
1337 		      aobj->interleaved_channels());
1338   if (was_open == false) aobj->close();
1339   return t;
1340 }
1341 
1342 /**
1343  * Sets the default audio format to the match the currently
1344  * select audio input's audio format.
1345  *
1346  *  @pre is_selected() == true
1347  *  @pre connected_chainsetup() != selected_chainsetup()
1348  *  @pre selected_audio_object_repp != 0
1349  */
set_default_audio_format_to_selected_input(void)1350 void ECA_CONTROL::set_default_audio_format_to_selected_input(void)
1351 {
1352   // --------
1353   DBC_REQUIRE(is_selected() == true);
1354   DBC_REQUIRE(connected_chainsetup() != selected_chainsetup());
1355   DBC_REQUIRE(get_audio_input() != 0);
1356   // --------
1357   set_default_audio_format(get_audio_format(selected_audio_input_repp));
1358 
1359 }
1360 
1361 /**
1362  * Sets the default audio format to the match the currently
1363  * select audio output's audio format.
1364  *
1365  *  @pre is_selected() == true
1366  *  @pre connected_chainsetup() != selected_chainsetup()
1367  *  @pre selected_audio_object_repp != 0
1368  */
set_default_audio_format_to_selected_output(void)1369 void ECA_CONTROL::set_default_audio_format_to_selected_output(void)
1370 {
1371   // --------
1372   DBC_REQUIRE(is_selected() == true);
1373   DBC_REQUIRE(connected_chainsetup() != selected_chainsetup());
1374   DBC_REQUIRE(get_audio_output() != 0);
1375   // --------
1376   set_default_audio_format(get_audio_format(selected_audio_output_repp));
1377 }
1378 
1379 /**
1380  * Adds a new audio input (file, soundcard device, etc). Input
1381  * is attached to currently selected chains (if any). If 'filename'
1382  * doesn't exist or is otherwise invalid, no input is added.
1383  *
1384  * require:
1385  *   filename.empty() == false
1386  *   is_selected() == true
1387  *   connected_chainsetup() != selected_chainsetup()
1388  */
add_audio_input(const string & filename)1389 void ECA_CONTROL::add_audio_input(const string& filename)
1390 {
1391   // --------
1392   DBC_REQUIRE(filename.empty() == false);
1393   DBC_REQUIRE(is_selected() == true);
1394   DBC_REQUIRE(connected_chainsetup() != selected_chainsetup());
1395   // --------
1396 
1397   selected_audio_input_repp = 0;
1398   selected_chainsetup_repp->interpret_object_option("-i:" + filename);
1399   if (selected_chainsetup_repp->interpret_result() == true) {
1400     select_audio_input(kvu_get_argument_number(1, filename));
1401     ECA_LOG_MSG(ECA_LOGGER::info, "Added audio input \"" + filename + "\".");
1402   }
1403   else {
1404     set_last_error(selected_chainsetup_repp->interpret_result_verbose());
1405   }
1406 }
1407 
1408 /**
1409  * Adds a new audio output (file, soundcard device, etc). Output
1410  * is attached to currently selected chains (if any). If 'filename'
1411  * doesn't exist or is otherwise invalid, no input is added.
1412  *
1413  * require:
1414  *   filename.empty() == false
1415  *   is_selected() == true
1416  *   connected_chainsetup() != selected_chainsetup()
1417  */
add_audio_output(const string & filename)1418 void ECA_CONTROL::add_audio_output(const string& filename)
1419 {
1420   // --------
1421   DBC_REQUIRE(filename.empty() == false);
1422   DBC_REQUIRE(is_selected() == true);
1423   DBC_REQUIRE(connected_chainsetup() != selected_chainsetup());
1424   // --------
1425 
1426   selected_audio_output_repp = 0;
1427   selected_chainsetup_repp->interpret_object_option("-o:" + filename);
1428   if (selected_chainsetup_repp->interpret_result() == true) {
1429     select_audio_output(kvu_get_argument_number(1, filename));
1430     ECA_LOG_MSG(ECA_LOGGER::info, "Added audio output \"" + filename +
1431 		"\".");
1432   } else {
1433     set_last_error(selected_chainsetup_repp->interpret_result_verbose());
1434   }
1435 }
1436 
1437 /**
1438  * Adds a default output (as defined in ~/.ecasoundrc) and attach
1439  * it to currently selected chains.
1440  *
1441  * require:
1442  *  is_selected() == true
1443  *  connected_chainsetup() != selected_chainsetup()
1444  */
add_default_output(void)1445 void ECA_CONTROL::add_default_output(void)
1446 {
1447   // --------
1448   DBC_REQUIRE(selected_chains().size() > 0);
1449   DBC_REQUIRE(is_selected() == true);
1450   DBC_REQUIRE(connected_chainsetup() != selected_chainsetup());
1451   // --------
1452   add_audio_output(ECA_OBJECT_FACTORY::probe_default_output_device());
1453   ECA_LOG_MSG(ECA_LOGGER::info, "Added default output to selected chains.");
1454 }
1455 
1456 /**
1457  * Gets a vector of all audio input names.
1458  *
1459  * require:
1460  *  is_selected() == true
1461  */
audio_input_names(void) const1462 std::vector<string> ECA_CONTROL::audio_input_names(void) const
1463 {
1464   // --------
1465   DBC_REQUIRE(is_selected() == true);
1466   // --------
1467   return selected_chainsetup_repp->audio_input_names();
1468 }
1469 
1470 /**
1471  * Gets a vector of all audio output names.
1472  *
1473  * require:
1474  *  is_selected() == true
1475  */
audio_output_names(void) const1476 std::vector<string> ECA_CONTROL::audio_output_names(void) const
1477 {
1478   // --------
1479   DBC_REQUIRE(is_selected() == true);
1480   // --------
1481   return selected_chainsetup_repp->audio_output_names();
1482 }
1483 
1484 /**
1485  * Gets a pointer to the currently selected audio input.
1486  * Returns 0 if no audio object is selected.
1487  *
1488  * require:
1489  *  is_selected() == true
1490  */
get_audio_input(void)1491 const AUDIO_IO* ECA_CONTROL::get_audio_input(void)
1492 {
1493   // --------
1494   DBC_REQUIRE(is_selected() == true);
1495   // --------
1496 
1497   /* note, here we check that the pointer is still a valid one */
1498   if (selected_chainsetup_repp->ok_audio_object(selected_audio_input_repp) != true)
1499     selected_audio_input_repp = 0;
1500 
1501   return selected_audio_input_repp;
1502 }
1503 
1504 /**
1505  * Gets a pointer to the currently selected audio output.
1506  * Returns 0 if no audio object is selected.
1507  *
1508  * require:
1509  *  is_selected() == true
1510  */
get_audio_output(void)1511 const AUDIO_IO* ECA_CONTROL::get_audio_output(void)
1512 {
1513   // --------
1514   DBC_REQUIRE(is_selected() == true);
1515   // --------
1516   /* note, here we check that the pointer is still a valid one */
1517   if (selected_chainsetup_repp->ok_audio_object(selected_audio_output_repp) != true)
1518     selected_audio_output_repp = 0;
1519 
1520   return selected_audio_output_repp;
1521 }
1522 
1523 /**
1524  * Removes the selected audio input.
1525  *
1526  *  @pre is_selected() == true
1527  *  @pre connected_chainsetup() != selected_chainsetup()
1528  *  @pre get_audio_input() != 0
1529  *
1530  *  @post selected_audio_input_repp = 0
1531  */
remove_audio_input(void)1532 void ECA_CONTROL::remove_audio_input(void)
1533 {
1534   // --------
1535   DBC_REQUIRE(is_selected() == true);
1536   DBC_REQUIRE(connected_chainsetup() != selected_chainsetup());
1537   DBC_REQUIRE(get_audio_input() != 0);
1538   // --------
1539   ECA_LOG_MSG(ECA_LOGGER::info, "Removing selected audio input \"" + selected_audio_input_repp->label() +
1540 	      "\" from selected chains.");
1541   selected_chainsetup_repp->remove_audio_input(selected_audio_input_repp);
1542   selected_audio_input_repp = 0;
1543 
1544   // --------
1545   DBC_ENSURE(selected_audio_input_repp == 0);
1546   // --------
1547 }
1548 
1549 /**
1550  * Removes the selected audio output.
1551  *
1552  *  @pre is_selected() == true
1553  *  @pre connected_chainsetup() != selected_chainsetup()
1554  *  @pre get_audio_output() != 0
1555  *
1556  *  @post selected_audio_output_repp = 0
1557  */
remove_audio_output(void)1558 void ECA_CONTROL::remove_audio_output(void)
1559 {
1560   // --------
1561   DBC_REQUIRE(is_selected() == true);
1562   DBC_REQUIRE(connected_chainsetup() != selected_chainsetup());
1563   DBC_REQUIRE(get_audio_output() != 0);
1564   // --------
1565   ECA_LOG_MSG(ECA_LOGGER::info, "Removing selected audio output \"" + selected_audio_output_repp->label() +
1566 	      "\" from selected chains.");
1567   selected_chainsetup_repp->remove_audio_output(selected_audio_output_repp);
1568   selected_audio_output_repp = 0;
1569 
1570   // --------
1571   DBC_ENSURE(selected_audio_output_repp == 0);
1572   // --------
1573 }
1574 
1575 /**
1576  * Attaches selected audio input to selected chains
1577  *
1578  * @pre is_selected() == true
1579  * @pre connected_chainsetup() != selected_chainsetup()
1580  * @pre get_audio_input() != 0
1581  */
attach_audio_input(void)1582 void ECA_CONTROL::attach_audio_input(void)
1583 {
1584   // --------
1585   DBC_REQUIRE(is_selected() == true);
1586   DBC_REQUIRE(connected_chainsetup() != selected_chainsetup());
1587   DBC_REQUIRE(get_audio_input() != 0);
1588   // --------
1589   selected_chainsetup_repp->attach_input_to_selected_chains(selected_audio_input_repp);
1590 
1591   ECA_LOG_MSG(ECA_LOGGER::info, "Attached audio input \"" + selected_audio_input_repp->label() +
1592 	      "\" to selected chains.");
1593 }
1594 
1595 /**
1596  * Attaches selected audio output to selected chains
1597  *
1598  *  @pre is_selected() == true
1599  *  @pre connected_chainsetup() != selected_chainsetup()
1600  *  @pre get_audio_output() != 0
1601  */
attach_audio_output(void)1602 void ECA_CONTROL::attach_audio_output(void)
1603 {
1604   // --------
1605   DBC_REQUIRE(is_selected() == true);
1606   DBC_REQUIRE(connected_chainsetup() != selected_chainsetup());
1607   DBC_REQUIRE(get_audio_output() != 0);
1608   // --------
1609   selected_chainsetup_repp->attach_output_to_selected_chains(selected_audio_output_repp);
1610 
1611   ECA_LOG_MSG(ECA_LOGGER::info, "Attached audio output \"" + selected_audio_output_repp->label() +
1612 	      "\" to selected chains.");
1613 }
1614 
1615 /**
1616  * Get a string containing a comma separated list of all chains
1617  * attached to input with index 'aiod'.
1618  *
1619  * @pre is_selected() == true
1620  */
attached_chains_input(AUDIO_IO * aiod) const1621 string ECA_CONTROL::attached_chains_input(AUDIO_IO* aiod) const
1622 {
1623   // --------
1624   DBC_REQUIRE(is_selected() == true);
1625   // --------
1626 
1627   vector<string> t = selected_chainsetup_repp->get_attached_chains_to_input(aiod);
1628   string out = "";
1629   vector<string>::const_iterator p = t.begin();
1630   while(p != t.end()) {
1631     out += *p;
1632     ++p;
1633     if (p != t.end()) out += ",";
1634   }
1635   return out;
1636 }
1637 
1638 /**
1639  * Get a string containing a comma separated list of all chains
1640  * attached to output with index 'aiod'.
1641  *
1642  * @pre is_selected() == true
1643  */
attached_chains_output(AUDIO_IO * aiod) const1644 string ECA_CONTROL::attached_chains_output(AUDIO_IO* aiod) const
1645 {
1646   // --------
1647   DBC_REQUIRE(is_selected() == true);
1648   // --------
1649 
1650   vector<string> t = selected_chainsetup_repp->get_attached_chains_to_output(aiod);
1651   string out = "";
1652   vector<string>::const_iterator p = t.begin();
1653   while(p != t.end()) {
1654     out += *p;
1655     ++p;
1656     if (p != t.end()) out += ",";
1657   }
1658   return out;
1659 }
1660 
1661 /**
1662  * Get a string containing a comma separated list of all chains
1663  * attached to audio object with name 'filename'.
1664  *
1665  * @pre is_selected() == true
1666  */
attached_chains(const string & filename) const1667 vector<string> ECA_CONTROL::attached_chains(const string& filename) const
1668 {
1669   // --------
1670   DBC_REQUIRE(is_selected() == true);
1671   // --------
1672 
1673   return selected_chainsetup_repp->get_attached_chains_to_iodev(filename);
1674 }
1675 
1676 /**
1677  * Rewinds selected audio object by 'pos_in_seconds' seconds
1678  *
1679  *  @pre is_selected() == true
1680  *  @pre connected_chainsetup() != selected_chainsetup()
1681  *  @pre selected_audio_object_repp != 0
1682  */
rewind_audio_object(double seconds)1683 void ECA_CONTROL::rewind_audio_object(double seconds)
1684 {
1685   // --------
1686   DBC_REQUIRE(is_selected() == true);
1687   DBC_REQUIRE(connected_chainsetup() != selected_chainsetup());
1688   DBC_REQUIRE(get_audio_input() != 0 || get_audio_output() != 0);
1689   // --------
1690   selected_audio_object_repp->seek_position_in_seconds(selected_audio_object_repp->position_in_seconds_exact() - seconds);
1691 }
1692 
1693 /**
1694  * Forwards selected audio object by 'pos_in_seconds' seconds
1695  *
1696  *  @pre is_selected() == true
1697  *  @pre connected_chainsetup() != selected_chainsetup()
1698  *  @pre selected_audio_object_repp != 0
1699  */
forward_audio_object(double seconds)1700 void ECA_CONTROL::forward_audio_object(double seconds)
1701 {
1702   // --------
1703   DBC_REQUIRE(is_selected() == true);
1704   DBC_REQUIRE(connected_chainsetup() != selected_chainsetup());
1705   DBC_REQUIRE(get_audio_input() != 0 || get_audio_output() != 0);
1706   // --------
1707   selected_audio_object_repp->seek_position_in_seconds(selected_audio_object_repp->position_in_seconds_exact() + seconds);
1708 }
1709 
1710 /**
1711  * Sets position of selected audio object
1712  *
1713  * require:
1714  *  is_selected() == true
1715  *  connected_chainsetup() != selected_chainsetup()
1716  *  selected_audio_object_repp != 0
1717  */
set_audio_object_position(double seconds)1718 void ECA_CONTROL::set_audio_object_position(double seconds)
1719 {
1720   // --------
1721   DBC_REQUIRE(is_selected() == true);
1722   DBC_REQUIRE(connected_chainsetup() != selected_chainsetup());
1723   DBC_REQUIRE(get_audio_input() != 0 || get_audio_output() != 0);
1724   // --------
1725   selected_audio_object_repp->seek_position_in_seconds(seconds);
1726 }
1727 
1728 /**
1729  * Sets position of selected audio object
1730  *
1731  * @pre is_selected() == true
1732  * @pre connected_chainsetup() != selected_chainsetup()
1733  * @pre selected_audio_object_repp != 0
1734  */
set_audio_object_position_samples(SAMPLE_SPECS::sample_pos_t samples)1735 void ECA_CONTROL::set_audio_object_position_samples(SAMPLE_SPECS::sample_pos_t samples)
1736 {
1737   // --------
1738   DBC_REQUIRE(is_selected() == true);
1739   DBC_REQUIRE(connected_chainsetup() != selected_chainsetup());
1740   DBC_REQUIRE(get_audio_input() != 0 || get_audio_output() != 0);
1741   // --------
1742   selected_audio_object_repp->seek_position_in_samples(samples);
1743 }
1744 
1745 
1746 /**
1747  * Spawns an external wave editor for editing selected audio object.
1748  *
1749  * require:
1750  *  is_selected()
1751  *  connected_chainsetup() != selected_chainsetup()
1752  *  selected_audio_object_repp != 0
1753  */
wave_edit_audio_object(void)1754 void ECA_CONTROL::wave_edit_audio_object(void)
1755 {
1756   // --------
1757   DBC_REQUIRE(is_selected() == true);
1758   DBC_REQUIRE(connected_chainsetup() != selected_chainsetup());
1759   DBC_REQUIRE(get_audio_input() != 0 || get_audio_output() != 0);
1760   // --------
1761   string name = selected_audio_object_repp->label();
1762 
1763   int res = ::system(string(resource_value("ext-cmd-wave-editor") + " " + name).c_str());
1764   if (res == 127 || res == -1) {
1765     ECA_LOG_MSG(ECA_LOGGER::info, "Can't edit; unable to open wave editor \""
1766 		+ resource_value("x-wave-editor") + "\".");
1767   }
1768 }
1769 
1770 /**
1771  * Adds a new chain operator
1772  *
1773  * @param gcontrol_params is an Ecasound option string describing
1774  *        a controlled: syntax is either "-<id_string>:par1,...,parN",
1775  *        or just "<id_string>:par1,...,parN"
1776  *
1777  * require:
1778  *  is_selected() == true
1779  *  selected_chains().size() == 1
1780  */
add_chain_operator(const string & chainop_params)1781 void ECA_CONTROL::add_chain_operator(const string& chainop_params)
1782 {
1783   // --------
1784   DBC_REQUIRE(is_selected() == true);
1785   DBC_REQUIRE(selected_chains().size() == 1);
1786   // --------
1787 
1788   ECA::chainsetup_edit_t edit;
1789   edit.type = ECA::edit_cop_add;
1790   edit.cs_ptr = selected_chainsetup_repp;
1791 
1792   unsigned int p = selected_chainsetup_repp->first_selected_chain();
1793   if (p < selected_chainsetup_repp->chains.size()) {
1794     /* note: unlike all other functions, first_selected_chain()
1795      *       returns 0..N */
1796     edit.m.c_generic_param.chain = p + 1;
1797     edit.param = chainop_params;
1798     edit.need_chain_reinit = true;
1799     execute_edit_on_selected(edit);
1800   }
1801 }
1802 
1803 /**
1804  * Adds a new chain operator. Pointer given as argument
1805  * will remain to be usable, but notice that it is
1806  * _NOT_ thread-safe to use assigned/registered objects
1807  * from client programs. You must be sure that ecasound
1808  * isn't using the same object as you are. The
1809  * easiest way to assure this is to disconnect
1810  * the chainsetup to which object is attached.
1811  *
1812  * require:
1813  *  is_selected() == true
1814  *  connected_chainsetup() != selected_chainsetup()
1815  *  selected_chains().size() == 1
1816  *  cotmp != 0
1817  */
add_chain_operator(CHAIN_OPERATOR * cotmp)1818 void ECA_CONTROL::add_chain_operator(CHAIN_OPERATOR* cotmp)
1819 {
1820   // --------
1821   DBC_REQUIRE(is_selected() == true);
1822   DBC_REQUIRE(selected_chains().size() == 1);
1823   DBC_REQUIRE(cotmp != 0);
1824   // --------
1825 
1826   selected_chainsetup_repp->add_chain_operator(cotmp);
1827 }
1828 
1829 /**
1830  * Returns a pointer to the the selected chain operator. If no chain
1831  * operator is selected, 0 is returned.
1832  *
1833  * require:
1834  *  is_selected() == true
1835  *  selected_chains().size() == 1
1836  */
get_chain_operator(void) const1837 const CHAIN_OPERATOR* ECA_CONTROL::get_chain_operator(void) const
1838 {
1839   // --------
1840   DBC_REQUIRE(is_selected() == true);
1841   DBC_REQUIRE(selected_chains().size() == 1);
1842   // --------
1843 
1844   if (is_selected() == true) {
1845     unsigned int p = selected_chainsetup_repp->first_selected_chain();
1846     if (p < selected_chainsetup_repp->chains.size())
1847       return  selected_chainsetup_repp->chains[p]->get_selected_chain_operator();
1848   }
1849 
1850   return 0;
1851 }
1852 
1853 /**
1854  * Returns a list of chain operator names.
1855  *
1856  * require:
1857  *  is_selected() == true
1858  *  selected_chains().size() == 1
1859  */
chain_operator_names(void) const1860 std::vector<string> ECA_CONTROL::chain_operator_names(void) const
1861 {
1862   // --------
1863   DBC_REQUIRE(is_selected() == true);
1864   DBC_REQUIRE(selected_chains().size() == 1);
1865   // --------
1866 
1867   std::vector<string> result;
1868   unsigned int p = selected_chainsetup_repp->first_selected_chain();
1869   if (p < selected_chainsetup_repp->chains.size()) {
1870     CHAIN* selected_chain = selected_chainsetup_repp->chains[p];
1871     int save_selected_cop = selected_chain->selected_chain_operator();
1872     for(int n = 0;
1873 	n < selected_chain->number_of_chain_operators();
1874 	n++) {
1875       selected_chain->select_chain_operator(n + 1);
1876       result.push_back(selected_chain->chain_operator_name());
1877     }
1878     selected_chain->select_chain_operator(save_selected_cop);
1879   }
1880   return result;
1881 }
1882 
1883 /**
1884  * Returns the index of the selected chain operator. If no chain
1885  * operator is selected, -1 is returned.
1886  *
1887  * require:
1888  *  is_selected() == true
1889  *  selected_chains().size() == 1
1890  */
selected_chain_operator(void) const1891 int ECA_CONTROL::selected_chain_operator(void) const
1892 {
1893   // --------
1894   DBC_REQUIRE(is_selected() == true);
1895   DBC_REQUIRE(selected_chains().size() == 1);
1896   // --------
1897 
1898   unsigned int p = selected_chainsetup_repp->first_selected_chain();
1899   if (p < selected_chainsetup_repp->chains.size())
1900     return  selected_chainsetup_repp->chains[p]->selected_chain_operator();
1901 
1902   return -1;
1903 }
1904 
1905 /**
1906  * Removes the selected chain operator
1907  *
1908  * require:
1909  *  is_selected() == true
1910  *  connected_chainsetup() != selected_chainsetup()
1911  *  selected_chains().size() == 1
1912  */
remove_chain_operator(void)1913 void ECA_CONTROL::remove_chain_operator(void)
1914 {
1915   // --------
1916   DBC_REQUIRE(is_selected() == true);
1917   DBC_REQUIRE(selected_chainsetup() != connected_chainsetup());
1918   DBC_REQUIRE(selected_chains().size() == 1);
1919   // --------
1920 
1921   unsigned int p = selected_chainsetup_repp->first_selected_chain();
1922   if (p < selected_chainsetup_repp->chains.size())
1923     selected_chainsetup_repp->chains[p]->remove_chain_operator(-1);
1924 }
1925 
1926 /**
1927  * Selects chain operator 'chainop_id'.
1928  *
1929  * require:
1930  *  is_selected() == true
1931  *  selected_chains().size() == 1
1932  *  chainop_id > 0
1933  */
select_chain_operator(int chainop_id)1934 void ECA_CONTROL::select_chain_operator(int chainop_id)
1935 {
1936   // --------
1937   DBC_REQUIRE(is_selected() == true);
1938   DBC_REQUIRE(selected_chains().size() == 1);
1939   DBC_REQUIRE(chainop_id > 0);
1940   // --------
1941 
1942   unsigned int p = selected_chainsetup_repp->first_selected_chain();
1943   if (p < selected_chainsetup_repp->chains.size()) {
1944     CHAIN *ch = selected_chainsetup_repp->chains[p];
1945     if (chainop_id < ch->number_of_chain_operators() + 1) {
1946       ch->select_chain_operator(chainop_id);
1947     }
1948   }
1949 }
1950 
1951 /**
1952  * Returns a list of chain operator parameter names.
1953  *
1954  * require:
1955  *  is_selected  == true
1956  *  selected_chains().size() == 1
1957  */
chain_operator_parameter_names(void) const1958 std::vector<string> ECA_CONTROL::chain_operator_parameter_names(void) const
1959 {
1960   // --------
1961   DBC_REQUIRE(is_selected() == true);
1962   DBC_REQUIRE(selected_chains().size() == 1);
1963   // --------
1964 
1965   std::vector<string> result;
1966   unsigned int p = selected_chainsetup_repp->first_selected_chain();
1967   if (p < selected_chainsetup_repp->chains.size()) {
1968     CHAIN* selected_chain = selected_chainsetup_repp->chains[p];
1969 
1970     if (selected_chain->selected_chain_operator() > 0) {
1971 
1972       int save_selected_copp = selected_chain->selected_chain_operator_parameter();
1973 
1974       for(int n = 0;
1975 	  n < selected_chain->number_of_chain_operator_parameters();
1976 	  n++) {
1977 	selected_chain->select_chain_operator_parameter(n + 1);
1978 	result.push_back(selected_chain->chain_operator_parameter_name());
1979       }
1980 
1981       selected_chain->select_chain_operator_parameter(save_selected_copp);
1982     }
1983   }
1984   return result;
1985 }
1986 
1987 /**
1988  * Selects chain operator parameter 'param'.
1989  *
1990  * require:
1991  *  is_selected() == true
1992  *  selected_chains().size() == 1
1993  *  get_chain_operator() != 0
1994  *  param > 0
1995  */
select_chain_operator_parameter(int param)1996 void ECA_CONTROL::select_chain_operator_parameter(int param)
1997 {
1998   // --------
1999   DBC_REQUIRE(is_selected() == true);
2000   DBC_REQUIRE(selected_chains().size() == 1);
2001   DBC_REQUIRE(param > 0);
2002   // --------
2003 
2004   unsigned int p = selected_chainsetup_repp->first_selected_chain();
2005   if (p < selected_chainsetup_repp->chains.size()) {
2006     selected_chainsetup_repp->chains[p]->select_chain_operator_parameter(param);
2007   }
2008 }
2009 
2010 
2011 /**
2012  * Sets chain operator parameter value
2013  *
2014  * require:
2015  *  is_selected() == true
2016  *  selected_chains().size() == 1
2017  *  get_chain_operator() != 0
2018  */
set_chain_operator_parameter(CHAIN_OPERATOR::parameter_t value)2019 void ECA_CONTROL::set_chain_operator_parameter(CHAIN_OPERATOR::parameter_t value)
2020 {
2021   // --------
2022   DBC_REQUIRE(is_selected() == true);
2023   DBC_REQUIRE(selected_chains().size() == 1);
2024   DBC_REQUIRE(get_chain_operator() != 0);
2025   // --------
2026 
2027   ECA::chainsetup_edit_t edit;
2028   edit.type = ECA::edit_cop_set_param;
2029   edit.cs_ptr = selected_chainsetup_repp;
2030   edit.need_chain_reinit = false;
2031 
2032   unsigned int p = selected_chainsetup_repp->first_selected_chain();
2033   if (p < selected_chainsetup_repp->chains.size()) {
2034     /* note: unlike all other functions, first_selected_chain()
2035      *       returns 0..N */
2036     CHAIN *chain = selected_chainsetup_repp->chains[p];
2037     edit.m.cop_set_param.chain = p + 1;
2038     edit.m.cop_set_param.op = chain->selected_chain_operator();
2039     edit.m.cop_set_param.param = chain->selected_chain_operator_parameter();
2040     edit.m.cop_set_param.value = value;
2041 
2042     execute_edit_on_selected(edit);
2043   }
2044 }
2045 
2046 /**
2047  * Parsers input parameter to bypass/mute/etc toggles.
2048  *
2049  * if (str == "on") return 1
2050  * if (str == "off") return 0
2051  * if (str == "toggle") return -1
2052  * return -1
2053  */
priv_onofftoggle_to_int(const string & str)2054 static int priv_onofftoggle_to_int(const string& str)
2055 {
2056   if (str == "on") return 1;
2057   if (str == "off") return 0;
2058   return -1;
2059 }
2060 
2061 /**
2062  * Toggles whether chain is muted or not
2063  *
2064  * require:
2065  *  is_selected() == true
2066  *  selected_chains().size() > 0
2067  */
set_chain_muting(const string & arg)2068 void ECA_CONTROL::set_chain_muting(const string &arg)
2069 {
2070   // --------
2071   DBC_REQUIRE(is_selected() == true);
2072   DBC_REQUIRE(selected_chains().size() > 0);
2073   // --------
2074 
2075   ECA::chainsetup_edit_t edit;
2076   edit.type = ECA::edit_c_muting;
2077   edit.cs_ptr = selected_chainsetup_repp;
2078   edit.need_chain_reinit = false;
2079 
2080   int state_arg = priv_onofftoggle_to_int(arg);
2081 
2082   unsigned int p = selected_chainsetup_repp->first_selected_chain();
2083   if (p < selected_chainsetup_repp->chains.size()) {
2084     edit.m.c_muting.chain = p + 1;
2085     edit.m.c_muting.val = state_arg;
2086     execute_edit_on_selected(edit);
2087   }
2088 }
2089 
2090 /**
2091  * Toggles whether chain operators are enabled or disabled
2092  *
2093  * require:
2094  *  is_selected() == true
2095  *  selected_chains().size() > 0
2096  */
set_chain_bypass(const string & arg)2097 void ECA_CONTROL::set_chain_bypass(const string& arg)
2098 {
2099   // --------
2100   DBC_REQUIRE(is_selected() == true);
2101   DBC_REQUIRE(selected_chains().size() > 0);
2102   // --------
2103 
2104   ECA::chainsetup_edit_t edit;
2105   edit.type = ECA::edit_c_bypass;
2106   edit.cs_ptr = selected_chainsetup_repp;
2107   edit.need_chain_reinit = false;
2108 
2109   int state_arg = priv_onofftoggle_to_int(arg);
2110 
2111   unsigned int p = selected_chainsetup_repp->first_selected_chain();
2112   if (p < selected_chainsetup_repp->chains.size()) {
2113     edit.m.c_bypass.chain = p + 1;
2114     edit.m.c_bypass.val = state_arg;
2115     execute_edit_on_selected(edit);
2116   }
2117 }
2118 
2119 /**
2120  * Modify chain operator bypass state
2121  *
2122  * require:
2123  *  is_selected() == true
2124  *  selected_chains().size() == 1
2125  *  get_chain_operator() != 0
2126  */
bypass_chain_operator(const string & arg)2127 void ECA_CONTROL::bypass_chain_operator(const string& arg)
2128 {
2129   // --------
2130   DBC_REQUIRE(is_selected() == true);
2131   DBC_REQUIRE(selected_chains().size() == 1);
2132   DBC_REQUIRE(get_chain_operator() != 0);
2133   // --------
2134 
2135   ECA::chainsetup_edit_t edit;
2136   edit.type = ECA::edit_cop_bypass;
2137   edit.cs_ptr = selected_chainsetup_repp;
2138   edit.need_chain_reinit = false;
2139 
2140   int bypass_arg = priv_onofftoggle_to_int(arg);
2141 
2142   unsigned int p = selected_chainsetup_repp->first_selected_chain();
2143   if (p < selected_chainsetup_repp->chains.size()) {
2144     /* note: unlike all other functions, first_selected_chain()
2145      *       returns 0..N */
2146     CHAIN *chain = selected_chainsetup_repp->chains[p];
2147     edit.m.cop_bypass.chain = p + 1;
2148     edit.m.cop_bypass.op = chain->selected_chain_operator();
2149     edit.m.cop_bypass.bypass = bypass_arg;
2150 
2151     execute_edit_on_selected(edit);
2152 
2153     ECA_LOG_MSG(ECA_LOGGER::user_objects, "Set bypass of chain "
2154 		+ kvu_numtostr(p) + " op "
2155 		+ kvu_numtostr(edit.m.cop_bypass.op) + " to "
2156 		+ kvu_numtostr(bypass_arg));
2157   }
2158 }
2159 
2160 /**
2161  * Returns true if selected chain op is bypasssed
2162  *
2163  * require:
2164  *  is_selected() == true
2165  *  selected_chains().size() == 1
2166  *  get_chain_operator() != 0
2167  */
chain_operator_is_bypassed(void) const2168 bool ECA_CONTROL::chain_operator_is_bypassed(void) const
2169 {
2170   // --------
2171   DBC_REQUIRE(is_selected() == true);
2172   DBC_REQUIRE(selected_chains().size() == 1);
2173   DBC_REQUIRE(get_chain_operator() != 0);
2174   // --------
2175 
2176   int op_index = selected_chain_operator();
2177   CHAIN* c = get_chain_priv();
2178   if (c != 0) {
2179     return c->is_operator_bypassed(op_index);
2180   }
2181   return false;
2182 }
2183 
2184 /**
2185  * Returns true if selected chain is bypassed
2186  */
chain_is_bypassed(void) const2187 bool ECA_CONTROL::chain_is_bypassed(void) const
2188 {
2189   // --------
2190   DBC_REQUIRE(is_selected() == true);
2191   DBC_REQUIRE(selected_chains().size() == 1);
2192   // --------
2193 
2194   CHAIN* c = get_chain_priv();
2195   if (c != 0) {
2196     return c->is_bypassed();
2197   }
2198   return false;
2199 }
2200 
2201 /**
2202  * Returns true if selected chain is bypassed
2203  */
chain_is_muted(void) const2204 bool ECA_CONTROL::chain_is_muted(void) const
2205 {
2206   // --------
2207   DBC_REQUIRE(is_selected() == true);
2208   DBC_REQUIRE(selected_chains().size() == 1);
2209   // --------
2210 
2211   CHAIN* c = get_chain_priv();
2212   if (c != 0) {
2213     return c->is_muted();
2214   }
2215   return false;
2216 }
2217 
2218 /**
2219  * Returns the selected chain operator parameter value
2220  *
2221  * require:
2222  *  is_selected() == true
2223  *  selected_chains().size() == 1
2224  */
get_chain_operator_parameter(void) const2225 CHAIN_OPERATOR::parameter_t ECA_CONTROL::get_chain_operator_parameter(void) const
2226 {
2227   // --------
2228   DBC_REQUIRE(is_selected() == true);
2229   DBC_REQUIRE(selected_chains().size() == 1);
2230   // --------
2231 
2232   unsigned int p = selected_chainsetup_repp->first_selected_chain();
2233   if (p < selected_chainsetup_repp->chains.size()) {
2234     if (selected_chainsetup_repp->chains[p]->selected_chain_operator() > 0 &&
2235 	selected_chainsetup_repp->chains[p]->selected_chain_operator_parameter() > 0)
2236       return selected_chainsetup_repp->chains[p]->get_parameter();
2237   }
2238   return 0.0f;
2239 }
2240 
2241 /**
2242  * Returns the index number of selected  chain operator parameter.
2243  * If no parameter is selected, 0 is returned.
2244  *
2245  * require:
2246  *  is_selected() == true
2247  *  selected_chains().size() == 1
2248  *  get_chain_operator() != 0
2249  */
selected_chain_operator_parameter(void) const2250 int ECA_CONTROL::selected_chain_operator_parameter(void) const
2251 {
2252   // --------
2253   DBC_REQUIRE(is_selected() == true);
2254   DBC_REQUIRE(selected_chains().size() == 1);
2255   DBC_REQUIRE(get_chain_operator() != 0);
2256   // --------
2257 
2258   unsigned int p = selected_chainsetup_repp->first_selected_chain();
2259   if (p < selected_chainsetup_repp->chains.size()) {
2260     return selected_chainsetup_repp->chains[p]->selected_chain_operator_parameter();
2261   }
2262   return 0;
2263 }
2264 
2265 /**
2266  * Adds a new controller
2267  *
2268  * @param gcontrol_params is an Ecasound option string describing
2269  *        a controlled: syntax is either "-<id_string>:par1,...,parN",
2270  *        or just "<id_string>:par1,...,parN"
2271  *
2272  * require:
2273  *  is_selected() == true
2274  *  connected_chainsetup() != selected_chainsetup()
2275  *  selected_chains().size() > 0
2276  */
add_controller(const string & gcontrol_params)2277 void ECA_CONTROL::add_controller(const string& gcontrol_params)
2278 {
2279   // --------
2280   DBC_REQUIRE(is_selected() == true);
2281   DBC_REQUIRE(selected_chains().size() > 0);
2282   // --------
2283 
2284   ECA::chainsetup_edit_t edit;
2285   edit.type = ECA::edit_ctrl_add;
2286   edit.cs_ptr = selected_chainsetup_repp;
2287 
2288   unsigned int p = selected_chainsetup_repp->first_selected_chain();
2289   if (p < selected_chainsetup_repp->chains.size()) {
2290     /* note: unlike all other functions, first_selected_chain()
2291      *       returns 0..N */
2292     edit.m.c_generic_param.chain = p + 1;
2293     edit.param = gcontrol_params;
2294     edit.need_chain_reinit = true;
2295     execute_edit_on_selected(edit);
2296   }
2297 }
2298 
2299 /**
2300  * Selects the Nth controller.
2301  *
2302  * require:
2303  *  is_selected() == true
2304  *  connected_chainsetup() != selected_chainsetup()
2305  *  selected_chains().size() == 1
2306  *  controller_id > 0
2307  */
select_controller(int controller_id)2308 void ECA_CONTROL::select_controller(int controller_id)
2309 {
2310   // --------
2311   DBC_REQUIRE(is_selected() == true);
2312   DBC_REQUIRE(selected_chains().size() == 1);
2313   DBC_REQUIRE(controller_id > 0);
2314   // --------
2315 
2316   unsigned int p = selected_chainsetup_repp->first_selected_chain();
2317   if (p < selected_chainsetup_repp->chains.size()) {
2318     CHAIN *ch = selected_chainsetup_repp->chains[p];
2319     if (controller_id < ch->number_of_controllers() + 1) {
2320       selected_chainsetup_repp->chains[p]->select_controller(controller_id);
2321     }
2322   }
2323 }
2324 
2325 /**
2326  * Removes the selected controller.
2327  *
2328  * require:
2329  *  is_selected() == true
2330  *  connected_chainsetup() != selected_chainsetup()
2331  *  selected_chains().size() == 1
2332  *  get_controller() != 0
2333  */
remove_controller(void)2334 void ECA_CONTROL::remove_controller(void)
2335 {
2336   // --------
2337   DBC_REQUIRE(is_selected() == true);
2338   DBC_REQUIRE(selected_chainsetup() != connected_chainsetup());
2339   DBC_REQUIRE(selected_chains().size() == 1);
2340   DBC_REQUIRE(get_controller() != 0);
2341   // --------
2342 
2343   unsigned int p = selected_chainsetup_repp->first_selected_chain();
2344   if (p < selected_chainsetup_repp->chains.size()) {
2345     selected_chainsetup_repp->chains[p]->remove_controller();
2346   }
2347 }
2348 
2349 /**
2350  * Returns a list of controller names.
2351  *
2352  * require:
2353  *  is_selected() == true
2354  *  selected_chains().size() == 1
2355  */
controller_names(void) const2356 std::vector<string> ECA_CONTROL::controller_names(void) const
2357 {
2358   // --------
2359   DBC_REQUIRE(is_selected() == true);
2360   DBC_REQUIRE(selected_chains().size() == 1);
2361   // --------
2362 
2363   std::vector<string> result;
2364   unsigned int p = selected_chainsetup_repp->first_selected_chain();
2365   if (p < selected_chainsetup_repp->chains.size()) {
2366     CHAIN* selected_chain = selected_chainsetup_repp->chains[p];
2367     int save_selected_ctrl = selected_chain->selected_controller();
2368     for(int n = 0;
2369 	n < selected_chain->number_of_controllers();
2370 	n++) {
2371       selected_chain->select_controller(n + 1);
2372       result.push_back(selected_chain->controller_name());
2373     }
2374     selected_chain->select_controller(save_selected_ctrl);
2375   }
2376   return result;
2377 }
2378 
2379 /**
2380  * Returns a pointer to the selected controller. If no controller is
2381  * selected, 0 is returned.
2382  *
2383  * require:
2384  *  is_selected() == true
2385  *  selected_chains().size() == 1
2386  */
get_controller(void) const2387 const GENERIC_CONTROLLER* ECA_CONTROL::get_controller(void) const
2388 {
2389   // --------
2390   DBC_REQUIRE(is_selected() == true);
2391   DBC_REQUIRE(selected_chains().size() == 1);
2392   // --------
2393 
2394   unsigned int p = selected_chainsetup_repp->first_selected_chain();
2395   if (p < selected_chainsetup_repp->chains.size())
2396     return  selected_chainsetup_repp->chains[p]->get_selected_controller();
2397 
2398   return 0;
2399 }
2400 
2401 /**
2402  * Returns the index number of the selected controller. If no controller is
2403  * selected, 0 is returned.
2404  *
2405  * require:
2406  *  is_selected() == true
2407  *  selected_chains().size() == 1
2408  */
selected_controller(void) const2409 int ECA_CONTROL::selected_controller(void) const
2410 {
2411   // --------
2412   DBC_REQUIRE(is_selected() == true);
2413   DBC_REQUIRE(selected_chains().size() == 1);
2414   // --------
2415 
2416   unsigned int p = selected_chainsetup_repp->first_selected_chain();
2417   if (p < selected_chainsetup_repp->chains.size())
2418     return  selected_chainsetup_repp->chains[p]->selected_controller();
2419 
2420   return 0;
2421 }
2422 
2423 /**
2424  * Returns a list of the selected controller's parameter names
2425  *
2426  * require:
2427  *  is_selected() == true
2428  *  selected_chains().size() == 1
2429  *  get_controller() != 0
2430  */
controller_parameter_names(void) const2431 std::vector<string> ECA_CONTROL::controller_parameter_names(void) const
2432 {
2433   // --------
2434   DBC_REQUIRE(is_selected() == true);
2435   DBC_REQUIRE(selected_chains().size() == 1);
2436   DBC_REQUIRE(get_controller() != 0);
2437   // --------
2438 
2439   std::vector<string> result;
2440   unsigned int p = selected_chainsetup_repp->first_selected_chain();
2441   if (p < selected_chainsetup_repp->chains.size()) {
2442     CHAIN* selected_chain = selected_chainsetup_repp->chains[p];
2443     int save_selected_ctrlp = selected_chain->selected_controller_parameter();
2444 
2445     for(int n = 0;
2446 	  n < selected_chain->number_of_controller_parameters();
2447 	  n++) {
2448 	selected_chain->select_controller_parameter(n + 1);
2449 	result.push_back(selected_chain->controller_parameter_name());
2450     }
2451 
2452     selected_chain->select_controller_parameter(save_selected_ctrlp);
2453   }
2454   return result;
2455 
2456 }
2457 
2458 /**
2459  * Select a particular controller parameter
2460  *
2461  * require:
2462  *  is_selected() == true
2463  *  selected_chains().size() == 1
2464  *  param > 0
2465  */
select_controller_parameter(int param)2466 void ECA_CONTROL::select_controller_parameter(int param)
2467 {
2468   // --------
2469   DBC_REQUIRE(is_selected() == true);
2470   DBC_REQUIRE(selected_chains().size() == 1);
2471   DBC_REQUIRE(param > 0);
2472   // --------
2473 
2474   unsigned int p = selected_chainsetup_repp->first_selected_chain();
2475   if (p < selected_chainsetup_repp->chains.size()) {
2476     selected_chainsetup_repp->chains[p]->select_controller_parameter(param);
2477   }
2478 }
2479 
2480 /**
2481  * Returns the index number of selected  controller parameter.
2482  * If no parameter is selected, 0 is returned.
2483  *
2484  * require:
2485  *  is_selected() == true
2486  *  selected_chains().size() == 1
2487  *  get_controller() != 0
2488  */
selected_controller_parameter(void) const2489 int ECA_CONTROL::selected_controller_parameter(void) const
2490 {
2491   // --------
2492   DBC_REQUIRE(is_selected() == true);
2493   DBC_REQUIRE(selected_chains().size() == 1);
2494   DBC_REQUIRE(get_controller() != 0);
2495   // --------
2496 
2497   unsigned int p = selected_chainsetup_repp->first_selected_chain();
2498   if (p < selected_chainsetup_repp->chains.size()) {
2499     return selected_chainsetup_repp->chains[p]->selected_controller_parameter();
2500   }
2501   return 0;
2502 }
2503 
2504 
2505 /**
2506  * Set the currently selected controller parameter
2507  *
2508  * require:
2509  *  is_selected() == true
2510  *  selected_chains().size() == 1
2511  *  get_controller() != 0
2512  */
set_controller_parameter(CHAIN_OPERATOR::parameter_t value)2513 void ECA_CONTROL::set_controller_parameter(CHAIN_OPERATOR::parameter_t value)
2514 {
2515   // --------
2516   DBC_REQUIRE(is_selected() == true);
2517   DBC_REQUIRE(selected_chains().size() == 1);
2518   DBC_REQUIRE(get_controller() != 0);
2519   // --------
2520 
2521   ECA::chainsetup_edit_t edit;
2522   edit.type = ECA::edit_ctrl_set_param;
2523   edit.cs_ptr = selected_chainsetup_repp;
2524   edit.need_chain_reinit = false;
2525 
2526   unsigned int p = selected_chainsetup_repp->first_selected_chain();
2527   if (p < selected_chainsetup_repp->chains.size()) {
2528     /* note: unlike all other functions, first_selected_chain()
2529      *       returns 0..N */
2530     CHAIN *chain = selected_chainsetup_repp->chains[p];
2531     edit.m.ctrl_set_param.chain = p + 1;
2532     edit.m.ctrl_set_param.op = chain->selected_controller();
2533     edit.m.ctrl_set_param.param = chain->selected_controller_parameter();
2534     edit.m.ctrl_set_param.value = value;
2535 
2536     execute_edit_on_selected(edit);
2537   }
2538 }
2539 
2540 /**
2541  * Returns the value of the currently selected controller parameter
2542  * If no controller or controller parameter is selected, 0.0 is returned.
2543  *
2544  * require:
2545  *  is_selected() == true
2546  *  selected_chains().size() == 1
2547  */
get_controller_parameter(void) const2548 CONTROLLER_SOURCE::parameter_t ECA_CONTROL::get_controller_parameter(void) const
2549 {
2550   // --------
2551   DBC_REQUIRE(is_selected() == true);
2552   DBC_REQUIRE(selected_chains().size() == 1);
2553   // --------
2554 
2555   unsigned int p = selected_chainsetup_repp->first_selected_chain();
2556   if (p < selected_chainsetup_repp->chains.size()) {
2557     if (selected_chainsetup_repp->chains[p]->selected_controller() > 0 &&
2558 	selected_chainsetup_repp->chains[p]->selected_controller_parameter() > 0)
2559       return selected_chainsetup_repp->chains[p]->get_controller_parameter();
2560   }
2561   return 0.0f;
2562 }
2563 
2564 /**
2565  * Returns the index number of chain operator that is the target for the currently selected
2566  * controller.
2567  *
2568  * require:
2569  *  is_selected() == true
2570  *  selected_chains().size() == 1
2571  *  get_controller() != 0
2572  */
selected_controller_target(void) const2573 int ECA_CONTROL::selected_controller_target(void) const
2574 {
2575   // --------
2576   DBC_REQUIRE(is_selected() == true);
2577   DBC_REQUIRE(selected_chains().size() == 1);
2578   DBC_REQUIRE(get_controller() != 0);
2579   // --------
2580 
2581   /*
2582   We find the index of the chain_op that corresponds to the the selected controller's target
2583   by looping through the chain ops and comparing the value of the chain operator (a CHAIN_OPERATOR*)
2584   with the value of the controllers "target_pointer()".
2585   */
2586   unsigned int p = selected_chainsetup_repp->first_selected_chain();
2587   int result = 0;
2588   if (p < selected_chainsetup_repp->chains.size()) {
2589 
2590     CHAIN* selected_chain = selected_chainsetup_repp->chains[p];
2591     OPERATOR* target = selected_chain->get_selected_controller()->target_pointer();
2592     for(int n = 0;
2593 	  n < selected_chain->number_of_chain_operators();
2594 	  n++) {
2595 
2596       if (selected_chain->get_chain_operator(n) == target) {
2597         result = n+1;
2598         break;
2599       }
2600     }
2601   }
2602   return result;
2603 }
2604 
2605