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
This commit is contained in:
parent
02c498a8fa
commit
a2897ecef6
@ -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)
|
||||
{
|
||||
@ -209,163 +188,10 @@ AudioStreamView::add_region_view_internal (boost::shared_ptr<Region> r, bool wai
|
||||
return region_view;
|
||||
}
|
||||
|
||||
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
|
||||
@ -600,14 +396,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)
|
||||
{
|
||||
@ -744,40 +532,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 ()
|
||||
{
|
||||
@ -796,45 +550,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)
|
||||
{
|
||||
@ -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 ();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
@ -365,54 +348,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 ()
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 ();
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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") {
|
||||
|
@ -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"
|
||||
@ -517,127 +516,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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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 ()
|
||||
{
|
||||
|
@ -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"
|
||||
@ -478,32 +477,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
|
||||
{
|
||||
@ -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.
|
||||
*/
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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',
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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 ();
|
||||
|
@ -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__ */
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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) {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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()) {
|
||||
/* COMPUTE DETAILS OF ANY FADES INVOLVED IN THIS READ */
|
||||
|
||||
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" */
|
||||
}
|
||||
/* Amount of fade in that we are dealing with in this read */
|
||||
framecnt_t fade_in_limit = 0;
|
||||
|
||||
} else {
|
||||
/* 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;
|
||||
|
||||
/* track is N-channel, this region has less 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" */
|
||||
}
|
||||
|
||||
} 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 */
|
||||
|
||||
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;
|
||||
|
||||
_fade_out->curve().get_vector (curve_offset, curve_offset+fo_limit, gain_buffer, fo_limit);
|
||||
|
||||
for (framecnt_t n = 0, m = fade_offset; n < fo_limit; ++n, ++m) {
|
||||
mixdown_buffer[m] *= gain_buffer[n];
|
||||
}
|
||||
/* (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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Regular gain curves and scaling */
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
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 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" */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
apply_gain_to_buffer (mixdown_buffer, to_read, _scale_amplitude);
|
||||
}
|
||||
|
||||
// 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 FADES TO THE DATA IN mixdown_buffer AND MIX THE RESULTS INTO buf */
|
||||
|
||||
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 < fade_in_limit; ++n) {
|
||||
buf[n] += mixdown_buffer[n] * gain_buffer[n];
|
||||
}
|
||||
}
|
||||
|
||||
if (!opaque() && (buf != mixdown_buffer)) {
|
||||
|
||||
/* gack. the things we do for users.
|
||||
*/
|
||||
|
||||
buf += buf_offset;
|
||||
|
||||
for (framecnt_t n = 0; n < to_read; ++n) {
|
||||
buf[n] += mixdown_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" {
|
||||
|
||||
|
@ -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)) {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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 ());
|
||||
}
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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);
|
||||
|
@ -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 ()
|
||||
{
|
||||
|
@ -19,6 +19,7 @@
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <cmath>
|
||||
#include <xmmintrin.h>
|
||||
|
||||
#include "pbd/compose.h"
|
||||
#include "pbd/debug_rt_alloc.h"
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)) {
|
||||
@ -1703,148 +1707,13 @@ Playlist::regions_at (framepos_t frame)
|
||||
return region;
|
||||
}
|
||||
|
||||
boost::shared_ptr<RegionList>
|
||||
Playlist::regions_to_read (framepos_t start, framepos_t end)
|
||||
{
|
||||
/* 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 */
|
||||
|
||||
RegionSortByPosition cmp;
|
||||
rlist->sort (cmp);
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("<<<<< REGIONS TO READ returns %1\n", rlist->size()));
|
||||
|
||||
return rlist;
|
||||
}
|
||||
|
||||
boost::shared_ptr<RegionList>
|
||||
Playlist::find_regions_at (framepos_t frame)
|
||||
{
|
||||
/* 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);
|
||||
@ -1854,6 +1723,40 @@ Playlist::find_regions_at (framepos_t frame)
|
||||
return rlist;
|
||||
}
|
||||
|
||||
boost::shared_ptr<RegionList>
|
||||
Playlist::regions_with_start_within (Evoral::Range<framepos_t> range)
|
||||
{
|
||||
RegionLock rlock (this);
|
||||
boost::shared_ptr<RegionList> rlist (new RegionList);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
boost::shared_ptr<RegionList>
|
||||
Playlist::regions_with_end_within (Evoral::Range<framepos_t> range)
|
||||
{
|
||||
RegionLock rlock (this);
|
||||
boost::shared_ptr<RegionList> rlist (new RegionList);
|
||||
|
||||
for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 ();
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -47,6 +47,8 @@
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
#include <xmmintrin.h>
|
||||
|
||||
using namespace ARDOUR;
|
||||
using namespace PBD;
|
||||
using namespace std;
|
||||
|
@ -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;
|
||||
|
85
libs/ardour/test/audio_region_test.cc
Normal file
85
libs/ardour/test/audio_region_test.cc
Normal file
@ -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]));
|
||||
}
|
||||
}
|
15
libs/ardour/test/audio_region_test.h
Normal file
15
libs/ardour/test/audio_region_test.h
Normal file
@ -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);
|
||||
};
|
171
libs/ardour/test/playlist_read_test.cc
Normal file
171
libs/ardour/test/playlist_read_test.cc
Normal file
@ -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]));
|
||||
}
|
||||
}
|
27
libs/ardour/test/playlist_read_test.h
Normal file
27
libs/ardour/test/playlist_read_test.h
Normal file
@ -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);
|
||||
};
|
@ -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 ());
|
||||
|
||||
|
4
libs/ardour/test/test_globals.cc
Normal file
4
libs/ardour/test/test_globals.cc
Normal file
@ -0,0 +1,4 @@
|
||||
#include "test_globals.h"
|
||||
|
||||
int const Fs = 44100;
|
||||
int const sinusoid_frequency = 440;
|
3
libs/ardour/test/test_globals.h
Normal file
3
libs/ardour/test/test_globals.h
Normal file
@ -0,0 +1,3 @@
|
||||
|
||||
extern int const Fs;
|
||||
extern int const sinusoid_frequency;
|
@ -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);
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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 {
|
||||
|
219
libs/evoral/evoral/Range.hpp
Normal file
219
libs/evoral/evoral/Range.hpp
Normal file
@ -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
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
*/
|
||||
|
||||
/* 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;
|
||||
|
||||
/* y intercept of the line */
|
||||
double const c = double (_list.events().back()->value) - (m_num * _list.events().back()->when / m_den);
|
||||
|
||||
/* dx that we are using */
|
||||
double dx_num = 0;
|
||||
double dx_den = 1;
|
||||
if (veclen > 1) {
|
||||
dx = (hx - lx) / (veclen - 1) ;
|
||||
dx_num = hx - lx;
|
||||
dx_den = veclen - 1;
|
||||
}
|
||||
|
||||
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 {
|
||||
dx = 0; // not used
|
||||
vec[i] = lx;
|
||||
}
|
||||
|
||||
double slope = (_list.events().back()->value - _list.events().front()->value)/
|
||||
(_list.events().back()->when - _list.events().front()->when);
|
||||
double yfrac = dx*slope;
|
||||
|
||||
vec[0] = _list.events().front()->value + slope * (lx - _list.events().front()->when);
|
||||
|
||||
for (i = 1; i < veclen; ++i) {
|
||||
vec[i] = vec[i-1] + yfrac;
|
||||
}
|
||||
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) {
|
||||
|
84
libs/evoral/test/RangeTest.cpp
Normal file
84
libs/evoral/test/RangeTest.cpp
Normal file
@ -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 ());
|
||||
}
|
19
libs/evoral/test/RangeTest.hpp
Normal file
19
libs/evoral/test/RangeTest.hpp
Normal file
@ -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 ();
|
||||
};
|
||||
|
||||
|
@ -124,6 +124,7 @@ def build(bld):
|
||||
obj.source = '''
|
||||
test/SequenceTest.cpp
|
||||
test/SMFTest.cpp
|
||||
test/RangeTest.cpp
|
||||
test/testrunner.cpp
|
||||
'''
|
||||
obj.includes = ['.', './src']
|
||||
|
Loading…
Reference in New Issue
Block a user