X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Faudio_playlist.cc;h=76d0228547d01f1aae99a151cda1976cc61636c2;hb=204baa31d842d4c2f833d319b6fa55e402a1bfb8;hp=9c3bce4196eb39b9560afa27e343c3a584fb45b3;hpb=875f0befd5fb52678d25544fcbcb6e6b55a2c483;p=ardour.git diff --git a/libs/ardour/audio_playlist.cc b/libs/ardour/audio_playlist.cc index 9c3bce4196..76d0228547 100644 --- a/libs/ardour/audio_playlist.cc +++ b/libs/ardour/audio_playlist.cc @@ -27,7 +27,7 @@ #include "ardour/audioplaylist.h" #include "ardour/audioregion.h" #include "ardour/crossfade.h" -#include "ardour/crossfade_compare.h" +#include "ardour/region_sorters.h" #include "ardour/session.h" #include "pbd/enumwriter.h" @@ -54,7 +54,7 @@ CrossfadeListProperty::CrossfadeListProperty (AudioPlaylist& pl) : SequenceProperty > > (Properties::crossfades.property_id, boost::bind (&AudioPlaylist::update, &pl, _1)) , _playlist (pl) { - + } CrossfadeListProperty::CrossfadeListProperty (CrossfadeListProperty const & p) @@ -109,8 +109,12 @@ AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden add_property (_crossfades); in_set_state++; - set_state (node, Stateful::loading_state_version); + if (set_state (node, Stateful::loading_state_version)) { + throw failed_constructor(); + } in_set_state--; + + relayer (); } AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden) @@ -125,7 +129,7 @@ AudioPlaylist::AudioPlaylist (boost::shared_ptr other, stri , _crossfades (*this) { add_property (_crossfades); - + RegionList::const_iterator in_o = other->regions.begin(); RegionList::iterator in_n = regions.begin(); @@ -164,12 +168,81 @@ AudioPlaylist::AudioPlaylist (boost::shared_ptr other, stri } } -AudioPlaylist::AudioPlaylist (boost::shared_ptr other, nframes_t start, nframes_t cnt, string name, bool hidden) +AudioPlaylist::AudioPlaylist (boost::shared_ptr other, framepos_t start, framecnt_t cnt, string name, bool hidden) : Playlist (other, start, cnt, name, hidden) , _crossfades (*this) { + RegionLock rlock2 (const_cast (other.get())); + in_set_state++; + add_property (_crossfades); - + + framepos_t const end = start + cnt - 1; + + /* Audio regions that have been created by the Playlist constructor + will currently have the same fade in/out as the regions that they + were created from. This is wrong, so reset the fades here. + */ + + RegionList::iterator ours = regions.begin (); + + for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) { + boost::shared_ptr region = boost::dynamic_pointer_cast (*i); + assert (region); + + framecnt_t fade_in = 64; + framecnt_t fade_out = 64; + + switch (region->coverage (start, end)) { + case OverlapNone: + continue; + + case OverlapInternal: + { + framecnt_t const offset = start - region->position (); + framecnt_t const trim = region->last_frame() - end; + if (region->fade_in()->back()->when > offset) { + fade_in = region->fade_in()->back()->when - offset; + } + if (region->fade_out()->back()->when > trim) { + fade_out = region->fade_out()->back()->when - trim; + } + break; + } + + case OverlapStart: { + if (end > region->position() + region->fade_in()->back()->when) + fade_in = region->fade_in()->back()->when; //end is after fade-in, preserve the fade-in + if (end > region->last_frame() - region->fade_out()->back()->when) + fade_out = region->fade_out()->back()->when - ( region->last_frame() - end ); //end is inside the fadeout, preserve the fades endpoint + break; + } + + case OverlapEnd: { + if (start < region->last_frame() - region->fade_out()->back()->when) //start is before fade-out, preserve the fadeout + fade_out = region->fade_out()->back()->when; + + if (start < region->position() + region->fade_in()->back()->when) + fade_in = region->fade_in()->back()->when - (start - region->position()); //end is inside the fade-in, preserve the fade-in endpoint + break; + } + + case OverlapExternal: + fade_in = region->fade_in()->back()->when; + fade_out = region->fade_out()->back()->when; + break; + } + + boost::shared_ptr our_region = boost::dynamic_pointer_cast (*ours); + assert (our_region); + + our_region->set_fade_in_length (fade_in); + our_region->set_fade_out_length (fade_out); + ++ours; + } + + in_set_state--; + /* this constructor does NOT notify others (session) */ } @@ -184,14 +257,14 @@ struct RegionSortByLayer { } }; -ARDOUR::nframes_t -AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nframes_t start, - nframes_t cnt, unsigned chan_n) +ARDOUR::framecnt_t +AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, framepos_t start, + framecnt_t cnt, unsigned chan_n) { - nframes_t ret = cnt; - nframes_t end; - nframes_t read_frames; - nframes_t skip_frames; + framecnt_t ret = cnt; + + DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Playlist %1 read @ %2 for %3, channel %4, regions %5 xfades %6\n", + name(), start, cnt, chan_n, regions.size(), _crossfades.size())); /* optimizing this memset() away involves a lot of conditionals that may well cause more of a hit due to cache misses @@ -214,17 +287,11 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nf Glib::RecMutex::Lock rm (region_lock); - end = start + cnt - 1; - read_frames = 0; - skip_frames = 0; - _read_data_count = 0; - - _read_data_count = 0; + framepos_t const end = start + cnt - 1; - RegionList* rlist = regions_to_read (start, start+cnt); + boost::shared_ptr rlist = regions_to_read (start, start+cnt); if (rlist->empty()) { - delete rlist; return cnt; } @@ -239,9 +306,17 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nf } } + DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Checking %1 xfades\n", _crossfades.size())); + for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) { + DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 check xfade between %2 and %3 ... [ %4 ... %5 | %6 ... %7]\n", + name(), (*i)->out()->name(), (*i)->in()->name(), + (*i)->first_frame(), (*i)->last_frame(), + start, end)); if ((*i)->coverage (start, end) != OverlapNone) { relevant_xfades[(*i)->upper_layer()].push_back (*i); + DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\t\txfade is relevant (coverage = %2), place on layer %1\n", + (*i)->upper_layer(), enum_2_string ((*i)->coverage (start, end)))); } } @@ -258,6 +333,8 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nf for (vector::iterator l = relevant_layers.begin(); l != relevant_layers.end(); ++l) { + DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read for layer %1\n", *l)); + vector > r (relevant_regions[*l]); vector >& x (relevant_xfades[*l]); @@ -266,20 +343,15 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nf boost::shared_ptr ar = boost::dynamic_pointer_cast(*i); DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read from region %1\n", ar->name())); assert(ar); - ar->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n, read_frames, skip_frames); - _read_data_count += ar->read_data_count(); + ar->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n); } for (vector >::iterator i = x.begin(); i != x.end(); ++i) { + DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read from xfade between %1 & %2\n", (*i)->out()->name(), (*i)->in()->name())); (*i)->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n); - - /* don't JACK up _read_data_count, since its the same data as we just - read from the regions, and the OS should handle that for us. - */ } } - delete rlist; return ret; } @@ -418,7 +490,7 @@ AudioPlaylist::check_dependents (boost::shared_ptr r, bool norefresh) boost::shared_ptr top; boost::shared_ptr bottom; boost::shared_ptr xfade; - RegionList* touched_regions = 0; + boost::shared_ptr touched_regions; if (in_set_state || in_partition) { return; @@ -450,6 +522,10 @@ AudioPlaylist::check_dependents (boost::shared_ptr r, bool norefresh) continue; } + if (other->position() == r->position() && other->length() == r->length()) { + /* precise overlay of two regions - no xfade */ + continue; + } if (other->layer() < region->layer()) { top = region; @@ -465,8 +541,7 @@ AudioPlaylist::check_dependents (boost::shared_ptr r, bool norefresh) OverlapType c = top->coverage (bottom->position(), bottom->last_frame()); - delete touched_regions; - touched_regions = 0; + touched_regions.reset (); try { framecnt_t xfade_length; @@ -497,7 +572,8 @@ AudioPlaylist::check_dependents (boost::shared_ptr r, bool norefresh) if (top_region_at (top->first_frame()) == top) { - xfade = boost::shared_ptr (new Crossfade (top, bottom, xfade_length, top->first_frame(), StartOfIn)); + xfade = boost::shared_ptr (new Crossfade (top, bottom, xfade_length, StartOfIn)); + xfade->set_position (top->first_frame()); add_crossfade (xfade); } @@ -508,7 +584,8 @@ AudioPlaylist::check_dependents (boost::shared_ptr r, bool norefresh) would cover it). */ - xfade = boost::shared_ptr (new Crossfade (bottom, top, xfade_length, top->last_frame() - xfade_length, EndOfOut)); + xfade = boost::shared_ptr (new Crossfade (bottom, top, xfade_length, EndOfOut)); + xfade->set_position (top->last_frame() - xfade_length); add_crossfade (xfade); } break; @@ -577,8 +654,6 @@ AudioPlaylist::check_dependents (boost::shared_ptr r, bool norefresh) } } - - delete touched_regions; } void @@ -636,7 +711,9 @@ AudioPlaylist::set_state (const XMLNode& node, int version) in_set_state++; - Playlist::set_state (node, version); + if (Playlist::set_state (node, version)) { + return -1; + } freeze (); @@ -841,15 +918,13 @@ AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared } void -AudioPlaylist::crossfades_at (nframes_t frame, Crossfades& clist) +AudioPlaylist::crossfades_at (framepos_t frame, Crossfades& clist) { RegionLock rlock (this); for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) { - nframes_t start, end; - - start = (*i)->position(); - end = start + (*i)->overlap_length(); // not length(), important difference + framepos_t const start = (*i)->position (); + framepos_t const end = start + (*i)->overlap_length(); // not length(), important difference if (frame >= start && frame <= end) { clist.push_back (*i); @@ -890,3 +965,242 @@ AudioPlaylist::find_crossfade (const PBD::ID& id) const return *i; } + +struct crossfade_triple { + boost::shared_ptr old_in; + boost::shared_ptr new_in; + boost::shared_ptr new_out; +}; + +void +AudioPlaylist::copy_dependents (const vector& old_and_new, Playlist* other) const +{ + AudioPlaylist* other_audio = dynamic_cast(other); + + if (!other_audio) { + return; + } + + /* our argument is a vector of old and new regions. Each old region + might be participant in a crossfade that is already present. Each new + region is a copy of the old region, present in the other playlist. + + our task is to find all the relevant xfades in our playlist (involving + the "old" regions) and place copies of them in the other playlist. + */ + + typedef map,crossfade_triple> CrossfadeInfo; + CrossfadeInfo crossfade_info; + + /* build up a record that links crossfades, old regions and new regions + */ + + for (vector::const_iterator on = old_and_new.begin(); on != old_and_new.end(); ++on) { + + for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) { + + if ((*i)->in() == on->first) { + + CrossfadeInfo::iterator cf; + + if ((cf = crossfade_info.find (*i)) != crossfade_info.end()) { + + /* already have a record for the old fade-in region, + so note the new fade-in region + */ + + cf->second.new_in = on->second; + + } else { + + /* add a record of this crossfade, keeping an association + with the new fade-in region + */ + + crossfade_triple ct; + + ct.old_in = on->first; + ct.new_in = on->second; + + crossfade_info[*i] = ct; + } + + } else if ((*i)->out() == on->first) { + + /* this old region is the fade-out region of this crossfade */ + + CrossfadeInfo::iterator cf; + + if ((cf = crossfade_info.find (*i)) != crossfade_info.end()) { + + /* already have a record for this crossfade, so just keep + an association for the new fade out region + */ + + cf->second.new_out = on->second; + + } else { + + /* add a record of this crossfade, keeping an association + with the new fade-in region + */ + + crossfade_triple ct; + + ct.old_in = on->first; + ct.new_out = on->second; + + crossfade_info[*i] = ct; + } + } + } + } + + for (CrossfadeInfo::iterator ci = crossfade_info.begin(); ci != crossfade_info.end(); ++ci) { + + /* for each crossfade that involves at least two of the old regions, + create a new identical crossfade with the new regions + */ + + if (!ci->second.new_in || !ci->second.new_out) { + continue; + } + + boost::shared_ptr new_xfade (new Crossfade (ci->first, + boost::dynamic_pointer_cast(ci->second.new_in), + boost::dynamic_pointer_cast(ci->second.new_out))); + + /* add it at the right position - which must be at the start + * of the fade-in region + */ + + new_xfade->set_position (ci->second.new_in->position()); + other_audio->add_crossfade (new_xfade); + } +} + +void +AudioPlaylist::pre_combine (vector >& copies) +{ + RegionSortByPosition cmp; + boost::shared_ptr ar; + + sort (copies.begin(), copies.end(), cmp); + + ar = boost::dynamic_pointer_cast (copies.front()); + + /* disable fade in of the first region */ + + if (ar) { + ar->set_fade_in_active (false); + } + + ar = boost::dynamic_pointer_cast (copies.back()); + + /* disable fade out of the last region */ + + if (ar) { + ar->set_fade_out_active (false); + } +} + +void +AudioPlaylist::post_combine (vector >& originals, boost::shared_ptr compound_region) +{ + RegionSortByPosition cmp; + boost::shared_ptr ar; + boost::shared_ptr cr; + + if ((cr = boost::dynamic_pointer_cast (compound_region)) == 0) { + return; + } + + sort (originals.begin(), originals.end(), cmp); + + ar = boost::dynamic_pointer_cast (originals.front()); + + /* copy the fade in of the first into the compound region */ + + if (ar) { + cr->set_fade_in (ar->fade_in()); + } + + ar = boost::dynamic_pointer_cast (originals.back()); + + if (ar) { + /* copy the fade out of the last into the compound region */ + cr->set_fade_out (ar->fade_out()); + } +} + +void +AudioPlaylist::pre_uncombine (vector >& originals, boost::shared_ptr compound_region) +{ + RegionSortByPosition cmp; + boost::shared_ptr ar; + boost::shared_ptr cr = boost::dynamic_pointer_cast(compound_region); + + if (!cr) { + return; + } + + sort (originals.begin(), originals.end(), cmp); + + /* no need to call clear_changes() on the originals because that is + * done within Playlist::uncombine () + */ + + for (vector >::iterator i = originals.begin(); i != originals.end(); ++i) { + + if ((ar = boost::dynamic_pointer_cast (*i)) == 0) { + continue; + } + + /* scale the uncombined regions by any gain setting for the + * compound one. + */ + + ar->set_scale_amplitude (ar->scale_amplitude() * cr->scale_amplitude()); + + if (i == originals.begin()) { + + /* copy the compound region's fade in back into the first + original region. + */ + + if (cr->fade_in()->back()->when <= ar->length()) { + /* don't do this if the fade is longer than the + * region + */ + ar->set_fade_in (cr->fade_in()); + } + + + } else if (*i == originals.back()) { + + /* copy the compound region's fade out back into the last + original region. + */ + + if (cr->fade_out()->back()->when <= ar->length()) { + /* don't do this if the fade is longer than the + * region + */ + ar->set_fade_out (cr->fade_out()); + } + + } + + _session.add_command (new StatefulDiffCommand (*i)); + } +} + +void +AudioPlaylist::get_equivalent_crossfades (boost::shared_ptr c, vector > & results) +{ + for (list >::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) { + if ((*i)->equivalent (c)) { + results.push_back (*i); + } + } +}