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