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

« back to all changes in this revision

Viewing changes to libecasound/audioio-seqbase.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Junichi Uekawa
  • Date: 2008-09-26 09:58:52 UTC
  • mfrom: (1.1.6 upstream)
  • Revision ID: james.westby@ubuntu.com-20080926095852-k3v9ewhmxpaltusw
Tags: 2.5.2-3
yodl 2.13.1 removed --unique-output option. Remove --unique-output
accordingly.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// ------------------------------------------------------------------------
 
2
// audioio-seqbase.cpp: Base class for audio sequencer objects
 
3
// Copyright (C) 1999,2002,2005,2008 Kai Vehmanen
 
4
//
 
5
// Attributes:
 
6
//     eca-style-version: 3 (see Ecasound Programmer's Guide)
 
7
//
 
8
// This program is free 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 <algorithm>
 
24
#include <string>
 
25
#include <vector>
 
26
#include <iostream>
 
27
#include <fstream>
 
28
#include <cmath>
 
29
 
 
30
#include <kvu_message_item.h>
 
31
#include <kvu_numtostr.h>
 
32
#include <kvu_dbc.h>
 
33
 
 
34
#include "eca-object-factory.h"
 
35
#include "samplebuffer.h"
 
36
#include "audioio-seqbase.h"
 
37
 
 
38
#include "eca-error.h"
 
39
#include "eca-logger.h"
 
40
 
 
41
using std::cout;
 
42
using std::endl;
 
43
using SAMPLE_SPECS::sample_pos_t;
 
44
 
 
45
/**
 
46
 * FIXME notes  (last update 2008-03-04)
 
47
 *
 
48
 *  - Add check for supports_sample_accurate_seek(). This could
 
49
 *    be used to warn users if EWF source file cannot provide
 
50
 *    accurate enough seeking (which will lead to unexpected
 
51
 *    results).
 
52
 *  - Remove write-support altogether (changes supported io_modes(),
 
53
 *    modify write_buffer(), and remove child_write_started.
 
54
 *  - Should extend the object length when looping is enabled.
 
55
 */
 
56
 
 
57
/**
 
58
 * Returns the child position in samples for the given 
 
59
 * public position.
 
60
 */
 
61
sample_pos_t AUDIO_SEQUENCER_BASE::priv_public_to_child_pos(sample_pos_t pubpos) const
 
62
{
 
63
  if (pubpos <= child_offset_rep.samples()) 
 
64
    return child_start_pos_rep.samples();
 
65
 
 
66
  if (child_looping_rep == true) {
 
67
    DBC_CHECK(child()->finite_length_stream() == true);
 
68
    sample_pos_t one_loop = 
 
69
      child_length_rep.samples() -
 
70
      child_start_pos_rep.samples();
 
71
    sample_pos_t res =
 
72
      pubpos - child_offset_rep.samples();
 
73
    DBC_CHECK(res > 0);
 
74
    if (one_loop > 0)
 
75
      res = res % one_loop;
 
76
    return res + child_start_pos_rep.samples();
 
77
  }
 
78
  else {
 
79
    /* not looping */
 
80
    return pubpos - child_offset_rep.samples() + child_start_pos_rep.samples();
 
81
  }
 
82
}
 
83
 
 
84
AUDIO_SEQUENCER_BASE::AUDIO_SEQUENCER_BASE (void)
 
85
  : child_looping_rep(false),
 
86
    child_length_set_by_client_rep(false),
 
87
    buffersize_rep(-1),
 
88
    child_write_started(false),
 
89
    init_rep(false)
 
90
{
 
91
}
 
92
 
 
93
AUDIO_SEQUENCER_BASE::~AUDIO_SEQUENCER_BASE(void)
 
94
{
 
95
}
 
96
 
 
97
AUDIO_SEQUENCER_BASE* AUDIO_SEQUENCER_BASE::clone(void) const
 
98
{
 
99
  AUDIO_SEQUENCER_BASE* target = new AUDIO_SEQUENCER_BASE();
 
100
  for(int n = 0; n < number_of_params(); n++) {
 
101
    target->set_parameter(n + 1, get_parameter(n + 1));
 
102
  }
 
103
  return target;
 
104
}
 
105
 
 
106
void AUDIO_SEQUENCER_BASE::open(void) throw(AUDIO_IO::SETUP_ERROR &)
 
107
{
 
108
  if (init_rep != true) {
 
109
    AUDIO_IO* tmp = ECA_OBJECT_FACTORY::create_audio_object(child_name_rep);
 
110
    if (tmp == 0) 
 
111
      throw(SETUP_ERROR(SETUP_ERROR::unexpected, "AUDIOIO-SEQBASE: Couldn't open child object."));
 
112
 
 
113
    ECA_LOG_MSG(ECA_LOGGER::user_objects, "Opening audio sequencer file:" + tmp->label() + ".");
 
114
 
 
115
    set_child(tmp);
 
116
    init_rep = true;
 
117
  }
 
118
 
 
119
  pre_child_open();
 
120
  child()->open();
 
121
 
 
122
  if (child()->finite_length_stream() != true &&
 
123
      child_looping_rep) {
 
124
    child()->close();
 
125
    throw(SETUP_ERROR(SETUP_ERROR::unexpected, "AUDIOIO-SEQBASE: Unable to loop an object with infinite length."));
 
126
  }
 
127
 
 
128
  /* step: set srate for audio time variable */
 
129
  child_offset_rep.set_samples_per_second_keeptime(child()->samples_per_second());
 
130
  child_start_pos_rep.set_samples_per_second_keeptime(child()->samples_per_second());
 
131
  child_length_rep.set_samples_per_second_keeptime(child()->samples_per_second());
 
132
 
 
133
  post_child_open();
 
134
 
 
135
  /* step: unless overridden by the client, store the length
 
136
   *       of child object */
 
137
  ECA_AUDIO_TIME len_tmp (child()->length_in_samples(),
 
138
                          child()->samples_per_second());
 
139
  if (child_length_set_by_client_rep != true)
 
140
    set_child_length_private(len_tmp);
 
141
  else if (len_tmp.valid() == true &&
 
142
           child_length_rep.valid() == true) {
 
143
    if (len_tmp.seconds() < 
 
144
        child_length_rep.seconds()) 
 
145
      ECA_LOG_MSG(ECA_LOGGER::info, 
 
146
                  "WARNING: object '" + child()->label() +
 
147
                  "' is too short, reducing segment length to '" 
 
148
                  + kvu_numtostr(len_tmp.seconds()) + "'sec.");
 
149
  }
 
150
 
 
151
  /* step: set public length of the EWF object;
 
152
   *       child length is infinite, we will extend our object
 
153
   *       length in read_buffer(), see also
 
154
   *       AUDIO_SEQUENCER_BASE::finite_length_stream() */
 
155
  if (child_looping_rep != true)
 
156
    set_length_in_samples(child_offset_rep.samples() + child_length_rep.samples());
 
157
 
 
158
  //dump_child_debug();
 
159
 
 
160
  tmp_buffer.number_of_channels(child()->channels());
 
161
  tmp_buffer.length_in_samples(child()->buffersize());
 
162
 
 
163
  AUDIO_IO_PROXY::open();
 
164
}
 
165
 
 
166
void AUDIO_SEQUENCER_BASE::close(void)
 
167
{
 
168
  if (child()->is_open() == true)
 
169
    child()->close();
 
170
 
 
171
  AUDIO_IO_PROXY::close();
 
172
}
 
173
 
 
174
 
 
175
void AUDIO_SEQUENCER_BASE::read_buffer(SAMPLE_BUFFER* sbuf)
 
176
{
 
177
  /**
 
178
   * implementation notes:
 
179
   * 
 
180
   * position:             the current global position (note, this
 
181
   *                       can exceed child_offset+child_length when
 
182
   *                       looping is used.
 
183
   * child_offset:         global position when child is activated
 
184
   * child_start_position: position inside the child-object where
 
185
   *                       input is started (data between child
 
186
   *                       beginning and child_start_pos is not used)
 
187
   * child_length:         amount of child data between start and end
 
188
   *                       positions (end can in middle of stream or
 
189
   *                       at end-of-file position)
 
190
   * child_looping:        when child end is reached, whether to jump 
 
191
   *                       back to start position?
 
192
   * 
 
193
   * note! all cases (if-else blocks) end to setting a new 
 
194
   *       position_in_samples value
 
195
   */
 
196
 
 
197
  //dump_child_debug();
 
198
 
 
199
  if (position_in_samples() + buffersize() <= child_offset_rep.samples()) {
 
200
    // ---
 
201
    // case 1: child not active yet
 
202
    // ---
 
203
    sbuf->make_silent();
 
204
    /* ensure that channel count matches that of child */
 
205
    sbuf->number_of_channels(child()->channels());
 
206
    change_position_in_samples(buffersize());
 
207
  }
 
208
  else if (position_in_samples() < child_offset_rep.samples()) {
 
209
    // ---
 
210
    // case 2: next read will bring in the first child samples
 
211
    // ---
 
212
    
 
213
    DBC_CHECK(position_in_samples() + buffersize() > child_offset_rep.samples());
 
214
 
 
215
    /* make sure child position is correct at start */
 
216
    sample_pos_t chipos = 
 
217
      priv_public_to_child_pos(position_in_samples());
 
218
    if (chipos != child()->position_in_samples())
 
219
      child()->seek_position_in_samples(chipos);
 
220
    
 
221
    sample_pos_t segment = 
 
222
      position_in_samples()
 
223
      + buffersize() 
 
224
      - child_offset_rep.samples();
 
225
 
 
226
    if (segment > buffersize())
 
227
      segment = buffersize();
 
228
 
 
229
    ECA_LOG_MSG(ECA_LOGGER::user_objects, 
 
230
                "child-object '" + child()->label() + "' activated.");
 
231
 
 
232
    /* step: read segment of audio and copy it to the correct 
 
233
     *       location */
 
234
    long int save_bsize = child()->buffersize();
 
235
    DBC_CHECK(save_bsize == buffersize());
 
236
    child()->set_buffersize(segment);
 
237
    child()->read_buffer(&tmp_buffer);
 
238
    sbuf->copy_range(tmp_buffer, 
 
239
                     0,
 
240
                     tmp_buffer.length_in_samples(), 
 
241
                     buffersize() - segment);
 
242
    /* ensure that channel count matches that of child */
 
243
    sbuf->number_of_channels(child()->channels());
 
244
    child()->set_buffersize(save_bsize);
 
245
    change_position_in_samples(buffersize());
 
246
  }
 
247
  else {
 
248
    // ---
 
249
    // case 3: child is active
 
250
    // ---
 
251
 
 
252
    sample_pos_t chipos1 = 
 
253
      priv_public_to_child_pos(position_in_samples());
 
254
    sample_pos_t chipos2 = 
 
255
      priv_public_to_child_pos(position_in_samples() + buffersize());
 
256
 
 
257
    if (chipos2 >= 
 
258
        (child_length_rep.samples() + child_start_pos_rep.samples()) &&
 
259
        child_looping_rep != true &&
 
260
        child()->finite_length_stream() == true) {
 
261
        // ---
 
262
        // case 3a: not looping, reaching child file end during the next read
 
263
        // ---
 
264
 
 
265
      child()->read_buffer(sbuf);
 
266
 
 
267
      sample_pos_t over_child_eof = 
 
268
        chipos2 - (child_length_rep.samples() + child_start_pos_rep.samples());
 
269
      
 
270
      DBC_CHECK(over_child_eof >= 0);
 
271
      DBC_CHECK((child()->finished() == true &&
 
272
                 buffersize() > sbuf->length_in_samples()) ||
 
273
                child()->finished() != true);
 
274
      
 
275
      /* resize the sbuf if needed: either EOF was encountered
 
276
       * and sbuf is shorter than buffersize() or otherwise we
 
277
       * need to drop some of the samples read so at to not
 
278
       * go beyond set length */
 
279
      sample_pos_t data_left = buffersize() - over_child_eof;
 
280
      sbuf->length_in_samples(data_left);
 
281
      change_position_in_samples(sbuf->length_in_samples());
 
282
    }
 
283
    else if (chipos2 < chipos1 &&
 
284
             child_looping_rep == true) {
 
285
      // ---
 
286
      // case 3b: looping, we will run out of data during read
 
287
      // ---
 
288
 
 
289
      child()->read_buffer(sbuf);
 
290
      
 
291
      sample_pos_t over_child_eof = chipos2 - child_start_pos_rep.samples();
 
292
      
 
293
      /* step: copy segment 1 from loop end, and segment 2 from
 
294
       *       loop start point */
 
295
      sample_pos_t chistartpos = 
 
296
        priv_public_to_child_pos(position_in_samples() + 
 
297
                                 buffersize() - over_child_eof);
 
298
 
 
299
      DBC_CHECK(chistartpos == child_start_pos_rep.samples());
 
300
      child()->seek_position_in_samples(chistartpos);
 
301
 
 
302
      if (over_child_eof > 0) {
 
303
        long int save_bsize = child()->buffersize();
 
304
        DBC_CHECK(save_bsize == buffersize());
 
305
        child()->set_buffersize(over_child_eof);
 
306
        tmp_buffer.number_of_channels(channels());
 
307
        child()->read_buffer(&tmp_buffer);
 
308
        DBC_CHECK(tmp_buffer.length_in_samples() == over_child_eof);
 
309
        child()->set_buffersize(save_bsize);
 
310
        DBC_CHECK((buffersize() - over_child_eof) < buffersize());
 
311
        sbuf->length_in_samples(buffersize());
 
312
        sbuf->copy_range(tmp_buffer, 
 
313
                         0,
 
314
                         tmp_buffer.length_in_samples(), 
 
315
                         buffersize() - over_child_eof);
 
316
      }
 
317
      change_position_in_samples(buffersize());
 
318
    }
 
319
    else {
 
320
      // ---
 
321
      // case 3c: normal case, read samples from child
 
322
      // ---
 
323
 
 
324
      child()->set_buffersize(buffersize());      
 
325
      child()->read_buffer(sbuf);
 
326
      /* note: if the 'length' parameter value is longer than 
 
327
       *       actual child object length, less than buffersize()
 
328
       *       samples will be read */
 
329
 
 
330
      change_position_in_samples(sbuf->length_in_samples());
 
331
    }
 
332
  }
 
333
 
 
334
  /* note: as we are looping indefinitely, so our length must
 
335
   *       be continuously updated (see child_lengt() implementation) */
 
336
  if (child_looping_rep == true ||
 
337
      (child_length_set_by_client_rep != true &&
 
338
      child()->finite_length_stream() != true))
 
339
    extend_position();
 
340
}
 
341
 
 
342
void AUDIO_SEQUENCER_BASE::dump_child_debug(void)
 
343
{
 
344
  cout << "global position (in samples): " << position_in_samples() << endl;
 
345
  cout << "child-pos: " << child()->position_in_samples() << endl;
 
346
  cout << "child-offset: " << child_offset_rep.samples() << endl;
 
347
  cout << "child-startpos: " << child_start_pos_rep.samples() << endl;
 
348
  cout << "child-length: " << child_length_rep.samples() << endl;
 
349
}
 
350
 
 
351
void AUDIO_SEQUENCER_BASE::write_buffer(SAMPLE_BUFFER* sbuf)
 
352
{
 
353
  if (child_write_started != true) {
 
354
    child_write_started = true;
 
355
    child_offset_rep.set_samples(position_in_samples());
 
356
    MESSAGE_ITEM m;
 
357
    m << "found child_offset_rep " << child_offset_rep.seconds() << ".";
 
358
    ECA_LOG_MSG(ECA_LOGGER::user_objects, m.to_string());
 
359
  }
 
360
  
 
361
  child()->write_buffer(sbuf);
 
362
  change_position_in_samples(sbuf->length_in_samples());
 
363
  extend_position();
 
364
}
 
365
 
 
366
SAMPLE_SPECS::sample_pos_t AUDIO_SEQUENCER_BASE::seek_position(SAMPLE_SPECS::sample_pos_t pos)
 
367
{
 
368
  /* in write mode, seek can be only performed once
 
369
   * the initial write has been performed to the child */
 
370
  if (is_open() == true &&
 
371
      (io_mode() == AUDIO_IO::io_read ||
 
372
       (io_mode() != AUDIO_IO::io_read &&
 
373
        child_write_started == true))) {
 
374
    sample_pos_t chipos = 
 
375
      priv_public_to_child_pos(pos);
 
376
 
 
377
    child()->seek_position_in_samples(chipos);
 
378
  }
 
379
 
 
380
  return pos;
 
381
}
 
382
 
 
383
void AUDIO_SEQUENCER_BASE::set_child_object_string(const std::string& v)
 
384
{
 
385
  child_name_rep = v;
 
386
}
 
387
 
 
388
void AUDIO_SEQUENCER_BASE::set_child_offset(const ECA_AUDIO_TIME& v)
 
389
{
 
390
  child_offset_rep = v;
 
391
}
 
392
 
 
393
void AUDIO_SEQUENCER_BASE::set_child_start_position(const ECA_AUDIO_TIME& v)
 
394
{
 
395
  child_start_pos_rep = v;
 
396
}
 
397
 
 
398
void AUDIO_SEQUENCER_BASE::set_child_length_private(const ECA_AUDIO_TIME& v)
 
399
{
 
400
  child_length_rep = v;
 
401
}
 
402
 
 
403
void AUDIO_SEQUENCER_BASE::set_child_length(const ECA_AUDIO_TIME& v)
 
404
{
 
405
  set_child_length_private(v);
 
406
  child_length_set_by_client_rep = true;
 
407
}
 
408
 
 
409
ECA_AUDIO_TIME AUDIO_SEQUENCER_BASE::child_length(void) const
 
410
{
 
411
  /* case: infinite child length */
 
412
  if (child_length_set_by_client_rep != true &&
 
413
      child()->finite_length_stream() != true) {
 
414
    ECA_AUDIO_TIME tmp;
 
415
    tmp.mark_as_invalid();
 
416
    return tmp;
 
417
  }
 
418
 
 
419
  return child_length_rep;
 
420
 
421
 
 
422
bool AUDIO_SEQUENCER_BASE::finite_length_stream(void) const
 
423
{
 
424
  if (child_looping_rep == true)
 
425
    return false;
 
426
 
 
427
  return child()->finite_length_stream();
 
428
}
 
429
 
 
430
bool AUDIO_SEQUENCER_BASE::finished(void) const
 
431
{
 
432
  /** 
 
433
   * File is finished if...
 
434
   *  1) the child object is out of data (implies that looping
 
435
   *     is disabled), or
 
436
   *  2) file is open in read mode, looping is disabled, child is
 
437
   *     a finite length stream and its position has gone beyond 
 
438
   *     the requested child_length
 
439
   */
 
440
  if (child()->finished()) {
 
441
    ECA_LOG_MSG(ECA_LOGGER::user_objects, 
 
442
                "Child object " + child()->label() + " finished.");
 
443
    return true;
 
444
  }
 
445
 
 
446
  if (io_mode() != AUDIO_IO::io_read) {
 
447
    return false;
 
448
  }
 
449
 
 
450
  if (child_looping_rep != true &&
 
451
      child()->finite_length_stream() == true && 
 
452
      priv_public_to_child_pos(position_in_samples()) 
 
453
      >= child_length_rep.samples() + child_start_pos_rep.samples()) {
 
454
    ECA_LOG_MSG(ECA_LOGGER::user_objects, 
 
455
                "Finite length child object " + child()->label() + 
 
456
                " finished. All samples from the requested range have been read.");
 
457
    return true;
 
458
  }
 
459
 
 
460
  return false;
 
461
}