summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarl Hetherington <carl@carlh.net>2012-04-16 16:32:22 +0000
committerCarl Hetherington <carl@carlh.net>2012-04-16 16:32:22 +0000
commita2897ecef6da6a458aa1de8c2d9973a1e809dca2 (patch)
tree189e34b829823fc73d11fba249f283e00336d44d
parent02c498a8fa1c2e47988a256321bdcf5e9e869de1 (diff)
Fairly major change to the way in which crossfades are handled;
they are now done with region fades, rather than separate objects. After this commit, Ardour will try to convert your session files to the new crossfade format, but will make a backup in your session folder first. If you have works in progress using Ardour 3 it is ***STRONGLY RECOMMENDED*** that you back up session files before updating to this commit. git-svn-id: svn://localhost/ardour2/branches/3.0@11986 d708f5d6-7413-0410-9779-e7cbd77b26cf
-rw-r--r--gtk2_ardour/audio_streamview.cc297
-rw-r--r--gtk2_ardour/audio_streamview.h27
-rw-r--r--gtk2_ardour/audio_time_axis.cc65
-rw-r--r--gtk2_ardour/audio_time_axis.h5
-rw-r--r--gtk2_ardour/crossfade_view.cc302
-rw-r--r--gtk2_ardour/editor.cc226
-rw-r--r--gtk2_ardour/editor.h27
-rw-r--r--gtk2_ardour/editor_actions.cc2
-rw-r--r--gtk2_ardour/editor_canvas_events.cc122
-rw-r--r--gtk2_ardour/editor_drag.cc2
-rw-r--r--gtk2_ardour/editor_ops.cc22
-rw-r--r--gtk2_ardour/editor_selection.cc50
-rw-r--r--gtk2_ardour/midi_time_axis.cc1
-rw-r--r--gtk2_ardour/public_editor.h2
-rw-r--r--gtk2_ardour/route_time_axis.cc1
-rw-r--r--gtk2_ardour/streamview.cc2
-rw-r--r--gtk2_ardour/streamview.h2
-rw-r--r--gtk2_ardour/time_selection.cc6
-rw-r--r--gtk2_ardour/wscript2
-rw-r--r--libs/ardour/ardour/audioplaylist.h68
-rw-r--r--libs/ardour/ardour/audioregion.h13
-rw-r--r--libs/ardour/ardour/crossfade.h180
-rw-r--r--libs/ardour/ardour/crossfade_binder.h52
-rw-r--r--libs/ardour/ardour/diskstream.h6
-rw-r--r--libs/ardour/ardour/midi_playlist.h6
-rw-r--r--libs/ardour/ardour/playlist.h19
-rw-r--r--libs/ardour/ardour/region.h18
-rw-r--r--libs/ardour/ardour/types.h20
-rw-r--r--libs/ardour/audio_diskstream.cc2
-rw-r--r--libs/ardour/audio_playlist.cc945
-rw-r--r--libs/ardour/audioregion.cc192
-rw-r--r--libs/ardour/butler.cc3
-rw-r--r--libs/ardour/crossfade.cc1008
-rw-r--r--libs/ardour/crossfade_binder.cc63
-rw-r--r--libs/ardour/diskstream.cc14
-rw-r--r--libs/ardour/enums.cc8
-rw-r--r--libs/ardour/globals.cc76
-rw-r--r--libs/ardour/graph.cc1
-rw-r--r--libs/ardour/midi_diskstream.cc2
-rw-r--r--libs/ardour/midi_playlist.cc28
-rw-r--r--libs/ardour/playlist.cc304
-rw-r--r--libs/ardour/region.cc2
-rw-r--r--libs/ardour/session.cc3
-rw-r--r--libs/ardour/session_butler.cc1
-rw-r--r--libs/ardour/session_command.cc15
-rw-r--r--libs/ardour/session_process.cc2
-rw-r--r--libs/ardour/session_state.cc2
-rw-r--r--libs/ardour/test/audio_region_test.cc85
-rw-r--r--libs/ardour/test/audio_region_test.h15
-rw-r--r--libs/ardour/test/playlist_read_test.cc171
-rw-r--r--libs/ardour/test/playlist_read_test.h27
-rw-r--r--libs/ardour/test/tempo_test.cc9
-rw-r--r--libs/ardour/test/test_globals.cc4
-rw-r--r--libs/ardour/test/test_globals.h3
-rw-r--r--libs/ardour/test/test_needing_playlist_and_regions.cc20
-rw-r--r--libs/ardour/wscript10
-rw-r--r--libs/evoral/evoral/ControlList.hpp1
-rw-r--r--libs/evoral/evoral/Range.hpp219
-rw-r--r--libs/evoral/evoral/types.hpp17
-rwxr-xr-xlibs/evoral/run-tests.sh4
-rw-r--r--libs/evoral/src/ControlList.cpp2
-rw-r--r--libs/evoral/src/Curve.cpp41
-rw-r--r--libs/evoral/test/RangeTest.cpp84
-rw-r--r--libs/evoral/test/RangeTest.hpp19
-rw-r--r--libs/evoral/wscript1
65 files changed, 1140 insertions, 3808 deletions
diff --git a/gtk2_ardour/audio_streamview.cc b/gtk2_ardour/audio_streamview.cc
index c7afa6e3bf..b01fbdc630 100644
--- a/gtk2_ardour/audio_streamview.cc
+++ b/gtk2_ardour/audio_streamview.cc
@@ -46,7 +46,6 @@
#include "selection.h"
#include "public_editor.h"
#include "ardour_ui.h"
-#include "crossfade_view.h"
#include "rgb_macros.h"
#include "gui_thread.h"
#include "utils.h"
@@ -61,32 +60,12 @@ using namespace Editing;
AudioStreamView::AudioStreamView (AudioTimeAxisView& tv)
: StreamView (tv)
{
- crossfades_visible = tv.session()->config.get_xfades_visible ();
color_handler ();
_amplitude_above_axis = 1.0;
Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&AudioStreamView::parameter_changed, this, _1), gui_context());
}
-AudioStreamView::~AudioStreamView ()
-{
- for (CrossfadeViewList::iterator xi = crossfade_views.begin(); xi != crossfade_views.end(); ++xi) {
- delete xi->second;
- }
-}
-
-int
-AudioStreamView::set_samples_per_unit (gdouble spp)
-{
- StreamView::set_samples_per_unit(spp);
-
- for (CrossfadeViewList::iterator xi = crossfade_views.begin(); xi != crossfade_views.end(); ++xi) {
- xi->second->set_samples_per_unit (spp);
- }
-
- return 0;
-}
-
int
AudioStreamView::set_amplitude_above_axis (gdouble app)
{
@@ -210,162 +189,9 @@ AudioStreamView::add_region_view_internal (boost::shared_ptr<Region> r, bool wai
}
void
-AudioStreamView::remove_region_view (boost::weak_ptr<Region> weak_r)
-{
- ENSURE_GUI_THREAD (*this, &AudioStreamView::remove_region_view, weak_r);
-
- boost::shared_ptr<Region> r (weak_r.lock());
-
- if (!r) {
- return;
- }
-
- if (!_trackview.session()->deletion_in_progress()) {
-
- for (CrossfadeViewList::iterator i = crossfade_views.begin(); i != crossfade_views.end();) {
- CrossfadeViewList::iterator tmp;
-
- tmp = i;
- ++tmp;
-
- boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(r);
- if (ar && i->second->crossfade->involves (ar)) {
- delete i->second;
- crossfade_views.erase (i);
- }
-
- i = tmp;
- }
- }
-
- StreamView::remove_region_view(r);
-}
-
-void
-AudioStreamView::undisplay_track ()
-{
- StreamView::undisplay_track ();
-
- for (CrossfadeViewList::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
- delete i->second;
- }
-
- crossfade_views.clear ();
-}
-
-void
-AudioStreamView::playlist_layered (boost::weak_ptr<Track> wtr)
-{
- boost::shared_ptr<Track> tr (wtr.lock());
-
- if (!tr) {
- return;
- }
-
- StreamView::playlist_layered (wtr);
-
- /* make sure xfades are on top and all the regionviews are stacked correctly. */
-
- for (CrossfadeViewList::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
- i->second->get_canvas_group()->raise_to_top();
- }
-}
-
-void
-AudioStreamView::playlist_switched (boost::weak_ptr<Track> wtr)
-{
- boost::shared_ptr<Track> tr (wtr.lock());
-
- if (!tr) {
- return;
- }
-
- playlist_connections.drop_connections ();
-
- StreamView::playlist_switched (tr);
-
- boost::shared_ptr<AudioPlaylist> apl = boost::dynamic_pointer_cast<AudioPlaylist> (tr->playlist());
-
- if (apl) {
- apl->NewCrossfade.connect (playlist_connections, invalidator (*this), ui_bind (&AudioStreamView::add_crossfade, this, _1), gui_context());
- }
-}
-
-void
-AudioStreamView::add_crossfade (boost::weak_ptr<Crossfade> wc)
-{
- boost::shared_ptr<Crossfade> crossfade (wc.lock());
-
- if (!crossfade) {
- return;
- }
-
- AudioRegionView* lview = 0;
- AudioRegionView* rview = 0;
-
- /* first see if we already have a CrossfadeView for this Crossfade */
-
- CrossfadeViewList::iterator i = crossfade_views.find (crossfade);
- if (i != crossfade_views.end()) {
- if (!crossfades_visible) {
- i->second->hide();
- } else {
- i->second->show ();
- }
- i->second->set_valid (true);
- return;
- }
-
- /* create a new one */
-
- for (list<RegionView *>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
- AudioRegionView* arv = dynamic_cast<AudioRegionView*>(*i);
-
- if (!lview && arv && (arv->region() == crossfade->out())) {
- lview = arv;
- }
- if (!rview && arv && (arv->region() == crossfade->in())) {
- rview = arv;
- }
- }
-
- CrossfadeView *cv = new CrossfadeView (_trackview.canvas_display (),
- _trackview,
- crossfade,
- _samples_per_unit,
- region_color,
- *lview, *rview);
- cv->set_valid (true);
- crossfade->Invalidated.connect (*this, invalidator (*this), ui_bind (&AudioStreamView::remove_crossfade, this, _1), gui_context());
- crossfade_views[cv->crossfade] = cv;
- if (!crossfades_visible) {
- cv->hide ();
- }
-
- update_content_height (cv);
-}
-
-void
-AudioStreamView::remove_crossfade (boost::shared_ptr<Region> r)
-{
- ENSURE_GUI_THREAD (*this, &AudioStreamView::remove_crossfade, r)
-
- boost::shared_ptr<Crossfade> xfade = boost::dynamic_pointer_cast<Crossfade> (r);
-
- for (CrossfadeViewList::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
- if (i->second->crossfade == xfade) {
- delete i->second;
- crossfade_views.erase (i);
- break;
- }
- }
-}
-
-void
AudioStreamView::redisplay_track ()
{
list<RegionView *>::iterator i;
- CrossfadeViewList::iterator xi, tmpx;
// Flag region views as invalid and disable drawing
for (i = region_views.begin(); i != region_views.end(); ++i) {
@@ -373,41 +199,11 @@ AudioStreamView::redisplay_track ()
(*i)->enable_display (false);
}
- // Flag crossfade views as invalid
- for (xi = crossfade_views.begin(); xi != crossfade_views.end(); ++xi) {
- xi->second->set_valid (false);
- if (xi->second->visible()) {
- xi->second->show ();
- }
- }
-
- // Add and display region and crossfade views, and flag them as valid
-
+ // Add and display views, and flag them as valid
if (_trackview.is_audio_track()) {
_trackview.track()->playlist()->foreach_region(
sigc::hide_return (sigc::mem_fun (*this, &StreamView::add_region_view))
);
-
- boost::shared_ptr<AudioPlaylist> apl = boost::dynamic_pointer_cast<AudioPlaylist>(
- _trackview.track()->playlist()
- );
-
- if (apl) {
- apl->foreach_crossfade (sigc::mem_fun (*this, &AudioStreamView::add_crossfade));
- }
- }
-
- // Remove invalid crossfade views
- for (xi = crossfade_views.begin(); xi != crossfade_views.end();) {
- tmpx = xi;
- tmpx++;
-
- if (!xi->second->valid()) {
- delete xi->second;
- crossfade_views.erase (xi);
- }
-
- xi = tmpx;
}
// Stack regions by layer, and remove invalid regions
@@ -601,14 +397,6 @@ AudioStreamView::setup_rec_box ()
}
void
-AudioStreamView::foreach_crossfadeview (void (CrossfadeView::*pmf)(void))
-{
- for (CrossfadeViewList::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
- (i->second->*pmf) ();
- }
-}
-
-void
AudioStreamView::rec_peak_range_ready (framepos_t start, framecnt_t cnt, boost::weak_ptr<Source> weak_src)
{
ENSURE_GUI_THREAD (*this, &AudioStreamView::rec_peak_range_ready, start, cnt, weak_src)
@@ -745,40 +533,6 @@ AudioStreamView::hide_all_fades ()
}
void
-AudioStreamView::show_all_xfades ()
-{
- foreach_crossfadeview (&CrossfadeView::show);
- crossfades_visible = true;
-}
-
-void
-AudioStreamView::hide_all_xfades ()
-{
- foreach_crossfadeview (&CrossfadeView::hide);
- crossfades_visible = false;
-}
-
-void
-AudioStreamView::hide_xfades_involving (AudioRegionView& rv)
-{
- for (CrossfadeViewList::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
- if (i->second->crossfade->involves (rv.audio_region())) {
- i->second->fake_hide ();
- }
- }
-}
-
-void
-AudioStreamView::reveal_xfades_involving (AudioRegionView& rv)
-{
- for (CrossfadeViewList::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
- if (i->second->crossfade->involves (rv.audio_region()) && i->second->visible()) {
- i->second->show ();
- }
- }
-}
-
-void
AudioStreamView::color_handler ()
{
//case cAudioTrackBase:
@@ -797,45 +551,6 @@ AudioStreamView::color_handler ()
}
void
-AudioStreamView::update_contents_height ()
-{
- StreamView::update_contents_height ();
-
- for (CrossfadeViewList::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
- update_content_height (i->second);
- }
-}
-
-void
-AudioStreamView::update_content_height (CrossfadeView* cv)
-{
- switch (_layer_display) {
- case Overlaid:
- cv->set_y (0);
- cv->set_heights (height, height);
- break;
-
- case Stacked:
- case Expanded:
- layer_t const inl = cv->crossfade->in()->layer ();
- layer_t const outl = cv->crossfade->out()->layer ();
-
- layer_t const high = max (inl, outl);
- layer_t const low = min (inl, outl);
-
- const double h = child_height ();
-
- if (_layer_display == Stacked) {
- cv->set_y ((_layers - high - 1) * h);
- cv->set_heights ((high - low + 1) * h, h);
- } else {
- cv->set_y (((_layers - high) * 2 - 1) * h);
- cv->set_heights (((high - low) * 2 + 1) * h, h);
- }
- }
-}
-
-void
AudioStreamView::parameter_changed (string const & p)
{
if (p == "show-waveforms") {
@@ -847,13 +562,3 @@ AudioStreamView::parameter_changed (string const & p)
}
}
-void
-AudioStreamView::horizontal_position_changed ()
-{
- /* we only `draw' the bit of the curve that is visible, so we need to update here */
-
- for (CrossfadeViewList::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
- i->second->horizontal_position_changed ();
- }
-}
-
diff --git a/gtk2_ardour/audio_streamview.h b/gtk2_ardour/audio_streamview.h
index 0c50986f93..4812903a35 100644
--- a/gtk2_ardour/audio_streamview.h
+++ b/gtk2_ardour/audio_streamview.h
@@ -36,7 +36,6 @@ namespace Gdk {
namespace ARDOUR {
class Route;
- class Crossfade;
class PeakData;
class AudioRegion;
class Source;
@@ -47,33 +46,21 @@ class Selectable;
class AudioTimeAxisView;
class AudioRegionView;
class RegionSelection;
-class CrossfadeView;
class Selection;
class AudioStreamView : public StreamView
{
public:
AudioStreamView (AudioTimeAxisView&);
- ~AudioStreamView ();
-
- int set_samples_per_unit (gdouble spp);
- void horizontal_position_changed ();
int set_amplitude_above_axis (gdouble app);
gdouble get_amplitude_above_axis () { return _amplitude_above_axis; }
void set_show_waveforms (bool yn);
- void foreach_crossfadeview (void (CrossfadeView::*pmf)(void));
-
void show_all_fades ();
void hide_all_fades ();
- void show_all_xfades ();
- void hide_all_xfades ();
- void hide_xfades_involving (AudioRegionView&);
- void reveal_xfades_involving (AudioRegionView&);
-
RegionView* create_region_view (boost::shared_ptr<ARDOUR::Region>, bool, bool);
private:
@@ -82,32 +69,18 @@ class AudioStreamView : public StreamView
void update_rec_regions (ARDOUR::framepos_t, ARDOUR::framecnt_t);
RegionView* add_region_view_internal (boost::shared_ptr<ARDOUR::Region>, bool wait_for_waves, bool recording = false);
- void remove_region_view (boost::weak_ptr<ARDOUR::Region> );
void remove_audio_region_view (boost::shared_ptr<ARDOUR::AudioRegion> );
- void undisplay_track ();
void redisplay_track ();
- void playlist_layered (boost::weak_ptr<ARDOUR::Track>);
- void playlist_switched (boost::weak_ptr<ARDOUR::Track>);
-
- void add_crossfade (boost::weak_ptr<ARDOUR::Crossfade>);
- void remove_crossfade (boost::shared_ptr<ARDOUR::Region>);
void color_handler ();
- void update_contents_height ();
- void update_content_height (CrossfadeView *);
-
void parameter_changed (std::string const &);
void set_waveform_shape (ARDOUR::WaveformShape);
void set_waveform_scale (ARDOUR::WaveformScale);
double _amplitude_above_axis;
- typedef std::map<boost::shared_ptr<ARDOUR::Crossfade>, CrossfadeView*> CrossfadeViewList;
- CrossfadeViewList crossfade_views;
- bool crossfades_visible;
-
std::map<boost::shared_ptr<ARDOUR::Source>, bool> rec_data_ready_map;
bool outline_region;
diff --git a/gtk2_ardour/audio_time_axis.cc b/gtk2_ardour/audio_time_axis.cc
index b2a1d6bf10..eca908eeb4 100644
--- a/gtk2_ardour/audio_time_axis.cc
+++ b/gtk2_ardour/audio_time_axis.cc
@@ -55,7 +55,6 @@
#include "audio_time_axis.h"
#include "automation_line.h"
#include "canvas_impl.h"
-#include "crossfade_view.h"
#include "enums.h"
#include "gui_thread.h"
#include "automation_time_axis.h"
@@ -176,22 +175,6 @@ AudioTimeAxisView::hide ()
TimeAxisView::hide ();
}
-
-void
-AudioTimeAxisView::append_extra_display_menu_items ()
-{
- using namespace Menu_Helpers;
-
- MenuList& items = display_menu->items();
-
- // crossfade stuff
- if (!Profile->get_sae() && is_track ()) {
- items.push_back (MenuElem (_("Hide All Crossfades"), sigc::bind (sigc::mem_fun(*this, &AudioTimeAxisView::hide_all_xfades), true)));
- items.push_back (MenuElem (_("Show All Crossfades"), sigc::bind (sigc::mem_fun(*this, &AudioTimeAxisView::show_all_xfades), true)));
- items.push_back (SeparatorElem ());
- }
-}
-
void
AudioTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
{
@@ -366,54 +349,6 @@ AudioTimeAxisView::hide_all_automation (bool apply_to_selection)
}
void
-AudioTimeAxisView::show_all_xfades (bool apply_to_selection)
-{
- if (apply_to_selection) {
- _editor.get_selection().tracks.foreach_audio_time_axis (boost::bind (&AudioTimeAxisView::show_all_xfades, _1, false));
- } else {
- AudioStreamView* asv = audio_view ();
- if (asv) {
- asv->show_all_xfades ();
- }
- }
-}
-
-void
-AudioTimeAxisView::hide_all_xfades (bool apply_to_selection)
-{
- if (apply_to_selection) {
- _editor.get_selection().tracks.foreach_audio_time_axis (boost::bind (&AudioTimeAxisView::hide_all_xfades, _1, false));
- } else {
- AudioStreamView* asv = audio_view ();
- if (asv) {
- asv->hide_all_xfades ();
- }
- }
-}
-
-void
-AudioTimeAxisView::hide_dependent_views (TimeAxisViewItem& tavi)
-{
- AudioStreamView* asv = audio_view();
- AudioRegionView* rv;
-
- if (asv && (rv = dynamic_cast<AudioRegionView*>(&tavi)) != 0) {
- asv->hide_xfades_involving (*rv);
- }
-}
-
-void
-AudioTimeAxisView::reveal_dependent_views (TimeAxisViewItem& tavi)
-{
- AudioStreamView* asv = audio_view();
- AudioRegionView* rv;
-
- if (asv && (rv = dynamic_cast<AudioRegionView*>(&tavi)) != 0) {
- asv->reveal_xfades_involving (*rv);
- }
-}
-
-void
AudioTimeAxisView::route_active_changed ()
{
update_control_names ();
diff --git a/gtk2_ardour/audio_time_axis.h b/gtk2_ardour/audio_time_axis.h
index c096c6706a..8a5b6ab0ac 100644
--- a/gtk2_ardour/audio_time_axis.h
+++ b/gtk2_ardour/audio_time_axis.h
@@ -73,10 +73,6 @@ class AudioTimeAxisView : public RouteTimeAxisView
AudioStreamView* audio_view();
void set_show_waveforms_recording (bool yn);
- void show_all_xfades (bool apply_to_selection = false);
- void hide_all_xfades (bool apply_to_selection = false);
- void hide_dependent_views (TimeAxisViewItem&);
- void reveal_dependent_views (TimeAxisViewItem&);
/* Overridden from parent to store display state */
guint32 show_at (double y, int& nth, Gtk::VBox *parent);
@@ -94,7 +90,6 @@ class AudioTimeAxisView : public RouteTimeAxisView
void route_active_changed ();
- void append_extra_display_menu_items ();
Gtk::Menu* build_mode_menu();
void build_automation_action_menu (bool);
diff --git a/gtk2_ardour/crossfade_view.cc b/gtk2_ardour/crossfade_view.cc
deleted file mode 100644
index 10792e0a93..0000000000
--- a/gtk2_ardour/crossfade_view.cc
+++ /dev/null
@@ -1,302 +0,0 @@
-/*
- Copyright (C) 2003 Paul Davis
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <algorithm>
-
-#include "ardour/region.h"
-#include <gtkmm2ext/doi.h>
-
-#include "canvas-simplerect.h"
-#include "canvas-curve.h"
-#include "crossfade_view.h"
-#include "global_signals.h"
-#include "gui_thread.h"
-#include "rgb_macros.h"
-#include "audio_time_axis.h"
-#include "public_editor.h"
-#include "audio_region_view.h"
-#include "utils.h"
-#include "canvas_impl.h"
-#include "ardour_ui.h"
-
-using namespace ARDOUR;
-using namespace PBD;
-using namespace Editing;
-using namespace Gnome;
-using namespace Canvas;
-
-PBD::Signal1<void,CrossfadeView*> CrossfadeView::CatchDeletion;
-
-CrossfadeView::CrossfadeView (ArdourCanvas::Group *parent,
- RouteTimeAxisView &tv,
- boost::shared_ptr<Crossfade> xf,
- double spu,
- Gdk::Color& basic_color,
- AudioRegionView& lview,
- AudioRegionView& rview)
-
-
- : TimeAxisViewItem ("xfade" /*xf.name()*/, *parent, tv, spu, basic_color, xf->position(),
- xf->length(), false, false, TimeAxisViewItem::Visibility (TimeAxisViewItem::ShowFrame)),
- crossfade (xf),
- left_view (lview),
- right_view (rview),
- _all_in_view (false),
- _child_height (0)
-{
- _valid = true;
- _visible = true;
-
- fade_in = new Line (*group);
- fade_in->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_CrossfadeLine.get();
- fade_in->property_width_pixels() = 1;
-
- fade_out = new Line (*group);
- fade_out->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_CrossfadeLine.get();
- fade_out->property_width_pixels() = 1;
-
- /* no frame around the xfade or overlap rects */
-
- frame->property_outline_what() = 0;
-
- /* never show the vestigial frame */
- vestigial_frame->hide();
- show_vestigial = false;
-
- group->signal_event().connect (sigc::bind (sigc::mem_fun (tv.editor(), &PublicEditor::canvas_crossfade_view_event), group, this));
-
- PropertyChange all_crossfade_properties;
- all_crossfade_properties.add (ARDOUR::Properties::active);
- all_crossfade_properties.add (ARDOUR::Properties::follow_overlap);
- crossfade_changed (all_crossfade_properties);
-
- crossfade->PropertyChanged.connect (*this, invalidator (*this), ui_bind (&CrossfadeView::crossfade_changed, this, _1), gui_context());
- crossfade->FadesChanged.connect (*this, invalidator (*this), ui_bind (&CrossfadeView::crossfade_fades_changed, this), gui_context());
- ColorsChanged.connect (sigc::mem_fun (*this, &CrossfadeView::color_handler));
-}
-
-CrossfadeView::~CrossfadeView ()
-{
- CatchDeletion (this) ; /* EMIT_SIGNAL */
-}
-
-void
-CrossfadeView::reset_width_dependent_items (double pixel_width)
-{
- TimeAxisViewItem::reset_width_dependent_items (pixel_width);
-
- active_changed ();
-
- if (pixel_width < 5) {
- fade_in->hide();
- fade_out->hide();
- }
-}
-
-void
-CrossfadeView::set_heights (double fade_height, double child_height)
-{
- if (child_height > TimeAxisViewItem::NAME_HIGHLIGHT_THRESH) {
- fade_height -= NAME_HIGHLIGHT_SIZE;
- child_height -= NAME_HIGHLIGHT_SIZE;
- }
-
- TimeAxisViewItem::set_height (fade_height);
- _child_height = child_height;
-
- redraw_curves ();
-}
-
-void
-CrossfadeView::crossfade_changed (const PropertyChange& what_changed)
-{
- bool need_redraw_curves = false;
-
- if (what_changed.contains (ARDOUR::bounds_change)) {
- set_position (crossfade->position(), this);
- set_duration (crossfade->length(), this);
-
- /* set_duration will call reset_width_dependent_items which in turn will call redraw_curves via active_changed,
- so no need for us to call it */
- need_redraw_curves = false;
- }
-
- if (what_changed.contains (ARDOUR::Properties::follow_overlap)) {
- need_redraw_curves = true;
- }
-
- if (what_changed.contains (ARDOUR::Properties::active)) {
- /* calls redraw_curves */
- active_changed ();
- } else if (need_redraw_curves) {
- redraw_curves ();
- }
-}
-
-/** Set up our fade_in and fade_out curves to contain points for the currently visible portion
- * of the crossfade.
- */
-void
-CrossfadeView::redraw_curves ()
-{
- if (!crossfade->following_overlap()) {
- /* curves should not be visible */
- fade_in->hide ();
- fade_out->hide ();
- return;
- }
-
- if (_height < 0) {
- /* no space allocated yet */
- return;
- }
-
- PublicEditor& editor = get_time_axis_view().editor ();
-
- framepos_t const editor_left = editor.leftmost_position ();
- framepos_t const editor_right = editor_left + editor.current_page_frames ();
- framepos_t const xfade_left = crossfade->position ();
- framepos_t const xfade_right = xfade_left + crossfade->length ();
-
- /* Work out the range of our frames that are visible */
- framepos_t const min_frames = std::max (editor_left, xfade_left);
- framepos_t const max_frames = std::min (editor_right, xfade_right);
-
- _all_in_view = (editor_left <= xfade_left && editor_right >= xfade_right);
-
- /* Hence the number of points that we will render */
- int32_t const npoints = editor.frame_to_pixel (max_frames - min_frames);
-
- if (!_visible || !crossfade->active() || npoints < 3) {
- fade_in->hide();
- fade_out->hide();
- return;
- } else {
- fade_in->show();
- fade_out->show();
- }
-
- Points* points = get_canvas_points ("xfade edit redraw", npoints);
- float* vec = new float[npoints];
-
- crossfade->fade_in().curve().get_vector (min_frames - crossfade->position(), max_frames - crossfade->position(), vec, npoints);
-
- /* Work out the offset from the start of the crossfade to the visible part, in pixels */
- double xoff = 0;
- if (crossfade->position() < editor.leftmost_position()) {
- xoff = editor.frame_to_pixel (min_frames) - editor.frame_to_pixel (crossfade->position ());
- }
-
- for (int i = 0, pci = 0; i < npoints; ++i) {
- Art::Point &p = (*points)[pci++];
- p.set_x (xoff + i + 1);
-
- double const ho = crossfade->in()->layer() > crossfade->out()->layer() ? _child_height : _height;
- p.set_y (ho - ((_child_height - 2) * vec[i]));
- }
-
- fade_in->property_points() = *points;
-
- crossfade->fade_out().curve().get_vector (min_frames - crossfade->position(), max_frames - crossfade->position(), vec, npoints);
-
- for (int i = 0, pci = 0; i < npoints; ++i) {
- Art::Point &p = (*points)[pci++];
- p.set_x (xoff + i + 1);
-
- double const ho = crossfade->in()->layer() < crossfade->out()->layer() ? _child_height : _height;
- p.set_y (ho - ((_child_height - 2) * vec[i]));
- }
-
- fade_out->property_points() = *points;
-
- delete [] vec;
-
- delete points;
-
- /* XXX this is ugly, but it will have to wait till Crossfades are reimplented
- as regions. This puts crossfade views on top of a track, above all regions.
- */
-
- group->raise_to_top();
-}
-
-void
-CrossfadeView::active_changed ()
-{
- if (crossfade->active()) {
- frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_ActiveCrossfade.get();
- } else {
- frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_InactiveCrossfade.get();
- }
-
- redraw_curves ();
-}
-
-void
-CrossfadeView::color_handler ()
-{
- active_changed ();
-}
-
-void
-CrossfadeView::set_valid (bool yn)
-{
- _valid = yn;
-}
-
-void
-CrossfadeView::show ()
-{
- _visible = true;
- group->show();
- redraw_curves ();
-}
-
-void
-CrossfadeView::hide ()
-{
- group->hide();
- _visible = false;
-}
-
-void
-CrossfadeView::fake_hide ()
-{
- group->hide();
-}
-
-void
-CrossfadeView::crossfade_fades_changed ()
-{
- redraw_curves ();
-}
-
-void
-CrossfadeView::horizontal_position_changed ()
-{
- /* If the crossfade curves are entirely within the editor's visible space, there is
- no need to redraw them here as they will be completely drawn (as distinct from
- the other case where the horizontal position change will uncover `undrawn'
- sections).
- */
-
- if (!_all_in_view) {
- redraw_curves ();
- }
-}
diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc
index 3a7023ba94..51ad761d64 100644
--- a/gtk2_ardour/editor.cc
+++ b/gtk2_ardour/editor.cc
@@ -90,7 +90,6 @@
#include "canvas-noevent-text.h"
#include "canvas_impl.h"
#include "crossfade_edit.h"
-#include "crossfade_view.h"
#include "debug.h"
#include "editing.h"
#include "editor.h"
@@ -303,7 +302,6 @@ Editor::Editor ()
clicked_regionview = 0;
clicked_axisview = 0;
clicked_routeview = 0;
- clicked_crossfadeview = 0;
clicked_control_point = 0;
last_update_frame = 0;
pre_press_cursor = 0;
@@ -340,7 +338,6 @@ Editor::Editor ()
have_pending_keyboard_selection = false;
_follow_playhead = true;
_stationary_playhead = false;
- _xfade_visibility = true;
editor_ruler_menu = 0;
no_ruler_shown_update = false;
marker_menu = 0;
@@ -1514,10 +1511,6 @@ Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type,
}
break;
- case CrossfadeViewItem:
- build_menu_function = &Editor::build_track_crossfade_context_menu;
- break;
-
case StreamItem:
if (clicked_routeview->track()) {
build_menu_function = &Editor::build_track_context_menu;
@@ -1563,9 +1556,6 @@ Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type,
case SelectionItem:
break;
- case CrossfadeViewItem:
- break;
-
case StreamItem:
break;
@@ -1650,11 +1640,6 @@ Editor::build_track_region_context_menu ()
region_edit_menu_split_item = 0;
region_edit_menu_split_multichannel_item = 0;
- /* we might try to use items that are currently attached to a crossfade menu,
- so clear that, too.
- */
- track_crossfade_context_menu.items().clear ();
-
RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (clicked_axisview);
if (rtv) {
@@ -1671,54 +1656,6 @@ Editor::build_track_region_context_menu ()
return &track_region_context_menu;
}
-Menu*
-Editor::build_track_crossfade_context_menu ()
-{
- using namespace Menu_Helpers;
- MenuList& edit_items = track_crossfade_context_menu.items();
- edit_items.clear ();
-
- /* we might try to use items that are currently attached to a crossfade menu,
- so clear that, too.
- */
- track_region_context_menu.items().clear ();
-
- AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_axisview);
-
- if (atv) {
- boost::shared_ptr<Track> tr;
- boost::shared_ptr<Playlist> pl;
- boost::shared_ptr<AudioPlaylist> apl;
-
- if ((tr = atv->track()) && ((pl = tr->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) != 0)) {
-
- AudioPlaylist::Crossfades xfades;
- framepos_t where;
- bool ignored;
-
- /* The xfade menu is a bit of a special case, as we always use the mouse position
- to decide whether or not to display it (rather than the edit point). No particularly
- strong reasons for this, other than it is a bit surprising to right-click on a xfade
- and not get a menu.
- */
- mouse_frame (where, ignored);
- apl->crossfades_at (where, xfades);
-
- bool const many = xfades.size() > 1;
-
- for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) {
- add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many);
- }
-
- add_region_context_items (edit_items, tr);
- }
- }
-
- add_dstream_context_items (edit_items);
-
- return &track_crossfade_context_menu;
-}
-
void
Editor::analyze_region_selection ()
{
@@ -1769,73 +1706,6 @@ Editor::build_track_selection_context_menu ()
return &track_selection_context_menu;
}
-/** Add context menu items relevant to crossfades.
- * @param edit_items List to add the items to.
- */
-void
-Editor::add_crossfade_context_items (AudioStreamView* view, boost::shared_ptr<Crossfade> xfade, Menu_Helpers::MenuList& edit_items, bool many)
-{
- using namespace Menu_Helpers;
- Menu *xfade_menu = manage (new Menu);
- MenuList& items = xfade_menu->items();
- xfade_menu->set_name ("ArdourContextMenu");
- string str;
-
- if (xfade->active()) {
- str = _("Mute");
- } else {
- str = _("Unmute");
- }
-
- items.push_back (
- MenuElem (str, sigc::bind (sigc::mem_fun (*this, &Editor::toggle_xfade_active), &view->trackview(), boost::weak_ptr<Crossfade> (xfade)))
- );
-
- items.push_back (
- MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun (*this, &Editor::edit_xfade), boost::weak_ptr<Crossfade> (xfade)))
- );
-
- if (xfade->can_follow_overlap()) {
-
- if (xfade->following_overlap()) {
- str = _("Convert to Short");
- } else {
- str = _("Convert to Full");
- }
-
- items.push_back (
- MenuElem (str, sigc::bind (sigc::mem_fun (*this, &Editor::toggle_xfade_length), &view->trackview(), xfade))
- );
- }
-
- if (many) {
- str = xfade->out()->name();
- str += "->";
- str += xfade->in()->name();
- } else {
- str = _("Crossfade");
- }
-
- edit_items.push_back (MenuElem (str, *xfade_menu));
- edit_items.push_back (SeparatorElem());
-}
-
-void
-Editor::xfade_edit_left_region ()
-{
- if (clicked_crossfadeview) {
- clicked_crossfadeview->left_view.show_region_editor ();
- }
-}
-
-void
-Editor::xfade_edit_right_region ()
-{
- if (clicked_crossfadeview) {
- clicked_crossfadeview->right_view.show_region_editor ();
- }
-}
-
void
Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, boost::shared_ptr<Track> track)
{
@@ -2391,12 +2261,6 @@ Editor::set_state (const XMLNode& node, int /*version*/)
_regions->reset_sort_type ((RegionListSortType) string_2_enum (prop->value(), st), true);
}
- if ((prop = node.property ("xfades-visible"))) {
- bool yn = string_is_affirmative (prop->value());
- _xfade_visibility = !yn;
- // set_xfade_visibility (yn);
- }
-
if ((prop = node.property ("show-editor-mixer"))) {
Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
@@ -2523,7 +2387,6 @@ Editor::get_state ()
node->add_property ("maximised", _maximised ? "yes" : "no");
node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no");
- node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
node->add_property ("region-list-sort-type", enum_2_string (_regions->sort_type ()));
node->add_property ("mouse-mode", enum2str(mouse_mode));
node->add_property ("internal-edit", _internal_editing ? "yes" : "no");
@@ -3745,80 +3608,6 @@ Editor::set_stationary_playhead (bool yn)
}
}
-void
-Editor::toggle_xfade_active (RouteTimeAxisView* tv, boost::weak_ptr<Crossfade> wxfade)
-{
- boost::shared_ptr<Crossfade> xfade (wxfade.lock());
- if (!xfade) {
- return;
- }
-
- vector<boost::shared_ptr<Crossfade> > all = get_equivalent_crossfades (*tv, xfade, ARDOUR::Properties::edit.property_id);
-
- _session->begin_reversible_command (_("Change crossfade active state"));
-
- for (vector<boost::shared_ptr<Crossfade> >::iterator i = all.begin(); i != all.end(); ++i) {
- (*i)->clear_changes ();
- (*i)->set_active (!(*i)->active());
- _session->add_command (new StatefulDiffCommand (*i));
- }
-
- _session->commit_reversible_command ();
-}
-
-void
-Editor::toggle_xfade_length (RouteTimeAxisView* tv, boost::weak_ptr<Crossfade> wxfade)
-{
- boost::shared_ptr<Crossfade> xfade (wxfade.lock());
- if (!xfade) {
- return;
- }
-
- vector<boost::shared_ptr<Crossfade> > all = get_equivalent_crossfades (*tv, xfade, ARDOUR::Properties::edit.property_id);
-
- /* This can't be a StatefulDiffCommand as the fade shapes are not
- managed by the Stateful properties system.
- */
- _session->begin_reversible_command (_("Change crossfade length"));
-
- for (vector<boost::shared_ptr<Crossfade> >::iterator i = all.begin(); i != all.end(); ++i) {
- XMLNode& before = (*i)->get_state ();
- (*i)->set_follow_overlap (!(*i)->following_overlap());
- XMLNode& after = (*i)->get_state ();
-
- _session->add_command (new MementoCommand<Crossfade> (*i->get(), &before, &after));
- }
-
- _session->commit_reversible_command ();
-}
-
-void
-Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
-{
- boost::shared_ptr<Crossfade> xfade (wxfade.lock());
-
- if (!xfade) {
- return;
- }
-
- CrossfadeEditor cew (_session, xfade, xfade->fade_in().get_min_y(), 1.0);
-
- ensure_float (cew);
-
- switch (cew.run ()) {
- case RESPONSE_ACCEPT:
- break;
- default:
- return;
- }
-
- cew.apply ();
- PropertyChange all_crossfade_properties;
- all_crossfade_properties.add (ARDOUR::Properties::active);
- all_crossfade_properties.add (ARDOUR::Properties::follow_overlap);
- xfade->PropertyChanged (all_crossfade_properties);
-}
-
PlaylistSelector&
Editor::playlist_selector () const
{
@@ -4396,19 +4185,6 @@ Editor::idle_visual_changer ()
double const last_time_origin = horizontal_position ();
- if (p & VisualChange::TimeOrigin) {
- /* This is a bit of a hack, but set_frames_per_unit
- below will (if called) end up with the
- CrossfadeViews looking at Editor::leftmost_frame,
- and if we're changing origin and zoom in the same
- operation it will be the wrong value unless we
- update it here.
- */
-
- leftmost_frame = pending_visual_change.time_origin;
- assert (leftmost_frame >= 0);
- }
-
if (p & VisualChange::ZoomLevel) {
set_frames_per_unit (pending_visual_change.frames_per_unit);
@@ -5382,7 +5158,6 @@ Editor::session_going_away ()
clicked_regionview = 0;
clicked_axisview = 0;
clicked_routeview = 0;
- clicked_crossfadeview = 0;
entered_regionview = 0;
entered_track = 0;
last_update_frame = 0;
@@ -5506,7 +5281,6 @@ Editor::setup_fade_images ()
_fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-out-long-cut")));
}
-
/** @return Gtk::manage()d menu item for a given action from `editor_actions' */
Gtk::MenuItem&
Editor::action_menu_item (std::string const & name)
diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h
index 89ee98d29c..177f862939 100644
--- a/gtk2_ardour/editor.h
+++ b/gtk2_ardour/editor.h
@@ -83,7 +83,6 @@ namespace ARDOUR {
class NamedSelection;
class Session;
class Filter;
- class Crossfade;
class ChanCount;
class MidiOperator;
class Track;
@@ -106,7 +105,6 @@ class AutomationTimeAxisView;
class BundleManager;
class ButtonJoiner;
class ControlPoint;
-class CrossfadeView;
class DragManager;
class GroupedButtons;
class GUIObjectState;
@@ -370,12 +368,10 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
void toggle_measure_visibility ();
void toggle_logo_visibility ();
- /* fades/xfades */
+ /* fades */
void toggle_region_fades (int dir);
void update_region_fade_visibility ();
- bool xfade_visibility() const { return _xfade_visibility; }
- void update_xfade_visibility ();
/* redirect shared ops menu. caller must free returned menu */
@@ -632,16 +628,12 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
RegionView* clicked_regionview;
RegionSelection latest_regionviews;
uint32_t clicked_selection;
- CrossfadeView* clicked_crossfadeview;
ControlPoint* clicked_control_point;
void sort_track_selection (TrackViewList&);
void get_equivalent_regions (RegionView* rv, std::vector<RegionView*> &, PBD::PropertyID) const;
RegionSelection get_equivalent_regions (RegionSelection &, PBD::PropertyID) const;
- std::vector<boost::shared_ptr<ARDOUR::Crossfade> > get_equivalent_crossfades (
- RouteTimeAxisView&, boost::shared_ptr<ARDOUR::Crossfade>, PBD::PropertyID
- ) const;
void mapover_tracks (sigc::slot<void,RouteTimeAxisView&,uint32_t> sl, TimeAxisView*, PBD::PropertyID) const;
void mapover_tracks_with_unique_playlists (sigc::slot<void,RouteTimeAxisView&,uint32_t> sl, TimeAxisView*, PBD::PropertyID) const;
@@ -650,9 +642,6 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
void mapped_use_new_playlist (RouteTimeAxisView&, uint32_t, std::vector<boost::shared_ptr<ARDOUR::Playlist> > const &);
void mapped_use_copy_playlist (RouteTimeAxisView&, uint32_t, std::vector<boost::shared_ptr<ARDOUR::Playlist> > const &);
void mapped_clear_playlist (RouteTimeAxisView&, uint32_t);
- void mapped_get_equivalent_crossfades (
- RouteTimeAxisView&, uint32_t, boost::shared_ptr<ARDOUR::Crossfade>, std::vector<boost::shared_ptr<ARDOUR::Crossfade> >*
- ) const;
void button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type);
bool button_release_can_deselect;
@@ -675,7 +664,6 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
Gtk::Menu track_context_menu;
Gtk::Menu track_region_context_menu;
Gtk::Menu track_selection_context_menu;
- Gtk::Menu track_crossfade_context_menu;
Gtk::MenuItem* region_edit_menu_split_item;
Gtk::MenuItem* region_edit_menu_split_multichannel_item;
@@ -689,12 +677,10 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
Gtk::Menu* build_track_context_menu ();
Gtk::Menu* build_track_bus_context_menu ();
Gtk::Menu* build_track_region_context_menu ();
- Gtk::Menu* build_track_crossfade_context_menu ();
Gtk::Menu* build_track_selection_context_menu ();
void add_dstream_context_items (Gtk::Menu_Helpers::MenuList&);
void add_bus_context_items (Gtk::Menu_Helpers::MenuList&);
void add_region_context_items (Gtk::Menu_Helpers::MenuList&, boost::shared_ptr<ARDOUR::Track>);
- void add_crossfade_context_items (AudioStreamView*, boost::shared_ptr<ARDOUR::Crossfade>, Gtk::Menu_Helpers::MenuList&, bool many);
void add_selection_context_items (Gtk::Menu_Helpers::MenuList&);
Gtk::MenuItem* _popup_region_menu_item;
@@ -1386,7 +1372,6 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
bool canvas_selection_rect_event (GdkEvent* event,ArdourCanvas::Item*, SelectionRect*);
bool canvas_selection_start_trim_event (GdkEvent* event,ArdourCanvas::Item*, SelectionRect*);
bool canvas_selection_end_trim_event (GdkEvent* event,ArdourCanvas::Item*, SelectionRect*);
- bool canvas_crossfade_view_event (GdkEvent* event,ArdourCanvas::Item*, CrossfadeView*);
bool canvas_fade_in_event (GdkEvent* event,ArdourCanvas::Item*, AudioRegionView*);
bool canvas_fade_in_handle_event (GdkEvent* event,ArdourCanvas::Item*, AudioRegionView*);
bool canvas_fade_out_event (GdkEvent* event,ArdourCanvas::Item*, AudioRegionView*);
@@ -1864,10 +1849,6 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
void nudge_track (bool use_edit_point, bool forwards);
- /* xfades */
-
- bool _xfade_visibility;
-
#ifdef WITH_CMT
void handle_new_imageframe_time_axis_view(const std::string & track_name, void* src) ;
void handle_new_imageframe_marker_time_axis_view(const std::string & track_name, TimeAxisView* marked_track) ;
@@ -1909,12 +1890,6 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
ImageFrameSocketHandler* image_socket_listener ;
#endif
- void toggle_xfade_active (RouteTimeAxisView *, boost::weak_ptr<ARDOUR::Crossfade>);
- void toggle_xfade_length (RouteTimeAxisView *, boost::weak_ptr<ARDOUR::Crossfade>);
- void edit_xfade (boost::weak_ptr<ARDOUR::Crossfade>);
- void xfade_edit_left_region ();
- void xfade_edit_right_region ();
-
static const int32_t default_width = 995;
static const int32_t default_height = 765;
diff --git a/gtk2_ardour/editor_actions.cc b/gtk2_ardour/editor_actions.cc
index 2a55c20630..071e76ffff 100644
--- a/gtk2_ardour/editor_actions.cc
+++ b/gtk2_ardour/editor_actions.cc
@@ -1428,8 +1428,6 @@ Editor::parameter_changed (std::string p)
update_punch_range_view (true);
} else if (p == "timecode-format") {
update_just_timecode ();
- } else if (p == "xfades-visible") {
- update_xfade_visibility ();
} else if (p == "show-region-fades") {
update_region_fade_visibility ();
} else if (p == "edit-mode") {
diff --git a/gtk2_ardour/editor_canvas_events.cc b/gtk2_ardour/editor_canvas_events.cc
index 71f5a9fb21..72de353500 100644
--- a/gtk2_ardour/editor_canvas_events.cc
+++ b/gtk2_ardour/editor_canvas_events.cc
@@ -35,7 +35,6 @@
#include "audio_region_view.h"
#include "audio_streamview.h"
#include "canvas-noevent-text.h"
-#include "crossfade_view.h"
#include "audio_time_axis.h"
#include "region_gain_line.h"
#include "automation_line.h"
@@ -518,127 +517,6 @@ struct DescendingRegionLayerSorter {
};
bool
-Editor::canvas_crossfade_view_event (GdkEvent* event, ArdourCanvas::Item* item, CrossfadeView* xfv)
-{
- /* we handle only button 3 press/release events */
-
- switch (event->type) {
- case GDK_BUTTON_PRESS:
- clicked_crossfadeview = xfv;
- clicked_axisview = &clicked_crossfadeview->get_time_axis_view();
- clicked_routeview = dynamic_cast<RouteTimeAxisView*>(clicked_axisview);
- if (event->button.button == 3) {
- return button_press_handler (item, event, CrossfadeViewItem);
- }
- break;
-
- case GDK_BUTTON_RELEASE:
- if (event->button.button == 3) {
- bool ret = button_release_handler (item, event, CrossfadeViewItem);
- return ret;
- }
- break;
-
- default:
- break;
-
- }
-
- /* XXX do not forward double clicks */
-
- if (event->type == GDK_2BUTTON_PRESS) {
- return false;
- }
-
- /* proxy for an underlying regionview */
-
- /* XXX really need to check if we are in the name highlight,
- and proxy to that when required.
-
- XXX or in the trim rectangles
- */
-
- TimeAxisView& tv (xfv->get_time_axis_view());
- AudioTimeAxisView* atv;
-
- if ((atv = dynamic_cast<AudioTimeAxisView*>(&tv)) != 0) {
-
- if (atv->is_audio_track()) {
-
- boost::shared_ptr<AudioPlaylist> pl;
- if ((pl = boost::dynamic_pointer_cast<AudioPlaylist> (atv->track()->playlist())) != 0) {
-
- boost::shared_ptr<RegionList> rl = pl->regions_at (event_frame (event));
- if (!rl->empty()) {
-
- if (atv->layer_display() == Overlaid) {
-
- /* we're in overlaid mode; proxy to the uppermost region view */
-
- DescendingRegionLayerSorter cmp;
- rl->sort (cmp);
-
- RegionView* rv = atv->view()->find_view (rl->front());
-
- /* proxy */
- return canvas_region_view_event (event, rv->get_canvas_group(), rv);
-
- } else {
-
- /* we're in stacked mode; proxy to the region view under the mouse */
-
- double cx = 0;
- double cy = 0;
- switch (event->type) {
- case GDK_BUTTON_PRESS:
- case GDK_BUTTON_RELEASE:
- cx = event->button.x;
- cy = event->button.y;
- break;
- case GDK_MOTION_NOTIFY:
- cx = event->motion.x;
- cy = event->motion.y;
- break;
- case GDK_ENTER_NOTIFY:
- case GDK_LEAVE_NOTIFY:
- cx = event->crossing.x;
- cy = event->crossing.y;
- break;
- default:
- /* XXX: this may be wrong for some events */
- cx = event->button.x;
- cy = event->button.y;
- }
-
- /* position of the event within the track */
- atv->view()->canvas_item()->w2i (cx, cy);
-
- /* hence layer that we're over */
- double const c = atv->view()->child_height ();
- layer_t const l = pl->top_layer () + 1 - (cy / c);
-
- /* hence region */
- RegionList::iterator i = rl->begin();
- while (i != rl->end() && (*i)->layer() != l) {
- ++i;
- }
-
- if (i != rl->end()) {
- RegionView* rv = atv->view()->find_view (*i);
-
- /* proxy */
- return canvas_region_view_event (event, rv->get_canvas_group(), rv);
- }
- }
- }
- }
- }
- }
-
- return TRUE;
-}
-
-bool
Editor::canvas_control_point_event (GdkEvent *event, ArdourCanvas::Item* item, ControlPoint* cp)
{
switch (event->type) {
diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc
index 21b47c388f..e377509351 100644
--- a/gtk2_ardour/editor_drag.cc
+++ b/gtk2_ardour/editor_drag.cc
@@ -4117,7 +4117,7 @@ AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lin
/* check this range against all the AudioRanges that we are using */
list<AudioRange>::const_iterator k = _ranges.begin ();
while (k != _ranges.end()) {
- if (k->coverage (r.first, r.second) != OverlapNone) {
+ if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
break;
}
++k;
diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc
index 92d4ed63f5..fdca009185 100644
--- a/gtk2_ardour/editor_ops.cc
+++ b/gtk2_ardour/editor_ops.cc
@@ -2510,7 +2510,7 @@ static void
add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
{
switch (rv->region()->coverage (ar->start, ar->end - 1)) {
- case OverlapNone:
+ case Evoral::OverlapNone:
break;
default:
rs->push_back (rv);
@@ -3263,7 +3263,7 @@ Editor::trim_region_to_location (const Location& loc, const char* str)
/* require region to span proposed trim */
switch (rv->region()->coverage (loc.start(), loc.end())) {
- case OverlapInternal:
+ case Evoral::OverlapInternal:
break;
default:
continue;
@@ -5245,24 +5245,6 @@ Editor::update_region_fade_visibility ()
}
}
-/** Update crossfade visibility after its configuration has been changed */
-void
-Editor::update_xfade_visibility ()
-{
- _xfade_visibility = _session->config.get_xfades_visible ();
-
- for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
- AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
- if (v) {
- if (_xfade_visibility) {
- v->show_all_xfades ();
- } else {
- v->hide_all_xfades ();
- }
- }
- }
-}
-
void
Editor::set_edit_point ()
{
diff --git a/gtk2_ardour/editor_selection.cc b/gtk2_ardour/editor_selection.cc
index 17597d9d15..f788ef1f9d 100644
--- a/gtk2_ardour/editor_selection.cc
+++ b/gtk2_ardour/editor_selection.cc
@@ -38,7 +38,6 @@
#include "audio_streamview.h"
#include "automation_line.h"
#include "control_point.h"
-#include "crossfade_view.h"
#include "editor_regions.h"
#include "editor_cursors.h"
#include "midi_region_view.h"
@@ -479,32 +478,6 @@ Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionVi
}
void
-Editor::mapped_get_equivalent_crossfades (
- RouteTimeAxisView& tv, uint32_t, boost::shared_ptr<Crossfade> basis, vector<boost::shared_ptr<Crossfade> >* equivs
- ) const
-{
- boost::shared_ptr<Playlist> pl;
- vector<boost::shared_ptr<Crossfade> > results;
- boost::shared_ptr<Track> tr;
-
- if ((tr = tv.track()) == 0) {
- /* bus */
- return;
- }
-
- if ((pl = tr->playlist()) != 0) {
- boost::shared_ptr<AudioPlaylist> apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl);
- if (apl) {
- apl->get_equivalent_crossfades (basis, *equivs);
- }
- }
-
- /* We might have just checked basis for equivalency with itself, so we need to remove dupes */
- sort (equivs->begin (), equivs->end ());
- unique (equivs->begin (), equivs->end ());
-}
-
-void
Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
{
mapover_tracks_with_unique_playlists (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), basis, &equivalent_regions), &basis->get_time_axis_view(), property);
@@ -537,19 +510,6 @@ Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) c
return equivalent;
}
-vector<boost::shared_ptr<Crossfade> >
-Editor::get_equivalent_crossfades (RouteTimeAxisView& v, boost::shared_ptr<Crossfade> c, PBD::PropertyID prop) const
-{
- vector<boost::shared_ptr<Crossfade> > e;
- mapover_tracks_with_unique_playlists (
- sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_crossfades), c, &e),
- &v,
- prop
- );
-
- return e;
-}
-
int
Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
{
@@ -700,7 +660,7 @@ Editor::set_selected_regionview_from_click (bool press, Selection::Operation op)
/* 2. figure out the boundaries for our search for new objects */
switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
- case OverlapNone:
+ case Evoral::OverlapNone:
if (last_frame < clicked_regionview->region()->first_frame()) {
first_frame = last_frame;
last_frame = clicked_regionview->region()->last_frame();
@@ -710,7 +670,7 @@ Editor::set_selected_regionview_from_click (bool press, Selection::Operation op)
}
break;
- case OverlapExternal:
+ case Evoral::OverlapExternal:
if (last_frame < clicked_regionview->region()->first_frame()) {
first_frame = last_frame;
last_frame = clicked_regionview->region()->last_frame();
@@ -720,7 +680,7 @@ Editor::set_selected_regionview_from_click (bool press, Selection::Operation op)
}
break;
- case OverlapInternal:
+ case Evoral::OverlapInternal:
if (last_frame < clicked_regionview->region()->first_frame()) {
first_frame = last_frame;
last_frame = clicked_regionview->region()->last_frame();
@@ -730,8 +690,8 @@ Editor::set_selected_regionview_from_click (bool press, Selection::Operation op)
}
break;
- case OverlapStart:
- case OverlapEnd:
+ case Evoral::OverlapStart:
+ case Evoral::OverlapEnd:
/* nothing to do except add clicked region to selection, since it
overlaps with the existing selection in this track.
*/
diff --git a/gtk2_ardour/midi_time_axis.cc b/gtk2_ardour/midi_time_axis.cc
index 259f7c14d2..f449dcaef0 100644
--- a/gtk2_ardour/midi_time_axis.cc
+++ b/gtk2_ardour/midi_time_axis.cc
@@ -63,7 +63,6 @@
#include "automation_time_axis.h"
#include "canvas-note-event.h"
#include "canvas_impl.h"
-#include "crossfade_view.h"
#include "editor.h"
#include "enums.h"
#include "ghostregion.h"
diff --git a/gtk2_ardour/public_editor.h b/gtk2_ardour/public_editor.h
index 5a0a6138e1..ee4c7162cb 100644
--- a/gtk2_ardour/public_editor.h
+++ b/gtk2_ardour/public_editor.h
@@ -73,7 +73,6 @@ class Selection;
class AutomationLine;
class ControlPoint;
class SelectionRect;
-class CrossfadeView;
class RouteTimeAxisView;
class RegionView;
class AudioRegionView;
@@ -315,7 +314,6 @@ class PublicEditor : public Gtk::Window, public PBD::StatefulDestructible {
virtual bool canvas_selection_rect_event (GdkEvent* event, ArdourCanvas::Item*, SelectionRect*) = 0;
virtual bool canvas_selection_start_trim_event (GdkEvent* event, ArdourCanvas::Item*, SelectionRect*) = 0;
virtual bool canvas_selection_end_trim_event (GdkEvent* event, ArdourCanvas::Item*, SelectionRect*) = 0;
- virtual bool canvas_crossfade_view_event (GdkEvent* event, ArdourCanvas::Item*, CrossfadeView*) = 0;
virtual bool canvas_fade_in_event (GdkEvent* event, ArdourCanvas::Item*, AudioRegionView*) = 0;
virtual bool canvas_fade_in_handle_event (GdkEvent* event, ArdourCanvas::Item*, AudioRegionView*) = 0;
virtual bool canvas_fade_out_event (GdkEvent* event, ArdourCanvas::Item*, AudioRegionView*) = 0;
diff --git a/gtk2_ardour/route_time_axis.cc b/gtk2_ardour/route_time_axis.cc
index 13f169b96e..751d44e077 100644
--- a/gtk2_ardour/route_time_axis.cc
+++ b/gtk2_ardour/route_time_axis.cc
@@ -68,7 +68,6 @@
#include "route_time_axis.h"
#include "automation_time_axis.h"
#include "canvas_impl.h"
-#include "crossfade_view.h"
#include "enums.h"
#include "gui_thread.h"
#include "keyboard.h"
diff --git a/gtk2_ardour/streamview.cc b/gtk2_ardour/streamview.cc
index 040635d628..9ee7907a48 100644
--- a/gtk2_ardour/streamview.cc
+++ b/gtk2_ardour/streamview.cc
@@ -547,7 +547,7 @@ StreamView::get_selectables (framepos_t start, framepos_t end, double top, doubl
layer_ok = (min_layer <= l && l <= max_layer);
}
- if ((*i)->region()->coverage (start, end) != OverlapNone && layer_ok) {
+ if ((*i)->region()->coverage (start, end) != Evoral::OverlapNone && layer_ok) {
results.push_back (*i);
}
}
diff --git a/gtk2_ardour/streamview.h b/gtk2_ardour/streamview.h
index 349b380d9f..f49bc87259 100644
--- a/gtk2_ardour/streamview.h
+++ b/gtk2_ardour/streamview.h
@@ -142,7 +142,7 @@ protected:
void diskstream_changed ();
void layer_regions ();
- virtual void playlist_switched (boost::weak_ptr<ARDOUR::Track>);
+ void playlist_switched (boost::weak_ptr<ARDOUR::Track>);
virtual void color_handler () = 0;
diff --git a/gtk2_ardour/time_selection.cc b/gtk2_ardour/time_selection.cc
index 064e654cff..4bc0e2a96a 100644
--- a/gtk2_ardour/time_selection.cc
+++ b/gtk2_ardour/time_selection.cc
@@ -55,9 +55,9 @@ TimeSelection::consolidate ()
continue;
}
- if ((*a).coverage ((*b).start, (*b).end) != OverlapNone) {
- (*a).start = std::min ((*a).start, (*b).start);
- (*a).end = std::max ((*a).end, (*b).end);
+ if (a->coverage (b->start, b->end) != Evoral::OverlapNone) {
+ a->start = std::min (a->start, b->start);
+ a->end = std::max (a->end, b->end);
erase (b);
changed = true;
goto restart;
diff --git a/gtk2_ardour/wscript b/gtk2_ardour/wscript
index 575a7a50d9..338a585dca 100644
--- a/gtk2_ardour/wscript
+++ b/gtk2_ardour/wscript
@@ -67,8 +67,6 @@ gtk2_ardour_sources = [
'configinfo.cc',
'control_point.cc',
'control_point_dialog.cc',
- 'crossfade_edit.cc',
- 'crossfade_view.cc',
'curvetest.cc',
'debug.cc',
'diamond.cc',
diff --git a/libs/ardour/ardour/audioplaylist.h b/libs/ardour/ardour/audioplaylist.h
index fea70aade7..39efd2505d 100644
--- a/libs/ardour/ardour/audioplaylist.h
+++ b/libs/ardour/ardour/audioplaylist.h
@@ -33,96 +33,32 @@ class Region;
class AudioRegion;
class Source;
-namespace Properties {
- /* fake the type, since crossfades are handled by SequenceProperty which doesn't
- care about such things.
- */
- extern PBD::PropertyDescriptor<bool> crossfades;
-}
-
class AudioPlaylist;
-class CrossfadeListProperty : public PBD::SequenceProperty<std::list<boost::shared_ptr<Crossfade> > >
-{
-public:
- CrossfadeListProperty (AudioPlaylist &);
-
- void get_content_as_xml (boost::shared_ptr<Crossfade>, XMLNode &) const;
- boost::shared_ptr<Crossfade> get_content_from_xml (XMLNode const &) const;
-
-private:
- CrossfadeListProperty* clone () const;
- CrossfadeListProperty* create () const;
-
- /* copy construction only by ourselves */
- CrossfadeListProperty (CrossfadeListProperty const & p);
-
- friend class AudioPlaylist;
- /* we live and die with our playlist, no lifetime management needed */
- AudioPlaylist& _playlist;
-};
-
-
class AudioPlaylist : public ARDOUR::Playlist
{
public:
- typedef std::list<boost::shared_ptr<Crossfade> > Crossfades;
- static void make_property_quarks ();
-
AudioPlaylist (Session&, const XMLNode&, bool hidden = false);
AudioPlaylist (Session&, std::string name, bool hidden = false);
AudioPlaylist (boost::shared_ptr<const AudioPlaylist>, std::string name, bool hidden = false);
AudioPlaylist (boost::shared_ptr<const AudioPlaylist>, framepos_t start, framecnt_t cnt, std::string name, bool hidden = false);
- ~AudioPlaylist ();
-
- void clear (bool with_signals=true);
-
framecnt_t read (Sample *dst, Sample *mixdown, float *gain_buffer, framepos_t start, framecnt_t cnt, uint32_t chan_n=0);
- int set_state (const XMLNode&, int version);
-
- PBD::Signal1<void,boost::shared_ptr<Crossfade> > NewCrossfade;
-
- void foreach_crossfade (boost::function<void (boost::shared_ptr<Crossfade>)>);
- void crossfades_at (framepos_t frame, Crossfades&);
-
bool destroy_region (boost::shared_ptr<Region>);
- void update (const CrossfadeListProperty::ChangeRecord &);
-
- boost::shared_ptr<Crossfade> find_crossfade (const PBD::ID &) const;
- void get_equivalent_crossfades (boost::shared_ptr<Crossfade>, std::vector<boost::shared_ptr<Crossfade> > &);
-
protected:
- /* playlist "callbacks" */
- void notify_crossfade_added (boost::shared_ptr<Crossfade>);
- void flush_notifications (bool);
-
- void finalize_split_region (boost::shared_ptr<Region> orig, boost::shared_ptr<Region> left, boost::shared_ptr<Region> right);
-
- void refresh_dependents (boost::shared_ptr<Region> region);
- void check_dependents (boost::shared_ptr<Region> region, bool norefresh);
- void remove_dependents (boost::shared_ptr<Region> region);
- void copy_dependents (const std::vector<TwoRegions>&, Playlist*) const;
+ void check_crossfades (Evoral::Range<framepos_t>);
void pre_combine (std::vector<boost::shared_ptr<Region> >&);
void post_combine (std::vector<boost::shared_ptr<Region> >&, boost::shared_ptr<Region>);
void pre_uncombine (std::vector<boost::shared_ptr<Region> >&, boost::shared_ptr<Region>);
private:
- CrossfadeListProperty _crossfades;
- Crossfades _pending_xfade_adds;
-
- void crossfade_invalidated (boost::shared_ptr<Region>);
- XMLNode& state (bool full_state);
+ int set_state (const XMLNode&, int version);
void dump () const;
-
bool region_changed (const PBD::PropertyChange&, boost::shared_ptr<Region>);
- void crossfade_changed (const PBD::PropertyChange&);
- void add_crossfade (boost::shared_ptr<Crossfade>);
-
void source_offset_changed (boost::shared_ptr<AudioRegion> region);
};
diff --git a/libs/ardour/ardour/audioregion.h b/libs/ardour/ardour/audioregion.h
index 077f90c95a..7236cd69a9 100644
--- a/libs/ardour/ardour/audioregion.h
+++ b/libs/ardour/ardour/audioregion.h
@@ -34,7 +34,8 @@
#include "ardour/region.h"
class XMLNode;
-
+class AudioRegionTest;
+class PlaylistReadTest;
namespace ARDOUR {
@@ -92,6 +93,8 @@ class AudioRegion : public Region
boost::shared_ptr<AutomationList> fade_out() { return _fade_out; }
boost::shared_ptr<AutomationList> envelope() { return _envelope; }
+ Evoral::Range<framepos_t> body_range () const;
+
virtual framecnt_t read_peaks (PeakData *buf, framecnt_t npeaks,
framecnt_t offset, framecnt_t cnt,
uint32_t chan_n=0, double samples_per_unit= 1.0) const;
@@ -137,6 +140,9 @@ class AudioRegion : public Region
void set_fade_out (FadeShape, framecnt_t);
void set_fade_out (boost::shared_ptr<AutomationList>);
+ void set_default_fade_in ();
+ void set_default_fade_out ();
+
void set_envelope_active (bool yn);
void set_default_envelope ();
@@ -182,6 +188,9 @@ class AudioRegion : public Region
AudioRegion (SourceList &);
private:
+ friend class ::AudioRegionTest;
+ friend class ::PlaylistReadTest;
+
PBD::Property<bool> _envelope_active;
PBD::Property<bool> _default_fade_in;
PBD::Property<bool> _default_fade_out;
@@ -195,8 +204,6 @@ class AudioRegion : public Region
void init ();
void set_default_fades ();
- void set_default_fade_in ();
- void set_default_fade_out ();
void recompute_gain_at_end ();
void recompute_gain_at_start ();
diff --git a/libs/ardour/ardour/crossfade.h b/libs/ardour/ardour/crossfade.h
deleted file mode 100644
index 1c7075eb7d..0000000000
--- a/libs/ardour/ardour/crossfade.h
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- Copyright (C) 2000 Paul Davis
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#ifndef __ardour_overlap_h__
-#define __ardour_overlap_h__
-
-#include <vector>
-#include <algorithm>
-#include <boost/shared_ptr.hpp>
-
-
-#include "pbd/undo.h"
-#include "pbd/statefuldestructible.h"
-
-#include "ardour/ardour.h"
-#include "ardour/audioregion.h"
-#include "evoral/Curve.hpp"
-
-namespace ARDOUR {
- namespace Properties {
- /* "active" is defined elsewhere but we use it with crossfade also */
- extern PBD::PropertyDescriptor<bool> active;
- extern PBD::PropertyDescriptor<bool> follow_overlap;
- }
-
-enum AnchorPoint {
- StartOfIn,
- EndOfIn,
- EndOfOut
-};
-
-class Playlist;
-
-class Crossfade : public ARDOUR::AudioRegion
-{
- public:
-
- class NoCrossfadeHere: std::exception {
- public:
- virtual const char *what() const throw() { return "no crossfade should be constructed here"; }
- };
-
- /* constructor for "fixed" xfades at each end of an internal overlap */
-
- Crossfade (boost::shared_ptr<ARDOUR::AudioRegion> in, boost::shared_ptr<ARDOUR::AudioRegion> out,
- framecnt_t initial_length,
- AnchorPoint);
-
- /* constructor for xfade between two regions that are overlapped in any way
- except the "internal" case.
- */
-
- Crossfade (boost::shared_ptr<ARDOUR::AudioRegion> in, boost::shared_ptr<ARDOUR::AudioRegion> out, CrossfadeModel, bool active);
-
-
- /* copy constructor to copy a crossfade with new regions. used (for example)
- when a playlist copy is made
- */
- Crossfade (boost::shared_ptr<Crossfade>, boost::shared_ptr<ARDOUR::AudioRegion>, boost::shared_ptr<ARDOUR::AudioRegion>);
-
- /* the usual XML constructor */
-
- Crossfade (const Playlist&, XMLNode const &);
- virtual ~Crossfade();
-
- static void make_property_quarks ();
-
- XMLNode& get_state (void);
- int set_state (const XMLNode&, int version);
-
- boost::shared_ptr<ARDOUR::AudioRegion> in() const { return _in; }
- boost::shared_ptr<ARDOUR::AudioRegion> out() const { return _out; }
-
- framecnt_t read_at (Sample *buf, Sample *mixdown_buffer,
- float *gain_buffer, framepos_t position, framecnt_t cnt,
- uint32_t chan_n) const;
-
- bool refresh ();
-
- uint32_t upper_layer () const {
- return std::max (_in->layer(), _out->layer());
- }
-
- uint32_t lower_layer () const {
- return std::min (_in->layer(), _out->layer());
- }
-
- bool involves (boost::shared_ptr<ARDOUR::AudioRegion> region) const {
- return _in == region || _out == region;
- }
-
- bool involves (boost::shared_ptr<ARDOUR::AudioRegion> a, boost::shared_ptr<ARDOUR::AudioRegion> b) const {
- return (_in == a && _out == b) || (_in == b && _out == a);
- }
-
- framecnt_t overlap_length() const;
-
- PBD::Signal1<void,boost::shared_ptr<Region> > Invalidated;
-
- OverlapType coverage (framepos_t start, framepos_t end) const;
-
- static void set_buffer_size (framecnt_t);
-
- bool active () const { return _active; }
- void set_active (bool yn);
-
- bool following_overlap() const { return _follow_overlap; }
- bool can_follow_overlap() const;
- void set_follow_overlap (bool yn);
-
- AutomationList& fade_in() { return _fade_in; }
- AutomationList& fade_out() { return _fade_out; }
-
- framecnt_t set_xfade_length (framecnt_t);
-
- bool is_dependent() const { return true; }
- bool depends_on (boost::shared_ptr<Region> other) const {
- return other == _in || other == _out;
- }
-
- static framecnt_t short_xfade_length() { return _short_xfade_length; }
- static void set_short_xfade_length (framecnt_t n);
-
- /** emitted when the actual fade curves change, as opposed to one of the Stateful properties */
- PBD::Signal0<void> FadesChanged;
-
- private:
- friend struct CrossfadeComparePtr;
- friend class AudioPlaylist;
-
- static framecnt_t _short_xfade_length;
-
- boost::shared_ptr<ARDOUR::AudioRegion> _in;
- boost::shared_ptr<ARDOUR::AudioRegion> _out;
- PBD::Property<bool> _active;
- PBD::Property<bool> _follow_overlap;
- bool _in_update;
- OverlapType overlap_type;
- AnchorPoint _anchor_point;
- bool _fixed;
- int32_t layer_relation;
-
-
- mutable AutomationList _fade_in;
- mutable AutomationList _fade_out;
-
- static Sample* crossfade_buffer_out;
- static Sample* crossfade_buffer_in;
-
- void initialize ();
- void register_properties ();
- int compute (boost::shared_ptr<ARDOUR::AudioRegion>, boost::shared_ptr<ARDOUR::AudioRegion>, CrossfadeModel);
- bool update ();
-
- bool operator== (const ARDOUR::Crossfade&);
-
- protected:
- framecnt_t read_raw_internal (Sample*, framepos_t, framecnt_t, int) const;
-};
-
-
-} // namespace ARDOUR
-
-#endif /* __ardour_overlap_h__ */
diff --git a/libs/ardour/ardour/crossfade_binder.h b/libs/ardour/ardour/crossfade_binder.h
deleted file mode 100644
index 1adfdde545..0000000000
--- a/libs/ardour/ardour/crossfade_binder.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- Copyright (C) 2011 Paul Davis
- Author: Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include "pbd/id.h"
-#include "pbd/memento_command.h"
-
-class XMLNode;
-
-namespace ARDOUR {
-
-class Crossfade;
-class Playlist;
-class SessionPlaylists;
-
-/** A MementoCommandBinder for Crossfades; required because the undo record
- * may contain details of crossfades that have subsequently been deleted.
- * This class allows recovery of a crossfade from an ID once it has been
- * recreated by a previous undo step.
- */
-class CrossfadeBinder : public MementoCommandBinder<ARDOUR::Crossfade>
-{
-public:
- CrossfadeBinder (boost::shared_ptr<ARDOUR::SessionPlaylists>, PBD::ID);
- CrossfadeBinder (XMLNode *, boost::shared_ptr<SessionPlaylists>);
-
- ARDOUR::Crossfade* get () const;
- std::string type_name () const;
- void add_state (XMLNode *);
-
-private:
- boost::shared_ptr<ARDOUR::SessionPlaylists> _playlists;
- PBD::ID _id;
-};
-
-}
diff --git a/libs/ardour/ardour/diskstream.h b/libs/ardour/ardour/diskstream.h
index fb2327324d..bbc3a85dbe 100644
--- a/libs/ardour/ardour/diskstream.h
+++ b/libs/ardour/ardour/diskstream.h
@@ -243,8 +243,10 @@ class Diskstream : public SessionObject, public PublicDiskstream
virtual void use_destructive_playlist () {}
virtual void prepare_to_stop (framepos_t pos);
- void calculate_record_range(OverlapType ot, framepos_t transport_frame, framecnt_t nframes,
- framecnt_t& rec_nframes, framecnt_t& rec_offset);
+ void calculate_record_range (
+ Evoral::OverlapType ot, framepos_t transport_frame, framecnt_t nframes,
+ framecnt_t& rec_nframes, framecnt_t& rec_offset
+ );
static framecnt_t disk_io_chunk_frames;
std::vector<CaptureInfo*> capture_info;
diff --git a/libs/ardour/ardour/midi_playlist.h b/libs/ardour/ardour/midi_playlist.h
index 339e07faa1..543e1b353f 100644
--- a/libs/ardour/ardour/midi_playlist.h
+++ b/libs/ardour/ardour/midi_playlist.h
@@ -63,12 +63,6 @@ public:
protected:
- /* playlist "callbacks" */
-
- void finalize_split_region (boost::shared_ptr<Region> original, boost::shared_ptr<Region> left, boost::shared_ptr<Region> right);
-
- void check_dependents (boost::shared_ptr<Region> region, bool norefresh);
- void refresh_dependents (boost::shared_ptr<Region> region);
void remove_dependents (boost::shared_ptr<Region> region);
private:
diff --git a/libs/ardour/ardour/playlist.h b/libs/ardour/ardour/playlist.h
index d315daacaf..3648ceda8f 100644
--- a/libs/ardour/ardour/playlist.h
+++ b/libs/ardour/ardour/playlist.h
@@ -153,7 +153,8 @@ public:
boost::shared_ptr<RegionList> regions_at (framepos_t frame);
uint32_t count_regions_at (framepos_t) const;
boost::shared_ptr<RegionList> regions_touched (framepos_t start, framepos_t end);
- boost::shared_ptr<RegionList> regions_to_read (framepos_t start, framepos_t end);
+ boost::shared_ptr<RegionList> regions_with_start_within (Evoral::Range<framepos_t>);
+ boost::shared_ptr<RegionList> regions_with_end_within (Evoral::Range<framepos_t>);
uint32_t region_use_count (boost::shared_ptr<Region>) const;
boost::shared_ptr<Region> find_region (const PBD::ID&) const;
boost::shared_ptr<Region> top_region_at (framepos_t frame);
@@ -170,7 +171,7 @@ public:
void foreach_region (boost::function<void (boost::shared_ptr<Region>)>);
XMLNode& get_state ();
- int set_state (const XMLNode&, int version);
+ virtual int set_state (const XMLNode&, int version);
XMLNode& get_template ();
PBD::Signal1<void,bool> InUse;
@@ -322,10 +323,7 @@ public:
void splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude);
void splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude);
- virtual void finalize_split_region (boost::shared_ptr<Region> /*original*/, boost::shared_ptr<Region> /*left*/, boost::shared_ptr<Region> /*right*/) {}
-
- virtual void check_dependents (boost::shared_ptr<Region> /*region*/, bool /*norefresh*/) {}
- virtual void refresh_dependents (boost::shared_ptr<Region> /*region*/) {}
+ virtual void check_crossfades (Evoral::Range<framepos_t>) {}
virtual void remove_dependents (boost::shared_ptr<Region> /*region*/) {}
virtual XMLNode& state (bool);
@@ -351,14 +349,6 @@ public:
void _split_region (boost::shared_ptr<Region>, framepos_t position);
typedef std::pair<boost::shared_ptr<Region>, boost::shared_ptr<Region> > TwoRegions;
- virtual void copy_dependents (const std::vector<TwoRegions>&, Playlist*) const { }
-
- struct RegionInfo {
- boost::shared_ptr<Region> region;
- framepos_t position;
- framecnt_t length;
- framepos_t start;
- };
/* this is called before we create a new compound region */
virtual void pre_combine (std::vector<boost::shared_ptr<Region> >&) {}
@@ -372,6 +362,7 @@ public:
private:
void setup_layering_indices (RegionList const &) const;
+ void coalesce_and_check_crossfades (std::list<Evoral::Range<framepos_t> >);
boost::shared_ptr<RegionList> find_regions_at (framepos_t);
};
diff --git a/libs/ardour/ardour/region.h b/libs/ardour/ardour/region.h
index a238ff9038..788a8d90c9 100644
--- a/libs/ardour/ardour/region.h
+++ b/libs/ardour/ardour/region.h
@@ -141,6 +141,14 @@ class Region
framepos_t first_frame () const { return _position; }
framepos_t last_frame () const { return _position + _length - 1; }
+ Evoral::Range<framepos_t> last_range () const {
+ return Evoral::Range<framepos_t> (_last_position, _last_position + _last_length - 1);
+ }
+
+ Evoral::Range<framepos_t> range () const {
+ return Evoral::Range<framepos_t> (first_frame(), last_frame());
+ }
+
bool hidden () const { return _hidden; }
bool muted () const { return _muted; }
bool opaque () const { return _opaque; }
@@ -168,8 +176,14 @@ class Region
return first_frame() <= frame && frame <= last_frame();
}
- OverlapType coverage (framepos_t start, framepos_t end) const {
- return ARDOUR::coverage (first_frame(), last_frame(), start, end);
+ /** @return coverage of this region with the given range;
+ * OverlapInternal: the range is internal to this region.
+ * OverlapStart: the range overlaps the start of this region.
+ * OverlapEnd: the range overlaps the end of this region.
+ * OverlapExternal: the range overlaps all of this region.
+ */
+ Evoral::OverlapType coverage (framepos_t start, framepos_t end) const {
+ return Evoral::coverage (first_frame(), last_frame(), start, end);
}
bool equivalent (boost::shared_ptr<const Region>) const;
diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h
index 3ebf5478e6..b3c1666dbb 100644
--- a/libs/ardour/ardour/types.h
+++ b/libs/ardour/ardour/types.h
@@ -36,6 +36,8 @@
#include "pbd/id.h"
+#include "evoral/Range.hpp"
+
#include "ardour/chan_count.h"
#include <map>
@@ -101,17 +103,6 @@ namespace ARDOUR {
ARDOUR::ChanCount after;
};
- enum OverlapType {
- OverlapNone, // no overlap
- OverlapInternal, // the overlap is 100% with the object
- OverlapStart, // overlap covers start, but ends within
- OverlapEnd, // overlap begins within and covers end
- OverlapExternal // overlap extends to (at least) begin+end
- };
-
- ARDOUR::OverlapType coverage (framepos_t sa, framepos_t ea,
- framepos_t sb, framepos_t eb);
-
/* policies for inserting/pasting material where overlaps
might be an issue.
*/
@@ -278,6 +269,9 @@ namespace ARDOUR {
}
};
+ /* XXX: slightly unfortunate that there is this and Evoral::Range<>,
+ but this has a uint32_t id which Evoral::Range<> does not.
+ */
struct AudioRange {
framepos_t start;
framepos_t end;
@@ -295,8 +289,8 @@ namespace ARDOUR {
return start == other.start && end == other.end;
}
- OverlapType coverage (framepos_t s, framepos_t e) const {
- return ARDOUR::coverage (start, end, s, e);
+ Evoral::OverlapType coverage (framepos_t s, framepos_t e) const {
+ return Evoral::coverage (start, end, s, e);
}
};
diff --git a/libs/ardour/audio_diskstream.cc b/libs/ardour/audio_diskstream.cc
index 037ff34eb5..c38901f430 100644
--- a/libs/ardour/audio_diskstream.cc
+++ b/libs/ardour/audio_diskstream.cc
@@ -457,7 +457,7 @@ AudioDiskstream::process (framepos_t transport_frame, pframes_t nframes, framecn
if (record_enabled()) {
- OverlapType ot = coverage (first_recordable_frame, last_recordable_frame, transport_frame, transport_frame + nframes);
+ Evoral::OverlapType ot = Evoral::coverage (first_recordable_frame, last_recordable_frame, transport_frame, transport_frame + nframes);
calculate_record_range (ot, transport_frame, nframes, rec_nframes, rec_offset);
if (rec_nframes && !was_recording) {
diff --git a/libs/ardour/audio_playlist.cc b/libs/ardour/audio_playlist.cc
index 76d0228547..bcb0219e2a 100644
--- a/libs/ardour/audio_playlist.cc
+++ b/libs/ardour/audio_playlist.cc
@@ -26,7 +26,6 @@
#include "ardour/configuration.h"
#include "ardour/audioplaylist.h"
#include "ardour/audioregion.h"
-#include "ardour/crossfade.h"
#include "ardour/region_sorters.h"
#include "ardour/session.h"
#include "pbd/enumwriter.h"
@@ -37,77 +36,14 @@ using namespace ARDOUR;
using namespace std;
using namespace PBD;
-namespace ARDOUR {
- namespace Properties {
- PBD::PropertyDescriptor<bool> crossfades;
- }
-}
-
-void
-AudioPlaylist::make_property_quarks ()
-{
- Properties::crossfades.property_id = g_quark_from_static_string (X_("crossfades"));
- DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for crossfades = %1\n", Properties::crossfades.property_id));
-}
-
-CrossfadeListProperty::CrossfadeListProperty (AudioPlaylist& pl)
- : SequenceProperty<std::list<boost::shared_ptr<Crossfade> > > (Properties::crossfades.property_id, boost::bind (&AudioPlaylist::update, &pl, _1))
- , _playlist (pl)
-{
-
-}
-
-CrossfadeListProperty::CrossfadeListProperty (CrossfadeListProperty const & p)
- : PBD::SequenceProperty<std::list<boost::shared_ptr<Crossfade> > > (p)
- , _playlist (p._playlist)
-{
-
-}
-
-
-CrossfadeListProperty *
-CrossfadeListProperty::create () const
-{
- return new CrossfadeListProperty (_playlist);
-}
-
-CrossfadeListProperty *
-CrossfadeListProperty::clone () const
-{
- return new CrossfadeListProperty (*this);
-}
-
-void
-CrossfadeListProperty::get_content_as_xml (boost::shared_ptr<Crossfade> xfade, XMLNode & node) const
-{
- /* Crossfades are not written to any state when they are no
- longer in use, so we must write their state here.
- */
-
- XMLNode& c = xfade->get_state ();
- node.add_child_nocopy (c);
-}
-
-boost::shared_ptr<Crossfade>
-CrossfadeListProperty::get_content_from_xml (XMLNode const & node) const
-{
- XMLNodeList const c = node.children ();
- assert (c.size() == 1);
- return boost::shared_ptr<Crossfade> (new Crossfade (_playlist, *c.front()));
-}
-
-
AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden)
: Playlist (session, node, DataType::AUDIO, hidden)
- , _crossfades (*this)
{
#ifndef NDEBUG
const XMLProperty* prop = node.property("type");
assert(!prop || DataType(prop->value()) == DataType::AUDIO);
#endif
- add_property (_crossfades);
-
in_set_state++;
if (set_state (node, Stateful::loading_state_version)) {
throw failed_constructor();
@@ -119,64 +55,20 @@ AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden
AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden)
: Playlist (session, name, DataType::AUDIO, hidden)
- , _crossfades (*this)
{
- add_property (_crossfades);
}
AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, string name, bool hidden)
: Playlist (other, name, hidden)
- , _crossfades (*this)
{
- add_property (_crossfades);
-
- RegionList::const_iterator in_o = other->regions.begin();
- RegionList::iterator in_n = regions.begin();
-
- while (in_o != other->regions.end()) {
- boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*in_o);
-
- // We look only for crossfades which begin with the current region, so we don't get doubles
- for (Crossfades::const_iterator xfades = other->_crossfades.begin(); xfades != other->_crossfades.end(); ++xfades) {
- if ((*xfades)->in() == ar) {
- // We found one! Now copy it!
-
- RegionList::const_iterator out_o = other->regions.begin();
- RegionList::const_iterator out_n = regions.begin();
-
- while (out_o != other->regions.end()) {
-
- boost::shared_ptr<AudioRegion>ar2 = boost::dynamic_pointer_cast<AudioRegion>(*out_o);
-
- if ((*xfades)->out() == ar2) {
- boost::shared_ptr<AudioRegion>in = boost::dynamic_pointer_cast<AudioRegion>(*in_n);
- boost::shared_ptr<AudioRegion>out = boost::dynamic_pointer_cast<AudioRegion>(*out_n);
- boost::shared_ptr<Crossfade> new_fade = boost::shared_ptr<Crossfade> (new Crossfade (*xfades, in, out));
- add_crossfade(new_fade);
- break;
- }
-
- out_o++;
- out_n++;
- }
-// cerr << "HUH!? second region in the crossfade not found!" << endl;
- }
- }
-
- in_o++;
- in_n++;
- }
}
AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, framepos_t start, framecnt_t cnt, string name, bool hidden)
: Playlist (other, start, cnt, name, hidden)
- , _crossfades (*this)
{
RegionLock rlock2 (const_cast<AudioPlaylist*> (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
@@ -194,10 +86,10 @@ AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, fram
framecnt_t fade_out = 64;
switch (region->coverage (start, end)) {
- case OverlapNone:
+ case Evoral::OverlapNone:
continue;
- case OverlapInternal:
+ case Evoral::OverlapInternal:
{
framecnt_t const offset = start - region->position ();
framecnt_t const trim = region->last_frame() - end;
@@ -210,7 +102,7 @@ AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, fram
break;
}
- case OverlapStart: {
+ case Evoral::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)
@@ -218,7 +110,7 @@ AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, fram
break;
}
- case OverlapEnd: {
+ case Evoral::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;
@@ -227,7 +119,7 @@ AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, fram
break;
}
- case OverlapExternal:
+ case Evoral::OverlapExternal:
fade_in = region->fade_in()->back()->when;
fade_out = region->fade_out()->back()->when;
break;
@@ -246,14 +138,14 @@ AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, fram
/* this constructor does NOT notify others (session) */
}
-AudioPlaylist::~AudioPlaylist ()
-{
- _crossfades.clear ();
-}
-
-struct RegionSortByLayer {
+/** Sort by descending layer and then by ascending position */
+struct ReadSorter {
bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
- return a->layer() < b->layer();
+ if (a->layer() != b->layer()) {
+ return a->layer() > b->layer();
+ }
+
+ return a->position() < b->position();
}
};
@@ -261,10 +153,8 @@ ARDOUR::framecnt_t
AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, framepos_t start,
framecnt_t cnt, unsigned chan_n)
{
- 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()));
+ DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Playlist %1 read @ %2 for %3, channel %4, regions %5\n",
+ name(), start, cnt, chan_n, regions.size()));
/* optimizing this memset() away involves a lot of conditionals
that may well cause more of a hit due to cache misses
@@ -287,498 +177,178 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, fr
Glib::RecMutex::Lock rm (region_lock);
- framepos_t const end = start + cnt - 1;
-
- boost::shared_ptr<RegionList> rlist = regions_to_read (start, start+cnt);
-
- if (rlist->empty()) {
- return cnt;
- }
-
- map<uint32_t,vector<boost::shared_ptr<Region> > > relevant_regions;
- map<uint32_t,vector<boost::shared_ptr<Crossfade> > > relevant_xfades;
- vector<uint32_t> relevant_layers;
-
- for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ++i) {
- if ((*i)->coverage (start, end) != OverlapNone) {
- relevant_regions[(*i)->layer()].push_back (*i);
- relevant_layers.push_back ((*i)->layer());
- }
- }
-
- 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))));
- }
- }
-
-// RegionSortByLayer layer_cmp;
-// relevant_regions.sort (layer_cmp);
-
- /* XXX this whole per-layer approach is a hack that
- should be removed once Crossfades become
- CrossfadeRegions and we just grab a list of relevant
- regions and call read_at() on all of them.
+ /* Find all the regions that are involved in the bit we are reading,
+ and sort them by descending layer and ascending position.
*/
+ boost::shared_ptr<RegionList> all = regions_touched (start, start + cnt - 1);
+ all->sort (ReadSorter ());
- sort (relevant_layers.begin(), relevant_layers.end());
-
- for (vector<uint32_t>::iterator l = relevant_layers.begin(); l != relevant_layers.end(); ++l) {
-
- DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read for layer %1\n", *l));
-
- vector<boost::shared_ptr<Region> > r (relevant_regions[*l]);
- vector<boost::shared_ptr<Crossfade> >& x (relevant_xfades[*l]);
-
-
- for (vector<boost::shared_ptr<Region> >::iterator i = r.begin(); i != r.end(); ++i) {
- boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*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);
- }
+ /* This will be a list of the bits of our read range that we have
+ read completely (ie for which no more regions need to be read).
+ It is a list of ranges in session frames.
+ */
+ Evoral::RangeList<framepos_t> done;
+
+ for (RegionList::iterator i = all->begin(); i != all->end(); ++i) {
+ boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (*i);
- for (vector<boost::shared_ptr<Crossfade> >::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);
- }
- }
+ /* Trim region range to the bit we are reading */
- return ret;
-}
+ /* Work out which bits of this region need to be read;
+ first, trim to the range we are reading...
+ */
+ Evoral::Range<framepos_t> region_range = ar->range ();
+ region_range.from = max (region_range.from, start);
+ region_range.to = min (region_range.to, start + cnt - 1);
+ /* ... and then remove the bits that are already done */
+ Evoral::RangeList<framepos_t> to_do = Evoral::subtract (region_range, done);
-void
-AudioPlaylist::remove_dependents (boost::shared_ptr<Region> region)
-{
- boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
+ /* Read those bits, adding their bodies (the parts between end-of-fade-in
+ and start-of-fade-out) to the `done' list.
+ */
- if (in_set_state) {
- return;
- }
+ Evoral::RangeList<framepos_t>::List t = to_do.get ();
- if (r == 0) {
- fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
- << endmsg;
- return;
- }
+ for (Evoral::RangeList<framepos_t>::List::iterator i = t.begin(); i != t.end(); ++i) {
+ Evoral::Range<framepos_t> d = *i;
- for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ) {
+ /* Read the whole range, possibly including fades */
+ ar->read_at (buf + d.from - start, mixdown_buffer, gain_buffer, d.from, d.to - d.from + 1, chan_n);
- if ((*i)->involves (r)) {
- i = _crossfades.erase (i);
- } else {
- ++i;
+ if (ar->opaque ()) {
+ /* Cut this range down to just the body and mark it done */
+ Evoral::Range<framepos_t> body = ar->body_range ();
+ if (body.from < d.to && body.to > d.from) {
+ d.from = max (d.from, body.from);
+ d.to = min (d.to, body.to);
+ done.add (d);
+ }
+ }
}
}
-}
-
-
-void
-AudioPlaylist::flush_notifications (bool from_undo)
-{
- Playlist::flush_notifications (from_undo);
- if (in_flush) {
- return;
- }
-
- in_flush = true;
-
- Crossfades::iterator a;
- for (a = _pending_xfade_adds.begin(); a != _pending_xfade_adds.end(); ++a) {
- NewCrossfade (*a); /* EMIT SIGNAL */
- }
-
- _pending_xfade_adds.clear ();
-
- in_flush = false;
+ return cnt;
}
void
-AudioPlaylist::refresh_dependents (boost::shared_ptr<Region> r)
+AudioPlaylist::check_crossfades (Evoral::Range<framepos_t> range)
{
- boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(r);
- set<boost::shared_ptr<Crossfade> > updated;
-
- if (ar == 0) {
+ if (in_set_state || in_partition || !_session.config.get_auto_xfade ()) {
return;
}
+
+ boost::shared_ptr<RegionList> starts = regions_with_start_within (range);
+ boost::shared_ptr<RegionList> ends = regions_with_end_within (range);
- for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
-
- Crossfades::iterator tmp;
-
- tmp = x;
- ++tmp;
+ RegionList all = *starts;
+ std::copy (ends->begin(), ends->end(), back_inserter (all));
- /* only update them once */
+ all.sort (RegionSortByLayer ());
- if ((*x)->involves (ar)) {
+ set<boost::shared_ptr<Region> > done_start;
+ set<boost::shared_ptr<Region> > done_end;
- pair<set<boost::shared_ptr<Crossfade> >::iterator, bool> const u = updated.insert (*x);
+ for (RegionList::reverse_iterator i = all.rbegin(); i != all.rend(); ++i) {
+ for (RegionList::reverse_iterator j = all.rbegin(); j != all.rend(); ++j) {
- if (u.second) {
- /* x was successfully inserted into the set, so it has not already been updated */
- try {
- (*x)->refresh ();
- }
-
- catch (Crossfade::NoCrossfadeHere& err) {
- // relax, Invalidated during refresh
- }
+ if (i == j) {
+ continue;
}
- }
-
- x = tmp;
- }
-}
-
-void
-AudioPlaylist::finalize_split_region (boost::shared_ptr<Region> o, boost::shared_ptr<Region> l, boost::shared_ptr<Region> r)
-{
- boost::shared_ptr<AudioRegion> orig = boost::dynamic_pointer_cast<AudioRegion>(o);
- boost::shared_ptr<AudioRegion> left = boost::dynamic_pointer_cast<AudioRegion>(l);
- boost::shared_ptr<AudioRegion> right = boost::dynamic_pointer_cast<AudioRegion>(r);
-
- for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
- Crossfades::iterator tmp;
- tmp = x;
- ++tmp;
- boost::shared_ptr<Crossfade> fade;
+ if ((*i)->muted() || (*j)->muted()) {
+ continue;
+ }
- if ((*x)->_in == orig) {
- if (! (*x)->covers(right->position())) {
- fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, left, (*x)->_out));
- } else {
- // Overlap, the crossfade is copied on the left side of the right region instead
- fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, right, (*x)->_out));
+ if ((*i)->position() == (*j)->position() && ((*i)->length() == (*j)->length())) {
+ /* precise overlay: no xfade */
+ continue;
}
- }
- if ((*x)->_out == orig) {
- if (! (*x)->covers(right->position())) {
- fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, right));
+ if ((*i)->position() == (*j)->position() || ((*i)->last_frame() == (*j)->last_frame())) {
+ /* starts or ends match: no xfade */
+ continue;
+ }
+
+
+ boost::shared_ptr<AudioRegion> top;
+ boost::shared_ptr<AudioRegion> bottom;
+
+ if ((*i)->layer() < (*j)->layer()) {
+ top = boost::dynamic_pointer_cast<AudioRegion> (*j);
+ bottom = boost::dynamic_pointer_cast<AudioRegion> (*i);
} else {
- // Overlap, the crossfade is copied on the right side of the left region instead
- fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, left));
+ top = boost::dynamic_pointer_cast<AudioRegion> (*i);
+ bottom = boost::dynamic_pointer_cast<AudioRegion> (*j);
+ }
+
+ if (!top->opaque ()) {
+ continue;
}
- }
-
- if (fade) {
- _crossfades.remove (*x);
- add_crossfade (fade);
- }
- x = tmp;
- }
-}
-
-void
-AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
-{
- boost::shared_ptr<AudioRegion> other;
- boost::shared_ptr<AudioRegion> region;
- boost::shared_ptr<AudioRegion> top;
- boost::shared_ptr<AudioRegion> bottom;
- boost::shared_ptr<Crossfade> xfade;
- boost::shared_ptr<RegionList> touched_regions;
-
- if (in_set_state || in_partition) {
- return;
- }
-
- if ((region = boost::dynamic_pointer_cast<AudioRegion> (r)) == 0) {
- fatal << _("programming error: non-audio Region tested for overlap in audio playlist")
- << endmsg;
- return;
- }
-
- if (!norefresh) {
- refresh_dependents (r);
- }
-
-
- if (!_session.config.get_auto_xfade()) {
- return;
- }
-
- for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
- other = boost::dynamic_pointer_cast<AudioRegion> (*i);
-
- if (other == region) {
- continue;
- }
-
- if (other->muted() || region->muted()) {
- 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;
- bottom = other;
- } else {
- top = other;
- bottom = region;
- }
-
- if (!top->opaque()) {
- continue;
- }
-
- OverlapType c = top->coverage (bottom->position(), bottom->last_frame());
-
- touched_regions.reset ();
-
- try {
- framecnt_t xfade_length;
- switch (c) {
- case OverlapNone:
- break;
-
- case OverlapInternal:
- /* {=============== top =============}
- * [ ----- bottom ------- ]
- */
- break;
-
- case OverlapExternal:
-
- /* [ -------- top ------- ]
- * {=========== bottom =============}
- */
-
- /* to avoid discontinuities at the region boundaries of an internal
- overlap (this region is completely within another), we create
- two hidden crossfades at each boundary. this is not dependent
- on the auto-xfade option, because we require it as basic
- audio engineering.
- */
-
- xfade_length = min ((framecnt_t) 720, top->length());
-
- if (top_region_at (top->first_frame()) == top) {
-
- xfade = boost::shared_ptr<Crossfade> (new Crossfade (top, bottom, xfade_length, StartOfIn));
- xfade->set_position (top->first_frame());
- add_crossfade (xfade);
- }
-
- if (top_region_at (top->last_frame() - 1) == top) {
-
- /*
- only add a fade out if there is no region on top of the end of 'top' (which
- would cover it).
- */
-
- xfade = boost::shared_ptr<Crossfade> (new Crossfade (bottom, top, xfade_length, EndOfOut));
- xfade->set_position (top->last_frame() - xfade_length);
- add_crossfade (xfade);
- }
- break;
- case OverlapStart:
- /* { ==== top ============ }
- * [---- bottom -------------------]
+ Evoral::OverlapType const c = top->coverage (bottom->position(), bottom->last_frame());
+
+ if (c == Evoral::OverlapStart) {
+
+ /* top starts within bottom but covers bottom's end */
+
+ /* { ==== top ============ }
+ * [---- bottom -------------------]
*/
- if (_session.config.get_xfade_model() == FullCrossfade) {
- touched_regions = regions_touched (top->first_frame(), bottom->last_frame());
- if (touched_regions->size() <= 2) {
- xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
- add_crossfade (xfade);
- }
- } else {
-
- touched_regions = regions_touched (top->first_frame(),
- top->first_frame() + min ((framecnt_t) _session.config.get_short_xfade_seconds() * _session.frame_rate(),
- top->length()));
- if (touched_regions->size() <= 2) {
- xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
- add_crossfade (xfade);
- }
+ if (done_start.find (top) == done_start.end() && done_end.find (bottom) == done_end.end ()) {
+ framecnt_t const len = bottom->last_frame () - top->first_frame ();
+ top->set_fade_in_length (len);
+ top->set_fade_in_active (true);
+ done_start.insert (top);
+ bottom->set_fade_out_length (len);
+ bottom->set_fade_out_active (true);
+ done_end.insert (bottom);
}
- break;
- case OverlapEnd:
-
- /* [---- top ------------------------]
- * { ==== bottom ============ }
+ } else if (c == Evoral::OverlapEnd) {
+
+ /* top covers start of bottom but ends within it */
+
+ /* [---- top ------------------------]
+ * { ==== bottom ============ }
*/
- if (_session.config.get_xfade_model() == FullCrossfade) {
-
- touched_regions = regions_touched (bottom->first_frame(), top->last_frame());
- if (touched_regions->size() <= 2) {
- xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
- _session.config.get_xfade_model(), _session.config.get_xfades_active()));
- add_crossfade (xfade);
- }
-
- } else {
- touched_regions = regions_touched (bottom->first_frame(),
- bottom->first_frame() + min ((framecnt_t)_session.config.get_short_xfade_seconds() * _session.frame_rate(),
- bottom->length()));
- if (touched_regions->size() <= 2) {
- xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
- add_crossfade (xfade);
- }
+ if (done_end.find (top) == done_end.end() && done_start.find (bottom) == done_start.end ()) {
+ framecnt_t const len = top->last_frame () - bottom->first_frame ();
+ top->set_fade_out_length (len);
+ top->set_fade_out_active (true);
+ done_end.insert (top);
+ bottom->set_fade_in_length (len);
+ bottom->set_fade_in_active (true);
+ done_start.insert (bottom);
}
- break;
- default:
- xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
- _session.config.get_xfade_model(), _session.config.get_xfades_active()));
- add_crossfade (xfade);
}
}
-
- catch (failed_constructor& err) {
- continue;
- }
-
- catch (Crossfade::NoCrossfadeHere& err) {
- continue;
- }
-
- }
-}
-
-void
-AudioPlaylist::add_crossfade (boost::shared_ptr<Crossfade> xfade)
-{
- Crossfades::iterator ci;
-
- for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) {
- if (*(*ci) == *xfade) { // Crossfade::operator==()
- break;
- }
- }
-
- if (ci != _crossfades.end()) {
- // it will just go away
- } else {
- _crossfades.push_back (xfade);
-
- xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
- xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
-
- notify_crossfade_added (xfade);
- }
-}
-
-void AudioPlaylist::notify_crossfade_added (boost::shared_ptr<Crossfade> x)
-{
- if (g_atomic_int_get(&block_notifications)) {
- _pending_xfade_adds.insert (_pending_xfade_adds.end(), x);
- } else {
- NewCrossfade (x); /* EMIT SIGNAL */
- }
-}
-
-void
-AudioPlaylist::crossfade_invalidated (boost::shared_ptr<Region> r)
-{
- Crossfades::iterator i;
- boost::shared_ptr<Crossfade> xfade = boost::dynamic_pointer_cast<Crossfade> (r);
-
- xfade->in()->resume_fade_in ();
- xfade->out()->resume_fade_out ();
-
- if ((i = find (_crossfades.begin(), _crossfades.end(), xfade)) != _crossfades.end()) {
- _crossfades.erase (i);
}
-}
-
-int
-AudioPlaylist::set_state (const XMLNode& node, int version)
-{
- XMLNode *child;
- XMLNodeList nlist;
- XMLNodeConstIterator niter;
-
- in_set_state++;
-
- if (Playlist::set_state (node, version)) {
- return -1;
- }
-
- freeze ();
-
- nlist = node.children();
-
- for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
-
- child = *niter;
-
- if (child->name() != "Crossfade") {
- continue;
- }
- try {
- boost::shared_ptr<Crossfade> xfade = boost::shared_ptr<Crossfade> (new Crossfade (*((const Playlist *)this), *child));
- _crossfades.push_back (xfade);
- xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
- xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
- NewCrossfade(xfade);
- }
-
- catch (failed_constructor& err) {
- // cout << string_compose (_("could not create crossfade object in playlist %1"),
- // _name)
- // << endl;
- continue;
+ for (RegionList::iterator i = starts->begin(); i != starts->end(); ++i) {
+ if (done_start.find (*i) == done_start.end()) {
+ boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (*i);
+ r->set_default_fade_in ();
}
}
- thaw ();
- in_set_state--;
-
- return 0;
-}
-
-void
-AudioPlaylist::clear (bool with_signals)
-{
- _crossfades.clear ();
- Playlist::clear (with_signals);
-}
-
-XMLNode&
-AudioPlaylist::state (bool full_state)
-{
- XMLNode& node = Playlist::state (full_state);
-
- if (full_state) {
- for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
- node.add_child_nocopy ((*i)->get_state());
+ for (RegionList::iterator i = ends->begin(); i != ends->end(); ++i) {
+ if (done_end.find (*i) == done_end.end()) {
+ boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (*i);
+ r->set_default_fade_out ();
}
}
-
- return node;
}
void
AudioPlaylist::dump () const
{
boost::shared_ptr<Region>r;
- boost::shared_ptr<Crossfade> x;
cerr << "Playlist \"" << _name << "\" " << endl
<< regions.size() << " regions "
- << _crossfades.size() << " crossfades"
<< endl;
for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
@@ -791,21 +361,6 @@ AudioPlaylist::dump () const
<< r->layer ()
<< endl;
}
-
- for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
- x = *i;
- cerr << " xfade ["
- << x->out()->name()
- << ','
- << x->in()->name()
- << " @ "
- << x->position()
- << " length = "
- << x->length ()
- << " active ? "
- << (x->active() ? "yes" : "no")
- << endl;
- }
}
bool
@@ -818,8 +373,6 @@ AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
}
bool changed = false;
- Crossfades::iterator c, ctmp;
- set<boost::shared_ptr<Crossfade> > unique_xfades;
{
RegionLock rlock (this);
@@ -853,18 +406,6 @@ AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
region->set_playlist (boost::shared_ptr<Playlist>());
}
- for (c = _crossfades.begin(); c != _crossfades.end(); ) {
- ctmp = c;
- ++ctmp;
-
- if ((*c)->involves (r)) {
- unique_xfades.insert (*c);
- _crossfades.erase (c);
- }
-
- c = ctmp;
- }
-
if (changed) {
/* overload this, it normally means "removed", not destroyed */
notify_region_removed (region);
@@ -873,22 +414,6 @@ AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
return changed;
}
-void
-AudioPlaylist::crossfade_changed (const PropertyChange&)
-{
- if (in_flush || in_set_state) {
- return;
- }
-
- /* XXX is there a loop here? can an xfade change not happen
- due to a playlist change? well, sure activation would
- be an example. maybe we should check the type of change
- that occured.
- */
-
- notify_contents_changed ();
-}
-
bool
AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
{
@@ -918,168 +443,6 @@ AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared
}
void
-AudioPlaylist::crossfades_at (framepos_t frame, Crossfades& clist)
-{
- RegionLock rlock (this);
-
- for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
- 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);
- }
- }
-}
-
-void
-AudioPlaylist::foreach_crossfade (boost::function<void (boost::shared_ptr<Crossfade>)> s)
-{
- RegionLock rl (this, false);
- for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
- s (*i);
- }
-}
-
-void
-AudioPlaylist::update (const CrossfadeListProperty::ChangeRecord& change)
-{
- for (CrossfadeListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
- add_crossfade (*i);
- }
-
- /* don't remove crossfades here; they will be dealt with by the dependency code */
-}
-
-boost::shared_ptr<Crossfade>
-AudioPlaylist::find_crossfade (const PBD::ID& id) const
-{
- Crossfades::const_iterator i = _crossfades.begin ();
- while (i != _crossfades.end() && (*i)->id() != id) {
- ++i;
- }
-
- if (i == _crossfades.end()) {
- return boost::shared_ptr<Crossfade> ();
- }
-
- return *i;
-}
-
-struct crossfade_triple {
- boost::shared_ptr<Region> old_in;
- boost::shared_ptr<Region> new_in;
- boost::shared_ptr<Region> new_out;
-};
-
-void
-AudioPlaylist::copy_dependents (const vector<TwoRegions>& old_and_new, Playlist* other) const
-{
- AudioPlaylist* other_audio = dynamic_cast<AudioPlaylist*>(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<boost::shared_ptr<Crossfade>,crossfade_triple> CrossfadeInfo;
- CrossfadeInfo crossfade_info;
-
- /* build up a record that links crossfades, old regions and new regions
- */
-
- for (vector<TwoRegions>::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<Crossfade> new_xfade (new Crossfade (ci->first,
- boost::dynamic_pointer_cast<AudioRegion>(ci->second.new_in),
- boost::dynamic_pointer_cast<AudioRegion>(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<boost::shared_ptr<Region> >& copies)
{
RegionSortByPosition cmp;
@@ -1195,12 +558,52 @@ AudioPlaylist::pre_uncombine (vector<boost::shared_ptr<Region> >& originals, boo
}
}
-void
-AudioPlaylist::get_equivalent_crossfades (boost::shared_ptr<Crossfade> c, vector<boost::shared_ptr<Crossfade> > & results)
+int
+AudioPlaylist::set_state (const XMLNode& node, int version)
{
- for (list<boost::shared_ptr<Crossfade> >::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
- if ((*i)->equivalent (c)) {
- results.push_back (*i);
+ int const r = Playlist::set_state (node, version);
+ if (r) {
+ return r;
+ }
+
+ /* Read legacy Crossfade nodes and set up region fades accordingly */
+
+ XMLNodeList children = node.children ();
+ for (XMLNodeConstIterator i = children.begin(); i != children.end(); ++i) {
+ if ((*i)->name() == X_("Crossfade")) {
+
+ XMLProperty* p = (*i)->property (X_("active"));
+ assert (p);
+ if (!string_is_affirmative (p->value())) {
+ continue;
+ }
+
+ p = (*i)->property (X_("in"));
+ assert (p);
+ boost::shared_ptr<Region> in = region_by_id (PBD::ID (p->value ()));
+ assert (in);
+ boost::shared_ptr<AudioRegion> in_a = boost::dynamic_pointer_cast<AudioRegion> (in);
+ assert (in_a);
+
+ p = (*i)->property (X_("out"));
+ assert (p);
+ boost::shared_ptr<Region> out = region_by_id (PBD::ID (p->value ()));
+ assert (out);
+ boost::shared_ptr<AudioRegion> out_a = boost::dynamic_pointer_cast<AudioRegion> (out);
+ assert (out_a);
+
+ XMLNodeList c = (*i)->children ();
+ for (XMLNodeConstIterator j = c.begin(); j != c.end(); ++j) {
+ if ((*j)->name() == X_("FadeIn")) {
+ in_a->fade_in()->set_state (**j, version);
+ in_a->set_fade_in_active (true);
+ } else if ((*j)->name() == X_("FadeOut")) {
+ out_a->fade_out()->set_state (**j, version);
+ out_a->set_fade_out_active (true);
+ }
+ }
}
}
+
+ return 0;
}
diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc
index 2c63f1d40c..617678d634 100644
--- a/libs/ardour/audioregion.cc
+++ b/libs/ardour/audioregion.cc
@@ -345,16 +345,17 @@ framecnt_t
AudioRegion::read (Sample* buf, framepos_t timeline_position, framecnt_t cnt, int channel) const
{
/* raw read, no fades, no gain, nada */
+ /* XXX: xfade: passes no mixbuf... */
return _read_at (_sources, _length, buf, 0, 0, _position + timeline_position, cnt, channel, ReadOps (0));
}
framecnt_t
AudioRegion::read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer,
- framepos_t file_position, framecnt_t cnt, uint32_t chan_n) const
+ framepos_t position, framecnt_t cnt, uint32_t chan_n) const
{
/* regular diskstream/butler read complete with fades etc */
return _read_at (_sources, _length, buf, mixdown_buffer, gain_buffer,
- file_position, cnt, chan_n, ReadOps (~0));
+ position, cnt, chan_n, ReadOps (~0));
}
framecnt_t
@@ -369,7 +370,9 @@ AudioRegion::master_read_at (Sample *buf, Sample *mixdown_buffer, float *gain_bu
buf, mixdown_buffer, gain_buffer, position, cnt, chan_n, ReadOps (0));
}
-/** @param position Position within the session */
+/** @param position Position within the session to read from.
+ * @param cnt Number of frames to read.
+ */
framecnt_t
AudioRegion::_read_at (const SourceList& srcs, framecnt_t limit,
Sample *buf, Sample *mixdown_buffer, float *gain_buffer,
@@ -378,44 +381,27 @@ AudioRegion::_read_at (const SourceList& srcs, framecnt_t limit,
uint32_t chan_n,
ReadOps rops) const
{
+ /* We are reading data from this region into buf (possibly via mixdown_buffer).
+ The caller has verified that we cover the desired section.
+ */
+
assert (cnt >= 0);
- frameoffset_t internal_offset;
- frameoffset_t buf_offset;
- framecnt_t to_read;
- bool raw = (rops == ReadOpsNone);
-
if (n_channels() == 0) {
return 0;
}
- if (muted() && !raw) {
+ if (muted() && rops != ReadOpsNone) {
return 0; /* read nothing */
}
- /* precondition: caller has verified that we cover the desired section */
+
+ /* WORK OUT WHERE TO GET DATA FROM */
- if (position < _position) {
- internal_offset = 0;
- buf_offset = _position - position;
- /* if this fails then the requested section is entirely
- before the position of this region. An error in xfade
- construction that was fixed in oct 2011 (rev 10259)
- led to this being the case. We don't want to crash
- when this error is encountered, so just settle
- on displaying an error.
- */
- if (cnt < buf_offset) {
- error << "trying to read region " << name() << " @ " << position << " which is outside region bounds "
- << _position << " .. " << last_frame() << " (len = " << length() << ')'
- << endmsg;
- return 0; // read nothing
- }
- cnt -= buf_offset;
- } else {
- internal_offset = position - _position;
- buf_offset = 0;
- }
+ framecnt_t to_read;
+
+ assert (position >= _position);
+ frameoffset_t const internal_offset = position - _position;
if (internal_offset >= limit) {
return 0; /* read nothing */
@@ -425,46 +411,26 @@ AudioRegion::_read_at (const SourceList& srcs, framecnt_t limit,
return 0; /* read nothing */
}
- if (opaque() || raw) {
- /* overwrite whatever is there */
- mixdown_buffer = buf + buf_offset;
- } else {
- mixdown_buffer += buf_offset;
- }
-
- if (chan_n < n_channels()) {
-
- boost::shared_ptr<AudioSource> src = boost::dynamic_pointer_cast<AudioSource> (srcs[chan_n]);
- if (src->read (mixdown_buffer, _start + internal_offset, to_read) != to_read) {
- return 0; /* "read nothing" */
- }
-
- } else {
- /* track is N-channel, this region has less channels; silence the ones
- we don't have.
- */
+ /* COMPUTE DETAILS OF ANY FADES INVOLVED IN THIS READ */
- if (Config->get_replicate_missing_region_channels()) {
- /* track is N-channel, this region has less channels, so use a relevant channel
- */
+ /* Amount of fade in that we are dealing with in this read */
+ framecnt_t fade_in_limit = 0;
- uint32_t channel = n_channels() % chan_n;
- boost::shared_ptr<AudioSource> src = boost::dynamic_pointer_cast<AudioSource> (srcs[channel]);
-
- if (src->read (mixdown_buffer, _start + internal_offset, to_read) != to_read) {
- return 0; /* "read nothing" */
- }
+ /* Offset from buf / mixdown_buffer of the start
+ of any fade out that we are dealing with
+ */
+ frameoffset_t fade_out_offset = 0;
+
+ /* Amount of fade in that we are dealing with in this read */
+ framecnt_t fade_out_limit = 0;
- } else {
- memset (mixdown_buffer, 0, sizeof (Sample) * cnt);
- }
- }
+ framecnt_t fade_interval_start = 0;
if (rops & ReadOpsFades) {
- /* fade in */
-
+ /* Fade in */
+
if (_fade_in_active && _session.config.get_use_region_fades()) {
framecnt_t fade_in_length = (framecnt_t) _fade_in->back()->when;
@@ -472,20 +438,11 @@ AudioRegion::_read_at (const SourceList& srcs, framecnt_t limit,
/* see if this read is within the fade in */
if (internal_offset < fade_in_length) {
-
- framecnt_t fi_limit;
-
- fi_limit = min (to_read, fade_in_length - internal_offset);
-
- _fade_in->curve().get_vector (internal_offset, internal_offset+fi_limit, gain_buffer, fi_limit);
-
- for (framecnt_t n = 0; n < fi_limit; ++n) {
- mixdown_buffer[n] *= gain_buffer[n];
- }
+ fade_in_limit = min (to_read, fade_in_length - internal_offset);
}
}
- /* fade out */
+ /* Fade out */
if (_fade_out_active && _session.config.get_use_region_fades()) {
@@ -508,28 +465,50 @@ AudioRegion::_read_at (const SourceList& srcs, framecnt_t limit,
*/
- framecnt_t fade_out_length = (framecnt_t) _fade_out->back()->when;
- framecnt_t fade_interval_start = max(internal_offset, limit-fade_out_length);
+ fade_interval_start = max (internal_offset, limit - framecnt_t (_fade_out->back()->when));
framecnt_t fade_interval_end = min(internal_offset + to_read, limit);
if (fade_interval_end > fade_interval_start) {
- /* (part of the) the fade out is in this buffer */
+ /* (part of the) the fade out is in this buffer */
+ fade_out_limit = fade_interval_end - fade_interval_start;
+ fade_out_offset = fade_interval_start - internal_offset;
+ }
+ }
+ }
- framecnt_t fo_limit = fade_interval_end - fade_interval_start;
- framecnt_t curve_offset = fade_interval_start - (limit-fade_out_length);
- framecnt_t fade_offset = fade_interval_start - internal_offset;
+ /* READ DATA FROM THE SOURCE INTO mixdown_buffer.
+ We can never read directly into buf, since it may contain data
+ from a transparent region `above' this one in the stack; we
+ must always mix.
+ */
- _fade_out->curve().get_vector (curve_offset, curve_offset+fo_limit, gain_buffer, fo_limit);
+ if (chan_n < n_channels()) {
- for (framecnt_t n = 0, m = fade_offset; n < fo_limit; ++n, ++m) {
- mixdown_buffer[m] *= gain_buffer[n];
- }
- }
+ boost::shared_ptr<AudioSource> src = boost::dynamic_pointer_cast<AudioSource> (srcs[chan_n]);
+ if (src->read (mixdown_buffer, _start + internal_offset, to_read) != to_read) {
+ return 0; /* "read nothing" */
+ }
+
+ } else {
+
+ /* track is N-channel, this region has fewer channels; silence the ones
+ we don't have.
+ */
+
+ if (Config->get_replicate_missing_region_channels()) {
+ /* track is N-channel, this region has less channels, so use a relevant channel
+ */
+
+ uint32_t channel = n_channels() % chan_n;
+ boost::shared_ptr<AudioSource> src = boost::dynamic_pointer_cast<AudioSource> (srcs[channel]);
+ if (src->read (mixdown_buffer, _start + internal_offset, to_read) != to_read) {
+ return 0; /* "read nothing" */
+ }
}
}
- /* Regular gain curves and scaling */
+ /* APPLY REGULAR GAIN CURVES AND SCALING TO mixdown_buffer */
if ((rops & ReadOpsOwnAutomation) && envelope_active()) {
_envelope->curve().get_vector (internal_offset, internal_offset + to_read, gain_buffer, to_read);
@@ -544,27 +523,34 @@ AudioRegion::_read_at (const SourceList& srcs, framecnt_t limit,
}
}
} else if ((rops & ReadOpsOwnScaling) && _scale_amplitude != 1.0f) {
-
- // XXX this should be using what in 2.0 would have been:
- // Session::apply_gain_to_buffer (mixdown_buffer, to_read, _scale_amplitude);
-
- for (framecnt_t n = 0; n < to_read; ++n) {
- mixdown_buffer[n] *= _scale_amplitude;
- }
+ apply_gain_to_buffer (mixdown_buffer, to_read, _scale_amplitude);
}
- if (!opaque() && (buf != mixdown_buffer)) {
- /* gack. the things we do for users.
- */
+ /* APPLY FADES TO THE DATA IN mixdown_buffer AND MIX THE RESULTS INTO buf */
- buf += buf_offset;
+ if (fade_in_limit != 0) {
+ _fade_in->curve().get_vector (internal_offset, internal_offset + fade_in_limit, gain_buffer, fade_in_limit);
- for (framecnt_t n = 0; n < to_read; ++n) {
- buf[n] += mixdown_buffer[n];
+ for (framecnt_t n = 0; n < fade_in_limit; ++n) {
+ buf[n] += mixdown_buffer[n] * gain_buffer[n];
}
}
+ if (fade_out_limit != 0) {
+ framecnt_t const curve_offset = fade_interval_start - (limit - _fade_out->back()->when);
+ _fade_out->curve().get_vector (curve_offset, curve_offset + fade_out_limit, gain_buffer, fade_out_limit);
+
+ for (framecnt_t n = 0, m = fade_out_offset; n < fade_out_limit; ++n, ++m) {
+ buf[m] += mixdown_buffer[m] * gain_buffer[n];
+ }
+ }
+
+
+ /* MIX THE REGION BODY FROM mixdown_buffer INTO buf */
+
+ mix_buffers_no_gain (buf + fade_in_limit, mixdown_buffer + fade_in_limit, to_read - fade_in_limit - fade_out_limit);
+
return to_read;
}
@@ -1519,7 +1505,11 @@ AudioRegion::find_silence (Sample threshold, framecnt_t min_length, InterThreadI
return silent_periods;
}
-
+Evoral::Range<framepos_t>
+AudioRegion::body_range () const
+{
+ return Evoral::Range<framepos_t> (first_frame() + _fade_in->back()->when, last_frame() - _fade_out->back()->when);
+}
extern "C" {
diff --git a/libs/ardour/butler.cc b/libs/ardour/butler.cc
index 72b9e5f586..3e9c7b0d96 100644
--- a/libs/ardour/butler.cc
+++ b/libs/ardour/butler.cc
@@ -24,7 +24,6 @@
#include "pbd/error.h"
#include "pbd/pthread_utils.h"
#include "ardour/butler.h"
-#include "ardour/crossfade.h"
#include "ardour/io.h"
#include "ardour/midi_diskstream.h"
#include "ardour/session.h"
@@ -86,8 +85,6 @@ Butler::start_thread()
MidiDiskstream::set_readahead_frames ((framecnt_t) (Config->get_midi_readahead() * rate));
- Crossfade::set_buffer_size (audio_dstream_playback_buffer_size);
-
should_run = false;
if (pipe (request_pipe)) {
diff --git a/libs/ardour/crossfade.cc b/libs/ardour/crossfade.cc
deleted file mode 100644
index d1db33bdcd..0000000000
--- a/libs/ardour/crossfade.cc
+++ /dev/null
@@ -1,1008 +0,0 @@
-/*
- Copyright (C) 2003-2006 Paul Davis
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include "ardour/debug.h"
-#include "ardour/types.h"
-#include "ardour/crossfade.h"
-#include "ardour/audioregion.h"
-#include "ardour/playlist.h"
-#include "ardour/utils.h"
-#include "ardour/session.h"
-#include "ardour/source.h"
-#include "ardour/region_factory.h"
-
-#include "i18n.h"
-#include <locale.h>
-
-using namespace std;
-using namespace ARDOUR;
-using namespace PBD;
-
-framecnt_t Crossfade::_short_xfade_length = 0;
-
-/* XXX if and when we ever implement parallel processing of the process()
- callback, these will need to be handled on a per-thread basis.
-*/
-
-Sample* Crossfade::crossfade_buffer_out = 0;
-Sample* Crossfade::crossfade_buffer_in = 0;
-
-
-#define CROSSFADE_DEFAULT_PROPERTIES \
- _active (Properties::active, _session.config.get_xfades_active ()) \
- , _follow_overlap (Properties::follow_overlap, false)
-
-
-namespace ARDOUR {
- namespace Properties {
- PropertyDescriptor<bool> follow_overlap;
- }
-}
-
-void
-Crossfade::make_property_quarks ()
-{
- Properties::follow_overlap.property_id = g_quark_from_static_string (X_("follow-overlap"));
- DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for follow-overlap = %1\n", Properties::follow_overlap.property_id));
-}
-
-void
-Crossfade::set_buffer_size (framecnt_t sz)
-{
- delete [] crossfade_buffer_out;
- crossfade_buffer_out = 0;
-
- delete [] crossfade_buffer_in;
- crossfade_buffer_in = 0;
-
- if (sz) {
- crossfade_buffer_out = new Sample[sz];
- crossfade_buffer_in = new Sample[sz];
- }
-}
-
-bool
-Crossfade::operator== (const Crossfade& other)
-{
- return (_in == other._in) && (_out == other._out);
-}
-
-Crossfade::Crossfade (boost::shared_ptr<AudioRegion> in, boost::shared_ptr<AudioRegion> out,
- framecnt_t length,
- AnchorPoint ap)
- : AudioRegion (in->session(), 0, length, in->name() + string ("<>") + out->name())
- , CROSSFADE_DEFAULT_PROPERTIES
- , _fade_in (Evoral::Parameter(FadeInAutomation)) // linear (gain coefficient) => -inf..+6dB
- , _fade_out (Evoral::Parameter(FadeOutAutomation)) // linear (gain coefficient) => -inf..+6dB
-
-{
- register_properties ();
-
- _in = in;
- _out = out;
- _anchor_point = ap;
- _fixed = true;
- _follow_overlap = false;
-
- initialize ();
-}
-
-Crossfade::Crossfade (boost::shared_ptr<AudioRegion> a, boost::shared_ptr<AudioRegion> b, CrossfadeModel model, bool act)
- : AudioRegion (a->session(), 0, 0, a->name() + string ("<>") + b->name())
- , CROSSFADE_DEFAULT_PROPERTIES
- , _fade_in (Evoral::Parameter(FadeInAutomation)) // linear (gain coefficient) => -inf..+6dB
- , _fade_out (Evoral::Parameter(FadeOutAutomation)) // linear (gain coefficient) => -inf..+6dB
-{
- register_properties ();
-
- _in_update = false;
- _fixed = false;
- _follow_overlap = false;
-
- if (compute (a, b, model)) {
- throw failed_constructor();
- }
-
- _active = act;
-
- initialize ();
-}
-
-Crossfade::Crossfade (const Playlist& playlist, XMLNode const & node)
- : AudioRegion (playlist.session(), 0, 0, "unnamed crossfade")
- , CROSSFADE_DEFAULT_PROPERTIES
- , _fade_in (Evoral::Parameter(FadeInAutomation)) // linear (gain coefficient) => -inf..+6dB
- , _fade_out (Evoral::Parameter(FadeOutAutomation)) // linear (gain coefficient) => -inf..+6dB
-
-{
- register_properties ();
-
- boost::shared_ptr<Region> r;
- XMLProperty const * prop;
- LocaleGuard lg (X_("POSIX"));
-
- /* we have to find the in/out regions before we can do anything else */
-
- if ((prop = node.property ("in")) == 0) {
- error << _("Crossfade: no \"in\" region in state") << endmsg;
- throw failed_constructor();
- }
-
- PBD::ID id (prop->value());
-
- r = playlist.find_region (id);
-
- if (!r) {
- /* the `in' region is not in a playlist, which probably means that this crossfade
- is in the undo record, so we have to find the region in the global region map.
- */
- r = RegionFactory::region_by_id (id);
- }
-
- if (!r) {
- error << string_compose (_("Crossfade: no \"in\" region %1 found in playlist %2 nor in region map"), id, playlist.name())
- << endmsg;
- throw failed_constructor();
- }
-
- if ((_in = boost::dynamic_pointer_cast<AudioRegion> (r)) == 0) {
- throw failed_constructor();
- }
-
- if ((prop = node.property ("out")) == 0) {
- error << _("Crossfade: no \"out\" region in state") << endmsg;
- throw failed_constructor();
- }
-
- PBD::ID id2 (prop->value());
-
- r = playlist.find_region (id2);
-
- if (!r) {
- r = RegionFactory::region_by_id (id2);
- }
-
- if (!r) {
- error << string_compose (_("Crossfade: no \"out\" region %1 found in playlist %2 nor in region map"), id2, playlist.name())
- << endmsg;
- throw failed_constructor();
- }
-
- if ((_out = boost::dynamic_pointer_cast<AudioRegion> (r)) == 0) {
- throw failed_constructor();
- }
-
- _length = 0;
- initialize();
- _active = true;
-
- if (set_state (node, Stateful::loading_state_version)) {
- throw failed_constructor();
- }
-}
-
-Crossfade::Crossfade (boost::shared_ptr<Crossfade> orig, boost::shared_ptr<AudioRegion> newin, boost::shared_ptr<AudioRegion> newout)
- : AudioRegion (boost::dynamic_pointer_cast<const AudioRegion> (orig), 0)
- , CROSSFADE_DEFAULT_PROPERTIES
- , _fade_in (orig->_fade_in)
- , _fade_out (orig->_fade_out)
-{
- register_properties ();
-
- _active = orig->_active;
- _in_update = orig->_in_update;
- _anchor_point = orig->_anchor_point;
- _follow_overlap = orig->_follow_overlap;
- _fixed = orig->_fixed;
- _position = orig->_position;
-
- _in = newin;
- _out = newout;
-
- // copied from Crossfade::initialize()
- _in_update = false;
-
- _out->suspend_fade_out ();
- _in->suspend_fade_in ();
-
- overlap_type = _in->coverage (_out->position(), _out->last_frame());
- layer_relation = (int32_t) (_in->layer() - _out->layer());
-
- // Let's make sure the fade isn't too long
- set_xfade_length(_length);
-}
-
-
-Crossfade::~Crossfade ()
-{
-}
-
-void
-Crossfade::register_properties ()
-{
- add_property (_active);
- add_property (_follow_overlap);
-}
-
-void
-Crossfade::initialize ()
-{
- /* merge source lists from regions */
-
- _sources = _in->sources();
- _sources.insert (_sources.end(), _out->sources().begin(), _out->sources().end());
-
- for (SourceList::iterator i = _sources.begin(); i != _sources.end(); ++i) {
- (*i)->inc_use_count ();
- }
-
- _master_sources = _in->master_sources();
- _master_sources.insert(_master_sources.end(), _out->master_sources().begin(), _out->master_sources().end());
-
- for (SourceList::iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
- (*i)->inc_use_count ();
- }
-
- _in_update = false;
-
- _out->suspend_fade_out ();
- _in->suspend_fade_in ();
-
- _fade_out.freeze ();
- _fade_out.clear ();
-
-#define EQUAL_POWER_MINUS_3DB
-#ifdef EQUAL_POWER_MINUS_3DB
-
- _fade_out.add ((_length * 0.000000), 1.000000);
- _fade_out.add ((_length * 0.166667), 0.948859);
- _fade_out.add ((_length * 0.333333), 0.851507);
- _fade_out.add ((_length * 0.500000), 0.707946);
- _fade_out.add ((_length * 0.666667), 0.518174);
- _fade_out.add ((_length * 0.833333), 0.282192);
- _fade_out.add ((_length * 1.000000), 0.000000);
-
-#else // EQUAL_POWER_MINUS_6DB
-
- _fade_out.add ((_length * 0.000000), 1.000000);
- _fade_out.add ((_length * 0.166667), 0.833033);
- _fade_out.add ((_length * 0.333333), 0.666186);
- _fade_out.add ((_length * 0.500000), 0.499459);
- _fade_out.add ((_length * 0.666667), 0.332853);
- _fade_out.add ((_length * 0.833333), 0.166366);
- _fade_out.add ((_length * 1.000000), 0.000000);
-#endif
-
- _fade_out.thaw ();
-
- _fade_in.freeze ();
- _fade_in.clear ();
-
-#define EQUAL_POWER_MINUS_3DB
-#ifdef EQUAL_POWER_MINUS_3DB
-
- _fade_in.add ((_length * 0.000000), 0.000000);
- _fade_in.add ((_length * 0.166667), 0.282192);
- _fade_in.add ((_length * 0.333333), 0.518174);
- _fade_in.add ((_length * 0.500000), 0.707946);
- _fade_in.add ((_length * 0.666667), 0.851507);
- _fade_in.add ((_length * 0.833333), 0.948859);
- _fade_in.add ((_length * 1.000000), 1.000000);
-
-#else // EQUAL_POWER_MINUS_SIX_DB
-
- _fade_in.add ((_length * 0.000000), 0.000000);
- _fade_in.add ((_length * 0.166667), 0.166366);
- _fade_in.add ((_length * 0.333333), 0.332853);
- _fade_in.add ((_length * 0.500000), 0.499459);
- _fade_in.add ((_length * 0.666667), 0.666186);
- _fade_in.add ((_length * 0.833333), 0.833033);
- _fade_in.add ((_length * 1.000000), 1.000000);
-
-#endif
-
- _fade_in.thaw ();
-
- overlap_type = _in->coverage (_out->position(), _out->last_frame());
- layer_relation = (int32_t) (_in->layer() - _out->layer());
-}
-
-framecnt_t
-Crossfade::read_raw_internal (Sample* buf, framepos_t start, framecnt_t cnt, int channel) const
-{
- Sample* mixdown = new Sample[cnt];
- float* gain = new float[cnt];
- framecnt_t ret;
-
- ret = read_at (buf, mixdown, gain, start, cnt, channel);
-
- delete [] mixdown;
- delete [] gain;
-
- return ret;
-}
-
-framecnt_t
-Crossfade::read_at (Sample *buf, Sample *mixdown_buffer,
- float *gain_buffer, framepos_t start, framecnt_t cnt, uint32_t chan_n) const
-{
- frameoffset_t offset;
- framecnt_t to_write;
-
- if (!_active) {
- return 0;
- }
-
- if (start < _position) {
-
- /* handle an initial section of the read area that we do not
- cover.
- */
-
- offset = _position - start;
-
- if (offset < cnt) {
- cnt -= offset;
- } else {
- return 0;
- }
-
- start = _position;
- buf += offset;
- to_write = min (_length.val(), cnt);
-
- } else {
-
- to_write = min ((_length - (start - _position)), cnt);
-
- }
-
- offset = start - _position;
-
- /* Prevent data from piling up inthe crossfade buffers when reading a transparent region */
- if (!(_out->opaque())) {
- memset (crossfade_buffer_out, 0, sizeof (Sample) * to_write);
- } else if (!(_in->opaque())) {
- memset (crossfade_buffer_in, 0, sizeof (Sample) * to_write);
- }
-
- _out->read_at (crossfade_buffer_out, mixdown_buffer, gain_buffer, start, to_write, chan_n);
- _in->read_at (crossfade_buffer_in, mixdown_buffer, gain_buffer, start, to_write, chan_n);
-
- float* fiv = new float[to_write];
- float* fov = new float[to_write];
-
- _fade_in.curve().get_vector (offset, offset+to_write, fiv, to_write);
- _fade_out.curve().get_vector (offset, offset+to_write, fov, to_write);
-
- /* note: although we have not explicitly taken into account the return values
- from _out->read_at() or _in->read_at(), the length() function does this
- implicitly. why? because it computes a value based on the in+out regions'
- position and length, and so we know precisely how much data they could return.
- */
-
- for (framecnt_t n = 0; n < to_write; ++n) {
- buf[n] = (crossfade_buffer_out[n] * fov[n]) + (crossfade_buffer_in[n] * fiv[n]);
- }
-
- delete [] fov;
- delete [] fiv;
-
- return to_write;
-}
-
-OverlapType
-Crossfade::coverage (framepos_t start, framepos_t end) const
-{
- framepos_t my_end = _position + _length;
-
- if ((start >= _position) && (end <= my_end)) {
- return OverlapInternal;
- }
- if ((end >= _position) && (end <= my_end)) {
- return OverlapStart;
- }
- if ((start >= _position) && (start <= my_end)) {
- return OverlapEnd;
- }
- if ((_position >= start) && (_position <= end) && (my_end <= end)) {
- return OverlapExternal;
- }
- return OverlapNone;
-}
-
-void
-Crossfade::set_active (bool yn)
-{
- if (_active != yn) {
- _active = yn;
- PropertyChanged (PropertyChange (Properties::active));
- }
-}
-
-bool
-Crossfade::refresh ()
-{
- /* crossfades must be between non-muted regions */
-
- if (_out->muted() || _in->muted()) {
- Invalidated (shared_from_this ());
- return false;
- }
-
- /* Top layer shouldn't be transparent */
-
- if (!((layer_relation > 0 ? _in : _out)->opaque())) {
- Invalidated (shared_from_this());
- return false;
- }
-
- /* regions must cannot be identically sized and placed */
-
- if (_in->position() == _out->position() && _in->length() == _out->length()) {
- Invalidated (shared_from_this());
- return false;
- }
-
- /* layer ordering cannot change */
-
- int32_t new_layer_relation = (int32_t) (_in->layer() - _out->layer());
-
- if (new_layer_relation * layer_relation < 0) { // different sign, layers rotated
- Invalidated (shared_from_this ());
- return false;
- }
-
- OverlapType ot = _in->coverage (_out->first_frame(), _out->last_frame());
-
- if (ot == OverlapNone) {
- Invalidated (shared_from_this ());
- return false;
- }
-
- bool send_signal;
-
- if (ot != overlap_type) {
-
- if (_follow_overlap) {
-
- try {
- compute (_in, _out, _session.config.get_xfade_model());
- }
-
- catch (NoCrossfadeHere& err) {
- Invalidated (shared_from_this ());
- return false;
- }
-
- send_signal = true;
-
- } else {
- Invalidated (shared_from_this ());
- return false;
- }
-
- } else {
-
- send_signal = update ();
- }
-
- if (send_signal) {
- PropertyChange bounds;
- bounds.add (Properties::start);
- bounds.add (Properties::position);
- bounds.add (Properties::length);
- PropertyChanged (bounds); /* EMIT SIGNAL */
- }
-
- _in_update = false;
-
- return true;
-}
-
-bool
-Crossfade::update ()
-{
- framecnt_t newlen;
-
- if (_follow_overlap) {
- newlen = _out->first_frame() + _out->length() - _in->first_frame();
- } else {
- newlen = _length;
- }
-
- if (newlen == 0) {
- Invalidated (shared_from_this ());
- return false;
- }
-
- _in_update = true;
-
- if ((_follow_overlap && newlen != _length) || (_length > newlen)) {
-
- double factor = newlen / (double) _length;
-
- _fade_out.x_scale (factor);
- _fade_in.x_scale (factor);
-
- _length = newlen;
- }
-
- switch (_anchor_point) {
- case StartOfIn:
- _position = _in->first_frame();
- break;
-
- case EndOfIn:
- _position = _in->last_frame() - _length;
- break;
-
- case EndOfOut:
- _position = _out->last_frame() - _length;
- }
-
- return true;
-}
-
-int
-Crossfade::compute (boost::shared_ptr<AudioRegion> a, boost::shared_ptr<AudioRegion> b, CrossfadeModel model)
-{
- boost::shared_ptr<AudioRegion> top;
- boost::shared_ptr<AudioRegion> bottom;
- framecnt_t short_xfade_length;
-
- short_xfade_length = _short_xfade_length;
-
- if (a->layer() < b->layer()) {
- top = b;
- bottom = a;
- } else {
- top = a;
- bottom = b;
- }
-
- /* first check for matching ends */
-
- if (top->first_frame() == bottom->first_frame()) {
-
- /* Both regions start at the same point */
-
- if (top->last_frame() < bottom->last_frame()) {
-
- /* top ends before bottom, so put an xfade
- in at the end of top.
- */
-
- /* [-------- top ---------- ]
- * {====== bottom =====================}
- */
-
- _in = bottom;
- _out = top;
-
- if (top->last_frame() < short_xfade_length) {
- _position = 0;
- } else {
- _position = top->last_frame() - short_xfade_length;
- }
-
- set_xfade_length (min (short_xfade_length, top->length()));
- _follow_overlap = false;
- _anchor_point = EndOfIn;
- _active = true;
- _fixed = true;
-
- } else {
- /* top ends after (or same time) as bottom - no xfade
- */
-
- /* [-------- top ------------------------ ]
- * {====== bottom =====================}
- */
-
- throw NoCrossfadeHere();
- }
-
- } else if (top->last_frame() == bottom->last_frame()) {
-
- /* Both regions end at the same point */
-
- if (top->first_frame() > bottom->first_frame()) {
-
- /* top starts after bottom, put an xfade in at the
- start of top
- */
-
- /* [-------- top ---------- ]
- * {====== bottom =====================}
- */
-
- _in = top;
- _out = bottom;
- _position = top->first_frame();
- set_xfade_length (min (short_xfade_length, top->length()));
- _follow_overlap = false;
- _anchor_point = StartOfIn;
- _active = true;
- _fixed = true;
-
- } else {
- /* top starts before bottom - no xfade
- */
-
- /* [-------- top ------------------------ ]
- * {====== bottom =====================}
- */
-
- throw NoCrossfadeHere();
- }
-
- } else {
-
- /* OK, time to do more regular overlapping */
-
- OverlapType ot = top->coverage (bottom->first_frame(), bottom->last_frame());
-
- switch (ot) {
- case OverlapNone:
- /* should be NOTREACHED as a precondition of creating
- a new crossfade, but we need to handle it here.
- */
- throw NoCrossfadeHere();
- break;
-
- case OverlapInternal:
- case OverlapExternal:
- /* should be NOTREACHED because of tests above */
- throw NoCrossfadeHere();
- break;
-
- case OverlapEnd: /* top covers start of bottom but ends within it */
-
- /* [---- top ------------------------]
- * { ==== bottom ============ }
- */
-
- _in = bottom;
- _out = top;
- _anchor_point = EndOfOut;
-
- if (model == FullCrossfade) {
- _position = bottom->first_frame(); // "{"
- set_xfade_length (_out->first_frame() + _out->length() - _in->first_frame());
- /* leave active alone */
- _follow_overlap = true;
- } else {
- set_xfade_length (min (short_xfade_length, top->length()));
- _position = top->last_frame() - _length; // "]" - length
- _active = true;
- _follow_overlap = false;
-
- }
- break;
-
- case OverlapStart: /* top starts within bottom but covers bottom's end */
-
- /* { ==== top ============ }
- * [---- bottom -------------------]
- */
-
- _in = top;
- _out = bottom;
- _position = top->first_frame();
- _anchor_point = StartOfIn;
-
- if (model == FullCrossfade) {
- set_xfade_length (_out->first_frame() + _out->length() - _in->first_frame());
- /* leave active alone */
- _follow_overlap = true;
- } else {
- set_xfade_length (min (short_xfade_length, top->length()));
- _active = true;
- _follow_overlap = false;
-
- }
-
- break;
- }
- }
-
- return 0;
-}
-
-XMLNode&
-Crossfade::get_state ()
-{
- XMLNode* node = new XMLNode (X_("Crossfade"));
- XMLNode* child;
- char buf[64];
- LocaleGuard lg (X_("POSIX"));
-
- id().print (buf, sizeof (buf));
- node->add_property ("id", buf);
- _out->id().print (buf, sizeof (buf));
- node->add_property ("out", buf);
- _in->id().print (buf, sizeof (buf));
- node->add_property ("in", buf);
- node->add_property ("active", (_active ? "yes" : "no"));
- node->add_property ("follow-overlap", (_follow_overlap ? "yes" : "no"));
- node->add_property ("fixed", (_fixed ? "yes" : "no"));
- snprintf (buf, sizeof(buf), "%" PRId64, _length.val());
- node->add_property ("length", buf);
- snprintf (buf, sizeof(buf), "%" PRIu32, (uint32_t) _anchor_point);
- node->add_property ("anchor-point", buf);
- snprintf (buf, sizeof(buf), "%" PRId64, _position.val());
- node->add_property ("position", buf);
-
- child = node->add_child ("FadeIn");
-
- for (AutomationList::iterator ii = _fade_in.begin(); ii != _fade_in.end(); ++ii) {
- XMLNode* pnode;
-
- pnode = new XMLNode ("point");
-
- snprintf (buf, sizeof (buf), "%" PRId64, (framepos_t) floor ((*ii)->when));
- pnode->add_property ("x", buf);
- snprintf (buf, sizeof (buf), "%.12g", (*ii)->value);
- pnode->add_property ("y", buf);
- child->add_child_nocopy (*pnode);
- }
-
- child = node->add_child ("FadeOut");
-
- for (AutomationList::iterator ii = _fade_out.begin(); ii != _fade_out.end(); ++ii) {
- XMLNode* pnode;
-
- pnode = new XMLNode ("point");
-
- snprintf (buf, sizeof (buf), "%" PRId64, (framepos_t) floor ((*ii)->when));
- pnode->add_property ("x", buf);
- snprintf (buf, sizeof (buf), "%.12g", (*ii)->value);
- pnode->add_property ("y", buf);
- child->add_child_nocopy (*pnode);
- }
-
- return *node;
-}
-
-int
-Crossfade::set_state (const XMLNode& node, int /*version*/)
-{
- XMLNodeConstIterator i;
- XMLNodeList children;
- XMLNode* fi;
- XMLNode* fo;
- const XMLProperty* prop;
- LocaleGuard lg (X_("POSIX"));
- PropertyChange what_changed;
- framepos_t val;
-
- set_id (node);
-
- if ((prop = node.property ("position")) != 0) {
- sscanf (prop->value().c_str(), "%" PRId64, &val);
- if (val != _position) {
- _position = val;
- what_changed.add (Properties::position);
- }
- } else {
- warning << _("old-style crossfade information - no position information") << endmsg;
- _position = _in->first_frame();
- }
-
- if ((prop = node.property ("active")) != 0) {
- bool x = string_is_affirmative (prop->value());
- if (x != _active) {
- _active = x;
- what_changed.add (Properties::active);
- }
- } else {
- _active = true;
- }
-
- if ((prop = node.property ("follow-overlap")) != 0) {
- _follow_overlap = string_is_affirmative (prop->value());
- } else {
- _follow_overlap = false;
- }
-
- if ((prop = node.property ("fixed")) != 0) {
- _fixed = string_is_affirmative (prop->value());
- } else {
- _fixed = false;
- }
-
- if ((prop = node.property ("anchor-point")) != 0) {
- _anchor_point = AnchorPoint (atoi ((prop->value().c_str())));
- } else {
- _anchor_point = StartOfIn;
- }
-
- if ((prop = node.property ("length")) != 0) {
-
- sscanf (prop->value().c_str(), "%" PRId64, &val);
- if (val != _length) {
- _length = val;
- what_changed.add (Properties::length);
- }
-
- } else {
-
- /* XXX this branch is legacy code from before
- the point where we stored xfade lengths.
- */
-
- if ((_length = overlap_length()) == 0) {
- throw failed_constructor();
- }
- }
-
- if ((fi = find_named_node (node, "FadeIn")) == 0) {
- return -1;
- }
-
- if ((fo = find_named_node (node, "FadeOut")) == 0) {
- return -1;
- }
-
- /* fade in */
-
- _fade_in.freeze ();
- _fade_in.clear ();
-
- children = fi->children();
-
- for (i = children.begin(); i != children.end(); ++i) {
- if ((*i)->name() == "point") {
- framepos_t x;
- float y;
-
- prop = (*i)->property ("x");
- sscanf (prop->value().c_str(), "%" PRId64, &x);
-
- prop = (*i)->property ("y");
- sscanf (prop->value().c_str(), "%f", &y);
-
- _fade_in.add (x, y);
- }
- }
-
- if (_fade_in.size() < 2) {
- /* fade state somehow saved with no points */
- return -1;
- }
-
- _fade_in.front()->value = 0.0;
- _fade_in.back()->value = 1.0;
-
- _fade_in.thaw ();
-
- /* fade out */
-
- _fade_out.freeze ();
- _fade_out.clear ();
-
- children = fo->children();
-
- for (i = children.begin(); i != children.end(); ++i) {
- if ((*i)->name() == "point") {
- framepos_t x;
- float y;
- XMLProperty* prop;
-
- prop = (*i)->property ("x");
- sscanf (prop->value().c_str(), "%" PRId64, &x);
-
- prop = (*i)->property ("y");
- sscanf (prop->value().c_str(), "%f", &y);
-
- _fade_out.add (x, y);
- }
- }
-
- if (_fade_out.size() < 2) {
- /* fade state somehow saved with no points */
- return -1;
- }
-
- _fade_out.front()->value = 1.0;
- _fade_out.back()->value = 0.0;
-
- _fade_out.thaw ();
-
- PropertyChanged (what_changed); /* EMIT SIGNAL */
- FadesChanged (); /* EMIT SIGNAL */
-
- return 0;
-}
-
-bool
-Crossfade::can_follow_overlap () const
-{
- return !_fixed;
-}
-
-void
-Crossfade::set_follow_overlap (bool yn)
-{
- if (yn == _follow_overlap || _fixed) {
- return;
- }
-
- _follow_overlap = yn;
-
- if (!yn) {
- set_xfade_length (_short_xfade_length);
- } else {
- set_xfade_length (_out->first_frame() + _out->length() - _in->first_frame());
- }
-
- PropertyChanged (PropertyChange (Properties::follow_overlap));
-}
-
-framecnt_t
-Crossfade::set_xfade_length (framecnt_t len)
-{
- framecnt_t limit = 0;
-
- switch (_anchor_point) {
- case StartOfIn:
- limit = _in->length();
- break;
-
- case EndOfIn:
- limit = _in->length();
- break;
-
- case EndOfOut:
- limit = _out->length();
- break;
-
- }
-
- len = min (limit, len);
-
- double factor = len / (double) _length;
-
- _in_update = true;
- _fade_out.x_scale (factor);
- _fade_in.x_scale (factor);
- _in_update = false;
-
- _length = len;
-
- PropertyChanged (PropertyChange (Properties::length));
-
- return len;
-}
-
-framecnt_t
-Crossfade::overlap_length () const
-{
- if (_fixed) {
- return _length;
- }
- return _out->first_frame() + _out->length() - _in->first_frame();
-}
-
-void
-Crossfade::set_short_xfade_length (framecnt_t n)
-{
- _short_xfade_length = n;
-}
diff --git a/libs/ardour/crossfade_binder.cc b/libs/ardour/crossfade_binder.cc
deleted file mode 100644
index 34e7adab4b..0000000000
--- a/libs/ardour/crossfade_binder.cc
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- Copyright (C) 2011 Paul Davis
- Author: Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include "ardour/crossfade_binder.h"
-#include "ardour/session_playlists.h"
-#include "ardour/crossfade.h"
-
-using namespace ARDOUR;
-
-CrossfadeBinder::CrossfadeBinder (boost::shared_ptr<SessionPlaylists> playlists, PBD::ID id)
- : _playlists (playlists)
- , _id (id)
-{
-
-}
-
-
-CrossfadeBinder::CrossfadeBinder (XMLNode* node, boost::shared_ptr<SessionPlaylists> playlists)
- : _playlists (playlists)
-{
- XMLProperty* id = node->property ("crossfade-id");
- assert (id);
-
- _id = PBD::ID (id->value ());
-}
-
-ARDOUR::Crossfade *
-CrossfadeBinder::get () const
-{
- ARDOUR::Crossfade* c = _playlists->find_crossfade (_id).get ();
- assert (c);
- return c;
-}
-
-std::string
-CrossfadeBinder::type_name () const
-{
- return "ARDOUR::Crossfade";
-}
-
-void
-CrossfadeBinder::add_state (XMLNode* node)
-{
- node->add_property ("crossfade-id", _id.to_s ());
-}
-
diff --git a/libs/ardour/diskstream.cc b/libs/ardour/diskstream.cc
index 1cf4048c69..6e23eb81aa 100644
--- a/libs/ardour/diskstream.cc
+++ b/libs/ardour/diskstream.cc
@@ -685,15 +685,15 @@ Diskstream::route_going_away ()
}
void
-Diskstream::calculate_record_range(OverlapType ot, framepos_t transport_frame, framecnt_t nframes,
- framecnt_t & rec_nframes, framecnt_t & rec_offset)
+Diskstream::calculate_record_range (Evoral::OverlapType ot, framepos_t transport_frame, framecnt_t nframes,
+ framecnt_t & rec_nframes, framecnt_t & rec_offset)
{
switch (ot) {
- case OverlapNone:
+ case Evoral::OverlapNone:
rec_nframes = 0;
break;
- case OverlapInternal:
+ case Evoral::OverlapInternal:
/* ---------- recrange
|---| transrange
*/
@@ -701,7 +701,7 @@ Diskstream::calculate_record_range(OverlapType ot, framepos_t transport_frame, f
rec_offset = 0;
break;
- case OverlapStart:
+ case Evoral::OverlapStart:
/* |--------| recrange
-----| transrange
*/
@@ -711,7 +711,7 @@ Diskstream::calculate_record_range(OverlapType ot, framepos_t transport_frame, f
}
break;
- case OverlapEnd:
+ case Evoral::OverlapEnd:
/* |--------| recrange
|-------- transrange
*/
@@ -719,7 +719,7 @@ Diskstream::calculate_record_range(OverlapType ot, framepos_t transport_frame, f
rec_offset = 0;
break;
- case OverlapExternal:
+ case Evoral::OverlapExternal:
/* |--------| recrange
-------------- transrange
*/
diff --git a/libs/ardour/enums.cc b/libs/ardour/enums.cc
index d1eca7401c..4559ed457d 100644
--- a/libs/ardour/enums.cc
+++ b/libs/ardour/enums.cc
@@ -53,7 +53,6 @@ setup_enum_writer ()
vector<int> i;
vector<string> s;
- OverlapType _OverlapType;
AlignStyle _AlignStyle;
AlignChoice _AlignChoice;
MeterPoint _MeterPoint;
@@ -132,13 +131,6 @@ setup_enum_writer ()
#define REGISTER_ENUM(e) i.push_back (e); s.push_back (#e)
#define REGISTER_CLASS_ENUM(t,e) i.push_back (t::e); s.push_back (#e)
- REGISTER_ENUM (OverlapNone);
- REGISTER_ENUM (OverlapInternal);
- REGISTER_ENUM (OverlapStart);
- REGISTER_ENUM (OverlapEnd);
- REGISTER_ENUM (OverlapExternal);
- REGISTER (_OverlapType);
-
REGISTER_ENUM (GainAutomation);
REGISTER_ENUM (PanAzimuthAutomation);
REGISTER_ENUM (PanElevationAutomation);
diff --git a/libs/ardour/globals.cc b/libs/ardour/globals.cc
index f490a6526e..96d93ea5bc 100644
--- a/libs/ardour/globals.cc
+++ b/libs/ardour/globals.cc
@@ -493,82 +493,6 @@ ARDOUR::setup_fpu ()
#endif
}
-ARDOUR::OverlapType
-ARDOUR::coverage (framepos_t sa, framepos_t ea,
- framepos_t sb, framepos_t eb)
-{
- /* OverlapType returned reflects how the second (B)
- range overlaps the first (A).
-
- The diagrams show various relative placements
- of A and B for each OverlapType.
-
- Notes:
- Internal: the start points cannot coincide
- External: the start and end points can coincide
- Start: end points can coincide
- End: start points can coincide
-
- XXX Logically, Internal should disallow end
- point equality.
- */
-
- /*
- |--------------------| A
- |------| B
- |-----------------| B
-
-
- "B is internal to A"
-
- */
-
- if ((sb > sa) && (eb <= ea)) {
- return OverlapInternal;
- }
-
- /*
- |--------------------| A
- ----| B
- -----------------------| B
- --| B
-
- "B overlaps the start of A"
-
- */
-
- if ((eb >= sa) && (eb <= ea)) {
- return OverlapStart;
- }
- /*
- |---------------------| A
- |----------------- B
- |----------------------- B
- |- B
-
- "B overlaps the end of A"
-
- */
- if ((sb > sa) && (sb <= ea)) {
- return OverlapEnd;
- }
- /*
- |--------------------| A
- -------------------------- B
- |----------------------- B
- ----------------------| B
- |--------------------| B
-
-
- "B overlaps all of A"
- */
- if ((sa >= sb) && (sa <= eb) && (ea <= eb)) {
- return OverlapExternal;
- }
-
- return OverlapNone;
-}
-
string
ARDOUR::translation_kill_path ()
{
diff --git a/libs/ardour/graph.cc b/libs/ardour/graph.cc
index c38106506e..556748e2de 100644
--- a/libs/ardour/graph.cc
+++ b/libs/ardour/graph.cc
@@ -19,6 +19,7 @@
*/
#include <stdio.h>
#include <cmath>
+#include <xmmintrin.h>
#include "pbd/compose.h"
#include "pbd/debug_rt_alloc.h"
diff --git a/libs/ardour/midi_diskstream.cc b/libs/ardour/midi_diskstream.cc
index e62ec6837e..9b3b619d1c 100644
--- a/libs/ardour/midi_diskstream.cc
+++ b/libs/ardour/midi_diskstream.cc
@@ -339,7 +339,7 @@ MidiDiskstream::process (framepos_t transport_frame, pframes_t nframes, framecnt
adjust_capture_position = 0;
if (nominally_recording || (re && was_recording && _session.get_record_enabled() && _session.config.get_punch_in())) {
- OverlapType ot = coverage (first_recordable_frame, last_recordable_frame, transport_frame, transport_frame + nframes);
+ Evoral::OverlapType ot = Evoral::coverage (first_recordable_frame, last_recordable_frame, transport_frame, transport_frame + nframes);
calculate_record_range(ot, transport_frame, nframes, rec_nframes, rec_offset);
diff --git a/libs/ardour/midi_playlist.cc b/libs/ardour/midi_playlist.cc
index 84bcc70d0b..9b0eb374d5 100644
--- a/libs/ardour/midi_playlist.cc
+++ b/libs/ardour/midi_playlist.cc
@@ -128,13 +128,13 @@ MidiPlaylist::read (Evoral::EventSink<framepos_t>& dst, framepos_t start, framec
*/
switch ((*i)->coverage (start, end)) {
- case OverlapStart:
- case OverlapInternal:
- case OverlapExternal:
+ case Evoral::OverlapStart:
+ case Evoral::OverlapInternal:
+ case Evoral::OverlapExternal:
regs.push_back (*i);
break;
- case OverlapEnd:
+ case Evoral::OverlapEnd:
/* this region ends within the read range */
regs.push_back (*i);
ended.push_back (*i);
@@ -316,26 +316,6 @@ MidiPlaylist::remove_dependents (boost::shared_ptr<Region> region)
}
}
-
-void
-MidiPlaylist::refresh_dependents (boost::shared_ptr<Region> /*r*/)
-{
- /* MIDI regions have no dependents (crossfades) */
-}
-
-void
-MidiPlaylist::finalize_split_region (boost::shared_ptr<Region> /*original*/, boost::shared_ptr<Region> /*left*/, boost::shared_ptr<Region> /*right*/)
-{
- /* No MIDI crossfading (yet?), so nothing to do here */
-}
-
-void
-MidiPlaylist::check_dependents (boost::shared_ptr<Region> /*r*/, bool /*norefresh*/)
-{
- /* MIDI regions have no dependents (crossfades) */
-}
-
-
int
MidiPlaylist::set_state (const XMLNode& node, int version)
{
diff --git a/libs/ardour/playlist.cc b/libs/ardour/playlist.cc
index f7267e4545..b146f942bd 100644
--- a/libs/ardour/playlist.cc
+++ b/libs/ardour/playlist.cc
@@ -210,35 +210,35 @@ Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, f
framepos_t position = 0;
framecnt_t len = 0;
string new_name;
- OverlapType overlap;
+ Evoral::OverlapType overlap;
region = *i;
overlap = region->coverage (start, end);
switch (overlap) {
- case OverlapNone:
+ case Evoral::OverlapNone:
continue;
- case OverlapInternal:
+ case Evoral::OverlapInternal:
offset = start - region->position();
position = 0;
len = cnt;
break;
- case OverlapStart:
+ case Evoral::OverlapStart:
offset = 0;
position = region->position() - start;
len = end - region->position();
break;
- case OverlapEnd:
+ case Evoral::OverlapEnd:
offset = start - region->position();
position = 0;
len = region->length() - offset;
break;
- case OverlapExternal:
+ case Evoral::OverlapExternal:
offset = 0;
position = region->position() - start;
len = region->length();
@@ -561,7 +561,6 @@ Playlist::notify_region_added (boost::shared_ptr<Region> r)
void
Playlist::flush_notifications (bool from_undo)
{
- set<boost::shared_ptr<Region> > dependent_checks_needed;
set<boost::shared_ptr<Region> >::iterator s;
bool regions_changed = false;
@@ -575,6 +574,10 @@ Playlist::flush_notifications (bool from_undo)
regions_changed = true;
}
+ /* XXX: it'd be nice if we could use pending_bounds for
+ RegionsExtended and RegionsMoved.
+ */
+
/* we have no idea what order the regions ended up in pending
bounds (it could be based on selection order, for example).
so, to preserve layering in the "most recently moved is higher"
@@ -584,24 +587,26 @@ Playlist::flush_notifications (bool from_undo)
// RegionSortByLayer cmp;
// pending_bounds.sort (cmp);
+ list<Evoral::Range<framepos_t> > crossfade_ranges;
+
for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
- dependent_checks_needed.insert (*r);
+ crossfade_ranges.push_back ((*r)->last_range ());
+ crossfade_ranges.push_back ((*r)->range ());
}
for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
+ crossfade_ranges.push_back ((*s)->range ());
remove_dependents (*s);
- // cerr << _name << " sends RegionRemoved\n";
RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
}
-
+
for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
- // cerr << _name << " sends RegionAdded\n";
- /* don't emit RegionAdded signal until relayering is done,
- so that the region is fully setup by the time
- anyone hear's that its been added
- */
- dependent_checks_needed.insert (*s);
- }
+ crossfade_ranges.push_back ((*s)->range ());
+ /* don't emit RegionAdded signal until relayering is done,
+ so that the region is fully setup by the time
+ anyone hears that its been added
+ */
+ }
if (
((regions_changed || pending_contents_change) && !in_set_state) ||
@@ -621,11 +626,12 @@ Playlist::flush_notifications (bool from_undo)
RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
}
- for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
- check_dependents (*s, false);
- }
+ coalesce_and_check_crossfades (crossfade_ranges);
if (!pending_range_moves.empty ()) {
+ /* We don't need to check crossfades for these as pending_bounds has
+ already covered it.
+ */
RangesMoved (pending_range_moves, from_undo);
}
@@ -754,7 +760,7 @@ Playlist::flush_notifications (bool from_undo)
notify_region_added (region);
if (!holding_state ()) {
- check_dependents (region, false);
+ check_crossfades (region->range ());
}
region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
@@ -879,7 +885,7 @@ Playlist::flush_notifications (bool from_undo)
boost::shared_ptr<Region> current;
string new_name;
RegionList::iterator tmp;
- OverlapType overlap;
+ Evoral::OverlapType overlap;
framepos_t pos1, pos2, pos3, pos4;
in_partition = true;
@@ -915,7 +921,7 @@ Playlist::flush_notifications (bool from_undo)
continue;
}
- if ((overlap = current->coverage (start, end)) == OverlapNone) {
+ if ((overlap = current->coverage (start, end)) == Evoral::OverlapNone) {
continue;
}
@@ -924,7 +930,7 @@ Playlist::flush_notifications (bool from_undo)
pos3 = end;
pos4 = current->last_frame();
- if (overlap == OverlapInternal) {
+ if (overlap == Evoral::OverlapInternal) {
/* split: we need 3 new regions, the front, middle and end.
cut: we need 2 regions, the front and end.
*/
@@ -986,7 +992,7 @@ Playlist::flush_notifications (bool from_undo)
thawlist.push_back (current);
current->cut_end (pos2 - 1);
- } else if (overlap == OverlapEnd) {
+ } else if (overlap == Evoral::OverlapEnd) {
/*
start end
@@ -1026,7 +1032,7 @@ Playlist::flush_notifications (bool from_undo)
thawlist.push_back (current);
current->cut_end (pos2 - 1);
- } else if (overlap == OverlapStart) {
+ } else if (overlap == Evoral::OverlapStart) {
/* split: we need 2 regions: the front and the end.
cut: just trim current to skip the cut area
@@ -1069,7 +1075,7 @@ Playlist::flush_notifications (bool from_undo)
current->suspend_property_changes ();
thawlist.push_back (current);
current->trim_front (pos3);
- } else if (overlap == OverlapExternal) {
+ } else if (overlap == Evoral::OverlapExternal) {
/* split: no split required.
cut: remove the region.
@@ -1098,9 +1104,7 @@ Playlist::flush_notifications (bool from_undo)
in_partition = false;
}
- for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
- check_dependents (*i, false);
- }
+ check_crossfades (Evoral::Range<framepos_t> (start, end));
}
boost::shared_ptr<Playlist>
@@ -1381,9 +1385,6 @@ Playlist::flush_notifications (bool from_undo)
add_region_internal (left, region->position());
add_region_internal (right, region->position() + before);
-
- finalize_split_region (region, left, right);
-
remove_region_internal (region);
_splicing = old_sp;
@@ -1508,7 +1509,10 @@ Playlist::flush_notifications (bool from_undo)
} else {
notify_contents_changed ();
relayer ();
- check_dependents (region, false);
+ list<Evoral::Range<framepos_t> > xf;
+ xf.push_back (Evoral::Range<framepos_t> (region->last_range()));
+ xf.push_back (Evoral::Range<framepos_t> (region->range()));
+ coalesce_and_check_crossfades (xf);
}
}
}
@@ -1556,7 +1560,7 @@ Playlist::flush_notifications (bool from_undo)
}
if (what_changed.contains (our_interests) && !what_changed.contains (pos_and_length)) {
- check_dependents (region, false);
+ check_crossfades (region->range ());
}
if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
@@ -1704,156 +1708,55 @@ Playlist::regions_at (framepos_t frame)
}
boost::shared_ptr<RegionList>
-Playlist::regions_to_read (framepos_t start, framepos_t end)
+Playlist::find_regions_at (framepos_t frame)
{
- /* Caller must hold lock */
-
- RegionList covering;
- set<framepos_t> to_check;
- set<boost::shared_ptr<Region> > unique;
-
- to_check.insert (start);
- to_check.insert (end);
-
- DEBUG_TRACE (DEBUG::AudioPlayback, ">>>>> REGIONS TO READ\n");
-
- for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
-
- /* find all/any regions that span start+end */
-
- switch ((*i)->coverage (start, end)) {
- case OverlapNone:
- break;
-
- case OverlapInternal:
- covering.push_back (*i);
- DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OInternal)\n", (*i)->name()));
- break;
-
- case OverlapStart:
- to_check.insert ((*i)->position());
- if ((*i)->position() != 0) {
- to_check.insert ((*i)->position()-1);
- }
- DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
- covering.push_back (*i);
- break;
-
- case OverlapEnd:
- to_check.insert ((*i)->last_frame());
- to_check.insert ((*i)->last_frame()+1);
- DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OEnd)\n", (*i)->name()));
- DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
- DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
- covering.push_back (*i);
- break;
-
- case OverlapExternal:
- covering.push_back (*i);
- to_check.insert ((*i)->position());
- if ((*i)->position() != 0) {
- to_check.insert ((*i)->position()-1);
- }
- to_check.insert ((*i)->last_frame());
- to_check.insert ((*i)->last_frame()+1);
- DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OExt)\n", (*i)->name()));
- DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
- DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
- break;
- }
-
- /* don't go too far */
-
- if ((*i)->position() > end) {
- break;
- }
- }
-
- boost::shared_ptr<RegionList> rlist (new RegionList);
-
- /* find all the regions that cover each position .... */
-
- if (covering.size() == 1) {
-
- rlist->push_back (covering.front());
- DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Just one covering region (%1)\n", covering.front()->name()));
-
- } else {
-
- RegionList here;
- for (set<framepos_t>::iterator t = to_check.begin(); t != to_check.end(); ++t) {
-
- here.clear ();
-
- DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("++++ Considering %1\n", *t));
-
- for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) {
-
- if ((*x)->covers (*t)) {
- here.push_back (*x);
- DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 covers %2\n",
- (*x)->name(),
- (*t)));
- } else {
- DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 does NOT covers %2\n",
- (*x)->name(),
- (*t)));
- }
-
- }
-
- RegionSortByLayer cmp;
- here.sort (cmp);
-
- /* ... and get the top/transparent regions at "here" */
-
- for (RegionList::reverse_iterator c = here.rbegin(); c != here.rend(); ++c) {
-
- unique.insert (*c);
-
- if ((*c)->opaque()) {
-
- /* the other regions at this position are hidden by this one */
- DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 is opaque, ignore all others\n",
- (*c)->name()));
- break;
- }
- }
- }
-
- for (set<boost::shared_ptr<Region> >::iterator s = unique.begin(); s != unique.end(); ++s) {
- rlist->push_back (*s);
- }
-
- if (rlist->size() > 1) {
- /* now sort by time order */
+ /* Caller must hold lock */
+
+ boost::shared_ptr<RegionList> rlist (new RegionList);
+
+ for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+ if ((*i)->covers (frame)) {
+ rlist->push_back (*i);
+ }
+ }
+
+ return rlist;
+}
- RegionSortByPosition cmp;
- rlist->sort (cmp);
- }
- }
+boost::shared_ptr<RegionList>
+Playlist::regions_with_start_within (Evoral::Range<framepos_t> range)
+{
+ RegionLock rlock (this);
+ boost::shared_ptr<RegionList> rlist (new RegionList);
- DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("<<<<< REGIONS TO READ returns %1\n", rlist->size()));
+ for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+ if ((*i)->first_frame() >= range.from && (*i)->first_frame() <= range.to) {
+ rlist->push_back (*i);
+ }
+ }
- return rlist;
- }
+ return rlist;
+}
boost::shared_ptr<RegionList>
-Playlist::find_regions_at (framepos_t frame)
+Playlist::regions_with_end_within (Evoral::Range<framepos_t> range)
{
- /* Caller must hold lock */
-
+ RegionLock rlock (this);
boost::shared_ptr<RegionList> rlist (new RegionList);
for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
- if ((*i)->covers (frame)) {
+ if ((*i)->last_frame() >= range.from && (*i)->last_frame() <= range.to) {
rlist->push_back (*i);
}
}
-
+
return rlist;
}
+/** @param start Range start.
+ * @param end Range end.
+ * @return regions which have some part within this range.
+ */
boost::shared_ptr<RegionList>
Playlist::regions_touched (framepos_t start, framepos_t end)
{
@@ -1861,12 +1764,12 @@ Playlist::regions_touched (framepos_t start, framepos_t end)
boost::shared_ptr<RegionList> rlist (new RegionList);
for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
- if ((*i)->coverage (start, end) != OverlapNone) {
+ if ((*i)->coverage (start, end) != Evoral::OverlapNone) {
rlist->push_back (*i);
}
}
- return rlist;
+ return rlist;
}
framepos_t
@@ -2205,7 +2108,7 @@ Playlist::regions_touched (framepos_t start, framepos_t end)
*/
for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
- check_dependents (*r, false);
+ check_crossfades ((*r)->range ());
}
}
@@ -2674,6 +2577,8 @@ Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
_shuffling = true;
+ Evoral::Range<framepos_t> old_range = region->range ();
+
{
RegionLock rlock (const_cast<Playlist*> (this));
@@ -2772,7 +2677,11 @@ Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
if (moved) {
relayer ();
- check_dependents (region, false);
+
+ list<Evoral::Range<framepos_t> > xf;
+ xf.push_back (old_range);
+ xf.push_back (region->range ());
+ coalesce_and_check_crossfades (xf);
notify_contents_changed();
}
@@ -2936,10 +2845,6 @@ Playlist::combine (const RegionList& r)
pre_combine (copies);
- /* add any dependent regions to the new playlist */
-
- copy_dependents (old_and_new_regions, pl.get());
-
/* now create a new PlaylistSource for each channel in the new playlist */
SourceList sources;
@@ -3081,13 +2986,13 @@ Playlist::uncombine (boost::shared_ptr<Region> target)
modified_region = false;
switch (original->coverage (adjusted_start, adjusted_end)) {
- case OverlapNone:
+ case Evoral::OverlapNone:
/* original region does not cover any part
of the current state of the compound region
*/
continue;
- case OverlapInternal:
+ case Evoral::OverlapInternal:
/* overlap is just a small piece inside the
* original so trim both ends
*/
@@ -3095,13 +3000,13 @@ Playlist::uncombine (boost::shared_ptr<Region> target)
modified_region = true;
break;
- case OverlapExternal:
+ case Evoral::OverlapExternal:
/* overlap fully covers original, so leave it
as is
*/
break;
- case OverlapEnd:
+ case Evoral::OverlapEnd:
/* overlap starts within but covers end,
so trim the front of the region
*/
@@ -3109,7 +3014,7 @@ Playlist::uncombine (boost::shared_ptr<Region> target)
modified_region = true;
break;
- case OverlapStart:
+ case Evoral::OverlapStart:
/* overlap covers start but ends within, so
* trim the end of the region.
*/
@@ -3152,10 +3057,6 @@ Playlist::uncombine (boost::shared_ptr<Region> target)
add_region ((*i), (*i)->position());
}
- /* now move dependent regions back from the compound to this playlist */
-
- pl->copy_dependents (old_and_new_regions, this);
-
in_partition = false;
thaw ();
}
@@ -3178,3 +3079,34 @@ Playlist::set_orig_track_id (const PBD::ID& id)
{
_orig_track_id = id;
}
+
+void
+Playlist::coalesce_and_check_crossfades (list<Evoral::Range<framepos_t> > ranges)
+{
+ /* XXX: it's a shame that this coalesce algorithm also exists in
+ TimeSelection::consolidate().
+ */
+
+ /* XXX: xfade: this is implemented in Evoral::RangeList */
+
+restart:
+ for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
+ for (list<Evoral::Range<framepos_t> >::iterator j = ranges.begin(); j != ranges.end(); ++j) {
+
+ if (i == j) {
+ continue;
+ }
+
+ if (Evoral::coverage (i->from, i->to, j->from, j->to) != Evoral::OverlapNone) {
+ i->from = min (i->from, j->from);
+ i->to = max (i->to, j->to);
+ ranges.erase (j);
+ goto restart;
+ }
+ }
+ }
+
+ for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
+ check_crossfades (*i);
+ }
+}
diff --git a/libs/ardour/region.cc b/libs/ardour/region.cc
index 99906665d7..efaa104d35 100644
--- a/libs/ardour/region.cc
+++ b/libs/ardour/region.cc
@@ -1312,7 +1312,7 @@ Region::send_change (const PropertyChange& what_changed)
bool
Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
{
- return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
+ return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone;
}
bool
diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc
index 7416187cf5..152113aa83 100644
--- a/libs/ardour/session.cc
+++ b/libs/ardour/session.cc
@@ -66,7 +66,6 @@
#include "ardour/click.h"
#include "ardour/configuration.h"
#include "ardour/control_protocol_manager.h"
-#include "ardour/crossfade.h"
#include "ardour/cycle_timer.h"
#include "ardour/data_type.h"
#include "ardour/debug.h"
@@ -328,8 +327,6 @@ Session::destroy ()
delete *i;
}
- Crossfade::set_buffer_size (0);
-
/* not strictly necessary, but doing it here allows the shared_ptr debugging to work */
playlists.reset ();
diff --git a/libs/ardour/session_butler.cc b/libs/ardour/session_butler.cc
index c92c0604c7..87e3c34fb5 100644
--- a/libs/ardour/session_butler.cc
+++ b/libs/ardour/session_butler.cc
@@ -35,7 +35,6 @@
#include "ardour/audioengine.h"
#include "ardour/butler.h"
#include "ardour/configuration.h"
-#include "ardour/crossfade.h"
#include "ardour/io.h"
#include "ardour/midi_diskstream.h"
#include "ardour/session.h"
diff --git a/libs/ardour/session_command.cc b/libs/ardour/session_command.cc
index e5ddd0a097..68aba1a689 100644
--- a/libs/ardour/session_command.cc
+++ b/libs/ardour/session_command.cc
@@ -34,8 +34,6 @@
#include "ardour/session_playlists.h"
#include "ardour/region_factory.h"
#include "ardour/midi_automation_list_binder.h"
-#include "ardour/crossfade_binder.h"
-#include "ardour/crossfade.h"
#include "pbd/error.h"
#include "pbd/id.h"
#include "pbd/statefuldestructible.h"
@@ -143,19 +141,6 @@ Session::memento_command_factory(XMLNode *n)
cerr << "Alist " << id << " not found\n";
- } else if (obj_T == "ARDOUR::Crossfade") {
- if (have_id) {
- boost::shared_ptr<Crossfade> c = playlists->find_crossfade (id);
- if (c) {
- return new MementoCommand<Crossfade> (*c.get(), before, after);
- }
- } else {
- return new MementoCommand<Crossfade> (
- new CrossfadeBinder (n, playlists),
- before, after
- );
- }
-
} else if (registry.count(id)) { // For Editor and AutomationLine which are off-limits herea
return new MementoCommand<PBD::StatefulDestructible>(*registry[id], before, after);
}
diff --git a/libs/ardour/session_process.cc b/libs/ardour/session_process.cc
index 60cb6ecee1..be3f4f968b 100644
--- a/libs/ardour/session_process.cc
+++ b/libs/ardour/session_process.cc
@@ -47,6 +47,8 @@
#include "i18n.h"
+#include <xmmintrin.h>
+
using namespace ARDOUR;
using namespace PBD;
using namespace std;
diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc
index 48e60c1a0e..218f70cd5b 100644
--- a/libs/ardour/session_state.cc
+++ b/libs/ardour/session_state.cc
@@ -83,7 +83,6 @@
#include "ardour/butler.h"
#include "ardour/configuration.h"
#include "ardour/control_protocol_manager.h"
-#include "ardour/crossfade.h"
#include "ardour/cycle_timer.h"
#include "ardour/directory_names.h"
#include "ardour/filename_extensions.h"
@@ -232,7 +231,6 @@ Session::first_stage_init (string fullpath, string snapshot_name)
/* default short fade = 15ms */
- Crossfade::set_short_xfade_length ((framecnt_t) floor (config.get_short_xfade_seconds() * frame_rate()));
SndFileSource::setup_standard_crossfades (*this, frame_rate());
last_mmc_step.tv_sec = 0;
diff --git a/libs/ardour/test/audio_region_test.cc b/libs/ardour/test/audio_region_test.cc
new file mode 100644
index 0000000000..72e886b4e1
--- /dev/null
+++ b/libs/ardour/test/audio_region_test.cc
@@ -0,0 +1,85 @@
+#include "ardour/playlist.h"
+#include "ardour/region.h"
+#include "ardour/audioregion.h"
+#include "audio_region_test.h"
+#include "test_globals.h"
+
+CPPUNIT_TEST_SUITE_REGISTRATION (AudioRegionTest);
+
+using namespace std;
+using namespace ARDOUR;
+
+void
+AudioRegionTest::readTest ()
+{
+ int const N = 1024;
+
+ Sample buf[N];
+ Sample mbuf[N];
+ float gbuf[N];
+
+ int const P = 100;
+ boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (_region[0]);
+
+ /* Simple read: 256 frames from start of region, no fades */
+
+ /* gbuf should be ignored; set it to 0 to ensure that it is */
+ for (int i = 0; i < N; ++i) {
+ gbuf[i] = 0;
+ }
+
+ ar->set_position (P);
+ ar->set_length (1024);
+
+ for (int i = 0; i < N; ++i) {
+ buf[i] = 0;
+ }
+
+ ar->_read_at (ar->_sources, ar->_length, buf, mbuf, gbuf, P, 256, 0, AudioRegion::ReadOps (0));
+ check_staircase (buf, 0, 256);
+
+ for (int i = 0; i < N; ++i) {
+ buf[i] = 0;
+ }
+
+ /* Offset read: 256 frames from 128 frames into the region, no fades */
+ ar->_read_at (ar->_sources, ar->_length, buf, mbuf, gbuf, P + 128, 256, 0, AudioRegion::ReadOps (0));
+ check_staircase (buf, 128, 256);
+
+ /* Simple read with a fade-in: 256 frames from start of region, with fades */
+ ar->set_default_fade_in ();
+ CPPUNIT_ASSERT_EQUAL (double (64), ar->_fade_in->back()->when);
+
+ for (int i = 0; i < N; ++i) {
+ buf[i] = 0;
+ }
+
+ ar->read_at (buf, mbuf, gbuf, P, 256, 0);
+ for (int i = 0; i < 64; ++i) {
+ /* XXX: this isn't very accurate, but close enough for now; needs investigation */
+ CPPUNIT_ASSERT_DOUBLES_EQUAL (float (i * i / 63.0), buf[i], 1e-4);
+ }
+ for (int i = 64; i < P; ++i) {
+ CPPUNIT_ASSERT_EQUAL (i, int (buf[i]));
+ }
+
+ /* Offset read: 256 frames from 128 frames into the region, with fades
+ (though the fade should not affect it, as it is finished before the read starts)
+ */
+
+ for (int i = 0; i < N; ++i) {
+ buf[i] = 0;
+ }
+
+ ar->read_at (buf, mbuf, gbuf, P + 128, 256, 0);
+ check_staircase (buf, 128, 256);
+}
+
+void
+AudioRegionTest::check_staircase (Sample* b, int offset, int N)
+{
+ for (int i = 0; i < N; ++i) {
+ int const j = i + offset;
+ CPPUNIT_ASSERT_EQUAL (j, int (b[i]));
+ }
+}
diff --git a/libs/ardour/test/audio_region_test.h b/libs/ardour/test/audio_region_test.h
new file mode 100644
index 0000000000..e746b19ef3
--- /dev/null
+++ b/libs/ardour/test/audio_region_test.h
@@ -0,0 +1,15 @@
+#include "ardour/types.h"
+#include "test_needing_playlist_and_regions.h"
+
+class AudioRegionTest : public TestNeedingPlaylistAndRegions
+{
+ CPPUNIT_TEST_SUITE (AudioRegionTest);
+ CPPUNIT_TEST (readTest);
+ CPPUNIT_TEST_SUITE_END ();
+
+public:
+ void readTest ();
+
+private:
+ void check_staircase (ARDOUR::Sample *, int, int);
+};
diff --git a/libs/ardour/test/playlist_read_test.cc b/libs/ardour/test/playlist_read_test.cc
new file mode 100644
index 0000000000..55c6abe4ad
--- /dev/null
+++ b/libs/ardour/test/playlist_read_test.cc
@@ -0,0 +1,171 @@
+#include "ardour/playlist.h"
+#include "ardour/region.h"
+#include "ardour/audioplaylist.h"
+#include "ardour/audioregion.h"
+#include "ardour/session.h"
+#include "playlist_read_test.h"
+#include "test_globals.h"
+
+CPPUNIT_TEST_SUITE_REGISTRATION (PlaylistReadTest);
+
+using namespace std;
+using namespace ARDOUR;
+
+void
+PlaylistReadTest::setUp ()
+{
+ TestNeedingPlaylistAndRegions::setUp ();
+
+ _N = 1024;
+ _buf = new Sample[_N];
+ _mbuf = new Sample[_N];
+ _gbuf = new float[_N];
+
+ _session->config.set_auto_xfade (false);
+
+ _apl = boost::dynamic_pointer_cast<AudioPlaylist> (_playlist);
+
+ for (int i = 0; i < _N; ++i) {
+ _buf[i] = 0;
+ }
+}
+
+void
+PlaylistReadTest::tearDown ()
+{
+ delete[] _buf;
+ delete[] _mbuf;
+ delete[] _gbuf;
+
+ _apl.reset ();
+
+ TestNeedingPlaylistAndRegions::tearDown ();
+}
+
+void
+PlaylistReadTest::singleReadTest ()
+{
+ /* Single-region read with fades */
+
+ boost::shared_ptr<AudioRegion> ar0 = boost::dynamic_pointer_cast<AudioRegion> (_region[0]);
+ ar0->set_name ("ar0");
+ _apl->add_region (ar0, 0);
+ ar0->set_default_fade_in ();
+ ar0->set_default_fade_out ();
+ CPPUNIT_ASSERT_EQUAL (double (64), ar0->_fade_in->back()->when);
+ CPPUNIT_ASSERT_EQUAL (double (64), ar0->_fade_out->back()->when);
+ ar0->set_length (1024);
+ _apl->read (_buf, _mbuf, _gbuf, 0, 256, 0);
+
+ for (int i = 0; i < 64; ++i) {
+ /* Note: this specific float casting is necessary so that the rounding
+ is done here the same as it is done in AudioPlaylist.
+ */
+ CPPUNIT_ASSERT_DOUBLES_EQUAL (float (i * float (i / 63.0)), _buf[i], 1e-16);
+ }
+
+ for (int i = 64; i < 256; ++i) {
+ CPPUNIT_ASSERT_EQUAL (i, int (_buf[i]));
+ }
+}
+
+void
+PlaylistReadTest::overlappingReadTest ()
+{
+ /* Overlapping read; ar0 and ar1 are both 1024 frames long, ar0 starts at 0,
+ ar1 starts at 128. We test a read from 0 to 256, which should consist
+ of the start of ar0, with its fade in, followed by ar1's fade in (mixed with ar0)
+ and some more of ar1.
+ */
+
+ boost::shared_ptr<AudioRegion> ar0 = boost::dynamic_pointer_cast<AudioRegion> (_region[0]);
+ ar0->set_name ("ar0");
+ _apl->add_region (ar0, 0);
+ ar0->set_default_fade_in ();
+ ar0->set_default_fade_out ();
+ CPPUNIT_ASSERT_EQUAL (double (64), ar0->_fade_in->back()->when);
+ CPPUNIT_ASSERT_EQUAL (double (64), ar0->_fade_out->back()->when);
+ ar0->set_length (1024);
+
+ boost::shared_ptr<AudioRegion> ar1 = boost::dynamic_pointer_cast<AudioRegion> (_region[1]);
+ ar1->set_name ("ar1");
+ _apl->add_region (ar1, 128);
+ ar1->set_default_fade_in ();
+ ar1->set_default_fade_out ();
+
+ CPPUNIT_ASSERT_EQUAL (double (64), ar1->_fade_in->back()->when);
+ CPPUNIT_ASSERT_EQUAL (double (64), ar1->_fade_out->back()->when);
+
+ ar1->set_length (1024);
+ _apl->read (_buf, _mbuf, _gbuf, 0, 256, 0);
+
+ /* ar0's fade in */
+ for (int i = 0; i < 64; ++i) {
+ /* Note: this specific float casting is necessary so that the rounding
+ is done here the same as it is done in AudioPlaylist.
+ */
+ CPPUNIT_ASSERT_DOUBLES_EQUAL (float (i * float (i / 63.0)), _buf[i], 1e-16);
+ }
+
+ /* bit of ar0 */
+ for (int i = 64; i < 128; ++i) {
+ CPPUNIT_ASSERT_EQUAL (i, int (_buf[i]));
+ }
+
+ /* ar1's fade in */
+ for (int i = 0; i < 64; ++i) {
+ /* Similar carry-on to above with float rounding */
+ CPPUNIT_ASSERT_DOUBLES_EQUAL (i + 128 + float (i * float (i / 63.0)), _buf[i + 128], 1e-4);
+ }
+}
+
+void
+PlaylistReadTest::transparentReadTest ()
+{
+ boost::shared_ptr<AudioRegion> ar0 = boost::dynamic_pointer_cast<AudioRegion> (_region[0]);
+ ar0->set_name ("ar0");
+ _apl->add_region (ar0, 0);
+ ar0->set_default_fade_in ();
+ ar0->set_default_fade_out ();
+ CPPUNIT_ASSERT_EQUAL (double (64), ar0->_fade_in->back()->when);
+ CPPUNIT_ASSERT_EQUAL (double (64), ar0->_fade_out->back()->when);
+ ar0->set_length (1024);
+
+ boost::shared_ptr<AudioRegion> ar1 = boost::dynamic_pointer_cast<AudioRegion> (_region[1]);
+ ar1->set_name ("ar1");
+ _apl->add_region (ar1, 0);
+ ar1->set_default_fade_in ();
+ ar1->set_default_fade_out ();
+ CPPUNIT_ASSERT_EQUAL (double (64), ar1->_fade_in->back()->when);
+ CPPUNIT_ASSERT_EQUAL (double (64), ar1->_fade_out->back()->when);
+ ar1->set_length (1024);
+ ar1->set_opaque (false);
+
+ _apl->read (_buf, _mbuf, _gbuf, 0, 1024, 0);
+
+ /* ar0 and ar1 fade-ins, mixed */
+ for (int i = 0; i < 64; ++i) {
+ float const fade = i / 63.0;
+ CPPUNIT_ASSERT_DOUBLES_EQUAL (float (i * fade) * 2, _buf[i], 1e-16);
+ }
+
+ /* ar0 and ar1 bodies, mixed */
+ for (int i = 64; i < (1024 - 64); ++i) {
+ CPPUNIT_ASSERT_DOUBLES_EQUAL (float (i * 2), _buf[i], 1e-16);
+ }
+
+ /* ar0 and ar1 fade-outs, mixed */
+ for (int i = (1024 - 64); i < 1024; ++i) {
+ float const fade = (1023 - i) / 63.0;
+ CPPUNIT_ASSERT_DOUBLES_EQUAL (float (i * fade) * 2, _buf[i], 1e-16);
+ }
+}
+
+void
+PlaylistReadTest::check_staircase (Sample* b, int offset, int N)
+{
+ for (int i = 0; i < N; ++i) {
+ int const j = i + offset;
+ CPPUNIT_ASSERT_EQUAL (j, int (b[i]));
+ }
+}
diff --git a/libs/ardour/test/playlist_read_test.h b/libs/ardour/test/playlist_read_test.h
new file mode 100644
index 0000000000..9328d926e0
--- /dev/null
+++ b/libs/ardour/test/playlist_read_test.h
@@ -0,0 +1,27 @@
+#include "ardour/types.h"
+#include "test_needing_playlist_and_regions.h"
+
+class PlaylistReadTest : public TestNeedingPlaylistAndRegions
+{
+ CPPUNIT_TEST_SUITE (PlaylistReadTest);
+ CPPUNIT_TEST (singleReadTest);
+ CPPUNIT_TEST (transparentReadTest);
+ CPPUNIT_TEST_SUITE_END ();
+
+public:
+ void setUp ();
+ void tearDown ();
+
+ void singleReadTest ();
+ void overlappingReadTest ();
+ void transparentReadTest ();
+
+private:
+ int _N;
+ ARDOUR::Sample* _buf;
+ ARDOUR::Sample* _mbuf;
+ float* _gbuf;
+ boost::shared_ptr<ARDOUR::AudioPlaylist> _apl;
+
+ void check_staircase (ARDOUR::Sample *, int, int);
+};
diff --git a/libs/ardour/test/tempo_test.cc b/libs/ardour/test/tempo_test.cc
index 845116470e..fa9ad0a520 100644
--- a/libs/ardour/test/tempo_test.cc
+++ b/libs/ardour/test/tempo_test.cc
@@ -41,15 +41,6 @@ TempoTest::recomputeMapTest ()
Meter meterB (3, 4);
map.add_meter (meterB, BBT_Time (4, 1, 0));
- cout << "\n\n\n";
- for (list<MetricSection*>::iterator i = map.metrics.begin(); i != map.metrics.end(); ++i) {
- if (dynamic_cast<TempoSection*> (*i)) {
- cout << "\tTempo MS @ " << (*i)->start() << " " << (*i)->frame() << "\n";
- } else {
- cout << "\tMeter MS @ " << (*i)->start() << " " << (*i)->frame() << "\n";
- }
- }
-
list<MetricSection*>::iterator i = map.metrics.begin();
CPPUNIT_ASSERT_EQUAL (framepos_t (0), (*i)->frame ());
diff --git a/libs/ardour/test/test_globals.cc b/libs/ardour/test/test_globals.cc
new file mode 100644
index 0000000000..ff00fbc2e5
--- /dev/null
+++ b/libs/ardour/test/test_globals.cc
@@ -0,0 +1,4 @@
+#include "test_globals.h"
+
+int const Fs = 44100;
+int const sinusoid_frequency = 440;
diff --git a/libs/ardour/test/test_globals.h b/libs/ardour/test/test_globals.h
new file mode 100644
index 0000000000..8595e0c9b1
--- /dev/null
+++ b/libs/ardour/test/test_globals.h
@@ -0,0 +1,3 @@
+
+extern int const Fs;
+extern int const sinusoid_frequency;
diff --git a/libs/ardour/test/test_needing_playlist_and_regions.cc b/libs/ardour/test/test_needing_playlist_and_regions.cc
index fea08cc4d9..4e0e42c67b 100644
--- a/libs/ardour/test/test_needing_playlist_and_regions.cc
+++ b/libs/ardour/test/test_needing_playlist_and_regions.cc
@@ -3,7 +3,9 @@
#include "ardour/source_factory.h"
#include "ardour/region.h"
#include "ardour/region_factory.h"
+#include "ardour/sndfilesource.h"
#include "test_needing_playlist_and_regions.h"
+#include "test_globals.h"
using namespace std;
using namespace PBD;
@@ -14,9 +16,25 @@ TestNeedingPlaylistAndRegions::setUp ()
{
TestNeedingSession::setUp ();
+ /* This is important, otherwise createWritable will mark the source immutable (hence unwritable) */
+ unlink ("libs/ardour/test/test.wav");
string const test_wav_path = "libs/ardour/test/test.wav";
_playlist = PlaylistFactory::create (DataType::AUDIO, *_session, "test");
- _source = SourceFactory::createWritable (DataType::AUDIO, *_session, test_wav_path, "", false, 44100);
+ _source = SourceFactory::createWritable (DataType::AUDIO, *_session, test_wav_path, "", false, Fs);
+
+ /* Write a staircase to the source */
+
+ boost::shared_ptr<SndFileSource> s = boost::dynamic_pointer_cast<SndFileSource> (_source);
+ assert (s);
+
+ int const signal_length = 4096;
+
+ Sample staircase[signal_length];
+ for (int i = 0; i < signal_length; ++i) {
+ staircase[i] = i;
+ }
+
+ s->write (staircase, signal_length);
PropertyList plist;
plist.add (Properties::start, 0);
diff --git a/libs/ardour/wscript b/libs/ardour/wscript
index aec91cb2fd..b6dca5f376 100644
--- a/libs/ardour/wscript
+++ b/libs/ardour/wscript
@@ -18,7 +18,7 @@ LIBARDOUR_VERSION = "%s.%s.%s" % (MAJOR, MINOR, MICRO)
LIBARDOUR_LIB_VERSION = '3.0.0'
# default state file version for this build
-CURRENT_SESSION_FILE_VERSION = 3000
+CURRENT_SESSION_FILE_VERSION = 3001
# Variables for 'waf dist'
APPNAME = 'libardour3'
@@ -70,8 +70,6 @@ libardour_sources = [
'config_text.cc',
'control_protocol_manager.cc',
'control_protocol_search_path.cc',
- 'crossfade.cc',
- 'crossfade_binder.cc',
'cycle_timer.cc',
'data_type.cc',
'default_click.cc',
@@ -408,8 +406,7 @@ def build(bld):
obj.source += [ 'audio_unit.cc' ]
if Options.options.fpu_optimization:
- if (bld.env['build_target'] == 'i386'
- or bld.env['build_target'] == 'i686'):
+ if (bld.env['build_target'] == 'i386' or bld.env['build_target'] == 'i686'):
obj.source += [ 'sse_functions_xmm.cc', 'sse_functions.s' ]
elif bld.env['build_target'] == 'x86_64':
obj.source += [ 'sse_functions_xmm.cc', 'sse_functions_64bit.s' ]
@@ -430,6 +427,8 @@ def build(bld):
test/dummy_lxvst.cc
test/test_needing_session.cc
test/test_needing_playlist_and_regions.cc
+ test/test_globals.cc
+ test/audio_region_test.cc
test/bbt_test.cc
test/tempo_test.cc
test/interpolation_test.cc
@@ -439,6 +438,7 @@ def build(bld):
test/framepos_plus_beats_test.cc
test/framepos_minus_beats_test.cc
test/playlist_layering_test.cc
+ test/playlist_read_test.cc
test/testrunner.cc
'''.split()
diff --git a/libs/evoral/evoral/ControlList.hpp b/libs/evoral/evoral/ControlList.hpp
index 01e0f8c1fb..324d03cc28 100644
--- a/libs/evoral/evoral/ControlList.hpp
+++ b/libs/evoral/evoral/ControlList.hpp
@@ -26,6 +26,7 @@
#include <glibmm/thread.h>
#include "pbd/signals.h"
#include "evoral/types.hpp"
+#include "evoral/Range.hpp"
#include "evoral/Parameter.hpp"
namespace Evoral {
diff --git a/libs/evoral/evoral/Range.hpp b/libs/evoral/evoral/Range.hpp
new file mode 100644
index 0000000000..bc353a47d7
--- /dev/null
+++ b/libs/evoral/evoral/Range.hpp
@@ -0,0 +1,219 @@
+/* This file is part of Evoral.
+ * Copyright (C) 2008 David Robillard <http://drobilla.net>
+ * Copyright (C) 2000-2008 Paul Davis
+ *
+ * Evoral is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Evoral is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef EVORAL_RANGE_HPP
+#define EVORAL_RANGE_HPP
+
+#include <list>
+
+namespace Evoral {
+
+enum OverlapType {
+ OverlapNone, // no overlap
+ OverlapInternal, // the overlap is 100% with the object
+ OverlapStart, // overlap covers start, but ends within
+ OverlapEnd, // overlap begins within and covers end
+ OverlapExternal // overlap extends to (at least) begin+end
+};
+
+template<typename T>
+OverlapType coverage (T sa, T ea, T sb, T eb) {
+ /* OverlapType returned reflects how the second (B)
+ range overlaps the first (A).
+
+ The diagrams show various relative placements
+ of A and B for each OverlapType.
+
+ Notes:
+ Internal: the start points cannot coincide
+ External: the start and end points can coincide
+ Start: end points can coincide
+ End: start points can coincide
+
+ XXX Logically, Internal should disallow end
+ point equality.
+ */
+
+ /*
+ |--------------------| A
+ |------| B
+ |-----------------| B
+
+
+ "B is internal to A"
+
+ */
+
+ if ((sb > sa) && (eb <= ea)) {
+ return OverlapInternal;
+ }
+
+ /*
+ |--------------------| A
+ ----| B
+ -----------------------| B
+ --| B
+
+ "B overlaps the start of A"
+
+ */
+
+ if ((eb >= sa) && (eb <= ea)) {
+ return OverlapStart;
+ }
+ /*
+ |---------------------| A
+ |----------------- B
+ |----------------------- B
+ |- B
+
+ "B overlaps the end of A"
+
+ */
+ if ((sb > sa) && (sb <= ea)) {
+ return OverlapEnd;
+ }
+ /*
+ |--------------------| A
+ -------------------------- B
+ |----------------------- B
+ ----------------------| B
+ |--------------------| B
+
+
+ "B overlaps all of A"
+ */
+ if ((sa >= sb) && (sa <= eb) && (ea <= eb)) {
+ return OverlapExternal;
+ }
+
+ return OverlapNone;
+}
+
+/** Type to describe a time range */
+template<typename T>
+struct Range {
+ Range (T f, T t) : from (f), to (t) {}
+ T from; ///< start of the range
+ T to; ///< end of the range
+};
+
+template<typename T>
+bool operator== (Range<T> a, Range<T> b) {
+ return a.from == b.from && a.to == b.to;
+}
+
+template<typename T>
+class RangeList {
+public:
+ RangeList () : _dirty (false) {}
+
+ typedef std::list<Range<T> > List;
+
+ List const & get () {
+ coalesce ();
+ return _list;
+ }
+
+ void add (Range<T> const & range) {
+ _dirty = true;
+ _list.push_back (range);
+ }
+
+ bool empty () const {
+ return _list.empty ();
+ }
+
+private:
+ void coalesce () {
+ if (!_dirty) {
+ return;
+ }
+
+ restart:
+ for (typename List::iterator i = _list.begin(); i != _list.end(); ++i) {
+ for (typename List::iterator j = _list.begin(); j != _list.end(); ++j) {
+
+ if (i == j) {
+ continue;
+ }
+
+ if (coverage (i->from, i->to, j->from, j->to) != OverlapNone) {
+ i->from = std::min (i->from, j->from);
+ i->to = std::max (i->to, j->to);
+ _list.erase (j);
+ goto restart;
+ }
+ }
+ }
+
+ _dirty = false;
+ }
+
+ List _list;
+ bool _dirty;
+};
+
+/** Type to describe the movement of a time range */
+template<typename T>
+struct RangeMove {
+ RangeMove (T f, double l, T t) : from (f), length (l), to (t) {}
+ T from; ///< start of the range
+ double length; ///< length of the range
+ T to; ///< new start of the range
+};
+
+template<typename T>
+RangeList<T> subtract (Range<T> range, RangeList<T> sub)
+{
+ RangeList<T> result;
+
+ if (sub.empty ()) {
+ result.add (range);
+ return result;
+ }
+
+ T x = range.from;
+
+ typename RangeList<T>::List s = sub.get ();
+
+ for (typename RangeList<T>::List::const_iterator i = s.begin(); i != s.end(); ++i) {
+
+ if (coverage (range.from, range.to, i->from, i->to) == OverlapNone) {
+ continue;
+ }
+
+ Range<T> clamped (std::max (range.from, i->from), std::min (range.to, i->to));
+
+ if (clamped.from != x) {
+ result.add (Range<T> (x, clamped.from - 1));
+ }
+
+ x = clamped.to;
+ }
+
+ if (s.back().to < range.to) {
+ result.add (Range<T> (x, range.to));
+ }
+
+ return result;
+}
+
+}
+
+#endif
diff --git a/libs/evoral/evoral/types.hpp b/libs/evoral/evoral/types.hpp
index c3cb6a9c21..35dec6de0b 100644
--- a/libs/evoral/evoral/types.hpp
+++ b/libs/evoral/evoral/types.hpp
@@ -46,23 +46,6 @@ static inline bool musical_time_equal (MusicalTime a, MusicalTime b) {
/** Type of an event (opaque, mapped by application) */
typedef uint32_t EventType;
-/** Type to describe a time range */
-template<typename T>
-struct Range {
- Range (T f, T t) : from (f), to (t) {}
- T from; ///< start of the range
- T to; ///< end of the range
-};
-
-/** Type to describe the movement of a time range */
-template<typename T>
-struct RangeMove {
- RangeMove (T f, double l, T t) : from (f), length (l), to (t) {}
- T from; ///< start of the range
- double length; ///< length of the range
- T to; ///< new start of the range
-};
-
} // namespace Evoral
namespace PBD {
diff --git a/libs/evoral/run-tests.sh b/libs/evoral/run-tests.sh
index 35531a613e..8eb7ba8820 100755
--- a/libs/evoral/run-tests.sh
+++ b/libs/evoral/run-tests.sh
@@ -1,14 +1,14 @@
#!/bin/sh
srcdir=`pwd`
-export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$srcdir/../../build/default/libs/evoral:$srcdir/../../build/default/libs/pbd
+export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$srcdir/../../build/libs/evoral:$srcdir/../../build/libs/pbd
if [ ! -f './test/testdata/TakeFive.mid' ]; then
echo "This script must be run from within the libs/evoral directory";
exit 1;
fi
# Make symlink to TakeFive.mid in build directory
-cd ../../build/default/libs/evoral
+cd ../../build/libs/evoral
mkdir -p ./test/testdata
ln -fs $srcdir/test/testdata/TakeFive.mid \
./test/testdata/TakeFive.mid
diff --git a/libs/evoral/src/ControlList.cpp b/libs/evoral/src/ControlList.cpp
index 75b591d6a3..758dc141e0 100644
--- a/libs/evoral/src/ControlList.cpp
+++ b/libs/evoral/src/ControlList.cpp
@@ -465,6 +465,8 @@ ControlList::fast_simple_add (double when, double value)
/* to be used only for loading pre-sorted data from saved state */
_events.insert (_events.end(), new ControlEvent (when, value));
assert(_events.back());
+
+ mark_dirty ();
}
void
diff --git a/libs/evoral/src/Curve.cpp b/libs/evoral/src/Curve.cpp
index dd327d488a..42f0eee49c 100644
--- a/libs/evoral/src/Curve.cpp
+++ b/libs/evoral/src/Curve.cpp
@@ -190,7 +190,7 @@ Curve::get_vector (double x0, double x1, float *vec, int32_t veclen)
void
Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen)
{
- double rx, dx, lx, hx, max_x, min_x;
+ double rx, lx, hx, max_x, min_x;
int32_t i;
int32_t original_veclen;
int32_t npoints;
@@ -276,26 +276,34 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen)
/* linear interpolation between 2 points */
- /* XXX I'm not sure that this is the right thing to
- do here. but its not a common case for the envisaged
- uses.
+ /* XXX: this numerator / denominator stuff is pretty grim, but it's the only
+ way I could get the maths to be accurate; doing everything with pure doubles
+ gives ~1e-17 errors in the vec[i] computation.
*/
- if (veclen > 1) {
- dx = (hx - lx) / (veclen - 1) ;
- } else {
- dx = 0; // not used
- }
+ /* gradient of the line */
+ double const m_num = _list.events().back()->value - _list.events().front()->value;
+ double const m_den = _list.events().back()->when - _list.events().front()->when;
- double slope = (_list.events().back()->value - _list.events().front()->value)/
- (_list.events().back()->when - _list.events().front()->when);
- double yfrac = dx*slope;
+ /* y intercept of the line */
+ double const c = double (_list.events().back()->value) - (m_num * _list.events().back()->when / m_den);
- vec[0] = _list.events().front()->value + slope * (lx - _list.events().front()->when);
+ /* dx that we are using */
+ double dx_num = 0;
+ double dx_den = 1;
+ if (veclen > 1) {
+ dx_num = hx - lx;
+ dx_den = veclen - 1;
+ }
- for (i = 1; i < veclen; ++i) {
- vec[i] = vec[i-1] + yfrac;
+ if (veclen > 1) {
+ for (int i = 0; i < veclen; ++i) {
+ vec[i] = (lx * (m_num / m_den) + m_num * i * dx_num / (m_den * dx_den)) + c;
+ }
+ } else {
+ vec[i] = lx;
}
+
return;
}
@@ -305,10 +313,9 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen)
rx = lx;
+ double dx = 0;
if (veclen > 1) {
dx = (hx - lx) / (veclen - 1);
- } else {
- dx = 0;
}
for (i = 0; i < veclen; ++i, rx += dx) {
diff --git a/libs/evoral/test/RangeTest.cpp b/libs/evoral/test/RangeTest.cpp
new file mode 100644
index 0000000000..ff9856a9b6
--- /dev/null
+++ b/libs/evoral/test/RangeTest.cpp
@@ -0,0 +1,84 @@
+#include "RangeTest.hpp"
+#include "evoral/Range.hpp"
+
+CPPUNIT_TEST_SUITE_REGISTRATION (RangeTest);
+
+using namespace Evoral;
+
+void
+RangeTest::coalesceTest ()
+{
+ RangeList<int> fred;
+ fred.add (Range<int> (2, 4));
+ fred.add (Range<int> (5, 6));
+ fred.add (Range<int> (6, 8));
+
+ RangeList<int>::List jim = fred.get ();
+
+ RangeList<int>::List::iterator i = jim.begin ();
+ CPPUNIT_ASSERT_EQUAL (2, i->from);
+ CPPUNIT_ASSERT_EQUAL (4, i->to);
+
+ ++i;
+ CPPUNIT_ASSERT_EQUAL (5, i->from);
+ CPPUNIT_ASSERT_EQUAL (8, i->to);
+}
+
+void
+RangeTest::subtractTest1 ()
+{
+ Range<int> fred (0, 10);
+
+ RangeList<int> jim;
+ jim.add (Range<int> (2, 4));
+ jim.add (Range<int> (7, 8));
+
+ RangeList<int> sheila = subtract (fred, jim);
+
+ RangeList<int>::List s = sheila.get ();
+ CPPUNIT_ASSERT_EQUAL (size_t (3), s.size ());
+
+ RangeList<int>::List::iterator i = s.begin ();
+ CPPUNIT_ASSERT_EQUAL (0, i->from);
+ CPPUNIT_ASSERT_EQUAL (1, i->to);
+
+ ++i;
+ CPPUNIT_ASSERT_EQUAL (4, i->from);
+ CPPUNIT_ASSERT_EQUAL (6, i->to);
+
+ ++i;
+ CPPUNIT_ASSERT_EQUAL (8, i->from);
+ CPPUNIT_ASSERT_EQUAL (10, i->to);
+}
+
+void
+RangeTest::subtractTest2 ()
+{
+ Range<int> fred (0, 10);
+
+ RangeList<int> jim;
+ jim.add (Range<int> (12, 19));
+
+ RangeList<int> sheila = subtract (fred, jim);
+
+ RangeList<int>::List s = sheila.get ();
+ CPPUNIT_ASSERT_EQUAL (size_t (1), s.size ());
+
+ RangeList<int>::List::iterator i = s.begin ();
+ CPPUNIT_ASSERT_EQUAL (0, i->from);
+ CPPUNIT_ASSERT_EQUAL (10, i->to);
+}
+
+void
+RangeTest::subtractTest3 ()
+{
+ Range<int> fred (0, 10);
+
+ RangeList<int> jim;
+ jim.add (Range<int> (0, 12));
+
+ RangeList<int> sheila = subtract (fred, jim);
+
+ RangeList<int>::List s = sheila.get ();
+ CPPUNIT_ASSERT_EQUAL (size_t (0), s.size ());
+}
diff --git a/libs/evoral/test/RangeTest.hpp b/libs/evoral/test/RangeTest.hpp
new file mode 100644
index 0000000000..6b65ad02d2
--- /dev/null
+++ b/libs/evoral/test/RangeTest.hpp
@@ -0,0 +1,19 @@
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+
+class RangeTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE (RangeTest);
+ CPPUNIT_TEST (coalesceTest);
+ CPPUNIT_TEST (subtractTest1);
+ CPPUNIT_TEST (subtractTest3);
+ CPPUNIT_TEST_SUITE_END ();
+
+public:
+ void coalesceTest ();
+ void subtractTest1 ();
+ void subtractTest2 ();
+ void subtractTest3 ();
+};
+
+
diff --git a/libs/evoral/wscript b/libs/evoral/wscript
index e8bf097db2..9cc0356c7f 100644
--- a/libs/evoral/wscript
+++ b/libs/evoral/wscript
@@ -124,6 +124,7 @@ def build(bld):
obj.source = '''
test/SequenceTest.cpp
test/SMFTest.cpp
+ test/RangeTest.cpp
test/testrunner.cpp
'''
obj.includes = ['.', './src']