~ubuntu-branches/ubuntu/feisty/ecasound2.2/feisty

« back to all changes in this revision

Viewing changes to libecasound/eca-session.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Junichi Uekawa
  • Date: 2005-04-14 09:15:48 UTC
  • Revision ID: james.westby@ubuntu.com-20050414091548-o7kgb47z0tcunh0s
Tags: upstream-2.4.1
ImportĀ upstreamĀ versionĀ 2.4.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// ------------------------------------------------------------------------
 
2
// eca-session.cpp: Ecasound runtime setup and parameters.
 
3
// Copyright (C) 1999-2004 Kai Vehmanen
 
4
//
 
5
// Attributes:
 
6
//     eca-style-version: 3
 
7
//
 
8
// This program is fre software; you can redistribute it and/or modify
 
9
// it under the terms of the GNU General Public License as published by
 
10
// the Free Software Foundation; either version 2 of the License, or
 
11
// (at your option) any later version.
 
12
// 
 
13
// This program is distributed in the hope that it will be useful,
 
14
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
// GNU General Public License for more details.
 
17
// 
 
18
// You should have received a copy of the GNU General Public License
 
19
// along with this program; if not, write to the Free Software
 
20
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 
21
// ------------------------------------------------------------------------
 
22
 
 
23
#include <string>
 
24
#include <cstring>
 
25
#include <algorithm>
 
26
#include <vector>
 
27
#include <iostream>
 
28
 
 
29
#ifdef HAVE_CONFIG_H
 
30
#include <config.h>
 
31
#endif
 
32
 
 
33
#include <kvu_utils.h>
 
34
#include <kvu_com_line.h>
 
35
#include <kvu_message_item.h>
 
36
#include <kvu_dbc.h>
 
37
 
 
38
#include "eca-resources.h"
 
39
#include "eca-version.h"
 
40
 
 
41
#include "eca-chain.h"
 
42
#include "audiofx.h"
 
43
#include "audioio.h"
 
44
#include "audioio-mp3.h"
 
45
#include "audioio-mikmod.h"
 
46
#include "audioio-timidity.h"
 
47
#include "audioio-ogg.h"
 
48
#include "audioio-flac.h"
 
49
#include "audioio-aac.h"
 
50
 
 
51
#include "osc-gen-file.h"
 
52
 
 
53
#include "eca-error.h"
 
54
#include "eca-logger.h"
 
55
#include "eca-logger.h"
 
56
#include "eca-session.h"
 
57
#include "eca-chainsetup.h"
 
58
 
 
59
using std::string;
 
60
using std::vector;
 
61
 
 
62
ECA_SESSION::ECA_SESSION(void)
 
63
{
 
64
  ECA_LOG_MSG(ECA_LOGGER::subsystems, "Session created (empty)");
 
65
  set_defaults();
 
66
}
 
67
 
 
68
ECA_SESSION::~ECA_SESSION(void)
 
69
{
 
70
  // ECA_LOG_MSG(ECA_LOGGER::system_objects,"ECA_SESSION destructor!");
 
71
 
 
72
  for(std::vector<ECA_CHAINSETUP*>::iterator q = chainsetups_rep.begin(); q != chainsetups_rep.end(); q++) {
 
73
    delete *q;
 
74
  }
 
75
}
 
76
 
 
77
ECA_SESSION::ECA_SESSION(COMMAND_LINE& cline) throw(ECA_ERROR&)
 
78
{
 
79
  int errors = 0;
 
80
 
 
81
  set_defaults();
 
82
 
 
83
  cline.combine();
 
84
 
 
85
  std::vector<std::string> options,csoptions;
 
86
  create_chainsetup_options(cline, &options);
 
87
  preprocess_options(&options);
 
88
  errors += interpret_general_options(options,&csoptions);
 
89
 
 
90
  /* NOTE: must be printed after general options are parsed 
 
91
  *        in case user has specified -q (quiet operation) */
 
92
  ECA_LOG_MSG(ECA_LOGGER::subsystems, "Session created");
 
93
 
 
94
  if (errors > 0) {
 
95
    throw(ECA_ERROR("ECA-SESSION", "Errors parsing session-level options. Unable to create session."));
 
96
  }
 
97
 
 
98
  if (chainsetups_rep.size() == 0) {
 
99
    /* Try to create a valid chainsetup from the options given
 
100
     * on the command-line. */
 
101
 
 
102
    ECA_CHAINSETUP* comline_setup = new ECA_CHAINSETUP(csoptions);
 
103
 
 
104
    if (comline_setup->interpret_result() != true) {
 
105
      string temp = comline_setup->interpret_result_verbose();
 
106
      delete comline_setup;
 
107
      // std::cerr << "EXCEPTION DETECTED:'" << temp << "'. Core dump follows if you've compiled with gcc-3.3...\n";
 
108
      throw(ECA_ERROR("ECA-SESSION", temp));
 
109
    }
 
110
    else {
 
111
      add_chainsetup(comline_setup); /* ownership object transfered */
 
112
      if (selected_chainsetup_repp == 0) {
 
113
        /* adding the chainsetup failed */
 
114
        delete comline_setup;
 
115
      }
 
116
      else if (selected_chainsetup_repp->is_valid() != true) {
 
117
        ECA_LOG_MSG(ECA_LOGGER::info, "Note! Unable to create a valid chainsetup from the command-line arguments.");
 
118
      }
 
119
    }
 
120
  }
 
121
}
 
122
 
 
123
void ECA_SESSION::set_defaults(void)
 
124
{
 
125
  connected_chainsetup_repp = 0;
 
126
  selected_chainsetup_repp = 0;
 
127
 
 
128
  // ---
 
129
  // Interpret resources 
 
130
 
 
131
  ECA_RESOURCES ecaresources;
 
132
 
 
133
  MP3FILE::set_input_cmd(ecaresources.resource("ext-cmd-mp3-input"));
 
134
  MP3FILE::set_output_cmd(ecaresources.resource("ext-cmd-mp3-output"));
 
135
  MIKMOD_INTERFACE::set_mikmod_cmd(ecaresources.resource("ext-cmd-mikmod"));
 
136
  TIMIDITY_INTERFACE::set_timidity_cmd(ecaresources.resource("ext-cmd-timidity"));
 
137
  OGG_VORBIS_INTERFACE::set_input_cmd(ecaresources.resource("ext-cmd-ogg-input"));
 
138
  OGG_VORBIS_INTERFACE::set_output_cmd(ecaresources.resource("ext-cmd-ogg-output"));
 
139
  FLAC_FORKED_INTERFACE::set_input_cmd(ecaresources.resource("ext-cmd-flac-input"));
 
140
  FLAC_FORKED_INTERFACE::set_output_cmd(ecaresources.resource("ext-cmd-flac-output"));
 
141
  AAC_FORKED_INTERFACE::set_input_cmd(ecaresources.resource("ext-cmd-aac-input"));
 
142
  AAC_FORKED_INTERFACE::set_output_cmd(ecaresources.resource("ext-cmd-aac-output"));
 
143
}
 
144
 
 
145
 
 
146
/**
 
147
 * Add a new chainsetup
 
148
 *
 
149
 * require:
 
150
 *  name.empty() != true
 
151
 *
 
152
 * ensure:
 
153
 *  selected_chainsetup->name() == name ||
 
154
 *  chainsetup_names().size() has not changed
 
155
 */
 
156
void ECA_SESSION::add_chainsetup(const std::string& name)
 
157
{
 
158
  // --------
 
159
  DBC_REQUIRE(name != "");
 
160
  // --------
 
161
 
 
162
  ECA_CHAINSETUP* newsetup = new ECA_CHAINSETUP;
 
163
  newsetup->set_name(name);
 
164
  add_chainsetup(newsetup);
 
165
  if (selected_chainsetup_repp == 0) {
 
166
    /* adding the chainsetup failed */
 
167
    delete newsetup;
 
168
  }
 
169
 
 
170
  // --------
 
171
  DBC_ENSURE(selected_chainsetup_repp != 0 &&
 
172
             selected_chainsetup_repp->name() == name ||
 
173
             selected_chainsetup_repp == 0);
 
174
  // --------
 
175
}
 
176
 
 
177
/**
 
178
 * Add a new chainsetup. Ownership of the object given as argument
 
179
 * is passed along the call. If a chainsetup with the same name already
 
180
 * exists, the call will fail and the chainsetup given as argument
 
181
 * is deleted.
 
182
 *
 
183
 * require:
 
184
 *   comline_setup != 0
 
185
 *
 
186
 * ensure:
 
187
 *   (selected_chainsetup_repp == comline_setup && 
 
188
 *    static_cast<int>(chainsetups_rep.size()) == old_size + 1) ||
 
189
 *   (selected_chainsetup_repp == 0 && 
 
190
 *    static_cast<int>(chainsetups_rep.size()) == old_size)
 
191
 */
 
192
void ECA_SESSION::add_chainsetup(ECA_CHAINSETUP* comline_setup)
 
193
{
 
194
  // --------
 
195
  DBC_REQUIRE(comline_setup != 0);
 
196
  DBC_DECLARE(int old_size = chainsetups_rep.size());
 
197
  // --------
 
198
 
 
199
  selected_chainsetup_repp = comline_setup;
 
200
  
 
201
  std::vector<ECA_CHAINSETUP*>::const_iterator p = chainsetups_rep.begin();
 
202
  while(p != chainsetups_rep.end()) {
 
203
    if ((*p)->name() == comline_setup->name()) {
 
204
      ECA_LOG_MSG(ECA_LOGGER::info, 
 
205
                  "Unable to add chainsetup, chainsetup with the same name already exists.");
 
206
      selected_chainsetup_repp = 0;
 
207
      break;
 
208
    }
 
209
    ++p;
 
210
  }
 
211
 
 
212
  if (selected_chainsetup_repp != 0) {
 
213
    chainsetups_rep.push_back(selected_chainsetup_repp);
 
214
  }
 
215
 
 
216
  // --------
 
217
  DBC_ENSURE((selected_chainsetup_repp == comline_setup && 
 
218
              static_cast<int>(chainsetups_rep.size()) == old_size + 1) ||
 
219
             (selected_chainsetup_repp == 0 && 
 
220
              static_cast<int>(chainsetups_rep.size()) == old_size));
 
221
  // --------
 
222
}
 
223
 
 
224
/**
 
225
 * Removes selected chainsetup
 
226
 *
 
227
 * require:
 
228
 *  connected_chainsetup != selected_chainsetup
 
229
 *
 
230
 * ensure:
 
231
 *  selected_chainsetup == 0
 
232
 */
 
233
void ECA_SESSION::remove_chainsetup(void)
 
234
{
 
235
  // --------
 
236
  // require:
 
237
  DBC_REQUIRE(connected_chainsetup_repp != selected_chainsetup_repp);
 
238
  // --------
 
239
 
 
240
  std::vector<ECA_CHAINSETUP*>::iterator p = chainsetups_rep.begin();
 
241
  while(p != chainsetups_rep.end()) {
 
242
    if (*p == selected_chainsetup_repp) {
 
243
      selected_chainsetup_repp = 0;
 
244
      delete *p;
 
245
      chainsetups_rep.erase(p);
 
246
      break;
 
247
    }
 
248
    ++p;
 
249
  }
 
250
 
 
251
  // --------
 
252
  DBC_ENSURE(selected_chainsetup_repp == 0);
 
253
  // --------
 
254
}
 
255
 
 
256
void ECA_SESSION::select_chainsetup(const std::string& name)
 
257
{
 
258
  // --------
 
259
  // require:
 
260
  DBC_REQUIRE(name.empty() != true);
 
261
  // --------
 
262
 
 
263
  selected_chainsetup_repp = 0;
 
264
  std::vector<ECA_CHAINSETUP*>::const_iterator p = chainsetups_rep.begin();
 
265
  while(p != chainsetups_rep.end()) {
 
266
    if ((*p)->name() == name) {
 
267
      //  ECA_LOG_MSG(ECA_LOGGER::system_objects, "Chainsetup \"" + name + "\" selected.");
 
268
      selected_chainsetup_repp = *p;
 
269
      break;
 
270
    }
 
271
    ++p;
 
272
  }
 
273
 
 
274
  // --------
 
275
  DBC_ENSURE(selected_chainsetup_repp == 0 ||
 
276
             selected_chainsetup_repp->name() == name);
 
277
  // --------
 
278
}
 
279
 
 
280
void ECA_SESSION::save_chainsetup(void) throw(ECA_ERROR&)
 
281
{
 
282
  // --------
 
283
  // require:
 
284
  DBC_REQUIRE(selected_chainsetup_repp != 0);
 
285
  // --------
 
286
 
 
287
  selected_chainsetup_repp->save();
 
288
}
 
289
 
 
290
void ECA_SESSION::save_chainsetup(const std::string& filename) throw(ECA_ERROR&)
 
291
{
 
292
  // --------
 
293
  // require:
 
294
  DBC_REQUIRE(selected_chainsetup_repp != 0 && filename.empty() != true);
 
295
  // --------
 
296
 
 
297
  selected_chainsetup_repp->save_to_file(filename);
 
298
}
 
299
 
 
300
/**
 
301
 * Load a chainsetup from file (ecs). If operation fails,
 
302
 * selected_chainsetup_repp == 0, ie. no chainsetup 
 
303
 * selected.
 
304
 *
 
305
 * @post (selected_chainsetup_repp != 0 &&
 
306
 *        selected_chainsetup_repp->filename() == filename || 
 
307
 *        selected_chainsetup_repp == 0)
 
308
 */
 
309
void ECA_SESSION::load_chainsetup(const std::string& filename)
 
310
{
 
311
  // --------
 
312
  DBC_REQUIRE(filename.empty() != true);
 
313
  // --------
 
314
 
 
315
  ECA_CHAINSETUP* new_setup = new ECA_CHAINSETUP(filename);
 
316
  if (new_setup->interpret_result() != true) {
 
317
    string temp = new_setup->interpret_result_verbose();
 
318
    delete new_setup;
 
319
    selected_chainsetup_repp = 0;
 
320
    ECA_LOG_MSG(ECA_LOGGER::info, "Error loading chainsetup: " + temp);
 
321
  }
 
322
  else {
 
323
    add_chainsetup(new_setup);
 
324
    if (selected_chainsetup_repp == 0) {
 
325
      /* adding the chainsetup failed */
 
326
      delete new_setup;
 
327
    }
 
328
  }
 
329
  
 
330
  // --------
 
331
  DBC_ENSURE(selected_chainsetup_repp != 0 &&
 
332
             selected_chainsetup_repp->filename() == filename || 
 
333
             selected_chainsetup_repp == 0);
 
334
  // --------
 
335
}
 
336
 
 
337
void ECA_SESSION::connect_chainsetup(void) throw(ECA_ERROR&)
 
338
{
 
339
  // --------
 
340
  DBC_REQUIRE(selected_chainsetup_repp != 0);
 
341
  DBC_REQUIRE(selected_chainsetup_repp->is_valid());
 
342
  // --------
 
343
 
 
344
  ECA_LOG_MSG(ECA_LOGGER::subsystems, "Connecting chainsetup");
 
345
 
 
346
  if (selected_chainsetup_repp == connected_chainsetup_repp) return;
 
347
 
 
348
  if (connected_chainsetup_repp != 0) {
 
349
    disconnect_chainsetup();
 
350
  }
 
351
 
 
352
  /** 
 
353
   * enable() throws an exception if it wasn't possibly
 
354
   * to open/activate all input and output objects 
 
355
   */
 
356
  selected_chainsetup_repp->enable();
 
357
  connected_chainsetup_repp = selected_chainsetup_repp;
 
358
 
 
359
  ECA_LOG_MSG(ECA_LOGGER::subsystems, "Chainsetup connected");
 
360
 
 
361
  // --------
 
362
  // ensure:
 
363
  DBC_ENSURE(selected_chainsetup_repp == connected_chainsetup_repp);
 
364
  // --------
 
365
}
 
366
 
 
367
void ECA_SESSION::disconnect_chainsetup(void)
 
368
{
 
369
  // --------
 
370
  DBC_REQUIRE(connected_chainsetup_repp != 0);
 
371
  // --------
 
372
 
 
373
  connected_chainsetup_repp->disable();
 
374
  connected_chainsetup_repp = 0;
 
375
 
 
376
  ECA_LOG_MSG(ECA_LOGGER::subsystems, "Chainsetup disconnected");
 
377
 
 
378
  // --------
 
379
  DBC_ENSURE(connected_chainsetup_repp == 0);
 
380
  // --------
 
381
}
 
382
 
 
383
std::vector<std::string> ECA_SESSION::chainsetup_names(void) const
 
384
{
 
385
  std::vector<std::string> result;
 
386
  std::vector<ECA_CHAINSETUP*>::const_iterator p = chainsetups_rep.begin();
 
387
  while(p != chainsetups_rep.end()) {
 
388
    result.push_back((*p)->name());
 
389
    ++p;
 
390
  }
 
391
  return result;
 
392
}
 
393
 
 
394
void ECA_SESSION::create_chainsetup_options(COMMAND_LINE& cline,
 
395
                                            std::vector<std::string>* options)
 
396
{
 
397
  cline.begin();
 
398
  cline.next(); // skip the program name
 
399
  while(cline.end() == false) {
 
400
    options->push_back(cline.current());
 
401
    cline.next();
 
402
  }
 
403
}
 
404
 
 
405
/**
 
406
 * Tests whether the given argument is a session-level option.
 
407
 */
 
408
bool ECA_SESSION::is_session_option(const std::string& arg) const
 
409
{
 
410
  if (arg.size() < 2 ||
 
411
      arg[0] != '-') return false;
 
412
 
 
413
  switch(arg[1]) {
 
414
  case 'd':
 
415
  case 'q':
 
416
    return true;
 
417
 
 
418
  case 's': 
 
419
    if (arg.size() > 2 && arg[2] == ':') return true;
 
420
  }
 
421
  return false;
 
422
}
 
423
 
 
424
/**
 
425
 * Preprocesses a set of options.
 
426
 * 
 
427
 * Notes! See also ECA_CHAINSETUP_PARSER::preprocess_options()
 
428
 *
 
429
 * ensure:
 
430
 *  all options valid for further processing (all options
 
431
 *  must start with a '-' sign)
 
432
 */
 
433
void ECA_SESSION::preprocess_options(std::vector<std::string>* opts)
 
434
{
 
435
  std::vector<std::string>::iterator p = opts->begin();
 
436
  while(p != opts->end()) {
 
437
 
 
438
    if (p->size() > 0 && (*p)[0] != '-') {
 
439
      /* hack1: options ending with .ecs as "-s:file.ecs" */
 
440
      string::size_type pos = p->find(".ecs");
 
441
      if (pos + 4 == p->size()) {
 
442
        ECA_LOG_MSG(ECA_LOGGER::info, "Note! Interpreting option " +
 
443
                    *p +
 
444
                    " as -s:" +
 
445
                    *p +
 
446
                    ".");
 
447
        *p = "-s:" + *p;
 
448
      }
 
449
    }
 
450
    ++p;
 
451
  }
 
452
}
 
453
 
 
454
/**
 
455
 * Interprets all session specific options from 'inopts'.
 
456
 * All unprocessed options are copied to 'outopts'. 
 
457
 *
 
458
 * @return number of parsing errors
 
459
 */
 
460
int ECA_SESSION::interpret_general_options(const std::vector<std::string>& inopts,
 
461
                                           std::vector<std::string>* outopts) 
 
462
{
 
463
  int errors = 0;
 
464
 
 
465
  std::vector<std::string>::const_iterator p = inopts.begin();
 
466
  while(p != inopts.end()) {
 
467
    if (p->size() > 0 && (*p)[0] == '-')
 
468
      errors += interpret_general_option(*p);
 
469
    ++p;
 
470
  }
 
471
 
 
472
  p = inopts.begin();
 
473
  while(p != inopts.end()) {
 
474
    if (p->size() > 0 && (*p)[0] == '-')
 
475
      errors += interpret_chainsetup_option(*p);
 
476
 
 
477
    if (is_session_option(*p) != true) outopts->push_back(*p);
 
478
 
 
479
    ++p;
 
480
  }
 
481
 
 
482
  return errors;
 
483
}
 
484
 
 
485
/**
 
486
 * Parses session option 'argu'.
 
487
 *
 
488
 * @return number of parsing errors
 
489
 */
 
490
int ECA_SESSION::interpret_general_option (const std::string& argu)
 
491
{
 
492
  if (argu.size() < 2) return 0;
 
493
  if (argu[0] != '-') return 0;
 
494
 
 
495
  switch(argu[1]) {
 
496
  case 'd':
 
497
    {
 
498
      if (argu.size() > 2 && argu[2] == ':') {
 
499
        /* argu == "-d:XXX" */
 
500
        ECA_LOGGER::instance().set_log_level_bitmask(atoi(kvu_get_argument_number(1, argu).c_str()));
 
501
      }
 
502
      else {
 
503
        /* argu == "-dXXX" */
 
504
        
 
505
        ECA_LOGGER::instance().set_log_level_bitmask(0);
 
506
        ECA_LOGGER::instance().set_log_level(ECA_LOGGER::errors, 1);
 
507
        ECA_LOGGER::instance().set_log_level(ECA_LOGGER::info, 1);
 
508
        ECA_LOGGER::instance().set_log_level(ECA_LOGGER::subsystems, 1);
 
509
        ECA_LOGGER::instance().set_log_level(ECA_LOGGER::module_names, 1);
 
510
        ECA_LOGGER::instance().set_log_level(ECA_LOGGER::user_objects, 1);
 
511
        ECA_LOGGER::instance().set_log_level(ECA_LOGGER::eiam_return_values, 1);
 
512
 
 
513
        if (argu.size() > 2 && argu[2] == 'd') {
 
514
          /* argu == "-dd" */
 
515
          ECA_LOGGER::instance().set_log_level(ECA_LOGGER::system_objects, 1);    
 
516
 
 
517
          if (argu.size() > 3 && argu[3] == 'd') {
 
518
            /* argu == "-ddd" */
 
519
            ECA_LOGGER::instance().set_log_level(ECA_LOGGER::functions, 1);
 
520
            ECA_LOGGER::instance().set_log_level(ECA_LOGGER::continuous, 1);
 
521
          }
 
522
        }
 
523
      }
 
524
 
 
525
      MESSAGE_ITEM mtempd;
 
526
      mtempd << "Set debug level to: " << ECA_LOGGER::instance().get_log_level_bitmask();
 
527
      ECA_LOG_MSG(ECA_LOGGER::info, mtempd.to_string());
 
528
      break;
 
529
    }
 
530
 
 
531
  case 'q':
 
532
    ECA_LOGGER::instance().disable();
 
533
    break;
 
534
 
 
535
  default: { }
 
536
  }
 
537
 
 
538
  return 0;
 
539
}
 
540
 
 
541
/**
 
542
 * Parses session chainsetup option 'argu'.
 
543
 *
 
544
 * @return number of parsing errors
 
545
 */
 
546
int ECA_SESSION::interpret_chainsetup_option (const std::string& argu)
 
547
{
 
548
  int errors = 0;
 
549
 
 
550
  if (argu.size() == 0) return errors;
 
551
  
 
552
  string tname = kvu_get_argument_number(1, argu);
 
553
 
 
554
  if (argu.size() < 2) return errors;
 
555
  switch(argu[1]) {
 
556
  case 's': {
 
557
    if (argu.size() > 2 && argu[2] == ':') {
 
558
      load_chainsetup(tname);
 
559
      if (selected_chainsetup_repp == 0 || 
 
560
          selected_chainsetup_repp->is_valid_for_connection(true) != true) {
 
561
        ECA_LOG_MSG(ECA_LOGGER::info, "Chainsetup loaded from '" + tname + "' is not valid!");
 
562
        ++errors;
 
563
      }
 
564
      if (errors == 0) connect_chainsetup();
 
565
    }
 
566
    break;
 
567
  }
 
568
  default: { }
 
569
  }
 
570
  
 
571
  return errors;
 
572
}