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