ardour
audio_playlist.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 Paul Davis
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or
7  (at your option) any later version.
8 
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with this program; if not, write to the Free Software
16  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 
18 */
19 
20 #include <algorithm>
21 
22 #include <cstdlib>
23 
24 #include "ardour/types.h"
25 #include "ardour/debug.h"
26 #include "ardour/audioplaylist.h"
27 #include "ardour/audioregion.h"
28 #include "ardour/region_sorters.h"
29 #include "ardour/session.h"
30 
31 #include "i18n.h"
32 
33 using namespace ARDOUR;
34 using namespace std;
35 using namespace PBD;
36 
37 AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden)
38  : Playlist (session, node, DataType::AUDIO, hidden)
39 {
40 #ifndef NDEBUG
41  const XMLProperty* prop = node.property("type");
42  assert(!prop || DataType(prop->value()) == DataType::AUDIO);
43 #endif
44 
45  in_set_state++;
47  throw failed_constructor();
48  }
49  in_set_state--;
50 
51  relayer ();
52 
54 }
55 
56 AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden)
57  : Playlist (session, name, DataType::AUDIO, hidden)
58 {
59 }
60 
62  : Playlist (other, name, hidden)
63 {
64 }
65 
67  : Playlist (other, start, cnt, name, hidden)
68 {
69  RegionReadLock rlock2 (const_cast<AudioPlaylist*> (other.get()));
70  in_set_state++;
71 
72  framepos_t const end = start + cnt - 1;
73 
74  /* Audio regions that have been created by the Playlist constructor
75  will currently have the same fade in/out as the regions that they
76  were created from. This is wrong, so reset the fades here.
77  */
78 
79  RegionList::iterator ours = regions.begin ();
80 
81  for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
83  assert (region);
84 
85  framecnt_t fade_in = 64;
86  framecnt_t fade_out = 64;
87 
88  switch (region->coverage (start, end)) {
90  continue;
91 
93  {
94  framecnt_t const offset = start - region->position ();
95  framecnt_t const trim = region->last_frame() - end;
96  if (region->fade_in()->back()->when > offset) {
97  fade_in = region->fade_in()->back()->when - offset;
98  }
99  if (region->fade_out()->back()->when > trim) {
100  fade_out = region->fade_out()->back()->when - trim;
101  }
102  break;
103  }
104 
105  case Evoral::OverlapStart: {
106  if (end > region->position() + region->fade_in()->back()->when)
107  fade_in = region->fade_in()->back()->when; //end is after fade-in, preserve the fade-in
108  if (end > region->last_frame() - region->fade_out()->back()->when)
109  fade_out = region->fade_out()->back()->when - ( region->last_frame() - end ); //end is inside the fadeout, preserve the fades endpoint
110  break;
111  }
112 
113  case Evoral::OverlapEnd: {
114  if (start < region->last_frame() - region->fade_out()->back()->when) //start is before fade-out, preserve the fadeout
115  fade_out = region->fade_out()->back()->when;
116 
117  if (start < region->position() + region->fade_in()->back()->when)
118  fade_in = region->fade_in()->back()->when - (start - region->position()); //end is inside the fade-in, preserve the fade-in endpoint
119  break;
120  }
121 
123  fade_in = region->fade_in()->back()->when;
124  fade_out = region->fade_out()->back()->when;
125  break;
126  }
127 
129  assert (our_region);
130 
131  our_region->set_fade_in_length (fade_in);
132  our_region->set_fade_out_length (fade_out);
133  ++ours;
134  }
135 
136  in_set_state--;
137 
138  /* this constructor does NOT notify others (session) */
139 }
140 
142 struct ReadSorter {
144  if (a->layer() != b->layer()) {
145  return a->layer() > b->layer();
146  }
147 
148  return a->position() < b->position();
149  }
150 };
151 
153 struct Segment {
155 
158 };
159 
164 AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, framepos_t start,
165  framecnt_t cnt, unsigned chan_n)
166 {
167  DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Playlist %1 read @ %2 for %3, channel %4, regions %5 mixdown @ %6 gain @ %7\n",
168  name(), start, cnt, chan_n, regions.size(), mixdown_buffer, gain_buffer));
169 
170  /* optimizing this memset() away involves a lot of conditionals
171  that may well cause more of a hit due to cache misses
172  and related stuff than just doing this here.
173 
174  it would be great if someone could measure this
175  at some point.
176 
177  one way or another, parts of the requested area
178  that are not written to by Region::region_at()
179  for all Regions that cover the area need to be
180  zeroed.
181  */
182 
183  memset (buf, 0, sizeof (Sample) * cnt);
184 
185  /* this function is never called from a realtime thread, so
186  its OK to block (for short intervals).
187  */
188 
189  Playlist::RegionReadLock rl (this);
190 
191  /* Find all the regions that are involved in the bit we are reading,
192  and sort them by descending layer and ascending position.
193  */
194  boost::shared_ptr<RegionList> all = regions_touched_locked (start, start + cnt - 1);
195  all->sort (ReadSorter ());
196 
197  /* This will be a list of the bits of our read range that we have
198  handled completely (ie for which no more regions need to be read).
199  It is a list of ranges in session frames.
200  */
202 
203  /* This will be a list of the bits of regions that we need to read */
204  list<Segment> to_do;
205 
206  /* Now go through the `all' list filling in `to_do' and `done' */
207  for (RegionList::iterator i = all->begin(); i != all->end(); ++i) {
209 
210  /* muted regions don't figure into it at all */
211  if ( ar->muted() )
212  continue;
213 
214  /* Work out which bits of this region need to be read;
215  first, trim to the range we are reading...
216  */
217  Evoral::Range<framepos_t> region_range = ar->range ();
218  region_range.from = max (region_range.from, start);
219  region_range.to = min (region_range.to, start + cnt - 1);
220 
221  /* ... and then remove the bits that are already done */
222 
223  Evoral::RangeList<framepos_t> region_to_do = Evoral::subtract (region_range, done);
224 
225  /* Make a note to read those bits, adding their bodies (the parts between end-of-fade-in
226  and start-of-fade-out) to the `done' list.
227  */
228 
229  Evoral::RangeList<framepos_t>::List t = region_to_do.get ();
230 
231  for (Evoral::RangeList<framepos_t>::List::iterator j = t.begin(); j != t.end(); ++j) {
233  to_do.push_back (Segment (ar, d));
234 
235  if (ar->opaque ()) {
236  /* Cut this range down to just the body and mark it done */
238  if (body.from < d.to && body.to > d.from) {
239  d.from = max (d.from, body.from);
240  d.to = min (d.to, body.to);
241  done.add (d);
242  }
243  }
244  }
245  }
246 
247  /* Now go backwards through the to_do list doing the actual reads */
248  for (list<Segment>::reverse_iterator i = to_do.rbegin(); i != to_do.rend(); ++i) {
249  DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\tPlaylist %1 read %2 @ %3 for %4, channel %5, buf @ %6 offset %7\n",
250  name(), i->region->name(), i->range.from,
251  i->range.to - i->range.from + 1, (int) chan_n,
252  buf, i->range.from - start));
253  i->region->read_at (buf + i->range.from - start, mixdown_buffer, gain_buffer, i->range.from, i->range.to - i->range.from + 1, chan_n);
254  }
255 
256  return cnt;
257 }
258 
259 void
261 {
263 
264  cerr << "Playlist \"" << _name << "\" " << endl
265  << regions.size() << " regions "
266  << endl;
267 
268  for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
269  r = *i;
270  cerr << " " << r->name() << " @ " << r << " ["
271  << r->start() << "+" << r->length()
272  << "] at "
273  << r->position()
274  << " on layer "
275  << r->layer ()
276  << endl;
277  }
278 }
279 
280 bool
282 {
284 
285  if (!r) {
286  return false;
287  }
288 
289  bool changed = false;
290 
291  {
292  RegionWriteLock rlock (this);
293 
294  for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
295 
296  RegionList::iterator tmp = i;
297  ++tmp;
298 
299  if ((*i) == region) {
300  regions.erase (i);
301  changed = true;
302  }
303 
304  i = tmp;
305  }
306 
307  for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
308 
309  set<boost::shared_ptr<Region> >::iterator xtmp = x;
310  ++xtmp;
311 
312  if ((*x) == region) {
313  all_regions.erase (x);
314  changed = true;
315  }
316 
317  x = xtmp;
318  }
319 
321  }
322 
323  if (changed) {
324  /* overload this, it normally means "removed", not destroyed */
325  notify_region_removed (region);
326  }
327 
328  return changed;
329 }
330 
331 bool
333 {
334  if (in_flush || in_set_state) {
335  return false;
336  }
337 
338  PropertyChange our_interests;
339 
340  our_interests.add (Properties::fade_in_active);
341  our_interests.add (Properties::fade_out_active);
342  our_interests.add (Properties::scale_amplitude);
343  our_interests.add (Properties::envelope_active);
344  our_interests.add (Properties::envelope);
345  our_interests.add (Properties::fade_in);
346  our_interests.add (Properties::fade_out);
347 
348  bool parent_wants_notify;
349 
350  parent_wants_notify = Playlist::region_changed (what_changed, region);
351 
352  if (parent_wants_notify || (what_changed.contains (our_interests))) {
354  }
355 
356  return true;
357 }
358 
359 void
361 {
364 
365  sort (copies.begin(), copies.end(), cmp);
366 
367  ar = boost::dynamic_pointer_cast<AudioRegion> (copies.front());
368 
369  /* disable fade in of the first region */
370 
371  if (ar) {
372  ar->set_fade_in_active (false);
373  }
374 
375  ar = boost::dynamic_pointer_cast<AudioRegion> (copies.back());
376 
377  /* disable fade out of the last region */
378 
379  if (ar) {
380  ar->set_fade_out_active (false);
381  }
382 }
383 
384 void
386 {
390 
391  if ((cr = boost::dynamic_pointer_cast<AudioRegion> (compound_region)) == 0) {
392  return;
393  }
394 
395  sort (originals.begin(), originals.end(), cmp);
396 
397  ar = boost::dynamic_pointer_cast<AudioRegion> (originals.front());
398 
399  /* copy the fade in of the first into the compound region */
400 
401  if (ar) {
402  cr->set_fade_in (ar->fade_in());
403  }
404 
405  ar = boost::dynamic_pointer_cast<AudioRegion> (originals.back());
406 
407  if (ar) {
408  /* copy the fade out of the last into the compound region */
409  cr->set_fade_out (ar->fade_out());
410  }
411 }
412 
413 void
415 {
419 
420  if (!cr) {
421  return;
422  }
423 
424  sort (originals.begin(), originals.end(), cmp);
425 
426  /* no need to call clear_changes() on the originals because that is
427  * done within Playlist::uncombine ()
428  */
429 
430  for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
431 
432  if ((ar = boost::dynamic_pointer_cast<AudioRegion> (*i)) == 0) {
433  continue;
434  }
435 
436  /* scale the uncombined regions by any gain setting for the
437  * compound one.
438  */
439 
441 
442  if (i == originals.begin()) {
443 
444  /* copy the compound region's fade in back into the first
445  original region.
446  */
447 
448  if (cr->fade_in()->back()->when <= ar->length()) {
449  /* don't do this if the fade is longer than the
450  * region
451  */
452  ar->set_fade_in (cr->fade_in());
453  }
454 
455 
456  } else if (*i == originals.back()) {
457 
458  /* copy the compound region's fade out back into the last
459  original region.
460  */
461 
462  if (cr->fade_out()->back()->when <= ar->length()) {
463  /* don't do this if the fade is longer than the
464  * region
465  */
466  ar->set_fade_out (cr->fade_out());
467  }
468 
469  }
470 
472  }
473 }
474 
475 int
476 AudioPlaylist::set_state (const XMLNode& node, int version)
477 {
478  return Playlist::set_state (node, version);
479 }
480 
481 void
483 {
484  /* Read legacy Crossfade nodes and set up region fades accordingly */
485 
486  XMLNodeList children = node.children ();
487  for (XMLNodeConstIterator i = children.begin(); i != children.end(); ++i) {
488  if ((*i)->name() == X_("Crossfade")) {
489 
490  XMLProperty* p = (*i)->property (X_("active"));
491  assert (p);
492 
493  if (!string_is_affirmative (p->value())) {
494  continue;
495  }
496 
497  if ((p = (*i)->property (X_("in"))) == 0) {
498  continue;
499  }
500 
502 
503  if (!in) {
504  warning << string_compose (_("Legacy crossfade involved an incoming region not present in playlist \"%1\" - crossfade discarded"),
505  name())
506  << endmsg;
507  continue;
508  }
509 
511  assert (in_a);
512 
513  if ((p = (*i)->property (X_("out"))) == 0) {
514  continue;
515  }
516 
518 
519  if (!out) {
520  warning << string_compose (_("Legacy crossfade involved an outgoing region not present in playlist \"%1\" - crossfade discarded"),
521  name())
522  << endmsg;
523  continue;
524  }
525 
527  assert (out_a);
528 
529  /* now decide whether to add a fade in or fade out
530  * xfade and to which region
531  */
532 
533  if (in->layer() <= out->layer()) {
534 
535  /* incoming region is below the outgoing one,
536  * so apply a fade out to the outgoing one
537  */
538 
539  const XMLNodeList c = (*i)->children ();
540 
541  for (XMLNodeConstIterator j = c.begin(); j != c.end(); ++j) {
542  if ((*j)->name() == X_("FadeOut")) {
543  out_a->fade_out()->set_state (**j, version);
544  } else if ((*j)->name() == X_("FadeIn")) {
545  out_a->inverse_fade_out()->set_state (**j, version);
546  }
547  }
548 
549  out_a->set_fade_out_active (true);
550 
551  } else {
552 
553  /* apply a fade in to the incoming region,
554  * since its above the outgoing one
555  */
556 
557  const XMLNodeList c = (*i)->children ();
558 
559  for (XMLNodeConstIterator j = c.begin(); j != c.end(); ++j) {
560  if ((*j)->name() == X_("FadeIn")) {
561  in_a->fade_in()->set_state (**j, version);
562  } else if ((*j)->name() == X_("FadeOut")) {
563  in_a->inverse_fade_in()->set_state (**j, version);
564  }
565  }
566 
567  in_a->set_fade_in_active (true);
568  }
569  }
570  }
571 }
framecnt_t read(Sample *dst, Sample *mixdown, float *gain_buffer, framepos_t start, framecnt_t cnt, uint32_t chan_n=0)
virtual void set_playlist(boost::weak_ptr< ARDOUR::Playlist >)
Definition: region.cc:411
ARDOUR::Session & _session
RangeList< T > subtract(Range< T > range, RangeList< T > sub)
Definition: Range.hpp:213
LIBARDOUR_API PBD::PropertyDescriptor< bool > fade_out_active
Definition: audioregion.cc:65
boost::shared_ptr< Region > region_by_id(const PBD::ID &) const
Definition: playlist.cc:2646
Evoral::Range< framepos_t > body_range() const
void set_fade_out(FadeShape, framecnt_t)
virtual bool region_changed(const PBD::PropertyChange &, boost::shared_ptr< Region >)
Definition: playlist.cc:1602
const std::string & value() const
Definition: xml++.h:159
void pre_combine(std::vector< boost::shared_ptr< Region > > &)
bool destroy_region(boost::shared_ptr< Region >)
void set_fade_out_length(framecnt_t)
LIBARDOUR_API PBD::PropertyDescriptor< bool > hidden
Definition: route_group.h:50
bool region_changed(const PBD::PropertyChange &, boost::shared_ptr< Region >)
Container::iterator begin()
shared_ptr< T > dynamic_pointer_cast(shared_ptr< U > const &r)
Definition: shared_ptr.hpp:396
boost::shared_ptr< AutomationList > fade_in()
Definition: audioregion.h:87
int set_state(const XMLNode &, int version)
void add_command(Command *const cmd)
Definition: session.h:787
boost::shared_ptr< AutomationList > fade_out()
Definition: audioregion.h:89
friend class RegionReadLock
Definition: playlist.h:392
T to
end of the range (inclusive: to lies inside the range)
Definition: Range.hpp:139
Definition: Beats.hpp:239
LIBPBD_API Transmitter warning
const XMLNodeList & children(const std::string &str=std::string()) const
Definition: xml++.cc:329
std::ostream & endmsg(std::ostream &ostr)
Definition: transmitter.h:71
LIBARDOUR_API uint64_t AudioPlayback
Definition: debug.cc:49
LIBARDOUR_API PBD::PropertyDescriptor< framepos_t > start
Definition: region.cc:63
Definition: id.h:32
std::list< XMLNode * > XMLNodeList
Definition: xml++.h:44
#define _(Text)
Definition: i18n.h:11
void post_combine(std::vector< boost::shared_ptr< Region > > &, boost::shared_ptr< Region >)
#define X_(Text)
Definition: i18n.h:13
int64_t framecnt_t
Definition: types.h:76
XMLProperty * property(const char *)
Definition: xml++.cc:413
float Sample
Definition: types.h:54
void add(Range< T > const &range)
Definition: Range.hpp:160
void pre_uncombine(std::vector< boost::shared_ptr< Region > > &, boost::shared_ptr< Region >)
std::set< boost::shared_ptr< Region > > all_regions
Definition: playlist.h:272
T from
start of the range
Definition: Range.hpp:138
Container::iterator end()
bool string_is_affirmative(const std::string &str)
Definition: convert.cc:282
Container::iterator erase(typename Container::iterator i)
Definition: amp.h:29
void notify_region_removed(boost::shared_ptr< Region >)
Definition: playlist.cc:467
AudioPlaylist(Session &, const XMLNode &, bool hidden=false)
void set_fade_in_length(framecnt_t)
gain_t scale_amplitude() const
Definition: audioregion.h:78
bool muted() const
Definition: region.h:162
std::list< Range< T > > List
Definition: Range.hpp:153
void set_fade_in(FadeShape, framecnt_t)
LIBARDOUR_API PBD::PropertyDescriptor< boost::shared_ptr< AutomationList > > fade_out
Definition: audioregion.cc:69
Evoral::OverlapType coverage(framepos_t start, framepos_t end) const
Definition: region.h:195
LIBARDOUR_API PBD::PropertyDescriptor< boost::shared_ptr< AutomationList > > fade_in
Definition: audioregion.cc:67
#define DEBUG_TRACE(bits, str)
Definition: debug.h:55
void set_fade_out_active(bool yn)
int64_t framepos_t
Definition: types.h:66
RegionListProperty regions
Definition: playlist.h:271
static int loading_state_version
Definition: stateful.h:90
PBD::Property< std::string > _name
layer_t layer() const
Definition: region.h:115
T * get() const
Definition: shared_ptr.hpp:268
bool changed() const
Definition: stateful.cc:325
void set_fade_in_active(bool yn)
framepos_t position() const
Definition: region.h:112
void load_legacy_crossfades(const XMLNode &, int version)
virtual int set_state(const XMLNode &, int version)
Definition: playlist.cc:2080
Container::size_type size() const
const char * name
void notify_contents_changed()
Definition: playlist.cc:445
boost::shared_ptr< AutomationList > inverse_fade_in()
Definition: audioregion.h:88
Definition: xml++.h:95
std::string name() const
LIBARDOUR_API PBD::PropertyDescriptor< framepos_t > position
Definition: region.cc:65
uint32_t in_set_state
Definition: playlist.h:291
Segment(boost::shared_ptr< AudioRegion > r, Evoral::Range< framepos_t > a)
void set_scale_amplitude(gain_t)
Definition: debug.h:30
List const & get()
Definition: Range.hpp:155
int set_state(const XMLNode &, int version)
boost::shared_ptr< RegionList > regions_touched_locked(framepos_t start, framepos_t end)
Definition: playlist.cc:1831
bool opaque() const
Definition: region.h:163
boost::shared_ptr< AudioRegion > region
the region
bool contains(PropertyDescriptor< T > p) const
framecnt_t length() const
Definition: region.h:114
Evoral::Range< framepos_t > range() const
Definition: region.h:157
ControlEvent * back()
framepos_t start() const
Definition: region.h:113
boost::shared_ptr< AutomationList > inverse_fade_out()
Definition: audioregion.h:90
Evoral::Range< framepos_t > range
range of the region to read, in session frames
XMLNodeList::const_iterator XMLNodeConstIterator
Definition: xml++.h:49
LIBARDOUR_API PBD::PropertyDescriptor< float > scale_amplitude
Definition: audioregion.cc:66
LIBARDOUR_API PBD::PropertyDescriptor< bool > envelope_active
Definition: audioregion.cc:61
void add(PropertyID id)
LIBARDOUR_API PBD::PropertyDescriptor< bool > fade_in_active
Definition: audioregion.cc:64
std::string string_compose(const std::string &fmt, const T1 &o1)
Definition: compose.h:208
LIBARDOUR_API PBD::PropertyDescriptor< boost::shared_ptr< AutomationList > > envelope
Definition: audioregion.cc:71
framepos_t last_frame() const
Definition: region.h:142