2 Copyright (C) 2004 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #include <sigc++/bind.h>
24 #include <gtkmm/frame.h>
25 #include <gtkmm/image.h>
26 #include <gtkmm/scrolledwindow.h>
28 #include <libgnomecanvasmm/line.h>
30 #include "ardour/automation_list.h"
31 #include "evoral/Curve.hpp"
32 #include "ardour/crossfade.h"
33 #include "ardour/session.h"
34 #include "ardour/auditioner.h"
35 #include "ardour/audioplaylist.h"
36 #include "ardour/audiosource.h"
37 #include "ardour/region_factory.h"
38 #include "ardour/profile.h"
40 #include <gtkmm2ext/gtk_ui.h>
42 #include "ardour_ui.h"
43 #include "crossfade_edit.h"
44 #include "rgb_macros.h"
47 #include "gui_thread.h"
48 #include "canvas_impl.h"
49 #include "simplerect.h"
54 using namespace ARDOUR;
57 using namespace Editing;
59 using Gtkmm2ext::Keyboard;
63 const int32_t CrossfadeEditor::Point::size = 7;
64 const double CrossfadeEditor::canvas_border = 10;
65 CrossfadeEditor::Presets* CrossfadeEditor::fade_in_presets = 0;
66 CrossfadeEditor::Presets* CrossfadeEditor::fade_out_presets = 0;
68 CrossfadeEditor::Half::Half ()
70 , normative_curve (Evoral::Parameter(GainAutomation))
71 , gain_curve (Evoral::Parameter(GainAutomation))
75 CrossfadeEditor::CrossfadeEditor (Session* s, boost::shared_ptr<Crossfade> xf, double my, double mxy)
76 : ArdourDialog (_("ardour: x-fade edit")),
78 clear_button (_("Clear")),
79 revert_button (_("Reset")),
80 audition_both_button (_("Fade")),
81 audition_left_dry_button (_("Out (dry)")),
82 audition_left_button (_("Out")),
83 audition_right_dry_button (_("In (dry)")),
84 audition_right_button (_("In")),
86 preroll_button (_("With Pre-roll")),
87 postroll_button (_("With Post-roll")),
93 fade_out_table (3, 3),
95 select_in_button (_("Fade In")),
96 select_out_button (_("Fade Out"))
100 set_wmclass (X_("ardour_automationedit"), "Ardour");
101 set_name ("CrossfadeEditWindow");
102 set_position (Gtk::WIN_POS_MOUSE);
104 add_accel_group (ActionManager::ui_manager->get_accel_group());
106 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
108 RadioButtonGroup sel_but_group = select_in_button.get_group();
109 select_out_button.set_group (sel_but_group);
110 select_out_button.set_mode (false);
111 select_in_button.set_mode (false);
113 get_action_area()->set_layout(BUTTONBOX_SPREAD);
114 get_action_area()->pack_start(clear_button);
115 get_action_area()->pack_start(revert_button);
116 cancel_button = add_button ("Cancel", RESPONSE_CANCEL);
117 ok_button = add_button ("OK", RESPONSE_ACCEPT);
119 if (fade_in_presets == 0) {
123 point_grabbed = false;
126 canvas = new ArdourCanvas::CanvasAA ();
127 canvas->signal_size_allocate().connect (sigc::mem_fun(*this, &CrossfadeEditor::canvas_allocation));
128 canvas->set_size_request (425, 200);
130 toplevel = new ArdourCanvas::SimpleRect (*(canvas->root()));
131 toplevel->property_x1() = 0.0;
132 toplevel->property_y1() = 0.0;
133 toplevel->property_x2() = 10.0;
134 toplevel->property_y2() = 10.0;
135 toplevel->property_fill() = true;
136 toplevel->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_CrossfadeEditorBase.get();
137 toplevel->property_outline_pixels() = 0;
138 toplevel->signal_event().connect (sigc::mem_fun (*this, &CrossfadeEditor::canvas_event));
140 fade[Out].line = new ArdourCanvas::Line (*(canvas->root()));
141 fade[Out].line->property_width_pixels() = 1;
142 fade[Out].line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_CrossfadeEditorLine.get();
144 fade[Out].shading = new ArdourCanvas::Polygon (*(canvas->root()));
145 fade[Out].shading->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_CrossfadeEditorLineShading.get();
147 fade[In].line = new ArdourCanvas::Line (*(canvas->root()));
148 fade[In].line->property_width_pixels() = 1;
149 fade[In].line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_CrossfadeEditorLine.get();
151 fade[In].shading = new ArdourCanvas::Polygon (*(canvas->root()));
152 fade[In].shading->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_CrossfadeEditorLineShading.get();
154 fade[In].shading->signal_event().connect (sigc::mem_fun (*this, &CrossfadeEditor::canvas_event));
155 fade[In].line->signal_event().connect (sigc::mem_fun (*this, &CrossfadeEditor::curve_event));
156 fade[Out].shading->signal_event().connect (sigc::mem_fun (*this, &CrossfadeEditor::canvas_event));
157 fade[Out].line->signal_event().connect (sigc::mem_fun (*this, &CrossfadeEditor::curve_event));
159 select_in_button.set_name (X_("CrossfadeEditCurveButton"));
160 select_out_button.set_name (X_("CrossfadeEditCurveButton"));
162 select_in_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (*this, &CrossfadeEditor::curve_select_clicked), In));
163 select_out_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (*this, &CrossfadeEditor::curve_select_clicked), Out));
165 HBox* acbox = manage (new HBox);
167 audition_box.set_border_width (7);
168 audition_box.set_spacing (5);
169 audition_box.set_homogeneous (false);
170 audition_box.pack_start (audition_left_dry_button, false, false);
171 audition_box.pack_start (audition_left_button, false, false);
172 audition_box.pack_start (audition_both_button, false, false);
173 audition_box.pack_start (audition_right_button, false, false);
174 audition_box.pack_start (audition_right_dry_button, false, false);
176 Frame* audition_frame = manage (new Frame (_("Audition")));
178 audition_frame->set_name (X_("CrossfadeEditFrame"));
179 audition_frame->add (audition_box);
181 acbox->pack_start (*audition_frame, true, false);
183 Frame* canvas_frame = manage (new Frame);
184 canvas_frame->add (*canvas);
185 canvas_frame->set_shadow_type (Gtk::SHADOW_IN);
187 fade_in_table.attach (select_in_button, 0, 2, 0, 1, Gtk::FILL|Gtk::EXPAND);
188 fade_out_table.attach (select_out_button, 0, 2, 0, 1, Gtk::FILL|Gtk::EXPAND);
198 for (list<Preset*>::iterator i = fade_in_presets->begin(); i != fade_in_presets->end(); ++i) {
200 pxmap = manage (new Image (::get_icon ((*i)->image_name)));
201 pbutton = manage (new Button);
202 pbutton->add (*pxmap);
203 pbutton->set_name ("CrossfadeEditButton");
204 pbutton->signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &CrossfadeEditor::apply_preset), *i));
205 ARDOUR_UI::instance()->set_tip (pbutton, (*i)->name, "");
206 fade_in_table.attach (*pbutton, col, col+1, row, row+1);
207 fade_in_buttons.push_back (pbutton);
220 for (list<Preset*>::iterator i = fade_out_presets->begin(); i != fade_out_presets->end(); ++i) {
222 pxmap = manage (new Image (::get_icon ((*i)->image_name)));
223 pbutton = manage (new Button);
224 pbutton->add (*pxmap);
225 pbutton->set_name ("CrossfadeEditButton");
226 pbutton->signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &CrossfadeEditor::apply_preset), *i));
227 ARDOUR_UI::instance()->set_tip (pbutton, (*i)->name, "");
228 fade_out_table.attach (*pbutton, col, col+1, row, row+1);
229 fade_out_buttons.push_back (pbutton);
239 clear_button.set_name ("CrossfadeEditButton");
240 revert_button.set_name ("CrossfadeEditButton");
241 ok_button->set_name ("CrossfadeEditButton");
242 cancel_button->set_name ("CrossfadeEditButton");
243 preroll_button.set_name ("CrossfadeEditButton");
244 postroll_button.set_name ("CrossfadeEditButton");
245 audition_both_button.set_name ("CrossfadeEditAuditionButton");
246 audition_left_dry_button.set_name ("CrossfadeEditAuditionButton");
247 audition_left_button.set_name ("CrossfadeEditAuditionButton");
248 audition_right_dry_button.set_name ("CrossfadeEditAuditionButton");
249 audition_right_button.set_name ("CrossfadeEditAuditionButton");
251 clear_button.signal_clicked().connect (sigc::mem_fun(*this, &CrossfadeEditor::clear));
252 revert_button.signal_clicked().connect (sigc::mem_fun(*this, &CrossfadeEditor::reset));
253 audition_both_button.signal_toggled().connect (sigc::mem_fun(*this, &CrossfadeEditor::audition_toggled));
254 audition_right_button.signal_toggled().connect (sigc::mem_fun(*this, &CrossfadeEditor::audition_right_toggled));
255 audition_right_dry_button.signal_toggled().connect (sigc::mem_fun(*this, &CrossfadeEditor::audition_right_dry_toggled));
256 audition_left_button.signal_toggled().connect (sigc::mem_fun(*this, &CrossfadeEditor::audition_left_toggled));
257 audition_left_dry_button.signal_toggled().connect (sigc::mem_fun(*this, &CrossfadeEditor::audition_left_dry_toggled));
259 roll_box.pack_start (preroll_button, false, false);
260 roll_box.pack_start (postroll_button, false, false);
262 Gtk::HBox* rcenter_box = manage (new HBox);
263 rcenter_box->pack_start (roll_box, true, false);
265 VBox* vpacker2 = manage (new (VBox));
267 vpacker2->set_border_width (12);
268 vpacker2->set_spacing (7);
269 vpacker2->pack_start (*acbox, false, false);
270 vpacker2->pack_start (*rcenter_box, false, false);
272 curve_button_box.set_spacing (7);
273 curve_button_box.pack_start (fade_out_table, false, false, 12);
274 curve_button_box.pack_start (*vpacker2, false, false, 12);
275 curve_button_box.pack_start (fade_in_table, false, false, 12);
277 get_vbox()->pack_start (*canvas_frame, true, true);
278 get_vbox()->pack_start (curve_button_box, false, false);
280 /* button to allow hackers to check the actual curve values */
282 // Button* foobut = manage (new Button ("dump"));
283 // foobut-.signal_clicked().connect (sigc::mem_fun(*this, &CrossfadeEditor::dump));
284 // vpacker.pack_start (*foobut, false, false);
287 set (xfade->fade_in(), In);
290 set (xfade->fade_out(), Out);
292 curve_select_clicked (In);
294 xfade->PropertyChanged.connect (state_connection, invalidator (*this), ui_bind (&CrossfadeEditor::xfade_changed, this, _1), gui_context());
296 _session->AuditionActive.connect (_session_connections, invalidator (*this), ui_bind (&CrossfadeEditor::audition_state_changed, this, _1), gui_context());
300 CrossfadeEditor::~CrossfadeEditor()
302 /* most objects will be destroyed when the toplevel window is. */
304 for (list<Point*>::iterator i = fade[In].points.begin(); i != fade[In].points.end(); ++i) {
308 for (list<Point*>::iterator i = fade[Out].points.begin(); i != fade[Out].points.end(); ++i) {
314 CrossfadeEditor::dump ()
316 for (AutomationList::iterator i = fade[Out].normative_curve.begin(); i != fade[Out].normative_curve.end(); ++i) {
317 cerr << (*i)->when << ' ' << (*i)->value << endl;
322 CrossfadeEditor::audition_state_changed (bool yn)
324 ENSURE_GUI_THREAD (*this, &CrossfadeEditor::audition_state_changed, yn)
327 audition_both_button.set_active (false);
328 audition_left_button.set_active (false);
329 audition_right_button.set_active (false);
330 audition_left_dry_button.set_active (false);
331 audition_right_dry_button.set_active (false);
336 CrossfadeEditor::set (const ARDOUR::AutomationList& curve, WhichFade which)
339 ARDOUR::AutomationList::const_iterator the_end;
341 for (list<Point*>::iterator i = fade[which].points.begin(); i != fade[which].points.end(); ++i) {
345 fade[which].points.clear ();
346 fade[which].gain_curve.clear ();
347 fade[which].normative_curve.clear ();
353 the_end = curve.end();
356 firstx = (*curve.begin())->when;
357 endx = (*the_end)->when;
359 for (ARDOUR::AutomationList::const_iterator i = curve.begin(); i != curve.end(); ++i) {
361 double xfract = ((*i)->when - firstx) / (endx - firstx);
362 double yfract = ((*i)->value - miny) / (maxy - miny);
364 Point* p = make_point ();
366 p->move_to (x_coordinate (xfract), y_coordinate (yfract),
369 fade[which].points.push_back (p);
372 /* no need to sort because curve is already time-ordered */
376 swap (which, current);
378 swap (which, current);
382 CrossfadeEditor::curve_event (GdkEvent* event)
384 /* treat it like a toplevel event */
386 return canvas_event (event);
390 CrossfadeEditor::point_event (GdkEvent* event, Point* point)
393 if (point->curve != fade[current].line) {
397 switch (event->type) {
398 case GDK_BUTTON_PRESS:
399 point_grabbed = true;
401 case GDK_BUTTON_RELEASE:
402 point_grabbed = false;
404 if (Keyboard::is_delete_event (&event->button)) {
405 fade[current].points.remove (point);
412 case GDK_MOTION_NOTIFY:
416 /* can't drag first or last points horizontally */
418 if (point == fade[current].points.front() || point == fade[current].points.back()) {
421 new_x = (event->motion.x - canvas_border)/effective_width();
424 new_y = 1.0 - ((event->motion.y - canvas_border)/effective_height());
425 point->move_to (x_coordinate (new_x), y_coordinate (new_y),
437 CrossfadeEditor::canvas_event (GdkEvent* event)
439 switch (event->type) {
440 case GDK_BUTTON_PRESS:
441 add_control_point ((event->button.x - canvas_border)/effective_width(),
442 1.0 - ((event->button.y - canvas_border)/effective_height()));
451 CrossfadeEditor::Point::~Point()
456 CrossfadeEditor::Point*
457 CrossfadeEditor::make_point ()
459 Point* p = new Point;
461 p->box = new ArdourCanvas::SimpleRect (*(canvas->root()));
462 p->box->property_fill() = true;
463 p->box->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_CrossfadeEditorPointFill.get();
464 p->box->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_CrossfadeEditorPointOutline.get();
465 p->box->property_outline_pixels() = 1;
467 p->curve = fade[current].line;
469 p->box->signal_event().connect (sigc::bind (sigc::mem_fun (*this, &CrossfadeEditor::point_event), p));
475 CrossfadeEditor::add_control_point (double x, double y)
479 /* enforce end point x location */
481 if (fade[current].points.empty()) {
483 } else if (fade[current].points.size() == 1) {
487 Point* p = make_point ();
489 p->move_to (x_coordinate (x), y_coordinate (y), x, y);
491 fade[current].points.push_back (p);
492 fade[current].points.sort (cmp);
498 CrossfadeEditor::Point::move_to (double nx, double ny, double xfract, double yfract)
500 if ( xfract < 0.0 ) {
502 } else if ( xfract > 1.0 ) {
506 if ( yfract < 0.0 ) {
508 } else if ( yfract > 1.0 ) {
512 const double half_size = rint(size/2.0);
513 double x1 = nx - half_size;
514 double x2 = nx + half_size;
516 box->property_x1() = x1;
517 box->property_x2() = x2;
519 box->property_y1() = ny - half_size;
520 box->property_y2() = ny + half_size;
527 CrossfadeEditor::canvas_allocation (Gtk::Allocation& /*alloc*/)
530 toplevel->property_x1() = 0.0;
531 toplevel->property_y1() = 0.0;
532 toplevel->property_x2() = (double) canvas->get_allocation().get_width() + canvas_border;
533 toplevel->property_y2() = (double) canvas->get_allocation().get_height() + canvas_border;
536 canvas->set_scroll_region (0.0, 0.0,
537 canvas->get_allocation().get_width(),
538 canvas->get_allocation().get_height());
540 Point* end = make_point ();
543 if (fade[In].points.size() > 1) {
544 Point* old_end = fade[In].points.back();
545 fade[In].points.pop_back ();
546 end->move_to (x_coordinate (old_end->x),
547 y_coordinate (old_end->y),
548 old_end->x, old_end->y);
553 end->move_to (x_coordinate (x), y_coordinate (y), x, y);
557 fade[In].points.push_back (end);
558 fade[In].points.sort (cmp);
560 for (list<Point*>::iterator i = fade[In].points.begin(); i != fade[In].points.end(); ++i) {
561 (*i)->move_to (x_coordinate((*i)->x), y_coordinate((*i)->y),
567 if (fade[Out].points.size() > 1) {
568 Point* old_end = fade[Out].points.back();
569 fade[Out].points.pop_back ();
570 end->move_to (x_coordinate (old_end->x),
571 y_coordinate (old_end->y),
572 old_end->x, old_end->y);
577 end->move_to (x_coordinate (x), y_coordinate (y), x, y);
581 fade[Out].points.push_back (end);
582 fade[Out].points.sort (cmp);
584 for (list<Point*>::iterator i = fade[Out].points.begin(); i != fade[Out].points.end(); ++i) {
585 (*i)->move_to (x_coordinate ((*i)->x),
586 y_coordinate ((*i)->y),
590 WhichFade old_current = current;
595 current = old_current;
597 double spu = xfade->length() / (double) effective_width();
599 if (fade[In].waves.empty()) {
600 make_waves (xfade->in(), In);
603 if (fade[Out].waves.empty()) {
604 make_waves (xfade->out(), Out);
608 vector<ArdourCanvas::WaveView*>::iterator i;
611 ht = canvas->get_allocation().get_height() / xfade->in()->n_channels();
613 for (n = 0, i = fade[In].waves.begin(); i != fade[In].waves.end(); ++i, ++n) {
618 (*i)->property_y() = yoff;
619 (*i)->property_height() = ht;
620 (*i)->property_samples_per_unit() = spu;
623 ht = canvas->get_allocation().get_height() / xfade->out()->n_channels();
625 for (n = 0, i = fade[Out].waves.begin(); i != fade[Out].waves.end(); ++i, ++n) {
630 (*i)->property_y() = yoff;
631 (*i)->property_height() = ht;
632 (*i)->property_samples_per_unit() = spu;
639 CrossfadeEditor::xfade_changed (const PropertyChange&)
641 set (xfade->fade_in(), In);
642 set (xfade->fade_out(), Out);
646 CrossfadeEditor::redraw ()
648 if (canvas->get_allocation().get_width() < 2) {
652 nframes_t len = xfade->length ();
654 fade[current].normative_curve.clear ();
655 fade[current].gain_curve.clear ();
657 for (list<Point*>::iterator i = fade[current].points.begin(); i != fade[current].points.end(); ++i) {
658 fade[current].normative_curve.add ((*i)->x, (*i)->y);
661 offset = xfade->in()->start();
663 offset = xfade->out()->start()+xfade->out()->length()-xfade->length();
664 fade[current].gain_curve.add (((*i)->x * len) + offset, (*i)->y);
668 size_t npoints = (size_t) effective_width();
671 fade[current].normative_curve.curve().get_vector (0, 1.0, vec, npoints);
673 ArdourCanvas::Points pts;
674 ArdourCanvas::Points spts;
676 while (pts.size() < npoints) {
677 pts.push_back (Gnome::Art::Point (0,0));
680 while (spts.size() < npoints + 3) {
681 spts.push_back (Gnome::Art::Point (0,0));
684 /* the shade coordinates *MUST* be in anti-clockwise order.
691 spts[0].set_x (canvas_border);
692 spts[0].set_y (effective_height() + canvas_border);
696 spts[1].set_x (effective_width() + canvas_border);
697 spts[1].set_y (effective_height() + canvas_border);
701 spts[2].set_x (effective_width() + canvas_border);
702 spts[2].set_y (canvas_border);
709 spts[0].set_x (canvas_border);
710 spts[0].set_y (canvas_border);
714 spts[1].set_x (canvas_border);
715 spts[1].set_y (effective_height() + canvas_border);
719 spts[2].set_x (effective_width() + canvas_border);
720 spts[2].set_y (effective_height() + canvas_border);
724 size_t last_spt = (npoints + 3) - 1;
726 for (size_t i = 0; i < npoints; ++i) {
730 pts[i].set_x (canvas_border + i);
731 pts[i].set_y (y_coordinate (y));
733 spts[last_spt - i].set_x (canvas_border + i);
734 spts[last_spt - i].set_y (pts[i].get_y());
737 fade[current].line->property_points() = pts;
738 fade[current].shading->property_points() = spts;
740 for (vector<ArdourCanvas::WaveView*>::iterator i = fade[current].waves.begin(); i != fade[current].waves.end(); ++i) {
741 (*i)->property_gain_src() = static_cast<Evoral::Curve*>(&fade[current].gain_curve.curve());
746 CrossfadeEditor::apply_preset (Preset *preset)
749 WhichFade wf = find(fade_in_presets->begin(), fade_in_presets->end(), preset) != fade_in_presets->end() ? In : Out;
754 select_in_button.clicked();
756 select_out_button.clicked();
759 curve_select_clicked (wf);
762 for (list<Point*>::iterator i = fade[current].points.begin(); i != fade[current].points.end(); ++i) {
766 fade[current].points.clear ();
768 for (Preset::iterator i = preset->begin(); i != preset->end(); ++i) {
769 Point* p = make_point ();
770 p->move_to (x_coordinate ((*i).x), y_coordinate ((*i).y),
772 fade[current].points.push_back (p);
779 CrossfadeEditor::apply ()
785 CrossfadeEditor::_apply_to (boost::shared_ptr<Crossfade> xf)
787 ARDOUR::AutomationList& in (xf->fade_in());
788 ARDOUR::AutomationList& out (xf->fade_out());
793 ARDOUR::AutomationList::const_iterator the_end = in.end();
796 double firstx = (*in.begin())->when;
797 double endx = (*the_end)->when;
798 double miny = in.get_min_y ();
799 double maxy = in.get_max_y ();
804 for (list<Point*>::iterator i = fade[In].points.begin(); i != fade[In].points.end(); ++i) {
806 double when = firstx + ((*i)->x * (endx - firstx));
807 double value = (*i)->y; // miny + ((*i)->y * (maxy - miny));
808 in.add (when, value);
816 firstx = (*out.begin())->when;
817 endx = (*the_end)->when;
818 miny = out.get_min_y ();
819 maxy = out.get_max_y ();
824 for (list<Point*>::iterator i = fade[Out].points.begin(); i != fade[Out].points.end(); ++i) {
826 double when = firstx + ((*i)->x * (endx - firstx));
827 double value = (*i)->y; // miny + ((*i)->y * (maxy - miny));
828 out.add (when, value);
836 CrossfadeEditor::setup (boost::shared_ptr<Crossfade> xfade)
839 xfade->set_active (true);
840 xfade->fade_in().curve().solve ();
841 xfade->fade_out().curve().solve ();
845 CrossfadeEditor::clear ()
847 for (list<Point*>::iterator i = fade[current].points.begin(); i != fade[current].points.end(); ++i) {
851 fade[current].points.clear ();
857 CrossfadeEditor::reset ()
859 set (xfade->fade_in(), In);
860 set (xfade->fade_out(), Out);
862 curve_select_clicked (current);
866 CrossfadeEditor::build_presets ()
870 fade_in_presets = new Presets;
871 fade_out_presets = new Presets;
875 p = new Preset ("Linear (-6dB)", "crossfade-in-linear");
876 p->push_back (PresetPoint (0, 0));
877 p->push_back (PresetPoint (0.000000, 0.000000));
878 p->push_back (PresetPoint (0.166667, 0.166366));
879 p->push_back (PresetPoint (0.333333, 0.332853));
880 p->push_back (PresetPoint (0.500000, 0.499459));
881 p->push_back (PresetPoint (0.666667, 0.666186));
882 p->push_back (PresetPoint (0.833333, 0.833033));
883 p->push_back (PresetPoint (1.000000, 1.000000));
884 fade_in_presets->push_back (p);
886 p = new Preset ("S(1)-curve", "crossfade-in-S1");
887 p->push_back (PresetPoint (0, 0));
888 p->push_back (PresetPoint (0.1, 0.01));
889 p->push_back (PresetPoint (0.2, 0.03));
890 p->push_back (PresetPoint (0.8, 0.97));
891 p->push_back (PresetPoint (0.9, 0.99));
892 p->push_back (PresetPoint (1, 1));
893 fade_in_presets->push_back (p);
895 p = new Preset ("S(2)-curve", "crossfade-in-S2");
896 p->push_back (PresetPoint (0.0, 0.0));
897 p->push_back (PresetPoint (0.055, 0.222));
898 p->push_back (PresetPoint (0.163, 0.35));
899 p->push_back (PresetPoint (0.837, 0.678));
900 p->push_back (PresetPoint (0.945, 0.783));
901 p->push_back (PresetPoint (1.0, 1.0));
902 fade_in_presets->push_back (p);
904 p = new Preset ("Constant Power (-3dB)", "crossfade-in-constant-power");
906 p->push_back (PresetPoint (0.000000, 0.000000));
907 p->push_back (PresetPoint (0.166667, 0.282192));
908 p->push_back (PresetPoint (0.333333, 0.518174));
909 p->push_back (PresetPoint (0.500000, 0.707946));
910 p->push_back (PresetPoint (0.666667, 0.851507));
911 p->push_back (PresetPoint (0.833333, 0.948859));
912 p->push_back (PresetPoint (1.000000, 1.000000));
914 fade_in_presets->push_back (p);
916 if (!Profile->get_sae()) {
918 p = new Preset ("Short cut", "crossfade-in-short-cut");
919 p->push_back (PresetPoint (0, 0));
920 p->push_back (PresetPoint (0.389401, 0.0333333));
921 p->push_back (PresetPoint (0.629032, 0.0861111));
922 p->push_back (PresetPoint (0.829493, 0.233333));
923 p->push_back (PresetPoint (0.9447, 0.483333));
924 p->push_back (PresetPoint (0.976959, 0.697222));
925 p->push_back (PresetPoint (1, 1));
926 fade_in_presets->push_back (p);
928 p = new Preset ("Slow cut", "crossfade-in-slow-cut");
929 p->push_back (PresetPoint (0, 0));
930 p->push_back (PresetPoint (0.304147, 0.0694444));
931 p->push_back (PresetPoint (0.529954, 0.152778));
932 p->push_back (PresetPoint (0.725806, 0.333333));
933 p->push_back (PresetPoint (0.847926, 0.558333));
934 p->push_back (PresetPoint (0.919355, 0.730556));
935 p->push_back (PresetPoint (1, 1));
936 fade_in_presets->push_back (p);
938 p = new Preset ("Fast cut", "crossfade-in-fast-cut");
939 p->push_back (PresetPoint (0, 0));
940 p->push_back (PresetPoint (0.0737327, 0.308333));
941 p->push_back (PresetPoint (0.246544, 0.658333));
942 p->push_back (PresetPoint (0.470046, 0.886111));
943 p->push_back (PresetPoint (0.652074, 0.972222));
944 p->push_back (PresetPoint (0.771889, 0.988889));
945 p->push_back (PresetPoint (1, 1));
946 fade_in_presets->push_back (p);
948 p = new Preset ("Long cut", "crossfade-in-long-cut");
949 p->push_back (PresetPoint (0, 0));
950 p->push_back (PresetPoint (0.0207373, 0.197222));
951 p->push_back (PresetPoint (0.0645161, 0.525));
952 p->push_back (PresetPoint (0.152074, 0.802778));
953 p->push_back (PresetPoint (0.276498, 0.919444));
954 p->push_back (PresetPoint (0.481567, 0.980556));
955 p->push_back (PresetPoint (0.767281, 1));
956 p->push_back (PresetPoint (1, 1));
957 fade_in_presets->push_back (p);
962 // p = new Preset ("regout.xpm");
963 p = new Preset ("Linear (-6dB cut)", "crossfade-out-linear");
964 p->push_back (PresetPoint (0, 1));
965 p->push_back (PresetPoint (0.000000, 1.000000));
966 p->push_back (PresetPoint (0.166667, 0.833033));
967 p->push_back (PresetPoint (0.333333, 0.666186));
968 p->push_back (PresetPoint (0.500000, 0.499459));
969 p->push_back (PresetPoint (0.666667, 0.332853));
970 p->push_back (PresetPoint (0.833333, 0.166366));
971 p->push_back (PresetPoint (1.000000, 0.000000));
972 fade_out_presets->push_back (p);
974 p = new Preset ("S(1)-Curve", "crossfade-out-S1");
975 p->push_back (PresetPoint (0, 1));
976 p->push_back (PresetPoint (0.1, 0.99));
977 p->push_back (PresetPoint (0.2, 0.97));
978 p->push_back (PresetPoint (0.8, 0.03));
979 p->push_back (PresetPoint (0.9, 0.01));
980 p->push_back (PresetPoint (1, 0));
981 fade_out_presets->push_back (p);
983 p = new Preset ("S(2)-Curve", "crossfade-out-S2");
984 p->push_back (PresetPoint (0.0, 1.0));
985 p->push_back (PresetPoint (0.163, 0.678));
986 p->push_back (PresetPoint (0.055, 0.783));
987 p->push_back (PresetPoint (0.837, 0.35));
988 p->push_back (PresetPoint (0.945, 0.222));
989 p->push_back (PresetPoint (1.0, 0.0));
990 fade_out_presets->push_back (p);
992 // p = new Preset ("linout.xpm");
993 p = new Preset ("Constant Power (-3dB cut)", "crossfade-out-constant-power");
994 p->push_back (PresetPoint (0.000000, 1.000000));
995 p->push_back (PresetPoint (0.166667, 0.948859));
996 p->push_back (PresetPoint (0.333333, 0.851507));
997 p->push_back (PresetPoint (0.500000, 0.707946));
998 p->push_back (PresetPoint (0.666667, 0.518174));
999 p->push_back (PresetPoint (0.833333, 0.282192));
1000 p->push_back (PresetPoint (1.000000, 0.000000));
1001 fade_out_presets->push_back (p);
1003 if (!Profile->get_sae()) {
1004 // p = new Preset ("hiout.xpm");
1005 p = new Preset ("Short cut", "crossfade-out-short-cut");
1006 p->push_back (PresetPoint (0, 1));
1007 p->push_back (PresetPoint (0.305556, 1));
1008 p->push_back (PresetPoint (0.548611, 0.991736));
1009 p->push_back (PresetPoint (0.759259, 0.931129));
1010 p->push_back (PresetPoint (0.918981, 0.68595));
1011 p->push_back (PresetPoint (0.976852, 0.22865));
1012 p->push_back (PresetPoint (1, 0));
1013 fade_out_presets->push_back (p);
1015 p = new Preset ("Slow cut", "crossfade-out-slow-cut");
1016 p->push_back (PresetPoint (0, 1));
1017 p->push_back (PresetPoint (0.228111, 0.988889));
1018 p->push_back (PresetPoint (0.347926, 0.972222));
1019 p->push_back (PresetPoint (0.529954, 0.886111));
1020 p->push_back (PresetPoint (0.753456, 0.658333));
1021 p->push_back (PresetPoint (0.9262673, 0.308333));
1022 p->push_back (PresetPoint (1, 0));
1023 fade_out_presets->push_back (p);
1025 p = new Preset ("Fast cut", "crossfade-out-fast-cut");
1026 p->push_back (PresetPoint (0, 1));
1027 p->push_back (PresetPoint (0.080645, 0.730556));
1028 p->push_back (PresetPoint (0.277778, 0.289256));
1029 p->push_back (PresetPoint (0.470046, 0.152778));
1030 p->push_back (PresetPoint (0.695853, 0.0694444));
1031 p->push_back (PresetPoint (1, 0));
1032 fade_out_presets->push_back (p);
1034 // p = new Preset ("loout.xpm");
1035 p = new Preset ("Long cut", "crossfade-out-long-cut");
1036 p->push_back (PresetPoint (0, 1));
1037 p->push_back (PresetPoint (0.023041, 0.697222));
1038 p->push_back (PresetPoint (0.0553, 0.483333));
1039 p->push_back (PresetPoint (0.170507, 0.233333));
1040 p->push_back (PresetPoint (0.370968, 0.0861111));
1041 p->push_back (PresetPoint (0.610599, 0.0333333));
1042 p->push_back (PresetPoint (1, 0));
1043 fade_out_presets->push_back (p);
1049 CrossfadeEditor::curve_select_clicked (WhichFade wf)
1055 for (vector<ArdourCanvas::WaveView*>::iterator i = fade[In].waves.begin(); i != fade[In].waves.end(); ++i) {
1056 (*i)->property_wave_color() = ARDOUR_UI::config()->canvasvar_SelectedCrossfadeEditorWave.get();
1057 (*i)->property_fill_color() = ARDOUR_UI::config()->canvasvar_SelectedCrossfadeEditorWave.get();
1060 for (vector<ArdourCanvas::WaveView*>::iterator i = fade[Out].waves.begin(); i != fade[Out].waves.end(); ++i) {
1061 (*i)->property_wave_color() = ARDOUR_UI::config()->canvasvar_CrossfadeEditorWave.get();
1062 (*i)->property_fill_color() = ARDOUR_UI::config()->canvasvar_CrossfadeEditorWave.get();
1065 fade[In].line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedCrossfadeEditorLine.get();
1066 fade[Out].line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_CrossfadeEditorLine.get();
1067 fade[Out].shading->hide();
1068 fade[In].shading->show();
1070 for (list<Point*>::iterator i = fade[Out].points.begin(); i != fade[Out].points.end(); ++i) {
1074 for (list<Point*>::iterator i = fade[In].points.begin(); i != fade[In].points.end(); ++i) {
1080 for (vector<ArdourCanvas::WaveView*>::iterator i = fade[In].waves.begin(); i != fade[In].waves.end(); ++i) {
1081 (*i)->property_wave_color() = ARDOUR_UI::config()->canvasvar_CrossfadeEditorWave.get();
1082 (*i)->property_fill_color() = ARDOUR_UI::config()->canvasvar_CrossfadeEditorWave.get();
1085 for (vector<ArdourCanvas::WaveView*>::iterator i = fade[Out].waves.begin(); i != fade[Out].waves.end(); ++i) {
1086 (*i)->property_wave_color() = ARDOUR_UI::config()->canvasvar_SelectedCrossfadeEditorWave.get();
1087 (*i)->property_fill_color() = ARDOUR_UI::config()->canvasvar_SelectedCrossfadeEditorWave.get();
1090 fade[Out].line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedCrossfadeEditorLine.get();
1091 fade[In].line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_CrossfadeEditorLine.get();
1092 fade[In].shading->hide();
1093 fade[Out].shading->show();
1095 for (list<Point*>::iterator i = fade[In].points.begin(); i != fade[In].points.end(); ++i) {
1099 for (list<Point*>::iterator i = fade[Out].points.begin(); i != fade[Out].points.end(); ++i) {
1107 CrossfadeEditor::x_coordinate (double& xfract) const
1109 xfract = min (1.0, xfract);
1110 xfract = max (0.0, xfract);
1112 return canvas_border + (xfract * effective_width());
1116 CrossfadeEditor::y_coordinate (double& yfract) const
1118 yfract = min (1.0, yfract);
1119 yfract = max (0.0, yfract);
1121 return (canvas->get_allocation().get_height() - (canvas_border)) - (yfract * effective_height());
1125 CrossfadeEditor::make_waves (boost::shared_ptr<AudioRegion> region, WhichFade which)
1128 uint32_t nchans = region->n_channels();
1133 color = ARDOUR_UI::config()->canvasvar_SelectedCrossfadeEditorWave.get();
1135 color = ARDOUR_UI::config()->canvasvar_CrossfadeEditorWave.get();
1138 ht = canvas->get_allocation().get_height() / (double) nchans;
1139 spu = xfade->length() / (double) effective_width();
1141 for (uint32_t n = 0; n < nchans; ++n) {
1143 gdouble yoff = n * ht;
1145 if (region->audio_source(n)->peaks_ready (boost::bind (&CrossfadeEditor::peaks_ready, this, boost::weak_ptr<AudioRegion>(region), which), peaks_ready_connection, gui_context())) {
1146 WaveView* waveview = new WaveView (*(canvas->root()));
1148 waveview->property_data_src() = region.get();
1149 waveview->property_cache_updater() = true;
1150 waveview->property_cache() = WaveView::create_cache();
1151 waveview->property_channel() = n;
1152 waveview->property_length_function() = (void*) region_length_from_c;
1153 waveview->property_sourcefile_length_function() = (void*) sourcefile_length_from_c;
1154 waveview->property_peak_function() = (void*) region_read_peaks_from_c;
1155 waveview->property_gain_function() = (void*) curve_get_vector_from_c;
1156 waveview->property_gain_src() = static_cast<Evoral::Curve*>(&fade[which].gain_curve.curve());
1157 waveview->property_x() = canvas_border;
1158 waveview->property_y() = yoff;
1159 waveview->property_height() = ht;
1160 waveview->property_samples_per_unit() = spu;
1161 waveview->property_amplitude_above_axis() = 2.0;
1162 waveview->property_wave_color() = color;
1163 waveview->property_fill_color() = color;
1166 waveview->property_region_start() = region->start();
1168 waveview->property_region_start() = region->start()+region->length()-xfade->length();
1170 waveview->lower_to_bottom();
1171 fade[which].waves.push_back (waveview);
1175 toplevel->lower_to_bottom();
1179 CrossfadeEditor::peaks_ready (boost::weak_ptr<AudioRegion> wr, WhichFade which)
1181 boost::shared_ptr<AudioRegion> r (wr.lock());
1187 /* this should never be called, because the peak files for an xfade
1188 will be ready by the time we want them. but our API forces us
1189 to provide this, so ..
1191 peaks_ready_connection.disconnect ();
1192 make_waves (r, which);
1196 CrossfadeEditor::audition (Audition which)
1198 AudioPlaylist& pl (_session->the_auditioner()->prepare_playlist());
1201 nframes_t left_start_offset;
1202 nframes_t right_length;
1203 nframes_t left_length;
1205 if (which != Right && preroll_button.get_active()) {
1206 preroll = _session->frame_rate() * 2; //2 second hardcoded preroll for now
1211 if (which != Left && postroll_button.get_active()) {
1212 postroll = _session->frame_rate() * 2; //2 second hardcoded postroll for now
1217 // Is there enough data for the whole preroll?
1218 left_length = xfade->length();
1219 if ((left_start_offset = xfade->out()->length() - xfade->length()) > preroll) {
1220 left_start_offset -= preroll;
1222 preroll = left_start_offset;
1223 left_start_offset = 0;
1225 left_length += preroll;
1227 // Is there enough data for the whole postroll?
1228 right_length = xfade->length();
1229 if ((xfade->in()->length() - right_length) > postroll) {
1230 right_length += postroll;
1232 right_length = xfade->in()->length();
1235 PropertyList left_plist;
1236 PropertyList right_plist;
1239 left_plist.add (ARDOUR::Properties::start, left_start_offset);
1240 left_plist.add (ARDOUR::Properties::length, left_length);
1241 left_plist.add (ARDOUR::Properties::name, string ("xfade out"));
1242 left_plist.add (ARDOUR::Properties::layer, 0);
1243 left_plist.add (ARDOUR::Properties::fade_in_active, true);
1245 right_plist.add (ARDOUR::Properties::start, 0);
1246 right_plist.add (ARDOUR::Properties::length, right_length);
1247 right_plist.add (ARDOUR::Properties::name, string("xfade in"));
1248 right_plist.add (ARDOUR::Properties::layer, 0);
1249 right_plist.add (ARDOUR::Properties::fade_out_active, true);
1251 if (which == Left) {
1252 right_plist.add (ARDOUR::Properties::scale_amplitude, 0.0f);
1253 } else if (which == Right) {
1254 left_plist.add (ARDOUR::Properties::scale_amplitude, 0.0f);
1257 boost::shared_ptr<AudioRegion> left (boost::dynamic_pointer_cast<AudioRegion>
1258 (RegionFactory::create (xfade->out(), left_plist, false)));
1259 boost::shared_ptr<AudioRegion> right (boost::dynamic_pointer_cast<AudioRegion>
1260 (RegionFactory::create (xfade->in(), right_plist, false)));
1262 // apply a 20ms declicking fade at the start and end of auditioning
1263 // XXX this should really be a property
1265 left->set_fade_in_length (_session->frame_rate() / 50);
1266 right->set_fade_out_length (_session->frame_rate() / 50);
1268 pl.add_region (left, 0);
1269 pl.add_region (right, 1 + preroll);
1271 /* there is only one ... */
1272 pl.foreach_crossfade (sigc::mem_fun (*this, &CrossfadeEditor::setup));
1274 _session->audition_playlist ();
1278 CrossfadeEditor::audition_both ()
1284 CrossfadeEditor::audition_left_dry ()
1288 plist.add (ARDOUR::Properties::start, xfade->out()->length() - xfade->length());
1289 plist.add (ARDOUR::Properties::length, xfade->length());
1290 plist.add (ARDOUR::Properties::name, string("xfade left"));
1291 plist.add (ARDOUR::Properties::layer, 0);
1293 boost::shared_ptr<AudioRegion> left (boost::dynamic_pointer_cast<AudioRegion>
1294 (RegionFactory::create (xfade->out(), plist, false)));
1296 _session->audition_region (left);
1300 CrossfadeEditor::audition_left ()
1306 CrossfadeEditor::audition_right_dry ()
1310 plist.add (ARDOUR::Properties::start, 0);
1311 plist.add (ARDOUR::Properties::length, xfade->length());
1312 plist.add (ARDOUR::Properties::name, string ("xfade right"));
1313 plist.add (ARDOUR::Properties::layer, 0);
1315 boost::shared_ptr<AudioRegion> right (boost::dynamic_pointer_cast<AudioRegion>
1316 (RegionFactory::create (xfade->in(), plist, false)));
1318 _session->audition_region (right);
1322 CrossfadeEditor::audition_right ()
1328 CrossfadeEditor::cancel_audition ()
1330 _session->cancel_audition ();
1334 CrossfadeEditor::audition_toggled ()
1338 if ((x = audition_both_button.get_active ()) != _session->is_auditioning()) {
1349 CrossfadeEditor::audition_right_toggled ()
1353 if ((x = audition_right_button.get_active ()) != _session->is_auditioning()) {
1364 CrossfadeEditor::audition_right_dry_toggled ()
1368 if ((x = audition_right_dry_button.get_active ()) != _session->is_auditioning()) {
1371 audition_right_dry ();
1379 CrossfadeEditor::audition_left_toggled ()
1383 if ((x = audition_left_button.get_active ()) != _session->is_auditioning()) {
1394 CrossfadeEditor::audition_left_dry_toggled ()
1398 if ((x = audition_left_dry_button.get_active ()) != _session->is_auditioning()) {
1401 audition_left_dry ();
1409 CrossfadeEditor::on_key_press_event (GdkEventKey */*ev*/)
1415 CrossfadeEditor::on_key_release_event (GdkEventKey* ev)
1417 switch (ev->keyval) {
1419 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1420 audition_right_dry_button.set_active (!audition_right_dry_button.get_active());
1422 audition_right_button.set_active (!audition_right_button.get_active());
1427 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1428 audition_left_dry_button.set_active (!audition_left_dry_button.get_active());
1430 audition_left_button.set_active (!audition_left_button.get_active());
1435 if (_session->is_auditioning()) {
1438 audition_both_button.set_active (!audition_both_button.get_active());