Initial Gain Coefficient tweaks
[ardour.git] / gtk2_ardour / crossfade_edit.cc
1 /*
2     Copyright (C) 2004 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <cmath>
21
22 #include <sigc++/bind.h>
23
24 #include <gtkmm/frame.h>
25 #include <gtkmm/image.h>
26 #include <gtkmm/scrolledwindow.h>
27
28
29 #include "pbd/memento_command.h"
30 #include "ardour/automation_list.h"
31 #include "evoral/Curve.hpp"
32 #include "ardour/crossfade.h"
33 #include "ardour/dB.h"
34 #include "ardour/session.h"
35 #include "ardour/auditioner.h"
36 #include "ardour/audioplaylist.h"
37 #include "ardour/audiosource.h"
38 #include "ardour/region_factory.h"
39 #include "ardour/profile.h"
40 #include "ardour/crossfade_binder.h"
41
42 #include <gtkmm2ext/gtk_ui.h>
43
44 #include "canvas/rectangle.h"
45 #include "canvas/wave_view.h"
46 #include "canvas/line.h"
47 #include "canvas/polygon.h"
48
49 #include "ardour_ui.h"
50 #include "crossfade_edit.h"
51 #include "rgb_macros.h"
52 #include "keyboard.h"
53 #include "gui_thread.h"
54 #include "actions.h"
55
56 using namespace std;
57 using namespace ARDOUR;
58 using namespace PBD;
59 using namespace Gtk;
60 using namespace Editing;
61
62 using Gtkmm2ext::Keyboard;
63
64 #include "i18n.h"
65
66 const int32_t CrossfadeEditor::Point::size = 7;
67 const double CrossfadeEditor::canvas_border = 10;
68 CrossfadeEditor::Presets* CrossfadeEditor::fade_in_presets = 0;
69 CrossfadeEditor::Presets* CrossfadeEditor::fade_out_presets = 0;
70
71 CrossfadeEditor::Half::Half ()
72         : line (0)
73         , normative_curve (Evoral::Parameter(GainAutomation))
74         , gain_curve (Evoral::Parameter(GainAutomation))
75 {
76 }
77
78 CrossfadeEditor::CrossfadeEditor (Session* s, boost::shared_ptr<Crossfade> xf, double my, double mxy)
79         : ArdourDialog (_("Edit Crossfade")),
80           xfade (xf),
81           clear_button (_("Clear")),
82           revert_button (_("Reset")),
83           audition_both_button (_("Fade")),
84           audition_left_dry_button (_("Out (dry)")),
85           audition_left_button (_("Out")),
86           audition_right_dry_button (_("In (dry)")),
87           audition_right_button (_("In")),
88
89           preroll_button (_("With Pre-roll")),
90           postroll_button (_("With Post-roll")),
91
92           miny (my),
93           maxy (mxy),
94
95           fade_in_table (3, 3),
96           fade_out_table (3, 3),
97
98           select_in_button (_("Fade In")),
99           select_out_button (_("Fade Out")),
100
101           _peaks_ready_connection (0)
102
103 {
104         set_session (s);
105
106         set_wmclass (X_("ardour_automationedit"), PROGRAM_NAME);
107         set_name ("CrossfadeEditWindow");
108
109         add_accel_group (ActionManager::ui_manager->get_accel_group());
110
111         add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
112
113         RadioButtonGroup sel_but_group = select_in_button.get_group();
114         select_out_button.set_group (sel_but_group);
115         select_out_button.set_mode (false);
116         select_in_button.set_mode (false);
117
118         get_action_area()->set_layout(BUTTONBOX_SPREAD);
119         get_action_area()->pack_start(clear_button);
120         get_action_area()->pack_start(revert_button);
121         cancel_button = add_button ("Cancel", RESPONSE_CANCEL);
122         ok_button = add_button ("OK", RESPONSE_ACCEPT);
123
124         if (fade_in_presets == 0) {
125                 build_presets ();
126         }
127
128         point_grabbed = false;
129         toplevel = 0;
130
131         canvas = new ArdourCanvas::GtkCanvas ();
132         canvas->signal_size_allocate().connect (sigc::mem_fun(*this, &CrossfadeEditor::canvas_allocation));
133         canvas->set_size_request (425, 200);
134
135         toplevel = new ArdourCanvas::Rectangle (canvas->root());
136         toplevel->set (ArdourCanvas::Rect (0, 0, 10, 10));
137         toplevel->set_fill (true);
138         toplevel->set_fill_color (ARDOUR_UI::config()->get_CrossfadeEditorBase());
139         toplevel->set_outline (false);
140         toplevel->Event.connect (sigc::mem_fun (*this, &CrossfadeEditor::canvas_event));
141
142         fade[Out].line = new ArdourCanvas::PolyLine (canvas->root());
143         fade[Out].line->set_outline_color (ARDOUR_UI::config()->get_CrossfadeEditorLine());
144
145         fade[Out].shading = new ArdourCanvas::Polygon (canvas->root());
146         fade[Out].shading->set_fill_color (ARDOUR_UI::config()->get_CrossfadeEditorLineShading());
147
148         fade[In].line = new ArdourCanvas::PolyLine (canvas->root());
149         fade[In].line->set_outline_color (ARDOUR_UI::config()->get_CrossfadeEditorLine());
150
151         fade[In].shading = new ArdourCanvas::Polygon (canvas->root());
152         fade[In].shading->set_fill_color (ARDOUR_UI::config()->get_CrossfadeEditorLineShading());
153
154         fade[In].shading->Event.connect (sigc::mem_fun (*this, &CrossfadeEditor::canvas_event));
155         fade[In].line->Event.connect (sigc::mem_fun (*this, &CrossfadeEditor::curve_event));
156         fade[Out].shading->Event.connect (sigc::mem_fun (*this, &CrossfadeEditor::canvas_event));
157         fade[Out].line->Event.connect (sigc::mem_fun (*this, &CrossfadeEditor::curve_event));
158
159         select_in_button.set_name (X_("CrossfadeEditCurveButton"));
160         select_out_button.set_name (X_("CrossfadeEditCurveButton"));
161
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));
164
165         HBox* acbox = manage (new HBox);
166
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);
175
176         Frame* audition_frame = manage (new Frame (_("Audition")));
177
178         audition_frame->set_name (X_("CrossfadeEditFrame"));
179         audition_frame->add (audition_box);
180
181         acbox->pack_start (*audition_frame, true, false);
182
183         Frame* canvas_frame = manage (new Frame);
184         canvas_frame->add (*canvas);
185         canvas_frame->set_shadow_type (Gtk::SHADOW_IN);
186
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);
189
190         Image *pxmap;
191         Button* pbutton;
192         int row;
193         int col;
194
195         row = 1;
196         col = 0;
197
198         for (list<Preset*>::iterator i = fade_in_presets->begin(); i != fade_in_presets->end(); ++i) {
199
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);
208
209                 col++;
210
211                 if (col == 2) {
212                         col = 0;
213                         row++;
214                 }
215         }
216
217         row = 1;
218         col = 0;
219
220         for (list<Preset*>::iterator i = fade_out_presets->begin(); i != fade_out_presets->end(); ++i) {
221
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);
230
231                 col++;
232
233                 if (col == 2) {
234                         col = 0;
235                         row++;
236                 }
237         }
238
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");
250
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));
258
259         roll_box.pack_start (preroll_button, false, false);
260         roll_box.pack_start (postroll_button, false, false);
261
262         Gtk::HBox* rcenter_box = manage (new HBox);
263         rcenter_box->pack_start (roll_box, true, false);
264
265         VBox* vpacker2 = manage (new (VBox));
266
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);
271
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);
276
277         get_vbox()->pack_start (*canvas_frame, true, true);
278         get_vbox()->pack_start (curve_button_box, false, false);
279
280         /* button to allow hackers to check the actual curve values */
281
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);
285
286         current = In;
287         set (xfade->fade_in(), In);
288
289         current = Out;
290         set (xfade->fade_out(), Out);
291
292         curve_select_clicked (In);
293
294         xfade->PropertyChanged.connect (state_connection, invalidator (*this), boost::bind (&CrossfadeEditor::xfade_changed, this, _1), gui_context());
295
296         _session->AuditionActive.connect (_session_connections, invalidator (*this), boost::bind (&CrossfadeEditor::audition_state_changed, this, _1), gui_context());
297         show_all_children();
298 }
299
300 CrossfadeEditor::~CrossfadeEditor()
301 {
302         /* most objects will be destroyed when the toplevel window is. */
303
304         for (list<Point*>::iterator i = fade[In].points.begin(); i != fade[In].points.end(); ++i) {
305                 delete *i;
306         }
307
308         for (list<Point*>::iterator i = fade[Out].points.begin(); i != fade[Out].points.end(); ++i) {
309                 delete *i;
310         }
311
312         delete _peaks_ready_connection;
313 }
314
315 void
316 CrossfadeEditor::dump ()
317 {
318         for (AutomationList::iterator i = fade[Out].normative_curve.begin(); i != fade[Out].normative_curve.end(); ++i) {
319                 cerr << (*i)->when << ' ' << (*i)->value << endl;
320         }
321 }
322
323 void
324 CrossfadeEditor::audition_state_changed (bool yn)
325 {
326         ENSURE_GUI_THREAD (*this, &CrossfadeEditor::audition_state_changed, yn)
327
328         if (!yn) {
329                 audition_both_button.set_active (false);
330                 audition_left_button.set_active (false);
331                 audition_right_button.set_active (false);
332                 audition_left_dry_button.set_active (false);
333                 audition_right_dry_button.set_active (false);
334         }
335 }
336
337 void
338 CrossfadeEditor::set (const ARDOUR::AutomationList& curve, WhichFade which)
339 {
340         double firstx, endx;
341         ARDOUR::AutomationList::const_iterator the_end;
342
343         for (list<Point*>::iterator i = fade[which].points.begin(); i != fade[which].points.end(); ++i) {
344                         delete *i;
345         }
346
347         fade[which].points.clear ();
348         fade[which].gain_curve.clear ();
349         fade[which].normative_curve.clear ();
350
351         if (curve.empty()) {
352                 goto out;
353         }
354
355         the_end = curve.end();
356         --the_end;
357
358         firstx = (*curve.begin())->when;
359         endx = (*the_end)->when;
360
361         for (ARDOUR::AutomationList::const_iterator i = curve.begin(); i != curve.end(); ++i) {
362
363                 double xfract = ((*i)->when - firstx) / (endx - firstx);
364                 double yfract = ((*i)->value - miny) / (maxy - miny);
365
366                 Point* p = make_point ();
367
368                 p->move_to (x_coordinate (xfract), y_coordinate (yfract),
369                             xfract, yfract);
370
371                 fade[which].points.push_back (p);
372         }
373
374         /* no need to sort because curve is already time-ordered */
375
376   out:
377
378         swap (which, current);
379         redraw ();
380         swap (which, current);
381 }
382
383 bool
384 CrossfadeEditor::curve_event (GdkEvent* event)
385 {
386         /* treat it like a toplevel event */
387
388         return canvas_event (event);
389 }
390
391 bool
392 CrossfadeEditor::point_event (GdkEvent* event, Point* point)
393 {
394
395         if (point->curve != fade[current].line) {
396                 return FALSE;
397         }
398
399         switch (event->type) {
400         case GDK_BUTTON_PRESS:
401                 point_grabbed = true;
402                 break;
403         case GDK_BUTTON_RELEASE:
404                 point_grabbed = false;
405
406                 if (Keyboard::is_delete_event (&event->button)) {
407                         fade[current].points.remove (point);
408                         delete point;
409                 }
410
411                 redraw ();
412                 break;
413
414         case GDK_MOTION_NOTIFY:
415                 if (point_grabbed) {
416                         double new_x, new_y;
417
418                         /* can't drag first or last points horizontally or vertically */
419
420                         if (point == fade[current].points.front() || point == fade[current].points.back()) {
421                                 new_x = point->x;
422                                 new_y = point->y;
423                         } else {
424                                 new_x = (event->motion.x - canvas_border)/effective_width();
425                                 new_y = 1.0 - ((event->motion.y - canvas_border)/effective_height());
426                         }
427
428                         point->move_to (x_coordinate (new_x), y_coordinate (new_y),
429                                         new_x, new_y);
430                         redraw ();
431                 }
432                 break;
433         default:
434                 break;
435         }
436         return TRUE;
437 }
438
439 bool
440 CrossfadeEditor::canvas_event (GdkEvent* event)
441 {
442         switch (event->type) {
443         case GDK_BUTTON_PRESS:
444                 add_control_point ((event->button.x - canvas_border)/effective_width(),
445                                    1.0 - ((event->button.y - canvas_border)/effective_height()));
446                 return true;
447                 break;
448         default:
449                 break;
450         }
451         return false;
452 }
453
454 CrossfadeEditor::Point::~Point()
455 {
456         delete box;
457 }
458
459 CrossfadeEditor::Point*
460 CrossfadeEditor::make_point ()
461 {
462         Point* p = new Point;
463
464         p->box = new ArdourCanvas::Rectangle (canvas->root());
465         p->box->set_fill (true);
466         p->box->set_fill_color (ARDOUR_UI::config()->get_CrossfadeEditorPointFill());
467         p->box->set_outline_color (ARDOUR_UI::config()->get_CrossfadeEditorPointOutline());
468
469         p->curve = fade[current].line;
470
471         p->box->Event.connect (sigc::bind (sigc::mem_fun (*this, &CrossfadeEditor::point_event), p));
472
473         return p;
474 }
475
476 void
477 CrossfadeEditor::add_control_point (double x, double y)
478 {
479         PointSorter cmp;
480
481         /* enforce end point x location */
482
483         if (fade[current].points.empty()) {
484                 x = 0.0;
485         } else if (fade[current].points.size() == 1) {
486                 x = 1.0;
487         }
488
489         Point* p = make_point ();
490
491         p->move_to (x_coordinate (x), y_coordinate (y), x, y);
492
493         fade[current].points.push_back (p);
494         fade[current].points.sort (cmp);
495
496         redraw ();
497 }
498
499 void
500 CrossfadeEditor::Point::move_to (double nx, double ny, double xfract, double yfract)
501 {
502         if ( xfract < 0.0 ) {
503                 xfract = 0.0;
504         } else if ( xfract > 1.0 ) {
505                 xfract = 1.0;
506         }
507
508         if ( yfract < 0.0 ) {
509                 yfract = 0.0;
510         } else if ( yfract > 1.0 ) {
511                 yfract = 1.0;
512         }
513
514         const double half_size = rint(size/2.0);
515         double x1 = nx - half_size;
516         double x2 = nx + half_size;
517
518         box->set (ArdourCanvas::Rect (x1, ny - half_size, x2, ny + half_size));
519
520         x = xfract;
521         y = yfract;
522 }
523
524 void
525 CrossfadeEditor::canvas_allocation (Gtk::Allocation& /*alloc*/)
526 {
527         if (toplevel) {
528                 toplevel->set (
529                         ArdourCanvas::Rect (
530                                 0,
531                                 0,
532                                 canvas->get_allocation().get_width() + canvas_border,
533                                 canvas->get_allocation().get_height() + canvas_border
534                                 )
535                         );
536         }
537
538         /* XXX: CANVAS */
539 //      canvas->set_scroll_region (0.0, 0.0,
540 //                                 canvas->get_allocation().get_width(),
541 //                                 canvas->get_allocation().get_height());
542
543         Point* end = make_point ();
544         PointSorter cmp;
545
546         if (fade[In].points.size() > 1) {
547                 Point* old_end = fade[In].points.back();
548                 fade[In].points.pop_back ();
549                 end->move_to (x_coordinate (old_end->x),
550                               y_coordinate (old_end->y),
551                               old_end->x, old_end->y);
552                 delete old_end;
553         } else {
554                 double x = 1.0;
555                 double y = 0.5;
556                 end->move_to (x_coordinate (x), y_coordinate (y), x, y);
557
558         }
559
560         fade[In].points.push_back (end);
561         fade[In].points.sort (cmp);
562
563         for (list<Point*>::iterator i = fade[In].points.begin(); i != fade[In].points.end(); ++i) {
564                 (*i)->move_to (x_coordinate((*i)->x), y_coordinate((*i)->y),
565                                (*i)->x, (*i)->y);
566         }
567
568         end = make_point ();
569
570         if (fade[Out].points.size() > 1) {
571                 Point* old_end = fade[Out].points.back();
572                 fade[Out].points.pop_back ();
573                 end->move_to (x_coordinate (old_end->x),
574                               y_coordinate (old_end->y),
575                               old_end->x, old_end->y);
576                 delete old_end;
577         } else {
578                 double x = 1.0;
579                 double y = 0.5;
580                 end->move_to (x_coordinate (x), y_coordinate (y), x, y);
581
582         }
583
584         fade[Out].points.push_back (end);
585         fade[Out].points.sort (cmp);
586
587         for (list<Point*>::iterator i = fade[Out].points.begin(); i != fade[Out].points.end(); ++i) {
588                 (*i)->move_to (x_coordinate ((*i)->x),
589                                y_coordinate ((*i)->y),
590                                (*i)->x, (*i)->y);
591         }
592
593         WhichFade old_current = current;
594         current = In;
595         redraw ();
596         current = Out;
597         redraw ();
598         current = old_current;
599
600         double spu = xfade->length() / (double) effective_width();
601
602         if (fade[In].waves.empty()) {
603                 make_waves (xfade->in(), In);
604         }
605
606         if (fade[Out].waves.empty()) {
607                 make_waves (xfade->out(), Out);
608         }
609
610         double ht;
611         vector<ArdourCanvas::WaveView*>::iterator i;
612         uint32_t n;
613
614         ht = canvas->get_allocation().get_height() / xfade->in()->n_channels();
615
616         for (n = 0, i = fade[In].waves.begin(); i != fade[In].waves.end(); ++i, ++n) {
617                 double yoff;
618
619                 yoff = n * ht;
620
621                 (*i)->set_y_position (yoff);
622                 (*i)->set_height (ht);
623                 (*i)->set_samples_per_pixel (spu);
624         }
625
626         ht = canvas->get_allocation().get_height() / xfade->out()->n_channels();
627
628         for (n = 0, i = fade[Out].waves.begin(); i != fade[Out].waves.end(); ++i, ++n) {
629                 double yoff;
630
631                 yoff = n * ht;
632
633                 (*i)->set_y_position (yoff);
634                 (*i)->set_height (ht);
635                 (*i)->set_samples_per_pixel (spu);
636         }
637
638 }
639
640
641 void
642 CrossfadeEditor::xfade_changed (const PropertyChange&)
643 {
644         set (xfade->fade_in(), In);
645         set (xfade->fade_out(), Out);
646 }
647
648 void
649 CrossfadeEditor::redraw ()
650 {
651         if (canvas->get_allocation().get_width() < 2) {
652                 return;
653         }
654
655         framecnt_t len = xfade->length ();
656
657         fade[current].normative_curve.clear ();
658         fade[current].gain_curve.clear ();
659
660         for (list<Point*>::iterator i = fade[current].points.begin(); i != fade[current].points.end(); ++i) {
661                 fade[current].normative_curve.add ((*i)->x, (*i)->y);
662                 double offset;
663                 if (current==In)
664                         offset = xfade->in()->start();
665                 else
666                         offset = xfade->out()->start()+xfade->out()->length()-xfade->length();
667                 fade[current].gain_curve.add (((*i)->x * len) + offset, (*i)->y);
668         }
669
670
671         size_t npoints = (size_t) effective_width();
672         float vec[npoints];
673
674         fade[current].normative_curve.curve().get_vector (0, 1.0, vec, npoints);
675
676         ArdourCanvas::Points pts;
677         ArdourCanvas::Points spts;
678
679         while (pts.size() < npoints) {
680                 pts.push_back (ArdourCanvas::Duple (0,0));
681         }
682
683         while (spts.size() < npoints + 3) {
684                 spts.push_back (ArdourCanvas::Duple (0,0));
685         }
686
687         /* the shade coordinates *MUST* be in anti-clockwise order.
688          */
689
690         if (current == In) {
691
692                 /* lower left */
693
694                 spts[0].x = canvas_border;
695                 spts[0].y = effective_height() + canvas_border;
696
697                 /* lower right */
698
699                 spts[1].x = effective_width() + canvas_border;
700                 spts[1].y = effective_height() + canvas_border;
701
702                 /* upper right */
703
704                 spts[2].x = effective_width() + canvas_border;
705                 spts[2].y = canvas_border;
706
707
708         } else {
709
710                 /*  upper left */
711
712                 spts[0].x = canvas_border;
713                 spts[0].y = canvas_border;
714
715                 /* lower left */
716
717                 spts[1].x = canvas_border;
718                 spts[1].y = effective_height() + canvas_border;
719
720                 /* lower right */
721
722                 spts[2].x = effective_width() + canvas_border;
723                 spts[2].y = effective_height() + canvas_border;
724
725         }
726
727         size_t last_spt = (npoints + 3) - 1;
728
729         for (size_t i = 0; i < npoints; ++i) {
730
731                 double y = vec[i];
732
733                 pts[i].x = canvas_border + i;
734                 pts[i].y = y_coordinate (y);
735
736                 spts[last_spt - i].x = canvas_border + i;
737                 spts[last_spt - i].y = pts[i].y;
738         }
739
740         fade[current].line->set (pts);
741         fade[current].shading->set (pts);
742
743         for (vector<ArdourCanvas::WaveView*>::iterator i = fade[current].waves.begin(); i != fade[current].waves.end(); ++i) {
744                 (*i)->property_gain_src() = static_cast<Evoral::Curve*>(&fade[current].gain_curve.curve());
745         }
746 }
747
748 void
749 CrossfadeEditor::apply_preset (Preset *preset)
750 {
751
752         WhichFade wf =  find(fade_in_presets->begin(), fade_in_presets->end(), preset) != fade_in_presets->end() ? In : Out;
753
754         if (current != wf) {
755
756                 if (wf == In) {
757                         select_in_button.clicked();
758                 } else {
759                         select_out_button.clicked();
760                 }
761
762                 curve_select_clicked (wf);
763         }
764
765         for (list<Point*>::iterator i = fade[current].points.begin(); i != fade[current].points.end(); ++i) {
766                 delete *i;
767         }
768
769         fade[current].points.clear ();
770
771         for (Preset::iterator i = preset->begin(); i != preset->end(); ++i) {
772                 Point* p = make_point ();
773                 p->move_to (x_coordinate ((*i).x), y_coordinate ((*i).y),
774                             (*i).x, (*i).y);
775                 fade[current].points.push_back (p);
776         }
777
778         redraw ();
779 }
780
781 void
782 CrossfadeEditor::apply ()
783 {
784         the_editor().begin_reversible_command (_("Edit crossfade"));
785
786         XMLNode& before = xfade->get_state ();
787
788         _apply_to (xfade);
789
790         _session->add_command (
791                 new MementoCommand<Crossfade> (
792                         new ARDOUR::CrossfadeBinder (_session->playlists, xfade->id ()),
793                         &before, &xfade->get_state ()
794                         )
795                 );
796
797         the_editor().commit_reversible_command ();
798 }
799
800 void
801 CrossfadeEditor::_apply_to (boost::shared_ptr<Crossfade> xf)
802 {
803         ARDOUR::AutomationList& in (xf->fade_in());
804         ARDOUR::AutomationList& out (xf->fade_out());
805
806         /* IN */
807
808
809         ARDOUR::AutomationList::const_iterator the_end = in.end();
810         --the_end;
811
812         double firstx = (*in.begin())->when;
813         double endx = (*the_end)->when;
814
815         in.freeze ();
816         in.clear ();
817
818         for (list<Point*>::iterator i = fade[In].points.begin(); i != fade[In].points.end(); ++i) {
819
820                 double when = firstx + ((*i)->x * (endx - firstx));
821                 double value = (*i)->y;
822                 in.add (when, value);
823         }
824
825         /* OUT */
826
827         the_end = out.end();
828         --the_end;
829
830         firstx = (*out.begin())->when;
831         endx = (*the_end)->when;
832
833         out.freeze ();
834         out.clear ();
835
836         for (list<Point*>::iterator i = fade[Out].points.begin(); i != fade[Out].points.end(); ++i) {
837
838                 double when = firstx + ((*i)->x * (endx - firstx));
839                 double value = (*i)->y;
840                 out.add (when, value);
841         }
842
843         in.thaw ();
844         out.thaw ();
845 }
846
847 void
848 CrossfadeEditor::setup (boost::shared_ptr<Crossfade> xfade)
849 {
850         _apply_to (xfade);
851         xfade->set_active (true);
852         xfade->fade_in().curve().solve ();
853         xfade->fade_out().curve().solve ();
854 }
855
856 void
857 CrossfadeEditor::clear ()
858 {
859         for (list<Point*>::iterator i = fade[current].points.begin(); i != fade[current].points.end(); ++i) {
860                 delete *i;
861         }
862
863         fade[current].points.clear ();
864
865         redraw ();
866 }
867
868 void
869 CrossfadeEditor::reset ()
870 {
871         set (xfade->fade_in(),  In);
872         set (xfade->fade_out(), Out);
873
874         curve_select_clicked (current);
875 }
876
877 void
878 CrossfadeEditor::build_presets ()
879 {
880         Preset* p;
881
882         fade_in_presets = new Presets;
883         fade_out_presets = new Presets;
884
885         /* FADE IN */
886
887         p = new Preset ("Linear (-6dB)", "fadein-linear");
888         p->push_back (PresetPoint (0.000000, GAIN_COEFF_SMALL));
889         p->push_back (PresetPoint (0.166667, 0.166366));
890         p->push_back (PresetPoint (0.333333, 0.332853));
891         p->push_back (PresetPoint (0.500000, 0.499459));
892         p->push_back (PresetPoint (0.666667, 0.666186));
893         p->push_back (PresetPoint (0.833333, 0.833033));
894         p->push_back (PresetPoint (1.000000, GAIN_COEFF_UNITY));
895         fade_in_presets->push_back (p);
896
897         p = new Preset ("S(1)-curve", "fadein-S1");
898         p->push_back (PresetPoint (0, GAIN_COEFF_SMALL));
899         p->push_back (PresetPoint (0.1, 0.01));
900         p->push_back (PresetPoint (0.2, 0.03));
901         p->push_back (PresetPoint (0.8, 0.97));
902         p->push_back (PresetPoint (0.9, 0.99));
903         p->push_back (PresetPoint (1, GAIN_COEFF_UNITY));
904         fade_in_presets->push_back (p);
905
906         p = new Preset ("S(2)-curve", "fadein-S2");
907         p->push_back (PresetPoint (0.0, GAIN_COEFF_SMALL));
908         p->push_back (PresetPoint (0.055, 0.222));
909         p->push_back (PresetPoint (0.163, 0.35));
910         p->push_back (PresetPoint (0.837, 0.678));
911         p->push_back (PresetPoint (0.945, 0.783));
912         p->push_back (PresetPoint (1.0, GAIN_COEFF_UNITY));
913         fade_in_presets->push_back (p);
914
915         p = new Preset ("Constant power (-3dB)", "fadein-constant-power");
916
917         p->push_back (PresetPoint (0.000000, GAIN_COEFF_SMALL));
918         p->push_back (PresetPoint (0.166667, 0.282192));
919         p->push_back (PresetPoint (0.333333, 0.518174));
920         p->push_back (PresetPoint (0.500000, 0.707946));
921         p->push_back (PresetPoint (0.666667, 0.851507));
922         p->push_back (PresetPoint (0.833333, 0.948859));
923         p->push_back (PresetPoint (1.000000, GAIN_COEFF_UNITY));
924
925         fade_in_presets->push_back (p);
926
927         if (!Profile->get_sae()) {
928
929                 p = new Preset ("Short cut", "fadein-short-cut");
930                 p->push_back (PresetPoint (0, GAIN_COEFF_SMALL));
931                 p->push_back (PresetPoint (0.389401, 0.0333333));
932                 p->push_back (PresetPoint (0.629032, 0.0861111));
933                 p->push_back (PresetPoint (0.829493, 0.233333));
934                 p->push_back (PresetPoint (0.9447, 0.483333));
935                 p->push_back (PresetPoint (0.976959, 0.697222));
936                 p->push_back (PresetPoint (1, GAIN_COEFF_UNITY));
937                 fade_in_presets->push_back (p);
938
939                 p = new Preset ("Slow cut", "fadein-slow-cut");
940                 p->push_back (PresetPoint (0, GAIN_COEFF_SMALL));
941                 p->push_back (PresetPoint (0.304147, 0.0694444));
942                 p->push_back (PresetPoint (0.529954, 0.152778));
943                 p->push_back (PresetPoint (0.725806, 0.333333));
944                 p->push_back (PresetPoint (0.847926, 0.558333));
945                 p->push_back (PresetPoint (0.919355, 0.730556));
946                 p->push_back (PresetPoint (1, GAIN_COEFF_UNITY));
947                 fade_in_presets->push_back (p);
948
949                 p = new Preset ("Fast cut", "fadein-fast-cut");
950                 p->push_back (PresetPoint (0, GAIN_COEFF_SMALL));
951                 p->push_back (PresetPoint (0.0737327, 0.308333));
952                 p->push_back (PresetPoint (0.246544, 0.658333));
953                 p->push_back (PresetPoint (0.470046, 0.886111));
954                 p->push_back (PresetPoint (0.652074, 0.972222));
955                 p->push_back (PresetPoint (0.771889, 0.988889));
956                 p->push_back (PresetPoint (1, GAIN_COEFF_UNITY));
957                 fade_in_presets->push_back (p);
958
959                 p = new Preset ("Long cut", "fadein-long-cut");
960                 p->push_back (PresetPoint (0, GAIN_COEFF_SMALL));
961                 p->push_back (PresetPoint (0.0207373, 0.197222));
962                 p->push_back (PresetPoint (0.0645161, 0.525));
963                 p->push_back (PresetPoint (0.152074, 0.802778));
964                 p->push_back (PresetPoint (0.276498, 0.919444));
965                 p->push_back (PresetPoint (0.481567, 0.980556));
966                 p->push_back (PresetPoint (0.767281, 1));
967                 p->push_back (PresetPoint (1, GAIN_COEFF_UNITY));
968                 fade_in_presets->push_back (p);
969         }
970
971         /* FADE OUT */
972
973         // p = new Preset ("regout.xpm");
974         p = new Preset ("Linear (-6dB cut)", "fadeout-linear");
975         p->push_back (PresetPoint (0.000000, GAIN_COEFF_UNITY));
976         p->push_back (PresetPoint (0.166667, 0.833033));
977         p->push_back (PresetPoint (0.333333, 0.666186));
978         p->push_back (PresetPoint (0.500000, 0.499459));
979         p->push_back (PresetPoint (0.666667, 0.332853));
980         p->push_back (PresetPoint (0.833333, 0.166366));
981         p->push_back (PresetPoint (1.000000, GAIN_COEFF_SMALL));
982         fade_out_presets->push_back (p);
983
984         p = new Preset ("S(1)-Curve", "fadeout-S1");
985         p->push_back (PresetPoint (0, GAIN_COEFF_UNITY));
986         p->push_back (PresetPoint (0.1, 0.99));
987         p->push_back (PresetPoint (0.2, 0.97));
988         p->push_back (PresetPoint (0.8, 0.03));
989         p->push_back (PresetPoint (0.9, 0.01));
990         p->push_back (PresetPoint (1, GAIN_COEFF_SMALL));
991         fade_out_presets->push_back (p);
992
993         p = new Preset ("S(2)-Curve", "fadeout-S2");
994         p->push_back (PresetPoint (0.0, GAIN_COEFF_UNITY));
995         p->push_back (PresetPoint (0.163, 0.678));
996         p->push_back (PresetPoint (0.055, 0.783));
997         p->push_back (PresetPoint (0.837, 0.35));
998         p->push_back (PresetPoint (0.945, 0.222));
999         p->push_back (PresetPoint (1.0, GAIN_COEFF_SMALL));
1000         fade_out_presets->push_back (p);
1001
1002         // p = new Preset ("linout.xpm");
1003         p = new Preset ("Constant power (-3dB cut)", "fadeout-constant-power");
1004         p->push_back (PresetPoint (0.000000, GAIN_COEFF_UNITY));
1005         p->push_back (PresetPoint (0.166667, 0.948859));
1006         p->push_back (PresetPoint (0.333333, 0.851507));
1007         p->push_back (PresetPoint (0.500000, 0.707946));
1008         p->push_back (PresetPoint (0.666667, 0.518174));
1009         p->push_back (PresetPoint (0.833333, 0.282192));
1010         p->push_back (PresetPoint (1.000000, GAIN_COEFF_SMALL));
1011         fade_out_presets->push_back (p);
1012
1013         if (!Profile->get_sae()) {
1014                 // p = new Preset ("hiout.xpm");
1015                 p = new Preset ("Short cut", "fadeout-short-cut");
1016                 p->push_back (PresetPoint (0, GAIN_COEFF_UNITY));
1017                 p->push_back (PresetPoint (0.305556, GAIN_COEFF_UNITY));
1018                 p->push_back (PresetPoint (0.548611, 0.991736));
1019                 p->push_back (PresetPoint (0.759259, 0.931129));
1020                 p->push_back (PresetPoint (0.918981, 0.68595));
1021                 p->push_back (PresetPoint (0.976852, 0.22865));
1022                 p->push_back (PresetPoint (1, GAIN_COEFF_SMALL));
1023                 fade_out_presets->push_back (p);
1024
1025                 p = new Preset ("Slow cut", "fadeout-slow-cut");
1026                 p->push_back (PresetPoint (0, GAIN_COEFF_UNITY));
1027                 p->push_back (PresetPoint (0.228111, 0.988889));
1028                 p->push_back (PresetPoint (0.347926, 0.972222));
1029                 p->push_back (PresetPoint (0.529954, 0.886111));
1030                 p->push_back (PresetPoint (0.753456, 0.658333));
1031                 p->push_back (PresetPoint (0.9262673, 0.308333));
1032                 p->push_back (PresetPoint (1, GAIN_COEFF_SMALL));
1033                 fade_out_presets->push_back (p);
1034
1035                 p = new Preset ("Fast cut", "fadeout-fast-cut");
1036                 p->push_back (PresetPoint (0, GAIN_COEFF_UNITY));
1037                 p->push_back (PresetPoint (0.080645, 0.730556));
1038                 p->push_back (PresetPoint (0.277778, 0.289256));
1039                 p->push_back (PresetPoint (0.470046, 0.152778));
1040                 p->push_back (PresetPoint (0.695853, 0.0694444));
1041                 p->push_back (PresetPoint (1, GAIN_COEFF_SMALL));
1042                 fade_out_presets->push_back (p);
1043
1044                 // p = new Preset ("loout.xpm");
1045                 p = new Preset ("Long cut", "fadeout-long-cut");
1046                 p->push_back (PresetPoint (0, GAIN_COEFF_UNITY));
1047                 p->push_back (PresetPoint (0.023041, 0.697222));
1048                 p->push_back (PresetPoint (0.0553,   0.483333));
1049                 p->push_back (PresetPoint (0.170507, 0.233333));
1050                 p->push_back (PresetPoint (0.370968, 0.0861111));
1051                 p->push_back (PresetPoint (0.610599, 0.0333333));
1052                 p->push_back (PresetPoint (1, GAIN_COEFF_SMALL));
1053                 fade_out_presets->push_back (p);
1054
1055         }
1056 }
1057
1058 void
1059 CrossfadeEditor::curve_select_clicked (WhichFade wf)
1060 {
1061         current = wf;
1062
1063         if (wf == In) {
1064
1065                 for (vector<ArdourCanvas::WaveView*>::iterator i = fade[In].waves.begin(); i != fade[In].waves.end(); ++i) {
1066                         (*i)->set_outline_color (ARDOUR_UI::config()->get_SelectedCrossfadeEditorWave());
1067                         (*i)->set_fill_color (ARDOUR_UI::config()->get_SelectedCrossfadeEditorWave());
1068                 }
1069
1070                 for (vector<ArdourCanvas::WaveView*>::iterator i = fade[Out].waves.begin(); i != fade[Out].waves.end(); ++i) {
1071                         (*i)->set_outline_color (ARDOUR_UI::config()->get_CrossfadeEditorWave());
1072                         (*i)->set_fill_color (ARDOUR_UI::config()->get_CrossfadeEditorWave());
1073                 }
1074
1075                 fade[In].line->set_outline_color (ARDOUR_UI::config()->get_SelectedCrossfadeEditorLine());
1076                 fade[Out].line->set_outline_color (ARDOUR_UI::config()->get_CrossfadeEditorLine());
1077                 fade[Out].shading->hide();
1078                 fade[In].shading->show();
1079
1080                 for (list<Point*>::iterator i = fade[Out].points.begin(); i != fade[Out].points.end(); ++i) {
1081                         (*i)->box->hide();
1082                 }
1083
1084                 for (list<Point*>::iterator i = fade[In].points.begin(); i != fade[In].points.end(); ++i) {
1085                         (*i)->box->show ();
1086                 }
1087
1088         } else {
1089
1090                 for (vector<ArdourCanvas::WaveView*>::iterator i = fade[In].waves.begin(); i != fade[In].waves.end(); ++i) {
1091                         (*i)->set_outline_color (ARDOUR_UI::config()->get_CrossfadeEditorWave());
1092                         (*i)->set_fill_color (ARDOUR_UI::config()->get_CrossfadeEditorWave());
1093                 }
1094
1095                 for (vector<ArdourCanvas::WaveView*>::iterator i = fade[Out].waves.begin(); i != fade[Out].waves.end(); ++i) {
1096                         (*i)->set_outline_color (ARDOUR_UI::config()->get_SelectedCrossfadeEditorWave());
1097                         (*i)->set_fill_color (ARDOUR_UI::config()->get_SelectedCrossfadeEditorWave());
1098                 }
1099
1100                 fade[Out].line->set_outline_color (ARDOUR_UI::config()->get_SelectedCrossfadeEditorLine());
1101                 fade[In].line->set_outline_color (ARDOUR_UI::config()->get_CrossfadeEditorLine());
1102                 fade[In].shading->hide();
1103                 fade[Out].shading->show();
1104
1105                 for (list<Point*>::iterator i = fade[In].points.begin(); i != fade[In].points.end(); ++i) {
1106                         (*i)->box->hide();
1107                 }
1108
1109                 for (list<Point*>::iterator i = fade[Out].points.begin(); i != fade[Out].points.end(); ++i) {
1110                         (*i)->box->show();
1111                 }
1112
1113         }
1114 }
1115
1116 double
1117 CrossfadeEditor::x_coordinate (double& xfract) const
1118 {
1119         xfract = min (1.0, xfract);
1120         xfract = max (0.0, xfract);
1121
1122         return canvas_border + (xfract * effective_width());
1123 }
1124
1125 double
1126 CrossfadeEditor::y_coordinate (double& yfract) const
1127 {
1128         yfract = min (1.0, yfract);
1129         yfract = max (0.0, yfract);
1130
1131         return (canvas->get_allocation().get_height() - (canvas_border)) - (yfract * effective_height());
1132 }
1133
1134 void
1135 CrossfadeEditor::make_waves (boost::shared_ptr<AudioRegion> region, WhichFade which)
1136 {
1137         gdouble ht;
1138         uint32_t nchans = region->n_channels();
1139         guint32 color;
1140         double spu;
1141
1142         if (which == In) {
1143                 color = ARDOUR_UI::config()->get_SelectedCrossfadeEditorWave();
1144         } else {
1145                 color = ARDOUR_UI::config()->get_CrossfadeEditorWave();
1146         }
1147
1148         ht = canvas->get_allocation().get_height() / (double) nchans;
1149         spu = xfade->length() / (double) effective_width();
1150
1151         delete _peaks_ready_connection;
1152         _peaks_ready_connection = 0;
1153
1154         for (uint32_t n = 0; n < nchans; ++n) {
1155
1156                 gdouble yoff = n * ht;
1157
1158                 if (region->audio_source(n)->peaks_ready (boost::bind (&CrossfadeEditor::peaks_ready, this, boost::weak_ptr<AudioRegion>(region), which), &_peaks_ready_connection, gui_context())) {
1159                         ArdourCanvas::WaveView* waveview = new ArdourCanvas::WaveView (canvas->root(), region);
1160
1161                         waveview->set_channel (n);
1162                         waveview->property_gain_function() = (void*) curve_get_vector_from_c;
1163                         waveview->property_gain_src() = static_cast<Evoral::Curve*>(&fade[which].gain_curve.curve());
1164                         waveview->set_x_position (canvas_border);
1165                         waveview->set_y_position (yoff);
1166                         waveview->set_height (ht);
1167                         waveview->set_samples_per_pixel (spu);
1168                         waveview->property_amplitude_above_axis() = 2.0;
1169                         waveview->set_outline_color (color);
1170                         waveview->set_fill_color (color);
1171
1172                         if (which != In) {
1173                                 waveview->set_region_start (region->start() + region->length() - xfade->length());
1174                         }
1175
1176                         waveview->lower_to_bottom();
1177                         fade[which].waves.push_back (waveview);
1178                 }
1179         }
1180
1181         toplevel->lower_to_bottom();
1182 }
1183
1184 void
1185 CrossfadeEditor::peaks_ready (boost::weak_ptr<AudioRegion> wr, WhichFade which)
1186 {
1187         boost::shared_ptr<AudioRegion> r (wr.lock());
1188
1189         if (!r) {
1190                 return;
1191         }
1192
1193         /* this should never be called, because the peak files for an xfade
1194            will be ready by the time we want them. but our API forces us
1195            to provide this, so ..
1196         */
1197         delete _peaks_ready_connection;
1198         _peaks_ready_connection = 0;
1199
1200         make_waves (r, which);
1201 }
1202
1203 void
1204 CrossfadeEditor::audition (Audition which)
1205 {
1206         AudioPlaylist& pl (_session->the_auditioner()->prepare_playlist());
1207         framecnt_t preroll;
1208         framecnt_t postroll;
1209         framecnt_t left_start_offset;
1210         framecnt_t right_length;
1211         framecnt_t left_length;
1212
1213         if (which != Right && preroll_button.get_active()) {
1214                 preroll = _session->frame_rate() * 2;  //2 second hardcoded preroll for now
1215         } else {
1216                 preroll = 0;
1217         }
1218
1219         if (which != Left && postroll_button.get_active()) {
1220                 postroll = _session->frame_rate() * 2;  //2 second hardcoded postroll for now
1221         } else {
1222                 postroll = 0;
1223         }
1224
1225         // Is there enough data for the whole preroll?
1226         left_length = xfade->length();
1227         if ((left_start_offset = xfade->out()->length() - xfade->length()) > preroll) {
1228                 left_start_offset -= preroll;
1229         } else {
1230                 preroll = left_start_offset;
1231                 left_start_offset = 0;
1232         }
1233         left_length += preroll;
1234
1235         // Is there enough data for the whole postroll?
1236         right_length = xfade->length();
1237         if ((xfade->in()->length() - right_length) > postroll) {
1238                 right_length += postroll;
1239         } else {
1240                 right_length = xfade->in()->length();
1241         }
1242
1243         PropertyList left_plist;
1244         PropertyList right_plist;
1245
1246
1247         left_plist.add (ARDOUR::Properties::start, left_start_offset);
1248         left_plist.add (ARDOUR::Properties::length, left_length);
1249         left_plist.add (ARDOUR::Properties::name, string ("xfade out"));
1250         left_plist.add (ARDOUR::Properties::layer, 0);
1251         left_plist.add (ARDOUR::Properties::fade_in_active, true);
1252
1253         right_plist.add (ARDOUR::Properties::start, 0);
1254         right_plist.add (ARDOUR::Properties::length, right_length);
1255         right_plist.add (ARDOUR::Properties::name, string("xfade in"));
1256         right_plist.add (ARDOUR::Properties::layer, 0);
1257         right_plist.add (ARDOUR::Properties::fade_out_active, true);
1258
1259         if (which == Left) {
1260                 right_plist.add (ARDOUR::Properties::scale_amplitude, 0.0f);
1261         } else if (which == Right) {
1262                 left_plist.add (ARDOUR::Properties::scale_amplitude, 0.0f);
1263         }
1264
1265         boost::shared_ptr<AudioRegion> left (boost::dynamic_pointer_cast<AudioRegion>
1266                                                      (RegionFactory::create (xfade->out(), left_plist, false)));
1267         boost::shared_ptr<AudioRegion> right (boost::dynamic_pointer_cast<AudioRegion>
1268                                               (RegionFactory::create (xfade->in(), right_plist, false)));
1269
1270         // apply a 20ms declicking fade at the start and end of auditioning
1271         // XXX this should really be a property
1272
1273         left->set_fade_in_length (_session->frame_rate() / 50);
1274         right->set_fade_out_length (_session->frame_rate() / 50);
1275
1276         pl.add_region (left, 0);
1277         pl.add_region (right, 1 + preroll);
1278
1279         /* there is only one ... */
1280         pl.foreach_crossfade (sigc::mem_fun (*this, &CrossfadeEditor::setup));
1281
1282         _session->audition_playlist ();
1283 }
1284
1285 void
1286 CrossfadeEditor::audition_both ()
1287 {
1288         audition (Both);
1289 }
1290
1291 void
1292 CrossfadeEditor::audition_left_dry ()
1293 {
1294         PropertyList plist;
1295
1296         plist.add (ARDOUR::Properties::start, xfade->out()->length() - xfade->length());
1297         plist.add (ARDOUR::Properties::length, xfade->length());
1298         plist.add (ARDOUR::Properties::name, string("xfade left"));
1299         plist.add (ARDOUR::Properties::layer, 0);
1300
1301         boost::shared_ptr<AudioRegion> left (boost::dynamic_pointer_cast<AudioRegion>
1302                                              (RegionFactory::create (xfade->out(), plist, false)));
1303
1304         _session->audition_region (left);
1305 }
1306
1307 void
1308 CrossfadeEditor::audition_left ()
1309 {
1310         audition (Left);
1311 }
1312
1313 void
1314 CrossfadeEditor::audition_right_dry ()
1315 {
1316         PropertyList plist;
1317
1318         plist.add (ARDOUR::Properties::start, 0);
1319         plist.add (ARDOUR::Properties::length, xfade->length());
1320         plist.add (ARDOUR::Properties::name, string ("xfade right"));
1321         plist.add (ARDOUR::Properties::layer, 0);
1322
1323         boost::shared_ptr<AudioRegion> right (boost::dynamic_pointer_cast<AudioRegion>
1324                                               (RegionFactory::create (xfade->in(), plist, false)));
1325
1326         _session->audition_region (right);
1327 }
1328
1329 void
1330 CrossfadeEditor::audition_right ()
1331 {
1332         audition (Right);
1333 }
1334
1335 void
1336 CrossfadeEditor::cancel_audition ()
1337 {
1338         _session->cancel_audition ();
1339 }
1340
1341 void
1342 CrossfadeEditor::audition_toggled ()
1343 {
1344         bool x;
1345
1346         if ((x = audition_both_button.get_active ()) != _session->is_auditioning()) {
1347
1348                 if (x) {
1349                         audition_both ();
1350                 } else {
1351                         cancel_audition ();
1352                 }
1353         }
1354 }
1355
1356 void
1357 CrossfadeEditor::audition_right_toggled ()
1358 {
1359         bool x;
1360
1361         if ((x = audition_right_button.get_active ()) != _session->is_auditioning()) {
1362
1363                 if (x) {
1364                         audition_right ();
1365                 } else {
1366                         cancel_audition ();
1367                 }
1368         }
1369 }
1370
1371 void
1372 CrossfadeEditor::audition_right_dry_toggled ()
1373 {
1374         bool x;
1375
1376         if ((x = audition_right_dry_button.get_active ()) != _session->is_auditioning()) {
1377
1378                 if (x) {
1379                         audition_right_dry ();
1380                 } else {
1381                         cancel_audition ();
1382                 }
1383         }
1384 }
1385
1386 void
1387 CrossfadeEditor::audition_left_toggled ()
1388 {
1389         bool x;
1390
1391         if ((x = audition_left_button.get_active ()) != _session->is_auditioning()) {
1392
1393                 if (x) {
1394                         audition_left ();
1395                 } else {
1396                         cancel_audition ();
1397                 }
1398         }
1399 }
1400
1401 void
1402 CrossfadeEditor::audition_left_dry_toggled ()
1403 {
1404         bool x;
1405
1406         if ((x = audition_left_dry_button.get_active ()) != _session->is_auditioning()) {
1407
1408                 if (x) {
1409                         audition_left_dry ();
1410                 } else {
1411                         cancel_audition ();
1412                 }
1413         }
1414 }
1415
1416 bool
1417 CrossfadeEditor::on_key_press_event (GdkEventKey */*ev*/)
1418 {
1419         return true;
1420 }
1421
1422 bool
1423 CrossfadeEditor::on_key_release_event (GdkEventKey* ev)
1424 {
1425         switch (ev->keyval) {
1426         case GDK_Right:
1427                 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1428                         audition_right_dry_button.set_active (!audition_right_dry_button.get_active());
1429                 } else {
1430                         audition_right_button.set_active (!audition_right_button.get_active());
1431                 }
1432                 break;
1433
1434         case GDK_Left:
1435                 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1436                         audition_left_dry_button.set_active (!audition_left_dry_button.get_active());
1437                 } else {
1438                         audition_left_button.set_active (!audition_left_button.get_active());
1439                 }
1440                 break;
1441
1442         case GDK_space:
1443                 if (_session->is_auditioning()) {
1444                         cancel_audition ();
1445                 } else {
1446                         audition_both_button.set_active (!audition_both_button.get_active());
1447                 }
1448                 break;
1449
1450         default:
1451                 break;
1452         }
1453
1454         return true;
1455 }